#!/usr/bin/perl -W
#-------------------------------------------------------------------------------
# nag-reminder.pl                                (C) 2006 T.Birnthaler OSTC GmbH
# $Id: nag-reminder.pl,v 1.5 2006-05-17 15:32:03 tsbirn Exp $
#-------------------------------------------------------------------------------
# Horde-Aufgaben überwachen und bei Ablauf der Warnzeit eine Erinnerungs-Mail
# schicken (nur 1x!). Dieser Vorgang wird in der Aufgaben-Beschreibung vermerkt,
# indem der minimale(!) Erledigungs-Zeitpunkt dort eingetragen wird.
#
# Ändert der Benutzer die Erledigungs-Zeitpunkt ab, wird dies erkannt, die
# Erinnerungs-Mail erneut verschickt und die Aufgaben-Beschreibung angepasst.
#
# Als externes Programm nur durch Zugriff auf die entsprechenden DB-Tabellen
# realisiert, da der Einbau in die Horde-NAG-Infrastruktur zu komplex erschien.
#
# Getestet mit Horde-3.1.1 und NAG-H3-2.1.
#-------------------------------------------------------------------------------
# Tabelle "horde.nag_tasks":        +--- Für SELECT/UPDATE notwendig
# +----------------+--------------+ | +--- Für Mail-Versand notwendig
# | Field          | Type         | | | +--- Für Mail-Inhalt notwendig
# +----------------+--------------+ | | |
# | task_id        | varchar(32)  | * - - Aufgaben-ID (Zugriffsschlüssel)
# | task_owner     | varchar(255) | * * - Aufgaben-Besitzername
# | task_name      | varchar(64)  | * * * Aufgaben-Name
# | task_uid       | varchar(255) | - - - User-ID
# | task_desc      | text         | * * * Aufgaben-Beschreibung
# | task_due       | int(11)      | * * * Erledigungszeitpunkt UNIX-Timestamp
# | task_priority  | int(11)      | * * * Aufgaben-Priorität
# | task_category  | varchar(80)  | * * * Aufgaben-Kategorie
# | task_completed | smallint(6)  | * - - Erledigt
# | task_alarm     | int(11)      | * * - Vorwarnzeit in Minuten
# | task_private   | int(11)      | - - - Privat (???)
# +----------------+--------------+
#-------------------------------------------------------------------------------
use strict;

# Module
use DBI;
use Mail::Mailer;

# Produktion oder Debugging (Schalter "-p")
my $dbg  = 1;
my $cond = "AND task_name LIKE '%xyz%'" if ($dbg);
if (@ARGV > 0 and $ARGV[0] eq "-p")
{
	$dbg  = 0;
	$cond = "";
}

# Datenbank-Anmeldedaten
my $dbhost   = "127.0.0.1";
my $dbport   = "3306";
my $dbname   = "horde";
my $tblname  = "nag_tasks";
my $username = "USER";
my $password = "GEHEIM";

# Aktueller Zeitpunkt
my $time = time();
print "TIME: $time\n" if ($dbg);

# SELECT-Statement für noch nicht erledigte Aufgaben im richtigen Zeitraum
my $stm = "
	SELECT task_id,
	       task_owner,
	       task_name,
	       task_desc,
	       task_due,
	       task_priority,
	       task_category,
	       task_alarm
	FROM ${tblname}
	WHERE task_completed = 0
		AND $time >= task_due - task_alarm * 60
		AND $time < task_due
		$cond
";

# Datenbank-Verbindung öffnen
#;host=127.0.0.1;port=3306";
my $db = DBI->connect("dbi:mysql:database=$dbname;host=$dbhost;port=$dbport", $username, $password)
	or die(DBI::errstr);

# Passende nicht erledigte Aufgaben aus Datenbank abfragen
print "SELECT: $stm\n" if ($dbg);
my $sql = $db->prepare($stm) or die $db->errstr;
$sql->execute() or die $db->errstr;

# Passende nicht erledigte Aufgaben von Datenbank holen
my @todo;
while (my $row = $sql->fetchrow_hashref)
{
	my $id       = $row->{'task_id'};
	my $owner    = $row->{'task_owner'};
	my $name     = $row->{'task_name'};
	my $desc     = $row->{'task_desc'};
	my $due      = $row->{'task_due'};
	my $priority = $row->{'task_priority'};
	my $category = $row->{'task_category'};
	my $alarm    = $row->{'task_alarm'};

	printf "FOUND: $id, $owner, $name, $priority, $category, $desc\n" if ($dbg);
	print "due: $due, alarm: $alarm, time: $time,"    if ($dbg);
	print " diff1: ", $due - $time - $alarm * 60, "," if ($dbg);
	print " diff2: ", $due - $time, "\n"              if ($dbg);

	# Alarm schon mal ausgelöst?
	# (= Text "ERINNERUNG VERSANDT (TIMESTAMP)" am Beschreibungsende)
	if ($desc =~ /ERINNERUNG VERSANDT/)
	{
		# Sendezeit der alten Mail vor aktueller minimaler Alarmzeit?
		# -> Nochmal senden (Text vorher aus Beschreibung entfernen!)
		my ($sentdate) = ($desc =~ /ERINNERUNG VERSANDT \(([0-9]+)\)/);
		print "SENTDATE: $1\n" if ($dbg);
		next if ($sentdate >= $due - $alarm * 60);

		# Hilfs-Text aus Beschreibung entfernen
		$desc =~ s/\n*ERINNERUNG VERSANDT.*$//s;
		print "NEW-DESC: $desc" if ($dbg);
	}

	# Merken, erst nach Abarbeitung des SELECT zugehörige Mails versenden
	print "TODO ($id, $owner, $name, $due, $priority, $category, $alarm, $desc)\n" if ($dbg);
	push @todo, [ $id, $owner, $name, $desc, $due, $priority, $category, $alarm ];
}

# Mail für gefundene Aufgaben verschicken (und in DB eintragen)
&HandleFoundTasks(@todo);

# Datenbank-Verbindung schließen
$db->disconnect();

#-------------------------------------------------------------------------------
# Für gefundene zeitlich passende Aufgaben Erinnerungsmail verschicken (nur 1x!)
# und in der Aufgaben-Beschreibung vermerken (WICHTIG: min. Erinnerungszeit!).
# 1-N: Task-Liste mit Array-Referenzen auf (id, owner, name, due, alarm, desc)
#-------------------------------------------------------------------------------
sub HandleFoundTasks
{
	foreach (@_)
	{
		my ($id, $owner, $name, $desc, $due, $priority, $category, $alarm) = @$_;
		my $minimum = $due - $alarm * 60;

		# Erinnerungsmail in Beschreibung vermerken
		my $stm = "
			UPDATE ${tblname}
			SET task_desc = '$desc\n\nERINNERUNG VERSANDT ($minimum)'
			WHERE task_id = '$id'
		";
		print "SQL: $stm\n" if ($dbg);
		$sql = $db->prepare($stm);
		$sql->execute() or die $db->errstr;

		SendMail($owner, $name, $desc, $due, $priority, $category, $alarm);
	}
}

#-------------------------------------------------------------------------------
# Erinnerungs-Mail bzgl. zu erledigender Aufgabe verschicken.
# 1: Wer (Text)
# 2: Aufgaben-Name (Text)
# 3: Aufgaben-Beschreibung (Text)
# 4: Zieldatum (UNIX-Timestamp)
# 5: Priorität (1-5)
# 6: Kategorie (Text)
# 7: Alarm-Zeitraum vorher (Minuten)
#-------------------------------------------------------------------------------
sub SendMail
{
	my ($owner, $name, $desc, $due, $priority, $category, $alarm) = @_;

	print "SendMail($owner, $name, $desc, $due, $priority, $category, $alarm)\n" if ($dbg);

	# Due-Timestamp -> Ziel-Zeitpunkt
	my ($sec, $min, $hour, $day, $mon, $year) = localtime($due);
	$year += 1900;
	$mon  += 1;
	my $datetime = sprintf("%d.%d.%d %d:%02d", $day, $mon, $year, $hour, $min);

	# Mail verschicken
	my $mailer = new Mail::Mailer;
	$mailer->open({
		"From"    => "horde@ostc.de",
		"To"      => $owner,
		"Subject" => "Erinnerung: Aufgabe '$name' ($datetime, $priority, $category)!",
	});
	print $mailer $desc;  # desc = Body
	$mailer->close;
}
