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

#
# Mail route generation for sendmail (or other MTAs)
#
# Syntax:
#	mkmroute [-h][-q][-v][-n] [-w <view>]
#	    -h: help
#	    -q: silent operation
#	    -v: verbose output
#	    -n: don't modify files
#	    -w <view> : limit generation to view where mail host is
#
# History:
#   2004/03/09 : pda/jean : original specification
#   2004/03/09 : pda/jean : design
#   2011/05/14 : pda      : i18n and re-design
#   2011/06/10 : pda      : clarify verbose levels
#   2012/10/24 : pda/jean : add views
#

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

#
# Self explanatory
#

set conf(usage) {usage: %1$s [-h][-q][-v][-n][-w <view>]
    -h : get this help
    -q : keep silent on normal operation
    -v : verbose (show file or diffs)
    -n : don't perform file installation
    -w <view> : limit generation to this view
}

#
# Generates a mail route text
#
# Input:
#   - parameters
#	- dbfd: database handle
#	- idview: id specified view (where host is), or -1
#	- mrouteprologue: name of file containing mail route prologue
#	- mroutefmt: format of individual mail routes (1: address, 2: mail host)
#	- _mroutetxt : variable containing generated text in return
# Output:
#   - return value: empty string, or error message
#   - variable _mroutetxt: generated mail routes
#
# History
#   2004/03/09 : pda/jean : design
#   2011/05/14 : pda      : use configuration variables
#   2012/10/24 : pda/jean : add views
#

proc gen-mroute {dbfd idview mrouteprologue mroutefmt _mroutetxt} {
    upvar $_mroutetxt mroutetxt

    #
    # Step 1: read prologue
    #

    if {[catch {set fd [open $mrouteprologue "r"]} err]} then {
	return $err
    }
    set mroutetxt [read $fd]
    close $fd

    #
    # Step 2: read mail routes from database
    #

    set sql "SELECT r1.name || '.' || d1.name AS addr,
		    r2.name || '.' || d2.name AS mbox
		FROM dns.rr r1, dns.domain d1,
		    dns.rr r2, dns.domain d2,
		    dns.mail_role
		WHERE ($idview = -1 OR r2.idview = $idview)
		    AND mail_role.mailaddr = r1.idrr
		    AND r1.iddom = d1.iddom
		    AND mail_role.mboxhost = r2.idrr
		    AND r2.iddom = d2.iddom
		ORDER BY d2.name ASC, r2.name ASC, d1.name ASC, r1.name ASC
		"
    pg_select $dbfd $sql tab {
	append mroutetxt [format $mroutefmt $tab(addr) $tab(mbox)]
	append mroutetxt "\n"
    }

    return ""
}

#
# Install new contents in a file, and run appropriate command
#
# Input:
#   - parameters
#	- file: name of file
#	- text: new file content
#	- cmd: command to apply to file or empty string
#	- _errmsg: variable containing error message in return
# Output:
#   - return value: 1 (ok) or 0 (error)
#   - variable _errmsg: error message, if return value = 0
#
# History
#   2004/03/09 : pda/jean : design
#   2011/05/14 : pda      : use configuration variables
#   2011/05/22 : pda      : simplification
#   2011/06/05 : pda      : use fileinst class
#

proc install-file-from-text {file text cmd _errmsg} {
    upvar $_errmsg errmsg

    set fq [::fileinst create %AUTO%]
    set errmsg [$fq add $file $text]
    if {$errmsg eq ""} then {
	set errmsg [$fq commit]
	if {$errmsg eq "" && $cmd ne ""} then {
	    if {[catch {exec -ignorestderr sh -c $cmd} errmsg]} then {
		set errmsg "Cannot run command $cmd\n$errmsg"
		set msg [$fq uncommit]
		if {$msg ne ""} then {
		    append errmsg "\n$msg"
		}
		return 0
	    }
	}
    }
    $fq destroy

    return 1
}

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

proc usage {argv0} {
    global conf

    regsub ".*/" $argv0 "" argv0
    puts -nonewline stderr [format $conf(usage) $argv0]
    exit 1
}

proc main {argv0 argv} {
    global conf

    #
    # Netmagis database access
    #

    set msg [d init-script dbfd $argv0 true tabcor]
    if {$msg ne ""} then {
	fatal-error "$msg\nAbort."
    }

    #
    # Argument checking
    #

    set verbose 0
    set doit 1
    set view ""

    while {[llength $argv] > 0} {
	set a [lindex $argv 0]
	switch -glob -- $a {
	    -h {
		usage $argv0
	    }
	    -q {
		set verbose -1
		set argv [lreplace $argv 0 0]
	    }
	    -v {
		set verbose 1
		set argv [lreplace $argv 0 0]
	    }
	    -n {
		set doit 0
		set argv [lreplace $argv 0 0]
	    }
	    -w {
		set view [lindex $argv 1]
		set argv [lreplace $argv 0 1]
	    }
	    -* {
		warning "Unknown option '$a'"
		usage $argv0
	    }
	    default {
		break
	    }
	}
    }

    if {[llength $argv] > 1} then {
	usage $argv0
    }

    #
    # Get configuration values
    #

    foreach o {diff mroutefile mrouteprologue mroutefmt mroutecmd} {
	set $o [get-local-conf $o]
    }

    #
    # Check view name
    #

    set idview -1
    if {$view ne ""} then {
	set qview [::pgsql::quote $view]
	set sql "SELECT idview FROM dns.view WHERE name = '$qview'"
	pg_select $dbfd $sql tab {
	    set idview $tab(idview)
	}
	if {$idview == -1} then {
	    d error "View '$view' not found"
	}
    }

    #
    # Do the work
    #

    set msg [gen-mroute $dbfd $idview $mrouteprologue $mroutefmt txt]
    if {$msg ne ""} then {
	d error $msg
    }

    switch [compare-file-with-text $mroutefile $txt msg] {
	-1 {
	    d error $msg
	}
	0 {
	    # nothing
	    if {! $doit} then {
		if {$verbose >= 0} then {
		    puts "Mail routes are not modified"
		    if {$verbose == 1} then {
			puts -nonewline $txt
		    }
		}
	    }
	}
	1 {
	    if {$verbose >= 0} then {
		puts "Mail routes are modified"
	    }
	    if {$doit} then {
		if {$verbose == 1} then {
		    show-diff-file-text stdout $diff $mroutefile $txt
		}
		if {! [install-file-from-text $mroutefile $txt $mroutecmd msg]} then {
		    d error $msg
		}
	    } else {
		if {$verbose == 1} then {
		    puts -nonewline $txt
		}
	    }
	}
    }

    d end
    return 0
}

exit [main $argv0 $argv]
