Index: unk/DNSDB.pm
===================================================================
--- /trunk/DNSDB.pm	(revision 3)
+++ 	(revision )
@@ -1,484 +1,0 @@
-# dns/trunk/DNSDB.pm
-# Abstraction functions for DNS administration
-###
-# SVN revision info
-# $Date$
-# SVN revision $Rev$
-# Last update by $Author$
-###
-# Copyright (C) 2008 - Kris Deugau <kdeugau@deepnet.cx>
-
-package DNSDB;
-
-use strict;
-use warnings;
-use Exporter;
-use DBI;
-#use Net::SMTP;
-#use NetAddr::IP qw( Compact );
-#use POSIX;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
-
-$VERSION	= 0.1;
-@ISA		= qw(Exporter);
-@EXPORT_OK	= qw(
-	&initGlobals &connectDB &finish &addDomain &delDomain &domainName &getSOA &getRecLine &getDomRecs
-	&addRec &delRec &domStatus
-	%typemap %reverse_typemap
-	);
-
-@EXPORT		= (); # Export nothing by default.
-%EXPORT_TAGS	= ( ALL => [qw(
-		&initGlobals &connectDB &finish &addDomain &delDomain &domainName &getSOA &getRecLine &getDomRecs
-		&addRec &delRec &domStatus
-		%typemap %reverse_typemap
-		)]
-	);
-
-our $group = 1;
-our $errstr = '';
-
-# Halfway sane defaults for SOA, TTL, etc.
-our %def = qw (
-	contact	hostmaster.DOMAIN
-	prins	ns1.myserver.com
-	soattl	86400
-	refresh	10800
-	retry	3600
-	expire	604800
-	minttl	10800
-	ttl	10800
-);
-
-# DNS record type map and reverse map.
-# loaded from the database, from http://www.iana.org/assignments/dns-parameters
-our %typemap;
-our %reverse_typemap;
-
-##
-## Initialization and cleanup subs
-##
-
-## DNSDB::connectDB()
-# Creates connection to DNS database.
-# Requires the database name, username, and password.
-# Returns a handle to the db.
-# Set up for a PostgreSQL db;  could be any transactional DBMS with the
-# right changes.
-sub connectDB {
-  $errstr = '';
-  my ($dbname,$user,$pass) = @_;
-  my $dbh;
-  my $DSN = "DBI:Pg:dbname=$dbname";
-
-  my $host = shift;
-  $DSN .= ";host=$host" if $host;
-
-# Note that we want to autocommit by default, and we will turn it off locally as necessary.
-# We may not want to print gobbledygook errors;  YMMV.  Have to ponder that further.
-  $dbh = DBI->connect($DSN, $user, $pass, {
-	AutoCommit => 1,
-	PrintError => 0
-	})
-    or return (undef, $DBI::errstr) if(!$dbh);
-
-# Return here if we can't select.  Note that this indicates a
-# problem executing the select.
-  my $sth = $dbh->prepare("select group_id from groups limit 1");
-  $sth->execute();
-  return (undef,$DBI::errstr) if ($sth->err);
-
-# See if the select returned anything (or null data).  This should
-# succeed if the select executed, but...
-  $sth->fetchrow();
-  return (undef,$DBI::errstr)  if ($sth->err);
-
-  $sth->finish;
-
-# If we get here, we should be OK.
-  return ($dbh,"DB connection OK");
-} # end connectDB
-
-
-## DNSDB::finish()
-# Cleans up after database handles and so on.
-# Requires a database handle
-sub finish {
-  my $dbh = $_[0];
-  $dbh->disconnect;
-} # end finish
-
-
-## DNSDB::initGlobals()
-# Initialize global variables
-# NB: this does NOT include web-specific session variables!
-# Requires a database handle
-sub initGlobals {
-  my $dbh = shift;
-
-# load system-wide site defaults and things from config file
-  open SYSDEFAULTS, "</etc/dnsdb.conf";
-##fixme - error check!
-  while (<SYSDEFAULTS>) {
-    next if /^\s*#/;
-    $def{contact}	= $1 if /contact ?= ?([a-z0-9_.-]+)/i;
-    $def{prins}		= $1 if /prins ?= ?([a-z0-9_.-]+)/i;
-    $def{soattl}	= $1 if /soattl ?= ?([a-z0-9_.-]+)/i;
-    $def{refresh}	= $1 if /refresh ?= ?([a-z0-9_.-]+)/i;
-    $def{retry}		= $1 if /retry ?= ?([a-z0-9_.-]+)/i;
-    $def{expire}	= $1 if /expire ?= ?([a-z0-9_.-]+)/i;
-    $def{minttl}	= $1 if /minttl ?= ?([a-z0-9_.-]+)/i;
-    $def{ttl}		= $1 if /ttl ?= ?([a-z0-9_.-]+)/i;
-##fixme?  load DB user/pass from config file?
-  }
-# load from database
-  my $sth = $dbh->prepare("select val,name from rectypes");
-  $sth->execute;
-  while (my ($recval,$recname) = $sth->fetchrow_array()) {
-    $typemap{$recval} = $recname;
-    $reverse_typemap{$recname} = $recval;
-  }
-} # end initGlobals
-
-
-##
-## Processing subs
-##
-
-## DNSDB::addDomain()
-# Add a domain
-# Takes a database handle, domain name, numeric group, and boolean(ish) state (active/inactive)
-# Returns a status code and message
-sub addDomain {
-  $errstr = '';
-  my $dbh = shift;
-  return ('FAIL',"Need database handle") if !$dbh;
-  my $domain = shift;
-  return ('FAIL',"Need domain") if !defined($domain);
-  my $group = shift;
-  return ('FAIL',"Need group") if !defined($group);
-  my $state = shift;
-  return ('FAIL',"Need domain status") if !defined($state);
-
-  my $dom_id;
-
-  # Allow transactions, and raise an exception on errors so we can catch it later.
-  # Use local to make sure these get "reset" properly on exiting this block
-  local $dbh->{AutoCommit} = 0;
-  local $dbh->{RaiseError} = 1;
-
-  # Wrap all the SQL in a transaction
-  eval {
-    # insert the domain...
-    my $sth = $dbh->prepare("insert into domains (domain,group_id,status) values (?,?,?)");
-    $sth->execute($domain,$group,$state);
-
-    # get the ID...
-    $sth = $dbh->prepare("select domain_id from domains where domain='$domain'");
-    $sth->execute;
-    ($dom_id) = $sth->fetchrow_array();
-
-    # ... and now we construct the standard records from the default set.  NB:  group should be variable.
-    $sth = $dbh->prepare("select host,type,val,distance,weight,port,ttl from default_records where group_id=$group");
-    my $sth_in = $dbh->prepare("insert into records (domain_id,host,type,val,distance,weight,port,ttl)".
-	" values ($dom_id,?,?,?,?,?,?,?)");
-    $sth->execute;
-    while (my ($host,$type,$val,$dist,$weight,$port,$ttl) = $sth->fetchrow_array()) {
-      $host =~ s/DOMAIN/$domain/g;
-      $sth_in->execute($host,$type,$val,$dist,$weight,$port,$ttl);
-    }
-
-    # once we get here, we should have suceeded.
-    $dbh->commit;
-  }; # end eval
-
-  if ($@) {
-    my $msg = $@;
-    eval { $dbh->rollback; };
-    return ('FAIL',$msg);
-  } else {
-    return ('OK',$dom_id);
-  }
-} # end addDomain
-
-
-## DNSDB::delDomain()
-# Delete a domain.
-# for now, just delete the records, then the domain.
-# later we may want to archive it in some way instead (status code 2, for example?)
-sub delDomain {
-  my $dbh = shift;
-  my $domain = shift;
-
-  # Allow transactions, and raise an exception on errors so we can catch it later.
-  # Use local to make sure these get "reset" properly on exiting this block
-  local $dbh->{AutoCommit} = 0;
-  local $dbh->{RaiseError} = 1;
-
-  # Wrap all the SQL in a transaction
-  eval {
-    # brute force.
-    my $sth = $dbh->prepare("select domain_id from domains where domain=?");
-    $sth->execute($domain);
-    die "Domain not found, can't delete\n" if $sth->rows < 1;
-    my ($id) = $sth->fetchrow_array;
-
-    $sth = $dbh->prepare("delete from records where domain_id=$id");
-    $sth->execute;
-    $sth = $dbh->prepare("delete from domains where domain=?");
-    $sth->execute($domain);
-
-    # once we get here, we should have suceeded.
-    $dbh->commit;
-  }; # end eval
-
-  if ($@) {
-    my $msg = $@;
-    eval { $dbh->rollback; };
-    return ('FAIL',$msg);
-  } else {
-    return ('OK','OK');
-  }
-
-} # end delDomain()
-
-
-## DNSDB::domainName()
-# Return the domain name based on a domain ID
-# Takes a database handle and the domain ID
-# Returns the domain name or undef on failure
-sub domainName {
-  $errstr = '';
-  my $dbh = shift;
-  my $domid = shift;
-  my $sth = $dbh->prepare("select domain from domains where domain_id=?");
-  $sth->execute($domid);
-  my ($domname) = $sth->fetchrow_array();
-  $errstr = $DBI::errstr if !$domname;
-  return $domname if $domname;
-} # end domainName
-
-
-## DNSDB::editRecord()
-# Change an existing record
-# Takes a database handle, default/live flag, record ID, and new data and updates the data fields for it
-sub editRecord {
-  $errstr = '';
-  my $dbh = shift;
-  my $defflag = shift;
-  my $recid = shift;
-  my $host = shift;
-  my $address = shift;
-  my $distance = shift;
-  my $weight = shift;
-  my $port = shift;
-  my $ttl = shift;
-}
-
-
-## DNSDB::getSOA()
-# Return all suitable fields from an SOA record in separate elements of a hash
-# Takes a database handle, default/live flag, and group (default) or domain (live) ID
-sub getSOA {
-  $errstr = '';
-  my $dbh = shift;
-  my $def = shift;
-  my $id = shift;
-  my %ret;
-
-  my $sql = "select record_id,host,val,ttl from";
-  if ($def eq 'def' or $def eq 'y') {
-    $sql .= " default_records where group_id=$id and type=$reverse_typemap{SOA}";
-  } else {
-    # we're editing a live SOA record;  find based on domain
-    $sql .= " records where domain_id=$id and type=$reverse_typemap{SOA}";
-  }
-#print "getSOA DEBUG: $sql<br>\n";
-  my $sth = $dbh->prepare($sql);
-  $sth->execute;
-
-  my ($recid,$host,$val,$ttl) = $sth->fetchrow_array();
-  my ($prins,$contact) = split /:/, $host;
-  my ($refresh,$retry,$expire,$minttl) = split /:/, $val;
-
-  $ret{recid}	= $recid;
-  $ret{ttl}	= $ttl;
-  $ret{prins}	= $prins;
-  $ret{contact}	= $contact;
-  $ret{refresh}	= $refresh;
-  $ret{retry}	= $retry;
-  $ret{expire}	= $expire;
-  $ret{minttl}	= $minttl;
-
-  return %ret;
-} # end getSOA()
-
-
-## DNSDB::getRecLine()
-# Return all data fields for a zone record in separate elements of a hash
-# Takes a database handle, default/live flag, and record ID
-sub getRecLine {
-  $errstr = '';
-  my $dbh = shift;
-  my $def = shift;
-  my $id = shift;
-
-  my $sql = "select record_id,host,type,val,distance,weight,port,ttl from ".
-	(($def eq 'def' or $def eq 'y') ? 'default_' : '').
-	"records where record_id=$id";
-print "MDEBUG: $sql<br>\n";
-  my $sth = $dbh->prepare($sql);
-  $sth->execute;
-
-  my ($recid,$host,$rtype,$val,$distance,$weight,$port,$ttl) = $sth->fetchrow_array();
-
-  if ($sth->err) {
-    $errstr = $DBI::errstr;
-    return undef;
-  }
-  my %ret;
-  $ret{recid}	= $recid;
-  $ret{host}	= $host;
-  $ret{type}	= $rtype;
-  $ret{val}	= $val;
-  $ret{distance}= $distance;
-  $ret{weight}	= $weight;
-  $ret{port}	= $port;
-  $ret{ttl}	= $ttl;
-
-  return %ret;
-}
-
-
-##fixme: should use above (getRecLine()) to get lines for below?
-## DNSDB::getDomRecs()
-# Return records for a domain
-# Takes a database handle, default/live flag, group/domain ID, start,
-# number of records, sort field, and sort order
-# Returns a reference to an array of hashes
-sub getDomRecs {
-  $errstr = '';
-  my $dbh = shift;
-  my $type = shift;
-  my $id = shift;
-
-  my $sql = "select record_id,host,type,val,distance,weight,port,ttl from";
-  if ($type eq 'def' or $type eq 'y') {
-    $sql .= " default_records where group_id=$id";
-  } else {
-    $sql .= " records where domain_id=$id";
-  }
-  $sql .= " and not type=$reverse_typemap{SOA}";
-  my $sth = $dbh->prepare($sql);
-  $sth->execute;
-
-  my @retbase;
-  while (my $ref = $sth->fetchrow_hashref()) {
-    push @retbase, $ref;
-  }
-
-  my $ret = \@retbase;
-  return $ret;
-} # end getDomRecs()
-
-
-## DNSDB::addRec()
-# Add a new record to a domain or a group's default records
-# Takes a database handle, default/live flag, group/domain ID,
-# host, type, value, and TTL
-# Some types require additional detail: "distance" for MX and SRV,
-# and weight/port for SRV
-# Returns a status code and detail message in case of error
-sub addRec {
-  $errstr = '';
-  my $dbh = shift;
-  my $defrec = shift;
-  my $id = shift;
-
-  my $host = shift;
-  my $rectype = shift;
-  my $val = shift;
-  my $ttl = shift;
-
-  my $fields = ($defrec eq 'y' ? 'group_id' : 'domain_id').",host,type,val,ttl";
-  my $vallist = "$id,'$host',$rectype,'$val',$ttl";
-
-  my $dist;
-  if ($rectype == $reverse_typemap{MX} or $rectype == $reverse_typemap{SRV}) {
-    $dist = shift;
-    return ('FAIL',"Need distance for $typemap{$rectype} record") if !defined($dist);
-    $fields .= ",distance";
-    $vallist .= ",$dist";
-  }
-  my $weight;
-  my $port;
-  if ($rectype == $reverse_typemap{SRV}) {
-    $weight = shift;
-    $port = shift;
-    return ('FAIL',"Need weight and port for SRV record") if !defined($weight) or !defined($port);
-    $fields .= ",weight,port";
-    $vallist .= ",$weight,$port";
-  }
-
-  my $sql = "insert into ".($defrec eq 'y' ? 'default_' : '')."records ($fields) values ($vallist)";
-# something is bugging me about this...
-print "DEBUG: $sql<br>\n";
-  my $sth = $dbh->prepare($sql);
-  $sth->execute;
-
-  return ('FAIL',$sth->errstr) if $sth->err;
-
-  return ('OK','OK');
-} # end addRec()
-
-
-## DNSDB::delRec()
-# Delete a record.  
-sub delRec {
-  $errstr = '';
-  my $dbh = shift;
-  my $defrec = shift;
-  my $id = shift;
-
-  my $sth = $dbh->prepare("delete from ".($defrec eq 'y' ? 'default_' : '')."records where record_id=?");
-  $sth->execute($id);
-
-  return ('FAIL',$sth->errstr) if $sth->err;
-
-  return ('OK','OK');
-} # end delRec()
-
-
-## DNSDB::domStatus()
-# Sets and/or returns a domain's status
-# Takes a database handle, domain ID and optionally a status argument
-# Returns undef on errors.
-sub domStatus {
-  my $dbh = shift;
-  my $id = shift;
-  my $newstatus = shift;
-
-  return undef if $id !~ /^\d+$/;
-
-  my $sth;
-
-# ooo, fun!  let's see what we were passed for status
-  if ($newstatus) {
-    $sth = $dbh->prepare("update domains set status=? where domain_id=?");
-    # ass-u-me caller knows what's going on in full
-    if ($newstatus =~ /^[01]$/) {	# only two valid for now.
-      $sth->execute($newstatus,$id);
-    } elsif ($newstatus =~ /^domo(?:n|ff)$/) {
-      $sth->execute(($newstatus eq 'domon' ? 1 : 0),$id);
-    }
-  }
-
-  $sth = $dbh->prepare("select status from domains where domain_id=?");
-  $sth->execute($id);
-  my ($status) = $sth->fetchrow_array;
-  return $status;
-} # end domStatus()
-
-
-# shut Perl up
-1;
Index: unk/dns.cgi
===================================================================
--- /trunk/dns.cgi	(revision 3)
+++ 	(revision )
@@ -1,438 +1,0 @@
-#!/usr/bin/perl -w -T
-# dns/cgi-bin/dns.cgi
-###
-# SVN revision info
-# $Date$
-# SVN revision $Rev$
-# Last update by $Author$
-###
-# Copyright (C) 2008,2009 - Kris Deugau <kdeugau@deepnet.cx>
-
-use strict;
-use warnings;
-
-use CGI::Carp qw (fatalsToBrowser);
-use CGI::Simple;
-use HTML::Template;
-use CGI::Session;
-use DBI;
-
-use lib '.';
-# custom modules
-use DNSDB qw(:ALL);
-
-# Let's do these templates right...
-my $templatedir = "templates";
-my $sessiondir = "session";
-
-# Set up the CGI object...
-my $q = new CGI::Simple;
-# ... and get query-string params as well as POST params if necessary
-$q->parse_query_string;
-
-my %webvar;
-# This is probably excessive fiddling, but it puts the parameters somewhere my fingers know about...
-foreach ($q->param()) {
-  $webvar{$_} = $q->param($_);
-}
-
-my $sid = ($webvar{sid} ? $webvar{sid} : undef);
-my $session = new CGI::Session("driver:File", $sid, {Directory => $sessiondir});
-#$sid = $session->id() if !$sid;
-if (!$sid) {
-  # init stuff.  can probably axe this down to just above if'n'when user manipulation happens
-  $sid = $session->id();
-# need to know the "upper" group the user can deal with;  may as well
-# stick this in the session rather than calling out to the DB every time.
-  $session->param('logingroupid',1);
-  $session->param('workinggroupid',1);	# yes, we *do* need to track this too.  er, probably.
-}
-
-# handle login redirect
-if ($webvar{action} && $webvar{action} eq 'login') {
-  # handle user check
-  my $newurl = "http://$ENV{HTTP_HOST}$ENV{REQUEST_URI}?sid=$sid&page=domlist";
-  print "Status: 302\nLocation: $newurl\n\n";
-  exit;
-}
-
-my $header = HTML::Template->new(filename => "$templatedir/header.tmpl");
-my $footer = HTML::Template->new(filename => "$templatedir/footer.tmpl");
-
-print "Content-type: text/html\n\n", $header->output;
-
-# default
-#my $perpage = 15;
-my $perpage = 3;
-my $offset = ($webvar{offset} ? $webvar{offset} : 0);
-
-# NB:  these must match the field name and SQL ascend/descend syntax respectively
-my $sortfield = "domains";
-my $sortorder = "asc";
-
-my ($dbh,$msg) = connectDB("dnsdb","dnsdb","secret");
-#my $dbh = DBI->connect("DBI:mysql:database=vegadns","vegadns","secret",
-#	{ AutoCommit => 0 }) or die $DBI::errstr;
-
-##fixme.  PLEASE!  <G>
-print $msg if !$dbh;
-
-# fiddle hardcoded "defaults" as per system/user (?) prefs
-initGlobals($dbh);
-
-# Default page is a login page
-my $page;	# to be initialized as an HTML::Template entity sooner or later
-
-
-
-# decide which page to spit out...
-if (!$webvar{page}) {
-  $page = HTML::Template->new(filename => "$templatedir/login.tmpl");
-} else {
-  $page = HTML::Template->new(filename => "$templatedir/$webvar{page}.tmpl");
-}
-
-$page->param(sid => $sid);
-
-if ($webvar{page} eq 'domlist' or $webvar{page} eq 'index') {
-
-# hmm.  seeing problems in some possibly-not-so-corner cases.
-  if (defined($webvar{action})) {
-    domStatus($dbh,$webvar{id},$webvar{action});
-  }
-
-  my $sth = $dbh->prepare("select count(*) from domains");
-  $sth->execute;
-  my ($count) = ($sth->fetchrow_array);
-
-##fixme
-  if ($offset eq 'all') {
-    print "foo!  wanna see'em all\n";
-  } else {
-    # all these bits only have sensible behaviour if offset is numeric. err, probably.
-    if ($count > $perpage) {
-      # if there are more results than the default, always show the "all" link
-      $page->param(navall => 1);
-
-      if ($offset > 0) {
-        $page->param(navfirst => 1);
-        $page->param(navprev => 1);
-        $page->param(prevoffs => $offset-1);
-      }
-
-      # show "next" and "last" links if we're not on the last page of results
-      if ( (($offset+1) * $perpage - $count) < 0 ) {
-        $page->param(navnext => 1);
-        $page->param(nextoffs => $offset+1);
-        $page->param(navlast => 1);
-        $page->param(lastoffs => int $count/$perpage);
-      }
-    }
-  }
-
-  $page->param(ndomains => $count);
-  $page->param(nstart => (($offset eq 'all' ? 0 : $offset)*$perpage+1));
-  $page->param(npglast => ($offset eq 'all' ? $count :
-	( (($offset+1)*$perpage) > $count ? $count : (($offset+1)*$perpage) )
-	));
-  my @domlist;
-  $sth = $dbh->prepare("select domain_id,domain,status,groups.name from domains".
-	" inner join groups on domains.group_id=groups.group_id".
-	" order by domain".($offset eq 'all' ? '' : " limit $perpage offset ".$offset*$perpage));
-  $sth->execute;
-  my $rownum = 0;
-  while (my @data = $sth->fetchrow_array) {
-    my %row;
-    $row{domainid} = $data[0];
-    $row{domain} = $data[1];
-    $row{status} = ($data[2] ? 'Active' : 'Inactive');
-    $row{group} = $data[3];
-    $row{bg} = ($rownum++)%2;
-#    $row{mkactive} = ($data[2] eq 'inactive' ? 1 : 0);
-    $row{mkactive} = !$data[2];
-    $row{sid} = $sid;
-    $row{offset} = $offset;
-##fixme:  need to clean up status indicator/usage/inversion
-    push @domlist, \%row;
-  }
-  $page->param(domtable => \@domlist);
-
-} elsif ($webvar{page} eq 'reclist') {
-
-#} elsif ($webvar{page} eq 'domdetail') {
-
-  $page->param(defrec => $webvar{defrec});
-
-##fixme
-  $page->param(domain => domainName($dbh,$webvar{id}));
-  $page->param(grpid => 1);
-##fixme
-
-  if ($webvar{defrec} eq 'y') {
-    $page->param(id => $webvar{grpid});	##fixme, hack! hack!
-    showdomain('y',1);
-  } else {
-    $page->param(id => $webvar{id});
-    showdomain('n',$webvar{id});
-  }
-
-} elsif ($webvar{page} eq 'defrecs') {
-
-  showdomain('def',1);
-  $page->param(defrec => 'y');
-
-  my $sth = $dbh->prepare("select * from default_records");
-  $sth->execute;
-print "<pre>\n";
-  while (my @data = $sth->fetchrow_array) {
-    foreach (@data) { print; print "\t" };
-    print "\n";
-  }
-print "</pre>\n";
-
-} elsif ($webvar{page} eq 'newdomain') {
-  # weesa gonna discard parent_group_id for now
-  my $sth = $dbh->prepare("select group_id,parent_group_id,name from groups order by group_id");
-  $sth->execute;
-  my @grplist;
-  while (my ($grpid,$pargrp,$grpname) = $sth->fetchrow_array()) {
-    my %row;
-    $row{grpname} = $grpname;
-    $row{grpval} = $grpid;
-##fixme: need magic
-#    $row{defgrp} = '';
-    push @grplist, \%row;
-  }
-
-  $page->param(grplist => \@grplist);
-
-} elsif ($webvar{page} eq 'newrec') {
-  print "whee!\n";
-
-  # populate most fields as needed.  (eg, type list.)
-  newrec();
-
-} elsif ($webvar{page} eq 'addrec') {
-  print "wanna add<br>DEBUG: $webvar{parentid}<br>\n";
-
-  my @recargs = ($dbh,$webvar{defrec},$webvar{parentid},$webvar{name},$webvar{type},$webvar{address},$webvar{ttl});
-  if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV}) {
-    push @recargs, $webvar{distance};
-    if ($webvar{type} == $reverse_typemap{SRV}) {
-      push @recargs, $webvar{weight};
-      push @recargs, $webvar{port};
-    }
-  }
-  push @recargs, 
-  my ($code,$msg) = addRec(@recargs);
-
-  if ($code eq 'OK') {
-    showdomain($webvar{defrec},$webvar{parentid});
-# NB: should **really** redirect here, in case of reload.  >_<  eyowch.
-  } else {
-    $page->param(add_failed => 1);
-    $page->param(errmsg => $msg);
-    newrec();	# populate the form... er, mostly.
-    $page->param(name => $webvar{name});
-    $page->param(address => $webvar{address});
-    $page->param(distance => $webvar{distance})
-	if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV});
-    $page->param(weight => $webvar{weight}) if $webvar{type} == $reverse_typemap{SRV};
-    $page->param(port => $webvar{port}) if $webvar{type} == $reverse_typemap{SRV};
-  }
-
-  $page->param(defrec => $webvar{defrec});
-
-} elsif ($webvar{page} eq 'conf_del') {
-
-  $page->param(id => $webvar{id});
-  $page->param(defrec => $webvar{defrec});
-
-  my @tmp = getrecdata($dbh,$webvar{id},$webvar{defrec});
-
-  
-} elsif ($webvar{page} eq 'delrec') {
-
-  $page->param(id => $webvar{id});
-  $page->param(defrec => $webvar{defrec});
-  # first pass = confirm y/n (sorta)
-  if (!defined($webvar{del})) {
-    $page->param(del_getconf => 1);
-    my %rec = getRecLine($dbh,$webvar{defrec},$webvar{id});
-    $page->param(host => $rec{host});
-    $page->param(ftype => $typemap{$rec{type}});
-    $page->param(recval => $rec{val});
-  } else {
-    my ($code,$msg) = delRec($dbh,$webvar{defrec},$webvar{id});
-    if ($code ne 'OK') {
-## need to find failure mode
-      $page->param(del_failed => 1);
-      $page->param(errmsg => $msg);
-    }
-##fixme:  group/parent instead of hardcoded 1
-    showdomain('y',1);
-  }
-
-} elsif ($webvar{page} eq 'editsoa') {
-
-  fillsoa($webvar{defrec},$webvar{recid});
-
-} elsif ($webvar{page} eq 'updatesoa') {
-  print "ooooo!\n";
-
-  my $sth;
-  my $sql = '';
-  # no domain ID, so we're editing the default SOA for a group (we don't care which one here)
-  # plus a bit of magic to update the appropriate table
-  $sql = "update ".($webvar{domainid} eq '' ? "default_records" : "records").
-	" set host='$webvar{prins}:$webvar{contact}',".
-	" val='$webvar{refresh}:$webvar{retry}:$webvar{expire}:$webvar{minttl}',".
-	" ttl=$webvar{ttl} where record_id=$webvar{recid}";
-  $sth = $dbh->prepare($sql);
-  $sth->execute;
-
-  if ($sth->err) {
-    $page->param(update_failed => 1);
-    $page->param(msg => $DBI::errstr);
-    fillsoa($webvar{defrec},1);
-  } else {
-    $page->param(update_failed => 0);
-##fixme!  need to set group ID properly here
-    showdomain('y',1);
-  }
-
-} elsif ($webvar{page} eq 'adddomain') {
-  # Need some magic here.
-
-##fixme:  Group should be variable
-  my ($code,$msg) = addDomain($dbh,$webvar{domain},1,($webvar{makeactive} eq 'on' ? 1 : 0));
-
-# hokay, a bit of magic to decide which page we hit.
-  if ($code eq 'OK') {
-    $page = HTML::Template->new(filename => "$templatedir/reclist.tmpl");
-    showdomain(0,$msg);
-##work
-  } else {
-# oooh, yeah, this is supposed to be a redirect.  er, maybe.  whee.
-    $page = HTML::Template->new(filename => "$templatedir/newdomain.tmpl");
-    $page->param(add_failed => 1);
-    $page->param(domain => $webvar{domain});
-    $page->param(errmsg => $msg);
-  }
-
-
-}
-
-
-# spit it out
-print $page->output;
-
-print "<div id=debug>webvar keys: <pre>\n";
-foreach my $key (keys %webvar) {
-  print "key: $key\tval: $webvar{$key}\n";
-}
-print "</pre>\nENV:\n<pre>\n";
-foreach my $key (keys %ENV) {
-  print "key: $key\tval: $ENV{$key}\n";
-}
-print "</pre></div>\n";
-
-print $footer->output;
-
-
-exit 0;
-
-
-sub fillsoa {
-  my $def = shift;
-  my $id = shift;
-  my $domname;
-
-  if ($webvar{domain} == 0) {
-    $domname = "DOMAIN";
-  } else {
-    my $sth = $dbh->prepare("select domain from domains where domain_id=$webvar{domain}");
-    $sth->execute();
-    ($domname) = $sth->fetchrow_array();
-  }
-
-  $page->param(domain => $domname);
-  $page->param(defrec => !$webvar{domain});
-  $page->param(group => $DNSDB::group);
-
-# defaults
-  $page->param(defcontact => $DNSDB::def{contact});
-  $page->param(defns => $DNSDB::def{prins});
-  $page->param(defsoattl => $DNSDB::def{soattl});
-  $page->param(defrefresh => $DNSDB::def{refresh});
-  $page->param(defretry => $DNSDB::def{retry});
-  $page->param(defexpire => $DNSDB::def{expire});
-  $page->param(defminttl => $DNSDB::def{minttl});
-
-  # there are probably better ways to do this.  TMTOWTDI.
-  my %soa = getSOA($dbh,$def,$id);
-
-  $page->param(domainid	=> $webvar{domain});
-  $page->param(recid	=> $soa{recid});
-  $page->param(prins	=> ($soa{prins} ? $soa{prins} : $DNSDB::def{prins}));
-  $page->param(contact	=> ($soa{contact} ? $soa{contact} : $DNSDB::def{contact}));
-  $page->param(refresh	=> ($soa{refresh} ? $soa{refresh} : $DNSDB::def{refresh}));
-  $page->param(retry	=> ($soa{retry} ? $soa{retry} : $DNSDB::def{retry}));
-  $page->param(expire	=> ($soa{expire} ? $soa{expire} : $DNSDB::def{expire}));
-  $page->param(minttl	=> ($soa{minttl} ? $soa{minttl} : $DNSDB::def{minttl}));
-  $page->param(ttl	=> ($soa{ttl} ? $soa{ttl} : $DNSDB::def{soattl}));
-}
-
-sub showdomain {
-  my $def = shift;
-  my $id = shift;
-
-  # get the SOA first
-  my %soa = getSOA($dbh,$def,$id);
-
-  $page->param(recid	=> $soa{recid});
-  $page->param(contact	=> $soa{contact});
-  $page->param(prins	=> $soa{prins});
-  $page->param(refresh	=> $soa{refresh});
-  $page->param(retry	=> $soa{retry});
-  $page->param(expire	=> $soa{expire});
-  $page->param(minttl	=> $soa{minttl});
-  $page->param(ttl	=> $soa{ttl});
-
-#  my @foo2 = getDomRecs($dbh,'def',1);
-  my $foo2 = getDomRecs($dbh,$def,$id);
-
-  my $row = 0;
-  foreach my $rec (@$foo2) {
-    $rec->{type} = $typemap{$rec->{type}};
-    $rec->{row} = $row % 2;
-    # Feh.
-    $rec->{defrec} = $webvar{defrec};
-    # And **FEH!!**
-    $rec->{sid} = $webvar{sid};
-    $row++;
-  }
-  $page->param(reclist => $foo2);
-}
-
-sub newrec {
-  my $sth = $dbh->prepare("select val,name from rectypes where stdflag=1 order by listorder");
-  $sth->execute;
-
-  my @typelist;
-  while (my ($rval,$rname) = $sth->fetchrow_array()) {
-    my %row = ( recval => $rval, recname => $rname );
-    $row{tselect} = 1 if $rval == $webvar{type};
-    push @typelist, \%row;
-  }
-  $page->param(typelist	=> \@typelist);
-  $page->param(domain	=> domainName($dbh,$webvar{domainid}));
-  $page->param(defrec	=> $webvar{defrec});
-  $page->param(ttl	=> ($webvar{ttl} ? $webvar{ttl} : $DNSDB::def{minttl}));
-  if ($webvar{defrec} eq 'y') {
-    ##fixme - should be groupid
-    $page->param(parentid => 1);
-  } else {
-    $page->param(parentid => $webvar{parentid});
-  }
-}
Index: unk/index.shtml
===================================================================
--- /trunk/index.shtml	(revision 3)
+++ 	(revision )
@@ -1,1 +1,0 @@
-<!--#include virtual="dns.cgi" -->
Index: unk/new-dns.sql
===================================================================
--- /trunk/new-dns.sql	(revision 3)
+++ 	(revision )
@@ -1,144 +1,0 @@
--- fixme:  include template-ish ACL?
-CREATE TABLE groups (
-	group_id SERIAL UNIQUE,
-	parent_group_id int NOT NULL DEFAULT '1',
-	name varchar(255) NOT NULL DEFAULT ''
-);
--- gotta have a default group to attach some further default data to...
-INSERT INTO groups (name) VALUES ('default');
-
--- use ON DELETE (CASCADE|RESTRICT|NO ACTION) on FKs
-
-CREATE TABLE domains (
-	domain_id SERIAL UNIQUE,
-	domain varchar(80) NOT NULL PRIMARY KEY,
-	group_id int NOT NULL DEFAULT 1 REFERENCES groups (group_id),
-	description varchar(255) NOT NULL DEFAULT '',
-	status int NOT NULL DEFAULT 1
-);
-
-CREATE TABLE default_records (
-	record_id SERIAL PRIMARY KEY,
-	group_id int NOT NULL DEFAULT 1 REFERENCES groups (group_id),
-	host varchar(100) NOT NULL DEFAULT '',
-	type int NOT NULL DEFAULT 1,
-	val varchar(100) NOT NULL DEFAULT '',
-	distance int NOT NULL DEFAULT 0,
-	weight int NOT NULL DEFAULT 0,
-	port int NOT NULL DEFAULT 0,
-	ttl int NOT NULL DEFAULT 86400,
-	description varchar(255)
-);
-
--- let's take pity on the poor sod installing things and include some default data that's halfway sane
-INSERT INTO default_records (group_id,host,type,val,ttl) VALUES (1,'ns1.example.com:hostmaster.DOMAIN',6,'10800:3600:604800:10800',86400);
-
-CREATE TABLE records (
-	domain_id int NOT NULL REFERENCES domains (domain_id),
-	record_id SERIAL PRIMARY KEY,
-	host varchar(100) NOT NULL DEFAULT '',
-	type int NOT NULL DEFAULT 1,
-	val varchar(100) NOT NULL DEFAULT '',
-	distance int NOT NULL DEFAULT 0,
-	weight int NOT NULL DEFAULT 0,
-	port int NOT NULL DEFAULT 0,
-	ttl int NOT NULL DEFAULT 86400,
-	description varchar(255)
-);
-
--- oooo! oooo!  allow staff/unwashed-masses parallel users?
---  - multitable, or just another column in the database...?
---  - go with column for now (could even overload groups...)
-CREATE TABLE users (
-	user_id SERIAL PRIMARY KEY,
-	group_id int NOT NULL DEFAULT 1 REFERENCES groups (group_id),
-	email varchar(60) NOT NULL,
-	password varchar(34) NOT NULL,
-	firstname varchar(30),
-	lastname varchar(30),
-	phone varchar(15),
-	type char NOT NULL DEFAULT 'S',
-	status int NOT NULL DEFAULT 1,
-	acl varchar(40) NOT NULL DEFAULT 'b'
-);
-
--- include a default user, so we don't break on install.  not happy with this, but...
--- default password is 'test'
-INSERT INTO users (group_id,email,password,type,status,acl) VALUES (1,'test@test','$1$BByge8u2$48AaGX3YeHplfErX5Tlqa1','S',1,'A');
-
--- these could be stuffed in a hash, but that gets tedious
-CREATE TABLE rectypes (
-	val int NOT NULL,
-	name varchar(12) NOT NULL,
-	stdflag int NOT NULL DEFAULT 1,
-	listorder int NOT NULL DEFAULT 255,
-	PRIMARY KEY (val,name)
-);
--- data from http://www.iana.org/assignments/dns-parameters
-COPY rectypes (val, name, stdflag, listorder) FROM stdin;
-1	A	1	1
-2	NS	1	2
-3	MD	2	255
-4	MF	2	255
-5	CNAME	1	6
-6	SOA	0	8
-7	MB	3	255
-8	MG	3	255
-9	MR	3	255
-10	NULL	3	255
-11	WKS	3	255
-12	PTR	2	4
-13	HINFO	3	255
-14	MINFO	3	255
-15	MX	1	3
-16	TXT	1	5
-17	RP	2	255
-18	AFSDB	3	255
-19	X25	3	255
-20	ISDN	3	255
-21	RT	3	255
-22	NSAP	3	255
-23	NSAP-PTR	3	255
-24	SIG	3	255
-25	KEY	3	255
-26	PX	3	255
-27	GPOS	3	255
-28	AAAA	2	2
-29	LOC	3	255
-30	NXT	3	255
-31	EID	3	255
-32	NIMLOC	3	255
-33	SRV	1	7
-34	ATMA	3	255
-35	NAPTR	3	255
-36	KX	3	255
-37	CERT	3	255
-38	A6	3	3
-39	DNAME	3	255
-40	SINK	3	255
-41	OPT	3	255
-42	APL	3	255
-43	DS	3	255
-44	SSHFP	3	255
-45	IPSECKEY	3	255
-46	RRSIG	3	255
-47	NSEC	3	255
-48	DNSKEY	3	255
-49	DHCID	3	255
-50	NSEC3	3	255
-51	NSEC3PARAM	3	255
-55	HIP	3	255
-99	SPF	3	255
-100	UINFO	3	255
-101	UID	3	255
-102	GID	3	255
-103	UNSPEC	3	255
-249	TKEY	3	255
-250	TSIG	3	255
-251	IXFR	3	255
-252	AXFR	3	255
-253	MAILB	3	255
-254	MAILA	3	255
-32768	TA	3	255
-32769	DLV	3	255
-\.
Index: unk/notes
===================================================================
--- /trunk/notes	(revision 3)
+++ 	(revision )
@@ -1,225 +1,0 @@
-web frontend:
-
-workflow:
-
-log in, see domain list from current group
-
--> "current group" includes all subgroups?  (hairy SQL)
-
-
-logic:
--> need to pass session ID on every call, otherwise we don't know who we are
--> should check ACLs on every call in case of changing permissions
--> should be able to store all webvar bits in the session
-
-
-ooo! ooo!  what about "clone existing domain"?
-
-
-components:
-menu list (actions/sections)
-domain list
-group tree
-user list
-<various edit <entity> pages>
-
-
-
-time tables:
-3600: 1h
-7200: 2h
-10800: 3h
-14400: 4h
-21600: 6h
-43200: 12h
-86400: 1d
-172800: 2d
-604800: 7d (1w)
-
-valid records:
-
-nb:  wildcards are supported for most types.  use with extreme caution!  (I don't plan on writing
-tools that will create them.)
-
-.fqdn:ip:x:ttl:timestamp:lo
- -> x.fqdn is a nameserver at ip, SOA sets x.fqdn as master with hostmaster@fqdn as contact
- -> if x contains a . that is used as the NS name
-(Not much use for us)
-
-Zfqdn:primary:contact:serial:refresh:retry:expire:minttl:recttl:(timestamp:lo)
- -> SOA for fqdn
-
-&fqdn:ip:x:ttl:timestamp:lo
- -> x.fqdn is a nameserver
- -> if x contains a . that is used as the NS name
- -> ip may be omitted;  A record for fqdn->ip is created otherwise
-
-=fqdn:ip:ttl:timestamp:lo
- -> A record and matching PTR record with fqdn and ip
-
-+fqdn:ip:ttl:timestamp:lo
- -> A record
-
-^ptr:fqdn:ttl:timestamp:lo
- -> PTR record.  note ptr must be reverse-IP format ending in .in-addr.arpa
-
-@fqdn:ip:x:dist:ttl:timestamp:lo
- -> MX.  Dist defaults to 0.
- -> ip may be omitted;  A record for fqdn->ip is created otherwise
-
--fqdn:ip:ttl:timestamp:lo
- -> ignored
-
-'fqdn:text:ttl:timestamp:lo
- -> TXT record
- -> octal-encode special characters in text as \nnn
-
-Cfqdn:name:ttl:timestamp:lo
- -> CNAME for fqdn pointing to name
-
-:fqdn:n:data:ttl:timestamp:lo
- -> Generic data, of type n (n is a 16-bit unsigned integer)
-    17 is RP
-    16 is TXT
-    2 (NS), 5 (CNAME), 6 (SOA), 12 (PTR), 15 (MX) and 252 (AXFR) (WTF?) should not be used
- -> data must use octal escapes for : or nondisplayable characters.  axfr-get seems to escape all
-    non-alphanumerics, therefore so will we.
-
-two primary groups of data:
--> forward zones
-  -> local master zones
-  -> local slave zones
--> reverse zones
-- note that it would be really nice to eliminate duplicated A records (+domain.com:ip:: plus =domain.com:ip::)
-
-operations:
--> import zone data (BIND*, djbdns, vegadns-mysql)
-  -> allow overwrite of existing SOA
--> export data (BIND*, djbdns)
--> add zone/domain
-  -> as slave
-  -> as master
--> delete zone/domain
--> add record to domain
--> remove record from domain
--> change record
-  -> A record IP
-  -> CNAME destination
-  -> MX destination
-  -> MX priority
-  -> A <-> CNAME ?
-  -> flag record as "primary" A record for an IP (means that PTR will set that name as rDNS)
--> force propagation (execute propagation script)
--> User ACL fiddling:
-  -> take IPDB model, include groups/delegation/etc
-    "admin" -> nominally full access to anything/everything
-    "staff" -> general access to all domains, can create users and delegate domains to them
-    "bulk hoster" -> customer with more than one domain, (can create users and delegate domains to them)?
-    "user" -> customer with one domain
-    
-
-recommended SOA/TTL/etc times:
-refresh 86400 (24h), retry 7200 (2h), expire 2592000 (30d), ttl 345600 (4d)
-
-Qs re: new servers:
--> IPs for cache/authoritative?
-
-
-flow of data:
-user input -> database -> local "zone" data -> rsync/scp to slaves
-
-don't use "domain ID" goop;  its only advantage is slightly lower disk use.  otherwise it's more
-complicated, less traceable thru the DB manually
-
-
-db structure (current)
-
-domains:
-| domain_id   | int(11)                   |      | MUL | NULL     | auto_increment |
-| domain      | varchar(100)              |      |     |          |                |
-| group_id    | int(11)                   | YES  |     | NULL     |                |
-| description | varchar(255)              |      |     |          |                |
-| status      | enum('active','inactive') |      |     | inactive |                |
-
-records:
-| domain_id   | int(11)      |      | MUL | 0       |                |
-| record_id   | int(11)      |      | PRI | NULL    | auto_increment |
-| host        | varchar(100) |      |     |         |                |
-| type        | char(1)      | YES  |     | NULL    |                |
-| val         | varchar(100) | YES  |     | NULL    |                |
-| distance    | int(4)       | YES  |     | 0       |                |
-| weight      | int(4)       | YES  |     | NULL    |                | - for SRV only
-| port        | int(4)       | YES  |     | NULL    |                | - for SRV only
-| ttl         | int(11)      |      |     | 86400   |                |
-| description | varchar(255) |      |     |         |                |
-
-default_records is a duplicate of records structurally
-
-log:  (not sure how useful this really is, in this form...)
-| domain_id | int(11)      |      |     | 0       |       |
-| user_id   | int(11)      |      |     | 0       |       |
-| group_id  | int(11)      |      |     | 0       |       |
-| email     | varchar(60)  |      |     |         |       |
-| name      | varchar(60)  |      |     |         |       |
-| entry     | varchar(200) |      |     |         |       |
-| time      | int(11)      |      |     | 0       |       |
-
-db structure (proposed)
-
-domains:
-domain	char(128)	pk, indexed
-group	char(32)	fk, indexed?
-status	enum?
-masterns char(64)
-email	char(128)
-serial  long int (needs 2^32 at least)
-refresh	long int	needs semi-sane default
-retry	long int	needs semi-sane default
-expire	long int	needs semi-sane default
-minttl	long int	needs semi-sane default
-ctime	timestamp
-mtime	timestamp
-
-records:
-recid	serial
-domain	char(128)	fk, indexed?
-host	char(128)	pk, indexed
-type	enum?
-val	char(256)	to allow for 255-char TXT records
-extra	char(10)	10 should be enough to express any needs for MX, SRV, or anything else...right?
-ttl	long int
-ctime	timestamp
-mtime	timestamp
-
-default records to be either database-coded as default values or coded in er, code.
--> hrm.
-  database-level defaults are "recommended practice" according to the cricket book
-  code-level defaults may be hardcoded (easy) or loaded from a config file (harder, but cleaner)
-  all must be overrideable by a database-table-stored "local policy defaults" widget
-
-add domain:
- -> need domain name
- -> IP/"company default" radio button pair, with some Javascript to change defaults for:
- -> radio buttons with sane defaults for standard hosts (www/mail/ftp/smtp)
-   -> www CNAME @
-   -> FTP CNAME @
-   -> mail CNAME mail.company.com
-   -> smtp CNAME smtp.company.com
-   -> MX defaults to <mxlist>
-   
-+----------------------------------------------------------------+
-| Domain: _________________________________                      |
-| o Company hosting       o Slave zone        o Custom settings  |
-+----------------------------------------------------------------+
-
-add_domain($domain,$class)
-update_domain($domain,$group,$status,[$contact,$primary,$serial,$ttl,$refresh,$retry,$expire,$minttl])
-delete_domain($domain)
-lock_domain($domain)  -hm..  lock/unlock may be admin-level "don't touch!" flags vs "active/inactive" flags
-unlock_domain($domain)
-add_record($domain,$host,$type,$value,$extra,$ttl)
-update_record($id,$host,$type,$val,$extra,$ttl)
-delete_record($id)
-export_data($domain,$format) ->takes special <ALL> arg for all zones.  $format -> BIND or djb (implement DJB first)
-update_nameservers()
-
