dns updates from Erik L: add ttl support, add check for SRV and finish allowing addit...
[freeside.git] / FS / FS / part_export / domain_sql.pm
1 package FS::part_export::domain_sql;
2
3 use vars qw(@ISA %info);
4 use Tie::IxHash;
5 use FS::part_export;
6
7 @ISA = qw(FS::part_export);
8
9 #quite a bit of false laziness w/acct_sql - some stuff should be generalized
10 #out to a "dababase base class"
11
12 tie my %options, 'Tie::IxHash',
13   'datasrc'            => { label => 'DBI data source' },
14   'username'           => { label => 'Database username' },
15   'password'           => { label => 'Database password' },
16   'table'              => { label => 'Database table' },
17   'schema'             => { label =>
18                               'Database schema mapping to Freeside methods.',
19                             type  => 'textarea',
20                           },
21   'static'             => { label =>
22                               'Database schema mapping to static values.',
23                             type  => 'textarea',
24                           },
25   'primary_key'        => { label => 'Database primary key' },
26 ;
27
28 tie my %postfix_transport_map, 'Tie::IxHash', 
29   'domain' => 'domain'
30 ;
31 my $postfix_transport_map = 
32   join('\n', map "$_ $postfix_transport_map{$_}",
33                  keys %postfix_transport_map      );
34 tie my %postfix_transport_static, 'Tie::IxHash',
35   'transport' => 'virtual:',
36 ;
37 my $postfix_transport_static = 
38   join('\n', map "$_ $postfix_transport_static{$_}",
39                  keys %postfix_transport_static      );
40
41 %info  = (
42   'svc'     => 'svc_domain',
43   'desc'    => 'Real time export of domains to SQL databases '.
44                '(postfix, others?)',
45   'options' => \%options,
46   'notes'   => <<END
47 Export domains (svc_domain records) to SQL databases.  Currently this is a
48 simple export with a default for Postfix, but it can be extended for other
49 uses.
50
51 <BR><BR>Use these buttons for useful presets:
52 <UL>
53   <LI><INPUT TYPE="button" VALUE="postfix_transport" onClick='
54     this.form.table.value = "transport";
55     this.form.schema.value = "$postfix_transport_map";
56     this.form.static.value = "$postfix_transport_static";
57     this.form.primary_key.value = "domain";
58   '>
59 </UL>
60 END
61 );
62
63 sub _schema_map { shift->_map('schema'); }
64 sub _static_map { shift->_map('static'); }
65
66 sub _map {
67   my $self = shift;
68   map { /^\s*(\S+)\s*(\S+)\s*$/ } split("\n", $self->option(shift) );
69 }
70
71 sub _export_insert {
72   my($self, $svc_domain) = (shift, shift);
73
74   my %schema = $self->_schema_map;
75   my %static = $self->_static_map;
76
77   my %record = ( ( map { $_ => $static{$_}       } keys %static ),
78                  ( map { my $method = $schema{$_};
79                        $_ => $svc_domain->$method();
80                        }
81                        keys %schema
82                  )
83                );
84
85   my $err_or_queue = 
86     $self->domain_sql_queue(
87       $svc_domain->svcnum,
88       'insert',
89       $self->option('table'),
90       %record
91     );
92   return $err_or_queue unless ref($err_or_queue);
93
94   '';
95 }
96
97 sub _export_replace {
98   my($self, $new, $old) = (shift, shift, shift);
99
100   my %schema = $self->_schema_map;
101   my %static = $self->_static_map;
102   #my %map = (%schema, %static);
103
104   my @primary_key = ();
105   if ( $self->option('primary_key') =~ /,/ ) {
106     foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
107       my $keymap = $schema{$key};
108       push @primary_key, $old->$keymap();
109     }
110   } else {
111     my %map = (%schema, %static);
112     my $keymap = $map{$self->option('primary_key')};
113     push @primary_key, $old->$keymap();
114   }
115
116   my %record = ( ( map { $_ => $static{$_}       } keys %static ),
117                  ( map { my $method = $schema{$_};
118                          $_ => $new->$method();
119                        }
120                        keys %schema
121                  )
122                );
123
124   my $err_or_queue = $self->domain_sql_queue(
125     $new->svcnum,
126     'replace',
127     $self->option('table'),
128     $self->option('primary_key'), @primary_key, 
129     %record,
130   );
131   return $err_or_queue unless ref($err_or_queue);
132   '';
133 }
134
135 sub _export_delete {
136   my ( $self, $svc_domain ) = (shift, shift);
137
138   my %schema = $self->_schema_map;
139   my %static = $self->_static_map;
140   my %map = (%schema, %static);
141
142   my %primary_key = ();
143   if ( $self->option('primary_key') =~ /,/ ) {
144     foreach my $key ( split(/\s*,\s*/, $self->option('primary_key') ) ) {
145       my $keymap = $map{$key};
146       $primary_key{ $key } = $svc_domain->$keymap();
147     }
148   } else {
149     my $keymap = $map{$self->option('primary_key')};
150     $primary_key{ $self->option('primary_key') } = $svc_domain->$keymap(),
151   }
152
153   my $err_or_queue = $self->domain_sql_queue(
154     $svc_domain->svcnum,
155     'delete',
156     $self->option('table'),
157     %primary_key,
158     #$self->option('primary_key') => $svc_domain->$keymap(),
159   );
160   return $err_or_queue unless ref($err_or_queue);
161   '';
162 }
163
164 sub domain_sql_queue {
165   my( $self, $svcnum, $method ) = (shift, shift, shift);
166   my $queue = new FS::queue {
167     'svcnum' => $svcnum,
168     'job'    => "FS::part_export::domain_sql::domain_sql_$method",
169   };
170   $queue->insert(
171     $self->option('datasrc'),
172     $self->option('username'),
173     $self->option('password'),
174     @_,
175   ) or $queue;
176 }
177
178 sub domain_sql_insert { #subroutine, not method
179   my $dbh = domain_sql_connect(shift, shift, shift);
180   my( $table, %record ) = @_;
181
182   my $sth = $dbh->prepare(
183     "INSERT INTO $table ( ". join(", ", keys %record).
184     " ) VALUES ( ". join(", ", map '?', keys %record ). " )"
185   ) or die $dbh->errstr;
186
187   $sth->execute( values(%record) )
188     or die "can't insert into $table table: ". $sth->errstr;
189
190   $dbh->disconnect;
191 }
192
193 sub domain_sql_delete { #subroutine, not method
194   my $dbh = domain_sql_connect(shift, shift, shift);
195   my( $table, %record ) = @_;
196
197   my $sth = $dbh->prepare(
198     "DELETE FROM $table WHERE ". join(' AND ', map "$_ = ? ", keys %record )
199   ) or die $dbh->errstr;
200
201   $sth->execute( map $record{$_}, keys %record )
202     or die "can't delete from $table table: ". $sth->errstr;
203
204   $dbh->disconnect;
205 }
206
207 sub domain_sql_replace { #subroutine, not method
208   my $dbh = domain_sql_connect(shift, shift, shift);
209
210   my( $table, $pkey ) = ( shift, shift );
211
212   my %primary_key = ();
213   if ( $pkey =~ /,/ ) {
214     foreach my $key ( split(/\s*,\s*/, $pkey ) ) {
215       $primary_key{$key} = shift;
216     }
217   } else {
218     $primary_key{$pkey} = shift;
219   }
220
221   my %record = @_;
222
223   my $sth = $dbh->prepare(
224     "UPDATE $table".
225     ' SET '.   join(', ',    map "$_ = ?", keys %record      ).
226     ' WHERE '. join(' AND ', map "$_ = ?", keys %primary_key )
227   ) or die $dbh->errstr;
228
229   $sth->execute( values(%record), values(%primary_key) );
230
231   $dbh->disconnect;
232 }
233
234 sub domain_sql_connect {
235   #my($datasrc, $username, $password) = @_;
236   #DBI->connect($datasrc, $username, $password) or die $DBI::errstr;
237   DBI->connect(@_) or die $DBI::errstr;
238 }
239
240 1;
241