diff --git a/.eslintignore b/.eslintignore
index 682db9004..4682851df 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,19 @@
+*.min.js
+
+# libraries
web/api/lib
-web/skins/classic/js/jquery-1.11.3.js
+web/includes/csrf/
+web/js/videojs.zoomrotate.js
+web/skins/classic/js/bootstrap.js
+web/skins/classic/js/chosen
+web/skins/classic/js/dateTimePicker
+web/skins/classic/js/jquery-*.js
+web/skins/classic/js/jquery-ui-*
web/skins/classic/js/jquery.js
+web/skins/classic/js/moment.js
+web/skins/classic/js/video.js
web/tools/mootools
+
+# Cannot be parsed as JS
+web/skins/classic/includes/export_functions.php
+web/skins/classic/views/events.php
diff --git a/.eslintrc.js b/.eslintrc.js
index fa4d6b6e5..64950ee0d 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -5,25 +5,46 @@ module.exports = {
"browser": true,
},
"extends": ["google"],
+ "overrides": [{
+ // eslint-plugin-html handles eol-last slightly different - it applies to
+ // each set of script tags, so we turn it off here.
+ "files": "**/*.*php",
+ "rules": {
+ "eol-last": "off",
+ "indent": "off",
+ },
+ }],
+ "plugins": [
+ "html",
+ "php-markup",
+ ],
"rules": {
- "brace-style": "off",
"camelcase": "off",
"comma-dangle": "off",
- "key-spacing": "off",
+ "guard-for-in": "off",
"max-len": "off",
"new-cap": ["error", {
capIsNewExceptions: ["Error", "Warning", "Debug", "Polygon_calcArea", "Play", "Stop"],
newIsCapExceptionPattern: "^Asset\.."
}],
"no-array-constructor": "off",
- "no-caller": "off",
- "no-new-object": "off",
- "no-unused-vars": "off",
+ "no-unused-vars": ["error", {
+ "vars": "local",
+ "args": "none",
+ "ignoreRestSiblings": false
+ }],
"no-var": "off",
- "object-curly-spacing": "off",
"prefer-rest-params": "off",
"quotes": "off",
"require-jsdoc": "off",
"spaced-comment": "off",
},
+ "settings": {
+ "php/php-extensions": [".php"],
+ "php/markup-replacement": {"php": "", "=": "0"},
+ "php/keep-eol": false,
+ "php/remove-whitespace": false,
+ "php/remove-empty-line": false,
+ "php/remove-php-lint": false
+ },
};
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..8804e639c
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: zoneminder # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 5f80fe591..610480e8e 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,23 +1,46 @@
-You should only file an issue if you found a bug. Feature and enhancement requests, general discussions and support questions should occur in one of the following areas:
+**THIS FORUM IS FOR BUG REPORTS ONLY**
-- The ZoneMinder IRC channel - irc.freenode.net #zoneminder
+Do not post feature or enhancement requests, general discussions, or support questions here.
+
+Feature and enhancement requests, general discussions, and support questions should occur in one of the following areas:
+
+- The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/)
- The [ZoneMinder Forum](https://forums.zoneminder.com/)
-**Do not post feature or enhancement requests, general discussions or support questions here.**
-
Docker related issues should be posted here: https://github.com/ZoneMinder/zmdockerfiles
-Make sure you are running the latest version of ZoneMinder before reporting an issue.
+In order to submit a bug report, please populate the fields below this line. This is required.
-**ZoneMinder Version (`zmaudit.pl -v`):**
+----------------------------------------------------------------------------------------------------
-**Are you using a development snapshot / git checkout? If so, what is the latest commit? (`git rev-parse HEAD`):**
+**Describe Your Environment**
+- Version of ZoneMinder [release version, development version, or commit]
+- How you installed ZoneMinder [e.g. PPA, RPMFusion, from-source, etc]
+- Full name and version of OS
+- Browser name and version (if this is an issue with the web interface)
-**Linux Distribution and Version (`cat /etc/os-release` or `cat /etc/redhat-release`):**
+**If the issue concerns a camera**
+- Make and Model
+- Frame rate
+- Resolution
+- ZoneMinder Source Type:
-**If the issue concerns a camera, provide the make, model, frame rate, resolution and ZoneMinder Source Type:**
+**Describe the bug**
+A clear and concise description of what the bug is.
-**Relevant log lines:**
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Debug Logs**
```
-log lines here
+
+
+
```
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..b5df0a5ee
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,40 @@
+---
+name: Bug report
+about: Issues that do not follow the template will be closed
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe Your Environment**
+- Version of ZoneMinder [release version, development version, or commit]
+- How you installed ZoneMinder [e.g. PPA, RPMFusion, from-source, etc]
+- Full name and version of OS
+- Browser name and version (if this is an issue with the web interface)
+
+**If the issue concerns a camera**
+- Make and Model
+- frame rate
+- resolution
+- ZoneMinder Source Type:
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Debug Logs**
+```
+
+
+
+```
diff --git a/.github/config.yml b/.github/config.yml
new file mode 100644
index 000000000..543a2c160
--- /dev/null
+++ b/.github/config.yml
@@ -0,0 +1,21 @@
+# Configuration for welcome - https://github.com/behaviorbot/welcome
+
+# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
+
+# Comment to be posted to on first time issues
+newIssueWelcomeComment: >
+ Thanks for opening your first issue here! Just a reminder, this forum is for Bug Reports only. Be sure to follow the issue template!
+
+# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
+
+# Comment to be posted to on PRs from first time contributors in your repository
+#newPRWelcomeComment: >
+# Thanks for opening this pull request! Please check out our contributing guidelines.
+
+# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
+
+# Comment to be posted to on pull requests merged by a first time user
+#firstPRMergeComment: >
+# Congrats on merging your first pull request! We here at behaviorbot are proud of you!
+
+# It is recommend to include as many gifs and emojis as possible
diff --git a/.github/issue-close-app.yml b/.github/issue-close-app.yml
new file mode 100644
index 000000000..ebe4cdcfa
--- /dev/null
+++ b/.github/issue-close-app.yml
@@ -0,0 +1,16 @@
+# Comment that will be sent if an issue is judged to be closed
+comment: "This issue is closed because it does not meet our bug report issue template. Please read it."
+issueConfigs:
+# There can be several configs for different kind of issues.
+- content:
+# Example 1: bug report
+ - "Describe Your Environment"
+ - "Describe the bug"
+ - "Expected behavior"
+# Optional configuration:
+#
+# whether the keywords are case-insensitive
+# default value is false, which means keywords are case-sensitive
+caseInsensitive: true
+# The issue is judged to be legal if it includes all keywords from any of these two configs.
+# Or it will be closed by the app.
diff --git a/.github/no-response.yml b/.github/no-response.yml
new file mode 100644
index 000000000..7e40c036f
--- /dev/null
+++ b/.github/no-response.yml
@@ -0,0 +1,13 @@
+# Configuration for probot-no-response - https://github.com/probot/no-response
+
+# Number of days of inactivity before an Issue is closed for lack of response
+daysUntilClose: 7
+# Label requiring a response
+responseRequiredLabel: more-information-needed
+# Comment to post when closing an Issue for lack of response. Set to `false` to disable
+closeComment: >
+ This issue has been automatically closed because there has been no response
+ to our request for more information from the original author. With only the
+ information that is currently in the issue, we don't have enough information
+ to take action. Please reach out if you have or find the answers we need so
+ that we can investigate further.
diff --git a/.github/stale.yml b/.github/stale.yml
index 971d85ea5..9ff190b1f 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -9,6 +9,9 @@ daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "Under Review"
+ - "Bounty"
+ - "bug"
+ - "Feature"
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
diff --git a/.github/support.yml b/.github/support.yml
new file mode 100644
index 000000000..c62515ee2
--- /dev/null
+++ b/.github/support.yml
@@ -0,0 +1,29 @@
+# Configuration for support-requests - https://github.com/dessant/support-requests
+
+# Label used to mark issues as support requests
+supportLabel: support
+
+# Comment to post on issues marked as support requests, `{issue-author}` is an
+# optional placeholder. Set to `false` to disable
+supportComment: >
+ :wave: @{issue-author}, we use the issue tracker exclusively for bug reports.
+ However, this issue appears to be a support request, a feature request, or
+ attempts to ask a question.
+
+ Please use our support channels to get help with or discuss this project:
+
+ - The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/)
+
+ - The [ZoneMinder USer Forum](https://forums.zoneminder.com/)
+
+# Close issues marked as support requests
+close: true
+
+# Lock issues marked as support requests
+lock: true
+
+# Assign `off-topic` as the reason for locking. Set to `false` to disable
+setLockReason: true
+
+# Repository to extend settings from
+# _extends: repo
diff --git a/.gitignore b/.gitignore
index 0dae662d7..3167e62a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -157,3 +157,4 @@ web/undef.log
zm.conf
zmconfgen.pl
zmlinkcontent.sh
+**/.DS_Store
diff --git a/.travis.yml b/.travis.yml
index 1ae0988d5..7cfe96854 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,6 @@
language: cpp
sudo: required
-dist: trusty
+dist: xenial
git:
depth: 9999999
notifications:
@@ -32,23 +32,31 @@ install:
- update-binfmts --enable qemu-arm
env:
- global:
- - SMPFLAGS=-j4
- matrix:
- - OS=el DIST=7
- - OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack
- - OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack
- - OS=ubuntu DIST=trusty
- - OS=ubuntu DIST=xenial
- - OS=ubuntu DIST=trusty ARCH=i386
- - OS=ubuntu DIST=xenial ARCH=i386
- - OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
+ - SMPFLAGS=-j4 OS=el DIST=7
+ - SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack
+ - SMPFLAGS=-j4 OS=fedora DIST=30
+ - SMPFLAGS=-j4 OS=fedora DIST=31
+ - SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack
+ - SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386
+ - SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386
+ - SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386
+ - SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
+ - SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386
+ - SMPFLAGS=-j4 OS=eslint DIST=eslint
compiler:
- gcc
services:
- mysql
- docker
+
script:
- utils/packpack/startpackpack.sh
@@ -64,4 +72,3 @@ deploy:
script: utils/packpack/rsync_xfer.sh
on:
branch: master
-
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 72d39764f..000000000
--- a/AUTHORS
+++ /dev/null
@@ -1,10 +0,0 @@
-ZoneMinder - A Linux based camera monitoring and analysis tool.
-
-This project was imagined and created by Philip Coombes in
-September 2002, shortly after being burglarised of his power tools.
-He can be contacted at philip.coombes@zoneminder.com
-
-In early 2013 after nearly two years of no development,
-the community reached out to Phil in an attempt to open up the
-project. With Phil's blessing, the code was migrated to github,
-and the community took over the project.
diff --git a/BUGS b/BUGS
deleted file mode 100644
index b78c01bed..000000000
--- a/BUGS
+++ /dev/null
@@ -1 +0,0 @@
-Please see https://github.com/ZoneMinder/ZoneMinder/issues?state=open
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c847e06a3..b43680b0b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -127,7 +127,8 @@ mark_as_advanced(
ZM_PATH_ARP
ZM_CONFIG_DIR
ZM_CONFIG_SUBDIR
- ZM_SYSTEMD)
+ ZM_SYSTEMD
+ ZM_MANPAGE_DEST_PREFIX)
set(ZM_RUNDIR "/var/run/zm" CACHE PATH
"Location of transient process files, default: /var/run/zm")
@@ -161,13 +162,12 @@ set(ZM_WEB_GROUP "" CACHE STRING
Leave empty to be the same as the web user")
set(ZM_DIR_EVENTS "${ZM_CONTENTDIR}/events" CACHE PATH
"Location where events are recorded to, default: ZM_CONTENTDIR/events")
-set(ZM_DIR_IMAGES "${ZM_CONTENTDIR}/images" CACHE PATH
- "Location where images, not directly associated with events,
- are recorded to, default: ZM_CONTENTDIR/images")
set(ZM_DIR_SOUNDS "sounds" CACHE PATH
"Location to look for optional sound files, default: sounds")
set(ZM_PATH_ZMS "/cgi-bin/nph-zms" CACHE PATH
"Web url to zms streaming server, default: /cgi-bin/nph-zms")
+set(ZM_PATH_SHUTDOWN "/sbin/shutdown" CACHE PATH
+ "Path to shutdown binary, default: /sbin/shutdown")
# Advanced
set(ZM_PATH_MAP "/dev/shm" CACHE PATH
@@ -185,8 +185,6 @@ set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING
set(ZM_NO_MMAP "OFF" CACHE BOOL
"Set to ON to not use mmap shared memory. Shouldn't be enabled unless you
experience problems with the shared memory. default: OFF")
-set(ZM_NO_FFMPEG "OFF" CACHE BOOL
- "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. 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_CURL "OFF" CACHE BOOL
@@ -210,6 +208,10 @@ set(ZM_TARGET_DISTRO "" CACHE STRING
"Build ZoneMinder for a specific distribution. Currently, valid names are: fc27, fc26, el7, OS13, FreeBSD")
set(ZM_SYSTEMD "OFF" CACHE BOOL
"Set to ON to force building ZM with systemd support. default: OFF")
+set(ZM_MANPAGE_DEST_PREFIX "share/man" CACHE PATH
+ "Relative path used to install ZoneMinder's Man pages into a
+ non-standard folder. Most Linux users will not need to change this.
+ BSD users may need to set this.")
# Reassign some variables if a target distro has been specified
if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc"))
@@ -222,7 +224,6 @@ if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc"))
set(ZM_WEBDIR "/usr/share/zoneminder/www")
set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin")
set(ZM_DIR_EVENTS "/var/lib/zoneminder/events")
- set(ZM_DIR_IMAGES "/var/lib/zoneminder/images")
set(ZM_PATH_ZMS "/cgi-bin-zm/nph-zms")
elseif(ZM_TARGET_DISTRO STREQUAL "OS13")
set(ZM_RUNDIR "/var/run/zoneminder")
@@ -513,123 +514,137 @@ endif(MP4V2_LIBRARIES)
set(PATH_FFMPEG "")
set(OPT_FFMPEG "no")
-# Do not check for ffmpeg if ZM_NO_FFMPEG is on
-if(NOT ZM_NO_FFMPEG)
- # avformat (using find_library and find_path)
- find_library(AVFORMAT_LIBRARIES avformat)
- if(AVFORMAT_LIBRARIES)
- set(HAVE_LIBAVFORMAT 1)
- list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}")
- find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h" /usr/include/ffmpeg)
- if(AVFORMAT_INCLUDE_DIR)
- include_directories("${AVFORMAT_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_INCLUDES "${AVFORMAT_INCLUDE_DIR}")
- endif(AVFORMAT_INCLUDE_DIR)
- mark_as_advanced(FORCE AVFORMAT_LIBRARIES AVFORMAT_INCLUDE_DIR)
- check_include_file("libavformat/avformat.h" HAVE_LIBAVFORMAT_AVFORMAT_H)
- set(optlibsfound "${optlibsfound} AVFormat")
- else(AVFORMAT_LIBRARIES)
- set(optlibsnotfound "${optlibsnotfound} AVFormat")
- endif(AVFORMAT_LIBRARIES)
+# avformat (using find_library and find_path)
+find_library(AVFORMAT_LIBRARIES avformat)
+if(AVFORMAT_LIBRARIES)
+ set(HAVE_LIBAVFORMAT 1)
+ list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}")
+ find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h" /usr/include/ffmpeg)
+ if(AVFORMAT_INCLUDE_DIR)
+ include_directories("${AVFORMAT_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${AVFORMAT_INCLUDE_DIR}")
+ endif(AVFORMAT_INCLUDE_DIR)
+ mark_as_advanced(FORCE AVFORMAT_LIBRARIES AVFORMAT_INCLUDE_DIR)
+ check_include_file("libavformat/avformat.h" HAVE_LIBAVFORMAT_AVFORMAT_H)
+ set(optlibsfound "${optlibsfound} AVFormat")
+else(AVFORMAT_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} AVFormat")
+endif(AVFORMAT_LIBRARIES)
- # avcodec (using find_library and find_path)
- find_library(AVCODEC_LIBRARIES avcodec)
- if(AVCODEC_LIBRARIES)
- set(HAVE_LIBAVCODEC 1)
- list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}")
- find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h" /usr/include/ffmpeg)
- if(AVCODEC_INCLUDE_DIR)
- include_directories("${AVCODEC_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_INCLUDES "${AVCODEC_INCLUDE_DIR}")
- endif(AVCODEC_INCLUDE_DIR)
- mark_as_advanced(FORCE AVCODEC_LIBRARIES AVCODEC_INCLUDE_DIR)
- check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H)
- set(optlibsfound "${optlibsfound} AVCodec")
- else(AVCODEC_LIBRARIES)
- set(optlibsnotfound "${optlibsnotfound} AVCodec")
- endif(AVCODEC_LIBRARIES)
+# avcodec (using find_library and find_path)
+find_library(AVCODEC_LIBRARIES avcodec)
+if(AVCODEC_LIBRARIES)
+ set(HAVE_LIBAVCODEC 1)
+ list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}")
+ find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h" /usr/include/ffmpeg)
+ if(AVCODEC_INCLUDE_DIR)
+ include_directories("${AVCODEC_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${AVCODEC_INCLUDE_DIR}")
+ endif(AVCODEC_INCLUDE_DIR)
+ mark_as_advanced(FORCE AVCODEC_LIBRARIES AVCODEC_INCLUDE_DIR)
+ check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H)
+ set(optlibsfound "${optlibsfound} AVCodec")
+else(AVCODEC_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} AVCodec")
+endif(AVCODEC_LIBRARIES)
- # avdevice (using find_library and find_path)
- find_library(AVDEVICE_LIBRARIES avdevice)
- if(AVDEVICE_LIBRARIES)
- set(HAVE_LIBAVDEVICE 1)
- list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}")
- find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h" /usr/include/ffmpeg)
- if(AVDEVICE_INCLUDE_DIR)
- include_directories("${AVDEVICE_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_INCLUDES "${AVDEVICE_INCLUDE_DIR}")
- endif(AVDEVICE_INCLUDE_DIR)
- mark_as_advanced(FORCE AVDEVICE_LIBRARIES AVDEVICE_INCLUDE_DIR)
- check_include_file("libavdevice/avdevice.h" HAVE_LIBAVDEVICE_AVDEVICE_H)
- set(optlibsfound "${optlibsfound} AVDevice")
- else(AVDEVICE_LIBRARIES)
- set(optlibsnotfound "${optlibsnotfound} AVDevice")
- endif(AVDEVICE_LIBRARIES)
+# avdevice (using find_library and find_path)
+find_library(AVDEVICE_LIBRARIES avdevice)
+if(AVDEVICE_LIBRARIES)
+ set(HAVE_LIBAVDEVICE 1)
+ list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}")
+ find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h" /usr/include/ffmpeg)
+ if(AVDEVICE_INCLUDE_DIR)
+ include_directories("${AVDEVICE_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${AVDEVICE_INCLUDE_DIR}")
+ endif(AVDEVICE_INCLUDE_DIR)
+ mark_as_advanced(FORCE AVDEVICE_LIBRARIES AVDEVICE_INCLUDE_DIR)
+ check_include_file("libavdevice/avdevice.h" HAVE_LIBAVDEVICE_AVDEVICE_H)
+ set(optlibsfound "${optlibsfound} AVDevice")
+else(AVDEVICE_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} AVDevice")
+endif(AVDEVICE_LIBRARIES)
- # avutil (using find_library and find_path)
- find_library(AVUTIL_LIBRARIES avutil)
- if(AVUTIL_LIBRARIES)
- set(HAVE_LIBAVUTIL 1)
- list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}")
- find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h" /usr/include/ffmpeg)
- if(AVUTIL_INCLUDE_DIR)
- include_directories("${AVUTIL_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_INCLUDES "${AVUTIL_INCLUDE_DIR}")
- endif(AVUTIL_INCLUDE_DIR)
- mark_as_advanced(FORCE AVUTIL_LIBRARIES AVUTIL_INCLUDE_DIR)
- check_include_file("libavutil/avutil.h" HAVE_LIBAVUTIL_AVUTIL_H)
- check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H)
- check_include_file("libavutil/hwcontext.h" HAVE_LIBAVUTIL_HWCONTEXT_H)
- set(optlibsfound "${optlibsfound} AVUtil")
- else(AVUTIL_LIBRARIES)
- set(optlibsnotfound "${optlibsnotfound} AVUtil")
- endif(AVUTIL_LIBRARIES)
+# avutil (using find_library and find_path)
+find_library(AVUTIL_LIBRARIES avutil)
+if(AVUTIL_LIBRARIES)
+ set(HAVE_LIBAVUTIL 1)
+ list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}")
+ find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h" /usr/include/ffmpeg)
+ if(AVUTIL_INCLUDE_DIR)
+ include_directories("${AVUTIL_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${AVUTIL_INCLUDE_DIR}")
+ endif(AVUTIL_INCLUDE_DIR)
+ mark_as_advanced(FORCE AVUTIL_LIBRARIES AVUTIL_INCLUDE_DIR)
+ check_include_file("libavutil/avutil.h" HAVE_LIBAVUTIL_AVUTIL_H)
+ check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H)
+ check_include_file("libavutil/hwcontext.h" HAVE_LIBAVUTIL_HWCONTEXT_H)
+ set(optlibsfound "${optlibsfound} AVUtil")
+else(AVUTIL_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} AVUtil")
+endif(AVUTIL_LIBRARIES)
- # swscale (using find_library and find_path)
- find_library(SWSCALE_LIBRARIES swscale)
- if(SWSCALE_LIBRARIES)
- set(HAVE_LIBSWSCALE 1)
- list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}")
- find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h" /usr/include/ffmpeg)
- if(SWSCALE_INCLUDE_DIR)
- include_directories("${SWSCALE_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_INCLUDES "${SWSCALE_INCLUDE_DIR}")
- endif(SWSCALE_INCLUDE_DIR)
- mark_as_advanced(FORCE SWSCALE_LIBRARIES SWSCALE_INCLUDE_DIR)
- check_include_file("libswscale/swscale.h" HAVE_LIBSWSCALE_SWSCALE_H)
- set(optlibsfound "${optlibsfound} SWScale")
- else(SWSCALE_LIBRARIES)
- set(optlibsnotfound "${optlibsnotfound} SWScale")
- endif(SWSCALE_LIBRARIES)
+# swscale (using find_library and find_path)
+find_library(SWSCALE_LIBRARIES swscale)
+if(SWSCALE_LIBRARIES)
+ set(HAVE_LIBSWSCALE 1)
+ list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}")
+ find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h" /usr/include/ffmpeg)
+ if(SWSCALE_INCLUDE_DIR)
+ include_directories("${SWSCALE_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${SWSCALE_INCLUDE_DIR}")
+ endif(SWSCALE_INCLUDE_DIR)
+ mark_as_advanced(FORCE SWSCALE_LIBRARIES SWSCALE_INCLUDE_DIR)
+ check_include_file("libswscale/swscale.h" HAVE_LIBSWSCALE_SWSCALE_H)
+ set(optlibsfound "${optlibsfound} SWScale")
+else(SWSCALE_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} SWScale")
+endif(SWSCALE_LIBRARIES)
- # rescale (using find_library and find_path)
- find_library(AVRESAMPLE_LIBRARIES avresample)
- if(AVRESAMPLE_LIBRARIES)
- set(HAVE_LIBAVRESAMPLE 1)
- list(APPEND ZM_BIN_LIBS "${AVRESAMPLE_LIBRARIES}")
- find_path(AVRESAMPLE_INCLUDE_DIR "libavresample/avresample.h" /usr/include/ffmpeg)
- if(AVRESAMPLE_INCLUDE_DIR)
- include_directories("${AVRESAMPLE_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_INCLUDES "${AVRESAMPLE_INCLUDE_DIR}")
- endif(AVRESAMPLE_INCLUDE_DIR)
- mark_as_advanced(FORCE AVRESAMPLE_LIBRARIES AVRESAMPLE_INCLUDE_DIR)
- check_include_file("libavresample/avresample.h" HAVE_LIBAVRESAMPLE_AVRESAMPLE_H)
- set(optlibsfound "${optlibsfound} AVResample")
- else(AVRESAMPLE_LIBRARIES)
- set(optlibsnotfound "${optlibsnotfound} AVResample")
- endif(AVRESAMPLE_LIBRARIES)
+# SWresample (using find_library and find_path)
+find_library(SWRESAMPLE_LIBRARIES swresample)
+if(SWRESAMPLE_LIBRARIES)
+ set(HAVE_LIBSWRESAMPLE 1)
+ list(APPEND ZM_BIN_LIBS "${SWRESAMPLE_LIBRARIES}")
+ find_path(SWRESAMPLE_INCLUDE_DIR "libswresample/swresample.h" /usr/include/ffmpeg)
+ if(SWRESAMPLE_INCLUDE_DIR)
+ include_directories("${SWRESAMPLE_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${SWRESAMPLE_INCLUDE_DIR}")
+ endif(SWRESAMPLE_INCLUDE_DIR)
+ mark_as_advanced(FORCE SWRESAMPLE_LIBRARIES SWRESAMPLE_INCLUDE_DIR)
+ check_include_file("libswresample/swresample.h" HAVE_LIBSWRESAMPLE_SWRESAMPLE_H)
+ set(optlibsfound "${optlibsfound} SWResample")
+else(SWRESAMPLE_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} SWResample")
- # Find the path to the ffmpeg executable
- find_program(FFMPEG_EXECUTABLE
- NAMES ffmpeg avconv
- PATH_SUFFIXES ffmpeg)
- if(FFMPEG_EXECUTABLE)
- set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}")
- set(OPT_FFMPEG "yes")
- mark_as_advanced(FFMPEG_EXECUTABLE)
- endif(FFMPEG_EXECUTABLE)
+ # AVresample (using find_library and find_path)
+ find_library(AVRESAMPLE_LIBRARIES avresample)
+ if(AVRESAMPLE_LIBRARIES)
+ set(HAVE_LIBAVRESAMPLE 1)
+ list(APPEND ZM_BIN_LIBS "${AVRESAMPLE_LIBRARIES}")
+ find_path(AVRESAMPLE_INCLUDE_DIR "libavresample/avresample.h" /usr/include/ffmpeg)
+ if(AVRESAMPLE_INCLUDE_DIR)
+ include_directories("${AVRESAMPLE_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_INCLUDES "${AVRESAMPLE_INCLUDE_DIR}")
+ endif(AVRESAMPLE_INCLUDE_DIR)
+ mark_as_advanced(FORCE AVRESAMPLE_LIBRARIES AVRESAMPLE_INCLUDE_DIR)
+ check_include_file("libavresample/avresample.h" HAVE_LIBAVRESAMPLE_AVRESAMPLE_H)
+ set(optlibsfound "${optlibsfound} AVResample")
+ else(AVRESAMPLE_LIBRARIES)
+ set(optlibsnotfound "${optlibsnotfound} AVResample")
+ endif(AVRESAMPLE_LIBRARIES)
-endif(NOT ZM_NO_FFMPEG)
+endif(SWRESAMPLE_LIBRARIES)
+
+# Find the path to the ffmpeg executable
+find_program(FFMPEG_EXECUTABLE
+ NAMES ffmpeg avconv
+ PATH_SUFFIXES ffmpeg)
+if(FFMPEG_EXECUTABLE)
+ set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}")
+ set(OPT_FFMPEG "yes")
+ mark_as_advanced(FFMPEG_EXECUTABLE)
+endif(FFMPEG_EXECUTABLE)
# Do not check for libvlc if ZM_NO_LIBVLC is on
if(NOT ZM_NO_LIBVLC)
@@ -802,6 +817,24 @@ if(WITH_SYSTEMD)
endif(NOT POLKIT_FOUND)
endif(WITH_SYSTEMD)
+# Find the path to an arp compatible executable
+if(ZM_PATH_ARP STREQUAL "")
+ find_program(ARP_EXECUTABLE arp)
+ if(ARP_EXECUTABLE)
+ set(ZM_PATH_ARP "${ARP_EXECUTABLE}")
+ mark_as_advanced(ARP_EXECUTABLE)
+ else(ARP_EXECUTABLE)
+ find_program(ARP_EXECUTABLE ip)
+ if(ARP_EXECUTABLE)
+ set(ZM_PATH_ARP "${ARP_EXECUTABLE} neigh")
+ mark_as_advanced(ARP_EXECUTABLE)
+ endif(ARP_EXECUTABLE)
+ endif(ARP_EXECUTABLE)
+ if(ARP_EXECUTABLE-NOTFOUND)
+ message(WARNING "Unable to find a compatible arp binary. Monitor probe will not function." )
+ endif(ARP_EXECUTABLE-NOTFOUND)
+endif(ZM_PATH_ARP STREQUAL "")
+
# Some variables that zm expects
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf")
@@ -839,6 +872,13 @@ include(Pod2Man)
ADD_MANPAGE_TARGET()
# Process subdirectories
+
+# build a bcrypt static library
+set(BUILD_SHARED_LIBS_SAVED "${BUILD_SHARED_LIBS}")
+set(BUILD_SHARED_LIBS OFF)
+add_subdirectory(src/libbcrypt EXCLUDE_FROM_ALL)
+set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_SAVED}")
+
add_subdirectory(src)
add_subdirectory(scripts)
add_subdirectory(db)
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index afab3413a..000000000
--- a/ChangeLog
+++ /dev/null
@@ -1 +0,0 @@
-This is too hard to maintain. See https://github.com/ZoneMinder/ZoneMinder/commits/master
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 666105c40..000000000
--- a/INSTALL
+++ /dev/null
@@ -1,149 +0,0 @@
-Installing ZoneMinder with cmake
---------------------------------
-Starting with ZoneMinder 1.26.4, ZoneMinder can now be installed using cmake. This requires cmake version 2.6 or newer.
-cmake is an alternative to the autotools collection (libtool, autoconf, automake, autoheader and such). Its more recent and has many advantages, including, but not limited to:
-* One program (cmake) instead of multiple. (libtool, autoconf, automake, etc)
-* One file per directory (CMakeLists.txt) instead of multiple. (configure.ac, Makefile.am and sometimes more)
-* One syntax (cmake's syntax) instead of multiple. (bash and m4)
-* Generation of makefiles for many platforms, including Windows.
-* Newer than autotools and is being actively developed.
-* Generates colored makefiles with progress indicator.
-* Slightly faster because its based on C and not bash.
-* Lots of documentation, unlike autotools: http://www.cmake.org/cmake/help/cmake2.6docs.html
-
-At this point, its still possible to use autotools for the ZoneMinder project. Choosing cmake or autotools is now a matter of preference.
-Hopefully in the future, cmake will become the default way to install ZoneMinder.
-
-Important differences
----------------------
-* Unlike the autotools way, the cmake way does not require any options. It attempts to detect some things by its own (system directories, libarch, web user and group) and uses defaults for others (installation paths and such).
-* Unlike the autotools way, which links the binaries to a fixed list of libraries, the cmake way only links to libraries that it found on the system. If a library is not found, but required, a fatal error will be shown during the configuration step.
-* Unlike the autotools way, the cmake way does not modify the system in any way it shouldn't. It only does what its supposed to do: Install files to your system. Nothing else and nothing leaks out of the DESTDIR environment variable (if used). This means that depending on your configuration, there might be an extra required step after installation: to link WEB_PATH/events and WEB_PATH/images folders to the correct places.
-
-Configuration
--------------
-cmake by default does not require any parameters, but its possible to override the defaults with the options below.
-
-Configuration can be done in 4 ways:
-1) As a command line parameter, e.g. cmake -DCMAKE_VERBOSE_MAKEFILE=ON .
-2) Using cmake-gui
-3) Providing cmake with an initial cache file with the -C option
- IMPORTANT: Do not use the -C option if any major part of your system, excluding the version of zoneminder, has changed.
- For example, do not use this option if: you have upgraded your distro to a new version, have gone from 32 to 64 bits,
- or have migrated from one machine to another.
-4) By editing the cache file CMakeCache.txt (after it has been generated) - Not recommended
-
-Possible configuration options:
- ZM_RUNDIR Location of transient process files, default: /var/run/zm
- ZM_SOCKDIR Location of Unix domain socket files, default /var/run/zm
- ZM_TMPDIR Location of temporary files, default: /tmp/zm
- ZM_LOGDIR Location of generated log files, default: /var/log/zm
- ZM_WEBDIR Location of the web files, default: /share/zoneminder/www
- ZM_CGIDIR Location of the cgi-bin files, default: /libexec/zoneminder/cgi-bin
- ZM_CONTENTDIR Location of dynamic content (events and images), default: /var/lib/zoneminder
- ZM_DB_HOST Hostname where ZoneMinder database located, default: localhost
- ZM_DB_NAME Name of ZoneMinder database, default: zm
- ZM_DB_USER Name of ZoneMinder database user, default: zmuser
- ZM_DB_PASS Password of ZoneMinder database user, default: zmpass
- ZM_DB_SSL_CA_CERT Path to SSL CA certificate, default: empty; SSL not enabled
- ZM_DB_SSL_CLIENT_KEY Path to SSL client key, default: empty; SSL not enabled
- ZM_DB_SSL_CLIENT_CERT Path to SSL client certificate, default: empty; SSL not enabled
- ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force
- ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user
- ZM_DIR_EVENTS Location where events are recorded to, default: ZM_CONTENTDIR/events
- ZM_DIR_IMAGES Location where images, not directly associated with events, are recorded to, default: ZM_CONTENTDIR/images
- ZM_DIR_SOUNDS Location to look for optional sound files, default: sounds
- ZM_PATH_ZMS Web url to zms streaming server, default: /cgi-bin/nph-zms
-Advanced:
- ZM_PATH_MAP Location to save mapped memory files, default: /dev/shm
- ZM_PATH_ARP Full path to compatible arp binary. Leave empty for automatic detection.
- ZM_CONFIG_DIR Location of the main ZoneMinder config file, zm.conf. default: /etc/zm
- ZM_CONFIG_SUBDIR Location of custom config files. default: ZM_CONFIG_DIR/conf.d
- ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora
- ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB
- ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF
- ZM_NO_FFMPEG Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF
- ZM_NO_X10 Set to ON to build ZoneMinder without X10 support. default: OFF
- ZM_PERL_MM_PARMS By default, ZoneMinder's Perl modules are installed into the Vendor folders, as defined by your installation of Perl. You can change that here. Consult Perl's MakeMaker documentation for a definition of acceptable parameters. If you set this to something that causes the modules to be installed outside Perl's normal serach path, then you will also need to set ZM_PERL_SEARCH_PATH accordingly. default: "INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1"
- ZM_PERL_SEARCH_PATH Use to add a folder to your Perl's search path. This will need to be set in cases where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are installed outside Perl's default search path. default: ""
-
-Useful configuration options provided by cmake:
-CMAKE_VERBOSE_MAKEFILE - Set this to ON (default OFF) to see what cmake is doing. Very useful for troubleshooting.
-CMAKE_BUILD_TYPE - Set this to Debug (default Release) to build ZoneMinder with debugging enabled.
-CMAKE_INSTALL_PREFIX - Use this to change the prefix (default /usr/local). This option behaves like --prefix from autoconf. Package maintainers will probably want to set this to "/usr".
-
-Useful environment variables provided by cmake:
-CMAKE_INCLUDE_PATH - Use this to add to the include search path.
-CMAKE_LIBRARY_PATH - Use this to add to the library search path.
-CMAKE_PREFIX_PATH - Use this to add to both include and library search paths. /include will be added to the include search path and /lib to the library search path. Multiple paths can be specified, separated by a : character. For example: export CMAKE_PREFIX_PATH="/opt/libjpeg-turbo:/opt/ffmpeg-from-git"
-
-CFLAGS, CPPFLAGS and other environment variables:
-To append to the CFLAGS and CXXFLAGS, please use the CFLAGS and CXXFLAGS environment variables.
-Or use the CMAKE_C_FLAGS and CMAKE_CXX_FLAGS configuration options.
-To replace the CFLAGS and CXXFLAGS entirely:
-* For the Release build type: use CMAKE_C_FLAGS_RELEASE for the CFLAGS and CMAKE_CXX_FLAGS_RELEASE for the CXXFLAGS
-* For the Debug build type: use CMAKE_C_FLAGS_DEBUG for the CFLAGS and CMAKE_CXX_FLAGS_DEBUG for the CXXFLAGS
-Other important environment variables (such as LDFLAGS) are also supported.
-
-The DESTDIR environment variable is also supported, however it needs to be set before invoking make install. For example: DESTDIR=mydestdir make install
-For more information about DESTDIR, see:
-* http://www.gnu.org/prep/standards/html_node/DESTDIR.html
-
-Basic steps for installing ZoneMinder on a fresh system
--------------------------------------------------------
-1) After installing all the required dependencies, in the project directory, run "cmake [extra options] ."
-This behaves like ./configure. It is also possible to supply configuration options, e.g. cmake -DZM_DB_PASS="mypass" .
-IMPORTANT: Don't forget the dot "." at the end.
-2) Run "make" to compile ZoneMinder
-3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system.
-4) Create a directory for the content and the necessary symlinks by running zmlinkcontent.sh with the directory you want to use. e.g. ./zmlinkcontent.sh /nfs/zm
-5) Create a database for zoneminder, called "zm".
-6) Create a user for the zoneminder database, called zmuser with password and full privileges to the "zm" database.
-NOTE: The database server, database name, user and password can be different and adjusted during configuration step with the options in this file, or by editing /etc/zm.conf
-7) Populate the zoneminder database using the script zm_create.sql. This should be found in /share/zoneminder/db or in the project/db directory.
-
-8) Create an apache virtual host for ZoneMinder. Make sure to use the same paths as ZM_WEBDIR and ZM_CGIDIR in /etc/zm.conf
-9) Verify date.timezone is set to your timezone. This parameter is often found inside the system php.ini file. Consult your distribution's documentation for the proper way to set this value.
-10) Create other config if desired (e.g. rsyslog, logrotate and such). Some of this can be found in /share/zoneminder/misc or project/misc directory
-11) Setup an appropriate startup script for your system. Two generic startup scripts have been provided, a legacy Sys V Init script and a Systemd service file.
-
-*Sys V Init Setup*
-- Copy the sys v init script /scripts/zm from the build folder to /etc/init.
-- Inspect the contents to make sure they apply to your distro.
-
-*SystemD Setup*
-- Copy the zoneminder systemd service file /misc/zoneminder.service from the build folder to the systemd service file location.
- For Redhat based distros, that folder is /usr/lib/systemd/system.
-- Inspect the contents to make sure they apply to your distro.
-- Tell systemd to load the new service file: "sudo systemctl daemon-reload".
-- Copy /misc/zoneminder-tmpfiles.conf to /etc/tmpfiles.d
-- Tell systemd to process this file: "sudo /usr/bin/systemd-tmpfiles --create /etc/tmpfiles.d/zoneminder.conf".
-
-Basic steps for upgrading ZoneMinder
-------------------------------------
-1) If you wish to use the same paths and configuration as the currently installed ZoneMinder, you need to provide cmake with options that match your current installation.
-You can provide those options in the command line to cmake, e.g. cmake -DZM_DB_PASS="blah" -DZM_WEBDIR="/usr/local/share/zoneminder/www" -DCMAKE_INSTALL_FULL_BINDIR="/usr/bin" .
-Or alternatively, for convenience, use the cmakecacheimport.sh script. This reads a zoneminder configuration file (zm.conf) and creates a cmake initial cache file called zm_conf.cmake, which you can then provide to cmake.
-For example:
-./cmakecacheimport.sh /etc/zm.conf
-cmake -C zm_conf.cmake [extra options] .
-
-2) Run "make" to compile ZoneMinder
-3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system.
-4) Depending on your configuration: If the DIR_EVENTS and DIR_IMAGES options are set to default (pointing to web directory/events and web directory/images), You will need to update the symlinks in the web directory to the correct folders. e.g. web directory/events should point to the real events directory, and likewise for the images directory.
-You can use the zmlinkcontent.sh script for this. For example, if /var/lib/zoneminder is the folder that contains the "images" and "events" directories, you can use:
-./zmlinkcontent.sh /var/lib/zoneminder
-By default, the content directory for new installations is /var/lib/zoneminder. This can be overridden in cmake with the ZM_CONTENTDIR option. e.g. cmake -DZM_CONTENTDIR="/some/big/storage/zm" .
-
-5) Run zmupdate.pl to update the database layout to the new version.
-
-Uninstallation:
----------------
-By default, cmake does not have an uninstall target, however we have added a one. Simply run make uninstall (or DESTDIR=mydestdir make uninstall if a DESTDIR was used) and it will remove all the files that cmake installed.
-It's also possible to do this manually. The file install_manifest.txt contains the list of files installed to the system. This can be used in many ways to delete all files installed by cmake, such as: xargs rm < install_manifest.txt
-
-Contributions:
---------------
-Please visit our GitHub at http://github.com/ZoneMinder/ZoneMinder
-
-
diff --git a/NEWS b/NEWS
deleted file mode 100644
index 3160e2dce..000000000
--- a/NEWS
+++ /dev/null
@@ -1 +0,0 @@
-Please see README file.
diff --git a/README.md b/README.md
index 3bdf1ffd1..b9d215a40 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,10 @@
ZoneMinder
==========
-[](https://travis-ci.org/ZoneMinder/zoneminder) [](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
+[](https://travis-ci.org/ZoneMinder/zoneminder)
+[](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
+[](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
+[](https://webchat.freenode.net/?channels=zoneminder)
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
@@ -29,7 +32,7 @@ Better methods exist today that do much of this for you. The current development
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
-- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
+- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor)
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
- Fedora via [RPM Fusion](http://rpmfusion.org)
@@ -40,7 +43,7 @@ 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 a ZoneMinder Package
+### 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.
@@ -51,9 +54,9 @@ Lastly, if you desire to build a development snapshot from the master branch, it
Please visit our [ReadtheDocs site](https://zoneminder.readthedocs.org/en/stable/installationguide/index.html) for distro specific instructions.
### Package Maintainers
-Many of the ZoneMinder configration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source.
+Many of the ZoneMinder configuration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source.
-For example, let's say I have created a new ZoneMinder package that contains the cambolzola javascript file. However, by default cambozola support is turned off. To fix that, add this to the pacakging script:
+For example, let's say I have created a new ZoneMinder package that contains the cambozola javascript file. However, by default cambozola support is turned off. To fix that, add this to the packaging script:
```bash
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
```
diff --git a/TODO b/TODO
deleted file mode 100644
index f449b2a7e..000000000
--- a/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-Please see README.md file.
-
diff --git a/cmake/Modules/Pod2Man.cmake b/cmake/Modules/Pod2Man.cmake
index 6804fab53..734be239b 100644
--- a/cmake/Modules/Pod2Man.cmake
+++ b/cmake/Modules/Pod2Man.cmake
@@ -21,7 +21,7 @@
# To use it, include this file in CMakeLists.txt and
# invoke POD2MAN()
-MACRO(POD2MAN PODFILE MANFILE SECTION)
+MACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX)
FIND_PROGRAM(POD2MAN pod2man)
FIND_PROGRAM(GZIP gzip)
@@ -58,9 +58,9 @@ MACRO(POD2MAN PODFILE MANFILE SECTION)
INSTALL(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz
- DESTINATION share/man/man${SECTION}
+ DESTINATION ${MANPAGE_DEST_PREFIX}/man${SECTION}
)
-ENDMACRO(POD2MAN PODFILE MANFILE SECTION)
+ENDMACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX)
MACRO(ADD_MANPAGE_TARGET)
# It is not possible add a dependency to target 'install'
diff --git a/conf.d/01-system-paths.conf.in b/conf.d/01-system-paths.conf.in
index 9f45abdbd..e1aaf0bef 100644
--- a/conf.d/01-system-paths.conf.in
+++ b/conf.d/01-system-paths.conf.in
@@ -16,11 +16,6 @@
# The web account user must have full read/write permission to this folder.
ZM_DIR_EVENTS=@ZM_DIR_EVENTS@
-# Full path to the folder images, not directly associated with events,
-# are recorded to.
-# The web account user must have full read/write permission to this folder.
-ZM_DIR_IMAGES=@ZM_DIR_IMAGES@
-
# Foldername under the webroot where ZoneMinder looks for optional sound files
# to play when an alarm is detected.
ZM_DIR_SOUNDS=@ZM_DIR_SOUNDS@
@@ -50,4 +45,4 @@ ZM_PATH_SWAP=@ZM_TMPDIR@
# Full path to optional arp binary
# ZoneMinder will find the arp binary automatically on most systems
-ZM_PATH_ARP=@ZM_PATH_ARP@
+ZM_PATH_ARP="@ZM_PATH_ARP@"
diff --git a/db/triggers.sql b/db/triggers.sql
index 2f13be435..87c8465b4 100644
--- a/db/triggers.sql
+++ b/db/triggers.sql
@@ -4,8 +4,8 @@ DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
FOR EACH ROW BEGIN
UPDATE Monitors SET
- HourEvents = COALESCE(HourEvents,1)-1,
- HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0),
+ HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
END;
//
@@ -20,23 +20,21 @@ FOR EACH ROW
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
- UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
- UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
-DELIMITER ;
-delimiter //
DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
FOR EACH ROW BEGIN
UPDATE Monitors SET
- DayEvents = COALESCE(DayEvents,1)-1,
- DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0),
+ DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
END;
//
@@ -50,10 +48,10 @@ FOR EACH ROW
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
- UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
- UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
@@ -64,8 +62,8 @@ DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
FOR EACH ROW BEGIN
UPDATE Monitors SET
- WeekEvents = COALESCE(WeekEvents,1)-1,
- WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0),
+ WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
END;
//
@@ -79,10 +77,10 @@ FOR EACH ROW
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
- UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
- UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
@@ -92,8 +90,8 @@ DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
FOR EACH ROW BEGIN
UPDATE Monitors SET
- MonthEvents = COALESCE(MonthEvents,1)-1,
- MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0),
+ MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
END;
//
@@ -107,10 +105,10 @@ FOR EACH ROW
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
- UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
ELSE
- UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
@@ -128,14 +126,14 @@ BEGIN
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN
- UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId;
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId;
END IF;
ELSE
IF ( NEW.DiskSpace ) THEN
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
END IF;
IF ( OLD.DiskSpace ) THEN
- UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId;
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId;
END IF;
END IF;
@@ -150,12 +148,16 @@ BEGIN
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
- UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
+ UPDATE Monitors
+ SET
+ ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0),
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
ELSE
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Monitors SET
- ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
END IF;
END IF;
@@ -164,18 +166,18 @@ BEGIN
END IF;
IF ( diff ) THEN
- UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
+ UPDATE Monitors
+ SET
+ TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
END IF;
END;
//
-delimiter ;
+DROP TRIGGER IF EXISTS event_insert_trigger//
-DROP TRIGGER IF EXISTS event_insert_trigger;
-
-delimiter //
/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
* The DiskSpace will get update in the Event Update Trigger
*/
@@ -203,7 +205,7 @@ CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
FOR EACH ROW
BEGIN
IF ( OLD.DiskSpace ) THEN
- UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId;
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId;
END IF;
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
DELETE FROM Events_Day WHERE EventId=OLD.Id;
@@ -212,15 +214,15 @@ BEGIN
IF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET
- ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
- ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
- TotalEvents = COALESCE(TotalEvents,1) - 1,
- TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
+ ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0),
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0),
+ TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0),
+ TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
- TotalEvents = COALESCE(TotalEvents,1)-1,
- TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0),
+ TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId;
END IF;
END;
diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in
index 83b75f768..6b561a116 100644
--- a/db/zm_create.sql.in
+++ b/db/zm_create.sql.in
@@ -63,11 +63,12 @@ DROP TABLE IF EXISTS `Controls`;
CREATE TABLE `Controls` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
- `Type` enum('Local','Remote','Ffmpeg','Libvlc','cURL','WebSite') NOT NULL default 'Local',
+ `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') 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',
`CanReset` tinyint(3) unsigned NOT NULL default '0',
+ `CanReboot` tinyint(3) unsigned NOT NULL default '0',
`CanZoom` tinyint(3) unsigned NOT NULL default '0',
`CanAutoZoom` tinyint(3) unsigned NOT NULL default '0',
`CanZoomAbs` tinyint(3) unsigned NOT NULL default '0',
@@ -185,6 +186,7 @@ CREATE TABLE `Events` (
`Id` bigint unsigned NOT NULL auto_increment,
`MonitorId` int(10) unsigned NOT NULL default '0',
`StorageId` smallint(5) unsigned default 0,
+ `SecondaryStorageId` smallint(5) unsigned default 0,
`Name` varchar(64) NOT NULL default '',
`Cause` varchar(32) NOT NULL default '',
`StartTime` datetime default NULL,
@@ -207,7 +209,7 @@ CREATE TABLE `Events` (
`Executed` tinyint(3) unsigned NOT NULL default '0',
`Notes` text,
`StateId` int(10) unsigned NOT NULL,
- `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
+ `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0',
`DiskSpace` bigint unsigned default NULL,
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`Locked` BOOLEAN NOT NULL DEFAULT False,
@@ -223,7 +225,7 @@ CREATE TABLE `Events_Hour` (
`EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
- `DiskSpace` bigint unsigned default NULL,
+ `DiskSpace` bigint default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Hour_MonitorId_idx` (`MonitorId`),
KEY `Events_Hour_StartTime_idx` (`StartTime`)
@@ -234,7 +236,7 @@ CREATE TABLE `Events_Day` (
`EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
- `DiskSpace` bigint unsigned default NULL,
+ `DiskSpace` bigint default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Day_MonitorId_idx` (`MonitorId`),
KEY `Events_Day_StartTime_idx` (`StartTime`)
@@ -245,7 +247,7 @@ CREATE TABLE `Events_Week` (
`EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
- `DiskSpace` bigint unsigned default NULL,
+ `DiskSpace` bigint default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Week_MonitorId_idx` (`MonitorId`),
KEY `Events_Week_StartTime_idx` (`StartTime`)
@@ -256,7 +258,7 @@ CREATE TABLE `Events_Month` (
`EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
- `DiskSpace` bigint unsigned default NULL,
+ `DiskSpace` bigint default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Month_MonitorId_idx` (`MonitorId`),
KEY `Events_Month_StartTime_idx` (`StartTime`)
@@ -267,7 +269,7 @@ DROP TABLE IF EXISTS `Events_Archived`;
CREATE TABLE `Events_Archived` (
`EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
- `DiskSpace` bigint unsigned default NULL,
+ `DiskSpace` bigint default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Archived_MonitorId_idx` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@@ -280,7 +282,7 @@ DROP TABLE IF EXISTS `Filters`;
CREATE TABLE `Filters` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
- `Query` text NOT NULL,
+ `Query_json` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
`AutoUpload` tinyint(3) unsigned NOT NULL default '0',
@@ -291,6 +293,8 @@ CREATE TABLE `Filters` (
`AutoDelete` tinyint(3) unsigned NOT NULL default '0',
`AutoMove` tinyint(3) unsigned NOT NULL default '0',
`AutoMoveTo` smallint(5) unsigned NOT NULL default 0,
+ `AutoCopy` tinyint(3) unsigned NOT NULL default '0',
+ `AutoCopyTo` smallint(5) unsigned NOT NULL default 0,
`UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0',
`Background` tinyint(1) unsigned NOT NULL default '0',
`Concurrent` tinyint(1) unsigned NOT NULL default '0',
@@ -350,6 +354,7 @@ CREATE INDEX `Groups_Monitors_MonitorId_idx` ON `Groups_Monitors` (`MonitorId`);
DROP TABLE IF EXISTS `Logs`;
CREATE TABLE `Logs` (
+ `Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`TimeKey` decimal(16,6) NOT NULL,
`Component` varchar(32) NOT NULL,
`ServerId` int(10) unsigned,
@@ -359,6 +364,7 @@ CREATE TABLE `Logs` (
`Message` text NOT NULL,
`File` varchar(255) DEFAULT NULL,
`Line` smallint(5) unsigned DEFAULT NULL,
+ PRIMARY KEY (`Id`),
KEY `TimeKey` (`TimeKey`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@@ -397,7 +403,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','NVSocket') NOT NULL default 'Local',
+ `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL default 'Local',
`Device` tinytext,
`Channel` tinyint(3) unsigned default NULL,
`Format` int(10) unsigned default NULL,
@@ -410,7 +416,7 @@ CREATE TABLE `MonitorPresets` (
`Width` smallint(5) unsigned default NULL,
`Height` smallint(5) unsigned default NULL,
`Palette` int(10) unsigned default NULL,
- `MaxFPS` decimal(5,2) default NULL,
+ `MaxFPS` decimal(5,3) default NULL,
`Controllable` tinyint(3) unsigned NOT NULL default '0',
`ControlId` varchar(16) default NULL,
`ControlDevice` varchar(255) default NULL,
@@ -428,9 +434,10 @@ DROP TABLE IF EXISTS `Monitors`;
CREATE TABLE `Monitors` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
+ `Notes` TEXT,
`ServerId` int(10) unsigned,
`StorageId` smallint(5) unsigned default 0,
- `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
+ `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') 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),
@@ -453,8 +460,10 @@ CREATE TABLE `Monitors` (
`Height` smallint(5) unsigned NOT NULL default '0',
`Colours` tinyint(3) unsigned NOT NULL default '1',
`Palette` int(10) unsigned NOT NULL default '0',
- `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
+ `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0',
`Deinterlacing` int(10) unsigned NOT NULL default '0',
+ `DecoderHWAccelName` varchar(64),
+ `DecoderHWAccelDevice` varchar(255),
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
`OutputCodec` enum('h264','mjpeg','mpeg1','mpeg2'),
@@ -478,6 +487,7 @@ CREATE TABLE `Monitors` (
`StreamReplayBuffer` int(10) unsigned NOT NULL default '1000',
`AlarmFrameCount` smallint(5) unsigned NOT NULL default '1',
`SectionLength` int(10) unsigned NOT NULL default '600',
+ `MinSectionLength` int(10) unsigned NOT NULL default '10',
`FrameSkip` smallint(5) unsigned NOT NULL default '0',
`MotionFrameSkip` smallint(5) unsigned NOT NULL default '0',
`AnalysisFPSLimit` decimal(5,2) default NULL,
@@ -496,9 +506,9 @@ CREATE TABLE `Monitors` (
`TrackDelay` smallint(5) unsigned,
`ReturnLocation` tinyint(3) NOT NULL default '-1',
`ReturnDelay` smallint(5) unsigned,
- `DefaultView` enum('Events','Control') NOT NULL default 'Events',
`DefaultRate` smallint(5) unsigned NOT NULL default '100',
`DefaultScale` smallint(5) unsigned NOT NULL default '100',
+ `DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto',
`SignalCheckPoints` INT UNSIGNED NOT NULL default '0',
`SignalCheckColour` varchar(32) NOT NULL default '#0000BE',
`WebColour` varchar(32) NOT NULL default 'red',
@@ -517,6 +527,7 @@ CREATE TABLE `Monitors` (
`ArchivedEvents` int(10) default NULL,
`ArchivedEventDiskSpace` bigint default NULL,
`ZoneCount` TINYINT NOT NULL DEFAULT 0,
+ `Refresh` int(10) unsigned default NULL,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@@ -555,7 +566,12 @@ INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1');
DROP TABLE IF EXISTS `Servers`;
CREATE TABLE `Servers` (
`Id` int(10) unsigned NOT NULL auto_increment,
+ `Protocol` TEXT,
`Hostname` TEXT,
+ `Port` INTEGER UNSIGNED,
+ `PathToIndex` TEXT,
+ `PathToZMS` TEXT,
+ `PathToApi` TEXT,
`Name` varchar(64) NOT NULL default '',
`State_Id` int(10) unsigned,
`Status` enum('Unknown','NotRunning','Running') NOT NULL default 'Unknown',
@@ -567,6 +583,7 @@ CREATE TABLE `Servers` (
`zmstats` BOOLEAN NOT NULL DEFAULT FALSE,
`zmaudit` BOOLEAN NOT NULL DEFAULT FALSE,
`zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE,
+ `zmeventnotification` BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@@ -578,9 +595,10 @@ CREATE INDEX `Servers_Name_idx` ON `Servers` (`Name`);
DROP TABLE IF EXISTS `Stats`;
CREATE TABLE `Stats` (
+ `Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`MonitorId` int(10) unsigned NOT NULL default '0',
`ZoneId` int(10) unsigned NOT NULL default '0',
- `EventId` int(10) unsigned NOT NULL default '0',
+ `EventId` BIGINT UNSIGNED NOT NULL,
`FrameId` int(10) unsigned NOT NULL default '0',
`PixelDiff` tinyint(3) unsigned NOT NULL default '0',
`AlarmPixels` int(10) unsigned NOT NULL default '0',
@@ -594,6 +612,7 @@ CREATE TABLE `Stats` (
`MinY` smallint(5) unsigned NOT NULL default '0',
`MaxY` smallint(5) unsigned NOT NULL default '0',
`Score` smallint(5) unsigned NOT NULL default '0',
+ PRIMARY KEY (`Id`),
KEY `EventId` (`EventId`),
KEY `MonitorId` (`MonitorId`),
KEY `ZoneId` (`ZoneId`)
@@ -631,7 +650,9 @@ CREATE TABLE `Users` (
`Devices` enum('None','View','Edit') NOT NULL default 'None',
`System` enum('None','View','Edit') NOT NULL default 'None',
`MaxBandwidth` varchar(16),
- `MonitorIds` tinytext,
+ `MonitorIds` text,
+ `TokenMinExpiry` BIGINT UNSIGNED NOT NULL DEFAULT 0,
+ `APIEnabled` tinyint(3) UNSIGNED NOT NULL default 1,
PRIMARY KEY (`Id`),
UNIQUE KEY `UC_Username` (`Username`)
) ENGINE=@ZM_MYSQL_ENGINE@;
@@ -732,51 +753,69 @@ insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, N
--
-- Create a default admin user.
--
-insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','','');
+insert into Users VALUES (NULL,'admin','$2b$12$NHZsm6AM2f2LQVROriz79ul3D6DnmFiZC.ZK5eqbF.ZWfwH9bqUJ6','',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','','',0,1);
--
-- Add a sample filter to purge the oldest 100 events when the disk is 95% full
--
-insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',0/*AutoArchive*/,0/*AutoVideo*/,0/*AutoUpload*/,0/*AutoEmail*/,0/*AutoMessage*/,0/*AutoExecute*/,'',1/*AutoDelete*/,0/*AutoMove*/,0/*MoveTo*/,0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/);
-insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',0,0,0,0,0,0,'',0,0,0,1,1,0);
+insert into Filters values (NULL,'PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":100,"sort_asc":1}',
+ 0/*AutoArchive*/,
+ 0/*AutoVideo*/,
+ 0/*AutoUpload*/,
+ 0/*AutoEmail*/,
+ 0/*AutoMessage*/,
+ 0/*AutoExecute*/,'',
+ 1/*AutoDelete*/,
+ 0/*AutoMove*/,0/*MoveTo*/,
+ 0/*AutoCopy*/,0/*CopyTo*/,
+ 0/*UpdateDiskSpace*/,1/*Background*/,0/*Concurrent*/);
+insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpace","op":"IS","val":"NULL"}]}',0,0,0,0,0,0,'',0,0,0,0,0,1,1,0);
--
-- Add in some sample control protocol definitions
--
-INSERT INTO Controls VALUES (NULL,'Pelco-D','Local','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
-INSERT INTO Controls VALUES (NULL,'Pelco-P','Local','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
-INSERT INTO Controls VALUES (NULL,'Sony VISCA','Local','Visca',1,1,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0);
-INSERT INTO Controls VALUES (NULL,'Axis API v2','Remote','AxisV2',0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0);
-INSERT INTO Controls VALUES (NULL,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
-INSERT INTO Controls VALUES (NULL,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
-INSERT INTO Controls VALUES (NULL,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
-INSERT INTO Controls VALUES (NULL,'Pelco-D','Ffmpeg','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
-INSERT INTO Controls VALUES (NULL,'Pelco-P','Ffmpeg','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
-INSERT INTO Controls VALUES (NULL,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'Foscam FI8908W','Remote','FI8908W',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'WanscamPT','Remote','Wanscam',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO Controls VALUES (NULL,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 1, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0);
-INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0);
-INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
-INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'Pelco-D','Local','PelcoD',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
+INSERT INTO Controls VALUES (NULL,'Pelco-P','Local','PelcoP',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
+INSERT INTO Controls VALUES (NULL,'Sony VISCA','Local','Visca',1,1,0,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0);
+INSERT INTO Controls VALUES (NULL,'Axis API v2','Remote','AxisV2',0,0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0);
+INSERT INTO Controls VALUES (NULL,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
+INSERT INTO Controls VALUES (NULL,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
+INSERT INTO Controls VALUES (NULL,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
+INSERT INTO Controls VALUES (NULL,'Pelco-D','Ffmpeg','PelcoD',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
+INSERT INTO Controls VALUES (NULL,'Pelco-P','Ffmpeg','PelcoP',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
+INSERT INTO Controls VALUES (NULL,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'Foscam FI8908W','Remote','FI8908W',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'WanscamPT','Remote','Wanscam',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO Controls VALUES (NULL,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 0,1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 1, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0);
+INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 0,1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0);
+INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 1, 0,1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'D-Link DCS-5020L','Remote','DCS5020L',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,24,1,0,1,1,1,0,1,0,1,0,0,1,30,0,0,0,0,0,1,0,0,1,30,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
+INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0);
+INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5);
--
-- Add some monitor preset values
@@ -803,6 +842,8 @@ INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
+INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
+INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple',':@','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,':@',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
@@ -819,6 +860,7 @@ INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
+INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video>','0',255,'http','simple','','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
diff --git a/db/zm_update-1.28.99.sql b/db/zm_update-1.28.99.sql
index 5b342a811..ccbc9c059 100644
--- a/db/zm_update-1.28.99.sql
+++ b/db/zm_update-1.28.99.sql
@@ -322,7 +322,7 @@ WHERE NOT EXISTS (
--
-- Hide USE_DEEP_STORAGE from user to prevent accidental event loss
--
-UPDATE `zm`.`Config` SET `Category`='hidden' WHERE `Name`='ZM_USE_DEEP_STORAGE';
+UPDATE Config SET Category='hidden' WHERE Name='ZM_USE_DEEP_STORAGE';
--
-- Add Id column to State
diff --git a/db/zm_update-1.31.43.sql b/db/zm_update-1.31.43.sql
index d8d6eaefd..c97405903 100644
--- a/db/zm_update-1.31.43.sql
+++ b/db/zm_update-1.31.43.sql
@@ -5,7 +5,7 @@
-- Add Refresh column to Monitors table
--
-ALTER TABLE `zm`.`Monitors`
+ALTER TABLE `Monitors`
CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ;
SET @s = (SELECT IF(
@@ -16,7 +16,7 @@ SET @s = (SELECT IF(
AND column_name = 'Refresh'
) > 0,
"SELECT 'Column Refresh exists in Monitors'",
-"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL"
+"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`"
));
PREPARE stmt FROM @s;
diff --git a/db/zm_update-1.31.45.sql b/db/zm_update-1.31.45.sql
new file mode 100644
index 000000000..c3be6c4cd
--- /dev/null
+++ b/db/zm_update-1.31.45.sql
@@ -0,0 +1,23 @@
+--
+-- This updates a 1.31.44 database to 1.31.45
+--
+-- Add WebSite enum to Monitor.Type
+-- Add Refresh column to Monitors table
+
+-- This is the same as the update to 1.31.43, but due to Refresh not being added to zm_create.sql.in we need to have it
+-- again in order to fix people who did a fresh install from 1.31.43 or 1.31.44.
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'Monitors'
+ AND table_schema = DATABASE()
+ AND column_name = 'Refresh'
+ ) > 0,
+"SELECT 'Column Refresh exists in Monitors'",
+"ALTER TABLE Monitors ADD `Refresh` int(10) unsigned default NULL AFTER `ZoneCount`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.31.46.sql b/db/zm_update-1.31.46.sql
new file mode 100644
index 000000000..8c9767e75
--- /dev/null
+++ b/db/zm_update-1.31.46.sql
@@ -0,0 +1,2 @@
+ALTER TABLE Stats MODIFY COLUMN EventId bigint unsigned NOT NULL;
+
diff --git a/db/zm_update-1.31.47.sql b/db/zm_update-1.31.47.sql
new file mode 100644
index 000000000..714afd133
--- /dev/null
+++ b/db/zm_update-1.31.47.sql
@@ -0,0 +1 @@
+ALTER TABLE Frames MODIFY COLUMN EventId bigint unsigned NOT NULL;
diff --git a/db/zm_update-1.32.0.sql b/db/zm_update-1.32.0.sql
new file mode 100644
index 000000000..f0ff1719b
--- /dev/null
+++ b/db/zm_update-1.32.0.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.31.47 database to 1.32.0
+--
+-- No changes required
+--
diff --git a/db/zm_update-1.32.1.sql b/db/zm_update-1.32.1.sql
new file mode 100644
index 000000000..ed9a1879b
--- /dev/null
+++ b/db/zm_update-1.32.1.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.32.0 database to 1.32.1
+--
+-- No changes required
+--
diff --git a/db/zm_update-1.32.2.sql b/db/zm_update-1.32.2.sql
new file mode 100644
index 000000000..0bb156dad
--- /dev/null
+++ b/db/zm_update-1.32.2.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.32.1 database to 1.32.2
+--
+-- No changes required
+--
diff --git a/db/zm_update-1.32.3.sql b/db/zm_update-1.32.3.sql
new file mode 100644
index 000000000..6c3e2c47f
--- /dev/null
+++ b/db/zm_update-1.32.3.sql
@@ -0,0 +1,362 @@
+--
+-- This updates a 1.32.2 database to 1.32.3
+--
+
+delimiter //
+DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
+CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ HourEvents = COALESCE(HourEvents,1)-1,
+ HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
+
+CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+//
+DELIMITER ;
+
+delimiter //
+DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
+CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ DayEvents = COALESCE(DayEvents,1)-1,
+ DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Day_update_trigger;
+CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+
+DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
+CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ WeekEvents = COALESCE(WeekEvents,1)-1,
+ WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Week_update_trigger;
+CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
+CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ MonthEvents = COALESCE(MonthEvents,1)-1,
+ MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Month_update_trigger;
+CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+drop procedure if exists update_storage_stats//
+
+drop trigger if exists event_update_trigger//
+
+CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
+FOR EACH ROW
+BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( NEW.StorageId = OLD.StorageID ) THEN
+ IF ( diff ) THEN
+ UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId;
+ END IF;
+ ELSE
+ IF ( NEW.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
+ END IF;
+ IF ( OLD.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId;
+ END IF;
+ END IF;
+
+ UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+
+ IF ( NEW.Archived != OLD.Archived ) THEN
+ IF ( NEW.Archived ) THEN
+ INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
+ UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
+ ELSEIF ( OLD.Archived ) THEN
+ DELETE FROM Events_Archived WHERE EventId=OLD.Id;
+ UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
+ ELSE
+ IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
+ UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Monitors SET
+ ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+ END IF;
+ ELSEIF ( NEW.Archived AND diff ) THEN
+ UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ END IF;
+
+ IF ( diff ) THEN
+ UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
+ END IF;
+
+END;
+
+//
+
+delimiter ;
+
+DROP TRIGGER IF EXISTS event_insert_trigger;
+
+delimiter //
+/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
+ * The DiskSpace will get update in the Event Update Trigger
+ */
+CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
+FOR EACH ROW
+ BEGIN
+
+ INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ UPDATE Monitors SET
+ HourEvents = COALESCE(HourEvents,0)+1,
+ DayEvents = COALESCE(DayEvents,0)+1,
+ WeekEvents = COALESCE(WeekEvents,0)+1,
+ MonthEvents = COALESCE(MonthEvents,0)+1,
+ TotalEvents = COALESCE(TotalEvents,0)+1
+ WHERE Id=NEW.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS event_delete_trigger//
+
+CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
+FOR EACH ROW
+BEGIN
+ IF ( OLD.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId;
+ END IF;
+ DELETE FROM Events_Hour WHERE EventId=OLD.Id;
+ DELETE FROM Events_Day WHERE EventId=OLD.Id;
+ DELETE FROM Events_Week WHERE EventId=OLD.Id;
+ DELETE FROM Events_Month WHERE EventId=OLD.Id;
+ IF ( OLD.Archived ) THEN
+ DELETE FROM Events_Archived WHERE EventId=OLD.Id;
+ UPDATE Monitors SET
+ ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
+ ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
+ TotalEvents = COALESCE(TotalEvents,1) - 1,
+ TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+ ELSE
+ UPDATE Monitors SET
+ TotalEvents = COALESCE(TotalEvents,1)-1,
+ TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+END;
+
+//
+
+DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
+CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
+FOR EACH ROW
+ BEGIN
+ UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
+ END
+//
+DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
+CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
+FOR EACH ROW
+ BEGIN
+ UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
+ END
+//
+
+DELIMITER ;
+
+REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day);
+REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week);
+REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month);
+REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1;
+
+UPDATE Monitors INNER JOIN (
+ SELECT MonitorId,
+ COUNT(Id) AS TotalEvents,
+ SUM(DiskSpace) AS TotalEventDiskSpace,
+ SUM(IF(Archived,1,0)) AS ArchivedEvents,
+ SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
+ FROM Events GROUP BY MonitorId
+ ) AS E ON E.MonitorId=Monitors.Id SET
+ Monitors.TotalEvents = E.TotalEvents,
+ Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
+ Monitors.ArchivedEvents = E.ArchivedEvents,
+ Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
+ Monitors.HourEvents = E.HourEvents,
+ Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
+ Monitors.DayEvents = E.DayEvents,
+ Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
+ Monitors.WeekEvents = E.WeekEvents,
+ Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
+ Monitors.MonthEvents = E.MonthEvents,
+ Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;
+
+--
+-- Add Protocol column to Storage
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Servers'
+ AND column_name = 'Protocol'
+ ) > 0,
+"SELECT 'Column Protocol already exists in Servers'",
+"ALTER TABLE Servers ADD `Protocol` TEXT AFTER `Id`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+--
+-- Add PathToIndex column to Storage
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Servers'
+ AND column_name = 'PathToIndex'
+ ) > 0,
+"SELECT 'Column PathToIndex already exists in Servers'",
+"ALTER TABLE Servers ADD `PathToIndex` TEXT AFTER `Hostname`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+--
+-- Add PathToZMS column to Storage
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Servers'
+ AND column_name = 'PathToZMS'
+ ) > 0,
+"SELECT 'Column PathToZMS already exists in Servers'",
+"ALTER TABLE Servers ADD `PathToZMS` TEXT AFTER `PathToIndex`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+--
+-- Add PathToApi column to Storage
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Servers'
+ AND column_name = 'PathToApi'
+ ) > 0,
+"SELECT 'Column PathToApi already exists in Servers'",
+"ALTER TABLE Servers ADD `PathToApi` TEXT AFTER `PathToZMS`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+--
+-- Add Port column to Storage
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Servers'
+ AND column_name = 'Port'
+ ) > 0,
+"SELECT 'Column Port already exists in Servers'",
+"ALTER TABLE Servers ADD `Port` INTEGER UNSIGNED AFTER `Hostname`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.0.sql b/db/zm_update-1.33.0.sql
new file mode 100644
index 000000000..cc7a257c7
--- /dev/null
+++ b/db/zm_update-1.33.0.sql
@@ -0,0 +1,21 @@
+--
+-- This updates a 1.32.3 database to 1.33.0
+--
+--
+-- Remove DefaultView from Monitors table.
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = 'Monitors'
+ AND table_schema = DATABASE()
+ AND column_name = 'DefaultView'
+ ) > 0,
+"ALTER TABLE Monitors DROP COLUMN DefaultView",
+"SELECT 'Column DefaultView no longer exists in Monitors'"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
diff --git a/db/zm_update-1.33.1.sql b/db/zm_update-1.33.1.sql
new file mode 100644
index 000000000..ddac8adae
--- /dev/null
+++ b/db/zm_update-1.33.1.sql
@@ -0,0 +1,16 @@
+--
+-- This updates a 1.33.0 database to 1.33.1
+--
+-- Add WebSite enum to Monitor.Type
+-- Add Refresh column to Monitors table
+--
+
+ALTER TABLE `Monitors`
+CHANGE COLUMN `Type` `Type` ENUM('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL DEFAULT 'Local' ;
+
+ALTER TABLE `MonitorPresets`
+CHANGE COLUMN `Type` `Type` ENUM('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL DEFAULT 'Local' ;
+
+ALTER TABLE `Controls`
+CHANGE COLUMN `Type` `Type` ENUM('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket') NOT NULL DEFAULT 'Local' ;
+
diff --git a/db/zm_update-1.33.10.sql b/db/zm_update-1.33.10.sql
new file mode 100644
index 000000000..8eb8fc548
--- /dev/null
+++ b/db/zm_update-1.33.10.sql
@@ -0,0 +1,12 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Monitors'
+ AND column_name = 'MinSectionLength'
+ ) > 0,
+ "SELECT 'Column MinSectionLength already exists in Monitors'",
+ "ALTER TABLE Monitors ADD `MinSectionLength` int(10) unsigned NOT NULL default '10' AFTER SectionLength"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.11.sql b/db/zm_update-1.33.11.sql
new file mode 100644
index 000000000..57b04ce11
--- /dev/null
+++ b/db/zm_update-1.33.11.sql
@@ -0,0 +1,24 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Monitors'
+ AND column_name = 'DecoderHWAccelName'
+ ) > 0,
+ "SELECT 'Column DecoderHWAccelName already exists in Monitors'",
+ "ALTER TABLE Monitors ADD `DecoderHWAccelName` varchar(64) AFTER `Deinterlacing`"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Monitors'
+ AND column_name = 'DecoderHWAccelDevice'
+ ) > 0,
+ "SELECT 'Column DecoderHWAccelDevice already exists in Monitors'",
+ "ALTER TABLE Monitors ADD `DecoderHWAccelDevice` varchar(255) AFTER `DecoderHWAccelName`"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.12.sql b/db/zm_update-1.33.12.sql
new file mode 100644
index 000000000..8188ad841
--- /dev/null
+++ b/db/zm_update-1.33.12.sql
@@ -0,0 +1,27 @@
+--
+-- Add primary keys for Logs and Stats tables
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Logs'
+ AND column_name = 'Id'
+ ) > 0,
+"SELECT 'Column Id already exists in Logs'",
+"ALTER TABLE `Logs` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`Id`)"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Stats'
+ AND column_name = 'Id'
+ ) > 0,
+"SELECT 'Column Id already exists in Stats'",
+"ALTER TABLE `Stats` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (`Id`)"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.13.sql b/db/zm_update-1.33.13.sql
new file mode 100644
index 000000000..8114205c0
--- /dev/null
+++ b/db/zm_update-1.33.13.sql
@@ -0,0 +1,6 @@
+--
+-- Add primary keys for Logs and Stats tables
+--
+
+SELECT "Modifying Monitors MaxFPS to DECIMAL(5,3)";
+ALTER TABLE `Monitors` MODIFY `MaxFPS` decimal(5,3) default NULL;
diff --git a/db/zm_update-1.33.14.sql b/db/zm_update-1.33.14.sql
new file mode 100644
index 000000000..83d0cfbba
--- /dev/null
+++ b/db/zm_update-1.33.14.sql
@@ -0,0 +1,51 @@
+--
+-- Add CopyTo action to Filters
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Filters'
+ AND column_name = 'AutoCopy'
+ ) > 0,
+"SELECT 'Column AutoCopy already exists in Filters'",
+"ALTER TABLE Filters ADD `AutoCopy` tinyint(3) unsigned NOT NULL default '0' AFTER `AutoMove`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Filters'
+ AND column_name = 'AutoCopyTo'
+ ) > 0,
+"SELECT 'Column AutoCopyTo already exists in Filters'",
+"ALTER TABLE Filters ADD `AutoCopyTo` smallint(5) unsigned NOT NULL default '0' AFTER `AutoCopy`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Filters'
+ AND column_name = 'Query_json'
+ ) > 0,
+"SELECT 'Column Query_json already exists in Filters'",
+"ALTER TABLE `Filters` Change `Query` `Query_json` text NOT NULL"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Events'
+ AND column_name = 'SecondaryStorageId'
+ ) > 0,
+"SELECT 'Column SecondaryStorageId already exists in Events'",
+"ALTER TABLE `Events` ADD `SecondaryStorageId` smallint(5) unsigned default 0 AFTER `StorageId`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.15.sql b/db/zm_update-1.33.15.sql
new file mode 100644
index 000000000..56b26fa2d
--- /dev/null
+++ b/db/zm_update-1.33.15.sql
@@ -0,0 +1,19 @@
+ALTER TABLE Events MODIFY `Orientation` enum('0','90','180','270','hori','vert','ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0';
+UPDATE Events SET Orientation='ROTATE_0' WHERE Orientation='0';
+UPDATE Events SET Orientation='ROTATE_90' WHERE Orientation='90';
+UPDATE Events SET Orientation='ROTATE_180' WHERE Orientation='180';
+UPDATE Events SET Orientation='ROTATE_270' WHERE Orientation='270';
+UPDATE Events SET Orientation='FLIP_HORI' WHERE Orientation='hori';
+UPDATE Events SET Orientation='FLIP_VERT' WHERE Orientation='vert';
+
+ALTER TABLE Events MODIFY `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0';
+
+ALTER TABLE Monitors MODIFY `Orientation` enum('0','90','180','270','hori','vert','ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0';
+UPDATE Monitors SET Orientation='ROTATE_0' WHERE Orientation='0';
+UPDATE Monitors SET Orientation='ROTATE_90' WHERE Orientation='90';
+UPDATE Monitors SET Orientation='ROTATE_180' WHERE Orientation='180';
+UPDATE Monitors SET Orientation='ROTATE_270' WHERE Orientation='270';
+UPDATE Monitors SET Orientation='FLIP_HORI' WHERE Orientation='hori';
+UPDATE Monitors SET Orientation='FLIP_VERT' WHERE Orientation='vert';
+
+ALTER TABLE Monitors MODIFY `Orientation` enum('ROTATE_0','ROTATE_90','ROTATE_180','ROTATE_270','FLIP_HORI','FLIP_VERT') NOT NULL default 'ROTATE_0';
diff --git a/db/zm_update-1.33.16.sql b/db/zm_update-1.33.16.sql
new file mode 100644
index 000000000..87058e3f8
--- /dev/null
+++ b/db/zm_update-1.33.16.sql
@@ -0,0 +1,12 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Monitors'
+ AND column_name = 'Notes'
+ ) > 0,
+ "SELECT 'Column Notes already exists in Monitors'",
+ "ALTER TABLE `Monitors` ADD `Notes` TEXT AFTER `Name`"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.2.sql b/db/zm_update-1.33.2.sql
new file mode 100644
index 000000000..ea0237b7e
--- /dev/null
+++ b/db/zm_update-1.33.2.sql
@@ -0,0 +1,13 @@
+--
+-- This updates a 1.33.0 database to 1.33.1
+--
+-- Add WebSite enum to Monitor.Type
+-- Add Refresh column to Monitors table
+--
+
+ALTER TABLE `Events_Hour` MODIFY DiskSpace BIGINT default NULL;
+ALTER TABLE `Events_Day` MODIFY DiskSpace BIGINT default NULL;
+ALTER TABLE `Events_Week` MODIFY DiskSpace BIGINT default NULL;
+ALTER TABLE `Events_Month` MODIFY DiskSpace BIGINT default NULL;
+ALTER TABLE `Events_Archived` MODIFY DiskSpace BIGINT default NULL;
+
diff --git a/db/zm_update-1.33.3.sql b/db/zm_update-1.33.3.sql
new file mode 100644
index 000000000..5488f809d
--- /dev/null
+++ b/db/zm_update-1.33.3.sql
@@ -0,0 +1,12 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Servers'
+ AND column_name = 'zmeventnotification'
+ ) > 0,
+ "SELECT 'Column zmeventnotification already exists in Servers'",
+ "ALTER TABLE Servers ADD `zmeventnotification` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmtrigger`"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.4.sql b/db/zm_update-1.33.4.sql
new file mode 100644
index 000000000..d9b498bc8
--- /dev/null
+++ b/db/zm_update-1.33.4.sql
@@ -0,0 +1,12 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Monitors'
+ AND column_name = 'DefaultCodec'
+ ) > 0,
+ "SELECT 'Column DefaultCodec already exists in Monitors'",
+ "ALTER TABLE Monitors ADD `DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto' AFTER `DefaultScale`"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.5.sql b/db/zm_update-1.33.5.sql
new file mode 100644
index 000000000..39400b5d8
--- /dev/null
+++ b/db/zm_update-1.33.5.sql
@@ -0,0 +1,12 @@
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Controls'
+ AND column_name = 'CanReboot'
+ ) > 0,
+ "SELECT 'Column CanReboot already exists in Controls'",
+ "ALTER TABLE Controls ADD `CanReboot` tinyint(3) unsigned NOT NULL default '0' AFTER `CanReset`"
+ ));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.33.6.sql b/db/zm_update-1.33.6.sql
new file mode 100644
index 000000000..159f80ac6
--- /dev/null
+++ b/db/zm_update-1.33.6.sql
@@ -0,0 +1 @@
+ALTER TABLE Users MODIFY MonitorIds text;
diff --git a/db/zm_update-1.33.7.sql b/db/zm_update-1.33.7.sql
new file mode 100644
index 000000000..b29fe0e88
--- /dev/null
+++ b/db/zm_update-1.33.7.sql
@@ -0,0 +1,283 @@
+
+delimiter //
+DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
+CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ HourEvents = COALESCE(HourEvents,1)-1,
+ HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
+
+CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+//
+
+DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
+CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0),
+ DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Day_update_trigger;
+CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+
+DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
+CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ WeekEvents = COALESCE(WeekEvents,1)-1,
+ WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Week_update_trigger;
+CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
+CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ MonthEvents = COALESCE(MonthEvents,1)-1,
+ MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Month_update_trigger;
+CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+drop procedure if exists update_storage_stats//
+
+drop trigger if exists event_update_trigger//
+
+CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
+FOR EACH ROW
+BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( NEW.StorageId = OLD.StorageID ) THEN
+ IF ( diff ) THEN
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId;
+ END IF;
+ ELSE
+ IF ( NEW.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
+ END IF;
+ IF ( OLD.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId;
+ END IF;
+ END IF;
+
+ UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+
+ IF ( NEW.Archived != OLD.Archived ) THEN
+ IF ( NEW.Archived ) THEN
+ INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
+ UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
+ ELSEIF ( OLD.Archived ) THEN
+ DELETE FROM Events_Archived WHERE EventId=OLD.Id;
+ UPDATE Monitors
+ SET
+ ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0),
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ ELSE
+ IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
+ UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Monitors SET
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+ END IF;
+ ELSEIF ( NEW.Archived AND diff ) THEN
+ UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ END IF;
+
+ IF ( diff ) THEN
+ UPDATE Monitors
+ SET
+ TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+
+END;
+
+//
+
+DROP TRIGGER IF EXISTS event_insert_trigger//
+
+/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
+ * The DiskSpace will get update in the Event Update Trigger
+ */
+CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
+FOR EACH ROW
+ BEGIN
+
+ INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ UPDATE Monitors SET
+ HourEvents = COALESCE(HourEvents,0)+1,
+ DayEvents = COALESCE(DayEvents,0)+1,
+ WeekEvents = COALESCE(WeekEvents,0)+1,
+ MonthEvents = COALESCE(MonthEvents,0)+1,
+ TotalEvents = COALESCE(TotalEvents,0)+1
+ WHERE Id=NEW.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS event_delete_trigger//
+
+CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
+FOR EACH ROW
+BEGIN
+ IF ( OLD.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId;
+ END IF;
+ DELETE FROM Events_Hour WHERE EventId=OLD.Id;
+ DELETE FROM Events_Day WHERE EventId=OLD.Id;
+ DELETE FROM Events_Week WHERE EventId=OLD.Id;
+ DELETE FROM Events_Month WHERE EventId=OLD.Id;
+ IF ( OLD.Archived ) THEN
+ DELETE FROM Events_Archived WHERE EventId=OLD.Id;
+ UPDATE Monitors SET
+ ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0),
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0),
+ TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0),
+ TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ ELSE
+ UPDATE Monitors SET
+ TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0),
+ TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+END;
+
+//
+
+DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
+CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
+FOR EACH ROW
+ BEGIN
+ UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
+ END
+//
+DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
+CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
+FOR EACH ROW
+ BEGIN
+ UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
+ END
+//
+
+DELIMITER ;
+
+REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour);
+REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day);
+REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week);
+REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month);
+REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1;
+
+UPDATE Monitors INNER JOIN (
+ SELECT MonitorId,
+ COUNT(Id) AS TotalEvents,
+ SUM(DiskSpace) AS TotalEventDiskSpace,
+ SUM(IF(Archived,1,0)) AS ArchivedEvents,
+ SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
+ FROM Events GROUP BY MonitorId
+ ) AS E ON E.MonitorId=Monitors.Id SET
+ Monitors.TotalEvents = E.TotalEvents,
+ Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
+ Monitors.ArchivedEvents = E.ArchivedEvents,
+ Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
+ Monitors.HourEvents = E.HourEvents,
+ Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
+ Monitors.DayEvents = E.DayEvents,
+ Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
+ Monitors.WeekEvents = E.WeekEvents,
+ Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
+ Monitors.MonthEvents = E.MonthEvents,
+ Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;
+
diff --git a/db/zm_update-1.33.8.sql b/db/zm_update-1.33.8.sql
new file mode 100644
index 000000000..a3fed9629
--- /dev/null
+++ b/db/zm_update-1.33.8.sql
@@ -0,0 +1,283 @@
+
+delimiter //
+DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
+CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0),
+ HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
+
+CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+//
+
+DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
+CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0),
+ DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Day_update_trigger;
+CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+
+DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
+CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0),
+ WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Week_update_trigger;
+CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
+CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
+FOR EACH ROW BEGIN
+ UPDATE Monitors SET
+ MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0),
+ MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS Events_Month_update_trigger;
+CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
+FOR EACH ROW
+ BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( diff ) THEN
+ IF ( NEW.MonitorID != OLD.MonitorID ) THEN
+ UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId;
+ UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
+ ELSE
+ UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
+ END IF;
+ END IF;
+ END;
+ //
+
+drop procedure if exists update_storage_stats//
+
+drop trigger if exists event_update_trigger//
+
+CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
+FOR EACH ROW
+BEGIN
+ declare diff BIGINT default 0;
+
+ set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
+ IF ( NEW.StorageId = OLD.StorageID ) THEN
+ IF ( diff ) THEN
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId;
+ END IF;
+ ELSE
+ IF ( NEW.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
+ END IF;
+ IF ( OLD.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId;
+ END IF;
+ END IF;
+
+ UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+
+ IF ( NEW.Archived != OLD.Archived ) THEN
+ IF ( NEW.Archived ) THEN
+ INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
+ UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
+ ELSEIF ( OLD.Archived ) THEN
+ DELETE FROM Events_Archived WHERE EventId=OLD.Id;
+ UPDATE Monitors
+ SET
+ ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0),
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ ELSE
+ IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
+ UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ UPDATE Monitors SET
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+ END IF;
+ ELSEIF ( NEW.Archived AND diff ) THEN
+ UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
+ END IF;
+
+ IF ( diff ) THEN
+ UPDATE Monitors
+ SET
+ TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+
+END;
+
+//
+
+DROP TRIGGER IF EXISTS event_insert_trigger//
+
+/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
+ * The DiskSpace will get update in the Event Update Trigger
+ */
+CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
+FOR EACH ROW
+ BEGIN
+
+ INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
+ UPDATE Monitors SET
+ HourEvents = COALESCE(HourEvents,0)+1,
+ DayEvents = COALESCE(DayEvents,0)+1,
+ WeekEvents = COALESCE(WeekEvents,0)+1,
+ MonthEvents = COALESCE(MonthEvents,0)+1,
+ TotalEvents = COALESCE(TotalEvents,0)+1
+ WHERE Id=NEW.MonitorId;
+END;
+//
+
+DROP TRIGGER IF EXISTS event_delete_trigger//
+
+CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
+FOR EACH ROW
+BEGIN
+ IF ( OLD.DiskSpace ) THEN
+ UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId;
+ END IF;
+ DELETE FROM Events_Hour WHERE EventId=OLD.Id;
+ DELETE FROM Events_Day WHERE EventId=OLD.Id;
+ DELETE FROM Events_Week WHERE EventId=OLD.Id;
+ DELETE FROM Events_Month WHERE EventId=OLD.Id;
+ IF ( OLD.Archived ) THEN
+ DELETE FROM Events_Archived WHERE EventId=OLD.Id;
+ UPDATE Monitors SET
+ ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0),
+ ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0),
+ TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0),
+ TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ ELSE
+ UPDATE Monitors SET
+ TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0),
+ TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
+ WHERE Id=OLD.MonitorId;
+ END IF;
+END;
+
+//
+
+DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
+CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
+FOR EACH ROW
+ BEGIN
+ UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
+ END
+//
+DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
+CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
+FOR EACH ROW
+ BEGIN
+ UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
+ END
+//
+
+DELIMITER ;
+
+REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour);
+REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day);
+REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week);
+REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month);
+REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1;
+
+UPDATE Monitors INNER JOIN (
+ SELECT MonitorId,
+ COUNT(Id) AS TotalEvents,
+ SUM(DiskSpace) AS TotalEventDiskSpace,
+ SUM(IF(Archived,1,0)) AS ArchivedEvents,
+ SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
+ SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
+ FROM Events GROUP BY MonitorId
+ ) AS E ON E.MonitorId=Monitors.Id SET
+ Monitors.TotalEvents = E.TotalEvents,
+ Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
+ Monitors.ArchivedEvents = E.ArchivedEvents,
+ Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
+ Monitors.HourEvents = E.HourEvents,
+ Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
+ Monitors.DayEvents = E.DayEvents,
+ Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
+ Monitors.WeekEvents = E.WeekEvents,
+ Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
+ Monitors.MonthEvents = E.MonthEvents,
+ Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;
+
diff --git a/db/zm_update-1.33.9.sql b/db/zm_update-1.33.9.sql
new file mode 100644
index 000000000..e0d289ba4
--- /dev/null
+++ b/db/zm_update-1.33.9.sql
@@ -0,0 +1,27 @@
+--
+-- Add per user API enable/disable and ability to set a minimum issued time for tokens
+--
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Users'
+ AND column_name = 'TokenMinExpiry'
+ ) > 0,
+"SELECT 'Column TokenMinExpiry already exists in Users'",
+"ALTER TABLE Users ADD `TokenMinExpiry` BIGINT UNSIGNED NOT NULL DEFAULT 0 AFTER `MonitorIds`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
+
+SET @s = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
+ AND table_name = 'Users'
+ AND column_name = 'APIEnabled'
+ ) > 0,
+"SELECT 'Column APIEnabled already exists in Users'",
+"ALTER TABLE Users ADD `APIEnabled` tinyint(3) UNSIGNED NOT NULL default 1 AFTER `TokenMinExpiry`"
+));
+
+PREPARE stmt FROM @s;
+EXECUTE stmt;
diff --git a/db/zm_update-1.34.0.sql b/db/zm_update-1.34.0.sql
new file mode 100644
index 000000000..b8bd3e4ed
--- /dev/null
+++ b/db/zm_update-1.34.0.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.33.16 database to 1.34.0
+--
+-- No changes required
+--
diff --git a/db/zm_update-1.34.1.sql b/db/zm_update-1.34.1.sql
new file mode 100644
index 000000000..65ba2e5ff
--- /dev/null
+++ b/db/zm_update-1.34.1.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.34.0 database to 1.34.1
+--
+-- No changes required
+--
diff --git a/db/zm_update-1.34.2.sql b/db/zm_update-1.34.2.sql
new file mode 100644
index 000000000..1fcc882d2
--- /dev/null
+++ b/db/zm_update-1.34.2.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.34.1 database to 1.34.2
+--
+-- No changes required
+--
diff --git a/db/zm_update-1.34.3.sql b/db/zm_update-1.34.3.sql
new file mode 100644
index 000000000..b84207047
--- /dev/null
+++ b/db/zm_update-1.34.3.sql
@@ -0,0 +1,5 @@
+--
+-- This updates a 1.34.2 database to 1.34.3
+--
+-- No changes required
+--
diff --git a/description-pak b/description-pak
deleted file mode 100644
index 7afaa14a4..000000000
--- a/description-pak
+++ /dev/null
@@ -1 +0,0 @@
-Zoneminder with kfir performances patches and fixes from UnixMedia (nextime)
diff --git a/distros/debian/control b/distros/debian/control
index 9de030e87..6bb59f206 100644
--- a/distros/debian/control
+++ b/distros/debian/control
@@ -13,6 +13,7 @@ Build-Depends: debhelper (>= 9), cmake
, libv4l-dev (>= 0.8.3)
, libbz2-dev
, ffmpeg | libav-tools
+ , net-tools
, libnetpbm10-dev
, libvlccore-dev, libvlc-dev
, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev
@@ -20,11 +21,13 @@ Build-Depends: debhelper (>= 9), cmake
, libphp-serialization-perl
, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl
, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl
- , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl
+ , libmodule-load-perl, libsys-mmap-perl, libjson-maybexs-perl
, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl
, libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl
, libsys-cpu-perl, libsys-meminfo-perl
, libdata-uuid-perl
+ , libssl-dev
+ , libcrypt-eksblowfish-perl, libdata-entropy-perl
Standards-Version: 3.9.4
Package: zoneminder
@@ -36,11 +39,13 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, libphp-serialization-perl
, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl
, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl
- , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl
+ , libmodule-load-perl, libsys-mmap-perl, libjson-maybexs-perl
, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl
, libdata-dump-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
, libpcre3
, ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57
, rsyslog | system-log-daemon
@@ -48,6 +53,9 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, zip
, libvlccore5 | libvlccore7 | libvlccore8, libvlc5
, libpolkit-gobject-1-0, php5-gd
+ , libssl
+ ,libcrypt-eksblowfish-perl, libdata-entropy-perl
+
Recommends: mysql-server | mariadb-server
Description: Video camera security and surveillance solution
ZoneMinder is intended for use in single or multi-camera video security
diff --git a/distros/debian/postinst b/distros/debian/postinst
index 3cd3fd277..36472436a 100644
--- a/distros/debian/postinst
+++ b/distros/debian/postinst
@@ -31,6 +31,10 @@ if [ "$1" = "configure" ]; then
# 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,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else
diff --git a/distros/debian/rules b/distros/debian/rules
index fe725c2d0..13e36d461 100755
--- a/distros/debian/rules
+++ b/distros/debian/rules
@@ -21,11 +21,11 @@ override_dh_auto_configure:
-DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \
-DZM_WEB_USER=www-data \
-DZM_WEB_GROUP=www-data \
- -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
+ -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_CONFIG_DIR="/etc/zm" \
- -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
- -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
- -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
+ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
+ -DZM_PATH_SHUTDOWN="/sbin/shutdown" \
+ -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
override_dh_auto_install:
dh_auto_install --buildsystem=cmake
diff --git a/distros/opensuse/CMakeLists.txt b/distros/opensuse/CMakeLists.txt
index 10654f26d..6ce1a999f 100644
--- a/distros/opensuse/CMakeLists.txt
+++ b/distros/opensuse/CMakeLists.txt
@@ -8,16 +8,6 @@ SET(zmgid_final www)
SET(webroot /srv/www/htdocs)
SET(zm_webdir ${webroot}/zoneminder)
-# Download jscalendar & move files into position
-file(DOWNLOAD http://downloads.sourceforge.net/jscalendar/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip STATUS download_jsc)
-if(download_jsc EQUAL 0)
-message(STATUS "Jscalander successfully downloaded. Installing...")
-execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE unzip_jsc)
-message(STATUS "Status of jscalender script was: ${unzip_jsc}")
-else(download_jsc EQUAL 0)
-message(STATUS "Unable to download optional jscalander. Skipping...")
-endif(download_jsc EQUAL 0)
-
# Create several empty folders
file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
@@ -45,7 +35,3 @@ install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.
install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
-# Install jscalendar
-if(unzip_jsc STREQUAL "")
-install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar)
-endif(unzip_jsc STREQUAL "")
diff --git a/distros/opensuse/jscalendar.sh b/distros/opensuse/jscalendar.sh
deleted file mode 100755
index 80acaafec..000000000
--- a/distros/opensuse/jscalendar.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-unzip -o jscalendar-1.0.zip
-mkdir -v jscalendar-doc
-cd jscalendar-1.0
-mv -v *html *php doc/* README ../jscalendar-doc
-rmdir -v doc
diff --git a/distros/opensuse/zoneminder.cmake.OS13.spec b/distros/opensuse/zoneminder.cmake.OS13.spec
index e1a55d3cb..e1ed14325 100644
--- a/distros/opensuse/zoneminder.cmake.OS13.spec
+++ b/distros/opensuse/zoneminder.cmake.OS13.spec
@@ -16,7 +16,6 @@ Version: 1.27.0
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
-# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/
# Mootools is under the MIT license: http://mootools.net/
License: GPLv2+ and LGPLv2+ and MIT
URL: http://www.zoneminder.com/
@@ -141,7 +140,7 @@ fi
%files
%defattr(-,root,root,-)
-%doc AUTHORS COPYING README.md distros/opensuse/README.OpenSuse distros/opensuse/jscalendar-doc
+%doc AUTHORS COPYING README.md distros/opensuse/README.OpenSuse
%docdir /opt/zoneminder/share/man
%config %attr(640,root,%{zmgid_final}) /etc/zm.conf
%config(noreplace) %attr(644,root,root) /etc/apache2/conf.d/zoneminder.conf
diff --git a/distros/redhat/CMakeLists.txt b/distros/redhat/CMakeLists.txt
index e42c5db61..f1a1bc75b 100644
--- a/distros/redhat/CMakeLists.txt
+++ b/distros/redhat/CMakeLists.txt
@@ -1,4 +1,9 @@
-# CMakeLists.txt for the Redhat/CentOS Target Distro.
+# CMakeLists.txt for the Redhat Target Distros.
+
+#
+# General strategy is to configure and install all files specific to Apache and Nginx
+# Then let the rpm specfile sort them into the appropriate sub-package
+#
# Display a message to show the RHEL build options are being processed.
if(ZM_TARGET_DISTRO MATCHES "^el")
@@ -9,46 +14,62 @@ else(ZM_TARGET_DISTRO MATCHES "^el")
message([WARNING] "Unknown Build Option Detected" ...)
endif(ZM_TARGET_DISTRO MATCHES "^el")
-if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
- message([FATAL_ERROR] "Experimental Nginx support is currently only supported on Fedora" ...)
-endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
+#
+# CONFIGURE STAGE
+#
-# Configure the zoneminder service files
-configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
-if(ZM_WEB_USER STREQUAL "nginx")
- configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
- configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
- configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
- configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
- configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
-else(ZM_WEB_USER STREQUAL "nginx")
- configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
- configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
- configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
-endif(ZM_WEB_USER STREQUAL "nginx")
+# Configure the common zoneminder files
+configure_file(common/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
+configure_file(common/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
+file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events temp)
-# Unpack jscalendar & move files into position
-message(STATUS "Unpacking and Installing jscalendar...")
-execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/misc/jscalendar.sh
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- ERROR_VARIABLE unzip_jsc
- )
-if("${unzip_jsc}" STREQUAL "")
- message(STATUS "jscalendar successfully installed.")
-else("${unzip_jsc}" STREQUAL "")
- message(FATAL_ERROR "\nAn error occurred while jscalendar was being processed:\n${unzip_jsc}")
-endif("${unzip_jsc}" STREQUAL "")
+# Configure the Apache zoneminder files
+configure_file(httpd/zm-httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zm-httpd.conf @ONLY)
+configure_file(httpd/zoneminder.httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.httpd.conf @ONLY)
+configure_file(httpd/zoneminder.httpd.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.httpd.tmpfiles.conf @ONLY)
+configure_file(httpd/com.zoneminder.systemctl.rules.httpd.in ${CMAKE_CURRENT_SOURCE_DIR}/com.zoneminder.systemctl.rules.httpd @ONLY)
-# Create several empty folders
-file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
+# Configure the Nginx zoneminder files
+configure_file(nginx/zm-nginx.conf ${CMAKE_CURRENT_SOURCE_DIR}/zm-nginx.conf COPYONLY)
+configure_file(nginx/zoneminder.nginx.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.nginx.conf @ONLY)
+configure_file(nginx/redirect.nginx.conf ${CMAKE_CURRENT_SOURCE_DIR}/redirect.nginx.conf COPYONLY)
+configure_file(nginx/zoneminder.nginx.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.nginx.tmpfiles.conf @ONLY)
+configure_file(nginx/zm-web-user.conf ${CMAKE_CURRENT_SOURCE_DIR}/zm-web-user.conf COPYONLY)
+configure_file(nginx/zoneminder.php-fpm.conf ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf COPYONLY)
+configure_file(nginx/com.zoneminder.systemctl.rules.nginx ${CMAKE_CURRENT_SOURCE_DIR}/com.zoneminder.systemctl.rules.nginx COPYONLY)
+
+#
+# INSTALLATION STAGE
+#
+
+
+# Install the common zoneminder files
+install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
-# Install the empty folders
install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/cache DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
-install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
+install(DIRECTORY events temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
+
+# Install the Apache zoneminder files
+install(FILES zm-httpd.conf DESTINATION /usr/lib/systemd/system/zoneminder.service.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zoneminder.httpd.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zoneminder.httpd.tmpfiles.conf DESTINATION /usr/lib/tmpfiles.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES com.zoneminder.systemctl.rules.httpd DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+
+# Install the Nginx zoneminder files
+install(FILES zm-nginx.conf DESTINATION /usr/lib/systemd/system/zoneminder.service.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zoneminder.nginx.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES redirect.nginx.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zoneminder.nginx.tmpfiles.conf DESTINATION /usr/lib/tmpfiles.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES com.zoneminder.systemctl.rules.nginx DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zm-web-user.conf DESTINATION /etc/zm/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+
+# Miscellaneous
# Symlink the cake php temp folder to the ZoneMinder temp folder
install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")")
@@ -57,17 +78,5 @@ install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminde
install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")")
# Install auxiliary files
-install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
-install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar)
-
-# Install zoneminder service files
-install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
-install(FILES zoneminder.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
-
-if(ZM_WEB_USER STREQUAL "nginx")
- install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf)
-endif(ZM_WEB_USER STREQUAL "nginx")
-
-install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
-install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
+install(FILES common/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
diff --git a/distros/redhat/misc/redalert.wav b/distros/redhat/common/redalert.wav
similarity index 100%
rename from distros/redhat/misc/redalert.wav
rename to distros/redhat/common/redalert.wav
diff --git a/distros/redhat/common/zoneminder.logrotate.in b/distros/redhat/common/zoneminder.logrotate.in
new file mode 100644
index 000000000..210a84c07
--- /dev/null
+++ b/distros/redhat/common/zoneminder.logrotate.in
@@ -0,0 +1,12 @@
+@ZM_LOGDIR@/*.log {
+ missingok
+ notifempty
+ sharedscripts
+ delaycompress
+ compress
+ postrotate
+ @BINDIR@/zmpkg.pl logrot > /dev/null 2>/dev/null || true
+ endscript
+ daily
+ rotate 7
+}
diff --git a/distros/redhat/systemd/zoneminder.service.in b/distros/redhat/common/zoneminder.service.in
similarity index 76%
rename from distros/redhat/systemd/zoneminder.service.in
rename to distros/redhat/common/zoneminder.service.in
index 68918ab9e..8551a60e2 100644
--- a/distros/redhat/systemd/zoneminder.service.in
+++ b/distros/redhat/common/zoneminder.service.in
@@ -1,13 +1,12 @@
# ZoneMinder systemd unit file for RedHat distros and clones
+# See drop-in folder for additional config directives
[Unit]
Description=ZoneMinder CCTV recording and security system
-After=network.target mariadb.service httpd.service
-Requires=mariadb.service httpd.service
+After=network.target mariadb.service
+Requires=mariadb.service
[Service]
-User=@WEB_USER@
-Group=@WEB_GROUP@
Type=forking
ExecStart=@BINDIR@/zmpkg.pl start
ExecReload=@BINDIR@/zmpkg.pl restart
diff --git a/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in b/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in
new file mode 100644
index 000000000..d101dad69
--- /dev/null
+++ b/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in
@@ -0,0 +1,7 @@
+polkit.addRule(function(action, subject) {
+ if (action.id == "com.zoneminder.policykit.pkexec.run-zmsystemctl" &&
+ subject.user != "@WEB_USER@") {
+ return polkit.Result.NO;
+ }
+
+});
diff --git a/distros/redhat/httpd/zm-httpd.conf.in b/distros/redhat/httpd/zm-httpd.conf.in
new file mode 100644
index 000000000..1a00d8d9b
--- /dev/null
+++ b/distros/redhat/httpd/zm-httpd.conf.in
@@ -0,0 +1,8 @@
+# Additional config directives for ZoneMinder with Apache web server
+
+[Unit]
+After=httpd.service
+
+[Service]
+User=@WEB_USER@
+Group=@WEB_GROUP@
diff --git a/distros/redhat/apache/zoneminder.conf.in b/distros/redhat/httpd/zoneminder.httpd.conf.in
similarity index 100%
rename from distros/redhat/apache/zoneminder.conf.in
rename to distros/redhat/httpd/zoneminder.httpd.conf.in
diff --git a/distros/redhat/systemd/zoneminder.tmpfiles.in b/distros/redhat/httpd/zoneminder.httpd.tmpfiles.in
similarity index 79%
rename from distros/redhat/systemd/zoneminder.tmpfiles.in
rename to distros/redhat/httpd/zoneminder.httpd.tmpfiles.in
index 21e6119fe..b403fb24d 100644
--- a/distros/redhat/systemd/zoneminder.tmpfiles.in
+++ b/distros/redhat/httpd/zoneminder.httpd.tmpfiles.in
@@ -2,4 +2,4 @@ D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_CACHEDIR@ 0755 @WEB_USER@ @WEB_GROUP@
d @ZM_DIR_EVENTS@ 0755 @WEB_USER@ @WEB_GROUP@
-D @ZM_DIR_IMAGES@ 0755 @WEB_USER@ @WEB_GROUP@
+
diff --git a/distros/redhat/misc/jscalendar-1.0.zip b/distros/redhat/misc/jscalendar-1.0.zip
deleted file mode 100644
index f33dc072c..000000000
Binary files a/distros/redhat/misc/jscalendar-1.0.zip and /dev/null differ
diff --git a/distros/redhat/misc/jscalendar.sh b/distros/redhat/misc/jscalendar.sh
deleted file mode 100755
index 068e47bf9..000000000
--- a/distros/redhat/misc/jscalendar.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-unzip -o misc/jscalendar-1.0.zip
-mkdir -v jscalendar-doc
-cd jscalendar-1.0
-mv -v *html *php doc/* README ../jscalendar-doc
-rmdir -v doc
diff --git a/distros/redhat/misc/local_zoneminder.te b/distros/redhat/misc/local_zoneminder.te
deleted file mode 100644
index c49505785..000000000
--- a/distros/redhat/misc/local_zoneminder.te
+++ /dev/null
@@ -1,125 +0,0 @@
-module local_zoneminder 1.2;
-
-require {
- type afs_ka_port_t;
- type netsupport_port_t;
- type port_t;
- type presence_port_t;
- type postfix_master_t;
- type postfix_qmgr_t;
- type postfix_pickup_t;
- type httpd_t;
- type var_lib_t;
- type ionixnetmon_port_t;
- type glance_port_t;
- type mmcc_port_t;
- type postfix_master_t;
- type commplex_port_t;
- type syslogd_port_t;
- type dcc_port_t;
- type sip_port_t;
- type amqp_port_t;
- type condor_port_t;
- type afs_fs_port_t;
- type nodejs_debug_port_t;
- type httpd_var_lib_t;
- type websm_port_t;
- type afs_pt_port_t;
- type postfix_qmgr_t;
- type git_port_t;
- type ipp_port_t;
- type aol_port_t;
- type unconfined_t;
- type kernel_t;
- type init_t;
- type auditd_t;
- type mysqld_t;
- type httpd_log_t;
- type syslogd_t;
- type httpd_t;
- type initrc_state_t;
- type initrc_t;
- type var_lib_t;
- type udev_t;
- type mysqld_safe_t;
- type sshd_t;
- type crond_t;
- type getty_t;
- type httpd_var_lib_t;
- type initrc_var_run_t;
- type tmpfs_t;
- type dhcpc_t;
- type v4l_device_t;
- type file_t;
- class sock_file { write create unlink };
- class unix_stream_socket { read connectto };
- class lnk_file { write create getattr read lock unlink };
- class dir {search getattr };
- class udp_socket name_bind;
- class file { write getattr read lock unlink open };
- class shm { unix_read unix_write associate read write getattr };
- class chr_file getattr;
-}
-
-#============= httpd_t ==============
-allow httpd_t auditd_t:dir { search getattr };
-allow httpd_t auditd_t:file { read getattr open };
-allow httpd_t crond_t:dir { search getattr };
-allow httpd_t crond_t:file { read getattr open };
-allow httpd_t dhcpc_t:dir { search getattr };
-allow httpd_t dhcpc_t:file { read getattr open };
-allow httpd_t getty_t:dir { search getattr };
-allow httpd_t getty_t:file { read getattr open };
-allow httpd_t httpd_log_t:file write;
-allow httpd_t httpd_var_lib_t:lnk_file { write getattr read lock unlink };
-allow httpd_t init_t:dir { search getattr };
-allow httpd_t init_t:file { read getattr open };
-#!!!! The source type 'httpd_t' can write to a 'file' of the following types:
-#squirrelmail_spool_t, mirrormanager_var_run_t, dirsrvadmin_config_t, httpd_lock_t, httpd_tmp_t, dirsrv_config_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, httpd_squirrelmail_t, dirsrv_var_run_t, dirsrv_var_log_t, httpd_var_lib_t, httpd_var_run_t, zarafa_var_lib_t, httpd_prewikka_rw_content_t, httpd_mediawiki_rw_content_t, httpd_squid_rw_content_t, passenger_var_run_t, httpd_smokeping_cgi_rw_content_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_rw_content_t, httpd_w3c_validator_rw_content_t, httpd_collectd_rw_content_t, cluster_var_lib_t, cluster_var_run_t, httpd_user_rw_content_t, httpd_awstats_rw_content_t, httpdcontent, root_t, httpd_cobbler_rw_content_t, httpd_munin_rw_content_t, cluster_conf_t, httpd_bugzilla_rw_content_t, passenger_tmp_t, httpd_cvs_rw_content_t, httpd_git_rw_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t, httpd_nagios_rw_content_t, httpd_apcupsd_cgi_rw_content_t, httpd_nutups_cgi_rw_content_t, httpd_dspam_rw_content_t
-
-allow httpd_t initrc_state_t:file { read write getattr unlink open };
-allow httpd_t initrc_t:unix_stream_socket connectto;
-allow httpd_t initrc_t:shm { unix_read unix_write associate read write getattr };
-allow httpd_t initrc_var_run_t:file { write read lock open };
-allow httpd_t kernel_t:dir { search getattr };
-allow httpd_t kernel_t:file { read getattr open };
-allow httpd_t mysqld_safe_t:dir { search getattr };
-allow httpd_t mysqld_safe_t:file { read getattr open };
-allow httpd_t mysqld_t:dir { search getattr };
-allow httpd_t mysqld_t:file { read getattr open };
-allow httpd_t sshd_t:dir { search getattr };
-allow httpd_t sshd_t:file { read getattr open };
-allow httpd_t syslogd_t:dir { search getattr };
-allow httpd_t syslogd_t:file { read getattr open };
-allow httpd_t tmpfs_t:sock_file write;
-allow httpd_t udev_t:dir { search getattr };
-allow httpd_t udev_t:file { read getattr open };
-allow httpd_t unconfined_t:dir { search getattr };
-allow httpd_t unconfined_t:file { read getattr open };
-allow httpd_t var_lib_t:lnk_file { write getattr read lock unlink };
-allow httpd_t var_lib_t:sock_file { write unlink };
-allow httpd_t v4l_device_t:chr_file getattr;
-allow httpd_t afs_fs_port_t:udp_socket name_bind;
-allow httpd_t afs_ka_port_t:udp_socket name_bind;
-allow httpd_t afs_pt_port_t:udp_socket name_bind;
-allow httpd_t amqp_port_t:udp_socket name_bind;
-allow httpd_t aol_port_t:udp_socket name_bind;
-allow httpd_t commplex_port_t:udp_socket name_bind;
-allow httpd_t condor_port_t:udp_socket name_bind;
-allow httpd_t dcc_port_t:udp_socket name_bind;
-allow httpd_t git_port_t:udp_socket name_bind;
-allow httpd_t glance_port_t:udp_socket name_bind;
-allow httpd_t httpd_var_lib_t:lnk_file create;
-allow httpd_t ionixnetmon_port_t:udp_socket name_bind;
-allow httpd_t ipp_port_t:udp_socket name_bind;
-allow httpd_t mmcc_port_t:udp_socket name_bind;
-allow httpd_t netsupport_port_t:udp_socket name_bind;
-allow httpd_t nodejs_debug_port_t:udp_socket name_bind;
-allow httpd_t port_t:udp_socket name_bind;
-allow httpd_t postfix_master_t:dir { search getattr };
-allow httpd_t postfix_master_t:file { read getattr open };
-allow httpd_t postfix_pickup_t:dir { search getattr };
-allow httpd_t postfix_pickup_t:file { read getattr open };
-allow httpd_t postfix_qmgr_t:dir { search getattr };
-allow httpd_t postfix_qmgr_t:file { read getattr open };
-allow httpd_t presence_port_t:udp_socket name_bind;
diff --git a/distros/redhat/nginx/README.Fedora b/distros/redhat/nginx/README.Fedora
deleted file mode 100644
index 0a5168231..000000000
--- a/distros/redhat/nginx/README.Fedora
+++ /dev/null
@@ -1,165 +0,0 @@
-What's New
-==========
-
-1. This is an *experimental* build of zoneminder which uses the
- nginx web server.
-
-2. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
- "/cgi-bin-zm/zms". This has been to done to avoid this bug:
- https://bugzilla.redhat.com/show_bug.cgi?id=973067
-
- IMPORTANT: You must manually inspect the value for PATH_ZMS under Options
- and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
- in a broken system. You have been warned.
-
-3. Due to the active state of the ZoneMinder project, we now recommend granting
- ALL permission to the ZoneMinder mysql account. This change must be done
- manually before ZoneMinder will run. See the installation steps below.
-
-4. This package uses the HTTPS protocol by default to access the web portal.
- Requests using HTTP will auto-redirect to HTTPS. See README.https for
- more information.
-
-5. This package ships with the new ZoneMinder API enabled.
-
-New installs
-============
-
-1. This package supports either community-mysql-server or mariadb-server with
- mariadb being the preferred choice. Unless you are already using MariaDB or
- Mysql server, you need to ensure that the server is configured to start
- during boot and properly secured by running:
-
- sudo dnf install mariadb-server
- sudo systemctl enable mariadb
- sudo systemctl start mariadb.service
- mysql_secure_installation
-
-2. Assuming the database is local and using the password for the root account
- set during the previous step, you will need to create the ZoneMinder
- database and configure a database account for ZoneMinder to use:
-
- mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
- mysql -uroot -p -e "grant all on zm.* to \
- 'zmuser'@localhost identified by 'zmpass';"
- mysqladmin -uroot -p reload
-
- The database account credentials, zmuser/zmpass, are arbitrary. Set them to
- anything that suits your environment.
-
-3. If you have chosen to change the zoneminder database account credentials to
- something other than zmuser/zmpass, you must now edit /etc/zm/zm.conf.
- Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous
- step.
-
- This version of zoneminder no longer requires you to make a similar change
- to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php
- This now happens dynamically. Do *not* make any changes to this file.
-
-4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
- timezone. PHP will complain loudly if this is not set, or if it is set
- incorrectly, and these complaints will show up in the zoneminder logging
- system as errors.
-
- If you are not sure of the proper timezone specification to use, look at
- http://php.net/date.timezone
-
-5. Disable SELinux
-
- We currently do not have the resources to create and maintain an accurate
- SELinux policy for ZoneMinder on Fedora. We will gladly accept pull
- reqeusts from anyone who wishes to do the work. In the meantime, SELinux
- will need to be disabled or put into permissive mode.
-
- To immediately disbale SELinux for the current seesion, issue the following
- from the command line:
-
- sudo setenforce 0
-
- To permanently disable SELinux, edit /etc/selinux/config and change the
- SELINUX line from "enforcing" to "disabled". This change will take
- effect after a reboot.
-
-6. This package comes preconfigured for HTTPS using the default self signed
- certificate on your system. We recommend you keep this configuration.
-
- If this does not meet your needs, then read README.https to
- learn about alternatives.
-
-7. Edit /etc/sysconfig/fcgiwrap and set DAEMON_PROCS to the maximum number of
- simulatneous streams the server should support. Generally, a good minimum
- value for this equals the total number of cameras you expect to view at the
- same time.
-
-8. Now start the web server:
-
- sudo systemctl enable nginx
- sudo systemctl start nginx
-
-9. Now start zoneminder:
-
- sudo systemctl enable zoneminder
- sudo systemctl start zoneminder
-
-10.The Fedora repos have a ZoneMinder package available, but it does not
- support ffmpeg or libvlc, which many modern IP cameras require. Most users
- will want to prevent the ZoneMinder package in the Fedora repos from
- overwriting the ZoneMinder package in zmrepo, during a future dnf update. To
- prevent that from happening you must edit /etc/yum.repos.d/fedora.repo
- and /etc/yum.repos.d/fedora-updates.repo. Add the line "exclude=zoneminder*"
- without the quotes under the [fedora] and [fedora-updates] blocks,
- respectively.
-
-Upgrades
-========
-
-1. Verify /etc/zm/zm.conf.
-
- If zm.conf was manually edited before running the upgrade, the installation
- may not overwrite it. In this case, it will create the file
- /etc/zm/zm.conf.rpmnew.
-
- For example, this will happen if you are using database account credentials
- other than zmuser/zmpass.
-
- Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf
- contains any new config settings that may be in zm.conf.rpmnew.
-
- This version of zoneminder no longer requires you to make a similar change
- to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php
- This now happens dynamically. Do *not* make any changes to this file.
-
-2. Verify permissions of the zmuser account.
-
- Over time, the database account permissions required for normal operation
- have increased. Verify the zmuser database account has been granted all
- permission to the ZoneMinder database:
-
- mysql -uroot -p -e "show grants for zmuser@localhost;"
-
- See step 2 of the Installation section to add missing permissions.
-
-3. Verify the ZoneMinder Apache configuration file in the folder
- /etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there
- may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
- exists, inspect it and merge anything new in that file with zoneminder.conf.
- Verify the SSL REquirements meet your needs. Read README.https if necessary.
-
-4. Upgrade the database before starting ZoneMinder.
-
- Most upgrades can be performed by executing the following command:
-
- sudo zmupdate.pl
-
- Recent versions of ZoneMinder don't require any parameters added to the
- zmupdate command. However, if ZoneMinder complains, you may need to call
- zmupdate in the following manner:
-
- sudo zmupdate.pl --user=root --pass= --version=
-
-5. Now restart nginx and php-fpm then start and zoneminder:
-
- sudo systemctl restart nginx
- sudo systemctl restart php-fpm
- sudo systemctl start zoneminder
-
diff --git a/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx b/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx
new file mode 100644
index 000000000..8eaa78d5c
--- /dev/null
+++ b/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx
@@ -0,0 +1,7 @@
+polkit.addRule(function(action, subject) {
+ if (action.id == "com.zoneminder.policykit.pkexec.run-zmsystemctl" &&
+ subject.user != "nginx") {
+ return polkit.Result.NO;
+ }
+
+});
diff --git a/distros/redhat/nginx/redirect.nginx.conf b/distros/redhat/nginx/redirect.nginx.conf
new file mode 100644
index 000000000..67ef08124
--- /dev/null
+++ b/distros/redhat/nginx/redirect.nginx.conf
@@ -0,0 +1,2 @@
+# Auto redirect to https
+return 301 https://$host$request_uri;
diff --git a/distros/redhat/nginx/zm-nginx.conf b/distros/redhat/nginx/zm-nginx.conf
new file mode 100644
index 000000000..5f0319b17
--- /dev/null
+++ b/distros/redhat/nginx/zm-nginx.conf
@@ -0,0 +1,9 @@
+# Additional config directives for ZoneMinder with Nginx web server
+
+[Unit]
+After=nginx.service php-fpm.service fcgiwrap.service
+Requires=php-fpm.service fcgiwrap@nginx.service
+
+[Service]
+User=nginx
+Group=nginx
diff --git a/distros/redhat/nginx/zm-web-user.conf b/distros/redhat/nginx/zm-web-user.conf
new file mode 100644
index 000000000..3146679fd
--- /dev/null
+++ b/distros/redhat/nginx/zm-web-user.conf
@@ -0,0 +1,3 @@
+ZM_WEB_USER=nginx
+ZM_WEB_GROUP=nginx
+
diff --git a/distros/redhat/nginx/zoneminder.conf.in b/distros/redhat/nginx/zoneminder.conf.in
deleted file mode 100644
index b8ffd816a..000000000
--- a/distros/redhat/nginx/zoneminder.conf.in
+++ /dev/null
@@ -1,49 +0,0 @@
-listen 443 ssl;
-listen [::]:443 ssl;
-
-ssl_certificate "/etc/pki/tls/certs/localhost.crt";
-ssl_certificate_key "/etc/pki/tls/private/localhost.key";
-ssl_session_cache shared:SSL:1m;
-ssl_session_timeout 10m;
-ssl_ciphers PROFILE=SYSTEM;
-ssl_prefer_server_ciphers on;
-
-# Auto-redirect HTTP requests to HTTPS
-if ($scheme != "https") {
- rewrite ^/?(zm)(.*)$ https://$host/$1$2 permanent;
-}
-
-location /cgi-bin-zm {
- gzip off;
- alias "@ZM_CGIDIR@";
-
- include /etc/nginx/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $request_filename;
- fastcgi_pass unix:/run/fcgiwrap.sock;
-}
-
-location /zm {
- gzip off;
- alias "@ZM_WEBDIR@";
- index index.php;
-
- location ~ \.php$ {
- if (!-f $request_filename) { return 404; }
- expires epoch;
- include /etc/nginx/fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $request_filename;
- fastcgi_index index.php;
- fastcgi_pass unix:/run/php-fpm/www.sock;
- }
-
- location ~ \.(jpg|jpeg|gif|png|ico)$ {
- access_log off;
- expires 33d;
- }
-
- location /zm/api/ {
- alias "@ZM_WEBDIR@";
- rewrite ^/zm/api(.+)$ /zm/api/index.php?p=$1 last;
- }
-}
-
diff --git a/distros/redhat/nginx/zoneminder.nginx.conf.in b/distros/redhat/nginx/zoneminder.nginx.conf.in
new file mode 100644
index 000000000..2976a83b2
--- /dev/null
+++ b/distros/redhat/nginx/zoneminder.nginx.conf.in
@@ -0,0 +1,57 @@
+server {
+ listen 443 ssl default_server;
+ listen [::]:443 ssl default_server;
+ server_name = localhost $hostname;
+
+ ssl_certificate "/etc/pki/tls/certs/localhost.crt";
+ ssl_certificate_key "/etc/pki/tls/private/localhost.key";
+ ssl_session_cache shared:SSL:1m;
+ ssl_session_timeout 10m;
+ ssl_ciphers PROFILE=SYSTEM;
+ ssl_prefer_server_ciphers on;
+
+ # Auto redirect to server/zm when no url suffix was given
+ location = / {
+ return 301 zm;
+ }
+
+ location /cgi-bin-zm {
+ gzip off;
+ alias "@ZM_CGIDIR@";
+
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $request_filename;
+ fastcgi_pass unix:/run/fcgiwrap/fcgiwrap-nginx.sock;
+ }
+
+ location /zm/cache {
+ alias "@ZM_CACHEDIR@";
+ }
+
+ location /zm {
+ gzip off;
+ alias "@ZM_WEBDIR@";
+ index index.php;
+
+ location ~ \.php$ {
+ try_files $uri =404;
+ expires epoch;
+ include /etc/nginx/fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $request_filename;
+ fastcgi_index index.php;
+ fastcgi_pass unix:/run/php-fpm/www.sock;
+ }
+
+ location ~ \.(jpg|jpeg|gif|png|ico)$ {
+ access_log off;
+ expires 33d;
+ }
+
+ location /zm/api/ {
+ alias "@ZM_WEBDIR@";
+ rewrite ^/zm/api(.+)$ /zm/api/app/webroot/index.php?p=$1 last;
+ }
+ }
+
+}
+
diff --git a/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in b/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in
new file mode 100644
index 000000000..502b817d8
--- /dev/null
+++ b/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in
@@ -0,0 +1,5 @@
+D @ZM_TMPDIR@ 0755 nginx nginx
+D @ZM_SOCKDIR@ 0755 nginx nginx
+D @ZM_CACHEDIR@ 0755 nginx nginx
+d @ZM_DIR_EVENTS@ 0755 nginx nginx
+
diff --git a/distros/redhat/nginx/zoneminder.php-fpm.conf b/distros/redhat/nginx/zoneminder.php-fpm.conf
new file mode 100644
index 000000000..cd60a5120
--- /dev/null
+++ b/distros/redhat/nginx/zoneminder.php-fpm.conf
@@ -0,0 +1,14 @@
+; This config file is needed when using ZoneMinder with web servers other
+; than Apache. You can ignore this file if you are using Apache web server.
+; Change the user and group of the default pool to the web server account
+[www]
+
+user = nginx
+group = nginx
+
+; These parameters are typically a tradoff between performance and memory
+; consumption. See the contents of www.conf for details.
+
+pm = ondemand
+pm.max_children = 50
+pm.process_idle_timeout = 10s
diff --git a/distros/redhat/nginx/zoneminder.php-fpm.conf.in b/distros/redhat/nginx/zoneminder.php-fpm.conf.in
deleted file mode 100644
index 26e8c62cf..000000000
--- a/distros/redhat/nginx/zoneminder.php-fpm.conf.in
+++ /dev/null
@@ -1,10 +0,0 @@
-# Change the user and group of the default pool to the web server account
-[www]
-
-user = @WEB_USER@
-group = @WEB_GROUP@
-
-# Uncomment these on machines with little memory
-#pm = ondemand
-#pm.max_children = 10
-#pm.process_idle_timeout = 10s
diff --git a/distros/redhat/nginx/zoneminder.service.in b/distros/redhat/nginx/zoneminder.service.in
deleted file mode 100644
index 7e2e36585..000000000
--- a/distros/redhat/nginx/zoneminder.service.in
+++ /dev/null
@@ -1,22 +0,0 @@
-# ZoneMinder systemd unit file for Fedora
-# Replace mariadb with community-mysql if using mysql service instead of mariadb
-
-[Unit]
-Description=ZoneMinder CCTV recording and security system
-After=network.target mariadb.service nginx.service php-fpm.service fcgiwrap.service
-Requires=mariadb.service nginx.service php-fpm.service fcgiwrap.service
-
-[Service]
-User=@WEB_USER@
-Group=@WEB_GROUP@
-Type=forking
-ExecStart=@BINDIR@/zmpkg.pl start
-ExecReload=@BINDIR@/zmpkg.pl restart
-ExecStop=@BINDIR@/zmpkg.pl stop
-PIDFile=@ZM_RUNDIR@/zm.pid
-Environment=TZ=/etc/localtime
-RuntimeDirectory=zoneminder
-RuntimeDirectoryMode=0755
-
-[Install]
-WantedBy=multi-user.target
diff --git a/distros/redhat/nginx/zoneminder.tmpfiles.in b/distros/redhat/nginx/zoneminder.tmpfiles.in
deleted file mode 100644
index 8040a7877..000000000
--- a/distros/redhat/nginx/zoneminder.tmpfiles.in
+++ /dev/null
@@ -1,5 +0,0 @@
-D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@
-D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@
-D /var/lib/php/session 770 root @WEB_GROUP@
-D /var/lib/php/wsdlcache 770 root @WEB_GROUP@
-
diff --git a/distros/redhat/readme/README b/distros/redhat/readme/README
new file mode 100644
index 000000000..f0fff828f
--- /dev/null
+++ b/distros/redhat/readme/README
@@ -0,0 +1,32 @@
+What's New
+==========
+
+1. See the ZoneMinder release notes for a list of new features:
+ https://github.com/ZoneMinder/zoneminder/releases
+
+2. The contents of the ZoneMinder Apache config file have changed. In
+ addition, this ZoneMinder package now requires you to manually symlink the
+ ZoneMinder Apache config file. See new install step 6 and upgrade step 3
+ in the appropriate README for details.
+
+3. This package has been split into sub-packages to allow compatibility with
+ other web servers. Here is a breakdown of the available packages:
+
+ zoneminder - Meta-package installs zoneminder-common and zoneminder-httpd
+ This exists soley for backwards compatibility.
+ zoneminder-common - Common files that do not differ based on the web server
+ zoneminder-httpd - Files needed for compatibility with the Apache web server
+ zoneminder-nginx - Files needed for compatibility with the Nginx web server
+
+ You can switch between different subpackages with dnf/yum. Be advised that,
+ switching between httpd <-> nginx requires manaully changing ownership of
+ all event folders and the php session folder after the change.
+
+4. The timezone must now be set from the ZoneMinder web console. See the
+ appropriate README, mentioned in the next step, for details.
+
+6. Continue on to the next README that corresponds to your chosen webserver:
+
+ README.httpd - Follow these steps when using Apache
+ README.nginx - Follow these steps when using Nginx
+
diff --git a/distros/redhat/readme/README.Fedora b/distros/redhat/readme/README.httpd
similarity index 77%
rename from distros/redhat/readme/README.Fedora
rename to distros/redhat/readme/README.httpd
index 3366c31a6..1ba0ee688 100644
--- a/distros/redhat/readme/README.Fedora
+++ b/distros/redhat/readme/README.httpd
@@ -1,17 +1,8 @@
-What's New
-==========
-
-1. See the ZoneMinder release notes for a list of new features:
- https://github.com/ZoneMinder/zoneminder/releases
-
-2. The contents of the ZoneMinder Apache config file have changed. In
- addition, this ZoneMinder package now requires you to manually symlink the
- ZoneMinder Apache config file. See new install step 6 and upgrade step 3
- below for details.
-
New installs
============
+NOTE: EL7 users should replace "dnf" with "yum" in the instructions below.
+
1. Unless you are already using MariaDB server, you need to ensure that the
server is configured to start during boot and properly secured by running:
@@ -45,20 +36,17 @@ New installs
sudo chown root:apache *.conf
sudo chmod 640 *.conf
-4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
- timezone. PHP will complain loudly if this is not set, or if it is set
- incorrectly, and these complaints will show up in the zoneminder logging
- system as errors.
+4. Manually setting the timezone in /etc/php.ini is deprecated.
- If you are not sure of the proper timezone specification to use, look at
- http://php.net/date.timezone
+ Instead, navigate to Options -> System from the ZoneMinder web console.
+ Do this after completing step 10, below.
+
+ Note that timezone errors will appear in the ZoneMinder log until this
+ has been completed.
5. Disable SELinux
- We currently do not have the resources to create and maintain an accurate
- SELinux policy for ZoneMinder on Fedora. We will gladly accept pull
- reqeusts from anyone who wishes to do the work. In the meantime, SELinux
- will need to be disabled or put into permissive mode.
+ SELinux must be disabled or put into permissive mode. This is not optional!
To immediately disbale SELinux for the current seesion, issue the following
from the command line:
@@ -72,19 +60,19 @@ New installs
6. Configure the web server
This package uses the HTTPS protocol by default to access the web portal,
- using rhe default self signed certificate on your system. Requests using
+ using the default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
Inspect the web server configuration file and verify it meets your needs:
- /etc/zm/www/zoneminder.conf
+ /etc/zm/www/zoneminder.httpd.conf
If you are running other web enabled services then you may need to edit
this file to suite. See README.https to learn about other alternatives.
When in doubt, proceed with the default:
- sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/
+ sudo ln -sf /etc/zm/www/zoneminder.httpd.conf /etc/httpd/conf.d/
sudo dnf install mod_ssl
7. Now start the web server:
@@ -129,7 +117,7 @@ New installs
Upgrades
========
-1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom
+1. Conf.d folder support has been added to ZoneMinder. Any custom
changes previously made to zm.conf must now be made in one or more custom
config files, created under the conf.d folder. Do this now. See
/etc/zm/conf.d/README for details. Once you recreate any custom config changes
@@ -146,10 +134,17 @@ Upgrades
See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder
- /etc/zm/www. You will have a file called "zoneminder.conf" and there
- may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
+ /etc/zm/www. You will have a file called "zoneminder.httpd.conf" and there
+ may also be one or more files with "rpmnew" extenstion. If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf.
- Verify the SSL REquirements meet your needs. Read README.https if necessary.
+ Verify the SSL Requirements meet your needs. Read README.https if necessary.
+
+ The contents of this file must be merged into your Apache configuration.
+ See step 6 of the installation section if you have not already done this
+ during a previous upgrade.
+
+ IMPORTANT: Failure to complete this step properly will result in a mostly
+ empty or significantly corrupted web console post-upgrade.
4. Upgrade the database before starting ZoneMinder.
@@ -168,3 +163,11 @@ Upgrades
sudo systemctl restart httpd
sudo systemctl start zoneminder
+6. Manually setting the timezone in /etc/php.ini is deprecated.
+
+ Instead, navigate to Options -> System from the ZoneMinder web console.
+ Do this now.
+
+ Note that timezone errors will appear in the ZoneMinder log until this
+ has been completed.
+
diff --git a/distros/redhat/readme/README.https b/distros/redhat/readme/README.https
index 4b02aaa0d..620588bf9 100644
--- a/distros/redhat/readme/README.https
+++ b/distros/redhat/readme/README.https
@@ -20,7 +20,8 @@ experience.
to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling
"centos certificate" reveals many articles on the subject.
-3. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL
- directives found in /etc/httpd/conf.d/zoneminder.conf. You should also
- comment out the HTTP -> HTTPS Rewrite rule.
+3. When using Apache, you can turn off HTTPS entirely by simply commenting
+ out the SSLRequireSSL directives found in
+ /etc/zm/www/zoneminder.apache.conf. You should also comment out the
+ HTTP -> HTTPS Rewrite rule.
diff --git a/distros/redhat/readme/README.Redhat7 b/distros/redhat/readme/README.nginx
similarity index 69%
rename from distros/redhat/readme/README.Redhat7
rename to distros/redhat/readme/README.nginx
index bb2dcdaa0..8bc3bbdc1 100644
--- a/distros/redhat/readme/README.Redhat7
+++ b/distros/redhat/readme/README.nginx
@@ -1,28 +1,17 @@
-What's New
-==========
-
-1. See the ZoneMinder release notes for a list of new features:
- https://github.com/ZoneMinder/zoneminder/releases
-
-2. The contents of the ZoneMinder Apache config file have changed. In
- addition, this ZoneMinder package now requires you to manually symlink the
- ZoneMinder Apache config file. See new install step 6 and upgrade step 3
- below for details.
-
New installs
============
1. Unless you are already using MariaDB server, you need to ensure that the
server is configured to start during boot and properly secured by running:
- sudo yum install mariadb-server
+ sudo dnf install mariadb-server
sudo systemctl enable mariadb
sudo systemctl start mariadb.service
mysql_secure_installation
-2. Using the password for the root account set during the previous step, you
- will need to create the ZoneMinder database and configure a database
- account for ZoneMinder to use:
+2. Assuming the database is local and using the password for the root account
+ set during the previous step, you will need to create the ZoneMinder
+ database and configure a database account for ZoneMinder to use:
mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
mysql -uroot -p -e "grant all on zm.* to \
@@ -42,21 +31,21 @@ New installs
Once the file has been saved, set proper file & ownership permissions on it:
- sudo chown root:apache *.conf
- sudo chmod 640 *.conf
+ sudo chown root:nginx *.conf
+ sudo chmod 640 *.conf
-4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
- timezone. PHP will complain loudly if this is not set, or if it is set
- incorrectly, and these complaints will show up in the zoneminder logging
- system as errors.
+4. Manually setting the timezone in /etc/php.ini is deprecated.
- If you are not sure of the proper timezone specification to use, look at
- http://php.net/date.timezone
+ Instead, navigate to Options -> System from the ZoneMinder web console.
+ Do this after completing step 10, below.
+
+ Note that timezone errors will appear in the ZoneMinder log until this
+ has been completed.
5. Disable SELinux
We currently do not have the resources to create and maintain an accurate
- SELinux policy for ZoneMinder on CentOS 7. We will gladly accept pull
+ SELinux policy for ZoneMinder on Fedora. We will gladly accept pull
reqeusts from anyone who wishes to do the work. In the meantime, SELinux
will need to be disabled or put into permissive mode.
@@ -72,8 +61,7 @@ New installs
6. Configure the web server
This package uses the HTTPS protocol by default to access the web portal,
- using rhe default self signed certificate on your system. Requests using
- HTTP will auto-redirect to HTTPS.
+ using the default self signed certificate on your system.
Inspect the web server configuration file and verify it meets your needs:
@@ -82,22 +70,30 @@ New installs
If you are running other web enabled services then you may need to edit
this file to suite. See README.https to learn about other alternatives.
+ If you wish http requests to auto-redirect to https requests, then link or
+ copy /etc/zm/www/redirect.nginx.conf into /etc/nginx/default.d folder.
+
When in doubt, proceed with the default:
- sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/
- sudo yum install mod_ssl
+ sudo ln -sf /etc/zm/www/zoneminder.nginx.conf /etc/nginx/conf.d/
+ sudo ln -sf /etc/zm/www/redirect.nginx.conf /etc/nginx/default.d/
-7. Now start the web server:
+7. Edit /etc/sysconfig/fcgiwrap and set DAEMON_PROCS to the maximum number of
+ simulatneous streams the server should support. Generally, a good minimum
+ value for this equals the total number of cameras you expect to view at the
+ same time.
- sudo systemctl enable httpd
- sudo systemctl start httpd
+8. Now start the web server:
-8. Now start zoneminder:
+ sudo systemctl enable nginx
+ sudo systemctl start nginx
+
+9. Now start zoneminder:
sudo systemctl enable zoneminder
sudo systemctl start zoneminder
-9. Optionally configure the firewall
+10. Optionally configure the firewall
All Redhat distros ship with the firewall enabled. That means you will not
be able to access the ZoneMinder web console from a remote machine until
@@ -117,7 +113,7 @@ New installs
security requirements and how you use the system. It is up to you to verify
these commands are sufficient.
-10. Access the ZoneMinder web console
+11. Access the ZoneMinder web console
You may now access the ZoneMinder web console from your web browser using
an appropriate url. Here are some examples:
@@ -129,7 +125,7 @@ New installs
Upgrades
========
-1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom
+1. Conf.d folder support has been added to ZoneMinder. Any custom
changes previously made to zm.conf must now be made in one or more custom
config files, created under the conf.d folder. Do this now. See
/etc/zm/conf.d/README for details. Once you recreate any custom config changes
@@ -144,13 +140,17 @@ Upgrades
mysql -uroot -p -e "show grants for zmuser@localhost;"
See step 2 of the Installation section to add missing permissions.
-
-3. Verify the ZoneMinder Apache configuration file in the folder
+
+3. Verify the ZoneMinder Nginx configuration file in the folder
/etc/zm/www. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary.
+ The contents of this file must be merged into your Nginx configuration.
+ See step 6 of the installation section if you have not already done this
+ during a previous upgrade.
+
4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command:
@@ -163,9 +163,17 @@ Upgrades
sudo zmupdate.pl --user=root --pass= --version=
-5. Now restart the web server then start zoneminder:
+5. Now restart nginx and php-fpm then start zoneminder:
- sudo systemctl restart httpd
+ sudo systemctl restart nginx
+ sudo systemctl restart php-fpm
sudo systemctl start zoneminder
+6. Manually setting the timezone in /etc/php.ini is deprecated.
+
+ Instead, navigate to Options -> System from the ZoneMinder web console.
+ Do this now.
+
+ Note that timezone errors will appear in the ZoneMinder log until this
+ has been completed.
diff --git a/distros/redhat/systemd/zoneminder.logrotate.in b/distros/redhat/systemd/zoneminder.logrotate.in
deleted file mode 100644
index b4919eb5e..000000000
--- a/distros/redhat/systemd/zoneminder.logrotate.in
+++ /dev/null
@@ -1,8 +0,0 @@
-@ZM_LOGDIR@/*.log {
- missingok
- notifempty
- sharedscripts
- postrotate
- @BINDIR@/zmpkg.pl logrot 2> /dev/null > /dev/null || :
- endscript
-}
diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec
index 644bd5ee7..7297648a9 100644
--- a/distros/redhat/zoneminder.spec
+++ b/distros/redhat/zoneminder.spec
@@ -1,3 +1,4 @@
+# Leaving this to allow one to build zoneminder-http subpackage using arbitrary user account
%global zmuid_final apache
%global zmgid_final apache
@@ -7,30 +8,30 @@
# CakePHP-Enum-Behavior is configured as a git submodule
%global ceb_version 1.0-zm
-%if "%{zmuid_final}" == "nginx"
-%global with_nginx 1
-%endif
-
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
%global sslkey %{_sysconfdir}/pki/tls/private/localhost.key
# This will tell zoneminder's cmake process we are building against a known distro
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
-# Fedora >= 25 needs apcu backwards compatibility module
-%if 0%{?fedora} >= 25
+# Fedora needs apcu backwards compatibility module
+%if 0%{?fedora}
%global with_apcu_bc 1
%endif
-%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora}
+# Newer php's keep json functions in a subpackage
+%if 0%{?fedora} || 0%{?rhel} >= 8
+%global with_php_json 1
+%endif
+
+# The default for everything but el7 these days
%global _hardened_build 1
Name: zoneminder
-Version: 1.31.44
+Version: 1.34.3
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
-# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/
# Mootools is inder the MIT license: http://mootools.net/
# CakePHP is under the MIT license: https://github.com/cakephp/cakephp
# Crud is under the MIT license: https://github.com/FriendsOfCake/crud
@@ -46,13 +47,14 @@ BuildRequires: systemd-devel
BuildRequires: mariadb-devel
BuildRequires: perl-podlators
BuildRequires: polkit-devel
-BuildRequires: cmake >= 2.8.7
+BuildRequires: cmake3
BuildRequires: gnutls-devel
BuildRequires: bzip2-devel
BuildRequires: pcre-devel
BuildRequires: libjpeg-turbo-devel
BuildRequires: findutils
BuildRequires: coreutils
+BuildRequires: net-tools
BuildRequires: perl
BuildRequires: perl-generators
BuildRequires: perl(Archive::Tar)
@@ -75,6 +77,8 @@ BuildRequires: vlc-devel
BuildRequires: libcurl-devel
BuildRequires: libv4l-devel
BuildRequires: desktop-file-utils
+BuildRequires: gzip
+BuildRequires: zlib-devel
# ZoneMinder looks for and records the location of the ffmpeg binary during build
BuildRequires: ffmpeg
@@ -84,15 +88,29 @@ BuildRequires: ffmpeg-devel
BuildRequires: libmp4v2-devel
BuildRequires: x264-devel
-%{?with_nginx:Requires: nginx}
-%{?with_nginx:Requires: fcgiwrap}
-%{?with_nginx:Requires: php-fpm}
-%{!?with_nginx:Requires: httpd}
-%{!?with_nginx:Requires: php}
+# Allow existing user base to seamlessly transition to sub-packages
+Requires: %{name}-common%{?_isa} = %{version}-%{release}
+Requires: %{name}-httpd%{?_isa} = %{version}-%{release}
+
+%description
+ZoneMinder is a set of applications which is intended to provide a complete
+solution allowing you to capture, analyze, record and monitor any cameras you
+have attached to a Linux based machine. It is designed to run on kernels which
+support the Video For Linux (V4L) interface and has been tested with cameras
+attached to BTTV cards, various USB cameras and IP network cameras. It is
+designed to support as many cameras as you can attach to your computer without
+too much degradation of performance.
+
+This is a meta package for backwards compatibility with the existing
+ZoneMinder user base.
+
+%package common
+Summary: Common files for ZoneMinder, not tied to a specific web server
+
Requires: php-mysqli
Requires: php-common
Requires: php-gd
-%{?fedora:Requires: php-json}
+%{?with_php_json:Requires: php-json}
Requires: php-pecl-apcu
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
Requires: cambozola
@@ -113,16 +131,12 @@ Requires: perl(Net::FTP)
Requires: perl(LWP::Protocol::https)
Requires: ca-certificates
Requires: zip
-
-Requires(post): systemd
-Requires(post): systemd-sysv
-Requires(preun): systemd
-Requires(postun): systemd
+%{?systemd_requires}
Requires(post): %{_bindir}/gpasswd
-Requires(post): %{_bindir}/less
+Requires(post): %{_bindir}/chown
-%description
+%description common
ZoneMinder is a set of applications which is intended to provide a complete
solution allowing you to capture, analyze, record and monitor any cameras you
have attached to a Linux based machine. It is designed to run on kernels which
@@ -131,15 +145,57 @@ attached to BTTV cards, various USB cameras and IP network cameras. It is
designed to support as many cameras as you can attach to your computer without
too much degradation of performance.
+This is a meta-package that exists solely to allow the existing user base to
+seamlessly transition to sub-packages.
+
+%package httpd
+Summary: ZoneMinder configuration for Apache web server
+Requires: %{name}-common%{?_isa} = %{version}-%{release}
+Requires: httpd
+Requires: php
+
+Conflicts: %{name}-nginx
+
+%description httpd
+ZoneMinder is a set of applications which is intended to provide a complete
+solution allowing you to capture, analyze, record and monitor any cameras you
+have attached to a Linux based machine. It is designed to run on kernels which
+support the Video For Linux (V4L) interface and has been tested with cameras
+attached to BTTV cards, various USB cameras and IP network cameras. It is
+designed to support as many cameras as you can attach to your computer without
+too much degradation of performance.
+
+This sub-package contains configuration specific to Apache web server
+
+%package nginx
+Summary: ZoneMinder configuration for Nginx web server
+Requires: %{name}-common%{?_isa} = %{version}-%{release}
+Requires: nginx
+Requires: php-fpm
+Requires: fcgiwrap
+
+Conflicts: %{name}-httpd
+
+%description nginx
+ZoneMinder is a set of applications which is intended to provide a complete
+solution allowing you to capture, analyze, record and monitor any cameras you
+have attached to a Linux based machine. It is designed to run on kernels which
+support the Video For Linux (V4L) interface and has been tested with cameras
+attached to BTTV cards, various USB cameras and IP network cameras. It is
+designed to support as many cameras as you can attach to your computer without
+too much degradation of performance.
+
+This sub-package contains support for ZoneMinder with the Nginx web server
+
%prep
-%autosetup -p 1 -a 1 -n ZoneMinder-%{version}
-%{__rm} -rf ./web/api/app/Plugin/Crud
-%{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud
+%autosetup -p 1 -a 1
+rm -rf ./web/api/app/Plugin/Crud
+mv -f crud-%{crud_version} ./web/api/app/Plugin/Crud
# The all powerful autosetup macro does not work after the second source tarball
-%{__gzip} -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf -
-%{__rm} -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior
-%{__mv} -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior
+gzip -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf -
+rm -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior
+mv -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior
# Change the following default values
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
@@ -150,9 +206,9 @@ too much degradation of performance.
./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no
%build
-%cmake \
+%cmake3 \
-DZM_WEB_USER="%{zmuid_final}" \
- -DZM_WEB_GROUP="%{zmuid_final}" \
+ -DZM_WEB_GROUP="%{zmgid_final}" \
-DZM_TARGET_DISTRO="%{zmtargetdistro}" \
.
@@ -174,10 +230,13 @@ find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist
find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!%{_buildshell}\' {} \; -exec %{__chmod} 755 {} \;
# Use the system cacert file rather then the one bundled with CakePHP
-%{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
-%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
+rm -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
+ln -s ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
-%post
+# Handle the polkit file differently for web server agnostic support (see post)
+rm -f %{buildroot}%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
+
+%post common
# Initial installation
if [ $1 -eq 1 ] ; then
%systemd_post %{name}.service
@@ -185,28 +244,48 @@ fi
# Upgrade from a previous version of zoneminder
if [ $1 -eq 2 ] ; then
-
# Add any new PTZ control configurations to the database (will not overwrite)
%{_bindir}/zmcamtool.pl --import >/dev/null 2>&1 || :
# Freshen the database
%{_bindir}/zmupdate.pl -f >/dev/null 2>&1 || :
-
- # We can't run this automatically when new sql account permissions need to
- # be manually added first
- # Run zmupdate non-interactively
- # zmupdate.pl --nointeractive
fi
+# Warn the end user to read the README file
+echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, you must read the README file\nto finish the installation or upgrade!"
+echo -e "\nThe README file is located here: %{_pkgdocdir}-common/README\n"
+
+%post httpd
+# For the case of changing from nginx <-> httpd, files in these folders must change ownership if they exist
+%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_sharedstatedir}/php/session/* >/dev/null 2>&1 || :
+%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_localstatedir}/log/zoneminder/* >/dev/null 2>&1 || :
+
+ln -sf %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.httpd %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
+# backwards compatibility
+ln -sf %{_sysconfdir}/zm/www/zoneminder.httpd.conf %{_sysconfdir}/zm/www/zoneminder.conf
+
# Allow zoneminder access to local video sources, serial ports, and x10
%{_bindir}/gpasswd -a %{zmuid_final} video >/dev/null 2>&1 || :
%{_bindir}/gpasswd -a %{zmuid_final} dialout >/dev/null 2>&1 || :
-# Warn the end user to read the README file
-echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.%{readme_suffix} to finish the\ninstallation or upgrade!\n"
-echo -e "\nThe README file is located here: %{_docdir}/%{name}\n"
+%post nginx
+
+# Php package owns the session folder and sets group ownership to apache account
+# We could override the folder permission, but adding nginx to the apache group works better
+%{_bindir}/gpasswd -a nginx apache >/dev/null 2>&1 || :
+
+# For the case of changing from httpd <-> nginx, files in these folders must change ownership if they exist
+%{_bindir}/chown -R nginx:nginx %{_sharedstatedir}/php/session/* >/dev/null 2>&1 || :
+%{_bindir}/chown -R nginx:nginx %{_localstatedir}/log/zoneminder/* >/dev/null 2>&1 || :
+
+ln -sf %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.nginx %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
+# backwards compatibility
+ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zoneminder.conf
+
+# Allow zoneminder access to local video sources, serial ports, and x10
+%{_bindir}/gpasswd -a nginx video >/dev/null 2>&1 || :
+%{_bindir}/gpasswd -a nginx dialout >/dev/null 2>&1 || :
-%if 0%{?with_nginx}
# Nginx does not create an SSL certificate like the apache package does so lets do that here
if [ -f %{sslkey} -o -f %{sslcert} ]; then
exit 0
@@ -232,7 +311,6 @@ SomeOrganizationalUnit
${FQDN}
root@${FQDN}
EOF
-%endif
%preun
%systemd_preun %{name}.service
@@ -240,19 +318,12 @@ EOF
%postun
%systemd_postun_with_restart %{name}.service
-%triggerun -- zoneminder < 1.25.0-4
-# Save the current service runlevel info
-# User must manually run systemd-sysv-convert --apply zoneminder
-# to migrate them to systemd targets
-%{_bindir}/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||:
-
-# Run these because the SysV package being removed won't do them
-/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || :
-/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || :
-
%files
+# nothing
+
+%files common
%license COPYING
-%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
+%doc README.md distros/redhat/readme/README distros/redhat/readme/README.httpd distros/redhat/readme/README.nginx distros/redhat/readme/README.https
# We want these two folders to have "normal" read permission
# compared to the folder contents
@@ -262,21 +333,11 @@ EOF
# Config folder contents contain sensitive info
# and should not be readable by normal users
%{_sysconfdir}/zm/conf.d/README
-%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
-%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
-%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf
-%config(noreplace) %attr(644,root,root) /etc/zm/www/zoneminder.conf
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
-%if 0%{?with_nginx}
-%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf
-%endif
-
-%{_tmpfilesdir}/zoneminder.conf
%{_unitdir}/zoneminder.service
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
-%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%{_bindir}/zmsystemctl.pl
%{_bindir}/zma
@@ -297,6 +358,7 @@ EOF
%{_bindir}/zmx10.pl
%{_bindir}/zmonvif-probe.pl
%{_bindir}/zmstats.pl
+%{_bindir}/zmrecover.pl
%{perl_vendorlib}/ZoneMinder*
%{perl_vendorlib}/ONVIF*
@@ -307,25 +369,119 @@ EOF
%{_libexecdir}/zoneminder/
%{_datadir}/zoneminder/
-%{_datadir}/applications/*%{name}.desktop
+%{_datadir}/applications/*zoneminder.desktop
+%files httpd
+%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
+%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/0*.conf
+%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf
+%config(noreplace) %{_sysconfdir}/zm/www/zoneminder.httpd.conf
+%ghost %{_sysconfdir}/zm/www/zoneminder.conf
+%config(noreplace) %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.httpd
+%ghost %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
+
+%{_unitdir}/zoneminder.service.d/zm-httpd.conf
+%{_tmpfilesdir}/zoneminder.httpd.tmpfiles.conf
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events
-%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/images
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/sock
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/swap
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/cache/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload
-%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
+
+%files nginx
+%config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/zm.conf
+%config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/conf.d/*.conf
+%ghost %attr(640,root,nginx) %{_sysconfdir}/zm/conf.d/zmcustom.conf
+%config(noreplace) %{_sysconfdir}/zm/www/zoneminder.nginx.conf
+%config(noreplace) %{_sysconfdir}/zm/www/redirect.nginx.conf
+%ghost %{_sysconfdir}/zm/www/zoneminder.conf
+%config(noreplace) %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.nginx
+%ghost %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
+
+%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.php-fpm.conf
+
+
+%{_unitdir}/zoneminder.service.d/zm-nginx.conf
+%{_tmpfilesdir}/zoneminder.nginx.tmpfiles.conf
+%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder
+%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/events
+%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/sock
+%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/swap
+%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/temp
+%dir %attr(755,nginx,nginx) %{_localstatedir}/cache/zoneminder
+%dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder
+%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
%changelog
-* Sun Apr 22 2018 Andrew Bauer - 1.31.42-1
-- Remove support for sysvinit a.k.a. el6
-- use desktop-file-install for new zoneminder.desktop file
-- add new web cache folder
-- 1.31.42 development snapshot
+* Tue Feb 04 2020 Andrew Bauer - 1.34.2-1
+- 1.34.2 Release
+
+* Fri Jan 31 2020 Andrew Bauer - 1.34.1-1
+- 1.34.1 Release
+
+* Sat Jan 18 2020 Andrew Bauer - 1.34.0-1
+- 1.34.0 Release
+
+* Tue Dec 17 2019 Leigh Scott - 1.32.3-5
+- Mass rebuild for x264
+
+* Wed Aug 07 2019 Leigh Scott - 1.32.3-4
+- Rebuild for new ffmpeg version
+
+* Tue Mar 12 2019 Sérgio Basto - 1.32.3-3
+- Mass rebuild for x264
+
+* Tue Mar 05 2019 RPM Fusion Release Engineering - 1.32.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Sat Dec 08 2018 Andrew Bauer - 1.32.3-1
+- 1.32.3 Release
+- Break into sub-packages
+
+* Tue Nov 13 2018 Antonio Trande - 1.32.2-2
+- Rebuild for ffmpeg-3.4.5 on el7
+- Use CMake3
+
+* Sat Oct 13 2018 Andrew Bauer - 1.32.2-1
+- 1.32.2 release
+- Bug fix release
+
+* Thu Oct 04 2018 Sérgio Basto - 1.32.1-2
+- Mass rebuild for x264 and/or x265
+
+* Tue Oct 2 2018 Andrew Bauer - 1.32.1-1
+- 1.32.1 release
+- Bug fix release
+
+* Wed Sep 12 2018 Andrew Bauer - 1.32.0-1
+- 1.32.0 release
+- remove el6 (sys v init) support
+- Make README name consistent across all supported distros
+- remove jscalendar
+- add requires php-json, zip
+- support zm/conf.d folder
+- support zm cache (busting) folder
+
+* Sun Aug 19 2018 Leigh Scott - 1.30.4-9
+- Rebuilt for Fedora 29 Mass Rebuild binutils issue
+
+* Fri Jul 27 2018 RPM Fusion Release Engineering - 1.30.4-8
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Thu Mar 08 2018 RPM Fusion Release Engineering - 1.30.4-7
+- Rebuilt for new ffmpeg snapshot
+
+* Thu Mar 01 2018 RPM Fusion Release Engineering - 1.30.4-6
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Thu Jan 18 2018 Leigh Scott - 1.30.4-5
+- Rebuilt for ffmpeg-3.5 git
+
+* Thu Aug 31 2017 RPM Fusion Release Engineering - 1.30.4-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
* Tue May 09 2017 Andrew Bauer - 1.30.4-1
- modify autosetup macro parameters
diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control
index febcb9435..e5688a421 100644
--- a/distros/ubuntu1204/control
+++ b/distros/ubuntu1204/control
@@ -1,12 +1,13 @@
Source: zoneminder
Section: net
Priority: optional
-Maintainer: Dmitry Smirnov
-Uploaders: Vagrant Cascadian
+Maintainer: Isaac Connor
+Uploaders: Isaac Connor
Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake
,libx264-dev, libmp4v2-dev
,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev
+ ,libavresample-dev
,libbz2-dev
,libgcrypt-dev
,libcurl4-gnutls-dev
@@ -23,6 +24,9 @@ Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh
,libsys-mmap-perl [!hurd-any]
,libwww-perl
,libdata-uuid-perl
+ ,libssl-dev
+ ,libcrypt-eksblowfish-perl
+ ,libdata-entropy-perl
# Unbundled (dh_linktree):
,libjs-jquery
,libjs-mootools
@@ -49,10 +53,11 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdbd-mysql-perl
,libdevice-serialport-perl
,libimage-info-perl
- ,libjson-any-perl
+ ,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
,libwww-perl
+ ,libdatetime-perl
,libdata-uuid-perl
,libnumber-bytes-human-perl
,libfile-slurp-perl
@@ -62,8 +67,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,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
+ ,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
+ ,libcrypt-eksblowfish-perl
+ ,libdata-entropy-perl
Recommends: ${misc:Recommends}
,libapache2-mod-php5 | php5-fpm
,mysql-server | virtual-mysql-server
@@ -90,7 +99,7 @@ Description: video camera security and surveillance solution
# ,libdbd-mysql-perl
# ,libdevice-serialport-perl
# ,libimage-info-perl
-# ,libjson-any-perl
+# ,libjson-maybexs-perl
# ,libsys-mmap-perl [!hurd-any]
# ,liburi-encode-perl
# ,libwww-perl
diff --git a/distros/ubuntu1204/rules b/distros/ubuntu1204/rules
index aed63c110..657697fcf 100755
--- a/distros/ubuntu1204/rules
+++ b/distros/ubuntu1204/rules
@@ -27,8 +27,8 @@ override_dh_auto_configure:
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
- -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
- -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)
diff --git a/distros/ubuntu1204/zoneminder.logrotate b/distros/ubuntu1204/zoneminder.logrotate
index 846abd4fb..3195d0fb2 100644
--- a/distros/ubuntu1204/zoneminder.logrotate
+++ b/distros/ubuntu1204/zoneminder.logrotate
@@ -9,4 +9,5 @@
endscript
daily
rotate 7
+ maxage 7
}
diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst
index ef715375b..603786ff6 100644
--- a/distros/ubuntu1204/zoneminder.postinst
+++ b/distros/ubuntu1204/zoneminder.postinst
@@ -34,6 +34,10 @@ if [ "$1" = "configure" ]; then
# 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
diff --git a/distros/ubuntu1410/control b/distros/ubuntu1410/control
index 4f979f8c2..5a873f133 100644
--- a/distros/ubuntu1410/control
+++ b/distros/ubuntu1410/control
@@ -32,7 +32,7 @@ Package: libzoneminder-perl
Section: perl
Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
- libdevice-serialport-perl, libimage-info-perl, libjson-any-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.
diff --git a/distros/ubuntu1504_cmake_split_packages/control b/distros/ubuntu1504_cmake_split_packages/control
index 5f313897f..b24d67cf2 100644
--- a/distros/ubuntu1504_cmake_split_packages/control
+++ b/distros/ubuntu1504_cmake_split_packages/control
@@ -45,7 +45,7 @@ Package: libzoneminder-perl
Section: perl
Architecture: all
Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl,
- libdevice-serialport-perl, libimage-info-perl, libjson-any-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.
diff --git a/distros/ubuntu1504_cmake_split_packages/rules b/distros/ubuntu1504_cmake_split_packages/rules
index 0eca4b511..f6169c495 100755
--- a/distros/ubuntu1504_cmake_split_packages/rules
+++ b/distros/ubuntu1504_cmake_split_packages/rules
@@ -64,7 +64,6 @@ override_dh_auto_configure:
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_CONFIG_DIR="/etc/zm" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
- -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
override_dh_auto_test:
diff --git a/distros/ubuntu1604/conf/apache2/zoneminder.conf b/distros/ubuntu1604/conf/apache2/zoneminder.conf
index 598996bc0..e3164d36c 100644
--- a/distros/ubuntu1604/conf/apache2/zoneminder.conf
+++ b/distros/ubuntu1604/conf/apache2/zoneminder.conf
@@ -6,6 +6,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Require all granted
+
# Order matters. This alias must come first.
Alias /zm/cache /var/cache/zoneminder/cache
diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control
index 0b934b7d2..617b3e852 100644
--- a/distros/ubuntu1604/control
+++ b/distros/ubuntu1604/control
@@ -1,16 +1,19 @@
Source: zoneminder
Section: net
Priority: optional
-Maintainer: Dmitry Smirnov
-Uploaders: Vagrant Cascadian
-Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree
+Maintainer: Isaac Connor
+Uploaders: Isaac Connor
+Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree, dh-systemd, dh-apache2
,cmake
,libx264-dev, libmp4v2-dev
,libavdevice-dev (>= 6:10~)
,libavcodec-dev (>= 6:10~)
,libavformat-dev (>= 6:10~)
,libavutil-dev (>= 6:10~)
+ ,libswresample-dev | libavresample-dev
,libswscale-dev (>= 6:10~)
+ ,ffmpeg | libav-tools
+ ,net-tools
,libbz2-dev
,libgcrypt-dev | libgcrypt11-dev
,libcurl4-gnutls-dev
@@ -27,6 +30,9 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libsys-mmap-perl [!hurd-any]
,libwww-perl
,libdata-uuid-perl
+ ,libssl-dev
+ ,libcrypt-eksblowfish-perl
+ ,libdata-entropy-perl
# Unbundled (dh_linktree):
,libjs-jquery
,libjs-mootools
@@ -39,7 +45,9 @@ Package: zoneminder
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,javascript-common
- ,libmp4v2-2, libx264-142|libx264-148|libx264-152, libswscale-ffmpeg3|libswscale4|libswscale3
+ ,libmp4v2-2, libx264-142|libx264-148|libx264-152|libx264-155
+ ,libswscale-ffmpeg3|libswscale4|libswscale3|libswscale5
+ ,libswresample2|libswresample3|libswresample24|libswresample-ffmpeg1
,ffmpeg | libav-tools
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libdbd-mysql-perl
@@ -50,11 +58,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdbd-mysql-perl
,libdevice-serialport-perl
,libimage-info-perl
- ,libjson-any-perl
+ ,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
- ,libwww-perl
+ ,libwww-perl, liburi-perl
,libdata-dump-perl
+ ,libdatetime-perl
,libclass-std-fast-perl
,libsoap-wsdl-perl
,libio-socket-multicast-perl
@@ -65,11 +74,14 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libfile-slurp-perl
,mysql-client | mariadb-client | virtual-mysql-client
,perl-modules
- ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc
+ ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc, php-json | php5-json
,policykit-1
,rsyslog | system-log-daemon
,zip
,libpcre3
+ ,libssl | libssl1.0.0 | libssl1.1
+ ,libcrypt-eksblowfish-perl
+ ,libdata-entropy-perl
Recommends: ${misc:Recommends}
,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
,mysql-server | mariadb-server | virtual-mysql-server
@@ -97,7 +109,7 @@ Description: video camera security and surveillance solution
# ,libdbd-mysql-perl
# ,libdevice-serialport-perl
# ,libimage-info-perl
-# ,libjson-any-perl
+# ,libjson-maybexs-perl
# ,libsys-mmap-perl [!hurd-any]
# ,liburi-encode-perl
# ,libwww-perl
diff --git a/distros/ubuntu1604/rules b/distros/ubuntu1604/rules
index f0808a8e1..c671a1b03 100755
--- a/distros/ubuntu1604/rules
+++ b/distros/ubuntu1604/rules
@@ -27,7 +27,7 @@ override_dh_auto_configure:
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
- -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
+ -DZM_PATH_SHUTDOWN="/sbin/shutdown" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
override_dh_clean:
diff --git a/distros/ubuntu1604/zoneminder.logrotate b/distros/ubuntu1604/zoneminder.logrotate
index 59238b7fe..6162e9c4d 100644
--- a/distros/ubuntu1604/zoneminder.logrotate
+++ b/distros/ubuntu1604/zoneminder.logrotate
@@ -9,4 +9,5 @@
endscript
daily
rotate 7
+ maxage 7
}
diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst
index 17272c2df..d3983950b 100644
--- a/distros/ubuntu1604/zoneminder.postinst
+++ b/distros/ubuntu1604/zoneminder.postinst
@@ -22,7 +22,7 @@ if [ "$1" = "configure" ]; then
if [ "$ZM_DB_HOST" = "localhost" ]; then
- if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]; 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 $?
@@ -56,6 +56,10 @@ if [ "$1" = "configure" ]; then
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
@@ -68,6 +72,7 @@ if [ "$1" = "configure" ]; then
# 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
@@ -78,7 +83,6 @@ if [ "$1" = "configure" ]; then
else
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)."
fi
- echo "Done Updating; starting ZoneMinder."
deb-systemd-invoke restart zoneminder.service
fi
diff --git a/distros/ubuntu1604/zoneminder.service b/distros/ubuntu1604/zoneminder.service
index ac719b733..cb2d6791e 100644
--- a/distros/ubuntu1604/zoneminder.service
+++ b/distros/ubuntu1604/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/ubuntu1604/zoneminder.tmpfile b/distros/ubuntu1604/zoneminder.tmpfile
index 9435be2a8..cbfdec1de 100644
--- a/distros/ubuntu1604/zoneminder.tmpfile
+++ b/distros/ubuntu1604/zoneminder.tmpfile
@@ -1,4 +1,4 @@
-d /var/run/zm 0755 www-data www-data
+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/doc-pak/AUTHORS b/doc-pak/AUTHORS
deleted file mode 100644
index 7f0362072..000000000
--- a/doc-pak/AUTHORS
+++ /dev/null
@@ -1,6 +0,0 @@
-ZoneMinder - A Linux based camera monitoring and analysis tool.
-
-This project was imagined and created by Philip Coombes in
-September 2002, shortly after being burglarised of his power tools.
-
-mailto:philip.coombes@zoneminder.com
diff --git a/doc-pak/BUGS b/doc-pak/BUGS
deleted file mode 100644
index 3160e2dce..000000000
--- a/doc-pak/BUGS
+++ /dev/null
@@ -1 +0,0 @@
-Please see README file.
diff --git a/doc-pak/COPYING b/doc-pak/COPYING
deleted file mode 100644
index d60c31a97..000000000
--- a/doc-pak/COPYING
+++ /dev/null
@@ -1,340 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- , 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/doc-pak/ChangeLog b/doc-pak/ChangeLog
deleted file mode 100644
index 3160e2dce..000000000
--- a/doc-pak/ChangeLog
+++ /dev/null
@@ -1 +0,0 @@
-Please see README file.
diff --git a/doc-pak/INSTALL b/doc-pak/INSTALL
deleted file mode 100644
index 1de0ac2ad..000000000
--- a/doc-pak/INSTALL
+++ /dev/null
@@ -1,185 +0,0 @@
-Basic Installation
-==================
-
- These are generic installation instructions.
-
- Please see http://www.zoneminder.com/wiki/index.php/Documentation
- for full details.
-
- The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation. It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions. Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, a file
-`config.cache' that saves the results of its tests to speed up
-reconfiguring, and a file `config.log' containing compiler output
-(useful mainly for debugging `configure').
-
- If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release. If at some point `config.cache'
-contains results you don't want to keep, you may remove or edit it.
-
- The file `configure.in' is used to create `configure' by a program
-called `autoconf'. You only need `configure.in' if you want to change
-it or regenerate `configure' using a newer version of `autoconf'.
-
-The simplest way to compile this package is:
-
- 1. `cd' to the directory containing the package's source code and type
- `./configure' to configure the package for your system. If you're
- using `csh' on an old version of System V, you might need to type
- `sh ./configure' instead to prevent `csh' from trying to execute
- `configure' itself.
-
- Running `configure' takes awhile. While running, it prints some
- messages telling which features it is checking for.
-
- 2. Type `make' to compile the package.
-
- 3. Optionally, type `make check' to run any self-tests that come with
- the package.
-
- 4. Type `make install' to install the programs and any data files and
- documentation.
-
- 5. You can remove the program binaries and object files from the
- source code directory by typing `make clean'. To also remove the
- files that `configure' created (so you can compile the package for
- a different kind of computer), type `make distclean'. There is
- also a `make maintainer-clean' target, but that is intended mainly
- for the package's developers. If you use it, you may have to get
- all sorts of other programs in order to regenerate files that came
- with the distribution.
-
-Compilers and Options
-=====================
-
- Some systems require unusual options for compilation or linking that
-the `configure' script does not know about. You can give `configure'
-initial values for variables by setting them in the environment. Using
-a Bourne-compatible shell, you can do that on the command line like
-this:
- CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
-
-Or on systems that have the `env' program, you can do it like this:
- env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
-
-Compiling For Multiple Architectures
-====================================
-
- You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory. To do this, you must use a version of `make' that
-supports the `VPATH' variable, such as GNU `make'. `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script. `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
-
- If you have to use a `make' that does not supports the `VPATH'
-variable, you have to compile the package for one architecture at a time
-in the source code directory. After you have installed the package for
-one architecture, use `make distclean' before reconfiguring for another
-architecture.
-
-Installation Names
-==================
-
- By default, `make install' will install the package's files in
-`/usr/local/bin', `/usr/local/man', etc. You can specify an
-installation prefix other than `/usr/local' by giving `configure' the
-option `--prefix=PATH'.
-
- You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files. If you
-give `configure' the option `--exec-prefix=PATH', the package will use
-PATH as the prefix for installing programs and libraries.
-Documentation and other data files will still use the regular prefix.
-
- In addition, if you use an unusual directory layout you can give
-options like `--bindir=PATH' to specify different values for particular
-kinds of files. Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
-
- If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-Optional Features
-=================
-
- Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System). The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
- For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-Specifying the System Type
-==========================
-
- There may be some features `configure' can not figure out
-automatically, but needs to determine by the type of host the package
-will run on. Usually `configure' can figure that out, but if it prints
-a message saying it can not guess the host type, give it the
-`--host=TYPE' option. TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name with three fields:
- CPU-COMPANY-SYSTEM
-
-See the file `config.sub' for the possible values of each field. If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the host type.
-
- If you are building compiler tools for cross-compiling, you can also
-use the `--target=TYPE' option to select the type of system they will
-produce code for and the `--build=TYPE' option to select the type of
-system on which you are compiling the package.
-
-Sharing Defaults
-================
-
- If you want to set default values for `configure' scripts to share,
-you can create a site shell script called `config.site' that gives
-default values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists. Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Operation Controls
-==================
-
- `configure' recognizes the following options to control how it
-operates.
-
-`--cache-file=FILE'
- Use and save the results of the tests in FILE instead of
- `./config.cache'. Set FILE to `/dev/null' to disable caching, for
- debugging `configure'.
-
-`--help'
- Print a summary of the options to `configure', and exit.
-
-`--quiet'
-`--silent'
-`-q'
- Do not print messages saying which checks are being made. To
- suppress all normal output, redirect it to `/dev/null' (any error
- messages will still be shown).
-
-`--srcdir=DIR'
- Look for the package's source code in directory DIR. Usually
- `configure' can determine that directory automatically.
-
-`--version'
- Print the version of Autoconf used to generate the `configure'
- script, and exit.
-
-`configure' also accepts some other, not widely useful, options.
diff --git a/doc-pak/NEWS b/doc-pak/NEWS
deleted file mode 100644
index 3160e2dce..000000000
--- a/doc-pak/NEWS
+++ /dev/null
@@ -1 +0,0 @@
-Please see README file.
diff --git a/doc-pak/README b/doc-pak/README
deleted file mode 100644
index e3dfa62e1..000000000
--- a/doc-pak/README
+++ /dev/null
@@ -1,5 +0,0 @@
-
-All documentation for ZoneMinder is now online at
-
-http://www.zoneminder.com/wiki/index.php/Documentation
-
diff --git a/doc-pak/TODO b/doc-pak/TODO
deleted file mode 100644
index 69af9c87e..000000000
--- a/doc-pak/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-Please see README file.
-
diff --git a/docs/_static/Icon_External_Link.png b/docs/_static/Icon_External_Link.png
new file mode 100644
index 000000000..16f9b92db
Binary files /dev/null and b/docs/_static/Icon_External_Link.png differ
diff --git a/docs/_static/README.txt b/docs/_static/README.txt
new file mode 100644
index 000000000..acc1d59d9
--- /dev/null
+++ b/docs/_static/README.txt
@@ -0,0 +1,2 @@
+The external link icon is GPL licensed. Original license link:
+https://commons.wikimedia.org/wiki/Category:External_link_icons#/media/File:Icon_External_Link.png
diff --git a/docs/_static/zmstyle.css b/docs/_static/zmstyle.css
index b8f0235f3..986614fa6 100644
--- a/docs/_static/zmstyle.css
+++ b/docs/_static/zmstyle.css
@@ -1,3 +1,18 @@
img {
border: 1px solid black !important;
}
+
+.admonition-todo {
+ border-top: 2px solid red;
+ border-bottom: 2px solid red;
+ border-left: 2px solid red;
+ border-right: 2px solid red;
+ background-color: #ff6347;
+ }
+
+ a.reference.external:link,
+ a.reference.external:visited{
+ background: url('./Icon_External_Link.png') center right no-repeat;
+ padding-right: 13px;
+
+ }
\ No newline at end of file
diff --git a/docs/api.rst b/docs/api.rst
index 706748880..76eadcaa3 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -1,7 +1,8 @@
+
API
====
-This document will provide an overview of ZoneMinder's API. This is work in progress.
+This document will provide an overview of ZoneMinder's API.
Overview
^^^^^^^^
@@ -13,48 +14,187 @@ The API is built in CakePHP and lives under the ``/api`` directory. It
provides a RESTful service and supports CRUD (create, retrieve, update, delete)
functions for Monitors, Events, Frames, Zones and Config.
-Security
+API Wrappers
+^^^^^^^^^^^^^
+- pyzm is a python wrapper for the ZoneMinder APIs. It supports both the legacy and new token based API, as well as ZM logs/ZM shared memory support. See `its project site `__ for more details. Documentation is `here `__.
+
+API evolution
+^^^^^^^^^^^^^^^
+
+The ZoneMinder API has evolved over time. Broadly speaking the iterations were as follows:
+
+* Prior to version 1.29, there really was no API layer. Users had to use the same URLs that the web console used to 'mimic' operations, or use an XML skin
+* Starting version 1.29, a v1.0 CakePHP based API was released which continues to evolve over time. From a security perspective, it still tied into ZM auth and required client cookies for many operations. Primarily, two authentication modes were offered:
+
+ * You use cookies to maintain session state (``ZM_SESS_ID``)
+ * You use an authentication hash to validate yourself, which included encoding personal information and time stamps which at times caused timing validation issues, especially for mobile consumers
+
+* Starting version 1.34, ZoneMinder has introduced a new "token" based system which is based JWT. We have given it a '2.0' version ID. These tokens don't encode any personal data and can be statelessly passed around per request. It introduces concepts like access tokens, refresh tokens and per user level API revocation to manage security better. The internal components of ZoneMinder all support this new scheme now and if you are using the APIs we strongly recommend you migrate to 1.34 and use this new token system (as a side note, 1.34 also moves from MYSQL PASSWORD to Bcrypt for passwords, which is also a good reason why you should migate).
+* Note that as of 1.34, both versions of API access will work (tokens and the older auth hash mechanism), however we no longer use sessions by default. You will have to add a ``stateful=1`` query parameter during login to tell ZM to set a COOKIE and store the required info in the session. This option is only available if ``OPT_USE_LEGACY_API_AUTH`` is set to ON.
+
+.. note::
+ For the rest of the document, we will specifically highlight v2.0 only features. If you don't see a special mention, assume it applies for both API versions.
+
+
+
+Enabling API
+^^^^^^^^^^^^^
+
+ZoneMinder comes with APIs enabled. To check if APIs are enabled, visit ``Options->System``. If ``OPT_USE_API`` is enabled, your APIs are active.
+For v2.0 APIs, you have an additional option right below it:
+
+ * ``OPT_USE_LEGACY_API_AUTH`` which is enabled by default. When enabled, the `login.json` API (discussed later) will return both the old style (``auth=``) and new style (``token=``) credentials. The reason this is enabled by default is because any existing apps that use the API would break if they were not updated to use v2.0. (Note that zmNinja 1.3.057 and beyond will support tokens)
+
+Enabling secret key
+^^^^^^^^^^^^^^^^^^^
+
+* It is **important** that you create a "Secret Key". This needs to be a set of hard to guess characters, that only you know. ZoneMinder does not create a key for you. It is your responsibility to create it. If you haven't created one already, please do so by going to ``Options->Systems`` and populating ``AUTH_HASH_SECRET``. Don't forget to save.
+* If you plan on using V2.0 token based security, **it is mandatory to populate this secret key**, as it is used to sign the token. If you don't, token authentication will fail. V1.0 did not mandate this requirement.
+
+
+Getting an API key
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To get an API key:
+
+::
+
+ curl -XPOST -d "user=yourusername&pass=yourpassword" https://yourserver/zm/api/host/login.json
+
+
+If you want to use a stateful connection, so you don't have to pass auth credentials with each query, you can use the following:
+
+::
+
+ curl -XPOST -c cookies.txt -d "user=yourusername&pass=yourpassword&stateful=1" https://yourserver/zm/api/host/login.json
+
+This returns a payload like this for API v1.0:
+
+::
+
+ {
+ "credentials": "auth=05f3a50e8f7063",
+ "append_password": 0,
+ "version": "1.33.9",
+ "apiversion": "1.0"
+ }
+
+Or for API 2.0:
+
+::
+
+ {
+ "access_token": "eyJ0eXAiOiJKHE",
+ "access_token_expires": 3600,
+ "refresh_token": "eyJ0eXAiOimPs",
+ "refresh_token_expires": 86400,
+ "credentials": "auth=05f3a50e8f7063", # only if OPT_USE_LEGACY_API_AUTH is enabled
+ "append_password": 0, # only if OPT_USE_LEGACY_API_AUTH is enabled
+ "version": "1.33.9",
+ "apiversion": "2.0"
+ }
+
+Using these keys with subsequent requests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Once you have the keys (a.k.a credentials (v1.0, v2.0) or token (v2.0)) you should now supply that key to subsequent API calls like this:
+
+::
+
+ # v1.0 or 2.0 based API access (will only work if AUTH_HASH_LOGINS is enabled
+
+ # RECOMMENDED: v2.0 token based
+ curl -XGET https://yourserver/zm/api/monitors.json?token=
+
+ # or, for legacy mode:
+
+ curl -XGET https://yourserver/zm/api/monitors.json?auth=
+
+ # or, if you specified -c cookies.txt in the original login request
+
+ curl -b cookies.txt -XGET https://yourserver/zm/api/monitors.json
+
+
+.. NOTE::
+
+ If you are using an ``HTTP GET`` request, the token/auth needs to be passed as a query parameter in the URL. If you are using an ``HTTP POST`` (like when you use the API to modify a monitor, for example), you can choose to pass the token as a data payload instead. The API layer discards data payloads for ``HTTP GET``. Finally, If you don't pass keys, you could also use cookies (not recommended as a general approach).
+
+Key lifetime (v1.0)
+^^^^^^^^^^^^^^^^^^^^^
+
+If you are using the old credentials mechanism present in v1.0, then the credentials will time out based on PHP session timeout (if you are using cookies), or the value of ``AUTH_HASH_TTL`` (if you are using ``auth=`` and have enabled ``AUTH_HASH_LOGINS``) which defaults to 2 hours. Note that there is no way to look at the hash and decipher how much time is remaining. So it is your responsibility to record the time you got the hash and assume it was generated at the time you got it and re-login before that time expires.
+
+Key lifetime (v2.0)
+^^^^^^^^^^^^^^^^^^^^^^
+
+In version 2.0, it is easy to know when a key will expire before you use it. You can find that out from the ``access_token_expires`` and ``refresh_token_expires`` values (in seconds) after you decode the JWT key (there are JWT decode libraries for every language you want). You should refresh the keys before the timeout occurs, or you will not be able to use the APIs.
+
+Understanding access/refresh tokens (v2.0)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you are using V2.0, then you need to know how to use these tokens effectively:
+
+* Access tokens are short lived. ZoneMinder issues access tokens that live for 3600 seconds (1 hour).
+* Access tokens should be used for all subsequent API accesses.
+* Refresh tokens should ONLY be used to generate new access tokens. For example, if an access token lives for 1 hour, before the hour completes, invoke the ``login.json`` API above with the refresh token to get a new access token. ZoneMinder issues refresh tokens that live for 24 hours.
+* To generate a new refresh token before 24 hours are up, you will need to pass your user login and password to ``login.json``
+
+**To Summarize:**
+
+* Pass your ``username`` and ``password`` to ``login.json`` only once in 24 hours to renew your tokens
+* Pass your "refresh token" to ``login.json`` once in two hours (or whatever you have set the value of ``AUTH_HASH_TTL`` to) to renew your ``access token``
+* Use your ``access token`` for all API invocations.
+
+In fact, V2.0 will reject your request (if it is not to ``login.json``) if it comes with a refresh token instead of an access token to discourage usage of this token when it should not be used.
+
+This minimizes the amount of sensitive data that is sent over the wire and the lifetime durations are made so that if they get compromised, you can regenerate or invalidate them (more on this later)
+
+Understanding key security
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* Version 1.0 uses an MD5 hash to generate the credentials. The hash is computed over your secret key (if available), username, password and some time parameters (along with remote IP if enabled). This is not a secure/recommended hashing mechanism. If your auth hash is compromised, an attacker will be able to use your hash till it expires. To avoid this, you could disable the user in ZoneMinder. Furthermore, enabling remote IP (``AUTH_HASH_REMOTE_IP``) requires that you issue future requests from the same IP that generated the tokens. While this may be considered an additional layer for security, this can cause issues with mobile devices.
+
+* Version 2.0 uses a different approach. The hash is a simple base64 encoded form of "claims", but signed with your secret key. Consider for example, the following access key:
+
+::
+
+ eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJab25lTWluZGVyIiwiaWF0IjoxNTU3OTQwNzUyLCJleHAiOjE1NTc5NDQzNTIsInVzZXIiOiJhZG1pbiIsInR5cGUiOiJhY2Nlc3MifQ.-5VOcpw3cFHiSTN5zfGDSrrPyVya1M8_2Anh5u6eNlI
+
+If you were to use any `JWT token verifier `__ it can easily decode that token and will show:
+
+::
+
+ {
+ "iss": "ZoneMinder",
+ "iat": 1557940752,
+ "exp": 1557944352,
+ "user": "admin",
+ "type": "access"
+ }
+ Invalid Signature
+
+
+Don't be surprised. JWT tokens, by default, are `not meant to be encrypted `__. It is just an assertion of a claim. It states that the issuer of this token was ZoneMinder,
+It was issued at (iat) Wednesday, 2019-05-15 17:19:12 UTC and will expire on (exp) Wednesday, 2019-05-15 18:19:12 UTC. This token claims to be owned by an admin and is an access token. If your token were to be stolen, this information is available to the person who stole it. Note that there are no sensitive details like passwords in this claim.
+
+However, that person will **not** have your secret key as part of this token and therefore, will NOT be able to create a new JWT token to get, say, a refresh token. They will however, be able to use your access token to access resources just like the auth hash above, till the access token expires (2 hrs). To revoke this token, you don't need to disable the user. Go to ``Options->API`` and tap on "Revoke All Access Tokens". This will invalidate the token immediately (this option will invalidate all tokens for all users, and new ones will need to be generated).
+
+Over time, we will provide you with more fine grained access to these options.
+
+**Summarizing good practices:**
+
+* Use HTTPS, not HTTP
+* If possible, use free services like `LetsEncrypt `__ instead of self-signed certificates (sometimes this is not possible)
+* Keep your tokens as private as possible, and use them as recommended above
+* If you believe your tokens are compromised, revoke them, but also check if your attacker has compromised more than you think (example, they may also have your username/password or access to your system via other exploits, in which case they can regenerate as many tokens/credentials as they want).
+
+
+.. NOTE::
+ Subsequent sections don't explicitly callout the key addition to APIs. We assume that you will append the correct keys as per our explanation above.
+
+
+Examples
^^^^^^^^^
-The APIs tie into ZoneMinder's existing security model. This means if you have
-OPT_AUTH enabled, you need to log into ZoneMinder using the same browser you plan to
-use the APIs from. If you are developing an app that relies on the API, you need
-to do a POST login from the app into ZoneMinder before you can access the API.
-
-Then, you need to re-use the authentication information of the login (returned as cookie states)
-with subsequent APIs for the authentication information to flow through to the APIs.
-
-This means if you plan to use cuRL to experiment with these APIs, you first need to do
-
-::
-
- curl -d "username=XXXX&password=YYYY&action=login&view=console" -c cookies.txt http://yourzmip/zm/index.php
-
-replacing *XXXX* and *YYYY* with your username and password, respectively.
-
-Please make sure you do this in a directory where you have write permissions, otherwise cookies.txt will not be created
-and the command will silently fail.
-
-
-What the "-c cookies.txt" does is store a cookie state reflecting that you have logged into ZM. You now need
-to apply that cookie state to all subsequent APIs. You do that by using a '-b cookies.txt' to subsequent APIs if you are
-using CuRL like so:
-
-::
-
- curl -b cookies.txt http://yourzmip/zm/api/monitors.json
-
-This would return a list of monitors and pass on the authentication information to the ZM API layer.
-
-So remember, if you are using authentication, please add a ``-b cookies.txt`` to each of the commands below if you are using
-CuRL. If you are not using CuRL and writing your own app, you need to make sure you pass on cookies to subsequent requests
-in your app.
-
-Examples (please read security notice above)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-You will see each URL ending in either ``.xml`` or ``.json``. This is the
-format of the request, and it determines the format that any data returned to
-you will be in. I like json, however you can use xml if you'd like.
(In all examples, replace 'server' with IP or hostname & port where ZoneMinder is running)
@@ -74,6 +214,22 @@ Return a list of all monitors
curl http://server/zm/api/monitors.json
+It is worthwhile to note that starting ZM 1.32.3 and beyond, this API also returns a ``Monitor_Status`` object per monitor. It looks like this:
+
+::
+
+ "Monitor_Status": {
+ "MonitorId": "2",
+ "Status": "Connected",
+ "CaptureFPS": "1.67",
+ "AnalysisFPS": "1.67",
+ "CaptureBandwidth": "52095"
+ }
+
+
+If you don't see this in your API, you are running an older version of ZM. This gives you a very convenient way to check monitor status without calling the ``daemonCheck`` API described later.
+
+
Retrieve monitor 1
^^^^^^^^^^^^^^^^^^^
@@ -90,6 +246,13 @@ This API changes monitor 1 to Modect and Enabled
::
curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]=1"
+
+Get Daemon Status of Monitor 1
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ curl http://server/zm/api/monitors/daemonStatus/id:1/daemon:zmc.json
Add a monitor
^^^^^^^^^^^^^^
@@ -258,6 +421,15 @@ This returns number of events per monitor that were recorded in the last day whe
+Return sorted events
+^^^^^^^^^^^^^^^^^^^^^^
+
+This returns a list of events within a time range and also sorts it by descending order
+
+::
+
+ curl -XGET "http://server/zm/api/events/index/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:208:43:56.json?sort=StartTime&direction=desc"
+
Configuration Apis
^^^^^^^^^^^^^^^^^^^
@@ -326,11 +498,13 @@ Create a Zone
&Zone[MaxBlobs]=\
&Zone[OverloadFrames]=0"
-PTZ Control APIs
-^^^^^^^^^^^^^^^^
+PTZ Control Meta-Data APIs
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
PTZ controls associated with a monitor are stored in the Controls table and not the Monitors table inside ZM. What that means is when you get the details of a Monitor, you will only know if it is controllable (isControllable:true) and the control ID.
To be able to retrieve PTZ information related to that Control ID, you need to use the controls API
+Note that these APIs only retrieve control data related to PTZ. They don't actually move the camera. See the "PTZ on live streams" section to move the camera.
+
This returns all the control definitions:
::
@@ -348,7 +522,192 @@ ZM APIs have various APIs that help you in determining host (aka ZM) daemon stat
::
- curl -XGET http://server/zm/api/host/daemonCheck.json # 1 = ZM running 0=not running
curl -XGET http://server/zm/api/host/getLoad.json # returns current load of ZM
- curl -XGET http://server/zm/api/host/getDiskPercent.json # returns in GB (not percentage), disk usage per monitor (that is, space taken to store various event related information,images etc. per monitor)
+
+ # Note that ZM 1.32.3 onwards has the same information in Monitors.json which is more reliable and works for multi-server too.
+ curl -XGET http://server/zm/api/host/daemonCheck.json # 1 = ZM running 0=not running
+
+ # The API below uses "du" to calculate disk space. We no longer recommend you use it if you have many events. Use the Storage APIs instead, described later
+ curl -XGET http://server/zm/api/host/getDiskPercent.json # returns in GB (not percentage), disk usage per monitor (that is,space taken to store various event related information,images etc. per monitor)
+
+
+Storage and Server APIs
+^^^^^^^^^^^^^^^^^^^^^^^
+
+ZoneMinder introduced many new options that allowed you to configure multiserver/multistorage configurations. While a part of this was available in previous versions, a lot of rework was done as part of ZM 1.31 and 1.32. As part of that work, a lot of new and useful APIs were added. Some of these are part of ZM 1.32 and others will be part of ZM 1.32.3 (of course, if you build from master, you can access them right away, or wait till a stable release is out.
+
+
+
+This returns storage data for my single server install. If you are using multi-storage, you'll see many such "Storage" entries, one for each storage defined:
+
+::
+
+ curl http://server/zm/api/storage.json
+
+Returns:
+
+::
+
+ {
+ "storage": [
+ {
+ "Storage": {
+ "Id": "0",
+ "Path": "\/var\/cache\/zoneminder\/events",
+ "Name": "Default",
+ "Type": "local",
+ "Url": null,
+ "DiskSpace": "364705447651",
+ "Scheme": "Medium",
+ "ServerId": null,
+ "DoDelete": true
+ }
+ }
+ ]
+ }
+
+
+
+"DiskSpace" is the disk used in bytes. While this doesn't return disk space data as rich as ``/host/getDiskPercent``, it is much more efficient.
+
+Similarly,
+
+::
+
+ curl http://server/zm/api/servers.json
+
+Returns:
+
+::
+
+ {
+ "servers": [
+ {
+ "Server": {
+ "Id": "1",
+ "Name": "server1",
+ "Hostname": "server1.mydomain.com",
+ "State_Id": null,
+ "Status": "Running",
+ "CpuLoad": "0.9",
+ "TotalMem": "6186237952",
+ "FreeMem": "156102656",
+ "TotalSwap": "536866816",
+ "FreeSwap": "525697024",
+ "zmstats": false,
+ "zmaudit": false,
+ "zmtrigger": false
+ }
+ }
+ ]
+ }
+
+This only works if you have a multiserver setup in place. If you don't it will return an empty array.
+
+Other APIs
+^^^^^^^^^^
+This is not a complete list. ZM supports more parameters/APIs. A good way to dive in is to look at the `API code `__ directly.
+
+Streaming Interface
+^^^^^^^^^^^^^^^^^^^
+Developers working on their application often ask if there is an "API" to receive live streams, or recorded event streams.
+It is possible to stream both live and recorded streams. This isn't strictly an "API" per-se (that is, it is not integrated
+into the Cake PHP based API layer discussed here) and also why we've used the term "Interface" instead of an "API".
+
+Live Streams
+~~~~~~~~~~~~~~
+What you need to know is that if you want to display "live streams", ZoneMinder sends you streaming JPEG images (MJPEG)
+which can easily be rendered in a browser using an ``img src`` tag.
+
+For example:
+
+::
+
+
+
+ # or
+
+
+
+
+
+
+will display a live feed from monitor id 1, scaled down by 50% in quality and resized to 640x480px.
+
+* This assumes ``/zm/cgi-bin`` is your CGI_BIN path. Change it to what is correct in your system
+* The "auth" token you see above is required if you use ZoneMinder authentication. To understand how to get the auth token, please read the "Login, Logout & API security" section below.
+* The "connkey" parameter is essentially a random number which uniquely identifies a stream. If you don't specify a connkey, ZM will generate its own. It is recommended to generate a connkey because you can then use it to "control" the stream (pause/resume etc.)
+* Instead of dealing with the "auth" token, you can also use ``&user=username&pass=password`` where "username" and "password" are your ZoneMinder username and password respectively. Note that this is not recommended because you are transmitting them in a URL and even if you use HTTPS, they may show up in web server logs.
+
+
+PTZ on live streams
+-------------------
+PTZ commands are pretty cryptic in ZoneMinder. This is not meant to be an exhaustive guide, but just something to whet your appetite:
+
+
+Lets assume you have a monitor, with ID=6. Let's further assume you want to pan it left.
+
+You'd need to send a:
+``POST`` command to ``https://yourserver/zm/index.php`` with the following data payload in the command (NOT in the URL)
+
+``view=request&request=control&id=6&control=moveConLeft&xge=30&yge=30``
+
+Obviously, if you are using authentication, you need to be logged in for this to work.
+
+Like I said, at this stage, this is only meant to get you started. Explore the ZoneMinder code and use "Inspect source" as you use PTZ commands in the ZoneMinder source code.
+`control_functions.php `__ is a great place to start.
+
+
+Pre-recorded (past event) streams
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Similar to live playback, if you have chosen to store events in JPEG mode, you can play it back using:
+
+::
+
+
+
+ # or
+
+
+
+
+
+* This assumes ``/zm/cgi-bin`` is your CGI_BIN path. Change it to what is correct in your system
+* This will playback event 293820, starting from frame 1 as an MJPEG stream
+* Like before, you can add more parameters like ``scale`` etc.
+* auth and connkey have the same meaning as before, and yes, you can replace auth by ``&user=usename&pass=password`` as before and the same security concerns cited above apply.
+
+If instead, you have chosen to use the MP4 (Video) storage mode for events, you can directly play back the saved video file:
+
+::
+
+
+
+
+ # or
+
+
+
+
+This above will play back the video recording for event 294690
+
+What other parameters are supported?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The best way to answer this question is to play with ZoneMinder console. Open a browser, play back live or recorded feed, and do an "Inspect Source" to see what parameters
+are generated. Change and observe.
+
+
+
+Further Reading
+^^^^^^^^^^^^^^^^
+
+As described earlier, treat this document as an "introduction" to the important parts of the API and streaming interfaces.
+There are several details that haven't yet been documented. Till they are, here are some resources:
+
+* zmNinja, the open source mobile app for ZoneMinder is 100% based on ZM APIs. Explore its `source code `__ to see how things work.
+* Launch up ZM console in a browser, and do an "Inspect source". See how images are being rendered. Go to the networks tab of the inspect source console and look at network requests that are made when you pause/play/forward streams.
+* If you still can't find an answer, post your question in the `forums `__ (not the github repo).
+
+
diff --git a/docs/conf.py b/docs/conf.py
index 9bda5df7b..9de7bd820 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -31,7 +31,7 @@ def setup(app):
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = []
+extensions = ['sphinx.ext.todo']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -265,3 +265,6 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
+
+# Display todos by setting to True
+todo_include_todos = True
diff --git a/docs/faq.rst b/docs/faq.rst
index 7735ff38f..56b67d082 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -1,8 +1,11 @@
FAQ
=====
+.. todo:: needs to be reviewed - some entries may be old/invalid. I've done one round, but icOn needs to review.
+
This is the FAQ page. Feel free to contribute any FAQs that you think are missing.
+.. note:: It is always a good idea to refer to the `ZoneMinder forums `__ for tips and tricks. While we try and make sure this FAQ is pruned/adjusted to align with the latest stable release, some of the entries may no longer be accurate (or there may be better suggestions in the forums).
How can I stop ZoneMinder filling up my disk?
---------------------------------------------
@@ -29,23 +32,22 @@ To delete events that are older than 7 days, create a new filter with "Date" set
Save with 'Run Filter In Background' enabled to have it run automatically.
Optional skip archived events: click on the plus sign next to -7 days to add another condition. "and" "archive status" equal to "unarchived only".
-Optional slow delete: limit the number of results to 3. If you have a large backlog of events that would be deleted, this can hard spike the CPU usage for a long time. Limiting the number of results to only the first three each time the filter is run spreads out the delete processes over time, dramatically lessening the CPU load.
+Optional slow delete: limit the number of results to a number, say ``10`` in the filter. If you have a large backlog of events that would be deleted, this can hard spike the CPU usage for a long time. Limiting the number of results to only the first three each time the filter is run spreads out the delete processes over time, dramatically lessening the CPU load.
+
+
+.. warning:: We no longer recommend use enable ``OPT_FAST_DELETE`` or ``RUN_AUDIT`` anymore, unless you are using an old or low powered system to run Zoneminder. Please consider the remaining tips in this answer to be 'generally deprecated, use only if you must'.
There are two methods for ZM to remove files when they are deleted that can be found in Options under the System tab ZM_OPT_FAST_DELETE and ZM_RUN_AUDIT.
ZM_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 you are trying to do a lot of events at once. If you are running on an older or under-powered system, you may want to set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later. If you do so, disk space will not be freed immediately so you will need to run zmaudit more frequently. On modern systems, we recommend that you leave this off.
-
-
+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 you are trying to do a lot of events at once. If you are running on an older or under-powered system, you may want to set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later. If you do so, disk space will not be freed immediately so you will need to run zmaudit more frequently. On modern systems, we recommend that you leave this **off**.
ZM_RUN_AUDIT:
The zmaudit daemon exists to check that the saved information in the database and on the file system 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 synchronize the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is recommended for most systems however if you have a very large number of events the process of scanning the database and file system may take a long time and impact performance. In this case you may prefer to not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times.
-
-
ZM_AUDIT_CHECK_INTERVAL:
The zmaudit daemon exists to check that the saved information in the database and on the files system 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 synchronize 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 file system 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.
@@ -64,12 +66,15 @@ In *general* a good estimate of memory required would be:
Min Bits of Memory = 20% overhead * (image-width*image-height*image buffer size*target color space*number of cameras)
Where:
+
* image-width and image-height are the width and height of images that your camera is configured for (in my case, 1280x960). This value is in the Source tab for each monitor
* image buffer size is the # of images ZM will keep in memory (this is used by ZM to make sure it has pre and post images before detecting an alarm - very useful because by the time an alarm is detected, the reason for the alarm may move out of view and a buffer is really useful for this, including for analyzing stats/scores). This value is in the buffers tab for each monitor
* target color space is the color depth - 8bit, 24bit or 32bit. It's again in the source tab of each monitor
-The 1.2 at the start is basically adding 20% on top of the calculation to account for image/stream overheads (this is an estimate)
+
+The 20% overhead on top of the calculation to account for image/stream overheads (this is an estimate)
The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space:
+
::
1280*960 = 1,228,800 (bytes)
@@ -96,8 +101,6 @@ So a good rule of thumb is to make sure you have twice the memory as the calcula
As it turns out, ZM uses mapped memory and by default, 50% of your physical memory is what this will grow to. When you reach that limit , ZM breaks down with various errors.
-(**Note**: Mapped memory is applicable when you install ZoneMinder with mapped memory support, which is the default mode. If you have specifically disabled mapped memory then please see the next FAQ enty on how to increase shared memory)
-
A good way to know how much memory is allocated to ZM for its operation is to do a ``df -h``
A sample output on Ubuntu:
@@ -116,140 +119,43 @@ For example, if you want to increase this limit to 70% of your memory, add the f
where SHMPATH is the ``Mounted on`` path. Here, that would be ``/run/shm``. Other systems may be ``/dev/shm``.
-What does a 'Can't shmget: Invalid argument' error in my logs mean? (and my camera does not display at higher resolutions)
---------------------------------------------------------------------------------------------------------------------------------------
-
-(*Note*: This is applicable for systems that have mapped memory disabled in ZoneMinder. By default, Mapped memory is enabled and unless you have disabled it manually, please refer to the "Math for Memory" question above and how to increase mapped memory limits)
-
-This error is discussed in the README in the following excerpt:-
-''...this is caused by an attempt to allocate an amount of shared memory greater than your system can handle. The size it requests is based on the following formula, ``ring buffer size x image width x image height x 3 (for 24 bit images) + a bit of overhead``.
-
-So, for example:
-
-::
-
- 384x288 capture resolution, that makes: 110 592 pixels
- in 24 bit color that's x 3 = 331,776 bytes per frame
- by 80 frames ring buffer x80 = 26,542,080 bytes per camera
- by 4 cameras x4 = 106,168,320 bytes.
- Plus 10% overhead = 116,785,152 bytes
- Thats 114,048 kB, respectively 111.38 MB.
- If my shared memory is set to 134,217,728, which is exactly 128MB,
- that means I shouldn't have any problem.
- (Note that 1kbyte = 1024bytes, 1MB = 1024 kB)
-
-If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here `__
-
-You should be able to use a similar procedure with other distributions to modify the shared memory pool without kernel recompilations though in some cases this may be necessary. Note, this error also sometimes occurs if you have an old shared memory segment lying around from a previous run that is too small. Use the ipcs and ipcrm system commands to check and remove it if necessary.'"
-
-You can often find out how many 4KB shared memory pages are available by typing the following :-
-
-::
-
- # cat /proc/sys/kernel/shmall
- 2097152
-
-In recent kernels the shmall is set to 2097152 memory pages multiplied by 4096 bytes per page for a total of 8 GB of shared memory available. You only need to increase the shmall value if you have a computer with more than 8GB of memory and wish to use more of it for shared memory usage, such as large databases.
-
-The most shared memory bytes you can allocate in one go :-
-
-::
-
- # cat /proc/sys/kernel/shmmax
- 33554432
-
-In recent kernels the shmmax is set to 33554432 bytes for only 32 MB of maximum shared memory allocatable at a time, hardly enough for ZoneMinder to go above 320 x 240 x 24-bit resolution at 40 frames in the buffer if it is using the /dev/shm shared memory device, so this value needs to be increased. If you are using ZoneMinder with the memory mapped (mmap) compile time option then this doesn't affect you.
-
-To change the value to 128 MB temporarily during this kernel execution type (for example) :-
-``echo 536870912 >/proc/sys/kernel/shmmax``
-
-*Be sure to restart ZoneMinder after this.*
-
-However be aware that sometimes you will only need to change the shmmax value as shmall is often large enough. Also changing these values in this way is only effective until your machine is rebooted.
-
-To change them permanently you will need to edit ``/etc/sysctl.conf`` and add the following lines (for example) :-
-``kernel.shmmax = 536870912``
-
-Or if your distribution has the ``/etc/sysctl.d/`` folder you can create a file in this folder without modifying the ``/etc/sysctl.d`` so you won't lose the changes during distro upgrades :-
-```echo kernel.shmmax = 536870912 >/etc/sysctl.d/60-kernel-shm.conf```
-
-To load these settings in the sysctl.conf file type:
-``sysctl -p``
-
-To check your shared memory settings type:
-``ipcs -l``
-
-Note that with Megapixel cameras like the Axis 207mw becoming cheaper and more attractive, the above memory settings are not adequate. To get Zoneminder working with a full 1280x1024 resolution camera in full color, increase ``134217728`` (128 MB) to, for example, ``268435456`` (256 MB) and multiple this value by each camera.
-
-These changes will now also be set the next time your machine is restarted.
-
-Versions 1.24.x of ZoneMinder also allows you to use an alternate method of shared memory allocation, `Mmap mapped memory `__ . This requires less configuration and can be simpler to use. Mapped memory allows you to use a special type of file as the placeholder for your memory and this file is 'mapped' into memory space for easy and fast access.
-
-To enable mapped memory in ZoneMinder you need add add the --enable--mmap=yes switch to your configure line. By default mapped memory files are created in /dev/shm which on most distributions is a dedicated pseudo-partition containing memory formatted as a filesystem. If your system uses a different path then this can be changed in ZoneMinder in Options->paths->PATH_MAP. It uses a filesystem type called `tmpfs `__. If you type ``df -h`` you should see this area and the size of memory it currently allows. To increase size for tmpfs you need to edit /etc/default/tmpfs. Search for:
-``SHM_SIZE=128M``
-and change to something like
-``SHM_SIZE=1G``
-then reboot the system. You could possibly need to change RUN_SIZE, too.
-
-It is important that you do not use a disk based filesystem for your memory mapped files as this will cause memory access to be extremely slow. ZoneMinder creates files called .zm.mmap. in the mapped memory filesystem.
-
-Mapped memory is subject to the same limitations in terms of total memory as using more traditional shared memory but does not require any configuration per allocation or chunk. In future versions of ZoneMinder this will be the default shared memory storage method.
-
-Another good article about shared memory settings can be found `here `__ .
-
-The essential difference was that the kernel.shmall setting is NOT in a direct memory setting in KB but in pages of memory. it is Max Pages of memory
-
-*For example:* If you want to allocate a maximum memory setting to 8GB you have to convert it to the number of pages (or segments).
-with a page size of 4096.
-kernel.shmall = 8000x1024x1024/4096
-``kernel.shmall = 2097152``
-NOT 8388608000 as would be suggested in the RedHat article linked above.
-
-shmmax is the max amount to allocate in one request -
-this is is an actual memory size (as opposed to pages) set to 4GB
-``kernel.shmmax = 4294967296``
-
-The ``/etc/sysctl.conf`` would have these lines
-
-::
-
- kernel.shmall = 2097152
- kernel.shmmax = 4294967296
-
-As above, reload your sysctl.conf with ``sysctl -p`` and check that the settings are correct with ``ipcs -l``.
I have enabled motion detection but it is not always being triggered when things happen in the camera view
---------------------------------------------------------------------------------------------------------------
-ZoneMinder uses zones to examine images for motion detection. When you create the initial zones you can choose from a number of preset values for sensitivity etc. Whilst these are usually a good starting point they are not always suitable for all situations and you will probably need to tweak the values for your specific circumstances. The meanings of the various settings are described in the documentation (`here `__) however if you believe you have sensible settings configured then there are two diagnostic approaches you can use.
+ZoneMinder uses zones to examine images for motion detection. When you create the initial zones you can choose from a number of preset values for sensitivity etc. Whilst these are usually a good starting point they are not always suitable for all situations and you will probably need to tweak the values for your specific circumstances. The meanings of the various settings are described in the documentation (`here `__). Another user contributed illustrated Zone definition guide can be found here: `An illustrated guide to Zones `__
+
+However if you believe you have sensible settings configured then there are diagnostic approaches you can use.
-Another user contributed illustrated Zone definition guide can be found here: `An illustrated guide to Zones `__
Event Statistics
^^^^^^^^^^^^^^^^^
-The first technique is to use event statistics. Firstly you should ensure they are switched on in Options->Logging->RECORD_EVENT_STATS. This will then cause the raw motion detection statistics for any subsequently generated events to be written to the DB. These can then be accessed by first clicking on the Frames or Alarm Frames values of the event from any event list view in the web gui. Then click on the score value to see the actual values that caused the event. Alternatively the stats can be accessed by clicking on the 'Stats' link when viewing any individual frame. The values displayed there correspond with the values that are used in the zone configuration and give you an idea of what 'real world' values are being generated.
+The first technique is to use event statistics. Firstly you should ensure they are switched on in ``Options->Logging->RECORD_EVENT_STATS``. This will then cause the raw motion detection statistics for any subsequently generated events to be written to the DB. These can then be accessed by first clicking on the Frames or Alarm Frames values of the event from any event list view in the web gui. Then click on the score value to see the actual values that caused the event. Alternatively the stats can be accessed by clicking on the 'Stats' link when viewing any individual frame. The values displayed there correspond with the values that are used in the zone configuration and give you an idea of what 'real world' values are being generated.
Note that if you are investigating why events 'do not' happen then these will not be saved and so won't be accessible. The best thing to do in that circumstance is to make your zone more sensitive so that it captures all events (perhap even ones you don't want) so you can get an idea of what values are being generated and then start to adjust back to less sensitive settings if necessary. You should make sure you test your settings under a variety of lighting conditions (e.g. day and night, sunny or dull) to get the best feel for that works and what doesn't.
Using statistics will slow your system down to a small degree and use a little extra disk space in the DB so once you are happy you can switch them off again. However it is perfectly feasible to keep them permanently on if your system is able to cope which will allow you to review your setting periodically.
-Diagnostic Images
-^^^^^^^^^^^^^^^^^^^^
-The second approach is to use diagnostic images which are saved copies of the intermediate images that ZM uses when determining motion detection. These are switched on and off using Options->Logging->RECORD_DIAG_IMAGES.
+Diagnostic Images along with FIFO
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The second approach is to use diagnostic images which are saved copies of the intermediate images that ZM uses when determining motion detection. These are switched on and off using ``Options->Logging->RECORD_DIAG_IMAGES``.
+
+.. note:: In addition to the detailed explanation below, a recently added ``RECORD_DIAG_IMAGES_FIFO`` option, also available in ``Options->Logging`` can be an invaluable tool to see how your current motion settings are affecting motion detection. The ``delta`` stream along with the ``raw`` (json output) stream can be invaluable to see the effect in real time. Please refer to the explanation of this feature in :doc:`/userguide/options/options_logging`
There are two kinds of diagnostic images which are and are written (and continuously overwritten) to the top level monitor event directory. If an event occurs then the files are additionally copied to the event directory and renamed with the appropriate frame number as a prefix.
-The first set are produced by the monitor on the image as a whole. The diag-r.jpg image is the current reference image against which all individual frames are compared and the diag-d.jpg image is the delta image highlighting the difference between the reference image and the last analysed image. In this images identical pixels will be black and the more different a pixel is the whiter it will be. Viewing this image and determining the colour of the pixels is a good way of getting a feel for the pixel differences you might expect (often more than you think).
+The first set are produced by the monitor on the image as a whole. The ``diag-r.jpg`` image is the current reference image against which all individual frames are compared and the ``diag-d.jpg`` image is the delta image highlighting the difference between the reference image and the last analysed image. In this images identical pixels will be black and the more different a pixel is the whiter it will be. Viewing this image and determining the colour of the pixels is a good way of getting a feel for the pixel differences you might expect (often more than you think).
-The second set of diag images are labelled as diag--.jpg where zoneid is the id of the zone in question (Smile) and the stage is where in the alarm check process the image is generated from. So if you have several zones you can expect to see multiple files. Also these files are only interested in what is happening in their zone only and will ignore anything else outside of the zone. The stages that each number represents are as follows,
+The second set of diag images are labelled as ``diag--.jpg`` where zoneid is the id of the zone in question (Smile) and the stage is where in the alarm check process the image is generated from. So if you have several zones you can expect to see multiple files. Also these files are only interested in what is happening in their zone only and will ignore anything else outside of the zone. The stages that each number represents are as follows,
-# Alarmed Pixels - This image shows all pixels in the zone that are considered to be alarmed as white pixels and all other pixels as black.
-# Filtered Pixels - This is as stage one except that all pixels removed by the filters are now black. The white pixels represent the pixels that are candidates to generate an event.
-# Raw Blobs - This image contains all alarmed pixels from stage 2 but aggrageted into blobs. Each blob will have a different greyscale value (between 1 and 254) so they can be difficult to spot with the naked eye but using a colour picker or photoshop will make it easier to see what blob is what.
-# Filtered Blobs - This image is as stage 3 but under (or over) sized blobs have been removed. This is the final step before determining if an event has occurred, just prior to the number of blobs being counted. Thus this image forms the basis for determining whether an event is generated and outlining on alarmed images is done from the blobs in this image.
+* Alarmed Pixels - This image shows all pixels in the zone that are considered to be alarmed as white pixels and all other pixels as black.
+* Filtered Pixels - This is as stage one except that all pixels removed by the filters are now black. The white pixels represent the pixels that are candidates to generate an event.
+* Raw Blobs - This image contains all alarmed pixels from stage 2 but aggrageted into blobs. Each blob will have a different greyscale value (between 1 and 254) so they can be difficult to spot with the naked eye but using a colour picker or photoshop will make it easier to see what blob is what.
+* Filtered Blobs - This image is as stage 3 but under (or over) sized blobs have been removed. This is the final step before determining if an event has occurred, just prior to the number of blobs being counted. Thus this image forms the basis for determining whether an event is generated and outlining on alarmed images is done from the blobs in this image.
Using the above images you should be able to tell at all stages what ZM is doing to determine if an event should happen or not. They are useful diagnostic tools but as is mentioned elsewhere they will massively slow your system down and take up a great deal more space. You should never leave ZM running for any length of time with diagnostic images on.
+
Why can't ZoneMinder capture images (either at all or just particularly fast) when I can see my camera just fine in xawtv or similar?
----------------------------------------------------------------------------------------------------------------------------------------------
@@ -259,14 +165,14 @@ On average a card that can capture at 25fps per chip PAL for one input will do m
When using xawtv etc to view the stream you are not looking at an image captured using the frame grabber but the card's video memory mapped onto your screen. This requires no capture or processing unless you do an explicit capture via the J or ctrl-J keys for instance. Some cards or drivers do not support the frame grabber interface at all so may not work with ZoneMinder even though you can view the stream in xawtv. If you can grab a still using the grab functionality of xawtv then in general your card will work with ZoneMinder.
-Why can't I see streamed images when I can see stills in the Zone window etc?
+Why can't I see streamed images when I can see stills in the zone window etc?
-------------------------------------------------------------------------------------
This issue is normally down to one of two causes
-1) You are using Internet Explorer and are trying to view multi-part jpeg streams. IE does not support these streams directly, unlike most other browsers. You will need to install Cambozola or another multi-part jpeg aware plugin to view them. To do this you will need to obtain the applet from the Downloads page and install the cambozola.jar file in the same directory as the ZoneMinder php files. Then find the ZoneMinder Options->Images page and enable ZM_OPT_CAMBOZOLA and enter the web path to the .jar file in ZM_PATH_CAMBOZOLA. This will ordinarily just be cambozola.jar. Provided (Options / B/W tabs) WEB_H_CAN_STREAM is set to auto and WEB_H_STREAM_METHOD is set to jpeg then Cambozola should be loaded next time you try and view a stream.
+1) You are using Internet Explorer and are trying to view multi-part jpeg streams. IE does not support these streams directly, unlike most other browsers. You will need to install Cambozola or another multi-part jpeg aware plugin to view them. To do this you will need to obtain the applet from the Downloads page and install the cambozola.jar file in the same directory as the ZoneMinder php files. Then find the ZoneMinder Options->Images page and enable ``OPT_CAMBOZOLA`` and enter the web path to the .jar file in ``PATH_CAMBOZOLA``. This will ordinarily just be cambozola.jar. Provided (Options / B/W tabs) ``WEB_H_CAN_STREAM`` is set to auto and ``WEB_H_STREAM_METHOD`` is set to jpeg then Cambozola should be loaded next time you try and view a stream.
-'''NOTE''': If you find that the Cambozola applet loads in IE but the applet just displays the version # of Cambozola and the author's name (as opposed to seeing the streaming images), you may need to chmod (''-rwxrwxr-x'') your (''usr/share/zoneminder/'') cambozola.jar:
+**NOTE**: If you find that the Cambozola applet loads in IE but the applet just displays the version of Cambozola and the author's name (as opposed to seeing the streaming images), you may need to chmod (``-rwxrwxr-x``) your (``usr/share/zoneminder/``) cambozola.jar:
::
@@ -274,9 +180,7 @@ This issue is normally down to one of two causes
Once I did this, images started to stream for me.
-2) The other common cause for being unable to view streams is that you have installed the ZoneMinder cgi binaries (zms and nph-zms) in a different directory than your web server is expecting. Make sure that the --with-cgidir option you use to the ZoneMinder configure script is the same as the CGI directory configure for your web server. If you are using Apache, which is the most common one, then in your httpd.conf file there should be a line like ``ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"`` where the last directory in the quotes is the one you have specified. If not then change one or the other to match. Be warned that configuring apache can be complex so changing the one passed to the ZoneMinder configure (and then rebuilding and reinstalling) is recommended in the first instance. If you change the apache config you will need to restart apache for the changes to take effect. If you still cannot see stream reliably then try changing Options->Paths->ZM_PATH_ZMS to just use zms if nph-zms is specified, or vice versa. Also check in your apache error logs.
-
-Also, please check the value of the ZM_PATH_ZMS setting under the Paths Options tab. It is where you configure the URL to the zms or nph-zms CGI executable. Under most Debian-based distros this value should be /zm/cgi-bin/nph-zms but in the past may have been /cgi-bin/nph-zms or you may have configured it to be something else.
+2) The other common cause for being unable to view streams is that you have installed the ZoneMinder cgi binaries (zms and nph-zms) in a different directory than your web server is expecting. Make sure that the --with-cgidir option you use to the ZoneMinder configure script is the same as the CGI directory configure for your web server. If you are using Apache, which is the most common one, then in your httpd.conf file there should be a line like ``ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"`` where the last directory in the quotes is the one you have specified. If not then change one or the other to match. Be warned that configuring apache can be complex so changing the one passed to the ZoneMinder configure (and then rebuilding and reinstalling) is recommended in the first instance. If you change the apache config you will need to restart apache for the changes to take effect. If you still cannot see stream reliably then try changing ``ZM_PATH_ZMS`` in your ``/etc/zm/config`` directory to just use zms if nph-zms is specified, or vice versa. Also check in your apache error logs.
Lastly, please look for errors created by the zmc processes. If zmc isn't running, then zms will not be able to get an image from it and will exit.
@@ -302,6 +206,11 @@ change the 3 to a 1
network.http.max-persistent-connections-per-proxy -> 100 again
network.http.max-persistent-connections-per-server -> 100 again
+
+I can't see more than 6 monitors in montage on my browser
+---------------------------------------------------------
+Browsers such a Chrome and Safari only support upto 6 streams from the same domain. To work around that, take a look at the multi-port configuration discussed in the ``MIN_STREAMING_PORT`` configuration in :doc:`/userguide/options/options_network`
+
Why is ZoneMinder using so much CPU?
---------------------------------------
@@ -311,12 +220,12 @@ There are a number of specific reasons why processor loads can be high either by
The main causes are.
- * Using a video palette other than greyscale or RGB24. This can cause a relatively minor performace hit, though still significant. Although some cameras and cards require using planar palettes ZM currently doesn't support this format internally and each frame is converted to an RGB representation prior to processing. Unless you have compelling reasons for using YUV or reduced RGB type palettes such as hitting USB transfer limits I would experiment to see if RGB24 or greyscale is quicker. Put your monitors into 'Monitor' mode so that only the capture daemons are running and monitor the process load of these (the 'zmc' processes) using top. Try it with various palettes to see if it makes a difference.
+ * Using a video palette other than greyscale or RGB24. This can cause a relatively minor performance hit, though still significant. Although some cameras and cards require using planar palettes ZM currently doesn't support this format internally and each frame is converted to an RGB representation prior to processing. Unless you have compelling reasons for using YUV or reduced RGB type palettes such as hitting USB transfer limits I would experiment to see if RGB24 or greyscale is quicker. Put your monitors into 'Monitor' mode so that only the capture daemons are running and monitor the process load of these (the 'zmc' processes) using top. Try it with various palettes to see if it makes a difference.
* Big image sizes. A image of 640x480 requires at least four times the processing of a 320x240 image. Experiment with different sizes to see what effect it may have. Sometimes a large image is just two interlaced smaller frames so has no real benefit anyway. This is especially true for analog cameras/cards as image height over 320 (NTSC) or 352 PAL) are invariably interlaced.
* Capture frame rates. Unless there's a compelling reason in your case there is often little benefit in running cameras at 25fps when 5-10fps would often get you results just as good. Try changing your monitor settings to limit your cameras to lower frame rates. You can still configure ZM to ignore these limits and capture as fast as possible when motion is detected.
* Run function. Obviously running in Record or Mocord modes or in Modect with lots of events generates a lot of DB and file activity and so CPU and load will increase.
- * Basic default detection zones. By default when a camera is added one detection zone is added which covers the whole image with a default set of parameters. If your camera covers a view in which various regions are unlikely to generate a valid alarm (ie the sky) then I would experiment with reducing the zone sizes or adding inactive zones to blank out areas you don't want to monitor. Additionally the actual settings of the zone themselves may not be optimal. When doing motion detection the number of changed pixels above a threshold is examined, then this is filter, then contiguous regions are calculated to see if an alarm is generated. If any maximum or minimum threshold is exceeded according to your zone settings at any time the calculation stops. If your settings always result in the calculations going through to the last stage before being failed then additional CPU time is used unnecessarily. Make sure your maximum and minimumzone thresholds are set to sensible values and experiment by switching RECORD_EVENT_STATS on and seeing what the actual values of alarmed pixels etc are during sample events.
- * Optimise your settings. After you've got some settings you're happy with then switching off RECORD_EVENT_STATS will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ZM_FAST_RGB_DIFFS and the JPEG_xxx_QUALITY ones.
+ * Basic default detection zones. By default when a camera is added one detection zone is added which covers the whole image with a default set of parameters. If your camera covers a view in which various regions are unlikely to generate a valid alarm (ie the sky) then I would experiment with reducing the zone sizes or adding inactive zones to blank out areas you don't want to monitor. Additionally the actual settings of the zone themselves may not be optimal. When doing motion detection the number of changed pixels above a threshold is examined, then this is filter, then contiguous regions are calculated to see if an alarm is generated. If any maximum or minimum threshold is exceeded according to your zone settings at any time the calculation stops. If your settings always result in the calculations going through to the last stage before being failed then additional CPU time is used unnecessarily. Make sure your maximum and minimumzone thresholds are set to sensible values and experiment by switching ``RECORD_EVENT_STATS`` on and seeing what the actual values of alarmed pixels etc are during sample events.
+ * Optimise your settings. After you've got some settings you're happy with then switching off ``RECORD_EVENT_STATS`` will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ``ZM_FAST_RGB_DIFFS`` and the ``JPEG_xxx_QUALITY`` ones.
I'm sure there are other things which might make a difference such as what else you have running on the box and memory sizes (make sure there's no swapping going on). Also speed of disk etc will make some difference during event capture and also if you are watching the whole time then you may have a bunch of zms processes running also.
@@ -330,17 +239,19 @@ Using the timeline view is only recommended when using FireFox, however even the
This function has from time to time been corrupted in the SVN release or in the stable releases, try and reinstall from a fresh download.
+.. _disk_bw_faq:
+
How much Hard Disk Space / Bandwidth do I need for ZM?
---------------------------------------------------------------
-Please see `this excel sheet `__ or `this online excel sheet `__ (both are user contributed excel sheets)
+Please see `this online excel sheet `__. Note that this is just an estimate
-Or go to `this link `__ for the Axis bandwidth calculator. Although this is aimed at Axis cameras it still produces valid results for any kind of IP camera.
+Or go to `this link `__ for the Axis bandwidth calculator. Although this is aimed at Axis cameras it still produces valid results for any kind of IP camera.
As a quick guide I have 4 cameras at 320x240 storing 1 fps except during alarm events. After 1 week 60GB of space in the volume where the events are stored (/var/www/html/zm) has been used.
When I try and run ZoneMinder I get lots of audit permission errors in the logs and it won't start
-------------------------------------------------------------------------------------------------------
-Many Linux distributions nowadays are built with security in mind. One of the latest methods of achieving this is via SELinux (Secure Linux) which controls who is able to run what in a more precise way then traditional accounting and file based permissions (`link `__).
+Many Linux distributions nowadays are built with security in mind. One of the latest methods of achieving this is via SELinux (Secure Linux) which controls who is able to run what in a more precise way then traditional accounting and file based permissions (`link `__).
If you are seeing entries in your system log like:
Jun 11 20:44:02 kernel: audit(1150033442.443:226): avc: denied { read } for pid=5068
@@ -353,38 +264,13 @@ Note that SELinux may cause errors other than those listed above. If you are in
How do I enable ZoneMinder's security?
-------------------------------------------
-In the console, click on Options. Check the box next to "ZM_OPT_USE_AUTH". You will immediately be asked to login. The default username is 'admin' and the password is 'admin'.
+In the console, click on ``Options->System``. Check the box next to ``ZM_OPT_USE_AUTH``. You will immediately be asked to login. The default username is 'admin' and the password is 'admin'.
To Manage Users:
-In main console, go to Options->Users.
+In main console, go to ``Options->Users``.
-You may also consider to use the web server security, for example, htaccess files under Apache scope; You may even use this as an additional/redundant security on top of Zoneminders built-in security features;
+You may also consider to use the web server security, for example, htaccess files under Apache scope; You may even use this as an additional/redundant security on top of Zoneminders built-in security features. Note that if you choose to enable webserver auth, zmNinja may have issues. Please read the `zmNinja FAQ on basic authentication `__ for more information. Also please note that zmNinja does not support digest authentication.
-Why does ZM stop recording once I have 32000 events for my monitor?
-------------------------------------------------------------------------
-Storing more than 32k files in a single folder is a limitation of some filesystems. To avoid this, enable USE_DEEP_STORAGE under Options.
-
-USE_DEEP_STORAGE is now the default for new ZoneMinder systems so this limitation should only apply to users upgrading from a previous version of ZoneMinder.
-
-Versions of ZM from 1.23.0 onwards allow you to have a deeper filesystem with fewer files per individual directory. As well as not being susceptible to the 32k limit, this is also somewhat faster.
-
-If you have upgraded from a previous version of ZoneMinder and this option is not already enabled, it is very important to follow the steps below to enable it on an existing system. Failure to properly follow these steps **WILL RESULT IN LOSS OF YOUR DATA!**
-
-::
-
- # Stop ZoneMinder
- # Backup your event data and the dB if you have the available storage
- # Enable USE_DEEP_STORAGE under Options.
- # From the command line, run "sudo zmupdate.pl --migrate-events"
- # Monitor the output for any events that fail to convert.
- # After the conversion completes, you can restart ZoneMinder
-
-Note that you can re-run the migrate-events command if any error messages scroll off the screen.
-
-You can read about the lack of a limit in the number of sub-directories in the ext4 filesystem at: `this link `__
-and see what tools may assist in your use of this filesystem `here `__
-If you search for ext3 or reiserfs on the forums you will find various threads on this issue with guidance on
-how to convert.
Managing system load (with IP Cameras in mind)
----------------------------------------------------
@@ -410,7 +296,7 @@ Zoneminder runs on Linux, Linux measures system load using "load", which is comp
A load of 1.0 means the processor has "just enough to do right now". Also worth noting that a load of 4.0 means exactly the same for a quad processor machine - each number equals a single processor's workload. A very high load can be fine on a computer that has a stacked workload - such as a machine sending out bulk emails, or working its way through a knotty problem; it'll just keep churning away until it's done. However - Zoneminder needs to process information in real time so it can't afford to stack its jobs, it needs to deal with them right away.
-For a better and full explanation of Load: `Please read this `__
+For a better and full explanation of Load: `Please read this `__
My load is too high, how can I reduce it?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -427,7 +313,7 @@ Zoneminder is *very* tweakable and it's possible to tune it to compromise. The f
* Experiment with using jpeg instead of mjpeg. Some users have reported it gives better performance, but YMMV.
- * Tweak the zones. Keep them as small and as few as possible. Stick to one zone unless you really need more. Read `this `__ for an easy to understand explanation along with the official Zone guide.
+ * Tweak the zones. Keep them as small and as few as possible. Stick to one zone unless you really need more. Read `this `__ for an easy to understand explanation along with the official Zone guide.
* Schedule. If you are running a linux system at near capacity, you'll need to think carefully about things like backups and scheduled tasks. updatedb - the process which maintains a file database so that 'locate' works quickly, is normally scheduled to run once a day and if on a busy system can create a heavy increase on the load. The same is true for scheduled backups, especially those which compress the files. Re-schedule these tasks to a time when the cpu is less likely to be busy, if possible - and also use the "nice" command to reduce their priority. (crontab and /etc/cron.daily/ are good places to start)
@@ -440,73 +326,16 @@ More expensive options:
* Faster CPU. Simple but effective. Zoneminder also works very well with multiple processor systems out of the box (if SMP is enabled in your kernel). The load of different cameras is spread across the processors.
- * Try building Zoneminder with processor specific instructions that are optimised to the system it will be running on, also increasing the optimisation level of GCC beyond -O2 will help.
+ * Try building Zoneminder with processor specific instructions that are optimised to the system it will be running on, also increasing the optimisation level of GCC beyond -O2 will help. This topic is beyond the scope of this document.
-::
-
- ./configure CFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" CXXFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp"
-
-The above command is optimised for an Athlon XP cpu so you will need to use the specific processor tag for your cpu, also the compiler optimisation has been increased to -O3.
-
-You also need to put in your normal ./configure commands as if you were compiling with out this optimisation.
-
-A further note is that the compile must be performed on the system that Zoneminder will be running on as this optimisation will make it hardware specific code.
-
-Processor specific commands can be found in the GCC manual along with some more options that may increase performanc.
-``__
-
-The below command has been used to compile Zoneminder on a Athlon XP system running CentOS 5.5 and along with the libjpeg-turbo modification to reduce the CPU load in half, libjpeg-turbo reduced the load by 1/3 before the processor optimisation.
-::
-
- ./configure --with-webdir=/var/www/html/zm --with-cgidir=/var/www/cgi-bin CFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" CXXFLAGS="-D__STDC_CONSTANT_MACROS -g -O3 -march=athlon-xp -mtune=athlon-xp" --enable-mmap --sysconfdir=/etc/zm
-
-The following command has been used to compile Zoneminder 1.25 on a CentOS 6.0 system, the native command should choose the processor automatically during compile time, this needs to be performed on the actual system!!.
-
-::
-
- CFLAGS="-g -O3 -march=native -mtune=native" CXXFLAGS="-D__STDC_CONSTANT_MACROS -g -O3 -march=native -mtune=native" ./configure --with-webdir=/var/www/html/zm --with-cgidir=/var/www/cgi-bin --with-webuser=apache --with-webgroup=apache ZM_DB_HOST=localhost ZM_DB_NAME=zm ZM_DB_USER=your_zm_user ZM_DB_PASS=your_zm_password ZM_SSL_LIB=openssl
+Processor specific commands can be found in the `GCC manual `__ along with some more options that may increase performance.
What about disks and bandwidth?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A typical 100mbit LAN will cope with most setups easily. If you're feeding from cameras over smaller or internet links, obviously fps will be much lower.
-Disk and Bandwidth calculators are referenced on the Zoneminder wiki here: http://www.zoneminder.com/wiki/index.php/FAQ#How_much_Hard_Disk_Space_.2F_Bandwidth_do_I_need_for_ZM.3F
-
-
-Building ZoneMinder
---------------------
-
-When running configure I am getting a lot of messages about not being able to compile the ffmpeg libraries
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-If you see output from configure that looks like this
-
-::
-
- checking libavcodec/avcodec.h usability... no
- checking libavcodec/avcodec.h presence... yes
- configure: WARNING: libavcodec/avcodec.h: present but cannot be compiled
- configure: WARNING: libavcodec/avcodec.h: check for missing
- prerequisite headers?
- configure: WARNING: libavcodec/avcodec.h: see the Autoconf documentation
- configure: WARNING: libavcodec/avcodec.h: section "Present But
- Cannot Be Compiled"
- configure: WARNING: libavcodec/avcodec.h: proceeding with the compiler's
- result
- configure: WARNING: ## ------------------------------------- ##
- configure: WARNING: ## Report this to support@zoneminder.com ##
- configure: WARNING: ## ------------------------------------- ##
-
-then it is caused not by the ZoneMinder build system but ffmpeg itself. However there is a workaround you can use which is to add ``CPPFLAGS=-D__STDC_CONSTANT_MACROS``
-
-to the ZoneMinder ``./configure`` command which should solve the issue. However this is not a proper 'fix' as such, which can only come from the ffmpeg project itself.
-
-I cannot build ZoneMinder and am getting lots of undefined C++ template errors
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-This is almost certainly due to the 'ccache' package which attempts to speed up compilation by caching compiled objects. Unfortunately one of the side effects is that it breaks the GNU g++ template resolution method that ZoneMinder uses in building by prevent files getting recompiled. The simplest way around this is to remove the ccache package using your distros package manager.
+Disk and Bandwidth calculators are referenced in :ref:`disk_bw_faq`.
How do I build for X10 support?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -530,7 +359,7 @@ To save a run state you should first configure your monitors for Modect, Record,
Now you can switch between these two states by selecting them from the same dialog you saved them, or from the command line from issue the command ''zmpkg.pl '', for example ''zmpkg.pl Daytime''.
-The final step you need to take, is scheduling the time the changes take effect. For this you can use `cron `__. A simple entry to change to the Daylight state at at 8am and to the nighttime state at 8pm would be as follows,
+The final step you need to take, is scheduling the time the changes take effect. For this you can use `cron `__. A simple entry to change to the Daylight state at at 8am and to the nighttime state at 8pm would be as follows,
::
@@ -544,50 +373,10 @@ Although the example above describes changing states at different times of day,
How can I use ZoneMinder to trigger something else when there is an alarm?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-ZoneMinder includes a perl API which means you can create a script to interact with the ZM shared memory data and use it in your own scripts to react to ZM alarms or to trigger ZM to generate new alarms. Full details are in the README or by doing ``perldoc ZoneMinder``, ``perldoc ZoneMinder::SharedMem`` etc.
-Below is an example script that checks all monitors for alarms and when one occurs, prints a message to the screen. You can add in your own code to make this reaction a little more useful.
+ZoneMinder includes a perl API which means you can create a script to interact with the ZM shared memory data and use it in your own scripts to react to ZM alarms or to trigger ZM to generate new alarms. Full details are in the README or by doing ``perldoc ZoneMinder`` etc.
-::
+ZoneMinder provides a sample alarm script called `zmalarm.pl `__ that you can refer to as a starting point.
- #!/usr/bin/perl -w
-
- use strict;
-
- use ZoneMinder;
-
- $| = 1;
-
- zmDbgInit( "myscript", level=>0, to_log=>0, to_syslog=>0, to_term=>1 );
-
- my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS );
-
- my $sql = "select M.*, max(E.Id) as LastEventId from Monitors as M left join Events as E on M.Id = E.MonitorId where M.Function != 'None' group by (M.Id)";
- my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
-
- my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() );
- my @monitors;
- while ( my $monitor = $sth->fetchrow_hashref() )
- {
- push( @monitors, $monitor );
- }
-
- while( 1 )
- {
- foreach my $monitor ( @monitors )
- {
- next if ( !zmMemVerify( $monitor ) );
-
- if ( my $last_event_id = zmHasAlarmed( $monitor, $monitor->{LastEventId} ) )
- {
- $monitor->{LastEventId} = $last_event_id;
- print( "Monitor ".$monitor->{Name}." has alarmed\n" );
- #
- # Do your stuff here
- #
- }
- }
- sleep( 1 );
- }
Trouble Shooting
-------------------
@@ -596,13 +385,9 @@ This is also how to obtain the info that we need to help you on the forums.
What logs should I check for errors?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-ZoneMinder creates its own logs and are usually located in the ``/tmp`` directory.
+ZoneMinder creates its own logs and are usually located in the ``/var/log/`` directory. Refer to the logging discussion in :doc:`/userguide/options/options_logging` for more details on where logs are stored and how to enable various log levels.
-The ZoneMinder logs for the RPM packages are located in ``/var/log/zm``.
-
-Depending on your problem errors can show up in any of these logs but, usually the logs of interest are ``zmdc.log`` and ``zmpkg.log`` if ZM is not able to start.
-
-Now since ZM is dependent on other components to work, you might not find errors in ZM but in the other components.
+Since ZM is dependent on other components to work, you might not find errors in ZM but in the other components.
::
@@ -618,8 +403,6 @@ If ZM is not functioning, you should always be able to find an error in at least
This will append any data entered to any of these logs to your console screen (``-f``). To exit, hit [ctrl -c].
-More verbose logging for the ZoneMinder binaries is available by enabling the debug option from the control panel and will be placed in the path you have configured for the debug logs. Output can be limited to a specific binary as described in the Debug options page under the "?" marks.
-
How can I trouble shoot the hardware and/or software?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -643,12 +426,33 @@ The apache web server needs to have the right permissions and configuration to b
Why am I getting broken images when trying to view events?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Zoneminder and the Apache web server need to have the right permissions. Check this forum topic and similar ones:
-http://www.zoneminder.com/forums/viewtopic.php?p=48754#48754
+Zoneminder and the Apache web server need to have the right permissions. Check `this forum topic `__ and similar ones:
+
+
+
+I can review events for the current day, but ones from yesterday and beyond error out
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you've checked that the ``www-data`` user has permissions to the storage folders, perhaps your php.ini's timezone setting is incorrect. They _must_ match for certain playback functions.
+
+If you're using Linux, this can be found using the following command:
+
+::
+
+ timedatectl | grep "Time zone"
+
+If using FreeBSD, you can use this one-liner:
+
+::
+
+ cd /usr/share/zoneinfo/ && find * -type f -exec cmp -s {} /etc/localtime \; -print;
+
+Once you know what timezone your system is set to make sure you set the right time zone in ZM (Available in ``Options->System->TimeZone``)
+
Why is the image from my color camera appearing in black and white?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If you recently upgraded to zoneminder 1.26, there is a per camera option that defaults to black and white and can be mis-set if your upgrade didn't happen right. See this thread: http://www.zoneminder.com/forums/viewtopic.php?f=30&t=21344
+If you recently upgraded to zoneminder 1.26, there is a per camera option that defaults to black and white and can be mis-set if your upgrade didn't happen right. See this thread: https://forums.zoneminder.com/viewtopic.php?f=30&t=21344
This may occur if you have a NTSC analog camera but have configured the source in ZoneMinder as PAL for the Device Format under the source tab. You may also be mislead because zmu can report the video port as being PAL when the camera is actually NTSC. Confirm the format of your analog camera by checking it's technical specifications, possibly found with the packaging it came in, on the manufacturers website, or even on the retail website where you purchased the camera. Change the Device Format setting to NTSC and set it to the lowest resolution of 320 x 240. If you have confirmed that the camera itself is NTSC format, but don't get a picture using the NTSC setting, consider increasing the shared memory '''kernel.shmall''' and '''kernel.shmmax''' settings in /etc/sysctl.conf to a larger value such as 268435456. This is also the reason you should start with the 320x240 resolution, so as to minimize the potential of memory problems which would interfere with your attempts to troubleshoot the device format issue. Once you have obtained a picture in the monitor using the NTSC format, then you can experiment with raising the resolution.
@@ -658,39 +462,17 @@ If this camera is attached to a capture card, then you may have selected the wro
Why do I only see black screens with a timestamp when monitoring my camera?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-In the monitor windows where you see the black screen with a timestamp, select settings and enter the Brightness, Contrast, Hue, and Color settings reported for the device by '''zmu -d -q -v'''. 32768 may be appropriate values to try for these settings. After saving the settings, select Settings again to confirm they saved successfully.
+In the monitor windows where you see the black screen with a timestamp, select settings and enter the Brightness, Contrast, Hue, and Color settings reported for the device by ``zmu -d -q -v``. 32768 may be appropriate values to try for these settings. After saving the settings, select Settings again to confirm they saved successfully.
-I am getting messages about a backtrace in my logs, what do I do?
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-If you are seeing entries in your log like the following
-
-::
-
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6 [0x3347230210]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6(memset+0xce) [0x334727684e]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x40ee9a]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x419946]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x4213cf]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma(cos+0x35c) [0x404674]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6(__libc_start_main+0xf4) [0x334721da44]]
- Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma(cos+0xd1) [0x4043e9]]
- Jan 11 20:25:22 localhost zma_m2[19051]: INF [Backtrace complete]
-
-then you can help diagnose the problem by running a special command to translate the hex addresses into helpful information. This command is called addr2line and you can type 'man addr2line' for more information.
-Basically addr2line takes two sets of parameters, the first is the name of the binary file, and the second is a list of addresses. Both of these pieces of information are displayed in the logs. The filename is the first part after the 'Backtrace:' tag, in this case /usr/local/bin/zma, though it may well be different in your case. Some of the lines refer to libraries rather than the zma executable but those can be ignored for now, the important part is noting which ZM binary is involved. The binary file is passed in following the -e flag. The addresses to pass to addr2line are those contained in the '[]' pairs. Again you can ignore those that are on a line that refers to a library but it will not hurt if you include them.
-So in the example above, the command would be ``addr2line -e /usr/local/bin/zma 0x40ee9a 0x419946 0x4213cf 0x404674 0x4043e9``
-This should then dump out a more symbolic list containing source file names and line numbers, and it is this information which will be helpful if posted to the forums. Sometimes addr2line fails to produce useful output. This is usually because either the problem is so severe that it has corrupted the stack and prevented useful information from being displayed, or that you have either compiled ZM without the -g flag for debug, or you have stripped the binaries of symbol information after installation. This this case you would need to rebuild temporarily with debug enabled for the information to be useful.
-
-
-This error some times happens when a linked camera looses its link or it is corrupted by the user or some other system event, try deleting the affected cameras and recreating them in the Zoneminder console.
How do I repair the MySQL Database?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There is two ways to go about this. In most cases you can run from the command prompt ->
-* mysqlcheck --all-databases --auto-repair -p'''your_database_password''' -u '''your_databse_user'''
+``mysqlcheck --all-databases --auto-repair -p your_database_password -u your_databse_user``
-If that does not work then you will have to make sure that ZoneMinder is stopped then run the following (nothing should be using the database while running this and you will have to adjust for your correct path if it is different). ->
-* myisamchk --silent --force --fast --update-state -O key_buffer=64M -O sort_buffer=64M -O read_buffer=1M -O write_buffer=1M /var/lib/mysql/*/*.MYI
+If that does not work then you will have to make sure that ZoneMinder is stopped then run the following (nothing should be using the database while running this and you will have to adjust for your correct path if it is different):
+
+``myisamchk --silent --force --fast --update-state -O key_buffer=64M -O sort_buffer=64M -O read_buffer=1M -O write_buffer=1M /var/lib/mysql/*/*.MYI``
How do I repair the MySQL Database when the cli fails?
@@ -698,7 +480,7 @@ How do I repair the MySQL Database when the cli fails?
In Ubuntu, the commands listed above do not seem to work. However, actually doing it by hand from within MySQL does. (But that is beyond the scope of this document) But that got me thinking... And phpmyadmin does work. Bring up a terminal.
``sudo apt-get install phpmyadmin``
-Now go to http://zoneminder_IP/ and stop the ZM service. Continue to http://zoneminder_IP/phpmyadmin and select the zoneminder database. Select and tables marked 'in use' and pick the action 'repare' to fix. Restart the zoneminder service from the web browser. Remove or disable the phpmyadmin tool, as it is not always the most secure thing around, and opens your database wide to any skilled hacker.
+Now go to ``http://zoneminder_IP/`` and stop the ZM service. Continue to ``http://zoneminder_IP/phpmyadmin`` and select the zoneminder database. Select and tables marked 'in use' and pick the action 'repare' to fix. Restart the zoneminder service from the web browser. Remove or disable the phpmyadmin tool, as it is not always the most secure thing around, and opens your database wide to any skilled hacker.
``sudo apt-get remove phpmyadmin``
I upgraded by distribution and ZM stopped working
@@ -728,6 +510,8 @@ What causes "Invalid JPEG file structure: two SOI markers" from zmc (1.24.x)
Some settings that used to be global only are now per camera. On the Monitor Source tab, if you are using Remote Protocol "HTTP" and Remote Method "Simple", try changing Remote Method to "Regexp".
+
+
Miscellaneous
-------------------
I see ZoneMinder is licensed under the GPL. What does that allow or restrict me in doing with ZoneMinder?
@@ -741,10 +525,13 @@ The ZoneMinder license is described at the end of the documentation and consists
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.
-This means that ZoneMinder is licensed under the terms described `here `__. There is a comprehensive FAQ covering the GPL at http://www.gnu.org/licenses/gpl-faq.html but in essence you are allowed to redistribute or modify GPL licensed software provided that you release your distribution or modifications freely under the same terms. You are allowed to sell systems based on GPL software. You are not allowed to restrict or reduce the rights of GPL software in your distribution however. Of course if you are just making modifications for your system locally you are not releasing changes so you have no obligations in this case. I recommend reading the GPL FAQ for more in-depth coverage of this issue.
+This means that ZoneMinder is licensed under the terms described `here `__. There is a comprehensive FAQ covering the GPL at https://www.gnu.org/licenses/gpl-faq.html but in essence you are allowed to redistribute or modify GPL licensed software provided that you release your distribution or modifications freely under the same terms. You are allowed to sell systems based on GPL software. You are not allowed to restrict or reduce the rights of GPL software in your distribution however. Of course if you are just making modifications for your system locally you are not releasing changes so you have no obligations in this case. I recommend reading the GPL FAQ for more in-depth coverage of this issue.
Can I use ZoneMinder as part of my commercial product?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-The GPL license allows you produce systems based on GPL software provided your systems also adhere to that license and any modifications you make are also released under the same terms. The GPL does not permit you to include ZoneMinder in proprietary systems (see http://www.gnu.org/licenses/gpl-faq.html#GPLInProprietarySystem for details). If you wish to include ZoneMinder in this kind of system then you will need to license ZoneMinder under different terms. This is sometimes possible and you will need to contact me for further details in these circumstances.
+The GPL license allows you produce systems based on GPL software provided your systems also adhere to that license and any modifications you make are also released under the same terms. The GPL does not permit you to include ZoneMinder in proprietary systems (see https://www.gnu.org/licenses/gpl-faq.html#GPLInProprietarySystem for details). If you wish to include ZoneMinder in this kind of system then you will need to license ZoneMinder under different terms. This is sometimes possible and you will need to contact me for further details in these circumstances.
+I am having issues with zmNinja and/or Event Notification Server
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+zmNinja and the Event Notification Server are 3rd party solutions. The developer maintains exhaustive `documentation and FAQs `__. Please direct your questions there.
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
index 46da979cf..740e9ec26 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -10,7 +10,9 @@ ZoneMinder Documentation
faq
contributing
-Welcome to ZoneMinder's documentation, the following resources are available
+Welcome to ZoneMinder's documentation. Please navigate to one of the links below.
+
+If you are facing issues that are not covered in the documentation, please feel free to check the `ZoneMinder Forums `__ or join the `ZoneMinder-Chat Slack channel `__ if you prefer real time interaction.
:doc:`installationguide/index`
Many distribution repos only hold older versions of ZoneMinder, current versions contain many bug fixes and updated functionality. Instructions here for installing updated packages or compiling from source.
@@ -27,6 +29,9 @@ Welcome to ZoneMinder's documentation, the following resources are available
:doc:`contributing`
How to contribute to ZoneMinder. As a community project we always need help, you don't need to be a coder to test or update documentation.
+`Event Notification Server and Machine Learning hooks `__
+ Documentation for the 3rd party Event Notification Server and Machine Learning for Object/People/Face detection.
+
Indices and tables
diff --git a/docs/installationguide/debian.rst b/docs/installationguide/debian.rst
index 0b926435f..74a9ca81d 100644
--- a/docs/installationguide/debian.rst
+++ b/docs/installationguide/debian.rst
@@ -3,6 +3,146 @@ Debian
.. contents::
+Easy Way: Debian Stretch
+------------------------
+
+This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.32.3 on Debian 9.8.
+
+**Step 1:** Make sure your system is up to date
+
+Open a console and use ``su`` command to become Root.
+
+::
+
+ apt update
+ apt upgrade
+
+
+**Step 2:** Setup Sudo (optional but recommended)
+
+By default Debian does not come with sudo, so you have to install it and configure it manually. This step is optional but recommended and the following instructions assume that you have setup sudo. If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly.
+
+::
+
+ apt install sudo
+ usermod -a -G sudo
+ exit
+
+Now your terminal session is back under your normal user. You can check that you are now part of the sudo group with the command ``groups``, "sudo" should appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``.
+
+
+**Step 3:** Install Apache and MySQL
+
+These are not dependencies for the ZoneMinder package as they could be installed elsewhere. If they are not installed yet in your system, you have to trigger their installation manually.
+
+::
+
+ sudo apt install apache2 mysql-server
+
+**Step 4:** Add ZoneMinder's Package repository to your apt sources
+
+ZoneMinder's Debian packages are not included in Debian's official package repositories. To be able to install ZoneMinder with APT, you have to edit the list of apt sources and add ZoneMinder's repository.
+
+::
+
+ sudo nano /etc/apt/sources.list
+
+Add the following to the bottom of the file
+
+::
+
+ # ZoneMinder repository
+ deb https://zmrepo.zoneminder.com/debian/release stretch/
+
+CTRL+o and to save
+CTRL+x to exit
+
+Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS.
+::
+
+ sudo apt install apt-transport-https
+
+Finally, download the GPG key for ZoneMinder's repository:
+::
+
+ wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add -
+
+
+**Step 5:** Install ZoneMinder
+
+::
+
+ sudo apt update
+ sudo apt install zoneminder
+
+**Step 6:** Read the Readme
+
+The rest of the install process is covered in the README.Debian, so feel free to have
+a read.
+
+::
+
+ zcat /usr/share/doc/zoneminder/README.Debian.gz
+
+
+**Step 7:** Enable ZoneMinder service
+
+::
+
+ sudo systemctl enable zoneminder.service
+
+**Step 8:** Configure Apache
+
+The following commands will setup the default /zm virtual directory and configure
+required apache modules.
+
+::
+
+ sudo a2enconf zoneminder
+ sudo a2enmod rewrite
+ sudo a2enmod cgi # this is done automatically when installing the package. Redo this command manually only for troubleshooting.
+
+
+**Step 9:** Edit Timezone in PHP
+
+Automated way:
+::
+
+ sudo sed -i "s/;date.timezone =/date.timezone = $(sed 's/\//\\\//' /etc/timezone)/g" /etc/php/7.0/apache2/php.ini
+
+Manual way
+::
+
+ sudo nano /etc/php/7.0/apache2/php.ini
+
+Search for [Date] (Ctrl + w then type Date and press Enter) and change
+date.timezone for your time zone. Don't forget to remove the ; from in front
+of date.timezone.
+
+::
+
+ [Date]
+ ; Defines the default timezone used by the date functions
+ ; http://php.net/date.timezone
+ date.timezone = America/New_York
+
+CTRL+o then [Enter] to save
+
+CTRL+x to exit
+
+
+**Step 10:** Start ZoneMinder
+
+Reload Apache to enable your changes and then start ZoneMinder.
+
+::
+
+ sudo systemctl reload apache2
+ sudo systemctl start zoneminder
+
+You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
+
+
Easy Way: Debian Jessie
-----------------------
@@ -49,11 +189,17 @@ Add the following to the bottom of the file
::
# Backports repository
- deb http://httpredir.debian.org/debian jessie-backports main contrib non-free
+ deb http://archive.debian.org/debian/ jessie-backports main contrib non-free
CTRL+o and to save
CTRL+x to exit
+Run the following
+
+::
+
+ echo 'Acquire::Check-Valid-Until no;' > /etc/apt/apt.conf.d/99no-check-valid-until
+
**Step 5:** Install ZoneMinder
::
@@ -68,8 +214,7 @@ a read.
::
- gunzip /usr/share/doc/zoneminder/README.Debian.gz
- cat /usr/share/doc/zoneminder/README.Debian
+ zcat /usr/share/doc/zoneminder/README.Debian.gz
**Step 7:** Setup Database
@@ -81,7 +226,7 @@ should you choose to change default database user and password.
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,index on zm.* to 'zmuser'@localhost identified by "zmpass";' | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql
-** Step 8:** zm.conf Permissions
+**Step 8:** zm.conf Permissions
Adjust permissions to the zm.conf file to allow web account to access it.
@@ -91,7 +236,7 @@ Adjust permissions to the zm.conf file to allow web account to access it.
**Step 9:** Setup ZoneMinder service
-::
+ ::
systemctl enable zoneminder.service
@@ -127,7 +272,24 @@ CTRL+o then [Enter] to save
CTRL+x to exit
-**Step 12:** Start ZoneMinder
+
+**Step 12:** Please check the configuration
+
+Zoneminder 1.32.x
+ 1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms
+ ::
+ cat /etc/zm/conf.d/zmcustom.conf
+
+ 2. Check config of /etc/apache2/conf-enabled/zoneminder.conf has the same ScriptAlias /zm/cgi-bin that is configured
+ in ZM_PATH. The part /nph-zms has to be left out of the ScriptAlias
+
+ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
+
+
+ ::
+ cat /etc/apache2/conf-enabled/zoneminder.conf
+
+**Step 13:** Start ZoneMinder
Reload Apache to enable your changes and then start ZoneMinder.
@@ -136,7 +298,7 @@ Reload Apache to enable your changes and then start ZoneMinder.
systemctl reload apache2
systemctl start zoneminder
-**Step 13:** Making sure ZoneMinder works
+**Step 14:** Making sure ZoneMinder works
1. Open up a browser and go to ``http://hostname_or_ip/zm`` - should bring up ZoneMinder Console
diff --git a/docs/installationguide/dedicateddrive.rst b/docs/installationguide/dedicateddrive.rst
index f0dfadc28..6a7367b8b 100644
--- a/docs/installationguide/dedicateddrive.rst
+++ b/docs/installationguide/dedicateddrive.rst
@@ -31,7 +31,6 @@ Add the following content to the file and save your changes:
::
ZM_DIR_EVENTS=/full/path/to/the/events/folder
- ZM_DIR_IMAGES=/full/path/to/the/images/folder
**Step 5:** Start ZoneMinder and inspect the ZoneMinder log files for errors.
::
diff --git a/docs/installationguide/easydocker.rst b/docs/installationguide/easydocker.rst
new file mode 100644
index 000000000..899ce0e5d
--- /dev/null
+++ b/docs/installationguide/easydocker.rst
@@ -0,0 +1,11 @@
+An Easy To Use Docker Image
+===========================
+If you are interested in trying out ZoneMinder quickly, user Dan Landon maintains an easy to use docker image for ZoneMinder. With a few simple configuration changes, it also provides complete Event Notification Server and Machine Learning hook support. Please follow instructions in his repostory. He maintains two repositories:
+
+* If you want to run the latest stable release, please use his `zoneminder repository `__.
+* If you want to run the latest zoneminder master, please use his `zoneminder master repository `__.
+
+In both cases, instructions are provided in the repo README files.
+
+If you are looking at building your own native (non docker) binary packages of ZoneMinder for your distro, please refer to the distro specific install guides or :doc:`packpack`.
+
diff --git a/docs/installationguide/images/zm_first_screen_post_install.png b/docs/installationguide/images/zm_first_screen_post_install.png
index 568d7fcf5..59e2af6d3 100644
Binary files a/docs/installationguide/images/zm_first_screen_post_install.png and b/docs/installationguide/images/zm_first_screen_post_install.png differ
diff --git a/docs/installationguide/index.rst b/docs/installationguide/index.rst
index 4e2288de6..602632c94 100644
--- a/docs/installationguide/index.rst
+++ b/docs/installationguide/index.rst
@@ -1,14 +1,18 @@
Installation Guide
======================================
+.. todo:: This entire guide needs to be checked/updated as needed
+
Contents:
.. toctree::
:maxdepth: 2
+ easydocker
packpack
ubuntu
debian
redhat
+ windows_wsl
multiserver
dedicateddrive
diff --git a/docs/installationguide/packpack.rst b/docs/installationguide/packpack.rst
index dd54ef4b2..72fdf8661 100644
--- a/docs/installationguide/packpack.rst
+++ b/docs/installationguide/packpack.rst
@@ -1,6 +1,8 @@
All Distros - A Docker Way to Build ZoneMinder
===============================================
+.. note:: If you are looking for an easy way to run ZoneMinder and not interested in building your own docker image, please refer to :doc:`easydocker`.
+
.. contents::
These instructions represent an alternative way to build ZoneMinder for any supported distro.
@@ -124,4 +126,4 @@ More testing needs to be done for Redhat distros but it appears Fedora users can
sudo systemctl start systemd-binfmt
-TO-DO: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome.
+.. todo:: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome.
diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst
index 12d9ed769..d35f1fbd3 100644
--- a/docs/installationguide/redhat.rst
+++ b/docs/installationguide/redhat.rst
@@ -101,7 +101,7 @@ Certain commands in these instructions require root privileges while other comma
Set Up Your Environment
***********************
-Before you begin, set up an rpmbuild environment by following `this guide `_ by the CentOS developers.
+Before you begin, set up an rpmbuild environment by following `this guide `_ by the CentOS developers.
In addition, make sure RPM Fusion is enabled as described in the previous section `How to Install ZoneMinder`_.
@@ -158,7 +158,7 @@ The list of available Mock config files are available here:
ls /etc/mock/*rpmfusion_free.cfg
-You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
+You choose the config file based on the desired distro (e.g. el7, f29, f30) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
Installation
************
@@ -188,8 +188,8 @@ Now clone the ZoneMinder git repository from your home folder:
::
cd
- git clone https://github.com/ZoneMinder/ZoneMinder
- cd ZoneMinder
+ git clone https://github.com/ZoneMinder/zoneminder
+ cd zoneminder
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
@@ -197,27 +197,27 @@ If you have previsouly cloned the ZoneMinder git repo and wish to update it to t
::
- cd ~\ZoneMinder
+ cd ~/zoneminder
git pull origin master
Get the crud submodule tarball:
::
- spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec
+ spectool -f -g -R -s 1 ~/zoneminder/distros/redhat/zoneminder.spec
At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first:
::
- cd ~\ZoneMinder
+ cd ~/zoneminder
git checkout -b mynewbranch
Again, depending on what you want to do with those changes, you may want to commit your changes:
::
- cd ~\ZoneMinder
+ cd ~/zoneminder
git add .
git commit
@@ -225,28 +225,28 @@ Once you have made your changes, it is time to turn your work into a new tarball
::
- less ~/ZoneMinder/distros/redhat/zoneminder.spec
+ less ~/zoneminder/distros/redhat/zoneminder.spec
Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command:
::
- cd ~\ZoneMinder
- git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD
+ cd ~/zoneminder
+ git archive --prefix=zoneminder-1.33.4/ -o ~/rpmbuild/SOURCES/zoneminder-1.33.4.tar.gz HEAD
-Replace "1.31.1" with the Version shown in the rpm specfile.
+Replace "1.33.4" with the Version shown in the rpm specfile.
From the root of the local ZoneMinder git repo, execute the following:
::
- cd ~\ZoneMinder
+ cd ~/zoneminder
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
This step will create a source rpm and it will tell you where it was saved. For example:
::
- Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm
+ Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.33.4-1.fc26.src.rpm
Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm.
diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst
index e7c821838..491ee1eb8 100644
--- a/docs/installationguide/ubuntu.rst
+++ b/docs/installationguide/ubuntu.rst
@@ -3,9 +3,9 @@ Ubuntu
.. contents::
-Easy Way: Ubuntu 16.04
-----------------------
-These instructions are for a brand new ubuntu 16.04 system which does not have ZM
+Easy Way: Ubuntu 18.04 (Bionic)
+-------------------------------
+These instructions are for a brand new ubuntu 18.04 system which does not have ZM
installed.
@@ -15,6 +15,7 @@ achieve the same result by running:
::
+ sudo apt-get install tasksel
sudo tasksel install lamp-server
During installation it will ask you to set up a master/root password for the MySQL.
@@ -35,13 +36,13 @@ guide you with a quick search.
`releases page `_ for
the latest release.
- Alternatively, the ZoneMinder project team maintains a ppa, which is updated immediately
+ Alternatively, the ZoneMinder project team maintains a `PPA `_, which is updated immediately
following a new release of ZoneMinder. To use this repository instead of the
official Ubuntu repository, enter the following from the command line:
::
- add-apt-repository ppa:iconnor/zoneminder
+ add-apt-repository ppa:iconnor/zoneminder-1.32
Update repo and upgrade.
@@ -51,6 +52,7 @@ Update repo and upgrade.
apt-get upgrade
apt-get dist-upgrade
+
**Step 3:** Configure MySQL
.. sidebar :: Note
@@ -62,8 +64,10 @@ Update repo and upgrade.
| /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf
| /etc/mysql/mysql.cnf is a basic file
-Certain new defaults in MySQL 5.7 are currently causing some issues with ZoneMinder,
-the workaround is to modify the sql_mode setting of MySQL.
+Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0,
+the workaround is to modify the sql_mode setting of MySQL. Please note that these
+changes are NOT required for ZoneMinder 1.32.0 and some people have reported them
+causing problems in 1.32.0.
To better manage the MySQL server it is recommended to copy the sample config file and
replace the default my.cnf symbolic link.
@@ -104,10 +108,12 @@ Restart MySQL
**Step 5:** Configure the ZoneMinder Database
+This step should not be required on ZoneMinder 1.32.0.
+
::
mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
- mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';"
+ mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';"
**Step 6:** Set permissions
@@ -124,9 +130,16 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content
::
- a2enconf zoneminder
- a2enmod cgi
+ a2enmod cgi
a2enmod rewrite
+ a2enconf zoneminder
+
+You may also want to enable to following modules to improve caching performance
+
+::
+
+ a2enmod expires
+ a2enmod headers
**Step 8:** Enable and start Zoneminder
@@ -139,10 +152,10 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content
::
- nano /etc/php/7.0/apache2/php.ini
+ nano /etc/php/7.2/apache2/php.ini
Search for [Date] (Ctrl + w then type Date and press Enter) and change
-date.timezone for your time zone, see [this](http://php.net/manual/en/timezones.php).
+date.timezone for your time zone, see [this](https://www.php.net/manual/en/timezones.php).
**Don't forget to remove the ; from in front of date.timezone**
::
@@ -181,8 +194,199 @@ CTRL+x to exit
PPA install may need some tweaking of ZMS_PATH in ZoneMinder options. `Socket_sendto or no live streaming`_
-Easy Way: Ubuntu 14.x
----------------------
+Easy Way: Ubuntu 16.04 (Xenial)
+-------------------------------
+These instructions are for a brand new ubuntu 16.04 system which does not have ZM
+installed.
+
+
+It is recommended that you use an Ubuntu Server install and select the LAMP option
+during install to install Apache, MySQL and PHP. If you failed to do this you can
+achieve the same result by running:
+
+::
+
+ sudo tasksel install lamp-server
+
+During installation it will ask you to set up a master/root password for the MySQL.
+Installing LAMP is not ZoneMinder specific so you will find plenty of resources to
+guide you with a quick search.
+
+**Step 1:** Either run commands in this install using sudo or use the below to become root
+::
+
+ sudo -i
+
+**Step 2:** Update Repos
+
+.. topic :: Latest Release
+
+ ZoneMinder is now part of the current standard Ubuntu repository, but
+ sometimes the official repository can lag behind. To find out check our
+ `releases page `_ for
+ the latest release.
+
+ Alternatively, the ZoneMinder project team maintains a `PPA `_, which is updated immediately
+ following a new release of ZoneMinder. To use this repository instead of the
+ official Ubuntu repository, enter the following from the command line:
+
+ ::
+
+ add-apt-repository ppa:iconnor/zoneminder
+ add-apt-repository ppa:iconnor/zoneminder-1.32
+
+Update repo and upgrade.
+
+::
+
+ apt-get update
+ apt-get upgrade
+ apt-get dist-upgrade
+
+
+**Step 3:** Configure MySQL
+
+.. sidebar :: Note
+
+ The MySQL default configuration file (/etc/mysql/mysql.cnf)is read through
+ several symbolic links beginning with /etc/mysql/my.cnf as follows:
+
+ | /etc/mysql/my.cnf -> /etc/alternatives/my.cnf
+ | /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf
+ | /etc/mysql/mysql.cnf is a basic file
+
+Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0,
+the workaround is to modify the sql_mode setting of MySQL. Please note that these
+changes are NOT required for ZoneMinder 1.32.0 and some people have reported them
+causing problems in 1.32.0.
+
+To better manage the MySQL server it is recommended to copy the sample config file and
+replace the default my.cnf symbolic link.
+
+::
+
+ rm /etc/mysql/my.cnf (this removes the current symbolic link)
+ cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/my.cnf
+
+To change MySQL settings:
+
+::
+
+ nano /etc/mysql/my.cnf
+
+In the [mysqld] section add the following
+
+::
+
+ sql_mode = NO_ENGINE_SUBSTITUTION
+
+CTRL+o then [Enter] to save
+
+CTRL+x to exit
+
+Restart MySQL
+
+::
+
+ systemctl restart mysql
+
+
+**Step 4:** Install ZoneMinder
+
+::
+
+ apt-get install zoneminder
+
+**Step 5:** Configure the ZoneMinder Database
+
+This step should not be required on ZoneMinder 1.32.0.
+
+::
+
+ mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
+ mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';"
+
+
+**Step 6:** Set permissions
+
+Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content
+
+::
+
+ chmod 740 /etc/zm/zm.conf
+ chown root:www-data /etc/zm/zm.conf
+ chown -R www-data:www-data /usr/share/zoneminder/
+
+**Step 7:** Configure Apache correctly:
+
+::
+
+ a2enmod cgi
+ a2enmod rewrite
+ a2enconf zoneminder
+
+You may also want to enable to following modules to improve caching performance
+
+::
+
+ a2enmod expires
+ a2enmod headers
+
+**Step 8:** Enable and start Zoneminder
+
+::
+
+ systemctl enable zoneminder
+ systemctl start zoneminder
+
+**Step 9:** Edit Timezone in PHP
+
+::
+
+ nano /etc/php/7.0/apache2/php.ini
+
+Search for [Date] (Ctrl + w then type Date and press Enter) and change
+date.timezone for your time zone, see [this](https://www.php.net/manual/en/timezones.php).
+**Don't forget to remove the ; from in front of date.timezone**
+
+::
+
+ [Date]
+ ; Defines the default timezone used by the date functions
+ ; http://php.net/date.timezone
+ date.timezone = America/New_York
+
+CTRL+o then [Enter] to save
+
+CTRL+x to exit
+
+**Step 10:** Reload Apache service
+
+::
+
+ systemctl reload apache2
+
+**Step 11:** Making sure ZoneMinder works
+
+1. Open up a browser and go to ``http://hostname_or_ip/zm`` - should bring up ZoneMinder Console
+
+2. (Optional API Check)Open up a tab in the same browser and go to ``http://hostname_or_ip/zm/api/host/getVersion.json``
+
+ If it is working correctly you should get version information similar to the example below:
+
+ ::
+
+ {
+ "version": "1.29.0",
+ "apiversion": "1.29.0.1"
+ }
+
+**Congratulations** Your installation is complete
+
+PPA install may need some tweaking of ZMS_PATH in ZoneMinder options. `Socket_sendto or no live streaming`_
+
+Easy Way: Ubuntu 14.x (Trusty)
+------------------------------
**These instructions are for a brand new ubuntu 14.x system which does not have ZM installed.**
**Step 1:** Either run commands in this install using sudo or use the below to become root
@@ -230,7 +434,7 @@ Easy Way: Ubuntu 14.x
nano /etc/php5/apache2/php.ini
Search for [Date] (Ctrl + w then type Date and press Enter) and change
-date.timezone for your time zone, see [this](http://php.net/manual/en/timezones.php).
+date.timezone for your time zone, see [this](https://www.php.net/manual/en/timezones.php).
**Don't forget to remove the ; from in front of date.timezone**
::
diff --git a/docs/installationguide/windows_wsl.rst b/docs/installationguide/windows_wsl.rst
new file mode 100644
index 000000000..6861f522e
--- /dev/null
+++ b/docs/installationguide/windows_wsl.rst
@@ -0,0 +1,10 @@
+Windows 10+ using WSL
+=======================
+
+With Windows 10, Microsoft released the `Window Subsystem for Linux (WSL) `__ that enables you to run native Linux tools directly on Windows, alongside your traditional Windows desktop and modern store apps. To install WSL, please refer to the
+`installation guide from Microsoft `__.
+
+ZoneMinder now runs on Windows 10+ systems which have WSL enabled. This guide will explain how to go about installing an Ubuntu ZM image on Windows 10+.
+
+.. todo:: Isaac to add instructions.
+
diff --git a/docs/userguide/components.rst b/docs/userguide/components.rst
index 48aff0be4..064e27d5f 100644
--- a/docs/userguide/components.rst
+++ b/docs/userguide/components.rst
@@ -17,8 +17,6 @@ Binaries
This is the ZoneMinder Capture daemon. This binary's job is to sit on a video device and suck frames off it as fast as possible, this should run at more or less constant speed.
**zma**
This is the ZoneMinder Analysis daemon. This is the component that goes through the captured frames and checks them for motion which might generate an alarm or event. It generally keeps up with the Capture daemon but if very busy may skip some frames to prevent it falling behind.
-**zmf**
- This is the ZoneMinder Frame daemon. This is an optional daemon that can run in concert with the Analysis daemon and whose function it is to actually write captured frames to disk. This frees up the Analysis daemon to do more analysis (!) and so keep up with the Capture daemon better. If it isn’t running or dies then the Analysis daemon just writes them itself.
**zms**
This is the ZoneMinder Streaming server. The web interface connects with this to get real-time or historical streamed images. It runs only when a live monitor stream or event stream is actually being viewed and dies when the event finishes or the associate web page is closed. If you find you have several zms processes running when nothing is being viewed then it is likely you need a patch for apache (see the Troubleshooting section). A non-parsed header version of zms, called nph-zms, is also installed and may be used instead depending on your web server configuration.
**zmu**
@@ -41,11 +39,11 @@ Finally some perl scripts in the scripts directory. These scripts all have some
**zmpkg.pl**
This is the ZoneMinder Package Control script. This is used by the web interface and service scripts to control the execution of the system as a whole.
**zmdc.pl**
- This is the ZoneMinder Daemon Control script. This is used by the web interface and the zmpkg.pl script to control and maintain the execution of the capture and analysis daemons, amongst others. You should not need to run this script yourself.
+ This is the ZoneMinder Daemon Control script. This is used by the web interface and the zmpkg.pl script to control and maintain the execution of the capture and analysis daemons, amongst others. You should not need to run this script yourself, although you can use it to start/top individual ZM processes.
**zmfilter.pl**
- This script controls the execution of saved filters and will be started and stopped by the web interface based on whether there are filters that have been defined to be autonomous. This script is also responsible for the automatic uploading of events to a 3rd party server.
+ This script controls the execution of saved filters and will be started and stopped by the web interface based on whether there are filters that have been defined to be autonomous(background). This script is also responsible for the automatic uploading of events to a 3rd party server. Prior to 1.32 there was one zmfilter.pl process. In 1.32 onwards we start a zmfilter.pl process for each background filter so that the processing time of one filter doesn't delay the processing of another filter.
**zmaudit.pl**
- This script is used to check the consistency of the event file system and database. It can delete orphaned events, i.e. ones that appear in one location and not the other as well as checking that all the various event related tables are in line. It can be run interactively or in batch mode either from the command line or a cron job or similar. In the zmconfig.pl there is an option to specify fast event deletes where the web interface only deletes the event entry from the database itself. If this is set then it is this script that tidies up the rest.
+ This script is used to check the consistency of the event file system and database. It can delete orphaned events, i.e. ones that appear in one location and not the other as well as checking that all the various event related tables are in line. It can be run interactively or in batch mode either from the command line or a cron job or similar. In the zmconfig.pl there is an option to specify fast event deletes where the web interface only deletes the event entry from the database itself. If this is set then it is this script that tidies up the rest. We do not recommend fast event deletion and we do not recommend having zmaudit.pl run in the background. It is a very ram cpu and disk io intensive program, constantly scanning every event. Please run it manually or from a cron job on weekends or something.
**zmwatch.pl**
This is a simple script purely designed to keep an eye on the capture daemons and restart them if they lockup. It has been known for sync problems in the video drivers to cause this so this script makes sure that nothing important gets missed.
**zmupdate.pl**
@@ -74,6 +72,9 @@ Finally some perl scripts in the scripts directory. These scripts all have some
**zm**
This is the (optional) ZoneMinder init script, see below for details.
+**zmeventnotification.pl**
+ This is an optional 3rd party real time event notification server that also provides push notifications for zmNinja as well as machine learning powered object/face-detection. Please see `Event Notification Server Documentation `__ for more details (Note that the machine learning components are optional, and are developed in Python3)
+
Finally, there are also a number of ZoneMinder perl modules included. These are used by the scripts above, but can also be used by your own or 3rd party scripts. Full documentation for most modules is available in ‘pod’ form via ‘perldoc’ but the general purpose of each module is as follows.
**ZoneMinder.pm**
@@ -86,9 +87,15 @@ Finally, there are also a number of ZoneMinder perl modules included. These are
This module contains the defined Debug and Error functions etc, that are used by scripts to produce diagnostic information in a standard format.
**ZoneMinder/Database.pm**
This module contains database access definitions and functions. Currently not a lot is in this module but it is included as a placeholder for future development.
+**ZoneMinder/Event.pm**
+ This module contains functions to load, manipulate, delete, copy, move events.
+**ZoneMinder/Filter.pm**
+ This module contains functions to load, execute etc filters.
**ZoneMinder/SharedMem.pm**
This module contains standard shared memory access functions. These can be used to access the current state of monitors etc as well as issuing commands to the monitors to switch things on and off. This module effectively provides a ZoneMinder API.
**ZoneMinder/ConfigAdmin.pm**
This module is a specialised module that contains the definition, and other information, about the various configuration options. It is not intended for use by 3rd parties.
+**ZoneMinder/Control/\*.pm**
+ These modules contain implementations of the various PTZ protocols.
**ZoneMinder/Trigger/\*.pm**
These modules contain definitions of trigger channels and connections used by the zmtrigger.pl script. Although they can be used ‘as is’, they are really intended as examples that can be customised or specialised for different interfaces. Contributed modules for new channels or connections will be welcomed and included in future versions of ZoneMinder.
diff --git a/docs/userguide/configfiles.rst b/docs/userguide/configfiles.rst
new file mode 100644
index 000000000..879d181fd
--- /dev/null
+++ b/docs/userguide/configfiles.rst
@@ -0,0 +1,47 @@
+Configuration Files
+--------------------
+This section describes configuration files that ZoneMinder uses beyond the various Web UI options.
+
+.. _replacement_for_options_path:
+
+System Path Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+At one point of time, ZoneMinder stored various system path configurations under the Web UI (``Options->Paths``). This was removed a few versions ago and now resides in a configuration file. The motivation for this change can be read in `this discussion `__.
+
+Typically, path configurations now reside in ``/etc/zm``.
+
+Here is an example of the file hierarchy:
+
+::
+
+ /etc/zm
+ ├── conf.d
+ │ ├── 01-system-paths.conf
+ │ ├── 02-multiserver.conf
+ | ├── 03-custom.conf #optional
+ │ └── README
+ ├── objectconfig.ini # optional
+ ├── zm.conf
+ └── zmeventnotification.ini #optional
+
+The roles of the files are as follows:
+
+* ``zm.conf`` contains various base configuration entries. You should not edit this file as it may be overwritten on an upgrade.
+* ``zmeventnotification.ini`` is only present if you have installed the ZoneMinder Event Notification Server.
+* ``objectconfig.ini`` is only present if you have installed the machine learning hooks for the Event Notification Server.
+* ``conf.d`` contains additional configuration items as follows:
+
+ * ``01-system-paths.conf`` contains all the paths that were once part of ``Options->Paths`` in the Web UI. You should not edit this file as it may be overwritten on an upgrade
+ * ``02-multiserver.conf`` file consists of custom variables if you are deploying ZoneMinder in a multi-server configuration (see :doc:`/installationguide/multiserver`)
+ * ``03-custom.conf`` is an custom config file that I created to override specific variables in the path files. **This is the recommended way to customize entries**. Anything that you want to change should be in a new file inside ``conf.d``. Note that ZoneMinder will sort all the files alphabetically and run their contents in ascending order. So it doesn't really matter what you name them, as long as you make sure your changes are not overwritten by another file in the sorting sequence. It is therefore good practice to prefix your file names by ``nn-`` where ``nn`` is a monotonically increasing numerical sequence ``01-`` ``02-`` ``03-`` and so forth, so you know the order they will be processed.
+
+Timezone Configuration
+~~~~~~~~~~~~~~~~~~~~~~~
+Earlier versions of ZoneMinder relied on ``php.ini`` to set Date/Time Zone. This is no longer the case. You can (and must) set the Timezone via the Web UI, starting ZoneMinder version 1.34. See :ref:`here `.
+
+Database Specific Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. todo:: do we really need to have this section? Not sure if its generic and not specific to ZM
+
+While the ZoneMinder specific database config entries reside in ``/etc/zm/zm.conf`` and related customizations discussed above, general database configuration items can be tweaked in ``/etc/mysql`` (or whichever path your DB server is installed)
\ No newline at end of file
diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst
index 8f85feb98..a5b622872 100644
--- a/docs/userguide/definemonitor.rst
+++ b/docs/userguide/definemonitor.rst
@@ -46,6 +46,10 @@ Linked Monitors
This field allows you to select other monitors on your system that act as triggers for this monitor. So if you have a camera covering one aspect of your property you can force all cameras to record while that camera detects motion or other events. You can either directly enter a comma separated list of monitor ids or click on ‘Select’ to choose a selection. Be very careful not to create circular dependencies with this feature however you will have infinitely persisting alarms which is almost certainly not what you want! To unlink monitors you can ctrl-click.
Maximum FPS
+
+ .. warning::
+ Unless you know what you are doing, please leave this field empty, especially if you are configuring a network camera. More often than not, putting a value here adversely affects recording.
+
On some occasions you may have one or more cameras capable of high capture rates but find that you generally do not require this performance at all times and would prefer to lighten the load on your server. This option permits you to limit the maximum capture rate to a specified value. This may allow you to have more cameras supported on your system by reducing the CPU load or to allocate video bandwidth unevenly between cameras sharing the same video device. This value is only a rough guide and the lower the value you set the less close the actual FPS may approach it especially on shared devices where it can be difficult to synchronise two or more different capture rates precisely. This option controls the maximum FPS in the circumstance where no alarm is occurring only.
This feature is limited and will only work under the following conditions:
@@ -56,9 +60,14 @@ Maximum FPS
Using this field for video streams from IP cameras will cause undesirable results when the value is equal to or less than the frame rate from the camera. Note that placing a value higher than the camera's frame rate is allowed and can help prevent cpu spikes when communication from the camera is lost.
Alarm Maximum FPS
+
+ .. warning::
+ Unless you know what you are doing, please leave this field empty, especially if you are configuring a network camera. More often than not, putting a value here adversely affects recording.
+
+
If you have specified a Maximum FPS it may be that you don’t want this limitation to apply when your monitor is recording motion or other event. This setting allows you to override the Maximum FPS value if this circumstance occurs. As with the Maximum FPS setting leaving this blank implies no limit so if you have set a maximum fps in the previous option then when an alarm occurs this limit would be ignored and ZoneMinder would capture as fast as possible for the duration of the alarm, returning to the limited value after the alarm has concluded. Equally you could set this to the same, or higher (or even lower) value than Maximum FPS for more precise control over the capture rate in the event of an alarm.
- **IMPORTANT:** This field is subject to the same limitations as the Maxium FPS field. Ignoring these limitations will produce undesriable results.
+ **IMPORTANT:** This field is subject to the same limitations as the Maximum FPS field. Ignoring these limitations will produce undesriable results.
Reference Image Blend %ge
Each analysed image in ZoneMinder is a composite of previous images and is formed by applying the current image as a certain percentage of the previous reference image. Thus, if we entered the value of 10 here, each image’s part in the reference image will diminish by a factor of 0.9 each time round. So a typical reference image will be 10% the previous image, 9% the one before that and then 8.1%, 7.2%, 6.5% and so on of the rest of the way. An image will effectively vanish around 25 images later than when it was added. This blend value is what is specified here and if higher will make slower progressing events less detectable as the reference image would change more quickly. Similarly events will be deemed to be over much sooner as the reference image adapts to the new images more quickly. In signal processing terms the higher this value the steeper the event attack and decay of the signal. It depends on your particular requirements what the appropriate value would be for you but start with 10 here and adjust it (usually down) later if necessary.
@@ -71,15 +80,15 @@ Source Tab
FFmpeg
^^^^^^
- This is the recommended source type for most modern ip cameras.
+ This is the **recommended** source type for most modern ip cameras.
Source Path
Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this:
* Check the documentation that came with your camera
- * Look for your camera in the hardware compatibilty list in the wiki http://wiki.zoneminder.com/Hardware_Compatibility_List
+ * Look for your camera in the hardware compatibilty list in the `hardware compatibility wiki `__
* Try ZoneMinder's new ONVIF probe feature
- * Download and install the ONVIF Device Manager onto a Windows machine https://sourceforge.net/projects/onvifdm/
+ * Download and install the `ONVIF Device Manager `__ onto a Windows machine
* Use Google to find third party sites, such as ispy, which document this information
Source Colours
Specify the amount of colours in the captured image. 32 bit is the preferred choice here. Unlike with local cameras changing this has no controlling effect on the remote camera itself so ensure that your camera is actually capturing to this palette beforehand.
@@ -121,7 +130,7 @@ Remote Protocol
Remote Method
When HTTP is the Remote Protocol, your choices are Simple and Regexp. Most should choose Simple. When RTSP is the Remote Protocol, your choices are RTP/Unicast, RTP/Multicast, RTP/RTSP, RTP,RTSP,HTTP. Try each of these to determine which works with your camera. Most cameras will use either RTP/Unicast (UDP) or RTP/RTSP (TCP).
Remote Host/Port/Path
- Use these fields to enter the full URL of the camera. Basically if your camera is at http://camserver.home.net:8192/cameras/camera1.jpg then these fields will be camserver.home.net, 8192 and /cameras/camera1.jpg respectively. Leave the port at 80 if there is no special port required. If you require authentication to access your camera then add this onto the host name in the form :@.com. This will usually be 32 or 24 bit colour even if the image looks black and white. Look in Supported Hardware > Network Cameras section, how to obtain these strings that may apply to your camera.
+ Use these fields to enter the full URL of the camera. Basically if your camera is at ``http://camserver.home.net:8192/cameras/camera1.jpg`` then these fields will be camserver.home.net, 8192 and /cameras/camera1.jpg respectively. Leave the port at 80 if there is no special port required. If you require authentication to access your camera then add this onto the host name in the form :@.com. This will usually be 32 or 24 bit colour even if the image looks black and white. Look in Supported Hardware > Network Cameras section, how to obtain these strings that may apply to your camera.
Remote Image Colours
Specify the amount of colours in the captured image. Unlike with local cameras changing this has no controlling effect on the remote camera itself so ensure that your camera is actually capturing to this palette beforehand.
Capture Width/Height
@@ -150,7 +159,7 @@ Orientation
WebSite
^^^^^^^
-This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue.
+This Source Type allows one to configure an arbitrary website as a non-recordable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue.
Website URL
Enter the full http or https url to the desired website.
@@ -164,6 +173,29 @@ Height (pixels)
Web Site Refresh
If the website in question has static content, optionally enter a time period in seconds for ZoneMinder to refresh the content.
+Storage Tab
+-----------
+
+The storage section allows for each monitor to configure if and how video and audio are recorded.
+
+Save JPEGs
+ Records video in individual JPEG frames. Storing JPEG frames requires more storage space than h264 but it allows to view an event anytime while it is being recorded.
+
+ * Disabled – video is not recorded as JPEG frames. If this setting is selected, then "Video Writer" should be enabled otherwise there is no video recording at all.
+ * Frames only – video is recorded in individual JPEG frames.
+ * Analysis images only (if available) – video is recorded in invidual JPEG frames with an overlay of the motion detection analysis information. Note that this overlay remains permanently visible in the frames.
+ * Frames + Analysis images (if available) – video is recorded twice, once as normal individual JPEG frames and once in invidual JPEG frames with analysis information overlaid.
+
+Video Writer
+ Records video in real video format. It provides much better compression results than saving JPEGs, thus longer video history can be stored.
+
+ * Disabled – video is not recorded in video format. If this setting is selected, then "Save JPEGs" should be enabled otherwise there is no video recording at all.
+ * X264 Encode – the video or picture frames received from the camera are transcoded into h264 and stored as a video. This option is useful if the camera cannot natively stream h264.
+ * H264 Camera Passthrough – this option assumes that the camera is already sending an h264 stream. Video will be recorded as is, without any post-processing in zoneminder. Video characteristics such as bitrate, encoding mode, etc. should be set directly in the camera.
+
+Recording Audio
+ Check the box labeled "Whether to store the audio stream when saving an event." in order to save audio (if available) when events are recorded.
+
Timestamp Tab
-------------
@@ -182,7 +214,8 @@ Warm-up Frames
Pre/Post Event Image Buffer
These options determine how many frames from before and after an event should be preserved with it. This allows you to view what happened immediately prior and subsequent to the event. A value of 10 for both of these will get you started but if you get a lot of short events and would prefer them to run together to form fewer longer ones then increase the Post Event buffer size. The pre-event buffer is a true buffer and should not really exceed half the ring buffer size. However the post-event buffer is just a count that is applied to captured frames and so can be managed more flexibly. You should also bear in mind the frame rate of the camera when choosing these values. For instance a network camera capturing at 1FPS will give you 10 seconds before and after each event if you chose 10 here. This may well be too much and pad out events more than necessary. However a fast video card may capture at 25FPS and you will want to ensure that this setting enables you to view a reasonable time frame pre and post event.
Stream Replay Image Buffer
- This option ...
+ The number of frames buffered to allow pausing and rewinding of the stream when live viewing a monitor. A value of 0 disables the feature.
+ Frames are buffered to ZM_PATH_SWAP. If this path points to a physical drive, a lot of IO will be caused during live view / montage. If you experience high system load in those situations, either disable the feature or use a RAM drive for ZM_PATH_SWAP.
Alarm Frame Count
This option allows you to specify how many consecutive alarm frames must occur before an alarm event is generated. The usual, and default, value is 1 which implies that any alarm frame will cause or participate in an event. You can enter any value up to 16 here to eliminate bogus events caused perhaps by screen flickers or other transients. Values over 3 or 4 are unlikely to be useful however. Please note that if you have statistics recording enabled then currently statistics are not recorded for the first ‘Alarm Frame Count’-1 frames of an event. So if you set this value to 5 then the first 4 frames will be missing statistics whereas the more usual value of 1 will ensure that all alarm frames have statistics recorded.
@@ -247,3 +280,7 @@ Default Scale
If your monitor has been defined with a particularly large or small image size then you can choose a default scale here with which to view the monitor so it is easier or more visible from the web interface.
Web Colour
Some elements of ZoneMinder now use colours to identify monitors on certain views. You can select which colour is used for each monitor here. Any specification that is valid for HTML colours is valid here, e.g. ‘red’ or ‘#ff0000’. A small swatch next to the input box displays the colour you have chosen.
+Embed EXIF data into image:
+ Embeds EXIF data into each jpeg frame
+
+ .. todo:: what about mp4s?
diff --git a/docs/userguide/definezone.rst b/docs/userguide/definezone.rst
index cf33f44c9..1d00c71b4 100644
--- a/docs/userguide/definezone.rst
+++ b/docs/userguide/definezone.rst
@@ -29,7 +29,7 @@ Type
Triggers an alarm when motion is detected within it, as long as no alarms have already been triggered in an Active zone. This is the most specialized of the zone types. For instance in the camera covering my garden I keep watch for a hedgehog that visits most nights and scoffs the food out of my cats bowls. By creating a sensitive Exclusive zone in that area I can ensure that a hedgehog alarm will only trigger if there is activity in that small area. If something much bigger occurs, like someone walking by it will trigger a regular alarm and not one from the Exclusive zone. Thus I can ensure I get alarms for big events and also special small events but not the noise in between.
* Preclusive
- This zone type is relatively recent. It is called a Preclusive zone because if it is triggered it actually precludes an alarm being generated for that image frame. So motion or other changes that occur in a Preclusive zone will have the effect of ensuring that no alarm occurs at all. The application for this zone type is primarily as a shortcut for detecting general large-scale lighting or other changes. Generally this may be achieved by limiting the maximum number of alarm pixels or other measure in an Active zone. However in some cases that zone may cover an area where the area of variable illumination occurs in different places as the sun and/or shadows move and it thus may be difficult to come up with general values. Additionally, if the sun comes out rapidly then although the initial change may be ignored in this way as the reference image catches up an alarm may ultimately be triggered as the image becomes less different. Using one or more Preclusive zones offers a different approach. Preclusive zones are designed to be fairly small, even just a few pixels across, with quite low alarm thresholds. They should be situated in areas of the image that are less likely to have motion occur such as high on a wall or in a corner. Should a general illumination change occur they would be triggered at least as early as any Active zones and prevent any other zones from generating an alarm. Obviously careful placement is required to ensure that they do not cancel any genuine alarms or that they are not so close together that any motion just hops from one Preclusive zone to another. Preclusive zones may also be used to reduce processing time by situating one over an Active zone. The Preclusive zone is processed first; if it is small, and is triggered, the rest of the zone/image will not be processed.
+ This zone type is relatively recent. It is called a Preclusive zone because if it is triggered it actually precludes an alarm being generated for that image frame. So motion or other changes that occur in a Preclusive zone will have the effect of ensuring that no alarm occurs at all. The application for this zone type is primarily as a shortcut for detecting general large-scale lighting or other changes. Generally this may be achieved by limiting the maximum number of alarm pixels or other measure in an Active zone. However in some cases that zone may cover an area where the area of variable illumination occurs in different places as the sun and/or shadows move and it thus may be difficult to come up with general values. Additionally, if the sun comes out rapidly then although the initial change may be ignored in this way as the reference image catches up an alarm may ultimately be triggered as the image becomes less different. Using one or more Preclusive zones offers a different approach. Preclusive zones are designed to be fairly small, even just a few pixels across, with quite low alarm thresholds. They should be situated in areas of the image that are less likely to have motion occur such as high on a wall or in a corner. Should a general illumination change occur they would be triggered at least as early as any Active zones and prevent any other zones from generating an alarm. Obviously careful placement is required to ensure that they do not cancel any genuine alarms or that they are not so close together that any motion just hops from one Preclusive zone to another. Preclusive zones may also be used to reduce processing time by situating one over an Active zone. The Preclusive zone is processed first; if it is small, and is triggered, the rest of the zone/image will not be processed. See Extend Alarm Frame Count below for a way to hold the preclusive zone active for an extended period.
* Inactive
Suppresses the detection of motion within it. This can be layered on top of any other zone type, preventing motion within the Inactive zone from being effective for any other zone type. Use inactive zones to cover areas in which nothing notable will ever happen or where you get false alarms that don't relate to what you are trying to monitor. Inactive zones may be overlaid on other zones to blank out areas, and are processed first (with the exception of Privacy zones, see below). As a general practice, you should try and make zones abut each other instead of overlapping to avoid repeated duplicate processing of the same area.
@@ -40,7 +40,7 @@ Type
Preset
The preset chooser sets sensible default values based on computational needs (fast v. best) and sensitivity (low, medium, high.) It is not required that you select a preset, and you can alter any of the parameters after choosing a preset. For a small number of monitors with ZoneMinder running on modern equipment, Best, high sensitivity can be chosen as a good starting point.
- It is important to understand that the available presets are intended merely as a starting point. Since every camera's view is unique, they are not guaranteed to work properly in every case. Presets tend to work acceptably for indoor cameras, where the objects of interest are relatively close and there typically are few or no unwanted objects moving within the cameras view. Presets, on the other hand, tend to not work acceptably for outdoor cameras, where the field of view is typically much wider, objects of interest are farther away, and changing weather patterns can cause false triggers. For outdoor cameras in particular, you will almost certainly have to tune your motion detection zone to get desired results. Please refer to `this guide `__ to learn how to do this.
+ It is important to understand that the available presets are intended merely as a starting point. Since every camera's view is unique, they are not guaranteed to work properly in every case. Presets tend to work acceptably for indoor cameras, where the objects of interest are relatively close and there typically are few or no unwanted objects moving within the cameras view. Presets, on the other hand, tend to not work acceptably for outdoor cameras, where the field of view is typically much wider, objects of interest are farther away, and changing weather patterns can cause false triggers. For outdoor cameras in particular, you will almost certainly have to tune your motion detection zone to get desired results. Please refer to `this guide `__ to learn how to do this.
Units
* Pixels - Selecting this option will allow many of the following values to be entered (or viewed) in units of pixels.
@@ -104,7 +104,10 @@ Overload Frame Ignore Count
* Number of Blobs > Max Blobs
The idea is that after a change like a light going on that is considered too big to count as an alarm, it could take a couple of frames for things to settle down again.
+Extend Alarm Frame Count
+ This field applies to Preclusive Zones only. Placing a value in this field holds the Preclusive zone active for the specified number of frames after the initial triggering event. This is useful in cases where a sudden change in light level triggers the Preclusive zone, but the zone needs to be held active for a few frames as the camera itself adjusts to that change in light level.
+
Other information
-----------------
-Refer to `this `__ user contributed Zone guide for additional information will illustrations if you are new to zones and need more help.
+Refer to `this `__ user contributed Zone guide for additional information will illustrations if you are new to zones and need more help.
diff --git a/docs/userguide/filterevents.rst b/docs/userguide/filterevents.rst
index d3c4955c9..8359aaf89 100644
--- a/docs/userguide/filterevents.rst
+++ b/docs/userguide/filterevents.rst
@@ -8,10 +8,7 @@ Filters allow you to define complex conditions with associated actions in ZoneMi
And many more.
-The filter window can be accessed from various views, one of which is to simply tap on the filter button in the main web view:
-
-.. image:: images/filter-button.png
- :width: 600px
+The filter window can be accessed by tapping on the top level filter menu
You can use the filter window to create your own filters or to modify existing ones. You can even save your favourite filters to re-use at a future date. Filtering itself is fairly simple; you first choose how many expressions you'd like your filter to contain. Changing this value will cause the window to redraw with a corresponding row for each expression. You then select what you want to filter on and how the expressions relate by choosing whether they are 'and' or 'or' relationships. For filters comprised of many expressions you will also get the option to bracket parts of the filter to ensure you can express it as desired. Then if you like choose how you want your results sorted and whether you want to limit the amount of events displayed.
@@ -22,62 +19,75 @@ Here is what the filter window looks like
:width: 800px
* *A*: This is a dropdown list where you can select pre-defined filters. You will notice that ZoneMinder comes with a PurgeWhenFull filter that is configured to delete events if you reach 95% of disk space.
-* *B* and *C*: This is where you specify conditions that need to match before the filter is executed. You use the "+" and "-" buttons to add/delete conditions
-* *D*: This is where you specify what needs to happen when the conditions match:
+* *B*: If you are creating a new filter, you can type in a name for your filter here
+* *C*: This is where you specify conditions that need to match before the filter is executed. You use the "+" and "-" buttons to add/delete conditions
+* *D*: This allows you to perform sorting and limiting operations on the output before you take an action
+* *E*: This is where you specify what needs to happen when the conditions match:
+
* Archive all matches: sets the archive field to 1 in the Database for the matched events.
Think of 'archiving' as grouping them under a special category - you can view archived
events later and also make sure archived events don't get deleted, for example
- * Email details of all matches: Sends an email to the configured address with details about the event.
- The email can be customized as per TBD
- * Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event.
- * Delete all matches: Deletes all the matched events
-* *E*: Use 'Submit' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings
+
+ .. todo ::
+ fill in what update used disk space, copy all matches, move all matches do. For the "create video" filter, put in more details on how it works, any dependencies etc.
-.. NOTE:: More details on filter conditions:
+ * Update used disk space:
+ * Create video for all matches: creates a video file of all the events that match
+ * Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event.
+ * Delete all matches: Deletes all the matched events.
+ * Copy all matches:
+ * Move all matches:
+ * Run filter in background: When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a “*” next to it.
+ * Run filter concurrently: Allows this filter to run in its own thread thereby letting other filters run in parallel.
+
+* *F*: Use 'List Matches' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings
+
+.. note:: More details on filter conditions:
There are several different elements to an event that you can filter on, some of which require further explanation. These are as follows,
* 'Date/Time' which must evaluate to a date and a time together,
* 'Date' and 'Time' which are variants which may only contain the relevant subsets of this,
* 'Weekday' which as expected is a day of the week.
- All of the preceding elements take a very flexible free format of dates and time based on the PHP strtotime function (http://www.php.net/manual/en/function.strtotime.php). This allows values such as 'last Wednesday' etc to be entered. We recommend acquainting yourself with this function to see what the allowed formats are. However automated filters are run in perl and so are parsed by the Date::Manip package. Not all date formats are available in both so if you are saved your filter to do automatic deletions or other tasks you should make sure that the date and time format you use is compatible with both methods. The safest type of format to use is ‘-3 day’ or similar with easily parseable numbers and units are in English.
+ All of the preceding elements take a very flexible free format of dates and time based on the PHP strtotime function (https://www.php.net/manual/en/function.strtotime.php). This allows values such as 'last Wednesday' etc to be entered. We recommend acquainting yourself with this function to see what the allowed formats are. However automated filters are run in perl and so are parsed by the Date::Manip package. Not all date formats are available in both so if you are saved your filter to do automatic deletions or other tasks you should make sure that the date and time format you use is compatible with both methods. The safest type of format to use is ‘-3 day’ or similar with easily parseable numbers and units are in English.
The other things you can filter on are all fairly self explanatory, except perhaps for 'Archived' which you can use to include or exclude Archived events. In general you'll probably do most filtering on un-archived events. There are also two elements, Disk Blocks and Disk Percent which don’t directly relate to the events themselves but to the disk partition on which the events are stored. These allow you to specify an amount of disk usage either in blocks or in percentage as returned by the ‘df’ command. They relate to the amount of disk space used and not the amount left free. Once your filter is specified, clicking 'submit' will filter the events according to your specification. As the disk based elements are not event related directly if you create a filter and include the term ‘DiskPercent > 95’ then if your current disk usage is over that amount when you submit the filter then all events will be listed whereas if it is less then none at all will. As such the disk related terms will tend to be used mostly for automatic filters (see below). If you have created a filter you want to keep, you can name it and save it by clicking 'Save'.
If you do this then the subsequent dialog will also allow you specify whether you want this filter automatically applied in order to delete events or upload events via ftp to another server and mail notifications of events to one or more email accounts. Emails and messages (essentially small emails intended for mobile phones or pagers) have a format defined in the Options screen, and may include a variety of tokens that can be substituted for various details of the event that caused them. This includes links to the event view or the filter as well as the option of attaching images or videos to the email itself. Be aware that tokens that represent links may require you to log in to access the actual page, and sometimes may function differently when viewed outside of the general ZoneMinder context. The tokens you can use are as follows.
- * %EI% Id of the event
- * %EN% Name of the event
- * %EC% Cause of the event
- * %ED% Event description
- * %ET% Time of the event
- * %EL% Length of the event
- * %EF% Number of frames in the event
- * %EFA% Number of alarm frames in the event
- * %EST% Total score of the event
- * %ESA% Average score of the event
- * %ESM% Maximum score of the event
- * %EP% Path to the event
- * %EPS% Path to the event stream
- * %EPI% Path to the event images
- * %EPI1% Path to the first alarmed event image
- * %EPIM% Path to the (first) event image with the highest score
- * %EI1% Attach first alarmed event image
- * %EIM% Attach (first) event image with the highest score
- * %EV% Attach event mpeg video
- * %MN% Name of the monitor
- * %MET% Total number of events for the monitor
- * %MEH% Number of events for the monitor in the last hour
- * %MED% Number of events for the monitor in the last day
- * %MEW% Number of events for the monitor in the last week
- * %MEM% Number of events for the monitor in the last month
- * %MEA% Number of archived events for the monitor
- * %MP% Path to the monitor window
- * %MPS% Path to the monitor stream
- * %MPI% Path to the monitor recent image
- * %FN% Name of the current filter that matched
- * %FP% Path to the current filter that matched
- * %ZP% Path to your ZoneMinder console
+ * %EI% Id of the event
+ * %EN% Name of the event
+ * %EC% Cause of the event
+ * %ED% Event description
+ * %ET% Time of the event
+ * %EL% Length of the event
+ * %EF% Number of frames in the event
+ * %EFA% Number of alarm frames in the event
+ * %EST% Total score of the event
+ * %ESA% Average score of the event
+ * %ESM% Maximum score of the event
+ * %EP% Path to the event
+ * %EPS% Path to the event stream
+ * %EPI% Path to the event images
+ * %EPI1% Path to the first alarmed event image
+ * %EPIM% Path to the (first) event image with the highest score
+ * %EI1% Attach first alarmed event image
+ * %EIM% Attach (first) event image with the highest score
+ * %EV% Attach event mpeg video
+ * %MN% Name of the monitor
+ * %MET% Total number of events for the monitor
+ * %MEH% Number of events for the monitor in the last hour
+ * %MED% Number of events for the monitor in the last day
+ * %MEW% Number of events for the monitor in the last week
+ * %MEM% Number of events for the monitor in the last month
+ * %MEA% Number of archived events for the monitor
+ * %MOD% Path to image containing object detection
+ * %MP% Path to the monitor window
+ * %MPS% Path to the monitor stream
+ * %MPI% Path to the monitor recent image
+ * %FN% Name of the current filter that matched
+ * %FP% Path to the current filter that matched
+ * %ZP% Path to your ZoneMinder console
Finally you can also specify a script which is run on each matched event. This script should be readable and executable by your web server user. It will get run once per event and the relative path to the directory containing the event in question. Normally this will be of the form / so from this path you can derive both the monitor name and event id and perform any action you wish. Note that arbitrary commands are not allowed to be specified in the filter, for security the only thing it may contain is the full path to an executable. What that contains is entirely up to you however.
@@ -87,15 +97,8 @@ Here is what the filter window looks like
Saving filters
-----------------
-.. image:: images/filter-save.png
- :width: 400px
-
When saving filters, if you want the filter to run in the background make sure you select the "Run filter in background" option. When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a "*" next to it.
-For example:
-
-.. image:: images/filter-choosefilter.png
- :width: 400px
How filters actually work
--------------------------
diff --git a/docs/userguide/gettingstarted.rst b/docs/userguide/gettingstarted.rst
index 713a667b5..226da8a41 100644
--- a/docs/userguide/gettingstarted.rst
+++ b/docs/userguide/gettingstarted.rst
@@ -5,32 +5,144 @@ Having followed the :doc:`/installationguide/index` for your distribution you sh
.. image:: ../installationguide/images/zm_first_screen_post_install.png
+.. _timezone_config:
+
+Setting Timezone
+^^^^^^^^^^^^^^^^^
+Previous versions of ZoneMinder required the user to set up Timezone correctly in ``php.ini``. This is no longer the case. Starting 1.34, ZoneMinder allows you to specify the TimeZone in the UI. Please make sure it is set up correctly. The Timezone can be changed by selecting ``Options->System->Timezone``
+
+.. image:: images/getting-started-timezone.png
Enabling Authentication
^^^^^^^^^^^^^^^^^^^^^^^
We strongly recommend enabling authentication right away. There are some situations where certain users don't enable authentication, such as instances where the server is in a LAN not directly exposed to the Internet, and is only accessible via VPN etc., but in most cases, authentication should be enabled. So let's do that right away.
-* Click on the Options link on the top right corner of the web interface
-* You will now be presented with a screen full of options. Click on the "System" tab
+* Click on the Options link on the top bar of the web interface
+* You will now be presented with a sidebar full of options. Click on the "System" link
.. image:: images/getting-started-enable-auth.png
* The relevant portions to change are marked in red above
* Enable OPT_USE_AUTH - this automatically switches to authentication mode with a default user (more on that later)
* Select a random string for AUTH_HASH_SECRET - this is used to make the authentication logic more secure, so
- please generate your own string and please don't use the same value in the example.
+ please generate your own string and make sure it is sufficiently randomized and long. Note that if you plan to use APIs with ZoneMinder (needed by zmNinja/other apps), it is mandatory that you have this field populated
* The other options highlighed above should already be set, but if not, please make sure they are
+* Note that if you are planning to use zmNinja and plan to use ZM authentication, you must also:
+
+ * set ``AUTH_RELAY`` to hashed
+ * Enable ``AUTH_HASH_LOGINS``
+
* Click on Save at the bottom and that's it! The next time you refresh that page, you will now be presented with a login screen. Job well done!
.. image:: images/getting-started-login.png
-.. NOTE:: The default login/password is "admin/admin"
+.. note:: The default login/password is "admin/admin"
-Switching to flat theme
-^^^^^^^^^^^^^^^^^^^^^^^
-What you see is what is called a "classic" skin. Zoneminder has a host of configuration options that you can customize over time. This guide is meant to get you started the easiest possible way, so we will not go into all the details. However, it is worthwhile to note that Zoneminder also has a 'flat' theme that depending on your preferences may look more modern. So let's use that as an example of introducing you to the Options menu
+Understanding the Web Console
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Before we proceed, lets spend a few minutes understanding the key functions of the web console.
+For the sake of illustration, we are going to use a populated zoneminder configuration with several monitors and events.
+
+.. image:: images/getting-started-understand-console.png
+
+This screen is called the "console" screen in ZoneMinder and shows a summary of your monitors, associated events and more information.
+
+* **A**: The options menu lets you configure many aspects of ZoneMinder. Refer to :doc:`options`.
+* **B**: This brings up a color coded log window that shows various system and component level logs. This window is useful if you are trying to diagnose issues. Refer to :doc:`logging`.
+* **C**: ZoneMinder allows you to group monitors gor logical separation. This option lets you create new groups, associate monitors to them and edit/delete existing groups.
+* **D**: Filters are a powerful mechanism to perform actions when certain conditions are met. ZoneMinder comes with some preset filters that keep a tab of disk space and others. Many users create their own filters for more advanced actions like sending emails when certain events occur and more. Refer to :doc:`filterevents`.
+* **E**: The Cycle option allows you to rotate between live views of each cofigured monitor.
+* **F**: The Montage option shows a collage of your monitors. You can customize them including moving them around.
+* **G**: Montage Review allows you to simultaneously view past events for different monitors. Note that this is a very resource intensive page and its performance will vary based on your system capabilities.
+* **H**: Audit Events Report is more of a power user feature. This option looks for recording gaps in events and recording issues in mp4 files.
+* **I**: This is the user you are currently logged in as.
+* **J**: ZoneMinder allows you to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ `. More esoteric examples of switching run states based on phone location can be found `here `__.
+
+
+Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes.
+
+.. image:: images/runstates.png
+
+* **K**: This line shows you system health information
+* **L**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage).
+* **M**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Green means the monitor is running. Red means there is something wrong with that camera.
+* **N**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month.
+* **O**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones.
+* **P**: This is a "visual filter" which lets you 'filter' the console display based on text you enter. While this may not be particularly useful for small systems, ZoneMinder is also used in mega-installations will well over 200+ cameras and this visual filter helps reduce the monitors you are seeing at one time.
+
+Adding Monitors
+^^^^^^^^^^^^^^^
+Now that we have a basic understanding of the web console, lets go about adding a new camera (monitor). For this example, lets assume we have an IP camera that streams RTSP at LAN IP address 192.168.1.33.
+
+.. sidebar:: Note
+
+ This is meant to be a simple example. For a more detailed explanation of other options available when creating a monitor, please see :doc:`/userguide/definemonitor`
+
+The first thing we will need to know is how to access that camera's video feed. You will need to consult your camera's manual or check their forum. Zoneminder community users also have a frequently updated list right `here `__ that lists information about many cameras. If you don't find your list there and can't seem to find it elsewhere, feel free to register and ask in the `user forums `__.
+
+The camera we are using as an example here is a Foscam 9831W which is a 1280x960 RTSP camera, and the URL to access it's feed is *username:password@IPADDRESS:PORT/videoMain*
+
+Let's get started:
+
+Click on the "Add" button below:
+
+.. image:: images/getting-started-modern-look.png
+ :width: 600px
+
+This brings up the new monitor window:
+
+.. image:: images/getting-started-add-monitor-general.png
+ :width: 600px
+
+* We've given it a name of 'Garage', because, well, its better than Monitor-1 and this is my Garage camera.
+
+* There are various source types. As a brief introduction you'd want to use 'Local' if your camera is physically attached to your ZM server (like a USB camera, for example), and one of 'Remote', 'FFMpeg', 'Libvlc' or 'cURL' for a remote camera (not necessarily, but usually). For this example, let's go with 'FFMpeg'.
+
+.. note::
+ As a thumb rule, if you have a camera accessible via IP and it does HTTP or RTSP,
+ start with FFMpeg first and libvlc if it doesn't work (:doc:`/userguide/definemonitor`
+ covers other modes in more details). If you are wondering what 'File' does, well, ZoneMinder was
+ built with compatibility in mind. Take a look at `this post
+ `__ to see how file can be used for leisure reading.
+
+* In this example, the Function is 'Modect', which means it will start recording if motion is detected on that camera feed. The parameters for what constitutes motion detected is specific in :doc:`definezone`
+
+* In Analysis FPS, we've put in 5FPS here. Note that you should not put an FPS that is greater than the camera FPS. In my case, 5FPS is sufficient for my needs
+
+.. note::
+ Leave Maximum FPS and Alarm Maximum FPS **empty** if you are configuring an IP camera. In older versions of ZoneMinder, you were encouraged to put a value here, but that is no longer recommended. Infact, if you see your feed going much slower than the feed is supposed to go, or you get a lot of buffering/display issues, make sure this is empty. If you need to control camera FPS, please do it directly on the camera (via its own web interface, for example)
+
+
+* We are done for the General tab. Let's move to the next tab
+
+.. image:: images/getting-started-add-monitor-source.png
+ :width: 800px
+
+* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera)
+* Note that starting ZM 1.34, GPUs are supported. In my case, I have an NVIDIA GeForce GTX1050i. These ``cuda`` and ``cuvid`` parameters are what my system supports to use the NVIDIA hardware decoder and GPU resources. If you don't have a GPU, or don't know how to configure your ffmpeg to support it, leave it empty for now. In future, we will add a section on how to set up a GPU
+
+.. todo::
+ add GPU docs
+
+That's pretty much it. Click on Save. We are not going to explore the other tabs in this simple guide.
+
+You now have a configured monitor:
+
+.. image:: images/getting-started-add-monitor-modect-ready.png
+
+
+And then, finally, to see if everything works, if you click on the garage monitor you just added, you should be able to see its live feed. If you don't, inspect your webserver logs and your ZoneMinder logs to see what is going on.
+
+
+Switching to another theme
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. todo::
+ Fix theme text after I clearly understand that System->CSS is doing
+
+When you first install ZoneMinder, you see is what is called a "classic" skin. Zoneminder has a host of configuration options that you can customize over time. This guide is meant to get you started the easiest possible way, so we will not go into all the details. However, it is worthwhile to note that Zoneminder also has a 'flat' theme that depending on your preferences may look more modern. So let's use that as an example of introducing you to the Options menu
* Click on the Options link on the top right of the web interface in the image above
* This will bring you to the options window as shown below. Click on the "System" tab and then select the
@@ -51,98 +163,6 @@ Congratulations! You now have a modern looking interface.
.. image:: images/getting-started-modern-look.png
-Understanding the Web Console
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Before we proceed, lets spend a few minutes understanding the key functions of the web console.
-For the sake of illustration, we are going to use a populated zoneminder configuration with several monitors and events.
-Obviously, this does not reflect your current web console - which is essentially void of any useful information till now,
-as we are yet to add things. Let's take a small break and understand what the various functions are before we configure our
-own empty screen.
-
-.. image:: images/getting-started-understand-console.png
-
-* **A**: This is the username that is logged in. You are logged in as 'admin' here
-* **B**: Click here to explore the various options of ZoneMinder and how to configure them. You already used this to enable authentication and change style above. Over time, you will find this to have many other things you will want to customize.
-* **C**: This link, when clicked, opens up a color coded log window of what is going on in Zoneminder and often gives you good insight into what is going wrong or right. Note that the color here is red - that is an indication that some error occurred in ZoneMinder. You should click it and investigate.
-* **D**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month.
-* **E**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones.
-* **F**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Note the color red on item F ? Well that means there is something wrong with that camera. No wonder the log also shows red. Good indication for you to tap on logs and investigate
-* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage).
-* **H**: If you click on these links you can view a "Montage" of all your configured monitors or cycle through each one
-* **I**: One of the most often missed features is the ability of ZoneMinder to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ `. More esoteric examples of switching run states based on phone location can be found `here `__.
-
-Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes.
-
-.. image:: images/runstates.png
-
-
-
-Adding Monitors
-^^^^^^^^^^^^^^^
-Now that we have a basic understanding of the web console, lets go about adding a new camera (monitor). For this example, lets assume we have an IP camera that streams RTSP at LAN IP address 192.168.1.33.
-
-The first thing we will need to know is how to access that camera's video feed. You will need to consult your camera's manual or check their forum. Zoneminder community users also have a frequently updated list right `here `__ that lists information about many cameras. If you don't find your list there and can't seem to find it elsewhere, feel free to register and ask in the `user foums `__.
-
-The camera we are using as an example here is a Foscam 9831W which is a 1280x960 RTSP camera, and the URL to access it's feed is *username:password@IPADDRESS:PORT/videoMain*
-
-Let's get started:
-
-Click on the "Add new monitor" button below:
-
-.. image:: images/getting-started-modern-look.png
-
-This brings up the new monitor window:
-
-.. image:: images/getting-started-add-monitor-general.png
- :width: 800px
-
-* We've given it a name of 'Garage', because, well, its better than Monitor-1 and this is my Garage camera.
-
-* There are various source types. As a brief introduction you'd want to use 'Local' if your camera is physically attached to your ZM server (like a USB camera, for example), and one of 'Remote', 'FFMpeg', 'Libvlc' or 'cURL' for a remote camera (not necessarily, but usually). For this example, let's go with 'Remote'.
-
-.. NOTE::
- As a thumb rule, if you have a camera accessible via IP and it does HTTP or RTSP,
- start with Remote, then try FFMpeg and libvlc if it doesn't work (:doc:`/userguide/definemonitor`
- covers other modes in more details). If you are wondering what 'File' does, well, ZoneMinder was
- built with compatibility in mind. Take a look at `this post
- `__ to see how file can be used for leisure reading.
-
-* Let's leave the Function as 'Monitor' just so we can use this as an example to change it later another way. Practically, feel free to select your mode right now - Modect, Record etc depending on what you want ZoneMinder to do with this camera
-
-* We've put in MaxFPS and AlarmFPS as 20 here. **You can leave this empty too**. Whatever you do here, *it's important to make sure these values are higher than the FPS of the camera*. The reason we've added a value here is that as of Aug 2015, if a camera goes offline, ZoneMinder eats up a lot of CPU trying to reach it and putting a larger value here than the actual FPS helps in that specific situation.
-
-.. NOTE::
- We strongly recommend not putting in a lower FPS here that the one configured inside your camera.
- Zoneminder should not be used to manage camera frame rate. That always causes many problems. It's
- much better you set the value directly in-camera and either leave this blank or specify a higher FPS
- here. In this case, our actual camera FPS is 3 and we've set this value here to 10.
-
-* We are done for the General tab. Let's move to the next tab
-
-.. image:: images/getting-started-add-monitor-source.png
- :width: 800px
-
-* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera)
-* The other boxes are mostly self-explanatory
-
-That's pretty much it. Click on Save. We are not going to explore the other tabs in this simple guide.
-
-You now have a configured monitor:
-
-.. image:: images/getting-started-add-monitor-orange.png
-
-If you want to change its mode from Monitor to say, Modect (Motion Detect), later all you need to do is click on the Function column that says 'Monitor' and change it to 'Modect' like so:
-
-
-.. image:: images/getting-started-add-monitor-modect.png
-
-and we now have:
-
-.. image:: images/getting-started-add-monitor-modect-ready.png
-
-And then, finally, to see if everything works, lets click on the monitor name ('Garage' in this example) and that should bring up a live feed just like this:
-
-.. image:: images/getting-started-add-monitor-live.png
Conclusion
diff --git a/docs/userguide/images/filter-button.png b/docs/userguide/images/filter-button.png
deleted file mode 100644
index a480b1900..000000000
Binary files a/docs/userguide/images/filter-button.png and /dev/null differ
diff --git a/docs/userguide/images/filter-filterview.png b/docs/userguide/images/filter-filterview.png
index 561996668..e09f34b4d 100644
Binary files a/docs/userguide/images/filter-filterview.png and b/docs/userguide/images/filter-filterview.png differ
diff --git a/docs/userguide/images/filter-save.png b/docs/userguide/images/filter-save.png
deleted file mode 100644
index 96db574c6..000000000
Binary files a/docs/userguide/images/filter-save.png and /dev/null differ
diff --git a/docs/userguide/images/getting-started-add-monitor-general.png b/docs/userguide/images/getting-started-add-monitor-general.png
index 9b722907d..cf4c93487 100644
Binary files a/docs/userguide/images/getting-started-add-monitor-general.png and b/docs/userguide/images/getting-started-add-monitor-general.png differ
diff --git a/docs/userguide/images/getting-started-add-monitor-live.png b/docs/userguide/images/getting-started-add-monitor-live.png
deleted file mode 100644
index a1d7f4100..000000000
Binary files a/docs/userguide/images/getting-started-add-monitor-live.png and /dev/null differ
diff --git a/docs/userguide/images/getting-started-add-monitor-modect-ready.png b/docs/userguide/images/getting-started-add-monitor-modect-ready.png
index 338a0e097..fcac21140 100644
Binary files a/docs/userguide/images/getting-started-add-monitor-modect-ready.png and b/docs/userguide/images/getting-started-add-monitor-modect-ready.png differ
diff --git a/docs/userguide/images/getting-started-add-monitor-modect.png b/docs/userguide/images/getting-started-add-monitor-modect.png
deleted file mode 100644
index bc6795b37..000000000
Binary files a/docs/userguide/images/getting-started-add-monitor-modect.png and /dev/null differ
diff --git a/docs/userguide/images/getting-started-add-monitor-orange.png b/docs/userguide/images/getting-started-add-monitor-orange.png
deleted file mode 100644
index 9d5e227aa..000000000
Binary files a/docs/userguide/images/getting-started-add-monitor-orange.png and /dev/null differ
diff --git a/docs/userguide/images/getting-started-add-monitor-source.png b/docs/userguide/images/getting-started-add-monitor-source.png
index e8b19925e..a3b9324f5 100644
Binary files a/docs/userguide/images/getting-started-add-monitor-source.png and b/docs/userguide/images/getting-started-add-monitor-source.png differ
diff --git a/docs/userguide/images/getting-started-audit-event.png b/docs/userguide/images/getting-started-audit-event.png
new file mode 100644
index 000000000..91e1386d8
Binary files /dev/null and b/docs/userguide/images/getting-started-audit-event.png differ
diff --git a/docs/userguide/images/getting-started-enable-auth.png b/docs/userguide/images/getting-started-enable-auth.png
index 9160ffecc..ec376c478 100644
Binary files a/docs/userguide/images/getting-started-enable-auth.png and b/docs/userguide/images/getting-started-enable-auth.png differ
diff --git a/docs/userguide/images/getting-started-login.png b/docs/userguide/images/getting-started-login.png
index d981048c0..6b6c8e05d 100644
Binary files a/docs/userguide/images/getting-started-login.png and b/docs/userguide/images/getting-started-login.png differ
diff --git a/docs/userguide/images/getting-started-modern-look.png b/docs/userguide/images/getting-started-modern-look.png
index 34dbcdfc8..6cb62718c 100644
Binary files a/docs/userguide/images/getting-started-modern-look.png and b/docs/userguide/images/getting-started-modern-look.png differ
diff --git a/docs/userguide/images/getting-started-timezone.png b/docs/userguide/images/getting-started-timezone.png
new file mode 100644
index 000000000..e372eb8b0
Binary files /dev/null and b/docs/userguide/images/getting-started-timezone.png differ
diff --git a/docs/userguide/images/getting-started-understand-console.png b/docs/userguide/images/getting-started-understand-console.png
index f332b4977..89c735992 100644
Binary files a/docs/userguide/images/getting-started-understand-console.png and b/docs/userguide/images/getting-started-understand-console.png differ
diff --git a/docs/userguide/images/viewevents-stream.png b/docs/userguide/images/viewevents-stream.png
index 672a74e4d..a1448e0bc 100644
Binary files a/docs/userguide/images/viewevents-stream.png and b/docs/userguide/images/viewevents-stream.png differ
diff --git a/docs/userguide/images/viewmonitor-main.png b/docs/userguide/images/viewmonitor-main.png
index 74354f0b0..80791ebb6 100644
Binary files a/docs/userguide/images/viewmonitor-main.png and b/docs/userguide/images/viewmonitor-main.png differ
diff --git a/docs/userguide/images/viewmonitor-stream.png b/docs/userguide/images/viewmonitor-stream.png
index 126a67bb2..58c35952c 100644
Binary files a/docs/userguide/images/viewmonitor-stream.png and b/docs/userguide/images/viewmonitor-stream.png differ
diff --git a/docs/userguide/images/zm-system-overview.jpg b/docs/userguide/images/zm-system-overview.jpg
index de02dcf9e..61594a337 100644
Binary files a/docs/userguide/images/zm-system-overview.jpg and b/docs/userguide/images/zm-system-overview.jpg differ
diff --git a/docs/userguide/images/zm-system-overview.xml b/docs/userguide/images/zm-system-overview.xml
index e4314666a..8cbd3b3ca 100644
--- a/docs/userguide/images/zm-system-overview.xml
+++ b/docs/userguide/images/zm-system-overview.xml
@@ -1,2 +1 @@
-
-
\ No newline at end of file
+7V1Zk6s2Fv41XZU82AWI9bGXdCZTuVW3pieVe/MyRRvazTS2HMC9/fqRAGF0JGyBhU33XKfS1yxmOct3VkkX6Hr1+msWbh6/4ChOLywjer1ANxeWZZqBTf6he96qPR4yqx3LLInqk3Y77pL3uN5p1Hu3SRTn3IkFxmmRbPidC7xex4uC2xdmGX7hT3vAKX/XTbiMhR13izAV9/6ZRMUjey832B34R5wsH+tb+5ZbHbgPF0/LDG/X9f0uLPRQfqrDq5Bdq37R/DGM8EtrF/rlAl1nGBfVt9XrdZxS2jKyVb+77TjaPHcWrwuVH1jVD57DdBuzJy6fq3hjtHh5TIr4bhMu6PYL4fcFunosVinZMslX8Y71QzzHWRG/tnbVT/BrjFdxkb2RU+qjNqNGLS1+vfnSIr1X73tsUd1kJ4Y1u5fNpXevTL7Uby2nABIo8NeXa/MMVHBMh6OCaYhkcCVUQBqIYMuIcA5REIhgn44IjowI6wkQoUGLExDBPwwI8Tq6pBhLttZ4HfOvH78mxTfy3Zg79dZ3ukW/r8mjtA7Rze/sV+voNqHPVJ4ZhfljHB2iZ4632SLmWFeE2TIuOJGOIw7pRZq3aOpIaMr2ZXEaFskzbx9khK7v8BUn5Hl3LAUQ17CUXaJ6m/pXbcCGF/L4CzXAyC5U0UC4UMn35rWVRCGQ6MPlOZDRtsE7S5BRxjsd+sA8Ep4KZ7GSkAoSaByNCqaMCufARkgFGTaORgUFb2mC4MiY10ZHJtYH4ZGRvQt2FOFTHSltu8P49UVKO3Dkj6wfKS0RJHoKRsP+NvN3YvKtJUCtI5xg5OR9CnaLRRrmebJgu+vTlEUmECUG9RQYB6KVM5bAIIAI7E69BcY5cCGNAiPi6ccWGIYnQzwwRn4XSIwzmsRAG+IMlRj3wIU0SszRtmdqEiOxSs6RRskdS2Ics+NOvd13y9l/IY0SI2Y2BIlpyQd1wZJFmP4e3sfpV5wnRYLX5Ng9Lgq8ap1wmSZLeqDAm5L3GX5qkmRWs+capzgrb4IMwzduqfg8EBFp7a8TY+QXLAnG+TdGdWRDH3X1uqSJxnmCc2+eLPA6nz8nUYz/Q76S58rD6mGvMlzUX29mNDukxeV0hfDDE8QMScTMdLrFTNXlZHnFH0zUzUTbPx0TvR9MHIWJTbrqFEwUM2Pvq5ewWDzON6nAzlMkCX2OFoj5W+0koSPSQkcgjETT8r6KFuchhA2ypSclhJg4FyjQ8sp2LpM0KWC7FkwLkO9f4ywhDxZn9b5OkrW9KyatU0l/2gGvuMHQ9KfNS73lIP5C+vwnJJreFV4nBQFNQv4ML+I8v7DctKBQmTyTr0v6tYSECC/ZIXKb1lFBOoiQF7w8ZHGevIf35Qklv9FVWAP9grCcyoFgAVZJFNEfXKXUZlw1RUgZxrexvw4TRJFigl0XWOvHudhlOpTVc2bMDWYAa6bNLC1CZUGLzl8APzzk8dFCIBaF+kKcEr37w7/Ba1RgCqDnIYkldC0NqKfgz1Dguas34/Qev/yy23FV7iAHHnGWvON1EaZVqLkfJzsC2MBi2xAp5WGt5aiCKFLNeZ0GRG0fGDpzKIiaMG0xWmaUOWw9QZTADvFd0w+OoZ5GDHVNBslNFKpFqIDzNGP2VS+KiqVEETBU3SRJjqoGBvNCyGwp6Tlzlrh097R03xIiH2DtVHW/8byaqgi4kEbd75fklnJ9mcXxWghuEQxV1dg8LZa6KJgHBvJt13ToX4vni+nOzYAEKZ5b/oVsUu8XAAlHGAtr5LcYDJWpgY+C10xeteC15Xs8Xh/r9GoFZNs6mWoej86uiM49S0sO8JxMy547AfLGKhe4NlHt1sffd/feGi28zHgaLUY/H0ujraM1+lj3CvG84n+vR5cV6jyT0WVP1OWeRT8HtnWgsXUZeF4wbzhYb1177jPzTv+CyFyjGouZrI+lxujsamzythwkHfXosUpWZV+Fn1deugdn4ZpqFdRinTX9iXjTtgu0yxiopvBC7ni5EVmvdSsV0mK8+/eWjgi5ogo6qxXwkpyRxg/F7uguQVJdJd+Ea+llHvC6mOXlqBt6FdPYvJJ/yl8aabKOZ4xB5dG5Jd7ich2mb+T3dGwNTeWscFns2+Vrqnvzz9M7jcMXMGuJl0CGOiTJcrWYnP2QVglJcl5pCDvFvUddCgG3zxUslC8pS9l7LJTySIZj+xGVoWRPQ+tYjUOod5cQUGjeN2dD1fR3mcHI25DfuH8JzOWuO17y1pElcD4IQC3CTbHNKEDVrs515UBQ/aUOKKbQlYWrOP8BWnvKSqcELTEncSPSm/WhLN6IHEQl3c5SfyNxCE8oSbeqLcMRDa0oTr+ITwLvbQwXsb5du8NZ8YiXmJj7dgFPEc95IzB1FxIZIDU+tEkBjmc1YWm6A6IJQcO31mkbekK+54HhYMnA2Ptcwvn19k7sqicYbC/6dcMMdztOLH6MfGCggWefxpGA9yXipMWRUHyfEfwKWVPFB/Er8gKXXgXzHSpPIkryJ3ohyw1X1PxUf6nHERZhSpuBjIQ+0c3VD2+jbUQD3ohKRjSahjmSuyGmW95XRZYsl3F2nkZGmGls4Lrd0zNSI6OjMu5be0vPIT+kI90c+M2OrpYfSUBKvt+8tjfeehoGMOTcO6evIsjK4Kx0YM4De/fhY0vyuHOj/XFHcWRcMJCHeWLaHBOx/4QAaZwRr5YieKX0bfxe3+f0H4rt/B5Jr1LpdVA4p234lLELnEXJeroNoJzWLale1t/BqICbX+h/9JwsjBJy54NRFEMRPR1PTSP3sX49n4ebmWNk710x5fZnfE923MXZMxWufuZEiUc1H5TZ09siIdAoaLtoLtokJJubR4dRcmWzDpDtX//4jfz9afO4KXWW+ln/vPv5k5AYnZbEYtKlJPHl198+Bz2RI/qUMmKyyvRRxFTJy0zFifJNv8uJ+iQlQWR6clHo3zPJX8iCtUVt+Rxwn0CvG+Sq5GfOJp+9xqXrLCgxDJxw+28jukyU4cx3yqlJ40DcoC/L44pZnm1OPaHbMFol4txBLLO/XaWXCzpAYPBI2MkZJY+NzmlKdmojyGHvwSCjpCuyZ7TWqfKSyY462/zVdFk2q4Q1KV22QLBrw/5N5fwtKDMgWK/QZZYAZtj1YE5tZkmMzt9Xm6flmYYT+3xOEklmuhprODG71aQMdCdNJ2wv7eCAaijby65wQrOOwSmJmIHXpWOeylicqYQmVtv3M+aehlRuW1YZ3uxN77rnHS8P5NceOtTTBTNUIcVJ8PrKL3xgpDl08cQ8BZ93vc3i3Y6w2OaCeE800drX+WSarCXH6hiWHrzluT9Kf7SnK7uiz5HthJupmEHoywQDXU2LNQOwzATEI12upgAjejtUPIUMyAeeiSmlEnS/Te8B3nTKaY8Q1gcSwJrQ2rV6W5RkHVMvebKREVVxjTZ0cOzr6PTwaaMH7OWo0hFGvsiSTdEq11UX7ajXMfKvcUFBZfykePUSN3tDYHU++qDpB0nmZbUkiKQlvOk3ufwgl7LxGi3oNU4drL3AI66ujZBjuYZpOmB6L2TODd/1DMcyLGQKeqWK5PQuYIJlT62oPwBu/SnGs8Mmkig3d0FHuXUw6mgHFAzEhKiDS1WdtanEg8ECzD8qCxnMTClmmfu6C35HdK7LXfDFqLlpFvmc0QXTWC3Rhe0aYOIvpEVOZ3DSGiheWuINXww5tXgd4ZpSCWVRRfqCbkXxc0KAQtEFmVIXaKcUavBVPKcj6mjX8iU+J0LdEqW8Do4YKbyv6mmpzpOY9gzgEQSnaw8NpmzIxeqwkkF2p+X+uT5vy+yh3Qoe7ChSjNUHQGQgyyprgMhFuIqzkP58Ey+SByIX5WRxBV7QOeGMvG6pM37axFlacnrxFC7jn1UB9EN0Mvlg7pAmN9KSx0AyxaMWfZfZvmnqu8d57qCJRH2uR4b3bW88UEUIpsIwa6+4NtGASUngndDQcNDqeGTdnjqsIWvu8A6m2HrXKXgTMTk+mHNucJUUiqOpOGlGbykCD8waZLRJkdiy1Jiir//+S5CoTxL0BcfPr1MfJe4XNFvWcaKqNaQLRKde4CnL76bJ+mmvtqsOQQXDXeHEGGrpqf2GbCJ44vrO3HR3H9DNagdz1PpYIO2oDDY+P0YpGC19GSgsvnJucRkuE7uZZXmm8TE3Kzdrd2Ga6TabrrKh3dngQmOuiCpbA2S7icIiLnMCxoz8v91Q4C3HBtOhFP1Cj/ayKYGmNEqzanmjiJLSnSlhqpZIQtbcR4xVMWiU7TQiMwRXs/AlmZiRxpg085pNytPtX1PxVAMzJkAT7uhFcPjq0G4tOHHIiJPPNePqW3LUzMdvVAr6aXuoGiXSUubwDDA+aMYWCj5WrhC47BhtVabRPRuHahLPNGVZPOK3xPlbXsSrvr0VEV5sVyW3TtBfocEgWCZvEBxxeIfb02lStwc9F2PJ4yUlLWcRxmnj3Ruy8ItRq07pd6K2OTh/kwEWxVAeoQGXDR0puybcp35g1eeqS5260igNvTinL9xGSfFhfT7L4lXcNEUdH8/nE53oU+v44ZXroYOnnHpvxGXKmCCsmzLUx0OgV3qkVKn4vN7ex4Ln16kybZBg6gpbNA5DlPcK9ljzfFqLLJpgkLCQCFHOqMCV2EcaNNJMeQ7uo0/oPne7d/wWX2hvumlqv2+AiAcavXXYOVMWivzg115+wRGKTd/aKfj1uVcnH4VfQhuXKWbYR+PX516IfBR+wQXUZMtLj8YvhQETn4pfY5Q/LAOMdzDURi5pYaBC4PapGKhD4UC5yhcD7bHYZSkEKT/YBefLAIGjuGLAaOyyfrCrNxqCINGxTsivKTbpHZ29ZsHyRLIAHkjkOENnBvagpCjOejckULfEQP2xKDZldemWTulQdnzPF0RNrdv3lSA1n6UuaWnsxDMCMOvYpFagbVYtFVeGJ9hoGff0Xd5X0aKsGFyX6lNe4D4uX6HsnCrPTB4owbIKVA8LxuccKtWs1cSUVVKeaPrZufqEhhpkuHq6u/vmEpFKgm/ev17++Pv+edYwuAPc5QvLlPrWGMfdOb9jaotLBv43Loq3u2pQfLgtMM/ePZ0o/WcN4pYuFZHfUgR+ZYVTJfj378n7Q57hv+4WT4unIMJf3v+eyTTqfRU/k5da44KOoilDqwEVuMZh2aXGBcmU0K9TWF04tt3xJHMeN++juyWtg3r/n7msoxjpeS5gpCtj5DjupJyNaNo+5tywgraf2b2QxZ75BoZjnDD5wAHQ2w80Ahaey+d14fS4QytfDl0DqXM1DGSCPhBdy1/A6RI0T2PQoSmiz10ai4vdAMuqa+s+2zVs3X75nbIQ46cP0xyovJKFstPNq80+HNrXNqhtfYsZL6WzYA4GAw5x2Mlmhmk73+50alK+4CimZ/wP
\ No newline at end of file
diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst
index 35bfcf39d..905a87292 100644
--- a/docs/userguide/index.rst
+++ b/docs/userguide/index.rst
@@ -15,4 +15,5 @@ User Guide
cameracontrol
mobile
logging
+ configfiles
diff --git a/docs/userguide/introduction.rst b/docs/userguide/introduction.rst
index 8da67a5e9..a6573a24d 100644
--- a/docs/userguide/introduction.rst
+++ b/docs/userguide/introduction.rst
@@ -1,15 +1,14 @@
Introduction
============
-Welcome to ZoneMinder, the all-in-one Linux GPL'd security camera solution.
+Welcome to ZoneMinder, the all-in-one security camera solution for Linux with GPL License.
-Most commercial "security systems" are designed as a monitoring system that also records. Recording quality can vary from bad to unusable, locating the relevant video can range from challenging to impractical, and exporting can often only be done with the manual present. ZoneMinder was designed primarily to record, and allow easy searches and exporting. Recordings are of the best possible quality, easy to filter and find, and simple to export using any system with a web browser. It also monitors.
+Commercial "security systems" are often designed as a monitoring system with little attention to recording quality. In such a system, locating and exporting relevant video can be challenging and often requires extensive human intervention. ZoneMinder was designed to provide the best possible record quality while allowing easy searching, filtering and exporting of security footage.
-ZoneMinder is designed around a series of independent components that only function when necessary limiting any wasted resource and maximising the efficiency of your machine. A fairly ancient Pentium II PC should be able to track one camera per device at up to 25 frames per second with this dropping by half approximately for each additional camera on the same device. Additional cameras on other devices do not interact so can maintain this frame rate. Even monitoring several cameras still will not overload the CPU as frame processing is designed to synchronise with capture and not stall it.
+ZoneMinder is designed around a series of independent components that only function when necessary, limiting any wasted resource and maximising the efficiency of your machine. An outdated Pentium II PC can have multiple recording devices connected to it, and it is able to track one camera per device at up to 25 frames per second, which drops by approximately half for each additional camera on the same device. Additional cameras on devices that do not interact with other devices can maintain the 25 frame rate per second. Monitoring several cameras will not overload the CPU as frame processing is designed to synchronise with capture.
-As well as being fast ZoneMinder is designed to be friendly and even more than that, actually useful. As well as the fast video interface core it also comes with a user friendly and comprehensive PHP based web interface allowing you to control and monitor your cameras from home, at work, on the road, or even a web enabled cell phone. It supports variable web capabilities based on available bandwidth. The web interface also allows you to view events that your cameras have captured and archive them or review them time and again, or delete the ones you no longer wish to keep. The web pages directly interact with the core daemons ensuring full co-operation at all times. ZoneMinder can even be installed as a system service ensuring it is right there if your computer has to reboot for any reason.
+A fast video interface core, a user-friendly and comprehensive PHP based web interface allows ZoneMinder to be efficient, friendly and most importantly useful. You can control and monitor your cameras from home, at work, on the road, or a web-enabled cell phone. It supports variable web capabilities based on available bandwidth. The web interface also allows you to view events that your cameras have captured, which can be archived, reviewed or deleted. The web application directly interacts with the core daemons ensuring full co-operation at all times. ZoneMinder can also be installed as a system service to reboot a system remotely.
-The core of ZoneMinder is the capture and analysis of images and there is a highly configurable set of parameters that allow you to ensure that you can eliminate false positives whilst ensuring that anything you don't want to miss will be captured and saved. ZoneMinder allows you to define a set of 'zones' for each camera of varying sensitivity and functionality. This allows you to eliminate regions that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones.
-
-ZoneMinder is free, but if you do find it useful then please feel free to visit http://www.zoneminder.com/donate.html and help to fund future improvements to ZoneMinder.
+The core of ZoneMinder is the capture and analysis of images and a highly configurable set of parameters that eliminate false positives whilst ensuring minimum loss of footage. For example, you can define a set of 'zones' for each camera of varying sensitivity and functionality. This eliminates zones that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones.
+ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit https://zoneminder.com/donate/ and help us fund our future improvements.
diff --git a/docs/userguide/logging.rst b/docs/userguide/logging.rst
index 4256079ae..a6dfdb0c0 100644
--- a/docs/userguide/logging.rst
+++ b/docs/userguide/logging.rst
@@ -1,18 +1,33 @@
Logging
=======
-Most components of ZoneMinder can emit informational, warning, error and debug messages in a standard format. These messages can be logged in one or more locations. By default all messages produced by scripts are logged in ';
- Error ('Invalid recaptcha secret detected');
- }
- }
- } // end if success==false
-
- } // end if using reCaptcha
-
- $username = validStr( $_REQUEST['username'] );
- $password = isset($_REQUEST['password'])?validStr($_REQUEST['password']):'';
- userLogin( $username, $password );
- $refreshParent = true;
- $view = 'console';
- $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console';
-} else if ( $action == 'logout' ) {
- userLogout();
- $refreshParent = true;
- $view = 'none';
-} else if ( $action == 'bandwidth' && isset($_REQUEST['newBandwidth']) ) {
- $_COOKIE['zmBandwidth'] = validStr($_REQUEST['newBandwidth']);
- setcookie( 'zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10 );
- $refreshParent = true;
-}
-
-// Event scope actions, view permissions only required
-if ( canView('Events') ) {
-
- if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) {
- if ( $action == 'addterm' ) {
- $_REQUEST['filter'] = addFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] );
- } elseif ( $action == 'delterm' ) {
- $_REQUEST['filter'] = delFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] );
- } else if ( canEdit( 'Events' ) ) {
- if ( $action == 'delete' ) {
- if ( ! empty($_REQUEST['Id']) ) {
- dbQuery('DELETE FROM Filters WHERE Id=?', array($_REQUEST['Id']));
- }
- } else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) {
- # or ( $action == 'submit' ) ) {
-
- $sql = '';
- $_REQUEST['filter']['Query']['sort_field'] = validStr($_REQUEST['filter']['Query']['sort_field']);
- $_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']);
- $_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']);
- if ( $action == 'execute' ) {
- $tempFilterName = '_TempFilter'.time();
- $sql .= ' Name = \''.$tempFilterName.'\'';
- } else {
- $sql .= ' Name = '.dbEscape($_REQUEST['filter']['Name']);
- }
- $sql .= ', Query = '.dbEscape(jsonEncode($_REQUEST['filter']['Query']));
- $sql .= ', AutoArchive = '.(!empty($_REQUEST['filter']['AutoArchive']) ? 1 : 0);
- $sql .= ', AutoVideo = '. ( !empty($_REQUEST['filter']['AutoVideo']) ? 1 : 0);
- $sql .= ', AutoUpload = '. ( !empty($_REQUEST['filter']['AutoUpload']) ? 1 : 0);
- $sql .= ', AutoEmail = '. ( !empty($_REQUEST['filter']['AutoEmail']) ? 1 : 0);
- $sql .= ', AutoMessage = '. ( !empty($_REQUEST['filter']['AutoMessage']) ? 1 : 0);
- $sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0);
- $sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']);
- $sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0);
- if ( !empty($_REQUEST['filter']['AutoMove']) ? 1 : 0) {
- $sql .= ', AutoMove = 1, AutoMoveTo='. validInt($_REQUEST['filter']['AutoMoveTo']);
- } else {
- $sql .= ', AutoMove = 0';
- }
- $sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0);
- $sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0);
- $sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
-
- if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
- dbQuery('UPDATE Filters SET ' . $sql. ' WHERE Id=?', array($_REQUEST['Id']));
- } else {
- dbQuery('INSERT INTO Filters SET' . $sql);
- $_REQUEST['Id'] = dbInsertId();
- }
- if ( $action == 'execute' ) {
- executeFilter( $tempFilterName );
- }
-
- } // end if save or execute
- } // end if canEdit(Events)
- return;
- } // end if object == filter
- else {
-
- // Event scope actions, edit permissions required
- if ( canEdit('Events') ) {
- if ( ($action == 'rename') && isset($_REQUEST['eventName']) && !empty($_REQUEST['eid']) ) {
- dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid']));
- } else if ( $action == 'eventdetail' ) {
- if ( !empty($_REQUEST['eid']) ) {
- dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) );
- } else {
- $dbConn->beginTransaction();
- foreach( getAffectedIds('markEid') as $markEid ) {
- dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid ) );
- }
- $dbConn->commit();
- }
- $refreshParent = true;
- $closePopup = true;
- } elseif ( $action == 'archive' || $action == 'unarchive' ) {
- $archiveVal = ($action == 'archive')?1:0;
- if ( !empty($_REQUEST['eid']) ) {
- dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $_REQUEST['eid']));
- } else {
- $dbConn->beginTransaction();
- foreach( getAffectedIds( 'markEid' ) as $markEid ) {
- dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $markEid));
- }
- $dbConn->commit();
- $refreshParent = true;
- }
- } elseif ( $action == 'delete' ) {
- $dbConn->beginTransaction();
- foreach( getAffectedIds( 'markEid' ) as $markEid ) {
- deleteEvent( $markEid );
- }
- $dbConn->commit();
- $refreshParent = true;
- }
- } // end if canEdit(Events)
- } // end if filter or something else
-} // end canView(Events)
-
-// Monitor control actions, require a monitor id and control view permissions for that monitor
-if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {
- require_once( 'control_functions.php' );
- require_once( 'Monitor.php' );
- $mid = validInt($_REQUEST['mid']);
- if ( $action == 'control' ) {
- $monitor = new Monitor( $mid );
-
- $ctrlCommand = buildControlCommand( $monitor );
- sendControlCommand( $monitor->Id(), $ctrlCommand );
- } elseif ( $action == 'settings' ) {
- $args = ' -m ' . escapeshellarg($mid);
- $args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']);
- $args .= ' -C' . escapeshellarg($_REQUEST['newContrast']);
- $args .= ' -H' . escapeshellarg($_REQUEST['newHue']);
- $args .= ' -O' . escapeshellarg($_REQUEST['newColour']);
-
- $zmuCommand = getZmuCommand( $args );
-
- $zmuOutput = exec( $zmuCommand );
- list( $brightness, $contrast, $hue, $colour ) = explode( ' ', $zmuOutput );
- dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid));
- }
-}
-
-// Control capability actions, require control edit permissions
-if ( canEdit('Control') ) {
- if ( $action == 'controlcap' ) {
- require_once( 'Control.php' );
- $Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null );
-
- //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
- $Control->save( $_REQUEST['newControl'] );
- $refreshParent = true;
- $view = 'none';
- } elseif ( $action == 'delete' ) {
- if ( isset($_REQUEST['markCids']) ) {
- foreach( $_REQUEST['markCids'] as $markCid ) {
- dbQuery( 'delete from Controls where Id = ?', array($markCid) );
- dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) );
- $refreshParent = true;
- }
- }
- } // end if action
-} // end if canEdit Controls
-
-if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
- if ( $action == 'save' ) {
- foreach ( $_REQUEST['mids'] as $mid ) {
- $mid = ValidInt( $mid );
- if ( ! canEdit('Monitors', $mid ) ) {
- Warning("Cannot edit monitor $mid");
- continue;
- }
- $Monitor = new Monitor( $mid );
- if ( $Monitor->Type() != 'WebSite' ) {
- $Monitor->zmaControl('stop');
- $Monitor->zmcControl('stop');
- }
- $Monitor->save( $_REQUEST['newMonitor'] );
- if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
- $Monitor->zmcControl('start');
- if ( $Monitor->Enabled() ) {
- $Monitor->zmaControl('start');
- }
- }
-
- } // end foreach mid
- $refreshParent = true;
- } // end if action == save
-} // end if object is Monitor
-
-// Monitor edit actions, require a monitor id and edit permissions for that monitor
-if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) {
- $mid = validInt($_REQUEST['mid']);
- if ( $action == 'function' ) {
- $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
-
- $newFunction = validStr($_REQUEST['newFunction']);
- # Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0
- $newEnabled = ( !isset( $_REQUEST['newEnabled'] ) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1';
- $oldFunction = $monitor['Function'];
- $oldEnabled = $monitor['Enabled'];
- if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) {
- dbQuery( 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', array( $newFunction, $newEnabled, $mid ) );
-
- $monitor['Function'] = $newFunction;
- $monitor['Enabled'] = $newEnabled;
- if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
- $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled);
- zmaControl( $monitor, 'stop' );
- zmcControl( $monitor, $restart?'restart':'' );
- zmaControl( $monitor, 'start' );
- }
- $refreshParent = true;
- }
- } elseif ( $action == 'zone' && isset( $_REQUEST['zid'] ) ) {
- $zid = validInt($_REQUEST['zid']);
- $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
-
- if ( !empty($zid) ) {
- $zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array( $mid, $zid ) );
- } else {
- $zone = array();
- }
-
- if ( $_REQUEST['newZone']['Units'] == 'Percent' ) {
- $_REQUEST['newZone']['MinAlarmPixels'] = intval(($_REQUEST['newZone']['MinAlarmPixels']*$_REQUEST['newZone']['Area'])/100);
- $_REQUEST['newZone']['MaxAlarmPixels'] = intval(($_REQUEST['newZone']['MaxAlarmPixels']*$_REQUEST['newZone']['Area'])/100);
- if ( isset($_REQUEST['newZone']['MinFilterPixels']) )
- $_REQUEST['newZone']['MinFilterPixels'] = intval(($_REQUEST['newZone']['MinFilterPixels']*$_REQUEST['newZone']['Area'])/100);
- if ( isset($_REQUEST['newZone']['MaxFilterPixels']) )
- $_REQUEST['newZone']['MaxFilterPixels'] = intval(($_REQUEST['newZone']['MaxFilterPixels']*$_REQUEST['newZone']['Area'])/100);
- if ( isset($_REQUEST['newZone']['MinBlobPixels']) )
- $_REQUEST['newZone']['MinBlobPixels'] = intval(($_REQUEST['newZone']['MinBlobPixels']*$_REQUEST['newZone']['Area'])/100);
- if ( isset($_REQUEST['newZone']['MaxBlobPixels']) )
- $_REQUEST['newZone']['MaxBlobPixels'] = intval(($_REQUEST['newZone']['MaxBlobPixels']*$_REQUEST['newZone']['Area'])/100);
- }
-
- unset( $_REQUEST['newZone']['Points'] );
- $types = array();
- $changes = getFormChanges( $zone, $_REQUEST['newZone'], $types );
-
- if ( count( $changes ) ) {
- if ( $zid > 0 ) {
- dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) );
- } else {
- dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) );
- }
- if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
- if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
- zmaControl( $monitor, 'stop' );
- zmcControl( $monitor, 'restart' );
- zmaControl( $monitor, 'start' );
- } else {
- zmaControl( $mid, 'restart' );
- }
- }
- if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) {
- require_once( 'control_functions.php' );
- sendControlCommand( $mid, 'quit' );
- }
- $refreshParent = true;
- }
- $view = 'none';
- } elseif ( $action == 'plugin' && isset($_REQUEST['pl'])) {
- $sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?';
- $pconfs=dbFetchAll( $sql, NULL, array( $mid, $_REQUEST['zid'], $_REQUEST['pl'] ) );
- $changes=0;
- foreach( $pconfs as $pconf ) {
- $value=$_REQUEST['pluginOpt'][$pconf['Name']];
- if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) {
- dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array( $value, $pconf['Id'] ) );
- $changes++;
- }
- }
- if($changes>0) {
- if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
- zmaControl( $mid, 'restart' );
- }
- $refreshParent = true;
- }
- $view = 'none';
- } elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) {
- $smid = validInt($_REQUEST['smid']);
- $monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) );
- $smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) );
-
- dbQuery( 'update Monitors set Sequence=? where Id=?', array( $smonitor['Sequence'], $monitor['Id'] ) );
- dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) );
-
- $refreshParent = true;
- fixSequences();
- } elseif ( $action == 'delete' ) {
- if ( isset($_REQUEST['markZids']) ) {
- $deletedZid = 0;
- foreach( $_REQUEST['markZids'] as $markZid ) {
- $zone = dbFetchOne( 'select * from Zones where Id=?', NULL, array($markZid) );
- dbQuery( 'delete from Zones WHERE MonitorId=? AND Id=?', array( $mid, $markZid) );
- $deletedZid = 1;
- }
- if ( $deletedZid ) {
- if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) {
- if ( $zone['Type'] == 'Privacy' ) {
- zmaControl( $mid, 'stop' );
- zmcControl( $mid, 'restart' );
- zmaControl( $mid, 'start' );
- } else {
- zmaControl( $mid, 'restart' );
- }
- } // end if daemonCheck()
- $refreshParent = true;
- } // end if deletedzid
- } // end if isset($_REQUEST['markZids'])
- } // end if action
-} // end if $mid and canEdit($mid)
-
-// Monitor edit actions, monitor id derived, require edit permissions for that monitor
-if ( canEdit( 'Monitors' ) ) {
- if ( $action == 'monitor' ) {
- $mid = 0;
- if ( !empty($_REQUEST['mid']) ) {
- $mid = validInt($_REQUEST['mid']);
- $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
-
- if ( ZM_OPT_X10 ) {
- $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) );
- if ( !$x10Monitor )
- $x10Monitor = array();
- }
- } else {
- $monitor = array();
- if ( ZM_OPT_X10 ) {
- $x10Monitor = array();
- }
- }
- $Monitor = new Monitor($monitor);
-
- // Define a field type for anything that's not simple text equivalent
- $types = array(
- 'Triggers' => 'set',
- 'Controllable' => 'toggle',
- 'TrackMotion' => 'toggle',
- 'Enabled' => 'toggle',
- 'DoNativeMotDet' => 'toggle',
- 'Exif' => 'toggle',
- 'RTSPDescribe' => 'toggle',
- 'RecordAudio' => 'toggle',
- 'Method' => 'raw',
- );
-
- if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
- $_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
- Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] );
- if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
- $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
- Logger::Debug("Auto selecting server to " . ZM_SERVER_ID);
- }
- }
-
- $columns = getTableColumns('Monitors');
- $changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns);
-
- if ( count( $changes ) ) {
- if ( $mid ) {
-
- # If we change anything that changes the shared mem size, zma can complain. So let's stop first.
- if ( $monitor['Type'] != 'WebSite' ) {
- zmaControl($monitor, 'stop');
- zmcControl($monitor, 'stop');
- }
- dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
- // Groups will be added below
- if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
- $OldStorage = new Storage( $monitor['StorageId'] );
- $saferOldName = basename( $monitor['Name'] );
- if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) )
- unlink( $OldStorage->Path().'/'.$saferOldName );
-
- $NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] );
- if ( ! file_exists( $NewStorage->Path().'/'.$mid ) )
- mkdir( $NewStorage->Path().'/'.$mid, 0755 );
- $saferNewName = basename( $_REQUEST['newMonitor']['Name'] );
- symlink( $mid, $NewStorage->Path().'/'.$saferNewName );
- }
- if ( isset($changes['Width']) || isset($changes['Height']) ) {
- $newW = $_REQUEST['newMonitor']['Width'];
- $newH = $_REQUEST['newMonitor']['Height'];
- $newA = $newW * $newH;
- $oldW = $monitor['Width'];
- $oldH = $monitor['Height'];
- $oldA = $oldW * $oldH;
-
- $zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid) );
- foreach ( $zones as $zone ) {
- $newZone = $zone;
- $points = coordsToPoints( $zone['Coords'] );
- for ( $i = 0; $i < count($points); $i++ ) {
- $points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1));
- $points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1));
- }
- $newZone['Coords'] = pointsToCoords( $points );
- $newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA));
- $newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA));
- $newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA));
- $newZone['MinFilterPixels'] = intval(round(($newZone['MinFilterPixels']*$newA)/$oldA));
- $newZone['MaxFilterPixels'] = intval(round(($newZone['MaxFilterPixels']*$newA)/$oldA));
- $newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA));
- $newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA));
-
- $changes = getFormChanges( $zone, $newZone, $types );
-
- if ( count( $changes ) ) {
- dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) );
- }
- }
- }
- $restart = true;
- } else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors
-# FIXME This is actually a race condition. Should lock the table.
- $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence');
- $changes[] = 'Sequence = '.($maxSeq+1);
-
- if ( dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ) ) {
- $mid = dbInsertId();
- $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height'];
- dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
- //$view = 'none';
- $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] );
- mkdir( $Storage->Path().'/'.$mid, 0755 );
- $saferName = basename($_REQUEST['newMonitor']['Name']);
- symlink( $mid, $Storage->Path().'/'.$saferName );
-
- } else {
- Error("Error saving new Monitor.");
- return;
- }
- } else {
- Error("Users with Monitors restrictions cannot create new monitors.");
- return;
- }
-
- $restart = true;
- } else {
- Logger::Debug("No action due to no changes to Monitor");
- } # end if count(changes)
-
- if (
- ( !isset($_POST['newMonitor']['GroupIds']) )
- or
- ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) )
- or
- array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds())
- ) {
- if ( $Monitor->Id() )
- dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid));
-
- if ( isset($_POST['newMonitor']['GroupIds']) ) {
- foreach ( $_POST['newMonitor']['GroupIds'] as $group_id ) {
- dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid));
- }
- }
- } // end if there has been a change of groups
-
- if ( ZM_OPT_X10 ) {
- $x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
-
- if ( count( $x10Changes ) ) {
- if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) {
- dbQuery( 'update TriggersX10 set '.implode( ', ', $x10Changes ).' where MonitorId=?', array($mid) );
- } elseif ( !$user['MonitorIds'] ) {
- if ( !$x10Monitor ) {
- dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) );
- } else {
- dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) );
- }
- }
- $restart = true;
- }
- }
-
- if ( $restart ) {
-
- $new_monitor = new Monitor($mid);
- //fixDevices();
-
- if ( $monitor['Type'] != 'WebSite' ) {
- $new_monitor->zmcControl('start');
- $new_monitor->zmaControl('start');
- }
-
- if ( $new_monitor->Controllable() ) {
- require_once( 'control_functions.php' );
- sendControlCommand( $mid, 'quit' );
- }
- // really should thump zmwatch and maybe zmtrigger too.
- //daemonControl( 'restart', 'zmwatch.pl' );
- $refreshParent = true;
- } // end if restart
- $view = 'none';
- } elseif ( $action == 'delete' ) {
- if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) {
- require_once( 'Monitor.php' );
- foreach( $_REQUEST['markMids'] as $markMid ) {
- if ( canEdit('Monitors', $markMid) ) {
- // This could be faster as a select all
- if ( $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid) ) ) {
- $Monitor = new Monitor($monitor);
- $Monitor->delete();
- } // end if monitor found in db
- } // end if canedit this monitor
- } // end foreach monitor in MarkMid
- } // markMids is set and we aren't limited to specific monitors
- } // end if action == Delete
-}
-
-// Device view actions
-if ( canEdit( 'Devices' ) ) {
- if ( $action == 'device' ) {
- if ( !empty($_REQUEST['command']) ) {
- setDeviceStatusX10( $_REQUEST['key'], $_REQUEST['command'] );
- } elseif ( isset( $_REQUEST['newDevice'] ) ) {
- if ( isset($_REQUEST['did']) ) {
- dbQuery( 'update Devices set Name=?, KeyString=? where Id=?', array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) );
- } else {
- dbQuery( 'insert into Devices set Name=?, KeyString=?', array( $_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'] ) );
- }
- $refreshParent = true;
- $view = 'none';
- }
- } elseif ( $action == 'delete' ) {
- if ( isset($_REQUEST['markDids']) ) {
- foreach( $_REQUEST['markDids'] as $markDid ) {
- dbQuery( 'delete from Devices where Id=?', array($markDid) );
- $refreshParent = true;
- }
- }
- } // end if action
-} // end if canedit devices
-
-// Group view actions
-if ( canView( 'Groups' ) && $action == 'setgroup' ) {
- if ( !empty($_REQUEST['gid']) ) {
- setcookie( 'zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10 );
- } else {
- setcookie( 'zmGroup', '', time()-3600*24*2 );
- }
- $refreshParent = true;
-}
-
-// 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 ( $action == 'group' ) {
- $monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
- $group_id = null;
- if ( !empty($_POST['gid']) ) {
- $group_id = $_POST['gid'];
- dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?',
- array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) );
- 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();
- }
- if ( $group_id ) {
- foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) {
- dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) );
- }
- }
- $view = 'none';
- $refreshParent = true;
- } else if ( $action == 'delete' ) {
- if ( !empty($_REQUEST['gid']) ) {
- if ( is_array( $_REQUEST['gid'] ) ) {
- foreach( $_REQUEST['gid'] as $gid ) {
- $Group = new Group( $gid );
- $Group->delete();
- }
- } else {
- $Group = new Group( $_REQUEST['gid'] );
- $Group->delete();
- }
- }
- $refreshParent = true;
- } # end if action
-} // end if can edit groups
-
-// System edit actions
-if ( canEdit( 'System' ) ) {
- if ( isset( $_REQUEST['object'] ) ) {
- if ( $_REQUEST['object'] == 'MontageLayout' ) {
- require_once('MontageLayout.php');
- if ( $action == 'Save' ) {
- $Layout = null;
- if ( $_REQUEST['Name'] != '' ) {
- $Layout = new MontageLayout();
- $Layout->Name( $_REQUEST['Name'] );
- } else {
- $Layout = new MontageLayout( $_REQUEST['zmMontageLayout'] );
- }
- $Layout->Positions( $_REQUEST['Positions'] );
- $Layout->save();
- session_start();
- $_SESSION['zmMontageLayout'] = $Layout->Id();
- setcookie('zmMontageLayout', $Layout->Id(), 1 );
- session_write_close();
- $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montagereview';
- } // end if save
-
- } else if ( $_REQUEST['object'] == 'server' ) {
-
- if ( $action == 'Save' ) {
- if ( !empty($_REQUEST['id']) )
- $dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) );
- else
- $dbServer = array();
-
- $types = array();
- $changes = getFormChanges( $dbServer, $_REQUEST['newServer'], $types );
-
- if ( count( $changes ) ) {
- if ( !empty($_REQUEST['id']) ) {
- dbQuery( 'UPDATE Servers SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
- } else {
- dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) );
- }
- $refreshParent = true;
- }
- $view = 'none';
- } else if ( $action == 'delete' ) {
- if ( !empty($_REQUEST['markIds']) ) {
- foreach( $_REQUEST['markIds'] as $Id )
- dbQuery( 'DELETE FROM Servers WHERE Id=?', array($Id) );
- }
- $refreshParent = true;
- } else {
- Error( "Unknown action $action in saving Server" );
- }
- } else if ( $_REQUEST['object'] == 'storage' ) {
- if ( $action == 'Save' ) {
- if ( !empty($_REQUEST['id']) )
- $dbStorage = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']) );
- else
- $dbStorage = array();
-
- $types = array();
- $changes = getFormChanges( $dbStorage, $_REQUEST['newStorage'], $types );
-
- if ( count( $changes ) ) {
- if ( !empty($_REQUEST['id']) ) {
- dbQuery( 'UPDATE Storage SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) );
- } else {
- dbQuery( 'INSERT INTO Storage set '.implode( ', ', $changes ) );
- }
- $refreshParent = true;
- }
- $view = 'none';
- } else if ( $action == 'delete' ) {
- if ( !empty($_REQUEST['markIds']) ) {
- foreach( $_REQUEST['markIds'] as $Id )
- dbQuery( 'DELETE FROM Storage WHERE Id=?', array($Id) );
- }
- $refreshParent = true;
- } else {
- Error( "Unknown action $action in saving Storage" );
- }
- } # end if isset($_REQUEST['object'] )
-
- } else if ( $action == 'version' && isset($_REQUEST['option']) ) {
- $option = $_REQUEST['option'];
- switch( $option ) {
- case 'go' :
- {
- // Ignore this, the caller will open the page itself
- break;
- }
- case 'ignore' :
- {
- dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" );
- break;
- }
- case 'hour' :
- case 'day' :
- case 'week' :
- {
- $nextReminder = time();
- if ( $option == 'hour' ) {
- $nextReminder += 60*60;
- } elseif ( $option == 'day' ) {
- $nextReminder += 24*60*60;
- } elseif ( $option == 'week' ) {
- $nextReminder += 7*24*60*60;
- }
- dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" );
- break;
- }
- case 'never' :
- {
- dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" );
- break;
- }
- }
- }
- if ( $action == 'donate' && isset($_REQUEST['option']) ) {
- $option = $_REQUEST['option'];
- switch( $option ) {
- case 'go' :
- {
- // Ignore this, the caller will open the page itself
- break;
- }
- case 'hour' :
- case 'day' :
- case 'week' :
- case 'month' :
- {
- $nextReminder = time();
- if ( $option == 'hour' ) {
- $nextReminder += 60*60;
- } elseif ( $option == 'day' ) {
- $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_DONATE_REMINDER_TIME'" );
- break;
- }
- case 'never' :
- case 'already' :
- {
- dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" );
- break;
- }
- } // end switch option
- }
- if ( $action == 'options' && isset($_REQUEST['tab']) ) {
- $configCat = $configCats[$_REQUEST['tab']];
- $changed = false;
- foreach ( $configCat as $name=>$value ) {
- unset( $newValue );
- if ( $value['Type'] == 'boolean' && empty($_REQUEST['newConfig'][$name]) ) {
- $newValue = 0;
- } else if ( isset($_REQUEST['newConfig'][$name]) ) {
- $newValue = preg_replace( "/\r\n/", "\n", stripslashes( $_REQUEST['newConfig'][$name] ) );
- }
-
- if ( isset($newValue) && ($newValue != $value['Value']) ) {
- dbQuery( 'UPDATE Config SET Value=? WHERE Name=?', array( $newValue, $name ) );
- $changed = true;
- }
- }
- if ( $changed ) {
- switch( $_REQUEST['tab'] ) {
- case 'system' :
- case 'config' :
- $restartWarning = true;
- break;
- case 'web' :
- case 'tools' :
- break;
- case 'logging' :
- case 'network' :
- case 'mail' :
- case 'upload' :
- $restartWarning = true;
- break;
- case 'highband' :
- case 'medband' :
- case 'lowband' :
- break;
- }
- $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab'];
- }
- loadConfig( false );
- } elseif ( $action == 'user' ) {
- if ( !empty($_REQUEST['uid']) )
- $dbUser = dbFetchOne( "SELECT * FROM Users WHERE Id=?", NULL, array($_REQUEST['uid']) );
- else
- $dbUser = array();
-
- $types = array();
- $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types );
-
- if ( $_REQUEST['newUser']['Password'] )
- $changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')';
- else
- unset( $changes['Password'] );
-
- if ( count( $changes ) ) {
- if ( !empty($_REQUEST['uid']) ) {
- dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['uid']) );
- # If we are updating the logged in user, then update our session user data.
- if ( $user and ( $dbUser['Username'] == $user['Username'] ) )
- userLogin( $dbUser['Username'], $dbUser['Password'] );
- } else {
- dbQuery( 'insert into Users set '.implode( ', ', $changes ) );
- }
- $refreshParent = true;
- }
- $view = 'none';
- } elseif ( $action == 'state' ) {
- if ( !empty($_REQUEST['runState']) ) {
- //if ( $cookies ) session_write_close();
- packageControl( $_REQUEST['runState'] );
- $refreshParent = true;
- }
- } elseif ( $action == 'save' ) {
- if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) {
- $sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id';
- $definitions = array();
- foreach( dbFetchAll( $sql ) as $monitor )
- {
- $definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled'];
- }
- $definition = join( ',', $definitions );
- if ( $_REQUEST['newState'] )
- $_REQUEST['runState'] = $_REQUEST['newState'];
- dbQuery( 'replace into States set Name=?, Definition=?', array( $_REQUEST['runState'],$definition) );
- }
- } elseif ( $action == 'delete' ) {
- if ( isset($_REQUEST['runState']) )
- dbQuery( 'delete from States where Name=?', array($_REQUEST['runState']) );
-
- if ( isset($_REQUEST['markUids']) ) {
- foreach( $_REQUEST['markUids'] as $markUid )
- dbQuery( 'delete from Users where Id = ?', array($markUid) );
- if ( $markUid == $user['Id'] )
- userLogout();
- }
- }
-} else {
- if ( ZM_USER_SELF_EDIT && $action == 'user' ) {
- $uid = $user['Id'];
-
- $dbUser = dbFetchOne( 'SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid) );
-
- $types = array();
- $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types );
-
- if ( !empty($_REQUEST['newUser']['Password']) )
- $changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')';
- else
- unset( $changes['Password'] );
- if ( count( $changes ) ) {
- dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) );
- $refreshParent = true;
- }
- $view = 'none';
- }
-}
-
-if ( $action == 'reset' ) {
- session_start();
- $_SESSION['zmEventResetTime'] = strftime( STRF_FMT_DATETIME_DB );
- setcookie( 'zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10 );
- session_write_close();
-}
-
-?>
diff --git a/web/includes/actions/bandwidth.php b/web/includes/actions/bandwidth.php
new file mode 100644
index 000000000..c5fafa1c9
--- /dev/null
+++ b/web/includes/actions/bandwidth.php
@@ -0,0 +1,29 @@
+
diff --git a/web/includes/actions/console.php b/web/includes/actions/console.php
new file mode 100644
index 000000000..6e0dfbb6c
--- /dev/null
+++ b/web/includes/actions/console.php
@@ -0,0 +1,40 @@
+delete();
+ } // end if monitor found in db
+ } // end if canedit this monitor
+ } // end foreach monitor in MarkMid
+ } // markMids is set and we aren't limited to specific monitors
+} // end if action == Delete
+?>
diff --git a/web/includes/actions/control.php b/web/includes/actions/control.php
new file mode 100644
index 000000000..5c996e636
--- /dev/null
+++ b/web/includes/actions/control.php
@@ -0,0 +1,41 @@
+sendControlCommand($ctrlCommand);
+ $view = 'none';
+}
+?>
diff --git a/web/includes/actions/controlcap.php b/web/includes/actions/controlcap.php
new file mode 100644
index 000000000..ad4985f11
--- /dev/null
+++ b/web/includes/actions/controlcap.php
@@ -0,0 +1,95 @@
+ 0,
+ 'CanSleep' => 0,
+ 'CanReset' => 0,
+ 'CanReboot' => 0,
+ 'CanMove' => 0,
+ 'CanMoveDiag' => 0,
+ 'CanMoveMap' => 0,
+ 'CanMoveRel' => 0,
+ 'CanMoveAbs' => 0,
+ 'CanMoveCon' => 0,
+ 'CanPan' => 0,
+ 'HasPanSpeed' => 0,
+ 'HasTurboPan' => 0,
+ 'CanTilt' => 0,
+ 'HasTiltSpeed' => 0,
+ 'HasTurboTilt' => 0,
+ 'CanZoom' => 0,
+ 'CanZoomRel' => 0,
+ 'CanZoomAbs' => 0,
+ 'CanZoomCon' => 0,
+ 'HasZoomSpeed' => 0,
+ 'CanFocus' => 0,
+ 'CanAutoFocus' => 0,
+ 'CanFocusRel' => 0,
+ 'CanFocusAbs' => 0,
+ 'CanFocusCon' => 0,
+ 'HasFocusSpeed' => 0,
+ 'CanGain' => 0,
+ 'CanAutoGain' => 0,
+ 'CanGainRel' => 0,
+ 'CanGainAbs' => 0,
+ 'CanGainCon' => 0,
+ 'HasGainSpeed' => 0,
+ 'CanWhite' => 0,
+ 'CanAutoWhite' => 0,
+ 'CanWhiteRel' => 0,
+ 'CanWhiteAbs' => 0,
+ 'CanWhiteCon' => 0,
+ 'HasWhiteSpeed' => 0,
+ 'CanIris' => 0,
+ 'CanAutoIris' => 0,
+ 'CanIrisRel' => 0,
+ 'CanIrisAbs' => 0,
+ 'CanIrisCon' => 0,
+ 'HasIrisSpeed' => 0,
+ 'HasPresets' => 0,
+ 'HasHomePreset' => 0,
+ 'CanSetPresets' => 0,
+ );
+
+ # Checkboxes don't return an element in the POST data, so won't be present in newControl.
+ # So force a value for these fields
+ foreach ( $field_defaults as $field => $value ) {
+ if ( ! (isset($_REQUEST['newControl'][$field]) and $_REQUEST['newControl'][$field]) ) {
+ $_REQUEST['newControl'][$field] = $value;
+ }
+ } # end foreach type
+
+ //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
+ $Control->save($_REQUEST['newControl']);
+ $refreshParent = true;
+ $view = 'none';
+} // end if action
+?>
diff --git a/web/includes/actions/controlcaps.php b/web/includes/actions/controlcaps.php
new file mode 100644
index 000000000..3e5310d19
--- /dev/null
+++ b/web/includes/actions/controlcaps.php
@@ -0,0 +1,36 @@
+
diff --git a/web/includes/actions/device.php b/web/includes/actions/device.php
new file mode 100644
index 000000000..c1a882d89
--- /dev/null
+++ b/web/includes/actions/device.php
@@ -0,0 +1,45 @@
+
diff --git a/web/includes/actions/devices.php b/web/includes/actions/devices.php
new file mode 100644
index 000000000..01c562370
--- /dev/null
+++ b/web/includes/actions/devices.php
@@ -0,0 +1,35 @@
+
diff --git a/web/includes/actions/donate.php b/web/includes/actions/donate.php
new file mode 100644
index 000000000..b8659b269
--- /dev/null
+++ b/web/includes/actions/donate.php
@@ -0,0 +1,54 @@
+
diff --git a/web/includes/actions/event.php b/web/includes/actions/event.php
new file mode 100644
index 000000000..a3c6d56c3
--- /dev/null
+++ b/web/includes/actions/event.php
@@ -0,0 +1,51 @@
+
diff --git a/web/includes/actions/eventdetail.php b/web/includes/actions/eventdetail.php
new file mode 100644
index 000000000..20bd084ee
--- /dev/null
+++ b/web/includes/actions/eventdetail.php
@@ -0,0 +1,47 @@
+beginTransaction();
+ foreach ( $_REQUEST['markEids'] as $markEid ) {
+ dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?',
+ array(
+ $_REQUEST['newEvent']['Cause'],
+ $_REQUEST['newEvent']['Notes'],
+ $markEid
+ )
+ );
+ }
+ $dbConn->commit();
+ $refreshParent = true;
+ $closePopup = true;
+}
+?>
diff --git a/web/includes/actions/events.php b/web/includes/actions/events.php
new file mode 100644
index 000000000..abea69b60
--- /dev/null
+++ b/web/includes/actions/events.php
@@ -0,0 +1,52 @@
+beginTransaction();
+ foreach( getAffectedIds('eids') as $markEid ) {
+ dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(1, $markEid));
+ }
+ $dbConn->commit();
+ $refreshParent = true;
+} else if ( $action == 'unarchive' ) {
+ $dbConn->beginTransaction();
+ foreach( getAffectedIds('eids') as $markEid ) {
+ dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(0, $markEid));
+ }
+ $dbConn->commit();
+ $refreshParent = true;
+} else if ( $action == 'delete' ) {
+ foreach ( getAffectedIds('eids') as $markEid ) {
+ deleteEvent($markEid);
+ }
+ $refreshParent = true;
+}
+?>
diff --git a/web/includes/actions/filter.php b/web/includes/actions/filter.php
new file mode 100644
index 000000000..ac2435302
--- /dev/null
+++ b/web/includes/actions/filter.php
@@ -0,0 +1,112 @@
+Background() ) {
+ $filter->control('stop');
+ }
+ $filter->delete();
+
+ } else {
+ ZM\Error('No filter id passed when deleting');
+ }
+ } else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) {
+
+ $sql = '';
+ $_REQUEST['filter']['Query']['sort_field'] = validStr($_REQUEST['filter']['Query']['sort_field']);
+ $_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']);
+ $_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']);
+
+ $_REQUEST['filter']['AutoCopy'] = empty($_REQUEST['filter']['AutoCopy']) ? 0 : 1;
+ $_REQUEST['filter']['AutoMove'] = empty($_REQUEST['filter']['AutoMove']) ? 0 : 1;
+ $_REQUEST['filter']['AutoArchive'] = empty($_REQUEST['filter']['AutoArchive']) ? 0 : 1;
+ $_REQUEST['filter']['AutoVideo'] = empty($_REQUEST['filter']['AutoVideo']) ? 0 : 1;
+ $_REQUEST['filter']['AutoUpload'] = empty($_REQUEST['filter']['AutoUpload']) ? 0 : 1;
+ $_REQUEST['filter']['AutoEmail'] = empty($_REQUEST['filter']['AutoEmail']) ? 0 : 1;
+ $_REQUEST['filter']['AutoMessage'] = empty($_REQUEST['filter']['AutoMessage']) ? 0 : 1;
+ $_REQUEST['filter']['AutoExecute'] = empty($_REQUEST['filter']['AutoExecute']) ? 0 : 1;
+ $_REQUEST['filter']['AutoDelete'] = empty($_REQUEST['filter']['AutoDelete']) ? 0 : 1;
+ $_REQUEST['filter']['UpdateDiskSpace'] = empty($_REQUEST['filter']['UpdateDiskSpace']) ? 0 : 1;
+ $_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1;
+ $_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1;
+ $changes = $filter->changes($_REQUEST['filter']);
+ ZM\Logger::Debug('Changes: ' . print_r($changes,true));
+
+ if ( $_REQUEST['Id'] and ( $action == 'Save' ) ) {
+ if ( $filter->Background() )
+ $filter->control('stop');
+ $filter->save($changes);
+ } else {
+
+ if ( $action == 'execute' ) {
+ if ( count($changes) ) {
+ $filter->Name('_TempFilter'.time());
+ $filter->Id(null);
+ }
+ } else if ( $action == 'SaveAs' ) {
+ $filter->Id(null);
+ }
+ $filter->save($changes);
+
+ // We update the request id so that the newly saved filter is auto-selected
+ $_REQUEST['Id'] = $filter->Id();
+ }
+ if ( $filter->Background() )
+ $filter->control('start');
+
+ if ( $action == 'execute' ) {
+ $filter->execute();
+ if ( count($changes) )
+ $filter->delete();
+
+ $view = 'events';
+ }
+
+ } else if ( $action == 'control' ) {
+ if ( $_REQUEST['command'] == 'start'
+ or $_REQUEST['command'] == 'stop'
+ or $_REQUEST['command'] == 'restart'
+ ) {
+ $filter->control($_REQUEST['command'], $_REQUEST['ServerId']);
+ } else {
+ ZM\Error('Invalid command for filter ('.$_REQUEST['command'].')');
+ }
+ } // end if save or execute
+ } // end if canEdit(Events)
+} // end if object == filter
+
+?>
diff --git a/web/includes/actions/function.php b/web/includes/actions/function.php
new file mode 100644
index 000000000..00e4e21f7
--- /dev/null
+++ b/web/includes/actions/function.php
@@ -0,0 +1,60 @@
+
diff --git a/web/includes/actions/group.php b/web/includes/actions/group.php
new file mode 100644
index 000000000..9aa988540
--- /dev/null
+++ b/web/includes/actions/group.php
@@ -0,0 +1,62 @@
+
diff --git a/web/includes/actions/groups.php b/web/includes/actions/groups.php
new file mode 100644
index 000000000..8e4522187
--- /dev/null
+++ b/web/includes/actions/groups.php
@@ -0,0 +1,49 @@
+$_REQUEST['gid'])) as $Group ) {
+ $Group->delete();
+ }
+ }
+ $redirect = '?view=groups';
+ $refreshParent = true;
+} # end if action
+?>
diff --git a/web/includes/actions/login.php b/web/includes/actions/login.php
new file mode 100644
index 000000000..68ca4e604
--- /dev/null
+++ b/web/includes/actions/login.php
@@ -0,0 +1,110 @@
+ ZM_OPT_GOOG_RECAPTCHA_SECRETKEY,
+ 'response' => $_REQUEST['g-recaptcha-response'],
+ 'remoteip' => $_SERVER['REMOTE_ADDR']
+ );
+ $res = do_post_request($url, http_build_query($fields));
+ $responseData = json_decode($res, true);
+ // credit: https://github.com/google/recaptcha/blob/master/src/ReCaptcha/Response.php
+ // if recaptcha resulted in error, we might have to deny login
+ if ( isset($responseData['success']) && ($responseData['success'] == false) ) {
+ // PP - before we deny auth, let's make sure the error was not 'invalid secret'
+ // because that means the user did not configure the secret key correctly
+ // in this case, we prefer to let him login in and display a message to correct
+ // the key. Unfortunately, there is no way to check for invalid site key in code
+ // as it produces the same error as when you don't answer a recaptcha
+ if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) {
+ if ( !in_array('invalid-input-secret', $responseData['error-codes']) ) {
+ ZM\Error('reCaptcha authentication failed. response was: ' . print_r($responseData['error-codes'],true));
+ unset($user); // unset should be ok here because we aren't in a function
+ return;
+ } else {
+ ZM\Error('Invalid recaptcha secret detected');
+ }
+ }
+ } // end if success==false
+ if ( ! (empty($_REQUEST['username']) or empty($_REQUEST['password'])) ) {
+ $ret = validateUser($_REQUEST['username'], $_REQUEST['password']);
+ if ( !$ret[0] ) {
+ ZM\Error($ret[1]);
+ unset($user); // unset should be ok here because we aren't in a function
+ } else {
+ $user = $ret[0];
+ }
+ } # end if have username and password
+ } // end if using reCaptcha
+
+ // if captcha existed, it was passed
+
+ if ( ! isset($user) ) {
+ $_SESSION['loginFailed'] = true;
+ return;
+ }
+
+ $close_session = 0;
+ if ( !is_session_started() ) {
+ zm_session_start();
+ $close_session = 1;
+ }
+
+ $username = $_REQUEST['username'];
+ $password = $_REQUEST['password'];
+
+ ZM\Info("Login successful for user \"$username\"");
+ $password_type = password_type($password);
+
+ if ( $password_type == 'mysql' or $password_type == 'mysql+bcrypt' ) {
+ ZM\Info('Migrating password, if possible for future logins');
+ migrateHash($username, $password);
+ }
+ unset($_SESSION['loginFailed']);
+ if ( ZM_AUTH_TYPE == 'builtin' ) {
+ $_SESSION['passwordHash'] = $user['Password'];
+ }
+ $_SESSION['username'] = $user['Username'];
+ if ( ZM_AUTH_RELAY == 'plain' ) {
+ // Need to save this in session, can't use the value in User because it is hashed
+ $_SESSION['password'] = $_REQUEST['password'];
+ }
+ zm_session_regenerate_id();
+ generateAuthHash(ZM_AUTH_HASH_IPS, true);
+ if ( $close_session )
+ session_write_close();
+
+ $view = 'postlogin';
+} # end if doing a login action
+?>
diff --git a/web/includes/actions/logout.php b/web/includes/actions/logout.php
new file mode 100644
index 000000000..aecdb7683
--- /dev/null
+++ b/web/includes/actions/logout.php
@@ -0,0 +1,29 @@
+
diff --git a/web/includes/actions/monitor.php b/web/includes/actions/monitor.php
new file mode 100644
index 000000000..f1223a6f9
--- /dev/null
+++ b/web/includes/actions/monitor.php
@@ -0,0 +1,279 @@
+ array(),
+ 'Controllable' => 0,
+ 'TrackMotion' => 0,
+ 'Enabled' => 0,
+ 'Exif' => 0,
+ 'RTSPDescribe' => 0,
+ 'RecordAudio' => 0,
+ 'Method' => 'raw',
+ 'GroupIds' => array(),
+ 'LinkedMonitors' => array()
+ );
+
+ # Checkboxes don't return an element in the POST data, so won't be present in newMonitor.
+ # So force a value for these fields
+ foreach ( $types as $field => $value ) {
+ if ( ! isset($_REQUEST['newMonitor'][$field] ) ) {
+ $_REQUEST['newMonitor'][$field] = $value;
+ }
+ } # end foreach type
+
+ if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) {
+ $_REQUEST['newMonitor']['ServerId'] = dbFetchOne(
+ 'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id');
+ ZM\Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId']);
+ if ( ( !$_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) {
+ $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID;
+ ZM\Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID);
+ }
+ }
+
+ $changes = $monitor->changes($_REQUEST['newMonitor'], $types);
+ $restart = false;
+
+ if ( count($changes) ) {
+ if ( $monitor->Id() ) {
+
+ # If we change anything that changes the shared mem size, zma can complain. So let's stop first.
+ if ( $monitor->Type() != 'WebSite' ) {
+ $monitor->zmaControl('stop');
+ $monitor->zmcControl('stop');
+ }
+
+ # These are used in updating zones
+ $oldW = $monitor->Width();
+ $oldH = $monitor->Height();
+
+ if ( $monitor->save($changes) ) {
+
+ // Groups will be added below
+ if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
+ // creating symlinks when symlink already exists reports errors, but is perfectly ok
+ error_reporting(0);
+
+ $OldStorage = $monitor->Storage();
+ $saferOldName = basename($monitor->Name());
+ if ( file_exists($OldStorage->Path().'/'.$saferOldName) )
+ unlink($OldStorage->Path().'/'.$saferOldName);
+
+ $NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']);
+ if ( !file_exists($NewStorage->Path().'/'.$mid) ) {
+ if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) {
+ ZM\Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid);
+ }
+ }
+ $saferNewName = basename($_REQUEST['newMonitor']['Name']);
+ $link_path = $NewStorage->Path().'/'.$saferNewName;
+ if ( !symlink($NewStorage->Path().'/'.$mid, $link_path) ) {
+ if ( ! ( file_exists($link_path) and is_link($link_path) ) ) {
+ ZM\Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName);
+ }
+ }
+ } // end if Name or Storage Area Change
+
+ if ( isset($changes['Width']) || isset($changes['Height']) ) {
+ $newW = $_REQUEST['newMonitor']['Width'];
+ $newH = $_REQUEST['newMonitor']['Height'];
+
+ $zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid));
+
+ if ( ($newW == $oldH) and ($newH == $oldW) ) {
+ foreach ( $zones as $zone ) {
+ $newZone = $zone;
+ # Rotation, no change to area etc just swap the coords
+ $newZone = $zone;
+ $points = coordsToPoints($zone['Coords']);
+ for ( $i = 0; $i < count($points); $i++ ) {
+ $x = $points[$i]['x'];
+ $points[$i]['x'] = $points[$i]['y'];
+ $points[$i]['y'] = $x;
+
+ if ( $points[$i]['x'] > ($newW-1) ) {
+ ZM\Warning("Correcting x {$points[$i]['x']} > $newW of zone {$newZone['Name']} as it extends outside the new dimensions");
+ $points[$i]['x'] = ($newW-1);
+ }
+ if ( $points[$i]['y'] > ($newH-1) ) {
+ ZM\Warning("Correcting y {$points[$i]['y']} $newH of zone {$newZone['Name']} as it extends outside the new dimensions");
+ $points[$i]['y'] = ($newH-1);
+ }
+ }
+
+ $newZone['Coords'] = pointsToCoords($points);
+ $changes = getFormChanges($zone, $newZone, $types);
+
+ if ( count($changes) ) {
+ dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?',
+ array($mid, $zone['Id']));
+ }
+ } # end foreach zone
+ } else {
+ $newA = $newW * $newH;
+ $oldA = $oldW * $oldH;
+
+ foreach ( $zones as $zone ) {
+ $newZone = $zone;
+ $points = coordsToPoints($zone['Coords']);
+ for ( $i = 0; $i < count($points); $i++ ) {
+ $points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1));
+ $points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1));
+ if ( $points[$i]['x'] > ($newW-1) ) {
+ ZM\Warning("Correcting x of zone {$newZone['Name']} as it extends outside the new dimensions");
+ $points[$i]['x'] = ($newW-1);
+ }
+ if ( $points[$i]['y'] > ($newH-1) ) {
+ ZM\Warning("Correcting y of zone {$newZone['Name']} as it extends outside the new dimensions");
+ $points[$i]['y'] = ($newH-1);
+ }
+ }
+ $newZone['Coords'] = pointsToCoords($points);
+ $newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA));
+ $newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA));
+ $newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA));
+ $newZone['MinFilterPixels'] = intval(round(($newZone['MinFilterPixels']*$newA)/$oldA));
+ $newZone['MaxFilterPixels'] = intval(round(($newZone['MaxFilterPixels']*$newA)/$oldA));
+ $newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA));
+ $newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA));
+
+ $changes = getFormChanges($zone, $newZone, $types);
+
+ if ( count($changes) ) {
+ dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?',
+ array($mid, $zone['Id']));
+ }
+ } // end foreach zone
+ } // end if rotation or just size change
+ } // end if changes in width or height
+ } // end if successful save
+ $restart = true;
+ } else { // new monitor
+ // Can only create new monitors if we are not restricted to specific monitors
+# FIXME This is actually a race condition. Should lock the table.
+ $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence');
+ $changes['Sequence'] = $maxSeq+1;
+
+ if ( $monitor->save($changes) ) {
+ $mid = $monitor->Id();
+ $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height'];
+ dbQuery("INSERT INTO Zones SET MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) );
+ //$view = 'none';
+ $Storage = $monitor->Storage();
+
+ error_reporting(0);
+ mkdir($Storage->Path().'/'.$mid, 0755);
+ $saferName = basename($_REQUEST['newMonitor']['Name']);
+ symlink($mid, $Storage->Path().'/'.$saferName);
+
+ } else {
+ ZM\Error('Error saving new Monitor.');
+ return;
+ }
+ }
+
+ if ( isset($changes['GroupIds']) ) {
+ dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', array($mid));
+ foreach ( $changes['GroupIds'] as $group_id ) {
+ dbQuery('INSERT INTO Groups_Monitors (GroupId, MonitorId) VALUES (?,?)', array($group_id, $mid));
+ }
+ } // end if there has been a change of groups
+
+ $restart = true;
+ } else {
+ ZM\Logger::Debug('No action due to no changes to Monitor');
+ } # end if count(changes)
+
+ if ( !$mid ) {
+ ZM\Error("We should have a mid by now. Something went wrong.");
+ return;
+ }
+
+ if ( ZM_OPT_X10 ) {
+ $x10Changes = getFormChanges($x10Monitor, $_REQUEST['newX10Monitor']);
+
+ if ( count($x10Changes) ) {
+ if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) {
+ dbQuery('UPDATE TriggersX10 SET '.implode(', ', $x10Changes).' WHERE MonitorId=?', array($mid));
+ } elseif ( !$user['MonitorIds'] ) {
+ if ( !$x10Monitor ) {
+ dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid));
+ } else {
+ dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid));
+ }
+ }
+ $restart = true;
+ } # end if has x10Changes
+ } # end if ZM_OPT_X10
+
+ if ( $restart ) {
+ if ( $monitor->Function() != 'None' and $monitor->Type() != 'WebSite' ) {
+ $monitor->zmcControl('start');
+ if ( ($monitor->Function() == 'Modect' or $monitor->Function() == 'Mocord') and $monitor->Enabled() )
+ $monitor->zmaControl('start');
+
+ if ( $monitor->Controllable() ) {
+ require_once('includes/control_functions.php');
+ $monitor->sendControlCommand('quit');
+ }
+ }
+ // really should thump zmwatch and maybe zmtrigger too.
+ //daemonControl( 'restart', 'zmwatch.pl' );
+ $refreshParent = true;
+ } // end if restart
+ $view = 'none';
+} else {
+ ZM\Warning("Unknown action $action in Monitor");
+} // end if action == Delete
+?>
diff --git a/web/includes/actions/monitors.php b/web/includes/actions/monitors.php
new file mode 100644
index 000000000..8f56b0e3d
--- /dev/null
+++ b/web/includes/actions/monitors.php
@@ -0,0 +1,52 @@
+Type() != 'WebSite' ) {
+ $Monitor->zmaControl('stop');
+ $Monitor->zmcControl('stop');
+ }
+ $Monitor->save($_REQUEST['newMonitor']);
+ if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
+ $Monitor->zmcControl('start');
+ if ( $Monitor->Enabled() ) {
+ $Monitor->zmaControl('start');
+ }
+ }
+ } // end foreach mid
+ $refreshParent = true;
+ $view = 'none';
+} else {
+ ZM\Warning("Unknown action $action in Monitor");
+} // end if action == Delete
+?>
diff --git a/web/includes/actions/montage.php b/web/includes/actions/montage.php
new file mode 100644
index 000000000..207c9a0f0
--- /dev/null
+++ b/web/includes/actions/montage.php
@@ -0,0 +1,48 @@
+Name($_REQUEST['Name']);
+ } else {
+ $Layout = new ZM\MontageLayout($_REQUEST['zmMontageLayout']);
+ }
+ $Layout->Positions($_REQUEST['Positions']);
+ $Layout->save();
+ session_start();
+ $_SESSION['zmMontageLayout'] = $Layout->Id();
+ setcookie('zmMontageLayout', $Layout->Id(), 1);
+ session_write_close();
+ $redirect = '?view=montage';
+ } // end if save
+
+ } # end if isset($_REQUEST['object'] )
+} # end if isset($_REQUEST['object'] )
+?>
diff --git a/web/includes/actions/options.php b/web/includes/actions/options.php
new file mode 100644
index 000000000..9f1655632
--- /dev/null
+++ b/web/includes/actions/options.php
@@ -0,0 +1,102 @@
+
diff --git a/web/includes/actions/privacy.php b/web/includes/actions/privacy.php
new file mode 100644
index 000000000..5877bb60d
--- /dev/null
+++ b/web/includes/actions/privacy.php
@@ -0,0 +1,42 @@
+
diff --git a/web/includes/actions/server.php b/web/includes/actions/server.php
new file mode 100644
index 000000000..1ffe4933d
--- /dev/null
+++ b/web/includes/actions/server.php
@@ -0,0 +1,53 @@
+
diff --git a/web/includes/actions/settings.php b/web/includes/actions/settings.php
new file mode 100644
index 000000000..c33f3a54c
--- /dev/null
+++ b/web/includes/actions/settings.php
@@ -0,0 +1,49 @@
+
diff --git a/web/includes/actions/shutdown.php b/web/includes/actions/shutdown.php
new file mode 100644
index 000000000..4d7a4ee78
--- /dev/null
+++ b/web/includes/actions/shutdown.php
@@ -0,0 +1,44 @@
+&1", $output, $rc);
+ #exec('sudo -n /bin/systemctl poweroff -i 2>&1', $output, $rc);
+ ZM\Logger::Debug("Shutdown output $rc " . implode("\n",$output));
+ #ZM\Logger::Debug("Shutdown output " . shell_exec('/bin/systemctl poweroff -i 2>&1'));
+ } else if ( $action == 'restart' ) {
+ $output = array();
+ exec('sudo -n '.ZM_PATH_SHUTDOWN." -r $when 2>&1", $output);
+ #exec('sudo -n /bin/systemctl reboot -i 2>&1', $output);
+ ZM\Logger::Debug("Shutdown output " . implode("\n",$output));
+ } else if ( $action == 'cancel' ) {
+ $output = array();
+ exec('sudo '.ZM_PATH_SHUTDOWN.' -c 2>&1', $output);
+ }
+} # end if action
+?>
diff --git a/web/includes/actions/state.php b/web/includes/actions/state.php
new file mode 100644
index 000000000..0f7a9e9a5
--- /dev/null
+++ b/web/includes/actions/state.php
@@ -0,0 +1,49 @@
+
diff --git a/web/includes/actions/storage.php b/web/includes/actions/storage.php
new file mode 100644
index 000000000..f60c8227d
--- /dev/null
+++ b/web/includes/actions/storage.php
@@ -0,0 +1,49 @@
+
diff --git a/web/includes/actions/user.php b/web/includes/actions/user.php
new file mode 100644
index 000000000..bdbafd176
--- /dev/null
+++ b/web/includes/actions/user.php
@@ -0,0 +1,84 @@
+
diff --git a/web/includes/actions/version.php b/web/includes/actions/version.php
new file mode 100644
index 000000000..fde85427f
--- /dev/null
+++ b/web/includes/actions/version.php
@@ -0,0 +1,52 @@
+ 0
+ $types = array(
+ 'OverloadFrames' => 'integer',
+ 'ExtendAlarmFrames' => 'integer',
+ );
+
+ $changes = getFormChanges($zone, $_REQUEST['newZone'], $types);
+
+ if ( count($changes) ) {
+ if ( $zid > 0 ) {
+ dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', array($mid, $zid));
+ } else {
+ dbQuery('INSERT INTO Zones SET MonitorId=?, '.implode(', ', $changes), array($mid));
+ }
+ if ( daemonCheck() && ($monitor->Type() != 'WebSite') ) {
+ if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) {
+ $monitor->zmaControl('stop');
+ $monitor->zmcControl('restart');
+ $monitor->zmaControl('start');
+ } else {
+ $monitor->zmaControl('restart');
+ }
+ }
+ if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor->Controllable() ) {
+ $monitor->sendControlCommand('quit');
+ }
+ $refreshParent = true;
+ } // end if changes
+ $view = 'none';
+ } // end if action
+} // end if $mid and canEdit($mid)
+?>
diff --git a/web/includes/actions/zones.php b/web/includes/actions/zones.php
new file mode 100644
index 000000000..babb4fa7b
--- /dev/null
+++ b/web/includes/actions/zones.php
@@ -0,0 +1,49 @@
+Type() != 'WebSite' ) {
+ zmaControl($mid, 'stop');
+ if ( $restart_zmc )
+ zmcControl($mid, 'restart');
+ zmaControl($mid, 'start');
+ } // end if daemonCheck()
+ $refreshParent = true;
+ } // end if isset($_REQUEST['markZids'])
+ } // end if action
+} // end if $mid and canEdit($mid)
+
+?>
diff --git a/web/includes/auth.php b/web/includes/auth.php
index b14323103..f958463c3 100644
--- a/web/includes/auth.php
+++ b/web/includes/auth.php
@@ -2,137 +2,217 @@
//
// ZoneMinder auth library, $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.
-//
+//
+//
+require_once('session.php');
+require_once(__DIR__.'/../vendor/autoload.php');
+use \Firebase\JWT\JWT;
-function userLogin($username, $password='', $passwordHashed=false) {
- global $user, $cookies;
+function password_type($password) {
+ if ( $password[0] == '*' ) {
+ return 'mysql';
+ } else if ( preg_match('/^\$2[ayb]\$.+$/', $password) ) {
+ return 'bcrypt';
+ } else if ( substr($password, 0,4) == '-ZM-' ) {
+ // zmupdate.pl adds a '-ZM-' prefix to overlay encrypted passwords
+ // this is done so that we don't spend cycles doing two bcrypt password_verify calls
+ // for every wrong password entered. This will only be invoked for passwords zmupdate.pl has
+ // overlay hashed
+ return 'mysql+bcrypt';
+ }
+ return 'plain';
+}
- $sql = 'SELECT * FROM Users WHERE Enabled=1';
- $sql_values = NULL;
- if ( ZM_AUTH_TYPE == 'builtin' ) {
- if ( $passwordHashed ) {
- $sql .= ' AND Username=? AND Password=?';
- } else {
- $sql .= ' AND Username=? AND Password=password(?)';
- }
- $sql_values = array($username, $password);
+// this function migrates mysql hashing to bcrypt, if you are using PHP >= 5.5
+// will be called after successful login, only if mysql hashing is detected
+function migrateHash($user, $pass) {
+ if ( function_exists('password_hash') ) {
+ ZM\Info("Migrating $user to bcrypt scheme");
+ // let it generate its own salt, and ensure bcrypt as PASSWORD_DEFAULT may change later
+ // we can modify this later to support argon2 etc as switch to its own password signature detection
+ $bcrypt_hash = password_hash($pass, PASSWORD_BCRYPT);
+ //ZM\Info ("hased bcrypt $pass is $bcrypt_hash");
+ $update_password_sql = 'UPDATE Users SET Password=\''.$bcrypt_hash.'\' WHERE Username=\''.$user.'\'';
+ dbQuery($update_password_sql);
+ # Since password field has changed, existing auth_hash is no longer valid
+ generateAuthHash(ZM_AUTH_HASH_IPS, true);
} else {
- $sql .= ' AND Username=?';
- $sql_values = array($username);
+ ZM\Info('Cannot migrate password scheme to bcrypt, as you are using PHP < 5.3');
+ return;
}
- session_start();
- $_SESSION['username'] = $username;
- if ( ZM_AUTH_RELAY == 'plain' ) {
- // Need to save this in session
- $_SESSION['password'] = $password;
+}
+
+// core function used to load a User record by username and password
+function validateUser($username='', $password='') {
+
+ $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
+ // local user, shouldn't affect the global user
+ $user = dbFetchOne($sql, NULL, array($username));
+
+ if ( ! $user ) {
+ return array(false, "Could not retrieve user $username details");
}
- $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking
- if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) {
- Info("Login successful for user \"$username\"");
- $_SESSION['user'] = $user = $dbUser;
- unset($_SESSION['loginFailed']);
- if ( ZM_AUTH_TYPE == 'builtin' ) {
- $_SESSION['passwordHash'] = $user['Password'];
- }
- session_regenerate_id();
- } else {
- Warning("Login denied for user \"$username\"");
- $_SESSION['loginFailed'] = true;
- unset($user);
+
+ switch ( password_type($user['Password']) ) {
+ case 'mysql' :
+ // We assume we don't need to support mysql < 4.1
+ // Starting MY SQL 4.1, mysql concats a '*' in front of its password hash
+ // https://blog.pythian.com/hashing-algorithm-in-mysql-password-2/
+ ZM\Logger::Debug('Saved password is using MYSQL password function');
+ $input_password_hash = '*'.strtoupper(sha1(sha1($password, true)));
+ $password_correct = ($user['Password'] == $input_password_hash);
+ break;
+ case 'bcrypt' :
+ ZM\Logger::Debug('bcrypt signature found, assumed bcrypt password');
+ $password_correct = password_verify($password, $user['Password']);
+ break;
+ case 'mysql+bcrypt' :
+ // zmupdate.pl adds a '-ZM-' prefix to overlay encrypted passwords
+ // this is done so that we don't spend cycles doing two bcrypt password_verify calls
+ // for every wrong password entered. This will only be invoked for passwords zmupdate.pl has
+ // overlay hashed
+ ZM\Logger::Debug("Detected bcrypt overlay hashing for $username");
+ $bcrypt_hash = substr($user['Password'], 4);
+ $mysql_encoded_password = '*'.strtoupper(sha1(sha1($password, true)));
+ ZM\Logger::Debug("Comparing password $mysql_encoded_password to bcrypt hash: $bcrypt_hash");
+ $password_correct = password_verify($mysql_encoded_password, $bcrypt_hash);
+ break;
+ default:
+ // we really should nag the user not to use plain
+ ZM\Warning('assuming plain text password as signature is not known. Please do not use plain, it is very insecure');
+ $password_correct = ($user['Password'] == $password);
+ } // switch password_type
+
+ if ( $password_correct ) {
+ return array($user, 'OK');
}
- session_write_close();
-} # end function userLogin
+ return array(false, "Login denied for user \"$username\"");
+} # end function validateUser
function userLogout() {
global $user;
- Info('User "'.$user['Username'].'" logged out');
- session_start();
- unset($_SESSION['user']);
- unset($user);
-
- session_destroy();
+ ZM\Info('User "'.$user['Username'].'" logged out');
+ $user = null;// unset only clears the local variable
+ zm_session_clear();
}
+function validateToken($token, $allowed_token_type='access') {
+ global $user;
+ $key = ZM_AUTH_HASH_SECRET;
+ //if (ZM_AUTH_HASH_IPS) $key .= $_SERVER['REMOTE_ADDR'];
+ try {
+ $decoded_token = JWT::decode($token, $key, array('HS256'));
+ } catch (Exception $e) {
+ ZM\Error("Unable to authenticate user. error decoding JWT token:".$e->getMessage());
+ return array(false, $e->getMessage());
+ }
+
+ // convert from stdclass to array
+ $jwt_payload = json_decode(json_encode($decoded_token), true);
+
+ $type = $jwt_payload['type'];
+ if ( $type != $allowed_token_type ) {
+ ZM\Error("Token type mismatch. Expected $allowed_token_type but got $type");
+ return array(false, 'Incorrect token type');
+ }
+ $username = $jwt_payload['user'];
+ $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
+ $saved_user_details = dbFetchOne($sql, NULL, array($username));
+
+ if ( $saved_user_details ) {
+ $issuedAt = $jwt_payload['iat'];
+ $minIssuedAt = $saved_user_details['TokenMinExpiry'];
+
+ if ( $issuedAt < $minIssuedAt ) {
+ ZM\Error("Token revoked for $username. Please generate a new token");
+ $user = null;// unset only clears the local variable
+ return array(false, 'Token revoked. Please re-generate');
+ }
+ $user = $saved_user_details;
+ return array($user, 'OK');
+ }
+ ZM\Error("Could not retrieve user $username details");
+ $user = null;// unset only clears the local variable
+ return array(false, 'No such user/credentials');
+} // end function validateToken($token, $allowed_token_type='access')
+
function getAuthUser($auth) {
- if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' && !empty($auth) ) {
+ if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') && !empty($auth) ) {
$remoteAddr = '';
if ( ZM_AUTH_HASH_IPS ) {
$remoteAddr = $_SERVER['REMOTE_ADDR'];
if ( !$remoteAddr ) {
- Error("Can't determine remote address for authentication, using empty string");
+ ZM\Error("Can't determine remote address for authentication, using empty string");
$remoteAddr = '';
}
}
+ $values = array();
if ( isset($_SESSION['username']) ) {
# Most of the time we will be logged in already and the session will have our username, so we can significantly speed up our hash testing by only looking at our user.
# Only really important if you have a lot of users.
- $sql = "SELECT * FROM Users WHERE Enabled = 1 AND Username='".$_SESSION['username']."'";
+ $sql = 'SELECT * FROM Users WHERE Enabled = 1 AND Username=?';
+ array_push($values, $_SESSION['username']);
} else {
$sql = 'SELECT * FROM Users WHERE Enabled = 1';
}
- foreach ( dbFetchAll($sql) as $user ) {
+ foreach ( dbFetchAll($sql, NULL, $values) as $user ) {
$now = time();
- for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= (3600) ) { // Try for last two hours
+ for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= 3600 ) { // Try for last TTL hours
$time = localtime($now);
$authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5];
$authHash = md5($authKey);
if ( $auth == $authHash ) {
return $user;
- }
+ } // end if $auth == $authHash
} // end foreach hour
} // end foreach user
} // end if using auth hash
- Error("Unable to authenticate user from auth hash '$auth'");
- return false;
+ ZM\Error("Unable to authenticate user from auth hash '$auth'");
+ return null;
} // end getAuthUser($auth)
function generateAuthHash($useRemoteAddr, $force=false) {
- if ( ZM_OPT_USE_AUTH and ZM_AUTH_RELAY == 'hashed' and isset($_SESSION['username']) and $_SESSION['passwordHash'] ) {
- # regenerate a hash at half the liftetime of a hash, an hour is 3600 so half is 1800
+ global $user;
+ if ( ZM_OPT_USE_AUTH and (ZM_AUTH_RELAY == 'hashed') and isset($user['Username']) and isset($user['Password']) ) {
$time = time();
+
+ # We use 1800 so that we regenerate the hash at half the TTL
$mintime = $time - ( ZM_AUTH_HASH_TTL * 1800 );
- if ( $force or ( !isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime ) ) {
+ # Appending the remoteAddr prevents us from using an auth hash generated for a different ip
+ if ( $force or ( !isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime ) ) {
# Don't both regenerating Auth Hash if an hour hasn't gone by yet
$local_time = localtime();
$authKey = '';
if ( $useRemoteAddr ) {
- $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5];
+ $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$_SESSION['remoteAddr'].$local_time[2].$local_time[3].$local_time[4].$local_time[5];
} else {
- $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$local_time[2].$local_time[3].$local_time[4].$local_time[5];
+ $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$local_time[2].$local_time[3].$local_time[4].$local_time[5];
}
- #Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] );
+ #ZM\Logger::Debug("Generated using hour:".$local_time[2] . ' mday:' . $local_time[3] . ' month:'.$local_time[4] . ' year: ' . $local_time[5] );
$auth = md5($authKey);
- if ( !$force ) {
- session_start();
- $_SESSION['AuthHash'] = $auth;
- $_SESSION['AuthHashGeneratedAt'] = $time;
- session_write_close();
- } else {
- return $auth;
- }
- #Logger::Debug("Generated new auth $auth at " . $_SESSION['AuthHashGeneratedAt']. " using $authKey" );
- #} else {
- #Logger::Debug("Using cached auth " . $_SESSION['AuthHash'] ." beacuse generatedat:" . $_SESSION['AuthHashGeneratedAt'] . ' < now:'. $time . ' - ' . ZM_AUTH_HASH_TTL . ' * 1800 = '. $mintime);
+ $_SESSION['AuthHash'.$_SESSION['remoteAddr']] = $auth;
+ $_SESSION['AuthHashGeneratedAt'] = $time;
+ # Because we don't write out the session, it shouldn't actually get written out to disk. However if it does, the GeneratedAt should protect us.
} # end if AuthHash is not cached
- return $_SESSION['AuthHash'];
+ return $_SESSION['AuthHash'.$_SESSION['remoteAddr']];
} # end if using AUTH and AUTH_RELAY
return '';
}
@@ -140,19 +220,74 @@ function generateAuthHash($useRemoteAddr, $force=false) {
function visibleMonitor($mid) {
global $user;
- return ( empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) );
+ return ( $user && empty($user['MonitorIds']) || in_array($mid, explode(',', $user['MonitorIds'])) );
}
function canView($area, $mid=false) {
global $user;
- return ( ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) );
+ return ( $user && ($user[$area] == 'View' || $user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ) );
}
function canEdit($area, $mid=false) {
global $user;
- return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) ));
+ return ( $user && ($user[$area] == 'Edit') && ( !$mid || visibleMonitor($mid) ));
}
+function userFromSession() {
+ $user = null; // Not global
+ if ( isset($_SESSION['username']) ) {
+ if ( ZM_AUTH_HASH_LOGINS and (ZM_AUTH_RELAY == 'hashed') ) {
+ # Extra validation, if logged in, then the auth hash will be set in the session, so we can validate it.
+ # This prevent session modification to switch users
+ if ( isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) )
+ $user = getAuthUser($_SESSION['AuthHash'.$_SESSION['remoteAddr']]);
+ else
+ ZM\Logger::Debug("No auth hash in session, there should have been");
+ } else {
+ # Need to refresh permissions and validate that the user still exists
+ $sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
+ $user = dbFetchOne($sql, NULL, array($_SESSION['username']));
+ }
+ } else {
+ ZM\Logger::Debug('No username in session');
+ }
+ return $user;
+}
+
+if ( ZM_OPT_USE_AUTH ) {
+ if ( !empty($_REQUEST['token']) ) {
+ $ret = validateToken($_REQUEST['token'], 'access');
+ $user = $ret[0];
+ } else {
+ // Non token based auth
+
+ $user = userFromSession();
+
+ if ( ZM_AUTH_HASH_LOGINS && empty($user) && !empty($_REQUEST['auth']) ) {
+ $user = getAuthUser($_REQUEST['auth']);
+ } else if (
+ ! (
+ empty($_REQUEST['username']) or
+ empty($_REQUEST['password']) or
+ (defined('ZM_OPT_USE_GOOG_RECAPTCHA') && ZM_OPT_USE_GOOG_RECAPTCHA)
+ ) ) {
+ $ret = validateUser($_REQUEST['username'], $_REQUEST['password']);
+ if ( !$ret[0] ) {
+ ZM\Error($ret[1]);
+ unset($user); // unset should be ok here because we aren't in a function
+ return;
+ }
+ $user = $ret[0];
+ }
+
+ if ( !empty($user) ) {
+ // generate it once here, while session is open. Value will be cached in session and return when called later on
+ generateAuthHash(ZM_AUTH_HASH_IPS);
+ }
+ } # end if token based auth
+} else {
+ $user = $defaultUser;
+} # end if ZM_OPT_USE_AUTH
?>
diff --git a/web/includes/config.php.in b/web/includes/config.php.in
index c6aa93c63..57064f22d 100644
--- a/web/includes/config.php.in
+++ b/web/includes/config.php.in
@@ -33,9 +33,9 @@ $configFile = ZM_CONFIG;
$localConfigFile = basename($configFile);
if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 ) {
if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) )
- print( "Warning, overriding installed $localConfigFile file with local copy\n" );
+ print("Warning, overriding installed $localConfigFile file with local copy\n");
else
- error_log( "Warning, overriding installed $localConfigFile file with local copy" );
+ error_log("Warning, overriding installed $localConfigFile file with local copy");
$configFile = $localConfigFile;
}
@@ -49,19 +49,19 @@ if ( is_dir($configSubFolder) ) {
if ( is_readable($configSubFolder) ) {
foreach ( glob("$configSubFolder/*.conf") as $filename ) {
//error_log("processing $filename");
- $configvals = array_replace($configvals, process_configfile($filename) );
+ $configvals = array_replace($configvals, process_configfile($filename));
}
} else {
- error_log( "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder." );
+ error_log("WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on $configSubFolder.");
}
} else {
- error_log( "WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $configSubFolder." );
+ error_log("WARNING: ZoneMinder configuration subfolder found but is not a directory. Check $configSubFolder.");
}
# Now that our array our finalized, define each key => value
# pair in the array as a constant
-foreach( $configvals as $key => $value) {
- define( $key, $value );
+foreach ( $configvals as $key => $value ) {
+ define($key, $value);
}
//
@@ -135,10 +135,10 @@ define( 'SCALE_BASE', 100 ); // The additional scalin
define( 'STRF_FMT_DATETIME_DB', '%Y-%m-%d %H:%M:%S' ); // Strftime format for database queries, don't change
define( 'MYSQL_FMT_DATETIME_SHORT', '%y/%m/%d %H:%i:%S' ); // MySQL date_format shorter format for dates with time
-require_once( 'database.php' );
-require_once( 'logger.php' );
+require_once('database.php');
+require_once('logger.php');
loadConfig();
-Logger::fetch()->initialise();
+ZM\Logger::fetch()->initialise();
$GLOBALS['defaultUser'] = array(
'Username' => 'admin',
@@ -158,68 +158,64 @@ $GLOBALS['defaultUser'] = array(
function loadConfig( $defineConsts=true ) {
global $config;
- global $configCats;
global $dbConn;
$config = array();
- $configCat = array();
- $result = $dbConn->query( 'select * from Config order by Id asc' );
+ $result = $dbConn->query('SELECT Name,Value FROM Config');
if ( !$result )
echo mysql_error();
- $monitors = array();
- while( $row = dbFetchNext( $result ) ) {
+ while( $row = dbFetchNext($result) ) {
if ( $defineConsts )
- define( $row['Name'], $row['Value'] );
+ define($row['Name'], $row['Value']);
$config[$row['Name']] = $row;
- if ( !($configCat = &$configCats[$row['Category']]) ) {
- $configCats[$row['Category']] = array();
- $configCat = &$configCats[$row['Category']];
- }
- $configCat[$row['Name']] = $row;
}
- //print_r( $config );
- //print_r( $configCats );
-}
+} # end function loadConfig
// For Human-readability, use ZM_SERVER_HOST or ZM_SERVER_NAME in zm.conf, and convert it here to a ZM_SERVER_ID
if ( ! defined('ZM_SERVER_ID') ) {
+ require_once('Server.php');
if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) {
- $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_NAME));
- if ( ! $server_id ) {
- Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.');
+ # Use Server lookup so that it caches
+ $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_NAME));
+ if ( !$Server ) {
+ ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_NAME set to ' . ZM_SERVER_NAME . ' in zm.conf, but no corresponding entry found in Servers table.');
} else {
- define( 'ZM_SERVER_ID', $server_id );
+ define('ZM_SERVER_ID', $Server->Id());
}
} else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) {
- $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_HOST));
- if ( ! $server_id ) {
- Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.');
+ $Server = ZM\Server::find_one(array('Name'=>ZM_SERVER_HOST));
+ if ( ! $Server ) {
+ ZM\Error('Invalid Multi-Server configration detected. ZM_SERVER_HOST set to ' . ZM_SERVER_HOST . ' in zm.conf, but no corresponding entry found in Servers table.');
} else {
- define( 'ZM_SERVER_ID', $server_id );
+ define('ZM_SERVER_ID', $Server->Id());
}
}
}
+if ( ZM_TIMEZONE )
+ ini_set('date.timezone', ZM_TIMEZONE);
+
function process_configfile($configFile) {
if ( is_readable( $configFile ) ) {
$configvals = array();
- $cfg = fopen( $configFile, 'r') or Error("Could not open config file: $configFile.");
+ $cfg = fopen($configFile, 'r') or ZM\Error("Could not open config file: $configFile.");
while ( !feof($cfg) ) {
- $str = fgets( $cfg, 256 );
- if ( preg_match( '/^\s*$/', $str ))
+ $str = fgets($cfg, 256);
+ if ( preg_match('/^\s*(#.*)?$/', $str) ) {
continue;
- elseif ( preg_match( '/^\s*#/', $str ))
- continue;
- elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches ))
+ } else if ( preg_match( '/^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/', $str, $matches )) {
$configvals[$matches[1]] = $matches[2];
+ } else {
+ ZM\Error("Malformed line in config $configFile\n$str");
+ }
}
- fclose( $cfg );
- return( $configvals );
+ fclose($cfg);
+ return $configvals;
} else {
- error_log( "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile." );
- return( false );
+ error_log("WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $configFile.");
+ return false;
}
}
diff --git a/web/includes/control_functions.php b/web/includes/control_functions.php
index bea235668..3fe131fab 100644
--- a/web/includes/control_functions.php
+++ b/web/includes/control_functions.php
@@ -1,13 +1,14 @@
Control();
if ( isset($_REQUEST['xge']) || isset($_REQUEST['yge']) ) {
$slow = 0.9; // Threshold for slow speed/timeouts
$turbo = 0.9; // Threshold for turbo speed
- if ( preg_match( '/^([a-z]+)([A-Z][a-z]+)([A-Za-z]+)+$/', $_REQUEST['control'], $matches ) ) {
+ if ( preg_match('/^([a-z]+)([A-Z][a-z]+)([A-Za-z]+)+$/', $_REQUEST['control'], $matches) ) {
$command = $matches[1];
$mode = $matches[2];
$dirn = $matches[3];
@@ -16,22 +17,22 @@ function buildControlCommand( $monitor ) {
case 'focus' :
{
$factor = $_REQUEST['yge']/100;
- if ( $monitor->HasFocusSpeed() ) {
- $speed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$factor)));
+ if ( $control->HasFocusSpeed() ) {
+ $speed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
{
- $step = intval(round($monitor->MinFocusStep()+(($monitor->MaxFocusStep()-$monitor->MinFocusStep())*$factor)));
+ $step = intval(round($control->MinFocusStep()+(($control->MaxFocusStep()-$control->MinFocusStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
case 'Con' :
{
if ( $monitor->AutoStopTimeout() ) {
- $slowSpeed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$slow)));
+ $slowSpeed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$slow)));
if ( $speed < $slowSpeed ) {
$ctrlCommand .= ' --autostop';
}
@@ -43,19 +44,19 @@ function buildControlCommand( $monitor ) {
}
case 'zoom' :
$factor = $_REQUEST['yge']/100;
- if ( $monitor->HasZoomSpeed() ) {
- $speed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$factor)));
+ if ( $control->HasZoomSpeed() ) {
+ $speed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinZoomStep()+(($monitor->MaxZoomStep()-$monitor->MinZoomStep())*$factor)));
+ $step = intval(round($control->MinZoomStep()+(($control->MaxZoomStep()-$control->MinZoomStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
case 'Con' :
if ( $monitor->AutoStopTimeout() ) {
- $slowSpeed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$slow)));
+ $slowSpeed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$slow)));
if ( $speed < $slowSpeed ) {
$ctrlCommand .= ' --autostop';
}
@@ -65,42 +66,42 @@ function buildControlCommand( $monitor ) {
break;
case 'iris' :
$factor = $_REQUEST['yge']/100;
- if ( $monitor->HasIrisSpeed() ) {
- $speed = intval(round($monitor->MinIrisSpeed()+(($monitor->MaxIrisSpeed()-$monitor->MinIrisSpeed())*$factor)));
+ if ( $control->HasIrisSpeed() ) {
+ $speed = intval(round($control->MinIrisSpeed()+(($control->MaxIrisSpeed()-$control->MinIrisSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinIrisStep()+(($monitor->MaxIrisStep()-$monitor->MinIrisStep())*$factor)));
+ $step = intval(round($control->MinIrisStep()+(($control->MaxIrisStep()-$control->MinIrisStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
break;
case 'white' :
$factor = $_REQUEST['yge']/100;
- if ( $monitor->HasWhiteSpeed() ) {
- $speed = intval(round($monitor->MinWhiteSpeed()+(($monitor->MaxWhiteSpeed()-$monitor->MinWhiteSpeed())*$factor)));
+ if ( $control->HasWhiteSpeed() ) {
+ $speed = intval(round($control->MinWhiteSpeed()+(($control->MaxWhiteSpeed()-$control->MinWhiteSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinWhiteStep()+(($monitor->MaxWhiteStep()-$monitor->MinWhiteStep())*$factor)));
+ $step = intval(round($control->MinWhiteStep()+(($control->MaxWhiteStep()-$control->MinWhiteStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
break;
case 'gain' :
$factor = $_REQUEST['yge']/100;
- if ( $monitor->HasGainSpeed() ) {
- $speed = intval(round($monitor->MinGainSpeed()+(($monitor->MaxGainSpeed()-$monitor->MinGainSpeed())*$factor)));
+ if ( $control->HasGainSpeed() ) {
+ $speed = intval(round($control->MinGainSpeed()+(($control->MaxGainSpeed()-$control->MinGainSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinGainStep()+(($monitor->MaxGainStep()-$monitor->MinGainStep())*$factor)));
+ $step = intval(round($control->MinGainStep()+(($control->MaxGainStep()-$control->MinGainStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
@@ -109,9 +110,9 @@ function buildControlCommand( $monitor ) {
$xFactor = empty($_REQUEST['xge'])?0:$_REQUEST['xge']/100;
$yFactor = empty($_REQUEST['yge'])?0:$_REQUEST['yge']/100;
- if ( $monitor->Orientation() != '0' ) {
+ if ( $monitor->Orientation() != 'ROTATE_0' ) {
$conversions = array(
- '90' => array(
+ 'ROTATE_90' => array(
'Up' => 'Left',
'Down' => 'Right',
'Left' => 'Down',
@@ -121,7 +122,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'DownRight',
'DownRight' => 'UpRight',
),
- '180' => array(
+ 'ROTATE_180' => array(
'Up' => 'Down',
'Down' => 'Up',
'Left' => 'Right',
@@ -131,7 +132,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'UpRight',
'DownRight' => 'UpLeft',
),
- '270' => array(
+ 'ROTATE_270' => array(
'Up' => 'Right',
'Down' => 'Left',
'Left' => 'Up',
@@ -141,7 +142,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'UpLeft',
'DownRight' => 'DownLeft',
),
- 'hori' => array(
+ 'FLIP_HORI' => array(
'Up' => 'Up',
'Down' => 'Down',
'Left' => 'Right',
@@ -151,7 +152,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'DownRight',
'DownRight' => 'DownLeft',
),
- 'vert' => array(
+ 'FLIP_VERT' => array(
'Up' => 'Down',
'Down' => 'Up',
'Left' => 'Left',
@@ -167,29 +168,29 @@ function buildControlCommand( $monitor ) {
$dirn = $new_dirn;
}
- if ( $monitor->HasPanSpeed() && $xFactor ) {
- if ( $monitor->HasTurboPan() ) {
+ if ( $control->HasPanSpeed() && $xFactor ) {
+ if ( $control->HasTurboPan() ) {
if ( $xFactor >= $turbo ) {
- $panSpeed = $monitor->TurboPanSpeed();
+ $panSpeed = $control->TurboPanSpeed();
} else {
$xFactor = $xFactor/$turbo;
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
} else {
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
$ctrlCommand .= ' --panspeed='.$panSpeed;
}
- if ( $monitor->HasTiltSpeed() && $yFactor ) {
- if ( $monitor->HasTurboTilt() ) {
+ if ( $control->HasTiltSpeed() && $yFactor ) {
+ if ( $control->HasTurboTilt() ) {
if ( $yFactor >= $turbo ) {
- $tiltSpeed = $monitor->TurboTiltSpeed();
+ $tiltSpeed = $control->TurboTiltSpeed();
} else {
$yFactor = $yFactor/$turbo;
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
} else {
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
$ctrlCommand .= ' --tiltspeed='.$tiltSpeed;
}
@@ -197,18 +198,18 @@ function buildControlCommand( $monitor ) {
case 'Rel' :
case 'Abs' :
if ( preg_match( '/(Left|Right)$/', $dirn ) ) {
- $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor)));
+ $panStep = intval(round($control->MinPanStep()+(($control->MaxPanStep()-$control->MinPanStep())*$xFactor)));
$ctrlCommand .= ' --panstep='.$panStep;
}
if ( preg_match( '/^(Up|Down)/', $dirn ) ) {
- $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor)));
+ $tiltStep = intval(round($control->MinTiltStep()+(($control->MaxTiltStep()-$control->MinTiltStep())*$yFactor)));
$ctrlCommand .= ' --tiltstep='.$tiltStep;
}
break;
case 'Con' :
if ( $monitor->AutoStopTimeout() ) {
- $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow)));
- $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow)));
+ $slowPanSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$slow)));
+ $slowTiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$slow)));
if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) {
$ctrlCommand .= ' --autostop';
}
@@ -224,38 +225,38 @@ function buildControlCommand( $monitor ) {
$x = deScale( $_REQUEST['x'], $_REQUEST['scale'] );
$y = deScale( $_REQUEST['y'], $_REQUEST['scale'] );
switch ( $monitor->Orientation() ) {
- case '0' :
- case '180' :
- case 'hori' :
- case 'vert' :
+ case 'ROTATE_0' :
+ case 'ROTATE_180' :
+ case 'FLIP_HORI' :
+ case 'FLIP_VERT' :
$width = $monitor->Width();
$height = $monitor->Height();
break;
- case '90' :
- case '270' :
+ case 'ROTATE_90' :
+ case 'ROTATE_270' :
$width = $monitor->Height();
$height = $monitor->Width();
break;
}
switch ( $monitor->Orientation() ) {
- case '90' :
+ case 'ROTATE_90' :
$tempY = $y;
$y = $height - $x;
$x = $tempY;
break;
- case '180' :
+ case 'ROTATE_180' :
$x = $width - $x;
$y = $height - $y;
break;
- case '270' :
+ case 'ROTATE_270' :
$tempX = $x;
$x = $width - $y;
$y = $tempX;
break;
- case 'hori' :
+ case 'FLIP_HORI' :
$x = $width - $x;
break;
- case 'vert' :
+ case 'FLIP_VERT' :
$y = $height - $y;
break;
}
@@ -271,24 +272,24 @@ function buildControlCommand( $monitor ) {
$yFactor = ($y - $halfHeight)/$halfHeight;
switch ( $monitor->Orientation() ) {
- case '90' :
+ case 'ROTATE_90' :
$tempYFactor = $y;
$yFactor = -$xFactor;
$xFactor = $tempYFactor;
break;
- case '180' :
+ case 'ROTATE_180' :
$xFactor = -$xFactor;
$yFactor = -$yFactor;
break;
- case '270' :
+ case 'ROTATE_270' :
$tempXFactor = $x;
$xFactor = -$yFactor;
$yFactor = $tempXFactor;
break;
- case 'hori' :
+ case 'FLIP_HORI' :
$xFactor = -$xFactor;
break;
- case 'vert' :
+ case 'FLIP_VERT' :
$yFactor = -$yFactor;
break;
}
@@ -319,36 +320,36 @@ function buildControlCommand( $monitor ) {
$xFactor = abs($xFactor);
$yFactor = abs($yFactor);
- if ( $monitor->HasPanSpeed() && $xFactor ) {
- if ( $monitor->HasTurboPan() ) {
+ if ( $control->HasPanSpeed() && $xFactor ) {
+ if ( $control->HasTurboPan() ) {
if ( $xFactor >= $turbo ) {
- $panSpeed = $monitor->TurboPanSpeed();
+ $panSpeed = $control->TurboPanSpeed();
} else {
$xFactor = $xFactor/$turbo;
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
} else {
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
}
- if ( $monitor->HasTiltSpeed() && $yFactor ) {
- if ( $monitor->HasTurboTilt() ) {
+ if ( $control->HasTiltSpeed() && $yFactor ) {
+ if ( $control->HasTurboTilt() ) {
if ( $yFactor >= $turbo ) {
- $tiltSpeed = $monitor->TurboTiltSpeed();
+ $tiltSpeed = $control->TurboTiltSpeed();
} else {
$yFactor = $yFactor/$turbo;
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
} else {
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
}
if ( preg_match( '/(Left|Right)$/', $dirn ) ) {
- $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor)));
+ $panStep = intval(round($control->MinPanStep()+(($control->MaxPanStep()-$control->MinPanStep())*$xFactor)));
$ctrlCommand .= ' --panstep='.$panStep.' --panspeed='.$panSpeed;
}
if ( preg_match( '/^(Up|Down)/', $dirn ) ) {
- $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor)));
+ $tiltStep = intval(round($control->MinTiltStep()+(($control->MaxTiltStep()-$control->MinTiltStep())*$yFactor)));
$ctrlCommand .= ' --tiltstep='.$tiltStep.' --tiltspeed='.$tiltSpeed;
}
}
@@ -362,24 +363,24 @@ function buildControlCommand( $monitor ) {
$yFactor = ($y - $halfHeight)/$halfHeight;
switch ( $monitor->Orientation() ) {
- case '90' :
+ case 'ROTATE_90' :
$tempYFactor = $y;
$yFactor = -$xFactor;
$xFactor = $tempYFactor;
break;
- case '180' :
+ case 'ROTATE_180' :
$xFactor = -$xFactor;
$yFactor = -$yFactor;
break;
- case '270' :
+ case 'ROTATE_270' :
$tempXFactor = $x;
$xFactor = -$yFactor;
$yFactor = $tempXFactor;
break;
- case 'hori' :
+ case 'FLIP_HORI' :
$xFactor = -$xFactor;
break;
- case 'vert' :
+ case 'FLIP_VERT' :
$yFactor = -$yFactor;
break;
}
@@ -410,28 +411,28 @@ function buildControlCommand( $monitor ) {
$xFactor = abs($xFactor);
$yFactor = abs($yFactor);
- if ( $monitor->HasPanSpeed() && $xFactor ) {
- if ( $monitor->HasTurboPan() ) {
+ if ( $control->HasPanSpeed() && $xFactor ) {
+ if ( $control->HasTurboPan() ) {
if ( $xFactor >= $turbo ) {
- $panSpeed = $monitor->TurboPanSpeed();
+ $panSpeed = $control->TurboPanSpeed();
} else {
$xFactor = $xFactor/$turbo;
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
} else {
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
}
- if ( $monitor->HasTiltSpeed() && $yFactor ) {
- if ( $monitor->HasTurboTilt() ) {
+ if ( $control->HasTiltSpeed() && $yFactor ) {
+ if ( $control->HasTurboTilt() ) {
if ( $yFactor >= $turbo ) {
- $tiltSpeed = $monitor->TurboTiltSpeed();
+ $tiltSpeed = $control->TurboTiltSpeed();
} else {
$yFactor = $yFactor/$turbo;
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
} else {
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
}
if ( preg_match( '/(Left|Right)$/', $dirn ) ) {
@@ -441,8 +442,8 @@ function buildControlCommand( $monitor ) {
$ctrlCommand .= ' --tiltspeed='.$tiltSpeed;
}
if ( $monitor->AutoStopTimeout() ) {
- $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow)));
- $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow)));
+ $slowPanSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$slow)));
+ $slowTiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$slow)));
if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) {
$ctrlCommand .= ' --autostop';
}
@@ -470,19 +471,19 @@ function buildControlCommand( $monitor ) {
$factor = ($y+1)/$long_y;
break;
}
- if ( $monitor->HasFocusSpeed() ) {
- $speed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$factor)));
+ if ( $control->HasFocusSpeed() ) {
+ $speed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinFocusStep()+(($monitor->MaxFocusStep()-$monitor->MinFocusStep())*$factor)));
+ $step = intval(round($control->MinFocusStep()+(($control->MaxFocusStep()-$control->MinFocusStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
case 'Con' :
if ( $monitor->AutoStopTimeout() ) {
- $slowSpeed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$slow)));
+ $slowSpeed = intval(round($control->MinFocusSpeed()+(($control->MaxFocusSpeed()-$control->MinFocusSpeed())*$slow)));
if ( $speed < $slowSpeed ) {
$ctrlCommand .= ' --autostop';
}
@@ -499,19 +500,19 @@ function buildControlCommand( $monitor ) {
$factor = ($y+1)/$long_y;
break;
}
- if ( $monitor->HasZoomSpeed() ) {
- $speed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$factor)));
+ if ( $control->HasZoomSpeed() ) {
+ $speed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinZoomStep()+(($monitor->MaxZoomStep()-$monitor->MinZoomStep())*$factor)));
+ $step = intval(round($control->MinZoomStep()+(($control->MaxZoomStep()-$control->MinZoomStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
case 'Con' :
if ( $monitor->AutoStopTimeout() ) {
- $slowSpeed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$slow)));
+ $slowSpeed = intval(round($control->MinZoomSpeed()+(($control->MaxZoomSpeed()-$control->MinZoomSpeed())*$slow)));
if ( $speed < $slowSpeed ) {
$ctrlCommand .= ' --autostop';
}
@@ -528,14 +529,14 @@ function buildControlCommand( $monitor ) {
$factor = ($y+1)/$long_y;
break;
}
- if ( $monitor->HasIrisSpeed() ) {
- $speed = intval(round($monitor->MinIrisSpeed()+(($monitor->MaxIrisSpeed()-$monitor->MinIrisSpeed())*$factor)));
+ if ( $control->HasIrisSpeed() ) {
+ $speed = intval(round($control->MinIrisSpeed()+(($control->MaxIrisSpeed()-$control->MinIrisSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinIrisStep()+(($monitor->MaxIrisStep()-$monitor->MinIrisStep())*$factor)));
+ $step = intval(round($control->MinIrisStep()+(($control->MaxIrisStep()-$control->MinIrisStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
@@ -549,14 +550,14 @@ function buildControlCommand( $monitor ) {
$factor = ($y+1)/$long_y;
break;
}
- if ( $monitor->HasWhiteSpeed() ) {
- $speed = intval(round($monitor->MinWhiteSpeed()+(($monitor->MaxWhiteSpeed()-$monitor->MinWhiteSpeed())*$factor)));
+ if ( $control->HasWhiteSpeed() ) {
+ $speed = intval(round($control->MinWhiteSpeed()+(($control->MaxWhiteSpeed()-$control->MinWhiteSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinWhiteStep()+(($monitor->MaxWhiteStep()-$monitor->MinWhiteStep())*$factor)));
+ $step = intval(round($control->MinWhiteStep()+(($control->MaxWhiteStep()-$control->MinWhiteStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
@@ -570,14 +571,14 @@ function buildControlCommand( $monitor ) {
$factor = ($y+1)/$long_y;
break;
}
- if ( $monitor->HasGainSpeed() ) {
- $speed = intval(round($monitor->MinGainSpeed()+(($monitor->MaxGainSpeed()-$monitor->MinGainSpeed())*$factor)));
+ if ( $control->HasGainSpeed() ) {
+ $speed = intval(round($control->MinGainSpeed()+(($control->MaxGainSpeed()-$control->MinGainSpeed())*$factor)));
$ctrlCommand .= ' --speed='.$speed;
}
switch( $mode ) {
case 'Abs' :
case 'Rel' :
- $step = intval(round($monitor->MinGainStep()+(($monitor->MaxGainStep()-$monitor->MinGainStep())*$factor)));
+ $step = intval(round($control->MinGainStep()+(($control->MaxGainStep()-$control->MinGainStep())*$factor)));
$ctrlCommand .= ' --step='.$step;
break;
}
@@ -597,9 +598,9 @@ function buildControlCommand( $monitor ) {
$xFactor = ($x+1)/$short_x;
}
- if ( $monitor->Orientation() != '0' ) {
+ if ( $monitor->Orientation() != 'ROTATE_0' ) {
$conversions = array(
- '90' => array(
+ 'ROTATE_90' => array(
'Up' => 'Left',
'Down' => 'Right',
'Left' => 'Down',
@@ -609,7 +610,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'DownRight',
'DownRight' => 'UpRight',
),
- '180' => array(
+ 'ROTATE_180' => array(
'Up' => 'Down',
'Down' => 'Up',
'Left' => 'Right',
@@ -619,7 +620,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'UpRight',
'DownRight' => 'UpLeft',
),
- '270' => array(
+ 'ROTATE_270' => array(
'Up' => 'Right',
'Down' => 'Left',
'Left' => 'Up',
@@ -629,7 +630,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'UpLeft',
'DownRight' => 'DownLeft',
),
- 'hori' => array(
+ 'FLIP_HORI' => array(
'Up' => 'Up',
'Down' => 'Down',
'Left' => 'Right',
@@ -639,7 +640,7 @@ function buildControlCommand( $monitor ) {
'DownLeft' => 'DownRight',
'DownRight' => 'DownLeft',
),
- 'vert' => array(
+ 'FLIP_VERT' => array(
'Up' => 'Down',
'Down' => 'Up',
'Left' => 'Left',
@@ -655,29 +656,29 @@ function buildControlCommand( $monitor ) {
$dirn = $new_dirn;
}
- if ( $monitor->HasPanSpeed() && $xFactor ) {
- if ( $monitor->HasTurboPan() ) {
+ if ( $control->HasPanSpeed() && $xFactor ) {
+ if ( $control->HasTurboPan() ) {
if ( $xFactor >= $turbo ) {
- $panSpeed = $monitor->TurboPanSpeed();
+ $panSpeed = $control->TurboPanSpeed();
} else {
$xFactor = $xFactor/$turbo;
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
} else {
- $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor)));
+ $panSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$xFactor)));
}
$ctrlCommand .= ' --panspeed='.$panSpeed;
}
- if ( $monitor->HasTiltSpeed() && $yFactor ) {
- if ( $monitor->HasTurboTilt() ) {
+ if ( $control->HasTiltSpeed() && $yFactor ) {
+ if ( $control->HasTurboTilt() ) {
if ( $yFactor >= $turbo ) {
- $tiltSpeed = $monitor->TurboTiltSpeed();
+ $tiltSpeed = $control->TurboTiltSpeed();
} else {
$yFactor = $yFactor/$turbo;
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
} else {
- $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor)));
+ $tiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$yFactor)));
}
$ctrlCommand .= ' --tiltspeed='.$tiltSpeed;
}
@@ -685,18 +686,18 @@ function buildControlCommand( $monitor ) {
case 'Rel' :
case 'Abs' :
if ( preg_match( '/(Left|Right)$/', $dirn ) ) {
- $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor)));
+ $panStep = intval(round($control->MinPanStep()+(($control->MaxPanStep()-$control->MinPanStep())*$xFactor)));
$ctrlCommand .= ' --panstep='.$panStep;
}
if ( preg_match( '/^(Up|Down)/', $dirn ) ) {
- $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor)));
+ $tiltStep = intval(round($control->MinTiltStep()+(($control->MaxTiltStep()-$control->MinTiltStep())*$yFactor)));
$ctrlCommand .= ' --tiltstep='.$tiltStep;
}
break;
case 'Con' :
if ( $monitor->AutoStopTimeout() ) {
- $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow)));
- $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow)));
+ $slowPanSpeed = intval(round($control->MinPanSpeed()+(($control->MaxPanSpeed()-$control->MinPanSpeed())*$slow)));
+ $slowTiltSpeed = intval(round($control->MinTiltSpeed()+(($control->MaxTiltSpeed()-$control->MinTiltSpeed())*$slow)));
if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) {
$ctrlCommand .= ' --autostop';
}
@@ -716,12 +717,16 @@ function buildControlCommand( $monitor ) {
if ( canEdit( 'Control' ) ) {
$preset = validInt($_REQUEST['preset']);
$newLabel = validJsStr($_REQUEST['newLabel']);
- $row = dbFetchOne( 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', NULL, array( $monitor->Id(), $preset ) );
+ $row = dbFetchOne(
+ 'SELECT * FROM `ControlPresets` WHERE `MonitorId` = ? AND `Preset`=?',
+ NULL, array($monitor->Id(), $preset));
if ( $newLabel != $row['Label'] ) {
if ( $newLabel ) {
- dbQuery( 'REPLACE INTO ControlPresets ( MonitorId, Preset, Label ) VALUES ( ?, ?, ? )', array( $monitor->Id(), $preset, $newLabel ) );
+ dbQuery('REPLACE INTO `ControlPresets` (`MonitorId`, `Preset`, `Label`) VALUES ( ?, ?, ? )',
+ array($monitor->Id(), $preset, $newLabel));
} else {
- dbQuery( 'DELETE FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', array( $monitor->Id(), $preset ) );
+ dbQuery('DELETE FROM `ControlPresets` WHERE `MonitorId`=? AND `Preset`=?',
+ array($monitor->Id(), $preset));
}
}
$ctrlCommand .= ' --preset='.$preset;
@@ -732,32 +737,6 @@ function buildControlCommand( $monitor ) {
}
}
$ctrlCommand .= ' --command='.$_REQUEST['control'];
- return( $ctrlCommand );
+ return $ctrlCommand;
}
-function sendControlCommand($mid,$command) {
- // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
- $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 );
- if ( $socket < 0 ) {
- Fatal( 'socket_create() failed: '.socket_strerror($socket) );
- }
- $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$mid.'.sock';
- if ( @socket_connect( $socket, $sockFile ) ) {
- $options = array();
- foreach ( explode( ' ', $command ) as $option ) {
- if ( preg_match( '/--([^=]+)(?:=(.+))?/', $option, $matches ) ) {
- $options[$matches[1]] = $matches[2]?$matches[2]:1;
- }
- }
- $optionString = jsonEncode( $options );
- if ( !socket_write( $socket, $optionString ) ) {
- Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) );
- }
- socket_close( $socket );
- } else if ( $command != 'quit' ) {
- $command .= ' --id='.$mid;
-
- // Can't connect so use script
- $ctrlOutput = exec( escapeshellcmd( $command ) );
- }
-} // end function sendControlCommand( $mid, $command )
diff --git a/web/includes/csrf/csrf-magic.js b/web/includes/csrf/csrf-magic.js
index 0989c1065..06ed62076 100644
--- a/web/includes/csrf/csrf-magic.js
+++ b/web/includes/csrf/csrf-magic.js
@@ -9,183 +9,185 @@
// The wrapper must be set BEFORE onreadystatechange is written to, since
// a bug in ActiveXObject prevents us from properly testing for it.
CsrfMagic = function(real) {
- // try to make it ourselves, if you didn't pass it
- if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
- if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
- if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
- if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
- this.csrf = real;
- // properties
- var csrfMagic = this;
- real.onreadystatechange = function() {
- csrfMagic._updateProps();
- return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
- };
+ // try to make it ourselves, if you didn't pass it
+ if (!real) try {real = new XMLHttpRequest;} catch (e) {;}
+ if (!real) try {real = new ActiveXObject('Msxml2.XMLHTTP');} catch (e) {;}
+ if (!real) try {real = new ActiveXObject('Microsoft.XMLHTTP');} catch (e) {;}
+ if (!real) try {real = new ActiveXObject('Msxml2.XMLHTTP.4.0');} catch (e) {;}
+ this.csrf = real;
+ // properties
+ var csrfMagic = this;
+ real.onreadystatechange = function() {
csrfMagic._updateProps();
-}
+ return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
+ };
+ csrfMagic._updateProps();
+};
CsrfMagic.prototype = {
- open: function(method, url, async, username, password) {
- if (method == 'POST') this.csrf_isPost = true;
- // deal with Opera bug, thanks jQuery
- if (username) return this.csrf_open(method, url, async, username, password);
- else return this.csrf_open(method, url, async);
- },
- csrf_open: function(method, url, async, username, password) {
- if (username) return this.csrf.open(method, url, async, username, password);
- else return this.csrf.open(method, url, async);
- },
+ open: function(method, url, async, username, password) {
+ if (method == 'POST') this.csrf_isPost = true;
+ // deal with Opera bug, thanks jQuery
+ if (username) return this.csrf_open(method, url, async, username, password);
+ else return this.csrf_open(method, url, async);
+ },
+ csrf_open: function(method, url, async, username, password) {
+ if (username) return this.csrf.open(method, url, async, username, password);
+ else return this.csrf.open(method, url, async);
+ },
- send: function(data) {
- if (!this.csrf_isPost) return this.csrf_send(data);
- prepend = csrfMagicName + '=' + csrfMagicToken + '&';
+ send: function(data) {
+ if (!this.csrf_isPost) return this.csrf_send(data);
+ prepend = csrfMagicName + '=' + csrfMagicToken + '&';
// XXX: Removed to eliminate 'Refused to set unsafe header "Content-length" ' errors in modern browsers
// if (this.csrf_purportedLength === undefined) {
// this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
// delete this.csrf_purportedLength;
// }
- delete this.csrf_isPost;
- return this.csrf_send(prepend + data);
- },
- csrf_send: function(data) {
- return this.csrf.send(data);
- },
+ delete this.csrf_isPost;
+ return this.csrf_send(prepend + data);
+ },
+ csrf_send: function(data) {
+ return this.csrf.send(data);
+ },
- setRequestHeader: function(header, value) {
- // We have to auto-set this at the end, since we don't know how long the
- // nonce is when added to the data.
- if (this.csrf_isPost && header == "Content-length") {
- this.csrf_purportedLength = value;
- return;
- }
- return this.csrf_setRequestHeader(header, value);
- },
- csrf_setRequestHeader: function(header, value) {
- return this.csrf.setRequestHeader(header, value);
- },
+ setRequestHeader: function(header, value) {
+ // We have to auto-set this at the end, since we don't know how long the
+ // nonce is when added to the data.
+ if (this.csrf_isPost && header == "Content-length") {
+ this.csrf_purportedLength = value;
+ return;
+ }
+ return this.csrf_setRequestHeader(header, value);
+ },
+ csrf_setRequestHeader: function(header, value) {
+ return this.csrf.setRequestHeader(header, value);
+ },
- abort: function() {
- return this.csrf.abort();
- },
- getAllResponseHeaders: function() {
- return this.csrf.getAllResponseHeaders();
- },
- getResponseHeader: function(header) {
- return this.csrf.getResponseHeader(header);
- } // ,
-}
+ abort: function() {
+ return this.csrf.abort();
+ },
+ getAllResponseHeaders: function() {
+ return this.csrf.getAllResponseHeaders();
+ },
+ getResponseHeader: function(header) {
+ return this.csrf.getResponseHeader(header);
+ } // ,
+};
// proprietary
CsrfMagic.prototype._updateProps = function() {
- this.readyState = this.csrf.readyState;
- if (this.readyState == 4) {
- this.responseText = this.csrf.responseText;
- this.responseXML = this.csrf.responseXML;
- this.status = this.csrf.status;
- this.statusText = this.csrf.statusText;
- }
-}
+ this.readyState = this.csrf.readyState;
+ if (this.readyState == 4) {
+ this.responseText = this.csrf.responseText;
+ this.responseXML = this.csrf.responseXML;
+ this.status = this.csrf.status;
+ this.statusText = this.csrf.statusText;
+ }
+};
+
CsrfMagic.process = function(base) {
- if(typeof base == 'object') {
- base[csrfMagicName] = csrfMagicToken;
- return base;
- }
- var prepend = csrfMagicName + '=' + csrfMagicToken;
- if (base) return prepend + '&' + base;
- return prepend;
-}
+ if ( typeof base == 'object' ) {
+ base[csrfMagicName] = csrfMagicToken;
+ return base;
+ }
+ var prepend = csrfMagicName + '=' + csrfMagicToken;
+ if ( base ) return prepend + '&' + base;
+ return prepend;
+};
+
// callback function for when everything on the page has loaded
CsrfMagic.end = function() {
- // This rewrites forms AGAIN, so in case buffering didn't work this
- // certainly will.
- forms = document.getElementsByTagName('form');
- for (var i = 0; i < forms.length; i++) {
- form = forms[i];
- if (form.method.toUpperCase() !== 'POST') continue;
- if (form.elements[csrfMagicName]) continue;
- var input = document.createElement('input');
- input.setAttribute('name', csrfMagicName);
- input.setAttribute('value', csrfMagicToken);
- input.setAttribute('type', 'hidden');
- form.appendChild(input);
- }
-}
+ // This rewrites forms AGAIN, so in case buffering didn't work this
+ // certainly will.
+ forms = document.getElementsByTagName('form');
+ for (var i = 0; i < forms.length; i++) {
+ form = forms[i];
+ if (form.method.toUpperCase() !== 'POST') continue;
+ if (form.elements[csrfMagicName]) continue;
+ var input = document.createElement('input');
+ input.setAttribute('name', csrfMagicName);
+ input.setAttribute('value', csrfMagicToken);
+ input.setAttribute('type', 'hidden');
+ form.appendChild(input);
+ }
+};
// Sets things up for Mozilla/Opera/nice browsers
// We very specifically match against Internet Explorer, since they haven't
// implemented prototypes correctly yet.
-if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') {
- var x = XMLHttpRequest.prototype;
- var c = CsrfMagic.prototype;
+if ( window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v' ) {
+ var x = XMLHttpRequest.prototype;
+ var c = CsrfMagic.prototype;
- // Save the original functions
- x.csrf_open = x.open;
- x.csrf_send = x.send;
- x.csrf_setRequestHeader = x.setRequestHeader;
+ // Save the original functions
+ x.csrf_open = x.open;
+ x.csrf_send = x.send;
+ x.csrf_setRequestHeader = x.setRequestHeader;
- // Notice that CsrfMagic is itself an instantiatable object, but only
- // open, send and setRequestHeader are necessary as decorators.
- x.open = c.open;
- x.send = c.send;
- x.setRequestHeader = c.setRequestHeader;
+ // Notice that CsrfMagic is itself an instantiatable object, but only
+ // open, send and setRequestHeader are necessary as decorators.
+ x.open = c.open;
+ x.send = c.send;
+ x.setRequestHeader = c.setRequestHeader;
} else {
- // The only way we can do this is by modifying a library you have been
- // using. We support YUI, script.aculo.us, prototype, MooTools,
- // jQuery, Ext and Dojo.
- if (window.jQuery) {
- // jQuery didn't implement a new XMLHttpRequest function, so we have
- // to do this the hard way.
- jQuery.csrf_ajax = jQuery.ajax;
- jQuery.ajax = function( s ) {
- if (s.type && s.type.toUpperCase() == 'POST') {
- s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
- if ( s.data && s.processData && typeof s.data != "string" ) {
- s.data = jQuery.param(s.data);
- }
- s.data = CsrfMagic.process(s.data);
- }
- return jQuery.csrf_ajax( s );
+ // The only way we can do this is by modifying a library you have been
+ // using. We support YUI, script.aculo.us, prototype, MooTools,
+ // jQuery, Ext and Dojo.
+ if ( window.jQuery ) {
+ // jQuery didn't implement a new XMLHttpRequest function, so we have
+ // to do this the hard way.
+ jQuery.csrf_ajax = jQuery.ajax;
+ jQuery.ajax = function( s ) {
+ if (s.type && s.type.toUpperCase() == 'POST') {
+ s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
+ if ( s.data && s.processData && typeof s.data != "string" ) {
+ s.data = jQuery.param(s.data);
}
- }
- if (window.Prototype) {
- // This works for script.aculo.us too
- Ajax.csrf_getTransport = Ajax.getTransport;
- Ajax.getTransport = function() {
- return new CsrfMagic(Ajax.csrf_getTransport());
- }
- }
- if (window.MooTools) {
- Browser.csrf_Request = Browser.Request;
- Browser.Request = function () {
- return new CsrfMagic(Browser.csrf_Request());
- }
- }
- if (window.YAHOO) {
- // old YUI API
- YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
- YAHOO.util.Connect.createXhrObject = function (transaction) {
- obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
- obj.conn = new CsrfMagic(obj.conn);
- return obj;
- }
- }
- if (window.Ext) {
- // Ext can use other js libraries as loaders, so it has to come last
- // Ext's implementation is pretty identical to Yahoo's, but we duplicate
- // it for comprehensiveness's sake.
- Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
- Ext.lib.Ajax.createXhrObject = function (transaction) {
- obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
- obj.conn = new CsrfMagic(obj.conn);
- return obj;
- }
- }
- if (window.dojo) {
- // NOTE: this doesn't work with latest dojo
- dojo.csrf__xhrObj = dojo._xhrObj;
- dojo._xhrObj = function () {
- return new CsrfMagic(dojo.csrf__xhrObj());
- }
- }
-}
+ s.data = CsrfMagic.process(s.data);
+ }
+ return jQuery.csrf_ajax(s);
+ };
+ }
+ if ( window.Prototype ) {
+ // This works for script.aculo.us too
+ Ajax.csrf_getTransport = Ajax.getTransport;
+ Ajax.getTransport = function() {
+ return new CsrfMagic(Ajax.csrf_getTransport());
+ };
+ }
+ if ( window.MooTools ) {
+ Browser.csrf_Request = Browser.Request;
+ Browser.Request = function() {
+ return new CsrfMagic(Browser.csrf_Request());
+ };
+ }
+ if ( window.YAHOO ) {
+ // old YUI API
+ YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
+ YAHOO.util.Connect.createXhrObject = function(transaction) {
+ obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
+ obj.conn = new CsrfMagic(obj.conn);
+ return obj;
+ };
+ }
+ if ( window.Ext ) {
+ // Ext can use other js libraries as loaders, so it has to come last
+ // Ext's implementation is pretty identical to Yahoo's, but we duplicate
+ // it for comprehensiveness's sake.
+ Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
+ Ext.lib.Ajax.createXhrObject = function(transaction) {
+ obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
+ obj.conn = new CsrfMagic(obj.conn);
+ return obj;
+ };
+ }
+ if ( window.dojo ) {
+ // NOTE: this doesn't work with latest dojo
+ dojo.csrf__xhrObj = dojo._xhrObj;
+ dojo._xhrObj = function() {
+ return new CsrfMagic(dojo.csrf__xhrObj());
+ };
+ }
+};
diff --git a/web/includes/csrf/csrf-magic.php b/web/includes/csrf/csrf-magic.php
index 55819329c..d96165eb9 100644
--- a/web/includes/csrf/csrf-magic.php
+++ b/web/includes/csrf/csrf-magic.php
@@ -150,24 +150,27 @@ function csrf_ob_handler($buffer, $flags) {
return $buffer;
}
}
+ global $cspNonce;
$tokens = csrf_get_tokens();
$name = $GLOBALS['csrf']['input-name'];
$endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : '';
$input = "";
$buffer = preg_replace('#(
CSRF check failed. Your form session may have expired, or you may not have
- cookies enabled.
-
-
Debug: $tokens
', $script . '', $buffer, $count);
if (!$count) {
$buffer .= $script;
@@ -183,6 +186,7 @@ function csrf_ob_handler($buffer, $flags) {
*/
function csrf_check($fatal = true) {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true;
+ global $cspNonce;
csrf_start();
$name = $GLOBALS['csrf']['input-name'];
$ok = false;
@@ -207,6 +211,7 @@ break;
}
$ok = true;
} while (false);
+
if ($fatal && !$ok) {
$callback = $GLOBALS['csrf']['callback'];
if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden';
@@ -286,9 +291,14 @@ function csrf_callback($tokens) {
echo "