Linux: Scan2Fileserver-Gateway mit einem Multifunktionsdrucker und Postfix

Multifunktionsgeräte mit Scan- und Druckfunktion gibt es mittlerweile zu Hauf. Allerdings ist eine anständige Linux-Treiberunterstützung immer noch Mangelware. Also greift man am besten zu einem Gerät, welches Postscript als Druckersprache unterstützt. Wie aber nutzt man die Scanfunktion, wenn keine Treiber verfügbar sind?
Wenn das Gerät scan2mail unterstützt (d. h. gescannte Dokumente als PDF oder Jpeg per Mail verschickt), kann man sich mit Postfix eine recht elegante Lösung basteln.

Ausgangssituation

Ich verwende als Multifunktionsdrucker einen OKI C5550 MFP, der sich einerseits durch eine große Funktionsvielfalt auszeichnet (Ethernet-Anschluss, Vorlageneinzug, optionale Duplexeinheit, Farbdrucker und -scanner, Scan2Mail), jedoch andererseits keinerlei Linux-Unterstützung seitens des Herstellers erfährt. Da es sich um einen Postscript-Drucker handelt, ist das Fehlen eines Linux-Treibers nicht so tragisch.
Die Scan-Einheit wird weder von Sane noch von den sonstigen üblichen Verdächtigen unterstützt, beherrscht jedoch Scan2Mail. Deshalb kann man das Gerät mit der Hilfe von Postfix und einem Bash-Skript zu einem recht komfortablen Netzwerkscanner aufrüsten.

Das soll folgendermaßen ablaufen:

Ablauf Scannen

Drucker/Scanner

Der Drucker/Scanner muss wissen, wohin er die gescannten Dokumente schicken soll. Dafür trägt man (irgendwo in der Gerätekonfiguration) die Adresse des Servers ein, auf dem Postfix läuft (siehe nächster Punkt).

Postfix

Eine E-Mail ist - grob vereinfacht - eine Textdatei, die man im Klartext lesen kann. Sie besteht aus einem Kopfteil mit Betreff, Sender- und Absenderadresse sowie einem Hauptteil mit der eigentlichen Nachricht. Auch Binäranhänge wie PDF-Dokumente oder Bilder werden in Blöcke von ASCII-Zeichen übersetzt und sind im Hauptteil enthalten.

Zunächst muss Postfix so konfiguriert werden, dass es E-Mails an eine bestimmte Adresse nicht an ein Postfach ausliefert, sondern an ein (noch zu behandelndes) Bash-Skript übergibt. Eine Anleitung hierzu ist (in anderem Zusammenhang) in einem Artikel des Linux NetMags zu finden (Punkt 6.11).

Zunächst muss ein sogenannter Transport definiert werden. Dabei wird Postfix mitgeteilt, dass E-Mails an eine bestimmte Subdomain besonders zu behandeln sind.

Da mein DruckScanner über eine Zahlentastatur verfügt, wähle ich 90 als Subdomain, weil sich das einfach eintippen lässt. Alle E-Mails, die ich an die Adresse irgendwas@90.meinserver sende, sollen an mein Bash-Skript (siehe unten) weiter gereicht werden. „irgendwas“ und „.meinserver“ kann man auch weglassen, das heißt, ich muss auf der Zahlentastatur nur @90 eingeben.

Das Folgende muss in die Datei /etc/postfix/transport eingefügt werden:

# <meinserver> muss durch den Hostnamen des Servers ersetzt werden
90.<meinserver>         mailfile:localhost

Damit wird bestimmt, dass alle Mails an die Subdomain „90“ an den Transport „mailfile“ übergeben werden. Die Konfigurationsdatei muss mittels dem Befehl

postmap /etc/postfix/transport

in ein für Postfix lesbares Format umgewandelt werden und es muss geprüft werden, ob in der Datei /etc/postfix/main.cf die Zeile

transport_maps = hash:/etc/postfix/transport

vorhanden ist, damit die Anpassungen auch wirksam werden.

Jetzt muss Postfix noch gesagt werden, wie genau es E-Mails mit dem Transport „mailfile“ behandeln soll. Hierzu müssen in der Datei /etc/postfix/master.cf die Zeilen

mailfile  unix  -       n       n       -       1       pipe
  flags=    user=scanmail argv=/usr/local/bin/scanmail2file ${recipient}

angefügt werden.

Damit wird festgelegt, dass E-Mails, die vom Transport „mailfile“ behandelt werden, an das Skript /usr/local/bin/scanmail2file übergeben werden. Das Skript wird mit den Rechten des Benutzers „scanmail“ ausgeführt. Dieser muss also existieren (oder die Zeile muss angepasst werden).

Mit dem Befehl

postfix reload

werden die Änderungen aktiviert.

Skript

Postfix bietet Mechanismen, um den Inhalt ein- und ausgehender Mails mittels Filtern zu bearbeiten. Beschrieben ist das unter anderem hier auf der Postfix-Webseite. Das dort beschriebene Skript habe ich abgewandelt, um es an meine Anforderungen anzupassen. Es sollte unter /usr/local/bin/scanmail2file abgelegt werden (Achtung: Die Datei muss ausführbar sein!). Das Skript (und die Beispiel-Konfigurationsdatei) kann auch als Archiv heruntergeladen werden.

#!/bin/sh
#####################################################################
# scanmail2file                                                     #
# Simple shell-based filter. It is meant to be used as postfix mail #
# transport and is invoked as follows:                              #
#                                                                   #
# /path/to/script -f <recipient>...                                 #
#                                                                   #
# Author: yeat (y[]yeat.net)                                        #
#                                                                   #
# It takes a message from stdin, extracts all MIME-parts            #
# (i. e. binary attachments) and saves PS and PDF attachments to    #
# disk.                                                             #
#####################################################################


#####################################################################
# Configuration                                                     #
# adjust variables to your system environment                       #
#####################################################################
#Location of file map
INIFILE=/etc/scanmail/scanmail2file
#Location of logfile
LOGFILE=/var/log/scanmail2file
#Location for temporary files
TMP_DIR=/tmp

#####################################################################
# No user-servicable parts beyond this point...                     #
#####################################################################

RECEIVER=$1
SENDER_NO=$(echo $RECEIVER | sed 's/^.*@\([0-9]*\)\..*$/\1/')
EX_TEMPFAIL=75


# Clean up when done or when aborting.
trap "cd ..; rm -rf mailfax.$$" 0 1 2 3 15

# Check for scan map
[ -f $INIFILE ] || {
    echo $INIFILE does not exist; exit $EX_TEMPFAIL; }

# Start processing.
cd $TMP_DIR || {
    echo $INSPECT_DIR does not exist; exit $EX_TEMPFAIL; }


mkdir mailfile.$$
cd mailfile.$$


cat >in.$$ || {
     echo Cannot save mail to file; exit $EX_TEMPFAIL; }


grep $SENDER_NO $INIFILE | while read PREF DESTINATION PERM; do

  #write all attachments to temporary files
  cat in.$$ | METAMAIL_TMPDIR=. metamail -d -w -q -r

  #copy all PDF, PS, TIF and JPG files to destination
  for fn in $(ls -tr *.[pP][dD][fF] *.[pP][sS] *.[tT][iI][fF] *.[jJ][pP][gG] 2>/dev/null); do

     #filename
     DEST_FN="$DESTINATION/$(date +%Y%m%d%H%M%N)$(echo $fn | sed -e 's/.*\(\..*\)$/\1/')"

     #copy
     cp "$fn" "$DEST_FN"

     #file permissions
     chmod $PERM "$DEST_FN"

  done

done

exit $?

Der Ort für die Ablage der Dateien wird in der Konfigurationsdatei /etc/scanmail/scanmail2file festgelegt. In der hier besprochenen Konstellation sieht sie folgendermaßen aus:

#############################################################################
#sender-id      destination                             permission
#############################################################################
90              /media/data/scanner                     660

Die erste Spalte gibt die Subdomain an, die zweite Spalte definiert das Zielverzeichnis für die gescannten Dateien (Achtung: Der in der Datei master.cf angegebene Benutzer muss Schreibrechte auf das Verzeichnis besitzen!). In der dritten Spalte werden die Zugriffsrechte zugewiesen.

Wenn Postfix eine E-Mail an das Skript übergibt, dann passiert Folgendes:

  • in /tmp ($TMP_DIR) wird ein temporäres Unterverzeichnis angelegt (mailfile.$$) und dort hin gewechselt
  • die E-Mail wird in die Datei in.$$ geschrieben
  • die Dateianhänge der E-Mail werden mittels metamail extrahiert und gespeichert
  • die extrahierten Dateien werden in das entsprechende Zielverzeichnis kopiert, als Dateiname wird ein Zeitstempel verwendet
  • die Zugriffsberechtigungen werden gesetzt
  • das temporäre Verzeichnis wird beim Beenden des Skripts (auch bei fehlerbedingtem Abbruch) gelöscht

Es ist übrigens möglich, mehrere Scan-Ziele als Postfix-Transport und in /etc/scanmail/scanmail2file zu definieren.

Cronjob

Da ich nicht möchte, dass die Festplatte im Server nach und nach mit gescannten Dateien zugemüllt wird, erstelle ich einen Cronjob, der die Dateien nach einer festgelegten Zeit wieder löscht. Dazu wird die Datei /etc/crontab um die Zeile

25  * * * *     root  /usr/bin/find /media/data/scanner/ -mmin +60 -type f -exec rm {} \; &>/dev/null

erweitert. Damit wird dem Cron-Dämon folgendes mitgeteilt:
Suche 25 Minuten nach jeder vollen Stunde im Verzeichnis /media/data/scanner nach Dateien, die älter sind als 60 Minuten und lösche diese.
Die von mir verwendeten Zeiten können natürlich nach Belieben angepasst werden (Angabe „25 * * * *“ ganz am Anfang sowie „-mmin +60“ in der Mitte).