218 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/perl -w
 | 
						|
 | 
						|
use esmith::AccountsDB;
 | 
						|
use esmith::ConfigDB;
 | 
						|
use esmith::event;
 | 
						|
use Text::Template;
 | 
						|
use DateTime;
 | 
						|
use User::pwent;
 | 
						|
use Getopt::Long;
 | 
						|
 | 
						|
my $a = esmith::AccountsDB->open || die "Couldn't open the AccountsDB";
 | 
						|
my $c = esmith::ConfigDB->open   || die "Couldn't open the ConfigDB";
 | 
						|
my $domain = $c->get_value('DomainName');
 | 
						|
 | 
						|
my $debug   = 0;
 | 
						|
 | 
						|
GetOptions(
 | 
						|
  debug    => \$debug,
 | 
						|
);
 | 
						|
 | 
						|
 | 
						|
my $service = $c->get('ExpireAccounts') ||
 | 
						|
  $c->new_record('ExpireAccounts',
 | 
						|
                   {
 | 
						|
                     type   => 'service',
 | 
						|
                     status => 'enabled'
 | 
						|
                   }
 | 
						|
  );
 | 
						|
 | 
						|
my $warn_delay        = $service->prop('WarningDelay')        || '30';
 | 
						|
my $recipient         = $service->prop('WarningRecipient')    || 'root';
 | 
						|
my $days_between_warn = $service->prop('DaysBetweenWarnings') || '5';
 | 
						|
my $archive_compress  = $service->prop('ArchiveCompression')  || '/usr/bin/xz';
 | 
						|
my $archive_path      = $service->prop('ArchivePath')         || '/home/e-smith/files/archives/users';
 | 
						|
my $archive_ext       = 'xz';
 | 
						|
 | 
						|
if ( !-x $archive_compress ){
 | 
						|
  print "$archive_compress not found, switching back to the default /bin/gzip\n" if ($debug);
 | 
						|
  $archive_compress = '/bin/gzip';
 | 
						|
}
 | 
						|
 | 
						|
if ( $archive_compress =~ m/xz$/ ){
 | 
						|
  $archive_ext = 'xz';
 | 
						|
}
 | 
						|
elsif ( $archive_compress =~ m/bzip2$/ ){
 | 
						|
  $archive_ext = 'bz2';
 | 
						|
}
 | 
						|
elsif ( $archive_compress =~ m/gzip$/ ){
 | 
						|
  $archive_ext = 'gz';
 | 
						|
}
 | 
						|
else{
 | 
						|
  print "$archive_compress creates unknown archive format, using a generic .z extension\n" if ($debug);
 | 
						|
  $archive_ext = 'z';
 | 
						|
}
 | 
						|
 | 
						|
my $rem    = {};
 | 
						|
my @lock   = ();
 | 
						|
my @delete = ();
 | 
						|
 | 
						|
my $now = DateTime->now;
 | 
						|
 | 
						|
foreach my $user ($a->users){
 | 
						|
  my $pass = $user->prop('PasswordSet') || 'no';
 | 
						|
  if ( $pass ne 'yes' ){
 | 
						|
    print $user->key . ": Account already locked\n" if $debug;
 | 
						|
    my $delete      = $user->prop('ExpireDeleteAfterLock') || 'never';
 | 
						|
    my $locked_date = $user->prop('ExpireLockedOn') || '';
 | 
						|
    if ($delete =~ m/^\d+$/ && $locked_date =~ m/^(\d{4})\-(\d{1,2})\-(\d{1,2})$/){
 | 
						|
      my $locked = DateTime->new(
 | 
						|
        year  => $1,
 | 
						|
        month => $2,
 | 
						|
        day   => $3,
 | 
						|
      );
 | 
						|
      my $delete_date = $locked->add( days => $delete );
 | 
						|
      if ( $delete_date < $now ){
 | 
						|
        print $user->key . ": Account must be deleted\n" if $debug;
 | 
						|
        push @delete, $user->key;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    next;
 | 
						|
  }
 | 
						|
  my $lock_date = $user->prop('ExpireLockOn') || 'never';
 | 
						|
  if ( $lock_date eq 'never' ){
 | 
						|
    print $user->key . ": No expiration date set\n" if $debug;
 | 
						|
    next;
 | 
						|
  }
 | 
						|
  elsif ( $lock_date !~ m/^(\d{4})\-(\d{1,2})\-(\d{1,2})$/ ){
 | 
						|
    print $user->key . ": Invalide expiration date ($lock_date)\n" if $debug;
 | 
						|
    next;
 | 
						|
  }
 | 
						|
  my $exp = DateTime->new(
 | 
						|
    year  => $1,
 | 
						|
    month => $2,
 | 
						|
    day   => $3,
 | 
						|
  );
 | 
						|
 | 
						|
  if ( $exp < $now ){
 | 
						|
    print $user->key . ": Expiration date is passed, account must be locked\n" if $debug;
 | 
						|
    push @lock, $user->key;
 | 
						|
  }
 | 
						|
  else{
 | 
						|
    my $remaining = $exp->delta_days($now)->in_units('days');
 | 
						|
    print $user->key . ": Account will expire on $lock_date ($remaining days)\n" if $debug;
 | 
						|
    $rem->{$user->key} = $remaining if $remaining < $warn_delay;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
# Lock accounts which need to be locked now
 | 
						|
foreach my $u (@lock){
 | 
						|
  event_signal('user-expire-account', $u);
 | 
						|
}
 | 
						|
 | 
						|
# Accounts to be deleted
 | 
						|
foreach my $u (@delete){
 | 
						|
  my $user = $a->get($u);
 | 
						|
  if ( !$user || !$user->prop('type') || $user->prop('type') ne 'user' ){
 | 
						|
    print $u . ": This is not a user account\n";
 | 
						|
    next;
 | 
						|
  }
 | 
						|
  unless( -d $archive_path ){
 | 
						|
    print $u . ": $archive_path doesn't exist, can't continue\n" if ($debug);
 | 
						|
    next;
 | 
						|
  }
 | 
						|
  my $archive = $user->prop('ExpireArchiveBeforeDelete') || 'yes';
 | 
						|
  if ( $archive =~ m/^yes|enabled|1|on$/ ){
 | 
						|
    # Suspend email delivery, prevent tar ending with exit code 1
 | 
						|
    # because Maildir changed during archive
 | 
						|
    system('chmod', '+t', getpwnam($u)->dir);
 | 
						|
    my $tar = $archive_path . '/' . $u . '-' . $now->ymd . '.tar.' . $archive_ext;
 | 
						|
    my $res = system(
 | 
						|
      'tar',
 | 
						|
      'cf',
 | 
						|
      $tar,
 | 
						|
      '--use-compress-program',
 | 
						|
      $archive_compress,
 | 
						|
      '-C',
 | 
						|
      getpwnam($u)->dir,
 | 
						|
      getpwnam($u)->dir
 | 
						|
    );
 | 
						|
    if ( $res == 0 ){
 | 
						|
      print $u . ": Data archived as $tar. The user account will now be deleted\n" if ($debug);
 | 
						|
      $user->set_prop( type => 'user-deleted' );
 | 
						|
      event_signal('user-delete', $u);
 | 
						|
      $a = esmith::AccountsDB->open || die "Couldn't open AccountsDB\n";
 | 
						|
      $a->get($u)->delete;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
# Send a warning for accounts which will be locked soon
 | 
						|
my $send_warn_for = {};
 | 
						|
foreach my $user (keys %$rem){
 | 
						|
  my $rec        = $a->get($user)                     || next;
 | 
						|
  my $last_notif = $rec->prop('ExpireLastNotifiedOn') || 'never';
 | 
						|
  my $warn_user  = $rec->prop('ExpireWarnUser')       || 'disabled';
 | 
						|
  if ( $last_notif =~ m/^(\d{4})\-(\d{1,2})\-(\d{1,2})/ ){
 | 
						|
    $last_notif = DateTime->new(
 | 
						|
      year  => $1,
 | 
						|
      month => $2,
 | 
						|
      day   => $3,
 | 
						|
    );
 | 
						|
    if ( $last_notif->delta_days($now)->in_units('days') < $days_between_warn ){
 | 
						|
      next;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  my $templates = '/etc/e-smith/templates';
 | 
						|
  my $source = '/usr/lib/smeserver-expire-accounts/userNotifAccountExpiration.tmpl';
 | 
						|
  # Use templates-custom version by preference if it exists
 | 
						|
  -f "${templates}-custom${source}" and $templates .= "-custom";
 | 
						|
 | 
						|
  if ( $warn_user =~ m/^enabled|yes|on|1$/ ){
 | 
						|
    my $t = new Text::Template(TYPE   => 'FILE',
 | 
						|
                             SOURCE => "${templates}${source}");
 | 
						|
 | 
						|
    open(QMAIL, "|/var/qmail/bin/qmail-inject -fdo-not-reply\@$domain $user")
 | 
						|
        || die "Could not send mail via qmail-inject!\n";
 | 
						|
 | 
						|
    print QMAIL $t->fill_in( HASH => {
 | 
						|
      conf     => \$c,
 | 
						|
      user     => $user,
 | 
						|
      days     => $rem->{$user}
 | 
						|
    });
 | 
						|
 | 
						|
    close QMAIL;
 | 
						|
  }
 | 
						|
 | 
						|
  $rec->set_prop('ExpireLastNotifiedOn', $now->ymd);
 | 
						|
  # This one will be notified to the admin
 | 
						|
  $send_warn_for->{$user} = {
 | 
						|
    remaining => $rem->{$user},
 | 
						|
    name      => $rec->prop('FirstName') . ' ' . $rec->prop('LastName')
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
if (keys %$send_warn_for > 0){
 | 
						|
  $templates = '/etc/e-smith/templates';
 | 
						|
  $source = '/usr/lib/smeserver-expire-accounts/adminNotifNextExpirations.tmpl';
 | 
						|
 | 
						|
  # Use templates-custom version by preference if it exists
 | 
						|
  -f "${templates}-custom${source}" and $templates .= "-custom";
 | 
						|
 | 
						|
  my $t = new Text::Template(TYPE   => 'FILE',
 | 
						|
                             SOURCE => "${templates}${source}");
 | 
						|
 | 
						|
  open(QMAIL, "|/var/qmail/bin/qmail-inject -fdo-not-reply\@$domain $recipient")
 | 
						|
      || die "Could not send mail via qmail-inject!\n";
 | 
						|
 | 
						|
  print QMAIL $t->fill_in( HASH => {
 | 
						|
    conf    => \$c,
 | 
						|
    users   => \$send_warn_for
 | 
						|
  });
 | 
						|
 | 
						|
  close QMAIL;
 | 
						|
}
 | 
						|
 | 
						|
exit 0;
 |