From 19cda318496a5ba4e7f6fa95abc643c73434fea7 Mon Sep 17 00:00:00 2001 From: David Goodwin Date: Fri, 28 Dec 2018 19:27:33 +0000 Subject: [PATCH] remove psalm warnings from code; fix password_expiry behaviour when enabled/disabled on MySQL --- functions.inc.php | 358 +++++++++++++++++---------------- model/AdminpasswordHandler.php | 4 +- model/CliEdit.php | 59 +++--- model/Config.php | 14 +- model/MailboxHandler.php | 13 +- model/Shell.php | 4 + psalm.xml | 63 ++++++ public/login.php | 2 +- public/users/login.php | 2 +- 9 files changed, 301 insertions(+), 218 deletions(-) create mode 100644 psalm.xml diff --git a/functions.inc.php b/functions.inc.php index 7769da96..001b35b8 100644 --- a/functions.inc.php +++ b/functions.inc.php @@ -155,14 +155,14 @@ function _flash_string($type, $string) { } /** - * @param int $use_post - set to 0 if $_POST should NOT be read + * @param bool $use_post - set to 0 if $_POST should NOT be read * @return string e.g en * Try to figure out what language the user wants based on browser / cookie */ -function check_language($use_post = 1) { +function check_language($use_post = true) { global $supported_languages; # from languages/languages.php - $lang = Config::read('default_language'); + $lang = Config::read_string('default_language'); if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $lang_array = preg_split('/(\s*,\s*)/', $_SERVER['HTTP_ACCEPT_LANGUAGE']); @@ -173,13 +173,14 @@ function check_language($use_post = 1) { array_unshift($lang_array, safepost('lang')); # but prefer $_POST['lang'] even more } - for ($i = 0; $i < count($lang_array); $i++) { - $lang_next = $lang_array[$i]; - $lang_next = strtolower(trim($lang_next)); + foreach($lang_array as $value) { + if(!is_string($value)) { + continue; + } + $lang_next = strtolower(trim($value)); $lang_next = preg_replace('/;.*$/', '', $lang_next); # remove things like ";q=0.8" if (array_key_exists($lang_next, $supported_languages)) { - $lang = $lang_next; - break; + return $lang_next; } } } @@ -219,8 +220,8 @@ function language_selector() { * @param string $domain * @return string empty if the domain is valid, otherwise string with the errormessage * - * TODO: make check_domain able to handle as example .local domains - * TODO: skip DNS check if the domain exists in PostfixAdmin? + * @todo make check_domain able to handle as example .local domains + * @todo skip DNS check if the domain exists in PostfixAdmin? */ function check_domain($domain) { if (!preg_match('/^([-0-9A-Z]+\.)+' . '([-0-9A-Z]){2,13}$/i', ($domain))) { @@ -266,51 +267,53 @@ function check_domain($domain) { * @return int password expiration value for this domain (DAYS, or zero if not enabled) */ function get_password_expiration_value ($domain) { - $table_domain = table_by_key('domain'); - $query = "SELECT password_expiry FROM $table_domain WHERE domain='$domain'"; - $result = db_query ($query); - $password_expiration_value = db_array ($result['result']); - return $password_expiration_value[0]; + $table_domain = table_by_key('domain'); + $domain = escape_string($domain); + $query = "SELECT password_expiry FROM $table_domain WHERE domain='$domain'"; + $result = db_query($query); + $password_expiration_value = db_assoc($result['result']); + return $password_expiration_value['password_expiry']; } /** * check_email * Checks if an email is valid - if it is, return true, else false. + * @todo make check_email able to handle already added domains * @param string $email - a string that may be an email address. * @return string empty if it's a valid email address, otherwise string with the errormessage - * TODO: make check_email able to handle already added domains */ function check_email($email) { + $ce_email=$email; //strip the vacation domain out if we are using it //and change from blah#foo.com@autoreply.foo.com to blah@foo.com if (Config::bool('vacation')) { - $vacation_domain = Config::read('vacation_domain'); + $vacation_domain = Config::read_string('vacation_domain'); $ce_email = preg_replace("/@$vacation_domain\$/", '', $ce_email); $ce_email = preg_replace("/#/", '@', $ce_email); } // Perform non-domain-part sanity checks if (!preg_match('/^[-!#$%&\'*+\\.\/0-9=?A-Z^_{|}~]+' . '@' . '[^@]+$/i', $ce_email)) { - return Config::lang_f('pInvalidMailRegex', $email); + return "" . Config::lang_f('pInvalidMailRegex', $email); } if (function_exists('filter_var')) { $check = filter_var($email, FILTER_VALIDATE_EMAIL); if (!$check) { - return Config::lang_f('pInvalidMailRegex', $email); + return "" . Config::lang_f('pInvalidMailRegex', $email); } } // Determine domain name - $matches=array(); - if (!preg_match('|@(.+)$|', $ce_email, $matches)) { - return Config::lang_f('pInvalidMailRegex', $email); + $matches = array(); + if (preg_match('|@(.+)$|', $ce_email, $matches)) { + $domain=$matches[1]; + # check domain name + return "" . check_domain($domain); } - $domain=$matches[1]; - # check domain name - return check_domain($domain); + return "" . Config::lang_f('pInvalidMailRegex', $email); } @@ -325,31 +328,25 @@ function check_email($email) { function escape_string($string) { global $CONF; - // if the string is actually an array, do a recursive cleaning. - // Note, the array keys are not cleaned. - if (is_array($string)) { - $clean = array(); - foreach ($string as $k => $v) { - $clean[$k] = escape_string($v); - } - return $clean; - } if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) { $string = stripslashes($string); } + + $escaped_string = ''; + if (!is_numeric($string)) { $link = db_connect(); - if ($CONF['database_type'] == "mysql") { + if ($CONF['database_type'] == "mysql" && is_resource($link)) { $escaped_string = mysql_real_escape_string($string, $link); } - if ($CONF['database_type'] == "mysqli") { + if ($CONF['database_type'] == "mysqli" && $link instanceof mysqli) { $escaped_string = mysqli_real_escape_string($link, $string); } if (db_sqlite()) { $escaped_string = SQLite3::escapeString($string); } - if (db_pgsql()) { + if (db_pgsql() && is_resource($link)) { // php 5.2+ allows for $link to be specified. if (version_compare(phpversion(), "5.2.0", ">=")) { $escaped_string = pg_escape_string($link, $string); @@ -360,6 +357,7 @@ function escape_string($string) { } else { $escaped_string = $string; } + return $escaped_string; } @@ -389,7 +387,7 @@ function safeget($param, $default = "") * @see safeget() * @param string $param parameter name * @param string $default (optional) default value (defaults to "") - * @return string|array - value in $_POST[$param] or $default + * @return string|array - value in $_POST[$param] or $default */ function safepost($param, $default = "") { @@ -531,6 +529,8 @@ function create_page_browser($idxfield, $querypart) { $label_len = 2; $pagebrowser = array(); + $count_results = 0; + if ($page_size < 2) { # will break the page browser die('$CONF[\'page_size\'] must be 2 or more!'); } @@ -618,7 +618,7 @@ function divide_quota($quota) { if ($quota == -1) { return $quota; } - $value = round($quota / Config::read('quota_multiplier'), 2); + $value = round($quota / (int) Config::read_string('quota_multiplier'), 2); return $value; } @@ -658,6 +658,8 @@ function list_domains_for_admin($username) { $table_domain = table_by_key('domain'); $table_domain_admins = table_by_key('domain_admins'); + $condition = array(); + $E_username = escape_string($username); $query = "SELECT $table_domain.domain FROM $table_domain "; @@ -700,8 +702,10 @@ function list_domains() { if ($result['rows'] > 0) { $i = 0; while ($row = db_assoc($result['result'])) { - $list[$i] = $row['domain']; - $i++; + if(is_array($row)) { + $list[$i] = $row['domain']; + $i++; + } } } return $list; @@ -889,10 +893,11 @@ function generate_password($length = 12) { * @return array of error messages, or empty array if the password is ok */ function validate_password($password) { - $val_conf = Config::read('password_validation'); + $result = array(); + $val_conf = Config::read_array('password_validation'); - $minlen = (int) Config::read('min_password_length'); # used up to 2.3.x - check it for backward compatibility + $minlen = (int) Config::read_string('min_password_length'); # used up to 2.3.x - check it for backward compatibility if ($minlen > 0) { $val_conf['/.{' . $minlen . '}/'] = "password_too_short $minlen"; } @@ -1035,7 +1040,7 @@ function _pacrypt_dovecot($pw, $pw_db) { fclose($pipes[0]); // Read hash from pipe stdout - $password = fread($pipes[1], "200"); + $password = fread($pipes[1], 200); if (empty($dovepasstest)) { if (!preg_match('/^\{' . $method . '\}/', $password)) { @@ -1311,8 +1316,8 @@ function md5crypt($pw, $salt="", $magic="") { } function create_salt() { - srand((double) microtime()*1000000); - $salt = substr(md5(rand(0, 9999999)), 0, 8); + srand((int) microtime()*1000000); + $salt = substr(md5("" . rand(0, 9999999)), 0, 8); return $salt; } @@ -1366,9 +1371,9 @@ function smtp_mail($to, $from, $data, $body = "") { if (!empty($CONF['smtp_client'])) { $smtp_server = $CONF['smtp_client']; } - $errno = "0"; + $errno = 0; $errstr = "0"; - $timeout = "30"; + $timeout = 30; if ($body != "") { $maildata = @@ -1414,10 +1419,10 @@ function smtp_mail($to, $from, $data, $body = "") { * smtp_get_admin_email * Action: Get configured email address or current user if nothing configured * Call: smtp_get_admin_email - * @return String - username/mail address + * @return string - username/mail address */ function smtp_get_admin_email() { - $admin_email = Config::read('admin_email'); + $admin_email = Config::read_string('admin_email'); if (!empty($admin_email)) { return $admin_email; } else { @@ -1442,14 +1447,13 @@ function smtp_get_response($fh) { -$DEBUG_TEXT = "\n -

\n - Please check the documentation and website for more information.\n -

\n - Postfix Admin
\n - Forums - "; - +$DEBUG_TEXT = <<Please check the documentation and website for more information.

+ +EOF; /** * db_connect @@ -1465,22 +1469,29 @@ $DEBUG_TEXT = "\n * array($link, $error_text); * * @param bool $ignore_errors - * @return resource connection to db (normally) + * @return resource|mysqli|SQLite3 connection to db */ -function db_connect($ignore_errors = false) { +function db_connect() { + list($link, $_) = db_connect_with_errors(); + unset($_); + return $link; +} + +/** + * @param bool $ignore_errors + * @return array + */ +function db_connect_with_errors() { global $CONF; global $DEBUG_TEXT; - if ($ignore_errors != 0) { - $DEBUG_TEXT = ''; - } + + $DEBUG_TEXT = ''; $error_text = ''; static $link; if (isset($link) && $link) { - if ($ignore_errors) { return array($link, $error_text); - } - return $link; + } $link = 0; @@ -1514,7 +1525,7 @@ function db_connect($ignore_errors = false) { $error_text .= "

DEBUG INFORMATION:
MySQL 4.1 functions not available! (php5-mysqli installed?)
database_type = 'mysqli' in config.inc.php, are you using a different database? $DEBUG_TEXT"; } } - if ($is_connected) { + if ($is_connected && $link instanceof mysqli) { @mysqli_query($link, "SET CHARACTER SET utf8"); @mysqli_query($link, "SET COLLATION_CONNECTION='utf8_general_ci'"); } @@ -1524,7 +1535,9 @@ function db_connect($ignore_errors = false) { $error_text .= ("

DEBUG INFORMATION
Connect: given database path does not exist, is not writable, or \$CONF['database_name'] is empty."); } else { $link = new SQLite3($CONF['database_name']) or $error_text .= ("

DEBUG INFORMATION
Connect: failed to connect to database. $DEBUG_TEXT"); - $link->createFunction('base64_decode', 'base64_decode'); + if($link instanceof SQLite3) { + $link->createFunction('base64_decode', 'base64_decode'); + } } } else { $error_text .= "

DEBUG INFORMATION:
SQLite functions not available! (php5-sqlite installed?)
database_type = 'sqlite' in config.inc.php, are you using a different database? $DEBUG_TEXT"; @@ -1546,28 +1559,14 @@ function db_connect($ignore_errors = false) { $error_text = "

DEBUG INFORMATION:
Invalid \$CONF['database_type']! Please fix your config.inc.php! $DEBUG_TEXT"; } - if ($ignore_errors) { - return array($link, $error_text); - } elseif ($error_text != "") { - print $error_text; - die(); - } elseif ($link) { - return $link; - } else { - print "DEBUG INFORMATION:
\n"; - print "Connect: Unable to connect to database
\n"; - print "
\n"; - print "Make sure that you have set the correct database type in the config.inc.php file
\n"; - print $DEBUG_TEXT; - die(); - } + return array($link, $error_text); } /** * Returns the appropriate boolean value for the database. * Currently only PostgreSQL and MySQL are supported. - * @param boolean $bool (REQUIRED) - * @return String or int as appropriate. + * @param bool $bool + * @return string|int as appropriate for underlying db platform */ function db_get_boolean($bool) { if (! (is_bool($bool) || $bool == '0' || $bool == '1')) { @@ -1679,25 +1678,18 @@ function db_query($query, $ignore_errors = 0) { $DEBUG_TEXT = ""; } - if ($CONF['database_type'] == "mysql") { - /* @var resource $link */ - $result = @mysql_query($query, $link) - or $error_text = "Invalid query: " . mysql_error($link); + if ($CONF['database_type'] == "mysql" && is_resource($link)) { + $result = @mysql_query($query, $link) or $error_text = "Invalid query: " . mysql_error($link); } - if ($CONF['database_type'] == "mysqli") { - /* @var resource $link */ - $result = @mysqli_query($link, $query) - or $error_text = "Invalid query: " . mysqli_error($link); + if ($CONF['database_type'] == "mysqli" && $link instanceof mysqli) { + $result = @mysqli_query($link, $query) or $error_text = "Invalid query: " . mysqli_error($link); } - if (db_sqlite()) { - /* @var SQLite3 $link */ - $result = @$link->query($query) - or $error_text = "Invalid query: " . $link->lastErrorMsg(); + if (db_sqlite() && $link instanceof SQLite3) { + $result = @$link->query($query) or $error_text = "Invalid query: " . $link->lastErrorMsg(); } - if (db_pgsql()) { + if (db_pgsql() && is_resource($link)) { /* @var resource $link */ - $result = @pg_query($link, $query) - or $error_text = "Invalid query: " . pg_last_error(); + $result = @pg_query($link, $query) or $error_text = "Invalid query: " . pg_last_error(); } if ($error_text != "" && $ignore_errors == 0) { error_log($error_text); @@ -1706,8 +1698,9 @@ function db_query($query, $ignore_errors = 0) { } if ($error_text == "") { - if (db_sqlite()) { + if (db_sqlite() && $result instanceof SQLite3Result && $link instanceof SQLite3) { /* @var SQLite3Result $result */ + /* @var SQLite3 $link */ if ($result->numColumns()) { // Query returned something $num_rows = 0; @@ -1723,26 +1716,26 @@ function db_query($query, $ignore_errors = 0) { } elseif (preg_match("/^SELECT/i", trim($query))) { /* @var resource $result */ // if $query was a SELECT statement check the number of rows with [database_type]_num_rows (). - if ($CONF['database_type'] == "mysql") { + if ($CONF['database_type'] == "mysql" && is_resource($result)) { $number_rows = mysql_num_rows($result); } - if ($CONF['database_type'] == "mysqli") { + if ($CONF['database_type'] == "mysqli" && $result instanceof mysqli_result) { $number_rows = mysqli_num_rows($result); } - if (db_pgsql()) { + if (db_pgsql() && is_resource($result)) { $number_rows = pg_num_rows($result); } } else { /* @var resource $result */ // if $query was something else, UPDATE, DELETE or INSERT check the number of rows with // [database_type]_affected_rows (). - if ($CONF['database_type'] == "mysql") { + if ($CONF['database_type'] == "mysql" && is_resource($link)) { $number_rows = mysql_affected_rows($link); } - if ($CONF['database_type'] == "mysqli") { + if ($CONF['database_type'] == "mysqli" && $link instanceof mysqli) { $number_rows = mysqli_affected_rows($link); } - if (db_pgsql()) { + if (db_pgsql() && is_resource($result)) { $number_rows = pg_affected_rows($result); } } @@ -1762,49 +1755,58 @@ function db_query($query, $ignore_errors = 0) { // Action: Returns a row from a table // Call: db_row (int result) +/** + * @param resource|mysqli_result|SQLite3Result $result + * @return array + */ function db_row($result) { global $CONF; $row = ""; - if ($CONF['database_type'] == "mysql") { + if ($CONF['database_type'] == "mysql" && is_resource($result)) { $row = mysql_fetch_row($result); } - if ($CONF['database_type'] == "mysqli") { + if ($CONF['database_type'] == "mysqli" && $result instanceof mysqli_result) { $row = mysqli_fetch_row($result); } - if (db_sqlite()) { - /* @var SQLite3Result $result */ + if (db_sqlite() && $result instanceof SQLite3Result) { $row = $result->fetchArray(SQLITE3_NUM); } - if (db_pgsql()) { - /* @var resource $result */ + if (db_pgsql() && is_resource($result)) { $row = pg_fetch_row($result); } + + if(!is_array($row)) { + return array(); + } return $row; } /** * Return array from a db resource (presumably not associative). - * @param resource $result - * @return array|null|string + * @param resource|SQLite3Result|mysqli_result $result + * @return array */ function db_array($result) { global $CONF; $row = ""; - if ($CONF['database_type'] == "mysql") { + if ($CONF['database_type'] == "mysql" && is_resource($result)) { $row = mysql_fetch_array($result); } - if ($CONF['database_type'] == "mysqli") { + if ($CONF['database_type'] == "mysqli" && $result instanceof mysqli_result) { $row = mysqli_fetch_array($result); } - if (db_sqlite()) { - /* @var SQLite3Result $result */ + if (db_sqlite() && $result instanceof SQLite3Result) { $row = $result->fetchArray(); } - if (db_pgsql()) { - /* @var resource $result */ + if (db_pgsql() && is_resource($result)) { $row = pg_fetch_array($result); } + + if(!is_array($row)) { + return []; + } + return $row; } @@ -1813,26 +1815,28 @@ function db_array($result) { * Get an associative array from a DB query resource. * * @param mixed $result - either resource or SQLite3Result depending on DB type chosen. - * @return array|null|string + * @return array */ function db_assoc($result) { global $CONF; $row = []; - if ($CONF['database_type'] == "mysql") { - /* @var resource $result */ + if ($CONF['database_type'] == "mysql" && is_resource($result)) { $row = mysql_fetch_assoc($result); } - if ($CONF['database_type'] == "mysqli") { - /* @var resource $result */ + if ($CONF['database_type'] == "mysqli" && $result instanceof mysqli_result) { $row = mysqli_fetch_assoc($result); + } - if (db_sqlite()) { - /* @var SQLite3Result $result */ + if (db_sqlite() && $result instanceof SQLite3Result) { $row = $result->fetchArray(SQLITE3_ASSOC); } - if (db_pgsql()) { + if (db_pgsql() && is_resource($result)) { $row = pg_fetch_assoc($result); } + + if(!is_array($row)) { + $row = []; + } return $row; } @@ -1872,7 +1876,7 @@ function db_delete($table, $where, $delete, $additionalwhere='') { * @param array $timestamp (optional) - array of fields to set to now() - default: array('created', 'modified') * @return int - number of inserted rows */ -function db_insert ($table, $values, $timestamp = array('created', 'modified'), $timestamp_expiration = array('password_expiry') ) { +function db_insert ($table, array $values, $timestamp = array('created', 'modified') ) { $table = table_by_key($table); foreach (array_keys($values) as $key) { @@ -1887,21 +1891,22 @@ function db_insert ($table, $values, $timestamp = array('created', 'modified'), } } - global $CONF; - if ($CONF['password_expiration_enabled'] == 'YES') { - if ($table == 'mailbox') { + $_table = trim($table, "`'"); + if (Config::bool('password_expiration')) { + if ($_table == 'mailbox') { $domain_dirty = $values['domain']; - $domain = substr($domain_dirty, 1, -1); // really the update to the mailbox password_expiry should be based on a trigger, or a query like : - // .... NOW() + INTERVAL domain.password_expiry DAY - $password_expiration_value = get_password_expiration_value($domain); - foreach($timestamp_expiration as $key) { - $values[$key] = "now() + interval " . $password_expiration_value . " day"; - } + $domain = trim($domain_dirty, "`'"); // naive assumption it is ' escaping. + $password_expiration_value = (int) get_password_expiration_value($domain); + $values['password_expiry'] = "now() + interval " . $password_expiration_value . " day"; + } + } + else { + if($_table == 'mailbox') { + unset($values['password_expiry']); } } - $sql_values = "(" . implode(",", escape_string(array_keys($values))).") VALUES (".implode(",", $values).")"; - + $sql_values = "(" . implode(",", array_keys($values)) .") VALUES (". implode(",", $values).")"; $result = db_query("INSERT INTO $table $sql_values"); return $result['rows']; } @@ -1934,7 +1939,10 @@ function db_update($table, $where_col, $where_value, $values, $timestamp = array * @return int - number of updated rows */ function db_update_q($table, $where, $values, $timestamp = array('modified')) { - $table = table_by_key($table); + global $CONF; + + $table_key = table_by_key($table); + $sql_values = array(); foreach ($values as $key => $value) { $sql_values[$key] = $key . "='" . escape_string($value) . "'"; @@ -1948,23 +1956,26 @@ function db_update_q($table, $where, $values, $timestamp = array('modified')) { } } - global $CONF; - if ($CONF['password_expiration_enabled'] == 'YES') { - $where_type = explode('=',$where); - $email = ($where_type[1]); - $domain_dirty = explode('@',$email)[1]; - $domain = substr($domain_dirty, 0, -1); + if (Config::bool('password_expiration')) { if ($table == 'mailbox') { + + error_log("db_update_q : " . json_Encode($where)); + $where_type = explode('=', $where); + $email = ($where_type[1]); + $domain_dirty = explode('@',$email)[1]; + $domain = substr($domain_dirty, 0, -1); $password_expiration_value = get_password_expiration_value($domain); $key = 'password_expiry'; $sql_values[$key] = $key . " = now() + interval " . $password_expiration_value . " day"; } } - $sql="UPDATE $table SET " . implode(",", $sql_values) . " WHERE $where"; - + $sql="UPDATE $table_key SET " . implode(",", $sql_values) . " WHERE $where"; $result = db_query($sql); - return $result['rows']; + if(array_key_exists('rows', $result)) { + return $result['rows']; + } + return 0; } @@ -2008,11 +2019,12 @@ function db_log($domain, $action, $data) { * Call: db_in_clause (string field, array values) * @param string $field * @param array $values + * @return string */ -function db_in_clause($field, $values) { - return " $field IN ('" - . implode("','", escape_string(array_values($values))) - . "') "; +function db_in_clause($field, array $values) { + + $v = array_map('escape_string', array_values($values)); + return " $field IN ('" . implode("','", $v) . "') "; } /** @@ -2123,10 +2135,12 @@ function table_by_key($table_key) { } -/* +/** * check if the database layout is up to date * returns the current 'version' value from the config table * if $error_out is True (default), die() with a message that recommends to run setup.php. + * @param bool $error_out + * @return int */ function check_db_version($error_out = true) { global $min_db_version; @@ -2136,11 +2150,14 @@ function check_db_version($error_out = true) { $sql = "SELECT value FROM $table WHERE name = 'version'"; $r = db_query($sql); + $dbversion = 0; + if ($r['rows'] == 1) { $row = db_assoc($r['result']); - $dbversion = $row['value']; + if(isset($row['value'])) { + $dbversion = (int) $row['value']; + } } else { - $dbversion = 0; db_query("INSERT INTO $table (name, value) VALUES ('version', '0')", 0); } @@ -2152,13 +2169,16 @@ function check_db_version($error_out = true) { return $dbversion; } -// -// gen_show_status -// Action: Return a string of colored  's that indicate -// the if an alias goto has an error or is sent to -// addresses list in show_custom_domains -// Call: gen_show_status (string alias_address) -// + +/** + * + * Action: Return a string of colored  's that indicate + * the if an alias goto has an error or is sent to + * addresses list in show_custom_domains + * + * @param string $show_alias + * @return string + */ function gen_show_status($show_alias) { global $CONF; $table_alias = table_by_key('alias'); @@ -2173,6 +2193,8 @@ function gen_show_status($show_alias) { $stat_goto = $row[0]; } + $delimiter_regex = null; + if (!empty($CONF['recipient_delimiter'])) { $delimiter = preg_quote($CONF['recipient_delimiter'], "/"); $delimiter_regex = '/' .$delimiter. '[^' .$delimiter. '@]*@/'; @@ -2197,11 +2219,11 @@ function gen_show_status($show_alias) { list($local_part, $stat_domain) = explode('@', $g); $stat_delimiter = ""; - if (!empty($CONF['recipient_delimiter'])) { + if (!empty($CONF['recipient_delimiter']) && isset($delimiter_regex)) { $stat_delimiter = "OR address = '" . escape_string(preg_replace($delimiter_regex, "@", $g)) . "'"; } $stat_result = db_query("SELECT address FROM $table_alias WHERE address = '" . escape_string($g) . "' OR address = '@" . escape_string($stat_domain) . "' $stat_delimiter"); - if ($stat_result['rows'] == 0) { + if (array_key_exists('rows', $stat_result) && $stat_result['rows'] == 0) { $stat_ok = 0; } if ($stat_ok == 0) { @@ -2250,7 +2272,7 @@ function gen_show_status($show_alias) { // POP/IMAP CHECK if ($CONF['show_popimap'] == 'YES') { $stat_delimiter = ""; - if (!empty($CONF['recipient_delimiter'])) { + if (!empty($CONF['recipient_delimiter']) && isset($delimiter_regex)) { $stat_delimiter = ',' . preg_replace($delimiter_regex, "@", $stat_goto); } diff --git a/model/AdminpasswordHandler.php b/model/AdminpasswordHandler.php index d8add63a..3f03376d 100644 --- a/model/AdminpasswordHandler.php +++ b/model/AdminpasswordHandler.php @@ -9,11 +9,11 @@ class AdminpasswordHandler extends PFAHandler { protected $skip_empty_pass = false; protected function no_domain_field() { - # PFAHandler die()s if domain field is not set. Disable this behaviour for AdminHandler. + return true; } protected function validate_new_id() { - # unused in AdminpasswordHandler, but must be defined + return true; } # init $this->struct, $this->db_table and $this->id_field diff --git a/model/CliEdit.php b/model/CliEdit.php index b662dda3..83389dd0 100644 --- a/model/CliEdit.php +++ b/model/CliEdit.php @@ -1,20 +1,22 @@ args)) { $this->__interactive(); } else { @@ -27,12 +29,13 @@ class CliEdit extends Shell { * read, check and handle all --* parameters * The list of allowed params is based on $handler->struct */ - private function __handle_params() { - $handler = new $this->handler_to_use($this->new); + private function __handle_params() + { + $handler = new $this->handler_to_use($this->new); $form_fields = $handler->getStruct(); - $id_field = $handler->getId_field(); + $id_field = $handler->getId_field(); - $values = array(); + $values = array(); $param_error = 0; foreach ($this->params as $key => $val) { @@ -69,13 +72,14 @@ class CliEdit extends Shell { } /** - * Interactive mode - */ - private function __interactive() { + * Interactive mode + */ + private function __interactive() + { $handler = new $this->handler_to_use($this->new); $form_fields = $handler->getStruct(); - $id_field = $handler->getId_field(); + $id_field = $handler->getId_field(); $values = array($id_field => ''); @@ -103,7 +107,7 @@ class CliEdit extends Shell { foreach ($form_fields as $key => $field) { if ($field['editable'] && $field['display_in_form'] && $key != $id_field) { - while (0==0) { # endlees loop - except if input is valid + while (0 == 0) { # endlees loop - except if input is valid $question = $field['label'] . ':'; if ($field['desc'] != '') { $question .= "\n(" . $field['desc'] . ')'; @@ -124,15 +128,15 @@ class CliEdit extends Shell { foreach ($field['options'] as $optionkey => $optionval) { // $this->in hates number 0 $optionkey = $optionkey + 1; - $optiontxt[] = '['.$optionkey.'] - '.$optionval; + $optiontxt[] = '[' . $optionkey . '] - ' . $optionval; $optionlist[] = $optionkey; } $question .= "\n" . join("\n", $optiontxt) . "\n"; - $values[$key] = $this->in($question, $optionlist); + $selected = (int) $this->in($question, $optionlist); - $values[$key] = $field['options'][$values[$key]-1]; # convert int to option name + $values[$key] = $field['options'][$selected - 1]; # convert int to option name } elseif ($field['type'] == 'txtl') { $values[$key] = array(); $nextval = $this->in($question); @@ -173,10 +177,11 @@ class CliEdit extends Shell { } /** - * (try to) store values - */ - private function __handle($id, $values) { - $handler = new $this->handler_to_use($this->new); + * (try to) store values + */ + private function __handle($id, $values) + { + $handler = new $this->handler_to_use($this->new); if (!$handler->init($id)) { $this->err($handler->errormsg); return; @@ -198,9 +203,10 @@ class CliEdit extends Shell { } /** - * Displays help contents - */ - public function help() { + * Displays help contents + */ + public function help() + { if ($this->new) { $cmd = 'add'; $cmdtext = 'Adds'; @@ -213,7 +219,7 @@ class CliEdit extends Shell { $module = strtolower($module); $this->out( -"Usage: + "Usage: postfixadmin-cli $module $cmd @@ -232,7 +238,7 @@ class CliEdit extends Shell { $handler = new $this->handler_to_use($this->new); $form_fields = $handler->getStruct(); - $id_field = $handler->getId_field(); + $id_field = $handler->getId_field(); foreach ($form_fields as $key => $field) { if ($field['editable'] && $field['display_in_form'] && $key != $id_field) { @@ -247,7 +253,6 @@ class CliEdit extends Shell { } - $this->_stop(); } } diff --git a/model/Config.php b/model/Config.php index 6badb63d..d972ec91 100644 --- a/model/Config.php +++ b/model/Config.php @@ -161,7 +161,6 @@ final class Config { * @param string $var Variable to obtain * @param string $value Value to use as sprintf parameter * @return string value of Config::$var, parsed by sprintf - * @access public */ public static function read_f($var, $value) { $text = self::read_string($var); @@ -170,9 +169,6 @@ final class Config { # check if sprintf changed something - if not, there are chances that $text didn't contain a %s if ($text == $newtext) { - if (is_array($var)) { - $var = join('.', $var); - } error_log("$var used via read_f, but nothing replaced (value $value)"); } @@ -236,8 +232,13 @@ final class Config { */ public static function lang($var) { $value = self::read("__LANG.{$var}"); + + if(is_null($value)) { + return ''; + } + if(!is_string($value)) { - throw new InvalidArgumentException("Expected string value for $var "); + trigger_error('In '.__FUNCTION__.": expected config $var to be a string , but received a " . gettype($value), E_USER_ERROR); } return $value; } @@ -249,10 +250,9 @@ final class Config { * @param string $var Text (from $PALANG) to obtain * @param string $value Value to use as sprintf parameter * @return string value of $PALANG[$var], parsed by sprintf - * @access public */ public static function lang_f($var, $value) { - return self::read_f('__LANG'. $var, $value); + return self::read_f('__LANG.'. $var, $value); } /** diff --git a/model/MailboxHandler.php b/model/MailboxHandler.php index d0cde7dc..6cccd6e5 100644 --- a/model/MailboxHandler.php +++ b/model/MailboxHandler.php @@ -449,10 +449,9 @@ class MailboxHandler extends PFAHandler { * * @param int $quota - quota wanted for the mailbox * @return boolean - true if requested quota is OK, otherwise false + * @todo merge with allowed_quota? */ - # TODO: merge with allowed_quota? protected function check_quota($quota) { - $rval = false; if (!Config::bool('quota')) { return true; # enforcing quotas is disabled - just allow it @@ -461,10 +460,6 @@ class MailboxHandler extends PFAHandler { list(/*NULL*/, $domain) = explode('@', $this->id); $limit = get_domain_properties($domain); - if ($limit['maxquota'] == 0) { - $rval = true; # maxquota unlimited -> OK, but domain level quota could still be hit - } - if (($limit['maxquota'] < 0) and ($quota < 0)) { return true; # maxquota and $quota are both disabled -> OK, no need for more checks } @@ -475,12 +470,6 @@ class MailboxHandler extends PFAHandler { if ($limit['maxquota'] != 0 && $quota > $limit['maxquota']) { return false; # mailbox bigger than maxquota restriction (and maxquota != unlimited) -> not allowed, no more checks needed - } else { - $rval = true; # mailbox size looks OK, but domain level quota could still be hit - } - - if (!$rval) { - return false; # over quota - no need to check domain_quota } # TODO: detailed error message ("domain quota exceeded", "mailbox quota too big" etc.) via flash_error? Or "available quota: xxx MB"? diff --git a/model/Shell.php b/model/Shell.php index fa6ea28f..8a2de2b8 100644 --- a/model/Shell.php +++ b/model/Shell.php @@ -59,6 +59,7 @@ class Shell { * @access public */ public $args = array(); + /** * The file name of the shell that was invoked. * @@ -66,6 +67,7 @@ class Shell { * @access public */ public $shell; + /** * The class name of the shell that was invoked. * @@ -73,12 +75,14 @@ class Shell { * @access public */ public $className; + /** * The command called if public methods are available. * * @var string * @access public */ + public $command; /** * The name of the shell in camelized. diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..5bba1f03 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/login.php b/public/login.php index 9658ccd8..ba884bb2 100644 --- a/public/login.php +++ b/public/login.php @@ -45,7 +45,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { $fUsername = trim(safepost('fUsername')); $fPassword = safepost('fPassword'); - if ($lang != check_language(0)) { # only set cookie if language selection was changed + if ($lang != check_language(false)) { # only set cookie if language selection was changed setcookie('lang', $lang, time() + 60*60*24*30); # language cookie, lifetime 30 days # (language preference cookie is processed even if username and/or password are invalid) } diff --git a/public/users/login.php b/public/users/login.php index 65f863e0..59c64fe8 100644 --- a/public/users/login.php +++ b/public/users/login.php @@ -41,7 +41,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") { $fUsername = trim(safepost('fUsername')); $fPassword = safepost('fPassword'); - if ($lang != check_language(0)) { # only set cookie if language selection was changed + if ($lang != check_language(false)) { # only set cookie if language selection was changed setcookie('lang', $lang, time() + 60*60*24*30); # language cookie, lifetime 30 days # (language preference cookie is processed even if username and/or password are invalid) }