communigate phase 3: archive messages, RT#7515
[freeside.git] / FS / FS / part_export / communigate_pro.pm
1 package FS::part_export::communigate_pro;
2
3 use strict;
4 use vars qw(@ISA %info %options %quotas $DEBUG);
5 use Data::Dumper;
6 use Tie::IxHash;
7 use FS::part_export;
8 use FS::queue;
9
10 @ISA = qw(FS::part_export);
11
12 $DEBUG = 1;
13
14 tie %options, 'Tie::IxHash',
15   'port'          => { label   =>'Port number', default=>'106', },
16   'login'         => { label   =>'The administrator account name.  The name can contain a domain part.', },
17   'password'      => { label   =>'The administrator account password.', },
18   'accountType'   => { label   => 'Type for newly-created accounts (default when not specified in service)',
19                        type    => 'select',
20                        options => [qw(MultiMailbox TextMailbox MailDirMailbox AGrade BGrade CGrade)],
21                        default => 'MultiMailbox',
22                      },
23   'externalFlag'  => { label   => 'Create accounts with an external (visible for legacy mailers) INBOX.',
24                        type    => 'checkbox',
25                      },
26   'AccessModes'   => { label   => 'Access modes (default when not specified in service)',
27                        default => 'Mail POP IMAP PWD WebMail WebSite',
28                      },
29   'create_domain' => { label   => 'Domain creation API call',
30                        type    => 'select',
31                        options => [qw( CreateDomain CreateSharedDomain )],
32                      }
33 ;
34
35 %info = (
36   'svc'     => [qw( svc_acct svc_domain svc_forward svc_mailinglist )],
37   'desc'    => 'Real-time export of accounts, domains, mail forwards and mailing lists to a CommuniGate Pro mail server',
38   'options' => \%options,
39   'notes'   => <<'END'
40 Real time export of accounts, domains, mail forwards and mailing lists to a
41 <a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a>
42 mail server.  The
43 <a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a>
44 must be installed as CGP::CLI.
45 END
46 );
47
48 %quotas = (
49   'quota'        => 'MaxAccountSize',
50   'file_quota'   => 'MaxWebSize',
51   'file_maxnum'  => 'MaxWebFiles',
52   'file_maxsize' => 'MaxFileSize',
53 );
54
55 sub rebless { shift; }
56
57 sub export_username {
58   my($self, $svc_acct) = (shift, shift);
59   $svc_acct->email;
60 }
61
62 sub _export_insert {
63   my( $self, $svc_x ) = (shift, shift);
64
65   my $table = $svc_x->table;
66   my $method = "_export_insert_$table";
67   $self->$method($svc_x, @_);
68 }
69
70 sub _export_insert_svc_acct {
71   my( $self, $svc_acct ) = (shift, shift);
72
73   my %settings = (
74     'AccessModes'    => [ split(' ', ( $svc_acct->cgp_accessmodes
75                                        || $self->option('AccessModes') )
76                                )
77                         ],
78     'RealName'       => $svc_acct->finger,
79     'Password'       => $svc_acct->_password,
80
81     'PasswordRecovery' => ($svc_acct->password_recover ? 'YES':'NO'),
82
83     'RulesAllowed'     => $svc_acct->cgp_rulesallowed,
84     'RPOPAllowed'      =>($svc_acct->cgp_rpopallowed    ?'YES':'NO'),
85     'MailToAll'        =>($svc_acct->cgp_mailtoall      ?'YES':'NO'),
86     'AddMailTrailer'   =>($svc_acct->cgp_addmailtrailer ?'YES':'NO'),
87
88     'ArchiveMessagesAfter' => $svc_acct->cgp_archiveafter,
89
90     map { $quotas{$_} => $svc_acct->$_() }
91         grep $svc_acct->$_(), keys %quotas
92   );
93   #XXX phase 3: mailing lists
94
95   my @options = ( 'CreateAccount',
96     'accountName'    => $self->export_username($svc_acct),
97     'accountType'    => ( $svc_acct->cgp_type
98                           || $self->option('accountType') ), 
99     'settings'       => \%settings
100   );
101
102   push @options, 'externalFlag'   => $self->option('externalFlag')
103     if $self->option('externalFlag');
104
105   #let's do the create realtime too, for much the same reasons, and to avoid
106   #pain of trying to queue w/dep the prefs & aliases
107   eval { $self->communigate_pro_runcommand( @options ) };
108   return $@ if $@;
109
110   #preferences
111   my %prefs = ();
112   $prefs{'DeleteMode'} = $svc_acct->cgp_deletemode if $svc_acct->cgp_deletemode;
113   $prefs{'EmptyTrash'} = $svc_acct->cgp_emptytrash if $svc_acct->cgp_emptytrash;
114   $prefs{'Language'} = $svc_acct->cgp_language if $svc_acct->cgp_language;
115   $prefs{'TimeZone'} = $svc_acct->cgp_timezone if $svc_acct->cgp_timezone;
116   $prefs{'SkinName'} = $svc_acct->cgp_skinname if $svc_acct->cgp_skinname;
117   $prefs{'ProntoSkinName'} = $svc_acct->cgp_prontoskinname if $svc_acct->cgp_prontoskinname;
118   $prefs{'SendMDNMode'} = $svc_acct->cgp_sendmdnmode if $svc_acct->cgp_sendmdnmode;
119   if ( keys %prefs ) {
120     my $pref_err = $self->communigate_pro_queue( $svc_acct->svcnum,
121       'UpdateAccountPrefs',
122       $self->export_username($svc_acct),
123       %prefs,
124     );
125    warn "WARNING: error queueing UpdateAccountPrefs job: $pref_err"
126     if $pref_err;
127   }
128
129   #aliases
130   if ( $svc_acct->cgp_aliases ) {
131     my $alias_err = $self->communigate_pro_queue( $svc_acct->svcnum,
132       'SetAccountAliases',
133       $self->export_username($svc_acct),
134       [ split(/\s*[,\s]\s*/, $svc_acct->cgp_aliases) ],
135     );
136     warn "WARNING: error queueing SetAccountAliases job: $alias_err"
137       if $alias_err;
138   }
139
140   my $rule_error = $self->communigate_pro_queue(
141     $svc_acct->svcnum,
142     'SetAccountMailRules',
143     $self->export_username($svc_acct),
144     $svc_acct->cgp_rule_arrayref,
145   );
146   warn "WARNING: error queueing SetAccountMailRules job: $rule_error"
147     if $rule_error;
148
149   '';
150
151 }
152
153 sub _export_insert_svc_domain {
154   my( $self, $svc_domain ) = (shift, shift);
155
156   my $create = $self->option('create_domain') || 'CreateDomain';
157
158   my %settings = (
159     'DomainAccessModes'    => [ split(' ', $svc_domain->cgp_accessmodes ) ],
160   );
161   $settings{'AccountsLimit'} = $svc_domain->max_accounts
162     if $svc_domain->max_accounts;
163   $settings{'AdminDomainName'} = $svc_domain->parent_svc_x->domain
164     if $svc_domain->parent_svcnum;
165   $settings{'TrailerText'} = $svc_domain->trailer
166     if $svc_domain->trailer;
167
168   my @options = ( $create, $svc_domain->domain, \%settings );
169
170   eval { $self->communigate_pro_runcommand( @options ) };
171   return $@ if $@;
172
173   #aliases
174   if ( $svc_domain->cgp_aliases ) {
175     my $alias_err = $self->communigate_pro_queue( $svc_domain->svcnum,
176       'SetDomainAliases',
177       $svc_domain->domain,
178       split(/\s*[,\s]\s*/, $svc_domain->cgp_aliases),
179     );
180     warn "WARNING: error queueing SetDomainAliases job: $alias_err"
181       if $alias_err;
182   }
183
184   #account defaults
185   my $def_err = $self->communigate_pro_queue( $svc_domain->svcnum,
186     'SetAccountDefaults',
187     $svc_domain->domain,
188     'PWDAllowed'     =>($svc_domain->acct_def_password_selfchange ? 'YES':'NO'),
189     'PasswordRecovery' => ($svc_domain->acct_def_password_recover ? 'YES':'NO'),
190     'AccessModes'      => $svc_domain->acct_def_cgp_accessmodes,
191     'MaxAccountSize'   => $svc_domain->acct_def_quota,
192     'MaxWebSize'       => $svc_domain->acct_def_file_quota,
193     'MaxWebFile'       => $svc_domain->acct_def_file_maxnum,
194     'MaxFileSize'      => $svc_domain->acct_def_file_maxsize,
195     'RulesAllowed'     => $svc_domain->acct_def_cgp_rulesallowed,
196     'RPOPAllowed'      =>($svc_domain->acct_def_cgp_rpopallowed    ?'YES':'NO'),
197     'MailToAll'        =>($svc_domain->acct_def_cgp_mailtoall      ?'YES':'NO'),
198     'AddMailTrailer'   =>($svc_domain->acct_def_cgp_addmailtrailer ?'YES':'NO'),
199     'ArchiveMessagesAfter' => $svc_domain->acct_def_cgp_archiveafter,
200   );
201   warn "WARNING: error queueing SetAccountDefaults job: $def_err"
202     if $def_err;
203
204   #account defaults prefs
205   my $pref_err = $self->communigate_pro_queue( $svc_domain->svcnum,
206     'SetAccountDefaultPrefs',
207     $svc_domain->domain,
208     'DeleteMode'     => $svc_domain->acct_def_cgp_deletemode,
209     'EmptyTrash'     => $svc_domain->acct_def_cgp_emptytrash,
210     'Language'       => $svc_domain->acct_def_cgp_language,
211     'TimeZone'       => $svc_domain->acct_def_cgp_timezone,
212     'SkinName'       => $svc_domain->acct_def_cgp_skinname,
213     'ProntoSkinName' => $svc_domain->acct_def_cgp_prontoskinname,
214     'SendMDNMode'    => $svc_domain->acct_def_cgp_sendmdnmode,
215   );
216   warn "WARNING: error queueing SetAccountDefaultPrefs job: $pref_err"
217     if $pref_err;
218
219   my $rule_error = $self->communigate_pro_queue(
220     $svc_domain->svcnum,
221     'SetDomainMailRules',
222     $svc_domain->domain,
223     $svc_domain->cgp_rule_arrayref,
224   );
225   warn "WARNING: error queueing SetDomainMailRules job: $rule_error"
226     if $rule_error;
227
228   '';
229
230 }
231
232 sub _export_insert_svc_forward {
233   my( $self, $svc_forward ) = (shift, shift);
234
235   my $src = $svc_forward->src || $svc_forward->srcsvc_acct->email;
236   my $dst = $svc_forward->dst || $svc_forward->dstsvc_acct->email;
237
238   #real-time here, presuming CGP does some dup detection?
239   eval { $self->communigate_pro_runcommand( 'CreateForwarder', $src, $dst); };
240   return $@ if $@;
241
242   '';
243 }
244
245 sub _export_insert_svc_mailinglist {
246   my( $self, $svc_mlist ) = (shift, shift);
247
248   my @members = map $_->email_address,
249                     $svc_mlist->mailinglist->mailinglistmember;
250
251   #real-time here, presuming CGP does some dup detection
252   eval { $self->communigate_pro_runcommand(
253            'CreateGroup',
254            $svc_mlist->username.'@'.$svc_mlist->domain,
255            { 'RealName'      => $svc_mlist->listname,
256              'SetReplyTo'    => ( $svc_mlist->reply_to         ? 'YES' : 'NO' ),
257              'RemoveAuthor'  => ( $svc_mlist->remove_from      ? 'YES' : 'NO' ),
258              'RejectAuto'    => ( $svc_mlist->reject_auto      ? 'YES' : 'NO' ),
259              'RemoveToAndCc' => ( $svc_mlist->remove_to_and_cc ? 'YES' : 'NO' ),
260              'Members'       => \@members,
261            }
262          );
263        };
264   return $@ if $@;
265
266   '';
267
268 }
269
270 sub _export_replace {
271   my( $self, $new, $old ) = (shift, shift, shift);
272
273   my $table = $new->table;
274   my $method = "_export_replace_$table";
275   $self->$method($new, $old, @_);
276 }
277
278 sub _export_replace_svc_acct {
279   my( $self, $new, $old ) = (shift, shift, shift);
280
281   #let's just do the rename part realtime rather than trying to queue
282   #w/dependencies.  we don't want FS winding up out-of-sync with the wrong
283   #username and a queued job anyway.  right??
284   if ( $self->export_username($old) ne $self->export_username($new) ) {
285     eval { $self->communigate_pro_runcommand(
286       'RenameAccount',
287       $self->export_username($old),
288       $self->export_username($new),
289     ) };
290     return $@ if $@;
291   }
292
293   if ( $new->_password ne $old->_password
294        && '*SUSPENDED* '.$old->_password ne $new->_password
295   ) {
296     $self->communigate_pro_queue( $new->svcnum, 'SetAccountPassword',
297                                   $self->export_username($new), $new->_password
298                                 );
299   }
300
301   my %settings = ();
302
303   $settings{'RealName'} = $new->finger
304     if $old->finger ne $new->finger;
305   $settings{$quotas{$_}} = $new->$_()
306     foreach grep $old->$_() ne $new->$_(), keys %quotas;
307   $settings{'accountType'} = $new->cgp_type
308     if $old->cgp_type ne $new->cgp_type;
309   $settings{'AccessModes'} = $new->cgp_accessmodes
310     if $old->cgp_accessmodes ne $new->cgp_accessmodes
311     || $old->cgp_type ne $new->cgp_type;
312
313   $settings{'PasswordRecovery'} = ( $new->password_recover ? 'YES':'NO' )
314     if $old->password_recover ne $new->password_recover;
315
316   $settings{'RulesAllowed'} = $new->cgp_rulesallowed
317     if $old->cgp_rulesallowed ne $new->cgp_rulesallowed;
318   $settings{'RPOPAllowed'} = ( $new->cgp_rpopallowed ? 'YES':'NO' )
319     if $old->cgp_rpopallowed ne $new->cgp_rpopallowed;
320   $settings{'MailToAll'} = ( $new->cgp_mailtoall ? 'YES':'NO' )
321     if $old->cgp_mailtoall ne $new->cgp_mailtoall;
322   $settings{'AddMailTrailer'} = ( $new->cgp_addmailtrailer ? 'YES':'NO' )
323     if $old->cgp_addmailtrailer ne $new->cgp_addmailtrailer;
324   $settings{'ArchiveMessagesAfter'} = $new->cgp_archiveafter
325     if $old->cgp_archiveafter ne $new->cgp_archiveafter;
326
327   #XXX phase 3: mailing lists
328
329   if ( keys %settings ) {
330     my $error = $self->communigate_pro_queue(
331       $new->svcnum,
332       'UpdateAccountSettings',
333       $self->export_username($new),
334       %settings,
335     );
336     return $error if $error;
337   }
338
339   #preferences
340   my %prefs = ();
341   $prefs{'DeleteMode'} = $new->cgp_deletemode
342     if $old->cgp_deletemode ne $new->cgp_deletemode;
343   $prefs{'EmptyTrash'} = $new->cgp_emptytrash
344     if $old->cgp_emptytrash ne $new->cgp_emptytrash;
345   $prefs{'Language'} = $new->cgp_language
346     if $old->cgp_language ne $new->cgp_language;
347   $prefs{'TimeZone'} = $new->cgp_timezone
348     if $old->cgp_timezone ne $new->cgp_timezone;
349   $prefs{'SkinName'} = $new->cgp_skinname
350     if $old->cgp_skinname ne $new->cgp_skinname;
351   $prefs{'ProntoSkinName'} = $new->cgp_prontoskinname
352     if $old->cgp_prontoskinname ne $new->cgp_prontoskinname;
353   $prefs{'SendMDNMode'} = $new->cgp_sendmdnmode
354     if $old->cgp_sendmdnmode ne $new->cgp_sendmdnmode;
355   if ( keys %prefs ) {
356     my $pref_err = $self->communigate_pro_queue( $new->svcnum,
357       'UpdateAccountPrefs',
358       $self->export_username($new),
359       %prefs,
360     );
361    warn "WARNING: error queueing UpdateAccountPrefs job: $pref_err"
362     if $pref_err;
363   }
364
365   if ( $old->cgp_aliases ne $new->cgp_aliases ) {
366     my $error = $self->communigate_pro_queue(
367       $new->svcnum,
368       'SetAccountAliases',
369       $self->export_username($new),
370       [ split(/\s*[,\s]\s*/, $new->cgp_aliases) ],
371     );
372     return $error if $error;
373   }
374
375   my $rule_error = $self->communigate_pro_queue(
376     $new->svcnum,
377     'SetAccountMailRules',
378     $self->export_username($new),
379     $new->cgp_rule_arrayref,
380   );
381   warn "WARNING: error queueing SetAccountMailRules job: $rule_error"
382     if $rule_error;
383
384   '';
385
386 }
387
388 sub _export_replace_svc_domain {
389   my( $self, $new, $old ) = (shift, shift, shift);
390
391   #let's just do the rename part realtime rather than trying to queue
392   #w/dependencies.  we don't want FS winding up out-of-sync with the wrong
393   #username and a queued job anyway.  right??
394   if ( $old->domain ne $new->domain ) {
395     eval { $self->communigate_pro_runcommand(
396              'RenameDomain', $old->domain, $new->domain,
397          ) };
398     return $@ if $@;
399   }
400
401   my %settings = ();
402   $settings{'AccountsLimit'} = $new->max_accounts
403     if $old->max_accounts ne $new->max_accounts;
404   $settings{'TrailerText'} = $new->trailer
405     if $old->trailer ne $new->trailer;
406   $settings{'DomainAccessModes'} = $new->cgp_accessmodes
407     if $old->cgp_accessmodes ne $new->cgp_accessmodes;
408   $settings{'AdminDomainName'} =
409     $new->parent_svcnum ? $new->parent_svc_x->domain : ''
410       if $old->parent_svcnum != $new->parent_svcnum;
411
412   if ( keys %settings ) {
413     my $error = $self->communigate_pro_queue( $new->svcnum,
414       'UpdateDomainSettings',
415       $new->domain,
416       %settings,
417     );
418     return $error if $error;
419   }
420
421   if ( $old->cgp_aliases ne $new->cgp_aliases ) {
422     my $error = $self->communigate_pro_queue( $new->svcnum,
423       'SetDomainAliases',
424       $new->domain,
425       split(/\s*[,\s]\s*/, $new->cgp_aliases),
426     );
427     return $error if $error;
428   }
429
430   #below this identical to insert... any value to doing an Update here?
431   #not seeing any big one... i guess it would be nice to avoid the update
432   #when things haven't changed
433
434   #account defaults
435   my $def_err = $self->communigate_pro_queue( $new->svcnum,
436     'SetAccountDefaults',
437     $new->domain,
438     'PWDAllowed'       => ( $new->acct_def_password_selfchange ? 'YES' : 'NO' ),
439     'PasswordRecovery' => ( $new->acct_def_password_recover    ? 'YES' : 'NO' ),
440     'AccessModes'      => $new->acct_def_cgp_accessmodes,
441     'MaxAccountSize'   => $new->acct_def_quota,
442     'MaxWebSize'       => $new->acct_def_file_quota,
443     'MaxWebFile'       => $new->acct_def_file_maxnum,
444     'MaxFileSize'      => $new->acct_def_file_maxsize,
445     'RulesAllowed'     => $new->acct_def_cgp_rulesallowed,
446     'RPOPAllowed'      => ( $new->acct_def_cgp_rpopallowed    ? 'YES' : 'NO' ),
447     'MailToAll'        => ( $new->acct_def_cgp_mailtoall      ? 'YES' : 'NO' ),
448     'AddMailTrailer'   => ( $new->acct_def_cgp_addmailtrailer ? 'YES' : 'NO' ),
449     'ArchiveMessagesAfter' => $new->acct_def_cgp_archiveafter,
450   );
451   warn "WARNING: error queueing SetAccountDefaults job: $def_err"
452     if $def_err;
453
454   #account defaults prefs
455   my $pref_err = $self->communigate_pro_queue( $new->svcnum,
456     'SetAccountDefaultPrefs',
457     $new->domain,
458     'DeleteMode'     => $new->acct_def_cgp_deletemode,
459     'EmptyTrash'     => $new->acct_def_cgp_emptytrash,
460     'Language'       => $new->acct_def_cgp_language,
461     'TimeZone'       => $new->acct_def_cgp_timezone,
462     'SkinName'       => $new->acct_def_cgp_skinname,
463     'ProntoSkinName' => $new->acct_def_cgp_prontoskinname,
464     'SendMDNMode'    => $new->acct_def_cgp_sendmdnmode,
465   );
466   warn "WARNING: error queueing SetAccountDefaultPrefs job: $pref_err"
467     if $pref_err;
468
469   my $rule_error = $self->communigate_pro_queue(
470     $new->svcnum,
471     'SetDomainMailRules',
472     $new->domain,
473     $new->cgp_rule_arrayref,
474   );
475   warn "WARNING: error queueing SetDomainMailRules job: $rule_error"
476     if $rule_error;
477
478   '';
479 }
480
481 sub _export_replace_svc_forward {
482   my( $self, $new, $old ) = (shift, shift, shift);
483
484   my $osrc = $old->src || $old->srcsvc_acct->email;
485   my $nsrc = $new->src || $new->srcsvc_acct->email;
486   my $odst = $old->dst || $old->dstsvc_acct->email;
487   my $ndst = $new->dst || $new->dstsvc_acct->email;
488
489   if ( $odst ne $ndst ) {
490
491     #no change command, so delete and create (real-time)
492     eval { $self->communigate_pro_runcommand('DeleteForwarder', $osrc) };
493     return $@ if $@;
494     eval { $self->communigate_pro_runcommand('CreateForwarder', $nsrc, $ndst)};
495     return $@ if $@;
496
497   } elsif ( $osrc ne $nsrc ) {
498
499     #real-time here, presuming CGP does some dup detection?
500     eval { $self->communigate_pro_runcommand( 'RenameForwarder', $osrc, $nsrc)};
501     return $@ if $@;
502
503   } else {
504     warn "communigate replace called for svc_forward with no changes\n";#confess
505   }
506
507   '';
508 }
509
510 sub _export_replace_svc_mailinglist {
511   my( $self, $new, $old ) = (shift, shift, shift);
512
513   my $oldGroupName = $old->username.'@'.$old->domain;
514   my $newGroupName = $new->username.'@'.$new->domain;
515
516   if ( $oldGroupName ne $newGroupName ) {
517     eval { $self->communigate_pro_runcommand(
518              'RenameGroup', $oldGroupName, $newGroupName ); };
519     return $@ if $@;
520   }
521
522   my @members = map $_->email_address,
523                 $new->mailinglist->mailinglistmember;
524
525   #real-time here, presuming CGP does some dup detection
526   eval { $self->communigate_pro_runcommand(
527            'SetGroup', $newGroupName,
528            { 'RealName'      => $new->listname,
529              'SetReplyTo'    => ( $new->reply_to         ? 'YES' : 'NO' ),
530              'RemoveAuthor'  => ( $new->remove_from      ? 'YES' : 'NO' ),
531              'RejectAuto'    => ( $new->reject_auto      ? 'YES' : 'NO' ),
532              'RemoveToAndCc' => ( $new->remove_to_and_cc ? 'YES' : 'NO' ),
533              'Members'       => \@members,
534            }
535          );
536        };
537   return $@ if $@;
538
539   '';
540
541 }
542
543 sub _export_delete {
544   my( $self, $svc_x ) = (shift, shift);
545
546   my $table = $svc_x->table;
547   my $method = "_export_delete_$table";
548   $self->$method($svc_x, @_);
549 }
550
551 sub _export_delete_svc_acct {
552   my( $self, $svc_acct ) = (shift, shift);
553
554   $self->communigate_pro_queue( $svc_acct->svcnum, 'DeleteAccount',
555     $self->export_username($svc_acct),
556   );
557 }
558
559 sub _export_delete_svc_domain {
560   my( $self, $svc_domain ) = (shift, shift);
561
562   $self->communigate_pro_queue( $svc_domain->svcnum, 'DeleteDomain',
563     $svc_domain->domain,
564     #XXX turn on force option for domain deletion?
565   );
566 }
567
568 sub _export_delete_svc_forward {
569   my( $self, $svc_forward ) = (shift, shift);
570
571   $self->communigate_pro_queue( $svc_forward->svcnum, 'DeleteForwarder',
572     ($svc_forward->src || $svc_forward->srcsvc_acct->email),
573   );
574 }
575
576 sub _export_delete_svc_mailinglist {
577   my( $self, $svc_mailinglist ) = (shift, shift);
578
579   #real-time here, presuming CGP does some dup detection
580   eval { $self->communigate_pro_runcommand(
581            'DeleteGroup',
582            $svc_mailinglist->username.'@'.$svc_mailinglist->domain,
583          );
584        };
585   return $@ if $@;
586
587   '';
588
589 }
590
591 sub _export_suspend {
592   my( $self, $svc_x ) = (shift, shift);
593
594   my $table = $svc_x->table;
595   my $method = "_export_suspend_$table";
596   $self->$method($svc_x, @_);
597
598 }
599
600 sub _export_suspend_svc_acct {
601   my( $self, $svc_acct ) = (shift, shift);
602
603   #XXX is this the desired suspnsion action?
604
605    $self->communigate_pro_queue(
606     $svc_acct->svcnum,
607     'UpdateAccountSettings',
608     $self->export_username($svc_acct),
609     'AccessModes' => 'Mail',
610   );
611
612 }
613
614 sub _export_suspend_svc_domain {
615   my( $self, $svc_domain) = (shift, shift);
616
617   #XXX domain operations
618   '';
619
620 }
621
622 sub _export_unsuspend {
623   my( $self, $svc_x ) = (shift, shift);
624
625   my $table = $svc_x->table;
626   my $method = "_export_unsuspend_$table";
627   $self->$method($svc_x, @_);
628
629 }
630
631 sub _export_unsuspend_svc_acct {
632   my( $self, $svc_acct ) = (shift, shift);
633
634   $self->communigate_pro_queue(
635     $svc_acct->svcnum,
636     'UpdateAccountSettings',
637     $self->export_username($svc_acct),
638     'AccessModes' => ( $svc_acct->cgp_accessmodes
639                          || $self->option('AccessModes') ),
640   );
641
642 }
643
644 sub _export_unsuspend_svc_domain {
645   my( $self, $svc_domain) = (shift, shift);
646
647   #XXX domain operations
648   '';
649
650 }
651
652 sub export_mailinglistmember_insert {
653   my( $self, $svc_mailinglist, $mailinglistmember ) = (shift, shift, shift);
654   $svc_mailinglist->replace();
655 }
656
657 sub export_mailinglistmember_replace {
658   my( $self, $svc_mailinglist, $new, $old ) = (shift, shift, shift, shift);
659   die "no way to do this from the UI right now";
660 }
661
662 sub export_mailinglistmember_delete {
663   my( $self, $svc_mailinglist, $mailinglistmember ) = (shift, shift, shift);
664   $svc_mailinglist->replace();
665 }
666
667 sub export_getsettings {
668   my($self, $svc_x) = (shift, shift);
669
670   my $table = $svc_x->table;
671   my $method = "export_getsettings_$table";
672
673   $self->can($method) ? $self->$method($svc_x, @_) : '';
674
675 }
676
677 sub export_getsettings_svc_domain {
678   my($self, $svc_domain, $settingsref, $defaultref ) = @_;
679
680   my $settings = eval { $self->communigate_pro_runcommand(
681     'GetDomainSettings',
682     $svc_domain->domain
683   ) };
684   return $@ if $@;
685
686   my $effective_settings = eval { $self->communigate_pro_runcommand(
687     'GetDomainEffectiveSettings',
688     $svc_domain->domain
689   ) };
690   return $@ if $@;
691
692   my $acct_defaults = eval { $self->communigate_pro_runcommand(
693     'GetAccountDefaults',
694     $svc_domain->domain
695   ) };
696   return $@ if $@;
697
698   my $acct_defaultprefs = eval { $self->communigate_pro_runcommand(
699     'GetAccountDefaultPrefs',
700     $svc_domain->domain
701   ) };
702   return $@ if $@;
703
704   my $rules = eval { $self->communigate_pro_runcommand(
705     'GetDomainMailRules',
706     $svc_domain->domain
707   ) };
708   return $@ if $@;
709
710   #aliases too
711   my $aliases = eval { $self->communigate_pro_runcommand(
712     'GetDomainAliases',
713     $svc_domain->domain
714   ) };
715   return $@ if $@;
716
717   my %more = (
718     ( map { ("Acct. Default $_" => $acct_defaults->{$_}); }
719           keys(%$acct_defaults)
720     ),
721     ( map { ("Acct. Default $_" => $acct_defaultprefs->{$_}); } #diff label??
722           keys(%$acct_defaultprefs)
723     ),
724     ( map _rule2string($_), @$rules ),
725     'Aliases' => join(', ', @$aliases),
726   );
727
728   %$effective_settings = ( %$effective_settings, %more );
729   %$settings = ( %$settings, %more );
730
731   #false laziness w/below
732   
733   my %defaults = map { $_ => 1 }
734                    grep !exists(${$settings}{$_}), keys %$effective_settings;
735
736   foreach my $key ( grep ref($effective_settings->{$_}),
737                     keys %$effective_settings )
738   {
739     $effective_settings->{$key} = _pretty( $effective_settings->{$key} );
740   }
741
742   %{$settingsref} = %$effective_settings;
743   %{$defaultref} = %defaults;
744
745   '';
746 }
747
748 sub export_getsettings_svc_acct {
749   my($self, $svc_acct, $settingsref, $defaultref ) = @_;
750
751   my $settings = eval { $self->communigate_pro_runcommand(
752     'GetAccountSettings',
753     $svc_acct->email
754   ) };
755   return $@ if $@;
756
757   delete($settings->{'Password'});
758
759   my $effective_settings = eval { $self->communigate_pro_runcommand(
760     'GetAccountEffectiveSettings',
761     $svc_acct->email
762   ) };
763   return $@ if $@;
764
765   delete($effective_settings->{'Password'});
766
767   #prefs/effectiveprefs too
768
769   my $prefs = eval { $self->communigate_pro_runcommand(
770     'GetAccountPrefs',
771     $svc_acct->email
772   ) };
773   return $@ if $@;
774
775   my $effective_prefs = eval { $self->communigate_pro_runcommand(
776     'GetAccountEffectivePrefs',
777     $svc_acct->email
778   ) };
779   return $@ if $@;
780
781   %$effective_settings = ( %$effective_settings,
782                            map { ("Pref $_" => $effective_prefs->{$_}); }
783                                keys(%$effective_prefs)
784                          );
785   %$settings = ( %$settings,
786                  map { ("Pref $_" => $prefs->{$_}); }
787                      keys(%$prefs)
788                );
789
790   #mail rules
791   my $rules = eval { $self->communigate_pro_runcommand(
792     'GetAccountMailRules',
793     $svc_acct->email
794   ) };
795   return $@ if $@;
796
797   %$effective_settings = ( %$effective_settings,
798                            map _rule2string($_), @$rules
799                          );
800   %$settings = ( %$settings,
801                  map _rule2string($_), @$rules
802                );
803
804   #aliases too
805   my $aliases = eval { $self->communigate_pro_runcommand(
806     'GetAccountAliases',
807     $svc_acct->email
808   ) };
809   return $@ if $@;
810
811   $effective_settings->{'Aliases'} = join(', ', @$aliases);
812   $settings->{'Aliases'}           = join(', ', @$aliases);
813
814   #false laziness w/above
815
816   my %defaults = map { $_ => 1 }
817                    grep !exists(${$settings}{$_}), keys %$effective_settings;
818
819   foreach my $key ( grep ref($effective_settings->{$_}),
820                     keys %$effective_settings )
821   {
822     $effective_settings->{$key} = _pretty( $effective_settings->{$key} );
823   }
824
825   %{$settingsref} = %$effective_settings;
826   %{$defaultref} = %defaults;
827
828   '';
829
830 }
831
832 sub _pretty {
833   my $value = shift;
834   if ( ref($value) eq 'ARRAY' ) {
835     '['. join(' ', map { ref($_) ? _pretty($_) : $_ } @$value ). ']';
836   } elsif ( ref($value) eq 'HASH' ) {
837     '{'. join(', ',
838         map { my $v = $value->{$_};
839               "$_:". ( ref($v) ? _pretty($v) : $v );
840             }
841             keys %$value
842     ). '}';
843   } else {
844     warn "serializing ". ref($value). " for table display not yet handled";
845   }
846 }
847
848 sub export_getsettings_svc_forward {
849   my($self, $svc_forward, $settingsref, $defaultref ) = @_;
850
851   my $dest = eval { $self->communigate_pro_runcommand(
852     'GetForwarder',
853     ($svc_forward->src || $svc_forward->srcsvc_acct->email),
854   ) };
855   return $@ if $@;
856
857   my $settings = { 'Destination' => $dest };
858
859   %{$settingsref} = %$settings;
860   %{$defaultref} = ();
861
862   '';
863 }
864
865 sub _rule2string {
866   my $rule = shift;
867   my($priority, $name, $conditions, $actions, $comment) = @$rule;
868   $conditions = join(', ', map { my $a = $_; join(' ', @$a); } @$conditions);
869   $actions    = join(', ', map { my $a = $_; join(' ', @$a); } @$actions);
870   ("Mail rule $name" => "$priority IF $conditions THEN $actions ($comment)");
871 }
872
873 sub export_getsettings_svc_mailinglist {
874   my($self, $svc_mailinglist, $settingsref, $defaultref ) = @_;
875
876   my $settings = eval { $self->communigate_pro_runcommand(
877     'GetGroup',
878     $svc_mailinglist->username.'@'.$svc_mailinglist->domain,
879   ) };
880   return $@ if $@;
881
882   $settings->{'Members'} = join(', ', @{ $settings->{'Members'} } );
883
884   %{$settingsref} = %$settings;
885
886   '';
887 }
888
889 sub communigate_pro_queue {
890   my( $self, $svcnum, $method ) = (shift, shift, shift);
891   my $jobnum = ''; #don't actually care
892   $self->communigate_pro_queue_dep( \$jobnum, $svcnum, $method, @_);
893 }
894
895 sub communigate_pro_queue_dep {
896   my( $self, $jobnumref, $svcnum, $method ) = splice(@_,0,4);
897
898   my %kludge_methods = (
899     #'CreateAccount'             => 'CreateAccount',
900     'UpdateAccountSettings'     => 'UpdateAccountSettings',
901     'UpdateAccountPrefs'        => 'cp_Scalar_Hash',
902     #'CreateDomain'              => 'cp_Scalar_Hash',
903     #'CreateSharedDomain'        => 'cp_Scalar_Hash',
904     'UpdateDomainSettings'      => 'cp_Scalar_settingsHash',
905     'SetDomainAliases'          => 'cp_Scalar_Array',
906     'SetAccountDefaults'        => 'cp_Scalar_settingsHash',
907     'UpdateAccountDefaults'     => 'cp_Scalar_settingsHash',
908     'SetAccountDefaultPrefs'    => 'cp_Scalar_settingsHash',
909     'UpdateAccountDefaultPrefs' => 'cp_Scalar_settingsHash',
910   );
911   my $sub = exists($kludge_methods{$method})
912               ? $kludge_methods{$method}
913               : 'communigate_pro_command';
914
915   my $queue = new FS::queue {
916     'svcnum' => $svcnum,
917     'job'    => "FS::part_export::communigate_pro::$sub",
918   };
919   my $error = $queue->insert(
920     $self->machine,
921     $self->option('port'),
922     $self->option('login'),
923     $self->option('password'),
924     $method,
925     @_,
926   );
927   $$jobnumref = $queue->jobnum unless $error;
928
929   return $error;
930 }
931
932 sub communigate_pro_runcommand {
933   my( $self, $method ) = (shift, shift);
934
935   communigate_pro_command(
936     $self->machine,
937     $self->option('port'),
938     $self->option('login'),
939     $self->option('password'),
940     $method,
941     @_,
942   );
943
944 }
945
946 #XXX one sub per arg prototype is lame.  more magic?  i suppose queue needs
947 # to store data strctures properly instead of just an arg list.  right.
948
949 sub cp_Scalar_Hash {
950   my( $machine, $port, $login, $password, $method, $scalar, %hash ) = @_;
951   my @args = ( $scalar, \%hash );
952   communigate_pro_command( $machine, $port, $login, $password, $method, @args );
953 }
954
955 sub cp_Scalar_Array {
956   my( $machine, $port, $login, $password, $method, $scalar, @array ) = @_;
957   my @args = ( $scalar, \@array );
958   communigate_pro_command( $machine, $port, $login, $password, $method, @args );
959 }
960
961 #sub cp_Hash {
962 #  my( $machine, $port, $login, $password, $method, %hash ) = @_;
963 #  my @args = ( \%hash );
964 #  communigate_pro_command( $machine, $port, $login, $password, $method, @args );
965 #}
966
967 sub cp_Scalar_settingsHash {
968   my( $machine, $port, $login, $password, $method, $domain, %settings ) = @_;
969   for (qw( AccessModes DomainAccessModes )) {
970     $settings{$_} = [split(' ',$settings{$_})] if $settings{$_};
971   }
972   my @args = ( 'domain' => $domain, 'settings' => \%settings );
973   communigate_pro_command( $machine, $port, $login, $password, $method, @args );
974 }
975
976 #sub CreateAccount {
977 #  my( $machine, $port, $login, $password, $method, %args ) = @_;
978 #  my $accountName  = delete $args{'accountName'};
979 #  my $accountType  = delete $args{'accountType'};
980 #  my $externalFlag = delete $args{'externalFlag'};
981 #  $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
982 #  my @args = ( accountName  => $accountName,
983 #               accountType  => $accountType,
984 #               settings     => \%args,
985 #             );
986 #               #externalFlag => $externalFlag,
987 #  push @args, externalFlag => $externalFlag if $externalFlag;
988 #
989 #  communigate_pro_command( $machine, $port, $login, $password, $method, @args );
990 #
991 #}
992
993 sub UpdateAccountSettings {
994   my( $machine, $port, $login, $password, $method, $accountName, %args ) = @_;
995   $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
996   my @args = ( $accountName, \%args );
997   communigate_pro_command( $machine, $port, $login, $password, $method, @args );
998 }
999
1000 sub communigate_pro_command { #subroutine, not method
1001   my( $machine, $port, $login, $password, $method, @args ) = @_;
1002
1003   eval "use CGP::CLI";
1004   die $@ if $@;
1005
1006   my $cli = new CGP::CLI( {
1007     'PeerAddr' => $machine,
1008     'PeerPort' => $port,
1009     'login'    => $login,
1010     'password' => $password,
1011   } ) or die "Can't login to CGPro: $CGP::ERR_STRING\n";
1012
1013   #warn "$method ". Dumper(@args) if $DEBUG;
1014
1015   my $return = $cli->$method(@args)
1016     or die "Communigate Pro error: ". $cli->getErrMessage. "\n";
1017
1018   $cli->Logout; # or die "Can't logout of CGPro: $CGP::ERR_STRING\n";
1019
1020   $return;
1021
1022 }
1023
1024 1;
1025