From 865a24d0bd8fc2d62d25a669b627c150c13567f5 Mon Sep 17 00:00:00 2001 From: Christophe Vandeplas Date: Thu, 15 Mar 2012 15:06:45 +0100 Subject: [PATCH] Migration to CakePHP 2.1. Most of the functionality migrated, Q&A review required. --- .gitignore | 5 +- app/.htaccess | 2 +- .../schema => Config/Schema}/db_acl.php | 19 +- app/Config/Schema/db_acl.sql | 40 + app/{config/schema => Config/Schema}/i18n.php | 15 +- app/Config/Schema/i18n.sql | 26 + .../schema => Config/Schema}/sessions.php | 15 +- app/Config/Schema/sessions.sql | 16 + app/{config => Config}/acl.ini.php | 14 +- app/Config/acl.php | 134 +++ app/Config/bootstrap.php | 139 +++ app/Config/core.php | 278 ++++++ app/Config/database.php | 17 + app/Config/email.php | 12 + app/{config => Config}/routes.php | 27 +- app/Console/Command/AppShell.php | 31 + .../components => Console/Command/Task}/empty | 0 app/{libs => Console/Templates}/empty | 0 app/Console/cake | 33 + app/Console/cake.bat | 32 + app/Console/cake.php | 33 + app/Controller/AppController.php | 107 +++ .../Component}/empty | 0 app/Controller/EventsController.php | 808 ++++++++++++++++++ app/Controller/PagesController.php | 82 ++ app/Controller/SignaturesController.php | 189 ++++ app/Controller/UsersController.php | 309 +++++++ app/{models/behaviors => Lib}/empty | 0 .../eng/LC_MESSAGES}/empty | 0 app/Model/AppModel.php | 34 + app/{plugins => Model/Behavior}/empty | 0 .../behaviors => Model/Datasource}/empty | 0 app/Model/Event.php | 155 ++++ app/Model/Signature.php | 249 ++++++ app/Model/User.php | 244 ++++++ app/{tests/cases/components => Plugin}/empty | 0 app/README.txt | 13 +- .../Case/Controller/Component}/empty | 0 .../Case/Controller/EventsControllerTest.php | 142 +++ .../Controller/SignaturesControllerTest.php | 142 +++ .../Case/Controller/UsersControllerTest.php | 142 +++ .../Case/Model/Behavior}/empty | 0 app/Test/Case/Model/EventTest.php | 37 + app/Test/Case/Model/GroupTest.php | 37 + app/Test/Case/Model/SignatureTest.php | 37 + app/Test/Case/Model/UserTest.php | 37 + .../models => Test/Case/View/Helper}/empty | 0 app/Test/Fixture/EventFixture.php | 41 + app/Test/Fixture/GroupFixture.php | 31 + app/Test/Fixture/SignatureFixture.php | 39 + app/Test/Fixture/UserFixture.php | 51 ++ app/{tests/fixtures => Test/Fixture}/empty | 0 app/{tests/groups => Vendor}/empty | 0 .../Elements}/actions_menu.ctp | 0 .../shells/tasks => View/Elements}/empty | 0 app/View/Emails/html/default.ctp | 25 + .../email => View/Emails}/text/body.ctp | 0 app/View/Emails/text/default.ctp | 19 + .../email => View/Emails}/text/new_event.ctp | 0 app/View/Errors/error400.ctp | 31 + .../errors => View/Errors}/error403.ctp | 0 app/View/Errors/error500.ctp | 28 + app/{views/events => View/Events}/add.ctp | 2 - app/{views/events => View/Events}/contact.ctp | 3 +- app/{views/events => View/Events}/edit.ctp | 2 - app/{views/events => View/Events}/export.ctp | 6 +- app/{views/events => View/Events}/index.ctp | 27 +- app/{views/events => View/Events}/nids.ctp | 0 app/{views/events => View/Events}/text.ctp | 0 app/{views/events => View/Events}/view.ctp | 48 +- app/View/Events/xml.ctp | 8 + app/View/Helper/AppHelper.php | 34 + .../Layouts/Emails}/html/default.ctp | 15 +- .../Layouts/Emails}/text/default.ctp | 9 +- app/View/Layouts/ajax.ctp | 19 + .../layouts => View/Layouts}/default.ctp | 34 +- app/View/Layouts/flash.ctp | 37 + app/View/Layouts/js/default.ctp | 2 + app/View/Layouts/rss/default.ctp | 14 + .../xml.ctp => View/Layouts/text/default.ctp} | 0 app/View/Layouts/xml/default.ctp | 1 + app/View/Pages/home.ctp | 188 ++++ .../shells/templates => View/Scaffolds}/empty | 0 .../signatures => View/Signatures}/add.ctp | 19 +- app/View/Signatures/edit.ctp | 25 + app/View/Signatures/index.ctp | 52 ++ .../signatures => View/Signatures}/search.ctp | 3 +- app/{views/users => View/Users}/add.ctp | 2 +- app/View/Users/admin_add.ctp | 28 + app/View/Users/admin_edit.ctp | 30 + app/View/Users/admin_index.ctp | 62 ++ app/View/Users/admin_view.ctp | 117 +++ app/{views/users => View/Users}/edit.ctp | 1 - app/{views/users => View/Users}/index.ctp | 0 app/{views/users => View/Users}/login.ctp | 0 .../users => View/Users}/memberslist.ctp | 17 +- app/{views/users => View/Users}/news.ctp | 6 + app/{views/users => View/Users}/terms.ctp | 2 +- app/View/Users/view.ctp | 58 ++ app/app_controller.php | 272 ------ app/app_error.php | 27 - app/config/bootstrap.php | 62 -- app/config/core.php | 317 ------- app/config/database.php | 87 -- app/controllers/events_controller.php | 773 ----------------- app/controllers/groups_controller.php | 75 -- app/controllers/signatures_controller.php | 208 ----- app/controllers/users_controller.php | 334 -------- app/index.php | 5 +- app/models/event.php | 102 --- app/models/group.php | 38 - app/models/signature.php | 210 ----- app/models/user.php | 222 ----- .../controllers/components/recaptcha.php | 143 ---- app/plugins/recaptcha/license.txt | 25 - .../locale/fre/LC_MESSAGES/recaptcha.po | 28 - app/plugins/recaptcha/locale/recaptcha.pot | 39 - .../recaptcha/models/behaviors/recaptcha.php | 71 -- app/plugins/recaptcha/readme.md | 57 -- .../tests/cases/behaviors/recaptcha.test.php | 73 -- .../tests/cases/components/recaptcha.test.php | 84 -- .../tests/cases/helpers/recaptcha.test.php | 67 -- .../tests/fixtures/article_fixture.php | 45 - .../recaptcha/views/helpers/recaptcha.php | 215 ----- .../email/html => tmp/cache/models}/empty | 0 .../email/text => tmp/cache/persistent}/empty | 0 app/{views/elements => tmp/cache/views}/empty | 0 app/{views/errors => tmp/logs}/empty | 0 app/{views/helpers => tmp/sessions}/empty | 0 .../layouts/email/html => tmp/tests}/empty | 0 app/views/events/xml.ctp | 4 - app/views/groups/add.ctp | 19 - app/views/groups/edit.ctp | 21 - app/views/groups/index.ctp | 49 -- app/views/groups/view.ctp | 72 -- app/views/layouts/email/text/empty | 0 app/views/layouts/js/empty | 0 app/views/layouts/rss/empty | 0 app/views/layouts/xml/empty | 0 app/views/pages/empty | 0 app/views/scaffolds/empty | 0 app/views/signatures/edit.ctp | 23 - app/views/signatures/index.ctp | 56 -- app/views/signatures/view.ctp | 41 - app/views/users/view.ctp | 63 -- app/webroot/.htaccess | 4 +- app/webroot/css.php | 96 --- app/webroot/css/cake.generic.css | 393 ++++++--- app/webroot/favicon.ico | Bin app/webroot/files/empty | 0 app/webroot/gpg.asc | 54 +- app/webroot/img/cake.icon.png | Bin app/webroot/img/cake.power.gif | Bin app/webroot/img/test-error-icon.png | Bin app/webroot/img/test-fail-icon.png | Bin app/webroot/img/test-pass-icon.png | Bin app/webroot/img/test-skip-icon.png | Bin app/webroot/index.php | 48 +- app/webroot/js/empty | 0 app/webroot/js/jquery.js | 18 - app/webroot/test.php | 56 +- 161 files changed, 4999 insertions(+), 4385 deletions(-) rename app/{config/schema => Config/Schema}/db_acl.php (92%) mode change 100755 => 100644 create mode 100644 app/Config/Schema/db_acl.sql rename app/{config/schema => Config/Schema}/i18n.php (89%) mode change 100755 => 100644 create mode 100644 app/Config/Schema/i18n.sql rename app/{config/schema => Config/Schema}/sessions.php (84%) mode change 100755 => 100644 create mode 100644 app/Config/Schema/sessions.sql rename app/{config => Config}/acl.ini.php (89%) mode change 100755 => 100644 create mode 100644 app/Config/acl.php create mode 100644 app/Config/bootstrap.php create mode 100644 app/Config/core.php create mode 100644 app/Config/database.php create mode 100644 app/Config/email.php rename app/{config => Config}/routes.php (62%) mode change 100755 => 100644 create mode 100644 app/Console/Command/AppShell.php rename app/{controllers/components => Console/Command/Task}/empty (100%) mode change 100755 => 100644 rename app/{libs => Console/Templates}/empty (100%) mode change 100755 => 100644 create mode 100755 app/Console/cake create mode 100644 app/Console/cake.bat create mode 100644 app/Console/cake.php create mode 100644 app/Controller/AppController.php rename app/{locale/eng/LC_MESSAGES => Controller/Component}/empty (100%) mode change 100755 => 100644 create mode 100644 app/Controller/EventsController.php create mode 100644 app/Controller/PagesController.php create mode 100644 app/Controller/SignaturesController.php create mode 100644 app/Controller/UsersController.php rename app/{models/behaviors => Lib}/empty (100%) mode change 100755 => 100644 rename app/{models/datasources => Locale/eng/LC_MESSAGES}/empty (100%) mode change 100755 => 100644 create mode 100644 app/Model/AppModel.php rename app/{plugins => Model/Behavior}/empty (100%) mode change 100755 => 100644 rename app/{tests/cases/behaviors => Model/Datasource}/empty (100%) mode change 100755 => 100644 create mode 100644 app/Model/Event.php create mode 100644 app/Model/Signature.php create mode 100644 app/Model/User.php rename app/{tests/cases/components => Plugin}/empty (100%) mode change 100755 => 100644 rename app/{tests/cases/controllers => Test/Case/Controller/Component}/empty (100%) mode change 100755 => 100644 create mode 100644 app/Test/Case/Controller/EventsControllerTest.php create mode 100644 app/Test/Case/Controller/SignaturesControllerTest.php create mode 100644 app/Test/Case/Controller/UsersControllerTest.php rename app/{tests/cases/helpers => Test/Case/Model/Behavior}/empty (100%) mode change 100755 => 100644 create mode 100644 app/Test/Case/Model/EventTest.php create mode 100644 app/Test/Case/Model/GroupTest.php create mode 100644 app/Test/Case/Model/SignatureTest.php create mode 100644 app/Test/Case/Model/UserTest.php rename app/{tests/cases/models => Test/Case/View/Helper}/empty (100%) mode change 100755 => 100644 create mode 100644 app/Test/Fixture/EventFixture.php create mode 100644 app/Test/Fixture/GroupFixture.php create mode 100644 app/Test/Fixture/SignatureFixture.php create mode 100644 app/Test/Fixture/UserFixture.php rename app/{tests/fixtures => Test/Fixture}/empty (100%) mode change 100755 => 100644 rename app/{tests/groups => Vendor}/empty (100%) mode change 100755 => 100644 rename app/{views/elements => View/Elements}/actions_menu.ctp (100%) rename app/{vendors/shells/tasks => View/Elements}/empty (100%) mode change 100755 => 100644 create mode 100644 app/View/Emails/html/default.ctp rename app/{views/elements/email => View/Emails}/text/body.ctp (100%) create mode 100644 app/View/Emails/text/default.ctp rename app/{views/elements/email => View/Emails}/text/new_event.ctp (100%) create mode 100644 app/View/Errors/error400.ctp rename app/{views/errors => View/Errors}/error403.ctp (100%) create mode 100644 app/View/Errors/error500.ctp rename app/{views/events => View/Events}/add.ctp (84%) rename app/{views/events => View/Events}/contact.ctp (88%) rename app/{views/events => View/Events}/edit.ctp (89%) rename app/{views/events => View/Events}/export.ctp (83%) rename app/{views/events => View/Events}/index.ctp (64%) rename app/{views/events => View/Events}/nids.ctp (100%) rename app/{views/events => View/Events}/text.ctp (100%) rename app/{views/events => View/Events}/view.ctp (65%) create mode 100755 app/View/Events/xml.ctp create mode 100644 app/View/Helper/AppHelper.php rename app/{views/layouts/email => View/Layouts/Emails}/html/default.ctp (60%) mode change 100755 => 100644 rename app/{views/layouts/email => View/Layouts/Emails}/text/default.ctp (65%) mode change 100755 => 100644 create mode 100644 app/View/Layouts/ajax.ctp rename app/{views/layouts => View/Layouts}/default.ctp (71%) mode change 100755 => 100644 create mode 100644 app/View/Layouts/flash.ctp create mode 100644 app/View/Layouts/js/default.ctp create mode 100644 app/View/Layouts/rss/default.ctp rename app/{views/layouts/xml/xml.ctp => View/Layouts/text/default.ctp} (100%) mode change 100755 => 100644 create mode 100644 app/View/Layouts/xml/default.ctp create mode 100644 app/View/Pages/home.ctp rename app/{vendors/shells/templates => View/Scaffolds}/empty (100%) mode change 100755 => 100644 rename app/{views/signatures => View/Signatures}/add.ctp (50%) create mode 100755 app/View/Signatures/edit.ctp create mode 100755 app/View/Signatures/index.ctp rename app/{views/signatures => View/Signatures}/search.ctp (76%) rename app/{views/users => View/Users}/add.ctp (93%) create mode 100644 app/View/Users/admin_add.ctp create mode 100644 app/View/Users/admin_edit.ctp create mode 100644 app/View/Users/admin_index.ctp create mode 100644 app/View/Users/admin_view.ctp rename app/{views/users => View/Users}/edit.ctp (94%) rename app/{views/users => View/Users}/index.ctp (100%) rename app/{views/users => View/Users}/login.ctp (100%) rename app/{views/users => View/Users}/memberslist.ctp (76%) rename app/{views/users => View/Users}/news.ctp (82%) rename app/{views/users => View/Users}/terms.ctp (99%) create mode 100755 app/View/Users/view.ctp delete mode 100755 app/app_controller.php delete mode 100755 app/app_error.php delete mode 100755 app/config/bootstrap.php delete mode 100755 app/config/core.php delete mode 100755 app/config/database.php delete mode 100755 app/controllers/events_controller.php delete mode 100755 app/controllers/groups_controller.php delete mode 100755 app/controllers/signatures_controller.php delete mode 100755 app/controllers/users_controller.php mode change 100755 => 100644 app/index.php delete mode 100755 app/models/event.php delete mode 100755 app/models/group.php delete mode 100755 app/models/signature.php delete mode 100755 app/models/user.php delete mode 100755 app/plugins/recaptcha/controllers/components/recaptcha.php delete mode 100755 app/plugins/recaptcha/license.txt delete mode 100755 app/plugins/recaptcha/locale/fre/LC_MESSAGES/recaptcha.po delete mode 100755 app/plugins/recaptcha/locale/recaptcha.pot delete mode 100755 app/plugins/recaptcha/models/behaviors/recaptcha.php delete mode 100755 app/plugins/recaptcha/readme.md delete mode 100755 app/plugins/recaptcha/tests/cases/behaviors/recaptcha.test.php delete mode 100755 app/plugins/recaptcha/tests/cases/components/recaptcha.test.php delete mode 100755 app/plugins/recaptcha/tests/cases/helpers/recaptcha.test.php delete mode 100755 app/plugins/recaptcha/tests/fixtures/article_fixture.php delete mode 100755 app/plugins/recaptcha/views/helpers/recaptcha.php rename app/{views/elements/email/html => tmp/cache/models}/empty (100%) mode change 100755 => 100644 rename app/{views/elements/email/text => tmp/cache/persistent}/empty (100%) mode change 100755 => 100644 rename app/{views/elements => tmp/cache/views}/empty (100%) mode change 100755 => 100644 rename app/{views/errors => tmp/logs}/empty (100%) mode change 100755 => 100644 rename app/{views/helpers => tmp/sessions}/empty (100%) mode change 100755 => 100644 rename app/{views/layouts/email/html => tmp/tests}/empty (100%) mode change 100755 => 100644 delete mode 100755 app/views/events/xml.ctp delete mode 100755 app/views/groups/add.ctp delete mode 100755 app/views/groups/edit.ctp delete mode 100755 app/views/groups/index.ctp delete mode 100755 app/views/groups/view.ctp delete mode 100755 app/views/layouts/email/text/empty delete mode 100755 app/views/layouts/js/empty delete mode 100755 app/views/layouts/rss/empty delete mode 100755 app/views/layouts/xml/empty delete mode 100755 app/views/pages/empty delete mode 100755 app/views/scaffolds/empty delete mode 100755 app/views/signatures/edit.ctp delete mode 100755 app/views/signatures/index.ctp delete mode 100755 app/views/signatures/view.ctp delete mode 100755 app/views/users/view.ctp mode change 100755 => 100644 app/webroot/.htaccess delete mode 100755 app/webroot/css.php mode change 100755 => 100644 app/webroot/css/cake.generic.css mode change 100755 => 100644 app/webroot/favicon.ico mode change 100755 => 100644 app/webroot/files/empty mode change 100755 => 100644 app/webroot/gpg.asc mode change 100755 => 100644 app/webroot/img/cake.icon.png mode change 100755 => 100644 app/webroot/img/cake.power.gif mode change 100755 => 100644 app/webroot/img/test-error-icon.png mode change 100755 => 100644 app/webroot/img/test-fail-icon.png mode change 100755 => 100644 app/webroot/img/test-pass-icon.png mode change 100755 => 100644 app/webroot/img/test-skip-icon.png mode change 100755 => 100644 app/webroot/index.php mode change 100755 => 100644 app/webroot/js/empty delete mode 100755 app/webroot/js/jquery.js mode change 100755 => 100644 app/webroot/test.php diff --git a/.gitignore b/.gitignore index 021ff0ed9..67b161f7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -/app/tmp /plugins /vendors +/lib +/build.properties +/build.xml /.project /.settings /.buildpath @@ -10,3 +12,4 @@ /index.php /README /app/tmp/sessions/sess_* +/app/tmp/logs/*.log diff --git a/app/.htaccess b/app/.htaccess index 0ed8662ea..fc3aac4b2 100644 --- a/app/.htaccess +++ b/app/.htaccess @@ -2,4 +2,4 @@ RewriteEngine on RewriteRule ^$ webroot/ [L] RewriteRule (.*) webroot/$1 [L] - \ No newline at end of file + \ No newline at end of file diff --git a/app/config/schema/db_acl.php b/app/Config/Schema/db_acl.php old mode 100755 new mode 100644 similarity index 92% rename from app/config/schema/db_acl.php rename to app/Config/Schema/db_acl.php index 2e3546f4c..7133acb19 --- a/app/config/schema/db_acl.php +++ b/app/Config/Schema/db_acl.php @@ -1,11 +1,12 @@ array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), 'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), 'model' => array('type'=>'string', 'null' => true), @@ -48,7 +49,7 @@ class DbAclSchema extends CakeSchema { 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) ); - var $aros = array( + public $aros = array( 'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), 'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10), 'model' => array('type'=>'string', 'null' => true), @@ -59,7 +60,7 @@ class DbAclSchema extends CakeSchema { 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) ); - var $aros_acos = array( + public $aros_acos = array( 'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), 'aro_id' => array('type'=>'integer', 'null' => false, 'length' => 10, 'key' => 'index'), 'aco_id' => array('type'=>'integer', 'null' => false, 'length' => 10), diff --git a/app/Config/Schema/db_acl.sql b/app/Config/Schema/db_acl.sql new file mode 100644 index 000000000..973995f5f --- /dev/null +++ b/app/Config/Schema/db_acl.sql @@ -0,0 +1,40 @@ +# $Id$ +# +# Copyright 2005-2011, Cake Software Foundation, Inc. +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE acos ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + model VARCHAR(255) DEFAULT '', + foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, + alias VARCHAR(255) DEFAULT '', + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE aros_acos ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + aro_id INTEGER(10) UNSIGNED NOT NULL, + aco_id INTEGER(10) UNSIGNED NOT NULL, + _create CHAR(2) NOT NULL DEFAULT 0, + _read CHAR(2) NOT NULL DEFAULT 0, + _update CHAR(2) NOT NULL DEFAULT 0, + _delete CHAR(2) NOT NULL DEFAULT 0, + PRIMARY KEY(id) +); + +CREATE TABLE aros ( + id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, + parent_id INTEGER(10) DEFAULT NULL, + model VARCHAR(255) DEFAULT '', + foreign_key INTEGER(10) UNSIGNED DEFAULT NULL, + alias VARCHAR(255) DEFAULT '', + lft INTEGER(10) DEFAULT NULL, + rght INTEGER(10) DEFAULT NULL, + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/app/config/schema/i18n.php b/app/Config/Schema/i18n.php old mode 100755 new mode 100644 similarity index 89% rename from app/config/schema/i18n.php rename to app/Config/Schema/i18n.php index 2c578fadc..377645796 --- a/app/config/schema/i18n.php +++ b/app/Config/Schema/i18n.php @@ -1,11 +1,12 @@ array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'), 'locale' => array('type'=>'string', 'null' => false, 'length' => 6, 'key' => 'index'), 'model' => array('type'=>'string', 'null' => false, 'key' => 'index'), diff --git a/app/Config/Schema/i18n.sql b/app/Config/Schema/i18n.sql new file mode 100644 index 000000000..5a053ff3c --- /dev/null +++ b/app/Config/Schema/i18n.sql @@ -0,0 +1,26 @@ +# $Id$ +# +# Copyright 2005-2011, Cake Software Foundation, Inc. +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE i18n ( + id int(10) NOT NULL auto_increment, + locale varchar(6) NOT NULL, + model varchar(255) NOT NULL, + foreign_key int(10) NOT NULL, + field varchar(255) NOT NULL, + content mediumtext, + PRIMARY KEY (id), +# UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field), +# INDEX I18N_LOCALE_ROW(locale, model, foreign_key), +# INDEX I18N_LOCALE_MODEL(locale, model), +# INDEX I18N_FIELD(model, foreign_key, field), +# INDEX I18N_ROW(model, foreign_key), + INDEX locale (locale), + INDEX model (model), + INDEX row_id (foreign_key), + INDEX field (field) +); \ No newline at end of file diff --git a/app/config/schema/sessions.php b/app/Config/Schema/sessions.php old mode 100755 new mode 100644 similarity index 84% rename from app/config/schema/sessions.php rename to app/Config/Schema/sessions.php index 287e43dd7..4e2ba5f55 --- a/app/config/schema/sessions.php +++ b/app/Config/Schema/sessions.php @@ -1,11 +1,12 @@ array('type'=>'string', 'null' => false, 'key' => 'primary'), 'data' => array('type'=>'text', 'null' => true, 'default' => NULL), 'expires' => array('type'=>'integer', 'null' => true, 'default' => NULL), diff --git a/app/Config/Schema/sessions.sql b/app/Config/Schema/sessions.sql new file mode 100644 index 000000000..18a68b4b3 --- /dev/null +++ b/app/Config/Schema/sessions.sql @@ -0,0 +1,16 @@ +# $Id$ +# +# Copyright 2005-2011, Cake Software Foundation, Inc. +# 1785 E. Sahara Avenue, Suite 490-204 +# Las Vegas, Nevada 89104 +# +# Licensed under The MIT License +# Redistributions of files must retain the above copyright notice. +# MIT License (http://www.opensource.org/licenses/mit-license.php) + +CREATE TABLE cake_sessions ( + id varchar(255) NOT NULL default '', + data text, + expires int(11) default NULL, + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/app/config/acl.ini.php b/app/Config/acl.ini.php old mode 100755 new mode 100644 similarity index 89% rename from app/config/acl.ini.php rename to app/Config/acl.ini.php index 2c68cb27e..1241aeff3 --- a/app/config/acl.ini.php +++ b/app/Config/acl.ini.php @@ -1,12 +1,11 @@ -; -; SVN FILE: $Id$ +; ;/** -; * ACL configuration +; * ACL Configuration ; * ; * -; * PHP versions 4 and 5 +; * PHP 5 ; * -; * CakePHP(tm) : Rapid Development Framework http://cakephp.org +; * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) ; * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) ; * ; * Licensed under The MIT License @@ -14,8 +13,7 @@ ; * ; * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) ; * @link http://cakephp.org CakePHP(tm) Project -; * @package cake -; * @subpackage cake.app.config +; * @package app.Config ; * @since CakePHP(tm) v 0.10.0.1076 ; * @license MIT License (http://www.opensource.org/licenses/mit-license.php) ; */ @@ -67,4 +65,4 @@ allow = aco3, aco4 [groupname-goes-here] deny = aco5, aco6 -allow = aco7, aco8 \ No newline at end of file +allow = aco7, aco8 diff --git a/app/Config/acl.php b/app/Config/acl.php new file mode 100644 index 000000000..83cfbc0f0 --- /dev/null +++ b/app/Config/acl.php @@ -0,0 +1,134 @@ +Auth->authorize = array('Actions' => array('actionPath' => 'controllers/'),...) + * + * Now, when a user (i.e. jeff) authenticates successfully and requests a controller action (i.e. /invoices/delete) + * that is not allowed by default (e.g. via $this->Auth->allow('edit') in the Invoices controller) then AuthComponent + * will ask the configured ACL interface if access is granted. Under the assumptions 1. and 2. this will be + * done via a call to Acl->check() with + * + * array('User' => array('username' => 'jeff', 'group_id' => 4, ...)) + * + * as ARO and + * + * '/controllers/invoices/delete' + * + * as ACO. + * + * If the configured map looks like + * + * $config['map'] = array( + * 'User' => 'User/username', + * 'Role' => 'User/group_id', + * ); + * + * then PhpAcl will lookup if we defined a role like User/jeff. If that role is not found, PhpAcl will try to + * find a definition for Role/4. If the definition isn't found then a default role (Role/default) will be used to + * check rules for the given ACO. The search can be expanded by defining aliases in the alias configuration. + * E.g. if you want to use a more readable name than Role/4 in your definitions you can define an alias like + * + * $config['alias'] = array( + * 'Role/4' => 'Role/editor', + * ); + * + * In the roles configuration you can define roles on the lhs and inherited roles on the rhs: + * + * $config['roles'] = array( + * 'Role/admin' => null, + * 'Role/accountant' => null, + * 'Role/editor' => null, + * 'Role/manager' => 'Role/editor, Role/accountant', + * 'User/jeff' => 'Role/manager', + * ); + * + * In this example manager inherits all rules from editor and accountant. Role/admin doesn't inherit from any role. + * Lets define some rules: + * + * $config['rules'] = array( + * 'allow' => array( + * '*' => 'Role/admin', + * 'controllers/users/(dashboard|profile)' => 'Role/default', + * 'controllers/invoices/*' => 'Role/accountant', + * 'controllers/articles/*' => 'Role/editor', + * 'controllers/users/*' => 'Role/manager', + * 'controllers/invoices/delete' => 'Role/manager', + * ), + * 'deny' => array( + * 'controllers/invoices/delete' => 'Role/accountant, User/jeff', + * 'controllers/articles/(delete|publish)' => 'Role/editor', + * ), + * ); + * + * Ok, so as jeff inherits from Role/manager he's matched every rule that references User/jeff, Role/manager, + * Role/editor, Role/accountant and Role/default. However, for jeff, rules for User/jeff are more specific than + * rules for Role/manager, rules for Role/manager are more specific than rules for Role/editor and so on. + * This is important when allow and deny rules match for a role. E.g. Role/accountant is allowed + * controllers/invoices/* but at the same time controllers/invoices/delete is denied. But there is a more + * specific rule defined for Role/manager which is allowed controllers/invoices/delete. However, the most specific + * rule denies access to the delete action explicitly for User/jeff, so he'll be denied access to the resource. + * + * If we would remove the role definition for User/jeff, then jeff would be granted access as he would be resolved + * to Role/manager and Role/manager has an allow rule. + */ + +/** + * The role map defines how to resolve the user record from your application + * to the roles you defined in the roles configuration. + */ +$config['map'] = array( + 'User' => 'User/username', + 'Role' => 'User/group_id', +); + +/** + * define aliases to map your model information to + * the roles defined in your role configuration. + */ +$config['alias'] = array( + 'Role/4' => 'Role/editor', +); + +/** + * role configuration + */ +$config['roles'] = array( + 'Role/admin' => null, +); + +/** + * rule configuration + */ +$config['rules'] = array( + 'allow' => array( + '*' => 'Role/admin', + ), + 'deny' => array(), +); diff --git a/app/Config/bootstrap.php b/app/Config/bootstrap.php new file mode 100644 index 000000000..ba120e2d3 --- /dev/null +++ b/app/Config/bootstrap.php @@ -0,0 +1,139 @@ + 'File', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path + * 'prefix' => 'cake_', //[optional] prefix every cache file with this string + * 'lock' => false, //[optional] use file locking + * 'serialize' => true, // [optional] + * 'mask' => 0666, // [optional] permission mask to use when creating cache files + * )); + * + * APC (http://pecl.php.net/package/APC) + * + * Cache::config('default', array( + * 'engine' => 'Apc', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * )); + * + * Xcache (http://xcache.lighttpd.net/) + * + * Cache::config('default', array( + * 'engine' => 'Xcache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * 'user' => 'user', //user from xcache.admin.user settings + * 'password' => 'password', //plaintext password (xcache.admin.pass) + * )); + * + * Memcache (http://memcached.org/) + * + * Cache::config('default', array( + * 'engine' => 'Memcache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * 'servers' => array( + * '127.0.0.1:11211' // localhost, default port 11211 + * ), //[optional] + * 'persistent' => true, // [optional] set this to false for non-persistent connections + * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) + * )); + * + * Wincache (http://php.net/wincache) + * + * Cache::config('default', array( + * 'engine' => 'Wincache', //[required] + * 'duration'=> 3600, //[optional] + * 'probability'=> 100, //[optional] + * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string + * )); + */ +Cache::config('default', array('engine' => 'File')); + +//Configure::write('CyDefSIG.baseurl', 'https://sig.cyber-defence.be'); +Configure::write('CyDefSIG.baseurl', 'http://localhost:8888'); + +Configure::write('CyDefSIG.showorg', 'false'); // show the name of the organisation that uploaded the data +Configure::write('CyDefSIG.email', 'no-reply@sig.mil.be'); // email from for all the mails + +Configure::write('GnuPG.onlyencrypted', 'true'); // only allow encrypted email, do not allow plaintext mails +Configure::write('GnuPG.email', 'no-reply@sig.mil.be'); +Configure::write('GnuPG.password', 'ii3naxoK|o2a'); +Configure::write('GnuPG.homedir', '/Users/chri/Documents/Work/Projects/201107-CyDefSIG/.gnupg/'); + +Configure::write('Recaptcha.publicKey', '6LdzJsYSAAAAAFCn4Ju7pnmKQ8yhiDbXT-b1eJtN'); +Configure::write('Recaptcha.privateKey', '6LdzJsYSAAAAAD2IwnWwp16gIclZYVKynhsZEMZh'); + + +/** + * The settings below can be used to set additional paths to models, views and controllers. + * + * App::build(array( + * 'Plugin' => array('/full/path/to/plugins/', '/next/full/path/to/plugins/'), + * 'Model' => array('/full/path/to/models/', '/next/full/path/to/models/'), + * 'View' => array('/full/path/to/views/', '/next/full/path/to/views/'), + * 'Controller' => array('/full/path/to/controllers/', '/next/full/path/to/controllers/'), + * 'Model/Datasource' => array('/full/path/to/datasources/', '/next/full/path/to/datasources/'), + * 'Model/Behavior' => array('/full/path/to/behaviors/', '/next/full/path/to/behaviors/'), + * 'Controller/Component' => array('/full/path/to/components/', '/next/full/path/to/components/'), + * 'View/Helper' => array('/full/path/to/helpers/', '/next/full/path/to/helpers/'), + * 'Vendor' => array('/full/path/to/vendors/', '/next/full/path/to/vendors/'), + * 'Console/Command' => array('/full/path/to/shells/', '/next/full/path/to/shells/'), + * 'Locale' => array('/full/path/to/locale/', '/next/full/path/to/locale/') + * )); + * + */ + +/** + * Custom Inflector rules, can be set to correctly pluralize or singularize table, model, controller names or whatever other + * string is passed to the inflection functions + * + * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); + * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); + * + */ + +/** + * Plugins need to be loaded manually, you can either load them one by one or all of them in a single call + * Uncomment one of the lines below, as you need. make sure you read the documentation on CakePlugin to use more + * advanced ways of loading plugins + * + * CakePlugin::loadAll(); // Loads all plugins at once + * CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit + * + */ diff --git a/app/Config/core.php b/app/Config/core.php new file mode 100644 index 000000000..87b13a4a3 --- /dev/null +++ b/app/Config/core.php @@ -0,0 +1,278 @@ + 0 + * and log errors with CakeLog when debug = 0. + * + * Options: + * + * - `handler` - callback - The callback to handle errors. You can set this to any callback type, + * including anonymous functions. + * - `level` - int - The level of errors you are interested in capturing. + * - `trace` - boolean - Include stack traces for errors in log files. + * + * @see ErrorHandler for more information on error handling and configuration. + */ + Configure::write('Error', array( + 'handler' => 'ErrorHandler::handleError', + 'level' => E_ALL & ~E_DEPRECATED, + 'trace' => true + )); + +/** + * Configure the Exception handler used for uncaught exceptions. By default, + * ErrorHandler::handleException() is used. It will display a HTML page for the exception, and + * while debug > 0, framework errors like Missing Controller will be displayed. When debug = 0, + * framework errors will be coerced into generic HTTP errors. + * + * Options: + * + * - `handler` - callback - The callback to handle exceptions. You can set this to any callback type, + * including anonymous functions. + * - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you + * should place the file for that class in app/Lib/Error. This class needs to implement a render method. + * - `log` - boolean - Should Exceptions be logged? + * + * @see ErrorHandler for more information on exception handling and configuration. + */ + Configure::write('Exception', array( + 'handler' => 'ErrorHandler::handleException', + 'renderer' => 'ExceptionRenderer', + 'log' => true + )); + +/** + * Application wide charset encoding + */ + Configure::write('App.encoding', 'UTF-8'); + +/** + * To configure CakePHP *not* to use mod_rewrite and to + * use CakePHP pretty URLs, remove these .htaccess + * files: + * + * /.htaccess + * /app/.htaccess + * /app/webroot/.htaccess + * + * And uncomment the App.baseUrl below: + */ + //Configure::write('App.baseUrl', env('SCRIPT_NAME')); + +/** + * Uncomment the define below to use CakePHP prefix routes. + * + * The value of the define determines the names of the routes + * and their associated controller actions: + * + * Set to an array of prefixes you want to use in your application. Use for + * admin or other prefixed routes. + * + * Routing.prefixes = array('admin', 'manager'); + * + * Enables: + * `admin_index()` and `/admin/controller/index` + * `manager_index()` and `/manager/controller/index` + * + */ + Configure::write('Routing.prefixes', array('admin')); + +/** + * Turn off all caching application-wide. + * + */ + Configure::write('Cache.disable', true); + +/** + * Enable cache checking. + * + * If set to true, for view caching you must still use the controller + * public $cacheAction inside your controllers to define caching settings. + * You can either set it controller-wide by setting public $cacheAction = true, + * or in each action using $this->cacheAction = true. + * + */ + //Configure::write('Cache.check', true); + +/** + * Defines the default error type when using the log() function. Used for + * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG. + */ + define('LOG_ERROR', 2); + +/** + * Session configuration. + * + * Contains an array of settings to use for session configuration. The defaults key is + * used to define a default preset to use for sessions, any settings declared here will override + * the settings of the default config. + * + * ## Options + * + * - `Session.cookie` - The name of the cookie to use. Defaults to 'CAKEPHP' + * - `Session.timeout` - The number of minutes you want sessions to live for. This timeout is handled by CakePHP + * - `Session.cookieTimeout` - The number of minutes you want session cookies to live for. + * - `Session.checkAgent` - Do you want the user agent to be checked when starting sessions? You might want to set the + * value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices and AJAX + * - `Session.defaults` - The default configuration set to use as a basis for your session. + * There are four builtins: php, cake, cache, database. + * - `Session.handler` - Can be used to enable a custom session handler. Expects an array of of callables, + * that can be used with `session_save_handler`. Using this option will automatically add `session.save_handler` + * to the ini array. + * - `Session.autoRegenerate` - Enabling this setting, turns on automatic renewal of sessions, and + * sessionids that change frequently. See CakeSession::$requestCountdown. + * - `Session.ini` - An associative array of additional ini values to set. + * + * The built in defaults are: + * + * - 'php' - Uses settings defined in your php.ini. + * - 'cake' - Saves session files in CakePHP's /tmp directory. + * - 'database' - Uses CakePHP's database sessions. + * - 'cache' - Use the Cache class to save sessions. + * + * To define a custom session handler, save it at /app/Model/Datasource/Session/.php. + * Make sure the class implements `CakeSessionHandlerInterface` and set Session.handler to + * + * To use database sessions, run the app/Config/Schema/sessions.php schema using + * the cake shell command: cake schema create Sessions + * + */ + Configure::write('Session', array( + 'defaults' => 'php' + )); + +/** + * The level of CakePHP security. + */ + Configure::write('Security.level', 'medium'); + +/** + * A random string used in security hashing methods. + */ + Configure::write('Security.salt', 'Rooraenietu8Eeyo 0. Set to 'force' to always enable + * timestamping regardless of debug value. + */ + //Configure::write('Asset.timestamp', true); + +/** + * Compress CSS output by removing comments, whitespace, repeating tags, etc. + * This requires a/var/cache directory to be writable by the web server for caching. + * and /vendors/csspp/csspp.php + * + * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css(). + */ + //Configure::write('Asset.filter.css', 'css.php'); + +/** + * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the + * output, and setting the config below to the name of the script. + * + * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link(). + */ + //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php'); + +/** + * The classname and database used in CakePHP's + * access control lists. + */ + Configure::write('Acl.classname', 'DbAcl'); + Configure::write('Acl.database', 'default'); + +/** + * Uncomment this line and correct your server timezone to fix + * any date & time related errors. + */ + //date_default_timezone_set('UTC'); + +/** + * Pick the caching engine to use. If APC is enabled use it. + * If running via cli - apc is disabled by default. ensure it's available and enabled in this case + * + * Note: 'default' and other application caches should be configured in app/Config/bootstrap.php. + * Please check the comments in boostrap.php for more info on the cache engines available + * and their setttings. + */ +$engine = 'File'; +if (extension_loaded('apc') && function_exists('apc_dec') && (php_sapi_name() !== 'cli' || ini_get('apc.enable_cli'))) { + $engine = 'Apc'; +} + +// In development mode, caches should expire quickly. +$duration = '+999 days'; +if (Configure::read('debug') >= 1) { + $duration = '+10 seconds'; +} + +// Prefix each application on the same server with a different string, to avoid Memcache and APC conflicts. +$prefix = 'myapp_'; + +/** + * Configure the cache used for general framework caching. Path information, + * object listings, and translation cache files are stored with this configuration. + */ +Cache::config('_cake_core_', array( + 'engine' => $engine, + 'prefix' => $prefix . 'cake_core_', + 'path' => CACHE . 'persistent' . DS, + 'serialize' => ($engine === 'File'), + 'duration' => $duration +)); + +/** + * Configure the cache for model and datasource caches. This cache configuration + * is used to store schema descriptions, and table listings in connections. + */ +Cache::config('_cake_model_', array( + 'engine' => $engine, + 'prefix' => $prefix . 'cake_model_', + 'path' => CACHE . 'models' . DS, + 'serialize' => ($engine === 'File'), + 'duration' => $duration +)); diff --git a/app/Config/database.php b/app/Config/database.php new file mode 100644 index 000000000..e1cea2aaa --- /dev/null +++ b/app/Config/database.php @@ -0,0 +1,17 @@ + 'Database/Mysql', + 'persistent' => false, + 'host' => '127.0.0.1', + 'login' => 'cyberdefence', + 'port' => 8889, + 'password' => 'PhwaxFJbNhnRGfZ8', + 'database' => 'cyberdefence_sig', + 'prefix' => '', + //'encoding' => 'utf8', + ); + +} diff --git a/app/Config/email.php b/app/Config/email.php new file mode 100644 index 000000000..39b9579a2 --- /dev/null +++ b/app/Config/email.php @@ -0,0 +1,12 @@ + 'Mail', + //'from' => 'you@localhost', + 'charset' => 'utf-8', + //'headerCharset' => 'utf-8', + ); + + +} \ No newline at end of file diff --git a/app/config/routes.php b/app/Config/routes.php old mode 100755 new mode 100644 similarity index 62% rename from app/config/routes.php rename to app/Config/routes.php index 0269e4e9e..44e92b11e --- a/app/config/routes.php +++ b/app/Config/routes.php @@ -6,30 +6,35 @@ * Routes are very important mechanism that allows you to freely connect * different urls to chosen controllers and their actions (functions). * - * PHP versions 4 and 5 + * PHP 5 * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) - * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org) + * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package cake - * @subpackage cake.app.config + * @package app.Config * @since CakePHP(tm) v 0.2.9 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ /** * Here, we are connecting '/' (base path) to controller called 'Pages', * its action called 'display', and we pass a param to select the view file - * to use (in this case, /app/views/pages/home.ctp)... + * to use (in this case, /app/View/Pages/home.ctp)... */ -// Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home')); -/** - * ...and connect the rest of 'Pages' controller's urls. - */ -// Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display')); + Router::connect('/', array('controller' => 'events', 'action' => 'index')); -Router::connect('/', array('controller' => 'events', 'action' => 'index')); \ No newline at end of file +/** + * Load all plugin routes. See the CakePlugin documentation on + * how to customize the loading of plugin routes. + */ + CakePlugin::routes(); + +/** + * Load the CakePHP default routes. Remove this if you do not want to use + * the built-in default routes. + */ + require CAKE . 'Config' . DS . 'routes.php'; diff --git a/app/Console/Command/AppShell.php b/app/Console/Command/AppShell.php new file mode 100644 index 000000000..6aec56224 --- /dev/null +++ b/app/Console/Command/AppShell.php @@ -0,0 +1,31 @@ + array( + 'authenticate' => array( + 'Form' => array( + 'fields' => array('username' => 'email') + ) + ), + 'loginRedirect' => array('controller' => 'users', 'action' => 'routeafterlogin'), + 'logoutRedirect' => array('controller' => 'users', 'action' => 'login'), + 'authorize' => array('Controller') // Added this line + ) + ); + + + public function isAuthorized($user) { + if (isset($user['org']) && $user['org'] === 'admin') { + return true; // admin can access every action + } + elseif ($this->Auth->loggedIn()) { + return true; // logged users + } + return false; // The rest don't + } + + function beforeFilter() { + + } + + + /** + * Convert an array to the same array but with the values also as index instead of an interface_exists + */ + function _arrayToValuesIndexArray($old_array) { + $new_array = Array(); + foreach ($old_array as $value) + $new_array[$value] = $value; + return $new_array; + } + + /** + * checks if the currently logged user is an administrator + */ + public function _isAdmin() { + $user = $this->Auth->user(); + if (isset($user['org']) && $user['org'] === 'admin') { + return true; + } + return false; + } + + /** + * Refreshes the Auth session with new/updated data + * @return void + */ + function _refreshAuth() { + if (isset($this->User)) { + $user = $this->User->read(false, $this->Auth->user('id')); + } else { + $user= ClassRegistry::init('User')->findById($this->Auth->user('id')); + } + $this->Auth->login($user['User']); + + } + + + + + + +} diff --git a/app/locale/eng/LC_MESSAGES/empty b/app/Controller/Component/empty old mode 100755 new mode 100644 similarity index 100% rename from app/locale/eng/LC_MESSAGES/empty rename to app/Controller/Component/empty diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php new file mode 100644 index 000000000..4f93fd633 --- /dev/null +++ b/app/Controller/EventsController.php @@ -0,0 +1,808 @@ + 50, + 'order' => array( + 'Event.date' => 'DESC' + ) + ); + + + function beforeFilter() { + + $this->Auth->allow('xml'); + $this->Auth->allow('nids'); + $this->Auth->allow('text'); + + // These variables are required for every view + $this->set('me', $this->Auth->user()); + $this->set('isAdmin', $this->_isAdmin()); + } + +/** + * index method + * + * @return void + */ + function index() { + // list the events + $this->Event->recursive = 0; + $this->set('events', $this->paginate()); + + if (!$this->Auth->user('gpgkey')) { + $this->Session->setFlash('No GPG key set in your profile. To receive emails, submit your public key in your profile.'); + } + } + +/** + * view method + * + * @param string $id + * @return void + */ + public function view($id = null) { + $this->Event->id = $id; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + $this->set('event', $this->Event->read(null, $id)); + $this->set('relatedEvents', $this->Event->getRelatedEvents()); + + $related_signatures = array(); + $this->loadModel('Signature'); + foreach ($this->Event->data['Signature'] as $signature) { + $related_signatures[$signature['id']] = $this->Signature->getRelatedSignatures($signature); + } + $this->set('relatedSignatures', $related_signatures); + + } + +/** + * add method + * + * @return void + */ + public function add() { + if ($this->request->is('post')) { + // force check userid and orgname to be from yourself + $this->request->data['Event']['user_id'] = $this->Auth->user('id'); + $this->request->data['Event']['org'] = $this->Auth->user('org'); + $this->Event->create(); + if ($this->Event->save($this->request->data)) { + $this->Session->setFlash(__('The event has been saved')); + $this->redirect(array('action' => 'view', $this->Event->getId())); + } else { + $this->Session->setFlash(__('The event could not be saved. Please, try again.'), 'default', array(), 'error'); + } + } + // combobox for risks + $risks = $this->Event->validate['risk']['rule'][1]; + $risks = $this->_arrayToValuesIndexArray($risks); + $this->set('risks',compact('risks')); + } + +/** + * edit method + * + * @param string $id + * @return void + */ + public function edit($id = null) { + $this->Event->id = $id; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + // only edit own events + $old_event = $this->Event->read(null, $id); + if (!$this->_isAdmin() && $this->Auth->user('org') != $old_event['Event']['org']) { + throw new UnauthorizedException('You are only allowed to edit events of your own organisation.'); + } + if ($this->request->is('post') || $this->request->is('put')) { + // always force the user and org, but do not force it for admins + if (!$this->_isAdmin()) { + $this->request->data['Event']['user_id'] = $this->Auth->user('id'); + $this->request->data['Event']['org'] = $this->Auth->user('org'); + } else { + $this->request->data['Event']['user_id'] = $old_event['Event']['id']; + $this->request->data['Event']['org'] = $old_event['Event']['org']; + } + // we probably also want to remove the alerted flag + $this->request->data['Event']['alerted'] = 0; + + // say what fields are to be updated + $fieldList=array('user_id', 'org', 'date', 'risk', 'info', 'alerted'); + if ($this->Event->save($this->request->data, true, $fieldList)) { + $this->Session->setFlash(__('The event has been saved')); + $this->redirect(array('action' => 'view', $id)); + } else { + $this->Session->setFlash(__('The event could not be saved. Please, try again.')); + } + } else { + $this->request->data = $this->Event->read(null, $id); + } + + // combobox for types + $risks = $this->Event->validate['risk']['rule'][1]; + $risks = $this->_arrayToValuesIndexArray($risks); + $this->set('risks',compact('risks')); + } + +/** + * delete method + * + * @param string $id + * @return void + */ + public function delete($id = null) { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(); + } + $this->Event->id = $id; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + // only edit own events + $this->Event->read(); + if (!$this->_isAdmin() && $this->Auth->user('org') != $this->Event->data['Event']['org']) { + throw new UnauthorizedException('You are only allowed to edit your own events.'); + } + if ($this->Event->delete()) { + $this->Session->setFlash(__('Event deleted')); + $this->redirect(array('action' => 'index')); + } + $this->Session->setFlash(__('Event was not deleted')); + $this->redirect(array('action' => 'index')); + } + + /** + * Send out an alert email to all the users that wanted to be notified. + * Users with a GPG key will get the mail encrypted, other users will get the mail unencrypted + */ + function alert($id = null) { + $this->Event->id = $id; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + + // only allow form submit CSRF protection. + if ($this->request->is('post') || $this->request->is('put')) { + // only allow alert for own events or admins + $this->Event->id = $id; + $this->Event->read(); + + if (!$this->_isAdmin() && $this->Auth->user('org') != $this->Event->data['Event']['org']) { + throw new UnauthorizedException('You are only allowed to finish events of your own organisation.'); + } + + // fetch the event and build the body + if (1 == $this->Event->data['Event']['alerted']) { + $this->Session->setFlash(__('Everyone has already been alerted for this event. To alert again, first edit this event.', true), 'default', array(), 'error'); + $this->redirect(array('action' => 'view', $id)); + } + + // The mail body, Sanitize::html() is NOT needed as we are sending plain-text mails. + $body = ""; + $appendlen = 20; + $body .= 'URL : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$this->Event->data['Event']['id']."\n"; + $body .= 'Event : '.$this->Event->data['Event']['id']."\n"; + $body .= 'Date : '.$this->Event->data['Event']['date']."\n"; + if ('true' == Configure::read('CyDefSIG.showorg')) { + $body .= 'Reported by : '.$this->Event->data['Event']['org']."\n"; + } + $body .= 'Risk : '.$this->Event->data['Event']['risk']."\n"; + $relatedEvents = $this->Event->getRelatedEvents($id); + if (!empty($relatedEvents)) { + foreach ($relatedEvents as $relatedEvent){ + $body .= 'Related to : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$relatedEvent['Event']['id'].' ('.$relatedEvent['Event']['date'].')'."\n" ; + + } + } + $body .= 'Info : '."\n"; + $body .= $this->Event->data['Event']['info']."\n"; + $body .= "\n"; + $body .= 'Signatures :'."\n"; + $body_temp_other = ""; + + if (isset($this->Event->data['Signature'])) { + foreach ($this->Event->data['Signature'] as $signature){ + $line = '- '.$signature['type'].str_repeat(' ', $appendlen - 2 - strlen( $signature['type'])).': '.$signature['value']."\n"; + if ('other' == $signature['type']) // append the 'other' signature types to the bottom. + $body_temp_other .= $line; + else $body .= $line; + } + } + $body .= "\n"; + $body .= $body_temp_other; // append the 'other' signature types to the bottom. + + // sign the body + require_once 'Crypt/GPG.php'; + $gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'))); + $gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password')); + $body_signed = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR); + + $this->loadModel('User'); + + // + // Build a list of the recipients that get a non-encrypted mail + // But only do this if it is allowed in the bootstrap.php file. + // + if ('false' == Configure::read('GnuPG.onlyencrypted')) { + $alert_users = $this->User->find('all', array( + 'conditions' => array('User.autoalert' => 1, + 'User.gpgkey =' => ""), + 'recursive' => 0, + ) ); + $alert_emails = Array(); + foreach ($alert_users as $user) { + $alert_emails[] = $user['User']['email']; + } + // prepare the the unencrypted email + $this->Email->from = Configure::read('CyDefSIG.email'); + //$this->Email->to = "CyDefSIG "; TODO check if it doesn't break things to not set a to , like being spammed away + $this->Email->bcc = $alert_emails; + $this->Email->subject = "[CyDefSIG] Event ".$id." - ".$this->Event->data['Event']['risk']." - TLP Amber"; + $this->Email->template = 'body'; + $this->Email->sendAs = 'text'; // both text or html + $this->set('body', $body_signed); + // send it + $this->Email->send(); + // If you wish to send multiple emails using a loop, you'll need + // to reset the email fields using the reset method of the Email component. + $this->Email->reset(); + } + + // + // Build a list of the recipients that wish to receive encrypted mails. + // + $alert_users = $this->User->find('all', array( + 'conditions' => array( 'User.autoalert' => 1, + 'User.gpgkey !=' => ""), + 'recursive' => 0, + ) + ); + // encrypt the mail for each user and send it separately + foreach ($alert_users as $user) { + // send the email + $this->Email->from = Configure::read('CyDefSIG.email'); + $this->Email->to = $user['User']['email']; + $this->Email->subject = "[CyDefSIG] Event ".$id." - ".$this->Event->data['Event']['risk']." - TLP Amber"; + $this->Email->template = 'body'; + $this->Email->sendAs = 'text'; // both text or html + + // import the key of the user into the keyring + // this is not really necessary, but it enables us to find + // the correct key-id even if it is not the same as the emailaddress + $key_import_output = $gpg->importKey($user['User']['gpgkey']); + // say what key should be used to encrypt + $gpg = new Crypt_GPG(); + $gpg->addEncryptKey($key_import_output['fingerprint']); // use the key that was given in the import + + $body_enc_sig = $gpg->encrypt($body_signed, true); + + $this->set('body', $body_enc_sig); + $this->Email->send(); + // If you wish to send multiple emails using a loop, you'll need + // to reset the email fields using the reset method of the Email component. + $this->Email->reset(); + } + + // update the DB to set the alerted flag + $this->Event->saveField('alerted', 1); + + // redirect to the view event page + $this->Session->setFlash(__('Email sent to all participants.', true)); + $this->redirect(array('action' => 'view', $id)); + } + } + + + /** + * Send out an contact email to the person who posted the event. + * Users with a GPG key will get the mail encrypted, other users will get the mail unencrypted + * @todo allow the user to enter a comment in the contact email. + */ + public function contact($id = null) { + $this->Event->id = $id; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + + // User has filled in his contact form, send out the email. + if ($this->request->is('post') || $this->request->is('put')) { + $message = $this->request->data['Event']['message']; + if ($this->_sendContactEmail($id, $message)) { + // redirect to the view event page + $this->Session->setFlash(__('Email sent to the reporter.', true)); + } else { + $this->Session->setFlash(__('Sending of email failed', true), 'default', array(), 'error'); + } + $this->redirect(array('action' => 'view', $id)); + } + // User didn't see the contact form yet. Present it to him. + if (empty($this->data)) { + $this->data = $this->Event->read(null, $id); + } + } + + + /** + * + * Sends out an email with the request to be contacted about a specific event. + * @todo move _sendContactEmail($id, $message) to a better place. (components?) + * + * @param unknown_type $id The id of the event for wich you want to contact the person. + * @param unknown_type $message The custom message that will be appended to the email. + * @return True if success, False if error + */ + private function _sendContactEmail($id, $message) { + // fetch the event + $event = $this->Event->read(null, $id); + $reporter = $event['User']; // email, gpgkey + + // The mail body, Sanitize::html() is NOT needed as we are sending plain-text mails. + $body = ""; + $body .="Hello, \n"; + $body .="\n"; + $body .="Someone wants to get in touch with you concerning a CyDefSIG event. \n"; + $body .="\n"; + $body .="You can reach him at ".$this->Auth->user('email')."\n"; + if (!$this->Auth->user('gpgkey')) + $body .="His GPG/PGP key is added as attachment to this email. \n"; + $body .="\n"; + $body .="He wrote the following message: \n"; + $body .=$message."\n"; + $body .="\n"; + $body .="\n"; + $body .="The event is the following: \n"; + + // print the event in mail-format + // LATER place event-to-email-layout in a function + $appendlen = 20; + $body .= 'URL : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$event['Event']['id']."\n"; + $body .= 'Event : '.$event['Event']['id']."\n"; + $body .= 'Date : '.$event['Event']['date']."\n"; + if ('true' == Configure::read('CyDefSIG.showorg')) { + $body .= 'Reported by : '.$event['Event']['org']."\n"; + } + $body .= 'Risk : '.$event['Event']['risk']."\n"; + $relatedEvents = $this->Event->getRelatedEvents($id); + if (!empty($relatedEvents)) { + foreach ($relatedEvents as $relatedEvent){ + $body .= 'Related to : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$relatedEvent['Event']['id'].' ('.$relatedEvent['Event']['date'].')'."\n" ; + + } + } + $body .= 'Info : '."\n"; + $body .= $event['Event']['info']."\n"; + $body .= "\n"; + $body .= 'Signatures :'."\n"; + $body_temp_other = ""; + if (!empty($event['Signature'])) { + foreach ($event['Signature'] as $signature){ + $line = '- '.$signature['type'].str_repeat(' ', $appendlen - 2 - strlen( $signature['type'])).': '.$signature['value']."\n"; + if ('other' == $signature['type']) // append the 'other' signature types to the bottom. + $body_temp_other .= $line; + else $body .= $line; + } + } + $body .= "\n"; + $body .= $body_temp_other; // append the 'other' signature types to the bottom. + + // sign the body + require_once 'Crypt/GPG.php'; + $gpg = new Crypt_GPG(array('homedir' => Configure::read('GnuPG.homedir'))); + $gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password')); + $body_signed = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR); + + if (!empty($reporter['gpgkey'])) { + // import the key of the user into the keyring + // this isn't really necessary, but it gives it the fingerprint necessary for the next step + $key_import_output = $gpg->importKey($reporter['gpgkey']); + // say what key should be used to encrypt + $gpg = new Crypt_GPG(); + $gpg->addEncryptKey($key_import_output['fingerprint']); // use the key that was given in the import + + $body_enc_sig = $gpg->encrypt($body_signed, true); + } else { + $body_enc_sig = $body_signed; + // FIXME should I allow sending unencrypted "contact" mails to people if they didn't import they GPG key? + } + + // prepare the email + $this->Email->from = Configure::read('CyDefSIG.email'); + $this->Email->to = $reporter['email']; + $this->Email->subject = "[CyDefSIG] Need info about event ".$id." - TLP Amber"; + //$this->Email->delivery = 'debug'; // do not really send out mails, only display it on the screen + $this->Email->template = 'body'; + $this->Email->sendAs = 'text'; // both text or html + $this->set('body', $body_enc_sig); + + // Add the GPG key of the user as attachment + // LATER sign the attached GPG key + if (!empty($me_user['gpgkey'])) { + // save the gpg key to a temporary file + $tmpfname = tempnam(TMP, "GPGkey"); + $handle = fopen($tmpfname, "w"); + fwrite($handle, $me_user['gpgkey']); + fclose($handle); + // attach it + $this->Email->attachments = array( + 'gpgkey.asc' => $tmpfname + ); + } + + // send it + $result = $this->Email->send(); + + // remove the temporary gpg file + if (!empty($me_user['gpgkey'])) + unlink($tmpfname); + + return $result; + } + + + public function export() { + // Simply display a static view + + // generate the list of signature types + $this->loadModel('Signature'); + $this->set('sig_types', $this->Signature->validate['type']['rule'][1]); + + } + + + public function xml($key) { + // FIXME implement XML output + // check if the key is valid -> search for users based on key + $this->loadModel('User'); + // no input sanitization necessary, it's done by model + $user = $this->User->findByAuthkey($key); + if (empty($user)) { + throw new UnauthorizedException('Incorrect authentication key'); + } + // display the full xml + $this->header('Content-Type: text/xml'); // set the content type + $this->layout = 'xml/default'; +// $this->header('Content-Disposition: attachment; filename="cydefsig.xml"'); + + $conditions = array("Event.alerted" => 1); + $fields = array('Event.id', 'Event.date', 'Event.risk', 'Event.info'); + if ('true' == Configure::read('CyDefSIG.showorg')) { + $fields[] = 'Event.org'; + } + // $this->Event->Behaviors->attach('Containable'); + // $contain = array('Signature.id', 'Signature.type', 'Signature.value', 'Signature.to_snort'); + $params = array('conditions' => $conditions, + 'recursive' => 1, + 'fields' => $fields, + // 'contain' => $contain + ); + $results = $this->Event->find('all', $params); + + +/* $xml = Xml::build(''); */ + + $myXmlOriginal = 'value'; + $xml = Xml::build($myXmlOriginal); + $xml->root->addChild('young', 'new value'); + +// foreach ($results as $result) { +// debug($result); +// $xml->CyDefSIG->addChild('f', 'b'); +// debug($xml); +// } +// debug($results); +// $xml= Xml::fromArray(array('event' =>$results), array('format' => 'tags', 'return' => 'domdocument')); +// debug($xml->saveXML()); + + } + + + public function nids($key) { + // check if the key is valid -> search for users based on key + $this->loadModel('User'); + // no input sanitization necessary, it's done by model + $user = $this->User->findByAuthkey($key); + if (empty($user)) { + throw new UnauthorizedException('Incorrect authentication key'); + } + // display the full snort rulebase + $this->header('Content-Type: text/plain'); // set the content type + $this->header('Content-Disposition: attachment; filename="cydefsig.rules"'); + $this->layout = 'text/default'; + + $rules= array(); + + // find events that are finished + $events = $this->Event->findAllByAlerted(1); + $classtype = 'targeted-attack'; + + foreach ($events as $event) { + # proto src_ip src_port direction dst_ip dst_port msg rule_content tag sid rev + $rule_format_msg = 'msg: "CyDefSIG %s, Event '.$event['Event']['id'].', '.$event['Event']['risk'].'"'; + $rule_format_reference = 'reference:url,'.Configure::read('CyDefSIG.baseurl').'/events/view/'.$event['Event']['id']; + $rule_format = 'alert %s %s %s %s %s %s ('.$rule_format_msg.'; %s %s classtype:'.$classtype.'; sid:%d; rev:%d; '.$rule_format_reference.';) '; + + $sid = $user['User']['nids_sid']+($event['Event']['id']*100); // LATER this will cause issues with events containing more than 99 signatures + //debug($event); + foreach ($event['Signature'] as $signature) { + if (0 == $signature['to_ids']) continue; // signature is not to be exported to IDS. // LATER filter out to_ids=0 in the query + + $sid++; + switch ($signature['type']) { + // LATER test all the snort signatures + // LATER add the tag keyword in the rules to capture network traffic + // LATER sanitize every $signature['value'] to not conflict with snort + case 'ip-dst': + $rules[] = sprintf($rule_format, + 'ip', // proto + '$HOME_NET', // src_ip + 'any', // src_port + '->', // direction + $signature['value'], // dst_ip + 'any', // dst_port + 'Outgoing To Bad IP', // msg + '', // rule_content + '', // tag + $sid, // sid + 1 // rev + ); + break; + case 'ip-src': + $rules[] = sprintf($rule_format, + 'ip', // proto + $signature['value'], // src_ip + 'any', // src_port + '->', // direction + '$HOME_NET', // dst_ip + 'any', // dst_port + 'Incoming From Bad IP', // msg + '', // rule_content + '', // tag + $sid, // sid + 1 // rev + ); + break; + case 'email-src': + $rules[] = sprintf($rule_format, + 'tcp', // proto + '$EXTERNAL_NET', // src_ip + 'any', // src_port + '<>', // direction + '$SMTP_SERVERS', // dst_ip + '25', // dst_port + 'Bad Source Email Address', // msg + 'flow:established,to_server; content:"MAIL FROM|3a|"; nocase; content:"'.$signature['value'].'"; nocase;', // rule_content + 'tag:session,600,seconds;', // tag + $sid, // sid + 1 // rev + ); + break; + case 'email-dst': + $rules[] = sprintf($rule_format, + 'tcp', // proto + '$EXTERNAL_NET', // src_ip + 'any', // src_port + '<>', // direction + '$SMTP_SERVERS', // dst_ip + '25', // dst_port + 'Bad Destination Email Address',// msg + 'flow:established,to_server; content:"RCPT TO|3a|"; nocase; content:"'.$signature['value'].'"; nocase;', // rule_content + 'tag:session,600,seconds;', // tag + $sid, // sid + 1 // rev + ); + break; + case 'email-subject': + // LATER email-subject rule might not match because of line-wrapping + $rules[] = sprintf($rule_format, + 'tcp', // proto + '$EXTERNAL_NET', // src_ip + 'any', // src_port + '<>', // direction + '$SMTP_SERVERS', // dst_ip + '25', // dst_port + 'Bad Email Subject', // msg + 'flow:established,to_server; content:"Subject|3a|"; nocase; content:"'.$signature['value'].'"; nocase;', // rule_content + 'tag:session,600,seconds;', // tag + $sid, // sid + 1 // rev + ); + break; + case 'email-attachment': + // LATER email-attachment rule might not match because of line-wrapping + $rules[] = sprintf($rule_format, + 'tcp', // proto + '$EXTERNAL_NET', // src_ip + 'any', // src_port + '<>', // direction + '$SMTP_SERVERS', // dst_ip + '25', // dst_port + 'Bad Email Attachment', // msg + 'flow:established,to_server; content:"Content-Disposition: attachment|3b| filename=|22|"; content:"'.$signature['value'].'|22|";', // rule_content // LATER test and finetune this snort rule https://secure.wikimedia.org/wikipedia/en/wiki/MIME#Content-Disposition + 'tag:session,600,seconds;', // tag + $sid, // sid + 1 // rev + ); + break; + case 'domain': + $rules[] = sprintf($rule_format, + 'udp', // proto + 'any', // src_ip + 'any', // src_port + '->', // direction + 'any', // dst_ip + '53', // dst_port + 'Lookup Of Bad Domain', // msg + 'content:"'.$this->_dnsNameToRawFormat($signature['value']).'"; nocase;', // rule_content + '', // tag + $sid, // sid + 1 // rev + ); + $sid++; + $rules[] = sprintf($rule_format, + 'tcp', // proto + 'any', // src_ip + 'any', // src_port + '->', // direction + 'any', // dst_ip + '53', // dst_port + 'Lookup Of Bad Domain', // msg + 'content:"'.$this->_dnsNameToRawFormat($signature['value']).'"; nocase;', // rule_content + '', // tag + $sid, // sid + 1 // rev + ); + $sid++; + //break; // domain should also detect the domain name in a url + case 'url': + $rules[] = sprintf($rule_format, + 'tcp', // proto + '$HOME_NET', // src_ip + 'any', // src_port + '->', // direction + '$EXTERNAL_NET', // dst_ip + '$HTTP_PORTS', // dst_port + 'Outgoing Bad HTTP URL', // msg + 'flow:to_server,established; uricontent:"'.$signature['value'].'"; nocase;', // rule_content + 'tag:session,600,seconds;', // tag + $sid, // sid + 1 // rev + ); + break; + case 'user-agent': + $rules[] = ""; + // TODO write snort user-agent rule + break; + case 'snort': + $tmp_rule = $signature['value']; + + // rebuild the rule by overwriting the different keywords using preg_replace() + // sid - '/sid\s*:\s*[0-9]+\s*;/' + // rev - '/rev\s*:\s*[0-9]+\s*;/' + // classtype - '/classtype:[a-zA-Z_-]+;/' + // msg - '/msg\s*:\s*".*"\s*;/' + // reference - '/reference\s*:\s*.+;/' + $replace_count=array(); + $tmp_rule = preg_replace('/sid\s*:\s*[0-9]+\s*;/', 'sid:'.$sid.';', $tmp_rule, -1, $replace_count['sid']); + if (null == $tmp_rule ) break; // don't output the rule on error with the regex + $tmp_rule = preg_replace('/rev\s*:\s*[0-9]+\s*;/', 'rev:1;', $tmp_rule, -1, $replace_count['rev']); + if (null == $tmp_rule ) break; // don't output the rule on error with the regex + $tmp_rule = preg_replace('/classtype:[a-zA-Z_-]+;/', 'classtype:'.$classtype.';', $tmp_rule, -1, $replace_count['classtype']); + if (null == $tmp_rule ) break; // don't output the rule on error with the regex + $tmp_message = sprintf($rule_format_msg, 'snort-rule'); + $tmp_rule = preg_replace('/msg\s*:\s*".*"\s*;/', $tmp_message.';', $tmp_rule, -1, $replace_count['msg']); + if (null == $tmp_rule ) break; // don't output the rule on error with the regex + $tmp_rule = preg_replace('/reference\s*:\s*.+;/', $rule_format_reference.';', $tmp_rule, -1, $replace_count['reference']); + if (null == $tmp_rule ) break; // don't output the rule on error with the regex + + // some values were not replaced, so we need to add them ourselves, and insert them in the rule + $extra_for_rule=""; + if (0 == $replace_count['sid']) { + $extra_for_rule .= 'sid:'.$sid.';'; + } if (0 == $replace_count['rev']) { + $extra_for_rule .= 'rev:1;'; + } if (0 == $replace_count['classtype']) { + $extra_for_rule .= 'classtype:'.$classtype.';'; + } if (0 == $replace_count['msg']) { + $extra_for_rule .= $tmp_message.';'; + } if (0 == $replace_count['reference']) { + $extra_for_rule .= $rule_format_reference.';'; + } + $tmp_rule = preg_replace('/;\s*\)/', '; '.$extra_for_rule.')', $tmp_rule); + + // finally the rule is cleaned up and can be outputed + $rules[] = $tmp_rule; + + // TODO test using lots of snort rules. + default: + break; + } + + } + + } + print ("#

This part is not finished and might be buggy. Please report any issues.

\n"); + + print "#
 \n";
+        foreach ($rules as $rule)
+        print $rule."\n";
+        print "#
\n"; + + $this->set('rules', $rules); + + } + + + public function text($key, $type="") { + // check if the key is valid -> search for users based on key + $this->loadModel('User'); + // no input sanitization necessary, it's done by model + $user = $this->User->findByAuthkey($key); + if (empty($user)) { + throw new UnauthorizedException('Incorrect authentication key'); + } + + $this->header('Content-Type: text/plain'); // set the content type + $this->layout = 'text/default'; + + $this->loadModel('Signature'); + $params = array( + 'conditions' => array('Signature.type' => $type), //array of conditions + 'recursive' => 0, //int + 'fields' => array('Signature.value'), //array of field names + 'order' => array('Signature.value'), //string or array defining order + 'group' => array('Signature.value'), //fields to GROUP BY + ); + $signatures = $this->Signature->find('all', $params); + + $this->set('signatures', $signatures); + } + + + /** + * // LATER move _dnsNameToRawFormat($name) function to a better place + * Converts a DNS name to a raw format usable in NIDS like Snort. + * example: foobar.com becomes |06|foobar|03|com|00| + * @param string $name dns name to be converted + * @return string raw snort compatible format of the dns name + */ + private function _dnsNameToRawFormat($name) { + $rawName = ""; + // explode using the dot + $explodedNames = explode('.', $name); + // for each part + foreach ($explodedNames as $explodedName) { + // count the lenght of the part, and add |length| before + $length = strlen($explodedName); + if ($length > 255) exit('ERROR: dns name is to long for RFC'); // LATER log correctly without dying + $hexLength = dechex($length); + if (1 == strlen($hexLength)) $hexLength = '0'.$hexLength; + $rawName .= '|'.$hexLength.'|'.$explodedName; + } + // put all together + $rawName .= '|00|'; + // and append |00| to terminate the name + return $rawName; + } + + + +} diff --git a/app/Controller/PagesController.php b/app/Controller/PagesController.php new file mode 100644 index 000000000..194ac4299 --- /dev/null +++ b/app/Controller/PagesController.php @@ -0,0 +1,82 @@ +redirect('/'); + } + $page = $subpage = $title_for_layout = null; + + if (!empty($path[0])) { + $page = $path[0]; + } + if (!empty($path[1])) { + $subpage = $path[1]; + } + if (!empty($path[$count - 1])) { + $title_for_layout = Inflector::humanize($path[$count - 1]); + } + $this->set(compact('page', 'subpage', 'title_for_layout')); + $this->render(implode('/', $path)); + } +} diff --git a/app/Controller/SignaturesController.php b/app/Controller/SignaturesController.php new file mode 100644 index 000000000..53facdb91 --- /dev/null +++ b/app/Controller/SignaturesController.php @@ -0,0 +1,189 @@ +set('me', $this->Auth->user()); + $this->set('isAdmin', $this->_isAdmin()); + } + + +/** + * index method + * + * @return void + */ + public function index() { + $this->Signature->recursive = 0; + $this->set('signatures', $this->paginate()); + } + +/** + * add method + * + * @return void + */ + public function add($event_id = null) { + if ($this->request->is('post')) { + $this->loadModel('Event'); + // only own signatures + $this->Event->recursive = 0; + $event = $this->Event->findById($this->request->data['Signature']['event_id']); + if (!$this->_isAdmin() && $this->Auth->user('org') != $event['Event']['org']) { + throw new UnauthorizedException('You can only add signatures for your own organisation.'); + } + + // remove the alerted flag from the event + $this->Event->id = $this->request->data['Signature']['event_id']; + $this->Event->saveField('alerted', 0); + + // + // multiple signatures in batch import + // + if ($this->data['Signature']['batch_import'] == 1) { + // make array from value field + $signatures = explode("\n", $this->request->data['Signature']['value']); + + $fails = ""; // will be used to keep a list of the lines that failed or succeeded + $successes = ""; + foreach ($signatures as $key => $signature) { + $signature = trim($signature); + if (strlen($signature) == 0 ) + continue; // don't do anything for empty lines + + $this->Signature->create(); + $this->request->data['Signature']['value'] = $signature; // set the value as the content of the single line + + if ($this->Signature->save($this->request->data)) { + $successes .= " ".($key+1); + } else { + $fails .= " ".($key+1); + } + + } + // we added all the signatures, + if ($fails) { + // list the ones that failed + $this->Session->setFlash(__('The lines'.$fails.' could not be saved. Please, try again.', true), 'default', array(), 'error'); + } + if ($successes) { + // list the ones that succeeded + $this->Session->setFlash(__('The lines'.$successes.' have been saved', true)); + } + + $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['Signature']['event_id'])); + + } + + else { + // + // single signature + // + // create the signature + $this->Signature->create(); + if ($this->Signature->save($this->request->data)) { + // inform the user and redirect + $this->Session->setFlash(__('The signature has been saved')); + $this->redirect(array('controller' => 'events', 'action' => 'view', $this->request->data['Signature']['event_id'])); + } else { + $this->Session->setFlash(__('The signature could not be saved. Please, try again.')); + } + } + } else { + // set the event_id in the form + $this->request->data['Signature']['event_id'] = $event_id; + } + + // combobox for types + $types = $this->Signature->validate['type']['rule'][1]; + $types = $this->_arrayToValuesIndexArray($types); + $this->set('types',compact('types')); + } + +/** + * edit method + * + * @param string $id + * @return void + */ + public function edit($id = null) { + $this->Signature->id = $id; + if (!$this->Signature->exists()) { + throw new NotFoundException(__('Invalid signature')); + } + // only own signatures + $this->Signature->read(); + if (!$this->_isAdmin() && $this->Auth->user('org') != $this->Signature->data['Event']['org']) { + throw new UnauthorizedException('You can only edit signatures from your own organisation.'); + } + if ($this->request->is('post') || $this->request->is('put')) { + + if ($this->Signature->save($this->request->data)) { + $this->Session->setFlash(__('The signature has been saved')); + $this->redirect(array('action' => 'index')); + } else { + $this->Session->setFlash(__('The signature could not be saved. Please, try again.')); + } + } else { + $this->request->data = $this->Signature->read(null, $id); + } + + } + +/** + * delete method + * + * @param string $id + * @return void + */ + public function delete($id = null) { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(); + } + $this->Signature->id = $id; + if (!$this->Signature->exists()) { + throw new NotFoundException(__('Invalid signature')); + } + + // only own signatures + $this->Signature->read(); + if (!$this->_isAdmin() && $this->Auth->user('org') != $this->Signature->data['Event']['org']) { + throw new UnauthorizedException('You can only delete signatures from your own organisation.'); + } + + if ($this->Signature->delete()) { + $this->Session->setFlash(__('Signature deleted')); + } else { + $this->Session->setFlash(__('Signature was not deleted')); + } + + $this->redirect($this->referer()); + } + + + + public function search() { + if ($this->request->is('post')) { + $keyword = $this->request->data['Signature']['keyword']; + + // search the db + $this->Signature->recursive = 0; + $this->paginate = array( + 'conditions' => array('Signature.value LIKE' => '%'.$keyword.'%'), + ); + $this->set('signatures', $this->paginate()); + + // set the same view as the index page + $this->render('index'); + } + } + +} diff --git a/app/Controller/UsersController.php b/app/Controller/UsersController.php new file mode 100644 index 000000000..e811aaa2f --- /dev/null +++ b/app/Controller/UsersController.php @@ -0,0 +1,309 @@ +Auth->allow('login', 'logout'); + + // These variables are required for every view + $this->set('me', $this->Auth->user()); + $this->set('isAdmin', $this->_isAdmin()); + } + + +/** + * view method + * + * @param string $id + * @return void + */ + public function view($id = null) { + if ("me" == $id) $id = $this->Auth->user('id'); + $this->User->id = $id; + if (!$this->User->exists()) { + throw new NotFoundException(__('Invalid user')); + } + // Only own profile + if ($this->Auth->user('id') != $id) { + throw new ForbiddenException('You are not authorized to access this profile.'); + } + $this->set('user', $this->User->read(null, $id)); + } + + +/** + * edit method + * + * @param string $id + * @return void + */ + public function edit($id = null) { + if ("me" == $id) $id = $this->Auth->user('id'); + $this->User->id = $id; + if (!$this->User->exists()) { + throw new NotFoundException(__('Invalid user')); + } + // Only own profile + if ($this->Auth->user('id') != $id) { + throw new ForbiddenException('You are not authorized to edit this profile.'); + } + if ($this->request->is('post') || $this->request->is('put')) { + // What fields should be saved (allowed to be saved) + $fieldList=array('email', 'autoalert', 'gpgkey', 'nids_sid' ); + if ("" != $this->data['User']['password']) + $fieldList[] = 'password'; + // Save the data + if ($this->User->save($this->request->data, true ,$fieldList)) { + $this->Session->setFlash(__('The profile has been updated')); + $this->redirect(array('action' => 'view', $id)); + } else { + $this->Session->setFlash(__('The profile could not be updated. Please, try again.')); + } + } else { + $this->User->recursive=0; + $this->User->read(null, $id); + $this->User->set('password', ''); + $this->request->data = $this->User->data; + } + $this->request->data['User']['org']=$this->Auth->user('org'); + } + +/** + * delete method + * + * @param string $id + * @return void + */ + public function delete($id = null) { + if ("me" == $id) $id = $this->Auth->user('id'); + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(); + } + $this->User->id = $id; + if (!$this->User->exists()) { + throw new NotFoundException(__('Invalid user')); + } + // Only own profile + if ($this->Auth->user('id') != $id) { + throw new ForbiddenException('You are not authorized to delete this profile.'); + } + if ($this->User->delete()) { + $this->Session->setFlash(__('User deleted')); + $this->redirect(array('action' => 'index')); + } + $this->Session->setFlash(__('User was not deleted')); + $this->redirect(array('action' => 'index')); + } +/** + * admin_index method + * + * @return void + */ + public function admin_index() { + $this->User->recursive = 0; + $this->set('users', $this->paginate()); + } + +/** + * admin_view method + * + * @param string $id + * @return void + */ + public function admin_view($id = null) { + $this->User->id = $id; + if (!$this->User->exists()) { + throw new NotFoundException(__('Invalid user')); + } + $this->set('user', $this->User->read(null, $id)); + } + +/** + * admin_add method + * + * @return void + */ + public function admin_add() { + if ($this->request->is('post')) { + $this->User->create(); + if ($this->User->save($this->request->data)) { + $this->Session->setFlash(__('The user has been saved')); + $this->redirect(array('action' => 'index')); + } else { + $this->Session->setFlash(__('The user could not be saved. Please, try again.')); + } + } + } + +/** + * admin_edit method + * + * @param string $id + * @return void + */ + public function admin_edit($id = null) { + $this->User->id = $id; + if (!$this->User->exists()) { + throw new NotFoundException(__('Invalid user')); + } + if ($this->request->is('post') || $this->request->is('put')) { + if ($this->User->save($this->request->data)) { + $this->Session->setFlash(__('The user has been saved')); + $this->redirect(array('action' => 'index')); + } else { + $this->Session->setFlash(__('The user could not be saved. Please, try again.')); + } + } else { + $this->request->data = $this->User->read(null, $id); + } + } + +/** + * admin_delete method + * + * @param string $id + * @return void + */ + public function admin_delete($id = null) { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(); + } + $this->User->id = $id; + if (!$this->User->exists()) { + throw new NotFoundException(__('Invalid user')); + } + if ($this->User->delete()) { + $this->Session->setFlash(__('User deleted')); + $this->redirect(array('action' => 'index')); + } + $this->Session->setFlash(__('User was not deleted')); + $this->redirect(array('action' => 'index')); + } + + + public function login() { + // FIXME implement authentication brute-force protection + if ($this->Auth->login()) { + $this->redirect($this->Auth->redirect()); + } else { + $this->Session->setFlash(__('Invalid username or password, try again')); + } + } + + public function routeafterlogin() { + // Terms and Conditions Page + if (!$this->Auth->user('termsaccepted')) { + $this->redirect(array('action' => 'terms')); + } + + // News page + $new_newsdate = new DateTime("2012-03-15"); + $newsdate = new DateTime($this->Auth->user('newsread')); + if ($new_newsdate > $newsdate) { + $this->redirect(array('action' => 'news')); + } + + // Events list + $this->redirect(array('controller' => 'events', 'action' => 'index')); + } + + public function logout() { + $this->Session->setFlash('Good-Bye'); + $this->redirect($this->Auth->logout()); + } + + + public function resetauthkey($id = null) { + if (!$id) { + $this->Session->setFlash(__('Invalid id for user', true), 'default', array(), 'error'); + $this->redirect(array('action'=>'index')); + } + if ('me' == $id ) $id = $this->Auth->user('id'); + + // only allow reset key for own account, except for admins + if (!$this->_isAdmin() && $id != $this->Auth->user('id')) { + throw new ForbiddenException('Not authorized to reset the key for this user'); + } + + // reset the key + $this->User->id = $id; + $newkey = $this->User->generateAuthKey(); + $this->User->saveField('authkey', $newkey); + $this->Session->setFlash(__('New authkey generated.', true)); + $this->redirect($this->referer()); + } + + public function memberslist() { + $this->loadModel('Signature'); + $this->loadModel('Event'); + + // Orglist + $fields = array('User.org', 'count(User.id) as `num_members`'); + $params = array('recursive' => 0, + 'fields' => $fields, + 'group' => array('User.org'), + 'order' => array('User.org'), + ); + $orgs = $this->User->find('all', $params); + $this->set('orgs', $orgs); + + // $fields = array('User.org', 'count(User.id) as `num_members`', 'count(Event.id) as `num_events`'); + // $params = array('recursive' => 0, + // 'fields' => $fields, + // 'group' => array('User.org'), + // 'order' => array('User.org'), + // ); + // $orgs = $this->Event->find('all', $params); + // $this->set('orgs', $orgs); + + + + + // What org posted what type of signature + // LATER beautify types_histogram + $this->loadModel('Signature'); + $fields = array('Event.org', 'Signature.type', 'count(Signature.type) as `num_types`'); + $params = array('recursive' => 0, + 'fields' => $fields, + 'group' => array('Signature.type', 'Event.org'), + 'order' => array('Event.org', 'num_types DESC'), + ); + $types_histogram = $this->Signature->find('all', $params); + $this->set('types_histogram', $types_histogram); + + + } + + public function terms() { + if ($this->request->is('post') || $this->request->is('put')) { + $this->User->id = $this->Auth->user('id'); + $this->User->saveField('termsaccepted', true); + + $this->_refreshAuth(); // refresh auth info + $this->Session->setFlash(__('You accepted the Terms and Conditions.')); + $this->redirect(array('action' => 'routeafterlogin')); + } + $this->set('termsaccepted', $this->Auth->user('termsaccepted')); + } + + public function news() { + $this->User->id = $this->Auth->user('id'); + $this->User->saveField('newsread', date("Y-m-d")); + $this->_refreshAuth(); // refresh auth info + } + + + + +} diff --git a/app/models/behaviors/empty b/app/Lib/empty old mode 100755 new mode 100644 similarity index 100% rename from app/models/behaviors/empty rename to app/Lib/empty diff --git a/app/models/datasources/empty b/app/Locale/eng/LC_MESSAGES/empty old mode 100755 new mode 100644 similarity index 100% rename from app/models/datasources/empty rename to app/Locale/eng/LC_MESSAGES/empty diff --git a/app/Model/AppModel.php b/app/Model/AppModel.php new file mode 100644 index 000000000..4c9b8bde7 --- /dev/null +++ b/app/Model/AppModel.php @@ -0,0 +1,34 @@ + array( + 'notempty' => array( + 'rule' => array('notempty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'date' => array( + 'date' => array( + 'rule' => array('date'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'risk' => array( + 'rule' => array('inList', array('Undefined', 'Low','Medium','High')), + 'message' => 'Options : Undefined, Low, Medium, High', + //'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + 'info' => array( + 'notempty' => array( + 'rule' => array('notempty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'user_id' => array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'alerted' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'uuid' => array( + 'uuid' => array( + 'rule' => array('uuid'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * belongsTo associations + * + * @var array + */ + public $belongsTo = array( + 'User' => array( + 'className' => 'User', + 'foreignKey' => 'user_id', + 'conditions' => '', + 'fields' => '', + 'order' => '' + ) + ); + +/** + * hasMany associations + * + * @var array + */ + public $hasMany = array( + 'Signature' => array( + 'className' => 'Signature', + 'foreignKey' => 'event_id', + 'dependent' => true, // cascade deletes + 'conditions' => '', + 'fields' => '', + 'order' => 'Signature.type ASC', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ) + ); + + function getRelatedEvents() { + // first get a list of related event_ids + // then do a single query to search for all the events with that id + $relatedEventIds = Array(); + foreach ($this->data['Signature'] as $signature ) { + if ($signature['type'] == 'other') + continue; // sigs of type 'other' should not be matched against the others + $conditions = array('Signature.value =' => $signature['value'], 'Signature.type =' => $signature['type']); + $similar_signatures = $this->Signature->find('all',array('conditions' => $conditions)); + foreach ($similar_signatures as $similar_signature) { + if ($this->id == $similar_signature['Signature']['event_id']) + continue; // same as this event, not needed in the list + $relatedEventIds[] = $similar_signature['Signature']['event_id']; + } + } + $conditions = array("Event.id" => $relatedEventIds); + $relatedEvents= $this->find('all', + array('conditions' => $conditions, + 'recursive' => 0, + 'order' => 'Event.date DESC', + 'fields' => 'Event.*' + ) + ); + return $relatedEvents; + } +} diff --git a/app/Model/Signature.php b/app/Model/Signature.php new file mode 100644 index 000000000..06f926ad4 --- /dev/null +++ b/app/Model/Signature.php @@ -0,0 +1,249 @@ + "DESC", "Signature.type" => "ASC"); +/** + * Validation rules + * + * @var array + */ + public $validate = array( + 'event_id' => array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'type' => array( + 'rule' => array('inList', array('md5','sha1', + 'filename', + 'ip-src', + 'ip-dst', + 'domain', + 'email-src', + 'email-dst', + 'email-subject', + 'email-attachment', + 'url', + 'user-agent', + 'regkey', + 'AS', + 'snort', + 'pattern-in-file', + 'other')), + 'message' => 'Options : md5, sha1, filename, ip, domain, email, url, regkey, AS, other, ...', + //'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + + ), + 'value' => array( + 'notempty' => array( + 'rule' => array('notempty'), + 'message' => 'Please fill in this field', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + 'userdefined' => array( + 'rule' => array('validateSignatureValue'), + 'message' => 'Value not in the right type/format. Please double check the value or select "other" for a type.', + //'allowEmpty' => false, + //'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'to_ids' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + 'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'uuid' => array( + 'uuid' => array( + 'rule' => array('uuid'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * belongsTo associations + * + * @var array + */ + public $belongsTo = array( + 'Event' => array( + 'className' => 'Event', + 'foreignKey' => 'event_id', + 'conditions' => '', + 'fields' => '', + 'order' => '' + ) + ); + + function validateSignatureValue ($fields) { + $value = $fields['value']; + $event_id = $this->data['Signature']['event_id']; + $type = $this->data['Signature']['type']; + + // check if the signature already exists in the same event + $params = array('recursive' => 0, + 'conditions' => array('Signature.event_id' => $event_id, + 'Signature.type' => $type, + 'Signature.value' => $value), + ); + if (0 != $this->find('count', $params) ) + return 'Signature already exists for this event.'; + + + // check data validation + switch($this->data['Signature']['type']) { + case 'md5': + if (preg_match("#^[0-9a-f]{32}$#i", $value)) + return true; + return 'Checksum has invalid lenght or format. Please double check the value or select "other" for a type.'; + break; + case 'sha1': + if (preg_match("#^[0-9a-f]{40}$#i", $value)) + return true; + return 'Checksum has invalid lenght or format. Please double check the value or select "other" for a type.'; + break; + case 'filename': + // no newline + if (!preg_match("#\n#", $value)) + return true; + break; + case 'ip-src': + $parts = explode("/", $value); + // [0] = the ip + // [1] = the network address + if (count($parts) <= 2 ) { + // ipv4 and ipv6 matching + if (filter_var($parts[0],FILTER_VALIDATE_IP)) { + // ip is validated, now check if we have a valid network mask + if (empty($parts[1])) + return true; + else if(is_numeric($parts[1]) && $parts[1] < 129) + return true; + } + } + return 'IP address has invalid format. Please double check the value or select "other" for a type.'; + break; + case 'ip-dst': + $parts = explode("/", $value); + // [0] = the ip + // [1] = the network address + if (count($parts) <= 2 ) { + // ipv4 and ipv6 matching + if (filter_var($parts[0],FILTER_VALIDATE_IP)) { + // ip is validated, now check if we have a valid network mask + if (empty($parts[1])) + return true; + else if(is_numeric($parts[1]) && $parts[1] < 129) + return true; + } + } + return 'IP address has invalid format. Please double check the value or select "other" for a type.'; + break; + case 'domain': + if(preg_match("#^[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $value)) + return true; + return 'Domain name has invalid format. Please double check the value or select "other" for a type.'; + break; + case 'email-src': + // we don't use the native function to prevent issues with partial email addresses + if(preg_match("#^[A-Z0-9._%+-]*@[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $value)) + return true; + return 'Email address has invalid format. Please double check the value or select "other" for a type.'; + break; + case 'email-dst': + // we don't use the native function to prevent issues with partial email addresses + if(preg_match("#^[A-Z0-9._%+-]*@[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $value)) + return true; + return 'Email address has invalid format. Please double check the value or select "other" for a type.'; + break; + case 'email-subject': + // no newline + if (!preg_match("#\n#", $value)) + return true; + break; + case 'email-attachment': + // no newline + if (!preg_match("#\n#", $value)) + return true; + break; + case 'url': + // no newline + if (!preg_match("#\n#", $value)) + return true; + break; + case 'user-agent': + // no newline + if (!preg_match("#\n#", $value)) + return true; + break; + case 'regkey': + // no newline + if (!preg_match("#\n#", $value)) + return true; + break; + case 'snort': + // no validation yet. TODO implement data validation on snort signature type + case 'other': + return true; + break; + } + + // default action is to return false + return true; + + } + + + + function getRelatedSignatures($signature) { + // LATER getRelatedSignatures($signature) this might become a performance bottleneck + $conditions = array('Signature.value =' => $signature['value'], + 'Signature.id !=' => $signature['id'], + 'Signature.type =' => $signature['type'], ); + // $fields = array('Event.*'); + $fields = array('Signature.*'); + + $similar_events = $this->find('all',array('conditions' => $conditions, + 'fields' => $fields, + 'order' => 'Signature.event_id DESC', ) + ); + return $similar_events; + } + +} diff --git a/app/Model/User.php b/app/Model/User.php new file mode 100644 index 000000000..66a1dc8bc --- /dev/null +++ b/app/Model/User.php @@ -0,0 +1,244 @@ + array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'password' => array( + 'minlength' => array( + 'rule' => array('minlength', 6), + 'message' => 'A password of a minimum length of 6 is required.', + //'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + 'complexity' => array( + 'rule' => array('complexPassword'), + 'message' => 'The password must contain at least one upper-case, one lower-case, one (digits or special character).', // TODO password strength requirements + //'allowEmpty' => false, + //'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'org' => array( + 'notempty' => array( + 'rule' => array('notempty'), + 'message' => 'Please specify the organisation where you are working.', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'email' => array( + 'email' => array( + 'rule' => array('email'), + 'message' => 'Please enter a valid email address.', + //'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + 'unique' => array( + 'rule' => 'isUnique', + 'message' => 'An account with this email address already exists.' + ), + ), + 'autoalert' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + 'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'authkey' => array( + 'notempty' => array( + 'rule' => array('notempty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'invited_by' => array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'gpgkey' => array( + 'notempty' => array( + 'rule' => array('validateGpgkey'), + 'message' => 'GPG key not valid, please enter a valid key.', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'nids_sid' => array( + 'numeric' => array( + 'rule' => array('numeric'), + 'message' => 'A SID should be an integer.', + 'allowEmpty' => false, + 'required' => true, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'termsaccepted' => array( + 'boolean' => array( + 'rule' => array('boolean'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + 'newsread' => array( + 'date' => array( + 'rule' => array('date'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * hasMany associations + * + * @var array + */ + public $hasMany = array( + 'Event' => array( + 'className' => 'Event', + 'foreignKey' => 'user_id', + 'dependent' => false, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ) + ); + +// var $actsAs = array('Acl' => array('type' => 'requester')); +// +// public function beforeValidate() { +// // Fix issue with an empty password being automagically hashed +// App::import('Core', 'Security'); // not sure whether this is necessary +// if ($this->data['User']['password'] == Security::hash('', null, true)) { +// $this->data['User']['password'] = ''; +// } +// return true; +// } + + public function beforeSave() { + if (isset($this->data[$this->alias]['password'])) { + $this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']); + } + return true; + } + + + /** + * Checks if the GPG key is a valid key + * But also import it in the keychain. + */ + function validateGpgkey($check) { + // LATER first remove the old gpgkey from the keychain + + // empty value + if (empty($check['gpgkey'])) + return true; + + // key is entered + require_once 'Crypt/GPG.php'; + $gpg = new Crypt_GPG(); + try { + $key_import_output = $gpg->importKey($check['gpgkey']); + if (!empty($key_import_output['fingerprint'])) { + return true; + } + } catch (Exception $e) { + debug($e); + return false; + } + } + + + function complexPassword($check) { + /* + 6 characters minimum + 1 or more upper-case letters + 1 or more lower-case letters + 1 or more digits or special characters + example: "EasyPeasy34" + */ + $value = array_values($check); + $value = $value[0]; + return preg_match('/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/', $value); + } + + /** + * Generates an authentication key for each user + */ + function generateAuthKey() { + //$key = sha1(mt_rand(30, 30).time()); + $length = 40; + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $char_len = strlen($characters)-1; + $key = ''; + for ($p = 0; $p < $length; $p++) { + $key .= $characters[rand(0, $char_len)]; + } + + return $key; + } +} diff --git a/app/tests/cases/components/empty b/app/Plugin/empty old mode 100755 new mode 100644 similarity index 100% rename from app/tests/cases/components/empty rename to app/Plugin/empty diff --git a/app/README.txt b/app/README.txt index bc3924b80..3a3af51f8 100755 --- a/app/README.txt +++ b/app/README.txt @@ -1,21 +1,16 @@ - - - + TODOs ----- Auth -- Use captcha authentication -- cleanup ACL and do it using the CakePHP concept -- password strength requirements +- Prevent bruteforce auth attempts implement auditing/logging system - add / edit events and signatures - failed / success logins (with source IP, headers,...) Security -- apply CSRF checks on the delete parameters by enabling security modules and rewriting some parts - force cookie reset after login @@ -26,10 +21,10 @@ Download CyDefSIG using git in the /var/www/ directory. cd /var/www/ git clone git@code.lab.modiss.be:cydefsig.git -Download and extract CakePHP 1.3 to the web root directory: +Download and extract CakePHP 2.x to the web root directory: cd /tmp/ -wget https://nodeload.github.com/cakephp/cakephp/tarball/1.3 +wget https://nodeload.github.com/cakephp/cakephp/tarball/2.1 tar zxvf cakephp-cakephp-.tar.gz cd cakephp-cakephp-* diff --git a/app/tests/cases/controllers/empty b/app/Test/Case/Controller/Component/empty old mode 100755 new mode 100644 similarity index 100% rename from app/tests/cases/controllers/empty rename to app/Test/Case/Controller/Component/empty diff --git a/app/Test/Case/Controller/EventsControllerTest.php b/app/Test/Case/Controller/EventsControllerTest.php new file mode 100644 index 000000000..1f326dbe1 --- /dev/null +++ b/app/Test/Case/Controller/EventsControllerTest.php @@ -0,0 +1,142 @@ +redirectUrl = $url; + } +} + +/** + * EventsController Test Case + * + */ +class EventsControllerTestCase extends CakeTestCase { +/** + * Fixtures + * + * @var array + */ + public $fixtures = array('app.event', 'app.user', 'app.group', 'app.signature'); + +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + $this->Events = new TestEventsController(); + $this->Events->constructClasses(); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->Events); + + parent::tearDown(); + } + +/** + * testIndex method + * + * @return void + */ + public function testIndex() { + + } +/** + * testView method + * + * @return void + */ + public function testView() { + + } +/** + * testAdd method + * + * @return void + */ + public function testAdd() { + + } +/** + * testEdit method + * + * @return void + */ + public function testEdit() { + + } +/** + * testDelete method + * + * @return void + */ + public function testDelete() { + + } +/** + * testAdminIndex method + * + * @return void + */ + public function testAdminIndex() { + + } +/** + * testAdminView method + * + * @return void + */ + public function testAdminView() { + + } +/** + * testAdminAdd method + * + * @return void + */ + public function testAdminAdd() { + + } +/** + * testAdminEdit method + * + * @return void + */ + public function testAdminEdit() { + + } +/** + * testAdminDelete method + * + * @return void + */ + public function testAdminDelete() { + + } +} diff --git a/app/Test/Case/Controller/SignaturesControllerTest.php b/app/Test/Case/Controller/SignaturesControllerTest.php new file mode 100644 index 000000000..98aa584b0 --- /dev/null +++ b/app/Test/Case/Controller/SignaturesControllerTest.php @@ -0,0 +1,142 @@ +redirectUrl = $url; + } +} + +/** + * SignaturesController Test Case + * + */ +class SignaturesControllerTestCase extends CakeTestCase { +/** + * Fixtures + * + * @var array + */ + public $fixtures = array('app.signature', 'app.event', 'app.user', 'app.group'); + +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + $this->Signatures = new TestSignaturesController(); + $this->Signatures->constructClasses(); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->Signatures); + + parent::tearDown(); + } + +/** + * testIndex method + * + * @return void + */ + public function testIndex() { + + } +/** + * testView method + * + * @return void + */ + public function testView() { + + } +/** + * testAdd method + * + * @return void + */ + public function testAdd() { + + } +/** + * testEdit method + * + * @return void + */ + public function testEdit() { + + } +/** + * testDelete method + * + * @return void + */ + public function testDelete() { + + } +/** + * testAdminIndex method + * + * @return void + */ + public function testAdminIndex() { + + } +/** + * testAdminView method + * + * @return void + */ + public function testAdminView() { + + } +/** + * testAdminAdd method + * + * @return void + */ + public function testAdminAdd() { + + } +/** + * testAdminEdit method + * + * @return void + */ + public function testAdminEdit() { + + } +/** + * testAdminDelete method + * + * @return void + */ + public function testAdminDelete() { + + } +} diff --git a/app/Test/Case/Controller/UsersControllerTest.php b/app/Test/Case/Controller/UsersControllerTest.php new file mode 100644 index 000000000..25bf8c463 --- /dev/null +++ b/app/Test/Case/Controller/UsersControllerTest.php @@ -0,0 +1,142 @@ +redirectUrl = $url; + } +} + +/** + * UsersController Test Case + * + */ +class UsersControllerTestCase extends CakeTestCase { +/** + * Fixtures + * + * @var array + */ + public $fixtures = array('app.user', 'app.group', 'app.event', 'app.signature'); + +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + $this->Users = new TestUsersController(); + $this->Users->constructClasses(); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->Users); + + parent::tearDown(); + } + +/** + * testIndex method + * + * @return void + */ + public function testIndex() { + + } +/** + * testView method + * + * @return void + */ + public function testView() { + + } +/** + * testAdd method + * + * @return void + */ + public function testAdd() { + + } +/** + * testEdit method + * + * @return void + */ + public function testEdit() { + + } +/** + * testDelete method + * + * @return void + */ + public function testDelete() { + + } +/** + * testAdminIndex method + * + * @return void + */ + public function testAdminIndex() { + + } +/** + * testAdminView method + * + * @return void + */ + public function testAdminView() { + + } +/** + * testAdminAdd method + * + * @return void + */ + public function testAdminAdd() { + + } +/** + * testAdminEdit method + * + * @return void + */ + public function testAdminEdit() { + + } +/** + * testAdminDelete method + * + * @return void + */ + public function testAdminDelete() { + + } +} diff --git a/app/tests/cases/helpers/empty b/app/Test/Case/Model/Behavior/empty old mode 100755 new mode 100644 similarity index 100% rename from app/tests/cases/helpers/empty rename to app/Test/Case/Model/Behavior/empty diff --git a/app/Test/Case/Model/EventTest.php b/app/Test/Case/Model/EventTest.php new file mode 100644 index 000000000..8e1d82e08 --- /dev/null +++ b/app/Test/Case/Model/EventTest.php @@ -0,0 +1,37 @@ +Event = ClassRegistry::init('Event'); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->Event); + + parent::tearDown(); + } + +} diff --git a/app/Test/Case/Model/GroupTest.php b/app/Test/Case/Model/GroupTest.php new file mode 100644 index 000000000..8d4fdba38 --- /dev/null +++ b/app/Test/Case/Model/GroupTest.php @@ -0,0 +1,37 @@ +Group = ClassRegistry::init('Group'); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->Group); + + parent::tearDown(); + } + +} diff --git a/app/Test/Case/Model/SignatureTest.php b/app/Test/Case/Model/SignatureTest.php new file mode 100644 index 000000000..49d9e88df --- /dev/null +++ b/app/Test/Case/Model/SignatureTest.php @@ -0,0 +1,37 @@ +Signature = ClassRegistry::init('Signature'); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->Signature); + + parent::tearDown(); + } + +} diff --git a/app/Test/Case/Model/UserTest.php b/app/Test/Case/Model/UserTest.php new file mode 100644 index 000000000..e78f24043 --- /dev/null +++ b/app/Test/Case/Model/UserTest.php @@ -0,0 +1,37 @@ +User = ClassRegistry::init('User'); + } + +/** + * tearDown method + * + * @return void + */ + public function tearDown() { + unset($this->User); + + parent::tearDown(); + } + +} diff --git a/app/tests/cases/models/empty b/app/Test/Case/View/Helper/empty old mode 100755 new mode 100644 similarity index 100% rename from app/tests/cases/models/empty rename to app/Test/Case/View/Helper/empty diff --git a/app/Test/Fixture/EventFixture.php b/app/Test/Fixture/EventFixture.php new file mode 100644 index 000000000..b4afefa68 --- /dev/null +++ b/app/Test/Fixture/EventFixture.php @@ -0,0 +1,41 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'date' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'info' => array('type' => 'text', 'null' => false, 'default' => NULL, 'key' => 'index', 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'alerted' => array('type' => 'boolean', 'null' => false, 'default' => '0'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'info' => array('column' => 'info', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + +/** + * Records + * + * @var array + */ + public $records = array( + array( + 'id' => 1, + 'org' => 'Lorem ipsum dolor sit amet', + 'date' => '2012-03-13', + 'info' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.', + 'user_id' => 1, + 'alerted' => 1, + 'uuid' => 'Lorem ipsum dolor sit amet' + ), + ); +} diff --git a/app/Test/Fixture/GroupFixture.php b/app/Test/Fixture/GroupFixture.php new file mode 100644 index 000000000..e126eddae --- /dev/null +++ b/app/Test/Fixture/GroupFixture.php @@ -0,0 +1,31 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + +/** + * Records + * + * @var array + */ + public $records = array( + array( + 'id' => 1, + 'name' => 'Lorem ipsum dolor sit amet' + ), + ); +} diff --git a/app/Test/Fixture/SignatureFixture.php b/app/Test/Fixture/SignatureFixture.php new file mode 100644 index 000000000..b2dfe261c --- /dev/null +++ b/app/Test/Fixture/SignatureFixture.php @@ -0,0 +1,39 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'event_id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'index'), + 'type' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100, 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'value' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_unicode_ci', 'charset' => 'utf8'), + 'to_ids' => array('type' => 'boolean', 'null' => false, 'default' => '1'), + 'uuid' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'event_id' => array('column' => 'event_id', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + +/** + * Records + * + * @var array + */ + public $records = array( + array( + 'id' => 1, + 'event_id' => 1, + 'type' => 'Lorem ipsum dolor sit amet', + 'value' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.', + 'to_ids' => 1, + 'uuid' => 'Lorem ipsum dolor sit amet' + ), + ); +} diff --git a/app/Test/Fixture/UserFixture.php b/app/Test/Fixture/UserFixture.php new file mode 100644 index 000000000..3e05763da --- /dev/null +++ b/app/Test/Fixture/UserFixture.php @@ -0,0 +1,51 @@ + array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), + 'group_id' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'password' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'key' => 'index', 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'org' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'email' => array('type' => 'string', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'autoalert' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'authkey' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 40, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'invited_by' => array('type' => 'integer', 'null' => false, 'default' => NULL), + 'gpgkey' => array('type' => 'text', 'null' => false, 'default' => NULL, 'collate' => 'utf8_bin', 'charset' => 'utf8'), + 'nids_sid' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'length' => 15), + 'termsaccepted' => array('type' => 'boolean', 'null' => false, 'default' => NULL), + 'newsread' => array('type' => 'date', 'null' => false, 'default' => NULL), + 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'username' => array('column' => 'password', 'unique' => 0)), + 'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_bin', 'engine' => 'MyISAM') + ); + +/** + * Records + * + * @var array + */ + public $records = array( + array( + 'id' => 1, + 'group_id' => 1, + 'password' => 'Lorem ipsum dolor sit amet', + 'org' => 'Lorem ipsum dolor sit amet', + 'email' => 'Lorem ipsum dolor sit amet', + 'autoalert' => 1, + 'authkey' => 'Lorem ipsum dolor sit amet', + 'invited_by' => 1, + 'gpgkey' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida, phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit, feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.', + 'nids_sid' => 1, + 'termsaccepted' => 1, + 'newsread' => '2012-03-13' + ), + ); +} diff --git a/app/tests/fixtures/empty b/app/Test/Fixture/empty old mode 100755 new mode 100644 similarity index 100% rename from app/tests/fixtures/empty rename to app/Test/Fixture/empty diff --git a/app/tests/groups/empty b/app/Vendor/empty old mode 100755 new mode 100644 similarity index 100% rename from app/tests/groups/empty rename to app/Vendor/empty diff --git a/app/views/elements/actions_menu.ctp b/app/View/Elements/actions_menu.ctp similarity index 100% rename from app/views/elements/actions_menu.ctp rename to app/View/Elements/actions_menu.ctp diff --git a/app/vendors/shells/tasks/empty b/app/View/Elements/empty old mode 100755 new mode 100644 similarity index 100% rename from app/vendors/shells/tasks/empty rename to app/View/Elements/empty diff --git a/app/View/Emails/html/default.ctp b/app/View/Emails/html/default.ctp new file mode 100644 index 000000000..9d8734706 --- /dev/null +++ b/app/View/Emails/html/default.ctp @@ -0,0 +1,25 @@ + + ' . $line . "

\n"; +endforeach; +?> \ No newline at end of file diff --git a/app/views/elements/email/text/body.ctp b/app/View/Emails/text/body.ctp similarity index 100% rename from app/views/elements/email/text/body.ctp rename to app/View/Emails/text/body.ctp diff --git a/app/View/Emails/text/default.ctp b/app/View/Emails/text/default.ctp new file mode 100644 index 000000000..29873b198 --- /dev/null +++ b/app/View/Emails/text/default.ctp @@ -0,0 +1,19 @@ + + \ No newline at end of file diff --git a/app/views/elements/email/text/new_event.ctp b/app/View/Emails/text/new_event.ctp similarity index 100% rename from app/views/elements/email/text/new_event.ctp rename to app/View/Emails/text/new_event.ctp diff --git a/app/View/Errors/error400.ctp b/app/View/Errors/error400.ctp new file mode 100644 index 000000000..09d590170 --- /dev/null +++ b/app/View/Errors/error400.ctp @@ -0,0 +1,31 @@ + +

+

+ : + '{$url}'" + ); ?> +

+ 0 ): + echo $this->element('exception_stack_trace'); +endif; +?> diff --git a/app/views/errors/error403.ctp b/app/View/Errors/error403.ctp similarity index 100% rename from app/views/errors/error403.ctp rename to app/View/Errors/error403.ctp diff --git a/app/View/Errors/error500.ctp b/app/View/Errors/error500.ctp new file mode 100644 index 000000000..6c500aa73 --- /dev/null +++ b/app/View/Errors/error500.ctp @@ -0,0 +1,28 @@ + +

+

+ : + +

+ 0 ): + echo $this->element('exception_stack_trace'); +endif; +?> diff --git a/app/views/events/add.ctp b/app/View/Events/add.ctp similarity index 84% rename from app/views/events/add.ctp rename to app/View/Events/add.ctp index c87fe634d..578be237b 100755 --- a/app/views/events/add.ctp +++ b/app/View/Events/add.ctp @@ -3,8 +3,6 @@
Form->hidden('user_id'); - echo $this->Form->hidden('org'); echo $this->Form->input('date'); echo $this->Form->input('risk'); echo $this->Form->input('info'); diff --git a/app/views/events/contact.ctp b/app/View/Events/contact.ctp similarity index 88% rename from app/views/events/contact.ctp rename to app/View/Events/contact.ctp index 7e2207f89..40fb95679 100755 --- a/app/views/events/contact.ctp +++ b/app/View/Events/contact.ctp @@ -2,7 +2,7 @@ Form->create('Event');?>
Form->value('Event.id'); ?> -

You are about to contact the person who reported event Form->value('Event.id'); ?>.
+

You are about to contact the person who reported event Form->value('Event.id'); ?>.
Feel free to add a custom message that will be sent to the reporter.
Your email address and details about the event will be added automagically to the message.

-

    element('actions_menu'); ?> diff --git a/app/views/events/edit.ctp b/app/View/Events/edit.ctp similarity index 89% rename from app/views/events/edit.ctp rename to app/View/Events/edit.ctp index 7f76d814b..e4aa2fa22 100755 --- a/app/views/events/edit.ctp +++ b/app/View/Events/edit.ctp @@ -4,8 +4,6 @@ Form->input('id'); - echo $this->Form->hidden('user_id'); - echo $this->Form->hidden('org'); echo $this->Form->input('date'); echo $this->Form->input('risk'); echo $this->Form->input('info'); diff --git a/app/views/events/export.ctp b/app/View/Events/export.ctp similarity index 83% rename from app/views/events/export.ctp rename to app/View/Events/export.ctp index cee462293..c9e0c3e83 100755 --- a/app/views/events/export.ctp +++ b/app/View/Events/export.ctp @@ -9,13 +9,13 @@ You can Html->link('reset', array('controller' => 'users', 'ac

    XML Export

    An automatic export of all events and signatures is available under a custom XML format.

    You can configure your tools to automatically download the following following file:

    -
    https://sig.cyber-defence.be/events/xml/
    +
    /events/xml/

    NIDS Export

    An automatic export of all network related signatures is available under the Snort rule format. Only signatures marked as to IDS are exported.

    You can configure your tools to automatically download the following following file:

    -
    https://sig.cyber-defence.be/events/nids/
    +
    /events/nids/

    Text Export

    @@ -23,7 +23,7 @@ You can Html->link('reset', array('controller' => 'users', 'ac

    You can configure your tools to automatically download the following following files:

     
    -https://sig.cyber-defence.be/events/text//
    +/events/text//
     
     

    diff --git a/app/views/events/index.ctp b/app/View/Events/index.ctp similarity index 64% rename from app/views/events/index.ctp rename to app/View/Events/index.ctp index e00ef5bdc..15a650d71 100755 --- a/app/views/events/index.ctp +++ b/app/View/Events/index.ctp @@ -9,33 +9,33 @@ Paginator->sort('date');?> Paginator->sort('risk');?> Paginator->sort('info');?> - + - onclick="document.location ='Html->url(array('action' => 'view', $event['Event']['id']), true) ;?>';"> -   + + + + Html->link($event['Event']['id'], array('controller' => 'events', 'action' => 'view', $event['Event']['id'])); ?> +           - + Html->link(__('Finish Edit', true), array('action' => 'alert', $event['Event']['id']), array(), 'Are you sure this event is complete and everyone should be alerted?'); + if (0 == $event['Event']['alerted'] && ($isAdmin || $event['Event']['org'] == $me['org'])) + echo $this->Form->postLink('Finish Edit', array('action' => 'alert', $event['Event']['id']), null, 'Are you sure this event is complete and everyone should be alerted?'); elseif (0 == $event['Event']['alerted']) echo 'Not finished editing'; ?> Html->link(__('Edit', true), array('action' => 'edit', $event['Event']['id'])); - echo $this->Html->link(__('Delete', true), array('action' => 'delete', $event['Event']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $event['Event']['id'])); + echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $event['Event']['id']), null, __('Are you sure you want to delete # %s?', $event['Event']['id'])); } ?> Html->link(__('View', true), array('action' => 'view', $event['Event']['id'])); ?> @@ -46,7 +46,7 @@

    Paginator->counter(array( - 'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true) + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') )); ?>

    @@ -58,7 +58,6 @@
-

    element('actions_menu'); ?> diff --git a/app/views/events/nids.ctp b/app/View/Events/nids.ctp similarity index 100% rename from app/views/events/nids.ctp rename to app/View/Events/nids.ctp diff --git a/app/views/events/text.ctp b/app/View/Events/text.ctp similarity index 100% rename from app/views/events/text.ctp rename to app/View/Events/text.ctp diff --git a/app/views/events/view.ctp b/app/View/Events/view.ctp similarity index 65% rename from app/views/events/view.ctp rename to app/View/Events/view.ctp index e76f389af..ce1190e01 100755 --- a/app/views/events/view.ctp +++ b/app/View/Events/view.ctp @@ -3,7 +3,9 @@ -
    • Html->link(__('Finish Edit', true), array('action' => 'alert', $event['Event']['id']), array(), 'Are you sure this event is complete and everyone should be alerted?'); ?>
    +
    • Form->postLink('Finish Edit', array('action' => 'alert', $event['Event']['id']), null, 'Are you sure this event is complete and everyone should be alerted?'); + ?>
    • Not finished editing
    @@ -14,39 +16,39 @@ -

    -
    - > - > +

    Event

    +
    +
    ID
    +
     
    - > - > +
    Org
    +
     
    - > - > +
    Date
    +
     
    - > - > +
    Risk
    +
     
    - > - > +
    Info
    +
     
diff --git a/app/View/Users/admin_add.ctp b/app/View/Users/admin_add.ctp new file mode 100644 index 000000000..4274e785d --- /dev/null +++ b/app/View/Users/admin_add.ctp @@ -0,0 +1,28 @@ +
+Form->create('User');?> +
+ + Form->input('password'); + echo $this->Form->input('org'); + echo $this->Form->input('email'); + echo $this->Form->input('autoalert'); + echo $this->Form->input('authkey'); + echo $this->Form->input('invited_by'); + echo $this->Form->input('gpgkey'); + echo $this->Form->input('nids_sid'); + echo $this->Form->input('termsaccepted'); + echo $this->Form->input('newsread'); + ?> +
+Form->end(__('Submit'));?> +
+
+

+
    + +
  • Html->link(__('List Users'), array('action' => 'index'));?>
  • +
  • Html->link(__('List Events'), array('controller' => 'events', 'action' => 'index')); ?>
  • +
  • Html->link(__('New Event'), array('controller' => 'events', 'action' => 'add')); ?>
  • +
+
diff --git a/app/View/Users/admin_edit.ctp b/app/View/Users/admin_edit.ctp new file mode 100644 index 000000000..6016876eb --- /dev/null +++ b/app/View/Users/admin_edit.ctp @@ -0,0 +1,30 @@ +
+Form->create('User');?> +
+ + Form->input('id'); + echo $this->Form->input('password'); + echo $this->Form->input('org'); + echo $this->Form->input('email'); + echo $this->Form->input('autoalert'); + echo $this->Form->input('authkey'); + echo $this->Form->input('invited_by'); + echo $this->Form->input('gpgkey'); + echo $this->Form->input('nids_sid'); + echo $this->Form->input('termsaccepted'); + echo $this->Form->input('newsread'); + ?> +
+Form->end(__('Submit'));?> +
+
+

+
    + +
  • Form->postLink(__('Delete'), array('action' => 'delete', $this->Form->value('User.id')), null, __('Are you sure you want to delete # %s?', $this->Form->value('User.id'))); ?>
  • +
  • Html->link(__('List Users'), array('action' => 'index'));?>
  • +
  • Html->link(__('List Events'), array('controller' => 'events', 'action' => 'index')); ?>
  • +
  • Html->link(__('New Event'), array('controller' => 'events', 'action' => 'add')); ?>
  • +
+
diff --git a/app/View/Users/admin_index.ctp b/app/View/Users/admin_index.ctp new file mode 100644 index 000000000..332166a74 --- /dev/null +++ b/app/View/Users/admin_index.ctp @@ -0,0 +1,62 @@ +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('id');?>Paginator->sort('password');?>Paginator->sort('org');?>Paginator->sort('email');?>Paginator->sort('autoalert');?>Paginator->sort('authkey');?>Paginator->sort('invited_by');?>Paginator->sort('gpgkey');?>Paginator->sort('nids_sid');?>Paginator->sort('termsaccepted');?>Paginator->sort('newsread');?>
            + Html->link(__('View'), array('action' => 'view', $user['User']['id'])); ?> + Html->link(__('Edit'), array('action' => 'edit', $user['User']['id'])); ?> + Form->postLink(__('Delete'), array('action' => 'delete', $user['User']['id']), null, __('Are you sure you want to delete # %s?', $user['User']['id'])); ?> +
+

+ Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>

+ +
+ Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); + ?> +
+
+
+

+
    +
  • Html->link(__('New User'), array('action' => 'add')); ?>
  • +
  • Html->link(__('List Events'), array('controller' => 'events', 'action' => 'index')); ?>
  • +
  • Html->link(__('New Event'), array('controller' => 'events', 'action' => 'add')); ?>
  • +
+
diff --git a/app/View/Users/admin_view.ctp b/app/View/Users/admin_view.ctp new file mode 100644 index 000000000..3e6031a42 --- /dev/null +++ b/app/View/Users/admin_view.ctp @@ -0,0 +1,117 @@ +
+
+
  • Html->link(__('Edit Profile', true), array('action' => 'edit', $user['User']['id'])); ?>
+
+

+
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+
+

+
    +
  • Html->link(__('Edit User'), array('action' => 'edit', $user['User']['id'])); ?>
  • +
  • Form->postLink(__('Delete User'), array('action' => 'delete', $user['User']['id']), null, __('Are you sure you want to delete # %s?', $user['User']['id'])); ?>
  • +
  • Html->link(__('List Users'), array('action' => 'index')); ?>
  • +
  • Html->link(__('New User'), array('action' => 'add')); ?>
  • +
  • Html->link(__('List Events'), array('controller' => 'events', 'action' => 'index')); ?>
  • +
  • Html->link(__('New Event'), array('controller' => 'events', 'action' => 'add')); ?>
  • +
+
+ diff --git a/app/views/users/edit.ctp b/app/View/Users/edit.ctp similarity index 94% rename from app/views/users/edit.ctp rename to app/View/Users/edit.ctp index 664883593..d056c9829 100755 --- a/app/views/users/edit.ctp +++ b/app/View/Users/edit.ctp @@ -4,7 +4,6 @@ Form->input('id'); - if ($isAdmin) echo $this->Form->input('group_id'); echo $this->Form->input('email'); echo $this->Form->input('password'); if ($isAdmin) echo $this->Form->input('org'); diff --git a/app/views/users/index.ctp b/app/View/Users/index.ctp similarity index 100% rename from app/views/users/index.ctp rename to app/View/Users/index.ctp diff --git a/app/views/users/login.ctp b/app/View/Users/login.ctp similarity index 100% rename from app/views/users/login.ctp rename to app/View/Users/login.ctp diff --git a/app/views/users/memberslist.ctp b/app/View/Users/memberslist.ctp similarity index 76% rename from app/views/users/memberslist.ctp rename to app/View/Users/memberslist.ctp index eef1fcf3c..9181627df 100644 --- a/app/views/users/memberslist.ctp +++ b/app/View/Users/memberslist.ctp @@ -1,19 +1,14 @@
-

+

Members

- > + @@ -30,15 +25,9 @@ - > + diff --git a/app/views/users/news.ctp b/app/View/Users/news.ctp similarity index 82% rename from app/views/users/news.ctp rename to app/View/Users/news.ctp index 24049a50b..5bd811955 100644 --- a/app/views/users/news.ctp +++ b/app/View/Users/news.ctp @@ -1,6 +1,12 @@

News

March 2012

+

Backend rewrite + security
+Complete rewrite of the backend code to migrate to CakePHP 2.x (from CakePHP 1.3).
+During this rewrite the code was cleaned up, CSRF protection should now be present on all the important actions.
+Password strength validation, anti-bruteforce has been implemented.
+Some intermittent bugs might have slipped in during the (manual) conversion. Please contact me user1088@qet.be to report any issues. +

Terms and News
Terms and conditions have been enabled. You should only see this page once.
When new software updates of CyDefSIG are installed you will see the news page.

diff --git a/app/views/users/terms.ctp b/app/View/Users/terms.ctp similarity index 99% rename from app/views/users/terms.ctp rename to app/View/Users/terms.ctp index dbddab390..edf228358 100644 --- a/app/views/users/terms.ctp +++ b/app/View/Users/terms.ctp @@ -43,7 +43,7 @@ holder or other party has been advised of the possibility of such damages. Form->create('User'); echo $this->Form->hidden('termsaccepted', array('default'=> '1')); echo $this->Form->end(__('Accept Terms', true)); diff --git a/app/View/Users/view.ctp b/app/View/Users/view.ctp new file mode 100755 index 000000000..9c45cafbd --- /dev/null +++ b/app/View/Users/view.ctp @@ -0,0 +1,58 @@ +
+
+
  • Html->link(__('Edit Profile', true), array('action' => 'edit', $user['User']['id'])); ?>
+
+

+
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ + (Html->link('reset', array('controller' => 'users', 'action' => 'resetauthkey', $user['User']['id']));?>) +   +
+
+
+ +   +
+
+
+ +   +
+
+
+ +   +
+
+
+
+

+
    +
  • Html->link(__('Edit User', true), array('action' => 'edit', $user['User']['id'])); ?>
  • +
  • Html->link(__('Delete User', true), array('action' => 'delete', $user['User']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $user['User']['id'])); ?>
  • +
  •  
  • + element('actions_menu'); ?> +
+
diff --git a/app/app_controller.php b/app/app_controller.php deleted file mode 100755 index 5a9e4c2a0..000000000 --- a/app/app_controller.php +++ /dev/null @@ -1,272 +0,0 @@ -Auth->authorize = 'actions'; - $this->Auth->userModel = 'User'; - $this->Auth->fields = array('username' => 'email', 'password' => 'password'); - $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); - $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'routeafterlogin'); - $this->Auth->loginRedirect = array('controller' => 'events', 'action' => 'index'); - - $this->Auth->actionPath = 'controllers/'; - - $this->Auth->allowedActions = array('build_acl', 'initDB'); // FIXME remove build_acl - - } - - - /** - * Convert an array to the same array but with the values also as index instead of an interface_exists - */ - function _arrayToValuesIndexArray($old_array) { - $new_array = Array(); - foreach ($old_array as $value) - $new_array[$value] = $value; - return $new_array; - } - - /** - * checks if the currently logged user is an administrator - */ - function isAdmin() { - // TODO group membership should be checked correctly. - // but as quick workaround I check if the group_id = 1 = admin - $user = $this->Auth->user(); - - if (1 == $user['User']['group_id']) - return true; - else - return false; - } - - - - - - /** - * These functions will look at every controller in your application. - * It will add any non-private, non Controller methods to the Acl table, - * nicely nested underneath the owning controller. You can add and run this - * in your AppController or any controller for that matter, just be sure to - * remove it before putting your application into production. - * Now run the action in your browser, eg. http://localhost/groups/build_acl, - * This will build your ACO table. - */ - - function build_acl() { - if (!Configure::read('debug')) { - return $this->_stop(); - } - $log = array(); - - $aco =& $this->Acl->Aco; - $root = $aco->node('controllers'); - if (!$root) { - $aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers')); - $root = $aco->save(); - $root['Aco']['id'] = $aco->id; - $log[] = 'Created Aco node for controllers'; - } else { - $root = $root[0]; - } - - App::import('Core', 'File'); - $Controllers = App::objects('controller'); - $appIndex = array_search('App', $Controllers); - if ($appIndex !== false ) { - unset($Controllers[$appIndex]); - } - $baseMethods = get_class_methods('Controller'); - $baseMethods[] = 'build_acl'; - - $Plugins = $this->_getPluginControllerNames(); - $Controllers = array_merge($Controllers, $Plugins); - - // look at each controller in app/controllers - foreach ($Controllers as $ctrlName) { - $methods = $this->_getClassMethods($this->_getPluginControllerPath($ctrlName)); - - // Do all Plugins First - if ($this->_isPlugin($ctrlName)){ - $pluginNode = $aco->node('controllers/'.$this->_getPluginName($ctrlName)); - if (!$pluginNode) { - $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginName($ctrlName))); - $pluginNode = $aco->save(); - $pluginNode['Aco']['id'] = $aco->id; - $log[] = 'Created Aco node for ' . $this->_getPluginName($ctrlName) . ' Plugin'; - } - } - // find / make controller node - $controllerNode = $aco->node('controllers/'.$ctrlName); - if (!$controllerNode) { - if ($this->_isPlugin($ctrlName)){ - $pluginNode = $aco->node('controllers/' . $this->_getPluginName($ctrlName)); - $aco->create(array('parent_id' => $pluginNode['0']['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginControllerName($ctrlName))); - $controllerNode = $aco->save(); - $controllerNode['Aco']['id'] = $aco->id; - $log[] = 'Created Aco node for ' . $this->_getPluginControllerName($ctrlName) . ' ' . $this->_getPluginName($ctrlName) . ' Plugin Controller'; - } else { - $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName)); - $controllerNode = $aco->save(); - $controllerNode['Aco']['id'] = $aco->id; - $log[] = 'Created Aco node for ' . $ctrlName; - } - } else { - $controllerNode = $controllerNode[0]; - } - - //clean the methods. to remove those in Controller and private actions. - foreach ($methods as $k => $method) { - if (strpos($method, '_', 0) === 0) { - unset($methods[$k]); - continue; - } - if (in_array($method, $baseMethods)) { - unset($methods[$k]); - continue; - } - $methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method); - if (!$methodNode) { - $aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method)); - $methodNode = $aco->save(); - $log[] = 'Created Aco node for '. $method; - } - } - } - if(count($log)>0) { - debug($log); - } - } - - function _getClassMethods($ctrlName = null) { - App::import('Controller', $ctrlName); - if (strlen(strstr($ctrlName, '.')) > 0) { - // plugin's controller - $num = strpos($ctrlName, '.'); - $ctrlName = substr($ctrlName, $num+1); - } - $ctrlclass = $ctrlName . 'Controller'; - $methods = get_class_methods($ctrlclass); - - // Add scaffold defaults if scaffolds are being used - $properties = get_class_vars($ctrlclass); - if (array_key_exists('scaffold',$properties)) { - if($properties['scaffold'] == 'admin') { - $methods = array_merge($methods, array('admin_add', 'admin_edit', 'admin_index', 'admin_view', 'admin_delete')); - } else { - $methods = array_merge($methods, array('add', 'edit', 'index', 'view', 'delete')); - } - } - return $methods; - } - - function _isPlugin($ctrlName = null) { - $arr = String::tokenize($ctrlName, '/'); - if (count($arr) > 1) { - return true; - } else { - return false; - } - } - - function _getPluginControllerPath($ctrlName = null) { - $arr = String::tokenize($ctrlName, '/'); - if (count($arr) == 2) { - return $arr[0] . '.' . $arr[1]; - } else { - return $arr[0]; - } - } - - function _getPluginName($ctrlName = null) { - $arr = String::tokenize($ctrlName, '/'); - if (count($arr) == 2) { - return $arr[0]; - } else { - return false; - } - } - - function _getPluginControllerName($ctrlName = null) { - $arr = String::tokenize($ctrlName, '/'); - if (count($arr) == 2) { - return $arr[1]; - } else { - return false; - } - } - - /** - * Get the names of the plugin controllers ... - * - * This function will get an array of the plugin controller names, and - * also makes sure the controllers are available for us to get the - * method names by doing an App::import for each plugin controller. - * - * @return array of plugin names. - * - */ - function _getPluginControllerNames() { - App::import('Core', 'File', 'Folder'); - $paths = Configure::getInstance(); - $folder =& new Folder(); - $folder->cd(APP . 'plugins'); - - // Get the list of plugins - $Plugins = $folder->read(); - $Plugins = $Plugins[0]; - $arr = array(); - - // Loop through the plugins - foreach($Plugins as $pluginName) { - // Change directory to the plugin - $didCD = $folder->cd(APP . 'plugins'. DS . $pluginName . DS . 'controllers'); - // Get a list of the files that have a file name that ends - // with controller.php - $files = $folder->findRecursive('.*_controller\.php'); - - // Loop through the controllers we found in the plugins directory - foreach($files as $fileName) { - // Get the base file name - $file = basename($fileName); - - // Get the controller name - $file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php'))); - if (!preg_match('/^'. Inflector::humanize($pluginName). 'App/', $file)) { - if (!App::import('Controller', $pluginName.'.'.$file)) { - debug('Error importing '.$file.' for plugin '.$pluginName); - } else { - /// Now prepend the Plugin name ... - // This is required to allow us to fetch the method names. - $arr[] = Inflector::humanize($pluginName) . "/" . $file; - } - } - } - } - return $arr; - } - - - - - - - - - - - - - - -} -?> diff --git a/app/app_error.php b/app/app_error.php deleted file mode 100755 index 63ecc68f9..000000000 --- a/app/app_error.php +++ /dev/null @@ -1,27 +0,0 @@ -controller->here; - } - $url = Router::normalize($url); - $this->controller->header("HTTP/1.0 403 Forbidden"); - $this->controller->set(array( - 'code' => '403', - 'name' => __('Forbidden', true), - 'message' => $message, - 'base' => $this->controller->base - )); - $this->_outputMessage('error403'); - } -} -?> diff --git a/app/config/bootstrap.php b/app/config/bootstrap.php deleted file mode 100755 index a1d233575..000000000 --- a/app/config/bootstrap.php +++ /dev/null @@ -1,62 +0,0 @@ - array('/full/path/to/plugins/', '/next/full/path/to/plugins/'), - * 'models' => array('/full/path/to/models/', '/next/full/path/to/models/'), - * 'views' => array('/full/path/to/views/', '/next/full/path/to/views/'), - * 'controllers' => array('/full/path/to/controllers/', '/next/full/path/to/controllers/'), - * 'datasources' => array('/full/path/to/datasources/', '/next/full/path/to/datasources/'), - * 'behaviors' => array('/full/path/to/behaviors/', '/next/full/path/to/behaviors/'), - * 'components' => array('/full/path/to/components/', '/next/full/path/to/components/'), - * 'helpers' => array('/full/path/to/helpers/', '/next/full/path/to/helpers/'), - * 'vendors' => array('/full/path/to/vendors/', '/next/full/path/to/vendors/'), - * 'shells' => array('/full/path/to/shells/', '/next/full/path/to/shells/'), - * 'locales' => array('/full/path/to/locale/', '/next/full/path/to/locale/') - * )); - * - */ - -/** - * As of 1.3, additional rules for the inflector are added below - * - * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); - * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); - * - */ - -Configure::write('CyDefSIG.baseurl', 'https://sig.cyber-defence.be'); - -Configure::write('CyDefSIG.showorg', 'false'); // show the name of the organisation that uploaded the data - -Configure::write('GnuPG.onlyencrypted', 'true'); // only allow encrypted email // do not allow plaintext mails -Configure::write('GnuPG.email', 'sig@cyber-defence.be'); -Configure::write('GnuPG.password', ''); -// Configure::write('GnuPG.homedir', ''); // LATER let the user choose the gnupg homedir using putenv('GNUPGHOME=/home/sender/.gnupg'); - -Configure::write('Recaptcha.publicKey', ''); -Configure::write('Recaptcha.privateKey', ''); diff --git a/app/config/core.php b/app/config/core.php deleted file mode 100755 index c7cbaa261..000000000 --- a/app/config/core.php +++ /dev/null @@ -1,317 +0,0 @@ -cacheAction = true. - * - */ - //Configure::write('Cache.check', true); - -/** - * Defines the default error type when using the log() function. Used for - * differentiating error logging and debugging. Currently PHP supports LOG_DEBUG. - */ - define('LOG_ERROR', 2); - -/** - * The preferred session handling method. Valid values: - * - * 'php' Uses settings defined in your php.ini. - * 'cake' Saves session files in CakePHP's /tmp directory. - * 'database' Uses CakePHP's database sessions. - * - * To define a custom session handler, save it at /app/config/.php. - * Set the value of 'Session.save' to to utilize it in CakePHP. - * - * To use database sessions, run the app/config/schema/sessions.php schema using - * the cake shell command: cake schema create Sessions - * - */ - Configure::write('Session.save', 'cake'); - -/** - * The model name to be used for the session model. - * - * 'Session.save' must be set to 'database' in order to utilize this constant. - * - * The model name set here should *not* be used elsewhere in your application. - */ - //Configure::write('Session.model', 'Session'); - -/** - * The name of the table used to store CakePHP database sessions. - * - * 'Session.save' must be set to 'database' in order to utilize this constant. - * - * The table name set here should *not* include any table prefix defined elsewhere. - * - * Please note that if you set a value for Session.model (above), any value set for - * Session.table will be ignored. - * - * [Note: Session.table is deprecated as of CakePHP 1.3] - */ - //Configure::write('Session.table', 'cake_sessions'); - -/** - * The DATABASE_CONFIG::$var to use for database session handling. - * - * 'Session.save' must be set to 'database' in order to utilize this constant. - */ - //Configure::write('Session.database', 'default'); - -/** - * The name of CakePHP's session cookie. - * - * Note the guidelines for Session names states: "The session name references - * the session id in cookies and URLs. It should contain only alphanumeric - * characters." - * @link http://php.net/session_name - */ - Configure::write('Session.cookie', 'CYDEFSIG_SESS'); - -/** - * Session time out time (in seconds). - * Actual value depends on 'Security.level' setting. - */ - Configure::write('Session.timeout', '120'); - -/** - * If set to false, sessions are not automatically started. - */ - Configure::write('Session.start', true); - -/** - * When set to false, HTTP_USER_AGENT will not be checked - * in the session. You might want to set the value to false, when dealing with - * older versions of IE, Chrome Frame or certain web-browsing devices and AJAX - */ - Configure::write('Session.checkAgent', true); - -/** - * The level of CakePHP security. The session timeout time defined - * in 'Session.timeout' is multiplied according to the settings here. - * Valid values: - * - * 'high' Session timeout in 'Session.timeout' x 10 - * 'medium' Session timeout in 'Session.timeout' x 100 - * 'low' Session timeout in 'Session.timeout' x 300 - * - * CakePHP session IDs are also regenerated between requests if - * 'Security.level' is set to 'high'. - */ - Configure::write('Security.level', 'medium'); - -/** - * A random string used in security hashing methods. - */ - Configure::write('Security.salt', 'Rooraenietu8Eeyo 0. Set to 'force' to always enable - * timestamping regardless of debug value. - */ - //Configure::write('Asset.timestamp', true); -/** - * Compress CSS output by removing comments, whitespace, repeating tags, etc. - * This requires a/var/cache directory to be writable by the web server for caching. - * and /vendors/csspp/csspp.php - * - * To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css(). - */ - //Configure::write('Asset.filter.css', 'css.php'); - -/** - * Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the - * output, and setting the config below to the name of the script. - * - * To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link(). - */ - //Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php'); - -/** - * The classname and database used in CakePHP's - * access control lists. - */ - Configure::write('Acl.classname', 'DbAcl'); - Configure::write('Acl.database', 'default'); - -/** - * If you are on PHP 5.3 uncomment this line and correct your server timezone - * to fix the date & time related errors. - */ - //date_default_timezone_set('UTC'); - -/** - * - * Cache Engine Configuration - * Default settings provided below - * - * File storage engine. - * - * Cache::config('default', array( - * 'engine' => 'File', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path - * 'prefix' => 'cake_', //[optional] prefix every cache file with this string - * 'lock' => false, //[optional] use file locking - * 'serialize' => true, [optional] - * )); - * - * - * APC (http://pecl.php.net/package/APC) - * - * Cache::config('default', array( - * 'engine' => 'Apc', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string - * )); - * - * Xcache (http://xcache.lighttpd.net/) - * - * Cache::config('default', array( - * 'engine' => 'Xcache', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string - * 'user' => 'user', //user from xcache.admin.user settings - * 'password' => 'password', //plaintext password (xcache.admin.pass) - * )); - * - * - * Memcache (http://www.danga.com/memcached/) - * - * Cache::config('default', array( - * 'engine' => 'Memcache', //[required] - * 'duration'=> 3600, //[optional] - * 'probability'=> 100, //[optional] - * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string - * 'servers' => array( - * '127.0.0.1:11211' // localhost, default port 11211 - * ), //[optional] - * 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) - * 'persistent' => true, // [optional] set this to false for non-persistent connections - * )); - * - */ - Cache::config('default', array('engine' => 'File')); diff --git a/app/config/database.php b/app/config/database.php deleted file mode 100755 index 119ea4e3e..000000000 --- a/app/config/database.php +++ /dev/null @@ -1,87 +0,0 @@ - The name of a supported driver; valid options are as follows: - * mysql - MySQL 4 & 5, - * mysqli - MySQL 4 & 5 Improved Interface (PHP5 only), - * sqlite - SQLite (PHP5 only), - * postgres - PostgreSQL 7 and higher, - * mssql - Microsoft SQL Server 2000 and higher, - * db2 - IBM DB2, Cloudscape, and Apache Derby (http://php.net/ibm-db2) - * oracle - Oracle 8 and higher - * firebird - Firebird/Interbase - * sybase - Sybase ASE - * adodb-[drivername] - ADOdb interface wrapper (see below), - * odbc - ODBC DBO driver - * - * You can add custom database drivers (or override existing drivers) by adding the - * appropriate file to app/models/datasources/dbo. Drivers should be named 'dbo_x.php', - * where 'x' is the name of the database. - * - * persistent => true / false - * Determines whether or not the database should use a persistent connection - * - * connect => - * ADOdb set the connect to one of these - * (http://phplens.com/adodb/supported.databases.html) and - * append it '|p' for persistent connection. (mssql|p for example, or just mssql for not persistent) - * For all other databases, this setting is deprecated. - * - * host => - * the host you connect to the database. To add a socket or port number, use 'port' => # - * - * prefix => - * Uses the given prefix for all the tables in this database. This setting can be overridden - * on a per-table basis with the Model::$tablePrefix property. - * - * schema => - * For Postgres and DB2, specifies which schema you would like to use the tables in. Postgres defaults to - * 'public', DB2 defaults to empty. - * - * encoding => - * For MySQL, MySQLi, Postgres and DB2, specifies the character encoding to use when connecting to the - * database. Uses database default. - * - */ -class DATABASE_CONFIG { - - var $default = array( - 'driver' => 'mysql', - 'persistent' => false, - 'host' => 'localhost', - 'login' => 'cyberdefence', - 'port' => 8889, - 'password' => '', - 'database' => 'cyberdefence_sig', - 'prefix' => '', - ); - -} diff --git a/app/controllers/events_controller.php b/app/controllers/events_controller.php deleted file mode 100755 index e42917f7d..000000000 --- a/app/controllers/events_controller.php +++ /dev/null @@ -1,773 +0,0 @@ - 50, - 'order' => array( - 'Event.date' => 'DESC' - ) - ); - var $components = array('Security', 'Email'); - var $helpers = array('Xml'); - - function beforeFilter() { - $this->Auth->allow('xml'); - $this->Auth->allow('snort'); // deprecated - $this->Auth->allow('nids'); - - // Prevent XSRF - $this->Security->requireAuth('add', 'edit', 'contact'); - //$this->Security->requirePost('delete'); // FIXME do this for every controller and fix the urls in the pages - - // These variables are required for every view - $me_user = $this->Auth->user(); - $this->set('me', $me_user['User']); - $this->set('isAdmin', $this->isAdmin()); - } - - - function index() { - // list the events - $this->Event->recursive = 0; - $this->set('events', $this->paginate()); - - $me_user = $this->Auth->user(); - if (empty($me_user['User']['gpgkey'])) { - $this->Session->setFlash('No GPG key set in your profile. To receive emails, submit your public key in your profile.', 'default', array(), 'gpg'); - } - } - - function view($id = null) { - if (!$id) { - $this->Session->setFlash('Invalid event', 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - $this->set('event', $this->Event->read(null, $id)); - $this->set('relatedEvents', $this->Event->getRelatedEvents()); - - $related_signatures = array(); - $this->loadModel('Signature'); - foreach ($this->Event->data['Signature'] as $signature) { - $related_signatures[$signature['id']] = $this->Signature->getRelatedSignatures($signature); - } - $this->set('relatedSignatures', $related_signatures); - - } - - function add() { - $user = $this->Auth->user(); - if (!empty($this->data)) { - // force check userid and orgname if its from yourself - $this->data['Event']['user_id'] = $user['User']['id']; - $this->data['Event']['org'] = $user['User']['org']; - $this->Event->create(); - if ($this->Event->save($this->data)) { - $this->Session->setFlash(__('The event has been saved', true)); - $this->redirect(array('action' => 'view', $this->Event->getId())); - } else { - $this->Session->setFlash('The event could not be saved. Please, try again.', 'default', array(), 'error'); - } - } - - // combobox for risks - $risks = $this->Event->validate['risk']['allowedChoice']['rule'][1]; - $risks = $this->_arrayToValuesIndexArray($risks); - $this->set('risks',compact('risks')); - } - - function edit($id = null) { - if (!$id && empty($this->data)) { - $this->Session->setFlash(__('Invalid event', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - // only edit own events - $user = $this->Auth->user(); - $old_event = $this->Event->read(null, $id); - if (!$this->isAdmin() && $user['User']['org'] != $old_event['Event']['org']) { - $this->Session->setFlash(__('You can only edit events from your organisation.', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'view', $id)); - } - // form submit - if (!empty($this->data)) { - // always force the user and org, but do not force it for admins - if (!$this->isAdmin()) { - $this->data['Event']['user_id'] = $user['User']['id']; - $this->data['Event']['org'] = $user['User']['org']; - } - // we probably also want to remove the alerted flag - $this->data['Event']['alerted'] = 0; - - if ($this->Event->save($this->data)) { - // redirect - $this->Session->setFlash(__('The event has been saved', true)); - $this->redirect(array('action' => 'view', $id)); - } else { - $this->Session->setFlash(__('The event could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - } - // no form submit - if (empty($this->data)) { - $this->data = $this->Event->read(null, $id); - } - - // combobox for types - $risks = $this->Event->validate['risk']['allowedChoice']['rule'][1]; - $risks = $this->_arrayToValuesIndexArray($risks); - $this->set('risks',compact('risks')); - } - - function delete($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid id for event', true)); - $this->redirect(array('action'=>'index')); - } - // only delete own events - $user = $this->Auth->user(); - $old_event = $this->Event->read(null, $id); - if (!$this->isAdmin() && $user['User']['org'] != $old_event['Event']['org']) { - $this->Session->setFlash(__('You can only delete events from your organisation.', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'view', $id)); - } - // delete event or throw error - if ($this->Event->delete($id)) { - $this->Session->setFlash(__('Event deleted', true)); - $this->redirect(array('action'=>'index')); - } - $this->Session->setFlash(__('Event was not deleted', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - - /** - * Send out an alert email to all the users that wanted to be notified. - * Users with a GPG key will get the mail encrypted, other users will get the mail unencrypted - */ - function alert($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid id for event', true), 'default', array(), 'error'); - $this->redirect(array('action'=>'index')); - } - // only allow alert for own events or admins - $user = $this->Auth->user(); - $old_event = $this->Event->read(null, $id); - if (!$this->isAdmin() && $user['User']['org'] != $old_event['Event']['org']) { - $this->Session->setFlash(__('You can only send alerts for events from your organisation.', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'view', $id)); - } - - // fetch the event and build the body - $event = $this->Event->read(null, $id); - if (1 == $event['Event']['alerted']) { - $this->Session->setFlash(__('Everyone has already been alerted for this event. To alert again, first edit this event.', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'view', $id)); - } - - // The mail body, Sanitize::html() is NOT needed as we are sending plain-text mails. - $body = ""; - $appendlen = 20; - $body .= 'URL : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$event['Event']['id']."\n"; - $body .= 'Event : '.$event['Event']['id']."\n"; - $body .= 'Date : '.$event['Event']['date']."\n"; - if ('true' == Configure::read('CyDefSIG.showorg')) { - $body .= 'Reported by : '.$event['Event']['org']."\n"; - } - $body .= 'Risk : '.$event['Event']['risk']."\n"; - $relatedEvents = $this->Event->getRelatedEvents($id); - if (!empty($relatedEvents)) { - foreach ($relatedEvents as $relatedEvent){ - $body .= 'Related to : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$relatedEvent['Event']['id'].' ('.$relatedEvent['Event']['date'].')'."\n" ; - - } - } - $body .= 'Info : '."\n"; - $body .= $event['Event']['info']."\n"; - $body .= "\n"; - $body .= 'Signatures :'."\n"; - $body_temp_other = ""; - if (!empty($event['Signature'])) { - foreach ($event['Signature'] as $signature){ - $line = '- '.$signature['type'].str_repeat(' ', $appendlen - 2 - strlen( $signature['type'])).': '.$signature['value']."\n"; - if ('other' == $signature['type']) // append the 'other' signature types to the bottom. - $body_temp_other .= $line; - else $body .= $line; - } - } - $body .= "\n"; - $body .= $body_temp_other; // append the 'other' signature types to the bottom. - - // sign the body - require_once 'Crypt/GPG.php'; - $gpg = new Crypt_GPG(); - $gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password')); - $body_signed = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR); - - - $this->loadModel('Users'); // LATER should be loadModel('User'), and change all subsequent calls to this object - - // - // Build a list of the recipients that get a non-encrypted mail - // But only do this it if it is allowed in the bootstrap.php file. - // - if ('false' == Configure::read('GnuPG.onlyencrypted')) { - $alert_users = $this->Users->find('all', array( - 'conditions' => array('Users.autoalert' => 1, - 'Users.gpgkey =' => ""), - 'recursive' => 0, - ) ); - $alert_emails = Array(); - foreach ($alert_users as $user) { - $alert_emails[] = $user['Users']['email']; - } - // prepare the the unencrypted email - $this->Email->from = "CyDefSIG "; - $this->Email->to = "CyDefSIG "; - $this->Email->return = "sig@cyber-defence.be"; - $this->Email->bcc = $alert_emails; - $this->Email->subject = "[CyDefSIG] Event ".$id." - ".$event['Event']['risk']." - TLP Amber"; - //$this->Email->delivery = 'debug'; // do not really send out mails, only display it on the screen - $this->Email->template = 'body'; - $this->Email->sendAs = 'text'; // both text or html - $this->set('body', $body_signed); - // send it - $this->Email->send(); - // If you wish to send multiple emails using a loop, you'll need - // to reset the email fields using the reset method of the Email component. - $this->Email->reset(); - } - - // - // Build a list of the recipients that wish to receive encrypted mails. - // - $alert_users = $this->Users->find('all', array( - 'conditions' => array('Users.autoalert' => 1, - 'Users.gpgkey !=' => ""), - 'recursive' => 0, - ) ); - // encrypt the mail for each user and send it separately - foreach ($alert_users as $user) { - // send the email - $this->Email->from = "CyDefSIG "; - $this->Email->to = "<".$user['Users']['email'].">"; - $this->Email->return = "sig@cyber-defence.be"; - $this->Email->subject = "[CyDefSIG] Event ".$id." - ".$event['Event']['risk']." - TLP Amber"; - //$this->Email->delivery = 'debug'; // do not really send out mails, only display it on the screen - $this->Email->template = 'body'; - $this->Email->sendAs = 'text'; // both text or html - - // import the key of the user into the keyring // LATER do that when the user uploads a new key, but don't forget to remove the old keys before - $key_import_output = $gpg->importKey($user['Users']['gpgkey']); - // say what key should be used to encrypt - $gpg = new Crypt_GPG(); - //$gpg->addEncryptKey($user['Users']['email']); - $gpg->addEncryptKey($key_import_output['fingerprint']); // use the key that was given in the import - - $body_enc_sig = $gpg->encrypt($body_signed, true); - - $this->set('body', $body_enc_sig); - //debug($body_enc_sig); - $this->Email->send(); - // If you wish to send multiple emails using a loop, you'll need - // to reset the email fields using the reset method of the Email component. - $this->Email->reset(); - } - - - - // update the DB to set the alerted flag - $this->Event->set('alerted', 1); - $this->Event->save(); - - // redirect to the view event page - $this->Session->setFlash(__('Email sent to all participants.', true)); - $this->redirect(array('action' => 'view', $id)); - } - - - /** - * Send out an contact email to the person who posted the event. - * Users with a GPG key will get the mail encrypted, other users will get the mail unencrypted - * @todo allow the user to enter a comment in the contact email. - */ - function contact($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid id for event', true), 'default', array(), 'error'); - $this->redirect(array('action'=>'index')); - } - - // User has filled in his contact form, send out the email. - if (!empty($this->data)) { - $message = $this->data['Event']['message']; - if ($this->_sendContactEmail($id, $message)) { - // redirect to the view event page - $this->Session->setFlash(__('Email sent to the reporter.', true)); - } else { - $this->Session->setFlash(__('Invalid id for event', true), 'default', array(), 'error'); - } - $this->redirect(array('action' => 'view', $id)); - } - // User didn't see the contact form yet. Present it to him. - if (empty($this->data)) { - $this->data = $this->Event->read(null, $id); - } - } - - /** - * - * Sends out an email with the request to be contacted about a specific event. - * @todo move _sendContactEmail($id, $message) to a better place. (components?) - * - * @param unknown_type $id The id of the event for wich you want to contact the person. - * @param unknown_type $message The custom message that will be appended to the email. - * @return True if success, False if error - */ - function _sendContactEmail($id, $message) { - // fetch the event - $event = $this->Event->read(null, $id); - $reporter = $event['User']; // email, gpgkey - - $me_user = $this->Auth->user(); - $me_user = $me_user['User']; // email, gpgkey - - // The mail body, Sanitize::html() is NOT needed as we are sending plain-text mails. - $body = ""; - $body .="Hello, \n"; - $body .="\n"; - $body .="Someone wants to get in touch with you concerning a CyDefSIG event. \n"; - $body .="\n"; - $body .="You can reach him at ".$me_user['email']."\n"; - if (!empty($me_user['gpgkey'])) - $body .="His GPG/PGP key is added as attachment to this email. \n"; - $body .="\n"; - $body .="He wrote the following message: \n"; - $body .=$message."\n"; - $body .="\n"; - $body .="\n"; - $body .="The event is the following: \n"; - - // print the event in mail-format - // LATER place event-to-email-layout in a function - $appendlen = 20; - $body .= 'URL : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$event['Event']['id']."\n"; - $body .= 'Event : '.$event['Event']['id']."\n"; - $body .= 'Date : '.$event['Event']['date']."\n"; - if ('true' == Configure::read('CyDefSIG.showorg')) { - $body .= 'Reported by : '.$event['Event']['org']."\n"; - } - $body .= 'Risk : '.$event['Event']['risk']."\n"; - $relatedEvents = $this->Event->getRelatedEvents($id); - if (!empty($relatedEvents)) { - foreach ($relatedEvents as $relatedEvent){ - $body .= 'Related to : '.Configure::read('CyDefSIG.baseurl').'/events/view/'.$relatedEvent['Event']['id'].' ('.$relatedEvent['Event']['date'].')'."\n" ; - - } - } - $body .= 'Info : '."\n"; - $body .= $event['Event']['info']."\n"; - $body .= "\n"; - $body .= 'Signatures :'."\n"; - $body_temp_other = ""; - if (!empty($event['Signature'])) { - foreach ($event['Signature'] as $signature){ - $line = '- '.$signature['type'].str_repeat(' ', $appendlen - 2 - strlen( $signature['type'])).': '.$signature['value']."\n"; - if ('other' == $signature['type']) // append the 'other' signature types to the bottom. - $body_temp_other .= $line; - else $body .= $line; - } - } - $body .= "\n"; - $body .= $body_temp_other; // append the 'other' signature types to the bottom. - - // sign the body - require_once 'Crypt/GPG.php'; - $gpg = new Crypt_GPG(); - $gpg->addSignKey(Configure::read('GnuPG.email'), Configure::read('GnuPG.password')); - $body_signed = $gpg->sign($body, Crypt_GPG::SIGN_MODE_CLEAR); - - if (!empty($reporter['gpgkey'])) { - // import the key of the user into the keyring - // this isn't really necessary, but it gives it the fingerprint necessary for the next step - $key_import_output = $gpg->importKey($reporter['gpgkey']); - // say what key should be used to encrypt - $gpg = new Crypt_GPG(); - $gpg->addEncryptKey($key_import_output['fingerprint']); // use the key that was given in the import - - $body_enc_sig = $gpg->encrypt($body_signed, true); - } else { - $body_enc_sig = $body_signed; - // FIXME should I allow sending unencrypted "contact" mails to people if they didn't import they GPG key? - } - - // prepare the email - $this->Email->from = "CyDefSIG "; - $this->Email->to = "<".$reporter['email'].">"; - $this->Email->subject = "[CyDefSIG] Need info about event ".$id." - TLP Amber"; - //$this->Email->delivery = 'debug'; // do not really send out mails, only display it on the screen - $this->Email->template = 'body'; - $this->Email->sendAs = 'text'; // both text or html - $this->set('body', $body_enc_sig); - - // Add the GPG key of the user as attachment - // LATER sign the attached GPG key - if (!empty($me_user['gpgkey'])) { - // save the gpg key to a temporary file - $tmpfname = tempnam(TMP, "GPGkey"); - $handle = fopen($tmpfname, "w"); - fwrite($handle, $me_user['gpgkey']); - fclose($handle); - // attach it - $this->Email->attachments = array( - 'gpgkey.asc' => $tmpfname - ); - } - - // send it - $result = $this->Email->send(); - - // remove the temporary gpg file - if (!empty($me_user['gpgkey'])) - unlink($tmpfname); - - return $result; - } - - - function export() { - // Simply display a static view - - // generate the list of signature types - $this->loadModel('Signature'); - $this->set('sig_types', $this->Signature->validate['type']['allowedChoice']['rule'][1]); - - } - - - function xml($key) { - // check if the key is valid -> search for users based on key - $this->loadModel('User'); - // no input sanitization necessary, it's done by model - $user = $this->User->findByAuthkey($key); - if (empty($user)) - $this->cakeError('error403', array('message' => 'Incorrect authentication key')); - // display the full xml - $this->header('Content-Type: text/xml'); // set the content type - $this->layout = 'xml/xml'; - $this->header('Content-Disposition: attachment; filename="cydefsig.xml"'); - - $conditions = array("Event.alerted" => 1); - $fields = array('Event.id', 'Event.date', 'Event.risk', 'Event.info'); - if ('true' == Configure::read('CyDefSIG.showorg')) { - $fields[] = 'Event.org'; - } -// $this->Event->Behaviors->attach('Containable'); -// $contain = array('Signature.id', 'Signature.type', 'Signature.value', 'Signature.to_snort'); - $params = array('conditions' => $conditions, - 'recursive' => 1, - 'fields' => $fields, -// 'contain' => $contain - ); - $result = $this->Event->find('all', $params); - $this->set('events', $result); - - } - - /** - * - * Old legacy method/url - * @param string $key - * @deprecated - */ - function snort($key) { - $this->redirect(array('action' => 'nids', $key)); - } - - function nids($key) { - // check if the key is valid -> search for users based on key - $this->loadModel('User'); - // no input sanitization necessary, it's done by model - $user = $this->User->findByAuthkey($key); - if (empty($user)) - $this->cakeError('error403', array('message' => 'Incorrect authentication key')); - // display the full snort rulebase - $this->header('Content-Type: text/plain'); // set the content type - $this->header('Content-Disposition: attachment; filename="cydefsig.rules"'); - $this->layout = 'xml/xml'; // LATER better layout than xml - - $rules= array(); - - // find events that are finished - $events = $this->Event->findAllByAlerted(1); - $classtype = 'targeted-attack'; - - foreach ($events as $event) { - # proto src_ip src_port direction dst_ip dst_port msg rule_content tag sid rev - $rule_format_msg = 'msg: "CyDefSIG %s, Event '.$event['Event']['id'].', '.$event['Event']['risk'].'"'; - $rule_format_reference = 'reference:url,'.Configure::read('CyDefSIG.baseurl').'/events/view/'.$event['Event']['id']; - $rule_format = 'alert %s %s %s %s %s %s ('.$rule_format_msg.'; %s %s classtype:'.$classtype.'; sid:%d; rev:%d; '.$rule_format_reference.';) '; - - $sid = $user['User']['nids_sid']+($event['Event']['id']*100); // LATER this will cause issues with events containing more than 99 signatures - //debug($event); - foreach ($event['Signature'] as $signature) { - if (0 == $signature['to_ids']) continue; // signature is not to be exported to IDS. // LATER filter out to_ids=0 in the query - - $sid++; - switch ($signature['type']) { - // LATER test all the snort signatures - // LATER add the tag keyword in the rules to capture network traffic - // LATER sanitize every $signature['value'] to not conflict with snort - case 'ip-dst': - $rules[] = sprintf($rule_format, - 'ip', // proto - '$HOME_NET', // src_ip - 'any', // src_port - '->', // direction - $signature['value'], // dst_ip - 'any', // dst_port - 'Outgoing To Bad IP', // msg - '', // rule_content - '', // tag - $sid, // sid - 1 // rev - ); - break; - case 'ip-src': - $rules[] = sprintf($rule_format, - 'ip', // proto - $signature['value'], // src_ip - 'any', // src_port - '->', // direction - '$HOME_NET', // dst_ip - 'any', // dst_port - 'Incoming From Bad IP', // msg - '', // rule_content - '', // tag - $sid, // sid - 1 // rev - ); - break; - case 'email-src': - $rules[] = sprintf($rule_format, - 'tcp', // proto - '$EXTERNAL_NET', // src_ip - 'any', // src_port - '<>', // direction - '$SMTP_SERVERS', // dst_ip - '25', // dst_port - 'Bad Source Email Address', // msg - 'flow:established,to_server; content:"MAIL FROM|3a|"; nocase; content:"'.$signature['value'].'"; nocase;', // rule_content - 'tag:session,600,seconds;', // tag - $sid, // sid - 1 // rev - ); - break; - case 'email-dst': - $rules[] = sprintf($rule_format, - 'tcp', // proto - '$EXTERNAL_NET', // src_ip - 'any', // src_port - '<>', // direction - '$SMTP_SERVERS', // dst_ip - '25', // dst_port - 'Bad Destination Email Address',// msg - 'flow:established,to_server; content:"RCPT TO|3a|"; nocase; content:"'.$signature['value'].'"; nocase;', // rule_content - 'tag:session,600,seconds;', // tag - $sid, // sid - 1 // rev - ); - break; - case 'email-subject': - // LATER email-subject rule might not match because of line-wrapping - $rules[] = sprintf($rule_format, - 'tcp', // proto - '$EXTERNAL_NET', // src_ip - 'any', // src_port - '<>', // direction - '$SMTP_SERVERS', // dst_ip - '25', // dst_port - 'Bad Email Subject', // msg - 'flow:established,to_server; content:"Subject|3a|"; nocase; content:"'.$signature['value'].'"; nocase;', // rule_content - 'tag:session,600,seconds;', // tag - $sid, // sid - 1 // rev - ); - break; - case 'email-attachment': - // LATER email-attachment rule might not match because of line-wrapping - $rules[] = sprintf($rule_format, - 'tcp', // proto - '$EXTERNAL_NET', // src_ip - 'any', // src_port - '<>', // direction - '$SMTP_SERVERS', // dst_ip - '25', // dst_port - 'Bad Email Attachment', // msg - 'flow:established,to_server; content:"Content-Disposition: attachment|3b| filename=|22|"; content:"'.$signature['value'].'|22|";', // rule_content // LATER test and finetune this snort rule https://secure.wikimedia.org/wikipedia/en/wiki/MIME#Content-Disposition - 'tag:session,600,seconds;', // tag - $sid, // sid - 1 // rev - ); - break; - case 'domain': - $rules[] = sprintf($rule_format, - 'udp', // proto - 'any', // src_ip - 'any', // src_port - '->', // direction - 'any', // dst_ip - '53', // dst_port - 'Lookup Of Bad Domain', // msg - 'content:"'.$this->_dnsNameToRawFormat($signature['value']).'"; nocase;', // rule_content - '', // tag - $sid, // sid - 1 // rev - ); - $sid++; - $rules[] = sprintf($rule_format, - 'tcp', // proto - 'any', // src_ip - 'any', // src_port - '->', // direction - 'any', // dst_ip - '53', // dst_port - 'Lookup Of Bad Domain', // msg - 'content:"'.$this->_dnsNameToRawFormat($signature['value']).'"; nocase;', // rule_content - '', // tag - $sid, // sid - 1 // rev - ); - $sid++; - //break; // domain should also detect the domain name in a url - case 'url': - $rules[] = sprintf($rule_format, - 'tcp', // proto - '$HOME_NET', // src_ip - 'any', // src_port - '->', // direction - '$EXTERNAL_NET', // dst_ip - '$HTTP_PORTS', // dst_port - 'Outgoing Bad HTTP URL', // msg - 'flow:to_server,established; uricontent:"'.$signature['value'].'"; nocase;', // rule_content - 'tag:session,600,seconds;', // tag - $sid, // sid - 1 // rev - ); - break; - case 'user-agent': - $rules[] = ""; - // TODO write snort user-agent rule - break; - case 'snort': - $tmp_rule = $signature['value']; - - // rebuild the rule by overwriting the different keywords using preg_replace() - // sid - '/sid\s*:\s*[0-9]+\s*;/' - // rev - '/rev\s*:\s*[0-9]+\s*;/' - // classtype - '/classtype:[a-zA-Z_-]+;/' - // msg - '/msg\s*:\s*".*"\s*;/' - // reference - '/reference\s*:\s*.+;/' - $replace_count=array(); - $tmp_rule = preg_replace('/sid\s*:\s*[0-9]+\s*;/', 'sid:'.$sid.';', $tmp_rule, -1, $replace_count['sid']); - if (null == $tmp_rule ) break; // don't output the rule on error with the regex - $tmp_rule = preg_replace('/rev\s*:\s*[0-9]+\s*;/', 'rev:1;', $tmp_rule, -1, $replace_count['rev']); - if (null == $tmp_rule ) break; // don't output the rule on error with the regex - $tmp_rule = preg_replace('/classtype:[a-zA-Z_-]+;/', 'classtype:'.$classtype.';', $tmp_rule, -1, $replace_count['classtype']); - if (null == $tmp_rule ) break; // don't output the rule on error with the regex - $tmp_message = sprintf($rule_format_msg, 'snort-rule'); - $tmp_rule = preg_replace('/msg\s*:\s*".*"\s*;/', $tmp_message.';', $tmp_rule, -1, $replace_count['msg']); - if (null == $tmp_rule ) break; // don't output the rule on error with the regex - $tmp_rule = preg_replace('/reference\s*:\s*.+;/', $rule_format_reference.';', $tmp_rule, -1, $replace_count['reference']); - if (null == $tmp_rule ) break; // don't output the rule on error with the regex - - // some values were not replaced, so we need to add them ourselves, and insert them in the rule - $extra_for_rule=""; - if (0 == $replace_count['sid']) { - $extra_for_rule .= 'sid:'.$sid.';'; - } if (0 == $replace_count['rev']) { - $extra_for_rule .= 'rev:1;'; - } if (0 == $replace_count['classtype']) { - $extra_for_rule .= 'classtype:'.$classtype.';'; - } if (0 == $replace_count['msg']) { - $extra_for_rule .= $tmp_message.';'; - } if (0 == $replace_count['reference']) { - $extra_for_rule .= $rule_format_reference.';'; - } - $tmp_rule = preg_replace('/;\s*\)/', '; '.$extra_for_rule.')', $tmp_rule); - - // finally the rule is cleaned up and can be outputed - $rules[] = $tmp_rule; - - // TODO test using lots of snort rules. - default: - break; - } - - } - - } - print ("#

This part is not finished and might be buggy. Please report any issues.

\n"); - - print "#
 \n";
-        foreach ($rules as $rule)
-        print $rule."\n";
-        print "#
\n"; - - $this->set('rules', $rules); - - } - - function text($key, $type="") { - // check if the key is valid -> search for users based on key - $this->loadModel('User'); - // no input sanitization necessary, it's done by model - $user = $this->User->findByAuthkey($key); - if (empty($user)) - $this->cakeError('error403', array('message' => 'Incorrect authentication key')); - - $this->header('Content-Type: text/plain'); // set the content type -// $this->header('Content-Disposition: attachment; filename="cydefsig.rules"'); - $this->layout = 'xml/xml'; // LATER better layout than xml - - $this->loadModel('Signature'); - $params = array( - 'conditions' => array('Signature.type' => $type), //array of conditions - 'recursive' => 0, //int - 'fields' => array('Signature.value'), //array of field names - 'order' => array('Signature.value'), //string or array defining order - 'group' => array('Signature.value'), //fields to GROUP BY - ); - $signatures = $this->Signature->find('all', $params); - - $this->set('signatures', $signatures); - } - - - /** - * // TODO move _dnsNameToRawFormat($name) function to a better place - * Converts a DNS name to a raw format usable in NIDS like Snort. - * example: foobar.com becomes |06|foobar|03|com|00| - * @param string $name dns name to be converted - * @return string raw snort compatible format of the dns name - */ - function _dnsNameToRawFormat($name) { - $rawName = ""; - // explode using the dot - $explodedNames = explode('.', $name); - // for each part - foreach ($explodedNames as $explodedName) { - // count the lenght of the part, and add |length| before - $length = strlen($explodedName); - if ($length > 255) exit('ERROR: dns name is to long for RFC'); // LATER log correctly without dying - $hexLength = dechex($length); - if (1 == strlen($hexLength)) $hexLength = '0'.$hexLength; - $rawName .= '|'.$hexLength.'|'.$explodedName; - } - // put all together - $rawName .= '|00|'; - // and append |00| to terminate the name - return $rawName; - } - - -} diff --git a/app/controllers/groups_controller.php b/app/controllers/groups_controller.php deleted file mode 100755 index d2a02c9e4..000000000 --- a/app/controllers/groups_controller.php +++ /dev/null @@ -1,75 +0,0 @@ -Security->requireAuth('add', 'edit'); - - if (! $this->isAdmin() ) { - $this->Session->setFlash(__('You are not authorized to access this location.', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'index')); - } - } - - function index() { - $this->Group->recursive = 0; - $this->set('groups', $this->paginate()); - } - - function view($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid group', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - $this->set('group', $this->Group->read(null, $id)); - } - - function add() { - if (!empty($this->data)) { - $this->Group->create(); - if ($this->Group->save($this->data)) { - $this->Session->setFlash(__('The group has been saved', true)); - $this->redirect(array('action' => 'index')); - } else { - $this->Session->setFlash(__('The group could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - } - } - - function edit($id = null) { - if (!$id && empty($this->data)) { - $this->Session->setFlash(__('Invalid group', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - if (!empty($this->data)) { - if ($this->Group->save($this->data)) { - $this->Session->setFlash(__('The group has been saved', true)); - $this->redirect(array('action' => 'index')); - } else { - $this->Session->setFlash(__('The group could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - } - if (empty($this->data)) { - $this->data = $this->Group->read(null, $id); - } - } - - function delete($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid id for group', true), 'default', array(), 'error'); - $this->redirect(array('action'=>'index')); - } - if ($this->Group->delete($id)) { - $this->Session->setFlash(__('Group deleted', true)); - $this->redirect(array('action'=>'index')); - } - $this->Session->setFlash(__('Group was not deleted', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - -} diff --git a/app/controllers/signatures_controller.php b/app/controllers/signatures_controller.php deleted file mode 100755 index 0dd062871..000000000 --- a/app/controllers/signatures_controller.php +++ /dev/null @@ -1,208 +0,0 @@ -Security->requireAuth('add', 'edit'); - - // These variables are required for every view - $me_user = $this->Auth->user(); - $this->set('me', $me_user['User']); - $this->set('isAdmin', $this->isAdmin()); - } - - function index() { - $this->Signature->recursive = 0; - $this->set('signatures', $this->paginate()); - } - - function view($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid signature', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - $this->set('signature', $this->Signature->read(null, $id)); - } - - function add($event_id = null) { - - if (!$event_id && empty($this->data)) { - $this->Session->setFlash(__('Invalid id for event', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action'=>'index')); - } - if ($event_id || !empty($this->data)) { - // only add signatures from events of yourself - $user = $this->Auth->user(); - if (!empty($this->data)) - $old_signature = $this->Signature->Event->read(null, $this->data['Signature']['event_id']); - else - $old_signature = $this->Signature->Event->read(null, $event_id); - if (!$this->isAdmin() && $user['User']['org'] != $old_signature['Event']['org']) { - $this->Session->setFlash(__('You can only add signatures for your own organisation.', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $old_signature['Event']['id'])); - } - - } - - if (!empty($this->data)) { - // remove the alerted flag from the event - $this->loadModel('Event'); - $event = $this->Event->read(null, $this->data['Signature']['event_id']); - $event['Event']['alerted'] = 0; - $this->Event->save($event); - - // - // multiple signatures in batch import - // - if ($this->data['Signature']['batch_import'] == 1) { - // make array from value field - $signatures = explode("\n", $this->data['Signature']['value']); - - $fails = ""; // will be used to keep a list of the lines that failed or succeeded - $successes = ""; - foreach ($signatures as $key => $signature) { - $signature = trim($signature); - if (strlen($signature) == 0 ) - continue; // don't do anything for empty lines - - $this->Signature->create(); - $this->data['Signature']['value'] = $signature; // set the value as the content of the single line - - if ($this->Signature->save($this->data)) { - $successes .= " ".($key+1); - } else { - $fails .= " ".($key+1); - } - - } - // we added all the signatures, - if ($fails) { // list the ones that failed - $this->Session->setFlash(__('The lines'.$fails.' could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - if ($successes) { // list the ones that succeeded - $this->Session->setFlash(__('The lines'.$successes.' have been saved', true)); - } - - $this->redirect(array('controller' => 'events', 'action' => 'view', $this->data['Signature']['event_id'])); - } - - else { - // - // single signature - // - // create the signature - $this->Signature->create(); - if ($this->Signature->save($this->data)) { - // inform the user and redirect - $this->Session->setFlash(__('The signature has been saved', true)); - $this->redirect(array('controller' => 'events', 'action' => 'view', $this->data['Signature']['event_id'])); - } else { - $this->Session->setFlash(__('The signature could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - } - } - if (empty($this->data)) { - $this->data['Signature']['event_id'] = $event_id; - } - - // combobox for types - $types = $this->Signature->validate['type']['allowedChoice']['rule'][1]; - $types = $this->_arrayToValuesIndexArray($types); - $this->set('types',compact('types')); - } - - function edit($id = null) { - if (!$id && empty($this->data)) { - $this->Session->setFlash(__('Invalid signature', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'index')); - } - // only edit own signatures (from events of yourself) - $user = $this->Auth->user(); - $old_signature = $this->Signature->read(null, $id); - if (!$this->isAdmin() && $user['User']['org'] != $old_signature['Event']['org']) { - $this->Session->setFlash(__('You can only edit signatures from your own organisation.', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $old_signature['Event']['id'])); - } - - // form submit - if (!empty($this->data)) { - // block naughty stuff where the id or event_id are changed in the form - if ($this->data['Signature']['id'] != $id || - $this->data['Signature']['event_id'] != $old_signature['Signature']['event_id']) { - $this->Session->setFlash(__('You can only edit signatures from your own organisation.', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $old_signature['Event']['id'])); - } - - // data is valid, let's save the update - if ($this->Signature->save($this->data)) { - // remove the alerted flag from the event - $this->loadModel('Event'); - $event = $this->Event->read(null, $this->data['Signature']['event_id']); - $event['Event']['alerted'] = 0; - $this->Event->save($event); - // inform the user and redirect - $this->Session->setFlash(__('The signature has been saved', true)); - $this->redirect(array('controller' => 'events', 'action' => 'view', $this->data['Signature']['event_id'])); - } else { - $this->Session->setFlash(__('The signature could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - } - if (empty($this->data)) { - $this->data = $this->Signature->read(null, $id); - } - - $events = $this->Signature->Event->find('list'); - $this->set(compact('events')); - - // combobox for types - $types = $this->Signature->validate['type']['allowedChoice']['rule'][1]; - $types = $this->_arrayToValuesIndexArray($types); - $this->set('types',compact('types')); - } - - function delete($id = null) { - if (!$id) { - $this->Session->setFlash(__('Invalid id for signature', true), 'default', array(), 'error'); - $this->redirect(array('action'=>'index')); - } - // only delete own signatures (from events of yourself) - $user = $this->Auth->user(); - $old_signature = $this->Signature->read(null, $id); - if (!$this->isAdmin() && $user['User']['org'] != $old_signature['Event']['org']) { - $this->Session->setFlash(__('You can only delete signatures from your own organisation.', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events', 'action' => 'view', $old_signature['Event']['id'])); - } - // delete the signature - if ($this->Signature->delete($id)) { - $this->Session->setFlash(__('Signature deleted', true)); - $this->redirect(array('controller' => 'events', 'action' => 'view', $old_signature['Event']['id'])); - } - $this->Session->setFlash(__('Signature was not deleted', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - - function search($keyword = null) { - if (!$keyword && !isset($this->data['Signature']['keyword'])) { - // no search keyword is given, show the search form - } else { - if (!$keyword) $keyword = $this->data['Signature']['keyword']; - - // search the db - $this->Signature->recursive = 0; - $this->paginate = array( - 'conditions' => array('Signature.value LIKE' => '%'.$keyword.'%'), - ); - $this->set('signatures', $this->paginate()); - - // set the same view as the index page - $this->action = 'index'; - - } - } -} diff --git a/app/controllers/users_controller.php b/app/controllers/users_controller.php deleted file mode 100755 index 72ffc51e6..000000000 --- a/app/controllers/users_controller.php +++ /dev/null @@ -1,334 +0,0 @@ -data['User']['password'] = ""; // empty out the password - - function beforeFilter() { - parent::beforeFilter(); - - // what pages are allowed for everyone - $this->Auth->allow('login', 'logout'); - - // Prevent XSRF -// $this->Security->requireAuth('add', 'edit'); - - // These variables are required for every view - $me_user = $this->Auth->user(); - $this->set('me', $me_user['User']); - $this->set('isAdmin', $this->isAdmin()); - } - - - function index() { - if (!$this->isAdmin()) { - $this->Session->setFlash(__('Not authorized to list users', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events' , 'action' => 'index')); - } - - $this->User->recursive = 0; - $this->set('users', $this->paginate()); - } - - function view($id = null) { - $me_user = $this->Auth->user(); - if (!$id) { - $this->Session->setFlash(__('Invalid user', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - if ('me' == $id ) $id = $me_user['User']['id']; - - // only allow access to own profile, except for admins - if (!$this->isAdmin() && $id != $me_user['User']['id']) { - $this->Session->setFlash(__('Not authorized to view this user', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events' , 'action' => 'index')); - } - - $user = $this->User->read(null, $id); - - if (empty($me_user['User']['gpgkey'])) { - $this->Session->setFlash(__('No GPG key set in your profile. To receive emails, submit your public key in your profile.', true), 'default', array(), 'gpg'); - } - - $this->set('user', $user); - } - - function add() { - if (!$this->isAdmin()) { - $this->Session->setFlash(__('Not authorized to create new users', true), 'default', array(), 'error'); - $this->redirect(array('controller' => 'events' , 'action' => 'index')); - } - - if (!empty($this->data)) { - if ($this->data['User']['password'] == '1deba050eee85e4ea7447edc6c289e4f55b81d45' ) { - // FIXME bug of auth ??? when passwd is empty it adds this hash - $this->data['User']['password'] = ''; - } - if (empty($this->data['User']['authkey'])) $this->data['User']['authkey'] = $this->User->generateAuthKey(); - $this->User->create(); - - if ($this->User->save($this->data)) { - // LATER send out email to user to inform of new user - // LATER send out email to admins to inform of new user - $this->Session->setFlash(__('The user has been saved', true)); - $this->redirect(array('action' => 'index')); - } else { - $this->Session->setFlash(__('The user could not be saved. Please, try again.', true), 'default', array(), 'error'); - return; - } - } - $groups = $this->User->Group->find('list'); - $this->set(compact('groups')); - } - - function edit($id = null) { - $me_user = $this->Auth->user(); - - if (!$id && empty($this->data)) { - $this->Session->setFlash(__('Invalid user', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - if ('me' == $id ) $id = $me_user['User']['id']; - - // only allow access to own profile, except for admins - if (!$this->isAdmin() && $id != $me_user['User']['id']) { - $this->Session->setFlash(__('Not authorized to edit this user', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - if (!empty($this->data)) { - $this->User->read(null, $id); - - if ("" != $this->data['User']['password'] && $this->data['User']['password'] != Security::hash('', null, true)) // workaround because password is automagically hashed - $this->User->set('password', $this->data['User']['password']); - $this->User->set('email', $this->data['User']['email']); - $this->User->set('autoalert', $this->data['User']['autoalert']); - $this->User->set('gpgkey', $this->data['User']['gpgkey']); - $this->User->set('nids_sid', $this->data['User']['nids_sid']); - - // administrative actions - if ($this->isAdmin()) { - $this->User->set('group_id', $this->data['User']['group_id']); - $this->User->set('org', $this->data['User']['org']); - } - - if ($this->User->save()) { - $this->Session->setFlash(__('The user has been saved', true)); - $this->Session->write('Auth', $this->User->read(null, $me_user['User']['id'])); // refresh auth info - $this->redirect(array('action' => 'view', $id)); - } else { - $this->Session->setFlash(__('The user could not be saved. Please, try again.', true), 'default', array(), 'error'); - } - - } - if (empty($this->data)) { - $this->data = $this->User->read(null, $id); - } - $this->data['User']['password'] = ""; // empty out the password - $groups = $this->User->Group->find('list'); - $this->set(compact('groups')); - } - - function delete($id = null) { - $me_user = $this->Auth->user(); - if (!$id) { - $this->Session->setFlash(__('Invalid id for user', true), 'default', array(), 'error'); - $this->redirect(array('action'=>'index')); - } - if ('me' == $id ) $id = $me_user['User']['id']; - - // only allow delete own account, except for admins - if (!$this->isAdmin() && $id != $me_user['User']['id']) { - $this->Session->setFlash(__('Not authorized to delete this user', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - if ($this->User->delete($id)) { - $this->Session->setFlash(__('User deleted', true)); - if (!$this->isAdmin()) { - // user deletes himself, force logout - $this->redirect(array('action'=>'logout')); - } - - } else { - $this->Session->setFlash(__('User was not deleted', true), 'default', array(), 'error'); - } - $this->redirect(array('action' => 'index')); - } - - - - function login() { -// if (!empty($this->data)) { -// // FIXME get the captcha to work -// if ($this->Recaptcha->verify()) { -// // do something, save you data, login, whatever -// -// } else { -// // display the raw API error -// $this->Session->setFlash($this->Recaptcha->error); -// $this->redirect($this->Auth->logout()); -// } -// } - - // if user is already logged in - if ($this->Session->read('Auth.User')) { - $this->Session->setFlash('You are already logged in!'); - $this->redirect('/', null, false); - } - - } - - - function routeafterlogin() { - $me_user = $this->Auth->user(); - - // Terms and Conditions Page - if (0 == $me_user['User']['termsaccepted']) { - $this->redirect(array('action' => 'terms')); - } - - // News page - $new_newsdate = new DateTime("2012-03-12"); - $newsdate = new DateTime($me_user['User']['newsread']); - if ($new_newsdate > $newsdate) { - $this->redirect(array('action' => 'news')); - } - - // Events list - $this->redirect(array('controller' => 'events', 'action' => 'index')); - } - - function logout() { - $this->Session->setFlash('Good-Bye'); - $this->redirect($this->Auth->logout()); - } - - function resetauthkey($id = null) { - $me_user = $this->Auth->user(); - if (!$id) { - $this->Session->setFlash(__('Invalid id for user', true), 'default', array(), 'error'); - $this->redirect(array('action'=>'index')); - } - if ('me' == $id ) $id = $me_user['User']['id']; - - // only allow reset key for own account, except for admins - if (!$this->isAdmin() && $id != $me_user['User']['id']) { - $this->Session->setFlash(__('Not authorized to reset the key for this user', true), 'default', array(), 'error'); - $this->redirect(array('action' => 'index')); - } - - - $data = array( - 'User' => array( - 'id' => $id, - 'authkey' => $this->User->generateAuthKey() - ) - ); - if ($this->User->save( $data, false, array('authkey') )) { - $this->Session->setFlash(__('New authkey generated.', true)); - $this->Session->write('Auth', $this->User->read(null, $me_user['User']['id'])); // refresh auth info - } else { - $this->Session->setFlash(__('Auth key could not be changed. Please, try again.', true), 'default', array(), 'error'); - } - - $this->redirect($this->referer()); - - } - - - function memberslist() { - $this->loadModel('Signature'); - $this->loadModel('Event'); - - // Orglist - $fields = array('User.org', 'count(User.id) as `num_members`'); - $params = array('recursive' => 0, - 'fields' => $fields, - 'group' => array('User.org'), - 'order' => array('User.org'), - ); - $orgs = $this->User->find('all', $params); - $this->set('orgs', $orgs); - -// $fields = array('User.org', 'count(User.id) as `num_members`', 'count(Event.id) as `num_events`'); -// $params = array('recursive' => 0, -// 'fields' => $fields, -// 'group' => array('User.org'), -// 'order' => array('User.org'), -// ); -// $orgs = $this->Event->find('all', $params); -// $this->set('orgs', $orgs); - - - - - // What org posted what type of signature - // LATER beautify types_histogram - $this->loadModel('Signature'); - $fields = array('Event.org', 'Signature.type', 'count(Signature.type) as `num_types`'); - $params = array('recursive' => 0, - 'fields' => $fields, - 'group' => array('Signature.type', 'Event.org'), - 'order' => array('Event.org', 'num_types DESC'), - ); - $types_histogram = $this->Signature->find('all', $params); - $this->set('types_histogram', $types_histogram); - - - } - - - - - function terms() { - $me_user = $this->Auth->user(); - - if (!empty($this->data)) { - $user = $this->User->read(null, $me_user['User']['id']); - $user['User']['termsaccepted'] = 1; - $this->User->save($user); - $this->Session->write('Auth', $user); // refresh auth info - - $this->redirect(array('action' => 'routeafterlogin')); - } - - $this->set('termsaccepted', $me_user['User']['termsaccepted']); - - } - - function news() { - $me_user = $this->Auth->user(); - - $user = $this->User->read(null, $me_user['User']['id']); - $user['User']['newsread'] = date("Y-m-d"); - $this->User->save($user); - $this->Session->write('Auth', $user); // refresh auth info - - - } - - - - function initDB() { - $group =& $this->User->Group; - //Allow admins to everything - $group->id = 1; - $this->Acl->allow($group, 'controllers'); - - //allow managers to posts and widgets - $group->id = 2; - $this->Acl->deny($group, 'controllers'); - $this->Acl->allow($group, 'controllers/Events'); - $this->Acl->allow($group, 'controllers/Signatures'); - $this->Acl->allow($group, 'controllers/Users'); - - //we add an exit to avoid an ugly "missing views" error message - echo "all done"; - exit; - } - - - -} diff --git a/app/index.php b/app/index.php old mode 100755 new mode 100644 index 1d5f4bb39..bf40cb72b --- a/app/index.php +++ b/app/index.php @@ -1,6 +1,6 @@ array( - 'notempty' => array( - 'rule' => array('notempty'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'date' => array( - 'date' => array( - 'rule' => array('date'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'user_id' => array( - 'numeric' => array( - 'rule' => array('numeric'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'risk' => array( - 'allowedChoice' => array( - 'rule' => array('inList', array('Undefined', 'Low','Medium','High')), - 'message' => 'Options : Undefined, Low, Medium, High' - ), - ), - 'alerted' => array( - 'boolean' => array( - 'rule' =>array('boolean'), - ), - ), - ); - //The Associations below have been created with all possible keys, those that are not needed can be removed - - var $belongsTo = array( - 'User' => array( - 'className' => 'User', - 'foreignKey' => 'user_id', - 'conditions' => '', - 'fields' => '', - 'order' => '' - ) - ); - - var $hasMany = array( - 'Signature' => array( - 'className' => 'Signature', - 'foreignKey' => 'event_id', - 'dependent' => true, // cascade deletes - 'conditions' => '', - 'fields' => '', - 'order' => 'Signature.type ASC', - 'limit' => '', - 'offset' => '', - 'exclusive' => '', - 'finderQuery' => '', - 'counterQuery' => '' - ) - ); - - - - function getRelatedEvents() { - // first get a list of related event_ids - // then do a single query to search for all the events with that id - $relatedEventIds = Array(); - foreach ($this->data['Signature'] as $signature ) { - if ($signature['type'] == 'other') - continue; // sigs of type 'other' should not be matched against the others - $conditions = array('Signature.value =' => $signature['value'], 'Signature.type =' => $signature['type']); - $similar_signatures = $this->Signature->find('all',array('conditions' => $conditions)); - foreach ($similar_signatures as $similar_signature) { - if ($this->id == $similar_signature['Signature']['event_id']) - continue; // same as this event, not needed in the list - $relatedEventIds[] = $similar_signature['Signature']['event_id']; - } - } - $conditions = array("Event.id" => $relatedEventIds); - $relatedEvents= $this->find('all', - array('conditions' => $conditions, - 'recursive' => 0, - 'order' => 'Event.date DESC', - 'fields' => 'Event.*' - ) - ); - return $relatedEvents; - } -} diff --git a/app/models/group.php b/app/models/group.php deleted file mode 100755 index 9d0655bec..000000000 --- a/app/models/group.php +++ /dev/null @@ -1,38 +0,0 @@ - array( - 'notempty' => array( - 'rule' => array('notempty'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - ); - //The Associations below have been created with all possible keys, those that are not needed can be removed - - var $hasMany = array( - 'User' => array( - 'className' => 'User', - 'foreignKey' => 'group_id', - 'dependent' => false, - 'conditions' => '', - 'fields' => '', - 'order' => '', - 'limit' => '', - 'offset' => '', - 'exclusive' => '', - 'finderQuery' => '', - 'counterQuery' => '' - ) - ); - var $actsAs = array('Acl' => array('type' => 'requester')); - function parentNode() { - return null; - } - -} diff --git a/app/models/signature.php b/app/models/signature.php deleted file mode 100755 index 4ecac3a77..000000000 --- a/app/models/signature.php +++ /dev/null @@ -1,210 +0,0 @@ - "DESC", "Signature.type" => "ASC"); - var $validate = array( - 'event_id' => array( - 'numeric' => array( - 'rule' => array('numeric'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - 'required' => true, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'type' => array( - 'allowedChoice' => array( - 'rule' => array('inList', array('md5','sha1', - 'filename', - 'ip-src', - 'ip-dst', - 'domain', - 'email-src', - 'email-dst', - 'email-subject', - 'email-attachment', - 'url', - 'user-agent', - 'regkey', - 'AS', - 'snort', - 'pattern-in-file', - 'other')), - 'message' => 'Options : md5, sha1, filename, ip, domain, email, url, regkey, AS, other, ...' - ), - ), - 'value' => array( - 'notempty' => array( - 'rule' => array('notempty'), - 'message' => 'Please fill in this field', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - 'rightformat' => array( - 'rule' => array('validateSignatureValue'), - 'message' => 'Value not in the right type/format. Please double check the value or select "other" for a type.' - ), - ), - 'to_ids' => array( - 'boolean' => array( - 'rule' => array('boolean'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - 'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - ); - //The Associations below have been created with all possible keys, those that are not needed can be removed - - var $belongsTo = array( - 'Event' => array( - 'className' => 'Event', - 'foreignKey' => 'event_id', - 'conditions' => '', - 'fields' => '', - 'order' => '' - ) - ); - - - function validateSignatureValue ($fields) { - $value = $fields['value']; - $event_id = $this->data['Signature']['event_id']; - $type = $this->data['Signature']['type']; - - // check if the signature already exists in the same event - $params = array('recursive' => 0, - 'conditions' => array('Signature.event_id' => $event_id, - 'Signature.type' => $type, - 'Signature.value' => $value), - ); - if (0 != $this->find('count', $params) ) - return 'Signature already exists for this event.'; - - - // check data validation - switch($this->data['Signature']['type']) { - case 'md5': - if (preg_match("#^[0-9a-f]{32}$#i", $value)) - return true; - return 'Checksum has invalid lenght or format. Please double check the value or select "other" for a type.'; - break; - case 'sha1': - if (preg_match("#^[0-9a-f]{40}$#i", $value)) - return true; - return 'Checksum has invalid lenght or format. Please double check the value or select "other" for a type.'; - break; - case 'filename': - // no newline - if (!preg_match("#\n#", $value)) - return true; - break; - case 'ip-src': - $parts = explode("/", $value); - // [0] = the ip - // [1] = the network address - if (count($parts) <= 2 ) { - // ipv4 and ipv6 matching - if (filter_var($parts[0],FILTER_VALIDATE_IP)) { - // ip is validated, now check if we have a valid network mask - if (empty($parts[1])) - return true; - else if(is_numeric($parts[1]) && $parts[1] < 129) - return true; - } - } - return 'IP address has invalid format. Please double check the value or select "other" for a type.'; - break; - case 'ip-dst': - $parts = explode("/", $value); - // [0] = the ip - // [1] = the network address - if (count($parts) <= 2 ) { - // ipv4 and ipv6 matching - if (filter_var($parts[0],FILTER_VALIDATE_IP)) { - // ip is validated, now check if we have a valid network mask - if (empty($parts[1])) - return true; - else if(is_numeric($parts[1]) && $parts[1] < 129) - return true; - } - } - return 'IP address has invalid format. Please double check the value or select "other" for a type.'; - break; - case 'domain': - if(preg_match("#^[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $value)) - return true; - return 'Domain name has invalid format. Please double check the value or select "other" for a type.'; - break; - case 'email-src': - // we don't use the native function to prevent issues with partial email addresses - if(preg_match("#^[A-Z0-9._%+-]*@[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $value)) - return true; - return 'Email address has invalid format. Please double check the value or select "other" for a type.'; - break; - case 'email-dst': - // we don't use the native function to prevent issues with partial email addresses - if(preg_match("#^[A-Z0-9._%+-]*@[A-Z0-9.-]+\.[A-Z]{2,4}$#i", $value)) - return true; - return 'Email address has invalid format. Please double check the value or select "other" for a type.'; - break; - case 'email-subject': - // no newline - if (!preg_match("#\n#", $value)) - return true; - break; - case 'email-attachment': - // no newline - if (!preg_match("#\n#", $value)) - return true; - break; - case 'url': - // no newline - if (!preg_match("#\n#", $value)) - return true; - break; - case 'user-agent': - // no newline - if (!preg_match("#\n#", $value)) - return true; - break; - case 'regkey': - // no newline - if (!preg_match("#\n#", $value)) - return true; - break; - case 'snort': - // no validation yet. TODO implement data validation on snort signature type - case 'other': - return true; - break; - } - - // default action is to return false - return true; - - } - - - - function getRelatedSignatures($signature) { - // LATER getRelatedSignatures($signature) this might become a performance bottleneck - $conditions = array('Signature.value =' => $signature['value'], - 'Signature.id !=' => $signature['id'], - 'Signature.type =' => $signature['type'], ); -// $fields = array('Event.*'); - $fields = array('Signature.*'); - - $similar_events = $this->find('all',array('conditions' => $conditions, - 'fields' => $fields, - 'order' => 'Signature.event_id DESC', ) - ); - return $similar_events; - } - -} diff --git a/app/models/user.php b/app/models/user.php deleted file mode 100755 index 478070955..000000000 --- a/app/models/user.php +++ /dev/null @@ -1,222 +0,0 @@ - array( - 'numeric' => array( - 'rule' => array('numeric'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'password' => array( - 'notempty' => array( - 'rule' => array('notempty'), - 'message' => 'A password is required', // LATER password strength requirements - //'allowEmpty' => false, - 'required' => true, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), -// 'complex' => array( -// 'rule' => array('complexPassword'), -// 'message' => 'Password must be 8 characters minimum and contain at least one number and one uppercase character' -// ), - - ), - 'org' => array( - 'notempty' => array( - 'rule' => array('notempty'), - 'message' => 'Please specify the organisation where you are working.', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'email' => array( - 'email' => array( - 'rule' => array('email'), - 'message' => 'Please enter a valid email address.', - //'allowEmpty' => false, - 'required' => true, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - 'unique' => array( - 'rule' => 'isUnique', - 'message' => 'An account with this email address already exists.' - ), - ), - 'autoalert' => array( - 'boolean' => array( - 'rule' => array('boolean'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - 'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'authkey' => array( - 'notempty' => array( - 'rule' => array('notempty'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - 'gpgkey' => array( - 'rule' => array('validateGpgkey'), - 'message' => 'GPG key not valid, please enter a valid key' - ), - 'nids_sid' => array ( - 'numeric' => array( - 'rule' => array('numeric'), - 'message' => 'A SID should be an integer.', - 'allowEmpty' => false, - 'required' => true, - ), - ), - - ); - //The Associations below have been created with all possible keys, those that are not needed can be removed - - var $belongsTo = array( - 'Group' => array( - 'className' => 'Group', - 'foreignKey' => 'group_id', - 'conditions' => '', - 'fields' => '', - 'order' => '' - ) - ); - - var $hasMany = array( - 'Event' => array( - 'className' => 'Event', - 'foreignKey' => 'user_id', - 'dependent' => false, // do not delete Events when user is deleted - 'conditions' => '', - 'fields' => '', - 'order' => '', - 'limit' => '', - 'offset' => '', - 'exclusive' => '', - 'finderQuery' => '', - 'counterQuery' => '' - ), - 'User' => array( - 'className' => 'User', - 'foreignKey' => 'invited_by', - 'dependent' => false, // do not delete Users when user is deleted - 'conditions' => '', - 'fields' => '', - 'order' => '', - 'limit' => '', - 'offset' => '', - 'exclusive' => '', - 'finderQuery' => '', - 'counterQuery' => '' - ) - ); - - - var $actsAs = array('Acl' => array('type' => 'requester')); - - public function beforeValidate() { - - // Fix issue with an empty password being automagically hashed - App::import('Core', 'Security'); // not sure whether this is necessary - if ($this->data['User']['password'] == Security::hash('', null, true)) { - $this->data['User']['password'] = ''; - } - return true; - } - - function parentNode() { - if (!$this->id && empty($this->data)) { - return null; - } - if (isset($this->data['User']['group_id'])) { - $groupId = $this->data['User']['group_id']; - } else { - $groupId = $this->field('group_id'); - } - if (!$groupId) { - return null; - } else { - return array('Group' => array('id' => $groupId)); - } - } - - function bindNode($user) { - return array('model' => 'Group', 'foreign_key' => $user['User']['group_id']); - } - - - /** - * Checks if the GPG key is a valid key - * But also import it in the keychain. - */ - function validateGpgkey($check) { - // LATER first remove the old gpgkey from the keychain - - // empty value - if (empty($check['gpgkey'])) - return true; - - // key is entered - require_once 'Crypt/GPG.php'; - $gpg = new Crypt_GPG(); - try { - $key_import_output = $gpg->importKey($check['gpgkey']); - if (!empty($key_import_output['fingerprint'])) { - return true; - } - } catch (Exception $e) { - debug($e); - return false; - } - } - - - function complexPassword($check) { - debug($check); - /* - 8 characters minimum - 1 or more upper-case letters - 1 or more lower-case letters - 1 or more digits or special characters - example: "EasyPeasy34" - */ - - $value = array_values($check); - $value = $value[0]; - return preg_match('/(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/', $value); - - } - - /** - * Generates an authentication key for each user - */ - function generateAuthKey() { - //$key = sha1(mt_rand(30, 30).time()); - $length = 40; - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $char_len = strlen($characters)-1; - $key = ''; - for ($p = 0; $p < $length; $p++) { - $key .= $characters[rand(0, $char_len)]; - } - - return $key; - } - - -} diff --git a/app/plugins/recaptcha/controllers/components/recaptcha.php b/app/plugins/recaptcha/controllers/components/recaptcha.php deleted file mode 100755 index 8cbbd3dd8..000000000 --- a/app/plugins/recaptcha/controllers/components/recaptcha.php +++ /dev/null @@ -1,143 +0,0 @@ -privateKey = Configure::read('Recaptcha.privateKey'); - $this->Controller = $controller; - - if (empty($this->privateKey)) { - throw new Exception(__d('recaptcha', "You must set your private recaptcha key using Configure::write('Recaptcha.privateKey', 'your-key');!", true)); - } - - $defaults = array( - 'modelClass' => $this->Controller->modelClass, - 'errorField' => 'recaptcha', - 'actions' => array()); - - $this->settings = array_merge($defaults, $settings); - $this->actions = array_merge($this->actions, $this->settings['actions']); - extract($this->settings); - - if ($this->enabled == true) { - $this->Controller->helpers[] = 'Recaptcha.Recaptcha'; - $this->Controller->{$modelClass}->Behaviors->attach('Recaptcha.Recaptcha', array( - 'field' => $errorField)); - - $this->Controller->{$modelClass}->recaptcha = true; - if (in_array($this->Controller->action, $this->actions)) { - if (!$this->verify()) { - $this->Controller->{$modelClass}->recaptcha = false; - $this->Controller->{$modelClass}->recaptchaError = $this->error; - } - } - } - } - -/** - * Verifies the recaptcha input - * - * Please note that you still have to pass the result to the model and do - * the validation there to make sure the data is not saved! - * - * @return boolean True if the response was correct - */ - public function verify() { - if (isset($this->Controller->params['form']['recaptcha_challenge_field']) && - isset($this->Controller->params['form']['recaptcha_response_field'])) { - - $response = $this->_getApiResponse(); - $response = explode("\n", $response); - - if ($response[0] == 'true') { - return true; - } - - if ($response[1] == 'incorrect-captcha-sol') { - $this->error = __d('recaptcha', 'Incorrect captcha', true); - } else { - $this->error = $response[1]; - } - - return false; - } - } - -/** - * Queries the Recaptcha API and and returns the raw response - * - * @return string - */ - protected function _getApiResponse() { - App::import('Core', 'HttpSocket'); - $Socket = new HttpSocket(); - return $Socket->post($this->apiUrl, array( - 'privatekey'=> $this->privateKey, - 'remoteip' => env('REMOTE_ADDR'), - 'challenge' => $this->Controller->params['form']['recaptcha_challenge_field'], - 'response' => $this->Controller->params['form']['recaptcha_response_field'])); - } - -} diff --git a/app/plugins/recaptcha/license.txt b/app/plugins/recaptcha/license.txt deleted file mode 100755 index 25e5ee24c..000000000 --- a/app/plugins/recaptcha/license.txt +++ /dev/null @@ -1,25 +0,0 @@ -The MIT License - -Copyright 2009-2010 -Cake Development Corporation -1785 E. Sahara Avenue, Suite 490-423 -Las Vegas, Nevada 89104 -http://cakedc.com - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/app/plugins/recaptcha/locale/fre/LC_MESSAGES/recaptcha.po b/app/plugins/recaptcha/locale/fre/LC_MESSAGES/recaptcha.po deleted file mode 100755 index 477a4cf56..000000000 --- a/app/plugins/recaptcha/locale/fre/LC_MESSAGES/recaptcha.po +++ /dev/null @@ -1,28 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: CakePHP Recaptcha Plugin\n" -"POT-Creation-Date: 2010-09-15 15:30+0200\n" -"PO-Revision-Date: \n" -"Last-Translator: Pierre MARTIN \n" -"Language-Team: CakeDC \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Language: French\n" - -#: /controllers/components/recaptcha.php:79 -msgid "You must set your private recaptcha key using Cofigure::write('Recaptcha.privateKey', 'your-key');!" -msgstr "Vous devez définir votre clé privée Recaptcha en utilisant Configure::write('Recaptcha.privateKey', 'votre-clé'); !" - -#: /controllers/components/recaptcha.php:110 -msgid "Incorect captcha" -msgstr "Captcha incorrect" - -#: /views/helpers/recaptcha.php:115 -msgid "To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed." -msgstr "Afin d'utiliser reCAPTCHA Mailhide, vous devez avoir le module php mcrypt installé." - -#: /views/helpers/recaptcha.php:147 -msgid "You need to set a private and public mail hide key. Please visit http://mailhide.recaptcha.net/apikey" -msgstr "Vous devez définir les clés publique et privée mailhide. Veuillez visiter http://mailhide.recaptcha.net/apikey" - diff --git a/app/plugins/recaptcha/locale/recaptcha.pot b/app/plugins/recaptcha/locale/recaptcha.pot deleted file mode 100755 index f78279fb0..000000000 --- a/app/plugins/recaptcha/locale/recaptcha.pot +++ /dev/null @@ -1,39 +0,0 @@ -# LANGUAGE translation of the CakePHP Categories plugin -# -# Copyright 2010, Cake Development Corporation (http://cakedc.com) -# -# Licensed under The MIT License -# Redistributions of files must retain the above copyright notice. -# -# @copyright Copyright 2010, Cake Development Corporation (http://cakedc.com) -# @license MIT License (http://www.opensource.org/licenses/mit-license.php) -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PROJECT VERSION\n" -"POT-Creation-Date: 2010-09-15 15:30+0200\n" -"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" -"Last-Translator: NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" - -#: /controllers/components/recaptcha.php:79 -msgid "You must set your private recaptcha key using Cofigure::write('Recaptcha.privateKey', 'your-key');!" -msgstr "" - -#: /controllers/components/recaptcha.php:110 -msgid "Incorect captcha" -msgstr "" - -#: /views/helpers/recaptcha.php:115 -msgid "To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed." -msgstr "" - -#: /views/helpers/recaptcha.php:147 -msgid "You need to set a private and public mail hide key. Please visit http://mailhide.recaptcha.net/apikey" -msgstr "" - diff --git a/app/plugins/recaptcha/models/behaviors/recaptcha.php b/app/plugins/recaptcha/models/behaviors/recaptcha.php deleted file mode 100755 index b5badf855..000000000 --- a/app/plugins/recaptcha/models/behaviors/recaptcha.php +++ /dev/null @@ -1,71 +0,0 @@ - 'recaptcha'); - -/** - * Setup - * - * @param AppModel $model - * @param array $settings - */ - public function setup(Model $Model, $settings = array()) { - if (!isset($this->settings[$Model->alias])) { - $this->settings[$Model->alias] = $this->defaults; - } - $this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], ife(is_array($settings), $settings, array())); - } - -/** - * Validates the captcha responses status set by the component to the model - * - * @object Model instance - * @return boolean - * @see RecaptchaComponent::initialize() - */ - public function validateCaptcha(Model $Model) { - if (isset($Model->recaptcha) && $Model->recaptcha === false) { - $Model->invalidate($this->settings[$Model->alias]['errorField'], $Model->recaptchaError); - } - return true; - } - -/** - * Validates the captcha - * - * @object Model instance - * @return void; - */ - public function beforeValidate(Model $Model) { - $this->validateCaptcha($Model); - return true; - } -} \ No newline at end of file diff --git a/app/plugins/recaptcha/readme.md b/app/plugins/recaptcha/readme.md deleted file mode 100755 index 1aabc4912..000000000 --- a/app/plugins/recaptcha/readme.md +++ /dev/null @@ -1,57 +0,0 @@ -# Recaptcha Plugin for CakePHP # - -The Recaptcha plugin for CakePHP provides spam protection in an easy use helper. - -## Usage ## - -To use the recaptcha plugin its required to include the following two lines in your `/app/config/bootstrap.php` file. - - Configure::write('Recaptcha.publicKey', 'your-public-api-key'); - Configure::write('Recaptcha.privateKey', 'your-private-api-key'); - -Don't forget to replace the placeholder text with your actual keys! - -Keys can be obtained for free from the [Recaptcha website](http://www.google.com/recaptcha). - -Controllers that will be using recaptcha require the Recaptcha Component to be included. Through inclusion of the component, the helper is automatically made available to your views. - -In the view simply call the helpers `display()` method to render the recaptcha input: - - echo $this->Recaptcha->display(); - -To check the result simply do something like this in your controller: - - if (!empty($this->data)) { - if ($this->Recaptcha->verify()) { - // do something, save you data, login, whatever - } else { - // display the raw API error - $this->Session->setFlash($this->Recaptcha->error); - } - } - -## Requirements ## - -* PHP version: PHP 5.2+ -* CakePHP version: Cakephp 1.3 Stable - -## Support ## - -For support and feature request, please visit the [Recaptcha Plugin Support Site](http://cakedc.lighthouseapp.com/projects/60546-recaptcha-plugin/). - -For more information about our Professional CakePHP Services please visit the [Cake Development Corporation website](http://cakedc.com). - -## License ## - -Copyright 2009-2010, [Cake Development Corporation](http://cakedc.com) - -Licensed under [The MIT License](http://www.opensource.org/licenses/mit-license.php)
-Redistributions of files must retain the above copyright notice. - -## Copyright ### - -Copyright 2009-2010
-[Cake Development Corporation](http://cakedc.com)
-1785 E. Sahara Avenue, Suite 490-423
-Las Vegas, Nevada 89104
-http://cakedc.com
diff --git a/app/plugins/recaptcha/tests/cases/behaviors/recaptcha.test.php b/app/plugins/recaptcha/tests/cases/behaviors/recaptcha.test.php deleted file mode 100755 index 2a2accd97..000000000 --- a/app/plugins/recaptcha/tests/cases/behaviors/recaptcha.test.php +++ /dev/null @@ -1,73 +0,0 @@ -Model = new RecaptchaArticle(); - $this->Behavior = new RecaptchaBehavior(); - } - -/** - * Destroy the model instance - * - * @return void - */ - public function endTest() { - unset($this->Model); - unset($this->Behavior); - ClassRegistry::flush(); - } - -/** - * testValidateCaptcha - * - * @return void - */ - public function testValidateCaptcha() { - $this->Model->validateCaptcha(); - $result = $this->Model->invalidFields(); - $this->assertTrue(empty($result)); - - $this->Model->recaptcha = false; - $this->Model->recaptchaError = 'Invalid Recaptcha'; - $this->Model->validateCaptcha(); - $result = $this->Model->invalidFields(); - $this->assertEqual($result, array('recaptcha' => 'Invalid Recaptcha')); - } - -} \ No newline at end of file diff --git a/app/plugins/recaptcha/tests/cases/components/recaptcha.test.php b/app/plugins/recaptcha/tests/cases/components/recaptcha.test.php deleted file mode 100755 index d2d7036ca..000000000 --- a/app/plugins/recaptcha/tests/cases/components/recaptcha.test.php +++ /dev/null @@ -1,84 +0,0 @@ -Controller = new ArticleTestController(); - $this->Controller->constructClasses(); - //$this->Controller->modelClass = 'RecaptchaTestArticle'; - $this->Controller->Component->init($this->Controller); - $this->Controller->Component->initialize($this->Controller); - } - -/** - * endTest - * - * @return void - */ - function endTest() { - unset($this->Controller); - ClassRegistry::flush(); - } - -/** - * testRecaptcha - * - * @return void - */ - public function testRecaptcha() { - $this->Controller->params['form']['recaptcha_challenge_field'] = 'something'; - $this->Controller->params['form']['recaptcha_response_field'] = 'something'; - $this->assertFalse($this->Controller->Recaptcha->verify()); - } - -} diff --git a/app/plugins/recaptcha/tests/cases/helpers/recaptcha.test.php b/app/plugins/recaptcha/tests/cases/helpers/recaptcha.test.php deleted file mode 100755 index f40493d87..000000000 --- a/app/plugins/recaptcha/tests/cases/helpers/recaptcha.test.php +++ /dev/null @@ -1,67 +0,0 @@ -Recaptcha); - } -} diff --git a/app/plugins/recaptcha/tests/fixtures/article_fixture.php b/app/plugins/recaptcha/tests/fixtures/article_fixture.php deleted file mode 100755 index 22d386627..000000000 --- a/app/plugins/recaptcha/tests/fixtures/article_fixture.php +++ /dev/null @@ -1,45 +0,0 @@ - array('type' => 'integer', 'key' => 'primary'), - 'title' => array('type' => 'string', 'null' => false), - 'comments' => array('type' => 'integer', 'null' => false, 'default' => '0')); - -/** - * records property - * - * @var array - */ - public $records = array( - array('title' => 'First Article', 'comments' => 2), - array('title' => 'Second Article', 'comments' => 0)); -} diff --git a/app/plugins/recaptcha/views/helpers/recaptcha.php b/app/plugins/recaptcha/views/helpers/recaptcha.php deleted file mode 100755 index fffee0425..000000000 --- a/app/plugins/recaptcha/views/helpers/recaptcha.php +++ /dev/null @@ -1,215 +0,0 @@ - null, - 'publicKey' => Configure::read('Recaptcha.publicKey'), - 'error' => null, - 'ssl' => true, - 'error' => false, - 'div' => array( - 'class' => 'recaptcha')); - $options = array_merge($defaults, $options); - extract($options); - - if ($ssl) { - $server = $this->secureApiUrl; - } else { - $server = $this->apiUrl; - } - - $errorpart = ""; - if ($error) { - $errorpart = "&error=" . $error; - } - - if (!empty($element)) { - $elementOptions = array(); - if (is_array($element)) { - $keys = array_keys($element); - $elementOptions = $element[$keys[0]]; - } - $View = $this->__view(); - return $View->element($element, $elementOptions); - } - - $script = ' - '; - - if (!empty($error)) { - $script .= $this->Form->error($error); - } - - if ($options['div'] != false) { - $script = $this->Html->tag('div', $script, $options['div']); - } - - return $script; - } - -/** - * Recaptcha signup URL - * - * @return string - */ - function signupUrl($appname = null) { - return "http://recaptcha.net/api/getkey?domain=" . WWW_ROOT . '&app=' . urlencode($appName); - } - -/** - * AES Pad - * - * @param string value - * @return string - */ - private function __AesPad($val) { - $blockSize = 16; - $numpad = $blockSize - (strlen($val) % $blockSize); - return str_pad($val, strlen ($val) + $numpad, chr($numpad)); - } - -/** - * AES Encryption - * - * @return string - */ - function __AesEncrypt($value, $key) { - if (!function_exists('mcrypt_encrypt')) { - throw new Exception(__d('recaptcha', 'To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.', true)); - } - - $mode = MCRYPT_MODE_CBC; - $encryption = MCRYPT_RIJNDAEL_128; - $value = $this->__AesPad($value); - - return mcrypt_encrypt($encryption, $key, $value, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); - } - -/** - * - * - * @return string base 64 encrypted string - */ - private function __mailhideUrlbase64 ($x) { - return strtr(base64_encode ($x), '+/', '-_'); - } - -/** - * Gets the reCAPTCHA Mailhide url for a given email - * - * @param $email - * @return string - */ - public function mailHideUrl($email = null) { - $publicKey = Configure::read('Recaptcha.mailHide.publicKey'); - $privateKey = Configure::read('Recaptcha.mailHide.privateKey'); - - if ($publicKey == '' || $publicKey == null || $privateKey == "" || $privateKey == null) { - throw new Exception(__d('recaptcha', "You need to set a private and public mail hide key. Please visit http://mailhide.recaptcha.net/apikey", true)); - } - - $key = pack('H*', $privateKey); - $cryptmail = $this->__AesEncrypt ($email, $key); - - return "http://mailhide.recaptcha.net/d?k=" . $publicKey . "&c=" . $this->__mailhideUrlbase64($cryptmail); - } - -/** - * Get a part of the email to show - * - * Given johndoe@example,com return ["john", "example.com"]. - * the email is then displayed as john...@example.com - * - * @param string email - * @return array - */ - private function __hideEmailParts($email) { - $array = preg_split("/@/", $email ); - - if (strlen ($array[0]) <= 4) { - $array[0] = substr ($array[0], 0, 1); - } else if (strlen ($array[0]) <= 6) { - $array[0] = substr ($array[0], 0, 3); - } else { - $array[0] = substr ($array[0], 0, 4); - } - return $array; - } - -/** - * Gets html to display an email address given a public an private key to get a key go to: - * http://mailhide.recaptcha.net/apikey - * - * @param string Email - * @return string - */ - public function mailHide($email) { - $emailparts = __hideEmailParts ($email); - $url = $this->mailHideUrl($email); - - return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); - } - -/** - * Get current view class - * - * @return object, View class - */ - private function __view() { - if (!empty($this->globalParams['viewInstance'])) { - return $this->globalParams['viewInstance']; - } - return ClassRegistry::getObject('view'); - } - -} diff --git a/app/views/elements/email/html/empty b/app/tmp/cache/models/empty old mode 100755 new mode 100644 similarity index 100% rename from app/views/elements/email/html/empty rename to app/tmp/cache/models/empty diff --git a/app/views/elements/email/text/empty b/app/tmp/cache/persistent/empty old mode 100755 new mode 100644 similarity index 100% rename from app/views/elements/email/text/empty rename to app/tmp/cache/persistent/empty diff --git a/app/views/elements/empty b/app/tmp/cache/views/empty old mode 100755 new mode 100644 similarity index 100% rename from app/views/elements/empty rename to app/tmp/cache/views/empty diff --git a/app/views/errors/empty b/app/tmp/logs/empty old mode 100755 new mode 100644 similarity index 100% rename from app/views/errors/empty rename to app/tmp/logs/empty diff --git a/app/views/helpers/empty b/app/tmp/sessions/empty old mode 100755 new mode 100644 similarity index 100% rename from app/views/helpers/empty rename to app/tmp/sessions/empty diff --git a/app/views/layouts/email/html/empty b/app/tmp/tests/empty old mode 100755 new mode 100644 similarity index 100% rename from app/views/layouts/email/html/empty rename to app/tmp/tests/empty diff --git a/app/views/events/xml.ctp b/app/views/events/xml.ctp deleted file mode 100755 index ee5654dfd..000000000 --- a/app/views/events/xml.ctp +++ /dev/null @@ -1,4 +0,0 @@ -Xml->header(); ?> - -Xml->serialize($events, array('format' => 'tags')); ?> - \ No newline at end of file diff --git a/app/views/groups/add.ctp b/app/views/groups/add.ctp deleted file mode 100755 index 9af24f997..000000000 --- a/app/views/groups/add.ctp +++ /dev/null @@ -1,19 +0,0 @@ -
-Form->create('Group');?> -
- - Form->input('name'); - ?> -
-Form->end(__('Submit', true));?> -
-
-

-
    - -
  • Html->link(__('List Groups', true), array('action' => 'index'));?>
  • -
  • Html->link(__('List Users', true), array('controller' => 'users', 'action' => 'index')); ?>
  • -
  • Html->link(__('New User', true), array('controller' => 'users', 'action' => 'add')); ?>
  • -
-
\ No newline at end of file diff --git a/app/views/groups/edit.ctp b/app/views/groups/edit.ctp deleted file mode 100755 index e8e7616a3..000000000 --- a/app/views/groups/edit.ctp +++ /dev/null @@ -1,21 +0,0 @@ -
-Form->create('Group');?> -
- - Form->input('id'); - echo $this->Form->input('name'); - ?> -
-Form->end(__('Submit', true));?> -
-
-

-
    - -
  • Html->link(__('Delete', true), array('action' => 'delete', $this->Form->value('Group.id')), null, sprintf(__('Are you sure you want to delete # %s?', true), $this->Form->value('Group.id'))); ?>
  • -
  • Html->link(__('List Groups', true), array('action' => 'index'));?>
  • -
  • Html->link(__('List Users', true), array('controller' => 'users', 'action' => 'index')); ?>
  • -
  • Html->link(__('New User', true), array('controller' => 'users', 'action' => 'add')); ?>
  • -
-
\ No newline at end of file diff --git a/app/views/groups/index.ctp b/app/views/groups/index.ctp deleted file mode 100755 index 781df81aa..000000000 --- a/app/views/groups/index.ctp +++ /dev/null @@ -1,49 +0,0 @@ -
-

-
Organisation # of members
   Amount
     
- - - - - - - > - - - - - -
Paginator->sort('id');?>Paginator->sort('name');?>
   - Html->link(__('View', true), array('action' => 'view', $group['Group']['id'])); ?> - Html->link(__('Edit', true), array('action' => 'edit', $group['Group']['id'])); ?> - Html->link(__('Delete', true), array('action' => 'delete', $group['Group']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $group['Group']['id'])); ?> -
-

- Paginator->counter(array( - 'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true) - )); - ?>

- -
- Paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled'));?> - | Paginator->numbers();?> - | - Paginator->next(__('next', true) . ' >>', array(), null, array('class' => 'disabled'));?> -
-
-
-

-
    -
  • Html->link(__('New Group', true), array('action' => 'add')); ?>
  • -
  • Html->link(__('List Users', true), array('controller' => 'users', 'action' => 'index')); ?>
  • -
  • Html->link(__('New User', true), array('controller' => 'users', 'action' => 'add')); ?>
  • -
-
\ No newline at end of file diff --git a/app/views/groups/view.ctp b/app/views/groups/view.ctp deleted file mode 100755 index 0f6c54648..000000000 --- a/app/views/groups/view.ctp +++ /dev/null @@ -1,72 +0,0 @@ -
-

-
- > - > - -   - - > - > - -   - -
-
-
-

-
    -
  • Html->link(__('Edit Group', true), array('action' => 'edit', $group['Group']['id'])); ?>
  • -
  • Html->link(__('Delete Group', true), array('action' => 'delete', $group['Group']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $group['Group']['id'])); ?>
  • -
  • Html->link(__('List Groups', true), array('action' => 'index')); ?>
  • -
  • Html->link(__('New Group', true), array('action' => 'add')); ?>
  • -
  • Html->link(__('List Users', true), array('controller' => 'users', 'action' => 'index')); ?>
  • -
  • Html->link(__('New User', true), array('controller' => 'users', 'action' => 'add')); ?>
  • -
-
- diff --git a/app/views/layouts/email/text/empty b/app/views/layouts/email/text/empty deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/views/layouts/js/empty b/app/views/layouts/js/empty deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/views/layouts/rss/empty b/app/views/layouts/rss/empty deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/views/layouts/xml/empty b/app/views/layouts/xml/empty deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/views/pages/empty b/app/views/pages/empty deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/views/scaffolds/empty b/app/views/scaffolds/empty deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/views/signatures/edit.ctp b/app/views/signatures/edit.ctp deleted file mode 100755 index 5d1f92b09..000000000 --- a/app/views/signatures/edit.ctp +++ /dev/null @@ -1,23 +0,0 @@ -
-Form->create('Signature');?> -
- - Form->input('id'); - echo $this->Form->hidden('event_id'); - echo $this->Form->input('type'); - echo $this->Form->input('to_ids'); - echo $this->Form->input('value'); - ?> -
-Form->end(__('Submit', true));?> -
-
-

-
    - -
  • Html->link(__('Delete', true), array('action' => 'delete', $this->Form->value('Signature.id')), null, sprintf(__('Are you sure you want to delete # %s?', true), $this->Form->value('Signature.id'))); ?>
  • -
  •  
  • - element('actions_menu'); ?> -
-
diff --git a/app/views/signatures/index.ctp b/app/views/signatures/index.ctp deleted file mode 100755 index 0f14cd08d..000000000 --- a/app/views/signatures/index.ctp +++ /dev/null @@ -1,56 +0,0 @@ -
-

- - - - - - - - - - onclick="document.location ='Html->url(array('controller' => 'events', 'action' => 'view', $signature['Signature']['event_id']), true) ;?>';"> - - - - - - - -
Paginator->sort('event_id');?>Paginator->sort('type');?>Paginator->sort('value');?>To IDS
- Html->link($signature['Event']['id'], array('controller' => 'events', 'action' => 'view', $signature['Event']['id'])); ?> -     - Html->link(__('Edit', true), array('action' => 'edit', $signature['Signature']['id'])); - echo $this->Html->link(__('Delete', true), array('action' => 'delete', $signature['Signature']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $signature['Signature']['id'])); - } ?> - Html->link(__('View', true), array('action' => 'view', $signature['Signature']['id'])); ?> -
-

- Paginator->counter(array( - 'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%', true) - )); - ?>

- -
- Paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled'));?> - | Paginator->numbers();?> - | - Paginator->next(__('next', true) . ' >>', array(), null, array('class' => 'disabled'));?> -
-
-
-

-
    - element('actions_menu'); ?> -
-
diff --git a/app/views/signatures/view.ctp b/app/views/signatures/view.ctp deleted file mode 100755 index 49e0594e4..000000000 --- a/app/views/signatures/view.ctp +++ /dev/null @@ -1,41 +0,0 @@ -
-

-
- > - > - -   - - > - > - Html->link($signature['Event']['id'], array('controller' => 'events', 'action' => 'view', $signature['Event']['id'])); ?> -   - - > - > - -   - - > - > - -   - - > - > - -   - -
-
-
-

-
    - -
  • Html->link(__('Edit Signature', true), array('action' => 'edit', $signature['Signature']['id'])); ?>
  • -
  • Html->link(__('Delete Signature', true), array('action' => 'delete', $signature['Signature']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $signature['Signature']['id'])); ?>
  • -
  •  
  • - - element('actions_menu'); ?> -
-
diff --git a/app/views/users/view.ctp b/app/views/users/view.ctp deleted file mode 100755 index 975d7d30a..000000000 --- a/app/views/users/view.ctp +++ /dev/null @@ -1,63 +0,0 @@ -
-
-
  • Html->link(__('Edit Profile', true), array('action' => 'edit', $user['User']['id'])); ?>
-
-

-
- > - > - -   - - > - > - Html->link($user['Group']['name'], array('controller' => 'groups', 'action' => 'view', $user['Group']['id'])); ?> -   - - > - > - -   - - > - > - - XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -   - - > - > - -   - - > - > - -   - - > (Html->link('reset', array('controller' => 'users', 'action' => 'resetauthkey', $user['User']['id']));?>) - > - -   - - >NIDS start SID - > - -   - - > - style="font-size: 10px; padding:0px; margin:0px;line-height:100%;"> -
-   - -
-
-
-

-
    -
  • Html->link(__('Edit User', true), array('action' => 'edit', $user['User']['id'])); ?>
  • -
  • Html->link(__('Delete User', true), array('action' => 'delete', $user['User']['id']), null, sprintf(__('Are you sure you want to delete # %s?', true), $user['User']['id'])); ?>
  • -
  •  
  • - element('actions_menu'); ?> -
-
diff --git a/app/webroot/.htaccess b/app/webroot/.htaccess old mode 100755 new mode 100644 index f9d8b938b..f2646d170 --- a/app/webroot/.htaccess +++ b/app/webroot/.htaccess @@ -2,5 +2,5 @@ RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] - \ No newline at end of file + RewriteRule ^(.*)$ index.php?/$1 [QSA,L] + diff --git a/app/webroot/css.php b/app/webroot/css.php deleted file mode 100755 index 8bf90839f..000000000 --- a/app/webroot/css.php +++ /dev/null @@ -1,96 +0,0 @@ -compress($data); - $ratio = 100 - (round(strlen($output) / strlen($data), 3) * 100); - $output = " /* file: $name, ratio: $ratio% */ " . $output; - return $output; - } -/** - * Write CSS cache - * - * @param unknown_type $path - * @param unknown_type $content - * @return unknown - */ - function write_css_cache($path, $content) { - if (!is_dir(dirname($path))) { - mkdir(dirname($path)); - } - $cache = new File($path); - return $cache->write($content); - } - - if (preg_match('|\.\.|', $url) || !preg_match('|^ccss/(.+)$|i', $url, $regs)) { - die('Wrong file name.'); - } - - $filename = 'css/' . $regs[1]; - $filepath = CSS . $regs[1]; - $cachepath = CACHE . 'css' . DS . str_replace(array('/','\\'), '-', $regs[1]); - - if (!file_exists($filepath)) { - die('Wrong file name.'); - } - - if (file_exists($cachepath)) { - $templateModified = filemtime($filepath); - $cacheModified = filemtime($cachepath); - - if ($templateModified > $cacheModified) { - $output = make_clean_css($filepath, $filename); - write_css_cache($cachepath, $output); - } else { - $output = file_get_contents($cachepath); - } - } else { - $output = make_clean_css($filepath, $filename); - write_css_cache($cachepath, $output); - $templateModified = time(); - } - - header("Date: " . date("D, j M Y G:i:s ", $templateModified) . 'GMT'); - header("Content-Type: text/css"); - header("Expires: " . gmdate("D, d M Y H:i:s", time() + DAY) . " GMT"); - header("Cache-Control: max-age=86400, must-revalidate"); // HTTP/1.1 - header("Pragma: cache"); // HTTP/1.0 - print $output; diff --git a/app/webroot/css/cake.generic.css b/app/webroot/css/cake.generic.css old mode 100755 new mode 100644 index 6eaa62227..ed01e935d --- a/app/webroot/css/cake.generic.css +++ b/app/webroot/css/cake.generic.css @@ -1,3 +1,4 @@ +@charset "utf-8"; /** * * Generic CSS for CakePHP @@ -10,8 +11,7 @@ * * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package cake - * @subpackage cake.app.webroot.css + * @package app.webroot.css * @since CakePHP(tm) * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ @@ -57,7 +57,7 @@ h2 { font-size: 190%; } h3 { - color: #993; + color: #2c6877; font-family:'Gill Sans','lucida grande', helvetica, arial, sans-serif; font-size: 165%; } @@ -75,7 +75,6 @@ em { font-weight: bold; } - /** Layout **/ #container { text-align: left; @@ -136,7 +135,6 @@ div.actions h3 { /** Tables **/ table { - background: #fff; border-right:0; clear: both; color: #333; @@ -148,6 +146,7 @@ th { border-bottom:2px solid #555; text-align: left; padding:4px; + white-space: nowrap; } th a { display: block; @@ -161,26 +160,25 @@ th a.desc:after { content: ' ⇡'; } table tr td { - /*background: #fff;*/ padding: 6px; text-align: left; vertical-align: top; border-bottom:1px solid #ddd; } -/*table tr:nth-child(2n) td { - background: #f5f5f5; +table tr:nth-child(even) { + background: #f9f9f9; } -table .altrow td { - background: #f5f5f5; -}*/ td.actions { - text-align: center; + text-align: right; white-space: nowrap; } table td.actions a { margin: 0px 6px; padding:2px 5px; } +th.actions { + text-align:center; +} /* added */ div .events table tr:hover, div .events table tr.altrow:hover, div .signatures table tr:hover, div .signatures table tr.altrow:hover{ @@ -195,8 +193,9 @@ table tr:nth-child(2n) { } /* /added */ -.cake-sql-log table { - background: #f4f4f4; +/* SQL log */ +.cake-sql-log { + background: #fff; } .cake-sql-log td { padding: 4px 8px; @@ -208,21 +207,48 @@ table tr:nth-child(2n) { } /** Paging **/ -div.paging { +.paging { background:#fff; color: #ccc; margin-top: 1em; clear:both; } -div.paging span.disabled { +.paging .current, +.paging .disabled, +.paging a { + text-decoration: none; + padding: 5px 8px; + display: inline-block +} +.paging > span { + display: inline-block; + border: 1px solid #ccc; + border-left: 0; +} +.paging > span:hover { + background: #efefef; +} +.paging .prev { + border-left: 1px solid #ccc; + -moz-border-radius: 4px 0 0 4px; + -webkit-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.paging .next { + -moz-border-radius: 0 4px 4px 0; + -webkit-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.paging .disabled { color: #ddd; - display: inline; } -div.paging span.current { +.paging .disabled:hover { + background: transparent; +} +.paging .current { + background: #efefef; color: #c73e14; } -div.paging span a { -} /** Scaffold View **/ dl { @@ -230,9 +256,11 @@ dl { margin: 0em 0em; width: 60%; } -dl .altrow { +dl dd:nth-child(4n+2), +dl dt:nth-child(4n+1) { background: #f4f4f4; } + dt { font-weight: bold; padding-left: 4px; @@ -258,7 +286,6 @@ fieldset { padding: 16px 20px; } fieldset legend { - background:#fff; color: #e32; font-size: 160%; font-weight: bold; @@ -336,8 +363,9 @@ input[type=radio] { width:auto; margin: 0 3px 7px 0; } -div.radio label { +.radio label { margin: 0 0 6px 20px; + line-height: 26px; } input[type=submit] { display: inline; @@ -346,112 +374,207 @@ input[type=submit] { } form .submit input[type=submit] { background:#62af56; - background: -webkit-gradient(linear, left top, left bottom, from(#a8ea9c), to(#62af56)); - background-image: -moz-linear-gradient(top, #a8ea9c, #62af56); + background-image: -webkit-gradient(linear, left top, left bottom, from(#76BF6B), to(#3B8230)); + background-image: -webkit-linear-gradient(top, #76BF6B, #3B8230); + background-image: -moz-linear-gradient(top, #76BF6B, #3B8230); border-color: #2d6324; - color: #000; - text-shadow: #8cee7c 0px 1px 0px; + color: #fff; + text-shadow: rgba(0, 0, 0, 0.5) 0px -1px 0px; + padding: 8px 10px; } form .submit input[type=submit]:hover { - background:#4ca83d; - background: -webkit-gradient(linear, left top, left bottom, from(#85e573), to(#4ca83d)); - background-image: -moz-linear-gradient(top, #85e573, #4ca83d); + background: #5BA150; +} +/* Form errors */ +form .error { + background: #FFDACC; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + font-weight: normal; +} +form .error-message { + -moz-border-radius: none; + -webkit-border-radius: none; + border-radius: none; + border: none; + background: none; + margin: 0; + padding-left: 4px; + padding-right: 0; +} +form .error, +form .error-message { + color: #9E2424; + -webkit-box-shadow: none; + -moz-box-shadow: none; + -ms-box-shadow: none; + -o-box-shadow: none; + box-shadow: none; + text-shadow: none; } /** Notices and Errors **/ -div.message { +.message { clear: both; color: #fff; font-size: 140%; font-weight: bold; margin: 0 0 1em 0; - background: #2d900e; padding: 5px; } -#errorMessage, #gpgMessage, #authMessage { - background: #c73e14; + +.success, +.message, +.cake-error, +.cake-debug, +.notice, +p.error, +.error-message { + background: #ffcc00; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(top, #ffcc00, #E6B800); + background-image: -ms-linear-gradient(top, #ffcc00, #E6B800); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ffcc00), to(#E6B800)); + background-image: -webkit-linear-gradient(top, #ffcc00, #E6B800); + background-image: -o-linear-gradient(top, #ffcc00, #E6B800); + background-image: linear-gradient(top, #ffcc00, #E6B800); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + border: 1px solid rgba(0, 0, 0, 0.2); + margin-bottom: 18px; + padding: 7px 14px; + color: #404040; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); } -div.error-message { +.success, +.message, +.cake-error, +p.error, +.error-message { clear: both; color: #fff; - font-weight: bold; - background: #c73e14; + background: #c43c35; + border: 1px solid rgba(0, 0, 0, 0.5); + background-repeat: repeat-x; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(top, #ee5f5b, #c43c35); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3); +} +.success { + clear: both; + color: #fff; + border: 1px solid rgba(0, 0, 0, 0.5); + background: #3B8230; + background-repeat: repeat-x; + background-image: -webkit-gradient(linear, left top, left bottom, from(#76BF6B), to(#3B8230)); + background-image: -webkit-linear-gradient(top, #76BF6B, #3B8230); + background-image: -moz-linear-gradient(top, #76BF6B, #3B8230); + background-image: -ms-linear-gradient(top, #76BF6B, #3B8230); + background-image: -o-linear-gradient(top, #76BF6B, #3B8230); + background-image: linear-gradient(top, #76BF6B, #3B8230); + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3); } p.error { - background-color: #e32; - color: #fff; - font-family: Courier, monospace; + font-family: Monaco, Consolas, Courier, monospace; font-size: 120%; - line-height: 140%; padding: 0.8em; margin: 1em 0; } p.error em { - color: #000; font-weight: normal; line-height: 140%; } .notice { - background: #ffcc00; color: #000; display: block; - font-family: Courier, monospace; font-size: 120%; - line-height: 140%; padding: 0.8em; margin: 1em 0; } .success { - background: green; color: #fff; } /** Actions **/ -div.actions ul { +.actions ul { margin: 0; padding: 0; } -div.actions li { +.actions li { margin:0 0 0.5em 0; list-style-type: none; white-space: nowrap; padding: 0; } -div.actions ul li a { +.actions ul li a { font-weight: normal; display: block; clear: both; } -div.actions ul li a:hover { - text-decoration: underline; -} +/* Buttons and button links */ input[type=submit], -div.actions ul li a, -td.actions a { +.actions ul li a, +.actions a { font-weight:normal; padding: 4px 8px; - background:#e6e49f; - background: -webkit-gradient(linear, left top, left bottom, from(#f1f1d4), to(#e6e49f)); - background-image: -moz-linear-gradient(top, #f1f1d4, #e6e49f); + background: #dcdcdc; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#dcdcdc)); + background-image: -webkit-linear-gradient(top, #fefefe, #dcdcdc); + background-image: -moz-linear-gradient(top, #fefefe, #dcdcdc); + background-image: -ms-linear-gradient(top, #fefefe, #dcdcdc); + background-image: -o-linear-gradient(top, #fefefe, #dcdcdc); + background-image: linear-gradient(top, #fefefe, #dcdcdc); color:#333; - border:1px solid #aaac62; - -webkit-border-radius:8px; - -moz-border-radius:8px; - border-radius:8px; - text-decoration:none; + border:1px solid #bbb; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + text-decoration: none; text-shadow: #fff 0px 1px 0px; min-width: 0; + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0px 1px 1px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0px 1px 1px rgba(0, 0, 0, 0.2); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.3), 0px 1px 1px rgba(0, 0, 0, 0.2); + -webkit-user-select: none; + user-select: none; } -input[type=submit]:hover, -div.actions ul li a:hover, -td.actions a:hover { - background: #f0f09a; - background: -webkit-gradient(linear, left top, left bottom, from(#f7f7e1), to(#eeeca9)); +.actions ul li a:hover, +.actions a:hover { + background: #ededed; + border-color: #acacac; + text-decoration: none; +} +input[type=submit]:active, +.actions ul li a:active, +.actions a:active { + background: #eee; + background-image: -webkit-gradient(linear, left top, left bottom, from(#dfdfdf), to(#eee)); + background-image: -webkit-linear-gradient(top, #dfdfdf, #eee); + background-image: -moz-linear-gradient(top, #dfdfdf, #eee); + background-image: -ms-linear-gradient(top, #dfdfdf, #eee); + background-image: -o-linear-gradient(top, #dfdfdf, #eee); + background-image: linear-gradient(top, #dfdfdf, #eee); + text-shadow: #eee 0px 1px 0px; + -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); + box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3); + border-color: #aaa; + text-decoration: none; } /** Related **/ -div.related { +.related { clear: both; display: block; } @@ -460,38 +583,108 @@ div.related { pre { color: #000; background: #f0f0f0; - padding: 1em; + padding: 15px; + -moz-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); + box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); } -pre.cake-debug { - background: #ffcc00; - font-size: 120%; - line-height: 140%; - margin-top: 1em; - overflow: auto; +.cake-debug-output { + padding: 0; position: relative; } -div.cake-stack-trace { - background: #fff; +.cake-debug-output > span { + position: absolute; + top: 5px; + right: 5px; + background: rgba(255, 255, 255, 0.3); + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + padding: 5px 6px; + color: #000; + display: block; + float: left; + -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.5); + -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.5); + box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(255, 255, 255, 0.5); + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.8); +} +.cake-debug, +.cake-error { + font-size: 16px; + line-height: 20px; + clear: both; +} +.cake-error > a { + text-shadow: none; +} +.cake-error { + white-space: normal; +} +.cake-stack-trace { + background: rgba(255, 255, 255, 0.7); color: #333; - margin: 0px; - padding: 6px; + margin: 10px 0 5px 0; + padding: 10px 10px 0 10px; font-size: 120%; line-height: 140%; overflow: auto; position: relative; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; } -div.cake-code-dump pre { +.cake-stack-trace a { + text-shadow: none; + background: rgba(255, 255, 255, 0.7); + padding: 5px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; + margin: 0px 4px 10px 2px; + font-family: sans-serif; + font-size: 14px; + line-height: 14px; + display: inline-block; + text-decoration: none; + -moz-box-shadow: inset 0px 1px 0 rgba(0, 0, 0, 0.3); + -webkit-box-shadow: inset 0px 1px 0 rgba(0, 0, 0, 0.3); + box-shadow: inset 0px 1px 0 rgba(0, 0, 0, 0.3); +} +.cake-code-dump pre { position: relative; overflow: auto; } -div.cake-stack-trace pre, div.cake-code-dump pre { +.cake-context { + margin-bottom: 10px; +} +.cake-stack-trace pre { color: #000; background-color: #F0F0F0; - margin: 0px; + margin: 0px 0 10px 0; padding: 1em; overflow: auto; + text-shadow: none; } -div.cake-code-dump pre, div.cake-code-dump pre code { +.cake-stack-trace li { + padding: 10px 5px 0px; + margin: 0 0 4px 0; + font-family: monospace; + border: 1px solid #bbb; + -moz-border-radius: 4px; + -wekbkit-border-radius: 4px; + border-radius: 4px; + background: #dcdcdc; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fefefe), to(#dcdcdc)); + background-image: -webkit-linear-gradient(top, #fefefe, #dcdcdc); + background-image: -moz-linear-gradient(top, #fefefe, #dcdcdc); + background-image: -ms-linear-gradient(top, #fefefe, #dcdcdc); + background-image: -o-linear-gradient(top, #fefefe, #dcdcdc); + background-image: linear-gradient(top, #fefefe, #dcdcdc); +} +/* excerpt */ +.cake-code-dump pre, +.cake-code-dump pre code { clear: both; font-size: 12px; line-height: 15px; @@ -499,25 +692,25 @@ div.cake-code-dump pre, div.cake-code-dump pre code { padding: 4px; overflow: auto; } -div.cake-code-dump span.code-highlight { - background-color: #ff0; - padding: 4px; +.cake-code-dump .code-highlight { + display: block; + background-color: rgba(255, 255, 0, 0.5); } -div.code-coverage-results div.code-line { +.code-coverage-results div.code-line { padding-left:5px; display:block; margin-left:10px; } -div.code-coverage-results div.uncovered span.content { +.code-coverage-results div.uncovered span.content { background:#ecc; } -div.code-coverage-results div.covered span.content { +.code-coverage-results div.covered span.content { background:#cec; } -div.code-coverage-results div.ignored span.content { +.code-coverage-results div.ignored span.content { color:#aaa; } -div.code-coverage-results span.line-num { +.code-coverage-results span.line-num { color:#666; display:block; float:left; @@ -525,41 +718,41 @@ div.code-coverage-results span.line-num { text-align:right; margin-right:5px; } -div.code-coverage-results span.line-num strong { +.code-coverage-results span.line-num strong { color:#666; } -div.code-coverage-results div.start { +.code-coverage-results div.start { border:1px solid #aaa; border-width:1px 1px 0px 1px; margin-top:30px; padding-top:5px; } -div.code-coverage-results div.end { +.code-coverage-results div.end { border:1px solid #aaa; border-width:0px 1px 1px 1px; margin-bottom:30px; padding-bottom:5px; } -div.code-coverage-results div.realstart { +.code-coverage-results div.realstart { margin-top:0px; } -div.code-coverage-results p.note { +.code-coverage-results p.note { color:#bbb; padding:5px; margin:5px 0 10px; font-size:10px; } -div.code-coverage-results span.result-bad { +.code-coverage-results span.result-bad { color: #a00; } -div.code-coverage-results span.result-ok { +.code-coverage-results span.result-ok { color: #fa0; } -div.code-coverage-results span.result-good { +.code-coverage-results span.result-good { color: #0a0; } /** Elements **/ #url-rewriting-warning { - display: none; + display:none; } diff --git a/app/webroot/favicon.ico b/app/webroot/favicon.ico old mode 100755 new mode 100644 diff --git a/app/webroot/files/empty b/app/webroot/files/empty old mode 100755 new mode 100644 diff --git a/app/webroot/gpg.asc b/app/webroot/gpg.asc old mode 100755 new mode 100644 index a0a75df76..7f80f9244 --- a/app/webroot/gpg.asc +++ b/app/webroot/gpg.asc @@ -1,31 +1,29 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v1.4.10 (GNU/Linux) +Version: GnuPG/MacGPG2 v2.0.17 (Darwin) -mQINBE4pfAwBEADYA3G9jJOHa8HDmFRrNdYXqDKvdkY7Lmb09LKe3ynteSB7Ix1Y -4uuph6ytfmfAZL9j9rKpIWkJNYQvWi9zolqF4JPLmZx0SL6e20mkP7GFz40vvWez -EDPlefcBAo413S2Uh6GedS15PwliLIS9s8VtmldTj0+uJIyiOb/STmYtqxpOuc3O -q0EpWePesNW9XXtyqzmPcGTL7Ve0utEumBrzUKglUAGjNeOgCbpmpYYvCKQNO8/L -eLAn+00vzSogODaZSONpNoo8Ay1M2G4VnKRp3rYCpt5CuXdoy4eB0iiGZybKr5V7 -NF512S+TEB9prZY7DOTOynMpU/Iw+vt55OvISOx7er839cnHWd29OJ2y0iCWTTtb -np8YbZn187Hl6LGuqayMtSpdnousRKoJevVR/ZMkItdbbvg8G376+XgBu6InanWQ -Dc9F7m/z0I37lyNY03oyLVHgIxJ7HzXbCSpO9loBTISiezfHqzFV446JVg5PbDw1 -EaTme7MjyN4zq6yIhqsGN80oMdhWppr36LEb+gH7894hH7a4i7gQ9dTtBaHZRWN9 -g5UdcR4g5RnYrF39q9tQb45EkYlnMNzk+Hf45dHeEcTSa7x0DDj16n6oSSjt9CVT -yR3wVl3/HUS2+PKkr9ATjrMIAgoa6LyyexxBqyu3Igqmp4hgczSJk/c3RQARAQAB -tGFDeURlZlNJRyAoS2V5IHVzZWQgYnkgdGhlIGh0dHA6Ly9zaWcuY3liZXItZGVm -ZW5jZS5iZSBzaWduYXR1cmUgcGxhdGZvcm0uKSA8c2lnQGN5YmVyLWRlZmVuY2Uu -YmU+iQI+BBMBAgAoBQJOKXwMAhsDBQkDwmcABgsJCAcDAgYVCAIJCgsEFgIDAQIe -AQIXgAAKCRCMjnRav5LcHbwJEACAO91oqFsHwatZ+X/mu8Z01tNKVaY59nLKV5W/ -rLWMy4UUM3qOymPoTPU/fRHH3Z719Krn6ENO6YqlH3va/expr6y2LMjyqJ9ghrqw -ltjgapxD8Spg2LHhcaGhCBw7BIK25VNwdLr/PFg2GK+oLrdHit9P7WyQsEaMKoVz -60QOBqU4qhH12viSBv9wr/GcmVONyC+4dXqg7Vvr9jhml+PNBrzGVCxAalYZxMva -5oftnmSQuu6GGcZoLAAMiSNaajomIh7L9qC7F+unr6FBIReuFTkAC47rGcKZKCi/ -4wMa+wH1tCESlyikPkhkTOy5tx3gDVTEZ2T7C1lw2njiwKQaobmuAq78bYme3tje -KKdUiPamG5QejPKx5arz6ppixe2K2ZqLnrn0ufz8x6xCAOFr9Zl9e2GNN9tjBt+A -iClNQqTfmh0c0/9u4qVvISPr7nI3nLd+clJbAgfIwzQ0uTUzdWT+vr2wFBjtIPW7 -apyon7OZWMkiDn3Va+AVI7MT2dxntKjd3FFjdg29NSkd9X1Fg+0OCj1cIfkaj1YM -nAHvdRi7rMPbHpL0BP0jkpOhftiNlJeJRdAcOZsWSIBVzN5S19+AblO/8MUdBbZr -RD0clGAc8rNvwpLfd8Y13B60LPg0QU1x5f8cS89gKR0AdQzSCoYH+IVszYETXRlL -Sbfjfg== -=eVHA +mQINBE9h3OwBEAC+VNGAIret/qhE6ozMyDbg2sOclTyAT8TToXZDBDDpcKojS4vt +1GQFZFxq1EQQnFzmQuwZyMBGTBPzyMRTu4s6ENjEBGks3lVX1RZz986dbGwuV8fY +eufspfUIh+fOyY35TYm9pdj96azJvhLjbq6eN84gsEciauzwYF9D1pxX+9LZJEBD +6Hss4kdhoOsPlCLb25BvjykBJNIF6CgRCAuuPW8D8TQZtl7jTq6mu2GUR7nvLnEg +axkoXjj4Y0I4qxl72KkSo6NOBZGli7hIaAH9/JtvC+xbsyiA2LdbCTA0jN9KvAZB +YwZA582irwT2O3NVWBAV/iqCp/FDIl0uMtt+o8r2KdeuG/pow3DUxgTf2NKKjy/H +T452FsrL7R6Qz/pGcLdtTZyN6X4gIRJa2DmrPZD/rumlWYkDNfG+gY3PGHBr1YB0 +DOjuK+mEaF9HaZPmQexJcEceNvg1FLj6vu54G0v4fh6Dr79gxq0eLY6w3staPvTU +tyGRXfL4EPhUURMxm808P0eC1MryW9y3a+6fHrMPqdoiBaqUcyxB53pvnst943PT +TwkA0MMju9h3ACSgVLEsZVmsRv1brUPPy4AOzmLQZbeInpTC2ySPnKzDX7BW49QR +iRrkgQNbdG31mASCBHG9Vd/yxxvqyvDeRIqK9BWdEtnisNlTSf9VOpJdNwARAQAB +tCpDeURlZlNJRyBERVZFTE9QTUVOVCA8bm8tcmVwbHlAc2lnLm1pbC5iZT6JAj4E +EwECACgFAk9h3OwCGwMFCQlmAYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJ +EP6ocJ8jgrhGne0P/AhBi/u+0lI7+AZj12tTIIO2a3xuMaSFs2V7q7tyhVH3IhPS +KuUUvE1l3ObAejpljrm1rQgrt2Ksd3ZFDh0OuiMnnxYv8UONk9UOlQ6OfF5NQHlz +eLzzGclF8er4OFKrxqw0gacIKLtBaaKllHdMtwal1C/lC+qy7c7ZU+nxSHc5OB/v +lxrsFW5e9Tu6uSEu6QuTt6xLFftpEwy2CrbN+TCuKZqRKlt7h+77OEDTSmFa1n0y +yRm2RsP7O2OqOAT4PDV0i5ipm3p18aUV6OFRGj0zvtEl8dKPkDQ9UXprupdkj0xX +cygqQKIpHkprBINcTO+qAO5WIb9Wa0+nFjPgtpGp6by+iJEqdI63JEYBfP6S605y +8DsJQCpZkdU4bM80rDL4samuBa42IsB3Z1J3tp+ZlAN9Gvn+D5/XrkMQACLrFdFY +4VCQ9pXFgnDDL7ij05g0H0nE+0NC7NYEoJS5EQ6aJxveLXgLMQf+P6PxN9RKUjwh +sYG/ZGMn1QUg9MMIneRzN+PWjetDjj/dOvfB81tCtRd+8OxtUApau0d+q2WmTDC3 +J4WAA1gJ5kYKrdNLdRyAm7xi7qvvksjEx7JrPJZheKrnd09tzL99JTTjhkT/klo9 +x2Bp5kt3n412h+dnEtMQ+PP5r8Ss0vZDx55dwEdP0hIEoPsxy4iLnz5E5NIa +=SqGr -----END PGP PUBLIC KEY BLOCK----- diff --git a/app/webroot/img/cake.icon.png b/app/webroot/img/cake.icon.png old mode 100755 new mode 100644 diff --git a/app/webroot/img/cake.power.gif b/app/webroot/img/cake.power.gif old mode 100755 new mode 100644 diff --git a/app/webroot/img/test-error-icon.png b/app/webroot/img/test-error-icon.png old mode 100755 new mode 100644 diff --git a/app/webroot/img/test-fail-icon.png b/app/webroot/img/test-fail-icon.png old mode 100755 new mode 100644 diff --git a/app/webroot/img/test-pass-icon.png b/app/webroot/img/test-pass-icon.png old mode 100755 new mode 100644 diff --git a/app/webroot/img/test-skip-icon.png b/app/webroot/img/test-skip-icon.png old mode 100755 new mode 100644 diff --git a/app/webroot/index.php b/app/webroot/index.php old mode 100755 new mode 100644 index 5b5c98709..755c9c2a9 --- a/app/webroot/index.php +++ b/app/webroot/index.php @@ -4,7 +4,7 @@ * * The Front Controller for handling every request * - * PHP versions 4 and 5 + * PHP 5 * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) @@ -14,8 +14,7 @@ * * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package cake - * @subpackage cake.app.webroot + * @package app.webroot * @since CakePHP(tm) v 0.2.9 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ @@ -45,13 +44,19 @@ if (!defined('APP_DIR')) { define('APP_DIR', basename(dirname(dirname(__FILE__)))); } + /** * The absolute path to the "cake" directory, WITHOUT a trailing DS. * + * Un-comment this line to specify a fixed path to CakePHP. + * This should point at the directory containing `Cake`. + * + * For ease of development CakePHP uses PHP's include_path. If you + * cannot modify your include_path set this value. + * + * Leaving this constant undefined will result in it being defined in Cake/bootstrap.php */ - if (!defined('CAKE_CORE_INCLUDE_PATH')) { - define('CAKE_CORE_INCLUDE_PATH', ROOT); - } + //define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib'); /** * Editing below this line should NOT be necessary. @@ -64,21 +69,28 @@ if (!defined('WWW_ROOT')) { define('WWW_ROOT', dirname(__FILE__) . DS); } - if (!defined('CORE_PATH')) { - if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) { - define('APP_PATH', null); - define('CORE_PATH', null); - } else { - define('APP_PATH', ROOT . DS . APP_DIR . DS); - define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); + + if (!defined('CAKE_CORE_INCLUDE_PATH')) { + if (function_exists('ini_set')) { + ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path')); + } + if (!include('Cake' . DS . 'bootstrap.php')) { + $failed = true; + } + } else { + if (!include(CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) { + $failed = true; } } - if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) { + if (!empty($failed)) { trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); } - if (isset($_GET['url']) && $_GET['url'] === 'favicon.ico') { + + if (isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] == '/favicon.ico') { return; - } else { - $Dispatcher = new Dispatcher(); - $Dispatcher->dispatch(); } + + App::uses('Dispatcher', 'Routing'); + + $Dispatcher = new Dispatcher(); + $Dispatcher->dispatch(new CakeRequest(), new CakeResponse(array('charset' => Configure::read('App.encoding')))); diff --git a/app/webroot/js/empty b/app/webroot/js/empty old mode 100755 new mode 100644 diff --git a/app/webroot/js/jquery.js b/app/webroot/js/jquery.js deleted file mode 100755 index 48590ecb9..000000000 --- a/app/webroot/js/jquery.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * jQuery JavaScript Library v1.6.2 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Thu Jun 30 14:16:56 2011 -0400 - */ -(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. -shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j -)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/app/webroot/test.php b/app/webroot/test.php old mode 100755 new mode 100644 index e0c69a164..e141912c3 --- a/app/webroot/test.php +++ b/app/webroot/test.php @@ -2,20 +2,19 @@ /** * Web Access Frontend for TestSuite * - * PHP versions 4 and 5 + * PHP 5 * * CakePHP(tm) Tests * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * - * Licensed under The Open Group Test Suite License - * Redistributions of files must retain the above copyright notice. + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice * * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://book.cakephp.org/view/1196/Testing - * @package cake - * @subpackage cake.app.webroot + * @package app.webroot * @since CakePHP(tm) v 1.2.0.4433 - * @license http://www.opensource.org/licenses/opengroup.php The Open Group Test Suite License + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ set_time_limit(0); ini_set('display_errors', 1); @@ -45,13 +44,16 @@ ini_set('display_errors', 1); if (!defined('APP_DIR')) { define('APP_DIR', basename(dirname(dirname(__FILE__)))); } + /** - * The absolute path to the "cake" directory, WITHOUT a trailing DS. + * The absolute path to the "Cake" directory, WITHOUT a trailing DS. * + * For ease of development CakePHP uses PHP's include_path. If you + * need to cannot modify your include_path, you can set this path. + * + * Leaving this constant undefined will result in it being defined in Cake/bootstrap.php */ - if (!defined('CAKE_CORE_INCLUDE_PATH')) { - define('CAKE_CORE_INCLUDE_PATH', ROOT); - } + //define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'lib'); /** * Editing below this line should not be necessary. @@ -64,31 +66,27 @@ if (!defined('WEBROOT_DIR')) { if (!defined('WWW_ROOT')) { define('WWW_ROOT', dirname(__FILE__) . DS); } -if (!defined('CORE_PATH')) { - if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) { - define('APP_PATH', null); - define('CORE_PATH', null); - } else { - define('APP_PATH', ROOT . DS . APP_DIR . DS); - define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); + +if (!defined('CAKE_CORE_INCLUDE_PATH')) { + if (function_exists('ini_set')) { + ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path')); + } + if (!include('Cake' . DS . 'bootstrap.php')) { + $failed = true; + } +} else { + if (!include(CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php')) { + $failed = true; } } -if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) { +if (!empty($failed)) { trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); } -$corePath = App::core('cake'); -if (isset($corePath[0])) { - define('TEST_CAKE_CORE_INCLUDE_PATH', rtrim($corePath[0], DS) . DS); -} else { - define('TEST_CAKE_CORE_INCLUDE_PATH', CAKE_CORE_INCLUDE_PATH); -} - if (Configure::read('debug') < 1) { - die(__('Debug setting does not allow access to this url.', true)); + die(__d('cake_dev', 'Debug setting does not allow access to this url.')); } -require_once CAKE_TESTS_LIB . 'cake_test_suite_dispatcher.php'; +require_once CAKE . 'TestSuite' . DS . 'CakeTestSuiteDispatcher.php'; -$Dispatcher = new CakeTestSuiteDispatcher(); -$Dispatcher->dispatch(); +CakeTestSuiteDispatcher::run();