1 package FS::part_export::ispconfig3;
5 use base qw( FS::part_export );
15 FS::part_export::ispconfig3
19 ISPConfig 3 integration for Freeside
23 This export offers basic svc_acct provisioning for ISPConfig 3.
24 All email accounts will be assigned to a single specified client.
26 This module also provides generic methods for working through the L</ISPConfig3 API>.
34 option_labels => { 'y' => 'yes', 'n' => 'no' },
37 tie my %options, 'Tie::IxHash',
38 'soap_location' => { label => 'SOAP Location' },
39 'username' => { label => 'User Name',
41 'password' => { label => 'Password',
43 'debug' => { type => 'checkbox',
44 label => 'Enable debug warnings' },
45 'subheading' => { type => 'title',
46 label => 'Account defaults' },
47 'client_id' => { label => 'Client ID' },
48 'server_id' => { label => 'Server ID' },
49 'maildir' => { label => 'Maildir (substitutions from svc_acct, e.g. /mail/$domain/$username)', },
50 'cc' => { label => 'Cc' },
51 'autoresponder_text' => { label => 'Autoresponder text',
52 default => 'Out of Office Reply' },
53 'move_junk' => { type => 'select',
55 option_labels => { 'y' => 'yes', 'n' => 'no' },
56 label => 'Move junk' },
57 'postfix' => { type => 'select',
60 'access' => { type => 'select',
63 'disableimap' => { type => 'select',
65 label => 'Disable IMAP' },
66 'disablepop3' => { type => 'select',
68 label => 'Disable POP3' },
69 'disabledeliver' => { type => 'select',
71 label => 'Disable deliver' },
72 'disablesmtp' => { type => 'select',
74 label => 'Disable SMTP' },
79 'desc' => 'Export email account to ISPConfig 3',
80 'options' => \%options,
83 All email accounts will be assigned to a single specified client and server.
87 sub _mail_user_params {
88 my ($self, $svc_acct) = @_;
89 # all available api fields are in comments below, even if we don't use them
92 'server_id' => $self->option('server_id'),
94 'email' => $svc_acct->username.'@'.$svc_acct->domain,
96 'login' => $svc_acct->username.'@'.$svc_acct->domain,
97 #password (varchar(255))
98 'password' => $svc_acct->_password,
100 'name' => $svc_acct->finger,
102 'uid' => $svc_acct->uid,
104 'gid' => $svc_acct->gid,
105 #maildir (varchar(255))
106 'maildir' => $self->_substitute($self->option('maildir'),$svc_acct),
108 'quota' => $svc_acct->quota,
110 'cc' => $self->option('cc'),
111 #homedir (varchar(255))
112 'homedir' => $svc_acct->dir,
114 ## initializing with autoresponder off, but this could become an export option...
115 #autoresponder (enum('n','y'))
116 'autoresponder' => 'n',
117 #autoresponder_start_date (datetime)
118 #autoresponder_end_date (datetime)
119 #autoresponder_text (mediumtext)
120 'autoresponder_text' => $self->option('autoresponder_text'),
122 #move_junk (enum('n','y'))
123 'move_junk' => $self->option('move_junk'),
124 #postfix (enum('n','y'))
125 'postfix' => $self->option('postfix'),
126 #access (enum('n','y'))
127 'access' => $self->option('access'),
129 ## not needed right now, not sure what it is
130 #custom_mailfilter (mediumtext)
132 #disableimap (enum('n','y'))
133 'disableimap' => $self->option('disableimap'),
134 #disablepop3 (enum('n','y'))
135 'disablepop3' => $self->option('disablepop3'),
136 #disabledeliver (enum('n','y'))
137 'disabledeliver' => $self->option('disabledeliver'),
138 #disablesmtp (enum('n','y'))
139 'disablesmtp' => $self->option('disablesmtp'),
144 my ($self, $svc_acct) = @_;
145 return $self->api_error || 'Error logging in'
146 unless $self->api_login;
147 my $params = $self->_mail_user_params($svc_acct);
148 my $remoteid = $self->api_call('mail_user_add',$self->option('client_id'),$params);
149 return $self->api_error_logout if $self->api_error;
150 my $error = $self->set_remoteid($svc_acct,$remoteid);
151 $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
157 sub _export_replace {
158 my ($self, $svc_acct, $svc_acct_old) = @_;
159 return $self->api_error || 'Error logging in'
160 unless $self->api_login;
161 my $remoteid = $self->get_remoteid($svc_acct_old);
162 return "Could not load remoteid for old service" unless $remoteid;
163 my $params = $self->_mail_user_params($svc_acct);
164 #API docs claim "Returns the number of affected rows"
165 my $success = $self->api_call('mail_user_update',$self->option('client_id'),$remoteid,$params);
166 return $self->api_error_logout if $self->api_error;
167 return "Server returned no rows updated, but no other error message" unless $success;
169 unless ($svc_acct->svcnum eq $svc_acct_old->svcnum) { # are these ever not equal?
170 $error = $self->set_remoteid($svc_acct,$remoteid);
171 $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
179 my ($self, $svc_acct) = @_;
180 return $self->api_error || 'Error logging in'
181 unless $self->api_login;
182 my $remoteid = $self->get_remoteid($svc_acct);
183 #don't abort deletion--
184 # might have been provisioned before export was implemented,
185 # still need to be able to delete from freeside
187 warn "Could not load remoteid for svcnum ".$svc_acct->svcnum.", unprovisioning anyway";
190 #API docs claim "Returns the number of deleted records"
191 my $success = $self->api_call('mail_user_delete',$remoteid);
192 return $self->api_error_logout if $self->api_error;
193 #don't abort deletion--
194 # if it's already been deleted remotely,
195 # still need to be able to delete from freeside
196 warn "Server returned no records deleted for svcnum ".$svc_acct->svcnum.
197 " remoteid $remoteid, unprovisioning anyway"
203 sub _export_suspend {
204 my ($self, $svc_acct) = @_;
208 sub _export_unsuspend {
209 my ($self, $svc_acct) = @_;
213 =head1 ISPConfig3 API
215 These methods allow access to the ISPConfig3 API using the credentials
216 set in the export options.
222 Accepts I<$method> and I<@params>. Places an api call to the specified
223 method with the specified params. Returns the result of that call
224 (empty on failure.) Retrieve error messages using L</api_error>.
226 Do not include session id in list of params; it will be included
227 automatically. Must run L</api_login> first.
232 my ($self,$method,@params) = @_;
233 # This does get used by api_login,
234 # to retrieve the session id after it sets the client,
235 # so we only check for existence of client,
236 # and we only include session id if we have one
237 my $client = $self->{'__ispconfig_client'};
239 $self->{'__ispconfig_error'} = 'Not logged in';
242 if ($self->{'__ispconfig_session'}) {
243 unshift(@params,$self->{'__ispconfig_session'});
245 # Contact server in eval, to trap connection errors
246 warn "Calling SOAP method $method with params:\n".Dumper(\@params)."\n"
247 if $self->option('debug');
248 my $response = eval { $client->$method(@params) };
250 $self->{'__ispconfig_error'} = "Error contacting server: $@";
253 # Set results and return
254 $self->{'__ispconfig_error'} = $response->fault
255 ? "Error from server: " . $response->faultstring
257 return $response->result;
262 Returns the error string set by L</ISPConfig3 API> methods,
263 or a blank string if most recent call produced no errors.
269 return $self->{'__ispconfig_error'} || '';
272 =head2 api_error_logout
274 Attempts L</api_logout>, but returns L</api_error> message from
275 before logout was attempted. Useful for logging out
276 properly after an error.
280 sub api_error_logout {
282 my $error = $self->api_error;
289 Initializes an api session using the credentials for this export.
290 Returns true on success, false on failure.
291 Retrieve error messages using L</api_error>.
297 if ($self->{'__ispconfig_session'} || $self->{'__ispconfig_client'}) {
298 $self->{'__ispconfig_error'} = 'Already logged in';
301 $self->{'__ispconfig_session'} = undef;
302 $self->{'__ispconfig_client'} =
303 SOAP::Lite->proxy( $self->option('soap_location'),
305 verify_hostname => 0,
306 SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
310 unless ($self->{'__ispconfig_client'}) {
311 $self->{'__ispconfig_error'} = 'Error creating SOAP client';
314 $self->{'__ispconfig_session'} =
315 $self->api_call('login',$self->option('username'),$self->option('password'))
317 return unless $self->{'__ispconfig_session'};
323 Ends the current api session established by L</api_login>.
324 Returns true on success, false on failure.
330 unless ($self->{'__ispconfig_session'}) {
331 $self->{'__ispconfig_error'} = 'Not logged in';
334 my $result = $self->api_call('logout');
335 # clear these even if there was a failure to logout
336 $self->{'__ispconfig_client'} = undef;
337 $self->{'__ispconfig_session'} = undef;
338 return if $self->api_error;
342 # false laziness with portaone export
344 my ($self, $string, @objects) = @_;
345 return '' unless $string;
346 foreach my $object (@objects) {
348 my @fields = $object->fields;
349 push(@fields,'domain') if $object->table eq 'svc_acct';
350 foreach my $field (@fields) {
352 my $value = $object->$field;
353 $string =~ s/\$$field/$value/g;
356 # strip leading/trailing whitespace
369 jonathan@freeside.biz