2009-03-13 21:48:24 +01:00
< ? php
2011-02-28 00:21:54 +01:00
# $Id$
2009-03-13 21:48:24 +01:00
/**
* Handlers User level alias actions - e . g . add alias , get aliases , update etc .
2011-03-06 03:14:10 +01:00
* @ property $username name of alias
* @ property $return return of methods
2009-03-13 21:48:24 +01:00
*/
2011-10-16 22:31:46 +02:00
class AliasHandler extends PFAHandler {
2009-03-13 21:48:24 +01:00
2011-12-27 19:13:32 +01:00
protected $domain_field = 'domain' ;
2011-03-06 03:14:10 +01:00
/**
*
* @ public
*/
public $return = null ;
2010-12-26 20:48:10 +01:00
2011-12-26 21:54:40 +01:00
protected function initStruct () {
$this -> db_table = 'alias' ;
$this -> id_field = 'address' ;
$this -> struct = array (
2011-12-27 19:13:32 +01:00
# field name allow display in... type $PALANG label $PALANG description default / ...
2011-12-26 21:54:40 +01:00
# editing? form list
2011-12-27 19:13:32 +01:00
'address' => pacol ( $this -> new , 1 , 1 , 'mail' , 'pEdit_alias_address' , 'pCreate_alias_catchall_text' ),
'localpart' => pacol ( $this -> new , 0 , 0 , 'text' , 'pEdit_alias_address' , 'pCreate_alias_catchall_text' , '' ,
/*options*/ '' ,
/*not_in_db*/ 1 ),
'domain' => pacol ( $this -> new , 0 , 0 , 'enum' , '' , '' , '' ,
/*options*/ $this -> allowed_domains ),
'goto' => pacol ( 1 , 1 , 1 , 'txtl' , 'pEdit_alias_goto' , 'pEdit_alias_help' ),
2012-04-08 19:41:02 +02:00
'is_mailbox' => pacol ( 0 , 0 , 1 , 'int' , '' , '' , 0 ,
# technically 'is_mailbox' is bool, but the automatic bool conversion breaks the query. Flagging it as int avoids this problem.
# Maybe having a vbool type (without the automatic conversion) would be cleaner - we'll see if we need it.
/*options*/ '' ,
/*not_in_db*/ 0 ,
/*dont_write_to_db*/ 1 ,
/*select*/ 'coalesce(__is_mailbox,0) as is_mailbox' ,
/*extrafrom*/ 'LEFT JOIN ( ' .
' 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 ( 1 , 1 , 1 , 'bool' , 'pEdit_alias_forward_and_store' , '' , 0 ,
/*options*/ '' ,
/*not_in_db*/ 1 ),
'on_vacation' => pacol ( 1 , 0 , 1 , 'bool' , 'pUsersMenu_vacation' , '' , 0 ,
2012-02-04 00:53:58 +01:00
/*options*/ '' ,
/*not_in_db*/ 1 ),
2011-12-27 19:13:32 +01:00
'active' => pacol ( 1 , 1 , 1 , 'bool' , 'pAdminEdit_domain_active' , '' , 1 ),
'created' => pacol ( 0 , 0 , 1 , 'ts' , 'created' , '' ),
'modified' => pacol ( 0 , 0 , 1 , 'ts' , 'pAdminList_domain_modified' , '' ),
2011-12-26 21:54:40 +01:00
);
}
protected function initMsg () {
$this -> msg [ 'error_already_exists' ] = 'pCreate_alias_address_text_error2' ;
$this -> msg [ 'error_does_not_exist' ] = 'pCreate_alias_address_text_error1' ; # TODO: better error message
if ( $this -> new ) {
$this -> msg [ 'logname' ] = 'create_alias' ;
$this -> msg [ 'store_error' ] = 'pCreate_alias_result_error' ;
} else {
$this -> msg [ 'logname' ] = 'edit_alias' ;
$this -> msg [ 'store_error' ] = 'pEdit_alias_result_error' ;
}
2009-03-13 21:48:24 +01:00
}
2009-03-13 22:48:38 +01:00
2011-12-26 21:54:40 +01:00
2011-12-27 19:13:32 +01:00
public function webformConfig () {
if ( $this -> new ) { # the webform will display a localpart field + domain dropdown on $new
$this -> struct [ 'address' ][ 'display_in_form' ] = 0 ;
$this -> struct [ 'localpart' ][ 'display_in_form' ] = 1 ;
$this -> struct [ 'domain' ][ 'display_in_form' ] = 1 ;
}
return array (
# $PALANG labels
'formtitle_create' => 'pCreate_alias_welcome' ,
'formtitle_edit' => 'pEdit_alias_welcome' ,
'create_button' => 'pCreate_alias_button' ,
'successmessage' => 'pCreate_alias_result_success' , # TODO: better message for edit
# various settings
'required_role' => 'admin' ,
'listview' => 'list-virtual.php' ,
'early_init' => 0 ,
);
}
2012-01-10 01:07:09 +01:00
public function init ( $id ) {
@ list ( $local_part , $domain ) = explode ( '@' , $id ); # supress error message if $id doesn't contain '@'
if ( $local_part == '*' ) { # catchall - postfix expects '@domain', not '*@domain'
$id = '@' . $domain ;
}
return parent :: init ( $id );
}
protected function validate_new_id () {
if ( $this -> id == '' ) {
$this -> errormsg [] = Lang :: read ( 'pCreate_alias_address_text_error1' );
return false ;
}
list ( $local_part , $domain ) = explode ( '@' , $this -> id );
if ( ! $this -> create_allowed ( $domain )) {
$this -> errormsg [] = Lang :: read ( 'pCreate_alias_address_text_error3' );
return false ;
}
# TODO: already checked in set() - does it make sense to check it here also? Only advantage: it's an early check
# if (!in_array($domain, $this->allowed_domains)) {
# $this->errormsg[] = Lang::read('pCreate_alias_address_text_error1');
# return false;
# }
if ( $local_part == '' ) { # catchall
$valid = true ;
} else {
$valid = check_email ( $this -> id ); # TODO: check_email should return error message instead of using flash_error itsself
}
return $valid ;
}
/**
* check number of existing aliases for this domain - is one more allowed ?
*/
private function create_allowed ( $domain ) {
$limit = get_domain_properties ( $domain );
if ( $limit [ 'aliases' ] == 0 ) return true ; # unlimited
if ( $limit [ 'aliases' ] < 0 ) return false ; # disabled
if ( $limit [ 'alias_count' ] >= $limit [ 'aliases' ]) return false ;
return true ;
}
2011-12-27 19:13:32 +01:00
/**
* merge localpart and domain to address
* called by edit . php ( if id_field is editable and hidden in editform ) _before_ -> init
*/
public function mergeId ( $values ) {
if ( $this -> struct [ 'localpart' ][ 'display_in_form' ] == 1 && $this -> struct [ 'domain' ][ 'display_in_form' ]) { # webform mode - combine to 'address' field
if ( empty ( $values [ 'localpart' ]) || empty ( $values [ 'domain' ]) ) { # localpart or domain not set
return " " ;
}
if ( $values [ 'localpart' ] == '*' ) $values [ 'localpart' ] = '' ; # catchall
return $values [ 'localpart' ] . '@' . $values [ 'domain' ];
} else {
return $values [ $this -> id_field ];
}
}
protected function setmore ( $values ) {
if ( $this -> new ) {
if ( $this -> struct [ 'address' ][ 'display_in_form' ] == 1 ) { # default mode - split off 'domain' field from 'address' # TODO: do this unconditional?
list ( /*NULL*/ , $domain ) = explode ( '@' , $values [ 'address' ]);
$this -> values [ 'domain' ] = $domain ;
}
}
$this -> values [ 'goto' ] = join ( ',' , $values [ 'goto' ]); # TODO: add mailbox and vacation aliases
}
2012-02-04 00:53:58 +01:00
protected function read_from_db_postprocess ( $db_result ) {
2011-12-27 19:13:32 +01:00
foreach ( $db_result as $key => $value ) {
$db_result [ $key ][ 'goto' ] = explode ( ',' , $db_result [ $key ][ 'goto' ]);
2012-02-04 00:53:58 +01:00
$vh = new VacationHandler ( $this -> id );
$vacation_alias = $vh -> getVacationAlias (); # TODO: move getVacationAlias to functions.inc.php to avoid the need
# for lots of VacationHandler instances (performance)?
2012-04-08 19:41:02 +02:00
# Vacation enabled?
2012-02-04 00:53:58 +01:00
list ( $db_result [ $key ][ 'on_vacation' ], $db_result [ $key ][ 'goto' ]) = remove_from_array ( $db_result [ $key ][ 'goto' ], $vacation_alias );
2012-04-08 19:41:02 +02:00
# if it is a mailbox, does the alias point to the mailbox?
if ( $db_result [ $key ][ 'is_mailbox' ]) {
# this intentionally does not catch mailbox targets with recipient delimiter.
# if it would, we would have to make goto_mailbox a text instead of a bool (which would annoy 99% of the users)
list ( $db_result [ $key ][ 'goto_mailbox' ], $db_result [ $key ][ 'goto' ]) = remove_from_array ( $db_result [ $key ][ 'goto' ], $key );
} else { # not a mailbox
$db_result [ $key ][ 'goto_mailbox' ] = 0 ;
}
2012-02-04 00:53:58 +01:00
}
2011-12-27 19:13:32 +01:00
#print_r($db_result); exit;
return $db_result ;
}
/* delete is already implemented in the " old functions " section
public function delete () {
$this -> errormsg [] = '*** Alias domain deletion not implemented yet ***' ;
return false ; # XXX function aborts here until TODO below is implemented! XXX
# TODO: move the needed code from delete.php here
}
*/
protected function _field_goto ( $field , $val ) {
if ( count ( $val ) == 0 ) {
# TODO: empty is ok for mailboxes - mailbox alias is in a separate field
$this -> errormsg [ $field ] = 'empty goto' ; # TODO: better error message
return false ;
}
2012-01-10 01:07:09 +01:00
$errors = array ();
2011-12-27 19:13:32 +01:00
foreach ( $val as $singlegoto ) {
2012-01-10 01:07:09 +01:00
if ( substr ( $singlegoto , 0 , 1 ) == '@' ) { # domain-wide forward - check only the domain part
# Note: alias domains are better, but we should keep this way supported for backward compatibility
# and because alias domains can't forward to external domains
list ( /*NULL*/ , $domain ) = explode ( '@' , $singlegoto );
if ( ! check_domain ( $domain )) {
$errors [] = " invalid: $singlegoto " ; # TODO: better error message
}
} elseif ( ! check_email ( $singlegoto )) {
$errors [] = " invalid: $singlegoto " ; # TODO: better error message
2011-12-27 19:13:32 +01:00
}
}
2012-01-10 01:07:09 +01:00
if ( count ( $errors )) {
$this -> errormsg [ $field ] = join ( " " , $errors );
return false ;
} else {
return true ;
}
2011-12-27 19:13:32 +01:00
}
/**********************************************************************************************************************************************************
old function from non - PFAHandler times of AliasHandler
They still work , but are deprecated and will be removed .
**********************************************************************************************************************************************************/
2011-12-26 21:54:40 +01:00
2009-03-13 21:48:24 +01:00
/**
2011-03-06 03:14:10 +01:00
* @ return bool true if succeed
2010-12-26 20:48:10 +01:00
* ( may be an empty list , especially if $CONF [ 'alias_control' ] is turned off ... )
2009-03-13 21:48:24 +01:00
* @ param boolean - by default we don ' t return special addresses ( e . g . vacation and mailbox alias ); pass in true here if you wish to .
*/
public function get ( $all = false ) {
2011-12-26 21:54:40 +01:00
$E_username = escape_string ( $this -> id );
2009-03-13 21:48:24 +01:00
$table_alias = table_by_key ( 'alias' );
2011-03-06 03:14:10 +01:00
$sql = " SELECT * FROM $table_alias WHERE address=' $E_username ' " ;
2009-03-13 21:48:24 +01:00
$result = db_query ( $sql );
2010-12-26 20:48:10 +01:00
if ( $result [ 'rows' ] != 1 ) {
return false ;
}
2011-01-06 21:40:34 +01:00
$row = db_array ( $result [ 'result' ]);
// At the moment Postfixadmin stores aliases in it's database in a comma seperated list; this may change one day.
$list = explode ( ',' , $row [ 'goto' ]);
if ( $all ) {
$this -> return = $list ;
return true ;
}
2009-03-13 21:48:24 +01:00
2011-01-06 21:40:34 +01:00
$filtered_list = array ();
/* if !$all, remove vacation & mailbox aliases */
foreach ( $list as $address ) {
if ( $address != '' ) {
if ( $this -> is_vacation_address ( $address ) || $this -> is_mailbox_alias ( $address )) {
# TODO: store "vacation_active" and "mailbox" status - should be readable public
}
else {
$filtered_list [] = $address ;
2009-03-13 21:48:24 +01:00
}
}
2011-01-06 21:40:34 +01:00
}
$this -> return = $filtered_list ;
return true ;
2009-03-13 21:48:24 +01:00
}
/**
* @ param string $address
* @ param string $username
* @ return boolean true if the username is an alias for the mailbox AND we have alias_control turned off .
2010-12-26 20:48:10 +01:00
* TODO : comment for @ return : does alias_control really matter here ?
2009-03-13 21:48:24 +01:00
*/
public function is_mailbox_alias ( $address ) {
global $CONF ;
2011-01-06 21:40:34 +01:00
2011-12-26 21:54:40 +01:00
if ( $address != $this -> id ) { # avoid false positives if $address is a mailbox
2011-01-06 21:40:34 +01:00
return false ;
}
2011-01-01 05:07:39 +01:00
$table_mailbox = table_by_key ( 'mailbox' );
2011-02-13 18:10:51 +01:00
$E_address = escape_string ( $address );
$sql = " SELECT * FROM $table_mailbox WHERE username=' $E_address ' " ;
2011-01-01 05:07:39 +01:00
$result = db_query ( $sql );
if ( $result [ 'rows' ] != 1 ) {
return false ;
} else {
return true ;
}
2009-03-13 21:48:24 +01:00
}
/**
* @ param string $address
* @ return boolean true if the address contains the vacation domain
*/
public function is_vacation_address ( $address ) {
global $CONF ;
if ( $CONF [ 'vacation' ] == 'YES' ) {
2010-12-26 20:48:10 +01:00
if ( stripos ( $address , '@' . $CONF [ 'vacation_domain' ])) { # TODO: check full vacation address user#domain.com@vacation_domain
2009-03-13 21:48:24 +01:00
return true ;
}
}
return false ;
}
/**
* @ return boolean true on success
* @ param string $username
* @ param array $addresses - list of aliases to set for the user .
* @ param string flags - forward_and_store or remote_only or ''
* @ param boolean $vacation_persist - set to false to stop the vacation address persisting across updates
2010-12-26 20:48:10 +01:00
* Set the user 's aliases to those provided. If $addresses ends up being empty the alias record is removed. # TODO: deleting that' s buggy behaviour , error out instead
2009-03-13 21:48:24 +01:00
*/
public function update ( $addresses , $flags = '' , $vacation_persist = true ) {
// find out if the user is on vacation or not; if they are,
// then the vacation alias needs adding to the db (as we strip it out in the get method)
// likewise with the alias_control address.
2010-12-26 20:48:10 +01:00
# TODO: move all validation from edit-alias/create-alias and users/edit-alias here
2009-03-13 21:48:24 +01:00
$valid_flags = array ( '' , 'forward_and_store' , 'remote_only' );
if ( ! in_array ( $flags , $valid_flags )) {
die ( " Invalid flag passed into update()... : $flag - valid options are : " . implode ( ',' , $valid_flags ));
}
$addresses = array_unique ( $addresses );
2011-12-26 21:54:40 +01:00
list ( /*NULL*/ , $domain ) = explode ( '@' , $this -> id );
2010-12-26 20:48:10 +01:00
if ( ! $this -> get ( true ) ) die ( " Alias not existing? " ); # TODO: better error behaviour
2009-03-13 21:48:24 +01:00
2010-12-26 20:48:10 +01:00
foreach ( $this -> return as $address ) {
2009-03-13 21:48:24 +01:00
if ( $vacation_persist ) {
if ( $this -> is_vacation_address ( $address )) {
$addresses [] = $address ;
}
}
if ( $flags != 'remote_only' ) {
if ( $this -> is_mailbox_alias ( $address )) {
$addresses [] = $address ;
}
}
}
$addresses = array_unique ( $addresses );
$new_list = array ();
if ( $flags == 'remote_only' ) {
2010-12-26 20:48:10 +01:00
foreach ( $addresses as $address ) { # TODO: write a remove_from_array function, see http://tech.petegraham.co.uk/2007/03/22/php-remove-values-from-array/
2009-03-13 21:48:24 +01:00
// strip out our username... if it's in the list given.
2011-12-26 21:54:40 +01:00
if ( $address != $this -> id ) {
2009-03-13 21:48:24 +01:00
$new_list [] = $address ;
}
}
$addresses = $new_list ;
}
if ( $flags == 'forward_and_store' ) {
2011-12-26 21:54:40 +01:00
if ( ! in_array ( $this -> id , $addresses )) {
$addresses [] = $this -> id ;
2009-03-13 21:48:24 +01:00
}
}
$new_list = array ();
foreach ( $addresses as $address ) {
if ( $address != '' ) {
2010-12-26 20:48:10 +01:00
$new_list [] = $address ; # TODO use remove_from_array, see above
2009-03-13 21:48:24 +01:00
}
}
$addresses = array_unique ( $new_list );
2011-12-26 21:54:40 +01:00
$E_username = escape_string ( $this -> id );
2010-12-26 20:48:10 +01:00
$goto = implode ( ',' , $addresses );
2009-03-13 21:48:24 +01:00
if ( sizeof ( $addresses ) == 0 ) {
2011-12-26 21:54:40 +01:00
# $result = db_delete('alias', 'address', $this->id); # '"DELETE FROM $table_alias WHERE address = '$username'"; # TODO: should never happen and causes broken behaviour
error_log ( " Alias set to empty / Attemp to delete: " . $this -> id ); # TODO: more/better error handling - maybe just return false?
2009-03-13 21:48:24 +01:00
}
2010-12-26 20:48:10 +01:00
if ( $this -> hasAliasRecord () == false ) { # TODO should never happen in update() - see also the comments on handling DELETE above
$alias_data = array (
2011-12-26 21:54:40 +01:00
'address' => $this -> id ,
2010-12-26 20:48:10 +01:00
'goto' => $goto ,
'domain' => $domain ,
'active' => db_get_boolean ( True ),
);
$result = db_insert ( 'alias' , $alias_data );
2011-01-06 21:40:34 +01:00
} else {
2010-12-26 20:48:10 +01:00
$alias_data = array (
'goto' => $goto ,
);
2011-12-26 21:54:40 +01:00
$result = db_update ( 'alias' , 'address' , $this -> id , $alias_data );
2009-03-13 21:48:24 +01:00
}
2010-12-26 20:48:10 +01:00
if ( $result != 1 ) {
2009-03-13 21:48:24 +01:00
return false ;
}
2011-02-15 23:20:27 +01:00
db_log ( $domain , 'edit_alias' , " $E_username -> $goto " );
2009-03-13 21:48:24 +01:00
return true ;
}
/**
* Determine whether a local delivery address is present . This is
* stores as an alias with the same name as the mailbox name ( username )
* @ return boolean true if local delivery is enabled
*/
public function hasStoreAndForward () {
2010-12-26 20:48:10 +01:00
$result = $this -> get ( true ); # TODO: error checking?
2011-12-26 21:54:40 +01:00
if ( in_array ( $this -> id , $this -> return )) {
2009-03-13 21:48:24 +01:00
return true ;
}
return false ;
}
/**
* @ return boolean true if the user has an alias record ( i . e row in alias table ); else false .
*/
2011-12-27 19:13:32 +01:00
private function hasAliasRecord () { # only used by update() in this class
2011-12-26 21:54:40 +01:00
$username = escape_string ( $this -> id );
2009-03-13 21:48:24 +01:00
$table_alias = table_by_key ( 'alias' );
$sql = " SELECT * FROM $table_alias WHERE address = ' $username ' " ;
$result = db_query ( $sql );
if ( $result [ 'rows' ] == 1 ) {
return true ;
}
return false ;
}
2010-12-29 00:49:14 +01:00
/**
* @ return true on success false on failure
*/
2010-12-31 20:23:31 +01:00
public function delete (){
2011-01-06 21:40:34 +01:00
if ( ! $this -> get () ) {
2010-12-31 20:23:31 +01:00
$this -> errormsg [] = 'An alias with that address does not exist.' ; # TODO: make translatable
2010-12-30 23:26:17 +01:00
return false ;
}
2011-12-26 21:54:40 +01:00
if ( $this -> is_mailbox_alias ( $this -> id ) ) {
2010-12-31 20:23:31 +01:00
$this -> errormsg [] = 'This alias belongs to a mailbox and can\'t be deleted.' ; # TODO: make translatable
2010-12-30 23:26:17 +01:00
return false ;
}
2011-12-26 21:54:40 +01:00
$result = db_delete ( 'alias' , 'address' , $this -> id );
2010-12-30 23:26:17 +01:00
if ( $result == 1 ) {
2011-12-26 21:54:40 +01:00
list ( /*NULL*/ , $domain ) = explode ( '@' , $this -> id );
db_log ( $domain , 'delete_alias' , $this -> id );
2010-12-30 23:26:17 +01:00
return true ;
2010-12-29 00:49:14 +01:00
}
}
2010-12-30 23:26:17 +01:00
2010-12-26 01:20:12 +01:00
}
/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */