1 package FS::part_export::acct_sql;
3 use vars qw(@ISA %info @saltset);
5 #use Digest::MD5 qw(md5_hex);
6 use FS::Record; #qw(qsearchs);
9 @ISA = qw(FS::part_export);
11 tie my %options, 'Tie::IxHash',
12 'datasrc' => { label => 'DBI data source' },
13 'username' => { label => 'Database username' },
14 'password' => { label => 'Database password' },
19 'desc' => 'Real-time export of accounts to SQL databases '.
20 '(Postfix+Courier IMAP, others?)',
21 'options' => \%options,
24 Export accounts (svc_acct records) to SQL databases. Written for
25 Postfix+Courier IMAP but intended to be generally useful for generic SQL
28 In contrast to sqlmail, this is newer and less well tested, and currently less
29 flexible. It is intended to export just svc_acct records only, rather than a
30 single export for svc_acct, svc_forward and svc_domain records, and to
31 be configured for different mail server setups through some subclassing
36 @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
38 #mapping needs to be configurable...
39 # export col => freeside col/method or callback
41 'username' => 'email',
42 'password' => '_password',
45 #false laziness w/shellcommands.pm
46 #eventually should check a "password-encoding" field
47 if ( length($svc_acct->_password) == 13
48 || $svc_acct->_password =~ /^\$(1|2a?)\$/ ) {
53 $saltset[int(rand(64))].$saltset[int(rand(64))]
59 'maildir' => sub { $_[0]->domain. '/maildirs/'. $_[0]->username. '/' },
60 'domain' => sub { shift->domain },
64 my $table = 'mailbox'; #also needs to be configurable...
66 my $primary_key = 'username';
68 sub rebless { shift; }
71 my($self, $svc_acct) = (shift, shift);
74 my %record = map { my $value = $map{$_};
76 ? &{$value}($svc_acct)
82 $self->acct_sql_queue( $svc_acct->svcnum, 'insert', $table, %record );
83 return $err_or_queue unless ref($err_or_queue);
93 my ( $self, $svc_acct ) = (shift, shift);
94 my $keymap = $map{$primary_key};
95 my $err_or_queue = $self->acct_sql_queue(
99 $primary_key => ref($keymap) ? &{$keymap}($svc_acct) : $svc_acct->$keymap()
101 return $err_or_queue unless ref($err_or_queue);
105 my( $self, $svcnum, $method ) = (shift, shift, shift);
106 my $queue = new FS::queue {
108 'job' => "FS::part_export::acct_sql::acct_sql_$method",
111 $self->option('datasrc'),
112 $self->option('username'),
113 $self->option('password'),
118 sub acct_sql_insert { #subroutine, not method
119 my $dbh = acct_sql_connect(shift, shift, shift);
120 my( $table, %record ) = @_;
122 my $sth = $dbh->prepare(
123 "INSERT INTO $table ( ". join(", ", keys %record).
124 " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
125 ) or die $dbh->errstr;
127 $sth->execute( map $record{$_}, keys %record )
128 or die "can't insert into $table table: ". $sth->errstr;
133 sub acct_sql_delete { #subroutine, not method
134 my $dbh = acct_sql_connect(shift, shift, shift);
135 my( $table, %record ) = @_;
137 my $sth = $dbh->prepare(
138 "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
139 ) or die $dbh->errstr;
141 $sth->execute( map $record{$_}, keys %record )
142 or die "can't delete from $table table: ". $sth->errstr;
147 sub acct_sql_connect {
148 #my($datasrc, $username, $password) = @_;
149 #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
150 DBI->connect(@_) or die $DBI::errstr;