73. pepsi-stage-list-deliver

Macht aus der Kopie einer Listennachricht die eigene Kopie eines Mitglieds.

73.1. Aufgabe

pepsi-stage-list-post hat bereits einen Datensatz pro Empfänger erzeugt; pepsi-stage-list-deliver ist das, was aus jedem davon die Kopie dieses Mitglieds macht und nicht bloß eine weitere Kopie derselben Nachricht. Ein Datensatz hinein, ein Datensatz hinaus. Die Stage vervollständigt List-Unsubscribe mit der Ein-Klick-https:-URI dieses Mitglieds neben der mailto:-Form und ergänzt List-Unsubscribe-Post: List-Unsubscribe=One-Click (RFC 8058); sie versieht die Nachricht mit Kopf- und Fußzeile der Liste in der Sprache des Mitglieds, wenn personalize der Liste individual oder full ist; und sie schreibt To: auf das Mitglied um, aber nur bei full – upstreams personalize_to ist bei individual eine Leeroperation, und hier ebenso. Referenz: pepsi-stage-list-deliver(1).

Dieses Subsystem ist eine Neuimplementierung von GNU Mailman 3; siehe Mailinglisten, wo benannt ist, was von upstream übernommen wurde.

Warnung

Diese Stage muss vor dem Signieren laufen. Jeder Header, den sie anfasst, gehört in die DKIM-h=-Liste, und List-Unsubscribe ist genau der Header, den ein Mailbox-Anbieter prüft, wenn er entscheidet, ob er eine Abmelden-Schaltfläche anzeigt. Eine Pipeline, die zuerst signiert, erzeugt Post, die signiert und danach verändert wurde – und die überall an DKIM scheitert. NEXT_STAGE zeigt daher auf pepsi-stage-dkim-sign und nicht auf ein Relay, und pepsi-setup verweigert die umgekehrte Reihenfolge. Upstream sagt dasselbe in dem Kommentar, mit dem arc-sign aus der eigenen Pipeline entfernt wird.

73.2. Ein-Klick-Abmeldung

Das Token in der https:-URI ist ein geschlüsselter Hash über die Liste, die Adresse des Mitglieds und eine Seriennummer, die das Mitglied wechseln kann. Es wird nicht gespeichert: eine List-Unsubscribe-URI entsteht einmal pro Mitglied pro Nachricht, eine Tabelle davon würde also mit den Zustellungen und nicht mit den Mitgliedern wachsen. Daraus folgt Dreierlei.

  • Das Token reist nie zwischen Stages – diese Stage hält das Geheimnis und berechnet es je Kopie.

  • Jede jemals für ein Abonnement ausgegebene URI zu widerrufen ist ein Erhöhen der unsubscribe_serial dieses Mitglieds: eine Zähleränderung und kein Tabellendurchlauf.

  • Es gibt nichts abzulaufen, denn ein Abonnement, das nicht mehr besteht, scheitert an der Mitgliedschaftsabfrage, was auch immer das Token sagt.

Das Geheimnis ist [pepsi-list] UNSUBSCRIBE_SECRET. Ohne Geheimnis oder ohne BASE_URL, aus der sich eine URI bauen ließe, steht die mailto:-Form allein da, wie bei upstream: eine nicht überprüfbare URI ist schlechter als keine. Das Geheimnis zu wechseln macht jede URI ungültig, die schon in jedem Postfach eines Abonnenten liegt.

73.3. Ausschmückung

Kopf- und Fußzeile werden aus [pepsi] TEMPLATE_DIR als list-header.<lang>.body und list-footer.<lang>.body gerendert, zuerst in der Sprache des Mitglieds und mit en als Rückfall. Eine Liste ohne Vorlagendateien wird nicht ausgeschmückt, was der Normalfall ist und nichts kostet.

Die Fußzeile wird an den ersten ``text/plain``-Teil angehängt und an nichts anderes. An einen multipart/signed-Teil angehängt bricht sie die Signatur, an ein PDF angehängt beschädigt sie es, daher wird eine Nachricht ohne Klartextteil unausgeschmückt zugestellt statt verstümmelt.

Eine Kopie, die avoid-duplicates markiert hat – das Mitglied stand schon im To oder Cc der Nachricht selbst und hat seine Listenkopie dennoch behalten –, erhält X-Pepsi-List-Copy: yes, damit der Leser sieht, warum er zwei hat.

73.4. Zwei Invarianten

Geschwisterdatensätze beginnen hier und nie am Kopf der Pipeline. Ein Wiedereintritt bei init würde die ARC-Prüfung erneut ausführen und wieder in den Router führen, was bei einer Dachliste kombinatorisch explodiert.

Die persönlichen Einstellungen eines Mitglieds konfigurieren die Listenzustellung nicht um. Pepsi legt Überschreibungen aus pepsi.settings je Adresse automatisch darüber (siehe Konfiguration), und der einzige Empfänger eines Geschwisterdatensatzes ist ein Mitglied, dessen Abwesenheits- oder Sprachüberschreibung nichts an den Zielen dieser Stage zu ändern hat — ein daraus übernommenes NEXT_STAGE könnte die Kopie an der Signierung vorbeileiten. Daher werden NEXT_STAGE, die [pepsi-list]-Optionen und das TEMPLATE_DIR aus [pepsi] aus der serverweiten Konfiguration gelesen, die keine domain:- oder address:-Konfigurationsüberschreibung und kein pepsi.settings-Datensatz erreicht, und die Sprache des Mitglieds stammt aus dem Deskriptor, den das Fan-out geschrieben hat. Nichts wird aus der Überlagerung übernommen.

73.5. Konfiguration

[stage-<name>]: PROGRAM = pepsi-stage-list-deliver und NEXT_STAGE (erforderlich – das Signaturende; pepsi-setup verweigert ein Relay). Alles Weitere, was die Stage braucht, ist der Mitgliedsdeskriptor aus dem Fan-out und die Attribute der Liste selbst, dazu [pepsi-list] BASE_URL und UNSUBSCRIBE_SECRET aus dem Serverabschnitt.

73.6. State

  • Eingaben: state.list – die Listen-ID, die Sprache des Mitglieds, seine Abmelde-Seriennummer und das Duplikat-Flag – sowie das einzige rcpt_to des Datensatzes, das für die Adresse maßgeblich ist.

  • Ausgaben: ein umgeschriebener Headerblock und Nachrichtentext. Kein state wird geändert.

  • Übergänge: Weiterschalten zu NEXT_STAGE, der einzige Übergang.

73.7. Siehe auch

Mailinglisten, pepsi-stage-list-post, pepsi-stage-dkim-sign, pepsi-list, pepsi-stage-list-deliver(1).