add per-agent invoice templates, add per-package suspend invoice events, fix automati...
[freeside.git] / FS / FS / svc_forward.pm
index 5264a60..b8d55fe 100644 (file)
@@ -1,28 +1,16 @@
 package FS::svc_forward;
 
 use strict;
 package FS::svc_forward;
 
 use strict;
-use vars qw( @ISA $nossh_hack $conf $shellmachine @qmailmachines @vpopmailmachines);
-use FS::Record qw( fields qsearch qsearchs );
+use vars qw( @ISA );
+use FS::Conf;
+use FS::Record qw( fields qsearch qsearchs dbh );
 use FS::svc_Common;
 use FS::cust_svc;
 use FS::svc_Common;
 use FS::cust_svc;
-use Net::SSH qw(ssh);
-use FS::Conf;
 use FS::svc_acct;
 use FS::svc_domain;
 
 @ISA = qw( FS::svc_Common );
 
 use FS::svc_acct;
 use FS::svc_domain;
 
 @ISA = qw( FS::svc_Common );
 
-#ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::svc_forward'} = sub { 
-  $conf = new FS::Conf;
-  $shellmachine = $conf->exists('qmailmachines')
-                  ? $conf->config('shellmachine')
-                  : '';
-  if ( $conf->exists('vpopmailmachines') ) {
-    @vpopmailmachines = $conf->config('vpopmailmachines');
-  }
-};
-
 =head1 NAME
 
 FS::svc_forward - Object methods for svc_forward records
 =head1 NAME
 
 FS::svc_forward - Object methods for svc_forward records
@@ -59,9 +47,11 @@ inherits from FS::Record.  The following fields are currently supported:
 
 =item srcsvc - svcnum of the source of the forward (see L<FS::svc_acct>)
 
 
 =item srcsvc - svcnum of the source of the forward (see L<FS::svc_acct>)
 
+=item src - literal source (username or full email address)
+
 =item dstsvc - svcnum of the destination of the forward (see L<FS::svc_acct>)
 
 =item dstsvc - svcnum of the destination of the forward (see L<FS::svc_acct>)
 
-=item dst - foreign destination (email address) - forward not local to freeside
+=item dst - literal destination (username or full email address)
 
 =back
 
 
 =back
 
@@ -78,7 +68,7 @@ database, see L<"insert">.
 
 sub table { 'svc_forward'; }
 
 
 sub table { 'svc_forward'; }
 
-=item insert
+=item insert [ , OPTION => VALUE ... ]
 
 Adds this mail forwarding alias to the database.  If there is an error, returns
 the error, otherwise returns false.
 
 Adds this mail forwarding alias to the database.  If there is an error, returns
 the error, otherwise returns false.
@@ -86,16 +76,11 @@ the error, otherwise returns false.
 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be 
 defined.  An FS::cust_svc record will be created and inserted.
 
 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be 
 defined.  An FS::cust_svc record will be created and inserted.
 
-If the configuration values (see L<FS::Conf>) vpopmailmachines exist, then
-the command:
-
-  [ -d /home/vpopmail/$vdomain/$source ] || {
-    echo "$destination" >> /home/vpopmail/$vdomain/$source/.$qmail
-    chown $vpopuid:$vpopgid /home/vpopmail/$vdomain/$source/.$qmail
-  }
+Currently available options are: I<depend_jobnum>
 
 
-is executed on each vpopmailmachine via ssh (see L<dot-qmail/"EXTENSION ADDRESSES">).
-This behaviour can be surpressed by setting $FS::svc_forward::nossh_hack true.
+If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
+jobnums), all provisioning jobs will have a dependancy on the supplied
+jobnum(s) (they will not run until the specific job(s) complete(s)).
 
 =cut
 
 
 =cut
 
@@ -110,33 +95,20 @@ sub insert {
   local $SIG{TSTP} = 'IGNORE';
   local $SIG{PIPE} = 'IGNORE';
 
   local $SIG{TSTP} = 'IGNORE';
   local $SIG{PIPE} = 'IGNORE';
 
-  $error=$self->check;
-  return $error if $error;
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
 
 
-  $error = $self->SUPER::insert;
+  $error = $self->check;
   return $error if $error;
 
   return $error if $error;
 
-  my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } );
-  my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $svc_acct->domsvc } );
-  my $source = $svc_acct->username . $svc_domain->domain;
-  my $destination;
-  if ($self->dstdvc) {
-    my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->dstsvc } );
-    my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $svc_acct->domsvc } );
-    $destination = $svc_acct->username . $svc_domain->domain;
-  } else {
-    $destination = $self->dst;
-  }
-    
-  my $vdomain = $svc_acct->domain;
-
-  foreach my $vpopmailmachine ( @vpopmailmachines ) {
-    my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachine);
-
-    ssh("root\@$machine","[ -d $vpopdir/$vdomain/$source ] || { echo $destination >> $vpopdir/$vdomain/$source/.qmail; chown $vpopuid:$vpopgid $vpopdir/$vdomain/$source/.qmail; }")  
-      if ( ! $nossh_hack && $machine);
+  $error = $self->SUPER::insert(@_);
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
   }
 
   }
 
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   ''; #no error
 
 }
   ''; #no error
 
 }
@@ -148,6 +120,33 @@ returns the error, otherwise returns false.
 
 The corresponding FS::cust_svc record will be deleted as well.
 
 
 The corresponding FS::cust_svc record will be deleted as well.
 
+=cut
+
+sub delete {
+  my $self = shift;
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::Autocommit = 0;
+  my $dbh = dbh;
+
+  my $error = $self->SUPER::delete;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+}
+
+
 =item replace OLD_RECORD
 
 Replaces OLD_RECORD with this one in the database.  If there is an error,
 =item replace OLD_RECORD
 
 Replaces OLD_RECORD with this one in the database.  If there is an error,
@@ -157,10 +156,34 @@ returns the error, otherwise returns false.
 
 sub replace {
   my ( $new, $old ) = ( shift, shift );
 
 sub replace {
   my ( $new, $old ) = ( shift, shift );
-  my $error;
 
 
- $new->SUPER::replace($old);
+  if ( $new->srcsvc != $old->srcsvc
+       && ( $new->dstsvc != $old->dstsvc
+            || ! $new->dstsvc && $new->dst ne $old->dst 
+          )
+      ) {
+    return "Can't change both source and destination of a mail forward!"
+  }
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
 
 
+  my $error = $new->SUPER::replace($old);
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
 }
 
 =item suspend
 }
 
 =item suspend
@@ -193,61 +216,89 @@ Sets any fixed values; see L<FS::part_svc>.
 
 sub check {
   my $self = shift;
 
 sub check {
   my $self = shift;
-  my $error;
 
   my $x = $self->setfixed;
   return $x unless ref($x);
 
   my $x = $self->setfixed;
   return $x unless ref($x);
-  my $part_svc = $x;
+  #my $part_svc = $x;
+
+  my $error = $self->ut_numbern('svcnum')
+              || $self->ut_numbern('srcsvc')
+              || $self->ut_numbern('dstsvc')
+  ;
+  return $error if $error;
+
+  return "Both srcsvc and src were defined; only one can be specified"
+    if $self->srcsvc && $self->src;
 
 
-  my($recref) = $self->hashref;
+  return "one of srcsvc or src is required"
+    unless $self->srcsvc || $self->src;
 
 
-  $recref->{srcsvc} =~ /^(\d+)$/ or return "Illegal srcsvc";
-  $recref->{srcsvc} = $1;
-  my($svc_acct);
-  return "Unknown srcsvc" unless
-    $svc_acct=qsearchs('svc_acct',{'svcnum'=> $recref->{srcsvc} } );
+  return "Unknown srcsvc: ". $self->srcsvc
+    unless ! $self->srcsvc || $self->srcsvc_acct;
 
 
-  return "Illegal use of dstsvc and dst" if
-    ($recref->{dstsvc} && $recref->{dst});
+  return "Both dstsvc and dst were defined; only one can be specified"
+    if $self->dstsvc && $self->dst;
 
 
-  return "Illegal use of dstsvc and dst" if
-    (! $recref->{dstsvc} && ! $recref->{dst});
+  return "one of dstsvc or dst is required"
+    unless $self->dstsvc || $self->dst;
 
 
-  $recref->{dstsvc} =~ /^(\d+)$/ or return "Illegal dstsvc";
-  $recref->{dstsvc} = $1;
+  return "Unknown dstsvc: ". $self->dstsvc
+    unless ! $self->dstsvc || $self->dstsvc_acct;
+  #return "Unknown dstsvc"
+  #  unless qsearchs('svc_acct', { 'svcnum' => $self->dstsvc } )
+  #         || ! $self->dstsvc;
 
 
-  if ($recref->{dstsvc}) {
-    my($svc_acct);
-    return "Unknown dstsvc" unless
-      my $svc_domain=qsearchs('svc_acct',{'svcnum'=> $recref->{dstsvc} } );
+  if ( $self->src ) {
+    $self->src =~ /^([\w\.\-\&]*)(\@([\w\-]+\.)+\w+)?$/
+       or return "Illegal src: ". $self->dst;
+    $self->src("$1$2");
+  } else {
+    $self->src('');
   }
 
   }
 
-  if ($recref->{dst}) {
-    $recref->{dst} =~ /^(\w\.\-]+)\@(([\w\.\-]+\.)+\w+)$/
-       or return "Illegal dst";
-    $recref->{dst} = $1;
+  if ( $self->dst ) {
+    $self->dst =~ /^([\w\.\-\&]*)(\@([\w\-]+\.)+\w+)?$/
+       or return "Illegal dst: ". $self->dst;
+    $self->dst("$1$2");
+  } else {
+    $self->dst('');
   }
 
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 }
 
-=back
+=item srcsvc_acct
 
 
-=head1 VERSION
+Returns the FS::svc_acct object referenced by the srcsvc column, or false for
+literally specified forwards.
 
 
-$Id: svc_forward.pm,v 1.2 2001-08-12 19:41:24 jeff Exp $
+=cut
 
 
-=head1 BUGS
+sub srcsvc_acct {
+  my $self = shift;
+  qsearchs('svc_acct', { 'svcnum' => $self->srcsvc } );
+}
 
 
-The remote commands should be configurable.
+=item dstsvc_acct
 
 
-The $recref stuff in sub check should be cleaned up.
+Returns the FS::svc_acct object referenced by the srcsvc column, or false for
+literally specified forwards.
+
+=cut
+
+sub dstsvc_acct {
+  my $self = shift;
+  qsearchs('svc_acct', { 'svcnum' => $self->dstsvc } );
+}
+
+=back
+
+=head1 BUGS
 
 =head1 SEE ALSO
 
 L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
 
 =head1 SEE ALSO
 
 L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
-L<FS::svc_acct>, L<FS::svc_domain>, L<Net::SSH>, L<ssh>, L<dot-qmail>,
-schema.html from the base documentation.
+L<FS::svc_acct>, L<FS::svc_domain>, schema.html from the base documentation.
 
 =cut
 
 
 =cut