import torrus 1.0.9
[freeside.git] / FS / FS / svc_acct.pm
index 6e7fb91..ac336b8 100644 (file)
@@ -1,13 +1,15 @@
 package FS::svc_acct;
 
 use strict;
-use base qw( FS::svc_Domain_Mixin FS::svc_Common );
+use base qw( FS::svc_Domain_Mixin FS::svc_CGP_Mixin FS::svc_CGPRule_Mixin
+             FS::svc_Common );
 use vars qw( $DEBUG $me $conf $skip_fuzzyfiles
              $dir_prefix @shells $usernamemin
              $usernamemax $passwordmin $passwordmax
              $username_ampersand $username_letter $username_letterfirst
              $username_noperiod $username_nounderscore $username_nodash
              $username_uppercase $username_percent $username_colon
+             $username_slash $username_equals
              $password_noampersand $password_noexclamation
              $warning_template $warning_from $warning_subject $warning_mimetype
              $warning_cc
@@ -46,6 +48,7 @@ use FS::part_export;
 use FS::svc_forward;
 use FS::svc_www;
 use FS::cdr;
+use FS::acct_snarf;
 
 $DEBUG = 0;
 $me = '[FS::svc_acct]';
@@ -72,6 +75,8 @@ FS::UID->install_callback( sub {
   $username_ampersand = $conf->exists('username-ampersand');
   $username_percent = $conf->exists('username-percent');
   $username_colon = $conf->exists('username-colon');
+  $username_slash = $conf->exists('username-slash');
+  $username_equals = $conf->exists('username-equals');
   $password_noampersand = $conf->exists('password-noexclamation');
   $password_noexclamation = $conf->exists('password-noexclamation');
   $dirhash = $conf->config('dirhash') || 0;
@@ -99,7 +104,7 @@ FS::UID->install_callback( sub {
 );
 
 @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
-@pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '!', '.', ',' );
+@pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '.', ',' );
 
 sub _cache {
   my $self = shift;
@@ -268,44 +273,12 @@ sub table_info {
                          disable_fixed => 1,
                          disable_select => 1,
                        },
-        'cgp_type'=> { 
-                       label => 'Communigate account type',
-                       type => 'select',
-                       select_list => [qw( MultiMailbox TextMailbox MailDirMailbox AGrade BGrade CGrade )],
-                       disable_inventory => 1,
-                       disable_select    => 1,
-                     },
-        'cgp_accessmodes' => { 
-                               label => 'Communigate enabled services',
-                               type  => 'communigate_pro-accessmodes',
-                               disable_inventory => 1,
-                               disable_select    => 1,
-                             },
-        'cgp_aliases' => { 
-                           label => 'Communigate aliases',
-                           type  => 'text',
-                           disable_inventory => 1,
-                           disable_select    => 1,
-                         },
         'password_selfchange' => { label => 'Password modification',
                                    type  => 'checkbox',
                                  },
         'password_recover'    => { label => 'Password recovery',
                                    type  => 'checkbox',
                                  },
-        'cgp_deletemode' => { 
-                              label => 'Communigate message delete method',
-                              type  => 'select',
-                              select_list => [ 'Move To Trash', 'Immediately', 'Mark' ],
-                              disable_inventory => 1,
-                              disable_select    => 1,
-                            },
-        'cgp_emptytrash' => { 
-                              label => 'Communigate on logout remove trash',
-                              type  => 'text',
-                              disable_inventory => 1,
-                              disable_select    => 1,
-                            },
         'quota'     => { 
                          label => 'Quota', #Mail storage limit
                          type => 'text',
@@ -434,6 +407,120 @@ sub table_info {
                                    label     => 'Last logout',
                                    type      => 'disabled',
                                  },
+
+        'cgp_aliases' => { 
+                           label => 'Communigate aliases',
+                           type  => 'text',
+                           disable_inventory => 1,
+                           disable_select    => 1,
+                         },
+        #settings
+        'cgp_type'=> { 
+                       label => 'Communigate account type',
+                       type => 'select',
+                       select_list => [qw( MultiMailbox TextMailbox MailDirMailbox AGrade BGrade CGrade )],
+                       disable_inventory => 1,
+                       disable_select    => 1,
+                     },
+        'cgp_accessmodes' => { 
+                               label => 'Communigate enabled services',
+                               type  => 'communigate_pro-accessmodes',
+                               disable_inventory => 1,
+                               disable_select    => 1,
+                             },
+        'cgp_rulesallowed'   => {
+          label       => 'Allowed mail rules',
+          type        => 'select',
+          select_list => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ],
+          disable_inventory => 1,
+          disable_select    => 1,
+        },
+        'cgp_rpopallowed'    => { label => 'RPOP modifications',
+                                  type  => 'checkbox',
+                                },
+        'cgp_mailtoall'      => { label => 'Accepts mail to "all"',
+                                  type  => 'checkbox',
+                                },
+        'cgp_addmailtrailer' => { label => 'Add trailer to sent mail',
+                                  type  => 'checkbox',
+                                },
+        'cgp_archiveafter'   => {
+          label       => 'Archive messages after',
+          type        => 'select',
+          select_hash => [ 
+                           -2 => 'default(730 days)',
+                           0 => 'Never',
+                           86400 => '24 hours',
+                           172800 => '2 days',
+                           259200 => '3 days',
+                           432000 => '5 days',
+                           604800 => '7 days',
+                           1209600 => '2 weeks',
+                           2592000 => '30 days',
+                           7776000 => '90 days',
+                           15552000 => '180 days',
+                           31536000 => '365 days',
+                           63072000 => '730 days',
+                         ],
+          disable_inventory => 1,
+          disable_select    => 1,
+        },
+        #XXX mailing lists
+
+        #preferences
+        'cgp_deletemode' => { 
+                              label => 'Communigate message delete method',
+                              type  => 'select',
+                              select_list => [ 'Move To Trash', 'Immediately', 'Mark' ],
+                              disable_inventory => 1,
+                              disable_select    => 1,
+                            },
+        'cgp_emptytrash' => { 
+                              label     => 'Communigate on logout remove trash',
+                              type        => 'select',
+                              select_list => __PACKAGE__->cgp_emptytrash_values,
+                              disable_inventory => 1,
+                              disable_select    => 1,
+                            },
+        'cgp_language' => {
+                            label => 'Communigate language',
+                            type  => 'select',
+                            select_list => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ],
+                            disable_inventory => 1,
+                            disable_select    => 1,
+                          },
+        'cgp_timezone' => {
+                            label       => 'Communigate time zone',
+                            type        => 'select',
+                            select_list => __PACKAGE__->cgp_timezone_values,
+                            disable_inventory => 1,
+                            disable_select    => 1,
+                          },
+        'cgp_skinname' => {
+                            label => 'Communigate layout',
+                            type  => 'select',
+                            select_list => [ '', '***', 'GoldFleece', 'Skin2' ],
+                            disable_inventory => 1,
+                            disable_select    => 1,
+                          },
+        'cgp_prontoskinname' => {
+                            label => 'Communigate Pronto style',
+                            type  => 'select',
+                            select_list => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ],
+                            disable_inventory => 1,
+                            disable_select    => 1,
+                          },
+        'cgp_sendmdnmode' => {
+          label => 'Communigate send read receipts',
+          type  => 'select',
+          select_list => [ '', 'Never', 'Manually', 'Automatically' ],
+          disable_inventory => 1,
+          disable_select    => 1,
+        },
+
+        #mail
+        #XXX RPOP settings
+
     },
   };
 }
@@ -658,82 +745,90 @@ sub insert {
     }
 
     #welcome email
-    my ($to,$welcome_template,$welcome_from,$welcome_subject,$welcome_subject_template,$welcome_mimetype)
-      = ('','','','','','');
-
-    if ( $conf->exists('welcome_email', $agentnum) ) {
-      $welcome_template = new Text::Template (
-        TYPE   => 'ARRAY',
-        SOURCE => [ map "$_\n", $conf->config('welcome_email', $agentnum) ]
-      ) or warn "can't create welcome email template: $Text::Template::ERROR";
-      $welcome_from = $conf->config('welcome_email-from', $agentnum);
-        # || 'your-isp-is-dum'
-      $welcome_subject = $conf->config('welcome_email-subject', $agentnum)
-        || 'Welcome';
-      $welcome_subject_template = new Text::Template (
-        TYPE   => 'STRING',
-        SOURCE => $welcome_subject,
-      ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
-      $welcome_mimetype = $conf->config('welcome_email-mimetype', $agentnum)
-        || 'text/plain';
+    my $error = '';
+    my $msgnum = $conf->config('welcome_msgnum', $agentnum);
+    if ( $msgnum ) {
+      my $msg_template = qsearchs('msg_template', { msgnum => $msgnum });
+      $error = $msg_template->send('cust_main' => $cust_main,
+                                   'object'    => $self);
     }
-    if ( $welcome_template && $cust_pkg ) {
-      my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
-      if ( $to ) {
-
-        my %hash = (
-                     'custnum'  => $self->custnum,
-                     'username' => $self->username,
-                     'password' => $self->_password,
-                     'first'    => $cust_main->first,
-                     'last'     => $cust_main->getfield('last'),
-                     'pkg'      => $cust_pkg->part_pkg->pkg,
-                   );
-        my $wqueue = new FS::queue {
-          'svcnum' => $self->svcnum,
-          'job'    => 'FS::svc_acct::send_email'
-        };
-        my $error = $wqueue->insert(
-          'to'       => $to,
-          'from'     => $welcome_from,
-          'subject'  => $welcome_subject_template->fill_in( HASH => \%hash, ),
-          'mimetype' => $welcome_mimetype,
-          'body'     => $welcome_template->fill_in( HASH => \%hash, ),
-        );
-        if ( $error ) {
-          $dbh->rollback if $oldAutoCommit;
-          return "error queuing welcome email: $error";
-        }
+    else { #!$msgnum
+      my ($to,$welcome_template,$welcome_from,$welcome_subject,$welcome_subject_template,$welcome_mimetype)
+        = ('','','','','','');
+
+      if ( $conf->exists('welcome_email', $agentnum) ) {
+        $welcome_template = new Text::Template (
+          TYPE   => 'ARRAY',
+          SOURCE => [ map "$_\n", $conf->config('welcome_email', $agentnum) ]
+        ) or warn "can't create welcome email template: $Text::Template::ERROR";
+        $welcome_from = $conf->config('welcome_email-from', $agentnum);
+          # || 'your-isp-is-dum'
+        $welcome_subject = $conf->config('welcome_email-subject', $agentnum)
+          || 'Welcome';
+        $welcome_subject_template = new Text::Template (
+          TYPE   => 'STRING',
+          SOURCE => $welcome_subject,
+        ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
+        $welcome_mimetype = $conf->config('welcome_email-mimetype', $agentnum)
+          || 'text/plain';
+      }
+      if ( $welcome_template ) {
+        my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
+        if ( $to ) {
+
+          my %hash = (
+                       'custnum'  => $self->custnum,
+                       'username' => $self->username,
+                       'password' => $self->_password,
+                       'first'    => $cust_main->first,
+                       'last'     => $cust_main->getfield('last'),
+                       'pkg'      => $cust_pkg->part_pkg->pkg,
+                     );
+          my $wqueue = new FS::queue {
+            'svcnum' => $self->svcnum,
+            'job'    => 'FS::svc_acct::send_email'
+          };
+          my $error = $wqueue->insert(
+            'to'       => $to,
+            'from'     => $welcome_from,
+            'subject'  => $welcome_subject_template->fill_in( HASH => \%hash, ),
+            'mimetype' => $welcome_mimetype,
+            'body'     => $welcome_template->fill_in( HASH => \%hash, ),
+          );
+          if ( $error ) {
+            $dbh->rollback if $oldAutoCommit;
+            return "error queuing welcome email: $error";
+          }
 
-        if ( $options{'depend_jobnum'} ) {
-          warn "$me depend_jobnum found; adding to welcome email dependancies"
-            if $DEBUG;
-          if ( ref($options{'depend_jobnum'}) ) {
-            warn "$me adding jobs ". join(', ', @{$options{'depend_jobnum'}} ).
-                 "to welcome email dependancies"
-              if $DEBUG;
-            push @jobnums, @{ $options{'depend_jobnum'} };
-          } else {
-            warn "$me adding job $options{'depend_jobnum'} ".
-                 "to welcome email dependancies"
+          if ( $options{'depend_jobnum'} ) {
+            warn "$me depend_jobnum found; adding to welcome email dependancies"
               if $DEBUG;
-            push @jobnums, $options{'depend_jobnum'};
+            if ( ref($options{'depend_jobnum'}) ) {
+              warn "$me adding jobs ". join(', ', @{$options{'depend_jobnum'}} ).
+                   "to welcome email dependancies"
+                if $DEBUG;
+              push @jobnums, @{ $options{'depend_jobnum'} };
+            } else {
+              warn "$me adding job $options{'depend_jobnum'} ".
+                   "to welcome email dependancies"
+                if $DEBUG;
+              push @jobnums, $options{'depend_jobnum'};
+            }
           }
-        }
 
-        foreach my $jobnum ( @jobnums ) {
-          my $error = $wqueue->depend_insert($jobnum);
-          if ( $error ) {
-            $dbh->rollback if $oldAutoCommit;
-            return "error queuing welcome email job dependancy: $error";
+          foreach my $jobnum ( @jobnums ) {
+            my $error = $wqueue->depend_insert($jobnum);
+            if ( $error ) {
+              $dbh->rollback if $oldAutoCommit;
+              return "error queuing welcome email job dependancy: $error";
+            }
           }
-        }
-
-      }
 
-    }
+        }
 
-  } # if ( $cust_pkg )
+      } # if $welcome_template
+    } # if !$msgnum
+  } # if $cust_pkg
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   ''; #no error
@@ -1105,18 +1200,33 @@ sub check {
               || $self->ut_snumbern('upbytes')
               || $self->ut_snumbern('downbytes')
               || $self->ut_snumbern('totalbytes')
+              || $self->ut_snumbern('seconds_threshold')
+              || $self->ut_snumbern('upbytes_threshold')
+              || $self->ut_snumbern('downbytes_threshold')
+              || $self->ut_snumbern('totalbytes_threshold')
               || $self->ut_enum('_password_encoding', ['',qw(plain crypt ldap)])
               || $self->ut_enum('password_selfchange', [ '', 'Y' ])
               || $self->ut_enum('password_recover',    [ '', 'Y' ])
+              #cardfortress
+              || $self->ut_anything('cf_privatekey')
+              #communigate
               || $self->ut_textn('cgp_accessmodes')
               || $self->ut_alphan('cgp_type')
               || $self->ut_textn('cgp_aliases' ) #well
-              || $self->ut_alphasn('cgp_deletemode')
-              || $self->ut_alphan('cgp_emptytrash')
+              # settings
               || $self->ut_alphasn('cgp_rulesallowed')
               || $self->ut_enum('cgp_rpopallowed', [ '', 'Y' ])
               || $self->ut_enum('cgp_mailtoall', [ '', 'Y' ])
               || $self->ut_enum('cgp_addmailtrailer', [ '', 'Y' ])
+              || $self->ut_snumbern('cgp_archiveafter')
+              # preferences
+              || $self->ut_alphasn('cgp_deletemode')
+              || $self->ut_enum('cgp_emptytrash', $self->cgp_emptytrash_values)
+              || $self->ut_alphan('cgp_language')
+              || $self->ut_textn('cgp_timezone')
+              || $self->ut_textn('cgp_skinname')
+              || $self->ut_textn('cgp_prontoskinname')
+              || $self->ut_alphan('cgp_sendmdnmode')
   ;
   return $error if $error;
 
@@ -1136,16 +1246,14 @@ sub check {
   }
 
   my $ulen = $usernamemax || $self->dbdef_table->column('username')->length;
-  if ( $username_uppercase ) {
-    $recref->{username} =~ /^([a-z0-9_\-\.\&\%\:]{$usernamemin,$ulen})$/i
-      or return gettext('illegal_username'). " ($usernamemin-$ulen): ". $recref->{username};
-    $recref->{username} = $1;
-  } else {
-    $recref->{username} =~ /^([a-z0-9_\-\.\&\%\:]{$usernamemin,$ulen})$/
-      or return gettext('illegal_username'). " ($usernamemin-$ulen): ". $recref->{username};
-    $recref->{username} = $1;
-  }
 
+  $recref->{username} =~ /^([a-z0-9_\-\.\&\%\:\/\=]{$usernamemin,$ulen})$/i
+    or return gettext('illegal_username'). " ($usernamemin-$ulen): ". $recref->{username};
+  $recref->{username} = $1;
+
+  unless ( $username_uppercase ) {
+    $recref->{username} =~ /[A-Z]/ and return gettext('illegal_username');
+  }
   if ( $username_letterfirst ) {
     $recref->{username} =~ /^[a-z]/ or return gettext('illegal_username');
   } elsif ( $username_letter ) {
@@ -1169,6 +1277,12 @@ sub check {
   unless ( $username_colon ) {
     $recref->{username} =~ /\:/ and return gettext('illegal_username');
   }
+  unless ( $username_slash ) {
+    $recref->{username} =~ /\// and return gettext('illegal_username');
+  }
+  unless ( $username_equals ) {
+    $recref->{username} =~ /\=/ and return gettext('illegal_username');
+  }
 
   $recref->{popnum} =~ /^(\d*)$/ or return "Illegal popnum: ".$recref->{popnum};
   $recref->{popnum} = $1;
@@ -1248,7 +1362,7 @@ sub check {
     }
   }
   $self->getfield('finger') =~
-    /^([\w \t\!\@\#\$\%\&\(\)\-\+\;\'\"\,\.\?\/\*\<\>]*)$/
+    /^([µ_0123456789aAáÁàÀâÂåÅäÄãêæÆbBcCçÇdDðÐeEéÉèÈêÊëËfFgGhHiIíÍìÌîÎïÏjJkKlLmMnNñÑoOóÓòÒôÔöÖõÕøغpPqQrRsSßtTuUúÚùÙûÛüÜvVwWxXyYýÝÿzZþÞ \t\!\@\#\$\%\&\(\)\-\+\;\'\"\,\.\?\/\*\<\>]*)$/
       or return "Illegal finger: ". $self->getfield('finger');
   $self->setfield('finger', $1);
 
@@ -1826,17 +1940,27 @@ sub email {
 =item acct_snarf
 
 Returns an array of FS::acct_snarf records associated with the account.
-If the acct_snarf table does not exist or there are no associated records,
-an empty list is returned
 
 =cut
 
 sub acct_snarf {
   my $self = shift;
-  return () unless dbdef->table('acct_snarf');
-  eval "use FS::acct_snarf;";
-  die $@ if $@;
-  qsearch('acct_snarf', { 'svcnum' => $self->svcnum } );
+  qsearch({
+    'table'    => 'acct_snarf',
+    'hashref'  => { 'svcnum' => $self->svcnum },
+    #'order_by' => 'ORDER BY priority ASC',
+  });
+}
+
+=item cgp_rpop_hashref
+
+Returns an arrayref of RPOP data suitable for Communigate Pro API commands.
+
+=cut
+
+sub cgp_rpop_hashref {
+  my $self = shift;
+  { map { $_->snarfname => $_->cgp_hashref } $self->acct_snarf };
 }
 
 =item decrement_upbytes OCTETS
@@ -2151,7 +2275,7 @@ sub set_usage {
   my $reset = 0;
   my %handyhash = ();
   if ( $options{null} ) { 
-    %handyhash = ( map { ( $_ => 'NULL', $_."_threshold" => 'NULL' ) }
+    %handyhash = ( map { ( $_ => undef, $_."_threshold" => undef ) }
                    qw( seconds upbytes downbytes totalbytes )
                  );
   }
@@ -2173,7 +2297,7 @@ sub set_usage {
   #die $error if $error;         #services not explicity changed via the UI
 
   my $sql = "UPDATE svc_acct SET " .
-    join (',', map { "$_ =  $handyhash{$_}" } (keys %handyhash) ).
+    join (',', map { "$_ =  ?" } (keys %handyhash) ).
     " WHERE svcnum = ". $self->svcnum;
 
   warn "$me $sql\n"
@@ -2182,7 +2306,7 @@ sub set_usage {
   if (scalar(keys %handyhash)) {
     my $sth = $dbh->prepare( $sql )
       or die "Error preparing $sql: ". $dbh->errstr;
-    my $rv = $sth->execute();
+    my $rv = $sth->execute(values %handyhash);
     die "Error executing $sql: ". $sth->errstr
       unless defined($rv);
     die "Can't update usage for svcnum ". $self->svcnum