Home » Mail Server » Birthday Reminders from your Linux Server
Classfactory.com
Tech Hacks and Solutions
Birthday Reminders from your Linux Server
Perl script sending birthday reminder emails from you Linux server using an Excel CSV file as birthday database.
Virtual Server Basic Setup for Email Service
Picking an operating system, dual IPv4 and IPv6 network interfaces, hostname and reverse DNS record for virtual server email setup.
Restore Hostname During Reboot
A custom system service to restore hostname settings during reboot on virtual servers overwriting network configuration files.
Postfix With Courier IMAP Mail Server Installation
Install and configure Postfix mail server with Courier IMAP and Maildir storage on Debian 11 or Ubuntu 20.04.
Add a Catch-All Account with Postfix-Mysql
How to implement a catch-all account with Postfix-Mysql receiving emails to unused aliases without entry in users or aliases tables.
Maildrop Spam Filtering For Postfix MySQL
How to use Courier-Maildrop or compatible tools for spam filtering and mail folder administration with Postfix-MySQL.

Birthday Reminders from your Linux Server

You keep forgetting about birthdays of people important to you? A simple Perl script running on your Linux server solves the problem sending birthday reminder mails to your email account.

Birthday Reminders from your Linux Server
Birthday reminders from your mail server generated by Perl script.

Prerequisite: your server has a mailer daemon running with sendmail interface.

[email protected]> sudo bash
[email protected]# which sendmail
/usr/sbin/sendmail

If your server doesn’t yet have a mailer daemon, you can install one with this command:

[email protected]# apt-get install sendmail

There is no need to open SMTP port 25 for incoming connections, but unless the script sends reminders to an account on the same server, your server needs to be able to connect to remote hosts on port 25. This can be verified with port scanners like nmap:

[email protected]# nmap -sT -p 25 remotehost.com

Birthday Reminder Script

Simply create a text or Excel CSV file with names and birthdays in the following line format under your home directory, where ‘user’ is your Unix user name:

[email protected]:/home/user/birthdays# cat bday.csv 
Justin Acquaintance,9-jan
Mark Mustermann,10-jan-1934
Sonia Goodfriend,16-jan-1998

So the birthday data has names separated from dates by a comma and accepts either full birth dates in ‘dd-mon-yyyy’ format or, for acquaintances, where we may only day and month of birth, a shortened ‘dd-mon’.

Under your home directory, create a folder ‘birthdays’ with the birthday data ‘bday.csv’ and a perl script ‘reminder.pl’ with the following content:

#!/usr/bin/perl

use strict;
use FindBin;
use Date::Parse;

my $ldir = "$FindBin::Bin";
my $mailx = '/usr/sbin/sendmail';
my $date = '/bin/date';
my $hostname = '/bin/hostname';
-x $mailx or die "Cannot execute $mailx";
-x $date or die "Cannot execute $date";

#Setting the initial variables

my $IFILE="$ldir/bday.csv";
my $mailerhost = `$hostname`;
$mailerhost =~ s/\s*$//;
my $MAILFROM="birthdays\@$mailerhost";
my $MAILID='[email protected]';

my $ADAY=60*60*24;
my $AWEEK=$ADAY*7;

# Retrieving today's date and month

my $dat = dateStr();
# Get time truncated to date 0:00:00
my $dt = str2time($dat);

# Reading the bday.csv file and finding the users whose 
# date matches with today and writing to OFILE
my $bdfil;
my $reminded = 0;
open($bdfil, "<", $IFILE) or die "Cannot open $IFILE for read";
while (<$bdfil>) {
        if (/^\s*([^\#\s][^,]*),\s*([\w\-\/]+)/) {
                my ($name, $bday) = ($1,$2);
                $name = trim($name);
                my ($day,$mon,$year) = split(/[\-\/]/, $bday);
                if (!defined($day) || !defined($mon)) {
                        print "Undefined day/month in $_\n";
                        next;
                }
                $day = trim($day);
                $mon = uc(trim($mon));
                $year = trim($year) if defined($year);
                # Get time of birthday, years defaulting to present!
                my $bddt = str2time("$day-$mon");
                my $bddtdiff = $bddt - $dt;
                if ($bddtdiff < 0) {
                        # Birthday is next year!
                        $bddt = str2time("[email protected]{[yearOfTime($dt)+1]}");
                        $bddtdiff = $bddt - $dt;
                }
                #print "$name $bday $bddtdiff\n";
                my $headline = "Birthday Reminder for $name: ";
                my $body = "This is a reminder for an upcoming birthday: $name born at @{[uc($bday)]}\n";
                my $subject;
                my $age = '';
                if ($year) {$age = (yearOfTime($bddt) - $year) . " years"; }
                if ($bddtdiff == 0) {
                        $subject = $headline . "$age TODAY";
                } elsif ($bddtdiff == $ADAY) {
                        $subject = $headline . "$age TOMORROW";
                } elsif ($bddtdiff == $AWEEK) {
                        $subject = $headline . "$age next week";
                }
                if ($subject) {
                        $reminded = 1;
                        my $rdate =trim(`$date`);
                        print "Remind: $rdate $subject\n$body\n";
                        my $pip;
                        open $pip, "|-", $mailx, "-t"
                            or die "Cannot pipe to $mailx";
		    	print $pip "To: $MAILID\n";
			print $pip "From: $MAILFROM\n"; 
			print $pip "Subject: $subject\n";
			print $pip "Content-type: text/plain\n\n";
                        print $pip $body;
                        close $pip;
                }
        }
}
if (!$reminded) {
        print trim(`$date`) . ": no reminders\n";
}

close $bdfil;

exit 0;

sub trim($) {
        my $string = shift;
        $string =~ s/^\s+//;
        $string =~ s/\s+$//;
        return $string;
}

sub yearOfTime($) {
    my ($sec,$min,$hour,$mday,$mon,$year) = gmtime(shift);
    return $year + 1900;
}

sub dateStr() {
    my ($sec,$min,$hour,$mday,$mon,$year) = gmtime();
    my @month=qw(jan feb mar apr may jun jul aug sep oct nov dec);
    return "[email protected]{[ucfirst($month[$mon])]}[email protected]{[$year+1900]}"; 
}

The script ‘reminder.pl’ will generate a reminder email a week ahead for people with full ‘dd-mon-yyyy’ birthdays. For everyone and short ‘dd-mon’ birthdays there will be reminders a day before and on the birthday.

Finally, add a script ‘birthday’ under /etc/cron.daily:

[email protected]:/etc/cron.daily# cat birthday 
#!/bin/sh

/bin/su user /bin/sh -c "/home/user/birthdays/reminder.pl >> /home/user/birthdays/reminder.log 2>&1"

The server will now run the birthday reminder script once per day. Note we change users from root to regular user for security reasons.

Don’t forget to set executable flags on scripts:

[email protected]:/home/user/birthdays> chmod a+x reminder.pl
[email protected]:/etc/cron.daily# chmod a+x birthday

You’ll need to change the Unix user name from “user” to your account name under cron.daily and replace “[email protected]” with your email address in reminder.pl. That’s it. You should now be getting birthday reminders. For testing, manually execute the ‘birthday’ script under cron.daily. You’ll also want to add test birthday lines in ‘bday.csv’ for whenever you’ll be testing the scripts.

The complete sources are available here: birthdays.tgz

References

Sending email with Perl using the sendmail utility: https://www.tutorialspoint.com/perl/perl_sending_email.htm