Merge remote-tracking branch 'svnexport/master'

pull/12/head
David Goodwin 8 years ago
commit 491df198cc

@ -9,6 +9,15 @@
# Last update: # Last update:
# $Id$ # $Id$
Changes since the 3.0.1 release
-------------------------------------------------
- AliasHandler: restrict mailbox subquery to allowed and specified domains
to improve performance on setups with lots of mailboxes
- allow switching between dovecot:* password schemes while still accepting
passwords hashed using the previous dovecot:* scheme
- FetchmailHandler: use a valid date as default for 'date'
Version 3.0.1 - 2016/09/19 - SVN r1870 Version 3.0.1 - 2016/09/19 - SVN r1870
------------------------------------------------- -------------------------------------------------

@ -22,7 +22,7 @@ If you need to setup Postfix to be able to handle Virtual Domains and Virtual
Users check out: Users check out:
- the PostfixAdmin documentation in the DOCUMENTS/ directory - the PostfixAdmin documentation in the DOCUMENTS/ directory
- our wiki at http://sourceforge.net/apps/mediawiki/postfixadmin/ - our wiki at https://sourceforge.net/p/postfixadmin/wiki/
There are also lots of HOWTOs around the web. Be warned that many of them There are also lots of HOWTOs around the web. Be warned that many of them
(even those listed below) may be outdated or incomplete. (even those listed below) may be outdated or incomplete.

@ -262,7 +262,7 @@ function maildir_name_hook($domain, $user) {
PostfixAdmin, but it does not create it in the database. You have to do PostfixAdmin, but it does not create it in the database. You have to do
that yourself. that yourself.
Please follow the naming policy for custom database fields and tables on Please follow the naming policy for custom database fields and tables on
http://sourceforge.net/apps/mediawiki/postfixadmin/index.php?title=Custom_fields https://sourceforge.net/p/postfixadmin/wiki/Custom_fields/
to avoid clashes with future versions of PostfixAdmin. to avoid clashes with future versions of PostfixAdmin.
See initStruct() in the *Handler class for the default $struct. See initStruct() in the *Handler class for the default $struct.

@ -933,7 +933,9 @@ function pacrypt ($pw, $pw_db="") {
elseif (preg_match("/^dovecot:/", $CONF['encrypt'])) { elseif (preg_match("/^dovecot:/", $CONF['encrypt'])) {
$split_method = preg_split ('/:/', $CONF['encrypt']); $split_method = preg_split ('/:/', $CONF['encrypt']);
$method = strtoupper($split_method[1]); # TODO: if $pw_db starts with {method}, change $method accordingly $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)) { die("invalid dovecot encryption method"); } # TODO: check against a fixed list? if (! preg_match("/^[A-Z0-9.-]+$/", $method)) { die("invalid dovecot encryption method"); } # TODO: check against a fixed list?
# if (strtolower($method) == 'md5-crypt') die("\$CONF['encrypt'] = 'dovecot:md5-crypt' will not work because dovecotpw generates a random salt each time. Please use \$CONF['encrypt'] = 'md5crypt' instead."); # if (strtolower($method) == 'md5-crypt') die("\$CONF['encrypt'] = 'dovecot:md5-crypt' will not work because dovecotpw generates a random salt each time. Please use \$CONF['encrypt'] = 'md5crypt' instead.");
# $crypt_method = preg_match ("/.*-CRYPT$/", $method); # $crypt_method = preg_match ("/.*-CRYPT$/", $method);
@ -1686,11 +1688,13 @@ function db_in_clause($field, $values) {
* db_where_clause * db_where_clause
* Action: builds and returns a WHERE clause for database queries. All given conditions will be AND'ed. * 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) * Call: db_where_clause (array $conditions, array $struct)
* param array $conditios: array('field' => 'value', 'field2' => 'value2, ...) * param array $condition: array('field' => 'value', 'field2' => 'value2, ...)
* param array $struct - field structure, used for automatic bool conversion * 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 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 * param array $searchmode - operators to use (=, <, > etc.) - defaults to = if not specified for a field (see
* $allowed_operators for available operators) * $allowed_operators for available operators)
* 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.
*/ */
function db_where_clause($condition, $struct, $additional_raw_where = '', $searchmode = array()) { function db_where_clause($condition, $struct, $additional_raw_where = '', $searchmode = array()) {
if (!is_array($condition)) { if (!is_array($condition)) {
@ -1703,7 +1707,7 @@ function db_where_clause($condition, $struct, $additional_raw_where = '', $searc
die('db_where_cond: parameter $struct is not an array!'); die('db_where_cond: parameter $struct is not an array!');
} }
$allowed_operators = explode(' ', '< > >= <= = != <> CONT LIKE'); $allowed_operators = explode(' ', '< > >= <= = != <> CONT LIKE NULL NOTNULL');
$where_parts = array(); $where_parts = array();
$having_parts = array(); $having_parts = array();
@ -1724,7 +1728,15 @@ function db_where_clause($condition, $struct, $additional_raw_where = '', $searc
die('db_where_clause: Invalid searchmode for ' . $field); die('db_where_clause: Invalid searchmode for ' . $field);
} }
} }
if ($operator == "NULL") {
$querypart = $field . ' IS NULL';
} elseif ($operator == "NOTNULL") {
$querypart = $field . ' IS NOT NULL';
} else {
$querypart = $field . $operator . "'" . escape_string($value) . "'"; $querypart = $field . $operator . "'" . escape_string($value) . "'";
}
if($struct[$field]['select'] != '') { if($struct[$field]['select'] != '') {
$having_parts[$field] = $querypart; $having_parts[$field] = $querypart;
} else { } else {

@ -136,10 +136,9 @@ $table_alias = table_by_key('alias');
$table_mailbox = table_by_key('mailbox'); $table_mailbox = table_by_key('mailbox');
if (count($search) == 0 || !isset($search['_'])) { if (count($search) == 0 || !isset($search['_'])) {
$list_param = "domain='$fDomain'"; $search_alias = array('domain' => $fDomain);
} else { } else {
$searchterm = escape_string($search['_']); $search_alias = array('_' => $search['_']);
$list_param = "(address LIKE '%$searchterm%' OR goto LIKE '%$searchterm%')";
} }
$handler = new AliasHandler(0, $admin_username); $handler = new AliasHandler(0, $admin_username);
@ -153,8 +152,8 @@ $alias_data['struct']['goto_mailbox']['display_in_list'] = 0; # not useful/defin
$alias_data['struct']['on_vacation']['display_in_list'] = 0; $alias_data['struct']['on_vacation']['display_in_list'] = 0;
$alias_data['msg']['show_simple_search'] = False; # hide search box $alias_data['msg']['show_simple_search'] = False; # hide search box
$handler->getList($list_param, array(), $page_size, $fDisplay); $handler->getList($search_alias, array(), $page_size, $fDisplay);
$pagebrowser_alias = $handler->getPagebrowser($list_param, array()); $pagebrowser_alias = $handler->getPagebrowser($search_alias, array());
$tAlias = $handler->result(); $tAlias = $handler->result();

@ -42,13 +42,9 @@ class AliasHandler extends PFAHandler {
/*options*/ '', /*options*/ '',
/*not_in_db*/ 0, /*not_in_db*/ 0,
/*dont_write_to_db*/ 1, /*dont_write_to_db*/ 1,
/*select*/ 'coalesce(__is_mailbox,0) as is_mailbox, __mailbox_username', /*select*/ 'coalesce(__is_mailbox,0) as is_mailbox' ),
# __mailbox_username is unused, but needed as workaround for a MariaDB bug /*extrafrom set via set_is_mailbox_extrafrom() */
/*extrafrom*/ 'LEFT JOIN ( ' . '__mailbox_username' => pacol( 0, 0, 1, 'vtxt', '' , '' , 0), # filled via is_mailbox
' SELECT 1 as __is_mailbox, username as __mailbox_username ' .
' FROM ' . table_by_key('mailbox') .
' WHERE username IS NOT NULL ' .
' ) AS __mailbox ON __mailbox_username = address' ),
'goto_mailbox' => pacol( $mbgoto, $mbgoto,$mbgoto,'bool', 'pEdit_alias_forward_and_store' , '' , 0, 'goto_mailbox' => pacol( $mbgoto, $mbgoto,$mbgoto,'bool', 'pEdit_alias_forward_and_store' , '' , 0,
/*options*/ '', /*options*/ '',
/*not_in_db*/ 1 ), # read_from_db_postprocess() sets the value /*not_in_db*/ 1 ), # read_from_db_postprocess() sets the value
@ -64,8 +60,37 @@ class AliasHandler extends PFAHandler {
array('select' => '1 as _can_delete') ), # read_from_db_postprocess() updates the value array('select' => '1 as _can_delete') ), # read_from_db_postprocess() updates the value
# aliases listed in $CONF[default_aliases] are read-only for domain admins if $CONF[special_alias_control] is NO. # aliases listed in $CONF[default_aliases] are read-only for domain admins if $CONF[special_alias_control] is NO.
); );
$this->set_is_mailbox_extrafrom();
}
/*
* set $this->struct['is_mailbox']['extrafrom'] based on the search conditions.
* If a listing for a specific domain is requested, optimize the subquery to only return mailboxes from that domain.
* This doesn't change the result of the main query, but improves the performance a lot on setups with lots of mailboxes.
* When using this function to optimize the is_mailbox extrafrom, don't forget to reset it to the default value
* (all domains for this admin) afterwards.
*/
private function set_is_mailbox_extrafrom($condition=array(), $searchmode=array()) {
$extrafrom = 'LEFT JOIN ( ' .
' SELECT 1 as __is_mailbox, username as __mailbox_username ' .
' FROM ' . table_by_key('mailbox') .
' WHERE username IS NOT NULL ';
if(isset($condition['domain']) && !isset($searchmode['domain']) && in_array($condition['domain'], $this->allowed_domains)) {
# listing for a specific domain, so restrict subquery to that domain
$extrafrom .= ' AND ' . db_in_clause($this->domain_field, array($condition['domain']));
} else {
# restrict subquery to all domains accessible to this admin
$extrafrom .= ' AND ' . db_in_clause($this->domain_field, $this->allowed_domains);
} }
$extrafrom .= ' ) AS __mailbox ON __mailbox_username = address';
$this->struct['is_mailbox']['extrafrom'] = $extrafrom;
}
protected function initMsg() { protected function initMsg() {
$this->msg['error_already_exists'] = 'email_address_already_exists'; $this->msg['error_already_exists'] = 'email_address_already_exists';
$this->msg['error_does_not_exist'] = 'alias_does_not_exist'; $this->msg['error_does_not_exist'] = 'alias_does_not_exist';
@ -303,22 +328,34 @@ class AliasHandler extends PFAHandler {
return $db_result; return $db_result;
} }
public function getList($condition, $searchmode = array(), $limit=-1, $offset=-1) { private function condition_ignore_mailboxes($condition, $searchmode) {
# only list aliases that do not belong to mailboxes # only list aliases that do not belong to mailboxes
# TODO: breaks if $condition is an array if (is_array($condition)) {
$condition['__mailbox_username'] = 1;
$searchmode['__mailbox_username'] = 'NULL';
} else {
if ($condition != '') { if ($condition != '') {
$condition = " AND ( $condition ) "; $condition = " ( $condition ) AND ";
}
$condition = " $condition __mailbox_username IS NULL ";
} }
return parent::getList( "__mailbox_username IS NULL $condition", $searchmode, $limit, $offset); return array($condition, $searchmode);
} }
public function getPagebrowser($condition, $searchmode = array()) { public function getList($condition, $searchmode = array(), $limit=-1, $offset=-1) {
# only list aliases that do not belong to mailboxes list($condition, $searchmode) = $this->condition_ignore_mailboxes($condition, $searchmode);
# TODO: breaks if $condition is an array $this->set_is_mailbox_extrafrom($condition, $searchmode);
if ($condition != '') { $result = parent::getList($condition, $searchmode, $limit, $offset);
$condition = " AND ( $condition ) "; $this->set_is_mailbox_extrafrom(); # reset to default
return $result;
} }
return parent::getPagebrowser( "__mailbox_username IS NULL $condition", $searchmode);
public function getPagebrowser($condition, $searchmode = array()) {
list($condition, $searchmode) = $this->condition_ignore_mailboxes($condition, $searchmode);
$this->set_is_mailbox_extrafrom($condition, $searchmode);
$result = parent::getPagebrowser($condition, $searchmode);
$this->set_is_mailbox_extrafrom(); # reset to default
return $result;
} }

@ -565,7 +565,7 @@ abstract class PFAHandler {
$no = escape_string(Config::lang('NO')); $no = escape_string(Config::lang('NO'));
if (db_pgsql()) { if (db_pgsql()) {
$formatted_date = "TO_DATE(text(###KEY###), '" . escape_string(Config::Lang('dateformat_pgsql')) . "')"; $formatted_date = "TO_CHAR(###KEY###, '" . escape_string(Config::Lang('dateformat_pgsql')) . "')";
# $base64_decode = "DECODE(###KEY###, 'base64')"; # $base64_decode = "DECODE(###KEY###, 'base64')";
} elseif (db_sqlite()) { } elseif (db_sqlite()) {
$formatted_date = "strftime(###KEY###, '" . escape_string(Config::Lang('dateformat_mysql')) . "')"; $formatted_date = "strftime(###KEY###, '" . escape_string(Config::Lang('dateformat_mysql')) . "')";

@ -21,7 +21,6 @@ class VacationHandler extends PFAHandler {
'body' => pacol( 1, 1, 0, 'text', 'pUsersVacation_body' , '' , '' ), 'body' => pacol( 1, 1, 0, 'text', 'pUsersVacation_body' , '' , '' ),
'activefrom' => pacol( 1, 1, 1, 'text', 'pUsersVacation_activefrom' , '' , '' ), 'activefrom' => pacol( 1, 1, 1, 'text', 'pUsersVacation_activefrom' , '' , '' ),
'activeuntil' => pacol( 1, 1, 1, 'text', 'pUsersVacation_activeuntil' , '' , '' ), 'activeuntil' => pacol( 1, 1, 1, 'text', 'pUsersVacation_activeuntil' , '' , '' ),
'cache' => pacol( 0, 0, 0, 'text', '' , '' , '' ), # leftover from 2.2
'active' => pacol( 1, 1, 1, 'bool', 'active' , '' , 1 ), 'active' => pacol( 1, 1, 1, 'bool', 'active' , '' , 1 ),
'created' => pacol( 0, 0, 1, 'ts', 'created' , '' ), 'created' => pacol( 0, 0, 1, 'ts', 'created' , '' ),
'modified' => pacol( 0, 0, 1, 'ts', 'last_modified' , '' ), 'modified' => pacol( 0, 0, 1, 'ts', 'last_modified' , '' ),
@ -180,7 +179,6 @@ class VacationHandler extends PFAHandler {
'active' => db_get_boolean(true), 'active' => db_get_boolean(true),
'activefrom' => $activeFrom, 'activefrom' => $activeFrom,
'activeuntil' => $activeUntil, 'activeuntil' => $activeUntil,
'cache' => '',
); );
// is there an entry in the vacaton table for the user, or do we need to insert? // is there an entry in the vacaton table for the user, or do we need to insert?

@ -64,6 +64,7 @@
{if $tCanAddMailbox} {if $tCanAddMailbox}
<br /><a href="{#url_create_mailbox#}&amp;domain={$fDomain|escape:"url"}" class="button">{$PALANG.add_mailbox}</a><br /> <br /><a href="{#url_create_mailbox#}&amp;domain={$fDomain|escape:"url"}" class="button">{$PALANG.add_mailbox}</a><br />
{/if} {/if}
<br /><br /><a href="list.php?table=mailbox&amp;output=csv">{$PALANG.download_csv}</a>
{/if} {/if}
{if $CONF.show_status===YES && $CONF.show_status_key===YES} {if $CONF.show_status===YES && $CONF.show_status_key===YES}
<br/><br/> <br/><br/>

@ -1619,14 +1619,14 @@ function upgrade_1824_sqlite() {
function upgrade_1835_mysql() { function upgrade_1835_mysql() {
# change default values for existing datetime fields with a 0000-00-00 default to {DATETIME} # change default values for existing datetime fields with a 0000-00-00 default to {DATETIME}
foreach (array('admin', 'alias', 'alias_domain', 'domain', 'mailbox', 'domain_admins', 'vacation') as $table_to_change) { foreach (array('domain_admins', 'vacation') as $table_to_change) {
$table = table_by_key($table_to_change); $table = table_by_key($table_to_change);
db_query_parsed("ALTER TABLE `$table` CHANGE `created` `created` {DATETIME}"); db_query_parsed("ALTER TABLE `$table` CHANGE `created` `created` {DATETIME}");
} }
foreach (array('admin', 'alias', 'alias_domain', 'domain', 'mailbox') as $table_to_change) { foreach (array('admin', 'alias', 'alias_domain', 'domain', 'mailbox') as $table_to_change) {
$table = table_by_key($table_to_change); $table = table_by_key($table_to_change);
db_query_parsed("ALTER TABLE `$table` CHANGE `modified` `modified` {DATETIME}"); db_query_parsed("ALTER TABLE `$table` CHANGE `created` `created` {DATETIME}, CHANGE `modified` `modified` {DATETIME}");
} }
$table = table_by_key('log'); $table = table_by_key('log');

Loading…
Cancel
Save