#!/usr/bin/env ruby.ruby3.4

# Copyright 2025 Open Text
#
# ------------------------------------------------------------------------------
# The only warranties for products and services of Open Text and its
# affiliates and licensors (“Open Text”) are as may be set forth in the express
# warranty statements accompanying such products and services.  Nothing herein
# should be construed as constituting an additional warranty. Open Text shall not
# be liable for technical or editorial errors or omissions contained herein. The
# information contained herein is subject to change without notice.
#
# Except as specifically indicated otherwise, this document contains
# confidential information and a valid license is required for possession, use or
# copying. If this work is provided to the U.S. Government, consistent with FAR
# 12.211 and 12.212, Commercial Computer Software, Computer Software
# Documentation, and Technical Data for Commercial Items are licensed to the U.S.
# Government under vendor's standard commercial license.
# ------------------------------------------------------------------------------

# Copyright (C) [2007-2009] Novell, Inc.  All Rights Reserved.

# THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
# IT MAY NOT BE USED, COPIED, DISTRIBUTED, DISCLOSED, ADAPTED, PERFORMED,
# DISPLAYED, COLLECTED, COMPILED, OR LINKED WITHOUT NOVELL'S PRIOR WRITTEN
# CONSENT.  USE OR EXPLOITATION OF THIS WORK WITHOUT AUTHORIZATION COULD
# SUBJECT THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.

# NOVELL PROVIDES THE WORK "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,
# INCLUDING WITHOUT THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE, AND NON-INFRINGEMENT. NOVELL, THE AUTHORS OF THE WORK,
# AND THE OWNERS OF COPYRIGHT IN THE WORK ARE NOT LIABLE FOR ANY CLAIM, DAMAGES,
# OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
# ARISING FROM, OUT OF, OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER
# DEALINGS IN THE WORK.
require 'rubygems'
require 'mig'
require 'yaml'
require 'base64'
require 'logger'
require 'psych'

include Migration

def closePasswdFileHandle
  @passwdFilefp.close if @passwdFilefp != nil
end
def create_ldap_containers ldap_name
  cn, context = parse_name ldap_name    
  if context != "\"\"" and not ldap_exist?(@opt['d'], @dest_username, context, @useSecure, @port)
    #recursively ensure each context exits before creating this one
    create_ldap_containers context
    justOne = context.split(",",2)
    array = justOne[0].split("=")  #array[0] will have the attribute and array[1] the value
    if array.length != 2
      print_message(ERROR, "Unable to parse ldap_name #{ldap_name}")
      return
    end
    if CONTAINER_HASH.has_key?(array[0].downcase)
      ou_str = <<STR 
dn: #{context}
objectClass: top
objectClass: #{CONTAINER_HASH[array[0].downcase]}
#{array[0]}: #{array[1]}
STR
      result = Migration.ldapadd @dest, @dest_username, ou_str, @passwd, @useSecure, @port
      if result == nil
        print_message(ERROR, $globalLdapErrorMsg)
        return
      end
    else
      print_message(WARN, "Cannot create container #{CONTAINER_HASH[array[0].downcase]}")
    end
  end
end

def add_ldap_object name, ldap_str
  name.gsub!("$", "\\$") if name.include?("$")
  if ldap_exist?(@opt['d'], @dest_username, name, @useSecure, @port)
    #requested by testing!
    print_message(DEBUG, "ldap string received in add_ldap_object method is:\n #{ldap_str}\n")
    if @opt['m']
      # The format of the contents written to the migdata.ldif file has been changed. So added the below if block to adjust the ldap string to the new format.
      # This is the fix for the bug #848001.
      if ldap_str.include?("ndsHomeDirectory:")
        ldap_str.insert(ldap_str.index("ndsHomeDirectory"), "changetype: modify\n")
        ldap_str.insert(ldap_str.index("ndsHomeDirectory"), "replace: ndsHomeDirectory\n")
        print_message(DEBUG, "ldap string after appending ldif new format is:\n #{ldap_str}\n")
      end
      result = Migration.ldapmod @dest, @dest_username, ldap_str, @passwd, @useSecure, @port 
      if result == nil
        print_message(ERROR,$globalLdapErrorMsg)
      end
    else
      print_message(WARN, "#{name} exists")
    end
  else
    create_ldap_containers name
    result = Migration.ldapadd @dest, @dest_username, ldap_str, @passwd, @useSecure, @port 
    if result == nil
      print_message(ERROR, $globalLdapErrorMsg)
      return
    end
  end
end

def add_password r
  unless r['password'] == nil
    cn, context = parse_name r['dn']
    passwd_str = <<STR
dn: #{r['dn']} 
changetype: modify
replace: userpassword
userpassword: #{r['password']}
STR
    result = Migration.ldapadd @dest, @dest_username, passwd_str, @passwd, @useSecure, @port 
    if result == nil
      print_message(ERROR, $globalLdapErrorMsg)
      return
    end
  end
end

def add_lum_object r
  unless r['primaryGroup'] == nil
    primary_group = r['primaryGroup'] 
    cn, context = parse_name r['dn'] 
    add_lum_group primary_group, true
    cn = cn.slice(3,cn.length)

    available_services = Dir.entries("/etc/pam.d")
    lum_services = r['lum-services']
    
    available_services.delete_if {|s| s == ".." or s == "."}
    available_services.each do |s|
      lum_services.delete_if do |d|
        s == d
      end
    end
    
    exclude = ""
    available_services.each do |e|
      exclude << "-E #{e} "  
    end
    
    #puts "namuseradd -m -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} -g #{primary_group} #{cn} 2>&1"
    #result = `namuseradd -m -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} -g #{primary_group} #{cn} 2>&1`
    #puts result
    err = nil
    #puts "Open3.popen3(\"namuseradd -m -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} -g #{primary_group} #{exclude} #{cn}\") do | stdin, stdout, stderr |" 
    Open3.popen3("namuseradd -m -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} -g #{primary_group} #{exclude} #{cn}") do | stdin, stdout, stderr | 
      Thread.new { loop { err += stderr.gets } }
      stdin.puts 
      #err = stderr.gets
    end
    if err
      err = err
      puts "- name: #{name}"
      puts "   error:#{err}"
    end

    unless r['groupMembership'] == nil
      #puts "namuseradd -m -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context2} -g #{r['groupMembership']} #{cn} 2>&1"
      #result = `namuseradd -m -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} -g #{primary_group} #{cn} 2>&1`
      #puts result
      err = nil
      Open3.popen3("namusermod -a #{@dest_username} -w #{ENV['MIGDATA2']} -G #{r['groupMembership']} #{r['dn']}") do | stdin, stdout, stderr | 
        Thread.new { loop { err += stderr.gets } }
        stdin.puts 
        #err = stderr.gets
      end
      if err
        err = err
        puts "- name: #{name}"
        puts "   error:#{err}"
      end
    end
  else
    unless r['lum-group'] == nil
      add_lum_group r['dn'], false
    end
  end
end

def add_lum_group name, primary = false
  cn, context = parse_name name
  cn = cn.slice(3,cn.length)
  #puts "namgroupadd -A -a #{@dest_username} -w #{ENV['MIGDATA2']@dest_password} -x #{context} #{cn} 2>&1"
  if primary
    result = `ifconfig | grep "inet addr" | grep -v 127.0.0.1`
    address = result.sub(/.*inet addr:(\S*).*/,'\1')
    address.strip!.rstrip!
    #puts "namgroupadd -A -W #{address} -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} #{cn} 2>&1"
    err = nil
    Open3.popen3("namgroupadd -A -W #{address} -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} #{cn}") do | stdin, stdout, stderr | 
      Thread.new { loop { err += stderr.gets } }
      stdin.puts
      #err = stderr.gets
    end
    if err
      err = err
      puts "- name: #{name}"
      puts "   error:#{err}"
    end
  else
    #result = `namgroupadd -A -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} #{cn} 2>&1` unless r['lum-group'] == nil
    err = nil
    Open3.popen3("namgroupadd -A -a #{@dest_username} -w #{ENV['MIGDATA2']} -x #{context} #{cn}") do | stdin, stdout, stderr | 
      Thread.new { loop { err += stderr.gets } }
      stdin.puts
      #err = stderr.gets
    end
    if err
      err = err
      puts "- name: #{name}"
      puts "   error:#{err}"
    end
  end
  #puts result
end

=begin
def test_users r
  e_t = false

  if check_ldap_exist? @source, @source_name, ENV['MIGDATA1'], r['entity']
    e_t = true
  end
  if e_t == false 
    $stdout.puts "test-mig-ldapusers:\n source-entity: #{r['entity']}\n entity-exists: #{e_t}" 
  end
end
=end

def audit_users r
  e_t = false
  l_t_s = false
  l_t = false
  p_u_t = false
  p_g_t = false

  if not @posix and check_ldap_exist?(@dest, @dest_username, r['entity'], @useSecure, @port)
    e_t = true
  end

  if r['primaryGroup'] and r['lum-group'] 
    l_t_s = true
    if check_lum_exist? @dest, @dest_username, ENV['MIGDATA2'], r
      l_t = true  
    end
  end

  if r['posix-group'] 
    if check_posix_group_exist? r['posix-group']
      p_g_t = true
    end
  end
  
  if r['posix-user'] 
    if check_posix_user_exist? r['posix-user'] 
      p_u_t = true
    end
  end

  unless @posix
    if e_t == false 
      $stdout.puts "audit-mig-users:\n- dest-entity: #{r['dn']}\n- entity-exists: #{e_t}" 
    elsif e_t and @opt['i']
      $stdout.puts "audit-mig-users:\n- dest-entity: #{r['dn']}\n- entity-exists: #{e_t}" 
    end
  end
  if e_t and l_t_s == true and l_t == false
    $stdout.puts "audit-mig-ldapusers:\n- dest-entity: #{r['dn']}\n- entity-exists: #{e_t}" 
  end
  if l_t_s == true and l_t == false
    $stdout.puts " lum-enabled: #{l_t}" 
  end
  $stdout.puts "audit-mig-posixuser: \n- dest-entity: #{r['posix-user']}\n- entity-exists: #{p_u_t}" if r['posix-user'] and (p_u_t == false or @opt['i'])
  $stdout.puts "audit-mig-posixgroup: \n- dest-entity: #{r['posix-group']}\n- entity-exists: #{p_g_t}" if r['posix-group'] and (p_g_t == false or @opt['i'])
end

def getEncodedValue val
  val.to_s.sub!(/_decoded_/, "")
  # Now encode the value
  val = Base64.encode64(val)
  if val != nil and val != ""
    val.strip!.gsub!("\n", "\n ")
  end
  return val
end

def mig_users r
  # Do not migrate user who is logged in to perform migration
  if not @opt['p'] and r['dn'].class == String and (r['dn'].casecmp(@destUserBeforeMod)) == 0
    return
  end
  if r['dn'] == nil or r['dn'] == ""
    print_message(WARN, "Could not find DN entry for object #{r['entity']}")
    return
  end

  ldap_str = ""
  ldif = r.clone
  ldif.delete("entity")
  ldif.delete("messageServer")
  ldif.delete("primaryGroup")
  ldif.delete("lum-services")
  ldif.delete("posix-group")
  ldif.delete("posix-user")
  # In ldif file first entry should be DN, so process it here only and delete it
  val = r['dn']
  if val =~ /^_decoded_/
    val = getEncodedValue(r['dn'])
    ldap_str = "dn::"
  else
    ldap_str = "dn:"
  end
  ldap_str << " #{val}\n"
  ldif.delete("dn")

  if ldif.has_key? "ndsHomeDirectory"
    test = ldif["ndsHomeDirectory"]
    if test != nil and test != ""
      test = test.slice(0, test.index("#")) if test.index("#")
      if not @posix and not ldap_exist?(@opt['d'], @dest_username, test, @useSecure, @port)
        ldif.delete("ndsHomeDirectory")
        print_message(WARN, "ndsHomeDirectory attribute was not set on #{ldap_str} because volume: #{test} doesn't exist in the tree")
        print_message(WARN, "Use: -H option on maptrustees or sed substitute operation on maptrustees output and retry migtrustees with the -m option") 
      end
    end
  end

  if ldif.has_key? "loginScript"
    if ldif['loginScript'] != nil and ldif['loginScript'] != ""
      ldif['loginScript'].gsub!(/#rn /,"\r\n")
    end
  end
  

  # Here we just need to handle the case where user random-password option. Specific password (if given)
  # is stored in a global variable
  if ldif.has_key?("objectClass") and ldif.values_at("objectClass").to_s.include?("inetOrgPerson")
    if @opt['r']
      chars = ("a".."z").to_a + ("1".."9").to_a
      passwd = Array.new(8, '').collect{chars[rand(chars.size)]}.join
    elsif @opt['s']
      passwd = @userpassword
    end
    ldif["userpassword"] = passwd
  end

  ldif.each do |key, val|
    keyStr = ""
    if val.class == Array
      val.each {|v|
        keyStr = ""
        # This code will take care of condition where we have multiple entries for single key, like multiple CNs
        # and some of them or all needs to encoded
        if v.to_s =~ /^_decoded_/ # If starts with _decoded_
          v = getEncodedValue(v)
          keyStr = "#{key}::"
        else
          keyStr = "#{key}:"
        end
        if key == 'groupMembership' or key == 'securityEquals' or 
          key == 'member' or key == 'equivalentToMe'
          ldap_str << "#{keyStr} #{v.to_s}\n" if not @posix and ldap_exist?(@opt['d'], @dest_username, v, @useSecure, @port)
        else
          ldap_str << "#{keyStr} #{v.to_s}\n"
        end
      }
    else
      if val.to_s =~ /^_decoded_/ # If starts with _decoded_
        val = getEncodedValue(val)
        keyStr = "#{key}::"
      else
        keyStr = "#{key}:"
      end
      unless key == "password"
        if key == 'groupMembership' or key == 'securityEquals' or
          key == 'member' or key == 'equivalentToMe'
          ldap_str << "#{keyStr} #{val.to_s}\n" if not @posix and ldap_exist?(@opt['d'], @dest_username, val, @useSecure, @port)
        else
          ldap_str << "#{keyStr} #{val.to_s}\n" 
        end
      end
    end
  end 
 # ldap_str << "\n"

  yaml_str = ""
  entity = r['entity']
  # When user executes migtustees to generate random password, we should check for @opt['r'] or @opt['--random-password'] and NOT @opt['i'].
  # This is the fix for bug#862481
  if @opt['r'] or @opt['--random-password']
    yaml_str = "- entity: #{entity}\n"  
    r.delete("entity")
    if ldap_str.include?("userpassword:") and @opt['r']
      # In this case just print the password in a specified file
      @passwdFilefp.puts "#{r['dn']} : #{passwd}"
    end
    
    r.each do |key, val|
      if val.class == Array
        yaml_str << "  #{key}:\n"
        val.each {|v|
          yaml_str << "  - #{v.to_s}\n"
        }
      else
        yaml_str << "  #{key}: #{val.to_s}\n"
      end
    end
    yaml_str << "\n"
    puts yaml_str
  end
  if @posix or r['posix-user'] or r['posix-group']
    add_posix_object r
  else
    unless entity == "[root]" or  entity == "[public]"
      add_ldap_object(r['dn'], ldap_str)
      add_lum_object r
    end 
    process_group_attrib(r['member'], 'groupMembership', r['dn'])
    process_group_attrib(r['equivalentToMe'], 'securityEquals', r['dn'])
    process_group_attrib(r['groupMembership'], 'member', r['dn'])
    process_group_attrib(r['securityEquals'], 'equivalentToMe', r['dn'])
  end
end

def process_group_attrib val, attrib, dn
  if val != nil
    if val.class == Array
      val.each do |v|
        check_and_add_attrib(dn, v, attrib)
      end
    else
      check_and_add_attrib(dn, val, attrib)
    end
  end
end

def check_and_add_attrib first, second, attrib
  origValOfSecond = second
  encoded = nil
  if second =~ /^_decoded_/
    origValOfSecond = second.sub(/_decoded_/, "")
    second = getEncodedValue(second)
    keyStr = "dn::"
  else
    keyStr = "dn:"
  end

  origValOfFirst = first
  if first =~ /^_decoded_/
    origValOfFirst = first.sub(/_decoded_/, "")
    first = getEncodedValue(first)
    attribKeyStr = "#{attrib}::"
  else
    attribKeyStr = "#{attrib}:"
  end

  if ldap_exist?(@opt['d'], @dest_username, origValOfSecond, @useSecure, @port) and not
     ldap_attribute_exist?(@opt['d'], @dest_username, origValOfSecond, attrib, origValOfFirst, @useSecure, @port)
    ldap_str = "#{keyStr} #{second}\nadd: #{attrib}\n#{attribKeyStr} #{first}\n"
    result = Migration.ldapmod @dest, @dest_username, ldap_str, @passwd, @useSecure, @port 
    if result == nil
      print_message(ERROR, $globalLdapErrorMsg) if not $globalLdapErrorMsg.include? "Type or value exists"
      return
    end
  end
end

def mig_ldif ldif
  add_ldap_object ldif.read  
end

def add_posix_object r
  if r['posix-user']
    groups = nil
    if r['groupMembership']
      groups = " -G "
      g = r['groupMembership']
      
      if g.class == Array  
        g.each_index do |i| 
          #Check if the groups exits 
          result = `groupadd #{g[i]} 2>&1`
          $stderr.puts "posix-group: #{g[i]}-> #{result}" unless result == "" or result.include?("already exists")
          if i == g.length-1
            groups << "#{g[i]}"
          else
            groups << "#{g[i]},"
          end
        end
      else
        #Check if the groups exits 
        result = `groupadd #{g} 2>&1`
        $stderr.puts "posix-group: #{g}-> #{result}" unless result == "" or result.include?("already exists")
        groups << "'#{g}'"
      end
    end

    flags = " --gid '#{r['primaryGroup']}'"
    if r['groupMembership']
      flags << groups
    end
    
    if (r['ndsHomeDirectory'] or r['homeDirectory'])
      if r['ndsHomeDirectory']
        if File.directory?(r['ndsHomeDirectory'])
          flags << " -d '#{r['ndsHomeDirectory']}'"
        else
          print_message(WARN, "Home Directory was not set for #{r['posix-user']} because directory #{r['ndsHomeDirectory']} doesn't exist")
          print_message(WARN, "use -H option of maptrustees or use sed to perform substitute operation on maptrustees ouput")
          print_message(WARN, "Home Directory was not set for #{r['posix-user']} because directory #{r['ndsHomeDirectory']} doesn't exist")
        end
      else
        if File.directory?(r['ndsHomeDirectory'])
          flags << " -d #{r['homeDirectory']}"
        else
          print_message(WARN, "Home Directory was not set for #{r['posix-user']} because directory #{r['ndsHomeDirectory']} doesn't exist")
                                  print_message(WARN, "use -H option of maptrustees or use sed to perform substitute operation on maptrustees ouput")
          print_message(WARN, "Home Directory was not set for #{r['posix-user']} because directory #{r['ndsHomeDirectory']} doesn't exist")
        end
      end
    end

    #puts "useradd #{flags} #{r['posix-user']} 2>&1"
    result = `useradd #{flags} #{r['posix-user']} 2>&1`
    if result.include? "already exists"
      if @opt['m']
        result = `usermod #{flags} #{r['posix-user']} 2>&1`
      else
        print_message(WARN, "posix-user: #{r['posix-user']}->already exists")
      end
    end
    #change the password
    if result == "" and r['userpassword']
      `echo #{r['userpassword']} | passwd #{r['posix-user']} --stdin 2>&1`
    end
    #puts result
    $stderr.puts "posix-user: #{r['posix-user']}-> #{result}" unless result == "" or result.include?("already exists")
  elsif r['posix-group']
    #puts "groupadd #{r['posix-group']} 2>&1"
    result = `groupadd #{r['posix-group']} 2>&1`
    $stderr.puts "posix-group: #{r['posix-group']}-> #{result}" unless result == "" or result.include?("already exists")
  end
end

def main
  $argdef = [
    ['d', 'destination-server', "the IP address of the destination server", ParseArgText],
    ['i', 'verbose', "displays verbose information", ParseArgBool],
    ['A', 'audit', "audit results of the user/group migration", ParseArgBool],
    ['m', 'modify-existing', "modify/update the user/group if it already exists", ParseArgBool],
    ['p', 'posix', "creates POSIX users and groups on destination server. The default is ldap if no mapping flag is specified", ParseArgBool],
    [nil, 'destination-unsecure-ldap', "use unsecure LDAP for all LDAP commands", ParseArgBool],
    [nil, 'destination-ldap-port', "port where LDAP server is listening", ParseArgText],
    ['r', 'random-password', "generates random passwords for users to be created (default is no password created)", ParseArgBool],
    ['s', 'specific-password', "creates specified password for each user to be created (default is no password created)", ParseArgBool],
    [nil, 'newusers-password-file', "Specifies the file name to store new user's credentials. This option can be used along with --random-password (-r)", ParseArgText],
    [nil, 'use-casa', "use CASA to store/retrieve username and password information", ParseArgBool],
    [nil, "inputfile", "the input file (usually produced by running 'maptrustees'). If not given on the commandline, will read from stdin", ParseArgOrd],
    ['c', "session-file", "stores the migration progress, this file can be used to continue the migration", ParseArgText],
    [nil, 'progress', "display progress in terms of number of objects processed", ParseArgBool],
    [nil, 'progress-interval', "time interval for displaying progress", ParseArgText],
    [nil, 'precheck', "checks whether system meets all pre-requisite to start migtrustees",ParseArgBool],
    [nil, 'debug', "generate debug log", ParseArgBool],
    ParseArgHelp,
    ParseArgUsage
  ]

  @init = true
  migtrusteesCommand = "#{$0}"
  debugOpt = false
  $*.each do |arg|
    if arg.strip == "--debug"
      debugOpt = true
    end
    migtrusteesCommand += " #{arg}"
  end
  initLog(debugOpt, "migtrustees")
  print_message(DEBUG, "migtrustees command started at #{`date +%d-%m-%y\\ %H:%M:%S`}")
  print_message(DEBUG, "migtrustees command executed as: #{migtrusteesCommand}")

  @opt = {}
  (@opt, extraargs) = parseargs(ARGV, $argdef)

  if @opt['progress-interval'] and not @opt['progress']
    print_message(FATAL, "Cannot specify --progress-interval without --progress")
    exit 1
  end
  @opt['use-casa'] = true if ENV["MIG_USE_CASA"] != nil

  filemap = @opt['inputfile']

  if @opt['p']
    @posix = true
  end

  if filemap == nil
    filemap = $stdin
    if filemap.isatty
      print_message(FATAL, "Must specify input file or provide input to stdin")
      printUsage($argdef)
      exit 1
    end
  end
  if @opt['inputfile']
    if not File.exist?(@opt['inputfile']) or not File.size?(@opt['inputfile'])
      print_message(FATAL, "Specified input file #{@opt['inputfile']} does not exist or it is empty")
      exit 1
    end
  end

  if filemap.class == String
    input = File.open filemap,"r" 
  else
    input = filemap
  end

  if @opt['L']
    mig_ldif input
  else
    # Load a single doc to know whether it is a POSIX and class of objects
    if input != $stdin
      Psych.load_stream(input) do |doc|
        if doc != nil
          doc.each do |obj|
            if obj.class == FalseClass
              print_message(FATAL, "Input file not in correct format") 
              exit 1
            end
            break
          end
          break
        else
          print_message(INFO, "Given maptrustees input file is empty")
          exit 0
        end
      end #do |doc|
      # Set the file pointer to beginning of the file
      input.seek(0, IO::SEEK_SET)
    end
    if @posix and @opt['d']
      print_message(FATAL, "Migrating posix users does not require --destination-ldap parameter")
      printUsage($argdef)
      return 1
    end
    
    if not @posix and not @opt['d']
      print_message(FATAL, "Must specify --destination-ldap")
      printUsage($argdef)
      exit 1
    end
    if @opt['progress-interval'] and @opt['progress-interval'].to_i <= 0
      print_message(FATAL, "value for --progress-interval should be greater than 0")
      exit 1
    end
    if @opt['s'] and @opt['r']
      print_message(FATAL, "Cannot pass both --specific-password and --random-password options")
      exit 1
    end
   if @opt['newusers-password-file'] and not @opt['r']
      print_message(FATAL, "Cannot pass newusers-password-file without option --random-password options")
      exit 1
   end
   if @opt['r'] and not @opt['newusers-password-file']
      print_message(FATAL, "Need to specify --newusers-password-file along with option --random-password options")
      exit 1
   end
   if @opt['r']
     if File.exist?(@opt['newusers-password-file'])
        @passwdFilefp = File.open(@opt['newusers-password-file'], "a")
     elsif
        @passwdFilefp = File.open(@opt['newusers-password-file'], File::CREAT| File::WRONLY, 0400)
     end
     if @passwdFilefp == nil
       print_message(FATAL, "Failed to create file #{@opt['newusers-password-file']}")
       exit 1
     end
   end

    if @opt['d']
      credentials = Migration::KeyStore.credential(@opt['d'], :ldap_dn, @opt['use-casa'])
      unless File.exist? "/var/novell/migration"
        Dir.mkdir "/var/novell/migration"
      end

      @passwd = credentials[:password]

      #ENV['MIGDATA2'] = credentials[:password]
      @dest_username = "\"#{credentials[:username]}\""
      @destUserBeforeMod = credentials[:username]
      @dest = @opt['d']
      credentials = nil
    end
    
    if @opt['s']
      @userpassword = readPassword("Enter Specific Password for new users:")
    end

    objNumber = 0
    @jobDone = false
    if @opt['c']
      $session =  "---\n"
      $session << "  started-on: #{`date`}"
      $session << "  dest-server: #{@opt['d']}\n"
      $session << "  utility: migtrustees\n"
      # Session file specified, check whether it exists... 
      if File.exist?(@opt['c']) and File.size?(@opt['c']) != 0
        sessionInput = File.open(@opt['c'], "r")
        sessionInfo = YAML.load(sessionInput)
        if sessionInfo['utility'] != "migtrustees" or
          sessionInfo['dest-server'] != @opt['d']
          print_message(FATAL, "Invalid session file #{@opt['c']} specified. Please check the file")
          exit 1
        end
        if sessionInfo['status'] =~ /Completed/
          print_message(WARN, "Session file #{@opt['c']} shows migtrustees's status as 'Completed'")
          exit 0
        elsif sessionInfo['status'] =~ /Stopped/
          objNumber = sessionInfo['stopped-at'].to_i
        else
          print_message(FATAL, "Session file is corrupted")
          exit 1
        end
        sessionInput.close
      end
    end
    if input != $stdin and @opt['progress']
      print_message(INFO, "precomputing for displaying progress")
      @totObjs = 0
      @totObjs = getTotalObjs(@opt['inputfile'])
      print_message(INFO, "Total number of objects to be processed is #{@totObjs}")
      if @opt['progress-interval']
        @progressInterval = @opt['progress-interval'].to_i
      else
        @progressInterval = 30
      end                         
      startTimer(@progressInterval)
    end
    if @opt['precheck']
      $stderr.puts "Information: Validation for migtrustees done successfully\n"
      exit 0
    end
    @useSecure = true
    @useSecure = false if @opt['destination-unsecure-ldap']
    @port = nil
    @port = @opt['destination-ldap-port'] if @opt['destination-ldap-port']
    createLdapConfFile()

    @init = false
    @cnt = 0
    Psych.load_stream(input) do |doc|
      if doc != nil
        doc.each do |obj|
          if @cnt >= objNumber
            if @opt['t']
              test_users obj
            elsif @opt['A']
              audit_users obj
            else
              mig_users obj
            end
            if @cnt % 20 == 0 and @opt['c']
              writeToSessionFile(@jobDone, @cnt)
            end
            if @cnt != 0 and @cnt % NO_OF_OBJECTS_PER_RECORD == 0
              puts "---"
            end
          end
          @cnt += 1
        end
      end
    end
  end
  input.close if input.class == File
  @passwdFilefp.close if @passwdFilefp != nil
  @passwdFilefp = nil
  if @opt['c']
    @jobDone = true
    writeToSessionFile(@jobDone, @cnt) 
  end
  deleteLdapConfFile()
  print_message(PROGRESS, "Created #{@totObjs} trustees of #{@totObjs}") if @opt['progress']
  print_message(DEBUG, "migtrustees completed successfully at #{`date +%d-%m-%y\\ %H:%M:%S`}")
end

begin
  trap("ALRM"){
    print_message(PROGRESS, "Created #{@cnt} trustees of #{@totObjs}")
    writeToSessionFile(@jobDone, @cnt) if @opt['c']
    startTimer(@progressInterval) if @opt['progress']
  }

  main
rescue SystemCallError => failed  
  print_message(FATAL, "SystemCallError, " +failed.message)
  closePasswdFileHandle()
  exit failed.errno
rescue MigrationException => e 
  print_message(FATAL, "MigrationException, " + e.to_s)
  closePasswdFileHandle()
  exit e.code 
rescue LdapAuthError => e 
  print_message(FATAL, "LdapAuthError, " + e.to_s)
  closePasswdFileHandle()
  exit e.code 
rescue MigSyntaxError => e
  print_message(FATAL, "MigSyntaxError" + e.to_s)
  closePasswdFileHandle()
  printUsage($argdef)
  exit e.code
rescue Interrupt
  print_message(DEBUG, "Interrupted by user")
  deleteLdapConfFile()
  writeToSessionFile(@jobDone, @cnt) if @opt['c'] and @init == false
  exit 1
rescue
  print_message(FATAL, "Caught exception: #{$!.message}\n Backtrace:\n #{$!.backtrace.join("\n")}")
  closePasswdFileHandle()
  exit 1
ensure
  deleteLdapConfFile()
  closePasswdFileHandle()
  File.delete("/tmp/migdata.ldif") if File.exist?("/tmp/migdata.ldif")
  $log.close if @opt['debug']
end
