stop incrementing usage for CF accounts
[freeside.git] / FS / FS / svc_acct.pm
index d3e23f2..1584cce 100644 (file)
@@ -44,7 +44,6 @@ use FS::PagedSearch qw( psearch ); # XXX in v4, replace with FS::Cursor
 use FS::part_pkg;
 use FS::part_svc;
 use FS::svc_acct_pop;
-use FS::cust_main_invoice;
 use FS::svc_domain;
 use FS::svc_pbx;
 use FS::raddb;
@@ -68,12 +67,12 @@ FS::UID->install_callback( sub {
   @shells = $conf->config('shells');
   $usernamemin = $conf->config('usernamemin') || 2;
   $usernamemax = $conf->config('usernamemax');
-  $passwordmin = $conf->config('passwordmin'); # || 6;
-  #blank->6, keep 0
+  $passwordmin = $conf->config('passwordmin');
+  #blank->8, keep 0
   $passwordmin = ( defined($passwordmin) && $passwordmin =~ /\d+/ )
                    ? $passwordmin
-                   : 6;
-  $passwordmax = $conf->config('passwordmax') || 8;
+                   : 8;
+  $passwordmax = $conf->config('passwordmax') || 12;
   $username_letter = $conf->exists('username-letter');
   $username_letterfirst = $conf->exists('username-letterfirst');
   $username_noperiod = $conf->exists('username-noperiod');
@@ -94,12 +93,11 @@ FS::UID->install_callback( sub {
   $smtpmachine = $conf->config('smtpmachine');
   $radius_password = $conf->config('radius-password') || 'Password';
   $radius_ip = $conf->config('radius-ip') || 'Framed-IP-Address';
-  @pw_set = ( 'A'..'Z' ) if $conf->exists('password-generated-allcaps');
+  @pw_set = FS::svc_acct->pw_set;
 }
 );
 
 @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
-@pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '.', ',' );
 
 sub _cache {
   my $self = shift;
@@ -298,7 +296,7 @@ sub table_info {
                          disable_inventory => 1,
                        },
         '_password' => { label => 'Password',
-                         required => 1
+          #required => 1
                        },
         'gid'       => {
                          label    => 'GID',
@@ -318,6 +316,7 @@ sub table_info {
         'domsvc'    => {
                          label     => 'Domain',
                          type      => 'select',
+                         select_svc => 1,
                          select_table => 'svc_domain',
                          select_key   => 'svcnum',
                          select_label => 'domain',
@@ -330,6 +329,15 @@ sub table_info {
                          disable_select => 1, #UI wonky, pry works otherwise
                        },
         'sectornum' => 'Tower sector',
+        'routernum' => 'Router/block',
+        'blocknum'  => {
+                         'label' => 'Address block',
+                         'type'  => 'select',
+                         'select_table' => 'addr_block',
+                          'select_key'   => 'blocknum',
+                         'select_label' => 'cidr',
+                         'disable_inventory' => 1,
+                       },
         'usergroup' => {
                          label => 'RADIUS groups',
                          type  => 'select-radius_group.html',
@@ -711,23 +719,46 @@ sub insert {
         || $conf->exists('emailinvoiceauto')
         && ! $cust_main->invoicing_list_emailonly
        ) {
-      my @invoicing_list = $cust_main->invoicing_list;
-      push @invoicing_list, $self->email;
-      $cust_main->invoicing_list(\@invoicing_list);
-    }
 
-    #welcome email
-    my @welcome_exclude_svcparts = $conf->config('svc_acct_welcome_exclude');
-    unless ( grep { $_ eq $self->svcpart } @welcome_exclude_svcparts ) {
-        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);
-          #should this do something on error?
-        }
+      # slight false laziness w/ edit/process/cust_main.cgi...
+      # and also slightly arbitrary behavior.
+      #
+      # this will never happen but check it anyway
+      my ($contact) = map { $_->contact }
+        qsearch('contact_email', { emailaddress => $self->email });
+
+      if (!$contact) {
+        # if the "real name" of this account matches the first + last name
+        # of a contact, attach the email address to that person.
+        my @contacts = map { $_->contact } $cust_main->cust_contact;
+        my $myname = $self->get('finger');
+        my ($contact) =
+          grep { $_->get('first') . ' ' . $_->get('last') eq $myname } @contacts;
+        # otherwise just pick the first one
+        $contact = $contacts[0];
+      }
+      # if there is one
+      $contact ||= FS::contact->new({
+          'custnum'       => $cust_main->get('custnum'),
+          'locationnum'   => $cust_main->get('bill_locationnum'),
+          'last'          => $cust_main->get('last'),
+          'first'         => $cust_main->get('first'),
+      });
+      $contact->set('emailaddress', $self->email);
+      $contact->set('invoice_dest', 'Y');
+
+      if ( $contact->get('contactnum') ) {
+        $error = $contact->replace;
+      } else {
+        $error = $contact->insert;
+      }
+
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "creating invoice destination contact: $error";
+      }
     }
+
   } # if $cust_pkg
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -800,37 +831,32 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  foreach my $cust_main_invoice (
-    qsearch( 'cust_main_invoice', { 'dest' => $self->svcnum } )
+  foreach my $svc_domain (
+    qsearch( 'svc_domain', { 'catchall' => $self->svcnum } )
   ) {
-    unless ( defined($cust_main_invoice) ) {
-      warn "WARNING: something's wrong with qsearch";
-      next;
-    }
-    my %hash = $cust_main_invoice->hash;
-    $hash{'dest'} = $self->email;
-    my $new = new FS::cust_main_invoice \%hash;
-    my $error = $new->replace($cust_main_invoice);
+    my %hash = new FS::svc_domain->hash;
+    $hash{'catchall'} = '';
+    my $new = new FS::svc_domain \%hash;
+    my $error = $new->replace($svc_domain);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return $error;
     }
   }
 
-  foreach my $svc_domain (
-    qsearch( 'svc_domain', { 'catchall' => $self->svcnum } )
+  foreach my $svc_phone (
+    qsearch( 'svc_phone', { 'forward_svcnum' => $self->svcnum })
   ) {
-    my %hash = new FS::svc_domain->hash;
-    $hash{'catchall'} = '';
-    my $new = new FS::svc_domain \%hash;
-    my $error = $new->replace($svc_domain);
+    $svc_phone->set('forward_svcnum', '');
+    my $error = $svc_phone->replace;
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return $error;
     }
   }
 
-  my $error = $self->SUPER::delete; # usergroup here
+  my $error = $self->delete_password_history
+           || $self->SUPER::delete; # usergroup here
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
@@ -1306,8 +1332,7 @@ sub check {
       $recref->{_password} = $1;
     } else {
       return gettext('illegal_password'). " $passwordmin-$passwordmax ".
-             FS::Msgcat::_gettext('illegal_password_characters').
-             ": ". $recref->{_password};
+             FS::Msgcat::_gettext('illegal_password_characters');
     }
 
     if ( $password_noampersand ) {
@@ -1955,6 +1980,9 @@ sub _op_usage {
 
   return '' unless $amount;
 
+  return ''
+    if $self->cust_svc->part_svc->part_svc_column($column)->columnflag eq 'F';
+
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
   local $SIG{QUIT} = 'IGNORE';
@@ -2290,8 +2318,8 @@ sub last_login_text {
 
 Returns a paged search (L<FS::PagedSearch>) for Call Detail Records
 associated with this service. For svc_acct, "associated with" means that
-either the "src" or the "charged_party" field of the CDR matches the
-"username" field of the service.
+either the "src" or the "charged_party" field of the CDR matches either
+the "username" field of the service or the username@domain label.
 
 =cut
 
@@ -2302,6 +2330,7 @@ sub psearch_cdrs {
   my @where;
 
   my $did = dbh->quote($self->username);
+  my $diddomain = dbh->quote($self->label);
 
   my $prefix = $options{'default_prefix'} || ''; #convergent.au '+61'
   my $prefixdid = dbh->quote($prefix . $self->username);
@@ -2317,12 +2346,16 @@ sub psearch_cdrs {
   if (!$options{'disable_charged_party'}) {
     push @orwhere,
       "charged_party = $did",
-      "charged_party = $prefixdid";
+      "charged_party = $prefixdid",
+      "charged_party = $diddomain"
+      ;
   }
   if (!$options{'disable_src'}) {
     push @orwhere,
       "src = $did AND charged_party IS NULL",
-      "src = $prefixdid AND charged_party IS NULL";
+      "src = $prefixdid AND charged_party IS NULL",
+      "src = $diddomain AND charged_party IS NULL"
+      ;
   }
   push @where, '(' . join(' OR ', @orwhere) . ')';
 
@@ -2521,6 +2554,11 @@ sub crypt_password {
       );
     } elsif ( $encryption eq 'md5' ) {
       return unix_md5_crypt( $self->_password );
+    } elsif ( $encryption eq 'sha512' ) {
+      return crypt(
+        $self->_password,
+        '$6$rounds=15420$'. join('', map $saltset[int(rand(64))], (1..16) )
+      );
     } elsif ( $encryption eq 'sha1_base64' ) { #for acct_sql
       my $pass = sha1_base64( $self->_password );
       $pass .= '=' x (4 - length($pass) % 4); #properly padded base64
@@ -2666,6 +2704,25 @@ sub virtual_maildir {
   $self->domain. '/maildirs/'. $self->username. '/';
 }
 
+=item password_svc_check
+
+Override, for L<FS::Password_Mixin>.  Not really intended for other use.
+
+=cut
+
+sub password_svc_check {
+  my ($self, $password) = @_;
+  foreach my $field ( qw(username finger) ) {
+    foreach my $word (split(/\W+/,$self->get($field))) {
+      next unless length($word) > 2;
+      if ($password =~ /$word/i) {
+        return qq(Password contains account information '$word');
+      }
+    }
+  }
+  return '';
+}
+
 =back
 
 =head1 CLASS METHODS