#!/opt/local/bin/perl -w
# @(#) SEPserver.pl	Acquires Secure-Email-Print files from POP3 server and
#			passes them to designated printer(s). Rev'd: 2003-03-15.
#
# 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 File::Basename;
use Net::POP3;
use Sys::Syslog;
use Crypt::GPG;
use Printer;
use Env qw(GPGBIN MAXMESS MAXJOB EXPIRE SLEEP);
use vars qw($VERSION);
$VERSION = "2.01";
my $gpgbin=$GPGBIN?$GPGBIN:"/usr/local/bin/gpg";# Name of GPG executable.
my $maxmess=$MAXMESS?$MAXMESS:23000000;		# Maximum message-size (bytes),
my $maxjob =$MAXJOB ?$MAXJOB :33554432;		# encrypted-job-size (bytes),
my $expire =$EXPIRE ?$EXPIRE :10800;		# expire-time(secs) and
my $sleep  =$SLEEP  ?$SLEEP  :30;		# sleep-time (secs).
my %uidtime;
defined($ARGV[0]) || die "Usage: ", basename($0). " printer1 [printer2 ..]\n";
syslog ('local7|info',basename($0)." start .. $maxmess/$maxjob/$expire/$sleep");

while (1) {					# Loop forever, processing
  foreach my $user (@ARGV) {			# print-queues in turn.
    my (%slot,%tp,@start); sleep $sleep;
    my $pop=Net::POP3->new() or next;		# Login to POP3 server and get
    my $count=$pop->login($user) or next;	# header plus 1st 30 lines of
  I:for (my $i=1;$i<=$count;$i++) {		# each message. Mark old and
      my $s=$pop->list($i) or next;		# over-size messages for
      my $u=$user.".".$pop->uidl($i) or next;	# deletion.
      $uidtime{$u}=time if ! exists $uidtime{$u};
      if ((time-$uidtime{$u})>$expire) {$uidtime{$u}=0}
      if (($s>$maxmess) or ($uidtime{$u}<1)) {	# Delete marked messages; this
        $pop->delete($i);			# mechanism picks up messages
        syslog('local7|info',"$u expired!");next# which didn't get deleted due
      }						# to server connection failure.
      my $top30=$pop->top($i,30) or next;
      my @part;
      for (my $j=0;$j<99;$j++) {		# Extract "name=" fields.
        if (! defined(@$top30[$j])) {last}
        if ( @$top30[$j]=~m/name=\"/ ) {
          (my @m)=split(/=/,@$top30[$j]); $m[1]=~s/\"//g; chomp $m[1];
          (@part)=split(/\./,$m[1]);      $start[$i]=$j;
          if (defined($part[1])) {$slot{$part[0].".".$part[1]}=$i}
          if ($m[1]=~m/\.$/) {$tp{$part[0]}=$part[1]}
        }
        else {next}
        next if ! exists $tp{$part[0]};
        for (my $k=1;$k<=$tp{$part[0]};$k++){	# Check if we have all parts.
          next I if ! defined $slot{$part[0].".".$k}
        }
        my $buffer=""; my $first="Y";
        for (my $k=1;$k<=$tp{$part[0]};$k++){
          my $message=$pop->get($slot{$part[0].".".$k}) or next I;
          my $l=$start[$slot{$part[0].".".$k}]+1; my $print="N";
          while ( defined @$message[$l] ) {	# Assemble parts.
            chomp @$message[$l];
            if ($print eq "Y") {
              if (@$message[$l] eq "") {last if $first eq "N"; $first="N"} 
              $buffer.=@$message[$l]."\n";
              if (length($buffer) > $maxjob) {$uidtime{$u}=0; next I}
            }
            if ((@$message[$l] eq "") && ($print eq "N")) {$print="Y"}
            $l++
          }
        } #$k
        my $gpg=new Crypt::GPG; $gpg->gpgbin($gpgbin);
        ($buffer,my $sig)=$gpg->verify($buffer);# If all-part decrypt succeeded,
        if (length($buffer)>0) {		# mark one part for deletion,
          $uidtime{$u}=0;  	 		# then print decrypted string.
          my $prn=new Printer($^O=>"$user"); $prn->print($buffer);
          syslog('local7|info',"$part[0] $tp{$part[0]} => $user")
        }
        next I
      } #$j
    } #$i
    if (defined $count) {			# If a server mailbox is empty
      $pop->quit();				# clean out all records relating
      if ($count<1) {				# to it.
        foreach my $r (keys(%uidtime)) {delete $uidtime{$r} if $r=~m#^$user=#}
      }
    }
  } #$user
}

__END__

=head1 NAME

SEPserver - Secure Email Print server program (GPG-based).

=head1 README

SEPserver is a Secure Email Print server.
It acquires Secure-Email-Print files from
a POP3 server, and decodes them using GPG.

=head1 DESCRIPTION

C<SEPserver> is a Secure Email Print server program (GPG-based).
It acquires Secure-Email-Print files from a POP3 server, and
aggregates their contents where appropriate then decodes them
using GPG; output is fed to designated printers.

=head1 USAGE

=over 4

=item SEPserver printer1 [ printer2 ..]

=back

This program should be executed by a user whose home directory
contains GPG public signatures for those clients authorised to
use it. Environment variables can be set to modify the defaults
assigned for GPG-executable location and message-size limits as
shown near the start of the program.

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

=head1 PREREQUISITES

This script requires the C<Crypt::GPG>, C<Net::POP3>
and C<Printer> modules.  The POP3 server is determined using
C<Net::Config>, and login passwords are extracted using C<Net::Netrc>.

=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
