|
|
|
#!/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;
|