#!/usr/bin/perl -w

#################################################################################
# Messaging Server tool for Upgrading to V6.x
#
# Copyright (c) 2000-2003, Sun Microsystems Inc. All Rights Reserved.
#
# Last updated: mar mai  3 12:51:39 PDT 2005
#
#################################################################################
#  This is an outline of what is required for upgrading from older versions of
# the Messaging Server to the 6.0 release and later versions.
# 
# The upgrade utility should be run AFTER you have completed
# the install and have generated the initial runtime configuration.
# 
# This is a necessary step as it verifies that your install and
# configuration have been done correctly. 
# 
# Prequisites
# 
#  You must run the configure program to generate an initial runtime configuration
# of Msg6.0. The Upgrade Utility will compare the information in your old
# configuration file and those in your initial runtime configuration and then
# generate a brand new config.
# 
#  You are expected to install Msg6.0 into a completely different server root than
# the where your iMS installation resides.
# 
# 
# The utility is run :
# 
#      program-name /path/to/OldMsg5InstanceRoot /path/to/Msg60ServerRoot
# 
# For example: 
# 
#     ./program-name.pl /opt/marsupial/msg-footscray /opt/SUNWmsgsr
# 
# The Upgrade Utility requires two arguments to run:
# 
#     1. The path to the instance Root that is being upgraded
#     2. The path to the Server Root of the Habaner Install
# 
# 
#  You must stop your messaging servers (5.x and 6.0) when you are running
# this utility. The utility will build a set of new configuration 
# files which you will then use to make the appropriate changes 
# to your ldap directory and or local configuration files. 
# 
# Program Output
# 
#  The program will run and extract the current values from your legacy
# configuration files and compare them to those held in the Msg60
# version which you are upgrading to. For each of the files examined
# it will generate a copy of the original file with the ".MERGED" extension
# appended to the original filename. The program also produces a ".DIFF" file
# which outlines the changes made between the legacy version of the
# configuration file and the habanero version of that file in order to
# produce the ".MERGED" file. 
# 
# -------------------------------------------------------------------------#
# Background on the Upgrade Utility Operation
# -------------------------------------------------------------------------#
# The primary purpose of this utility is to move from a directory structure
# of:
#
#  Msg_ServerRoot
#       |
#       msg-&lt;INSTANCENAME>/
#       	|-----/config/
#       	|-----/imta/
#       	|	|-----/bin/
#       	|	|-----/config/
#       	|	|	|-----/locale/
#       	|	|	|-----/&lt;language>/
#       	|	|			|-----/LC_MESSAGES/
#       	|	|-----/db/
#       	|	|-----/programs/
#		|
#       	|-----/report/
#       	|-----/store/
#       	|	|-----/mboxlist/
#       	|	|-----/partition/
#       	|	|	|-----/primary
#       	|	|-----/user/
#  
# To a directory structure structure/software layout of:
#  
#  msg_svr_base/
#  	|-----/sbin
#  	|-----/bin
#  	|-----/admin-serv
#  	|-----/data/
#  	|	|-----/config/
#  	|       |	|-----/locale/
#  	|	|		|-----/some-language/
#  	|	|		|-----/LC_MESSAGES/
#  	|	|-----/store
#	|	|	|------/mboxlist/
#  	|	|	|	
#  	|	|	|-----/partition/
#  	|	|	|	|-----/primary
#  	|	|	|-----/store
#	|	|
#  	|	|-----/log
#  	|	|-----/site-programs
#  	|	|-----/setup
#  
# ... while at the same time carry forward the configuration/customizations from 
# the previous install.
#
# There are four types information which you will be concerned with when
# you upgrade,
#
#  -- configuration settings in the mta files
#  -- configutil settings for the message store operations
#  -- the configuration of the backup service for the message store data
#  -- the update of the mboxlist databases to use the new database engine.
#
# So as to allow you to do your upgrade with the minimum amount of downtime and
# the most flexibility, this utility can be run without stopping either the legacy
# installation or the new install. As a result of this design, you may in fact make
# a minimal tarball of your legacy installation and use that as input for running this
# utility. 
# 
# This minimal tarball would contain an archive of the following directories
# 
#    ./msg-instance-root/imta
#    ./msg-instance-root/imta/tmp
#    ./msg-instance-root/imta/tmp/tmpdb
#    ./msg-instance-root/imta/tmp/backupdb
#    ./msg-instance-root/imta/db
#    ./msg-instance-root/imta/config
#    ./msg-instance-root/imta/programs
#    ./msg-instance-root/imta/bin
#    ./msg-instance-root/imta/dl
#    ./msg-instance-root/config
#    ./msg-instance-root/store/mboxlist
#
#
#############################################################################
# Now Let us begin
#############################################################################

# Helps to declare the vars being used by this program.

use vars qw( $subroutine
             @obseleted_LegacyVars
             %HasHofInclFiles
             @preserve_Msg60Vars );

# Msg60 configuration entities which should be preserved
# These are configutil entities which will most likely differ 
# between the Legacy server and the Msg60 server. They should 
# not need to change since they represent the most stable set of
# config params for the messaging server. 

my @preserve_Msg60Vars =  ( 
                            "nsClassname", 
                            "local.ldaphost", 
                            "local.ldapport", 
                            "local.defdomain", 
                            "local.serveruid", 
                            "local.ldapsiedn", 
                            "local.ldapsiecred", 
                            "local.ugldaphost", 
                            "local.ugldapport", 
                            "local.ugldapbinddn", 
                            "local.service.pab.ldaphost", 
                            "local.service.pab.ldappasswd", 
                            "local.ugldapbindcred", 
                            "local.enduseradmindn",
                            "local.enduseradmincred", 
                            "local.service.pab.ldapbinddn",
                            "local.servername", 
                            "local.servergid"
                          );

# Legacy configuration entities which should be skipped/removed
# These params are obselete.

@obseleted_LegacyVars = (
                         "channel=autoreply",
                         "imta_exe",
                         "imta_com",
                         "imta_scratch", 
                         "imta_jbc_service", 
                         "queue_chache_mode", 
                         "imta_queue_cache_database",
                         "periodic_job=dirsync",
                         "channel=addressing", 
                         "channel=hold",
                         "channel=l" 
                       ) ;

my $USAGE_ERROR = "Usage: 
	$0 
		/Path/to/Legacy/msg-Msg5InstanceRoot	Path-to-Msg60ServerRoot

";

my $Msg5InstanceRoot = shift || die $USAGE_ERROR ;
chomp ($Msg5InstanceRoot);

my $Msg60ServerRoot = shift || die $USAGE_ERROR ;
chomp ($Msg60ServerRoot);

my $command_debug = $ENV{'SCRIPT_DEBUG'};

# create a temporary directory
$TmpDir = "/var/tmp/UpgradeMsg5toMsg6.ScratchDir"; 
mkdir ("$TmpDir",0700);

# Announce what the program will do.
&announce ; 

# Load the configutil params from the Legacy server into a hash table.
my %LegacyConfigs = &GetMsgConfigInfo ($Msg5InstanceRoot);

# Load the configutil params from the Msg60 server into a hash table.
my %TargetConfigs = &GetMsgConfigInfo ($Msg60ServerRoot);

# compare the configutil between the two server versions. Merge the appropriate
# changes and overrides from both versions and write to a changelog. 

&MergeParseConfigutil ; 

# files which are in attribute = value fomat.
my @avpl_config_files = ("imta_tailor", "option.dat","native_option") ;

# files which have a structured format and divided into sections.
my @structured_config_files = ("job_controller.cnf","dispatcher.cnf"); 

print "Parsing configuration files ...\n\n" ; 

# loop through the list of structured config files
foreach $config_file ( @structured_config_files ) {

  # Get the file path in the Legacy Server and the Msg6.0 server.
  $legacy_config = "$Msg5InstanceRoot/imta/config/$config_file" ; 
  $target_config = "$Msg60ServerRoot/config/$config_file" ; 

  # Parse the Legacy Config file first.
  %Msg5tmpHasH = &ParseStructFile($legacy_config) ;

  # Now parse the Msg6.0 config file.
  %TmpMsg6HasH = &ParseStructFile($target_config) ;

  # Attempt the Merge of configurations.
  &MergeParseStructFile($legacy_config); 

}

# loop through the list of avpl config files
foreach $config_file ( @avpl_config_files ) {

  # Get the file path in the Legacy Server and the Msg6.0 server.
  $legacy_config = "$Msg5InstanceRoot/imta/config/$config_file" ; 
  $target_config = "$Msg60ServerRoot/config/$config_file" ; 

  # Parse the Legacy Config file first.
  %Msg5tmpHasH = &ParseOptionsFile($legacy_config) ;

  # Now parse the Msg6.0 config file. 
  %TmpMsg6HasH = &ParseOptionsFile($target_config) ;

  # Attempt the Merge of configurations.
  &MergeParseOptionsFile($legacy_config); 

}

# Scan the IMTA config subdirectory
opendir(DIR, "$Msg5InstanceRoot/imta/config" ) 
 || die qq/Unable to open "$Msg5InstanceRoot ": $!/;

# look for .filter, any option files and .rules files.
@option_files = grep(/.filter|.opt|_option|.rules/i, readdir(DIR));
closedir(DIR);

# add the other configuration files to the list
push @option_files, ("imta.cnf","conversions", "mappings", "aliases", "dl_auth.map");

print "Building a relocation map for configuration files\n"; 

foreach $file (@option_files) {
  # skip certain files.
  next if $file =~ /dirsync|ChannelDefaults/; 

  # check if the file actually exists
  if ( -f "$Msg5InstanceRoot/imta/config/$file" ) {

       # Determine the new location for the config file while searching for include files.
       &RelocateConfigFiles("$file", "$Msg5InstanceRoot/imta/config/$file",
                          "$Msg60ServerRoot/config/$file","make_mta_config_changes.sh");

  }

}

$secondary_includes = 1; 

# Parse and relocate all first level files.
foreach $secondary_file (sort @include_files_list) {

  $path_to_secondary_include_file = "$Msg5InstanceRoot/imta/config/$secondary_file";

  $path_to_secondary_target = "${Msg60ServerRoot}/config/$secondary_file" ; 

  &RelocateConfigFiles("$secondary_file","$path_to_secondary_include_file", 
                       "$path_to_secondary_target","make_mta_config_changes.sh") ;

}

# Parse and relocate all secondary level files.
foreach $tertiary_file (sort @secondary_files_list) {

  $path_to_tertiary_include_file = "$Msg5InstanceRoot/imta/config/$tertiary_file";

  $path_to_tertiary_target = "$Msg60ServerRoot/config/$tertiary_file" ; 

  &RelocateConfigFiles("$tertiary_file","$path_to_tertiary_include_file", 
                       "$path_to_tertiary_target","make_mta_config_changes.sh") ;

}

# relocate the backup config file
$backup_file = "backup-groups.conf";

$ims_backup_path = "$Msg5InstanceRoot/config/$backup_file";

$habanero_backup_path = "$Msg60ServerRoot/config/$backup_file";

if ( -f "$ims_backup_path" ) {

  &RelocateConfigFiles("$backup_file","$ims_backup_path", 
               "$habanero_backup_path","make_backup_config_changes.sh") ;

}

# add db_upgrade steps
&UpgradeDBFiles ;

## &append_new_channels ;

print "\n";

# Give a summary of the changes to the user.
# Print instructions on how to proceed.

&announce_changes; 

# end of MAIN program

###################################################################
## Subroutines
###################################################################

sub append_new_channels
{

 # the tcp_lmtp* channels are new in Msg60. Maker sure that these
 # sample channels gets defined in the merged imta.cnf file.

 open (CNF, ">>$TmpDir/imta.cnf.MERGED");

print CNF <<__EOF;

!
! tcp_lmtpss (LMTP server - store)
!tcp_lmtpss lmtp subdirs 20
!tcp_lmtpss-daemon

!
! tcp_lmtpsn (LMTP server - native)
!tcp_lmtpsn lmtp subdirs 20
!tcp_lmtpsn-daemon

!
! tcp_lmtpcs (LMTP client - store)
!tcp_lmtpcs defragment lmtp port 225 nomx single_sys subdirs 20 maxjobs 7 pool SMTP_POOL dequeue_removeroute
!lmtpcs-daemon

!
! tcp_lmtpcn (LMTP client - native)
!tcp_lmtpcn defragment lmtp port 226 nomx single_sys subdirs 20 maxjobs 7 pool SMTP_POOL dequeue_removeroute
!lmtpcn-daemon

!
__EOF

  close CNF;

}

sub announce
{

  open (TODO_LIST, ">$TmpDir/do_the_upgrade.sh");

  $target_file=""; 
  $legacy_file="";

  print TODO_LIST <<__EOFF__TODO; 
#!/bin/sh
##
## ============================================================
## do_the_upgrade.sh - autogenerated uber script for executing
## the upgrade changes.
## ============================================================

SAVEDIR=$TmpDir

## date and timestamp
_DATE=`date +%a%d%b%H%M%S%Y`
export _DATE

## ============================================================
##  Upgrade of the MTA configuration files
## ============================================================

$TmpDir/make_mta_config_changes.sh

## ============================================================
##  Upgrade of the CONFIGUTIL settings
## ============================================================

$TmpDir/make_configutil_changes.sh

## ============================================================
##  Upgrade of the backup.conf settings
## ============================================================

$TmpDir/make_backup_config_changes.sh

## ============================================================
##  Upgrade of the mboxlist databases
## ============================================================

$TmpDir/make_mboxlistdb_changes.sh

## End of uber Script
__EOFF__TODO

  close TODO_LIST ;

  open (STORE_TODO_LIST, ">$TmpDir/make_configutil_changes.sh") ; 
  print STORE_TODO_LIST <<__EOFF__STORE_TODO;
#!/bin/sh
##
## make_configutil_changes.sh - script for executing the configutil changes 
##   and relocating the cert files.

SAVEDIR=$TmpDir


if [ -z \"\$_DATE\" ]; then
  _DATE=`date +%a%d%b%H%M%S%Y`
  export _DATE
fi

mkdir -p $Msg60ServerRoot/data/setup/pre-Upgrade.\$_DATE

## ============================================================
##  BEGIN commands for relocating the cert files.
## ============================================================
__EOFF__STORE_TODO

  $target_file="${Msg60ServerRoot}/config/sslpassword.conf"; 
  $legacy_file="$Msg5InstanceRoot/config/sslpassword.conf"; 

  if ( -f "$target_file") { 
    print STORE_TODO_LIST "\ncp $target_file \\\n"; 
    print STORE_TODO_LIST "\t${Msg60ServerRoot}/data/setup/pre-Upgrade.\$_DATE \n"; 
  }

  if ( -f "$legacy_file" ) {
    print STORE_TODO_LIST "\ncp $legacy_file \\\n"; 
    print STORE_TODO_LIST "\t$target_file \n"; 
  }

  if ( defined ($TargetConfigs{'encryption_nskeyfile'} )) {
     $target_file="${Msg60ServerRoot}/"
                . $TargetConfigs{'encryption_nskeyfile'} ;

     if ( -f "$target_file" ) { 
       print STORE_TODO_LIST "\ncp $target_file \\\n"; 
       print STORE_TODO_LIST "\t${Msg60ServerRoot}/data/setup/pre-Upgrade.\$_DATE \n"; 
     }

  }

  if ( defined ($LegacyConfigs{'encryption_nskeyfile'} )) {
     $legacy_file="$Msg5InstanceRoot/../../$legacy_file"
              . $LegacyConfigs{'encryption_nskeyfile'};

     if ( -f "$legacy_file" ) {
       print STORE_TODO_LIST "\ncp $legacy_file \\\n"; 
       print STORE_TODO_LIST "\t$target_file \n"; 
     }

  }

  if ( defined ($TargetConfigs{'encryption_nscertfile'} )) {
     $target_file="${Msg60ServerRoot}/"
                . $TargetConfigs{'encryption_nscertfile'} ;

     if ( -f "$target_file") { 
       print STORE_TODO_LIST "\ncp $target_file \\\n"; 
       print STORE_TODO_LIST "\t${Msg60ServerRoot}/data/setup/pre-Upgrade.\$_DATE \n"; 
     }

  }

  if ( defined ($LegacyConfigs{'encryption_nscertfile'} )) {
     $legacy_file="$Msg5InstanceRoot/../../$legacy_file"
              . $LegacyConfigs{'encryption_nscertfile'};

     if ( -f "$legacy_file" ) {
       print STORE_TODO_LIST "\ncp $legacy_file \\\n"; 
       print STORE_TODO_LIST "\t$target_file \n"; 
     }

  }

  print STORE_TODO_LIST "\ncd \$SAVEDIR";
  print STORE_TODO_LIST "\n\n## ============================================================"; 

  close STORE_TODO_LIST ;

  open (BACKUP_TODO_LIST, ">$TmpDir/make_backup_config_changes.sh") ; 
  print BACKUP_TODO_LIST <<___BACKUP__EOFF; 
#!/bin/sh
##
## make_backup_config_changes.sh - script for executing the upgrade changes.
##

SAVEDIR=$TmpDir

if [ -z \"\$_DATE\" ]; then
  _DATE=`date +%a%d%b%H%M%S%Y`
  export _DATE
fi

cd \$SAVEDIR
___BACKUP__EOFF

  close BACKUP_TODO_LIST ;

  open (DB_TODO_LIST, ">$TmpDir/make_mboxlistdb_changes.sh") ; 
  print DB_TODO_LIST <<__DB_TODO; 
#!/bin/sh
##
## make_mboxlistdb_changes.sh - script for executing the mboxlist 
## database upgrade changes.
##

SAVEDIR=$TmpDir

if [ -z "\$_DATE" ]; then
    _DATE=`date +%a%d%b%H%M%S%Y`
    export _DATE
fi

cd \$SAVEDIR
__DB_TODO

  close DB_TODO_LIST ;

  open (MTA_TODO_LIST, ">$TmpDir/make_mta_config_changes.sh") ; 
  print MTA_TODO_LIST <<__MTA_TODO;
#!/bin/sh
##
## make_mta_config_changes.sh - script for executing the mtaconfig changes.
##

SAVEDIR=$TmpDir

if [ -z "\$_DATE\" ]; then
   _DATE=`date +%a%d%b%H%M%S%Y`
   export _DATE
fi
mkdir -p $Msg60ServerRoot/data/setup/pre-Upgrade.\$_DATE

## ============================================================
## commands for relocating distribution lists, db files etc.
## ============================================================

cd $Msg5InstanceRoot/imta

__MTA_TODO

  if ( -d "$Msg5InstanceRoot/imta/dl" ) {
    print MTA_TODO_LIST "tar cf - dl | (cd $Msg60ServerRoot/data ; tar xf - )\n"; 
  }
  if ( -d "$Msg5InstanceRoot/imta/db" ) {
    print MTA_TODO_LIST "tar cf - db | (cd $Msg60ServerRoot/data ; tar xf - )\n"; 
  }
  print MTA_TODO_LIST "\ncd \$SAVEDIR\n\n";
  close MTA_TODO_LIST ;
  return;

}

sub announce_changes
{

  open (MTA_TODO_LIST, ">>$TmpDir/make_mta_config_changes.sh") ; 
  print MTA_TODO_LIST <<__MTA_TODO_END;

##=============================================================
## commands for relocating localization files to a temporary area
##=============================================================

cd $Msg5InstanceRoot/imta/config/locale

for language in `ls -F | grep / | cut -d/ -f1`
do 
  cp -rp \$language/LC_MESSAGES \\
        ${Msg60ServerRoot}/config/locale/\${language}--OLD
done
 
##=============================================================
## commands for relocating the pipe programs
##=============================================================

cd $Msg5InstanceRoot/imta/programs
   tar cf - . | (cd ${Msg60ServerRoot}/data/site-programs; tar xf -)

##=============================================================
## commands for relocating the old imta/bin directory
##=============================================================

cd $Msg5InstanceRoot/imta/
    tar cf - bin | (cd ${Msg60ServerRoot}/data/site-programs; tar xf -)

__MTA_TODO_END

  close MTA_TODO_LIST ; 

  # The program.opt file is parsed via its own subroutine.

  &RelocatePipePrograms;

  open  MTA_TODO_LIST, ">>$TmpDir/make_mta_config_changes.sh"; 

  print MTA_TODO_LIST "## ============================================================\n";
  print MTA_TODO_LIST "## Finally do a cnbuild\n";
  print MTA_TODO_LIST "## ============================================================\n";
  print MTA_TODO_LIST "\necho MTA changes complete... Now running cnbuild\n";
  print MTA_TODO_LIST "\n${Msg60ServerRoot}/sbin/imsimta cnbuild\n\n";
  close MTA_TODO_LIST ; 

  $script_files = "do_the_upgrade.sh make_configutil_changes.sh make_backup_config_changes.sh";
  $script_files .= " make_mboxlistdb_changes.sh make_mta_config_changes.sh";

  system ("cd $TmpDir; chmod 700 $script_files"); 

  print STDOUT <<__EOFF__;

First phase completed ...

     The upgrade utility has completed a comparison between your v5.2 installation
     and your initial runtime configuration and wrote a set of .MERGED and .CHANGES
     files are in the $TmpDir directory.

     The .MERGED files contain the merge of items from the initial runtime configuration
     that was generated by the msg6.x configurator and most customizations from your
     legacy install.

     The .CHANGES files lists the major differences between the MERGED files and the
     two sets of configuration information used to create it.

To complete the upgrade:

     Carefully review the .MERGED and .CHANGES files. Once you are satisfied
     with the suggested changes, you have the following options:

     A) Run the following shell scripts individually:  

         '$TmpDir/make_mta_config_changes.sh' 
         '$TmpDir/make_configutil_changes.sh' 
         '$TmpDir/make_backup_config_changes.sh' 
         '$TmpDir/make_mboxlistdb_changes.sh' 

     B) Run the uber script

         '$TmpDir/do_the_upgrade.sh' 

        which calls the previous set of scripts in the order listed above.

NOTES: 

 (1)  The 'webmail' files are not touched by this utility, if you have made
      site specific customizations to these files, you will have to manually determine the
      appropriate changes to the new family of webmail files provides in this release.

 (2)  The mappings.locale file was not adjusted by this utility. 

      If have customized the files referenced by mappings.locale, you must manually merge 
      those changes into the new set of files files included in this release.  In the curent
      release, these files were located at ~/locale/<lang>. In the previous (msg5x) release, 
      those files are located in the ~/locale/<lang>/LC_MESSAGES subdirectory. Copies of the
      Msg5x files have been saved to the msg_srv_base/config/locale/<lang>--OLD location.  


__EOFF__

  return; 

}

sub GetMsgConfigInfo
{

  ##################################################################################
  # Subroutine GetMsgConfigInfo
  #
  # Purpose: 
  #         Given a msg.conf or local.conf file, build a hash table and return 
  #         a hash table.
  #
  ##################################################################################

  my ($config_params, $config_line) = ""; 
  my (@config_params, %config ) = (); 
  $instance_dir = shift;
  
  $cfg_msg_conf = "$instance_dir/config/msg.conf";
  $cfg_local_conf = "$instance_dir/config/local.conf";

  if ( ! -f $cfg_msg_conf) {
    die ("ERROR: the file $cfg_msg_conf was not found\n");
  }
  if ( ! -f $cfg_local_conf) {
    die ("ERROR: the file $cfg_local_conf was not found\n");
  }

  local $/ = ""; # set the record separator to paragraph mode

  open(MSGCONF, "<$cfg_msg_conf")
      || abort("ERROR: Unable to open $cfg_msg_conf");

  while (defined($_ = <MSGCONF>)) {
    s/\n\s+//g;           # unfold the lines
    push @config_params, split (/\n/,$_) ;
  }
  close MSGCONF ;

  open(LOCALCONF, "<$cfg_local_conf")
      || abort("ERROR: Unable to open $cfg_local_conf");

  while (defined($_ = <LOCALCONF>)) {
    s/\n\s+//g; # unfold the lines
    s/: /= /g;  # change ': ' to '= '
    if ( $_ !~ /gen.installed/i ) {
        s/,/, /g;  # change ',' to ', '
        s/,\s+/, /g;  # change ',     ' to ', '
    }
    push @config_params, split (/\n/,$_ ) ;
  }
  close LOCALCONF ;

  undef $/ ; 

  foreach $config_line (@config_params) {

    # skip over blank lines, comment lines and indented lines
    next if $config_line =~ (/(^#.*)|(^$)|(^\s+.*)/);

    chomp;

    #split the input buffer into a key value pair
    ($key,$value) = split(/=/,$config_line,2);

    # remove leading and trailing spaces
    $value =~ s/^\s+//g;
    $value =~ s/\s+$//g;
    $key =~ s/^\s+//g;
    $key =~ s/\s+$//g;

    # turn dots to underscores
    $key =~ s/local\./local_/g;
    # change service.nsmsg to service.blah
    $key =~ s/\.nsmsg/\./g; 

    # force keys to lowercase
    $key =~ tr/A-Z/a-z/; 

    # change dots to underscores for encryption keyfile and certfile

    #if ( $key =~ /encryption.nskeyfile/i ) {
    #   $key =~ s/encryption.nskeyfile/encryption_nskeyfile/ig; 
    #}
    #if ( $key =~ /encryption.nscertfile/i ) {
    #   $key =~ s/encryption.nscertfile/encryption_nscertfile/ig; 
    #}

    # remove double quotes
    $value =~ s/\"//g;
    # escape single quotes
    $value =~ s/\'/\\'/g;

    # Account for cases where there are multivalued attributes
    if (!defined $config{$key})
    {
      # insert the keyvalup pair into a the %config hash table
      $config{$key} = $value ;
    } else {
      @i = @j = ();
      $config{$key} .= "_x_|_x_$value" ;
      @i = split (/_x_\|_x_/ , $config{$key}) ;
      @j = sort @i ;
      # Now insert the keyvalup pair into a the %config hash table
      $config{$key} = sprintf join ("_x_|_x_", @j);
    }
  }

  # Further parsing of the ldapsiedn.......

  # Remove leading and trailing double quotes
  $config{local_ldapsiedn} =~ s/^\"//;
  $config{local_ldapsiedn} =~ s/\"$//;

  # Escape the open and close parens
  $config{local_ldapsiedn} =~ s/\(/\\(/g;
  $config{local_ldapsiedn} =~ s/\)/\\)/g;

  # split the ldapsiedn into components so as to get the instanceName and
  # server group information. Extact by splitting at ", "
  ( 
    $config{InstanceName}, 
    $config{productName}, 
    $config{GroupName},
    $config{ServerHostName}, 
    $config{AdminDomain}, 
    $config{suffix} 
  ) = split (/, /,$config{local_ldapsiedn},6);

  $config{InstanceName} =~ s/cn=//g;

  return %config ;

}

sub ParseIncludeRefs
{

  ##################################################################################
  #
  # Subroutine ParseIncludeRefs
  #
  # Purpose: 
  #         Extract and untangle the include refs  
  #
  ##################################################################################

  local $generic_file_name = shift; 
  local $full_file_path = shift; 


  # no need to parse files which are called from the conversions. file.
  return if $generic_file_name =~ /__COMMAND__CLAUSE__/; 

  # no need to parse the mappings.locale file.
  return if $generic_file_name =~ /mappings.locale/; 

  print "\n\tscanning $generic_file_name for include files\n";

  open (INPUT, "<$full_file_path") || die ("unable to open the file $full_file_path\n");
  open (MERGED_FILE , ">$TmpDir/${generic_file_name}.MERGED") ;

  while (<INPUT>)
  {
    # skip over comment lines by just printing them.
    if ( /^[!#]/ ) {
         print MERGED_FILE $_;
         next; 
    }

    # do not parse the mappings.locale file
    if ( /mappings.locale/ ) {
         print MERGED_FILE $_;
         next; 
    }
    # seek out lines which have include references
    if ( /(<)|(file:)|(COMMAND=)/i ) {

      $fname_buffer = $_;
      $fname_buffer =~ s/<(.*)/$1/g;
      $fname_buffer =~ s/(.*)file:\/\///g;

      # flag any command clauses 
      $fname_buffer =~ s/(.*)command="/__COMMAND__CLAUSE__/i; 
      # remove leading and trainling spaces
      $fname_buffer =~ s/^\s+ //g;
      $fname_buffer =~ s/ $//g;
      # remove quotes.
      $fname_buffer =~ s/"//g;
      # remove the second and subsequent words.
      $fname_buffer =~ s/(.*?) (.*)$/$1/;
      # get rid of control Ms
      $fname_buffer =~ s/\cM//g;

      chomp ($fname_buffer);

      print "Detected an include file $fname_buffer\n" if $command_debug ;

      # check to see if the include file is directly below the MTA config directory.
      if (( $fname_buffer =~ 
             s/$LegacyConfigs{'local_serverroot'}.$LegacyConfigs{InstanceName}.imta.config.// ) 
             || ( $fname_buffer =~ s/IMTA_TABLE://i)  ) 
      {

         # All that remains now is the bare filename.
         $include_file = $fname_buffer ;

         # The path the include file is prepended with the InstanceRoot that was given on the cmd line.
         $path_to_include_file = "$Msg5InstanceRoot/imta/config/$fname_buffer" ;

         # remove the marker which tells that the include file is from a conversions entry.
         $path_to_include_file =~ s/__COMMAND__CLAUSE__//;

         # The path the traget is prepended with the InstanceRoot that was given on the cmd line.
         $path_to_target = "$TargetConfigs{'local_serverroot'}/config/$fname_buffer" ;

         # remove the marker which tells that the include file is from a conversions entry.
         $path_to_target =~ s/__COMMAND__CLAUSE__//;

         # Build the array of first and second level include files.
         if ( $secondary_includes ) {
           push @include_files_list , "$include_file" ; 
         } else {
           push @secondary_files_list , "$include_file" ;
         } 

         # Modify the current input line to reflect the new location of the include file
         s/$path_to_include_file/$path_to_target/; 

         # remove the marker
         s/__COMMAND__CLAUSE__//; 

         # print the resulting input line to the MERGED file.
         print MERGED_FILE $_;

      } else { 
         # the current line, while it does contain an inlcude ref, does not need 
         # to be changed. Simply print it. 
         print MERGED_FILE $_;
      }

    } else { 
       # No include references found on this line. Simply print to the MERGED file.
       print MERGED_FILE $_; 
    }
  }

  close MERGED_FILE; 
  close INPUT ;
  return ;
 
}

sub UpgradeDBFiles
{

  ##################################################################################
  # Subroutine  UpgradeDBFiles
  # Purpose: 
  #         Take a message store db and relocate it under the new server root.
  #
  ##################################################################################

  $Msg5MboxlistPath = "$Msg5InstanceRoot/store/mboxlist";
  $Msg60MboxlistPath = "${Msg60ServerRoot}/data/store/mboxlist";

  open DB_TODO_LIST, ">>$TmpDir/make_mboxlistdb_changes.sh";

  print DB_TODO_LIST <<__DB_TODO_EOF; 
## ============================================================
## commands for upgrading mboxlist db files
## ============================================================

echo upgrading mboxlist db files...

## ============================================================
# A quick check to Ensure that the store process is NOT Running!
## ============================================================

imspidfile=$Msg5InstanceRoot/config/pidfile.store

if [ -f "\$imspidfile" ]; then
  store_pid=`head -1 \$imspidfile`
  kill -0 \$store_pid >/dev/null 2>&1
  if [ \$? -eq 0 ]; then
    echo ERROR: Cannot upgrade store database, IMS Store process is running!
    exit 2
  fi
fi

mkdir -p $Msg60MboxlistPath

mv $Msg60MboxlistPath \\
       $Msg60MboxlistPath.60

mkdir -p $Msg60MboxlistPath

chown $serveruid:$servergid $Msg60MboxlistPath
  
for dbfile in folder.db quota.db subscr.db peruser.db
do
  if [ ! -f "$Msg5MboxlistPath/\$dbfile" ]; then
    echo ERROR: cannot find $Msg5MboxlistPath/\$dbfile
    echo Cannot upgrade store database, $Msg5MboxlistPath/\$dbfile is missing
    exit 2
  else
    cp -p $Msg5MboxlistPath/\$dbfile \\
              $Msg60MboxlistPath/\$dbfile
    if [ \$? -ne 0 ]; then
      echo ERROR: cannot cp $Msg5MboxlistPath/\$dbfile
      exit 2
    fi
  fi
done

${Msg60ServerRoot}/sbin/ims_db_upgrade

if [ \$? -eq 0 ]; then
  echo ims_db_upgrade completed!
  rm -rf $Msg60MboxlistPath.60
else
  echo ERROR: ims_db_upgrade failed, restoring original mboxlist data
  rm -rf $Msg60MboxlistPath
  mv $Msg60MboxlistPath.60 $Msg60MboxlistPath
fi

__DB_TODO_EOF

  return ;

}

sub RelocateConfigFiles
{

  ##################################################################################
  # Subroutine  RelocateConfigFiles
  # Purpose: 
  #         Take a MTA config file and relocate it under the new server root.
  #
  ##################################################################################

  $input_file = shift ;
  $current_path = shift ;
  $target_path = shift ;
  $action_logfile = shift || '-' ;

  open ACTION_SCRIPT, ">>$TmpDir/$action_logfile";

  print ACTION_SCRIPT "\n## ============================================================";
  print ACTION_SCRIPT "\n# commands for relocating ${input_file}" ; 
  print ACTION_SCRIPT "\n## ============================================================";

  # Watch for command clauses which occur in the conversions files. 
  if ($input_file =~ s/__COMMAND__CLAUSE__// ) {

    $current_path =~ s/__COMMAND__CLAUSE__//;
    $target_path =~ s/__COMMAND__CLAUSE__//;

    if ( -f "$target_path" ) {
      print ACTION_SCRIPT "\ncp ${target_path} \\\n    ${Msg60ServerRoot}/data/setup/pre-Upgrade.\$_DATE \n";
    }
    print ACTION_SCRIPT "\ncp ${current_path} \\\n    ${target_path}\n" ; 

  } else {

    if ( -f "$target_path" ) {
      print ACTION_SCRIPT "\ncp ${target_path} \\\n    ${Msg60ServerRoot}/data/setup/pre-Upgrade.\$_DATE \n";
    }
    print ACTION_SCRIPT "\ncp \$SAVEDIR/${input_file}.MERGED \\\n    ${target_path}\n" ; 

    close ACTION_SCRIPT ; 

    &ParseIncludeRefs("$input_file","$current_path","$target_path") ; 

  }

  return ;

}

sub RelocatePipePrograms
{

  ##################################################################################
  # Subroutine RelocatePipePrograms
  # Purpose: 
  #         Take a pipe program and reroot it under the new server root.
  #
  # Takes advantage if the program.opt file to find out what the pipe programs are
  # and the parameters used to define each method. Sample format below
  ##################################################################################
  # ! VERSION= 1.0
  # listserver.m=listserver
  # listserver.p=/opt/marsupial/msg-footscray/imta/programs/listserv/listserv.csh
  # listserver.g=
  # listserver.e=1
  ##################################################################################


  my ($confvar,$key,$value,$server_root) = "";
  my %HasHofConfigsParams = ();

  $pipe_option_file = "$Msg5InstanceRoot/imta/config/program.opt" ; 

  if ( ! -f "$pipe_option_file" ) {

    return;

  } else { 

    print "Locating Pipe programs (if any...)\n\n";
    open MTA_TODO_LIST, ">>$TmpDir/make_mta_config_changes.sh";
    open PIPE_OPTION, "$pipe_option_file";

    while (<PIPE_OPTION>) {

      chomp ; 
      # skip comment lines
      next if /^!/;

      # change the path to the new IMTA_PROGRAM location
      s/$LegacyConfigs{local_serverroot}.$LegacyConfigs{InstanceName}.imta.programs/$Msg60ServerRoot\/data\/site-programs/g;

      # method name by splitting at the first dot.
      ($alias,$params) = split(/\./,$_,2);

      # separate the params and args by splitting at the .eq. sign.
      ($key,$value) = split(/=/,$params,2);
     
      # load into a Hash table. 
      $HasHofConfigsParams{$alias}{$key} = $value ; 

    }

  }

  # loop through pipe methods
  foreach $method (keys %HasHofConfigsParams) {

    print "\tpipe channel delivery method '$method' detected\n";
    print MTA_TODO_LIST "$Msg60ServerRoot/sbin/imsimta program \\\n  -a ";

    foreach $attrib (sort( keys %{$HasHofConfigsParams{$method} } )) {
       print MTA_TODO_LIST "-$attrib \"$HasHofConfigsParams{$method}{$attrib}\" ";
    }

    print MTA_TODO_LIST "\n";

  }

  close PIPE_OPTION ; 
  close MTA_TODO_LIST ; 
  return ;

}

sub MergeParseStructFile
{

  ##################################################################################
  #
  # Subroutine MergeParseStructFile
  #
  # Purpose: 
  #         Parse a structured configuration file like the dispatcher.cnf or
  #         the job_controller.cnf file. Merge the differences and print a report.
  #
  ##################################################################################

  my ($output_file, $section_name, $output_buffer, $section) = "" ;
  my ($obselete_sections, $undefined_sections, $output_line) = "" ;
  my $input_file = shift ; 

  # NOTE: Our input file is the version from the Msg5.x configuration.
  open (LEGACY_INPUT, "<$input_file") || die ("unable to open the file $input_file\n");

  print "\n\tMerging ...\n"; 

  open (MERGED_FILE, ">$TmpDir/$config_file.MERGED");
  open (CHANGELOG, ">$TmpDir/$config_file.CHANGES");
  open MTA_TODO_LIST, ">>$TmpDir/make_mta_config_changes.sh";

  while (<LEGACY_INPUT>) # NOTE: Our input file is the version from the Msg5.x configuration.
  {

    # The imta_tailor file and the dispatcher.cnf file now uses 
    # 'IMTA_BIN:filename' rather than full path names. Normalize
    s/$LegacyConfigs{local_serverroot}.bin.msg.imta.bin./IMTA_BIN:/g;

    # Msg6.0 now uses return_job instead of return.sh
    s/IMTA_EXE:return.sh/IMTA_BIN:return_job/;

    # Msg6.0 now uses IMTA_BIN instead of IMTA_EXE
    s/IMTA_EXE/IMTA_BIN/g;

    # No need to examine blank lines. Just add to output buffer
    if ( /^$/ ) {
      $output_buffer  .= $_;
      next ;
    }

    # No need to examine comment lines. Just add to output buffer
    if ( /^!|#/ ) {
      $output_buffer  .= $_;
      next;
    }

    chomp;

    # If no $section_name is defined then this must be the global 
    # defaults section.
    if (!defined $section_name) {
      $section_name = "Global defaults";
    }

    # Are we at a new section? Test for the '[' character in column 1,
    # it is used to mark the start of a new section
    if (s/^\[//) {     # begin processing of a section.

      s/]$//;

      # clear the flags used to detect if section is obselete or preserved.
      $obselete_flag = 0 ;
      $preserve_flag = 0 ;

      # test for obselete sections which should be skipped.
      foreach $obseleted (@obseleted_LegacyVars) {
        $obselete_flag = 1 if ( $section_name =~ /$obseleted/i )
      }

      if ( $obselete_flag == 1 ) {
          # Append that output buffer to $obselete_section variable
          $obselete_sections .= "!\n[$section_name]\n$output_buffer" ;
          # clean the output buffer
          $output_buffer  = "";
          # set the new section name.
          $section_name = $_ ;
          next;
      }

      # Is this within the Global defaults section of the Msg5.x file? 
      if ( $section_name =~ /Global defaults/ ) {

        # Loop through each config setting found in the Hash table for the
        # Msg6.0 Global defaults section. 
        foreach $attrib ( keys %{$TmpMsg6HasH{$section_name} } ) {

          # Test if there is a Msg6.0 option/setting which is NOT DEFINED in the 
          # Msg5.x hash table of the same section.
          if ( !defined $Msg5tmpHasH{$section_name}{$attrib} ) {
            # Place the new msg60 Configuration setting at the end of the output buffer
            $output_buffer .= "$attrib";
	    if ( defined $TmpMsg6HasH{$section_name}{$attrib} ) {
              $output_buffer .= "=$TmpMsg6HasH{$section_name}{$attrib}";
            }
            $output_buffer .= "\n!\n";
            print CHANGELOG "!\n!\n!The variable $attrib is new in the $section_name section\n";
            print CHANGELOG "!\t$attrib\n";
            #print CHANGELOG "!\t\tMsg6.0 value        :\t$attrib=$TmpMsg6HasH{$section_name}{$attrib}\n";
            #print CHANGELOG "!\t\tThe MERGED file uses:\t$attrib=$TmpMsg6HasH{$section_name}{$attrib}\n" ; 
            next; 
          }
        }

        # What is remains are comments and other attribute/values that were unchanged between
        # the Msg6.0 and Msg5.x hash tables for this section.
        print MERGED_FILE "$output_buffer" ;
        $output_buffer  = "" ;
        $section_name = $_ ;
        next;

      }

      # Test for site customized sections/services.
      if ( !defined ($TmpMsg6HasH{$section_name}) ) {
        $undefined_sections .= "!\n[$section_name]\n$output_buffer";
        print MERGED_FILE "!\n[$section_name]\n$output_buffer";
        $output_buffer  = "" ;
        $section_name = $_ ;
        next;
      }

      # Test if there are differences between common sections.
      if ( $TmpMsg6HasH{$section_name} ne $Msg5tmpHasH{$section_name} ) {

        # Print the section name to the $config_file.MERGED output file 
        print MERGED_FILE "[$section_name]\n";

        # find out what makes the sections different from each other.
        foreach $attrib ( keys %{$TmpMsg6HasH{$section_name} } ) {

          # The case where a new attribute is introduced. 
          if ( !defined $Msg5tmpHasH{$section_name}{$attrib} ) {
            print MERGED_FILE "$attrib=$TmpMsg6HasH{$section_name}{$attrib}\n";
            print CHANGELOG "!\n!\n!The new variable $attrib has been introduced in the $section_name section\n";
            print CHANGELOG "!\t$attrib\n";
            print CHANGELOG "!\t\tMsg6.0 value        :\t$attrib=$TmpMsg6HasH{$section_name}{$attrib}\n";
            print CHANGELOG "!\t\tThe MERGED file uses:\t$attrib=$TmpMsg6HasH{$section_name}{$attrib}\n" ; 
            next;
          }

          # the case where the values of an attribute section are different.
          if ( $Msg5tmpHasH{$section_name}{$attrib} ne $TmpMsg6HasH{$section_name}{$attrib} ) {
            next;
          }

        }
      }

      # What is remains are comments and other attribute/values that were unchanged between
      # the Msg6.0 and Msg5.x hash tables for this section.
      print MERGED_FILE "$output_buffer" ;
      $output_buffer  = "" ;
      $section_name = $_ ;
      next;

    } ## end of processing a section.

    # take current line and append it to the output buffer.
    $output_buffer  .= "$_\n" ;

  } # end of while <LEGACY_INPUT> 

  # There is still a buffer which should be processed. 
  $obselete_flag = 0 ;
  $preserve_flag = 0 ;

  # test for obselete sections which should be skipped.
  foreach $obseleted (@obseleted_LegacyVars) {
    $obselete_flag = 1 if $section_name =~ /$obseleted/i ;
  }
  if ( $obselete_flag == 1 ) {
      # flush
      $obselete_sections .= "!\n[$section_name]\n$output_buffer" ;
      $output_buffer  = "";
      $section_name = $_ ;
  } 

  if ( defined $section_name ){
    # Test if there is a custom section created by a site. 
    if ( !defined  ($TmpMsg6HasH{$section_name}) ) {
        $undefined_sections .= "!\n[$section_name]\n$output_buffer";
        print MERGED_FILE "!\n[$section_name]\n$output_buffer";
    } else {
        # otherwise just print the section name and the output buffer.
        print MERGED_FILE "[$section_name]\n$output_buffer";
    }
  }

  # Now start recording items to the changelog.
  # sections which are new to Msg6.0

  print CHANGELOG "!\n\nConfiguration Entities found only in the Msg6.x version of $config_file\n\n";

  foreach $newSection (keys %TmpMsg6HasH) {
    if ( !defined $Msg5tmpHasH{$newSection} ) {
      printf MERGED_FILE "!\n[%s]\n", $newSection;
      printf CHANGELOG  "\n\nNew section\n\n'[%s]'\n\n!\t[%s]\t", $newSection,$newSection ;
      foreach $attrib ( keys %{$TmpMsg6HasH{$newSection} } ) {
        print MERGED_FILE "$attrib=$TmpMsg6HasH{$newSection}{$attrib}\n";
        print CHANGELOG "\n!\t$attrib=$TmpMsg6HasH{$newSection}{$attrib}";
      }
    }
  }

  $obselete_sections =~ s/\\//g; 
  $undefined_sections =~ s/\\//g; 

  print CHANGELOG "\n\nConfiguration Entities obseleted in Msg6.x version of $config_file\n\n$obselete_sections\n";
  print CHANGELOG "\n\nConfiguration Entities found only in the Legacy version of $config_file\n\n$undefined_sections\n";

  print "\t\tWrote ${config_file}.MERGED\n";
  print "\t\tWrote changelog to ${config_file}.CHANGES \n\n";

  # write to the master todo list.
  print MTA_TODO_LIST "\n## ============================================================"; 
  print MTA_TODO_LIST "\n# commands for relocating ${config_file}" ;
  print MTA_TODO_LIST "\n## ============================================================"; 
  if ( -f "$Msg60ServerRoot/config/${config_file}" ) {
    print MTA_TODO_LIST "\ncp $Msg60ServerRoot/config/${config_file}";
    print MTA_TODO_LIST "  \\\n\t${Msg60ServerRoot}/data/setup/pre-Upgrade.\$_DATE \n";
  }
  print MTA_TODO_LIST "\ncp \$SAVEDIR/${config_file}.MERGED $Msg60ServerRoot/config/${config_file}\n" ;

  close LEGACY_INPUT ;
  close MTA_TODO_LIST ;
  close MERGED_FILE ;
  close CHANGELOG ;

}

sub ParseStructFile
{

  ##################################################################################
  #
  # Subroutine ParseStructFile
  #
  # Purpose: 
  #         Parse a structured configuration file like the dispatcher.cnf or
  #         the job_controller.cnf file.
  #
  ##################################################################################

  my ($section_name, $lhs, $rhs, $section) = "" ;
  my %values = () ;
  my $input_file = shift ; 

  print "\texamining $input_file\n" ; 

  open (INPUT, "<$input_file") || die ("unable to open the file $input_file\n");

  $section_name = "Global defaults";
  while (<INPUT>)
  {

    # The imta_tailor file and the dispatcher.cnf file now uses 
    # 'IMTA_BIN:filename' rather than full path names. Normalize
    s/$LegacyConfigs{local_serverroot}.bin.msg.imta.bin./IMTA_BIN:/g;

    # Msg6.0 now uses return_job instead of return.sh
    s/IMTA_EXE:return.sh/IMTA_BIN:return_job/;

    # Msg6.0 now uses IMTA_BIN instead of IMTA_EXE
    s/IMTA_EXE/IMTA_BIN/g;

    chomp;

    # skip over comment lines
    next if /^[!#]/;
    # skip over blank lines
    next if /^$/;

    # test for the '[' character in column 1, it marks the begining of a section
    if (s/^\[//) {
      s/]$//;
      $section_name = $_ ;
      next;
    }

    # split into attribute and values and load into hash table
    ($lhs,$rhs) = split (/=/,$_,2);

    # hash of hashes pg 271 of the programming perl
    $values{$section_name}{$lhs} = $rhs ;
    next;
  }

  close INPUT ;
  return %values;

}

sub ParseOptionsFile
{

  ##################################################################################
  #
  # Subroutine ParseOptionsFile
  #
  # Purpose: 
  #         Parse a MTA option file and return a hashtable.
  #
  ##################################################################################

  my ($section_name, $lhs, $rhs, $section) = "" ;
  undef my (%values) ;
  my $input_file = shift; 

  print "\texamining $input_file \n";

  open (INPUT, "<$input_file") || die ("unable to open the file $input_file\n");

  $InputFieldSep = "=" ;

  while (<INPUT>)
  {

    # skip over blank lines and comment lines
    next if /^[!#]/;
    next if /^$/;

    chomp;

    ( $lhs , $rhs ) = split (/$InputFieldSep/,$_,2);

    # force $lhs to lowercase
    $lhs =~ tr/A-Z/a-z/; 
    
    # escape the slashes and ? marks.
    $rhs =~ s/\//\\\//g;
    $rhs =~ s/\?/\\\?/g;
    # remove leading and trailing spaces
    $rhs =~ s/^ //g;
    $rhs =~ s/ $//g;

    # Account for cases where there are multivalued attributes
    if (!defined $values{$lhs})
    {
      $values{$lhs} = $rhs ;
      next;
    } else {
      @i = @j = ();
      $values{$lhs} .= "_x_|_x_$rhs" ;
      @i = split (/_x_\|_x_/ , $values{$lhs}) ;
      @j = sort @i ;
      $values{$lhs} = sprintf join ("_x_|_x_", @j);
      next;
    }

  }

  close INPUT ;
  return %values;

}

sub MergeParseConfigutil
{

  ##################################################################################
  #
  # Subroutine MergeParseConfigutil
  #
  # Purpose: 
  #         Merge configutil settings
  #
  ##################################################################################

  print "\nParsing configutil settings\n\n";

  open (TODO_LIST, ">>$TmpDir/do_the_upgrade.sh");
  open (MAKECONF, ">>$TmpDir/make_configutil_changes.sh");
  open (CHANGELOG, ">$TmpDir/configutil.CHANGES");
  open (CONFIGS, ">$TmpDir/configutil.in");

  print MAKECONF "\n# commands for adjusting configutil settings";
  print MAKECONF "\n## ============================================================\n\n";
  print MAKECONF "cd $Msg60ServerRoot/sbin\n\n" ; 

  $serveruid = $TargetConfigs{local_serveruid} ; 
  $servergid = $TargetConfigs{local_servergid} ; 

  foreach $configvar (keys %LegacyConfigs) {

    $varname = $configvar ;
    $varname =~ s/^local_/local./;

    next if $configvar =~ /objectclass/i; 
    next if $configvar =~ /PipePrograms/i; 
    next if $configvar =~ /timestamp/i; 
    next if $configvar =~ /name$/i; 

    $preserve_flag=0 ;

    foreach $PreservedVar ( @preserve_Msg60Vars ) {
      $preserve_flag = 1 if ( $configvar =~ /$PreservedVar/i ) ;
    }

    if ( $preserve_flag == 1 ) {
      next;
    }

    # migrate expire schedule
    if ( $varname eq "store.nsmsgexpirestart" ) {
      print CONFIGS "local.schedule.expire=\"* $LegacyConfigs{$configvar} * * * $Msg60ServerRoot/sbin/imexpire\"\n";
##    print MAKECONF "./configutil -o local.schedule.expire -v \\\"* $LegacyConfigs{$configvar} * * * $Msg60ServerRoot/sbin/imexpire\\\"\n";
      next; 
    }

    if ( $varname eq "store.partition.primary.Path" ) {
      if ( $LegacyConfigs{$configvar} =~ /^$LegacyConfigs{local_serverroot}/ ) {
          print MAKECONF "echo Note: The default primary partition for msg5.x is defined as\n";
          print MAKECONF "echo\n";
          print MAKECONF "echo             $LegacyConfigs{$configvar}\n";
          print MAKECONF "echo\n";
          print MAKECONF "echo       while the default primary partition for msg6.x is defined as\n";
          print MAKECONF "echo\n";
          print MAKECONF "echo             $TargetConfigs{$configvar}\n";
          print MAKECONF "echo\n";
          print MAKECONF "echo       Please manually define the correct primary partition that msg6.0 should use.\n";
          print MAKECONF "echo\n";
          print MAKECONF "echo       Either\n";
          print MAKECONF "echo            ./configutil -o $varname -v $LegacyConfigs{$configvar}\n";
          print MAKECONF "echo       or\n";
          print MAKECONF "echo            ./configutil -o $varname -v $TargetConfigs{$configvar}\n";
          print MAKECONF "echo\n";
          print MAKECONF "echo Defaulting to old value: $LegacyConfigs{$configvar}\n\n";
      }
      print CONFIGS "$varname=\"$LegacyConfigs{$configvar}\"\n";
##    print MAKECONF "./configutil -o $varname -v ";
##    print MAKECONF "$LegacyConfigs{$configvar} \n";
      print MAKECONF "# ./configutil -o $varname -v ";
      print MAKECONF "$TargetConfigs{$configvar} \n";
      print MAKECONF "\n";
      next;
    }


    if ( !defined $TargetConfigs{$configvar} ) {
      print CONFIGS "$varname=\"$LegacyConfigs{$configvar}\"\n";
##    print MAKECONF "./configutil -o $varname -v ";
##    if ( $LegacyConfigs{$configvar} =~ / / ){
##        print MAKECONF "\\\"$LegacyConfigs{$configvar}\\\"\n";
##    }
##    else {
##        print MAKECONF "$LegacyConfigs{$configvar} \n";
##    }
      next;
    }

    if ( $TargetConfigs{$configvar} ne $LegacyConfigs{$configvar} ) {
      if ( $LegacyConfigs{$configvar} =~ /^$LegacyConfigs{local_serverroot}/ ) {
        next;
      }
      print CHANGELOG "!The variable $configvar has been changed\n"; 
      print CHANGELOG "!\tfrom\t$TargetConfigs{$configvar}\n"; 
      print CHANGELOG "!\tto  \t$LegacyConfigs{$configvar}\n"; 
      print CONFIGS "$varname=\"$LegacyConfigs{$configvar}\"\n";
##    print MAKECONF "./configutil -o $varname -v ";
##    if ( $LegacyConfigs{$configvar} =~ / / ){
##        print MAKECONF "\\\"$LegacyConfigs{$configvar}\\\" \n";
##    }
##    else {
##        print MAKECONF "$LegacyConfigs{$configvar}\n";
##    }
      next;
    }
  }

  print CHANGELOG "\nConfigutil Entities found only in the Legacy Messaging Server\n\n";

  foreach $configvar (keys %LegacyConfigs) {
    next if $configvar =~ /timestamp/i; 
    next if $configvar =~ /objectclass/i; 
    next if $configvar =~ /name$/i; 
    $varname = $configvar ; 
    $varname =~ s/local_/local./g;
    if ( !defined $TargetConfigs{$configvar} ) {
      print CHANGELOG "!\t$varname = \"$LegacyConfigs{$configvar}\"\n";
      next; 
    }
  }
  print CHANGELOG "\nConfigutil Entities found only in the Msg60 Messaging Server\n\n";

  foreach $configvar (keys %TargetConfigs) {

    next if $configvar =~ /timestamp/i; 
    next if $configvar =~ /objectclass/i; 
    next if $configvar =~ /name$/i; 

    $varname = $configvar ; 
    $varname =~ s/local_/local./g;
    if ( !defined ($LegacyConfigs{$configvar}) ) {
      print CHANGELOG "!\t$varname = \"$TargetConfigs{$configvar}\"\n";
      next; 
    }

  }

  print MAKECONF "./configutil -i $TmpDir/configutil.in\n";

  close MTA_TODO_LIST ;
  close CHANGELOG ;
  close MAKECONF ;
  close CONFIGS ;

  print "\tCreated shell script make_configutil_changes.sh\n\n";
  print "\tWrote changelog file configutil.CHANGES \n\n";

}

sub MergeParseOptionsFile
{

  ##################################################################################
  #
  # Subroutine MergeParseOptionFile
  #
  # Purpose: 
  #         Merge two versions of the option.dat or imta_tailor or native_option file
  #
  ##################################################################################

  my ($output_file, $section_name,$values,$key,$lhs,$rhs, $section) = "" ;
  my ($obselete_vars,$attribute,$currentValue) = "";
  my (%values) = () ;
  my $input_file = shift ; 

  open (TODO_LIST,">>$TmpDir/do_the_upgrade.sh");
  open (INPUT, "<$input_file") || die ("unable to open the file $input_file\n");

  $undefined_vars = ""; 

  print "\n\tMerging ...\n";

  open (OUTPUT, ">$TmpDir/$config_file.MERGED");
  open (CHANGELOG, ">$TmpDir/$config_file.CHANGES");

  $InputFieldSep = "=" ;
  $OutputFieldSep = "=" ;

  while (<INPUT>)
  {

    # skip over comment lines
    if ( /^[!#]|^$/) {
      print OUTPUT $_ ;
      next;
    }

    chomp;

    ($lhs,$rhs) = split (/$InputFieldSep/,$_,2);

    # store the attribute in the $attribute variable before up casing. 
    $attribute = $lhs;
    # force $lhs to lowercase
    $lhs =~ tr/A-Z/a-z/; 

    # remove leading and trailing spaces
    $rhs =~ s/^ //g;
    $rhs =~ s/ $//g;

    # load into the %values hash table.
    if (!exists $values{$lhs})
    {
      $values{$lhs} = $rhs ;
    } else {
      @i = @j = ();
      $values{$lhs} .= "_x_|_x_$rhs" ;
      @i = split (/_x_\|_x_/ , $values{$lhs}) ;
      @j = sort @i ;
      $values{$lhs} = sprintf join ("_x_|_x_", @j);
    }

    # cleanup slashes and remove leading and trailing spaces.
    $currentValue = $rhs;
    $currentValue =~ s/\\//g;
    $currentValue =~ s/^ //g;
    $currentValue =~ s/ $//g;

    # clear the flags used to detect if section is obselete or preserved.
    $obselete_flag = 0;

    # test for obselete vars which should be skipped.
    foreach $obseleted (@obseleted_LegacyVars) {
      $obselete_flag = 1 if (( $lhs =~ /$obseleted/i ) && ( "$lhs" ne "imta_command_data" ));
    }

    if ( $obselete_flag == 1 ) {

       $obselete_vars .= "!\t$_\n";
       next;

    }

    if ( !defined ($TmpMsg6HasH{$lhs}) ) {
      
      $undefined_vars .= "!\t$_\n";
      print OUTPUT $_, "\n";
      next;

    } else {

      $targetValue = $TmpMsg6HasH{$lhs};
      $targetValue =~ s/\\//g;

      if ( $TmpMsg6HasH{$lhs} ne $values{$lhs} ) {
        # exemptions
        $preserve_flag = 0 ;
        foreach $PreservedVar ( @preserve_Msg60Vars ) {
          $preserve_flag= 1 if ( $lhs =~ /$PreservedVar/ ) ;
        }

        if ( $preserve_flag == 1 ) {
          $attribute =~ s/local_/local./g;
          print OUTPUT "$attribute${OutputFieldSep}$targetValue\n";
          print CHANGELOG "\n# The value of $attribute has been redefined:\n#\tfrom\t$currentValue";
          print CHANGELOG "\n#\tto\t$targetValue";
          next;
        }

        # See if the variable is a reference to something which lives under the
        # old server root. Make substitution in such cases.

        if ( $values{$lhs} =~ /^$LegacyConfigs{local_serverroot}/ ) {
          print OUTPUT "$attribute${OutputFieldSep}$targetValue\n";
          print CHANGELOG "\n# The value of $attribute has been redefined:\n#\tfrom\t$currentValue";
          print CHANGELOG "\n#\tto\t$targetValue";
          next;
        }

        print OUTPUT $_ ,"\n";
        next;

      }

      print OUTPUT $_ ,"\n";

    }

  }

  $obselete_vars =~ s/\\//g; 
  $undefined_vars =~ s/\\//g; 

  # sections which are new to msg60

  print CHANGELOG "!\n\nConfiguration Entities found only in the Msg60 version of $config_file\n\n";
  foreach $TmpMsg6Variable (keys %TmpMsg6HasH) {
    $TmpMsg6HasH{$TmpMsg6Variable} =~ s/\\//g;
    if ( !defined $Msg5tmpHasH{$TmpMsg6Variable} ) {
      print OUTPUT "$TmpMsg6Variable${OutputFieldSep}$TmpMsg6HasH{$TmpMsg6Variable}\n";
      print CHANGELOG "!\t$TmpMsg6Variable${OutputFieldSep}$TmpMsg6HasH{$TmpMsg6Variable}\n";
    }
  }

  if ( $obselete_flag == 1 ) {

       print CHANGELOG "\n!\n\nConfiguration Entities which are obseleted in Msg60 ";
       print CHANGELOG " $config_file\n$obselete_vars";
       next;

  }

  print CHANGELOG "\n!\n\nConfiguration Entities found only in the legacy";
  print CHANGELOG " version of $config_file\n\n\n$undefined_vars";

  open (MTA_TODO_LIST,">>$TmpDir/make_mta_config_changes.sh");
  print MTA_TODO_LIST "\n## ============================================================" ;
  print MTA_TODO_LIST "\n## commands for relocating ${config_file}" ;
  print MTA_TODO_LIST "\n## ============================================================" ;
  if ( -f "$Msg60ServerRoot/config/${config_file}" ) {
    print MTA_TODO_LIST "\ncp $Msg60ServerRoot/config/${config_file}";
    print MTA_TODO_LIST "  \\\n\t$Msg60ServerRoot/data/setup/pre-Upgrade.\$_DATE\n" ;
  }
  print MTA_TODO_LIST "\ncp ./${config_file}.MERGED $Msg60ServerRoot/config/${config_file}\n" ;

  print "\t\tWrote ${config_file}.MERGED \n";
  print "\t\tWrote changelog file ${config_file}.CHANGES \n\n";

  close MTA_TODO_LIST ;
  close INPUT ;
  close OUTPUT ;
  close CHANGELOG ;

}

