# Functions for migrating a cpanel backup

# migration_cpanel_validate(file, domain, [user], [&parent], [prefix], [pass])
# Make sure the given file is a cPanel backup, and contains the domain
sub migration_cpanel_validate
{
my ($file, $dom, $user, $parent, $prefix, $pass) = @_;

# Password is needed for cPanel migrations
if (!$parent && !$pass) {
	return ("A password must be supplied for cPanel migrations");
	}

# Extract the backup and find paths inside it
my ($ok, $root) = &extract_cpanel_dir($file);
$ok || return ("Not a cPanel tar.gz file : $root");
my $daily = glob("$root/backup*/cpbackup/daily");
my ($homedir) = glob("$root/*/homedir");
$homedir = "$root/homedir" if (!-d $homedir);
my $datastore = "$root/.cpanel-datastore";
$datastore = "$root/.cpanel/datastore" if (!-d $datastore);
-d $daily || -d $homedir || -d $datastore ||
	return ("Not a cPanel daily or home directory backup file");

# Try to work out the domain
if (!$dom) {
	my @domfiles = glob("$root/*/vf/*");
	if (!@domfiles) {
		@domfiles = glob("$root/vf/*");
		}
	my @doms = map { /\/vf\/([^\/]+)$/; $1 } @domfiles;
	if (@doms > 1) {
		# Hack to work out primary domain
		my $ds = "$homedir/.cpanel-datastore";
		$ds = $datastore if (!-d $ds);
		opendir(DATASTORE, $ds);
		foreach my $gdi (readdir(DATASTORE)) {
			if ($gdi =~ /^apache_GETDOMAINIP_(\S+)$/) {
				$gdi{$1} = 1;
				}
			}
		closedir(DATASTORE);
		if (scalar(keys %gdi)) {
			# Can limit by domain IP to find the master
			@doms = grep { $gdi{$_} } @doms;
			}
		else {
			# Look at the cp/username file
			my ($cpfile) = glob("$root/*/cp/*");
			if ($cpfile && -r $cpfile) {
				my %cp;
				&read_env_file($cpfile, \%cp);
				@doms = ( $cp{'DNS'} );
				}
			}
		}
	if (@doms == 1) {
		$dom = $doms[0];
		}
	elsif (@doms > 1) {
		return ("More than one domain name was found in the cPanel backup : ".join(" ", @doms));
		}
	else {
		return ("Could not work out domain name from cPanel backup");
		}
	}

if (-d $daily) {
	# Older style backup - check for user and Apache domain file
	if (!$user) {
		my ($tgz) = glob("$daily/*.tar.gz");
		$tgz =~ /\/([^\/]+)\.tar\.gz$/ ||
		    return ("Could not work out username from cPanel backup");
		$user = $1;
		}
	-r "$daily/$user.tar.gz" ||
		return ("Could not find directory for $user in backup");
	my $httpd = &extract_cpanel_file("$daily/files/_etc_httpd_conf_httpd.conf.gz");
	my ($vconf, $virt) = &get_apache_virtual($dom, undef, $httpd);
	$vconf ||
	    return ("Could not find Apache virtual server $dom in backup");
	}
elsif (-d $homedir) {
	# Newer style backup - check for aliases file
	($vfdom) = glob("$root/*/vf/$dom");
	-r $vfdom ||
	    return ("Could not find mail aliases file for $dom in backup");
	if (!$user && $homedir =~ /\/backup-([^\/]+)_([^\/]+)\//) {
		$user = $2;
		}
	if (!$user && $homedir =~ /\/cpmove-([^\/]+)\//) {
		$user = $1;
		}
	if (!$user) {
		opendir(ROOT, $root);
		my @rootfiles = grep { !/^\./ } readdir(ROOT);
		closedir(ROOT);
		$user = $rootfiles[0];
		}
	$user || return ("Could not work out username from cPanel backup");
	}
else {
	# Home-only backup
	$user || return ("Username must be supplied for this type of cPanel backup");
	}

return (undef, $dom, $user, $pass);
}

# migration_cpanel_migrate(file, domain, username, create-webmin, template-id,
#			   &ipinfo, pass, [&parent], [prefix], [email], [&plan])
# Actually extract the given cPanel backup, and return the list of domains
# created.
sub migration_cpanel_migrate
{
my ($file, $dom, $user, $webmin, $template, $ipinfo, $pass, $parent,
       $prefix, $email, $plan) = @_;
my ($ok, $root) = &extract_cpanel_dir($file);
$ok || &error("Failed to extract backup : $root");
my $daily = glob("$root/backup*/cpbackup/daily");
my $datastore = "$root/.cpanel-datastore";
$datastore = "$root/.cpanel/datastore" if (!-d $datastore);
my $tmpl = &get_template($template);

# Check for prefix clash
$prefix ||= &compute_prefix($dom, undef, $parent, 1);
my $pclash = &get_domain_by("prefix", $prefix);
$pclash && &error("A virtual server using the prefix $prefix already exists");

# Get shells for users
my ($nologin_shell, $ftp_shell, undef, $def_shell) =
	&get_common_available_shells();
$nologin_shell ||= $def_shell;
$ftp_shell ||= $def_shell;

# Work out the username again if it wasn't supplied
my $origuser;
my ($homedir) = glob("$root/*/homedir");
$homedir = "$root/homedir" if (!-d $homedir);
if (-d $daily) {
	my ($tgz) = glob("$daily/*.tar.gz");
	$tgz =~ /\/([^\/]+)\.tar\.gz$/;
	$origuser = $1;
	}
elsif (-d $homedir) {
	$homedir =~ /\/backup-([^\/]+)_([^\/]+)\//;
	$origuser = $2;
	}
$user ||= $origuser;
$user || &error("Could not work out username automatically");
my $group = $user;
my $ugroup = $group;

# First work out what features we have ..
&$first_print("Checking for cPanel features ..");
my @got = ( "dir", $parent ? () : ("unix"), &domain_has_website(),
	       "logrotate" );
push(@got, "webmin") if ($webmin && !$parent);
my $userdir;
my $homesrc;
if (-d $daily) {
	my $named = &extract_cpanel_file("$daily/files/_etc_named.conf.gz");
	my $zone;
	if ($named) {
		push(@got, "dns");
		}
	my $localdomains = &extract_cpanel_file("$daily/files/_etc_localdomains.gz");
	if ($localdomains) {
		# Check for mail domain
		my $lref = &read_file_lines($localdomains);
		foreach my $l (@$lref) {
			push(@got, "mail") if ($l eq $dom);
			}
		}
	($ok, $userdir) = &extract_cpanel_dir("$daily/$user.tar.gz");
	$ok || return "Failed to extract user directory : $userdir";
	$userdir .= "/".$user;
	-d $userdir || return "No user directory found - maybe username ".
			      "$user is incorrect";
	$homesrc = "$userdir/homedir";
	}
elsif (-d $datastore) {
	# For a home-based only backup, assume we have web, mail and DNS
	push(@got, "dns", "mail");
	$userdir = $homesrc = $root;
	}
else {
	# For a homedir only backup, assume we have web, mail and DNS
	push(@got, "dns", "mail");
	if (-d "$root/$user") {
		# Sub-directory is named after user
		$userdir = "$root/$user";
		}
	else {
		# Sub-directory has date-based name
		($userdir) = glob("$root/*");
		}
	-d $userdir || return "No user directory found - ".
			      "maybe username $user is incorrect";
	$homesrc = "$userdir/homedir";
	$datastore = "$homesrc/.cpanel-datastore";
	if (-d "$homesrc/.cpanel/datastore") {
		$datastore = "$homesrc/.cpanel/datastore";
		}
	}
if (&indexof("mail", @got) >= 0 && -e "$homedir/.spamassassinenable") {
	push(@got, "spam");
	}

# Work out if the original domain was a sub-server in cPanel
my $waschild = 0;
my $wasuser = $dom;
$wasuser =~ s/\..*$//;
my $aliasdom;
if (-r "$datastore/apache_LISTMULTIPARKED_0") {
	# Sub-servers are in this config file. We can also work out the original
	# 'username' for the sub-directory.
	my $subs = &read_file_contents(
		"$datastore/apache_LISTMULTIPARKED_0");
	if ($subs =~ /(\/[a-z0-9\.\-_\/]+)[^a-z0-9\.\-]+\Q$dom\E[^a-z0-9\.\-]+([a-z0-9\.\-]+)?/i) {
		$waschild = 1;
		my $wasdir = $1;
		$aliasdom = $2;
		if ($wasdir =~ /public_html\/(.*)$/) {
			$wasuser = $1;
			}
		$aliasdom = undef if ($aliasdom !~ /\./);	# Data error
		}
	}
elsif (-d "$homesrc/tmp/webalizer") {
	# Sub-servers had separate webalizer config
	$waschild = -d "$homesrc/tmp/webalizer/$dom" ? 1 : 0;
	}
else {
	# Can't be sure, so guess
	$waschild = $parent ? 1 : 0;
	}

# Check for Webalizer and AWstats
my $webalizer = $waschild ? "$homesrc/tmp/webalizer/$dom"
			     : "$homesrc/tmp/webalizer";
if (-d $webalizer) {
	push(@got, "webalizer");
	}
if (-r "$homesrc/tmp/awstats/awstats.$dom.conf") {
	push(@got, "virtualmin-awstats");
	}

if (-s "$userdir/mysql.sql" && !$waschild) {
	# Check for mysql
	my $mycount = 0;
	my $mydir = "$userdir/mysql";
	my %dblist = map { $_, 1 } &get_cpanel_db_list("$userdir/mysql.sql",
							  $user, $origuser);
	opendir(MYDIR, $mydir);
	while($myf = readdir(MYDIR)) {
		if ($myf =~ /^(\Q$user\E_\S*).sql$/ ||
		    $myf =~ /^(\Q$origuser\E_\S*).sql$/ ||
		    $myf eq "$user.sql" ||
		    $myf eq "$origuser.sql" ||
		    $myf =~ /^(\S+).sql$/ && $dblist{$1}) {
			$mycount++;
			}
		}
	closedir(MYDIR);
	push(@got, "mysql") if ($mycount);
	}
if ($ipinfo->{'virt'} && -s "$userdir/sslcerts/www.$dom.crt" &&
			         -s "$userdir/sslkeys/www.$dom.key") {
	# Enable SSL, if we have a private IP and if the key was found
	push(@got, &domain_has_ssl());
	}

# Look for mailing lists
my ($ml, @lists);
opendir(MM, "$userdir/mm");
foreach $ml (readdir(MM)) {
	if ($ml =~ /^(\S+)_\Q$dom\E$/) {
		push(@lists, $1);
		}
	}
closedir(MM);
if (@lists && &plugin_defined("virtualmin-mailman", "create_list")) {
	push(@got, "virtualmin-mailman");
	}

# Tell the user what we have got
@got = &show_check_migration_features(@got);
my %got = map { $_, 1 } @got;

# Work out user and group IDs
my ($gid, $ugid, $uid, $duser);
if ($parent) {
	# UID and GID come from parent
	$gid = $parent->{'gid'};
	$ugid = $parent->{'ugid'};
	$uid = $parent->{'uid'};
	$duser = $parent->{'user'};
	$group = $parent->{'group'};
	$ugroup = $parent->{'ugroup'};
	}
else {
	# IDs are allocated by setup_unix
	$uid = $gid = $ugid = undef;
	$duser = $user;
	}

my $quota;
if (-r "$userdir/quota") {
	# Get the quota (from home directory backup)
	open(QUOTA, "<$userdir/quota");
	$quota = <QUOTA>;
	close(QUOTA);
	$quota = int($quota) * 1024;	# cpanel quotas are in MB
	}
elsif (-r "$datastore/quota_-v") {
	# Get the quota (from v10 backup)
	local $_;
	open(QUOTA, "<$datastore/quota_-v");
	while(<QUOTA>) {
		if (/^\s+\S+\s+(\d+)\s+(\d+)\s+(\d+)/) {
			$quota = $2;
			}
		}
	close(QUOTA);
	}

# Create the virtual server object
my %dom;
$prefix ||= &compute_prefix($dom, $group, $parent, 1);
$plan = $parent ? &get_plan($parent->{'plan'}) :
        $plan ? $plan : &get_default_plan();
%dom = ( 'id', &domain_id(),
	 'dom', $dom,
         'user', $duser,
         'group', $group,
         'ugroup', $ugroup,
         'uid', $uid,
         'gid', $gid,
         'ugid', $ugid,
         'owner', "Migrated cPanel server $dom",
         'email', $email ? $email : $parent ? $parent->{'email'} : undef,
	 'dns_ip', $ipinfo->{'virt'} ? undef :
		   &get_dns_ip($parent ? $parent->{'resel'} : undef, 4),
	 'dns_ip6', $ipinfo->{'virt6'} ? undef :
		   &get_dns_ip($parent ? $parent->{'resel'} : undef, 6),
	 $parent ? ( 'pass', $parent->{'pass'} )
		 : ( 'pass', $pass ),
	 'source', 'migrate.cgi',
	 'template', $template,
	 'plan', $plan->{'id'},
	 'parent', $parent ? $parent->{'id'} : undef,
	 'reseller', $parent ? $parent->{'reseller'} : undef,
	 'prefix', $prefix,
	 'no_tmpl_aliases', 1,
	 'no_mysql_db', $got{'mysql'} ? 1 : 0,
	 'nocreationmail', 1,
	 'nocopyskel', 1,
	 'nocreationscripts', 1,
	 'parent', $parent ? $parent->{'id'} : undef,
	 'creation_type', 'migrate',
	 'migration_type', 'cpanel',
        );
&merge_ipinfo_domain(\%dom, $ipinfo);
if (!$parent) {
	&set_limits_from_plan(\%dom, $plan);
	$dom{'quota'} = $quota;
	$dom{'uquota'} = $quota;
	&set_capabilities_from_plan(\%dom, $plan);
	}
$dom{'db'} = &database_name(\%dom);
foreach my $f (@features, &list_feature_plugins()) {
	$dom{$f} = $got{$f} ? 1 : 0;
	}
&set_chained_features(\%dom, undef);
&set_featurelimits_from_plan(\%dom, $plan);

# Work out the master admin MySQL password
if (open(MYSQL, "<$userdir/mysql.sql")) {
	while(<MYSQL>) {
		s/\r|\n//g;
		if (/^GRANT USAGE ON \*\.\* TO '(\S+)'\@'(\S+)' IDENTIFIED BY PASSWORD '(\S+)';/ && $1 eq $user) {
			$dom{'mysql_enc_pass'} = $3;
			}
		}
	close(MYSQL);
	}

my $orighome;
if (-d $daily) {
	# Work out home directory (use cpanel home by default)
	my $httpd = &extract_cpanel_file("$daily/files/_etc_httpd_conf_httpd.conf.gz");
	my ($srcvconf, $srcvirt) = &get_apache_virtual($dom, undef, $httpd);
	$orighome = &apache::find_directive("DocumentRoot", $srcvirt);
	$orighome =~ s/\/public_html$//;
	}
else {
	# Try to stick with cpanel home standard (/home/$user)
	if (!$waschild) {
		$orighome = "/home/$user";
		}
	}
if ($orighome && &is_under_directory($home_base, $orighome) && !$parent) {
	# Use same home directory as cPanel
	$dom{'home'} = $orighome;
	}
else {
	# Use Virtualmin's home
	$dom{'home'} = &server_home_directory(\%dom, $parent);
	}

# Set cgi directories to cpanel standard
$dom{'cgi_bin_dir'} = "public_html/cgi-bin";
$dom{'cgi_bin_path'} = "$dom{'home'}/$dom{'cgi_bin_dir'}";
$dom{'cgi_bin_correct'} = 1;	# So that setup_web doesn't fix it

&set_provision_features(\%dom);
&generate_domain_password_hashes(\%dom, 1);
&complete_domain(\%dom);

# Check for various clashes
&$first_print("Checking for clashes and dependencies ..");
$derr = &virtual_server_depends(\%dom);
if ($derr) {
	&$second_print($derr);
	return ( );
	}
$cerr = &virtual_server_clashes(\%dom);
if ($cerr) {
	&$second_print($cerr);
	return ( );
	}
&$second_print(".. all OK");

# Create the initial server
&$first_print("Creating initial virtual server $dom ..");
&$indent_print();
my $err = &create_virtual_server(\%dom, $parent,
				    $parent ? $parent->{'user'} : undef);
&$outdent_print();
if ($err) {
	&$second_print($err);
	return ( );
	}
else {
	&$second_print(".. done");
	}
my @rvdoms = ( \%dom );

# Extract homedir.tar if needed
my $hometar = "$userdir/homedir.tar";
if (-r $hometar) {
	&$first_print("Extracting home directory TAR file ..");
	my $out;
	if (!-d $homesrc) {
		&make_dir($homesrc, 0755);
		}
	&execute_command("cd ".quotemeta($homesrc)." && ".
			 &make_tar_command("xf", $hometar),
			 undef, \$out, \$out);
	if ($?) {
		&$second_print(".. TAR failed : <tt>$out</tt>");
		}
	else {
		&$second_print(".. done");
		}
	&set_home_ownership(\%dom);
	}

# Migrate Apache configuration
if ($got{'web'} && -d $daily) {
	&$first_print("Copying Apache directives ..");
	if ($srcvconf) {
		# Copy any directives not set by Virtualmin
		my $conf = &apache::get_config();
		my ($vconf, $virt) = &get_apache_virtual($dom, undef);
		my %dirs;
		foreach my $a (@$virt) {
			next if ($a->{'type'});
			$dirs{$a->{'name'}}++;
			}
		$dirs{'ScriptAlias'} = 0;	# Always copy this
		$dirs{'ServerAlias'} = 0;	# and this
		$dirs{'BytesLog'} = 1;		# Not supported
		$dirs{'User'} = 1;		# Don't copy user-related
		$dirs{'Group'} = 1;		# settings, as Virtualmin will
		$dirs{'SuexecUserGroup'} = 1;	# have already set them
		my %vals;
		foreach my $a (@$srcvirt) {
			next if ($a->{'type'} || $dirs{$a->{'name'}});
			if ($dom{'home'} ne $orighome) {
				$a->{'value'} =~ s/$orighome/$dom{'home'}/g;
				}
			push(@{$vals{$a->{'name'}}}, $a->{'value'});
			}
		foreach my $an (keys %vals) {
			&apache::save_directive($an, $vals{$an}, $virt, $conf);
			}
		&$second_print(".. done");
		&register_post_action(\&restart_apache) if (!$got{'ssl'});
		}
	else {
		&$second_print(".. could not find Apache configuration");
		}
	}
elsif ($got{'web'}) {
	# Just adjust cgi-bin directory to match cPanel
	my $conf = &apache::get_config();
	my ($virt, $vconf) = &get_apache_virtual($dom, undef);
	if ($virt) {
		&apache::save_directive("ScriptAlias",
			[ "/cgi-bin $dom{'home'}/public_html/cgi-bin" ],
			$vconf, $conf);
		&flush_file_lines($virt->{'file'});
		&register_post_action(\&restart_apache) if (!$got{'ssl'});
		}
	&save_domain(\%dom);
	&add_script_language_directives(\%dom, $tmpl, $dom{'web_port'});
	}
$dom{'cgi_bin_correct'} = 0;	# So that it is computed from now on

if ($got{&domain_has_ssl()}) {
	# Copy and use the SSL certs that came with the domain
	&$first_print("Copying SSL certificate and key ..");
	&copy_source_dest("$userdir/sslcerts/www.$dom.crt", $dom{'ssl_cert'});
	&copy_source_dest("$userdir/sslcerts/www.$dom.key", $dom{'ssl_key'});
	if ($got{'ssl'}) {
		&register_post_action(\&restart_apache, 1);
		}
	&$second_print(".. done");
	}

# Migrate DNS domain
&cpanel_migrate_dns_zone(\%dom, $dom);

my $out;
my $ht = &public_html_dir(\%dom);
my $qht = quotemeta($ht);
if ($waschild) {
	# Migrate web directory
	my $qhtsrc = "$homesrc/public_html/$wasuser";
	&$first_print("Copying web pages to $ht ..");
	&execute_command("cd $qhtsrc && ".
			 "(".&make_tar_command("cf", "-", ".").
			 " | (cd $qht && ".
			 &make_tar_command("xf", "-")."))",
			 undef, \$out, \$out);
	}
else {
	# Migrate home directory contents (except logs and mail)
	&$first_print("Copying home directory to $dom{'home'} ..");
	my $qhome = quotemeta($dom{'home'});
	my $xtemp = &transname();
	&open_tempfile(XTEMP, ">$xtemp");
	&print_tempfile(XTEMP, "./logs\n");
	&print_tempfile(XTEMP, "./mail\n");
	&close_tempfile(XTEMP);
	&execute_command("cd $homesrc && ".
			 "(".&make_tar_command("cfX", "-", $xtemp, ".").
			 " | (cd $qhome && ".
			 &make_tar_command("xf", "-")."))",
			 undef, \$out, \$out);
	}
if ($?) {
	&$second_print(".. copy failed : <tt>$out</tt>");
	}
else {
	&$second_print(".. done");
	}
&set_home_ownership(\%dom);

# If php.ini is migrated wrong, fix it
if ($dom{'web'}) {
	my $mode = &get_domain_php_mode(\%dom);
	if ($mode eq "cgi" || $mode eq "fcgid") {
		&fix_php_extension_dir(\%dom);
		}
	}

# Fix up home ownership and permissions
&$first_print("Fixing home directory permissions ..");
if (defined(&set_php_wrappers_writable)) {
	&set_php_wrappers_writable(\%dom, 1);
	}
&set_home_ownership(\%dom);
&system_logged("chmod '$uconfig{'homedir_perms'}' ".
	       quotemeta($dom{'home'}));
foreach my $sd (&virtual_server_directories(\%dom)) {
	&system_logged("chmod $sd->[1] ".
		       quotemeta("$dom{'home'}/$sd->[0]"));
	}
if (defined(&set_php_wrappers_writable)) {
	&set_php_wrappers_writable(\%dom, 0);
	}
&$second_print(".. done");

# Lock the user DB and build list of used IDs
&obtain_lock_unix(\%dom);
&obtain_lock_mail(\%dom);
my (%taken, %utaken);
&build_taken(\%taken, \%utaken);

&foreign_require("mailboxes");
$mailboxes::no_permanent_index = 1;
my %usermap;
if ($got{'mail'}) {
	# Migrate mail users
	&cpanel_migrate_mailboxes($dom, \%dom, \%usermap, $homesrc);
	}

# Move server owner's inbox file
my $owner = &get_domain_owner(\%dom);
if ($owner && !$parent) {
	&$first_print("Moving server owner's mailbox ..");
	my ($mfile, $mtype) = &user_mail_file($owner);
	my $srcfolder;
	if (-d "$homesrc/mail/cur" || -d "$homesrc/mail/new") {
		# Maildir format
		$srcfolder = { 'type' => 1, 'file' => "$homesrc/mail" };
		}
	elsif (-r "$homesrc/mail/inbox") {
		# mbox format
		$srcfolder = { 'type' => 0,
			       'file' => "$homesrc/mail/inbox" };
		}
	if ($srcfolder) {
		my $dstfolder = { 'type' => $mtype, 'file' => $mfile };
		&mailboxes::mailbox_move_folder($srcfolder, $dstfolder);
		&set_mailfolder_owner($dstfolder, $owner);
		&mailboxes::mailbox_uncompress_folder($dstfolder)
			if (defined(&mailboxes::mailbox_uncompress_folder));
		&$second_print(".. done");
		}
	else {
		&$second_print(".. none exists");
		}
	}

# Build map from email addresses to users
my %useremail;
foreach my $uinfo (&list_domain_users(\%dom)) {
	if ($uinfo->{'email'}) {
		$useremail{$uinfo->{'email'}} = $uinfo;
		}
	}

if ($got{'mail'}) {
	# Copy mail aliases
	my $acount = 0;
	my $domfwd;
	&$first_print("Copying email aliases ..");
	&set_alias_programs();
	my %gotvirt = map { $_->{'from'}, $_ } &list_virtusers();
	local $_;
	open(VAD, "<$userdir/vad/$dom");
	while(<VAD>) {
		s/\r|\n//g;
		s/^\s*#.*$//;
		if (/^(\S+):\s*(\S+)$/) {
			# A domain forward exists ..
			my $virt = { 'from' => "\@$dom",
					'to' => [ "%1\@$2" ] };
			my $clash = $gotvirt{$virt->{'from'}};
			&delete_virtuser($clash) if ($clash);
			&create_virtuser($virt);
			$acount++;
			$domfwd++;
			}
		}
	close(VAD);
	open(VA, "<$userdir/va/$dom");
	while(<VA>) {
		s/\r|\n//g;
		s/^\s*#.*$//;
		if (/^(\S+):\s*(.*)$/) {
			my ($name, $v) = ($1, $2);
			next if (!$name);
			my @values;
			if ($v !~ /,/ && $v !~ /"/) {
				# A single destination, not quoted!
				@values = ( $v );
				}
			else {
				# Comma-separated alias destinations
				while($v =~ /^\s*,?\s*"(\|)([^"]+)"(.*)$/ ||
				      $v =~ /^\s*,?\s*()"([^"]+)"(.*)$/ ||
				      $v =~ /^\s*,?\s*(\|)"([^"]+)"(.*)$/ ||
				      $v =~ /^\s*,?\s*()([^,\s]+)(.*)$/) {
					push(@values, $1.$2);
					$v = $3;
					}
				}
			my $mailman = 0;
			foreach my $v (@values) {
				if ($v =~ /:fail:\s+(.*)/) {
					# Fix bounce alias
					$v = "BOUNCE $1";
					}
				my ($atype, $aname) = &alias_type($v, $name);
				if ($atype == 4 && $aname =~ /autorespond\s+(\S+)\@(\S+)\s+(\S+)/) {
					# Turn into Virtualmin auto-responder
					$v = "| $module_config_directory/autoreply.pl $3/$name $1";
					&set_ownership_permissions(
						undef, undef, 0755,
						$3, "$3/$name");
					}
				elsif ($atype == 4 && $aname =~ /mailman/) {
					$mailman++;
					}
				}
			# Don't create aliases for mailman lists
			next if ($mailman || $name =~ /^owner-/);

			# Already done a domain forward
			next if ($name =~ /^\*/ && $domfwd);

			# No need for a catchall that bounces mail, as this
			# will happen anyway
			next if ($name =~ /^\*/ && @values == 1 &&
				 $values[0] =~ /^BOUNCE/);

			if ($useremail{$name}) {
				# This is an alias from a user. Preserve
				# delivery to his mailbox though, as this is
				# what cPanel seems to do.
				my $uinfo = $useremail{$name};
				my $olduinfo = { %$uinfo };
				my $touser = $uinfo->{'user'};
				if ($mail_system == 0 && $escuser =~ /\@/) {
					$touser = &escape_replace_atsign_if_exists($touser);
					}
				$touser = "\\".$touser;
				$uinfo->{'to'} = [ $touser, @values ];
				&modify_user($uinfo, $olduinfo, \%dom);
				}
			else {
				# Just create an alias
				if ($name !~ /\@/) {
					$name .= "\@".$dom;
					}
				my $virt = { 'from' => $name =~ /^\*/ ?
						  "\@".$dom : $name,
						'to' => \@values };
				my $clash = $gotvirt{$virt->{'from'}};
				&delete_virtuser($clash) if ($clash);
				&create_virtuser($virt);
				$acount++;
				}
			}
		}
	close(VA);
	&$second_print(".. done (migrated $acount aliases)");
	}

if ($got{'spam'}) {
	# Migrate spam preferences
	my $up = &read_file_contents("$homedir/.spamassassin/user_prefs");
	my $spamdir = "$spam_config_dir/$dom{'id'}";
	&open_tempfile(SPAMFILE, ">>$spamdir/virtualmin.cf");
	&print_tempfile(SPAMFILE, $up);
	&close_tempfile(SPAMFILE);
	}

# Create mailing lists
if ($got{'virtualmin-mailman'}) {
	my $lcount = 0;
	&$first_print("Re-creating mailing lists ..");
	foreach $ml (@lists) {
		my $err = &plugin_call("virtualmin-mailman", "create_list",
			     $ml, $dom, "Migrated cPanel mailing list",
			     undef, $dom{'emailto_addr'}, $dom{'pass'});
		if ($err) {
			&$second_print("Failed to create $ml : $err");
			}
		else {
			&execute_command("cp ".quotemeta("$userdir/mm/${ml}_${dom}/")."*.pck ".quotemeta("$virtualmin_mailman::mailman_var/lists/$ml"));
			$lcount++;
			}
		}
	&$second_print(".. done (created $lcount lists)");
	}

# Copy cron jobs for user (direct to his cron file)
if (-r "$userdir/cron/$user" && !$waschild) {
	&foreign_require("cron");
	&$first_print("Copying Cron jobs ..");
	$cron::cron_temp_file = &transname();
	eval {
		local $main::error_must_die = 1;
		if ($parent) {
			# Append migrated cron to parent user's
			&cron::copy_cron_temp({ 'user' => $parent->{'user'} });
			&execute_command(
			  "cat $userdir/cron/$user >>$cron::cron_temp_file");
			&cron::copy_crontab($parent->{'user'});
			}
		else {
			# Just over-write cron
			&execute_command(
			  "cp $userdir/cron/$user $cron::cron_temp_file");
			&cron::copy_crontab($user);
			}
		};
	if ($@) {
		&$second_print(".. failed : $@");
		}
	else {
		&$second_print(".. done");
		}
	}

if ($got{'mysql'}) {
	# Re-create all MySQL databases
	my $mycount = 0;
	&$first_print("Re-creating and loading MySQL databases ..");
	&disable_quotas(\%dom);
	my $mydir = "$userdir/mysql";
	my %dblist = map { $_, 1 } &get_cpanel_db_list("$userdir/mysql.sql",
							  $user, $origuser);
	opendir(MYDIR, $mydir);
	while($myf = readdir(MYDIR)) {
		if ($myf =~ /^(\Q$user\E_\S*).sql$/ ||
		    $myf =~ /^(\Q$origuser\E_\S*).sql$/ ||
		    $myf =~ /^(\Q$user\E).sql$/ ||
		    $myf =~ /^(\Q$origuser\E).sql$/ ||
		    $myf =~ /^(\S+).sql$/ && $dblist{$1}) {
			my $db = $1;
			&$indent_print();
			&create_mysql_database(\%dom, $db);
			&save_domain(\%dom, 1);
			my ($ex, $out) = &execute_dom_sql_file(\%dom, $db, "$mydir/$myf");
			if ($ex) {
				&$first_print("Error loading $db : $out");
				}
			&$outdent_print();
			$mycount++;
			}
		}
	closedir(MYDIR);
	&enable_quotas(\%dom);
	&$second_print(".. done (created $mycount databases)");

	# Re-create MySQL users
	if ($got{'mysql'}) {
		my $myucount = 0;
		&$first_print("Re-creating MySQL users ..");
		my %myusers;
		local $_;
		my (%donemysqluser, %donemysqlpriv);
		open(MYSQL, "<$userdir/mysql.sql");
		while(<MYSQL>) {
			s/\r|\n//g;
			if (/^GRANT USAGE ON \*\.\* TO '(\S+)'\@'(\S+)' IDENTIFIED BY PASSWORD '(\S+)';/) {
				# Creating a MySQL user
				my ($myuser, $mypass) = ($1, $3);
				next if ($myuser eq $user);	# domain owner
				next if ($donemysqluser{$myuser}++);
				my $myuinfo = &create_initial_user(\%dom);
				$myuinfo->{'user'} = $myuser;
				$myuinfo->{'pass'} = "x";	# not needed
				$myuinfo->{'mysql_pass'} = $mypass;
				$myuinfo->{'gid'} = $dom{'gid'};
				$myuinfo->{'real'} = "MySQL user";
				$myuinfo->{'home'} = "$dom{'home'}/$config{'homes_dir'}/$myuser";
				$myuinfo->{'shell'} = $nologin_shell->{'shell'};
				delete($myuinfo->{'email'});
				$myusers{$myuser} = $myuinfo;
				}
			elsif (/GRANT ALL PRIVILEGES ON `(\S+)`\.\* TO '(\S+)'\@'(\S+)';/ || /GRANT SELECT.*\sON `(\S+)`\.\* TO '(\S+)'\@'(\S+)';/) {
				# Granting access to a MySQL database
				my ($mydb, $myuser) = ($1, $2);
				next if ($myuser eq $user);	# domain owner
				next if ($donemysqlpriv{$mydb,$myuser}++);
				$mydb =~ s/\\(.)/$1/g;
				if ($myusers{$myuser}) {
					push(@{$myusers{$myuser}->{'dbs'}},
					     { 'type' => 'mysql', 'name' => $mydb });
					}
				}
			}
		close(MYSQL);
		foreach my $myuinfo (values %myusers) {
			my $already = $usermap{$myuinfo->{'user'}};
			if ($already) {
				# User already exists, so just give him the dbs
				my $olduinfo = { %$already };
				$already->{'dbs'} = $myuinfo->{'dbs'};
				&modify_user($already, $olduinfo, \%dom);
				}
			else {
				$myuinfo->{'uid'} = &allocate_uid(\%taken);
				&create_user_home($myuinfo, \%dom, 1);
				&create_user($myuinfo, \%dom);
				&create_mail_file($myuinfo, \%dom);
				$taken{$myuinfo->{'uid'}}++;
				$usermap{$myuinfo->{'user'}} = $myuinfo;
				}
			$myucount++;
			}
		&$second_print(".. done (created $myucount MySQL users)");
		}
	}

# Migrate or update FTP users
if (-r "$userdir/proftpdpasswd" && !$waschild) {
	my $fcount = 0;
	&$first_print("Re-creating FTP users ..");
	local $_;
	open(FTP, "<$userdir/proftpdpasswd");
	while(<FTP>) {
		s/\r|\n//g;
		s/^\s*#.*$//;
		my ($fuser, $fpass, $fuid, $fgid, $fdummy, $fhome, $fshell) = split(/:/, $_);
		next if (!$fuser);
		$fuser = &remove_userdom($fuser, \%dom);
		next if ($fuser eq "ftp" || $fuser eq $user ||
			 $fuser eq $user."_logs");	# skip cpanel users
		my $fullfuser = &userdom_name(lc($fuser), \%dom);
		if ($fhome eq "/dev/null" ||
		    !&is_under_directory($dom{'home'}, $fhome)) {
			$fhome = "$dom{'home'}/$config{'homes_dir'}/$fuser";
			}
		my $already = $usermap{$fuser} ||
				 $usermap{$fullfuser};
		if ($already) {
			# Turn on FTP for existing user
			my $olduinfo = { %$already };
			$already->{'shell'} = $ftp_shell->{'shell'};
			&modify_user($already, $olduinfo, \%dom);
			}
		else {
			# Create new FTP-only user
			my $fuinfo = &create_initial_user(\%dom, 0,
			     $fhome eq $ht || $fhome eq $dom{'home'});
			$fuinfo->{'user'} = $fullfuser;
			$fuinfo->{'pass'} = $fpass;
			if ($fuinfo->{'webowner'}) {
				$fuinfo->{'uid'} = $dom{'uid'};
				}
			else {
				$fuinfo->{'uid'} = &allocate_uid(\%taken);
				}
			$fuinfo->{'gid'} = $dom{'gid'};
			$fuinfo->{'real'} = "FTP user";
			$fuinfo->{'home'} = $fhome;
			$fuinfo->{'shell'} = $ftp_shell->{'shell'};
			delete($fuinfo->{'email'});
			$usermap{$fuser} = $fuinfo;
			if (!$user->{'nocreatehome'}) {
				&create_user_home($fuinfo, \%dom, 1);
				}
			&create_user($fuinfo, \%dom);
			if (!$user->{'nomailfile'}) {
				&create_mail_file($fuinfo, \%dom);
				}
			$taken{$fuinfo->{'uid'}}++;
			$fcount++;
			}
		}
	close(FTP);
	&$second_print(".. done (created $fcount FTP users)");
	}
&release_lock_unix(\%dom);
&release_lock_mail(\%dom);

# Migrate any parked domains as alias domains
my @parked;
if (!$waschild) {
	local $_;
	open(PARKED, "<$userdir/pds");
	while(<PARKED>) {
		s/\r|\n//g;
		my ($pdom) = split(/\s+/, $_);
		push(@parked, $pdom) if ($pdom && $pdom !~ /^\*/);
		}
	close(PARKED);
	}

# Create alias domain for sub-domain
if ($aliasdom && $aliasdom !~ /^\*/) {
	push(@parked, $aliasdom);
	}

# Actually create alias doms
foreach my $pdom (&unique(@parked)) {
	if ($pdom eq $aliasdom) {
		&$first_print("Creating alias domain $pdom ..");
		}
	else {
		&$first_print("Creating parked domain $pdom ..");
		}
	if (&domain_name_clash($pdom)) {
		&$second_print(".. the domain $pdom already exists");
		next;
		}
	&$indent_print();
	my %alias = ( 'id', &domain_id(),
			 'dom', $pdom,
			 'user', $dom{'user'},
			 'group', $dom{'group'},
			 'prefix', $dom{'prefix'},
			 'ugroup', $dom{'ugroup'},
			 'pass', $dom{'pass'},
			 'alias', $dom{'id'},
			 'aliasmail', 1,
			 'uid', $dom{'uid'},
			 'gid', $dom{'gid'},
			 'ugid', $dom{'ugid'},
			 'owner', $pdom eq $aliasdom ?
					"Migrated cPanel alias for $dom{'dom'}":
					"Parked domain for $dom{'dom'}",
			 'email', $dom{'email'},
			 'name', 1,
			 'ip', $dom{'ip'},
			 'dns_ip', $dom{'dns_ip'},
			 'virt', 0,
			 'source', $dom{'source'},
			 'parent', $dom{'id'},
			 'template', $dom{'template'},
			 'reseller', $dom{'reseller'},
			 'nocreationmail', 1,
			 'nocopyskel', 1,
			);
	foreach my $f (@alias_features) {
		$alias{$f} = $dom{$f};
		}
	my $parentdom = $dom{'parent'} ? &get_domain($dom{'parent'})
					  : \%dom;
	$alias{'home'} = &server_home_directory(\%alias, $parentdom);
	&generate_domain_password_hashes(\%alias, 1);
	&complete_domain(\%alias);
	&create_virtual_server(\%alias, $parentdom,
			       $parentdom->{'user'});
	if ($alias{'mail'}) {
		&cpanel_migrate_mailboxes(
			$alias{'dom'}, \%alias, undef, $homesrc);
		}

	# Create parked domain aliases
	&$first_print("Copying email aliases for parked domain $pdom ..");
	my $acount = &cpanel_migrate_addon_aliases($pdom, $userdir);
	&$second_print(".. done (migrated $acount aliases)");

	&$outdent_print();
	&$second_print($text{'setup_done'});
	push(@rvdoms, \%alias);
	}

# Read addons domain mapping file
my %addons;
my $lref = &read_file_lines("$userdir/addons");
foreach my $l (@$lref) {
	my ($a, $t) = split(/=/, $l);
	if ($a && $t) {
		$t =~ s/_/\./;
		$addons{$a} = $t;
		}
	}

# Create sub-domains, silently skipping those for which no parent exists yet
&create_sub_domains(1, $userdir);

# Create addon domains
&create_addon_domains(0, $userdir, $homesrc);

# Create sub-domains again, to catch those for which an addon target exists now
&create_sub_domains(0, $userdir);

if ($got{'webalizer'}) {
	# Copy existing Weblizer stats to ~/public_html/stats
	&$first_print("Copying Weblizer data files ..");
	&execute_command("cp ".
		quotemeta($webalizer)."/*.{png,gif,html,current,hist} ".
		quotemeta(&webalizer_stats_dir(\%dom)));
	&execute_command("chown -R $dom{'uid'}:$dom{'ugid'} ".
			 quotemeta(&webalizer_stats_dir(\%dom)));
        &$second_print($text{'setup_done'});
	}

if ($got{'virtualmin-awstats'}) {
	# Copy AWstats data files to ~/awstats
	&$first_print("Copying AWstats data files ..");
	&execute_command("cp ".quotemeta("$homesrc/tmp/awstats")."/*.$dom.txt ".
			       quotemeta("$dom{'home'}/awstats"));
	&execute_command("chown -R $dom{'uid'}:$dom{'ugid'} ".
			 quotemeta("$dom{'home'}/awstats"));
        &$second_print($text{'setup_done'});
	}

if ($parent) {
	# Re-save parent user, to update Webmin ACLs
	&refresh_webmin_user($parent);
	}

&sync_alias_virtuals(\%dom);
return @rvdoms;
}

# extract_cpanel_dir(file)
# Extracts a tar.gz file, and returns a status code and either the directory
# under which it was extracted, or an error message
sub extract_cpanel_dir
{
my ($file) = @_;
my $dir;
if ($main::cpanel_dir_cache{$file} && -d $main::cpanel_dir_cache{$file}) {
	# Use cached extract from this session
	return (1, $main::cpanel_dir_cache{$file});
	}
if (!-e $file) {
	return (0, "File $file does not exist");
	}
elsif (-d $file) {
	# Already extracted
	$dir = $file;
	}
else {
	$dir = &transname();
	mkdir($dir, 0700);
	my $err = &extract_compressed_file($file, $dir);
	if ($err) {
		return (0, $err);
		}
	}
$main::cpanel_dir_cache{$file} = $dir;
return (1, $dir);
}

# extract_cpanel_file(file)
# Extracts a .gz file, and returns the filename that is was extracted to
sub extract_cpanel_file
{
return undef if (!-r $_[0]);
my $temp = &transname();
my $qf = quotemeta($_[0]);
my $out = &backquote_command("gunzip -c $qf >".quotemeta($temp));
return $? ? undef : $temp;
}

# read_cpanel_userdata_file(file, [offset])
# Converts a userdata file into a perl hash ref
sub read_cpanel_userdata_file
{
my ($file, $pos) = @_;
$pos ||= 0;
my $lref = &read_file_lines($file, 1);
my $startindent = 0;
if ($lref->[$pos] =~ /^(\s+)/) {
	$startindent = length($1);
	}
my %rv;
my $lastname;
while($pos < scalar(@$lref)) {
	my $indent = 0;
	if ($lref->[$pos] =~ /^(\s+)/) {
		$indent = length($1);
		}
	if ($indent < $startindent) {
		# End of this section
		last;
		}
	elsif ($indent > $startindent) {
		# Start of a new section
		if ($lref->[$pos] =~ /^\s*\-/) {
			# Element in an array
			$rv{$lastname} ||= [ ];
			my ($subrv, $subpos) =
				&read_cpanel_userdata_file($file, $pos+1);
			push(@{$rv{$lastname}}, $subrv);
			$pos = $subpos - 1;
			}
		else {
			# Start of a section
			my ($subrv, $subpos) =
				&read_cpanel_userdata_file($file, $pos);
			$rv{$lastname} = $subrv;
			$pos = $subpos - 1;
			}
		}
	elsif ($lref->[$pos] =~ /^\s*(\S+):\s*(.*)/) {
		# A value in this section
		$rv{$1} = $2;
		$lastname = $1;
		}
	$pos++;
	}
return wantarray ? ( \%rv, $pos ) : \%rv;
}

sub create_addon_domains
{
my ($skip_missing_target, $userdir, $homesrc) = @_;

# Create addon domains as alias domains
opendir(VF, "$userdir/vf");
foreach my $vf (readdir(VF)) {
	my ($clash) = grep { $_->{'dom'} eq $vf } @rvdoms;
	next if ($clash);
	next if ($vf eq "." || $vf eq ".." || $clash);
	next if (!$addons{$vf});
	my $target = &get_domain_by("dom", $addons{$vf});
	next if (!$target && $skip_missing_target);
	&$first_print("Creating addon domain $vf ..");
	if (!$target) {
		&$second_print(".. skipping, as target $addons{$vf} does not exist");
		next;
		}
	if (&domain_name_clash($vf)) {
		&$second_print(".. the domain $vf already exists");
		next;
		}
	&$indent_print();
	my %alias = ( 'id', &domain_id(),
			 'dom', $vf,
			 'user', $dom{'user'},
			 'group', $dom{'group'},
			 'prefix', $dom{'prefix'},
			 'ugroup', $dom{'ugroup'},
			 'pass', $dom{'pass'},
			 'alias', $target->{'id'},
			 'aliasmail', 1,
			 'uid', $dom{'uid'},
			 'gid', $dom{'gid'},
			 'ugid', $dom{'ugid'},
			 'owner', "Migrated cPanel alias for $target->{'dom'}",
			 'email', $dom{'email'},
			 'name', 1,
			 'ip', $target->{'ip'},
			 'dns_ip', $target->{'dns_ip'},
			 'virt', 0,
			 'source', $dom{'source'},
			 'parent', $dom{'id'},
			 'template', $target->{'template'},
			 'reseller', $target->{'reseller'},
			 'nocreationmail', 1,
			 'nocopyskel', 1,
			);
	foreach my $f (@alias_features) {
		$alias{$f} = $target->{$f};
		}
	my $parentdom = $dom{'parent'} ? &get_domain($dom{'parent'})
					  : \%dom;
	$alias{'home'} = &server_home_directory(\%alias, $parentdom);
	&generate_domain_password_hashes(\%alias, 1);
	&complete_domain(\%alias);
	&create_virtual_server(\%alias, $parentdom,
			       $parentdom->{'user'});
	if ($alias{'mail'}) {
		&cpanel_migrate_mailboxes(
			$alias{'dom'}, \%alias, undef, $homesrc);
		}
	&$outdent_print();
	&$second_print($text{'setup_done'});
	push(@rvdoms, \%alias);

	# Create parked domain aliases
	&$first_print("Copying email aliases for addon domain $vf ..");
	my $acount = &cpanel_migrate_addon_aliases($vf, $userdir);
	&$second_print(".. done (migrated $acount aliases)");
	}
}

sub create_sub_domains
{
my ($skip_missing_target, $userdir) = @_;

# Create sub-domains as virtualmin sub-domains, from vf directory
opendir(VF, "$userdir/vf");
foreach my $vf (readdir(VF)) {
	my ($clash) = grep { $_->{'dom'} eq $vf } @rvdoms;
	next if ($vf eq "." || $vf eq ".." || $clash);
	next if ($addons{$vf});
	my (%subof, $subprefix);
	foreach my $rv (grep { !$_->{'subdom'} } @rvdoms) {
		if ($vf =~ /^(\S+)\.\Q$rv->{'dom'}\E$/) {
			$subprefix = $1;
			%subof = %$rv;
			last;
			}
		}
	if ((!%subof || !$subof{'dom'}) && $skip_missing_target) {
		# Skip silently
		next;
		}
	if (!%subof || !$subof{'dom'}) {
		&$first_print("Creating sub-domain $vf ..");
		&$second_print(".. skipping, as not a sub-domain of $dom or any other migrated domain");
		next;
		}
	if (&domain_name_clash($vf)) {
		&$first_print("Creating sub-domain $vf ..");
		&$second_print(".. the domain $vf already exists");
		next;
		}
	elsif ($subof{'alias'}) {
		# Sub-domain of an alias ... need to create as a sub-server
		# of the main domain
		&$first_print("Creating sub-server sub-domain $vf ..");
		&$indent_print();
		my %subs = ( 'id', &domain_id(),
				'dom', $vf,
				'user', $dom{'user'},
				'group', $dom{'group'},
				'prefix', $dom{'prefix'},
				'ugroup', $dom{'ugroup'},
				'pass', $dom{'pass'},
				'uid', $dom{'uid'},
				'gid', $dom{'gid'},
				'ugid', $dom{'ugid'},
				'owner', "Migrated cPanel sub-server",
				'email', $dom{'email'},
				'name', 1,
				'ip', $dom{'ip'},
				'dns_ip', $dom{'dns_ip'},
				'virt', 0,
				'source', $dom{'source'},
				'parent', $dom{'id'},
				'template', $dom{'template'},
				'reseller', $dom{'reseller'},
				'nocreationmail', 1,
				'nocopyskel', 1,
				);
		foreach my $f (@subdom_features) {
			if ($f ne "unix" && $f ne "webmin") {
				$subs{$f} = $dom{$f};
				}
			}
		my $parentdom = $dom{'parent'} ? &get_domain($dom{'parent'})
						  : \%dom;
		$subs{'home'} = &server_home_directory(\%subs, $parentdom);
		&generate_domain_password_hashes(\%subs, 1);
		&complete_domain(\%subs);
		&create_virtual_server(\%subs, $parentdom,
				       $parentdom->{'user'});

		# Copy files from parent
		my $sdsrc = "$ht/$subprefix";
		my $qsdsrc = quotemeta($sdsrc);
		my $sddst = &public_html_dir(\%subs);
		my $qsddst = quotemeta($sddst);
		my $out;
		if (-d $sdsrc) {
			&$first_print(
				"Copying web pages from $sdsrc to $sddst ..");
			&execute_command("cd $qsdsrc && ".
				 "(".&make_tar_command("cf", "-", ".").
				 " | (cd $qsddst && ".
				 &make_tar_command("xf", "-")."))",
				 undef, \$out, \$out);
			if ($?) {
				&$second_print(".. copy failed :<tt>$out</tt>");
				}
			else {
				&$second_print(".. done");
				}
			&set_home_ownership(\%subs);
			}

		&$outdent_print();
		&$second_print($text{'setup_done'});
		push(@rvdoms, \%subs);
		}
	else {
		# Sub-domain of a regular domain
		&$first_print("Creating sub-domain $vf ..");
		&$indent_print();
		my %subd = ( 'id', &domain_id(),
				'dom', $vf,
				'user', $dom{'user'},
				'group', $dom{'group'},
				'prefix', $dom{'prefix'},
				'ugroup', $dom{'ugroup'},
				'pass', $dom{'pass'},
				'subdom', $subof{'id'},
				'subprefix', $subprefix,
				'uid', $dom{'uid'},
				'gid', $dom{'gid'},
				'ugid', $dom{'ugid'},
				'owner', "Migrated cPanel sub-domain",
				'email', $dom{'email'},
				'name', 1,
				'ip', $dom{'ip'},
				'dns_ip', $dom{'dns_ip'},
				'virt', 0,
				'source', $dom{'source'},
				'parent', $dom{'id'},
				'template', $dom{'template'},
				'reseller', $dom{'reseller'},
				'nocreationmail', 1,
				'nocopyskel', 1,
				'no_tmpl_aliases', 1,
				);
		$subd{'mail'} = 0;
		foreach my $f (@subdom_features) {
			if ($f eq 'mail') {
				$subd{$f} = $subof{$f} && -r "$userdir/va/$vf";
				}
			elsif ($f eq 'ssl') {
				# Off for sub-domains, for now
				$subd{$f} = 0;
				}
			else {
				$subd{$f} = $subof{$f};
				}
			}
		my $parentdom = $dom{'parent'} ? &get_domain($dom{'parent'})
						  : \%dom;
		$subd{'home'} = &server_home_directory(\%subd, $parentdom);
		&generate_domain_password_hashes(\%subd, 1);
		&complete_domain(\%subd);

		# Extract correct sub-domain root dir
		my $userdata = &read_cpanel_userdata_file(
					"$userdir/userdata/$vf");
		if ($userdata->{'documentroot'} =~
		    /^\/home\/([^\/]+)\/public_html\/(.*)/) {
			$subd{'public_html_dir'} =
				"../../$subof{'public_html_dir'}/$2"; 
			$subd{'public_html_path'} =
				"$subof{'public_html_path'}/$2";
			}

		# Set cgi directories to cpanel standard
		$subd{'cgi_bin_dir'} =
			"../../$subof{'public_html_dir'}/$subprefix/cgi-bin";
		$subd{'cgi_bin_path'} =
			"$subof{'public_html_path'}/$subprefix/cgi-bin";

		&create_virtual_server(\%subd, $parentdom,
				       $parentdom->{'user'});
		&create_standard_directory_for_domain(
		    \%subd, $subd{'cgi_bin_path'}, $tmpl->{'web_html_perms'}); 

		# Cpanel sub-domains always seem to forward mail to the parent
		if ($subd{'mail'}) {
			my $virt = { 'from' => "\@$vf",
					'to' => [ "%1\@".$subof{'dom'} ] };
			&create_virtuser($virt);
			}

		&$outdent_print();
		&$second_print($text{'setup_done'});
		push(@rvdoms, \%subd);
		}
	}
closedir(VF);
}

# cpanel_migrate_mailboxes(domain-name, &domain, &user-map, homesrc)
# Re-create mailbox users from a cPanel backup
sub cpanel_migrate_mailboxes
{
my ($dom, $d, $usermap, $homesrc) = @_;
&foreign_require("mailboxes");
&$first_print("Re-creating mail users for $dom ..");
my $mcount = 0;
my (%pass, %quota);
local $_;
open(SHADOW, "<$homesrc/etc/$dom/shadow");
while(<SHADOW>) {
	s/\r|\n//g;
	my ($suser, $spass) = split(/:/, $_);
	$pass{$suser} = $spass;
	}
close(SHADOW);
local $_;
my $bsize = &quota_bsize("home");
open(QUOTA, "<$homesrc/etc/$dom/quota");
while(<QUOTA>) {
	s/\r|\n//g;
	my ($quser, $qquota) = split(/:/, $_);
	$quota{$quser} = $bsize ? int($qquota/$bsize) : 0;
	}
close(QUOTA);
local $_;
open(PASSWD, "<$homesrc/etc/$dom/passwd");
while(<PASSWD>) {
	# Create the user
	s/\r|\n//g;
	my ($muser, $mdummy, $muid, $mgid, $mreal, $mdir, $mshell) =
		split(/:/, $_);
	next if (!$muser);
	$muser = &remove_userdom($muser, $d);
	next if ($muser =~ /_logs$/);		# Special logs user
	next if ($muser eq $user && !$parent);	# Domain owner
	my $uinfo = &create_initial_user($d);
	$uinfo->{'user'} = &userdom_name(lc($muser), $d);
	$uinfo->{'pass'} = $pass{$muser};
	$uinfo->{'uid'} = &allocate_uid(\%taken);
	$uinfo->{'gid'} = $d->{'gid'};
	$uinfo->{'real'} = $mreal;
	$uinfo->{'home'} = "$d->{'home'}/$config{'homes_dir'}/".
			   lc($muser);
	$uinfo->{'shell'} = $nologin_shell->{'shell'};
	$uinfo->{'email'} = lc($muser)."\@$dom";
	$uinfo->{'quota'} = $quota{$muser};
	&create_user_home($uinfo, $d, 1);
	&create_user($uinfo, $d);
	$taken{$uinfo->{'uid'}}++;
	my ($crfile, $crtype) = &create_mail_file($uinfo, $d);

	# Move his mail files
	my $mailsrc = "$homesrc/mail/$dom/$muser";
	my $sfdir = $mailboxes::config{'mail_usermin'};
	my $sftype = $sfdir eq 'Maildir' ? 1 : 0;
	my $sfpath = "$uinfo->{'home'}/$sfdir";
	if (!-d $sfpath && !$sftype) {
		# Create ~/mail if needed
		&make_dir($sfpath, 0755);
		&set_ownership_permissions(
		     $uinfo->{'uid'}, $uinfo->{'gid'}, undef, $sfpath);
		}
	if (-d "$mailsrc/cur" || -d "$mailsrc/new") {
		# Mail directory is in Maildir format, and sub-folders
		# are in Maildir++
		my $srcfolder = { 'type' => 1,
				     'file' => $mailsrc };
		my $dstfolder = { 'file' => $crfile,
				     'type' => $crtype };
		&mailboxes::mailbox_move_folder($srcfolder, $dstfolder);
		&set_mailfolder_owner($dstfolder, $uinfo);
		&mailboxes::mailbox_uncompress_folder($dstfolder)
			if (defined(&mailboxes::mailbox_uncompress_folder));
		opendir(DIR, $mailsrc);
		while(my $mf = readdir(DIR)) {
			next if ($mf eq "." || $mf eq ".." ||
				 $mf !~ /^\./);
			my $srcfolder = { 'type' => 1,
				'file' => "$mailsrc/$mf" };
			# Remove . if destination is not Maildir++
			$mf =~ s/^\.// if (!$sftype);
			my $dstfolder = { 'type' => $sftype,
					     'file' => "$sfpath/$mf" };
			&mailboxes::mailbox_move_folder($srcfolder,
							$dstfolder);
			&set_mailfolder_owner($dstfolder, $uinfo);
			&mailboxes::mailbox_uncompress_folder($dstfolder)
				if (defined(&mailboxes::mailbox_uncompress_folder));
			}
		closedir(DIR);
		}
	elsif (-d "$mailsrc/storage") {
		# Mail directory is in mdbox format .. convert to mbox then move
		my $mdtemp = &transname();
		&copy_source_dest($mailsrc, $mdtemp);
		my $temp = &transname();
		&make_dir($temp, 0755);
		my $err;
		&execute_command("chown -R ".quotemeta($uinfo->{'user'}).": ".quotemeta($mdtemp)." ".quotemeta($temp), undef, \$err, \$err);
		my $out = &backquote_command("dsync -u ".
			quotemeta($uinfo->{'user'})." -o ".
			quotemeta("mail_location=mdbox:$mdtemp").
			" backup mbox:".quotemeta($temp)." 2>&1");
		&unlink_file($mdtemp);
		opendir(DIR, $temp);
		while(my $mf = readdir(DIR)) {
			next if ($mf =~ /^\./);
			my $srcfolder = { 'type' => 0,
					     'file' => "$temp/$mf" };
			my $dstfolder;
			if ($mf eq "inbox") {
				$dstfolder = { 'file' => $crfile,
					       'type' => $crtype };
				}
			else {
				# Copying an extra folder - use
				# Maildir++ name if dest folders are
				# under ~/Maildir
				$mf = ".$mf" if ($sftype);
				$dstfolder = { 'type' => $sftype,
					'file' => "$sfpath/$mf" };
				}
			&mailboxes::mailbox_move_folder($srcfolder,
							$dstfolder);
			&set_mailfolder_owner($dstfolder, $uinfo);
			}
		closedir(DIR);
		&unlink_file($temp);
		}
	elsif (!-d $mailsrc) {
		# Assume that mail files are mbox formatted
		opendir(DIR, $mailsrc);
		while(my $mf = readdir(DIR)) {
			next if ($mf =~ /^\./);
			my $srcfolder = { 'type' => 0,
					     'file' => "$mailsrc/$mf" };
			my $dstfolder;
			if ($mf eq "inbox") {
				$dstfolder = { 'file' => $crfile,
					       'type' => $crtype };
				}
			else {
				# Copying an extra folder - use
				# Maildir++ name if dest folders are
				# under ~/Maildir
				$mf = ".$mf" if ($sftype);
				$dstfolder = { 'type' => $sftype,
					'file' => "$sfpath/$mf" };
				}
			&mailboxes::mailbox_move_folder($srcfolder,
							$dstfolder);
			&set_mailfolder_owner($dstfolder, $uinfo);
			}
		closedir(DIR);
		}
	$mcount++;
	if ($usermap) {
		$usermap->{$muser} = $uinfo;
		}
	}
close(PASSWD);
&$second_print(".. done (migrated $mcount mail users)");
}

# cpanel_migrate_addon_aliases(domain, userdir)
sub cpanel_migrate_addon_aliases
{
my ($vf, $userdir) = @_;
my $acount = 0;
my %gotvirt = map { $_->{'from'}, $_ } &list_virtusers();
open(VA, "<$userdir/va/$vf");
while(<VA>) {
	s/\r|\n//g;
	s/^\s*#.*$//;
	if (/^(\S+):\s*(.*)$/) {
		my ($name, $v) = ($1, $2);
		next if (!$name);
		my @values;
		if ($v !~ /,/ && $v !~ /"/) {
			# A single destination, not quoted!
			@values = ( $v );
			}
		else {
			# Comma-separated alias destinations
			while($v =~ /^\s*,?\s*"(\|)([^"]+)"(.*)$/ ||
			      $v =~ /^\s*,?\s*()"([^"]+)"(.*)$/ ||
			      $v =~ /^\s*,?\s*(\|)"([^"]+)"(.*)$/ ||
			      $v =~ /^\s*,?\s*()([^,\s]+)(.*)$/) {
				push(@values, $1.$2);
				$v = $3;
				}
			}
		my $mailman = 0;
		foreach my $v (@values) {
			if ($v =~ /:fail:\s+(.*)/) {
				# Fix bounce alias
				$v = "BOUNCE $1";
				}
			my ($atype, $aname) = &alias_type($v, $name);
			if ($atype == 4 && $aname =~ /autorespond\s+(\S+)\@(\S+)\s+(\S+)/) {
				# Turn into Virtualmin auto-responder
				$v = "| $module_config_directory/autoreply.pl $3/$name $1";
				&set_ownership_permissions(
					undef, undef, 0755,
					$3, "$3/$name");
				}
			elsif ($atype == 4 && $aname =~ /mailman/) {
				$mailman++;
				}
			}
		# Don't create aliases for mailman lists
		next if ($mailman || $name =~ /^owner-/);

		# Already done a domain forward
		next if ($name =~ /^\*/);

		# Just create an alias
		if ($name !~ /\@/) {
			$name .= "\@".$vf;
			}
		my $virt = { 'from' => $name =~ /^\*/ ? "\@".$vf
							 : $name,
				'to' => \@values };
		my $clash = $gotvirt{$virt->{'from'}};
		&delete_virtuser($clash) if ($clash);
		&create_virtuser($virt);
		$acount++;
		}
	}
close(VA);
return $acount;
}

# cpanel_migrate_dns_zone(&domain, dname)
# Migrate DNS records for a cPanel domain
sub cpanel_migrate_dns_zone
{
my ($d, $dom) = @_;
my ($zsrc) = glob("$root/backup*/dnszones/$dom.db");
if (!$zsrc && $d->{'dns'} && -d $daily) {
	&require_bind();
	my ($ok, $named) = &extract_cpanel_dir(
				"$daily/dirs/_var_named.tar.gz");
	my $zsrcfile = &bind8::find_value("file", $zone->{'members'});
	if (-r "$named/$zsrcfile") {
		$zsrc = "$named/$zsrcfile";
		}
	}
if ($zsrc) {
	&$first_print("Copying and fixing DNS records for $dom ..");
	my $zdstfile = &get_domain_dns_file_from_bind($d);
	&copy_source_dest($zsrc, &bind8::make_chroot($zdstfile));
	my ($recs, $zdstfile) = &get_domain_dns_records_and_file($d, 1);
	foreach my $r (@$recs) {
		my $change = 0;
		if (($r->{'name'} eq $dom."." ||
		     $r->{'name'} eq "www.".$dom."." ||
		     $r->{'name'} eq "ftp.".$dom."." ||
		     $r->{'name'} eq "mail.".$dom.".") &&
		    $r->{'type'} eq 'A') {
			# Fix IP in domain record
			$r->{'values'} = [ $d->{'dns_ip'} || $d->{'ip'} ];
			$change++;
			}
		elsif ($r->{'name'} eq $dom."." &&
		       $r->{'type'} eq 'NS') {
			# Set NS record to this server
			my $master = $bconfig{'default_prins'} ||
					&get_system_hostname();
			$master .= "." if ($master !~ /\.$/);
			$r->{'values'} = [ $master ];
			$change++;
			}
		if ($change) {
			&modify_dns_record($recs, $zdstfile, $r);
			}
		}
	&post_records_change($d, $recs, $zdstfile);
	&refresh_dkim_dns($d);
	&$second_print(".. done");
	&register_post_action(\&restart_bind);
	}
}

# get_cpanel_db_list(file, user, origuser)
# Reads from mysql.sql the list of DBs granted to the domain user
sub get_cpanel_db_list
{
my ($file, $user1, $user2) = @_;
my $lref = &read_file_lines($file, 1);
my @rv;
foreach my $l (@$lref) {
	if ($l =~ /^GRANT ALL PRIVILEGES ON `([^`]+)`\.\* TO '([^']+)'/ &&
	    ($2 eq $user1 || $2 eq $user2)) {
		my $db = $1;
		$db =~ s/\\(.)/$1/g;
		push(@rv, $db);
		}
	}
return @rv;
}

1;
