#!/usr/bin/rhswish -f

if {[catch set env(CONTROL_PANEL_LIB_DIR)] != 0} {
    set env(CONTROL_PANEL_LIB_DIR) /usr/lib/rhs/control-panel
}

if {[catch {source $env(CONTROL_PANEL_LIB_DIR)/dialog.tcl}] != 0} {
    puts "Couldn't load dialog.tcl"
    puts "Start from control-panel or set environment variable"
    puts "CONTROL_PANEL_LIB_DIR to the control-panel library dir."
    puts "(normally this is /usr/lib/rhs/control-panel)"
    exit 0
}

set fqdn ""
set hostname ""
set domainname ""

set nameservers {}

set ifconfigbin "/sbin/ifconfig"

set datadir "/usr/lib/rhs/netcfg"
set scriptdir "/etc/sysconfig/network-scripts"
set resolvconfdir "/etc/resolv.conf"
set hostnamedir "/etc/HOSTNAME"
set hostsdir "/etc/hosts"
set syscfgdir "/etc/sysconfig"

set iftemplates {}
set ifaces {}

set iflistmap(0) 0

set hosts {}

set hostslistmap(0) 0

set field(0) ""
set nseditfield ""

set nslistmap(0) 0

set execperms 700

###############
# SingleSelect Drag-move listbox

proc syncmap {listn mapn} {
    upvar $listn list
    upvar $mapn map

    set newlist {}

    set len [llength $list]
    for {set i 0} {$i < $len} {incr i} {
	lappend newlist [lindex $list $map($i)]
	set map($i) $i
    }

    set list $newlist
}

proc listboxSSD_motion {box y mapname} {
    upvar $mapname map

    set sel [$box curselection]
    if {[llength $sel] != 1} {
	return
    }
    set citem [lindex $sel 0]

    set item [$box nearest $y]

    if {$item == $citem} {
	return
    }

    set contents [$box get $citem]
    $box delete $citem
    $box insert $item $contents
    $box select set $item $item

    set tmp $map($item)
    set map($item) $map($citem)
    set map($citem) $tmp
}

proc listboxSSD {w map} {
    bind $w <1> {%W select clear; %W select from [%W nearest %y]}
    bind $w <B1-Motion> "listboxSSD_motion %W %y $map"
    bind $w <Shift-1> {%W select clear; %W select from [%W nearest %y]}
    bind $w <Shift-B1-Motion> {%W select clear; %W select from [%W nearest %y]}
    bind $w <Control-1> {%W select clear; %W select from [%W nearest %y]}
    bind $w <Control-B1-Motion> {%W select clear; %W select from [%W nearest %y]}
    bind $w <Control-Shift-1> {;}
    bind $w <3> {%W select clear [%W nearest %y]}
    bind $w <B3-Motion> {;}
    bind $w <Control-3> {;}
    bind $w <Control-Shift-3> {;}
}

###############
# Data writes

#####
# /etc/HOSTNAME

proc writehostname {} {
    global hostnamedir fqdn

    if {[catch {set f [open $hostnamedir w]}] != 0} {
	rhs_error_dialog "Couldn't write to $hostnamedir."
	return
    }

    puts $f $fqdn

    close $f
}

#####
# /etc/sysconfig/network

proc writesyscfghn {} {
    global syscfgdir hostname

    if {[catch {set f [open "$syscfgdir/network" w]}] != 0} {
	rhs_error_dialog "Couldn't write to $syscfgdir/network."
	return
    }

    puts $f "NETWORKING=yes"
    puts $f "HOSTNAME=$hostname"

    exec /bin/hostname $hostname

    close $f
}

#####
# /etc/resolv.conf

proc writeresolv {} {
    global resolvconfdir nameservers nslistmap domainname

    syncmap nameservers nslistmap
    updatenslist

    set buf "domain $domainname\nsearch"
    set domain_bit $domainname
    foreach n [split $domainname "."] {
	set buf "$buf $domain_bit"
	set tmp [expr [string first "." $domain_bit]+1]
	set domain_bit [string range $domain_bit $tmp end]
	if {! [regexp {\.} $domain_bit]} {
	    break
	}
    }

    foreach n $nameservers {
	set buf "${buf}\nnameserver $n"
    }

    if {[catch {set f [open $resolvconfdir r]}] != 0} {
	rhs_error_dialog "Couldn't read from $resolvconfdir."
	return
    }

    while {[gets $f str] >= 0} {
	if {[scan $str "nameserver %s" foo] == 1} {
	    ;
	} elseif {[scan $str "domain %s" foo] == 1} {
	    ;
	} elseif {[scan $str "search %s" foo] == 1} {
	    ;
	} else {
	    set buf "$buf\n$str"
	}
    }

    close $f

    if {[catch {set f [open $resolvconfdir w]}] != 0} {
	rhs_error_dialog "Couldn't write to $resolvconfdir."
	return
    }

    puts $f $buf

    close $f
}

#####
# /etc/hosts

proc writehosts {} {
    global hosts hostslistmap hostsdir

    syncmap hosts hostslistmap
    updatehostslist

    if {[catch {set f [open $hostsdir w]}] != 0} {
	rhs_error_dialog "Couldn't write to $hostsdir."
	return
    }

    foreach i $hosts {
	set str [format "%-15s %-20s %s" [lindex $i 0] [lindex $i 1] \
		     [lindex $i 2]]
	puts $f $str
    }

    close $f
}

#####
# Interfaces

proc writeifaces {} {
    global scriptdir ifaces iflistmap iftemplates execperms

    syncmap ifaces iflistmap
    updateifacelist

    if {[catch {set files [glob "$scriptdir/ifcfg-*"]}] == 0} {
	foreach i $files {
	    scan $i "$scriptdir/ifcfg-%s" ifname
	    set exists 0
	    foreach if $ifaces {
		if {[string compare $ifname [lindex $if 0]] == 0} {
		    set exists 1
		}
	    }
	    if {$exists == 0} {
		exec rm -f $scriptdir/ifcfg-$ifname
		exec rm -f $scriptdir/ifup-$ifname
		exec rm -f $scriptdir/ifdown-$ifname
	    }
	}
    }

    foreach iface $ifaces {
	set ifname [lindex $iface 0]
	set ifdevice [lindex $iface 1]
	set ifvars [lindex $iface 2]

	set template {}
	foreach i $iftemplates {
	    if {[string compare [lindex $i 0] [lindex $iface 1]] == 0} {
		set template $i
	    }
	}
	if {$template == {}} {
	    return
	}

	if {[catch {set f [open "$scriptdir/ifcfg-$ifname" w]}] != 0} {
	    rhs_error_dialog "Couldn't write to $i."
	    return
	}

	set o "#!/bin/sh\n"
	set o "$o\n"
	set o "$o#>>>Device type: $ifdevice\n"
	set o "$o\n"
	set o "$o#>>>Variable declarations:\n"
	set i 0
	foreach e $ifvars {
	    set o "$o[lindex $e 0]=[lindex $e 1]\n"
	    incr i
	}
	set o "$o#>>>End variable declarations"

	if {[catch {puts $f $o}] != 0} {
	    rhs_error_dialog "Couldn't write to $i."
	    return
	}

	close $f

	set fname "$scriptdir/ifup-$ifname"
	if {[file exists $fname] == 0} {
	    if {[catch {set f [open $fname w]}] != 0} {
		rhs_error_dialog "Couldn't write to $fname."
		return
	    }
	    regsub -all {@@@interface@@@} [lindex $template 1] \
		[lindex $iface 0] str
	    puts $f $str
	    close $f
	    exec chmod $execperms $fname
	}

	set fname "$scriptdir/ifdown-$ifname"
	if {[file exists $fname] == 0} {
	    if {[catch {set f [open $fname w]}] != 0} {
		rhs_error_dialog "Couldn't write to $fname."
		return
	    }
	    regsub -all {@@@interface@@@} [lindex $template 2] \
		[lindex $iface 0] str
	    puts $f $str
	    close $f
	    exec chmod $execperms $fname
	}
    }
}

proc writeallfiles {} {
    writehostname
    writesyscfghn
    writeresolv
    writehosts
    writeifaces
}

###############
# Generic data reads

#####
# /etc/HOSTNAME

proc readhostname {} {
    global hostnamedir fqdn

    if {[catch {set f [open $hostnamedir r]}] != 0} {
	rhs_error_dialog "Couldn't read from $hostnamedir."
	return
    }

    set fqdn [gets $f]

    close $f
}

#####
# /etc/sysconfig/network

proc readsyscfghn {} {
    global syscfgdir hostname

    if {[catch {set f [open "$syscfgdir/network" r]}] != 0} {
	rhs_error_dialog "Couldn't read from $syscfgdir/network."
	return
    }

    while {[gets $f str] >= 0} {
	if {[scan $str "HOSTNAME=%s" foo] == 1} {
	    set hostname $foo
	}
    }

    close $f
}

#####
# /etc/resolv.conf

proc readresolv {} {
    global resolvconfdir nameservers domainname

    set nameservers {}

    if {[catch {set f [open $resolvconfdir r]}] != 0} {
	rhs_error_dialog "Couldn't read from $resolvconfdir."
	return
    }

    while {[gets $f str] >= 0} {
	if {[scan $str "nameserver %s" foo] == 1} {
	    lappend nameservers $foo
	} elseif {[scan $str "domain %s" foo] == 1} {
	    set domainname $foo
	}
    }

    close $f
}

#####
# /etc/hosts

proc readhosts {} {
    global hosts hostsdir

    if {[catch {set f [open $hostsdir r]}] != 0} {
	rhs_error_dialog "Couldn't read from $hostsdir."
	return
    }

    set hosts {}
    while {[gets $f str] >= 0} {
	if {[regexp {^\#} $str] == 1} {
	    ;
	} else {
	    # the [   ] contain a space and a tab
	    regsub {^([0-9\.]*).*} $str {\1} ipaddr
	    #regsub {^[0-9\.]*[ 	]*([A-Za-z0-9\.]*).*} $str {\1} hostname
	    #regsub {^[0-9\.]*[ 	]*[A-Za-z0-9\.]*[ 	]*(.*)$} $str {\1} aliases
	    regsub {^[0-9\.]*[ 	]*([-_A-Za-z0-9\.]*).*} $str {\1} hostname
	    regsub {^[0-9\.]*[ 	]*[-_A-Za-z0-9\.]*[ 	]*(.*)$} $str {\1} aliases
	    regsub -all {[ 	]+} $aliases " " aliases
	    set l "$ipaddr $hostname"
	    lappend l "$aliases"
	    lappend hosts $l
	}
    }

    close $f
}

#####
# Read current interface configuration

proc readifaces {} {
    global scriptdir ifaces

    set ifaces {}

    if {[catch {set files [glob "$scriptdir/ifcfg-*"]}] != 0} {
	return
    }
    foreach i $files {
	scan $i "$scriptdir/ifcfg-%s" ifname
	set ifdevice "unknown"
	set ifvars {}

	if {[catch {set f [open $i r]}] != 0} {
	    rhs_error_dialog "Couldn't read from $i."
	    return
	}

	set ifvars {}

	while {[gets $f str] >= 0} {
	    set str [string trim $str "\n"]
	    if {[scan $str "#>>>Device type: %s" foo] == 1} {
		set ifdevice $foo
	    } elseif {$str == "#>>>Variable declarations:"} {
		set iter 1
		set vname ""
		set vdef ""
		while {$iter == 1} {
		    if {[gets $f str] < 0} {
			set iter 0
		    } elseif {$str == "#>>>End variable declarations"} {
			set iter 0
		    } elseif {[regsub {^(.*)=(.*)$} $str {\1} vname] == 1} {
			if {[regsub {^(.*)=(.*)$} $str {\2} vdef] == 1} {
			    lappend ifvars "$vname $vdef"
			}
		    }
		}
	    }
	}

	close $f

	lappend ifaces "$ifname $ifdevice {$ifvars}"
    }
}

###############
# Hostname

##########
# End hostname configuration

proc edithostname_end {} {
    global fqdn hostname domainname field

    destroy .edithostn

    set fqdn $field(0)
    set hostname $field(1)
    set domainname $field(2)
}

##########
# Hostname configuration toplevel

#####
# Update entry fields

proc edithostname_updfields {} {
    global field

    if {$field(1) == ""} {
	regsub {^([a-zA-Z0-9]*)\..*$} $field(0) {\1} field(1)
    }

    if {$field(2) == ""} {
	regsub {^[a-zA-Z0-9]*\.(.*)$} $field(0) {\1} field(2)
    }

    focus .edithostn.hostname.entry
}

#####
# Construct the toplevel

proc edithostname {} {
    global fqdn hostname domainname field

    set field(0) $fqdn
    set field(1) $hostname
    set field(2) $domainname

    toplevel .edithostn
    wm withdraw .edithostn

    frame .edithostn.fqdn
    pack .edithostn.fqdn -side top -fill x -expand 1

    label .edithostn.fqdn.label -text "FQDN"
    entry .edithostn.fqdn.entry -width 20 -relief sunken -bd 2 \
	-font fixed -textvariable field(0)
    pack .edithostn.fqdn.label -side left -padx 1m -pady 1m
    pack .edithostn.fqdn.entry -side right -padx 1m -pady 1m

    frame .edithostn.hostname
    pack .edithostn.hostname -side top -fill x -expand 1

    label .edithostn.hostname.label -text "Hostname"
    entry .edithostn.hostname.entry -width 20 -relief sunken -bd 2 \
	-font fixed -textvariable field(1)
    pack .edithostn.hostname.label -side left -padx 1m -pady 1m
    pack .edithostn.hostname.entry -side right -padx 1m -pady 1m

    frame .edithostn.domain
    pack .edithostn.domain -side top -fill x -expand 1

    label .edithostn.domain.label -text "Domainname"
    entry .edithostn.domain.entry -width 20 -relief sunken -bd 2 \
	-font fixed -textvariable field(2)
    pack .edithostn.domain.label -side left -padx 1m -pady 1m
    pack .edithostn.domain.entry -side right -padx 1m -pady 1m

    bind .edithostn.fqdn.entry <Return> "edithostname_updfields"
    bind .edithostn.hostname.entry <Return> "focus .edithostn.domain.entry"
    bind .edithostn.domain.entry <Return> "focus .edithostn.fqdn.entry"

    focus .edithostn.fqdn.entry

    frame .edithostn.buttons
    pack .edithostn.buttons -side top

    button .edithostn.ok -width 8 -text OK -command "edithostname_end"
    button .edithostn.cancel -width 8 -text Cancel \
	-command "destroy .edithostn"
    pack .edithostn.ok .edithostn.cancel -side left -padx 2m -pady 2m \
	-in .edithostn.buttons

    wm title .edithostn "Edit hostname"
    center_dialog .edithostn
    grab .edithostn
}

###############
# Interfaces

##########
# Display/data management

#####
# Read current template configuration

proc readiftemplates {} {
    global datadir iftemplates

    set iftemplates {}

    if {[catch {set gpat [glob "$datadir/netif-*"]}] != 0} {
	return
    }

    foreach i $gpat {
	scan $i "$datadir/netif-%s" ifname

	if {[catch {set f [open $i r]}] != 0} {
	    rhs_error_dialog "Couldn't read from $i."
	    return
	}

	set mode "none"
	set vars {}
	set activatescr ""
	set deactivatescr ""
	set vtype "entry"
	while {[gets $f str] >= 0} {
	    if {$str == ">>>variables>>>"} {
		set mode "variables"
	    } elseif {$str == ">>>activate>>>"} {
		set mode "activate"
	    } elseif {$str == ">>>deactivate>>>"} {
		set mode "deactivate"
	    } elseif {$str == ">>>end>>>"} {
		set mode "none"
	    } elseif {$mode == "variables"} {
		set str [string trim $str "\n"]
		set thisvar [split $str ^]
		if {[lindex $thisvar 3]  != -1} {
		    set iter 1
		    set func ""
		    while {$iter == 1} {
			if {[gets $f str] < 0} {
			    set iter 0
			} elseif {$str == "<<done<<"} {
			    set iter 0
			} else {
			    set func "$func$str"
			}
		    }
		    lappend thisvar $func
		}
		lappend vars "$thisvar"
	    } elseif {$mode == "activate"} {
		set activatescr "$activatescr$str\n"
	    } elseif {$mode == "deactivate"} {
		set deactivatescr "$deactivatescr$str\n"
	    }
	}

	set x $ifname 
	lappend x $activatescr
	lappend x $deactivatescr
	lappend x $vars
	lappend iftemplates $x
    }
}

#####
# Update interface listbox

proc updateifacelist {} {
    global ifaces ifconfigbin iflistmap

    .iface.list.list delete 0 end

    set ifout "\n[exec $ifconfigbin]"

    set n 0
    foreach i $ifaces {
	set up "down"
	if {[regexp "\n[lindex $i 0]" $ifout] == 1} {
	    set up "up"
	}
	set ipaddr ""
	foreach v [lindex $i 2] {
	    if {[lindex $v 0] == "IPADDR"} {
		set ipaddr [lindex $v 1]
	    }
	}
	set str [format "%-5s %-4s %s" [lindex $i 0] $up $ipaddr]
	.iface.list.list insert end $str
	set iflistmap($n) $n
	incr n
    }
}

##########
# Interface configuration toplevel

#####
# Finish interface configuration

proc configiface_end {ifid} {
    global scriptdir ifaces iftemplates field execperms

    destroy .configiface

    set iface [lindex $ifaces $ifid]

    set template {}
    foreach i $iftemplates {
	if {[string compare [lindex $i 0] [lindex $iface 1]] == 0} {
	    set template $i
	}
    }
    if {$template == {}} {
	return
    }

    set vars {}
    set i 0
    foreach e [lindex $template 3] {
	lappend vars "[lindex $e 1] $field($i)"
	incr i
    }
    set iface [lreplace $iface 2 2 $vars]

    set ifaces [lreplace $ifaces $ifid $ifid $iface]

    updateifacelist
}

#####
# Update inteface fields

proc configiface_fieldupd {template cur next} {
    global field

    set fields [lindex $template 3]
    set i 0
    foreach e $fields {
	if {[lindex $e 3] == $cur} {
	    set current $field($i)
	    set a ""
	    set b ""
	    set c ""
	    set d ""
	    scan $field($cur) "%d.%d.%d.%d" a b c d
	    if {$a < 128} {
		set class "a"
	    } elseif {$a < 192} {
		set class "b"
	    } elseif {$a < 224} {
		set class "c"
	    } else {
		set class "e"
	    }
	    eval [lindex $e 4]
	    if {[string compare $result "error"] != 0} {
		set field($i) $result
	    }
	}
	incr i
    }

    focus .configiface.field$next.entry
}

#####
# Construct interface configuration toplevel

proc configiface_main {ifaceid {newiface {}}} {
    global field ifaces iftemplates

    set iface [lindex $ifaces $ifaceid]

    set template {}
    foreach i $iftemplates {
	if {[string compare [lindex $i 0] [lindex $iface 1]] == 0} {
           set template $i
	}
    }
    if {$template == {}} {
	return
    }
    set fields [lindex $template 3]

    toplevel .configiface
    wm withdraw .configiface

    set entrylist {}
    set i 0
    foreach e $fields {
	set field($i) ""
	foreach v [lindex $iface 2] {
	    if {[string compare [lindex $v 0] [lindex $e 1]] == 0} {
		set field($i) [lindex $v 1]
	    }
	}

	if {[lindex $e 2] == "entry"} {
	    lappend entrylist $i

	    frame .configiface.field$i
	    pack .configiface.field$i -side top -fill x -expand 1

	    label .configiface.field$i.label -text [lindex $e 0]
	    entry .configiface.field$i.entry -width 20 -relief sunken -bd 2 \
		-font fixed -textvariable field($i)
	    pack .configiface.field$i.label -side left -padx 1m -pady 1m
	    pack .configiface.field$i.entry -side right -padx 1m -pady 1m

	} elseif {[lindex $e 2] == "checkbutton"} {
	    if {$field($i) == ""} {
		set field($i) "no"
	    }

	    checkbutton .configiface.field$i -offvalue "no" -onvalue "yes" \
		-variable field($i) -text [lindex $e 0]
	    pack .configiface.field$i -fill x -padx 2m
	}

	incr i
    }

    set n 1
    if {[llength $entrylist] > 0} {
	foreach i $entrylist {
	    if {$n == [llength $entrylist]} {
		bind .configiface.field$i.entry <Return> \
		    "configiface_fieldupd {$template} $i [lindex $entrylist 0]"
	    } else {
		bind .configiface.field$i.entry <Return> \
		  "configiface_fieldupd {$template} $i [lindex $entrylist $n]"
	    }
	    incr n
	}

	focus .configiface.field[lindex $entrylist 0].entry
    }

    frame .configiface.buttons
    pack .configiface.buttons -side top

    button .configiface.ok -width 8 -text OK \
	-command "configiface_end $ifaceid"
    if {$newiface == "newiface"} {
	button .configiface.cancel -width 8 -text Cancel \
	    -command "destroy .configiface; deliface $ifaceid"
    } else {
	button .configiface.cancel -width 8 -text Cancel \
	    -command "destroy .configiface"
    }
    pack .configiface.ok .configiface.cancel -side left -padx 2m -pady 2m \
	-in .configiface.buttons

    wm title .configiface "Configure Interface"
    center_dialog .configiface
    grab .configiface
}

#####
# Configure an interface

proc configiface {} {
    global ifaces iflistmap

    set l [.iface.list.list curselection]

    if {[llength $l] >= 1} {
	syncmap ifaces iflistmap
	updateifacelist

	configiface_main $iflistmap([lindex $l 0])
    }
}

#########
# Add interface toplevel

#####
# Finish add interface toplevel

proc addiface_end {} {
    global ifaces iftemplates field

    set sel [.addiface.list.list curselection]
    if {[llength $sel] == 0} {
	rhs_error_dialog "You must specify a device type and name."
	return
    }

    if {[string length $field(0)] == 0} {
	rhs_error_dialog "You must specify a device type and name."
	return
    }

    set x 0
    foreach i $ifaces {
	if {[string compare [lindex $i 0] $field(0)] == 0} {
	    set x 1
	}
    }
    if {$x == 1} {
	rhs_error_dialog "There is already an interface named $field(0)."
	return
    }

    set iface "$field(0) [.addiface.list.list get [lindex $sel 0]] {}"
    lappend ifaces $iface

    destroy .addiface

    configiface_main [expr [llength $ifaces]-1] newiface
}

#####
# Add an interface (construct toplevel)

proc addiface {} {
    global iftemplates ifaces iflistmap field

    syncmap ifaces iflistmap
    updateifacelist

    toplevel .addiface
    wm withdraw .addiface

    label .addiface.label1 -text "Device type"
    pack .addiface.label1 -side top -padx 2m -pady 2m

    frame .addiface.list
    pack .addiface.list -side top -fill both -expand 1 -padx 2m

    listbox .addiface.list.list -relief raised -borderwidth 2 \
	-yscrollcommand ".addiface.list.scroll set" -geometry 15x4 \
	-exportselection 0 -font fixed
    tk_listboxSingleSelect .addiface.list.list
    pack .addiface.list.list -side left -fill both -expand 1
    scrollbar .addiface.list.scroll -command ".addiface.list.list yview"
    pack .addiface.list.scroll -side right -fill y

    foreach i $iftemplates {
	.addiface.list.list insert end [lindex $i 0]
    }

    label .addiface.label2 -text "Device name"
    pack .addiface.label2 -side top -padx 2m -pady 2m

    set field(0) ""
    entry .addiface.entry -width 15 -relief sunken -bd 2 -font fixed \
	-textvariable field(0)
    bind .addiface.entry <Return> addiface_end
    pack .addiface.entry -side top -padx 2m -fill x -expand 1

    frame .addiface.buttons
    pack .addiface.buttons -side top -padx 2m -pady 2m

    button .addiface.buttons.ok -text OK -command "addiface_end" -width 6
    button .addiface.buttons.cancel -text Cancel \
	-command "destroy .addiface" -width 6
    pack .addiface.buttons.ok .addiface.buttons.cancel -side left -padx 2m

    focus .addiface.entry

    wm title .addiface "Add interface"
    center_dialog .addiface
    grab .addiface
}

##########
# Remove an interface

proc rmiface {} {
    global ifaces iflistmap

    set sel [.iface.list.list curselection]

    if {[llength $sel] >= 1} {
	set idx $iflistmap([lindex $sel 0])

	set r [rhs_dialog .rmiface "Question" \
		   "Are you certain you want to\nremove\
                   [lindex [lindex $ifaces $idx] 0]?" \
		   question 0 "Yes" "No"]
	if {$r == 1} {
	    return
	}

	syncmap ifaces iflistmap
	set idx $iflistmap([lindex $sel 0])
	set ifaces [lreplace $ifaces $idx $idx]
	updateifacelist
    }
}

proc deliface {id} {
    global ifaces

    set ifaces [lreplace $ifaces $id $id]
    updateifacelist
}

##########
# Activate an interface

proc activateiface {} {
    global scriptdir ifaces iflistmap

    syncmap ifaces iflistmap

    set sel [.iface.list.list curselection]

    if {[llength $sel] == 1} {
	set iface [lindex $ifaces $iflistmap([lindex $sel 0])];
	set ifname [lindex $iface 0]

	if {[file exists "$scriptdir/ifup-$ifname"] == 1} {
	    if {[catch {exec "$scriptdir/ifup-$ifname"}] != 0} {
		# Put error reporting here.
	    }
	}
    }

    updateifacelist
}

##########
# Deactivate an interface

proc deactivateiface {} {
    global scriptdir ifaces iflistmap

    syncmap ifaces iflistmap

    set sel [.iface.list.list curselection]

    if {[llength $sel] == 1} {
	set iface [lindex $ifaces $iflistmap([lindex $sel 0])];
	set ifname [lindex $iface 0]

	if {[file exists "$scriptdir/ifdown-$ifname"] == 1} {
	    if {[catch {exec "$scriptdir/ifdown-$ifname"}] != 0} {
		rhs_error_dialog "Error deactivating interface."
	    }
	}
    }

    updateifacelist
}

##########
# Create primary interface frame

proc createifaceframe {} {
    global iflistmap

    frame .iface -relief groove -bd 2
    pack .iface -side left -fill both -expand 1 -padx 2m -pady 2m -in .up

    label .iface.title -text "Interfaces"
    pack .iface.title -side top

    frame .iface.pf
    pack .iface.pf -side top -fill both -expand 1 -padx 2m -pady 2m

    frame .iface.list
    pack .iface.list -side top -fill both -expand 1 -in .iface.pf

    listbox .iface.list.list -relief raised -borderwidth 2 \
	-yscrollcommand ".iface.list.scroll set" -geometry 20x4 \
	-exportselection 0 -font fixed
    listboxSSD .iface.list.list iflistmap
    pack .iface.list.list -side left -fill both -expand 1
    scrollbar .iface.list.scroll -command ".iface.list.list yview"
    pack .iface.list.scroll -side right -fill y

    frame .iface.buttonsb
    pack .iface.buttonsb -side top -fill x -in .iface.pf

    button .iface.buttonsb.add -width 8 -text Add -command addiface
    button .iface.buttonsb.remove -width 8 -text Remove -command rmiface
    pack .iface.buttonsb.add .iface.buttonsb.remove \
	-side left -fill x -expand 1

    frame .iface.buttons
    pack .iface.buttons -side top -fill x -in .iface.pf

    button .iface.buttons.configure -text Configure -command configiface
    button .iface.buttons.activate -text Activate -command activateiface
    button .iface.buttons.deactivate -text Deactivate -command deactivateiface
    pack .iface.buttons.configure .iface.buttons.activate \
	.iface.buttons.deactivate -side left -fill x -expand 1
}

###############
# /etc/hosts

##########
# Data/display management

#####
# Rebuild display

proc updatehostslist {} {
    global hosts hostslistmap

    .hosts.list.list delete 0 end

    set n 0
    foreach i $hosts {
	set str [format "%-15s %-20s %s" [lindex $i 0] [lindex $i 1] \
		     [lindex $i 2]]
	.hosts.list.list insert end $str
	set hostslistmap($n) $n
	incr n
    }
}

##########
# Shared host operations functions

#####
# Generic host editing frame

proc edithost_fields {} {
    global field

    frame .edithost.ip
    pack .edithost.ip -side top -fill x -expand 1

    label .edithost.ip.label -text "IP Address"
    entry .edithost.ip.entry -width 20 -relief sunken -bd 2 \
	-font fixed -textvariable field(0)
    pack .edithost.ip.label -side left -padx 1m -pady 1m
    pack .edithost.ip.entry -side right -padx 1m -pady 1m

    frame .edithost.hn
    pack .edithost.hn -side top -fill x -expand 1

    label .edithost.hn.label -text "Hostname"
    entry .edithost.hn.entry -width 20 -relief sunken -bd 2 \
	-font fixed -textvariable field(1)
    pack .edithost.hn.label -side left -padx 1m -pady 1m
    pack .edithost.hn.entry -side right -padx 1m -pady 1m

    frame .edithost.al
    pack .edithost.al -side top -fill x -expand 1

    label .edithost.al.label -text "Aliases"
    entry .edithost.al.entry -width 20 -relief sunken -bd 2 \
	-font fixed -textvariable field(2)
    pack .edithost.al.label -side left -padx 1m -pady 1m
    pack .edithost.al.entry -side right -padx 1m -pady 1m

    focus .edithost.ip.entry

    bind .edithost.ip.entry <Return> "focus .edithost.hn.entry"
    bind .edithost.hn.entry <Return> "focus .edithost.al.entry"
    bind .edithost.al.entry <Return> "focus .edithost.ip.entry"
}

##########
# Edit a host

proc edithost_end {hostid} {
    global field hosts hostslistmap

    destroy .edithost

    syncmap hosts hostslistmap

    set i $hostslistmap($hostid)
    set host [lindex $hosts $i]
    set host [lreplace $host 0 0 $field(0)]
    set host [lreplace $host 1 1 $field(1)]
    # The [  ] has a space and a tab in it
    regsub -all {[ 	]+} $field(2) " " aliases
    if {[llength $host] < 3} {
	set host "$host $aliases"
    } else {
	set host [lreplace $host 2 2 $aliases]
    }
    set hosts [lreplace $hosts $i $i $host]

    updatehostslist
}

proc edithost_main {hostid} {
    global field hosts hostslistmap

    set host [lindex $hosts $hostslistmap([lindex $hostid 0])]
    set field(0) [lindex $host 0]
    set field(1) [lindex $host 1]
    set field(2) [lindex $host 2]

    toplevel .edithost
    wm withdraw .edithost

    edithost_fields

    frame .edithost.buttons
    pack .edithost.buttons -side top

    button .edithost.ok -width 8 -text OK -command "edithost_end $hostid"
    button .edithost.cancel -width 8 -text Cancel -command "destroy .edithost"
    pack .edithost.ok .edithost.cancel -side left -padx 2m -pady 2m \
	-in .edithost.buttons

    wm title .edithost "Edit Host"
    center_dialog .edithost
    grab .edithost
}

proc edithost {} {
    global field

    set sel [.hosts.list.list curselection]

    if {[llength $sel] >= 1} {
	edithost_main [lindex $sel 0]
    }
}

##########
# Add a host

proc addhost_end {} {
    global hosts hostslistmap field

    destroy .edithost

    syncmap hosts hostslistmap

    set host {}
    lappend host $field(0)
    lappend host $field(1)
    lappend host $field(2)
    lappend hosts $host

    updatehostslist
}

proc addhost {} {
    global field hosts hostslistmap

    set field(0) ""
    set field(1) ""
    set field(2) ""

    toplevel .edithost
    wm withdraw .edithost

    edithost_fields

    frame .edithost.buttons
    pack .edithost.buttons -side top

    button .edithost.ok -width 8 -text OK -command "addhost_end"
    button .edithost.cancel -width 8 -text Cancel -command "destroy .edithost"
    pack .edithost.ok .edithost.cancel -side left -padx 2m -pady 2m \
	-in .edithost.buttons

    wm title .edithost "Add Host"
    center_dialog .edithost
    grab .edithost
}

##########
# Add a host

proc rmhost {} {
    global hosts hostslistmap

    set sel [.hosts.list.list curselection]

    syncmap hosts hostslistmap

    if {[llength $sel] >= 1} {
	set i $hostslistmap([lindex $sel 0])
	set hosts [lreplace $hosts $i $i]
    }

    updatehostslist
}

##########
# Create primary hosts frame

proc createhostsframe {} {
    frame .hosts -relief groove -bd 2
    pack .hosts -side top -fill both -expand 1 -padx 2m -pady 2m

    label .hosts.title -text "Hosts"
    pack .hosts.title -side top

    frame .hosts.pf
    pack .hosts.pf -side top -fill both -expand 1 -padx 2m -pady 2m

    frame .hosts.list
    pack .hosts.list -side top -fill both -expand 1 -in .hosts.pf

    listbox .hosts.list.list -relief raised -borderwidth 2 \
	-yscrollcommand ".hosts.list.scroll set" -geometry 50x5 \
	-exportselection 0 -font fixed
    listboxSSD .hosts.list.list hostslistmap
    pack .hosts.list.list -side left -fill both -expand 1
    scrollbar .hosts.list.scroll -command ".hosts.list.list yview"
    pack .hosts.list.scroll -side right -fill y

    frame .hosts.buttons
    pack .hosts.buttons -side top -fill x -in .hosts.pf

    button .hosts.buttons.add -width 8 -text Add -command addhost
    button .hosts.buttons.remove -width 8 -text Remove -command rmhost
    button .hosts.buttons.edit -width 8 -text Edit -command edithost
    pack .hosts.buttons.add .hosts.buttons.remove .hosts.buttons.edit \
	-side left -fill x -expand 1
}

###############
# Nameservers

##########
# Nameserver list management

#####
# Redisplay nameserver list

proc updatenslist {} {
    global nameservers nseditfield nslistmap

    .editns.list.list delete 0 end
    set i 0
    foreach n $nameservers {
	.editns.list.list insert end $n
	set nslistmap($i) $i
	incr i
    }

    set nseditfield ""
    focus .mbar
}

#####
# Handle nameserver list selection change

proc editnameservers_selch {y} {
    global nseditfield

    .editns.list.list select clear
    .editns.list.list select from [.editns.list.list nearest $y]
    set nseditfield [.editns.list.list get [.editns.list.list nearest $y]]
    focus .editns.nsent
}

proc editnameservers_seloff {y} {
    global nseditfield nslistmap

    set sel [.editns.list.list curselection]

    if {[llength $sel] != 1} {
	return
    }

    if {[.editns.list.list nearest $y] != $nslistmap([lindex $sel 0])} {
	return
    }

    .editns.list.list select clear
    set nseditfield ""
    focus .mbar
}

#####
# Handle nameserver list edit finish

proc editnameservers_editend {} {
    global nseditfield nameservers nslistmap

    syncmap nameservers nslistmap

    set sel [.editns.list.list curselection]

    if {[llength $sel] == 1} {
	set i [lindex $sel 0]
	set nameservers [lreplace $nameservers $i $i $nseditfield]
    }

    updatenslist
}

##########
# Add nameserver

proc addns {} {
    global nameservers nslistmap nseditfield

    syncmap nameservers nslistmap

    set i [llength $nameservers]

    lappend nameservers "0.0.0.0"

    updatenslist

    .editns.list.list select clear
    .editns.list.list select from $i
    set nseditfield [.editns.list.list get $i]
    focus .editns.nsent
}

##########
# Remove nameserver

proc rmns {} {
    global nameservers nslistmap

    set sel [.editns.list.list curselection]

    syncmap nameservers nslistmap

    if {[llength $sel] >= 1} {
	set i [lindex $sel 0]
	set nameservers [lreplace $nameservers $i $i]
    }

    updatenslist
}

##########
# Create nameserver interface frame

proc creatensframe {} {
    global nseditfield nslistmap

    frame .editns -relief groove -bd 2
    pack .editns -side top -padx 2m -pady 2m -in .up -expand 1 -fill y

    label .editns.title -text "Nameservers"
    pack .editns.title -side top

    frame .editns.top -relief groove
    pack .editns.top -side top -fill both -expand 1 -pady 2m

    frame .editns.buttons
    pack .editns.buttons -side bottom -padx 2m -fill x -in .editns.top

    button .editns.buttons.add -width 7 -text "Add" -command addns
    button .editns.buttons.remove -width 7 -text "Remove" -command rmns
    pack .editns.buttons.add .editns.buttons.remove \
	-expand 1 -fill x -side left

    frame .editns.list
    pack .editns.list -side top -fill both -expand 1 -padx 2m \
	-in .editns.top

    listbox .editns.list.list -relief raised -borderwidth 2 \
	-yscrollcommand ".editns.list.scroll set" -geometry 16x3 \
	-exportselection 0 -font fixed
    listboxSSD .editns.list.list nslistmap
    bind .editns.list.list <1> {editnameservers_selch %y}
    bind .editns.list.list <Shift-1> {editnameservers_selch %y}
    bind .editns.list.list <Shift-B1-Motion> {editnameservers_selch %y}
    bind .editns.list.list <Control-1> {editnameservers_selch %y}
    bind .editns.list.list <Control-B1-Motion> {editnameservers_selch %y}
    bind .editns.list.list <Control-Shift-1> {;}
    bind .editns.list.list <3> {editnameservers_seloff %y}
    bind .editns.list.list <B3-Motion> {;}
    bind .editns.list.list <Control-3> {;}
    bind .editns.list.list <Control-Shift-3> {;}

    pack .editns.list.list -side left -fill both -expand 1
    scrollbar .editns.list.scroll -command ".editns.list.list yview"
    pack .editns.list.scroll -side right -fill y

    entry .editns.nsent -relief sunken -bd 2 -width 15 \
	-textvariable nseditfield -font fixed
    pack .editns.nsent -side top -fill x -expand 0 -in .editns.top -padx 2m
    bind .editns.nsent <Return> {editnameservers_editend}
}

###############
# Upper management -- the root of all evil

##########
# Create menubar

proc createmenu {} {
    frame .mbar -relief raised -bd 2
    pack .mbar -side top -fill x

    menubutton .mbar.netcfg -text NetCfg -menu .mbar.netcfg.menu
    pack .mbar.netcfg -side left

    menu .mbar.netcfg.menu
    .mbar.netcfg.menu add command -label "Edit hostname" -command edithostname
    .mbar.netcfg.menu add command -label "Quit" -command {writeallfiles ; exit}

    tk_menuBar .mbar .mbar.netcfg
}

##########
# Top level toplevel

proc createmainwindow {} {
    global fqdn

    createmenu

    label .hostname -textvariable fqdn
    pack .hostname -side top -fill x

    frame .up
    pack .up -side top

    createifaceframe
    creatensframe
    createhostsframe

    button .save -text Save -command writeallfiles
    pack .save -side top -fill x -padx 2m -pady 2m

    wm title . "RHS Linux Network Manager"

    updatehostslist
    updatenslist
    updateifacelist
}

##########
# Initialize

proc initialize {} {
    readhostname
    readsyscfghn
    readhosts
    readresolv
    readiftemplates
    readifaces
    createmainwindow
}

initialize
