#!/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 entries related to DPS instances in CDS
#         (see usage for more details)


#####################
# Checks that syncVersion argument value is valid
# can only be 5.2 - means udpate CDS to go back to "5.2" version
#####################

proc assertSyncVersion { syncVersion {msg [list]} } {

    if [string compare "5.2" $syncVersion] {
	error [lappend msg "allowed value is 5.2."]
    }

    return $syncVersion
}



proc assertExist { path {msg [list]} } {
    
    if [file exists $path] {
    } else {
	error [lappend msg "$path does not exist."]
    }
    
    return $path
}
proc assertFileExist { path {msg [list]} } {
    
    if [file isfile [assertExist $path $msg]] {
    } else {
	error [lappend msg "$path isn't a file."]
    }
    
    return $path
}

proc assertDirExist { path {msg [list]} } {
    
    if [file isdirectory [assertExist $path $msg]] {
    } else {
	error [lappend msg "$path isn't a directory."]
    }
    
    return $path
}
proc assertFileExistReadable { path {msg [list]} } {
    if [file readable [assertFileExist $path $msg]] {
    } else {
	error [lappend msg "Permissions don't allow read access to $path."]
    }
    
    return $path
}
proc assertDirExistReadable { path {msg [list]} } {
    if [file readable [assertDirExist $path $msg]] {
    } else {
	error [lappend msg "Permissions don't allow read access to $path."]
    }
    
    return $path
}

proc assertASFootprint { path {msg [list]} } {
    assertDirExistReadable [file join $path] $msg
    assertFileExistReadable [file join $path admin-serv config adm.conf] $msg
    return $path
}

proc assertCIDFootprint { path {msg [list]} } {
    assertDirExistReadable $path $msg
    assertDirExistReadable [file join $path bin dps install script] $msg
    return $path
}

#########################
# Parameter 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
}

###########################
# fetch SIE from adm.conf
###########################

proc fetchServer { xName } {

    upvar $xName x

    set iName [file join $x(-serverroot) admin-serv/config/adm.conf]
    set iFile [open $iName r]

    set server "Server Group"

    while {[gets $iFile line] >= 0} {
	if [regexp -nocase "^isie\:" $line] {
	    regexp -nocase "(\[^,\]+),(\[^,\]+)," $line a b c
	    regexp -nocase "(\[^=\]+)=(.+)" $c d e server
	}
    }

    close $iFile

    return $server
}

proc fetchSIE { xName } {

    upvar $xName x

    set iName [file join $x(-serverroot) admin-serv/config/adm.conf]
    set iFile [open $iName r]
    set sie ""
    while {[gets $iFile line] >= 0} {
	if [regexp -nocase "^sie\:" $line] {
	    regexp -nocase "^sie\:\[ \t\]*(.+)" $line a sie
	}
    }

    close $iFile

    if [string length $sie] {
	set sie "@$sie"
    }

    return $sie
}

#################################################
# creates ldif file with changes between releases
#################################################

proc create_mod_ldif_file { xName xInstance } {

    upvar $xName x
    upvar $xInstance instance

    global tcl_platform

    set productname $x(productname)
    set productversion $x(version)
    set jarname $x(jarfileName)
    set instancename $x(instance)
    set x(sie) [fetchSIE x]
    set x(server) [fetchServer x]
    
    set x(nsClassname) [list "com.iplanet.idar.ui.server.IDARServer@dps522.jar$x(sie)"]
    
    if [string equal $tcl_platform(platform) "unix"] {
	set oFile [open $x(ldif) w 0600]
    } else {
	set oFile [open $x(ldif) w]
    }

    puts $oFile "dn: cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsproductname"
    puts $oFile "nsproductname: ${productname}"
    puts $oFile ""

    puts $oFile "dn: cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsproductversion"
    puts $oFile "nsproductversion: ${productversion}"
    puts $oFile ""

    puts $oFile "dn: cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: description"
    puts $oFile "description: An instance of a ${productname}"
    puts $oFile ""

    puts $oFile "dn: cn=configuration, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.IDARServer@${jarname}$x(sie)"
    puts $oFile ""

    puts $oFile "dn: cn=KeyCert, cn=Operation, cn=Tasks, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.task.KeyCert@${jarname}$x(sie)"
    puts $oFile ""

    puts $oFile "dn: cn=ConfigureLoadBalancing, cn=Operation, cn=Tasks, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.task.ConfigureLoadBalancing@${jarname}$x(sie)"
    puts $oFile ""

    puts $oFile "dn: cn=Start, cn=Operation, cn=Tasks, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.task.Start@${jarname}$x(sie)"
    puts $oFile ""

    puts $oFile "dn: cn=Stop, cn=Operation, cn=Tasks, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.task.Stop@${jarname}$x(sie)"
    puts $oFile ""

    puts $oFile "dn: cn=Restart, cn=Operation, cn=Tasks, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.task.Restart@${jarname}$x(sie)"
    puts $oFile ""

    puts $oFile "dn: cn=ReloadConfiguration, cn=Operation, cn=Tasks, cn=${instancename}, cn=Sun ONE Directory Proxy Server, cn=$x(server), cn=$x(fullHostname), ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.server.task.ReloadConfiguration@${jarname}$x(sie)"
    puts $oFile ""
    
    puts $oFile "dn: cn=dpsconfigplugin, cn=topologyplugin, ou=4.0, ou=Admin, ou=Global Preferences, ou=$x(domain), o=NetscapeRoot"
    puts $oFile "changetype: modify"
    puts $oFile "replace: nsclassname"
    puts $oFile "nsclassname: com.iplanet.idar.ui.configurator.ConfiguratorTopologyPlugin@${jarname}$x(sie)"

    close $oFile

}

####################################################
# ldapmodify in CDS from ldif file
####################################################
proc modify_cds_from_ldif_file  { xName xInstance } {

    upvar $xName x
    upvar $xInstance instance

    set result "modify_cds_with_ldif $x(ldif) \n"

    # ldapmodify is linked so that if it is executed
    # with shared/bin as the current working directory
    # it will be able to locate its libraries.
    #
    set sharedBin [file join $x(-serverroot) shared bin]
    cd $sharedBin
    set cmd [list [file join $sharedBin ldapmodify] \
		 -h $x(cdsHost) \
		 -p $x(cdsPort) \
		 -D $x(user) \
		 -w $x(password) \
		 -f $x(ldif) \
	       ]

    set rc [catch [linsert $cmd 0 exec] lresult]

    if [string length $lresult] {
	if { $rc } {
	    puts stderr $lresult
	} else {
	    append result $lresult
	}
    }

    return $result
}


####################################################
# Do the DPS specific part of the update of the CDS
####################################################
proc upgrade_specific_in_cds  { xName xInstance } {

    upvar $xName x
    upvar $xInstance instance

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

    set result "upgrade_specific_in_cds  \n"
    
    set x(ldif) [file join $x(-serverroot) ${instance}/etc/modcds.tmp]

    # Create ldif file of modification beetween pervious and current file
    cd [file join ${instanceRoot} etc]
    append result  [create_mod_ldif_file x x(instance) ]
    
    # Apply the diffs to the CDS
    cd [file join ${instanceRoot} etc]
    append result [modify_cds_from_ldif_file  x x(instance)]

    # clean the tmp diff file
    file delete $x(ldif)
    
    return $result
}

####################################################
# Does the instance by instance CDS update stuff
####################################################

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

    set result ""
    set lin ""
    set fullyHostName ""
    set fulldn ""
    set fi ""
    set isieDN ""


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

    # Relies on the context file to get all install vars
    set context  [file join ${instanceRoot} uninstallContext.tcl]
    if [catch {source $context} lresult] {
	puts stderr "While attempting to source $context, the following occured:"
	puts stderr $lresult
    } else {
	if [catch {init x} lresult] {
	    puts stderr $lresult
	} else {
	    append result "\ninit from $context\n"
	}
    }

    # unscramble password
    set x(password) [unscramble $x(password)]

    # Read tailor to get infos to access CDS
    set tailorName [file join $instanceRoot  etc tailor.txt]
    
    set dok [catch {file isdirectory [file join $instanceRoot  etc]} lresult]

    if { $dok == 0 && $lresult == 1} {
		
	if [file exists $tailorName] {

	    set tailorFile [open ${tailorName} r+ ]
	    set crName [file join $instanceRoot  etc tmpcr.txt]
	    set crFile [open $crName  w  0400 ]
	    while {[gets $tailorFile line] >= 0} {
		if [regexp "configuration_url" $line] {
		    set lin [split $line "/"]
		    set fullyHostName [lindex $lin 2]
		    set fulldn [lindex $lin 3]
		    set fi [string first " cn=" $fulldn]
		    set isieDN [string range $fulldn $fi [string length $fulldn]]
		} else {
		    if [regexp "configuration_bind_dn" $line] {
			puts $crFile "Admin Id:  [lindex [split $line ":"] 1]"
		    } else {
			if [regexp "configuration_bind_pw" $line] {
			    set dwp [string trim [lindex [split $line ":"] 1] " "]
			    puts $crFile "Admin Password: [unscramble $dwp]"
			}
		    }
		}
	    }
	    close $tailorFile
	    close $crFile
	    
	    # set the jarfileName/version/.. attributes according to upgrade/downgrade
	    if [string compare $x(-syncVersion) "5.2"] {
		set x(jarfileName) "dps522.jar"
		set x(version) "5.2.2"
		set x(propertiesname) "DpsSetupResources.properties"
	    } else {
		set x(jarfileName) "dps52.jar"
		set x(version) "5.2"
		set x(propertiesname) "Sun_ONE_Directory_Proxy_Server_v5_2Resources.properties"
	    }
	    
	    # Get the Build Number related to the current binray
	    set x(dpsbuild) ""
	    set iName [file join $x(-cid) setup dps locale resources $x(propertiesname)]
	    set iFile [open $iName r]
	    while {[gets $iFile line] >= 0} {
		if [regexp "^DPS_BUILD=" $line] {
		    regexp "^DPS_BUILD=\[ \t\]*(.+)" $line a DPS_BUILD
		} else {
		    if [regexp "^DPS_REVISION=" $line] {
			regexp "^DPS_REVISION=\[ \t\]*(.+)" $line a DPS_REVISION
		    } else {
			if [regexp "^DPS_SECURITY=" $line] {
			    regexp "^DPS_SECURITY=\[ \t\]*(.+)" $line a DPS_SECURITY
			} else {
			    if [regexp "^DPS_PRODUCT_NAME=" $line] {
				regexp "^DPS_PRODUCT_NAME=\[ \t\]*(.+)" $line a DPS_PRODUCT_NAME
			    } else {
			    }
			}
		    }
		}
	    }
	    close $iFile
	    if [string length $DPS_BUILD] {
		set x(build) $DPS_BUILD
	    } else {
		error [list "Did not find DPS_BUILD in $iName"]
	    }
	    if [string length $DPS_PRODUCT_NAME] {
		set x(productname) $DPS_PRODUCT_NAME
	    } else {
		error [list "Did not find DPS_PRODUCT_NAME in $iName"]
	    }

	    # Update generic entries CDS
	    cd [file join $x(-serverroot) shared bin]
	    set rc [catch {exec $x(cmd_to_sync_cds) -r $x(-serverroot) \
                -i ${isieDN} -v $x(version) -m $x(version) -j $x(jarfileName) -n $x(productname) -b $x(build) -f ${crName}} lresult]
	    
	    append result "$x(cmd_to_sync_cds) -r $x(-serverroot) \
                -i ${isieDN} -v $x(version) -m $x(version) -j $x(jarfileName) -n $x(productname) -b $x(build) result: $lresult rc=${rc}\n"
	    
	    # cleaning tmp file
	    file delete [file join $instanceRoot  etc tmpcr.txt]

	    if { ${rc} != 0 } {
		error [ "WARNING:$result CDS should be up and available to allow update of datas.\n" ]
	    } else {
	    
		# Update DPS specific entries in CDS
		append result [upgrade_specific_in_cds x x(instance)]

		append result "SUCCESS: ${instance} updated in CDS.\n"
	    }
	}
	
	# We'd like to restart the servers from here but calling restart makes
	# this script never exit (like waiting on file descriptors or ... )
	# another possibility would be to use AdminServ if running .. this has to be investigated
	# For now, we count on the caller script (patch) or the user to do it

	# set cmdRestart [file join $instanceRoot restart-dps]
	# cd [file join $instanceRoot]
	# exec ${cmdRestart}
	# append result "\nRestarted $instance $l2result.\n"
    }
    return $result
}

############
# CDS update
############

proc syncdpscds { xName } {
    upvar $xName x

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

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

set x(cdsHost) ""
set x(cdsPort) "" 
set x(user) ""
set x(password) ""
set x(ldif) ""


# edit array : valued options => have to check value
set edit(-cid) assertCIDFootprint
set edit(-syncVersion) assertSyncVersion
set edit(-serverroot) assertASFootprint

#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 -syncVersion]
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-cds.tcl  \[-syncVersion 5.2\] -serverroot path \[-cid installdir\]" \
	    "\t\t\[-verbose\]" \
	    "" \
	    ]
}

if [llength $errmsg] {
    foreach i $errmsg {
	foreach j $i {
	    puts stderr $j
	}
    }
    puts stderr "Update of Configuration Directory Server failed."
    exit 1
}

set result "Updating CDS for dps instances."

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

# Get cmd line to sync cds path
set x(cmd_to_sync_cds)  [file join $x(-cid) shared bin sync-product-cds]
append result "cmd_to_sync_cds: $x(cmd_to_sync_cds)"

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

if {$x(-verbose)} {
    append result "Update of CDS completed."
    puts stdout $result
    puts stdout ""
}

# And report
if  {$rc != 0} {
    puts stderr "Update of Configuration Directory Server ($x(cdsHost):$x(cdsPort)) failed."
    exit 1
} else {
    puts stdout "Update of Configuration Directory Server ($x(cdsHost):$x(cdsPort)) succeeded."
    exit 0
}

