1 package FS::part_export::ispconfig3;
5 use base qw( FS::part_export );
14 FS::part_export::ispconfig3
18 ISPConfig 3 integration for Freeside
22 This export offers basic svc_acct provisioning for ISPConfig 3.
23 All email accounts will be assigned to a single specified client.
25 This module also provides generic methods for working through the L</ISPConfig3 API>.
33 option_labels => { 'y' => 'yes', 'n' => 'no' },
36 tie my %options, 'Tie::IxHash',
37 'soap_location' => { label => 'SOAP Location' },
38 'username' => { label => 'User Name',
40 'password' => { label => 'Password',
42 'debug' => { type => 'checkbox',
43 label => 'Enable debug warnings' },
44 'subheading' => { type => 'title',
45 label => 'Account defaults' },
46 'client_id' => { label => 'Client ID' },
47 'server_id' => { label => 'Server ID' },
48 'maildir' => { label => 'Maildir (substitutions from svc_acct, e.g. /mail/$domain/$username)', },
49 'cc' => { label => 'Cc' },
50 'autoresponder_text' => { label => 'Autoresponder text',
51 default => 'Out of Office Reply' },
52 'move_junk' => { type => 'select',
54 option_labels => { 'y' => 'yes', 'n' => 'no' },
55 label => 'Move junk' },
56 'postfix' => { type => 'select',
59 'access' => { type => 'select',
62 'disableimap' => { type => 'select',
64 label => 'Disable IMAP' },
65 'disablepop3' => { type => 'select',
67 label => 'Disable POP3' },
68 'disabledeliver' => { type => 'select',
70 label => 'Disable deliver' },
71 'disablesmtp' => { type => 'select',
73 label => 'Disable SMTP' },
78 'desc' => 'Export email account to ISPConfig 3',
79 'options' => \%options,
82 All email accounts will be assigned to a single specified client and server.
86 sub _mail_user_params {
87 my ($self, $svc_acct) = @_;
88 # all available api fields are in comments below, even if we don't use them
91 'server_id' => $self->option('server_id'),
93 'email' => $svc_acct->username.'@'.$svc_acct->domain,
95 'login' => $svc_acct->username.'@'.$svc_acct->domain,
96 #password (varchar(255))
97 'password' => $svc_acct->_password,
99 'name' => $svc_acct->finger,
101 'uid' => $svc_acct->uid,
103 'gid' => $svc_acct->gid,
104 #maildir (varchar(255))
105 'maildir' => $self->_substitute($self->option('maildir'),$svc_acct),
107 'quota' => $svc_acct->quota,
109 'cc' => $self->option('cc'),
110 #homedir (varchar(255))
111 'homedir' => $svc_acct->dir,
113 ## initializing with autoresponder off, but this could become an export option...
114 #autoresponder (enum('n','y'))
115 'autoresponder' => 'n',
116 #autoresponder_start_date (datetime)
117 #autoresponder_end_date (datetime)
118 #autoresponder_text (mediumtext)
119 'autoresponder_text' => $self->option('autoresponder_text'),
121 #move_junk (enum('n','y'))
122 'move_junk' => $self->option('move_junk'),
123 #postfix (enum('n','y'))
124 'postfix' => $self->option('postfix'),
125 #access (enum('n','y'))
126 'access' => $self->option('access'),
128 ## not needed right now, not sure what it is
129 #custom_mailfilter (mediumtext)
131 #disableimap (enum('n','y'))
132 'disableimap' => $self->option('disableimap'),
133 #disablepop3 (enum('n','y'))
134 'disablepop3' => $self->option('disablepop3'),
135 #disabledeliver (enum('n','y'))
136 'disabledeliver' => $self->option('disabledeliver'),
137 #disablesmtp (enum('n','y'))
138 'disablesmtp' => $self->option('disablesmtp'),
143 my ($self, $svc_acct) = @_;
144 my $params = $self->_mail_user_params($svc_acct);
146 my $remoteid = $self->api_call('mail_user_add',$self->option('client_id'),$params);
147 return $self->api_error_logout if $self->api_error;
148 my $error = $self->set_remoteid($svc_acct,$remoteid);
149 $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
152 $error ||= "Systems updated, but error logging out: ".$self->api_error
157 sub _export_replace {
158 my ($self, $svc_acct, $svc_acct_old) = @_;
159 my $remoteid = $self->get_remoteid($svc_acct_old);
160 return "Could not load remoteid for old service" unless $remoteid;
161 my $params = $self->_mail_user_params($svc_acct);
162 #API docs claim "Returns the number of affected rows"
163 my $success = $self->api_call('mail_user_update',$self->option('client_id'),$remoteid,$params);
164 return $self->api_error_logout if $self->api_error;
165 return "Server returned no rows updated, but no other error message" unless $success;
167 unless ($svc_acct->svcnum eq $svc_acct_old->svcnum) { # are these ever not equal?
168 $error = $self->set_remoteid($svc_acct,$remoteid);
169 $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
173 $error ||= "Systems updated, but error logging out: ".$self->api_error
179 my ($self, $svc_acct) = @_;
180 my $remoteid = $self->get_remoteid($svc_acct);
181 return "Could not load remoteid for old service" unless $remoteid;
182 #API docs claim "Returns the number of deleted records"
183 my $success = $self->api_call('mail_user_delete',$remoteid);
184 return $self->api_error_logout if $self->api_error;
185 my $error = $success ? '' : "Server returned no records deleted";
187 $error ||= "Systems updated, but error logging out: ".$self->api_error
192 sub _export_suspend {
193 my ($self, $svc_acct) = @_;
197 sub _export_unsuspend {
198 my ($self, $svc_acct) = @_;
202 =head1 ISPConfig3 API
204 These methods allow access to the ISPConfig3 API using the credentials
205 set in the export options.
211 Accepts I<$method> and I<@params>. Places an api call to the specified
212 method with the specified params. Returns the result of that call
213 (empty on failure.) Retrieve error messages using L</api_error>.
215 Do not include session id in list of params; it will be included
216 automatically. Must run L</api_login> first.
221 my ($self,$method,@params) = @_;
222 $self->{'__ispconfig_response'} = undef;
223 # This does get used by api_login,
224 # to retrieve the session id after it sets the client,
225 # so we only check for existence of client,
226 # and we only include session id if we have one
227 my $client = $self->{'__ispconfig_client'};
229 $self->{'__ispconfig_error'} = 'Not logged in';
232 if ($self->{'__ispconfig_session'}) {
233 unshift(@params,$self->{'__ispconfig_session'});
235 # Contact server in eval, to trap connection errors
236 warn "Calling SOAP method $method with params:\n".Dumper(\@params)."\n"
237 if $self->option('debug');
238 my $response = eval { $client->$method(@params) };
240 $self->{'__ispconfig_error'} = "Error contacting server: $@";
243 # Set results and return
244 $self->{'__ispconfig_error'} = $response->fault
245 ? "Error from server: " . $response->faultstring
247 $self->{'__ispconfig_response'} = $response;
248 return $response->result;
253 Returns the error string set by L</ISPConfig3 API> methods,
254 or a blank string if most recent call produced no errors.
260 return $self->{'__ispconfig_error'} || '';
263 =head2 api_error_logout
265 Attempts L</api_logout>, but returns L</api_error> message from
266 before logout was attempted. Useful for logging out
267 properly after an error.
271 sub api_error_logout {
273 my $error = $self->api_error;
280 Initializes an api session using the credentials for this export.
281 Returns true on success, false on failure.
282 Retrieve error messages using L</api_error>.
288 if ($self->{'__ispconfig_session'} || $self->{'__ispconfig_client'}) {
289 $self->{'__ispconfig_error'} = 'Already logged in';
292 $self->{'__ispconfig_session'} = undef;
293 $self->{'__ispconfig_client'} =
294 SOAP::Lite->proxy($self->option('soap_location'), ssl_opts => [ verify_hostname => 0 ] )
296 unless ($self->{'__ispconfig_client'}) {
297 $self->{'__ispconfig_error'} = 'Error creating SOAP client';
300 $self->{'__ispconfig_session'} =
301 $self->api_call('login',$self->option('username'),$self->option('password'))
303 return unless $self->{'__ispconfig_session'};
309 Ends the current api session established by L</api_login>.
310 Returns true on success, false on failure.
316 unless ($self->{'__ispconfig_session'}) {
317 $self->{'__ispconfig_error'} = 'Not logged in';
320 my $result = $self->api_call('logout');
321 # clear these even if there was a failure to logout
322 $self->{'__ispconfig_client'} = undef;
323 $self->{'__ispconfig_session'} = undef;
324 return if $self->api_error;
328 # false laziness with portaone export
330 my ($self, $string, @objects) = @_;
331 return '' unless $string;
332 foreach my $object (@objects) {
334 my @fields = $object->fields;
335 push(@fields,'domain') if $object->table eq 'svc_acct';
336 foreach my $field (@fields) {
338 my $value = $object->$field;
339 $string =~ s/\$$field/$value/g;
342 # strip leading/trailing whitespace
355 jonathan@freeside.biz