#!/bin/sh
# \
	exec tclsh "$0" ${1+"$@"}
#
# Copyright C 2003 Sun Microsystems, Inc.
# All rights reserved. Use is subject to license terms. 
#
# 
# Sun, Sun Microsystems, and the Sun logo are trademarks or registered
# trademarks of Sun Microsystems, Inc. in the United States and other
# countries.
# 
# Federal Acquisitions: Commercial Software--Government Users Subject to
# Standard License Terms and Conditions
#

# Role : This script
# ----     
#        - downgrades/upgrades the DPS part of serverroot and the DPS instances in serverroot
#         (see usage for more details)



#####################
# Checks that syncType argument value is valid
#####################
proc assertUpOrDown { syncType {msg [list]} } {

    if [string compare "upgrade" $syncType] {
	if [string compare "downgrade" $syncType] {
	    error [lappend msg "value should be upgrade or downgrade."]
	}
    }

    return $syncType
}

#########################
# Argument where value check is done
#########################

proc noedit { x {msg [list]} } {
    return $x
}

#################################
# Clarify content of tailor
#################################

proc unscramble {string} {
    
    if [string match "{}*" $string] {
	
        set i 0
        foreach char   {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h\
			    i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /} {
	    set theArray($char) $i
	    incr i
        }
	
        set theGroup 0
        set Bits 18
        foreach char [split [string trim $string "{}"] {}] {
	    if {[string compare $char "="]} {
		set theBits $theArray($char)
		set theGroup [expr {$theGroup | ($theBits << $Bits)}]
		if {[incr Bits -6] < 0} {
		    scan [format %06x $theGroup] %2x%2x%2x t1 t2 t3
		    append theResult [format %c%c%c $t1 $t2 $t3]
		    set theGroup 0
		    set Bits 18
		}
	    } else {
		scan [format %04x $theGroup] %2x%2x t1 t2
		if {$Bits == 6} {
		    append theResult [format %c $t1]
		} elseif {$Bits == 0} {
		    append theResult [format %c%c $t1 $t2]
		}
		break
	    }
        }
	
    } else {
	set theResult $string
    }
    return $theResult
}

#########################################
# Clarify content of uninstallContext.tcl
#########################################

proc decode_uninstall_dwp { xName xInstance } {
    upvar $xName x
    upvar $xInstance instance

    set instanceRoot [file join $x(-serverroot) ${instance}]

    set fName [file join $instanceRoot uninstallContext.tcl]
    set 522fName [file join $instanceRoot 522uninstallContext.tcl]

    if [file exists $fName] {

	file rename $fName $522fName

	set 522fFile [open ${522fName} r+ ]
	set fperm  [file attributes ${522fName} -permissions]
	set fgroup [file attributes ${522fName} -group]
	set fowner [file attributes ${522fName} -owner]
	
	set fFile [open ${fName}  w  $fperm ]

	while {[gets $522fFile line] >= 0} {
	    if [regexp {(password)} $line] {
		set dwp [string trim [lindex [split $line "\""] 1] " "]
		puts $fFile "  set x(password) \"[unscramble $dwp]\""
	    } else {
		puts $fFile $line
	    }
	}
	close $fFile
	file attributes ${fName} -permissions $fperm -group $fgroup -owner $fowner

	close $522fFile
	file delete $522fName
	return "SUCCESS: $fName clarified.\n"
    } else {
	return "WARNING: $fName NOT FOUND.\n"
    }
}

#################################
# Clarify content of tailor file
#################################

proc decode_tailor_dwp { xName xInstance } {
    upvar $xName x
    upvar $xInstance instance

    set instanceRoot [file join $x(-serverroot) ${instance}]

    set tailorName [file join $instanceRoot  etc tailor.txt]
    set 522tailorName [file join $instanceRoot  etc 522tailor.txt]

    if [file exists $tailorName] {

	file rename $tailorName $522tailorName

	set 522tailorFile [open ${522tailorName} r+ ]
	set tailorperm  [file attributes ${522tailorName} -permissions]
	set tailorgroup [file attributes ${522tailorName} -group]
	set tailorowner [file attributes ${522tailorName} -owner]
	
	set tailorFile [open ${tailorName}  w  $tailorperm ]

	set pattern configuration_bind_pw
	
	while {[gets $522tailorFile line] >= 0} {
	    if [regexp $pattern $line] {
		set dwp [string trim [lindex [split $line " "] 1] " "]
		puts $tailorFile "configuration_bind_pw: [unscramble $dwp]"
	    } else {
		puts $tailorFile $line
	    }
	}
	close $tailorFile
	file attributes ${tailorName} -permissions $tailorperm -group $tailorgroup -owner $tailorowner

	close $522tailorFile
	file delete $522tailorName
	return "SUCCESS: $tailorName clarified.\n"
    } else {
	return "WARNING: $tailorName NOT FOUND.\n"
    }
}

###########################################################
# changes in utilities files the name of the jar to be used
###########################################################

proc emit { xName fName cName } {
    upvar $xName x
    
    set perm  [file attributes [file join $x(-serverroot) $fName] -permissions]
    set owner [file attributes [file join $x(-serverroot) $fName] -owner]
    set group [file attributes [file join $x(-serverroot) $fName] -group]

    set oName [file join $x(-serverroot) $fName]
    set oFile [open $oName w]
    
    puts $oFile "#!/bin/sh"
    puts $oFile "#"
    puts $oFile "# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to"
    puts $oFile "# license terms. Copyright C 2001-2003 Sun Microsystems, Inc."
    puts $oFile "# All rights reserved."
    puts $oFile "#"
    puts $oFile "\"[file join $x(-serverroot) bin/base/jre/bin/java]\" -cp \"[file join $x(-serverroot) java/jars/$x(jarName)]:[file join $x(-serverroot) java/ldapjdk.jar]:[file join $x(-serverroot) java/mcc52.jar]\" $cName \"\$@\""
    
    close $oFile

    file attributes ${oName} -permissions $perm -group $group -owner $owner
    
}

##################################################
# Does the common (to all instances) upgrade stuff
##################################################

proc upgrade_serverroot_global { xName } {
    upvar $xName x
    global tcl_platform

    set result ""
    if [string equal $tcl_platform(platform) unix] {
	
	# Replace symlink by dir hierarchy
	set DPSDIR [file join $x(-serverroot) bin dps]

	if [catch {file readlink ${DPSDIR}} result] {
	    append result "\nWARNING: ${DPSDIR} is expected to exist as a symbolic link : no action.\n"
	    return $result
	} else {
	    set target $result
	    set group [file attributes [file join $x(-serverroot)] -group]
	    set owner [file attributes [file join $x(-serverroot)] -owner]
	    append result "\nTarget: ${target} ${owner} ${group}"
	}

	# sr/bin/dps is a symlink
	cd [file join $x(-serverroot) bin]
	# rm symlink
	file delete dps
	# creates subdirs instead
	file mkdir [file join $x(-serverroot) bin dps]
	file attributes [file join $x(-serverroot) bin dps] -permissions 0755 -group $group -owner $owner

	file mkdir [file join $x(-serverroot) bin dps server]
	file attributes [file join $x(-serverroot) bin dps server] -permissions 0755 -group $group -owner $owner

	file mkdir [file join $x(-serverroot) bin dps server bin]
	file attributes [file join $x(-serverroot) bin dps server bin] -permissions 0755 -group $group -owner $owner

	cd [file join $x(-serverroot) bin dps]

	exec ln -s [file join ${target} admin] admin
	exec chown -h  $owner admin
	exec chgrp -h  $group admin
	exec ln -s [file join ${target} install] install
	exec chown -h  $owner install
	exec chgrp -h  $group install
	exec ln -s [file join ${target} COPYRIGHT] COPYRIGHT
	exec chown -h  $owner COPYRIGHT
	exec chgrp -h  $group COPYRIGHT
	exec ln -s [file join ${target} license.txt] license.txt
	exec chown -h  $owner license.txt
	exec chgrp -h  $group license.txt
	
	cd [file join $x(-serverroot) bin dps server]
	exec ln -s [file join ${target} server script] script
	exec chown -h  $owner script
	exec chgrp -h  $group script
	cd [file join $x(-serverroot) bin dps server bin]
	exec ln -s [file join ${target} server bin dpsAsks.txt] dpsAsks.txt
	exec chown -h  $owner dpsAsks.txt
	exec chgrp -h  $group dpsAsks.txt
	exec ln -s [file join ${target} server bin dpsAsks_en.txt] dpsAsks_en.txt
	exec chown -h  $owner dpsAsks_en.txt
	exec chgrp -h  $group dpsAsks_en.txt
	exec ln -s [file join ${target} server bin ldapfwd] ldapfwd 
	exec chown -h  $owner ldapfwd
	exec chgrp -h  $group ldapfwd

	# Create link from serverroot to installdir for new jars
	cd [file join $x(-serverroot) java jars]
	if [catch {file readlink dps52.jar} result] {
	    append result "\nWARNING: dps52.jar is expected to exist as a symbolic link : no action.\n"
	} else {
	    
	    set group [file attributes [file join $x(-serverroot) java jars] -group]
	    set owner [file attributes [file join $x(-serverroot) java jars] -owner]

	    set jartarget [file dirname $result]
	    append result "\njartarget: ${jartarget} ${owner}\n"
	    cd $x(-cid)/java/jars
	    if [ catch {glob dps522*.jar} iList ] {
	    } else {
		if [expr [llength $iList] > 0] {
		    foreach i $iList {
			exec ln -s [file join ${jartarget} $i] [file join $x(-serverroot) java jars $i]
			exec chown -h  $owner [file join $x(-serverroot) java jars $i]
			exec chgrp -h  $group [file join $x(-serverroot) java jars $i]
		    }
		}
	    }
	}	
    }

    # changes in utilities files the name of the jar to be used
    set x(jarName) dps522.jar
    emit x bin/dps_utilities/dpsconfig2ldif com.iplanet.idar.util.IDARPrintConfig 
    emit x bin/dps_utilities/dpsldif2config com.iplanet.idar.task.ImportConfigurationLdif
    emit x bin/dps_utilities/migratefromidar50 com.iplanet.idar.util.MigrateConfig3

    append result "SUCCESS: $x(-serverroot) upgraded.\n"
    return $result
}

####################################################
# Does the instance by instance upgrade stuff
####################################################

proc upgrade_serverroot_instance { xName xInstance } {
    upvar $xName x
    upvar $xInstance instance

    set result "${instance} will be upgraded\n"

    set instanceRoot [file join $x(-serverroot) ${instance}]
    set patch_to_version "50202"

    cd [file join $x(-serverroot) shared bin]
    set rc [catch {exec $x(cmd_to_sync_version) -isUpgradeNeeded -v ${patch_to_version} \
		       -i ${instanceRoot}} cmdresult]
    append result "${instance}: -isUpgradeNeeded result:$cmdresult rc:$rc expected:0=upgrade needed.\n"

    if { $rc == 0 } {

	set rcv [catch {exec $x(cmd_to_sync_version) -getVersionToUpgrade -v ${patch_to_version}  \
			   -i ${instanceRoot}} cmdresult]
	append result "${instance}: -getVersionToUpgrade result:$cmdresult rcv:$rcv expected:1=RTM.\n"
	
	if { $rcv == 1 } {

	    cd [file join $x(-serverroot) alias]
	    if [catch {glob ${instance}*.db} aliasList] {
		append result "${instance}: no certificate to upgrade.\n"
	    } else {
		# Upgrade NSS certificates
		cd [file join $x(-serverroot) shared bin]
		set rccert [catch {exec $x(cmd_to_upgrade_sec) -r $x(-serverroot) -i ${instance} -V 7 -v 8} cmdresult]
		append result "${instance}: $x(cmd_to_upgrade_sec) result:$cmdresult rc:$rccert expected:0=SUCCESS.\n"
	    }
	    
	    # No upgrade of tailor encoding for existing instance
	    
            cd [file join $x(-serverroot) shared bin]
	    set rc [catch {exec $x(cmd_to_sync_version) -upgradeNotification -v ${patch_to_version} \
			   -i ${instanceRoot}} cmdresult]
	    append result "${instance}: -upgradeNotification result:$cmdresult rc:$rc.\n"

	    append result "SUCCESS: ${instance}: upgraded.\n"
	}
    }
    return $result
}

##############################
# Upgrade from 52 to 52patch2
# 2 steps :
#   - global job (not "instance by instance")
#   - instance by instance job
##############################

proc upgrade_serverroot_52_to_52patch2 { xName } {
    upvar $xName x

    # common part of the job
    set result [upgrade_serverroot_global x]

    # instance by instance part of the job
    if [catch {glob $x(-serverroot)/dps-*} iList] {
	append result "No instances.\n"
    } else {
	append result "Instances list : $iList\n"
	if [expr [llength $iList] > 0] {
	    foreach i $iList {
		set x(instance) [file tail "$i"]
		set lrc [upgrade_serverroot_instance x x(instance)]
		append result "$lrc"
	    }
	}
    }
    return $result
}


####################################################
# Does the common (to all instances) downgrade stuff
####################################################

proc downgrade_serverroot_global { xName } {
    upvar $xName x
    global tcl_platform

    set result ""

    if [string equal $tcl_platform(platform) unix] {
	
	# Replace dir hierarchy by symlink
	set DPSDIR [file join $x(-serverroot) bin dps]
	
       	# SR/bin/dps must be a directory and not a symlink
	if [catch {file readlink ${DPSDIR}} result ] {
	    if [catch {file isdirectory ${DPSDIR}} result ] {
		set result "${DPSDIR} is expected to exist as a directory : no common update needed.\n"
		return $result
	    }
	}

	# isdirectory ${DPSDIR}/server/bin
	if [catch {file isdirectory [file join ${DPSDIR} server bin]} result ] {
	    error "[file join ${DPSDIR} server bin] is expected to exist as a directory."
	} 

	cd [file join ${DPSDIR} server bin]
	
	# rm symlink dpsAsks.txt
	set fn dpsAsks.txt
	if [catch {file readlink ${fn}} result ] {
	    error "[file join ${DPSDIR} server bin ${fn}] is expected to exist as a symbolic link."
	} 
	file delete $fn
	
	# rm symlink dpsAsks_en.txt
	set fn dpsAsks_en.txt
	if [catch {file readlink ${fn}} result ] {
	    error "[file join ${DPSDIR} server bin ${fn}] is expected to exist as a symbolic link."
	}
	file delete $fn
	
	# rm symlink ldapfwd
	set fn ldapfwd
	if [catch {file readlink ${fn}} result ] {
	    error "[file join ${DPSDIR} server bin ${fn}] is expected to exist as a symbolic link."
	}
	file delete $fn
	
	# rm dir ${DPSDIR}/server/bin
	cd [file join ${DPSDIR} server]
	file delete bin
    }
        
    # rm symlink  script
    cd [file join ${DPSDIR} server]
    if [catch {file readlink script} lresult] {
	error "[file join ${DPSDIR} server script] is expected to exist as a symbolic link."
    } else {
	# keep track of the directory containing binaries
	set target [file dirname [file dirname $lresult]]
	append result "Target: ${target}"
    }
    file delete script
    
    cd ${DPSDIR}
    # rm dir ${DPSDIR}/server
    if [catch {file isdirectory server} result] {
	error "[file join ${DPSDIR} server] is expected to exist as a directory."
    }
    file delete server
    
    # rm symlink admin
    if [catch {file readlink admin} result] {
	error "[file join ${DPSDIR} admin] is expected to exist as a symbolic link"
    }
    file delete admin

    # rm symlink install
    if [catch {file readlink install} result] {
	error "[file join ${DPSDIR} install] is expected to exist as a symbolic link"
    }
    file delete install

    # rm symlink COPYRIGHT
    if [catch {file readlink COPYRIGHT} result] {
	error "[file join ${DPSDIR} COPYRIGHT] is expected to exist as a symbolic link"
    }
    file delete COPYRIGHT

    # rm symlink license.txt
    if [catch {file readlink license.txt} result] {
	error "[file join ${DPSDIR} license.txt] is expected to exist as a symbolic link"
    }
    file delete license.txt

    # replace dir dps by symlink
    cd [file join $x(-serverroot) bin]
    file delete dps    
    set cmd [list exec ln -s ${target} ${DPSDIR}]
    if [catch $cmd result] {
	error "$cmd : $result"
    }
    exec chown -h  [file attributes [file join $x(-serverroot)] -owner] ${DPSDIR}
    exec chgrp -h  [file attributes [file join $x(-serverroot)] -group] ${DPSDIR}

    # rm symlink to new jars
    # also eventually delete icon file that may have been downloaded by the console
    cd [file join $x(-serverroot) java jars]
    if [ catch {glob dps522*} iList ] {
    } else {
	append result "\nWill delete : $iList\n"
	if [expr [llength $iList] > 0] {
	    foreach i $iList {
		catch {file delete $i} lresult
	    }
	}
    }

    # changes in utilities files the name of the jar to be used
    set x(jarName) dps52.jar
    emit x bin/dps_utilities/dpsconfig2ldif com.iplanet.idar.util.IDARPrintConfig 
    emit x bin/dps_utilities/dpsldif2config com.iplanet.idar.task.ImportConfigurationLdif
    emit x bin/dps_utilities/migratefromidar50 com.iplanet.idar.util.MigrateConfig3
        
    append result "SUCCESS: $x(-serverroot) downgraded."
    return $result
}

####################################################
# Does the instance by instance downgrade stuff
####################################################
 
proc downgrade_serverroot_instance { xName xInstance } {
    upvar $xName x
    upvar $xInstance instance

    set result ""

    set instanceRoot [file join $x(-serverroot) ${instance}]
    set backout_from_version "50202"

    cd [file join $x(-serverroot) shared bin]
    set rc [catch {exec $x(cmd_to_sync_version) -isDowngradePossible  -v ${backout_from_version} \
		       -i ${instanceRoot}} cmdresult]
    append result "${instance}: -isDowngradePossible result:$cmdresult rc:$rc expected:0=Yes.\n"

    if { $rc == 0 } {

	cd [file join $x(-serverroot) shared bin]
	set rc [catch {exec $x(cmd_to_sync_version) -getVersionToDowngradeTo  -v ${backout_from_version} \
			   -i ${instanceRoot}} cmdresult]
	append result "${instance}:-getVersionToDowngradeTo result:$cmdresult rc:$rc expected:1=RTM.\n"
	
	if { $rc == 1 } {

	    cd [file join $x(-serverroot) alias]
	    if [catch {glob ${instance}*.db} aliasList] {
		append result "${instance}: no certificate to be manually downgraded.\n"
	    } else {
		# Warning to manually downgrade NSS certificates
		puts stdout "Reverting to prepatch certificate database for ${instance}."
		puts stdout "Certificates added after patching the server must be downgraded manually."
		puts stdout "See 'Installation Guide' or patch README for instructions."
	    }

	    # Downgrade encoding in tailor and uninstallcontext files
	    append result [decode_tailor_dwp x  instance]
	    append result [decode_uninstall_dwp x  instance]

	    cd [file join $x(-serverroot) shared bin]
	    set rc [catch {exec $x(cmd_to_sync_version) -downgradeNotification \
			       -i ${instanceRoot}} cmdresult]
	    append result "${instance}:-downgradeNotification result:$cmdresult rc:$rc.\n"
	    
	    # Remove the upgrade directory
	    set rc [catch [file delete [file join ${instanceRoot} upgrade versions.conf]] lresult]
	    append result "${instance}: delete [file join ${instanceRoot} upgrade versions.conf] result:$lresult rc:$rc.\n"

	    set rc [catch [file delete [file join ${instanceRoot} upgrade]] lresult]
	    append result "${instance}: delete [file join ${instanceRoot} upgrade] result:$lresult rc:$rc.\n"
	    
	    append result "SUCCESS: ${instance} downgraded.\n"
	}
    }
    return $result
}


###############################
# Downgrade from 52patch2 to 52
# 2 steps :
#   - global job (not "instance by instance")
#   - instance by instance job
###############################

proc downgrade_serverroot_52patch2_to_52 { xName } {
    upvar $xName x

    # common part of the job
    set result [downgrade_serverroot_global x]

    # instance by instance part of the job
    if [catch {glob $x(-serverroot)/dps-*} iList] {
	append result "No instances.\n"
    } else {
	if [expr [llength $iList] > 0] {
	    foreach i $iList {
		set x(instance) [file tail "$i"]
		append result "[downgrade_serverroot_instance x x(instance)]"
	    }
	}
    }
    return $result
}


######################
# Downgrade or Upgrade 
# according to syncType
######################

proc syncserverroot { xName }  {
    upvar $xName x

    if [string equal upgrade $x(-syncType)] {
	set result [upgrade_serverroot_52_to_52patch2 x]
    } else {
	set result [downgrade_serverroot_52patch2_to_52 x]
    } 
    return $result
}

######
#
# main
#
######


# edit array : valued options => have to check value
set edit(-cid) "noedit"
set edit(-syncType) assertUpOrDown
set edit(-serverroot) "noedit"

set defParm(-cid) -serverroot

foreach i [array names edit] {
    # add to parms, list of allowed params
    lappend parms $i
    set x($i) ""
}

# knob array : option
set knob(-verbose) [expr 0]
foreach i [array names knob] {
    # add to parms, list of allowed params
    lappend parms $i
    set x($i) $knob($i)
}

#
set optional [list -cid]
set required [list -serverroot]
set errmsg [list]
set mode ""
set usage [expr 0]

# from argv, sets errmsg, x with name or 0
foreach i $argv {
    if [string length $mode] {
	if [string length $x($mode)] {
	    lappend errmsg [list "Parameter $mode stipulated multiple times."]
	} else {
	    # run the edit defined for that param
	    if [catch {$edit($mode) $i [list "For parameter $mode:"]} result] {
		lappend errmsg $result
		set x($mode) $i
	    } else {
		set x($mode) $result
	    }
	}
	set mode ""
    } else {
	if [expr [lsearch $parms $i] < 0] {
	    lappend errmsg [list "Unknown parm $i."]
	    incr usage
	} else {
	    if [info exist knob($i)] {
		set x($i) [expr $knob($i) == 0]
	    } else {
		set mode $i
	    }
	}
    }
}

# no default value

set missing [list]

foreach i $required {
    if [string length $x($i)] {
    } else {
	lappend missing $i
    }
}

if [llength $missing] {
    lappend errmsg [list "Required parameter(s) missing: $missing."]
    set usage [expr 1]
}

if [llength $errmsg] {
} else {
    foreach i $parms {
	if [expr [lsearch $optional $i] < 0] {
	    if [string length $x($i)] {
	    } else {
		lappend errmsg [list "Parameter $i is required and missing."]
		incr usage
	    }
	}
    }
}

if {$usage} {
    lappend errmsg [list \
	    "Usage:" \
	    "sync-dps.tcl  -syncType synctype -serverroot path \[-cid installdir\]" \
	    "\t\t\[-verbose\]" \
	    "" \
	    ]
}

if [llength $errmsg] {
    foreach i $errmsg {
	foreach j $i {
	    puts stderr $j
	}
    }
    exit 1
}

set result "Updating serverroot for DPS instances."

# set x(tclsh) [file join $x(-cid) bin tcl8.2 [cmd tclsh]]

# Get the path to subcommands that will help updating
set x(cmd_to_sync_version) [file join $x(-cid) shared bin sync-version]
append result "cmd_to_sync_version $x(cmd_to_sync_version)"

set x(cmd_to_upgrade_sec)  [file join $x(-cid) shared bin cert-convert]
append result "cmd_to_upgrade_sec $x(cmd_to_upgrade_sec)"

# Do the job
set rc [catch {syncserverroot x} result]

# And report
if {$x(-verbose)} {
    append result "SUCCESS: sync-dps.tcl script."
    puts stdout $result
    puts stdout ""
}
if {$rc != 0} {
    puts stderr $result
    puts stderr "ERROR : $x(-serverroot) update failure."
    exit 1
}

exit 0