From a995d72ebd5f561e0a53183a1a7347bb24756758 Mon Sep 17 00:00:00 2001 From: Andrew Bauer Date: Sat, 22 Aug 2015 12:36:49 -0500 Subject: [PATCH 01/20] add warning and help text for maxfps fields --- web/lang/en_gb.php | 11 ++++++++++- web/skins/classic/views/monitor.php | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index bf0258caf..6112aaf8c 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -903,7 +903,16 @@ $OLANG = array( 'OPTIONS_EXIF' => array( 'Help' => "Enable this option to embed EXIF data into each jpeg frame." ), - + 'OPTIONS_MAXFPS' => array( + 'Help' => "This field has certain limitations when used for non-local devices.~~ ". + "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". + "and missed events~~". + "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". + " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". + "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". + "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". + "for new images. In this case, it is safe to use thie field." + ), // 'LANG_DEFAULT' => array( // 'Prompt' => "This is a new prompt for this option", diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 5920409ad..7935e4f5c 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -680,9 +680,19 @@ switch ( $tab ) - - +  () +  () + + + + From af76d19646f4334164a530b239ddd17a91349070 Mon Sep 17 00:00:00 2001 From: SteveGilvarry Date: Fri, 18 Dec 2015 01:47:10 +1100 Subject: [PATCH 02/20] Add a constrained namespace implementation around Authenticator class to prevent conflict with libvlc live555. --- src/zm_remote_camera.cpp | 2 +- src/zm_remote_camera.h | 2 +- src/zm_remote_camera_http.cpp | 4 ++-- src/zm_rtsp.cpp | 4 ++-- src/zm_rtsp.h | 2 +- src/zm_rtsp_auth.cpp | 4 ++++ src/zm_rtsp_auth.h | 4 ++++ 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/zm_remote_camera.cpp b/src/zm_remote_camera.cpp index 608311649..8ac4a51ba 100644 --- a/src/zm_remote_camera.cpp +++ b/src/zm_remote_camera.cpp @@ -71,7 +71,7 @@ void RemoteCamera::Initialise() } mNeedAuth = false; - mAuthenticator = new Authenticator(username,password); + mAuthenticator = new zm::Authenticator(username,password); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); diff --git a/src/zm_remote_camera.h b/src/zm_remote_camera.h index 44c00bf65..7e3ae79a8 100644 --- a/src/zm_remote_camera.h +++ b/src/zm_remote_camera.h @@ -50,7 +50,7 @@ protected: // fill required fields and set needAuth // subsequent requests can set the required authentication header. bool mNeedAuth; - Authenticator* mAuthenticator; + zm::Authenticator* mAuthenticator; protected: struct addrinfo *hp; diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 44004564f..440c24e05 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -306,7 +306,7 @@ int RemoteCameraHttp::GetResponse() std::string Header = header; mAuthenticator->checkAuthResponse(Header); - if ( mAuthenticator->auth_method() == AUTH_DIGEST ) { + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { Debug( 2, "Need Digest Authentication" ); request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); @@ -750,7 +750,7 @@ Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_ Debug(2, "Checking for digest auth in %s", authenticate_header ); mAuthenticator->checkAuthResponse(Header); - if ( mAuthenticator->auth_method() == AUTH_DIGEST ) { + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { Debug( 2, "Need Digest Authentication" ); request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 0dbcb7329..7ac80e9b6 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -203,9 +203,9 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, mNeedAuth = false; StringVector parts = split(auth,":"); if (parts.size() > 1) - mAuthenticator = new Authenticator(parts[0], parts[1]); + mAuthenticator = new zm::Authenticator(parts[0], parts[1]); else - mAuthenticator = new Authenticator(parts[0], ""); + mAuthenticator = new zm::Authenticator(parts[0], ""); } RtspThread::~RtspThread() diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h index acd28e651..192200d0b 100644 --- a/src/zm_rtsp.h +++ b/src/zm_rtsp.h @@ -66,7 +66,7 @@ private: // subsequent requests can set the required authentication header. bool mNeedAuth; int respCode; - Authenticator* mAuthenticator; + zm::Authenticator* mAuthenticator; std::string mHttpSession; ///< Only for RTSP over HTTP sessions diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index fd9087afa..10ecf3475 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -24,6 +24,8 @@ #include #include +namespace zm { + Authenticator::Authenticator(std::string &username, std::string password) { #ifdef HAVE_GCRYPT_H // Special initialisation for libgcrypt @@ -227,3 +229,5 @@ void Authenticator::checkAuthResponse(std::string &response) { Debug( 2, "Didn't find auth line in %s", authLine.c_str()); } } + +} // namespace zm diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 23745f9c7..079dec639 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -32,6 +32,8 @@ #include #endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO +namespace zm { + enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 }; class Authenticator { public: @@ -62,4 +64,6 @@ private: int nc; }; +} // namespace zm + #endif // ZM_RTSP_AUTH_H From ca45ac23fa412b3b02e8dd601a3e7995b18c2574 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 18 Dec 2015 13:30:42 -0500 Subject: [PATCH 03/20] increase height of group edit window --- web/skins/classic/js/dark.js | 2 +- web/skins/classic/js/flat.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/dark.js b/web/skins/classic/js/dark.js index 06a940482..b8ab38888 100644 --- a/web/skins/classic/js/dark.js +++ b/web/skins/classic/js/dark.js @@ -42,7 +42,7 @@ var popupSizes = { 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 100 }, 'frames': { 'width': 500, 'height': 600 }, 'function': { 'width': 300, 'height': 92 }, - 'group': { 'width': 360, 'height': 180 }, + 'group': { 'width': 360, 'height': 300 }, 'groups': { 'width': 440, 'height': 220 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 1080, 'height': 720 }, diff --git a/web/skins/classic/js/flat.js b/web/skins/classic/js/flat.js index 3b1882250..fc03b91e0 100644 --- a/web/skins/classic/js/flat.js +++ b/web/skins/classic/js/flat.js @@ -42,7 +42,7 @@ var popupSizes = { 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 100 }, 'frames': { 'width': 500, 'height': 600 }, 'function': { 'width': 300, 'height': 140 }, - 'group': { 'width': 360, 'height': 180 }, + 'group': { 'width': 360, 'height': 300 }, 'groups': { 'width': 440, 'height': 220 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 1080, 'height': 720 }, From 983d87f69b84e062e9bf6976cef8fe27687a263e Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 18 Dec 2015 13:34:44 -0500 Subject: [PATCH 04/20] increase height and width of groups window to fit buttons in --- web/skins/classic/js/dark.js | 2 +- web/skins/classic/js/flat.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/dark.js b/web/skins/classic/js/dark.js index b8ab38888..f8867feda 100644 --- a/web/skins/classic/js/dark.js +++ b/web/skins/classic/js/dark.js @@ -43,7 +43,7 @@ var popupSizes = { 'frames': { 'width': 500, 'height': 600 }, 'function': { 'width': 300, 'height': 92 }, 'group': { 'width': 360, 'height': 300 }, - 'groups': { 'width': 440, 'height': 220 }, + 'groups': { 'width': 540, 'height': 420 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 1080, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, diff --git a/web/skins/classic/js/flat.js b/web/skins/classic/js/flat.js index fc03b91e0..6d5a89bf7 100644 --- a/web/skins/classic/js/flat.js +++ b/web/skins/classic/js/flat.js @@ -43,7 +43,7 @@ var popupSizes = { 'frames': { 'width': 500, 'height': 600 }, 'function': { 'width': 300, 'height': 140 }, 'group': { 'width': 360, 'height': 300 }, - 'groups': { 'width': 440, 'height': 220 }, + 'groups': { 'width': 540, 'height': 420 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 1080, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, From a0f8e8be4b84b4d4ae387e5339b03e9d09454711 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Fri, 18 Dec 2015 13:43:16 -0500 Subject: [PATCH 05/20] increase width and height of monitors popup to fit new fields --- web/skins/classic/js/dark.js | 2 +- web/skins/classic/js/flat.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/skins/classic/js/dark.js b/web/skins/classic/js/dark.js index f8867feda..f0561c78f 100644 --- a/web/skins/classic/js/dark.js +++ b/web/skins/classic/js/dark.js @@ -48,7 +48,7 @@ var popupSizes = { 'log': { 'width': 1080, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, 'logout': { 'width': 260, 'height': 100 }, - 'monitor': { 'width': 525, 'height': 700 }, + 'monitor': { 'width': 600, 'height': 780 }, 'monitorpreset':{ 'width': 440, 'height': 200 }, 'monitorprobe': { 'width': 500, 'height': 240 }, 'monitorselect':{ 'width': 160, 'height': 200 }, diff --git a/web/skins/classic/js/flat.js b/web/skins/classic/js/flat.js index 9b04ca16d..a90fa9987 100644 --- a/web/skins/classic/js/flat.js +++ b/web/skins/classic/js/flat.js @@ -48,7 +48,7 @@ var popupSizes = { 'log': { 'width': 1080, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, 'logout': { 'width': 260, 'height': 100 }, - 'monitor': { 'width': 550, 'height': 700 }, + 'monitor': { 'width': 600, 'height': 780 }, 'monitorpreset':{ 'width': 440, 'height': 200 }, 'monitorprobe': { 'width': 500, 'height': 240 }, 'monitorselect':{ 'width': 160, 'height': 200 }, From 9e2b1ba3cb9cdf1d1393fb4e340038dfd5beedc8 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Fri, 18 Dec 2015 15:08:40 -0500 Subject: [PATCH 06/20] remove hardcoded version --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 2d376f03d..1e7f35737 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,9 @@ copyright = u'2014, https://github.com/ZoneMinder/ZoneMinder/graphs/contributors # built documents. # # The short X.Y version. -version = '1.28.1' +#version = '1.28.1' # The full version, including alpha/beta/rc tags. -release = '1.28.1' +#release = '1.28.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 511421bff1f864156fac15255d5449e9a25f2427 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Fri, 18 Dec 2015 15:08:56 -0500 Subject: [PATCH 07/20] rewrite --- docs/installationguide/ubuntu.rst | 540 +++++++++++++++++++++++------- 1 file changed, 428 insertions(+), 112 deletions(-) diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index b95d46a89..fa065cb96 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -1,64 +1,466 @@ Ubuntu ====== -Option A: Install a ready made package ---------------------------------------- +.. contents:: -Installation procedure (common for all versions of Ubuntu) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Easy Way: Install ZoneMinder from a package (Ubuntu 15.x+) +----------------------------------------------------------- +These instructions are for a brand new ubuntu 15.04 system which does not have ZM installed. -It is important that you first apply any system software upgrades first to Ubuntu, especially if you have just created a new image of Ubuntu. -Not doing this may cause the PPA process to fail and complain about various unmet dependencies. - -If you also plan to install the database in the same server (which is typically the case), first do: +**Step 1**: Make sure we add the correct packages :: - sudo apt-get install mysql-server + sudo add-apt-repository ppa:iconnor/zoneminder-master + sudo apt-get update -This will ask you for a user and password to configure for Zoneminder. -Note that when you install the PPA, it will also create a username of zmuser and a password of zmpass irrespective of what you select at this stage - -Now add the ppa repository path: +if you don't have mysql already installed: :: - sudo apt-add-repository ppa:iconnor/zoneminder + sudo apt-get install mysql-server + +This will ask you to set up a master password for the DB (you are asked for the mysql root password when installing mysql server). + +**Step 2**: Install ZoneMinder -Once you have updated the repository then update and install the package.: - :: - sudo apt-get update - sudo apt-get install zoneminder + sudo apt-get install zoneminder +**Step 3**: Configure the Database +:: + sudo 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';" -Post Install steps for Ubuntu 15.x or newer (systemd) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +You don't really need this, but no harm (needed if you are upgrading) :: sudo /usr/bin/zmupdate.pl + +**Step 4**: Configure systemd to recognize ZoneMinder and configure Apache correctly: + +:: + sudo systemctl enable zoneminder sudo a2enconf zoneminder + sudo a2enmod cgi + sudo chown -R www-data:www-data /usr/share/zoneminder/ + + +We need this for API routing to work: + +:: + + sudo a2enmod rewrite + +This is probably a bug with iconnor's PPA as of Oct 3, 2015 with package 1.28.107. After installing, ``zm.conf`` does not have the right read permissions, so we need to fix that. This may go away in future PPA releases: + +:: + + sudo chown www-data:www-data /etc/zm/zm.conf + +We also need to install php5-gd (as of 1.28.107, this is not installed) + +:: + + sudo apt-get install php5-gd + +**Step 5**: Edit Timezone in PHP + +:: + + vi /etc/php5/apache2/php.ini + +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see this: http://php.net/manual/en/timezones.php) + +In my case: + +:: + + date.timezone = America/New_York + +**Step 6**: Restart services + +:: + sudo service apache2 reload - systemctl restart zoneminder + sudo systemctl restart zoneminder -You should now be able to view the zoneminder interface at ``http://localhost/zm`` (replace localhost with your server IP if you are accessing it remotely) -.. image:: images/zm_first_screen_post_install.png +**Step 7: make sure live streaming works**: Make sure you can view Monitor streams: + +startup ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + + +**Step 8**: If you have changed your DB login/password from zmuser/zmpass, the API won't know about it + +If you changed the DB password **after** installing ZM, the APIs will not be able to connect to the DB. + +If you have, go to ``zoneminder/www/api/app/Config`` & Edit ``database.php`` + +There is a class there called ``DATABASE_CONFIG`` - change the ``$default`` array to reflect your new details. Example: + +:: + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'mynewDBusername', + 'password' => 'mynewDBpassword' + 'database' => 'zm', + 'prefix' => '', + //'encoding' => 'utf8', + ); + + +You are done. Lets proceed to make sure everything works: + +Making sure ZM and APIs work: + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + + +**Then your APIs are working** + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** your installation is complete + + +Easy Way: Install ZoneMinder from a package (Ubuntu 14.x) +----------------------------------------------------------- +**These instructions are for a brand new ubuntu 14.x system which does not have ZM installed.** + +**Step 1:** Install ZoneMinder + +:: + + sudo add-apt-repository ppa:iconnor/zoneminder-master + sudo apt-get update + sudo apt-get install zoneminder + +(just press OK for the prompts you get) + +**Step 2:** Set up DB + +:: + + sudo 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';" + +**Step 3:** Set up Apache + +:: + + sudo a2enconf zoneminder + sudo a2enmod rewrite + sudo a2enmod cgi + +**Step 4:**:Some tweaks that will be needed: + +Edit /etc/init.d/zoneminder: + +add a ``sleep 10`` right after line 25 that reads ``echo -n "Starting $prog:"`` +(The reason we need this sleep is to make sure ZM starts after mysqld starts) + +As of Oct 3 2015, zm.conf is not readable by ZM. This is likely a bug and will go away in the next package + +:: + + sudo chown www-data:www-data /etc/zm/zm.conf + + + +**Step 5**: If you have changed your DB login/password + +If you changed the DB password **after** installing ZM, the APIs will not be able to connect to the DB. + +If you have, go to zoneminder/www/api/app/Config & Edit ``database.php`` + +There is a class there called ``DATABASE_CONFIG`` - change the ``$default`` array to reflect your new details. Example: + +:: + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'mynewDBusername', + 'password' => 'mynewDBpassword' + 'database' => 'zm', + 'prefix' => '', + //'encoding' => 'utf8',` + ); + +We also need to install php5-gd (as of 1.28.107, this is not installed) + +:: + + sudo apt-get install php5-gd + + +**Step 6**: Edit Timezone in PHP + +vi /etc/php5/apache2/php.ini +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see [this](http://php.net/manual/en/timezones.php)) + +In my case: + +:: + + date.timezone = America/New_York + + +**Step 7: make sure live streaming works**: Make sure you can view Monitor streams: + +startup ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + + + +restart: + +:: + + sudo service apache2 restart + /etc/init.d/zoneminder restart + +**Step 8**: Making sure ZM and APIs work: (optional - only if you need APIs) + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + +Then your APIs are working + +Make sure you can view Monitor View: +1. Open up ZM, configure your monitors and verify you can view Monitor feeds. +2. If not, open up ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** Your installation is complete -Post install steps for Ubuntu 14.x or older (SystemV) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Harder Way: Build Package From Source +------------------------------------------- +(These instructions assume installation from source on a ubuntu 15.x+ system) + +**Step 1:** First make sure you have the needed tools + +:: + + sudo apt-get update + sudo apt-get install cmake git + +**Step 2:** Next up make sure you have all the dependencies + +:: + + sudo apt-get install apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2 libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch policykit-1 libpolkit-gobject-1-dev libextutils-pkgconfig-perl libcurl3 libvlc-dev libcurl4-openssl-dev curl php5-gd + +(you are asked for the mysql root password when installing mysql server - put in a password that you'd like). + +**Step 3:** Download ZoneMinder source code and compile+install: + +:: + + git clone https://github.com/ZoneMinder/ZoneMinder.git + cd ZoneMinder/ + git submodule init + git submodule update + cmake . + make + sudo make install + +**Step 4:** Now make sure your symlinks to events and images are set correctly: + +:: + + sudo ./zmlinkcontent.sh + +**Step 5:** Now lets make sure ZM has DB permissions to write to the DB: + +:: + + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + +**Step 6:** Now lets create the DB & its tables that ZM needs + +:: + + mysql -uroot -p + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + + Alias /zm /usr/local/share/zoneminder/www + + php_flag register_globals off + Options Indexes FollowSymLinks + + DirectoryIndex index.php + + + + + AllowOverride All + + +**Step 11:** Now lets make sure ZM can read/write to the zoneminder directory: + +:: + + sudo chown -R www-data:www-data /usr/local/share/zoneminder/ + + +**Step 12:** Make sure you can view Monitor View + +1. Open up ZM, configure your monitors and verify you can view Monitor feeds +2. If not, open up ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM + +**Step 13**: Edit Timezone in PHP + +vi /etc/php5/apache2/php.ini +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see http://php.net/manual/en/timezones.php) + +In my case: + +:: + + date.timezone = America/New_York + +**Step 14:** Finally, lets make a config change to apache (needed for htaccess overrides to work for APIs) +Edit /etc/apache2/apache2.conf and add this: + +:: + + + AllowOverride All + Require all granted + + +Restart apache + +:: + + sudo service apache2 reload + +You are done. Lets proceed to make sure everything works: + +Making sure ZM and APIs work: + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + +Then your APIs are working + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** your installation is complete + Suggested changes to MySQL (Optional but recommended) -""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +------------------------------------------------------ For most of you Zoneminder will run just fine with the default MySQL settings. There are a couple of settings that may, in time, provide beneficial especially if you have a number of cameras and many events with a lot of files. One setting we recommend is the "innodb_file_per_table" This will be a default setting in MySQL 5.6 but should be added in MySQL 5.5 which comes with Ubuntu 14.04. A description can be found here: http://dev.mysql.com/doc/refman/5.5/en/innodb-multiple-tablespaces.html To add "innodb_file_per_table" edit the my.cnf file: @@ -72,90 +474,4 @@ Save and exit. Restart MySQL ``service mysql restart`` -Adding a sleep for mysql dependency (Ubuntu 14.x and below only) -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -We recommend you add a "sleep" command just after ``start() {`` in ``/etc/init.d/zoneminder`` to make sure mysql starts before ZoneMinder does. To do this, -simply modify ``/etc/init.d/zoneminder`` at around line 25 (where you will find the start function) to look like this: - -:: - - start() { - echo -n "Making sure mysql started... Sleeping for 10 seconds..." - sleep 10 - echo -n "Starting $prog: " - -Making Apache aware of ZoneMinder -"""""""""""""""""""""""""""""""""""""""""""" - -Next, we need to make sure apache knows about zoneminder's configuration for apache. - -:: - - ln -s /etc/zm/apache.conf /etc/apache2/conf-available/zoneminder.conf - -If you are upgrading from an old version: - -:: - - sudo /usr/bin/zmupdate.pl - -Then look for ``/etc/apache2/conf-enable/zm.conf`` - if you have it, please remove it - -:: - - a2enconf zoneminder - adduser www-data video - - -lets make sure we restart apache: - -:: - - service apache2 restart - - -You should now be able to view the zoneminder interface at ``http://localhost/zm`` (replace localhost with your server IP if you are accessing it remotely) - -.. image:: images/zm_first_screen_post_install.png - - -Finally, in the zoneminder web interface, go to Options->Paths, change PATH_ZMS to ``/zm/cgi-bin/nph-zms`` - - -Option B: Build Package From Source -------------------------------------------- - -A fresh build based on master branch running Ubuntu 1204 LTS. Will likely work for other versions as well.:: - - root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; - - root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; - root@host:~# cd zoneminder; - root@host:~# ln -s distros/ubuntu1204 debian; - root@host:~# dpkg-checkbuilddeps; - root@host:~# dpkg-buildpackage; - - -One level above you'll now find a deb package matching the architecture of the build host\::: - - root@host:~# ls -1 ~/zoneminder\*; - /root/zoneminder_1.26.4-1_amd64.changes - /root/zoneminder_1.26.4-1_amd64.deb - /root/zoneminder_1.26.4-1.dsc - /root/zoneminder_1.26.4-1.tar.gz - - -The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. - -To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed.:: - - root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; - root@host:~# dpkg-reconfigure zoneminder; - -Alternatively you may also use gdebi to automatically resolve dependencies during installation\::: - - root@host:~# aptitude install -y gdebi; - root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; - - sudo apt-get install apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg-turbo8-dev libjpeg-turbo8 apache2-mpm-prefork libapache2-mod-php5 php5-cli From b444061cc6993618fc56805381f276f7087f58a4 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Fri, 18 Dec 2015 15:11:05 -0500 Subject: [PATCH 08/20] removed -master from ppa - when 1.29 releases it will be stable --- docs/installationguide/ubuntu.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index fa065cb96..1d5412077 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -11,7 +11,7 @@ These instructions are for a brand new ubuntu 15.04 system which does not have Z :: - sudo add-apt-repository ppa:iconnor/zoneminder-master + sudo add-apt-repository ppa:iconnor/zoneminder sudo apt-get update if you don't have mysql already installed: @@ -158,7 +158,7 @@ Easy Way: Install ZoneMinder from a package (Ubuntu 14.x) :: - sudo add-apt-repository ppa:iconnor/zoneminder-master + sudo add-apt-repository ppa:iconnor/zoneminder sudo apt-get update sudo apt-get install zoneminder From 3fe64f8ce656c97a252e25fda442622f8911991d Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 13:38:54 -0500 Subject: [PATCH 09/20] rtd theme --- docs/conf.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 1e7f35737..11d1fb392 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -98,14 +98,15 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +#html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -html_theme_options = { - "stickysidebar": "true" -} +#html_theme_options = { +# "stickysidebar": "true" +#} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -129,8 +130,8 @@ html_theme_options = { # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] -html_style='zmstyles.css' +#html_static_path = ['_static'] +#html_style='zmstyles.css' # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied From 2763c3d92ed8235967c15c5126ec9b92e5e278e3 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 13:40:21 -0500 Subject: [PATCH 10/20] deleted --- docs/_static/zmstyles.css | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 docs/_static/zmstyles.css diff --git a/docs/_static/zmstyles.css b/docs/_static/zmstyles.css deleted file mode 100644 index 8f59f3910..000000000 --- a/docs/_static/zmstyles.css +++ /dev/null @@ -1,13 +0,0 @@ -@import url("default.css"); - -div.admonition-note { -border-top: 2px solid red; -border-bottom: 2px solid red; -border-left: 2px solid red; -border-right: 2px solid red; -background-color: #ff6347 -} - -img { - border: 1px solid black !important; -} From c4bdb127ce4b4c98ea988dfb025a79425e82b1c4 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 16:53:30 -0500 Subject: [PATCH 11/20] fixed custom style breaking rtd theme --- docs/conf.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 11d1fb392..109161b24 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,6 +15,9 @@ import sys import os +def setup(app): + app.add_stylesheet('zmstyle.css') + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -130,7 +133,7 @@ html_theme = 'sphinx_rtd_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +html_static_path = ['_static'] #html_style='zmstyles.css' # Add any extra paths that contain custom files (such as robots.txt or From cbea550d70f58f4d8d2ba2fd137d99623d17bbd5 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 16:53:47 -0500 Subject: [PATCH 12/20] custom theming for ZM --- docs/_static/zmstyle.css | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/_static/zmstyle.css diff --git a/docs/_static/zmstyle.css b/docs/_static/zmstyle.css new file mode 100644 index 000000000..b8f0235f3 --- /dev/null +++ b/docs/_static/zmstyle.css @@ -0,0 +1,3 @@ +img { + border: 1px solid black !important; + } From 27b1b37d5b0f549aa55680acf4cdd7ab471483b3 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 17:43:36 -0500 Subject: [PATCH 13/20] added opt_use_api --- .../lib/ZoneMinder/ConfigData.pm.in | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 7fff3d62e..9a065f8cf 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -357,7 +357,23 @@ our @options = type => $types{boolean}, category => "system", }, - # PP - Google reCaptcha settings + { + name => "ZM_OPT_USE_API", + default => "yes", + description => "Enable ZoneMinder APIs", + help => qqq(" + ZoneMinder now features a new API using which 3rd party + applications can interact with ZoneMinder data. It is + STRONGLY recommended that you enable authentication along + with APIs. Note that the APIs return sensitive data like + Monitor access details which are configured as JSON objects. + Which is why we recommend you enabling authentication, especially + if you are exposing your ZM instance on the Internet. + "), + type => $types{boolean}, + category => "system", + }, +# PP - Google reCaptcha settings { name => "ZM_OPT_USE_GOOG_RECAPTCHA", default => "no", @@ -410,6 +426,7 @@ our @options = type => $types {string}, category => "system", }, + { name => "ZM_DIR_EVENTS", From 028c9b956c59185421b4e4707e2b8a4a55a78c09 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 17:44:02 -0500 Subject: [PATCH 14/20] check for opt_use_api, also pull in user roles support --- web/api/app/Controller/AppController.php | 51 +++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php index 1d85115e8..8494aaa6c 100644 --- a/web/api/app/Controller/AppController.php +++ b/web/api/app/Controller/AppController.php @@ -58,14 +58,63 @@ class AppController extends Controller { // Also checking to do this only if ZM_OPT_USE_AUTH is on public function beforeFilter() { $this->loadModel('Config'); + + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_API')); + $config = $this->Config->find('first', $options); + $zmOptApi = $config['Config']['Value']; + + if ($zmOptApi !='1') + { + throw new UnauthorizedException(__('API Disabled')); + return; + } + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')); $config = $this->Config->find('first', $options); $zmOptAuth = $config['Config']['Value']; if (!$this->Session->Read('user.Username') && ($zmOptAuth=='1')) { - throw new NotFoundException(__('Not Authenticated')); + throw new UnauthorizedException(__('Not Authenticated')); return; } + // PP #1155 fixes + else + { + $this->loadModel('User'); + $loggedinUser = $this->Session->Read('user.Username'); + $isEnabled = $this->Session->Read('user.Enabled'); + // PP - this will likely never happen as if its + // not enabled, login will fail and Not Auth will be returned + // however, keeping this here for now + if ($isEnabled != "1" && $zmOptAuth=="1") + { + throw new UnauthorizedException(__('User is not enabled')); + return; + } + + if ($zmOptAuth=='1') + { + $options = array ('conditions' => array ('User.Username' => $loggedinUser)); + $userMonitors = $this->User->find('first', $options); + $this->Session->Write('allowedMonitors',$userMonitors['User']['MonitorIds']); + $this->Session->Write('streamPermission',$userMonitors['User']['Stream']); + $this->Session->Write('eventPermission',$userMonitors['User']['Events']); + $this->Session->Write('controlPermission',$userMonitors['User']['Control']); + $this->Session->Write('systemPermission',$userMonitors['User']['System']); + $this->Session->Write('monitorPermission',$userMonitors['User']['Monitors']); + } + else // if auth is not on, you can do everything + { + //$userMonitors = $this->User->find('first', $options); + $this->Session->Write('allowedMonitors',''); + $this->Session->Write('streamPermission','View'); + $this->Session->Write('eventPermission','Edit'); + $this->Session->Write('controlPermission','Edit'); + $this->Session->Write('systemPermission','Edit'); + $this->Session->Write('monitorPermission','Edit'); + } + } + } From f56688e7c2e1012dd002866d5c5a6b5a334a1fb4 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 17:44:16 -0500 Subject: [PATCH 15/20] initial user role support --- web/api/app/Controller/EventsController.php | 130 +++++++++++++++----- 1 file changed, 96 insertions(+), 34 deletions(-) diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 988c19110..e82e7844e 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -14,6 +14,18 @@ class EventsController extends AppController { */ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); +//# PP #1155 +public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('eventPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + /** * index method * @@ -22,6 +34,19 @@ class EventsController extends AppController { */ public function index() { $this->Event->recursive = -1; + + //PP #1155 + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } + if ($this->request->params['named']) { $this->FilterComponent = $this->Components->load('Filter'); @@ -49,14 +74,15 @@ class EventsController extends AppController { 'limit' => '100', 'order' => array('StartTime', 'MaxScore'), 'paramType' => 'querystring', - 'conditions' => $conditions + // PP #1155 + 'conditions' => array (array($conditions, $mon_options)) ); $events = $this->Paginator->paginate('Event'); // For each event, get its thumbnail data (path, width, height) foreach ($events as $key => $value) { - // PP - $thumbData = $this->createThumbnail($value['Event']['Id']); - $thumbData =""; + //$thumbData = $this->createThumbnail($value['Event']['Id']); + $thumbData = ""; $events[$key]['thumbData'] = $thumbData; } @@ -71,41 +97,56 @@ class EventsController extends AppController { * @param string $id * @return void */ - public function view($id = null) { - $this->loadModel('Config'); - $configs = $this->Config->find('list', array( - 'fields' => array('Name', 'Value'), - 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) - )); + public function view($id = null) +{ + $this->loadModel('Config'); + $configs = $this->Config->find('list', array( + 'fields' => array('Name', 'Value'), + 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) + )); - $this->Event->recursive = 1; - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } - $options = array('conditions' => array('Event.' . $this->Event->primaryKey => $id)); - $event = $this->Event->find('first', $options); + $this->Event->recursive = 1; + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } - $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; - $event['Event']['BasePath'] = $path; + //PP #1155 + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); - # Get the previous and next events for any monitor - $this->Event->id = $id; + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } + + $options = array('conditions' => array(array('Event.' . $this->Event->primaryKey => $id), $mon_options)); + $event = $this->Event->find('first', $options); + + $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; + $event['Event']['BasePath'] = $path; + + # Get the previous and next events for any monitor + $this->Event->id = $id; $event_neighbors = $this->Event->find('neighbors'); - $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; - $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; + $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; + $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; - # Also get the previous and next events for the same monitor - $event_monitor_neighbors = $this->Event->find('neighbors', array( + # Also get the previous and next events for the same monitor + $event_monitor_neighbors = $this->Event->find('neighbors', array( 'conditions'=>array('Event.MonitorId'=>$event['Event']['MonitorId']) - )); - $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; - $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + )); + $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; + $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + + $this->set(array( + 'event' => $event, + '_serialize' => array('event') + )); + } - $this->set(array( - 'event' => $event, - '_serialize' => array('event') - )); - } /** * add method @@ -113,6 +154,14 @@ class EventsController extends AppController { * @return void */ public function add() { + + //PP - #1155 + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + if ($this->request->is('post')) { $this->Event->create(); if ($this->Event->save($this->request->data)) { @@ -131,6 +180,14 @@ class EventsController extends AppController { * @return void */ public function edit($id = null) { + + //PP - #1155 + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->Event->id = $id; if (!$this->Event->exists($id)) { @@ -157,15 +214,20 @@ class EventsController extends AppController { * @return void */ public function delete($id = null) { + //PP - #1155 + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } $this->Event->id = $id; if (!$this->Event->exists()) { throw new NotFoundException(__('Invalid event')); } $this->request->allowMethod('post', 'delete'); if ($this->Event->delete()) { - // PP - lets make sure the frame table entry is removed too - $this->loadModel('Frame'); - $this->Frame->delete(); + //$this->loadModel('Frame'); + //$this->Event->Frame->delete(); return $this->flash(__('The event has been deleted.'), array('action' => 'index')); } else { return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index')); From e812398fb9244967ae74d57babc242b2f1815624 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 17:44:39 -0500 Subject: [PATCH 16/20] initial user role support --- web/api/app/Controller/HostController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index ee5ecd30f..2e2c49551 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -101,7 +101,10 @@ class HostController extends AppController { function getVersion() { $version = Configure::read('ZM_VERSION'); - $apiversion = Configure::read('ZM_API_VERSION'); + // PP - not going to use the ZM_API_VERSION + // requires recompilation and dependency on ZM upgrade + //$apiversion = Configure::read('ZM_API_VERSION'); + $apiversion = '1.0'; $this->set(array( 'version' => $version, From b4fdaa9b4d032b9b62285738bab1bb1ffc058c1c Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 17:44:46 -0500 Subject: [PATCH 17/20] initial user role support --- web/api/app/Controller/MonitorsController.php | 68 +++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 3aeb31aa8..d13ed3a31 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -16,6 +16,20 @@ class MonitorsController extends AppController { */ public $components = array('Paginator', 'RequestHandler'); + +//# PP #1155 +public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('monitorPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + + /** * index method * @@ -23,7 +37,18 @@ class MonitorsController extends AppController { */ public function index() { $this->Monitor->recursive = 0; - $monitors = $this->Monitor->find('all'); + //PP #1155 + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $options = array('conditions'=>array('Monitor.Id'=> $allowedMonitors)); + } + else + { + $options=''; + } + $monitors = $this->Monitor->find('all',$options); $this->set(array( 'monitors' => $monitors, '_serialize' => array('monitors') @@ -42,7 +67,22 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - $options = array('conditions' => array('Monitor.' . $this->Monitor->primaryKey => $id)); + //PP #1155 + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + if (!empty($allowedMonitors)) + { + $restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors); + } + else + { + $restricted = ''; + } + + $options = array('conditions' => array( + array('Monitor.' . $this->Monitor->primaryKey => $id), + $restricted + ) + ); $monitor = $this->Monitor->find('first', $options); $this->set(array( 'monitor' => $monitor, @@ -57,6 +97,14 @@ class MonitorsController extends AppController { */ public function add() { if ($this->request->is('post')) { + + //PP - #1155 + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthotizedException(__('Insufficient privileges')); + return; + } + $this->Monitor->create(); if ($this->Monitor->save($this->request->data)) { $this->daemonControl($this->Monitor->id, 'start', $this->request->data); @@ -78,7 +126,12 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - + //PP - #1155 + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } if ($this->Monitor->save($this->request->data)) { $message = 'Saved'; } else { @@ -90,8 +143,7 @@ class MonitorsController extends AppController { '_serialize' => array('message') )); // PP - restart this monitor after change - $this->daemonControl($this->Monitor->id, 'restart', $this->request->data); - + $this->daemonControl($this->Monitor->id, 'restart', $this->request->data); } /** @@ -106,6 +158,12 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists()) { throw new NotFoundException(__('Invalid monitor')); } + //PP - #1155 + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } $this->request->allowMethod('post', 'delete'); $this->daemonControl($this->Monitor->id, 'stop'); From c4461c328eddb5a82f1dc5755ab8a955bee86333 Mon Sep 17 00:00:00 2001 From: arjunrc Date: Sat, 19 Dec 2015 17:45:03 -0500 Subject: [PATCH 18/20] initial user role support --- web/api/app/Model/User.php | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 web/api/app/Model/User.php diff --git a/web/api/app/Model/User.php b/web/api/app/Model/User.php new file mode 100644 index 000000000..9d9dd7f85 --- /dev/null +++ b/web/api/app/Model/User.php @@ -0,0 +1,32 @@ + Date: Sat, 19 Dec 2015 18:36:38 -0500 Subject: [PATCH 19/20] PP - Gone,flitted away, Taken the starts from the night and sun, From the day! Gone, and a cloud in my heart. - Tennyson --- web/api/app/Controller/AppController.php | 8 ++++---- .../app/Controller/Component/ImageComponent.php | 2 +- web/api/app/Controller/ConfigsController.php | 2 +- web/api/app/Controller/EventsController.php | 16 ++++++++-------- web/api/app/Controller/HostController.php | 2 +- web/api/app/Controller/MonitorsController.php | 14 +++++++------- web/api/app/Model/User.php | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php index 8494aaa6c..d961269d3 100644 --- a/web/api/app/Controller/AppController.php +++ b/web/api/app/Controller/AppController.php @@ -34,7 +34,7 @@ class AppController extends Controller { use CrudControllerTrait; public $components = [ - 'Session', // PP - We are going to use SessionHelper to check PHP session vars + 'Session', // We are going to use SessionHelper to check PHP session vars 'RequestHandler', 'Crud.Crud' => [ 'actions' => [ @@ -49,7 +49,7 @@ class AppController extends Controller { ] ]; - //PP - Global beforeFilter function + // Global beforeFilter function //Zoneminder sets the username session variable // to the logged in user. If this variable is set // then you are logged in @@ -77,13 +77,13 @@ class AppController extends Controller { throw new UnauthorizedException(__('Not Authenticated')); return; } - // PP #1155 fixes + // #1155 fixes else { $this->loadModel('User'); $loggedinUser = $this->Session->Read('user.Username'); $isEnabled = $this->Session->Read('user.Enabled'); - // PP - this will likely never happen as if its + // this will likely never happen as if its // not enabled, login will fail and Not Auth will be returned // however, keeping this here for now if ($isEnabled != "1" && $zmOptAuth=="1") diff --git a/web/api/app/Controller/Component/ImageComponent.php b/web/api/app/Controller/Component/ImageComponent.php index 68a254031..f8ed7a533 100644 --- a/web/api/app/Controller/Component/ImageComponent.php +++ b/web/api/app/Controller/Component/ImageComponent.php @@ -48,7 +48,7 @@ class ImageComponent extends Component { $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath; //$thumbFile = ZM_DIR_EVENTS."/".$thumbPath; $thumbFile = $thumbPath; - // PP: This segment of code results in errors when trying to get Events API + // This segment of code results in errors when trying to get Events API // This actually seems to be generating images for the angular UI web view // and should not be a part of the API anyway // I've commented it so events APIs continue to work diff --git a/web/api/app/Controller/ConfigsController.php b/web/api/app/Controller/ConfigsController.php index e70978246..48f50ae8e 100644 --- a/web/api/app/Controller/ConfigsController.php +++ b/web/api/app/Controller/ConfigsController.php @@ -15,7 +15,7 @@ class ConfigsController extends AppController { public $components = array('RequestHandler'); /** - * PP - resolves the issue of not returning all config parameters + * resolves the issue of not returning all config parameters * refer https://github.com/ZoneMinder/ZoneMinder/issues/953 * index method * diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index e82e7844e..28bef7a64 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -14,7 +14,7 @@ class EventsController extends AppController { */ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); -//# PP #1155 +//#1155 public function beforeFilter() { parent::beforeFilter(); $canView = $this->Session->Read('eventPermission'); @@ -35,7 +35,7 @@ public function beforeFilter() { public function index() { $this->Event->recursive = -1; - //PP #1155 + // #1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) @@ -64,7 +64,7 @@ public function beforeFilter() { $this->Paginator->settings = array( // https://github.com/ZoneMinder/ZoneMinder/issues/995 // 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'], - // PP - 25 events per page which is what the above + // 25 events per page which is what the above // default is, is way too low for an API // changing this to 100 so we don't kill ZM // with many event APIs. In future, we can @@ -74,7 +74,7 @@ public function beforeFilter() { 'limit' => '100', 'order' => array('StartTime', 'MaxScore'), 'paramType' => 'querystring', - // PP #1155 + // #1155 'conditions' => array (array($conditions, $mon_options)) ); $events = $this->Paginator->paginate('Event'); @@ -110,7 +110,7 @@ public function beforeFilter() { throw new NotFoundException(__('Invalid event')); } - //PP #1155 + // #1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) @@ -155,7 +155,7 @@ public function beforeFilter() { */ public function add() { - //PP - #1155 + // - #1155 if ($this->Session->Read('eventPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); @@ -181,7 +181,7 @@ public function beforeFilter() { */ public function edit($id = null) { - //PP - #1155 + // - #1155 if ($this->Session->Read('eventPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); @@ -214,7 +214,7 @@ public function beforeFilter() { * @return void */ public function delete($id = null) { - //PP - #1155 + //- #1155 if ($this->Session->Read('eventPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index 2e2c49551..57b210a7e 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -101,7 +101,7 @@ class HostController extends AppController { function getVersion() { $version = Configure::read('ZM_VERSION'); - // PP - not going to use the ZM_API_VERSION + // not going to use the ZM_API_VERSION // requires recompilation and dependency on ZM upgrade //$apiversion = Configure::read('ZM_API_VERSION'); $apiversion = '1.0'; diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index d13ed3a31..3d493762f 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -17,7 +17,7 @@ class MonitorsController extends AppController { public $components = array('Paginator', 'RequestHandler'); -//# PP #1155 +//# 1155 public function beforeFilter() { parent::beforeFilter(); $canView = $this->Session->Read('monitorPermission'); @@ -37,7 +37,7 @@ public function beforeFilter() { */ public function index() { $this->Monitor->recursive = 0; - //PP #1155 + //#1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) @@ -67,7 +67,7 @@ public function beforeFilter() { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - //PP #1155 + //#1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) { @@ -98,7 +98,7 @@ public function beforeFilter() { public function add() { if ($this->request->is('post')) { - //PP - #1155 + //- #1155 if ($this->Session->Read('systemPermission') != 'Edit') { throw new UnauthotizedException(__('Insufficient privileges')); @@ -126,7 +126,7 @@ public function beforeFilter() { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - //PP - #1155 + //- #1155 if ($this->Session->Read('systemPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); @@ -142,7 +142,7 @@ public function beforeFilter() { 'message' => $message, '_serialize' => array('message') )); - // PP - restart this monitor after change + // - restart this monitor after change $this->daemonControl($this->Monitor->id, 'restart', $this->request->data); } @@ -158,7 +158,7 @@ public function beforeFilter() { if (!$this->Monitor->exists()) { throw new NotFoundException(__('Invalid monitor')); } - //PP - #1155 + // #1155 if ($this->Session->Read('systemPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); diff --git a/web/api/app/Model/User.php b/web/api/app/Model/User.php index 9d9dd7f85..c0392f51d 100644 --- a/web/api/app/Model/User.php +++ b/web/api/app/Model/User.php @@ -1,7 +1,7 @@ Date: Sat, 19 Dec 2015 19:04:27 -0500 Subject: [PATCH 20/20] removed issue # --- web/api/app/Controller/AppController.php | 1 - web/api/app/Controller/EventsController.php | 7 ------- web/api/app/Controller/MonitorsController.php | 6 ------ web/api/app/Model/User.php | 1 - 4 files changed, 15 deletions(-) diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php index d961269d3..5b39597b3 100644 --- a/web/api/app/Controller/AppController.php +++ b/web/api/app/Controller/AppController.php @@ -77,7 +77,6 @@ class AppController extends Controller { throw new UnauthorizedException(__('Not Authenticated')); return; } - // #1155 fixes else { $this->loadModel('User'); diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 28bef7a64..ef569cd7b 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -14,7 +14,6 @@ class EventsController extends AppController { */ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); -//#1155 public function beforeFilter() { parent::beforeFilter(); $canView = $this->Session->Read('eventPermission'); @@ -35,7 +34,6 @@ public function beforeFilter() { public function index() { $this->Event->recursive = -1; - // #1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) @@ -74,7 +72,6 @@ public function beforeFilter() { 'limit' => '100', 'order' => array('StartTime', 'MaxScore'), 'paramType' => 'querystring', - // #1155 'conditions' => array (array($conditions, $mon_options)) ); $events = $this->Paginator->paginate('Event'); @@ -110,7 +107,6 @@ public function beforeFilter() { throw new NotFoundException(__('Invalid event')); } - // #1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) @@ -155,7 +151,6 @@ public function beforeFilter() { */ public function add() { - // - #1155 if ($this->Session->Read('eventPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); @@ -181,7 +176,6 @@ public function beforeFilter() { */ public function edit($id = null) { - // - #1155 if ($this->Session->Read('eventPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); @@ -214,7 +208,6 @@ public function beforeFilter() { * @return void */ public function delete($id = null) { - //- #1155 if ($this->Session->Read('eventPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 3d493762f..9ab7461f9 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -17,7 +17,6 @@ class MonitorsController extends AppController { public $components = array('Paginator', 'RequestHandler'); -//# 1155 public function beforeFilter() { parent::beforeFilter(); $canView = $this->Session->Read('monitorPermission'); @@ -37,7 +36,6 @@ public function beforeFilter() { */ public function index() { $this->Monitor->recursive = 0; - //#1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) @@ -67,7 +65,6 @@ public function beforeFilter() { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - //#1155 $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); if (!empty($allowedMonitors)) { @@ -98,7 +95,6 @@ public function beforeFilter() { public function add() { if ($this->request->is('post')) { - //- #1155 if ($this->Session->Read('systemPermission') != 'Edit') { throw new UnauthotizedException(__('Insufficient privileges')); @@ -126,7 +122,6 @@ public function beforeFilter() { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - //- #1155 if ($this->Session->Read('systemPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); @@ -158,7 +153,6 @@ public function beforeFilter() { if (!$this->Monitor->exists()) { throw new NotFoundException(__('Invalid monitor')); } - // #1155 if ($this->Session->Read('systemPermission') != 'Edit') { throw new UnauthorizedException(__('Insufficient privileges')); diff --git a/web/api/app/Model/User.php b/web/api/app/Model/User.php index c0392f51d..8ef18d131 100644 --- a/web/api/app/Model/User.php +++ b/web/api/app/Model/User.php @@ -1,7 +1,6 @@