0
0
mirror of https://github.com/postfixadmin/postfixadmin.git synced 2024-09-19 19:22:14 +02:00
postfixadmin/model/DomainHandler.php

382 lines
17 KiB
PHP
Raw Permalink Normal View History

<?php
2022-06-28 14:46:11 +02:00
# $Id$
/**
* Handlers User level alias actions - e.g. add alias, get aliases, update etc.
*/
2021-03-22 10:28:28 +01:00
class DomainHandler extends PFAHandler
{
protected $db_table = 'domain';
protected $id_field = 'domain';
protected $domain_field = 'domain';
2021-04-13 22:19:16 +02:00
protected function validate_new_id()
{
$domain_check = check_domain($this->id);
if ($domain_check != '') {
$this->errormsg[$this->id_field] = $domain_check;
return false;
}
if (Config::read('vacation_domain') == $this->id) {
$this->errormsg[$this->id_field] = Config::Lang('domain_conflict_vacation_domain');
return false;
}
# still here? good.
return true;
}
2021-04-13 22:19:16 +02:00
protected function initStruct()
{
# TODO: shorter PALANG labels ;-)
$super = $this->is_superadmin;
$transp = min($super, Config::intbool('transport'));
$editquota = min($super, Config::intbool('quota'));
$quota = Config::intbool('quota');
$edit_dom_q = min($super, Config::intbool('domain_quota'), $quota);
$dom_q = min(Config::intbool('domain_quota'), $quota);
$pwexp = min($super, Config::intbool('password_expiration'));
$query_used_domainquota = 'round(coalesce(__total_quota/' . intval(Config::read('quota_multiplier')) . ',0))';
DomainHander: on the way to a common interface for all classes and easy-to-edit field lists. With lots of inspiration from fetchmail.php and a base class I started 2.5 years ago. model/DomainHandler.php - base on PFAHandler (see below) - new protected variables - $id_field (column that contains $username) - $struct (field list) - $defaults (default values, option lists) - $new (switch between new and edit mode) - change $username to protected (preparation for move to PFAHandler) - add optional $new parameter to __construct. Targets (not yet implemented): - early validation of $username (domain name in this case) - if $new == 1, check that item does NOT exist and is a valid domain - else: check if item exists. error out if not. - new function initStruct to fill $id_field, $struct, $defaults - add(): - use an array as parameter instead of single parameters Advantage: this makes it easy to add another field - use _inp_* base validation - create default aliases only in create mode, not in edit mode - view(): fix error message - added various TODO notes. Some affect design questions - feedback welcome ;-) scripts/shells/domain.php: - change $handler->add call to array usage - add some TODO notes - some whitespace fixes in execute() model/PFAHandler.php: - new base class for *Handler classes - contains only some generic input validation for now - more code will be moved from DomainHandler to PFAHandler later git-svn-id: https://svn.code.sf.net/p/postfixadmin/code/trunk@978 a1433add-5e2c-0410-b055-b7f2511e0802
2011-03-02 23:37:22 +01:00
# NOTE: There are dependencies between alias_count, mailbox_count and total_quota.
# NOTE: If you disable "display in list" for one of them, the SQL query for the others might break.
# NOTE: (Disabling all of them shouldn't be a problem.)
#
// https://github.com/postfixadmin/postfixadmin/issues/299
$domain_quota_default = Config::read('domain_quota_default');
if ($domain_quota_default === null) {
$domain_quota_default = -1;
}
$this->struct = array(
# field name allow display in... type $PALANG label $PALANG description default / options / ...
DomainHander: on the way to a common interface for all classes and easy-to-edit field lists. With lots of inspiration from fetchmail.php and a base class I started 2.5 years ago. model/DomainHandler.php - base on PFAHandler (see below) - new protected variables - $id_field (column that contains $username) - $struct (field list) - $defaults (default values, option lists) - $new (switch between new and edit mode) - change $username to protected (preparation for move to PFAHandler) - add optional $new parameter to __construct. Targets (not yet implemented): - early validation of $username (domain name in this case) - if $new == 1, check that item does NOT exist and is a valid domain - else: check if item exists. error out if not. - new function initStruct to fill $id_field, $struct, $defaults - add(): - use an array as parameter instead of single parameters Advantage: this makes it easy to add another field - use _inp_* base validation - create default aliases only in create mode, not in edit mode - view(): fix error message - added various TODO notes. Some affect design questions - feedback welcome ;-) scripts/shells/domain.php: - change $handler->add call to array usage - add some TODO notes - some whitespace fixes in execute() model/PFAHandler.php: - new base class for *Handler classes - contains only some generic input validation for now - more code will be moved from DomainHandler to PFAHandler later git-svn-id: https://svn.code.sf.net/p/postfixadmin/code/trunk@978 a1433add-5e2c-0410-b055-b7f2511e0802
2011-03-02 23:37:22 +01:00
# editing? form list
2018-12-27 22:43:11 +01:00
'domain' => pacol($this->new, 1, 1, 'text', 'domain' , '' , '', array(),
array('linkto' => 'list-virtual.php?domain=%s')),
'description' => pacol($super, $super, $super, 'text', 'description' , ''),
# Aliases
'aliases' => pacol($super, $super, 0, 'num' , 'aliases' , 'pAdminEdit_domain_aliases_text' , Config::read('aliases')),
2018-12-27 22:43:11 +01:00
'alias_count' => pacol(0, 0, 1, 'vnum', '' , '' , '', array(),
/*not_in_db*/ 0,
/*dont_write_to_db*/ 1,
/*select*/ 'coalesce(__alias_count,0) - coalesce(__mailbox_count,0) as alias_count',
/*extrafrom*/ 'left join ( select count(*) as __alias_count, domain as __alias_domain from ' . table_by_key('alias') .
' group by domain) as __alias on domain = __alias_domain'),
2018-12-27 22:43:11 +01:00
'aliases_quot' => pacol(0, 0, 1, 'quot', 'aliases' , '' , 0, array(),
array('select' => db_quota_text('__alias_count - coalesce(__mailbox_count,0)', 'aliases', 'aliases_quot'))),
'_aliases_quot_percent' => pacol(0, 0, 1, 'vnum', '' ,'' , 0, array(),
array('select' => db_quota_percent('__alias_count - coalesce(__mailbox_count,0)', 'aliases', '_aliases_quot_percent'))),
# Mailboxes
'mailboxes' => pacol($super, $super, 0, 'num' , 'mailboxes' , 'pAdminEdit_domain_aliases_text' , Config::read('mailboxes')),
2018-12-27 22:43:11 +01:00
'mailbox_count' => pacol(0, 0, 1, 'vnum', '' , '' , '', array(),
/*not_in_db*/ 0,
/*dont_write_to_db*/ 1,
/*select*/ 'coalesce(__mailbox_count,0) as mailbox_count',
/*extrafrom*/ 'left join ( select count(*) as __mailbox_count, sum(quota) as __total_quota, domain as __mailbox_domain from ' . table_by_key('mailbox') .
' group by domain) as __mailbox on domain = __mailbox_domain'),
2018-12-27 22:43:11 +01:00
'mailboxes_quot' => pacol(0, 0, 1, 'quot', 'mailboxes' , '' , 0, array(),
array('select' => db_quota_text('__mailbox_count', 'mailboxes', 'mailboxes_quot'))),
'_mailboxes_quot_percent' => pacol(0, 0, 1, 'vnum', '' , '' , 0, array(),
array('select' => db_quota_percent('__mailbox_count', 'mailboxes', '_mailboxes_quot_percent'))),
'maxquota' => pacol($editquota,$editquota,$quota, 'num', 'pOverview_get_quota' , 'pAdminEdit_domain_maxquota_text' , Config::read('maxquota')),
# Domain quota
'quota' => pacol($edit_dom_q,$edit_dom_q, 0, 'num', 'pAdminEdit_domain_quota' , 'pAdminEdit_domain_maxquota_text' , $domain_quota_default),
2018-12-27 22:43:11 +01:00
'total_quota' => pacol(0, 0, 1, 'vnum', '' , '' , '', array(),
array('select' => "$query_used_domainquota AS total_quota") /*extrafrom*//* already in mailbox_count */),
'total_quot' => pacol(0, 0, $dom_q, 'quot', 'pAdminEdit_domain_quota' , '' , 0, array(),
array('select' => db_quota_text($query_used_domainquota, 'quota', 'total_quot'))),
'_total_quot_percent' => pacol(0, 0, $dom_q, 'vnum', '' , '' , 0, array(),
array('select' => db_quota_percent($query_used_domainquota, 'quota', '_total_quot_percent'))),
'transport' => pacol($transp, $transp,$transp,'enum', 'transport' , 'pAdminEdit_domain_transport_text' , Config::read('transport_default') ,
/*options*/ Config::read_array('transport_options')),
'backupmx' => pacol($super, $super, 1, 'bool', 'pAdminEdit_domain_backupmx' , '' , 0),
'active' => pacol($super, $super, 1, 'bool', 'active' , '' , 1),
'default_aliases' => pacol($this->new, $this->new, 0, 'bool', 'pAdminCreate_domain_defaultaliases', '' , 1,array(), /*not in db*/ 1),
'created' => pacol(0, 0, 0, 'ts', 'created' , ''),
'modified' => pacol(0, 0, $super, 'ts', 'last_modified' , ''),
'password_expiry' => pacol($super, $pwexp, $pwexp, 'num', 'password_expiration' , 'password_expiration_desc' , 365),
'_can_edit' => pacol(0, 0, 1, 'int', '' , '' , 0 ,
2018-12-27 22:43:11 +01:00
/*options*/ array(),
/*not_in_db*/ 0,
/*dont_write_to_db*/ 1,
/*select*/ $this->is_superadmin . ' as _can_edit'),
'_can_delete' => pacol(0, 0, 1, 'int', '' , '' , 0 ,
2018-12-27 22:43:11 +01:00
/*options*/ array(),
/*not_in_db*/ 0,
/*dont_write_to_db*/ 1,
/*select*/ $this->is_superadmin . ' as _can_delete'),
DomainHander: on the way to a common interface for all classes and easy-to-edit field lists. With lots of inspiration from fetchmail.php and a base class I started 2.5 years ago. model/DomainHandler.php - base on PFAHandler (see below) - new protected variables - $id_field (column that contains $username) - $struct (field list) - $defaults (default values, option lists) - $new (switch between new and edit mode) - change $username to protected (preparation for move to PFAHandler) - add optional $new parameter to __construct. Targets (not yet implemented): - early validation of $username (domain name in this case) - if $new == 1, check that item does NOT exist and is a valid domain - else: check if item exists. error out if not. - new function initStruct to fill $id_field, $struct, $defaults - add(): - use an array as parameter instead of single parameters Advantage: this makes it easy to add another field - use _inp_* base validation - create default aliases only in create mode, not in edit mode - view(): fix error message - added various TODO notes. Some affect design questions - feedback welcome ;-) scripts/shells/domain.php: - change $handler->add call to array usage - add some TODO notes - some whitespace fixes in execute() model/PFAHandler.php: - new base class for *Handler classes - contains only some generic input validation for now - more code will be moved from DomainHandler to PFAHandler later git-svn-id: https://svn.code.sf.net/p/postfixadmin/code/trunk@978 a1433add-5e2c-0410-b055-b7f2511e0802
2011-03-02 23:37:22 +01:00
);
}
2021-04-13 22:19:16 +02:00
protected function initMsg()
{
$this->msg['error_already_exists'] = 'pAdminCreate_domain_domain_text_error';
$this->msg['error_does_not_exist'] = 'domain_does_not_exist';
$this->msg['confirm_delete'] = 'confirm_delete_domain';
if ($this->new) {
$this->msg['logname'] = 'create_domain';
$this->msg['store_error'] = 'pAdminCreate_domain_result_error';
$this->msg['successmessage'] = 'pAdminCreate_domain_result_success';
} else {
$this->msg['logname'] = 'edit_domain';
$this->msg['store_error'] = 'pAdminEdit_domain_result_error';
$this->msg['successmessage'] = 'domain_updated';
}
$this->msg['can_create'] = $this->is_superadmin;
}
2021-04-13 22:19:16 +02:00
public function webformConfig()
{
return array(
# $PALANG labels
'formtitle_create' => 'pAdminCreate_domain_welcome',
'formtitle_edit' => 'pAdminEdit_domain_welcome',
'create_button' => 'pAdminCreate_domain_button',
# various settings
'required_role' => 'admin',
'listview' => 'list.php?table=domain',
'early_init' => 0,
);
}
2022-06-28 14:46:11 +02:00
protected function preSave(): bool
2021-04-13 22:19:16 +02:00
{
# TODO: is this function superfluous? _can_edit should already cover this
if ($this->is_superadmin) {
return true;
}
2018-12-27 22:43:11 +01:00
$this->errormsg[] = Config::Lang_f('edit_not_allowed', $this->id);
return false;
}
/**
* called by $this->store() after storing $this->values in the database
* can be used to update additional tables, call scripts etc.
*/
2022-06-28 14:46:11 +02:00
protected function postSave(): bool
2021-04-13 22:19:16 +02:00
{
if ($this->new && $this->values['default_aliases']) {
foreach (Config::read_array('default_aliases') as $address => $goto) {
$address = $address . "@" . $this->id;
# if $goto doesn't contain @, let the alias point to the same domain
2018-01-26 15:45:57 +01:00
if (!strstr($goto, '@')) {
$goto = $goto . "@" . $this->id;
}
# TODO: use AliasHandler->add instead of writing directly to the alias table
$arr = array(
'address' => $address,
'goto' => $goto,
'domain' => $this->id,
);
2018-01-26 15:45:57 +01:00
$result = db_insert('alias', $arr);
# TODO: error checking
}
}
if ($this->new) {
if (!$this->domain_postcreation()) {
$this->errormsg[] = Config::lang('domain_postcreate_failed');
}
} else {
if (!$this->domain_postedit()) {
$this->errormsg[] = Config::lang('domain_postedit_failed');
}
}
return true; # TODO: don't hardcode
}
/**
2020-06-21 17:44:43 +02:00
* @return bool
*/
2021-04-13 22:19:16 +02:00
public function delete()
{
# TODO: check for _can_delete instead
if (! $this->is_superadmin) {
$this->errormsg[] = Config::Lang_f('no_delete_permissions', $this->id);
return false;
}
2018-01-26 15:45:57 +01:00
if (! $this->view()) {
$this->errormsg[] = Config::Lang('domain_does_not_exist'); # TODO: can users hit this message at all? init() should already fail...
return false;
}
if (Config::bool('alias_domain')) {
2018-01-26 15:45:57 +01:00
# check if this domain is an alias domain target - if yes, do not allow to delete it
$handler = new AliasdomainHandler(0, $this->admin_username);
$handler->getList("target_domain = '" . escape_string($this->id) . "'");
$aliasdomains = $handler->result();
if (count($aliasdomains) > 0) {
$this->errormsg[] = Config::Lang_f('delete_domain_aliasdomain_target', $this->id);
return false;
}
}
2018-01-26 15:45:57 +01:00
# the correct way would be to recursively delete mailboxes, aliases, alias_domains, fetchmail entries
# with *Handler before deleting the domain, but this would be terribly slow on domains with many aliases etc.,
# so we do it the fast way on the database level
# cleaning up all tables doesn't hurt, even if vacation or displaying the quota is disabled
# some tables don't have a domain field, so we need a workaround
$like_domain = "LIKE '" . escape_string('%@' . $this->id) . "'";
db_delete('domain_admins', 'domain', $this->id);
db_delete('alias', 'domain', $this->id);
db_delete('mailbox', 'domain', $this->id);
db_delete('alias_domain', 'alias_domain', $this->id);
db_delete('vacation', 'domain', $this->id);
db_delete('vacation_notification', 'on_vacation', $this->id, "OR on_vacation $like_domain");
db_delete('quota', 'username', $this->id, "OR username $like_domain");
db_delete('quota2', 'username', $this->id, "OR username $like_domain");
db_delete('fetchmail', 'mailbox', $this->id, "OR mailbox $like_domain");
db_delete('log', 'domain', $this->id); # TODO: should we really delete the log?
# finally delete the domain
2018-01-26 15:45:57 +01:00
db_delete($this->db_table, $this->id_field, $this->id);
2018-01-26 15:45:57 +01:00
if (!$this->domain_postdeletion()) {
2018-12-27 22:43:11 +01:00
$this->errormsg[] = Config::Lang('domain_postdel_failed');
}
2018-01-26 15:45:57 +01:00
db_log($this->id, 'delete_domain', $this->id); # TODO delete_domain is not a valid db_log keyword yet
$this->infomsg[] = Config::Lang_f('pDelete_delete_success', $this->id);
return true;
}
/**
* get formatted version of fields
*
* @param array values of current item
*/
2021-04-13 22:19:16 +02:00
public function _formatted_aliases($item)
{
2018-01-26 15:45:57 +01:00
return $item['alias_count'] . ' / ' . $item['aliases'] ;
}
2021-04-13 22:19:16 +02:00
public function _formatted_mailboxes($item)
{
2018-01-26 15:45:57 +01:00
return $item['mailbox_count'] . ' / ' . $item['mailboxes'];
}
2021-04-13 22:19:16 +02:00
public function _formatted_quota($item)
{
2018-01-26 15:45:57 +01:00
return $item['total_quota'] . ' / ' . $item['quota'] ;
}
/**
* Called after a domain has been added
*
* @return boolean
*/
2021-04-13 22:19:16 +02:00
protected function domain_postcreation()
{
$script = Config::read_string('domain_postcreation_script');
if (empty($script)) {
return true;
}
if (empty($this->id)) {
$this->errormsg[] = 'Empty domain parameter in domain_postcreation';
return false;
}
$cmdarg1 = escapeshellarg($this->id);
$command = "$script $cmdarg1";
$retval = 0;
$output = array();
$firstline = '';
$firstline = exec($command, $output, $retval);
if (0 != $retval) {
error_log("Running $command yielded return value=$retval, first line of output=$firstline");
$this->errormsg[] = 'Problems running domain postcreation script!';
2018-01-26 15:45:57 +01:00
return false;
}
return true;
}
/**
* Called after a domain has been edited
*
* @return boolean
*/
protected function domain_postedit()
{
$script = Config::read_string('domain_postedit_script');
if (empty($script)) {
return true;
}
if (empty($this->id)) {
$this->errormsg[] = 'Empty domain parameter in domain_postedit';
return false;
}
$cmdarg1 = escapeshellarg($this->id);
$command = "$script $cmdarg1";
$retval = 0;
$output = array();
$firstline = '';
$firstline = exec($command, $output, $retval);
if (0 != $retval) {
error_log("Running $command yielded return value=$retval, first line of output=$firstline");
$this->errormsg[] = 'Problems running domain postedit script!';
return false;
}
2018-01-26 15:45:57 +01:00
return true;
}
/**
* Called after a domain has been deleted
*
* @return boolean
*/
2021-04-13 22:19:16 +02:00
protected function domain_postdeletion()
{
$script = Config::read_string('domain_postdeletion_script');
if (empty($script)) {
return true;
}
if (empty($this->id)) {
$this->errormsg[] = 'Empty domain parameter in domain_postdeletion';
return false;
}
$cmdarg1 = escapeshellarg($this->id);
$command = "$script $cmdarg1";
$retval = 0;
$output = array();
$firstline = '';
$firstline = exec($command, $output, $retval);
if (0 != $retval) {
error_log("Running $command yielded return value=$retval, first line of output=$firstline");
$this->errormsg[] = 'Problems running domain postdeletion script!';
2018-01-26 15:45:57 +01:00
return false;
}
2018-01-26 15:45:57 +01:00
return true;
}
}
/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */