#!/usr/bin/perl
#
# (C) Copyright 2003 SWsoft, Inc. All rights reserved.
# contact email: contact@plesk.com
#
# LICENSE :


%STD_LIBDIRS = ();
%PSA_LIBDIRS = ();

BEGIN {
	$psalib = $0;
	my $count  = $psalib =~ tr/\///;
	$count-- if $psalib  =~/^\.\// ;
	if( $count > 1 ) {
		$psalib =~ s/(\/[^\/]+?){2}$//;
	} elsif( $count == 1 ) {
		$psalib = '.';
	} else {
		$psalib = '..';
	}
	$psa_prefix .= $psalib;
	$psalib .= "/lib/perl5";

	# define perl version
	$perl_ver = sprintf("%vd", $^V);
	if ($perl_ver eq '%vd') { ## perl dosn't support $^V
		$perl_version = $];
		$perl_version =~ m/^([0-9]\.[0-9]{3})/;
		$perl_ver = $1;	
	} else {  ## perl supports $^V
		$perl_version = $perl_ver;
	}

	if ($^O eq 'freebsd') {
		if ("$perl_ver" ne "5.8.6") {
			print "WARNING: YOU ARE USING PERL $perl_version VERSION, BUT BU ARE COMPILED WITH PERL $  AND WE NOT GUARANTEE THAT Backup Utils WILL BE WORK PROPERLY\n";
			print "YOU SHOULD INSTALL THE FOLLOWING MODULES FOR THE PERL $perl_version TO USER Backup Utils:\n";
			print "HTML-Parser, DBI, MIME-Base64, Digest-MD5, Digest-SHA1, XML-Parser, Text-Iconv, Compress-Zlib, DBD-mysql, XML-DOM, MIME-Lite\n\n";
		}
	}

	use Config;
	my $new_prefix = $psa_prefix;
	$new_prefix =~ s,([^/])$,\1/,;
	my $sys_prefix = (($^O eq 'freebsd') && ($] < 5.006)) ? "/usr/local" : $Config{'prefix'};
	my @lib_dir_list = ('INSTALLSITELIB', 'INSTALLSITEARCH', 'INSTALLARCHLIB', 'INSTALLPRIVLIB');

	foreach my $dir_name (@lib_dir_list) {
		$_ = $Config{lc($dir_name)};
		### define standard libdir
		$STD_LIBDIRS{$dir_name} = $_;
		### define psa libdir
		s,^$sys_prefix/?,$new_prefix,;
		$PSA_LIBDIRS{$dir_name} = $_;
	}
}

use lib $STD_LIBDIRS{'INSTALLSITELIB'};
use lib $STD_LIBDIRS{'INSTALLSITEARCH'};
use lib $STD_LIBDIRS{'INSTALLARCHLIB'};
use lib $STD_LIBDIRS{'INSTALLPRIVLIB'};

use lib $PSA_LIBDIRS{'INSTALLSITELIB'};
use lib $PSA_LIBDIRS{'INSTALLSITEARCH'};
use lib $PSA_LIBDIRS{'INSTALLARCHLIB'};
use lib $PSA_LIBDIRS{'INSTALLPRIVLIB'};

use lib ".";
use lib "$psalib";



use DBI;
use XML::XQL;
use XML::XQL::DOM;
use XML::XQL::Debug;
use MIME::Base64 qw(encode_base64 decode_base64);
use Digest::MD5 qw(md5_hex);
use POSIX qw(tmpnam);

use BU::PSA::Const;
use BU::PSA::Logger;

$flags{OVERWRITE} = 1;

umask(0022);

my $usage = "Usage: $0 [-h] [-L] [-t] [-V vhosts_dir] -f <dump_file> -m <ip_map_file> -s <shells_map_file> [-c clients_map_file] [options]";

$Help = << "EOF";
$usage
Options:
    -t - test mode, if this key is specified then restoring procedure will 
	     not run, only the checking process;
    -m <ip_map_file> (is a required parameter)
	     - file with IP's list for further IP mapping, (if the 
	     specified file does not exist then it will be created as template);
    -f <dump_file> (is a required parameter)
         /fullpath/filename - regular file.
         ftp://<login>:<password>@<server>/<dumpfile_path> - storing the backup file on remote ftp server.
    -s <shells_map_file> (is a required parameter)
         file with list of shells for mapping;
    -u <sysusers_map_file>
         file with list of system user logins for mapping,
		 it is required for Redhat 9.0;
    -c <clients_map_file>
         file which automaticaly will be generated with name (clients.rej)
         if a collision for some client is detected;
    -L	<locale_id | list> - the language will be changed automatically to
		locale with <locale_id> for the clients, if a corresponding language
		pack is not installed in PSA, you should to use list - for getting
		all existing locales;
    -h - displays this help page;
    -V or --vhosts-dir the VHOSTS directory for the VHOSTS restoration;
		-V conf or --vhosts-dir=conf - use VHOSTS dir from psa.conf file;
		-V dump or --vhosts-dir=dump - use VHOSTS dir from dump file;
    --force   - each question will be answered with default answer;
    --noblock - do not block backup/restore actions, i.e. allow backup/restore
                actions for other domains at the same time;
    --version - show version of Backup/Restore utilities;

    --clients-logins - file with clients logins list which must be restored;
    --domains-names  - file with domains names list which must be restored;

    --restore-bu-cert - restore BU certificate from dump (by default server's 
                        BU certificate is left untouched)
    --restore-admin - restore admin information and preferences
			(use this if you want to restore admin and PSA is already configured)
    --restore-server - restore server preferences
			(use this if you want to restore server preferences and PSA is already configured)
EOF
#	--assign-domains-to - client
#         tftp://<host>/ - retrieve from remote host via tftp,
#         for stdin use symbol '-' instead of the file name.

use Getopt::Long;

Getopt::Long::Configure("no_ignore_case", "no_ignore_case_always", "no_pass_through");
my ($opt_h, $opt_f, $opt_m, $opt_I, $opt_L, $opt_t, $opt_v, $opt_V, $opt_force, $opt_vhosts_dir) =
	('','','','','','','','','','');
my %lopts_all = (
'h'						=> \$opt_h,
'help'					=> \$opt_h,
'f=s'					=> \$opt_f,
'm=s'					=> \$opt_m,
's=s'					=> \$opt_s,
'L=s'						=> \$opt_L,
't'						=> \$opt_t,
'd=s'					=> \$opt_d,
'version'				=> \$opt_v,
'V=s'					=> \$opt_V,
'vhosts-dir=s'			=> \$opt_vhosts_dir,
'force'					=> \$opt_force,
'clients-logins=s'		=> \$opt_clients_logins,
'domains-names=s'		=> \$opt_domains_names,
#'assign-domains-to=s'	=> \$opt_assign_domains_to,
'single-domain-mode'	=> \$single_domain_mode,
'domain-name=s'			=> \$single_mode_domain_name,
'domain-ip=s'			=> \$single_mode_domain_ip,
'client-login=s'		=> \$single_mode_client_login,
'restore-bu-cert'		=> \$restore_bu_cert,
'c=s'					=> \$clients_map_file,
'u=s'					=> \$opt_sysusers_map,
'restore-admin'			=> \$restore_admin,
'restore-server'		=> \$restore_server,
'noblock'				=> \$no_block
);

$opt_assign_domains_to = '';

unless ( GetOptions (%lopts_all) ) {
	print_stderr ("$usage\n");
	exit 1;
}

$flags{'SINGLE_DOMAIN_MODE'} = $single_domain_mode;
$flags{'RESTORE_ADMIN'} = $restore_admin;
$flags{'RESTORE_SERVER'} = $restore_server;
$flags{'NO_BLOCK'} = $no_block;

if ($opt_v) {
	$flags{'SHOW_PROCESSING'} = 1;
	printPASTitle();
	exit 0;
}

# analize command line options
if ($opt_h) {
	print_stderr ($Help);
	exit 0;
}

$do_exit = ''; ### $do_exit = 1 (TRUE) if some error occured in command line options

if ($opt_f eq '') {
	print_stderr("Option -f <dump_file> is required.\n");
	$do_exit = 1;
}

$conf{'CLIENTS_TOTAL_NUMBER'} = 0;
$conf{'DOMAINS_TOTAL_NUMBER'} = 0;


if ($single_domain_mode) {
	LoggerInit($conf{'DUMP_D'} . "/$single_mode_domain_name/" . $const{DOMAIN_BU_LOG_FILE});
			# ,$conf{'DUMP_D'} . "/$single_mode_domain_name/" . $const{DOMAIN_BU_SYS_LOG_FILE});

	### set global flag for single domain mode
	$flags{'SINGLE_DOMAIN_MODE'} = 1;
	$conf{'CLIENTS_TOTAL_NUMBER'} = 1;
	$conf{'DOMAINS_TOTAL_NUMBER'} = 1;

   #######################################################################
   #  If this is a dump in single mode we should ensure that server-wide
   #  dump/restore is not on our way because collision of domain/server
   #  backup operations can lead to a data loss...
   #######################################################################
   if (is_bu_process_locked()) {

      my $message = "Can't run restore for single domain - ".
            "server-wide backup/restore operation is running\n";

      ################################
      # Prints on stderr for console
      ################################
      print_stderr($message);

      #####################################
      # Prints on stdout for PHP frontend
      #####################################
      print_stdout($message);

      exit 1;
   }

   unless ($single_mode_domain_name) {
       	print_stderr ("In single domain mode domain name parameter --domain-name is required.\n");
       	$do_exit = 1;
   }

   unless ($single_mode_domain_ip) {
       	print_stderr ("In single domain mode domain ip parameter --domain-ip is required.\n");
       	$do_exit = 1;
   }

   unless ($single_mode_client_login) {
       	print_stderr ("In single domain mode --client-login is required.\n");
       	$do_exit = 1;
   }

} else {
	if ($single_mode_domain_name) {
		printf_stderr("Option --domain-name is used for single domain mode, you should use --domains-names option for restoration by domains list.\n");
		$do_exit = 1;
	}

	if ($single_mode_client_login) {
		printf_stderr("Option --client-login is used for single domain mode, you should use --clients-logins option for restoration by clients list.\n");
		$do_exit = 1;
	}

	if ($opt_m eq '') {
		print_stderr ("Option -m <ip_map_file> is required.\n");
		$do_exit = 1;
	} else {
		$ip_map_file = ($opt_m =~ m/^-/) ? '' : $opt_m;
		unless ($ip_map_file) {
			printf_stderr("ERROR: invalid -m parameter value.\n");
			$do_exit = 1;
		}
	}

	if ($opt_s eq '') {
		print_stderr ("Option -s <shells_map_file> is required.\n");
		$do_exit = 1;
	} else {
		$shells_map_file = ($opt_s =~ m/^-/) ? '' : $opt_s;
		unless ($shells_map_file) {
			printf_stderr("ERROR: invalid -s parameter value.\n");
			$do_exit = 1;
		}
	}
}

if ($do_exit) {
	print_stderr("$usage\n");
	exit 1;
}

if ($opt_clients_logins && $opt_domains_names) {
	print_stderr("--clients-logins and --domains-names options cannot be set at the same time.\n");
	print_stderr ("$Help");
	exit 1;
}

if ($restore_bu_cert) {
	####################################################################
	#	BU certificate will be restored from dump in Restore/Server.pm
	####################################################################
	$conf{RESTORE_BU_CERTIFICATE} = "true";
}

$flags{forceLocales} = $opt_L;
$flags{testMode} = $opt_t;
$flags{'FORCE'} = $opt_force;

if ($opt_V || $opt_vhosts_dir) {
	$conf{psaVhostsDir} = $opt_V ? $opt_V : $opt_vhosts_dir;
	if (($conf{psaVhostsDir} ne 'conf') and ($conf{psaVhostsDir} ne 'dump')) {
		print_stderr("ERROR: invalid parameter for key -V or --vhosts-dir\n");
		exit 1;
	}
}

if ($opt_domains_names) {
	%domainsListForRestore = ();
	$conf{'RESTORING_BY_DOMAINS_LIST'} = \%domainsListForRestore;
} else {
	$conf{'RESTORING_BY_DOMAINS_LIST'} = '';
}

if ($opt_clients_logins) {
	%clientsListForRestore = ();
	$conf{'RESTORING_BY_CLIENTS_LIST'} = \%clientsListForRestore;
} else {
	$conf{'RESTORING_BY_CLIENTS_LIST'} = '';
}

if (!$flags{'FORCE'} && $opt_assign_domains_to && !$conf{'RESTORING_BY_DOMAINS_LIST'}) {
	unless (askInteractive("Do you realy want to assigne all dumped domains to client with login '$opt_assign_domains_to' ?")) {
		exit 1;
	}
}


if (!$opt_d)	{
	$debug=0;
	$PSA::Const::debug = 0;
} elsif ($opt_d =~ m/^\s*([0-9]+)\s*$/) {
	$debug = $1;
	$PSA::Const::debug = $1;
} else {
	print_stderr("ERROR: invalid debug level value.\n");
	die "\n";
}

use BU::FileFunc;
use BU::PSA::CommonFunc;

unless ($single_domain_mode) {
	unless (lock_bu_process()) {
		exit 1;
	}
	LoggerInit(); ### initialize logging process
}

checkInstalledPSAVersion;

$PRINTLEVEL = 0;
$SUBLEVELMARKER = '\-->';
$PRETTYPRINT = '';

$| = 0;

print_stdout ($const{LINE});

use BU::PSA::DB;

print_stdout ("Attempt to connect to MySQL server ... ");
unless (dbConnect($const{PSA_ADMIN_NAME}, $conf{ADMIN_PASSWD}, $const{PSA_DB_NAME})) {
	print_stdout ("\n", $const{LINE});

	print_stdout ("Now the MySQL PSA service will be started.\n\n");
	system($conf{MYSQL_SCRIPT} . ' start');

	sleep 3;

	print_stdout ($const{LINE});

	print_stdout ("Attempt to connect to MySQL server ... ");
	unless (dbConnect($const{PSA_ADMIN_NAME}, $conf{ADMIN_PASSWD}, $const{PSA_DB_NAME}, 'mysql')) {
		print_stderr("ERROR: unable to connect to PSA database.\n");
		die "\n";
	}
}
print_stdout ("done\n");

$conf{'HARD_DISK_QUOTA_IS_SUPPORTED'} = hard_disk_quota_is_supported();


if ($single_domain_mode) {
	$lock_domain_struct = lock_domain($single_mode_domain_name);
	unless ($lock_domain_struct) {
		exit 1;
	}
}

#################################################
# CHECK FOR INSTALLED PACKAGES
print_stdout ($const{LINE});
print_stdout ("Checking packages installation ... ");
checkPackagesInstallation();
print_stdout ("done\n");
#################################################

if ($single_domain_mode) {
	# CHECK DOMAIN EXISTING
	my $query = dbDoQuery('SELECT id FROM domains WHERE name=' . dbQuote($single_mode_domain_name));
	unless ($query) {
		printf_stderr("Unable to check domain existing\n");
		&unlock_and_exit(1);
	}
	unless ($query->fetchrow_array()) {
		printf_stderr("'%s' domain is not exist, it required - domain must be exist for single domain restoration.\n",
						$single_mode_domain_name);
		&unlock_and_exit(1);
	}

	# CHECK CLIENT EXISTING
	$query = dbDoQuery('SELECT id FROM clients WHERE login=' . dbQuote($single_mode_client_login));
	unless ($query) {
		printf_stderr("Unable to check client existing\n");
		&unlock_and_exit(1);
	}
	unless ($query->fetchrow_array()) {
		printf_stderr("'%s' client is not exist!\n", $single_mode_client_login);
		&unlock_and_exit(1);
	}

	# CHECK BELONGING IP ADDRESS TO CLIENT POOL
	my $buff = sprintf('SELECT ipa.id FROM IP_Addresses ipa, clients c, Repository r WHERE c.pool_id=r.rep_id and r.component_id=ipa.id and ipa.ip_address=%s and c.login=%s',
						dbQuote($single_mode_domain_ip), dbQuote($single_mode_client_login));

	$query = dbDoQuery($buff);
	unless ($query) {
		printf_stderr("Unable to check whether ip address belongs to client's pool.\n");
		&unlock_and_exit(1);
	}
	unless ($query->fetchrow_array()) {
		printf_stderr("'%s' ip address does not belong to '%s' client ip pool.\n",
						$single_mode_domain_ip, $single_mode_client_login);
		&unlock_and_exit(1);
	}
}

###########################
### CHECK LOCALES IN DUMP
if ($flags{forceLocales} && ($flags{forceLocales} eq 'list')) { ### get installed locales list
	printf_stderr("Installed locales:\n");
	my $query = dbDoQuery('SELECT id FROM locales');
	unless ($query) {
		printf_stderr("Unable to get installed locales list.\n");
		&unlock_and_exit(1);
	}
	while (my ($id) = $query->fetchrow_array()) {
		printf_stderr("%s\n", $id);
	}
	&unlock_and_exit(0);
} elsif ($flags{forceLocales}) { ### check locale
	my $query = dbDoQuery('SELECT id FROM locales WHERE id=' . dbQuote($flags{forceLocales}));
	unless ($query) {
		printf_stderr("Unable to check '%s' locale existing.\n", $flags{forceLocales});
		&unlock_and_exit(1);
	}
	unless ($query->fetchrow_array()) {
		printf_stderr("Invalid locale specified for -L option '%s'.\n", $flags{forceLocales});
		&unlock_and_exit(1);
	}
} elsif (!$flags{forceLocales} && ($single_domain_mode)) {
	my $query = dbDoQuery('SELECT id FROM locales WHERE id=' . dbQuote(getMiscParam('def_locale')));
	unless ($query) {
		printf_stderr("Unable to get default server locale.\n");
		&unlock_and_exit(1);
	}
	unless (($flags{forceLocales}) = $query->fetchrow_array()) {
		printf_stderr("The locale which is specified as server default is absent in locales psa database.\n");
		&unlock_and_exit(1);
	}
}


use BU::PSA::Objects::Object;
$BU::PSA::Objects::Object::MODE = 'BACKUP_RESTORE'; # do not alter this line
use BU::PSA::Map;
use BU::MIME::Mime;
use BU::Params;
use BU::PSA::Restore::Config;
use BU::PSA::Restore::CommonData;
use BU::PSA::Restore::IPMapping;
use BU::PSA::Restore::Domain;


print_stdout ($const{LINE});


my $file_def = "psa.archive";
my ($proto, $metod, @metod_params) = getTarget($opt_f, $file_def);

unless ($metod) {
	print_stderr ("ERROR: the specified transfer method is not supported or the file name is invalid: $opt_f\n");
	print_stderr ($Help);
	&unlock_and_exit(1);
}

if($metod == $metod_const{STD}) {
	$arc = new BU::MIME::Mime({'file_descriptor' => \*stdin});

} elsif($metod == $metod_const{FILE}) {
	my ($file)= $metod_params[0];

	print_log("Processing file with PSA dump: $file\n\n");
	
	$arc = new BU::MIME::Mime({'file_name' => $file});
} elsif($metod == $metod_const{TFTP}) {
	my ($host, $file)= @metod_params;

	print_stdout ("Trying to get file $file from $host\n");

	my $tftp = Net::TFTP->new($host, BlockSize => 1024) || die "TFTP ERROR: %s.\n", $tftp->error."\n";
	$tftp->binary;
#	$tftp->debug() if ($debug > 0);
	$fh = $tftp->get($file) || die "TFTP ERROR: %s.\n", $tftp->error."\n";

	$arc = new BU::MIME::Mime({'file_descriptor' => $fh});
} elsif ($metod == $metod_const{FTP}) {
	my ($url, $file_path) = (@metod_params);
	use IO::Ftp;
	my $ftp_in = IO::Ftp->new('<',"//$url/$file_path", TYPE=>'a');
	$arc = new BU::MIME::Mime({'file_descriptor' => $ftp_in});
} else {
	print_stderr("ERROR: the specified transfer method is not supported.\n");
	die "\n";
}

unless ($arc) {
	printf_stderr("ERROR: unable to create BU::MIME::Mime object: %s\n", $arc->getErrorMsg());
	die "\n";
}

if ($arc->error) {
	printf_stderr("Dump reader error: %s\n", $arc->getErrorMsg());
	die "\n";
}

######################################
### check for single-domain-mode dump
######################################
if (($arc->getMimeHeaderField('Level') + 0 > 0) && !$single_domain_mode) {
	printf_stderr("Restoration is stopped: domain dump is detected.\n");
	&unlock_and_exit(1);
}


##################################################
### check for old beta builds (do not support it)
##################################################
my $bu_name = $arc->getMimeHeaderField('Bu-Build');
$bu_name =~ m/_build([0-9]+)\.[0-9][0-9]_/;
my $dump_bu_build_date = $1;
unless ($dump_bu_build_date) {
	printf_stderr("Unable to define BU build version in dump.\n");
	&unlock_and_exit(1);
}

my $bu_name = $const{'BUILD_NAME'};
$bu_name =~ m/_build([0-9]+)\.[0-9][0-9]_/;
my $bu_build_date = $1;
unless ($bu_build_date) {
	printf_stderr("Unable to define BU build version.\n");
	&unlock_and_exit(1);
}

#if ($dump_bu_build_date < $bu_build_date) {
#	printf_stderr("Unable to restore this dump because it is old beta version dump. It is not compatible with release version.\n");
#	&unlock_and_exit(1);
#}


#############################################
# get PSA version from database and check it
my $psaRealVer = getMiscParam('version');
$psaRealVer =~ s/^\s*0([0-9])([0-9])([0-9])\s*$/\1.\2.\3/;

my $psaRealVerInt = ver2int($psaRealVer);

if ($psaRealVerInt <= 0) {
	print_stderr("ERROR: unable to retrieve PSA database version number.\n");
	die "\n";
}

if (ver2int($const{PSA_VERSION}) != $psaRealVerInt) {
	print_stderr("ERROR: inconsistens PSA version values in version file and PSA database.\n");
	die "\n";
}

my $dumpPSAVer = $arc->getMimeHeaderField('Dumped-Psa-Version');
my $dumpPSAVerInt = ver2int($dumpPSAVer);

if ($dumpPSAVerInt <= 0) {
	print_stderr("ERROR: PSA version in MIME header is invalid.\n");
	die "\n";
}

if ($psaRealVerInt != $dumpPSAVerInt) {
	print_stderr("ERROR: can't restore dump for PSA '$dumpPSAVer' on PSA '$psaRealVer'\n");
	die "\n";
}

# check for key assign_domains_to
if ($opt_assign_domains_to) {
	my $query = dbDoQuery('SELECT id FROM clients WHERE login='. dbQuote($opt_assign_domains_to));

	unless ($query) {
		print_stderr("ERROR: unable to check assign_domains_to PSA user login.\n");
		die "\n";
	}

	($assign_domains_to_client_id) = $query->fetchrow_array();
	unless ($assign_domains_to_client_id) {
		print_stderr("ERROR: invalid assign-domains-to PSA user.\n");
		die "\n";
	}
}


$arc->readPartHeader();
if ($arc->error()) {
	printf_stderr("Error during read part header from dump: %s\n", $arc->getErrorMsg());
	die "\n";
}

print_stdout ("Reading and parsing the file containing the saved PSA configuration ...");


my $dom = new XML::DOM::Parser;

#####my $doc = $dom->parsefile(XML::XQL::Debug::filename($temp_file));
my $doc = $dom->parse($arc->getPartContent());
#####unlink($temp_file);

# global variables - XML root node and database wrapper
$psaNode = $doc->getChildNodes()->item(0);

print_stdout ("done\n");

$mailmanServiceExistsInDump = '';
$tomcatServiceExistsInDump = '';
$fpServiceExistsInDump = '';
$coldfusionServiceExistsInDump = '';
$spamassassinServiceExistsInDump = '';


if ($single_domain_mode) {
	$conf{'CLIENTS_TOTAL_NUMBER'} = 0;
	$conf{'DOMAINS_TOTAL_NUMBER'} = 1;

	if (my ($domainNode) = $psaNode->xql('./sites/domain')) {
		if ($domainNode->getAttribute('name') ne $single_mode_domain_name) {
			print_stderr("\nRetoration error: incorrect domain name in dump.\n");
			printf_stderr("Unable to restore dump from domain '%s' for domain '%s'\n", $domainNode->getAttribute('name'), $single_mode_domain_name);
			die "\n";
		}
	}
} elsif ($opt_clients_logins || $opt_domains_names) {
	print_stderr ($const{LINE});

	print_stderr ("Load list for restoring ... ");

	my $fname = $opt_clients_logins || $opt_domains_names;
	my $line = 0;

	unless (open (fd, $fname)) {
		print_stderr("Unable to open '$fname' file: $!\n");
		die "\n";
	}

	while (<fd>) {
		$line++;
		chomp($_);
		s/\s//g;
		next unless $_;

		my $syntax_error = ($conf{'RESTORING_BY_CLIENTS_LIST'} && !m/^[a-zA-Z0-9]{1}[-A-Za-z0-9_.-]{0,19}$/) ||
							($conf{'RESTORING_BY_DOMAINS_LIST'} && !m/^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9]){0,1}(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9]){0,1})+$/);

		if ($syntax_error) {
			print_stderr("Syntax error in file '$fname' on line $line\n");
			die "\n";
		}

		if ($conf{'RESTORING_BY_CLIENTS_LIST'}) {
			$clientsListForRestore{$_} = '';
		} elsif ($conf{'RESTORING_BY_DOMAINS_LIST'}) {
			$domainsListForRestore{$_} = '';
		}
	}

	close fd;

	if ($line <= 0) {
		print_stderr("Specified file '$fname' is empty.\n");
		die "\n";
	}

	if ($conf{'RESTORING_BY_DOMAINS_LIST'}) {
		$conf{'CLIENTS_TOTAL_NUMBER'} = 0;
		$conf{'DOMAINS_TOTAL_NUMBER'} = 0;

		while(my ($domainName, $checked) = each (%domainsListForRestore)) {
			next if ($checked || !$domainName);

			my ($domainNode) = $psaNode->xql('./user[@type = "client"]/sites/domain[@name = "'.$domainName.'"]');
			unless ($domainNode) {
				print_stderr("ERROR: unable to find the domain '$domainName' in the dump.\n");
				if ($flags{'FORCE'}) {
					next;
				} else {
					die "\n";
				}
			}
			$domainNode->setAttribute('restore_by_list', 'true');
			$conf{'DOMAINS_TOTAL_NUMBER'}++;

			unless ($fpServiceExistsInDump) {
				if ($domainNode->xql('./user[@type = "hosting"]/services/fp')) {
					$fpServiceExistsInDump = 1;
				}
			}
			unless ($tomcatServiceExistsInDump) {
				if ($domainNode->xql('./domain_services/domain_service[@type="tomcat"]')) {
					$tomcatServiceExistsInDump = 1;
				}
			}
			unless ($mailmanServiceExistsInDump) {
				if ($domainNode->xql('./domain_services/domain_service[@type="maillists"]')) {
					$mailmanServiceExistsInDump = 1;
				}
			}
			unless ($coldfusionServiceExistsInDump) {
				if ($domainNode->xql('./user[@type="hosting"]/services[@coldfusion $and$ @coldfusion="true"]')) {
					$coldfusionServiceExistsInDump = 1;
				}
			}

			unless ($spamassassinServiceExistsInDump) {
				if ($domainNode->xql('./user[@type="mail"]/spamassassin[@flt_enabled="true"]')) {
					$spamassassinServiceExistsInDump = 1;
				}
			}

			my $clientNode = $domainNode->getParentNode()->getParentNode();
			unless ($clientNode) {
				print_stderr("Unable to get domain owner XML node for domain '$domainName'\n");
				die "\n";
			}
			$clientNode->setAttribute('restore_by_list', 'true');

			my ($loginNode) = $clientNode->xql('./login');
			unless ($loginNode) {
				print_stderr("Unable to define domain owner login name for domain '$domainName'\n");
				die "\n";
			}

			unless (exists ($clientsListForRestore{$loginNode->getAttribute('name')})) {
				$conf{'CLIENTS_TOTAL_NUMBER'}++;
				$clientsListForRestore{$loginNode->getAttribute('name')} = '';
			}

			foreach my $domainNode ($domainNode->getParentNode()->xql('./domain')) {
				my $domainName = $domainNode->getAttribute('name');

				if (exists($domainsListForRestore{$domainName})) {
					$domainNode->setAttribute('restore_by_list', 'true');
					$domainsListForRestore{$domainName} = 1;
				}

			}
		}
	} elsif ($conf{'RESTORING_BY_CLIENTS_LIST'}) {
		$conf{'CLIENTS_TOTAL_NUMBER'} = 0;
		$conf{'DOMAINS_TOTAL_NUMBER'} = 0;

		foreach $clientNode ($psaNode->xql('./user[@type = "client"]')) {
			my ($loginNode) = $clientNode->xql('./login');
			next if (!$loginNode || !exists($clientsListForRestore{$loginNode->getAttribute('name')}));
			$clientNode->setAttribute('restore_by_list', 'true');
			$conf{'CLIENTS_TOTAL_NUMBER'}++;

			unless ($fpServiceExistsInDump) {
				if ($clientNode->xql('./sites/domain/user[@type="hosting"]/services/fp')) {
					$fpServiceExistsInDump = 1;
				}
			}
			unless ($tomcatServiceExistsInDump) {
				if ($clientNode->xql('./sites/domain/domain_services/domain_service[@type="tomcat"]')) {
					$tomcatServiceExistsInDump = 1;
				}
			}
			unless ($mailmanServiceExistsInDump) {
				if ($clientNode->xql('./sites/domain/domain_services/domain_service[@type="maillists"]')) {
					$mailmanServiceExistsInDump = 1;
				}
			}
			unless ($coldfusionServiceExistsInDump) {
				if ($clientNode->xql('./sites/domain/user[@type="hosting"]/services[@coldfusion $and$ @coldfusion="true"]')) {
					$coldfusionServiceExistsInDump = 1;
				}
			}

			unless ($spamassassinServiceExistsInDump) {
				if ($clientNode->xql('./sites/domain/user[@type="mail"]/spamassassin[@flt_enabled="true"]')) {
					$spamassassinServiceExistsInDump = 1;
				}
			}
		}

		### detect total domains number which will be restored
#		my $dom_number = $psaNode->xql('count(./user[@type="client" $and$ @restore_by_list="true"]/sites/domain)');
#		$conf{'DOMAINS_TOTAL_NUMBER'} = $dom_number;
		my @dom_number = $psaNode->xql('./user[@type="client" $and$ @restore_by_list="true"]/sites/domain');
		$conf{'DOMAINS_TOTAL_NUMBER'} = 1 + $#dom_number;
	}

	print "done\n";
} else {
	print_stdout ($const{LINE});
	print_stdout("Checking FrontPage service existing in dump ... ");
	if ($psaNode->xql(($single_domain_mode ? '.' : './user[@type="client"]') . '/sites/domain/user[@type="hosting"]/services/fp')) {
			$fpServiceExistsInDump = 1;
	}
	print_stdout("done\n");

	print_stdout("Checking Tomcat service existing in dump ... ");
	if ($psaNode->xql(($single_domain_mode ? '.' : './user[@type="client"]') . '/sites/domain/domain_services/domain_service[@type="tomcat"]')) {
		$tomcatServiceExistsInDump = 1;
	}
	print_stdout("done\n");

	print_stdout("Checking MailMan service existing in dump ... ");
	if ($psaNode->xql(($single_domain_mode ? '.' : './user[@type="client"]') . '/sites/domain/domain_services/domain_service[@type="maillists"]')) {
		$mailmanServiceExistsInDump = 1;
	}
	print_stdout("done\n");

	print_stdout("Checking ColdFusion service existing in dump ... ");
	unless ($coldfusionServiceExistsInDump) {
		if ($psaNode->xql(($single_domain_mode ? '.' : './user[@type="client"]') . '/sites/domain/user[@type="hosting"]/services[@coldfusion $and$ @coldfusion="true"]')) {
			$coldfusionServiceExistsInDump = 1;
		}
	}
	print_stdout("done\n");

	print_stdout("Checking SpamAssassin service existing in dump ... ");
	unless ($spamassassinServiceExistsInDump) {
		if ($psaNode->xql(($single_domain_mode ? '.' : './user[@type="client"]') . '/sites/domain/user[@type="mail"]/spamassassin')) {
				$spamassassinServiceExistsInDump = 1;
		}
	}
	print_stdout("done\n");


	### detect total clients number which will be restored
#	my $cl_number = $psaNode->xql('count(./user[@type="client"])');
#	$conf{'CLIENTS_TOTAL_NUMBER'} = $cl_number;
	my @cl_number = $psaNode->xql('./user[@type="client"]');
	$conf{'CLIENTS_TOTAL_NUMBER'} = 1 + $#cl_number;

	### detect total domains number which will be restored
#	my $dom_number = $psaNode->xql('count(./user[@type="client"]/sites/domain)');
#	$conf{'DOMAINS_TOTAL_NUMBER'} = $dom_number;
	my @dom_number = $psaNode->xql('./user[@type="client"]/sites/domain');
	$conf{'DOMAINS_TOTAL_NUMBER'} = 1 + $#dom_number;
}


$do_exist = '';

if ($tomcatServiceExistsInDump && !$conf{'TOMCAT_IS_INSTALLED'}) {
	print_stderr("\nTomcat service service is used in dump, but tomcat package is not installed: \n");
	if ($flags{'FORCE'}) {
		print_stderr("--force options is used, all web application will not be restored from dump.\n");
	} else {
		print_stderr("restoring is stopped (you can ignore this using --force).\n");
		$do_exit = 1;
	}
}

if ($fpServiceExistsInDump && !$conf{'FRONTPAGE_IS_INSTALLED'}) {
	print_stderr("\nFrontPage service is used in dump, but FrontPage package is not installed: \n");
	if ($flags{'FORCE'}) {
		print_stderr("--force options is used, frontpage service will be turned off for all domains.\n");
	} else {
		print_stderr("restoring is stopped (you can ignore this using --force).\n");
		$do_exit = 1;
	}
}

if ($mailmanServiceExistsInDump && !$conf{'MAILMAN_IS_INSTALLED'}) {
	print_stderr("\nMailMan service is used in dump, but MailMan package is not installed: \n");
	if ($flags{'FORCE'}) {
		print_stderr("--force options is used, MailMan service will be turned off for all domains.\n");
	} else {
		print_stderr("restoring is stopped (you can ignore this using --force, in other case you should install accordable rpms and configure it in ControlPanel).\n");
		$do_exit = 1;
	}
}

if ($coldfusionServiceExistsInDump && !$conf{'COLDFUSION_IS_INSTALLED'}) {
	print_stderr("\nColdFusion service is used in dump, but ColdFusion package is not installed: \n");
	if ($flags{'FORCE'}) {
		print_stderr("--force options is used, ColdFusion service will be turned off for all domains.\n");
	} else {
		print_stderr("restoring is stopped (you can ignore this using --force, in other case you should install accordable rpms and configure it in ControlPanel).\n");
		$do_exit = 1;
	}
}

if ($spamassassinServiceExistsInDump && !$conf{'SPAMASSASSIN_IS_INSTALLED'}) {
	print_stderr("\nSpamAssassin service is used in dump, but SpamAssassin package is not installed: \n");
	if ($flags{'FORCE'}) {
		print_stderr("--force options is used, SpamAssassin service will be turned off for all domains.\n");
	} else {
		print_stderr("restoring is stopped (you can ignore this using --force, in other case you should install accordable rpms and configure it in ControlPanel).\n");
		$do_exit = 1;
	}
}

&unlock_and_exit(1) if $do_exit;


$flags{psaIsConfigured} = (getMiscParam('psa_configured') eq 'true');

if (my ($confNode) = $psaNode->xql('./server/params/psa_conf')) {
	$const{'OLD_HTTPD_VHOSTS_D'} = $confNode->getAttribute('HTTPD_VHOSTS_D');
	$conf{'OLD_MAILMAN_ROOT_D'} = $confNode->getAttribute('MAILMAN_ROOT_D');
}

$flags{psaIsConfigured} = (getMiscParam('psa_configured') eq 'true');

$conf{'FULL_HOST_NAME_IN_DUMP'} = '';
if (my ($fhnNode) = $psaNode->xql('./server/params/ip[@tagname = "FullHostName"]')) {
	$conf{'FULL_HOST_NAME_IN_DUMP'} = $fhnNode->getAttribute('host');
}
$conf{'FULL_HOST_NAME'} = '';

if ($flags{psaIsConfigured}) {
	$conf{'FULL_HOST_NAME'} = getMiscParam('FullHostName');
} else {
	$conf{'FULL_HOST_NAME'} = $conf{'FULL_HOST_NAME_IN_DUMP'};
}

print_stdout ($const{LINE});

my $single_domain_ip_id = undef;
# make ip addresses index hash $PSA::Restore::IPMaping::ipAddressesIndex
if (my ($node) = $psaNode->xql('./server/ip_addresses_list')) {
	if ($single_domain_mode) {
		my $query = dbDoQuery('SELECT * FROM IP_Addresses WHERE ip_address=' . dbQuote($single_mode_domain_ip));
		if (my $hash = $query->fetchrow_hashref()) {
			if ($ip_node = $node->getFirstChild()) {
				if ($ip_node->getAttribute('type') ne $$hash{'type'}) {
					printf_stderr("Error: destination ip address type is differ from source ip address type in dump.\n");
					&unlock_and_exit(1);
				}
			}
			use BU::XML::CommonFunc;
			my $sn = createNodeFromHash($doc, 'ip_address', $hash);
			$node->appendChild($sn);
			$sn->setAttribute('real_id', $$hash{'id'});
			$single_domain_ip_id = $$hash{'id'};
		}
	}
	makeIPAddressIndex($node);
}

# make certs index hash $PSA::Restore::IPMaping::certificatesIndex
if (my ($node) = $psaNode->xql('./server/certificates_list')) {
	makeCertificatesIndex($node);
}

### clients mapping
if ($clients_map_file) {
	printf_stdout("Load clients map file ...");
	my ($clients_logins_map, $clients_pnames_map) = readClientsMapFile($clients_map_file);
	if (!$clients_logins_map && !$clients_pnames_map) {
		printf_stdout("error\n");
		die "\n";
	}
	printf_stdout("done\n\n");
	if ($clients_logins_map) {
		while (my ($login, $new_login) = each(%$clients_logins_map)) {
			next if ($login eq $new_login);
			if (my ($node) = $psaNode->xql('./user[@type="client"]/login[@name="'.$login.'"]')) {
				printf_stdout("Map client with login '%s' to '%s' \n", $login, $new_login);
				$node->setAttribute('name', $new_login);
			}
		}
	}
	if ($clients_pnames_map) {
		while (my ($pname, $new_pname) = each(%$clients_pnames_map)) {
			next if ($pname eq $new_pname);
			if (my ($node) = $psaNode->xql('./user[@type="client"]/card/pname[text()="'.$pname.'"]')) {
				printf_stdout("Map client with personal name '%s' to '%s' \n", $pname, $new_pname);
				my $sn = $node->getFirstChild();
				$node->removeChild($sn);
				$node->appendChild($doc->createTextNode(a2u($new_pname)));
			}
		}
	}
}


# do ip mapping
$do_exit = '';

if ($ip_map_file || $single_domain_mode) {
	print_stdout ($const{LINE});

	my $ipList = undef;
	my $ipMap = undef;
	my $externalIPList = undef;
	my $clientsIPPool = undef;

	($ipList, $externalIPList, $clientsIPPool) = getIPsListForMapping();
	unless ($ipList) {
		print_stderr("ERROR: IP's list for mapping is empty.\n");
		die "\n";
	}

	my ($sysIfacesList, $sysIPsList) = getSysIPsList();
	unless ($sysIPsList) {
		print_stderr("ERROR: unable to retrieve system IP's list.\n");
		die "\n";
	}

	if ($const{OSid} eq 'freebsd') {
		correctFreeBSDIPMasks($sysIPsList);
	}

	if ($single_domain_mode) {
		### EMULATE IP MAPPING PROCEDURE
		foreach $i (keys %$ipList) {
			next if ($$ipList{$i} ne $single_mode_domain_name);
			$ipMap{$i} = $single_mode_domain_ip;
		}
	} else {
		if (-e $ip_map_file) {
			unless ($ipMap = doParseIPsMapFile($ip_map_file, $ipList, $externalIPList, $clientsIPPool, $sysIfacesList, $sysIPsList)) {
				printf_stderr("Unable to perform mapping procedure: IP's map is empty.\n");
				die "\n";
			}
		} else {
			writeIPsListForMapping($ipList, $externalIPList, $sysIPsList, $clientsIPPool, $ip_map_file);
			print_stdout ("IP's list has been written into the file $ip_map_file\n");
			print_stdout ("You should indicate IP addresses for mapping by editing that file.\n");
			$do_exit = 1;
		}
	}

	unless ($do_exit) {
		print_stdout ("\n\nThe IP's mapping procedure has been started ...\n\n");
		doIPsMapping($psaNode, $ipList, $externalIPList, $ipMap, $sysIPsList, $clientsIPPool);
	}
}

### shells mapping
if ($shells_map_file && !$single_domain_mode) {
	print_stdout ($const{LINE});

	unless ( -e $shells_map_file) {
		print_stderr("Check system users shells in dump ... ");
		my $shells_list = getShellsForMapping();
		unless ($shells_list) {
			printf_stderr("ERROR: unable to get shells list for mapping.\n");
			&unlock_and_exit(1);
		}
		print_stderr("done\n");

		writeShellsListToMapFile($shells_map_file, $shells_list);
		print_stdout ("Shell's list has been written into the file $shells_map_file\n");
		print_stdout ("You should indicate shells locations for mapping by editing that file.\n");
		$do_exit = 1;
	}
}



###########################################################
### CHECK SYSTEM USER LOGINS SYNTAX FOR LINUX REDHAT 9.0

### calculate base node path 
my $SYSUSER_PREFIX = '';
if ($conf{'RESTORING_BY_CLIENTS_LIST'}) {
	$SYSUSER_PREFIX = './user[@type="client" $and$ @restore_by_list="true"]';
} elsif ($conf{'RESTORING_BY_DOMAINS_LIST'}) {
	$SYSUSER_PREFIX = './user[@type="client"]/sites/domain[@restore_by_list="true"]';
} else {
	$SYSUSER_PREFIX = './user[@type="client"]/sites/domain';
}

if ((!$opt_sysusers_map || !(-e $opt_sysusers_map)) && (ver2int($const{OS_VERSION}) >= 900000000)) {
	print_stdout("ATTENTION: RedHat 9.0 does not able to create system user with dot and upper case characters in login.\n");

	### hash like (lower_case_login => login, ...)
	my %logins_used_in_dump = (); 

	### map hash for login
	my %login_map = (); 

	my $exists_bad_login = undef;

	print_stdout("Check dump for system users with such logins ... ");
	print_stderr("Search all users: " . $SYSUSER_PREFIX . '//user/login[@real="true"]' . "\n") if $debug > 1;
	foreach my $node ($psaNode->xql($SYSUSER_PREFIX . '//user/login[@real="true"]')) {
		$login = $node->getAttribute('name');
		print_stderr("check login:($login)\n") if $debug > 1;
		if ($login =~ m/[A-Z\.]/) {
			$exists_bad_login = 1;
			$lower_case_login = lc($login);
			if (exists($logins_used_in_dump{$lower_case_login})) {
				### such login in lower case is used in dump
				my $prev_login = $logins_used_in_dump{$lower_case_login};
				### map previous found login with empty target
				$login_map{$prev_login} = '' if ($prev_login ne $lower_case_login);
				### map current login with empty target
				$login_map{$login} = '';
			} elsif (util_exec('usermng', ('exist', $lower_case_login))) {
				### user with such login in lower case is exist in system
				$login_map{$login} = '';
			} else {
				### such login in lower case is not used - can be mapped
				$login_map{$login} = $lower_case_login;
			}
		} else {
			### currect login no contains big chars and dot, but such login in lower
			### case is used in dump
			$lower_case_login = lc($login);
			if (exists($logins_used_in_dump{$lower_case_login})) {
				$login_map{$logins_used_in_dump{$lower_case_login}} = '';
			}
		}
		$logins_used_in_dump{$lower_case_login} = $login;
	}
	print_stdout("done\n");

	if ($exists_bad_login) {
		print_stderr("System users with such login are exist in dump.\n");

		$sysusers_map = 'sysusers.map';
		unless (open (fd, ">$sysusers_map")) {
			printf_stderr("Unable to create '%s' file: %s\n", $sysusers_map, $!);
			die;
		}
		while (my ($login, $target_login) = each(%login_map)) {
			print fd "$login=>$target_login\n";
		}
		close fd;
		print_stderr("All logins for such system users has been written into '$sysusers_map'.\n");
		print_stdout ("You should indicate new logins for such system users for mapping by editing that file.\n");
		$do_exit = 1;
	}
}

### EXIT IF do_exit if set to TRUE (it may be --test mode)
&unlock_and_exit(0) if $do_exit;



###########################################################
### CHECK SYSTEM USER LOGINS SYNTAX FOR LINUX REDHAT 9.0
if ($opt_sysusers_map && (ver2int($const{OS_VERSION}) >= 900000000)) {

	printf_stdout("Load system users map file '%s' ... ", $opt_sysusers_map);
	unless (open (fd, $opt_sysusers_map)) {
		printf_stderr("Error open '%s' file: %s\n", $opt_sysusers_map, $!);
		&unlock_and_exit(1);
	}
	binmode fd;
	my $line = 0;
	while (<fd>) {
		$line++;
		s/#.*$//;
		next if m/^$/;
		if (m/^\s*([^\s=>]+)\s*=>\s*([a-z][0-9a-z_-]{0,16})\s*$/) {
			$sysusers_map{$1} = $2;
			next;
		}
		printf_stderr("Syntax error in '%s' file in line %s.\n", $opt_sysusers_map, $line);
		&unlock_and_exit(1);
	}
	close fd;
	printf_stdout("done\n");

	my $bad_login_notmapped = undef;

	print_stdout("Check and change login for system users: \n");
	foreach my $node ($psaNode->xql($SYSUSER_PREFIX . '//user/login[@real="true"]')) {
		$login = $node->getAttribute('name');
		if ($login =~ m/[A-Z\.]/) {
			printf_stdout("'%s' map processing ... ", $login);
			if (exists($sysusers_map{$login})) {
				my $new_login = $sysusers_map{$login};
				$node->setAttribute('name', $new_login);
				printf_stdout("mapped to '%s'\n", $new_login);
			} else {
				$bad_login_notmapped = 1;
				print_stdout("Unable to map '$login' login: terget login is absent in map.\n");
			}
		}
	}
	print_stdout("done\n");

	if ($bad_login_notmapped) {
		&unlock_and_exit(1);
	}
}


if ($shells_map_file && !$single_domain_mode) {
	print_stdout ($const{LINE});

	my $shells_from_system = getShellsList(1); # get shells list in the following format: shellfile => shellname
	unless ($shells_from_system) {
		print_stderr("Unable to get list of awailable system shells.\n");
		&unlock_and_exit(1);
	}
	print_stderr("Read file with shells map ... ");
	my $shells_list = readShellsListFromMapFile($shells_map_file, $shells_from_system);
	unless ($shells_list) {
		printf_stderr("Unable to map shells: map list is empty.\n");
		&unlock_and_exit(1);
	}
	print_stderr("done\n");
	print_stderr("Attempt to mapping shell for all system users ... ");
	if (doShellsMapping($shells_list, $shells_from_system)) {
		print_stderr("done\n");
	} else {
		&unlock_and_exit(1);
	}
}


### eq TRUE if postgres database is exist in dump
$conf{'PG_DB_IS_EXIST_IN_DUMP'} = '';

if ($conf{PGSQL_IS_INSTALLED}) {
	print_stdout ($const{LINE});

	printf_stderr("Check postgres database existing in dump ... ");
	unless (my $tmp = $psaNode->xql('./' . ($single_domain_mode ? '' : 'user[@type="client"]/') . 'sites/domain/data_bases[@type="postgresql"]')) {
		print_stderr("postgres database(s) not found.\n");
	} else {
		$conf{'PG_DB_IS_EXIST_IN_DUMP'} = 1;
		print_stderr("found postgres database(s).\n");

		print_stderr("\nCheck Postgres server running ... ");
		$conf{PG_IS_RUNNING} = checkPostgresRunning();
		if ($conf{PG_IS_RUNNING}) {
			print_stderr("done\n\n");
		} elsif ($single_domain_mode) {
			print_stderr("is not running.\n\n");
		} else {
			print_stderr ("Now the Postgres PSA service will be started.\n");
			system($conf{PGSQL_SCRIPT} . ' start 1>&2');
			sleep 3;

			print_stderr ("\nCheck Postgres server running ... ");
			$conf{PG_IS_RUNNING} = checkPostgresRunning();
			if ($conf{PG_IS_RUNNING}) {
				print_stderr ("done\n");
			} else {
				print_stderr("Error starting Postgres PSA service.\n");
			}
		}

		############################################################
		#
		#	Check postgresql version. There are some changes in
		#	postgres configuration files which should be concerned
		#	during postgres database restoration.
		#
		############################################################
		{ ### BEGIN

		unless (open(FD, $conf{PGSQL_BIN_D} . '/psql --version|')) {
			print_stderr("Unable to get postgresql version: $!\n");
			die "\n";
		}
		binmode FD;

		my $version_str = undef;
		while ($_ = <FD>) {
			if ($_ =~ m/PostgreSQL[^0-9]+(\d\.\d(\.\d)?)\s*$/) {
				$version_str = $1;
				last;
			}
		}

		if ($version_str) {
			######################################################
			#	We are interesting only in differing 7.2 and 7.3
			######################################################
			if (ver2int($version_str) >= ver2int('7.3.0')) {
				$conf{'pg_access_string1'} = "local all all trust\n";
				$conf{'pg_access_string2'} = "host  all all 127.0.0.1 255.255.255.255 trust\n";
			} else {
				$conf{'pg_access_string1'} = "local all trust\n";
				$conf{'pg_access_string2'} = "host  all 127.0.0.1 255.255.255.255 trust\n";
			}

		} else {
				print_stderr("Error determining Postgres version. Assuming 7.1\n");

				$conf{'pg_access_string1'} = "local all trust\n";
				$conf{'pg_access_string2'} = "host  all 127.0.0.1 255.255.255.255 trust\n";
		}

		close FD;

		} ### END

		$conf{'postgresql_admin_login'} = getMiscParam('postgresql_admin_login');
		$conf{'postgresql_admin_passwd'} = getMiscParam('postgresql_admin_passwd');
		if ($conf{PG_IS_RUNNING} && !$single_domain_mode) {
			### IF single_domain_mode THEN do not initialize postgres 
			my $init_result = initPostgres($psaNode);
			if ($init_result > 1) {
				print_stderr ("Now the Postgres PSA service will be restarted.\n");
				system($conf{PGSQL_SCRIPT} . ' stop 1>&2');
				sleep 3;
				### remove postmaster.pid file
				unlink($conf{'PGSQL_DATA_D'} . '/postmaster.pid') if (-e $conf{'PGSQL_DATA_D'} . '/postmaster.pid');
				system($conf{'PGSQL_SCRIPT'} . ' start 1>&2');
				sleep 3;
				print_stderr ("\n");
			}
			if ($init_result) {
				print_stdout ("Attempt to connect to Postgres server ... ");
				if (checkPostgresConnecting()) {
					print_stdout("done\n");
				} else {
					printf_stderr("Error: can't connect to postgres server: with login '%s' and password '%s'.\n", $conf{'postgresql_admin_login'}, $conf{'postgresql_admin_passwd'});
					$conf{PG_IS_RUNNING} = 0;
				}
			}
		}
	}
} else {
	print_stdout ($const{LINE});
	# Mark it as not running anyway
	print_stderr("Postgres is not installed.\n");
	$conf{PG_IS_RUNNING} = 0;
}

print_stdout ($const{LINE});

print_stdout ("Checking the PSA system configuration.\n\n");

my $useDumpedVhostsDir = '';
if (!$flags{psaIsConfigured} && ($const{OLD_HTTPD_VHOSTS_D} ne $conf{HTTPD_VHOSTS_D})) {
	if (exists($conf{psaVhostsDir})) {
		$useDumpedVhostsDir = ($conf{psaVhostsDir} eq 'dump');
	} else {
		if ($metod == $metod_const{STD}) {
			print_stderr("ERROR: vhosts directory is not determined.\n");
			print_stderr("One of option -V or --vhosts-dir must be specified.\n");
			&unlock_and_exit(1);
		} else {
			printf_stderr("psa.conf VHOSTS directory '%s' and dumped VHOSTS directory '%s' do not match.\n",
							$conf{HTTPD_VHOSTS_D}, $const{OLD_HTTPD_VHOSTS_D});
			$useDumpedVhostsDir = askInteractive('Do you want to use dumped VHOSTS directory ?');
		}
	}

	if ($useDumpedVhostsDir) {
		$conf{OLD_HTTPD_VHOSTS_D} = $conf{HTTPD_VHOSTS_D};
		$conf{HTTPD_VHOSTS_D} = $const{OLD_HTTPD_VHOSTS_D};
	} else {
		$conf{OLD_HTTPD_VHOSTS_D} = $const{OLD_HTTPD_VHOSTS_D};
	}

} else {
	if ($const{OLD_HTTPD_VHOSTS_D} ne $conf{HTTPD_VHOSTS_D}) {
		warn_log "WARNING: vhosts directory will be restored into the '%s' directory, because PSA has been already configured.\n",
				$conf{HTTPD_VHOSTS_D}."\n";

		$conf{OLD_HTTPD_VHOSTS_D} = $const{OLD_HTTPD_VHOSTS_D};
	}
}

$flags{RESTORE_MODE} = 'CHECK';
$conf{'CLIENTS_PROCESSED_NUMBER'} = 0;
$conf{'DOMAINS_PROCESSED_NUMBER'} = 0;
$ProcessName = 'Check the';
$fpRequired = '';

if ($single_domain_mode) {
	my $query = dbDoQuery('SELECT id FROM clients WHERE login=' . dbQuote($single_mode_client_login));
	my ($clientId) = $query->fetchrow_array();

	unless ($clientId) {
		print_stderr("ERROR: client with login '$single_mode_client_login' is not exist.\n");
		&unlock_and_exit(1);
	}

	unless ($single_domain_ip_id) {
		printf_stderr("Unable to define id for '%s' domain ip address\n", $single_mode_domain_ip);
		&unlock_and_exit(1);
	}

	if (my ($serverNode) = $psaNode->xql('./server')) {
		# check site app packages installation
		restoreSiteAppPackages($serverNode);
	}

	if (my ($base_node) = $psaNode->xql('./sites/domain')) {
		$base_node->setAttribute('name', $single_mode_domain_name);
		restoreDomain($clientId, $single_mode_client_login, $base_node, $single_domain_mode, $single_domain_ip_id);
	}
} elsif ($opt_assign_domains_to) {
	if ($conf{'RESTORING_BY_DOMAINS_LIST'}) {
		if (my ($serverNode) = $psaNode->xql('./server')) {
			# check site app packages installation
			restoreSiteAppPackages($serverNode);
		}

		foreach $domainNode ($psaNode->xql('./user[@type = "client"]/sites/domain[@restore_by_list = "true"]')) {
			restoreDomain($assign_domains_to_client_id, $opt_assign_domains_to, $domainNode);
			delete $domainsListForRestore{$domainNode->getAttribute('name')} if ($conf{'RESTORING_BY_DOMAINS_LIST'});
		}

		my $notExistsDomains = '';

		while (my ($domainName, $tmp) = each(%domainsListForRestore)) {
			next unless $domainName;
			print_stderr "WARNING: domain with name '$domainName' from list is not exist in dump.\n";
			$notExistsDomains = 1;
		}

		if ($notExistsDomains) {
			print_stderr("ERROR: found some domains in list which are not exist in dump.\n");
			die unless $flags{'FORCE'};
		}
	} else {
		foreach $domainNode ($psaNode->xql('./user[@type = "client"]/sites/domain')) {
			restoreDomain($assign_domains_to_client_id, $opt_assign_domains_to, $domainNode);
		}
	}
} else {
	restoreConfig();
}

print_stdout ($const{LINE});


if (!$single_domain_mode && !$flags{'IGNORE_CLIENT_COLLISIONS'} && $flags{'CLIENT_COLLISION_DETECTED'}) {
	printf_stderr("Clients collision detected.\n");
	writeClientsRejectList($clients_map_file);
	print_stdout ($const{LINE});
}



#############################################
#
#	Check key limits if Plesk is configured
#
#############################################
if ($flags{psaIsConfigured}) {

	print_stdout("Check key limits ... ");
	my $do_exit = '';

	if (exists($conf{CLIENTS_LIMIT}) && ($conf{CLIENTS_LIMIT} > 0)) {
		$query = dbDoQuery('SELECT COUNT(*) FROM clients');
		my ($psa_clients_usage) = $query->fetchrow_array();

		printf_stderr("CL: (%s) < (%s + %s)\n", $conf{CLIENTS_LIMIT}, $psa_clients_usage, $conf{CLIENTS_USAGE}) if ($debug > 0);

		if ($conf{CLIENTS_LIMIT} < ($psa_clients_usage + $conf{CLIENTS_USAGE} + 0)) {
			print_stdout("\n");
			printf_stderr("Error: key limit 'clients' is %s, but required for restore: %s (in addition to %s already exists in Plesk)\n",
						  $conf{CLIENTS_LIMIT}, $conf{CLIENTS_USAGE}, $psa_clients_usage);
			$do_exit = 1;
		}
	}

	if (exists($conf{DOMAINS_LIMIT}) && ($conf{DOMAINS_LIMIT} > 0)) {
		$query = dbDoQuery('SELECT COUNT(*) FROM domains');
		my ($psa_domains_usage) = $query->fetchrow_array();

		if ($conf{DOMAINS_LIMIT} < ($psa_domains_usage + $conf{DOMAINS_USAGE} + 0)) {
			print_stdout("\n");
			printf_stderr("Error: key limit 'domains' is %s, but required for restore: %s (in addition to %s already exists in Plesk)\n",
						  $conf{DOMAINS_LIMIT}, $conf{DOMAINS_USAGE}, $psa_domains_usage);
			$do_exit = 1;
		}
	}

	if (exists($conf{MAILNAMES_LIMIT}) && ($conf{MAILNAMES_LIMIT} > 0)) {
		$query = dbDoQuery('SELECT COUNT(*) FROM mail');
		my ($psa_mailnames_usage) = $query->fetchrow_array();

		if ($conf{MAILNAMES_LIMIT} < ($psa_mailnames_usage + $conf{MAILNAMES_USAGE} + 0)) {
			print_stdout("\n");
			printf_stderr("Error: key limit 'mailnames' is %s, but required for restore: %s (in addition to %s already exists in Plesk)\n",
				      $conf{MAILNAMES_LIMIT}, $conf{MAILNAMES_USAGE}, $psa_mailnames_usage);
			$do_exit = 1;
		}
	}

	if (exists($conf{WEBUSERS_LIMIT}) && ($conf{WEBUSERS_LIMIT} > 0)) {
		$query = dbDoQuery('SELECT COUNT(*) FROM web_users');
		my ($psa_webusers_usage) = $query->fetchrow_array();

		if ($conf{WEBUSERS_LIMIT} < ($psa_webusers_usage + $conf{WEBUSERS_USAGE} + 0)) {
			print_stdout("\n");
			printf_stderr("Error: key limit 'web users' is %s, but required for restore: %s (in addition to %s already exists in Plesk)\n",
				      $conf{WEBUSERS_LIMIT}, $conf{WEBUSERS_USAGE}, $psa_webusers_usage);
			$do_exit = 1;
		}
	}

	if ($do_exit) {
		print_stdout("Restoration is impossible: some key limits exceed.\n");
		$flags{'KEY_LIMITS_EXCEED'} = 1;
	} else {
		print_stdout("done\n");
	}
}

if ($fpRequired && ($conf{DISTRIB_TYPE} == RPM)) {
	unless ($conf{'FRONTPAGE_IS_INSTALLED'}) {
		die_log 'Unable to restoration process begin: frontpage rpm required, '
			.'because some domains have a frontpage support.' . "\n";
	}
}

# CHECK mode processing for certificates
if (my ($node) = $psaNode->xql('./server/certificates_list')) {
	restoreCertificates($node);
}

# CHECK mode processing for ip addresses
if (my ($node) = $psaNode->xql('./server/ip_addresses_list')) {
	restoreIPAddresses($node);
}


### IF TEST MODE THEN EXIT
if ($flags{testMode}) {
	if (!$conf{PG_IS_RUNNING} && $conf{'PG_DB_IS_EXIST_IN_DUMP'}) {
		print_stderr("WARNING: Postgresql is not running now. All the postgres databases will not be restored.\n\n");
	}

	print_stdout ("\nChecking has been finished.\n");
	&unlock_and_exit(0);
}



########################################################
#
#	Restore Plesk key during server-wide restore 
#	if Plesk is not configured. If Plesk is configured
#	key is restoring to psa.key.restored.
#
########################################################
if (!$single_domain_mode && !$conf{'RESTORING_BY_CLIENTS_LIST'} && !$conf{'RESTORING_BY_DOMAINS_LIST'}) {

	printf_stdout("Restore Plesk key file ... ");

	my $key_str_buff = '';
	if (my ($node) = $psaNode->xql('./server/psa_key')) {
		if (my $text_node = $node->getFirstChild()) {
			$key_str_buff = $text_node->toString();
		} else {
			printf_stderr("unable to fetch key value from xml node.\n");
		}
	} else {
#		printf_stderr("unable to find key node in xml.\n");
	}

	if ($key_str_buff) {
		my $key_file = '';
		if ($flags{psaIsConfigured}) {
			$key_file = $const{'PSA_KEY'} . '.restored';
		} else {
			$key_file = $const{'PSA_KEY'};
			system(sprintf("cp -f %s %s.old", $key_file, $key_file));
			system(sprintf('chown %s:%s %s.old', $const{ADM_USER}, $const{ADM_GROUP}, $key_file));
			chmod (0400, shellArgQuote("$key_file.old"));
		}

		unless (open (fd, ">$key_file")) {
			printf_stderr("Unable to open '%s' file for writing: %s\n", $key_file, $!);
			&unlock_and_exit(1);
		}
		binmode fd;
		my $buff = decode_base64($key_str_buff);
		unless (syswrite(fd, $buff, $const{'PSA_KEY_BUFF_SIZE'})) {
			printf_stderr("An error occured during writing '%s' file: %s\n", $key_file, $!);
		}
		if (close fd) {
			printf_stdout("done\n");
		} else {
			printf_stderr("fail: %s\n", $!);
		}

		printf_stdout("Restored Plesk key is stored in file '%s' because Plesk is %s configured.\n",
					  $key_file, $flags{psaIsConfigured} ? 'already' : 'not');

		if ($flags{psaIsConfigured}) {
			printf_stdout("If you want to use restored key you should copy '%s' to '%s' and restart Plesk.\n",
						  $key_file, $const{'PSA_KEY'});
		} else {
			printf_stdout("Old Plesk key is stored in file '%s'\n", $key_file . '.old');
		}
	} else {
		printf_stderr("key is empty.\n");
	}
}

### exit here for key saving
die "\n" if $flags{'KEY_LIMITS_EXCEED'};

unless ($single_domain_mode) {
	print_stdout ($const{LINE});

	print_stdout ("Now all the PSA services will be stopped.\n\n");
	system($conf{PSA_SCRIPT_STOPALL});
	wait_for_mysql_shutdown();

	print_stdout ($const{LINE});

	print_stdout ("Now the MySQL PSA service will be started.\n");
	system($conf{MYSQL_SCRIPT} . ' start');
	sleep 3;

	print_stdout ("\nAttempt to reconnect to MySQL server ... ");
	unless (dbConnect($const{PSA_ADMIN_NAME}, $conf{ADMIN_PASSWD}, $const{PSA_DB_NAME}, 'mysql')) {
		print_stderr("ERROR: unable to connect to PSA database.\n");
		die "\n";
	}
	print_stdout ("done\n");

	#
	# If PostgreSQL is installed - check that it's running
	#
	if ($conf{PGSQL_IS_INSTALLED} && $conf{PG_IS_RUNNING} && $conf{'PG_DB_IS_EXIST_IN_DUMP'}) {
		#### if PG is installed and was started before stopping all PSA services
		print_stdout ($const{LINE});

		print_stderr ("Now the Postgres PSA service will be started.\n");
		system($conf{PGSQL_SCRIPT} . ' start 1>&2');
		sleep 3;

		print_stderr ("\nCheck Postgres server running ... ");
		$conf{PG_IS_RUNNING} = checkPostgresRunning();
		unless ($conf{PG_IS_RUNNING}) {
			print_stderr("Error starting Postgres PSA service.\n");
		} else {
			print_stderr ("done\n");

			initPostgres($psaNode);
			print_stdout ("\nAttempt to reconnect to Postgres server ... ");
			if (checkPostgresConnecting()) {
				print_stdout ("done\n");
			} else {
				printf_stderr("Error: can't connect to postgres server: with login '%s' and password '%s'.\n", $conf{'postgresql_admin_login'}, $conf{'postgresql_admin_passwd'});
				$conf{PG_IS_RUNNING} = 0;
			}
		}
	} else {
		# Mark it as not running anyway
		unless ($conf{PGSQL_IS_INSTALLED}) {
			print_stderr("Postgres is not installed.\n");
		}
		$conf{PG_IS_RUNNING} = 0;
	}
}

if (!$conf{PG_IS_RUNNING} && $conf{'PG_DB_IS_EXIST_IN_DUMP'}) {
	printf_stderr("All postgres databases will not be restored: Postgres PSA service is not working properly.\n");
}

print_stdout ($const{LINE});

if ($useDumpedVhostsDir && ($conf{HTTPD_VHOSTS_D} ne $conf{OLD_HTTPD_VHOSTS_D})) {
	system('mkdir -p ' . dbQuote($conf{HTTPD_VHOSTS_D})) unless ( -d $conf{HTTPD_VHOSTS_D} );
	if (system('tar -cf - -C ' . shellArgQuote($conf{OLD_HTTPD_VHOSTS_D}) . ' . | tar -xf - --same-permissions -C ' . shellArgQuote($conf{HTTPD_VHOSTS_D})) != 0) {
		print_stderr("An error occured during copying the vhosts directories.\n");
		&unlock_and_exit(1);
	}

	if (-d $conf{HTTPD_VHOSTS_D}) {
		system('chown root:0 ' . shellArgQuote($conf{HTTPD_VHOSTS_D}));
		chmod (0755, shellArgQuote($conf{HTTPD_VHOSTS_D}));
		system('rm -rf ' . shellArgQuote($conf{OLD_HTTPD_VHOSTS_D}) . '>/dev/null 2>&1');
		save_conf();
	}
}

print_stdout ("The content restoration from backup archive is in process ...\n\n");
while ($arc->readPartHeader()) {
	if ($arc->error()) {
		printf_stderr("An error occured during part header reading: %s\n", $arc->getErrorMsg());
		next;
	}

	my $content_type = $arc->getPartHeaderField('Content-Type');
	if ($content_type =~ m/x-fake-node/) {
		$arc->skipPartContent();
		next;
	}

	my $mimeId = $arc->getPartHeaderField('Content-Id');
	unless ($mimeId) {
		print_stderr("ERROR: MIME Content-Id is invalid.\n");
		$arc->skipPartContent();
		next;
	}

	print_stdout ("Content-Id of current part: '$mimeId'\n") if ($debug > 0);

	my $objectNode = '';
	if (exists($globIndex{$mimeId})) {
		$objectNode = $globIndex{$mimeId};
#	} elsif (($objectNode) = $psaNode->xql('./server/certificates_list/certificate/*[@src="cid:' . $mimeId . '"]')) {
#	} elsif (($objectNode) = $psaNode->xql('./server/certificates_list/certificate/*[@src="cid:' . $mimeId . '" $and$ ((@restore = "true") $or$ @repository)]')) {
			# it is private/public key of restorable certificate 
	} elsif (!$single_domain_mode && !$conf{'RESTORING_BY_DOMAINS_LIST'} && !$conf{'RESTORING_BY_CLIENTS_LIST'}) {
		if (($objectNode) = $psaNode->xql('./server/crontab[@src="cid:' . $mimeId . '"]')) {
			# it is system (not PSA) user crontab
		}
	}

	unless ($objectNode) {
		printf_stderr("Unable to locate configuration info for the object with mime Content-Id: $mimeId\n") if $debug > 0;
#		if ($flags{'FORCE'}) {
			$arc->skipPartContent();
			next;
#		} else {
#			die "\n";
#		}
	}

#	if ($conf{'RESTORING_BY_DOMAINS_LIST'} || $conf{'RESTORING_BY_CLIENTS_LIST'}) {
#		if ($objectNode->getAttribute('restore_by_list') ne 'true') {
#			my $parentNode = $objectNode->getParentNode();
#			if (!$parentNode || ($parentNode->getAttribute('restore') ne 'true')) {
#				$arc->skipPartContent();
#				next;
#			}
#		}
#	}


	my $skip = ($objectNode->getAttribute('collision') eq 'true')
				|| ($objectNode->getAttribute('restore') eq 'false')
				|| ($objectNode->getAttribute('content_restore') eq 'false');

	if ($skip) {
		$arc->skipPartContent();
		next;
	}

	unless (preRestoreObject($objectNode)) {
		print_stderr("Error occured during preparation for the object restoring.\n");
		$arc->skipPartContent();
		next;
	}

	print_stdout ("Content-Type of current part: '$content_type'\n") if ($debug > 0);

	my $zip_cmd = ($arc->getPartHeaderField('Content-Encoding') eq 'gzip') ? $const{zip}.' -d -c - 2>/dev/null |' : '';

	if ($content_type =~ m/\/x-dir/) {
		if (!$conf{'MAILMAN_IS_INSTALLED'} && ($objectNode->getParentNode()->getNodeName() eq 'mail_list')) {
			printf_stdout("skipped\n");
			$arc->skipPartContent();
			next;
		}

		my $dest_dir = $objectNode->getAttribute('destdir');
		unless ($dest_dir) {
			printf_stderr("Unable to retrieve necessary info on the destination directory location for the content restoring for object '%s'\n", $objectNode->getNodeName());
			if ($objectNode->getAttribute('name')) {
				printf_stderr("Object name: %s\n", $objectNode->getAttribute('name'));
			}
			$arc->skipPartContent();
			next;
		}

		if (($objectNode->getNodeName() eq 'vhost_skeleton')) {
			if (-d $dest_dir) {
				printf_stdout("Removing old skeleton $dest_dir ... ");
				system("rm -rf $dest_dir");
				printf_stdout("done\n");
			}
		}

		my $what_restored = 'directory';
		if ($objectNode->getNodeName() eq 'domain') {
			$what_restored = 'hosting user data';
		} elsif ($objectNode->getNodeName() eq 'system_data') {
			$what_restored = 'hosting system data';
		} elsif (($objectNode->getNodeName() eq 'vhost_skeleton') && ($objectNode->getAttribute('admin') ne 'true')) {
			$what_restored = 'client skeleton in temporary directory';
		}
		printf_stdout("Restoring $what_restored: %s/%s ...", $dest_dir, $objectNode->getAttribute('dir'));

		unless (-d $dest_dir) {
			if (system("mkdir -p $dest_dir") != 0) {
				printf_stderr("Unable to create directory '%s'\n", $dest_dir);
				$arc->skipPartContent();
				next;
			}
		}

		open OUT, "|$zip_cmd".$const{tar}." -xf - --same-permissions --directory=$dest_dir >/dev/null 2>&1" || warn_log "Unable to open tar stream: ";
		$arc->readPartContent(\*OUT);
		close OUT;

	} elsif ($content_type =~ m/\/x-mysql-dump/) {
		my $dbName = u2a($objectNode->getAttribute('name'));

		if ($dbName eq 'test\_%') {
			printf_stderr("Mysql database %s restoration has been skipped: it is a test abstract database.\n", $dbName);
			$arc->skipPartContent();
			next;
		}

		if ($dbName eq 'horde') {
			if (-d $conf{MYSQL_VAR_D} . '/horde') {
				printf_stdout("Dropping mysql database '%s' ... ", $dbName);
				unless (dbDoQuery('drop database `' . $dbName . '`')) {
					printf_stderr("Unable to drop mysql database '%s'.\n", $dbName);
					$arc->skipPartContent();
					next;
				}
				print_stdout("done\n");
			} else {
				print_stderr("IMP Horde database content restoration is skipped, because 'horde' database is not exists.");
				$arc->skipPartContent();
				next;
			}
		} else {
			if (existDir($conf{MYSQL_VAR_D}."/$dbName")) {
				printf_stderr("Mysql database '%s' restoration has been skipped: database already exists.\n", $dbName);
				$arc->skipPartContent();
				next;
			}
		}

		printf_stdout("Restoring mysql database '%s' ...", $dbName);

		unless (dbDoQuery('create database `' . $dbName . '`')) {
			printf_stderr("Unable to create mysql database '%s'\n", $dbName);
			$arc->skipPartContent();
			next;
		}

		open OUT, "|$zip_cmd".$conf{MYSQL_BIN_D}.'/mysql -uadmin -p' . shellArgQuote($conf{ADMIN_PASSWD}) . " $dbName" || warn_log "%!\n";
		$arc->readPartContent(\*OUT);
		close OUT;
	} elsif ($content_type =~ m/\/x-pgsql-dump/) {

		my $dbName = u2a($objectNode->getAttribute('name'));

		if ($dbName eq 'test\_%') {
			warn_log "Postgres database %s restoration has been skipped: it is a test abstract database.\n", "$dbName\n";
			$arc->skipPartContent();
			next;
		}

		unless ($conf{PG_IS_RUNNING}) {
			warn_log "Postgres database %s restoration has been skipped: postgres service is not running now.\n", "$dbName\n";
			$arc->skipPartContent();
			next;
		}

		my $res = checkPostgresDBExisting($dbName);
		if ($res < 0) {
			printf_stderr("Postgres database %s restoration has been skipped: error checking database existing.\n", $dbName);
			$arc->skipPartContent();
			next;
		}
		if ($res > 0) {
			printf_stderr("Postgres database %s restoration has been skipped: database already exists.\n", $dbName);
			$arc->skipPartContent();
			next;
		}

		printf_stdout("Restoring postgres database %s ...", $dbName);

		system('umask 0022; ' . $conf{UTILS_DIR} . '/pg_manage rm_db ' . shellArgQuote($dbName) . ' 2>&1 >/dev/null');
		system('umask 0022; ' . $conf{UTILS_DIR} . '/pg_manage add_db ' . shellArgQuote($dbName) . ' 2>&1 >/dev/null');

		my $buff = sprintf('PGPASSWORD=%s %s/psql -U%s -tc "CREATE DATABASE \\"%s\\"" template1 2>&1 >/dev/null',
				   shellArgQuote($conf{'postgresql_admin_passwd'}), $conf{PGSQL_BIN_D},
				   shellArgQuote($conf{'postgresql_admin_login'}), $dbName);
		if (system($buff) != 0) {
		    printf_stderr("Unable to create postgres database %s\n", $dbName);
		    $arc->skipPartContent();
		    next;
		}

		##################################################################
		#
		#	Database created. Now it's time to 'unpack' all the database
		#	users because it's impossible to correctly restore database
		#	content without already existing database users.
		#
		##################################################################

		sub restart_postmaster {

			my $postmaster_pid_path = $conf{PGSQL_DATA_D} . '/postmaster.pid';

			if (not open(POSTMASTER_PID, "< $postmaster_pid_path")) {
				return 1;
			}

			################################################
			#	Postmaster PID is placed in the first line
			################################################
			my $postmaster_pid = <POSTMASTER_PID>;
			$postmaster_pid += 0;

			close(POSTMASTER_PID);

			return system("kill -HUP $postmaster_pid")
		}

		sub open_and_lock_pg_hba {

			my $pg_hba_conf_path = $conf{PGSQL_DATA_D} . '/pg_hba.conf';

			unless (open(PG_HBA_CONF, "+< $pg_hba_conf_path")) {
				return '';
			}

			#####################################
			#	LOCK_EX = 2
			#	LOCK_NB = 4
			#####################################
			unless (flock(PG_HBA_CONF, 2|4)) {

			printf_stderr("Can't lock file %s - it's locked by other process.\n".
				"Will wait for freeing the lock...\n", $pg_hba_conf_path);

				unless (flock(PG_HBA_CONF, 2)) {
					printf_stderr("Can't open file - impossible to lock file\n");
					return '';
				}
			}
			return \*PG_HBA_CONF;
		}

		###################################################################
		#	Get all the Plesk database users (for given database) without
		#	collisions (collising users are not restored at all).
		###################################################################
		foreach my $userNode ($objectNode->xql('./user[@type="db_users"]')) {

			##########################
			#	Check for collision.
			##########################
			next if ($userNode->getAttribute('collision') eq 'true');

			my ($loginNode) = $userNode->xql('./login');

			########################################
			#	Check for existence and collision.
			########################################
			next if ((not $loginNode) or ($loginNode->getAttribute('collision') eq 'true'));

			my $login    = $loginNode->getAttribute('name');
			my $password = u2a($loginNode->getAttribute('password'));
			my $database = $dbName;

			###########################################
			#	Check for login/password correctness?
			###########################################
			next if (not $login or not $password);

			################################################
			#	Restore users of this database... 
			#	Yeap, directly to postgres...
			#	Following code inspired by 
			#	BU::PSA::Objects::DatabaseUser::doCreate()
			################################################

			#############################################
			#	Add record to access configuration file
			#############################################
			my $buff = sprintf(
				"umask 0022; %s/pg_manage add_db_usr %s %s >/dev/null 2>&1",
				$conf{'UTILS_DIR'}, shellArgQuote($database), shellArgQuote($login));

			print_stderr("SYSTEM EXEC:($buff)\n") if ($debug > 1);

			if (system($buff) != 0) {
				printf_stderr("Unable to add database user: error occured during pg_manage executing: %s\n", util_exec_errormsg());
				next;
			}

			##############################################################
			#	Postgres administrator should create given postgres user
			##############################################################
			my $query = sprintf(
				"CREATE USER \\\"%s\\\" WITH PASSWORD %s NOCREATEDB NOCREATEUSER",
				$login, dbQuote($password));

			$buff = sprintf(
				"umask 0022; export PGPASSWORD=%s; %s/psql -U%s -tc \"%s\" template1 2>&1 >/dev/null",
				shellArgQuote($conf{'postgresql_admin_passwd' }), $conf{PGSQL_BIN_D}, 
				shellArgQuote($conf{'postgresql_admin_login'}), $query);

			print_stderr("SYSTEM EXEC:($buff)\n") if ($debug > 1);

			######################################################################
			#	Actually, this operation shouldn't fail as user has no collision
			#	So, if user see following error message - smth. goes wrong.
			######################################################################
			if (system($buff) != 0) {
				print_stderr("Unable to add database user: error occured during psql executing.\n");
				next;
			}
		}

		#################################################################
		#	Next - we should provide passwordless access for all 
		#	those just-added users, as pg_dump uses \connect commandset
		#	not providing any authentification mechanism.
		#################################################################
		my $pg_hba_fh = open_and_lock_pg_hba();

		if (not $pg_hba_fh) {
			printf_stderr("Database %s restoration has been skiped\n", $dbName);
			$arc->skipPartContent();
			next;
		}

		##################################################
		#	Add wildcard access to all databases
		#	Place it after all comments skiped 
		#	(firs element - hack for pg_manage - it adds
		#	records after all comments skiped)
		##################################################
		my @pg_hba_conf_add = (
			"\n",
			"# Following 2 lines are inserted by Plesk BU. If you see it after psarestore completes - DELETE IT and restart postgres\n",
			$conf{'pg_access_string1'},	# it depends on Postgres version
			$conf{'pg_access_string2'}	# it depends on Postgres version
		);

		my @pg_hba_conf;

		my $added = 0;

		while (my $line = <$pg_hba_fh>) {

			if (not $added and $line =~ m/^\s*[^\#]/) {

				##############################
				#	Found non-comment line
				#	Push additional lines...
				##############################
				push(@pg_hba_conf, @pg_hba_conf_add);
				$added = 1;
			}

			###############
			#	push line
			###############
			push(@pg_hba_conf, ($line));
		}

		if (not $added) {
			push(@pg_hba_conf, @pg_hba_conf_add);
		}

		###########################
		#	Save new file content
		###########################
		seek($pg_hba_fh, 0, 0);
		print $pg_hba_fh @pg_hba_conf;
		truncate($pg_hba_fh, tell($pg_hba_fh));

		close($pg_hba_fh);

		###############################################
		#	Restart postmaster to changes take effect
		###############################################
		if (restart_postmaster()) {
			sprintf("Can't restart postmaster: tables of database %s would not be restored!", $dbName);
		}

		####################################################################
		#	There is other problem with postgres database restoration.
		#	To restore database content database should already exists and
		#	has a user with proper access rights. So we add postgres
		#	administrator to users of this database.
		####################################################################
		if (system (sprintf("umask 0022 ; %s/pg_manage add_db_usr %s %s > /dev/null 2>&1",
			$conf{UTILS_DIR}, shellArgQuote($dbName), shellArgQuote($conf{'postgresql_admin_login'}))) != 0) {

			###############################
			#	Can't add administrator
			###############################
			printf_stderr("Can't add postgres administrator to database '%s' users\n", $dbName);
			$arc->skipPartContent();
			next;
		}

		##################################################################
		#	And finally - content restoration...
		#	During database restoration postgres will try to reconnect 
		#	under login of right user to create tables, which user owns.
		#	(Bug #17819)
		##################################################################

		my $command = sprintf("|$zip_cmd PGPASSWORD=%s %s/pg_restore -Fc -U%s -d %s > /dev/null 2>&1",
			shellArgQuote($conf{'postgresql_admin_passwd' }), $conf{PGSQL_BIN_D}, 
			shellArgQuote($conf{'postgresql_admin_login'}), shellArgQuote($dbName));
		open(OUT, $command) || warn_log "%!\n";

		$arc->readPartContent(\*OUT);
		close OUT;

		#######################################################
		#	Remove postgres administrator from database users
		#######################################################
		if (system(sprintf("umask 0022 ; %s/pg_manage rm_db_usr %s %s > /dev/null 2>&1",
			$conf{UTILS_DIR}, shellArgQuote($dbName), shellArgQuote($conf{'postgresql_admin_login'}))) != 0) {

			#########################################
			#	Can't remove postgres administrator
			#########################################
			printf_stderr("Can't remove postgres administrator from users of database '%s'\n", $dbName);
		}

		#########################################
		#	Remove wildcard access to databases
		#########################################
		{
		my $pg_hba_fh = open_and_lock_pg_hba();

		if (not $pg_hba_fh) {
			printf_stderr("Can't remove wildcard access from postgres databases. You should manually edit pg_hba.conf\n");
			next;
		}
		#############################################
		#	Remove 3 lines, which were added before
		#############################################
		my @pg_hba_conf_del = (
			"# Following 2 lines are inserted by Plesk BU. If you see it after psarestore completes - DELETE IT and restart postgres\n",
			$conf{'pg_access_string1'},	# it depends on Postgres version
			$conf{'pg_access_string2'}	# it depends on Postgres version
		);

		my @pg_hba_conf;

		while (my $line = <$pg_hba_fh>) {

			if ($line eq $pg_hba_conf_del[0]) {

				#########################################################
				#	Ok, found. Should delete this and following 2 lines
				#########################################################
				$line = <$pg_hba_fh>;
				if ($line ne $pg_hba_conf_del[1]) {
					push(@pg_hba_conf, ($line));
				}

				$line = <$pg_hba_fh>;
				if ($line ne $pg_hba_conf_del[2]){
					push(@pg_hba_conf, ($line));
				}
			} else {

				push(@pg_hba_conf, ($line));
			}
		}

		###########################
		#	Save new file content
		###########################
		seek($pg_hba_fh, 0, 0);
		print $pg_hba_fh @pg_hba_conf;
		truncate($pg_hba_fh, tell($pg_hba_fh));

		close($pg_hba_fh);

		###############################################
		#	Restart postmaster to changes take effect
		###############################################
		if (restart_postmaster()) {
			printf_stderr("Can't restart postmaster - it will be restarted later");
		}
		}

		###############################################################
		#	Footnote:
		#	Because we in fact just made database user restoration, 
		#	BU::PSA::Objects::DatabaseUser::doCreate() shouldn't fail 
		#	when encounter existing user during it's restoration.
		###############################################################

	} elsif ($content_type =~ m/\/x-certificate-key/) {
		my $name = '';
		my $parentNode = $objectNode->getParentNode();
		unless ($parentNode) {
			print_stderr("Error: unable to define certificate node for key node\n");
			$arc->skipPartContent();
			next;
		}

		my $name = $parentNode->getAttribute('name');
		my $key_name = $objectNode->getNodeName();
		my $fileNode = undef;
		if ($key_name eq 'pub_key') {
			$fileNode = $parentNode;
			printf_stdout("Saving certificate %s public key ...", $name);
		} elsif ($key_name eq 'pvt_key') {
			$fileNode = $parentNode;
			printf_stdout("Saving certificate %s private key ...", $name);
		} elsif ($key_name eq 'ca_cert') {
			$fileNode = $objectNode;
			printf_stdout("Saving CA for %s cetificate ...", $name);
		} elsif ($key_name eq 'csr') {
			$fileNode = $parentNode;
			printf_stdout("Saving CSR for %s certificate ...", $name);
		} else {
			print_stderr("Found unknown certificate content : '$name'\n");
			$arc->skipPartContent();
			next;
		}

		my $cert_file = $fileNode->getAttribute('cert_file');
		unless ($cert_file) {
			my $tmp = 10;
			do {
				$cert_file = randFileName('cert');
				$dest_file = $conf{CERTIFICATES_D} . '/' . $cert_file;
			} while ((-e $dest_file) && $tmp--);
			if ($tmp <= 0) {
				print_stderr("Error: unable to generate unique name for certificate file\n");
				$arc->skipPartContent();
				next;
			}
		}
		$dest_file = $conf{CERTIFICATES_D} . '/' . $cert_file;

		$fileNode->setAttribute('cert_file', $cert_file);

		$key = $arc->getPartContent();

		open OUT, ">>$dest_file" || warn_log "Unable to open file %s: %!\n", "$cert_file\n";
		print OUT url_decode($key);
		close OUT;
	} elsif ($content_type =~ m/\/x-psa-key/) {
#		print_stdout("Unpacking psa key ...");
#		$objectNode->appendChild($doc->createTextNode(encode_base64($arc->getPartContent())));
		$arc->skipPartContent();

	} elsif ($content_type =~ m/\/x-crontab/) {

		############################
		# unpacking users crontabs
		############################

		# server-wide crontabs contains attribute "user"
		my $user = u2a($objectNode->getAttribute('user'));

		# user-level crontabs should be subnode of "user" node
		if (not $user) {

			my ($loginNode) = $objectNode->getParentNode->xql('./login');

			# can't determine login of the user - skip crontab restoration
			if (not $loginNode) {
				print_stderr("Can't get system user name to restore crontab\n".
						"\tsrc = '" . $objectNode->getAttribute('src') . "'\n");
				$arc->skipPartContent();
				next;
			}

			$user = u2a($loginNode->getAttribute('name'));

			# can't determine login of the user - skip crontab restoration
			if (not $user) {
				print_stderr("Can't get system user name to restore crontab\n".
						"\tsrc = '" . $objectNode->getAttribute('src') . "'\n");
				$arc->skipPartContent();
				next;
			}
		}

		# check system user existing
		unless (util_exec('usermng', ('exist', $user))) {
			$arc->skipPartContent();
			next;
		}

		print_stdout("Restoring crontab for system user '$user' ...");

		# unpacking crontab to temporary file
		my $tmpName = tmpnam();

		if (not open OUT, ">$tmpName") {
			print_stderr("Can't restore crontab for user '$user': can't open temporary file '$tmpName'\n");
			$arc->skipPartContent();
			next;
		}
		$arc->readPartContent(\*OUT);
		close OUT;

		# register user crontab in PSA
		if (system($conf{UTILS_DIR}."/crontabmng set $user $tmpName 2>&1 > /dev/null")) {
			print_stderr("Can't restore crontab for user '$user': error calling crontabmng\n");
			unlink($tmpName);
			next;
		}

		# remove temporary file
		unlink($tmpName);

	} else {
		if (($objectNode->getNodeName() eq 'web_app') && !$conf{'TOMCAT_IS_INSTALLED'}) {
			printf_stdout("Restoring webappication %s is skipped\n", u2a($objectNode->getAttribute('name')));
			$arc->skipPartContent();
			next;
		}

		my $dest_file = $objectNode->getAttribute('destfile');
		unless ($dest_file) {
			printf_stderr("Unable to restore object with MIME Content-Id '%s' (object xml tag: '%s', name: '%s')\n",
							$mimeId, $objectNode->getNodeName(), $objectNode->getAttribute('name'));
			$arc->skipPartContent();
			next;
		}

		printf_stdout("Restoring file %s ...", $dest_file);

#		if (($objectNode->getNodeName() eq 'web_app') && !$conf{'TOMCAT_IS_INSTALLED'}) {
#			printf_stdout("skipped\n");
#			$arc->skipPartContent();
#			next;
#		}

		# create directory if it does not exists
		if ($dest_file =~ m/^(.*)\/[^\/]+$/) {
			unless (existDir($1)) {
				system("mkdir -p $1 >/dev/null 2>&1");
			}
		}

		open OUT, ">$dest_file" || warn_log "Unable to open file %s: %!\n", "$dest_file\n";
		$arc->readPartContent(\*OUT);
		close OUT;
	}

	if ($arc->error()) {
		warn_log "ERROR: %s\n", $arc->getErrorMsg()."\n";
	}

	print_stdout (" done\n");
}
if ($arc->error()) {
	printf_stderr("An error occured during part header reading: %s\n", $arc->getErrorMsg());
}
#print_stdout ("done\n");

print_stdout ($const{LINE});

print_stdout ("Checking and fixing all restored data.\n\n");
$flags{RESTORE_MODE} = 'RESTORE';
$conf{'CLIENTS_PROCESSED_NUMBER'} = 0;
$conf{'DOMAINS_PROCESSED_NUMBER'} = 0;
$ProcessName = 'Check and fix the';

# RESTORE mode processing for certificates
if (my ($node) = $psaNode->xql('./server/certificates_list')) {
	restoreCertificates($node);
}

# RESTORE mode processing for ip addresses
if (my ($node) = $psaNode->xql('./server/ip_addresses_list')) {
	restoreIPAddresses($node);
}


if ($single_domain_mode) {
	my $query = dbDoQuery('SELECT id FROM clients WHERE login=' . dbQuote($single_mode_client_login));
	my ($clientId) = $query->fetchrow_array();

	unless ($clientId) {
		print_stderr("ERROR: client with login '$single_mode_client_login' is not exist.\n");
		&unlock_and_exit(1);
	}

	unless ($single_domain_ip_id) {
		printf_stderr("Unable to define id for '%s' domain ip address\n", $single_mode_domain_ip);
		&unlock_and_exit(1);
	}

	if (my ($base_node) = $psaNode->xql('./sites/domain')) {
		$base_node->setAttribute('name', $single_mode_domain_name);
		restoreDomain($clientId, $single_mode_client_login, $base_node, $single_domain_mode, $single_domain_ip_id);
	}
} elsif ($opt_assign_domains_to) {
	foreach $domainNode ($psaNode->xql('./user[@type = "client"]/sites/domain[@restore_by_list = "true"]')) {
		restoreDomain($assign_domains_to_client_id, $opt_assign_domains_to, $domainNode);
	}
} else {
	restoreConfig();
}

print_stdout ($const{LINE});

if ($single_domain_mode) {
#	print_stdout ("Rebuilding apache configuration file ... ");

#	util_exec('websrvmng', '--reconfigure-vhost', '--vhost-name=' . $signle_mode_domain_name);

#	print_stdout("done (it will be applyed in next apache restart)\n\n");
	print_stdout("Single domain restoration is successfully completed\n");

	&unlock_and_exit(0);
} else {

	#################################################################
	# Following code disabled due to the bug #17857 in mchk utility
	# It can be safely enabled if you run Plesk 6.0.3 or above...
	# However, it's working without it...
	#################################################################
	#print_stdout ("Checking and fixing mailnames configuration ... ");
	#unless (util_exec('mchk')) {
	#	warn_log "%.An error occured during checking and fixing mailnames configuration.\n";
	#} else {
	#	print_stdout ("done\n");
	#}
	#################################################################

	print_stdout ("Rebuilding apache configuration file ... ");
	unless (util_exec('websrvmng', '--reconfigure-all', '--no-daemon')) {
		printf_stderr("An error occured during rebuilding apache configuration file.\n", util_exec_errormsg());
	} else {
		print_stdout ("done\n");
	}
}

unless ($single_domain_mode) {
	print_stdout ($const{LINE});

	if ($conf{PG_IS_RUNNING}) {
		print_stdout ("Now the Postgres PSA service will be stopped.\n");
		system ($conf{PGSQL_SCRIPT} . ' stop');
		sleep 3;
	}

	print_stdout ($const{LINE});

	print_stdout ("Now the MySQL PSA service will be stopped.\n");
	system ($conf{MYSQL_SCRIPT} . ' stop');
	wait_for_mysql_shutdown();

	print_stdout ($const{LINE});

	print_stdout ("Now all the PSA services will be started:\n\n");
	system ($conf{PSA_SCRIPT_STARTALL});

}

print_stdout ($const{LINE});

printf_stdout ("VHOSTS directory location is %s.\n", shellArgQuote($conf{HTTPD_VHOSTS_D}));
print_stdout ("\n");

print_stdout ("ATTENTION: PSA admin password remains unchanged.\n");
unless ($flags{psaIsConfigured}) {
	print_stdout ("ATTENTION: Do not forget to change the default PSA admin password for the better security.\n");
}

print_stdout ("\n");

#########################################
#  Print domains which weren't restored
#########################################
printNotRestoredDomains();

print_stdout("Server restoration is completed\n");
print EOF;

&unlock_and_exit(0);


###################      BEGIN SUBROUTINES BLOCK      ###################

sub preRestoreObject($)
{
	my ($objectNode) = @_;

	foreach my $systemUser ($objectNode->xql('.//login[@real="true"]')) {

		my $name = u2a($systemUser->getAttribute('name'));
		unless ($name) {
			print_stderr("Unable to check system user existantion: login is empty.\n");
			return undef;
		}

		unless (util_exec('usermng', ('exist', $name))) {
			if (util_exec_error()) {
				print_stderr("Unable to check system user '$name' existantion: " . util_exec_errormsg());
				die;
			}

			unless (util_exec ('usermng', ('add', $name, shellArgQuote(u2a($systemUser->getAttribute('home')))))) {
				printf_stderr("Unable to add a system user '%s':%s\n", shellArgQuote($name), util_exec_errormsg());
				return '';
			}

			my $crypt_passwd = 'x';

			if ($systemUser->getAttribute('PW_TYPE') eq 'plain') {
				$crypt_passwd = crypt(u2a($systemUser->getAttribute('password')), genSaltForCrypt());
			} elsif ($systemUser->getAttribute('PW_TYPE') eq 'crypt') {
				$crypt_passwd = u2a($systemUser->getAttribute('password'));
			}
			setPasswd($name, $crypt_passwd);

			if (($objectNode->getNodeName() eq 'user') && ($objectNode->getAttribute('type') eq 'hosting')) {
				my $domainName = $objectNode->getParentNode()->getAttribute('name');
				unless (-d $conf{'HTTPD_VHOSTS_D'} . "/$domainName/httpdocs") {
					unless (util_exec('vhostmng', '--install-vhost',
								'--vhost-name=' . shellArgQuote($domainName),
								'--user-name=' . shellArgQuote($name)))
					{
						printf_stderr("An error occured during vhostmng executing: %s\n", util_exec_errormsg());
					}
				}
			}

			if (($systemUser->getAttribute('shell') ne '') && ($systemUser->getAttribute('shell') !~ m/\/false$/)) {
				unless (util_exec ('usermng', ('shell', $name, shellArgQuote(u2a($systemUser->getAttribute('shell')))))) {
					printf_stderr("Unable to set shell access for system user '%s':%s\n", $name, util_exec_errormsg());
					return '';
				}			
			}
		}
	}

	return 1;
}


sub pretty_print($)
{
	my ($objName) = @_;

	for (my $i=0; $i < ($PRINTLEVEL * (length($SUBLEVELMARKER) + 1)); $i++) { print ' '; }

	print_stdout ($SUBLEVELMARKER, ' ', $objName, "\n");
}


# print current level
sub print_cl($)
{
	my ($objName) = @_;

	pretty_print($objName);
}


# print sub level
sub print_sl($)
{
	my ($objName) = @_;

	$PRINTLEVEL++;

	pretty_print($objName);
}


# print sub level
sub print_upl()
{
	if ($PRINTLEVEL > 0) {
		$PRINTLEVEL--;
	}
}


sub unlock_and_exit()
{
	my ($exit_code) = @_;

	if ($single_domain_mode) {
		unlock_domain($single_mode_domain_name, $lock_domain_struct);
	} else {
		unlock_bu_process();
	}

	exit $exit_code;
}


sub getShellsForMapping()
{
	my %shells_list = ();

	foreach my $node ($psaNode->xql('//user/login[(@real = "true") $and$ (@shell != "")]')) {
		$_ = $node->getAttribute('shell');
		next unless $_;

		if (m/^[ \t]*([^ \t]+\/([^\/ \t]+))[ \t]*$/) {
			$shells_list{$1} = $2;
		} else {
			$shells_list{$_} = $_;
		}
	}

	return \%shells_list;
}


sub doShellsMapping($ $)
{
	my ($shells_map, $shells_from_system) = @_;

	foreach my $node ($psaNode->xql('//user/login[(@real = "true") $and$ (@shell != "")]')) {
		my $shell = $node->getAttribute('shell');
#		next unless $shell;

		unless (exists($shells_map->{$shell})) {
			unless (exists($shells_from_system->{$shell})) {
				print_stderr("Shell '$shell' is absend in shells map file and does not exists in system.\n");
				return undef;
			}
			next;
		}

		$node->setAttribute('shell', $shells_map->{$shell});
	}

	return 1;
}


sub readClientsMapFile
{
	my ($clients_map_file) = @_;

	my %clients_logins_map = ();
	my %clients_pnames_map = ();

	unless ($clients_map_file) {
		printf_stderr("ERROR: clients map file is not defined.\n");
		return undef;
	}

	unless (open(fd, $clients_map_file)) {
		printf_stderr("ERROR: unable to open file '%s': %s\n", $clients_map_file, $!);
		return undef;
	}

	binmode fd;

	my %used_dst_clients_logins = ();
	my %used_dst_clients_pnames = ();

	my $got_login = undef;
	my $got_pname = undef;
	my $line = 0;

	while (<fd>) {
		$line++;
		chomp($_);
		chomp($_);
		chomp($_);
		my $line_source = $_;

		next if m/^\s*#/;
		s/#.*$//;
		next if m/^\s*$/;

		if (/^\s*-+\s*$/) {
			$got_login = undef;
			$got_pname = undef;
			next;
		}

		if ($got_login && $got_pname) {
			print_stderr("ERROR: delimiter expected in client map file on line $line:$line_source\n");
			close fd;
			return undef;
		}

		if (m/^\s*login\s*:\s*([^\s]+)\s*=>\s*([^\s]+)\s*$/) {
			if ($got_login) {
				print_stderr("ERROR: login definition found in client map file on line $line but personal name (pname) expected.\n");
				close fd;
				return undef;
			}

			my $src_login = $1;
			my $dst_login = $2;

			unless ($src_login =~ /^[a-zA-Z0-9][-A-Za-z0-9_.-]{0,19}$/) {
				print_stderr("ERROR: invalid source login in client map file on line $line '$src_login'.\n");
				close fd;
				return undef;
			}

			unless ($dst_login =~ /^[a-zA-Z0-9][-A-Za-z0-9_.-]{0,19}$/) {
				print_stderr("ERROR: invalid target login in client map file on line $line: '$dst_login'.\n");
				close fd;
				return undef;
			}

			if (exists($clients_logins_map{$src_login})) {
				print_stderr("ERROR: dublicate entry in client map file on line $line: '$src_login' login already mapped.\n");
				close fd;
				return undef;
			}

			if (exists($used_dst_clients_logins{$dst_login})) {
				print_stderr("ERROR: dublicate entry in client map file on line $line: '$dst_login' login already used in map file.\n");
				close fd;
				return undef;
			}

#			my $query = dbDoQuery('SELECT id FROM clients WHERE login=' . dbQuote($dst_login));
#			unless ($query) {
#				print_stderr("ERROR: unable to check existing of client with login '$dst_login' in Plesk.\n");
#				close fd;
#				return undef;
#			}
#
#			if ($query->fetchrow_array()) {
#				print_stderr("ERROR: target login '$dst_login' in client map file on line $line already used in Plesk.\n");
#				close fd;
#				return undef;
#			}

			$clients_logins_map{$src_login} = $dst_login;
			$used_dst_clients_logins{$dst_login} = 1;
			$got_login = 1;
			next;
		}

		if (my ($src_pname) = m/^\s*source\s*personal\s*name\s*:\s*(|[^\s]|[^\s].*[^\s])\s*$/) {

			while (<fd>) {$line++; chomp($_); last if $_;}
			my ($dst_pname) = m/^\s*target\s*personal\s*name\s*:\s*(|[^\s]|[^\s].*[^\s])\s*$/;

			if ($src_pname =~ /^\s*$/) {
				$got_pname = 1;
				next;
			}

			if ($src_pname && $dst_pname) {
				if ($got_pname) {
					print_stderr("ERROR: personal name (pname) definition found in client map file on line $line but login expected.\n");
					close fd;
					return undef;
				}

				unless ($src_pname =~ /^.{0,255}$/) {
					print_stderr("ERROR: invalid source login in client map file on line $line '$src_pname'.\n");
					close fd;
					return undef;
				}

				unless ($dst_pname =~ /^.{0,255}$/) {
					print_stderr("ERROR: invalid target login in client map file on line " . ($line + 1) . ": '$dst_pname'.\n");
					close fd;
					return undef;
				}

				if (exists($clients_pnames_map{$src_pname})) {
					print_stderr("ERROR: dublicate entry in client map file on line $line: '$src_pname' personal name (pname) already mapped.\n");
					close fd;
					return undef;
				}

				if (exists($used_dst_clients_pnames{$dst_pname})) {
					print_stderr("ERROR: dublicate entry in client map file on line " . ($line + 1) . ": '$dst_pname' personal name (pname) already used in map file.\n");
					close fd;
					return undef;
				}

#				my $query = dbDoQuery('SELECT id FROM clients WHERE pname=' . dbQuote($dst_pname));
#				unless ($query) {
#					print_stderr("ERROR: unable to check existing of client with personal name (pname) '$dst_pname' in PSA.\n");
#					close fd;
#					return undef;
#				}
#
#				if ($query->fetchrow_array()) {
#					print_stderr("ERROR: target personal name '$dst_pname' in client map file on line $line already used in PSA.\n");
#					close fd;
#					return undef;
#				}

				$clients_pnames_map{$src_pname} = $dst_pname;
				$used_dst_clients_pnames{$dst_pname} = 1;
				$got_pname = 1;
				next;
			}
		}

		print_stderr("ERROR: syntax error detected in clients map file on lines $line: $line_source\n");
		return undef;
	}

	if (!($got_login && $got_pname) && ($got_login || $got_pname)) {
		my $expected = $got_login ? 'personal name' : 'login';
		print_stderr("ERROR: '$expected' expected after line $line.\n");
		close fd;
		return undef;
	}

	close fd;

	return (\%clients_logins_map, \%clients_pnames_map);
}


sub writeClientsRejectList
{
	my ($clients_map_file) = @_;

	$clients_map_file = 'clients.rej' unless $clients_map_file;

	unless (-e $clients_map_file) {

		unless (open (fd, ">$clients_map_file")) {
			printf_stderr("ERROR: unable to open file '%s' for writing: %s.\n", $clients_map_file, $!);
			return '';
		}

		print fd "#The following clients presented in dump have a collision with some clients in PSA.\n";
		print fd "#Collision is detected for a client if he has login or personal name which coincides\n#login or personal name of some client in PSA.\n";
#		print fd "#If a line with login or personal name begins with '#' symbol then this line can be left unchanged.\n";
		print fd "#\n#\n#\n";

		foreach my $node ($psaNode->xql('./user[@type = "client" $and$ @collision = "true"]')) {
			my $login = undef;
			my $pname = undef;

			if (my ($pnameNode) = $node->xql('./card/pname')) {
				my $sn = $pnameNode->getFirstChild();
				$pname = $sn ? u2a($sn->toString()) : undef;
			}

			if (my ($loginNode) = $node->xql('./login')) {
				$login = u2a($loginNode->getAttribute('name'));
			}

			my $query = dbDoQuery('SELECT login, pname FROM clients WHERE login=' . dbQuote($login) . ($pname ? ' or pname=' . dbQuote($pname) : ''));
			unless ($query) {
				print_stderr("ERROR: unable to get client info from database.\n");
				close fd;
				die;
			}

			if (my ($login_in_psa, $pname_in_psa) = $query->fetchrow_array()) {
				print fd ("login:$login => ");
				print fd "$login" if ($login_in_psa ne $login);

				print fd ("\nsource personal name:$pname\ntarget personal name:");
				print fd $pname if ($pname_in_psa ne $pname);
				print fd "\n------------------------\n";
			}
		}

		close fd;

		printf_stdout ("List of rejected clients has been written into the file '%s'\n", $clients_map_file);
		print_stdout ("You should indicate logins and personal names for mapping by editing that file.\n");
		exit 1;
	} else {
		print_stderr("ERROR: clients collisions detected (clients mapping file clients.rej already exists).\n");
		print_stdout ("You should indicate logins and personal names for mapping by editing that file.\n");
		die "\n";
	}
}

sub wait_for_mysql_shutdown
{
        $delay = 5;
        do {
            $delay--;
            sleep $delay;
        } while ($delay > 0 && 0 == system('ps ax 2>/dev/null | grep mysqld_safe >/dev/null 2>&1'));
}

