#!/usr/bin/perl -w
# @(#) BIPclient.pl	Brother-Internet-Print client.
#			Rev'd: 2003-03-06.
#
# 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 Sys::Syslog;
use Net::Config;
use Net::hostent;
use Net::SMTP;
use MIME::Base64;
use vars qw($VERSION $opt_P $opt_n);
$VERSION = "1.03";
my $Config="/usr/local/conf/BIPclient.cf";	## Adjust as necessary.

getopt('Pn');					## Usage/Configuration 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}});
mes ('info',"Seeking records for printer $opt_P  and user $opt_n");

my (@printrec, @userrec); 			## Address lookup.
if (open(FILE,$Config)) {
  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>=3) && ($#r<=4) && ($r[0] eq "U") && ($r[1] eq $opt_n) )
                                                                 {@userrec=@r }
  }
}
if ( (@userrec) && ($#userrec==3) ) {$userrec[3]="Never"; $userrec[4]=""} 
mes ('warning',"Valid records for printer $opt_P  or user $opt_n ".
     "not found in file $Config") unless ( (@printrec) && (@userrec) );

my @InpBuf; my $TotalParts=0;			## Read and split STDIN.
mes ('warning',"Can't read input!\n") unless open(FILE,"-");
while (read(FILE,$InpBuf[++$TotalParts],1024*$printrec[2]) ) {}

my @l=localtime;				## Generate a sequence-number.
my $seq=sprintf "%04d%02d%02d%02d%02d%02d%01d",
        $l[5]+1900,$l[4]+1,$l[3],$l[2],$l[1],$l[0],$$%10; 

foreach my $PartNo (1..--$TotalParts) {		## Encode and send each part.
T:for (my $t=1;$t<=3;$t++) {
  H:foreach my $Host (@Hosts) {
      my $smtp; my $k=2;
      mes ('debug',"Trying SMTP host $Host");
      if ( gethost($Host) && ($smtp=Net::SMTP->new($Host)) &&
                             $smtp->mail($userrec[2])        ) {} else {next H}
      while ( defined $printrec[++$k] ) {
        if ( $smtp->to($printrec[$k]) )                        {} else {next H}
      }
      if ( $smtp->data("MIME-Version: 1.0\n".
             "Subject: Job for Printer $opt_P\n".
             'Content-Type: multipart/mixed; boundary="=_cuthere"'."\n\n\n".
             "--=_cuthere\n".
             'Content-Type: text/plain; charset="US-ASCII"'."\n\n".
             "START-BROBROBRO-START\nBRO-SERVICE=ZYXWVUTSRQ980\n".
             "BRO-NOTIFY=$userrec[3]\n".
             "BRO-REPLY=$userrec[4]\n" .
             "BRO-PARTIAL=$PartNo"."/"."$TotalParts\n".
             "BRO-UID=$userrec[2]".$seq."\n".
             "BRO-LANG=0x09\nSTOP-BROBROBRO-STOP\n\n--=_cuthere\n".
             'Content-Type: application/octet-stream; name="PrintJob.PRN"'."\n".
             "Content-Transfer-Encoding: base64\n\n".
             encode_base64($InpBuf[$PartNo])."\n--=_cuthere--\n") &&
           $smtp->quit                                       ) {
        undef $InpBuf[$PartNo];
        mes ('debug',"Part $PartNo/$TotalParts => $Host");
        last T
      }
    }
    sleep 1
  }
  mes ('warning',"Couldn't send part: $PartNo!") if defined $InpBuf[$PartNo]
}

exit 0;						## All done!

sub mes { 					## Message/exit function.
  syslog ('lpr|'.$_[0],basename($0)." $_[1]"); exit (1) if ($_[0] eq 'warning') 
}
__END__

=head1 NAME

BIPclient - client for Brother-Internet-Print protocol

=head1 README

BIPclient uses the Brother-Internet-Print protocol
for emailing jobs to remote printers.

=head1 DESCRIPTION

C<BIPclient> is a simple client program for the 
Brother-Internet-Print protocol. It can be invoked
as a BSD print filter, and uses a simple configuration
file to determine the email address(es) which should
be used for each remote printer. 

The program has been designed to mimic the functionality
of its Windows counterpart (available at <www.brother.com>).
To that end, it uses the above-mentioned configuration
file to associate a reply-address and a reply-condition
with each user.

=head1 USAGE

=over 4

BIPclient -P printer -n user

=back

The SMTP server(s) are determined using Net::Config, and are
tried in turn for each job part. Progress messages are written
using the 'lpr' facility of syslog, at severity levels 'debug',
'info' and 'warning'.

=head1 CONFIGURATION

A typical configuration file is shown hereunder. It contains
entries for printers ('P' in first column) and users ('U' in
first column). The meanings of the field contents are the same
as those in the program's Windows counterpart.

 # Printers
 #Flag Name PartSz Address-1      [Address-2] ..
 P     lpt1 16     lpt1@acme.com  lpt1@acme.com.au
 P     lpt2 128    lpt2@acme.com
 # Users
 #Flag Name        Address        Condition   [Reply-To]
 U     foo         foo@acme.co.uk Always      foo@hotmail.com 
 U     bar         bar@acme.co.uk Never

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:

 lpt1:\
     :lp=/dev/null:sd=/var/spool/lpd/lpt1:\
     :if=/usr/local/bin/BIPclient.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
