#!/bin/awk -f

BEGIN {
    # split input fields by a colon
    FS=":"
    IGNORECASE=1
    # set default language
    LANGUAGE="en_US.UTF8"
    # set translation domain
    TEXTDOMAIN = "alterator-amanda"
    BACKUP_VARIANTS_DIR = "/etc/amanda"
    AMCHECK = "/usr/sbin/amcheck"
    AMGETCONF = "/usr/sbin/amgetconf"
    AMDUMP = "/usr/sbin/amdump"
    AMFETCHDUMP = "/usr/sbin/amfetchdump"
    AMADMIN = "/usr/sbin/amadmin"
    READ_TEMPLATE = "cd /usr/share/alterator-amanda && "
    AMANDA_USER = "amanda"
    CHECK_RESULT = ""
    readmsg=0
    LOGFILE="/var/log/configd.log"
}

# error reporting function
function debug(text, errno) {
    printf "%s amanda: %s: %s\n", strftime("%B %d %H:%M:%S"), text, errno >> LOGFILE
    fflush(LOGFILE)
}

# remove leading and trailing spaces
function trim(arg) {
    gsub(/^[[:space:]]+/,"", arg)
    gsub(/[[:space:]]+$/,"", arg)
    return arg
}

# search a regular expression in a file
function grep(regex, file, line) {
    while ((error = getline line <file) > 0)
	if (line ~ regex) {
	    close(file)
	    return 1
	}
    if (error == -1)
	debug("Error reading file '" file "'", ERRNO)
    close(file)
    return 0
}

# quote name
function wrap(name) {
    return "(\"" name "\")"
}

# quote name/value pair
function wrap2(name, param, value) {
    return "(\"" name "\" " param " \"" value "\")"
}

# show translation
# overwriting locale settings
function N_(text, list, cmd, line) {
    split(LANGUAGE, list, /:/)
    cmd = "LANGUAGE=\"" LANGUAGE "\" LANG=\"" list[1] ".UTF8\" gettext " TEXTDOMAIN " \"" text "\""
    cmd | getline line
    close(cmd)
    return line
}

# print file contents skipping empty lines
function cat_file(file,	error, line) {
    while ((error = getline line <file) > 0) {
	if (line ~ /^[[:space:]]*$/)
	    continue
	print line
    }
    if (error == -1)
	debug("Error reading file '" file "'", ERRNO)
    close(file)
}

function find_backup_variants() {
    delete VARIANTS
    ind = 1
    cmd = "find " BACKUP_VARIANTS_DIR " -type f -name amanda.conf"
    while (cmd | getline variant) {
	count = split(variant, list, /\//)
	VARIANTS[ind] = list[count-1]
	++ind
    }
    if (close(cmd) != 0)
	debug("Cannot search backup variants in '" BACKUP_VARIANTS_DIR "'")
}

function check_backup_variant(variant) {
    cmd = "find " BACKUP_VARIANTS_DIR " -type f -name amanda.conf"
    while (cmd | getline variant) {
	count = split(variant, list, /\//)
	VARIANTS[ind] = list[count-1]
	++ind
    }
    if (close(cmd) != 0)
	debug("Cannot search backup variants in '" BACKUP_VARIANTS_DIR "'")
}

function list_backup_variants() {
    n = asort(VARIANTS)
    for (i = 1; i <= n; i++)
	print wrap(VARIANTS[i])
}

function list_params(param) {
    # FIXME: split function for params from config and for from template
    split(params["_objects"], list, /\//)
    if (list[1] == ".object")
	cmd = "su - -c \"" READ_TEMPLATE AMGETCONF " --list " param  "\" -s /bin/sh " AMANDA_USER
    else
	cmd = "su - -c \"" AMGETCONF " " list[1] " --list " param  "\" -s /bin/sh " AMANDA_USER
    while (cmd | getline message) {
	print wrap(message)
    }
    # Fixme: print error message from amgetconf
    close(cmd)
}

function get_global_param(variant,param) {
    cmd = "su - -c \"" AMGETCONF " " variant " " param "\" -s /bin/sh " AMANDA_USER
    cmd | getline message
    close(cmd)
    return message
}

function get_global_param_template(param) {
    cmd = "su - -c \"" READ_TEMPLATE AMGETCONF " " param "\" -s /bin/sh " AMANDA_USER
    cmd | getline message
    close(cmd)
    return message
}

function quote(string) {
    if (string ~ /".*"/)
	return string
    else 
	return "\"" string "\""
}

function unquote(string) {
    quoted_reg = "\"([^\"]+)\""
    if (string ~ quoted_reg)
	return gensub(quoted_reg,"\\1","",string)
    else 
	return string
}

function write_all_params() {
    write_global_param(params["_objects"],"mailto",quote(params["mailto"]))
    write_global_param(params["_objects"],"tapetype",params["tapetype"])
    write_global_param(params["_objects"],"tpchanger",quote(params["tpchanger"]))
}

function write_global_param(variant, param, value) {
    param_inserted = 0
    param_reg = "(^[[:space:]]*" param ")(|[[:space:]]+[^#]*)(|#.*)$"
    commented_param_reg = "(^[[:space:]]*#+[[:space:]]*" param ")(|[[:space:]]+[^#]*)(|#.*)$"
    CONFIG_FILE = BACKUP_VARIANTS_DIR "/" variant "/amanda.conf"
    if (grep(param_reg, CONFIG_FILE))
	param_exists = 1
    else
	param_exists = 0

    cmd = "mktemp"
    # FIXME: check mktemp exit status
    cmd | getline tempfile
    close(cmd)

    while ((error = getline line <CONFIG_FILE) > 0) {
	if (line ~ param_reg) {
    	    print gensub(param_reg,"\\1 " value " \\3","",line) >> tempfile
	} else if (line ~ commented_param_reg) {
    	    print line >> tempfile
    	    if (!param_exists && !param_inserted) {
        	print param " " value >> tempfile
        	param_inserted = 1
    	    }
	} else {
    	    print line >> tempfile
	}
    }
    if (!param_exists && !param_inserted) print param " " value >> tempfile
    if (error == -1)
	debug("Error reading file '" CONFIG_FILE "'", ERRNO)
    close(CONFIG_FILE)
    close(tempfile)
    system("cat " tempfile " > " CONFIG_FILE)
    system("rm -f " tempfile)
}

function escape_regex(string) {
    # FIXME: check for - +
    gsub(/[\[\]\.\*\&\^\$\[\|\-\+\?\(\)]/, "\\\\&", string)
    return string
}

function urlencode(string) {
    # FIXME: add tab and other space symbols
    # check '
    gsub(/%/, "%25", string)
    gsub(/ /, "%20", string)
    gsub(/!/, "%21", string)
    gsub(/"/, "%22", string)
    gsub(/#/, "%23", string)
    gsub(/\$/, "%24", string)
    gsub(/\&/, "%26", string)
    gsub(/'/, "%27", string)
    gsub(/\(/, "%28", string)
    gsub(/\)/, "%29", string)
    gsub(/\*/, "%2a", string)
    gsub(/+/, "%2b", string)
    gsub(/,/, "%2c", string)
    gsub(/-/, "%2d", string)
    gsub(/\./, "%2e", string)
    gsub(/\//, "%2f", string)
    gsub(/:/, "%3a", string)
    gsub(/;/, "%3b", string)
    gsub(/?/, "%3f", string)
    gsub(/@/, "%40", string)
    gsub(/\[/, "%5b", string)
    gsub(/\\/, "%5c", string)
    gsub(/\]/, "%5d", string)
    gsub(/\^/, "%5e", string)
    gsub(/_/, "%5f", string)
    gsub(/`/, "%60", string)
    gsub(/{/, "%7b", string)
    gsub(/\|/, "%7c", string)
    gsub(/}/, "%7d", string)
    gsub(/~/, "%7e", string)
    return string
}

function disklist() {
    split(params["_objects"], list, /\//)
    DISKLIST_FILE = BACKUP_VARIANTS_DIR "/" list[1] "/disklist"
    disk_reg = "^[[:space:]]*([^# ]+)[[:space:]]+(\"[^\"]+\"|[^# ]+)[[:space:]]+([^# ]+)[[:space:]]*([[:space:]]+#.*)?$"

    while ((error = getline line <DISKLIST_FILE) > 0) {
	if (line ~ disk_reg) {
	    hostname = gensub(disk_reg,"\\1","",line)
	    diskname = unquote(gensub(disk_reg,"\\2","",line))
	    dumptype = gensub(disk_reg,"\\3","",line)
    	    print "(\"" hostname urlencode(diskname) "\" hostname \"" hostname "\" diskname \"" diskname "\" dumptype \"" dumptype "\")"
	}
    }
    if (error == -1)
	debug("Error reading file '" DISKLIST_FILE "'", ERRNO)
    close(DISKLIST_FILE)
}

function list_disk_for_backup() {
    split(params["_objects"], list, /\//)
    DISKLIST_FILE = BACKUP_VARIANTS_DIR "/" list[1] "/disklist"
    disk_reg = "^[[:space:]]*([^# ]+)[[:space:]]+(\"[^\"]+\"|[^# ]+)[[:space:]]+([^# ]+)[[:space:]]*([[:space:]]+#.*)?$"

    while ((error = getline line <DISKLIST_FILE) > 0) {
	if (line ~ disk_reg) {
	    hostname = gensub(disk_reg,"\\1","",line)
	    diskname = unquote(gensub(disk_reg,"\\2","",line))
	    dumptype = gensub(disk_reg,"\\3","",line)
    	    print "(\"" hostname urlencode(diskname) "\" hostname \"" hostname "\" diskname \"" diskname "\")"
	}
    }
    if (error == -1)
	debug("Error reading file '" DISKLIST_FILE "'", ERRNO)
    close(DISKLIST_FILE)
}

function strip_datetime(string) {
    gsub(/[- :]/,"",string)
    return string
}

function list_disk_for_restore() {
    split(params["_objects"], list, /\//)
    # FIXME: for now only localhost restoring supported
    # Long live for backend :)
    date_reg = "(19|20|21)[0-9][0-9]-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"
    time_reg = "[0-2][0-9]:[0-5][0-9]:[0-5][0-9]"
    backups_reg = "^(" date_reg "[[:space:]]+(" time_reg "[[:space:]]+)?)([^ ]+)[[:space:]]+(\"[^\"]+\"|[^ ]+)[[:space:]]+.*"
    cmd = "su - -c \"" AMADMIN " " list[1] " find localhost\" -s /bin/sh " AMANDA_USER
    while (cmd | getline message) {
	if (message ~ backups_reg) {
	    datetime = gensub(backups_reg,"\\1","",message)
	    hostname = gensub(backups_reg,"\\6","",message)
	    diskname = unquote(gensub(backups_reg,"\\7","",message))
	    print "(\"" hostname strip_datetime(datetime) urlencode(diskname) "\" datetime \"" datetime "\" diskname \"" diskname "\")"
	}
    }
    close(cmd)
}

function duplicated_diskname(new_hostname, new_diskname) {
    split(params["_objects"], list, /\//)
    DISKLIST_FILE = BACKUP_VARIANTS_DIR "/" list[1] "/disklist"
    disk_reg = "^[[:space:]]*([^# ]+)[[:space:]]+(\"[^\"]+\"|[^# ]+)[[:space:]]+([^# ]+)[[:space:]]*([[:space:]]+#.*)?$"

    while ((error = getline line <DISKLIST_FILE) > 0) {
	if (line ~ disk_reg) {
	    hostname = gensub(disk_reg,"\\1","",line)
	    diskname = unquote(gensub(disk_reg,"\\2","",line))
	    gsub(/\//,"_",diskname)
	    gsub(/\//,"_",new_diskname)
	    if (hostname == new_hostname && diskname == new_diskname) {
		close(DISKLIST_FILE)
		return 1
	    }
	}
    }
    if (error == -1)
	debug("Error reading file '" DISKLIST_FILE "'", ERRNO)
    close(DISKLIST_FILE)
    return 0
}

function add_to_disklist(hostname, diskname, dumptype) {
    split(params["_objects"], list, /\//)
    DISKLIST_FILE = BACKUP_VARIANTS_DIR "/" list[1] "/disklist"
    if (duplicated_diskname(hostname, diskname))
	print "(error \"Pair \\\"" hostname " " diskname "\\\" already exists.\")"
    else {
	system("echo '" hostname " " quote(diskname) " " dumptype "' >> " DISKLIST_FILE)
	print "()"
    }
}

function delete_from_disklist(hostname, diskname, dumptype) {
    split(params["_objects"], list, /\//)
    DISKLIST_FILE = BACKUP_VARIANTS_DIR "/" list[1] "/disklist"
    diskname = escape_regex(diskname)
    disk_reg = "^[[:space:]]*" hostname "[[:space:]]+(" diskname "|\"" diskname "\")[[:space:]]+" dumptype ".*"

    cmd = "mktemp"
    # FIXME: check mktemp exit status
    cmd | getline tempfile
    close(cmd)
    while ((error = getline line <DISKLIST_FILE) > 0) {
	if (line ~ disk_reg) {
	} else {
    	    print line >> tempfile
	}
    }
    if (error == -1)
	debug("Error reading file '" DISKLIST_FILE "'", ERRNO)
    close(DISKLIST_FILE)
    close(tempfile)
    system("cat " tempfile " > " DISKLIST_FILE)
    system("rm -f " tempfile)
}

function do_backup(hostname, diskname) {
    split(params["_objects"], list, /\//)
    cmd = "su - -c \"" AMDUMP " " list[1] " " hostname  " '" diskname "'\" -s /bin/sh " AMANDA_USER
    system(cmd)
}

function do_restore(diskname, datetime) {
    split(params["_objects"], list, /\//)
    cmd = "mkdir -p '" diskname "' && cd '" diskname "' && " AMFETCHDUMP " -a -p " list[1] " localhost '" diskname "' " strip_datetime(datetime) " | /bin/tar -xpGf -" 
    system(cmd)
}

function service_state() {
    cmd = "LANG=C /sbin/chkconfig amanda --list"
    cmd | getline state
    close(cmd)
    if (state ~ /\yon$/) { return "#t" } else {return "#f"}
}

function amanda_ready() {
    error_messages = ""
    cmd = "LANG=C /sbin/service xinetd status"
    cmd | getline state
    close(cmd)
    if (state !~ /\yrunning$/) 
	error_messages = error_messages " " N_("xinetd not running")
    cmd = "LANG=C /sbin/chkconfig amanda --list"
    cmd | getline state
    close(cmd)
    if (state !~ /\yon$/)
	error_messages = error_messages " " N_("Amanda services disabled")
    if (error_messages != "") {
	print "(error \"" error_messages "\")"
	return 0
    } else
	return 1
}

# start message reading
/^_message:begin$/ {
    readmsg=1
    next
}

# stop message reading
/^_message:end$/ {
    readmsg=0
    debug("==========", "==========")
    for (attribute in params)
	debug("params[" attribute "]", params[attribute])
    # overwrite current language
    if (params["language"] != "")
	LANGUAGE = gensub(/;/, ":", "g", params["language"])

    switch (params["action"]) {
	case "constraints":
	    switch (params["_objects"]) {
		case "/":
		    switch (params["orig_action"]) {
			case "general":
			    print "("
			    printf " service_state (default #f label \"%s\" )", N_("Enable Amanda services")
			    print ")"
			    break
			default:
			    print "("
			    printf "name (required #t match (\"^[a-zA-Z]+[^[:space:]]*$\" \"%s\") label \"%s\" )", \
			    N_("should starts with latin letter"), N_("Configuration name")
			    printf " service_state (default #f label \"%s\" )", N_("Enable Amanda services")
			    printf " tapedev (required #t label \"%s\" )", N_("Tape device or directory for storing backups")
			    print ")"
		    }
		    break
		default:
		    switch (params["orig_action"]) {
			case "restore":
			    print "("
			    # FIXME: add alternative for user typed quoted diskname
			    printf "diskname (required #t match (\"(^/([a-zA-Z[:digit:][:space:]`~!@#$%^&*()+-_=:;.,\\\\|?])*$)\" \"%s\") label \"%s\" )", \
			    N_("should be an absolute path (only latin symbols)"), N_("Disk name")
			    printf " datetime (label \"%s\" )", N_("Date")
			    print ")"
			    break
			case "global":
			    print "("
			    printf " variant (label \"%s\" )", N_("Configuration variant")
			    printf " tapedev (label \"%s\" )", N_("Tape device or directory for storing backups")
			    printf " mailto (label \"%s\" )", N_("Mail reports to users (space separated list)")
			    printf " tapetype (label \"%s\" )", N_("Tape type")
			    printf " tpchanger (label \"%s\" )", N_("Tape changer")
			    print ")"
			    break
			default:
			    print "("
			    # FIXME: add alternative for user typed quoted diskname
			    printf "diskname (required #t match (\"(^/([a-zA-Z[:digit:][:space:]`~!@#$%^&*()+-_=:;.,\\\\|?])*$)\" \"%s\") label \"%s\" )", \
			    N_("should be an absolute path (only latin symbols)"), N_("Disk name")
			    # FIXME: more tests on hostname regex
			    printf " hostname (required #t match (\"^[a-zA-Z[:digit:].-]+$\" \"%s\") label \"%s\" )", \
			    N_("should be valid host name (latin symbols, digits, dots)"), N_("Host name")
			    printf " dumptype (label \"%s\" )", N_("Dump type")
			    printf " variant (label \"%s\" )", N_("Configuration variant")
			    printf " tapedev (label \"%s\" )", N_("Tape device or directory for storing backups")
			    printf " mailto (label \"%s\" )", N_("Mail reports to users (space separated list)")
			    printf " tapetype (label \"%s\" )", N_("Tape type")
			    printf " tpchanger (label \"%s\" )", N_("Tape changer")
			    printf " datetime (label \"%s\" )", N_("Date")
			    print ")"
		    }
	    }
	    break
	case "backup":
	    if (params["backup"] == "#t") {
		if (amanda_ready()) {
		    do_backup(params["hostname"], params["diskname"])
		    print "()"
		}
	    } else {
		print "()"
	    }
	    break
	case "restore":
	    if (params["restore"] == "#t") {
		if (amanda_ready()) {
		    do_restore(params["diskname"],params["datetime"])
		    print "()"
		}
	    } else {
		print "()"
	    }
	    break
	case "delete":
	    # Check for actual deletion. Sometimes action 'delete' invoked without "delete = #t"
	    if (params["delete"] == "#t") {
		# Get type of deletion from _objects
		split(params["_objects"], list, /\//)
		switch (list[2]) {
		case "disklist":
		    delete_from_disklist(params["hostname"], params["diskname"], params["dumptype"])
		    print "()"
		    break
		default:
		    cmd = "/usr/share/alterator-amanda/delete-configuration.sh " params["_objects"] " \"delete backup\"" " &> /dev/null"
		    # FIXME: check cmd result
		    system(cmd)
		    print "()"
		}
	    }
	    else {
		print "()"
	    }
	    break
	case "new":
	    switch (params["type"]) {
	    case "disklist":
		add_to_disklist(params["hostname"], unquote(params["diskname"]), params["dumptype"])
		break
	    default:
		found = 0
		for (i in VARIANTS)
		    if (VARIANTS[i] == params["name"]) {
			found = 1
		    }
		if (found == 0) {
		    if (params["name"] == "gnutar-lists" || params["name"] == "vtapes")
			printf "(error \"Name \\\"%s\\\" used by Amanda internals\")", params["name"]
		    else {
			cmd = "/usr/share/alterator-amanda/prepare-configuration.sh /usr/share/alterator-amanda/ " params["name"] " &> /dev/null"
			res = system(cmd)
			write_global_param(params["name"],"tapedev",quote(params["tapedev"]))
			cmd = "/usr/share/alterator-amanda/prepare-vtapes.sh " params["name"] " &> /dev/null"
			res = system(cmd)
			switch (res) {
			    case 0:
				print "()"
				break
			    case 1:
			        cmd = "/usr/share/alterator-amanda/delete-configuration.sh " params["name"] " \"delete backup\"" " &> /dev/null"
				system(cmd)
				print "(error \"" N_("Can't use existing directory:") " " params["tapedev"] "\")"
				break
			    default:
			        cmd = "/usr/share/alterator-amanda/delete-configuration.sh " params["name"] " \"delete backup\"" " &> /dev/null"
				system(cmd)
				print "(error \"" N_("Can't create vtapes") ")"
			}
		    }
		}
		else {
		    print "(error \"Configuration name already exists\")"
		}
	    }
	    break
	case "list": 
	    print "("
	    switch (params["_objects"]) {
		case /tapetype_list\y/:
    		    list_params("tapetype")
		    break
		case /dumptype_list\y/: 
    		    list_params("dumptype")
		    break
		case /holdingdisk_list\y/: 
    		    list_params("holdingdisk")
		    break
		case /tpchanger_list\y/:
		    print wrap("chg-disk")
		    print wrap("chg-multi")
		    print wrap("chg-manual")
		    print wrap("chg-mtx")
		    print wrap("chg-zd-mtx")
		    print wrap("chg-scsi-chio")
		    print wrap("chg-scsi")
		    print wrap("chg-chio")
		    print wrap("chg-chs")
		    print wrap("chg-rth")
		    print wrap("chg-juke")
		    print wrap("chg-rait")
		    print wrap("chg-iomega")
		    print wrap("chg-null")
		    break
		case /disklist\y/:
		    disklist()
		    break
		case /list_disk_for_backup\y/:
		    list_disk_for_backup()
		    break
		case /list_disk_for_restore\y/:
		    list_disk_for_restore()
		    break
		default:
		    find_backup_variants()
		    list_backup_variants()

	    }
	    print ")"
	    break
	case "read": 
	    print "("
	    switch (params["_objects"]) {
		case "/":
		    find_backup_variants()
		    printf " service_state %s", service_state()
		    printf " tapedev \"%s\"", get_global_param_template("tapedev")
		    break
		default:
		    printf " tapetype \"%s\"", get_global_param(params["_objects"],"tapetype")
		    printf " mailto \"%s\"", get_global_param(params["_objects"],"mailto")
		    printf " tpchanger \"%s\"", get_global_param(params["_objects"],"tpchanger")
		    printf " tapedev \"%s\"", get_global_param(params["_objects"],"tapedev")
		    printf " variant \"%s\"", params["_objects"]
		    print  " dumptype \"compressed-tar\""
		    print  " hostname \"localhost\""
	    }
	    print ")"
	    break
# Check	for configuration is correct
# cmd = "su - -c \"" AMCHECK " -s " params["variant"] " 2> /dev/null \" -s /bin/sh " AMANDA_USER
	case "global":
	    write_all_params()
	    print "()"
	    break
	case "general":
	    if (params["service_state"] == "#t") {
		cmd = "/sbin/chkconfig amanda on"
		system(cmd)
		cmd = "/sbin/chkconfig amandaidx on"
		system(cmd)
		cmd = "/sbin/chkconfig amidxtape on"
		system(cmd)
		# xinetd stops if no services was enabled
		cmd = "/sbin/service xinetd start >&2"
		system(cmd)
	    } else {
		cmd = "/sbin/chkconfig amanda off"
		system(cmd)
		cmd = "/sbin/chkconfig amandaidx off"
		system(cmd)
		cmd = "/sbin/chkconfig amidxtape off"
		system(cmd)
	    }
	    print "()"
	    break
	default:
	    print "#f"
    }
    fflush()
    # delete attribute/value pairs before next cycle
    delete params
    #exit
    next
}

# save attribute/value pairs
{
    if (! readmsg)
	next
    attribute=$1
    value=$2
    # join the rest of fields with a colon
    for (n = 3; n <= NF; n++)
	value=value ":" $n
    value = gensub(/([^\\])\\n/, "\\1\n", "g", value)
    params[attribute]=value
}
