1,9 → 1,11 |
#!/usr/bin/perl -w |
|
# |
# Sample backend for HostAdmiral |
# (copyleft) Anatoli Klassen |
# |
################################################################################################### |
# # |
# Sample backend for HostAdmiral # |
# (copyleft) Anatoli Klassen # |
# # |
################################################################################################### |
|
# |
# Design: |
31,8 → 33,6 |
|
# FIXME secure to show wrong (m.b. hacked) strings in logs and socket answers? |
# FIXME double check validate_* functions |
# FIXME by delete and rename domain - should the frontend send us an event for each mailbox |
# (alias, destination etc) in the domain or should we do it at once? |
|
use strict; |
use vars; |
42,7 → 42,7 |
use Time::HiRes qw( gettimeofday tv_interval ); |
use File::Basename qw( dirname ); |
|
# == configuration ============================= |
#=== configuration ================================================================================ |
|
my $base_dir = dirname($0); |
|
58,7 → 58,7 |
my $config_name = "$base_dir/backend.conf"; |
require $config_name; # read the config |
|
# == constants ================================= |
#=== constants ==================================================================================== |
|
# protocol description |
my $protocol_ver_maj = '1'; |
96,7 → 96,7 |
my $code_script_error = 506; |
my $code_panic = 600; |
|
# == internal global variables ================= |
#=== internal global variables ==================================================================== |
|
my %handlers; |
my $database_connection; |
103,7 → 103,7 |
my $database_in_use = 0; |
my $database_in_transaction = 0; |
|
# == functions ================================= |
#=== functions ==================================================================================== |
|
sub handle_request |
{ |
186,7 → 186,7 |
# rollback transaction if it's still open |
if($database_in_transaction) { |
eval { |
log_warn("Rollback transaction"); |
log_error("Rollback transaction"); |
db_rollback_transaction(); |
}; |
if($@) { |
202,8 → 202,10 |
} |
} |
|
### request handlers ###################################### |
### request handlers ############################################################################## |
|
#-- user handlers --------------------------------------------------------------------------------- |
|
sub handle_user_create |
{ |
my $request = shift @_; |
225,6 → 227,8 |
set_request_code($request, $code_ignored, "Not interesting in users"); |
} |
|
#-- system user handlers -------------------------------------------------------------------------- |
|
sub handle_system_user_create |
{ |
my $request = shift @_; |
246,6 → 250,8 |
set_request_code($request, $code_ignored, "Not interesting in system users"); |
} |
|
#-- domain handlers ------------------------------------------------------------------------------- |
|
sub save_domain |
{ |
my $request = shift @_; |
257,16 → 263,60 |
{ domain => $oldName }, |
{ domain => $name, comment => $comment, transport => 'virtual:' } ); |
# FIXME: transport => 'procmail:'? then restart mail system by transport change too |
return 'error' if($res_action eq 'error'); |
|
# update users and aliases tables |
if($name ne $oldName) { |
my $dbh = db_begin($request); |
return 'error' unless($dbh); |
|
eval { |
$dbh->do("update users set domain=?, login=concat(name,'\@',?)," |
. " maildir=concat(?,'/',name,'/'), mailid=concat(id,'\@',?)" |
. " where domain=?", |
undef, $name, $name, $name, $name, $oldName); |
}; |
if($@) { |
return 'error' unless(db_end($request)); |
set_request_code($request, $code_db_inconsistent, "Cannot update users"); |
return 'error'; |
} |
|
eval { |
$dbh->do("update aliases set domain=?, alias=concat(name,'\@',?) where domain=?", |
undef, $name, $name, $oldName); |
}; |
if($@) { |
return 'error' unless(db_end($request)); |
set_request_code($request, $code_db_inconsistent, "Cannot update aliases"); |
return 'error'; |
} |
|
eval { |
$dbh->do("update aliases set rcpt_domain=?, rcpt=concat(rcpt_name,'\@',?)" |
. " where rcpt_domain=?", |
undef, $name, $name, $oldName); |
}; |
if($@) { |
return 'error' unless(db_end($request)); |
set_request_code($request, $code_db_inconsistent, "Cannot update aliases"); |
return 'error'; |
} |
|
return 'error' unless(db_end($request)); |
} |
|
# restart postfix |
if($res_action eq 'insert' || ($res_action eq 'update' && $name ne $oldName)) { |
return 'error' unless(restart_mail_system()); |
} |
|
# move domain dir |
if($oldName ne $name) { |
my $call_res = call_external_script($request, 'delete_domain.sh', [ $oldName ]); |
log_debug("delete_domain.sh: $call_res"); |
my $call_res = call_external_script($request, 'move_domain.sh', [ $oldName, $name ]); |
log_debug("move_domain.sh: $call_res"); |
if($call_res >= 3) { |
set_request_code($request, $code_script_error, "Cannot delete domain dir"); |
set_request_code($request, $code_script_error, "Cannot move domain dir"); |
return 'error'; |
} |
} |
375,6 → 425,8 |
db_commit_transaction($request); |
} |
|
#-- mailbox handlers ------------------------------------------------------------------------------ |
|
sub update_mailbox_mailid |
{ |
my $request = shift @_; |
392,7 → 444,12 |
# create alias loop |
my $res_action3 = save_to_db($request, 'aliases', |
{ alias => "$id\@$domain", rcpt => "$id\@$domain" }, |
{ alias => "$id\@$domain", rcpt => "$id\@$domain", |
{ name => $id, |
domain => $domain, |
alias => "$id\@$domain", |
rcpt_name => $id, |
rcpt_domain => $domain, |
rcpt => "$id\@$domain", |
comment => "loop for $login\@$domain" } ); |
return 0 if($res_action3 eq 'error'); |
|
415,7 → 472,9 |
my $id; |
my $res_action = save_to_db($request, 'users', |
{ login => "$oldLogin\@$oldDomain" }, |
{ login => "$login\@$domain", |
{ name => $login, |
domain => $domain, |
login => "$login\@$domain", |
password => $password, |
maildir => "$domain/$login/", |
expired => $expired, |
426,6 → 485,53 |
# set mailid for the new record |
return 'error' unless(update_mailbox_mailid($request, $id, $login, $domain)); |
|
# update aliases table |
if($domain ne $oldDomain || $login ne $oldLogin) { |
my $dbh = db_begin($request); |
return 'error' unless($dbh); |
|
# find id |
eval { |
my $select_sth = $dbh->prepare("select id from users where login=?"); |
$select_sth->execute("$login\@$domain"); |
$id = $select_sth->fetchrow_array(); |
}; |
if(!$id || $@) { |
return 'error' unless(db_end($request)); |
set_request_code($request, $code_db_inconsistent, "Cannot find id"); |
return; |
} |
|
# aliases |
if($domain ne $oldDomain) { |
eval { |
$dbh->do("update aliases set rcpt_domain=?, rcpt=concat(rcpt_name,'\@',?)" |
. " where rcpt_name=? and rcpt_domain=?", |
undef, $domain, $domain, $id, $oldDomain); |
}; |
if($@) { |
return 'error' unless(db_end($request)); |
set_request_code($request, $code_db_inconsistent, "Cannot update aliases"); |
return 'error'; |
} |
} |
|
# the loop |
eval { |
$dbh->do("update aliases set domain=?, alias=?, rcpt_domain=?, rcpt=?, comment=?" |
. " where alias=?", |
undef, $domain, "$id\@$domain", $domain, "$id\@$domain", |
"loop for $login\@$domain", "$id\@$oldDomain"); |
}; |
if($@) { |
return 'error' unless(db_end($request)); |
set_request_code($request, $code_db_inconsistent, "Cannot update loop"); |
return 'error'; |
} |
|
return 'error' unless(db_end($request)); |
} |
|
# update disk |
my $call_res = call_external_script($request, 'move_mailbox.sh', |
[ (defined($systemUser) ? $systemUser : ''), "$oldDomain/$oldLogin", "$domain/$login" ]); |
553,7 → 659,7 |
{ login => "$params{'login'}\@$params{'domain'}" } ); |
return if($res_action eq 'error'); |
|
my $res_action2 = delete_from_db($request, 'aliases', { alias => $mail_id } ); |
my $res_action2 = delete_from_db($request, 'aliases', { rcpt => $mail_id } ); |
return if($res_action2 eq 'error'); |
|
# delete from disk - last one, because disk has no transactions |
577,6 → 683,8 |
db_commit_transaction($request); |
} |
|
#-- mail aliases handlers ------------------------------------------------------------------------- |
|
sub save_mail_alias_dest |
{ |
my $request = shift @_; |
619,9 → 727,16 |
# save |
foreach my $rcpt (@$rcpts) { |
log_debug("save $rcpt"); |
my ($rcpt_name, $rcpt_domain) = ($rcpt =~ /^(.*)\@(.*)$/); |
my $res_action = save_to_db($request, 'aliases', |
undef, |
{ alias => "$address\@$domain", rcpt => $rcpt, comment => $comment } ); |
{ name => $address, |
domain => $domain, |
alias => "$address\@$domain", |
rcpt_name => $rcpt_name, |
rcpt_domain => $rcpt_domain, |
rcpt => $rcpt, |
comment => $comment } ); |
return 0 if($res_action eq 'error'); |
} |
|
759,7 → 874,7 |
db_commit_transaction($request); |
} |
|
### validators ############################################ |
### validators #################################################################################### |
|
sub validate_boolean |
{ |
813,7 → 928,7 |
return ($_ eq '' || $_ >= 1000) ? 1 : 0; # additional security check |
} |
|
### request parsing ####################################### |
### request parsing ############################################################################### |
|
sub decode_param |
{ |
882,7 → 997,7 |
return %values; |
} |
|
### hanlders help functions ############################### |
### hanlders help functions ####################################################################### |
|
sub restart_mail_system |
{ |
916,7 → 1031,8 |
return 7; |
} |
elsif($res & 127) { |
set_request_code($request, $code_exec_error, "Script died with signal " . ($res & 127)); |
set_request_code($request, $code_exec_error, |
"Script died with signal " . ($res & 127)); |
return 7; |
} |
else { |
924,7 → 1040,7 |
} |
} |
|
### db functions ########################################## |
### db functions ################################################################################## |
|
sub db_connect |
{ |
1203,7 → 1319,7 |
return $res_action; |
} |
|
### logging and response formers ########################## |
### logging and response formers ################################################################## |
|
sub strip_request_password |
{ |
1275,7 → 1391,7 |
print shift @_, ":\t", shift @_, "\n"; |
} |
|
### main functions ######################################## |
### main functions ################################################################################ |
|
sub init |
{ |