#
#  Module IpAddrLib.tcl
#
#  $Log: IpAddrLib.tcl,v $
#  Revision 1.2  2006/10/26 09:39:17  vfom
#  Added code for netstate server query
#
#  Revision 1.1.1.1  2006/09/21 10:57:02  vfom
#  Loading TkNetmon to CVS
#
#
namespace eval ::IpAddr {
variable	editIpAddr_exitFlag	0
}
#
# Procedire checks if 'addr' - acceptable IP address format string.
#  returns 'true' or 'false'
#
proc IsValidIpAddr { addr } {    
    #
    # IP address must be four groups of digits, separated by '.'
    # Every group must be integer in range 0..255
    #
    if {! [ regexp {^\d+\.\d+\.\d+\.\d+$} $addr ]} {
	return false
    }
    set tmp_list [ split $addr '.' ]
    set i1 [lindex $tmp_list 0]
    set i2 [lindex $tmp_list 1]
    set i3 [lindex $tmp_list 2]
    set i4 [lindex $tmp_list 3]
        
    set i1 [expr int($i1)]
    if { $i1 <0 || $i1 >255 } { return  false } 
    set i2 [expr int($i2)]
    if { $i2 <0 || $i2 >255 } { return  false }
    set i3 [expr int($i3)]
    if { $i3 <0 || $i3 >255 } { return  false }
    set i4 [expr int($i4)]
    if { $i4 <0 || $i4 >255 } { return  false }
    
    return  true
}
#
# Procedure converts string-represented IP address to 
#  unsigned_32bit_integer.
#
proc IpAddr2int { ip } {
    set tmp_list [ split $ip "." ]        
    set i1 [ expr int([ lindex $tmp_list 0 ])<<24 ]
    set i2 [ expr int([ lindex $tmp_list 1 ])<<16 ]
    set i3 [ expr int([ lindex $tmp_list 2 ])<<8 ]
    set i4 [ expr int([ lindex $tmp_list 3 ]) ]
    set u_32bit_int [ expr $i1 + $i2 + $i3 + $i4 ]
    return $u_32bit_int
}
#
# Procedure converts unsigned_32bit_integer to 
#   string-represented IP address.
#
proc int2IpAddr { u_32bit_int } {
    set i1 [ expr ($u_32bit_int>>24) & 0x000000ff ]
    set i2 [ expr ($u_32bit_int>>16) & 0x000000ff ]
    set i3 [ expr ($u_32bit_int>>8) & 0x000000ff ]
    set i4 [ expr $u_32bit_int & 0x000000ff ]
    set ip [ format "%d.%d.%d.%d" $i1 $i2 $i3 $i4 ]
    return $ip
}
#
# Procedure checks, is it valid IP addresses range,
# bounded by IP adresses 'start' 'stop'
# Returns 'true' or 'false'
#
proc IsValidIpAddrRange { start stop } {
    if {[IsValidIpAddr $start ]} { } else { return false } 
    if {[IsValidIpAddr $stop  ]} { } else { return false } 
    set i_start [ IpAddr2int $start ]
    set i_stop  [ IpAddr2int $stop  ]
    #
    # I don't know right way to compare unsigned-32bit-int's...
    #
    set iup_start [ expr $i_start>>16 & 0xffff ]
    set idn_start [ expr $i_start & 0xffff ]
    set iup_stop  [ expr $i_stop>>16 & 0xffff ]
    set idn_stop  [ expr $i_stop & 0xffff ]

    if { $i_start == $i_stop } { 
	return true 
    } elseif { $iup_start < $iup_stop } { 
	return true 
    } elseif { $iup_start == $iup_stop && $idn_start < $idn_stop } { 
	return true 
    }
    return false 
}
#
# Procedure expands IP addresses range and returns
# full list of IP adresses. Be carefull: it can be very
# big list to fit in memory.
#
proc ExpandIpRange { start stop } {
    set i_start [ IpAddr2int $start ]
    set i_stop  [ IpAddr2int $stop  ]
    set ip_list [ list ]

    if { $i_start == $i_stop } { return [list $start] }

    set ip_list [ list ]
    set iup_start [ expr ($i_start>>16) & 0xffff ]
    set idn_start [ expr $i_start & 0xffff ]
    set iup_stop  [ expr ($i_stop>>16) & 0xffff ]
    set idn_stop  [ expr $i_stop & 0xffff ]

    set i $iup_start
    set j $idn_start
    while { $j <= $idn_stop || $i < $iup_stop } {
	set temp [expr $i<<16 ]
	lappend ip_list  [ int2IpAddr [expr $temp + $j ] ]
	if { $j == 0xffff } {
	    set j 0
	    incr i
	} else {
	    incr j
	}
    } 
    return $ip_list
}
#
# Procedure expands reange of IP adresses and
# for each IP adress run specified PROCEDURE. 
#  ( prototyped as: 
#      proc SomeProcedure { ip } {} 
#  )
proc ProcessIpRange { start stop procedure } {
    set i_start [ IpAddr2int $start ]
    set i_stop  [ IpAddr2int $stop  ]

    if { $i_start == $i_stop } { 
	eval $procedure $start
    }

    set iup_start [ expr ($i_start>>16) & 0xffff ]
    set idn_start [ expr $i_start & 0xffff ]
    set iup_stop  [ expr ($i_stop>>16> & 0xffff ]
    set idn_stop  [ expr $i_stop & 0xffff ]

    set i $iup_start
    set j $idn_start
    while { $j <= $idn_stop || $i < $iup_stop } {
	set temp [expr $i<<16 ]
	eval $procedure  [ int2IpAddr [expr $temp + $j ] ]
	if { $j == 0xffff } {
	    set j 0
	    incr i
	} else {
	    incr j
	}
    } 
}
#
#
proc IsIpAddrInRange { ip start stop } {
    if {[IsValidIpAddr $ip    ]} { } else { return false } 
    if {[IsValidIpAddrRange $start $stop  ]} { } else { return false } 

    set i_ip    [ IpAddr2int $ip ]
    set i_start [ IpAddr2int $start ]
    set i_stop  [ IpAddr2int $stop  ]
    #
    # I don't know right way to compare unsigned-32bit-int's...
    #
    set iup_ip    [ expr $i_ip>>16 & 0xffff ]
    set idn_ip    [ expr $i_ip & 0xffff ]
    set iup_start [ expr $i_start>>16 & 0xffff ]
    set idn_start [ expr $i_start & 0xffff ]
    set iup_stop  [ expr $i_stop>>16 & 0xffff ]
    set idn_stop  [ expr $i_stop & 0xffff ]

    if { $iup_ip > $iup_start & $iup_ip < $iup_stop } { 
	return true  
    } elseif { $iup_start == $iup_stop & $iup_ip == $iup_start } {
	if { $idn_ip > $idn_start & $idn_ip < $idn_stop  } { return true }
    } elseif { $iup_ip == $iup_start } {
	if { $idn_ip >= $idn_start } { return true }
    } elseif { $iup_ip > $iup_start } {
	if { $idn_ip <= $idn_stop } { return true }
    }
    return false 
}
#-------------------------------------------------------------
#
#  EditIpAddr dialog
#
#-------------------------------------------------------------
proc EditIpAddr_GUIconstructor { p } {
    if { $p=="."} { set p {} }
    set w $p.editIpAddr
    set e $w.entryFrame
    set b $w.buttonFrame
#
    toplevel $w -class Dialog
    wm transient $w [winfo toplevel [winfo parent $w]]	
    wm title $w [msgcat::mc "IP address"]
    wm withdraw $w

#
    frame $e -borderwidth 1 -relief sunken 
    pack $e -side top -padx 5 -pady 5 -expand on -fill both
    label  $e.lip -text [msgcat::mc "Enter Ip Address:"]
    entry  $e.eip -width 15 \
	-validatecommand {  return [EditIpAddr_validate [%W get] ] } \
	-invalidcommand  "EditIpAddr_invalid %W"
    pack $e.lip $e.eip -side left -padx 5 -pady 5
#
    frame $b 
    pack $b -side bottom -padx 5 -pady 5 -expand off -anchor s
    button $b.ok -text [msgcat::mc "Ok"] -command "EditIpAddr_OkButton $e"
    button $b.cancel -text [msgcat::mc "Cancel"] -command "set ::IpAddr::editIpAddr_exitFlag 2"
    pack $b.ok $b.cancel -side left -padx 5 -pady 5
#
    update idletasks
    set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
    	- [winfo vrootx [winfo parent $w]]}]
    set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
    	- [winfo vrooty [winfo parent $w]]}]
    wm geom $w +$x+$y
    #
    bind $w <Destroy> "set ::IpAddr::editIpAddr_exitFlag 2"
    bind $w <Escape>  "set ::IpAddr::editIpAddr_exitFlag 2"
}
#
proc EditIpAddr { p default } {
    if { $p=="."} { 
	set w .editIpAddr
    } else { 
	set w $p.editIpAddr
    }
    set e $w.entryFrame
    set b $w.buttonFrame
#
    if { ![winfo exists $w] } {
	EditIpAddr_GUIconstructor $p
    }
#
    set rez $default
    $e.eip insert 0 $default
    $e.eip configure -validate focusout
#
    if { [string equal [wm state $w] withdrawn ] } {
        wm deiconify $w
    } 
    raise $w
        
    set ::IpAddr::editIpAddr_exitFlag 0
    update idletasks
    set oldFocus [focus]
    set oldGrab [grab current $w]
    if { [string compare $oldGrab ""] } {
	set grabStatus [grab status $oldGrab]
    }
    grab $w 
    focus $b.ok
    #
    tkwait variable ::IpAddr::editIpAddr_exitFlag
    #
    if {[winfo exists $w]} {
	wm withdraw $w
	catch {focus $oldFocus}
	grab release $w
	if {$::IpAddr::editIpAddr_exitFlag==1} {
	    set rez [ $e.eip get ]
	}
	$e.eip configure -validate none
	$e.eip delete 0 end
    }    
    #
    if {[string compare $oldGrab ""] && [winfo exists $oldGrab] } {
	if {![string compare $grabStatus "global"]} {
	    grab -global $oldGrab
	} else {
	    grab $oldGrab
	}
    }
    return $rez
} 
#
proc EditIpAddr_validate { ip } {
    if { ![IsValidIpAddr $ip ] } { return false }
    return true
}
#
proc EditIpAddr_invalid { p } {
    selection clear
    tk_dialog $p.error [msgcat::mc "Error"] \
	[msgcat::mc "Invalid IP address."] \
	error 0 [msgcat::mc "Ok"]	
    focus $p
}
#
proc EditIpAddr_OkButton { e } {
    if { [$e.eip validate ] } {
	set ::IpAddr::editIpAddr_exitFlag 1
    }
}
#-------------------------------------------------------------
