Subversion Repositories general

Compare Revisions

Ignore whitespace Rev 1219 → Rev 1220

/hostadmiral/trunk/backend/backend.pl
5,9 → 5,34
# (copyleft) Anatoli Klassen
#
 
# FIXME use transactions
#
# Design:
#
# * Each handle has to (exactly in given order):
# 1. check input data, exit immediately if any errors;
# 2. open DB transaction;
# 3. update DB;
# 4. call external script;
# 5. set 2** status in request;
# 6. commit transaction.
#
# Simple handlers, which do not use DB may omit all steps 2, 3, 6 (but not part of them).
#
# If handler exits after step 1 - it just 'info' for admin, but should never hapen
# with error-free frontend.
#
# Handler may exit during steps 2-6, but it should hapen on wrong configured system
# (DB state problems, wrong file structure, insufficient rights etc), so it
# is 'error' for admin.
#
# Handler may no 'die' - it's program error. So it has to use blocks with possible
# 'die' (e.g. all DB operations) in 'eval' and then analizy '$@'.
#
 
# 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;
31,22 → 56,23
our $sudo = '/usr/local/bin/sudo'; # path to sudo
 
my $config_name = "$base_dir/backend.conf";
require "$config_name"; # read the config
require $config_name; # read the config
 
# == constants =================================
 
my $protocol_ver_maj = "1";
my $protocol_ver_min = "0";
my $protocol_header = "HostAdmiral_TcpListener";
my $password_header = "password=";
my $domain_header = "inetDomain";
my $user_header = "user";
my $system_user_header = "systemUser";
my $mailbox_header = "mailbox";
my $mail_alias_header = "mailAlias";
my $create_action = "create";
my $modify_action = "modify";
my $delete_action = "delete";
# protocol description
my $protocol_ver_maj = '1';
my $protocol_ver_min = '0';
my $protocol_header = 'HostAdmiral_TcpListener';
my $password_header = 'password=';
my $domain_header = 'inetDomain';
my $user_header = 'user';
my $system_user_header = 'systemUser';
my $mailbox_header = 'mailbox';
my $mail_alias_header = 'mailAlias';
my $create_action = 'create';
my $modify_action = 'modify';
my $delete_action = 'delete';
 
# response codes
my $code_ok = 200;
67,57 → 93,18
my $code_db_close_error = 503;
my $code_db_inconsistent = 504;
my $code_exec_error = 505;
my $code_script_error = 506;
my $code_panic = 600;
 
# == internal global variables =================
 
my %handlers;
my $database_connection;
my $database_in_use = 0;
my $database_in_use = 0;
my $database_in_transaction = 0;
 
# == functions =================================
 
sub connection_loop
{
# listen for connections
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) {
# get connection
my $rem_addr = accept(CLIENT, SERVER);
my $buf;
my $body = "";
my %request = ();
#log_debug("Remote: $rem_addr");
$request{'start_timestamp'} = [gettimeofday];
 
# receive request body
while((my $size = sysread CLIENT, $buf, 65536) > 0) {
$body .= $buf;
}
$request{'body'} = $body;
$request{'body_timestamp'} = [gettimeofday];
 
# call handler
handle_request(\%request);
$request{'done_timestamp'} = [gettimeofday];
 
# print out response
print CLIENT "$protocol_header $protocol_ver_maj.$protocol_ver_min"
. "\n$request{'code'} $request{'response'}\n\n";
close CLIENT;
$request{'stop_timestamp'} = [gettimeofday];
log_debug("Duration: " . tv_interval($request{'start_timestamp'},
$request{'stop_timestamp'}) . " sec");
}
 
# close the port
close SERVER;
return undef;
}
 
sub handle_request
{
my $request = shift @_;
141,7 → 128,7
$cur++;
 
# check end lines
if($#lines < $cur+1 || $lines[$#lines-1] ne "" || $lines[$#lines] ne "") {
if($#lines < $cur+1 || $lines[$#lines-1] ne '' || $lines[$#lines] ne '') {
set_request_code($request, $code_no_end_lines,
"Request doesn't end with \\n\\n");
return;
187,9 → 174,36
# call
log_debug("call $request->{'command'}_$request->{'subcommand'}");
my @params = @lines[$cur..$#lines-2];
&{$request->{'handler'}}($request, @params);
$database_in_transaction = 0;
eval {
&{$request->{'handler'}}($request, @params);
};
if($@) {
log_panic("Handler $request->{'command'}_$request->{'subcommand'} died: $@");
set_request_code($request, $code_panic, "Internal error");
}
 
# rollback transaction if it's still open
if($database_in_transaction) {
eval {
log_warn("Rollback transaction");
db_rollback_transaction();
};
if($@) {
log_error("Cannot rollback transaction: $@");
}
$database_in_transaction = 0;
}
 
# last check
if(!$request->{'code'} || !$request->{'response'}) {
log_panic("Empty request code or response");
set_request_code($request, $code_panic, "Internal error");
}
}
 
### request handlers ######################################
 
sub handle_user_create
{
my $request = shift @_;
239,18 → 253,22
my $name = shift @_;
my $comment = shift @_;
 
my $res_action = save_to_db($request, "transport",
my $res_action = save_to_db($request, 'transport',
{ domain => $oldName },
{ domain => $name, comment => $comment, transport => 'virtual:' } );
# FIXME: transport => 'procmail:'? then restart mail system by transport change too
 
if($res_action eq 'insert' || ($res_action eq 'update' && $name ne $oldName)) {
return "error" unless(restart_mail_system());
return 'error' unless(restart_mail_system());
}
if($oldName ne $name) {
my $call_res = call_external_script($request, 'delete_domain.sh', [ $oldName ]);
log_debug("delete_domain.sh: $call_res");
if($call_res >= 3) {
set_request_code($request, $code_script_error, "Cannot delete domain dir");
return 'error';
}
}
 
return $res_action;
259,15 → 277,22
sub handle_domain_create
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "name" => \&validate_domain,
"enabled" => \&validate_boolean,
"comment" => \&validate_comment } );
{ name => \&validate_domain,
enabled => \&validate_boolean,
comment => \&validate_comment } );
return unless(%params);
 
my $res_action = save_domain($request, $params{"name"},
$params{"name"}, $params{"comment"});
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $res_action = save_domain($request, $params{'name'},
$params{'name'}, $params{'comment'});
return if($res_action eq 'error');
 
# step 5.
if($res_action eq 'update') {
set_request_code($request, $code_ok_but, "Domain exists, modified");
}
274,21 → 299,31
elsif($res_action eq 'insert') {
set_request_code($request, $code_ok, "Domain created");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub handle_domain_modify
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "oldName" => \&validate_domain,
"name" => \&validate_domain,
"enabled" => \&validate_boolean,
"comment" => \&validate_comment } );
{ oldName => \&validate_domain,
name => \&validate_domain,
enabled => \&validate_boolean,
comment => \&validate_comment } );
return unless(%params);
 
my $res_action = save_domain($request, $params{"oldName"},
$params{"name"}, $params{"comment"});
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $res_action = save_domain($request, $params{'oldName'},
$params{'name'}, $params{'comment'});
return if($res_action eq 'error');
 
# step 5.
if($res_action eq 'update') {
set_request_code($request, $code_ok, "Domain modified");
}
295,30 → 330,49
elsif($res_action eq 'insert') {
set_request_code($request, $code_ok_but, "Domain not found, created");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub handle_domain_delete
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "name" => \&validate_domain } );
{ name => \&validate_domain } );
return unless(%params);
 
my $res_action = delete_from_db($request, "transport",
{ domain => $params{"name"} } );
# step 2.
return unless db_begin_transaction($request);
 
# step 3.
my $res_action = delete_from_db($request, 'transport',
{ domain => $params{'name'} } );
 
# step 4.
if($res_action ne 'error') {
my $call_res = call_external_script($request, 'delete_domain.sh', [ $params{"name"} ]);
my $call_res = call_external_script($request, 'delete_domain.sh', [ $params{'name'} ]);
log_debug("delete_domain.sh: $call_res");
if($call_res >= 3) {
set_request_code($request, $code_script_error, "Cannot delete domain dir");
return;
}
}
if($res_action eq 'delete') {
return unless(restart_mail_system());
}
 
# step 5.
if($res_action eq 'delete') {
return unless(restart_mail_system());
set_request_code($request, $code_ok, "Domain deleted");
}
elsif($res_action eq 'not found') {
set_request_code($request, $code_ok_but, "Domain not found");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub update_mailbox_mailid
330,17 → 384,17
 
return 1 unless($id);
 
my $res_action2 = save_to_db($request, "users",
{ login => "$login\@$domain" },
my $res_action2 = save_to_db($request, 'users',
{ login => "$login\@$domain" },
{ mailid => "$id\@$domain" });
return 0 if($res_action2 eq "error");
return 0 if($res_action2 eq 'error');
 
# create alias loop
my $res_action3 = save_to_db($request, "aliases",
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 0 if($res_action3 eq 'error');
 
return 1;
}
359,7 → 413,7
# insert or update main information
my $id;
my $res_action = save_to_db($request, "users",
my $res_action = save_to_db($request, 'users',
{ login => "$oldLogin\@$oldDomain" },
{ login => "$login\@$domain",
password => $password,
370,11 → 424,16
\$id);
 
# set mailid for the new record
return "error" unless(update_mailbox_mailid($request, $id, $login, $domain));
return 'error' unless(update_mailbox_mailid($request, $id, $login, $domain));
 
# update disk
my $call_res = call_external_script($request, 'move_mailbox.sh',
[ (defined($systemUser) ? $systemUser : ""), "$oldDomain/$oldLogin", "$domain/$login" ]);
[ (defined($systemUser) ? $systemUser : ''), "$oldDomain/$oldLogin", "$domain/$login" ]);
log_debug("move_mailbox.sh: $call_res");
if($call_res >= 3) {
set_request_code($request, $code_script_error, "Cannot create maildir");
return 'error';
}
 
return $res_action;
}
382,25 → 441,31
sub handle_mailbox_create
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "login" => \&validate_name,
"domain" => \&validate_domain,
"password" => \&validate_password,
"enabled" => \&validate_boolean,
"comment" => \&validate_comment,
"systemUser" => \&validate_system_user_id,
"virusCheck" => \&validate_boolean,
"spamCheck" => \&validate_boolean } );
{ login => \&validate_name,
domain => \&validate_domain,
password => \&validate_password,
enabled => \&validate_boolean,
comment => \&validate_comment,
systemUser => \&validate_system_user_id,
virusCheck => \&validate_boolean,
spamCheck => \&validate_boolean } );
return unless(%params);
 
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $res_action = save_mailbox($request,
$params{'login'}, $params{'domain'},
$params{'login'}, $params{'domain'},
$params{"password"},
($params{"enabled"} eq "true" ? 0 : 1),
$params{"comment"},
($params{"systemUser"} ? $params{"systemUser"} : undef));
$params{'password'},
($params{'enabled'} eq 'true' ? 0 : 1),
$params{'comment'},
($params{'systemUser'} ? $params{'systemUser'} : undef));
 
# step 5.
if($res_action eq 'update') {
set_request_code($request, $code_ok_but, "Mailbox exists, modified");
}
407,32 → 472,41
elsif($res_action eq 'insert') {
set_request_code($request, $code_ok, "Mailbox created");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub handle_mailbox_modify
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "oldLogin" => \&validate_name,
"oldDomain" => \&validate_domain,
"login" => \&validate_name,
"domain" => \&validate_domain,
"password" => \&validate_password,
"enabled" => \&validate_boolean,
"comment" => \&validate_comment,
"systemUser" => \&validate_system_user_id,
"virusCheck" => \&validate_boolean,
"spamCheck" => \&validate_boolean } );
{ oldLogin => \&validate_name,
oldDomain => \&validate_domain,
login => \&validate_name,
domain => \&validate_domain,
password => \&validate_password,
enabled => \&validate_boolean,
comment => \&validate_comment,
systemUser => \&validate_system_user_id,
virusCheck => \&validate_boolean,
spamCheck => \&validate_boolean } );
return unless(%params);
 
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $res_action = save_mailbox($request,
$params{'oldLogin'}, $params{'oldDomain'},
$params{'login'}, $params{'domain'},
$params{"password"},
($params{"enabled"} eq "true" ? 0 : 1),
$params{"comment"},
($params{"systemUser"} ? $params{"systemUser"} : undef));
$params{'password'},
($params{'enabled'} eq 'true' ? 0 : 1),
$params{'comment'},
($params{'systemUser'} ? $params{'systemUser'} : undef));
 
# step 5.
if($res_action eq 'update') {
set_request_code($request, $code_ok, "Mailbox modified");
}
439,40 → 513,59
elsif($res_action eq 'insert') {
set_request_code($request, $code_ok_but, "Mailbox not found, created");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub handle_mailbox_delete
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "login" => \&validate_name,
"domain" => \&validate_domain } );
{ login => \&validate_name,
domain => \&validate_domain } );
return unless(%params);
 
my $call_res = call_external_script($request, 'delete_mailbox.sh',
[ "$params{'domain'}/$params{'login'}" ]);
log_debug("delete_mailbox.sh: $call_res");
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
 
# 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) {
my $mail_id;
eval {
my $select_sth = $dbh->prepare("select mailid from users where login=?");
$select_sth->execute("$params{'login'}\@$params{'domain'}");
$mail_id = $select_sth->fetchrow_array();
};
return unless(db_end($request));
if(!$mail_id || $@) {
set_request_code($request, $code_db_inconsistent, "Cannot find mailid");
return;
}
 
# clear db
my $res_action = delete_from_db($request, "users",
my $res_action = delete_from_db($request, 'users',
{ login => "$params{'login'}\@$params{'domain'}" } );
return if($res_action eq 'error');
 
my $res_action2 = delete_from_db($request, "aliases", { alias => $mail_id } );
return if($res_action2 eq "error");
my $res_action2 = delete_from_db($request, 'aliases', { alias => $mail_id } );
return if($res_action2 eq 'error');
 
# delete from disk - last one, because disk has no transactions
my $call_res = call_external_script($request, 'delete_mailbox.sh',
[ "$params{'domain'}/$params{'login'}" ]);
log_debug("delete_mailbox.sh: $call_res");
if($call_res >= 3) {
set_request_code($request, $code_script_error, "Cannot delete maildir");
return;
}
 
# step 5.
if($res_action eq 'delete') {
set_request_code($request, $code_ok, "Mailbox deleted");
}
479,6 → 572,9
elsif($res_action eq 'not found') {
set_request_code($request, $code_ok_but, "Mailbox not found");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub save_mail_alias_dest
493,10 → 589,10
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 .= ',' unless($param_count++ == 0);
$select_sql .= '?';
}
$select_sql .= ")";
$select_sql .= ')';
 
my $dbh = db_begin($request);
return unless($dbh);
518,21 → 614,35
}
}
}
db_end();
return unless(db_end($request));
 
# save
foreach my $rcpt (@$rcpts) {
log_debug("save $rcpt");
my $res_action = save_to_db($request, "aliases",
my $res_action = save_to_db($request, 'aliases',
undef,
{ alias => "$address\@$domain",
rcpt => $rcpt, comment => $comment } );
return 0 if($res_action eq "error");
{ alias => "$address\@$domain", rcpt => $rcpt, comment => $comment } );
return 0 if($res_action eq 'error');
}
 
return 1;
}
 
sub check_mail_recipients
{
my $request = shift @_;
my $rcpts = shift @_;
 
# validate recipients
foreach (@$rcpts) {
unless(validate_email($_)) {
set_request_code($request, $code_wrong_params, "Wrong email $_");
return 0;
}
}
return 1;
}
 
sub save_mail_alias
{
my $request = shift @_;
543,21 → 653,13
my $comment = shift @_;
my $rcpts = shift @_;
 
# validate recipients
foreach (@$rcpts) {
unless(validate_email($_)) {
set_request_code($request, $code_wrong_params, "Wrong email $_");
return "error";
}
}
 
# delete all from db
my $del_action = delete_from_db($request, "aliases",
my $del_action = delete_from_db($request, 'aliases',
{ alias => "$oldAddress\@$oldDomain" } );
return "error" if($del_action eq "error");
return 'error' if($del_action eq 'error');
 
# save new
return "error" unless(save_mail_alias_dest($request, $address, $domain,
return 'error' unless(save_mail_alias_dest($request, $address, $domain,
$comment, $rcpts));
 
return $del_action;
566,17 → 668,24
sub handle_mail_alias_create
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "address" => \&validate_name,
"domain" => \&validate_domain,
"enabled" => \&validate_boolean,
"comment" => \&validate_comment } );
{ address => \&validate_name,
domain => \&validate_domain,
enabled => \&validate_boolean,
comment => \&validate_comment } );
return unless(%params);
my @rcpts = parse_command_array($request, @_);
return unless(check_mail_recipients(\@rcpts));
 
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $del_action = save_mail_alias($request, $params{'address'}, $params{'domain'},
$params{'address'}, $params{'domain'}, $params{'comment'}, \@rcpts);
 
# step 5.
if($del_action eq 'delete') {
set_request_code($request, $code_ok_but, "Mail alias exists, modified");
}
583,24 → 692,34
elsif($del_action eq 'not found') {
set_request_code($request, $code_ok, "Mail alias created");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub handle_mail_alias_modify
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "oldAddress" => \&validate_name,
"oldDomain" => \&validate_domain,
"address" => \&validate_name,
"domain" => \&validate_domain,
"enabled" => \&validate_boolean,
"comment" => \&validate_comment } );
{ oldAddress => \&validate_name,
oldDomain => \&validate_domain,
address => \&validate_name,
domain => \&validate_domain,
enabled => \&validate_boolean,
comment => \&validate_comment } );
return unless(%params);
my @rcpts = parse_command_array($request, @_);
return unless(check_mail_recipients(\@rcpts));
 
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $del_action = save_mail_alias($request, $params{'oldAddress'}, $params{'oldDomain'},
$params{'address'}, $params{'domain'}, $params{'comment'}, \@rcpts);
 
# step 5.
if($del_action eq 'delete') {
set_request_code($request, $code_ok, "Mail alias modified");
}
607,19 → 726,28
elsif($del_action eq 'not found') {
set_request_code($request, $code_ok_but, "Mail alias not found, created");
}
 
# step 6.
db_commit_transaction($request);
}
 
sub handle_mail_alias_delete
{
my $request = shift @_;
# step 1.
my %params = parse_command_params($request, shift @_,
{ "address" => \&validate_name,
"domain" => \&validate_domain } );
{ address => \&validate_name,
domain => \&validate_domain } );
return unless(%params);
 
my $res_action = delete_from_db($request, "aliases",
# step 2.
return unless db_begin_transaction($request);
 
# steps 3. & 4.
my $res_action = delete_from_db($request, 'aliases',
{ alias => "$params{'address'}\@$params{'domain'}" } );
 
# step 5.
if($res_action eq 'delete') {
set_request_code($request, $code_ok, "Mail alias deleted");
}
626,8 → 754,13
elsif($res_action eq 'not found') {
set_request_code($request, $code_ok_but, "Mail alias not found");
}
 
# step 6.
db_commit_transaction($request);
}
 
### validators ############################################
 
sub validate_boolean
{
$_ = shift @_;
662,7 → 795,8
sub validate_email
{
$_ = 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-]*[a-zA-Z0-9])?)$/ ? 1 : 0; # FIXME too restrict for user name?
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-]*[a-zA-Z0-9])?)$/
? 1 : 0; # FIXME too restrict for user name?
}
 
sub validate_param_name
679,6 → 813,8
return ($_ eq '' || $_ >= 1000) ? 1 : 0; # additional security check
}
 
### request parsing #######################################
 
sub decode_param
{
my $value = shift @_;
746,20 → 882,56
return %values;
}
 
### hanlders help functions ###############################
 
sub restart_mail_system
{
my $request = shift @_;
 
# FIXME do not restart to often
 
my $call_res = call_external_script($request, 'restart_mail_system.sh');
log_debug("restart_mail_system.sh: $call_res");
if($call_res >= 3) {
set_request_code($request, $code_script_error, "Cannot restart mail system");
return 0;
}
 
return 1;
}
 
sub call_external_script
{
my $request = shift @_;
my $script = shift @_;
my $params = shift @_;
 
my @args;
push @args, "$base_dir/$script";
push @args, @$params if($params);
 
my $res = system($sudo, @args);
if($res == -1) {
set_request_code($request, $code_exec_error, "Cannot execute script: $!");
return 7;
}
elsif($res & 127) {
set_request_code($request, $code_exec_error, "Script died with signal " . ($res & 127));
return 7;
}
else {
return ($res >> 8);
}
}
 
### db functions ##########################################
 
sub db_connect
{
eval {
$database_connection = DBI->connect($db_url, $db_user, $db_password);
$database_connection = DBI->connect($db_url, $db_user, $db_password, {AutoCommit => 1});
$database_connection->{AutoCommit} = 0;
$database_connection->{RaiseError} = 1;
};
 
if($@) {
777,7 → 949,7
};
}
 
sub db_begin
sub db_reconnect
{
my $request = shift @_;
 
787,7 → 959,7
my $error = db_connect();
unless($database_connection) {
set_request_code($request, $code_db_connect_error, $error);
return undef;
return 0;
}
}
 
795,15 → 967,30
eval {
$database_connection->selectrow_array("select 1");
};
if($database_connection->state eq "S1000") {
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 0;
}
}
 
return $database_connection;
}
 
sub db_begin
{
my $request = shift @_;
 
if($database_in_use) {
log_panic("Database is already in use");
set_request_code($request, $code_panic, "Internal error");
return undef;
}
 
return undef unless(db_reconnect($request));
 
$database_in_use = 1;
return $database_connection;
}
810,9 → 997,67
 
sub db_end
{
my $request = shift @_;
 
unless($database_in_use) {
log_panic("Database is not in use");
set_request_code($request, $code_panic, "Internal error");
return 0;
}
 
$database_in_use = 0;
return 1;
}
 
sub db_begin_transaction
{
my $request = shift @_;
 
if($database_in_transaction) {
log_panic("Database transaction is already open");
set_request_code($request, $code_panic, "Internal error");
return 0;
}
 
return 0 unless(db_reconnect($request)),
 
$database_in_transaction = 1;
return 1;
}
 
sub db_commit_transaction
{
my $request = shift @_;
 
unless($database_in_transaction) {
log_panic("Database transaction is not open");
set_request_code($request, $code_panic, "Internal error");
return 0;
}
 
eval {
$database_connection->commit;
};
if($@) {
set_request_code($request, $code_db_error, "Cannot commit transaction");
return 0;
}
 
$database_in_transaction = 0;
return 1;
}
 
# @throws exception
sub db_rollback_transaction
{
unless($database_in_transaction) {
die("Database transaction is not open");
}
 
$database_connection->rollback;
$database_in_transaction = 0;
}
 
sub delete_from_db
{
my $request = shift @_;
821,10 → 1066,10
 
my $res_action = 'none';
my $dbh = db_begin($request);
return unless($dbh);
return 'error' unless($dbh);
 
eval {
my $sql = "";
my $sql = '';
while(my ($key, $value) = each(%$key_columns)) {
next unless(defined $value);
$sql .= " and " if($sql);
848,8 → 1093,13
$res_action = 'delete';
}
};
if($@) {
set_request_code($request, $code_db_error, "Cannot delete from DB: $@");
$res_action = 'error';
}
 
db_end();
return 'error' unless(db_end($request));
 
return $res_action;
}
 
866,14 → 1116,14
my $error_set = 0;
my $res_action = 'none';
my $dbh = db_begin($request);
return unless($dbh);
return 'error' unless($dbh);
 
eval {
my $res = 0;
 
if($key_columns) {
my $update_sql = "";
my $where = "";
my $update_sql = '';
my $where = '';
while(my ($key, $value) = each(%$value_columns)) {
next unless(defined $value);
$update_sql .= ", " if($update_sql);
902,8 → 1152,8
}
 
if($res < 1) {
my $insert_sql = "";
my $sql_params = "";
my $insert_sql = '';
my $sql_params = '';
while(my ($key, $value) = each(%$key_columns)) {
next unless(defined $value);
next if($value_columns->{$key});
911,8 → 1161,8
$insert_sql .= ", ";
$sql_params .= ", ";
}
$insert_sql .= "$key";
$sql_params .= "?";
$insert_sql .= $key;
$sql_params .= '?';
}
while(my ($key, $value) = each(%$value_columns)) {
next unless(defined $value);
920,8 → 1170,8
$insert_sql .= ", ";
$sql_params .= ", ";
}
$insert_sql .= "$key";
$sql_params .= "?";
$insert_sql .= $key;
$sql_params .= '?';
}
$insert_sql = "insert into $table ($insert_sql)"
. " values ($sql_params)";
943,36 → 1193,18
$$last_id_ref = $dbh->selectrow_array("select LAST_INSERT_ID()") if($last_id_ref);
}
};
# FIXME handle exceptions?
if($@) {
set_request_code($request, $code_db_error, "Cannot update DB: $@");
$res_action = 'error';
}
 
db_end();
return 'error' unless(db_end($request));
 
return $res_action;
}
 
sub call_external_script
{
my $request = shift @_;
my $script = shift @_;
my $params = shift @_;
### logging and response formers ##########################
 
my @args;
push @args, "$base_dir/$script";
push @args, @$params if($params);
 
my $res = system($sudo, @args);
if($res == -1) {
set_request_code($request, $code_exec_error, "Cannot execute script: $!");
return undef;
}
elsif($res & 127) {
set_request_code($request, $code_exec_error, "Script died with signal " . ($res & 127));
return undef;
}
else {
return ($res >> 8);
}
}
 
sub strip_request_password
{
$_ = shift @_;
986,12 → 1218,20
my $code = shift @_;
my $message = shift @_;
 
if($request->{'code'} || $request->{'response'}) {
log_panic("Request code or response is already set");
return;
}
 
$request->{'code'} = $code;
$request->{'response'} = $message;
 
my $error = "Error $code '$message' in request [\n"
. strip_request_password($request->{'body'}) . "]";
if($code >= 500 && $code < 600) {
if($code == 600) {
log_panic($error);
}
elsif($code >= 500 && $code < 600) {
log_error($error);
}
elsif($code >= 400 && $code < 500) {
1001,35 → 1241,42
log_info("$code $message");
}
else {
log_error("unknown code $code");
log_panic("unknown code $code");
}
}
 
sub log_debug
{
log_message("DEBUG", shift @_) if ($log_level >= 9);
log_message('DEBUG', shift @_) if ($log_level >= 9);
}
 
sub log_info
{
log_message("INFO", shift @_) if ($log_level >= 5);
log_message('INFO', shift @_) if ($log_level >= 5);
}
 
sub log_warning
{
log_message("WARN", shift @_) if ($log_level >= 3);
log_message('WARN', shift @_) if ($log_level >= 3);
}
 
sub log_error
{
log_message("ERROR", shift @_) if ($log_level >= 1);
log_message('ERROR', shift @_) if ($log_level >= 1);
}
 
sub log_panic
{
log_message('PANIC', shift @_);
}
 
sub log_message
{
print shift @_, ":\t", shift @_, "\n";
}
 
### main functions ########################################
 
sub init
{
$handlers{"${user_header}_${create_action}"} = \&handle_user_create;
1051,6 → 1298,50
return undef;
}
 
sub connection_loop
{
# listen for connections
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);
 
log_debug("Listen on $host:$port");
 
while(1) {
# get connection
my $rem_addr = accept(CLIENT, SERVER);
my $buf;
my $body = '';
my %request = ();
#log_debug("Remote: $rem_addr");
$request{'start_timestamp'} = [gettimeofday];
 
# receive request body
while((my $size = sysread CLIENT, $buf, 65536) > 0) {
$body .= $buf;
}
$request{'body'} = $body;
$request{'body_timestamp'} = [gettimeofday];
 
# call handler
handle_request(\%request);
$request{'done_timestamp'} = [gettimeofday];
 
# print out response
print CLIENT "$protocol_header $protocol_ver_maj.$protocol_ver_min"
. "\n$request{'code'} $request{'response'}\n\n";
close CLIENT;
$request{'stop_timestamp'} = [gettimeofday];
log_debug("Duration: " . tv_interval($request{'start_timestamp'},
$request{'stop_timestamp'}) . " sec");
}
 
# close the port
close SERVER;
return undef;
}
 
sub main
{
my $error;
/hostadmiral/trunk/backend/delete_domain.sh
31,5 → 31,7
 
### work ##################################################
 
[ -d "${ROOT}${DIR}" ] && rmdir "${ROOT}${DIR}" || { echo "Cannot delete domain dir"; exit 5; }
if [ -d "${ROOT}${DIR}" ] ; then
rmdir "${ROOT}${DIR}" || { echo "Cannot delete domain dir"; exit 5; }
fi