From: jeff Date: Sun, 12 Aug 2001 19:41:26 +0000 (+0000) Subject: merging vpopmail support branch X-Git-Tag: merged-freeside_vpopmail_support~1 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=4d513ff5a17bd3b697502b54c9516577a0a8d3e4 merging vpopmail support branch --- diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index 4b7ec98c4..83263b0e4 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -82,6 +82,10 @@ FS::svc_acct - Object methods for svc_acct records %hash = $record->radius; + %hash = $record->radius_reply; + + %hash = $record->radius_check; + =head1 DESCRIPTION An FS::svc_acct object represents an account. FS::svc_acct inherits from @@ -113,6 +117,8 @@ FS::svc_Common. The following fields are currently supported: =item radius_I - I +=item domsvc - service number of svc_domain with which to associate + =back =head1 METHODS @@ -169,7 +175,9 @@ sub insert { return $error if $error; return "Username ". $self->username. " in use" - if qsearchs( 'svc_acct', { 'username' => $self->username } ); + if qsearchs( 'svc_acct', { 'username' => $self->username, + 'domsvc' => $self->domsvc, + } ); my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } ); return "Unknown svcpart" unless $part_svc; @@ -544,7 +552,7 @@ sub radius_check { =head1 VERSION -$Id: svc_acct.pm,v 1.19 2001-07-30 07:34:41 ivan Exp $ +$Id: svc_acct.pm,v 1.20 2001-08-12 19:41:24 jeff Exp $ =head1 BUGS diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm index 01aa21a6c..fcfcfa955 100644 --- a/FS/FS/svc_domain.pm +++ b/FS/FS/svc_domain.pm @@ -336,6 +336,9 @@ sub check { $recref->{action} =~ /^(M|N)$/ or return "Illegal action"; $recref->{action} = $1; + my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $recref->{catchall} } ); + return "Unknown catchall" unless $svc_acct || ! $recref->{catchall}; + $self->ut_textn('purpose'); } @@ -478,7 +481,7 @@ sub submit_internic { =head1 VERSION -$Id: svc_domain.pm,v 1.12 2001-06-03 17:22:52 ivan Exp $ +$Id: svc_domain.pm,v 1.13 2001-08-12 19:41:24 jeff Exp $ =head1 BUGS diff --git a/FS/FS/svc_forward.pm b/FS/FS/svc_forward.pm new file mode 100644 index 000000000..5264a60b1 --- /dev/null +++ b/FS/FS/svc_forward.pm @@ -0,0 +1,255 @@ +package FS::svc_forward; + +use strict; +use vars qw( @ISA $nossh_hack $conf $shellmachine @qmailmachines @vpopmailmachines); +use FS::Record qw( fields qsearch qsearchs ); +use FS::svc_Common; +use FS::cust_svc; +use Net::SSH qw(ssh); +use FS::Conf; +use FS::svc_acct; +use FS::svc_domain; + +@ISA = qw( FS::svc_Common ); + +#ask FS::UID to run this stuff for us later +$FS::UID::callback{'FS::svc_forward'} = sub { + $conf = new FS::Conf; + $shellmachine = $conf->exists('qmailmachines') + ? $conf->config('shellmachine') + : ''; + if ( $conf->exists('vpopmailmachines') ) { + @vpopmailmachines = $conf->config('vpopmailmachines'); + } +}; + +=head1 NAME + +FS::svc_forward - Object methods for svc_forward records + +=head1 SYNOPSIS + + use FS::svc_forward; + + $record = new FS::svc_forward \%hash; + $record = new FS::svc_forward { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + + $error = $record->suspend; + + $error = $record->unsuspend; + + $error = $record->cancel; + +=head1 DESCRIPTION + +An FS::svc_forward object represents a mail forwarding alias. FS::svc_forward +inherits from FS::Record. The following fields are currently supported: + +=over 4 + +=item svcnum - primary key (assigned automatcially for new accounts) + +=item srcsvc - svcnum of the source of the forward (see L) + +=item dstsvc - svcnum of the destination of the forward (see L) + +=item dst - foreign destination (email address) - forward not local to freeside + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new mail forwarding alias. To add the mail forwarding alias to the +database, see L<"insert">. + +=cut + +sub table { 'svc_forward'; } + +=item insert + +Adds this mail forwarding alias to the database. If there is an error, returns +the error, otherwise returns false. + +The additional fields pkgnum and svcpart (see L) should be +defined. An FS::cust_svc record will be created and inserted. + +If the configuration values (see L) vpopmailmachines exist, then +the command: + + [ -d /home/vpopmail/$vdomain/$source ] || { + echo "$destination" >> /home/vpopmail/$vdomain/$source/.$qmail + chown $vpopuid:$vpopgid /home/vpopmail/$vdomain/$source/.$qmail + } + +is executed on each vpopmailmachine via ssh (see L). +This behaviour can be surpressed by setting $FS::svc_forward::nossh_hack true. + +=cut + +sub insert { + my $self = shift; + my $error; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + $error=$self->check; + return $error if $error; + + $error = $self->SUPER::insert; + return $error if $error; + + my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } ); + my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $svc_acct->domsvc } ); + my $source = $svc_acct->username . $svc_domain->domain; + my $destination; + if ($self->dstdvc) { + my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->dstsvc } ); + my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $svc_acct->domsvc } ); + $destination = $svc_acct->username . $svc_domain->domain; + } else { + $destination = $self->dst; + } + + my $vdomain = $svc_acct->domain; + + foreach my $vpopmailmachine ( @vpopmailmachines ) { + my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachine); + + ssh("root\@$machine","[ -d $vpopdir/$vdomain/$source ] || { echo $destination >> $vpopdir/$vdomain/$source/.qmail; chown $vpopuid:$vpopgid $vpopdir/$vdomain/$source/.qmail; }") + if ( ! $nossh_hack && $machine); + } + + ''; #no error + +} + +=item delete + +Deletes this mail forwarding alias from the database. If there is an error, +returns the error, otherwise returns false. + +The corresponding FS::cust_svc record will be deleted as well. + +=item replace OLD_RECORD + +Replaces OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +sub replace { + my ( $new, $old ) = ( shift, shift ); + my $error; + + $new->SUPER::replace($old); + +} + +=item suspend + +Just returns false (no error) for now. + +Called by the suspend method of FS::cust_pkg (see L). + +=item unsuspend + +Just returns false (no error) for now. + +Called by the unsuspend method of FS::cust_pkg (see L). + +=item cancel + +Just returns false (no error) for now. + +Called by the cancel method of FS::cust_pkg (see L). + +=item check + +Checks all fields to make sure this is a valid mail forwarding alias. If there +is an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +Sets any fixed values; see L. + +=cut + +sub check { + my $self = shift; + my $error; + + my $x = $self->setfixed; + return $x unless ref($x); + my $part_svc = $x; + + my($recref) = $self->hashref; + + $recref->{srcsvc} =~ /^(\d+)$/ or return "Illegal srcsvc"; + $recref->{srcsvc} = $1; + my($svc_acct); + return "Unknown srcsvc" unless + $svc_acct=qsearchs('svc_acct',{'svcnum'=> $recref->{srcsvc} } ); + + return "Illegal use of dstsvc and dst" if + ($recref->{dstsvc} && $recref->{dst}); + + return "Illegal use of dstsvc and dst" if + (! $recref->{dstsvc} && ! $recref->{dst}); + + $recref->{dstsvc} =~ /^(\d+)$/ or return "Illegal dstsvc"; + $recref->{dstsvc} = $1; + + if ($recref->{dstsvc}) { + my($svc_acct); + return "Unknown dstsvc" unless + my $svc_domain=qsearchs('svc_acct',{'svcnum'=> $recref->{dstsvc} } ); + } + + if ($recref->{dst}) { + $recref->{dst} =~ /^(\w\.\-]+)\@(([\w\.\-]+\.)+\w+)$/ + or return "Illegal dst"; + $recref->{dst} = $1; + } + + ''; #no error +} + +=back + +=head1 VERSION + +$Id: svc_forward.pm,v 1.2 2001-08-12 19:41:24 jeff Exp $ + +=head1 BUGS + +The remote commands should be configurable. + +The $recref stuff in sub check should be cleaned up. + +=head1 SEE ALSO + +L, L, L, L, L, +L, L, L, L, L, +schema.html from the base documentation. + +=cut + +1; + diff --git a/bin/fs-migrate-svc_acct_sm b/bin/fs-migrate-svc_acct_sm new file mode 100755 index 000000000..d0d4a94b6 --- /dev/null +++ b/bin/fs-migrate-svc_acct_sm @@ -0,0 +1,242 @@ +#!/usr/bin/perl -Tw +# +# $Id: fs-migrate-svc_acct_sm,v 1.2 2001-08-12 19:41:25 jeff Exp $ +# +# jeff@cmh.net 01-Jul-20 +# +# $Log: fs-migrate-svc_acct_sm,v $ +# Revision 1.2 2001-08-12 19:41:25 jeff +# merging vpopmail support branch +# +# Revision 1.1.2.1 2001/08/08 17:45:35 jeff +# initial vpopmail support +# +# +# +# Initial vpopmail changes +# + +#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 <); + chop $response; + if ($response =~ /^[yY]/) { + print "\n\n", &menu_domain_svc, "\n", < $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 <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-setup b/bin/fs-setup index 314a7c234..a3e067bec 100755 --- a/bin/fs-setup +++ b/bin/fs-setup @@ -1,6 +1,6 @@ #!/usr/bin/perl -Tw # -# $Id: fs-setup,v 1.40 2001-08-11 05:53:42 ivan Exp $ +# $Id: fs-setup,v 1.41 2001-08-12 19:41:25 jeff Exp $ # # ivan@sisd.com 97-nov-8,9 # @@ -32,7 +32,10 @@ # fix radius attributes ivan@sisd.com 98-sep-27 # # $Log: fs-setup,v $ -# Revision 1.40 2001-08-11 05:53:42 ivan +# Revision 1.41 2001-08-12 19:41:25 jeff +# merging vpopmail support branch +# +# Revision 1.40 2001/08/11 05:53:42 ivan # add comments field # # Revision 1.39 2001/07/30 07:42:39 ivan @@ -271,7 +274,7 @@ 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 svc_www)) { +foreach (qw(svc_acct svc_acct_sm svc_domain svc_forward svc_www)) { my($table)=$dbdef->table($_); my($col); foreach $col ( $table->columns ) { @@ -707,10 +710,11 @@ sub tables_hash_hack { 'quota', 'varchar', 'NULL', $char_d, 'slipip', 'varchar', 'NULL', 15, #four TINYINTs, bah. 'seconds', 'int', 'NULL', '', #uhhhh + 'domsvc', 'int', '', '', ], 'primary_key' => 'svcnum', 'unique' => [ [] ], - 'index' => [ ['username'] ], + 'index' => [ ['username'], ['domsvc'] ], }, 'svc_acct_sm' => { @@ -739,6 +743,7 @@ sub tables_hash_hack { 'columns' => [ 'svcnum', 'int', '', '', 'domain', 'varchar', '', $char_d, + 'catchall', 'int', '', '', ], 'primary_key' => 'svcnum', 'unique' => [ ['domain'] ], @@ -759,6 +764,18 @@ sub tables_hash_hack { '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', '', '', diff --git a/bin/svc_acct.export b/bin/svc_acct.export index 7e92c61e8..a7a21b3e5 100755 --- a/bin/svc_acct.export +++ b/bin/svc_acct.export @@ -1,99 +1,42 @@ #!/usr/bin/perl -w # -# $Id: svc_acct.export,v 1.21 2001-07-30 06:07:46 ivan Exp $ +# $Id: svc_acct.export,v 1.22 2001-08-12 19:41:25 jeff Exp $ # -# Create and export password files: passwd, passwd.adjunct, shadow, -# acp_passwd, acp_userinfo, acp_dialup, users +# 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. # -# 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 -# # $Log: svc_acct.export,v $ -# Revision 1.21 2001-07-30 06:07:46 ivan -# allow !! for locked accounts instead of changing to *SUSPENDED* -# -# Revision 1.20 2001/06/20 08:33:42 ivan -# > Use of uninitialized value in concatenation (.) at svc_acct.export line -# > 276. -# -# Revision 1.19 2001/05/08 10:44:17 ivan -# fix for OO Net::SCP -# -# Revision 1.18 2001/04/22 01:56:15 ivan -# get rid of FS::SSH.pm (became Net::SSH and Net::SCP on CPAN) -# -# Revision 1.17 2001/02/21 23:48:19 ivan -# add icradius_secrets config file to export to a non-Freeside MySQL database for -# ICRADIUS -# -# Revision 1.16 2000/07/06 13:23:29 ivan -# tyop +# Revision 1.22 2001-08-12 19:41:25 jeff +# merging vpopmail support branch # -# Revision 1.15 2000/07/06 08:57:28 ivan -# support for radius check attributes (except importing). poorly documented. -# -# Revision 1.14 2000/06/29 15:01:25 ivan -# another silly typo in svc_acct.export -# -# Revision 1.13 2000/06/28 12:37:28 ivan -# add support for config option textradiusprepend -# -# Revision 1.12 2000/06/15 14:07:02 ivan -# added ICRADIUS radreply table support, courtesy of Kenny Elliott -# -# Revision 1.11 2000/03/06 16:00:39 ivan -# sync up with working versoin -# -# Revision 1.2 1998/12/10 07:23:15 ivan -# use FS::Conf, need user (for datasrc) # use strict; use vars qw($conf); +use Archive::Tar; use Fcntl qw(:flock); +use File::Path; use IO::Handle; -use DBI; 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 fields); +use FS::Record qw(qsearch qsearchs fields); use FS::svc_acct; +use FS::svc_domain; +use FS::svc_forward; 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'); @@ -130,6 +73,29 @@ my $textradiusprepend = ? $conf->config('textradiusprepend') : ''; +my @vpopmailmachines = $conf->config('vpopmailmachines') + if $conf->exists('vpopmailmachines'); + +my ($machine, $vpopdir, $vpopuid, $vpopgid) = split (/\s+/, $vpopmailmachines[0]); + +my($shellmachine, @qmailmachines); +if ( $conf->exists('qmailmachines') ) { + $shellmachine = $conf->config('shellmachine'); + @qmailmachines = $conf->config('qmailmachines'); +} + +my(@sendmailmachines, $sendmailconfigpath, $sendmailrestart); +if ( $conf->exists('sendmailmachines') ) { + @sendmailmachines = $conf->config('sendmailmachines'); + $sendmailconfigpath = $conf->config('sendmailconfigpath') || '/etc'; + $sendmailrestart = $conf->config('sendmailrestart'); +} + +my $mydomain = $conf->config('domain') if $conf->exists('domain'); + + + + my(@saltset)= ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' ); require 5.004; #srand(time|$$); @@ -142,42 +108,81 @@ unless ( flock(EXPORT,LOCK_EX|LOCK_NB) ) { seek(EXPORT,0,0); my($pid)=; 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", ; +rmtree"$spooldir/domains", 0, 1; +mkdir "$spooldir/domains", 0700; + if ( $icradiusmachines ) { my $sth = $icradius_dbh->prepare("DELETE FROM radcheck"); $sth->execute or die "Can't reset radcheck table: ". $sth->errstr; @@ -187,168 +192,321 @@ if ( $icradiusmachines ) { setpriority(0,0,10); -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 '!!' ) - && ( $password ne '' ) - ) { - $cpassword=crypt($password, - $saltset[int(rand(64))].$saltset[int(rand(64))] - ); - $rpassword=$password; - } else { - $cpassword=$password; - $rpassword='UNIX'; - } - - 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'; +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"; + + (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->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 \"\" $username\@$domain\n"; + }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 SHADOW FILE HERE - print SHADOW join(":", - $svc_acct->username, - $cpassword, - '', - '', - '', - '', - '', - '', - '', - ), "\n"; - + # FORMAT OF THE .QMAIL-DEFAULT FILE HERE + print QMAILDEFAULT "| $vpopdir/bin/vdelivermail \"\" bounce-no-mailbox\n"; } - if ( $svc_acct->slipip ne '' ) { + 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 ) + && ( $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; - ### - # FORMAT OF THE ACP_* FILES HERE - print ACP_PASSWD join(":", - $svc_acct->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 $svc_acct->username, "\t*\t", $svc_acct->slipip, "\n"; + 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; + } else { + die "Unknown policy in username_policy\n"; } - my %radreply = $svc_acct->radius_reply; - my %radcheck = $svc_acct->radius_check; + 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"; + } - my $radcheck = join ", ", map { qq($_ = "$radcheck{$_}") } keys %radcheck; - $radcheck .= ", " if $radcheck; + ### + # FORMAT OF THE VPASSWD FILE HERE + print VPASSWD join(":", + $svc_acct->username, + $cpassword, + '1', + '0', + $svc_acct->username, + "$vpopdir/domains/" . $svc_domain->domain ."/" . $svc_acct->username, + 'NOQUOTA', + ), "\n"; - ### - # FORMAT OF THE USERS FILE HERE - print USERS - $svc_acct->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 ) { + if ( $svc_acct->slipip ne '' ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". - join(", ", map { $icradius_dbh->quote( $_ ) } ( - '', - $svc_acct->username, - "Password", - $svc_acct->_password, - ) ). " )" - ); - $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr; + ### + # FORMAT OF THE ACP_* FILES HERE + print ACP_PASSWD join(":", + $username, + $cpassword, + "0", + "0", + "", + "", + "", + ), "\n"; - foreach my $attribute ( keys %radcheck ) { + 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( $_ ) } ( '', - $svc_acct->username, - $attribute, - $radcheck{$attribute}, + $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; } } - - foreach my $attribute ( keys %radreply ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radreply (id, UserName, Attribute, Value) VALUES ( ". - join(", ", map { $icradius_dbh->quote( $_ ) } ( - '', - $svc_acct->username, - $attribute, - $radreply{$attribute}, - ) ). " )" - ); - $sth->execute or die "Can't insert into radreply table: ". $sth->errstr; + } + + ### + # 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; @@ -356,19 +514,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) { +my($ashellmachine); +foreach $ashellmachine (@shellmachines) { my $scp = new Net::SCP; - $scp->scp("$spooldir/passwd","root\@$shellmachine:/etc/passwd.new") + $scp->scp("$spooldir/passwd","root\@$ashellmachine:/etc/passwd.new") or die "scp error: ". $scp->{errstr}; - $scp->scp("$spooldir/shadow","root\@$shellmachine:/etc/shadow.new") + $scp->scp("$spooldir/shadow","root\@$ashellmachine:/etc/shadow.new") or die "scp error: ". $scp->{errstr}; - ssh("root\@$shellmachine", + ssh("root\@$ashellmachine", "( ". "mv /etc/passwd.new /etc/passwd; ". "mv /etc/shadow.new /etc/shadow; ". @@ -457,6 +622,65 @@ foreach my $icradiusmachine ( @icradiusmachines ) { 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", + "( ". + "tar xf vpoptarball; ". + "chown -R $vpopuid:$vpopgid domains; ". + "tar cf vpoptarball domains; ". + "cd $vpopdir; ". + "tar xf ~/vpoptarball; ". + " )" + ) + == 0 or die "ssh error: $!"; + + $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}; +} + +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; diff --git a/bin/svc_acct_sm.export b/bin/svc_acct_sm.export deleted file mode 100755 index d7a7840f1..000000000 --- a/bin/svc_acct_sm.export +++ /dev/null @@ -1,254 +0,0 @@ -#!/usr/bin/perl -Tw -# -# $Id: svc_acct_sm.export,v 1.10 2001-05-08 10:44:17 ivan Exp $ -# -# Create and export config files for sendmail, qmail -# -# (used to) 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 -# -# $Log: svc_acct_sm.export,v $ -# Revision 1.10 2001-05-08 10:44:17 ivan -# fix for OO Net::SCP -# -# Revision 1.9 2001/04/22 01:56:15 ivan -# get rid of FS::SSH.pm (became Net::SSH and Net::SCP on CPAN) -# -# Revision 1.8 2000/07/06 03:37:24 ivan -# don't error out on invalid svc_acct_sm.domuid's that can't be matched in -# svc_acct.uid - just warn. -# -# Revision 1.7 2000/07/03 09:13:10 ivan -# get rid of double sendmailrestart invocation; no need for multiple sessions -# -# Revision 1.6 2000/07/03 09:09:14 ivan -# typo -# -# Revision 1.5 2000/07/03 09:03:14 ivan -# added sendmailrestart and sendmailconfigpath config files -# -# Revision 1.4 2000/06/29 14:02:29 ivan -# add sendmailrestart configuration file -# -# Revision 1.3 2000/06/12 08:37:56 ivan -# sendmail fix from Jeff Finucane -# -# Revision 1.2 1998/12/10 07:23:17 ivan -# use FS::Conf, need user (for datasrc) -# - -use strict; -use vars qw($conf); -use Fcntl qw(:flock); -use Net::SSH qw(ssh); -use Net::SCP qw(scp); -use FS::UID qw(adminsuidsetup datasrc); -use FS::Record qw(qsearch qsearchs); -use FS::svc_acct; -use FS::svc_acct_sm; -use FS::svc_domain; - -my $user = shift or die &usage; -adminsuidsetup $user; - -$conf = new FS::Conf; - -my($shellmachine, @qmailmachines); -if ( $conf->exists('qmailmachines') ) { - $shellmachine = $conf->config('shellmachine'); - @qmailmachines = $conf->config('qmailmachines'); -} - -my(@sendmailmachines, $sendmailconfigpath, $sendmailrestart); -if ( $conf->exists('sendmailmachines') ) { - @sendmailmachines = $conf->config('sendmailmachines'); - $sendmailconfigpath = $conf->config('sendmailconfigpath') || '/etc'; - $sendmailrestart = $conf->config('sendmailrestart'); -} - -my $mydomain = $conf->config('domain'); - -my $spooldir = "/usr/local/etc/freeside/export.". datasrc; -my $spoollock = "/usr/local/etc/freeside/svc_acct_sm.export.lock.". datasrc; - -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)=; - 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"; - -( 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}); - unless ( $svc_acct ) { - warn "WARNING: couldn't find svc_acct.uid $domuid (svc_acct_sm.svcnum ". - $svc_acct_sm->svcnum. ") - corruped database?\n"; - next; - } - 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) { - 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/htdocs/docs/config.html b/htdocs/docs/config.html index 9282e826d..532a826be 100644 --- a/htdocs/docs/config.html +++ b/htdocs/docs/config.html @@ -100,6 +100,8 @@ All further configuration files and directories are located in
  • usernamemax - Maximum username length (default is the size of the SQL column, probably specified when fs-setup was run)
  • username-letter - The existance of this file will turn on the requirement that usernames contain at least one letter.
  • username-letterfirst - The existance of this file will turn on the requirement that usernames start with a letter. +
  • username_policy - This file controls the mechanism for preventing duplicate usernames in passwd/radius files exported from svc_accts. This should be one of 'prepend domsvc' 'append domsvc' or 'append domain' +
  • vpopmailmachines - Your vpopmail pop toasters, one per line. Each line is of the form "machinename vpopdir vpopuid vpopgid". Eg: poptoaster.domain.tld /home/vpopmail 508 508 Note: vpopuid and vpopgid are values taken from the vpopmail machine's /etc/passwd diff --git a/htdocs/docs/upgrade8.html b/htdocs/docs/upgrade8.html new file mode 100644 index 000000000..379892256 --- /dev/null +++ b/htdocs/docs/upgrade8.html @@ -0,0 +1,52 @@ + + Upgrading to 1.3.2 + + +

    Upgrading to 1.3.2 from 1.3.1

    +
      +
    • If migrating from 1.0.0, see these instructions first. +
    • If migrating from less than 1.1.4, see these instructions first. +
    • If migrating from less than 1.2.0, see these instructions first. +
    • If migrating from less than 1.2.2, see these instructions first. +
    • If migrating from less than 1.2.3, see these instructions first. +
    • If migrating from less than 1.3.0, see these instructions first. +
    • If migrating from less than 1.3.1, see these instructions first. +
    • Back up your data and current Freeside installation. +
    • Apply the following changes to your database: +
      +CREATE TABLE svc_forward (
      +  svcnum int NOT NULL,
      +  srcsvc int NOT NULL,
      +  dstsvc int NOT NULL,
      +  dst varchar(80),
      +  PRIMARY KEY (svcnum)
      +);
      +ALTER TABLE svc_acct ADD domsvc integer NOT NULL;
      +ALTER TABLE svc_domain ADD catchall integer;
      +ALTER TABLE part_svc ADD svc_acct__domsvc integer NULL;
      +ALTER TABLE part_svc ADD svc_acct__domsvc_flag char(1) NULL;
      +ALTER TABLE part_svc ADD svc_domain__catchall integer NULL;
      +ALTER TABLE part_svc ADD svc_domain__catchall_flag char(1) NULL;
      +ALTER TABLE part_svc ADD svc_forward__srcsvc integer NULL;
      +ALTER TABLE part_svc ADD svc_forward__srcsvc_flag char(1) NULL;
      +ALTER TABLE part_svc ADD svc_forward__dstsvc integer NULL;
      +ALTER TABLE part_svc ADD svc_forward__dstsvc_flag char(1) NULL;
      +ALTER TABLE part_svc ADD svc_forward__dst integer NULL;
      +ALTER TABLE part_svc ADD svc_forward__dst_flag char(1) NULL;
      +
      +
      +
    • Copy or symlink htdocs to the new copy. +
    • Remove the symlink or directory (your_site_perl_directory)/FS. +
    • Change to the FS directory in the new tarball, and build and install the + Perl modules: +
      +$ cd FS/
      +$ perl Makefile.PL
      +$ make
      +$ su
      +# make install
      +
    • Run bin/dbdef-create. +
    • create a service based on svc_forward +
    • Run bin/fs-migrate-svc_acct_sm +
    • create the conf.dbsrc/user_policy as appropriate for your site + diff --git a/httemplate/docs/config.html b/httemplate/docs/config.html index 49be7200b..9526f697e 100644 --- a/httemplate/docs/config.html +++ b/httemplate/docs/config.html @@ -101,6 +101,9 @@ All further configuration files and directories are located in
    • usernamemax - Maximum username length (default is the size of the SQL column, probably specified when fs-setup was run)
    • username-letter - The existance of this file will turn on the requirement that usernames contain at least one letter.
    • username-letterfirst - The existance of this file will turn on the requirement that usernames start with a letter. +
    • username_policy - This file controls the mechanism for preventing duplicate usernames in passwd/radius files exported from svc_accts. This should be one of 'prepend domsvc' 'append domsvc' or 'append domain' +
    • vpopmailmachines - Your vpopmail pop toasters, one per line. Each line is of the form "machinename vpopdir vpopuid vpopgid". Eg: poptoaster.domain.tld /home/vpopmail 508 508 Note: vpopuid and vpopgid are values taken from the vpopmail machine's /etc/passwd +
    diff --git a/httemplate/docs/upgrade8.html b/httemplate/docs/upgrade8.html index 702da5a1b..1a5c998c6 100644 --- a/httemplate/docs/upgrade8.html +++ b/httemplate/docs/upgrade8.html @@ -61,6 +61,30 @@ $ perl Makefile.PL $ make $ su # make install UNINST=1 +
  • Apply the following changes to your database: +
    +CREATE TABLE svc_forward (
    +  svcnum int NOT NULL,
    +  srcsvc int NOT NULL,
    +  dstsvc int NOT NULL,
    +  dst varchar(80),
    +  PRIMARY KEY (svcnum)
    +);
    +ALTER TABLE svc_acct ADD domsvc integer NOT NULL;
    +ALTER TABLE svc_domain ADD catchall integer;
    +ALTER TABLE part_svc ADD svc_acct__domsvc integer NULL;
    +ALTER TABLE part_svc ADD svc_acct__domsvc_flag char(1) NULL;
    +ALTER TABLE part_svc ADD svc_domain__catchall integer NULL;
    +ALTER TABLE part_svc ADD svc_domain__catchall_flag char(1) NULL;
    +ALTER TABLE part_svc ADD svc_forward__srcsvc integer NULL;
    +ALTER TABLE part_svc ADD svc_forward__srcsvc_flag char(1) NULL;
    +ALTER TABLE part_svc ADD svc_forward__dstsvc integer NULL;
    +ALTER TABLE part_svc ADD svc_forward__dstsvc_flag char(1) NULL;
    +ALTER TABLE part_svc ADD svc_forward__dst integer NULL;
    +ALTER TABLE part_svc ADD svc_forward__dst_flag char(1) NULL;
    +
    +
    +
  • If you are using PostgreSQL, apply the following changes to your database:
     CREATE UNIQUE INDEX agent_pkey ON agent ( agentnum );
    @@ -112,4 +136,7 @@ ALTER TABLE cust_main ADD COLUMN ship_fax varchar(12) NULL;
     ALTER TABLE cust_main ADD COLUMN comments varchar NULL;
     
  • Run bin/dbdef-create. +
  • create a service based on svc_forward +
  • Run bin/fs-migrate-svc_acct_sm +
  • create the conf.dbsrc/user_policy as appropriate for your site