package FS::part_export;
use strict;
-use vars qw( @ISA );
+use vars qw( @ISA @EXPORT_OK %exports );
+use Exporter;
+use Tie::IxHash;
use FS::Record qw( qsearch qsearchs dbh );
use FS::part_svc;
use FS::part_export_option;
+use FS::export_svc;
@ISA = qw(FS::Record);
+@EXPORT_OK = qw(export_info);
=head1 NAME
$record = new FS::part_export \%hash;
$record = new FS::part_export { 'column' => 'value' };
- ($new_record, $options) = $template_recored->clone( $svcpart );
+ #($new_record, $options) = $template_recored->clone( $svcpart );
$error = $record->insert( { 'option' => 'value' } );
$error = $record->insert( \%options );
=item exportnum - primary key
-=item svcpart - Service definition (see L<FS::part_svc>) to which this export applies
-
=item machine - Machine name
=item exporttype - Export type
sub table { 'part_export'; }
-=item clone SVCPART
-
-An alternate constructor. Creates a new export by duplicating an existing
-export. The given svcpart is assigned to the new export.
-
-Returns a list consisting of the new export object and a hashref of options.
-
=cut
-sub clone {
- my $self = shift;
- my $class = ref($self);
- my %hash = $self->hash;
- $hash{'exportnum'} = '';
- $hash{'svcpart'} = shift;
- ( $class->new( \%hash ),
- { map { $_->optionname => $_->optionvalue }
- qsearch('part_export_option', { 'exportnum' => $self->exportnum } )
- }
- );
-}
+#=item clone SVCPART
+#
+#An alternate constructor. Creates a new export by duplicating an existing
+#export. The given svcpart is assigned to the new export.
+#
+#Returns a list consisting of the new export object and a hashref of options.
+#
+#=cut
+#
+#sub clone {
+# my $self = shift;
+# my $class = ref($self);
+# my %hash = $self->hash;
+# $hash{'exportnum'} = '';
+# $hash{'svcpart'} = shift;
+# ( $class->new( \%hash ),
+# { map { $_->optionname => $_->optionvalue }
+# qsearch('part_export_option', { 'exportnum' => $self->exportnum } )
+# }
+# );
+#}
=item insert HASHREF
'';
-};
+}
=item delete
}
}
+ foreach my $export_svc ( $self->export_svc ) {
+ my $error = $export_svc->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
my $error =
$self->ut_numbern('exportnum')
|| $self->ut_domain('machine')
- || $self->ut_number('svcpart')
|| $self->ut_alpha('exporttype')
;
return $error if $error;
- return "Unknown svcpart: ". $self->svcpart
- unless qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
+ warn $self->machine. "!!!\n";
$self->machine =~ /^([\w\-\.]*)$/
or return "Illegal machine: ". $self->machine;
$self->nodomain =~ /^(Y?)$/ or return "Illegal nodomain: ". $self->nodomain;
$self->nodomain($1);
+ $self->deprecated(1); #BLAH
+
#check exporttype?
''; #no error
}
-=item part_svc
+#=item part_svc
+#
+#Returns the service definition (see L<FS::part_svc>) for this export.
+#
+#=cut
+#
+#sub part_svc {
+# my $self = shift;
+# qsearchs('part_svc', { svcpart => $self->svcpart } );
+#}
+
+sub part_svc {
+ use Carp;
+ croak "FS::part_export::part_svc deprecated";
+ #confess "FS::part_export::part_svc deprecated";
+}
+
+=item export_svc
-Returns the service definition (see L<FS::part_svc>) for this export.
+Returns a list of associated FS::export_svc records.
=cut
-sub part_svc {
+sub export_svc {
my $self = shift;
- qsearchs('part_svc', { svcpart => $self->svcpart } );
+ qsearch('export_svc', { 'exportnum' => $self->exportnum } );
}
=item part_export_option
sub rebless {
my $self = shift;
my $exporttype = $self->exporttype;
- my $class = ref($self);
- bless($self, $class."::$exporttype");
+ my $class = ref($self). "::$exporttype";
+ eval "use $class;" or die $@;
+ bless($self, $class);
}
=item export_insert SVC_OBJECT
$self->_export_delete(@_);
}
-=back
-
-=cut
-
-#infostreet
-
-package FS::part_export::infostreet;
-use vars qw(@ISA);
-@ISA = qw(FS::part_export);
-
+#fallbacks providing useful error messages intead of infinite loops
sub _export_insert {
- my( $self, $svc_acct ) = (shift, shift);
- $self->infostreet_queue( $svc_acct->svcnum,
- 'createUser', $svc_acct->username, $svc_acct->password );
+ my $self = shift;
+ return "_export_insert: unknown export type ". $self->exporttype;
}
sub _export_replace {
- my( $self, $new, $old ) = (shift, shift, shift);
- return "can't change username with InfoStreet"
- if $old->username ne $new->username;
- return '' unless $old->_password ne $new->_password;
- $self->infostreet_queue( $new->svcnum,
- 'passwd', $new->username, $new->password );
+ my $self = shift;
+ return "_export_replace: unknown export type ". $self->exporttype;
}
sub _export_delete {
- my( $self, $svc_acct ) = (shift, shift);
- $self->infostreet_queue( $svc_acct->svcnum,
- 'purgeAccount,releaseUsername', $svc_acct->username );
+ my $self = shift;
+ return "_export_delete: unknown export type ". $self->exporttype;
}
-sub infostreet_queue {
- my( $self, $svcnum, $method ) = (shift, shift, shift);
- my $queue = new FS::queue {
- 'svcnum' => $svcnum,
- 'job' => 'FS::part_export::infostreet::infostreet_command',
- };
- $queue->insert(
- $self->option('url'),
- $self->option('login'),
- $self->option('password'),
- $self->option('groupID'),
- $method,
- @_,
- );
-}
+=back
-sub infostreet_command { #subroutine, not method
- my($url, $username, $password, $groupID, $method, @args) = @_;
+=head1 SUBROUTINES
- #quelle hack
- if ( $method =~ /,/ ) {
- foreach my $part ( split(/,\s*/, $method) ) {
- infostreet_command($url, $username, $password, $groupID, $part, @args);
- }
- return;
- }
+=over 4
- eval "use Frontier::Client;";
+=item export_info [ SVCDB ]
- my $conn = Frontier::Client->new( url => $url );
- my $key_result = $conn->call( 'authenticate', $username, $password, $groupID);
- my %key_result = _infostreet_parse($key_result);
- die $key_result{error} unless $key_result{success};
- my $key = $key_result{data};
+Returns a hash reference of the exports for the given I<svcdb>, or if no
+I<svcdb> is specified, for all exports. The keys of the hash are
+I<exporttype>s and the values are again hash references containing information
+on the export:
- my $result = $conn->call($method, $key, @args);
- my %result = _infostreet_parse($result);
- die $result{error} unless $result{success};
+ 'desc' => 'Description',
+ 'options' => {
+ 'option' => { label=>'Option Label' },
+ 'option2' => { label=>'Another label' },
+ },
+ 'nodomain' => 'Y', #or ''
+ 'notes' => 'Additional notes',
-}
+=cut
-sub _infostreet_parse { #subroutine, not method
- my $arg = shift;
- map {
- my $value = $arg->{$_};
- #warn ref($value);
- $value = $value->value()
- if ref($value) && $value->isa('Frontier::RPC2::DataType');
- $_=>$value;
- } keys %$arg;
+sub export_info {
+ #warn $_[0];
+ return $exports{$_[0]} if @_;
+ #{ map { %{$exports{$_}} } keys %exports };
+ my $r = { map { %{$exports{$_}} } keys %exports };
}
-#sqlradius
-
-package FS::part_export::sqlradius;
-use vars qw(@ISA);
-@ISA = qw(FS::part_export);
-
-sub _export_insert {
- my($self, $svc_acct) = (shift, shift);
- $self->sqlradius_queue( $svc_acct->svcnum, 'insert',
- 'reply', $svc_acct->username, $svc_acct->radius_reply );
- $self->sqlradius_queue( $svc_acct->svcnum, 'insert',
- 'check', $svc_acct->username, $svc_acct->radius_check );
-}
+=item exporttype2svcdb EXPORTTYPE
-sub _export_replace {
- my( $self, $new, $old ) = (shift, shift, shift);
-
- #return "can't (yet) change username with sqlradius"
- # if $old->username ne $new->username;
- if ( $old->username ne $new->username ) {
- my $error = $self->sqlradius_queue( $new->svcnum, 'rename',
- $new->username, $old->username );
- return $error if $error;
- }
+Returns the applicable I<svcdb> for an I<exporttype>.
- foreach my $table (qw(reply check)) {
- my $method = "radius_$table";
- my %new = $new->$method;
- my %old = $old->$method;
- if ( grep { !exists $old{$_} #new attributes
- || $new{$_} ne $old{$_} #changed
- } keys %new
- ) {
- my $error = $self->sqlradius_queue( $new->svcnum, 'insert',
- $table, $new->username, %new );
- return $error if $error;
- }
+=cut
- my @del = grep { !exists $new{$_} } keys %old;
- if ( @del ) {
- my $error = $self->sqlradius_queue( $new->svcnum, 'attrib_delete',
- $table, $new->username, @del );
- return $error if $error;
- }
+sub exporttype2svcdb {
+ my $exporttype = $_[0];
+ foreach my $svcdb ( keys %exports ) {
+ return $svcdb if grep { $exporttype eq $_ } keys %{$exports{$svcdb}};
}
-
'';
}
-sub _export_delete {
- my( $self, $svc_acct ) = (shift, shift);
- $self->sqlradius_queue( $svc_acct->svcnum, 'delete',
- $svc_acct->username );
-}
-
-sub sqlradius_queue {
- my( $self, $svcnum, $method ) = (shift, shift, shift);
- my $queue = new FS::queue {
- 'svcnum' => $svcnum,
- 'job' => "FS::part_export::sqlradius::sqlradius_$method",
- };
- $queue->insert(
- $self->option('datasrc'),
- $self->option('username'),
- $self->option('password'),
- @_,
- );
-}
-
-sub sqlradius_insert { #subroutine, not method
- my $dbh = sqlradius_connect(shift, shift, shift);
- my( $replycheck, $username, %attributes ) = @_;
-
- foreach my $attribute ( keys %attributes ) {
- my $u_sth = $dbh->prepare(
- "UPDATE rad$replycheck SET Value = ? WHERE UserName = ? AND Attribute = ?" ) or die $dbh->errstr;
- my $i_sth = $dbh->prepare(
- "INSERT INTO rad$replycheck ( id, UserName, Attribute, Value ) ".
- "VALUES ( ?, ?, ?, ? )" )
- or die $dbh->errstr;
- $u_sth->execute($attributes{$attribute}, $username, $attribute) > 0
- or $i_sth->execute( '', $username, $attribute, $attributes{$attribute} )
- or die "can't insert into rad$replycheck table: ". $i_sth->errstr;
- }
- $dbh->disconnect;
-}
+tie my %shellcommands_options, 'Tie::IxHash',
+ #'machine' => { label=>'Remote machine' },
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'useradd' => { label=>'Insert command',
+ default=>'useradd -d $dir -m -s $shell -u $uid $username'
+ #default=>'cp -pr /etc/skel $dir; chown -R $uid.$gid $dir'
+ },
+ 'userdel' => { label=>'Delete command',
+ default=>'userdel $username',
+ #default=>'rm -rf $dir',
+ },
+ 'usermod' => { label=>'Modify command',
+ default=>'usermod -d $new_dir -l $new_username -s $new_shell -u $new_uid $old_username',
+ #default=>'[ -d $old_dir ] && mv $old_dir $new_dir || ( '.
+ # 'chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; '.
+ # 'find . -depth -print | cpio -pdm $new_dir; '.
+ # 'chmod u-t $new_dir; chown -R $uid.$gid $new_dir; '.
+ # 'rm -rf $old_dir'.
+ #')'
+ },
+;
+
+tie my %sqlradius_options, 'Tie::IxHash',
+ 'datasrc' => { label=>'DBI data source ' },
+ 'username' => { label=>'Database username' },
+ 'password' => { label=>'Database password' },
+;
+
+tie my %cyrus_options, 'Tie::IxHash',
+ 'server' => { label=>'IMAP server' },
+ 'username' => { label=>'Admin username' },
+ 'password' => { label=>'Admin password' },
+;
+
+tie my %cp_options, 'Tie::IxHash',
+ 'host' => { label=>'Hostname' },
+ 'port' => { label=>'Port number' },
+ 'username' => { label=>'Username' },
+ 'password' => { label=>'Password' },
+ 'domain' => { label=>'Domain' },
+ 'workgroup' => { label=>'Default Workgroup' },
+;
+
+tie my %infostreet_options, 'Tie::IxHash',
+ 'url' => { label=>'XML-RPC Access URL', },
+ 'login' => { label=>'InfoStreet login', },
+ 'password' => { label=>'InfoStreet password', },
+ 'groupID' => { label=>'InfoStreet groupID', },
+;
+
+tie my %vpopmail_options, 'Tie::IxHash',
+ 'machine' => { label=>'vpopmail machine', },
+ 'dir' => { label=>'directory', }, # ?more info? default?
+ 'uid' => { label=>'vpopmail uid' },
+ 'gid' => { label=>'vpopmail gid' },
+;
+
+tie my %bind_options, 'Tie::IxHash',
+ #'machine' => { label=>'named machine' },
+ 'named_conf' => { label => 'named.conf location',
+ default=> '/etc/bind/named.conf' },
+ 'zonepath' => { label => 'path to zone files',
+ default=> '/etc/bind/', },
+;
+
+tie my %bind_slave_options, 'Tie::IxHash',
+ #'machine' => { label=> 'Slave machine' },
+ 'master' => { label=> 'Master IP address(s) (semicolon-separated)' },
+ 'named_conf' => { label => 'named.conf location',
+ default => '/etc/bind/named.conf' },
+;
+
+
+
+#export names cannot have dashes...
+%exports = (
+ 'svc_acct' => {
+ 'sysvshell' => {
+ 'desc' =>
+ 'Batch export of /etc/passwd and /etc/shadow files (Linux/SysV)',
+ 'options' => {},
+ },
+ 'bsdshell' => {
+ 'desc' =>
+ 'Batch export of /etc/passwd and /etc/master.passwd files (BSD)',
+ 'options' => {},
+ },
+# 'nis' => {
+# 'desc' =>
+# 'Batch export of /etc/global/passwd and /etc/global/shadow for NIS ',
+# 'options' => {},
+# },
+ 'textradius' => {
+ 'desc' => 'Batch export of a text /etc/raddb/users file (Livingston, Cistron)',
+ 'options' => {},
+ },
+
+ 'shellcommands' => {
+ 'desc' => 'Real-time export via remote SSH (i.e. useradd, userdel, etc.)',
+ 'options' => \%shellcommands_options,
+ 'nodomain' => 'Y',
+ 'notes' => 'shellcommandsnotes... (this one is the nodomain one)',
+ },
+
+ 'sqlradius' => {
+ 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS)',
+ 'options' => \%sqlradius_options,
+ 'nodomain' => 'Y',
+ 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. Use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI-1.23/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source. If using <a href="http://www.freeradius.org/">FreeRADIUS</a> 0.5 or above, make sure your <b>op</b> fields are set to allow NULL values.',
+ },
+
+ 'cyrus' => {
+ 'desc' => 'Real-time export to Cyrus IMAP server',
+ 'options' => \%cyrus_options,
+ 'nodomain' => 'Y',
+ 'notes' => 'Integration with <a href="http://asg.web.cmu.edu/cyrus/imapd/">Cyrus IMAP Server</a>. Cyrus::IMAP::Admin should be installed locally and the connection to the server secured. <B>svc_acct.quota</B>, if available, is used to set the Cyrus quota. '
+ },
+
+ 'cp' => {
+ 'desc' => 'Real-time export to Critical Path Account Provisioning Protocol',
+ 'options' => \%cp_options,
+ 'notes' => 'Real-time export to <a href="http://www.cp.net/">Critial Path Account Provisioning Protocol</a>. Requires installation of <a href="http://search.cpan.org/search?dist=Net-APP">Net::APP</a> from CPAN.',
+ },
+
+ 'infostreet' => {
+ 'desc' => 'Real-time export to InfoStreet streetSmartAPI',
+ 'options' => \%infostreet_options,
+ 'nodomain' => 'Y',
+ 'notes' => 'Real-time export to <a href="http://www.infostreet.com/">InfoStreet</a> streetSmartAPI. Requires installation of <a href="http://search.cpan.org/search?dist=Frontier-Client">Frontier::Client</a> from CPAN.',
+ },
+
+ 'vpopmail' => {
+ 'desc' => 'Real-time export to vpopmail text files',
+ 'options' => \%vpopmail_options,
+
+ 'notes' => 'Real time export to <a href="http://inter7.com/vpopmail/">vpopmail</a> text files (...extended description from jeff?...)',
+ },
+
+ },
+
+ 'svc_domain' => {
+
+ 'bind' => {
+ 'desc' =>'Batch export to BIND named',
+ 'options' => \%bind_options,
+ 'notes' => 'bind export notes File::Rsync dependancy, run bind.export',
+ },
+
+ 'bind_slave' => {
+ 'desc' =>'Batch export to slave BIND named',
+ 'options' => \%bind_slave_options,
+ 'notes' => 'bind export notes (secondary munge) File::Rsync dependancy, run bind.export',
+ },
+
+
+ },
+
+ 'svc_acct_sm' => {},
+
+ 'svc_forward' => {},
+
+ 'svc_www' => {},
+
+);
-sub sqlradius_rename { #subroutine, not method
- my $dbh = sqlradius_connect(shift, shift, shift);
- my($new_username, $old_username) = @_;
- foreach my $table (qw(radreply radcheck)) {
- my $sth = $dbh->prepare("UPDATE $table SET Username = ? WHERE UserName = ?")
- or die $dbh->errstr;
- $sth->execute($new_username, $old_username)
- or die "can't update $table: ". $sth->errstr;
- }
- $dbh->disconnect;
-}
-
-sub sqlradius_attrib_delete { #subroutine, not method
- my $dbh = sqlradius_connect(shift, shift, shift);
- my( $replycheck, $username, @attrib ) = @_;
-
- foreach my $attribute ( @attrib ) {
- my $sth = $dbh->prepare(
- "DELETE FROM rad$replycheck WHERE UserName = ? AND Attribute = ?" )
- or die $dbh->errstr;
- $sth->execute($username,$attribute)
- or die "can't delete from rad$replycheck table: ". $sth->errstr;
- }
- $dbh->disconnect;
-}
-
-sub sqlradius_delete { #subroutine, not method
- my $dbh = sqlradius_connect(shift, shift, shift);
- my $username = shift;
-
- foreach my $table (qw( radcheck radreply )) {
- my $sth = $dbh->prepare( "DELETE FROM $table WHERE UserName = ?" );
- $sth->execute($username)
- or die "can't delete from $table table: ". $sth->errstr;
- }
- $dbh->disconnect;
-}
-
-sub sqlradius_connect {
- #my($datasrc, $username, $password) = @_;
- #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
- DBI->connect(@_) or die $DBI::errstr;
-}
+=back
=head1 NEW EXPORT CLASSES
- #myexport
-
- package FS::part_export::myexport;
- use vars qw(@ISA);
- @ISA = qw(FS::part_export);
-
- sub _export_insert {
- my($self, $svc_something) = (shift, shift);
- $self->myexport_queue( $svc_acct->svcnum, 'insert',
- $svc_something->username, $svc_something->password );
- }
-
- sub _export_replace {
- my( $self, $new, $old ) = (shift, shift, shift);
- #return "can't change username with myexport"
- # if $old->username ne $new->username;
- #return '' unless $old->_password ne $new->_password;
- $self->myexport_queue( $new->svcnum,
- 'replace', $new->username, $new->password );
- }
-
- sub _export_delete {
- my( $self, $svc_something ) = (shift, shift);
- $self->myexport_queue( $svc_acct->svcnum,
- 'delete', $svc_something->username );
- }
-
- #a good idea to queue anything that could fail or take any time
- sub myexport_queue {
- my( $self, $svcnum, $method ) = (shift, shift, shift);
- my $queue = new FS::queue {
- 'svcnum' => $svcnum,
- 'job' => "FS::part_export::myexport::myexport_$method",
- };
- $queue->insert( @_ );
- }
-
- sub myexport_insert { #subroutine, not method
- }
- sub myexport_replace { #subroutine, not method
- }
- sub myexport_delete { #subroutine, not method
- }
+Should be added to the %export hash here, and a module should be added in
+FS/FS/part_export/ (an example may be found in eg/export_template.pm)
=head1 BUGS
Probably.
-Hmm, export code has wound up in here. Move those sub-classes out into their
-own files, at least. Also hmm... cust_export class (not necessarily a
-database table...) ... ?
+Hmm... cust_export class (not necessarily a database table...) ... ?
+
+deprecated column...
=head1 SEE ALSO
-L<FS::part_export_option>, L<FS::part_svc>, L<FS::svc_acct>, L<FS::svc_domain>,
+L<FS::part_export_option>, L<FS::export_svc>, L<FS::svc_acct>,
+L<FS::svc_domain>,
L<FS::svc_forward>, L<FS::Record>, schema.html from the base documentation.
=cut