Sieve - A Mail Filtering Language | |
| von Marcel Holtmann |
Sieve ist ein Sprache, die es erlaubt E-Mail Filter zu schreiben. Es gibt keine Bindung an ein spezielles Betriebssystem oder E-Mail Architektur. Die einzige Bedingung ist die Benutzung von RFC822 konformen E-Mail Systemen.
In dem RFC 3028 wird die Sprache Sieve definiert, dessen einzige Aufgabe darin besteht einen Mailfilter zu schreiben. Die Sprache ist nicht "Turing-Complete", d.h. es gibt weder Schleifen, noch Funktionen, noch Variablen und keine Möglichkeit externe Programme aufzurufen. Diese Eigenschaft macht Sieve in der Zeit der E-Mail Viren zu einer relativ sicheren Filtersprache, die sich gut zum serverseitigen Einsatz verwenden lässt.
Man kann Sieve am einfachsten als eine "test-and-action" Skriptsprache bezeichnen, denn sie macht auch nichts anderes. Für jede E-Mail wird das Skript einmal aufgerufen und nach dem Abarbeiten aller definierten Tests gibt es eine oder mehrere Aktionen, die mit der E-Mail verbunden sind. Bei den Aktionen beschränkt man sich wieder auf die wesentlichen.
| keep | behalten |
|---|---|
| discard | ignorieren/löschen |
| redirect | weiterleiten |
| reject | ablehnen |
| fileinto | in einer Mailbox ablegen |
| vacation | eine Abwesenheit anzeigen (nicht in RFC3028 definiert) |
Weitere Aktionen können in anderen RFC's definiert werden. Als Beispiele kann man hier die Bearbeitung von IMAP Flags oder die Benutzung von regulären Ausdrücken nennen. Die Aktion "vacation" ist eigentlich als eine Erweiterung definiert, dennoch ist sie in fast allen Implementierungen standardmäßig vorhanden. Weiterhin sei noch zu erwähnen, das "reject" und "fileinto" auch Erweiterungen sind, welche aber im RFC3028 schon definiert sind.
In Sieve gibt es nur drei unterschiedliche Datentypen. Diese sind Nummern, Zeichenketten und Listen von Zeichenketten. Die Nummern hierbei sind einfache positive ganze Zahlen, die aber einen angehängten Multiplikator in Form eines Buchstaben besitzen dürfen. In der aktuellen Spezifikation sind die drei Multiplikatoren "K", "M" und "G" definiert. Hierbei steht "K" für "kibi-" oder 1.024 (2^10), "M" für "mebi-" oder 1.048.576 (2^20) und "G" für "tebi-" oder 1.073.741.824 (2^30).
25 100K 12M # Als Größenangabe würde es 12 Megabyte entsprechen |
Eine Zeichenkette ist eine Folge von Buchstaben, die zwischen zwei Anführungszeichen (") eingeschlossen ist. Um das Zeichen " darzustellen muss es mit einem Backslash (\) maskiert werden. Der Backslash selber wird dann wiederrum mit einem zweiten Backslash maskiert.
"marcel@holtmann.org" # Als Beispiel für eine E-Mail Adresse "Dies ist ein \"Test\"" # Das Wort "Test" "Ein Backslash (\\)" # Der Backslash an sich |
Der dritte Datentyp ist die Liste von Zeichenketten. Hierbei wird einfach eine Menge von Zeichenketten durch Kommata getrennt zwischen zwei eckigen Klammern gestellt. Wenn man nur eine Zeichenkette in der Liste hat, kann man die eckigen Klammern auch weglassen. Somit ist eine Liste mit einem Element genau das gleiche wie eine Zeichenkette. Mit Hilfe der Listen können sehr komplexe Vergleiche einfach dargestellt werden. In den meisten Vergleichen kann man die Listen als "oder" Verknüpfungen ansehen.
["marcel@holtmann.org", "info@holtman.org"] # Liste von E-Mail Adressen ["XXX", "Porn", "Sex", "Adult" ] # Liste von Subject's "webmaster@holtmann.org" # Ist auch eine Liste |
Die Aktionen "keep" und "discard" sind die elementaren Aktionen. Mit "keep" wird die E-Mail in der Mailbox behalten, wie es auch ohne Filterskript ist. Mit der Aktion "discard" erfolgt eine sofortige Löschung der E-Mail ohne das sie vorher in der Mailbox abelegt wird. Die Auslieferung endet dann im "Mail Delivery Agent" (MDA) ohne das der Absender eine Benachrichtigung darüber bekommt.
Syntax: keep Syntax: discard |
Mit der "redirect" Aktion kann man eine E-Mail an eine andere Mailadresse weiterleiten. Hierbei wird kein Text der Nachricht verändert. Es kann aber passieren, das der "Mail Transfer Agent" (MTA) der Weiterleitungsadresse zusätzliche Header der Mail hinzufügt.
Syntax: redirect <address: string> |
Mit einem einzelnen "redirect" kann man jede ankommende Mail weiterleiten. Will man die E-Mails aber zusätzlich auch noch lokal ablegen, so muss man explizit ein "keep" mit angeben. Soll eine E-Mail an mehrere Adressen weitergeleitet werden, muss jede Adresse mit einer eigenen "redirect" Anweisung angegeben werden. Hierbei muss man natürlich aufpassen, das man keine Mailschleifen erzeugt.
redirect "marcel@holtmann.org"; keep; |
Mit Hilfe von "reject" kann man eine E-Mail zurückweisen und dem Absender mitteilen, warum man diese nicht angenommen hat. Hierbei wird dann eine "Message Disposition Notification" (MDN) zurückgesendet, welche den Grund der Ablehnung beinhalten sollte. Diesen Grund gibt man einfach als Parameter beim Aufruf mit an.
Syntax: reject <reason: string> |
Man kann die Aktion "reject" als eine Erweiterung von "discard" sehen. Während bei "discard" keine Anforderungen an den "Mail Delivery Agent" (MDA) gestellt wurden, muss bei "reject" dieser aber "Message Disposition Notification" (MDN) unterstützen. Darum wurde die Aktion "reject" auch als optionales Feature deklariert und muss mit "require" angemeldet werden (siehe nächsten Abschnitt).
require "reject"; reject "I am not taking mail from you!"; |
Man sollte bei "reject" darauf achten, das man nicht falsche Leute mit solchen Antworten verärgert. Meistens ist ein "discard" die bessere Lösung. Weiterhin sollte man darauf achten den Grund in englische Sprache anzugeben, oder wenigsten eine englische Übersetzung mit anfügen.
Wenn man einen IMAP Server benutzt, der mehrere Unterordner in der Mailbox unterstützt, ist es sinnvoll die E-Mails in verschiedene Ordner zu vorsortieren. So kann man zum Beispiel die Nachrichten einer Mailingliste in einem Ordner zusammenfassen und die Liste somit besser verfolgen. Bei Sieve gibt es hierfür die Aktion "fileinto".
Syntax: fileinto <folder: string> |
Als Parameter wird der Ordner angeben, in dem die E-Mail abgelegt werden soll. Hierbei ist zu beachten, das man sich an die Namenskonvention des IMAP Server halten muss, da ansonsten nur Fehler bei der Ausführung des Skriptes entstehen.
require "fileinto"; fileinto "INBOX.Spam; |
Auch die Aktion "fileinto" ist als Erweiterung definiert, weil man nicht davon ausgehen kann, das Sieve immer auf einem IMAP Server eingesetzt wird. Dennoch macht diese Aktion die Sprache erst so richtig mächtig. Wer einmal mit auf dem Server vorsortierten Nachrichten gearbeitet hat, möchte dieses nicht mehr missen!
Es gibt drei verschiedenen Datentypen und es gibt auch drei verschiedene Arten von Kommandos. Als erstes sind hier die schon erwähnten Aktionen zu nennen und dann gibt es noch Tests und Kontrollkommandos. Bei den Kontrollkommandos handelt es sich im wesentlichen um drei Stück.
Das Kommando "require" darf nur am Anfang eines Skriptes stehen. Hiermit wird dem Interpreter mitgeteilt welche zusätzlichen Funktionalitäten man benutzen möchte.
Syntax: require <capabilities: string-list> |
Als einzigen Paramter hat das "require" Kommando eine Liste von sogenannten "capabilities". Es können auch mehrere "require" Anweisungen hintereinander benutzt werden. Somit können die Parameter als Liste angegeben werden, oder man benutzt für jeden Parameter eine eigene "require" Anweisung.
require ["fileinto", "reject"]; require "vacation"; |
Mit dem Kommando "stop" wird jeder Abarbeitung des Skriptes beendet. Wurde keine Aktion ausgeührt wird die Aktion "keep" als letztes Kommando angenommen. Die gleiche Funktionalität gilt für das Ende eines Skriptes. Somit ist die "keep" Aktionen die Voreinstellung, bis sie durch eine andere Aktion abgelöst wird.
Das dritte wichtige Kommando ist die aus allen Programmiersprachen bekannte "if-elsif-else" Bedingung. Hiermit können Aktionen in Abhängigkeit von dem Ausgang eines Tests ausgeführt werden. Diese Kontrollstruktur ist die einzige, die aus den Programmiersprachen mit übernommen wurde, denn Schleifen oder ähnliches gibt es nicht.
Syntax: if <test1: test> <block1: block> Syntax: elsif <test2: test> <block2: block> Syntax: else <block> |
Auf die Definition von "test" geht der nächste Abschnitt genauer ein. Die "block" Struktur ist eine einfache Ansammlung von Aktionen, weiteren "if-elsif-else" Strukturen und/oder dem Kommando "stop". Die einzelnen Einträge müssen mit einem Semikolon beendet werden. Die ganze Gruppe wird in geschweifte Klammern gestellt.
if size :over 1M {
discard;
stop;
}
else {
keep;
stop;
}
|
Hierbei ist zu beachten, das "if", "elsif" und "else" bei der Definition der Sprache getrennte Kommandos sind. Dennoch darf ein "elsif" bzw. "else" nur auf eine "if" oder "elsif" Anweisung folgen.
Die Tests sind die elementaren Entscheidungen und das Wichtigste.
Mit der "vacation" Erweiterung wird ein Autoresponder implementiert, der eine Benachrichtigung an den Absender eine E-Mail zurück senden kann, um zum Beispiel eine Abwesenheit mitzuteilen. Diese Erweiterung ist analog zu dem unter Unix verfügbaren Programm vacation und fordert in der einfachen Form nur die Angabe eines Textes, der ähnlich wie bei "reject" dem Absender der E-Mail zurück gesandt wird.
Syntax: vacation [":days" number] [":addresses" string-list]
[":subject" string] [":mime"] <reason: string>
|
Mit dem ":days" Parameter wird angegeben, in welchem Zeitraum der Absender eine Benarichtigung erhält. Auf jede weitere Meldung in diesem Zeitraum folgt dann keine automatische Antwort. Erst nach verstreichen dieser Zeit kann es zu einer zweiten Nachricht kommen. Der Standardwert entspricht hier sieben Tagen, also einer Woche. Das Minimum ist 1 Tag und das Maximum sollte nicht 30 Tage überschreiten.
Mit dem ":addresses" Parameter kann der Benutzer verschiedene E-Mail Adressen angegeben auf die kontrolliert werden soll. Das "vacation" Kommando wird dann nur ausgeführt wenn sich eine dieser E-Mail Adressen in den Headerzeilen "To:", "Cc:" oder "Bcc:" befindet. Wenn ein Benutzer zum Beispiel zwei E-Mail Adressen hat, die er zu einer Mailbox weiterleiten lässt auch hierauf Rücksicht nehmen.
require "vacation"; vacation :days 7 :addresses "marcel@holtmann.org" "Ich bin außer Haus und werde Ihre E-mail erst in einer Woche lesen. Wenden Sie sich in dringenden Fällen bitte an ..."; |
Normalerweise enthält die zurückgesendete E-Mail das gleiche Subject wie die ursprüngliche E-Mail, nur das sie um den Präfix "Re:" ergänzt wurde, falls dieser noch nicht vorhanden war. Mit dem Parameter ":subject" kann nun dieses Verhalten gesteuert werden und ein beliebiges Subject angegeben werden.
Mit dem vierten Parameter kann man angeben, ob die zurückzusendende Nachricht reiner Text ist, oder ob sie ein MIME Teil einer Nachricht mit den dazugehörenden Headerzeilen ist.
require "vacation"; vacation :subject "Ich bin im Urlaub :-)" :mime "MIME-Version: 1.0 Content-type: text/plain; charset=us-ascii Eine Nachricht mit Header ..."; |
# Example Sieve Filter
# Declare any optional features or extension used by the script
require ["fileinto", "reject"];
# Reject any large messages
if size :over 1M {
reject "Please do not send me large attachments.";
stop;
}
# Handle messages from known mailing lists
# Move messages from IETF filter discussion list to filter folder
if header :is "Sender" "owner-ietf-mta-filters@imc.org" {
fileinto "filter";
# move to "filter" folder
}
|
A Protocol for Remotely Managing Sieve Scripts
Sieve Vacation Extension
Sieve extension for modification of flags on an IMAP server
Sieve Instant Notification Extension
Subaddress Extension
Regular Expression Extension
Relational Tests Extension
Sieve Homepage (auf Englisch)
Sieve RFC 3028 (von der IETF Seite)
Sieve: Einrichten von Mailfiltern für IMAP
Mail-Filter mit Sieve an der TU Chemnitz
Sieve, Server-Side E-mail Filter at Nada (auf Englisch)