#!/usr/bin/perl
package IkiWiki::Hosting;

no lib '.';
use warnings;
use strict;
use IkiWiki;
use IkiWiki::Hosting;

sub meta_createkey {
	required => [qw{client-hostname}],
	options => [qw{}],
	description => "creates key for a client, and outputs the key filenames",
	section => "primary",
}
sub createkey {
	my $client=shift;

	assert_root();
	assert_client_haskey($client, 0);

	shell("mkdir", "-p", "$config{keydir}/dns");
	shell("chmod", "700", $config{keydir});
	chdir "$config{keydir}/dns" || die "chdir $config{keydir}/dns: $!";
	my $keyid=`dnssec-keygen -b 512 -a HMAC-MD5 -n HOST "$client."`;
	chomp $keyid;
	if (! length $keyid) {
		error "dnssec-keygen failed";
	}
	setupbind();

	return "$config{keydir}/dns/$keyid.key", "$config{keydir}/dns/$keyid.private";
}

sub meta_deletekey {
	required => [qw{client-hostname}],
	options => [qw{}],
	description => "removes key files for a client",
	section => "primary",
}
sub deletekey {
	my $client=shift;

	assert_root();
	assert_client_haskey($client, 1);

	my @keys=(dns_private_keys($client), dns_keys($client));
	shell("rm", "-f", @keys);
	setupbind();
}

sub meta_setupbind {
	required => [qw{}],
	options => [qw{}],
	description => "generates bind config files that allow updates using keys",
	section => "utility",
}
sub setupbind {
	assert_root();

	# use template to generate named.conf file
	my $named_conf="/etc/bind/ikisite.conf";
	my @keys = map { {
		key => dns_keyid($_),
		secret => dns_keysecret($_),
	} } dns_private_keys();

	my @domains;
	foreach my $domain (split ' ', $config{domains}) {
		my $basedir="/etc/bind/$domain";
		my $zonefile="$basedir/zonefile";

		shell("mkdir", "-p", $basedir);

		# bind needs permission to write to the basedir
		shell("chown", "root:bind", $basedir);
		shell("chmod", "770", $basedir);

		if (! -e $zonefile) {
			# use template to generate stub zone file
			outtemplate($zonefile, "zonefile.tmpl",
				domain => $domain,
			);
		}

		push @domains, {
			domain => $domain,
			zonefile => $zonefile,
			keys => \@keys,
		};
	}
	my $template=IkiWiki::Hosting::ikisite_template("named.conf.tmpl");
	$template->param(
		keys => \@keys,
		dns_domains => \@domains,
	);
	open(OUT, ">", $named_conf) || error "$named_conf: $!";
	# contains keys, so needs to be only readable by bind
	shell("chown", "root:bind", $named_conf);
	shell("chmod", "640", $named_conf);
	print OUT $template->output;
	close OUT;
	
	# add include to named.conf.local
	my $named_conf_comment="// automatically added by ikidns";
	open(NAMED_CONF_IN, "<", "/etc/bind/named.conf.local") || error("named.conf.local: $!");
	open(NAMED_CONF_OUT, ">", "/etc/bind/named.conf.local.tmp") || error("named.conf.local.tmp: $!");
	while (<NAMED_CONF_IN>) {
		chomp;
		print NAMED_CONF_OUT "$_\n"
			unless /^include\s+\".*\";\s*\Q$named_conf_comment\E/;
	}
	close NAMED_CONF_IN;
	print NAMED_CONF_OUT "include \"$named_conf\"; $named_conf_comment\n";
	close NAMED_CONF_OUT || error "close named.conf.tmp: $!";
	shell("mv", "-f", "/etc/bind/named.conf.local.tmp", "/etc/bind/named.conf.local");

	shell("/etc/init.d/bind9", "restart");
}

#############################################################################

sub assert_client_haskey {
	my $client=shift;
	my $true=shift;

	my @keys=(dns_private_keys($client), dns_keys($client));
	if (! $true && @keys) {
		error("dns key already exists for $client");
	}
	elsif ($true && ! @keys) {
		error("dns key does not exist for $client");
	}
}

sub dns_private_keys {
	my $client=shift || "*";

	my @list=glob("$config{keydir}/dns/K$client.+*.private");
	return @list;
}

sub dns_keys {
	my $client=shift || "*";

	my @list=glob("$config{keydir}/dns/K$client.+*.key");
	return @list;
}

sub dns_keyid {
	my $keyfile=shift;

	$keyfile=IkiWiki::basename($keyfile);
	$keyfile=~s/^K//;
	$keyfile=~s/\+.*//;
	return $keyfile;
}

sub dns_keysecret {
	my $keyfile=shift;

	my $file=readfile($keyfile);
	my ($key)=$file=~/^Key:\s+(.*)$/m;
	return $key;
}

main();
