6,6 → 6,7 |
# |
|
# FIXME use transactions |
# FIXME validate all information |
|
use strict; |
use vars; |
59,17 → 60,20 |
my $code_db_connect_error = 501; |
my $code_db_error = 502; |
my $code_db_close_error = 503; |
my $code_db_inconsistent = 504; |
|
# == internal global variables ================= |
|
my %handlers; |
my $database_connection; |
my $database_in_use = 0; |
|
sub connection_loop |
{ |
# listen for connections |
socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "$!\n"; |
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or die "$!\n"; |
bind(SERVER, sockaddr_in($port, inet_aton($host))) or die "$!\n"; |
socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or return $!; |
setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1) or return $!; |
bind(SERVER, sockaddr_in($port, inet_aton($host))) or return $!; |
listen(SERVER, 1); |
|
while(1) { |
103,6 → 107,7 |
|
# close the port |
close SERVER; |
return undef; |
} |
|
sub handle_request |
109,7 → 114,7 |
{ |
my $request = shift @_; |
|
log_debug("Handle request [\n$request->{'body'}]"); |
log_debug("Handle request [\n" . strip_request_password($request->{'body'}) . "]"); |
|
my @lines = split /\n/, $request->{'body'}, -1; |
my $cur = 0; |
198,6 → 203,7 |
set_request_code($request, $code_ignored, "Not interesting in users"); |
} |
|
# FIXME: merge with handle_domain_modify |
sub handle_domain_create |
{ |
my $request = shift @_; |
227,6 → 233,8 |
my %params = parse_command_params($request, shift @_, ("oldName", "name")); |
return unless(%params); |
|
# FIXME: move maildirs, update users and aliases tables |
|
my $res_action = save_to_db($request, "transport", |
{ domain => $params{"oldName"} }, |
{ domain => $params{"name"}, comment => $params{"comment"}, |
248,6 → 256,8 |
my %params = parse_command_params($request, shift @_, ("name")); |
return unless(%params); |
|
# FIXME: delete maildirs, update users and aliases tables (or they are already deleted by frontend?) |
|
my $res_action = delete_from_db($request, "transport", |
{ domain => $params{"name"} } ); |
|
272,6 → 282,30 |
{ |
} |
|
sub update_mailbox_mailid |
{ |
my $request = shift @_; |
my $id = shift @_; |
my $login = shift @_; |
my $domain = shift @_; |
|
return 1 unless($id); |
|
my $res_action2 = save_to_db($request, "users", |
{ login => "$login\@$domain" }, |
{ mailid => "$id\@$domain" }); |
return 0 if($res_action2 eq "error"); |
|
my $res_action3 = save_to_db($request, "aliases", |
{ alias => "$id\@$domain", rcpt => "$id\@$domain" }, |
{ alias => "$id\@$domain", rcpt => "$id\@$domain", |
comment => "loop for $login\@$domain" } ); |
return 0 if($res_action3 eq "error"); |
|
return 1; |
} |
|
# FIXME: merge with handle_mailbox_modify |
sub handle_mailbox_create |
{ |
my $request = shift @_; |
279,6 → 313,8 |
("login", "password", "domain")); |
return unless(%params); |
|
# insert or update main information |
my $id; |
my $res_action = save_to_db($request, "users", |
{ login => "$params{'login'}\@$params{'domain'}" }, |
{ login => "$params{'login'}\@$params{'domain'}", |
286,10 → 322,15 |
maildir => "$params{'domain'}/$params{'login'}/", |
expired => ($params{"enabled"} eq "true" ? 0 : 1), |
comment => $params{"comment"}, |
uid => ($params{"systemUser"} ? $params{"systemUser"} : undef) } ); |
uid => ($params{"systemUser"} ? $params{"systemUser"} : undef) }, |
\$id); |
|
# FIXME mailid? |
# set mailid for the new record |
return unless(update_mailbox_mailid($request, $id, $params{'login'}, $params{'domain'})); |
|
# FIXME create an empty maildir |
|
# result |
if($res_action eq 'update') { |
set_request_code($request, $code_ok_but, "Mailbox exists, modified"); |
} |
307,6 → 348,8 |
|
# FIXME move the old maildir |
|
# insert or update main information |
my $id; |
my $res_action = save_to_db($request, "users", |
{ login => "$params{'oldLogin'}\@$params{'oldDomain'}" }, |
{ login => "$params{'login'}\@$params{'domain'}", |
314,8 → 357,13 |
maildir => "$params{'domain'}/$params{'login'}/", |
expired => ($params{"enabled"} eq "true" ? "0" : "1"), |
comment => $params{"comment"}, |
uid => ($params{"systemUser"} ? $params{"systemUser"} : undef) } ); |
uid => ($params{"systemUser"} ? $params{"systemUser"} : undef) }, |
\$id); |
|
# set mailid for the new record |
return unless(update_mailbox_mailid($request, $id, $params{'login'}, $params{'domain'})); |
|
# result |
if($res_action eq 'update') { |
set_request_code($request, $code_ok, "Mailbox modified"); |
} |
332,9 → 380,26 |
|
# FIXME remove the maildir |
|
# get mailid |
my $dbh = db_begin($request); |
return unless($dbh); |
|
my $select_sth = $dbh->prepare("select mailid from users where login=?"); |
$select_sth->execute("$params{'login'}\@$params{'domain'}"); |
my $mail_id = $select_sth->fetchrow_array(); |
db_end(); |
unless($mail_id) { |
set_request_code($request, $code_db_inconsistent, "Cannot find mailid"); |
return; |
} |
|
# clear db |
my $res_action = delete_from_db($request, "users", |
{ login => "$params{'login'}\@$params{'domain'}" } ); |
|
my $res_action2 = delete_from_db($request, "aliases", { alias => $mail_id } ); |
return if($res_action2 eq "error"); |
|
if($res_action eq 'delete') { |
set_request_code($request, $code_ok, "Mailbox deleted"); |
} |
343,6 → 408,58 |
} |
} |
|
sub save_mail_alias_dest |
{ |
my $request = shift @_; |
my $address = shift @_; |
my $domain = shift @_; |
my $comment = shift @_; |
my $rcpts = shift @_; |
|
# try to find mailboxes with given names |
my $select_sql = "select login, mailid from users where login in ("; |
my $param_count = 0; |
foreach my $rcpt (@$rcpts) { |
$select_sql .= "," unless($param_count++ == 0); |
$select_sql .= "?"; |
} |
$select_sql .= ")"; |
|
my $dbh = db_begin($request); |
return unless($dbh); |
|
my $select_sth = $dbh->prepare($select_sql); |
$param_count = 0; |
foreach my $rcpt (@$rcpts) { |
$select_sth->bind_param(++$param_count, $rcpt); |
} |
|
$select_sth->execute || return 0; |
my @row; |
while(@row = $select_sth->fetchrow_array()) { |
my $login = $row[0]; |
my $mailid = $row[1]; |
foreach my $rcpt (@$rcpts) { |
if($rcpt eq $login) { |
$rcpt = $mailid; |
} |
} |
} |
db_end(); |
|
# save |
foreach my $rcpt (@$rcpts) { |
log_debug("save $rcpt"); |
my $res_action = save_to_db($request, "aliases", |
undef, |
{ alias => "$address\@$domain", |
rcpt => $rcpt, comment => $comment } ); |
return 0 if($res_action eq "error"); |
} |
|
return 1; |
} |
|
sub handle_mail_alias_create |
{ |
my $request = shift @_; |
354,14 → 471,8 |
{ alias => "$params{'address'}\@$params{'domain'}" } ); |
return if($del_action eq "error"); |
|
foreach my $rcpt (@rcpts) { |
log_debug("save $rcpt"); |
my $res_action = save_to_db($request, "aliases", |
undef, |
{ alias => "$params{'address'}\@$params{'domain'}", |
rcpt => $rcpt, comment => $params{"comment"} } ); |
return if($res_action eq "error"); |
} |
return unless(save_mail_alias_dest($request, $params{'address'}, $params{'domain'}, |
$params{'comment'}, \@rcpts)); |
|
if($del_action eq 'delete') { |
set_request_code($request, $code_ok_but, "Mail alias exists, modified"); |
382,16 → 493,8 |
{ alias => "$params{'address'}\@$params{'domain'}" } ); |
return if($del_action eq "error"); |
|
foreach my $rcpt (@rcpts) { |
log_debug("save $rcpt"); |
my $res_action = save_to_db($request, "aliases", |
undef, |
{ alias => "$params{'address'}\@$params{'domain'}", |
rcpt => $rcpt, comment => $params{"comment"} } ); |
return if($res_action eq "error"); |
} |
# FIXME add the loopback destination? handle "a@domain.com => a@domain.com" |
# as "a@domain.com => _something_@domain.com + _something_@domain.com => _something_@domain.com" ? |
return unless(save_mail_alias_dest($request, $params{'address'}, $params{'domain'}, |
$params{'comment'}, \@rcpts)); |
|
if($del_action eq 'delete') { |
set_request_code($request, $code_ok, "Mail alias modified"); |
418,6 → 521,17 |
} |
} |
|
sub validate_domain |
{ |
$_ = shift @_; |
return /^(([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)\.)*([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)$/ |
? 1 : 0; |
} |
|
sub validate_name |
{ |
} |
|
sub decode_param |
{ |
my $value = shift @_; |
479,42 → 593,61 |
|
sub db_connect |
{ |
my $request = shift @_; |
my $dbh = undef; |
eval { |
$database_connection = DBI->connect($db_url, $db_user, $db_password); |
}; |
|
eval { $dbh = DBI->connect($db_url, $db_user, $db_password); }; |
if($@) { |
set_request_code($request, $code_db_connect_error, $@); |
$dbh = undef; |
$database_connection = undef; |
return $@; |
} |
|
return $dbh; |
return undef; |
} |
|
sub db_close |
{ |
my $request = shift @_; |
my $dbh = shift @_; |
my $error = shift @_; |
my $no_error = 1; |
eval { |
$database_connection->disconnect() if($database_connection); |
}; |
} |
|
if($error) { |
set_request_code($request, $code_db_error, $error); |
$no_error = 0; |
sub db_begin |
{ |
my $request = shift @_; |
|
# connect if not yet connection |
unless($database_connection) { |
log_info("No DB connection, try to connect"); |
my $error = db_connect(); |
unless($database_connection) { |
set_request_code($request, $code_db_connect_error, $error); |
return undef; |
} |
} |
|
# verify connection and reconnect if needed |
eval { |
$dbh->disconnect() if($dbh); |
$database_connection->selectrow_array("select 1"); |
}; |
|
if($@ && $no_error) { |
set_request_code($request, $code_db_close_error, $@); |
$no_error = 0; |
if($database_connection->state eq "S1000") { |
log_info("Lost DB connection, try to reconnect"); |
my $error = db_connect(); |
unless($database_connection) { |
set_request_code($request, $code_db_connect_error, $error); |
return undef; |
} |
} |
|
return $no_error; |
$database_in_use = 1; |
return $database_connection; |
} |
|
sub db_end |
{ |
$database_in_use = 0; |
} |
|
sub delete_from_db |
{ |
my $request = shift @_; |
522,10 → 655,9 |
my $key_columns = shift @_; |
|
my $res_action = 'none'; |
my $dbh = db_connect($request); |
my $dbh = db_begin($request); |
return unless($dbh); |
|
return 'error' unless($dbh); |
|
eval { |
my $sql = ""; |
while(my ($key, $value) = each(%$key_columns)) { |
552,12 → 684,8 |
} |
}; |
|
if(db_close($request, $dbh, $@)) { |
return $res_action; |
} |
else { |
return 'error'; |
} |
db_end(); |
return $res_action; |
} |
|
sub save_to_db |
566,13 → 694,15 |
my $table = shift @_; |
my $key_columns = shift @_; |
my $value_columns = shift @_; |
my $last_id_ref = shift @_; |
|
$$last_id_ref = 0 if($last_id_ref); |
|
my $error_set = 0; |
my $res_action = 'none'; |
my $dbh = db_connect($request); |
my $dbh = db_begin($request); |
return unless($dbh); |
|
return 'error' unless($dbh); |
|
eval { |
my $res = 0; |
|
643,20 → 773,24 |
$insert_sth->bind_param(++$count, $value); |
} |
|
$res = $insert_sth->execute; |
$res_action = 'insert'; |
$res = $insert_sth->execute; |
$res_action = 'insert'; |
$$last_id_ref = $dbh->selectrow_array("select LAST_INSERT_ID()") if($last_id_ref); |
} |
}; |
# FIXME handle exceptions? |
# FIXME handle exceptions? |
|
if(db_close($request, $dbh, $@)) { |
return $res_action; |
} |
else { |
return 'error'; |
} |
db_end(); |
return $res_action; |
} |
|
sub strip_request_password |
{ |
$_ = shift @_; |
s/^password=.*$/password=*****/gm; |
return $_; |
} |
|
sub set_request_code |
{ |
my $request = shift @_; |
666,7 → 800,8 |
$request->{'code'} = $code; |
$request->{'response'} = $message; |
|
my $error = "Error $code '$message' in request [\n$request->{'body'}]"; |
my $error = "Error $code '$message' in request [\n" |
. strip_request_password($request->{'body'}) . "]"; |
if($code >= 500 && $code < 600) { |
log_error($error); |
} |
723,19 → 858,25 |
$handlers{"${mail_alias_header}_${create_action}"} = \&handle_mail_alias_create; |
$handlers{"${mail_alias_header}_${modify_action}"} = \&handle_mail_alias_modify; |
$handlers{"${mail_alias_header}_${delete_action}"} = \&handle_mail_alias_delete; |
|
return undef; |
} |
|
sub main |
{ |
#my $sth = $dbh->prepare("SELECT * FROM transport"); |
#$sth->execute(); |
#while(my $ref = $sth->fetchrow_hashref()) { |
# print "id = $ref->{'id'}\n"; |
#} |
#$sth->finish(); |
my $error; |
|
init(); |
connection_loop(); |
$error = init(); |
die "Cannot init: $error\n" if($error); |
|
# connect now to show any error to admin |
$error = db_connect(); |
die "Cannot connect to DB: $error\n" if($error); |
|
$error = connection_loop(); |
|
db_close(); |
die "Cannot listen for connections: $error\n" if($error); |
} |
|
main(); |