#!/usr/bin/perl
#
# LMS version 1.5.0 Marduk
#
#  (C) 2001-2004 LMS Developers
#
#  Please, see the doc/AUTHORS for more information about authors!
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License Version 2 as
#  published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
#  USA.
#
#  $Id: lms-rtparser,v 1.32 2004/09/18 19:21:10 alec Exp $

use strict;
use DBI;
use Config::IniFiles;
use Getopt::Long;
use vars qw($configfile $help $version $queue $debug);
use POSIX qw(strftime);
use File::Copy;
use MIME::Parser;
#use Data::Dumper;
use Sys::Hostname;

$ENV{'PATH'}='/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin';

sub basename($) { my @tmp = split '/',$_[0]; return pop @tmp }

my $_version = '1.5.0 Marduk';

my %options = (
	"--config-file|C=s"     =>      \$configfile,
	"--help|h"              =>      \$help,
	"--version|v"           =>      \$version,
	"--debug|d"             =>      \$debug,
	"--queue|q=s"		=>	\$queue
);

Getopt::Long::config("no_ignore_case");
GetOptions(%options);

if($help)
{
	print STDERR <<EOF;
lms-rtparser, version $_version
(C) 2001-2004 LMS Developers

-C, --config-file=/etc/lms/lms.ini
		alternate config file (default: /etc/lms/lms.ini);
-h, --help	print this help and exit;
-v, --version	print version info and exit;
-d, --debug	print out debug information, do not log any messages into
		system;
-q, --queue	queue ID (it means, QUEUE ID, numeric! NOT NAME! also
		it's required to run!)
				
EOF
	exit 0;
}							

if($version)
{
	print STDERR <<EOF;
lms-rtparser, version $_version
(C) 2001-2004 LMS Developers

EOF
	exit 0;
}

if(!$configfile)
{
	$configfile = "/etc/lms/lms.ini";
}

my $hostname = hostname || 'example.com';

my $ini = new Config::IniFiles -file => $configfile;
print @Config::IniFiles::errors;

my $dbtype = $ini->val('database', 'type') || 'mysql';
my $dbhost = $ini->val('database', 'host') || 'localhost';
my $dbuser = $ini->val('database', 'user') || 'root';
my $dbpasswd = $ini->val('database', 'password') || '';
my $dbname = $ini->val('database', 'database') || 'lms';
my $sendmail = $ini->val('rt', 'sendmail_binary') || '/usr/sbin/sendmail';
my $save_path = $ini->val('rt', 'mail_dir') || '';
my $queue = $queue || $ini->val('rt', 'default_queue');
my $autoreply_from = $ini->val('rt', 'mail_from') || '';

# very ugly code, but we really need tempdir..
my $tmpdir = (defined $ini->val('rt', 'tmp_dir') && ! ($ini->val('rt', 'tmp_dir') eq '')) || (defined $ENV{'TMP'} && ! ($ENV{'TMP'} eq '') ? $ENV{'TMP'} : '/tmp');

my $parser = new MIME::Parser;

# TODO - FIXME - better handling of temporary dir - we should use sth simillar to mktemp();

$parser->decode_headers(1);
$parser->extract_uuencode(1);
$parser->output_dir($tmpdir);

my $entity = $parser->parse(\*STDIN);

my $headers = $entity->head;
my $mh_from = $headers->get('From');
my $mh_to = $headers->get('To');
my $mh_msgid = $headers->get('Message-ID');
my $mh_replyto = $headers->get('Reply-To');
my $mh_subject = $headers->get('Subject');
my $mh_references = $headers->get('References');
my $mailheaders = $headers->as_string;
my $body;
my $mailbody;
my @attachments;

# Taken from pl.comp.lang.perl FAQ - http://www.kt.agh.edu.pl/other/perl/faq/5.html#6

if($entity->mime_type =~ m|multipart/|i) # if we deal with multipart message...
{
	for(my $partnum = 0; $partnum < $entity->parts; $partnum++)
	{
		if(($body = $entity->parts($partnum))->mime_type =~ m|text/|i && $mailbody eq '') # if we have empty body now.
		{
			$body = $entity->parts($partnum);
			$mailbody = $body->as_string;
		}
		else
		{
			# pchnijmy index encji do tablicy z zacznikami
			push @attachments, $partnum;
		}
	}
}
else
{
	$body = $entity->bodyhandle;
	$mailbody = $body->as_string;
}

chomp $mh_from;
chomp $mh_to;
chomp $mh_subject;
chomp $mh_msgid;
chomp $mailbody;
chomp $mailheaders;
chomp $mh_replyto;
chomp $mh_references;

$mailbody =~ s/\\/\\\\/g;
$mailbody =~ s/\'/\\\'/g;
$mailheaders =~ s/\\/\\\\/g;
$mailheaders =~ s/\'/\\\'/g;
$mh_from =~ s/\\/\\\\/g;
$mh_from =~ s/\'/\\\'/g;
$mh_to =~ s/\\/\\\\/g;
$mh_to =~ s/\'/\\\'/g;
$mh_msgid =~ s/\\/\\\\/g;
$mh_msgid =~ s/\'/\\\'/g;
$mh_subject =~ s/\\/\\\\/g;
$mh_subject =~ s/\'/\\\'/g;
$mh_replyto =~ s/\\/\\\\/g;
$mh_replyto =~ s/\'/\\\'/g;
$mh_references =~ s/\\/\\\\/g;
$mh_references =~ s/\'/\\\'/g;

if($mh_subject eq '')
{
	$mh_subject = '(brak tematu)';
}

my $dbase;
my $utsfmt;

if($dbtype eq "mysql")
{
	$dbase = DBI->connect("DBI:mysql:database=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
	$utsfmt = "UNIX_TIMESTAMP()";
}
elsif($dbtype eq "postgres")
{
	$dbase = DBI->connect("DBI:Pg:dbname=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
	$utsfmt = "EXTRACT(EPOCH FROM CURRENT_TIMESTAMP(0))";
}
elsif($dbtype eq "sqlite")
{
	$dbase = DBI->connect("DBI:SQLite:dbname=$dbname;host=$dbhost","$dbuser","$dbpasswd", { RaiseError => 1 });
	$utsfmt = "strftime('%s','now')";	
}
else
{
	print STDERR "Fatal error: unsupported database type: $dbtype, exiting.\n";
	exit 1;
}

# for security, fetch timestamp from database.
my $dbq = $dbase->prepare("SELECT $utsfmt AS timestamp");
$dbq->execute();
my $row = $dbq->fetchrow_hashref();
my $timestamp = $row->{'timestamp'};

# before we create new ticket try to find references...
# no because: somebody would like to make new request while replying 
# with new subject (without ticketdid)

my $prev_tid = 0;
my $inreplytoid = 0;
my @reftab = split(' ', $mh_references);
my $lastref = $reftab[scalar(@reftab)-1];

#check 'References'
if($lastref)
{
	$dbq = $dbase->prepare("SELECT id, ticketid FROM rtmessages WHERE messageid = '$lastref'");
	$dbq->execute();
	if($row = $dbq->fetchrow_hashref())
	{
		$prev_tid = $row->{'ticketid'};
		$inreplytoid = $row->{'id'};
	}
}

# check email subject
if( !$prev_tid && ($mh_subject =~ /\[RT#[0-9]{6,}\]/) )
{
	$prev_tid = $mh_subject;
	$prev_tid =~ s/.*\[RT#([0-9]{6,})\].*/\1/;
	$prev_tid = sprintf('%d',$prev_tid);
	
	$dbq = $dbase->prepare("SELECT id FROM rttickets WHERE id = $prev_tid");
	$dbq->execute();
	if(!($row = $dbq->fetchrow_hashref()))
	{
		$prev_tid = 0;
	}
}

my $ticket_id;
my $rt_msgid;
my $reqcustid = 0;
my $reqadminid = 0;
my $fromemail = $mh_from;
$fromemail =~ s/^.*<(.+\@.+)>$/\1/g;
my $toemail = $mh_to;
$toemail =~ s/^.*<(.+\@.+)>$/\1/g;

if(! $prev_tid) # generate new ticket if previous not found
{
	# find queue ID if not specified
	if(!$queue)
	{
		$dbq = $dbase->prepare("SELECT id FROM rtqueues WHERE email = '$toemail'");
		$dbq->execute();
		if(my $row = $dbq->fetchrow_hashref())
		{
			$queue = $row->{'id'};
		}
	}
	
	if(!$queue)
	{
		print STDERR "Fatal error: Queue ID not found, exiting.\n";
		exit 1;
	}

	# find userid
	$dbq = $dbase->prepare("SELECT id FROM users WHERE email = '$fromemail'");
	$dbq->execute();
	if(my $row = $dbq->fetchrow_hashref())
	{
		$reqcustid = $row->{'id'};
	}

	$dbq = $dbase->prepare("INSERT INTO rttickets (queueid, requestor, userid, subject, createtime) VALUES ($queue, '$mh_from', $reqcustid, '$mh_subject', $timestamp)");
	$dbq->execute();
	
	$dbq = $dbase->prepare("SELECT id FROM rttickets WHERE queueid = '$queue' AND requestor = '$mh_from' AND subject = '$mh_subject' AND createtime = $timestamp");
	
	$dbq->execute();
	$row = $dbq->fetchrow_hashref();
	$ticket_id = $row->{'id'};

	$dbq = $dbase->prepare("INSERT INTO rtmessages (ticketid, mailfrom, replyto, userid, subject, messageid, headers, body, createtime) VALUES ($ticket_id, '$mh_from', '$mh_replyto', $reqcustid, '$mh_subject', '$mh_msgid', '$mailheaders', '$mailbody', $timestamp)");	
	$dbq->execute();

	# i think that this expr should be enought to fetch id of previously inserted message
	$dbq = $dbase->prepare("SELECT id FROM rtmessages WHERE ticketid = $ticket_id AND messageid = '$mh_msgid' AND createtime = $timestamp");
	$dbq->execute();
	$row = $dbq->fetchrow_hashref();
	$rt_msgid = $row->{'id'};	

	# get sender e-mail if not specified
	if(!$autoreply_from)
	{
		$dbq = $dbase->prepare("SELECT email FROM rtqueues WHERE id = '$queue'");
		$dbq->execute();
		if(my $row = $dbq->fetchrow_hashref())
		{
			$autoreply_from = $row->{'id'};
		}
	}

        my $replymail = MIME::Entity->build(
		'Type' => "text/plain",
		'Charset' => "ISO-8859-2",
#		'From' => $autoreply_from,
		'Encoding' => "quoted-printable",
		'Subject' => "[RT#".sprintf("%06d",$ticket_id)."] Potwierdzenie przyjcia zgoszenia o temacie '".$mh_subject."'",
		'To' => $mh_replyto || $mh_from,
		'References' => $mh_references.' '.$mh_msgid,
		'Data' => [
			"Uprzejmie informujemy e Pastwa zgoszenie zostao zarejestrowane w naszym\n",
			"systemie. Zgoszeniu zosta przydzielony numer: RT#".sprintf("%06d",$ticket_id)."\n",
			"\n",
			"W przypadku jakiejkolwiek korespondencji zwizanej z Pastwa zgoszeniem, prosimy\n",
			"umieci w polu tematu wiadomoci nastpujcy cig znakw: [RT#".sprintf("%06d",$ticket_id)."]\n",
			"\n",
		],
		'Message-ID' => '<confirm.'.$ticket_id.'.'.$queue.'.'.$timestamp.'@rtsystem.'.$hostname.'>',
		'In-Reply-To' => $mh_msgid,
		'X-Mailer' => 'LMS/RT v.'.$_version
	);
	
	#TODO - is sendmail via pipe is correct way?
	open SENDMAIL, "|$sendmail -t -oi -oem -f $autoreply_from"
		or die "$0: open sendmail: $!\n";
	$replymail->print(\*SENDMAIL);
	close SENDMAIL;
	die "sendmail failed" if ($? >> 255);
}
else
{
	# find adminid
	$dbq = $dbase->prepare("SELECT id FROM admins WHERE email = '$fromemail' AND email!=''");
	$dbq->execute();
	if(my $row = $dbq->fetchrow_hashref())
	{
		$reqadminid = $row->{'id'};
	}

	# find userid
	$dbq = $dbase->prepare("SELECT id FROM users WHERE email = '$fromemail' AND email!=''");
	$dbq->execute();
	if(my $row = $dbq->fetchrow_hashref())
	{
		$reqcustid = $row->{'id'};
	}

	$dbq = $dbase->prepare("INSERT INTO rtmessages (ticketid, mailfrom, userid, adminid, subject, messageid, replyto, headers, body, inreplyto, createtime) VALUES ($prev_tid, '$mh_from', $reqcustid, $reqadminid, '$mh_subject', '$mh_msgid', '$mh_replyto', '$mailheaders', '$mailbody', $inreplytoid, $timestamp)");
	$dbq->execute();
	$dbq = $dbase->prepare("SELECT id FROM rtmessages WHERE ticketid = $prev_tid AND messageid = '$mh_msgid' AND createtime = $timestamp");
	$dbq->execute();
	$row = $dbq->fetchrow_hashref();
	$rt_msgid = $row->{'id'};
	
	$ticket_id = $prev_tid;
}

if(scalar @attachments && $save_path)
{
	mkdir($save_path.'/'.sprintf('%06d',$ticket_id));
	mkdir($save_path.'/'.sprintf('%06d',$ticket_id).'/'.sprintf('%06d',$rt_msgid));
	foreach my $partnum (@attachments)
	{
		$body = $entity->parts($partnum);					
		my $filename = $body->bodyhandle->path;
		my $content_type = $body->mime_type;
		copy($filename, $save_path.'/'.sprintf('%06d',$ticket_id).'/'.sprintf('%06d',$rt_msgid));
		$dbq = $dbase->prepare("INSERT INTO rtattachments (messageid, filename, contenttype) VALUES ($rt_msgid, '".basename($filename)."', '$content_type')");
		$dbq->execute();
	}
}
$parser->filer->purge;			# cleanup
$dbq->finish();
$dbase->disconnect();
