diff options
Diffstat (limited to 'bin')
-rw-r--r-- | bin/backup-freeside | 6 | ||||
-rwxr-xr-x | bin/bill | 188 | ||||
-rwxr-xr-x | bin/dbdef-create | 83 | ||||
-rwxr-xr-x | bin/freeside-init | 60 | ||||
-rwxr-xr-x | bin/freeside-session-kill | 100 | ||||
-rwxr-xr-x | bin/fs-migrate-part_svc | 41 | ||||
-rwxr-xr-x | bin/fs-migrate-payref | 31 | ||||
-rwxr-xr-x | bin/fs-migrate-svc_acct_sm | 230 | ||||
-rwxr-xr-x | bin/fs-radius-add-check | 59 | ||||
-rwxr-xr-x | bin/fs-radius-add-reply | 60 | ||||
-rwxr-xr-x | bin/fs-setup | 646 | ||||
-rwxr-xr-x | bin/generate-prepay | 35 | ||||
-rwxr-xr-x | bin/generate-raddb | 37 | ||||
-rwxr-xr-x | bin/generate-tests | 21 | ||||
-rwxr-xr-x | bin/masonize | 70 | ||||
-rwxr-xr-x | bin/pod2x | 53 | ||||
-rwxr-xr-x | bin/svc_acct.export | 805 | ||||
-rwxr-xr-x | bin/svc_acct.import | 119 | ||||
-rwxr-xr-x | bin/svc_acct_sm.export | 221 | ||||
-rwxr-xr-x | bin/svc_acct_sm.import | 84 | ||||
-rw-r--r-- | bin/svc_domain.import | 92 |
21 files changed, 2110 insertions, 931 deletions
diff --git a/bin/backup-freeside b/bin/backup-freeside new file mode 100644 index 0000000..a39b046 --- /dev/null +++ b/bin/backup-freeside @@ -0,0 +1,6 @@ +#!/bin/sh +/etc/init.d/apache stop +/etc/init.d/mysql stop +tar czvf var-lib-mysql-`date +%y%m%d%H%M%S`.tar.gz /var/lib/mysql +/etc/init.d/mysql start +/etc/init.d/apache start diff --git a/bin/bill b/bin/bill deleted file mode 100755 index 5c5be70..0000000 --- a/bin/bill +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/local/bin/perl -Tw -# -# bill: Bill customer(s) -# -# Usage: bill [ -c [ i ] ] [ -d 'date' ] [ -b ] -# -# Bills all customers. -# -# Adds record to /dbin/cust_bill and /dbin/cust_pay (if payment made - -# CARD & COMP), prints invoice / charges card etc. -# -# -c: Turn on collecting (you probably want this). -# -# -i: real-time billing (as opposed to batch billing). only relevant -# for credit cards. -# -# -d: Pretent it's 'date'. Date is in any format Date::Parse is happy with, -# but be careful. -# -# ## n/a ## -b: send batch when done billing -# -# ivan@voicenet.com sep/oct 96 -# -# separated billing and collections, cleaned up code. -# ivan@voicenet.com 96-nov-11 -# -# added -d option -# ivan@voicenet.com 96-nov-13 -# -# added -v option and started to implement it, added 'd:' to getopts call -# (oops!) -# ivan@voicenet.com 97-jan-2 -# -# added more debug messages, moved some searches to fssearch.pl library (for -# speed) -# rewrote "all customer" finder to know about bill dates, for speed. -# ivan@voicenet.com 97-jan-8 -# -# thought about it a while, and removed passing of the -d option to collect...? -# ivan@voicenet.com 97-jan-14 -# -# make all -v stuff STDERR -# ivan@voicenet.com 97-feb-4 -# -# added pkgnum as argument to program from /db/part_pkg, with kludge for the -# "/bin/echo XX" 's already there. -# ivan@voicenet.com 97-feb-23 -# -# - general cleanup -# - customers who are suspended can still be billed for the setup fee -# - cust_pkg record is re-read after the package setup fee program is run. -# this way, -# that program can modify the record (for example, to start accounts off -# suspended) -# (best to think four or five times before modifying anything else!) -# ivan@voicenet.com 97-feb-26 -# -# don't bill recurring fee if its not time! (was removed) -# ivan@voicenet.com 97-mar-6 -# -# added -b option, send batch when done billing. -# ivan@voicenet.com 97-apr-4 -# -#insecure dependency on line 179ish below needs to be fixed before bill is -#used setuid -# ivan@voicenet.com 97-jun-2 -# -# removed running of setup program (depriciated) -# ivan@voicenet.com 97-jul-21 -# -# rewrote for new API, removed option to specify custnums (use FS::Bill -# instead), removed -v option (?) -# ivan@voicenet.com 97-jul-22 - 23 - 25 -28 -# (need to add back in email stuff, look in /home/ivan/old/dbin/collect) -# -# s/suidsetup/adminsuidsetup/, s/FS::Search/FS::Record/, added some batch -# exporting stuff (which still needs to be generalized) and removed &idiot -# ivan@sisd.com 98-may-27 - -# setup - -use strict; -use Fcntl qw(:flock); -use Date::Parse; -use Getopt::Std; -use FS::UID qw(adminsuidsetup swapuid); -use FS::Record qw(qsearch qsearchs); -use FS::Bill; - -my($batchfile)="/var/spool/freeside/batch"; -my($batchlock)="/var/spool/freeside/batch.lock"; - -adminsuidsetup; - -&untaint_argv; #what it sounds like (eww) -use vars qw($opt_b $opt_c $opt_i $opt_d); -getopts("bcid:"); #switches - -#we're at now now (and later). -my($time)= $main::opt_d ? str2time($main::opt_d) : $^T; - -# find packages w/ bill < time && cancel != '', and create corresponding -# customer objects - -my($cust_main,%saw); -foreach $cust_main ( - map { - if ( ( $_->getfield('bill') || 0 ) <= $time && - !$saw{ $_->getfield('custnum') }++ ) { - qsearchs('cust_main',{'custnum'=> $_->getfield('custnum') } ); - } else { - (); - } - } qsearch('cust_pkg',{'cancel'=>''}) -) { - - # and bill them - - print "Billing customer #" . $cust_main->getfield('custnum') . "\n"; - - bless($cust_main,"FS::Bill"); - - my($error); - - $error=$cust_main->bill('time'=>$time); - warn "Error billing, customer #" . $cust_main->getfield('custnum') . - ":" . $error if $error; - - if ($main::opt_c) { - $error=$cust_main->collect('invoice_time'=>$time, - 'batch_card' => $main::opt_i ? 'no' : 'yes', - ); - warn "Error collecting customer #" . $cust_main->getfield('custnum') . - ":" . $error if $error; - - #sleep 1; - - } - -} - -#if ($main::opt_b) { -# -# die "Batch still waiting for reply? ($batchlock exists)\n" if -e $batchlock; -# open(BATCHLOCK,"+>>$batchlock") or die "Can't open $batchlock: $!"; -# select(BATCHLOCK); $|=1; select(STDOUT); -# unless ( flock(BATCHLOCK,,LOCK_EX|LOCK_NB) ) { -# seek(BATCHLOCK,0,0); -# my($pid)=<BATCHLOCK>; -# chop($pid); -# die "Is a batch running? (pid $pid)\n"; -# } -# seek(BATCHLOCK,0,0); -# print BATCHLOCK $$,"\n"; -# -# ( open(BATCH,">$batchfile") -# and flock(BATCH,LOCK_EX|LOCK_NB) -# ) or die "Can't open $batchfile: $!"; -# -# my($cust_pay_batch); -# foreach $cust_pay_batch (qsearch('cust_pay_batch',{})) { -# print BATCH join(':', -# $_->getfield('cardnum'), -# $_->getfield('exp'), -# $_->getfield('amount'), -# $_->getfield('payname') -# || $_->getfield('first'). ' '. $_->getfield('last'), -# "Description", -# $_->getfield('zip'), -# ),"\n"; -# } -# -# flock(BATCH,LOCK_UN); -# close BATCH; -# -# flock(BATCHLOCK,LOCK_UN); -# close BATCHLOCK; -#} - -# subroutines - -sub untaint_argv { - foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV - $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\""; - $ARGV[$_]=$1; - } -} - diff --git a/bin/dbdef-create b/bin/dbdef-create index eb62c77..0b297b9 100755 --- a/bin/dbdef-create +++ b/bin/dbdef-create @@ -1,85 +1,26 @@ #!/usr/bin/perl -Tw # -# create dbdef file for existing mySQL database (needs SHOW|DESCRIBE command -# not in Pg) based on fs-setup -# -# ivan@sisd.com 98-jun-2 +# $Id: dbdef-create,v 1.5 2001-08-21 02:43:18 ivan Exp $ use strict; use DBI; -use FS::dbdef; -use FS::UID qw(adminsuidsetup datasrc); - -#needs to match FS::Record -my($dbdef_file) = "/var/spool/freeside/dbdef.". datasrc; - -my($dbh)=adminsuidsetup; - -my($tables_sth)=$dbh->prepare("SHOW TABLES"); -my($tables_rv)=$tables_sth->execute; +use DBIx::DBSchema; +use FS::UID qw(adminsuidsetup datasrc driver_name); -my(@tables); -foreach ( @{$tables_sth->fetchall_arrayref} ) { - my($table)=${$_}[0]; - #print "TABLE\t$table\n"; +my $user = shift or die &usage; - my($index_sth)=$dbh->prepare("SHOW INDEX FROM $table"); - my($primary_key)=''; - my(%index,%unique); - for ( 1 .. $index_sth->execute ) { - my($row)=$index_sth->fetchrow_hashref; - if ( ${$row}{'Key_name'} eq "PRIMARY" ) { - $primary_key=${$row}{'Column_name'}; - next; - } - if ( ${$row}{'Non_unique'} ) { #index - push @{$index{${$row}{'Key_name'}}}, ${$row}{'Column_name'}; - } else { #unique - push @{$unique{${$row}{'Key_name'}}}, ${$row}{'Column_name'}; - } - } +my($dbh)=adminsuidsetup $user; - my(@index)=values %index; - my(@unique)=values %unique; - #print "\tPRIMARY KEY $primary_key\n"; - foreach (@index) { - #print "\tINDEX\t", join(', ', @{$_}), "\n"; - } - foreach (@unique) { - #print "\tUNIQUE\t", join(', ', @{$_}), "\n"; - } - - my($columns_sth)=$dbh->prepare("SHOW COLUMNS FROM $table"); - my(@columns); - for ( 1 .. $columns_sth->execute ) { - my($row)=$columns_sth->fetchrow_hashref; - #print "\t", ${$row}{'Field'}, "\n"; - ${$row}{'Type'} =~ /^(\w+)\(?([\d\,]+)?\)?( unsigned)?$/ - or die "Illegal type ${$row}{'Type'}\n"; - my($type,$length)=($1,$2); - my($null)=${$row}{'Null'}; - $null =~ s/YES/NULL/; - push @columns, new FS::dbdef_column ( - ${$row}{'Field'}, - $type, - $null, - $length, - ); - } +#needs to match FS::Record +my($dbdef_file) = "/usr/local/etc/freeside/dbdef.". datasrc; - #print "\n"; - push @tables, new FS::dbdef_table ( - $table, - $primary_key, - new FS::dbdef_unique (\@unique), - new FS::dbdef_index (\@index), - @columns, - ); +my $dbdef = new_native DBIx::DBSchema $dbh; -} - -my($dbdef) = new FS::dbdef ( @tables ); +#print $dbdef->pretty_print; #important $dbdef->save($dbdef_file); +sub usage { + die "Usage:\n dbdef-create user\n"; +} diff --git a/bin/freeside-init b/bin/freeside-init new file mode 100755 index 0000000..fe12931 --- /dev/null +++ b/bin/freeside-init @@ -0,0 +1,60 @@ +#! /bin/sh +# +# start the freeside job queue daemon + +#PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/local/bin/freeside-queued +NAME=freeside-queued +DESC="freeside job queue daemon" +USER="ivan" + +test -f $DAEMON || exit 0 + +set -e + +case "$1" in + start) + echo -n "Starting $DESC: " +# start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid -b -m\ +# --exec $DAEMON + $DAEMON $USER & + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \ + --exec $DAEMON + echo "$NAME." + rm /var/run/$NAME.pid + ;; + #reload) + # + # If the daemon can reload its config files on the fly + # for example by sending it SIGHUP, do it here. + # + # If the daemon responds to changes in its config file + # directly anyway, make this a do-nothing entry. + # + # echo "Reloading $DESC configuration files." + # start-stop-daemon --stop --signal 1 --quiet --pidfile \ + # /var/run/$NAME.pid --exec $DAEMON + #;; + restart|force-reload) + # + # If the "reload" option is implemented, move the "force-reload" + # option to the "reload" entry above. If not, "force-reload" is + # just the same as "restart". + # + $0 stop + sleep 1 + $0 start + ;; + *) + N=/etc/init.d/$NAME + # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $N {start|stop|restart|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/bin/freeside-session-kill b/bin/freeside-session-kill new file mode 100755 index 0000000..9f11abd --- /dev/null +++ b/bin/freeside-session-kill @@ -0,0 +1,100 @@ +#!/usr/bin/perl -w + +use strict; +use vars qw($conf); +use Fcntl qw(:flock); +use FS::UID qw(adminsuidsetup datasrc dbh); +use FS::Record qw(dbdef qsearch fields); +use FS::session; +use FS::svc_acct; + +my $user = shift or die &usage; +adminsuidsetup $user; + +my $sessionlock = "/usr/local/etc/freeside/session-kill.lock.". datasrc; + +open(LOCK,"+>>$sessionlock") or die "Can't open $sessionlock: $!"; +select(LOCK); $|=1; select(STDOUT); +unless ( flock(LOCK,LOCK_EX|LOCK_NB) ) { + seek(LOCK,0,0); + my($pid)=<LOCK>; + chop($pid); + #no reason to start loct of blocking processes + die "Is another session kill process running under pid $pid?\n"; +} +seek(LOCK,0,0); +print LOCK $$,"\n"; + +$FS::UID::AutoCommit = 0; + +my $now = time; + +#uhhhhh + +use DBIx::DBSchema; +use DBIx::DBSchema::Table; #down this path lies madness +use DBIx::DBSchema::Column; + +my $dbdef = dbdef or die; +#warn $dbdef; +#warn $dbdef->{'tables'}; +#warn keys %{$dbdef->{'tables'}}; +my $session_table = $dbdef->table('session') or die; +my $svc_acct_table = $dbdef->table('svc_acct') or die; + +my $session_svc_acct = new DBIx::DBSchema::Table ( 'session,svc_acct', '', '', '', + map( DBIx::DBSchema::Column->new( "session.$_", + $session_table->column($_)->type, + $session_table->column($_)->null, + $session_table->column($_)->length, + ), $session_table->columns() ), + map( DBIx::DBSchema::Column->new( "svc_acct.$_", + $svc_acct_table->column($_)->type, + $svc_acct_table->column($_)->null, + $svc_acct_table->column($_)->length, + ), $svc_acct_table->columns ), +# map("svc_acct.$_", $svc_acct_table->columns), +); + +$dbdef->addtable($session_svc_acct); #madness, i tell you + +$FS::Record::DEBUG = 1; +my @session = qsearch('session,svc_acct', {}, '', ' WHERE '. join(' AND ', + 'svc_acct.svcnum = session.svcnum', + '( session.logout IS NULL OR session.logout = 0 )', + "( $now - session.login ) >= svc_acct.seconds" +). " FOR UPDATE" ); + +my $dbh = dbh; + +foreach my $join ( @session ) { + + my $session = new FS::session ( { + map { $_ => $join->{'Hash'}{"session.$_"} } fields('session') + } ); #see no evil + + my $svc_acct = new FS::svc_acct ( { + map { $_ => $join->{'Hash'}{"svc_acct.$_"} } fields('svc_acct') + } ); + + #false laziness w/ fs_session_server + my $nsession = new FS::session ( { $session->hash } ); + my $error = $nsession->replace($session); + if ( $error ) { + $dbh->rollback; + die $error; + } + my $time = $nsession->logout - $nsession->login; + my $new_svc_acct = new FS::svc_acct ( { $svc_acct->hash } ); + my $seconds = $new_svc_acct->seconds; + $seconds -= $time; + $seconds = 0 if $seconds < 0; + $new_svc_acct->seconds( $seconds ); + $error = $new_svc_acct->replace( $svc_acct ); + warn "can't debit time from ". $svc_acct->username. ": $error\n"; #don't want to rollback, though + #ssenizal eslaf + +} + +$dbh->commit or die $dbh->errstr; + diff --git a/bin/fs-migrate-part_svc b/bin/fs-migrate-part_svc new file mode 100755 index 0000000..b0f3ac5 --- /dev/null +++ b/bin/fs-migrate-part_svc @@ -0,0 +1,41 @@ +#!/usr/bin/perl + +use strict; +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch fields); +use FS::part_svc; + +my $user = shift or die &usage; +my $dbh = adminsuidsetup $user; + +my $oldAutoCommit = $FS::UID::AutoCommit; +local $FS::UID::AutoCommit = 0; + +foreach my $part_svc ( qsearch('part_svc', {} ) ) { + foreach my $field ( + grep { defined($part_svc->getfield($part_svc->svcdb.'__'.$_.'_flag') ) } + fields($part_svc->svcdb) + ) { + my $flag = $part_svc->getfield($part_svc->svcdb.'__'.$field.'_flag'); + if ( uc($flag) =~ /^([DF])$/ ) { + my $part_svc_column = new FS::part_svc_column { + 'svcpart' => $part_svc->svcpart, + 'columnname' => $field, + 'columnflag' => $1, + 'columnvalue' => $part_svc->getfield($part_svc->svcdb.'__'.$field), + }; + my $error = $part_svc_column->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + die $error; + } + } + } +} + +$dbh->commit or die $dbh->errstr; + +sub usage { + die "Usage:\n fs-migrate-part_svc user\n"; +} + diff --git a/bin/fs-migrate-payref b/bin/fs-migrate-payref new file mode 100755 index 0000000..1584197 --- /dev/null +++ b/bin/fs-migrate-payref @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +use strict; +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch); +use FS::cust_pay; +use FS::cust_refund; + +my $user = shift or die &usage; +my $dbh = adminsuidsetup $user; + +# apply payments to invoices + +foreach my $cust_pay ( qsearch('cust_pay', {} ) ) { + my $error = $cust_pay->upgrade_replace; + warn $error if $error; +} + +# apply refunds to credits + +foreach my $cust_refund ( qsearch('cust_refund') ) { + my $error = $cust_refund->upgrade_replace; + warn $error if $error; +} + +# ? apply credits to invoices + +sub usage { + die "Usage:\n fs-migrate-payref user\n"; +} + diff --git a/bin/fs-migrate-svc_acct_sm b/bin/fs-migrate-svc_acct_sm new file mode 100755 index 0000000..ae2dc76 --- /dev/null +++ b/bin/fs-migrate-svc_acct_sm @@ -0,0 +1,230 @@ +#!/usr/bin/perl -Tw +# +# $Id: fs-migrate-svc_acct_sm,v 1.3 2001-08-21 02:43:18 ivan Exp $ +# +# jeff@cmh.net 01-Jul-20 + +#to delay loading dbdef until we're ready +#BEGIN { $FS::Record::setup_hack = 1; } + +use strict; +use Term::Query qw(query); +#use DBI; +#use DBIx::DBSchema; +#use DBIx::DBSchema::Table; +#use DBIx::DBSchema::Column; +#use DBIx::DBSchema::ColGroup::Unique; +#use DBIx::DBSchema::ColGroup::Index; +use FS::Conf; +use FS::UID qw(adminsuidsetup datasrc checkeuid getsecrets); +use FS::Record qw(qsearch qsearchs); +use FS::svc_domain; +use FS::svc_forward; +use vars qw( $conf $old_default_domain %part_domain_svc %part_acct_svc %part_forward_svc $svc_acct $svc_acct_sm $error); + +die "Not running uid freeside!" unless checkeuid(); + +my $user = shift or die &usage; +getsecrets($user); + +$conf = new FS::Conf; +$old_default_domain = $conf->config('domain'); + +#needs to match FS::Record +#my($dbdef_file) = "/usr/local/etc/freeside/dbdef.". datasrc; + +### +# This section would be the appropriate place to manipulate +# the schema & tables. +### + +## we need to add the domsvc to svc_acct +## we must add a svc_forward record.... +## I am thinking that the fields svcnum (int), destsvc (int), and +## dest (varchar (80)) are appropriate, with destsvc/dest an either/or +## much in the spirit of cust_main_invoice + +### +# massage the data +### + +my($dbh)=adminsuidsetup $user; + +$|=1; + +$FS::svc_acct::nossh_hack = 1; +$FS::svc_forward::nossh_hack = 1; +$FS::svc_domain::whois_hack = 1; + +%part_domain_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_domain'}); +%part_acct_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'}); +%part_forward_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_forward'}); + +die "No services with svcdb svc_domain!\n" unless %part_domain_svc; +die "No services with svcdb svc_acct!\n" unless %part_acct_svc; +die "No services with svcdb svc_forward!\n" unless %part_forward_svc; + +my($svc_domain) = qsearchs('svc_domain', { 'domain' => $old_default_domain }); +if (! $svc_domain || $svc_domain->domain != $old_default_domain) { + print <<EOF; + +Your database currently does not contain a svc_domain record for the +domain $old_default_domain. Would you like me to add one for you? +EOF + + my($response)=scalar(<STDIN>); + chop $response; + if ($response =~ /^[yY]/) { + print "\n\n", &menu_domain_svc, "\n", <<END; +I need to create new domain accounts. Which service shall I use for that? +END + my($domain_svcpart)=&getdomainpart; + + $svc_domain = new FS::svc_domain { + 'domain' => $old_default_domain, + 'svcpart' => $domain_svcpart, + 'action' => 'M', + }; +# $error=$svc_domain->insert && die "Error adding domain $old_default_domain: $error"; + $error=$svc_domain->insert; + die "Error adding domain $old_default_domain: $error" if $error; + }else{ + print <<EOF; + + This program cannot function properly until a svc_domain record matching +your conf_dir/domain file exists. +EOF + + exit 1; + } +} + +print "\n\n", &menu_acct_svc, "\n", <<END; +I may need to create some new pop accounts and set up forwarding to them +for some users. Which service shall I use for that? +END +my($pop_svcpart)=&getacctpart; + +print "\n\n", &menu_forward_svc, "\n", <<END; +I may need to create some new forwarding for some users. Which service +shall I use for that? +END +my($forward_svcpart)=&getforwardpart; + +sub menu_domain_svc { + ( join "\n", map "$_: ".$part_domain_svc{$_}->svc, sort keys %part_domain_svc ). "\n"; +} +sub menu_acct_svc { + ( join "\n", map "$_: ".$part_acct_svc{$_}->svc, sort keys %part_acct_svc ). "\n"; +} +sub menu_forward_svc { + ( join "\n", map "$_: ".$part_forward_svc{$_}->svc, sort keys %part_forward_svc ). "\n"; +} +sub getdomainpart { + $^W=0; # Term::Query isn't -w-safe + my $return = query "Enter part number:", 'irk', [ keys %part_domain_svc ]; + $^W=1; + $return; +} +sub getacctpart { + $^W=0; # Term::Query isn't -w-safe + my $return = query "Enter part number:", 'irk', [ keys %part_acct_svc ]; + $^W=1; + $return; +} +sub getforwardpart { + $^W=0; # Term::Query isn't -w-safe + my $return = query "Enter part number:", 'irk', [ keys %part_forward_svc ]; + $^W=1; + $return; +} + + +#migrate data + +my(@svc_accts) = qsearch('svc_acct', {}); +foreach $svc_acct (@svc_accts) { + my(@svc_acct_sms) = qsearch('svc_acct_sm', { + domuid => $svc_acct->getfield('uid'), + } + ); + + # Ok.. we've got the svc_acct record, and an array of svc_acct_sm's + # What do we do from here? + + # The intuitive: + # plop the svc_acct into the 'default domain' + # and then represent the svc_acct_sm's with svc_forwards + # they can be gussied up manually, later + # + # Perhaps better: + # when no svc_acct_sm exists, place svc_acct in 'default domain' + # when one svc_acct_sm exists, place svc_acct in corresponding + # domain & possibly create a svc_forward in 'default domain' + # when multiple svc_acct_sm's exists (in different domains) we'd + # better use the 'intuitive' approach. + # + # Specific way: + # as 'perhaps better,' but we may be able to guess which domain + # is correct by comparing the svcnum of domains to the username + # of the svc_acct + # + + # The intuitive way: + + my $def_acct = new FS::svc_acct ( { $svc_acct->hash } ); + $def_acct->setfield('domsvc' => $svc_domain->getfield('svcnum')); + $error = $def_acct->replace($svc_acct); + die "Error replacing svc_acct for " . $def_acct->username . " : $error" if $error; + + foreach $svc_acct_sm (@svc_acct_sms) { + + my($domrec)=qsearchs('svc_domain', { + svcnum => $svc_acct_sm->getfield('domsvc'), + }) || die "svc_acct_sm references invalid domsvc $svc_acct_sm->getfield('domsvc')\n"; + + if ($svc_acct_sm->getfield('domuser') =~ /^\*$/) { + + my($newdom) = new FS::svc_domain ( { $domrec->hash } ); + $newdom->setfield('catchall', $svc_acct->svcnum); + $newdom->setfield('action', "M"); + $error = $newdom->replace($domrec); + die "Error replacing svc_domain for (anything)@" . $domrec->domain . " : $error" if $error; + + } else { + + my($newacct) = new FS::svc_acct { + 'svcpart' => $pop_svcpart, + 'username' => $svc_acct_sm->getfield('domuser'), + 'domsvc' => $svc_acct_sm->getfield('domsvc'), + 'dir' => '/dev/null', + }; + $error = $newacct->insert; + die "Error adding svc_acct for " . $newacct->username . " : $error" if $error; + + my($newforward) = new FS::svc_forward { + 'svcpart' => $forward_svcpart, + 'srcsvc' => $newacct->getfield('svcnum'), + 'dstsvc' => $def_acct->getfield('svcnum'), + }; + $error = $newforward->insert; + die "Error adding svc_forward for " . $newacct->username ." : $error" if $error; + } + + $error = $svc_acct_sm->delete; + die "Error deleting svc_acct_sm for " . $svc_acct_sm->domuser ." : $error" if $error; + + }; + +}; + + +$dbh->commit or die $dbh->errstr; +$dbh->disconnect or die $dbh->errstr; + +print "svc_acct_sm records sucessfully migrated\n"; + +sub usage { + die "Usage:\n fs-migrate-svc_acct_sm user\n"; +} + diff --git a/bin/fs-radius-add-check b/bin/fs-radius-add-check new file mode 100755 index 0000000..35f4d32 --- /dev/null +++ b/bin/fs-radius-add-check @@ -0,0 +1,59 @@ +#!/usr/bin/perl -Tw + +# quick'n'dirty hack of fs-setup to add radius attributes + +use strict; +use DBI; +use FS::UID qw(adminsuidsetup checkeuid getsecrets); +use FS::raddb; + +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); + +my $dbh = adminsuidsetup $user; + +### + +print "\n\n", <<END, ":"; +Enter the additional RADIUS check attributes you need to track for +each user, separated by whitespace. +END +my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; } + split(" ",&getvalue); + +sub getvalue { + my($x)=scalar(<STDIN>); + chop $x; + $x; +} + +### + +my($char_d) = 80; #default maxlength for text fields + +### + +foreach my $attribute ( @attributes ) { + my $statement = + "ALTER TABLE svc_acct ADD COLUMN rc_$attribute varchar($char_d) NULL"; + my $sth = $dbh->prepare( $statement ) + or warn "Error preparing $statement: ". $dbh->errstr; + my $rc = $sth->execute + or warn "Error executing $statement: ". $sth->errstr; +} + +$dbh->commit or die $dbh->errstr; + +$dbh->disconnect or die $dbh->errstr; + +print "\n\n", "Now you must run dbdef-create.\n\n"; + +sub usage { + die "Usage:\n fs-radius-add-check user\n"; +} + diff --git a/bin/fs-radius-add-reply b/bin/fs-radius-add-reply new file mode 100755 index 0000000..6b9a39e --- /dev/null +++ b/bin/fs-radius-add-reply @@ -0,0 +1,60 @@ +#!/usr/bin/perl -Tw + +# quick'n'dirty hack of fs-setup to add radius attributes + +use strict; +use DBI; +use FS::UID qw(adminsuidsetup checkeuid getsecrets); +use FS::raddb; + +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); + +my $dbh = adminsuidsetup $user; + +### + +print "\n\n", <<END, ":"; +Enter the additional RADIUS reply attributes you need to track for +each user, separated by whitespace. +END +my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; } + split(" ",&getvalue); + +sub getvalue { + my($x)=scalar(<STDIN>); + chop $x; + $x; +} + +### + +my($char_d) = 80; #default maxlength for text fields + +### + +foreach my $attribute ( @attributes ) { + my $statement = + "ALTER TABLE svc_acct ADD COLUMN radius_$attribute varchar($char_d) NULL"; + my $sth = $dbh->prepare( $statement ) + or warn "Error preparing $statement: ". $dbh->errstr; + $sth->execute + or warn "Error executing $statement: ". $sth->errstr; +} + +$dbh->commit or die $dbh->errstr; + +$dbh->disconnect or die $dbh->errstr; + +print "\n\n", "Now you must run dbdef-create.\n\n"; + +sub usage { + die "Usage:\n fs-radius-add-reply user\n"; +} + + diff --git a/bin/fs-setup b/bin/fs-setup index 45332d8..3ad0bbd 100755 --- a/bin/fs-setup +++ b/bin/fs-setup @@ -1,60 +1,64 @@ #!/usr/bin/perl -Tw # -# create database and necessary tables, etc. DBI version. -# -# ivan@sisd.com 97-nov-8,9 -# -# agent_type and type_pkgs added. -# (index need to be declared, & primary keys shoudln't have mysql syntax) -# ivan@sisd.com 97-nov-13 -# -# pulled modified version back out of register.cgi ivan@sisd.com 98-feb-21 -# -# removed extraneous sample data ivan@sisd.com 98-mar-23 -# -# gained the big hash from dbdef.pm, dbdef.pm usage rewrite ivan@sisd.com -# 98-apr-19 - 98-may-11 plus -# -# finished up ivan@sisd.com 98-jun-1 -# -# part_svc fields are all forced NULL, not the opposite -# hmm: also are forced varchar($char_d) as fixed '0' for things like -# uid is Not Good. will this break anything else? -# ivan@sisd.com 98-jun-29 -# -# ss is 11 chars ivan@sisd.com 98-jul-20 -# -# setup of arbitrary radius fields ivan@sisd.com 98-aug-9 -# -# ouch, removed index on company name that wasn't supposed to be there -# ivan@sisd.com 98-sep-4 -# -# fix radius attributes ivan@sisd.com 98-sep-27 +# $Id: fs-setup,v 1.81 2002-02-22 07:50:19 ivan Exp $ #to delay loading dbdef until we're ready BEGIN { $FS::Record::setup_hack = 1; } use strict; use DBI; -use FS::dbdef; -use FS::UID qw(adminsuidsetup datasrc); +use DBIx::DBSchema 0.19; +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) = "/var/spool/freeside/dbdef.". datasrc; +my($dbdef_file) = "/usr/local/etc/freeside/dbdef.". datasrc; ### -print "\nEnter the maximum username length: "; -my($username_len)=&getvalue; +#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 attributes User-Name, Password and Framed-IP-Address -for each user. Enter any additional RADIUS attributes you need to track for -each user, separated by whitespace. +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 @attributes = map { s/\-/_/g; $_; } split(" ",&getvalue); +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>); @@ -62,19 +66,20 @@ sub getvalue { $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) = ( 'long varchar', 'NULL', '' ); -my(@money_type); -if (datasrc =~ m/Pg/) { #Pg can't do decimal(10,2) - @money_type = ( 'money', '', '' ); -} else { - @money_type = ( 'decimal', '', '10,2' ); -} +my(@perl_type) = ( 'text', 'NULL', '' ); +my @money_type = ( 'decimal', '', '10,2' ); ### # create a dbdef object from the old data structure @@ -83,30 +88,35 @@ if (datasrc =~ m/Pg/) { #Pg can't do decimal(10,2) my(%tables)=&tables_hash_hack; #turn it into objects -my($dbdef) = new FS::dbdef ( map { +my($dbdef) = new DBIx::DBSchema ( map { my(@columns); while (@{$tables{$_}{'columns'}}) { my($name,$type,$null,$length)=splice @{$tables{$_}{'columns'}}, 0, 4; - push @columns, new FS::dbdef_column ( $name,$type,$null,$length ); + push @columns, new DBIx::DBSchema::Column ( $name,$type,$null,$length ); } - FS::dbdef_table->new( + DBIx::DBSchema::Table->new( $_, $tables{$_}{'primary_key'}, - #FS::dbdef_unique->new(@{$tables{$_}{'unique'}}), - #FS::dbdef_index->new(@{$tables{$_}{'index'}}), - FS::dbdef_unique->new($tables{$_}{'unique'}), - FS::dbdef_index->new($tables{$_}{'index'}), + 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 on ship_last and ship_company + push @{$cust_main->index->lol_ref}, ( ['ship_last'], ['ship_company'] ) +} + #add radius attributes to svc_acct my($svc_acct)=$dbdef->table('svc_acct'); my($attribute); foreach $attribute (@attributes) { - $svc_acct->addcolumn ( new FS::dbdef_column ( + $svc_acct->addcolumn ( new DBIx::DBSchema::Column ( 'radius_'. $attribute, 'varchar', 'NULL', @@ -114,82 +124,147 @@ foreach $attribute (@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_acct_sm svc_domain)) { - my($table)=$dbdef->table($_); - my($col); - foreach $col ( $table->columns ) { - next if $col =~ /^svcnum$/; - $part_svc->addcolumn( new FS::dbdef_column ( - $table->name. '__' . $table->column($col)->name, - 'varchar', #$table->column($col)->type, - 'NULL', - $char_d, #$table->column($col)->length, - )); - $part_svc->addcolumn ( new FS::dbdef_column ( - $table->name. '__'. $table->column($col)->name . "_flag", - 'char', - 'NULL', - 1, - )); - } +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, +# )); +# } +#} + #important $dbdef->save($dbdef_file); -FS::Record::reload_dbdef; +&FS::Record::reload_dbdef($dbdef_file); ### # create 'em ### -my($dbh)=adminsuidsetup; +my($dbh)=adminsuidsetup $user; #create tables $|=1; -my($table); -foreach ($dbdef->tables) { - my($table)=$dbdef->table($_); - print "Creating $_..."; - - my($statement); - - #create table - foreach $statement ($table->sql_create_table(datasrc)) { - #print $statement, "\n"; - $dbh->do( $statement ) - or die "CREATE error: ",$dbh->errstr, "\ndoing statement: $statement"; - } - - print "\n"; +my @sql = $dbdef->sql($dbh); +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 TT UT VT VI VA WA WV WI WY AE AA AP +SC SD TN TX UT VT VI VA WA WV WI WY AE AA AP ) ) { - my($cust_main_county)=create FS::cust_main_county({ + 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->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. ### @@ -206,7 +281,7 @@ sub tables_hash_hack { 'agentnum', 'int', '', '', 'agent', 'varchar', '', $char_d, 'typenum', 'int', '', '', - 'freq', 'smallint', 'NULL', '', + 'freq', 'int', 'NULL', '', 'prog', @perl_type, ], 'primary_key' => 'agentnum', @@ -240,14 +315,43 @@ sub tables_hash_hack { 'custnum', 'int', '', '', '_date', @date_type, 'charged', @money_type, - 'owed', @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, + ], + 'primary_key' => 'eventnum', + 'unique' => [ [ 'eventpart', 'invnum' ] ], + 'index' => [ ['invnum'] ], + }, + + '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', '', '', @@ -268,20 +372,35 @@ sub tables_hash_hack { 'custnum', 'int', '', '', '_date', @date_type, 'amount', @money_type, - 'credited', @money_type, 'otaker', 'varchar', '', 8, - 'reason', 'varchar', '', 255, + '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, @@ -289,33 +408,62 @@ sub tables_hash_hack { 'address2', 'varchar', 'NULL', $char_d, 'city', 'varchar', '', $char_d, 'county', 'varchar', 'NULL', $char_d, - 'state', 'char', '', 2, + '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', 16, - 'paydate', @date_type, + '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'], ], + 'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ] ], }, - 'cust_main_county' => { #county+state are checked off the cust_main_county - #table for validation and to provide a tax rate. - #add country? + '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', 'char', '', 2, #two letters max in US... elsewhere? - 'county', 'varchar', '', $char_d, + 'state', 'varchar', 'NULL', $char_d, + 'county', 'varchar', 'NULL', $char_d, + 'country', 'char', '', 2, 'tax', 'real', '', '', #tax % ], 'primary_key' => 'taxnum', @@ -327,22 +475,38 @@ sub tables_hash_hack { 'cust_pay' => { 'columns' => [ 'paynum', 'int', '', '', - 'invnum', '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', 16, #see cust_main above 'paybatch', 'varchar', 'NULL', $char_d, #for auditing purposes. + 'closed', 'char', 'NULL', 1, ], 'primary_key' => 'paynum', 'unique' => [ [] ], - 'index' => [ ['invnum'] ], + '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, @@ -350,16 +514,17 @@ sub tables_hash_hack { 'address1', 'varchar', '', $char_d, 'address2', 'varchar', 'NULL', $char_d, 'city', 'varchar', '', $char_d, - 'state', 'char', '', 2, + 'state', 'varchar', '', $char_d, 'zip', 'varchar', '', 10, 'country', 'char', '', 2, - 'trancode', 'TINYINT', '', '', +# 'trancode', 'int', '', '', 'cardnum', 'varchar', '', 16, - 'exp', @date_type, + #'exp', @date_type, + 'exp', 'varchar', '', 11, 'payname', 'varchar', 'NULL', $char_d, 'amount', @money_type, ], - 'primary_key' => '', + 'primary_key' => 'paybatchnum', 'unique' => [ [] ], 'index' => [ ['invnum'], ['custnum'] ], }, @@ -375,6 +540,7 @@ sub tables_hash_hack { 'susp', @date_type, 'cancel', @date_type, 'expire', @date_type, + 'manual_flag', 'char', 'NULL', 1, ], 'primary_key' => 'pkgnum', 'unique' => [ [] ], @@ -384,7 +550,8 @@ sub tables_hash_hack { 'cust_refund' => { 'columns' => [ 'refundnum', 'int', '', '', - 'crednum', 'int', '', '', + #now cust_credit_refund #'crednum', 'int', '', '', + 'custnum', 'int', '', '', '_date', @date_type, 'refund', @money_type, 'otaker', 'varchar', '', 8, @@ -392,16 +559,32 @@ sub tables_hash_hack { 'payby', 'char', '', 4, # CARD/BILL/COMP, should be index # into payment type table. 'payinfo', 'varchar', 'NULL', 16, #see cust_main above + 'paybatch', 'varchar', 'NULL', $char_d, + 'closed', 'char', 'NULL', 1, ], 'primary_key' => 'refundnum', 'unique' => [ [] ], - 'index' => [ ['crednum'] ], + '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', '', '', + 'pkgnum', 'int', 'NULL', '', 'svcpart', 'int', '', '', ], 'primary_key' => 'svcnum', @@ -415,14 +598,29 @@ sub tables_hash_hack { 'pkg', 'varchar', '', $char_d, 'comment', 'varchar', '', $char_d, 'setup', @perl_type, - 'freq', 'smallint', '', '', #billing frequency (months) + '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, ], 'primary_key' => 'pkgpart', 'unique' => [ [] ], 'index' => [ [] ], }, +# 'part_title' => { +# 'columns' => [ +# 'titlenum', 'int', '', '', +# 'title', 'varchar', '', $char_d, +# ], +# 'primary_key' => 'titlenum', +# 'unique' => [ [] ], +# 'index' => [ [] ], +# }, + 'pkg_svc' => { 'columns' => [ 'pkgpart', 'int', '', '', @@ -449,57 +647,87 @@ sub tables_hash_hack { 'svcpart', 'int', '', '', 'svc', 'varchar', '', $char_d, 'svcdb', 'varchar', '', $char_d, + 'disabled', 'char', 'NULL', 1, ], 'primary_key' => 'svcpart', 'unique' => [ [] ], 'index' => [ [] ], }, + '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', 'char', '', 2, + 'state', 'varchar', '', $char_d, 'ac', 'char', '', 3, 'exch', 'char', '', 3, - #rest o' number? + 'loc', 'char', 'NULL', 4, #NULL for legacy purposes ], 'primary_key' => 'popnum', 'unique' => [ [] ], - 'index' => [ [] ], + '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' => 'popnum', + 'unique' => [ [] ], + 'index' => [ [ 'npa', 'nxx' ] ], }, 'svc_acct' => { 'columns' => [ 'svcnum', 'int', '', '', 'username', 'varchar', '', $username_len, #unique (& remove dup code) - '_password', 'varchar', '', 25, #13 for encryped pw's plus ' *SUSPENDED* + '_password', 'varchar', '', 50, #13 for encryped pw's plus ' *SUSPENDED* (mp5 passwords can be 34) 'popnum', 'int', 'NULL', '', - 'uid', 'bigint', 'NULL', '', - 'gid', 'bigint', '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' => [ [] ], - 'index' => [ ['username'] ], + 'unique' => [ [ 'username', 'domsvc' ] ], + 'index' => [ ['username'], ['domsvc'] ], }, - 'svc_acct_sm' => { - 'columns' => [ - 'svcnum', 'int', '', '', - 'domsvc', 'int', '', '', - 'domuid', 'bigint', '', '', - 'domuser', 'varchar', '', $char_d, - ], - 'primary_key' => 'svcnum', - 'unique' => [ [] ], - 'index' => [ ['domsvc'], ['domuid'] ], - }, +# 'svc_acct_sm' => { +# 'columns' => [ +# 'svcnum', 'int', '', '', +# 'domsvc', 'int', '', '', +# 'domuid', 'int', '', '', +# 'domuser', 'varchar', '', $char_d, +# ], +# 'primary_key' => 'svcnum', +# 'unique' => [ [] ], +# 'index' => [ ['domsvc'], ['domuid'] ], +# }, #'svc_charge' => { # 'columns' => [ @@ -515,12 +743,50 @@ sub tables_hash_hack { '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, + 'recaf', 'char', '', 2, + 'rectype', 'char', '', 5, + 'recdata', 'varchar', '', $char_d, + ], + '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', '', '', @@ -534,6 +800,106 @@ sub tables_hash_hack { # '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' ] ], + }, + + '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' ] ], + }, + ); %tables; diff --git a/bin/generate-prepay b/bin/generate-prepay new file mode 100755 index 0000000..cb4ba7f --- /dev/null +++ b/bin/generate-prepay @@ -0,0 +1,35 @@ +#!/usr/bin/perl -w + +use strict; +use FS::UID qw(adminsuidsetup); +use FS::prepay_credit; + +require 5.004; #srand(time|$$); + +my $user = shift or die &usage; +&adminsuidsetup( $user ); + +my $amount = shift or die &usage; + +my $seconds = shift or die &usage; + +my $num_digits = shift or die &usage; + +my $num_entries = shift or die &usage; + +for ( 1 .. $num_entries ) { + my $identifier = join( '', map int(rand(10)), ( 1 .. $num_digits ) ); + my $prepay_credit = new FS::prepay_credit { + 'identifier' => $identifier, + 'amount' => $amount, + 'seconds' => $seconds, + }; + my $error = $prepay_credit->insert; + die $error if $error; + print "$identifier\n"; +} + +sub usage { + die "Usage:\n\n generate-prepay user amount seconds num_digits num_entries"; +} + diff --git a/bin/generate-raddb b/bin/generate-raddb new file mode 100755 index 0000000..1d0053a --- /dev/null +++ b/bin/generate-raddb @@ -0,0 +1,37 @@ +#!/usr/bin/perl + +# usage: generate-raddb radius-server/raddb/dictionary* >raddb.pm +# i.e.: generate-raddb ~/src/freeradius-0.2/raddb/dictionary* >FS/raddb.pm + +print <<END; +package FS::raddb; +use vars qw(%attrib); + +%attrib = ( +END + +while (<>) { + next if /^(#|\s*$|\$INCLUDE\s+)/; + next if /^(VALUE|VENDOR|BEGIN\-VENDOR|END\-VENDOR)\s+/; + /^(ATTRIBUTE|ATTRIB_NMC)\s+([\w\-]+)\s+/ or die $_; + $attrib = $2; + $dbname = lc($2); + $dbname =~ s/\-/_/g; + $hash{$dbname} = $attrib; + #print "$2\n"; +} + +foreach ( keys %hash ) { +# print "$_\n" if length($_)>24; +# print substr($_,0,24),"\n" if length($_)>24; +# $max = length($_) if length($_)>$max; +#everything >24 is still unique, at least with freeradius comprehensive dataset + print " '". substr($_,0,24). "' => '$hash{$_}',\n"; +} + +print <<END; +); + +1; +END + diff --git a/bin/generate-tests b/bin/generate-tests new file mode 100755 index 0000000..73fd29e --- /dev/null +++ b/bin/generate-tests @@ -0,0 +1,21 @@ +#!/usr/bin/perl +@files = glob('FS/*.pm'); +foreach (@files) { +# warn $_; + chomp; + s/^FS\///; + $f=$_; + $f=~s/pm$/t/; + $m=$_; + $m=~s/\.pm$//; + open(TEST,">t/$f"); + print "t/$f\n"; + print TEST + 'BEGIN { $| = 1; print "1..1\n" }'. "\n". + 'END {print "not ok 1\n" unless $loaded;}'. "\n". + "use FS::$m;\n". + '$loaded=1;'. "\n". + 'print "ok 1\n";'. "\n" + ; + close TEST; +} diff --git a/bin/masonize b/bin/masonize new file mode 100755 index 0000000..475c9a6 --- /dev/null +++ b/bin/masonize @@ -0,0 +1,70 @@ +#!/usr/bin/perl + +foreach $file ( split(/\n/, `find . -depth -print | grep cgi\$`) ) { + open(F,$file) or die "can't open $file for reading: $!"; + @file = <F>; + #print "$file ". scalar(@file). "\n"; + close $file; + system("chmod u+w $file"); + open(W,">$file") or die "can't open $file for writing: $!"; + select W; $| = 1; select STDOUT; + $all = join('',@file); + + $mode = 'html'; + while ( length($all) ) { + + if ( $mode eq 'html' ) { + + if ( $all =~ /^(.+?)(<%=?.*)$/s && $1 !~ /<%/s ) { + print W $1; + $all = $2; + next; + } elsif ( $all =~ /^<%=(.*)$/s ) { + print W '<%'; + $all = $1; + $mode = 'perlv'; + #die; + next; + } elsif ( $all =~ /^<%(.*)$/s ) { + print W "\n"; + $all = $1; + $mode = 'perlc'; + next; + } elsif ( $all !~ /<%/s ) { + print W $all; + last; + } else { + warn length($all); die; + } + die; + + } elsif ( $mode eq 'perlv' ) { + + if ( $all =~ /^(.*?%>)(.*)$/s ) { + print W $1; + $all=$2; + $mode = 'html'; + next; + } + die 'unterminated <%= ???'; + + } elsif ( $mode eq 'perlc' ) { + + if ( $all =~ /^([^\n]*?)%>(.*)$/s ) { + print W "%$1\n"; + $all=$2; + $mode='html'; + next; + } + if ( $all =~ /^([^\n]*)\n(.*)$/s ) { + print W "%$1\n"; + $all=$2; + next; + } + + } else { die }; + + } + + close W; +} @@ -3,21 +3,54 @@ #use Pod::Text; #$Pod::Text::termcap=1; -my $site_perl = "./site_perl"; +my $site_perl = "./FS"; #my $catman = "./catman"; -my $catman = "./htdocs/docs/man"; +#my $catman = "./htdocs/docs/man"; #my $html = "./htdocs/docs/man"; +my $html = "./httemplate/docs/man"; $|=1; -die "Can't find $site_perl and $catman" - unless [ -d $site_perl ] && [ -d $catman ] && [ -d $html ]; +die "Can't find $site_perl" unless -d $site_perl; +#die "Can't find $catman" unless -d $catman; +die "Can't find $html" unless -d $html; -foreach my $file (glob("$site_perl/*.pm")) { - $file =~ /\/([\w\-]+)\.pm$/ or die "oops file $file"; - my $name = $1; - print "$name\n"; - system "pod2text $file >$catman/$name.txt"; -# system "pod2html --podpath=$site_perl $file >$html/$name.html"; +#make some useless links +foreach my $file ( + glob("$site_perl/bin/freeside-*"), +) { + next if $file =~ /\.pod$/; + #symlink $file, "$file.pod"; # or die "link $file to $file.pod: $!"; + system("cp $file $file.pod"); +} + +foreach my $file ( + glob("$site_perl/*.pm"), + glob("$site_perl/*/*.pm"), + glob("$site_perl/*/*/*.pm"), + glob("$site_perl/bin/*.pod"), + glob("./fs_sesmon/FS-SessionClient/*.pm"), + glob("./fs_signup/FS-SignupClient/*.pm"), + glob("./fs_selfadmin/FS-MailAdminServer/*.pm"), +) { + next if $file =~ /^blib\//; + #$file =~ /\/([\w\-]+)\.pm$/ or die "oops file $file"; + my $name; + if ( $file =~ /fs_\w+\/FS\-\w+\/(.*)\.pm$/ ) { + $name = "FS/$1"; + } elsif ( $file =~ /$site_perl\/(.*)\.(pm|pod)$/ ) { + $name = $1; + } else { + die "oops file $file"; + } + print "$name\n"; + my $htmlroot = join('/', map '..',1..(scalar($file =~ tr/\///)-2)) || '.'; +# system "pod2text $file >$catman/$name.txt"; + system "pod2html --podroot=$site_perl --podpath=./FS:./FS/UI:.:./bin --norecurse --htmlroot=$htmlroot $file >$html/$name.html"; + #system "pod2html --podroot=$site_perl --htmlroot=$htmlroot $file >$html/$name.html"; # system "pod2html $file >$html/$name.html"; } + +#remove the useless links +unlink glob("$site_perl/bin/*.pod"); + diff --git a/bin/svc_acct.export b/bin/svc_acct.export index 3f65a08..82a8935 100755 --- a/bin/svc_acct.export +++ b/bin/svc_acct.export @@ -1,108 +1,112 @@ -#!/usr/bin/perl -Tw +#!/usr/bin/perl -w # -# Create and export password files: passwd, passwd.adjunct, shadow, -# acp_passwd, acp_userinfo, acp_dialup, users +# $Id: svc_acct.export,v 1.34 2002-02-23 02:14:26 jeff Exp $ # -# ivan@voicenet.com late august/september 96 -# (the password encryption bits were from melody) -# -# use a temporary copy of svc_acct to minimize lock time on the real file, -# and skip blank entries. -# -# ivan@voicenet.com 96-Oct-6 -# -# change users / acp_dialup file formats -# ivan@voicenet.com 97-jan-28-31 -# -# change priority (after copies) to 19, not 10 -# ivan@voicenet.com 97-feb-5 -# -# added exit if stuff is already locked 97-apr-15 -# -# rewrite ivan@sisd.com 98-mar-9 -# -# Changed 'password' to '_password' because Pg6.3 reserves this word -# Added code to create a FreeBSD style master.passwd file -# bmccane@maxbaud.net 98-Apr-3 -# -# don't export non-root 0 UID's, even if they get put in the database -# ivan@sisd.com 98-jul-14 -# -# Uses Idle_Timeout, Port_Limit, Framed_Netmask and Framed_Route if they -# exist; need some way to support arbitrary radius fields. also -# /var/spool/freeside/conf/ ivan@sisd.com 98-jul-26, aug-9 -# -# OOPS! added arbitrary radius fields (pry 98-aug-16) but forgot to say so. -# ivan@sisd.com 98-sep-18 +# Create and export password, radius and vpopmail password files: +# passwd, passwd.adjunct, shadow, acp_passwd, acp_userinfo, acp_dialup +# users/assign, domains/vdomain/vpasswd +# Also export sendmail and qmail config files. use strict; +use vars qw($conf); use Fcntl qw(:flock); -use FS::SSH qw(scp ssh); -use FS::UID qw(adminsuidsetup); -use FS::Record qw(qsearch fields); - -my($fshellmachines)="/var/spool/freeside/conf/shellmachines"; -my(@shellmachines); -if ( -e $fshellmachines ) { - open(SHELLMACHINES,$fshellmachines); - @shellmachines=map { - /^(.*)$/ or die "Illegal line in conf/shellmachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <SHELLMACHINES>; - close SHELLMACHINES; +use File::Path; +use IO::Handle; +use FS::Conf; +use Net::SSH qw(ssh); +use Net::SCP qw(scp); +use FS::UID qw(adminsuidsetup datasrc dbh); +use FS::Record qw(qsearch qsearchs fields); +use FS::svc_acct; +use FS::svc_domain; +use FS::svc_forward; + +my $ssh='ssh'; +my $rsync='rsync'; + +my $user = shift or die &usage; +adminsuidsetup $user; + +$conf = new FS::Conf; + +my $userpolicy = $conf->config('username_policy') + if $conf->exists('username_policy'); + +my @shellmachines = $conf->config('shellmachines') + if $conf->exists('shellmachines'); + +my @bsdshellmachines = $conf->config('bsdshellmachines') + if $conf->exists('bsdshellmachines'); + +my @nismachines = $conf->config('nismachines') + if $conf->exists('nismachines'); + +my @erpcdmachines = $conf->config('erpcdmachines') + if $conf->exists('erpcdmachines'); + +my @radiusmachines = $conf->config('radiusmachines') + if $conf->exists('radiusmachines'); + +my $icradiusmachines = $conf->exists('icradiusmachines'); +my @icradiusmachines = $conf->config('icradiusmachines') if $icradiusmachines; +my $icradius_mysqldest = + $conf->config('icradius_mysqldest') || "/usr/local/var" + if $icradiusmachines; +my $icradius_mysqlsource = + $conf->config('icradius_mysqlsource') || "/usr/local/var/freeside" + if $icradiusmachines; +my $icradius_dbh; +if ( $icradiusmachines && $conf->exists('icradius_secrets') ) { + $icradius_dbh = DBI->connect($conf->config('icradius_secrets')) + or die $DBI::errstr; +} else { + $icradius_dbh = dbh; } -my($fbsdshellmachines)="/var/spool/freeside/conf/bsdshellmachines"; -my(@bsdshellmachines); -if ( -e $fbsdshellmachines ) { - open(BSDSHELLMACHINES,$fbsdshellmachines); - @bsdshellmachines=map { - /^(.*)$/ or die "Illegal line in conf/bsdshellmachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <BSDSHELLMACHINES>; - close BSDSHELLMACHINES; -} +my $textradiusprepend = + $conf->exists('textradiusprepend') + ? $conf->config('textradiusprepend') + : ''; -my($fnismachines)="/var/spool/freeside/conf/nismachines"; -my(@nismachines); -if ( -e $fnismachines ) { - open(NISMACHINES,$fnismachines); - @nismachines=map { - /^(.*)$/ or die "Illegal line in conf/nismachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <NISMACHINES>; - close NISMACHINES; -} +warn "using depriciated textradiusprepend file" if $textradiusprepend; + + +my $radiusprepend = + $conf->exists('radiusprepend') + ? join("\n", $conf->config('radiusprepend')) + : ''; + +my @vpopmailmachines = $conf->config('vpopmailmachines') + if $conf->exists('vpopmailmachines'); +my $vpopmailrestart = ''; +$vpopmailrestart = $conf->config('vpopmailrestart') + if $conf->exists('vpopmailrestart'); -my($ferpcdmachines)="/var/spool/freeside/conf/erpcdmachines"; -my(@erpcdmachines); -if ( -e $ferpcdmachines ) { - open(ERPCDMACHINES,$ferpcdmachines); - @erpcdmachines=map { - /^(.*)$/ or die "Illegal line in conf/erpcdmachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <ERPCDMACHINES>; - close ERPCDMACHINES; +my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachines[0]) if $vpopmailmachines[0]; + +my($shellmachine, @qmailmachines); +if ( $conf->exists('qmailmachines') ) { + $shellmachine = $conf->config('shellmachine'); + @qmailmachines = $conf->config('qmailmachines'); } -my($fradiusmachines)="/var/spool/freeside/conf/radiusmachines"; -my(@radiusmachines); -if ( -e $fradiusmachines ) { - open(RADIUSMACHINES,$fradiusmachines); - @radiusmachines=map { - /^(.*)$/ or die "Illegal line in conf/radiusmachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <RADIUSMACHINES>; - close RADIUSMACHINES; +my(@sendmailmachines, $sendmailconfigpath, $sendmailrestart); +if ( $conf->exists('sendmailmachines') ) { + @sendmailmachines = $conf->config('sendmailmachines'); + $sendmailconfigpath = $conf->config('sendmailconfigpath') || '/etc'; + $sendmailrestart = $conf->config('sendmailrestart'); } -my($spooldir)="/var/spool/freeside/export"; -my($spoollock)="/var/spool/freeside/svc_acct.export.lock"; +my $mydomain = $conf->config('domain') if $conf->exists('domain'); + + -adminsuidsetup; my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' ); -srand(time|$$); +require 5.004; #srand(time|$$); + +my $spooldir = "/usr/local/etc/freeside/export.". datasrc; +my $spoollock = "/usr/local/etc/freeside/svc_acct.export.lock.". datasrc; open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!"; select(EXPORT); $|=1; select(STDOUT); @@ -110,159 +114,426 @@ unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) { seek(EXPORT,0,0); my($pid)=<EXPORT>; chop($pid); - #no reason to start loct of blocking processes + #no reason to start lots of blocking processes die "Is another export process running under pid $pid?\n"; } seek(EXPORT,0,0); print EXPORT $$,"\n"; -my(@svc_acct)=qsearch('svc_acct',{}); +my(@svc_domain)=qsearch('svc_domain',{}); ( open(MASTER,">$spooldir/master.passwd") - and flock(MASTER,LOCK_EX|LOCK_NB) -) or die "Can't open $spooldir/master.passwd: $!"; + and flock(MASTER,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/.master.passwd: $!"; ( open(PASSWD,">$spooldir/passwd") and flock(PASSWD,LOCK_EX|LOCK_NB) ) or die "Can't open $spooldir/passwd: $!"; ( open(SHADOW,">$spooldir/shadow") - and flock(SHADOW,LOCK_EX|LOCK_NB) + and flock(SHADOW,LOCK_EX|LOCK_NB) ) or die "Can't open $spooldir/shadow: $!"; -( open(ACP_PASSWD,">$spooldir/acp_passwd") - and flock (ACP_PASSWD,LOCK_EX|LOCK_NB) +( open(ACP_PASSWD,">$spooldir/acp_passwd") + and flock(ACP_PASSWD,LOCK_EX|LOCK_NB) ) or die "Can't open $spooldir/acp_passwd: $!"; -( open (ACP_DIALUP,">$spooldir/acp_dialup") - and flock(ACP_DIALUP,LOCK_EX|LOCK_NB) +( open(ACP_DIALUP,">$spooldir/acp_dialup") + and flock(ACP_DIALUP,LOCK_EX|LOCK_NB) ) or die "Can't open $spooldir/acp_dialup: $!"; -( open (USERS,">$spooldir/users") - and flock(USERS,LOCK_EX|LOCK_NB) +( open(USERS,">$spooldir/users") + and flock(USERS,LOCK_EX|LOCK_NB) ) or die "Can't open $spooldir/users: $!"; +( open(ASSIGN,">$spooldir/assign") + and flock(ASSIGN,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/assign: $!"; +( open(RCPTHOSTS,">$spooldir/rcpthosts") + and flock(RCPTHOSTS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/rcpthosts: $!"; +( open(VPOPRCPTHOSTS,">$spooldir/vpoprcpthosts") + and flock(VPOPRCPTHOSTS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/rcpthosts: $!"; +( open(RECIPIENTMAP,">$spooldir/recipientmap") + and flock(RECIPIENTMAP,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/recipientmap: $!"; +( open(VIRTUALDOMAINS,">$spooldir/virtualdomains") + and flock(VIRTUALDOMAINS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/virtualdomains: $!"; +( open(VPOPVIRTUALDOMAINS,">$spooldir/vpopvirtualdomains") + and flock(VPOPVIRTUALDOMAINS,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/virtualdomains: $!"; +( open(VIRTUSERTABLE,">$spooldir/virtusertable") + and flock(VIRTUSERTABLE,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/virtusertable: $!"; +( open(SENDMAIL_CW,">$spooldir/sendmail.cw") + and flock(SENDMAIL_CW,LOCK_EX|LOCK_NB) +) or die "Can't open $spooldir/sendmail.cw: $!"; + + + chmod 0644, "$spooldir/passwd", "$spooldir/acp_dialup", + "$spooldir/assign", + "$spooldir/sendmail.cw", + "$spooldir/virtusertable", + "$spooldir/rcpthosts", + "$spooldir/vpoprcpthosts", + "$spooldir/recipientmap", + "$spooldir/virtualdomains", + "$spooldir/vpopvirtualdomains", + ; chmod 0600, "$spooldir/master.passwd", - "$spooldir/acp_passwd", + "$spooldir/acp_passwd", "$spooldir/shadow", "$spooldir/users", ; -setpriority(0,0,10); +rmtree"$spooldir/domains", 0, 1; +mkdir "$spooldir/domains", 0700; -my($svc_acct); -foreach $svc_acct (@svc_acct) { - - my($password)=$svc_acct->getfield('_password'); - my($cpassword,$rpassword); - if ( ( length($password) <= 8 ) - && ( $password ne '*' ) - && ( $password ne '' ) - ) { - $cpassword=crypt($password, - $saltset[int(rand(64))].$saltset[int(rand(64))] - ); - $rpassword=$password; - } else { - $cpassword=$password; - $rpassword='UNIX'; - } +if ( $icradiusmachines ) { + my $sth = $icradius_dbh->prepare("DELETE FROM radcheck"); + $sth->execute or die "Can't reset radcheck table: ". $sth->errstr; + my $sth2 = $icradius_dbh->prepare("DELETE FROM radreply"); + $sth2->execute or die "Can't reset radreply table: ". $sth2->errstr; +} - if ( $svc_acct->uid =~ /^(\d+)$/ ) { +setpriority(0,0,10); - die "Non-root user ". $svc_acct->username. " has 0 UID!" - if $svc_acct->uid == 0 && $svc_acct->username ne 'root'; +print USERS "$radiusprepend\n"; + +my %usernames; ## this hack helps keep the passwd files sane +my @sendmail; + +my $svc_domain; +foreach $svc_domain (sort {$a->domain cmp $b->domain} @svc_domain) { + + my($domain)=$svc_domain->domain; + print RCPTHOSTS "$domain\n.$domain\n"; + print VPOPRCPTHOSTS "$domain\n"; + print SENDMAIL_CW "$domain\n"; + + ### + # FORMAT OF THE ASSIGN/USERS FILE HERE + print ASSIGN join(":", + "+" . $domain . "-", + $domain, + $vpopuid, + $vpopgid, + $vpopdir . "/domains/" . $domain, + "-", + "", + "", + ), "\n" if $vpopmailmachines[0]; + + (mkdir "$spooldir/domains/" . $domain, 0700) + or die "Can't create $spooldir/domains/" . $domain .": $!"; + + ( open(QMAILDEFAULT,">$spooldir/domains/" . $domain . "/.qmail-default") + and flock(QMAILDEFAULT,LOCK_EX|LOCK_NB) + ) or die "Can't open $spooldir/domains/" . $domain . "/.qmail-default: $!"; + + ( open(VPASSWD,">$spooldir/domains/" . $domain . "/vpasswd") + and flock(VPASSWD,LOCK_EX|LOCK_NB) + ) or die "Can't open $spooldir/domains/" . $domain . "/vpasswd: $!"; + + my ($svc_acct); + + if ($svc_domain->getfield('catchall')) { + $svc_acct = qsearchs('svc_acct', {'svcnum' => $svc_domain->catchall}); + die "Cannot find catchall account for domain $domain\n" unless $svc_acct; + + my $username = $svc_acct->username; + push @sendmail, "\@$domain\t$username\n"; + print VIRTUALDOMAINS "$domain:$username-$domain\n", + ".$domain:$username-$domain\n", + ; ### - # FORMAT OF FreeBSD MASTER PASSWD FILE HERE - print MASTER join(":", - $svc_acct->username, # User name - $cpassword, # Encrypted password - $svc_acct->uid, # User ID - $svc_acct->gid, # Group ID - "", # Login Class - "0", # Password Change Time - "0", # Password Expiration Time - $svc_acct->finger, # Users name - $svc_acct->dir, # Users home directory - $svc_acct->shell, # shell - ), "\n" ; + # FORMAT OF THE .QMAIL-DEFAULT FILE HERE + print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" " . $svc_acct->email . "\n" + if $vpopmailmachines[0]; + }else{ ### - # FORMAT OF THE PASSWD FILE HERE - print PASSWD join(":", - $svc_acct->username, - 'x', # "##". $svc_acct->$username, - $svc_acct->uid, - $svc_acct->gid, - $svc_acct->finger, - $svc_acct->dir, - $svc_acct->shell, - ), "\n"; + # FORMAT OF THE .QMAIL-DEFAULT FILE HERE + print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" bounce-no-mailbox\n" + if $vpopmailmachines[0]; + } - ### - # FORMAT OF THE SHADOW FILE HERE - print SHADOW join(":", - $svc_acct->username, - $cpassword, - '', - '', - '', - '', - '', - '', - '', - ), "\n"; + print VPOPVIRTUALDOMAINS "$domain:$domain\n"; + + foreach $svc_acct (qsearch('svc_acct', {'domsvc' => $svc_domain->svcnum})) { + my($password)=$svc_acct->getfield('_password'); + my($cpassword,$rpassword); + #if ( ( length($password) <= 8 ) + if ( ( length($password) <= 12 ) + && ( $password ne '*' ) + && ( $password ne '!!' ) + && ( $password ne '' ) + ) { + $cpassword=crypt($password, + $saltset[int(rand(64))].$saltset[int(rand(64))] + ); + $rpassword=$password; + } else { + $cpassword=$password; + $rpassword='UNIX'; + } - } + my $username; + + if ($mydomain && ($mydomain eq $svc_domain->domain)) { + $username=$svc_acct->username; + } elsif ($userpolicy =~ /^prepend domsvc$/) { + $username=$svc_acct->domsvc . $svc_acct->username; + } elsif ($userpolicy =~ /^append domsvc$/) { + $username=$svc_acct->username . $svc_acct->domsvc; + } elsif ($userpolicy =~ /^append domain$/) { + $username=$svc_acct->username . $svc_domain->domain; + } elsif ($userpolicy =~ /^append domain$/) { + $username=$svc_acct->username . $svc_domain->domain; + } elsif ($userpolicy =~ /^append \@domain$/) { + $username=$svc_acct->username . '@'. $svc_domain->domain; + } else { + die "Unknown policy in username_policy\n"; + } - if ( $svc_acct->slipip ne '' ) { + if ($svc_acct->dir ne '/dev/null' || $svc_acct->slipip ne '') { + if ($usernames{$username}++) { + die "Duplicate username detected: $username\n"; + } + } + + if ( $svc_acct->uid =~ /^(\d+)$/ ) { + + die "Non-root user ". $svc_acct->username. " has 0 UID!" + if $svc_acct->uid == 0 && $svc_acct->username ne 'root'; + + if ( $svc_acct->dir ne "/dev/null") { + + ### + # FORMAT OF FreeBSD MASTER PASSWD FILE HERE + print MASTER join(":", + $username, # User name + $cpassword, # Encrypted password + $svc_acct->uid, # User ID + $svc_acct->gid, # Group ID + "", # Login Class + "0", # Password Change Time + "0", # Password Expiration Time + $svc_acct->finger, # Users name + $svc_acct->dir, # Users home directory + $svc_acct->shell, # shell + ), "\n" ; + + + ### + # FORMAT OF THE PASSWD FILE HERE + print PASSWD join(":", + $username, + 'x', # "##". $username, + $svc_acct->uid, + $svc_acct->gid, + $svc_acct->finger, + $svc_acct->dir, + $svc_acct->shell, + ), "\n"; + + ### + # FORMAT OF THE SHADOW FILE HERE + print SHADOW join(":", + $username, + $cpassword, + '', + '', + '', + '', + '', + '', + '', + ), "\n"; + } + } ### - # FORMAT OF THE ACP_* FILES HERE - print ACP_PASSWD join(":", + # FORMAT OF THE VPASSWD FILE HERE + print VPASSWD join(":", $svc_acct->username, $cpassword, - "0", - "0", - "", - "", - "", + '1', + '0', + $svc_acct->username, + "$vpopdir/domains/" . $svc_domain->domain ."/" . $svc_acct->username, + 'NOQUOTA', ), "\n"; - my($ip)=$svc_acct->slipip; - unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) { - print ACP_DIALUP $svc_acct->username, "\t*\t", $svc_acct->slipip, "\n"; + if ( $svc_acct->slipip ne '' ) { + + ### + # FORMAT OF THE ACP_* FILES HERE + print ACP_PASSWD join(":", + $username, + $cpassword, + "0", + "0", + "", + "", + "", + ), "\n"; + + my($ip)=$svc_acct->slipip; + + unless ( $ip eq '0.0.0.0' || $svc_acct->slipip eq '0e0' ) { + print ACP_DIALUP $username, "\t*\t", $svc_acct->slipip, "\n"; + } + + my %radreply = $svc_acct->radius_reply; + my %radcheck = $svc_acct->radius_check; + + my $radcheck = join ", ", map { qq($_ = "$radcheck{$_}") } keys %radcheck; + $radcheck .= ", " if $radcheck; + + ### + # FORMAT OF THE USERS FILE HERE + print USERS + $username, + qq(\t${textradiusprepend}), + $radcheck, + qq(Password = "$rpassword"\n\t), + join ",\n\t", map { qq($_ = "$radreply{$_}") } keys %radreply; + + if ( $ip && $ip ne '0e0' ) { + #print USERS qq(,\n\tFramed-Address = "$ip"\n\n); + print USERS qq(,\n\tFramed-IP-Address = "$ip"\n\n); + } else { + print USERS qq(\n\n); + } + + ### + # ICRADIUS export + if ( $icradiusmachines ) { + + my $sth = $icradius_dbh->prepare( + "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". + join(", ", map { $icradius_dbh->quote( $_ ) } ( + '', + $username, + "Password", + $svc_acct->_password, + ) ). " )" + ); + $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr; + + foreach my $attribute ( keys %radcheck ) { + my $sth = $icradius_dbh->prepare( + "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". + join(", ", map { $icradius_dbh->quote( $_ ) } ( + '', + $username, + $attribute, + $radcheck{$attribute}, + ) ). " )" + ); + $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr; } + + foreach my $attribute ( keys %radreply ) { + my $sth = $icradius_dbh->prepare( + "INSERT INTO radreply (id, UserName, Attribute, Value) VALUES ( ". + join(", ", map { $icradius_dbh->quote( $_ ) } ( + '', + $username, + $attribute, + $radreply{$attribute}, + ) ). " )" + ); + $sth->execute or die "Can't insert into radreply table: ". $sth->errstr; } + + if ( $ip && $ip ne '0e0' ) { + my $sth = $icradius_dbh->prepare( + "INSERT INTO radreply (id, UserName, Attribute, Value) VALUES ( ". + join(", ", map { $icradius_dbh->quote( $_ ) } ( + '', + $username, + 'Framed-IP-Address', + $ip, + ) ). " )" + ); + $sth->execute or die "Can't insert into radreply table: ". $sth->errstr; } + } } - + ### - # FORMAT OF THE USERS FILE HERE - print USERS - $svc_acct->username, qq(\tPassword = "$rpassword"\n\t), - - join ",\n\t", - map { - /^(radius_(.*))$/; - my($field,$attrib)=($1,$2); - $attrib =~ s/_/\-/g; - "$attrib = \"". $svc_acct->getfield($field). "\""; - } grep /^radius_/ && $svc_acct->getfield($_), fields('svc_acct') - ; - if ( $ip && $ip ne '0e0' ) { - print USERS qq(,\n\tFramed-Address = "$ip"\n\n); - } else { - print USERS qq(\n\n); + # vpopmail directory structure creation + + (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username, 0700) + or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . ": $!"; + (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir", 0700) + or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir: $!"; + (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/cur", 0700) + or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/cur: $!"; + (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/new", 0700) + or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/new: $!"; + (mkdir "$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/Maildir/tmp", 0700) + or die "Can't create $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . " /Maildir/tmp: $!"; + + ( open(DOTQMAIL,">$spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/.qmail") + and flock(DOTQMAIL,LOCK_EX|LOCK_NB) + ) or die "Can't open $spooldir/domains/" . $svc_domain->domain . "/" . $svc_acct->username . "/.qmail: $!"; + + my($svc_forward); + foreach $svc_forward (qsearch('svc_forward', {'srcsvc' => $svc_acct->svcnum})) { + my($destination); + if ($svc_forward->dstsvc) { + my $dst_acct = qsearchs('svc_acct', {'svcnum' => $svc_forward->dstsvc}); + my $dst_domain = qsearchs('svc_domain', {'svcnum' => $dst_acct->domsvc}); + $destination = $dst_acct->username . '@' . $dst_domain->domain; + + if ($dst_domain->domain eq $mydomain) { + print VIRTUSERTABLE $svc_acct->username . "@" . $svc_domain->domain . + "\t" . $dst_acct->username . "\n"; + print RECIPIENTMAP $svc_acct->username . "@" . $svc_domain->domain . + ":$destination\n"; + } + } else { + $destination = $svc_forward->dst; + } + + ### + # FORMAT OF .QMAIL FILES HERE + print DOTQMAIL "$destination\n"; } + flock(DOTQMAIL,LOCK_UN); + close DOTQMAIL; + } + flock(VPASSWD,LOCK_UN); + flock(QMAILDEFAULT,LOCK_UN); + close VPASSWD; + close QMAILDEFAULT; + } +### +# FORMAT OF THE ASSIGN/USERS FILE FINAL LINE HERE +print ASSIGN ".\n"; + +print VIRTUSERTABLE @sendmail; + flock(MASTER,LOCK_UN); flock(PASSWD,LOCK_UN); flock(SHADOW,LOCK_UN); flock(ACP_DIALUP,LOCK_UN); flock(ACP_PASSWD,LOCK_UN); flock(USERS,LOCK_UN); +flock(ASSIGN,LOCK_UN); +flock(SENDMAIL_CW,LOCK_UN); +flock(VIRTUSERTABLE,LOCK_UN); +flock(RCPTHOSTS,LOCK_UN); +flock(VPOPRCPTHOSTS,LOCK_UN); +flock(RECIPIENTMAP,LOCK_UN); +flock(VPOPVIRTUALDOMAINS,LOCK_UN); close MASTER; close PASSWD; @@ -270,18 +541,26 @@ close SHADOW; close ACP_DIALUP; close ACP_PASSWD; close USERS; +close ASSIGN; +close SENDMAIL_CW; +close VIRTUSERTABLE; +close RCPTHOSTS; +close VPOPRCPTHOSTS; +close RECIPIENTMAP; +close VPOPVIRTUALDOMAINS; ### # export stuff # -my($shellmachine); -foreach $shellmachine (@shellmachines) { - scp("$spooldir/passwd","root\@$shellmachine:/etc/passwd.new") - == 0 or die "scp error: $!"; - scp("$spooldir/shadow","root\@$shellmachine:/etc/shadow.new") - == 0 or die "scp error: $!"; - ssh("root\@$shellmachine", +my($ashellmachine); +foreach $ashellmachine (@shellmachines) { + my $scp = new Net::SCP; + $scp->scp("$spooldir/passwd","root\@$ashellmachine:/etc/passwd.new") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/shadow","root\@$ashellmachine:/etc/shadow.new") + or die "scp error: ". $scp->{errstr}; + ssh("root\@$ashellmachine", "( ". "mv /etc/passwd.new /etc/passwd; ". "mv /etc/shadow.new /etc/shadow; ". @@ -292,14 +571,16 @@ foreach $shellmachine (@shellmachines) { my($bsdshellmachine); foreach $bsdshellmachine (@bsdshellmachines) { - scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new") - == 0 or die "scp error: $!"; - scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new") - == 0 or die "scp error: $!"; + my $scp = new Net::SCP; + $scp->scp("$spooldir/passwd","root\@$bsdshellmachine:/etc/passwd.new") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/master.passwd","root\@$bsdshellmachine:/etc/master.passwd.new") + or die "scp error: ". $scp->{errstr}; ssh("root\@$bsdshellmachine", "( ". "mv /etc/passwd.new /etc/passwd; ". - "mv /etc/master.passwd.new /etc/master.passwd; ". + #"mv /etc/master.passwd.new /etc/master.passwd; ". + "pwd_mkdb /etc/master.passwd.new; ". " )" ) == 0 or die "ssh error: $!"; @@ -307,10 +588,11 @@ foreach $bsdshellmachine (@bsdshellmachines) { my($nismachine); foreach $nismachine (@nismachines) { - scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd") - == 0 or die "scp error: $!"; - scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow") - == 0 or die "scp error: $!"; + my $scp = new Net::SCP; + $scp->scp("$spooldir/passwd","root\@$nismachine:/etc/global/passwd") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/shadow","root\@$nismachine:/etc/global/shadow") + or die "scp error: ". $scp->{errstr}; ssh("root\@$nismachine", "( ". "cd /var/yp; make; ". @@ -321,10 +603,11 @@ foreach $nismachine (@nismachines) { my($erpcdmachine); foreach $erpcdmachine (@erpcdmachines) { - scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd") - == 0 or die "scp error: $!"; - scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup") - == 0 or die "scp error: $!"; + my $scp = new Net::SCP; + $scp->scp("$spooldir/acp_passwd","root\@$erpcdmachine:/usr/annex/acp_passwd") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/acp_dialup","root\@$erpcdmachine:/usr/annex/acp_dialup") + or die "scp error: ". $scp->{errstr}; ssh("root\@$erpcdmachine", "( ". "kill -USR1 \`cat /usr/annex/erpcd.pid\'". @@ -335,9 +618,10 @@ foreach $erpcdmachine (@erpcdmachines) { my($radiusmachine); foreach $radiusmachine (@radiusmachines) { - scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users") - == 0 or die "scp error: $!"; - ssh("root\@$erpcdmachine", + my $scp = new Net::SCP; + $scp->scp("$spooldir/users","root\@$radiusmachine:/etc/raddb/users") + or die "scp error: ". $scp->{errstr}; + ssh("root\@$radiusmachine", "( ". "builddbm". " )" @@ -345,7 +629,108 @@ foreach $radiusmachine (@radiusmachines) { == 0 or die "ssh error: $!"; } +foreach my $icradiusmachine ( @icradiusmachines ) { + my( $machine, $db, $user, $pass ) = split(/\s+/, $icradiusmachine); + chdir $icradius_mysqlsource or die "Can't cd $icradius_mysqlsource: $!"; + open(WRITER,"|ssh root\@$machine mysql -v --user=$user -p $db"); + my $oldfh = select WRITER; $|=1; select $oldfh; + print WRITER "$pass\n"; + sleep 2; + print WRITER "LOCK TABLES radcheck WRITE, radreply WRITE;\n"; + foreach my $file ( glob("radcheck.*") ) { + my $scp = new Net::SCP; + $scp->scp($file,"root\@$machine:$icradius_mysqldest/$db/$file") + or die "scp error: ". $scp->{errstr}; + } + foreach my $file ( glob("radreply.*") ) { + my $scp = new Net::SCP; + $scp->scp($file,"root\@$machine:$icradius_mysqldest/$db/$file") + or die "scp error: ". $scp->{errstr}; + } + close WRITER; +} + +#my @args = ("/bin/tar", "c", "--force-local", "-C", "$spooldir", "-f", "$spooldir/vpoptarball", "domains"); + +#system {$args[0]} @args; + +my($vpopmailmachine); +foreach $vpopmailmachine (@vpopmailmachines) { + my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachine); + my $scp = new Net::SCP; +# $scp->scp("$spooldir/vpoptarball","root\@$machine:vpoptarball") +# or die "scp error: ". $scp->{errstr}; +# ssh("root\@$machine", +# "( ". +# "rm -rf domains; ". +# "tar xf vpoptarball; ". +# "chown -R $vpopuid:$vpopgid domains; ". +# "tar cf vpoptarball domains; ". +# "cd $vpopdir; ". +# "tar xf ~/vpoptarball; ". +# " )" +# ) +# == 0 or die "ssh error: $!"; + + chdir $spooldir; + my @args = ("$rsync", "-rlpt", "-e", "$ssh", "domains/", "vpopmail\@$machine:$vpopdir/domains/"); + + system {$args[0]} @args; + + $scp->scp("$spooldir/assign","root\@$machine:/var/qmail/users/assign") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/vpopvirtualdomains","root\@$machine:/var/qmail/control/virtualdomains") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/vpoprcpthosts","root\@$machine:/var/qmail/control/rcpthosts") + or die "scp error: ". $scp->{errstr}; + + ssh("root\@$machine", + "( ". + $vpopmailrestart . + " )" + ) + == 0 or die "ssh error: $!"; + + +} + +my($sendmailmachine); +foreach $sendmailmachine (@sendmailmachines) { + my $scp = new Net::SCP; + $scp->scp("$spooldir/sendmail.cw","root\@$sendmailmachine:$sendmailconfigpath/sendmail.cw.new") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/virtusertable","root\@$sendmailmachine:$sendmailconfigpath/virtusertable.new") + or die "scp error: ". $scp->{errstr}; + ssh("root\@$sendmailmachine", + "( ". + "mv $sendmailconfigpath/sendmail.cw.new $sendmailconfigpath/sendmail.cw; ". + "mv $sendmailconfigpath/virtusertable.new $sendmailconfigpath/virtusertable; ". + $sendmailrestart. + " )" + ) + == 0 or die "ssh error: $!"; +} + +my($qmailmachine); +foreach $qmailmachine (@qmailmachines) { + my $scp = new Net::SCP; + $scp->scp("$spooldir/recipientmap","root\@$qmailmachine:/var/qmail/control/recipientmap") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/virtualdomains","root\@$qmailmachine:/var/qmail/control/virtualdomains") + or die "scp error: ". $scp->{errstr}; + $scp->scp("$spooldir/rcpthosts","root\@$qmailmachine:/var/qmail/control/rcpthosts") + or die "scp error: ". $scp->{errstr}; + #ssh("root\@$qmailmachine","/etc/init.d/qmail restart") + # == 0 or die "ssh error: $!"; +} + unlink $spoollock; flock(EXPORT,LOCK_UN); close EXPORT; +# + +sub usage { + die "Usage:\n\n svc_acct.export user\n"; +} + diff --git a/bin/svc_acct.import b/bin/svc_acct.import index c4b8c5e..eb94e1c 100755 --- a/bin/svc_acct.import +++ b/bin/svc_acct.import @@ -1,31 +1,22 @@ #!/usr/bin/perl -Tw -# -# ivan@sisd.com 98-mar-9 -# -# changed 'password' field to '_password' because PgSQL 6.3 reserves this word -# bmccane@maxbaud.net 98-Apr-3 -# -# generalized svcparts (still needs radius import) ivan@sisd.com 98-mar-23 -# -# radius import, now an interactive script. still needs erpcd import? -# ivan@sisd.com 98-jun-24 -# -# arbitrary radius attributes ivan@sisd.com 98-aug-9 -# -# don't import /var/spool/freeside/conf/shells! ivan@sisd.com 98-aug-13 +# $Id: svc_acct.import,v 1.17 2001-08-19 10:25:44 ivan Exp $ use strict; use vars qw(%part_svc); use Date::Parse; -use FS::SSH qw(iscp); -use FS::UID qw(adminsuidsetup); +use Term::Query qw(query); +use Net::SCP qw(iscp); +use FS::UID qw(adminsuidsetup datasrc); use FS::Record qw(qsearch); use FS::svc_acct; +use FS::part_svc; + +my $user = shift or die &usage; +adminsuidsetup $user; -adminsuidsetup; +push @FS::svc_acct::shells, qw(/bin/sync /sbin/shuddown /bin/halt); #others? -#my($spooldir)="/var/spool/freeside/export"; -my($spooldir)="unix/"; +my($spooldir)="/usr/local/etc/freeside/export.". datasrc; $FS::svc_acct::nossh_hack = 1; @@ -33,6 +24,8 @@ $FS::svc_acct::nossh_hack = 1; %part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'}); +die "No services with svcdb svc_acct!\n" unless %part_svc; + print "\n\n", &menu_svc, "\n", <<END; Most accounts probably have entries in passwd and users (with Port-Limit nonexistant or 1). @@ -58,8 +51,7 @@ my($oisdn_svcpart)=&getpart; print "\n\n", &menu_svc, "\n", <<END; POP mail accounts have entries in passwd only, and have a particular shell. END -print "Enter that shell: "; -my($pop_shell)=&getvalue; +my($pop_shell)=&getvalue("Enter that shell:"); my($popmail_svcpart)=&getpart; print "\n\n", &menu_svc, "\n", <<END; @@ -71,37 +63,38 @@ print "\n\n", <<END; Enter the location and name of your _user_ passwd file, for example "mail.isp.com:/etc/passwd" or "nis.isp.com:/etc/global/passwd" END -print ":"; -my($loc_passwd)=&getvalue; +my($loc_passwd)=&getvalue(":"); iscp("root\@$loc_passwd", "$spooldir/passwd.import"); print "\n\n", <<END; Enter the location and name of your _user_ shadow file, for example "mail.isp.com:/etc/shadow" or "bsd.isp.com:/etc/master.passwd" END -print ":"; -my($loc_shadow)=&getvalue; +my($loc_shadow)=&getvalue(":"); iscp("root\@$loc_shadow", "$spooldir/shadow.import"); print "\n\n", <<END; Enter the location and name of your radius "users" file, for example "radius.isp.com:/etc/raddb/users" END -print ":"; -my($loc_users)=&getvalue; +my($loc_users)=&getvalue(":"); iscp("root\@$loc_users", "$spooldir/users.import"); sub menu_svc { ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n"; } sub getpart { - print "Enter part number, or 0 for none: "; - &getvalue; + $^W=0; # Term::Query isn't -w-safe + my $return = query "Enter part number:", 'irk', [ keys %part_svc ]; + $^W=1; + $return; } sub getvalue { - my($x)=scalar(<STDIN>); - chop $x; - $x; + my $prompt = shift; + $^W=0; # Term::Query isn't -w-safe + my $return = query $prompt, ''; + $^W=1; + $return; } print "\n\n"; @@ -116,12 +109,14 @@ my(%upassword,%ip,%allparam); my(%param,$username); while (<USERS>) { chop; - next if /^$/; + next if /^\s*$/; + next if /^\s*#/; if ( /^\S/ ) { - /^(\w+)\s+Password\s+=\s+"([^"]+)"(,\s+Expiration\s+=\s+"([^"]*")\s*)?$/ + /^(\w+)\s+(Auth-Type\s+=\s+Local,\s+)?Password\s+=\s+"([^"]+)"(,\s+Expiration\s+=\s+"([^"]*")\s*)?$/ or die "1Unexpected line in users.import: $_"; my($password,$expiration); - ($username,$password,$expiration)=(lc($1),$2,$4); + ($username,$password,$expiration)=(lc($1),$3,$5); + $password = '' if $password eq 'UNIX'; $upassword{$username}=$password; undef %param; } else { @@ -130,8 +125,12 @@ while (<USERS>) { while (<USERS>) { chop; if ( /^\s*$/ ) { - $ip{$username}=$param{'radius_Framed_IP_Address'}||'0e0'; - delete $param{'radius_Framed_IP_Address'}; + if ( defined $param{'radius_Framed_IP_Address'} ) { + $ip{$username} = $param{'radius_Framed_IP_Address'}; + delete $param{'radius_Framed_IP_Address'}; + } else { + $ip{$username} = '0e0'; + } $allparam{$username}={ %param }; last; } elsif ( /^\s+([\w\-]+)\s=\s"?([\w\.\-\s]+)"?,?\s*$/ ) { @@ -144,14 +143,20 @@ while (<USERS>) { } } #? incase there isn't a terminating blank line ? -$ip{$username}=$param{'radius_Framed_IP_Address'}||'0e0'; -delete $param{'radius_Framed_IP_Address'}; +if ( defined $param{'radius_Framed_IP_Address'} ) { + $ip{$username} = $param{'radius_Framed_IP_Address'}; + delete $param{'radius_Framed_IP_Address'}; +} else { + $ip{$username} = '0e0'; +} $allparam{$username}={ %param }; my(%password); while (<SHADOW>) { chop; my($username,$password)=split(/:/); + #$password =~ s/^\!$/\*/; + #$password =~ s/\!+/\*SUSPENDED\* /; $password{$username}=$password; } @@ -176,16 +181,16 @@ while (<PASSWD>) { $svcpart = $shell_svcpart; } - my($svc_acct) = create FS::svc_acct ({ - 'svcpart' => $svcpart, - 'username' => $username, - 'password' => $password, - 'uid' => $uid, - 'gid' => $gid, - 'finger' => $finger, - 'dir' => $dir, - 'shell' => $shell, - 'slipip' => $ip{$username}, + my($svc_acct) = new FS::svc_acct ({ + 'svcpart' => $svcpart, + 'username' => $username, + '_password' => $password, + 'uid' => $uid, + 'gid' => $gid, + 'finger' => $finger, + 'dir' => $dir, + 'shell' => $shell, + 'slipip' => $ip{$username}, %{$allparam{$username}}, }); my($error); @@ -210,11 +215,11 @@ foreach $username ( keys %upassword ) { die "Illegal Port-Limit in users!\n"; } - my($svc_acct) = create FS::svc_acct ({ - 'svcpart' => $svcpart, - 'username' => $username, - 'password' => $password, - 'slipip' => $ip{$username}, + my($svc_acct) = new FS::svc_acct ({ + 'svcpart' => $svcpart, + 'username' => $username, + '_password' => $password, + 'slipip' => $ip{$username}, %{$allparam{$username}}, }); my($error); @@ -225,3 +230,9 @@ foreach $username ( keys %upassword ) { delete $upassword{$username}; } +# + +sub usage { + die "Usage:\n\n svc_acct.import user\n"; +} + diff --git a/bin/svc_acct_sm.export b/bin/svc_acct_sm.export deleted file mode 100755 index c2ec1e5..0000000 --- a/bin/svc_acct_sm.export +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/perl -Tw -# -# Create and export VoiceNet_quasar.m4 -# -# ivan@voicenet.com late oct 96 -# -# change priority (after copies) to 19, not 10 -# ivan@voicenet.com 97-feb-5 -# -# put file in different place and run different script, as per matt and -# mohamed -# ivan@voicenet.com 97-mar-10 -# -# added exit if stuff is already locked ivan@voicenet.com 97-apr-15 -# -# removed mail2 -# ivan@voicenet.com 97-jul-10 -# -# rewrote lots of the bits, now exports qmail "virtualdomain", -# "recipientmap" and "rcpthosts" files as well -# -# ivan@voicenet.com 97-sep-4 -# -# adds ".extra" files -# -# ivan@voicenet.com 97-sep-29 -# -# added ".pp" files, ugh. -# -# ivan@voicenet.com 97-oct-1 -# -# rewrite ivan@sisd.com 98-mar-9 -# -# now can create .qmail-default files ivan@sisd.com 98-mar-10 -# -# put example $my_domain declaration in ivan@sisd.com 98-mar-23 -# -# /var/spool/freeside/conf and sendmail updates ivan@sisd.com 98-aug-14 - -use strict; -use Fcntl qw(:flock); -use FS::SSH qw(ssh scp); -use FS::UID qw(adminsuidsetup); -use FS::Record qw(qsearch qsearchs); - -my($conf_shellm)="/var/spool/freeside/conf/shellmachine"; -my($fqmailmachines)="/var/spool/freeside/conf/qmailmachines"; -my($shellmachine); -my(@qmailmachines); -if ( -e $fqmailmachines ) { - open(SHELLMACHINE,$conf_shellm) or die "Can't open $conf_shellm: $!"; - <SHELLMACHINE> =~ /^([\w\.\-]+)$/ or die "Illegal $conf_shellm"; - $shellmachine = $1; - close SHELLMACHINE; - open(QMAILMACHINES,$fqmailmachines); - @qmailmachines=map { - /^(.*)$/ or die "Illegal line in conf/qmailmachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <QMAILMACHINES>; - close QMAILMACHINES; -} - -my($fsendmailmachines)="/var/spool/freeside/conf/sendmailmachines"; -my(@sendmailmachines); -if ( -e $fsendmailmachines ) { - open(SENDMAILMACHINES,$fsendmailmachines); - @sendmailmachines=map { - /^(.*)$/ or die "Illegal line in conf/sendmailmachines"; #we trust the file - $1; - } grep $_ !~ /^(#|$)/, <SENDMAILMACHINES>; - close SENDMAILMACHINES; -} - -my($conf_domain)="/var/spool/freeside/conf/domain"; -open(DOMAIN,$conf_domain) or die "Can't open $conf_domain: $!"; -my($mydomain)=map { - /^(.*)$/ or die "Illegal line in $conf_domain!"; #yes, we trust the file - $1 -} grep $_ !~ /^(#|$)/, <DOMAIN>; -close DOMAIN; - -my($spooldir)="/var/spool/freeside/export"; -my($spoollock)="/var/spool/freeside/svc_acct_sm.export.lock"; - -adminsuidsetup; -umask 066; - -open(EXPORT,"+>>$spoollock") or die "Can't open $spoollock: $!"; -select(EXPORT); $|=1; select(STDOUT); -unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) { - seek(EXPORT,0,0); - my($pid)=<EXPORT>; - chop($pid); - #no reason to start locks of blocking processes - die "Is another export process running under pid $pid?\n"; -} -seek(EXPORT,0,0); -print EXPORT $$,"\n"; - -my(@svc_acct_sm)=qsearch('svc_acct_sm',{}); - -( open(RCPTHOSTS,">$spooldir/rcpthosts") - and flock(RCPTHOSTS,LOCK_EX|LOCK_NB) -) or die "Can't open $spooldir/rcpthosts: $!"; -( open(RECIPIENTMAP,">$spooldir/recipientmap") - and flock(RECIPIENTMAP,LOCK_EX|LOCK_NB) -) or die "Can't open $spooldir/recipientmap: $!"; -( open(VIRTUALDOMAINS,">$spooldir/virtualdomains") - and flock(VIRTUALDOMAINS,LOCK_EX|LOCK_NB) -) or die "Can't open $spooldir/virtualdomains: $!"; -( open(VIRTUSERTABLE,">$spooldir/virtusertable") - and flock(VIRTUSERTABLE,LOCK_EX|LOCK_NB) -) or die "Can't open $spooldir/virtusertable: $!"; -( open(SENDMAIL_CW,">$spooldir/sendmail.cw") - and flock(SENDMAIL_CW,LOCK_EX|LOCK_NB) -) or die "Can't open $spooldir/sendmail.cw: $!"; - -setpriority(0,0,10); - -my($svc_domain,%domain); -foreach $svc_domain ( qsearch('svc_domain',{}) ) { - my($domain)=$svc_domain->domain; - $domain{$svc_domain->svcnum}=$domain; - print RCPTHOSTS "$domain\n.$domain\n"; - print SENDMAIL_CW "$domain\n"; -} - -my(@sendmail); - -my($svc_acct_sm); -foreach $svc_acct_sm ( qsearch('svc_acct_sm') ) { - my($domsvc,$domuid,$domuser)=( - $svc_acct_sm->domsvc, - $svc_acct_sm->domuid, - $svc_acct_sm->domuser, - ); - my($domain)=$domain{$domsvc}; - my($svc_acct)=qsearchs('svc_acct',{'uid'=>$domuid}); - my($username,$dir,$uid,$gid)=( - $svc_acct->username, - $svc_acct->dir, - $svc_acct->uid, - $svc_acct->gid, - ); - next unless $username && $domain && $domuser; - - if ($domuser eq '*') { - push @sendmail, "\@$domain\t$username\n"; - print VIRTUALDOMAINS "$domain:$username-$domain\n", - ".$domain:$username-$domain\n", - ; - ### - # qmail - ssh("root\@$shellmachine", - "[ -e $dir/.qmail-default ] || { touch $dir/.qmail-default; chown $uid:$gid $dir/.qmail-default; }" - ) if ( $shellmachine && $dir && $uid ); - - } else { - print VIRTUSERTABLE "$domuser\@$domain\t$username\n"; - print RECIPIENTMAP "$domuser\@$domain:$username\@$mydomain\n"; - } - - print VIRTUSERTABLE @sendmail; - -} - -chmod 0644, "$spooldir/sendmail.cw", - "$spooldir/virtusertable", - "$spooldir/rcpthosts", - "$spooldir/recipientmap", - "$spooldir/virtualdomains", -; - -flock(SENDMAIL_CW,LOCK_UN); -flock(VIRTUSERTABLE,LOCK_UN); -flock(RCPTHOSTS,LOCK_UN); -flock(RECIPIENTMAP,LOCK_UN); -flock(VIRTUALDOMAINS,LOCK_UN); - -close SENDMAIL_CW; -close VIRTUSERTABLE; -close RCPTHOSTS; -close RECIPIENTMAP; -close VIRTUALDOMAINS; - -### -# export stuff -# - -my($sendmailmachine); -foreach $sendmailmachine (@sendmailmachines) { - scp("$spooldir/sendmail.cw","root\@$sendmailmachine:/etc/sendmail.cw.new") - == 0 or die "scp error: $!"; - scp("$spooldir/virtusertable","root\@$sendmailmachine:/etc/virtusertable.new") - == 0 or die "scp error: $!"; - ssh("root\@$sendmailmachine", - "( ". - "mv /etc/sendmail.cw.new /etc/sendmail.cw; ". - "mv /etc/virtusertable.new /etc/virtusertable; ". - #"/etc/init.d/sendmail restart; ". - " )" - ) - == 0 or die "ssh error: $!"; -} - -my($qmailmachine); -foreach $qmailmachine (@qmailmachines) { - scp("$spooldir/recipientmap","root\@$qmailmachine:/var/qmail/control/recipientmap") - == 0 or die "scp error: $!"; - scp("$spooldir/virtualdomains","root\@$qmailmachine:/var/qmail/control/virtualdomains") - == 0 or die "scp error: $!"; - scp("$spooldir/rcpthosts","root\@$qmailmachine:/var/qmail/control/rcpthosts") - == 0 or die "scp error: $!"; - #ssh("root\@$qmailmachine","/etc/init.d/qmail restart") - # == 0 or die "ssh error: $!"; -} - -unlink $spoollock; -flock(EXPORT,LOCK_UN); -close EXPORT; - diff --git a/bin/svc_acct_sm.import b/bin/svc_acct_sm.import index 10d7e4c..b668405 100755 --- a/bin/svc_acct_sm.import +++ b/bin/svc_acct_sm.import @@ -1,30 +1,22 @@ #!/usr/bin/perl -Tw # -# ivan@sisd.com 98-mar-9 -# -# generalized svcparts ivan@sisd.com 98-mar-23 - -# You really need to enable ssh into a shell machine as this needs to rename -# .qmail-extension files. -# -# now an interactive script ivan@sisd.com 98-jun-30 -# -# has an (untested) section for sendmail, s/warn/die/g and generates a program -# to run on your mail machine _later_ instead of ssh'ing for each user -# ivan@sisd.com 98-jul-13 +# $Id: svc_acct_sm.import,v 1.10 2001-08-21 02:43:18 ivan Exp $ use strict; use vars qw(%d_part_svc %m_part_svc); -use FS::SSH qw(iscp); -use FS::UID qw(adminsuidsetup); +use Term::Query qw(query); +use Net::SCP qw(iscp); +use FS::UID qw(adminsuidsetup datasrc); use FS::Record qw(qsearch qsearchs); use FS::svc_acct_sm; use FS::svc_domain; +use FS::svc_acct; +use FS::part_svc; -adminsuidsetup; +my $user = shift or die &usage; +adminsuidsetup $user; -#my($spooldir)="/var/spool/freeside/export"; -my($spooldir)="unix"; +my($spooldir)="/usr/local/etc/freeside/export.". datasrc; my(%mta) = ( 1 => "qmail", @@ -38,22 +30,33 @@ my(%mta) = ( %m_part_svc = map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct_sm'}); +die "No services with svcdb svc_domain!\n" unless %d_part_svc; +die "No services with svcdb svc_svc_acct_sm!\n" unless %m_part_svc; + print "\n\n", ( join "\n", map "$_: ".$d_part_svc{$_}->svc, sort keys %d_part_svc ), - "\n\nEnter part number for domains: "; -my($domain_svcpart)=&getvalue; + "\n\n"; +$^W=0; #Term::Query isn't -w-safe +my $domain_svcpart = + query "Enter part number for domains: ", 'irk', [ keys %d_part_svc ]; +$^W=1; print "\n\n", ( join "\n", map "$_: ".$m_part_svc{$_}->svc, sort keys %m_part_svc ), - "\n\nEnter part number for mail aliases: "; -my($mailalias_svcpart)=&getvalue; + "\n\n"; +$^W=0; #Term::Query isn't -w-safe +my $mailalias_svcpart = + query "Enter part number for mail aliases: ", 'irk', [ keys %m_part_svc ]; +$^W=1; print "\n\n", <<END; Select your MTA from the following list. END print join "\n", map "$_: $mta{$_}", sort keys %mta; -print "\n\n:"; -my($mta)=&getvalue; +print "\n\n"; +$^W=0; #Term::Query isn't -w-safe +my $mta = query ":", 'irk', [ keys %mta ]; +$^W=1; if ( $mta{$mta} eq "qmail" ) { @@ -61,8 +64,7 @@ if ( $mta{$mta} eq "qmail" ) { Enter the location and name of your qmail control directory, for example "mail.isp.com:/var/qmail/control" END - print ":"; - my($control)=&getvalue; + my($control)=&getvalue(":"); iscp("root\@$control/rcpthosts","$spooldir/rcpthosts.import"); # iscp("root\@$control/recipientmap","$spooldir/recipientmap.import"); iscp("root\@$control/virtualdomains","$spooldir/virtualdomains.import"); @@ -80,16 +82,14 @@ END Enter the location and name of your sendmail virtual user table, for example "mail.isp.com:/etc/virtusertable" END - print ":"; - my($virtusertable)=&getvalue; + my($virtusertable)=&getvalue(":"); iscp("root\@$virtusertable","$spooldir/virtusertable.import"); print "\n\n", <<END; Enter the location and name of your sendmail.cw file, for example "mail.isp.com:/etc/sendmail.cw" END - print ":"; - my($sendmail_cw)=&getvalue; + my($sendmail_cw)=&getvalue(":"); iscp("root\@$sendmail_cw","$spooldir/sendmail.cw.import"); } else { @@ -97,9 +97,11 @@ END } sub getvalue { - my($x)=scalar(<STDIN>); - chop $x; - $x; + my $prompt = shift; + $^W=0; #Term::Query isn't -w-safe + my $data = query $prompt, ''; + $^W=1; + $data; } print "\n\n"; @@ -123,13 +125,14 @@ my(%svcnum); while (<RCPTHOSTS>) { next if /^(#|$)/; + next if $mta{$mta} eq 'sendmail' && /^\s*$/; #blank lines /^\.?([\w\-\.]+)$/ #or do { warn "Strange rcpthosts/sendmail.cw line: $_"; next; }; or die "Strange rcpthosts/sendmail.cw line: $_"; my $domain = $1; my($svc_domain); unless ( $svc_domain = qsearchs('svc_domain', {'domain'=>$domain} ) ) { - $svc_domain = create FS::svc_domain ({ + $svc_domain = new FS::svc_domain ({ 'domain' => $domain, 'svcpart' => $domain_svcpart, 'action' => 'N', @@ -184,7 +187,7 @@ END } unless ( exists $svcnum{$domain} ) { - my($svc_domain) = create FS::svc_domain ({ + my($svc_domain) = new FS::svc_domain ({ 'domain' => $domain, 'svcpart' => $domain_svcpart, 'action' => 'N', @@ -195,7 +198,7 @@ END $svcnum{$domain}=$svc_domain->svcnum; } - my($svc_acct_sm)=create FS::svc_acct_sm ({ + my($svc_acct_sm)=new FS::svc_acct_sm ({ 'domsvc' => $svcnum{$domain}, 'domuid' => $svc_acct->uid, 'domuser' => '*', @@ -215,7 +218,8 @@ END or die "Can't open $spooldir/virtusertable.import: $!"; while (<VIRTUSERTABLE>) { next if /^#/; #comments? - /^([\w\-\.]+)?\@([\w\-\.]+)\t([\w\-\.]+)$/ + next if /^\s*$/; #blank lines + /^([\w\-\.]+)?\@([\w\-\.]+)\t+([\w\-\.]+)$/ #or do { warn "Strange virtusertable line: $_"; next; }; or die "Strange virtusertable line: $_"; my($domuser,$domain,$username)=($1,$2,$3); @@ -225,7 +229,7 @@ END die "Unknown user $username in virtusertable"; next; } - my($svc_acct_sm)=create FS::svc_acct_sm ({ + my($svc_acct_sm)=new FS::svc_acct_sm ({ 'domsvc' => $svcnum{$domain}, 'domuid' => $svc_acct->uid, 'domuser' => $domuser || '*', @@ -250,3 +254,9 @@ Don\'t forget to run $spooldir/virtualdomains.FIX before using $spooldir/virtualdomains ! END +# + +sub usage { + die "Usage:\n\n svc_acct_sm.import user\n"; +} + diff --git a/bin/svc_domain.import b/bin/svc_domain.import new file mode 100644 index 0000000..10fe5b5 --- /dev/null +++ b/bin/svc_domain.import @@ -0,0 +1,92 @@ +#!/usr/bin/perl -w +# +# $Id: svc_domain.import,v 1.3 2001-11-05 12:07:10 ivan Exp $ + +use strict; +use vars qw( %d_part_svc ); +use Term::Query qw(query); +use Net::SCP qw(iscp); +use FS::UID qw(adminsuidsetup datasrc); +#use FS::Record qw(qsearch qsearchs); +#use FS::svc_acct_sm; +use FS::svc_domain; +use FS::domain_record; +#use FS::svc_acct; +#use FS::part_svc; + +my $user = shift or die &usage; +adminsuidsetup $user; + +my($spooldir)="/usr/local/etc/freeside/export.". datasrc; + +%d_part_svc = + map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_domain'}); + +print "\n\n", + ( join "\n", map "$_: ".$d_part_svc{$_}->svc, sort keys %d_part_svc ), + "\n\n"; +$^W=0; #Term::Query isn't -w-safe +my $domain_svcpart = + query "Enter part number for domains: ", 'irk', [ keys %d_part_svc ]; +$^W=1; + + print "\n\n", <<END; +Enter the location and name of your primary named.conf file, for example +"ns.isp.com:/var/named/named.conf" +END + my($named_conf)=&getvalue(":"); + iscp("root\@$named_conf","$spooldir/named.conf.import"); + +my $named_machine = (split(/:/, $named_conf))[0]; + +print "\n\n"; + +## + +$FS::svc_domain::whois_hack=1; + +open(NAMED_CONF,"<$spooldir/named.conf.import") + or die "Can't open $spooldir/named.conf.import: $!"; + +while (<NAMED_CONF>) { + next unless /^\s*options/; +} +my $directory; +while (<NAMED_CONF>) { + last if /^\s*directory\s+\"([\/\w+]+)\";/; +} +$directory = $1 or die "can't locate directory in named.conf!"; +while (<NAMED_CONF>) { + next unless /^\s*zone\s+\"([\w\.\-]+)\"\s+\{/; + my $zone = $1; + while (<NAMED_CONF>) { + my $type; + if ( /^\s*type\s+(master|slave)\s*\;/ ) { + $type = $1; + } + if ( /^\s*file\s+\"([\w\.\-]+)\"\s*\;/ && $type eq 'master' ) { + + # + # (add svc_domain) + my $file = $1; + iscp("root\@$named_machine:$directory/$file","$spooldir/$file.import"); + open(ZONE,"<$spooldir/$file.import") + or die "Can't open $spooldir/$file.import: $!"; + while (<ZONE>) { + # (add domain_record) + } + + # + + } + + last if /^\s*\}\s*\;/; + } +} + +## + +sub usage { + die "Usage:\n\n svc_domain.import user\n"; +} + |