Changeset 548 for branches


Ignore:
Timestamp:
12/11/13 16:01:18 (10 years ago)
Author:
Kris Deugau
Message:

/branches/stable

Merge reverse DNS work and object conversion from /trunk, part 5

Includes changes through r543 with a few more minor conflicts.

Location:
branches/stable
Files:
5 deleted
48 edited
2 copied

Legend:

Unmodified
Added
Removed
  • branches/stable

  • branches/stable/DNSDB.pm

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2008-2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2008-2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
     
    2828use Crypt::PasswdMD5;
    2929use Net::SMTP;
    30 use NetAddr::IP qw(:lower);
     30use NetAddr::IP 4.027 qw(:lower);
    3131use POSIX;
    3232use Fcntl qw(:flock);
     33use Time::TAI64 qw(:tai64);
    3334
    3435use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
     
    4041        &getPermissions &changePermissions &comparePermissions
    4142        &changeGroup
    42         &loadConfig &connectDB &finish
     43        &connectDB &finish
    4344        &addDomain &delZone &domainName &revName &domainID &revID &addRDNS
    4445        &getZoneCount &getZoneList &getZoneLocation
     
    4950        &addLoc &updateLoc &delLoc &getLoc
    5051        &getLocCount &getLocList &getLocDropdown
    51         &getSOA &updateSOA &getRecLine &getDomRecs &getRecCount
     52        &getSOA &updateSOA &getRecLine &getRecList &getRecCount
    5253        &addRec &updateRec &delRec
    5354        &getLogCount &getLogEntries
     
    5960        &export
    6061        &mailNotify
    61         %typemap %reverse_typemap %config
     62        %typemap %reverse_typemap
    6263        @permtypes $permlist %permchains
    6364        );
     
    6869                &getPermissions &changePermissions &comparePermissions
    6970                &changeGroup
    70                 &loadConfig &connectDB &finish
     71                &connectDB &finish
    7172                &addDomain &delZone &domainName &revName &domainID &revID &addRDNS
    7273                &getZoneCount &getZoneList &getZoneLocation
     
    7778                &addLoc &updateLoc &delLoc &getLoc
    7879                &getLocCount &getLocList &getLocDropdown
    79                 &getSOA &updateSOA &getRecLine &getDomRecs &getRecCount
     80                &getSOA &updateSOA &getRecLine &getRecList &getRecCount
    8081                &addRec &updateRec &delRec
    8182                &getLogCount &getLogEntries
     
    8788                &export
    8889                &mailNotify
    89                 %typemap %reverse_typemap %config
     90                %typemap %reverse_typemap
    9091                @permtypes $permlist %permchains
    9192                )]
     
    9495our $errstr = '';
    9596our $resultstr = '';
    96 
    97 # Halfway sane defaults for SOA, TTL, etc.
    98 # serial defaults to 0 for convenience.
    99 # value will be either YYYYMMDDNN for BIND/etc, or auto-internal for tinydns
    100 our %def = qw (
    101         contact hostmaster.DOMAIN
    102         prins   ns1.myserver.com
    103         serial  0
    104         soattl  86400
    105         refresh 10800
    106         retry   3600
    107         expire  604800
    108         minttl  10800
    109         ttl     10800
    110 );
    11197
    11298# Arguably defined wholly in the db, but little reason to change without supporting code changes
     
    135121our %typemap;
    136122our %reverse_typemap;
    137 
    138 # Prepopulate a basic config.  Note some of these *will* cause errors if left unset.
    139 # note:  add appropriate stanzas in loadConfig to parse these
    140 our %config = (
    141                 # Database connection info
    142                 dbname  => 'dnsdb',
    143                 dbuser  => 'dnsdb',
    144                 dbpass  => 'secret',
    145                 dbhost  => '',
    146 
    147                 # Email notice settings
    148                 mailhost        => 'smtp.example.com',
    149                 mailnotify      => 'dnsdb@example.com', # to
    150                 mailsender      => 'dnsdb@example.com', # from
    151                 mailname        => 'DNS Administration',
    152                 orgname         => 'Example Corp',
    153                 domain          => 'example.com',
    154 
    155                 # Template directory
    156                 templatedir     => 'templates/',
    157 # fmeh.  this is a real web path, not a logical internal one.  hm..
    158 #               cssdir  => 'templates/',
    159                 sessiondir      => 'session/',
    160                 exportcache     => 'cache/',
    161 
    162                 # Session params
    163                 timeout         => '3600',      # 1 hour default
    164 
    165                 # Other miscellanea
    166                 log_failures    => 1,   # log all evarthing by default
    167                 perpage         => 15,
    168                 maxfcgi         => 100, # reasonable default?
    169         );
    170123
    171124## (Semi)private variables
     
    221174  my $class = ref($this) || $this;
    222175  my %args = @_;
    223 ##fixme?  to ponder:  do we do some magic if the caller sets eg dbname to prevent parsing of the config file?
    224   if (!loadConfig(basename => $args{configfile})) {
    225     warn "Using default configuration;  unable to load custom settings: $errstr\n";
    226   }
    227   my $self = \%config;
    228   $self->{configfile} = $args{configfile};
     176
     177  # Prepopulate a basic config.  Note some of these *will* cause errors if left unset.
     178  # note:  add appropriate stanzas in __cfgload() to parse these
     179  my %defconfig = (
     180                # The only configuration options not loadable from a config file.
     181                configfile => "/etc/dnsdb/dnsdb.conf",  ##CFG_LEAF##
     182
     183                # Database connection info
     184                dbname  => 'dnsdb',
     185                dbuser  => 'dnsdb',
     186                dbpass  => 'secret',
     187                dbhost  => '',
     188
     189                # Email notice settings
     190                mailhost        => 'smtp.example.com',
     191                mailnotify      => 'dnsdb@example.com', # to
     192                mailsender      => 'dnsdb@example.com', # from
     193                mailname        => 'DNS Administration',
     194                orgname         => 'Example Corp',
     195                domain          => 'example.com',
     196
     197                # Template directory
     198                templatedir     => 'templates/',
     199# fmeh.  this is a real web path, not a logical internal one.  hm..
     200#               cssdir  => 'templates/',
     201                sessiondir      => 'session/',
     202                exportcache     => 'cache/',
     203
     204                # Session params
     205                timeout         => '1h',        # passed as-is to CGI::Session
     206
     207                # Other miscellanea
     208                log_failures    => 1,   # log all evarthing by default
     209                perpage         => 15,
     210                max_fcgi_requests => 100,       # reasonable default?
     211                force_refresh   => 1,
     212                lowercase       => 0,   # mangle as little as possible by default
     213        );
     214
     215  # Config file parse calls.
     216  # If we are passed a blank argument for $args{configfile},
     217  #   we should NOT parse the default config file - we will
     218  #   rely on hardcoded defaults OR caller-specified values.
     219  # If we are passed a non-blank argument, parse that file.
     220  # If no config file is specified, parse the default one.
     221  my %siteconfig;
     222  if (defined($args{configfile})) {
     223    if ($args{configfile}) {
     224      return if !__cfgload($args{configfile}, \%siteconfig);
     225    }
     226  } else {
     227    return if !__cfgload($defconfig{configfile}, \%siteconfig);
     228  }
     229
     230  # Assemble the object.  Apply configuration hashes in order of precedence.
     231  my $self = {
     232        # Hardcoded defaults
     233        %defconfig,
     234        # Default config file OR caller-specified one, loaded above
     235        %siteconfig,
     236        # Caller-specified arguments
     237        %args
     238        };
    229239  bless $self, $class;
     240
     241  # Several settings are booleans.  Handle multiple possible ways of setting them.
     242  for my $boolopt ('log_failures', 'force_refresh', 'lowercase') {
     243    if ($self->{$boolopt} ne '1' && $self->{$boolopt} ne '0') {
     244      # true/false, on/off, yes/no all valid.
     245      if ($self->{$boolopt} =~ /^(?:true|false|t|f|on|off|yes|no)$/) {
     246        if ($self->{$boolopt} =~ /(?:true|t|on|yes)/) {
     247         $self->{$boolopt} = 1;
     248        } else {
     249         $self->{$boolopt} = 0;
     250        }
     251      } else {
     252        warn "Bad $boolopt setting $self->{$boolopt}\n";
     253        $self->{$boolopt} = 1;
     254      }
     255    }
     256  }
     257
     258  # Try to connect to the DB, and initialize a number of handy globals.
    230259  $self->{dbh} = connectDB($self->{dbname}, $self->{dbuser}, $self->{dbpass}, $self->{dbhost}) or return;
    231260  $self->initGlobals();
     
    236265sub DESTROY {
    237266  my $self = shift;
    238   $self->{dbh}->disconnect;
     267  $self->{dbh}->disconnect if $self->{dbh};
    239268}
     269
     270sub errstr { $DNSDB::errstr; }
    240271
    241272##
     
    370401
    371402##fixme:  farm out the actual logging to different subs for file, syslog, internal, etc based on config
    372 #  if ($config{log_channel} eq 'sql') {
     403#  if ($self->{log_channel} eq 'sql') {
    373404  $dbh->do("INSERT INTO log (domain_id,rdns_id,group_id,entry,user_id,email,name) VALUES (?,?,?,?,?,?,?)",
    374405        undef,
    375406        ($args{domain_id}, $args{rdns_id}, $args{group_id}, $args{entry},
    376407                $self->{loguserid}, $self->{logusername}, $self->{logfullname}) );
    377 #  } elsif ($config{log_channel} eq 'file') {
    378 #  } elsif ($config{log_channel} eq 'syslog') {
     408#  } elsif ($self->{log_channel} eq 'file') {
     409#  } elsif ($self->{log_channel} eq 'syslog') {
    379410#  }
    380411} # end _log
     
    409440  # or the intended parent domain for live records.
    410441  my $pname = ($args{defrec} eq 'y' ? 'DOMAIN' : $self->domainName($args{id}));
    411   ${$args{host}} =~ s/\.*$/\.$pname/ if ${$args{host}} !~ /$pname$/;
     442  ${$args{host}} =~ s/\.*$/\.$pname/ if (${$args{host}} ne '@' && ${$args{host}} !~ /$pname$/);
    412443
    413444  # Check IP is well-formed, and that it's a v4 address
     
    526557        ${$args{val}} = "ZONE,${$args{val}}" unless ${$args{val}} =~ /^ZONE/;
    527558      }
    528       ${$args{host}} =~ s/\.*$/\.$config{domain}/ if ${$args{host}} !~ /(?:$config{domain}|ADMINDOMAIN)$/;
     559      ${$args{host}} =~ s/\.*$/\.$self->{domain}/ if ${$args{host}} !~ /(?:$self->{domain}|ADMINDOMAIN)$/;
    529560    }
    530561
     
    12021233  #major patterns:
    12031234  #dashed IP, forward and reverse
     1235  #underscoreed IP, forward and reverse
    12041236  #dotted IP, forward and reverse (even if forward is... dumb)
    1205   # -> %r for reverse, %i for forward, leading - or . to indicate separator, defaults to -
     1237  # -> %r for reverse, %i for forward, leading -, _, or . to indicate separator, defaults to -
    12061238  # %r or %-r   => %4d-%3d-%2d-%1d
     1239  # %_r         => %4d_%3d_%2d_%1d
    12071240  # %.r         => %4d.%3d.%2d.%1d
    12081241  # %i or %-i   => %1d-%2d-%3d-%4d
     1242  # %_i         => %1d_%2d_%3d_%4d
    12091243  # %.i         => %1d.%2d.%3d.%4d
    12101244  $$tmpl =~ s/\%r/\%4d-\%3d-\%2d-\%1d/g;
    1211   $$tmpl =~ s/\%([-.])r/\%4d$1\%3d$1\%2d$1\%1d/g;
     1245  $$tmpl =~ s/\%([-._])r/\%4d$1\%3d$1\%2d$1\%1d/g;
    12121246  $$tmpl =~ s/\%i/\%1d-\%2d-\%3d-\%4d/g;
    1213   $$tmpl =~ s/\%([-.])i/\%1d$1\%2d$1\%3d$1\%4d/g;
     1247  $$tmpl =~ s/\%([-._])i/\%1d$1\%2d$1\%3d$1\%4d/g;
    12141248
    12151249  #hex-coded IP
     
    12321266} # _template4_expand()
    12331267
     1268# Broad syntactic check on the hostname.  Checks for valid characters, correctly-expandable template patterns.
     1269# Takes the hostname, type, and live/default and forward/reverse flags
     1270# Returns true/false, sets errstr on failures
     1271sub _check_hostname_form {
     1272  my ($hname,$rectype,$defrec,$revrec) = @_;
     1273
     1274  if ($hname =~ /\%/ && ($rectype == 65282 || $rectype == 65283) ) {
     1275    my $tmphost = $hname;
     1276    # we don't actually need to test with the real IP passed;  that saves a bit of fiddling.
     1277    _template4_expand(\$tmphost, '10.10.10.10');
     1278    if ($tmphost =~ /\%/) {
     1279      $errstr = "Invalid template $hname";
     1280      return;
     1281    }
     1282  } elsif ($revrec eq 'y') {
     1283    # Reverse zones don't support @ in hostnames
     1284    # Also skip failure on revzone TXT records;  the hostname contains the TXT content in that case.
     1285    if ($rectype != $reverse_typemap{TXT} && lc($hname) !~ /^[0-9a-z_.-]+$/) {
     1286      $errstr = "Hostnames may not contain anything other than (0-9 a-z . _)";
     1287      return;
     1288    }
     1289  } else {
     1290    if (lc($hname) !~ /^(?:[0-9a-z_.-]+|@)$/) {
     1291      # Don't mention @, because it would be far too wordy to explain the nuance of @
     1292      $errstr = "Hostnames may not contain anything other than (0-9 a-z . _)";
     1293      return;
     1294    }
     1295  }
     1296  return 1;
     1297} # _check_hostname_form()
     1298
    12341299
    12351300##
     
    12371302##
    12381303
    1239 ## DNSDB::loadConfig()
    1240 # Load the minimum required initial state (DB connect info) from a config file
    1241 # Load misc other bits while we're at it.
    1242 # Takes an optional hash that may contain:
    1243 #  - basename and config path to look for
    1244 # Populates the %config and %def hashes
    1245 sub loadConfig {
    1246   my %args = @_;
    1247   $args{configfile} = '' if !$args{configfile};
    1248 
    1249 ##fixme  this is *intended* to load a system-default config template, and allow
    1250 # overriding on a per-tool or per-web-UI-instance basis with a secondary config
    1251 # file.  The "default" config file can't be deleted in the current form.
    1252 
    1253   my $deferr = '';      # place to put error from default config file in case we can't find either one
    1254 
    1255   my $configroot = "/etc/dnsdb";        ##CFG_LEAF##
    1256   $configroot = '' if $args{configfile} =~ m|^/|;  # allow passed siteconfig to specify an arbitrary absolute path
    1257   $args{configfile} .= ".conf" if $args{configfile} !~ /\.conf$/;
    1258   my $defconfig = "$configroot/dnsdb.conf";
    1259   my $siteconfig = "$configroot/$args{configfile}";
    1260 
    1261   # System defaults
    1262   __cfgload("$defconfig") or $deferr = $errstr;
    1263 
    1264   # Per-site-ish settings.
    1265   if ($args{configfile} ne '.conf') {
    1266     unless (__cfgload("$siteconfig")) {
    1267       $errstr = ($deferr ? "Error opening default config file $defconfig: $deferr\n" : '').
    1268         "Error opening site config file $siteconfig";
    1269       return;
    1270     }
    1271   }
    1272 
    1273   # Munge log_failures.
    1274   if ($config{log_failures} ne '1' && $config{log_failures} ne '0') {
    1275     # true/false, on/off, yes/no all valid.
    1276     if ($config{log_failures} =~ /^(?:true|false|on|off|yes|no)$/) {
    1277       if ($config{log_failures} =~ /(?:true|on|yes)/) {
    1278         $config{log_failures} = 1;
    1279       } else {
    1280         $config{log_failures} = 0;
    1281       }
    1282     } else {
    1283       $errstr = "Bad log_failures setting $config{log_failures}";
    1284       $config{log_failures} = 1;
    1285       # Bad setting shouldn't be fatal.
    1286       # return 2;
    1287     }
    1288   }
    1289 
    1290   # All good, clear the error and go home.
    1291   $errstr = '';
    1292   return 1;
    1293 } # end loadConfig()
    1294 
    1295 
    12961304## DNSDB::__cfgload()
    12971305# Private sub to parse a config file and load it into %config
    1298 # Takes a filename
     1306# Takes a filename and a hashref to put the parsed entries in
    12991307sub __cfgload {
    13001308  $errstr = '';
    13011309  my $cfgfile = shift;
     1310  my $cfg = shift;
    13021311
    13031312  if (open CFG, "<$cfgfile") {
     
    13101319#    $mode = $1 if /^\[(a-z)+]/;
    13111320    # DB connect info
    1312       $config{dbname}   = $1 if /^dbname\s*=\s*([a-z0-9_.-]+)/i;
    1313       $config{dbuser}   = $1 if /^dbuser\s*=\s*([a-z0-9_.-]+)/i;
    1314       $config{dbpass}   = $1 if /^dbpass\s*=\s*([a-z0-9_.-]+)/i;
    1315       $config{dbhost}   = $1 if /^dbhost\s*=\s*([a-z0-9_.-]+)/i;
    1316       # SOA defaults
    1317       $def{contact}     = $1 if /^contact\s*=\s*([a-z0-9_.-]+)/i;
    1318       $def{prins}       = $1 if /^prins\s*=\s*([a-z0-9_.-]+)/i;
    1319       $def{soattl}      = $1 if /^soattl\s*=\s*(\d+)/i;
    1320       $def{refresh}     = $1 if /^refresh\s*=\s*(\d+)/i;
    1321       $def{retry}       = $1 if /^retry\s*=\s*(\d+)/i;
    1322       $def{expire}      = $1 if /^expire\s*=\s*(\d+)/i;
    1323       $def{minttl}      = $1 if /^minttl\s*=\s*(\d+)/i;
    1324       $def{ttl}         = $1 if /^ttl\s*=\s*(\d+)/i;
     1321      $cfg->{dbname}    = $1 if /^dbname\s*=\s*([a-z0-9_.-]+)/i;
     1322      $cfg->{dbuser}    = $1 if /^dbuser\s*=\s*([a-z0-9_.-]+)/i;
     1323      $cfg->{dbpass}    = $1 if /^dbpass\s*=\s*([a-z0-9_.-]+)/i;
     1324      $cfg->{dbhost}    = $1 if /^dbhost\s*=\s*([a-z0-9_.-]+)/i;
    13251325      # Mail settings
    1326       $config{mailhost}         = $1 if /^mailhost\s*=\s*([a-z0-9_.-]+)/i;
    1327       $config{mailnotify}       = $1 if /^mailnotify\s*=\s*([a-z0-9_.\@-]+)/i;
    1328       $config{mailsender}       = $1 if /^mailsender\s*=\s*([a-z0-9_.\@-]+)/i;
    1329       $config{mailname}         = $1 if /^mailname\s*=\s*([a-z0-9\s_.-]+)/i;
    1330       $config{orgname}          = $1 if /^orgname\s*=\s*([a-z0-9\s_.,'-]+)/i;
    1331       $config{domain}           = $1 if /^domain\s*=\s*([a-z0-9_.-]+)/i;
     1326      $cfg->{mailhost}          = $1 if /^mailhost\s*=\s*([a-z0-9_.-]+)/i;
     1327      $cfg->{mailnotify}        = $1 if /^mailnotify\s*=\s*([a-z0-9_.\@-]+)/i;
     1328      $cfg->{mailsender}        = $1 if /^mailsender\s*=\s*([a-z0-9_.\@-]+)/i;
     1329      $cfg->{mailname}          = $1 if /^mailname\s*=\s*([a-z0-9\s_.-]+)/i;
     1330      $cfg->{orgname}           = $1 if /^orgname\s*=\s*([a-z0-9\s_.,'-]+)/i;
     1331      $cfg->{domain}            = $1 if /^domain\s*=\s*([a-z0-9_.-]+)/i;
    13321332      # session - note this is fed directly to CGI::Session
    1333       $config{timeout}          = $1 if /^[tT][iI][mM][eE][oO][uU][tT]\s*=\s*(\d+[smhdwMy]?)/;
    1334       $config{sessiondir}       = $1 if m{^sessiondir\s*=\s*([a-z0-9/_.-]+)}i;
     1333      $cfg->{timeout}           = $1 if /^[tT][iI][mM][eE][oO][uU][tT]\s*=\s*(\d+[smhdwMy]?)/;
     1334      $cfg->{sessiondir}        = $1 if m{^sessiondir\s*=\s*([a-z0-9/_.-]+)}i;
    13351335      # misc
    1336       $config{log_failures}     = $1 if /^log_failures\s*=\s*([a-z01]+)/i;
    1337       $config{perpage}          = $1 if /^perpage\s*=\s*(\d+)/i;
    1338       $config{exportcache}      = $1 if m{^exportcache\s*=\s*([a-z0-9/_.-]+)}i;
     1336      $cfg->{log_failures}      = $1 if /^log_failures\s*=\s*([a-z01]+)/i;
     1337      $cfg->{perpage}           = $1 if /^perpage\s*=\s*(\d+)/i;
     1338      $cfg->{exportcache}       = $1 if m{^exportcache\s*=\s*([a-z0-9/_.-]+)}i;
     1339      $cfg->{lowercase}         = $1 if /^lowercase\s*=\s*([a-z01]+)/i;
     1340# not supported in dns.cgi yet
     1341#      $cfg->{templatedir}      = $1 if m{^templatedir\s*=\s*([a-z0-9/_.-]+)}i;
     1342#      $cfg->{templateoverride} = $1 if m{^templateoverride\s*=\s*([a-z0-9/_.-]+)}i;
    13391343      # RPC options
    1340       $config{rpcmode}          = $1 if /^rpc_mode\s*=\s*(socket|HTTP|XMLRPC)\s*$/i;
    1341       $config{maxfcgi}          = $1 if /^max_fcgi_requests\s*=\s*(\d+)\s*$/i;
     1344      $cfg->{rpcmode}           = $1 if /^rpc_mode\s*=\s*(socket|HTTP|XMLRPC)\s*$/i;
     1345      $cfg->{maxfcgi}           = $1 if /^max_fcgi_requests\s*=\s*(\d+)\s*$/i;
     1346      $cfg->{force_refresh}     = $1 if /^force_refresh\s*=\s*([a-z01]+)/i;
    13421347      if (my ($tmp) = /^rpc_iplist\s*=\s*(.+)/i) {
    13431348        my @ips = split /[,\s]+/, $tmp;
    13441349        my $rpcsys = shift @ips;
    1345         push @{$config{rpcacl}{$rpcsys}}, @ips;
     1350        push @{$cfg->{rpcacl}{$rpcsys}}, @ips;
    13461351      }
    13471352    }
    13481353    close CFG;
    13491354  } else {
    1350     $errstr = $!;
     1355    $errstr = "Couldn't load configuration file $cfgfile: $!";
    13511356    return;
    13521357  }
     
    15311536# about having to open a file or a syslog channel
    15321537##fixme Need to call _initActionLog_blah() for various logging channels, configured
    1533 # via dnsdb.conf, in $config{log_channel} or something
     1538# via dnsdb.conf, in $self->{log_channel} or something
    15341539# See https://secure.deepnet.cx/trac/dnsadmin/ticket/21
    15351540sub initActionLog {
     
    15521557
    15531558  # convert to real check once we have other logging channels
    1554   # if ($config{log_channel} eq 'sql') {
     1559  # if ($self->{log_channel} eq 'sql') {
    15551560  #   Open Log, Sez Me!
    15561561  # }
     
    15941599  my $sth = $dbh->prepare($sql);
    15951600
    1596   $sth->execute($id) or die "argh: ".$sth->errstr;
     1601##fixme?  we don't trap other plain SELECT errors
     1602  $sth->execute($id);
    15971603
    15981604#  my $permref = $sth->fetchrow_hashref;
     
    17781784    my $msg = $@;
    17791785    eval { $dbh->rollback; };
    1780     if ($config{log_failures}) {
     1786    if ($self->{log_failures}) {
    17811787      $self->_log(group_id => $oldgid, entry => "Error moving $type $entname to $newgname: $msg");
    17821788      $dbh->commit;     # since we enabled transactions earlier
     
    18021808  my $self = shift;
    18031809  my $dbh = $self->{dbh};
    1804   return ('FAIL',"Need database handle") if !$dbh;
    18051810  my $domain = shift;
    1806   return ('FAIL',"Domain must not be blank") if !$domain;
     1811  return ('FAIL',"Domain must not be blank\n") if !$domain;
    18071812  my $group = shift;
    1808   return ('FAIL',"Need group") if !defined($group);
     1813  return ('FAIL',"Group must be specified\n") if !defined($group);
    18091814  my $state = shift;
    1810   return ('FAIL',"Need domain status") if !defined($state);
     1815  return ('FAIL',"Domain status must be specified\n") if !defined($state);
     1816  my $defloc = shift || '';
    18111817
    18121818  $state = 1 if $state =~ /^active$/;
     
    18171823  return ('FAIL',"Invalid domain status") if $state !~ /^\d+$/;
    18181824
     1825  $domain = lc($domain) if $self->{lowercase};
     1826
    18191827  return ('FAIL', "Invalid characters in domain") if $domain !~ /^[a-zA-Z0-9_.-]+$/;
    18201828
     
    18361844  eval {
    18371845    # insert the domain...
    1838     $dbh->do("INSERT INTO domains (domain,group_id,status) VALUES (?,?,?)", undef, ($domain, $group, $state));
     1846    $dbh->do("INSERT INTO domains (domain,group_id,status,default_location) VALUES (?,?,?,?)", undef,
     1847        ($domain, $group, $state, $defloc));
    18391848
    18401849    # get the ID...
     
    18471856    # ... and now we construct the standard records from the default set.  NB:  group should be variable.
    18481857    my $sth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl FROM default_records WHERE group_id=?");
    1849     my $sth_in = $dbh->prepare("INSERT INTO records (domain_id,host,type,val,distance,weight,port,ttl)".
    1850         " VALUES ($dom_id,?,?,?,?,?,?,?)");
     1858    my $sth_in = $dbh->prepare("INSERT INTO records (domain_id,host,type,val,distance,weight,port,ttl,location)".
     1859        " VALUES ($dom_id,?,?,?,?,?,?,?,?)");
    18511860    $sth->execute($group);
    1852     while (my ($host,$type,$val,$dist,$weight,$port,$ttl) = $sth->fetchrow_array()) {
     1861    while (my ($host, $type, $val, $dist, $weight, $port, $ttl) = $sth->fetchrow_array()) {
    18531862      $host =~ s/DOMAIN/$domain/g;
    18541863      $val =~ s/DOMAIN/$domain/g;
    1855       $sth_in->execute($host,$type,$val,$dist,$weight,$port,$ttl);
     1864      $sth_in->execute($host, $type, $val, $dist, $weight, $port, $ttl, $defloc);
    18561865      if ($typemap{$type} eq 'SOA') {
    18571866        my @tmp1 = split /:/, $host;
     
    18771886    eval { $dbh->rollback; };
    18781887    $self->_log(group_id => $group, entry => "Failed adding domain $domain ($msg)")
    1879         if $config{log_failures};
     1888        if $self->{log_failures};
    18801889    $dbh->commit;       # since we enabled transactions earlier
    18811890    return ('FAIL',$msg);
     
    19081917  return ('FAIL', ($revrec eq 'n' ? 'Domain' : 'Reverse zone')." ID $zoneid doesn't exist") if !$zone;
    19091918
    1910   # Set this up here since we may use if if $config{log_failures} is enabled
     1919  # Set this up here since we may use if if $self->{log_failures} is enabled
    19111920  my %loghash;
    19121921  $loghash{domain_id} = $zoneid if $revrec eq 'n';
     
    19561965    eval { $dbh->rollback; };
    19571966    $loghash{entry} = "Error deleting $zone: $msg ($failmsg)";
    1958     if ($config{log_failures}) {
     1967    if ($self->{log_failures}) {
    19591968      $self->_log(%loghash);
    19601969      $dbh->commit;     # since we enabled transactions earlier
     
    20082017  my ($domid) = $dbh->selectrow_array("SELECT domain_id FROM domains WHERE lower(domain) = lower(?)",
    20092018        undef, ($domain) );
    2010   $errstr = $DBI::errstr if !$domid;
     2019  if (!$domid) {
     2020    if ($dbh->err) {
     2021      $errstr = $DBI::errstr;
     2022    } else {
     2023      $errstr = "Domain $domain not present";
     2024    }
     2025  }
    20112026  return $domid if $domid;
    20122027} # end domainID()
     
    20222037  my $revzone = shift;
    20232038  my ($revid) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet=?", undef, ($revzone) );
    2024   $errstr = $DBI::errstr if !$revid;
     2039  if (!$revid) {
     2040    if ($dbh->err) {
     2041      $errstr = $DBI::errstr;
     2042    } else {
     2043      $errstr = "Reverse zone $revzone not present";
     2044    }
     2045  }
    20252046  return $revid if $revid;
    20262047} # end revID()
     
    20892110      }
    20902111
    2091       $host =~ s/ADMINDOMAIN/$config{domain}/g;
     2112      $host =~ s/ADMINDOMAIN/$self->{domain}/g;
    20922113
    20932114      # Check to make sure the IP stubs will fit in the zone.  Under most usage failures here should be rare.
     
    21832204    eval { $dbh->rollback; };
    21842205    $self->_log(group_id => $group, entry => "Failed adding reverse zone $zone ($msg)")
    2185         if $config{log_failures};
     2206        if $self->{log_failures};
    21862207    $dbh->commit;       # since we enabled transactions earlier
    21872208    return ('FAIL',$msg);
     
    22112232
    22122233  my %args = @_;
     2234
     2235  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     2236  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     2237    $errstr = "Bad or missing curgroup argument";
     2238    return;
     2239  }
     2240  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     2241  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     2242    $errstr = "Bad childlist argument";
     2243    return;
     2244  }
    22132245
    22142246  my @filterargs;
     
    22502282  $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
    22512283  $args{offset} = 0 if !$args{offset} || $args{offset} !~ /^(?:all|\d+)$/;
     2284
     2285  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     2286  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     2287    $errstr = "Bad or missing curgroup argument";
     2288    return;
     2289  }
     2290  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     2291  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     2292    $errstr = "Bad childlist argument";
     2293    return;
     2294  }
    22522295
    22532296  my @filterargs;
     
    22772320  # A common tail.
    22782321  $sql .= " ORDER BY ".($args{sortby} eq 'group' ? 'groups.group_name' : $args{sortby})." $args{sortorder} ".
    2279         ($args{offset} eq 'all' ? '' : " LIMIT $config{perpage}".
    2280         " OFFSET ".$args{offset}*$config{perpage});
     2322        ($args{offset} eq 'all' ? '' : " LIMIT $self->{perpage}".
     2323        " OFFSET ".$args{offset}*$self->{perpage});
    22812324  my $sth = $dbh->prepare($sql);
    22822325  $sth->execute(@filterargs);
     
    24042447    my $msg = $@;
    24052448    eval { $dbh->rollback; };
    2406     if ($config{log_failures}) {
     2449    if ($self->{log_failures}) {
    24072450      $self->_log(group_id => $pargroup, entry => "Failed to add group $groupname: $msg");
    24082451      $dbh->commit;
     
    24482491    my ($domcnt) = $dbh->selectrow_array("SELECT count(*) FROM domains WHERE group_id=?", undef, ($groupid));
    24492492    die "$domcnt domains still in group\n" if $domcnt;
     2493    my ($revcnt) = $dbh->selectrow_array("SELECT count(*) FROM revzones WHERE group_id=?", undef, ($groupid));
     2494    die "$revcnt reverse zones still in group\n" if $revcnt;
    24502495    my ($usercnt) = $dbh->selectrow_array("SELECT count(*) FROM users WHERE group_id=?", undef, ($groupid));
    24512496    die "$usercnt users still in group\n" if $usercnt;
     
    24682513    my $msg = $@;
    24692514    eval { $dbh->rollback; };
    2470     if ($config{log_failures}) {
     2515    if ($self->{log_failures}) {
    24712516      $self->_log(group_id => $parid, entry => "$failmsg: $msg");
    24722517      $dbh->commit;     # since we enabled transactions earlier
     
    25452590  my %args = @_;
    25462591
     2592  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     2593  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     2594    $errstr = "Bad or missing curgroup argument";
     2595    return;
     2596  }
     2597  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     2598  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     2599    $errstr = "Bad childlist argument";
     2600    return;
     2601  }
     2602
    25472603  my @filterargs;
    2548 
    25492604  $args{startwith} = undef if $args{startwith} && $args{startwith} !~ /^(?:[a-z]|0-9)$/;
    25502605  push @filterargs, "^$args{startwith}" if $args{startwith};
     
    25712626  my %args = @_;
    25722627
     2628  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     2629  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     2630    $errstr = "Bad or missing curgroup argument";
     2631    return;
     2632  }
     2633  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     2634  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     2635    $errstr = "Bad childlist argument";
     2636    return;
     2637  }
     2638
    25732639  my @filterargs;
    2574 
    25752640  $args{startwith} = undef if $args{startwith} && $args{startwith} !~ /^(?:[a-z]|0-9)$/;
    25762641  push @filterargs, "^$args{startwith}" if $args{startwith};
     
    25782643
    25792644  # protection against bad or missing arguments
    2580   $args{sortorder} = 'ASC' if !$args{sortorder};
     2645  $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
     2646  $args{sortby} = 'group' if !$args{sortby} || $args{sortby} !~ /^[\w_.]+$/;
    25812647  $args{offset} = 0 if !$args{offset} || $args{offset} !~ /^(?:all|\d+)$/;
    25822648
     
    25942660        " GROUP BY g.group_id, g.group_name, g2.group_name ".
    25952661        " ORDER BY $args{sortby} $args{sortorder} ".
    2596         ($args{offset} eq 'all' ? '' : " LIMIT $config{perpage} OFFSET ".$args{offset}*$config{perpage});
     2662        ($args{offset} eq 'all' ? '' : " LIMIT $self->{perpage} OFFSET ".$args{offset}*$self->{perpage});
    25972663  my $glist = $dbh->selectall_arrayref($sql, { Slice => {} }, (@filterargs) );
    25982664  $errstr = $dbh->errstr if !$glist;
     
    27492815    my $msg = $@;
    27502816    eval { $dbh->rollback; };
    2751     if ($config{log_failures}) {
     2817    if ($self->{log_failures}) {
    27522818      $self->_log(group_id => $group, entry => "Error adding user $username: $msg");
    27532819      $dbh->commit;     # since we enabled transactions earlier
     
    27722838  my %args = @_;
    27732839
     2840  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     2841  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     2842    $errstr = "Bad or missing curgroup argument";
     2843    return;
     2844  }
     2845  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     2846  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     2847    $errstr = "Bad childlist argument";
     2848    return;
     2849  }
     2850
    27742851  my @filterargs;
    2775 
    27762852  $args{startwith} = undef if $args{startwith} && $args{startwith} !~ /^(?:[a-z]|0-9)$/;
    27772853  push @filterargs, "^$args{startwith}" if $args{startwith};
    27782854  push @filterargs, $args{filter} if $args{filter};
    2779 
    27802855
    27812856  my $sql = "SELECT count(*) FROM users ".
     
    28012876  my %args = @_;
    28022877
     2878  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     2879  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     2880    $errstr = "Bad or missing curgroup argument";
     2881    return;
     2882  }
     2883  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     2884  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     2885    $errstr = "Bad childlist argument";
     2886    return;
     2887  }
     2888
    28032889  my @filterargs;
    2804 
    28052890  $args{startwith} = undef if $args{startwith} && $args{startwith} !~ /^(?:[a-z]|0-9)$/;
    28062891  push @filterargs, "^$args{startwith}" if $args{startwith};
     
    28132898
    28142899  # protection against bad or missing arguments
    2815   $args{sortorder} = 'ASC' if !$args{sortorder};
    2816   $args{sortby} = 'u.username' if !$args{sortby};
     2900  $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
     2901  $args{sortby} = 'u.username' if !$args{sortby} || $args{sortby} !~ /^[\w_.]+$/;
    28172902  $args{offset} = 0 if !$args{offset} || $args{offset} !~ /^(?:all|\d+)$/;
    28182903
     
    28252910        " AND NOT u.type = 'R' ".
    28262911        " ORDER BY $args{sortby} $args{sortorder} ".
    2827         ($args{offset} eq 'all' ? '' : " LIMIT $config{perpage} OFFSET ".$args{offset}*$config{perpage});
     2912        ($args{offset} eq 'all' ? '' : " LIMIT $self->{perpage} OFFSET ".$args{offset}*$self->{perpage});
    28282913  my $ulist = $dbh->selectall_arrayref($sql, { Slice => {} }, (@filterargs) );
    28292914  $errstr = $dbh->errstr if !$ulist;
     
    29082993    my $msg = $@;
    29092994    eval { $dbh->rollback; };
    2910     if ($config{log_failures}) {
     2995    if ($self->{log_failures}) {
    29112996      $self->_log(group_id => $group, entry => "Error updating user $username: $msg");
    29122997      $dbh->commit;     # since we enabled transactions earlier
     
    29473032    my $msg = $@;
    29483033    eval { $dbh->rollback; };
    2949     if ($config{log_failures}) {
     3034    if ($self->{log_failures}) {
    29503035      $self->_log(group_id => $userdata->{group_id}, entry => "Error deleting user ID ".
    29513036        "$userid/".$userdata->{username}.": $msg");
     
    31203205    my $msg = $@;
    31213206    eval { $dbh->rollback; };
    3122     if ($config{log_failures}) {
     3207    if ($self->{log_failures}) {
    31233208      $shdesc = $loc if !$shdesc;
    31243209      $self->_log(entry => "Failed adding location ($shdesc, '$iplist'): $msg");
     
    31673252    my $msg = $@;
    31683253    eval { $dbh->rollback; };
    3169     if ($config{log_failures}) {
     3254    if ($self->{log_failures}) {
    31703255      $shdesc = $loc if !$shdesc;
    31713256      $self->_log(entry => "Failed updating location ($shdesc, '$iplist'): $msg");
     
    32063291    my $msg = $@;
    32073292    eval { $dbh->rollback; };
    3208     if ($config{log_failures}) {
     3293    if ($self->{log_failures}) {
    32093294      $self->_log(entry => "Failed to delete location ($olddesc, '$oldloc->{iplist}'): $msg");
    32103295      $dbh->commit;
     
    32443329  my %args = @_;
    32453330
     3331  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     3332  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     3333    $errstr = "Bad or missing curgroup argument";
     3334    return;
     3335  }
     3336  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     3337  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     3338    $errstr = "Bad childlist argument";
     3339    return;
     3340  }
     3341
    32463342  my @filterargs;
    3247 
    32483343  $args{startwith} = undef if $args{startwith} && $args{startwith} !~ /^(?:[a-z]|0-9)$/;
    32493344  push @filterargs, "^$args{startwith}" if $args{startwith};
    32503345  push @filterargs, $args{filter} if $args{filter};
    3251 
    32523346
    32533347  my $sql = "SELECT count(*) FROM locations ".
     
    32683362  my %args = @_;
    32693363
     3364  # Fail on bad curgroup argument.  There's no sane fallback on this one.
     3365  if (!$args{curgroup} || $args{curgroup} !~ /^\d+$/) {
     3366    $errstr = "Bad or missing curgroup argument";
     3367    return;
     3368  }
     3369  # Fail on bad childlist argument.  This could be sanely ignored if bad, maybe.
     3370  if ($args{childlist} && $args{childlist} !~ /^[\d,]+$/) {
     3371    $errstr = "Bad childlist argument";
     3372    return;
     3373  }
     3374
    32703375  my @filterargs;
    3271 
    32723376  $args{startwith} = undef if $args{startwith} && $args{startwith} !~ /^(?:[a-z]|0-9)$/;
    32733377  push @filterargs, "^$args{startwith}" if $args{startwith};
     
    32803384
    32813385  # protection against bad or missing arguments
    3282   $args{sortorder} = 'ASC' if !$args{sortorder};
    3283   $args{sortby} = 'l.description' if !$args{sortby};
     3386  $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
     3387  $args{sortby} = 'l.description' if !$args{sortby} || $args{sortby} !~ /^[\w_.]+$/;
    32843388  $args{offset} = 0 if !$args{offset} || $args{offset} !~ /^(?:all|\d+)$/;
    32853389
     
    32913395        ($args{filter} ? " AND l.description ~* ?" : '').
    32923396        " ORDER BY $args{sortby} $args{sortorder} ".
    3293         ($args{offset} eq 'all' ? '' : " LIMIT $config{perpage} OFFSET ".$args{offset}*$config{perpage});
     3397        ($args{offset} eq 'all' ? '' : " LIMIT $self->{perpage} OFFSET ".$args{offset}*$self->{perpage});
    32943398  my $ulist = $dbh->selectall_arrayref($sql, { Slice => {} }, (@filterargs) );
    32953399  $errstr = $dbh->errstr if !$ulist;
     
    34113515    $logdata{entry} = "Error updating ".($defrec eq 'y' ? ($revrec eq 'y' ? 'default reverse zone ' : 'default ') : '').
    34123516        "SOA record for $parname: $msg";
    3413     if ($config{log_failures}) {
     3517    if ($self->{log_failures}) {
    34143518      $self->_log(%logdata);
    34153519      $dbh->commit;
     
    34333537  my $id = shift;
    34343538
     3539##fixme: do we need a knob to twist to switch between unix epoch and postgres time string?
    34353540  my $sql = "SELECT record_id,host,type,val,ttl".
    34363541        ($defrec eq 'n' ? ',location' : '').
    34373542        ($revrec eq 'n' ? ',distance,weight,port' : '').
    3438         (($defrec eq 'y') ? ',group_id FROM ' : ',domain_id,rdns_id FROM ').
     3543        (($defrec eq 'y') ? ',group_id FROM ' : ',domain_id,rdns_id,stamp,stamp < now() AS ispast,expires,stampactive FROM ').
    34393544        _rectable($defrec,$revrec)." WHERE record_id=?";
    34403545  my $ret = $dbh->selectrow_hashref($sql, undef, ($id) );
     
    34653570
    34663571##fixme: should use above (getRecLine()) to get lines for below?
    3467 ## DNSDB::getDomRecs()
    3468 # Return records for a domain
    3469 # Takes a database handle, default/live flag, group/domain ID, start,
     3572## DNSDB::getRecList()
     3573# Return records for a group or zone
     3574# Takes a default/live flag, group or zone ID, start,
    34703575# number of records, sort field, and sort order
    34713576# Returns a reference to an array of hashes
    3472 sub getDomRecs {
     3577sub getRecList {
    34733578  $errstr = '';
    34743579  my $self = shift;
     
    34823587
    34833588  # protection against bad or missing arguments
    3484   $args{sortorder} = 'ASC' if !$args{sortorder};
    3485   $args{sortby} = 'host' if !$args{sortby} && $args{revrec} eq 'n';     # default sort by host on domain record list
    3486   $args{sortby} = 'val' if !$args{sortby} && $args{revrec} eq 'y';      # default sort by IP on revzone record list
     3589  $args{sortorder} = 'ASC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
     3590  my $defsort;
     3591  $defsort = 'host' if $args{revrec} eq 'n';     # default sort by host on domain record list
     3592  $defsort = 'val' if $args{revrec} eq 'y';      # default sort by IP on revzone record list
     3593  $args{sortby} = '' if !$args{sortby};
     3594  $args{sortby} = $defsort if !$args{revrec};
     3595  $args{sortby} = $defsort if $args{sortby} !~ /^[\w_,.]+$/;
    34873596  $args{offset} = 0 if !$args{offset} || $args{offset} !~ /^(?:all|\d+)$/; 
     3597  my $perpage = ($args{nrecs} ? $args{nrecs} : $self->{perpage});
    34883598
    34893599  # sort reverse zones on IP, correctly
     
    35083618  $newsort =~ s/^,//;
    35093619
     3620##fixme:  do we need a knob to twist to switch from unix epoch to postgres time string?
     3621  my $sql = "SELECT r.record_id,r.host,r.type,r.val,r.ttl";
     3622  $sql .= ",l.description AS locname,stamp,r.stamp < now() AS ispast,r.expires,r.stampactive"
     3623        if $args{defrec} eq 'n';
     3624  $sql .= ",r.distance,r.weight,r.port" if $args{revrec} eq 'n';
     3625  $sql .= " FROM "._rectable($args{defrec},$args{revrec})." r ";
    35103626  $sql .= "INNER JOIN rectypes t ON r.type=t.val ";     # for sorting by type alphabetically
    35113627  $sql .= "LEFT JOIN locations l ON r.location=l.location " if $args{defrec} eq 'n';
     
    35163632  # ensure consistent ordering by sorting on record_id too
    35173633  $sql .= ", record_id $args{sortorder}";
    3518   $sql .= ($args{offset} eq 'all' ? '' : " LIMIT $config{perpage} OFFSET ".$args{offset}*$config{perpage});
     3634  $sql .= ($args{offset} eq 'all' ? '' : " LIMIT $perpage OFFSET ".$args{offset}*$perpage);
    35193635
    35203636  my @bindvars = ($args{id});
     
    35223638
    35233639  my $ret = $dbh->selectall_arrayref($sql, { Slice => {} }, (@bindvars) );
     3640  $errstr = "Error retrieving records: ".$dbh->errstr if !$ret;
     3641
    35243642  return $ret;
    3525 } # end getDomRecs()
     3643} # end getRecList()
    35263644
    35273645
     
    35813699  $location  = '' if !$location;
    35823700
     3701  my $expires = shift;
     3702  $expires = 1 if $expires eq 'until';  # Turn some special values into the appropriate booleans.
     3703  $expires = 0 if $expires eq 'after';
     3704  my $stamp = shift;
     3705  $stamp = '' if !$stamp;        # Timestamp should be a string at this point.
     3706
    35833707  # Spaces are evil.
    35843708  $host =~ s/^\s+//;
     
    35903714  }
    35913715
    3592   # Validation
    3593   my $addr = NetAddr::IP->new($val);
    3594   if ($rectype == $reverse_typemap{A}) {
    3595     return ('FAIL',$typemap{$rectype}." record must be a valid IPv4 address")
    3596         unless $addr && !$addr->{isv6};
    3597   }
    3598   if ($rectype == $reverse_typemap{AAAA}) {
    3599     return ('FAIL',$typemap{$rectype}." record must be a valid IPv6 address")
    3600         unless $addr && $addr->{isv6};
    3601   }
     3716  if ($self->{lowercase}) {
     3717    if ($typemap{$$rectype} ne 'TXT') {
     3718      $$host = lc($$host);
     3719      $$val = lc($$val);
     3720    } else {
     3721      # TXT records should preserve user entry in the string.
     3722      if ($revrec eq 'n') {
     3723        $$host = lc($$host);
     3724      } else {
     3725        $$val = lc($$val);
     3726      }
     3727    }
     3728  }
     3729
     3730  # prep for validation
     3731  my $addr = NetAddr::IP->new($$val);
     3732  $$host =~ s/\.+$//;   # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI.
    36023733
    36033734  my $domid = 0;
     
    36103741  return ('FAIL', "TTL must be numeric") unless $ttl =~ /^\d+$/;
    36113742
    3612   # Quick check on hostname parts.  Note the regex is more forgiving than the error message;
    3613   # domain names technically are case-insensitive, and we use printf-like % codes for a couple
    3614   # of types.  Other things may also be added to validate default records of several flavours.
    3615   return ('FAIL', "Hostnames may not contain anything other than (0-9 a-z . _)")
    3616         if $defrec eq 'n' && ($revrec eq 'y' ? $$rectype != $reverse_typemap{TXT} : 1) &&
    3617                 $$host !~ /^[0-9a-z_%.-]+$/i;
     3743  # Quick check on hostname parts.  There are enough variations to justify a sub now.
     3744  return ('FAIL', $errstr) if ! _check_hostname_form($$host, $$rectype, $defrec, $revrec);
    36183745
    36193746  # Collect these even if we're only doing a simple A record so we can call *any* validation sub
     
    36373764  push @vallist, ($$host,$$rectype,$$val,$ttl,$id);
    36383765
    3639   # locations are not for default records, silly coder!
    36403766  if ($defrec eq 'n') {
     3767    # locations are not for default records, silly coder!
    36413768    $fields .= ",location";
    36423769    push @vallist, $location;
    3643   }
     3770    # timestamps are rare.
     3771    if ($stamp) {
     3772      $fields .= ",stamp,expires,stampactive";
     3773      push @vallist, $stamp, $expires, 'y';
     3774    } else {
     3775      $fields .= ",stampactive";
     3776      push @vallist, 'n';
     3777    }
     3778  }
     3779
     3780  # a little magic to get the right number of ? placeholders based on how many values we're providing
    36443781  my $vallen = '?'.(',?'x$#vallist);
    36453782
     
    36693806  $logdata{entry} .= "', TTL $ttl";
    36703807  $logdata{entry} .= ", location ".$self->getLoc($location)->{description} if $location;
     3808  $logdata{entry} .= ($expires eq 'after' ? ', valid after ' : ', expires at ').$stamp if $stamp;
    36713809
    36723810  # Allow transactions, and raise an exception on errors so we can catch it later.
     
    36843822    my $msg = $@;
    36853823    eval { $dbh->rollback; };
    3686     if ($config{log_failures}) {
     3824    if ($self->{log_failures}) {
    36873825      $logdata{entry} = "Failed adding ".($defrec eq 'y' ? 'default ' : '').
    36883826        "record '$$host $typemap{$$rectype} $$val', TTL $ttl ($msg)";
     
    37223860  $location  = '' if !$location;
    37233861
    3724   # prep for validation
    3725   my $addr = NetAddr::IP->new($$val);
    3726   $$host =~ s/\.+$//;   # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI.
     3862  my $expires = shift;
     3863  $expires = 1 if $expires eq 'until';  # Turn some special values into the appropriate booleans.
     3864  $expires = 0 if $expires eq 'after';
     3865  my $stamp = shift;
     3866  $stamp = '' if !$stamp;        # Timestamp should be a string at this point.
     3867
     3868  # just set it to an empty string;  failures will be caught later.
     3869  $$host = '' if !$$host;
    37273870
    37283871  # Spaces are evil.
     
    37353878  }
    37363879
     3880  if ($self->{lowercase}) {
     3881    if ($typemap{$$rectype} ne 'TXT') {
     3882      $$host = lc($$host);
     3883      $$val = lc($$val);
     3884    } else {
     3885      # TXT records should preserve user entry in the string.
     3886      if ($revrec eq 'n') {
     3887        $$host = lc($$host);
     3888      } else {
     3889        $$val = lc($$val);
     3890      }
     3891    }
     3892  }
     3893
     3894  # prep for validation
     3895  my $addr = NetAddr::IP->new($$val);
     3896  $$host =~ s/\.+$//;   # FQDNs ending in . are an internal detail, and really shouldn't be exposed in the UI.
     3897
    37373898  my $domid = 0;
    37383899  my $revid = 0;
     
    37443905  return ('FAIL', "TTL must be numeric") unless $ttl =~ /^\d+$/;
    37453906
    3746   # Quick check on hostname parts.  Note the regex is more forgiving than the error message;
    3747   # domain names technically are case-insensitive, and we use printf-like % codes for a couple
    3748   # of types.  Other things may also be added to validate default records of several flavours.
    3749   return ('FAIL', "Hostnames may not contain anything other than (0-9 a-z - . _)")
    3750         if $defrec eq 'n' && ($revrec eq 'y' ? $$rectype != $reverse_typemap{TXT} : 1) &&
    3751                 $$host !~ /^[0-9a-z_%.-]+$/i;
     3907  # Quick check on hostname parts.  There are enough variations to justify a sub now.
     3908  return ('FAIL', $errstr) if ! _check_hostname_form($$host, $$rectype, $defrec, $revrec);
    37523909
    37533910  # only MX and SRV will use these
     
    37803937        ($defrec eq 'y' ? $oldrec->{group_id} : ($revrec eq 'n' ? $oldrec->{domain_id} : $oldrec->{rdns_id})) );
    37813938
    3782   # locations are not for default records, silly coder!
    37833939  if ($defrec eq 'n') {
     3940    # locations are not for default records, silly coder!
    37843941    $fields .= ",location";
    37853942    push @vallist, $location;
     3943    # timestamps are rare.
     3944    if ($stamp) {
     3945      $fields .= ",stamp,expires,stampactive";
     3946      push @vallist, $stamp, $expires, 'y';
     3947    } else {
     3948      $fields .= ",stampactive";
     3949      push @vallist, 'n';
     3950    }
    37863951  }
    37873952
     
    38374002  $logdata{entry} .= "', TTL $oldrec->{ttl}";
    38384003  $logdata{entry} .= ", location ".$self->getLoc($oldrec->{location})->{description} if $oldrec->{location};
     4004  $logdata{entry} .= ($oldrec->{expires} ? ', expires at ' : ', valid after ').$oldrec->{stamp}
     4005        if $oldrec->{stampactive};
    38394006  $logdata{entry} .= "\nto\n";
    38404007  # More NS special
     
    38484015  $logdata{entry} .= "', TTL $ttl";
    38494016  $logdata{entry} .= ", location ".$self->getLoc($location)->{description} if $location;
     4017  $logdata{entry} .= ($expires eq 'after' ? ', valid after ' : ', expires at ').$stamp if $stamp;
    38504018
    38514019  local $dbh->{AutoCommit} = 0;
     
    38644032    my $msg = $@;
    38654033    eval { $dbh->rollback; };
    3866     if ($config{log_failures}) {
     4034    if ($self->{log_failures}) {
    38674035      $logdata{entry} = "Failed updating ".($defrec eq 'y' ? 'default ' : '').
    38684036        "record '$oldrec->{host} $typemap{$oldrec->{type}} $oldrec->{val}', TTL $oldrec->{ttl} ($msg)";
     
    39754143    my $msg = $@;
    39764144    eval { $dbh->rollback; };
    3977     if ($config{log_failures}) {
     4145    if ($self->{log_failures}) {
    39784146      $logdata{entry} = "Error deleting ".($defrec eq 'y' ? 'default record' : 'record').
    39794147        " '$oldrec->{host} $typemap{$oldrec->{type}} $oldrec->{val}', TTL $oldrec->{ttl} ($msg)";
     
    40114179  $args{logtype} = 'domain' if $args{logtype} eq 'dom';         # hack pthui
    40124180  return if !grep /^$args{logtype}$/, ('group', 'domain', 'revzone', 'user');
    4013 
    4014   $args{logtype} = 'revzone' if $args{logtype} eq 'rdns';       # hack pthui
    40154181
    40164182  my $sql = "SELECT count(*) FROM log ".
     
    40474213
    40484214  # Sorting defaults
    4049   $args{sortby} = 'stamp' if !$args{sortby};
    4050   $args{sortorder} = 'DESC' if !$args{sortorder};
     4215  $args{sortorder} = 'DESC' if !$args{sortorder} || !grep /^$args{sortorder}$/, ('ASC','DESC');
     4216  $args{sortby} = 'stamp' if !$args{sortby} || $args{sortby} !~ /^[\w_.]+$/;
    40514217  $args{offset} = 0 if !$args{offset} || $args{offset} !~ /^(?:all|\d+)$/;
    40524218
     
    40604226        ($args{filter} ? " AND entry ~* ?" : '').
    40614227        " ORDER BY $args{sortby} $args{sortorder}, log_id $args{sortorder}".
    4062         ($args{offset} eq 'all' ? '' : " LIMIT $config{perpage} OFFSET ".$args{offset}*$config{perpage});
     4228        ($args{offset} eq 'all' ? '' : " LIMIT $self->{perpage} OFFSET ".$args{offset}*$self->{perpage});
    40634229  my $loglist = $dbh->selectall_arrayref($sql, { Slice => {} }, ($args{id}, @filterargs) );
    40644230  $errstr = $dbh->errstr if !$loglist;
     
    41114277    # default;  forward zone types.  technically $type eq 'f' but not worth the error message.
    41124278    $sql .= "stdflag=1 OR stdflag=2";
     4279    $sql .= " AND val < 65280" if $recgroup eq 'fo';  # An extra flag to trim off the pseudotypes as well.
    41134280  }
    41144281  $sql .= " ORDER BY listorder";
     
    42434410      $limiter++;
    42444411##fixme:  how often will this happen on a live site?  fail at max limiter <n>?
     4412# 2013/10/22 only seems to happen when you request an entity that doesn't exist.
    42454413      warn "no results looking for $sql with id $id (depth $limiter)\n";
    42464414      last;
     
    48415009  my %recflags;
    48425010
    4843   my $domsth = $dbh->prepare("SELECT domain_id,domain,status,changed FROM domains WHERE status=1");
    4844   my $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
    4845         "FROM records WHERE domain_id=? AND type < 65280");     # Just exclude all types relating to rDNS
    4846   my $zonesth = $dbh->prepare("UPDATE domains SET changed='n' WHERE domain_id=?");
    4847   $domsth->execute();
    4848   while (my ($domid,$dom,$domstat,$changed) = $domsth->fetchrow_array) {
    4849 ##fixme: need to find a way to block opening symlinked files without introducing a race.
    4850 #       O_NOFOLLOW
    4851 #              If  pathname  is a symbolic link, then the open fails.  This is a FreeBSD extension, which was
    4852 #              added to Linux in version 2.1.126.  Symbolic links in earlier components of the pathname  will
    4853 #              still be followed.
    4854 # but that doesn't help other platforms.  :/
    4855     sysopen(ZONECACHE, "$config{exportcache}/$dom", O_RDWR|O_CREAT);
    4856     flock(ZONECACHE, LOCK_EX);
    4857     if ($changed || -s "$config{exportcache}/$dom" == 0) {
    4858       $recsth->execute($domid);
    4859       while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc) = $recsth->fetchrow_array) {
    4860         next if $recflags{$recid};
    4861 
    4862         $loc = '' if !$loc;     # de-nullify - just in case
    4863 ##fixme:  handle case of record-with-location-that-doesn't-exist better.
    4864 # note this currently fails safe (tested) - records with a location that
    4865 # doesn't exist will not be sent to any client
    4866 #       $loc = '' if !$lochash->{$loc};
    4867 
    4868 ##fixme:  record validity timestamp. tinydns supports fiddling with timestamps.
    4869 # note $ttl must be set to 0 if we want to use tinydns's auto-expiring timestamps.
    4870 # timestamps are TAI64
    4871 # ~~ 2^62 + time()
    4872         my $stamp = '';
    4873 
    4874         # support tinydns' auto-TTL
    4875         $ttl = '' if $ttl == '0';
    4876 
    4877         # Spaces are evil.
    4878         $host =~ s/^\s+//;
    4879         $host =~ s/\s+$//;
    4880         if ($typemap{$type} ne 'TXT') {
    4881           # Leading or trailng spaces could be legit in TXT records.
    4882           $val =~ s/^\s+//;
    4883           $val =~ s/\s+$//;
    4884         }
    4885 
    4886         _printrec_tiny(*ZONECACHE, 'n', \%recflags,
    4887                 $dom, $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp)
    4888                 if *ZONECACHE;
    4889 
    4890         # in case the zone shrunk, get rid of garbage at the end of the file.
    4891         truncate(ZONECACHE, tell(ZONECACHE));
    4892 
    4893         $recflags{$recid} = 1;
    4894       } # while ($recsth)
    4895     }
    4896     # stream from cache, whether freshly created or existing
    4897     print $datafile $_ while <ZONECACHE>;
    4898     close ZONECACHE;
    4899     # mark domain as unmodified
    4900     $zonesth->execute($domid);
    4901   } # while ($domsth)
    4902 
    4903   my $revsth = $dbh->prepare("SELECT rdns_id,revnet,status,changed FROM revzones WHERE status=1 ".
    4904         "ORDER BY masklen(revnet) DESC");
    4905 
    49065011# For reasons unknown, we can't sanely UNION these statements.  Feh.
    49075012# Supposedly it should work though (note last 3 lines):
     
    49185023  my $soasth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
    49195024        "FROM records WHERE rdns_id=? AND type=6");
    4920   $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location ".
     5025  my $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
    49215026        "FROM records WHERE rdns_id=? AND not type=6 ".
    49225027        "ORDER BY masklen(CAST(val AS inet)) DESC, CAST(val AS inet)");
    4923   $zonesth = $dbh->prepare("UPDATE revzones SET changed='n' WHERE rdns_id=?");
     5028  my $revsth = $dbh->prepare("SELECT rdns_id,revnet,status,changed FROM revzones WHERE status=1 ".
     5029        "ORDER BY masklen(revnet) DESC");
     5030  my $zonesth = $dbh->prepare("UPDATE revzones SET changed='n' WHERE rdns_id=?");
    49245031  $revsth->execute();
    49255032  while (my ($revid,$revzone,$revstat,$changed) = $revsth->fetchrow_array) {
     
    49315038# but that doesn't help other platforms.  :/
    49325039    my $tmpzone = NetAddr::IP->new($revzone);
    4933     sysopen(ZONECACHE, "$config{exportcache}/".$tmpzone->network->addr, O_RDWR|O_CREAT);
    4934     flock(ZONECACHE, LOCK_EX);
    4935     if ($changed || -s "$config{exportcache}/".$tmpzone->network->addr == 0) {
    4936       # need to fetch this separately since the rest of the records all (should) have real IPs in val
    4937       $soasth->execute($revid);
    4938       my (@zsoa) = $soasth->fetchrow_array();
    4939       _printrec_tiny(*ZONECACHE,'y',\%recflags,$revzone,
    4940         $zsoa[0],$zsoa[1],$zsoa[2],$zsoa[3],$zsoa[4],$zsoa[5],$zsoa[6],$zsoa[8],'');
    4941 
    4942       $recsth->execute($revid);
    4943       while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc) = $recsth->fetchrow_array) {
    4944         next if $recflags{$recid};
    4945 
    4946         $loc = '' if !$loc;     # de-nullify - just in case
     5040##fixme:  locations/views?  subnet mask?  need to avoid possible collisions with zone/superzone
     5041##        (eg /20 vs /24, starting on .0.0)
     5042    my $cz = $tmpzone->network->addr."-".$tmpzone->masklen;
     5043    my $cachefile = "$self->{exportcache}/$cz";
     5044    my $tmpcache = "$self->{exportcache}/tmp.$cz.$$";
     5045    eval {
     5046
     5047      # only update the cache file if the zone has changed, or if the cache file has nothing in it.
     5048      if ($self->{force_refresh} || $changed || !-e $cachefile || -z $cachefile) {
     5049        open ZONECACHE, ">$tmpcache" or die "Error creating temporary file $tmpcache: $!\n";
     5050
     5051        # need to fetch this separately since the rest of the records all (should) have real IPs in val
     5052        $soasth->execute($revid);
     5053        my (@zsoa) = $soasth->fetchrow_array();
     5054        _printrec_tiny(*ZONECACHE,'y',\%recflags,$revzone,
     5055          $zsoa[0],$zsoa[1],$zsoa[2],$zsoa[3],$zsoa[4],$zsoa[5],$zsoa[6],$zsoa[8],'');
     5056
     5057        $recsth->execute($revid);
     5058        while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc,$stamp,$expires,$stampactive) = $recsth->fetchrow_array) {
     5059          next if $recflags{$recid};
     5060
     5061# not sure this is necessary for revzones.
     5062#         # Spaces are evil.
     5063#         $val =~ s/^\s+//;
     5064#         $val =~ s/\s+$//;
     5065#         if ($typemap{$type} ne 'TXT') {
     5066#           # Leading or trailng spaces could be legit in TXT records.
     5067#           $host =~ s/^\s+//;
     5068#           $host =~ s/\s+$//;
     5069#         }
     5070
     5071          _printrec_tiny(*ZONECACHE, 'y', \%recflags, $revzone,
     5072            $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive)
     5073                if *ZONECACHE;
     5074
     5075          $recflags{$recid} = 1;
     5076
     5077        } # while ($recsth)
     5078
     5079        close ZONECACHE; # force the file to be written
     5080
     5081        # catch obvious write errors that leave an empty temp file
     5082        if (-s $tmpcache) {
     5083          rename $tmpcache, $cachefile
     5084            or die "Error overwriting cache file $cachefile with temporary file: $!\n";
     5085        }
     5086
     5087      } # if $changed or cache filesize is 0
     5088
     5089    };
     5090    if ($@) {
     5091      print "error writing new data for $revzone: $@\n";
     5092      # error!  something borked, and we should be able to fall back on the old cache file
     5093      # report the error, somehow.
     5094    } else {
     5095      # mark zone as unmodified.  Only do this if no errors, that way
     5096      # export failures should recover a little more automatically.
     5097      $zonesth->execute($revid);
     5098    }
     5099    # Always stream the cache (even if stale or obsolete due to errors creating the new cache)
     5100    open CACHE, "<$cachefile";
     5101    print $datafile $_ while <CACHE>;
     5102    close CACHE;
     5103
     5104  } # while ($revsth)
     5105
     5106  my $domsth = $dbh->prepare("SELECT domain_id,domain,status,changed FROM domains WHERE status=1");
     5107  $recsth = $dbh->prepare("SELECT host,type,val,distance,weight,port,ttl,record_id,location,extract(epoch from stamp),expires,stampactive ".
     5108        "FROM records WHERE domain_id=?");      # Just exclude all types relating to rDNS
     5109#       "FROM records WHERE domain_id=? AND type < 65280");     # Just exclude all types relating to rDNS
     5110  $zonesth = $dbh->prepare("UPDATE domains SET changed='n' WHERE domain_id=?");
     5111  $domsth->execute();
     5112  while (my ($domid,$dom,$domstat,$changed) = $domsth->fetchrow_array) {
     5113##fixme: need to find a way to block opening symlinked files without introducing a race.
     5114#       O_NOFOLLOW
     5115#              If  pathname  is a symbolic link, then the open fails.  This is a FreeBSD extension, which was
     5116#              added to Linux in version 2.1.126.  Symbolic links in earlier components of the pathname  will
     5117#              still be followed.
     5118# but that doesn't help other platforms.  :/
     5119    my $cachefile = "$self->{exportcache}/$dom";
     5120    my $tmpcache = "$self->{exportcache}/tmp.$dom.$$";
     5121    eval {
     5122
     5123      # only update the cache file if the zone has changed, or if the cache file has nothing in it.
     5124      if ($self->{force_refresh} || $changed || !-e $cachefile || -z $cachefile) {
     5125        open ZONECACHE, ">$tmpcache" or die "Error creating temporary file $tmpcache: $!\n";
     5126
     5127        $recsth->execute($domid);
     5128        while (my ($host,$type,$val,$dist,$weight,$port,$ttl,$recid,$loc,$stamp,$expires,$stampactive) = $recsth->fetchrow_array) {
     5129          next if $recflags{$recid};
     5130
     5131          # Spaces are evil.
     5132          $host =~ s/^\s+//;
     5133          $host =~ s/\s+$//;
     5134          if ($typemap{$type} ne 'TXT') {
     5135            # Leading or trailng spaces could be legit in TXT records.
     5136            $val =~ s/^\s+//;
     5137            $val =~ s/\s+$//;
     5138          }
     5139
     5140          _printrec_tiny(*ZONECACHE, 'n', \%recflags,
     5141                $dom, $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive)
     5142                if *ZONECACHE;
     5143
     5144          $recflags{$recid} = 1;
     5145
     5146        } # while ($recsth)
     5147
     5148        close ZONECACHE; # force the file to be written
     5149
     5150        # catch obvious write errors that leave an empty temp file
     5151        if (-s $tmpcache) {
     5152          rename $tmpcache, $cachefile
     5153            or die "Error overwriting cache file $cachefile with temporary file: $!\n";
     5154        }
     5155
     5156      } # if $changed or cache filesize is 0
     5157
     5158    };
     5159    if ($@) {
     5160      print "error writing new data for $dom: $@\n";
     5161      # error!  something borked, and we should be able to fall back on the old cache file
     5162      # report the error, somehow.
     5163    } else {
     5164      # mark domain as unmodified.  Only do this if no errors, that way
     5165      # export failures should recover a little more automatically.
     5166      $zonesth->execute($domid);
     5167    }
     5168    # Always stream the cache (even if stale or obsolete due to errors creating the new cache)
     5169    open CACHE, "<$cachefile";
     5170    print $datafile $_ while <CACHE>;
     5171    close CACHE;
     5172
     5173  } # while ($domsth)
     5174
     5175} # end __export_tiny()
     5176
     5177
     5178# Utility sub for __export_tiny above
     5179sub _printrec_tiny {
     5180  my ($datafile,$revrec,$recflags,$zone,$host,$type,$val,$dist,$weight,$port,$ttl,$loc,$stamp,$expires,$stampactive) = @_;
     5181
     5182  $loc = '' if !$loc;   # de-nullify - just in case
    49475183##fixme:  handle case of record-with-location-that-doesn't-exist better.
    49485184# note this currently fails safe (tested) - records with a location that
     
    49505186#       $loc = '' if !$lochash->{$loc};
    49515187
    4952 ##fixme:  record validity timestamp. tinydns supports fiddling with timestamps.
    4953 # note $ttl must be set to 0 if we want to use tinydns's auto-expiring timestamps.
    4954 # timestamps are TAI64
    4955 # ~~ 2^62 + time()
    4956         my $stamp = '';
    4957 
    4958         # support tinydns' auto-TTL
    4959         $ttl = '' if $ttl == '0';
    4960 
    4961         _printrec_tiny(*ZONECACHE, 'y', \%recflags, $revzone,
    4962                 $host, $type, $val, $dist, $weight, $port, $ttl, $loc, $stamp)
    4963                 if *ZONECACHE;
    4964 
    4965         # in case the zone shrunk, get rid of garbage at the end of the file.
    4966         truncate(ZONECACHE, tell(ZONECACHE));
    4967 
    4968         $recflags{$recid} = 1;
    4969       } # while ($recsth)
    4970     }
    4971     # stream from cache, whether freshly created or existing
    4972     print $datafile $_ while <ZONECACHE>;
    4973     close ZONECACHE;
    4974     # mark zone as unmodified
    4975     $zonesth->execute($revid);
    4976   } # while ($domsth)
    4977 
    4978 } # end __export_tiny()
    4979 
    4980 
    4981 # Utility sub for __export_tiny above
    4982 sub _printrec_tiny {
    4983   my ($datafile,$revrec,$recflags,$zone,$host,$type,$val,$dist,$weight,$port,$ttl,$loc,$stamp) = @_;
     5188
     5189## Records that are valid only before or after a set time
     5190
     5191# record due to expire sometime is the complex case.  we don't want to just
     5192# rely on tinydns' auto-adjusting TTLs, because the default TTL in that case
     5193# is one day instead of the SOA minttl as BIND might do.
     5194
     5195# consider the case where a record is set to expire a week ahead, but the next
     5196# day later you want to change it NOW (or as NOWish as you get with your DNS
     5197# management practice).  but now you're stuck, because someone, somewhere,
     5198# has just done a lookup before your latest change was published, and they'll
     5199# be caching that old, broken record for 1 day instead of your zone default
     5200# TTL.
     5201
     5202# $stamp-$ttl is the *latest* we can publish the record with the defined TTL
     5203# to still have the expiry happen as scheduled, but we need to find some
     5204# *earlier* point.  We can maybe guess, and 2x TTL is probably reasonable,
     5205# but we need info on the export frequency.
     5206
     5207# export the normal, non-expiring record up until $stamp-<guesstimate>, then
     5208# switch to exporting a record with the TAI64 stamp and a 0 TTL so tinydns
     5209# takes over TTL management.
     5210
     5211  if ($stampactive) {
     5212    if ($expires) {
     5213      # record expires at $stamp;  decide if we need to keep the TTL and ignore
     5214      # the stamp for a time or if we need to change the TTL to 0 and convert
     5215      # $stamp to TAI64 so tinydns can use $stamp to autoadjust the TTL on the fly.
     5216# extra hack, optimally needs more knowledge of data export frequency
     5217# smack the idiot customer who insists on 0 TTLs;  they can suck up and
     5218# deal with a 10-minute TTL.  especially on scheduled changes.  note this
     5219# should be (export freq * 2), but we don't know the actual export frequency.
     5220$ttl = 300 if $ttl == 0;        #hack phtui
     5221      my $ahead = (86400 < $ttl*2 ? 86400 : $ttl*2);
     5222      if ((time() + $ahead) < $stamp) {
     5223        # more than 2x TTL OR more than one day (whichever is less) from expiry time;  publish normal record
     5224        $stamp = '';
     5225      } else {
     5226        # less than 2x TTL from expiry time, let tinydns take over TTL management and publish the TAI64 stamp.
     5227        $ttl = 0;
     5228        $stamp = unixtai64($stamp);
     5229        $stamp =~ s/\@//;
     5230      }
     5231    } else {
     5232      # record is "active after";  convert epoch from database to TAI64, publish, and collect $200.
     5233      $stamp = unixtai64($stamp);
     5234      $stamp =~ s/\@//;
     5235    }
     5236  } else {
     5237    # flag for active timestamp is false;  don't actually put a timestamp in the output
     5238    $stamp = '';
     5239  }
     5240
     5241  # support tinydns' auto-TTL
     5242  $ttl = '' if $ttl == -1;
     5243# these are WAY FREAKING HIGH - higher even than most TLD registry TTLs!
     5244# NS          259200  => 3d
     5245# all others   86400  => 1d
     5246
     5247  if ($revrec eq 'y') {
     5248    $val = $zone if $val eq '@';
     5249  } else {
     5250    $host = $zone if $host eq '@';
     5251  }
    49845252
    49855253  ## Convert a bare number into an octal-coded pair of octets.
     
    52025470      } elsif ($type == 65281) { # AAAA+PTR
    52035471
    5204 #$$recflags{$val}++;
     5472        $$recflags{$val}++;
    52055473        # treat these as two separate records.  since tinydns doesn't have
    52065474        # a native combined type, we have to create them separately anyway.
    5207         if ($revrec eq 'n') {
    5208           $type = 28;
    5209         } else {
    5210           $type = 12;
    5211         }
    5212         _printrec_tiny($datafile,$revrec,$recflags,$zone,$host,$type,$val,$dist,$weight,$port,$ttl,$loc,$stamp);
     5475        # print both;  a dangling record is harmless, and impossible via web
     5476        # UI anyway
     5477        _printrec_tiny($datafile,$revrec,$recflags,$zone,$host,28,$val,$dist,$weight,$port,$ttl,$loc,$stamp);
     5478        _printrec_tiny($datafile,$revrec,$recflags,$zone,$host,12,$val,$dist,$weight,$port,$ttl,$loc,$stamp);
    52135479##fixme: add a config flag to indicate use of the patch from http://www.fefe.de/dns/
    52145480# type 6 is for AAAA+PTR, type 3 is for AAAA
     
    53095575  my ($subj,$message) = @_;
    53105576
    5311   return if $config{mailhost} eq 'smtp.example.com';   # do nothing if still using default SMTP host.
    5312 
    5313   my $mailer = Net::SMTP->new($config{mailhost}, Hello => "dnsadmin.$config{domain}");
    5314 
    5315   my $mailsender = ($config{mailsender} ? $config{mailsender} : $config{mailnotify});
     5577  return if $self->{mailhost} eq 'smtp.example.com';   # do nothing if still using default SMTP host.
     5578
     5579  my $mailer = Net::SMTP->new($self->{mailhost}, Hello => "dnsadmin.$self->{domain}");
     5580
     5581  my $mailsender = ($self->{mailsender} ? $self->{mailsender} : $self->{mailnotify});
    53165582
    53175583  $mailer->mail($mailsender);
    5318   $mailer->to($config{mailnotify});
    5319   $mailer->data("From: \"$config{mailname}\" <$mailsender>\n",
    5320         "To: <$config{mailnotify}>\n",
     5584  $mailer->to($self->{mailnotify});
     5585  $mailer->data("From: \"$self->{mailname}\" <$mailsender>\n",
     5586        "To: <$self->{mailnotify}>\n",
    53215587        "Date: ".strftime("%a, %d %b %Y %H:%M:%S %z",localtime)."\n",
    53225588        "Subject: $subj\n",
    5323         "X-Mailer: DNSAdmin Notify v".sprintf("%.1d",$DNSDB::VERSION)."\n",
    5324         "Organization: $config{orgname}\n",
     5589        "X-Mailer: DNSAdmin v".$DNSDB::VERSION." Notify\n",
     5590        "Organization: $self->{orgname}\n",
    53255591        "\n$message\n");
    53265592  $mailer->quit;
  • branches/stable/INSTALL

    r263 r548  
    2323    - Net::Whois::Raw
    2424    - Net::DNS
     25    - Net::SMTP
    2526    - DBI
    2627    - DBD::Pg
    27     - NetAddr::IP >= 4.x.  3.x may work, however 4.x has been out for
    28       more than 4 years.
     28    - NetAddr::IP >= 4.0.27
    2929    - Frontier::Responder (only required if using dns-rpc.cgi)
     30    - FCGI (only required if you want to run the RPC script as FastCGI)
    3031- tinydns - support for other DNS server software is planned
     32
    3133
    3234Installing DeepNet DNS Administrator
     
    8991
    9092Basic installation should now be complete!  Log in and start adding
    91 your domains and domain records.
     93your domains and reverse zones and their records.
    9294
    9395A minimal export script is included (export.pl).  This should be modified
  • branches/stable/Makefile

    r546 r548  
    4646        INSTALL COPYING TODO Makefile dnsadmin.spec \
    4747        \
    48         new-dns.sql dns.sql dns.cgi dns-rpc.cgi textrecs.cgi DNSDB.pm vega-import.pl export.pl \
     48        dns.sql dns-1.0-1.2.sql \
     49        \
     50        $(SCRIPTS) $(MODULES) \
    4951        \
    5052        index.shtml \
    5153        \
    52         images/trash2.png images/fwd.png images/ffwd.png images/frev.png \
    53         images/rev.png images/DESC.png images/ASC.png images/tree_open.png images/tree_closed.png \
     54        $(IMAGES) \
    5455        \
    55         templates/adddomain.tmpl templates/addgroup.tmpl templates/addrec.tmpl templates/adduser.tmpl \
    56         templates/axfr.tmpl templates/bulkchange.tmpl templates/bulkdomain.tmpl templates/dberr.tmpl \
    57         templates/deldom.tmpl templates/delgrp.tmpl templates/delloc.tmpl templates/delrec.tmpl \
    58         templates/delrevzone.tmpl templates/deluser.tmpl \
    59         templates/dns.css templates/dnsq.tmpl templates/domlist.tmpl templates/edgroup.tmpl \
    60         templates/editsoa.tmpl templates/footer.tmpl templates/fpnla.tmpl templates/grouptree.css \
    61         templates/grouptree-ie.css templates/grpman.tmpl templates/grptree.tmpl templates/header.tmpl \
    62         templates/lettsearch.tmpl templates/login.tmpl templates/log.tmpl templates/menu.tmpl \
    63         templates/newdomain.tmpl templates/newgrp.tmpl templates/permlist_enabled.tmpl templates/permlist.tmpl \
    64         templates/pgcount.tmpl templates/reclist.tmpl templates/record.tmpl templates/sbox.tmpl \
    65         templates/soadata.tmpl templates/template.tmpl templates/textrecs.tmpl templates/updatesoa.tmpl \
    66         templates/useradmin.tmpl templates/user.tmpl templates/whoisq.tmpl \
     56        $(TEMPLATES) \
    6757        \
    6858        dnsdb.conf
     
    7262
    7363IMAGES = \
    74         images/trash2.png images/fwd.png images/ffwd.png images/frev.png \
    75         images/rev.png images/DESC.png images/ASC.png
     64        images/ASC.png images/DESC.png \
     65        images/ffwd.png images/frev.png images/fwd.png images/rev.png \
     66        images/trash2.png \
     67        images/tree_closed.png images/tree_open.png
    7668
    7769SCRIPTS = \
    78         dns.cgi dns-rpc.cgi textrecs.cgi vega-import.pl export.pl
     70        compact-recs.pl dns.cgi dns-rpc.cgi export.pl textrecs.cgi tiny-import.pl vega-import.pl
    7971
    8072MODULES = DNSDB.pm
    8173
    8274TEMPLATES = \
    83         templates/adddomain.tmpl templates/addgroup.tmpl templates/addrec.tmpl templates/adduser.tmpl \
    84         templates/axfr.tmpl templates/bulkchange.tmpl templates/bulkdomain.tmpl templates/dberr.tmpl \
    85         templates/deldom.tmpl templates/delgrp.tmpl templates/delloc.tmpl templates/delrec.tmpl \
    86         templates/delrevzone.tmpl templates/deluser.tmpl \
     75        templates/adddomain.tmpl templates/addgroup.tmpl templates/addrec.tmpl templates/addrevzone.tmpl \
     76        templates/adduser.tmpl templates/axfr.tmpl templates/badpage.tmpl templates/bulkchange.tmpl \
     77        templates/bulkdomain.tmpl templates/dberr.tmpl templates/deldom.tmpl templates/delgrp.tmpl \
     78        templates/delloc.tmpl templates/delrec.tmpl templates/delrevzone.tmpl templates/deluser.tmpl \
    8779        templates/dns.css templates/dnsq.tmpl templates/domlist.tmpl templates/edgroup.tmpl \
    8880        templates/editsoa.tmpl templates/footer.tmpl templates/fpnla.tmpl templates/grouptree.css \
    8981        templates/grouptree-ie.css templates/grpman.tmpl templates/grptree.tmpl templates/header.tmpl \
    90         templates/lettsearch.tmpl templates/login.tmpl templates/log.tmpl templates/menu.tmpl \
    91         templates/newdomain.tmpl templates/newgrp.tmpl templates/permlist_enabled.tmpl templates/permlist.tmpl \
    92         templates/pgcount.tmpl templates/reclist.tmpl templates/record.tmpl templates/sbox.tmpl \
    93         templates/soadata.tmpl templates/template.tmpl templates/textrecs.tmpl templates/updatesoa.tmpl \
    94         templates/useradmin.tmpl templates/user.tmpl templates/whoisq.tmpl
     82        templates/lettsearch.tmpl templates/location.tmpl templates/loclist.tmpl templates/login.tmpl \
     83        templates/log.tmpl templates/menu.tmpl templates/msgblock.tmpl templates/newdomain.tmpl \
     84        templates/newgrp.tmpl templates/newrevzone.tmpl templates/permlist.tmpl \
     85        templates/pgcount.tmpl templates/reclist.tmpl templates/record.tmpl templates/revzones.tmpl \
     86        templates/sbox.tmpl templates/soadata.tmpl templates/template.tmpl templates/textrecs.tmpl \
     87        templates/updatesoa.tmpl templates/useradmin.tmpl templates/user.tmpl templates/whoisq.tmpl
    9588
    9689CONFIGFILES = dnsdb.conf
     
    108101        @for i in $(SCRIPTS) $(MODULES); do \
    109102                $(INSTALL_SCRIPT) -D $$i $(DESTDIR)${datadir}/$(PKG_LEAF)/$$i ; \
    110                 perl -pi -e 's|use lib '.';     ##uselib##|use lib "${datadir}/$(PKG_LEAF)/";|;' $(DESTDIR)${datadir}/$(PKG_LEAF)/$$i ; \
    111                 perl -pi -e 's|use lib '.';     ##uselib##|use lib "${datadir}/$(PKG_LEAF)/";|;' $(DESTDIR)${datadir}/$(PKG_LEAF)/$$i ; \
     103                perl -pi -e "s|use lib '.';\s+##uselib##|use lib '${datadir}/$(PKG_LEAF)/';|;" $(DESTDIR)${datadir}/$(PKG_LEAF)/$$i ; \
    112104        done
    113105        @$(INSTALL) -d $(DESTDIR)${sysconfdir}/$(CFG_LEAF)/
     
    120112                        $(INSTALL_DATA) $$i $(DESTDIR)${sysconfdir}/$(CFG_LEAF)/ ; \
    121113                fi ; \
    122                 perl -pi -e 's|"/etc/dnsdb";    ##CFG_LEAF##|"${sysconfdir}/$(CFG_LEAF)";|;' $(DESTDIR)${datadir}/$(PKG_LEAF)/DNSDB.pm ; \
     114                perl -pi -e 's|"/etc/dnsdb/dnsdb.conf",\s+##CFG_LEAF##|"${sysconfdir}/$(CFG_LEAF)/dnsdb.conf",|;' $(DESTDIR)${datadir}/$(PKG_LEAF)/DNSDB.pm ; \
    123115        done
    124 #       # and now munge MyDNSDB.pm so it can find the core library
    125 #       perl -pi -e 's|##uselib##|use lib "${libdir}/dnsdb";|;' $(DESTDIR)${sysconfdir}/dnsdb/MyDNSDB.pm
    126116
    127117#clean:
  • branches/stable/UPGRADE

    r530 r548  
    11$Id$
    22
    3 Upgrading from a previous release
    4 =================================
     3DeepNet DNS Administrator - Upgrade Notes
     4=========================================
    55
    661.0 -> 1.2
     7  - Make sure your NetAddr::IP version is 4.027 or greater.  Older versions
     8    have been found to be missing key methods.
     9
    710  - FastCGI is now supported for RPC.  Install the FCGI module to support
    811    this, and rename or symlink:
  • branches/stable/compact-recs.pl

    r547 r548  
    2323use warnings;
    2424
    25 use lib '.';
     25use lib '.';    ##uselib##
    2626use DNSDB;
    2727
     
    120120    $dbh->do("INSERT INTO records (domain_id, rdns_id, host, type, val, ttl, location) VALUES (?,?,?,?,?,?,?)",
    121121        undef, ($dparent, $zone, $patt, $newtype, $cidr, $soa->{minttl}, $ploc) );
     122    $dbh->do("UPDATE revzones SET changed='y' WHERE rdns_id = ?", undef, ($zone));
     123    $dbh->do("UPDATE domains SET changed='y' WHERE domain_id = ?", undef, ($dparent)) if $dparent;
    122124    $dnsdb->_log(rdns_id => $zone, domain_id => $dparent, group_id => 1,
    123125        entry => "A+PTR and/or PTR records in $cidr matching $patt replaced by $typemap{$newtype} record for $cidr");
  • branches/stable/dns-1.0-1.2.sql

    r547 r548  
    4040\.
    4141 
    42 SELECT pg_catalog.setval('default_rev_records_record_id_seq', 5, false);
     42SELECT pg_catalog.setval('default_rev_records_record_id_seq', 4, true);
    4343
    4444ALTER TABLE domains ADD COLUMN changed boolean DEFAULT true NOT NULL;
     
    7272ALTER TABLE records ADD COLUMN rdns_id INTEGER DEFAULT 0 NOT NULL;
    7373ALTER TABLE records ADD COLUMN location character varying (4) DEFAULT '' NOT NULL;
     74-- Scheduled changes.
     75ALTER TABLE records ADD COLUMN stamp TIMESTAMP WITH TIME ZONE DEFAULT 'epoch' NOT NULL;
     76ALTER TABLE records ADD COLUMN expires boolean DEFAULT 'n' NOT NULL;
     77ALTER TABLE records ADD COLUMN stampactive boolean DEFAULT 'n' NOT NULL;
    7478
    7579-- ~120s -> 75s performance boost on 100K records when always exporting all records
     
    171175\.
    172176
     177-- and readd the primary key
     178ALTER TABLE ONLY rectypes
     179     ADD CONSTRAINT rectypes_pkey PRIMARY KEY (val, name);
     180
    173181-- Update dbversion
    174182UPDATE misc SET value='1.2' WHERE key='dbversion';
  • branches/stable/dns-rpc.cgi

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2012,2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
     
    4141
    4242my $methods = {
     43#sub getPermissions {
     44#sub changePermissions {
     45#sub comparePermissions {
     46#sub changeGroup {
    4347        'dnsdb.addDomain'       => \&addDomain,
    4448        'dnsdb.delZone'         => \&delZone,
     49#sub domainName {
     50#sub revName {
     51        'dnsdb.domainID'        => \&domainID,
     52#sub revID {
    4553        'dnsdb.addRDNS'         => \&addRDNS,
     54#sub getZoneCount {
     55#sub getZoneList {
     56#sub getZoneLocation {
    4657        'dnsdb.addGroup'        => \&addGroup,
    4758        'dnsdb.delGroup'        => \&delGroup,
     59#sub getChildren {
     60#sub groupName {
     61#sub getGroupCount {
     62#sub getGroupList {
     63#sub groupID {
    4864        'dnsdb.addUser'         => \&addUser,
     65#sub getUserCount {
     66#sub getUserList {
     67#sub getUserDropdown {
    4968        'dnsdb.updateUser'      => \&updateUser,
    5069        'dnsdb.delUser'         => \&delUser,
     70#sub userFullName {
     71#sub userStatus {
     72#sub getUserData {
     73#sub addLoc {
     74#sub updateLoc {
     75#sub delLoc {
     76#sub getLoc {
     77#sub getLocCount {
     78#sub getLocList {
    5179        'dnsdb.getLocDropdown'  => \&getLocDropdown,
    5280        'dnsdb.getSOA'          => \&getSOA,
     81#sub updateSOA {
    5382        'dnsdb.getRecLine'      => \&getRecLine,
    54         'dnsdb.getDomRecs'      => \&getDomRecs,
     83        'dnsdb.getRecList'      => \&getRecList,
    5584        'dnsdb.getRecCount'     => \&getRecCount,
    5685        'dnsdb.addRec'          => \&addRec,
    5786        'dnsdb.updateRec'       => \&updateRec,
     87#sub downconvert {
    5888        'dnsdb.addOrUpdateRevRec'       => \&addOrUpdateRevRec,
    5989        'dnsdb.delRec'          => \&delRec,
     
    6292#sub getLogEntries {}
    6393        'dnsdb.getRevPattern'   => \&getRevPattern,
     94        'dnsdb.getTypelist'     => \&getTypelist,
     95        'dnsdb.getTypemap'      => \&getTypemap,
     96        'dnsdb.getReverse_typemap'      => \&getReverse_typemap,
     97#sub parentID {
     98#sub isParent {
    6499        'dnsdb.zoneStatus'      => \&zoneStatus,
    65100        'dnsdb.getZonesByCIDR'  => \&getZonesByCIDR,
     101#sub importAXFR {
     102#sub importBIND {
     103#sub import_tinydns {
     104#sub export {
     105#sub mailNotify {
    66106
    67107        'dnsdb.getMethods'      => \&get_method_list
     
    95135  my $subsys = shift;
    96136  return 1 if grep /$ENV{REMOTE_ADDR}/, @{$dnsdb->{rpcacl}{$subsys}};
     137  warn "$subsys/$ENV{REMOTE_ADDR} not in ACL\n";        # a bit of logging
    97138  return 0;
    98139}
     
    111152                fullname => ($argref->{fullname} ? $argref->{fullname} : $argref->{rpcuser}) );
    112153  }
     154}
     155
     156# check for defrec and revrec;  only call on subs that deal with records
     157sub _reccheck {
     158  my $argref = shift;
     159  die "Missing defrec and/or revrec flags\n" if !($argref->{defrec} || $argref->{revrec});
    113160}
    114161
     
    145192  _commoncheck(\%args, 'y');
    146193
    147   my ($code, $msg) = $dnsdb->addDomain($args{domain}, $args{group}, $args{state});
    148   die $msg if $code eq 'FAIL';
     194  my ($code, $msg) = $dnsdb->addDomain($args{domain}, $args{group}, $args{state}, $args{location});
     195  die "$msg\n" if $code eq 'FAIL';
    149196  return $msg;  # domain ID
    150197}
     
    167214    ($code,$msg) = $dnsdb->delZone($zoneid, $args{revrec});
    168215  }
    169   die $msg if $code eq 'FAIL';
     216  die "$msg\n" if $code eq 'FAIL';
    170217  return $msg;
    171218}
     
    173220#sub domainName {}
    174221#sub revName {}
    175 #sub domainID {}
     222
     223sub domainID {
     224  my %args = @_;
     225
     226  _commoncheck(\%args, 'y');
     227
     228  my $domid = $dnsdb->domainID($args{domain});
     229  die "$dnsdb->errstr\n" if !$domid;
     230  return $domid;
     231}
     232
    176233#sub revID {}
    177234
     
    204261## optional $inhert arg?
    205262  my ($code,$msg) = $dnsdb->addGroup($args{groupname}, $args{parent_id}, $perms);
    206   die $msg if $code eq 'FAIL';
     263  die "$msg\n" if $code eq 'FAIL';
    207264  return $msg;
    208265}
     
    223280    ($code,$msg) = $dnsdb->delGroup($grpid);
    224281  }
    225   die $msg if $code eq 'FAIL';
     282  die "$msg\n" if $code eq 'FAIL';
    226283  return $msg;
    227284}
     
    249306  }
    250307  my ($code,$msg) = $dnsdb->addUser(@userargs);
    251   die $msg if $code eq 'FAIL';
     308  die "$msg\n" if $code eq 'FAIL';
    252309  return $msg;
    253310}
     
    275332#         have to pass them all in to be overwritten
    276333  my ($code,$msg) = $dnsdb->updateUser(@userargs);
    277   die $msg if $code eq 'FAIL';
     334  die "$msg\n" if $code eq 'FAIL';
    278335  return $msg;
    279336}
     
    286343  die "Missing UID\n" if !$args{uid};
    287344  my ($code,$msg) = $dnsdb->delUser($args{uid});
    288   die $msg if $code eq 'FAIL';
     345  die "$msg\n" if $code eq 'FAIL';
    289346  return $msg;
    290347}
     
    315372
    316373  _commoncheck(\%args);
     374
     375  _reccheck(\%args);
    317376
    318377  my $ret = $dnsdb->getSOA($args{defrec}, $args{revrec}, $args{id});
     
    334393  _commoncheck(\%args);
    335394
     395  _reccheck(\%args);
     396
    336397  my $ret = $dnsdb->getRecLine($args{defrec}, $args{revrec}, $args{id});
    337398
    338   die $dnsdb->errstr if !$ret;
     399  die "$dnsdb->errstr\n" if !$ret;
    339400
    340401  return $ret;
    341402}
    342403
    343 sub getDomRecs {
     404sub getRecList {
    344405  my %args = @_;
    345406
    346407  _commoncheck(\%args);
     408
     409  # deal gracefully with alternate calling convention for args{id}
     410  $args{id} = $args{ID} if !$args{id} && $args{ID};
     411  # ... and fail if we don't have one
     412  die "Missing zone ID\n" if !$args{id};
     413
     414  # set some optional args
     415  $args{offset} = 0 if !$args{offset};
     416## for order, need to map input to column names
     417  $args{order} = 'host' if !$args{order};
     418  $args{direction} = 'ASC' if !$args{direction};
     419  $args{defrec} = 'n' if !$args{defrec};
     420  $args{revrec} = 'n' if !$args{revrec};
     421
     422  # convert zone name to zone ID, if needed
     423  if ($args{defrec} eq 'n') {
     424    if ($args{revrec} eq 'n') {
     425      $args{id} = $dnsdb->domainID($args{id}) if $args{id} !~ /^\d+$/;
     426    } else {
     427      $args{id} = $dnsdb->revID($args{id}) if $args{id} !~ /^\d+$/
     428    }
     429  }
     430
     431  # fail if we *still* don't have a valid zone ID
     432  die "$dnsdb->errstr\n" if !$args{id};
     433
     434  # and finally retrieve the records.
     435  my $ret = $dnsdb->getRecList(defrec => $args{defrec}, revrec => $args{revrec}, id => $args{id},
     436        offset => $args{offset}, nrecs => $args{nrecs}, sortby => $args{sortby},
     437        sortorder => $args{sortorder}, filter => $args{filter});
     438  die "$dnsdb->errstr\n" if !$ret;
     439
     440  return $ret;
     441}
     442
     443sub getRecCount {
     444  my %args = @_;
     445
     446  _commoncheck(\%args);
     447
     448  _reccheck(\%args);
    347449
    348450  # set some optional args
     
    353455  $args{direction} = 'ASC' if !$args{direction};
    354456
    355   my $ret = $dnsdb->getDomRecs(defrec => $args{defrec}, revrec => $args{revrec}, id => $args{id},
    356         offset => $args{offset}, sortby => $args{sortby}, sortorder => $args{sortorder},
    357         filter => $args{filter});
    358 
    359   die $dnsdb->errstr if !$ret;
     457  my $ret = $dnsdb->getRecCount($args{defrec}, $args{revrec}, $args{id}, $args{filter});
     458
     459  die "$dnsdb->errstr\n" if !$ret;
    360460
    361461  return $ret;
    362462}
    363463
    364 sub getRecCount {
    365   my %args = @_;
    366 
    367   _commoncheck(\%args);
    368 
    369   # set some optional args
    370   $args{nrecs} = 'all' if !$args{nrecs};
    371   $args{nstart} = 0 if !$args{nstart};
    372 ## for order, need to map input to column names
    373   $args{order} = 'host' if !$args{order};
    374   $args{direction} = 'ASC' if !$args{direction};
    375 
    376   my $ret = $dnsdb->getRecCount($args{defrec}, $args{revrec}, $args{id}, $args{filter});
    377 
    378   die $dnsdb->errstr if !$ret;
    379 
    380   return $ret;
    381 }
    382 
    383464sub addRec {
    384465  my %args = @_;
     
    386467  _commoncheck(\%args, 'y');
    387468
     469  _reccheck(\%args);
    388470  _loccheck(\%args);
    389471  _ttlcheck(\%args);
    390472
     473  # allow passing text types rather than DNS integer IDs
     474  $args{type} = $DNSDB::reverse_typemap{$args{type}} if $args{type} !~ /^\d+$/;
     475
    391476  my @recargs = ($args{defrec}, $args{revrec}, $args{parent_id},
    392         \$args{name}, \$args{type}, \$args{address}, $args{ttl}, $args{location});
     477        \$args{name}, \$args{type}, \$args{address}, $args{ttl}, $args{location},
     478        $args{expires}, $args{stamp});
    393479  if ($args{type} == $DNSDB::reverse_typemap{MX} or $args{type} == $DNSDB::reverse_typemap{SRV}) {
    394480    push @recargs, $args{distance};
     
    401487  my ($code, $msg) = $dnsdb->addRec(@recargs);
    402488
    403   die $msg if $code eq 'FAIL';
     489  die "$msg\n" if $code eq 'FAIL';
    404490  return $msg;
    405491}
     
    410496  _commoncheck(\%args, 'y');
    411497
     498  _reccheck(\%args);
     499
     500  # put some caller-friendly names in their rightful DB column places
     501  $args{val} = $args{address};
     502  $args{host} = $args{name};
     503
    412504  # get old line, so we can update only the bits that the caller passed to change
    413   # note we subbed address for val since it's a little more caller-friendly
    414505  my $oldrec = $dnsdb->getRecLine($args{defrec}, $args{revrec}, $args{id});
    415   foreach my $field (qw(name type address ttl location distance weight port)) {
     506  foreach my $field (qw(host type val ttl location expires distance weight port)) {
    416507    $args{$field} = $oldrec->{$field} if !$args{$field} && defined($oldrec->{$field});
    417508  }
     509  # stamp has special handling when blank or 0.  "undefined" from the caller should mean "don't change"
     510  $args{stamp} = $oldrec->{stamp} if !defined($args{stamp}) && defined($oldrec->{stamp});
     511
     512  # allow passing text types rather than DNS integer IDs
     513  $args{type} = $DNSDB::reverse_typemap{$args{type}} if $args{type} !~ /^\d+$/;
    418514
    419515  # note dist, weight, port are not required on all types;  will be ignored if not needed.
    420516  # parent_id is the "primary" zone we're updating;  necessary for forward/reverse voodoo
    421517  my ($code, $msg) = $dnsdb->updateRec($args{defrec}, $args{revrec}, $args{id}, $args{parent_id},
    422         \$args{name}, \$args{type}, \$args{address}, $args{ttl}, $args{location},
     518        \$args{host}, \$args{type}, \$args{val}, $args{ttl}, $args{location},
     519        $args{expires}, $args{stamp},
    423520        $args{distance}, $args{weight}, $args{port});
    424521
    425   die $msg if $code eq 'FAIL';
     522  die "$msg\n" if $code eq 'FAIL';
    426523  return $msg;
    427524}
     
    443540      # We need to strip the CIDR mask on IPv4 /32 assignments, or we just add a new record all the time.
    444541      my $filt = ($cidr->{isv6} || $cidr->masklen != 32 ? "$cidr" : $cidr->addr);
    445       my $reclist = $dnsdb->getDomRecs(defrec => 'n', revrec => 'y',
     542      my $reclist = $dnsdb->getRecList(defrec => 'n', revrec => 'y',
    446543        id => $zonelist->[0]->{rdns_id}, filter => $filt);
    447544      if (scalar(@$reclist) == 0) {
     
    477574    # that spans multiple reverse zones (eg, /23 CIDR -> 2 /24 rzones)
    478575    foreach my $zdata (@$zonelist) {
    479       my $reclist = $dnsdb->getDomRecs(defrec => 'n', revrec => 'y',
     576      my $reclist = $dnsdb->getRecList(defrec => 'n', revrec => 'y',
    480577        id => $zdata->{rdns_id}, filter => $zdata->{revnet});
    481578      if (scalar(@$reclist) == 0) {
     
    502599  _commoncheck(\%args, 'y');
    503600
     601  _reccheck(\%args);
     602
    504603  my ($code, $msg) = $dnsdb->delRec($args{defrec}, $args{recrev}, $args{id});
    505604
    506   die $msg if $code eq 'FAIL';
     605  die "$msg\n" if $code eq 'FAIL';
    507606  return $msg;
    508607}
     
    527626      if ($args{delsubs}) {
    528627        # Delete ALL EVARYTHING!!one11!! in $args{cidr}
    529         my $reclist = $dnsdb->getDomRecs(defrec => 'n', revrec => 'y', id => $zonelist->[0]->{rdns_id});
     628        my $reclist = $dnsdb->getRecList(defrec => 'n', revrec => 'y', id => $zonelist->[0]->{rdns_id});
    530629        foreach my $rec (@$reclist) {
    531630          my $reccidr = new NetAddr::IP $rec->{val};
     
    554653        # We need to strip the CIDR mask on IPv4 /32 assignments, or we can't find single-IP records
    555654        my $filt = ($cidr->{isv6} || $cidr->masklen != 32 ? "$cidr" : $cidr->addr);
    556         my $reclist = $dnsdb->getDomRecs(defrec => 'n', revrec => 'y',
     655        my $reclist = $dnsdb->getRecList(defrec => 'n', revrec => 'y',
    557656          id => $zonelist->[0]->{rdns_id}, filter => $filt, sortby => 'val', sortorder => 'DESC');
    558657        foreach my $rec (@$reclist) {
     
    563662          if ($args{delforward} || $rec->{type} == 12) {
    564663            my ($code,$msg) = $dnsdb->delRec('n', 'y', $rec->{record_id});
    565             die $msg if $code eq 'FAIL';
     664            die "$msg\n" if $code eq 'FAIL';
    566665            return $msg;
    567666          } else {
    568667            my $ret = $dnsdb->downconvert($rec->{record_id}, $DNSDB::reverse_typemap{A});
    569             die $dnsdb->errstr if !$ret;
     668            die "$dnsdb->errstr\n" if !$ret;
    570669            return "A+PTR for $args{cidr} split and PTR removed";
    571670          }
     
    582681    # that spans multiple reverse zones (eg, /23 CIDR -> 2 /24 rzones)
    583682    foreach my $zdata (@$zonelist) {
    584       my $reclist = $dnsdb->getDomRecs(defrec => 'n', revrec => 'y', id => $zdata->{rdns_id});
     683      my $reclist = $dnsdb->getRecList(defrec => 'n', revrec => 'y', id => $zdata->{rdns_id});
    585684      if (scalar(@$reclist) == 0) {
    586685# nothing to do?  or do we (re)add a record based on the parent?
     
    627726}
    628727
    629 #sub getTypelist {}
     728sub getTypelist {
     729  my %args = @_;
     730  _commoncheck(\%args, 'y');
     731
     732  $args{selected} = $reverse_typemap{A} if !$args{selected};
     733
     734  return $dnsdb->getTypelist($args{recgroup}, $args{selected});
     735}
     736
     737sub getTypemap {
     738  my %args = @_;
     739  _commoncheck(\%args, 'y');
     740  return \%typemap;
     741}
     742
     743sub getReverse_typemap {
     744  my %args = @_;
     745  _commoncheck(\%args, 'y');
     746  return \%reverse_typemap;
     747}
     748
    630749#sub parentID {}
    631750#sub isParent {}
  • branches/stable/dns.cgi

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2008-2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2008-2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
     
    3030use Net::DNS;
    3131use DBI;
     32
    3233use Data::Dumper;
    3334
     
    6566$webvar{revrec} = 'n' if !$webvar{revrec};      # non-reverse (domain) records
    6667
    67 # load some local system defaults (mainly DB connect info)
    68 # note this is not *absolutely* fatal, since there's a default dbname/user/pass in DNSDB.pm
    69 # we'll catch a bad DB connect string once we get to trying that
    70 ##fixme:  pass params to loadConfig, and use them there, to allow one codebase to support multiple sites
     68# create a DNSDB object.  this loads some local system defaults and connects to the DB
     69# with the credentials configured
     70##fixme:  pass params for loadConfig, and use them there, to allow one codebase to support multiple sites
    7171my $dnsdb = new DNSDB;
    7272
     
    7575$footer->param(version => $DNSDB::VERSION);
    7676
     77##fixme:  slim chance this could be triggered on errors other than DB failure?
    7778if (!$dnsdb) {
    7879  print "Content-type: text/html\n\n";
     
    8788$header->param(orgname => $dnsdb->{orgname}) if $dnsdb->{orgname} ne 'Example Corp';
    8889
    89 # persistent stuff needed on most/all pages
    90 my $sid = ($webvar{sid} ? $webvar{sid} : undef);
    91 my $session = new CGI::Session("driver:File", $sid, {Directory => $dnsdb->{sessiondir}})
     90my $logingroup;
     91my $curgroup;
     92my @viewablegroups;
     93
     94# retrieve the session ID from our cookie, if possible
     95my $sid = $q->cookie('dnsadmin_session');
     96
     97# see if the session loads
     98my $session = CGI::Session->load("driver:File", $sid, {Directory => $dnsdb->{sessiondir}})
    9299        or die CGI::Session->errstr();
    93 #$sid = $session->id() if !$sid;
     100
    94101if (!$sid) {
    95   # init stuff.  can probably axe this down to just above if'n'when user manipulation happens
    96   $sid = $session->id();
    97   $session->expire($dnsdb->{timeout});
    98 # need to know the "upper" group the user can deal with;  may as well
    99 # stick this in the session rather than calling out to the DB every time.
    100   $session->param('logingroup',1);
    101   $session->param('curgroup',1);        # yes, we *do* need to track this too.  er, probably.
    102   $session->param('domlistsortby','domain');
    103   $session->param('domlistorder','ASC');
    104   $session->param('revzonessortby','revnet');
    105   $session->param('revzonesorder','ASC');
    106   $session->param('useradminsortby','user');
    107   $session->param('useradminorder','ASC');
    108   $session->param('grpmansortby','group');
    109   $session->param('grpmanorder','ASC');
    110   $session->param('reclistsortby','host');
    111   $session->param('reclistorder','ASC');
    112   $session->param('loclistsortby','description');
    113   $session->param('loclistorder','ASC');
    114   $session->param('logsortby','stamp');
    115   $session->param('logorder','DESC');
     102  $webvar{page} = 'login';
     103} else {
     104  # we have a session to load from, maybe
     105  $logingroup = ($session->param('logingroup') ? $session->param('logingroup') : 1);
     106  $curgroup = ($session->param('curgroup') ? $session->param('curgroup') : $logingroup);
     107  # security check - does the user have permission to view this entity?
     108  # this is a prep step used "many" places
     109  $dnsdb->getChildren($logingroup, \@viewablegroups, 'all');
     110  push @viewablegroups, $logingroup;
     111##fixme: make sessions persist through closing the site?
     112# this even bridges browser close too.  hmm...
     113  $webvar{page} = 'domlist' if !$webvar{page};
    116114}
    117115
    118 # Just In Case.  Stale sessions should not be resurrectable.
    119 if ($sid ne $session->id()) {
    120   $sid = '';
    121   changepage(page=> "login", sessexpired => 1);
    122 }
    123 
    124 # normal expiry, more or less
    125 if ($session->is_expired) {
    126   $sid = '';
    127   changepage(page=> "login", sessexpired => 1);
    128 }
    129 
    130 my $logingroup = ($session->param('logingroup') ? $session->param('logingroup') : 1);
    131 my $curgroup = ($session->param('curgroup') ? $session->param('curgroup') : $logingroup);
    132 
    133 # decide which page to spit out...
    134 # also set $webvar{page} before we try to use it.
     116# set $webvar{page} before we try to use it.
    135117$webvar{page} = 'login' if !$webvar{page};
    136118
    137 # per-page startwith, filter, searchsubs
     119## per-page startwith, filter, searchsubs
    138120
    139121##fixme:  complain-munge-and-continue with non-"[a-z0-9-.]" filter and startwith
     
    150132$webvar{searchsubs} =~ s/[^yn]//g if $webvar{searchsubs};
    151133
    152 $session->param($webvar{page}.'startwith', $webvar{startwith}) if defined($webvar{startwith});
    153 $session->param($webvar{page}.'filter', $webvar{filter}) if defined($webvar{filter});
     134# pagination
     135my $perpage = 15;  # Just In Case
     136$perpage = $dnsdb->{perpage} if $dnsdb->{perpage};
     137my $offset = ($webvar{offset} ? $webvar{offset} : 0);
     138
     139## set up "URL to self" (whereami edition)
     140# @#$%@%@#% XHTML - & in a URL must be escaped.  >:(
     141my $uri_self = $ENV{REQUEST_URI};
     142$uri_self =~ s/\&([a-z])/\&amp\;$1/g;
     143
     144# le sigh.  and we need to strip any previous action
     145$uri_self =~ s/\&amp;action=[^&]+//g;
     146
     147# much magic happens.  if startwith or a search string change (to, from, or
     148# across, in the request vs whatever's in the session) then the offset should
     149# be reset to 0 so that the first/prev/next/last widget populates correctly,
     150# and so that the list of whatever we're looking at actually shows things
     151# (since we may have started on page 42 of 300 with a LOOOOONG list, but we
     152# now only need 3 pages for the filtered list).
     153# while we're at it, plonk these into the session for safekeeping.
     154if (defined($webvar{startwith})) {
     155  if ($webvar{startwith} ne $session->param($webvar{page}.'startwith')) {
     156    $uri_self =~ s/\&amp;offset=[^&]//;
     157    $offset = 0;
     158  }
     159  $session->param($webvar{page}.'startwith', $webvar{startwith});
     160}
     161if (defined($webvar{filter})) {
     162  if ($webvar{filter} ne $session->param($webvar{page}.'filter')) {
     163    $uri_self =~ s/\&amp;offset=[^&]//;
     164    $offset = 0;
     165  }
     166  $session->param($webvar{page}.'filter', $webvar{filter})
     167}
    154168$session->param($webvar{page}.'searchsubs', $webvar{searchsubs}) if defined($webvar{searchsubs});
    155169
     170# and now that the search/filter criteria for this page are set, put them in some globals for actual use.
    156171my $startwith = $session->param($webvar{page}.'startwith');
    157172my $filter = $session->param($webvar{page}.'filter');
     
    162177push @filterargs, "^[$startwith]" if $startwith;
    163178push @filterargs, $filter if $filter;
    164 
    165 ## set up "URL to self"
    166 # @#$%@%@#% XHTML - & in a URL must be escaped.  >:(
    167 my $uri_self = $ENV{REQUEST_URI};
    168 $uri_self =~ s/\&([a-z])/\&amp\;$1/g;
    169 
    170 # le sigh.  and we need to strip any previous action
    171 $uri_self =~ s/\&amp;action=[^&]+//g;
    172179
    173180# and search filter options.  these get stored in the session, but discarded
     
    181188
    182189# Fix up $uri_self so we don't lose the session/page
    183 $uri_self .= "?sid=$sid&amp;page=$webvar{page}" if $uri_self =~ m{/dns.cgi$};
    184 $uri_self = "$ENV{SCRIPT_NAME}?sid=$sid&amp;page=$webvar{page}$1" if $uri_self =~ m{/dns.cgi\&(.+)$};
    185 
    186 # pagination
    187 my $perpage = 15;
    188 $perpage = $dnsdb->{perpage} if $dnsdb->{perpage};
    189 my $offset = ($webvar{offset} ? $webvar{offset} : 0);
     190$uri_self .= "?page=$webvar{page}" if $uri_self =~ m{/dns.cgi$};
     191$uri_self = "$ENV{SCRIPT_NAME}?page=$webvar{page}$1" if $uri_self =~ m{/dns.cgi\&(.+)$};
     192
     193## end uri_self monkeying
    190194
    191195# NB:  these must match the field name and SQL ascend/descend syntax respectively
     196# sortby is reset to a suitable "default", then re-reset to whatever the user has
     197# clicked on last in the record=listing subs, but best to put a default here.
    192198my $sortby = "domain";
    193199my $sortorder = "ASC";
    194200
    195 # security check - does the user have permission to view this entity?
    196 # this is a prep step used "many" places
    197 my @viewablegroups;
    198 $dnsdb->getChildren($logingroup, \@viewablegroups, 'all');
    199 push @viewablegroups, $logingroup;
    200 
     201# Create the page template object.  Display a reasonable error page and whine if the template doesn't exist.
    201202my $page;
    202203eval {
     
    217218}
    218219
    219 # handle login redirect
     220my $sesscookie;
     221
     222# handle can-happen-on-(almost)-any-page actions
    220223if ($webvar{action}) {
     224
    221225  if ($webvar{action} eq 'login') {
    222226    # Snag ACL/permissions here too
     
    226230    if ($userdata) {
    227231
     232      # (re)create the session
     233      $session = new CGI::Session("driver:File", $sid, {Directory => $dnsdb->{sessiondir}})
     234        or die CGI::Session->errstr();
     235      $sid = $session->id();
     236
     237      $sesscookie = $q->cookie( -name => 'dnsadmin_session',
     238        -value => $sid,
     239        -expires => "+".$dnsdb->{timeout},
     240        -secure => 0,
     241## fixme:  need to extract root path for cookie, so as to limit cookie to dnsadmin instance
     242#        -path => $url
     243        );
     244
    228245      # set session bits
     246      $session->expire($dnsdb->{timeout});
    229247      $session->param('logingroup',$userdata->{group_id});
    230248      $session->param('curgroup',$userdata->{group_id});
     
    232250      $session->param('username',$webvar{username});
    233251
    234       changepage(page => "domlist");
     252# for reference.  seems we don't need to set these on login any more.
     253#  $session->param('domlistsortby','domain');
     254#  $session->param('domlistorder','ASC');
     255#  $session->param('revzonessortby','revnet');
     256#  $session->param('revzonesorder','ASC');
     257#  $session->param('useradminsortby','user');
     258#  $session->param('useradminorder','ASC');
     259#  $session->param('grpmansortby','group');
     260#  $session->param('grpmanorder','ASC');
     261#  $session->param('reclistsortby','host');
     262#  $session->param('reclistorder','ASC');
     263#  $session->param('loclistsortby','description');
     264#  $session->param('loclistorder','ASC');
     265#  $session->param('logsortby','stamp');
     266#  $session->param('logorder','DESC');
     267
     268      ## "recover my link" - tack on request bits and use requested page instead of hardcoding domlist
     269      # this could possibly be compacted by munging changepage a little so we don't have to deconstruct
     270      # and reconstruct the URI argument list.
     271      my %target = (page => "domlist");
     272      if ($webvar{target} && $webvar{target} =~ /\?/) {
     273        my $tmp = (split /\?/, $webvar{target})[1];
     274        $tmp =~ s/^\&//;
     275        my @targs = split /\&/, $tmp;
     276        foreach (@targs) {
     277          my ($k,$v) = split /=/;
     278          $target{$k} = $v if $k;
     279          # if we're going through a "session expired" login, we may have a different
     280          # "current group" than the login group.
     281          $session->param('curgroup', $v) if $k eq 'curgroup';
     282##fixme:  page=record goes "FOOM", sometimes - cause/fix?
     283        }
     284      }
     285      changepage(%target);
    235286
    236287    } else {
     
    243294    $session->flush();
    244295
     296    my $sesscookie = $q->cookie( -name => 'dnsadmin_session',
     297      -value => $sid,
     298      -expires => "-1",
     299      -secure => 0,
     300## fixme:  need to extract root path for cookie, so as to limit cookie to dnsadmin instance
     301#      -path => $url
     302      );
     303
    245304    my $newurl = "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}";
    246305    $newurl =~ s|/[^/]+$|/|;
    247     print "Status: 302\nLocation: $newurl\n\n";
     306    print $q->redirect( -uri => $newurl, -cookie => $sesscookie);
    248307    exit;
    249308
    250   } elsif ($webvar{action} eq 'chgroup') {
     309  } elsif ($webvar{action} eq 'chgroup' && $webvar{page} ne 'login') {
    251310    # fiddle session-stored group data
    252311    # magic incantation to... uhhh...
     
    276335      changepage(%args);
    277336    }
    278 
    279   }
     337    # add offset back *into* $uri_self if we're also currently looking at a live record list.
     338    if ($webvar{page} eq 'reclist' && $webvar{defrec} eq 'n') {
     339      $uri_self .= "\&amp;offset=$offset";
     340    }
     341  } # done action=chgroup
    280342} # handle global webvar{action}s
     343
    281344
    282345# finally check if the user was disabled.  we could just leave this for logout/session expiry,
     
    295358$dnsdb->initActionLog($session->param('uid'));
    296359
    297 $page->param(sid => $sid) unless $webvar{page} eq 'login';      # no session ID on the login page
     360##
     361## Per-page processing
     362##
    298363
    299364if ($webvar{page} eq 'login') {
    300365
    301   $page->param(loginfailed => 1) if $webvar{loginfailed};
    302   $page->param(sessexpired => 1) if $webvar{sessexpired};
     366  my $target = $ENV{REQUEST_URI};
     367  $target =~ s/\&/\&amp;/g;
     368  $page->param(target => $target); # needs to be trimmed a little, maybe?
     369
     370  $page->param(sessexpired => 1) if (!$sid && $target !~ m|/$|);
     371
     372  if ($webvar{loginfailed}) {
     373    $page->param(loginfailed => 1);
     374    $webvar{target} =~ s/\&/\&amp;/g;   # XHTML we do (not) love you so
     375    $page->param(target => $webvar{target}) if $webvar{target};
     376  }
     377#  if $webvar{sessexpired};      # or this with below?
     378  if ($session->is_expired) {
     379    $page->param(sessexpired => 1);
     380    $session->delete();   # Just to make sure
     381    $session->flush();
     382  }
    303383  $page->param(version => $DNSDB::VERSION);
     384  $page->param(script_self => ($ENV{SCRIPT_NAME} =~ m|/([^/]+)$|)[0]);
    304385
    305386} elsif ($webvar{page} eq 'domlist' or $webvar{page} eq 'index') {
     
    337418  $webvar{group} = $curgroup if !$webvar{group};
    338419  fill_grouplist("grouplist", $webvar{group});
    339   fill_loclist();
     420  fill_loclist($curgroup, $webvar{defloc} ? $webvar{defloc} : '');
    340421
    341422  if ($session->param('add_failed')) {
     
    363444  $webvar{makeactive} = 0 if !defined($webvar{makeactive});
    364445
    365   my ($code,$msg) = $dnsdb->addDomain($webvar{domain}, $webvar{group}, ($webvar{makeactive} eq 'on' ? 1 : 0));
     446  my ($code,$msg) = $dnsdb->addDomain($webvar{domain}, $webvar{group}, ($webvar{makeactive} eq 'on' ? 1 : 0),
     447        $webvar{defloc});
    366448
    367449  if ($code eq 'OK') {
     
    373455    $session->param('add_failed', 1);
    374456##fixme:  domain a security risk for XSS?
    375     changepage(page => "newdomain", domain => $webvar{domain}, errmsg => $msg,
    376         makeactive => ($webvar{makeactive} ? 'y' : 'n'), group => $webvar{group});
     457    changepage(page => "newdomain", errmsg => $msg, domain => $webvar{domain},
     458        group => $webvar{group}, makeactive => ($webvar{makeactive} ? 'y' : 'n'), defloc => $webvar{defloc});
    377459  }
    378460
     
    640722
    641723    my @recargs = ($webvar{defrec}, $webvar{revrec}, $webvar{parentid},
    642         \$webvar{name}, \$webvar{type}, \$webvar{address}, $webvar{ttl}, $webvar{location});
     724        \$webvar{name}, \$webvar{type}, \$webvar{address}, $webvar{ttl}, $webvar{location},
     725        $webvar{expires}, $webvar{stamp});
    643726    if ($webvar{type} == $reverse_typemap{MX} or $webvar{type} == $reverse_typemap{SRV}) {
    644727      push @recargs, $webvar{distance};
     
    688771    $page->param(ttl            => $recdata->{ttl});
    689772    $page->param(typelist       => $dnsdb->getTypelist($webvar{revrec}, $recdata->{type}));
    690 
     773    if ($recdata->{stampactive}) {
     774      $page->param(stamp => $recdata->{stamp});
     775      $page->param(stamp_until => $recdata->{expires});
     776    }
    691777    if ($webvar{defrec} eq 'n') {
    692778      fill_loclist($curgroup, $recdata->{location});
     
    704790    my ($code,$msg) = $dnsdb->updateRec($webvar{defrec}, $webvar{revrec}, $webvar{id}, $webvar{parentid},
    705791        \$webvar{name}, \$webvar{type}, \$webvar{address}, $webvar{ttl}, $webvar{location},
     792        $webvar{expires}, $webvar{stamp},
    706793        $webvar{distance}, $webvar{weight}, $webvar{port});
    707794
     
    10011088  $page->param(perpage => $perpage);
    10021089
    1003   my $domlist = $dnsdb->getZoneList(revrec => 'n', curgroup => $curgroup);
     1090  my $domlist = $dnsdb->getZoneList(revrec => 'n', curgroup => $curgroup, offset => $offset);
    10041091  my $rownum = 0;
    10051092  foreach my $dom (@{$domlist}) {
     
    16931780
    16941781# start output here so we can redirect pages.
    1695 print "Content-type: text/html\n\n", $header->output;
     1782print $q->header( -cookie => $sesscookie);
     1783print $header->output;
    16961784
    16971785##common bits
     
    17221810  $page->param(inlogingrp => $curgroup == $logingroup);
    17231811
    1724 # fill in the URL-to-self
     1812# fill in the URL-to-self for the group tree and search-by-letter
    17251813  $page->param(whereami => $uri_self);
     1814# fill in general URL-to-self
     1815  $page->param(script_self => "$ENV{SCRIPT_NAME}?".($curgroup ? "curgroup=$curgroup" : ''));
    17261816}
    17271817
     
    17661856
    17671857  my @childlist;
     1858
     1859  # some magic to control bad offsets on group change
     1860  my $grp_uri_self = $uri_self;
     1861  $grp_uri_self =~ s/\&amp;offset=[^&]+// unless ($webvar{page} eq 'reclist' && $webvar{defrec} eq 'n');
    17681862
    17691863  my $grptree = HTML::Template->new(filename => 'templates/grptree.tmpl');
     
    17751869    $row{grpname} = $dnsdb->groupName($_);
    17761870    $row{grpnum} = $_;
    1777     $row{whereami} = $uri_self;
     1871    $row{whereami} = $grp_uri_self;
    17781872    $row{curgrp} = ($_ == $cur);
    17791873    $row{expanded} = $dnsdb->isParent($_, 'group', $cur, 'group');
     
    18051899
    18061900  # handle user check
    1807   my $newurl = "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?sid=$sid";
     1901  my $newurl = "http://$ENV{HTTP_HOST}$ENV{SCRIPT_NAME}?";
    18081902  foreach (sort keys %params) {
    18091903## fixme:  something is undefined here on add location
     
    18141908  $session->flush();
    18151909
    1816   print "Status: 302\nLocation: $newurl\n\n";
     1910  print $q->redirect ( -url => $newurl, -cookie => $sesscookie);
    18171911  exit;
    18181912} # end changepage
     
    19011995  $page->param(ttl      => $soa->{ttl});
    19021996
    1903   my $foo2 = $dnsdb->getDomRecs(defrec => $def, revrec => $rev, id => $id, offset => $webvar{offset},
     1997  my $foo2 = $dnsdb->getRecList(defrec => $def, revrec => $rev, id => $id, offset => $webvar{offset},
    19041998        sortby => $sortby, sortorder => $sortorder, filter => $filter);
    19051999
    19062000  foreach my $rec (@$foo2) {
    19072001    $rec->{type} = $typemap{$rec->{type}};
    1908     $rec->{sid} = $webvar{sid};
    19092002    $rec->{fwdzone} = $rev eq 'n';
    19102003    $rec->{distance} = 'n/a' unless ($rec->{type} eq 'MX' || $rec->{type} eq 'SRV');
     
    19152008    $rec->{record_delete} = ($permissions{admin} || $permissions{record_delete});
    19162009    $rec->{locname} = '' unless ($permissions{admin} || $permissions{location_view});
     2010# Timestamps
     2011    if ($rec->{expires}) {
     2012      $rec->{stamptype} = $rec->{ispast} ? 'expired at' : 'expires at';
     2013    } else {
     2014      $rec->{stamptype} = 'valid after';
     2015    }
     2016    # strip seconds and timezone?  no, not yet.  could probably offer a config knob on this display at some point.
     2017#    $rec->{stamp} =~ s/:\d\d-\d+$//;
     2018    delete $rec->{expires};
     2019    delete $rec->{ispast};
    19172020  }
    19182021  $page->param(reclist => $foo2);
     
    19452048  my $soa = $dnsdb->getSOA($webvar{defrec}, $webvar{revrec}, $webvar{parentid});
    19462049  $page->param(ttl      => ($webvar{ttl} ? $webvar{ttl} : $soa->{minttl}));
     2050  $page->param(stamp_until => ($webvar{expires} eq 'until'));
     2051  $page->param(stamp => $webvar{stamp});
    19472052}
    19482053
     
    20062111  my $parent = shift;
    20072112
    2008   # Fix display/UI bug where if you are not on the first page of the list, and
    2009   # you add a search term or click one of the "starts with" links, you end up
    2010   # on a page showing nothing.
    2011   # For bonus points, this reverts to the original offset on clicking the "All" link (mostly)
    2012   if ($offset ne 'all') {
    2013     $offset-- while ($offset * $perpage) >= $pgcount;
    2014   }
    2015 
    20162113  $page->param(ntot => $pgcount);
    20172114  $page->param(nfirst => (($offset eq 'all' ? 0 : $offset)*$perpage+1));
     
    20442141  fill_fpnla($count);
    20452142
     2143  $sortby = ($webvar{revrec} eq 'n' ? 'domain' : 'revnet');
    20462144# sort/order
    20472145  $session->param($webvar{page}.'sortby', $webvar{sortby}) if $webvar{sortby};
     
    20712169  my $zonelist = $dnsdb->getZoneList(childlist => $childlist, curgroup => $curgroup, revrec => $webvar{revrec},
    20722170        filter => ($filter ? $filter : undef), startwith => ($startwith ? $startwith : undef),
    2073         offset => $webvar{offset}, sortby => $sortby, sortorder => $sortorder
     2171        offset => $offset, sortby => $sortby, sortorder => $sortorder
    20742172        );
    20752173# probably don't need this, keeping for reference for now
     
    22922390  foreach my $col (@$cols) {
    22932391    my %coldata;
    2294     $coldata{sid} = $sid;
    22952392    $coldata{page} = $webvar{page};
    22962393    $coldata{offset} = $webvar{offset} if $webvar{offset};
  • branches/stable/dns.sql

    r547 r548  
    7878    default_location character varying (4) DEFAULT '' NOT NULL
    7979);
     80-- ~2x performance boost iff most zones are fed to output from the cache
     81CREATE INDEX dom_status_index ON domains (status);
     82
    8083
    8184CREATE TABLE revzones (
     
    9093    default_location character varying (4) DEFAULT '' NOT NULL
    9194);
     95CREATE INDEX rev_status_index ON revzones USING btree (status);
    9296
    9397CREATE TABLE groups (
     
    109113    log_id serial NOT NULL,
    110114    domain_id integer,
    111     rdns_id integer,
    112115    user_id integer,
    113116    group_id integer,
     
    115118    name character varying(60),
    116119    entry text,
    117     stamp timestamp with time zone DEFAULT now()
     120    stamp timestamp with time zone DEFAULT now(),
     121    rdns_id integer
    118122);
    119123
     
    134138    record_edit boolean DEFAULT false NOT NULL,
    135139    record_delete boolean DEFAULT false NOT NULL,
     140    user_id integer UNIQUE,
     141    group_id integer UNIQUE,
    136142    record_locchg boolean DEFAULT false NOT NULL,
    137143    location_create boolean DEFAULT false NOT NULL,
    138144    location_edit boolean DEFAULT false NOT NULL,
    139145    location_delete boolean DEFAULT false NOT NULL,
    140     location_view boolean DEFAULT false NOT NULL,
    141     user_id integer UNIQUE,
    142     group_id integer UNIQUE
     146    location_view boolean DEFAULT false NOT NULL
    143147);
    144148
    145149-- Need *two* basic permissions;  one for the initial group, one for the default admin user
    146 COPY permissions (permission_id, "admin", self_edit, group_create, group_edit, group_delete, user_create, user_edit, user_delete, domain_create, domain_edit, domain_delete, record_create, record_edit, record_delete, record_locchg, location_create, location_edit, location_delete, location_view, user_id, group_id) FROM stdin;
    147 1       f       f       f       f       f       f       f       f       t       t       t       t       t       t       f       f       f       f       f       \N      1
    148 2       t       f       f       f       f       f       f       f       f       f       f       f       f       f       f       f       f       f       f       1       \N
     150COPY permissions (permission_id, "admin", self_edit, group_create, group_edit, group_delete, user_create, user_edit, user_delete, domain_create, domain_edit, domain_delete, record_create, record_edit, record_delete, user_id, group_id, record_locchg, location_create, location_edit, location_delete, location_view) FROM stdin;
     1511       f       f       f       f       f       f       f       f       t       t       t       t       t       t       \N      1       f       f       f       f       f
     1522       t       f       f       f       f       f       f       f       f       f       f       f       f       f       1       \N      f       f       f       f       f
    149153\.
    150154
     
    152156CREATE TABLE records (
    153157    domain_id integer NOT NULL DEFAULT 0,
    154     rdns_id integer NOT NULL DEFAULT 0,
    155158    record_id serial NOT NULL,
    156159    host text DEFAULT '' NOT NULL,
     
    162165    ttl integer DEFAULT 7200 NOT NULL,
    163166    description text,
    164     location character varying (4) DEFAULT '' NOT NULL
    165 );
     167    rdns_id integer NOT NULL DEFAULT 0,
     168    location character varying (4) DEFAULT '' NOT NULL,
     169    stamp TIMESTAMP WITH TIME ZONE DEFAULT 'epoch' NOT NULL,
     170    expires boolean DEFAULT 'n' NOT NULL,
     171    stampactive boolean DEFAULT 'n' NOT NULL
     172);
     173CREATE INDEX rec_domain_index ON records USING btree (domain_id);
     174CREATE INDEX rec_revzone_index ON records USING btree (rdns_id);
     175CREATE INDEX rec_types_index ON records USING btree ("type");
    166176
    167177CREATE TABLE rectypes (
     
    309319    ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
    310320
     321ALTER TABLE ONLY default_records
     322    ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
     323
     324ALTER TABLE ONLY users
     325    ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
     326
    311327ALTER TABLE ONLY revzones
    312328    ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
    313329
    314 ALTER TABLE ONLY default_records
    315     ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
    316 
    317 ALTER TABLE ONLY users
    318     ADD CONSTRAINT "$1" FOREIGN KEY (group_id) REFERENCES groups(group_id);
    319 
    320330ALTER TABLE ONLY groups
    321331    ADD CONSTRAINT group_parent FOREIGN KEY (parent_group_id) REFERENCES groups(group_id);
    322332
    323333-- set starting sequence numbers, since we've inserted data before they're active
    324 SELECT pg_catalog.setval('misc_misc_id_seq', 2, false);
    325 SELECT pg_catalog.setval('default_records_record_id_seq', 8, false);
    326 SELECT pg_catalog.setval('default_rev_records_record_id_seq', 5, false);
    327 SELECT pg_catalog.setval('domains_domain_id_seq', 1, false);
    328 SELECT pg_catalog.setval('groups_group_id_seq', 2, false);
    329 SELECT pg_catalog.setval('permissions_permission_id_seq', 3, false);
    330 SELECT pg_catalog.setval('records_record_id_seq', 1, false);
    331 SELECT pg_catalog.setval('users_user_id_seq', 2, false);
     334-- only set the ones that have data loaded with \copy, and obey the convention
     335-- that comes out of pg_dump
     336SELECT pg_catalog.setval('misc_misc_id_seq', 1, true);
     337SELECT pg_catalog.setval('default_records_record_id_seq', 8, true);
     338SELECT pg_catalog.setval('default_rev_records_record_id_seq', 4, true);
     339SELECT pg_catalog.setval('groups_group_id_seq', 1, true);
     340SELECT pg_catalog.setval('permissions_permission_id_seq', 2, true);
     341SELECT pg_catalog.setval('users_user_id_seq', 1, true);
  • branches/stable/dnsdb.conf

    r216 r548  
    22
    33# Database connection info
    4 #dbname=dsndb
    5 #dbuser=dnsdb
    6 #dbpass=dnsdbpwd
    7 #dbhost=dnsdbhost
     4#dbname = dsndb
     5#dbuser = dnsdb
     6#dbpass = dnsdbpwd
     7#dbhost = dnsdbhost
    88
    9 # SOA defaults.  contact and prins may only contain a-z, 0-9, . and -,
    10 # the rest are expected to be numeric.
    11 #contact=hostmaster.example.com
    12 #prins=ns1.example.com
    13 #soattl=7200
    14 #refresh=86400
    15 #retry=14400
    16 #expire=28800
    17 #minttl=900
    18 #ttl=900
    19      
    209# Mail settings
    21 #mailhost=smtp.example.com
    22 #mailnotify=dns@example.com
    23 #mailsender=hostmaster@example.com
    24 #mailname=Example Corp DNS Administrator
    25 #orgname=Example Corp
    26 #domain=example.com
     10#mailhost = smtp.example.com
     11#mailnotify = dns@example.com
     12#mailsender = hostmaster@example.com
     13#mailname = Example Corp DNS Administrator
     14#orgname = Example Corp
     15#domain = example.com
    2716     
    2817# session - note this is fed directly to CGI::Session
    2918# timeout supports (s)econds, (m)inutes, (h)ours, (d)ays, (w)eeks, (M)months, or (y)ears
    30 #timeout=3h
    31 #sessiondir=/var/lib/dnsdb
     19#timeout = 3h
     20#sessiondir = /var/lib/dnsdb
    3221
    33 # misc
     22## misc
     23
    3424# flag to indicate if failed changes should be logged
    35 #log_failures=0
     25#log_failures = 1
    3626# number of entries to display in lists
    37 #perpage=25
     27#perpage = 25
     28# maximum number of FCGI requests to serve before reloading/restarting FCGI
     29#max_fcgi_requests = 10
     30# path for per-zone cache files for export
     31#exportcache = /var/cache/dnsdb
     32
     33# always refresh the cache from the DB on export if 1/on
     34# if 0/off, use the "changed" flag on a zone to determine if we export from
     35#  the DB or read from the existing cache file.
     36#force_refresh = 1
     37
     38# RPC ACL
     39# A comma-separated list starting with an abstract "system name"
     40# (passed by an RPC caller), followed by a list of IP addresses
     41# allowed to make RPC calls with that name.
     42# Finer-grained access control must be handled by the caller.
     43#rpc_iplist = billing, 192.168.0.11
     44#rpc_iplist = billing, 172.12.12.12
     45#rpc_iplist = custportal, 192.168.1.12, 192.168.1.13
  • branches/stable/export.pl

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2012,2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
  • branches/stable/reverse-patterns.html

    r532 r548  
    7373        supported since DNS names may not contain most other
    7474        non-alphanumerics.</p>
     75      <p>%blank% may be used to specifically prevent template expansion on
     76        a segment of a block if desired;  eg, if<br />
     77        192.168.23.0/24 has "unused-%i.example.com" set, adding an A+PTR
     78        template for 192.168.23.48/30 of<br />
     79        "%blank%" will leave 192.168.23.48 through .51 without PTR records
     80        unless specific entries exist for those IPs.<p>
    7581    </div>
    7682  </body>
  • branches/stable/templates/axfr.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi" method="post">
     6<form action="<TMPL_VAR NAME=script_self>" method="post">
    87<fieldset>
    9 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    108<input type="hidden" name="page" value="axfr" />
    119<input type="hidden" name="doit" value="y" />
  • branches/stable/templates/badpage.tmpl

    r544 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<div id="badpage">
    32<TMPL_IF badpage>
  • branches/stable/templates/bulkchange.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
  • branches/stable/templates/bulkdomain.tmpl

    r547 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi">
     6<form action="<TMPL_VAR NAME=script_self>" method="post">
    87<fieldset>
    98
    10 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    119<input type="hidden" name="page" value="bulkchange" />
    1210<input type="hidden" name="offset" value="<TMPL_VAR NAME=offset>" />
  • branches/stable/templates/deldom.tmpl

    r546 r548  
    55<td align="center" valign="top">
    66<h3>Are you really sure you want to delete domain <TMPL_VAR NAME=domain>?</h3>
    7 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=deldom&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=deldom&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
     7<a href="<TMPL_VAR NAME=script_self>&amp;page=deldom&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=deldom&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
    99</td></tr></table>
    1010
  • branches/stable/templates/delgrp.tmpl

    r546 r548  
    55<td align="center" valign="top">
    66<h3>Are you really sure you want to delete group <TMPL_VAR NAME=delgroupname>?</h3>
    7 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delgrp&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delgrp&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
     7<a href="<TMPL_VAR NAME=script_self>&amp;page=delgrp&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=delgrp&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
    99</td></tr></table>
    1010
  • branches/stable/templates/delloc.tmpl

    r546 r548  
    55<td align="center" valign="top">
    66<h3>Are you really sure you want to delete location <TMPL_VAR NAME=location>?</h3>
    7 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delloc&amp;del=cancel&amp;locid=<TMPL_VAR NAME=locid>">cancel</a> &nbsp; | &nbsp;
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delloc&amp;del=ok&amp;locid=<TMPL_VAR NAME=locid>">confirm</a>
     7<a href="<TMPL_VAR NAME=script_self>&amp;page=delloc&amp;del=cancel&amp;locid=<TMPL_VAR NAME=locid>">cancel</a> &nbsp; | &nbsp;
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=delloc&amp;del=ok&amp;locid=<TMPL_VAR NAME=locid>">confirm</a>
    99</td></tr></table>
    1010
  • branches/stable/templates/delrec.tmpl

    r544 r548  
    66<h3>Are you really sure you want to delete record:<br />
    77<TMPL_VAR NAME=host> <TMPL_VAR NAME=ftype> <TMPL_VAR NAME=recval></h3>
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delrec&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;parentid=<TMPL_VAR NAME=parentid>">cancel</a>
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=delrec&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;parentid=<TMPL_VAR NAME=parentid>">cancel</a>
    99 &nbsp; | &nbsp;
    10 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delrec&amp;del=ok&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;parentid=<TMPL_VAR NAME=parentid>">confirm</a>
     10<a href="<TMPL_VAR NAME=script_self>&amp;page=delrec&amp;del=ok&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;parentid=<TMPL_VAR NAME=parentid>">confirm</a>
    1111</td></tr></table>
    1212
  • branches/stable/templates/delrevzone.tmpl

    r546 r548  
    55<td align="center" valign="top">
    66<h3>Are you really sure you want to delete reverse zone <TMPL_VAR NAME=revzone>?</h3>
    7 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delrevzone&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delrevzone&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
     7<a href="<TMPL_VAR NAME=script_self>&amp;page=delrevzone&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=delrevzone&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
    99</td></tr></table>
    1010
  • branches/stable/templates/deluser.tmpl

    r546 r548  
    55<td align="center" valign="top">
    66<h3>Are you really sure you want to delete user <TMPL_VAR NAME=user>?</h3>
    7 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=deluser&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=deluser&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
     7<a href="<TMPL_VAR NAME=script_self>&amp;page=deluser&amp;del=cancel&amp;id=<TMPL_VAR NAME=id>">cancel</a> &nbsp; | &nbsp;
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=deluser&amp;del=ok&amp;id=<TMPL_VAR NAME=id>">confirm</a>
    99</td></tr></table>
    1010
  • branches/stable/templates/dns.css

    r210 r548  
    3636        background-color: #F0F0F0;
    3737}
    38 table.container {
     38.container {
    3939        background-color: #FFFFFF;
    4040        border: none;
     
    165165        text-align: center;
    166166        padding: 5px;
    167         width: 50%;
     167        width: 55%;
    168168}
    169169.warning {
  • branches/stable/templates/dnsq.tmpl

    r100 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    76<TMPL_IF errmsg><div class="errmsg">Query error: <TMPL_VARNAME=errmsg></div></TMPL_IF>
    87
    9 <form action="dns.cgi" method="post">
     8<form action="<TMPL_VAR NAME=script_self>" method="post">
    109<fieldset>
    11 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    1210<input type="hidden" name="page" value="dnsq" />
    1311
  • branches/stable/templates/domlist.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    1817<TMPL_IF domain_create>
    1918<TMPL_IF domlist>
    20 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=newdomain">New Domain</a>
     19<a href="<TMPL_VAR NAME=script_self>&amp;page=newdomain">New Domain</a>
    2120<TMPL_ELSE>
    22 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=newrevzone">New Reverse Zone</a>
     21<a href="<TMPL_VAR NAME=script_self>&amp;page=newrevzone">New Reverse Zone</a>
    2322</TMPL_IF>
    2423</TMPL_IF>
     
    2827<table width="98%" border="0" cellspacing="4" cellpadding="3">
    2928<tr>
    30 <TMPL_LOOP NAME=colheads>       <td class="datahead_<TMPL_IF __first__>l<TMPL_ELSE>s</TMPL_IF>"><a href="dns.cgi?sid=<TMPL_VAR
    31  NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
     29<TMPL_LOOP NAME=colheads>       <td class="datahead_<TMPL_IF __first__>l<TMPL_ELSE>s</TMPL_IF>"><a href="<TMPL_VAR
     30 NAME=script_self>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
    3231 NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR NAME=sortby>&amp;order=<TMPL_VAR NAME=order>"><TMPL_VAR
    3332 NAME=colname></a><TMPL_IF NAME=sortorder>&nbsp;<img alt="<TMPL_VAR NAME=sortorder>" src="images/<TMPL_VAR
     
    4039<TMPL_LOOP name=domtable>
    4140<tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
    42         <td align="left"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=reclist&amp;id=<TMPL_VAR NAME=domain_id>&amp;defrec=n<TMPL_UNLESS domlist>&amp;revrec=y</TMPL_UNLESS>"><TMPL_VAR NAME=domain></a></td>
     41        <td align="left"><a href="<TMPL_VAR NAME=script_self>&amp;page=reclist&amp;id=<TMPL_VAR NAME=domain_id>&amp;defrec=n<TMPL_UNLESS domlist>&amp;revrec=y</TMPL_UNLESS>"><TMPL_VAR NAME=domain></a></td>
    4342        <td><TMPL_IF status>Active<TMPL_ELSE>Inactive</TMPL_IF></td>
    4443        <td><TMPL_VAR name=group></td>
    45 <TMPL_IF domain_edit>   <td align="center"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;id=<TMPL_VAR NAME=domainid>&amp;zonestatus=<TMPL_IF status>domoff<TMPL_ELSE>domon</TMPL_IF>"><TMPL_IF status>deactivate<TMPL_ELSE>activate</TMPL_IF></a></td></TMPL_IF>
    46 <TMPL_IF domain_delete> <td align="center"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_IF domlist>deldom<TMPL_ELSE>delrevzone</TMPL_IF>&amp;id=<TMPL_VAR NAME=domain_id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td></TMPL_IF>
     44<TMPL_IF domain_edit>   <td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;id=<TMPL_VAR NAME=domainid>&amp;zonestatus=<TMPL_IF status>domoff<TMPL_ELSE>domon</TMPL_IF>"><TMPL_IF status>deactivate<TMPL_ELSE>activate</TMPL_IF></a></td></TMPL_IF>
     45<TMPL_IF domain_delete> <td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_IF domlist>deldom<TMPL_ELSE>delrevzone</TMPL_IF>&amp;id=<TMPL_VAR NAME=domain_id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td></TMPL_IF>
    4746</tr>
    4847</TMPL_LOOP>
     48<tr><td colspan="5" align="center"><TMPL_INCLUDE NAME="fpnla.tmpl"></td></tr>
    4949<TMPL_ELSE>
    5050<tr><td colspan="5" align="center">No <TMPL_IF domlist>domains<TMPL_ELSE>reverse zones</TMPL_IF> found</td></tr>
  • branches/stable/templates/edgroup.tmpl

    r207 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi" method="post">
     6<form action="<TMPL_VAR NAME=script_self>" method="post">
    87<fieldset>
    9 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    108<input type="hidden" name="page" value="edgroup" />
    119<input type="hidden" name="grpaction" value="updperms" />
  • branches/stable/templates/editsoa.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    1110<div id="tableholder">
    1211
    13 <form action="dns.cgi" method="post">
     12<form action="<TMPL_VAR NAME=script_self>" method="post">
    1413<fieldset>
    15 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    1614<input type="hidden" name="page" value="updatesoa" />
    1715<input type="hidden" name="id" value="<TMPL_VAR NAME=id>" />
  • branches/stable/templates/footer.tmpl

    r433 r548  
    55<div id="contact">
    66<a href="https://secure.deepnet.cx/trac/dnsadmin">dnsadmin</a> <TMPL_VAR NAME=version>
    7 &copy; 2008-2012 <a href="mailto:kdeugau@deepnet.cx">Kris Deugau</a>/<a href="http://www.deepnet.cx">deepnet</a><br />
     7&copy; 2008-2013 <a href="mailto:kdeugau@deepnet.cx">Kris Deugau</a>/<a href="http://www.deepnet.cx">deepnet</a><br />
    88Written for standards-based browsers (eg <a href="http://www.firefox.com">FireFox</a>/<a href="http://www.mozilla.org">Mozilla</a>)
    99</div>
  • branches/stable/templates/fpnla.tmpl

    r544 r548  
    1 <TMPL_IF navfirst><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=0<TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>"><img src="images/frev.png" alt="[ First ]" />First</a><TMPL_ELSE><img src="images/frev.png" alt="[ First ]" />First</TMPL_IF>&nbsp;
    2 <TMPL_IF navprev><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=<TMPL_VAR NAME=prevoffs><TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>"><img src="images/rev.png" alt="[ Previous ]" />Previous</a><TMPL_ELSE><img src="images/rev.png" alt="[ Previous ]" />Previous</TMPL_IF>&nbsp;
    3 <TMPL_IF navnext><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=<TMPL_VAR NAME=nextoffs><TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>">Next<img src="images/fwd.png" alt="[ Next ]" /></a><TMPL_ELSE>Next<img src="images/fwd.png" alt="[ Next ]" /></TMPL_IF>&nbsp;
    4 <TMPL_IF navlast><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=<TMPL_VAR NAME=lastoffs><TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>">Last<img src="images/ffwd.png" alt="[ Last ]" /></a><TMPL_ELSE>Last<img src="images/ffwd.png" alt="[ Last ]" /></TMPL_IF>&nbsp;
    5 <TMPL_IF navall><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=all<TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>">All</a><TMPL_ELSE><TMPL_UNLESS onepage><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=0<TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VARNAME=revrec></TMPL_IF>"><TMPL_VAR NAME=perpage> per page</a></TMPL_UNLESS></TMPL_IF>
     1<TMPL_IF navfirst><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=0<TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>"><img src="images/frev.png" alt="[ First ]" />First</a><TMPL_ELSE><img src="images/frev.png" alt="[ First ]" />First</TMPL_IF>&nbsp;
     2<TMPL_IF navprev><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=<TMPL_VAR NAME=prevoffs><TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>"><img src="images/rev.png" alt="[ Previous ]" />Previous</a><TMPL_ELSE><img src="images/rev.png" alt="[ Previous ]" />Previous</TMPL_IF>&nbsp;
     3<TMPL_IF navnext><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=<TMPL_VAR NAME=nextoffs><TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>">Next<img src="images/fwd.png" alt="[ Next ]" /></a><TMPL_ELSE>Next<img src="images/fwd.png" alt="[ Next ]" /></TMPL_IF>&nbsp;
     4<TMPL_IF navlast><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=<TMPL_VAR NAME=lastoffs><TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>">Last<img src="images/ffwd.png" alt="[ Last ]" /></a><TMPL_ELSE>Last<img src="images/ffwd.png" alt="[ Last ]" /></TMPL_IF>&nbsp;
     5<TMPL_IF navall><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=all<TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VAR NAME=revrec></TMPL_IF>">All</a><TMPL_ELSE><TMPL_UNLESS onepage><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=curpage>&amp;offset=0<TMPL_IF id>&amp;id=<TMPL_VAR NAME=id></TMPL_IF><TMPL_IF defrec>&amp;defrec=<TMPL_VAR NAME=defrec></TMPL_IF><TMPL_IF revrec>&amp;revrec=<TMPL_VARNAME=revrec></TMPL_IF>"><TMPL_VAR NAME=perpage> per page</a></TMPL_UNLESS></TMPL_IF>
  • branches/stable/templates/grpman.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    1615<tr><td colspan="3" align="center"><TMPL_INCLUDE NAME="lettsearch.tmpl"></td></tr>
    1716<tr>
    18         <td colspan="2"><TMPL_IF edgrp><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=edgroup&amp;gid=<TMPL_VAR NAME=gid>">Edit Current Group</a></TMPL_IF></td>
    19         <td align="right"><TMPL_IF addgrp><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=newgrp">New Group</a></TMPL_IF>
     17        <td colspan="2"><TMPL_IF edgrp><a href="<TMPL_VAR NAME=script_self>&amp;page=edgroup&amp;gid=<TMPL_VAR NAME=gid>">Edit Current Group</a></TMPL_IF></td>
     18        <td align="right"><TMPL_IF addgrp><a href="<TMPL_VAR NAME=script_self>&amp;page=newgrp">New Group</a></TMPL_IF>
    2019</td>
    2120</tr>
     
    2423<table width="98%" border="0" cellspacing="4" cellpadding="3">
    2524<tr>
    26 <TMPL_LOOP NAME=colheads>       <td class="datahead_<TMPL_IF __first__>l<TMPL_ELSE>s</TMPL_IF>"><a href="dns.cgi?sid=<TMPL_VAR
    27  NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
     25<TMPL_LOOP NAME=colheads>       <td class="datahead_<TMPL_IF __first__>l<TMPL_ELSE>s</TMPL_IF>"><a href="<TMPL_VAR
     26 NAME=script_self>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
    2827 NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR NAME=sortby>&amp;order=<TMPL_VAR NAME=order>"><TMPL_VAR
    2928 NAME=colname></a><TMPL_IF NAME=sortorder>&nbsp;<img alt="<TMPL_VAR NAME=sortorder>" src="images/<TMPL_VAR
     
    3736<TMPL_LOOP name=grouptable>
    3837<tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
    39         <td align="left"><TMPL_IF edgrp><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=edgroup&amp;gid=<TMPL_VAR NAME=groupid>"><TMPL_VAR NAME=groupname></a><TMPL_ELSE><TMPL_VAR NAME=groupname></TMPL_IF></td>
     38        <td align="left"><TMPL_IF edgrp><a href="<TMPL_VAR NAME=script_self>&amp;page=edgroup&amp;gid=<TMPL_VAR NAME=groupid>"><TMPL_VAR NAME=groupname></a><TMPL_ELSE><TMPL_VAR NAME=groupname></TMPL_IF></td>
    4039        <td><TMPL_VAR name=pgroup></td>
    4140        <td><TMPL_VAR name=nusers></td>
     
    4342        <td><TMPL_VAR NAME=nrevzones></td>
    4443<TMPL_IF delgrp>
    45         <td align="center"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delgrp&amp;id=<TMPL_VAR NAME=groupid>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td>
     44        <td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=delgrp&amp;id=<TMPL_VAR NAME=groupid>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td>
    4645</TMPL_IF>
    4746</tr>
    4847</TMPL_LOOP>
     48<tr><td colspan="6" align="center"><TMPL_INCLUDE NAME="fpnla.tmpl"></td></tr>
    4949<TMPL_ELSE>
    50 <tr><td colspan="5" align="center">No groups found</td></tr>
     50<tr><td colspan="6" align="center">No groups found</td></tr>
    5151</TMPL_IF>
    5252</table>
  • branches/stable/templates/header.tmpl

    r210 r548  
    2121        <link rel="stylesheet" type="text/css" href="templates/grouptree.css" />
    2222        <!-- [endif] -->
     23
     24        <!-- Custom local stylesheet, if desired -->
     25        <link rel="stylesheet" type="text/css" href="local.css" />
    2326    </head>
    2427<body>
  • branches/stable/templates/location.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    98<TMPL_ELSE>
    109
    11 <form action="dns.cgi" method="post">
     10<form action="<TMPL_VAR NAME=script_self>" method="post">
    1211<fieldset>
    1312
    1413<input type="hidden" name="page" value="location" />
    15 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    1614<TMPL_IF id><input type="hidden" name="id" value="<TMPL_VAR NAME=id>" /></TMPL_IF>
    1715<input type="hidden" name="locact" value="<TMPL_VAR NAME=locact>" />
  • branches/stable/templates/loclist.tmpl

    r546 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    1514</tr>
    1615<TMPL_IF addloc>
    17 <tr><td colspan="3" align="right"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=location">New Location/View</a></td></tr>
     16<tr><td colspan="3" align="right"><a href="<TMPL_VAR NAME=script_self>&amp;page=location">New Location/View</a></td></tr>
    1817</TMPL_IF>
    1918</table>
     
    2120<table width="98%" border="0" cellspacing="4" cellpadding="3" class="csubtable">
    2221<tr>
    23 <TMPL_LOOP NAME=colheads>       <td class="datahead_s"><a href="dns.cgi?sid=<TMPL_VAR
    24  NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
     22<TMPL_LOOP NAME=colheads>       <td class="datahead_s"><a href="<TMPL_VAR
     23 NAME=script_self>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
    2524 NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR NAME=sortby>&amp;order=<TMPL_VAR NAME=order>"><TMPL_VAR
    2625 NAME=colname></a><TMPL_IF NAME=sortorder>&nbsp;<img alt="<TMPL_VAR NAME=sortorder>" src="images/<TMPL_VAR
     
    3231<TMPL_LOOP name=loctable>
    3332<tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
    34         <td align="left"><TMPL_IF edloc><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=location&amp;locact=edit&amp;loc=<TMPL_VAR NAME=location>"><TMPL_VAR NAME=description></a><TMPL_ELSE><TMPL_VAR NAME=description></TMPL_IF></td>
     33        <td align="left"><TMPL_IF edloc><a href="<TMPL_VAR NAME=script_self>&amp;page=location&amp;locact=edit&amp;loc=<TMPL_VAR NAME=location>"><TMPL_VAR NAME=description></a><TMPL_ELSE><TMPL_VAR NAME=description></TMPL_IF></td>
    3534        <td><TMPL_VAR name=iplist></td>
    3635        <td><TMPL_VAR name=group_name></td>
    3736<TMPL_IF delloc>
    38         <td align="center"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delloc&amp;locid=<TMPL_VAR
     37        <td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=delloc&amp;locid=<TMPL_VAR
    3938NAME=location>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td>
    4039</TMPL_IF>
  • branches/stable/templates/log.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    2120      <!-- td>Customer ID</td -->
    2221<tr class="darkrowheader">
    23 <TMPL_LOOP NAME=colheads>       <td><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF
     22<TMPL_LOOP NAME=colheads>       <td><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=page><TMPL_IF
    2423 NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR
    2524 NAME=sortby>&amp;order=<TMPL_VAR NAME=order>&amp;id=<TMPL_VAR NAME=id>&amp;ltype=<TMPL_VAR
     
    4039    </tr>
    4140</TMPL_LOOP>
     41    <tr><td colspan="5" align="center"><TMPL_INCLUDE NAME="fpnla.tmpl"></td></tr>
    4242<TMPL_ELSE>
    4343    <tr class="datalinelight">
  • branches/stable/templates/login.tmpl

    r210 r548  
    1 <form method="post" action="dns.cgi">
     1<form method="post" action="<TMPL_VAR NAME=script_self>">
    22<fieldset>
    33<input type="hidden" name="action" value="login" />
    44<input type="hidden" name="page" value="login" />
     5<input type="hidden" name="target" value="<TMPL_VAR NAME=target>" />
    56
    67<div id="login">
  • branches/stable/templates/menu.tmpl

    r545 r548  
    11<td class="menu">
    22<TMPL_VAR NAME=username> logged in<br />
    3 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;action=logout">Log out</a>
     3<a href="<TMPL_VAR NAME=script_self>&amp;action=logout">Log out</a>
    44<hr />
    5 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=domlist">Domains</a><br />
    6 <TMPL_IF mayrdns><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=revzones">Reverse Zones</a><br /></TMPL_IF>
    7 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=useradmin">Users</a><br />
    8 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=log">Log</a><br />
    9 <TMPL_IF maydefrec><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=reclist&amp;id=<TMPL_VAR NAME=group>&amp;defrec=y">Default Records</a><br />
    10 <TMPL_IF mayrdns><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=reclist&amp;id=<TMPL_VAR NAME=group>&amp;defrec=y&amp;revrec=y">Default Reverse Records</a><br /></TMPL_IF></TMPL_IF>
    11 <TMPL_IF mayloc><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=loclist&amp;id=<TMPL_VAR NAME=group>">Locations/Views</a><br /></TMPL_IF>
    12 <TMPL_IF mayimport><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=axfr">AXFR Import</a><br /></TMPL_IF>
    13 <TMPL_IF maybulk><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=bulkdomain">Bulk Domain Operations</a><br /></TMPL_IF>
     5<a href="<TMPL_VAR NAME=script_self>&amp;page=domlist">Domains</a><br />
     6<TMPL_IF mayrdns><a href="<TMPL_VAR NAME=script_self>&amp;page=revzones">Reverse Zones</a><br /></TMPL_IF>
     7<a href="<TMPL_VAR NAME=script_self>&amp;page=useradmin">Users</a><br />
     8<a href="<TMPL_VAR NAME=script_self>&amp;page=log">Log</a><br />
     9<TMPL_IF maydefrec><a href="<TMPL_VAR NAME=script_self>&amp;page=reclist&amp;id=<TMPL_VAR NAME=group>&amp;defrec=y">Default Records</a><br />
     10<TMPL_IF mayrdns><a href="<TMPL_VAR NAME=script_self>&amp;page=reclist&amp;id=<TMPL_VAR NAME=group>&amp;defrec=y&amp;revrec=y">Default Reverse Records</a><br /></TMPL_IF></TMPL_IF>
     11<TMPL_IF mayloc><a href="<TMPL_VAR NAME=script_self>&amp;page=loclist&amp;id=<TMPL_VAR NAME=group>">Locations/Views</a><br /></TMPL_IF>
     12<TMPL_IF mayimport><a href="<TMPL_VAR NAME=script_self>&amp;page=axfr">AXFR Import</a><br /></TMPL_IF>
     13<TMPL_IF maybulk><a href="<TMPL_VAR NAME=script_self>&amp;page=bulkdomain">Bulk Domain Operations</a><br /></TMPL_IF>
    1414<br />
    15 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=grpman"><TMPL_IF chggrps>Manage<TMPL_ELSE>View</TMPL_IF> groups</a><br />
     15<a href="<TMPL_VAR NAME=script_self>&amp;page=grpman"><TMPL_IF chggrps>Manage<TMPL_ELSE>View</TMPL_IF> groups</a><br />
    1616<hr />
    1717<div id="grptree">
     
    3030<!-- hmm:  <TMPL_VAR NAME=groupname> -->
    3131<hr />
    32 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=dnsq">DNS Query</a><br />
    33 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=whoisq">WHOIS Query</a><br />
     32<a href="<TMPL_VAR NAME=script_self>&amp;page=dnsq">DNS Query</a><br />
     33<a href="<TMPL_VAR NAME=script_self>&amp;page=whoisq">WHOIS Query</a><br />
     34
    3435</td>
  • branches/stable/templates/newdomain.tmpl

    r546 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi">
     6<form action="<TMPL_VAR NAME=script_self>">
    87<fieldset>
    98
    10 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    119<input type="hidden" name="page" value="adddomain" />
    1210<input type="hidden" name="newdomain" value="yes" />
     
    3432<TMPL_IF location_view><TMPL_IF record_locchg>
    3533        <tr class="datalinelight">
    36                 <td>Default location/view:</td>
     34                <td>Location/view:</td>
    3735                <td><select name="defloc">
    3836<TMPL_LOOP name=loclist>                <option value="<TMPL_VAR NAME=loc>"<TMPL_IF selected> selected="selected"</TMPL_IF>><TMPL_VAR NAME=locname></option>
  • branches/stable/templates/newgrp.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi">
     6<form action="<TMPL_VAR NAME=script_self>">
    87<fieldset>
    98
    10 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    119<input type="hidden" name="page" value="newgrp" />
    1210<input type="hidden" name="grpaction" value="add" />
  • branches/stable/templates/newrevzone.tmpl

    r547 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi">
     6<form action="<TMPL_VAR NAME=script_self>">
    87<fieldset>
    98
    10 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    119<input type="hidden" name="page" value="addrevzone" />
    1210<input type="hidden" name="newrevzone" value="yes" />
     
    2826        </tr>
    2927        <tr class="datalinelight">
    30                 <td>Default location/view:</td>
    31                 <td align="left">
    32                         <select name="location">
    33                         <TMPL_LOOP loclist>
    34                         <option value="<TMPL_VAR NAME=loc>"<TMPL_IF selected> selected</TMPL_IF>><TMPL_VAR NAME=locname></option>
    35                         </TMPL_LOOP>
    36                         </select>
    37                 </td>
    38         </tr>
    39         <tr class="datalinelight">
    4028                <td>Add reverse zone to group:</td>
    4129                <td><select name="group">
     
    4735                <td>Make reverse zone active on next DNS propagation</td><td><input type="checkbox" name="makeactive" checked="checked" /></td>
    4836        </tr>
     37<TMPL_IF location_view><TMPL_IF record_locchg>
     38        <tr class="datalinelight">
     39                <td>Location/view:</td>
     40                <td align="left">
     41                        <select name="location">
     42                        <TMPL_LOOP loclist>
     43                        <option value="<TMPL_VAR NAME=loc>"<TMPL_IF selected> selected="selected"</TMPL_IF>><TMPL_VAR NAME=locname></option>
     44                        </TMPL_LOOP>
     45                        </select>
     46                </td>
     47        </tr>
     48</TMPL_IF></TMPL_IF>
    4949        <tr><td colspan="2" class="tblsubmit"><input type="submit" value="Add reverse zone" /></td></tr>
    5050    </table>
  • branches/stable/templates/reclist.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    2524        </td>
    2625        <td colspan="2" align="right">
    27                 <form action="dns.cgi">
     26                <form action="<TMPL_VAR NAME=script_self>">
    2827                <fieldset>
    29                 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    3028                <input type="hidden" name="page" value="reclist" />
    3129                <input type="hidden" name="offset" value="0" />
     
    4139<tr class="darkrowheader">
    4240        <td colspan="3">Records</td>
    43         <td align="center"><a href="textrecs.cgi?sid=<TMPL_VAR NAME=sid>&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>">Plain text</a></td>
    44 <TMPL_IF record_create> <td align="right"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=record&amp;parentid=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=new">Add record</a></td></TMPL_IF>
    45         <td align="right"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=log&amp;id=<TMPL_VAR NAME=id><TMPL_IF logdom>&amp;ltype=dom</TMPL_IF><TMPL_IF logrdns>&amp;ltype=rdns</TMPL_IF>">View log</a></td>
     41        <td align="center"><a href="textrecs.cgi?id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>">Plain text</a></td>
     42<TMPL_IF record_create> <td align="right"><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=new">Add record</a></td></TMPL_IF>
     43        <td align="right"><a href="<TMPL_VAR NAME=script_self>&amp;page=log&amp;id=<TMPL_VAR NAME=id><TMPL_IF logdom>&amp;ltype=dom</TMPL_IF><TMPL_IF logrdns>&amp;ltype=rdns</TMPL_IF>">View log</a></td>
    4644</tr>
    4745
     
    5149<TMPL_IF reclist>
    5250<tr class="darkrowheader">
    53 <TMPL_LOOP NAME=colheads>       <td><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF
     51<TMPL_LOOP NAME=colheads>       <td><a href="<TMPL_VAR NAME=script_self>&amp;page=<TMPL_VAR NAME=page><TMPL_IF
    5452 NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR
    5553 NAME=sortby>&amp;order=<TMPL_VAR NAME=order>&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR
     
    6361<tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
    6462<TMPL_IF fwdzone>
    65         <td><TMPL_IF record_edit><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=record&amp;parentid=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR NAME=record_id>"><TMPL_VAR NAME=host></a><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=host><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF></TMPL_IF></td>
     63        <td><TMPL_IF record_edit><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR
     64 NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR
     65 NAME=record_id>"><TMPL_VAR NAME=host></a><TMPL_IF locname> (<TMPL_VAR
     66 NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=host><TMPL_IF locname> (<TMPL_VAR
     67 NAME=locname>)</TMPL_IF></TMPL_IF><TMPL_IF stampactive><br />(<TMPL_VAR NAME=stamptype> <TMPL_VAR
     68 NAME=stamp>)</TMPL_IF></td>
    6669        <td><TMPL_VAR NAME=type></td>
    6770        <td><TMPL_VAR NAME=val></td>
     
    7073        <td><TMPL_VAR NAME=port></td>
    7174<TMPL_ELSE>
    72         <td><TMPL_IF record_edit><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=record&amp;parentid=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR NAME=record_id>"><TMPL_VAR NAME=val></a><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=val><TMPL_IF locname> (<TMPL_VAR NAME=locname>)</TMPL_IF></TMPL_IF></td>
     75        <td><TMPL_IF record_edit><a href="<TMPL_VAR NAME=script_self>&amp;page=record&amp;parentid=<TMPL_VAR
     76 NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;recact=edit&amp;id=<TMPL_VAR
     77 NAME=record_id>"><TMPL_VAR NAME=val></a><TMPL_IF locname> (<TMPL_VAR
     78 NAME=locname>)</TMPL_IF><TMPL_ELSE><TMPL_VAR NAME=val><TMPL_IF locname> (<TMPL_VAR
     79 NAME=locname>)</TMPL_IF></TMPL_IF><TMPL_IF stampactive><br />(<TMPL_VAR NAME=stamptype> <TMPL_VAR
     80 NAME=stamp>)</TMPL_IF></td>
    7381        <td><TMPL_VAR NAME=type></td>
    7482        <td><TMPL_VAR NAME=host></td>
    7583</TMPL_IF>
    7684        <td><TMPL_VAR NAME=ttl></td>
    77 <TMPL_IF record_delete> <td align="center"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=delrec&amp;id=<TMPL_VAR NAME=record_id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;parentid=<TMPL_VAR NAME=id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td></TMPL_IF>
     85<TMPL_IF record_delete> <td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=delrec&amp;id=<TMPL_VAR NAME=record_id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>&amp;parentid=<TMPL_VAR NAME=id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td></TMPL_IF>
    7886</tr>
    7987</TMPL_LOOP>
     88<tr class="darkrowheader"><td colspan="8" align="center"><TMPL_INCLUDE NAME="fpnla.tmpl"></td></tr>
    8089<TMPL_ELSE>
    81 <tr><td colspan="5">No records found</td></tr>
     90<tr><td colspan="8" align="center">No records found</td></tr>
    8291</TMPL_IF>
    8392</table>
  • branches/stable/templates/record.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    98<TMPL_ELSE>
    109
    11 <form action="dns.cgi" method="post">
     10<form action="<TMPL_VAR NAME=script_self>" method="post">
    1211<fieldset>
    1312
     
    1514<input type="hidden" name="defrec" value="<TMPL_VAR NAME=defrec>" />
    1615<input type="hidden" name="revrec" value="<TMPL_VAR NAME=revrec>" />
    17 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    1816<input type="hidden" name="parentid" value="<TMPL_VAR NAME=parentid>" />
    1917<input type="hidden" name="id" value="<TMPL_VAR NAME=id>" />
    2018<input type="hidden" name="recact" value="<TMPL_VAR NAME=recact>" />
    2119
    22 <table class="container" width="450">
     20<table class="container" width="520">
    2321<tr><td>
    2422
     
    2927<TMPL_IF fwdzone>
    3028                <td>Hostname</td>
    31                 <td><input type="text" name="name" value="<TMPL_VAR NAME=name>" size="30" /></td>
     29                <td><input type="text" name="name" value="<TMPL_VAR NAME=name>" size="30" />&nbsp; &nbsp; <a href="reverse-patterns.html">?</a></td>
    3230<TMPL_ELSE>
    3331                <td>IP Address</td>
     
    4947<TMPL_ELSE>
    5048                <td>Hostname</td>
    51                 <td><input type="text" name="name" value="<TMPL_VAR NAME=name>" size="30" /></td>
     49                <td><input type="text" name="name" value="<TMPL_VAR NAME=name>" size="30" />&nbsp; &nbsp; <a href="reverse-patterns.html">?</a></td>
    5250</TMPL_IF>
    5351        </tr>
     
    8381        </tr>
    8482</TMPL_IF>
     83<TMPL_UNLESS is_default>
     84        <tr class="datalinelight">
     85                <td>Timestamp<br />(blank or 0 disables timestamp)</td>
     86                <td>Valid <input type="radio" name="expires" value="until"<TMPL_IF stamp_until> checked="checked"</TMPL_IF>>until
     87                <input type="radio" name="expires" value="after"<TMPL_UNLESS stamp_until> checked="checked"</TMPL_UNLESS>>after:
     88                <input type="text" name="stamp" value="<TMPL_VAR NAME=stamp>" />
     89                </td>
     90        </tr>
     91</TMPL_UNLESS>
    8592        <tr class="datalinelight">
    8693                <td colspan="2" align="center"><input type="submit" value=" <TMPL_VAR NAME=todo> " /></td>
  • branches/stable/templates/soadata.tmpl

    r545 r548  
    33        <td align="left">SOA:</td>
    44<TMPL_IF mayeditsoa>
    5         <td align="right"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=editsoa&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>">edit</a></td></TMPL_IF>
     5        <td align="right"><a href="<TMPL_VAR NAME=script_self>&amp;page=editsoa&amp;id=<TMPL_VAR NAME=id>&amp;defrec=<TMPL_VAR NAME=defrec>&amp;revrec=<TMPL_VAR NAME=revrec>">edit</a></td></TMPL_IF>
    66</tr>
    77</table>
  • branches/stable/templates/template.tmpl

    r100 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
  • branches/stable/templates/user.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    54<td align="center" valign="top">
    65
    7 <form action="dns.cgi" method="post">
     6<form action="<TMPL_VAR NAME=script_self>" method="post">
    87<fieldset>
    98
    10 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    119<input type="hidden" name="page" value="user" />
    1210<input type="hidden" name="useraction" value="<TMPL_VAR NAME=action>" />
  • branches/stable/templates/useradmin.tmpl

    r545 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    1615<tr><td colspan="3" align="center"><TMPL_INCLUDE NAME="lettsearch.tmpl"></td></tr>
    1716<TMPL_IF adduser>
    18 <tr><td colspan="3" align="right"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=user">New User</a></td></tr>
     17<tr><td colspan="3" align="right"><a href="<TMPL_VAR NAME=script_self>&amp;page=user">New User</a></td></tr>
    1918</TMPL_IF>
    2019</table>
     
    2221<table width="98%" border="0" cellspacing="4" cellpadding="3" class="csubtable">
    2322<tr>
    24 <TMPL_LOOP NAME=colheads>       <td class="datahead_<TMPL_IF __first__>l<TMPL_ELSE>s</TMPL_IF>"><a href="dns.cgi?sid=<TMPL_VAR
    25  NAME=sid>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
     23<TMPL_LOOP NAME=colheads>       <td class="datahead_<TMPL_IF __first__>l<TMPL_ELSE>s</TMPL_IF>"><a href="<TMPL_VAR
     24 NAME=script_self>&amp;page=<TMPL_VAR NAME=page><TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR
    2625 NAME=offset></TMPL_IF>&amp;sortby=<TMPL_VAR NAME=sortby>&amp;order=<TMPL_VAR NAME=order>"><TMPL_VAR
    2726 NAME=colname></a><TMPL_IF NAME=sortorder>&nbsp;<img alt="<TMPL_VAR NAME=sortorder>" src="images/<TMPL_VAR
     
    3332<TMPL_LOOP name=usertable>
    3433<tr class="row<TMPL_IF __odd__>0<TMPL_ELSE>1</TMPL_IF>">
    35         <td align="left"><TMPL_IF eduser><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=user&amp;useraction=edit&amp;user=<TMPL_VAR NAME=user_id>"><TMPL_VAR NAME=username></a><TMPL_ELSE><TMPL_VAR NAME=username></TMPL_IF></td>
     34        <td align="left"><TMPL_IF eduser><a href="<TMPL_VAR NAME=script_self>&amp;page=user&amp;useraction=edit&amp;user=<TMPL_VAR NAME=user_id>"><TMPL_VAR NAME=username></a><TMPL_ELSE><TMPL_VAR NAME=username></TMPL_IF></td>
    3635        <td class="data_nowrap"><TMPL_VAR name=fname></td>
    3736        <td><TMPL_VAR name=type></td>
     
    3938        <td align="center">
    4039<TMPL_IF eduser>
    41                 <a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=useradmin<TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;id=<TMPL_VAR NAME=user_id>&amp;userstatus=<TMPL_IF status>useroff<TMPL_ELSE>useron</TMPL_IF>"><TMPL_IF status>enabled<TMPL_ELSE>disabled</TMPL_IF></a>
     40                <a href="<TMPL_VAR NAME=script_self>&amp;page=useradmin<TMPL_IF NAME=offset>&amp;offset=<TMPL_VAR NAME=offset></TMPL_IF>&amp;id=<TMPL_VAR NAME=user_id>&amp;userstatus=<TMPL_IF status>useroff<TMPL_ELSE>useron</TMPL_IF>"><TMPL_IF status>enabled<TMPL_ELSE>disabled</TMPL_IF></a>
    4241<TMPL_ELSE>
    4342                <TMPL_IF status>enabled<TMPL_ELSE>disabled</TMPL_IF>
     
    4544</td>
    4645<TMPL_IF deluser>
    47         <td align="center"><a href="dns.cgi?sid=<TMPL_VAR NAME=sid>&amp;page=deluser&amp;id=<TMPL_VAR NAME=user_id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td>
     46        <td align="center"><a href="<TMPL_VAR NAME=script_self>&amp;page=deluser&amp;id=<TMPL_VAR NAME=user_id>"><img src="images/trash2.png" alt="[ Delete ]" /></a></td>
    4847</TMPL_IF>
    4948</tr>
    5049</TMPL_LOOP>
     50<tr><td colspan="6" align="center"><TMPL_INCLUDE NAME="fpnla.tmpl"></td></tr>
    5151<TMPL_ELSE>
    52 <tr><td colspan="6">No users found</td></tr>
     52<tr><td colspan="6" align="center">No users found</td></tr>
    5353</TMPL_IF>
    5454</table>
  • branches/stable/templates/whoisq.tmpl

    r100 r548  
    1 <!-- <TMPL_VAR NAME=sid> -->
    21<table class="wholepage"><tr>
    32<TMPL_INCLUDE NAME="menu.tmpl">
     
    76<TMPL_IF errmsg><div class="errmsg">Query error: <TMPL_VARNAME=errmsg></div></TMPL_IF>
    87
    9 <form action="dns.cgi" method="post">
     8<form action="<TMPL_VAR NAME=script_self>" method="post">
    109<fieldset>
    11 <input type="hidden" name="sid" value="<TMPL_VAR NAME=sid>" />
    1210<input type="hidden" name="page" value="whoisq" />
    1311<input type="hidden" name="askaway" value="y" />
  • branches/stable/textrecs.cgi

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2012,2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
     
    5151
    5252# Check the session and if we have a zone ID to retrieve.  Call a failure sub if not.
    53 my $sid = ($webvar{sid} ? $webvar{sid} : undef);
     53my $sid = $q->cookie('dnsadmin_session');
    5454my $session = new CGI::Session("driver:File", $sid, {Directory => $dnsdb->{sessiondir}})
    5555        or die CGI::Session->errstr();
     
    6363
    6464##fixme:  do we support both HTML-plain and true plaintext?  could be done, with another $webvar{}
    65 # Don't die on bad parameters.  Saves munging the return from getDomRecs.
     65# Don't die on bad parameters.  Saves munging the return from getRecList.
    6666#my $page = HTML::Template->new(filename => "$templatedir/textrecs.tmpl",
    6767#       loop_context_vars => 1, global_vars => 1, die_on_bad_params => 0);
     
    7373print qq(Press the "Back" button to return to the standard record list.\n\n);
    7474
    75 my $reclist = $dnsdb->getDomRecs(defrec => $webvar{defrec}, revrec => $webvar{revrec}, id => $webvar{id},
     75my $reclist = $dnsdb->getRecList(defrec => $webvar{defrec}, revrec => $webvar{revrec}, id => $webvar{id},
    7676        sortby => ($webvar{revrec} eq 'n' ? 'type,host' : 'type,val'), sortorder => 'ASC');
    7777foreach my $rec (@$reclist) {
     
    8181  $rec->{val} = "$rec->{distance}  $rec->{val}" if $rec->{type} eq 'MX';
    8282  $rec->{val} = "$rec->{distance}  $rec->{weight}  $rec->{port}  $rec->{val}" if $rec->{type} eq 'SRV';
    83   printf "%-45s\t%d\t%s\t%s\n", $rec->{host}, $rec->{ttl}, $rec->{type}, $rec->{val};
     83  if ($webvar{revrec} eq 'y') {
     84    printf "%-16s\t%d\t%s\t%s\n", $rec->{val}, $rec->{ttl}, $rec->{type}, $rec->{host};
     85  } else {
     86    printf "%-45s\t%d\t%s\t%s\n", $rec->{host}, $rec->{ttl}, $rec->{type}, $rec->{val};
     87  }
    8488}
    8589#$page->param(defrec => ($webvar{defrec} eq 'y'));
  • branches/stable/tiny-import.pl

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2012,2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
     
    2525use strict;
    2626use warnings;
    27 
    28 use lib '.';
     27use POSIX;
     28use Time::TAI64 qw(:tai);
     29
     30use lib '.';    ##uselib##
    2931use DNSDB;
    3032
     
    3739        conv    => 0,
    3840        trial   => 0,
     41        legacy  => 0,
    3942        );
    4043# Handle some command-line arguments
    4144while ($ARGV[0] =~ /^-/) {
    4245  my $arg = shift @ARGV;
    43   usage() if $arg !~ /^-[rct]+$/;
     46  usage() if $arg !~ /^-[rclt]+$/;
    4447  # -r  rewrite imported files to comment imported records
    4548  # -c  coerce/downconvert A+PTR = records to PTR
     49  # -l  swallow A+PTR as-is
    4650  # -t  trial mode;  don't commit to DB or actually rewrite flatfile (disables -r)
    4751  $arg =~ s/^-//;
     
    5054    $importcfg{rw} = 1 if $_ eq 'r';
    5155    $importcfg{conv} = 1 if $_ eq 'c';
     56    $importcfg{legacy} = 1 if $_ eq 'l';
    5257    $importcfg{trial} = 1 if $_ eq 't';
    5358  }
     
    6570            Multiple passes may be necessary if SOA and = records are heavily
    6671            intermixed and not clustered together.
     72        -l  (for "legacy")  Force import of A+PTR records as-is.  Mutually exclusive
     73            with -c.  -l takes precedence as -c is lossy.
    6774        -t  Trial run mode;  spits out records that would be left unimported.
    6875            Disables -r if set.
     
    120127  }
    121128
    122   our $recsth = $dbh->prepare("INSERT INTO records (domain_id,rdns_id,host,type,val,distance,weight,port,ttl,location) ".
    123         " VALUES (?,?,?,?,?,?,?,?,?,?)");
     129  our $recsth = $dbh->prepare("INSERT INTO records (domain_id,rdns_id,host,type,val,distance,weight,port,ttl,location,stamp,expires,stampactive) ".
     130        " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
    124131
    125132  my %deleg;
     
    243250  }
    244251
     252  sub calcstamp {
     253    my $stampin = shift;
     254    my $ttl = shift;
     255    my $pzone = shift;
     256    my $revrec = shift;
     257
     258    return ($ttl, 'n', 'n', '1970-01-01 00:00:00 -0') if !$stampin;
     259
     260##fixme  Yes, this fails for records in 2038 sometime.  No, I'm not going to care for a while.
     261    $stampin = "\@$stampin";    # Time::TAI64 needs the leading @.  Feh.
     262    my $u = tai2unix($stampin);
     263    $stampin = strftime("%Y-%m-%d %H:%M:%S %z", localtime($u));
     264    my $expires = 'n';
     265    if ($ttl) {
     266      # TTL can stay put.
     267    } else {
     268      # TTL on import is 0, almost certainly wrong.  Get the parent zone's SOA and use the minttl.
     269      my $soa = $dnsdb->getSOA('n', $revrec, $pzone);
     270      $ttl = $soa->{minttl};
     271      $expires = 'y';
     272    }
     273    return ($ttl, 'y', $expires, $stampin);
     274  }
    245275
    246276  sub recslurp {
     
    263293      $host =~ s/^=//;
    264294      $host =~ s/\.$//;
    265       $ttl = 0 if !$ttl;
     295      $ttl = -1 if $ttl eq '';
    266296      $stamp = '' if !$stamp;
    267297      $loc = '' if !$loc;
     
    269299      my $fparent = $dnsdb->_hostparent($host);
    270300      my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($ip));
     301
     302      my $stampactive = 'n';
     303      my $expires = 'n';
     304
     305      # can't set a timestamp on an orphaned record.  we'll actually fail import of this record a little later.
     306      if ($fparent || $rparent) {
     307        if ($fparent) {
     308          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     309        } else {
     310          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     311        }
     312      }
     313
    271314      if ($fparent && $rparent) {
    272         $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc);
    273       } else {
    274         if ($importcfg{conv}) {
     315        $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     316      } else {
     317        if ($importcfg{legacy}) {
     318          # Just import it already!  Record may still be subject to downconversion on editing.
     319          $fparent = 0 if !$fparent;
     320          $rparent = 0 if !$rparent;
     321          if ($fparent || $rparent) {
     322            $recsth->execute($fparent, $rparent, $host, 65280, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     323          } else {
     324            # No parents found, cowardly refusing to add a dangling record
     325            push @deferred, $rec unless $nodefer;
     326            $impok = 0;
     327          }
     328        } elsif ($importcfg{conv}) {
    275329          # downconvert A+PTR if forward zone is not found
    276           $recsth->execute(0, $rparent, $host, 12, $ip, 0, 0, 0, $ttl, $loc);
     330          $recsth->execute(0, $rparent, $host, 12, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    277331          $converted++;
    278332        } else {
     
    290344      $host =~ s/\.$//;
    291345      $host =~ s/^\\052/*/;
    292       $ttl = 0 if !$ttl;
    293       $stamp = '' if !$stamp;
    294       $loc = '' if !$loc;
    295       $loc = '' if $loc =~ /^:+$/;
     346      $ttl = -1 if $ttl eq '';
     347      $stamp = '' if !$stamp;
     348      $loc = '' if !$loc;
     349      $loc = '' if $loc =~ /^:+$/;
     350
     351      my $stampactive = 'n';
     352      my $expires = 'n';
     353
    296354      if ($host =~ /\.arpa$/) {
    297355        ($code,$msg) = DNSDB::_zone2cidr($host);
    298356        my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    299         $recsth->execute(0, $rparent, $targ, 5, $msg->addr, 0, 0, 0, $ttl, $loc);
     357        if ($rparent) {
     358          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     359          $recsth->execute(0, $rparent, $targ, 5, $msg->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     360        } else {
     361          push @deferred, $rec unless $nodefer;
     362          $impok = 0;
     363          #  print "$tmporig deferred;  can't find parent zone\n";
     364        }
    300365
    301366##fixme:  automagically convert manually maintained sub-/24 delegations
     
    308373        my $fparent = $dnsdb->_hostparent($host);
    309374        if ($fparent) {
    310           $recsth->execute($fparent, 0, $host, 5, $targ, 0, 0, 0, $ttl, $loc);
     375          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     376          $recsth->execute($fparent, 0, $host, 5, $targ, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    311377        } else {
    312378          push @deferred, $rec unless $nodefer;
     
    324390      $ns =~ s/\.$//;
    325391      $ns = "$ns.ns.$zone" if $ns !~ /\./;
    326       $ttl = 0 if !$ttl;
    327       $stamp = '' if !$stamp;
    328       $loc = '' if !$loc;
    329       $loc = '' if $loc =~ /^:+$/;
     392      $ttl = -1 if $ttl eq '';
     393      $stamp = '' if !$stamp;
     394      $loc = '' if !$loc;
     395      $loc = '' if $loc =~ /^:+$/;
     396
     397      my $stampactive = 'n';
     398      my $expires = 'n';
     399
    330400      if ($zone =~ /\.arpa$/) {
    331401        ($code,$msg) = DNSDB::_zone2cidr($zone);
     
    336406#       if !$rparent;
    337407        if ($rparent) {
    338           $recsth->execute(0, $rparent, $ns, 2, $msg, 0, 0, 0, $ttl, $loc);
     408          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     409          $recsth->execute(0, $rparent, $ns, 2, $msg, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    339410        } else {
    340411          push @deferred, $rec unless $nodefer;
     
    344415        my $fparent = $dnsdb->_hostparent($zone);
    345416        if ($fparent) {
    346           $recsth->execute($fparent, 0, $zone, 2, $ns, 0, 0, 0, $ttl, $loc);
    347           $recsth->execute($fparent, 0, $ns, 2, $ip, 0, 0, 0, $ttl, $loc) if $ip;
     417          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     418          $recsth->execute($fparent, 0, $zone, 2, $ns, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     419          $recsth->execute($fparent, 0, $ns, 2, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive) if $ip;
    348420        } else {
    349421          push @deferred, $rec unless $nodefer;
     
    358430      $rip =~ s/^\^//;
    359431      $rip =~ s/\.$//;
    360       $ttl = 0 if !$ttl;
    361       $stamp = '' if !$stamp;
    362       $loc = '' if !$loc;
    363       $loc = '' if $loc =~ /^:+$/;
     432      $ttl = -1 if $ttl eq '';
     433      $stamp = '' if !$stamp;
     434      $loc = '' if !$loc;
     435      $loc = '' if $loc =~ /^:+$/;
     436
     437      my $stampactive = 'n';
     438      my $expires = 'n';
     439
    364440      my $rparent;
    365441      if (my ($i, $z) = ($rip =~ /^(\d+)\.(\d+-(?:\d+\.){4}in-addr.arpa)$/) ) {
     
    376452      }
    377453      if ($rparent) {
    378         $recsth->execute(0, $rparent, $host, 12, $msg->addr, 0, 0, 0, $ttl, $loc);
     454        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     455        $recsth->execute(0, $rparent, $host, 12, $msg->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    379456      } else {
    380457        push @deferred, $rec unless $nodefer;
     
    389466      $host =~ s/\.$//;
    390467      $host =~ s/^\\052/*/;
    391       $ttl = 0 if !$ttl;
    392       $stamp = '' if !$stamp;
    393       $loc = '' if !$loc;
    394       $loc = '' if $loc =~ /^:+$/;
     468      $ttl = -1 if $ttl eq '';
     469      $stamp = '' if !$stamp;
     470      $loc = '' if !$loc;
     471      $loc = '' if $loc =~ /^:+$/;
     472
     473      my $stampactive = 'n';
     474      my $expires = 'n';
    395475
    396476      my $domid = $dnsdb->_hostparent($host);
    397477      if ($domid) {
    398         $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc);
     478        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     479        $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    399480      } else {
    400481        push @deferred, $rec unless $nodefer;
     
    410491      $master =~ s/\.$//;
    411492      $contact =~ s/\.$//;
    412       $ttl = 0 if !$ttl;
    413       $stamp = '' if !$stamp;
    414       $loc = '' if !$loc;
    415       $loc = '' if $loc =~ /^:+$/;
     493      $ttl = -1 if $ttl eq '';
     494      $stamp = '' if !$stamp;
     495      $loc = '' if !$loc;
     496      $loc = '' if $loc =~ /^:+$/;
     497
     498      my $stampactive = 'n';
     499      my $expires = 'n';
     500
     501##fixme er... what do we do with an SOA with a timestamp?  O_o
     502# fail for now, since there's no clean way I can see to handle this (yet)
     503# maybe (ab)use the -l flag to import as-is?
     504      if ($stamp) {
     505        push @deferred, $rec unless $nodefer;
     506        return 0;
     507      }
     508
     509##fixme: need more magic on TTL, so we can decide whether to use the minttl or newttl
     510#      my $newttl;
     511#      ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'n');
     512#      $ttl = $newttl if !$ttl;
     513
    416514      if ($zone =~ /\.arpa$/) {
    417515        ($code,$msg) = DNSDB::_zone2cidr($zone);
     
    419517                undef, ($msg, $loc));
    420518        my ($rdns) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
    421         $recsth->execute(0, $rdns, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl, $loc);
     519        my $newttl;
     520        ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'y');
     521        $ttl = $newttl if !$ttl;
     522        $recsth->execute(0, $rdns, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl,
     523                $loc, $stamp, $expires, $stampactive);
    422524      } else {
    423525        $dbh->do("INSERT INTO domains (domain,group_id,status,default_location) VALUES (?,1,1,?)",
    424526                undef, ($zone, $loc));
    425527        my ($domid) = $dbh->selectrow_array("SELECT currval('domains_domain_id_seq')");
    426         $recsth->execute($domid, 0, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl, $loc);
     528        my $newttl;
     529        ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'n');
     530        $ttl = $newttl if !$ttl;
     531        $recsth->execute($domid, 0, "$contact:$master", 6, "$refresh:$retry:$expire:$minttl", 0, 0, 0, $ttl,
     532                $loc, $stamp, $expires, $stampactive);
    427533      }
    428534
     
    436542      $host =~ s/\.$//;
    437543      $host = "$host.mx.$zone" if $host !~ /\./;
    438       $ttl = 0 if !$ttl;
    439       $stamp = '' if !$stamp;
    440       $loc = '' if !$loc;
    441       $loc = '' if $loc =~ /^:+$/;
     544      $ttl = -1 if $ttl eq '';
     545      $stamp = '' if !$stamp;
     546      $loc = '' if !$loc;
     547      $loc = '' if $loc =~ /^:+$/;
     548
     549      my $stampactive = 'n';
     550      my $expires = 'n';
    442551
    443552# note we don't check for reverse domains here, because MX records don't make any sense in reverse zones.
     
    447556      my $domid = $dnsdb->_hostparent($zone);
    448557      if ($domid) {
    449         $recsth->execute($domid, 0, $zone, 15, $host, $dist, 0, 0, $ttl, $loc);
    450         $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc) if $ip;
     558        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     559        $recsth->execute($domid, 0, $zone, 15, $host, $dist, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     560        $recsth->execute($domid, 0, $host, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive) if $ip;
    451561      } else {
    452562        push @deferred, $rec unless $nodefer;
     
    461571      $fqdn =~ s/^\\052/*/;
    462572      _deoctal(\$rdata);
    463       $ttl = 0 if !$ttl;
    464       $stamp = '' if !$stamp;
    465       $loc = '' if !$loc;
    466       $loc = '' if $loc =~ /^:+$/;
     573      $ttl = -1 if $ttl eq '';
     574      $stamp = '' if !$stamp;
     575      $loc = '' if !$loc;
     576      $loc = '' if $loc =~ /^:+$/;
     577
     578      my $stampactive = 'n';
     579      my $expires = 'n';
    467580
    468581      if ($fqdn =~ /\.arpa$/) {
    469582        ($code,$msg) = DNSDB::_zone2cidr($fqdn);
    470583        my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    471         $recsth->execute(0, $rparent, $rdata, 16, "$msg", 0, 0, 0, $ttl, $loc);
     584        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     585        $recsth->execute(0, $rparent, $rdata, 16, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    472586      } else {
    473587        my $domid = $dnsdb->_hostparent($fqdn);
    474588        if ($domid) {
    475           $recsth->execute($domid, 0, $fqdn, 16, $rdata, 0, 0, 0, $ttl, $loc);
     589          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     590          $recsth->execute($domid, 0, $fqdn, 16, $rdata, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    476591        } else {
    477592          push @deferred, $rec unless $nodefer;
     
    488603      $ns =~ s/\.$//;
    489604      $ns = "$ns.ns.$fqdn" if $ns !~ /\./;
    490       $ttl = 0 if !$ttl;
    491       $stamp = '' if !$stamp;
    492       $loc = '' if !$loc;
    493       $loc = '' if $loc =~ /^:+$/;
     605      $ttl = -1 if $ttl eq '';
     606      $stamp = '' if !$stamp;
     607      $loc = '' if !$loc;
     608      $loc = '' if $loc =~ /^:+$/;
     609
     610      my $stampactive = 'n';
     611      my $expires = 'n';
     612
     613##fixme er... what do we do with an SOA with a timestamp?  O_o
     614# fail for now, since there's no clean way I can see to handle this (yet)
     615# maybe (ab)use the -l flag to import as-is?
     616      if ($stamp) {
     617        push @deferred, $rec unless $nodefer;
     618        return 0;
     619      }
     620
     621##fixme: need more magic on TTL, so we can decide whether to use the minttl or newttl
     622#      my $newttl;
     623#      ($newttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $minttl, 0, 'n');
    494624
    495625      if ($fqdn =~ /\.arpa$/) {
     
    501631                undef, ($msg, $loc));
    502632          ($rdns) = $dbh->selectrow_array("SELECT currval('revzones_rdns_id_seq')");
     633          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, 2560, 0, 'y');
    503634# this would probably make a lot more sense to do hostmaster.$config{admindomain}
    504           $recsth->execute(0, $rdns, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560", $loc);
    505         }
    506         $recsth->execute(0, $rdns, $ns, 2, "$msg", 0, 0, 0, $ttl, $loc);
     635# otherwise, it's as per the tinydns defaults that work tolerably well on a small scale
     636# serial -> modtime of data file, ref -> 16384, ret -> 2048, exp -> 1048576, min -> 2560
     637          $recsth->execute(0, $rdns, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560",
     638                $loc, $stamp, $expires, $stampactive);
     639        }
     640        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, 2560, $rdns, 'y') if !$stamp;
     641        $recsth->execute(0, $rdns, $ns, 2, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    507642##fixme:  (?)  implement full conversion of tinydns . records?
    508643# -> problem:  A record for NS must be added to the appropriate *forward* zone, not the reverse
    509 #$recsth->execute(0, $rdns, $ns, 1, $ip, 0, 0, 0, $ttl)
     644#$recsth->execute(0, $rdns, $ns, 1, $ip, 0, 0, 0, $ttl, $stamp, $expires, $stampactive)
    510645# ...  auto-A-record simply does not make sense in reverse zones.  Functionally
    511646# I think it would work, sort of, but it's a nasty mess and anyone hosting reverse
     
    521656                undef, ($fqdn, $loc));
    522657          ($domid) = $dbh->selectrow_array("SELECT currval('domains_domain_id_seq')");
    523           $recsth->execute($domid, 0, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560", $loc);
    524         }
    525         $recsth->execute($domid, 0, $fqdn, 2, $ns, 0, 0, 0, $ttl, $loc);
    526         $recsth->execute($domid, 0, $ns, 1, $ip, 0, 0, 0, $ttl, $loc) if $ip;
     658          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, 2560, 0, 'n');
     659          $recsth->execute($domid, 0, "hostmaster.$fqdn:$ns", 6, "16384:2048:1048576:2560", 0, 0, 0, "2560",
     660                $loc, $stamp, $expires, $stampactive);
     661        }
     662        ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n') if !$stamp;
     663        $recsth->execute($domid, 0, $fqdn, 2, $ns, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
     664        $recsth->execute($domid, 0, $ns, 1, $ip, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive) if $ip;
    527665      }
    528666
     
    556694      $fqdn =~ s/\.$//;
    557695      $fqdn =~ s/^\\052/*/;
    558       $ttl = 0 if !$ttl;
    559       $stamp = '' if !$stamp;
    560       $loc = '' if !$loc;
    561       $loc = '' if $loc =~ /^:+$/;
     696      $ttl = -1 if $ttl eq '';
     697      $stamp = '' if !$stamp;
     698      $loc = '' if !$loc;
     699      $loc = '' if $loc =~ /^:+$/;
     700
     701      my $stampactive = 'n';
     702      my $expires = 'n';
    562703
    563704      if ($type == 33) {
     
    588729        my $domid = $dnsdb->_hostparent($fqdn);
    589730        if ($domid) {
    590           $recsth->execute($domid, 0, $fqdn, 33, $target, $prio, $weight, $port, $ttl, $loc) if $domid;
     731          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     732          $recsth->execute($domid, 0, $fqdn, 33, $target, $prio, $weight, $port, $ttl, $loc, $stamp, $expires, $stampactive) if $domid;
    591733        } else {
    592734          push @deferred, $rec unless $nodefer;
     
    605747
    606748        my $fparent = $dnsdb->_hostparent($fqdn);
     749
    607750        if ($fparent) {
    608           $recsth->execute($fparent, 0, $fqdn, 28, $val->addr, 0, 0, 0, $ttl, $loc);
     751          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $fparent, 'n');
     752          $recsth->execute($fparent, 0, $fqdn, 28, $val->addr, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    609753        } else {
    610754          push @deferred, $rec unless $nodefer;
     
    620764          my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    621765          if ($rparent) {
    622             $recsth->execute(0, $rparent, $txtstring, 16, "$msg", 0, 0, 0, $ttl, $loc);
     766            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     767            $recsth->execute(0, $rparent, $txtstring, 16, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    623768          } else {
    624769            push @deferred, $rec unless $nodefer;
     
    628773          my $domid = $dnsdb->_hostparent($fqdn);
    629774          if ($domid) {
    630             $recsth->execute($domid, 0, $fqdn, 16, $txtstring, 0, 0, 0, $ttl, $loc);
     775            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     776            $recsth->execute($domid, 0, $fqdn, 16, $txtstring, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    631777          } else {
    632778            push @deferred, $rec unless $nodefer;
     
    648794          my ($rparent) = $dbh->selectrow_array("SELECT rdns_id FROM revzones WHERE revnet >> ?", undef, ($msg));
    649795          if ($rparent) {
    650             $recsth->execute(0, $rparent, "$email $txtrec", 17, "$msg", 0, 0, 0, $ttl, $loc);
     796            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $rparent, 'y');
     797            $recsth->execute(0, $rparent, "$email $txtrec", 17, "$msg", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive );
    651798          } else {
    652799            push @deferred, $rec unless $nodefer;
     
    656803          my $domid = $dnsdb->_hostparent($fqdn);
    657804          if ($domid) {
    658             $recsth->execute($domid, 0, $fqdn, 17, "$email $txtrec", 0, 0, 0, $ttl, $loc);
     805            ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     806            $recsth->execute($domid, 0, $fqdn, 17, "$email $txtrec", 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    659807          } else {
    660808            push @deferred, $rec unless $nodefer;
     
    672820        my $domid = $dnsdb->_hostparent($fqdn);
    673821        if ($domid) {
    674           $recsth->execute($domid, 0, $fqdn, 44, $sshfp, 0, 0, 0, $ttl, $loc);
     822          ($ttl, $stampactive, $expires, $stamp) = calcstamp($stamp, $ttl, $domid, 'n');
     823          $recsth->execute($domid, 0, $fqdn, 44, $sshfp, 0, 0, 0, $ttl, $loc, $stamp, $expires, $stampactive);
    675824        } else {
    676825          push @deferred, $rec unless $nodefer;
  • branches/stable/vega-import.pl

    r547 r548  
    33##
    44# $Id$
    5 # Copyright 2011,2012 Kris Deugau <kdeugau@deepnet.cx>
     5# Copyright 2011-2013 Kris Deugau <kdeugau@deepnet.cx>
    66#
    77#    This program is free software: you can redistribute it and/or modify
Note: See TracChangeset for help on using the changeset viewer.