#!/usr/local/bin/tclsh8.6

#
# Search for a name or an IP/MAC address and display all sessions
#
# History
#   2004/11/14 : jean     : design
#   2005/06/04 : jean     : search optimization (gen-ipmac and gen-portmac)
#			    remove SQL joints, 2 requests and tcl processing
#   2010/11/30 : pda/jean : integration a Netmagis
#   2010/12/12 : pda      : i18n
#   2010/12/26 : pda      : use cgi-dispatch
#

#
# Template pages used by this script
#

set conf(page)          mac.html
set conf(pagesearch)	macsearch.html

#
# Next actions
#

set conf(next)		"mac"

#
# Script parameters
#

set conf(tabsession) {
    global {
        chars {12 normal}
        align {left}
        botbar {yes}
        columns {15 35 15 35}
    }   
    pattern Title {
        vbar {yes}
        column { 
            multicolumn {4}
            align {center}
            chars {14 bold}
        }
        vbar {yes}
    }
    pattern Rem {
        vbar {yes}
        column { 
            multicolumn {4}
            align {center}
            chars {14}
        }
        vbar {yes}
    }
    pattern Close {
        vbar {yes}
        column {
            multicolumn {2}
        }   
        vbar {yes} 
        column {
            multicolumn {2}
        }   
        vbar {yes}
    }
    pattern Active {
        vbar {yes}
        column {
            multicolumn {2}
            chars {bold}
        }
        vbar {yes}
        column {
            multicolumn {2}
            chars {bold}
        }   
        vbar {yes}
    }   
    pattern Info {
        vbar {yes}
        column {
	}
        vbar {yes}
        column {
            multicolumn {3}
            chars {bold}
	    format {raw}
        }
        vbar {yes}
    }
}

set conf(tabmenuipmac) {
    global {
        chars {12 normal}
        align {left}
        botbar {yes}
        columns {12 48 15 25}
    }   
    pattern Data {
        vbar {yes}
        column {
	    format {raw}
	}
        vbar {yes}
        column { 
	    format {raw}
	}
        vbar {yes}
        column {
	    format {raw}
	}
        vbar {yes} 
        column { }
        vbar {yes}
    }
    pattern Title {
        vbar {yes}
        column { 
            chars {bold}
	}
        vbar {yes}
        column { 
            chars {bold}
	}
        vbar {yes}
        column {
            chars {bold}
        }
        vbar {yes} 
        column {
            chars {bold}
        }
        vbar {yes}
    }
}

set conf(tabmenuportmac) {
    global {
        chars {12 normal}
        align {left}
        botbar {yes}
        columns {5 5 10 10 25 25}
    }   
    pattern Data {
        vbar {yes}
        column {
	    format {raw}
	}
        vbar {yes}
        column { 
	    format {raw}
	}
        vbar {yes}
        column {
	    format {raw}
	}
        vbar {yes} 
        column {
	}
        vbar {yes}
        column {
	    format {raw}
	}
        vbar {yes}
        column {
	}
        vbar {yes}
    }
    pattern Title {
        vbar {yes}
        column { 
            chars {bold}
	}
        vbar {yes}
        column { 
            chars {bold}
	}
        vbar {yes}
        column { 
            chars {bold}
	}
        vbar {yes}
        column {
            chars {bold}
        }
        vbar {yes} 
        column {
            chars {bold}
        }
        vbar {yes}
        column {
            chars {bold}
        }
        vbar {yes}
    }
}

#
# Script parameters
#

set conf(sqldateformat) {'DD/MM/YYYY HH24:MI:SS'}
set conf(clockformat)   {%d/%m/%Y %H:%M:%S}

#
# Netmagis general library
#

source /usr/local/lib/netmagis/libnetmagis.tcl

# ::webapp::cgidebug ; exit

##############################################################################
# Utility functions
##############################################################################

#
# Guess parameter type
#
# Input: 
#   - mdbfd : mac database handle
#   - ndbfd : netmagis database handle
#   - param : parameter given by the user
#   - _msg : in return, error message
# Output:
#   - return value: empty list (error) or list {type param} where
#	- type = "ip", "name", "mac", "idipmac", ... 
#	- param = canonical representation of parameter
#   - msg : empty string or error message
#

proc guess-type {mdbfd ndbfd param _msg} {
    upvar $_msg msg

    set type ""
    set msg  ""

    set ipversion [::ip::version $param]

    set mac_re {[0-9a-f]{1,2}(:[0-9a-f]{1,2}){5}}
    set ip4_re {[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+}
    set ip6_re {[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+}
    set port_re {[0-9a-zA-Z/.\-]+}
    set vlan_re {[0-9]+}
    set ipmac_assoc_re "$mac_re,$ip4_re"
    set portmac_assoc_re "$ip4_re,$mac_re,$port_re,$vlan_re"
    set eq_re $ip4_re


    if {[regexp "^$mac_re\$" $param]} then {
	# MAC address
	set type "mac"
    } elseif {[regexp {^[0-9a-f]{4}(.[0-9a-f]{4}){2}$} $param]} then {
	# MAC address with Cisco format (00AB.1234.5678)
	set n {([0-9a-f]{2})}
	regsub "^$n$n.$n$n.$n$n" $param {\1:\2:\3:\4:\5:\6} param
	set type "mac"
    } elseif {[regexp {^[0-9a-f]{6}-[0-9a-f]{6}$} $param]} then {
	# MAC address with HP format (00206b-7a1477)
	set n {([0-9a-f]{2})}
	regsub "^$n$n$n-$n$n$n" $param {\1:\2:\3:\4:\5:\6} param
	set type "mac"
    } elseif {[regexp "^portmac_assoc:($portmac_assoc_re)\$" $param dummy \
		portmac_assoc]} then {
        # portmac association
	set type "portmac_assoc"
	set param $portmac_assoc
    } elseif {[regexp "^ipmac_assoc:($ipmac_assoc_re)\$" $param dummy \
		ipmac_assoc]} then {
        # ipmac association
	set type "ipmac_assoc"
	set param $ipmac_assoc
    } elseif {[regexp "^eq:($eq_re)\$" $param dummy eq]} then {
        # By equipment
	set type "equipement"
	set param $eq
    } elseif {[regexp "^vlan:($vlan_re)\$" $param dummy vlan]} then {
        # By VLAN
	set type "vlan"
	set param $vlan
    } elseif {$ipversion == 4 || $ipversion == 6} then {
	# IP Address
        set msg [check-ip-syntax $ndbfd $param "inet"]
        if {$msg eq ""} then {
  	    set type "ip"
	} else {
	    return {}
	}
    } elseif {[regexp {^[a-z0-9\-.]+$} $param]} then {
        # name
	set type "name"
    } else {
	# Other
	set msg [mc "Invalid search string '%s'" $param]
	return {}
    }

    return [list $type $param]
}

#
# Display an HTML result with search result
#
# Input:
#   - mdbfd : mac database handle
#   - ndbfd : netmagis database handle
#   - type : search type (ip, mac, etc.)
#   - param : search criterion
# Output:
#   - return value : HTML code
#

proc html-result {mdbfd ndbfd type param} {
    set tab ""
    switch $type {
	name {
	    set lip [resolve "name" $param resmsg]
	    if {$resmsg ne ""} {
		d error "resolve: $resmsg"
	    }
	    foreach ip $lip {
		append tab [gen-choice-ipmac $mdbfd $ndbfd $ip ""]
	    }
	}
	ip {
	    append tab [gen-choice-ipmac $mdbfd $ndbfd $param ""]
	}
	portmac_assoc {
	    append tab [gen-portmac-assoc $mdbfd $ndbfd $param]
	}
	ipmac_assoc {
	    append tab [gen-ipmac-assoc $mdbfd $ndbfd $param]
	}
	equipement {
	    append tab [gen-eq $mdbfd $ndbfd $param]
	}
	vlan {
	    append tab [gen-vlan $mdbfd $ndbfd $param]
	}
	mac {
	    append tab [gen-choice-ipmac $mdbfd $ndbfd "" $param]
 	    append tab [gen-mac $mdbfd $ndbfd $param]
	}
	default {
	    d error [mc "Internal error: invalid parameter type '%s'" $type]
	}
    }
    return $tab
}

#
# Display ip-mac association
# Input:
#      ipmac_assoc : string containing "ip,mac"
# Return:
#      html string containing ip-mac association sessions
#

proc gen-ipmac-assoc {mdbfd ndbfd ipmac_assoc} {
    global conf

    #
    # Check if IP and MAC addresses exist
    #

    lassign [split $ipmac_assoc ","] searchmac searchip
    set ip ""
    set mac ""
    set sql "SELECT (data).ip, (data).mac FROM mac.ipmac
		WHERE (data).ip = '$searchip' AND (data).mac = '$searchmac'
		LIMIT 1"
    pg_select $mdbfd $sql tab {
	set ip $tab(ip)
	set mac $tab(mac)
    }
    if {$ip eq ""} then {
	return [mc "IP-MAC association (%1$s,%2$s) not found" $searchip $searchmac]
    }

    set lines {}

    set dnsname [resolve "ip" $ip resmsg]
    if {$resmsg ne ""} {
	d error "resolve: $resmsg"
    }

    set iplink [gen-link [display-ip $ip $dnsname] $ip]
    lappend lines [list Info [mc "IP address"] $iplink]

    lappend lines [list Info [mc "MAC address"] \
			[gen-link [display-mac $mdbfd $mac] $mac] \
		    ]

    #
    # Display sessions
    #

    set found 0
    set sql "SELECT  to_char(start,$conf(sqldateformat)) AS dstart,
			to_char(stop,$conf(sqldateformat)) AS dstop,
			closed AS closed
		FROM mac.ipmac
		WHERE (data).ip = '$ip' AND (data).mac = '$mac'
		ORDER BY start DESC"
    pg_select $mdbfd $sql tab2 {
	if {$tab2(closed)} then { set pat "Close" } else { set pat "Active" }
	if {$found == 0} then {
	    lappend lines [list Active [mc "Start"] [mc "End"]]
	    set found 1
	}
	lappend lines [list $pat $tab2(dstart) $tab2(dstop)]
    }

    if {$found == 0} then {
	lappend lines [list Rem [mc "No IP-MAC association found"]]
    }

    # 
    # Format result
    # 

    return [::arrgen::output "html" $conf(tabsession) $lines]
}

#
# Display a portmac session list, for a given idportmac
#

proc gen-portmac-assoc {mdbfd ndbfd portmac_assoc} {
    global conf

    lassign [split $portmac_assoc ","] searchsrc searchmac searchport searchvlan
    if {$searchvlan eq ""} then {
	set searchvlan 0
    }
    set lines {}
    set sql "SELECT (data).mac AS mac,
		    (data).port AS port,
		    src AS src,
		    (data).vlanid AS vlanid
		FROM mac.portmac
		WHERE (data).mac='$searchmac'
			AND lower((data).port)='$searchport' 
			AND (data).vlanid=$searchvlan
			AND src='$searchsrc'"
    pg_select $mdbfd $sql tab {
	lappend lines [list "Info" \
			    [mc "MAC address"] \
			    [gen-link [display-mac $mdbfd $tab(mac)] $tab(mac)] \
			]
	set eq [get-eq-name $ndbfd $tab(src)]
	lappend lines [list "Info" \
			    [mc "Equipment"] \
			    [gen-link $eq "eq:$tab(src)"] \
			]
	lappend lines [list "Info" \
			    [mc "Interface"] \
			    $tab(port) \
			]
	set vlan [display-vlan $ndbfd $tab(vlanid)]
	lappend lines [list "Info" \
			    [mc "Vlan"] \
			    [gen-link $vlan "vlan:$tab(vlanid)"] \
			]
    }

    set found 0
    set sql "SELECT to_char(portmac.start,$conf(sqldateformat)) AS dstart,
		    to_char(portmac.stop,$conf(sqldateformat)) AS dstop,
		    portmac.closed AS closed
		FROM mac.portmac
		WHERE (data).mac='$searchmac'
			AND (data).port='$searchport' 
			AND (data).vlanid=$searchvlan
		ORDER BY portmac.stop DESC"
    pg_select $mdbfd $sql tab2 {
	if {$found==0} then {
	    lappend lines [list Active [mc "Start"] [mc "End"]]
	    set found 1
	}

	if {$tab2(closed)} then { set pat "Close" } else { set pat "Active" }
	lappend lines [list $pat $tab2(dstart) $tab2(dstop)]
    }

    return [::arrgen::output "html" $conf(tabsession) $lines]
}

#
# Display a menu of the different IP-MAC associations given an IP or MAC address
#
# Input:
#  - ip : ip address (may be null)
#  - mac : mac address (may be null)
#
# Output:
#  - return value : HTML code
#

proc gen-choice-ipmac {mdbfd ndbfd ip mac} {
    global conf

    # This case should not happen
    if {$mac eq "" && $ip eq ""} then {
	return ""
    }

    set table ""
    set lines {}
    lappend lines [list Title \
			[mc "Sessions"] \
			[mc "IP address"] \
			[mc "MAC address"] \
			[mc "Last occurrence"] \
		    ]
    set crit {}

    if {$ip ne ""} then {
	lappend crit " (data).ip='$ip' "
    }

    if {$mac ne ""} then {
	lappend crit " (data).mac='$mac' "
    }

    #
    # Search all ip-mac associations and last occurrence date
    #
    
    set n 0
    set sql "SELECT DISTINCT (data).ip AS ip, (data).mac AS mac
		FROM mac.ipmac WHERE [join $crit AND]"
    set d {}
    pg_select $mdbfd $sql tab {
	incr n
	set ipmac_assoc_link [gen-link [mc "Details"] "ipmac_assoc:$tab(mac),$tab(ip)"]
	set dnsname [resolve "ip" $tab(ip) resmsg]
	if {$resmsg ne ""} {
	    d error "resolve: $resmsg"
	}
	set iplink [gen-link [display-ip $tab(ip) $dnsname] $tab(ip)]

	pg_select $mdbfd "SELECT extract(epoch from max(stop)) AS laststop
		FROM mac.ipmac
		WHERE (data).ip='$tab(ip)' AND (data).mac='$tab(mac)'" tab2 {
	    set laststop $tab2(laststop)
	}

	if {$laststop ne ""} then {
	    set laststop [expr int($laststop)]
	} else {
	    set laststop 0
	}
	set maclink [gen-link [display-mac $mdbfd $tab(mac)] $tab(mac)]
	lappend d [list Data $ipmac_assoc_link $iplink $maclink $laststop]
    }

    #
    # Sort list and convert date
    #

    foreach l [lsort -index end -integer $d] {
	set t [lindex $l end]
	set date [clock format $t -format "$conf(clockformat)"]
	if {[regexp {^01/01/1970} $date]} then {
	    set date [mc "(no date)"]
	}
	lappend lines [lreplace $l end end $date]
    }
    
    if {$n > 0} then {
	set table [mc {%1$s MAC-IP address associations found for %2$s:} $n "$mac$ip"]
	append table "<br>"
	append table [::arrgen::output "html" $conf(tabmenuipmac) $lines]
    } else {
	set table [mc "No MAC-IP address association found for %s" "$mac$ip"]
	append table "<br>"
    }

    append table "<br>"
    return $table
}

#
# Display a menu of the different MAC-port-vlan associations given a
# MAC address. This is a just a wrapper to gen-choice-portmac
#
# Input:
#  - mac : MAC address
#
# Output:
#  - return value : HTML code
#

proc gen-mac {mdbfd ndbfd mac} {
    if {$mac eq ""} then {
	return ""
    }
    return [gen-choice-portmac $mdbfd $ndbfd "(data).mac='$mac'"]
}

#
# Display a menu of MAC-Port-Vlan for an equipment
#
# Input:
#   - mdbfd : mac database handle
#   - ndbfd : netmagis database handle
#   - eq : equipment ip address
#
# Output:
#  - return value : HTML code
#

proc gen-eq {mdbfd ndbfd eq} {
    if {$eq eq ""} then {
	return [mc "No equipment address"]
    }
    return [gen-choice-portmac $mdbfd $ndbfd "portmac.src='$eq'"]
}

#
# Display a menu of MAC-Port-Vlan for a Vlan
#
# Input:
#   - vlanid : vlan-id
#
# Output:
#  - return value : HTML code
#

proc gen-vlan {mdbfd ndbfd vlanid} {
    if {[display-vlan $ndbfd $vlanid] eq ""} then {
	return [mc "Vlan '%s' not found" $vlanid]
    }
    return [gen-choice-portmac $mdbfd $ndbfd "(data).vlanid=$vlanid"]
}

#
# Search all distinct Port-MAC-vlan associations and last occurrence date
# according to a criterion (SQL expression).
#
    
proc gen-choice-portmac {mdbfd ndbfd crit} {
    global conf

    set n 0
    set table ""
    set lines {}
    lappend lines [list Title \
			    [mc "Sessions"] \
			    [mc "MAC address"] \
			    [mc "Equipment"] \
			    [mc "Interface"] \
			    [mc "Vlan"] \
			    [mc "Last occurrence"] \
			]
    set sql "SELECT DISTINCT 	src AS src,
    				(data).mac AS mac,
				(data).port AS port,
				(data).vlanid AS vlanid
		    FROM	mac.portmac
		    WHERE	$crit
		    GROUP BY    src, data"
    set d {}
    pg_select $mdbfd $sql tab {
	incr n
	set portmac_assoc_link [gen-link [mc "Details"] \
		    "portmac_assoc:$tab(src),$tab(mac),$tab(port),$tab(vlanid)"]
	set maclink [gen-link [display-mac $mdbfd $tab(mac)] $tab(mac)]

    	if {$tab(vlanid) eq ""} then {
	    set tab(vlanid) 0
	}
	set vlanid $tab(vlanid)
	set vlanlink [gen-link [display-vlan $ndbfd $vlanid] "vlan:$vlanid"]
	set eqname [get-eq-name $ndbfd $tab(src)] 
	set eqlink [gen-link [display-eq $eqname] "eq:$tab(src)"]

	set sql "SELECT extract(epoch from max(stop)) AS stop
		    FROM mac.portmac
		    WHERE src='$tab(src)'
			    AND (data).mac='$tab(mac)'
			    AND (data).port='$tab(port)'
			    AND (data).vlanid=$tab(vlanid)"
	set stop ""
	pg_select $mdbfd $sql tab2 {
	    set stop $tab2(stop)
	}

	if {$stop ne ""} then {
	    set stop [expr int($stop)]
	} else {
	    set stop 0
	}
	lappend d [list Data \
	    $portmac_assoc_link $maclink $eqlink $tab(port) $vlanlink $stop ]

    }

    #
    # Sort list and convert date
    #

    foreach l [lsort -index end -integer $d] {
	set t [lindex $l end]
	set ndate [clock format $t -format "$conf(clockformat)"]
	if {[regexp {^01/01/1970} $ndate]} then {
	    set ndate [mc "(no date)"]
	}
	lappend lines [lreplace $l end end $ndate]
    }

    if {$n > 0} then {
	set table [mc "%s MAC-Port-VLAN associations found" $n]
	append table [::arrgen::output "html" $conf(tabmenuportmac) $lines]
    } else {
	set table [mc "No MAC-Port-VLAN association found"]
    }

    return $table
}

#
# Display an HTML link
#

proc gen-link {text target} {
    global conf

    d urlset "" $conf(next) [list [list "recherche" $target]]
    set url [d urlget ""]
    return [::webapp::helem "a" $text "href" $url]
}

#
# Display a Vlan name: "vlanid (description)" or "vlanid"
#

proc display-vlan {ndbfd vlanid} {
    global cachevlan

    if {[info exists cachevlan($vlanid)]} then {
	set descr $cachevlan($vlanid)
    } else {
	set descr ""
	if {[regexp {^[0-9]+$} $vlanid]} then {
	    set sql "SELECT descr AS descr FROM topo.vlan WHERE vlanid=$vlanid"
	    pg_select $ndbfd $sql tab {
		set descr $tab(descr)
	    }
	} else {
	    set descr "-"
	}
	set cachevlan($vlanid) $descr
    }

    if {$descr ne ""} then {
	set dispvlan "$vlanid ($descr)"
    } else {
	set dispvlan $vlanid
    }

    return $dispvlan
}

proc display-eq {eq} {
    global conf

    regsub "\.$conf(defaultdomain)\$" $eq "" eq
	
    return $eq
}

#
# Search equipment name given its address
#
# Input:
#   - ndbfd : netmagis database handle
#   - addr : ip address of equipment
#
# Return: equipment name or empty string if not found
#

proc get-eq-name {ndbfd addr} {
    global cacheeq

    if {[info exists cacheeq(addr:$addr)]} then {
	set eq $cacheeq(addr:$addr)
    } else {
	set eq [mc "Unknown equipment (address %s)" $addr]
	set sql "SELECT rr.name || '.' || domain.name AS fqdn
		    FROM dns.rr_ip, dns.rr, dns.domain
		    WHERE rr_ip.addr = '$addr'
		    	AND rr.idrr = rr_ip.idrr
			AND rr.iddom = domain.iddom"
	pg_select $ndbfd $sql tab {
	    set eq $tab(fqdn)
	}
	set cacheeq(name:$eq) $addr
	set cacheeq(addr:$addr) $eq
    }

    return $eq
}

#
# Search equipment addr given an equipment name
#
# Return: equipment IP address or empty string if not found
#

proc get-eq-addr {ndbfd eq} {
    global cacheeq

    if {[info exists cacheeq(name:$eq)]} then {
	set addr $cacheeq(name:$eq)
    } else {
	set l [split $eq "."]
	set shortname [lindex $l 0]
	set domain [join [lrange $l 1 end] "."]
	set sql "SELECT rr_ip.addr
		    FROM dns.rr_ip, dns.rr, dns.domain
		    WHERE rr.idrr = rr_ip.idrr
		    	AND rr.iddom = domain.iddom
		    	AND rr.name = '$shortname'
		    	AND domain.name = '$domain'"
	set addr ""
	pg_select $ndbfd $sql tab {
	    set addr $tab(addr)
	}
	set cacheeq(addr:$addr) $eq
	set cacheeq(name:$eq) $addr
    }
    return $addr
}

#
# Display search criterion on result page as a (somewhat) readable text
#

proc display-crit {ndbfd type param} {
    switch $type {
	mac		{ set m "MAC address %s" }
	ip        	{ set m "IP address %s" }
	ipmac_assoc	{ set m "IP-MAC" }
	portmac_assoc	{ set m "MAC-Port-Vlan" }
	vlan		{ set m "Vlan %s" ; set param [display-vlan $ndbfd $param] }
	name		{ set m "host %s" }
	default		{ set m "???" }
    }
    return [mc $m $param]
}

#
# Format an IP address: either IP followed by DNS name if it exists, or IP
# address only.
#

proc display-ip {ip dnsname} {
    if {$dnsname ne ""} then {
	set disp "$ip ($dnsname)"
    } else {
	set disp "$ip"
    }
    return $disp
}

#
# Search OUI for a given MAC address and returns a string built from
# MAC address and manufacturer name
# 
# Example:
# - MAC address "08:00:20:67:89:ab" becomes
#	"08:00:20:67:89:ab (SUN MICROSYSTEMS INC.)"
#
# - MAC address mac "01:23:45:67:89:ab" becomes
#	"01:23:45:67:89:ab"
#	(no change since OUI is not found)
#

proc display-mac {mdbfd mac} {
    global ouicache

    if {[info exists ouicache($mac)]} then {
	return $ouicache($mac)
    }

    set manuf 0
    pg_select $mdbfd "SELECT mac.manuf('$mac') AS m" tab {
	set manuf $tab(m)
    }

    if {$manuf ne ""} then {
	set dispmac "$mac ($manuf)"
    } else {
	set dispmac "$mac"
    }
    set ouicache($mac) $dispmac
    return $dispmac
}

#
# DNS resolver : calls "host" command and produces an IP address list or a FQDN
#
# Input: 
#  - type : "name" for a standard resolution, "ip" for a reverse resolution
#  - arg  : resolve parameter
# Output: 
#  - return value: addresse list or domain name
#

proc resolve {type arg _msg} {
    global conf
    global dnscache
    upvar $_msg msg

    set msg ""
 
    if {! ([regexp {^[a-z0-9\-.]+$} $arg] || [regexp {^([0-9.]+|[0-9a-f:.]+)$} $arg])} then {
	return {}
    }

    #
    # Filter definition
    #

    if {[info exists dnscache($arg)]} then {
	return $dnscache($arg)
    }

    switch $type {
	"name" { set pat "address" }
	"ip"  { set pat "domain name pointer" }
    }

    #
    # Extract "host" command result
    #

    set hostcmd [get-local-conf "hostcmd"]
    if {[catch {exec $hostcmd $arg} buf]} then {
	if {[regexp {Host .+ not found} $buf]} then {
	    # the host was not found, not an error
	} else {
	    # real error
	    set msg $buf
	}
        set buf ""
    }

    #
    # Keep only the last word of each line
    #

    set l {}
    foreach line [split $buf "\n"] {
	if {[regexp $pat $line]} then {
	    lappend l [lindex $line end]
	}
    }
    set dnscache($arg) $l
    return $l 
}

##############################################################################
# Display page
##############################################################################

d cgi-register {recherche {}} {} {
    global conf

    #
    # End of script: output page and close database
    #

    d urlset "%URLFORM%" $conf(next) {}
    d result $conf(pagesearch) {}
}

##############################################################################
# Search
##############################################################################

d cgi-register {recherche .+} {} {
    global conf

    #
    # Save netmagis database handler
    #

    set ndbfd $dbfd


    #
    # Get default domain
    #

    set conf(defaultdomain) [dnsconfig get "defdomain"]

    #
    # Open MAC database
    #

    set conninfo [get-conninfo "macdb"]
    if {[catch {set mdbfd [pg_connect -conninfo $conninfo]} msg]} then {
        d error $msg
    }

    #
    # Guess search type
    #

    set recherche [string tolower $recherche]
    set l [guess-type $mdbfd $ndbfd $recherche msg]
    if {[llength $l] == 0} then {
	d error $msg
    } 
    lassign $l type param

    #
    # Display output
    #

    set table [html-result $mdbfd $ndbfd $type $param]
    if {$table eq ""} then {
	set table [mc "No information found"]
    }

    #
    # End of script: output page and close database
    #

    d result $conf(page) \
	[list \
		[list %RECHERCHE% [display-crit $mdbfd $type $param]] \
		[list %TABLEAU%   $table] \
	    ]
}

##############################################################################
# Main procedure
##############################################################################

d cgi-dispatch "mac" "mac"
