Virtual Email ------------- Release-History --------------- 1.0 9. August 2001 Erstes Release 1.1 10. August 2001 -SMTP-Auth hinzugefügt -Bugfix im authdaemon: Er beendet sich nun sauber und tötet alle Kinder. Zudem erstellt er nun das File, in das er seine PID schreibt (gut für ein Stop-Skript). -Bessere Erläuterungen zum Erstellen von Usern. -Hinweise auf ein paar Mail-Lese-Web-Frontends in der FAQ 1.11 11. August 2001 Catchall-Aliases ermöglicht. 1.12 13. August 2001 -Fixed bug: Der Auth-Daemon authentifizierte case-insenstive, gab aber das Maildir case-sensitive an den IMAP-Server weiter. Die Folge: hat sich ein User mit gross-Schreibung angemeldet, wurde ein neues Maildir erstellt, anstatt auf das alte verweisen. -Hinweise zum automatischen Start und Beenden des authdaemon eingefügt. 1.2 17. August 2001 -Neue Tabellenstruktur: Das Konzept von Sites wurde eingeführt (siehe unten) -qualify_preserve_domain in den Aliases eingefügt fixt einen bug mit @-losen aliases und automatischem Einfügen der Domain -In überiggebliebenen DB-Strukturen INDEX-Optimierungen vorgenommen. 1.21 18. August 2001 -Bug gefixt: Mit der Einführung des neuen Datenbank-Schemas hat sich ein Bug in den authdaemon geschlichen, der authentifizierungen verunmöglichte. 1.30 27. August 2001 (spät) -Neues Script zum Erstellen des .CDB-Files. Ist nun in perl geschrieben und verwendet ein natives Perl-Modul. -Jeder user kann ein Exim-Filter-File haben, um seine Mail schon auf dem Server zu filtern. Hinweise zur Zusammenarbeit mit Courier eingefügt. Noch immer: Direkte Zustellung per Exim. Kein externer Prozess muss für die Auslieferung gestartet werden. -Der authdaemon kann als root gestartet werden und lässt automatisch seine Privilegien fallen. 1.31 06. September 2001 Entwarnung wegen des Filter-Hacks eingefügt. Phil (der Autor von Exim) hat die Unbedenklichkeit des Hacks bestätigt. 1.32 07. September 2001 Bessere Erläuterungen über die Architektur des authdaemon eingefügt, da in der Newsgruppe diesbezüglich Fragen aufgetaucht sind. Wie immer: Ich bin für jedes Feedback dankbar, das dieses Dokument besser zu machen vermag. 1.33 11. September 2001 Syntax-Fehler im Authdaemon gefixt 1.34 17. September 2001 Courier kann das per authdaemon übermittelte quota nicht selbst auswerten, sondern benötigt dazu eine Datei namens maildirsize im Maildir des Users. Erstelle nun diese Datei im Auth-Server. Damit kann das Quota nicht mehr übergangen werden, wenn Daten mit einem IMAP-Client hochgeladen werden. Dank an Oliver Siegmar für den Bugreport. 1.35 12. November 2001 -Zusammenführung von PMAIL und PMAIL UI und damit zusammenhängende Änderungen wie: Ausgliederung der beiden Perl-Skripte und der SQL-Statements zum Anlegen der Tabellen und weitere kleine Modifikationen 1.36 21. November 2001 -Namensänderung von PMAIL zu XAMS Diese Datei bleibt aber unverändert, da es keinen Sinn macht sie in deutscher Sprache weiter zu führen. Nachdem in de.comm.software.mailserver immer wieder die Frage nach virtuellem Email auf Basis einer Datenbank aufkommt und weil ich ohnehin ein neues System designen musste, denke ich, dass ich meine Konfiguration hier der Öffentlichkeit vorstellen kann. Dieser Artikel entstand auf Basis einer Frage nach Stabilität des Courier IMAP-Servers (Message-ID: io6lmtkfcnh2ob8g8hplgnscini2fhk6b7@4ax.com), die ich am 3. August 2001 in obiger Newsgruppe gestellt habe. Die hier vorgestellte Konfiguration unterstützt *) die Unabhängigkeit von UNIX-Usern *) unbegrenzt viele Domains *) unbegrenzt viele Adressen mit individueller Einstellung des Quota, sowie die Möglichkeiten des Abrufs (IMAP, POP oder beides) *) unbegrenzt viele Aliases *) Domain-Aliases: es soll möglich sein, dass ein- und derselbe User mehreren Domains angehört. Lies dazu den Abschnitt "Sites" im Kapitel "Datenstruktur" (new in Version 1.2) *) komplett normalisierte Datenstruktur (lies' dazu ebenfalls die umfangreiche Beschreibung der Datenstruktur) *) kein Abspalten von zusätzlichen Prozessen für die lokale Mail-Auslieferung Was noch fehlt sind kleinere Aufräum-Bots, aber auch eine lösche-alte-nachrichten-automatisch Funktion, wie GMX sie kennt. Dies ist eine super-kurze Anleitung: Ich habe die letzten 3 Nächte nicht gerade viel geschlafen und hasse eigentlich wenig mehr, als technische Artikel zu schreiben. Dazu kommt, dass ich möchte, dass Du die Anwendungen *verstehst*, die Du da am konfigurieren bist. Ich gebe daher absichtlich keine Installationsanweisungen. Lies' zumindest die INSTALL-Files der jeweiligen Packete, damit du wenigstens eine Ahnung hast, um was es in der Software geht. Internet Mail ist eine sehr kritische Anwendung und erfordert viel Fingerspitzengefühl. Dazu gehört auch, dass der Administrator fähig ist, die Software zu verstehen, die sich um die Mails kümmert. Wer noch (konkrete!) Fragen oder Verbesserungen hat, soll sie an mailen, damit ich sie beantworten und dieses Dokument erweitern kann. Die Konfiguration greift auf folgende Programme in aktuellen Versionen zu, sollte aber auch mit späteren Versionen ohne Modifikation laufen (kein Sourcecode-Patch findet Verwendung), was meiner Ansicht nach ein echter Vorteil gegenüber vergleichbarer Packete ist; der andere ist die normalisierte Datenstruktur, sowie das Konzept der Sites, das recht einzigartig ist. *) Exim *) Courier IMAP-Server *) MySQL (Postgres ginge auch) *) Perl 5 *) CDB_File-Perl-Modul. Erhältlich im CPAN (CDB::Simple ginge auch, ist aber wesentlich langsamer, da 100% Perl) Das PHP-Scriptchen, das in früheren Versionen hier genannt wurde, ist nicht mehr erforderlich. Das CDB-File (siehe unten) wird neu mit Perl erstellt. Im Folgenden wird die Konfiguration dem Weg eines Mails folgend beschrieben: Vom MTA, über die Speicherung zum IMAP/POP-Server, wobei zuvor aber noch die Datenstruktur definiert wird, da auch der MTA bereits auf sie zugreifen muss. Datenstruktur ------------- Die Datenbank sollte folgende Tabellen enthalten. Eine Erklährung der Funktion der einzelnen Tabellen/Werte kannst Du weiter unten lesen. Achte auf alle Fälle darauf, die Indizes ebenfalls zu erstellen. Ohne könnte die ganze Sache extrem langsam werden. Die Dinger sollten optimal eingerichtet und getunded (lies weiter unten) sein, so dies mit MySQL möglich ist. Möglicherweise ist mir aber noch der eine oder andere, sinvolle Index unter die Lappen gegangen... Die Konfiguration benötigt *alle* Tabellen (wobei Du natürlich auf die Aliases verzeichten kannst) -------------------8<---------------------------------------- Zum erstellen der Datenbank: mysql -u -p > create database pmail; >quit; Zum Erstellen der Tabellen in der Datenbank: cat pmail.sql |mysql pmail -u -p # Ein paar Beispieldatan: INSERT INTO pm_sites (ID, Name, MaxQuota, MaxAddr, MaxAliases, AddrType) VALUES (1,'pilif','','','',''); INSERT INTO pm_sites (ID, Name, MaxQuota, MaxAddr, MaxAliases, AddrType) VALUES (2,'gnegg','','','',''); INSERT INTO pm_domains (Name, SiteID) VALUES ('gnegg.here',2); INSERT INTO pm_domains (Name, SiteID) VALUES ('gnegg.there',2); INSERT INTO pm_domains (Name, SiteID) VALUES ('pilif.here',1); INSERT INTO pm_domains (Name, SiteID) VALUES ('pilif.there',1); INSERT INTO pm_users (ID, SiteID, Name, Password, Quota, AddrType, RelayOnAuth) VALUES (1,1,'pilif',MD5('passwort'),'','i,p','y'); INSERT INTO pm_users (ID, SiteID, Name, Password, Quota, AddrType, RelayOnAuth) VALUES (2,1,'user', MD5('passwort'),5000,'p','n'); INSERT INTO pm_users (ID, SiteID, Name, Password, Quota, AddrType, RelayOnAuth) VALUES (3,2,'gnegg',MD5('passwort'),'','i,p','y'); INSERT INTO pm_users (ID, SiteID, Name, Password, Quota, AddrType, RelayOnAuth) VALUES (4,2,'user', MD5('passwort'),5000,'p','n'); INSERT INTO pm_aliases (SiteID, LeftPart, RightPart) VALUES (2,'blepp','pilif@pilif.here'); INSERT INTO pm_aliases (SiteID, LeftPart, RightPart) VALUES (1,'*','pilif'); -------------------------------------------->8---------------- Drei Tabellen alleine für ein paar User und Domains? Eine Frage, die sich schnell mal aufdrängen kann, genauso wie die Frage, was denn eine "Site" ist. Für einen nicht-DB-Freak (ich für meinen Teil liebe die Dinger), kann das alles etwas unübersichtlich wirken. Lass' mich daher ein paar Worte über die Strukur, aber auch über deren Geschwindigkeit verlieren: Begriffe ~~~~~~~~ Site **** In Versionen vor 1.2 dieses Dokuments, war das Konzept der "Sites" nicht bekannt: Domains galten in damals als Contianer für die Accounts und Aliases. Diese Struktur erlaubte es damit bereits, dass die Adressen pilif@gnegg.home und pilif@pilif.home nicht die gleichen Adressen waren. Nun bin ich mit dieser Struktur aber in ein Real-World-Problem gerannt, dass sie nicht darzustellen in der Lage ist. Dies sei an einem Beispiel dargelegt: Ein Projekt der Firma in der ich arbeite (und Teilhaber bin), ist das schweizer Breitband-Portal superspeed.ch, das auch unter den Adressen ultraspeed.ch, turbospeed.ch, megaspeed.ch, aber auch adsl.ch und (bald) dsl.ch zu erreichen ist. Unter allen Domains bekommt der Nutzer die gleiche Website zu sehen, was bei einem Besucher schnell den Eindruck der Gleichheit erwecken kann, was wiederum dazu führt, dass er für die Mail-Kommunikation schnell mal seine "Lieblingsdomain" hinter dem @-Zeichen verwendet, mich also je nach Präferenz über pilif@superspeed.ch oder pilif@adsl.ch oder gar pilif@ultraspeed.ch zu erreichen versucht. Sind domains Container für User, so kann das gewünschte Verhalten nicht erreicht werden. Sollen Mails an pilif@superspeed.ch bei der gleichen Person/dem gleichen Postfach wie pilif@adsl.ch landen, so muss bei der einen Domain ein passendes Alias eingerichtet werden. Bei 5 Domains und 10 Adressen ein sehr mühsames Unterfangen, das sehr anfällig gegenüber Konfigurationsfehler ist (schnell ist eine Adresse vergessen). Wie immer in solchen Fällen, muss die Datenstruktur erweitert werden. Das Konzept der Sites mag dieses Problem zu lösen: Eine Site ist ein "Ding", das mehrere Accounts und/oder Aliases enthält, und das mindestens einen Domain-Namen hat, unter dem es zu erreichen ist. Das Ding selbst hat ebenfalls einen passenden, systemweit eindeutigen Namen. Dieser Name wird von Exim und Courier verwendet, wenn sie den Namen des passenden Maildirs finden: /woauchimmer/<>/<> Domain ****** Eine Domain ist eine Art Adresse, unter der eine Site (ein Ding) vom Internet aus erreicht werden kann. Jede Site hat mindestens eine Domain unter der sie zu erreichen ist (naja... sollte haben; sonst ist sie halt verwaist, kann aber durchaus existieren -> eine Art, ganze Sites lahmzulegen). Eine weitere Limitation, nicht durch das Datenmodell, sondern durch Exim limitiert: Eine Domain kann nur einer Site zugeordnet werden, will heissen: Man kann eine Domain nur für eine Site verwenden. pilif.home kann damit z.B. nur der Site 2 zugeordnet werden; nicht auch noch der Site 3 (wäre die Limitation in Exim nicht da, könnte man das als eine Funktion zum Erstellen von Kopien verwenden). Durch passende Unique-Indizes ist das nun bereits in der Datenbank abgeschaltet. Eine Domain spezifiziert Maximalwerte für Quoten, Account-Zahlen, sowie Alias-Zahlen, wobei Einhaltung dieser Maximalwerte bei MySQL allerdings Sache des passenden Administrationstools (noch nicht implementiert) ist. Eine richtige Datenbank würde mit Rules oder je nach dem trigger helfen, die Einschränkungen zu erzwingen. Account ******* Ein Account ist ein Empfänger in einer Site, der ein eigenes Maildir auf dem Server angefügt hat und dessen Mails über POP3, IMAP oder beiden Protokollen ausgelesen werden können. Ein Account hat einen Namen, ein Passwort, sowie ein Quota und eine definierte Art der erlaubten Abfrage der Mails (POP3 oder IMAP4). Zudem ist für jeden Account definiert, ob der dazugehörige Nutzer sich per SMTPAUTH beim SMTP-Server authentifizieren darf, um den Server als Relay zu gebrauchen. Grosse Sites sollten das vielleicht abschalten und selektiv wieder einschalten. Spammern sollte man es ohnehin sofort abschalten. Accounts sind einer Site zugeordnet: Jede Site darf beliebig viele Accounts haben, aber jeder Account hat genau eine Site zu der er gehört (aber wiederum beliebig viele Domains, da jede Site ja beliebig viele Domains haben kann) Alias ***** Ein Alias ist, wie ein Account, ein Ding einer Site, das in der Lage ist, ein Mail zu ampfangen. Im Gegansatz zu einem Account ist einem Alias aber kein Maildir zugeordnet, sondern bloss eine Information, an welche Adresse das Mail weitergeleitet werden soll. Damit benötigt ein Alias keinen Speicherplatz auf der Festplatte des Servers. Ein spezielles Alias ist das Alias mit dem Namen "*": Es ist ein Platzhalter für alle nirgendwo sonst definierten Empfäner einer Site und dient damit als Sammelbecken für alle eingehenden Mails einer Site. Wie Accounts kann jede Site beliebig viele Aliases haben, aber jedes Alias nur zu einer Site gehören, aber wiederum zu beliebig vielen Domains, da jede Site von beliebig vielen Domains adressiert werden kann. Empfänger ********* Ein Empfänger ist entweder ein Alias oder ein Account einer Site. Er kann durch eine der Domains einer Site erreicht werden. Ein Epmfänger wird repräsentiert durch jeweils genau ein Eintrag in pm_users oder pm_aliases (mit der Ausnahme des *-Aliases, das für *jeden* Empfänger, der nicht bereits anderswo eingetragen ist steht). Zu den Beispieldaten ~~~~~~~~~~~~~~~~~~~~ Die Beispieldaten oben erzeugen zuerst zwei Sites, pilif und gnegg mit den Default-Einstellungen (alles andere wird eh bloss von den Administrationstools, die noch nicht geschrieben sind, verwendet). Diesen beiden Sites werden je zwei Domains zugoerdnet: - pilif.here pilif - pilif.there - gnegg.here gnegg - gnegg.there Beide Sites bekommen nun je zwei User. Einer mit uneingeschränkten Einsttellungen und einen mit etwas mühsamen Einschränkungen (in eckigen Klammern steht der Name der Site, der in beiden Fällen ja 2 Domains entspricht): pilif@[pilif] ohne Quota, mit SMTPAUTH und POP wie IMAP user@[pilif] mit 5000 KB quota, ohne SMTPAUTH und bloss POP gnegg@[gnegg] ohne Quota, mit SMTPAUTH und POP wie IMAP user@[gnegg] mit 5000 KB quota, ohne SMTPAUTH und bloss POP Rein exemplarisch werden nun zwei Aliases erstellt: blepp@[gnegg] leitet alles an pilif@pilif.home weiter (siehe oben) pilif@[pilif] bekommt alles, was an [pilif] gesendet wird und vor dem @ nicht "user" hat. Performance ~~~~~~~~~~~ Datenbank-Kenner werden es gemerkt haben: Diese Struktur erfordert ein paar JOINS; pro Empfäner und Abfrage (es sind pro Mail 1-3: Eine für die Accounts, eine für die Aliases und eine für das *-Alias) wird ein Cross-Join über 3 Tabellen benötigt, was vor allem bei grossen Datenmengen extrem auf die Performance drücken kann. Angenehmerweise sind bereits recht brauchbare Indizes erstellt, so dass das Hauptargument gegen die JOINs, die lahme Performance, zumindest bei MySQL, etwas entschärft wird (eigentlich sogar: "total entschärft. Siehe die erfreulichen Werete unten). Ich habe einen Testlauf gemacht, in dem ich 1000 Sites erstellt habe, jeder dieser 1000 Sites 50 Domains und jeder Site 1000 User (insgesamt also 1 Million Einträge in pm_users) zugeordnet habe. Die Tests, die ich darauf mit dem MySQL-Kommandozeilentool durchgeführt haben, sprechen für sich: mysql> explain -> select -> u.ID, u.Quota -> FROM -> pm_domains d, pm_sites s, pm_users u -> WHERE -> s.ID = d.SiteID -> AND -> u.SiteID = s.ID -> AND -> u.Name = 'bejy4elpyq' -> AND -> d.Name = 'ogxore1xwoas'; +-------+-------+---------------------+----------+---------+-------------+------+-------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +-------+-------+---------------------+----------+---------+-------------+------+-------+ | d | const | PRIMARY,Name,SiteID | PRIMARY | 150 | const | 1 | | | s | const | PRIMARY | PRIMARY | 4 | const | 1 | | | u | const | DomainID | DomainID | 104 | const,const | 1 | | +-------+-------+---------------------+----------+---------+-------------+------+-------+ 3 rows in set (0.00 sec) mysql> select -> u.ID, u.Quota -> FROM -> pm_domains d, pm_sites s, pm_users u -> WHERE -> s.ID = d.SiteID -> AND -> u.SiteID = s.ID -> AND -> u.Name = 'bejy4elpyq' -> AND -> d.Name = 'ogxore1xwoas'; +--------+-------+ | ID | Quota | +--------+-------+ | 855700 | NULL | +--------+-------+ 1 row in set (0.00 sec) mysql> Das Schöne sind die "const"'s, die MySQL bei ref verwendet. Damit wird die Sache ungemein schnell und führt zu den noch schöneren Rows-Werten von 1. Die Abfrage ist die, die für das Quota des Accounts "bejy4elpyq@ogxore1xwoas" gebaurcht wird; die ID des Users nur noch zur Kontrolle ausgegeben. Schön ist die Zeile "1 row in set (0.00 sec)", die MySQL nach der Abfrage ausgibt: Weniger als eine Hundertstelsekunde für die Abfrage scheint bei dieser Menge an Accounts durchaus noch akzeptabel. Damit ist das System, und das freut mich besonders, zu sagen, auch für extrem hohe Ansprüche nicht nur flexibel, sondern auch performant genug. Da nun die Datenstruktur geklärt ist, kommen wir bereits zur Konfiguration des MTA. Exim ---- Exim kann ganz normal nach den Präferenzen des Administrators in ein beliebiges Verzeichnis unter einem beliebigen User installiert werden; der User sollte bloss einer sein, der Zugriff auf die Maildirs hat, auf die dann auch der IMAP-Server zugreift. Ich habe hier den user "pilif" gewählt, weil ich damit meine Demo-Installation schnell ändern konnte, ohne in eine ungewohnte Umgebung zu su-en. Falls Du das System in einer produktiven Umgebung nutzen willst, solltest Du vielleicht einen User einrichten, der sonst auf keine anderen Dateien Zugriff hat. Exim solltest Du selbstverstädnlich mit MySQL-Support kompilieren. Im Makefile muss dazu ein Kommentar entfernt werden. Keine Sorge: Das ist gut dokumentiert. Ebenfalls mitkompilieren solltest Du den Support für CDB-Files; zumindest, wenn Du meiner Konfiguration hier genau folgst. Lies' dazu weiter unten weitere Infos. Ebenfalls weiter unten findest Du eine Erklährung, wie User-definierte Filter in das System eingebunden werden können, die eine Filterung der Mails (auch in IMAP-Ordner) noch auf der Serverseite erlauben. Im folgenden die Änderungen, die Du an deiner Exim-Konfiguration vornehmen solltest: -------------------8<---------------------------------------- # local_domains enthält die domains, die Exim als "lokal" ansehen soll # wie Du zum .cdb-File kommst, und warum es da ist, steht im nächsten # Abschnitt. local_domains = cdb;/pfad/zu/einem/file/pdoms.cdb # Die Einstellungen zum SQL-Server. pmail ist der Name der Datenbank, # user ein User, der SELECT auf die obigen Tabellen ausführen darf und # passwort dessen Passwort. hide mysql_servers = localhost/pmail/user/passwort # willst Du authentifiziertern Usern das Relayen erlauben, so füge # bitte noch folgende Option hinzu: host_auth_accept_relay = * # nun zu den Transports. Füge den folgenden hinzu: # Das Verzeichnis /pfad/zu/den/maildirs muss existieren und der # Exim-User muss es beschreiben können # Wir können nicht einfach $domain als Maildir verwenden, da neu eine # Site mehrere Domains haben kann. Daher die SQL-Abfrage, um den zur # Site vergebenen Namen zu finden. pmail_delivery: driver = appendfile directory = /pfad/zu/den/maildirs/${lookup mysql{SELECT s.Name FROM pmail.pm_domains d, pmail.pm_sites s WHERE s.ID = d.SiteID AND d.Name = '$domain'}{${value}}}/$local_part delivery_date_add envelope_to_add return_path_add maildir_format user = <> group = mail prefix = suffix = check_string = maildir_tag= ,S=$message_size quota_size_regex = S=(\d+): quota = "${lookup mysql{SELECT u.Quota FROM pm_domains d, pm_sites s, pm_users u WHERE s.ID = d.SiteID AND u.SiteID = s.ID AND u.Name = '$local_part' AND d.Name = '$domain'}{${value}K}}" # prefix, suffix, sowie check_string werden geleert, damit die # Nachrichten im richtigen Format im maildir landen. # maildir_tag fügt die grösse jeder Nachricht an den Filenamen an, # die dann mit quota_size_regex wieder ausgelesen wird, um ein "stat" # für jedes Mail beim Berechnen des Quota zu vermeiden. # Courier verwendet das gleiche Format für den gleichen Zweck. # Der Wert für Quota wird aus der Datenbank gelesen. filter_transport_file: driver = appendfile delivery_date_add envelope_to_add return_path_add maildir_format user = <> group = mail prefix = suffix = check_string = maildir_tag= ,S=$message_size quota_size_regex = S=(\d+): quota = "${lookup mysql{SELECT u.Quota FROM pmail.pm_domains d, pmail.pm_sites s, pmail.pm_users u WHERE s.ID = d.SiteID AND u.SiteID = s.ID AND u.Name = '$local_part' AND d.Name = '$domain'}{${value}K}}" # Dieser Transport wird aufgerufen, falls in einem Filter das Kommando # "save" angewendet wird. Auch hier müssen wir darauf achten, das # korrekte Format zu verwenden, sowie die Quotas auszuwerten; # anderenfalls könnte das Quota mit einem simpel-filter übergangen # werden # Je nachdem kannst Du den local_delivery Transport nun löschen. # Zu den directors fügst du zuoberst den neuen aliases-Director # hinzu, der die Aliases aus der Datenbank liest: pmail_aliases: driver = aliasfile search_type = mysql query = "SELECT a.RightPart FROM pm_domains d, pm_sites s, pm_aliases a WHERE '$local_part' != '*' AND s.ID = d.SiteID AND a.SiteID = s.ID AND a.LeftPart = '$local_part' AND d.Name = '$domain'" qualify_preserve_domain # Qualify_preserve_domain erlaubt es, aliases ohne @ anzugeben. Exim # wird dann hinter das @ die aktuelle Domain und nicht die global mit # qualify_recipient definierte einfügen. pmail_forward: driver = forwardfile file_directory = /zu/den/maildirs/${lookup mysql{SELECT s.Name FROM pmail.pm_domains d, pmail.pm_sites s WHERE s.ID = d.SiteID AND d.Name = '$domain'}{${value}}}/$local_part file = /zu/den/filtern/${lookup mysql{SELECT s.Name FROM pmail.pm_domains d, pmail.pm_sites s WHERE s.ID = d.SiteID AND d.Name = '$domain'}{${value}}}/$local_part.filt no_verify no_expn check_ancestor no_check_local_user filter file_transport = filter_transport_file directory_transport = filter_transport_file pipe_transport = address_pipe reply_transport = address_reply # Viele hässliche Hacks sind leider in diesem Director erforderlich, der # sich um die User-Filter kümmert . Lies' zu file_directory und file # bitte den Abschnitt unten. file_transport und directory_transport # sind absichtlich identisch, damit ein im Filter vergessener / die # Mailbox nicht zerstört. # Den Default-Userforward, aliases- und Localuser-Director, solltest Du # möglicherweise entfernen (es sei denn, Du weisst, was Du tust, so wie # ich bei meinem System; das ist allerdings nicht mehr generisch und # daher nicht Teil des Artikels. # Folgender Director erkennt nun die lokalen Adressen: # Beachte, dass ich auch hier die Query nach dem Quota verwende: Hier # ist nur entscheidend, *ob* die Query ein Result hat, und *nicht*, # welches. Exim kann eine Abfrage cachen, so dass für das Quota nicht # nochmals die Datenbank befragt werden muss. Die Query selbst ist ja # schön schnell, aber das Senden alleine dauert natürlich bereits seine # Zeit, vor allem, wenn der SQL-Server entfernt ist. pmail_users: driver = aliasfile search_type = mysql query = "SELECT u.Quota FROM pm_domains d, pm_sites s, pm_users u WHERE s.ID = d.SiteID AND u.SiteID = s.ID AND u.Name = '$local_part' AND d.Name = '$domain'" transport = pmail_delivery # *UNTERHALB* unseres local-User-Erkennungs-Directors fügen wir nun noch # den folgenden Catchall-Director an. Damit ist es möglich, ein Alias # mit LeftPart * zu erzeugen, dass alle Adressen einer Domain schluckt # und an RightPart weiterleitet. Dieser director muss zuunterst sein, # da er sonst *alle* (also auch die eigentlich lokalen) adressen # schluckt pmail_aliases_default: driver = aliasfile search_type = mysql query = "SELECT a.RightPart FROM pm_domains d, pm_sites s, pm_aliases a WHERE s.ID = d.SiteID AND a.SiteID = s.ID AND a.LeftPart = '*' AND d.Name = '$domain'" qualify_preserve_domain # Ganz zuunterst im File kannst Du die beiden folgenden Authentikatoren # einfügen, um Deinen Usern das Relayen per SMTP-Auth zu erlauben. # Du kannst das über die Datenbank selektiv ausschalten. plain: driver = plaintext public_name = PLAIN server_condition = "${lookup mysql{SELECT u.ID FROM pm_users u, pm_domains AS d, pm_sites as s WHERE s.ID = d.SiteID AND u.SiteID = s.ID AND u.Name ='${local_part:$2}' AND d.Name ='${domain:$2}' AND u.RelayOnAuth = 'y' AND password=MD5('$3')}{1}}" server_set_id = $2 login: driver = plaintext public_name = LOGIN server_prompts = "Username:: : Password::" server_condition = "${lookup mysql{SELECT u.ID FROM pm_users u, pm_domains AS d, pm_sites as s WHERE s.ID = d.SiteID AND u.SiteID = s.ID AND u.Name ='${local_part:$1}' AND d.Name ='${domain:$1}' AND u.RelayOnAuth = 'y' AND password=MD5('$2')}{1}}" server_set_id = $1 # Die beiden Einträge sind notwendig, um auch O(j)E glücklich zu machen. -------------------------------------------->8---------------- Zwei Dinge fallen an der Konfiguration auf: *) Die lokalen Domanis sind in einem zusätzlichen File (/woauchimmer.cdb) gespeichert. *) Es finden sich Optionen für ominöse User-Filter, zusammen mit einem Kommentar, dass hässliche Hacks angewendet werden müssen. Beide Besonderheiten werden im folgenden erklährt, die Sache mit den Filtern zuerst, da das CDB-Erstellscript auch ein Framework für die Filter legt. User-Filter ~~~~~~~~~~~ Die Sache tönt gut: Wann auch immer, wohin auch immer ein Mail ankommt, es wird bereits auf dem Server in einen beliebigen IMAP-Ordner gefiltert. Von woher auch immer Du auf Deine Mails zugreifst, welchen Clienten Du auch immer verwendest, Deine Mails sind korrekt gefiltert. Wer Mitglied in vielen Mailinglists ist, weiss das sehr zu schätzen. Die hier vorgestellte Konfiguration lief so lange ohne diese Filterung, wie ich selbst das System nicht nutzte. Sobald aber die Umstellung auf den neuen Mailserver erfolgt ist, musste für mich auch das Filtering (früher ein Hack mit procmail) auch wieder integriert werden. Bei meinem Drang nach Sauberkeit und generischer Verwendbarkeit, musste alles natürlich schön in die aktuelle Konfiguration eingebaut werden. Ein weiteres Ziel bei der Entwicklung dieser Konfiguration war es, auf ein abspalten eines externen Prozesses zur Auslieferung der Mails zu verzichten, weshalb mir Exim gleich einen weiteren Gefallen tut, indem es eine leistungsfähige Filter-Sprache bereits integriert hat. Um es vornweg zu nehmen: Dieser dumme Hack, der laut Dokumentation weder funktioniert, noch nicht funktioniert (er ist undokumentiert), sowie die zusätzlichen Datenbank-Abfragen lassen mir das ganze Filter-Konzept etwas zweifelhaft erscheinen. Keine Frage: Es ist im praktischen Einsatz und läuft wie eine Eins. Ja. Ich nutze es auch selbst. Ja. Auch kommerziell. An Stabilität soll es nicht mangeln. Es ist bloss nicht mehr so "sauber", wie der Rest der Konfiguration. Solltest Du also auf das Filtering verzeichten können, so modifiziere die exim-Konfigurationsdatei oben entsprechend (entfernen von filter_transport_file und pmail_forward, sowie der zweiten Hälfte des Scripts im nächsten Abschnitt). Aber was ist nun der Hack? Und wie funktioniert das Filtern? Filter-Regeln Du kannst im hier vorgestellten System ganz normale exim-Filter verwenden (also ganz normale Dateien, die mit "# Exim Filter", gefolgt von einer Zeilenschaltung beginnen). Die vorgestellte Konfiguration sucht die Filter-Dateien in einem Verzeichnis in der Form /ein/pfad/<>/<>.filt Also einfach so ein File erstellen und entsprechend füllen. Momentan in Entwicklung befindet sich ein Plugin für die aktuelle Development-Version von IMP, das es erlauben wird, direkt per IMP-GUI ein solches Filterfile zu erstellen. Bis dahin musst Du eine andere Lösung finden, das File zu erstellen. Beachte, dass alle Filter dem Exim-User gehören sollten, was ein paar Klimmzüge benötigt, um die Dateien user-editierbar zu machen. Wie gesagt: Ich vertröste auf das IMP-Plugin in der nächsten Version (das die Filter-Files mit einem SUID exim-C-Programm an die richtige Stelle schreibt). Courier Wenn Du ein "save"-Kommando im Filter absetzt, sollte es folgendermassen aussehen: save $home/<>/ Courier legt seine Folder-Hierarchie in der Form .Folder .Folder.Subfolder .Folder.Subfolder1 .Folder.Subfolder1.Subsubfolder an. Um damit eine Nachricht in den SubSubFolder zu verschieben, müsstest Du folgendes machen: save $home/.Folder.Subfolder1.Subsubfolder/ *WICHTIG* Exim ist zwar in der Lage das jeweilge Maildir zu erstellen und tut das auch wunderbar, allerdings ist mir aufgefallen, dass sich Courier seltsam zu verhalten beginnt, falls er auf einen so von Exim erstellten Folder trifft. Will heissen: Folder erst mit dem IMAP-Clienten erstellen und *danach* den Filter bauen. Der Hack Sieht man sich die obige Zeile an, so fällt das $home auf, das den Delivery-Root des aktuellen Accoutns darstellt. $home ist von Exim eigentlich mit dem aktuellen HOME-Directory des aktuellen Local-Users belegt, der in diesem Fall aber gar nicht existiert (das ist ja der Sinn des Systems). Es gibt in einem User-Filter keine Möglichkeit, auf das aktuelle Verzeichnis zu verweisen. Ein Pfad ohne / wird immer mit einem vorangestellten $home versehen und ein Pfad mit / am Anfang wird als absoluter Pfad gewertet. Will man also nicht den kompletten Pfad angeben (und die Allgemeingültigkeit und Austauschbarkeit von Filter-Files verlieren), so gibt es zuerst mal keine Möglichkeit, User-Filter ohne Lokale User zu verwenden (Exim versucht amüsanterweise nach NULL/foldername zu delivern, was selbstverständlich fehlschlägt). Ein System-Filter kann nicht verwendet werden, da der wiederum den lokalen User nicht kennt und es ein extrem grosser Hack wäre, über einen System-Filter lokal auszuliefern, da das das ganze delivery-queue-System von Exim lahmlegen würde. Also suchen wir eine andere Lösung und finden die file_directory des forwardfile-directors, die, laut Dokumentation $home zu setzen vermag. Was sie laut dokumentation aber auch tut, ist eine Basis zu schaffen, in der das Filter-File gesucht wird. In unserem Beispiel sind das aber zwei verschiedene paar Schuhe, so dass diese Option zuerst unbrauchbar scheint. Der Hack besteht nun darin, dass man trotz file_directory einen *absoluten Pfad* zum Filter-File angeben kann, und so file_directory überschrieben wird, und schlussendlich nur zum setzen von $home benutzt wird. Schöner Hack? Ich weiss nicht. Auf der einen Seite löst er mein Problem, auf der anderen Seite verlässt er sich auf eine nicht-dokumentierte Tatsache, was das System in zukünftigen Versionen von Exim unbrauchbar machen könnte. Ich habe aus diesem Grund eine Nachricht an die exim-Mailingliste gesendet und warte sehnsüchtig auf eine bessere/andere Lösung, oder die Garantie, dass der hier vorgestellte Hack "in Ordnung" ist. Angenehmerweise hat mir Phil grünes Licht für diesen kleinen Hack gegeben, so dass ich von der Brauchbarkeit der Lösung recht überzeugt bin Date: Wed, 29 Aug 2001 09:57:58 +0100 (BST) From: Philip Hazel To: Philip Hofstetter cc: Subject: Re: [Exim] Configuration hack needed. Is it valid what I am trying to do? On Mon, 27 Aug 2001, Philip Hofstetter wrote: > So I came to this quite obscure hack, setting file_directory to > /var/spool/pmail/<>/<> and giving the file-option > an absolute path anyway: /etc/pmail/<>/<>.filter. > This leads $home in the filter-file to be the correct path. > > This is obviously against the documentation (there are only relative > paths used), but it works quite well. So my question: Is this ok? Or > is there a better solution? There is no reason why you shouldn't use absolute paths if you want to. (There is a subtle difference concerned with checking for mounted NFS directories. If you aren't using NFS, it doesn't matter. In Exim 4 this has changed, anyway.) -- Philip Hazel University of Cambridge Computing Service, ph10@cus.cam.ac.uk Cambridge, England. Phone: +44 1223 334714. CDB-Datei ~~~~~~~~~ Wie Du gesehen hast, sind die Domains in einem externen File untergebracht. Der Grund dazu ist einem Epfehlung vom Philip Hazel (der Autor von Exim) persönlich, man solle für local_domains ein Verfahren verwenden, das extrem schnell und zuverlässig ist, was man von einer Datenbank-Abfrage nicht behaupten kann. Daher habe ich ein Script in Perl geschrieben, das die Domains aus der Datenbank ausliest und daraus ein cdb-File erstellt. Gleichzeitig erstellt es für jede Site ein Verzeichnis, in dem die jeweilgen Filter-Dateien gespeichert werden. Das Script benötigt CDB_File, das Du in einem CPAN-Mirror Deines Vertrauens bekommen solltest (das Modul ist noch recht neu und daher möglicherweise noch nicht überall gespiegelt. Google hilft aber sicher gerne; ich habe die URL selbst bereits wieder verlegt ;-) cdb habe ich gewählt, weil das von allen Methoden zur Auflistung der Domains die Schnellste ist (abgesehen vom Auflisten direkt in exim.conf, aber da wäre es sehr komplex ein Script zu schreiben, das das automatisiert). Das Script sieht so aus: -------------------8<---------------------------------------- Siehe pmail2cdb.pl -------------------------------------------->8---------------- Das Script kann als zusätzliches Feature verwaiste Filter-Files von nicht mehr existenten Sites von der Festplatte entfernen. Bei der Verwendung also unter Umständen eine Sicherheitsabfrage einprogrammieren, oder Vorsicht walten lassen. Du sollstes noch die Konstanten zu Beginn des Scripts anpassen. PMAIL_UID ist die User-ID, unter der auch dein Exim läuft (in diesem Test-Script meine eigene). Du solltest das Script als root, oder als der Exim-User starten. Dieses Script ist, (leider) im Gegensatz zum authdaemon unten der BSD-Lizenz unterstellt und ist damit kein pseudo-freies Produkt unter fundamentalistischer Kontrolle. Sorry der Ausdruck. Ich bin für freie Software, die in *freier* Entwicklung unter *freien* Voraussetzungen zustande kommt und die keine Zwänge irgendeiner Art auferlegt und seien es nur Lizenz-Zwänge. Courier-Server -------------- Der Courier ist die komplexeste Sache von allen. Zwar hat das Programm eingebauten Support für MySQL; allerdings keinen für meine normalisierte Datenstruktur und auch keinen für den selektiven Zugriff nur per POP3 oder per IMAP. Dazu kommt, dass ich bei mir selbst eine spezielle Anforderung hatte, mit noch einer zweiten, vollkommen anderen Datentruktur umzugehen. Was für mich nicht in Frage kam, war es, den Sourcecode von Courier zu patchen, da ich nicht bei einem eventuellen Update der Software als allererstes mal wieder einen neuen Patch erstellen wollte. Zudem bin ich nicht wikrklich gut genug in C, um an einem produktiv eingesetzten IMAP-Server rumzuschrauben. Da musste also eine andere Lösung her. Andere Software, das Offensichtlichste von allem liegt leider nicht drin, da Courier der einzige ist, der die gewünschte Funktionalität wenigstens ansatzweise unterstützt. Die Rettung der Lage erkannte ich im Authdaemon von Courier: Dies ist ein Server (auf Basis von UNIX-Domain-Sockets), der zwischen Courier und den eigentlichen Authentication-Modulen arbeitet. Courier selbst verwendet bei Verwendung des Servers nur ein einziges Modul, authdaemon genannt, das als client zum authdaemond eine Verbindung aufbaut, der dann wiederum die einzelnen Module aufruft. Domain-Sockets. Server... Das tönt gut. Meine Lösung ist ein ein Perl geschriebener Server, der den authdaemond ersetzt, und keines der externen authentication-Module mehr aufruft, sondern die Authentifizierung selbst durchführt. Dies bietet alle geünschten Freiheiten, ist machbar, ohne ein Byte am Quelltext von Courier zu verändern (der alte daemon muss bloss im Startskript ausgeschaltet werden) und läuft in einer Sprache, in der ich genügend Kenntnisse für einen Produktiveinsatz habe. Als Bonus kriege ich die Möglichkeit, super-einfach spezielle Dinge einzubauen. Hier ist der Server. Der Code basiert teilweise auf einem Auth-Server für einen gepatchten Cyrus-Server und unterliegt daher (er ist ein Derivat) der GNU GPL Version 2: -------------------8<---------------------------------------- Siehe authdaemon.pl ------------------------------------------->8---------------- Dieser Server hat einen netten Bonus: Er erstellt ein Maildir, falls es nicht existiert und der Nutzer positiv authentifiziert werden konnte. Damit kann ein User erstellt werden, indem man einfach einen neuen Eintrag in die Datenbank macht. Es ist nicht nötig, das maildir gesondert zu erstellen. Übergibt man dem Courier den Pfad eines nicht-existenten Maildirs, so verhält er sich ein wenig seltsam. Weiterhin überprüft der neue Daemon auch, ob der jeweilge User überhaupt ein "erlaubtes" Protokoll verwendet. Damit kannst Du für jeden Account definieren, ob er ein POP, IMAP oder beides-Account ist (siehe oben bei der Beschreibung des Datenmodells). Installation des authcheck.pl-Daemons ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nachdem Du den authentication-Server erstellt und in ein Verzeichnis Deiner Wahl kopiert hast, musst Du ihn korrekt im System installieren und dafür sorgen, dass er (und nur er) beim Systemstart geladen wird. Sehen wir uns dazu nochmals die Arhcitektur an, die der Courier-Server verwendet: [Courier-IMAP] <--> <--> [authdaemon] <--> [auth*] (default-installation) [Courier-IMAP] <--> <--> [authcheck.pl] (diese installation) Der IMAP-Server nutzt die mitgelieferte authlib zur Authentifizierung von Benutzern. Dabei verwendet der IMAP-Server eines oder mehrere sogenannter authentifizierungsmodule. In der Konfiguration mit dem authdaemon kommuniziert der IMAP-Server mit genau einem Modul (authdaemon), das wiederum über einen UNIX-Domain-Socket (im Prinzip eine Mischung zwischen einem INET-Socket und einer Datei) mit dem authdaemond kommuniziert, der selbst wiederum die "normalen" Authentifizierungsmodule lädt. In dieser Konfiguration wird die ganze Sache abgekürzt: Der Courier-Server authentifiziert weiterhin über das authdaemon-Modul, aber dieses spricht nun nicht mehr mit dem authdaemond, sondern mit dem oben dargestellten Daemon authcheck.pl. Um dies zu erreichen, müssen einige Vorraussetzungen erfüllt werden: *) Der authcheck.pl-Daemon muss am gleichen Socket hängen, wie es der alte authdaemond getan hätte *) Der alte authdaemond darf nicht gestartet werden. Als erstes sollte nun der Start des alten authdaemons verhindert werden. Die Start-Scripts von Courier starten und stoppen diesen jeweils automatisch, so dass diese Skripte modifiziert werden müssen: In der Start)-Sektion der Skripte, kommentiere if test -x ${libexecdir}/authlib/authdaemond then /usr/bin/env - ${libexecdir}/authlib/authdaemond start fi aus, in der stop)-Sektion analog if test -x ${libexecdir}/authlib/authdaemond then ${libexecdir}/authlib/authdaemond stop fi Dies musst Du in allen Start-Scripts tun, mit denen Du die Komponenten von Courier startest (imapd.rc, pop3d.rc, imapd-ssl.rc, pop3d-ssl.rc). Nun hast Du verhindert, dass der Courier-eigene authdaemon gestartet wird. Nun musst Du den authcheck.pl-Daemon konfigurieren. Am wichtigsten ist dabei die Konstante PATH: Sie sollte auf den Namen des Sockets gesetzt werden, über den die Courier-Authlib kommunizieren will. Den Pfad findest Du ganz zuunterst in /pfad/zu/courier/etc/authdaemonrc Die anderen Konstanten in authcheck.pl sollten selbsterklährend sein. Weitere Modifikationen an der Courier-Konfiguration sind nicht notwendig (was ich als grossen Vorteil der hier vorgestellten Konfiiguration ansehe). Den Daemon selbst startest Du über /pfad/zum/authcheck.pl Beenden kannst Du ihn über ein kill `cat /pfad/zum/pidfile.pid` Wobei Du den /pfad/zum/pidfile.pid als Konstante in authcheck.pl selbst angeben kannst. Willst Du den Daemon in das Systemeigene Init-System aufnehmen, so wirst Du wohl die Dokumentation Deines Betriebssystems bemühen müssen. In Fällen mit einem SysV-Init-Stil wirst Du in /etc/init.d (oder so) ein Script namens skeleton finden, dass Du um die beiden obigen Zeilen erweitern kannst. FAQ -- 1) Wie erstelle ich denn nun eine Domain? Zuerst musst Du eine Site erstellen. Verwende dazu das folgende SQL-Kommando: INSERT INTO pm_sites (Name) VALUES ('<'); Nun kannst Du dieser Site beliebig viele Domains zuordnen. Verwende für jede Domain die folgende Anfrage: INSERT INTO pm_domains (Name, SiteID) VALUES ('<>',<); vergiss' nicht, zum Abschluss das Script auszuführen, das das cdb-File auf den neuesten Stand bringt, damit Exim auch merkt, dass die neue Domain erschienen ist. Du musst Exim nicht neu starten. 2) Wie erstelle ich einen User siehe oben. Einfach das passende INSERT-Kommando auf die Datenbank absetzen. Das passende Passwort kannst Du mit der MYSQL-internen Funktion MD5() erstellen. Ein mögliches INSERT-Kommando sähe also folgendermassen aus: INSERT INTO pm_users (SiteID, Name, Password, Quota, AddrType, RelayOnAuth) VALUES ( <>,'<>',MD5('<>'),'','i,p','y' ); 3) Wie erreiche ich, dass Mails, die an einen beliebigen (nicht-existierenden) User gesendet werden, in einem bestimmten Postfach landen? Kein Problem: Erstelle ein Alias mit dem LeftPart * und einem beliebigen RightPart: INSERT INTO pm_aliases (SiteID, LeftPart, RightPart) VALUES (<>,'*','pilif'); 3) Ich brauche ein Web-Inferface! 3) Kann ich die User auch über ein Web-Interface administrieren? Schön. Dafür gibt es nun PMAIL UI (PMAIL User Interface) das zusammen mit PMAIL in diesem Archiv ist. 4) Gibt es eine andere Lösung zur Web-Administration? Jein. Auf der einen Seite würde ich phpMyAdmin empfehlen; allerdings kannst Du damit keine getrennten Rechtevergaben für verscheidene User erstellen; Du musst also alle Administration selbst machen. Vergiss' auch hier nicht, das CDB-File upzudaten. 5) Wie lösche ich eine Domain? Verwende das folgende SQL-Statement: DELETE FROM pm_domains WHERE Name = '<>' 6) Wie lösche ich eine Site? Nimm' das Webinterface, oder mach' es manuell: *) Alle Accounts der Site aus der DB Löschen *) Alle Aliases der Site löschen *) Alle domains der Site löschen *) Die Site aus der DB löschen *) cdb-File neu erstellen *) Unnötige Maildirs kicken ("rm -rf <>" im spool-Verzeichnis) 6+7) Wie kann ich einen Account (umbenennen|löschen)? Webinterface oder manuell (siehe oben). 8) Ich möchte meinen Usern Zugang über's Web anbieten. Gibt es da fertige Lösungen, oder muss ich auf pilif warten, dass er was programmiert? Nein. Es gibt fertige Lösungen, und zwar wie Sand am Meer. Ein paar ausgewählte Lösungen stelle ich hier kurz vor, da sie mir alle irgendwie Eindruck gemacht haben. Weiter unten findest Du noch ein paar URLs, wo Du Zugriff auf die ganze Menge an Tools bekommen kannst: TWIG http://twig.screwdriver.net/ Das momentane Tool für das Webmail auf www.sen.ch. Ist auf der einen Seite etwas rudimentär, hat aber viele Features. Benötigt einen IMAP-Server (und, falls Du die hier vorgestellte Datenstruktur verwendest, auch die Berechtigung, IMAP zu verwenden), sowie PHP IMP http://www.horde.org/imp/ Der Klassiker unter den PHP-Mailern. Verwendet leider Frames und ist etwas mühsam zum installieren. Bietet allerdings viele Features. Auch IMP benötigt IMAP, kann aber auch mit POP umgehen. phpGroupWare http://www.phpgroupware.org Wenn es etwas mehr als Mails sein darf und die viel Zeit hast, Dich mit manchmal etwas seltsamem Code herumzuschalgen, dann ist phpGroupWare was für Dich. kmMail http://www.kmmail.org/ Ich liebe dieses Tool: Klein, schlank, gutaussehend und schnell installiert, wie auch in andere Applikationen integriert. Falls Du kein Adressbuch brauchst und mit PHP klarkommst, ist kmMail ein Treffer für Dich. sqWebMail http://inter7.com/sqwebmail/ sqWebMail ist vom gleichen Programmierer, wie der hier verwendete IMAP-Server. Die Spezialität des Programmes ist, dass es keinen Server benötgt, sondern die Mails direkt aus den maildir's liest. Nachteil: Ist in C geschrieben und damit (zumindest für mich) schwer anpass- und erweiterbar. CGI-Resourceindex http://cgi.resourceindex.com/Programs_and_Scripts/Perl/Web_Based_E_Mail/ Soll es lieber Perl sein? Such' Dir ein Script aus! Lizenz ------ Tu' damit, was immer Du tun willst, wann immer Du es tun willst. Nur: Verklag' mich nicht, wenn was schiefgeht, oder wenn Du deine Rechte verletzt siehst (sende dann ein Mail). Der neue Authdaemon sowie PMAIL UI unterliegt der GNU GPL, für die etwas weniger freie Regeln gelten. -- 17. August 2001; by Philip Hofstetter, Oliver Siegmar