diff --git a/src/lang/en_US.lang.php b/src/lang/en_US.lang.php index 621a6aed..570d32c2 100644 --- a/src/lang/en_US.lang.php +++ b/src/lang/en_US.lang.php @@ -247,6 +247,11 @@ $sm_lang = array( 'hour' => 'Hour', 'warning_threshold' => 'Warning threshold', 'warning_threshold_description' => 'Number of failed checks required before it is marked offline.', + 'ssl_cert_expiry_days' => 'SSL Certificate Validity', + 'ssl_cert_expiry_days_description' => 'The minimum remaining days the SSL certificate is still valid. Use 0 to + disable check.', + 'ssl_cert_expired' => 'SSL certificate expired since', + 'ssl_cert_expiring' => 'SSL certificate expiring:', 'chart_last_week' => 'Last week', 'chart_history' => 'History', 'chart_day_format' => '%Y-%m-%d', @@ -264,6 +269,8 @@ $sm_lang = array( 'error_server_ip_bad_website' => 'The website URL is not valid.', 'error_server_type_invalid' => 'The selected server type is invalid.', 'error_server_warning_threshold_invalid' => 'The warning threshold must be a valid integer greater than 0.', + 'error_server_ssl_cert_expiry_days' => 'The remaining days for SSL certificate validity must be a valid integer + greater than or equal to 0.', ), 'config' => array( 'general' => 'General', diff --git a/src/psm/Module/Server/Controller/AbstractServerController.php b/src/psm/Module/Server/Controller/AbstractServerController.php index 90f6e629..efc70072 100644 --- a/src/psm/Module/Server/Controller/AbstractServerController.php +++ b/src/psm/Module/Server/Controller/AbstractServerController.php @@ -85,6 +85,8 @@ abstract class AbstractServerController extends AbstractController `s`.`telegram`, `s`.`warning_threshold`, `s`.`warning_threshold_counter`, + `s`.`ssl_cert_expiry_days`, + `s`.`ssl_cert_expired_time`, `s`.`timeout`, `s`.`website_username`, `s`.`website_password`, diff --git a/src/psm/Module/Server/Controller/ServerController.php b/src/psm/Module/Server/Controller/ServerController.php index 097329fd..ab27dd68 100644 --- a/src/psm/Module/Server/Controller/ServerController.php +++ b/src/psm/Module/Server/Controller/ServerController.php @@ -208,6 +208,7 @@ class ServerController extends AbstractServerController 'edit_value_website_username' => $edit_server['website_username'], 'edit_value_website_password' => empty($edit_server['website_password']) ? '' : sha1($edit_server['website_password']), + 'edit_value_ssl_cert_expiry_days' => $edit_server['ssl_cert_expiry_days'], 'edit_type_selected_' . $edit_server['type'] => 'selected="selected"', 'edit_active_selected' => $edit_server['active'], 'edit_email_selected' => $edit_server['email'], @@ -281,6 +282,7 @@ class ServerController extends AbstractServerController 'header_name' => psm_POST('header_name', ''), 'header_value' => psm_POST('header_value', ''), 'warning_threshold' => intval(psm_POST('warning_threshold', 0)), + 'ssl_cert_expiry_days' => intval(psm_POST('ssl_cert_expiry_days', 1)), 'active' => in_array($_POST['active'], array('yes', 'no')) ? $_POST['active'] : 'no', 'email' => in_array($_POST['email'], array('yes', 'no')) ? $_POST['email'] : 'no', 'sms' => in_array($_POST['sms'], array('yes', 'no')) ? $_POST['sms'] : 'no', @@ -325,6 +327,7 @@ class ServerController extends AbstractServerController $server_validator->type($clean['type']); $server_validator->ip($clean['ip'], $clean['type']); $server_validator->warningThreshold($clean['warning_threshold']); + $server_validator->sslCertExpiryDays($clean['ssl_cert_expiry_days']); } catch (\InvalidArgumentException $ex) { $this->addMessage(psm_get_lang('servers', 'error_' . $ex->getMessage()), 'error'); return $this->executeEdit(); @@ -558,6 +561,8 @@ class ServerController extends AbstractServerController 'label_users' => psm_get_lang('servers', 'users'), 'label_warning_threshold' => psm_get_lang('servers', 'warning_threshold'), 'label_warning_threshold_description' => psm_get_lang('servers', 'warning_threshold_description'), + 'label_ssl_cert_expiry_days' => psm_get_lang('servers', 'ssl_cert_expiry_days'), + 'label_ssl_cert_expiry_days_description' => psm_get_lang('servers', 'ssl_cert_expiry_days_description'), 'label_action' => psm_get_lang('system', 'action'), 'label_save' => psm_get_lang('system', 'save'), 'label_go_back' => psm_get_lang('system', 'go_back'), diff --git a/src/psm/Util/Server/ServerValidator.php b/src/psm/Util/Server/ServerValidator.php index 63e5688b..18dc658b 100644 --- a/src/psm/Util/Server/ServerValidator.php +++ b/src/psm/Util/Server/ServerValidator.php @@ -154,4 +154,18 @@ class ServerValidator } return true; } + + /** + * Check SSL expiry days + * @param int $value + * @return boolean + * @throws \InvalidArgumentException + */ + public function sslCertExpiryDays($value) + { + if (!is_numeric($value) || $value < 0) { + throw new \InvalidArgumentException('server_ssl_cert_expiry_days'); + } + return true; + } } diff --git a/src/psm/Util/Server/Updater/StatusUpdater.php b/src/psm/Util/Server/Updater/StatusUpdater.php index bbcc5791..7e4ee950 100644 --- a/src/psm/Util/Server/Updater/StatusUpdater.php +++ b/src/psm/Util/Server/Updater/StatusUpdater.php @@ -41,6 +41,8 @@ class StatusUpdater public $header = ''; + public $curl_info = ''; + public $rtime = 0; public $status_new = false; @@ -86,6 +88,7 @@ class StatusUpdater $this->server_id = $server_id; $this->error = ''; $this->header = ''; + $this->curl_info = ''; $this->rtime = ''; // get server info from db @@ -96,7 +99,7 @@ class StatusUpdater 'type', 'pattern', 'pattern_online', 'post_field', 'allow_http_status', 'redirect_check', 'header_name', 'header_value', 'status', 'active', 'warning_threshold', - 'warning_threshold_counter', 'timeout', 'website_username', + 'warning_threshold_counter', 'ssl_cert_expiry_days', 'ssl_cert_expired_time', 'timeout', 'website_username', 'website_password', 'last_offline' )); if (empty($this->server)) { @@ -257,12 +260,13 @@ class StatusUpdater $this->server['request_method'], $this->server['post_field'] ); - $this->header = $curl_result; + $this->header = $curl_result['exec']; + $this->curl_info = $curl_result['info']; $this->rtime = (microtime(true) - $starttime); // the first line would be the status code.. - $status_code = strtok($curl_result, "\r\n"); + $status_code = strtok($curl_result['exec'], "\r\n"); // keep it general // $code[2][0] = status code // $code[3][0] = name of status code @@ -293,7 +297,7 @@ class StatusUpdater ($this->server['pattern_online'] == 'yes') == !preg_match( "/{$this->server['pattern']}/i", - $curl_result + $curl_result['exec'] ) ) { $this->error = "TEXT ERROR : Pattern '{$this->server['pattern']}' " . @@ -308,7 +312,7 @@ class StatusUpdater $location_matches = array(); preg_match( '/([Ll]ocation: )(https*:\/\/)(www.)?([a-zA-Z.:0-9]*)([\/][[:alnum:][:punct:]]*)/', - $curl_result, + $curl_result['exec'], $location_matches ); if (!empty($location_matches)) { @@ -329,7 +333,7 @@ class StatusUpdater if ($this->server['header_name'] != '' && $this->server['header_value'] != '') { $header_flag = false; // Only get the header text if the result also includes the body - $header_text = substr($curl_result, 0, strpos($curl_result, "\r\n\r\n")); + $header_text = substr($curl_result['exec'], 0, strpos($curl_result['exec'], "\r\n\r\n")); foreach (explode("\r\n", $header_text) as $i => $line) { if ($i === 0 || strpos($line, ':') == false) { continue; // We skip the status code & other non-header lines. Needed for proxy or redirects @@ -356,6 +360,9 @@ class StatusUpdater } } + // Check ssl cert + $this->checkSsl($this->server, $this->error, $result); + // check if server is available and rerun if asked. if (!$result && $run < $max_runs) { return $this->updateWebsite($max_runs, $run + 1); @@ -383,4 +390,33 @@ class StatusUpdater { return $this->rtime; } + + /** + * Check if a server speaks SSL and if the certificate is not expired. + * @param string $error + * @param bool $result + */ + private function checkSsl($server, &$error, &$result) + { + if ( + !empty($this->curl_info['certinfo']) && + $server['ssl_cert_expiry_days'] > 0 + ) { + $cert_expiration_date = strtotime($this->curl_info['certinfo'][0]['Expire date']); + $expiration_time = round((int)($cert_expiration_date - time()) / 86400); + $latest_time = time() + (86400 * $server['ssl_cert_expiry_days']); + if ($expiration_time >= 0) { + $this->header = psm_get_lang('servers', 'ssl_cert_expiring') . " " . + psm_date($this->curl_info['certinfo'][0]['Expire date']) . + "\n\n" . $this->header; + $save['ssl_cert_expired_time'] = null; + } else { + $error = psm_get_lang('servers', 'ssl_cert_expired') . " " . + psm_timespan($cert_expiration_date) . ".\n\n" . + $error; + $save['ssl_cert_expired_time'] = $expiration_time; + } + $this->db->save(PSM_DB_PREFIX . 'servers', $save, array('server_id' => $this->server_id)); + } + } } diff --git a/src/templates/default/module/server/server/update.tpl.html b/src/templates/default/module/server/server/update.tpl.html index 5d9eee53..e5fb7fbf 100644 --- a/src/templates/default/module/server/server/update.tpl.html +++ b/src/templates/default/module/server/server/update.tpl.html @@ -46,7 +46,9 @@ - {{ macro.input_field("number", "port", "port types typeService", "port", label_custom_port, edit_value_port, null, "5") }} + {{ macro.input_field("number", "port", "port types typeService", "port", label_custom_port, edit_value_port, null, "5") }} + + {{ macro.input_field("number", "ssl_cert_expiry_days", "types typeWebsite", "ssl_cert_expiry_days", label_ssl_cert_expiry_days, edit_value_ssl_cert_expiry_days, 0, "5", 'ssl_cert_help', label_ssl_cert_expiry_days_description) }}