2007-03-24 08:27:00 +01:00
< ? php
2017-03-21 17:43:27 +01:00
/**
* Postfix Admin
*
* LICENSE
* This source file is subject to the GPL license that is bundled with
* this package in the file LICENSE . TXT .
*
* Further details on the project are available at http :// postfixadmin . sf . net
*
* @ license GNU GPL v2 or later .
*
2007-09-30 17:53:54 +02:00
* File : functions . inc . php
* Contains re - usable code .
*/
2007-03-24 08:27:00 +01:00
2020-09-27 11:47:56 +02:00
2021-05-13 14:15:27 +02:00
$min_db_version = 1844 ; # update (at least) before a release with the latest function numbrer in upgrade.php
2007-09-22 12:59:24 +02:00
/**
* check_session
* Action : Check if a session already exists , if not redirect to login . php
* Call : check_session ()
* @ return String username ( e . g . foo @ example . com )
*/
2021-04-13 22:19:16 +02:00
function authentication_get_username ()
{
2011-02-15 22:59:03 +01:00
if ( defined ( 'POSTFIXADMIN_CLI' )) {
return 'CLI' ;
}
2011-12-20 02:06:49 +01:00
if ( defined ( 'POSTFIXADMIN_SETUP' )) {
return 'SETUP.PHP' ;
}
2009-01-15 13:24:36 +01:00
if ( ! isset ( $_SESSION [ 'sessid' ])) {
2018-01-26 15:45:57 +01:00
header ( " Location: login.php " );
2009-01-15 13:24:36 +01:00
exit ( 0 );
}
$SESSID_USERNAME = $_SESSION [ 'sessid' ][ 'username' ];
return $SESSID_USERNAME ;
2007-03-24 08:27:00 +01:00
}
2007-09-22 12:59:24 +02:00
/**
2017-03-21 17:43:27 +01:00
* Returns the type of user - either 'user' or 'admin'
2007-09-22 12:59:24 +02:00
* Returns false if neither ( E . g . if not logged in )
2018-06-18 22:54:05 +02:00
* @ return string | bool admin or user or ( boolean ) false .
2007-09-22 12:59:24 +02:00
*/
2021-04-13 22:19:16 +02:00
function authentication_get_usertype ()
{
2018-01-26 15:45:57 +01:00
if ( isset ( $_SESSION [ 'sessid' ])) {
if ( isset ( $_SESSION [ 'sessid' ][ 'type' ])) {
2009-01-15 13:24:36 +01:00
return $_SESSION [ 'sessid' ][ 'type' ];
}
}
return false ;
2007-03-24 08:27:00 +01:00
}
2007-09-22 12:59:24 +02:00
/**
*
* Used to determine whether a user has a particular role .
2018-06-18 22:54:05 +02:00
* @ param string $role role - name . ( E . g . admin , global - admin or user )
2007-09-22 12:59:24 +02:00
* @ return boolean True if they have the requested role in their session .
* Note , user < admin < global - admin
*/
2021-04-13 22:19:16 +02:00
function authentication_has_role ( $role )
{
2018-01-26 15:45:57 +01:00
if ( isset ( $_SESSION [ 'sessid' ])) {
if ( isset ( $_SESSION [ 'sessid' ][ 'roles' ])) {
if ( in_array ( $role , $_SESSION [ 'sessid' ][ 'roles' ])) {
2009-01-15 13:24:36 +01:00
return true ;
}
}
}
return false ;
2007-03-24 08:27:00 +01:00
}
2007-09-22 12:59:24 +02:00
/**
2017-03-21 17:43:27 +01:00
* Used to enforce that $user has a particular role when
2007-09-22 12:59:24 +02:00
* viewing a page .
2012-05-28 20:07:33 +02:00
* If they are lacking a role , redirect them to login . php
2007-09-22 12:59:24 +02:00
*
* Note , user < admin < global - admin
2018-06-18 22:54:05 +02:00
* @ param string $role
* @ return bool
2007-09-22 12:59:24 +02:00
*/
2021-04-13 22:19:16 +02:00
function authentication_require_role ( $role )
{
2009-01-15 13:24:36 +01:00
// redirect to appropriate page?
2018-01-26 15:45:57 +01:00
if ( authentication_has_role ( $role )) {
return true ;
2009-01-15 13:24:36 +01:00
}
2012-05-28 19:43:39 +02:00
2012-05-28 20:07:33 +02:00
header ( " Location: login.php " );
2009-01-15 13:24:36 +01:00
exit ( 0 );
2007-09-22 12:59:24 +02:00
}
2007-03-24 08:27:00 +01:00
2016-10-19 13:06:10 +02:00
/**
* Initialize a user or admin session
*
* @ param String $username the user or admin name
* @ param boolean $is_admin true if the user is an admin , false otherwise
* @ return boolean true on success
*/
2021-04-13 22:19:16 +02:00
function init_session ( $username , $is_admin = false )
{
2016-10-19 13:06:10 +02:00
$status = session_regenerate_id ( true );
$_SESSION [ 'sessid' ] = array ();
$_SESSION [ 'sessid' ][ 'roles' ] = array ();
$_SESSION [ 'sessid' ][ 'roles' ][] = $is_admin ? 'admin' : 'user' ;
$_SESSION [ 'sessid' ][ 'username' ] = $username ;
2021-08-04 17:56:37 +02:00
$_SESSION [ 'PFA_token' ] = md5 ( random_bytes ( 8 ) . uniqid ( 'pfa' , true ));
2016-10-19 13:06:10 +02:00
return $status ;
}
2007-03-24 08:27:00 +01:00
2007-09-28 21:27:51 +02:00
/**
* Add an error message for display on the next page that is rendered .
2018-06-18 22:54:05 +02:00
* @ param string | array $string message ( s ) to show .
2007-09-28 21:27:51 +02:00
*
* Stores string in session . Flushed through header template .
* @ see _flash_string ()
2018-06-18 22:54:05 +02:00
* @ return void
2007-09-28 21:27:51 +02:00
*/
2021-04-13 22:19:16 +02:00
function flash_error ( $string )
{
2009-01-15 13:24:36 +01:00
_flash_string ( 'error' , $string );
2007-09-28 21:27:51 +02:00
}
/**
* Used to display an info message on successful update .
2018-06-18 22:54:05 +02:00
* @ param string | array $string message ( s ) to show .
2012-04-29 16:39:41 +02:00
* Stores data in session .
2007-09-28 21:27:51 +02:00
* @ see _flash_string ()
2018-06-18 22:54:05 +02:00
* @ return void
2007-09-28 21:27:51 +02:00
*/
2021-04-13 22:19:16 +02:00
function flash_info ( $string )
{
2009-01-15 13:24:36 +01:00
_flash_string ( 'info' , $string );
2007-09-28 21:27:51 +02:00
}
/**
* 'Private' method used for flash_info () and flash_error () .
2018-06-18 22:54:05 +02:00
* @ param string $type
* @ param array | string $string
* @ retrn void
2007-09-28 21:27:51 +02:00
*/
2021-04-13 22:19:16 +02:00
function _flash_string ( $type , $string )
{
2012-04-29 16:39:41 +02:00
if ( is_array ( $string )) {
foreach ( $string as $singlestring ) {
_flash_string ( $type , $singlestring );
}
2013-02-19 23:22:59 +01:00
return ;
2012-04-29 16:39:41 +02:00
}
2018-01-26 15:45:57 +01:00
if ( ! isset ( $_SESSION [ 'flash' ])) {
2009-01-15 13:24:36 +01:00
$_SESSION [ 'flash' ] = array ();
}
2018-01-26 15:45:57 +01:00
if ( ! isset ( $_SESSION [ 'flash' ][ $type ])) {
2009-01-15 13:24:36 +01:00
$_SESSION [ 'flash' ][ $type ] = array ();
}
$_SESSION [ 'flash' ][ $type ][] = $string ;
2007-09-28 21:27:51 +02:00
}
2018-06-18 22:54:05 +02:00
/**
2018-12-28 20:27:33 +01:00
* @ param bool $use_post - set to 0 if $_POST should NOT be read
2018-06-18 22:54:05 +02:00
* @ return string e . g en
* Try to figure out what language the user wants based on browser / cookie
*/
2021-04-13 22:19:16 +02:00
function check_language ( $use_post = true )
{
2009-01-15 13:24:36 +01:00
global $supported_languages ; # from languages/languages.php
2020-03-14 23:34:52 +01:00
// prefer a $_POST['lang'] if present
2020-03-14 22:39:36 +01:00
if ( $use_post && safepost ( 'lang' )) {
$lang = safepost ( 'lang' );
2020-03-14 23:34:52 +01:00
if ( is_string ( $lang ) && array_key_exists ( $lang , $supported_languages )) {
2020-03-14 22:39:36 +01:00
return $lang ;
}
}
// Failing that, is there a $_COOKIE['lang'] ?
if ( safecookie ( 'lang' )) {
$lang = safecookie ( 'lang' );
2020-03-14 23:34:52 +01:00
if ( is_string ( $lang ) && array_key_exists ( $lang , $supported_languages )) {
2020-03-14 22:39:36 +01:00
return $lang ;
}
}
2018-12-28 20:27:33 +01:00
$lang = Config :: read_string ( 'default_language' );
2009-01-15 13:24:36 +01:00
2020-03-14 23:34:52 +01:00
// If not, did the browser give us any hint(s)?
2020-04-01 22:29:40 +02:00
if ( ! empty ( $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ])) {
2018-01-26 15:45:57 +01:00
$lang_array = preg_split ( '/(\s*,\s*)/' , $_SERVER [ 'HTTP_ACCEPT_LANGUAGE' ]);
2018-12-28 20:31:43 +01:00
foreach ( $lang_array as $value ) {
2018-12-28 20:27:33 +01:00
$lang_next = strtolower ( trim ( $value ));
2013-10-31 21:38:14 +01:00
$lang_next = preg_replace ( '/;.*$/' , '' , $lang_next ); # remove things like ";q=0.8"
2018-01-26 15:45:57 +01:00
if ( array_key_exists ( $lang_next , $supported_languages )) {
2018-12-28 20:27:33 +01:00
return $lang_next ;
2009-01-15 13:24:36 +01:00
}
}
}
return $lang ;
2007-03-24 08:27:00 +01:00
}
2018-06-18 22:54:05 +02:00
/**
* Action : returns a language selector dropdown with the browser ( or cookie ) language preselected
* @ return string
*
*
*/
2021-04-13 22:19:16 +02:00
function language_selector ()
{
2009-01-15 13:24:36 +01:00
global $supported_languages ; # from languages/languages.php
2008-07-29 21:32:32 +02:00
2009-01-15 13:24:36 +01:00
$current_lang = check_language ();
2007-12-30 02:32:33 +01:00
2009-01-15 13:24:36 +01:00
$selector = '<select name="lang" xml:lang="en" dir="ltr">' ;
2007-03-24 08:27:00 +01:00
2018-01-26 15:45:57 +01:00
foreach ( $supported_languages as $lang => $lang_name ) {
2009-01-15 13:24:36 +01:00
if ( $lang == $current_lang ) {
$selected = ' selected="selected"' ;
} else {
$selected = '' ;
}
$selector .= " <option value=' $lang ' $selected > $lang_name </option> " ;
}
$selector .= " </select> " ;
return $selector ;
2007-12-30 02:32:33 +01:00
}
2007-03-24 08:27:00 +01:00
2013-04-01 23:22:30 +02:00
/**
2017-03-21 17:43:27 +01:00
* Checks if a domain is valid
2013-04-01 23:22:30 +02:00
* @ param string $domain
2018-06-10 22:21:45 +02:00
* @ return string empty if the domain is valid , otherwise string with the errormessage
2013-04-01 23:22:30 +02:00
*
2018-12-28 20:27:33 +01:00
* @ todo make check_domain able to handle as example . local domains
* @ todo skip DNS check if the domain exists in PostfixAdmin ?
2013-04-01 23:22:30 +02:00
*/
2021-04-13 22:19:16 +02:00
function check_domain ( $domain )
{
2019-04-14 16:25:15 +02:00
if ( ! preg_match ( '/^([-0-9A-Z]+\.)+' . '([-0-9A-Z]){1,13}$/i' , ( $domain ))) {
2013-10-13 20:11:18 +02:00
return sprintf ( Config :: lang ( 'pInvalidDomainRegex' ), htmlentities ( $domain ));
2009-01-15 13:24:36 +01:00
}
2008-07-30 01:18:40 +02:00
2013-06-06 22:53:37 +02:00
if ( Config :: bool ( 'emailcheck_resolve_domain' ) && 'WINDOWS' != ( strtoupper ( substr ( php_uname ( 's' ), 0 , 7 )))) {
2008-07-30 01:18:40 +02:00
2009-01-15 13:24:36 +01:00
// Look for an AAAA, A, or MX record for the domain
2008-07-30 01:18:40 +02:00
2018-01-26 15:45:57 +01:00
if ( function_exists ( 'checkdnsrr' )) {
2015-06-13 21:56:26 +02:00
$start = microtime ( true ); # check for slow nameservers, part 1
2009-01-15 13:24:36 +01:00
// AAAA (IPv6) is only available in PHP v. >= 5
2018-01-26 15:45:57 +01:00
if ( version_compare ( phpversion (), " 5.0.0 " , " >= " ) && checkdnsrr ( $domain , 'AAAA' )) {
2015-06-13 21:56:26 +02:00
$retval = '' ;
2018-01-26 15:45:57 +01:00
} elseif ( checkdnsrr ( $domain , 'A' )) {
2015-06-13 21:56:26 +02:00
$retval = '' ;
2018-01-26 15:45:57 +01:00
} elseif ( checkdnsrr ( $domain , 'MX' )) {
2015-06-13 21:56:26 +02:00
$retval = '' ;
2020-05-15 22:06:59 +02:00
} elseif ( checkdnsrr ( $domain , 'NS' )) {
2020-06-02 13:54:26 +02:00
error_log ( " DNS is not correctly configured for $domain to send or receive email " );
2020-05-15 22:06:59 +02:00
$retval = '' ;
2015-06-13 21:56:26 +02:00
} else {
$retval = sprintf ( Config :: lang ( 'pInvalidDomainDNS' ), htmlentities ( $domain ));
}
$end = microtime ( true ); # check for slow nameservers, part 2
$time_needed = $end - $start ;
if ( $time_needed > 2 ) {
error_log ( " Warning: slow nameserver - lookup for $domain took $time_needed seconds " );
2009-01-15 13:24:36 +01:00
}
2015-06-13 21:56:26 +02:00
return $retval ;
2011-06-02 22:51:12 +02:00
} else {
2013-04-01 23:22:30 +02:00
return 'emailcheck_resolve_domain is enabled, but function (checkdnsrr) missing!' ;
2009-01-15 13:24:36 +01:00
}
}
2015-06-13 21:56:26 +02:00
2013-04-01 23:22:30 +02:00
return '' ;
2007-03-24 08:27:00 +01:00
}
2018-08-22 14:32:16 +02:00
/**
* Get password expiration value for a domain
2018-08-28 22:19:56 +02:00
* @ param string $domain - a string that may be a domain
* @ return int password expiration value for this domain ( DAYS , or zero if not enabled )
2018-08-22 14:32:16 +02:00
*/
2021-04-13 22:19:16 +02:00
function get_password_expiration_value ( $domain )
{
2018-12-28 20:27:33 +01:00
$table_domain = table_by_key ( 'domain' );
2019-01-06 21:25:24 +01:00
$query = " SELECT password_expiry FROM $table_domain WHERE domain= :domain " ;
2019-01-06 22:27:08 +01:00
$result = db_query_one ( $query , array ( 'domain' => $domain ));
2019-01-06 22:03:43 +01:00
if ( is_array ( $result ) && isset ( $result [ 'password_expiry' ])) {
2019-01-06 21:25:24 +01:00
return $result [ 'password_expiry' ];
}
2019-01-06 22:27:08 +01:00
return 0 ;
2018-08-22 14:32:16 +02:00
}
2007-03-24 08:27:00 +01:00
2007-10-25 14:29:38 +02:00
/**
* check_email
* Checks if an email is valid - if it is , return true , else false .
2018-12-28 20:27:33 +01:00
* @ todo make check_email able to handle already added domains
2018-06-10 22:21:45 +02:00
* @ 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
2007-10-25 14:29:38 +02:00
*/
2021-04-13 22:19:16 +02:00
function check_email ( $email )
{
2009-01-15 13:24:36 +01:00
$ce_email = $email ;
2007-08-17 17:10:59 +02:00
2009-01-15 13:24:36 +01:00
//strip the vacation domain out if we are using it
//and change from blah#foo.com@autoreply.foo.com to blah@foo.com
2017-03-21 17:43:27 +01:00
if ( Config :: bool ( 'vacation' )) {
2018-12-28 20:27:33 +01:00
$vacation_domain = Config :: read_string ( 'vacation_domain' );
2011-09-24 13:49:14 +02:00
$ce_email = preg_replace ( " /@ $vacation_domain\ $ / " , '' , $ce_email );
2009-01-15 13:24:36 +01:00
$ce_email = preg_replace ( " /#/ " , '@' , $ce_email );
}
2007-06-20 03:48:44 +02:00
2009-01-15 13:24:36 +01:00
// Perform non-domain-part sanity checks
2018-01-26 15:45:57 +01:00
if ( ! preg_match ( '/^[-!#$%&\'*+\\.\/0-9=?A-Z^_{|}~]+' . '@' . '[^@]+$/i' , $ce_email )) {
2018-12-28 20:27:33 +01:00
return " " . Config :: lang_f ( 'pInvalidMailRegex' , $email );
2009-01-15 13:24:36 +01:00
}
2007-03-24 08:27:00 +01:00
2018-02-18 20:59:37 +01:00
if ( function_exists ( 'filter_var' )) {
2018-02-10 21:56:56 +01:00
$check = filter_var ( $email , FILTER_VALIDATE_EMAIL );
2018-02-18 20:59:37 +01:00
if ( ! $check ) {
2018-12-28 20:27:33 +01:00
return " " . Config :: lang_f ( 'pInvalidMailRegex' , $email );
2018-02-10 21:56:56 +01:00
}
}
2009-01-15 13:24:36 +01:00
// Determine domain name
2018-12-28 20:27:33 +01:00
$matches = array ();
if ( preg_match ( '|@(.+)$|' , $ce_email , $matches )) {
$domain = $matches [ 1 ];
# check domain name
return " " . check_domain ( $domain );
2009-01-15 13:24:36 +01:00
}
2008-07-30 01:18:40 +02:00
2018-12-28 20:27:33 +01:00
return " " . Config :: lang_f ( 'pInvalidMailRegex' , $email );
2007-03-24 08:27:00 +01:00
}
2007-09-28 21:27:51 +02:00
/**
* Clean a string , escaping any meta characters that could be
2019-01-11 18:20:25 +01:00
* used to disrupt an SQL string . The method of the escaping is dependent on the underlying DB
* and MAY NOT be just \ ' ing. (e.g. sqlite and PgSQL change "it' s " to " it '' s " .
2007-09-28 21:27:51 +02:00
*
2019-01-11 18:12:16 +01:00
* The PDO quote function surrounds what you pass in with quote marks ; for legacy reasons we remove these ,
* but assume the caller will actually add them back in ( ! ) .
*
* e . g . caller code looks like :
*
* < code >
* $sql = " SELECT * FROM foo WHERE x = ' " . escape_string ( 'fish' ) . " ' " ;
* </ code >
*
2019-10-17 20:37:56 +02:00
* @ param int | string $string_or_int parameters to escape
2018-12-27 22:43:11 +01:00
* @ return string cleaned data , suitable for use within an SQL statement .
2007-09-28 21:27:51 +02:00
*/
2021-04-13 22:19:16 +02:00
function escape_string ( $string_or_int )
{
2019-01-06 22:03:43 +01:00
$link = db_connect ();
2019-01-16 10:24:33 +01:00
$string_or_int = ( string ) $string_or_int ;
$quoted = $link -> quote ( $string_or_int );
2019-01-11 18:12:16 +01:00
return trim ( $quoted , " ' " );
2007-03-24 08:27:00 +01:00
}
2007-10-25 14:29:38 +02:00
/**
* safeget
* Action : get value from $_GET [ $param ], or $default if $_GET [ $param ] is not set
* Call : $param = safeget ( 'param' ) # replaces $param = $_GET['param']
* - or -
* $param = safeget ( 'param' , 'default' )
*
2018-06-18 22:54:05 +02:00
* @ param string $param parameter name .
2020-11-09 22:40:52 +01:00
* @ param string $default ( optional ) - default value if key is not set .
* @ return string
2007-10-25 14:29:38 +02:00
*/
2021-04-13 22:19:16 +02:00
function safeget ( $param , $default = " " )
{
2018-09-07 12:01:18 +02:00
$retval = $default ;
2020-11-09 22:40:52 +01:00
if ( isset ( $_GET [ $param ]) && is_string ( $_GET [ $param ])) {
2018-09-07 12:01:18 +02:00
$retval = $_GET [ $param ];
2018-01-26 15:45:57 +01:00
}
2009-01-15 13:24:36 +01:00
return $retval ;
2007-10-23 01:49:10 +02:00
}
2007-10-25 14:29:38 +02:00
/**
2018-02-10 22:00:58 +01:00
* safepost - similar to safeget () but for $_POST
2007-10-25 14:29:38 +02:00
* @ see safeget ()
2018-02-10 22:00:58 +01:00
* @ param string $param parameter name
* @ param string $default ( optional ) default value ( defaults to " " )
2020-11-09 22:40:52 +01:00
* @ return string - value in $_POST [ $param ] or $default
2007-10-25 14:29:38 +02:00
*/
2021-04-13 22:19:16 +02:00
function safepost ( $param , $default = " " )
{
2018-09-07 12:01:18 +02:00
$retval = $default ;
2020-11-09 22:40:52 +01:00
if ( isset ( $_POST [ $param ]) && is_string ( $_POST [ $param ])) {
2018-09-07 12:01:18 +02:00
$retval = $_POST [ $param ];
2018-01-26 15:45:57 +01:00
}
2009-01-15 13:24:36 +01:00
return $retval ;
2007-10-23 01:49:10 +02:00
}
2007-10-25 14:29:38 +02:00
/**
* safeserver
* @ see safeget ()
2018-02-10 22:00:58 +01:00
* @ param string $param
* @ param string $default ( optional )
* @ return string value from $_SERVER [ $param ] or $default
2007-10-25 14:29:38 +02:00
*/
2021-04-13 22:19:16 +02:00
function safeserver ( $param , $default = " " )
{
2018-09-07 12:01:18 +02:00
$retval = $default ;
2018-01-26 15:45:57 +01:00
if ( isset ( $_SERVER [ $param ])) {
2018-09-07 12:01:18 +02:00
$retval = $_SERVER [ $param ];
2018-01-26 15:45:57 +01:00
}
2009-01-15 13:24:36 +01:00
return $retval ;
2007-10-23 01:49:10 +02:00
}
2007-12-30 02:32:33 +01:00
/**
* safecookie
* @ see safeget ()
2018-02-10 22:00:58 +01:00
* @ param string $param
* @ param string $default ( optional )
* @ return string value from $_COOKIE [ $param ] or $default
2007-12-30 02:32:33 +01:00
*/
2021-04-13 22:19:16 +02:00
function safecookie ( $param , $default = " " )
{
2018-09-07 12:01:18 +02:00
$retval = $default ;
2020-11-09 22:40:52 +01:00
if ( isset ( $_COOKIE [ $param ]) && is_string ( $_COOKIE [ $param ])) {
2018-09-07 12:01:18 +02:00
$retval = $_COOKIE [ $param ];
2018-01-26 15:45:57 +01:00
}
2009-01-15 13:24:36 +01:00
return $retval ;
2007-12-30 02:32:33 +01:00
}
2013-12-01 20:05:14 +01:00
/**
* safesession
* @ see safeget ()
2018-02-10 22:00:58 +01:00
* @ param string $param
* @ param string $default ( optional )
* @ return string value from $_SESSION [ $param ] or $default
2013-12-01 20:05:14 +01:00
*/
2021-04-13 22:19:16 +02:00
function safesession ( $param , $default = " " )
{
2018-09-07 12:01:18 +02:00
$retval = $default ;
2020-11-09 22:40:52 +01:00
if ( isset ( $_SESSION [ $param ]) && is_string ( $_SESSION [ $param ])) {
2018-09-07 12:01:18 +02:00
$retval = $_SESSION [ $param ];
2018-01-26 15:45:57 +01:00
}
2013-12-01 20:05:14 +01:00
return $retval ;
}
2011-07-30 00:06:52 +02:00
/**
* pacol
* @ param int $allow_editing
* @ param int $display_in_form
* @ param int display_in_list
2018-02-10 22:00:58 +01:00
* @ param string $type
* @ param string PALANG_label
* @ param string PALANG_desc
2011-07-30 00:06:52 +02:00
* @ param any optional $default
2018-02-10 22:00:58 +01:00
* @ param array $options optional options
2018-04-22 20:09:13 +02:00
* @ param int or $not_in_db - if array , can contain the remaining parameters as associated array . Otherwise counts as $not_in_db
2011-07-30 00:06:52 +02:00
* @ return array for $struct
*/
2021-04-13 22:19:16 +02:00
function pacol ( $allow_editing , $display_in_form , $display_in_list , $type , $PALANG_label , $PALANG_desc , $default = " " , $options = array (), $multiopt = 0 , $dont_write_to_db = 0 , $select = " " , $extrafrom = " " , $linkto = " " )
{
2018-01-26 15:45:57 +01:00
if ( $PALANG_label != '' ) {
$PALANG_label = Config :: lang ( $PALANG_label );
}
if ( $PALANG_desc != '' ) {
$PALANG_desc = Config :: lang ( $PALANG_desc );
}
2011-07-30 00:06:52 +02:00
2014-11-01 22:11:33 +01:00
if ( is_array ( $multiopt )) { # remaining parameters provided in named array
$not_in_db = 0 ; # keep default value
foreach ( $multiopt as $key => $value ) {
$$key = $value ; # extract everything to the matching variable
}
} else {
$not_in_db = $multiopt ;
}
2011-07-30 00:06:52 +02:00
return array (
'editable' => $allow_editing ,
'display_in_form' => $display_in_form ,
'display_in_list' => $display_in_list ,
'type' => $type ,
'label' => $PALANG_label , # $PALANG field label
'desc' => $PALANG_desc , # $PALANG field description
'default' => $default ,
'options' => $options ,
'not_in_db' => $not_in_db ,
2011-10-25 23:28:55 +02:00
'dont_write_to_db' => $dont_write_to_db ,
2011-10-23 00:49:01 +02:00
'select' => $select , # replaces the field name after SELECT
'extrafrom' => $extrafrom , # added after FROM xy - useful for JOINs etc.
2014-11-01 22:11:33 +01:00
'linkto' => $linkto , # make the value a link - %s will be replaced with the ID
2011-07-30 00:06:52 +02:00
);
}
2007-03-24 08:27:00 +01:00
2018-06-18 22:54:05 +02:00
/**
* Action : Get all the properties of a domain .
* @ param string $domain
* @ return array
*/
2021-04-13 22:19:16 +02:00
function get_domain_properties ( $domain )
{
2011-10-30 00:58:08 +02:00
$handler = new DomainHandler ();
if ( ! $handler -> init ( $domain )) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " Error: " . join ( " \n " , $handler -> errormsg ));
2009-01-15 13:24:36 +01:00
}
2011-10-30 00:58:08 +02:00
if ( ! $handler -> view ()) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " Error: " . join ( " \n " , $handler -> errormsg ));
2009-01-15 13:24:36 +01:00
}
2013-10-13 18:11:15 +02:00
$result = $handler -> result ();
2011-10-30 00:58:08 +02:00
return $result ;
2007-03-24 08:27:00 +01:00
}
2011-04-10 00:37:11 +02:00
/**
* create_page_browser
* Action : Get page browser for a long list of mailboxes , aliases etc .
*
2019-09-15 22:08:08 +02:00
* @ param string $idxfield - database field name to use as title e . g . alias . address
* @ param string $querypart - core part of the query ( starting at " FROM " ) e . g . FROM alias WHERE address like ...
2018-06-18 22:54:05 +02:00
* @ return array
2011-04-10 00:37:11 +02:00
*/
2021-04-13 22:19:16 +02:00
function create_page_browser ( $idxfield , $querypart , $sql_params = [])
{
2011-04-10 00:37:11 +02:00
global $CONF ;
2011-04-10 18:30:49 +02:00
$page_size = ( int ) $CONF [ 'page_size' ];
2011-04-10 00:37:11 +02:00
$label_len = 2 ;
$pagebrowser = array ();
2018-12-28 20:27:33 +01:00
$count_results = 0 ;
2011-04-10 18:30:49 +02:00
if ( $page_size < 2 ) { # will break the page browser
2019-09-14 22:01:09 +02:00
throw new Exception ( '$CONF[\'page_size\'] must be 2 or more!' );
2011-04-10 18:30:49 +02:00
}
# get number of rows
2012-05-28 19:27:17 +02:00
$query = " SELECT count(*) as counter FROM (SELECT $idxfield $querypart ) AS tmp " ;
2019-02-15 22:33:55 +01:00
$result = db_query_one ( $query , $sql_params );
2019-01-06 21:25:24 +01:00
if ( $result && isset ( $result [ 'counter' ])) {
$count_results = $result [ 'counter' ] - 1 ; # we start counting at 0, not 1
2011-04-10 18:30:49 +02:00
}
if ( $count_results < $page_size ) {
return array (); # only one page - no pagebrowser required
}
2011-04-10 00:37:11 +02:00
2011-04-10 18:30:49 +02:00
# init row counter
2019-01-08 19:33:14 +01:00
$initcount = " SET @r=-1 " ;
2013-11-16 01:00:53 +01:00
if ( db_pgsql ()) {
2011-04-10 21:20:31 +02:00
$initcount = " CREATE TEMPORARY SEQUENCE rowcount MINVALUE 0 " ;
}
2016-02-04 23:30:06 +01:00
if ( ! db_sqlite ()) {
2019-01-06 22:27:08 +01:00
db_execute ( $initcount );
2016-02-04 23:30:06 +01:00
}
2011-04-10 00:37:11 +02:00
2011-04-10 18:30:49 +02:00
# get labels for relevant rows (first and last of each page)
$page_size_zerobase = $page_size - 1 ;
$query = "
2012-05-13 23:09:03 +02:00
SELECT * FROM (
2019-01-08 19:33:14 +01:00
SELECT $idxfield AS label , @ r := @ r + 1 AS 'r' $querypart
) idx WHERE MOD ( idx . r , $page_size ) IN ( 0 , $page_size_zerobase ) OR idx . r = $count_results
2017-03-21 17:43:27 +01:00
" ;
2012-05-13 23:09:03 +02:00
2013-11-16 01:00:53 +01:00
if ( db_pgsql ()) {
2011-04-10 21:20:31 +02:00
$query = "
SELECT * FROM (
2019-12-08 17:23:49 +01:00
SELECT $idxfield AS label , nextval ( 'rowcount' ) AS r $querypart
) idx WHERE MOD ( idx . r , $page_size ) IN ( 0 , $page_size_zerobase ) OR idx . r = $count_results
2017-03-21 17:43:27 +01:00
" ;
2011-04-10 21:20:31 +02:00
}
2016-02-04 23:30:06 +01:00
if ( db_sqlite ()) {
2019-10-17 20:30:00 +02:00
$end = $idxfield ;
2019-10-19 22:36:27 +02:00
if ( strpos ( $idxfield , '.' ) !== false ) {
2019-10-17 20:30:00 +02:00
$bits = explode ( '.' , $idxfield );
$end = $bits [ 1 ];
}
2016-02-04 23:30:06 +01:00
$query = "
WITH idx AS ( SELECT * $querypart )
2019-12-08 17:23:49 +01:00
SELECT $end AS label , ( SELECT ( COUNT ( * ) - 1 ) FROM idx t1 WHERE t1 . $end <= t2 . $end ) AS r
2016-02-04 23:30:06 +01:00
FROM idx t2
2019-12-08 17:23:49 +01:00
WHERE ( r % $page_size ) IN ( 0 , $page_size_zerobase ) OR r = $count_results " ;
2016-02-04 23:30:06 +01:00
}
2018-01-26 15:45:57 +01:00
# PostgreSQL:
# http://www.postgresql.org/docs/8.1/static/sql-createsequence.html
# http://www.postgresonline.com/journal/archives/79-Simulating-Row-Number-in-PostgreSQL-Pre-8.4.html
# http://www.pg-forum.de/sql/1518-nummerierung-der-abfrageergebnisse.html
# CREATE TEMPORARY SEQUENCE foo MINVALUE 0 MAXVALUE $page_size_zerobase CYCLE
# afterwards: DROP SEQUENCE foo
2011-04-10 00:37:11 +02:00
2019-02-15 22:33:55 +01:00
$result = db_query_all ( $query , $sql_params );
2019-08-13 10:24:30 +02:00
for ( $k = 0 ; $k < count ( $result ); $k += 2 ) {
2019-01-06 22:03:43 +01:00
if ( isset ( $result [ $k + 1 ])) {
2019-07-20 00:11:54 +02:00
$label = substr ( $result [ $k ][ 'label' ], 0 , $label_len ) . '-' . substr ( $result [ $k + 1 ][ 'label' ], 0 , $label_len );
2019-01-06 22:03:43 +01:00
} else {
2019-07-20 00:11:54 +02:00
$label = substr ( $result [ $k ][ 'label' ], 0 , $label_len );
2011-04-10 00:37:11 +02:00
}
2019-01-06 21:25:24 +01:00
$pagebrowser [] = $label ;
2011-04-10 00:37:11 +02:00
}
2013-11-16 01:00:53 +01:00
if ( db_pgsql ()) {
2019-01-06 22:27:08 +01:00
db_execute ( " DROP SEQUENCE rowcount " );
2011-04-10 21:20:31 +02:00
}
2011-04-10 00:37:11 +02:00
return $pagebrowser ;
}
2007-03-24 08:27:00 +01:00
2018-06-10 22:21:45 +02:00
/**
* Recalculates the quota from MBs to bytes ( divide , / )
* @ param int $quota
* @ return float
*/
2021-04-13 22:19:16 +02:00
function divide_quota ( $quota )
{
2018-01-26 15:45:57 +01:00
if ( $quota == - 1 ) {
return $quota ;
}
2018-12-28 20:27:33 +01:00
$value = round ( $quota / ( int ) Config :: read_string ( 'quota_multiplier' ), 2 );
2009-01-15 13:24:36 +01:00
return $value ;
2007-03-24 08:27:00 +01:00
}
2018-06-10 22:21:45 +02:00
/**
* Checks if the admin is the owner of the domain ( or global - admin )
* @ param string $username
* @ param string $domain
* @ return bool
*/
2021-04-13 22:19:16 +02:00
function check_owner ( $username , $domain )
{
2013-12-08 20:41:01 +01:00
$table_domain_admins = table_by_key ( 'domain_admins' );
2012-06-03 00:13:27 +02:00
2019-01-06 22:27:08 +01:00
$result = db_query_all (
2019-01-06 21:25:24 +01:00
" SELECT 1 FROM $table_domain_admins WHERE username= ? AND (domain = ? OR domain = 'ALL') AND active = ? " ,
array ( $username , $domain , db_get_boolean ( true ))
);
2019-01-06 22:03:43 +01:00
if ( sizeof ( $result ) == 1 || sizeof ( $result ) == 2 ) { # "ALL" + specific domain permissions is possible
2012-06-03 00:13:27 +02:00
# TODO: if superadmin, check if given domain exists in the database
return true ;
} else {
2019-01-06 21:25:24 +01:00
if ( sizeof ( $result ) > 2 ) { # more than 2 results means something really strange happened...
2012-06-03 00:13:27 +02:00
flash_error ( " Permission check returned multiple results. Please go to 'edit admin' for your username and press the save "
2009-11-30 13:48:25 +01:00
. " button once to fix the database. If this doesn't help, open a bugreport. " );
2012-06-03 00:13:27 +02:00
}
2009-01-15 13:24:36 +01:00
return false ;
}
2007-03-24 08:27:00 +01:00
}
2007-09-28 21:27:51 +02:00
/**
2017-03-21 17:43:27 +01:00
* List domains for an admin user .
2021-03-02 22:33:00 +01:00
* @ param string $username
2007-09-28 21:27:51 +02:00
* @ return array of domain names .
*/
2021-04-13 22:19:16 +02:00
function list_domains_for_admin ( $username )
{
2013-12-08 20:41:01 +01:00
$table_domain = table_by_key ( 'domain' );
$table_domain_admins = table_by_key ( 'domain_admins' );
2009-01-15 13:24:36 +01:00
2018-12-28 20:27:33 +01:00
$condition = array ();
2011-11-12 21:44:35 +01:00
$E_username = escape_string ( $username );
$query = " SELECT $table_domain .domain FROM $table_domain " ;
$condition [] = " $table_domain .domain != 'ALL' " ;
2019-01-06 21:25:24 +01:00
$pvalues = array ();
2019-01-06 22:27:08 +01:00
$result = db_query_one ( " SELECT username FROM $table_domain_admins WHERE username= :username AND domain='ALL' " , array ( 'username' => $username ));
2019-01-06 21:25:24 +01:00
if ( empty ( $result )) { # not a superadmin
$pvalues [ 'username' ] = $username ;
$pvalues [ 'active' ] = db_get_boolean ( true );
$pvalues [ 'backupmx' ] = db_get_boolean ( false );
2011-11-12 21:44:35 +01:00
$query .= " LEFT JOIN $table_domain_admins ON $table_domain .domain= $table_domain_admins .domain " ;
2019-01-06 21:25:24 +01:00
$condition [] = " $table_domain_admins .username = :username " ;
$condition [] = " $table_domain .active = :active " ; # TODO: does it really make sense to exclude inactive...
$condition [] = " $table_domain .backupmx = :backupmx " ; # TODO: ... and backupmx domains for non-superadmins?
2011-11-12 21:44:35 +01:00
}
$query .= " WHERE " . join ( ' AND ' , $condition );
$query .= " ORDER BY $table_domain .domain " ;
2019-01-06 22:27:08 +01:00
$result = db_query_all ( $query , $pvalues );
2019-01-06 21:25:24 +01:00
return array_column ( $result , 'domain' );
}
2018-06-10 22:21:45 +02:00
/**
* List all available domains .
*
* @ return array
*/
2021-04-13 22:19:16 +02:00
function list_domains ()
{
2009-01-15 13:24:36 +01:00
$list = array ();
2007-03-24 08:27:00 +01:00
2013-12-08 20:41:01 +01:00
$table_domain = table_by_key ( 'domain' );
2019-01-06 22:27:08 +01:00
$result = db_query_all ( " SELECT domain FROM $table_domain WHERE domain!='ALL' ORDER BY domain " );
2019-01-06 21:25:24 +01:00
$i = 0 ;
2019-01-06 22:03:43 +01:00
foreach ( $result as $row ) {
2019-01-06 21:25:24 +01:00
$list [ $i ] = $row [ 'domain' ];
$i ++ ;
2009-01-15 13:24:36 +01:00
}
return $list ;
2007-03-24 08:27:00 +01:00
}
//
// list_admins
// Action: Lists all the admins
// Call: list_admins ()
//
// was admin_list_admins
//
2021-04-13 22:19:16 +02:00
function list_admins ()
{
2012-05-08 00:53:04 +02:00
$handler = new AdminHandler ();
2007-03-24 08:27:00 +01:00
2013-10-13 18:11:15 +02:00
$handler -> getList ( '' );
2019-01-06 22:03:43 +01:00
2013-10-13 18:11:15 +02:00
return $handler -> result ();
2007-03-24 08:27:00 +01:00
}
//
// encode_header
// Action: Encode a string according to RFC 1522 for use in headers if it contains 8-bit characters.
// Call: encode_header (string header, string charset)
//
2021-04-13 22:19:16 +02:00
function encode_header ( $string , $default_charset = " utf-8 " )
{
2018-01-26 15:45:57 +01:00
if ( strtolower ( $default_charset ) == 'iso-8859-1' ) {
$string = str_replace ( " \240 " , ' ' , $string );
2009-01-15 13:24:36 +01:00
}
2018-01-26 15:45:57 +01:00
$j = strlen ( $string );
$max_l = 75 - strlen ( $default_charset ) - 7 ;
$aRet = array ();
2009-01-15 13:24:36 +01:00
$ret = '' ;
$iEncStart = $enc_init = false ;
$cur_l = $iOffset = 0 ;
2011-06-02 22:51:12 +02:00
for ( $i = 0 ; $i < $j ; ++ $i ) {
2020-01-13 16:35:46 +01:00
switch ( $string [ $i ]) {
2011-06-02 22:51:12 +02:00
case '=' :
case '<' :
case '>' :
case ',' :
case '?' :
case '_' :
if ( $iEncStart === false ) {
$iEncStart = $i ;
}
$cur_l += 3 ;
if ( $cur_l > ( $max_l - 2 )) {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset , $iEncStart - $iOffset );
2009-01-15 13:24:36 +01:00
$aRet [] = " =? $default_charset ?Q? $ret ?= " ;
$iOffset = $i ;
$cur_l = 0 ;
$ret = '' ;
$iEncStart = false ;
2011-06-02 22:51:12 +02:00
} else {
2020-01-31 15:28:54 +01:00
$ret .= sprintf ( " =%02X " , ord ( $string [ $i ]));
2009-01-15 13:24:36 +01:00
}
2011-06-02 22:51:12 +02:00
break ;
case '(' :
case ')' :
if ( $iEncStart !== false ) {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset , $iEncStart - $iOffset );
2011-06-02 22:51:12 +02:00
$aRet [] = " =? $default_charset ?Q? $ret ?= " ;
2009-01-15 13:24:36 +01:00
$iOffset = $i ;
2011-06-02 22:51:12 +02:00
$cur_l = 0 ;
$ret = '' ;
$iEncStart = false ;
2009-01-15 13:24:36 +01:00
}
2011-06-02 22:51:12 +02:00
break ;
case ' ' :
if ( $iEncStart !== false ) {
2009-01-15 13:24:36 +01:00
$cur_l ++ ;
2011-06-02 22:51:12 +02:00
if ( $cur_l > $max_l ) {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset , $iEncStart - $iOffset );
2009-01-15 13:24:36 +01:00
$aRet [] = " =? $default_charset ?Q? $ret ?= " ;
$iOffset = $i ;
$cur_l = 0 ;
$ret = '' ;
2011-06-02 22:51:12 +02:00
$iEncStart = false ;
} else {
$ret .= '_' ;
2009-01-15 13:24:36 +01:00
}
2011-06-02 22:51:12 +02:00
}
break ;
default :
2020-01-31 15:28:54 +01:00
$k = ord ( $string [ $i ]);
2011-06-02 22:51:12 +02:00
if ( $k > 126 ) {
if ( $iEncStart === false ) {
// do not start encoding in the middle of a string, also take the rest of the word.
2018-01-26 15:45:57 +01:00
$sLeadString = substr ( $string , 0 , $i );
$aLeadString = explode ( ' ' , $sLeadString );
$sToBeEncoded = array_pop ( $aLeadString );
$iEncStart = $i - strlen ( $sToBeEncoded );
2011-06-02 22:51:12 +02:00
$ret .= $sToBeEncoded ;
2018-01-26 15:45:57 +01:00
$cur_l += strlen ( $sToBeEncoded );
2011-06-02 22:51:12 +02:00
}
$cur_l += 3 ;
// first we add the encoded string that reached it's max size
if ( $cur_l > ( $max_l - 2 )) {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset , $iEncStart - $iOffset );
2011-06-02 22:51:12 +02:00
$aRet [] = " =? $default_charset ?Q? $ret ?= " ;
$cur_l = 3 ;
$ret = '' ;
$iOffset = $i ;
$iEncStart = $i ;
}
$enc_init = true ;
2018-01-26 15:45:57 +01:00
$ret .= sprintf ( " =%02X " , $k );
2011-06-02 22:51:12 +02:00
} else {
if ( $iEncStart !== false ) {
$cur_l ++ ;
if ( $cur_l > $max_l ) {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset , $iEncStart - $iOffset );
2011-06-02 22:51:12 +02:00
$aRet [] = " =? $default_charset ?Q? $ret ?= " ;
$iEncStart = false ;
$iOffset = $i ;
$cur_l = 0 ;
$ret = '' ;
} else {
2020-01-31 15:40:38 +01:00
$ret .= $string [ $i ];
2011-06-02 22:51:12 +02:00
}
2009-01-15 13:24:36 +01:00
}
}
2011-06-02 22:51:12 +02:00
break ;
# end switch
2009-01-15 13:24:36 +01:00
}
}
2011-06-02 22:51:12 +02:00
if ( $enc_init ) {
if ( $iEncStart !== false ) {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset , $iEncStart - $iOffset );
2009-01-15 13:24:36 +01:00
$aRet [] = " =? $default_charset ?Q? $ret ?= " ;
2011-06-02 22:51:12 +02:00
} else {
2018-01-26 15:45:57 +01:00
$aRet [] = substr ( $string , $iOffset );
2009-01-15 13:24:36 +01:00
}
2018-01-26 15:45:57 +01:00
$string = implode ( '' , $aRet );
2009-01-15 13:24:36 +01:00
}
return $string ;
2007-03-24 08:27:00 +01:00
}
2018-12-29 21:54:25 +01:00
2018-12-27 14:55:21 +01:00
2018-05-02 22:10:06 +02:00
/**
* Generate a random password of $length characters .
* @ param int $length ( optional , default : 12 )
* @ return string
*
*/
2021-04-13 22:19:16 +02:00
function generate_password ( $length = 12 )
{
2011-04-10 16:16:17 +02:00
// define possible characters
$possible = " 2345678923456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ " ; # skip 0 and 1 to avoid confusion with O and l
// add random characters to $password until $length is reached
$password = " " ;
while ( strlen ( $password ) < $length ) {
2018-05-04 22:36:05 +02:00
$random = random_int ( 0 , strlen ( $possible ) - 1 );
2018-04-29 22:53:38 +02:00
$char = substr ( $possible , $random , 1 );
2011-04-10 16:16:17 +02:00
// we don't want this character if it's already in the password
if ( ! strstr ( $password , $char )) {
$password .= $char ;
}
}
2009-01-15 13:24:36 +01:00
return $password ;
2007-03-24 08:27:00 +01:00
}
2011-09-25 20:39:20 +02:00
/**
* Check if a password is strong enough based on the conditions in $CONF [ 'password_validation' ]
2018-04-29 22:53:38 +02:00
* @ param string $password
2011-09-25 20:39:20 +02:00
* @ return array of error messages , or empty array if the password is ok
*/
2021-04-13 22:19:16 +02:00
function validate_password ( $password )
{
2011-09-25 20:39:20 +02:00
$result = array ();
2018-12-28 20:27:33 +01:00
$val_conf = Config :: read_array ( 'password_validation' );
2011-09-25 20:39:20 +02:00
2021-01-26 21:47:45 +01:00
if ( Config :: has ( 'min_password_length' )) {
$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 " ;
}
2011-09-25 20:39:20 +02:00
}
2013-03-03 01:34:53 +01:00
foreach ( $val_conf as $regex => $message ) {
2020-11-09 22:37:47 +01:00
if ( is_callable ( $message )) {
$ret = $message ( $password );
if ( ! empty ( $ret )) {
$result [] = $ret ;
}
continue ;
}
2021-03-02 22:33:00 +01:00
2011-09-25 20:39:20 +02:00
if ( ! preg_match ( $regex , $password )) {
$msgparts = preg_split ( " / / " , $message , 2 );
if ( count ( $msgparts ) == 1 ) {
2013-10-13 20:11:18 +02:00
$result [] = Config :: lang ( $msgparts [ 0 ]);
2011-09-25 20:39:20 +02:00
} else {
2013-10-13 20:11:18 +02:00
$result [] = sprintf ( Config :: lang ( $msgparts [ 0 ]), $msgparts [ 1 ]);
2011-09-25 20:39:20 +02:00
}
}
}
2013-03-03 01:34:53 +01:00
2011-09-25 20:39:20 +02:00
return $result ;
}
2019-02-15 22:34:49 +01:00
/**
* @ param string $pw
* @ param string $pw_db - encrypted hash
* @ return string crypt ' ed password , should equal $pw_db if $pw matches the original
2021-05-08 22:56:05 +02:00
* @ deprecated
2019-02-15 22:34:49 +01:00
*/
2021-04-13 22:19:16 +02:00
function _pacrypt_md5crypt ( $pw , $pw_db = '' )
{
2019-02-15 22:34:49 +01:00
if ( $pw_db ) {
$split_salt = preg_split ( '/\$/' , $pw_db );
if ( isset ( $split_salt [ 2 ])) {
$salt = $split_salt [ 2 ];
return md5crypt ( $pw , $salt );
}
2018-02-19 22:01:23 +01:00
}
2018-02-19 21:59:52 +01:00
2018-02-19 22:01:23 +01:00
return md5crypt ( $pw );
2018-02-19 21:59:52 +01:00
}
2021-01-12 23:04:51 +01:00
/**
* @ todo fix this to not throw an E_NOTICE or deprecate / remove .
2021-05-08 22:56:05 +02:00
* @ deprecated
2021-01-12 23:04:51 +01:00
*/
2021-04-13 22:19:16 +02:00
function _pacrypt_crypt ( $pw , $pw_db = '' )
{
2018-02-19 22:01:23 +01:00
if ( $pw_db ) {
return crypt ( $pw , $pw_db );
}
2021-09-29 12:52:31 +02:00
// PHP8 - we have to specify a salt here....
$salt = substr ( str_shuffle ( '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ), 0 , 2 );
return crypt ( $pw , $salt );
2018-02-19 21:59:52 +01:00
}
2019-02-15 22:34:49 +01:00
/**
* Crypt with MySQL ' s ENCRYPT function
*
* @ param string $pw
* @ param string $pw_db ( hashed password )
* @ return string if $pw_db and the return value match then $pw matches the original password .
*/
2021-04-13 22:19:16 +02:00
function _pacrypt_mysql_encrypt ( $pw , $pw_db = '' )
{
2018-02-19 21:59:52 +01:00
// See https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583
// this is apparently useful for pam_mysql etc.
2019-02-15 22:34:49 +01:00
if ( $pw_db ) {
$res = db_query_one ( " SELECT ENCRYPT(:pw,:pw_db) as result " , [ 'pw' => $pw , 'pw_db' => $pw_db ]);
2018-02-19 22:01:23 +01:00
} else {
2021-01-12 23:04:51 +01:00
// see https://security.stackexchange.com/questions/150687/is-it-safe-to-use-the-encrypt-function-in-mysql-to-hash-passwords
// if no existing password, use a random SHA512 salt.
2021-01-16 22:41:45 +01:00
$salt = _php_crypt_generate_crypt_salt ();
$res = db_query_one ( " SELECT ENCRYPT(:pw, CONCAT(' $ 6 $ ', ' $salt ')) as result " , [ 'pw' => $pw ]);
2018-02-19 22:01:23 +01:00
}
2019-01-06 21:25:24 +01:00
return $res [ 'result' ];
2018-02-19 21:59:52 +01:00
}
2019-02-15 22:34:49 +01:00
/**
* Create / Validate courier authlib style crypt ' ed passwords . ( md5 , md5raw , crypt , sha1 )
*
* @ param string $pw
* @ param string $pw_db ( optional )
* @ return string crypted password - contains { xxx } prefix to identify mechanism .
*/
2021-04-13 22:19:16 +02:00
function _pacrypt_authlib ( $pw , $pw_db )
{
2018-02-19 21:59:52 +01:00
global $CONF ;
$flavor = $CONF [ 'authlib_default_flavor' ];
2018-02-19 22:01:23 +01:00
$salt = substr ( create_salt (), 0 , 2 ); # courier-authlib supports only two-character salts
if ( preg_match ( '/^{.*}/' , $pw_db )) {
// we have a flavor in the db -> use it instead of default flavor
$result = preg_split ( '/[{}]/' , $pw_db , 3 ); # split at { and/or }
$flavor = $result [ 1 ];
$salt = substr ( $result [ 2 ], 0 , 2 );
}
if ( stripos ( $flavor , 'md5raw' ) === 0 ) {
$password = '{' . $flavor . '}' . md5 ( $pw );
} elseif ( stripos ( $flavor , 'md5' ) === 0 ) {
$password = '{' . $flavor . '}' . base64_encode ( md5 ( $pw , true ));
} elseif ( stripos ( $flavor , 'crypt' ) === 0 ) {
$password = '{' . $flavor . '}' . crypt ( $pw , $salt );
} elseif ( stripos ( $flavor , 'SHA' ) === 0 ) {
$password = '{' . $flavor . '}' . base64_encode ( sha1 ( $pw , true ));
} else {
2019-09-14 22:01:09 +02:00
throw new Exception ( " authlib_default_flavor ' " . $flavor . " ' unknown. Valid flavors are 'md5raw', 'md5', 'SHA' and 'crypt' " );
2018-02-19 22:01:23 +01:00
}
return $password ;
2018-02-19 21:59:52 +01:00
}
2018-04-29 22:53:38 +02:00
/**
2019-02-15 22:34:49 +01:00
* Uses the doveadm pw command , crypted passwords have a { ... } prefix to identify type .
*
2018-04-29 22:53:38 +02:00
* @ param string $pw - plain text password
* @ param string $pw_db - encrypted password , or '' for generation .
2019-02-15 22:34:49 +01:00
* @ return string crypted password
2018-04-29 22:53:38 +02:00
*/
2021-04-13 22:19:16 +02:00
function _pacrypt_dovecot ( $pw , $pw_db = '' )
{
2018-02-19 21:59:52 +01:00
global $CONF ;
2018-02-19 22:01:23 +01:00
$split_method = preg_split ( '/:/' , $CONF [ 'encrypt' ]);
$method = strtoupper ( $split_method [ 1 ]);
# If $pw_db starts with {method}, change $method accordingly
if ( ! empty ( $pw_db ) && preg_match ( '/^\{([A-Z0-9.-]+)\}.+/' , $pw_db , $method_matches )) {
$method = $method_matches [ 1 ];
}
if ( ! preg_match ( " /^[A-Z0-9.-]+ $ / " , $method )) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " invalid dovecot encryption method " );
2018-02-19 22:01:23 +01:00
}
2019-02-19 20:50:21 +01:00
# digest-md5 hashes include the username - until someone implements it, let's declare it as unsupported
2018-02-19 22:01:23 +01:00
if ( strtolower ( $method ) == 'digest-md5' ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " Sorry, \$ CONF['encrypt'] = 'dovecot:digest-md5' is not supported by PostfixAdmin. " );
2018-02-19 22:01:23 +01:00
}
2018-06-10 22:21:45 +02:00
# TODO: add -u option for those hashes, or for everything that is salted (-u was available before dovecot 2.1 -> no problem with backward compatibility )
2018-02-19 22:01:23 +01:00
$dovecotpw = " doveadm pw " ;
if ( ! empty ( $CONF [ 'dovecotpw' ])) {
$dovecotpw = $CONF [ 'dovecotpw' ];
}
# Use proc_open call to avoid safe_mode problems and to prevent showing plain password in process table
$spec = array (
0 => array ( " pipe " , " r " ), // stdin
1 => array ( " pipe " , " w " ), // stdout
2 => array ( " pipe " , " w " ), // stderr
);
$nonsaltedtypes = " SHA|SHA1|SHA256|SHA512|CLEAR|CLEARTEXT|PLAIN|PLAIN-TRUNC|CRAM-MD5|HMAC-MD5|PLAIN-MD4|PLAIN-MD5|LDAP-MD5|LANMAN|NTLM|RPA " ;
$salted = ! preg_match ( " /^( $nonsaltedtypes )( \ .B64| \ .BASE64| \ .HEX)? $ / " , strtoupper ( $method ));
$dovepasstest = '' ;
if ( $salted && ( ! empty ( $pw_db ))) {
# only use -t for salted passwords to be backward compatible with dovecot < 2.1
$dovepasstest = " -t " . escapeshellarg ( $pw_db );
}
2020-08-13 17:59:40 +02:00
$pipes = [];
2018-02-19 22:01:23 +01:00
$pipe = proc_open ( " $dovecotpw '-s' $method $dovepasstest " , $spec , $pipes );
if ( ! $pipe ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " can't proc_open $dovecotpw " );
2018-02-19 22:01:23 +01:00
}
// use dovecot's stdin, it uses getpass() twice (except when using -t)
// Write pass in pipe stdin
if ( empty ( $dovepasstest )) {
fwrite ( $pipes [ 0 ], $pw . " \n " , 1 + strlen ( $pw ));
usleep ( 1000 );
}
2020-08-06 20:42:34 +02:00
2018-02-19 22:01:23 +01:00
fwrite ( $pipes [ 0 ], $pw . " \n " , 1 + strlen ( $pw ));
fclose ( $pipes [ 0 ]);
2020-08-06 16:33:37 +02:00
$stderr_output = stream_get_contents ( $pipes [ 2 ]);
2018-02-19 22:01:23 +01:00
// Read hash from pipe stdout
2018-12-28 20:27:33 +01:00
$password = fread ( $pipes [ 1 ], 200 );
2018-02-19 22:01:23 +01:00
2020-08-06 20:42:34 +02:00
if ( ! empty ( $stderr_output ) || empty ( $password )) {
error_log ( " Failed to read password from $dovecotpw ... stderr: $stderr_output , password: $password " );
2020-08-06 21:43:47 +02:00
throw new Exception ( " $dovecotpw failed, see error log for details " );
2020-08-06 16:33:37 +02:00
}
2018-02-19 22:01:23 +01:00
if ( empty ( $dovepasstest )) {
if ( ! preg_match ( '/^\{' . $method . '\}/' , $password )) {
2020-08-06 16:33:37 +02:00
error_log ( " dovecotpw password encryption failed (method: $method ) . stderr: $stderr_output " );
2019-09-14 22:01:09 +02:00
throw new Exception ( " can't encrypt password with dovecotpw, see error log for details " );
2018-02-19 22:01:23 +01:00
}
} else {
if ( ! preg_match ( '(verified)' , $password )) {
$password = " Thepasswordcannotbeverified " ;
} else {
$password = rtrim ( str_replace ( '(verified)' , '' , $password ));
}
}
fclose ( $pipes [ 1 ]);
fclose ( $pipes [ 2 ]);
proc_close ( $pipe );
if (( ! empty ( $pw_db )) && ( substr ( $pw_db , 0 , 1 ) != '{' )) {
# for backward compability with "old" dovecot passwords that don't have the {method} prefix
$password = str_replace ( '{' . $method . '}' , '' , $password );
}
return rtrim ( $password );
2018-02-19 21:59:52 +01:00
}
2018-05-02 22:18:38 +02:00
/**
2019-02-15 22:34:49 +01:00
* Supports DES , MD5 , BLOWFISH , SHA256 , SHA512 methods .
*
2020-04-01 22:31:16 +02:00
* Via config we support an optional prefix ( e . g . if you need hashes to start with { SHA256 - CRYPT } and optional rounds ( hardness ) setting .
*
2018-05-02 22:18:38 +02:00
* @ param string $pw
* @ param string $pw_db ( can be empty if setting a new password )
2019-02-15 22:34:49 +01:00
* @ return string crypt ' ed password ; if it matches $pw_db then $pw is the original password .
2018-05-02 22:18:38 +02:00
*/
2021-04-13 22:19:16 +02:00
function _pacrypt_php_crypt ( $pw , $pw_db )
{
2020-04-01 22:31:16 +02:00
$configEncrypt = Config :: read_string ( 'encrypt' );
2018-04-29 15:39:58 +02:00
// use PHPs crypt(), which uses the system's crypt()
// same algorithms as used in /etc/shadow
// you can have mixed hash types in the database for authentication, changed passwords get specified hash type
// the algorithm for a new hash is chosen by feeding a salt with correct magic to crypt()
2018-05-02 22:18:38 +02:00
// set $CONF['encrypt'] to 'php_crypt' to use the default SHA512 crypt method
2018-04-29 15:39:58 +02:00
// set $CONF['encrypt'] to 'php_crypt:METHOD' to use another method; methods supported: DES, MD5, BLOWFISH, SHA256, SHA512
2020-04-01 22:31:16 +02:00
// set $CONF['encrypt'] to 'php_crypt:METHOD:difficulty' where difficulty is between 1000-999999999
// set $CONF['encrypt'] to 'php_crypt:METHOD:difficulty:PREFIX' to prefix the hash with the {PREFIX} etc.
2018-04-29 15:39:58 +02:00
// tested on linux
2020-04-01 22:31:16 +02:00
$prefix = '' ;
2018-04-29 15:39:58 +02:00
if ( strlen ( $pw_db ) > 0 ) {
// existing pw provided. send entire password hash as salt for crypt() to figure out
$salt = $pw_db ;
2020-04-01 22:31:16 +02:00
// if there was a prefix in the password, use this (override anything given in the config).
if ( preg_match ( '/^\{([-A-Z0-9]+)\}(.+)$/' , $pw_db , $method_matches )) {
$salt = $method_matches [ 2 ];
$prefix = " { " . $method_matches [ 1 ] . " } " ;
}
2018-04-29 15:39:58 +02:00
} else {
2018-05-02 22:18:38 +02:00
$salt_method = 'SHA512' ; // hopefully a reasonable default (better than MD5)
2018-05-29 02:37:38 +02:00
$hash_difficulty = '' ;
2018-04-29 15:39:58 +02:00
// no pw provided. create new password hash
2020-04-01 22:31:16 +02:00
if ( strpos ( $configEncrypt , ':' ) !== false ) {
2018-04-29 15:39:58 +02:00
// use specified hash method
2020-04-01 22:31:16 +02:00
$spec = explode ( ':' , $configEncrypt );
$salt_method = $spec [ 1 ];
if ( isset ( $spec [ 2 ])) {
$hash_difficulty = $spec [ 2 ];
}
if ( isset ( $spec [ 3 ])) {
$prefix = $spec [ 3 ]; // hopefully something like {SHA256-CRYPT}
2018-05-29 02:36:08 +02:00
}
2018-04-29 15:39:58 +02:00
}
// create appropriate salt for selected hash method
2018-05-29 02:36:08 +02:00
$salt = _php_crypt_generate_crypt_salt ( $salt_method , $hash_difficulty );
2018-04-29 15:39:58 +02:00
}
2020-04-01 22:31:16 +02:00
2018-04-29 15:39:58 +02:00
$password = crypt ( $pw , $salt );
2020-04-01 22:31:16 +02:00
return " { $prefix } { $password } " ;
2018-04-29 15:39:58 +02:00
}
2020-04-01 22:31:16 +02:00
2018-05-02 22:18:38 +02:00
/**
* @ param string $hash_type must be one of : MD5 , DES , BLOWFISH , SHA256 or SHA512 ( default )
2018-06-10 22:21:45 +02:00
* @ param int hash difficulty
2018-05-02 22:18:38 +02:00
* @ return string
*/
2021-04-13 22:19:16 +02:00
function _php_crypt_generate_crypt_salt ( $hash_type = 'SHA512' , $hash_difficulty = null )
{
2018-04-29 15:39:58 +02:00
// generate a salt (with magic matching chosen hash algorithm) for the PHP crypt() function
// most commonly used alphabet
$alphabet = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ;
switch ( $hash_type ) {
case 'DES' :
$alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' ;
$length = 2 ;
$salt = _php_crypt_random_string ( $alphabet , $length );
return $salt ;
case 'MD5' :
$length = 12 ;
$algorithm = '1' ;
$salt = _php_crypt_random_string ( $alphabet , $length );
return sprintf ( '$%s$%s' , $algorithm , $salt );
case 'BLOWFISH' :
$length = 22 ;
2018-05-29 02:36:08 +02:00
if ( empty ( $hash_difficulty )) {
2018-05-29 01:29:50 +02:00
$cost = 10 ;
} else {
2018-05-29 02:36:08 +02:00
$cost = ( int ) $hash_difficulty ;
2018-05-29 01:29:50 +02:00
if ( $cost < 4 || $cost > 31 ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( 'invalid encrypt difficulty setting "' . $hash_difficulty . '" for ' . $hash_type . ', the valid range is 4-31' );
2018-05-29 01:29:50 +02:00
}
2018-05-29 01:14:38 +02:00
}
2018-04-29 15:39:58 +02:00
if ( version_compare ( PHP_VERSION , '5.3.7' ) >= 0 ) {
$algorithm = '2y' ; // bcrypt, with fixed unicode problem
} else {
$algorithm = '2a' ; // bcrypt
}
$salt = _php_crypt_random_string ( $alphabet , $length );
return sprintf ( '$%s$%02d$%s' , $algorithm , $cost , $salt );
2018-05-02 22:18:24 +02:00
2018-04-29 15:39:58 +02:00
case 'SHA256' :
$length = 16 ;
$algorithm = '5' ;
2018-05-29 02:36:08 +02:00
if ( empty ( $hash_difficulty )) {
$rounds = '' ;
2018-05-29 01:29:50 +02:00
} else {
2018-05-29 02:36:08 +02:00
$rounds = ( int ) $hash_difficulty ;
2018-05-29 01:29:50 +02:00
if ( $rounds < 1000 || $rounds > 999999999 ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( 'invalid encrypt difficulty setting "' . $hash_difficulty . '" for ' . $hash_type . ', the valid range is 1000-999999999' );
2018-05-29 01:29:50 +02:00
}
2018-05-29 01:14:38 +02:00
}
2018-04-29 15:39:58 +02:00
$salt = _php_crypt_random_string ( $alphabet , $length );
2018-05-29 02:36:08 +02:00
if ( ! empty ( $rounds )) {
$rounds = sprintf ( 'rounds=%d$' , $rounds );
}
return sprintf ( '$%s$%s%s' , $algorithm , $rounds , $salt );
2018-05-02 22:18:24 +02:00
2018-04-29 15:39:58 +02:00
case 'SHA512' :
$length = 16 ;
$algorithm = '6' ;
2018-05-29 02:36:08 +02:00
if ( empty ( $hash_difficulty )) {
$rounds = '' ;
2018-05-29 01:29:50 +02:00
} else {
2018-05-29 02:36:08 +02:00
$rounds = ( int ) $hash_difficulty ;
2018-05-29 01:29:50 +02:00
if ( $rounds < 1000 || $rounds > 999999999 ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( 'invalid encrypt difficulty setting "' . $hash_difficulty . '" for ' . $hash_type . ', the valid range is 1000-999999999' );
2018-05-29 01:29:50 +02:00
}
2018-05-29 01:14:38 +02:00
}
2018-04-29 15:39:58 +02:00
$salt = _php_crypt_random_string ( $alphabet , $length );
2018-05-29 02:36:08 +02:00
if ( ! empty ( $rounds )) {
$rounds = sprintf ( 'rounds=%d$' , $rounds );
}
return sprintf ( '$%s$%s%s' , $algorithm , $rounds , $salt );
2018-05-02 22:18:24 +02:00
2018-04-29 15:39:58 +02:00
default :
2019-09-14 22:01:09 +02:00
throw new Exception ( " unknown hash type: ' $hash_type ' " );
2018-04-29 15:39:58 +02:00
}
}
2018-05-02 22:18:38 +02:00
/**
* Generates a random string of specified $length from $characters .
* @ param string $characters
* @ param int $length
* @ return string of given $length
*/
2021-04-13 22:19:16 +02:00
function _php_crypt_random_string ( $characters , $length )
{
2018-04-29 15:39:58 +02:00
$string = '' ;
for ( $p = 0 ; $p < $length ; $p ++ ) {
2018-05-04 22:36:05 +02:00
$string .= $characters [ random_int ( 0 , strlen ( $characters ) - 1 )];
2018-04-29 15:39:58 +02:00
}
return $string ;
}
2011-09-25 20:39:20 +02:00
2009-03-08 22:16:41 +01:00
/**
2017-03-21 17:43:27 +01:00
* Encrypt a password , using the apparopriate hashing mechanism as defined in
* config . inc . php ( $CONF [ 'encrypt' ]) .
2019-02-15 22:34:49 +01:00
*
2009-03-08 22:16:41 +01:00
* When wanting to compare one pw to another , it ' s necessary to provide the salt used - hence
* the second parameter ( $pw_db ), which is the existing hash from the DB .
*
* @ param string $pw
2018-02-18 21:32:34 +01:00
* @ param string $pw_db optional encrypted password
2019-02-15 22:34:49 +01:00
* @ return string encrypted password - if this matches $pw_db then the original password is $pw .
2009-03-08 22:16:41 +01:00
*/
2021-05-08 22:50:39 +02:00
function pacrypt ( $pw , $pw_db = " " )
2021-04-13 22:19:16 +02:00
{
2009-01-15 13:24:36 +01:00
global $CONF ;
2021-07-07 22:42:17 +02:00
$mechanism = $CONF [ 'encrypt' ] ? ? 'CRYPT' ;
2009-01-15 15:32:43 +01:00
2021-09-29 19:32:46 +02:00
$mechanism = strtoupper ( $mechanism );
2009-03-14 00:32:20 +01:00
2021-09-29 19:32:46 +02:00
$crypts = [ 'PHP_CRYPT' , 'MD5CRYPT' , 'PHP_CRYPT:DES' , 'PHP_CRYPT:MD5' , 'PHP_CRYPT:SHA256' ];
2018-04-29 15:39:58 +02:00
2021-09-29 19:32:46 +02:00
if ( in_array ( $mechanism , $crypts )) {
2021-07-07 22:42:17 +02:00
$mechanism = 'CRYPT' ;
}
2021-11-14 21:52:24 +01:00
if ( preg_match ( '/^PHP_CRYPT:(DES|MD5|BLOWFISH|SHA256|SHA512):?/' , $mechanism , $matches )) {
return _pacrypt_php_crypt ( $pw , $pw_db );
2021-07-07 22:42:17 +02:00
}
2007-03-24 08:27:00 +01:00
2021-09-29 19:34:54 +02:00
if ( $mechanism == 'SHA512.B64' ) {
2021-09-29 22:05:19 +02:00
// postfixadmin incorrectly uses this as a SHA512-CRYPT.B64
2021-09-29 19:32:46 +02:00
$mechanism = 'SHA512-CRYPT.B64' ;
2020-03-14 23:30:51 +01:00
}
2021-11-14 21:52:24 +01:00
2021-09-29 19:34:54 +02:00
if ( preg_match ( '/^DOVECOT:(.*)$/i' , $mechanism , $matches )) {
2021-07-11 22:53:06 +02:00
$mechanism = strtoupper ( $matches [ 1 ]);
2020-03-14 23:34:52 +01:00
}
2020-03-14 23:30:51 +01:00
2021-09-29 19:34:54 +02:00
if ( preg_match ( '/^COURIER:(.*)$/i' , $mechanism , $matches )) {
2021-07-11 22:53:06 +02:00
$mechanism = strtoupper ( $mechanism );
}
2021-07-07 22:42:17 +02:00
if ( empty ( $pw_db )) {
$pw_db = null ;
}
2020-03-14 23:30:51 +01:00
2021-09-29 19:34:54 +02:00
if ( $mechanism == 'AUTHLIB' ) {
2021-09-29 19:32:46 +02:00
return _pacrypt_authlib ( $pw , $pw_db );
2020-03-14 23:30:51 +01:00
}
2021-07-07 22:42:17 +02:00
$hasher = new \PostfixAdmin\PasswordHashing\Crypt ( $mechanism );
return $hasher -> crypt ( $pw , $pw_db );
2020-03-14 23:30:51 +01:00
}
2019-02-15 22:34:49 +01:00
/**
* Creates MD5 based crypt formatted password .
* If salt is not provided we generate one .
*
* @ param string $pw plain text password
* @ param string $salt ( optional )
* @ param string $magic ( optional )
* @ return string hashed password in crypt format .
2021-05-08 22:56:05 +02:00
* @ deprecated see PFACrypt :: cryptMd5 () ( note this returns { MD5 } prefix
2019-02-15 22:34:49 +01:00
*/
2021-04-13 22:19:16 +02:00
function md5crypt ( $pw , $salt = " " , $magic = " " )
{
2009-01-15 13:24:36 +01:00
$MAGIC = " $ 1 $ " ;
2018-01-26 15:45:57 +01:00
if ( $magic == " " ) {
$magic = $MAGIC ;
}
if ( $salt == " " ) {
$salt = create_salt ();
}
$slist = explode ( " $ " , $salt );
if ( $slist [ 0 ] == " 1 " ) {
$salt = $slist [ 1 ];
}
2009-01-15 13:24:36 +01:00
2018-01-26 15:45:57 +01:00
$salt = substr ( $salt , 0 , 8 );
2009-01-15 13:24:36 +01:00
$ctx = $pw . $magic . $salt ;
2018-01-26 15:45:57 +01:00
$final = hex2bin ( md5 ( $pw . $salt . $pw ));
2009-01-15 13:24:36 +01:00
2018-01-26 15:45:57 +01:00
for ( $i = strlen ( $pw ); $i > 0 ; $i -= 16 ) {
2011-06-02 22:51:12 +02:00
if ( $i > 16 ) {
2018-01-26 15:45:57 +01:00
$ctx .= substr ( $final , 0 , 16 );
2011-06-02 22:51:12 +02:00
} else {
2018-01-26 15:45:57 +01:00
$ctx .= substr ( $final , 0 , $i );
2009-01-15 13:24:36 +01:00
}
}
2018-01-26 15:45:57 +01:00
$i = strlen ( $pw );
2009-01-15 13:24:36 +01:00
2011-06-02 22:51:12 +02:00
while ( $i > 0 ) {
2018-01-26 15:45:57 +01:00
if ( $i & 1 ) {
$ctx .= chr ( 0 );
} else {
$ctx .= $pw [ 0 ];
}
2009-01-15 13:24:36 +01:00
$i = $i >> 1 ;
}
2018-01-26 15:45:57 +01:00
$final = hex2bin ( md5 ( $ctx ));
2009-01-15 13:24:36 +01:00
2011-06-02 22:51:12 +02:00
for ( $i = 0 ; $i < 1000 ; $i ++ ) {
2009-01-15 13:24:36 +01:00
$ctx1 = " " ;
2011-06-02 22:51:12 +02:00
if ( $i & 1 ) {
2009-01-15 13:24:36 +01:00
$ctx1 .= $pw ;
2011-06-02 22:51:12 +02:00
} else {
2018-01-26 15:45:57 +01:00
$ctx1 .= substr ( $final , 0 , 16 );
}
if ( $i % 3 ) {
$ctx1 .= $salt ;
}
if ( $i % 7 ) {
$ctx1 .= $pw ;
2009-01-15 13:24:36 +01:00
}
2011-06-02 22:51:12 +02:00
if ( $i & 1 ) {
2018-01-26 15:45:57 +01:00
$ctx1 .= substr ( $final , 0 , 16 );
2011-06-02 22:51:12 +02:00
} else {
2009-01-15 13:24:36 +01:00
$ctx1 .= $pw ;
}
2018-01-26 15:45:57 +01:00
$final = hex2bin ( md5 ( $ctx1 ));
2009-01-15 13:24:36 +01:00
}
$passwd = " " ;
2018-01-26 15:45:57 +01:00
$passwd .= to64 ((( ord ( $final [ 0 ]) << 16 ) | ( ord ( $final [ 6 ]) << 8 ) | ( ord ( $final [ 12 ]))), 4 );
$passwd .= to64 ((( ord ( $final [ 1 ]) << 16 ) | ( ord ( $final [ 7 ]) << 8 ) | ( ord ( $final [ 13 ]))), 4 );
$passwd .= to64 ((( ord ( $final [ 2 ]) << 16 ) | ( ord ( $final [ 8 ]) << 8 ) | ( ord ( $final [ 14 ]))), 4 );
$passwd .= to64 ((( ord ( $final [ 3 ]) << 16 ) | ( ord ( $final [ 9 ]) << 8 ) | ( ord ( $final [ 15 ]))), 4 );
$passwd .= to64 ((( ord ( $final [ 4 ]) << 16 ) | ( ord ( $final [ 10 ]) << 8 ) | ( ord ( $final [ 5 ]))), 4 );
$passwd .= to64 ( ord ( $final [ 11 ]), 2 );
2009-01-15 13:24:36 +01:00
return " $magic $salt\ $ $passwd " ;
2007-03-24 08:27:00 +01:00
}
2019-02-15 22:34:49 +01:00
/**
* @ return string - should be random , 8 chars long
*/
2021-04-13 22:19:16 +02:00
function create_salt ()
{
2018-12-28 20:27:33 +01:00
srand (( int ) microtime () * 1000000 );
$salt = substr ( md5 ( " " . rand ( 0 , 9999999 )), 0 , 8 );
2009-01-15 13:24:36 +01:00
return $salt ;
2007-03-24 08:27:00 +01:00
}
2012-02-04 00:43:45 +01:00
/*
* remove item $item from array $array
*/
2021-04-13 22:19:16 +02:00
function remove_from_array ( $array , $item )
{
2012-02-04 00:43:45 +01:00
# array_diff might be faster, but doesn't provide an easy way to know if the value was found or not
# return array_diff($array, array($item));
$ret = array_search ( $item , $array );
if ( $ret === false ) {
$found = 0 ;
} else {
$found = 1 ;
2018-01-26 15:45:57 +01:00
unset ( $array [ $ret ]);
2012-02-04 00:43:45 +01:00
}
return array ( $found , $array );
}
2021-04-13 22:19:16 +02:00
function to64 ( $v , $n )
{
2009-01-15 13:24:36 +01:00
$ITOA64 = " ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz " ;
$ret = " " ;
2011-06-02 22:51:12 +02:00
while (( $n - 1 ) >= 0 ) {
2009-01-15 13:24:36 +01:00
$n -- ;
$ret .= $ITOA64 [ $v & 0x3f ];
$v = $v >> 6 ;
}
return $ret ;
2007-03-24 08:27:00 +01:00
}
2021-11-29 07:10:20 +01:00
function enable_socket_crypto ( $fh )
{
stream_set_blocking ( $fh , true );
stream_socket_enable_crypto ( $fh , true , STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT );
stream_set_blocking ( $fh , true );
}
2007-03-24 08:27:00 +01:00
2010-12-25 23:04:16 +01:00
/**
* smtp_mail
* Action : Send email
* Call : smtp_mail ( string to , string from , string subject , string body ]) - or -
* Call : smtp_mail ( string to , string from , string data ) - DEPRECATED
2021-03-02 22:33:00 +01:00
* @ param string $to
* @ param string $from
* @ param string $subject ( if called with 4 parameters ) or full mail body ( if called with 3 parameters )
* @ param string $password ( optional ) - Password
* @ param string $body ( optional , but recommended ) - mail body
2010-12-25 23:04:16 +01:00
* @ return bool - true on success , otherwise false
* TODO : Replace this with something decent like PEAR :: Mail or Zend_Mail .
*/
2021-04-13 22:19:16 +02:00
function smtp_mail ( $to , $from , $data , $password = " " , $body = " " )
{
2009-01-15 13:24:36 +01:00
global $CONF ;
2021-03-02 22:33:00 +01:00
2009-01-15 13:24:36 +01:00
$smtpd_server = $CONF [ 'smtp_server' ];
$smtpd_port = $CONF [ 'smtp_port' ];
2021-11-29 07:10:20 +01:00
$smtpd_type = $CONF [ 'smtp_type' ];
2021-03-02 22:33:00 +01:00
2016-03-28 20:28:40 +02:00
$smtp_server = php_uname ( 'n' );
2018-01-26 15:45:57 +01:00
if ( ! empty ( $CONF [ 'smtp_client' ])) {
2016-03-28 20:28:40 +02:00
$smtp_server = $CONF [ 'smtp_client' ];
}
2018-12-28 20:27:33 +01:00
$errno = 0 ;
2009-01-15 13:24:36 +01:00
$errstr = " 0 " ;
2018-12-28 20:27:33 +01:00
$timeout = 30 ;
2009-01-15 13:24:36 +01:00
2010-12-25 23:04:16 +01:00
if ( $body != " " ) {
2017-03-21 17:43:27 +01:00
$maildata =
2010-12-25 23:04:16 +01:00
" To: " . $to . " \n "
. " From: " . $from . " \n "
2018-01-26 15:45:57 +01:00
. " Subject: " . encode_header ( $data ) . " \n "
2010-12-25 23:04:16 +01:00
. " MIME-Version: 1.0 \n "
2018-09-21 22:56:35 +02:00
. " Date: " . date ( 'r' ) . " \n "
2010-12-25 23:04:16 +01:00
. " Content-Type: text/plain; charset=utf-8 \n "
. " Content-Transfer-Encoding: 8bit \n "
. " \n "
. $body
;
} else {
$maildata = $data ;
}
2018-01-26 15:45:57 +01:00
$fh = @ fsockopen ( $smtpd_server , $smtpd_port , $errno , $errstr , $timeout );
2009-01-15 13:24:36 +01:00
2011-06-02 22:51:12 +02:00
if ( ! $fh ) {
2011-10-20 14:13:39 +02:00
error_log ( " fsockopen failed - errno: $errno - errstr: $errstr " );
2009-01-15 13:24:36 +01:00
return false ;
2011-06-02 22:51:12 +02:00
} else {
2021-11-29 07:10:20 +01:00
if ( $smtpd_type === " tls " ) {
enable_socket_crypto ( $fh );
}
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2019-07-02 13:38:53 +02:00
2021-11-29 07:10:20 +01:00
if ( $smtpd_type === " starttls " ) {
2019-07-12 11:24:59 +02:00
fputs ( $fh , " STARTTLS \r \n " );
smtp_get_response ( $fh );
2021-11-29 07:10:20 +01:00
enable_socket_crypto ( $fh );
2019-07-12 11:24:59 +02:00
}
2019-07-02 13:38:53 +02:00
2018-01-26 15:45:57 +01:00
fputs ( $fh , " EHLO $smtp_server\r\n " );
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2019-05-31 11:42:42 +02:00
if ( ! empty ( $password )) {
fputs ( $fh , " AUTH LOGIN \r \n " );
smtp_get_response ( $fh );
fputs ( $fh , base64_encode ( $from ) . " \r \n " );
smtp_get_response ( $fh );
fputs ( $fh , base64_encode ( $password ) . " \r \n " );
smtp_get_response ( $fh );
}
2018-01-26 15:45:57 +01:00
fputs ( $fh , " MAIL FROM:< $from > \r \n " );
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2018-01-26 15:45:57 +01:00
fputs ( $fh , " RCPT TO:< $to > \r \n " );
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2018-01-26 15:45:57 +01:00
fputs ( $fh , " DATA \r \n " );
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2018-01-26 15:45:57 +01:00
fputs ( $fh , " $maildata\r\n . \r \n " );
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2018-01-26 15:45:57 +01:00
fputs ( $fh , " QUIT \r \n " );
2018-06-10 22:21:45 +02:00
smtp_get_response ( $fh );
2018-01-26 15:45:57 +01:00
fclose ( $fh );
2009-01-15 13:24:36 +01:00
}
return true ;
2007-03-24 08:27:00 +01:00
}
2011-07-19 23:12:25 +02:00
/**
* smtp_get_admin_email
* Action : Get configured email address or current user if nothing configured
* Call : smtp_get_admin_email
2018-12-28 20:27:33 +01:00
* @ return string - username / mail address
2011-07-19 23:12:25 +02:00
*/
2021-04-13 22:19:16 +02:00
function smtp_get_admin_email ()
{
2018-12-28 20:27:33 +01:00
$admin_email = Config :: read_string ( 'admin_email' );
2018-01-26 15:45:57 +01:00
if ( ! empty ( $admin_email )) {
return $admin_email ;
} else {
return authentication_get_username ();
}
2011-07-19 23:12:25 +02:00
}
2007-03-24 08:27:00 +01:00
2019-05-31 11:42:42 +02:00
/**
* smtp_get_admin_password
* Action : Get smtp password for admin email
* Call : smtp_get_admin_password
* @ return string - admin smtp password
*/
2021-04-13 22:19:16 +02:00
function smtp_get_admin_password ()
{
2019-05-31 11:42:42 +02:00
return Config :: read_string ( 'admin_smtp_password' );
}
2007-03-24 08:27:00 +01:00
//
// smtp_get_response
// Action: Get response from mail server
// Call: smtp_get_response (string FileHandle)
//
2021-04-13 22:19:16 +02:00
function smtp_get_response ( $fh )
{
2009-01-15 13:24:36 +01:00
$res = '' ;
2011-06-02 22:51:12 +02:00
do {
2013-10-08 21:30:28 +02:00
$line = fgets ( $fh , 256 );
$res .= $line ;
2018-01-26 15:45:57 +01:00
} while ( preg_match ( " /^ \ d \ d \ d \ -/ " , $line ));
2013-10-08 21:30:28 +02:00
return $res ;
2007-03-24 08:27:00 +01:00
}
2018-12-28 20:27:33 +01:00
$DEBUG_TEXT = <<< EOF
< p > Please check the documentation and website for more information .</ p >
< ul >
< li >< a href = " http://postfixadmin.sf.net " > PostfixAdmin - Project website </ a ></ li >
< li >< a href = 'https://sourceforge.net/p/postfixadmin/discussion/676076' > Forums </ a ></ li >
</ ul >
EOF ;
2007-03-24 08:27:00 +01:00
2021-01-28 23:12:05 +01:00
/**
* @ return string - PDO DSN for PHP .
* @ throws Exception
*/
2021-04-13 22:19:16 +02:00
function db_connection_string ()
{
2021-01-28 23:12:05 +01:00
global $CONF ;
$dsn = null ;
if ( db_mysql ()) {
$socket = false ;
if ( Config :: has ( 'database_socket' )) {
$socket = Config :: read_string ( 'database_socket' );
}
$database_name = Config :: read_string ( 'database_name' );
2021-10-12 20:49:53 +02:00
$dsn = 'mysql:' ;
2021-01-28 23:12:05 +01:00
if ( $socket ) {
2021-10-12 20:49:53 +02:00
$dsn .= " unix_socket= { $socket } " ;
2021-09-01 00:04:54 +02:00
} else {
2021-11-02 23:10:03 +01:00
$dsn .= " host= { $CONF [ 'database_host' ] } " ;
2021-01-28 23:12:05 +01:00
}
2021-10-12 20:49:53 +02:00
if ( isset ( $CONF [ 'database_port' ])) {
$dsn .= " ;port= { $CONF [ 'database_port' ] } " ;
}
$dsn .= " ;dbname= { $database_name } ;charset=UTF8 " ;
2021-01-28 23:12:05 +01:00
} elseif ( db_sqlite ()) {
$db = $CONF [ 'database_name' ];
$dsn = " sqlite: { $db } " ;
} elseif ( db_pgsql ()) {
$dsn = " pgsql:dbname= { $CONF [ 'database_name' ] } " ;
if ( isset ( $CONF [ 'database_host' ])) {
$dsn .= " ;host= { $CONF [ 'database_host' ] } " ;
}
if ( isset ( $CONF [ 'database_port' ])) {
$dsn .= " ;port= { $CONF [ 'database_port' ] } " ;
}
$dsn .= " ;options='-c client_encoding=utf8' " ;
} else {
2021-01-28 23:20:54 +01:00
throw new Exception ( " <p style='color: red'>FATAL Error:<br />Invalid \$ CONF['database_type'] <br/>Only: 'pgsql', 'mysql' or 'sqlite' supported. <br/> Please fix your config.inc.php!</p> " );
2021-01-28 23:12:05 +01:00
}
return $dsn ;
}
2007-10-07 19:23:29 +02:00
/**
* db_connect
* Action : Makes a connection to the database if it doesn ' t exist
* Call : db_connect ()
*
* Return value :
2017-02-24 11:59:45 +01:00
*
2019-02-10 13:11:58 +01:00
* @ return \PDO
2007-10-07 19:23:29 +02:00
*/
2021-04-13 22:19:16 +02:00
function db_connect ()
{
2009-01-15 13:24:36 +01:00
global $CONF ;
2019-06-07 17:23:34 +02:00
/* some attempt at not reopening an existing connection */
2017-03-21 17:44:26 +01:00
static $link ;
if ( isset ( $link ) && $link ) {
2019-06-07 17:23:34 +02:00
return $link ;
2017-03-21 17:44:26 +01:00
}
2019-06-07 17:23:34 +02:00
2019-02-10 12:03:52 +01:00
$link = false ;
2017-03-21 17:44:26 +01:00
2021-01-28 23:12:05 +01:00
// throws.
$dsn = db_connection_string ();
2019-01-06 22:27:08 +01:00
$options = array (
2019-01-06 21:25:24 +01:00
PDO :: ATTR_ERRMODE => PDO :: ERRMODE_EXCEPTION ,
2021-01-28 23:12:05 +01:00
PDO :: ATTR_TIMEOUT => 5 ,
2019-01-06 22:27:08 +01:00
);
2019-01-06 21:25:24 +01:00
$username_password = true ;
2019-01-06 22:27:08 +01:00
$queries = array ();
2019-01-06 21:25:24 +01:00
if ( db_mysql ()) {
if ( Config :: bool ( 'database_use_ssl' )) {
2020-02-28 15:19:22 +01:00
$options [ PDO :: MYSQL_ATTR_SSL_KEY ] = Config :: read_string ( 'database_ssl_key' );
2019-01-06 21:25:24 +01:00
$options [ PDO :: MYSQL_ATTR_SSL_CA ] = Config :: read_string ( 'database_ssl_ca' );
$options [ PDO :: MYSQL_ATTR_SSL_CAPATH ] = Config :: read_string ( 'database_ssl_ca_path' );
$options [ PDO :: MYSQL_ATTR_SSL_CERT ] = Config :: read_string ( 'database_ssl_cert' );
$options [ PDO :: MYSQL_ATTR_SSL_CIPHER ] = Config :: read_string ( 'database_ssl_cipher' );
2020-02-28 15:19:22 +01:00
$options = array_filter ( $options ); // remove empty settings.
2020-02-28 15:30:40 +01:00
$verify = Config :: read ( 'database_ssl_verify_server_cert' );
if ( $verify === null ) { // undefined
$verify = true ;
}
$options [ PDO :: MYSQL_ATTR_SSL_VERIFY_SERVER_CERT ] = ( bool ) $verify ;
2016-02-04 23:30:06 +01:00
}
2021-05-07 18:03:37 +02:00
$queries [] = 'SET NAMES utf8mb4' ;
2021-05-07 17:55:18 +02:00
$queries [] = " SET COLLATION_CONNECTION='utf8mb4_general_ci' " ;
2019-01-06 22:03:43 +01:00
} elseif ( db_sqlite ()) {
2019-02-10 06:06:59 +01:00
$db = $CONF [ 'database_name' ];
if ( ! file_exists ( $db )) {
2021-01-28 23:12:05 +01:00
$error_text = 'SQLite database missing: ' . $db ;
2019-06-07 17:23:34 +02:00
throw new Exception ( $error_text );
2019-02-10 06:06:59 +01:00
}
if ( ! is_writeable ( $db )) {
2021-01-28 23:12:05 +01:00
$error_text = 'SQLite database not writeable: ' . $db ;
2019-06-07 17:23:34 +02:00
throw new Exception ( $error_text );
2019-02-10 06:06:59 +01:00
}
2019-02-10 06:56:08 +01:00
if ( ! is_writeable ( dirname ( $db ))) {
2021-01-28 23:12:05 +01:00
$error_text = 'The directory the SQLite database is in is not writeable: ' . dirname ( $db );
2019-06-07 17:23:34 +02:00
throw new Exception ( $error_text );
2019-02-10 06:56:08 +01:00
}
2019-01-06 21:25:24 +01:00
$username_password = false ;
2013-11-16 01:00:53 +01:00
} elseif ( db_pgsql ()) {
2021-01-28 23:12:05 +01:00
// nothing to do.
2011-06-02 22:51:12 +02:00
} else {
2019-09-14 22:01:09 +02:00
throw new Exception ( " <p style='color: red'>FATAL Error:<br />Invalid \$ CONF['database_type']! Please fix your config.inc.php!</p> " );
2009-01-15 13:24:36 +01:00
}
2019-06-07 15:33:58 +02:00
if ( $username_password ) {
$link = new PDO ( $dsn , Config :: read_string ( 'database_user' ), Config :: read_string ( 'database_password' ), $options );
} else {
$link = new PDO ( $dsn , null , null , $options );
}
2019-01-06 21:25:24 +01:00
2019-06-07 15:33:58 +02:00
if ( ! empty ( $queries )) {
foreach ( $queries as $q ) {
$link -> exec ( $q );
2019-01-06 22:27:08 +01:00
}
}
2019-06-07 17:23:34 +02:00
return $link ;
2007-03-24 08:27:00 +01:00
}
2007-09-23 15:33:50 +02:00
/**
* Returns the appropriate boolean value for the database .
2018-12-31 23:19:13 +01:00
*
2019-09-17 22:05:08 +02:00
* @ param bool | string $bool
2018-12-28 20:27:33 +01:00
* @ return string | int as appropriate for underlying db platform
2007-09-23 15:33:50 +02:00
*/
2021-04-13 22:19:16 +02:00
function db_get_boolean ( $bool )
{
2018-01-26 15:45:57 +01:00
if ( ! ( is_bool ( $bool ) || $bool == '0' || $bool == '1' )) {
2014-05-12 01:09:18 +02:00
error_log ( " Invalid usage of 'db_get_boolean( $bool )' " );
2019-09-14 22:01:09 +02:00
throw new Exception ( " Invalid usage of 'db_get_boolean( $bool )' " );
2009-01-15 13:24:36 +01:00
}
2018-01-26 15:45:57 +01:00
if ( db_pgsql ()) {
2009-01-15 13:24:36 +01:00
// return either true or false (unquoted strings)
2018-01-26 15:45:57 +01:00
if ( $bool ) {
2009-08-04 22:41:24 +02:00
return 't' ;
2017-03-21 17:43:27 +01:00
}
2009-08-04 22:41:24 +02:00
return 'f' ;
2019-01-01 20:28:59 +01:00
} elseif ( db_mysql () || db_sqlite ()) {
2018-01-26 15:45:57 +01:00
if ( $bool ) {
2017-03-21 17:43:27 +01:00
return 1 ;
}
2009-01-15 13:24:36 +01:00
return 0 ;
2011-10-17 00:19:55 +02:00
} else {
2019-09-14 22:01:09 +02:00
throw new Exception ( 'Unknown value in $CONF[database_type]' );
2011-10-17 00:19:55 +02:00
}
}
2014-11-01 20:19:00 +01:00
/**
* Returns a query that reports the used quota ( " x / y " )
* @ param string column containing used quota
* @ param string column containing allowed quota
* @ param string column that will contain " x / y "
* @ return string
*/
2021-04-13 22:19:16 +02:00
function db_quota_text ( $count , $quota , $fieldname )
{
2016-04-25 13:23:35 +02:00
if ( db_pgsql () || db_sqlite ()) {
// SQLite and PostgreSQL use || to concatenate strings
2016-02-04 23:30:06 +01:00
return " CASE $quota
WHEN '-1' THEN ( coalesce ( $count , 0 ) || ' / -' )
WHEN '0' THEN ( coalesce ( $count , 0 ) || ' / " . escape_string(html_entity_decode(' & infin ; ')) . "' )
ELSE ( coalesce ( $count , 0 ) || ' / ' || $quota )
END AS $fieldname " ;
2018-01-26 15:45:57 +01:00
} else {
2016-02-04 23:30:06 +01:00
return " CASE $quota
WHEN '-1' THEN CONCAT ( coalesce ( $count , 0 ), ' / -' )
WHEN '0' THEN CONCAT ( coalesce ( $count , 0 ), ' / ' , '" . escape_string(html_entity_decode(' & infin ; ')) . "' )
ELSE CONCAT ( coalesce ( $count , 0 ), ' / ' , $quota )
END AS $fieldname " ;
}
2014-11-01 20:19:00 +01:00
}
/**
* Returns a query that reports the used quota ( " x / y " )
* @ param string column containing used quota
* @ param string column containing allowed quota
* @ param string column that will contain " x / y "
* @ return string
*/
2021-04-13 22:19:16 +02:00
function db_quota_percent ( $count , $quota , $fieldname )
{
2015-02-28 23:30:21 +01:00
return " CASE $quota
2014-11-01 20:19:00 +01:00
WHEN '-1' THEN - 1
2015-02-28 23:30:21 +01:00
WHEN '0' THEN - 1
2014-11-01 20:19:00 +01:00
ELSE round ( 100 * coalesce ( $count , 0 ) / $quota )
END AS $fieldname " ;
}
2018-01-03 17:05:46 +01:00
/**
* @ return boolean true if it ' s a MySQL database variant .
*/
2021-04-13 22:19:16 +02:00
function db_mysql ()
{
2018-01-03 17:05:46 +01:00
$type = Config :: Read ( 'database_type' );
2018-01-26 15:45:57 +01:00
if ( $type == 'mysql' || $type == 'mysqli' ) {
2018-01-03 17:05:46 +01:00
return true ;
}
return false ;
}
2013-11-16 01:00:53 +01:00
/**
2019-01-01 20:24:04 +01:00
* @ return bool true if PostgreSQL is used , false otherwise
2013-11-16 01:00:53 +01:00
*/
2021-04-13 22:19:16 +02:00
function db_pgsql ()
{
2019-01-01 20:28:59 +01:00
return Config :: read_string ( 'database_type' ) == 'pgsql' ;
2013-11-16 01:00:53 +01:00
}
2016-02-04 23:30:06 +01:00
/**
* returns true if SQLite is used , false otherwise
*/
2021-04-13 22:19:16 +02:00
function db_sqlite ()
{
2018-01-26 15:45:57 +01:00
if ( Config :: Read ( 'database_type' ) == 'sqlite' ) {
2016-02-04 23:30:06 +01:00
return true ;
} else {
return false ;
}
}
2018-02-10 22:00:58 +01:00
/**
2019-01-06 21:25:24 +01:00
* @ param string $sql
* @ param array $values
* @ return array
2018-02-10 22:00:58 +01:00
*/
2021-04-13 22:19:16 +02:00
function db_query_all ( $sql , array $values = [])
{
2019-01-06 22:27:08 +01:00
$r = db_query ( $sql , $values );
2019-01-06 21:25:24 +01:00
return $r [ 'result' ] -> fetchAll ( PDO :: FETCH_ASSOC );
}
/**
* @ param string $sql
* @ param array $values
* @ return array
*/
2021-04-13 22:19:16 +02:00
function db_query_one ( $sql , array $values = [])
{
2019-01-06 22:27:08 +01:00
$r = db_query ( $sql , $values );
2019-01-06 21:25:24 +01:00
return $r [ 'result' ] -> fetch ( PDO :: FETCH_ASSOC );
}
2019-02-10 12:03:52 +01:00
/**
* @ param string $sql e . g . UPDATE foo SET bar = : baz
* @ param array $values - parameters for the prepared statement e . g . [ 'baz' => 1234 ]
2019-02-27 15:33:01 +01:00
* @ param bool $throw_exceptions
2019-02-10 12:03:52 +01:00
* @ return int number of rows affected by the query
*/
2021-04-13 22:19:16 +02:00
function db_execute ( $sql , array $values = [], $throw_exceptions = false )
{
2018-01-26 15:45:57 +01:00
$link = db_connect ();
2009-01-15 13:24:36 +01:00
2019-01-06 21:25:24 +01:00
try {
$stmt = $link -> prepare ( $sql );
$stmt -> execute ( $values );
2019-01-06 22:03:43 +01:00
} catch ( PDOException $e ) {
2021-02-07 20:40:45 +01:00
$error_text = " Invalid query: " . $e -> getMessage () . " caused by " . $sql . ' ' . json_encode ( $values );
2019-01-06 21:25:24 +01:00
error_log ( $error_text );
2019-02-27 15:33:01 +01:00
if ( $throw_exceptions ) {
2019-01-06 22:27:08 +01:00
throw $e ;
}
2019-02-27 15:33:01 +01:00
return 0 ;
2018-01-26 15:45:57 +01:00
}
2019-02-27 15:33:01 +01:00
2019-01-06 21:25:24 +01:00
return $stmt -> rowCount ();
}
/**
* @ param string $sql
* @ param array $values
* @ param bool $ignore_errors - set to true to ignore errors .
* @ return array e . g . [ 'result' => PDOStatement , 'error' => string ]
*/
2021-04-13 22:19:16 +02:00
function db_query ( $sql , array $values = array (), $ignore_errors = false )
{
2019-01-06 21:25:24 +01:00
$link = db_connect ();
$error_text = '' ;
2020-01-31 17:30:46 +01:00
$stmt = null ;
2019-01-06 21:25:24 +01:00
try {
$stmt = $link -> prepare ( $sql );
$stmt -> execute ( $values );
2019-01-06 22:03:43 +01:00
} catch ( PDOException $e ) {
2019-01-06 21:25:24 +01:00
$error_text = " Invalid query: " . $e -> getMessage () . " caused by " . $sql ;
2013-11-18 23:57:37 +01:00
error_log ( $error_text );
2019-09-15 22:09:02 +02:00
if ( defined ( 'PHPUNIT_TEST' )) {
2019-09-15 22:08:08 +02:00
throw new Exception ( " SQL query failed: { { { $sql } }} with " . json_encode ( $values ) . " . Error message: " . $e -> getMessage ());
}
2019-01-06 22:03:43 +01:00
if ( ! $ignore_errors ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " DEBUG INFORMATION: " . $e -> getMessage () . " <br/> Check your error_log for the failed query " );
2009-01-15 13:24:36 +01:00
}
}
2019-01-06 21:25:24 +01:00
return array (
" result " => $stmt ,
" error " => $error_text ,
2009-01-15 13:24:36 +01:00
);
2007-03-24 08:27:00 +01:00
}
2019-01-06 22:27:08 +01:00
2007-03-24 08:27:00 +01:00
2018-02-10 22:00:58 +01:00
/**
* Delete a row from the specified table .
*
* DELETE FROM $table WHERE $where = $delete $aditionalWhere
*
* @ param string $table
* @ param string $where - should never be a user supplied value
* @ param string $delete
* @ param string $additionalwhere ( default '' ) .
* @ return int | mixed rows deleted .
*/
2021-04-13 22:19:16 +02:00
function db_delete ( $table , $where , $delete , $additionalwhere = '' )
{
2010-12-25 23:04:16 +01:00
$table = table_by_key ( $table );
2018-02-10 22:04:29 +01:00
2019-01-06 21:25:24 +01:00
$query = " DELETE FROM $table WHERE $where = ? $additionalwhere " ;
2019-01-06 22:27:08 +01:00
return db_execute ( $query , array ( $delete ));
2007-03-24 08:27:00 +01:00
}
2019-01-06 21:25:24 +01:00
2007-11-03 20:03:43 +01:00
/**
* db_insert
* Action : Inserts a row from a specified table
2010-12-25 23:04:16 +01:00
* Call : db_insert ( string table , array values [, array timestamp ])
2018-02-10 22:00:58 +01:00
*
* @ param string - table name
2018-09-07 12:09:34 +02:00
* @ param array $values - key / value map of data to insert into the table .
* @ param array $timestamp ( optional ) - array of fields to set to now () - default : array ( 'created' , 'modified' )
2019-10-17 20:37:56 +02:00
* @ param boolean $throw_exceptions
2007-11-04 01:50:09 +01:00
* @ return int - number of inserted rows
2007-11-03 20:03:43 +01:00
*/
2021-04-13 22:19:16 +02:00
function db_insert ( string $table , array $values , array $timestamp = array ( 'created' , 'modified' ), bool $throw_exceptions = false ) : int
{
2018-01-26 15:45:57 +01:00
$table = table_by_key ( $table );
2007-11-04 01:54:09 +01:00
2018-01-26 15:45:57 +01:00
foreach ( $timestamp as $key ) {
2016-02-04 23:30:06 +01:00
if ( db_sqlite ()) {
$values [ $key ] = " datetime('now') " ;
} else {
$values [ $key ] = " now() " ;
}
2009-01-15 13:24:36 +01:00
}
2008-07-29 21:32:32 +02:00
2019-01-06 21:25:24 +01:00
$value_string = '' ;
$comma = '' ;
$prepared_statment_values = $values ;
2019-01-06 22:03:43 +01:00
foreach ( $values as $field => $value ) {
if ( in_array ( $field , $timestamp )) {
2019-01-06 21:25:24 +01:00
$value_string .= $comma . $value ; // see above.
unset ( $prepared_statment_values [ $field ]);
2019-01-06 22:03:43 +01:00
} else {
2019-01-06 21:25:24 +01:00
$value_string .= $comma . " : { $field } " ;
}
$comma = ',' ;
}
2019-01-06 22:27:08 +01:00
return db_execute (
2019-01-06 21:25:24 +01:00
" INSERT INTO $table ( " . implode ( " , " , array_keys ( $values )) . " ) VALUES ( $value_string ) " ,
2019-02-27 15:33:01 +01:00
$prepared_statment_values ,
$throw_exceptions );
2007-11-04 01:50:09 +01:00
}
/**
* db_update
* Action : Updates a specified table
2010-12-31 21:13:53 +01:00
* Call : db_update ( string table , string where_col , string where_value , array values [, array timestamp ])
2018-02-10 22:00:58 +01:00
* @ param string $table - table name
* @ param string $where_col - column of WHERE condition
* @ param string $where_value - value of WHERE condition
* @ param array $values - key / value map of data to insert into the table .
* @ param array $timestamp ( optional ) - array of fields to set to now () - default : array ( 'modified' )
2007-11-04 01:50:09 +01:00
* @ return int - number of updated rows
*/
2021-04-13 22:19:16 +02:00
function db_update ( string $table , string $where_col , string $where_value , array $values , array $timestamp = array ( 'modified' ), bool $throw_exceptions = false ) : int
{
2018-12-28 20:27:33 +01:00
$table_key = table_by_key ( $table );
2007-11-04 01:50:09 +01:00
2019-01-06 22:27:08 +01:00
$pvalues = array ();
2019-01-06 21:25:24 +01:00
$set = array ();
2021-04-05 22:07:17 +02:00
2021-04-05 22:20:40 +02:00
foreach ( $timestamp as $k ) {
if ( ! isset ( $values [ $k ])) {
2021-04-05 22:07:17 +02:00
$values [ $k ] = 'x' ; // timestamp field not in the values list, add it in so we set it to now() see #469
}
}
2019-01-06 21:25:24 +01:00
foreach ( $values as $key => $value ) {
2019-01-06 22:03:43 +01:00
if ( in_array ( $key , $timestamp )) {
if ( db_sqlite ()) {
2019-01-06 21:25:24 +01:00
$set [] = " $key = datetime('now') " ;
2019-01-06 22:03:43 +01:00
} else {
2019-01-06 21:25:24 +01:00
$set [] = " $key = now() " ;
}
2021-04-05 22:07:17 +02:00
continue ;
2019-01-06 21:25:24 +01:00
}
2021-04-05 22:07:17 +02:00
$set [] = " $key = : $key " ;
$pvalues [ $key ] = $value ;
2019-01-06 22:03:43 +01:00
}
2007-11-04 01:50:09 +01:00
2019-01-06 21:25:24 +01:00
$pvalues [ 'where' ] = $where_value ;
$sql = " UPDATE $table_key SET " . implode ( " , " , $set ) . " WHERE $where_col = :where " ;
2019-02-27 15:33:01 +01:00
return db_execute ( $sql , $pvalues , $throw_exceptions );
2007-11-02 00:58:12 +01:00
}
2007-03-24 08:27:00 +01:00
2007-11-03 20:03:43 +01:00
/**
* db_log
* Action : Logs actions from admin
2011-02-15 23:20:27 +01:00
* Call : db_log ( string domain , string action , string data )
2014-03-16 22:52:26 +01:00
* Possible actions are defined in $LANG [ " pViewlog_action_ $action " ]
2007-09-28 22:28:18 +02:00
*/
2021-04-13 22:19:16 +02:00
function db_log ( $domain , $action , $data )
{
2018-02-10 22:07:10 +01:00
if ( ! Config :: bool ( 'logging' )) {
return true ;
}
2010-09-27 01:14:42 +02:00
$REMOTE_ADDR = getRemoteAddr ();
2009-01-15 13:24:36 +01:00
2011-02-15 22:59:03 +01:00
$username = authentication_get_username ();
2014-03-16 22:52:26 +01:00
if ( Config :: Lang ( " pViewlog_action_ $action " ) == '' ) {
2019-09-14 22:01:09 +02:00
throw new Exception ( " Invalid log action : $action " ); // could do with something better?
2009-01-15 13:24:36 +01:00
}
2018-02-10 22:07:10 +01:00
$logdata = array (
'username' => " $username ( $REMOTE_ADDR ) " ,
'domain' => $domain ,
'action' => $action ,
'data' => $data ,
);
$result = db_insert ( 'log' , $logdata , array ( 'timestamp' ));
if ( $result != 1 ) {
return false ;
} else {
return true ;
2009-01-15 13:24:36 +01:00
}
2007-03-24 08:27:00 +01:00
}
2009-11-02 01:24:40 +01:00
/**
* db_in_clause
* Action : builds and returns the " field in(x, y) " clause for database queries
* Call : db_in_clause ( string field , array values )
2018-02-10 22:00:58 +01:00
* @ param string $field
* @ param array $values
2018-12-28 20:27:33 +01:00
* @ return string
2009-11-02 01:24:40 +01:00
*/
2021-04-13 22:19:16 +02:00
function db_in_clause ( $field , array $values )
{
2018-12-28 20:27:33 +01:00
$v = array_map ( 'escape_string' , array_values ( $values ));
return " $field IN (' " . implode ( " ',' " , $v ) . " ') " ;
2009-11-02 01:24:40 +01:00
}
2007-03-24 08:27:00 +01:00
2011-10-22 13:12:03 +02:00
/**
* db_where_clause
* Action : builds and returns a WHERE clause for database queries . All given conditions will be AND ' ed .
* Call : db_where_clause ( array $conditions , array $struct )
2018-02-10 22:00:58 +01:00
* @ param array $condition - array ( 'field' => 'value' , 'field2' => ' value2 , ... )
* @ param array $struct - field structure , used for automatic bool conversion
* @ param string $additional_raw_where - raw sniplet to include in the WHERE part - typically needs to start with AND
* @ param array $searchmode - operators to use ( = , < , > etc . ) - defaults to = if not specified for a field ( see
2014-11-02 23:45:22 +01:00
* $allowed_operators for available operators )
2016-11-01 16:45:45 +01:00
* Note : the $searchmode operator will only be used if a $condition for that field is set .
* This also means you ' ll need to set a ( dummy ) condition for NULL and NOTNULL .
2011-10-22 13:12:03 +02:00
*/
2021-04-13 22:19:16 +02:00
function db_where_clause ( array $condition , array $struct , $additional_raw_where = '' , array $searchmode = array ())
{
2019-10-19 21:51:05 +02:00
if ( count ( $condition ) == 0 && trim ( $additional_raw_where ) == '' ) {
2019-09-15 12:42:21 +02:00
throw new Exception ( " db_where_cond: parameter is an empty array! " );
2011-10-22 13:12:03 +02:00
}
2018-02-21 20:47:57 +01:00
$allowed_operators = array ( '<' , '>' , '>=' , '<=' , '=' , '!=' , '<>' , 'CONT' , 'LIKE' , 'NULL' , 'NOTNULL' );
2014-11-02 23:45:22 +01:00
$where_parts = array ();
$having_parts = array ();
2018-01-26 15:45:57 +01:00
foreach ( $condition as $field => $value ) {
if ( isset ( $struct [ $field ]) && $struct [ $field ][ 'type' ] == 'bool' ) {
$value = db_get_boolean ( $value );
}
2014-11-02 23:45:22 +01:00
$operator = '=' ;
if ( isset ( $searchmode [ $field ])) {
if ( in_array ( $searchmode [ $field ], $allowed_operators )) {
$operator = $searchmode [ $field ];
if ( $operator == 'CONT' ) { # CONT - as in "contains"
$operator = ' LIKE ' ; # add spaces
$value = '%' . $value . '%' ;
} elseif ( $operator == 'LIKE' ) { # LIKE -without adding % wildcards (the search value can contain %)
$operator = ' LIKE ' ; # add spaces
}
} else {
2019-09-14 22:01:09 +02:00
throw new Exception ( 'db_where_clause: Invalid searchmode for ' . $field );
2014-11-02 23:45:22 +01:00
}
}
2016-11-01 16:45:45 +01:00
if ( $operator == " NULL " ) {
$querypart = $field . ' IS NULL' ;
} elseif ( $operator == " NOTNULL " ) {
$querypart = $field . ' IS NOT NULL' ;
} else {
$querypart = $field . $operator . " ' " . escape_string ( $value ) . " ' " ;
2018-09-07 12:04:19 +02:00
// might need other types adding here.
2018-12-31 23:19:13 +01:00
if ( db_pgsql () && isset ( $struct [ $field ]) && in_array ( $struct [ $field ][ 'type' ], array ( 'ts' , 'num' )) && $value === '' ) {
2018-12-28 20:31:43 +01:00
$querypart = $field . $operator . " NULL " ;
2018-09-07 12:04:19 +02:00
}
2016-11-01 16:45:45 +01:00
}
2018-01-26 15:45:57 +01:00
if ( ! empty ( $struct [ $field ][ 'select' ])) {
2014-11-02 23:45:22 +01:00
$having_parts [ $field ] = $querypart ;
} else {
$where_parts [ $field ] = $querypart ;
}
2011-10-22 13:12:03 +02:00
}
2014-11-02 23:45:22 +01:00
$query = ' WHERE 1=1 ' ;
$query .= " $additional_raw_where " ;
2018-01-26 15:45:57 +01:00
if ( count ( $where_parts ) > 0 ) {
$query .= " AND ( " . join ( " AND " , $where_parts ) . " ) " ;
}
if ( count ( $having_parts ) > 0 ) {
$query .= " HAVING ( " . join ( " AND " , $having_parts ) . " ) " ;
}
2014-11-02 23:45:22 +01:00
2018-01-26 15:45:57 +01:00
return $query ;
2011-10-22 13:12:03 +02:00
}
2018-01-03 17:05:46 +01:00
/**
2018-01-26 15:45:57 +01:00
* Convert a programmatic db table name into what may be the actual name .
2018-01-03 17:05:46 +01:00
*
* Takes into consideration any CONF database_prefix or database_tables map
*
* If it ' s a MySQL database , then we return the name with backticks around it ( ` ) .
*
* @ param string database table name .
2018-02-10 22:00:58 +01:00
* @ return string - database table name with appropriate prefix ( and quoting if MySQL )
2018-01-03 17:05:46 +01:00
*/
2021-04-13 22:19:16 +02:00
function table_by_key ( $table_key )
{
2009-01-15 13:24:36 +01:00
global $CONF ;
2018-01-03 17:05:46 +01:00
$table = $table_key ;
if ( ! empty ( $CONF [ 'database_tables' ][ $table_key ])) {
2010-09-27 01:14:42 +02:00
$table = $CONF [ 'database_tables' ][ $table_key ];
}
2018-01-03 17:05:46 +01:00
$table = $CONF [ 'database_prefix' ] . $table ;
2018-01-26 15:45:57 +01:00
if ( db_mysql ()) {
2018-01-03 17:05:46 +01:00
return " ` " . $table . " ` " ;
}
return $table ;
2007-03-24 08:27:00 +01:00
}
2018-01-03 17:05:46 +01:00
2018-12-28 20:27:33 +01:00
/**
2016-05-22 21:58:54 +02:00
* check if the database layout is up to date
* returns the current 'version' value from the config table
2019-09-14 22:01:09 +02:00
* if $error_out is True ( default ), exit ( 1 ) with a message that recommends to run setup . php .
2018-12-28 20:27:33 +01:00
* @ param bool $error_out
* @ return int
2016-05-22 21:58:54 +02:00
*/
2021-04-13 22:19:16 +02:00
function check_db_version ( $error_out = true )
{
2016-05-22 21:58:54 +02:00
global $min_db_version ;
$table = table_by_key ( 'config' );
$sql = " SELECT value FROM $table WHERE name = 'version' " ;
2019-01-06 22:27:08 +01:00
$row = db_query_one ( $sql );
2019-01-06 21:25:24 +01:00
if ( isset ( $row [ 'value' ])) {
$dbversion = ( int ) $row [ 'value' ];
2019-01-06 22:03:43 +01:00
} else {
2019-01-06 22:27:08 +01:00
db_execute ( " INSERT INTO $table (name, value) VALUES ('version', '0') " );
$dbversion = 0 ;
2016-05-22 21:58:54 +02:00
}
2007-03-24 08:27:00 +01:00
2018-01-26 15:45:57 +01:00
if (( $dbversion < $min_db_version ) && $error_out == true ) {
2016-05-22 21:58:54 +02:00
echo " ERROR: The PostfixAdmin database layout is outdated (you have r $dbversion , but r $min_db_version is expected). \n Please run setup.php to upgrade the database. \n " ;
exit ( 1 );
}
return $dbversion ;
}
2007-03-24 08:27:00 +01:00
2018-12-28 20:27:33 +01:00
/**
*
* Action : Return a string of colored & nbsp ; ' 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
*/
2021-04-13 22:19:16 +02:00
function gen_show_status ( $show_alias )
{
2013-12-08 20:41:01 +01:00
global $CONF ;
$table_alias = table_by_key ( 'alias' );
2009-01-15 13:24:36 +01:00
$stat_string = " " ;
$stat_goto = " " ;
2019-01-06 22:27:08 +01:00
$stat_result = db_query_one ( " SELECT goto FROM $table_alias WHERE address=? " , array ( $show_alias ));
2019-01-06 21:25:24 +01:00
2019-01-06 22:03:43 +01:00
if ( $stat_result ) {
$stat_goto = $stat_result [ 'goto' ];
2009-01-15 13:24:36 +01:00
}
2018-12-28 20:27:33 +01:00
$delimiter_regex = null ;
2011-06-03 00:53:40 +02:00
if ( ! empty ( $CONF [ 'recipient_delimiter' ])) {
$delimiter = preg_quote ( $CONF [ 'recipient_delimiter' ], " / " );
$delimiter_regex = '/' . $delimiter . '[^' . $delimiter . '@]*@/' ;
}
2009-01-15 13:24:36 +01:00
// UNDELIVERABLE CHECK
2018-01-26 15:45:57 +01:00
if ( $CONF [ 'show_undeliverable' ] == 'YES' ) {
2009-01-15 13:24:36 +01:00
$gotos = array ();
2018-01-26 15:45:57 +01:00
$gotos = explode ( ',' , $stat_goto );
2009-01-15 13:24:36 +01:00
$undel_string = " " ;
//make sure this alias goes somewhere known
$stat_ok = 1 ;
2018-02-18 20:59:37 +01:00
foreach ( $gotos as $g ) {
if ( ! $stat_ok ) {
2018-02-09 22:19:45 +01:00
break ;
}
2018-02-18 20:59:37 +01:00
if ( strpos ( $g , '@' ) === false ) {
2018-02-09 22:19:45 +01:00
continue ;
}
list ( $local_part , $stat_domain ) = explode ( '@' , $g );
2019-01-06 21:25:24 +01:00
$v = array ();
2009-05-03 19:23:48 +02:00
$stat_delimiter = " " ;
2019-01-06 21:25:24 +01:00
$sql = " SELECT address FROM $table_alias WHERE address = ? OR address = ? " ;
$v [] = $g ;
$v [] = '@' . $stat_domain ;
2018-12-28 20:27:33 +01:00
if ( ! empty ( $CONF [ 'recipient_delimiter' ]) && isset ( $delimiter_regex )) {
2019-01-06 21:25:24 +01:00
$v [] = preg_replace ( $delimiter_regex , " @ " , $g );
$sql .= " OR address = ? " ;
2018-01-26 15:45:57 +01:00
}
2019-01-06 21:25:24 +01:00
2019-01-06 22:27:08 +01:00
$stat_result = db_query_one ( $sql , $v );
2019-01-06 21:25:24 +01:00
2019-01-06 22:03:43 +01:00
if ( empty ( $stat_result )) {
2009-01-15 13:24:36 +01:00
$stat_ok = 0 ;
2007-09-21 14:33:14 +02:00
}
2019-01-06 22:03:43 +01:00
2018-01-26 15:45:57 +01:00
if ( $stat_ok == 0 ) {
if ( $stat_domain == $CONF [ 'vacation_domain' ] || in_array ( $stat_domain , $CONF [ 'show_undeliverable_exceptions' ])) {
2009-01-15 13:24:36 +01:00
$stat_ok = 1 ;
}
2007-09-21 14:33:14 +02:00
}
2009-01-15 13:24:36 +01:00
} // while
2018-01-26 15:45:57 +01:00
if ( $stat_ok == 0 ) {
2018-02-09 22:19:45 +01:00
$stat_string .= " <span style='background-color: " . $CONF [ 'show_undeliverable_color' ] . " '> " . $CONF [ 'show_status_text' ] . " </span> " ;
2011-06-02 22:51:12 +02:00
} else {
2007-09-21 14:33:14 +02:00
$stat_string .= $CONF [ 'show_status_text' ] . " " ;
2017-03-21 17:43:27 +01:00
}
2011-06-03 01:13:34 +02:00
}
2009-01-15 13:24:36 +01:00
2018-08-21 15:57:06 +02:00
// Vacation CHECK
2021-01-23 17:55:41 +01:00
if ( array_key_exists ( 'show_vacation' , $CONF ) && $CONF [ 'show_vacation' ] == 'YES' ) {
2019-06-30 15:28:25 +02:00
$stat_result = db_query_one ( " SELECT * FROM " . table_by_key ( 'vacation' ) . " WHERE email = ? AND active = ? " , array ( $show_alias , db_get_boolean ( true ) )) ;
2019-01-06 22:03:43 +01:00
if ( ! empty ( $stat_result )) {
2018-08-28 22:19:56 +02:00
$stat_string .= " <span style='background-color: " . $CONF [ 'show_vacation_color' ] . " '> " . $CONF [ 'show_status_text' ] . " </span> " ;
2018-08-21 15:57:06 +02:00
} else {
$stat_string .= $CONF [ 'show_status_text' ] . " " ;
}
}
// Disabled CHECK
2021-01-23 17:55:41 +01:00
if ( array_key_exists ( 'show_disabled' , $CONF ) && $CONF [ 'show_disabled' ] == 'YES' ) {
2019-01-06 22:27:08 +01:00
$stat_result = db_query_one (
2019-06-30 15:28:25 +02:00
" SELECT * FROM " . table_by_key ( 'mailbox' ) . " WHERE username = ? AND active = ? " ,
2019-01-06 21:25:24 +01:00
array ( $show_alias , db_get_boolean ( false ))
);
2019-01-06 22:03:43 +01:00
if ( ! empty ( $stat_result )) {
2018-08-28 22:19:56 +02:00
$stat_string .= " <span style='background-color: " . $CONF [ 'show_disabled_color' ] . " '> " . $CONF [ 'show_status_text' ] . " </span> " ;
2018-08-21 15:57:06 +02:00
} else {
$stat_string .= $CONF [ 'show_status_text' ] . " " ;
}
}
// Expired CHECK
2021-01-22 19:04:39 +01:00
if ( Config :: has ( 'password_expiration' ) && Config :: bool ( 'password_expiration' ) && Config :: bool ( 'show_expired' )) {
2018-12-28 20:59:35 +01:00
$now = 'now()' ;
if ( db_sqlite ()) {
$now = " datetime('now') " ;
}
2018-12-28 20:54:25 +01:00
2021-01-22 19:04:39 +01:00
$stat_result = db_query_one ( " SELECT * FROM " . table_by_key ( 'mailbox' ) . " WHERE username = ? AND password_expiry <= $now AND active = ? " , array ( $show_alias , db_get_boolean ( true )));
2018-12-28 20:57:21 +01:00
2019-01-06 22:03:43 +01:00
if ( ! empty ( $stat_result )) {
2018-08-28 22:19:56 +02:00
$stat_string .= " <span style='background-color: " . $CONF [ 'show_expired_color' ] . " '> " . $CONF [ 'show_status_text' ] . " </span> " ;
2018-08-21 15:57:06 +02:00
} else {
$stat_string .= $CONF [ 'show_status_text' ] . " " ;
}
}
2009-01-15 13:24:36 +01:00
// POP/IMAP CHECK
2018-01-26 15:45:57 +01:00
if ( $CONF [ 'show_popimap' ] == 'YES' ) {
$stat_delimiter = " " ;
2018-12-28 20:27:33 +01:00
if ( ! empty ( $CONF [ 'recipient_delimiter' ]) && isset ( $delimiter_regex )) {
2018-01-26 15:45:57 +01:00
$stat_delimiter = ',' . preg_replace ( $delimiter_regex , " @ " , $stat_goto );
}
2009-05-14 21:16:00 +02:00
2009-01-15 13:24:36 +01:00
//if the address passed in appears in its own goto field, its POP/IMAP
2011-06-03 00:53:40 +02:00
# TODO: or not (might also be an alias loop) -> check mailbox table!
2018-01-26 15:45:57 +01:00
if ( preg_match ( '/,' . $show_alias . ',/' , ',' . $stat_goto . $stat_delimiter . ',' )) {
2009-01-15 13:24:36 +01:00
$stat_string .= " <span style='background-color: " . $CONF [ 'show_popimap_color' ] .
" '> " . $CONF [ 'show_status_text' ] . " </span> " ;
2011-06-02 22:51:12 +02:00
} else {
2009-01-15 13:24:36 +01:00
$stat_string .= $CONF [ 'show_status_text' ] . " " ;
2017-03-21 17:43:27 +01:00
}
2009-01-15 13:24:36 +01:00
}
// CUSTOM DESTINATION CHECK
2018-01-26 15:45:57 +01:00
if ( count ( $CONF [ 'show_custom_domains' ]) > 0 ) {
for ( $i = 0 ; $i < sizeof ( $CONF [ 'show_custom_domains' ]); $i ++ ) {
if ( preg_match ( '/^.*' . $CONF [ 'show_custom_domains' ][ $i ] . '.*$/' , $stat_goto )) {
2009-01-15 13:24:36 +01:00
$stat_string .= " <span style='background-color: " . $CONF [ 'show_custom_colors' ][ $i ] .
" '> " . $CONF [ 'show_status_text' ] . " </span> " ;
2011-06-02 22:51:12 +02:00
} else {
2009-01-15 13:24:36 +01:00
$stat_string .= $CONF [ 'show_status_text' ] . " " ;
2017-03-21 17:43:27 +01:00
}
}
2011-06-02 22:51:12 +02:00
} else {
2021-08-04 18:25:45 +02:00
$stat_string .= " " ;
2017-03-21 17:43:27 +01:00
}
2007-08-31 02:31:35 +02:00
2009-01-15 13:24:36 +01:00
// $stat_string .= "<span style='background-color:green'> </span> " .
// "<span style='background-color:blue'> </span> ";
return $stat_string ;
2007-08-31 02:31:35 +02:00
}
2018-02-10 22:00:58 +01:00
/**
* @ return string
*/
2021-04-13 22:19:16 +02:00
function getRemoteAddr ()
{
2010-09-27 01:14:42 +02:00
$REMOTE_ADDR = 'localhost' ;
2018-01-26 15:45:57 +01:00
if ( isset ( $_SERVER [ 'REMOTE_ADDR' ])) {
2010-09-27 01:14:42 +02:00
$REMOTE_ADDR = $_SERVER [ 'REMOTE_ADDR' ];
2018-01-26 15:45:57 +01:00
}
2018-02-10 22:00:58 +01:00
2010-09-27 01:14:42 +02:00
return $REMOTE_ADDR ;
}
2007-10-07 19:23:29 +02:00
2021-02-07 21:35:58 +01:00
/**
* @ param array $server
* @ return string URL to Postfixadmin - will always end in a '/'
*/
2021-04-13 22:19:16 +02:00
function getSiteUrl ( array $server = []) : string
{
2021-11-10 16:41:08 +01:00
$config = Config :: getInstance () -> getAll ();
if ( isset ( $config [ 'site_url' ]) && is_string ( $config [ 'site_url' ])) {
$url = $config [ 'site_url' ];
2021-02-07 21:35:58 +01:00
if ( ! empty ( $url )) {
return $url ;
}
}
if ( empty ( $server )) {
$server = $_SERVER ;
}
// ideally need to support installation unnder a random prefix
// - https://example.com/my-postfixadmin-3.1.2/index.php
// - https://example.com/my-postfixadmin-3.1.2/users/password-recover.php
// in either case, we want https://example.com/my-postfixadmin-3.1.2/
$uri = dirname ( $server [ 'REQUEST_URI' ]);
if ( preg_match ( '!/users/.*.php!' , $uri )) {
$uri = dirname ( $uri );
}
// ensure it ends with a /
if ( substr ( $uri , - 1 , 1 ) !== '/' ) {
$uri = $uri . '/' ;
}
$https = isset ( $server [ 'HTTPS' ]) && $server [ 'HTTPS' ] == 'on' ? 'https' : 'http' ;
if ( isset ( $server [ 'REQUEST_SCHEME' ])) {
$https = $server [ 'REQUEST_SCHEME' ];
}
return $https . '://' . $server [ 'HTTP_HOST' ] . $uri ;
}
2009-02-09 20:11:43 +01:00
/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */