#!/usr/bin/perl -w
# SEPclient.pl		Secure Email Print client program (GPG-based).
#			Takes incoming stream and generates signed message
#			which is split in accordance then emailed to server.
#			Rev'd 2003-03-23.
#
# Copyright (c) 2003 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;
use Getopt::Std;
use File::Basename;
use Net::hostent;
use Net::SMTP;
use Net::Config;
use IO::Handle;
use Socket;
use Env qw(HOME PGPPASS);
use vars qw($VERSION $opt_P $opt_n);
$VERSION = "2.02";				## You can change the next line.
my @Conf=("C:\\Windows\\SEPclien.ini","/usr/local/etc/SEPclient.cf");

getopt('Pn');					## Usage Checks.
mes ('warning',"Usage.. ".basename($0)." -P printer -n user")
  unless ( defined($opt_P) && defined($opt_n) );
mes ('warning',"No SMTP Hosts defined")
  if ! (my @Hosts=@{$NetConfig{smtp_hosts}});

my (@printrec,@userrec,$Gpg);			## Address lookup.
foreach my $F (@Conf) {
  if (open(FILE,$F)) {
    mes ('info',"Seeking records for $opt_P  and $opt_n in $F");
    while (<FILE>) {
      chomp; (my @r)=split;
      if  ( ($#r>=3) && ($r[0] eq "P") && ($r[1] eq $opt_P) &&
            ($r[2]=~/^\d+$/) && ($r[2]>=16) )                    {@printrec=@r}
      elsif(($#r>=2) && ($r[0] eq "U") && ($r[1] eq $opt_n) )    {@userrec=@r }
      elsif(($#r==1) && ($r[0] eq "G") )                         {$Gpg=$r[1]  }
    }
  }
}
mes ('warning',"Valid printer/user records not found")
  unless ( (@printrec) && (@userrec) );
mes ('warning',"Name of 'gpg' executable not found") unless (defined $Gpg);

if ( open(FILE,"/etc/passwd") ) {		## Environment correction.
  while (<FILE>) { (my @r)=split /:/; if ( $r[2] eq $< ) {$HOME=$r[5]}}
}						## (Required with LPRng.)

my ($Child,$FilDes,$Stamp,$InpBuf,$OutBuf,$Number);
$Number=0; $OutBuf=$InpBuf="";
$Stamp=$userrec[2].time; $Stamp=~tr/a-z,A-Z,0-9/_/c;
if (defined $PGPPASS) { 
  eval {socketpair(CHILD,PARENT,AF_UNIX,SOCK_STREAM,PF_UNSPEC)};
  undef $PGPPASS if $@				## If 'socketpair' is supported,
}		 				## accept password from 
if (defined $PGPPASS) {				## environment variable. 
  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 -asz9 --passphrase-fd $FilDes -o - |")
  }
}
else {open (PIPE,"$Gpg -asz9 -o - |")}

while (<PIPE>) {				## Double-buffered read.
  if ( length($InpBuf.$_) > ($printrec[2]*1024) ) {do_output()}
  $InpBuf=$InpBuf.$_
}
foreach my $j (1,2) {do_output()}		## EOF: flush both buffers.
exit 0;

sub do_output {					## Output subroutine.
  my ($La,$Content);
  if ($OutBuf ne "") {
    $Number++; if ($InpBuf eq "") {$La="."} else {$La=""}
    $Content="application/octet-stream; name=\"$Stamp\.$Number$La\"";
    for (my $t=1;$t<=3;$t++) {			## (3 tries for each Mailhost.)
      foreach my $mailhost (@Hosts) {
        my $smtp; my $k=2;
        mes ('debug',"Trying SMTP host $mailhost");
        if (gethost($mailhost)   && ($smtp=Net::SMTP->new($mailhost))     &&
            $smtp->mail($userrec[2])                          ) {} else {next}
        while ( defined $printrec[++$k] ) {
          if ( $smtp->to($printrec[$k]) )                       {} else {next}
        }
        if ($smtp->data("Subject: SEP Job $Stamp Part $Number$La\n".
              "MIME-Version: 1.0\n"."Content-Type: $Content\n\n".$OutBuf) &&
            $smtp->quit                                                     ) {
          mes ('debug',"Part $Number$La => $mailhost");
          goto L
        }
      }
    }
    mes ('warning',"Couldn't send part $Number!")
  }
L:$OutBuf = $InpBuf;				## Move input buffer contents
  $InpBuf = ""					## to output buffer and exit.
}

sub mes {					## Message/exit function.
  eval "use Sys::Syslog";
  if ($@) {print STDERR $_[1],"\n"}
  else    {syslog ('lpr|'.$_[0],basename($0)." $_[1]")} 
  exit (1) if ($_[0] eq 'warning')
}
__END__

=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  then emailed to a server.  A simple configuration file
is used to determine a part-size and destination address for
each remote printer.

=head1 USAGE

=over 4

SEPclient -P printer -n user

=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.

Progress messages are written using the 'lpr' facility of syslog,
at severity levels 'debug', 'info' and 'warning'. If syslog is
not available, STDERR is used for all messages.

This version of the program is compatible with SEPserver.pl
versions 2.01 and later.

=head1 PREREQUISITES

This script requires the 'gpg' executable. The C<Net> module
is also required. The SMTP servers whose names appear in that
module's 'libnet.cfg' file will be tried in turn for sending
each job part.

=head1 CONFIGURATION

By default, the program tries to open the configuration files
C:\Windows\SEPclien.ini and /usr/local/etc/SEPclient.cf; the
program can be edited to change these if necessary.

A typical configuration file is shown hereunder. It contains
entries for printers ('P' in first column), users ('U' in
first column), and 'gpg' executable ('G' in first column). The
addresses associated with user names are
used to provide sender-addresses which are acceptable the SMTP
host(s), together with optional reply-conditions and
reply-to addresses which will be used in later
releases of this program.

 # Printers
 #Flag Name PartSz(kb) Address1       [Address2] ..
 P     lpt1 16         lpt1@acme.com
 P     lpt2 128        lpt2@acme.com  lpt2@acme.co.uk
 # Users
 #Flag Name Address        [Reply-Condition]  [Reply-To]
 U     foo  foo@acme.co.uk
 U     bar  bar@acme.co.uk Always              bar@hotmail.com
 # GPG
 #Flag Executable
 G     /usr/bin/gpg

The program can be invoked as an input filter on a machine which
uses BSD-style printing using a 'printcap' entry of the following
form; the GPG key to be used should in this case be contained in
the home directory of the printer-daemon user, and there should
be no passphrase.

 lpt1:\
     :lp=/dev/null:sd=/var/spool/lpd/lpt1:\
     :if=/usr/local/bin/SEPclient.pl:mx#0:sh:


=head1 SCRIPT CATEGORIES

Networking
UNIX/System_administration

=head1 AUTHOR

Graham Jenkins <grahjenk@au1.ibm.com>

=head1 COPYRIGHT

Copyright (c) 2003 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
