mail_data_migrator.pl [plain text]
use strict;
$| = 1;
my $CAT = "/bin/cat";
my $CP = "/bin/cp";
my $MV = "/bin/mv";
my $RM = "/bin/rm";
my $DSCL = "/usr/bin/dscl";
my $DU = "/usr/bin/du";
my $ECHO = "/bin/echo";
my $GREP = "/usr/bin/grep";
my $CHOWN = "/usr/sbin/chown";
my $LAUNCHCTL = "/bin/launchctl";
my $POSTCONF = "/usr/sbin/postconf";
my $MKDIR = "/bin/mkdir";
my $SERVER_ADMIN = "/usr/sbin/serveradmin";
my $PLIST_BUDDY = "/usr/libexec/PlistBuddy";
my $CVT_MAIL_DATA = "/usr/bin/cvt_mail_data";
my $TAR = "/usr/bin/tar";
my $MIGRATION_LOG= "/Library/Logs/MailMigration.log";
my @g_clean_partitions = ();
my $g_migration_plist = "/var/db/.mailmigration.plist";
my $g_imapd_conf = "/etc/imapd.conf";
my $g_source_root = "";
my $g_target_root = "";
my $g_source_version = "";
my $g_sl_src = 0; my $g_db_path = "/Previous System/private/var/imap";
my $g_purge = 0;
my $SYS_VERS = "0"; my $SYS_MAJOR = "0"; my $SYS_MINOR = "0"; my $SYS_UPDATE = "-"; my $SRV_VERS = "0"; my $SRV_MAJOR = "0"; my $SRV_MINOR = "0"; my $SRV_UPDATE = "-"; my $MIN_VER = "10.4"; my $MAX_VER = "10.8"; my $TARGET_VER = "10.7";
my $DEBUG = 0;
my $FUNC_LOG = 0;
use Foundation;
use File::Copy;
use File::Basename;
if ($ENV{DEBUG} eq 1) {
$DEBUG = '1'; }
if ($ENV{FUNC_LOG} eq 1) {
$FUNC_LOG = '1'; }
open (LOG_FILE, ">> ${MIGRATION_LOG}" ) or die("$MIGRATION_LOG: $!\n");
do_data_migration();
qx( ${RM} -f "${g_migration_plist}" 2>&1 >> ${MIGRATION_LOG} );
exit();
sub path_exists ($)
{
if (${FUNC_LOG}) {
print( "::path_exists : S\n"); }
my $exists = 0;
my ($in_path) = @_;
if (${DEBUG}) {
printf( "- path: %s\n", "${in_path}"); }
if (-e "${in_path}") {
$exists = 1;
if (${DEBUG}) {
printf( "-- Exists\n"); }
} else {
if (${DEBUG}) {
printf( "-- Does not exist\n"); }
}
if (${FUNC_LOG}) {
print( "::path_exists : E\n"); }
return( $exists );
}
sub is_valid_version ()
{
if (${FUNC_LOG}) {
print("::is_valid_version : S\n"); }
my ($valid) = 0;
if ((substr(${g_source_version}, 0, 4) >= ${MIN_VER}) && (substr(${g_source_version}, 0, 4) < ${MAX_VER})) {
$valid = 1;
if (${DEBUG}) {
printf( "- valid: ${g_source_version}\n");}
if (substr(${g_source_version}, 0, 4) eq "10.6" or substr(${g_source_version}, 0, 4) eq "10.7") {
$g_sl_src = 1;
}
} else {
printf ("- Version supplied was not valid: %s\n", $g_source_version);
}
if (${FUNC_LOG}) {
print( "::is_valid_version : E\n"); }
return( ${valid} );
}
sub do_dovecot_data_migration ($$)
{
my($src_path) = $_[0];
my($dst_path) = $_[1];
if ( ${FUNC_LOG} ) {
printf( "::do_dovecot_data_migration : S\n" ); }
if ( path_exists( "${g_imapd_conf}" ) ) {
qx( ${RM} -rf "${g_imapd_conf}" >> ${MIGRATION_LOG} ); }
open ( IMAPD_CONF, ">> ${g_imapd_conf}" ) or die("g_imapd_conf: $!\n");
print IMAPD_CONF "admins: _cyrus\n";
print IMAPD_CONF "postmaster: postmaster\n";
print IMAPD_CONF "configdirectory: ${g_db_path}\n";
print IMAPD_CONF "defaultpartition: default\n";
print IMAPD_CONF "partition-default: ${src_path}\n";
close( IMAPD_CONF );
my $cvt_flag = "-m";
if ( $g_purge == 0 ) {
$cvt_flag = "-c";
}
chdir( "${src_path}/user" ) or die "can't chdir ${src_path}/user: $!";
my $cyrus_bins;
if ( path_exists( "${g_source_root}/usr/bin/cyrus/bin" ) ) {
$cyrus_bins = "${g_source_root}/usr/bin/cyrus/bin";
} else {
$cyrus_bins = "/usr/bin/cyrus/bin";
}
my @mail_accts = <*>;
foreach my $user_id (@mail_accts) {
print LOG_FILE "Migrating user account: ${user_id} to: ${dst_path}\n";
my $user_tag = substr( $user_id, 0, 1 );
if ( ${DEBUG} ) {
printf( "- verifying user seen db: \"${g_db_path}/user/${user_tag}/${user_id}.seen\"\n" ); }
if ( path_exists( "${g_db_path}/user/${user_tag}/${user_id}.seen" ) ) {
qx( "${cyrus_bins}/cvt_cyrusdb" -C "${g_imapd_conf}" "${g_db_path}/user/${user_tag}/${user_id}.seen" skiplist "${g_db_path}/user/${user_tag}/${user_id}.seen.flat" flat >> "${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
if ( ${DEBUG} ) {
printf( "- ${CVT_MAIL_DATA} -g ${cvt_flag} -d ${g_db_path} -s ${src_path} -t ${dst_path} -a ${user_id}\n" );
qx( ${CVT_MAIL_DATA} -g ${cvt_flag} -d "${g_db_path}" -s "${src_path}" -t "${dst_path}" -a ${user_id} >> "${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
} else {
qx( ${CVT_MAIL_DATA} ${cvt_flag} -d "${g_db_path}" -s "${src_path}" -t "${dst_path}" -a ${user_id} >> "${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
}
qx( ${RM} "${g_db_path}/user/${user_tag}/${user_id}.seen.flat" >> "${MIGRATION_LOG}" );
} else {
if ( ${DEBUG} ) {
printf( "- ${CVT_MAIL_DATA} -g ${cvt_flag} -d ${g_db_path} -s ${src_path} -t ${dst_path} -a ${user_id}\n" );
qx( ${CVT_MAIL_DATA} -g ${cvt_flag} -d "${g_db_path}" -s "${src_path}" -t "${dst_path}" -a ${user_id} >> "${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
} else {
qx( ${CVT_MAIL_DATA} ${cvt_flag} -d "${g_db_path}" -s "${src_path}" -t "${dst_path}" -a ${user_id} >> "${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
}
}
if ( path_exists( "${dst_path}/${user_id}" ) ) {
print LOG_FILE "Warning: Found user id path: ${dst_path}/${user_id}\n";
my $i = 0;
for ( $i = 0; $i < 3; $i++ ) {
my $a_user_guid = qx( ${CVT_MAIL_DATA} -i ${user_id} );
chomp( ${a_user_guid} );
if ( substr(${a_user_guid}, 0, 13) eq "No GUID found" ) {
sleep(30);
} else {
qx( /bin/mv "${dst_path}/${user_id}" "${dst_path}/${a_user_guid}" );
last;
}
}
}
my $user_guid = qx( ${CVT_MAIL_DATA} -i ${user_id} );
chomp( ${user_guid} );
if ( substr(${user_guid}, 0, 13) eq "No GUID found" ) {
print LOG_FILE "Warning: No GUID found for user ${user_id} (mailbox path: ${dst_path}/${user_id})\n";
qx( chown -R _dovecot:mail "${dst_path}/${user_id}" >>"${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
} else {
qx( chown -R _dovecot:mail "${dst_path}/${user_guid}" >>"${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
print LOG_FILE "Setting account flag to migrated for: ${user_id}, (${user_guid})\n";
qx( ${CVT_MAIL_DATA} -j ${user_guid} );
}
}
if ( path_exists( "${g_imapd_conf}" ) ) {
qx( ${RM} -rf "${g_imapd_conf}" >> ${MIGRATION_LOG} ); }
if ( ${FUNC_LOG} ) {
printf( "::do_dovecot_data_migration : S\n" ); }
}
sub do_data_migration()
{
my $service_state = "SERVICE_DISABLE";
if (!path_exists("${g_migration_plist}")) {
print LOG_FILE "No migration data file: ${g_migration_plist}\n";
return;
} else {
copy("${g_migration_plist}", "${g_migration_plist}" . ".default");
}
$service_state = qx( ${PLIST_BUDDY} -c 'Print :serviceState' "/etc/MailServicesOther.plist" );
chomp(${service_state});
if ( ${service_state} eq "SERVICE_ENABLE" ) {
qx( ${PLIST_BUDDY} -c 'Set :serviceState string SERVICE_DISABLE' ${g_migration_plist} );
qx( ${SERVER_ADMIN} start mail >> ${MIGRATION_LOG} );
}
$g_source_version = qx(${PLIST_BUDDY} -c 'Print :sourceVersion' ${g_migration_plist});
chomp(${g_source_version});
if (!is_valid_version()) {
print LOG_FILE "Unsupported migration version: ${g_source_version}\n";
return;
}
$g_source_root = qx(${PLIST_BUDDY} -c 'Print :sourceRoot' ${g_migration_plist});
chomp(${g_source_root});
my $tries = 0;
while (!path_exists("${g_source_root}")) {
print LOG_FILE "Missing or invalid source path: ${g_source_root}\n";
return if ++$tries >= 15;
sleep 60;
}
print LOG_FILE "Found source path: ${g_source_root}\n";
$g_target_root = qx(${PLIST_BUDDY} -c 'Print :targetRoot' ${g_migration_plist});
chomp(${g_target_root});
$tries = 0;
while (!path_exists("${g_target_root}")) {
print LOG_FILE "Missing or invalid target root: ${g_target_root}\n";
return if ++$tries >= 15;
sleep 60;
}
print LOG_FILE "Found target path: ${g_target_root}\n";
$g_purge = qx(${PLIST_BUDDY} -c 'Print :purge' ${g_migration_plist});
chomp(${g_purge});
if (${g_sl_src}) {
return;
}
my $db_path = qx(${PLIST_BUDDY} -c 'Print :config_directory' ${g_migration_plist});
chomp($db_path);
if (!defined($db_path) || $db_path eq "") {
$g_db_path = "$g_source_root/private/var/imap";
} elsif ($g_source_root eq "/Previous System" && $db_path eq "/var/imap") {
$g_db_path = "/Previous System/private" . $db_path;
} elsif ($db_path !~ m,^/Volumes,) {
$g_db_path = $g_source_root . $db_path;
} else {
$g_db_path = $db_path;
}
print LOG_FILE "Config path: $g_db_path\n";
my $default_partition = qx(${PLIST_BUDDY} -c 'Print :default_partition' ${g_migration_plist});
chomp(${default_partition});
my $volume_tag = substr( ${default_partition}, 0, 8 );
my $src_path;
if ( ("${g_source_root}" eq "/Previous System") && ($default_partition eq "/var/spool/imap") ) {
$src_path = "/Previous System/private" . ${default_partition};
} elsif ($volume_tag ne "/Volumes") {
$src_path = $g_source_root . $default_partition;
} else {
$src_path = "${default_partition}";
}
$tries = 0;
while (!path_exists("${src_path}/user")) {
print LOG_FILE "Data store directory does not exist at: ${src_path}/user\n";
last if ++$tries >= 15;
sleep 60;
}
if (path_exists("${src_path}/user")) {
my $dst_path;
if ($default_partition eq "/var/spool/imap") {
$dst_path = "/Library/Server/Mail/Data/mail";
} else {
$dst_path = "${default_partition}/dovecot";
}
if ( ${DEBUG} ) {
printf( "- verifying data destination directory: \"$dst_path\"\n" ); }
if ( ! path_exists( "$dst_path" ) ) {
qx( /bin/mkdir -p -m 775 "$dst_path" >> "${MIGRATION_LOG}" );
print LOG_FILE "Creating destination directory: $dst_path\n";
}
qx( /usr/sbin/chown _dovecot:mail "$dst_path" >>"${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}" );
do_dovecot_data_migration( "${src_path}", "${dst_path}" );
} else {
print LOG_FILE "Error: Data store directory does not exist at: ${src_path}/user\n";
}
my @alt_paths = qx(${PLIST_BUDDY} -c 'Print :alternate_partitions' ${g_migration_plist});
if ($ for (my $i = 1; $i < $ my $a_partition = $alt_paths[$i];
chomp(${a_partition});
$a_partition =~ s/^\s*//;
$volume_tag = substr( $a_partition, 0, 8 );
if ( $volume_tag eq "/Volumes" ) {
$src_path = "$a_partition";
} else {
if ( "${g_source_root}" eq "/Previous System" ) {
$src_path = "${a_partition}";
} else {
$src_path = "${g_source_root}${a_partition}";
}
push( @g_clean_partitions, "${src_path}" );
}
if ( ${DEBUG} ) {
printf( "- verifying data store directory: \"${src_path}/user\"\n" ); }
$tries = 0;
while (!path_exists("${src_path}/user")) {
print LOG_FILE "Data store directory does not exist at: ${src_path}/user\n";
last if ++$tries >= 15;
sleep 60;
}
if( path_exists( "${src_path}/user" ) ) {
if ( ${DEBUG} ) {
printf( "- verifying data destination directory: \"${a_partition}/dovecot\"\n" ); }
if( !path_exists("${a_partition}/dovecot")) {
qx(/bin/mkdir "${a_partition}/dovecot" >> "${MIGRATION_LOG}");
}
qx(/usr/sbin/chown _dovecot:mail "${a_partition}/dovecot" >>"${MIGRATION_LOG}" 2>>"${MIGRATION_LOG}");
print LOG_FILE "Migrating alternate mail partition: ${src_path} to: ${a_partition}/dovecot\n";
do_dovecot_data_migration("${src_path}", "${a_partition}/dovecot");
} else {
print LOG_FILE "Error: Data store directory does not exist at: ${src_path}/user\n";
}
}
}
print LOG_FILE "End of migration\n";
}