#!/usr/local/bin/perl -w
# SEPclient.pl		Secure Email Print client program (GPG-based).
#			Takes incoming stream and generates signed message
#			which is split in accordance with RFC 2046, then
#			emailed to server. Requires 'gpg' program.
#
# Copyright (c) 2002 Graham Jenkins <grahjenk@au1.ibm.com>. All rights reserved.
# This program is free software; you can redistribute it and/or modify it under
# the same terms as Perl itself.

use strict;				# For MS Windows, call via 'redmon' ..
use File::Basename;			# <www.cs.wisc.edu/~ghost/redmon>
use IO::Handle;
use Socket;
use Net::SMTP;
use Net::hostent;
use Net::Config;
use Env qw(PGPPASS);
use vars qw($VERSION);
$VERSION = "1.0";
my (@Hosts,$Gpg,$Child,$FilDes,$Sender,$Stamp,$Dest,$InpBuf,$OutBuf,$Number);

die "Usage: ".basename($0)." kb-per-part sender destination [mailhost(s)]\n".
    " e.g.: ".basename($0).
            " 16 foo\@hotmail.com lp3\@pserv.acme.com < report.ps\n".
    "       Part-size must be >= 1\n"  if ( ($#ARGV < 2) or ($ARGV[0] < 1) );

@Hosts=@{$NetConfig{smtp_hosts}};
for (my $j=$#ARGV;$j>=3;$j--) {unshift @Hosts, $ARGV[$j]}
$Sender=$ARGV[1]; $Dest=$ARGV[2]; $Number=0; $OutBuf=$InpBuf=""; $Stamp=time;
print STDERR "Trying some 'gpg'commands ..\n";
foreach ("gpg", "C:\\UTILS\\GPG", "C:\\GNUPG\\GPG", "/usr/local/bin/gpg") {
  if(`$_ --version` =~m/Hash/) {print STDERR "OK!\n"; $Gpg = $_ ; last}
  else                         {print STDERR "Oops ..\n"}
}
die "Failed!\n" if not defined $Gpg;

if (defined $PGPPASS) { 
  eval {socketpair(CHILD,PARENT,AF_UNIX,SOCK_STREAM,PF_UNSPEC)};
  undef $PGPPASS if $@			# If 'socketpair' is supported, accept
}					# password from environment variable.
if (defined $PGPPASS) { 
  CHILD->autoflush(1);
  if ($Child = fork) {
    close(PARENT);
    print CHILD $PGPPASS, "\n";
    waitpid($Child,0);
    exit
  }
  else {
    close(CHILD);
    $FilDes=fileno(PARENT);
    open (PIPE,"$Gpg -as --passphrase-fd $FilDes -o - |")
  }
}
else {open (PIPE,"$Gpg -as -o - |")}

while (<PIPE>) {			# Read into $InpBuf until EOF found.
  if ( (length($InpBuf . $_)) > ($ARGV[0] * 1024) ) {do_output()}
  $InpBuf = $InpBuf . $_		# Whenever $InpBuf is full, call output
}					# routine to push it into $OutBuf.
foreach my $j (1,2) {do_output()}	# EOF: flush both buffers and exit.
exit 0;

sub do_output {				# Output subroutine.
my ($Total,$Content,$smtp);		# If output buffer contains data, 
  if ($OutBuf ne "") {			# increment Number, and check whether
    $Number++; $Total="";		# it is the last buffer.
    $Content="message/partial; id=\"$Sender.$Stamp\"; number=\"$Number\"";
    if ($InpBuf eq "") {$Total=$Number; $Content="$Content; total=\"$Total\""}
    while (1) {			
      foreach my $mailhost (@Hosts) {	# Ensure $mailhost address is known.
        print STDERR "Checking mailhost: $mailhost\n";
        next if ! defined gethost($mailhost);
        print STDERR "Sending part ",$Number,"/",$Total," via ",$mailhost,"\n";
        $smtp = Net::SMTP->new($mailhost) || next;
        $smtp->mail($Sender);
        $smtp->to  ($Dest);
        $smtp->data();
        $smtp->datasend("Content-Disposition: inline\n");
        $smtp->datasend("Content-Transfer-Encoding: 7bit\n");
        $smtp->datasend("Content-Type: $Content\n");
        $smtp->datasend("MIME-Version: 1.0\n");
        $smtp->datasend("Subject: Secure Email Print Job # ".$Stamp."\n\n");
        $smtp->datasend($OutBuf);
        $smtp->dataend;
        $smtp->quit;
        goto L
      }
      print STDERR "Connection failure (mailhost). Retrying ..\n";
      sleep 1
    }
  }
L:$OutBuf = $InpBuf;			# Move input buffer contents to
  $InpBuf = ""				# output buffer and exit.
}

=head1 NAME

SEPclient - Secure Email Print client program (GPG-based).

=head1 README

SEPclient is a Secure Email Print client
which uses GPG to sign a print job before
splitting and emailing it to a server.

=head1 DESCRIPTION

C<SEPclient> is a Secure Email Print client program (GPG-based).
It takes an incoming stream and generates a signed message which 
is split in accordance with RFC 2046, then emailed to a server.

=head1 USAGE

=over 4

=item SEPclient kb-per-part sender destination [mailhost(s)]

=back

Windows users may find it convenient to use a utility like
'redmon' <www.cs.wisc.edu/~ghost/redmon> for feeding print
jobs to this program.

On platforms which support the B<socketpair> function, the
user's GPG passphrase may be passed in the PGPPASS
environment variable.

=head1 PREREQUISITES

This script requires the 'gpg' executable. The C<Net> module
is also required. Mailhost names contained in that module's
'libnet.cfg' file will be appended to the list of mailhosts
which appears on the command line.

=head1 SCRIPT CATEGORIES

Mail
Networking

=head1 AUTHOR

Graham Jenkins <grahjenk@au1.ibm.com>

=head1 COPYRIGHT

Copyright (c) 2002 Graham Jenkins. All rights reserved.
This program is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.

=cut
