#!/usr/local/bin/perl
# Original Copyright (C) Dirk Husemann, Computer Science Department IV, 
# 	Friedrich-Alexander-Universitt Erlangen-Nrnberg, Germany, 1993
# All rights reserved.
# 

# We need to be run from the faxmailer wrapper which is setuid to 
# uucp.daemon

require "parser.pl";

# get path right
$faxhome = "/usr/local/bin";
$execpath = "/bin:/sbin:/usr/bin:/usr/sbin:$faxhome";
$ENV{'PATH'} = $execpath;

# debugging output
open(STDOUT, ">/tmp/faxmailer.out");
open(STDERR, ">/tmp/faxmailer.err");

$i = `id`;
print $i;
$human = "bobj@uic.edu";
$sendmail = "/usr/sbin/sendmail";
$sendfax = "/usr/local/bin/sendfax";
$faxstat = "/usr/local/bin/faxstat";
$faxmail = "/usr/local/bin/faxmail";
$faxrm = "/usr/local/bin/faxrm";
$cat = "/bin/cat";

$i = $$;
$sendfaxtmp = "/tmp/.faxmailer.$i";
$faxheadertmp = "/tmp/.faxheader.$i";
$faxbodytmp = "/tmp/.faxbody.$i";
$faxsendq = "/var/spool/fax/sendq";
$faxallow = "/var/spool/fax/etc/faxacct.dat";
$fax_node = "faxtest.uic.edu";
$auth_fax_node = "uic\.edu$"; # Anyone from this node can send US fax

%faxhelperfiles = (
	"help", "/usr/local/lib/fax/faxmailer.help-english",
);

$hostname = `/bin/hostname`;
chop $hostname;
$faxuser = "fax";		# user fax as found in passwd
$myname = "fax-daemon";
$servername = "faxserver";	# name to use for admin jobs (stat, rm)
$myaliases = "faxmanager";

$did_adminjob = 0;

sub purge {
	local($string) = @_;

	$string =~ s/_/ /g;
	return $string;
}

sub faxhdrslice {
	local($pattern, $prune) = @_;

	@g = grep(/^$pattern:\s*/i, @mail_header);
	$component = pop(@g);
	($prune == 0) && (return $component);
	$component =~ s/^$pattern:\s*//i;
	return $component;
}

sub faxoptslice {
	local($pattern, $t_string, $f_string) = @_;

	@g = grep(/$pattern/i, @fax_options);
	$component = pop(@g);
	($component) && (return $t_string);
	return $f_string;
}

sub faxproblemreport {
	local($originator, $subject, $to, $errmsg, $offender, $explain, $exityn) = @_;

	$originator =~ s/From:\s*//i;
	(! $to) && ($to = "--- NO RECIPIENT NAMED ---");

print "Got a problem: $errmsg\n";
	open(SENDMAIL, "|$sendmail -oi -f$myname -t") || 
		die "$sendmail does not like us ...\n";
	print SENDMAIL <<PROBLEM_REPORT_END;
From: $myname@$fax_node
To: $originator
Cc: <bobj@uic.edu>
Subject: FAX Problem Report --- $errmsg

While trying to process your facsimile to

             "$to"

I stumbled over $errmsg:

             $offender

$explain

Good luck the next time ...

So long --- and thanks for all the fish,
   Yours truly,
   $myname

   ----- Unsent message follows -----
PROBLEM_REPORT_END
	printf SENDMAIL "$mail_header\n";
	open(FAXBODY, "$faxbodytmp") || die "Somebody stole $faxbodytmp (fpr)";
	while(<FAXBODY>) {
		print SENDMAIL $_;
	}

	close(SENDMAIL);
	close(FAXBODY);
	if($exityn)  {
		&RemoveTempFiles;
		exit;
	}

print "Returning from faxproblemreport\n";
	$_ = 1;
}

sub faxbuildcmdline {
	local($option, $argument) = @_;

	$returnme = "";
	# remove ' --- this will confuse sendfax if found within $argument ...
	($argument) && ($argument =~ s/'//g);
	($argument) && ($returnme = " $option '$argument'");

	return $returnme;
}

sub admin_job {
	local($originator, $job_description, $vjob_description, 
		$cmdline, $failmessage, $passthru) = @_;

	if(!$passthru) {
print "Running: $cmdline\n";
		open(ADMIN_JOB, "$cmdline |") || die $failmessage;
		@adminjob = <ADMIN_JOB>;
		close(ADMIN_JOB);
	} else { @adminjob = ($failmessage); }

	open(SENDMAIL, "|$sendmail -oi -f$myname -t") || 
		die "$sendmail does not like us ...\n";
	print SENDMAIL <<ADMIN_JOB_REPORT;
From: $myname@$fax_node
To: $originator
Subject: $job_description

Your request for $vjob_description has been processed and produced 
the following results

 @adminjob

We hope to have provided a satisfactory service, and remain
   obediently yours,
   $myname

ADMIN_JOB_REPORT

	close(SENDMAIL);
	$did_adminjob++;
}

sub check_permission {
	local($from, $jobid) = @_;
	local($qjob, $sender, $realsender);

	$qjob = "$faxsendq/q$jobid";
	if(-f $qjob) {
		open(QJOB, "<$qjob");
		@qjob = <QJOB>;
		close(QJOB);
		($sender) = grep(/^sender:/, @qjob);
		($sender =~ /sender:\s*(.*)$/i) && ($sender = $1);

	# is the sender part of $from ?
		($from =~ /$sender/) && (return 1);
	}

	return 0;
}

sub fax_auth {
	local($to, $faxallow) = @_;
	local($mbox, $end, @temp, $tnode, @acct);

	($from_node =~ /^uicvm$/i) && ($from_node = "uic.edu");
print "Checking $from_id at $from_node -> $to\n";

	($to =~ /(^996\d{4}$|^413\d{4}$)/) && (return 1); # On campus: all ok 
	($to !~ /^011/) && # uic.edu domain to anywhere in the US
		($from_node =~ /(^$auth_fax_node|\.$auth_fax_node)/i) &&
		(return 1);
	open(ACCT, "<$faxallow") || die "Couldn't open fax ACCT file.";
	@acct = <ACCT>;
	close(ACCT);
	
	($to =~ /^011/) && (grep(/^$from_id@$from_node$/i, @acct)) &&
		(return 1);
	&faxproblemreport($fax_from, "Fax Authorization",
		$fax_address, "a foreign fax", $to,
		"You are not authorized to send an international fax.\n" .
		"To request authorization, send mail to fax@$fax_node.", 0);
	return 0;
}

sub RemoveTempFiles {
	unlink($faxheadertmp);
	unlink($faxbodytmp);
	$_ = 1;
}

#
# Main routine. All info is gotten from the body of the mail
# if we don't 
#    - get a real-fax-number (composed of [0-9\.\+\-\x])
# we need to return a trouble report to the originator of the fax.

$fax_resolution = " -m";
$/ = "\r\n\r\n";
$mail_header = <STDIN>;
$/ = "\n";

open(FAXBODY, "> $faxbodytmp") || die "Can't open outfile $faxbodytmp (main)";
while(<STDIN>) { # Read in file up to first non-blank line
	($_ !~ /\S/) && next; # If line is blank (no characters), iterate
	s/\r\n/\n/;
	$first_line = $_;
	last;
}

($first_line) && (print FAXBODY $first_line);
while(<STDIN>) { # Read in the rest of the body
	s/\r\n/\n/;
	print FAXBODY $_;
}

close(FAXBODY);
print "First non-blank line is: $first_line\n";

@mail_header = split(/\r\n/,$mail_header);

(open(OUTFILE, "> $faxheadertmp")) || die("Can't open output file.");
$_ = $mail_header;
s/\r\n/\n/g;
print OUTFILE $_;
close(OUTFILE);
$rc = &RFC822parser("$faxheadertmp, SUBJECT FROM RCPT");
if($rc == 8) { # Big time error
	&RemoveTempFiles;
	&faxproblemreport($human, "Error processing header",
		"",  "rc = $rc", $rfc822out, "", 1);
}

@fields = split(/\x15/, $rfc822out);
$temp_j = @fields;
foreach $temp_val (@fields) {
	($tag_name, @vals) = split(/ /, $temp_val, 4);
	if($tag_name eq "subject") { $subject = join(" ", @vals); }
	else { # Must be some type of address
		$uid = $vals[0];
		$node = $vals[1];
		if($tag_name eq "from") {
			(!$vals[2]) && ($vals[2] = "$uid@$node");

			$fax_from = "($vals[2]) <$uid@$node>";
			$from_id = $uid;
			$from_node = $node;
		}

		($tag_name eq "to") &&
			(push(@to_fields, "$uid $node $vals[2]"));
	}
}

foreach $to (@to_fields) {
	undef($to_uid, $to_node, $to_name);
	($to_uid, $to_node, $to_name) = split(/ /, $to, 3);
print "Processing To: field $to_uid ($to_node)\n";
	($to_node !~ /$fax_node/i) && next;
	$to_uid =~ s/^fax\.//i;
	$fax_number = $fax_address = $to_uid;
	$fax_number = &Canonicalize($fax_number);
print "$to_node ($to_uid) is ok\n";
	$fax_to = $to_name;
	&Process_To_Fields;
}

&RemoveTempFiles;
exit;

sub Process_To_Fields {
# first check for admin stuff
	if($fax_address =~ /$myname|$servername|$myaliases|$faxuser/i) {
print "I have an admin job: $subject\n";
		if($subject =~ /status|stats|queue|query|state/i) {
			&admin_job($fax_from, "Status Query", "a status query", 
			"$faxstat -aw", 
			"$faxstat --- not in this universe it seems ...\n", 0);
		}

		if($subject =~ /(erase|delete|del|rm|remove|kill)\s*(\d+)/i) {
			$jobid = $2;
			if(&check_permission($fax_from, $jobid)) {
				&admin_job($fax_from, "FAX Removal",
					"a facsimile deletion",
					"$faxrm -h $hostname $2", 
					"$faxrm --- failed, hmpf.\n", 0);
			} else {
				&admin_job($fax_from, "FAX Removal",
					"a facsimile deletion", "hurz",
					"Either you don't own job # $jobid" .
					" or it doesn't exist", 1);
			}
		}

		foreach $helpcommand (%faxhelperfiles) {
			($subject =~ /$helpcommand/i) && 
				&admin_job($fax_from, "Help Request", "help", 
				"$cat $faxhelperfiles{$helpcommand}",
				"No help file found.\n", 0);
		}

		($did_adminjob) && (&RemoveTempFiles) && exit;

# else forward to FaxMaster
		open(SENDMAIL, "|$sendmail -oi -f$myname -t") ||
			die "$sendmail hates me!";
		print SENDMAIL "From: $myname@$fax_node\n";
		print SENDMAIL "To: $human\n";
		print SENDMAIL "Subject: Fax admin request\n\n";
		print SENDMAIL "The following mail was send to fax admin:\n\n";
		open(FAXHEAD, "<$faxheadertmp") || die "Can't open header!";
		while(<FAXHEAD>) { print SENDMAIL $_; }
		close(FAXHEAD);
		open(FAXBODY, "<$faxbodytmp") || die "Can't open body!";
		while(<FAXBODY>) { print $_; }
		close(FAXBODY);
		close(SENDMAIL);
		&RemoveTempFiles;
		exit;
	}

	(!&fax_auth($fax_number, $faxallow)) && return;
	(!$first_line) && (&faxproblemreport($fax_from, "Blank mail body",
			@to_fields, "an empty file", "", 
			"The body of your fax mail was blank", 1));

# small consistency check for valid fax number: at least 5 digits, okay?
	($fax_number !~ /\d{5,}/) &&   # UIC - Match at least 5 digits
		(&faxproblemreport($fax_from, $fax_subject,
		$fax_address, "an invalid FAX number", $fax_number,
		"A valid FAX number needs to contain at least five digits.", 0))
		&& next;

	($subject) && ($fax_subject = $subject);
	$X_fax_options = &faxhdrslice("X-Fax-Options", 1);
	($X_fax_options) && ($fax_options = $X_fax_options);
	$fax_cover = "yes";
	if($fax_options) {
		@fax_options = split(/,/, $fax_options);
		$fax_cover = &faxoptslice("cover", "yes", "no");
	}

	($fax_cover eq "yes") || ($fax_cover = "no");
	if($fax_cover eq "yes") { $fax_cover = ""; }
	else { $fax_cover = " -n"; }

	if($first_line =~ /^%!PS/) {
print "It's a PS file!\n";
		$preprocessor = "";
		$prependheader = 0;
	}
	
	else {
print "It's a regular mail file.\n";
 		$preprocessor = $faxmail . " -n -x .25 | ";
 		$prependheader = 1;
	}

	undef($fax_fake_to);
	($fax_to) && ($fax_fake_to = $fax_to);
	($fax_fake_to) || ($fax_fake_to = $to_uid);
	($fax_fake_to) && ($fax_fake_to =~ s/^\(//g);
	($fax_fake_to) && ($fax_fake_to =~ s/\)$//g);

	$fax_destination = $fax_fake_to . "@" . $fax_number;

print "Processing: $fax_destination\n";
# open pipe to sendfax and call it with
#      sendfax -c $fax_comments -r $fax_subject_line \
#      -t 5 -x $fax_company -y $fax_location -d "$fax_to@$fax-number" \
#      -f $fax_from -DR

	$fax_from =~ s/^\(//g;
	$fax_from =~ s/\)//;
	$fax_hostname = $hostname;
	$cmdline = $preprocessor . $sendfax;
	$cmdline .= &faxbuildcmdline("-r", $fax_subject);
	$cmdline .= " -t 2 "; # Try max 5 times
	$cmdline .= &faxbuildcmdline("-f", $fax_from);
	$cmdline .= &faxbuildcmdline("-h", $fax_hostname);
	$cmdline .= $fax_cover;
	$cmdline .= $fax_resolution;
	$cmdline .= &faxbuildcmdline("-d", $fax_destination);
	$cmdline .= " -D 2>&1 1>$sendfaxtmp";

print "> $cmdline <\n";
	open(SENDFAX, "|$cmdline") ||
		die "$cmdline wasn't that hot an idea ...\n";

	if($preprocessor) {
		$scratch_mail_header = $mail_header;
		$scratch_mail_header =~ s/\r\n/\n/g;
		$scratch_mail_header =~ s/\nFrom:\s*(.*)\n/\nFrom: $fax_from\n/i;
		$scratch_mail_header =~ s/@$fax_node//ig;
		$scratch_mail_header =~ s/<fax./</ig;
		print SENDFAX $scratch_mail_header;
	}

	open(FAXBODY, "$faxbodytmp") ||
		die "Somebody stole $faxbodytmp (sendfax)";
	while (<FAXBODY>) {
		print SENDFAX $_;
	}

	close(FAXBODY);
	close(SENDFAX);

# now collect sendfax results
	open(SENDFAX, "<$sendfaxtmp") || die "Somebody stole $sendfaxtmp ...\n";
	@sendfaxresult = <SENDFAX>;
	close(SENDFAX);
	unlink($sendfaxtmp);

	$got_job_id = 0;
	while (@sendfaxresult) {
		$result = shift(@sendfaxresult);
		($result =~ /^Warning,/) && next;
		($result =~ /^request\s*id\s*is\s*(\d+)\s*for\s*host\s*(\w+).*$/) 
		&& ($jobid = $1, $host = $2, $got_job_id = 1) && next;
		($got_job_id) ||
			(&faxproblemreport($from, $fax_subject, $fax_to, 
			"couldn't send facsimile", $fax_number,
			"I'm puzzled ...\n@sendfaxresult\n", 1));
	}

# Copy faxbody to /var/spool/fax/orig/text.$jobid in case of failure.

	open(OUTFILE, "> /var/spool/fax/orig/text.$jobid") ||
		die "Can't open text file!";
	open(FAXBODY, "$faxbodytmp") ||
		die "Somebody stold $faxbodytmp (text)";
	while (<FAXBODY>) {
		print OUTFILE $_;
	}

	close(FAXBODY);
	close(OUTFILE);
# Change group to faxadmin so we can get to it in notify script
	chown(1, 201, "/var/spool/fax/orig/text.$jobid");

# Do a faxstat and collect its output and send it back to the originator.

	open(FAXSTAT, "$faxstat -aw|") || 
		die "$faxstat --- not in this universe it seems ...\n";
	@faxstatresult = <FAXSTAT>;
	close(FAXSTAT);
   
	$lateron ="as soon as possible";

	open(SENDMAIL, "|$sendmail -oi -f$myname -t") || 
		die "$sendmail avoids me ...\n";
	print SENDMAIL <<PROGRESS_REPORT_END;
From: $myname@$fax_node
To: $fax_from
Cc: <bobj@bobj.cc.uic.edu>
Subject: FAX Progress Report

Your facsimile to $to_uid has been queued for transmission
$lateron (Job Queue Id #$jobid).

The current FAX queue status is:

 @faxstatresult

All the best,
    Truly yours,
    $myname
PROGRESS_REPORT_END

	close(SENDMAIL);
}

sub Canonicalize { # Make a phone number 7 or 10 digits (or leave for intrntnl)
	($_) = @_;

	s/[^0-9]//g;			# Remove non-numbers
	s/^9011/I011/;			# 9   011        -> I011
	s/^011/I011/;			#     011        -> I011
	s/^9(\d{10})$/$1/;		# 9   7088335310 -> 7088335310
	s/^91(\d{10})$/$1/;		# 9 1 7088335310 -> 7088335310
	s/^1(\d{10})$/$1/;		#   1 7088335310 -> 7088335310
	s/^1(\d{7})$/$1/;		#   1    8335310 ->    8335310
	s/^9(\d{7})$/$1/;		# 9      8335310 ->    8335310
	s/^312(\d{7})$/$1/;		#     3128335310 ->    8335310
	s/^1312(\d{7})$/$1/;		#   1 3128335310 ->    8335310
	s/^9996(\d{4})$/$1/;		# 9      9966834 ->    9966834
	s/^9413(\d{4})$/$1/;		# 9      4131258 ->    4131258
	($_ =~ /^6\d{4}$/) && ($_ =~ s/^6/996/);
	($_ =~ /^3\d{4}$/) && ($_ =~ s/^3/413/);
	($_ =~ /\d{10}/) && (s/^/1/);	# Add 1 for LD dialing
	s/^I//;				# Remove "I" from above
	$_;
}
