#!/bin/awk -f

BEGIN {
	SQUID_CONF = "/etc/squid/squid.conf"
	SQUID_NETS = "/etc/squid/our_networks"
	SQUID_BLACKLIST = "/etc/squid/blacklist"
	SERVICE = "/sbin/service"
	# split input fields by a colon
	FS=":"
	# set default language
	LANGUAGE="en_US.UTF8"
	# set translation domain
	TEXTDOMAIN = "alterator-squid"
	readmsg=0
	LOGFILE="/var/log/configd.log"
}

# 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
}

# error reporting function
function debug(text, errno) {
	printf "%s squid: %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
}

# get Squid config variable
function squid_conf_read(param, 	count, regex, error,line, list) {
	regex = "^" param "[[:space:]]+(.*)$"
	while ((error = getline line <SQUID_CONF) > 0)
		if (match(line, regex, list) > 0) {
			close(SQUID_CONF)
			return list[1]
		}
	if (error == -1)
		debug("Error reading file '" SQUID_CONF "'", ERRNO)
	close(SQUID_CONF)
	return ""
}

# write Squid config variable
function squid_conf_write(param, value,		cmd, tempfile, found, regex, error, line) {
	cmd = "mktemp"
	cmd | getline tempfile
	close(cmd)

	found = 0
	regex = "^" param "[[:space:]]+.*$"
	while ((error = getline line <SQUID_CONF) > 0) {
		if (line !~ regex) {
			print line >> tempfile
			continue
		}
		print param " " value >> tempfile
		found = 1
	}
	if (error == -1)
		debug("Error reading file '" SQUID_CONF "'", ERRNO)
	if (!found)
		print param " " value >> tempfile
	close(SQUID_CONF)
	close(tempfile)
	system("cat " tempfile " > " SQUID_CONF)
	system("rm -f " tempfile)
}

# replace Squid config settings
function squid_conf_replace(regex, replace,		cmd, tempfile, error, line) {
	cmd = "mktemp"
	cmd | getline tempfile
	close(cmd)

	while ((error = getline line <SQUID_CONF) > 0) {
		if (line !~ regex) {
			print line >> tempfile
			continue
		}
		print gensub(regex, replace, "g", line) >> tempfile
	}
	if (error == -1)
		debug("Error reading file '" SQUID_CONF "'", ERRNO)
	close(SQUID_CONF)
	close(tempfile)
	system("cat " tempfile " > " SQUID_CONF)
	system("rm -f " tempfile)
}

# remove regex from file
function delete_regex(file, regex,	cmd, tempfile, error, line) {
	cmd = "mktemp"
	cmd | getline tempfile
	close(cmd)

	while ((error = getline line <file) > 0) {
		if (line ~ regex)
			continue
		print line >> tempfile
	}
	if (error == -1)
		debug("Error reading file '" file "'", ERRNO)
	close(file)
	close(tempfile)

	system("cat " tempfile " > " file)
	system("rm -f " tempfile)
}

# check IP range as implemented in Squid code with alterator additions
function validate_range(range,		ip, i) {
	# should have only mask
	i = split(range, ip, /\//)
	if (i > 2) {
		printf "(error \"%s: %s\")", N_("Invalid mask"), range
		return 0
	}
	# mask is in 0..32
	if (i > 1 && (ip[2] !~ /^[[:digit:]]+$/ || ip[2] < 0 || ip[2] > 32)) {
		printf "(error \"%s: %s\")", N_("Invalid mask"), ip[2]
		return 0
	}
	# give no mask for hostname or 32
	if (ip[1] ~ /[[:alpha:]]/) {
		if (i > 1 && ip[2] < 32) {
			printf "(error \"%s: %s\")", N_("Invalid mask"), ip[2]
			return 0
		}
		if (ip[1] ~ /^[[:alnum:].-]+$/) {
			return 1
		}
		printf "(error \"%s: %s\")", N_("Invalid hostname"), ip[1]
		return 0
	}
	# should have four bytes in IP
	if (split(ip[1], ip, /\./) != 4) {
		printf "(error \"%s: %s\")", N_("Invalid IP"), range
		return 0
	}
	# check byte bounds
	for (i=1; i<=4; i++)
		# how to replace "[0-9][0-9]?[0-9]?" with "[0-9]{1,3}" ?
		if (ip[i] !~ /^[0-9][0-9]?[0-9]?$/ || ip[i] < 0 || ip[i] > 255) {
			printf "(error \"%s: %s\")", N_("Invalid IP"), range
			return 0
		}
	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":
			print "("
			switch (params["type"]) {
				case "listen":
					printf " port (required #t match \"^[[:digit:]]+$\" label \"%s\")", N_("Port")
					printf " host (ipv4-address #t label \"%s\")", N_("Host")
					break
				default:
					printf " port (label \"%s\")", N_("Port")
					printf " host (label \"%s\")", N_("Host")
					printf " range (label \"%s\")", N_("Range")
					printf " domain (match \"^\\\\.\" label \"%s\")", N_("Domain")
					printf " chkconfig (label \"%s\")", N_("start, stop or restart service")
					printf " pattern (hostname #t label \"%s\")", N_("Site")
					printf " passwd1  (equal passwd2 label \"%s\")", N_("Cache manager password")
			}
			print ")"
			break
		case "list":
			print "("
			switch (params["_objects"]) {
				case "listen":
					while ((error = getline line <SQUID_CONF) > 0) {
					    if (match(line, /^http_port[[:space:]]+(.*)$/, port) == 0)
						    continue
					    else
						if (split(port[1], list, /:/) > 1)
							printf "(\"%s=%s\" host \"%s\" port \"%s\")", list[1], list[2], list[1], list[2]
						else
							printf "(\"=%s\" host \"\" port \"%s\")", list[1], list[1]
					}
					if (error == -1)
						debug("Error reading file '" SQUID_CONF "'", ERRNO)
					close(SQUID_CONF)
					break
				case "ournetworks":
					while ((error = getline line <SQUID_NETS) > 0)
						printf "(\"%s\" range \"%s\")", line, line
					if (error == -1)
						debug("Error reading file '" SQUID_NETS "'", ERRNO)
					close(SQUID_NETS)
					break
				case "blacklist":
					while ((error = getline line <SQUID_BLACKLIST) > 0)
						printf "(\"%s\" pattern \"%s\")", line, line
					if (error == -1)
						debug("Error reading file '" SQUID_BLACKLIST "'", ERRNO)
					close(SQUID_BLACKLIST)
					break
			}
			print ")"
			break
		case "read":
			print "("
			printf " cachemgr \"%s\"", squid_conf_read("cache_mgr")
			printf " domain \"%s\"", squid_conf_read("append_domain")
			print ")"
			break
		case "write":
			if (params["domain"])
				squid_conf_write("append_domain", params["domain"])
			if (params["cachemgr"])
				squid_conf_write("cache_mgr", params["cachemgr"])
			if (params["passwd1"])
				squid_conf_write("cachemgr_passwd", params["passwd1"] " all")
			system(SERVICE " squid reload &> /dev/null")
			print "()"
			break
		case "new":
			switch (params["type"]) {
			    case "ournetworks":
			    	if (validate_range(params["range"]))
				    if (grep("^[[:space:]]*" params["range"] "([[:space:]]|$)", SQUID_NETS))
				    	printf "(error \"%s\")", N_("same network is already defined")
				    else {
					print params["range"] >> SQUID_NETS
				    	fflush(SQUID_NETS)
					# check if SQUID_NETS file is accepted
					if (!grep("^[[:space:]]*acl our_networks src \"" SQUID_NETS "\"", SQUID_CONF))
						squid_conf_replace("^#[[:space:]]*acl our_networks src.*$", "acl our_networks src \"" SQUID_NETS "\"")
					if (!grep("^[[:space:]]*http_access allow our_networks", SQUID_CONF))
						squid_conf_replace("^#[[:space:]]*http_access allow our_networks.*$", "http_access allow our_networks")
					system(SERVICE " squid reload &> /dev/null")
					print "()"
				    }
				break
			    case "blacklist":
			    	if (grep("^" params["pattern"] "$", SQUID_BLACKLIST))
					printf "(error \"%s\")", N_("same site is already specified")
				else {
					print params["pattern"] >> SQUID_BLACKLIST
					fflush(SQUID_BLACKLIST)
					# check if blacklist is blocked
					if (!grep("^[[:space:]]*acl blacklist url_regex \"" SQUID_BLACKLIST "\"", SQUID_CONF))
						squid_conf_replace("^(# INSERT YOUR OWN RULE.*)$", "\\1\nacl blacklist url_regex \"" SQUID_BLACKLIST "\"\nhttp_access deny blacklist")
					system(SERVICE " squid reload &> /dev/null")
					print "()"
				    }
				break
			    case "listen":
				if (params["host"])
					pair = params["host"] ":" params["port"]
				else
					pair = params["port"]
				if (grep("^http_port[[:space:]]+" pair "([[:space:]]|$)", SQUID_CONF))
					printf "(error \"%s\")", N_("same ip:port is already defined")
				else {
					print "http_port " pair >> SQUID_CONF
					system(SERVICE " squid reload &> /dev/null")
					print "()"
				}
				break
			}
			break
		case "delete":
			switch (params["_objects"]) {
				case /^listen\y/:
					match(params["_objects"], /[^/]+\/(.*)=(.*)$/, list)
					if (list[1])
						delete_regex(SQUID_CONF, "^http_port[[:space:]]+" list[1] ":" list[2] "\\y$")
					else
						delete_regex(SQUID_CONF, "^http_port[[:space:]]+" list[2] "\\y$")
					break
				case /^ournetworks\y/:
					match(params["_objects"], /[^/]+\/(.*)$/, list)
					delete_regex(SQUID_NETS, "^[[:space:]]*" list[1] "\\y$")
					break
				case /^blacklist\y/:
					match(params["_objects"], /[^/]+\/(.*)$/, list)
					delete_regex(SQUID_BLACKLIST, "^[[:space:]]*" list[1] "\\y$")
					break
			}
			system(SERVICE " squid reload &> /dev/null")
			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
	params[attribute] = gensub(/([^\\])\\n/, "\\1\n", "g", value)
}

