Subversion Repositories general

Compare Revisions

Ignore whitespace Rev 1214 → Rev 1215

/hostadmiral/trunk/backend/backend.pl
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();