commit 217fa94867e5da638bf153c655566eca1bd66440 Author: Deon George Date: Tue Jul 19 15:57:37 2011 +1000 HAlMon Initial Application diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f69a7d4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "includes/kohana"] + path = includes/kohana + url = ssh://10.1.3.20/afs/local/git/lnkohana +[submodule "modules/lnApp"] + path = modules/lnApp + url = ssh://10.1.3.20/afs/local/git/lnapp diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..dfdb9a6 --- /dev/null +++ b/.htaccess @@ -0,0 +1,21 @@ +# Turn on URL rewriting +RewriteEngine On + +# Installation directory +RewriteBase / + +# Protect hidden files from being viewed + + Order Deny,Allow + Deny From All + + +# Protect application and system files from being viewed +RewriteRule ^(?:application|modules|includes/kohana)\b.* index.php/$0 [L] + +# Allow any files or directories that exist to be displayed directly +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d + +# Rewrite all other URLs to kh.php/URL +RewriteRule .* index.php/$0 [PT] diff --git a/application/bootstrap.php b/application/bootstrap.php new file mode 100644 index 0000000..9f78fdb --- /dev/null +++ b/application/bootstrap.php @@ -0,0 +1,139 @@ +" + */ +if (isset($_SERVER['KOHANA_ENV'])) +{ + Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV'])); +} + +/** + * Initialize Kohana, setting the default options. + * + * The following options are available: + * + * - string base_url path, and optionally domain, of your application NULL + * - string index_file name of your index file, usually "index.php" index.php + * - string charset internal character set used for input and output utf-8 + * - string cache_dir set the internal cache directory APPPATH/cache + * - boolean errors enable or disable error handling TRUE + * - boolean profile enable or disable internal profiling TRUE + * - boolean caching enable or disable internal caching FALSE + */ +Kohana::init(array( + 'base_url' => '', + 'index_file' => '', + 'caching' => TRUE, + 'cache_dir' => '/dev/shm/halmon', +)); + +/** + * Attach the file write to logging. Multiple writers are supported. + */ +Kohana::$log->attach(new Log_File(APPPATH.'logs')); + +/** + * Attach a file reader to config. Multiple readers are supported. + */ +Kohana::$config->attach(new Config_File); + +/** + * Enable modules. Modules are referenced by a relative or absolute path. + */ +Kohana::modules(array( + 'auth' => SMDPATH.'auth', // Basic authentication + 'cache' => SMDPATH.'cache', // Caching with multiple backends + // 'codebench' => SMDPATH.'codebench', // Benchmarking tool + 'database' => SMDPATH.'database', // Database access + // 'image' => SMDPATH.'image', // Image manipulation + 'orm' => SMDPATH.'orm', // Object Relationship Mapping + // 'unittest' => SMDPATH.'unittest', // Unit testing + 'userguide' => SMDPATH.'userguide', // User guide and API documentation + 'email' => SMDPATH.'email', // Email Module + )); + +/** + * Enable specalised interfaces + */ +Route::set('sections', '/(/(/(/)))', + array( + 'directory' => '('.implode('|',Kohana::config('config.method_directory')).')' + )); + +// Static file serving (CSS, JS, images) +Route::set('default/media', 'media(/)', array('file' => '.+')) + ->defaults(array( + 'controller' => 'welcome', + 'action' => 'media', + 'file' => NULL, + )); + +/** + * Set the routes. Each route must have a minimum of a name, a URI and a set of + * defaults for the URI. + */ +Route::set('default', '((/(/)))', array('id' => '[a-zA-Z0-9_.-]+')) + ->defaults(array( + 'controller' => 'welcome', + 'action' => 'index', + )); +?> diff --git a/application/classes/contactid.php b/application/classes/contactid.php new file mode 100644 index 0000000..a4f0095 --- /dev/null +++ b/application/classes/contactid.php @@ -0,0 +1,100 @@ +check($id)) + throw new Kohana_Exception('ContactID event :event is not valid',array(':event'=>$id)); + + // Process event trigger + $this->id = $id; + $this->acct = substr($id,0,4); + $this->sub = substr($id,4,2); + $this->qualifier = substr($id,6,1); + $this->type = substr($id,7,3); + $this->area = substr($id,10,2); + $this->info = substr($id,12,3); + $this->checksum = substr($id,15,1); + $this->datetime = $dt; + + // Check the event is from a valid account + // @todo This shouldnt stop program execution, rather it should generate an email to an admin + $ao = ORM::factory('account',array('alarmphone'=>$cf,'siteid'=>$this->acct)); + if (! $ao->loaded()) + throw new Kohana_Exception('Event from unknown account :id is not valid',array(':id'=>$ao->id)); + + $eo = ORM::factory('event'); + $eo->account_id = $ao->id; + $eo->alarmevent = $id; + $eo->datetime = $dt; + $eo->save(); + + // @todo This shouldnt stop program execution, rather it should generate an email to an admin + if (! $eo->saved()) + throw new Kohana_Exception('ContactID event :event is not valid',array(':event'=>$id)); + + return $this; + } + + public function __get($k) { + if (isset($this->{$k})) + return $this->{$k}; + else + throw new Kohana_Exception('Unknown property :property',array(':property'=>$k));; + } + + // Check that this event is valid + private function check($id) { + $checksum_map = array( + '0'=>10, + '1'=>1, + '2'=>2, + '3'=>3, + '4'=>4, + '5'=>5, + '6'=>6, + '7'=>7, + '8'=>8, + '9'=>9, + '*'=>11, + '#'=>12, + 'A'=>13, + 'B'=>14, + 'C'=>15, + ); + + if (strlen($id) != 16) + throw new Kohana_Exception('ContactID event :event has an invalid length :length',array(':event'=>$id,':length'=>strlen($id))); + + // Our checksum should be mod15 + $c = $t = 0; + while ($c diff --git a/application/classes/controller/task/event.php b/application/classes/controller/task/event.php new file mode 100644 index 0000000..f5dd3a6 --- /dev/null +++ b/application/classes/controller/task/event.php @@ -0,0 +1,113 @@ +event_file(file_get_contents($eventfile))) { + // Delete the event file, so that it isnt processed again + if (Kohana::config('config.event_file_keep')) + rename($eventfile,Kohana::config('config.event_dir').'/processed-'.$f); + else + unlink($eventfile); + + // Trigger + $eo = new Events($events); + $eo->trigger(); + + } else + rename($eventfile,Kohana::config('config.event_dir').'/bad-'.$f); + } + } + } + + private function event_file($file) { + $meta = $event = FALSE; + $et = $cf = $cn = $dt = NULL; + $good = TRUE; + $events = array(); + + foreach (explode("\n",$file) as $line) { + // Ignore out blank lines + if (! trim($line)) + continue; + + if (preg_match('/^\[metadata\]/',$line)) { + $meta = TRUE; + $event = FALSE; + + continue; + } elseif (preg_match('/^\[events\]/',$line)) { + $meta = FALSE; + $event = TRUE; + + continue; + } + + if ($meta) { + list ($k,$v) = explode('=',$line,2); + + switch ($k) { + case 'PROTOCOL': + switch ($v) { + case 'ADEMCO_CONTACT_ID': + $et = 'ContactID'; + + break; + + default: + throw new Kohana_Exception('Unknown protocol :protocol',array(':protocol'=>$v)); + } + + break; + + case 'CALLINGFROM': + $cf = $v; + break; + + case 'CALLERNAME': + $cn = $v; + break; + + case 'TIMESTAMP': + $dt = $v; + break; + + default: + printf('Extra data in event file - meta section (%s)',$line); + } + + } elseif ($event) { + if (! $et) + throw new Kohana_Exception('Event data without any protocol'); + + $eo = new $et; + if (! $eo->event($line,$dt,$cf,$cn)) + $good = FALSE; + else + array_push($events,$eo); + } + } + + return $events; + } +} +?> diff --git a/application/classes/events.php b/application/classes/events.php new file mode 100644 index 0000000..4a9dcbb --- /dev/null +++ b/application/classes/events.php @@ -0,0 +1,74 @@ +acct)) + $this->acct = $event->acct; + + elseif ($this->acct != $event->acct) + throw new Kohana_Exception('Events are for multiple accounts, unable to handle this.'); + } + + // Save our events + $this->events = $events; + } + + public function trigger() { + $ao = ORM::factory('account',array('siteid'=>$this->acct)); + + if (! $ao->loaded()) + throw new Kohana_Exception('No site configuration for :siteid?',array(':siteid'=>$this->acct)); + + if (! $ao->model->model) + throw new Kohana_Exception('No configured Alarm Model for Alarm ID :id',array(':id'=>$ao->model->id)); + + $ac = sprintf('Panel_%s',$ao->model->model); + if (! class_exists($ac)) + throw new Kohana_Exception('Unable to handle events for Alarm :model',array(':model'=>$ao->model->model)); + + $panel = new $ac($this->events); + + foreach ($panel->trigger() as $event) { + $to = $ao->trigger->trigger($event['alarm']); + + if (! $to->loaded()) + continue; + + // We need to email this event + if ($to->email) { + $e = Email::connect(); + $sm = Swift_Message::newInstance(); + $sm->setSubject('Notice from your alarm'); + $sm->setBody(sprintf('Alarm: %s. Received: %s',$event['desc'],date('d-m-Y h:i:s',$event['date'])),'text/plain'); + $sm->setTo($to->contact->email); + $sm->setFrom('noreply@leenooks.net'); + $e->send($sm); + } + + // We need to sms this event + if ($to->sms) { + printf('Alarm: %s, SMS: %s, Email: %s, To: %s (%s)', + $alo->description, + $to->sms, + $to->email, + $to->contact->email,$to->contact->phone); + echo "\n"; + } + } + } +} +?> diff --git a/application/classes/model/account.php b/application/classes/model/account.php new file mode 100644 index 0000000..4facfe3 --- /dev/null +++ b/application/classes/model/account.php @@ -0,0 +1,20 @@ +array('foreign_key'=>'id'), + ); + protected $_has_many = array( + 'trigger'=>array('far_key'=>'id'), + ); +} +?> diff --git a/application/classes/model/alarm.php b/application/classes/model/alarm.php new file mode 100644 index 0000000..44aa6a2 --- /dev/null +++ b/application/classes/model/alarm.php @@ -0,0 +1,14 @@ + diff --git a/application/classes/model/contact.php b/application/classes/model/contact.php new file mode 100644 index 0000000..8a9eb41 --- /dev/null +++ b/application/classes/model/contact.php @@ -0,0 +1,14 @@ + diff --git a/application/classes/model/event.php b/application/classes/model/event.php new file mode 100644 index 0000000..81d31d4 --- /dev/null +++ b/application/classes/model/event.php @@ -0,0 +1,14 @@ + diff --git a/application/classes/model/model.php b/application/classes/model/model.php new file mode 100644 index 0000000..731fce7 --- /dev/null +++ b/application/classes/model/model.php @@ -0,0 +1,17 @@ +array('far_key'=>'id'), + ); +} +?> diff --git a/application/classes/model/trigger.php b/application/classes/model/trigger.php new file mode 100644 index 0000000..4eff054 --- /dev/null +++ b/application/classes/model/trigger.php @@ -0,0 +1,21 @@ +array('foreign_key'=>'id'), + ); + + public function trigger($code) { + return $this->where('alarm','=',$code)->or_where('alarm','=',null)->find(); + } +} +?> diff --git a/application/classes/orm.php b/application/classes/orm.php new file mode 100644 index 0000000..e69c2f1 --- /dev/null +++ b/application/classes/orm.php @@ -0,0 +1,69 @@ +check()); + + // Always build a new validation object + $this->_validation(); + + $array = $this->_validation; + + if (($this->_valid = $array->check()) === FALSE OR $extra_errors) + { + return FALSE; + } + + return $this; + } + + /** + * Format fields for display purposes + * + * @param string column name + * @return mixed + */ + private function _format() { + foreach ($this->_display_filters as $column => $formats) + $this->_object_formated[$column] = $this->run_filter($column,$this->__get($column),array($column=>$formats)); + + $this->_formated = TRUE; + } + + /** + * Return a formated columns, as per the model definition + */ + public function display($column) { + // Trigger a load of the record. + $value = $this->__get($column); + + // If some of our fields need to be formated for display purposes. + if ($this->_loaded AND ! $this->_formated AND $this->_display_filters) + $this->_format(); + + if (isset($this->_object_formated[$column])) + return $this->_object_formated[$column]; + else + return HTML::nbsp($value); + } +} +?> diff --git a/application/classes/panel.php b/application/classes/panel.php new file mode 100644 index 0000000..d98749c --- /dev/null +++ b/application/classes/panel.php @@ -0,0 +1,21 @@ +events = $events; + } + + abstract public function trigger(); +} +?> diff --git a/application/classes/panel/nessd8.php b/application/classes/panel/nessd8.php new file mode 100644 index 0000000..118e63c --- /dev/null +++ b/application/classes/panel/nessd8.php @@ -0,0 +1,88 @@ +events as $eo) { + $i = count($return); + + $return[$i]['date'] = $eo->datetime; + $return[$i]['alarm'] = $eo->type; + + if ($eo->sub != 18) { + $return[$i]['desc'] = sprintf('Unknown Alarm: %s',$eo->id); + continue; + } + + switch ($eo->type) { + // Alarm + case 130: + switch ($eo->qualifier) { + case 1: $action = 'Alarm'; break; + case 3: $action = 'Reset'; break; + default: $action = 'Unknown'; + } + + $return[$i]['desc'] = sprintf('Alarm (%s) (Area %s) (%s)',$action,$eo->area,$eo->info); + break; + + // Exit Installer Mode + case 306: + switch ($eo->qualifier) { + case 1: $action = 'Exit Installer Mode'; break; + default: $action = 'Unknown'; + } + + $return[$i]['desc'] = sprintf('Auto Exclude (%s) (Area %s) (%s)',$action,$eo->area,$eo->info); + break; + + // Auto Exclude + case 380: + switch ($eo->qualifier) { + case 1: $action = 'Disarmed'; break; + case 3: $action = 'Armed'; break; + default: $action = 'Unknown'; + } + + $return[$i]['desc'] = sprintf('Auto Exclude (%s) (Area %s) (%s)',$action,$eo->area,$eo->info); + break; + + // Arm/Disarm Call + case 402: + switch ($eo->qualifier) { + case 1: $action = 'Disarmed'; break; + case 3: $action = 'Armed'; break; + default: $action = 'Unknown'; + } + + // @todo Change this to include the user name. + $user = substr($eo->info,1,2)==58 ? 'Short Cut' : substr($eo->info,1,2); + + // @todo Change this to include the area name. + $return[$i]['desc'] = sprintf('Panel %s (Area %s) (By %s)',$action,$eo->area,$user); + break; + + // Test Call + case 602: + $return[$i]['desc'] = 'Test Call'; + break; + + default: + $return[$i]['desc'] = sprintf('Unknown Alarm: %s',$eo->id); + } + } + + return $return; + } +} +?> diff --git a/application/config/config.php b/application/config/config.php new file mode 100644 index 0000000..80937b8 --- /dev/null +++ b/application/config/config.php @@ -0,0 +1,32 @@ + 'file', + 'date_format' => 'd-m-Y', + 'email_admin_only'=> array( + 'method'=>array('wurley@users.sf.net'=>'Deon George'), + ), + 'event_dir' => '/var/spool/asterisk/tmp/alarm/', + 'event_file_prefix'=>'event-', + 'event_file_keep' => TRUE, + 'method_directory'=> array( // Out method paths for the different functions + 'task', + ), + 'method_security' => TRUE, // Enables Method Security. Setting to false means any method can be run without authentication + 'site' => array( + ), + 'site_debug' => FALSE, + 'site_mode' => array( + ) +); +?> diff --git a/application/config/database.php b/application/config/database.php new file mode 100644 index 0000000..1da52f7 --- /dev/null +++ b/application/config/database.php @@ -0,0 +1,58 @@ + array + ( + 'type' => 'mysql', + 'connection' => array( + /** + * The following options are available for MySQL: + * + * string hostname server hostname, or socket + * string database database name + * string username database username + * string password database password + * boolean persistent use persistent connections? + * + * Ports and sockets may be appended to the hostname. + */ + 'hostname' => 'localhost', + 'database' => 'database', + 'username' => 'username', + 'password' => 'password', + 'persistent' => FALSE, + ), + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => FALSE, + 'profiling' => TRUE, + ), + 'alternate' => array( + 'type' => 'pdo', + 'connection' => array( + /** + * The following options are available for PDO: + * + * string dsn Data Source Name + * string username database username + * string password database password + * boolean persistent use persistent connections? + */ + 'dsn' => 'mysql:host=localhost;dbname=kohana', + 'username' => 'root', + 'password' => 'r00tdb', + 'persistent' => FALSE, + ), + /** + * The following extra options are available for PDO: + * + * string identifier set the escaping identifier + */ + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => FALSE, + 'profiling' => TRUE, + ), +); +?> diff --git a/application/views/template.php b/application/views/template.php new file mode 100644 index 0000000..e69de29 diff --git a/includes/kohana b/includes/kohana new file mode 160000 index 0000000..f96694b --- /dev/null +++ b/includes/kohana @@ -0,0 +1 @@ +Subproject commit f96694b18fdaa9bd6310debca71427068fe24046 diff --git a/index.php b/index.php new file mode 100644 index 0000000..96cbe16 --- /dev/null +++ b/index.php @@ -0,0 +1,121 @@ += 5.3, it is recommended to disable + * deprecated notices. Disable with: E_ALL & ~E_DEPRECATED + */ +error_reporting(E_ALL | E_STRICT); + +/** + * End of standard configuration! Changing any of the code below should only be + * attempted by those with a working knowledge of Kohana internals. + * + * @see http://kohanaframework.org/guide/using.configuration + */ + +// Set the full path to the docroot +define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR); + +// Make the application relative to the docroot, for symlink'd index.php +if ( ! is_dir($application) AND is_dir(DOCROOT.$application)) + $application = DOCROOT.$application; + +// Make the modules relative to the docroot, for symlink'd index.php +if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules)) + $modules = DOCROOT.$modules; + +// Make the system relative to the docroot, for symlink'd index.php +if ( ! is_dir($sysmodules) AND is_dir(DOCROOT.$sysmodules)) + $sysmodules = DOCROOT.$sysmodules; + +// Make the system relative to the docroot, for symlink'd index.php +if ( ! is_dir($system) AND is_dir(DOCROOT.$system)) + $system = DOCROOT.$system; + +// Define the absolute paths for configured directories +define('APPPATH', realpath($application).DIRECTORY_SEPARATOR); +define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR); +define('SMDPATH', realpath($sysmodules).DIRECTORY_SEPARATOR); +define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR); + +// Clean up the configuration vars +unset($application, $modules, $sysmodules, $system); + +if (file_exists('install'.EXT)) +{ + // Load the installation check + return include 'install'.EXT; +} + +/** + * Define the start time of the application, used for profiling. + */ +if ( ! defined('KOHANA_START_TIME')) +{ + define('KOHANA_START_TIME', microtime(TRUE)); +} + +/** + * Define the memory usage at the start of the application, used for profiling. + */ +if ( ! defined('KOHANA_START_MEMORY')) +{ + define('KOHANA_START_MEMORY', memory_get_usage()); +} + +// Bootstrap the application +require APPPATH.'bootstrap'.EXT; + +/** + * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. + * If no source is specified, the URI will be automatically detected. + */ +echo Request::factory() + ->execute() + ->send_headers() + ->body(); diff --git a/modules/lnApp b/modules/lnApp new file mode 160000 index 0000000..9e191ab --- /dev/null +++ b/modules/lnApp @@ -0,0 +1 @@ +Subproject commit 9e191ab0be69f296ae3cd2f079aafa80210f6928