This commit was manufactured by cvs2svn to create branch
authorcvs2git <cvs2git>
Tue, 14 Jan 2003 09:26:50 +0000 (09:26 +0000)
committercvs2git <cvs2git>
Tue, 14 Jan 2003 09:26:50 +0000 (09:26 +0000)
'FREESIDE_1_4_BRANCH'.

104 files changed:
FS/FS.pm
FS/FS/ClientAPI.pm
FS/FS/ClientAPI/passwd.pm
FS/FS/Conf.pm
FS/FS/InitHandler.pm
FS/FS/Record.pm
FS/FS/UID.pm
FS/FS/ac.pm [deleted file]
FS/FS/ac_block.pm [deleted file]
FS/FS/ac_field.pm [deleted file]
FS/FS/ac_type.pm [deleted file]
FS/FS/cust_bill.pm
FS/FS/cust_bill_pkg.pm
FS/FS/cust_main.pm
FS/FS/cust_main_county.pm
FS/FS/cust_main_invoice.pm
FS/FS/cust_pay.pm
FS/FS/cust_pkg.pm
FS/FS/cust_refund.pm
FS/FS/cust_svc.pm
FS/FS/part_ac_field.pm [deleted file]
FS/FS/part_export.pm
FS/FS/part_svc.pm
FS/FS/pkg_svc.pm
FS/FS/svc_acct.pm
FS/FS/svc_acct_sm.pm [new file with mode: 0644]
FS/FS/svc_broadband.pm [deleted file]
FS/FS/svc_domain.pm
FS/FS/type_pkgs.pm
FS/MANIFEST
FS/bin/freeside-addoutsourceuser
FS/bin/freeside-adduser
FS/bin/freeside-cc-receipts-report
FS/bin/freeside-credit-report
FS/bin/freeside-email
FS/bin/freeside-expiration-alerter
FS/bin/freeside-receivables-report
FS/bin/freeside-setup
FS/bin/freeside-tax-report
FS/t/svc_acct_sm.t [new file with mode: 0644]
Makefile
README
README.1.5.0pre1 [deleted file]
bin/create-history-tables
bin/dbdef-create
bin/fs-setup [new file with mode: 0755]
eg/export_template.pm
fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm [deleted file]
fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi [deleted file]
fs_selfadmin/FS-MailAdminServer/fs_mailadmind [deleted file]
fs_selfadmin/README [deleted file]
fs_selfadmin/fs_mailadmin_server [deleted file]
fs_selfservice/freeside-selfservice-server [new file with mode: 0644]
fs_signup/FS-SignupClient/cgi/signup.cgi
htetc/global.asa
htetc/handler.pl
httemplate/browse/ac.cgi [deleted file]
httemplate/browse/ac_type.cgi [deleted file]
httemplate/browse/cust_main_county.cgi
httemplate/docs/index.html
httemplate/docs/install.html
httemplate/docs/legacy.html
httemplate/docs/schema.dia
httemplate/docs/schema.html
httemplate/docs/session.html
httemplate/docs/upgrade4.html [new file with mode: 0644]
httemplate/docs/upgrade5.html [new file with mode: 0644]
httemplate/docs/upgrade6.html [new file with mode: 0644]
httemplate/docs/upgrade9.html
httemplate/edit/ac.cgi [deleted file]
httemplate/edit/ac_type.cgi [deleted file]
httemplate/edit/cust_main_county.cgi
httemplate/edit/part_bill_event.cgi
httemplate/edit/part_svc.cgi
httemplate/edit/process/ac.cgi [deleted file]
httemplate/edit/process/ac_block.cgi [deleted file]
httemplate/edit/process/ac_field.cgi [deleted file]
httemplate/edit/process/ac_type.cgi [deleted file]
httemplate/edit/process/cust_main_county.cgi
httemplate/edit/process/part_ac_field.cgi [deleted file]
httemplate/edit/process/part_svc.cgi
httemplate/edit/process/svc_acct_sm.cgi [new file with mode: 0755]
httemplate/edit/process/svc_broadband.cgi [deleted file]
httemplate/edit/svc_acct_sm.cgi [new file with mode: 0755]
httemplate/edit/svc_broadband.cgi [deleted file]
httemplate/edit/svc_forward.cgi
httemplate/graph/money_time-graph.cgi [deleted file]
httemplate/graph/money_time.cgi [deleted file]
httemplate/index.html
httemplate/misc/link.cgi
httemplate/misc/meta-import.cgi [deleted file]
httemplate/misc/process/meta-import.cgi [deleted file]
httemplate/search/report_cc.cgi
httemplate/search/report_credit.cgi
httemplate/search/report_receivables.cgi
httemplate/search/report_tax.cgi
httemplate/search/svc_acct.cgi
httemplate/search/svc_acct_sm.cgi [new file with mode: 0755]
httemplate/search/svc_acct_sm.html [new file with mode: 0755]
httemplate/search/svc_domain.cgi
httemplate/view/svc_acct.cgi
httemplate/view/svc_acct_sm.cgi [new file with mode: 0755]
httemplate/view/svc_broadband.cgi [deleted file]
init.d/freeside-init

index a2df6f1..963c735 100644 (file)
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -62,6 +62,8 @@ L<FS::domain_record> - DNS zone entries
 
 L<FS::svc_forward> - Mail forwarding class
 
+L<FS::svc_acct_sm> - (Depreciated) Vitual mail alias class
+
 L<FS::svc_www> - Web virtual host class.
 
 L<FS::part_svc> - Service definition class
@@ -185,7 +187,7 @@ first time, the suggested order will tend to reduce the number of forward
 references."
 
 If you've never used OO modules before,
-http://www.perl.com/doc/FMTEYEWTK/easy_objects.html might help you out.
+http://www.cpan.org/doc/FMTEYEWTK/easy_objects.html might help you out.
 
 =head1 DESCRIPTION
 
index 7cbbdbf..f7b8eb0 100644 (file)
@@ -1,13 +1,13 @@
 package FS::ClientAPI;
 
 use strict;
-use vars qw(%handler $domain);
+use vars qw(%handler);
 
 %handler = ();
 
 #find modules
 foreach my $INC ( @INC ) {
-  foreach my $file ( glob("$INC/FS/ClientAPI/*.pm") ) {
+  foreach my $file ( glob("$INC/FS/ClientAPI/*") ) {
     $file =~ /\/(\w+)\.pm$/ or do {
       warn "unrecognized ClientAPI file: $file";
       next
index 016ebff..2960622 100644 (file)
@@ -15,9 +15,8 @@ FS::ClientAPI->register_handlers(
 sub passwd {
   my $packet = shift;
 
-  my $domain = $FS::ClientAPI::domain || $packet->{'domain'};
-  my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
-    or return { error => "Domain $domain not found" };
+  #my $domain = qsearchs('svc_domain', { 'domain' => $packet->{'domain'} } )
+  #  or return { error => "Domain $domain not found" };
 
   my $old_password = $packet->{'old_password'};
   my $new_password = $packet->{'new_password'};
@@ -28,11 +27,11 @@ sub passwd {
   my $svc_acct =
     ( length($old_password) < 13
       && qsearchs( 'svc_acct', { 'username'  => $packet->{'username'},
-                                 'domsvc'    => $svc_domain->svcnum,
+                                 #'domsvc'    => $svc_domain->svcnum,
                                  '_password' => $old_password } )
     )
     || qsearchs( 'svc_acct', { 'username'  => $packet->{'username'},
-                               'domsvc'    => $svc_domain->svcnum,
+                               #'domsvc'    => $svc_domain->svcnum,
                                '_password' => $old_password } );
 
   unless ( $svc_acct ) { return { error => 'Incorrect password.' } }
index f0a4c9f..fcc150c 100644 (file)
@@ -297,6 +297,13 @@ httemplate/docs/config.html
   },
 
   {
+    'key'         => 'cybercash3.2',
+    'section'     => 'billing',
+    'description' => '<a href="http://www.cybercash.com/cashregister/">CyberCash Cashregister v3.2</a> support.  Two lines: the full path and name of your merchant_conf file, and the transaction type (`mauthonly\' or `mauthcapture\').',
+    'type'        => 'textarea',
+  },
+
+  {
     'key'         => 'cyrus',
     'section'     => 'deprecated',
     'description' => '<b>DEPRECATED</b>, add a <i>cyrus</i> <a href="../browse/part_export.cgi">export</a> instead.  This option used to integrate with <a href="http://asg.web.cmu.edu/cyrus/imapd/">Cyrus IMAP Server</a>, three lines: IMAP server, admin username, and admin password.  Cyrus::IMAP::Admin should be installed locally and the connection to the server secured.',
@@ -346,6 +353,13 @@ httemplate/docs/config.html
   },
 
   {
+    'key'         => 'domain',
+    'section'     => 'deprecated',
+    'description' => 'Your domain name.',
+    'type'        => 'text',
+  },
+
+  {
     'key'         => 'editreferrals',
     'section'     => 'UI',
     'description' => 'Enable advertising source modification for existing customers',
index 5038cf3..87f507c 100644 (file)
@@ -1,9 +1,5 @@
 package FS::InitHandler;
 
-# this leaks memory under graceful restarts and i wouldn't use it on any
-# modern server.  useful for very slow machines with memory to spare, just
-# always do a full restart
-
 use strict;
 use vars qw($DEBUG);
 use FS::UID qw(adminsuidsetup);
@@ -52,6 +48,7 @@ sub handler {
   use FS::session;
   use FS::svc_acct;
   use FS::svc_acct_pop;
+  use FS::svc_acct_sm;
   use FS::svc_domain;
   use FS::svc_forward;
   use FS::svc_www;
index 4021510..31b6070 100644 (file)
@@ -9,8 +9,8 @@ use Carp qw(carp cluck croak confess);
 use File::CounterFile;
 use Locale::Country;
 use DBI qw(:sql_types);
-use DBIx::DBSchema 0.21;
-use FS::UID qw(dbh getotaker datasrc driver_name);
+use DBIx::DBSchema 0.19;
+use FS::UID qw(dbh checkruid getotaker datasrc driver_name);
 use FS::SearchCache;
 use FS::Msgcat qw(gettext);
 
@@ -60,12 +60,14 @@ FS::Record - Database record objects
     $hashref = $record->hashref;
 
     $error = $record->insert;
+    #$error = $record->add; #deprecated
 
     $error = $record->delete;
+    #$error = $record->del; #deprecated
 
     $error = $new_record->replace($old_record);
+    #$error = $new_record->rep($old_record); #deprecated
 
-    # external use deprecated - handled by the database (at least for Pg, mysql)
     $value = $record->unique('column');
 
     $error = $record->ut_float('column');
@@ -86,7 +88,7 @@ FS::Record - Database record objects
 
     $quoted_value = _quote($value,'table','field');
 
-    #deprecated
+    #depriciated
     $fields = hfields('table');
     if ( $fields->{Field} ) { # etc.
 
@@ -165,7 +167,7 @@ sub create {
   my $self = {};
   bless ($self, $class);
   if ( defined $self->table ) {
-    cluck "create constructor is deprecated, use new!";
+    cluck "create constructor is depriciated, use new!";
     $self->new(@_);
   } else {
     croak "FS::Record::create called (not from a subclass)!";
@@ -211,7 +213,7 @@ sub qsearch {
       my $column = $_;
       if ( ref($record->{$_}) ) {
         $op = $record->{$_}{'op'} if $record->{$_}{'op'};
-        #$op = 'LIKE' if $op =~ /^ILIKE$/i && driver_name ne 'Pg';
+        #$op = 'LIKE' if $op =~ /^ILIKE$/i && driver_name !~ /^Pg$/i;
         if ( uc($op) eq 'ILIKE' ) {
           $op = 'LIKE';
           $record->{$_}{'value'} = lc($record->{$_}{'value'});
@@ -222,19 +224,19 @@ sub qsearch {
 
       if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) {
         if ( $op eq '=' ) {
-          if ( driver_name eq 'Pg' ) {
+          if ( driver_name =~ /^Pg$/i ) {
             qq-( $column IS NULL OR $column = '' )-;
           } else {
             qq-( $column IS NULL OR $column = "" )-;
           }
         } elsif ( $op eq '!=' ) {
-          if ( driver_name eq 'Pg' ) {
+          if ( driver_name =~ /^Pg$/i ) {
             qq-( $column IS NOT NULL AND $column != '' )-;
           } else {
             qq-( $column IS NOT NULL AND $column != "" )-;
           }
         } else {
-          if ( driver_name eq 'Pg' ) {
+          if ( driver_name =~ /^Pg$/i ) {
             qq-( $column $op '' )-;
           } else {
             qq-( $column $op "" )-;
@@ -351,7 +353,7 @@ Returns the table name.
 =cut
 
 sub table {
-#  cluck "warning: FS::Record::table deprecated; supply one in subclass!";
+#  cluck "warning: FS::Record::table depriciated; supply one in subclass!";
   my $self = shift;
   $self -> {'Table'};
 }
@@ -478,40 +480,24 @@ sub insert {
   return $error if $error;
 
   #single-field unique keys are given a value if false
-  #(like MySQL's AUTO_INCREMENT or Pg SERIAL)
+  #(like MySQL's AUTO_INCREMENT)
   foreach ( $self->dbdef_table->unique->singles ) {
     $self->unique($_) unless $self->getfield($_);
   }
-
-  #and also the primary key, if the database isn't going to
+  #and also the primary key
   my $primary_key = $self->dbdef_table->primary_key;
-  my $db_seq = 0;
-  if ( $primary_key ) {
-    my $col = $self->dbdef_table->column($primary_key);
-    
-    $db_seq =
-      uc($col->type) eq 'SERIAL'
-      || ( driver_name eq 'Pg'
-             && defined($col->default)
-             && $col->default =~ /^nextval\(/i
-         )
-      || ( driver_name eq 'mysql'
-             && defined($col->local)
-             && $col->local =~ /AUTO_INCREMENT/i
-         );
-    $self->unique($primary_key) unless $self->getfield($primary_key) || $db_seq;
-  }
+  $self->unique($primary_key) 
+    if $primary_key && ! $self->getfield($primary_key);
 
-  my $table = $self->table;
   #false laziness w/delete
   my @fields =
     grep defined($self->getfield($_)) && $self->getfield($_) ne "",
     $self->fields
   ;
-  my @values = map { _quote( $self->getfield($_), $table, $_) } @fields;
+  my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
   #eslaf
 
-  my $statement = "INSERT INTO $table ( ".
+  my $statement = "INSERT INTO ". $self->table. " ( ".
       join( ', ', @fields ).
     ") VALUES (".
       join( ', ', @values ).
@@ -520,6 +506,15 @@ sub insert {
   warn "[debug]$me $statement\n" if $DEBUG > 1;
   my $sth = dbh->prepare($statement) or return dbh->errstr;
 
+  my $h_sth;
+  if ( defined $dbdef->table('h_'. $self->table) ) {
+    my $h_statement = $self->_h_statement('insert');
+    warn "[debug]$me $h_statement\n" if $DEBUG > 2;
+    $h_sth = dbh->prepare($h_statement) or return dbh->errstr;
+  } else {
+    $h_sth = '';
+  }
+
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE'; 
@@ -528,64 +523,7 @@ sub insert {
   local $SIG{PIPE} = 'IGNORE';
 
   $sth->execute or return $sth->errstr;
-
-  if ( $db_seq ) { # get inserted id from the database, if applicable
-    warn "[debug]$me retreiving sequence from database\n" if $DEBUG;
-    my $insertid = '';
-    if ( driver_name eq 'Pg' ) {
-
-      my $oid = $sth->{'pg_oid_status'};
-      my $i_sql = "SELECT $primary_key FROM $table WHERE oid = ?";
-      my $i_sth = dbh->prepare($i_sql) or do {
-        dbh->rollback if $FS::UID::AutoCommit;
-        return dbh->errstr;
-      };
-      $i_sth->execute($oid) or do {
-        dbh->rollback if $FS::UID::AutoCommit;
-        return $i_sth->errstr;
-      };
-      $insertid = $i_sth->fetchrow_arrayref->[0];
-
-    } elsif ( driver_name eq 'mysql' ) {
-
-      $insertid = dbh->{'mysql_insertid'};
-      # work around mysql_insertid being null some of the time, ala RT :/
-      unless ( $insertid ) {
-        warn "WARNING: DBD::mysql didn't return mysql_insertid; ".
-             "using SELECT LAST_INSERT_ID();";
-        my $i_sql = "SELECT LAST_INSERT_ID()";
-        my $i_sth = dbh->prepare($i_sql) or do {
-          dbh->rollback if $FS::UID::AutoCommit;
-          return dbh->errstr;
-        };
-        $i_sth->execute or do {
-          dbh->rollback if $FS::UID::AutoCommit;
-          return $i_sth->errstr;
-        };
-        $insertid = $i_sth->fetchrow_arrayref->[0];
-      }
-
-    } else {
-      dbh->rollback if $FS::UID::AutoCommit;
-      return "don't know how to retreive inserted ids from ". driver_name. 
-             ", try using counterfiles (maybe run dbdef-create?)";
-    }
-    $self->setfield($primary_key, $insertid);
-  }
-
-  my $h_sth;
-  if ( defined $dbdef->table('h_'. $table) ) {
-    my $h_statement = $self->_h_statement('insert');
-    warn "[debug]$me $h_statement\n" if $DEBUG > 2;
-    $h_sth = dbh->prepare($h_statement) or do {
-      dbh->rollback if $FS::UID::AutoCommit;
-      return dbh->errstr;
-    };
-  } else {
-    $h_sth = '';
-  }
   $h_sth->execute or return $h_sth->errstr if $h_sth;
-
   dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
 
   '';
@@ -598,7 +536,7 @@ Depriciated (use insert instead).
 =cut
 
 sub add {
-  cluck "warning: FS::Record::add deprecated!";
+  cluck "warning: FS::Record::add depriciated!";
   insert @_; #call method in this scope
 }
 
@@ -616,7 +554,7 @@ sub delete {
     map {
       $self->getfield($_) eq ''
         #? "( $_ IS NULL OR $_ = \"\" )"
-        ? ( driver_name eq 'Pg'
+        ? ( driver_name =~ /^Pg$/i
               ? "$_ IS NULL"
               : "( $_ IS NULL OR $_ = \"\" )"
           )
@@ -662,7 +600,7 @@ Depriciated (use delete instead).
 =cut
 
 sub del {
-  cluck "warning: FS::Record::del deprecated!";
+  cluck "warning: FS::Record::del depriciated!";
   &delete(@_); #call method in this scope
 }
 
@@ -702,7 +640,7 @@ sub replace {
       map {
         $old->getfield($_) eq ''
           #? "( $_ IS NULL OR $_ = \"\" )"
-          ? ( driver_name eq 'Pg'
+          ? ( driver_name =~ /^Pg$/i
                 ? "$_ IS NULL"
                 : "( $_ IS NULL OR $_ = \"\" )"
             )
@@ -755,7 +693,7 @@ Depriciated (use replace instead).
 =cut
 
 sub rep {
-  cluck "warning: FS::Record::rep deprecated!";
+  cluck "warning: FS::Record::rep depriciated!";
   replace @_; #call method in this scope
 }
 
@@ -788,13 +726,8 @@ sub _h_statement {
 
 =item unique COLUMN
 
-B<Warning>: External use is B<deprecated>.  
-
-Replaces COLUMN in record with a unique number, using counters in the
-filesystem.  Used by the B<insert> method on single-field unique columns
-(see L<DBIx::DBSchema::Table>) and also as a fallback for primary keys
-that aren't SERIAL (Pg) or AUTO_INCREMENT (mysql).
-
+Replaces COLUMN in record with a unique number.  Called by the B<add> method
+on primary keys and single-field unique columns (see L<DBIx::DBSchema::Table>).
 Returns the new value.
 
 =cut
@@ -803,6 +736,8 @@ sub unique {
   my($self,$field) = @_;
   my($table)=$self->table;
 
+  #croak("&FS::UID::checkruid failed") unless &checkruid;
+
   croak "Unique called on field $field, but it is ",
         $self->getfield($field),
         ", not null!"
@@ -818,8 +753,9 @@ sub unique {
 #  my($counter) = new File::CounterFile "$user/$table.$field",0;
 # endhack
 
-  my $index = $counter->inc;
-  $index = $counter->inc while qsearchs($table, { $field=>$index } );
+  my($index)=$counter->inc;
+  $index=$counter->inc
+    while qsearchs($table,{$field=>$index}); #just in case
 
   $index =~ /^(\d*)$/;
   $index=$1;
@@ -1237,14 +1173,14 @@ sub _quote {
 
 =item hfields TABLE
 
-This is deprecated.  Don't use it.
+This is depriciated.  Don't use it.
 
 It returns a hash-type list with the fields of this record's table set true.
 
 =cut
 
 sub hfields {
-  carp "warning: hfields is deprecated";
+  carp "warning: hfields is depriciated";
   my($table)=@_;
   my(%hash);
   foreach (fields($table)) {
@@ -1280,7 +1216,7 @@ sub DESTROY { return; }
 This module should probably be renamed, since much of the functionality is
 of general use.  It is not completely unlike Adapter::DBI (see below).
 
-Exported qsearch and qsearchs should be deprecated in favor of method calls
+Exported qsearch and qsearchs should be depriciated in favor of method calls
 (against an FS::Record object like the old search and searchs that qsearch
 and qsearchs were on top of.)
 
@@ -1288,7 +1224,7 @@ The whole fields / hfields mess should be removed.
 
 The various WHERE clauses should be subroutined.
 
-table string should be deprecated in favor of DBIx::DBSchema::Table.
+table string should be depriciated in favor of DBIx::DBSchema::Table.
 
 No doubt we could benefit from a Tied hash.  Documenting how exists / defined
 true maps to the database (and WHERE clauses) would also help.
index ebf9b96..8934d49 100644 (file)
@@ -87,7 +87,7 @@ sub forksuidsetup {
   getsecrets;
   $dbh = DBI->connect($datasrc,$db_user,$db_pass, {
                           'AutoCommit' => 0,
-                          #'ChopBlanks' => 1,
+                          'ChopBlanks' => 1,
   } ) or die "DBI->connect error: $DBI::errstr\n";
 
   foreach ( keys %callback ) {
@@ -171,7 +171,9 @@ Returns the current Freeside user.
 =cut
 
 sub getotaker {
-  $user;
+  #$user;
+  #stupid kludge until schema otaker fields are not 8 chars
+  substr($user,0,8);
 }
 
 =item cgisetotaker
@@ -256,7 +258,7 @@ coderef into the hash %FS::UID::callback :
 
 =head1 VERSION
 
-$Id: UID.pm,v 1.21 2002-09-27 12:14:12 ivan Exp $
+$Id: UID.pm,v 1.19 2002-08-29 06:02:52 ivan Exp $
 
 =head1 BUGS
 
diff --git a/FS/FS/ac.pm b/FS/FS/ac.pm
deleted file mode 100644 (file)
index 5a2b360..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-package FS::ac;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch );
-use FS::ac_type;
-use FS::ac_block;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::ac - Object methods for ac records
-
-=head1 SYNOPSIS
-
-  use FS::ac;
-
-  $record = new FS::ac \%hash;
-  $record = new FS::ac { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::ac record describes a broadband Access Concentrator, such as a DSLAM
-or a wireless access point.  FS::ac inherits from FS::Record.  The following 
-fields are currently supported:
-
-narf
-
-=over 4
-
-=item acnum - primary key
-
-=item actypenum - AC type, see L<FS::ac_type>
-
-=item acname - descriptive name for the AC
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record.  To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'ac'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database.  If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  my $error =
-    $self->ut_numbern('acnum')
-    || $self->ut_number('actypenum')
-    || $self->ut_text('acname');
-  return $error if $error;
-
-  return "Unknown actypenum"
-    unless $self->ac_type;
-  '';
-}
-
-=item ac_type
-
-Returns the L<FS::ac_type> object corresponding to this object.
-
-=cut
-
-sub ac_type {
-  my $self = shift;
-  return qsearchs('ac_type', { actypenum => $self->actypenum });
-}
-
-=item ac_block
-
-Returns a list of L<FS::ac_block> objects (address blocks) associated
-with this object.
-
-=cut
-
-sub ac_block {
-  my $self = shift;
-  return qsearch('ac_block', { acnum => $self->acnum });
-}
-
-=item ac_field
-
-Returns a hash of L<FS::ac_field> objects assigned to this object.
-
-=cut
-
-sub ac_field {
-  my $self = shift;
-
-  return qsearch('ac_field', { acnum => $self->acnum });
-}
-
-=back
-
-=head1 VERSION
-
-$Id:
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_broadband>, L<FS::ac>, L<FS::ac_block>, L<FS::ac_field>,  schema.html
-from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/ac_block.pm b/FS/FS/ac_block.pm
deleted file mode 100755 (executable)
index 09de6a4..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-package FS::ac_block;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch );
-use FS::ac_type;
-use FS::ac;
-use FS::svc_broadband;
-use NetAddr::IP;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::ac - Object methods for ac records
-
-=head1 SYNOPSIS
-
-  use FS::ac_block;
-
-  $record = new FS::ac_block \%hash;
-  $record = new FS::ac_block { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::ac_block record describes an address block assigned for broadband 
-access.  FS::ac_block inherits from FS::Record.  The following fields are 
-currently supported:
-
-=over 4
-
-=item acnum - the access concentrator (see L<FS::ac_type>) to which this 
-block is assigned.
-
-=item ip_gateway - the gateway address used by customers within this block.  
-This functions as the primary key.
-
-=item ip_netmask - the netmask of the block, expressed as an integer.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record.  To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'ac_block'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database.  If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  my $error =
-    $self->ut_number('acnum')
-    || $self->ut_ip('ip_gateway')
-    || $self->ut_number('ip_netmask')
-  ;
-  return $error if $error;
-
-  return "Unknown acnum"
-    unless $self->ac;
-
-  my $self_addr = new NetAddr::IP ($self->ip_gateway, $self->ip_netmask);
-  return "Cannot parse address: ". $self->ip_gateway . '/' . $self->ip_netmask
-    unless $self_addr;
-
-  my @block = grep {
-    my $block_addr = new NetAddr::IP ($_->ip_gateway, $_->ip_netmask);
-    if($block_addr->contains($self_addr) 
-    or $self_addr->contains($block_addr)) { $_; };
-  } qsearch( 'ac_block', {});
-
-  foreach(@block) {
-    return "Block intersects existing block ".$_->ip_gateway."/".$_->ip_netmask;
-  }
-
-  '';
-}
-
-
-=item ac
-
-Returns the L<FS::ac> object corresponding to this object.
-
-=cut
-
-sub ac {
-  my $self = shift;
-  return qsearchs('ac', { acnum => $self->acnum });
-}
-
-=item svc_broadband
-
-Returns a list of L<FS::svc_broadband> objects associated
-with this object.
-
-=cut
-
-#sub svc_broadband {
-#  my $self = shift;
-#  my @svc = qsearch('svc_broadband', { actypenum => $self->ac->ac_type->actypenum });
-#  return grep { 
-#    my $svc_addr = new NetAddr::IP($_->ip_addr, $_->ip_netmask);
-#    $self_addr->contains($svc_addr);
-#  } @svc;
-#}
-
-=back
-
-=cut
-
-1;
-
diff --git a/FS/FS/ac_field.pm b/FS/FS/ac_field.pm
deleted file mode 100755 (executable)
index f601119..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-package FS::ac_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::part_ac_field;
-use FS::ac;
-
-use UNIVERSAL qw( can );
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::ac_field - Object methods for ac_field records
-
-=head1 SYNOPSIS
-
-  use FS::ac_field;
-
-  $record = new FS::ac_field \%hash;
-  $record = new FS::ac_field { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-L<FS::ac_field> contains values of fields defined by L<FS::part_ac_field>
-for an L<FS::ac>.  Values must be of the data type defined by ut_type in
-L<FS::part_ac_field>.
-Supported fields as follows:
-
-=over 4
-
-=item acfieldpart - Type of ac_field as defined by L<FS::part_ac_field>
-
-=item acnum - The L<FS::ac> to which this value belongs.
-
-=item value - The contents of the field.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record.  To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'ac_field'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database.  If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  return "acnum must be defined" unless $self->acnum;
-  return "acfieldpart must be defined" unless $self->acfieldpart;
-
-  my $ut_func = $self->can("ut_" . $self->part_ac_field->ut_type);
-  my $error = $self->$ut_func('value');
-
-  return $error if $error;
-
-  ''; #no error
-}
-
-=item part_ac_field
-
-Returns a reference to the L<FS:part_ac_field> that defines this L<FS::ac_field>
-
-=cut
-
-sub part_ac_field {
-  my $self = shift;
-
-  return qsearchs('part_ac_field', { acfieldpart => $self->acfieldpart });
-}
-
-=item ac
-
-Returns a reference to the L<FS::ac> to which this L<FS::ac_field> belongs.
-
-=cut
-
-sub ac {
-  my $self = shift;
-
-  return qsearchs('ac', { acnum => $self->acnum });
-}
-
-=back
-
-=head1 VERSION
-
-$Id: 
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_broadband>, L<FS::ac>, L<FS::ac_block>, L<FS::ac_field>,  schema.html
-from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/ac_type.pm b/FS/FS/ac_type.pm
deleted file mode 100755 (executable)
index e83c5c5..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-package FS::ac_type;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::ac;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::ac_type - Object methods for ac_type records
-
-=head1 SYNOPSIS
-
-  use FS::ac_type;
-
-  $record = new FS::ac_type \%hash;
-  $record = new FS::ac_type { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-L<FS::ac_type> refers to a type of access concentrator.  L<FS::svc_broadband>
-records refer to a specific L<FS::ac_type> limiting the choice of access
-concentrator to one of the chosen type.  This should be set as a fixed
-default in part_svc to prevent provisioning the wrong type of service for
-a given package or service type.  Supported fields as follows:
-
-=over 4
-
-=item actypenum - Primary key.  see L<FS::ac>
-
-=item actypename - Text identifier for access concentrator type.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record.  To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'ac_type'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database.  If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-
-  # What do we check?
-
-  ''; #no error
-}
-
-=item ac
-
-Returns a list of all L<FS::ac> records of this type.
-
-=cut
-
-sub ac {
-  my $self = shift;
-
-  return qsearch('ac', { actypenum => $self->actypenum });
-}
-
-=item part_ac_field
-
-Returns a list of all L<FS::part_ac_field> records of this type.
-
-=cut
-
-sub part_ac_field {
-  my $self = shift;
-
-  return qsearch('part_ac_field', { actypenum => $self->actypenum });
-}
-
-=back
-
-=head1 VERSION
-
-$Id: 
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_broadband>, L<FS::ac>, L<FS::ac_block>, L<FS::ac_field>,  schema.html
-from the base documentation.
-
-=cut
-
-1;
-
index c661baa..009f6af 100644 (file)
@@ -3,6 +3,7 @@ package FS::cust_bill;
 use strict;
 use vars qw( @ISA $conf $money_char );
 use vars qw( $lpr $invoice_from $smtpmachine );
+use vars qw( $cybercash );
 use vars qw( $xaction $E_NoErr );
 use vars qw( $bop_processor $bop_login $bop_password $bop_action @bop_options );
 use vars qw( $ach_processor $ach_login $ach_password $ach_action @ach_options );
@@ -41,7 +42,28 @@ $FS::UID::callback{'FS::cust_bill'} = sub {
   ( $ach_processor,$ach_login, $ach_password, $ach_action ) = ( '', '', '', '');
   @ach_options = ();
 
-  if ( $conf->exists('business-onlinepayment') ) {
+  if ( $conf->exists('cybercash3.2') ) {
+    require CCMckLib3_2;
+      #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2);
+    require CCMckDirectLib3_2;
+      #qw(SendCC2_1Server);
+    require CCMckErrno3_2;
+      #qw(MCKGetErrorMessage $E_NoErr);
+    import CCMckErrno3_2 qw($E_NoErr);
+
+    my $merchant_conf;
+    ($merchant_conf,$xaction)= $conf->config('cybercash3.2');
+    my $status = &CCMckLib3_2::InitConfig($merchant_conf);
+    if ( $status != $E_NoErr ) {
+      warn "CCMckLib3_2::InitConfig error:\n";
+      foreach my $key (keys %CCMckLib3_2::Config) {
+        warn "  $key => $CCMckLib3_2::Config{$key}\n"
+      }
+      my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status);
+      die "CCMckLib3_2::InitConfig fatal error: $errmsg\n";
+    }
+    $cybercash='cybercash3.2';
+  } elsif ( $conf->exists('business-onlinepayment') ) {
     ( $bop_processor,
       $bop_login,
       $bop_password,
@@ -370,7 +392,7 @@ sub send {
   my @print_text = $self->print_text('', $template);
   my @invoicing_list = $self->cust_main->invoicing_list;
 
-  if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list  ) { #email
+  if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list ) { #email
 
     #better to notify this person than silence
     @invoicing_list = ($invoice_from) unless @invoicing_list;
@@ -532,13 +554,10 @@ sub send_csv {
         time2str("%x", $cust_bill_pkg->edate),
       );
 
-    } else { #pkgnum tax
+    } else { #pkgnum Tax
       next unless $cust_bill_pkg->setup != 0;
-      my $itemdesc = defined $cust_bill_pkg->dbdef_table->column('itemdesc')
-                       ? ( $cust_bill_pkg->itemdesc || 'Tax' )
-                       : 'Tax';
       ($pkg, $setup, $recur, $sdate, $edate) =
-        ( $itemdesc, sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' );
+        ( 'Tax', sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' );
     }
 
     $csv->combine(
@@ -812,7 +831,7 @@ sub realtime_bop {
        'invnum'   => $self->invnum,
        'paid'     => $amount,
        '_date'     => '',
-       'payby'    => method2payby{$method},
+       'payby'    => $method2payby{$method},
        'payinfo'  => $cust_main->payinfo,
        'paybatch' => "$processor:". $transaction->authorization,
     } );
@@ -874,6 +893,84 @@ sub realtime_bop {
 
 }
 
+=item realtime_card_cybercash
+
+Attempts to pay this invoice with the CyberCash CashRegister realtime gateway.
+
+=cut
+
+sub realtime_card_cybercash {
+  my $self = shift;
+  my $cust_main = $self->cust_main;
+  my $amount = $self->owed;
+
+  return "CyberCash CashRegister real-time card processing not enabled!"
+    unless $cybercash eq 'cybercash3.2';
+
+  my $address = $cust_main->address1;
+  $address .= ", ". $cust_main->address2 if $cust_main->address2;
+
+  #fix exp. date
+  #$cust_main->paydate =~ /^(\d+)\/\d*(\d{2})$/;
+  $cust_main->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+  my $exp = "$2/$1";
+
+  #
+
+  my $paybatch = $self->invnum. 
+                  '-' . time2str("%y%m%d%H%M%S", time);
+
+  my $payname = $cust_main->payname ||
+                $cust_main->getfield('first').' '.$cust_main->getfield('last');
+
+  my $country = $cust_main->country eq 'US' ? 'USA' : $cust_main->country;
+
+  my @full_xaction = ( $xaction,
+    'Order-ID'     => $paybatch,
+    'Amount'       => "usd $amount",
+    'Card-Number'  => $cust_main->getfield('payinfo'),
+    'Card-Name'    => $payname,
+    'Card-Address' => $address,
+    'Card-City'    => $cust_main->getfield('city'),
+    'Card-State'   => $cust_main->getfield('state'),
+    'Card-Zip'     => $cust_main->getfield('zip'),
+    'Card-Country' => $country,
+    'Card-Exp'     => $exp,
+  );
+
+  my %result;
+  %result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction);
+  
+  if ( $result{'MStatus'} eq 'success' ) { #cybercash smps v.2 or 3
+    my $cust_pay = new FS::cust_pay ( {
+       'invnum'   => $self->invnum,
+       'paid'     => $amount,
+       '_date'     => '',
+       'payby'    => 'CARD',
+       'payinfo'  => $cust_main->payinfo,
+       'paybatch' => "$cybercash:$paybatch",
+    } );
+    my $error = $cust_pay->insert;
+    if ( $error ) {
+      # gah, even with transactions.
+      my $e = 'WARNING: Card debited but database not updated - '.
+              'error applying payment, invnum #' . $self->invnum.
+              " (CyberCash Order-ID $paybatch): $error";
+      warn $e;
+      return $e;
+    } else {
+      return '';
+    }
+#  } elsif ( $result{'Mstatus'} ne 'failure-bad-money'
+#            || $options{'report_badcard'}
+#          ) {
+  } else {
+     return 'Cybercash error, invnum #' . 
+       $self->invnum. ':'. $result{'MErrMsg'};
+  }
+
+}
+
 =item batch_card
 
 Adds a payment for this invoice to the pending credit card batch (see
@@ -953,9 +1050,7 @@ sub print_text {
   }
 
   #new charges
-  foreach ( ( grep {   $_->pkgnum } $self->cust_bill_pkg ),  #packages first
-            ( grep { ! $_->pkgnum } $self->cust_bill_pkg ),  #then taxes
-  ) {
+  foreach ( $self->cust_bill_pkg ) {
 
     if ( $_->pkgnum ) {
 
@@ -979,11 +1074,8 @@ sub print_text {
           map { [ "  ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels;
       }
 
-    } else { #pkgnum tax
-      my $itemdesc = defined $_->dbdef_table->column('itemdesc')
-                     ? ( $_->itemdesc || 'Tax' )
-                     : 'Tax';
-      push @buf,[$itemdesc, $money_char. sprintf("%10.2f",$_->setup) ] 
+    } else { #pkgnum Tax
+      push @buf,["Tax", $money_char. sprintf("%10.2f",$_->setup) ] 
         if $_->setup != 0;
     }
   }
@@ -1130,7 +1222,7 @@ sub print_text {
 
 =head1 VERSION
 
-$Id: cust_bill.pm,v 1.61 2003-01-10 07:41:05 ivan Exp $
+$Id: cust_bill.pm,v 1.41.2.20 2003-01-10 07:42:39 ivan Exp $
 
 =head1 BUGS
 
index 5a1dcd2..72f9ce4 100644 (file)
@@ -47,8 +47,6 @@ supported:
 
 =item edate - ending date of recurring fee
 
-=item itemdesc - Line item description (currentlty used only when pkgnum is 0)
-
 =back
 
 sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">.  Also
@@ -113,7 +111,6 @@ sub check {
       || $self->ut_money('recur')
       || $self->ut_numbern('sdate')
       || $self->ut_numbern('edate')
-      || $self->ut_textn('itemdesc')
   ;
   return $error if $error;
 
@@ -143,7 +140,7 @@ sub cust_pkg {
 
 =head1 VERSION
 
-$Id: cust_bill_pkg.pm,v 1.4 2002-09-21 11:17:39 ivan Exp $
+$Id: cust_bill_pkg.pm,v 1.3 2002-04-06 22:32:43 ivan Exp $
 
 =head1 BUGS
 
index 91ffa45..7045f96 100644 (file)
@@ -172,8 +172,6 @@ FS::Record.  The following fields are currently supported:
 
 =item comments - comments (optional)
 
-=item referral_custnum - referring customer number
-
 =back
 
 =head1 METHODS
@@ -486,7 +484,7 @@ sub replace {
 
   if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ &&
        grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) {
-    # card/check info has changed, want to retry realtime_card invoice events
+    # card info has changed, want to retry realtime_card invoice events
     #false laziness w/collect
     foreach my $cust_bill_event (
       grep {
@@ -923,12 +921,10 @@ sub bill {
   my( $total_setup, $total_recur ) = ( 0, 0 );
   #my( $taxable_setup, $taxable_recur ) = ( 0, 0 );
   my @cust_bill_pkg = ();
-  #my $tax = 0;##
+  my $tax = 0;##
   #my $taxable_charged = 0;##
   #my $charged = 0;##
 
-  my %tax;
-
   foreach my $cust_pkg (
     qsearch('cust_pkg', { 'custnum' => $self->custnum } )
   ) {
@@ -1121,10 +1117,7 @@ sub bill {
           } #if $cust_main_county->exempt_amount
 
           $taxable_charged = sprintf( "%.2f", $taxable_charged);
-
-          #$tax += $taxable_charged * $cust_main_county->tax / 100
-          $tax{ $cust_main_county->taxname || 'Tax' } +=
-            $taxable_charged * $cust_main_county->tax / 100
+          $tax += $taxable_charged * $cust_main_county->tax / 100
 
         } #unless $self->tax =~ /Y/i
           #       || $self->payby eq 'COMP'
@@ -1157,17 +1150,16 @@ sub bill {
 #      $taxable_charged * ( $cust_main_county->getfield('tax') / 100 )
 #    );
 
-  foreach my $taxname ( grep { $tax{$_} > 0 } keys %tax ) {
-    my $tax = sprintf("%.2f", $tax{$taxname} );
+  $tax = sprintf("%.2f", $tax);
+  if ( $tax > 0 ) {
     $charged = sprintf( "%.2f", $charged+$tax );
 
     my $cust_bill_pkg = new FS::cust_bill_pkg ({
-      'pkgnum'   => 0,
-      'setup'    => $tax,
-      'recur'    => 0,
-      'sdate'    => '',
-      'edate'    => '',
-      'itemdesc' => $taxname,
+      'pkgnum' => 0,
+      'setup'  => $tax,
+      'recur'  => 0,
+      'sdate'  => '',
+      'edate'  => '',
     });
     push @cust_bill_pkg, $cust_bill_pkg;
   }
index d8796e4..e41564d 100644 (file)
@@ -61,8 +61,6 @@ currently supported:
 
 =item exempt_amount
 
-=item taxname - if defined, printed on invoices instead of "Tax"
-
 =back
 
 =head1 METHODS
@@ -112,7 +110,6 @@ sub check {
     || $self->ut_float('tax')
     || $self->ut_textn('taxclass') # ...
     || $self->ut_money('exempt_amount')
-    || $self->ut_textn('taxname')
   ;
 
 }
index bcb1437..a5533a0 100644 (file)
@@ -134,6 +134,13 @@ sub checkdest {
       unless qsearchs( 'svc_acct', { 'svcnum' => $self->dest } );
   } elsif ( $self->dest =~ /^([\w\.\-\&\+]+)\@(([\w\.\-]+\.)+\w+)$/ ) {
     my($user, $domain) = ($1, $2);
+#    if ( $domain eq $mydomain ) {
+#      my $svc_acct = qsearchs( 'svc_acct', { 'username' => $user } );
+#      return "Unknown local account: $user\@$domain (specified literally)"
+#        unless $svc_acct;
+#      $svc_acct->svcnum =~ /^(\d+)$/ or die "Non-numeric svcnum?!";
+#      $self->dest($1);
+#    }
     $self->dest("$1\@$2");
   } else {
     return gettext("illegal_email_invoice_address");
@@ -163,7 +170,7 @@ sub address {
 
 =head1 VERSION
 
-$Id: cust_main_invoice.pm,v 1.13 2002-09-18 22:50:44 ivan Exp $
+$Id: cust_main_invoice.pm,v 1.12 2002-04-12 13:22:02 ivan Exp $
 
 =head1 BUGS
 
index 79cf827..67fdcf2 100644 (file)
@@ -406,7 +406,7 @@ sub unapplied {
 
 =head1 VERSION
 
-$Id: cust_pay.pm,v 1.23 2002-11-19 09:51:58 ivan Exp $
+$Id: cust_pay.pm,v 1.21.4.2 2002-11-19 09:52:02 ivan Exp $
 
 =head1 BUGS
 
index 88ffd4d..5132abe 100644 (file)
@@ -1,8 +1,7 @@
 package FS::cust_pkg;
 
 use strict;
-use vars qw(@ISA $disable_agentcheck);
-use vars qw( $quiet );
+use vars qw(@ISA $quiet $disable_agentcheck);
 use FS::UID qw( getotaker dbh );
 use FS::Record qw( qsearch qsearchs );
 use FS::cust_svc;
@@ -16,6 +15,7 @@ use FS::cust_bill_pkg;
 # setup }
 # because they load configuraion by setting FS::UID::callback (see TODO)
 use FS::svc_acct;
+use FS::svc_acct_sm;
 use FS::svc_domain;
 use FS::svc_www;
 use FS::svc_forward;
@@ -596,8 +596,7 @@ sub seconds_since_sqlradacct {
 
 Returns the sum of the given attribute for all accounts (see L<FS::svc_acct>)
 in this package for sessions ending between TIMESTAMP_START (inclusive) and
-TIMESTAMP_END
-(exclusive).
+TIMESTAMP_END (exclusive).
 
 TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
 L<perlfunc/"time">.  Also see L<Time::Local> and L<Date::Parse> for conversion
@@ -813,12 +812,11 @@ In sub order, the @pkgparts array (passed by reference) is clobbered.
 Also in sub order, no money is adjusted.  Once FS::part_pkg defines a standard
 method to pass dates to the recur_prog expression, it should do so.
 
-FS::svc_acct, FS::svc_domain, FS::svc_www, FS::svc_ip and FS::svc_forward are
-loaded via 'use' at compile time, rather than via 'require' in sub { setup,
-suspend, unsuspend, cancel } because they use %FS::UID::callback to load
-configuration values.  Probably need a subroutine which decides what to do
-based on whether or not we've fetched the user yet, rather than a hash.  See
-FS::UID and the TODO.
+FS::svc_acct, FS::svc_acct_sm, and FS::svc_domain are loaded via 'use' at 
+compile time, rather than via 'require' in sub { setup, suspend, unsuspend,
+cancel } because they use %FS::UID::callback to load configuration values.
+Probably need a subroutine which decides what to do based on whether or not
+we've fetched the user yet, rather than a hash.  See FS::UID and the TODO.
 
 Now that things are transactional should the check in the insert method be
 moved to check ?
index 7636717..aa81003 100644 (file)
@@ -267,7 +267,7 @@ sub check {
 
 =head1 VERSION
 
-$Id: cust_refund.pm,v 1.20 2002-11-19 09:51:58 ivan Exp $
+$Id: cust_refund.pm,v 1.18.4.2 2002-11-19 09:52:02 ivan Exp $
 
 =head1 BUGS
 
index 66daec1..800afaf 100644 (file)
@@ -9,6 +9,7 @@ use FS::part_pkg;
 use FS::part_svc;
 use FS::pkg_svc;
 use FS::svc_acct;
+use FS::svc_acct_sm;
 use FS::svc_domain;
 use FS::svc_forward;
 use FS::domain_record;
@@ -277,6 +278,11 @@ sub label {
   my $tag;
   if ( $svcdb eq 'svc_acct' ) {
     $tag = $svc_x->email;
+  } elsif ( $svcdb eq 'svc_acct_sm' ) {
+    my $domuser = $svc_x->domuser eq '*' ? '(anything)' : $svc_x->domuser;
+    my $svc_domain = qsearchs ( 'svc_domain', { 'svcnum' => $svc_x->domsvc } );
+    my $domain = $svc_domain->domain;
+    $tag = "$domuser\@$domain";
   } elsif ( $svcdb eq 'svc_forward' ) {
     my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->srcsvc } );
     $tag = $svc_acct->email. '->';
@@ -291,8 +297,6 @@ sub label {
   } elsif ( $svcdb eq 'svc_www' ) {
     my $domain = qsearchs( 'domain_record', { 'recnum' => $svc_x->recnum } );
     $tag = $domain->reczone;
-  } elsif ( $svcdb eq 'svc_broadband' ) {
-    $tag = $svc_x->ip_addr . '/' . $svc_x->ip_netmask;
   } else {
     cluck "warning: asked for label of unsupported svcdb; using svcnum";
     $tag = $svc_x->getfield('svcnum');
diff --git a/FS/FS/part_ac_field.pm b/FS/FS/part_ac_field.pm
deleted file mode 100755 (executable)
index dcb4452..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-package FS::part_ac_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs );
-use FS::ac_field;
-use FS::ac;
-
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::part_ac_field - Object methods for part_ac_field records
-
-=head1 SYNOPSIS
-
-  use FS::part_ac_field;
-
-  $record = new FS::part_ac_field \%hash;
-  $record = new FS::part_ac_field { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-=head1 DESCRIPTION
-
-
-=over 4
-
-=item blank
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record.  To add the record to the database, see L<"insert">.
-
-=cut
-
-sub table { 'part_ac_field'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database.  If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record.  If there is an error,
-returns the error, otherwise returns false.  Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-  my $error = '';
-
-  $self->name =~ /^([a-z0-9_\-\.]{1,15})$/i
-    or return "Invalid field name for part_ac_field";
-
-  ''; #no error
-}
-
-
-=back
-
-=head1 VERSION
-
-$Id: 
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_broadband>, L<FS::ac>, L<FS::ac_block>, L<FS::ac_field>,  schema.html
-from the base documentation.
-
-=cut
-
-1;
-
index d62bef5..d493bba 100644 (file)
@@ -878,6 +878,8 @@ tie my %ldap_options, 'Tie::IxHash',
 
   },
 
+  'svc_acct_sm' => {},
+
   'svc_forward' => {
     'sqlmail' => {
       'desc' => 'Real-time export to SQL-backed mail server',
@@ -896,9 +898,6 @@ tie my %ldap_options, 'Tie::IxHash',
 
   },
 
-  'svc_broadband' => {
-  },
-
 );
 
 =back
index 63bc2ad..552019a 100644 (file)
@@ -254,6 +254,31 @@ sub check {
   my @fields = eval { fields( $recref->{svcdb} ) }; #might die
   return "Unknown svcdb!" unless @fields;
 
+##REPLACED BY part_svc_column
+#  my $svcdb;
+#  foreach $svcdb ( qw(
+#    svc_acct svc_acct_sm svc_domain
+#  ) ) {
+#    my @rows = map { /^${svcdb}__(.*)$/; $1 }
+#      grep ! /_flag$/,
+#        grep /^${svcdb}__/,
+#          fields('part_svc');
+#    foreach my $row (@rows) {
+#      unless ( $svcdb eq $recref->{svcdb} ) {
+#        $recref->{$svcdb.'__'.$row}='';
+#        $recref->{$svcdb.'__'.$row.'_flag'}='';
+#        next;
+#      }
+#      $recref->{$svcdb.'__'.$row.'_flag'} =~ /^([DF]?)$/
+#        or return "Illegal flag for $svcdb $row";
+#      $recref->{$svcdb.'__'.$row.'_flag'} = $1;
+#
+#      my $error = $self->ut_anything($svcdb.'__'.$row);
+#      return $error if $error;
+#
+#    }
+#  }
+
   ''; #no error
 }
 
@@ -265,12 +290,12 @@ COLUMNNAME, or a new part_svc_column object if none exists.
 =cut
 
 sub part_svc_column {
-  my( $self, $columnname) = @_;
-  $self->svcpart &&
-    qsearchs('part_svc_column',  {
-                                   'svcpart'    => $self->svcpart,
-                                   'columnname' => $columnname,
-                                 }
+  my $self = shift;
+  my $columnname = shift;
+  qsearchs('part_svc_column',  {
+                                 'svcpart'    => $self->svcpart,
+                                 'columnname' => $columnname,
+                               }
   ) or new FS::part_svc_column {
                                  'svcpart'    => $self->svcpart,
                                  'columnname' => $columnname,
index 3c544ff..1812dbf 100644 (file)
@@ -137,7 +137,7 @@ sub part_svc {
 
 =head1 VERSION
 
-$Id: pkg_svc.pm,v 1.3 2002-06-10 01:39:50 khoff Exp $
+$Id: pkg_svc.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
 
 =head1 BUGS
 
index e0c4662..1a36fa3 100644 (file)
@@ -7,6 +7,7 @@ use vars qw( @ISA $DEBUG $me $conf
              $username_ampersand $username_letter $username_letterfirst
              $username_noperiod $username_nounderscore $username_nodash
              $username_uppercase
+             $mydomain
              $welcome_template $welcome_from $welcome_subject $welcome_mimetype
              $smtpmachine
              $radius_password
@@ -18,9 +19,11 @@ use FS::UID qw( datasrc );
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs fields dbh );
 use FS::svc_Common;
+use Net::SSH;
 use FS::cust_svc;
 use FS::part_svc;
 use FS::svc_acct_pop;
+use FS::svc_acct_sm;
 use FS::cust_main_invoice;
 use FS::svc_domain;
 use FS::raddb;
@@ -51,6 +54,7 @@ $FS::UID::callback{'FS::svc_acct'} = sub {
   $username_nodash = $conf->exists('username-nodash');
   $username_uppercase = $conf->exists('username-uppercase');
   $username_ampersand = $conf->exists('username-ampersand');
+  $mydomain = $conf->config('domain');
   $dirhash = $conf->config('dirhash') || 0;
   if ( $conf->exists('welcome_email') ) {
     $welcome_template = new Text::Template (
@@ -420,6 +424,11 @@ The corresponding FS::cust_svc record will be deleted as well.
 sub delete {
   my $self = shift;
 
+  if ( defined( $FS::Record::dbdef->table('svc_acct_sm') ) ) {
+    return "Can't delete an account which has (svc_acct_sm) mail aliases!"
+      if $self->uid && qsearch( 'svc_acct_sm', { 'domuid' => $self->uid } );
+  }
+
   return "Can't delete an account which is a (svc_forward) source!"
     if qsearch( 'svc_forward', { 'srcsvc' => $self->svcnum } );
 
@@ -785,7 +794,7 @@ sub check {
   unless ( $part_svc->part_svc_column('slipip')->columnflag eq 'F' ) {
     unless ( $recref->{slipip} eq '0e0' ) {
       $recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
-        or return "Illegal slipip". $self->slipip;
+        or return "Illegal slipip". $self->slipip;
       $recref->{slipip} = $1;
     } else {
       $recref->{slipip} = '0e0';
@@ -880,7 +889,7 @@ sub radius_check {
   my $self = shift;
   my $password = $self->_password;
   my $pw_attrib = length($password) <= 12 ? $radius_password : 'Crypt-Password';
-  ( $pw_attrib => $password,
+  ( $pw_attrib => $self->_password,
     map {
       /^(rc_(.*))$/;
       my($column, $attrib) = ($1, $2);
@@ -898,10 +907,14 @@ Returns the domain associated with this account.
 
 sub domain {
   my $self = shift;
-  die "svc_acct.domsvc is null for svcnum ". $self->svcnum unless $self->domsvc;
-  my $svc_domain = $self->svc_domain
-    or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc;
-  $svc_domain->domain;
+  if ( $self->domsvc ) {
+    #$self->svc_domain->domain;
+    my $svc_domain = $self->svc_domain
+      or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc;
+    $svc_domain->domain;
+  } else {
+    $mydomain or die "svc_acct.domsvc is null and no legacy domain config file";
+  }
 }
 
 =item svc_domain
@@ -962,7 +975,7 @@ external SQL radacct table, specified via sqlradius export.  Sessions which
 started in the specified range but are still open are counted from session
 start to the end of the range (unless they are over 1 day old, in which case
 they are presumed missing their stop record and not counted).  Also, sessions
-which end in therange but started earlier are counted from the start of the
+which end in the range but started earlier are counted from the start of the
 range to session end.  Finally, sessions which start before the range but end
 after are counted for the entire range.
 
@@ -996,6 +1009,7 @@ sub attribute_since_sqlradacct {
   $self->cust_svc->attribute_since_sqlradacct(@_);
 }
 
+
 =item radius_groups
 
 Returns all RADIUS groups for this account (see L<FS::radius_usergroup>).
@@ -1202,7 +1216,7 @@ probably live somewhere else...
 L<FS::svc_Common>, edit/part_svc.cgi from an installed web interface,
 export.html from the base documentation, L<FS::Record>, L<FS::Conf>,
 L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, L<FS::queue>,
-L<freeside-queued>), L<FS::svc_acct_pop>,
+L<freeside-queued>), L<Net::SSH>, L<ssh>, L<FS::svc_acct_pop>,
 schema.html from the base documentation.
 
 =cut
diff --git a/FS/FS/svc_acct_sm.pm b/FS/FS/svc_acct_sm.pm
new file mode 100644 (file)
index 0000000..c92f142
--- /dev/null
@@ -0,0 +1,260 @@
+package FS::svc_acct_sm;
+
+use strict;
+use vars qw( @ISA $nossh_hack $conf $shellmachine @qmailmachines );
+use FS::Record qw( fields qsearch qsearchs );
+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 );
+
+#ask FS::UID to run this stuff for us later
+#$FS::UID::callback{'FS::svc_acct_sm'} = sub { 
+#  $conf = new FS::Conf;
+#  $shellmachine = $conf->exists('qmailmachines')
+#                  ? $conf->config('shellmachine')
+#                  : '';
+#};
+
+=head1 NAME
+
+FS::svc_acct_sm - Object methods for svc_acct_sm records
+
+=head1 SYNOPSIS
+
+  use FS::svc_acct_sm;
+
+  $record = new FS::svc_acct_sm \%hash;
+  $record = new FS::svc_acct_sm { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+  $error = $record->suspend;
+
+  $error = $record->unsuspend;
+
+  $error = $record->cancel;
+
+=head1 WARNING
+
+FS::svc_acct_sm is B<depreciated>.  This class is only included for migration
+purposes.  See L<FS::svc_forward>.
+
+=head1 DESCRIPTION
+
+An FS::svc_acct_sm object represents a virtual mail alias.  FS::svc_acct_sm
+inherits from FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key (assigned automatcially for new accounts)
+
+=item domsvc - svcnum of the virtual domain (see L<FS::svc_domain>)
+
+=item domuid - uid of the target account (see L<FS::svc_acct>)
+
+=item domuser - virtual username
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new virtual mail alias.  To add the virtual mail alias to the
+database, see L<"insert">.
+
+=cut
+
+sub table { 'svc_acct_sm'; }
+
+=item insert
+
+Adds this virtual mail alias to the database.  If there is an error, returns
+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.
+
+ #If the configuration values (see L<FS::Conf>) shellmachine and qmailmachines
+ #exist, and domuser is `*' (meaning a catch-all mailbox), the command:
+ #
+ #  [ -e $dir/.qmail-$qdomain-default ] || {
+ #    touch $dir/.qmail-$qdomain-default;
+ #    chown $uid:$gid $dir/.qmail-$qdomain-default;
+ #  }
+ #
+ #is executed on shellmachine via ssh (see L<dot-qmail/"EXTENSION ADDRESSES">).
+ #This behaviour can be surpressed by setting $FS::svc_acct_sm::nossh_hack true.
+
+=cut
+
+sub insert {
+  my $self = shift;
+  my $error;
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  $error=$self->check;
+  return $error if $error;
+
+  return "Domain username (domuser) in use for this domain (domsvc)"
+    if qsearchs('svc_acct_sm',{ 'domuser'=> $self->domuser,
+                                'domsvc' => $self->domsvc,
+                              } );
+
+  return "First domain username (domuser) for domain (domsvc) must be " .
+         qq='*' (catch-all)!=
+    if $self->domuser ne '*'
+       && ! qsearch('svc_acct_sm',{ 'domsvc' => $self->domsvc } )
+       && ! $conf->exists('maildisablecatchall');
+
+  $error = $self->SUPER::insert;
+  return $error if $error;
+
+  #my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $self->domsvc } );
+  #my $svc_acct = qsearchs( 'svc_acct', { 'uid' => $self->domuid } );
+  #my ( $uid, $gid, $dir, $domain ) = (
+  #  $svc_acct->uid,
+  #  $svc_acct->gid,
+  #  $svc_acct->dir,
+  #  $svc_domain->domain,
+  #);
+  #my $qdomain = $domain;
+  #$qdomain =~ s/\./:/g; #see manpage for 'dot-qmail': EXTENSION ADDRESSES
+  #ssh("root\@$shellmachine","[ -e $dir/.qmail-$qdomain-default ] || { touch $dir/.qmail-$qdomain-default; chown $uid:$gid $dir/.qmail-$qdomain-default; }")  
+  #  if ( ! $nossh_hack && $shellmachine && $dir && $self->domuser eq '*' );
+
+  ''; #no error
+
+}
+
+=item delete
+
+Deletes this virtual mail alias from the database.  If there is an error,
+returns the error, otherwise returns false.
+
+The corresponding FS::cust_svc record will be deleted as well.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+  my ( $new, $old ) = ( shift, shift );
+  my $error;
+
+  return "Domain username (domuser) in use for this domain (domsvc)"
+    if ( $old->domuser ne $new->domuser
+         || $old->domsvc != $new->domsvc
+       )  && qsearchs('svc_acct_sm',{
+         'domuser'=> $new->domuser,
+         'domsvc' => $new->domsvc,
+       } )
+     ;
+
+ $new->SUPER::replace($old);
+
+}
+
+=item suspend
+
+Just returns false (no error) for now.
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Just returns false (no error) for now.
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Just returns false (no error) for now.
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid virtual mail alias.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert and
+replace methods.
+
+Sets any fixed values; see L<FS::part_svc>.
+
+=cut
+
+sub check {
+  my $self = shift;
+  my $error;
+
+  my $x = $self->setfixed;
+  return $x unless ref($x);
+  #my $part_svc = $x;
+
+  my($recref) = $self->hashref;
+
+  $recref->{domuser} =~ /^(\*|[a-z0-9_\-]{2,32})$/
+    or return "Illegal domain username (domuser)";
+  $recref->{domuser} = $1;
+
+  $recref->{domsvc} =~ /^(\d+)$/ or return "Illegal domsvc";
+  $recref->{domsvc} = $1;
+  my($svc_domain);
+  return "Unknown domsvc" unless
+    $svc_domain=qsearchs('svc_domain',{'svcnum'=> $recref->{domsvc} } );
+
+  $recref->{domuid} =~ /^(\d+)$/ or return "Illegal uid";
+  $recref->{domuid} = $1;
+  my($svc_acct);
+  return "Unknown uid" unless
+    $svc_acct=qsearchs('svc_acct',{'uid'=> $recref->{domuid} } );
+
+  ''; #no error
+}
+
+=back
+
+=head1 VERSION
+
+$Id: svc_acct_sm.pm,v 1.5 2001-09-06 20:41:59 ivan Exp $
+
+=head1 BUGS
+
+The remote commands should be configurable.
+
+The $recref stuff in sub check should be cleaned up.
+
+=head1 SEE ALSO
+
+L<FS::svc_forward>
+
+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.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
deleted file mode 100755 (executable)
index ab92fb3..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-package FS::svc_broadband;
-
-use strict;
-use vars qw(@ISA $conf);
-#use FS::Record qw( qsearch qsearchs );
-use FS::Record qw( qsearchs qsearch dbh );
-use FS::svc_Common;
-use FS::cust_svc;
-use NetAddr::IP;
-
-@ISA = qw( FS::svc_Common );
-
-$FS::UID::callback{'FS::svc_broadband'} = sub { 
-  $conf = new FS::Conf;
-};
-
-=head1 NAME
-
-FS::svc_broadband - Object methods for svc_broadband records
-
-=head1 SYNOPSIS
-
-  use FS::svc_broadband;
-
-  $record = new FS::svc_broadband \%hash;
-  $record = new FS::svc_broadband { 'column' => 'value' };
-
-  $error = $record->insert;
-
-  $error = $new_record->replace($old_record);
-
-  $error = $record->delete;
-
-  $error = $record->check;
-
-  $error = $record->suspend;
-
-  $error = $record->unsuspend;
-
-  $error = $record->cancel;
-
-=head1 DESCRIPTION
-
-An FS::svc_broadband object represents a 'broadband' Internet connection, such
-as a DSL, cable modem, or fixed wireless link.  These services are assumed to
-have the following properties:
-
-=over 2
-
-=item
-The network consists of one or more 'Access Concentrators' (ACs), such as
-DSLAMs or wireless access points.  (See L<FS::ac>.)
-
-=item
-Each AC provides connectivity to one or more contiguous blocks of IP addresses,
-each described by a gateway address and a netmask.  (See L<FS::ac_block>.)
-
-=item
-Each connection has one or more static IP addresses within one of these blocks.
-
-=item
-The details of configuring routers and other devices are to be handled by a 
-site-specific L<FS::part_export> subclass.
-
-=back
-
-FS::svc_broadband inherits from FS::svc_Common.  The following fields are
-currently supported:
-
-=over 4
-
-=item svcnum - primary key
-
-=item
-actypenum - access concentrator type; see L<FS::ac_type>.  This is included here
-so that a part_svc can specifically be a 'wireless' or 'DSL' service by
-designating actypenum as a fixed field.  It does create a redundant functional
-dependency between this table and ac_type, in that the matching ac_type could
-be found by looking up the IP address in ac_block and then finding the block's
-AC, but part_svc can't do that, and we don't feel like hacking it so that it
-can.
-
-=item
-speed_up - maximum upload speed, in bits per second.  If set to zero, upload
-speed will be unlimited.  Exports that do traffic shaping should handle this
-correctly, and not blindly set the upload speed to zero and kill the customer's
-connection.
-
-=item
-speed_down - maximum download speed, as above
-
-=item
-ip_addr - the customer's IP address.  If the customer needs more than one IP
-address, set this to the address of the customer's router.  As a result, the
-customer's router will have the same address for both it's internal and external
-interfaces thus saving address space.  This has been found to work on most NAT
-routers available.
-
-=item
-ip_netmask - the customer's netmask, as a single integer in the range 0-32.
-(E.g. '24', not '255.255.255.0'.  We assume that address blocks are contiguous.)
-This should be 32 unless the customer has multiple IP addresses.
-
-=item
-mac_addr - the MAC address of the customer's router or other device directly
-connected to the network, if needed.  Some systems (e.g. DHCP, MAC address-based
-access control) may need this.  If not, you may leave it blank.
-
-=item
-location - a human-readable description of the location of the connected site,
-such as its address.  This should not be used for billing or contact purposes;
-that information is stored in L<FS::cust_main>.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new svc_broadband.  To add the record to the database, see
-L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to.  You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-sub table { 'svc_broadband'; }
-
-=item insert
-
-Adds this record to the database.  If there is an error, returns 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.
-
-=cut
-
-# sub insert {}
-# Standard FS::svc_Common::insert
-# (any necessary Deep Magic is handled by exports)
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# Standard FS::svc_Common::delete
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database.  If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# Standard FS::svc_Common::replace
-# Notice a pattern here?
-
-=item suspend
-
-Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=item unsuspend
-
-Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=item cancel
-
-Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=item check
-
-Checks all fields to make sure this is a valid broadband service.  If there is
-an error, returns the error, otherwise returns false.  Called by the insert
-and replace methods.
-
-=cut
-
-sub check {
-  my $self = shift;
-  my $x = $self->setfixed;
-
-  return $x unless ref($x);
-
-  my $error =
-    $self->ut_numbern('svcnum')
-    || $self->ut_foreign_key('actypenum', 'ac_type', 'actypenum')
-    || $self->ut_number('speed_up')
-    || $self->ut_number('speed_down')
-    || $self->ut_ip('ip_addr')
-    || $self->ut_numbern('ip_netmask')
-    || $self->ut_textn('mac_addr')
-    || $self->ut_textn('location')
-  ;
-  return $error if $error;
-
-  if($self->speed_up < 0) { return 'speed_up must be positive'; }
-  if($self->speed_down < 0) { return 'speed_down must be positive'; }
-
-  # This should catch errors in the ip_addr and ip_netmask.  If it doesn't,
-  # they'll almost certainly not map into a valid block anyway.
-  my $self_addr = new NetAddr::IP ($self->ip_addr, $self->ip_netmask);
-  return 'Cannot parse address: ' . $self->ip_addr . '/' . $self->ip_netmask unless $self_addr;
-
-  my @block = grep { 
-    my $block_addr = new NetAddr::IP ($_->ip_gateway, $_->ip_netmask);
-    if ($block_addr->contains($self_addr)) { $_ };
-  } qsearch( 'ac_block', { acnum => $self->acnum });
-
-  if(scalar @block == 0) {
-    return 'Block not found for address '.$self->ip_addr.' in actype '.$self->actypenum;
-  } elsif(scalar @block > 1) {
-    return 'ERROR: Intersecting blocks found for address '.$self->ip_addr.' :'.
-        join ', ', map {$_->ip_addr . '/' . $_->ip_netmask} @block;
-  }
-  # OK, we've found a valid block.  We don't actually _do_ anything with it, though; we 
-  # just take comfort in the knowledge that it exists.
-
-  # A simple qsearchs won't work here.  Since we can assign blocks to customers,
-  # we have to make sure the new address doesn't fall within someone else's
-  # block.  Ugh.
-
-  my @conflicts = grep {
-    my $cust_addr = new NetAddr::IP($_->ip_addr, $_->ip_netmask);
-    if (($cust_addr->contains($self_addr)) and
-        ($_->svcnum ne $self->svcnum)) { $_; };
-  } qsearch('svc_broadband', {});
-
-  if (scalar @conflicts > 0) {
-    return 'Address in use by existing service';
-  }
-
-  # Are we trying to use a network, broadcast, or the AC's address?
-  foreach (qsearch('ac_block', { acnum => $self->acnum })) {
-    my $block_addr = new NetAddr::IP($_->ip_gateway, $_->ip_netmask);
-    if ($block_addr->network->addr eq $self_addr->addr) {
-      return 'Address is network address for block '. $block_addr->network;
-    }
-    if ($block_addr->broadcast->addr eq $self_addr->addr) {
-      return 'Address is broadcast address for block '. $block_addr->network;
-    }
-    if ($block_addr->addr eq $self_addr->addr) {
-      return 'Address belongs to the access concentrator: '. $block_addr->addr;
-    }
-  }
-
-  ''; #no error
-}
-
-=item ac_block
-
-Returns the FS::ac_block record (i.e. the address block) for this broadband service.
-
-=cut
-
-sub ac_block {
-  my $self = shift;
-  my $self_addr = new NetAddr::IP ($self->ip_addr, $self->ip_netmask);
-
-  foreach my $block (qsearch( 'ac_block', {} )) {
-    my $block_addr = new NetAddr::IP ($block->ip_addr, $block->ip_netmask);
-    if($block_addr->contains($self_addr)) { return $block; }
-  }
-  return '';
-}
-
-=item ac_type
-
-Returns the FS::ac_type record for this broadband service.
-
-=cut
-
-sub ac_type {
-  my $self = shift;
-  return qsearchs('ac_type', { actypenum => $self->actypenum });
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_Common>, L<FS::Record>, L<FS::ac_type>, L<FS::ac_block>,
-L<FS::part_svc>, schema.html from the base documentation.
-
-=cut
-
-1;
-
index 0d71b27..637d049 100644 (file)
@@ -251,6 +251,10 @@ sub delete {
   return "Can't delete a domain which has accounts!"
     if qsearch( 'svc_acct', { 'domsvc' => $self->svcnum } );
 
+  return "Can't delete a domain with (svc_acct_sm) mail aliases!"
+    if defined( $FS::Record::dbdef->table('svc_acct_sm') )
+       && qsearch('svc_acct_sm', { 'domsvc' => $self->svcnum } );
+
   #return "Can't delete a domain with (domain_record) zone entries!"
   #  if qsearch('domain_record', { 'svcnum' => $self->svcnum } );
 
index efba60d..99a79b9 100644 (file)
@@ -111,7 +111,7 @@ sub part_pkg {
 
 =head1 VERSION
 
-$Id: type_pkgs.pm,v 1.2 2002-10-04 12:57:06 ivan Exp $
+$Id: type_pkgs.pm,v 1.1.14.1 2002-10-04 12:56:35 ivan Exp $
 
 =head1 BUGS
 
index b3de623..1f8ec6e 100644 (file)
@@ -9,17 +9,13 @@ bin/freeside-email
 bin/freeside-queued
 bin/freeside-addoutsource
 bin/freeside-addoutsourceuser
-bin/freeside-deloutsource
-bin/freeside-deloutsourceuser
 bin/freeside-apply-credits
 bin/freeside-adduser
-bin/freeside-deluser
 bin/freeside-setup
 bin/freeside-setinvoice
 bin/freeside-overdue
 bin/freeside-radgroup
 bin/freeside-receivables-report
-bin/freeside-selfservice-server
 bin/freeside-sqlradius-radacctd
 bin/freeside-sqlradius-reset
 bin/freeside-sqlradius-seconds
@@ -44,9 +40,6 @@ FS/UI/Gtk.pm
 FS/UI/agent.pm
 FS/UID.pm
 FS/Msgcat.pm
-FS/ac.pm
-FS/ac_block.pm
-FS/ac_type.pm
 FS/agent.pm
 FS/agent_type.pm
 FS/cust_bill.pm
@@ -65,7 +58,6 @@ FS/cust_refund.pm
 FS/cust_credit_refund.pm
 FS/cust_svc.pm
 FS/part_bill_event.pm
-FS/part_ac_field.pm
 FS/export_svc.pm
 FS/part_export.pm
 FS/part_export_option.pm
@@ -96,7 +88,7 @@ FS/pkg_svc.pm
 FS/svc_Common.pm
 FS/svc_acct.pm
 FS/svc_acct_pop.pm
-FS/svc_broadband.pm
+FS/svc_acct_sm.pm
 FS/svc_domain.pm
 FS/type_pkgs.pm
 FS/nas.pm
@@ -174,6 +166,7 @@ t/radius_usergroup.t
 t/session.t
 t/svc_acct.t
 t/svc_acct_pop.t
+t/svc_acct_sm.t
 t/svc_Common.t
 t/svc_domain.t
 t/svc_forward.t
index 180cd93..bbad8aa 100644 (file)
@@ -11,5 +11,5 @@ freeside-adduser -h /usr/local/etc/freeside/htpasswd \
 
 [ -e /usr/local/etc/freeside/dbdef.DBI:Pg:host=localhost\;dbname=$domain ] \
  || ( freeside-setup $username 2>/dev/null; \
-      /home/ivan/freeside/bin/populate-msgcat $username )
+      /home/ivan/freeside/bin/populate-msgcat $username; 2>/dev/null )
 
index c3ee05b..4241232 100644 (file)
@@ -1,10 +1,9 @@
 #!/usr/bin/perl -w
 #
-# $Id: freeside-adduser,v 1.8 2002-09-27 05:36:29 ivan Exp $
+# $Id: freeside-adduser,v 1.7 2002-08-25 01:16:30 ivan Exp $
 
 use strict;
 use vars qw($opt_h $opt_b $opt_c $opt_s);
-use Fcntl qw(:flock);
 use Getopt::Std;
 
 my $FREESIDE_CONF = "/usr/local/etc/freeside";
@@ -25,8 +24,7 @@ if ( $opt_h ) {
 my $secretfile = $opt_s || 'secrets';
 
 open(MAPSECRETS,">>$FREESIDE_CONF/mapsecrets")
-  and flock(MAPSECRETS,LOCK_EX)
-    or die "can't open $FREESIDE_CONF/mapsecrets: $!";
+  or die "can't open $FREESIDE_CONF/mapsecrets: $!";
 print MAPSECRETS "$user $secretfile\n";
 close MAPSECRETS or die "can't close $FREESIDE_CONF/mapsecrets: $!";
 
index 136851a..f4225d2 100755 (executable)
@@ -245,7 +245,7 @@ user: From the mapsecrets file - see config.html from the base documentation
 
 =head1 VERSION
 
-$Id: freeside-cc-receipts-report,v 1.5 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-cc-receipts-report,v 1.4.4.1 2002-09-09 22:57:32 ivan Exp $
 
 =head1 BUGS
 
index 410dabe..da01d3b 100755 (executable)
@@ -199,7 +199,7 @@ user: From the mapsecrets file - see config.html from the base documentation
 
 =head1 VERSION
 
-$Id: freeside-credit-report,v 1.5 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-credit-report,v 1.4.4.1 2002-09-09 22:57:32 ivan Exp $
 
 =head1 BUGS
 
index 400dc2a..c7ff411 100755 (executable)
@@ -12,9 +12,11 @@ my $user = shift or die &usage;
 adminsuidsetup $user;
 
 my $conf = new FS::Conf;
+my $domain = $conf->config('domain');
 
 my @svc_acct = qsearch('svc_acct', {});
-my @emails = map $_->email, @svc_acct;
+my @usernames = map $_->username, @svc_acct;
+my @emails = map "$_\@$domain", @usernames;
 
 print join("\n", @emails), "\n";
 
@@ -49,7 +51,7 @@ user: From the mapsecrets file - see config.html from the base documentation
 
 =head1 VERSION
 
-$Id: freeside-email,v 1.2 2002-09-18 22:50:44 ivan Exp $
+$Id: freeside-email,v 1.1 2001-05-15 07:52:34 ivan Exp $
 
 =head1 BUGS
 
index 5399f6d..2c89bef 100755 (executable)
@@ -202,7 +202,7 @@ user: From the mapsecrets file - see config.html from the base documentation
 
 =head1 VERSION
 
-$Id: freeside-expiration-alerter,v 1.4 2002-09-16 09:27:14 ivan Exp $
+$Id: freeside-expiration-alerter,v 1.3.4.1 2002-09-16 09:27:12 ivan Exp $
 
 =head1 BUGS
 
index f3ad2a1..033e83c 100755 (executable)
@@ -192,7 +192,7 @@ user: From the mapsecrets file - see config.html from the base documentation
 
 =head1 VERSION
 
-$Id: freeside-receivables-report,v 1.6 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-receivables-report,v 1.5.4.1 2002-09-09 22:57:32 ivan Exp $
 
 =head1 BUGS
 
index 8b74662..74164a0 100755 (executable)
@@ -7,7 +7,7 @@ use strict;
 use vars qw($opt_s);
 use Getopt::Std;
 use DBI;
-use DBIx::DBSchema 0.21;
+use DBIx::DBSchema 0.20;
 use DBIx::DBSchema::Table;
 use DBIx::DBSchema::Column;
 use DBIx::DBSchema::ColGroup::Unique;
@@ -111,9 +111,8 @@ my($dbdef) = new DBIx::DBSchema ( map {
 my $cust_main = $dbdef->table('cust_main');
 unless ($ship) { #remove ship_ from cust_main
   $cust_main->delcolumn($_) foreach ( grep /^ship_/, $cust_main->columns );
-} else { #add indices
-  push @{$cust_main->index->lol_ref},
-    map { [ "ship_$_" ] } qw( last company daytime night fax );
+} else { #add indices on ship_last and ship_company
+  push @{$cust_main->index->lol_ref}, ( ['ship_last'], ['ship_company'] ) 
 }
 
 #add radius attributes to svc_acct
@@ -139,6 +138,33 @@ foreach $attribute (@check_attributes) {
   ));
 }
 
+##make part_svc table (but now as object)
+#
+#my($part_svc)=$dbdef->table('part_svc');
+#
+##because of svc_acct_pop
+##foreach (grep /^svc_/, $dbdef->tables) { 
+##foreach (qw(svc_acct svc_acct_sm svc_charge svc_domain svc_wo)) {
+#foreach (qw(svc_acct svc_domain svc_forward svc_www)) {
+#  my($table)=$dbdef->table($_);
+#  my($col);
+#  foreach $col ( $table->columns ) {
+#    next if $col =~ /^svcnum$/;
+#    $part_svc->addcolumn( new DBIx::DBSchema::Column (
+#      $table->name. '__' . $table->column($col)->name,
+#      'varchar', #$table->column($col)->type, 
+#      'NULL',
+#      $char_d, #$table->column($col)->length,
+#    ));
+#    $part_svc->addcolumn ( new DBIx::DBSchema::Column (
+#      $table->name. '__'. $table->column($col)->name . "_flag",
+#      'char',
+#      'NULL',
+#      1,
+#    ));
+#  }
+#}
+
 #create history tables (false laziness w/create-history-tables)
 foreach my $table ( grep { ! /^h_/ } $dbdef->tables ) {
   my $tableobj = $dbdef->table($table)
@@ -190,23 +216,7 @@ foreach my $table ( grep { ! /^h_/ } $dbdef->tables ) {
                        'default' => '',
                        'local'   => '',
                      } ),
-                     map {
-                       my $column = $tableobj->column($_);
-
-                       #clone so as to not disturb the original
-                       $column = DBIx::DBSchema::Column->new( {
-                         map { $_ => $column->$_() }
-                           qw( name type null length default local )
-                       } );
-
-                       $column->type('int')
-                         if $column->type eq 'serial';
-                       #$column->default('')
-                       #  if $column->default =~ /^nextval\(/i;
-                       #( my $local = $column->local ) =~ s/AUTO_INCREMENT//i;
-                       #$column->local($local);
-                       $column;
-                     } $tableobj->columns
+                     map { $tableobj->column($_) } $tableobj->columns
                    ],
   } );
   $dbdef->addtable($h_tableobj);
@@ -329,7 +339,7 @@ sub tables_hash_hack {
 
     'agent' => {
       'columns' => [
-        'agentnum', 'serial',            '',     '',
+        'agentnum', 'int',            '',     '',
         'agent',    'varchar',           '',     $char_d,
         'typenum',  'int',            '',     '',
         'freq',     'int',       'NULL', '',
@@ -342,7 +352,7 @@ sub tables_hash_hack {
 
     'agent_type' => {
       'columns' => [
-        'typenum',   'serial',  '', '',
+        'typenum',   'int',  '', '',
         'atype',     'varchar', '', $char_d,
       ],
       'primary_key' => 'typenum',
@@ -362,7 +372,7 @@ sub tables_hash_hack {
 
     'cust_bill' => {
       'columns' => [
-        'invnum',    'serial',  '', '',
+        'invnum',    'int',  '', '',
         'custnum',   'int',  '', '',
         '_date',     @date_type,
         'charged',   @money_type,
@@ -376,7 +386,7 @@ sub tables_hash_hack {
 
     'cust_bill_event' => {
       'columns' => [
-        'eventnum',    'serial',  '', '',
+        'eventnum',    'int',  '', '',
         'invnum',   'int',  '', '',
         'eventpart',   'int',  '', '',
         '_date',     @date_type,
@@ -391,7 +401,7 @@ sub tables_hash_hack {
 
     'part_bill_event' => {
       'columns' => [
-        'eventpart',    'serial',  '', '',
+        'eventpart',    'int',  '', '',
         'payby',       'char',  '', 4,
         'event',       'varchar',           '',     $char_d,
         'eventcode',    @perl_type,
@@ -414,20 +424,19 @@ sub tables_hash_hack {
         'recur',   @money_type,
         'sdate',   @date_type,
         'edate',   @date_type,
-        'itemdesc', 'varchar', 'NULL', $char_d,
       ],
       'primary_key' => '',
-      'unique' => [],
+      'unique' => [ ['pkgnum', 'invnum'] ],
       'index' => [ ['invnum'] ],
     },
 
     'cust_credit' => {
       'columns' => [
-        'crednum',  'serial', '', '',
+        'crednum',  'int', '', '',
         'custnum',  'int', '', '',
         '_date',    @date_type,
         'amount',   @money_type,
-        'otaker',   'varchar', '', 32,
+        'otaker',   'varchar', '', 8,
         'reason',   'text', 'NULL', '',
         'closed',    'char', 'NULL', 1,
       ],
@@ -438,7 +447,7 @@ sub tables_hash_hack {
 
     'cust_credit_bill' => {
       'columns' => [
-        'creditbillnum', 'serial', '', '',
+        'creditbillnum', 'int', '', '',
         'crednum',  'int', '', '',
         'invnum',  'int', '', '',
         '_date',    @date_type,
@@ -451,7 +460,7 @@ sub tables_hash_hack {
 
     'cust_main' => {
       'columns' => [
-        'custnum',  'serial',  '',     '',
+        'custnum',  'int',  '',     '',
         'agentnum', 'int',  '',     '',
 #        'titlenum', 'int',  'NULL',   '',
         'last',     'varchar', '',     $char_d,
@@ -489,7 +498,7 @@ sub tables_hash_hack {
         'paydate',  'varchar', 'NULL', 10,
         'payname',  'varchar', 'NULL', $char_d,
         'tax',      'char', 'NULL', 1,
-        'otaker',   'varchar', '',    32,
+        'otaker',   'varchar', '',     8,
         'refnum',   'int',  '',     '',
         'referral_custnum', 'int',  'NULL', '',
         'comments', 'text', 'NULL', '',
@@ -497,14 +506,12 @@ sub tables_hash_hack {
       'primary_key' => 'custnum',
       'unique' => [],
       #'index' => [ ['last'], ['company'] ],
-      'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ],
-                   [ 'daytime' ], [ 'night' ], [ 'fax' ],
-                 ],
+      'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ] ],
     },
 
     'cust_main_invoice' => {
       'columns' => [
-        'destnum',  'serial',  '',     '',
+        'destnum',  'int',  '',     '',
         'custnum',  'int',  '',     '',
         'dest',     'varchar', '',  $char_d,
       ],
@@ -517,14 +524,13 @@ sub tables_hash_hack {
                             #cust_main_county for validation and to provide
                             # a tax rate.
       'columns' => [
-        'taxnum',   'serial',   '',    '',
+        'taxnum',   'int',   '',    '',
         'state',    'varchar',  'NULL',    $char_d,
         'county',   'varchar',  'NULL',    $char_d,
         'country',  'char',  '', 2, 
         'taxclass',   'varchar', 'NULL', $char_d,
         'exempt_amount', @money_type,
         'tax',      'real',  '',    '', #tax %
-        'taxname',  'varchar',  'NULL',    $char_d,
       ],
       'primary_key' => 'taxnum',
       'unique' => [],
@@ -534,14 +540,14 @@ sub tables_hash_hack {
 
     'cust_pay' => {
       'columns' => [
-        'paynum',   'serial',    '',   '',
+        'paynum',   'int',    '',   '',
         #now cust_bill_pay #'invnum',   'int',    '',   '',
         'custnum',  'int',    '',   '',
         'paid',     @money_type,
         '_date',    @date_type,
         'payby',    'char',   '',     4, # CARD/BILL/COMP, should be index into
                                          # payment type table.
-        'payinfo',  'varchar',   'NULL', $char_d,  #see cust_main above
+        'payinfo',  'varchar',   'NULL', 16,  #see cust_main above
         'paybatch', 'varchar',   'NULL', $char_d, #for auditing purposes.
         'closed',    'char', 'NULL', 1,
       ],
@@ -552,7 +558,7 @@ sub tables_hash_hack {
 
     'cust_bill_pay' => {
       'columns' => [
-        'billpaynum', 'serial',     '',   '',
+        'billpaynum', 'int',     '',   '',
         'invnum',  'int',     '',   '',
         'paynum',  'int',     '',   '',
         'amount',  @money_type,
@@ -566,7 +572,7 @@ sub tables_hash_hack {
     'cust_pay_batch' => { #what's this used for again?  list of customers
                           #in current CARD batch? (necessarily CARD?)
       'columns' => [
-        'paybatchnum',   'serial',    '',   '',
+        'paybatchnum',   'int',    '',   '',
         'invnum',   'int',    '',   '',
         'custnum',   'int',    '',   '',
         'last',     'varchar', '',     $char_d,
@@ -591,10 +597,10 @@ sub tables_hash_hack {
 
     'cust_pkg' => {
       'columns' => [
-        'pkgnum',    'serial',    '',   '',
+        'pkgnum',    'int',    '',   '',
         'custnum',   'int',    '',   '',
         'pkgpart',   'int',    '',   '',
-        'otaker',    'varchar', '', 32,
+        'otaker',    'varchar', '', 8,
         'setup',     @date_type,
         'bill',      @date_type,
         'susp',      @date_type,
@@ -609,16 +615,16 @@ sub tables_hash_hack {
 
     'cust_refund' => {
       'columns' => [
-        'refundnum',    'serial',    '',   '',
+        'refundnum',    'int',    '',   '',
         #now cust_credit_refund #'crednum',      'int',    '',   '',
         'custnum',  'int',    '',   '',
         '_date',        @date_type,
         'refund',       @money_type,
-        'otaker',       'varchar',   '',   32,
+        'otaker',       'varchar',   '',   8,
         'reason',       'varchar',   '',   $char_d,
         'payby',        'char',   '',     4, # CARD/BILL/COMP, should be index
                                              # into payment type table.
-        'payinfo',      'varchar',   'NULL', $char_d,  #see cust_main above
+        'payinfo',      'varchar',   'NULL', 16,  #see cust_main above
         'paybatch',     'varchar',   'NULL', $char_d,
         'closed',    'char', 'NULL', 1,
       ],
@@ -629,7 +635,7 @@ sub tables_hash_hack {
 
     'cust_credit_refund' => {
       'columns' => [
-        'creditrefundnum', 'serial',     '',   '',
+        'creditrefundnum', 'int',     '',   '',
         'crednum',  'int',     '',   '',
         'refundnum',  'int',     '',   '',
         'amount',  @money_type,
@@ -643,7 +649,7 @@ sub tables_hash_hack {
 
     'cust_svc' => {
       'columns' => [
-        'svcnum',    'serial',    '',   '',
+        'svcnum',    'int',    '',   '',
         'pkgnum',    'int',    'NULL',   '',
         'svcpart',   'int',    '',   '',
       ],
@@ -654,7 +660,7 @@ sub tables_hash_hack {
 
     'part_pkg' => {
       'columns' => [
-        'pkgpart',    'serial',    '',   '',
+        'pkgpart',    'int',    '',   '',
         'pkg',        'varchar',   '',   $char_d,
         'comment',    'varchar',   '',   $char_d,
         'setup',      @perl_type,
@@ -695,7 +701,7 @@ sub tables_hash_hack {
 
     'part_referral' => {
       'columns' => [
-        'refnum',   'serial',    '',   '',
+        'refnum',   'int',    '',   '',
         'referral', 'varchar',   '',   $char_d,
       ],
       'primary_key' => 'refnum',
@@ -705,7 +711,7 @@ sub tables_hash_hack {
 
     'part_svc' => {
       'columns' => [
-        'svcpart',    'serial',    '',   '',
+        'svcpart',    'int',    '',   '',
         'svc',        'varchar',   '',   $char_d,
         'svcdb',      'varchar',   '',   $char_d,
         'disabled',   'char',  'NULL',   1,
@@ -717,7 +723,7 @@ sub tables_hash_hack {
 
     'part_svc_column' => {
       'columns' => [
-        'columnnum',   'serial',         '', '',
+        'columnnum',   'int',         '', '',
         'svcpart',     'int',         '', '',
         'columnname',  'varchar',     '', 64,
         'columnvalue', 'varchar', 'NULL', $char_d,
@@ -731,7 +737,7 @@ sub tables_hash_hack {
     #(this should be renamed to part_pop)
     'svc_acct_pop' => {
       'columns' => [
-        'popnum',    'serial',    '',   '',
+        'popnum',    'int',    '',   '',
         'city',      'varchar',   '',   $char_d,
         'state',     'varchar',   '',   $char_d,
         'ac',        'char',   '',   3,
@@ -745,7 +751,7 @@ sub tables_hash_hack {
 
     'part_pop_local' => {
       'columns' => [
-        'localnum',  'serial',     '',     '',
+        'localnum',  'int',     '',     '',
         'popnum',    'int',     '',     '',
         'city',      'varchar', 'NULL', $char_d,
         'state',     'char',    'NULL', 2,
@@ -780,6 +786,18 @@ sub tables_hash_hack {
       'index' => [ ['username'], ['domsvc'] ],
     },
 
+#    'svc_acct_sm' => {
+#      'columns' => [
+#        'svcnum',    'int',    '',   '',
+#        'domsvc',    'int',    '',   '',
+#        'domuid',    'int', '',   '',
+#        'domuser',   'varchar',   '',   $char_d,
+#      ],
+#      'primary_key' => 'svcnum',
+#      'unique' => [ [] ],
+#      'index' => [ ['domsvc'], ['domuid'] ], 
+#    },
+
     #'svc_charge' => {
     #  'columns' => [
     #    'svcnum',    'int',    '',   '',
@@ -803,14 +821,12 @@ sub tables_hash_hack {
 
     'domain_record' => {
       'columns' => [
-        'recnum',    'serial',     '',  '',
+        'recnum',    'int',     '',  '',
         'svcnum',    'int',     '',  '',
-        #'reczone',   'varchar', '',  $char_d,
-        'reczone',   'varchar', '',  255,
+        'reczone',   'varchar', '',  $char_d,
         'recaf',     'char',    '',  2,
         'rectype',   'char',    '',  5,
-        #'recdata',   'varchar', '',  $char_d,
-        'recdata',   'varchar', '',  255,
+        'recdata',   'varchar', '',  $char_d,
       ],
       'primary_key' => 'recnum',
       'unique'      => [],
@@ -855,7 +871,7 @@ sub tables_hash_hack {
 
     'prepay_credit' => {
       'columns' => [
-        'prepaynum',   'serial',     '',   '',
+        'prepaynum',   'int',     '',   '',
         'identifier',  'varchar', '', $char_d,
         'amount',      @money_type,
         'seconds',     'int',     'NULL', '',
@@ -867,7 +883,7 @@ sub tables_hash_hack {
 
     'port' => {
       'columns' => [
-        'portnum',  'serial',     '',   '',
+        'portnum',  'int',     '',   '',
         'ip',       'varchar', 'NULL', 15,
         'nasport',  'int',     'NULL', '',
         'nasnum',   'int',     '',   '',
@@ -879,7 +895,7 @@ sub tables_hash_hack {
 
     'nas' => {
       'columns' => [
-        'nasnum',   'serial',     '',    '',
+        'nasnum',   'int',     '',    '',
         'nas',      'varchar', '',    $char_d,
         'nasip',    'varchar', '',    15,
         'nasfqdn',  'varchar', '',    $char_d,
@@ -892,7 +908,7 @@ sub tables_hash_hack {
 
     'session' => {
       'columns' => [
-        'sessionnum', 'serial',       '',   '',
+        'sessionnum', 'int',       '',   '',
         'portnum',    'int',       '',   '',
         'svcnum',     'int',       '',   '',
         'login',      @date_type,
@@ -905,7 +921,7 @@ sub tables_hash_hack {
 
     'queue' => {
       'columns' => [
-        'jobnum', 'serial', '', '',
+        'jobnum', 'int', '', '',
         'job', 'text', '', '',
         '_date', 'int', '', '',
         'status', 'varchar', '', $char_d,
@@ -919,7 +935,7 @@ sub tables_hash_hack {
 
     'queue_arg' => {
       'columns' => [
-        'argnum', 'serial', '', '',
+        'argnum', 'int', '', '',
         'jobnum', 'int', '', '',
         'arg', 'text', 'NULL', '',
       ],
@@ -930,7 +946,7 @@ sub tables_hash_hack {
 
     'queue_depend' => {
       'columns' => [
-        'dependnum', 'serial', '', '',
+        'dependnum', 'int', '', '',
         'jobnum', 'int', '', '',
         'depend_jobnum', 'int', '', '',
       ],
@@ -941,7 +957,7 @@ sub tables_hash_hack {
 
     'export_svc' => {
       'columns' => [
-        'exportsvcnum' => 'serial', '', '',
+        'exportsvcnum' => 'int', '', '',
         'exportnum'    => 'int', '', '',
         'svcpart'      => 'int', '', '',
       ],
@@ -952,7 +968,7 @@ sub tables_hash_hack {
 
     'part_export' => {
       'columns' => [
-        'exportnum', 'serial', '', '',
+        'exportnum', 'int', '', '',
         #'svcpart',   'int', '', '',
         'machine', 'varchar', '', $char_d,
         'exporttype', 'varchar', '', $char_d,
@@ -965,7 +981,7 @@ sub tables_hash_hack {
 
     'part_export_option' => {
       'columns' => [
-        'optionnum', 'serial', '', '',
+        'optionnum', 'int', '', '',
         'exportnum', 'int', '', '',
         'optionname', 'varchar', '', $char_d,
         'optionvalue', 'text', 'NULL', '',
@@ -977,7 +993,7 @@ sub tables_hash_hack {
 
     'radius_usergroup' => {
       'columns' => [
-        'usergroupnum', 'serial', '', '',
+        'usergroupnum', 'int', '', '',
         'svcnum',       'int', '', '',
         'groupname',    'varchar', '', $char_d,
       ],
@@ -988,7 +1004,7 @@ sub tables_hash_hack {
 
     'msgcat' => {
       'columns' => [
-        'msgnum', 'serial', '', '',
+        'msgnum', 'int', '', '',
         'msgcode', 'varchar', '', $char_d,
         'locale', 'varchar', '', 16,
         'msg', 'text', '', '',
@@ -1000,7 +1016,7 @@ sub tables_hash_hack {
 
     'cust_tax_exempt' => {
       'columns' => [
-        'exemptnum', 'serial', '', '',
+        'exemptnum', 'int', '', '',
         'custnum',   'int', '', '',
         'taxnum',    'int', '', '',
         'year',      'int', '', '',
@@ -1012,77 +1028,7 @@ sub tables_hash_hack {
       'index'       => [],
     },
 
-    'ac_type' => {
-      'columns' => [
-        'actypenum', 'serial', '', '',
-        'actypename', 'varchar', '', $char_d,
-      ],
-      'primary_key' => 'actypenum',
-      'unique'      => [],
-      'index'       => [],
-    },
 
-    'ac' => {
-      'columns' => [
-        'acnum', 'serial', '', '',
-        'actypenum', 'int', '', '',
-        'acname', 'varchar', '', $char_d,
-      ],
-      'primary_key' => 'acnum',
-      'unique'      => [],
-      'index'       => [ [ 'actypenum' ] ],
-    },
-
-    'part_ac_field' => {
-      'columns' => [
-        'acfieldpart', 'serial', '', '',
-        'actypenum', 'int', '', '',
-        'name', 'varchar', '', $char_d,
-        'ut_type', 'varchar', '', $char_d,
-      ],
-      'primary_key' => 'acfieldpart',
-      'unique'      => [],
-      'index'       => [ [ 'actypenum' ] ],
-    },
-
-    'ac_field' => {
-      'columns' => [
-        'acfieldpart', 'int', '', '',
-        'acnum', 'int', '', '',
-        'value', 'text', '', '',
-      ],
-      'primary_key' => '',
-      'unique'      => [ [ 'acfieldpart', 'acnum' ] ],
-      'index'       => [ [ 'acnum' ] ],
-    },
-
-    'ac_block' => {
-      'columns' => [
-        'acnum', 'int', '', '',
-        'ip_gateway', 'varchar', '', 15,
-        'ip_netmask', 'int', '', '',
-      ],
-      'primary_key' => '',
-      'unique'      => [],
-      'index'       => [ [ 'acnum' ] ],
-    },
-
-    'svc_broadband' => {
-      'columns' => [
-        'svcnum', 'int', '', '',
-        'actypenum', 'int', '', '',
-        'speed_up', 'int', '', '',
-        'speed_down', 'int', '', '',
-        'acnum', 'int', '', '',
-        'ip_addr', 'varchar', '', 15,
-        'ip_netmask', 'int', '', '',
-        'mac_addr', 'char', '', 17,
-        'location', 'varchar', '', $char_d,
-      ],
-      'primary_key' => 'svcnum',
-      'unique'      => [],
-      'index'       => [ [ 'actypenum' ] ],
-    },
 
   );
 
index 240f3ad..d48da87 100755 (executable)
@@ -267,7 +267,7 @@ user: From the mapsecrets file - see config.html from the base documentation
 
 =head1 VERSION
 
-$Id: freeside-tax-report,v 1.5 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-tax-report,v 1.4.4.1 2002-09-09 22:57:32 ivan Exp $
 
 =head1 BUGS
 
diff --git a/FS/t/svc_acct_sm.t b/FS/t/svc_acct_sm.t
new file mode 100644 (file)
index 0000000..1082f2c
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_acct_sm;
+$loaded=1;
+print "ok 1\n";
index da56c73..47b85ab 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -11,31 +11,18 @@ TEMPLATE = asp
 
 ASP_GLOBAL = /usr/local/etc/freeside/asp-global
 
-#deb, others?
 FREESIDE_DOCUMENT_ROOT = /var/www/freeside
-#freebsd
-#FREESIDE_DOCUMENT_ROOT = /usr/local/www/data/freeside
 
-#deb, others?
 INIT_FILE = /etc/init.d/freeside
-#freebsd
-#INIT_FILE = /usr/local/etc/rc.d/011.freeside.sh
 
-#deb, others?
 HTTPD_RESTART = /etc/init.d/apache restart
-#freebsd
-#HTTPD_RESTART = /usr/local/etc/rc.d/apache.sh stop; sleep 1; /usr/local/etc/rc.d/apache.sh start
+FREESIDE_RESTART = /etc/init.d/freeside restart
 
-FREESIDE_RESTART = ${INIT_FILE} restart
-
-#deb, others?
 INSTALLGROUP = root
-#freebsd
-#INSTALLGROUP = wheel
 
 #edit the stuff below to have the daemons start
 
-QUEUED_USER=fs_queue
+QUEUED_USER=ivan
 
 #eventually this shouldn't be needed
 FREESIDE_PATH = `pwd`
@@ -56,8 +43,8 @@ SELFSERVICE_MACHINE = localhost
 #not changable yet
 FREESIDE_CONF = /usr/local/etc/freeside
 
-VERSION=1.5.0pre1
-TAG=freeside_1_5_0pre1
+VERSION=1.4.1beta6
+TAG=freeside_1_4_1beta6
 
 help:
        @echo "supported targets: aspdocs masondocs alldocs docs install-docs"
@@ -183,11 +170,11 @@ clean:
 #these are probably only useful if you're me...
 
 upload-docs: forcehtmlman
-       ssh cleanwhisker.420.am rm -rf /var/www/www.sisd.com/freeside/devdocs
-       scp -pr httemplate/docs cleanwhisker.420.am:/var/www/www.sisd.com/freeside/devdocs
+       ssh cleanwhisker.420.am rm -rf /var/www/www.sisd.com/freeside/docs
+       scp -pr httemplate/docs cleanwhisker.420.am:/var/www/www.sisd.com/freeside/docs
 
-release: upload-docs
-       cd /home/ivan/freeside
+release: upload-docs update-webdemo
+       cd /home/ivan/freeside1.4
        #cvs tag ${TAG}
        cvs tag -F ${TAG}
 
@@ -199,7 +186,7 @@ release: upload-docs
        mv freeside-${VERSION} freeside-${VERSION}.tar.gz ..
 
 update-webdemo:
-       ssh ivan@pouncequick.420.am '( cd freeside; cvs update -d -P )'
+       ssh ivan@pouncequick.420.am '( cd freeside; cvs update -r FREESIDE_1_4_BRANCH -d -P )'
        #ssh root@pouncequick.420.am '( cd /home/ivan/freeside; make clean; make deploy )'
        ssh root@pouncequick.420.am '( cd /home/ivan/freeside; make deploy )'
 
diff --git a/README b/README
index 44692e6..2951cee 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
 Freeside
 
-Copyright (C) 2000,2001,2002 Ivan Kohler
+Copyright (C) 2000,2001 Ivan Kohler
 Copyright (C) 1999 Silicon Interactive Software Design
 All rights reserved
 
diff --git a/README.1.5.0pre1 b/README.1.5.0pre1
deleted file mode 100644 (file)
index 0de86bc..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-preliminary upgrade instructions
-
-schema changes:
-  *** get svc_broadband changes from pc-intouch
-  *** otaker changes s/8/32 all otkaer fields
-  *** optional: sequence changes
-  *** add column cust_main_county.taxname
-  *** add column cust_bill_pkg.itemdesc
-  *** drop index cust_bill_pkg1
-  *** add index part_pkg1 and part_svc1
-
-install DBIx::DBSchema 0.21
-install NetAddr::IP
-
-Run dbdef-create
-something about history tables
-Restart apache and freeside-queued
index 33eb0e5..d37d682 100755 (executable)
@@ -2,7 +2,7 @@
 
 use strict;
 use DBI;
-use DBIx::DBSchema 0.21;
+use DBIx::DBSchema 0.20;
 use DBIx::DBSchema::Table;
 use DBIx::DBSchema::Column;
 use DBIx::DBSchema::ColGroup::Unique;
@@ -64,16 +64,7 @@ foreach my $table ( @tables ) {
                        'default' => '',
                        'local'   => '',
                      } ),
-                     map {
-                       my $column = $tableobj->column($_);
-                       $column->type('int')
-                         if $column->type eq 'serial';
-                       $column->default('')
-                         if $column->default =~ /^nextval\(/i;
-                       ( my $local = $column->local ) =~ s/AUTO_INCREMENT//i;
-                       $column->local($local);
-                       $column;
-                     } $tableobj->columns
+                     map { $tableobj->column($_) } $tableobj->columns
                    ],
   } );
   foreach my $statement ( $h_tableobj->sql_create_table($dbh) ) {
index c977f87..0b297b9 100755 (executable)
@@ -1,10 +1,10 @@
 #!/usr/bin/perl -Tw
 #
-# $Id: dbdef-create,v 1.6 2002-09-19 13:34:52 ivan Exp $
+# $Id: dbdef-create,v 1.5 2001-08-21 02:43:18 ivan Exp $
 
 use strict;
 use DBI;
-use DBIx::DBSchema 0.21;
+use DBIx::DBSchema;
 use FS::UID qw(adminsuidsetup datasrc driver_name);
 
 my $user = shift or die &usage;
diff --git a/bin/fs-setup b/bin/fs-setup
new file mode 100755 (executable)
index 0000000..c60181b
--- /dev/null
@@ -0,0 +1,1038 @@
+#!/usr/bin/perl -Tw
+#
+# $Id: fs-setup,v 1.96.4.6 2002-12-24 22:41:21 ivan Exp $
+
+#to delay loading dbdef until we're ready
+BEGIN { $FS::Record::setup_hack = 1; }
+
+use strict;
+use DBI;
+use DBIx::DBSchema 0.20;
+use DBIx::DBSchema::Table;
+use DBIx::DBSchema::Column;
+use DBIx::DBSchema::ColGroup::Unique;
+use DBIx::DBSchema::ColGroup::Index;
+use FS::UID qw(adminsuidsetup datasrc checkeuid getsecrets);
+use FS::Record;
+use FS::cust_main_county;
+use FS::raddb;
+use FS::part_bill_event;
+
+die "Not running uid freeside!" unless checkeuid();
+
+my %attrib2db =
+  map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
+
+my $user = shift or die &usage;
+getsecrets($user);
+
+#needs to match FS::Record
+my($dbdef_file) = "/usr/local/etc/freeside/dbdef.". datasrc;
+
+###
+
+#print "\nEnter the maximum username length: ";
+#my($username_len)=&getvalue;
+my $username_len = 32; #usernamemax config file
+
+print "\n\n", <<END, ":";
+Freeside tracks the RADIUS User-Name, check attribute Password and
+reply attribute Framed-IP-Address for each user.  You can specify additional
+check and reply attributes (or you can add them later with the
+fs-radius-add-check and fs-radius-add-reply programs).
+
+First enter any additional RADIUS check attributes you need to track for each 
+user, separated by whitespace.
+END
+my @check_attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
+                         split(" ",&getvalue);
+
+print "\n\n", <<END, ":";
+Now enter any additional reply attributes you need to track for each user,
+separated by whitespace.
+END
+my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
+                   split(" ",&getvalue);
+
+print "\n\n", <<END, ":";
+Do you wish to enable the tracking of a second, separate shipping/service
+address?
+END
+my $ship = &_yesno;
+
+sub getvalue {
+  my($x)=scalar(<STDIN>);
+  chop $x;
+  $x;
+}
+
+sub _yesno {
+  print " [y/N]:";
+  my $x = scalar(<STDIN>);
+  $x =~ /^y/i;
+}
+
+###
+
+my($char_d) = 80; #default maxlength for text fields
+
+#my(@date_type)  = ( 'timestamp', '', ''     );
+my(@date_type)  = ( 'int', 'NULL', ''     );
+my(@perl_type) = ( 'text', 'NULL', ''  ); 
+my @money_type = ( 'decimal',   '', '10,2' );
+
+###
+# create a dbdef object from the old data structure
+###
+
+my(%tables)=&tables_hash_hack;
+
+#turn it into objects
+my($dbdef) = new DBIx::DBSchema ( map {  
+  my(@columns);
+  while (@{$tables{$_}{'columns'}}) {
+    my($name,$type,$null,$length)=splice @{$tables{$_}{'columns'}}, 0, 4;
+    push @columns, new DBIx::DBSchema::Column ( $name,$type,$null,$length );
+  }
+  DBIx::DBSchema::Table->new(
+    $_,
+    $tables{$_}{'primary_key'},
+    DBIx::DBSchema::ColGroup::Unique->new($tables{$_}{'unique'}),
+    DBIx::DBSchema::ColGroup::Index->new($tables{$_}{'index'}),
+    @columns,
+  );
+} (keys %tables) );
+
+my $cust_main = $dbdef->table('cust_main');
+unless ($ship) { #remove ship_ from cust_main
+  $cust_main->delcolumn($_) foreach ( grep /^ship_/, $cust_main->columns );
+} else { #add indices
+  push @{$cust_main->index->lol_ref},
+    map { [ "ship_$_" ] } qw( last company daytime night fax );
+}
+
+#add radius attributes to svc_acct
+
+my($svc_acct)=$dbdef->table('svc_acct');
+
+my($attribute);
+foreach $attribute (@attributes) {
+  $svc_acct->addcolumn ( new DBIx::DBSchema::Column (
+    'radius_'. $attribute,
+    'varchar',
+    'NULL',
+    $char_d,
+  ));
+}
+
+foreach $attribute (@check_attributes) {
+  $svc_acct->addcolumn( new DBIx::DBSchema::Column (
+    'rc_'. $attribute,
+    'varchar',
+    'NULL',
+    $char_d,
+  ));
+}
+
+##make part_svc table (but now as object)
+#
+#my($part_svc)=$dbdef->table('part_svc');
+#
+##because of svc_acct_pop
+##foreach (grep /^svc_/, $dbdef->tables) { 
+##foreach (qw(svc_acct svc_acct_sm svc_charge svc_domain svc_wo)) {
+#foreach (qw(svc_acct svc_domain svc_forward svc_www)) {
+#  my($table)=$dbdef->table($_);
+#  my($col);
+#  foreach $col ( $table->columns ) {
+#    next if $col =~ /^svcnum$/;
+#    $part_svc->addcolumn( new DBIx::DBSchema::Column (
+#      $table->name. '__' . $table->column($col)->name,
+#      'varchar', #$table->column($col)->type, 
+#      'NULL',
+#      $char_d, #$table->column($col)->length,
+#    ));
+#    $part_svc->addcolumn ( new DBIx::DBSchema::Column (
+#      $table->name. '__'. $table->column($col)->name . "_flag",
+#      'char',
+#      'NULL',
+#      1,
+#    ));
+#  }
+#}
+
+#create history tables (false laziness w/create-history-tables)
+foreach my $table ( grep { ! /^h_/ } $dbdef->tables ) {
+  my $tableobj = $dbdef->table($table)
+    or die "unknown table $table";
+
+  die "unique->lol_ref undefined for $table"
+    unless defined $tableobj->unique->lol_ref;
+  die "index->lol_ref undefined for $table"
+    unless defined $tableobj->index->lol_ref;
+
+  my $h_tableobj = DBIx::DBSchema::Table->new( {
+    name        => "h_$table",
+    primary_key => 'historynum',
+    unique      => DBIx::DBSchema::ColGroup::Unique->new( [] ),
+    'index'     => DBIx::DBSchema::ColGroup::Index->new( [
+                     @{$tableobj->unique->lol_ref},
+                     @{$tableobj->index->lol_ref}
+                   ] ),
+    columns     => [
+                     DBIx::DBSchema::Column->new( {
+                       'name'    => 'historynum',
+                       'type'    => 'serial',
+                       'null'    => 'NOT NULL',
+                       'length'  => '',
+                       'default' => '',
+                       'local'   => '',
+                     } ),
+                     DBIx::DBSchema::Column->new( {
+                       'name'    => 'history_date',
+                       'type'    => 'int',
+                       'null'    => 'NULL',
+                       'length'  => '',
+                       'default' => '',
+                       'local'   => '',
+                     } ),
+                     DBIx::DBSchema::Column->new( {
+                       'name'    => 'history_user',
+                       'type'    => 'varchar',
+                       'null'    => 'NOT NULL',
+                       'length'  => '80',
+                       'default' => '',
+                       'local'   => '',
+                     } ),
+                     DBIx::DBSchema::Column->new( {
+                       'name'    => 'history_action',
+                       'type'    => 'varchar',
+                       'null'    => 'NOT NULL',
+                       'length'  => '80',
+                       'default' => '',
+                       'local'   => '',
+                     } ),
+                     map { $tableobj->column($_) } $tableobj->columns
+                   ],
+  } );
+  $dbdef->addtable($h_tableobj);
+}
+
+#important
+$dbdef->save($dbdef_file);
+&FS::Record::reload_dbdef($dbdef_file);
+
+###
+# create 'em
+###
+
+my($dbh)=adminsuidsetup $user;
+
+#create tables
+$|=1;
+
+foreach my $statement ( $dbdef->sql($dbh) ) {
+  $dbh->do( $statement )
+    or die "CREATE error: ". $dbh->errstr. "\ndoing statement: $statement";
+}
+
+#not really sample data (and shouldn't default to US)
+
+#cust_main_county
+
+#USPS state codes
+foreach ( qw(
+AL AK AS AZ AR CA CO CT DC DE FM FL GA GU HI ID IL IN IA KS KY LA
+ME MH MD MA MI MN MS MO MT NC ND NE NH NJ NM NV NY MP OH OK OR PA PW PR RI 
+SC SD TN TX UT VT VI VA WA WV WI WY AE AA AP
+) ) {
+  my($cust_main_county)=new FS::cust_main_county({
+    'state' => $_,
+    'tax'   => 0,
+    'country' => 'US',
+  });  
+  my($error);
+  $error=$cust_main_county->insert;
+  die $error if $error;
+}
+
+#AU "offical" state codes ala mark.williamson@ebbs.com.au (Mark Williamson)
+foreach ( qw(
+VIC NSW NT QLD TAS ACT WA SA
+) ) {
+  my($cust_main_county)=new FS::cust_main_county({
+    'state' => $_,
+    'tax'   => 0,
+    'country' => 'AU',
+  });  
+  my($error);
+  $error=$cust_main_county->insert;
+  die $error if $error;
+}
+
+#ISO 2-letter country codes (same as country TLDs) except US and AU
+foreach ( qw(
+AF AL DZ AS AD AO AI AQ AG AR AM AW AT AZ BS BH BD BB BY BE BZ BJ BM BT BO
+BA BW BV BR IO BN BG BF BI KH CM CA CV KY CF TD CL CN CX CC CO KM CG CK CR CI
+HR CU CY CZ DK DJ DM DO TP EC EG SV GQ ER EE ET FK FO FJ FI FR FX GF PF TF GA
+GM GE DE GH GI GR GL GD GP GU GT GN GW GY HT HM HN HK HU IS IN ID IR IQ IE IL
+IT JM JP JO KZ KE KI KP KR KW KG LA LV LB LS LR LY LI LT LU MO MK MG MW MY MV
+ML MT MH MQ MR MU YT MX FM MD MC MN MS MA MZ MM NA NR NP NL AN NC NZ NI NE NG
+NU NF MP NO OM PK PW PA PG PY PE PH PN PL PT PR QA RE RO RU RW KN LC VC WS SM
+ST SA SN SC SL SG SK SI SB SO ZA GS ES LK SH PM SD SR SJ SZ SE CH SY TW TJ TZ
+TH TG TK TO TT TN TR TM TC TV UG UA AE GB UM UY UZ VU VA VE VN VG VI WF EH
+YE YU ZR ZM ZW
+) ) {
+  my($cust_main_county)=new FS::cust_main_county({
+    'tax'   => 0,
+    'country' => $_,
+  });  
+  my($error);
+  $error=$cust_main_county->insert;
+  die $error if $error;
+}
+
+#billing events
+foreach my $aref ( 
+  [ 'COMP', 'Comp invoice', '$cust_bill->comp();', 30, 'comp' ],
+  [ 'CARD', 'Batch card', '$cust_bill->batch_card();', 40, 'batch-card' ],
+  [ 'BILL', 'Send invoice', '$cust_bill->send();', 50, 'send' ],
+) {
+
+  my $part_bill_event = new FS::part_bill_event({
+    'payby' => $aref->[0],
+    'event' => $aref->[1],
+    'eventcode' => $aref->[2],
+    'seconds' => 0,
+    'weight' => $aref->[3],
+    'plan' => $aref->[4],
+  });
+  my($error);
+  $error=$part_bill_event->insert;
+  die $error if $error;
+
+}
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+print "Freeside database initialized sucessfully\n";
+
+sub usage {
+  die "Usage:\n  fs-setup user\n"; 
+}
+
+###
+# Now it becomes an object.  much better.
+###
+sub tables_hash_hack {
+
+  #note that s/(date|change)/_$1/; to avoid keyword conflict.
+  #put a kludge in FS::Record to catch this or? (pry need some date-handling
+  #stuff anyway also)
+
+  my(%tables)=( #yech.}
+
+    'agent' => {
+      'columns' => [
+        'agentnum', 'int',            '',     '',
+        'agent',    'varchar',           '',     $char_d,
+        'typenum',  'int',            '',     '',
+        'freq',     'int',       'NULL', '',
+        'prog',     @perl_type,
+      ],
+      'primary_key' => 'agentnum',
+      'unique' => [],
+      'index' => [ ['typenum'] ],
+    },
+
+    'agent_type' => {
+      'columns' => [
+        'typenum',   'int',  '', '',
+        'atype',     'varchar', '', $char_d,
+      ],
+      'primary_key' => 'typenum',
+      'unique' => [],
+      'index' => [],
+    },
+
+    'type_pkgs' => {
+      'columns' => [
+        'typenum',   'int',  '', '',
+        'pkgpart',   'int',  '', '',
+      ],
+      'primary_key' => '',
+      'unique' => [ ['typenum', 'pkgpart'] ],
+      'index' => [ ['typenum'] ],
+    },
+
+    'cust_bill' => {
+      'columns' => [
+        'invnum',    'int',  '', '',
+        'custnum',   'int',  '', '',
+        '_date',     @date_type,
+        'charged',   @money_type,
+        'printed',   'int',  '', '',
+        'closed',    'char', 'NULL', 1,
+      ],
+      'primary_key' => 'invnum',
+      'unique' => [],
+      'index' => [ ['custnum'] ],
+    },
+
+    'cust_bill_event' => {
+      'columns' => [
+        'eventnum',    'int',  '', '',
+        'invnum',   'int',  '', '',
+        'eventpart',   'int',  '', '',
+        '_date',     @date_type,
+        'status', 'varchar', '', $char_d,
+        'statustext', 'text', 'NULL', '',
+      ],
+      'primary_key' => 'eventnum',
+      #no... there are retries now #'unique' => [ [ 'eventpart', 'invnum' ] ],
+      'unique' => [],
+      'index' => [ ['invnum'], ['status'] ],
+    },
+
+    'part_bill_event' => {
+      'columns' => [
+        'eventpart',    'int',  '', '',
+        'payby',       'char',  '', 4,
+        'event',       'varchar',           '',     $char_d,
+        'eventcode',    @perl_type,
+        'seconds',     'int', 'NULL', '',
+        'weight',      'int', '', '',
+        'plan',       'varchar', 'NULL', $char_d,
+        'plandata',   'text', 'NULL', '',
+        'disabled',     'char', 'NULL', 1,
+      ],
+      'primary_key' => 'eventpart',
+      'unique' => [],
+      'index' => [ ['payby'] ],
+    },
+
+    'cust_bill_pkg' => {
+      'columns' => [
+        'pkgnum',  'int', '', '',
+        'invnum',  'int', '', '',
+        'setup',   @money_type,
+        'recur',   @money_type,
+        'sdate',   @date_type,
+        'edate',   @date_type,
+      ],
+      'primary_key' => '',
+      'unique' => [ ['pkgnum', 'invnum'] ],
+      'index' => [ ['invnum'] ],
+    },
+
+    'cust_credit' => {
+      'columns' => [
+        'crednum',  'int', '', '',
+        'custnum',  'int', '', '',
+        '_date',    @date_type,
+        'amount',   @money_type,
+        'otaker',   'varchar', '', 8,
+        'reason',   'text', 'NULL', '',
+        'closed',    'char', 'NULL', 1,
+      ],
+      'primary_key' => 'crednum',
+      'unique' => [],
+      'index' => [ ['custnum'] ],
+    },
+
+    'cust_credit_bill' => {
+      'columns' => [
+        'creditbillnum', 'int', '', '',
+        'crednum',  'int', '', '',
+        'invnum',  'int', '', '',
+        '_date',    @date_type,
+        'amount',   @money_type,
+      ],
+      'primary_key' => 'creditbillnum',
+      'unique' => [],
+      'index' => [ ['crednum'], ['invnum'] ],
+    },
+
+    'cust_main' => {
+      'columns' => [
+        'custnum',  'int',  '',     '',
+        'agentnum', 'int',  '',     '',
+#        'titlenum', 'int',  'NULL',   '',
+        'last',     'varchar', '',     $char_d,
+#        'middle',   'varchar', 'NULL', $char_d,
+        'first',    'varchar', '',     $char_d,
+        'ss',       'char', 'NULL', 11,
+        'company',  'varchar', 'NULL', $char_d,
+        'address1', 'varchar', '',     $char_d,
+        'address2', 'varchar', 'NULL', $char_d,
+        'city',     'varchar', '',     $char_d,
+        'county',   'varchar', 'NULL', $char_d,
+        'state',    'varchar', 'NULL', $char_d,
+        'zip',      'varchar', '',     10,
+        'country',  'char', '',     2,
+        'daytime',  'varchar', 'NULL', 20,
+        'night',    'varchar', 'NULL', 20,
+        'fax',      'varchar', 'NULL', 12,
+        'ship_last',     'varchar', 'NULL', $char_d,
+#        'ship_middle',   'varchar', 'NULL', $char_d,
+        'ship_first',    'varchar', 'NULL', $char_d,
+        'ship_company',  'varchar', 'NULL', $char_d,
+        'ship_address1', 'varchar', 'NULL', $char_d,
+        'ship_address2', 'varchar', 'NULL', $char_d,
+        'ship_city',     'varchar', 'NULL', $char_d,
+        'ship_county',   'varchar', 'NULL', $char_d,
+        'ship_state',    'varchar', 'NULL', $char_d,
+        'ship_zip',      'varchar', 'NULL', 10,
+        'ship_country',  'char', 'NULL', 2,
+        'ship_daytime',  'varchar', 'NULL', 20,
+        'ship_night',    'varchar', 'NULL', 20,
+        'ship_fax',      'varchar', 'NULL', 12,
+        'payby',    'char', '',     4,
+        'payinfo',  'varchar', 'NULL', $char_d,
+        #'paydate',  @date_type,
+        'paydate',  'varchar', 'NULL', 10,
+        'payname',  'varchar', 'NULL', $char_d,
+        'tax',      'char', 'NULL', 1,
+        'otaker',   'varchar', '',     8,
+        'refnum',   'int',  '',     '',
+        'referral_custnum', 'int',  'NULL', '',
+        'comments', 'text', 'NULL', '',
+      ],
+      'primary_key' => 'custnum',
+      'unique' => [],
+      #'index' => [ ['last'], ['company'] ],
+      'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ],
+                   [ 'daytime' ], [ 'night' ], [ 'fax' ],
+                 ],
+    },
+
+    'cust_main_invoice' => {
+      'columns' => [
+        'destnum',  'int',  '',     '',
+        'custnum',  'int',  '',     '',
+        'dest',     'varchar', '',  $char_d,
+      ],
+      'primary_key' => 'destnum',
+      'unique' => [],
+      'index' => [ ['custnum'], ],
+    },
+
+    'cust_main_county' => { #county+state+country are checked off the
+                            #cust_main_county for validation and to provide
+                            # a tax rate.
+      'columns' => [
+        'taxnum',   'int',   '',    '',
+        'state',    'varchar',  'NULL',    $char_d,
+        'county',   'varchar',  'NULL',    $char_d,
+        'country',  'char',  '', 2, 
+        'taxclass',   'varchar', 'NULL', $char_d,
+        'exempt_amount', @money_type,
+        'tax',      'real',  '',    '', #tax %
+      ],
+      'primary_key' => 'taxnum',
+      'unique' => [],
+  #    'unique' => [ ['taxnum'], ['state', 'county'] ],
+      'index' => [],
+    },
+
+    'cust_pay' => {
+      'columns' => [
+        'paynum',   'int',    '',   '',
+        #now cust_bill_pay #'invnum',   'int',    '',   '',
+        'custnum',  'int',    '',   '',
+        'paid',     @money_type,
+        '_date',    @date_type,
+        'payby',    'char',   '',     4, # CARD/BILL/COMP, should be index into
+                                         # payment type table.
+        'payinfo',  'varchar',   'NULL', $char_d,  #see cust_main above
+        'paybatch', 'varchar',   'NULL', $char_d, #for auditing purposes.
+        'closed',    'char', 'NULL', 1,
+      ],
+      'primary_key' => 'paynum',
+      'unique' => [],
+      'index' => [ [ 'custnum' ], [ 'paybatch' ] ],
+    },
+
+    'cust_bill_pay' => {
+      'columns' => [
+        'billpaynum', 'int',     '',   '',
+        'invnum',  'int',     '',   '',
+        'paynum',  'int',     '',   '',
+        'amount',  @money_type,
+        '_date',   @date_type
+      ],
+      'primary_key' => 'billpaynum',
+      'unique' => [],
+      'index' => [ [ 'paynum' ], [ 'invnum' ] ],
+    },
+
+    'cust_pay_batch' => { #what's this used for again?  list of customers
+                          #in current CARD batch? (necessarily CARD?)
+      'columns' => [
+        'paybatchnum',   'int',    '',   '',
+        'invnum',   'int',    '',   '',
+        'custnum',   'int',    '',   '',
+        'last',     'varchar', '',     $char_d,
+        'first',    'varchar', '',     $char_d,
+        'address1', 'varchar', '',     $char_d,
+        'address2', 'varchar', 'NULL', $char_d,
+        'city',     'varchar', '',     $char_d,
+        'state',    'varchar', 'NULL', $char_d,
+        'zip',      'varchar', '',     10,
+        'country',  'char', '',     2,
+#        'trancode', 'int', '', '',
+        'cardnum',  'varchar', '',     16,
+        #'exp',      @date_type,
+        'exp',      'varchar', '',     11,
+        'payname',  'varchar', 'NULL', $char_d,
+        'amount',   @money_type,
+      ],
+      'primary_key' => 'paybatchnum',
+      'unique' => [],
+      'index' => [ ['invnum'], ['custnum'] ],
+    },
+
+    'cust_pkg' => {
+      'columns' => [
+        'pkgnum',    'int',    '',   '',
+        'custnum',   'int',    '',   '',
+        'pkgpart',   'int',    '',   '',
+        'otaker',    'varchar', '', 8,
+        'setup',     @date_type,
+        'bill',      @date_type,
+        'susp',      @date_type,
+        'cancel',    @date_type,
+        'expire',    @date_type,
+        'manual_flag', 'char', 'NULL', 1,
+      ],
+      'primary_key' => 'pkgnum',
+      'unique' => [],
+      'index' => [ ['custnum'] ],
+    },
+
+    'cust_refund' => {
+      'columns' => [
+        'refundnum',    'int',    '',   '',
+        #now cust_credit_refund #'crednum',      'int',    '',   '',
+        'custnum',  'int',    '',   '',
+        '_date',        @date_type,
+        'refund',       @money_type,
+        'otaker',       'varchar',   '',   8,
+        'reason',       'varchar',   '',   $char_d,
+        'payby',        'char',   '',     4, # CARD/BILL/COMP, should be index
+                                             # into payment type table.
+        'payinfo',      'varchar',   'NULL', $char_d,  #see cust_main above
+        'paybatch',     'varchar',   'NULL', $char_d,
+        'closed',    'char', 'NULL', 1,
+      ],
+      'primary_key' => 'refundnum',
+      'unique' => [],
+      'index' => [],
+    },
+
+    'cust_credit_refund' => {
+      'columns' => [
+        'creditrefundnum', 'int',     '',   '',
+        'crednum',  'int',     '',   '',
+        'refundnum',  'int',     '',   '',
+        'amount',  @money_type,
+        '_date',   @date_type
+      ],
+      'primary_key' => 'creditrefundnum',
+      'unique' => [],
+      'index' => [ [ 'crednum', 'refundnum' ] ],
+    },
+
+
+    'cust_svc' => {
+      'columns' => [
+        'svcnum',    'int',    '',   '',
+        'pkgnum',    'int',    'NULL',   '',
+        'svcpart',   'int',    '',   '',
+      ],
+      'primary_key' => 'svcnum',
+      'unique' => [],
+      'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
+    },
+
+    'part_pkg' => {
+      'columns' => [
+        'pkgpart',    'int',    '',   '',
+        'pkg',        'varchar',   '',   $char_d,
+        'comment',    'varchar',   '',   $char_d,
+        'setup',      @perl_type,
+        'freq',       'int', '', '',  #billing frequency (months)
+        'recur',      @perl_type,
+        'setuptax',  'char', 'NULL', 1,
+        'recurtax',  'char', 'NULL', 1,
+        'plan',       'varchar', 'NULL', $char_d,
+        'plandata',   'text', 'NULL', '',
+        'disabled',   'char', 'NULL', 1,
+        'taxclass',   'varchar', 'NULL', $char_d,
+      ],
+      'primary_key' => 'pkgpart',
+      'unique' => [],
+      'index' => [ [ 'disabled' ] ],
+    },
+
+#    'part_title' => {
+#      'columns' => [
+#        'titlenum',   'int',    '',   '',
+#        'title',      'varchar',   '',   $char_d,
+#      ],
+#      'primary_key' => 'titlenum',
+#      'unique' => [ [] ],
+#      'index' => [ [] ],
+#    },
+
+    'pkg_svc' => {
+      'columns' => [
+        'pkgpart',    'int',    '',   '',
+        'svcpart',    'int',    '',   '',
+        'quantity',   'int',    '',   '',
+      ],
+      'primary_key' => '',
+      'unique' => [ ['pkgpart', 'svcpart'] ],
+      'index' => [ ['pkgpart'] ],
+    },
+
+    'part_referral' => {
+      'columns' => [
+        'refnum',   'int',    '',   '',
+        'referral', 'varchar',   '',   $char_d,
+      ],
+      'primary_key' => 'refnum',
+      'unique' => [],
+      'index' => [],
+    },
+
+    'part_svc' => {
+      'columns' => [
+        'svcpart',    'int',    '',   '',
+        'svc',        'varchar',   '',   $char_d,
+        'svcdb',      'varchar',   '',   $char_d,
+        'disabled',   'char',  'NULL',   1,
+      ],
+      'primary_key' => 'svcpart',
+      'unique' => [],
+      'index' => [ [ 'disabled' ] ],
+    },
+
+    'part_svc_column' => {
+      'columns' => [
+        'columnnum',   'int',         '', '',
+        'svcpart',     'int',         '', '',
+        'columnname',  'varchar',     '', 64,
+        'columnvalue', 'varchar', 'NULL', $char_d,
+        'columnflag',  'char',    'NULL', 1, 
+      ],
+      'primary_key' => 'columnnum',
+      'unique' => [ [ 'svcpart', 'columnname' ] ],
+      'index' => [ [ 'svcpart' ] ],
+    },
+
+    #(this should be renamed to part_pop)
+    'svc_acct_pop' => {
+      'columns' => [
+        'popnum',    'int',    '',   '',
+        'city',      'varchar',   '',   $char_d,
+        'state',     'varchar',   '',   $char_d,
+        'ac',        'char',   '',   3,
+        'exch',      'char',   '',   3,
+        'loc',       'char',   'NULL',   4, #NULL for legacy purposes
+      ],
+      'primary_key' => 'popnum',
+      'unique' => [],
+      'index' => [ [ 'state' ] ],
+    },
+
+    'part_pop_local' => {
+      'columns' => [
+        'localnum',  'int',     '',     '',
+        'popnum',    'int',     '',     '',
+        'city',      'varchar', 'NULL', $char_d,
+        'state',     'char',    'NULL', 2,
+        'npa',       'char',    '',     3,
+        'nxx',       'char',    '',     3,
+      ],
+      'primary_key' => 'localnum',
+      'unique' => [],
+      'index' => [ [ 'npa', 'nxx' ], [ 'popnum' ] ],
+    },
+
+    'svc_acct' => {
+      'columns' => [
+        'svcnum',    'int',    '',   '',
+        'username',  'varchar',   '',   $username_len, #unique (& remove dup code)
+        '_password', 'varchar',   '',   50, #13 for encryped pw's plus ' *SUSPENDED* (mp5 passwords can be 34)
+        'sec_phrase', 'varchar',  'NULL',   $char_d,
+        'popnum',    'int',    'NULL',   '',
+        'uid',       'int', 'NULL',   '',
+        'gid',       'int', 'NULL',   '',
+        'finger',    'varchar',   'NULL',   $char_d,
+        'dir',       'varchar',   'NULL',   $char_d,
+        'shell',     'varchar',   'NULL',   $char_d,
+        'quota',     'varchar',   'NULL',   $char_d,
+        'slipip',    'varchar',   'NULL',   15, #four TINYINTs, bah.
+        'seconds',   'int', 'NULL',   '', #uhhhh
+        'domsvc',    'int', '',   '',
+      ],
+      'primary_key' => 'svcnum',
+      #'unique' => [ [ 'username', 'domsvc' ] ],
+      'unique' => [],
+      'index' => [ ['username'], ['domsvc'] ],
+    },
+
+#    'svc_acct_sm' => {
+#      'columns' => [
+#        'svcnum',    'int',    '',   '',
+#        'domsvc',    'int',    '',   '',
+#        'domuid',    'int', '',   '',
+#        'domuser',   'varchar',   '',   $char_d,
+#      ],
+#      'primary_key' => 'svcnum',
+#      'unique' => [ [] ],
+#      'index' => [ ['domsvc'], ['domuid'] ], 
+#    },
+
+    #'svc_charge' => {
+    #  'columns' => [
+    #    'svcnum',    'int',    '',   '',
+    #    'amount',    @money_type,
+    #  ],
+    #  'primary_key' => 'svcnum',
+    #  'unique' => [ [] ],
+    #  'index' => [ [] ],
+    #},
+
+    'svc_domain' => {
+      'columns' => [
+        'svcnum',    'int',    '',   '',
+        'domain',    'varchar',    '',   $char_d,
+        'catchall',  'int', 'NULL',    '',
+      ],
+      'primary_key' => 'svcnum',
+      'unique' => [ ['domain'] ],
+      'index' => [],
+    },
+
+    'domain_record' => {
+      'columns' => [
+        'recnum',    'int',     '',  '',
+        'svcnum',    'int',     '',  '',
+        #'reczone',   'varchar', '',  $char_d,
+        'reczone',   'varchar', '',  255,
+        'recaf',     'char',    '',  2,
+        'rectype',   'char',    '',  5,
+        #'recdata',   'varchar', '',  $char_d,
+        'recdata',   'varchar', '',  255,
+      ],
+      'primary_key' => 'recnum',
+      'unique'      => [],
+      'index'       => [ ['svcnum'] ],
+    },
+
+    'svc_forward' => {
+      'columns' => [
+        'svcnum',   'int',    '',  '',
+        'srcsvc',   'int',    '',  '',
+        'dstsvc',   'int',    '',  '',
+        'dst',      'varchar',    'NULL',  $char_d,
+      ],
+      'primary_key' => 'svcnum',
+      'unique'      => [],
+      'index'       => [ ['srcsvc'], ['dstsvc'] ],
+    },
+
+    'svc_www' => {
+      'columns' => [
+        'svcnum',   'int',    '',  '',
+        'recnum',   'int',    '',  '',
+        'usersvc',  'int',    '',  '',
+      ],
+      'primary_key' => 'svcnum',
+      'unique'      => [],
+      'index'       => [],
+    },
+
+    #'svc_wo' => {
+    #  'columns' => [
+    #    'svcnum',    'int',    '',   '',
+    #    'svcnum',    'int',    '',   '',
+    #    'svcnum',    'int',    '',   '',
+    #    'worker',    'varchar',   '',   $char_d,
+    #    '_date',     @date_type,
+    #  ],
+    #  'primary_key' => 'svcnum',
+    #  'unique' => [ [] ],
+    #  'index' => [ [] ],
+    #},
+
+    'prepay_credit' => {
+      'columns' => [
+        'prepaynum',   'int',     '',   '',
+        'identifier',  'varchar', '', $char_d,
+        'amount',      @money_type,
+        'seconds',     'int',     'NULL', '',
+      ],
+      'primary_key' => 'prepaynum',
+      'unique'      => [ ['identifier'] ],
+      'index'       => [],
+    },
+
+    'port' => {
+      'columns' => [
+        'portnum',  'int',     '',   '',
+        'ip',       'varchar', 'NULL', 15,
+        'nasport',  'int',     'NULL', '',
+        'nasnum',   'int',     '',   '',
+      ],
+      'primary_key' => 'portnum',
+      'unique'      => [],
+      'index'       => [],
+    },
+
+    'nas' => {
+      'columns' => [
+        'nasnum',   'int',     '',    '',
+        'nas',      'varchar', '',    $char_d,
+        'nasip',    'varchar', '',    15,
+        'nasfqdn',  'varchar', '',    $char_d,
+        'last',     'int',     '',    '',
+      ],
+      'primary_key' => 'nasnum',
+      'unique'      => [ [ 'nas' ], [ 'nasip' ] ],
+      'index'       => [ [ 'last' ] ],
+    },
+
+    'session' => {
+      'columns' => [
+        'sessionnum', 'int',       '',   '',
+        'portnum',    'int',       '',   '',
+        'svcnum',     'int',       '',   '',
+        'login',      @date_type,
+        'logout',     @date_type,
+      ],
+      'primary_key' => 'sessionnum',
+      'unique'      => [],
+      'index'       => [ [ 'portnum' ] ],
+    },
+
+    'queue' => {
+      'columns' => [
+        'jobnum', 'int', '', '',
+        'job', 'text', '', '',
+        '_date', 'int', '', '',
+        'status', 'varchar', '', $char_d,
+        'statustext', 'text', 'NULL', '',
+        'svcnum', 'int', 'NULL', '',
+      ],
+      'primary_key' => 'jobnum',
+      'unique'      => [],
+      'index'       => [ [ 'svcnum' ], [ 'status' ] ],
+    },
+
+    'queue_arg' => {
+      'columns' => [
+        'argnum', 'int', '', '',
+        'jobnum', 'int', '', '',
+        'arg', 'text', 'NULL', '',
+      ],
+      'primary_key' => 'argnum',
+      'unique'      => [],
+      'index'       => [ [ 'jobnum' ] ],
+    },
+
+    'queue_depend' => {
+      'columns' => [
+        'dependnum', 'int', '', '',
+        'jobnum', 'int', '', '',
+        'depend_jobnum', 'int', '', '',
+      ],
+      'primary_key' => 'dependnum',
+      'unique'      => [],
+      'index'       => [ [ 'jobnum' ], [ 'depend_jobnum' ] ],
+    },
+
+    'export_svc' => {
+      'columns' => [
+        'exportsvcnum' => 'int', '', '',
+        'exportnum'    => 'int', '', '',
+        'svcpart'      => 'int', '', '',
+      ],
+      'primary_key' => 'exportsvcnum',
+      'unique'      => [ [ 'exportnum', 'svcpart' ] ],
+      'index'       => [ [ 'exportnum' ], [ 'svcpart' ] ],
+    },
+
+    'part_export' => {
+      'columns' => [
+        'exportnum', 'int', '', '',
+        #'svcpart',   'int', '', '',
+        'machine', 'varchar', '', $char_d,
+        'exporttype', 'varchar', '', $char_d,
+        'nodomain',     'char', 'NULL', 1,
+      ],
+      'primary_key' => 'exportnum',
+      'unique'      => [],
+      'index'       => [ [ 'machine' ], [ 'exporttype' ] ],
+    },
+
+    'part_export_option' => {
+      'columns' => [
+        'optionnum', 'int', '', '',
+        'exportnum', 'int', '', '',
+        'optionname', 'varchar', '', $char_d,
+        'optionvalue', 'text', 'NULL', '',
+      ],
+      'primary_key' => 'optionnum',
+      'unique'      => [],
+      'index'       => [ [ 'exportnum' ], [ 'optionname' ] ],
+    },
+
+    'radius_usergroup' => {
+      'columns' => [
+        'usergroupnum', 'int', '', '',
+        'svcnum',       'int', '', '',
+        'groupname',    'varchar', '', $char_d,
+      ],
+      'primary_key' => 'usergroupnum',
+      'unique'      => [],
+      'index'       => [ [ 'svcnum' ], [ 'groupname' ] ],
+    },
+
+    'msgcat' => {
+      'columns' => [
+        'msgnum', 'int', '', '',
+        'msgcode', 'varchar', '', $char_d,
+        'locale', 'varchar', '', 16,
+        'msg', 'text', '', '',
+      ],
+      'primary_key' => 'msgnum',
+      'unique'      => [ [ 'msgcode', 'locale' ] ],
+      'index'       => [],
+    },
+
+    'cust_tax_exempt' => {
+      'columns' => [
+        'exemptnum', 'int', '', '',
+        'custnum',   'int', '', '',
+        'taxnum',    'int', '', '',
+        'year',      'int', '', '',
+        'month',     'int', '', '',
+        'amount',   @money_type,
+      ],
+      'primary_key' => 'exemptnum',
+      'unique'      => [ [ 'custnum', 'taxnum', 'year', 'month' ] ],
+      'index'       => [],
+    },
+
+
+
+  );
+
+  %tables;
+
+}
+
index ca58d4b..00942fd 100644 (file)
@@ -42,15 +42,9 @@ sub myexport_queue {
 }
 
 sub myexport_insert { #subroutine, not method
-  my( $username, $password ) = @_;
-  #do things with $username and $password
 }
-
 sub myexport_replace { #subroutine, not method
 }
-
 sub myexport_delete { #subroutine, not method
-  my( $username ) = @_;
-  #do things with $username
 }
 
diff --git a/fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm b/fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm
deleted file mode 100755 (executable)
index 46cde4c..0000000
+++ /dev/null
@@ -1,541 +0,0 @@
-package FS::MailAdminClient;
-
-use strict;
-use vars qw($VERSION @ISA @EXPORT_OK $fs_mailadmind_socket);
-use Exporter;
-use Socket;
-use FileHandle;
-use IO::Handle;
-
-$VERSION = '0.01';
-
-@ISA = qw( Exporter );
-@EXPORT_OK = qw( signup_info authenticate list_packages list_mailboxes delete_mailbox password_mailbox add_mailbox list_forwards list_pkg_forwards delete_forward add_forward new_customer );
-
-$fs_mailadmind_socket = "/usr/local/freeside/fs_mailadmind_socket";
-
-$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
-$ENV{'SHELL'} = '/bin/sh';
-$ENV{'IFS'} = " \t\n";
-$ENV{'CDPATH'} = '';
-$ENV{'ENV'} = '';
-$ENV{'BASH_ENV'} = '';
-
-my $freeside_uid = scalar(getpwnam('freeside'));
-die "not running as the freeside user\n" if $> != $freeside_uid;
-
-=head1 NAME
-
-FS::MailAdminClient - Freeside mail administration client API
-
-=head1 SYNOPSIS
-
-  use FS::MailAdminClient qw( signup_info list_mailboxes  new_customer );
-
-  ( $locales, $packages, $pops ) = signup_info;
-
-  ( $accounts ) = list_mailboxes;
-
-  $error = new_customer ( {
-    'first'          => $first,
-    'last'           => $last,
-    'ss'             => $ss,
-    'comapny'        => $company,
-    'address1'       => $address1,
-    'address2'       => $address2,
-    'city'           => $city,
-    'county'         => $county,
-    'state'          => $state,
-    'zip'            => $zip,
-    'country'        => $country,
-    'daytime'        => $daytime,
-    'night'          => $night,
-    'fax'            => $fax,
-    'payby'          => $payby,
-    'payinfo'        => $payinfo,
-    'paydate'        => $paydate,
-    'payname'        => $payname,
-    'invoicing_list' => $invoicing_list,
-    'pkgpart'        => $pkgpart,
-    'username'       => $username,
-    '_password'       => $password,
-    'popnum'         => $popnum,
-  } );
-
-=head1 DESCRIPTION
-
-This module provides an API for a remote mail administration server.
-
-It needs to be run as the freeside user.  Because of this, the program which
-calls these subroutines should be written very carefully.
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item signup_info
-
-Returns three array references of hash references.
-
-The first set of hash references is of allowable locales.  Each hash reference
-has the following keys:
-  taxnum
-  state
-  county
-  country
-
-The second set of hash references is of allowable packages.  Each hash
-reference has the following keys:
-  pkgpart
-  pkg
-
-The third set of hash references is of allowable POPs (Points Of Presence).
-Each hash reference has the following keys:
-  popnum
-  city
-  state
-  ac
-  exch
-
-=cut
-
-sub signup_info {
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "signup_info\n";
-  SOCK->flush;
-
-  chop ( my $n_cust_main_county = <SOCK> );
-  my @cust_main_county = map {
-    chop ( my $taxnum  = <SOCK> ); 
-    chop ( my $state   = <SOCK> ); 
-    chop ( my $county  = <SOCK> ); 
-    chop ( my $country = <SOCK> );
-    {
-      'taxnum'  => $taxnum,
-      'state'   => $state,
-      'county'  => $county,
-      'country' => $country,
-    };
-  } 1 .. $n_cust_main_county;
-
-  chop ( my $n_part_pkg = <SOCK> );
-  my @part_pkg = map {
-    chop ( my $pkgpart = <SOCK> ); 
-    chop ( my $pkg     = <SOCK> ); 
-    {
-      'pkgpart' => $pkgpart,
-      'pkg'     => $pkg,
-    };
-  } 1 .. $n_part_pkg;
-
-  chop ( my $n_svc_acct_pop = <SOCK> );
-  my @svc_acct_pop = map {
-    chop ( my $popnum = <SOCK> ); 
-    chop ( my $city   = <SOCK> ); 
-    chop ( my $state  = <SOCK> ); 
-    chop ( my $ac     = <SOCK> );
-    chop ( my $exch   = <SOCK> );
-    chop ( my $loc    = <SOCK> );
-    {
-      'popnum' => $popnum,
-      'city'   => $city,
-      'state'  => $state,
-      'ac'     => $ac,
-      'exch'   => $exch,
-      'loc'    => $loc,
-    };
-  } 1 .. $n_svc_acct_pop;
-
-  close SOCK;
-
-  \@cust_main_county, \@part_pkg, \@svc_acct_pop;
-}
-
-=item authenticate
-
-Authentictes against a service on the remote Freeside system.  Requires a hash
-reference as a parameter with the following keys:
-    authuser
-    _password
-
-Returns a scalar error message of the form "authuser OK|FAILED" or an error
-message.
-
-=cut
-
-sub authenticate {
-  my $hashref = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "authenticate", "\n";
-  SOCK->flush;
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    authuser _password
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  close SOCK;
-
-  $error;
-}
-
-=item list_packages
-
-Returns one array reference of hash references.
-
-The set of hash references is of existing packages.  Each hash reference
-has the following keys:
-  pkgnum
-  domain
-  account
-
-=cut
-
-sub list_packages {
-  my $user = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "list_packages\n", $user, "\n";
-  SOCK->flush;
-
-  chop ( my $n_packages = <SOCK> );
-  my @packages = map {
-    chop ( my $pkgnum  = <SOCK> ); 
-    chop ( my $domain  = <SOCK> ); 
-    chop ( my $account = <SOCK> ); 
-    {
-      'pkgnum'  => $pkgnum,
-      'domain'  => $domain,
-      'account' => $account,
-    };
-  } 1 .. $n_packages;
-
-  close SOCK;
-
-  \@packages;
-}
-
-=item list_mailboxes
-
-Returns one array references of hash references.
-
-The set of hash references is of existing accounts.  Each hash reference
-has the following keys:
-  svcnum
-  username
-  _password
-
-=cut
-
-sub list_mailboxes {
-  my ($user, $package) = @_;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "list_mailboxes\n", $user, "\n", $package, "\n";
-  SOCK->flush;
-
-  chop ( my $n_svc_acct = <SOCK> );
-  my @svc_acct = map {
-    chop ( my $svcnum  = <SOCK> ); 
-    chop ( my $username  = <SOCK> ); 
-    chop ( my $_password   = <SOCK> ); 
-    {
-      'svcnum'  => $svcnum,
-      'username'  => $username,
-      '_password'   => $_password,
-    };
-  } 1 .. $n_svc_acct;
-
-  close SOCK;
-
-  \@svc_acct;
-}
-
-=item delete_mailbox
-
-Deletes a mailbox service from the remote Freeside system.  Requires a hash
-reference as a paramater with the following keys:
-    authuser
-    account
-
-Returns a scalar error message, or the empty string for success.
-
-=cut
-
-sub delete_mailbox {
-  my $hashref = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "delete_mailbox", "\n";
-  SOCK->flush;
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    authuser account
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  close SOCK;
-
-  $error;
-}
-
-=item password_mailbox
-
-Changes the password for a mailbox service on the remote Freeside system.
-  Requires a hash reference as a paramater with the following keys:
-    authuser
-    account
-    _password
-
-Returns a scalar error message, or the empty string for success.
-
-=cut
-
-sub password_mailbox {
-  my $hashref = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "password_mailbox", "\n";
-  SOCK->flush;
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    authuser account _password
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  close SOCK;
-
-  $error;
-}
-
-=item add_mailbox
-
-Creates a mailbox service on the remote Freeside system.  Requires a hash
-reference as a parameter with the following keys:
-    authuser
-    package
-    account
-    _password
-
-Returns a scalar error message, or the empty string for success.
-
-=cut
-
-sub add_mailbox {
-  my $hashref = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "add_mailbox", "\n";
-  SOCK->flush;
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    authuser package account _password
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  close SOCK;
-
-  $error;
-}
-
-=item list_forwards
-
-Returns one array references of hash references.
-
-The set of hash references is of existing forwards.  Each hash reference
-has the following keys:
-  svcnum
-  dest
-
-=cut
-
-sub list_forwards {
-  my ($user, $service) = @_;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "list_forwards\n", $user, "\n", $service, "\n";
-  SOCK->flush;
-
-  chop ( my $n_svc_forward = <SOCK> );
-  my @svc_forward = map {
-    chop ( my $svcnum  = <SOCK> ); 
-    chop ( my $dest  = <SOCK> ); 
-    {
-      'svcnum'  => $svcnum,
-      'dest'  => $dest,
-    };
-  } 1 .. $n_svc_forward;
-
-  close SOCK;
-
-  \@svc_forward;
-}
-
-=item list_pkg_forwards
-
-Returns one array references of hash references.
-
-The set of hash references is of existing forwards.  Each hash reference
-has the following keys:
-  svcnum
-  srcsvc
-  dest
-
-=cut
-
-sub list_pkg_forwards {
-  my ($user, $package) = @_;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "list_pkg_forwards\n", $user, "\n", $package, "\n";
-  SOCK->flush;
-
-  chop ( my $n_svc_forward = <SOCK> );
-  my @svc_forward = map {
-    chop ( my $svcnum  = <SOCK> ); 
-    chop ( my $srcsvc  = <SOCK> ); 
-    chop ( my $dest  = <SOCK> ); 
-    {
-      'svcnum'  => $svcnum,
-      'srcsvc'  => $srcsvc,
-      'dest'  => $dest,
-    };
-  } 1 .. $n_svc_forward;
-
-  close SOCK;
-
-  \@svc_forward;
-}
-
-=item delete_forward
-
-Deletes a forward service from the remote Freeside system.  Requires a hash
-reference as a paramater with the following keys:
-    authuser
-    svcnum
-
-Returns a scalar error message, or the empty string for success.
-
-=cut
-
-sub delete_forward {
-  my $hashref = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "delete_forward", "\n";
-  SOCK->flush;
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    authuser svcnum
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  close SOCK;
-
-  $error;
-}
-
-=item add_forward
-
-Creates a forward service on the remote Freeside system.  Requires a hash
-reference as a parameter with the following keys:
-    authuser
-    package
-    source
-    dest
-
-Returns a scalar error message, or the empty string for success.
-
-=cut
-
-sub add_forward {
-  my $hashref = shift;
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "add_forward", "\n";
-  SOCK->flush;
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    authuser package source dest
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  close SOCK;
-
-  $error;
-}
-
-=item new_customer HASHREF
-
-Adds a customer to the remote Freeside system.  Requires a hash reference as
-a paramater with the following keys:
-  first
-  last
-  ss
-  comapny
-  address1
-  address2
-  city
-  county
-  state
-  zip
-  country
-  daytime
-  night
-  fax
-  payby
-  payinfo
-  paydate
-  payname
-  invoicing_list
-  pkgpart
-  username
-  _password
-  popnum
-
-Returns a scalar error message, or the empty string for success.
-
-=cut
-
-sub new_customer {
-  my $hashref = shift;
-
-  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
-  connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!";
-  print SOCK "new_customer\n";
-
-  print SOCK join("\n", map { $hashref->{$_} } qw(
-    first last ss company address1 address2 city county state zip country
-    daytime night fax payby payinfo paydate payname invoicing_list
-    pkgpart username _password popnum
-  ) ), "\n";
-  SOCK->flush;
-
-  chop( my $error = <SOCK> );
-  $error;
-}
-
-=back
-
-=head1 VERSION
-
-$Id: MailAdminClient.pm,v 1.1 2001-10-18 15:04:54 jeff Exp $
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<fs_signupd>, L<FS::SignupServer>, L<FS::cust_main>
-
-=cut
-
-1;
-
diff --git a/fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi b/fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi
deleted file mode 100755 (executable)
index c26c3dc..0000000
+++ /dev/null
@@ -1,698 +0,0 @@
-#!/usr/bin/perl
-########################################################################
-#                                                                      #
-#    mailadmin.cgi                NCI2000                              #
-#                                 Jeff Finucane <jeff@nci2000.net>     #
-#                                 26 April 2001                        #
-#                                                                      #
-########################################################################
-
-use DBI;
-use strict;
-use CGI;
-use FS::MailAdminClient qw(authenticate list_packages list_mailboxes delete_mailbox password_mailbox add_mailbox list_forwards list_pkg_forwards delete_forward add_forward);
-
-my $sessionfile = '/usr/local/apache/htdocs/mailadmin/adminsess';   # session file
-my $tmpdir = '/usr/local/apache/htdocs/mailadmin/tmp';         # Location to store temp files
-my $cookiedomain = ".your.dom";      # domain if THIS server, should prepend with a '.'
-my $cookieexpire = '+12h';              # expire the cookie session after this much idle time
-my $sessexpire = 43200;                 # expire session after this long of no use (in seconds)
-
-my $body = "<body bgcolor=dddddd>";
-
-#### Should not have to change anything under this line ####
-my $printmainpage = 1;
-my $i = 0;
-my $printheader = 1;
-my $query = new CGI;
-my $cgi = $query->url();
-my $now = getdatetime();
-my $current_package = 0;
-my $current_account = 0;
-my $current_domname = "";
-
-# if they are trying to login we wont check the session yet
-if ($query->param('login') eq '' && $query->param('action') ne 'login') {
-  checksession();
-  printheader();
-}
-
-if ($query->param('login') ne '') {
-
-   my $username = $query->param('username');
-   my $password = $query->param('password');
-
-   if (!checkuserpass($username, $password)) {
-      printheader();
-      error('not_admin');
-   }
-
-   my @alpha = ('A'..'Z', 'a'..'z', 0..9);
-   my $sessid = '';
-   for (my $i = 0; $i < 10; $i++) {
-       $sessid .= @alpha[rand(@alpha)];
-   }
-
-   my $cookie1 = $query->cookie(-name=>'username',
-                               -value=>$username,
-                               -expires=>$cookieexpire,
-                               -domain=>$cookiedomain);
-
-   my $cookie2 = $query->cookie(-name=>'ma_sessionid',
-                               -value=>$sessid,
-                               -expires=>$cookieexpire,
-                               -domain=>$cookiedomain);
-
-   my $now = time();
-   open(NEWSESS, ">>$sessionfile") || error('open');
-   print NEWSESS "$username $sessid $now 0 0\n";
-   close(NEWSESS);
-
-   print $query->header(-COOKIE=>[$cookie1, $cookie2]);
-   $printmainpage = 1;
-
-} elsif ($query->param('action') eq 'blankframe') {
-   
-  print "<html>$body</body></html>\n";
-   $printmainpage = 0;
-
-} elsif ($query->param('action') eq 'list_packages') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  my $list = list_packages($username);
-  print "<html>$body\n";
-  print "<center><table border=0>\n";
-  print "<tr><td></td><td><p>Package Number</td><td><p>Description</td></tr>\n";
-  foreach my $package ( @{$list} ) {
-    print "<tr>";
-    print "<td></td><td><p>$package->{'pkgnum'}</td><td><p>$package->{'domain'}</td>\n";
-    print "<td></td><td><a href=\"$cgi\?action=select&package=$package->{'pkgnum'}&account=$package->{'account'}&domname=$package->{'domain'}\" target=\"rightmainframe\">select</td>\n";
-    print "</tr>";
-  }
-  print "</table>\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('action') eq 'list_mailboxes') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username)  unless $current_package;
-  my $list = list_mailboxes($username, $current_package);
-  my $forwardlist = list_pkg_forwards($username, $current_package);
-  print "<html>$body\n";
-  print "<center><table border=0>\n";
-  print "<tr><td></td><td><p>Username</td><td><p>Password</td></tr>\n";
-  foreach my $account ( @{$list} ) {
-    print "<tr>";
-    print "<td></td><td><p>$account->{'username'}</td><td><p>$account->{'_password'}</td>\n";
-    print "<td></td><td><a href=\"$cgi\?action=change&account=$account->{'svcnum'}&mailbox=$account->{'username'}\" target=\"rightmainframe\">change</td>\n";
-    print "</tr>";
-
-#    my $forwardlist = list_forwards($username, $account->{'svcnum'});
-#    foreach my $forward ( @{$forwardlist} ) {
-#      my $label = qq!=> ! . $forward->{'dest'};
-#      print "<tr><td></td><td></td><td><p>$label</td></tr>\n";
-#    }
-    foreach my $forward ( @{$forwardlist} ) {
-      if ($forward->{'srcsvc'} == $account->{'svcnum'}) {
-        my $label = qq!=> ! . $forward->{'dest'};
-        print "<tr><td></td><td></td><td><p>$label</td></tr>\n";
-      }
-    }
-
-  }
-  print "</table>\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('action') eq 'select') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  $current_package = $query->param('package');
-  $current_account = $query->param('account');
-  $current_domname = $query->param('domname');
-  set_package();
-  print "<html>$body\n";
-  print "<form name=form1 action=\"$cgi\" method=post target=\"rightmainframe\">\n";
-  print "<center>\n";
-  print "<p>Selected package $current_package\n";
-  print "</center>\n";
-  print "</form>\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('action') eq 'change') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  my $mailbox  = $query->param('mailbox');
-  my $list = list_forwards($username, $account);
-  print "<html>$body\n";
-  print "<form name=form1 action=\"$cgi\" method=post target=\"rightmainframe\">\n";
-  print "<center><table border=0>\n";
-  print "<tr><td></td><td><p>Username</td><td><p>$mailbox</td></tr>\n";
-  print "<input type=hidden name=\"account\" value=\"$account\">\n";
-  print "<input type=hidden name=\"mailbox\" value=\"$mailbox\">\n";
-  foreach my $forward ( @{$list} ) {
-    my $label = qq!=> ! . $forward->{'dest'};
-#    print "<tr><td></td><td></td><td><p>$label</td></tr>\n";
-    print "<tr><td></td><td></td><td><p>$label</td><td><a href=\"$cgi\?action=deleteforward&service=$forward->{'svcnum'}&mailbox=$mailbox&dest=$forward->{'dest'}\" target=\"rightmainframe\">remove</td></tr>\n";
-  }
-  print "<tr><td></td><td><p>Password</td><td><input type=text name=\"_password\" value=\"\"></td></tr>\n";
-  print "</table>\n";
-  print "<input type=submit name=\"deleteaccount\" value=\"Delete This User\">\n";
-  print "<input type=submit name=\"changepassword\" value=\"Change The Password\">\n";
-  print "<input type=submit name=\"addforward\" value=\"Add Forwarding\">\n";
-  print "</center>\n";
-  print "</form>\n";
-  print "<br>\n";
-  print "<p> You may delete this user and all mailforwarding by pressing <B>Delete This User</B>.\n";
-  print "<p> To set or change the password for this user, type the new password in the box next to <B>Password</B> and press <B>Change The Password</B>.\n";
-  print "<p> If you would like to have mail destined for this user forwarded to another email address then press the <B>Add Forwarding</B> button.\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('deleteaccount') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  my $mailbox  = $query->param('mailbox');
-  print "<html>$body\n";
-  print "<form name=form1 action=\"$cgi\" method=post target=\"rightmainframe\">\n";
-  print "<p>Are you certain you want to delete user $mailbox?\n";
-  print "<p><input type=hidden name=\"account\" value=\"$account\">\n";
-  print "<input type=submit name=\"deleteaccounty\" value=\"Confirm\">\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('deleteaccounty') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  
-  if  ( my $error = delete_mailbox ( {
-      'authuser'         => $username,
-      'account'          => $account,
-    } ) ) {
-    print "<html>$body\n";
-    print "<p>$error\n";
-    print "</body></html>\n";
-      
-  } else {
-    print "<html>$body\n";
-    print "<p>Deleted\n";
-    print "</body></html>\n";
-  }
-
-  $printmainpage=0;
-
-} elsif ($query->param('changepassword') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  my $_password  = $query->param('_password');
-  
-  if  ( my $error = password_mailbox ( {
-      'authuser'         => $username,
-      'account'          => $account,
-      '_password'        => $_password,
-    } ) ) {
-    print "<html>$body\n";
-    print "<p>$error\n";
-    print "</body></html>\n";
-      
-  } else {
-    print "<html>$body\n";
-    print "<p>Changed\n";
-    print "</body></html>\n";
-  }
-
-  $printmainpage=0;
-
-} elsif ($query->param('action') eq 'newmailbox') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  print "<html>$body\n";
-  print "<form name=form1 action=\"$cgi\" method=post target=\"rightmainframe\">\n";
-  print "<center><table border=0>\n";
-  print "<tr><td></td><td><p>Username </td><td><input type=text name=\"account\" value=\"\"></td><td>@ " . $current_domname . "</td></tr>\n";
-  print "<tr><td></td><td><p>Password</td><td><input type=text name=\"_password\" value=\"\"></td></tr>\n";
-  print "</table>\n";
-  print "<input type=submit name=\"addmailbox\" value=\"Add This User\">\n";
-  print "</center>\n";
-  print "</form>\n";
-  print "<br>\n";
-  print "<p>Use this screen to add a new mailbox user.  If the domain name of the email address (the part after the <B>@</B> sign) is not what you expect then you may need to use <B>List Packages</B> to select the package with the correct domain.\n";
-  print "<p>Enter the first portion of the email address in the box adjacent to <B>Username</B> and enter the password for that user in the space next to <B>Password</B>.  Then press the button labeled <B>Add The User</B>.\n";
-  print "<p>If you do not want to add a new user at this time then select a choice from the menu at the left, such as <B>List Mailboxes</B>.\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('addmailbox') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  my $_password  = $query->param('_password');
-  
-  if  ( my $error = add_mailbox ( {
-      'authuser'         => $username,
-      'package'          => $current_package,
-      'account'          => $account,
-      '_password'        => $_password,
-    } ) ) {
-    print "<html>$body\n";
-    print "<p>$error\n";
-    print "</body></html>\n";
-      
-  } else {
-    print "<html>$body\n";
-    print "<p>Created\n";
-    print "</body></html>\n";
-  }
-
-  $printmainpage=0;
-
-} elsif ($query->param('action') eq 'deleteforward') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $svcnum   = $query->param('service');
-  my $mailbox  = $query->param('mailbox');
-  my $dest  = $query->param('dest');
-  print "<html>$body\n";
-  print "<form name=form1 action=\"$cgi\" method=post target=\"rightmainframe\">\n";
-  print "<p>Are you certain you want to remove the forwarding from $mailbox to $dest?\n";
-  print "<p><input type=hidden name=\"service\" value=\"$svcnum\">\n";
-  print "<input type=submit name=\"deleteforwardy\" value=\"Confirm\">\n";
-  print "</body></html>\n";
-  $printmainpage=0;
-
-} elsif ($query->param('deleteforwardy') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $service  = $query->param('service');
-  
-  if  ( my $error = delete_forward ( {
-      'authuser'        => $username,
-      'svcnum'          => $service,
-    } ) ) {
-    print "<html>$body\n";
-    print "<p>$error\n";
-    print "</body></html>\n";
-      
-  } else {
-    print "<html>$body\n";
-    print "<p>Forwarding Removed\n";
-    print "</body></html>\n";
-  }
-
-  $printmainpage=0;
-
-} elsif ($query->param('addforward') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  my $mailbox  = $query->param('mailbox');
-  
-  print "<html>$body\n";
-  print "<form name=form1 action=\"$cgi\" method=post target=\"rightmainframe\">\n";
-  print "<center><table border=0>\n";
-  print "<input type=hidden name=\"account\" value=\"$account\">\n";
-  print "<input type=hidden name=\"mailbox\" value=\"$mailbox\">\n";
-  print "<tr><td>Forward mail from </td><td><p>$mailbox:</td><td> to </td></tr>\n";
-  print "<tr><td></td><td><p>Destination:</td><td><input type=text name=\"dest\" value=\"\"></td></tr>\n";
-  print "</table>\n";
-  print "<input type=submit name=\"addforwarddst\" value=\"Add the Forwarding\">\n";
-  print "</center>\n";
-  print "</form>\n";
-  print "<br>\n";
-  print "<p> If you would like mail originally destined for the above address to be forwarded to a different email address then type that email address in the box next to <B>Destination:</B> and press the <B>Add the Forwarding</B> button.\n";
-  print "<p> If you do not want to add mail forwarding then select a choice from the menu at the left, such as <B>List Accounts</B>.\n";
-
-  $printmainpage=0;
-
-} elsif ($query->param('addforwarddst') ne '') {
-
-  my $username = $query->cookie(-name=>'username');  # session checked
-  select_package($username) unless $current_package;
-  my $account  = $query->param('account');
-  my $dest  = $query->param('dest');
-  
-  if  ( my $error = add_forward ( {
-      'authuser'         => $username,
-      'package'          => $current_package,
-      'source'           => $account,
-      'dest'             => $dest,
-    } ) ) {
-    print "<html>$body\n";
-    print "<p>$error\n";
-    print "</body></html>\n";
-      
-  } else {
-    print "<html>$body\n";
-    print "<p>Forwarding Created\n";
-    print "</body></html>\n";
-  }
-
-  $printmainpage=0;
-
-} elsif ($query->param('action') eq 'navframe') {
-
-  print "<html><body bgcolor=bbbbbb>\n";
-  print "<center><h2>NCI2000 MAIL ADMIN Web Interface</h2></center>\n";
-
-  print "<br><center>Choose Action:</center><br>\n";
-  print "<center><table border=0>\n";
-  print "<ul>\n";
-  print "<tr><td><li><a href=\"$cgi\?action=logout\" target=\"_top\">Log Off</a></td><tr>\n";
-  print "<tr><td><li><a href=\"$cgi\?action=list_packages\" target=\"rightmainframe\">List Packages</a></td><tr>\n";
-  print "<tr><td><li><a href=\"$cgi\?action=list_mailboxes\" target=\"rightmainframe\">List Accounts</a></td><tr>\n";
-  print "<tr><td><li><a href=\"$cgi\?action=newmailbox\" target=\"rightmainframe\">Add Account</a></td><tr>\n";
-  print "</ul>\n";
-  print "</table></center>\n";
-
-  print "<br><br><br>\n";
-  print "</body></html>\n";
-
-  $printmainpage = 0;
-
-} elsif ($query->param('action') eq 'rightmainframe') {
-
-  print "<html>$body\n";
-  print "<br><br><br>\n";
-  print "<font size=4><----- Please choose function on the left menu</font>\n";
-  print "<br><br>\n";
-  print "<p> Choose <B>Log Off</B> when you are finished.  This helps prevent unauthorized access to your accounts.\n";
-  print "<p> Use <B>List Packages</B> when you administer multiple packages.  When you have multiple domains at NCI2000 you are likely to have multiple packages.  Use of <B>List Packages</B> is not necessary if administer only one package.\n";
-  print "<p> Use <B>List Accounts</B> to view your current arrangement of mailboxes.  From this list you my choose to make changes to existing mailboxes or delete mailboxes.  If you would like to modify the forwarding associated with a mailbox then choose it from this list.\n";
-  print "<p> Use <B>Add Account</B> when you would like an additional mailbox.  After you have added the mailbox you may choose to make additional changes from the list provided by <B>List Accounts<B>.\n";
-  print "</body></html>\n";
-
-  $printmainpage = 0;
-
-}
-
-
-if ($query->param('action') eq 'login') {
-
-    printheader();
-    printlogin();
-
-} elsif ($query->param('action') eq 'logout') {
-
-    destroysession();
-    printheader();
-    printlogin();
-
-} elsif ($printmainpage) {
-
-
-  print "<html><head><title>NCI2000 MAIL ADMIN Web Interface</title></head>\n";
-  print "<FRAMESET cols=\"160,*\" BORDER=\"3\">\n";
-  print "<FRAME NAME=\"navframe\" src=\"$cgi?action=navframe\">\n";
-  print "<FRAME NAME=\"rightmainframe\" src=\"$cgi?action=rightmainframe\">\n";
-  print "</FRAMESET>\n";
-  print "</html>\n";
-
-
-}
-
-sub getdatetime {
-  my $today = localtime(time());
-  my ($day,$mon,$dayofmon,$time,$year) = split(/\s+/,$today);
-  my @datemonths = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
-
-  my $numidx = "01";
-  my ($nummon);
-  foreach my $mons (@datemonths) {
-    if ($mon eq $mons) {
-     $nummon = $numidx;
-    }
-    $numidx++;
-  }
-
-  return "$year-$nummon-$dayofmon $time";
-
-}
-
-sub error {
-
-  my $error = shift;
-  my $arg1 = shift;
-
-   printheader();
-
-   if ($error eq 'not_admin') {
-     print "<html><head><title>Error!</title></head>\n";
-     print "$body\n";
-     print "<center><h1><font face=arial>Error!</font></h1></center>\n";
-     print "<font face=arial>Unauthorized attempt to access mail administration.</font>\n";
-     print "<br><font face=arial>Please login again if you think this is an error.</font>\n";
-     print "<form><input type=button value=\"<<Back\" OnClick=\"history.back()\"></form>\n";
-     print "</body></html>\n";
-   } elsif ($error eq 'exists') {
-     print "<html><head><title>Error!</title></head>\n";
-     print "$body\n";
-     print "<center><h1><font face=arial>Error!</font></h1></center>\n";
-     print "<font face=arial>The user you are trying to enter already exists. Please go back and enter a different username</font>\n";
-     print "</font></body></html>\n";
-   } elsif ($error eq 'ingroup') {
-     print "<html><head><title>Error!</title></head>\n";
-     print "$body\n";
-     print "<center><h1><font face=arial>Error!</font></h1></center>\n";
-     print "<font face=arial>This user is already in the group <i>$arg1</i>. Please go back and deselect group <i>$arg1</i> from the list.</font>\n";
-     print "<form><input type=button value=\"<<Back\" OnClick=\"history.back()\"></form>\n";
-     print "</font></body></html>\n";
-   } elsif ($error eq 'sess_expired') {
-     print "<html>$body\n";
-     print "<center><font size=4>Your session has expired.</font></center>\n";
-     print "<br><br><center>Please login again <a href=\"$cgi\?action=login\" target=\"_top\"> HERE</a></center>\n";
-     print "</body></html>\n";
-   } elsif ($error eq 'open') {
-     print "<html>$body\n";
-     print "<center><font size=4>Unable to open or rename file.</font></center>\n";
-     print "<br><br><center>If this continues, please contact your administrator</center>\n";
-     print "</body></html>\n";
-   }
-
-
-   exit;
-
-}
-
-
-#print a html header if not printed yet
-sub printheader {
-
-  if ($printheader) {
-     print "Content-Type: text/html\n\n";
-     $printheader = 0;
-  }
-
-}
-
-
-#verify user can access administration
-sub checksession {
-
-  my $username = $query->cookie(-name=>'username');
-  my $sessionid = $query->cookie(-name=>'ma_sessionid');
-
-  if ($sessionid eq '') {
-     printheader();
-     if ($query->param()) {
-        error('sess_expired');
-     } else {
-        printlogin();
-        exit;
-    }
-  }
-
-  my $now = time();
-  my $founduser = 0;
-  open(SESSFILE, "$sessionfile") || error('open');
-  error('open') if -l "$tmpdir/adminsess.$$";
-  open(NEWSESS, ">$tmpdir/adminsess.$$") || error('open');
-  while (<SESSFILE>) {
-       chomp();
-       my ($user, $sess, $time, $pkgnum, $svcdomain, $domname) = split(/\s+/);
-       next if $now - $sessexpire > $time;
-       if ($username eq $user && !$founduser) {
-               if ($sess eq $sessionid) {
-                       $founduser = 1;
-                       print NEWSESS "$user $sess $now $pkgnum $svcdomain $domname\n";
-                        $current_package=$pkgnum;
-                        $current_account=$svcdomain;
-                        $current_domname=$domname;
-                       next;
-               }
-       }
-       print NEWSESS "$user $sess $time $pkgnum $svcdomain $domname\n";
-  }
-  close(SESSFILE);
-  close(NEWSESS);
-  system("mv $tmpdir/adminsess.$$ $sessionfile");
-  error('sess_expired') unless $founduser;
-
-  my $cookie1 = $query->cookie(-name=>'username',
-                               -value=>$username,
-                               -expires=>$cookieexpire,
-                               -domain=>$cookiedomain);
-
-  my $cookie2 = $query->cookie(-name=>'ma_sessionid',
-                               -value=>$sessionid,
-                               -expires=>$cookieexpire,
-                               -domain=>$cookiedomain);
-
-  print $query->header(-COOKIE=>[$cookie1, $cookie2]);
-  
-  $printheader = 0;
-
-  return 0;
-
-}
-
-sub destroysession {
-
-  my $username = $query->cookie(-name=>'username');
-  my $sessionid = $query->cookie(-name=>'ma_sessionid');
-
-  if ($sessionid eq '') {
-     printheader();
-     if ($query->param()) {
-        error('sess_expired');
-     } else {
-        printlogin();
-        exit;
-    }
-  }
-
-  my $now = time();
-  my $founduser = 0;
-  open(SESSFILE, "$sessionfile") || error('open');
-  error('open') if -l "$tmpdir/adminsess.$$";
-  open(NEWSESS, ">$tmpdir/adminsess.$$") || error('open');
-  while (<SESSFILE>) {
-       chomp();
-       my ($user, $sess, $time, $pkgnum, $svcdomain, $domname) = split(/\s+/);
-       next if $now - $sessexpire > $time;
-       if ($username eq $user && !$founduser) {
-               if ($sess eq $sessionid) {
-                       $founduser = 1;
-                       next;
-               }
-       }
-       print NEWSESS "$user $sess $time $pkgnum $svcdomain $domname\n";
-  }
-  close(SESSFILE);
-  close(NEWSESS);
-  system("mv $tmpdir/adminsess.$$ $sessionfile");
-  error('sess_expired') unless $founduser;
-
-  $printheader = 0;
-
-  return 0;
-
-}
-
-# checks the username and pass against the database
-sub checkuserpass {
-
-  my $username = shift;
-  my $password = shift;
-
-  my $error = authenticate ( {
-      'authuser'         => $username,
-      '_password'        => $password,
-    } ); 
-
-  if ($error eq "$username OK") {
-    return 1;
-  }else{
-    return 0;
-  }
-
-}
-
-#printlogin prints a login page
-sub printlogin {
-
-        print "<html>$body\n";
-        print "<center><font size=4>Please login to access MAIL ADMIN</font></center>\n";
-        print "<form action=\"$cgi\" method=post>\n";
-        print "<center>Email Address: &nbsp; <input type=text name=\"username\">\n";
-        print "<br>Email Password: <input type=password name=\"password\">\n";
-        print "<br><input type=submit name=\"login\" value=\"Login\">\n";
-        print "</form></center>\n";
-        print "</body></html>\n";
-}
-
-
-#select_package chooses a administrable package if more than one exists
-sub select_package {
-        my $user = shift;
-        my $packages = list_packages($user);
-        if (scalar(@{$packages}) eq 1) {
-          $current_package = @{$packages}[0]->{'pkgnum'};
-          set_package();
-        }
-        if (scalar(@{$packages}) > 1) {
-#          print $query->redirect("$cgi\?action=list_packages");
-           print "<p>No package selected.  You must first <a href=\"$cgi\?action=list_packages\" target=\"rightmainframe\">select a package</a>.\n";
-          exit;
-        }
-}
-
-sub set_package {
-
-  my $username = $query->cookie(-name=>'username');
-  my $sessionid = $query->cookie(-name=>'ma_sessionid');
-
-  if ($sessionid eq '') {
-     printheader();
-     if ($query->param()) {
-        error('sess_expired');
-     } else {
-        printlogin();
-        exit;
-    }
-  }
-
-  my $now = time();
-  my $founduser = 0;
-  open(SESSFILE, "$sessionfile") || error('open');
-  error('open') if -l "$tmpdir/adminsess.$$";
-  open(NEWSESS, ">$tmpdir/adminsess.$$") || error('open');
-  while (<SESSFILE>) {
-       chomp();
-       my ($user, $sess, $time, $pkgnum, $svcdomain, $domname) = split(/\s+/);
-       next if $now - $sessexpire > $time;
-       if ($username eq $user && !$founduser) {
-               if ($sess eq $sessionid) {
-                       $founduser = 1;
-                       print NEWSESS "$user $sess $time $current_package $current_account $current_domname\n";
-                       next;
-               }
-       }
-       print NEWSESS "$user $sess $time $pkgnum $svcdomain $domname\n";
-  }
-  close(SESSFILE);
-  close(NEWSESS);
-  system("mv $tmpdir/adminsess.$$ $sessionfile");
-  error('sess_expired') unless $founduser;
-
-  $printheader = 0;
-
-  return 0;
-
-}
-
diff --git a/fs_selfadmin/FS-MailAdminServer/fs_mailadmind b/fs_selfadmin/FS-MailAdminServer/fs_mailadmind
deleted file mode 100755 (executable)
index 746d782..0000000
+++ /dev/null
@@ -1,366 +0,0 @@
-#!/usr/bin/perl -Tw
-
-eval 'exec /usr/bin/perl -Tw -S $0 ${1+"$@"}'
-    if 0; # not running under some shell
-#
-# fs_mailadmind
-#
-# This is run REMOTELY over ssh by fs_mailadmin_server.
-#
-
-use strict;
-use Socket;
-
-use vars qw( $Debug );
-
-$Debug = 0;
-
-my($fs_mailadmind_socket)="/usr/local/freeside/fs_mailadmind_socket";
-
-$ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin';
-$ENV{'SHELL'} = '/bin/sh';
-$ENV{'IFS'} = " \t\n";
-$ENV{'CDPATH'} = '';
-$ENV{'ENV'} = '';
-$ENV{'BASH_ENV'} = '';
-
-$|=1;
-
-warn "[fs_mailadmind] Reading locales...\n" if $Debug;
-chomp( my $n_cust_main_county = <STDIN> );
-my @cust_main_county = map {
-  chomp( my $taxnum = <STDIN> );
-  chomp( my $state = <STDIN> );
-  chomp( my $county = <STDIN> );
-  chomp( my $country = <STDIN> );
-  {
-    'taxnum'  => $taxnum,
-    'state'   => $state,
-    'county'  => $county,
-    'country' => $country,
-  };
-} ( 1 .. $n_cust_main_county );
-
-warn "[fs_mailadmind] Reading package definitions...\n" if $Debug;
-chomp( my $n_part_pkg = <STDIN> );
-my @part_pkg = map {
-  chomp( my $pkgpart = <STDIN> );
-  chomp( my $pkg = <STDIN> );
-  {
-    'pkgpart' => $pkgpart,
-    'pkg'     => $pkg,
-  };
-} ( 1 .. $n_part_pkg );
-
-warn "[fs_mailadmind] Reading POPs...\n" if $Debug;
-chomp( my $n_svc_acct_pop = <STDIN> );
-my @svc_acct_pop = map {
-  chomp( my $popnum = <STDIN> );
-  chomp( my $city = <STDIN> );
-  chomp( my $state = <STDIN> );
-  chomp( my $ac = <STDIN> );
-  chomp( my $exch = <STDIN> );
-  chomp( my $loc = <STDIN> );
-  {
-    'popnum' => $popnum,
-    'city'   => $city,
-    'state'  => $state,
-    'ac'     => $ac,
-    'exch'   => $exch,
-    'loc'    => $loc,
-  };
-} ( 1 .. $n_svc_acct_pop );
-
-warn "[fs_mailadmind] Creating $fs_mailadmind_socket\n" if $Debug;
-my $uaddr = sockaddr_un($fs_mailadmind_socket);
-my $proto = getprotobyname('tcp');
-socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!";
-unlink($fs_mailadmind_socket);
-bind(Server, $uaddr) or die "bind: $!";
-listen(Server,SOMAXCONN) or die "listen: $!";
-
-warn "[fs_mailadmind] Entering main loop...\n" if $Debug;
-my $paddr;
-for ( ; $paddr = accept(Client,Server); close Client) {
-
-  chop( my $command = <Client> );
-
-  if ( $command eq "signup_info" ) {
-    warn "[fs_mailadmind] sending signup info...\n" if $Debug; 
-    print Client join("\n", $n_cust_main_county,
-      map {
-        $_->{taxnum},
-        $_->{state},
-        $_->{county},
-        $_->{country},
-      } @cust_main_county
-    ), "\n";
-
-    print Client join("\n", $n_part_pkg,
-      map {
-        $_->{pkgpart},
-        $_->{pkg},
-      } @part_pkg
-    ), "\n";
-
-    print Client join("\n", $n_svc_acct_pop,
-      map {
-        $_->{popnum},
-        $_->{city},
-        $_->{state},
-        $_->{ac},
-        $_->{exch},
-        $_->{loc},
-      } @svc_acct_pop
-    ), "\n";
-
-  } elsif ( $command eq "new_customer" ) {
-    warn "[fs_mailadmind] reading customer signup...\n" if $Debug;
-    my(
-      $first, $last, $ss, $company, $address1, $address2, $city, $county,
-      $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo,
-      $paydate, $payname, $invoicing_list, $pkgpart, $username, $password,
-      $popnum,
-    ) = map { scalar(<Client>) } ( 1 .. 23 );
-
-    warn "[fs_mailadmind] sending customer data to remote server...\n" if $Debug;
-    print 
-      $first, $last, $ss, $company, $address1, $address2, $city, $county,
-      $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo,
-      $paydate, $payname, $invoicing_list, $pkgpart, $username, $password,
-      $popnum,
-    ;
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-
-  } elsif ( $command eq "authenticate" ) {
-    warn "[fs_mailadmind] reading user information to auth...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading authentication material...\n" if $Debug;
-    chop( my $password = <Client> );
-    warn "[fs_mailadmind] sending information to remote server...\n" if $Debug;
-    print "authenticate\n", $user, "\n", $password, "\n";
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-    
-  } elsif ( $command eq "list_packages" ) {
-    warn "[fs_mailadmind] reading user information to list_packages...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug;
-    print "list_packages\n", $user, "\n";
-
-    warn "[fs_mailadmind] reading data from remote server...\n" if $Debug;
-    chomp( my $n_packages = <STDIN> );
-    my @packages = map {
-      chomp( my $pkgnum  = <STDIN> );
-      chomp( my $domain  = <STDIN> );
-      chomp( my $account = <STDIN> );
-      {
-        'pkgnum'  => $pkgnum,
-        'domain'  => $domain,
-        'account' => $account,
-      };
-    } ( 1 .. $n_packages );
-
-    warn "[fs_mailadmind] sending data to local client...\n" if $Debug;
-
-    print Client join("\n", $n_packages,
-      map {
-        $_->{pkgnum},
-        $_->{domain},
-        $_->{account},
-      } @packages
-    ), "\n";
-
-  } elsif ( $command eq "list_mailboxes" ) {
-    warn "[fs_mailadmind] reading user information to list_mailboxes...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading package number to list_mailboxes...\n" if $Debug;
-    chop( my $package = <Client> );
-    warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug;
-    print "list_mailboxes\n", $user, "\n", $package, "\n";
-
-    warn "[fs_mailadmind] reading data from remote server...\n" if $Debug;
-    chomp( my $n_svc_acct = <STDIN> );
-    my @svc_acct = map {
-      chomp( my $svcnum = <STDIN> );
-      chomp( my $username = <STDIN> );
-      chomp( my $_password = <STDIN> );
-      {
-        'svcnum' => $svcnum,
-        'username' => $username,
-        '_password'     => $_password,
-      };
-    } ( 1 .. $n_svc_acct );
-
-    warn "[fs_mailadmind] sending data to local client...\n" if $Debug;
-
-    print Client join("\n", $n_svc_acct,
-      map {
-        $_->{svcnum},
-        $_->{username},
-        $_->{_password},
-      } @svc_acct
-    ), "\n";
-
-  } elsif ( $command eq "delete_mailbox" ) {
-    warn "[fs_mailadmind] reading user information to auth...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading account information to delete...\n" if $Debug;
-    chop( my $account = <Client> );
-    warn "[fs_mailadmind] sending information to remote server...\n" if $Debug;
-    print "delete_mailbox\n", $user, "\n", $account, "\n";
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-
-  } elsif ( $command eq "password_mailbox" ) {
-    warn "[fs_mailadmind] reading user information to auth...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading account information to password...\n" if $Debug;
-    my(
-      $account, $_password,
-    ) = map { scalar(<Client>) } ( 1 .. 2 );
-
-    warn "[fs_mailadmind] sending password data to remote server...\n" if $Debug;
-    print "password_mailbox", "\n";
-    print 
-      $user, "\n", $account, $_password,
-    ;
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-
-  } elsif ( $command eq "add_mailbox" ) {
-    warn "[fs_mailadmind] reading user information to auth...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading account information to create...\n" if $Debug;
-    my(
-      $package, $account, $_password,
-    ) = map { scalar(<Client>) } ( 1 .. 3 );
-
-    warn "[fs_mailadmind] sending service data to remote server...\n" if $Debug;
-    print "add_mailbox", "\n";
-    print 
-      $user, "\n", $package, $account, $_password,
-    ;
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-
-  } elsif ( $command eq "add_forward" ) {
-    warn "[fs_mailadmind] reading user information to auth...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading forward information to create...\n" if $Debug;
-    my(
-      $package, $source, $dest,
-    ) = map { scalar(<Client>) } ( 1 .. 3 );
-
-    warn "[fs_mailadmind] sending service data to remote server...\n" if $Debug;
-    print "add_forward", "\n";
-    print 
-      $user, "\n", $package, $source, $dest,
-    ;
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-
-  } elsif ( $command eq "delete_forward" ) {
-    warn "[fs_mailadmind] reading user information to auth...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading forward information to delete...\n" if $Debug;
-    chop( my $service = <Client> );
-    warn "[fs_mailadmind] sending information to remote server...\n" if $Debug;
-    print "delete_forward\n", $user, "\n", $service, "\n";
-
-    warn "[fs_mailadmind] reading error from remote server...\n" if $Debug;
-    my $error = <STDIN>;
-
-    warn "[fs_mailadmind] sending error to local client...\n" if $Debug;
-    print Client $error;
-
-  } elsif ( $command eq "list_forwards" ) {
-    warn "[fs_mailadmind] reading user information to list_forwards...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading service number to list_forwards...\n" if $Debug;
-    chop( my $service = <Client> );
-    warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug;
-    print "list_forwards\n", $user, "\n", $service, "\n";
-
-    warn "[fs_mailadmind] reading data from remote server...\n" if $Debug;
-    chomp( my $n_svc_forward = <STDIN> );
-    my @svc_forward = map {
-      chomp( my $svcnum = <STDIN> );
-      chomp( my $dest = <STDIN> );
-      {
-        'svcnum' => $svcnum,
-        'dest' => $dest,
-      };
-    } ( 1 .. $n_svc_forward );
-
-    warn "[fs_mailadmind] sending data to local client...\n" if $Debug;
-
-    print Client join("\n", $n_svc_forward,
-      map {
-        $_->{svcnum},
-        $_->{dest},
-      } @svc_forward
-    ), "\n";
-
-  } elsif ( $command eq "list_pkg_forwards" ) {
-    warn "[fs_mailadmind] reading user information to list_pkg_forwards...\n" if $Debug;
-    chop( my $user = <Client> );
-    warn "[fs_mailadmind] reading service number to list_forwards...\n" if $Debug;
-    chop( my $package = <Client> );
-    warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug;
-    print "list_pkg_forwards\n", $user, "\n", $package, "\n";
-
-    warn "[fs_mailadmind] reading data from remote server...\n" if $Debug;
-    chomp( my $n_svc_forward = <STDIN> );
-    my @svc_forward = map {
-      chomp( my $svcnum = <STDIN> );
-      chomp( my $srcsvc = <STDIN> );
-      chomp( my $dest = <STDIN> );
-      {
-        'svcnum' => $svcnum,
-        'srcsvc' => $srcsvc,
-        'dest' => $dest,
-      };
-    } ( 1 .. $n_svc_forward );
-
-    warn "[fs_mailadmind] sending data to local client...\n" if $Debug;
-
-    print Client join("\n", $n_svc_forward,
-      map {
-        $_->{svcnum},
-        $_->{srcsvc},
-        $_->{dest},
-      } @svc_forward
-    ), "\n";
-
-  } else {
-    die "unexpected command from client: $command";
-  }
-
-}
-
diff --git a/fs_selfadmin/README b/fs_selfadmin/README
deleted file mode 100644 (file)
index d9857f0..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-
-This collection of files implements a 'self-administered mail service.'
-Configuration is similar to fs_signupd
-
-Additionally you will need to modify the database:
-
-CREATE TABLE svc_acct_admin (
-  svcnum int primary key,
-  adminsvc int not null
-);
-
-creating both as keys might be good
-
-(and perform the dbdef-create)
-
-
-As it exists now, a package containing one svc_domain, at least one
-svc_acct_admin, and other services can have its svc_acct's and svc_forward's
-manipulated by the svc_acct referenced by a svc_acct_admin in the package.
-
-One svc_acct may be referenced as svc_acct_admin for multiple packages.
-
-fs_mailadmin_server contains hard coded references to service numbers which
-will require editing for your system.
-
-It's not a lot, but it might provide inspiration.
-
diff --git a/fs_selfadmin/fs_mailadmin_server b/fs_selfadmin/fs_mailadmin_server
deleted file mode 100755 (executable)
index ef47885..0000000
+++ /dev/null
@@ -1,642 +0,0 @@
-#!/usr/bin/perl -Tw
-#
-# fs_mailadmin_server
-#
-
-use strict;
-use IO::Handle;
-use FS::SSH qw(sshopen2);
-use FS::UID qw(adminsuidsetup);
-use FS::Conf;
-use FS::Record qw( qsearch qsearchs );
-use FS::cust_main_county;
-use FS::cust_main;
-use FS::svc_acct_admin;
-
-use vars qw( $opt $Debug $conf $default_domain );
-
-$Debug = 1;
-
-#my @payby = qw(CARD PREPAY);
-
-my $user = shift or die &usage;
-&adminsuidsetup( $user ); 
-
-$conf = new FS::Conf;
-$default_domain = $conf->config('domain');
-
-my $machine = shift or die &usage;
-
-my $agentnum = shift or die &usage;
-my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } ) or die &usage;
-my $pkgpart = $agent->pkgpart_hashref;
-
-my $refnum = shift or die &usage;
-
-#causing trouble for some folks
-#$SIG{CHLD} = sub { wait() };
-
-my($fs_mailadmind)=$conf->config('fs_mailadmind');
-
-while (1) {
-  my($reader,$writer)=(new IO::Handle, new IO::Handle);
-  $writer->autoflush(1);
-  warn "[fs_mailadmin_server] Connecting to $machine...\n" if $Debug;
-  sshopen2($machine,$reader,$writer,$fs_mailadmind);
-
-  my $data;
-
-  warn "[fs_mailadmin_server] Sending locales...\n" if $Debug;
-  my @cust_main_county = qsearch('cust_main_county', {} );
-  print $writer $data = join("\n",
-    ( scalar(@cust_main_county) || die "no tax rates (cust_main_county records)" ),
-    map {
-      $_->taxnum,
-      $_->state,
-      $_->county,
-      $_->country,
-    } @cust_main_county
-  ),"\n";
-  warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-  warn "[fs_mailadmin_server] Sending package definitions...\n" if $Debug;
-  my @part_pkg = grep { $_->svcpart('svc_acct') && $pkgpart->{ $_->pkgpart } }
-    qsearch( 'part_pkg', {} );
-  print $writer $data = join("\n",
-    ( scalar(@part_pkg) || die "no usable package definitions, agent $agentnum" ),
-    map {
-      $_->pkgpart,
-      $_->pkg,
-    } @part_pkg
-  ), "\n";
-  warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-  warn "[fs_mailadmin_server] Sending POPs...\n" if $Debug;
-  my @svc_acct_pop = qsearch ('svc_acct_pop',{} );
-  print $writer $data = join("\n",
-    ( scalar(@svc_acct_pop) || die "No points of presence (svc_acct_pop records)" ),
-    map {
-      $_->popnum,
-      $_->city,
-      $_->state,
-      $_->ac,
-      $_->exch,
-      $_->loc,
-    } @svc_acct_pop
-  ), "\n";
-  warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-  warn "[fs_mailadmin_server] Entering main loop...\n" if $Debug;
-COMMAND:  while (1) {
-    warn "[fs_mailadmin_server] Reading (waiting for) command...\n" if $Debug;
-    chop( my($command, $user) = map { scalar(<$reader>) } ( 1 .. 2 ) );
-    my $domain = $default_domain;
-    $user =~ /^([\w\.\-]+)\@(([\w\-]+\.)+\w+)$/;
-    ($user, $domain) = ($1, $2);
-
-    if ($command eq 'authenticate'){
-      warn "[fs_mailadmin_server] Processing authenticate command for $user \n" if $Debug;
-      chop( my($password) = map { scalar(<$reader>) } ( 1 .. 1 ) );
-
-      my $error = '';
-
-      my @svc_domain = qsearchs('svc_domain', { 'domain'   => $domain });
-
-      if (scalar(@svc_domain) != 1) {
-        warn "Nonexistant or duplicate service account for \"$domain\"";
-        next COMMAND;
-      }
-
-      my @svc_acct = qsearchs('svc_acct', { 'username' => $user,
-                                            'domsvc'   => $svc_domain[0]->svcnum });
-      if (scalar(@svc_acct) != 1) {
-        die "Nonexistant or duplicate service account for \"$user\"";
-        next COMMAND;
-      }
-
-      if ($svc_acct[0]->_password eq $password) {
-        $error = "$user\@$domain OK";
-      }else{
-        $error = "$user\@$domain FAILED";
-      }
-      warn "[fs_mailadmin_server] Sending results...\n" if $Debug;
-      print $writer $error, "\n";
-    }
-    elsif ($command eq 'list_packages'){
-      warn "[fs_mailadmin_server] Processing list_packages command for $user \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval {find_administrable_packages( $user, $domain )};
-      warn "$@" if $@; 
-
-      my %packages;
-      my %accounts;
-
-      foreach my $package (@packages) {
-        $packages{my $pkgnum = $package->getfield('pkgnum')} = $default_domain;
-        $accounts{$pkgnum} = 0;
-        my @services = qsearch('cust_svc', { 'pkgnum' => $pkgnum });
-        foreach my $service (@services) {
-          if ($service->getfield('svcpart') eq '4'){
-            my $account=qsearchs('svc_domain', { 'svcnum' => $service->getfield('svcnum') });
-            $packages{$pkgnum}=$account->getfield('domain');
-            $accounts{$pkgnum}=$account->getfield('svcnum');
-          }
-        }
-      }
-      
-      print $writer $data = join("\n",
-        ( scalar(keys(%packages)) ),
-        map {
-          $_,
-          $packages{$_},
-          $accounts{$_},
-        } keys(%packages)
-      ), "\n";
-      warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-    }elsif ($command eq 'list_mailboxes'){
-
-      warn "[fs_mailadmin_server] Processing list_mailboxes command for $user" if $Debug;
-      chop( my($pkgnum) = map { scalar(<$reader>) } ( 1 .. 1 ) );
-      warn "package $pkgnum \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval {find_administrable_packages( $user, $domain )};
-      warn "$@" if $@; 
-
-      my @accounts;
-
-      foreach my $package (@packages) {
-        next unless ($pkgnum eq $package->getfield('pkgnum'));
-        my @services = qsearch('cust_svc', { 'pkgnum' => $package->getfield('pkgnum') });
-        foreach my $service (@services) {
-          if ($service->getfield('svcpart') eq '2'){
-            my $account=qsearchs('svc_acct', { 'svcnum' => $service->getfield('svcnum') });
-#           $accounts[$#accounts+1]=$account->getfield('username');
-            $accounts[$#accounts+1]=$account;
-          }
-        }
-      }
-      
-      print $writer $data = join("\n",
-#        ( scalar(@accounts) || die "No accounts (svc_acct records)" ),
-        ( scalar(@accounts) ),
-        map {
-          $_->svcnum,
-#          $_->username,
-          $_->email,
-#          $_->_password,
-          '*****',
-        } @accounts
-      ), "\n";
-      warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-      
-    } elsif ($command eq 'delete_mailbox'){
-      warn "[fs_mailadmin_server] Processing delete_mailbox command for $user " if $Debug;
-      chop( my($account) = map { scalar(<$reader>) } ( 1 .. 1 ) );
-      warn "account $account \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval { find_administrable_packages($user, $domain) };
-      warn "$@" if $@; 
-      $error ||= "$@" if $@; 
-
-      my @svc_acct = qsearchs('svc_acct', { 'svcnum' => $account }) unless $error;
-      if (scalar(@svc_acct) != 1) { $error ||= 'Nonexistant or duplicate service account for user.' };
-      if (! $error && check_administrator(\@packages, $svc_acct[0])){
-# not sure about the next three lines... do we delete? or return error
-        foreach my $svc_forward (qsearch('svc_forward', { 'dstsvc' => $svc_acct[0]->getfield('svcnum') })) {
-          $error ||= $svc_forward->delete;
-        }
-        foreach my $svc_forward (qsearch('svc_forward', { 'srcsvc' => $svc_acct[0]->getfield('svcnum') })) {
-          $error ||= $svc_forward->delete;
-        }
-        $error ||= $svc_acct[0]->delete;
-      } else {
-        $error ||= "Illegal attempt to remove service";
-      }
-
-      
-      warn "[fs_mailadmin_server] Sending results...\n" if $Debug;
-      print $writer $error, "\n";
-      
-    } elsif ($command eq 'password_mailbox'){
-      warn "[fs_mailadmin_server] Processing password_mailbox command for $user " if $Debug;
-      chop( my($account, $_password) = map { scalar(<$reader>) } ( 1 .. 2 ) );
-      warn "account $account with password $_password \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval { find_administrable_packages($user, $domain) };
-      warn "$@" if $@; 
-      $error ||= "$@" if $@; 
-
-      my @svc_acct = qsearchs('svc_acct', { 'svcnum' => $account }) unless $error;
-      if (scalar(@svc_acct) != 1) { $error ||= 'Nonexistant or duplicate service account.' };
-
-      if (! $error && check_administrator(\@packages, $svc_acct[0])){
-        my $new = new FS::svc_acct ({$svc_acct[0]->hash});
-        $new->setfield('_password' => $_password);
-        $error ||= $new->replace($svc_acct[0]);
-      } else {
-        $error ||= "Illegal attempt to change password";
-      }
-
-      
-      warn "[fs_mailadmin_server] Sending results...\n" if $Debug;
-      print $writer $error, "\n";
-      
-    } elsif ($command eq 'add_mailbox'){
-      warn "[fs_mailadmin_server] Processing add_mailbox command for $user " if $Debug;
-      chop( my($target_package, $account, $_password) = map { scalar(<$reader>) } ( 1 .. 3 ) );
-      warn "in package $target_package account $account with password $_password \n" if $Debug;
-
-      my $found_package;
-      my $domainsvc=0;
-      my $svcpart=2;    # this is 'email box'
-      my $svcpartsm=3;  # this is 'domain alias'
-      my $error = '';
-      my $found = 0;
-
-      my @packages = eval { find_administrable_packages($user, $domain) };
-      warn "$@" if $@; 
-      $error ||= "$@" if $@; 
-
-      foreach my $package (@packages) {
-        if ($package->getfield('pkgnum') eq $target_package) {
-          $found = 1;
-          $found_package=$package;
-          my @services = qsearch('cust_svc', { 'pkgnum' => $target_package });
-          foreach my $service (@services) {
-            if ($service->getfield('svcpart') eq '4'){
-              my @svc_domain=qsearchs('svc_domain', { 'svcnum' => $service->getfield('svcnum') });
-              if (scalar(@svc_domain) eq 1) {
-                $domainsvc=$svc_domain[0]->getfield('svcnum');
-              }
-            }
-          }
-          last;
-        }
-      }
-      warn "User $user does not have administration rights to package $target_package\n" unless $found;
-      $error ||= "User $user does not have administration rights to package $target_package\n" unless $found;
-
-      my $part_pkg = qsearchs('part_pkg',{'pkgpart'=>$found_package->getfield('pkgpart')});
-
-      #list of services this pkgpart includes (although at the moment we only care
-      #  about $svcpart
-      my $pkg_svc;
-      my %pkg_svc = ();
-      foreach $pkg_svc ( qsearch('pkg_svc',{'pkgpart'=> $found_package->pkgpart }) ) {
-        $pkg_svc{$pkg_svc->svcpart} = $pkg_svc->quantity if $pkg_svc->quantity;
-      }
-
-      my @services = qsearch('cust_svc', {'pkgnum'  => $found_package->getfield('pkgnum'),
-                                          'svcpart' => $svcpart,
-                                         });
-
-      if (scalar(@services) >= $pkg_svc{$svcpart}) {
-        $error="Maximum allowed already reached.";
-      }
-      
-      my $svc_acct = new FS::svc_acct ( {
-        'pkgnum'    => $found_package->pkgnum,
-        'svcpart'   => $svcpart,
-        'username'  => $account,
-        'domsvc'    => $domainsvc,
-        '_password' => $_password,
-      } );
-
-      my $y = $svc_acct->setdefault; # arguably should be in new method
-      $error ||= $y unless ref($y);
-      #and just in case you were silly
-      $svc_acct->pkgnum($found_package->pkgnum);
-      $svc_acct->svcpart($svcpart);
-      $svc_acct->username($account);
-      $svc_acct->domsvc($domainsvc);
-      $svc_acct->_password($_password);
-
-      $error ||= $svc_acct->check;
-
-      if ( ! $error ) { #in this case, $cust_pkg should always
-                                     #be definied, but....
-        $error ||= $svc_acct->insert;
-        warn "WARNING: $error on pre-checked svc_acct record!" if $error;
-      }
-
-      warn "[fs_mailadmin_server] Sending results...\n" if $Debug;
-      print $writer $error, "\n";
-      
-    }elsif ($command eq 'list_forwards'){
-
-      warn "[fs_mailadmin_server] Processing list_forwards command for $user" if $Debug;
-      chop( my($svcnum) = map { scalar(<$reader>) } ( 1 .. 1 ) );
-      warn "service $svcnum \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval {find_administrable_packages( $user, $domain )};
-      warn "$@" if $@; 
-
-      my @forwards;
-
-      foreach my $package (@packages) {
-#        next unless ($pkgnum eq $package->getfield('pkgnum'));
-        my @services = qsearch('cust_svc', { 'pkgnum' => $package->getfield('pkgnum') });
-        foreach my $service (@services) {
-          if ($service->getfield('svcpart') eq '10'){
-            my $forward=qsearchs('svc_forward', { 'svcnum' => $service->getfield('svcnum') });
-            $forwards[$#forwards+1]=$forward if ($forward->getfield('srcsvc') == $svcnum);
-          }
-        }
-      }
-      
-      print $writer $data = join("\n",
-        ( scalar(@forwards) ),
-        map {
-          $_->svcnum,
-          ($_->dstsvc ? qsearchs('svc_acct', {'svcnum' => $_->dstsvc})->email : $_->dst),
-        } @forwards
-      ), "\n";
-      warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-      
-    }elsif ($command eq 'list_pkg_forwards'){
-
-      warn "[fs_mailadmin_server] Processing list_pkg_forwards command for $user" if $Debug;
-      chop( my($pkgnum) = map { scalar(<$reader>) } ( 1 .. 1 ) );
-      warn "package $pkgnum \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval {find_administrable_packages( $user, $domain )};
-      warn "$@" if $@; 
-
-      my @forwards;
-
-      foreach my $package (@packages) {
-        next unless ($pkgnum eq $package->getfield('pkgnum'));
-        my @services = qsearch('cust_svc', { 'pkgnum' => $package->getfield('pkgnum') });
-        foreach my $service (@services) {
-          if ($service->getfield('svcpart') eq '10'){
-            my $forward=qsearchs('svc_forward', { 'svcnum' => $service->getfield('svcnum') });
-            $forwards[$#forwards+1]=$forward;
-          }
-        }
-      }
-      
-      print $writer $data = join("\n",
-        ( scalar(@forwards) ),
-        map {
-          $_->svcnum,
-          $_->srcsvc,
-          ($_->dstsvc ? qsearchs('svc_acct', {'svcnum' => $_->dstsvc})->email : $_->dst),
-        } @forwards
-      ), "\n";
-      warn "[fs_mailadmin_server] $data\n" if $Debug > 2;
-
-      
-    } elsif ($command eq 'delete_forward'){
-      warn "[fs_mailadmin_server] Processing delete_forward command for $user " if $Debug;
-      chop( my($forward) = map { scalar(<$reader>) } ( 1 .. 1 ) );
-      warn "forward $forward \n" if $Debug;
-
-      my $error = '';
-
-      my @packages = eval { find_administrable_packages($user, $domain) };
-      warn "$@" if $@; 
-      $error ||= "$@" if $@; 
-
-      my @svc_forward = qsearchs('svc_forward', { 'svcnum' => $forward }) unless $error;
-      if (scalar(@svc_forward) != 1) { $error ||= 'Nonexistant or duplicate service account for user.' };
-      if (! $error && check_administrator(\@packages, $svc_forward[0])){
-# not sure about the next three lines... do we delete? or return error
-        $error ||= $svc_forward[0]->delete;
-      } else {
-        $error ||= "Illegal attempt to remove service";
-      }
-
-      
-      warn "[fs_mailadmin_server] Sending results...\n" if $Debug;
-      print $writer $error, "\n";
-      
-    } elsif ($command eq 'add_forward'){
-      warn "[fs_mailadmin_server] Processing add_forward command for $user " if $Debug;
-      chop( my($target_package, $source, $dest) = map { scalar(<$reader>) } ( 1 .. 3 ) );
-      warn "in package $target_package source $source with destination $dest \n" if $Debug;
-
-      my $found_package;
-      my $domainsvc=0;
-      my $svcpart=10;   # this is 'forward service'
-      my $error = '';
-      my $found = 0;
-
-      my @packages = eval { find_administrable_packages($user, $domain) };
-      warn "$@" if $@; 
-      $error ||= "$@" if $@; 
-
-      foreach my $package (@packages) {
-        if ($package->getfield('pkgnum') eq $target_package) {
-          $found = 1;
-          $found_package=$package;
-          last;
-        }
-      }
-      warn "User $user does not have administration rights to package $target_package\n" unless $found;
-      $error ||= "User $user does not have administration rights to package $target_package\n" unless $found;
-
-      my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $source });
-      warn "Forwarding source $source does not exist.\n" unless $svc_acct;
-      $error ||= "Forwarding source $source does not exist.\n" unless $svc_acct;
-
-      my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $source });
-      warn "Forwarding source $source not attached to any account.\n" unless $cust_svc;
-      $error ||= "Forwarding source $source not attached to any account.\n" unless $cust_svc;
-
-      if ( ! $error ) {
-        warn "Forwarding source $source is not in package $target_package\n"
-          unless ($cust_svc->getfield('pkgnum') == $target_package);
-        $error ||= "Forwarding source $source is not in package $target_package\n"
-          unless ($cust_svc->getfield('pkgnum') == $target_package);
-      }
-
-      my $part_pkg = qsearchs('part_pkg',{'pkgpart'=>$found_package->getfield('pkgpart')});
-
-      #list of services this pkgpart includes (although at the moment we only care
-      #  about $svcpart
-      my $pkg_svc;
-      my %pkg_svc = ();
-      foreach $pkg_svc ( qsearch('pkg_svc',{'pkgpart'=> $found_package->pkgpart }) ) {
-        $pkg_svc{$pkg_svc->svcpart} = $pkg_svc->quantity if $pkg_svc->quantity;
-      }
-
-      my @services = qsearch('cust_svc', {'pkgnum'  => $found_package->getfield('pkgnum'),
-                                          'svcpart' => $svcpart,
-                                         });
-
-      if (scalar(@services) >= $pkg_svc{$svcpart}) {
-        $error="Maximum allowed already reached.";
-      }
-      
-      my $svc_forward = new FS::svc_forward ( {
-        'pkgnum'    => $found_package->pkgnum,
-        'svcpart'   => $svcpart,
-        'srcsvc'  => $source,
-        'dstsvc'    => 0,
-        'dst' => $dest,
-      } );
-
-      my $y = $svc_forward->setdefault; # arguably should be in new method
-      $error ||= $y unless ref($y);
-      #and just in case you were silly
-      $svc_forward->pkgnum($found_package->pkgnum);
-      $svc_forward->svcpart($svcpart);
-      $svc_forward->srcsvc($source);
-      $svc_forward->dstsvc(0);
-      $svc_forward->dst($dest);
-
-      $error ||= $svc_forward->check;
-
-      if ( ! $error ) { #in this case, $cust_pkg should always
-                                     #be definied, but....
-        $error ||= $svc_forward->insert;
-        warn "WARNING: $error on pre-checked svc_forward record!" if $error;
-      }
-
-      warn "[fs_mailadmin_server] Sending results...\n" if $Debug;
-      print $writer $error, "\n";
-      
-    } else {
-      warn "[fs_mailadmin_server] Bad command: $command \n" if $Debug;
-      print $writer "Bad command \n";
-    }
-  }
-  close $writer;
-  close $reader;
-  warn "connection to $machine lost!  waiting 60 seconds...\n";
-  sleep 60;
-  warn "reconnecting...\n";
-}
-
-sub usage {
-  die "Usage:\n\n  fs_mailadmin_server user machine agentnum refnum\n";
-}
-
-#sub find_administrable_packages {
-#      my $user = shift;
-#
-#      my $error = '';
-#
-#      my @svc_acct = qsearchs('svc_acct', { 'username' => $user });
-#      if (scalar(@svc_acct) != 1) {
-#        die "Nonexistant or duplicate service account for \"$user\"";
-#      }
-#
-#      my @cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct[0]->getfield('svcnum') });
-#      if (scalar(@cust_svc) != 1 ) {
-#        die "Nonexistant or duplicate customer service for \"$user\"";
-#      }
-#
-#      my @cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc[0]->getfield('pkgnum') });
-#      if (scalar(@cust_pkg) != 1) {
-#        die "Nonexistant or duplicate customer package for \"$user\"";
-#      }
-#
-#      my @cust_main = qsearchs('cust_main', { 'custnum' => $cust_pkg[0]->getfield('custnum') });
-#      if (scalar(@cust_main) != 1 ) {
-#        die "Nonexistant or duplicate customer for \"$user\"";
-#      }
-#
-#      my @packages = $cust_main[0]->ncancelled_pkgs;
-#}
-
-sub find_administrable_packages {
-      my $user = shift;
-      my $domain = shift;
-
-      my @packages;
-      my $error = '';
-
-      my @svc_domain = qsearchs('svc_domain', { 'domain'   => $domain });
-
-      if (scalar(@svc_domain) != 1) {
-        die "Nonexistant or duplicate service account for \"$domain\"";
-      }
-
-      my @svc_acct = qsearchs('svc_acct', { 'username' => $user,
-                                            'domsvc'   => $svc_domain[0]->svcnum });
-      if (scalar(@svc_acct) != 1) {
-        die "Nonexistant or duplicate service account for \"$user\"";
-      }
-
-      my @svc_acct_admin = qsearch('svc_acct_admin', {'adminsvc' => $svc_acct[0]->getfield('svcnum') });
-      die "Nonexistant or duplicate customer service for \"$user\"" unless scalar(@svc_acct_admin);
-
-      foreach my $svc_acct_admin (@svc_acct_admin) {
-        my @cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct_admin->getfield('svcnum') });
-        if (scalar(@cust_svc) != 1 ) {
-          die "Nonexistant or duplicate customer service for admin \"$svc_acct_admin->getfield('svcnum')\"";
-        }
-
-        my @cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc[0]->getfield('pkgnum') });
-        if (scalar(@cust_pkg) != 1) {
-          die "Nonexistant or duplicate customer package for admin \"$user\"";
-        }
-
-        push @packages, $cust_pkg[0] unless $cust_pkg[0]->getfield('cancel');
-
-      }
-      (@packages);
-}
-
-sub check_administrator {
-      my ($allowed_packages_aref, $svc_acct_ref) = @_;
-
-      my $error = '';
-      my $found = 0;
-
-      {
-        my @cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct_ref->getfield('svcnum') });
-        if (scalar(@cust_svc) != 1 ) {
-          warn "Nonexistant or duplicate customer service for \"$svc_acct_ref->getfield('username')\"";
-          last;
-        }
-
-        my @cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc[0]->getfield('pkgnum') });
-        if (scalar(@cust_pkg) != 1) {
-          warn "Nonexistant or duplicate customer package for \"$svc_acct_ref->getfield('username')\"";
-          last;
-        }
-
-        foreach my $package (@$allowed_packages_aref) {
-          if ($package->getfield('pkgnum') eq $cust_pkg[0]->getfield('pkgnum')) {
-            $found = 1;
-            last;
-          }
-        }
-      }
-
-      $found;
-}
-
-sub check_add {
-      my ($allowed_packages_aref, $target_package) = @_;
-
-      my $error = '';
-      my $found = 0;
-
-      foreach my $package (@$allowed_packages_aref) {
-        if ($package->getfield('pkgnum') eq $target_package) {
-          $found = 1;
-          last;
-        }
-      }
-
-      $found;
-}
-
diff --git a/fs_selfservice/freeside-selfservice-server b/fs_selfservice/freeside-selfservice-server
new file mode 100644 (file)
index 0000000..264cbc5
--- /dev/null
@@ -0,0 +1,235 @@
+#!/usr/bin/perl -w
+#
+# freeside-selfservice-server
+
+# alas, much false laziness with freeside-queued and fs_signup_server.  at
+# least it is slated to replace fs_{signup,passwd,mailadmin}_server
+# should probably generalize the version in here, or better yet use
+# Proc::Daemon or somesuch
+
+use strict;
+use vars qw( $Debug %kids $kids $max_kids $shutdown $log_file $ssh_pid );
+use Fcntl qw(:flock);
+use POSIX qw(:sys_wait_h setsid);
+use IO::Handle;
+use IO::Select;
+use IO::File;
+use Storable qw(nstore_fd fd_retrieve);
+use Net::SSH qw(sshopen2);
+use FS::UID qw(adminsuidsetup forksuidsetup);
+use FS::ClientAPI;
+
+use FS::Conf;
+use FS::cust_bill;
+use FS::cust_pkg;
+
+$Debug = 2; # >= 2 will log packet contents, including potentially compromising
+            # information
+
+$shutdown = 0;
+$max_kids = '10'; #?
+$kids = 0;
+
+my $user = shift or die &usage;
+my $machine = shift or die &usage;
+my $pid_file = "/var/run/freeside-selfservice-server.$user.pid";
+#my $pid_file = "/var/run/freeside-selfservice-server.$user.pid"; $FS::UID::datasrc not posible, but should include machine name at least, hmm
+
+&init($user);
+
+my $conf = new FS::Conf;
+
+if ($conf->exists('selfservice_server-quiet')) {
+    $FS::cust_bill::quiet = 1;
+    $FS::cust_pkg::quiet = 1;
+}
+
+my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
+
+my $warnkids=0;
+while (1) {
+  my($writer,$reader,$error) = (new IO::Handle, new IO::Handle, new IO::Handle);
+  warn "connecting to $machine\n" if $Debug;
+
+  $ssh_pid = sshopen2($machine,$reader,$writer,$clientd);
+
+#  nstore_fd(\*writer, {'hi'=>'there'});
+
+  warn "entering main loop\n" if $Debug;
+  my $undisp = 0;
+  my $s = IO::Select->new( $reader );
+  while (1) {
+
+    &reap_kids;
+
+    warn "waiting for packet from client\n" if $Debug && !$undisp;
+    $undisp = 1;
+    my @handles = $s->can_read(5);
+    unless ( @handles ) {
+      &shutdown if $shutdown;
+      next;
+    }
+
+    $undisp = 0;
+
+    warn "receiving packet from client\n" if $Debug;
+
+    my $packet = fd_retrieve($reader);
+    warn "packet received\n".
+         join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+      if $Debug > 1;
+
+    #prevent runaway forking
+    my $warnkids = 0;
+    while ( $kids >= $max_kids ) {
+      warn "WARNING: maximum $kids children reached\n" unless $warnkids++;
+      &reap_kids;
+      sleep 1;
+    }
+
+    warn "forking child\n" if $Debug;
+    defined( my $pid = fork ) or die "can't fork: $!";
+    if ( $pid ) {
+      $kids++;
+      $kids{$pid} = 1;
+      warn "child $pid spawned\n" if $Debug;
+    } else { #kid time
+
+      #get new db handle
+      $FS::UID::dbh->{InactiveDestroy} = 1;
+      forksuidsetup($user);
+
+      my $type = $packet->{_packet};
+      warn "calling $type handler\n" if $Debug; 
+      my $rv = eval { FS::ClientAPI->dispatch($type, $packet); };
+      if ( $@ ) {
+        warn my $error = "WARNING: error dispatching $type: $@";
+        $rv = { _error => $error };
+      }
+      $rv->{_token} = $packet->{_token}; #identifier
+
+      warn "sending response\n" if $Debug;
+      flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+      nstore_fd($rv, $writer) or die "FATAL: can't send response: $!";
+      $writer->flush or die "FATAL: can't flush: $!";
+      flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!";
+
+      warn "child exiting\n" if $Debug;
+      exit; #end-of-kid
+    }
+
+  }
+
+}
+
+###
+# utility subroutines
+###
+
+sub reap_kids {
+  #warn "reaping kids\n";
+  foreach my $pid ( keys %kids ) {
+    my $kid = waitpid($pid, WNOHANG);
+    if ( $kid > 0 ) {
+      $kids--;
+      delete $kids{$kid};
+    }
+  }
+  #warn "done reaping\n";
+}
+
+sub init {
+  my $user = shift;
+
+  chdir "/" or die "Can't chdir to /: $!";
+  open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
+  defined(my $pid = fork) or die "Can't fork: $!";
+  if ( $pid ) {
+    print "freeside-selfservice-server to $machine started with pid $pid\n"; #logging to $log_file
+    exit unless $pid_file;
+    my $pidfh = new IO::File ">$pid_file" or exit;
+    print $pidfh "$pid\n";
+    exit;
+  }
+
+#  sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; $kids--; }
+#  #sub REAPER { my $pid = wait; $kids--; $SIG{CHLD} = \&REAPER; }
+#  $SIG{CHLD} =  \&REAPER;
+
+  $shutdown = 0;
+  $SIG{HUP} = sub { warn "SIGHUP received; shutting down\n"; $shutdown++; };
+  $SIG{INT} = sub { warn "SIGINT received; shutting down\n"; $shutdown++; };
+  $SIG{TERM} = sub { warn "SIGTERM received; shutting down\n"; $shutdown++; };
+  $SIG{QUIT} = sub { warn "SIGQUIT received; shutting down\n"; $shutdown++; };
+  $SIG{PIPE} = sub { warn "SIGPIPE received; shutting down\n"; $shutdown++; };
+
+  #false laziness w/freeside-queued
+  my $freeside_gid = scalar(getgrnam('freeside'))
+    or die "can't setgid to freeside group\n";
+  $) = $freeside_gid;
+  $( = $freeside_gid;
+  #if freebsd can't setuid(), presumably it can't setgid() either.  grr fleabsd
+  ($(,$)) = ($),$();
+  $) = $freeside_gid;
+
+  $> = $FS::UID::freeside_uid;
+  $< = $FS::UID::freeside_uid;
+  #freebsd is sofa king broken, won't setuid()
+  ($<,$>) = ($>,$<);
+  $> = $FS::UID::freeside_uid;
+  #eslaf
+
+  $ENV{HOME} = (getpwuid($>))[7]; #for ssh
+  adminsuidsetup $user;
+
+  #$log_file = "/usr/local/etc/freeside/selfservice.". $FS::UID::datasrc; #MACHINE NAME
+  $log_file = "/usr/local/etc/freeside/selfservice.$machine.log";
+
+  open STDOUT, '>/dev/null'
+                            or die "Can't write to /dev/null: $!";
+  setsid                  or die "Can't start a new session: $!";
+  open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
+
+  $SIG{__DIE__} = \&_die;
+  $SIG{__WARN__} = \&_logmsg;
+
+  warn "freeside-selfservice-server starting\n";
+
+}
+
+sub shutdown {
+  my $wait = 12; #wait up to 1 minute
+  while ( $kids > 0 && $wait-- ) {
+    warn "waiting for $kids children to terminate";
+    sleep 5;
+  }
+  warn "abandoning $kids children" if $kids;
+  kill 'TERM', $ssh_pid if $ssh_pid;
+  die "exiting";
+}
+
+sub _die {
+  my $msg = shift;
+  unlink $pid_file if -e $pid_file;
+  _logmsg($msg);
+}
+
+sub _logmsg {
+  chomp( my $msg = shift );
+  _do_logmsg( "[server] [". scalar(localtime). "] [$$] $msg\n" );
+}
+
+sub _do_logmsg {
+  chomp( my $msg = shift );
+  my $log = new IO::File ">>$log_file";
+  flock($log, LOCK_EX);
+  seek($log, 0, 2);
+  print $log "$msg\n";
+  flock($log, LOCK_UN);
+  close $log;
+}
+
+sub usage {
+  die "Usage:\n\n  fs_signup_server user machine\n";
+}
+
index 6672e60..1b87045 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl -Tw
 #
-# $Id: signup.cgi,v 1.35 2002-12-24 23:03:27 ivan Exp $
+# $Id: signup.cgi,v 1.29.2.5 2002-12-24 23:03:25 ivan Exp $
 
 use strict;
 use vars qw( @payby $cgi $locales $packages $pops $init_data $error
@@ -376,8 +376,7 @@ sub pop_info {
 
 #horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
 sub popselector {
-
-  my( $popnum ) = @_;
+  my( $popnum, $state ) = @_;
 
   return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
   return $pops->[0]{city}. ', '. $pops->[0]{state}.
@@ -386,9 +385,7 @@ sub popselector {
     if scalar(@$pops) == 1;
 
   my %pop = ();
-  foreach (@$pops) {
-    push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
-  }
+  push @{ $pop{$_->{state}} }, $_ foreach @$pops;
 
   my $text = <<END;
     <SCRIPT>
@@ -397,82 +394,45 @@ sub popselector {
       var length = what.length;
       what.options[length] = optionName;
     }
-
-    function acstate_changed(what) {
+    
+    function popstate_changed(what) {
       state = what.options[what.selectedIndex].text;
-      for (var i = what.form.popac.length;i > 0;i--)
-                what.form.popac.options[i] = null;
-      what.form.popac.options[0] = new Option("Area code", "-1", false, true);
+      for (var i = what.form.popnum.length;i > 0;i--)
+                what.form.popnum.options[i] = null;
+      what.form.popnum.options[0] = new Option("", "", false, true);
 END
 
-  foreach my $state ( sort { $a cmp $b } keys %pop ) {
-    $text .= "\nif ( state == \"$state\" ) {\n";
+  foreach my $popstate ( sort { $a cmp $b } keys %pop ) {
+    $text .= "\nif ( state == \"$popstate\" ) {\n";
 
-    foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
-      $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
-      if ($ac eq $cgi->param('popac')) {
-        $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
-      }
-    }
-    $text .= "}\n";
-  }
-  $text .= "popac_changed(what.form.popac)}\n";
-
-  $text .= <<END;
-  function popac_changed(what) {
-    ac = what.options[what.selectedIndex].text;
-    for (var i = what.form.popnum.length;i > 0;i--)
-        what.form.popnum.options[i] = null;
-    what.form.popnum.options[0] = new Option("City", "-1", false, true);
-
-END
+    foreach my $pop ( @{$pop{$popstate}}) {
+      my $o_popnum = $pop->{popnum};
+      my $poptext =  $pop->{city}. ', '. $pop->{state}.
+                     ' ('. $pop->{ac}. ')/'. $pop->{exch};
 
-  foreach my $state ( keys %pop ) {
-    foreach my $popac ( keys %{ $pop{$state} } ) {
-      $text .= "\nif ( ac == \"$popac\" ) {\n";
-
-      foreach my $pop ( @{$pop{$state}->{$popac}}) {
-        my $o_popnum = $pop->{popnum};
-        my $poptext =  $pop->{city}. ', '. $pop->{state}.
-                       ' ('. $pop->{ac}. ')/'. $pop->{exch};
-
-        $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
-        if ($popnum == $o_popnum) {
-          $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
-        }
-      }
-      $text .= "}\n";
+      $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n"
     }
+    $text .= "}\n";
   }
 
-
   $text .= "}\n</SCRIPT>\n";
 
   $text .=
-    qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
-    qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
-  $text .= "<OPTION" . ($_ eq $cgi->param('acstate') ? " SELECTED" : "") .
-           ">$_" foreach sort { $a cmp $b } keys %pop;
-  $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD>';
+    qq!<SELECT NAME="popstate" SIZE=1 onChange="popstate_changed(this)">!.
+    qq!<OPTION> !;
+  $text .= "<OPTION>$_" foreach sort { $a cmp $b } keys %pop;
+  $text .= '</SELECT>'; #callback? return 3 html pieces?  #'</TD><TD>';
 
-  $text .=
-    qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
-    qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
-
-  $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
-
-  #comment this block to disable initial list polulation
-  foreach my $pop ( sort { $a->{state} cmp $b->{state} } @$pops ) {
+  $text .= qq!<SELECT NAME="popnum" SIZE=1><OPTION> !;
+  foreach my $pop ( @$pops ) {
     $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
              ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
              $pop->{city}. ', '. $pop->{state}.
                ' ('. $pop->{ac}. ')/'. $pop->{exch};
   }
-
-  $text .= qq!</SELECT></TD></TR></TABLE>!;
+  $text .= '</SELECT>';
 
   $text;
-
 }
 
 sub expselect {
index a2d8dee..d04a5ed 100644 (file)
@@ -1,26 +1,20 @@
-BEGIN { eval "use Devel::AutoProfiler;"; } #only if installed...
-#BEGIN { package Devel::AutoProfiler; use vars qw(%caller_info); }
-#use Devel::AutoProfiler;
-
 use strict;
 use vars qw( $cgi $p );
 use CGI;
 #use CGI::Carp qw(fatalsToBrowser);
 use Date::Format;
 use Date::Parse;
-use Time::Local;
 use Tie::IxHash;
 use HTML::Entities;
 use IO::Handle;
 use IO::File;
 use String::Approx qw(amatch);
-use Chart::LinesPoints;
 use HTML::Widgets::SelectLayers 0.02;
 use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
 use FS::Record qw(qsearch qsearchs fields dbdef);
 use FS::Conf;
 use FS::CGI qw(header menubar popurl table itable ntable idiot eidiot
-               small_custview myexit http_header);
+               small_custview myexit);
 use FS::Msgcat qw(gettext geterror);
 
 use FS::agent;
@@ -52,12 +46,6 @@ use FS::svc_acct_sm;
 use FS::svc_domain;
 use FS::svc_forward;
 use FS::svc_www;
-use FS::ac_type;
-use FS::ac;
-use FS::part_ac_field;
-use FS::ac_field;
-use FS::ac_block;
-use FS::svc_broadband;
 use FS::type_pkgs;
 use FS::part_export;
 use FS::part_export_option;
@@ -74,113 +62,22 @@ sub Script_OnStart {
   &cgisuidsetup($cgi);
   $p = popurl(2);
   #print $cgi->header( '-expires' => 'now' );
-  dbh->{'private_profile'} = {} if dbh->can('sprintProfile');
-
-  #really should check for FS::Profiler or something
-    # Devel::AutoProfiler _our_ VERSION?  thanks a fucking lot
-  if ( Devel::AutoProfiler->can('__recursively_fetch_subs_in_package') ) {
-    #should check to see it's my special version.  well, switch to FS::Profiler
-
-    #nicked from Devel::AutoProfiler::INIT
-    my %subs = Devel::AutoProfiler::__recursively_fetch_subs_in_package('main');
-
-
-    SUB : while( my ($name, $ref) = each(%subs) )
-      {
-        #next if $name =~ /^(main::)?Apache::/;
-        next unless $name =~ /FS/;
-        foreach my $sub (@Devel::AutoProfiler::do_not_instrument_this_sub)
-          {
-            if ($name =~ /$sub/)
-              {
-                next SUB;
-              }
-          }
-        next if ($Devel::AutoProfiler::do_not_instrument_this_sub{$name});
-        #warn "INIT name is $name \n";
-        Devel::AutoProfiler::__instrument_sub($name, $ref);
-      }
-
-  }
-
 }
 
 sub Script_OnFlush {
   my $ref = $Response->{BinaryRef};
-  #$$ref = $cgi->header( @FS::CGI::header ) . $$ref;
-  #$$ref = $cgi->header() . $$ref;
+  $$ref = $cgi->header( @FS::CGI::header ) . $$ref;
   if ( dbh->can('sprintProfile') ) {
-    if ( lc($Response->{ContentType}) eq 'text/html' ) {
-      $$ref =~ s/<\/BODY>[\s\n]*<\/HTML>[\s\n]*$//i
-        or warn "can't remove";
+
+    $$ref =~ s/<\/BODY>[\s\n]*<\/HTML>[\s\n]*$//i
+      or warn "can't remove";
   
-      #$$ref .= '<PRE>'. ("\n"x96). encode_entities(dbh->sprintProfile()). '</PRE>';
-      #  wtf?  konqueror...
-      $$ref .= '<PRE>'. ("\n"x4096). encode_entities(dbh->sprintProfile()).
-               "\n\n". &sprintAutoProfile(). '</PRE>';
+    #$$ref .= '<PRE>'. ("\n"x96). encode_entities(dbh->sprintProfile()). '</PRE>';
+    #  wtf?  konqueror...
+    $$ref .= '<PRE>'. ("\n"x4096). encode_entities(dbh->sprintProfile()). '</PRE>';
 
-      $$ref .= '</BODY></HTML>';
-    }
+    $$ref .= '</BODY></HTML>';
+    
     dbh->{'private_profile'} = {};
   }
 }
-
-if ( defined(@DBIx::Profile::ISA) && DBIx::Profile::db->can('sprintProfile') ) {
-
-  #warn "enabling profiling redirects";
-  *CGI::redirect = sub {
-    my( $self, $location) = @_;
-    my $page =
-      $cgi->header.
-      qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A><BR><BR>!.
-      '<PRE>'. encode_entities(dbh->sprintProfile()).
-      "\n\n". &sprintAutoProfile().  '</PRE>'.
-      '</BODY></HTML>';
-    dbh->{'private_profile'} = {};
-    return $page;
-  };
-
-}
-
-sub by_total_time 
-{ 
-  return $a->{total_time_in_sub} <=> $b->{total_time_in_sub}; 
-}
-
-sub sprintAutoProfile {
-  my %caller_info = %Devel::AutoProfiler::caller_info;
-  return unless keys %caller_info;
-
-  %Devel::AutoProfiler::caller_info = ();
-
-  my @keys = keys(%caller_info);
-
-  foreach my $key (@keys)
-    {
-      my $href = $caller_info{$key};
-
-      $href->{who_am_i} = $key;
-    }
-
-  my @subs = values(%caller_info);
-
-  #my @sorted = sort by_total_time ( @subs );
-  my @sorted = reverse sort by_total_time ( @subs );
-
-  # print Dumper \@sorted;
-
-  my @readable_info;
-
-  foreach my $sort (@sorted)
-    {
-      push(@readable_info, delete($sort->{who_am_i}));
-      push(@readable_info, $sort);
-    }
-
-  use Data::Dumper;
-  return encode_entities(Dumper(\@readable_info));
-
-}
-
-1;
-
index d55ba33..49bcbc0 100644 (file)
@@ -61,19 +61,17 @@ sub handler
       #use CGI::Carp qw(fatalsToBrowser);
       use Date::Format;
       use Date::Parse;
-      use Time::Local;
       use Tie::IxHash;
       use HTML::Entities;
       use IO::Handle;
       use IO::File;
       use String::Approx qw(amatch);
-      use Chart::LinesPoints;
       use HTML::Widgets::SelectLayers 0.02;
       use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
       use FS::Record qw(qsearch qsearchs fields dbdef);
       use FS::Conf;
       use FS::CGI qw(header menubar popurl table itable ntable idiot eidiot
-                     small_custview myexit http_header);
+                     small_custview myexit);
       use FS::Msgcat qw(gettext geterror);
 
       use FS::agent;
diff --git a/httemplate/browse/ac.cgi b/httemplate/browse/ac.cgi
deleted file mode 100755 (executable)
index 0ae138d..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<!-- mason kludge -->
-<%= header('Access Concentrator Listing', menubar(
-  'Main Menu'   => $p,
-  'Access Concentrator Types' => $p. 'browse/ac_type.cgi',
-)) %>
-<BR>
-<A HREF="<%= $p %>edit/ac.cgi"><I>Add a new Access Concentrator</I></A><BR><BR>
-
-<%= table() %>
-<TR>
-  <TH COLSPAN=2>AC</TH>
-  <TH>AC Type</TH>
-  <TH>Fields</TH>
-  <TH>Network/Mask</TH>
-</TR>
-<% 
-
-foreach my $ac ( qsearch('ac',{}) ) {
-  my($hashref)=$ac->hashref;
-  my($actypenum)=$hashref->{actypenum};
-  my($ac_type)=qsearchs('ac_type',{'actypenum'=>$actypenum});
-  my($actypename)=$ac_type->getfield('actypename');
-  print <<END;
-      <TR>
-        <TD><A HREF="${p}edit/ac.cgi?$hashref->{acnum}">
-          $hashref->{acnum}</A></TD>
-        <TD><A HREF="${p}edit/ac.cgi?$hashref->{acnum}">
-          $hashref->{acname}</A></TD>
-        <TD><A HREF="${p}edit/ac_type.cgi?$actypenum">$actypename</A></TD>
-        <TD>
-END
-
-  foreach my $ac_field ( qsearch('ac_field', { acnum => $hashref->{acnum} }) ) {
-    my $part_ac_field = qsearchs('part_ac_field',
-                         { acfieldpart => $ac_field->getfield('acfieldpart') });
-    print $part_ac_field->getfield('name') . ' ';
-    print $ac_field->getfield('value') . '<BR>';
-  }
-  print '</TD><TD>';
-
-  foreach (qsearch('ac_block', { acnum => $hashref->{acnum} })) {
-    my $net_addr = new NetAddr::IP($_->getfield('ip_gateway'),
-                                   $_->getfield('ip_netmask'));
-    print $net_addr->network->addr . '/' . $net_addr->network->mask . '<BR>';
-  }
-
-  print "<TR>\n";
-
-}
-
-print <<END;
-    </TABLE>
-  </BODY>
-</HTML>
-END
-
-%>
diff --git a/httemplate/browse/ac_type.cgi b/httemplate/browse/ac_type.cgi
deleted file mode 100755 (executable)
index 0ad8271..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<!-- mason kludge -->
-<%
-
-print header('Access Concentrator Types', menubar(
-  'Main Menu' => $p,
-  'Access Concentrators' => $p. 'browse/ac.cgi',
-)) %>
-<BR>
-<A HREF="<%= $p %>edit/ac_type.cgi"><I>Add new AC Type</I></A><BR><BR>
-<%= table() %>
-      <TR>
-        <TH></TH>
-        <TH>Type</TH>
-        <TH>Fields</TH>
-      </TR>
-
-<%
-foreach my $ac_type ( qsearch('ac_type',{}) ) {
-  my($hashref)=$ac_type->hashref;
-  print <<END;
-      <TR>
-        <TD><A HREF="${p}edit/ac_type.cgi?$hashref->{actypenum}">
-          $hashref->{actypenum}</A></TD>
-        <TD><A HREF="${p}edit/ac_type.cgi?$hashref->{actypenum}">
-          $hashref->{actypename}</A></TD>
-        <TD>
-END
-
-  foreach ( qsearch('part_ac_field', { actypenum => $hashref->{actypenum} }) ) {
-    my ($part_ac_field) = $_->hashref;
-    print $part_ac_field->{'name'} .
-          ' (' . $part_ac_field->{'ut_type'} . ')<BR>';
-  }
-
-}
-
-print <<END;
-       </TD>
-      </TR>
-      <TR>
-      </TR>
-    </TABLE>
-  </BODY>
-</HTML>
-END
-
-%>
index c2473c4..9916060 100755 (executable)
@@ -21,8 +21,7 @@ print '<BR><BR>'. &table(). <<END;
         <TH><FONT SIZE=-1>Country</FONT></TH>
         <TH><FONT SIZE=-1>State</FONT></TH>
         <TH>County</TH>
-        <TH>Taxclass<BR><FONT SIZE=-1>(per-package classification)</FONT></TH>
-        <TH>Tax name<BR><FONT SIZE=-1>(printed on invoices)</FONT></TH>
+        <TH>Taxclass</TH>
         <TH><FONT SIZE=-1>Tax</FONT></TH>
         <TH><FONT SIZE=-1>Exempt<BR>per<BR>month</TH>
       </TR>
@@ -112,14 +111,6 @@ END
   }
   print "</TD>";
 
-  print "<TD";
-  if ( $hashref->{taxname} ) {
-    print ' BGCOLOR="#ffffff">'. $hashref->{taxname};
-  } else {
-    print ' BGCOLOR="#cccccc">Tax';
-  }
-  print "</TD>";
-
   print "<TD BGCOLOR=\"#ffffff\">$hashref->{tax}%</TD>".
         '<TD BGCOLOR="#ffffff">$'.
           sprintf("%.2f", $hashref->{exempt_amount} || 0). '</TD>'.
index 8f6f913..eaa5b9b 100644 (file)
@@ -6,6 +6,9 @@
 <img src="overview.png">
 <ul>
   <li><a href="install.html">New Installation</a>
+  <li><a href="upgrade4.html">Upgrading from 1.2.x to 1.2.2</a>
+  <li><a href="upgrade5.html">Upgrading from 1.2.2 to 1.2.3</a>
+  <li><a href="upgrade6.html">Upgrading from 1.2.3 to 1.3.0</a>
   <li><a href="upgrade7.html">Upgrading from 1.3.0 to 1.3.1</a>
   <li><a href="upgrade8.html">Upgrading from 1.3.1 to 1.4.0</a>
   <li><a href="upgrade9.html">Upgrading from 1.4.0 to 1.4.1</a>
index 36b8ae0..c90e908 100644 (file)
@@ -53,7 +53,6 @@ Before installing, you need:
       <li><a href="http://search.cpan.org/search?dist=HTML-Widgets-SelectLayers">HTML-Widgets-SelectLayers</a>
       <li><a href="http://search.cpan.org/search?dist=Storable">Storable</a>
 <!-- MyAccounts, maybe only for dev     <li><a href="http://search.cpan.org/search?dist=Cache-Cache">Cache::Cache</a> -->
-      <li><a href="http://search.cpan.org/search?dist=NetAddr-IP">NetAddr-IP</a>
       <li><a href="http://search.cpan.org/search?dist=ApacheDBI">Apache::DBI</a> <i>(optional but recommended for better webinterface performance)</i>
     </ul>
 </ul>
@@ -64,7 +63,7 @@ Install the Freeside distribution:
     <ul>
       <li> with <a href="http://www.postgresql.org/users-lounge/docs/7.1/postgres/user-manag.html#DATABASE-USERS">PostgreSQL</a>:
         <pre>
-$ su postgres (pgsql on some distributions)
+$ su postgres
 $ createuser -P freeside
 Enter password for user "freeside": 
 Enter it again: 
@@ -81,7 +80,7 @@ mysql> GRANT SELECT,INSERT,UPDATE,DELETE,INDEX,ALTER,CREATE,DROP on freeside.* T
 <!--  <li>Unpack the tarball: <pre>gunzip -c fs-x.y.z.tar.gz | tar xvf -</pre>-->
   <li>Edit the top-level Makefile:
     <ul>
-      <li>Set <tt>DATASOURCE</tt> to your <a href="http://search.cpan.org/doc/TIMB/DBI-1.28/DBI.pm">DBI data source</a>, for example, <tt>DBI:Pg:dbname=freeside</tt> for PostgresSQL<!-- or <tt>DBI:mysql:freeside</tt> for MySQL-->.  See the <a href="http://search.cpan.org/doc/TIMB/DBI-1.28/DBI.pm">DBI manpage</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">manpage for your DBD</a> for the exact syntax of your DBI data source.
+      <li>Set <tt>DATASOURCE</tt> to your <a href="http://search.cpan.org/doc/TIMB/DBI-1.28/DBI.pm">DBI data source</a>, for example, <tt>DBI:Pg:host=localhost;dbname=freeside</tt> for PostgresSQL<!-- or <tt>DBI:mysql:freeside</tt> for MySQL-->.  See the <a href="http://search.cpan.org/doc/TIMB/DBI-1.28/DBI.pm">DBI manpage</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">manpage for your DBD</a> for the exact syntax of your DBI data source.
       <li>Set <tt>DB_PASSWORD</tt> to the freeside database user's password.
     </ul>
   <li>Add the freeside database to your database engine:
@@ -169,6 +168,7 @@ require&nbsp;"/usr/local/apache/conf/handler.pl";
 <li>Restrict access to this web interface - see the <a href="http://httpd.apache.org/docs/misc/FAQ.html#user-authentication">Apache documentation on user authentication</a>.    For example, to configure user authentication with <a href="http://httpd.apache.org/docs/mod/mod_auth.html">mod_auth</a> (flat files):
 <pre>
 &lt;Directory /usr/local/apache/htdocs/freeside-asp&gt;
+PerlSetVar Global /usr/local/etc/freeside/asp-global/
 AuthName Freeside
 AuthType Basic
 AuthUserFile /usr/local/etc/freeside/htpasswd
@@ -185,16 +185,16 @@ $ <a href="man/bin/freeside-adduser.html">freeside-adduser</a> -c -h /usr/local/
 $ <a href="man/bin/freeside-adduser.html">freeside-adduser</a> -h /usr/local/etc/freeside/htpasswd <i>username</i></pre></font>
     </ul>
   <i>(using other auth types, add each user to your <a href="http://httpd.apache.org/docs/misc/FAQ.html#user-authentication">Apache authentication</a> and then run: <tt>freeside-adduser <b>username</b></tt></i>
-  <li>As the freeside UNIX user, run <tt>freeside-setup <b>username</b></tt> to create the database tables, passing the username of a Freeside user you created above:
+  <li>As the freeside UNIX user, run <tt>bin/fs-setup <b>username</b></tt> (in the untar'ed freeside directory) to create the database tables, passing the username of a Freeside user you created above:
 <pre>
 $ su freeside
-$ freeside-setup <b>username</b>
+$ cd <b>/path/to/freeside/</b>
+$ bin/fs-setup <b>username</b>
 </pre>
-  Alternately, use the -s option to enable shipping addresses: <tt>freeside-setup -s <b>username</b></tt>
   <li>As the freeside UNIX user, run <tt>bin/populate-msgcat <b>username</b></tt> (in the untar'ed freeside directory) to populate the message catalog, passing the username of a Freeside user you created above:
 <pre>
 $ su freeside
-$ cd <b>/path/to/freeside/</b>
+$ cd <b>/path/to/freeside-1.4.0/</b>
 $ bin/populate-msgcat <b>username</b>
 </pre>
   <li><tt>freeside-queued</tt> was installed with the Perl modules.  Start it now and ensure that is run upon system startup (Do this manually, or edit the top-level Makefile, replacing INIT_FILE with the appropriate location on your systemand QUEUED_USER with the username of a Freeside user you created above, and run <tt>make install-init</tt>)
index 6787809..2db9edb 100755 (executable)
       <li>POP mail accounts have entries in passwd only, and have a particular shell.
       <li>Everything else in passwd is a shell account.
     </ul>
-<!--  <li><a name="svc_acct_sm">bin/svc_acct_sm.import</a> - Import qmail ( `virtualdomains' and `rcpthosts' ), or sendmail ( `virtusertable' and `sendmail.cw' ) files.  Before running bin/svc_acct_sm.import, you need <a href="../browse/part_svc.cgi">services</a> as follows:
+  <li><a name="svc_acct_sm">bin/svc_acct_sm.import</a> - Import qmail ( `virtualdomains' and `rcpthosts' ), or sendmail ( `virtusertable' and `sendmail.cw' ) files.  Before running bin/svc_acct_sm.import, you need <a href="../browse/part_svc.cgi">services</a> as follows:
     <ul>
       <li>Domain (table svc_acct)
       <li>Mail alias (table svc_acct_sm)
     </ul>
--->
   <li><a name="cust_main">Importing customer data</a>
     <ul>
       <li>Manually
index 7465615..092d2f8 100644 (file)
Binary files a/httemplate/docs/schema.dia and b/httemplate/docs/schema.dia differ
index b4d21f3..cec122f 100644 (file)
@@ -57,7 +57,6 @@
         <li>recur - recurring fee
         <li>sdate - starting date
         <li>edate - ending date
-        <li>itemdesc - Line item description (currently used only when pkgnum is 0)
       </ul>
     <li><a name="cust_credit" href="man/FS/cust_credit.html">cust_credit</a> - Credits.  The equivalent of a negative <a href="#cust_bill">cust_bill</a> record.
       <ul>
         <li>tax - % rate
         <li>taxclass
         <li>exempt_amount
-        <li>taxname - if defined, printed on invoices instead of "Tax"
       </ul>
     <li><a name="cust_tax_exempt" href="man/FS/cust_tax_exempt.html">cust_tax_exempt</a> - Tax exemption record
       <ul>
       <ul>
         <li>svcpart - primary key
         <li>svc - name of this service
-        <li>svcdb - table used for this service: svc_acct, svc_forward, svc_domain, svc_charge or svc_wo
+        <li>svcdb - table used for this service: svc_acct, svc_acct_sm, svc_forward, svc_domain, svc_charge or svc_wo
         <li>disabled - Disabled flag, empty or `Y'
 <!--        <li><i>table</i>__<i>field</i> - Default or fixed value for <i>field</i> in <i>table</i>
         <li><i>table</i>__<i>field</i>_flag - null, D or F
         <li>npa - area code
         <li>nxx - exchange
       </ul>
+    <li><a name="svc_acct_sm" href="man/FS/svc_acct_sm.html">svc_acct_sm</a> - <b>DEPRECIATED</b> Domain mail aliases
+      <ul>
+        <li>svcnum - <a href="#cust_svc">primary key</a>
+        <li>domsvc - <a href="#svc_domain">Domain</a> (by svcnum)
+        <li>domuid - <a href="#svc_acct">Account</a> (by uid)
+        <li>domuser - domuser @ <a href="#svc_domain">Domain</a> forwards to <a href="#svc_acct">Account</a>
+      </ul>
     <li><a name="svc_domain" href="man/FS/svc_domain.html">svc_domain</a> - Domains
       <ul>
         <li>svcnum - <a href="#cust_svc">primary key</a>
index 72e1642..7dac5fd 100644 (file)
@@ -38,14 +38,9 @@ freeside-logout username ( portnum | ip | nasnum nasport )</pre>
       <li><i>username</i> is a customer username from the svc_acct table
       <li><i>portnum</i>, <i>ip</i> or <i>nasport</i> and <i>nasnum</i> uniquely identify a port in the <a href="schema.html#port">port</a> database table.
     </ul>
-  <li>RADIUS - One of:
+  <li>RADIUS
     <ul>
-      <li>Run the <b>freeside-sqlradius-radacctd</b> daemon to import radacct
-        records from all configured sqlradius exports:
-          <tt>freeside-sqlradius-radacctd username</tt>
       <li>Configure your RADIUS server's login and logout callbacks to use the command-line <tt>freeside-login</tt> and <tt>freeside-logout</tt> utilites.
-      <li> <i>(incomplete)</i>Use the <b>fs_radlog/fs_radlogd</b> tool to
-        import records from a text radacct file.
     </ul>
 </ul>
 <h2>Callbacks</h2>
diff --git a/httemplate/docs/upgrade4.html b/httemplate/docs/upgrade4.html
new file mode 100644 (file)
index 0000000..1d70f8b
--- /dev/null
@@ -0,0 +1,27 @@
+<head>
+  <title>Upgrading to 1.2.2</title>
+</head>
+<body>
+<h1>Upgrading to 1.2.2 from 1.2.x</h1>
+<ul>
+  <li>If migrating from 1.0.0, see these <a href="upgrade.html">instructions</a> first.
+  <li>If migrating from less than 1.1.4, see these <a href="upgrade2.html">instructions</a> first.
+  <li>If migrating from less than 1.2.0, see these <a href="upgrade3.html">instructions</a> first.
+  <li>Back up your data and current Freeside installation.
+  <li>Install the Perl modules <a href="http://www.perl.com/CPAN/modules/by-module/Locale/">Locale-Codes</a> and <a href="http://www.perl.com/CPAN/modules/by-module/Net/">Net-Whois</a>.
+  <li>Apply the following changes to your database:
+<pre>
+ALTER TABLE cust_pay_batch CHANGE exp exp VARCHAR(11);
+</pre>
+  <li>Copy or symlink htdocs to the new copy.
+  <li>Remove the symlink or directory <i>(your_site_perl_directory)</i>/FS.
+  <li>Change to the FS directory in the new tarball, and build and install the
+      Perl modules:
+    <pre>
+$ cd FS/
+$ perl Makefile.PL
+$ make
+$ su
+# make install</pre>
+  <li>Run bin/dbdef-create.  This file uses MySQL-specific syntax.  If you are running a different database engine you will need to modify it slightly.
+</body>
diff --git a/httemplate/docs/upgrade5.html b/httemplate/docs/upgrade5.html
new file mode 100644 (file)
index 0000000..3f34316
--- /dev/null
@@ -0,0 +1,34 @@
+<head>
+  <title>Upgrading to 1.3.0</title>
+</head>
+<body>
+<h1>Upgrading to 1.2.3 from 1.2.2</h1>
+<ul>
+  <li>If migrating from 1.0.0, see these <a href="upgrade.html">instructions</a> first.
+  <li>If migrating from less than 1.1.4, see these <a href="upgrade2.html">instructions</a> first.
+  <li>If migrating from less than 1.2.0, see these <a href="upgrade3.html">instructions</a> first.
+  <li>If migrating from less than 1.2.2, see these <a href="upgrade4.html">instructions</a> first.
+  <li>Back up your data and current Freeside installation.
+  <li>Apply the following changes to your database:
+<pre>
+ALTER TABLE svc_acct_pop ADD loc CHAR(4);
+CREATE TABLE prepay_credit (
+  prepaynum int NOT NULL,
+  identifier varchar(80) NOT NULL,
+  amount decimal(10,2) NOT NULL,
+  PRIMARY KEY (prepaynum),
+  INDEX (identifier)
+);
+</pre>
+  <li>Copy or symlink htdocs to the new copy.
+  <li>Remove the symlink or directory <i>(your_site_perl_directory)</i>/FS.
+  <li>Change to the FS directory in the new tarball, and build and install the
+      Perl modules:
+    <pre>
+$ cd FS/
+$ perl Makefile.PL
+$ make
+$ su
+# make install</pre>
+  <li>Run bin/dbdef-create.  This file uses MySQL-specific syntax.  If you are running a different database engine you will need to modify it slightly.
+</body>
diff --git a/httemplate/docs/upgrade6.html b/httemplate/docs/upgrade6.html
new file mode 100644 (file)
index 0000000..dc82975
--- /dev/null
@@ -0,0 +1,66 @@
+<head>
+  <title>Upgrading to 1.3.0</title>
+</head>
+<body>
+<h1>Upgrading to 1.3.0 from 1.2.3</h1>
+<ul>
+  <li>If migrating from 1.0.0, see these <a href="upgrade.html">instructions</a> first.
+  <li>If migrating from less than 1.1.4, see these <a href="upgrade2.html">instructions</a> first.
+  <li>If migrating from less than 1.2.0, see these <a href="upgrade3.html">instructions</a> first.
+  <li>If migrating from less than 1.2.2, see these <a href="upgrade4.html">instructions</a> first.
+  <li>If migrating from less than 1.2.3, see these <a href="upgrade5.html">instructions</a> first.
+  <li>Back up your data and current Freeside installation.
+  <li>As 1.3.0 requires transactions, <b>MySQL's default <a href="http://www.mysql.com/doc/M/y/MyISAM.html">MyISAM</a> and <a href="http://www.mysql.com/doc/I/S/ISAM.html">ISAM</a> table types are no longer supported</b>.  Converting to <a href="http://www.postgresql.org/">PostgreSQL</a> is recommended.  If you really want to use MySQL, convert your tables to one of the <a href="http://www.mysql.com/doc/T/a/Table_types.html">transaction-safe table types</a> such as <a href="http://www.mysql.com/doc/B/D/BDB.html">BDB</a>.
+  <li>Copy the <i>invoice_template</i> file from the <i>conf/</i> directory in the distribution to your <a href="config.html">configuration directory</a>.
+  <li>Install the <a href="http://search.cpan.org/search?dist=Text-Template">Text-Template</a>, <a href="http://search.cpan.org/search?dist=DBIx-DBSchema">DBIx-DBSchema</a>, <a href="http://search.cpan.org/search?dist=Net-SSH">Net-SSH</a>, <a href="http://search.cpan.org/search?dist=String-ShellQuote">String-ShellQuote</a> and <a href="http://search.cpan.org/search?dist=Net-SCP">Net-SCP</a> Perl modules.
+  <li>Apply the following changes to your database:
+<pre>
+CREATE TABLE domain_record (
+  recnum int NOT NULL,
+  svcnum int NOT NULL,
+  reczone varchar(80) NOT NULL,
+  recaf char(2) NOT NULL,
+  rectype char(5) NOT NULL,
+  recdata varchar(80) NOT NULL,
+  PRIMARY KEY (recnum)
+);
+CREATE TABLE svc_www (
+  svcnum int NOT NULL,
+  recnum int NOT NULL,
+  usersvc int NOT NULL,
+  PRIMARY KEY (svcnum)
+);
+ALTER TABLE part_svc ADD svc_www__recnum varchar(80) NULL;
+ALTER TABLE part_svc ADD svc_www__recnum_flag char(1) NULL;
+ALTER TABLE part_svc ADD svc_www__usersvc varchar(80) NULL;
+ALTER TABLE part_svc ADD svc_www__uesrsvc_flag char(1) NULL;
+ALTER TABLE svc_acct CHANGE _password _password varchar(50) NULL;
+ALTER TABLE svc_acct ADD seconds integer NULL;
+ALTER TABLE part_svc ADD svc_acct__seconds integer NULL;
+ALTER TABLE part_svc ADD svc_acct__seconds_flag char(1) NULL;
+ALTER TABLE prepay_credit ADD seconds integer NULL;
+
+</pre>
+  <li>If your database supports dropping columns:
+<pre>
+ALTER TABLE cust_bill DROP owed;
+ALTER TABLE cust_credit DROP credited;
+</pre>
+     Or, if your database does not support dropping columns, you can do this:
+<pre>
+ALTER TABLE cust_bill CHANGE owed depriciated decimal(10,2);
+ALTER TABLE cust_credit CHANGE credited depriciated2 decimal(10,2);
+</pre>
+
+  <li>Copy or symlink htdocs to the new copy.
+  <li>Remove the symlink or directory <i>(your_site_perl_directory)</i>/FS.
+  <li>Change to the FS directory in the new tarball, and build and install the
+      Perl modules:
+    <pre>
+$ cd FS/
+$ perl Makefile.PL
+$ make
+$ su
+# make install</pre>
+  <li>Run bin/dbdef-create.
+</body>
index da5b643..c6afe2b 100644 (file)
 INSERT INTO msgcat ( msgnum, msgcode, locale, msg ) VALUES ( 18, 'daytime', 'en_US', 'Day Phone' );
 INSERT INTO msgcat ( msgnum, msgcode, locale, msg ) VALUES ( 19, 'night', 'en_US', 'Night Phone' );
 </pre>
+  <li>Optionally, apply the following changes to your database (performance improvement for large numbers of services or packages):
+<pre>
+CREATE INDEX part_pkg1 ON part_pkg ( disabled );
+CREATE INDEX part_svc1 ON part_svc ( disabled );
+</pre>
   <li>If you want to use ACH (electronic checks), you will need to make changes to your database.  The easiest way to make these changes is to dump your database (with pg_dump), change the payinfo field in the cust_pay, cust_refund, h_cust_pay and h_cust_refund tables from varchar(16) to varchar(80), reload the database from the dump, and run dbdef-create
   <li>Restart Apache and freeside-queued.
 </body>
diff --git a/httemplate/edit/ac.cgi b/httemplate/edit/ac.cgi
deleted file mode 100755 (executable)
index 86b05a4..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-<!-- mason kludge -->
-<%
-
-my($ac);
-if ( $cgi->param('error') ) {
-  $ac = new FS::ac ( {
-    map { $_, scalar($cgi->param($_)) } fields('ac')
-  } );
-} elsif ( $cgi->keywords ) { #editing
-  my( $query ) = $cgi->keywords;
-  $query =~ /^(\d+)$/;
-  $ac=qsearchs('ac',{'acnum'=>$1});
-} else { #adding
-  $ac = new FS::ac {};
-}
-my $action = $ac->acnum ? 'Edit' : 'Add';
-my $hashref = $ac->hashref;
-
-print header("$action Access Concentrator", menubar(
-  'Main Menu' => "$p",
-  'View all access concentrators' => "${p}browse/ac.cgi",
-));
-
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
-      "</FONT>"
-  if $cgi->param('error');
-
-print '<FORM ACTION="', popurl(1), 'process/ac.cgi" METHOD=POST>',
-      qq!<INPUT TYPE="hidden" NAME="acnum" VALUE="$hashref->{acnum}">!,
-      "Access Concentrator #", $hashref->{acnum} ? $hashref->{acnum} : "(NEW)";
-
-print <<END;
-
-<TABLE COLOR="#cccccc">
-  <TR>
-    <TH ALIGN="RIGHT">Access Concentrator</TH>
-    <TD>
-      <INPUT TYPE="text" NAME="acname" SIZE=15 VALUE="$hashref->{acname}">
-    </TD>
-  </TD>
-END
-
-
-if (! $ac->acnum) {
-  print <<END;
-  <TR>
-    <TH ALIGN="RIGHT">Access Concentrator Type</TH>
-    <TD><SELECT NAME="actypenum" SIZE="1"><OPTION VALUE=""></OPTION>
-END
-
-  foreach (qsearch('ac_type', {})) {
-    my $narf = $_->hashref;
-    print qq!<OPTION! .
-          ($narf->{actypenum} eq $hashref->{actypenum} ? ' SELECTED' : '') .
-          qq! VALUE="$narf->{actypenum}">$narf->{actypenum}: $narf->{actypename}! .
-          qq!</OPTION>!;
-  }
-
-  print '</TD></TR></TABLE>';
-} else {
-  print '</TABLE>';
-  print qq!<INPUT TYPE="hidden" NAME="actypenum" VALUE="$hashref->{actypenum}">!;
-}
-
-print qq!</TABLE><BR><BR><INPUT TYPE="submit" VALUE="!,
-      $hashref->{acnum} ? "Apply changes" : "Add access concentrator",
-      qq!"></FORM>!;
-
-if ($hashref->{acnum}) {
-  print table();
-  print <<END;
-    Additional Fields:<BR>
-    <TH>
-      <TD>Field Name</TD>
-      <TD COLSPAN=2>Field Value</TD>
-    </TH>
-END
-
-  #my @ac_fields = qsearch('ac_field', { acnum => $hashref->{acnum} });
-  my @ac_fields = $ac->ac_field;
-  foreach (@ac_fields) {
-    print qq!\n<TR><TD></TD>!;
-    my $part_ac_field = qsearchs('part_ac_field',
-                          { acfieldpart => $_->getfield('acfieldpart') });
-    print '<TD>' . $part_ac_field->getfield('name') .
-          '</TD><TD>' . $_->getfield('value') . '</TD></TR>';
-    print "\n";
-  }
-
-  print '<FORM ACTION="', popurl(1), 'process/ac_field.cgi" METHOD=POST>';
-  print <<END;
-    <TR>
-      <TD><INPUT TYPE="hidden" NAME="acnum" VALUE="$hashref->{acnum}">
-          <INPUT TYPE="hidden" NAME="acname" VALUE="$hashref->{acname}">
-          <INPuT TYPE="hidden" NAME="actypenum" VALUE="$hashref->{actypenum}">
-          <SMALL>(NEW)</SMALL>
-      </TD>
-      <TD><SELECT NAME="acfieldpart"><OPTION></OPTION>
-END
-
-  my @part_ac_fields = qsearch('part_ac_field',
-                         { actypenum => $hashref->{actypenum} });
-  foreach my $part_ac_field (@part_ac_fields) {
-    my $acfieldpart = $part_ac_field->getfield('acfieldpart');
-    if (grep {$_->getfield('acfieldpart') eq $acfieldpart} @ac_fields) {next;}
-    print qq!<OPTION VALUE="${acfieldpart}">! .
-          $part_ac_field->getfield('name') . '</OPTION>';
-  }
-
-  print <<END;
-        </SELECT>
-      </TD>
-      <TD><INPUT TYPE="text" SIZE="15" NAME="value"></TD>
-      <TD><INPUT TYPE="submit" VALUE="Add"></TD>
-    </TR>
-    </FORM>
-  </TABLE>
-END
-
-}
-
-if ($hashref->{acnum}) {
-
-  print qq!<BR><BR>IP Address Blocks:<BR>! . table() .
-        qq!<TR><TH></TH><TH>Network/Mask</TH>! .
-        qq!<TH>Gateway Address</TH><TH>Mask length</TH></TR>\n!;
-
-  foreach (qsearch('ac_block', { acnum => $hashref->{acnum} })) {
-    my $ip_addr = new NetAddr::IP($_->getfield('ip_gateway'),
-                                  $_->getfield('ip_netmask'));
-    print qq!<TR><TD></TD><TD>! . $ip_addr->network->addr() . '/' .
-          $ip_addr->network->mask() . qq!</TD>!;
-
-    print qq!<TD>! . $_->getfield('ip_gateway') . qq!</TD>\n! .
-          qq!<TD>! . $_->getfield('ip_netmask') . qq!</TD></TR>!;
-
-  }
-
-  print '<FORM ACTION="', popurl(1), 'process/ac_block.cgi" METHOD=POST>';
-  print <<END;
-  <TR>
-    <TD><INPUT TYPE="hidden" NAME="acnum" VALUE="$hashref->{acnum}">
-        <INPUT TYPE="hidden" NAME="acname" VALUE="$hashref->{acname}">
-        <INPuT TYPE="hidden" NAME="actypenum" VALUE="$hashref->{actypenum}">
-       <SMALL>(NEW)</SMALL>
-    </TD>
-    <TD></TD>
-    <TD><INPUT TYPE="text" NAME="ip_gateway" SIZE="15"></TD>
-    <TD><INPUT TYPE="text" NAME="ip_netmask" SIZE="2"></TD>
-    <TD><INPUT TYPE="submit" VALUE="Add"></TD>
-  </TR>
-  </FORM>
-</TABLE>
-END
-
-}
-
-print <<END;
-  </BODY>
-</HTML>
-END
-
-%>
diff --git a/httemplate/edit/ac_type.cgi b/httemplate/edit/ac_type.cgi
deleted file mode 100755 (executable)
index ccc3d57..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<!-- mason kludge -->
-<%
-
-my $ac_type;
-if ( $cgi->param('error') ) {
-  $ac_type = new FS::ac_type ( {
-    map { $_, scalar($cgi->param($_)) } fields('ac_type')
-  } );
-} elsif ( $cgi->keywords ) { #editing
-  my($query)=$cgi->keywords;
-  $query =~ /^(\d+)$/;
-  $ac_type=qsearchs('ac_type',{'actypenum'=>$1});
-} else { #adding
-  $ac_type = new FS::ac_type {};
-}
-my $action = $ac_type->actypenum ? 'Edit' : 'Add';
-my $hashref = $ac_type->hashref;
-
-my @ut_types = qw( float number text alpha anything ip domain );
-
-my $p1 = popurl(1);
-print header("$action Access Concentrator Type", menubar(
-  'Main Menu' => popurl(2),
-  'View all Access Concentrator types' => popurl(2). "browse/ac_type.cgi",
-));
-
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
-      "</FONT>"
-  if $cgi->param('error');
-
-print qq!<FORM ACTION="${p1}process/ac_type.cgi" METHOD=POST>!;
-
-#display
-
-print qq!<INPUT TYPE="hidden" NAME="actypenum" VALUE="$hashref->{actypenum}">!,
-      "AC Type #", $hashref->{actypenum} ? $hashref->{actypenum} : "(NEW)";
-
-print <<TROZ;
-<PRE>
-AC Type Name <INPUT TYPE="text" NAME="actypename" SIZE=15 VALUE="$hashref->{actypename}">
-</PRE>
-
-TROZ
-
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
-      $hashref->{actypenum} ? "Apply changes" : "Add AC Type",
-      qq!"></FORM>!;
-
-
-if ($hashref->{actypenum}) {
-  print qq!   <BR>Available fields:<BR>! .  table();
-
-  print qq!    <TH><TD>Field name</TD><TD>Field type</TD><TD></TD></TH>!;
-
-  my @part_ac_field = qsearch ( 'part_ac_field',
-                                { actypenum => $hashref->{actypenum} } );
-  foreach ( @part_ac_field ) {
-    my $pf_hashref = $_->hashref;
-    print <<END;
-      <TR>
-        <TD>$pf_hashref->{acfieldpart}</TD>
-        <TD>$pf_hashref->{name}</TD>
-        <TD>$pf_hashref->{ut_type}</TD>
-      </TR>
-END
-  }
-
-  my $name, $ut_type = '';
-  if ($cgi->param('error')) {
-    $name = $cgi->param('name');
-    $ut_type = $cgi->param('ut_type');
-  }
-
-  print <<END;
-      <FORM ACTION="${p1}process/part_ac_field.cgi" METHOD=GET>
-      <TR>
-       <TD><SMALL>(NEW)</SMALL>
-         <INPUT TYPE="hidden" NAME="actypenum" VALUE="$hashref->{actypenum}">
-       </TD>
-       <TD>
-         <INPUT TYPE="text" NAME="name" VALUE="${name}">
-       </TD>
-       <TD>
-         <SELECT NAME="ut_type" SIZE=1><OPTION>
-END
-
-  foreach ( @ut_types ) {
-    print qq!<OPTION! . ($ut_type ? " SELECTED>$_" : ">$_");
-  }
-
-  print <<END;
-        </SELECT>
-      </TD>
-      <TD><INPUT TYPE="submit" VALUE="Add"></TD>
-    </TR>
-    </FORM>
-  </TABLE>
-END
-
-}
-
-%>
-
- </BODY>
-</HTML>
-
index f3d2882..7ef37a4 100755 (executable)
@@ -15,8 +15,7 @@ print qq!<FORM ACTION="!, popurl(1),
         <TH><FONT SIZE=-1>Country</FONT></TH>
         <TH><FONT SIZE=-1>State</FONT></TH>
         <TH><FONT SIZE=-1>County</FONT></TH>
-        <TH><FONT SIZE=-1>Taxclass</FONT><BR><FONT SIZE=-2>(per-package classification)</FONT></TH>
-        <TH><FONT SIZE=-1>Tax name</FONT><BR><FONT SIZE=-2>(printed on invoices)</FONT></TH>
+        <TH><FONT SIZE=-1>Taxclass</FONT></TH>
         <TH><FONT SIZE=-1>Tax</FONT></TH>
         <TH><FONT SIZE=-1>Exempt<BR>per<BR>month</TH>
       </TR>
@@ -47,8 +46,6 @@ END
       : ' BGCOLOR="#cccccc">(ALL)'
     , "</TD>";
 
-  print qq!<TD><INPUT TYPE="text" NAME="taxname!, $hashref->{taxnum},
-        qq!" VALUE="!, $hashref->{taxname}, qq!"></TD>!;
   print qq!<TD><INPUT TYPE="text" NAME="tax!, $hashref->{taxnum},
         qq!" VALUE="!, $hashref->{tax}, qq!" SIZE=6 MAXLENGTH=6>%</TD>!;
   print qq!<TD>\$<INPUT TYPE="text" NAME="exempt_amount!, $hashref->{taxnum},
index 2104b45..bd32889 100755 (executable)
@@ -114,6 +114,12 @@ tie my %events, 'Tie::IxHash',
     'weight' => 30,
   },
 
+  'realtime-card-cybercash' => {
+    'name' => '(<b>deprecated</b>) Run card with <a href="http://www.cybercash.com/cashregister">CyberCash CashRegister</a> realtime gateway',
+    'code' => '$cust_bill->realtime_card_cybercash();',
+    'weight' => 30,
+  },
+
   'realtime-lec' => {
     'name' => 'Run phone bill ("LEC") billing with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway',
     'code' => '$cust_bill->realtime_lec();',
index d4bb470..683bf9e 100755 (executable)
@@ -50,9 +50,9 @@ Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref-
 Services are items you offer to your customers.
 <UL><LI>svc_acct - Shell accounts, POP mailboxes, SLIP/PPP and ISDN accounts
     <LI>svc_domain - Domains
+    <LI>svc_acct_sm - <B>deprecated</B> (use svc_forward for new installations) Virtual domain mail aliasing.
     <LI>svc_forward - mail forwarding
     <LI>svc_www - Virtual domain website
-    <LI>svc_broadband - Broadband/High-speed Internet service
 <!--   <LI>svc_charge - One-time charges (Partially unimplemented)
        <LI>svc_wo - Work orders (Partially unimplemented)
 -->
@@ -104,6 +104,11 @@ my %defs = (
   'svc_domain' => {
     'domain'    => 'Domain',
   },
+  'svc_acct_sm' => {
+    'domuser'   => 'domuser@virtualdomain.com',
+    'domuid'    => 'UID where domuser@virtualdomain.com mail is forwarded',
+    'domsvc'    => 'svcnum from svc_domain for virtualdomain.com',
+  },
   'svc_forward' => {
     'srcsvc'    => 'service from which mail is to be forwarded',
     'dstsvc'    => 'service to which mail is to be forwarded',
@@ -120,21 +125,11 @@ my %defs = (
     #'recnum' => '',
     #'usersvc' => '',
   },
-  'svc_broadband' => {
-    'actypenum' => 'This is the actypenum that refers to the type of AC that can be provisioned for this service.  This field must be set fixed.',
-    'speed_down' => 'Maximum download speed for this service in Kbps.  0 denotes unlimited.',
-    'speed_up' => 'Maximum upload speed for this service in Kbps.  0 denotes unlimited.',
-    'acnum' => 'acnum of a specific AC that this service is restricted to.  Not required',
-    'ip_addr' => 'IP address.  Leave blank for automatic assignment.',
-    'ip_netmask' => 'Mask length, aka. netmask bits.  (Eg. 255.255.255.0 == 24)',
-    'mac_addr' => 'MAC address which is used by some ACs for access control.  Specified by 6 colon seperated hex octets. (Eg. 00:00:0a:bc:1a:2b)',
-    'location' => 'Defines the physically location at which this service was installed.  This is not necessarily the billing address',
-  },
 );
 
   my @dbs = $hashref->{svcdb}
              ? ( $hashref->{svcdb} )
-             : qw( svc_acct svc_domain svc_forward svc_www svc_broadband );
+             : qw( svc_acct svc_domain svc_acct_sm svc_forward svc_www );
 
   tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } @dbs;
   my $widget = new HTML::Widgets::SelectLayers(
diff --git a/httemplate/edit/process/ac.cgi b/httemplate/edit/process/ac.cgi
deleted file mode 100755 (executable)
index fc434a8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<%
-
-my $acnum = $cgi->param('acnum');
-
-my $old = qsearchs('ac',{'acnum'=>$acnum}) if $acnum;
-
-my $new = new FS::ac ( {
-  map {
-    $_, scalar($cgi->param($_));
-  } fields('ac')
-} );
-
-my $error = '';
-if ( $acnum ) {
-  $error = $new->replace($old);
-} else {
-  $error = $new->insert;
-  $acnum=$new->getfield('acnum');
-}
-
-if ( $error ) {
-  $cgi->param('error', $error);
-  print $cgi->redirect(popurl(2). "ac.cgi?". $cgi->query_string );
-} else {
-  print $cgi->redirect(popurl(3). "browse/ac.cgi");
-}
-
-%>
diff --git a/httemplate/edit/process/ac_block.cgi b/httemplate/edit/process/ac_block.cgi
deleted file mode 100755 (executable)
index b1c3c72..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<%
-
-my $new = new FS::ac_block ( {
-  map {
-    $_, scalar($cgi->param($_));
-  } fields('ac_block')
-} );
-
-my $error = '';
-$error = $new->check;
-
-unless ( $error ) { $error = $new->insert; }
-
-if ( $error ) {
-  $cgi->param('error', $error);
-  print $cgi->redirect(popurl(2). "ac.cgi?". $cgi->query_string );
-} else {
-  print $cgi->redirect(popurl(2). "ac.cgi?". $cgi->param('acnum'));
-}
-
-%>
diff --git a/httemplate/edit/process/ac_field.cgi b/httemplate/edit/process/ac_field.cgi
deleted file mode 100755 (executable)
index 2bfe331..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<%
-
-my $new = new FS::ac_field ( {
-  map {
-    $_, scalar($cgi->param($_));
-  } fields('ac_field')
-} );
-
-my $error = '';
-$error = $new->check;
-
-unless ( $error ) { $error = $new->insert; }
-
-if ( $error ) {
-  $cgi->param('error', $error);
-  print $cgi->redirect(popurl(2). "ac.cgi?". $cgi->query_string );
-} else {
-  print $cgi->redirect(popurl(2). "ac.cgi?". $cgi->param('acnum'));
-}
-
-%>
diff --git a/httemplate/edit/process/ac_type.cgi b/httemplate/edit/process/ac_type.cgi
deleted file mode 100755 (executable)
index ca232ba..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<%
-
-my $actypenum = $cgi->param('actypenum');
-
-my $old = qsearchs('ac_type',{'actypenum'=>$actypenum}) if $actypenum;
-
-my $new = new FS::ac_type ( {
-  map {
-    $_, scalar($cgi->param($_));
-  } fields('ac_type')
-} );
-
-my $error = '';
-if ( $actypenum ) {
-  $error = $new->replace($old);
-} else {
-  $error = $new->insert;
-  $actypenum=$new->getfield('actypenum');
-}
-
-if ( $error ) {
-  $cgi->param('error', $error);
-  print $cgi->redirect(popurl(2). "ac_type.cgi?". $cgi->query_string );
-} else {
-  print $cgi->redirect(popurl(3). "browse/ac_type.cgi");
-}
-
-%>
index 6d80ad5..990a239 100755 (executable)
@@ -2,18 +2,17 @@
 
 foreach ( grep { /^tax\d+$/ } $cgi->param ) {
   /^tax(\d+)$/ or die "Illegal form $_!";
-  my $taxnum = $1;
-  my $old = qsearchs('cust_main_county', { 'taxnum' => $taxnum })
+  my($taxnum)=$1;
+  my($old)=qsearchs('cust_main_county',{'taxnum'=>$taxnum})
     or die "Couldn't find taxnum $taxnum!";
-  next unless    $old->tax           != $cgi->param("tax$taxnum")
-              || $old->exempt_amount != $cgi->param("exempt_amount$taxnum")
-              || $old->taxname       ne $cgi->param("taxname$taxnum");
+  my $exempt_amount = $cgi->param("exempt_amount$taxnum");
+  next unless $old->tax ne $cgi->param("tax$taxnum")
+              || $old->exempt_amount ne $exempt_amount;
   my %hash = $old->hash;
   $hash{tax} = $cgi->param("tax$taxnum");
-  $hash{exempt_amount} = $cgi->param("exempt_amount$taxnum");
-  $hash{taxname} = $cgi->param("taxname$taxnum");
-  my $new = new FS::cust_main_county \%hash;
-  my $error = $new->replace($old);
+  $hash{exempt_amount} = $exempt_amount;
+  my($new)=new FS::cust_main_county \%hash;
+  my($error)=$new->replace($old);
   if ( $error ) {
     $cgi->param('error', $error);
     print $cgi->redirect(popurl(2). "cust_main_county.cgi?". $cgi->query_string );
diff --git a/httemplate/edit/process/part_ac_field.cgi b/httemplate/edit/process/part_ac_field.cgi
deleted file mode 100755 (executable)
index 38ad586..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<%
-
-my $new = new FS::part_ac_field ( {
-  map {
-    $_, scalar($cgi->param($_));
-  } fields('part_ac_field')
-} );
-
-my $error = '';
-$error = $new->check;
-
-unless ( $error ) { $error = $new->insert; }
-
-if ( $error ) {
-  $cgi->param('error', $error);
-  print $cgi->redirect(popurl(2). "ac_type.cgi?". $cgi->query_string );
-} else {
-  print $cgi->redirect(popurl(2). "ac_type.cgi?". $cgi->param('actypenum'));
-}
-
-%>
index 9633fab..859670b 100755 (executable)
@@ -17,7 +17,7 @@ my $new = new FS::part_svc ( {
             push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
             map { ( $svcdb.'__'.$_, $svcdb.'__'.$_.'_flag' )  } @fields;
           } grep defined( $FS::Record::dbdef->table($_) ),
-                 qw( svc_acct svc_domain svc_forward svc_www svc_broadband )
+                 qw( svc_acct svc_domain svc_acct_sm svc_forward svc_www )
     )
 } );
 
diff --git a/httemplate/edit/process/svc_acct_sm.cgi b/httemplate/edit/process/svc_acct_sm.cgi
new file mode 100755 (executable)
index 0000000..41d03fb
--- /dev/null
@@ -0,0 +1,34 @@
+<%
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum =$1;
+
+my $old = qsearchs('svc_acct_sm',{'svcnum'=>$svcnum}) if $svcnum;
+
+#unmunge domsvc and domuid
+#$cgi->param('domsvc',(split(/:/, $cgi->param('domsvc') ))[0] );
+#$cgi->param('domuid',(split(/:/, $cgi->param('domuid') ))[0] );
+
+my $new = new FS::svc_acct_sm ( {
+  map {
+    ($_, scalar($cgi->param($_)));
+  #} qw(svcnum pkgnum svcpart domuser domuid domsvc)
+  } ( fields('svc_acct_sm'), qw( pkgnum svcpart ) )
+} );
+
+my $error = '';
+if ( $svcnum ) {
+  $error = $new->replace($old);
+} else {
+  $error = $new->insert;
+  $svcnum = $new->getfield('svcnum');
+} 
+
+if ($error) {
+  $cgi->param('error', $error);
+  print $cgi->redirect(popurl(2). "svc_acct_sm.cgi?". $cgi->query_string );
+} else {
+  print $cgi->redirect(popurl(3). "view/svc_acct_sm.cgi?$svcnum");
+}
+
+%>
diff --git a/httemplate/edit/process/svc_broadband.cgi b/httemplate/edit/process/svc_broadband.cgi
deleted file mode 100644 (file)
index fd7ba20..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<%
-
-$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
-my $svcnum = $1;
-
-my $old;
-if ( $svcnum ) {
-  $old = qsearchs('svc_broadband', { 'svcnum' => $svcnum } )
-    or die "fatal: can't find broadband service (svcnum $svcnum)!";
-} else {
-  $old = '';
-}
-
-my $new = new FS::svc_broadband ( {
-  map {
-    ($_, scalar($cgi->param($_)));
-  } ( fields('svc_broadband'), qw( pkgnum svcpart ) )
-} );
-
-unless ( $new->ip_addr ) {
-  $new->ip_addr(join('.', (map $cgi->param('ip_addr_'.$_), (a..d))));
-}
-
-unless ( $new->mac_addr) {
-  $new->mac_addr(join(':', (map $cgi->param('mac_addr_'.$_), (a..f))));
-}
-
-my $error;
-if ( $svcnum ) {
-  $error = $new->replace($old);
-} else {
-  $error = $new->insert;
-  $svcnum = $new->svcnum;
-}
-
-if ( $error ) {
-  $cgi->param('error', $error);
-  $cgi->param('ip_addr', $new->ip_addr);
-  $cgi->param('mac_addr', $new->mac_addr);
-  print $cgi->redirect(popurl(2). "svc_broadband.cgi?". $cgi->query_string );
-} else {
-  print $cgi->redirect(popurl(3). "view/svc_broadband.cgi?" . $svcnum );
-}
-
-%>
diff --git a/httemplate/edit/svc_acct_sm.cgi b/httemplate/edit/svc_acct_sm.cgi
new file mode 100755 (executable)
index 0000000..0fd5f76
--- /dev/null
@@ -0,0 +1,178 @@
+<!-- mason kludge -->
+<%
+
+my $conf = new FS::Conf;
+my $mydomain = $conf->config('domain');
+
+my($svcnum, $pkgnum, $svcpart, $part_svc, $svc_acct_sm );
+if ( $cgi->param('error') ) {
+  $svc_acct_sm = new FS::svc_acct_sm ( {
+    map { $_, scalar($cgi->param($_)) } fields('svc_acct_sm')
+  } );
+  $svcnum = $svc_acct_sm->svcnum;
+  $pkgnum = $cgi->param('pkgnum');
+  $svcpart = $cgi->param('svcpart');
+  #$part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+  #die "No part_svc entry!" unless $part_svc;
+} else {
+  my($query) = $cgi->keywords;
+  if ( $query =~ /^(\d+)$/ ) { #editing
+    $svcnum=$1;
+    $svc_acct_sm=qsearchs('svc_acct_sm',{'svcnum'=>$svcnum})
+      or die "Unknown (svc_acct_sm) svcnum!";
+
+    my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+      or die "Unknown (cust_svc) svcnum!";
+
+    $pkgnum=$cust_svc->pkgnum;
+    $svcpart=$cust_svc->svcpart;
+  
+    #$part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+    #die "No part_svc entry!" unless $part_svc;
+
+  } else { #adding
+
+    $svc_acct_sm = new FS::svc_acct_sm({});
+
+    foreach $_ (split(/-/,$query)) { #get & untaint pkgnum & svcpart
+      $pkgnum=$1 if /^pkgnum(\d+)$/;
+      $svcpart=$1 if /^svcpart(\d+)$/;
+    }
+    my $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+    die "No part_svc entry!" unless $part_svc;
+
+    $svcnum='';
+
+    #set fixed and default fields from part_svc
+    foreach my $part_svc_column (
+      grep { $_->columnflag } $part_svc->all_part_svc_column
+    ) {
+      $svc_acct_sm->setfield( $part_svc_column->columnname,
+                              $part_svc_column->columnvalue,
+                            );
+    }
+
+  }
+}
+my $action = $svc_acct_sm->svcnum ? 'Edit' : 'Add';
+
+my %username = ();
+my %domain = ();
+if ($pkgnum) {
+
+  #find all possible uids (and usernames)
+
+  my @u_acct_svcparts = ();
+  foreach my $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) {
+    push @u_acct_svcparts,$u_part_svc->getfield('svcpart');
+  }
+
+  my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+  my($custnum)=$cust_pkg->getfield('custnum');
+  foreach my $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
+    my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
+    my($acct_svcpart);
+    foreach $acct_svcpart (@u_acct_svcparts) {   #now find the corresponding 
+                                              #record(s) in cust_svc ( for this
+                                              #pkgnum ! )
+      my($i_cust_svc);
+      foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) {
+        my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')});
+        $username{$svc_acct->getfield('uid')}=$svc_acct->getfield('username');
+      }  
+    }
+  }
+
+  #find all possible domains (and domsvc's)
+
+  my @d_acct_svcparts = ();
+  foreach my $d_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_domain'}) ) {
+    push @d_acct_svcparts,$d_part_svc->getfield('svcpart');
+  }
+
+  foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
+    my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
+    my($acct_svcpart);
+    foreach $acct_svcpart (@d_acct_svcparts) {
+      my($i_cust_svc);
+      foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) {
+        my($svc_domain)=qsearch('svc_domain',{'svcnum'=>$i_cust_svc->getfield('svcnum')});
+        $domain{$svc_domain->getfield('svcnum')}=$svc_domain->getfield('domain');
+      }
+    }
+  }
+
+} elsif ( $action eq 'Edit' ) {
+
+  my($svc_acct)=qsearchs('svc_acct',{'uid'=>$svc_acct_sm->domuid});
+  $username{$svc_acct_sm->uid} = $svc_acct->username;
+
+  my($svc_domain)=qsearchs('svc_domain',{'svcnum'=>$svc_acct_sm->domsvc});
+  $domain{$svc_acct_sm->domsvc} = $svc_domain->domain;
+
+} else {
+  die "\$action eq Add, but \$pkgnum is null!\n";
+}
+
+my $p1 = popurl(1);
+print header("Mail Alias $action", '');
+
+print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
+      "</FONT>"
+  if $cgi->param('error');
+
+print qq!<FORM ACTION="${p1}process/svc_acct_sm.cgi" METHOD=POST>!;
+
+#display
+
+       #formatting
+       print "<PRE>";
+
+#svcnum
+print qq!<INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">!;
+print qq!Service #<FONT SIZE=+1><B>!, $svcnum ? $svcnum : " (NEW)", "</B></FONT>";
+
+#pkgnum
+print qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!;
+#svcpart
+print qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!;
+
+my($domuser,$domsvc,$domuid)=(
+  $svc_acct_sm->domuser,
+  $svc_acct_sm->domsvc,
+  $svc_acct_sm->domuid,
+);
+
+#domuser
+print qq!\n\nMail to <INPUT TYPE="text" NAME="domuser" VALUE="$domuser"> <I>( * for anything )</I>!;
+
+#domsvc
+print qq! \@ <SELECT NAME="domsvc" SIZE=1>!;
+foreach $_ (keys %domain) {
+  print "<OPTION", $_ eq $domsvc ? " SELECTED" : "",
+        qq! VALUE="$_">$domain{$_}!;
+}
+print "</SELECT>";
+
+#uid
+print qq!\nforwards to <SELECT NAME="domuid" SIZE=1>!;
+foreach $_ (keys %username) {
+  print "<OPTION", ($_ eq $domuid) ? " SELECTED" : "",
+        qq! VALUE="$_">$username{$_}!;
+}
+print "</SELECT>\@$mydomain mailbox.";
+
+       #formatting
+       print "</PRE>\n";
+
+print qq!<CENTER><INPUT TYPE="submit" VALUE="Submit"></CENTER>!;
+
+print <<END;
+
+    </FORM>
+  </BODY>
+</HTML>
+END
+
+%>
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
deleted file mode 100644 (file)
index d8a1f7a..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-<!-- mason kludge -->
-<%
-
-my( $svcnum,  $pkgnum, $svcpart, $part_svc, $svc_broadband );
-if ( $cgi->param('error') ) {
-  $svc_broadband = new FS::svc_broadband ( {
-    map { $_, scalar($cgi->param($_)) } fields('svc_broadband')
-  } );
-  $svcnum = $svc_broadband->svcnum;
-  $pkgnum = $cgi->param('pkgnum');
-  $svcpart = $cgi->param('svcpart');
-  $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
-  die "No part_svc entry!" unless $part_svc;
-} else {
-  my($query) = $cgi->keywords;
-  if ( $query =~ /^(\d+)$/ ) { #editing
-    $svcnum=$1;
-    $svc_broadband=qsearchs('svc_broadband',{'svcnum'=>$svcnum})
-      or die "Unknown (svc_broadband) svcnum!";
-
-    my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
-      or die "Unknown (cust_svc) svcnum!";
-
-    $pkgnum=$cust_svc->pkgnum;
-    $svcpart=$cust_svc->svcpart;
-  
-    $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
-    die "No part_svc entry!" unless $part_svc;
-
-  } else { #adding
-
-    $svc_broadband = new FS::svc_broadband({});
-
-    foreach $_ (split(/-/,$query)) { #get & untaint pkgnum & svcpart
-      $pkgnum=$1 if /^pkgnum(\d+)$/;
-      $svcpart=$1 if /^svcpart(\d+)$/;
-    }
-    $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
-    die "No part_svc entry!" unless $part_svc;
-
-    $svcnum='';
-
-    #set fixed and default fields from part_svc
-    foreach my $part_svc_column (
-      grep { $_->columnflag } $part_svc->all_part_svc_column
-    ) {
-      $svc_broadband->setfield( $part_svc_column->columnname,
-                                $part_svc_column->columnvalue,
-                              );
-    }
-
-  }
-}
-my $action = $svc_broadband->svcnum ? 'Edit' : 'Add';
-
-my @ac_list;
-
-if ($pkgnum) {
-
-  unless ($svc_broadband->actypenum) {die "actypenum must be set fixed";};
-  @ac_list = qsearch('ac', { actypenum => $svc_broadband->getfield('actypenum') });
-
-} elsif ( $action eq 'Edit' ) {
-
-  #Nothing?
-
-} else {
-  die "\$action eq Add, but \$pkgnum is null!\n";
-}
-
-
-my $p1 = popurl(1);
-print header("Broadband Service $action", '');
-
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
-      "</FONT>"
-  if $cgi->param('error');
-
-print qq!<FORM ACTION="${p1}process/svc_broadband.cgi" METHOD=POST>!;
-
-#display
-
-
-#svcnum
-print qq!<INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">!;
-print qq!Service #<B>!, $svcnum ? $svcnum : "(NEW)", "</B><BR><BR>";
-
-#pkgnum
-print qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!;
-#svcpart
-print qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!;
-
-#actypenum
-print '<INPUT TYPE="hidden" NAME="actypenum" VALUE="' .
-      $svc_broadband->actypenum . '">';
-
-
-print &ntable("#cccccc",2) . qq!<TR><TD ALIGN="right">AC</TD><TD>!;
-
-#acnum
-if (( $part_svc->part_svc_column('acnum')->columnflag eq 'F' ) or
-    ( !$pkgnum )) {
-
-  my $ac = qsearchs('ac', { acnum => $svc_broadband->acnum });
-  my ($acnum, $acname) = ($ac->acnum, $ac->acname);
-
-  print qq!<INPUT TYPE="hidden" NAME="acnum" VALUE="${acnum}">! .
-        qq!${acnum}: ${acname}</TD></TR>!;
-
-} else {
-
-  my @ac_list = qsearch('ac', { actypenum => $svc_broadband->actypenum });
-  print qq!<SELECT NAME="acnum" SIZE="1"><OPTION VALUE=""></OPTION>!;
-
-  foreach ( @ac_list ) {
-    my ($acnum, $acname) = ($_->acnum, $_->acname);
-    print qq!<OPTION VALUE="${acnum}"! .
-          ($acnum == $svc_broadband->acnum ? ' SELECTED>' : '>') .
-          qq!${acname}</OPTION>!;
-  }
-  print '</TD></TR>';
-
-}
-
-#speed_up & speed_down
-my ($speed_up, $speed_down) = ($svc_broadband->speed_up,
-                               $svc_broadband->speed_down);
-
-print '<TR><TD ALIGN="right">Download speed</TD><TD>';
-if ( $part_svc->part_svc_column('speed_down')->columnflag eq 'F' ) {
-  print qq!<INPUT TYPE="hidden" NAME="speed_down" VALUE="${speed_down}">! .
-        qq!${speed_down}Kbps</TD></TR>!;
-} else {
-  print qq!<INPUT TYPE="text" NAME="speed_down" SIZE=5 VALUE="${speed_down}">! .
-        qq!Kbps</TD></TR>!;
-}
-
-print '<TR><TD ALIGN="right">Upload speed</TD><TD>';
-if ( $part_svc->part_svc_column('speed_up')->columnflag eq 'F' ) {
-  print qq!<INPUT TYPE="hidden" NAME="speed_up" VALUE="${speed_up}">! .
-        qq!${speed_up}Kbps</TD></TR>!;
-} else {
-  print qq!<INPUT TYPE="text" NAME="speed_up" SIZE=5 VALUE="${speed_up}">! .
-        qq!Kbps</TD></TR>!;
-}
-
-#ip_addr & ip_netmask
-#We're assuming that ip_netmask is fixed if ip_addr is fixed.
-#If it isn't, well, <shudder> what the heck are you doing!?!?
-
-my ($ip_addr, $ip_netmask) = ($svc_broadband->ip_addr,
-                              $svc_broadband->ip_netmask);
-
-print '<TR><TD ALIGN="right">IP address/Mask</TD><TD>';
-if ( $part_svc->part_svc_column('ip_addr')->columnflag eq 'F' ) {
-  print qq!<INPUT TYPE="hidden" NAME="ip_addr" VALUE="${ip_addr}">! .
-        qq!<INPUT TYPE="hidden" NAME="ip_netmask" VALUE="${ip_netmask}">! .
-        qq!${ip_addr}/${ip_netmask}</TD></TR>!;
-} else {
-  $ip_addr =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
-  print <<END;
-  <INPUT TYPE="text" NAME="ip_addr_a" SIZE="3" MAXLENGTH="3" VALUE="${1}">.
-  <INPUT TYPE="text" NAME="ip_addr_b" SIZE="3" MAXLENGTH="3" VALUE="${2}">.
-  <INPUT TYPE="text" NAME="ip_addr_c" SIZE="3" MAXLENGTH="3" VALUE="${3}">.
-  <INPUT TYPE="text" NAME="ip_addr_d" SIZE="3" MAXLENGTH="3" VALUE="${4}">/
-  <INPUT TYPE="text" NAME="ip_netmask" SIZE="2" MAXLENGTH="2" VALUE="${ip_netmask}">
-</TD></TR>
-<TR><TD COLSPAN="2" WIDTH="300">
-<P><SMALL>Leave the IP address and netmask blank for automatic assignment of a /32 address.  Specifing the netmask and not the address will force assignment of a larger block.</SMALL></P>
-</TD></TR>
-END
-}
-
-#mac_addr
-my $mac_addr = $svc_broadband->mac_addr;
-
-unless (( $part_svc->part_svc_column('mac_addr')->columnflag eq 'F' ) and
-        ( $mac_addr eq '' )) {
-  print '<TR><TD ALIGN="right">MAC Address</TD><TD>';
-  if ( $part_svc->part_svc_column('mac_addr')->columnflag eq 'F' ) { #Why?
-    print qq!<INPUT TYPE="hidden" NAME="mac_addr" VALUE="${mac_addr}">! .
-          qq!${mac_addr}</TD></TR>!;
-  } else {
-    #Ewwww
-    $mac_addr =~ /^([a-f0-9]{2}):([a-f0-9]{2}):([a-f0-9]{2}):([a-f0-9]{2}):([a-f0-9]{2}):([a-f0-9]{2})$/i;
-    print <<END;
-  <INPUT TYPE="text" NAME="mac_addr_a" SIZE="2" MACLENGTH="2" VALUE="${1}">:
-  <INPUT TYPE="text" NAME="mac_addr_b" SIZE="2" MACLENGTH="2" VALUE="${2}">:
-  <INPUT TYPE="text" NAME="mac_addr_c" SIZE="2" MACLENGTH="2" VALUE="${3}">:
-  <INPUT TYPE="text" NAME="mac_addr_d" SIZE="2" MACLENGTH="2" VALUE="${4}">:
-  <INPUT TYPE="text" NAME="mac_addr_e" SIZE="2" MACLENGTH="2" VALUE="${5}">:
-  <INPUT TYPE="text" NAME="mac_addr_f" SIZE="2" MACLENGTH="2" VALUE="${6}">
-</TD></TR>
-END
-
-  }
-}
-
-#location
-my $location = $svc_broadband->location;
-
-print '<TR><TD VALIGN="top" ALIGN="right">Location</TD><TD BGCOLOR="#e8e8e8">';
-if ( $part_svc->part_svc_column('location')->columnflag eq 'F' ) {
-  print qq!<PRE>${location}</PRE></TD></TR>!;
-} else {
-  print qq!<TEXTAREA ROWS="4" COLS="30" NAME="location">${location}</TEXTAREA>!;
-}
-
-print '</TABLE><BR><INPUT TYPE="submit" VALUE="Submit">';
-
-print <<END;
-
-    </FORM>
-  </BODY>
-</HTML>
-END
-%>
index 0d815b9..bc19fe1 100755 (executable)
@@ -2,6 +2,7 @@
 <%
 
 my $conf = new FS::Conf;
+my $mydomain = $conf->config('domain');
 
 my($svcnum, $pkgnum, $svcpart, $part_svc, $svc_forward);
 if ( $cgi->param('error') ) {
diff --git a/httemplate/graph/money_time-graph.cgi b/httemplate/graph/money_time-graph.cgi
deleted file mode 100755 (executable)
index 944019a..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<%
-
-#my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
-my ($curmon,$curyear) = (localtime(time))[4,5];
-
-#find first month
-my $syear = $cgi->param('syear') || 1899+$curyear;
-my $smonth = $cgi->param('smonth') || $curmon+1;
-
-#find last month
-my $eyear = $cgi->param('eyear') || 1900+$curyear;
-my $emonth = $cgi->param('emonth') || $curmon+1;
-if ( $emonth++>12 ) { $emonth-=12; $eyear++; }
-
-my @labels;
-my %data;
-
-while ( $syear < $eyear || ( $syear == $eyear && $smonth < $emonth ) ) {
-  push @labels, "$smonth/$syear";
-
-  my $speriod = timelocal(0,0,0,1,$smonth-1,$syear);
-  if ( ++$smonth == 13 ) { $syear++; $smonth=1; }
-  my $eperiod = timelocal(0,0,0,1,$smonth-1,$syear);
-
-  my $where = "WHERE _date >= $speriod AND _date < $eperiod";
-
-  # Invoiced
-  my $charged_sql = "SELECT SUM(charged) FROM cust_bill $where";
-  my $charged_sth = dbh->prepare($charged_sql) or die dbh->errstr;
-  $charged_sth->execute or die $charged_sth->errstr;
-  my $charged = $charged_sth->fetchrow_arrayref->[0] || 0;
-
-  push @{$data{charged}}, $charged;
-
-  #accounts receivable
-#  my $ar_sql2 = "SELECT SUM(amount) FROM cust_credit $where";
-  my $credited_sql = "SELECT SUM(cust_credit_bill.amount) FROM cust_credit_bill, cust_bill WHERE cust_bill.invnum = cust_credit_bill.invnum AND cust_bill._date >= $speriod AND cust_bill._date < $eperiod";
-  my $credited_sth = dbh->prepare($credited_sql) or die dbh->errstr;
-  $credited_sth->execute or die $credited_sth->errstr;
-  my $credited = $credited_sth->fetchrow_arrayref->[0] || 0;
-
-    #horrible local kludge
-    my $expenses_sql = "SELECT SUM(cust_bill_pkg.setup) FROM cust_bill_pkg, cust_bill, cust_pkg, part_pkg WHERE cust_bill.invnum = cust_bill_pkg.invnum AND cust_bill._date >= $speriod AND cust_bill._date < $eperiod AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%'";
-    my $expenses_sth = dbh->prepare($expenses_sql) or die dbh->errstr;
-    $expenses_sth->execute or die $expenses_sth->errstr;
-    my $expenses = $expenses_sth->fetchrow_arrayref->[0] || 0;
-
-  push @{$data{ar}}, $charged-$credited-$expenses;
-
-  #deferred revenue
-#  push @{$data{defer}}, '0';
-
-  #cashflow
-  my $paid_sql = "SELECT SUM(paid) FROM cust_pay $where";
-  my $paid_sth = dbh->prepare($paid_sql) or die dbh->errstr;
-  $paid_sth->execute or die $paid_sth->errstr;
-  my $paid = $paid_sth->fetchrow_arrayref->[0] || 0;
-
-  my $refunded_sql = "SELECT SUM(refund) FROM cust_refund $where";
-  my $refunded_sth = dbh->prepare($refunded_sql) or die dbh->errstr;
-  $refunded_sth->execute or die $refunded_sth->errstr;
-  my $refunded = $refunded_sth->fetchrow_arrayref->[0] || 0;
-
-    #horrible local kludge that doesn't even really work right
-    my $expenses_sql = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND 0 < ( select count(*) from cust_bill_pkg, cust_pkg, part_pkg WHERE cust_bill.invnum = cust_bill_pkg.invnum AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%' )";
-
-#    my $expenses_sql = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill_pkg, cust_bill, cust_pkg, part_pkg WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill.invnum = cust_bill_pkg.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%'";
-    my $expenses_sth = dbh->prepare($expenses_sql) or die dbh->errstr;
-    $expenses_sth->execute or die $expenses_sth->errstr;
-    my $expenses = $expenses_sth->fetchrow_arrayref->[0] || 0;
-
-  push @{$data{cash}}, $paid-$refunded-$expenses;
-
-}
-
-#my $chart = Chart::LinesPoints->new(1024,480);
-my $chart = Chart::LinesPoints->new(768,480);
-
-$chart->set(
-  #'min_val' => 0,
-  'legend' => 'bottom',
-  'legend_labels' => [ #'Invoiced (cust_bill)',
-                       'Accounts receivable (invoices - applied credits)',
-                       #'Deferred revenue',
-                       'Actual cashflow (payments - refunds)' ],
-);
-
-my @data = ( \@labels,
-             #map $data{$_}, qw( ar defer cash )
-             #map $data{$_}, qw( charged ar cash )
-             map $data{$_}, qw( ar cash )
-           );
-
-#my $gd = $chart->plot(\@data);
-#open (IMG, ">i_r_c.png");
-#print IMG $gd->png;
-#close IMG;
-
-#$chart->png("i_r_c.png", \@data);
-
-#$chart->cgi_png(\@data);
-
-http_header('Content-Type' => 'image/png' );
-$Response->{ContentType} = 'image/png';
-
-$chart->_set_colors();
-
-%><%= $chart->scalar_png(\@data) %>
diff --git a/httemplate/graph/money_time.cgi b/httemplate/graph/money_time.cgi
deleted file mode 100644 (file)
index e24157c..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-<!-- mason kludge %>
-<%
-
-#my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
-my ($curmon,$curyear) = (localtime(time))[4,5];
-
-#find first month
-my $syear = $cgi->param('syear') || 1899+$curyear;
-my $smonth = $cgi->param('smonth') || $curmon+1;
-
-#find last month
-my $eyear = $cgi->param('eyear') || 1900+$curyear;
-my $emonth = $cgi->param('emonth') || $curmon+1;
-
-%>
-
-<HTML>
-  <HEAD>
-    <TITLE>Graphing monetary values over time</TITLE>
-  </HEAD>
-<BODY BGCOLOR="#e8e8e8">
-<IMG SRC="money_time-graph.cgi?<%= $cgi->query_string %>" WIDTH="768" HEIGHT="480">
-<BR>
-<FORM METHOD="POST">
-<INPUT TYPE="checkbox" NAME="ar">
-  Accounts receivable (invoices - applied credits)<BR>
-<INPUT TYPE="checkbox" NAME="charged">
-  Just Invoices<BR>
-<INPUT TYPE="checkbox" NAME="defer">
-  Accounts receivable, with deferred revenue (invoices - applied credits, with charges for annual/semi-annual/quarterly/etc. services deferred over applicable time period) (there has got to be a shorter description for this)<BR>
-<INPUT TYPE="checkbox" NAME="cash">
-  Cashflow (payments - refunds)<BR>
-<BR>
-From <SELECT NAME="smonth">
-<% my @m = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
-   foreach my $m ( 1..12 ) { %>
-<OPTION VALUE="<%= $m %>"<%= $m == $smonth ? ' SELECTED' : '' %>><%= $m[$m-1] %>
-<% } %>
-</SELECT>
-<SELECT NAME="syear">
-<% foreach my $y ( 1999 .. 2010 ) { %>
-<OPTION VALUE="<%= $y %>"<%= $y == $syear ? ' SELECTED' : '' %>><%= $y %>
-<% } %>
-</SELECT>
- to <SELECT NAME="emonth">
-<% foreach my $m ( 1..12 ) { %>
-<OPTION VALUE="<%= $m %>"<%= $m == $emonth ? ' SELECTED' : '' %>><%= $m[$m-1] %>
-<% } %>
-</SELECT>
-<SELECT NAME="eyear">
-<% foreach my $y ( 1999 .. 2010 ) { %>
-<OPTION VALUE="<%= $y %>"<%= $y == $eyear ? ' SELECTED' : '' %>><%= $y %>
-<% } %>
-</SELECT>
-
-<INPUT TYPE="submit" VALUE="Graph">
-</FORM>
-</BODY>
-</HTML>
index e8c3681..6890e80 100644 (file)
@@ -34,6 +34,7 @@
         <FORM ACTION="search/cust_main.cgi" METHOD="POST"><INPUT TYPE="hidden" NAME="phone_on" VALUE="1">Phone # <INPUT TYPE="text" NAME="phone_text"><INPUT TYPE="submit" VALUE="Search"></FORM>
         <BR><FORM ACTION="search/svc_acct.cgi" METHOD="POST">Username <INPUT TYPE="text" NAME="username"><SELECT NAME="username_type"><OPTION VALUE="All">(all)</OPTION><OPTION>Fuzzy</OPTION><OPTION>Substring</OPTION><OPTION SELECTED>Exact</OPTION></SELECT><INPUT TYPE="submit" VALUE="Search"> or <A HREF="search/svc_acct.cgi?username">all accounts by username</A> or <A HREF="search/svc_acct.cgi?uid">uid</A></FORM>
         <BR><FORM ACTION="search/svc_domain.cgi" METHOD="POST">Domain <INPUT TYPE="text" NAME="domain"><INPUT TYPE="submit" VALUE="Search"> or <A HREF="search/svc_domain.cgi?domain">all domains</A></FORM>
+<!--        <LI><A HREF="search/svc_acct_sm.html">mail aliases (by domain, and optionally username)</A>-->
 <!--        <LI><A HREF="search/svc_forward.html">mail forwards (by ?)</A>-->
       <BR>
     </TD></TR>
               into counties and assign different tax rates to each.
           <LI><A HREF="browse/svc_acct_pop.cgi">View/Edit Access Numbers</A>
             - Points of Presence 
-          <LI><A HREF="browse/ac_type.cgi">View/Edit AC Types</A>
-            - Broadband service access concentrator types.
-          <LI><A HREF="browse/ac.cgi">View/Edit AC</A>
-            - Broadband service access concentrators.
           <LI><A HREF="browse/part_bill_event.cgi">View/Edit invoice events</A> - Actions for overdue invoices
           <LI><A HREF="browse/msgcat.cgi">View/Edit message catalog</A> - Change error messages and other customizable labels.
         </ul>
index 79adce8..efc762c 100755 (executable)
@@ -4,6 +4,7 @@
 my %link_field = (
   'svc_acct'    => 'username',
   'svc_domain'  => 'domain',
+  'svc_acct_sm' => '',
   'svc_charge'  => '',
   'svc_wo'      => '',
 );
diff --git a/httemplate/misc/meta-import.cgi b/httemplate/misc/meta-import.cgi
deleted file mode 100644 (file)
index ebd2a7e..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-<!-- mason kludge -->
-<%= header('Import') %>
-<FORM ACTION="process/meta-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
-Import a CSV file containing customer records (zip tar etc).<BR><BR>
-
-##no##Default file format is CSV, with the following field order: <i>cust_pkg.setup, dayphone, first, last, address1, address2, city, state, zip, comments</i><BR><BR>
-
-<%
-  #false laziness with edit/cust_main.cgi
-  my @agents = qsearch( 'agent', {} );
-  die "No agents created!" unless @agents;
-  my $agentnum = $agents[0]->agentnum; #default to first
-
-  if ( scalar(@agents) == 1 ) {
-%>
-    <INPUT TYPE="hidden" NAME="agentnum" VALUE="<%= $agentnum %>">
-<% } else { %>
-    <BR><BR>Agent <SELECT NAME="agentnum" SIZE="1">
-  <% foreach my $agent (sort { $a->agent cmp $b->agent } @agents) { %>
-    <OPTION VALUE="<%= $agent->agentnum %>" <%= " SELECTED"x($agent->agentnum==$agentnum) %>><%= $agent->agent %></OPTION>
-  <% } %>
-    </SELECT><BR><BR>
-<% } %>
-
-<%
-  my @referrals = qsearch('part_referral',{});
-  die "No advertising sources created!" unless @referrals;
-  my $refnum = $referrals[0]->refnum; #default to first
-
-  if ( scalar(@referrals) == 1 ) {
-%>
-    <INPUT TYPE="hidden" NAME="refnum" VALUE="<%= $refnum %>">
-<% } else { %>
-    <BR><BR>Advertising source <SELECT NAME="refnum" SIZE="1">
-  <% foreach my $referral ( sort { $a->referral <=> $b->referral } @referrals) { %>
-    <OPTION VALUE="<%= $referral->refnum %>" <%= " SELECTED"x($referral->refnum==$refnum) %>><%= $referral->refnum %>: <%= $referral->referral %></OPTION>
-  <% } %>
-    </SELECT><BR><BR>
-<% } %>
-
-    First package: <SELECT NAME="pkgpart"><OPTION VALUE="">(none)</OPTION>
-<% foreach my $part_pkg ( qsearch('part_pkg',{'disabled'=>'' }) ) { %>
-     <OPTION VALUE="<%= $part_pkg->pkgpart %>"><%= $part_pkg->pkg. ' - '. $part_pkg->comment %></OPTION>
-<% } %>
-</SELECT><BR><BR>
-
-    CSV (zip, tar etc) Filename: <INPUT TYPE="file" NAME="csvfile"><BR><BR>
-    <INPUT TYPE="submit" VALUE="Import">
-    </FORM>
-  </BODY>
-<HTML>
-
diff --git a/httemplate/misc/process/meta-import.cgi b/httemplate/misc/process/meta-import.cgi
deleted file mode 100644 (file)
index 4c3c7d6..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-<!-- mason kludge -->
-<%= header('Map tables') %>
-<SCRIPT>
-var gSafeOnload = new Array();
-var gSafeOnsubmit = new Array();
-window.onload = SafeOnload;
-function SafeAddOnLoad(f) {
-  gSafeOnload[gSafeOnload.length] = f;
-}
-function SafeOnload() {
-  for (var i=0;i<gSafeOnload.length;i++)
-    gSafeOnload[i]();
-}
-function SafeAddOnSubmit(f) {
-  gSafeOnsubmit[gSafeOnsubmit.length] = f;
-}
-function SafeOnsubmit() {
-  for (var i=0;i<gSafeOnsubmit.length;i++)
-    gSafeOnsubmit[i]();
-}
-</SCRIPT>
-<FORM NAME="OneTrueForm" METHOD="POST" ACTION="meta-import.cgi">
-<%
-  #one
-  unless ( $cgi->param('magic') ) {
-
-    #oops, silly
-    #my $fh = $cgi->upload('csvfile');
-    ##warn $cgi;
-    ##warn $fh;
-    #
-    #use Archive::Tar;
-    #$tar = Archive::Tar->new();
-    #$tar->create_archive($fh); #or die $tar->error;
-
-    #haha for now
-    my @files = qw(
-authserv  credtype  dunprev  invoice  pmtdet    product   taxplan
-ccdet     customer  genlog   ledger   pops      pubvars   
-cchist    discplan  glacct   origco   prodcat   recur     users
-credcode  dundet    invline  payment  prodclas  repforms  webserv
-    );
-
-    %>
-    <INPUT TYPE="hidden" NAME="magic" VALUE="process">
-    <%= hashmaker('schema', \@files, [ grep { ! /^h_/ } dbdef->tables ] ) %>
-    <br><INPUT TYPE="submit" VALUE="done">
-    <%
-
-  } elsif ( $cgi->param('magic') eq 'process' ) {
-
-    %>
-    <INPUT TYPE="hidden" NAME="magic" VALUE="process2">
-    <%
-
-    my $schema_string = $cgi->param('schema');
-    %><INPUT TYPE="hidden" NAME="schema" VALUE="<%=$schema_string%>"><%
-    my %schema = map { /^\s*(\w+)\s*=>\s*(\w+)\s*$/
-                         or die "guru meditation #420: $_";
-                       ( $1 => $2 );
-                     }
-                 split( /\n/, $schema_string );
-
-    #*** should be in global.asa/handler.pl like the rest
-    eval 'use Text::CSV_XS;';
-
-    foreach my $table ( keys %schema ) {
-
-      my $csv = Text::CSV_XS->new({ 'binary'=>1 });
-      open(FILE,"</home/ivan/intergate/legacy/csvdir/$table")
-        or die "can't /home/ivan/intergate/legacy/csvdir/$table: $!";
-      my $header = lc(<FILE>);
-      close FILE;
-      $csv->parse($header) or die;
-      my @from_columns = $csv->fields;
-
-      my @fs_columns = dbdef->table($schema{$table})->columns;
-
-      %>
-      <%= hashmaker($table, \@from_columns, \@fs_columns, $table, $schema{$table} ) %>
-      <br><hr><br>
-      <%
-
-    }
-
-    %>
-    <br><INPUT TYPE="submit" VALUE="done">
-    <%
-
-  } elsif ( $cgi->param('magic') eq 'process2' ) {
-
-    print "<pre>\n";
-    #false laziness with above
-    my $schema_string = $cgi->param('schema');
-    my %schema = map { /^\s*(\w+)\s*=>\s*(\w+)\s*$/
-                         or die "guru meditation #420: $_";
-                       ( $1 => $2 );
-                     }
-                 split( /\n/, $schema_string );
-    foreach my $table ( keys %schema ) {
-      ( my $spaces = $table ) =~ s/./ /g;
-      print "'$table' => { 'table' => '$schema{$table}',\n".
-            #(length($table) x ' '). "         'map'   => {\n";
-            "$spaces        'map'   => {\n";
-      my %map = map { /^\s*(\w+)\s*=>\s*(\w+)\s*$/
-                         or die "guru meditation #420: $_";
-                       ( $1 => $2 );
-                     }
-                 split( /\n/, $cgi->param($table) );
-      foreach ( keys %map ) {
-        print "$spaces                     '$_' => '$map{$_}',\n";
-      }
-      print "$spaces                   },\n";
-      print "$spaces      },\n";
-
-    }
-    print "\n</pre>";
-
-  } else {
-    warn "unrecognized magic: ". $cgi->param('magic');
-  }
-
-  %>
-</FORM>
-</BODY>
-</HTML>
-
-  <%
-  #hashmaker widget
-  sub hashmaker {
-    my($name, $from, $to, $labelfrom, $labelto) = @_;
-    $fromsize = scalar(@$from);
-    $tosize = scalar(@$to);
-    "<TABLE><TR><TH>$labelfrom</TH><TH>$labelto</TH></TR><TR><TD>".
-        qq!<SELECT NAME="${name}_from" SIZE=$fromsize>\n!.
-        join("\n", map { qq!<OPTION VALUE="$_">$_</OPTION>! } sort { $a cmp $b } @$from ).
-        "</SELECT>\n".
-      '</TD><TD>'.
-        qq!<SELECT NAME="${name}_to" SIZE=$tosize>\n!.
-        join("\n", map { qq!<OPTION VALUE="$_">$_</OPTION>! } sort { $a cmp $b } @$to ).
-        "</SELECT>\n".
-      '</TD></TR>'.
-      '<TR><TD COLSPAN=2>'.
-        qq!<INPUT TYPE="button" VALUE="map" onClick="toke_$name(this.form)">!.
-      '</TD></TR><TR><TD COLSPAN=2>'.
-      qq!<TEXTAREA NAME="$name" COLS=80 ROWS=8></TEXTAREA>!.
-      '</TD></TR></TABLE>'.
-      "<script>
-            function toke_$name() {
-              fromObject = document.OneTrueForm.${name}_from;
-              for (var i=fromObject.options.length-1;i>-1;i--) {
-                if (fromObject.options[i].selected)
-                  fromname = deleteOption_$name(fromObject,i);
-              }
-              toObject = document.OneTrueForm.${name}_to;
-              for (var i=toObject.options.length-1;i>-1;i--) {
-                if (toObject.options[i].selected)
-                  toname = deleteOption_$name(toObject,i);
-              }
-              document.OneTrueForm.$name.value = document.OneTrueForm.$name.value + fromname + ' => ' + toname + '\\n';
-            }
-            function deleteOption_$name(object,index) {
-              value = object.options[index].value;
-              object.options[index] = null;
-              return value;
-            }
-      </script>".
-      '';
-  }
-
-%>
index c2ab726..ff8c1fb 100755 (executable)
@@ -1,7 +1,8 @@
 <!-- mason kludge -->
 <%
 
-my $user = getotaker;
+#my $user = getotaker;
+my $user = $FS::UID::user; #dumb 1.4 8-char workaround
 
 $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/;
 my $beginning = $1;
index 2adafc0..05017f4 100755 (executable)
@@ -1,7 +1,8 @@
 <!-- mason kludge -->
 <%
 
-my $user = getotaker;
+#my $user = getotaker;
+my $user = $FS::UID::user; #dumb 1.4 8-char workaround
 
 $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/;
 my $beginning = $1;
index fdd3779..04a4136 100755 (executable)
@@ -1,7 +1,8 @@
 <!-- mason kludge -->
 <%
 
-my $user = getotaker;
+#my $user = getotaker;
+my $user = $FS::UID::user; #dumb 1.4 8-char workaround
 
 print header('Current Receivables Report Results');
 
index ac76fad..835554a 100755 (executable)
@@ -1,7 +1,8 @@
 <!-- mason kludge -->
 <%
 
-my $user = getotaker;
+#my $user = getotaker;
+my $user = $FS::UID::user; #dumb 1.4 8-char workaround
 
 $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/;
 my $beginning = $1;
index b2ed723..549231d 100755 (executable)
@@ -1,5 +1,7 @@
 <%
 
+my $mydomain = '';
+
 my $conf = new FS::Conf;
 my $maxrecords = $conf->config('maxsearchrecordsperpage');
 
@@ -145,8 +147,14 @@ END
       $domain = "<A HREF=\"${p}view/svc_domain.cgi?". $svc_domain->svcnum.
                 "\">". $svc_domain->domain. "</A>";
     } else {
-      die "No svc_domain.svcnum record for svc_acct.domsvc: ".
-          $svc_acct->domsvc;
+      unless ( $mydomain ) {
+        my $conf = new FS::Conf;
+        unless ( $mydomain = $conf->config('domain') ) {
+          die "No legacy domain config file and no svc_domain.svcnum record ".
+              "for svc_acct.domsvc: ". $svc_acct->domsvc;
+        }
+      }
+      $domain = "<i>$mydomain</i><FONT COLOR=\"#FF0000\">*</FONT>";
     }
     my($cust_pkg,$cust_main);
     if ( $cust_svc->pkgnum ) {
@@ -208,8 +216,17 @@ END
 
   }
  
-  print "</TABLE>$pager<BR>".
-        '</BODY></HTML>';
+  print "</TABLE>$pager<BR>";
+
+  if ( $mydomain ) {
+    print "<BR><FONT COLOR=\"#FF0000\">*</FONT> The <I>$mydomain</I> domain ".
+          "is contained in your legacy <CODE>domain</CODE> ".
+          "<A HREF=\"${p}docs/config.html#domain\">configuration file</A>.  ".
+          "You should run the <CODE>bin/fs-migrate-svc_acct_sm</CODE> script ".
+          "to create a proper svc_domain record for this domain.";
+  }
+
+  print '</BODY></HTML>';
 
 }
 
diff --git a/httemplate/search/svc_acct_sm.cgi b/httemplate/search/svc_acct_sm.cgi
new file mode 100755 (executable)
index 0000000..4ee3006
--- /dev/null
@@ -0,0 +1,84 @@
+<%
+
+my $conf = new FS::Conf;
+my $mydomain = $conf->config('domain');
+
+$cgi->param('domuser') =~ /^([a-z0-9_\-]{0,32})$/;
+my $domuser = $1;
+
+$cgi->param('domain') =~ /^([\w\-\.]+)$/ or die "Illegal domain";
+my $svc_domain = qsearchs('svc_domain',{'domain'=>$1})
+  or die "Unknown domain";
+my $domsvc = $svc_domain->svcnum;
+
+my @svc_acct_sm;
+if ($domuser) {
+  @svc_acct_sm=qsearch('svc_acct_sm',{
+    'domuser' => $domuser,
+    'domsvc'  => $domsvc,
+  });
+} else {
+  @svc_acct_sm=qsearch('svc_acct_sm',{'domsvc' => $domsvc});
+}
+
+if ( scalar(@svc_acct_sm) == 1 ) {
+  my($svcnum)=$svc_acct_sm[0]->svcnum;
+  print $cgi->redirect(popurl(2). "view/svc_acct_sm.cgi?$svcnum");
+} elsif ( scalar(@svc_acct_sm) > 1 ) {
+%>
+<!-- mason kludge -->
+<%
+  print header('Mail Alias Search Results'), &table(), <<END;
+      <TR>
+        <TH>Mail to<BR><FONT SIZE=-1>(click to view mail alias)</FONT></TH>
+        <TH>Forwards to<BR><FONT SIZE=-1>(click to view account)</FONT></TH>
+      </TR>
+END
+
+  my($svc_acct_sm);
+  foreach $svc_acct_sm (@svc_acct_sm) {
+    my($svcnum,$domuser,$domuid,$domsvc)=(
+      $svc_acct_sm->svcnum,
+      $svc_acct_sm->domuser,
+      $svc_acct_sm->domuid,
+      $svc_acct_sm->domsvc,
+    );
+
+    my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $domsvc } );
+    if ( $svc_domain ) {
+      my $domain = $svc_domain->domain;
+
+      print qq!<TR><TD><A HREF="!. popurl(2). qq!view/svc_acct_sm.cgi?$svcnum">!,
+      #print '', ( ($domuser eq '*') ? "<I>(anything)</I>" : $domuser );
+            ( ($domuser eq '*') ? "<I>(anything)</I>" : $domuser ),
+            qq!\@$domain</A> </TD>!,
+      ;
+    } else {
+      my $warning = "couldn't find svc_domain.svcnum $svcnum ( svc_acct_sm.svcnum $svcnum";
+      warn $warning;
+      print "<TR><TD>WARNING: $warning</TD>";
+    }
+
+    my $svc_acct = qsearchs( 'svc_acct', { 'uid' => $domuid } );
+    if ( $svc_acct ) {
+      my $username = $svc_acct->username;
+      my $svc_acct_svcnum =$svc_acct->svcnum;
+      print qq!<TD><A HREF="!, popurl(2),
+            qq!view/svc_acct.cgi?$svc_acct_svcnum">$username\@$mydomain</A>!,
+            qq!</TD></TR>!
+      ;
+    } else {
+      my $warning = "couldn't find svc_acct.uid $domuid (svc_acct_sm.svcnum $svcnum)!";
+      warn $warning;
+      print "<TD>WARNING: $warning</TD></TR>";
+    }
+
+  }
+
+  print '</TABLE></BODY></HTML>';
+
+} else { #error
+  idiot("Mail Alias not found");
+}
+
+%>
diff --git a/httemplate/search/svc_acct_sm.html b/httemplate/search/svc_acct_sm.html
new file mode 100755 (executable)
index 0000000..0719856
--- /dev/null
@@ -0,0 +1,23 @@
+<HTML>
+  <HEAD>
+    <TITLE>Mail Alias Search</TITLE>
+  </HEAD>
+  <BODY>
+    <CENTER>
+      <H1>Mail Alias Search</H1>
+    </CENTER>
+    <HR>
+    <FORM ACTION="svc_acct_sm.cgi" METHOD="post">
+      Search for <B>mail alias</B>: 
+      <INPUT TYPE="text" NAME="domuser"><FONT SIZE=-1>(opt.)</FONT> @
+      <INPUT TYPE="text" NAME="domain"><FONT SIZE=-1>(req.)</FONT>
+
+      <P><INPUT TYPE="submit" VALUE="Search">
+
+    </FORM>
+
+  <HR>
+
+  </BODY>
+</HTML>
+
index c0acf11..fbdecc1 100755 (executable)
@@ -1,6 +1,7 @@
 <%
 
 my $conf = new FS::Conf;
+my $mydomain = $conf->config('domain');
 
 my($query)=$cgi->keywords;
 $query ||= ''; #to avoid use of unitialized value errors
@@ -67,6 +68,18 @@ END
       $svc_domain->svcnum,
       $svc_domain->domain,
     );
+    #my($malias);
+    #if ( qsearch('svc_acct_sm',{'domsvc'=>$svcnum}) ) {
+    #  $malias=(
+    #    qq|<FORM ACTION="svc_acct_sm.cgi" METHOD="post">|.
+    #      qq|<INPUT TYPE="hidden" NAME="domuser" VALUE="">|.
+    #      qq|<INPUT TYPE="hidden" NAME="domain" VALUE="$domain">|.
+    #      qq|<INPUT TYPE="submit" VALUE="(mail aliases)">|.
+    #      qq|</FORM>|
+    #  );
+    #} else {
+    #  $malias='';
+    #}
 
     #don't display all accounts here
     my $rowspan = 1;
index 2e324c1..47a9358 100755 (executable)
@@ -2,6 +2,7 @@
 <%
 
 my $conf = new FS::Conf;
+my $mydomain = $conf->config('domain');
 
 my($query) = $cgi->keywords;
 $query =~ /^(\d+)$/;
@@ -31,7 +32,11 @@ if ( $svc_acct->domsvc ) {
   die "Unknown domain" unless $svc_domain;
   $domain = $svc_domain->domain;
 } else {
-  die "No svc_domain.svcnum record for svc_acct.domsvc: ". $cust_svc->domsvc;
+  unless ( $mydomain ) {
+    die "No legacy domain config file and no svc_domain.svcnum record ".
+        "for svc_acct.domsvc: ". $cust_svc->domsvc;
+  }
+  $domain = $mydomain;
 }
 
 %>
diff --git a/httemplate/view/svc_acct_sm.cgi b/httemplate/view/svc_acct_sm.cgi
new file mode 100755 (executable)
index 0000000..4e5acc4
--- /dev/null
@@ -0,0 +1,58 @@
+<!-- mason kludge -->
+<%
+
+my $conf = new FS::Conf;
+my $mydomain = $conf->config('domain');
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_acct_sm = qsearchs('svc_acct_sm',{'svcnum'=>$svcnum});
+die "Unknown svcnum" unless $svc_acct_sm;
+
+my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum);
+if ($pkgnum) {
+  $cust_pkg=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+  $custnum=$cust_pkg->getfield('custnum');
+} else {
+  $cust_pkg = '';
+  $custnum = '';
+}
+
+my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } )
+  or die "Unkonwn svcpart";
+
+print header('Mail Alias View', menubar(
+  ( ( $pkgnum || $custnum )
+    ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum",
+        "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum",
+      )
+    : ( "Cancel this (unaudited) account" =>
+          "${p}misc/cancel-unaudited.cgi?$svcnum" )
+  ),
+  "Main menu" => $p,
+));
+
+my($domsvc,$domuid,$domuser) = (
+  $svc_acct_sm->domsvc,
+  $svc_acct_sm->domuid,
+  $svc_acct_sm->domuser,
+);
+my $svc = $part_svc->svc;
+my $svc_domain = qsearchs('svc_domain',{'svcnum'=>$domsvc})
+  or die "Corrupted database: no svc_domain.svcnum matching domsvc $domsvc";
+my $domain = $svc_domain->domain;
+my $svc_acct = qsearchs('svc_acct',{'uid'=>$domuid})
+  or die "Corrupted database: no svc_acct.uid matching domuid $domuid";
+my $username = $svc_acct->username;
+
+print qq!<A HREF="${p}edit/svc_acct_sm.cgi?$svcnum">Edit this information</A>!,
+      "<BR>Service #$svcnum",
+      "<BR>Service: <B>$svc</B>",
+      qq!<BR>Mail to <B>!, ( ($domuser eq '*') ? "<I>(anything)</I>" : $domuser ) , qq!</B>\@<B>$domain</B> forwards to <B>$username</B>\@$mydomain mailbox.!,
+      '</BODY></HTML>'
+;
+
+%>
diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi
deleted file mode 100644 (file)
index 156edfa..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-<!-- mason kludge -->
-<%
-
-my($query) = $cgi->keywords;
-$query =~ /^(\d+)$/;
-my $svcnum = $1;
-my $svc_broadband = qsearchs( 'svc_broadband', { 'svcnum' => $svcnum } )
-  or die "svc_broadband: Unknown svcnum $svcnum";
-
-#false laziness w/all svc_*.cgi
-my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } );
-my $pkgnum = $cust_svc->getfield('pkgnum');
-my($cust_pkg, $custnum);
-if ($pkgnum) {
-  $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
-  $custnum = $cust_pkg->custnum;
-} else {
-  $cust_pkg = '';
-  $custnum = '';
-}
-#eofalse
-
-my $ac = qsearchs('ac', { acnum => $svc_broadband->getfield('acnum') });
-
-my (
-     $acname,
-     $acnum,
-     $speed_down,
-     $speed_up,
-     $ip_addr,
-     $ip_netmask,
-     $mac_addr,
-     $location
-   ) = (
-     $ac->getfield('acname'),
-     $ac->getfield('acnum'),
-     $svc_broadband->getfield('speed_down'),
-     $svc_broadband->getfield('speed_up'),
-     $svc_broadband->getfield('ip_addr'),
-     $svc_broadband->getfield('ip_netmask'),
-     $svc_broadband->getfield('mac_addr'),
-     $svc_broadband->getfield('location')
-   );
-
-print header('Broadband Service View', menubar(
-  ( ( $custnum )
-    ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum",
-        "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum",
-      )                                                                       
-    : ( "Cancel this (unaudited) website" =>
-          "${p}misc/cancel-unaudited.cgi?$svcnum" )
-  ),
-  "Main menu" => $p,
-)).
-      qq!<A HREF="${p}edit/svc_broadband.cgi?$svcnum">Edit this information</A><BR>!.
-      ntable("#cccccc"). '<TR><TD>'. ntable("#cccccc",2).
-      qq!<TR><TD ALIGN="right">Service number</TD>!.
-        qq!<TD BGCOLOR="#ffffff">$svcnum</TD></TR>!.
-      qq!<TR><TD ALIGN="right">AC</TD>!.
-        qq!<TD BGCOLOR="#ffffff">$acnum: $acname</TD></TR>!.
-      qq!<TR><TD ALIGN="right">Download Speed</TD>!.
-        qq!<TD BGCOLOR="#ffffff">$speed_down</TD></TR>!.
-      qq!<TR><TD ALIGN="right">Upload Speed</TD>!.
-        qq!<TD BGCOLOR="#ffffff">$speed_up</TD></TR>!.
-      qq!<TR><TD ALIGN="right">IP Address/Mask</TD>!.
-        qq!<TD BGCOLOR="#ffffff">$ip_addr/$ip_netmask</TD></TR>!.
-      qq!<TR><TD ALIGN="right">MAC Address</TD>!.
-        qq!<TD BGCOLOR="#ffffff">$mac_addr</TD></TR>!.
-      qq!<TR><TD ALIGN="right" VALIGN="TOP">Location</TD>!.
-        qq!<TD BGCOLOR="#ffffff"><PRE>$location</PRE></TD></TR>!.
-      '</TABLE></TD></TR></TABLE>'.
-      '<BR>'. joblisting({'svcnum'=>$svcnum}, 1).
-      '</BODY></HTML>'
-;
-%>
index 68cba8d..37ced73 100644 (file)
@@ -34,7 +34,7 @@ case "$1" in
         echo "done."
 
         echo -n "Starting freeside-selfservice-server: "
-        freeside-selfservice-server $SELFSERVICE_USER $SELFSERVICE_MACHINE
+        $FREESIDE_PATH/fs_selfservice/freeside-selfservice-server $SELFSERVICE_USER $SELFSERVICE_MACHINE
         echo "done."
 
         ;;