Index: trunk/cgi-bin/IPDB.pm
===================================================================
--- trunk/cgi-bin/IPDB.pm	(revision 795)
+++ trunk/cgi-bin/IPDB.pm	(revision 798)
@@ -67,4 +67,7 @@
 # Mapping hash for pooltype -> poolip-as-netblock conversions
 my %poolmap = (sd => 'en', cd => 'cn', dp => 'cn', mp => 'cn', wp => 'cn', ld => 'in', ad => 'in', bd => 'in');
+
+# Backup fields, since we iterate over the set regularly
+our @backupfields = qw(brand model type src user vpass epass port ip);
 
 # Friendly display strings for merge scopes
@@ -1472,14 +1475,15 @@
       my $backupid = 0;
       if ($args{backup}) {
-        my $sql = "INSERT INTO backuplist (";
+        my $bksql = "INSERT INTO backuplist (";
         my @bkvals;
-        for my $bk (qw(brand model type port src user vpass epass)) {
+        my @bkfields;
+        for my $bk (@backupfields) {
           if ($args{"bk$bk"}) {
-            $sql .= "bk$bk,";
+            push @bkfields, "bk$bk";
             push @bkvals, $args{"bk$bk"};
           }
         }
-        $sql .= "ip) VALUES (".('?,' x scalar(@bkvals))."?)";
-        $dbh->do($sql, undef, @bkvals, $args{cidr});
+        $bksql .= join(',',@bkfields).") VALUES (".join(',', map {'?'} @bkfields).")";
+        $dbh->do($bksql, undef, @bkvals);
         ($backupid) = $dbh->selectrow_array("SELECT currval('backuplist_backup_id_seq')");
       }
@@ -1523,10 +1527,32 @@
 	$msg = "Unable to allocate $args{cidr} as '$disp_alloctypes{$args{type}}'";
 
+        # backup
+        my $backupid = 0;
+        if ($args{backup}) {
+          if (!$args{bkip}) {
+            # check for /32-ness.  no point in skipping /32 "netblocks", because they already single IPs
+            die "Backup data set on a netblock requires a backup IP\n" unless $args{cidr} =~ m{/32$};
+            $args{bkip} = $args{cidr};
+          }
+          my $bksql = "INSERT INTO backuplist (";
+          my @bkfields;
+          my @bkvals;
+          for my $bk (@backupfields) {
+            if ($args{"bk$bk"}) {
+              push @bkfields, "bk$bk";
+              push @bkvals, $args{"bk$bk"};
+            }
+          }
+          $bksql .= join(',',@bkfields).") VALUES (".join(',',map {'?'} @bkfields).")";
+          $dbh->do($bksql, undef, @bkvals);
+          ($backupid) = $dbh->selectrow_array("SELECT currval('backuplist_backup_id_seq')");
+        } # $args{backup}
+
 	# Insert the allocations entry
 	$dbh->do("INSERT INTO allocations ".
-		"(cidr,parent_id,master_id,vrf,vlan,custid,type,city,description,notes,circuitid,privdata,rdns)".
-		" VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", undef,
+		"(cidr,parent_id,master_id,vrf,vlan,custid,type,city,description,notes,circuitid,privdata,rdns,backup_id)".
+		" VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", undef,
 		($args{cidr}, $fbparent, $fbmaster, $args{vrf}, $args{vlan}, $args{custid}, $args{type}, $args{city},
-		$args{desc}, $args{notes}, $args{circid}, $args{privdata}, $args{rdns}) );
+		$args{desc}, $args{notes}, $args{circid}, $args{privdata}, $args{rdns}, $backupid) );
 	my ($bid) = $dbh->selectrow_array("SELECT currval('allocations_id_seq')");
 
@@ -1845,30 +1871,70 @@
     }
 
-    # check on the backup data first.  we may need to tweak the main update's parameters.
+    # backup
     if (!defined($args{ignorebk})) {
-      if ($args{backupfields}) {
-        my @bkfields;
-        my @bkvals;
-        for my $bk (qw(brand model type src user vpass epass)) {
-          if ($binfo->{"bk$bk"} ne $args{"bk$bk"}) {
-            push @bkfields, "bk$bk = ?";
-            push @bkvals, $args{"bk$bk"};
+      # backup data considered "restricted";  caller should set this flag if user does not have 's' permission
+
+      my $backupid = $binfo->{hasbk};
+      if (!$binfo->{hasbk}) {
+        if ($args{backup}) {
+          # failure mode:  backup data on netblock with no IP set
+          if (!$args{bkip}) {
+            # check for /32-ness.  no point in skipping /32 "netblocks", because they already single IPs
+            die "Backup data set on a netblock requires a backup IP\n" unless $binfo->{block} =~ m{/32$};
+            $args{bkip} = $binfo->{block};
           }
+          # insert new backup record since we don't have one
+          my $bksql = "INSERT INTO backuplist (";
+          my @bkfields;
+          my @bkvals;
+          for my $bk (@backupfields) {
+            if ($args{"bk$bk"}) {
+              push @bkfields, "bk$bk";
+              push @bkvals, $args{"bk$bk"};
+            }
+          }
+          $bksql .= join(',',@bkfields).") VALUES (".join(',', map {'?'} @bkfields).")";
+          $dbh->do($bksql, undef, @bkvals);
+          ($backupid) = $dbh->selectrow_array("SELECT currval('backuplist_backup_id_seq')");
+          # add the backup ID to the update
+          push @vallist, $backupid;
+          $sql .= " = ?, backup_id";
         }
-        $dbh->do("UPDATE backuplist SET ".join(',', @bkfields)." WHERE backup_id = ?",
-            undef, @bkvals, $binfo->{hasbk})
-            if @bkfields;
+
+      } else { # !$binfo->{hasbk}
+
+        # allocation already has backup data
+        if ($args{backup}) {
+          if (!$args{bkip}) {
+            # check for /32-ness.  no point in skipping /32 "netblocks", because they are already single IPs
+            die "Backup data set on a netblock requires a backup IP\n" unless $binfo->{block} =~ m{/32$};
+            $args{bkip} = $binfo->{block};
+          }
+          my @bkfields;
+          my @bkvals;
+          for my $bk (@backupfields) {
+            no warnings qw( uninitialized );
+            if ($binfo->{"bk$bk"} ne $args{"bk$bk"}) {
+              push @bkfields, "bk$bk = ?";
+              push @bkvals, $args{"bk$bk"};
+            }
+          }
+
+          $dbh->do("UPDATE backuplist SET ".join(',', @bkfields)." WHERE backup_id = ?",
+              undef, @bkvals, $binfo->{hasbk})
+              if @bkfields;
 ##todo: keep historic changes for $timeperiod, by adding a backref ID field, and on updates adding a new backup
 # record instead of updating the existing one.  should probably check if new==old so we don't do needless updates
 # in that case...
-      } else {
-        if ($binfo->{hasbk}) {
-          # had backup data, no longer checked - delete backup entry
-          $dbh->do("DELETE FROM backuplist WHERE backup_id = ?", undef, $binfo->{hasbk});
-          $sql .= " = ?, backup_id";
-          push @vallist, 0;
+        } else {
+          if ($binfo->{hasbk}) {
+            # had backup data, no longer checked - delete backup entry
+            $dbh->do("DELETE FROM backuplist WHERE backup_id = ?", undef, $binfo->{hasbk});
+            $sql .= " = ?, backup_id";
+            push @vallist, 0;
+          }
         }
-      }
-    }
+      } # $binfo->{hasbk} defined
+    } # if !args{ignorebk}
 
     # append another SQL fragment
@@ -3027,5 +3093,5 @@
 ## IPDB::getBlockData()
 # Get CIDR or IP, custid, type, city, circuit ID, description, notes, modification time,
-# private/restricted data, for a CIDR block or pool IP
+# private/restricted data, and backup fields, for a CIDR block or pool IP
 # Also returns SWIP status flag for CIDR blocks or pool netblock for IPs
 # Takes the block ID or IP to look up and an optional flag to indicate a pool IP lookup
@@ -3045,5 +3111,5 @@
 	privdata, vlan, rdns);
   my $bkfields = q(b.backup_id AS hasbk, b.bkbrand, b.bkmodel, b.bktype, b.bkport, b.bksrc,
-	b.bkuser, b.bkvpass, b.bkepass);
+	b.bkuser, b.bkvpass, b.bkepass, b.bkip);
 
   if ($type eq 'i') {
@@ -3051,8 +3117,8 @@
 	SELECT p.id, p.ip AS block, p.city, p.vrf, p.parent_id, p.master_id, $commonfields,
                 d.zone >> p.ip AS revavail,
-		$bkfields
+                $bkfields
 	FROM poolips p
         LEFT JOIN dnsavail d ON p.master_id = d.parent_alloc AND p.ip << d.zone
-	LEFT JOIN backuplist b ON p.backup_id = b.backup_id
+        LEFT JOIN backuplist b ON p.backup_id = b.backup_id
         WHERE id = ?
 	), undef, ($id) );
@@ -3062,8 +3128,10 @@
 	SELECT a.id, a.cidr AS block, a.city, a.vrf, a.parent_id, a.master_id, swip, $commonfields,
 		f.cidr AS reserve, f.id as reserve_id,
-                d.zone >>= a.cidr AS revavail, d.zone << a.cidr AS revpartial
+                d.zone >>= a.cidr AS revavail, d.zone << a.cidr AS revpartial,
+                $bkfields
 	FROM allocations a
         LEFT JOIN freeblocks f ON a.id=f.reserve_for
         LEFT JOIN dnsavail d ON a.master_id = d.parent_alloc AND (a.cidr <<= d.zone OR a.cidr >> d.zone)
+        LEFT JOIN backuplist b ON a.backup_id = b.backup_id
         WHERE a.id = ?
 	), undef, ($id) );
