#!/usr/bin/perl

use Net::LDAP;
use Carp;
use DateTime;
use Data::Dumper;
use Mail::Sender;

my $ldapserver = 'ldaps://ldap.mydomain.com';
my $basedn =   'dc=mydomain,dc=com';
my $defaultpolicy = "cn=sysadmins-policy,$basedn";
my ($entry,$warnage);
my %policy;

my $ldap = Net::LDAP->new($ldapserver);
my $mesg;

#We can do anonymous bind here for now ...
$mesg = $ldap->bind;
$mesg->code && croak $mesg->error;

#2)Find all password policies
$mesg = $ldap->search(	base => $basedn,
		filter => '(objectclass=pwdPolicy)',
		attrs => ['cn','pwdMaxAge','pwdExpireWarning'],
	);
$mesg->code && warn $mesg->error;
foreach $entry ($mesg->entries) {
	print {*STDERR} "Found password policy " . $entry->dn . " with max age " . $entry->get_value('pwdMaxAge') . "\n";
	$warnage = $entry->get_value('pwdMaxAge') - $entry->get_value('pwdExpireWarning');
	my $policyname = $entry->get_value('cn');
	my $policydn = $entry->dn;
	if ($warnage gt 0) {
		#$policy{$entry->dn} = $warnage;
	} else {
		#print "Policy $policydn has no warning time set, or no expiry\n";
		next;
	}
	$expireage = $entry->get_value('pwdMaxAge');
	$warnage = $entry->get_value('pwdMaxAge') - $entry->get_value('pwdExpireWarning');
	my $filterwarn = gen_filter($warnage);
	my $filterexpire = gen_filter($expireage);
	my $filter;
	if ( $entry->dn eq $defaultpolicy ) {
		$filter = "(&(|(!(pwdPolicySubEntry=*))(pwdPolicySubEntry=$defaultpolicy))(pwdChangedTime<=$filterwarn)(pwdChangedTime>=$filterexpire))";
	} else {
		$filter = "(&(pwdPolicySubEntry=$policydn)(pwdChangedTime<=$filterwarn)(pwdChangedTime>=$filterexpire))"
	}
	#print "Searching for users about to expire for policy $policyname\n";

	$mesg = $ldap->search(	base => $basedn,
		filter => $filter,
		attrs => ['cn', 'uid', 'mail', 'pwdChangedTime'],
	);
	$mesg->code && warn $mesg->error;
	@entries = $mesg->entries;
	foreach $entry (@entries) {
		my $changedtime = $entry->get_value('pwdChangedTime');
		my $willexpire=$filterexpire;
		#$changed="20090123100521Z"; 
		if ($changedtime =~ m/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\w+)$/) {
			#print "Got time $1 - $2 - $3 $4 : $5 : $6 $7\n"; 
			$willexpire=DateTime->new(year=>$1,month=>$2,day=>$3,hour=>$4,minute=>$5,second=>$6,time_zone=>$7);
			$willexpire->add(seconds=>$expireage);
		}
		print "User " . $entry->get_value('uid') . "(" . $entry->get_value('cn') . ") with email " . $entry->get_value('mail') . " has an expired password\n";
		notify_user($entry->get_value('uid'),$entry->get_value('cn'),$entry->get_value('mail')||"",$policyname,$willexpire);

	}
}

$ldap->unbind if (defined $ldap);


sub gen_filter {
	my ($warningtime) = @_;
	my $dt = DateTime->now;
	$dt->subtract(seconds => $warningtime);
	my $filtertime = $dt->strftime('%Y%m%d%H%M%SZ');
	return $filtertime;
}

sub notify_user {
	my ($username,$fullname,$email,$policyname,$expiretime) = @_;
	#$expiretime =~ s/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z/$1-$2-$3 $4h$5:$6 (UTC)/;
	$expiretime->set_time_zone('Africa/Johannesburg');
	my $expiretimetext = $expiretime->strftime("%Y-%m-%d %H:%M:%S (%Z)");
	if ($ENV{'DEBUG'}) {
		print "Warning user $username ($fullname) at $email according to policy $policyname expiring at $expiretimetext\n";
		return 0;
	} else {
	my $mail = new Mail::Sender {
		smtp => 'smtp.mydomain.com',
		from => 'Authentication team <root@mydomain.com>',
		to => ($email ne "") ? "$fullname <$email>" : "Password admin <root\@mydomain.com>",
		subject => 'Your password is about to expire',
	} or warn "Creating mail failed";
	$mail->Open or warn "Opening mail failed";
	$mail->SendLineEnc(
		"Dear $fullname,\n",
		"Your password will expire at $expiretimetext.\n",
		"Please change your password at one of the following locations:\n",
		"https://staff.mydomain.com/cgi-bin/password.cgi\n",
		"https://hobbit.mydomain.com/cgi-bin/password.cgi\n",
		"https://svn.mydomain.com/cgi-bin/password.cgi\n", 
		"https://spam.mydomain.com/cgi-bin/password.cgi\n",
	) or warn "Adding content to mail failed";
	$mail->Close or warn "Sending mail failed";
	}
}
