commit 50fd088dca681f63321aeb795b6598b32da0562f Author: Pepijn Over Date: Tue Jan 7 20:24:48 2014 +0100 Initial import of phpservermon v2.0.0 diff --git a/classes/mod/modConfig.class.php b/classes/mod/modConfig.class.php new file mode 100644 index 00000000..1f962b83 --- /dev/null +++ b/classes/mod/modConfig.class.php @@ -0,0 +1,153 @@ +. + */ + +class modConfig extends modCore { + + function __construct() { + parent::__construct(); + + if(!empty($_POST)) { + $this->executeSave(); + } + } + + public function executeSave() { + // save new config + $clean = array( + 'language' => $_POST['language'], + 'show_update' => (isset($_POST['show_update'])) ? '1' : '0', + 'email_status' => (isset($_POST['email_status'])) ? '1' : '0', + 'email_from_name' => $_POST['email_from_name'], + 'email_from_email' => $_POST['email_from_email'], + 'sms_status' => (isset($_POST['sms_status'])) ? '1' : '0', + 'sms_gateway' => $_POST['sms_gateway'], + 'sms_gateway_username' => $_POST['sms_gateway_username'], + 'sms_gateway_password' => $_POST['sms_gateway_password'], + 'sms_from' => $_POST['sms_from'], + 'alert_type' => $_POST['alert_type'], + 'log_status' => (isset($_POST['log_status'])) ? '1' : '0', + 'log_email' => (isset($_POST['log_email'])) ? '1' : '0', + 'log_sms' => (isset($_POST['log_sms'])) ? '1' : '0', + ); + + // save all values to the database + foreach($clean as $key => $value) { + $this->db->save( + SM_DB_PREFIX . 'config', + array('value' => $value), + array('key' => $key) + ); + } + + $this->message = sm_get_lang('config', 'updated'); + } + + public function createHTML() { + $this->tpl_id = 'config'; + $this->tpl->newTemplate($this->tpl_id, 'config.tpl.html'); + + $this->createHTMLUpdate(); + $this->createHTMLLabels(); + + $this->populateFields(); + + return parent::createHTML(); + } + + public function createHTMLUpdate() { + // get latest version number + + } + + public function populateFields() { + $config_db = $this->db->select( + SM_DB_PREFIX . 'config', + null, + array('key', 'value') + ); + + $config = array(); + foreach($config_db as $entry) { + $config[$entry['key']] = $entry['value']; + } + + $this->tpl->addTemplateData( + $this->tpl_id, + array( + 'language_selected_' . $config['language'] => 'selected="selected"', + 'email_status_checked' => ($config['email_status'] == '1') ? 'checked="checked"' : '', + 'email_from_name' => $config['email_from_name'], + 'email_from_email' => $config['email_from_email'], + 'sms_status_checked' => ($config['sms_status'] == '1') ? 'checked="checked"' : '', + 'sms_selected_' . $config['sms_gateway'] => 'selected="selected"', + 'sms_gateway_username' => $config['sms_gateway_username'], + 'sms_gateway_password' => $config['sms_gateway_password'], + 'sms_from' => $config['sms_from'], + 'alert_type_selected_' . $config['alert_type'] => 'selected="selected"', + 'log_status_checked' => ($config['log_status'] == '1') ? 'checked="checked"' : '', + 'log_email_checked' => ($config['log_email'] == '1') ? 'checked="checked"' : '', + 'log_sms_checked' => ($config['log_sms'] == '1') ? 'checked="checked"' : '', + 'show_update_checked' => ($config['show_update'] == '1') ? 'checked="checked"' : '', + ) + ); + } + + public function createHTMLLabels() { + $this->tpl->addTemplateData( + $this->tpl_id, + array( + 'label_settings_email' => sm_get_lang('config', 'settings_email'), + 'label_settings_sms' => sm_get_lang('config', 'settings_sms'), + 'label_settings_notification' => sm_get_lang('config', 'settings_notification'), + 'label_settings_log' => sm_get_lang('config', 'settings_log'), + 'label_general' => sm_get_lang('config', 'general'), + 'label_language' => sm_get_lang('config', 'language'), + 'label_language_english' => sm_get_lang('config', 'english'), + 'label_language_dutch' => sm_get_lang('config', 'dutch'), + 'label_show_update' => sm_get_lang('config', 'show_update'), + 'label_email_status' => sm_get_lang('config', 'email_status'), + 'label_email_from_email' => sm_get_lang('config', 'email_from_email'), + 'label_email_from_name' => sm_get_lang('config', 'email_from_name'), + 'label_sms_status' => sm_get_lang('config', 'sms_status'), + 'label_sms_gateway' => sm_get_lang('config', 'sms_gateway'), + 'label_sms_gateway_mollie' => sm_get_lang('config', 'sms_gateway_mollie'), + 'label_sms_gateway_spryng' => sm_get_lang('config', 'sms_gateway_spryng'), + 'label_sms_gateway_inetworx' => sm_get_lang('config', 'sms_gateway_inetworx'), + 'label_sms_gateway_username' => sm_get_lang('config', 'sms_gateway_username'), + 'label_sms_gateway_password' => sm_get_lang('config', 'sms_gateway_password'), + 'label_sms_from' => sm_get_lang('config', 'sms_from'), + 'label_alert_type' => sm_get_lang('config', 'alert_type'), + 'label_alert_type_status' => sm_get_lang('config', 'alert_type_status'), + 'label_alert_type_offline' => sm_get_lang('config', 'alert_type_offline'), + 'label_alert_type_always' => sm_get_lang('config', 'alert_type_always'), + 'label_log_status' => sm_get_lang('config', 'log_status'), + 'label_log_email' => sm_get_lang('config', 'log_email'), + 'label_log_sms' => sm_get_lang('config', 'log_sms'), + 'message' => ($this->message == '') ? ' ' : $this->message, + ) + ); + } +} + +?> \ No newline at end of file diff --git a/classes/mod/modCore.class.php b/classes/mod/modCore.class.php new file mode 100644 index 00000000..cc225638 --- /dev/null +++ b/classes/mod/modCore.class.php @@ -0,0 +1,45 @@ +. + */ + +abstract class modCore { + public $db; + public $message; + public $mode; + public $tpl; + public $tpl_id; + + function __construct() { + global $db, $tpl; + + $this->db = ($db) ? $db : new smDatabase(); + $this->tpl = ($tpl) ? $tpl : new smTemplate(); + } + + public function createHTML() { + $html = $this->tpl->getTemplate($this->tpl_id); + return $html; + } +} + +?> \ No newline at end of file diff --git a/classes/mod/modLog.class.php b/classes/mod/modLog.class.php new file mode 100644 index 00000000..fc294e20 --- /dev/null +++ b/classes/mod/modLog.class.php @@ -0,0 +1,137 @@ +. + */ + +class modLog extends modCore { + + function __construct() { + parent::__construct(); + } + + public function createHTML() { + $this->createHTMLList(); + + $this->tpl->addCSS('tabs.css', 'main'); + + $this->tpl->addTemplateData( + $this->tpl_id, + array( + 'label_status' => sm_get_lang('log', 'status'), + 'label_email' => sm_get_lang('log', 'email'), + 'label_sms' => sm_get_lang('log', 'sms'), + 'label_title' => sm_get_lang('log', 'title'), + 'label_server' => sm_get_lang('servers', 'server'), + 'label_type' => sm_get_lang('log', 'type'), + 'label_message' => sm_get_lang('system', 'message'), + 'label_date' => sm_get_lang('system', 'date'), + 'label_users' => ucfirst(sm_get_lang('system', 'users')), + ) + ); + + return parent::createHTML(); + } + + public function createHTMLList() { + $this->tpl_id = 'log_list'; + + $this->tpl->newTemplate($this->tpl_id, 'log.tpl.html'); + + $entries = array(); + $entries['status'] = $this->getEntries('status'); + $entries['email'] = $this->getEntries('email'); + $entries['sms'] = $this->getEntries('sms'); + + // get users + $users = $this->db->select(SM_DB_PREFIX.'users', null, array('user_id','name')); + + $users_labels = array(); + foreach ($users as $user) { + $users_labels[$user['user_id']] = $user['name']; + } + + foreach($entries as $key => $records) { + $log_count = count($records); + + for ($x = 0; $x < $log_count; $x++) { + $records[$x]['class'] = ($x & 1) ? 'odd' : 'even'; + $records[$x]['users'] = ''; + $records[$x]['server'] = $records[$x]['label'] . ' (' . $records[$x]['label_adv'] . ')'; + + // fix up user list + if($records[$x]['user_id'] == '') continue; + + $users = explode(',', $records[$x]['user_id']); + foreach($users as $user_id) { + if((int) $user_id == 0 || !isset($users_labels[$user_id])) continue; + + $records[$x]['users'] .= '
'.$users_labels[$user_id]; + } + } + + // add entries to template + $this->tpl->newTemplate('log_entries', 'log.tpl.html'); + $this->tpl->addTemplateDataRepeat('log_entries', 'entries', $records); + $this->tpl->addTemplateData( + 'log_entries', + array( + 'logtitle' => $key, + ) + ); + $this->tpl->addTemplateData( + $this->tpl_id, + array( + 'content_' . $key => $this->tpl->getTemplate('log_entries'), + ) + ); + } + + } + + public function getEntries($type) { + $entries = $this->db->query( + 'SELECT '. + '`servers`.`label`, '. + 'CONCAT_WS('. + '\':\','. + '`servers`.`ip`, '. + '`servers`.`port`'. + ') AS `label_adv`, '. + '`log`.`type`, '. + '`log`.`message`, '. + 'DATE_FORMAT('. + '`log`.`datetime`, '. + '\'%H:%i:%s %d-%m-%y\''. + ') AS `datetime_format`, '. + '`user_id` '. + 'FROM `'.SM_DB_PREFIX.'log` AS `log` '. + 'JOIN `'.SM_DB_PREFIX.'servers` AS `servers` ON (`servers`.`server_id`=`log`.`server_id`) '. + 'WHERE `log`.`type`=\''.$type.'\' '. + 'ORDER BY `datetime` DESC '. + 'LIMIT 0,20' + ); + return $entries; + } + +} + +?> \ No newline at end of file diff --git a/classes/mod/modServers.class.php b/classes/mod/modServers.class.php new file mode 100644 index 00000000..20ee7e04 --- /dev/null +++ b/classes/mod/modServers.class.php @@ -0,0 +1,219 @@ +. + */ + +class modServers extends modCore { + + function __construct() { + parent::__construct(); + + // check mode + if (isset($_GET['edit']) && is_numeric($_GET['edit'])) { + // edit mode or insert mode + $this->mode = 'update'; + } else { + $this->mode = 'list'; + + if(!empty($_POST)) { + $this->executeSave(); + } + if(isset($_GET['delete']) && is_numeric($_GET['delete'])) { + $this->executeDelete(); + } + } + } + + public function createHTML() { + switch($this->mode) { + case 'list': + $this->createHTMLList(); + break; + case 'update': + $this->createHTMLUpdate(); + break; + } + + // add labels + $this->tpl->addTemplateData( + $this->tpl_id, + array( + 'label_label' => sm_get_lang('servers', 'label'), + 'label_domain' => sm_get_lang('servers', 'domain'), + 'label_port' => sm_get_lang('servers', 'port'), + 'label_type' => sm_get_lang('servers', 'type'), + 'label_last_check' => sm_get_lang('servers', 'last_check'), + 'label_rtime' => sm_get_lang('servers', 'rtime'), + 'label_last_online' => sm_get_lang('servers', 'last_online'), + 'label_monitoring' => sm_get_lang('servers', 'monitoring'), + 'label_send_email' => sm_get_lang('servers', 'send_email'), + 'label_send_sms' => sm_get_lang('servers', 'send_sms'), + 'label_action' => sm_get_lang('system', 'action'), + 'label_save' => sm_get_lang('system', 'save'), + 'label_edit' => sm_get_lang('system', 'edit') . ' ' . sm_get_lang('servers', 'server'), + 'label_delete' => sm_get_lang('system', 'delete') . ' ' . sm_get_lang('servers', 'server'), + 'label_yes' => sm_get_lang('system', 'yes'), + 'label_no' => sm_get_lang('system', 'no'), + 'label_add_new' => sm_get_lang('system', 'add_new'), + 'message' => ($this->message == '') ? ' ' : $this->message, + ) + ); + + return parent::createHTML(); + } + + + public function createHTMLUpdate() { + $this->tpl_id = 'servers_update'; + $this->tpl->newTemplate($this->tpl_id, 'servers.tpl.html'); + + $server_id = $_GET['edit']; + + $tpl_data = array(); + + switch((int) $server_id) { + case 0: + // insert mode + $tpl_data['titlemode'] = sm_get_lang('system', 'insert'); + $tpl_data['edit_server_id'] = '0'; + break; + default: + // edit mode + + // get server entry + $edit_server = $this->db->selectRow( + SM_DB_PREFIX.'servers', + array('server_id' => $server_id) + ); + if (empty($edit_server)) { + $this->message = 'Invalid server id'; + return $this->createHTMLList(); + } + + $tpl_data = array_merge($tpl_data, array( + 'titlemode' => sm_get_lang('system', 'edit') . ' ' . $edit_server['label'], + 'edit_server_id' => $edit_server['server_id'], + 'edit_value_label' => $edit_server['label'], + 'edit_value_ip' => $edit_server['ip'], + 'edit_value_port' => $edit_server['port'], + 'edit_type_selected_' . $edit_server['type'] => 'selected="selected"', + 'edit_active_selected_' . $edit_server['active'] => 'selected="selected"', + 'edit_email_selected_' . $edit_server['email'] => 'selected="selected"', + 'edit_sms_selected_' . $edit_server['sms'] => 'selected="selected"', + )); + + break; + } + + $this->tpl->addTemplateData( + $this->tpl_id, + $tpl_data + ); + } + + public function createHTMLList() { + $this->tpl_id = 'servers_list'; + $this->tpl->newTemplate($this->tpl_id, 'servers.tpl.html'); + + // get servers from database + $servers = $this->db->query( + 'SELECT '. + '`server_id`, '. + '`ip`, '. + '`port`, '. + '`type`, '. + '`label`, '. + '`status`, '. + '`error`, '. + '`rtime`, '. + 'IF('. + '`last_check`=\'0000-00-00 00:00:00\', '. + '\'never\', '. + 'DATE_FORMAT(`last_check`, \'%d-%m-%y %H:%i\') '. + ') AS `last_check`, '. + 'IF('. + '`last_online`=\'0000-00-00 00:00:00\', '. + '\'never\', '. + 'DATE_FORMAT(`last_online`, \'%d-%m-%y %H:%i\') '. + ') AS `last_online`, '. + '`active`, '. + '`email`, '. + '`sms` '. + 'FROM `'.SM_DB_PREFIX.'servers` '. + 'ORDER BY `type` ASC, `label` ASC' + ); + + $server_count = count($servers); + + for ($x = 0; $x < $server_count; $x++) { + $servers[$x]['class'] = ($x & 1) ? 'odd' : 'even'; + $servers[$x]['rtime'] = round((float) $servers[$x]['rtime'], 4); + } + // add servers to template + $this->tpl->addTemplateDataRepeat($this->tpl_id, 'servers', $servers); + + } + + public function executeSave() { + // check for add/edit mode + if (isset($_POST['label']) && isset($_POST['ip']) && isset($_POST['port'])) { + $clean = array( + 'label' => $_POST['label'], + 'ip' => $_POST['ip'], + 'port' => $_POST['port'], + 'type' => $_POST['type'], + 'active' => $_POST['active'], + 'email' => $_POST['email'], + 'sms' => $_POST['sms'], + ); + + // check for edit or add + if ((int) $_POST['server_id'] > 0) { + // edit + $this->db->save( + SM_DB_PREFIX.'servers', + $clean, + array('server_id' => $_POST['server_id']) + ); + $this->message = sm_get_lang('servers', 'updated'); + } else { + // add + $clean['status'] = 'on'; + $this->db->save(SM_DB_PREFIX.'servers', $clean); + $this->message = sm_get_lang('servers', 'inserted'); + } + } + } + + public function executeDelete() { + // do delete + $this->db->delete( + SM_DB_PREFIX . 'servers', + array( + 'server_id' => $_GET['delete'] + ) + ); + $this->message = sm_get_lang('system', 'deleted'); + } +} + +?> \ No newline at end of file diff --git a/classes/mod/modUsers.class.php b/classes/mod/modUsers.class.php new file mode 100644 index 00000000..e9494fee --- /dev/null +++ b/classes/mod/modUsers.class.php @@ -0,0 +1,225 @@ +. + */ + +class modUsers extends modCore { + public $servers; + + function __construct() { + parent::__construct(); + + // check mode + if (isset($_GET['edit']) && is_numeric($_GET['edit'])) { + // edit mode or insert mode + $this->mode = 'update'; + } else { + $this->mode = 'list'; + + if(!empty($_POST)) { + $this->executeSave(); + } + if(isset($_GET['delete']) && is_numeric($_GET['delete'])) { + $this->executeDelete(); + } + } + + $this->servers = $this->db->select(SM_DB_PREFIX.'servers', null, array('server_id', 'label')); + } + + public function createHTML() { + switch($this->mode) { + case 'list': + $this->createHTMLList(); + break; + case 'update': + $this->createHTMLUpdate(); + break; + } + + // add labels + $this->tpl->addTemplateData( + $this->tpl_id, + array( + 'label_users' => sm_get_lang('system', 'users'), + 'label_name' => sm_get_lang('users', 'name'), + 'label_mobile' => sm_get_lang('users', 'mobile'), + 'label_email' => sm_get_lang('users', 'email'), + 'label_servers' => sm_get_lang('system', 'servers'), + 'label_action' => sm_get_lang('system', 'action'), + 'label_save' => sm_get_lang('system', 'save'), + 'label_edit' => sm_get_lang('system', 'edit') . ' ' . sm_get_lang('users', 'user'), + 'label_delete' => sm_get_lang('system', 'delete') . ' ' . sm_get_lang('users', 'user'), + 'label_add_new' => sm_get_lang('system', 'add_new'), + 'message' => ($this->message == '') ? ' ' : $this->message, + ) + ); + + return parent::createHTML(); + } + + + public function createHTMLUpdate() { + $this->tpl_id = 'users_update'; + $this->tpl->newTemplate($this->tpl_id, 'users.tpl.html'); + + $user_id = $_GET['edit']; + + $tpl_data = array(); + $servers_count = count($this->servers); + + switch((int) $user_id) { + case 0: + // insert mode + $tpl_data['titlemode'] = sm_get_lang('system', 'insert'); + $tpl_data['edit_user_id'] = '0'; + + // add inactive class to all servers + for ($i = 0; $i < $servers_count; $i++) { + $this->servers[$i]['class'] = 'inactive'; + } + + break; + default: + // edit mode + + // get user entry + $edit_user = $this->db->selectRow( + SM_DB_PREFIX.'users', + array('user_id' => $user_id) + ); + if (empty($edit_user)) { + $this->message = 'Invalid user id'; + return $this->createHTMLList(); + } + + $tpl_data = array_merge($tpl_data, array( + 'titlemode' => sm_get_lang('system', 'edit') . ' ' . $edit_user['name'], + 'edit_user_id' => $edit_user['user_id'], + 'edit_value_name' => $edit_user['name'], + 'edit_value_mobile' => $edit_user['mobile'], + 'edit_value_email' => $edit_user['email'], + )); + + // select servers for this user + $user_servers = explode(',', $edit_user['server_id']); + + for ($h = 0; $h < $servers_count; $h++) { + if(in_array($this->servers[$h]['server_id'], $user_servers)) { + $this->servers[$h]['edit_checked'] = 'checked="checked"'; + $this->servers[$h]['class'] = 'active'; + } + } + + break; + } + + $this->tpl->addTemplateData( + $this->tpl_id, + $tpl_data + ); + // add servers to template for the edit form + $this->tpl->addTemplateDataRepeat('users_update', 'servers', $this->servers); + } + + public function createHTMLList() { + $this->tpl_id = 'users_list'; + $this->tpl->newTemplate($this->tpl_id, 'users.tpl.html'); + + // build label array for the next loop + $servers_labels = array(); + foreach ($this->servers as $server) { + $servers_labels[$server['server_id']] = $server['label']; + } + + // get users from database + $users = $this->db->select( + SM_DB_PREFIX.'users', + null, + null, + null, + array('name') + ); + + $user_count = count($users); + + for ($x = 0; $x < $user_count; $x++) { + $users[$x]['class'] = ($x & 1) ? 'odd' : 'even'; + + $users[$x]['emp_servers'] = ''; + + // fix server list + $user_servers = explode(',', $users[$x]['server_id']); + if (empty($user_servers)) continue; + + foreach ($user_servers as $server) { + if (!isset($servers_labels[$server])) continue; + $users[$x]['emp_servers'] .= $servers_labels[$server] . '
'; + } + $users[$x]['emp_servers'] = substr($users[$x]['emp_servers'], 0, -5); + } + // add servers to template + $this->tpl->addTemplateDataRepeat($this->tpl_id, 'users', $users); + + } + + public function executeSave() { + // check for add/edit mode + + if (isset($_POST['name']) && isset($_POST['mobile']) && isset($_POST['email'])) { + $clean = array( + 'name' => $_POST['name'], + 'mobile' => $_POST['mobile'], + 'email' => $_POST['email'], + 'server_id' => (isset($_POST['server_id'])) ? implode(',', $_POST['server_id']) : '' + ); + + // check for edit or add + if ((int) $_POST['user_id'] > 0) { + // edit + $this->db->save( + SM_DB_PREFIX.'users', + $clean, + array('user_id' => $_POST['user_id']) + ); + $this->message = sm_get_lang('users', 'updated'); + } else { + // add + $this->db->save(SM_DB_PREFIX.'users', $clean); + $this->message = sm_get_lang('users', 'inserted'); + } + } + } + + public function executeDelete() { + // do delete + $this->db->delete( + SM_DB_PREFIX . 'users', + array( + 'user_id' => $_GET['delete'] + ) + ); + $this->message = sm_get_lang('system', 'deleted'); + } +} + +?> \ No newline at end of file diff --git a/classes/phpmailer.class.php b/classes/phpmailer.class.php new file mode 100644 index 00000000..0da0e8c9 --- /dev/null +++ b/classes/phpmailer.class.php @@ -0,0 +1,1615 @@ + +// +// License: LGPL, see LICENSE +//////////////////////////////////////////////////// + +/** + * phpmailer - PHP email transport class + * @author Brent R. Matzelle + */ +class phpmailer +{ + ///////////////////////////////////////////////// + // PUBLIC VARIABLES + ///////////////////////////////////////////////// + + /** + * Email priority (1 = High, 3 = Normal, 5 = low). Default value is 3. + * @public + * @type int + */ + var $Priority = 3; + + /** + * Sets the CharSet of the message. Default value is "iso-8859-1". + * @public + * @type string + */ + var $CharSet = "iso-8859-1"; + + /** + * Sets the Content-type of the message. Default value is "text/plain". + * @public + * @type string + */ + var $ContentType = "text/plain"; + + /** + * Sets the Encoding of the message. Options for this are "8bit" (default), + * "7bit", "binary", "base64", and "quoted-printable". + * @public + * @type string + */ + var $Encoding = "8bit"; + + /** + * Holds the most recent mailer error message. Default value is "". + * @public + * @type string + */ + var $ErrorInfo = ""; + + /** + * Sets the From email address for the message. Default value is "root@localhost". + * @public + * @type string + */ + var $From = "root@localhost"; + + /** + * Sets the From name of the message. Default value is "Root User". + * @public + * @type string + */ + var $FromName = "Root User"; + + /** + * Sets the Sender email of the message. If not empty, will be sent via -f to sendmail + * or as 'MAIL FROM' in smtp mode. Default value is "". + * @public + * @type string + */ + var $Sender = ""; + + /** + * Sets the Subject of the message. Default value is "". + * @public + * @type string + */ + var $Subject = ""; + + /** + * Sets the Body of the message. This can be either an HTML or text body. + * If HTML then run IsHTML(true). Default value is "". + * @public + * @type string + */ + var $Body = ""; + + /** + * Sets the text-only body of the message. This automatically sets the + * email to multipart/alternative. This body can be read by mail + * clients that do not have HTML email capability such as mutt. Clients + * that can read HTML will view the normal Body. + * Default value is "". + * @public + * @type string + */ + var $AltBody = ""; + + /** + * Sets word wrapping on the body of the message to a given number of + * characters. Default value is 0 (off). + * @public + * @type int + */ + var $WordWrap = 0; + + /** + * Method to send mail: ("mail", "sendmail", or "smtp"). + * Default value is "mail". + * @public + * @type string + */ + var $Mailer = "mail"; + + /** + * Sets the path of the sendmail program. Default value is + * "/usr/sbin/sendmail". + * @public + * @type string + */ + var $Sendmail = "/usr/sbin/sendmail"; + + /** + * Turns Microsoft mail client headers on and off. Useful mostly + * for older clients. Default value is false (off). + * @public + * @type bool + */ + var $UseMSMailHeaders = false; + + /** + * Path to phpmailer plugins. This is now only useful if the SMTP class + * is in a different directory than the PHP include path. + * Default is empty (""). + * @public + * @type string + */ + var $PluginDir = ""; + + /** + * Holds phpmailer version. + * @public + * @type string + */ + var $Version = "1.54"; + + /** + * Sets the email address that a reading confirmation will be sent. Default value is "". + * @public + * @type string + */ + var $ConfirmReadingTo = ""; + + /** + * Sets the line endings of the message. Default is "\n"; + * @public + * @type string + */ + var $LE = "\n"; + + + ///////////////////////////////////////////////// + // SMTP VARIABLES + ///////////////////////////////////////////////// + + /** + * Sets the SMTP hosts. All hosts must be separated by a + * semicolon. You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.domain.com:25;smtp2.domain.com"). + * Hosts will be tried in order. + * Default value is "localhost". + * @public + * @type string + */ + var $Host = "localhost"; + + /** + * Sets the default SMTP server port. Default value is 25. + * @public + * @type int + */ + var $Port = 25; + + /** + * Sets the SMTP HELO of the message. + * Default value is "localhost.localdomain". + * @public + * @type string + */ + var $Helo = "localhost.localdomain"; + + /** + * Sets SMTP authentication. Utilizes the Username and Password variables. + * Default value is false (off). + * @public + * @type bool + */ + var $SMTPAuth = false; + + /** + * Sets SMTP username. Default value is "". + * @public + * @type string + */ + var $Username = ""; + + /** + * Sets SMTP password. Default value is "". + * @public + * @type string + */ + var $Password = ""; + + /** + * Sets the SMTP server timeout in seconds. This function will not + * work with the win32 version. Default value is 10. + * @public + * @type int + */ + var $Timeout = 10; + + /** + * Sets SMTP class debugging on or off. Default value is false (off). + * @public + * @type bool + */ + var $SMTPDebug = false; + + + ///////////////////////////////////////////////// + // PRIVATE VARIABLES + ///////////////////////////////////////////////// + + /** + * Holds all "To" addresses. + * @type array + */ + var $to = array(); + + /** + * Holds all "CC" addresses. + * @type array + */ + var $cc = array(); + + /** + * Holds all "BCC" addresses. + * @type array + */ + var $bcc = array(); + + /** + * Holds all "Reply-To" addresses. + * @type array + */ + var $ReplyTo = array(); + + /** + * Holds all string and binary attachments. + * @type array + */ + var $attachment = array(); + + /** + * Holds all custom headers. + * @type array + */ + var $CustomHeader = array(); + + /** + * Holds the type of the message. + * @type string + */ + var $message_type = ""; + + /** + * Holds the message boundaries. + * @type string array + */ + var $boundary = array(); + + ///////////////////////////////////////////////// + // VARIABLE METHODS + ///////////////////////////////////////////////// + + /** + * Sets message type to HTML. Returns void. + * @public + * @returns void + */ + function IsHTML($bool) { + if($bool == true) + $this->ContentType = "text/html"; + else + $this->ContentType = "text/plain"; + } + + /** + * Sets Mailer to send message using SMTP. + * Returns void. + * @public + * @returns void + */ + function IsSMTP() { + $this->Mailer = "smtp"; + } + + /** + * Sets Mailer to send message using PHP mail() function. + * Returns void. + * @public + * @returns void + */ + function IsMail() { + $this->Mailer = "mail"; + } + + /** + * Sets Mailer to send message using the $Sendmail program. + * Returns void. + * @public + * @returns void + */ + function IsSendmail() { + $this->Mailer = "sendmail"; + } + + /** + * Sets Mailer to send message using the qmail MTA. Returns void. + * @public + * @returns void + */ + function IsQmail() { + //$this->Sendmail = "/var/qmail/bin/qmail-inject"; + $this->Sendmail = "/var/qmail/bin/sendmail"; + $this->Mailer = "sendmail"; + } + + + ///////////////////////////////////////////////// + // RECIPIENT METHODS + ///////////////////////////////////////////////// + + /** + * Adds a "To" address. Returns void. + * @public + * @returns void + */ + function AddAddress($address, $name = "") { + $cur = count($this->to); + $this->to[$cur][0] = trim($address); + $this->to[$cur][1] = $name; + } + + /** + * Adds a "Cc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. This is a PHP bug that has been submitted + * on http://bugs.php.net. The *NIX version of PHP + * functions correctly. Returns void. + * @public + * @returns void + */ + function AddCC($address, $name = "") { + $cur = count($this->cc); + $this->cc[$cur][0] = trim($address); + $this->cc[$cur][1] = $name; + } + + /** + * Adds a "Bcc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. This is a PHP bug that has been submitted + * on http://bugs.php.net. The *NIX version of PHP + * functions correctly. + * Returns void. + * @public + * @returns void + */ + function AddBCC($address, $name = "") { + $cur = count($this->bcc); + $this->bcc[$cur][0] = trim($address); + $this->bcc[$cur][1] = $name; + } + + /** + * Adds a "Reply-to" address. Returns void. + * @public + * @returns void + */ + function AddReplyTo($address, $name = "") { + $cur = count($this->ReplyTo); + $this->ReplyTo[$cur][0] = trim($address); + $this->ReplyTo[$cur][1] = $name; + } + + + ///////////////////////////////////////////////// + // MAIL SENDING METHODS + ///////////////////////////////////////////////// + + /** + * Creates message and assigns Mailer. If the message is + * not sent successfully then it returns false. Use the ErrorInfo + * variable to view description of the error. Returns bool. + * @public + * @returns bool + */ + function Send() { + $header = ""; + $body = ""; + + if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) + { + $this->error_handler("You must provide at least one recipient email address"); + return false; + } + + // Set whether the message is multipart/alternative + if(!empty($this->AltBody)) + $this->ContentType = "multipart/alternative"; + + // Attach sender information & date + $header = $this->received(); + $header .= sprintf("Date: %s%s", $this->rfc_date(), $this->LE); + $header .= $this->create_header(); + + if(!$body = $this->create_body()) + return false; + + //echo "
".$header . $body . "
"; // debugging + + // Choose the mailer + if($this->Mailer == "sendmail") + { + if(!$this->sendmail_send($header, $body)) + return false; + } + elseif($this->Mailer == "mail") + { + if(!$this->mail_send($header, $body)) + return false; + } + elseif($this->Mailer == "smtp") + { + if(!$this->smtp_send($header, $body)) + return false; + } + else + { + $this->error_handler(sprintf("%s mailer is not supported", $this->Mailer)); + return false; + } + + return true; + } + + /** + * Sends mail message to an assigned queue directory. Has an optional + * sendTime argument. This is used when the user wants the + * message to be sent from the queue at a predetermined time. + * The data must be a valid timestamp like that returned from + * the time() or strtotime() functions. Returns false on failure + * or the message file name if success. + * @public + * @returns string + */ + function SendToQueue($queue_path, $send_time = 0) { + $message = array(); + $header = ""; + $body = ""; + + // If invalid or empty just set to the current time + if($send_time == 0) + $send_time = time(); + + if(!is_dir($queue_path)) + { + $this->error_handler("The supplied queue directory does not exist"); + return false; + } + + if((count($this->to) + count($this->cc) + count($this->bcc)) < 1) + { + $this->error_handler("You must provide at least one recipient email address"); + return false; + } + + // Set whether the message is multipart/alternative + if(!empty($this->AltBody)) + $this->ContentType = "multipart/alternative"; + + $header = $this->create_header(); + if(!$body = $this->create_body()) + return false; + + // Seed randomizer + mt_srand(time()); + $msg_id = md5(uniqid(mt_rand())); + + $fp = fopen($queue_path . $msg_id . ".pqm", "wb"); + if(!$fp) + { + $this->error_handler(sprintf("Could not write to %s directory", $queue_path)); + return false; + } + + $message[] = sprintf("----START PQM HEADER----%s", $this->LE); + $message[] = sprintf("SendTime: %s%s", $send_time, $this->LE); + $message[] = sprintf("Mailer: %s%s", $this->Mailer, $this->LE); + + // Choose the mailer + if($this->Mailer == "sendmail") + { + $message[] = sprintf("Sendmail: %s%s", $this->Sendmail, $this->LE); + $message[] = sprintf("Sender: %s%s", $this->Sender, $this->LE); + } + elseif($this->Mailer == "mail") + { + $message[] = sprintf("Sender: %s%s", $this->Sender, $this->LE); + $message[] = sprintf("Subject: %s%s", $this->Subject, $this->LE); + $message[] = sprintf("to: %s%s", $this->addr_list($this->to), $this->LE); + } + elseif($this->Mailer == "smtp") + { + $message[] = sprintf("Host: %s%s", $this->Host, $this->LE); + $message[] = sprintf("Port: %d%s", $this->Port, $this->LE); + $message[] = sprintf("Helo: %s%s", $this->Helo, $this->LE); + $message[] = sprintf("Timeout: %d%s", $this->Timeout, $this->LE); + + if($this->SMTPAuth) + $auth_no = 1; + else + $auth_no = 0; + $message[] = sprintf("SMTPAuth: %d%s", $auth_no, $this->LE); + $message[] = sprintf("Username: %s%s", $this->Username, $this->LE); + $message[] = sprintf("Password: %s%s", $this->Password, $this->LE); + $message[] = sprintf("From: %s%s", $this->From, $this->LE); + + $message[] = sprintf("to: %s%s", $this->addr_list($this->to), $this->LE); + $message[] = sprintf("cc: %s%s", $this->addr_list($this->cc), $this->LE); + $message[] = sprintf("bcc: %s%s", $this->addr_list($this->bcc), $this->LE); + } + else + { + $this->error_handler(sprintf("%s mailer is not supported", $this->Mailer)); + return false; + } + + $message[] = sprintf("----END PQM HEADER----%s", $this->LE); // end of pqm header + $message[] = $header; + $message[] = $body; + + fwrite($fp, join("", $message)); + + return ($msg_id . ".pqm"); + } + + /** + * Sends mail using the $Sendmail program. Returns bool. + * @private + * @returns bool + */ + function sendmail_send($header, $body) { + if ($this->Sender != "") + $sendmail = sprintf("%s -oi -f %s -t", $this->Sendmail, $this->Sender); + else + $sendmail = sprintf("%s -oi -t", $this->Sendmail); + + if(!@$mail = popen($sendmail, "w")) + { + $this->error_handler(sprintf("Could not execute %s", $this->Sendmail)); + return false; + } + + fputs($mail, $header); + fputs($mail, $body); + + $result = pclose($mail) >> 8 & 0xFF; + if($result != 0) + { + $this->error_handler(sprintf("Could not execute %s", $this->Sendmail)); + return false; + } + + return true; + } + + /** + * Sends mail using the PHP mail() function. Returns bool. + * @private + * @returns bool + */ + function mail_send($header, $body) { + //$to = substr($this->addr_append("To", $this->to), 4, -2); + + // Cannot add Bcc's to the $to + $to = $this->to[0][0]; // no extra comma + for($i = 1; $i < count($this->to); $i++) + $to .= sprintf(",%s", $this->to[$i][0]); + + if ($this->Sender != "" && PHP_VERSION >= "4.0") + { + $old_from = ini_get("sendmail_from"); + ini_set("sendmail_from", $this->Sender); + } + + if ($this->Sender != "" && PHP_VERSION >= "4.0.5") + { + // The fifth parameter to mail is only available in PHP >= 4.0.5 + $params = sprintf("-oi -f %s", $this->Sender); + $rt = @mail($to, $this->Subject, $body, $header, $params); + } + else + { + $rt = @mail($to, $this->Subject, $body, $header); + } + + if (isset($old_from)) + ini_set("sendmail_from", $old_from); + + if(!$rt) + { + $this->error_handler("Could not instantiate mail()"); + return false; + } + + return true; + } + + /** + * Sends mail via SMTP using PhpSMTP (Author: + * Chris Ryan). Returns bool. Returns false if there is a + * bad MAIL FROM, RCPT, or DATA input. + * @private + * @returns bool + */ + function smtp_send($header, $body) { + // Include SMTP class code, but not twice + include_once($this->PluginDir . "class.smtp.php"); + + $smtp = new SMTP; + + $smtp->do_debug = $this->SMTPDebug; + + // Try to connect to all SMTP servers + $hosts = explode(";", $this->Host); + $index = 0; + $connection = false; + $smtp_from = ""; + $bad_rcpt = array(); + $e = ""; + + // Retry while there is no connection + while($index < count($hosts) && $connection == false) + { + if(strstr($hosts[$index], ":")) + list($host, $port) = explode(":", $hosts[$index]); + else + { + $host = $hosts[$index]; + $port = $this->Port; + } + + if($smtp->Connect($host, $port, $this->Timeout)) + $connection = true; + //printf("%s host could not connect
", $hosts[$index]); //debug only + $index++; + } + if(!$connection) + { + $this->error_handler("SMTP Error: could not connect to SMTP host server(s)"); + return false; + } + + // Must perform HELO before authentication + $smtp->Hello($this->Helo); + + // If user requests SMTP authentication + if($this->SMTPAuth) + { + if(!$smtp->Authenticate($this->Username, $this->Password)) + { + $this->error_handler("SMTP Error: Could not authenticate"); + return false; + } + } + + if ($this->Sender == "") + $smtp_from = $this->From; + else + $smtp_from = $this->Sender; + + if(!$smtp->Mail(sprintf("<%s>", $smtp_from))) + { + $e = sprintf("SMTP Error: From address [%s] failed", $smtp_from); + $this->error_handler($e); + return false; + } + + // Attempt to send attach all recipients + for($i = 0; $i < count($this->to); $i++) + { + if(!$smtp->Recipient(sprintf("<%s>", $this->to[$i][0]))) + $bad_rcpt[] = $this->to[$i][0]; + } + for($i = 0; $i < count($this->cc); $i++) + { + if(!$smtp->Recipient(sprintf("<%s>", $this->cc[$i][0]))) + $bad_rcpt[] = $this->cc[$i][0]; + } + for($i = 0; $i < count($this->bcc); $i++) + { + if(!$smtp->Recipient(sprintf("<%s>", $this->bcc[$i][0]))) + $bad_rcpt[] = $this->bcc[$i][0]; + } + + // Create error message + if(count($bad_rcpt) > 0) + { + for($i = 0; $i < count($bad_rcpt); $i++) + { + if($i != 0) + $e .= ", "; + $e .= $bad_rcpt[$i]; + } + $e = sprintf("SMTP Error: The following recipients failed [%s]", $e); + $this->error_handler($e); + + return false; + } + + + if(!$smtp->Data(sprintf("%s%s", $header, $body))) + { + $this->error_handler("SMTP Error: Data not accepted"); + return false; + } + $smtp->Quit(); + + return true; + } + + + ///////////////////////////////////////////////// + // MESSAGE CREATION METHODS + ///////////////////////////////////////////////// + + /** + * Creates recipient headers. Returns string. + * @private + * @returns string + */ + function addr_append($type, $addr) { + $addr_str = $type . ": "; + $addr_str .= $this->addr_format($addr[0]); + if(count($addr) > 1) + { + for($i = 1; $i < count($addr); $i++) + { + $addr_str .= sprintf(", %s", $this->addr_format($addr[$i])); + } + $addr_str .= $this->LE; + } + else + $addr_str .= $this->LE; + + return($addr_str); + } + + /** + * Creates a semicolon delimited list for use in pqm files. + * @private + * @returns string + */ + function addr_list($list_array) { + $addr_list = ""; + for($i = 0; $i < count($list_array); $i++) + { + if($i > 0) + $addr_list .= ";"; + $addr_list .= $list_array[$i][0]; + } + + return $addr_list; + } + + /** + * Formats an address correctly. + * @private + * @returns string + */ + function addr_format($addr) { + if(empty($addr[1])) + $formatted = $addr[0]; + else + $formatted = sprintf('"%s" <%s>', addslashes($addr[1]), $addr[0]); + + return $formatted; + } + + /** + * Wraps message for use with mailers that do not + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. Returns string. + * @private + * @returns string + */ + function word_wrap($message, $length, $qp_mode = false) { + if ($qp_mode) + $soft_break = sprintf(" =%s", $this->LE); + else + $soft_break = $this->LE; + + $message = $this->fix_eol($message); + if (substr($message, -1) == $this->LE) + $message = substr($message, 0, -1); + + $line = explode($this->LE, $message); + $message = ""; + for ($i=0 ;$i < count($line); $i++) + { + $line_part = explode(" ", $line[$i]); + $buf = ""; + for ($e = 0; $e $length)) + { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) + { + if ($space_left > 20) + { + $len = $space_left; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= " " . $part; + $message .= $buf . sprintf("=%s", $this->LE); + } + else + { + $message .= $buf . $soft_break; + } + $buf = ""; + } + while (strlen($word) > 0) + { + $len = $length; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) + $message .= $part . sprintf("=%s", $this->LE); + else + $buf = $part; + } + } + else + { + $buf_o = $buf; + if ($e == 0) + $buf .= $word; + else + $buf .= " " . $word; + if (strlen($buf) > $length and $buf_o != "") + { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . $this->LE; + } + + return ($message); + } + + /** + * Assembles message header. Returns a string if successful + * or false if unsuccessful. + * @private + * @returns string + */ + function create_header() { + $header = array(); + + // Set the boundaries + $uniq_id = md5(uniqid(time())); + $this->boundary[1] = "b1_" . $uniq_id; + $this->boundary[2] = "b2_" . $uniq_id; + + // To be created automatically by mail() + if(($this->Mailer != "mail") && (count($this->to) > 0)) + $header[] = $this->addr_append("To", $this->to); + + $header[] = sprintf("From: \"%s\" <%s>%s", addslashes($this->FromName), + trim($this->From), $this->LE); + if(count($this->cc) > 0) + $header[] = $this->addr_append("Cc", $this->cc); + + // sendmail and mail() extract Bcc from the header before sending + if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0)) + $header[] = $this->addr_append("Bcc", $this->bcc); + + if(count($this->ReplyTo) > 0) + $header[] = $this->addr_append("Reply-to", $this->ReplyTo); + + // mail() sets the subject itself + if($this->Mailer != "mail") + $header[] = sprintf("Subject: %s%s", trim($this->Subject), $this->LE); + + $header[] = sprintf("X-Priority: %d%s", $this->Priority, $this->LE); + $header[] = sprintf("X-Mailer: phpmailer [version %s]%s", $this->Version, $this->LE); + $header[] = sprintf("Return-Path: %s%s", trim($this->From), $this->LE); + + if($this->ConfirmReadingTo != "") + $header[] = sprintf("Disposition-Notification-To: <%s>%s", + trim($this->ConfirmReadingTo), $this->LE); + + // Add custom headers + for($index = 0; $index < count($this->CustomHeader); $index++) + $header[] = sprintf("%s%s", $this->CustomHeader[$index], $this->LE); + + if($this->UseMSMailHeaders) + $header[] = $this->AddMSMailHeaders(); + + $header[] = sprintf("MIME-Version: 1.0%s", $this->LE); + + // Determine what type of message this is + if(count($this->attachment) < 1 && strlen($this->AltBody) < 1) + $this->message_type = "plain"; + else + { + if(count($this->attachment) > 0) + $this->message_type = "attachments"; + if(strlen($this->AltBody) > 0 && count($this->attachment) < 1) + $this->message_type = "alt"; + if(strlen($this->AltBody) > 0 && count($this->attachment) > 0) + $this->message_type = "alt_attachments"; + } + + switch($this->message_type) + { + case "plain": + $header[] = sprintf("Content-Transfer-Encoding: %s%s", + $this->Encoding, $this->LE); + $header[] = sprintf("Content-Type: %s; charset = \"%s\"", + $this->ContentType, $this->CharSet); + break; + case "attachments": + case "alt_attachments": + if($this->EmbeddedImageCount() > 0) + { + $header[] = sprintf("Content-Type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", + "multipart/related", $this->LE, $this->LE, + $this->boundary[1], $this->LE); + } + else + { + $header[] = sprintf("Content-Type: %s;%s", + "multipart/mixed", $this->LE); + $header[] = sprintf("\tboundary=\"%s\"%s", $this->boundary[1], $this->LE); + } + break; + case "alt": + $header[] = sprintf("Content-Type: %s;%s", + "multipart/alternative", $this->LE); + $header[] = sprintf("\tboundary=\"%s\"%s", $this->boundary[1], $this->LE); + break; + } + + // No additional lines when using mail() function + if($this->Mailer != "mail") + $header[] = $this->LE.$this->LE; + + return(join("", $header)); + } + + /** + * Assembles the message body. Returns a string if successful + * or false if unsuccessful. + * @private + * @returns string + */ + function create_body() { + $body = array(); + + // wordwrap the message body if set + if($this->WordWrap > 0) + $this->Body = $this->word_wrap($this->Body, $this->WordWrap); + + switch($this->message_type) + { + case "alt": + // Return text of body + $bndry = new Boundary($this->boundary[1]); + $bndry->CharSet = $this->CharSet; + $bndry->Encoding = $this->Encoding; + $body[] = $bndry->GetSource(); + + $body[] = sprintf("%s%s", $this->AltBody, $this->LE.$this->LE); + + $bndry = new Boundary($this->boundary[1]); + $bndry->CharSet = $this->CharSet; + $bndry->ContentType = "text/html"; + $bndry->Encoding = $this->Encoding; + $body[] = $bndry->GetSource(); + + $body[] = sprintf("%s%s", $this->Body, $this->LE.$this->LE); + + // End the boundary + $body[] = sprintf("%s--%s--%s", $this->LE, + $this->boundary[1], $this->LE.$this->LE); + break; + case "plain": + $body[] = $this->Body; + break; + case "attachments": + $bndry = new Boundary($this->boundary[1]); + $bndry->CharSet = $this->CharSet; + $bndry->ContentType = $this->ContentType; + $bndry->Encoding = $this->Encoding; + $body[] = sprintf("%s%s%s%s", $bndry->GetSource(false), $this->LE, + $this->Body, $this->LE); + + if(!$body[] = $this->attach_all()) + return false; + break; + case "alt_attachments": + $body[] = sprintf("--%s%s", $this->boundary[1], $this->LE); + $body[] = sprintf("Content-Type: %s;%s" . + "\tboundary=\"%s\"%s", + "multipart/alternative", $this->LE, + $this->boundary[2], $this->LE.$this->LE); + + // Create text body + $bndry = new Boundary($this->boundary[2]); + $bndry->CharSet = $this->CharSet; + $bndry->ContentType = "text/plain"; + $bndry->Encoding = $this->Encoding; + $body[] = $bndry->GetSource() . $this->LE; + + $body[] = sprintf("%s%s", $this->AltBody, $this->LE.$this->LE); + + // Create the HTML body + $bndry = new Boundary($this->boundary[2]); + $bndry->CharSet = $this->CharSet; + $bndry->ContentType = "text/html"; + $bndry->Encoding = $this->Encoding; + $body[] = $bndry->GetSource() . $this->LE; + + $body[] = sprintf("%s%s", $this->Body, $this->LE.$this->LE); + + $body[] = sprintf("%s--%s--%s", $this->LE, + $this->boundary[2], $this->LE.$this->LE); + + if(!$body[] = $this->attach_all()) + return false; + break; + } + // Add the encode string code here + $sBody = join("", $body); + $sBody = $this->encode_string($sBody, $this->Encoding); + + return $sBody; + } + + + ///////////////////////////////////////////////// + // ATTACHMENT METHODS + ///////////////////////////////////////////////// + + /** + * Adds an attachment from a path on the filesystem. + * Checks if attachment is valid and then adds + * the attachment to the list. + * Returns false if the file could not be found + * or accessed. + * @public + * @returns bool + */ + function AddAttachment($path, $name = "", $encoding = "base64", $type = "application/octet-stream") { + if(!@is_file($path)) + { + $this->error_handler(sprintf("Could not access [%s] file", $path)); + return false; + } + + $filename = basename($path); + if($name == "") + $name = $filename; + + // Append to $attachment array + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = false; // isStringAttachment + $this->attachment[$cur][6] = "attachment"; + $this->attachment[$cur][7] = 0; + + return true; + } + + /** + * Attaches all fs, string, and binary attachments to the message. + * Returns a string if successful or false if unsuccessful. + * @private + * @returns string + */ + function attach_all() { + // Return text of body + $mime = array(); + + // Add all attachments + for($i = 0; $i < count($this->attachment); $i++) + { + // Check for string attachment + $isString = $this->attachment[$i][5]; + if ($isString) + { + $string = $this->attachment[$i][0]; + } + else + { + $path = $this->attachment[$i][0]; + } + $filename = $this->attachment[$i][1]; + $name = $this->attachment[$i][2]; + $encoding = $this->attachment[$i][3]; + $type = $this->attachment[$i][4]; + $disposition = $this->attachment[$i][6]; + $cid = $this->attachment[$i][7]; + + $mime[] = sprintf("--%s%s", $this->boundary[1], $this->LE); + $mime[] = sprintf("Content-Type: %s; name=\"%s\"%s", $type, $name, $this->LE); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if($disposition == "inline") + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + else + $mime[] = sprintf("Content-ID: <%s>%s", $name, $this->LE); + + $mime[] = sprintf("Content-Disposition: %s; filename=\"%s\"%s", + $disposition, $name, $this->LE.$this->LE); + + // Encode as string attachment + if($isString) + { + if(!$mime[] = sprintf("%s%s", $this->encode_string($string, $encoding), + $this->LE.$this->LE)) + return false; + } + else + { + if(!$mime[] = sprintf("%s%s", $this->encode_file($path, $encoding), + $this->LE.$this->LE)) + return false; + + $mime[] = sprintf("--%s--%s", $this->boundary[1], $this->LE); + + } + } + + return(join("", $mime)); + } + + /** + * Encodes attachment in requested format. Returns a + * string if successful or false if unsuccessful. + * @private + * @returns string + */ + function encode_file ($path, $encoding = "base64") { + if(!@$fd = fopen($path, "rb")) + { + $this->error_handler(sprintf("File Error: Could not open file %s", $path)); + return false; + } + $file = fread($fd, filesize($path)); + $encoded = $this->encode_string($file, $encoding); + fclose($fd); + + return($encoded); + } + + /** + * Encodes string to requested format. Returns a + * string if successful or false if unsuccessful. + * @private + * @returns string + */ + function encode_string ($str, $encoding = "base64") { + switch(strtolower($encoding)) { + case "base64": + // chunk_split is found in PHP >= 3.0.6 + $encoded = chunk_split(base64_encode($str)); + break; + + case "7bit": + case "8bit": + $encoded = $this->fix_eol($str); + if (substr($encoded, -2) != $this->LE) + $encoded .= $this->LE; + break; + + case "binary": + $encoded = $str; + break; + + case "quoted-printable": + $encoded = $this->encode_qp($str); + break; + + default: + $this->error_handler(sprintf("Unknown encoding: %s", $encoding)); + return false; + } + return($encoded); + } + + /** + * Encode string to quoted-printable. Returns a string. + * @private + * @returns string + */ + function encode_qp ($str) { + $encoded = $this->fix_eol($str); + if (substr($encoded, -2) != $this->LE) + $encoded .= $this->LE; + + // Replace every high ascii, control and = characters + $encoded = preg_replace("/([\001-\010\013\014\016-\037\075\177-\377])/e", + "'='.sprintf('%02X', ord('\\1'))", $encoded); + // Replace every spaces and tabs when it's the last character on a line + $encoded = preg_replace("/([\011\040])".$this->LE."/e", + "'='.sprintf('%02X', ord('\\1')).'".$this->LE."'", $encoded); + + // Maximum line length of 76 characters before CRLF (74 + space + '=') + $encoded = $this->word_wrap($encoded, 74, true); + + return $encoded; + } + + /** + * Adds a string or binary attachment (non-filesystem) to the list. + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @public + * @returns void + */ + function AddStringAttachment($string, $filename, $encoding = "base64", $type = "application/octet-stream") { + // Append to $attachment array + $cur = count($this->attachment); + $this->attachment[$cur][0] = $string; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $filename; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = true; // isString + $this->attachment[$cur][6] = "attachment"; + $this->attachment[$cur][7] = 0; + } + + /** + * Adds an embedded attachment. This can include images, sounds, and + * just about any other document. + * @param cid this is the Content Id of the attachment. Use this to identify + * the Id for accessing the image in an HTML form. + * @public + * @returns bool + */ + function AddEmbeddedImage($path, $cid, $name = "", $encoding = "base64", $type = "application/octet-stream") { + + if(!@is_file($path)) + { + $this->error_handler(sprintf("Could not access [%s] file", $path)); + return false; + } + + $filename = basename($path); + if($name == "") + $name = $filename; + + // Append to $attachment array + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + $this->attachment[$cur][5] = false; // isStringAttachment + $this->attachment[$cur][6] = "inline"; + $this->attachment[$cur][7] = $cid; + + return true; + } + + /** + * Returns the number of embedded images in an email. + * @private + * @returns int + */ + function EmbeddedImageCount() { + $ret = 0; + for($i = 0; $i < count($this->attachment); $i++) + { + if($this->attachment[$i][6] == "inline") + $ret++; + } + + return $ret; + } + + ///////////////////////////////////////////////// + // MESSAGE RESET METHODS + ///////////////////////////////////////////////// + + /** + * Clears all recipients assigned in the TO array. Returns void. + * @public + * @returns void + */ + function ClearAddresses() { + $this->to = array(); + } + + /** + * Clears all recipients assigned in the CC array. Returns void. + * @public + * @returns void + */ + function ClearCCs() { + $this->cc = array(); + } + + /** + * Clears all recipients assigned in the BCC array. Returns void. + * @public + * @returns void + */ + function ClearBCCs() { + $this->bcc = array(); + } + + /** + * Clears all recipients assigned in the ReplyTo array. Returns void. + * @public + * @returns void + */ + function ClearReplyTos() { + $this->ReplyTo = array(); + } + + /** + * Clears all recipients assigned in the TO, CC and BCC + * array. Returns void. + * @public + * @returns void + */ + function ClearAllRecipients() { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + } + + /** + * Clears all previously set filesystem, string, and binary + * attachments. Returns void. + * @public + * @returns void + */ + function ClearAttachments() { + $this->attachment = array(); + } + + /** + * Clears all custom headers. Returns void. + * @public + * @returns void + */ + function ClearCustomHeaders() { + $this->CustomHeader = array(); + } + + + ///////////////////////////////////////////////// + // MISCELLANEOUS METHODS + ///////////////////////////////////////////////// + + /** + * Adds the error message to the error container. + * Returns void. + * @private + * @returns void + */ + function error_handler($msg) { + $this->ErrorInfo = $msg; + } + + /** + * Returns the proper RFC 822 formatted date. Returns string. + * @private + * @returns string + */ + function rfc_date() { + $tz = date("Z"); + $tzs = ($tz < 0) ? "-" : "+"; + $tz = abs($tz); + $tz = ($tz/3600)*100 + ($tz%3600)/60; + $date = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz); + return $date; + } + + /** + * Returns received header for message tracing. Returns string. + * @private + * @returns string + */ + function received() { + // Check for vars because they might not exist. Possibly + // write a small retrieval function (that mailer can use too!) + + $str = sprintf("Received: from phpmailer ([%s]) by %s " . + "with HTTP;%s\t %s%s", + $this->get_server_var("REMOTE_ADDR"), + $this->get_server_var("SERVER_NAME"), + $this->LE, + $this->rfc_date(), + $this->LE); + + return $str; + } + + /** + * Returns the appropriate server variable. Should work with both + * PHP 4.1.0+ as well as older versions. Returns an empty string + * if nothing is found. + * @private + * @returns mixed + */ + function get_server_var($varName) { + global $HTTP_SERVER_VARS; + global $HTTP_ENV_VARS; + + if(!isset($_SERVER)) + { + $_SERVER = $HTTP_SERVER_VARS; + if(!isset($_SERVER["REMOTE_ADDR"])) + $_SERVER = $HTTP_ENV_VARS; // must be Apache + } + + if(isset($_SERVER[$varName])) + return $_SERVER[$varName]; + else + return ""; + } + + /** + * Changes every end of line from CR or LF to CRLF. Returns string. + * @private + * @returns string + */ + function fix_eol($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + $str = str_replace("\n", $this->LE, $str); + return $str; + } + + /** + * Adds a custom header. Returns void. + * @public + * @returns void + */ + function AddCustomHeader($custom_header) { + $this->CustomHeader[] = $custom_header; + } + + /** + * Adds all the Microsoft message headers. Returns string. + * @private + * @returns string + */ + function AddMSMailHeaders() { + $MSHeader = ""; + if($this->Priority == 1) + $MSPriority = "High"; + elseif($this->Priority == 5) + $MSPriority = "Low"; + else + $MSPriority = "Medium"; + + $MSHeader .= sprintf("X-MSMail-Priority: %s%s", $MSPriority, $this->LE); + $MSHeader .= sprintf("Importance: %s%s", $MSPriority, $this->LE); + + return($MSHeader); + } + +} + + +/** + * Boundary - MIME message boundary class + * @author Brent R. Matzelle + */ +class Boundary +{ + /** + * Sets the boundary ID. + * @private + * @type string + */ + var $ID = 0; + + /** + * Sets the boundary Content Type. + * @public + * @type string + */ + var $ContentType = "text/plain"; + + /** + * Sets the Encoding. + * @public + * @type string + */ + var $Encoding = ""; + + /** + * Sets an attachment disposition. + * @public + * @type string + */ + var $Disposition = ""; + + /** + * Sets an attachment file name. + * @public + * @type string + */ + var $FileName = ""; + + /** + * Sets the Char set. + * @public + * @type string + */ + var $CharSet = ""; + + /** + * Sets the line endings of the message. Default is "\n"; + * @public + * @type string + */ + var $LE = "\n"; + + /** + * Main constructor. + */ + function Boundary($boundary_id) { + $this->ID = $boundary_id; + } + + /** + * Returns the source of the boundary. + * @public + * @returns string + */ + function GetSource($bLineEnding = true) { + $ret = array(); + $mime[] = sprintf("--%s%s", $this->ID, $this->LE); + $mime[] = sprintf("Content-Type: %s; charset = \"%s\"%s", + $this->ContentType, $this->CharSet, $this->LE); + //$mime[] = sprintf("Content-Transfer-Encoding: %s%s", $this->Encoding, + // $this->LE); + + if(strlen($this->Disposition) > 0) + { + $mime[] = sprintf("Content-Disposition: %s;"); + if(strlen($this->FileName) > 0) + $mime[] = sprinf("filename=\"%s\"", $this->$this->FileName); + } + + if($bLineEnding) + $mime[] = $this->LE; + + return join("", $mime); + } +} + +?> \ No newline at end of file diff --git a/classes/sm/smCore.class.php b/classes/sm/smCore.class.php new file mode 100644 index 00000000..71809ae6 --- /dev/null +++ b/classes/sm/smCore.class.php @@ -0,0 +1,34 @@ +. + */ + +abstract class smCore { + public $db; + + function __construct() { + // add database handler + $this->db = $GLOBALS['db']; + } +} + +?> \ No newline at end of file diff --git a/classes/sm/smDatabase.class.php b/classes/sm/smDatabase.class.php new file mode 100644 index 00000000..cc668403 --- /dev/null +++ b/classes/sm/smDatabase.class.php @@ -0,0 +1,365 @@ +. + */ + +class smDatabase { + + protected $debug = array(); + protected $last_inserted_id; + protected $link; + protected $num_rows_found; + protected $num_rows_returned; + + function __construct() { + // Initizale connection + $this->link = mysql_connect(SM_DB_HOST, SM_DB_USER, SM_DB_PASS); + + if (!mysql_select_db(SM_DB_NAME, $this->link)) { + trigger_error(mysql_errno() . ": " . mysql_error()); + } + + // Setting the utf collection + mysql_query("SET NAMES utf8;", $this->getLink()); + mysql_query("SET CHARACTER SET 'utf8';", $this->getLink()); + } + + /** + * Executes a query + * + * @param $sql string MySQL query + * @return resource mysql resource + */ + + public function executeQuery($sql) { + + $result = mysql_query($sql, $this->getLink()); + + if (mysql_error($this->getLink())) { + trigger_error(mysql_errno($this->getLink()) . ': ' . mysql_error($this->getLink())); + return false; + } + + if (is_resource($result) && mysql_num_rows($result) > 0) { + // Rows returned + $this->num_rows_returned = mysql_num_rows($result); + + // Rows found + $result_num_rows_found = $this->fetchResults(mysql_query('SELECT FOUND_ROWS();')); + $this->num_rows_found = $result_num_rows_found[0]['FOUND_ROWS()']; + } + + if (substr(strtolower(trim($sql)), 0, 6) == 'insert') { + // we have an insert + $this->last_inserted_id = mysql_insert_id($this->getLink()); + $result = $this->last_inserted_id; + } + + return $result; + } + + /** + * Exectues query and fetches result + * + * @param $query string MySQL query + * @return $result array + */ + public function query($query) { + + // Execute query and process results + $result_resource = $this->executeQuery($query); + $result = $this->fetchResults($result_resource); + + return $result; + } + + /** + * Fetch results from a query + * + * @param resource $result result from a mysql query + * @return array $array with results (multi-dimimensial) for more than one rows + */ + + public function fetchResults($result_query){ + + if (!is_resource($result_query)) { + return array(); + } + + $num_rows = mysql_num_rows($result_query); + + $result = array(); + while($record = mysql_fetch_assoc($result_query)) { + $result[] = $record; + } + + return $result; + } + + /** + * Performs a select on the given table and returns an multi dimensional associative array with results + * + * @param string $table tablename + * @param mixed $where string or array with where data + * @param array $fields array with fields to be retrieved. if empty all fields will be retrieved + * @param string $limit limit. for example: 0,30 + * @param array $orderby fields for the orderby clause + * @param string $direction ASC or DESC. Defaults to ASC + * @return array multi dimensional array with results + */ + + public function select($table, $where = null, $fields = null, $limit = '', $orderby = null, $direction = 'ASC'){ + // build query + $query_parts = array(); + $query_parts[] = 'SELECT SQL_CALC_FOUND_ROWS'; + + // Fields + if ($fields !== null && !empty($fields)) { + $query_parts[] = "`".implode('`,`', $fields)."`"; + } else { + $query_parts[] = ' * '; + } + + // From + $query_parts[] = "FROM `{$table}`"; + + // Where clause + $query_parts[] = $this->buildWhereClause($table, $where); + + // Order by + if ($orderby !== null && !empty($orderby)) { + $orderby_clause = 'ORDER BY '; + + foreach($orderby as $field) { + $orderby_clause .= "`{$field}`, "; + } + $query_parts[] = substr($orderby_clause, 0, -2) . ' ' . $direction; + } + + // Limit + if ($limit != '') { + $query_parts[] = 'LIMIT ' . $limit; + } + + $query = implode(' ', $query_parts); + + // Get results + $result = $this->query($query); + + return $result; + } + + public function selectRow($table, $where = null, $fields = null, $limit = '', $orderby = null, $direction = 'ASC') { + $result = $this->select($table, $where, $fields, $limit, $orderby, $direction); + + if ($this->getNumRowsReturned() == '1') { + $result = $result[0]; + } + return $result; + } + + /** + * Remove a record from database + * + * @param string $table tablename + * @param mixed $where Where clause array or primary Id (string) or where clause (string) + * @return boolean + */ + public function delete($table, $where = null){ + + if ($table != '') { + + $sql = 'DELETE FROM `'.$table.'` ' . $this->buildWhereClause($table, $where); + + $this->query($sql); + } + } + + /** + * Insert or update data to the database + * + * @param array $table table name + * @param array $data data to save or insert + * @param mixed $where either string ('user_id=2' or just '2' (works only with primary field)) or array with where clause (only when updating) + */ + public function save($table, $data, $where = null) { + + if ($where === null) { + // insert mode + $query = "INSERT INTO "; + } else { + $query = "UPDATE "; + } + + $query .= "`{$table}` SET "; + + foreach($data as $field => $value) { + $value = $this->escapeValue($value); + $query .= "`{$table}`.`{$field}`='{$value}', "; + } + + $query = substr($query, 0, -2) . ' ' . $this->buildWhereClause($table, $where); + + return $this->query($query); + } + + /** + * Build WHERE clause for query + * + * @param string $table table name + * @param mixed $where can be primary id (eg '2'), can be string (eg 'name=pepe') or can be array + * @return string sql where clause + */ + public function buildWhereClause($table, $where = null) { + + $query = ''; + + if ($where !== null) { + if (is_array($where)) { + $query .= " WHERE "; + + foreach($where as $field => $value) { + $value = $this->escapeValue($value); + $query .= "`{$table}`.`{$field}`='{$value}' AND "; + } + $query = substr($query, 0, -5); + } else { + if (strpos($where, '=') === false) { + // no field given, use primary field + $structure = $this->getTableStructure($table); + $where = $this->escapeValue($where); + $query .= " WHERE `{$table}`.`{$structure['primary']}`='{$where}'"; + } elseif (strpos(strtolower(trim($where)), 'where') === false) { + $query .= " WHERE {$where}"; + } else { + $query .= ' '.$where; + } + } + } + return $query; + } + + /** + * Get table structure and primary key + * + * @param string $table table name + * @return array primary key and database structure + */ + public function getTableStructure($table) { + if ($table == '') return false; + + $structure = $this->query("DESCRIBE `{$table}`"); + + if (empty($structure)) return false; + + // use arrray search function to get primary key + $search_needle = array( + 'key' => 'Key', + 'value' => 'PRI' + ); + $primary = pep_array_search_key_value( + $structure, + array( + 'key' => 'Key', + 'value' => 'PRI' + ) + ); + + $primary_field = $structure[$primary[0]['path'][0]]['Field']; + return array( + 'primary' => $primary_field, + 'fields' => $structure + ); + } + + /** + * Get information about a field from the database + * + * @param string $table + * @param string $field + * @return array mysql field information + */ + public function getFieldInfo($table, $field) { + if ($table == '' || $field == '') return array(); + + $db_structure = $this->getTableStructure($table); + + $field_info = pep_array_search_key_value( + $db_structure, + array( + 'key' => 'Field', + 'value' => $field + ) + ); + + if (empty($field_info)) { + return array(); + } + + // return field info + return $field_info[0]['result']; + } + + /** + * Formats the value for the SQL query to secure against injections + * + * @param string $value + * @return string + */ + public function escapeValue($value) { + if(get_magic_quotes_gpc()) { + $value = stripslashes($value); + } + $value = mysql_real_escape_string($value, $this->link); + + return $value; + } + + /** + * Get number of rows found + * + * @return int number of rows found + */ + public function getNumRowsFound() { + return $this->num_rows_found; + } + + /** + * Get number of rows returned + * + * @return int number of rows returned + */ + public function getNumRowsReturned() { + return $this->num_rows_returned; + } + + /** + * Get the database connection identifier + * + * @return object db connection + */ + public function getLink() { + return $this->link; + } +} + +?> diff --git a/classes/sm/smTemplate.class.php b/classes/sm/smTemplate.class.php new file mode 100644 index 00000000..332f0c7d --- /dev/null +++ b/classes/sm/smTemplate.class.php @@ -0,0 +1,307 @@ +. + */ + +class smTemplate { + protected $css_files = array(); + protected $js_files = array(); + protected $output; + protected $templates = array(); + + function __construct() { + // add the main template + $this->newTemplate('main', 'main.tpl.html'); + } + + /** + * Add template + * + * @param string $id template id used in the tpl file (html) + * @param string $filename path to template file, or if file is located in the default template dir just the filename + * @return mixed false if template cannot be found, html code on success + */ + public function newTemplate($id, $filename) { + if (file_exists($filename)) { + $this->templates[$id] = $this->parseFile($filename); + } elseif (file_exists(SM_PATH_TPL.$filename)) { + $this->templates[$id] = $this->parseFile(SM_PATH_TPL.$filename); + } else { + // file does not exist + trigger_error('Template not found with id: '.$id.' and filename: '.$filename); + return false; + } + $use_tpl = null; + + // only get data from the file that's between the tpl id tags with this id + // find "tpl_{$row_id}" in the current template + //preg_match_all('{(.*?)}is', $this->templates[$id], $matches); + preg_match_all("{(.*?)}is", $this->templates[$id], $matches); + + // no repeat tpl found? skip to next one + if (empty($matches)) return false; + + // check if the row_id is in one of the matches (aka whether the supplied row id actually has a template in this file) + if (isset($matches[1][0])) { + $use_tpl = $matches[1][0]; + } + + // no template with the given id found.. + if ($use_tpl === null) return false; + + // remove tpl code tags from original template so it won't be in the source + $this->templates[$id] = preg_replace("{(.*?)}is", "", $use_tpl); + + return $this->templates[$id]; + } + + /** + * Add data to the template + * + * @param string $id template_id used by add_template() + * @param array $data + * @return string new template + */ + public function addTemplateData($id, $data) { + // does the template exist? + if (!isset($this->templates[$id])) { + // file does not exist + trigger_error('Template not found with id: '.$id); + return false; + } + + foreach($data as $key => $value) { + // check if $value is a file + $value = (file_exists($value)) ? $this->parseFile($value) : $value; + + $this->templates[$id] = str_replace('{'.$key.'}', $value, $this->templates[$id]); + } + return $this->templates[$id]; + } + + /** + * Add repeat rows to template + * + * @param string $id template id used by add_template() + * @param string $repeat_id ID used in template file for the repeat template: html + * @param array $data + * @return mixed false if repeat template cannot be found, html code on success + */ + public function addTemplateDataRepeat($id, $repeat_id, $data) { + // does the template exist? + if (!isset($this->templates[$id])) { + // file does not exist + trigger_error('Template not found with id: '.$id); + return false; + } + + $use_tpl = null; + + // find "tpl_repeat_{$repeat_id}_" in the current template + //preg_match_all('{(.*?)}is', $this->templates[$id], $matches); + preg_match_all("{(.*?)}is", $this->templates[$id], $matches); + + // no repeat tpl found? skip to next one + if (empty($matches)) return false; + + // check if the row_id is in one of the matches (aka whether the supplied row id actually has a repeat template in this file) + if (isset($matches[1][0])) { + $use_tpl = $matches[1][0]; + } + + // if we didn't find a repeat template for the row_id supplied, skip the rest.. + if ($use_tpl === null) return false; + + // remove repeat tpl code from original template so it won't be in the source + $this->templates[$id] = preg_replace("{(.*?)}is", "", $this->templates[$id]); + + // now lets go through all the records supplied and put them in the HTML repeat code we just found + $result = ''; + + foreach($data as $record) { + $tmp_string = $use_tpl; + foreach($record as $k => $v) { + $tmp_string = str_replace('{'.$k.'}', $v, $tmp_string); + } + $result .= $tmp_string."\n"; + } + + // add to main template.. + return $this->addTemplateData($id, array($repeat_id => $result)); + } + + public function display($id) { + // check if there are any unused tpl_repeat templates, and if there are remove them + $result = preg_replace('{(.*?)}is', '', $this->templates[$id]); + + // check for tpl variables that have not been replaced. ie: {name}. ignore literal stuff, though. ie: {{name}} is {name} and should not be removed + preg_match_all('~{?{(.+?)}}?~', $result, $matches); + + // add css and javascript files to header + $result = $this->addHeaderFiles($id, $result); + + foreach($matches[0] as $match) { + if (substr($match, 0, 2) == '{{') { + // literal! remove only first and last bracket! + $result = str_replace($match, substr($match, 1, -1), $result); + } else { + // unused variable, remove completely + $result = str_replace($match, '', $result); + } + } + return $result; + } + + /** + * Adds a css file to the list which will be added to the template when display() is called + * + * @param string $template_id + * @param string $filename + * @param string $path uses default set in config if non specified + */ + public function addCSS($filename, $template_id = 'main', $path = SM_PATH_CSS) { + if (!isset($this->css_files[$template_id])) { + $this->css_files[$template_id] = array(); + } + + // if file doesn't exist we assume it's inline + $type = (file_exists($path.$filename)) ? 'file' : 'inline'; + + $this->css_files[$template_id][$filename] = array( + 'file' => ($type == 'file') ? $path.$filename : $filename, + 'type' => $type + ); + } + + /** + * Adds a javascript file to the list which will be added to the template when display() is called + * + * @param string $template_id + * @param string $filename path to file or CSS code to be added inline + * @param string $path uses default set in config if non specified + */ + public function addJS($filename, $template_id = 'main', $path = SM_PATH_JS) { + if (!isset($this->js_files[$template_id])) { + $this->js_files[$template_id] = array(); + } + + // if file doesn't exist we assume it's inline + $type = (file_exists($path.$filename)) ? 'file' : 'inline'; + + $this->js_files[$template_id][$filename] = array( + 'file' => ($type == 'file') ? $path.$filename : $filename, + 'type' => $type + ); + } + + /** + * Get html code for a template, or if no template id given get all templates + * + * @param string $template_id + * @return mixed string when ID given, else array + */ + public function getTemplate($template_id = null) { + if ($template_id === null) { + return $this->templates; + } elseif (isset($this->templates[$template_id])) { + return $this->templates[$template_id]; + } else { + return false; + } + } + + /** + * Adds the CSS and JavaScript files to the header html. + * + * @param string $template_id + * @param string $html + * @return string new html code + */ + protected function addHeaderFiles($template_id, $html) { + // get code between tags + preg_match_all("{(.*?)<\/head>}is", $html, $matches); + + if (isset($matches[1][0]) && $matches[1][0] != '') { + $header = $matches[1][0]; + + if (isset($this->css_files[$template_id]) && !empty($this->css_files[$template_id])) { + $header .= "\t\n"; + + foreach($this->css_files[$template_id] as $filename => $info) { + switch($info['type']) { + case 'file': + $header .= "\t\n"; + break; + case 'inline': + $header .= + "\t\n"; + break; + } + } + } + + if (isset($this->js_files[$template_id]) && !empty($this->js_files[$template_id])) { + $header .= "\t\n"; + + foreach($this->js_files[$template_id] as $filename => $info) { + switch($info['type']) { + case 'file': + $header .= "\t\n"; + break; + case 'inline': + $header .= + "\t\n"; + break; + } + } + } + // add new header to html + $html = preg_replace('{'.$matches[1][0].'}is', $header, $html); + } + + return $html; + } + + /** + * + * Get file content + * + * @param string $filename filename + * @return string file contents + */ + protected function parseFile($filename) { + if (!file_exists($filename)) return false; + + ob_start(); + include($filename); + $file_content = ob_get_contents(); + ob_end_clean(); + + return $file_content; + } +} + +?> \ No newline at end of file diff --git a/classes/sm/smUpdaterStatus.class.php b/classes/sm/smUpdaterStatus.class.php new file mode 100644 index 00000000..d1a15589 --- /dev/null +++ b/classes/sm/smUpdaterStatus.class.php @@ -0,0 +1,354 @@ +. + */ + +class smUpdaterStatus extends smCore { + public $error; + public $notify; + public $rtime = 0; + public $server; + public $status_org = false; + public $status_new = false; + + /** + * Set a new server + * + * @param array $server + * @param string $status_org either 'on' or 'off' + */ + public function setServer($server, $status_org) { + $this->clearResults(); + + $this->server = $server; + $this->status_org = $status_org; + } + + /** + * Get the new status of the selected server. If the update has not been performed yet it will do so first + * + * @return string + */ + public function getStatus() { + if(!$this->server) { + return false; + } + if(!$this->status_new) { + $this->update(); + } + return $this->status_new; + } + + /** + * The function its all about. This one checks whether the given ip and port are up and running! + * If the server check fails it will try one more time + * + * @param int $max_runs how many times should the script recheck the server if unavailable. default is 2 + * @return string new status + */ + public function update($max_runs = 2) { + switch($this->server['type']) { + case 'service': + $result = $this->updateService($max_runs); + break; + case 'website': + $result = $this->updateWebsite($max_runs); + break; + } + return $result; + + } + + protected function updateService($max_runs, $run = 1) { + // save response time + $time = explode(' ', microtime()); + $starttime = $time[1] + $time[0]; + + @$fp = fsockopen ($this->server['ip'], $this->server['port'], $errno, $errstr, 10); + + $time = explode(" ", microtime()); + $endtime = $time[1] + $time[0]; + $this->rtime = ($endtime - $starttime); + + + $this->status_new = ($fp === false) ? 'off' : 'on'; + $this->error = $errstr; + // add the error to the server array for when parsing the messages + $this->server['error'] = $this->error; + + @fclose($fp); + + // check if server is available and rerun if asked. + if($this->status_new == 'off' && $run < $max_runs) { + return $this->updateService($max_runs, $run + 1); + } + + return $this->status_new; + } + + protected function updateWebsite($max_runs, $run = 1) { + // save response time + $time = explode(' ', microtime()); + $starttime = $time[1] + $time[0]; + + $ch = curl_init(); + curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt ($ch, CURLOPT_URL, $this->server['ip']); + curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt ($ch, CURLOPT_TIMEOUT, 10); + curl_setopt ($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11'); + + // We're only interested in the header, because that should tell us plenty! + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD'); + + $headers = curl_exec ($ch); + curl_close ($ch); + + $time = explode(" ", microtime()); + $endtime = $time[1] + $time[0]; + $this->rtime = ($endtime - $starttime); + + // the first line would be the status code.. + $status_code = strtok($headers, "\r\n"); + // keep it general + // $code[1][0] = status code + // $code[2][0] = name of status code + preg_match_all("/[A-Z]{2,5}\/\d\.\d\s(\d{3})\s(.*)/", $status_code, $code_matches); + + if(empty($code_matches[0])) { + // somehow we dont have a proper response. + $this->server['error'] = $this->error = 'no response from server'; + $this->status_new = 'off'; + } else { + $code = $code_matches[1][0]; + $msg = $code_matches[2][0]; + + // All status codes starting with a 4 mean trouble! + if(substr($code, 0, 1) == '4') { + $this->server['error'] = $this->error = $code . ' ' . $msg; + $this->status_new = 'off'; + } else { + $this->status_new = 'on'; + } + } + + // check if server is available and rerun if asked. + if($this->status_new == 'off' && $run < $max_runs) { + return $this->updateWebsite($max_runs, $run + 1); + } + + return $this->status_new; + } + + /** + * This function initializes the sending (text msg & email) and logging + * + */ + public function notify() { + if(sm_get_conf('email_status') == false && sm_get_conf('sms_status') == false && sm_get_conf('log_status') == false) { + // seems like we have nothing to do. skip the rest + return false; + } + $notify = false; + + // check which type of alert the user wants + switch(sm_get_conf('alert_type')) { + case 'always': + if($this->status_new == 'off') { + // server is offline. we are in error state. + $notify = true; + } + break; + case 'offline': + // only send a notification if the server goes down for the first time! + if($this->status_new == 'off' && $this->status_org == 'on') { + $notify = true; + } + break; + case 'status': + if($this->status_new != $this->status_org) { + // status has been changed! + $notify = true; + } + break; + } + + if(!$notify) { + return false; + } + + // first add to log (we use the same text as the SMS message because its short..) + if(sm_get_conf('log_status')) { + sm_add_log( + $this->server['server_id'], + 'status', + sm_parse_msg($this->status_new, 'sms', $this->server) + ); + } + + // check if email is enabled for this server + if(sm_get_conf('email_status') && $this->server['email'] == 'yes') { + // send email + $this->notifyByEmail(); + } + // check if sms is enabled for this server + if(sm_get_conf('sms_status') && $this->server['sms'] == 'yes') { + // yay lets wake those nerds up! + $this->notifyByTxtMsg(); + } + return true; + } + + /** + * This functions performs the email notifications + * + * @return boolean + */ + protected function notifyByEmail() { + $userlist = array(); + + // find all the users with this server listed + $users = $this->db->select( + SM_DB_PREFIX . 'users', + 'FIND_IN_SET(\''.$this->server['server_id'].'\', `server_id`) AND `email` != \'\'', + array('user_id', 'name', 'email') + ); + + if (empty($users)) { + return false; + } + + // build mail object with some default values + $mail = new phpmailer(); + + $mail->From = sm_get_conf('email_from_email'); + $mail->FromName = sm_get_conf('email_from_name'); + $mail->Subject = sm_parse_msg($this->status_new, 'email_subject', $this->server); + $mail->Priority = 1; + + $body = sm_parse_msg($this->status_new, 'email_body', $this->server); + $mail->Body = $body; + $mail->AltBody = str_replace('
', "\n", $body); + + // go through empl + foreach ($users as $user) { + // we sent a seperate email to every single user. + $userlist[] = $user['user_id']; + $mail->AddAddress($user['email'], $user['name']); + $mail->Send(); + $mail->ClearAddresses(); + } + + if(sm_get_conf('log_email')) { + // save to log + sm_add_log($this->server['server_id'], 'email', $body, implode(',', $userlist)); + } + } + + /** + * This functions performs the text message notifications + * + * @return unknown + */ + protected function notifyByTxtMsg() { + // send sms to all users for this server using defined gateway + $users = $this->db->select( + SM_DB_PREFIX . 'users', + 'FIND_IN_SET(\''.$this->server['server_id'].'\', `server_id`) AND `mobile` != \'\'', + array('user_id', 'name', 'mobile') + ); + + if (empty($users)) { + return false; + } + + // we have to build an userlist for the log table.. + $userlist = array(); + + // open the right class + // not making this any more dynamic, because perhaps some gateways need custom settings (like Mollie) + switch(strtolower(sm_get_conf('sms_gateway'))) { + case 'inetworx': + $sms = new txtmsgInetworx(); + break; + case 'mollie': + $sms = new txtmsgMollie(); + $sms->setGateway(1); + break; + case 'spryng': + $sms = new txtmsgSpryng(); + break; + } + + // copy login information from the config file + $sms->setLogin(sm_get_conf('sms_gateway_username'), sm_get_conf('sms_gateway_password')); + $sms->setOriginator(sm_get_conf('sms_from')); + + // add all users to the recipients list + foreach ($users as $user) { + $userlist[] = $user['user_id']; + $sms->addRecipients($user['mobile']); + } + + $message = sm_parse_msg($this->status_new, 'sms', $this->server); + + // Send sms + $result = $sms->sendSMS($message); + + if(sm_get_conf('log_sms')) { + // save to log + sm_add_log($this->server['server_id'], 'sms', $message, implode(',', $userlist)); + } + return $result; + } + + /** + * Get the error returned by the update function + * + * @return string + */ + public function getError() { + return $this->error; + } + + /** + * Get the response time of the server + * + * @return string + */ + public function getRtime() { + return $this->rtime; + } + + /** + * Clear all the results that are left from the previous run + * + */ + protected function clearResults() { + $this->error = ''; + $this->status_org = false; + $this->status_new = false; + } +} + +?> \ No newline at end of file diff --git a/classes/txtmsg/txtmsgCore.class.php b/classes/txtmsg/txtmsgCore.class.php new file mode 100644 index 00000000..2bc09edd --- /dev/null +++ b/classes/txtmsg/txtmsgCore.class.php @@ -0,0 +1,61 @@ +. + */ + +abstract class txtmsgCore implements txtmsgInterface { + protected $originator; + protected $password; + protected $recipients = array(); + protected $username; + + /** + * Define login information for the gateway + * + * @param string $username + * @param string $password + */ + public function setLogin($username, $password) { + $this->username = $username; + $this->password = $password; + } + + /** + * Set the mobile number the text message will be send from + * + * @param string $originator + */ + public function setOriginator($originator) { + $this->originator = $originator; + } + + /** + * Add new recipient to the list + * + * @param unknown_type $recipient + */ + public function addRecipients($recipient) { + array_push($this->recipients, $recipient); + } +} + +?> diff --git a/classes/txtmsg/txtmsgInetworx.class.php b/classes/txtmsg/txtmsgInetworx.class.php new file mode 100644 index 00000000..fd922b11 --- /dev/null +++ b/classes/txtmsg/txtmsgInetworx.class.php @@ -0,0 +1,142 @@ +. + */ + +class txtmsgInetworx extends txtmsgCore { + // ========================================================================= + // [ Fields ] + // ========================================================================= + + // ========================================================================= + // [ Methods ] + // ========================================================================= + + /** + * Send a text message to one or more recipients + * + * @param string $subject + * @param string $body + * @return boolean + */ + public function sendSMS($message) { + + if(empty($this->recipients)) { + return false; + } + + $errors = 0; + + foreach($this->recipients as $receiver) { + if(!$this->executeSend($message, $receiver, $this->originator)) { + $errors++; + } + } + $this->recipients = array(); + + return ($errors > 0) ? false : true; + } + + /** + * Performs the actual send operation + * + * @param string $subject + * @param string $body + * @param string $receiver + * @param string $sender + * @return unknown + */ + protected function executeSend($message, $receiver, $sender) { + $con_https[0] = 'sms.inetworx.ch'; + $con_https[1] = '443'; + $con_https[2] = 'inetworxag:conn2smsapp'; + $posturl = "/smsapp/sendsms.php"; + + if(!empty($receiver)) { + $postvars = 'user=' . urlencode($this->username) . + '&pass=' . urlencode($this->password) . + '&sender=' . urldecode($sender) . + '&rcpt=' . urlencode($receiver) . + '&msgbody=' . urlencode($message) + ; + //if enabled, it sends a flash-message (not stored in Inbox!!) + //$postvars .= "&mclass=1"; + + $rtnval = $this->_auth_https_post(array($con_https[0], $con_https[1], $con_https[2], $posturl, $postvars)); + + return $rtnval; + //echo "SMS-Send-Result: $rtnval"; + } else { + return false; + } + } + + protected function _auth_https_post($inarray) { + // AUTH_HTTPS_POST: Request POST URL using basic authentication and SSL. + // Input: inarray[0]: host name + // inarray[1]: service port + // inarray[2]: user/password + // inarray[3]: URL request + // inarray[4]: POST variables + // Output: Message returned by server. + + // Build the header. + $header = "POST ".$inarray[3]." HTTP/1.0\r\n"; + $header .= "Authorization: Basic ".base64_encode("$inarray[2]")."\r\n"; + $header .= "Host: ".$inarray[0]."\r\n"; + $header .= "Content-type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-length: ".strlen($inarray[4])."\r\n\r\n"; + // Connect to the server. + $connection = fsockopen("ssl://".$inarray[0], $inarray[1], &$errnum, &$errdesc, 10); + $msg = ""; + if (! $connection){ + $msg = $errdesc." (".$errnum.")"; + } + else + { + socket_set_blocking($connection,false); + fputs($connection,$header.$inarray[4]); + while (! feof($connection)) + { + $newline = fgets($connection,128); + switch ($newline) + { + // Skip http headers. + case (strstr($newline, 'Content-')): break; + case (strstr($newline, 'HTTP/1')): break; + case (strstr($newline, 'Date:')): break; + case (strstr($newline, 'Server:')): break; + case (strstr($newline, 'X-Powered-By:')): break; + case (strstr($newline, 'Connection:')): break; + case "": break; + case "\r\n": break; + default: $msg .= $newline; + } //end switch + } //end while + fclose($connection); + } //end else + return $msg; + } //end function auth_https_post + +} + +?> \ No newline at end of file diff --git a/classes/txtmsg/txtmsgInterface.class.php b/classes/txtmsg/txtmsgInterface.class.php new file mode 100644 index 00000000..6c51e8e8 --- /dev/null +++ b/classes/txtmsg/txtmsgInterface.class.php @@ -0,0 +1,34 @@ +. + */ + +interface txtmsgInterface { + + public function setLogin($username, $password); + public function setOriginator($originator); + public function addRecipients($recipient); + public function sendSMS($message); + +} + +?> \ No newline at end of file diff --git a/classes/txtmsg/txtmsgMollie.class.php b/classes/txtmsg/txtmsgMollie.class.php new file mode 100644 index 00000000..47ebdc37 --- /dev/null +++ b/classes/txtmsg/txtmsgMollie.class.php @@ -0,0 +1,96 @@ +. + */ + +class txtmsgMollie extends txtmsgCore { + // ========================================================================= + // [ Fields ] + // ========================================================================= + public $gateway = 1; + public $success = false; + public $reference = ''; + + // ========================================================================= + // [ Methods ] + // ========================================================================= + /** + * Select the gateway to use + * + * @param unknown_type $gateway + */ + public function setGateway($gateway) { + $this->gateway = $gateway; + } + + public function setReference ($reference) { + $this->reference = $reference; + } + + /** + * Send a text message to one or more recipients + * + * @param string $subject + * @param string $body + * @return boolean + */ + public function sendSMS($message) { + $recipients = implode(',', $this->recipients); + + $result = $this->_auth_https_post('www.mollie.nl', '/xml/sms/', + 'gateway='.urlencode($this->gateway). + '&username='.urlencode($this->username). + '&password='.urlencode($this->password). + '&originator='.urlencode($this->originator). + '&recipients='.urlencode($recipients). + '&message='.urlencode($message) . + (($this->reference != '') ? '&reference='.$this->reference : '') + ); + + $this->recipients = array(); + + list($headers, $xml) = preg_split("/(\r?\n){2}/", $result, 2); + $data = simplexml_load_string($xml); + + $this->success = ($data->item->success == 'true'); + return $this->success; + } + + protected function _auth_https_post($host, $path, $data) { + $fp = @fsockopen($host,80); + $buf = ''; + if ($fp) { + @fputs($fp, "POST $path HTTP/1.0\n"); + @fputs($fp, "Host: $host\n"); + @fputs($fp, "Content-type: application/x-www-form-urlencoded\n"); + @fputs($fp, "Content-length: " . strlen($data) . "\n"); + @fputs($fp, "Connection: close\n\n"); + @fputs($fp, $data); + while (!feof($fp)) { + $buf .= fgets($fp,128); + } + fclose($fp); + } + return $buf; + } +} +?> \ No newline at end of file diff --git a/classes/txtmsg/txtmsgSpryng.class.php b/classes/txtmsg/txtmsgSpryng.class.php new file mode 100644 index 00000000..f0fd03a6 --- /dev/null +++ b/classes/txtmsg/txtmsgSpryng.class.php @@ -0,0 +1,72 @@ +. + */ + +class txtmsgSpryng extends txtmsgCore { + // ========================================================================= + // [ Fields ] + // ========================================================================= + public $gateway = 1; + public $resultcode = null; + public $resultmessage = null; + public $success = false; + public $successcount = 0; + + // ========================================================================= + // [ Methods ] + // ========================================================================= + public function setGateway($gateway) { + $this->gateway = $gateway; + } + + public function sendSMS($message) { + $recipients = implode(',', $this->recipients); + + $result = $this->_auth_https_post('http://www.spryng.nl', '/SyncTextService', + '?OPERATION=send' . + '&USERNAME=' . $this->username . + '&PASSWORD=' . $this->password . + '&DESTINATION=' . $recipients . + '&SENDER=' . $this->originator . + '&BODY=' . $message . + '&SMSTYPE=' . 'BUSINESS' + ); + return $result; + } + + protected function _auth_https_post($host, $path, $data) { + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $host . $path . $data); + //curl_setopt($ch, CURLOPT_HEADER, 1); + //curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + $data = curl_exec($ch); + curl_close($ch); + return $data; + } +} + +?> \ No newline at end of file diff --git a/config.inc.php b/config.inc.php new file mode 100644 index 00000000..6bafc901 --- /dev/null +++ b/config.inc.php @@ -0,0 +1,71 @@ +. + */ + +######################################## +# +# START SERVER MONITOR CONFIGURATION +# +######################################## + +// Database information +// Prefix used for tables +define('SM_DB_PREFIX', 'monitor_'); +// Database username +define('SM_DB_USER', 'db_user'); +// Database password +define('SM_DB_PASS', 'db_pass'); +// Database name +define('SM_DB_NAME', 'db_name'); +// Database host +define('SM_DB_HOST', 'localhost'); + +######################################## +# +# END SERVER MONITOR CONFIGURATION +# +######################################## + +// Include paths +// Tell the script where to find the templates, css files and javascript files. +// If you haven't changed anything to the structure you should leave these unchanged +define('SM_PATH_TPL', 'tpl/'); +define('SM_PATH_CSS', 'inc/'); +define('SM_PATH_JS', 'inc/'); + +error_reporting(0); +ini_set('display_errors', '0'); + +require 'functions.inc.php'; +$db = new smDatabase(); + +sm_load_conf(); + +$lang = sm_get_conf('language'); + +if(!$lang) { + $lang = 'en'; +} +sm_load_lang($lang); + +?> \ No newline at end of file diff --git a/cron/status.cron.php b/cron/status.cron.php new file mode 100644 index 00000000..8b930fd1 --- /dev/null +++ b/cron/status.cron.php @@ -0,0 +1,70 @@ +. + */ + +// include main configuration and functionality +require_once dirname(__FILE__) . '/../config.inc.php'; + +// get the active servers from database +$servers = $db->select( + SM_DB_PREFIX.'servers', + array('active' => 'yes'), + array('server_id', 'ip', 'port', 'label', 'type', 'status', 'active', 'email', 'sms') +); + +$updater = new smUpdaterStatus(); + +foreach ($servers as $server) { + $status_org = $server['status']; + // remove the old status from the array to avoid confusion between the new and old status + unset($server['status']); + + $updater->setServer($server, $status_org); + + // check server status + // it returns the new status, and performs the update check automatically. + $status_new = $updater->getStatus(); + // notify the nerds if applicable + $updater->notify(); + + // update server status + $save = array( + 'last_check' => date('Y-m-d H:i:s'), + 'status' => $status_new, + 'error' => $updater->getError(), + 'rtime' => $updater->getRtime(), + ); + + // if the server is on, add the last_online value + if($save['status'] == 'on') { + $save['last_online'] = date('Y-m-d H:i:s'); + } + + $db->save( + SM_DB_PREFIX . 'servers', + $save, + array('server_id' => $server['server_id']) + ); +} + +?> \ No newline at end of file diff --git a/docs/CHANGELOG b/docs/CHANGELOG new file mode 100644 index 00000000..6c84ce5b --- /dev/null +++ b/docs/CHANGELOG @@ -0,0 +1,25 @@ +######################### +# +# Version 2.0.0 +# October 19, 2009 +# +######################### +- server type ("service" or "website") +- different types of notification +- new text message gateways +- code rewrite +- new layout +- check for updates function + +######################### +# +# Version 1.0.1 +# September 18, 2008 +# +######################### + + +- log.php + tpl/log.tpl.html + select order by clause used datetime field after DATE_FORMAT had been performed, + resulting in a wrong list of log entries shown \ No newline at end of file diff --git a/docs/COPYING b/docs/COPYING new file mode 100644 index 00000000..20d40b6b --- /dev/null +++ b/docs/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/docs/README b/docs/README new file mode 100644 index 00000000..c75dea11 --- /dev/null +++ b/docs/README @@ -0,0 +1,176 @@ + PHP Server Monitor v2.0.0 + http://phpservermon.sourceforge.net + Copyright (c) 2008-2009 Pepijn Over + + PHP Server Monitor is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHP Server Monitor is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PHP Server Monitor. If not, see . + +################# +# # +# SUMMARY # +# # +################# + +PHP Server Monitor is a script that checks whether the servers on your list are up +and running on the selected ports. +It comes with a web based user interface where you can add and remove servers or websites from the MySQL database, +and you can manage users for each server with a mobile number and email address. + +With version 2.0 there's the support for websites as well. On the "Add server" page, you can choose +whether it's a "service" or a "website": +* service + A connection will be made to the entered ip or domain, on the given port. This way you can check if certain + services on your machine are still running. To check your IMAP service for example, enter port 143. + +* website + The previous version only tried to establish a connection to the server on port 80. If you are running multiple + websites on 1 machine, there was no proper way to check each website for possible errors. Also it was impossible to make + sure your site was really up and running, all you knew was that the server was still online. + This function takes care of that. + You can enter a link to a website (for example http://sourceforge.net/index.php), it will use cURL to open the website and + check the HTTP status code (see http://en.wikipedia.org/wiki/List_of_HTTP_status_codes for details). + If the HTTP status code is in the 4xx range, it means an error occured and the website is not accesible to the public. + In that case the script will return a "status offline", and will start sending out notifications. + +Each server has it's own settings regarding notification. +You can choose for email notification or text message (SMS). As of version 2.0, there are 3 gateways +available: +* Mollie - http://www.mollie.nl +* Spryng - http://www.spryng.nl +* Inetworx - http://www.inetworx.ch +For these gateways you need an account with sufficient credits. + +If logging is enabled in the configuration, it will log any connection errors, emails and text messages sent. +The latest log records will be displayed on your web interface. +The cron/status.cron.php can be added as a cronjob which will keep the server status up to date. + +I'd appreciate any feedback you might have regarding this script. Please leave it on the sourceforge +project page (tracker), or send me an email (see top of file for link). + +################# +# # +# DOWNLOAD # +# # +################# + +The latest version can be found at http://phpservermon.sourceforge.net + + +################# +# # +# REQUIREMENTS # +# # +################# + + 1. php 5 + 2. MySQL Database + 3. FTP access + + +################# +# # +# INSTALL # +# # +################# + +By default the PHP Server Monitor does not come with any security methods. After uploading these files to +your public html folder these will be visible to everyone on the web. It is recommended to put a password +on this folder to prevent other people from playing with your tool. An example .htaccess login script can +be found in the example/ dir. To create your own username and password for the .htpasswd file, see +http://www.htaccesstools.com/htpasswd-generator/ + + 1. Configuration + Open the config.php file with a plain text editor such as Notepad. + The first thing to do now in order to get started, is to get your database login information + right. The information is stored using php's define() function. To change these values correctly, only + update the second parameter of the function. + For example: + define('SM_DB_USER', 'db_user'); + To change your username you should ONLY change the 'db_user' part. Do NOT remove the quotes around + your username as that will result in an error. + + 2. Upload files + The next step is to get your files onto your webserver where you can reach them. You can rename the + folder of the server monitor without trouble, but if you change the structure please make sure + to update the settings in the config.php file + + 3. Run install.php + Once your database login information is correct, you can run the install.php script located in the + root dir. This script will create all the database tables you will need. + After running the install.php script you can remove it. + + 4. Configure your installation + Open the main page of the server monitor, by simply calling index.php. In the menu on the top find "config", + it'll open a page where you can change the necessary information for your tool. + + 5. [optional] Add a cronjob + In order to keep the server monitor up to date, the monitor.php file has to run regulary. If you're running + this on a linux machine, the easiest way is to add a cronjob. If it's your own server or you have + shell access and permission to open the crontab, locate the "crontab" file + (usually in /etc/crontab, but depends on distro). Open the file (vi /etc/crontab), and add the following + (change the paths to match your installation directories): + + #server monitor every 15 min + */15 * * * * root /usr/bin/php /var/www/html/phpservermon/cron/status.cron.php + + As you can see, this line will run the status.cron.php script every 15 minutes. Change the line to suit your + needs. If you do not have shell access, ask your webhosting to set it up for you. + + 6. Voila! + + +################# +# # +# CUSTOMIZING # +# # +################# + + 1. Language + The server monitor uses language files. That means that any regular text you see on the screen can easily be + changed without having to digg through the code. These language files are stored in the directory "lang". + The language that's being used by the monitor is defined in the config table. If you like + you can make changes to the language file or even add a new one. + + 1.1 Changing the email or text message + Open the language file that corresponds to the selected language + (default is English ("en.lang.php")). Scroll all the way to the bottom until you spot this line: + + 'notifications' => array( + + After that you'll see the lines that hold the notification messages. For example: + + 'off_sms' => 'Server \'%LABEL%\' is DOWN: ip=%IP%, port=%PORT%. Error=%ERROR%', + + The first part of this line, 'off_sms', is the name of the notification. You should not change this. The + second part is the actual message. There are a few variables you can use in your message: + - %LABEL% The name of the server + - %IP% The ip of the server + - %PORT% The port of the server + - %ERROR% This one only works for the off_* messages and contains the error returned by the + monitor + + 1.2 Adding a new language + It's not the easiest thing to add a new language to the monitor, but if you can spare a few minutes of your time + to send in a translation, it can be added to a future release. + - Create a new file in the directory "lang" named "mylanguage.lang.php". + - Copy the contents of the file "en.lang.php" to your new file. + - Translate the English stuff to your own language. + - Send a copy to ipdope[at]users.sourceforge.net so I can add it to the next release :) + +################# +# # +# CREDITS # +# # +################# + +1. classes/phpmailer.class.php - Brent R. Matzelle \ No newline at end of file diff --git a/example/.htaccess b/example/.htaccess new file mode 100644 index 00000000..93aa72b4 --- /dev/null +++ b/example/.htaccess @@ -0,0 +1,4 @@ +AuthUserFile ".htpasswd" +AuthType Basic +AuthName "Server Monitor" +require valid-user diff --git a/example/.htpasswd b/example/.htpasswd new file mode 100644 index 00000000..d37bda04 --- /dev/null +++ b/example/.htpasswd @@ -0,0 +1 @@ +admin:dGRkPurkuWmW2 \ No newline at end of file diff --git a/functions.inc.php b/functions.inc.php new file mode 100644 index 00000000..fc9662f9 --- /dev/null +++ b/functions.inc.php @@ -0,0 +1,220 @@ +. + */ + +/** + * + * Autoload + * + */ +function __autoload($class) { + // first check if a subdir exists for the class + // it splits using uppercase chars + preg_match_all("/(\P{Lu}+)|(\p{Lu}+\P{Lu}*)/", $class, $subdir_matches); + + if(!empty($subdir_matches) && count($subdir_matches[0]) > 1) { + // okay we have some upper case, lets see if a dir exists + $dir = dirname(__FILE__) . '/classes/' . trim($subdir_matches[0][0]); + $file = $dir . '/' . trim($class) . '.class.php'; + + if(is_dir($dir) && file_exists($file)) { + require $file; + return $file; + } + } else { + $file = dirname(__FILE__).'/classes/'.trim(strtolower($class)).'.class.php'; + + if(file_exists($file)){ + require $file; + return $file; + } + } + + trigger_error("KERNEL_ERR : Unable to find file:\n\t\t[$file]\n\t associated with class:\n\t\t$class", E_USER_ERROR); + return false; +} + +############################################### +# +# Language functions +# +############################################### + +/** + * Retrieve language settings from the selected language file + * + * @return unknown + */ +function sm_get_lang() { + $args = func_get_args(); + + if(empty($args)) return $GLOBALS['sm_lang']; + + $result = null; + $node = null; + + if($args) { + $node = '$GLOBALS[\'sm_lang\'][\'' . implode('\'][\'', $args) . '\']'; + } + eval('if (isset('.$node.')) $result = '.$node.';'); + + return $result; +} + +function sm_load_lang($lang) { + $lang_file = dirname(__FILE__) . '/lang/' . $lang . '.lang.php'; + + if(!file_exists($lang_file)) { + die('unable to load language file: ' . $lang_file); + } + + require $lang_file; + + $GLOBALS['sm_lang'] = $sm_lang; +} + +function sm_get_conf($key) { + $result = (isset($GLOBALS['sm_config'][$key])) ? $GLOBALS['sm_config'][$key] : null; + + return $result; +} + +function sm_load_conf() { + global $db; + + // load config from database into global scope + $GLOBALS['sm_config'] = array(); + $config_db = $db->select(SM_DB_PREFIX . 'config', null, array('key', 'value')); + foreach($config_db as $setting) { + $GLOBALS['sm_config'][$setting['key']] = $setting['value']; + } + + if(empty($GLOBALS['sm_config']) && basename($_SERVER['SCRIPT_NAME']) != 'install.php') { + // no config found, go to install page + die('Failed to load config table. Please run the install.php file'); + } +} + + +############################################### +# +# Miscellaneous functions +# +############################################### + +/** + * This function merely adds the message to the log table. It does not perform any checks, + * everything should have been handled when calling this function + * + * @param string $server_id + * @param string $message + */ +function sm_add_log($server_id, $type, $message, $user_id = null) { + global $db; + + $db->save( + SM_DB_PREFIX.'log', + array( + 'server_id' => $server_id, + 'type' => $type, + 'message' => $message, + 'user_id' => ($user_id === null) ? '' : $user_id, + ) + ); +} + +/** + * Parses a string from the language file with the correct variables replaced in the message + * + * @param string $status is either 'on' or 'off' + * @param string $type is either 'sms' or 'email' + * @param array $server information about the server which may be placed in a message: %KEY% will be replaced by your value + * @return string parsed message + */ +function sm_parse_msg($status, $type, $vars) { + $message = ''; + + $message = sm_get_lang('notifications', $status . '_' . $type); + + if(!$message) { + return $message; + } + $vars['date'] = date('Y-m-d H:i:s'); + + foreach($vars as $k => $v) { + $message = str_replace('%' . strtoupper($k) . '%', $v, $message); + } + + return $message; +} + +function sm_curl_get($href) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + curl_setopt($ch, CURLOPT_URL, $href); + $result = curl_exec($ch); + curl_close($ch); + + return $result; +} + +function sm_check_updates() { + $latest = sm_curl_get('http://phpservermon.neanderthal-technology.com/version'); + $current = sm_get_conf('version'); + + if((int) $current < (int) $latest) { + // new update available + return true; + } + return false; +} + +############################################### +# +# Debug functions +# +############################################### + +/** + * Only used for debugging and testing + * + * @param mixed $arr + */ +function pre($arr = null) { + echo "
";
+	if ($arr === null) debug_print_backtrace();
+	print_r($arr);
+	echo "
"; +} + +function sm_no_cache() { + header("Expires: Mon, 20 Dec 1998 01:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: no-cache, must-revalidate"); + header("Pragma: no-cache"); +} + +?> \ No newline at end of file diff --git a/img/delete.png b/img/delete.png new file mode 100644 index 00000000..6fc4d3b2 Binary files /dev/null and b/img/delete.png differ diff --git a/img/edit.png b/img/edit.png new file mode 100644 index 00000000..05711a09 Binary files /dev/null and b/img/edit.png differ diff --git a/img/off.png b/img/off.png new file mode 100644 index 00000000..87789faa Binary files /dev/null and b/img/off.png differ diff --git a/img/on.png b/img/on.png new file mode 100644 index 00000000..34c6512a Binary files /dev/null and b/img/on.png differ diff --git a/img/opensource.png b/img/opensource.png new file mode 100644 index 00000000..7dffde8b Binary files /dev/null and b/img/opensource.png differ diff --git a/inc/monitor.css b/inc/monitor.css new file mode 100644 index 00000000..22c3ba40 --- /dev/null +++ b/inc/monitor.css @@ -0,0 +1,160 @@ +body { + color: #000000; + background-color: #FFFFFF; + font-family: Arial; + font-size: 12px; +} + +.main { + margin: 0 auto; + width: 900px; +} +.header { + width: 900px; + height: 101px; +} +.opensource { + width: 101px; + height: 101px; + float: left; +} +.menu { + float: left; + margin-left: 50px; + height: 101px; + width: 749px; + background-color: #B3B3B3; + text-align: center; +} +.menu h1 { + margin-top: 10px; + font-size: 30px; + color: #C6C6C6; + font-style: italic; + letter-spacing: 10px; +} +.menu ul { + list-style-type: square; +} +.menu ul li { + float: left; + width: 100px; + color: #FFFFFF; +} +.active { + font-weight: bold; +} +a, +a:visited { + color: #FFFFFF; + text-decoration: none; +} +a:hover, +a:active { + color: #FFFFFF; + text-decoration: underline; +} +.container { + border-left: 0; + border-right: 1px; + border-top: 0; + border-bottom: 0; + border-color: #B3B3B3; + border-style: solid; + padding: 40px 20px; +} + +.container h2 { + text-align: center; + font-size: 20px; + margin: 0 0 10px 0; + color: #286638; +} + +.container table { + border: 1px solid #B3B3B3; + margin: 0 auto; + width: 860px; +} + +.footer { + color: #666666; + background-color: #B3B3B3; + font-size: 10px; + text-align: center; +} + +div.multiple_select_container { + background: #fff; + overflow: auto; + height: 80px; + width: 160px; + border-left: 1px solid #404040; + border-top: 1px solid #404040; + border-bottom: 1px solid #d4d0c8; + border-right: 1px solid #d4d0c8; +} + +div.multiple_select_container label { + padding-right: 3px; + display: block; +} + +div.multiple_select_container label.active { + background-color: #0A246A; + color: #FFFFFF; +} + +div.multiple_select_container label.inactive { + background-color: #FFFFFF; + color: #000000; +} + +.odd td { + background-color: #FFFFFF; + padding-left: 15px; +} + +.even td { + background-color: #EEEEEE; + padding-left: 15px; +} + +.head td { + background-color: #EEEEEE; + text-align: center; + font-weight: bold; +} + +img { + border: 0; +} + +.message, +.message a, +.message a:visited, +.message a:hover, +.message a:active { + color: #FF0000; + text-align: center; + font-weight: bold; +} + +.small { + font-size: 10px; +} + +#update { + border: 1px solid #000000; + width: 500px; + margin: 0 auto; + padding: 10px; + margin-bottom: 30px; +} +#update a, +#update a:hover, +#update a:active, +#update a:visited { + color: #000000; + text-decoration: underline; +} \ No newline at end of file diff --git a/inc/monitor.js b/inc/monitor.js new file mode 100644 index 00000000..439b36e5 --- /dev/null +++ b/inc/monitor.js @@ -0,0 +1,33 @@ +function sm_delete(id, type) { + var del = confirm("Are you sure you want to delete this record?"); + if (del == true) { + var loc = 'index.php?delete=' + id + '&type=' + type; + window.location = loc; + } +} +function sm_highlight_label(elem) { + label = elem.parentNode; + + if (elem.checked) { + label.className = 'active'; + } else { + label.className = 'inactive'; + } +} + +function sm_switch_tab(active) { + categories = new Array(); + categories[0] = 'status'; + categories[1] = 'email'; + categories[2] = 'sms'; + + for(var i = 0; i < categories.length; i++) { + if(categories[i] == active) { + document.getElementById('tabs_content_' + categories[i]).style.display = 'block'; + document.getElementById('tabs_title_' + categories[i]).className = 'selected'; + } else { + document.getElementById('tabs_content_' + categories[i]).style.display = 'none'; + document.getElementById('tabs_title_' + categories[i]).className = ''; + } + } +} \ No newline at end of file diff --git a/inc/tabs.css b/inc/tabs.css new file mode 100644 index 00000000..d36301f1 --- /dev/null +++ b/inc/tabs.css @@ -0,0 +1,47 @@ +.tabs ul { + list-style: none; + padding: 0; + margin: 0; + padding-left: 240px; +} + +.tabs li { + float: left; + border: 1px solid #FFFFFF; + border-bottom-width: 0; + margin: 0 2px 0 0; +} + +.tabs a { + text-decoration: none; + display: block; + background: #A0A0A0; + padding: 0.24em 1em; + width: 8em; + text-align: center; + text-transform: uppercase; +} + +.tabs a:hover { + background: #828282; +} + +.tabs .selected { + color: #FFFFFF; +} + +.tabs .selected a { + position: relative; + top: 1px; + background: #FFFFFF; + color: #000000; + font-weight: bold; + border: 1px solid #A0A0A0; + border-bottom: 0px; +} + +.tabs_content { + border: 1px solid #FFFFFF; + clear: both; + padding: 0 1em; +} \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 00000000..74535506 --- /dev/null +++ b/index.php @@ -0,0 +1,97 @@ +. + */ + +require_once 'config.inc.php'; + +sm_no_cache(); + +if(isset($_GET['action']) && $_GET['action'] == 'check') { + require 'cron/status.cron.php'; + header('Location: index.php'); +} + +$tpl = new smTemplate(); + +$tpl->addJS('monitor.js'); +$tpl->addCSS('monitor.css'); + +// check for updates? +if(sm_get_conf('show_update')) { + $last_update = sm_get_conf('last_update_check'); + + if((time() - (7 * 24 * 60 * 60)) > $last_update) { + // been more than a week since update, lets go + $need_upd = sm_check_updates(); + // update "update-date" + $db->save(SM_DB_PREFIX . 'config', array('value' => time()), array('key' => 'last_update_check')); + + if($need_upd) { + // yay, new update available =D + $tpl->addTemplateData( + 'main', + array( + 'update_available' => '
'.sm_get_lang('system', 'update_available').'
', + ) + ); + } + } +} + + +$type = (!isset($_GET['type'])) ? 'servers' : $_GET['type']; +$allowed_types = array('servers', 'users', 'log', 'config'); + +// add some default vars +$tpl->addTemplateData( + 'main', + array( + 'title' => strtoupper(sm_get_lang('system', 'title')), + 'subtitle' => sm_get_lang('system', $type), + 'label_servers' => sm_get_lang('system', 'servers'), + 'label_users' => sm_get_lang('system', 'users'), + 'label_log' => sm_get_lang('system', 'log'), + 'label_config' => sm_get_lang('system', 'config'), + 'label_update' => sm_get_lang('system', 'update'), + 'label_help' => sm_get_lang('system', 'help'), + 'active_' . $type => 'active', + ) +); + +// make sure user selected a valid type. if so, include the file and add to template +if(in_array($type, $allowed_types)) { + eval('$mod = new mod'.ucfirst($type).'();'); + + $html = $mod->createHTML(); + + $tpl->addTemplatedata( + 'main', + array( + 'content' => $html, + ) + ); +} + +echo $tpl->display('main'); + +?> \ No newline at end of file diff --git a/install.php b/install.php new file mode 100644 index 00000000..14a9394c --- /dev/null +++ b/install.php @@ -0,0 +1,143 @@ +. + */ + +// this script creates all the database tables required for server monitor +error_reporting(0x0ffffff); + +require 'config.inc.php'; +$tpl = new smTemplate(); +$tpl->addCSS('monitor.css', 'install'); + +$tpl->newTemplate('install', 'install.tpl.html'); + + +if(!is_resource($db->getLink())) { + // no valid db info + $tpl->addTemplatedata( + 'install', + array('error' => 'Couldn\'t connect to database!') + ); + echo $tpl->display('install'); + die(); +} + +$tables = array( + 'users' => + array( + 0 => "CREATE TABLE `" . SM_DB_PREFIX . "users` ( + `user_id` int(11) NOT NULL auto_increment, + `server_id` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `mobile` varchar(15) NOT NULL, + `email` varchar(255) NOT NULL, + PRIMARY KEY (`user_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", + 1 => "INSERT INTO `" . SM_DB_PREFIX . "users` (`server_id`, `name`, `mobile`, `email`) VALUES ('1,2', 'example_user', '0123456789', 'user@example.com')" + ), + 'log' => + array( + 0 => "CREATE TABLE `" . SM_DB_PREFIX . "log` ( + `log_id` int(11) NOT NULL auto_increment, + `server_id` int(11) NOT NULL, + `type` enum('status','email','sms') NOT NULL, + `message` varchar(255) NOT NULL, + `datetime` timestamp NOT NULL default CURRENT_TIMESTAMP, + `user_id` varchar(255) NOT NULL, + PRIMARY KEY (`log_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", + ), + 'servers' => + array( + 0 => "CREATE TABLE `" . SM_DB_PREFIX . "servers` ( + `server_id` int(11) NOT NULL auto_increment, + `ip` varchar(100) NOT NULL, + `port` int(5) NOT NULL, + `label` varchar(255) NOT NULL, + `type` enum('service','website') NOT NULL default 'service', + `status` enum('on','off') NOT NULL default 'on', + `error` varchar(255) NOT NULL, + `rtime` FLOAT(9, 7) NOT NULL, + `last_online` datetime NOT NULL, + `last_check` datetime NOT NULL, + `active` enum('yes','no') NOT NULL default 'yes', + `email` enum('yes','no') NOT NULL default 'yes', + `sms` enum('yes','no') NOT NULL default 'no', + PRIMARY KEY (`server_id`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", + 1 => "INSERT INTO `" . SM_DB_PREFIX . "servers` (`ip`, `port`, `label`, `type`, `status`, `error`, `rtime`, `last_online`, `last_check`, `active`, `email`, `sms`) VALUES ('http://sourceforge.net/index.php', 80, 'SourceForge', 'website', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes'), ('smtp.gmail.com', 465, 'Gmail SMTP', 'service', 'on', '', '', '0000-00-00 00:00:00', '0000-00-00 00:00:00', 'yes', 'yes', 'yes')", + ), + 'config' => + array( + 0 => "CREATE TABLE `" . SM_DB_PREFIX . "config` ( + `config_id` int(11) NOT NULL AUTO_INCREMENT, + `key` varchar(255) NOT NULL, + `value` varchar(255) NOT NULL, + PRIMARY KEY (`config_id`), + KEY `key` (`key`(50)) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8;", + 1 => "INSERT INTO `" . SM_DB_PREFIX . "config` (`config_id`, `key`, `value`) VALUES + (null, 'language', 'en'), + (null, 'email_status', '1'), + (null, 'email_from_email', 'monitor@example.org'), + (null, 'email_from_name', 'Server Monitor'), + (null, 'sms_status', '1'), + (null, 'sms_gateway', 'mollie'), + (null, 'sms_gateway_username', 'username'), + (null, 'sms_gateway_password', 'password'), + (null, 'sms_from', '1234567890'), + (null, 'alert_type', 'status'), + (null, 'log_status', '1'), + (null, 'log_email', '1'), + (null, 'log_sms', '1'), + (null, 'version', '200'), + (null, 'show_update', '1'), + (null, 'last_update_check', '0');", + ) +); + +$result = array(); + +foreach($tables as $name => $queries) { + $if_table_exists = $db->query('SHOW TABLES LIKE \'' . SM_DB_PREFIX . $name.'\''); + + if(!empty($if_table_exists)) { + $message = 'Table ' . SM_DB_PREFIX . $name . ' already exists in your database!'; + } else { + $message = ''; + + foreach($queries as $query) { + $message .= 'Executing ' . $query . '

'; + $db->query($query); + } + } + + $result[] = array( + 'name' => $name, + 'result' => $message, + ); +} +$tpl->addTemplateDataRepeat('install', 'tables', $result); + +echo $tpl->display('install'); +?> \ No newline at end of file diff --git a/lang/en.lang.php b/lang/en.lang.php new file mode 100644 index 00000000..98ebb31a --- /dev/null +++ b/lang/en.lang.php @@ -0,0 +1,132 @@ +. + */ + +$sm_lang = array( + 'system' => array( + 'title' => 'Server Monitor', + 'servers' => 'servers', + 'users' => 'users', + 'log' => 'log', + 'update' => 'update', + 'config' => 'config', + 'help' => 'help', + 'action' => 'Action', + 'save' => 'Save', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'deleted' => 'Record has been deleted', + 'date' => 'Date', + 'message' => 'Message', + 'yes' => 'Yes', + 'no' => 'No', + 'edit' => 'Edit', + 'insert' => 'Insert', + 'add_new' => 'Add new?', + 'update_available' => 'A new update is available from http://phpservermon.sourceforge.net.', + ), + 'users' => array( + 'user' => 'user', + 'name' => 'Name', + 'mobile' => 'Mobile', + 'email' => 'Email', + 'updated' => 'User updated.', + 'inserted' => 'User added.', + ), + 'log' => array( + 'title' => 'Log entries', + 'type' => 'Type', + 'status' => 'status', + 'email' => 'email', + 'sms' => 'sms', + ), + 'servers' => array( + 'server' => 'Server', + 'label' => 'Label', + 'domain' => 'Domain/IP', + 'port' => 'Port', + 'type' => 'Type', + 'last_check' => 'Last check', + 'last_online' => 'Last online', + 'monitoring' => 'Monitoring', + 'send_email' => 'Send Email', + 'send_sms' => 'Send SMS', + 'updated' => 'Server updated.', + 'inserted' => 'Server added.', + 'rtime' => 'Response time', + ), + 'config' => array( + 'general' => 'General', + 'language' => 'Language', + 'show_update' => 'Check for new updates weekly?', + 'english' => 'English', + 'dutch' => 'Dutch', + 'email_status' => 'Allow sending email?', + 'email_from_email' => 'Email from address', + 'email_from_name' => 'Email from name', + 'sms_status' => 'Allow sending text messages?', + 'sms_gateway' => 'Gateway to use for sending messages', + 'sms_gateway_mollie' => 'Mollie', + 'sms_gateway_spryng' => 'Spryng', + 'sms_gateway_inetworx' => 'Inetworx', + 'sms_gateway_username' => 'Gateway username', + 'sms_gateway_password' => 'Gateway password', + 'sms_from' => 'Sender\'s phone number', + 'alert_type' => + 'Select when you\'d like to be notified.
'. + '
'. + '1) Status change
'. + 'You will receive a notifcation when a server has a change in status. So from online -> offline or offline -> online.
'. + '2) Offline
'. + 'You will receive a notification when a server goes offline for the *FIRST TIME ONLY*. For example, '. + 'your cronjob is every 15 mins and your server goes down at 1 am and stays down till 6 am. '. + 'You will get 1 notification at 1 am and thats it.
'. + '3) Always
'. + 'You will receive a notification every time the script runs and a site is down, even if the site has been '. + 'offline for hours.'. + '
', + + 'alert_type_status' => 'Status change', + 'alert_type_offline' => 'Offline', + 'alert_type_always' => 'Always', + 'log_status' => 'Log status
If log status is set to TRUE, the monitor will log the event whenever the Notification settings are passed
', + 'log_email' => 'Log emails sent by the script?', + 'log_sms' => 'Log text messages sent by the script?', + 'updated' => 'The configuration has been updated.', + 'settings_email' => 'Email settings', + 'settings_sms' => 'Text message settings', + 'settings_notification' => 'Notification settings', + 'settings_log' => 'Log settings', + ), + // for newlines in the email messages use
+ 'notifications' => array( + 'off_sms' => 'Server \'%LABEL%\' is DOWN: ip=%IP%, port=%PORT%. Error=%ERROR%', + 'off_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is DOWN', + 'off_email_body' => "Failed to connect to the following server:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Error: %ERROR%
Date: %DATE%", + 'on_sms' => 'Server \'%LABEL%\' is RUNNING: ip=%IP%, port=%PORT%', + 'on_email_subject' => 'IMPORTANT: Server \'%LABEL%\' is RUNNING', + 'on_email_body' => "Server '%LABEL%' is running again:

Server: %LABEL%
IP: %IP%
Port: %PORT%
Date: %DATE%", + ), +); + +?> \ No newline at end of file diff --git a/lang/nl.lang.php b/lang/nl.lang.php new file mode 100644 index 00000000..f38fcb26 --- /dev/null +++ b/lang/nl.lang.php @@ -0,0 +1,131 @@ +. + */ + +$sm_lang = array( + 'system' => array( + 'title' => 'Server Monitor', + 'servers' => 'servers', + 'users' => 'gebruikers', + 'log' => 'log', + 'update' => 'update', + 'config' => 'config', + 'help' => 'help', + 'action' => 'Actie', + 'save' => 'Opslaan', + 'edit' => 'Wijzig', + 'delete' => 'Verwijder', + 'deleted' => 'Record is verwijderd', + 'date' => 'Datum', + 'message' => 'Bericht', + 'yes' => 'Ja', + 'no' => 'Nee', + 'edit' => 'Wijzig', + 'insert' => 'Voeg toe', + 'add_new' => 'Voeg toe?', + 'update_available' => 'Een nieuwe update is beschikbaar op http://phpservermon.sourceforge.net.', + ), + 'users' => array( + 'user' => 'gebruiker', + 'name' => 'Naam', + 'mobile' => 'Mobiel', + 'email' => 'Email', + 'updated' => 'Gebruiker gewijzigd.', + 'inserted' => 'Gebruiker toegevoegd.', + ), + 'log' => array( + 'title' => 'Log entries', + 'type' => 'Type', + 'status' => 'status', + 'email' => 'email', + 'sms' => 'sms', + ), + 'servers' => array( + 'server' => 'Server', + 'label' => 'Label', + 'domain' => 'Domein/IP', + 'port' => 'Poort', + 'type' => 'Type', + 'last_check' => 'Laatst gecontroleerd', + 'last_online' => 'Laatst online', + 'monitoring' => 'Monitoring', + 'send_email' => 'Stuur email', + 'send_sms' => 'Stuur SMS', + 'updated' => 'Server gewijzigd.', + 'inserted' => 'Server toegevoegd.', + 'rtime' => 'Response tijd', + ), + 'config' => array( + 'general' => 'Algemeen', + 'language' => 'Taal', + 'show_update' => 'Check for new updates weekly?', + 'english' => 'Engels', + 'dutch' => 'Nederlands', + 'email_status' => 'Sta email berichten toe?', + 'email_from_email' => 'Email van adres', + 'email_from_name' => 'Email van naam', + 'sms_status' => 'Sta SMS berichten toe?', + 'sms_gateway' => 'Gateway voor het sturen van SMS', + 'sms_gateway_mollie' => 'Mollie', + 'sms_gateway_spryng' => 'Spryng', + 'sms_gateway_inetworx' => 'Inetworx', + 'sms_gateway_username' => 'Gateway gebruikersnaam', + 'sms_gateway_password' => 'Gateway wachtwoord', + 'sms_from' => 'Telefoonnummer afzender', + 'alert_type' => + 'Selecteer wanneer je een notificatie wilt.
'. + '
'. + '1) Status verandering
'. + 'Je ontvangt alleen bericht wanneer een server van status verandert. Dus van online -> offline of offline -> online.
'. + '2) Offline
'. + 'Je ontvangt bericht wanneer een server offline gaat voor de *EERSTE KEER*. Bijvoorbeeld, '. + 'je cronjob draait iedere 15 min en je server gaat down om 01:00 en blijft offline tot 06:00. '. + 'Je krijgt 1 bericht om 01:00 en dat is het.
'. + '3) Altijd
'. + 'Je krijgt een bericht elke keer dat het script draait en een website is down, ook al is de site al een paar uur offline.'. + '
', + + 'alert_type_status' => 'Status verandering', + 'alert_type_offline' => 'Offline', + 'alert_type_always' => 'Altijd', + 'log_status' => 'Log status
Als de log status op TRUE staat, zal de monitor een log aanmaken elke keer dat hij door de notificatie instellingen komt
', + 'log_email' => 'Log emails verstuurd bij het script?', + 'log_sms' => 'Log sms berichten verstuurd bij het script?', + 'updated' => 'De configuratie is gewijzigd.', + 'settings_email' => 'Email instellingen', + 'settings_sms' => 'SMS instellingen', + 'settings_notification' => 'Notificatie instellingen', + 'settings_log' => 'Log instellingen', + ), + // for newlines in the email messages use
+ 'notifications' => array( + 'off_sms' => 'Server %LABEL% is DOWN: ip=%IP%, poort=%PORT%. Fout=%ERROR%', + 'off_email_subject' => 'BELANGRIJK: Server %LABEL% is DOWN', + 'off_email_body' => "De server kon niet worden bereikt:

Server: %LABEL%
IP: %IP%
Poort: %PORT%
Fout: %ERROR%
Datum: %DATE%", + 'on_sms' => 'Server %LABEL% is RUNNING: ip=%IP%, poort=%PORT%', + 'on_email_subject' => 'BELANGRIJK: Server %LABEL% is RUNNING', + 'on_email_body' => "Server %LABEL% is weer online:

Server: %LABEL%
IP: %IP%
Poort: %PORT%
Datum: %DATE%", + ), +); + +?> \ No newline at end of file diff --git a/tpl/config.tpl.html b/tpl/config.tpl.html new file mode 100644 index 00000000..e07da956 --- /dev/null +++ b/tpl/config.tpl.html @@ -0,0 +1,125 @@ + +{config_update} +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {message} +
+ {label_general} +
{label_language} + +
{label_show_update}
+ {label_settings_email} +
{label_email_status}
{label_email_from_name}
{label_email_from_email}
+ {label_settings_sms} +
{label_sms_status}
{label_sms_gateway} + +
{label_sms_gateway_username}
{label_sms_gateway_password}
{label_sms_from}
+ {label_settings_notification} +
{label_alert_type} + +
+ {label_settings_log} +
{label_log_status}
{label_log_email}
{label_log_sms}
  + +
+
+ + + +
+ Please update! +
+ \ No newline at end of file diff --git a/tpl/install.tpl.html b/tpl/install.tpl.html new file mode 100644 index 00000000..98d8adc9 --- /dev/null +++ b/tpl/install.tpl.html @@ -0,0 +1,24 @@ + + + + + + PHP Server Monitor Installation script + + +

This script will create all the database tables required for PHP Server Monitor.

+

{error}

+ + +
+ Creating table {name}...

+ {result} +
+ + {tables} +
+ The installation is complete. Please check above if errors have occured.
+ If no errors have occured, you can remove this file and click here to go to your index + + + \ No newline at end of file diff --git a/tpl/log.tpl.html b/tpl/log.tpl.html new file mode 100644 index 00000000..0b887f6d --- /dev/null +++ b/tpl/log.tpl.html @@ -0,0 +1,42 @@ + + +
+{content_status} +
+ + + + + + + + + + + + + + + + + + + + + + + + + {entries} +
{logtitle}
{label_server}{label_message}{label_date}{label_users}
{server}{message}{datetime_format}{users}
+ \ No newline at end of file diff --git a/tpl/main.tpl.html b/tpl/main.tpl.html new file mode 100644 index 00000000..bf7c0f3b --- /dev/null +++ b/tpl/main.tpl.html @@ -0,0 +1,36 @@ + + + + + + PHP Server Monitor + + +
+ +
+

{subtitle}

+ {content} +
+ {update_available} + +
+ + + \ No newline at end of file diff --git a/tpl/servers.tpl.html b/tpl/servers.tpl.html new file mode 100644 index 00000000..8f5bcd05 --- /dev/null +++ b/tpl/servers.tpl.html @@ -0,0 +1,108 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {servers} +
 {label_label}{label_domain}{label_port}{label_type}{label_last_check}{label_rtime}{label_last_online}{label_monitoring}{label_send_email}{label_send_sms}{label_action}
{message}
{status}{label}{ip}{port}{type}{last_check}{rtime} s{last_online}{active}{email}{sms} + edit +   + delete +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

{titlemode}

{label_label}
{label_domain}
{label_port}
{label_type} + +
{label_monitoring} + +
{label_send_email} + +
{label_send_sms} + +
+ + + +
+
+ \ No newline at end of file diff --git a/tpl/users.tpl.html b/tpl/users.tpl.html new file mode 100644 index 00000000..7ce20f79 --- /dev/null +++ b/tpl/users.tpl.html @@ -0,0 +1,69 @@ + +
+ + + + + + + + + + + + + + + + + + + + + {users} +
{label_name}{label_mobile}{label_email}{label_servers}{label_action}
{message}
{name}{mobile}{email}{emp_servers} + edit +   + delete +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + +

{titlemode}

{label_name}
{label_mobile}
{label_email}
{label_servers} +
+ + + + {servers} +
+
+ + + +
+
+ \ No newline at end of file