#!/usr/bin/python
# Generate DNS and mail config files from /hosts.
# Adam Sampson <ats@offog.org>

import os, stat, sys

webserver = "212.67.207.13"
default_mx = [webserver]
default_ns = ["a.ns.us-lot.org"]
our_dns_address = webserver
known_our_mx = default_mx + ["a.mx.us-lot.org", "pils1.us-lot.org"]
local_mail_domains = ["pils", "pils.us-lot.org", "localhost", "localhost.us-lot.org"]
virtual_mail_domains = [
	"firebrick.azz.us-lot.org",
	"cartman.azz.us-lot.org",
	"tim.josstix.org",
	"roz.josstix.org",
	"amber.josstix.org",
	"adam.josstix.org",
	"lists.us-lot.org",
	"a.ns.us-lot.org",
	]

def trace(*l):
	return
	print >>sys.stderr, "TRACE: " + " ".join(map(str, l))

def inside_domain(outer, inner):
	o = outer.split(".")
	i = inner.split(".")
	return o[-len(i):] == i

def is_address(s):
	return s[0] in "0123456789"

def print_ns_list(op, dom, l):
	for i in range(len(l)):
		if i == 0:
			s = "."
		else:
			s = "&"
		s += dom + ":"
		a = l[i]
		if is_address(a):
			s += a + ":" + "abcdefgh"[i]
		else:
			s += ":" + a
		op.append(s)

def print_alias(op, mds, name, ws, mxs):
	op.append("+" + name + ":" + ws)
	s = "@" + name + ":"
	is_ours = 0
	for i in range(len(mxs)):
		mx = mxs[i]
		if is_address(mx):
			mname = "abcdefgh"[i]
			op.append(s + mx + ":" + mname + ":" + str(i))
			# If we use this MX again, use it by the first name we made up.
			mxs[i] = mname + ".mx." + name
		else:
			op.append(s + ":" + mx + ":" + str(i))
		if mx in known_our_mx:
			is_ours = 1
	if is_ours:
		mds.append(name)

def read_file(name):
	try:
		f = open(name)
		ls = [l.strip() for l in f.readlines()]
		f.close()
		return [l for l in ls if l != "" and l[0] != "#"]
	except IOError:
		return None

def list_hosts():
	ds = os.listdir(".")
	return [d for d in ds if stat.S_ISDIR(os.stat(d)[0])]

def main():
	hosts = map(lambda a: a.split("."), list_hosts())
	hosts.sort(lambda a, b: cmp(len(a), len(b)))

	alias_hosts = {}
	seen = {}
	for host in hosts:
		hn = ".".join(host)
		alias_hosts[hn] = []
		as = read_file(hn + "/aliases")
		if as is not None:
			for alias in as:
				ahost = alias.split(".")
				parent = None
				for i in range(len(ahost), 0, -1):
					phn = ".".join(ahost[-i:])
					if seen.has_key(phn):
						parent = phn
				if parent is not None:
					alias_hosts[hn].append(alias)
				else:
					print "Not creating DNS for alias host not in our domains:", alias
		seen[hn] = 1

	dns_output = []
	mail_domains = []
	auth_for = []

	seen = {}
	domain_mx = {}
	for host in hosts:
		hn = ".".join(host)
		trace("Processing", hn)

		parent = None
		for i in range(len(host), 0, -1):
			phn = ".".join(host[-i:])
			if seen.has_key(phn):
				parent = phn
				break

		if parent is None:
			trace("Domain has no parent!")
			ns = read_file(hn + "/nameservers")
			if ns is None:
				ns = default_ns
			print_ns_list(dns_output, hn, ns)
			# This might not necessarily be true in the future...
			auth_for.append(hn)
		else:
			trace("Parent is", parent)

		mx = read_file(hn + "/mxs")
		if mx is None and domain_mx.has_key(parent):
			mx = domain_mx[parent]
		if mx is None:
			mx = default_mx + []
		print_alias(dns_output, mail_domains, hn, webserver, mx)
		print_alias(dns_output, mail_domains, "www." + hn, webserver, mx)
		for ah in alias_hosts[hn]:
			print_alias(dns_output, mail_domains, ah, webserver, mx)

		if parent is None:
			domain_mx[hn] = mx

		seen[hn] = 1

	trace("Writing data.hosts")
	f = open("/service/tinydns/root/data.hosts", "w")
	print >>f, "\n".join(dns_output)
	f.close()

	trace("Writing dnscache config")
	for hn in auth_for:
		f = open("/service/dnscache/root/servers/" + hn, "w")
		print >>f, our_dns_address
		f.close()

	mail_domains += virtual_mail_domains

	trace("Writing rcpthosts")
	f = open("/var/qmail/control/rcpthosts", "w")
	print >>f, "\n".join(mail_domains + local_mail_domains)
	f.close()

	trace("Writing virtualdomains")
	f = open("/var/qmail/control/virtualdomains", "w")
	for dom in mail_domains:
		if dom.startswith("lists."):
			print >>f, dom + ":mailman"
		else:
			print >>f, dom + ":alias"
	f.close()

if __name__ == "__main__":
	main()


