Merge branch 'master' of git://git.distorted.org.uk/~mdw/odin-cgi
authorMark Wooding <mdw@distorted.org.uk>
Fri, 7 Aug 2015 06:56:29 +0000 (07:56 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 7 Aug 2015 06:56:29 +0000 (07:56 +0100)
* 'master' of git://git.distorted.org.uk/~mdw/odin-cgi:
  lib/Odin.pm: Alter `redir_query' to break ties by local part.
  bin/mailredir.userv, lib/Odin.pm: Modify multiple redirections at once.
  bin/mailredir.userv, lib/Odin.pm: Allocate multiple local parts at once.
  lib/Odin.pm: New function for parsing and checking integer arguments.

bin/mailredir.userv
lib/Odin.pm

index ab8b54d..f5acb06 100755 (executable)
@@ -68,11 +68,11 @@ Commands available:
        dormant
        help
        list
-       new [-GENOPTS] [-RECOPTS] RECIP
+       new [-n COUNT] [-GENOPTS] [-RECOPTS] [RECIP]
        release LPART ...
        reserve [-GENOPTS] N
        reserved
-       set [-RECOPTS] LPART
+       set [-RECOPTS] LPART ...
 
 GENOPTS ::= [-g GENERATOR] [-p PARAM=VALUE]
 RECOPTS ::= [-c COMMENT] [-r RECIP] [-x EXPIRES]
@@ -107,18 +107,23 @@ EOF
   my $gencls = Odin::default_generator_class $dom;
   my %gp = ();
   my %r = ();
+  my $n = 1;
   while (my $o = $op->get) {
-    gen_opt $dom, $gencls, %gp, $o, $op
-      or record_opt %r, $o, $op
-      or $op->unk;
+    if ($o eq "n") { $n = $op->intarg(undef, 0) }
+    else {
+      gen_opt $dom, $gencls, %gp, $o, $op
+       or record_opt %r, $o, $op
+       or $op->unk;
+    }
   }
   my @a = $op->rest;
   if (@a) { $r{recip} = shift @a; }
   !@a or $op->bad;
-  $op->ok or Odin::fail "usage: new [-GENOPTS] [-RECOPTS] RECIP";
+  $op->ok or
+    Odin::fail "usage: new [-n COUNT] [-GENOPTS] [-RECOPTS] [RECIP]";
   my $gen = $gencls->new($dom, \%gp);
-  my $l = Odin::new_redir $dom, $gen, %r;
-  print $l, "\n";
+  my @l = Odin::new_redir $dom, $gen, %r, $n;
+  print map { $_ . "\n" } @l;
 } elsif ($op eq "reserve") {
   my $op = Odin::OptParse->new(@ARGV);
   my $gencls = Odin::default_generator_class $dom;
@@ -160,10 +165,9 @@ EOF
       or $op->unk;
   }
   my @a = $op->rest;
-  my $l = shift @a or $op->bad;
-  @a and $op->bad;
-  $op->ok or Odin::fail "usage: set [-RECOPTS] LPART";
-  Odin::modify_redir $dom, $l, %r;
+  @a or $op->bad;
+  $op->ok or Odin::fail "usage: set [-RECOPTS] LPART ...";
+  Odin::modify_redir $dom, %r, @a;
 } else {
   Odin::fail "unknown operation `$op'";
 }
index 7182af2..a742839 100644 (file)
@@ -501,7 +501,7 @@ sub redir_query ($$$;$@) {
             ("SELECT lpart, expire, recip, comment
               FROM odin_mailredir WHERE dom = ? AND owner = ?" .
               (defined $cond ? " AND $cond" : "") . " " .
-             "ORDER BY expire", undef, $dom, $owner, @args)};
+             "ORDER BY expire, lpart", undef, $dom, $owner, @args)};
 }
 
 sub clear_redir_reservations ($) {
@@ -574,23 +574,27 @@ sub check_fixup_redir ($) {
   }
 }
 
-sub new_redir ($$\%) {
-  my ($dom, $gen, $r) = @_;
+sub new_redir ($$\%;$) {
+  my ($dom, $gen, $r, $n) = @_;
   my $db = open_db;
-  my $l;
+  my @l;
 
+  $n //= 1;
   check_fixup_redir $r;
   Odin::xact {
     clear_redir_reservations $db;
-    $l = Odin::gen_redir_name $db, $gen;
-    insert_record $db, "odin_mailredir",
-      lpart => $l, dom => $dom, owner => $WHO, st => 'live',
-      recip => $r->{recip} // qualify_recip $Odin::WHO,
-      expire => $r->{expire} // -1,
-      comment => $r->{comment} // "";
+    while (@l < $n) {
+      my $l = Odin::gen_redir_name $db, $gen;
+      insert_record $db, "odin_mailredir",
+       lpart => $l, dom => $dom, owner => $WHO, st => 'live',
+       recip => $r->{recip} // qualify_recip $Odin::WHO,
+       expire => $r->{expire} // -1,
+       comment => $r->{comment} // "";
+      push @l, $l;
+    }
     check_redir_limits $db;
   } $db;
-  return $l;
+  return @l;
 }
 
 sub reserve_redir ($$$) {
@@ -657,31 +661,34 @@ sub disable_redir ($$) {
   } $db;
 }
 
-sub modify_redir ($$\%) {
-  my ($dom, $l, $r) = @_;
+sub modify_redir ($\%@) {
+  my ($dom, $r, @l) = @_;
   my $db = open_db;
 
   check_fixup_redir $r;
   Odin::xact {
     clear_redir_reservations $db;
-    my ($recip, $st) = $db->selectrow_array
-      ("SELECT recip, st FROM odin_mailredir
-       WHERE lpart = ? AND dom = ? AND owner = ?", undef,
-       $l, $dom, $WHO);
-    if (!defined $recip) { Odin::fail "unknown local part `$l'"; }
-    elsif ($recip eq "") { $r->{recip} //= qualify_recip $WHO; }
-    if ($st ne "live") { $r->{st} = "live"; $r->{expire} //= -1; }
-    my @var = ();
-    my @val = ();
-    for my $v (keys %$r) {
-      push @var, $v;
-      push @val, $r->{$v};
+    for my $l (@l) {
+      my %r = %$r;
+      my ($recip, $st) = $db->selectrow_array
+       ("SELECT recip, st FROM odin_mailredir
+         WHERE lpart = ? AND dom = ? AND owner = ?", undef,
+        $l, $dom, $WHO);
+      if (!defined $recip) { Odin::fail "unknown local part `$l'"; }
+      elsif ($recip eq "") { $r{recip} //= qualify_recip $WHO; }
+      if ($st ne "live") { $r{st} = "live"; $r{expire} //= -1; }
+      my @var = ();
+      my @val = ();
+      for my $v (keys %r) {
+       push @var, $v;
+       push @val, $r{$v};
+      }
+      @var or fail "nothing to change";
+      $db->do("UPDATE odin_mailredir SET " .
+             join(", ", map { "$_ = ?" } @var) . " " .
+             "WHERE lpart = ? AND dom = ?", undef,
+             @val, $l, $dom);
     }
-    @var or fail "nothing to change";
-    $db->do("UPDATE odin_mailredir SET " .
-           join(", ", map { "$_ = ?" } @var) . " " .
-           "WHERE lpart = ? AND dom = ?", undef,
-           @val, $l, $dom);
   } $db;
 }
 
@@ -723,6 +730,19 @@ sub arg {
   return $a;
 }
 
+sub intarg {
+  my ($me, $what, $min, $max) = @_;
+  $what //= "option `-$me->{opt}'";
+  defined (my $a = $me->arg) or return undef;
+  if ($a !~ /^[-+]?\d+$/ ||
+      (defined $min && $a < $min) ||
+      (defined $max && $a > $max)) {
+    $me->err("invalid value `$a' for $what");
+    $a = undef;
+  }
+  return $a;
+}
+
 sub rest { return @{$_[0]->{args}}; }
 sub ok { return $_[0]->{ok}; }
 sub bad { $_[0]->{ok} = 0; }