271 lines
14 KiB
HTML
271 lines
14 KiB
HTML
<!--startcut ==============================================-->
|
|
<!-- *** BEGIN HTML header *** -->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
<HTML><HEAD>
|
|
<title>Internet Printing - Another Way LG #65</title>
|
|
</HEAD>
|
|
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
|
|
ALINK="#FF0000">
|
|
<!-- *** END HTML header *** -->
|
|
|
|
<CENTER>
|
|
<A HREF="http://www.linuxgazette.com/">
|
|
<H1><IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.png"
|
|
WIDTH="600" HEIGHT="124" border="0"></H1></A>
|
|
|
|
<!-- *** BEGIN navbar *** -->
|
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="correa.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue65/jenkins.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="joshi.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
|
<!-- *** END navbar *** -->
|
|
<P>
|
|
</CENTER>
|
|
|
|
<!--endcut ============================================================-->
|
|
|
|
<H4 ALIGN="center">
|
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
|
</H4>
|
|
|
|
<P> <HR> <P>
|
|
<!--===================================================================-->
|
|
|
|
<center>
|
|
<H1><font color="maroon">Internet Printing - Another Way</font></H1>
|
|
<H4>By <a href="mailto:grahjenk@au1.ibm.com">Graham Jenkins</a></H4>
|
|
</center>
|
|
<P> <HR> <P>
|
|
|
|
<!-- END header -->
|
|
|
|
|
|
|
|
|
|
<h2>The Problem</h2>
|
|
|
|
<p>You are doing some work on your home PC, connected to your favorite ISP - and
|
|
you decide you want to print a Word document on the high-speed color printer
|
|
at your office.
|
|
That printer is connected to the corporate LAN, but you can't talk to it
|
|
using LPR or IPP because it is hidden behind the corporate firewall.</p>
|
|
|
|
<p>You could perform a print-to-file operation, then email the file to somebody
|
|
at your office, and get them to send it to the printer. But there are a few
|
|
steps here - and it gets more complicated if there is a restriction on the
|
|
length of email messages which can be passed through one of the servers
|
|
along the way. You will then have to perform some sort of file-split operation
|
|
and send the individual parts.</p>
|
|
|
|
<h2>Client Software</h2>
|
|
|
|
<p>The people who make <a href="http://www.brother.com">Brother</a> printers thought
|
|
of all this, and developed a set of Windows printer drivers. These enable
|
|
users to print directly to a designated email address. The print-job is
|
|
automatically split into parts if necessary, and each part is base64-encoded
|
|
prior to transmission. Users can also nominate an address for email
|
|
confirmation.</p>
|
|
|
|
<p>These Windows printer drivers (for Windows 95/98, and for Windows
|
|
NT-4.0/2000) can be downloaded from the Brother website.</p>
|
|
|
|
<h2>Printer Capabilities</h2>
|
|
|
|
<p>What the Brother people expect users to do their printing on is, of course,
|
|
a Brother printer - specifically, in this instance, one equipped with a
|
|
network card able to accept, decode and re-assemble mail messages directed
|
|
to it.</p>
|
|
|
|
<p>But what if you wish to print on a printer from another manufacturer?</p>
|
|
|
|
<h2>Doing it in Software</h2>
|
|
|
|
<p>My first stab at this was a Korn-shell program to which appropriate
|
|
incoming mail items were piped via a sendmail alias. The program used
|
|
'awk' to extract information such as job and part number, then decoded
|
|
each such item into an appropriately named file in a designated directory.</p>
|
|
|
|
<p>After receiving a part, the program marked it as "complete", then
|
|
set an anti-simultaneity lock and went through a procedure to determine
|
|
if all necessary parts had been received in full. If they had, it
|
|
concatenated them in sequence, piped the result to the nominated printer,
|
|
and deleted them.</p>
|
|
|
|
<p>It was then that I started thinking: "What if there isn't enough room
|
|
to store all the parts for all the jobs which may currently be arriving?"
|
|
And: "How do the Brother people do it on a network card?"</p>
|
|
|
|
<h2>Doing it Without Local Storage</h2>
|
|
|
|
<p>The answer to my second question is: "They use a POP3 server!". The
|
|
components of each job stay on that server until the network card determines
|
|
that all necessary parts are available, at which stage it sucks them down
|
|
and decodes them in sequence, sending the output to the printer mechanism,
|
|
and requests their deletion from the server.</p>
|
|
|
|
<p>So here's how it can be done on a Linux machine. The program has been
|
|
written in Perl so that the NET::POP3 module can be used for easy access to
|
|
a POP3 server. It has been tested on both NetBSD and Solaris machines, so it
|
|
should work almost anywhere; all you'll have to change are the location of
|
|
the Perl interpreter, the name used for 'awk', and the format of the 'lpr'
|
|
command.
|
|
<A HREF="misc/jenkins/BIPprint.pl.txt">[Text version of this listing.]</A>
|
|
</p>
|
|
|
|
<pre>
|
|
#!/usr/bin/perl -w
|
|
# @(#) BIPprint.pl Acquires Brother-Internet-Print files from POP3 server
|
|
# and passes them to designated printer(s). Small-memory
|
|
# version. Intended for invocation via inittab entry.
|
|
# Graham Jenkins, IBM GSA, Feb. 2001. Rev'd: 17 Mar. 2001.
|
|
|
|
use strict;
|
|
use File::Basename;
|
|
use Net::POP3;
|
|
use Date::Manip;
|
|
use IO::File;
|
|
my $host="bronzeback.in.telstra.com.au"; # Same host and password for
|
|
my $pass="MySecret"; # each printer.
|
|
my $limit=30*1024*1024; # Maximum bytes per print job.
|
|
my ($printer,$awkprog);
|
|
defined($ARGV[0]) || die "Usage: ", basename($0). " printer1 [ printer2 ..]\n";
|
|
open(LOG,"|/usr/bin/logger -p local7.info -t ".basename($0)); autoflush LOG 1;
|
|
$awkprog="";
|
|
while (<DATA>) {$awkprog = $awkprog . $_}; # Build awk program for later,
|
|
while (1) { # then loop forever, processing
|
|
sleep 30; # all printers in each pass, and
|
|
foreach $printer (@ARGV) {process($printer);} # sleeping for 30 seconds
|
|
} # between each pass.
|
|
|
|
sub process {
|
|
my ($flag,$i,$j,$k,$l,$m,$allparts,$user,$pop,@field,@part,$count,$top15,
|
|
$msgdate,$parsdate,$notify,$reply,%slot,$fh);
|
|
$user = $_[0];
|
|
$pop = Net::POP3->new($host); # Login to POP3 server and get
|
|
$count = $pop->login($user,$pass) ; # header plus 1st 15 lines
|
|
$count = -1 if ! defined ($count) ; # of each message. Use apop
|
|
for ($i = 1; $i <= $count; $i++ ) { # instead of login if server
|
|
$top15=$pop->top($i,15) ; # supports it.
|
|
if ($top15) {
|
|
$msgdate = ""; $notify="None"; $reply="";
|
|
for ($j = 0; $j < 99; $j++ ) {
|
|
if (@$top15[$j]) { # Use arrival-date on POP3
|
|
if($msgdate eq "") { # server to ascertain age of
|
|
(@field) = split(/;/,@$top15[$j]); # message; if it is stale,
|
|
if ( defined($field[1])) { # delete it and loop for next.
|
|
$parsdate=&ParseDate($field[1]); # (Search for semi-colon
|
|
if( $parsdate ) { # followed by valid date.)
|
|
$msgdate="Y";
|
|
if(&Date_Cmp($parsdate, &DateCalc("today","-3 days") ) lt 0 ) {
|
|
print LOG "Stale msg: $user $parsdate\n";
|
|
$pop->delete($i);
|
|
goto I; # If POP3 server does
|
|
} # automatic message expiration
|
|
} # this entire section can be
|
|
} # omitted.
|
|
}
|
|
(@field) = split(/=/, @$top15[$j]);
|
|
if ( defined($field[0]) ) {
|
|
if ($field[0] eq "BRO-NOTIFY") {chomp $field[1];$notify=$field[1];}
|
|
if ($field[0] eq "BRO-REPLY") {chomp $field[1];$reply =$field[1];}
|
|
if ( $field[0] eq "BRO-PARTIAL" ) { # Comment above line to
|
|
( @part )=split("/", $field[1]); # prevent mail notification.
|
|
chomp $part[1];
|
|
}
|
|
if ( $field[0] eq "BRO-UID" ) { # Determine print-job and part
|
|
chomp $field[1]; # thereof contained in message.
|
|
$slot{$field[1]."=".$part[0]} = $i ;
|
|
$allparts = "Y"; # As we see each message, check
|
|
for ($k=1;$k<=$part[1];$k++) { # whether we have all parts.
|
|
$allparts = "N" if ! defined($slot{$field[1]."=".$k}) ;
|
|
}
|
|
if ( $allparts eq "Y" ) { # Print and delete all parts.
|
|
print LOG "$field[1] $part[1] => $user\n";
|
|
if(($notify ne "None") && ($reply ne "")) {system
|
|
"echo Print Job Received, $part[1] pcs|Mail -s$user $reply";}
|
|
$fh=new IO::File "|awk '{$awkprog}' Limit=$limit |lpr -P $user";
|
|
for ($k = 1;$k<=$part[1];$k++) {
|
|
$pop->get($slot{$field[1]."=".$k},$fh) ;
|
|
$pop->delete($slot{$field[1]."=".$k}) ;
|
|
} # If there is enough filespace,
|
|
$fh->close; # pipe awk output thru gzip to
|
|
} # a temporary file, then print
|
|
} # it and delete all parts; this
|
|
} # caters for connection failure.
|
|
}
|
|
} # The awk program here-under
|
|
} # is used to extract parts from
|
|
I:} # a file containing multiple
|
|
$pop->quit() if ($count >= 0); # parts and feed each of them
|
|
} # through a decoder to stdout.
|
|
__DATA__
|
|
if( Flag == 2 ) {
|
|
Size=Size+length
|
|
if(length == 0) { Flag=0; close("mmencode -u 2>/dev/null") }
|
|
else if(Size<=Limit*4/3) print $0 |"mmencode -u 2>/dev/null" }
|
|
if( Flag == 1 ) if(length == 0) Flag=2
|
|
if( Flag == 0 ) if($1 ~ /^Content-Transfer-Enc/) if($NF == "base64") Flag=1
|
|
</pre>
|
|
|
|
<h2>Program Walk-Through</h2>
|
|
|
|
<p>The program builds a small 'awk' program for later use; then, for each
|
|
printer declared on it's command line, it accesses a mailbox of the same
|
|
name and examines each message therein. If a message is stale, it is
|
|
deleted. Otherwise the contents of some Brother-specific lines are
|
|
extracted; these indicate
|
|
whether email notification is required, and which part of which job
|
|
is contained in the message.</p>
|
|
|
|
<p>If, during examination of a message, it is determined that all the parts
|
|
of its corresponding job have been seen in the mailbox, an email
|
|
notification is generated if required, and the parts are
|
|
extracted in sequence and piped via the 'awk' program (which decodes each
|
|
part as it arrives) to an appropriate printer command. Each part is deleted
|
|
as soon as it has been processed in this manner.</p>
|
|
|
|
<p>Ideally, we should wait
|
|
until success (or other) notification of print submission was obtained
|
|
before performing the email and deletion tasks; however, as noted in the
|
|
listing, this requires some local storage. In a like vein, whilst the
|
|
Brother client software allows selection of email notification for
|
|
several different conditions, we send notification of job submission unless
|
|
"None" has been selected.</p>
|
|
|
|
<h2>Concluding Remarks</h2>
|
|
|
|
<p>This program contains a password, so it should be readable only by the user
|
|
who will execute it. No special privileges are required for execution, and your
|
|
entry for it in /etc/inittab should look something like:
|
|
<pre>bi:345:respawn:su - nobody -c "/usr/local/bin/BIPprint.pl lp1 lp2 >/dev/null 2>&1"</pre>
|
|
|
|
<p>If you have read this far, you are probably saying: "OK, so the program
|
|
doesn't need much local storage - but it sends its output to a print
|
|
spooler! How bad is that?" If the size of your spool area is of
|
|
concern, you can use something like 'netcat' or 'hpnpout' to send the job
|
|
directly to a printer port instead of spooling it. Or you may be able to
|
|
pipe your job through an FTP connection to your printer. If you do
|
|
bypass the spooler in this fashion, you should use a separate instance
|
|
of the program for each printer.</p>
|
|
|
|
<p>It's not rocket science, and there's no user-authentication or
|
|
content-encryption. But it may make your life a little easier. Enjoy!</p>
|
|
|
|
|
|
|
|
<!-- *** BEGIN copyright *** -->
|
|
<P> <hr> <!-- P -->
|
|
<H5 ALIGN=center>
|
|
|
|
Copyright © 2001, Graham Jenkins.<BR>
|
|
Copying license <A HREF="../copying.html">http://www.linuxgazette.com/copying.html</A><BR>
|
|
Published in Issue 65 of <i>Linux Gazette</i>, April 2001</H5>
|
|
<!-- *** END copyright *** -->
|
|
|
|
<!--startcut ==========================================================-->
|
|
<HR><P>
|
|
<CENTER>
|
|
<!-- *** BEGIN navbar *** -->
|
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="correa.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue65/jenkins.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="joshi.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
|
<!-- *** END navbar *** -->
|
|
</CENTER>
|
|
</BODY></HTML>
|
|
<!--endcut ============================================================-->
|