#!/usr/bin/perl
use strict;

=head1 NAME

migrate.pl

=head1 SYNOPSIS

migrate.pl - migrate email domains from one Mail Toaster to another.


=head1 VERSION

This is version .07.


=head1 SYNOPSIS

  ./migrate.pl


=head1 DESCRIPTION



=head1 CHANGES

July 7, 06 - merged in some automation additions. use with caution.

=head1 TODO

Create the new users using vadduser -s. Will be more reliable in fringe cases where someone tampered with the vpopmail tables, as well as working on systems where password learning feature is disabled.

=cut

use lib 'lib';
use Mail::Toaster::Mysql;
use Mail::Toaster::Utility;

my $toaster = Mail::Toaster->new();
my $util  = toaster->get_util();
my $mysql = Mail::Toaster::Mysql->new( toaster => $toaster );

my ($type, $domain, $newhost) = @ARGV;
my $vpopdir = "/usr/local/vpopmail";
my $exists = 0;

unless ( $newhost && $newhost ne "" ) { _usage($domain, $newhost); };

if    ($type eq "user")   { migrate_user    ($domain, $newhost) }
elsif ($type eq "domain") { migrate_vpopmail($domain, $newhost) }
else                      { _usage          ($domain, $newhost) }


sub migrate_vpopmail {
	my ($domain, $host) = @_;

	my @users = get_userlist_from_mysql($domain);

	unless ( @users > 0 ) {
		warn "no users found for $domain!\n";
		exit 0;
	};

	print "run the following commands on $host:\n";

	my $postmaster = add_postmaster(undef, $domain, $host, @users);  # show the domain creation cmd
	add_emails(undef, $domain, $host, @users);    # the email accounts to add

	if ( $util->yes_or_no( "\nshall I try it for you?", force=>1) )
	{
		# does the domain directory exist on the other end?
		unless ( $util->syscmd("ssh $host test -d $vpopdir/domains/$domain", verbose=>0)) {
			$exists++;
			print "target directory already exists on $host!\n\tmoving it out of the way...";
			$util->syscmd("ssh $host mv $vpopdir/domains/$domain $vpopdir/domains/$domain.bak", verbose=>0);
			print "done.\n";
		};

		print "creating the domain and email accounts on $host.\n";
		$postmaster = add_postmaster(1, $domain, $host, @users);     # create the domain
		if ( ! $postmaster ) {                                   # add the rest of the accounts.
			die "failed to add postmaster account! I cannot continue.\n";
		};
		add_emails(1, $domain, $host, @users);
	};

	# this test could/should be automated!
	unless ( $util->yes_or_no( "\nhas the previous task completed successfully (w/o errors)? ", force=>1) ) {
		die "ok, bombing out!\n";
	};

	# get list of users and domains
	# create domains and users on the new server
	add_domain_to_smtproutes($domain, $host);

	# migrate contents of mailboxes to new server
	rsync_mailboxes_to_new($domain, $host);

	# test local email forwarding
	send_test_email("old", $domain);  # should get forwarded to new server via smtproutes

	# test remove email delivery
	send_test_email($host, $domain);

	# delete the domain locally
	delete_domain($domain);

	# make sure we can still accept email for it
	verify_domain_exists_in_rcpthosts($domain);

	# forward email for this domain to the new server
	verify_domain_exists_in_smtproutes($host, $domain);
};

sub add_emails
{
	my ($do, $domain, $host, @r) = @_;

    my $vadduser = "$vpopdir/bin/vadduser";
	foreach (@r) {
		next if ($_->{'pw_name'} eq "postmaster");
		#print "name: $_->{'pw_name'} \n";

		$vadduser .= "-n" if $_->{'pw_clear_passwd'} eq "";

		my $cmd = "$vadduser '$_->{'pw_name'}\@$_->{'pw_domain'}' '$_->{'pw_clear_passwd'}'";
		print " $cmd\n";

		if ( $do ) {
			$util->syscmd("ssh $host $cmd", verbose=>0);
		}
	};
};

sub add_postmaster
{
	my ($do, $domain, $host, @r) = @_;

	foreach (@r) {
		next unless ($_->{'pw_name'} eq "postmaster");    # find the postmaster account
		my $cmd = "$vpopdir/bin/vadddomain '$domain' '$_->{'pw_clear_passwd'}'";
		print "  $cmd\n";
		if ( $do ) {
			$util->syscmd("ssh $host $cmd", verbose=>0); # add the domain on the new server.
			return 1;
		};
	};

	return 0;
}

sub migrate_user
{
	my ($domain, $host) = @_;

	my $homedir = (getpwnam($domain))[7];
	die "no homedir for $domain" unless -d $homedir;

	print "\nrsync -avn -e ssh $homedir $host:/home/\n";

	print "add user with:\n";

	my $r = `grep $domain /etc/master.passwd`;

	print "$r\n";
}

sub get_userlist_from_mysql
{
	my $domain = shift;

	_check_my_cnf();
	my $dot = $mysql->parse_dot_file(".my.cnf", "[mysql]", 0);

	my ($dbh, $dsn, $drh) = $mysql->connect($dot, 0, 1 );

	my $query = "select * from vpopmail.vpopmail where pw_domain=\"$domain\"";

	return $mysql->get_hashes($dbh, $query, 1);
};

sub add_domain_to_smtproutes
{
	my ($domain, $host) = @_;

	print "checking smtproutes for $domain...";

	if  ( ! `grep $domain /var/qmail/control/smtproutes` ) {
		print "missing.\nadding $domain to smtproutes...";
		$util->file_write("/var/qmail/control/smtproutes", lines=>["$domain:$host"], append=>1);
	};

	if ( `grep $domain /var/qmail/control/smtproutes`) {
		print "ok.\n";
		return 1;
	};

	print "FAILED.\n";
	return 0;
}

sub send_test_email
{
	my ($host, $domain) = @_;

	$host ? print "on this (the old server), " : print "on the server $host, ";
	print "send an email to postmaster\@$domain with:\n
mail postmaster\@$domain
test email
testing
.\n\nand make sure it gets delivered properly.";

	unless ( $util->yes_or_no( "\nhave you completed the previous task successfully?", force=>1) ) {
		die "ok, bombing out!\n";
	};
};

sub delete_domain
{
	my ($domain) = @_;

	print "now delete the domain from the (old) server with:\n";
	print "\n  $vpopdir/bin/vdeldomain $domain";

	if ( $util->yes_or_no( "\nshall I try it for you?", force=>1) ) {
		$util->syscmd("$vpopdir/bin/vdeldomain $domain", verbose=>0);
	};

	unless ( $util->yes_or_no( "\nhave you completed the previous task successfully?", force=>1) ) {
		die "ok, bombing out!\n";
	};
};

sub verify_domain_exists_in_rcpthosts {
	my ($domain) = @_;

	print "checking rcpthosts for $domain...";
	my $r = `grep $domain /var/qmail/control/rcpthosts`;
	$r ? print "ok.\n" : print "FAILED.\n";

	unless ($r) {
		if ($util->yes_or_no( "shall I fix that for you?", force=>1) ){
			$util->file_write("/var/qmail/control/rcpthosts", lines=>[$domain], append=>1);
		};
	};
};

sub verify_domain_exists_in_smtproutes
{
	my ($host, $domain) = @_;

	print "checking smtproutes for $domain...";
	my $r = `grep $domain /var/qmail/control/smtproutes`;
	$r ? print "ok.\n" : print "FAILED.\n";

	unless ($r) {
		if ($util->yes_or_no( "shall I do that for you?", force=>1) ){
			$util->file_write("/var/qmail/control/smtproutes", lines=>["$domain:$host"], append=>1);
		};
	};
};

sub rsync_mailboxes_to_new
{
	my ($domain, $host) = @_;

	if ($exists) {
		print "executing commands on $host\n";
		my $cleanup = "rm -r $vpopdir/domains/$domain";
		my $clean2  = "mv $vpopdir/domains/$domain.bak $vpopdir/domains/$domain";
		print "   $cleanup\n";
		$util->syscmd("ssh $host $cleanup", verbose=>0);
		print "   $clean2\n";
		$util->syscmd("ssh $host $clean2", verbose=>0);
	};

	my $cmd = "rsync -av -e ssh --delete $vpopdir/domains/$domain $host:$vpopdir/domains/";

	print "rsync the maildirs from this (old) server to the new one with:\n\n   $cmd \n\n";

	if ( $util->yes_or_no( "\nshall I try it for you?", force=>1) ) {
		$util->syscmd($cmd, verbose=>0);
	};

	unless ( $util->yes_or_no( "\nhas the previous task completed successfully?", force=>1) ) {
		die "ok, bombing out!\n";
	};
};

sub _usage {
	my ($dom, $host) = @_;
	print "
This script must be run on the OLD server and it will copy files to new server. You'll need two things set up.

	1. rsync installed on both systems
	2. SSH key based authentication for root from old to new

The former you can figure out yourself. The latter is a little harder, but not terribly difficult. First, you need to have SSH as root enabled on the new server. Do NOT allow root authentication with passwords. Accomplish this by setting PermitRootLogin to without-password in your sshd_config file and restarting the sshd daemon. Then generate a ssh key on the old server (ssh-keygen -d) and add the public key (.ssh/id_dsa.pub) to ~/.ssh/authorized_keys on the new server.

	usage: $0 user   username    newhost.fqdn.com  (user   = /etc/passwd username)
	       $0 domain example.com newhost.fqdn.com  (domain = vpopmail domain name)

";

	die "\n" unless ($dom and $host);
}

sub _check_my_cnf
{

    my ($homedir) = (getpwuid ($<))[7];

	unless ( $util->is_readable( "$homedir/.my.cnf" ) )
	{
		print "\nHey bubba, I need to connect to your MySQL server as the root or vpopmail user. To facilitate this, I expect a configured ~/.my.cnf file. This file format is the same as the mysql client uses and properly configured might look like this:

[mysql]
user       = vpopmail
pass       = superSecretReallySecurePasswordBecauseImSmart

Create that file now, make sure it's readable by the user only (chmod 0400 ~/.my.cnf), and then rerun this script.

";
		die "\n";
	};
}

exit 0;


=head1 DEPENDENCIES

  Mail Toaster


=head1 BUGS AND LIMITATIONS

    Needs more and better documentation, and explanations of what it is doing

    Needs to migrate valias entries from MySQL


Please report any bugs or feature requests to
C<bug-mail-toaster@rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org>.


=head1 AUTHOR

Matt Simerson  C<< <matt@tnpi.net> >>