#!/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 $warn_users = $service->prop('WarnUsers') || 'no'; my $recipient = $service->prop('WarningRecipient') || 'root'; my $days_between_warn = $service->prop('DaysBetweenWarnings') || '5'; my $archive_compress = $service->prop('ArchiveCompression') || '/usr/bin/pxz'; 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/bz2$/ ){ $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); 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 - $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); } 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$/){ my $tar = $archive_path . '/' . $u . '-' . $now->ymd . '.' . $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"; } } } # 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'; 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 - $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_users){ 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;