#!/usr/bin/perl
#
#    asterisk-faxreceive - handles reception of faxes using app_rxfax and
#                          dispatches the received faxes via email
#
#    Copyright (C) 2005  Florian Zumbiehl <florz@gmx.de>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    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: receive_fax 77 2005-09-05 19:16:10Z florz $
#

use strict;
use Fcntl ':flock';
use File::Sync 'fsync';
use File::Temp qw(tempdir);
use File::Path;
use MIME::Lite;
use Config::Tiny;
use Storable qw(nstore retrieve);
use POSIX qw(setlocale);
use Locale::gettext;

my $fax_counter_dir='/var/lib/asterisk/faxreceive/counters';
my $config_file='/etc/asterisk/asterisk_faxreceive.conf/faxreceive.conf';

$|=1;

textdomain('asterisk-faxreceive')||die();

my $config=Config::Tiny->read($config_file)||die(Config::Tiny::errstr());

my $email_from;
my $email_to;
my $deadx;
my $deady;
my $dmw;
my $dmh;
my $email_locale;

while(1){
	defined($_=<stdin>)||die();
	chomp();
	length($_)||last;
};

#"Uses LOCALSTATIONID to identify itself to the remote end\n"
#"Sets REMOTESTATIONID to the sender CSID\n"
#"     FAXPAGES to the number of pages received\n"
#"     FAXBITRATE to the transmition rate\n"
#"     FAXRESOLUTION to the resolution\n"

if($ARGV[0] eq 'receive'){
	my $recipient=agi_get_var('FAXRECIPIENT');
	$recipient eq 'new'&&die();
	$recipient eq 'lock'&&die();

	my $temp_dir;
	my $config_defaults={
		'paper_name'=>'',
		'margin_vertical'=>0.25,
		'margin_horizontal'=>0.25
	};
	defined(my $recipient_config=$config->{"recipient $recipient"})||die();
	for my $c($config_defaults,$config->{_},$recipient_config){
		config_get($email_from,'email_from',$c);
		config_get($email_to,'email_to',$c);
		($dmw,$dmh)=resolve_papername($c->{paper_name})if(defined($c->{paper_name}));
		config_get($dmw,'paper_width',$c);
		config_get($dmh,'paper_height',$c);
		config_get($deadx,'margin_vertical',$c);
		config_get($deady,'margin_horizontal',$c);
		config_get($temp_dir,'temp_dir',$c);
		config_get($email_locale,'locale',$c);
	}

	my($recv_counter,$fax_counter)=inc_fetch_counters($recipient,'recv');

	my $recv_dirname=tempdir("asterisk_faxreceive.$recipient.".sprintf('%05d.%05d',$recv_counter,$fax_counter+1).'.XXXXXX',
		$temp_dir ne ''?(DIR=>$temp_dir):(TMPDIR=>1))||die();

	{
		no strict 'refs';
		nstore({
			'version'=>1,
			map({$_=>eval("\$$_")} (
				'recipient',
				'email_from',
				'email_to',
				'dmw',
				'dmh',
				'deadx',
				'deady',
				'email_locale'
			))
		},"$recv_dirname/info")||die();
	}

	agi_cmd("SET VARIABLE FAXRECVFILE \"$recv_dirname/fax.tiff\"",1);
	agi_cmd("EXEC RxFAX \"$recv_dirname/fax.tiff\"",0);
	agi_cmd("HANGUP",1);
}elsif($ARGV[0] eq 'deliver'){
	my $remotecallerid=agi_get_var('MY_CALLERIDNUM');
	my $recv_filename=agi_get_var('FAXRECVFILE');
	my $remotestationid=agi_get_var('REMOTESTATIONID');
	my $faxpages=agi_get_var('FAXPAGES');

	$recv_filename=~s/\.tiff$//||die();
	my $recv_dirname=$recv_filename;
	$recv_dirname=~m,.*/,||die();
	$recv_dirname=$&;

	my $recipient;
	(my %info_data=%{retrieve("$recv_dirname/info")})||die();
	$info_data{version}==1||die();
	{
		no strict 'refs';
		for(
			'recipient',
			'email_from',
			'email_to',
			'dmw',
			'dmh',
			'deadx',
			'deady',
			'email_locale'
		){
			my $x=$info_data{$_};
			eval("\$$_=\$x");
		}
	}

	system("tiff2ps -2az $recv_filename.tiff > $recv_filename.ps")&&die();
	$dmw-=2*$deadx;
	$dmh-=2*$deady;
	open(my $ps_file,'<',"$recv_filename.ps")||die();
	my $count;
	while(defined($_=<$ps_file>)){
		m/^(\d+\.\d+) (\d+\.\d+) scale$/||next;
		my $sw=$1/72*2.54;
		my $sh=$2/72*2.54;
		my $scale;
		if($dmw/$dmh>$sw/$sh){
			$scale=$dmh/$sh;
		}else{
			$scale=$dmw/$sw;
		}
		my $dw=$sw*$scale;
		my $dh=$sh*$scale;
		my $xoff=($dmw-$dw)/2+$deadx;
		my $yoff=$dmh-$dh+$deady;
		$count++;
		system("psselect -p$count $recv_filename.ps | pstops -w${sw}cm -h${sh}cm '0\@$scale(${xoff}cm,${yoff}cm)' >> $recv_filename.resized.ps")&&die();
	}
	close($ps_file)||die();

	system("ps2pdf $recv_filename.resized.ps $recv_filename.pdf 1>&2")&&die();

	my($fax_counter)=(inc_fetch_counters($recipient,'fax'))[1];

	$email_to||die();

	if($email_locale ne ''){
		defined(setlocale(LC_MESSAGES,$email_locale))||die();
		defined(setlocale(LC_CTYPE,$email_locale))||die();
	}
	my $mail=new MIME::Lite(
		From => $email_from,
		To => $email_to,
		Type => 'text/plain; charset=iso-8859-1',
		Subject => sprintf(gettext('Fax from %s (%s)'),$remotestationid,$faxpages==1?gettext('one page'):sprintf(gettext('%d pages'),$faxpages)),
		Data => sprintf(gettext(<<EOD),$remotestationid,$remotecallerid eq 'CID withheld'?gettext('CID withheld'):$remotecallerid,$faxpages)
Fax station ID of the sender..: %s
Caller-ID of the sender.......: %s
Number of pages...............: %d
EOD
	);
	$mail->attach(
		Type => 'application/pdf',
		Filename => sprintf(sprintf(gettext('fax%s.pdf'),'%05d'),$fax_counter),
		Path => "$recv_filename.pdf"
	);
	defined(setlocale(LC_MESSAGES,'C'))||die();
	defined(setlocale(LC_CTYPE,'C'))||die();
	$mail->send()||die();

	rmtree($recv_dirname,0,1);
}else{
	die();
}

exit(0);

sub config_get{
	my($x,$name,$config)=@_;
	$_[0]=$config->{$name}if(defined($config->{$name}));
}

sub resolve_papername{
	my $name=shift();
	open(my $paperconf,'-|',"paperconf -csz $name")||die();
	defined(my $paperdims=<$paperconf>)||die();
	close($paperconf)||die();
	($paperdims=~/^\s*(\d+(?:\.\d+)?|\.\d+)\s*cm\s*(\d+(?:\.\d+)?|\.\d+)\s*cm\s*$/)||die();
	my $width=$1;
	my $height=$2;
	return($width,$height);
}

sub agi_get_var{
	my $var=shift();
	print("GET VARIABLE $var\n")||die();
	defined(my $r=<stdin>)||die();
	$r=~/^200 result=1 \((.*)\)$/||die();
	return($1);
}

sub agi_cmd{
	my($cmd,$correct_result)=@_;
	print("$cmd\n")||die();
	defined(my $r=<stdin>)||die();
	$r=~/^200 result=$correct_result$/||die();
}

sub inc_fetch_counters{
	my($recipient,$which)=@_;
	open(my $fax_cnt_lock,'>',"$fax_counter_dir/lock")||die();
	flock($fax_cnt_lock,LOCK_EX)||die();
	unlink("$fax_counter_dir/$recipient.new"); # just in case an old version of this script left one of those lying around ...
	if(stat("$fax_counter_dir/$recipient.new2")){
		commit_counters($recipient);
	}elsif(!$!{ENOENT}){
		die();
	}
	my $recv_counter=0;
	my $fax_counter=0;
	if(open(my $fax_cnt,'<',"$fax_counter_dir/$recipient.cnt")){
		defined(my $cnt_file_version=<$fax_cnt>)||die();
		$cnt_file_version==1||die();
		defined($recv_counter=<$fax_cnt>)||die();
		defined($fax_counter=<$fax_cnt>)||die();
		$recv_counter+=0;
		$fax_counter+=0;
		close($fax_cnt)||die();
	}elsif(!$!{ENOENT}){
		die();
	}
	if($which eq 'recv'){
		++$recv_counter;
	}elsif($which eq 'fax'){
		++$fax_counter;
	}else{
		die();
	}
	open(my $fax_cnt,'>',"$fax_counter_dir/$recipient.new1")||die();
	print($fax_cnt "1\n")||die();
	print($fax_cnt "$recv_counter\n")||die();
	print($fax_cnt "$fax_counter\n")||die();
	close($fax_cnt)||die();
	open($fax_cnt,'<',"$fax_counter_dir/$recipient.new1")||die();
	fsync($fax_cnt)||die();
	close($fax_cnt)||die();
	rename("$fax_counter_dir/$recipient.new1","$fax_counter_dir/$recipient.new2")||die();
	commit_counters($recipient);
	flock($fax_cnt_lock,LOCK_UN)||die();
	close($fax_cnt_lock)||die();
	return($recv_counter,$fax_counter);
}

sub commit_counters{
	my($recipient)=@_;
	open(my $fax_cnt_dir,'<',$fax_counter_dir)||die();
	fsync($fax_cnt_dir)||die();
	unlink("$fax_counter_dir/$recipient.cnt")||$!{ENOENT}||die();
	link("$fax_counter_dir/$recipient.new2","$fax_counter_dir/$recipient.cnt")||die();
	fsync($fax_cnt_dir)||die();
	unlink("$fax_counter_dir/$recipient.new2")||die();
	close($fax_cnt_dir)||die();
}

