#!/usr/bin/perl
# Copyright (C) Dirk Husemann, Computer Science Department IV, 
# 	Friedrich-Alexander-Universitt Erlangen-Nrnberg, Germany, 1993
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#      This product includes software developed by the Dirk Husemann,
#      Friedrich-Alexander-Universitt Erlangen-Nrnberg, Germany
# 4. The name of the University may not be used to endorse or promote
#    products derived from this software without specific prior written
#    permission. 
# 
# THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE UNIVERSITY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

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


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

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

$sendmail = "/usr/sbin/sendmail";
$sendfax = "/usr/contrib/fax/bin/sendfax";
$faxstat = "/usr/contrib/fax/bin/faxstat";
$faxmail = "/usr/contrib/fax/bin/faxmail";
$faxrm = "/usr/contrib/fax/bin/faxrm";
$faxacct = "/usr/contrib/fax/bin/faxacct";
$cat = "/bin/cat";
$decode = "/usr/contrib/metamail/bin/mimencode -u -b";
$uncompress = "/usr/contrib/bin/gzip -cd";

$sendfaxtmp = "/tmp/.faxmailer.$$";
$faxsendq = "/var/spool/fax/sendq";
$faxallow = "/var/spool/fax/etc/hosts";
$faxallow_acct = "/var/spool/fax/etc/hosts-accounting";

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

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

$default_fax_number = "+49 9131 85-8732";
$CompressedPostScript = "FrameMaker|CompressedPostScript"; # Tags for compressed
			                                  # PostScript

# set up assoc array with directly connected fax modems. NB: we give the
# modem to use for sending TO the fax modem!
$local_numbers = "8732|8733";
$local_fax_modems{"8732"} = "ttya1";
$local_fax_modems{"8733"} = "ttya0";

$did_adminjob = 0;

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

    $string =~ s/_/ /g;

    return $string;
}

sub faxargslice {
    local ($pattern) = @_;
    local ($returnme);

    if (@fax_address_parts) {
	(shift(@fax_address_parts) =~ /($pattern)/) && ($returnme = $1);
    } else {
	$returnme = "";
    }

    $returnme;
}

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) = @_;

    $originator =~ s/From:\s*//i;

    (! $to) && ($to = "--- NO RECIPIENT NAMED ---");

    open(SENDMAIL, "|$sendmail -oi -f$myname -t") || 
	die "$sendmail does not like us ...\n";
    print SENDMAIL <<PROBLEM_REPORT_END;
To: $originator
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";
    while (<STDIN>) {
	s/\r\n/\n/;
	print SENDMAIL $_;
    }
    close(SENDMAIL);

    exit;
}

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) {
	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;
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($from, $faxallow) = @_;
    local($mbox);

    ($from =~ /<(\S+@\S+)>/) && ($mbox = $1);
        
    open(FAXALLOW, "<$faxallow") || die "No allow file ...\n";
    @faxallow = <FAXALLOW>;
    close(FAXALLOW);

    (grep(/$mbox/i, @faxallow)) && (return 1);
    return 0;
}

# check for number of arguements, we need two
#       fax-hostname fax-address
#
# with fax-address being something like
#      fax-number.to-person.to-organization.to-location@fax-hostname
#
# if we don't 
#    - get TWO args
#    - get a real-fax-number (composed of [0-9\.\+\-\x])
# we need to return a trouble report to the originator of the fax.

#if ($#ARGV <= 3) {
#   goto trouble;
#}

$fax_address = $ARGV[$#ARGV];
$myname = $ARGV[$#ARGV-1] . "-daemon";

if ($fax_address =~ m|/|) {
    ($fax_address =~ m|^([^/]*)/|) && ($fax_number = $1);
} else {
    $fax_number = $fax_address;
}

@fax_address_parts = split(m|/|, $fax_address);
undef($fax_options);
foreach $fax_addr_part (@fax_address_parts) {
    ($fax_addr_part =~ /^TO=(.*)$/i) && ($fax_to = &purge($1)) && next;
    ($fax_addr_part =~ /^(COMPANY|COMP|ORG)=(.*)$/i) &&
	($fax_company = &purge($2)) && next;
    ($fax_addr_part =~ /^(LOCATION|LOC|PLACE)=(.*)$/i) &&
	($fax_location = &purge($2)) && next;
    ($fax_addr_part =~ 
     /^(TRANSMIT-TIME|TRANS-TIME|TIME-TO-SEND|TTS)=(.*)$/i) &&
	 ($fax_transmit_time = &purge($2)) && next;
    ($fax_addr_part =~ /^(RESOLUTION|RES)=(.*)$/i) &&
	($fax_resolution = $2) && next;
    ($fax_addr_part =~ /^(MODEM|TTY)=(.*)$/i) && 
	($fax_modem = $2) && next;
    ($fax_addr_part =~ /^(CONTENT|CONTENTS|CONT|CONTS)=(.*)$/i) &&
	($fax_originator = $2) && next;
    if ($fax_addr_part =~ /^(OPTIONS|OPTS|OPT)=(.*)$/i) {
	if ($fax_options) {
	    $fax_options .= ", " .&purge($2);
	} else {
	    $fax_options = &purge($2);
	}
	next;
    }
    if ($fax_addr_part =~ /^COVER$/i) {
	if ($fax_options) {
	    $fax_options .= ", cover";
	} else {
	    $fax_options = "cover";
	}
	next;
    }
    ($fax_addr_part =~ /^(COMMENTS|COM|COMM)=(.*)$/i) && 
	($fax_comments = &purge($2)) && next;
    ($fax_addr_part =~ /^(REGARDING|SUBJECT|RE|SUBJ)=(.*)$/i) &&
	($fax_subject = &purge($2)) && next;
}

if ($fax_resolution =~ /LOW|low|lo/i) {
    $fax_resolution = " -l";
} else {
    $fax_resolution = " -m";
}

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

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

$from = &faxhdrslice("From", 0);
$fax_from = &faxhdrslice("From", 1);
$subject = &faxhdrslice("Subject", 1);
$check_to = &faxhdrslice("Apparently-To", 1);
if ($check_to =~ /[\(\)]/) {
    &faxproblemreport($from, $subject, $fax_number,
		      "an incorrectly formed FAX number", $check_to,
		      "A FAX number MUST NOT contain neither `\(' nor `\)'!");
}

if (!&fax_auth($fax_from, $faxallow)) {
    &faxproblemreport($from, $subject, $fax_number, 
		      "missing authorization to use FAX server", $fax_from,
		      "You need to be authorized first.");
}

# first check for admin stuff
if ($fax_address =~ /$myname|$servername|$myaliases/i) {
    if ($subject =~ /status|queue|state/i) {
	&admin_job($fax_from, "Status Query", "a status query", 
		   "$faxstat -a -h $hostname", 
		   "$faxstat --- not in this universe it seems ...\n", 0);
    }
    if ($subject =~ /(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);
	}
    }
    if ($subject =~ /accounting|acct/i) {
	if (!&fax_auth($fax_from, $faxallow_acct)) {
	    (&faxproblemreport($from, $subject, $fax_number, 
			       "missing authorization to retrieve " .
			       "accounting data", $fax_from,
			       "You need special authorization for this."));
	}
    	&admin_job($fax_from, "Accounting Data", 
		   "accounting data transmission", "$faxacct",
		   "No accounting data ???\n", 0);
    }
    foreach $helpcommand (%faxhelperfiles) {
	($subject =~ /$helpcommand/i) && 
	    &admin_job($fax_from, "Help Request", "help", 
		       "$cat $faxhelperfiles{$helpcommand}",
		       "No help file found.\n", 0);
    }
    ($did_adminjob) && exit;

    # else forward to FaxMaster
    exec("$sendmail -oi -f$myname FaxMaster");
}

($subject) && ($fax_subject = $subject);

$X_fax_number = &faxhdrslice("X-Fax-Number", 1);
$X_fax_to = &faxhdrslice("X-Fax-To", 1);
$X_fax_company = &faxhdrslice("X-Fax-Company", 1);
$X_fax_location = &faxhdrslice("X-Fax-Location", 1);
$X_fax_comments = &faxhdrslice("X-Fax-Comments", 1);
$X_fax_transmit_time = &faxhdrslice("X-Fax-Transmit-Time", 1);
$X_fax_return_number = &faxhdrslice("X-Fax-Return-Number", 1);
$X_fax_modem = &faxhdrslice("X-Fax-Modem", 1);
$X_fax_originator = &faxhdrslice("X-Fax-Originator|X-Fax-Content", 1);
$X_fax_options = &faxhdrslice("X-Fax-Options", 1);

($X_fax_options) && ($fax_options = $X_fax_options);
if ($fax_options) {
    @fax_options = split(/,/, $fax_options);
    $fax_resolution = &faxoptslice("low", " -l", " -m");
    $fax_papersize = &faxoptslice("letter", "", " -A");
    $fax_cover = &faxoptslice("cover", "yes", "no");
    $fax_report = &faxoptslice("report|status", "yes", "no");
    $fax_nomail = &faxoptslice("nomail|no-mail|noemail|no-email", 
			       "yes", "no");
}

# If $X_fax_originator is "FrameMaker" we per default don't want to have a
# cover page.
($X_fax_originator) && ($fax_originator = $X_fax_originator);
($fax_originator =~ /$CompressedPostScript/i)  && 
    (($fax_cover eq "yes") || ($fax_cover = "no"));
($fax_cover eq "yes") || ($fax_cover = "no");
if ($fax_cover eq "yes") {
    $fax_cover = "";
} else {
    $fax_cover = " -n";
}

$fax_return_number = $default_fax_number;

($X_fax_number) && ($fax_number = $X_fax_number);
($X_fax_to) && ($fax_to = $X_fax_to);
($X_fax_company) && ($fax_company = $X_fax_company);
($X_fax_location) && ($fax_location = $X_fax_location);
($X_fax_comments) && ($fax_comments = $X_fax_comments);
($X_fax_transmit_time) && ($fax_transmit_time = $X_fax_transmit_time);
($X_fax_return_number) && ($fax_return_number = $X_fax_return_number);
($X_fax_modem) && ($fax_modem = $X_fax_modem);

if ($fax_nomail eq "yes") {
    $preprocessor = "";
    $prependheader = 0;
} elsif ($fax_originator =~ /$CompressedPostScript/i) {
    $preprocessor = "$decode | $uncompress | ";
    $prependheader = 0;
} else {
    $preprocessor = $faxmail . " -n | ";
    $prependheader = 1;

    $fax_from .= " ($fax_return_number)";
    ($fax_to) && ($fax_fake_to = $fax_to);
    ($fax_company) && ($fax_fake_to .= ", $fax_company");
    ($fax_location) && ($fax_fake_to .= ", $fax_location");
    (($fax_fake_to) && ($fax_fake_to .= " ($fax_number)")) 
	|| ($fax_fake_to = $fax_number);
}

# small consistency check for valid fax number: at least one digit, okay?
($fax_number !~ /\d+/) &&
    (&faxproblemreport($from, $fax_subject, $fax_to, 
                       "an invalid FAX number", $fax_number,
                       "A valid FAX number needs to contain at least one digit."));

($fax_number =~ /^((\+49|0049|0)913185|85|x)($local_numbers)$/) &&
(! $fax_modem) && ($local_fax_modems{$3}) && 
    ($fax_modem = $local_fax_modems{$3});

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

# avoid ':' in $fax_transmit_time, it confuses faxstat ...

$fax_transmit_time =~ s/://g;
$fax_transmit_time =~ s/\.//g;

# open pipe to sendfax and call it with
#      sendfax -a $fax_transmit_time -c $fax_comments -r $fax_subject_line \
#      -x $fax_company -y $fax_location -d "$fax_to@$fax-number" \
#      -f $fax_from -DR

$fax_hostname = $hostname;
($fax_modem) && ($fax_hostname .= ":$fax_modem");

$cmdline = $preprocessor . $sendfax;
$cmdline .= &faxbuildcmdline("-a", $fax_transmit_time);
$cmdline .= &faxbuildcmdline("-c", $fax_comments);
$cmdline .= &faxbuildcmdline("-r", $fax_subject);
$cmdline .= &faxbuildcmdline("-x", $fax_company);
$cmdline .= &faxbuildcmdline("-y", $fax_location);
$cmdline .= &faxbuildcmdline("-f", $fax_from);
$cmdline .= &faxbuildcmdline("-h", $fax_hostname);
# $cmdline .= $fax_papersize;
$cmdline .= $fax_cover;
$cmdline .= $fax_resolution;
$cmdline .= &faxbuildcmdline("-d", $fax_destination);
$cmdline .= " -D 2>&1 1>$sendfaxtmp";

# open(SENDFAX, "| /bin/cat 2>&1 1>/tmp/hurz");
# print SENDFAX ">$cmdline<\n";
open(SENDFAX, "|$cmdline") || die "$cmdline wasn't that hot an idea ...\n";
if (($preprocessor) && !($fax_originator =~ /$CompressedPostScript/i)) {
    $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/\nTo:\s*(\S+)\n/\nTo: $fax_fake_to\n/i;
    print SENDFAX $scratch_mail_header;
}
while (<STDIN>) {
    s/\r\n/\n/;
    print SENDFAX $_;
}
close(SENDFAX);
# exit 0;

# 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 =~ /^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 ..."));
}

# if fax-transmit-time given do a faxstat and collect its output and send it
# back to the originator.

if ($fax_transmit_time || $fax_report eq "yes") {
    open(FAXSTAT, "$faxstat -a -h $hostname|") || 
        die "$faxstat --- not in this universe it seems ...\n";
    @faxstatresult = <FAXSTAT>;
    close(FAXSTAT);
   
    ($fax_transmit_time) || ($lateron ="as soon as possible");
    ($fax_transmit_time) && ($lateron ="at a later point in time");

    open(SENDMAIL, "|$sendmail -oi -f$myname -t!") || 
        die "$sendmail avoids me ...\n";
    print SENDMAIL <<PROGRESS_REPORT_END;
To: $fax_from
Subject: FAX Progress Report

Your facsimile to $fax_number 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);
}

# BYE-BYE
exit;
