!!! Eigenes CUPS Backend schreiben

''Wer CUPS nicht kennt oder allgemeinere Infos dazu sucht, findet
unter DruckenMitCUPS weitere Informationen.''

CUPS ist ein sehr stark modularisiertes Drucksystem. Dabei gibt es
für den eigentlichen Ausdruck, d.h. den Zugriff auf die Hardware
oder auch das Netzwerk die sogenannten '''backend'''s. Diese sind
sozusagen Hardware- und Schnittstellentreiber. Falls man mit
Druckjobs ausgefallene Sachen machen will, sind diese backends ein
guter Ansatzpunkt. So kann man über die CUPS-Homepage z.B. Links zu
Backends finden, die einen Druckbefehl per ssh auf einem fernen
Rechner ausführen, oder die eine Mail aus dem Druckjob generieren.

Da ich selber ein spezielles Problem hatte, habe ich beschlossen,
ein solches Backend selbst zu schreiben. Nach einigen Tücken war
das Ganze im Prinzip gar kein Problem.

!! Was ist ein backend?

Ein backend ist ein Shell-Programm (kann ein Executable sein, kann
aber auch ein Shell-Script oder ein Perl-Programm sein). Wie dieses
auszusehen hat, erfährt man auf der [CUPS-Homepage|http://www.cups.org]
im [Software Programmers Manual|http://cups.org/spm.html#WRITING_BACKENDS],
aber auch im Download-Bereich sollte
man sich umsehen und z.B. das ssh-backend (ein Shell-Script) und
das mailto-backend (in Python) ansehen.

Es gibt zwei Möglichkeiten, wie CUPS das backend aufruft:

Beim __Serverstart__ wird das Programm ohne Parameter aufgerufen.
Es kann daraufhin zeilenweise Informationen über verfügbare Drucker
ausgeben. Hierbei kann es sich um "fertige" Drucker-URIs handeln,
wenn eine komplette URI der Form
{{backend:/pfad}} angegeben wird oder aber
auch um ein Template, wenn nur {{backend}}
angegeben wird. Ich demonstriere unten beides, was dazu führt, daß
ein Drucker in der CUPS-Oberfläche direkt angewählt wird (Mail an
root), während bei der anderen Auswahl erst noch eine URI
(Mailadresse) eingegeben werden kann.

Beim __Ausdruck__ werden dem Programm 5 oder 6 Parameter
übergeben, die Informationen über den Druckjob enthalten.
Darüberhinaus gibt es noch einige interessante Umgebungsvariablen,
die man benutzen kann (z.B. für die Device-URI).

!! Installation

Selbstgeschriebene Backends werden einfach in das Verzeichnis für
Backends (bei Debian ''/usr/lib/cups/backend'') kopiert. Es ist
darauf zu achten, das die Datei ausführbar ist (ggf. ''chmod a+x
name'') und fertig!

!! Mein eigenes Backend

Zum Testen habe ich ein Backend __mail__ geschrieben. Man kann
eine URI der Form {{"mail:/tbayen@mailserver"}} angeben und bekommt die
Druckjobs als Mail zugestellt. Ich habe bisher als Druckertreiber
"raw" eingestellt und bekomme die Druckdateien dadurch genauso
zugesandt, wie der Client sie abschickt. Es dürfte allerdings auch
möglich sein, einen Postscript-Treiber auszuwählen und so immer
Postscript zugeschickt zu bekommen, das man dann im Mail-Programm
nur noch anzuklicken braucht, um es anzuzeigen.

Besonderes zu meinem Quelltext werde ich hier kurz fassen, da ich
versucht habe, die Dokumentation in den Quelltext zu packen. Also
RTFS! Übrigens ist der Code sicher auch interessant zum Thema
"Mailversand in Perl". Die längste Arbeit war nämlich, den Versand
des Druckjobs als Attachment richtig hinzubekommen. -- ThomasBayen

! Source

{{{
#!/usr/bin/perl
use strict;
use warnings;
use FileHandle;
use MIME::Entity;

# CUPS-Backend, um aus einem Druckauftrag eine Mail zu generieren.
# Die Device-URI muss die Form
#
#   mail:/user@computer.domain
#
# haben. Alles andere geht dann von selbst :-) Ein Device für den
# Versand an root@localhost wird dem Benutzer bei der Einrichtung
# schon vorgegeben.
# benötigt Debian-Paket libmime-perl

unless(@ARGV){
  # Wird kein Parameter angegeben, will CUPS nur Infos über
  # angeschlossene Drucker. Die sind Zeilenweise in der Form
  # class uri model description
  # class: file, direct, serial, network
  print 'network mail "Unknown" "EMail-Address Mailer"',"\n";
  print 'network mail:/root@localhost "Unknown" "Mail to
root"',"\n";
  exit;
}

# Jetzt extrahiere ich die Parameter, die für mich interessant
sind.
# Entgegen der Dokumentation gibts nur 5-6 statt 6-7 Parameter
# (printer als erstes wird nicht angegeben)

if($#ARGV<4 or $#ARGV>5){
  print STDERR "ERROR: mail job-id user title copies options
[file]\n";
  exit 1;
}

my($job,$user,$title,$copies,$options,$filename)=@ARGV;
my($deviceuri)=($ENV{DEVICE_URI} or '');
my($email)=($deviceuri=~m!^mail:/(.*)$!);

# Druckdaten lesen und MIME-Type feststellen:
my($file,$type);
if($filename){
  $type=`file -bi $filename`;
  open $file, "<$filename";
}else{
  $file=*STDIN;
  $type='application/octet-stream';
}
local $/;  # slurp mode
my $text=<$file>;

# Mail erzeugen:
my $mail=MIME::Entity->build(
  From   =>"CUPS mailer ($user)",
  To     =>$email,
  Subject=>"CUPS printjob '$title'",
  # Mail hat attachments und bestsht daher aus zwei Teilen:
  Type   =>'multipart/mixed',
);
$mail->attach(Data=><<"EOT");
Hello $email,

this mail contains a printjob. It was send to you from the CUPS
mail-backend.

Device-URI.......: $deviceuri
Job-ID...........: $job
Sending User.....: $user
Documenttitle....: $title
Number of Copies.: $copies
MIME-Type........: $type

CUPS-backend 'mail' written by Thomas Bayen.
EOT
$mail->attach(
  Data       =>$text,
  Type       =>$type,
  # Druckjob wird nicht "inline" angezeigt, sondern immer als
Attachment:
  Disposition=>'attachment',
  # Encoding, damit Sonderzeichen und Binärdateien nicht kaputt
gehen:
  # Encoding  =>'quoted-printable',  # hängt sich leider weg!?!
  Encoding   =>'base64',              # geht aber auch
);

$mail->send;

# So gehts auch:
#open PIPE,"|sendmail -t";
#print PIPE $mail->stringify;
#close PIPE;

exit 0;
}}}

[{Tag Drucker}]