From ac7e2c541a18c5c910548f5cddaf56502b6e51b0 Mon Sep 17 00:00:00 2001 From: Thomas Hochstein Date: Mon, 16 Aug 2010 22:16:26 +0200 Subject: [PATCH 1/1] Initial checkin of upstream version 4.09. Signed-off-by: Thomas Hochstein --- CHANGES | 484 +++++++++++ COPYING | 339 ++++++++ README | 1284 ++++++++++++++++++++++++++++++ UVconfig.pm | 329 ++++++++ UVformats.pm | 566 +++++++++++++ UVmenu.pm | 380 +++++++++ UVmessage.pm | 32 + UVreadmail.pm | 225 ++++++ UVrules.pm | 409 ++++++++++ UVsendmail.pm | 336 ++++++++ UVtemplate.pm | 810 +++++++++++++++++++ bdsgtext.cfg | 13 + mailpatterns.cfg | 42 + messages.cfg | 194 +++++ templates/ack-mail | 46 ++ templates/address-not-registered | 25 + templates/ballot | 34 + templates/ballot-personal | 63 ++ templates/ballot-request | 10 + templates/bdsg-error | 29 + templates/bouncelist | 10 + templates/cancelled | 12 + templates/invalid-account | 27 + templates/invalid-name | 19 + templates/mailheader | 16 + templates/multiple-votes | 19 + templates/no-ballot | 16 + templates/no-ballotid | 19 + templates/no-votes | 20 + templates/result-multi | 31 + templates/result-proportional | 31 + templates/result-single | 28 + templates/rule-violated | 20 + templates/voterlist | 12 + templates/votes-multi | 36 + templates/votes-single | 33 + templates/wrong-ballotid | 23 + tmp/ergebnis-1191790177 | 0 tmp/stimmen-1191790177 | 0 uidlcache | 0 usevote.cfg | 293 +++++++ usevote.rul | 68 ++ uvballot.pl | 116 +++ uvbounce.pl | 194 +++++ uvcfv.pl | 282 +++++++ uvcount.pl | 475 +++++++++++ uvvote.pl | 595 ++++++++++++++ 47 files changed, 8045 insertions(+) create mode 100644 CHANGES create mode 100644 COPYING create mode 100644 README create mode 100644 UVconfig.pm create mode 100644 UVformats.pm create mode 100644 UVmenu.pm create mode 100644 UVmessage.pm create mode 100644 UVreadmail.pm create mode 100644 UVrules.pm create mode 100644 UVsendmail.pm create mode 100644 UVtemplate.pm create mode 100644 bdsgtext.cfg create mode 100644 mailpatterns.cfg create mode 100644 messages.cfg create mode 100644 templates/ack-mail create mode 100644 templates/address-not-registered create mode 100644 templates/ballot create mode 100644 templates/ballot-personal create mode 100644 templates/ballot-request create mode 100644 templates/bdsg-error create mode 100644 templates/bouncelist create mode 100644 templates/cancelled create mode 100644 templates/invalid-account create mode 100644 templates/invalid-name create mode 100644 templates/mailheader create mode 100644 templates/multiple-votes create mode 100644 templates/no-ballot create mode 100644 templates/no-ballotid create mode 100644 templates/no-votes create mode 100644 templates/result-multi create mode 100644 templates/result-proportional create mode 100644 templates/result-single create mode 100644 templates/rule-violated create mode 100644 templates/voterlist create mode 100644 templates/votes-multi create mode 100644 templates/votes-single create mode 100644 templates/wrong-ballotid create mode 100644 tmp/ergebnis-1191790177 create mode 100644 tmp/stimmen-1191790177 create mode 100644 uidlcache create mode 100644 usevote.cfg create mode 100644 usevote.rul create mode 100644 uvballot.pl create mode 100644 uvbounce.pl create mode 100644 uvcfv.pl create mode 100644 uvcount.pl create mode 100644 uvvote.pl diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..2d0d6d2 --- /dev/null +++ b/CHANGES @@ -0,0 +1,484 @@ +UseVoteGer Versionshistorie (aktuelle Version: 4.09, released 07.10.2007) +========================================================================= + +TODO: +- aussortieren von Bounces aus Stimmenliste +- Unterstuetzung von Maildir +- [Zugschlus] ich fänd es klasse, wenn man eine Kopie des Wahlscheines ins +Abstimmungsverzeichnis legt und Usevote einem dann auf Abruf ein diff +oder wdiff zwischen Sollwahlschein und wirklich eingreichtem +Wahlschein macht. Auf diese Weise erwischt man auch kleine Änderungen +am Datenschutzhinweis (da bin ich eben in eine Falle von th-h getappt). + + +Version 4.09 (14.09.2007): +- "votefile"-Option in usevote.cfg an passendere Stelle verschoben und + den Kommentar korrigiert (natuerlich gilt pop3=0 als Bedingung, nicht + etwa wie vorher angegeben smtp=0) +- Fehler in Template für Ergebnisausgabe korrigiert (fehlendes Newline + nach umgebrochenen Abstimmungspunkten) +- Fehler in Doku der Kommandozeilenoptionen von uvcount.pl behoben + (--voters statt --votes) +- Date-Header in Englisch erzeugen (statt in eingestellter locale) + (verwendet nun Modul Email::Date) +- Message-ID-Header selbst erzeugen + +Version 4.08 (06.10.2005): +- beim Ignorieren von Regelverletzungen (im Menue mit "Stimmen OK" bestaetigt) + wird jetzt keine (dann ja unangebrachte) Fehlermail mehr verschickt. +- neuer Buchstabe I bzw. i in usevote.rul, der auf NEIN und ENTHALTUNG matcht. + Damit lässt sich eine Stichwahl realisieren, bei der nur für eine von + zwei Möglichkeiten mit JA gestimmt werden darf und im anderen Feld entweder + NEIN oder ENTHALTUNG (bzw. garnichts, was Enthaltung enspricht) + eingetragen werden muss. +- analog neuer Buchstabe H bzw. h für JA/ENTHALTUNG, der Vollstaendigkeit halber +- uvvote.pl sortiert die Liste der Ergebnisdateien jetzt vorm Zusammenfuegen + zur neuen ergebnis.alle, so dass die Reihenfolge auf jeden Fall stimmt, + auch wenn das System die Dateien unsortiert liefert +- wenn uvcount.pl in der ergebnis.alle auf eine falsche Anzahl von Abstimmungspunkten + bei einer Stimme trifft (z.B. versehentlich Leerzeichen oder Buchstabe + am Ende einer Zeile zuviel, wenn manuell editiert wurde), bricht es ab und + weist auf die fehlerhafte Stimme hin. Vorher wurde das als weiterer + Abstimmungspunkt gezählt. +- Doku ergänzt: "envelopefrom" bezieht sich nur auf SMTP, ansonsten muss + das in "mailcmd" konfiguriert werden +- Bug bei Eingruppenmodus behoben: Es wurde immer das selbe ausgegeben, + unabhaengig vom Ergebnis (keine 60 Stimmen, keine 2/3 Mehrheit) + +Version 4.07 (26.09.2004): +- wenn "nodup=1" gesetzt war, wurden auch keine Annullierungen aussortiert. + Ausserdem wurde ansonsten der Wahlleiter unnoetig gefragt, welche + Stimme aussortiert werden soll, auch wenn letztlich beide annulliert + waren (die Annullierung aber erst spaeter eingegangen war). + Um diese Fehler zu beheben, wurde ein zusaetzlicher Verarbeitungsschritt + in uvcount.pl eingefuehrt, der sich nur um Annullierungen kuemmert und + die gleich am Anfang verarbeitet. +- es ist jetzt auch moeglich, nach einer Annullierung mit derselben + Mailadresse nochmal abzustimmen. Vorher wurde so eine Stimmabgabe + durch die vorher erfolgte Annullierung mit erfasst +- Template result-proportional korrigiert. Es kam zu Darstellungsfehlern + bei umgebrochenen Gruppennamen/Wahlgegenstaenden (falsche Einrueckung, + falscher Umbruch) +- Formatfunktion 'generate-date-header' fuer Templates eingefuehrt +- Template 'mailheader' um Date-Header ergaenzt +- chomp auf Message-ID nur noch machen, wenn eine Message-ID vorhanden ist + (gibt sonst Warnung wegen undef) +- wenn die Option "mailcc" gesetzt ist, wurden die Hochkommata in der + domail-Datei (siehe Changelog von Version 4.06) um beide Adressen gesetzt. + Jetzt wird in einer Schleife jede Adresse einzeln gequotet. +- RegEx fuer Realnamenserkennung um den Bindestrich erweitert, damit + Doppelnamen anerkannt werden + +Version 4.06 (18.06.2004): +- Es werden nun "In-Reply-To:" und "References:" Header in den + generierten Mails erzeugt +- beim Schreiben des domail-Scripts (Verschicken von Mails ohne SMTP) + wurde ein fehlerhafter Zeilenumbruch eingefuegt +- Leerzeichen am Zeilenende in der usevote.cfg hatten dazu geführt, + dass Einstellungen nicht korrekt eingelesen wurden. Jetzt werden + beim Einlesen der Konfiguration solche Leerzeichen gelöscht (außer + wenn der Teil rechts vom Gleichzeichen durch Anführungsstriche umschlossen + ist) +- Mailadresse und Waehlername werden jetzt korrekt zurueckgesetzt, so + dass bei fehlendem From-Header nicht noch die Daten der vorherigen Mail + in den Variablen stehen +- Mailadresse wird nun in Hochkommata eingeschlossen, wenn sie in die + "domail"-Datei geschrieben wird (bei smtp=0 in usevote.cfg), damit + Shell-Metazeichen nicht beim Ausfuehren des MTA interpretiert und damit + die Mailadresse veraendert bzw. potentiell schaedlicher Code ausgefuehrt + wird +- Es ist jetzt moeglich, bei der Warnung "Es wurden nicht alle Fehler behoben, + der Waehler wird eine Fehlermail erhalten" zurueck ins Menue zu gehen + und die Fehler doch noch zu beheben +- in uvcfv.pl (Verschicken von personalisierten Wahlscheinen) einige + Bugs behoben [warum hatte das ueberhaupt so funktioniert? Schroedinger + laesst gruessen.] und den Hinweistext "Wahlschein wurde bereits einmal + zugeschickt" aus dem Perlcode in das Template ballot-personal verlagert +- mittels "uvcfv.pl -t" laesst sich jetzt ein personalisierter + Dummy-Wahlschein ausgeben (wenn "personal=1" in usevote.cfg), um ihn + vorab der dana-Moderation zur Pruefung zukommen lassen zu koennen + +Version 4.05 (27.12.2003): +- Aendern von Mailadressen oder Namen im Menue fuehrte zu "keine Scheinkennung" + Fehlern, auch wenn die personalisierten Wahlscheine nicht aktiviert waren + (personal=0 in usevote.cfg). +- Fehler beim Verarbeiten von Mailbox-Files behoben. In den letzten + Versionen funktionierte nur POP3. +- In Wahlschein-Templates "kann fuer ungueltig erklaert werden" in + "wird fuer ungueltig erklaert werden" geaendert (bei falschem Realname) +- kosmetische Aenderungen in Templates (Anpassung an neue Rechtschreibung, + Entfernung von Umlauten fuer einheitliches Schriftbild) + +Version 4.04 (22.11.2003): +- uvcount.pl: Fehler beim Aussortieren von Duplikaten behoben, was + i.d.R. nur bei doppelten Mailadressen, nicht aber bei doppelten Namen + funktionierte (falsche Regular Expression und Probleme bei + unterschiedlicher Gross-/Kleinschreibung der Namen) + +Version 4.03 (19.10.2003): +- UVsendmail.pm: Vernuenftige Fehlerbehandlung bei SMTP implementiert: + Bei fehlgeschlagenen Zustellversuchen wird jetzt die Datei ack.control + passend neu geschrieben, so dass mit "uvvote.pl clean" ein neuer + Versuch unternommen werden kann. Vorher wurde zwar eine Fehlermeldung + angezeigt, die Mail aber einfach geloescht... +- es wird kein Fehler mehr angezeigt, wenn ack.control bereits existiert, + da der Code durchaus damit umgehen kann (es wird einfach an die + Datei angehaengt). Entsprechende Fehlermeldung aus messages.txt + entfernt +- Schreibfehler in messages.cfg behoben (ggf. statt ggfls.) +- me@privacy.net in mailpatterns.cfg aufgenommen +- UIDLs werden jetzt in der Reihenfolge gespeichert, in der die + Mails auf dem POP3-Server lagen. Dadurch ist bei einem Abbruch + waehrend der Auswertung leichter kontrollierbar, welche Mails noch + einmal abgerufen werden sollen (einfach die letzten X UIDLs aus + der Datei uidlcache loeschen) +- uvcfv.pl, uvbounce.pl und uvvote.pl besitzen jetzt einen Locking- + Mechanismus, der ein gleichzeitiges bzw. mehrfaches Starten dieser + Programme unterbindet. Andernfalls koennte es zu Inkonsistenzen + im Datenbestand kommen (gleichzeitiger Abruf derselben Mailbox, + Auswertung noch nicht fertig geschriebener Ergebnisdateien) +- es koennen jetzt zusaetzliche Konfigurationsdateien in usevote.cfg + eingebunden werden, um z.B. die immer gleichen Einstellungen nur + einmal zentral abzulegen. Hierzu einfach eine Zeile + include dateiname + einfuegen. Die Position ist wichtig: Bei mehrfacher Definition + der selben Option gilt die letzte. Daher sollte eine globale + Konfigurationsdatei am Anfang eingebunden werden, um die + Einstellungen bei Bedarf mit wahlspezifischen ueberschreiben zu + koennen + +Version 4.02 (31.05.2003): +- UVpath.pm wieder entfernt, da mittlerweile eine bessere Loesung + gefunden: Das Modul FindBin wird eingesetzt, um den Pfad der + ausgefuehrten .pl Datei zu ermitteln. Wenn die .pm Dateien im selben + Verzeichnis liegen, werden sie dort gefunden. Ausserdem wurde der + Hinweis auf die Umgebungsvariable PERL5LIB in die README Datei + aufgenommen, die ansonsten auch auf den Pfad zu den Usevote-Perlmodulen + gesetzt werden kann. + +Version 4.01 (29.05.2003): +- Wahlschein-, Result- und Bestaetigungsmail-Templates angepasst, so dass + bei langem "votename" ein Umbruch im Wahlschein erfolgt und auch bei einer + zweistelligen Anzahl von Wahlgegenstaenden eine buendige Ausgabe erfolgt +- uvballot.pl und Template "result-multi" angepasst, so dass bei + Mehrgruppenabstimmungen die Anzahl der Enthaltungen nicht ausgegeben + wird (laesst sich nicht als Gesamtzahl ermitteln, koennte man hoechstens + fuer jede Gruppe einzeln angeben) +- Es brauchen jetzt nur noch die Konfigurationsdateien sowie die + UVpath.pm in einem Abstimmungsverzeichnis zu liegen, die .pl und .pm + Dateien koennen zentral fuer mehrere Abstimmungen abgelegt werden. +- Formatierungsfunktion "replace" in UVformats.pm implementiert, mit + deren Hilfe die Ersetzung von Zeichen oder Zeichenketten in Templates + moeglich ist. Praktische Anwendung ist z.B. die Verfremdung von + Mailadressen im Result als trivialer Spamschutz. Wie die Templates + dafuer geaendert werden muss, ist in der README Datei im Abschnitt 10 + beschrieben +- Bei den Standard-Funktionen append und justify (inkl. justify-before + und justify-behind) wird der uebergebene Key jetzt rekursiv ueber die + Formatdefinitionen im Template aufgeloest. Das ermoeglicht die + Vorbehandlung eines Wertes, z.B.: + mail := value mail | replace '@' '-at-' + line := value name | justify-before mail 70 + Hier wurde vorher die unveraenderte Mailadresse benutzt, jetzt wird + die obere Definition beachtet und zunaechst die Ersetzung durchgefuehrt. +- Formatierungsfunktion "sprintf" in UVformats.pm implementiert, um + z.B. Verhaeltnisse in Results formatiert ausgeben zu koennen +- Auswertung nach Verhaeltnis Ja- zu Nein-Stimmen implementiert + (in usevote.cfg proportional=1 setzen und prop_formula passend waehlen). + Damit ist z.B. fuer jeden Abstimmungsgegenstand das Verhaeltnis oder + auch die Differenz zwischen Ja- und Nein-Stimmen ermittelbar. Letzteres + wird fuer Moderationsnachwahlen benoetigt. Kombiniert werden kann dies + mit einer weiteren Bedingung, z.B. mindestens soviele Ja- wie Nein-Stimmen. + +Version 4.0 (22.03.2003): +- UVformats.pm dokumentiert +- Defaultwert fuer "formats" korrigiert (UVconfig.pm) +- Defaultwert fuer "bdsgfile" fehlte (UVconfig.pm) +- kosmetische Aenderung (fehlende Leerzeichen) an Template result-multi +- Windows-Pager-Empfehlung in README und usevote.cfg geaendert (vorher + wurde "more" empfohlen, da mitgeliefert, aber more ist so buggy, dass + jetzt die Installation von "less" nahegelegt wird +- Fehler in UVsendmail.pm behoben: Wenn beim "uvvote.pl clean" Aufruf keine + Mails zu verschicken waren, wurde das Programm in UVsendmail::send() + einfach mit "exit 0" beendet (korrigiert in "return 0"). Ausserdem + wurde die Fehlermeldung wegen eines Schreibfehlers im Konstantennamen + nicht angezeigt. + +Version 4.0beta15: +- Fehlermeldung bei nicht vorhandener messages.cfg korrigiert + (Dateiname wurde wegen falschen Configschluessels nicht angezeigt) +- Bei manuell eingegebener Scheinkennung wurde irrtuemlicherweise eine + Fehlermeldung angezeigt, auch wenn die Kennung zur Mailadresse passte +- Bei neu eingegebener Mailadresse wurde die Zugehoerigkeit der + Scheinkennung nicht neu geprueft +- bei nicht erkannten Abstimmungspunkten im Wahlschein wurde eine + Warnmeldung angezeigt, die auf Nicht-Wertung hinwies. In Wirklichkeit + wurde die Stimme aber normal bestaetigt und lediglich alle nicht erkannten + Punkte als "Enthaltung" gewertet. Die Warnmeldung erscheint jetzt nicht + mehr, um keine Verwirrung zu stiften. +- Fehler in UVmessage.pm behoben: Der Wert 0 wurde durch den leeren String + ersetzt. Jetzt wird defined() eingesetzt statt auf true/false zu pruefen. +- Fehler in UVsendmail.pm behoben: Wenn kein SMTP aktiviert war, wurde + die "domail" Datei mit den MTA-Aufrufen zwar geschrieben, aber nicht + ausgefuehrt. Dadurch schlug auch das Loeschen der Temp-Dateien fehl, + was beim naechsten Aufruf Fehlermeldungen verursachte. +- Templates eingebaut +- acktext.txt durch Templates ersetzt. BDSG-Text ist jetzt in der + Datei bdsgtext.cfg +- "cfvfile" Option entfernt (nicht mehr noetig, durch Templates abgeloest) +- Bedingungen fuer Wahlerfolg ueber usevote.cfg konfigurierbar gemacht. + Standardwerte: + condition1 = $yes>=2*$no + condition2 = $yes>=60 +- usevote.cfg bzgl. der Ueberschriften "jedes Mal anpassen" / "nur einmal + anpassen" ein wenig umsortiert +- wenn kein "smtphelo" definiert wurde, wird jetzt der eigene + Hostname genommen +- README an aktuelle Aenderungen angepasst (Dateilisten, Beschreibungen + der Menues) und vervollstaendigt + +Version 4.0beta14: +- Fehler in UVmenu.pm behoben, der beim Auswaehlen von + "Stimmen vom Waehler annulliert" im Menue auftrat +- Erkennung von doppelten, sich widersprechenden Stimmabgaben in einer + Mail funktioniert jetzt +- beim Verschicken per SMTP gibt es die neue Option envelopefrom, die + die Absenderadresse im Envelope (Return-Path) enthaelt, an die auch + Bounces zurueckgehen +- Neu: Erkennung von fehlenden Abstimmungspunkten im Wahlschein, Behandlung + wie bei unleserlichen Stimmabgaben mit entsprechendem Hinweis im Menue +- Fehler in uvvote.pl behoben, der bei unleserlichen Stimmabgaben auftrat +- statt encode_mimewords wird jetzt encode_mimeword verwendet und das + "Drumherum" komplett selbst gemacht. Man schaue in den Code von + MIME::Words::encode_mimewords(), dann weiss man, warum ;-) +- Weitere Texte in messages.txt ausgelagert (uvvote.pl, uvcount.pl) +- config test (-t Option) gibt jetzt auch Auskunft ueber die Konfiguration + (falls Option nicht in usevote.cfg gesetzt, wird der Standardwert + ausgegeben) + +Version 4.0beta13: +- Fehlerbehandlung bei SMTP eingefuehrt, so dass keine Mails verloren gehen +- es laesst sich jetzt ein anderer Port fuer SMTP/POP3 angeben +- uvbounce.pl benutzt jetzt auch POP3, falls dieses in usevote.cfg aktiviert + wurde. Mit der Option -f ist aber unabhaengig davon das Einlesen der + Bounces aus einer Datei in jedem Fall moeglich +- auftretende Fehler beim Ausfuehren von uvvote.pl werden jetzt in eine + Datei geschrieben und beim Verlassen wird darauf hingewiesen +- Wenn das interaktive Menue ausgeblendet und dazu der Bildschirm geloescht + wird, informiert jetzt eine Meldung darueber, dass Mails verarbeitet werden +- Menues so umgestellt, dass [a] immer fuer "alles OK" steht, egal ob + Mailadresse, Name, Stimmen oder die BDSG-Klausel strittig sind +- Ausgaben/Texte von UVreadmail.pm und UVsendmail.pm nach messages.txt + ausgelagert +- Verzeichnisnamen "fertig" und "tmp" jetzt konfigurierbar +- Zeilen "Waehleradresse: " und "Wahlscheinkennung: " im Wahlschein + konfigurierbar gemacht +- Pager konfigurierbar gemacht (vorher immer "more") und standardmaessig + auf "less" gesetzt, weil "more" mit der Umleitung von STDERR Probleme hat +- kleinere Bugs behoben + +Version 4.0beta12: +- Fehler in Menue behoben +- im Menue kann man jetzt explizit Stimmen ungueltig werten, indem man + den Namen, die Adresse oder die Stimmen ungueltig macht. Es wird eine + passende Fehlermail generiert. +- genauso kann man jetzt explizit annullieren (sinnvoll, falls der Waehler + z.B. "annullierung" falsch buchstabiert hat *g*), wobei automatisch + diverse andere Probleme als irrelevant erkannt werden (z.B. braucht man + in dem Fall keinen Datenschutzhinweis zu akzeptieren und nicht unbedingt + einen Namen anzugeben, falls die Adresse stimmt) +- MIME-Kodierung fuer Subject- und From-Header in UVsendmail.pm eingefuehrt +- In Bestaetigungsmails gibt es jetzt kein gesondertes Feld mehr zum + Korrigieren des Namens, sondern es kann einfach die ohnehin vorhandene + Zeile "Wahlername: Vorname Nachname" editiert werden. +- "nametext2" in usevote.cfg ist jetzt "Waehlername:" und wird auch + statt des fest kodierten Strings an den entsprechenden Codestellen + verwendet +- uvcount.pl: Bugs bei Annullierungen und fehlendem Namen behoben +- uvbounce an geaendertes UVreadmail.pm angepasst (funktioniert bei + Aktivierung des POP3-Zugriffs nicht mehr) +- saemtliche Ausgaben/Texte in UVmenu.pm nach messages.txt ausgelagert + +Version 4.0beta11: +- kompletter Rewrite der Ueberpruefungsfunktionen in uvvote.pl und UVmenu.pl. + Es werden jetzt alle Fehler an ein Array angehaengt und in einem Rutsch + von der Menue-Funktion verarbeitet. Fuer Darstellung der Votemail wird + "more" benutzt. + +Version 4.0beta10: +- Zeilenumbrueche richten sich jetzt nach der "rightmargin"-Einstellung + aus usevote.cfg (vorher waren die Zeilenlaengen teilweise noch hartkodiert) +- Fehler beim Erstellen des domail-Scripts behoben +- kosmetische Code-Aenderungen + +Version 4.0beta9: +- Auch bei zurueckgeschickten (korrigierten) Wahlbestaetigungen wird + jetzt der Name automatisch im Body erkannt (Zeile "Waehlername:"). +- Regular Expressions zur Stimmerkennung geaendert: Manche komische + Mailprogramme benutzen zum Kodieren von Leerzeichen =A0, was aber + nach der Dekodierung nicht als \s erkannt wird. \W ist nicht optimal, + aber funktioniert. +- uvbounce.pl: Bounces von Antworten auf Wahlscheinanforderungen werden + jetzt erkannt und mit einem gesonderten Hinweis gekennzeichnet + ("Wahlschein nicht zustellbar") +- in den Config Files koennen die Kommentarzeichen escaped werden: \# + +Version 4.0beta8: +- beim Einlesen aus usevote.cfg wird ein eventuelles \r geloescht +- es werden nicht mehr jedes Mal saemtliche Mails abgerufen (bei POP3), + sondern es wird mit dem UIDL Kommando geprueft, ob schon ein vorheriger + Abruf stattfand. Ausserdem ist es jetzt moeglich, die Mails vom Server + zu loeschen. +- uvcfv.pl kann jetzt auch richtig mit POP3 umgehen +- Platzhalter im Wahlschein bei personalisierten Wahlscheinen geaendert +- Aktuelle Werte werden teilweise jetzt im Menue angezeigt (wenn man + Stimmen, Name oder Mailadresse neu gesetzt hat) +- Dokumentation verbessert + +Version 4.0beta7: +- kosmetische Code-Aenderungen (Vereinfachungen, Verschoenerungen, ...) +- Inhalt der Datei bdsgtext.txt als Abschnitt [BDSG Hinweis] in + acktext.txt uebernommen (es gibt keinen Grund dafuer, dass dieser + Text eine eigene Datei bekommen sollte...) +- Testweise einige Programm-Meldungen in externe Datei (meldungen.cfg) + ausgelagert, um eine leichtere Anpassung zu ermoeglichen (z.B. + Uebersetzung in andere Sprachen). Nach und nach werden saemtliche + Meldungen in diese Konfigurationsdatei wandern. +- acktext.txt in acktext.cfg umbenannt: Alle Konfigurationsdateien + haben damit die Endung .cfg +- POP3-Abruf und Verschicken per SMTP eingebaut +- Shellbefehl-Aufrufe (chmod- und mkdir) durch Perl-Pendants ersetzt, + um Plattformunabhaengigkeit zu bieten +- In uvcfv.pl Doppelung im Mailsubject geloescht +- In uvcount.pl stimmte die Zuordnung von Abstimmungsgegenstand zur + einzelnen Stimmabgabe nicht (umgekehrte Reihenfolge) +- in UVmenu.pm entstand bei Mehrgruppenabstimmungen der Kommentar + "Wahlleiter setzte Stimmen, Stimmen, Stimmen" (jetzt nur noch + einmal gesetzt statt fuer jede Gruppe) +- Falls keine Scheinkennung und keine BDSG-Zustimmung: Bislang wurden + dann zwei Mails generiert (keine Abfrage auf bereits aufgetretenen + Fehler), nun behoben + +Version 4.0beta6: +- RegExp fuer Namensangabe im Body verbessert (wenn kein Name angegeben + wurde und auch im Header keiner zu finden war, wurde der nachfolgende + Hinweissatz "Wenn Du keinen Namen angibst..." als Realname erkannt +- RegExp fuer Namenserkennung konfigurierbar gemacht (usevote.cfg) + und um Accents erweitert +- RegExp fuer Erkennung verdaechtiger Adressen trifft jetzt nur noch + zu, wenn der String direkt am Anfang der Adresse steht. Gegenteiliges + Verhalten kann durch Wildcards herbeigefuehrt werden +- Statt manuellen Trennens von Header und Body wird in UVreadmail.pm + jetzt das Modul MIME::Parser eingesetzt +- kleinere Bugs behoben (z.B. einfache vs. doppelte Anfuehrungsstriche) +- Fehler bei UVmenu::menu-Aufruf im Falle von "keine Stimmen" behoben + (eine Variable fehlte in der Uebergabeliste) +- uvcount.pl um Eingruppen-Format ergaenzt +- uvballot.pl um Option -t ergaenzt, um eine Vorlage fuer cfv.txt bei + Verwendung von personalisierten Wahlscheinen zu erzeugen +- uvbounce.pl zur Generierung von ungueltigen Adressen aus einer + Mailbox mit Bounces implementiert +- Bei Annullierungen wird jetzt nicht mehr die BDSG-Klausel geprueft + (es erfolgt ja ohnehin eine Loeschung der Stimmabgabe) +- Statt Mail::Field wird nun eine eigene RegExp verwendet (Danke an + Marc Brockschmidt fuer die Idee), Mail::Field hat einige unschoene Bugs. +- Bei nicht erkannten Stimmabgaben bei Abstimmungen mit nur einem + Abstimmungsgegenstand wird nun auch die Ungueltigwertung angeboten + (als Alternative zu "Enthaltung"). + +Version 4.0beta5: +- die Mailboxdatei wird nun vor der Verarbeitung verschoben, so dass waehrend + des uvvote-Laufs keine neuen Mails angehaengt werden koennen +- es wird jetzt fuer jeden Durchlauf eine gesonderte Ergebnisdatei angelegt +- neu eingefuehrter Parameter "clean", der Bestaetigungen verschickt, + Ergebnisdatei und die Mailbox in das Verzeichnis fertig/ verschiebt, + temporaere Dateien loescht und aus allen Einzelergebnissen eine neue + Gesamtergebnisdatei (normalerweise "ergebnis.alle") erstellt +- dadurch sind jetzt zwei Durchgaenge erforderlich: Erster Aufruf ohne + die Option "clean" (Erzeugt die Ergebnisse und Mailvorlagen), dann kann + eine Kontrolle erfolgen, anschliessend ein weiterer Aufruf mit der Option + "clean" zum Aufraeumen und Verschicken der Bestaetigungen +- neues Modul UVsendmail.pm, in das die Funktion zum Erstellen von + Mails ausgelagert wurde +- neues Modul UVmenu.pm mit der Menuefunktionalitaet zum interaktiven + Eingriff in die Stimmwertung +- Bugs bei Stimmaufzeichnung behoben: Fehlerhafte Stimmen wurden teilweise + nicht in der Ergebnisdatei vermerkt. Ausserdem wurde nicht konsequent + auf die Option "voteack" geprueft +- Reply-To kann jetzt beachtet werden (muss in usevote.cfg eingeschaltet + werden) und ueberschreibt das From. Mit Vorsicht zu geniessen, da so + jeder Waehler fuer andere Stimmen abgeben und die Bestaetigungen zu + sich umlenken kann! +- Umgang mit personalisierten Wahlscheinen (Abschnitt 6a der Wahlregeln + fuer de.*), Generierung und Pruefung von Scheinkennungen. Siehe Optionen + "personal" und "idfile" in usevote.cfg sowie das Programm uvcfv.pl zum + Verschicken der persoenlichen Wahlscheine +- Das Flag beim Aufruf der Menuefunktion ist jetzt ein Hash, in dem + diverse Werte ueber- und zurueckgegeben werden. Dadurch auch feine + Steuerung der aktiven Menuepunkte moeglich +- Ausgabe der Fehlermeldung in UVmenu.pm verlagert (wird in besagtem + Hash uebergeben) +- Kommentarfeld fuer Ergebisdatei ("Wahlleiter setzte xyz") wird erst + zum Schluss erzeugt, vorher werden nur die vom Wahlleiter manuell + veraenderten Felder in einem Array mitgefuehrt +- uvcfv.pl zum Generieren der persoenlichen Wahlscheine und Verschicken + des CfVs implementiert +- Bisherige Funktionalitaet von uvack, uvcount, uvdup in uvcount.pl + implementiert +- uvballot.pl zum Erstellen eines Musterwahlscheins +- uvcfv.pl: Es wird nun auch die Vollstaendigkeit von $config{bdsgtext} + im Wahlschein geprueft und bei Auswahl von "Ende+Speichern" wird + noch einmal zur Sicherheit gefragt, ob die Stimme wirklich + gespeichert und verarbeitet werden soll +- Kompatibilitaetsprobleme mit Perl 5.6.1 bei Variablenzuweisungen behoben + +Version 4.0beta4: +- bei Regelverletzung wird jetzt ein interaktives Menue aufgerufen, + so dass der Wahlleiter entscheiden kann, was er machen will +- Stimmerkennung legt unbekannte Vote-Strings (nicht Ja, Nein, + Enthaltung oder Annullierung) jetzt dem Wahlleiter vor und + laesst ihn entscheiden (Default ist Enthaltung). Fehlermail ist + in so einem Fall bislang nicht vorgesehen, laesst sich aber noch + einbauen, falls erforderlich. Wenn der Waehler die Bestaetigung + ueberprueft, reicht es auch, wenn dort "Enthaltung" auftaucht... +- Pruefung auf vollstaendige Bestaetigung der Datenschutz-Klausel + ist implementiert und ueber usevote.cfg und bdsgtext.txt konfigurierbar +- Regelpruefung kompakter und dreimal schneller gemacht (Idee von + Cornell Binder), dafuer Code schlechter lesbar... aber dokumentiert ;-) +- Alle Regel-Subs in UVrules.pm ausgelagert + +Version 4.0beta3: +- Einlesen und Testen der Konfiguration sowie Ausgabe der Regeln + im Klartext sind jetzt im externen Modul UsevoteConfig.pm untergebracht +- Das Einlesen der Mail und MIME-Bearbeitung erfolgt wurde in das + Modul UsevoteReadmail.pm ausgelagert +- verdaechtige Mailadressen werden nun in einer gesonderten Datei + konfiguriert (Default: mailpatterns.cfg) +- Fehler bei Parsing von %body% und %headbody% in acktext.txt behoben + +Version 4.0beta2: +- Auslagerung des Abschnittes [Realname Info] nach acktext.txt + (vorher hardcoded) +- Einfuehrung des Platzhalters %version% fuer acktext.txt +- Einfuehrung der Option voteack (Einzelbestaetigung kann deaktiviert werden) +- saemtliche Konfigurationsoptionen sind nun mit Defaultwerten belegt, + aber wahlspezifische Optionen (Gruppennamen etc.) muessen natuerlich + auf jeden Fall gesetzt werden +- Aenderung der Stimmerkennungsmethode. Um problemlos mit eigenwilligen + Zeilenumbruechen diverser schrottiger Software umgehen zu koennen, + wird ein Identifier an den Zeilenanfang gesetzt und direkt dahinter + die Stimme. Der Gruppenname kann dann ruhig umgebrochen sein. + +Version 4.0beta1: +- kompletter Rewrite in Perl. Noch ziemlich unvollstaendig (nur uvvote.pl) + +bis Version 3.1beta7 (Wolfgang Behrens): +- Funktionialitaet fuer persoenliche Wahlschein eingebaut +- Reply-To Auswertung u.a. auf GVV-Beduerfnisse angepasst + +Version Usevote 3.0a +- Uebersetzung ins Deutsche und Anpassung an Wahlregeln in de.* + (Frederik Ramm) + +Version Usevote 3.0 +- Urversion von Ron Dippold (Englisch), nicht an hiesige Wahlregeln angepasst diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..e77696a --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/README b/README new file mode 100644 index 0000000..e2a7e66 --- /dev/null +++ b/README @@ -0,0 +1,1284 @@ +UseVoteGer 4.09 (c) 2001-2007 Marc Langer + +UseVoteGer is a voting software for usenet votes. + +This script package is free software; you can redistribute it and/or +modify it under the terms of the GNU Public License as published by the +Free Software Foundation. + +Many thanks to: +- Ron Dippold (Usevote 3.0, 1993/94) +- Frederik Ramm (German translation, 1994) +- Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99) +- Cornell Binder for some good advice and code fragments + (e.g. UVtemplate.pm, UVformats.pm) + +This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were +written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented! + +------------------------------------------------------------------------------- + +UseVoteGer 4.09 - Usenet-Abstimmungssoftware +=========================================== + +von Marc Langer + +aktuelle Versionen: http://www.usevote.de + + +Inhaltsverzeichnis +================== + +1. Kurzbeschreibung +2. Usevote fuer Ungeduldige +3. Kompatibilitaet zu frueheren Versionen +4. Liste der Dateien +5. Anfangs-Konfiguration +6. Konfiguration fuer einzelne Abstimmungen +7. Wahlschein generieren +8. Stimmen verarbeiten +9. Unzustellbare Bestaetigungen +10. 2. CfV / Result +11. Datenschutz +12. Personalisierte Wahlscheine +13. Die einzelnen Programme +14. Die Konfigurationsdateien +15. Templates + + +1. Kurzbeschreibung +=================== + +Diese Software vereinfacht die Durchfuehrung von Usenet-Abstimmungen. +Usevote in der Urversion wurde von Ron Dippold fuer die Reorganisation +der Gruppe comp.sys.ibm.pc.games geschrieben und spaeter fuer die +allgemeine Verwendung verbessert. + +Die deutsche Uebersetzung von Frederik Ramm diente jahrelang als +Grundlage fuer CfV-Auswertungen im deutschsprachigen Raum. +Wolfgang Behrens von den German Volunteer Votetakers (GVV) passte +es spaeter an neue Begebenheiten (z.B. personalisierte Wahlscheine) +an. Jedoch liefen die UseVoteGer 3.1beta-Versionen aufgrund eines +unbekannten Fehlers nicht auf jedem System (Segmentation Fault). + +Um groessere Flexibilitaet und Plattformunabhaengigkeit zu bieten, +habe ich mich entschlossen, UseVoteGer in Perl neu zu implementieren. +Getestet wurde diese Version mit Perl 5.x unter Linux sowie +ActiveState Perl unter Windows 98. + + +2. Usevote fuer Ungeduldige +=========================== + +I. Einmalig bei der Usevote-Erstinstallation + (1) usevote.cfg nach Bedarf anpassen (selbsterklaerend) + ACHTUNG: Unter Windows gibt es das Programm "less" nicht, da "more" + aber einigermassen broken ist, sollte man sich less fuer Windows + herunterladen: http://www.greenwoodsoftware.com/less/less381d.zip + Notfalls laesst sich auch in usevote.cfg die Einstellung "pager" + auf "more" setzen, aber viel Freude hat man damit nicht... + (2) Evtl. die Templates im gleichnamigen Unterverzeichnis fuer + eigenen Geschmack anpassen. Die Templates enthalten saemtliche + Texte fuer Bestaetigungs-/Fehlermails sowie die Ausgabeformate + und Texte fuer die CfVs und das Result. + (3) Perlmodule MIME::Parser, MIME::QuotedPrint, Digest::MD5, + Date::Parse und Email::Date installieren, falls noch nicht vorhanden. + Bei Benutzung von POP3 oder SMTP (siehe usevote.cfg) muss + Libnet installiert sein (Net::POP3 und Net::SMTP). + (Fuer Perl-Einsteiger: Unter Unix geht die Modul-Installation + sehr einfach mit "perl -MCPAN -e shell", ActivePerl fuer + Windows hat einen eigenen Paketmanager) + + Achtung: Bei meinem ActivePerl 5.6.1 war eine alte Libnet-Version + enthalten, die noch keine SMTP-Authentication unterstuetzte. + Bei Benutzung dieses Features muss evtl. erst upgedated werden. + +II. Fuer jedes Voting + (1) usevote.cfg auf das durchzufuehrende Voting anpassen + (selbsterklaerend) + (2) bei Sonderregeln (Abhaengigkeiten der einzelnen Abstimmungspunkte) + in usevote.rul Regeln definieren (selbsterklaerend) + (3) CfV erstellen und Wahlschein mit uvballot.pl generieren + (4) Fertigen CfV einreichen und auf Veroeffentlichung warten ;-) + (5) Bis zum 2. CfV regelmaessig (an den ersten Tagen mehrmals taeglich, + spaeter einmal taeglich) dies tun: + - uvvote.pl durchlaufen lassen + - im Verzeichnis tmp/ die Datei ergebnis.* (Ergebnis der Auswertung) + und evtl. ack.* (Bestaetigungsmails an die Waehler) kontrollieren + - Falls es Probleme gab: Bitte den Abschnit "Stimmen verarbeiten" + weiter unten in dieser Datei genau durchlesen. Dort steht + beschrieben, wie Fehler korrigiert werden koennen + - "uvvote.pl clean" aufrufen, um die Bestaetigungen zu verschicken + und das Ergebnis zu speichern/archivieren + - Falls Wahlbestaetigungen als unzustellbar zurueckkommen, solltest + Du diese zusammen in einer Datei speichern oder in ein spezielles + POP3-Postfach leiten + (6) Zwischenbestaetigung fuer den 2. CfV mit "uvcount.pl -l" erzeugen und + veroeffentlichen. Falls Wahlbestaetigungen als unzustellbar + zurueckkamen, kannst Du mit "uvbounce.pl dateiname" automatisch eine + Liste der ungueltigen Adressen erzeugen ("dateiname" ist die Datei, + in die Du zuvor die Bounces gespeichert hattest). Siehe auch Abschnitt + "Unzustellbare Bestaetigungen" weiter unten in dieser Datei, dort + ist auch erklaert, wie die Bounces aus einem POP3-Postfach gelesen + werden koennen. + (7) Bis zum Result wieder wie (5) + (8) Zum vorgegebenen Zeitpunkt die Wahl abschliessen, + mit "uvcount.pl -r -v" Endergebnis erzeugen und veroeffentlichen + (zuvor wiederum Liste der ungueltigen Mailadressen erzeugen, siehe + Punkt 6) + +Weitere Details finden sich im Folgenden. +Jeder Nutzer dieser Software sollte sich diesen Text wenigstens einmal +komplett durchlesen. + +Abschnitt 6 beschreibt, wie mehrere Abstimmungen gleichzeitig durchgefuehrt +werden koennen, ohne saemtliche Programmdateien zu kopieren. + + +3. Kompatibilitaet zu frueheren Versionen +========================================= + +- englische Usevote-Versionen: keine Kompatibilitaet + +- deutsche Usevote-Versionen (Usevote 3.0a, UseVoteGer 3.1): + * Wahlscheine haben neue Markierungen zum sicheren Erkennen + der Stimmen. Bisherige Wahlscheine sind mit dieser Version + nicht mehr einsetzbar! + + * acktext.txt gibt es nicht mehr, statt dessen sind im Unterverzeichnis + "templates" Vorlagen zu finden, die diese Texte enthalten. + + * usevote.cfg ist komplett inkompatibel + + * usevote.rul: voll kompatibel, ohne Einschraenkungen + + * Ergebnisdateien: Inhalt kompatibel, aber Namensgebung geaendert + (obwohl irrelevant, da Umstieg auf diese Version waehrend + einer laufenden Wahl nicht moeglich) + + * scheinkennungen: Dateiformat gaendert (aus selbigen Gruenden + nicht weiter wichtig) + + * uvshell: integriert in uvvote.pl, ueber usevote.cfg steuerbar + + * uvvote.pl: Aufrufparameter geaendert / Zusammenfuehrung mit uvshell + + * uvcfv.pl: Alte Funktionalitaet ist nicht mehr implementiert, + statt dessen dient das Tool jetzt zum Generieren der persoenlichen + Wahlscheine und Verschicken des CfV an die Waehler + + * uvcount.pl: Beinhaltet auch uvack und uvdup. Drei Einzelprogramme + schienen mir ineffizient. + + * uvbounce.pl: Ausgabeformat etwas anders + + +4. Liste der Dateien +==================== + +* Ausgelieferte Dateien: + +CHANGES Versionsaenderungen +COPYING GNU General Public License +README Diese Datei +UVconfig.pm Routinen zum Einlesen und Pruefen der Konfiguration +UVformats.pm Routinen zum Formatieren von Texten in Templates +UVmenu.pm Routinen zur Interaktion mit dem Wahlleiter +UVmessage.pm Routinen zum Parsen von Texten und Ersetzen von Platzhaltern +UVreadmail.pm Routinen zum Einlesen und MIME-Decodieren der Mails +UVrules.pm Routinen zur Regelverarbeitung (usevote.rul) +UVsendmail.pm Routinen zum Erzeugen von Mails +UVtemplate.pm Routinen zur Verarbeitung von Vorlagen (Templates) +bdsgtext.cfg Spezieller Text fuer den Wahlschein (Hinweis auf + Datenschutzgesetz), muss ausserhalb Deutschlands ggfls. + angepasst oder kann ignoriert werden (bdsg=0 in usevote.cfg) +mailpatterns.cfg Wildcards fuer verdaechtige Mailadressen +messages.cfg Programm-Meldungen (Ressourcen-Datei) +scheinkennungen Speicherung der Scheinkennungen bei personalisierten + Wahlscheinen. Muss zu Beginn der Abstimmung leer sein. +usevote.cfg Konfigurationsdatei +usevote.rul Abstimmungsregeln +uvballot.pl Wahlschein aus Abstimmungsdaten erstellen +uvbounce.pl Listen von unzustellbaren Bestaetigungen erzeugen +uvcfv.pl Erstellen und Verschicken von personalisierten Wahlscheinen +uvcount.pl Stimmenzaehlung, Erstellen von Waehlerlisten und + Aussortieren von doppelten Stimmabgaben +uvvote.pl Stimmen verarbeiten (fuer die taegliche Arbeit) + +templates/ Verzeichnis mit Templates (Vorlagen) + ack-mail Template fuer Bestaetigungsmails + address-not-registered Fehlermail: Noch keine Scheinkennung fuer Adresse + vergeben + ballot Template fuer einen Wahlschein + ballot-personal Template fuer personalisierten Wahlschein + ballot-request Template fuer eine Wahlscheinanforderung + bdsg-error Fehlermail: Datenschutzklausel nicht akzeptiert + bouncelist Template fuer Liste der ungueltigen Mailadressen + cancelled Template fuer Bestaetigung der Stimmannullierung + invalid-account Fehlermail: Fragwuerdige Mailadresse (Role Account) + invalid-name Fehlermail: Kein gueltiger Name in Mail/Wahlschein + gefunden + mailheader Template fuer den Header von Mails an die Waehler + multiple-votes Fehlermail: Widerspruechliche Stimmen im Wahlschein + no-ballot Fehlermail: Kein Wahlschein in Mail gefunden + no-ballotid Fehlermail: Keine Wahlscheinkennung gefunden + no-votes Fehlermail: Keine Stimmen abgegeben + result-multi Template fuer ein Wahlergebnis mit mehreren Gruppen + result-single Template fuer ein Wahlergebnis mit einer Gruppe + result-proportional Template fuer ein Wahlergebnis, bei dem das Verhaeltnis + oder die Differenz von Ja- und Nein-Stimmen entscheidet + (z.B. Moderationsnachwahlen, "proportional=1" gesetzt) + rule-violated Fehlermail: Wahlregeln aus usevote.rul verletzt + voterlist Template fuer eine Waehlerliste (2. CfV) + votes-multi Template fuer eine Stimmenliste mit mehreren Gruppen + votes-single Template fuer eine Stimmenliste bei einer Gruppe + wrong-ballotid Fehlermail: Falsche Wahlscheinkennung angegeben + +* Beim Programmlauf erstellte Dateien (Name ist konfigurierbar): + +ergebnis.alle Saemtliche Einzelergebnisse zusammengefasst (wird + bei jedem Lauf von uvvote.pl neu erstellt) +errors.log Fehlermeldungen, die beim Programmlauf auftauchen, + werden hier abgelegt. Normalerweise bleibt die Datei + leer, ansonsten wird beim Programmende eine Warnung + mit Verweis auf die Datei ausgegeben. +uidlcache Eindeutige IDs der abgerufenen Mails (bei Benutzung + von POP3). Darf waehrend einer laufenden Abstimmung + keinesfalls geloescht werden, um Mehrfachzaehlung von + Stimmen zu verhindern. +uidlcache_req dto, fuer die Mailbox mit den Wahlscheinanforderungen + (bei Verwendung von personalisierten Wahlscheinen) +uidlcache_bounce dto, fuer die Mailbox mit den Bounces (wird von + uvbounce.pl ausgewertet) +usevote.lock Temporaere Lockdatei, um gleichzeitiges Ausfuehren + mehrerer Instanzen zu verhindern. Kann (falls vorhanden) + gefahrlos geloescht werden, wenn gerade keines der + Usevote-Scripts laeuft. + +* Folgende Unterverzeichnisse werden von Usevote im aktuellen + Verzeichnis (CWD) angelegt: + +fertig/ Verzeichnis fuer Archivierung verarbeiteter Dateien +tmp/ Verzeichnis fuer temporaere Dateien + +* Von Usevote dort erstellte Dateien: + +ack.* Bestaetigungsmails (in tmp/) +ack.control Steuerungsdatei fuer Bestaetigungsmails (in tmp/) +domail Shellscript zum Verschicken der Mails (in tmp/) +ergebnis.* Ergebnisdateien einzelner Durchlaeufe (zunaecht in tmp/, + spaeter nach fertig/ verschoben und archiviert) +stimmen.* Archivierte Stimmendateien (dto.) + + +5. Anfangs-Konfiguration +======================== + +Zuerst solltest Du "usevote.cfg" fuer Dein System anpassen (Details +dort in den Kommentarzeilen und weiter hinten in dieser Dokumentation). +Unter Windows werden die Regular Expressions fuer die Stimmenerkennung +moeglicherweise lauter unlesbare Zeichen enthalten (anderer Zeichensatz), +dort sollten saemtliche in Namen vorkommende Umlaute und Buchstaben mit +Accents etc. aufgefuehrt werden. Weiter ist bei Nutzung von Windows zu +beachten, dass das Kommando zum Loeschen des Bildschirms "cls" und nicht +"clear" heisst. Dazu gibt es auch eine Einstellung in usevote.cfg. Den +standardmaessig eingestellten Pager "less" gibt es unter Windows zunaechst +nicht, more ist aber so buggy, dass man sich am besten "less" fuer +Windows herunterladen sollte: +http://www.greenwoodsoftware.com/less/less381d.zip + +Im Unterverzeichnis "templates" findest Du Vorlagen, die fuer die +einzelnen Bestaetigungstexte, Mails, Wahlscheine und Waehlerlisten +verwendet werden. Hier kannst Du unter Beachtung der Syntax (siehe +gesonderte Dokumentation) alle Texte an Deinen Geschmack und Deine +Beduerfnisse anpassen. + + +6. Konfiguration fuer einzelne Abstimmungen +=========================================== + +Fuer jede Abstimmung muss eine eigene Konfigurationsdatei geschrieben +werden. Aendere einfach usevote.cfg entsprechend der Regeln fuer die +betreffende Abstimmung. Falls Du haeufiger Wahlleiter spielst, solltest +Du Dir eine globale Konfigurationsdatei mit Standardeinstellungen +(z.B. als ~/.usevote-defaults.cfg) anlegen und in der usevote.cfg nur +noch die Einstellungen setzen, die sich fuer jede Abstimmunge aendern. +Die globale Datei kann am Anfang der usevote.cfg mit folgender Zeile +eingebunden werden: + +include ~/.usevote-defaults.cfg + +Zu beachten ist aber, dass jeweils die letzte Definition einer Option +benutzt wird. Die usevote.cfg sollte daher wirklich nur noch +abstimmungsspezifische Einstellungen enthalten, um nicht versehentlich +die globalen Optionen zu überschreiben. + +Ausserdem muss die Datei "usevote.rul" geprueft werden. Wenn Deine +Abstimmung irgendwelche speziellen Regeln enthaelt (z.B. man kann +nur fuer Gruppe x stimmen, wenn man auch fuer y gestimmt hat, oder man +muss fuer den .misc-Abschnitt stimmen, wenn man fuer irgendeinen +anderen Abschnitt gestimmt hat), dann solltest Du diese Datei lesen +und entsprechend aendern. Allerdings empfiehlt es sich, bereits im +RfD deutlich auf die Regeln hinzuweisen, die zur Anwendung kommen sollen. +Die eingegebenen Regeln koennen dann mit "uvvote -t" getestet werden. + +Es ist auch moeglich, mehrere Abstimmungen gleichzeitig durchzufuehren, +ohne die .pl und .pm Dateien jedesmal mitzukopieren. Folgende Dateien +sollten im Abstimmungsverzeichnis vorhanden sein (notwendig ist nur +usevote.cfg, darin können alle anderen Pfade angepasst werden): + +bdsgtext.cfg +mailpatterns.cfg +messages.cfg +scheinkennungen +usevote.cfg +usevote.rul + +Ggf. sollte das "templates"-Verzeichnis kopiert werden, falls +individuelle Templates fuer einzelne Abstimmungen verwendet werden. + +Außerdem muessen i.d.R. einige Pfade in der usevote.cfg angepasst +werden. Dadurch ist auch die Angabe eines anderen Verzeichnisses +fuer einige der oben genannten Konfigurationsdateien moeglich, +falls diese zentral gepflegt werden sollen (z.B. messages.cfg und +mailpatterns.cfg). + +Damit die Usevote-Perlmodule gefunden werden, sollten sie im +selben Verzeichnis wie die Programmdateien liegen bleiben. Ansonsten +muss eine Umgebungsvariable gesetzt werden: + +PERL5LIB=.. + +.. steht für das übergeordnete Verzeichnis. Ggf. muss hier ein +absoluter Pfad eingetragen werden, wenn das Abstimmungsverzeichnis +kein Unterverzeichnis des Usevote-Verzeichnisses ist. + +Je nach Betriebssystem kann die obige Zeile etwas anders aussehen, +unter Windows sollte z.B. "SET PERL5LIB=C:\PERL\USEVOTE" in die +autoexec.bat aufgenommen oder sonstwie ausgeführt werden (Pfad +natürlich entsprechend anpassen). + + +7. Wahlschein generieren +======================== + +Der Wahlschein ist ein Abschnitt des CfV, den die Leute zur Stimmabgabe +ausfuellen und zurueckschicken sollen. Am einfachsten erzeugst Du +diesen nach Anpassung der Konfiguration und ggfls. des Templates +mit "uvballot.pl". Folgendes Format ist erforderlich: + +#1 [ Stimme ] +#2 [ Stimme ] +[...] + +Eine zusaetzliche Beschreibung rechts daneben macht es dem Waehler +einfacher, ist aber fuer Usevote nicht zwingend erforderlich. Wichtig +sind nur die fortlaufenden Nummern, die entsprechend der Eintraege +in usevote.cfg zugeordnet werden. + +Je nach Konfiguration koennen weitere Zeilen noetig sein, dazu spaeter. + +Als Stimme in den eckigen Klammern werden diverse Strings erkannt, +die in usevote.cfg definiert werden koennen. JA, NEIN und ENTHALTUNG +sind Standard, einige Abwandlungen sind ebenfalls moeglich. +Zusaetzlich kann ANNULLIERUNG verwendet werden, um eine schon abgegebene +Stimme zu loeschen und somit nicht im Result aufzutauchen. + +Bei der Verwendung von persoenlichen Wahlscheinen (Abschnitt 6a der +Wahlregeln fuer de.*) ist das Vorgehen etwas anders, siehe hierzu +den gesonderten Abschnitt weiter hinten. + + +8. Stimmen verarbeiten +====================== + +Falls POP3 nicht benutzt werden soll (siehe usevote.cfg), werden alle +Wahlmails zu einer Abstimmung aus einer Mailboxdatei gelesen +(Name/Pfad der Datei in usevote.cfg einstellbar). Standardmaessig +wird das Unix-Mailboxformat erkannt, dieses ist aber ueber "mailstart" +in usevote.cfg konfigurierbar (bei komplett anderen Formaten muesste +der Quellcode geaendert werden). + +Wechsele nun in das Verzeichnis, in dem die passenden +Konfigurationsdateien liegen (normalerweise werden diese aus +dem aktuellen Verzeichnis gelesen, was mehrere gleichzeitige +Verfahren moeglich macht) und starte uvvote.pl. Bei Problemen +mit einzelnen Stimmen wirst Du interaktiv durch Menues gefuehrt. +Meistens gibt es folgende Moeglichkeiten, die durch die +eingeklammerten Buchstaben und Zahlen aufgerufen werden koennen: + +(1) Anzeigen der Wahlmail + +Bestaetigen oder Aendern von Wahlschein-Eigenschaften: +(2) Mailadresse +(3) Waehlername +(4) Stimmen +(5) Scheinkennung +(6) Datenschutzklausel + +(i) Diese Stimme ignorieren (ohne Benachrichtigung verwerfen) +(w) Weiter + +Teilweise gibt es Unteroptionen: + (a) OK (Zweifel sind unbegründet, ist alles in Ordnung) + (b) Aendern (z.B. wenn der Realname von Usevote nicht vollstaendig + erkannt wurde) + (c) Ungueltig (es handelt sich nach Deinem Ermessen nicht um + eine gueltige Stimmabgabe) + +Nun solltest Du zunaechst mit (1) die Mail ansehen und anhand +der bemaengelten Punkte entscheiden, ob der Wahlschein gueltig +oder ungueltig ist bzw. welche Maengel tatsaechlich zutreffen. + +Mit den Optionen (2) bis (6) kannst Du einzelne Eigenschaften +des Wahlscheins nachbessern (wenn Usevote z.B. einen Namen oder +eine Stimme nicht korrekt erkannt hat) oder auch einfach die +Korrektheit bestaetigen. + +Wenn Du alle oben auf der Seite aufgelisteten Maengel beseitigst, +indem Du die entsprechenden Optionen waehlst, wird die Stimme +akzeptiert. Bleiben Zweifel unausgeraeumt, wird eine entsprechende +Fehlermail verschickt (Du erhaeltst dann noch eine gesonderte +Warnung). + +Zum Schluss kannst Du (w) wählen, um fortzufahren. Möchtest Du, +dass eine Stimme komplett ignoriert wird (weil es sich z.B. +um Spam handelt), kannst Du (i) waehlen und die folgende +Sicherheitsfrage mit "JA" beantworten. + +Ist uvvote.pl durchgelaufen, koennen im Unterverzeichnis tmp/ +(wird ebenfalls aktuellen Verzeichnis angelegt!) die ermittelten +Ergebnisse und erzeugten Mails an die Waehler noch einmal geprueft +werden. Ist alles in Ordnung, werden mittels "uvvote.pl clean" +die Mails verschickt und die Ergebnisse gespeichert sowie die +Originalmails archiviert. Hierzu wird das Verzeichnis fertig/ +verwendet. + +Falls der komplette uvvote-Lauf verworfen werden soll, kann +die Datei mit den Waehlermails (stimmen-xyz im tmp-Verzeichnis, +xyz entspricht der aktuellen Unixtime waehrend des Durchlaufs) +wieder an den urspruenglichen Platz zurueckkopiert werden +(bei POP3: fuer den Neuversuch POP3 abschalten und die +Maildatei einlesen lassen, da auf dem Server die Mails +moeglicherweise geloescht wurden) und das komplette tmp-Verzeichnis +geloescht werden. Anschliessend den uvvote-Lauf erneut durchfuehren. +Wichtig ist nur, dass das Verzeichnis fertig/ waehrend der ganzen +Abstimmung mitsamt seines Inhaltes erhalten bleibt. + +Noch ein kurzes Wort zum Verschicken der Mails: Jede Mail wird +in einer gesonderten durchnummerierten Datei (ack.1, ack.2, ...) +gespeichert, zusaetzlich wird eine Steuerungsdatei ("ack.control") +erzeugt, welche zu jeder Mail Dateinamen und Empfaenger auflistet. +Je nach Einstellung werden die Mails dann per SMTP verschickt oder +es wird ein Shellscript ("domail") erzeugt, welches nach und +nach die Mails in Sendmail einspeist. Zwecks Serialisierung ist +in usevote.cfg die Einstellung "sleepcmd" konfigurierbar, +normalerweise wird ein "sleep 1" nach jeder Mail ausgefuehrt. +Das Verschicken der Mails (und damit ggfls. der Aufruf von "domail") +erfolgt automatisch mit "uvvote.pl clean". + +Unter Windows ist nur SMTP moeglich, es sei denn, es gibt +die Befehle sendmail, sleep und rm ;-) + + +9. Unzustellbare Bestaetigungen +=============================== + +Leider kommt es immer mal wieder vor, dass jemand bei der Abstimmung +eine ungueltige Mailadresse angegeben hat. In diesem Fall erhaeltst Du +einen Bounce an die Absenderadresse zurueck (diese laesst sich in +usevote.cfg mit der Option "envelopefrom" einstellen bzw. bei +direkten Aufruf eines Mailers über die entsprechende Kommandozeilenoption +in der mailcmd-Einstellung, z.B. bei Sendmail -f). Du solltest +solche Fehlermails in einer gesonderten Datei speichern oder in +einem gesonderten POP3-Postfach halten und beim Erstellen +des 2. CfVs und des Results uvbounce.pl aufrufen. + +Ausgegeben wird von uvbounce.pl eine Liste mit ungueltigen Adressen, +die einfach an den Abschnitt "ungueltige Stimmen" des 2. CfVs bzw. +Results angehaengt werden kann. Nicht vergessen, die Stimmen manuell +aus der Liste der gueltigen Stimmen zu loeschen (falls Du die +Policy verfolgst, nur Stimmen mit gueltiger Absenderadresse zu werten, +was empfehlenswert ist) und bei Bedarf das Ergebnis zu korrigieren! + + +10. 2.CfV / Result +=================== + +Zum Erstellen des 2. CfVs kannst Du erst einmal den 1. CfV als Vorlage +benutzen. Am Ende sollte allerdings eine Liste der Personen folgen, +die bereits abgestimmt haben. Diese laesst sich mit "uvcount.pl -l" +erzeugen. Moeglicherweise fragt uvcount.pl wegen scheinbar doppelter +Stimmabgaben nach und bietet an, eine oder beide Stimmen zu ignorieren. +Du solltest das dann genauer untersuchen und die passende Wahl treffen. + +Vorsicht bei doppelten Namen: Es gibt einige doppelt vorkommende +Namen, hinter denen sich voellig unterschiedliche Personen +verbergen! Die Mailadresse sollte in solchen Faellen Auskunft +geben koennen, notfalls einmal nachfragen. + +Das Endergebnis kannst Du mit "uvcount.pl -r" ausgeben lassen. +Bei Verfahren mit mehreren Abstimmungspunkten wird automatusch +das folgende tabellarische Format verwendet: + + Ja Nein : 2/3? >=60? : ang.? : Gruppe +==== ==== : ==== ===== : ===== : ======================================= + 100 70 : Nein Ja : Nein : Einrichtung von xyz + +Bei Abstimmungen ueber nur einen Punkt gibt es auch die Moeglichkeit, +einen einfachen beschreibenden Text zu verwenden: +"Es gab 100 JA und 70 NEIN-Stimmen ...". + +Falls in usevote.cfg die Einstellung "multigroup = 1" gesetzt ist, +wird immer die tabellarische Form gewaehlt, bei "multigroup = 0" +benutzt uvcount.pl bei nur einem Abstimmungspunkt den beschreibenden +Text. Überschreiben laesst sich dieses mit Aufrufparametern: + +uvcount.pl -r -m = Tabellarisch (m = multigroup) +uvcount.pl -r -o = Text (o = onegroup) + +Falls in usevote.cfg die Option "proportional = 1" aktiviert ist, +wird standardmaessig folgendes Ausgabeformat angewandt: + + Ja Nein : J>=N? Ja/Nein : ang.? : Gruppe +==== ==== : ===== ======= : ===== : ==================================== + 100 70 : Ja 1.429 : : Einrichtung von abc + 80 90 : Nein 0.888 : : Einrichtung von xyz + +Das genaue Aussehen ist im Template "result-proportional" aenderbar, +die Bedingungen und Auswertungsformeln sind ueber usevote.cfg +einzustellen (prop_formula und condition1). Die Spalte "ang.?" muss +in diesem Fall manuell ausgefüllt werden, da manchmal auch mehrere +Abstimmungsgegenstände angenommen werden, z.B. bei Moderationsnachwahlen +für verschiedene Posten. + +Die Waehlerliste mit abgegebenen Stimmen erzeugst Du mit +"uvcount -v". Diese Option laesst sich auch mit -r kombinieren, +so dass Du Dir das doppelte Aussortieren von Duplikaten sparst. +Wichtig: Diese Liste darf erst im Result veroeffentlicht werden, +nicht im 2. CfV! + +Falls ein trivialer Schutz vor Spammern gewuenscht ist, koennen durch +eine Aenderung in den Template "voterlist", "votes-single", "votes-multi" +und "bouncelist" alle Mailadressen verfremdet werden. Entweder Du fuegst +folgende Definition ein: + +mail := value mail | replace '@' ' -at - ' + +Oder Du veraenderst direkt die vorgegebenen Formate, indem Du +die Mailadresse mit Hilfe des Pipe-Symbols durch die replace-Funktion +schickst: + +multi-line := value mail | replace '@' ' -at- ' | justify-behind name 72 + +Die Replace-Funktion erwartet wie im Beispiel gezeigt zwei Werte, +die zu ersetztende Zeichenfolge (hier das '@') und eine neue Zeichenfolge, +die statt dessen eingesetzt werden soll und frei gewaehlt werden kann. + +Wie bereits beschrieben, sollte am Ende des 2. CfVs bzw. Results noch die +Liste der gebouncten Mailadressen angehaengt werden (z.B. mit dem Programm +uvbounce.pl) und bei Bedarf die urspruenglichen Stimmen geloescht und das +Ergebnis korrigiert werden. + + +11. Datenschutz +=============== + +Zum Schutz der deutschen Wahlleiter wurden spezielle Klauseln +aufgenommen, die vom Waehler die Erlaubnis zur Verarbeitung +der Stimme verlangen. Die Option "bdsg" in usevote.cfg schaltet +diese Sonderbehandlung ein oder aus. Folgende Besonderheiten +gelten bei Aktivierung: + +- Im Wahlschein ist ein spezieller Hinweistext enthalten, der + aus der Datei bdsgtext.cfg genommen wird. Beim Verarbeiten + abgegebener Stimmen wird der Abschnitt wieder mit der Datei + verglichen. Fehlt er oder wurde er veraendert, wird der + Wahlschein dem Wahlleiter zur Kontrolle vorgelegt. + Die Fehlerpruefung ignoriert Quote- und Leerzeichen sowie + Zeilenumbrueche, kann aber versagen, wenn dem Quotezeichen + Initialen vorangestellt sind (wie in manchen Mailboxnetzen + ueblich) oder eine Silbentrennung ueber den Text gelaufen ist. + +- Ausserdem enthaelt der Wahlschein eine spezielle Frage, + die mit JA zu beantworten ist. Anderenfalls muss ebenfalls + Kontrolle durch den Wahlleiter erfolgen. Der Text dieser + Frage ist ueber usevote.cfg einzustellen. + +Erfolgt eine derartige Kontroll-Vorlage beim uvvote.pl-Lauf, +sollte der Wahlleiter sich den Wahlschein genau anschauen +und dann entweder im Menue die Option (6)(a) waehlen oder +mit (w) fortfahren. Im letzteren Fall wird die Stimme +verworfen und eine Hinweismail zurueckgeschickt. + +Wenn eine Wahlbestaetigung veraendert und zurueckgeschickt +wird, fehlt der entsprechende Datenschutzhinweis. Da in +diesem Fall die Zustimmung aber bereits bei der ersten +Stimmabgabe gegeben wurde, kann der Wahlleiter beruhigt +die Korrektheit wie oben beschrieben bestaetigen. + + +12. Personalisierte Wahlscheine +=============================== + +Die Wahlregeln fuer de.* sehen im Abschnitt 6a ein Verfahren +mit persoenlichen Wahlscheinen vor, die mit einer speziellen +Kennung versehen mehr Sicherheit vor Manipulation bieten. + +Um dieses Verfahren einzuschalten, muss zunaechst die Option +"personal = 1" in usevote.cfg gesetzt werden und das Template +"ballot-personal" im Unterverzeichnis "templates" um den Text +des CfVs erweitert werden (einfach statt des von Sternchen +umrandeten Kommentars in der Datei einfuegen). Bitte eventuell +im Text vorkommende eckige Klammern wie folgt unschaedlich +machen: \[Text\] (Backslash voranstellen), da in den Templates +diese Klammern spezielle Bedeutungen haben (siehe gesonderten +Abschnitt zu dem Thema in dieser Datei). + +Der gepostete CfV enthaelt i.d.R. nur einen Dummy-Abschnitt +mit Hinweis auf die gesonderte Wahlscheinanforderung. So ein +Abschnitt kann mit uvballot.pl generiert werden. + +Der dana-Moderation sollte beim Einreichen des CfVs auch +ein Dummy-Wahlschein mitgesendet werden, wie er bei der +Wahlscheinanforderung verschickt wird. Dieser laesst sich mit +"uvcfv.pl -t" erzeugen. + +Die eingehenden Wahlscheinanforderungen sollten an eine +spezielle Mailadresse gehen und dann mit uvcfv.pl verarbeitet +werden. Je nach Konfiguration in usevote.cfg werden die +Mails aus einer Mailboxdatei oder per POP3 eingelesen und jeweils +mit einem personalisierten Wahlschein beantwortet. Bitte vor +dem Posten des CfVs einen Test durchfuehren, um sicherzustellen, +dass die generierten Wahlscheine korrekt sind und auch bei +der Auswertung akzeptiert werden (hierbei wird die speziell +eingefuegte Wahlscheinkennung in Verbindung mit der Absenderadresse +geprueft). + + +13. Die einzelnen Programme +=========================== + +uvballot.pl + Generiert den Wahlschein fuer den CfV. Bei personalisierten + Wahlscheinen (personal = 1 in usevote.cfg) wird nur ein Dummy- + Abschnitt mit Hinweisen zur Wahlscheinanforderung ausgegeben. + + Die Vorlage fuer den Wahlschein ist im templates-Unterverzeichnis + konfigurierbar: + - ballot Standard-Wahlschein (personal=0) + - ballot-request Dummy-Abschnitt mit Hinweisen zur + Wahlscheinanforderung (personal=1) + - ballot-personal Wahlschein mit Scheinkennung, wird von + uvcfv.pl verwendet + + Aufrufparameter: + + -c config_file liest die Konfiguration aus config_file + (usevote.cfg falls nicht angegeben) + -h + --help zeigen einen Hilfetext an + + +uvbounce.pl + Liest Bounces aus den uebergebenen Dateien oder aus einer POP3- + Mailbox ein (Verhalten haengt von usevote.cfg ab) und generiert + eine Liste von unzustellbaren Adressen, die an den 2. CfV oder das + Result angehaengt werden kann. Falls POP3 in usevote.cfg + eingeschaltet und die Option -f (siehe unten) nicht benutzt wurde, + werden die uebergebenen Dateinamen ignoriert. + + Die Vorlage fuer die ausgegebene Liste ist im templates- + Unterverzeichnis konfigurierbar (Datei "bouncelist"). + + Aufrufparamter: + + -c config_file liest die Konfiguration aus config_file + -f dateiname(n) + --file dateiname(n) lesen die Bounces aus den uebergebenen Dateien, + auch wenn in der Konfigurationsdatei POP3 + eingeschaltet ist. Diese Option wird benoetigt, + falls zwar die Stimmzettel per POP3 eingelesen + werden sollen, nicht aber die Bounces. + Die Dateinamen werden durch Leerzeichen getrennt. + -h + --help zeigen einen Hilfetext an + + +uvcfv.pl + + Liest Mailboxen ein und beantwortet alle Mails mit personalisierten + CfVs (falls personal=1 in usevote.cfg). + + Die Vorlage fuer die ausgegebene Liste ist im templates- + Unterverzeichnis konfigurierbar (Datei "ballot-personal"). + + Aufrufparamter: + + -c config_file liest die Konfiguration aus config_file + (usevote.cfg falls nicht angegeben) + -t + --test gibt einen Dummy-Wahlschein mit Scheinkennung + an der Standardausgabe aus, zur Pruefungszwecken + -h + --help zeigen einen Hilfetext an + + +uvcount.pl + + Zaehlt Stimmen und gibt Waehlerlisten aus. Dient zur Erstellung + von 2.CfV und Result. + + Es werden die folgenden Vorlagen aus dem templates-Unterverzeichnis + benutzt: + + - result-multi Ergebnisauszaehlung bei mehreren Gruppen + - result-single Ergebnisauszaehlung bei nur einem Abstimmungs- + gegenstand (nicht tabellarisch, sondern im + Fliesstext), falls in usevote.cfg multigroup=0 + gesetzt ist oder die Option -o verwendet wird + (siehe unten) + - voterlist Reine Auflistung der bisherigen Waehler ohne + Stimmen (fuer 2. CfV) + - votes-multi Stimmenliste im Mehrgruppenformat (eine Liste + mit allen Waehlern, rechts daneben die jeweils + abgegebenen Stimmen) + - votes-single Stimmenliste im Eingruppenformat (getrennte + Listen mit Ja-, Nein- und Enthaltungsstimmen) + + Aufrufparamter: + + -c config_file liest die Konfiguration aus config_file + (usevote.cfg falls nicht angegeben) + -f result_file liest die Stimmen aus result_file (ueberschreibt + die "resultfile"-Angabe aus der Konfigurationsdatei) + -l + --list Geben eine Liste aller Waehler aus (ohne Stimmen). + -v + --voters Wie --list, aber mit Angabe der abgegebenen Stimmen. + -r + --result Ausgabe des Endergebnisses (kann mit --list oder + --voters kombiniert werden). + -m + --multigroup Benutzt auch bei Eingruppenabstimmungen das + Mehrgruppenformat beim Endergebnis (ueberschreibt + die Einstellung aus usevote.cfg). + Nur in Kombination mit --result verwendbar, + schliesst --onegroup aus. + -o + --onegroup Benutzt bei Eingruppenabstimmungen immer das + Eingruppenformat beim Endergebnis (ueberschreibt + die Einstellung aus usevote.cfg). + Nur in Kombination mit --result verwendbar, + schliesst --multigroup aus. + -n + --nodup Verzichtet auf das Aussortieren von doppelten + Stimmabgaben. Nicht empfohlen! + -h + --help zeigen einen Hilfetext an + + + +uvvote.pl + + Liest Mailboxen aus einer Datei oder per POP3 ein wertet die Mails + als Stimmzettel aus. Erst beim Aufruf mit der Option "clean" werden + die Ergebnisse endgueltig gespeichert und die Bestaetigungsmails + verschickt. + + Es werden diverse Vorlagen aus dem templates-Unterverzeichnis + benutzt, um Bestaetigungs- und Fehlermails zu generieren. + + Aufrufparameter: + + -c config_file liest die Konfiguration aus config_file + (usevote.cfg falls nicht angegeben) + -t + --test fuehrt einen Test der Konfiguration durch und + gibt das ermittelte Ergebnis aus. + -h + --help zeigen einen Hilfetext an + + + +14. Die Konfigurationsdateien +============================= + +mailpatterns.cfg +---------------- + +Diese Datei enthaelt einen regulaeren Ausdruck (Perl-Syntax) pro +Zeile. Passt die Mailadresse eines Abstimmenden auf eines der Muster, +wird dem Wahlleiter bei der Auswertung eine Warnung angezeigt. +In der Regel kann man diese ignorieren, weil heutzutage viele +Privatleute ihre eigene Domain haben und Role-Accounts wie news@ +benutzen. Sieht man aber so etwas wie news@t-online.de ist +Vorsicht angebracht und man sollte durch Auswahl von "(W)eiter" +die Stimme ungueltig werten und eine entsprechende Fehlermail +zurueckschicken lassen. + + +messages.cfg +------------ + +Hier sind alle kuerzeren Texte enthalten, die Usevote ausgibt, +unabhaengig davon, ob sie nur dem Wahlleiter bei der Programmausfuehrung +ausgegeben werden oder in persoenlichen Mails bzw. dem Result +auftauchen. Laengere Textpassagen sind als eigene Dateien im +"templates"-Verzeichnis zu finden und bieten weitergehende +Formatierungsmoeglichkeiten. + +Jede Zeile in der messages.cfg besteht aus einem Identifier, einem +Gleichheitszeichen und dem eigentlichen Text. Der Identifier ist +grundsaetzlich in Grossbuchstaben gehalten und vom Programmcode fest +vorgegeben. Aenderungen duerfen nur rechts vom Gleichzeichen vorgenommen +werden, wenn Dir z.B. Formulierungen nicht gefallen oder Du anders- +sprachige Ausgaben haben moechtest. + +Variablen sind ueber ${VARIABLE} einzubauen, allerdings sind die +Variablennamen und deren Inhalte vom Programmcode vorgegeben und +sollten beibehalten werden. Es gibt keine vordefinierten Variablen, +die Du selbst einbauen kannst, es sei denn, Du erweiterst den +Programmcode an der entsprechenden Stelle. + +Die einzigen Texte, die fest im Programmcode stehen, sind die +Hilfetexte der einzelnen Programme (ueber die Option -h oder --help +aufrufbar) sowie die Startmeldungen (Copyright etc.) und initiale +Fehlermeldungen (usevote.cfg oder messages.cfg nicht gefunden, +ungueltige oder falsch kombinierte Aufrufparameter). + + +usevote.rul +----------- + +Mit dieser Datei koennen spezielle Regeln fuer eine gueltige Stimmabgabe +bei Mehrgruppenabstimmungen erstellt werden, z.B. dass man Punkt 3 oder 4 +waehlen muss, wenn man fuer Punkt 2 gestimmt hat (quasi als weitergehende +Auswahlmoeglichkeit). Solche Regeln verkomplizieren aber die Abstimmung +und sollten nur in Ausnahmefaellen benutzt werden. Urspruenglich gedacht +war diese Moeglichkeit z.B. fuer .misc Gruppen, die zwangsweise bei einer +Aufteilung mit angelegt werden mussten. Dadurch, dass das in den +Wahlregeln fuer de.* als Automatismus eingebaut ist, kann man sich +so etwas meistens sparen. Fuer den Fall, dass bei einer vorgeschlagenen +neuen Gruppe der Name noch unklar ist, gibt ebenfalls das Benutzen +von speziellen Wahlregeln wenig Sinn, da man auch eine Meinung ueber +den Gruppennamen haben kann, wenn man eigentlich gegen die Gruppe ist. + +Wenn Du aber ein Verfahren betreust, in dem Du solche Sonderregeln +benoetigst, kannst Du eine Regel pro Zeile in der folgenden Syntax schreiben: + +if .jjjjj then J.... + +Eine Regel beginnt immer mit "if", und danach folgen eine Anzahl Symbole; +diese Anzahl muss gleich der Anzahl der Gruppen sein, ueber die abgestimmt +wird. Oben geht es also um eine Abstimmung ueber sechs Gruppen. +Die Symbole hinter "if" geben an, in welchem Fall diese Regel Anwendung +finden soll. Falls sie zutrifft, wird die Bedingung hinter "then" +geprueft (dort muessen nochmal so viele Symbole folgen, wie Gruppen +im Wahlschein vorkommen). Ist diese Bedingung nicht erfuellt, wird +der Wahlschein dem Wahlleiter als regelverletzend vorgelegt, und +wenn diese mit "(w)eiter" den Fehler bestaetigt, wird eine entsprechende +Fehlermeldung zurueckgeschickt und die Stimme als ungueltig gewertet. + +Folgende Symbole sind erlaubt: + + J eine JA-Stimme + N eine NEIN-Stimme + E eine Enthaltung + S eine JA- oder NEIN-Stimme + H eine Enthaltung oder JA-Stimme + I eine Enthaltung oder NEIN-Stimme + . egal (Ja, nein oder Enthaltung) + j eine oder mehrere der markierten Gruppen hat JA-Stimme + n "" "" "" "" "" "" "" NEIN-Stimme + e "" "" "" "" "" "" "" Enthaltung + s "" "" "" "" "" "" "" Ja- oder Nein-Stimme + h "" "" "" "" "" "" "" Enthaltung oder Ja-Stimme + i "" "" "" "" "" "" "" Enthaltung oder Nein-Stimme + +Hier noch ein Beispiel: + if S... then .ss. + if .S.. then ..E. + if ..S. then .E.. + +Diese Regeln sagen: Wer fuer die erste Gruppe abstimmt, der muss auch +fuer die zweite und dritte Gruppe abstimmen - egal wie. Ausserdem muss er +(Regeln 2 und 3) sich bei 3 enthalten, wenn er bei 2 eine Stimme abgibt +und umgekehrt. Die vierte Gruppe wird hier gar nicht betroffen. + + +usevote.cfg +----------- + +Dieses ist die zentrale Konfigurationsdatei fuer Usevote. Einige +Einstellungen sind nur einmal auf das System und Deine Gewohnheiten +anzupassen, andere muessen fuer jede Abstimmung geaendert werden. +Alle Einstellungen sind mit mehr oder weniger sinnvollen +Standardwerten vorgegeben, die sich bei leerer usevote.cfg mittels +"uvvote.pl -t" ermitteln lassen. + +Es lassen sich durch eine Zeile "include dateiname" weitere +Konfigurationsdateien einbinden. Die Stelle, an der dieses geschieht, +ist dabei wichtig, da die usevote.cfg von oben nach unten gelesen +wird (zusätzlich eingebundene Konfigurationsdateien werden an der +Stelle der "include"-Zeile eingefügt) und bei mehrmals vorkommenen +Optionen der jeweils letzte gesetzte Wert benutzt wird. +Daher sollte eine Datei mit systemweiten Defaults am besten am +Anfang der usevote.cfg eingebunden werden, die dort gesetzten +Optionen sollten aber unbedingt aus der usevote.cfg geloescht werden, +damit die systemweiten Einstellungen nicht gleich wieder ueberschrieben +werden. + +Das Format ist allgemein: + +Option = Wert + +Der Wert muss in manchen Faellen 0 (=aus) oder ungleich 0 (=an) sein, +in anderen Faellen muss eine exakte Zahl oder Zeichenkette eingegeben +werden. + +Alles, was hinter einem # Zeichen steht, wird als Kommentar gewertet +und ignoriert. Steht hinter einem Wert so ein Kommentar, wird +normalerweise alles bis zum Kommentarzeichen noch in den String +einbezogen. In so einem Fall sollte der Wert von Anfuehrungszeichen +umschlossen werden. Im folgenden Beispiel wird ein Dateiname +definiert: + +# Der Dateiname endet mit 10 Leerzeichen (bis zum Kommentarzeichen): +tpl_mailheader = mailheader # Kommentar + +# Der Dateiname enthaelt keine Leerzeichen, da mit Anfuehrungsstrichen +# begrenzt: +tpl_mailheader = "mailheader" # Kommentar + +Folgt kein Kommentar in der selben Zeile, koennen die Anfuehrungsstriche +weggelassen werden. + +Hier eine Auflistung der immer anzupassenden Optionen +(BOOL bedeutet, dass 0 (aus) oder 1 (an) eingetragen werden muss): + +votename Name der Abstimmung +group1 Erster Abstimmungsgegenstand +group2 Zweiter (und so weiter durchnummerieren) +votefile Mailbox mit eingehenden Stimmen (falls POP3 ausgeschaltet) +personal Personalisierte Wahlscheine verwenden? [BOOL] + (siehe gesonderten Abschnitt weiter oben) +voteaccount Mailadresse, unter der abgestimmt werden kann + (Reply-To im CfV auf diese Adresse setzen) +requestfile Datei mit Anforderungsmails bei "personal=1", falls + POP3 ausgeschaltet ist +bdsg Datenschutzklausel generieren und auf deren Existenz + pruefen? [BOOL] (siehe gesonderten Abschnitt weiter oben) +replyto Reply-To Header auswerten? [BOOL] (nicht empfohlen und in + de.* von den GVV nicht praktiziert) +voteack Jede Stimme mit einer Bestaetigungsmail beantworten? [BOOL] + (in de.* so ueblich) +mailcc weitere Mailadresse, an die eine Kopie jeder Bestaetigungs- + oder Fehlermail geschickt wird (fuer Archivzwecke) +onestep Mails direkt verschicken und Ergebnisse speichern? [BOOL] + ("uvvote clean" Aufruf entfaellt) +multigroup Fuer das Ergebnis auch bei Eingruppenabstimmung des + Mehrgruppenformat waehlen? [BOOL] +condition1 Bedingungen fuer einen Erfolg der Abstimmung in Perl-Syntax +condition2 (normalerweise "$yes>=2*$no" und "$yes>=60" +resultfile Datei fuer Gesamtergebnis (normalerweise ergebnis.alle) +idfile Datei mit Scheinkennungen (bei "personal=1") +pop3 POP3 benutzen? [BOOL] (andernfalls Stimmen aus Datei lesen) +pop3server POP3-Server fuer eingehende Wahlscheine +pop3port POP3-Serverport (normalerweise 110) +pop3user Benutzername +pop3pass Passwort +pop3delete Mails nach Abruf vom Server loeschen? [BOOL] +pop3uidlcache Dateiname zum Speichern bereits abgerufener Mail-IDs (UIDL) +pop3*_req entsprechende Optionen fuer Mailbox mit eingehenden + Wahlscheinanforderungen (bei "personal=1") +pop3*_bounce entsprechende Optionen fuer Mailbox mit Bounces (Fehlermails) + fuer die Verarbeitung durch uvbounce.pl +smtp SMTP zum Verschicken von Mails benutzen? [BOOL] + Andernfalls wird direkt an den MTA uebergeben (nur Unix) +smtpserver SMTP-Server +smtpport SMTP-Serverport (normalerweise 25) +smtpauth SMTP-Authentication nach RFC 2554 benutzen? [BOOL] +smtpuser SMTP-Benutzername +smtppass SMTP-Passwort +smtphelo String fuer die HELO-Greeting bei SMTP (Default: Hostname) +fqdn String fuer den Host-Teil der Message-ID + (Fully Qualified Domain Name) (Default: Hostname) +archivedir Verzeichnis fuer Archivierung von verarbeiteten Stimmen +tmpdir temporaeres Verzeichnis +templatedir Verzeichnis mit Templates (Vorlagen) fuer diverse Zwecke + (normalerweise "templates", dort liegen bereits vordefinierte + Templates, die i.d.R. nicht angepasst zu werden brauchen) +formats Konvertierungsfunktionen fuer die Templates, mit Komma getrennt + (mitgelieferte UVformats.pm muss hier aufgefuehrt werden, + desweiteren koennen eigene Module mit weiteren Funktionen + hier eingebunden werden) +controlfile Name der Steuerungsdatei fuer den Mailversand (tmp/ack.control) +domailfile Name des Shellscripts zum Versenden der Bestaetigungsmails + (falls smtp=0, normalerweise tmp/domail) +mailcmd Aufruf des Mail Transfer Agents (MTA) zum Verschicken der + Bestaetigungsmails (falls smtp=0), z.B. + "sendmail -oi -oem -femail@adresse" +sleepcmd Weiteres Kommando, welches nach jeder Mail aufgerufen werden + soll (falls smtp=0). Sinnvoll ist ein "sleep x", wobei x bei + langsamen Systemen hoeher gewaehlt werden sollte. +clearcmd Shellbefehl zum Loeschen des Bildschirms (Standard: clear), + muss unter Windows auf "cls" geaendert werden. Falls Shell + oder Betriebssystem keinen solchen Befehl bereitstellen, + sollte ein Kommando verwendet werden, welches eine Trennlinie + oder aehnliches auf dem Bildschirm ausgibt, z.B. mit "echo" +pager Shellbefehl zum seitenweisen Darstellen von Mails auf dem + Bildschirm (normalerweise "less"). Unter Windows muss "more" + benutzt werden, unter Unix hingegen gibt es moeglicherweise + bei "more" Probleme mit der Umleitung von STDERR in eine Datei + (wie es z.B. von uvvote.pl benoetigt wird), daher ist "less" + der Standardwert. +mailfrom Absender fuer den From-Header der Bestaetigungsmails. Wird + auch in Bestaetigungsmails als Unterschrift eingefuegt und + sollte daher am besten in folgendem Format sein: + Vorname Nachname + (ohne Anfuehrungsstriche, falls rfc-konform moeglich) +envelopefrom Absender fuer den Envelope (Return-Path) der Bestaetigungsmails + bei Verwendung von SMTP (bitte nur die Adresse eintragen, ohne + Klammern und Zusaetze). Wenn SMTP ausgeschaltet ist, muss + diese Einstellung entsprechend der Mailer-Doku in "mailcmd" + gesetzt werden (z.B. bei Sendmail und kompatiblen mit -f) +messagefile Datei mit diversen Meldungen und Textfragmenten (messages.cfg) +rulefile Datei mit Wahlregeln (usevote.rul) +badaddrfile Datei mit verdaechtigen Mailadressen (mailpatterns.cfg) +errorfile Datei fuer Fehlermeldungen beim Programmlauf (errors.log) +mailstart Einleitungszeile fuer naechste Mail (RegExp), falls Mails + aus einer Datei eingelesen werden (smtp=0), normalerweise + "^From" +begin_divider Trennlinien vor und nach dem Wahlschein +end_divider ("Alles vor/nach dieser Zeile bitte loeschen") +nametext Text fuer die Namens-Angabe im Wahlschein. Muss im + Wahlschein genauso stehen und wird beim Standardtemplate + auch aus dieser Einstellung uebernommen. Beispieltext: + "Dein Realname, falls nicht im FROM-Header:" +nametext2 Text fuer Namens-Angabe in Bestaetigungsmails + (dito, Standardwert "Waehlername:") +addresstext Text fuer die Adress-Angabe im Wahlschein + (dito, Standardwert "Waehleradresse:") +ballotidtext Text für die Angabe der Wahlscheinkennung (falls personal=1) + (dito, Standardwert "Wahlscheinkennung:") +bdsgtext Text fuer Datenschutzklausel (falls bdsg=1), erscheint als + Abstimmungspunkt (z.B. "Datenschutzklausel - Zustimmung: Ich + bin mit der Verarbeitung meiner Daten wie oben beschrieben + einverstanden") +bdsgfile Datei mit Erklaerungstext fuer BDSG-Klausel (bdsgtext.cfg) +rightmargin Zeilenbreite bei Ausgaben, die nicht durch Templates + gesteuert werden. Hat vor allem auf Bildschirmausgaben + Auswirkung. +name_re Regulaerer Ausdruck fuer Erkennung eines gueltigen Realnamens. + Beispiel: "[a-zA-ZäöüáàâéèêíìîóòôúùûÄÖÜÁÁÂÉÈÊÍÌÎÓÒÔÚÙÛß-]{2,} + + .*[a-zA-ZäöüáàâéèêíìîóòôúùûÄÖÜÁÁÂÉÈÊÍÌÎÓÒÔÚÙÛß]{2,}" + (muss ohne Umbruch in eine Zeile) + +Regulaere Ausdruecke fuer Erkennung der Stimmen +(Gross-/Kleinschreibung spielt keine Rolle): + +ja_stimme Standardwert: (J\s*A|J|(D\s*A\s*)?F\s*U\s*E\s*R) +nein_stimme Standardwert: (N\s*E\s*I\s*N|N|(D\s*A\s*)?G\s*E\s*G\s*E\s*N) +enth_stimme Standardwert: (E|E\s*N\s*T\s*H\s*A\s*L\s*T\s*U\s*N\s*G) +ann_stimme Standardwert: A\s*N\s*N\s*U\s*L\s*L\s*I\s*E\s*R\s*U\s*N\s*G + +Definition der verwendeten Templatedateien +(werden im Unterverzeichnis "templates" mitgeliefert): + +tpl_mailheader "mailheader" # generally used mail header +tpl_bouncelist "bouncelist" # used by uvbounce.pl +tpl_result_multi "result-multi" # used by uvcount.pl -r -m +tpl_result_single "result-single" # used by uvcount.pl -r -o +tpl_votes_multi "votes-multi" # used by uvcount.pl -v (multiple groups) +tpl_votes_single "votes-single" # used by uvcount.pl -v (single group only) +tpl_voterlist "voterlist" # used by uvcount.pl -l (2nd CfV) +tpl_ballot "ballot" # used by uvballot.pl (personal = 0) +tpl_ballot_request "ballot-request" # used by uvballot.pl (personal = 1) +tpl_ballot_personal "ballot-personal" # used by uvcfv.pl (personal = 1) +tpl_addr_reg "address-not-registered" # used by uvvote.pl (personal = 1) +tpl_no_ballotid "no-ballotid" # used by uvvote.pl (personal = 1) +tpl_wrong_ballotid "wrong-ballotid" # used by uvvote.pl (personal = 1) +tpl_bdsg_error "bdsg-error" # used by uvvote.pl (bdsg = 1) +tpl_ack_mail "ack-mail" # used by uvvote.pl (voteack = 1) +tpl_cancelled "cancelled" # used by uvvote.pl +tpl_invalid_account "invalid-account" # used by uvvote.pl +tpl_invalid_name "invalid-name" # used by uvvote.pl +tpl_multiple_votes "multiple-votes" # used by uvvote.pl +tpl_no_ballot "no-ballot" # used by uvvote.pl +tpl_no_votes "no-votes" # used by uvvote.pl +tpl_rule_violated "rule-violated" # used by uvvote.pl (siehe usevote.rul) + + +15. Templates +============= + +Die mitgelieferten Templates befinden sich im Verzeichnis "templates". +In usevote.cfg koennen weitere Verzeichnisse definiert werden, in +denen auch nach Templates gesucht wird ("templatedir", kommaseparierte +Liste mit Verzeichnissen). Außerdem koennen dort die Dateinamen der einzelnen +Templates eingestellt werden, wodurch es moeglich ist, in einzelnen +Abstimmungen andere, selbst definierte Templates zu verwenden, die in +einem gesonderten Verzeichnis abgelegt sind. + +Eine Templatedatei besteht aus zwei Teilen. Am Anfang werden die +Formatierungen bestimmter Schluessel definiert und nach einem Trenner +folgt der eigentlich Template-Koerper, der dann von Programm bearbeitet +und ausgegeben wird. + + format-key := function1 param | function2 param + + == TEMPLATE ==================================== + + Ich bin nun das eigentliche Template: + + format-key: [$format-key] + +Der Trenner beginnt mit den Zeichen '== TEMPLATE' danach koennen beliebige +Zeichen folgen um die beiden Sektionen optisch voneinander abzugrenzen. + +Wenn es keine Formatierungsanweisungen gibt, kann der Trenner auch +weggelassen werden. D.h. wenn kein Trenner gefunden wird, wird der +komplette Text als Template-Koerper betrachtet. + + +Template-Koerper +---------------- + +Im Template-Koerper werden die zu ersetzenden Token durch eckige +Klammern abgegrenzt. Sollen eckige Klammern im Text ausgegeben werden, +muessen diese durch einen Backslash freigestellt werden. + + [$termersetzung] [@schleife] nur eine \[ Klammer + + +Termersetzung: + + Ersetzt den Token durch den Wert des angegeben Schluessels: + + [$formatierung] [$schluessel] + + Es wird zuerst nach einer Formatierung mit den entsprechenden + Bezeichner gesucht. Ist diese vorhanden, werden die entsprechenden + Funktionen ausgefuehrt (siehe folgender Abschnitt). + + Kann kein Format gefunden werden, wird direkt in der im Programmcode + gefuellten Datenstruktur nach einem Schluessel mit dem angegeben + Bezeichner gesucht und sein Wert eingesetzt. Alle Einstellungen + aus der usevote.cfg sind automatisch als Schluessel definiert, es + kann also z.B. [$nametext] jederzeit verwendet werden. Eingesetzt + wird an der Stelle der mit "nametext = " definierte Text aus + usevote.cfg. + + Schlussendlich ist es noch moeglich einen default-Wert zu + definieren, der eingesetzt wird, wenn keiner der obigen Wege + erfolgreich war: + + Hallo [$name|Unbekannter]! + + Diese Zeile gibt, wenn kein Name definiert wurde, das Wort + "Unbekannter" aus. + + +Bedingte Verzeigung: + + Ueberprueft ob der Wert des angegebenen Formats/Schluessel boolsch + WAHR ist. Dementsprechend wird der then oder else Block eingefuegt: + + [?if|then|else] oder auch nur [?if|then] + + Die then/else Bloecke werden natuerlich auch auf Tokens geparst und + diese dementsprechend ersetzt. + + +Schleifen/Listen: + + Der nachfolgende Textblock wird fuer alle Elemente des durch den + Schluessel bezeichneten Arrays ausgefuehrt und eingefuegt. + + [@schluessel|block] oder [@schluessel|block|sep] + + Als zweiter Parameter kann ein Separtor definiert werden, mit dem + sich z.B. kommaseparierte Listen erzeugen lassen, da der Separator + eben nur zwischen den Elementen eingefuegt wird. + + Auch fuer Schleifen koennen Formatierungen genutzt werden.Allerdings + darf kein String zurueckgegeben werden, sondern ein Array mit einer + Menge von UVtemplate-Objekten. + + +Kommentare: + + Token die nach der Bearbeitungen entfernt werden: + + [# mich sieht man nicht] + + +Sonstiges: + + Um in Listen einen Zeilenumbruch zu erzwingen, muss lediglich ein + '\n' eingefuegt werden, falls eine kompakte Definition der Liste + erfolgen soll. + + [@names|[name] [email]\n] + + +Formatierungen +-------------- + +Eine Formatierung besteht eigentlich nur aus dem entsprechenden +Namen und einer beliebigen Anzahl von Funktionsaufrufen: + + format := funktion param1 "param 2" | funktion param + +Aehnlich der Unix-Shell-Funktionalitaet, wird dabei die Ausgabe +einer Funktion an die folgende weitergeleitet. So ist es moeglich, +verschiedenste simple Formatierungen zu kombinieren um nicht fuer +jeden Spezialfall eine neue Funktion schreiben zu muessen. + +Die jeweilige Formatierungsfunktion erhaelt als Input die +Datenstruktur, den Output der vorherigen Funktion und die definierten +Parameter in der entsprechenden Reihenfolge. + +Zahlen und einfache Bezeichner koennen direkt definiert werden. +Sollen Sonderzeichen oder Leerzeichen uebergeben werden, muessen +diese gequotet werden. Dazu kann ' oder " verwendet werden. + +Die Funktionen geben im Allgemeinen einen String zurueck. Im Rahmen +von Listen können auch Arrays uebergeben werden. + +Die erste Funktion duerfte ueblicherweise 'value' sein. Sie gibt +den Wert des angegeben Schluessel zurueck, der dann von den +folgenden Funktionen definiert wird: + + name-60 := value name | fill-right 60 + +Das Format "name-60" definiert also den Wert des Schluessel "name", +der um Leerzeichen aufgefuellt wird, bis eine Laenge von 60 Zeichen +erreicht wird. + + name-email := value name | justify-behind mail 72 + +"name-email" resultiert in einem String, der zwischen den Werten von +"name" und "email" genau so viele Leerzeichen enthaelt, damit der +gesamte String 72 Zeichen lang ist. + +Wird dieses Format in einer Liste angewandt, erhaelt man eine Tabelle +in der die linke Spalte linksbuendig und die rechte Spalte +entsprechend rechtsbuendig ist. + +Soweit ein kleiner Ueberblick ueber die Formatierungen. Ausfuehrliche +Funktionsbeschreibungen und weitere Beispiele finden sich in der +POD-Dokumentation des Moduls UVformat ("perldoc UVformat.pm"). + +Es ist moeglich, selbst eigene Module mit Formatierungsfunktionen +zu schreiben und ueber die Option "formats" in usevote.cfg einzubinden. +Am besten kann dazu UVformats.pm als Vorlage genommen werden. +Wichtig: der Name einer Formatierungsfunktionen darf nur aus +Gross- und Kleinbuchstaben, Zahlen und Minuszeichen bestehen. +Unterstriche, Punkte etc. sind nicht erlaubt! + +Falls eine elementare Funktion fehlt, kannst Du Dich auch gerne +bei mir melden und ich pruefe, wie sie sich einbauen laesst. + +Marc Langer, im Oktober 2005 diff --git a/UVconfig.pm b/UVconfig.pm new file mode 100644 index 0000000..c93f135 --- /dev/null +++ b/UVconfig.pm @@ -0,0 +1,329 @@ +# UVconfig: Reads config files and tests configuration +# Used by all components + +package UVconfig; + +use strict; +use Net::Domain qw(hostname hostfqdn hostdomain); +use UVmessage; +use vars qw(@ISA @EXPORT $VERSION $usevote_version %config %messages + @rules @groups $bdsg_regexp $bdsg2_regexp %ids %functions); + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw($usevote_version %config %messages @rules @groups + $bdsg_regexp $bdsg2_regexp %ids %functions); + +# Module version +$VERSION = "0.18"; + +# Usevote version +$usevote_version = "UseVoteGer 4.09"; + +sub read_config { + + my ($cfgfile, $redir_errors) = @_; + + # Default configuration options (overwritten in usevote.cfg) + %config = (votefile => "votes", + votename => "unkonfiguriertes Usevote", + resultfile => "ergebnis.alle", + rulefile => "usevote.rul", + badaddrfile => "mailpatterns.cfg", + messagefile => "messages.cfg", + idfile => "scheinkennungen", + requestfile => "anforderung", + errorfile => "errors.log", + lockfile => "usevote.lock", + replyto => 0, + personal => 0, + proportional => 0, + bdsg => 0, + onestep => 0, + multigroup => 0, + voteack => 1, + voteaccount => "<> (unkonfiguriertes Usevote)", + mailfrom => "<> (unkonfiguriertes Usevote)", + envelopefrom => "<>", + mailstart => "^From ", + archivedir => "fertig", + tmpdir => "tmp", + templatedir => "templates", + formats => "UVformats.pm", + domailfile => "tmp/domail", + controlfile => "tmp/ack.control", + mailcmd => "sendmail -oi -oem", + mailcc => "", + sleepcmd => "sleep 1", + clearcmd => "clear", + pager => "less", + pop3 => 0, + pop3server => "localhost", + pop3port => 110, + pop3user => "default", + pop3pass => "default", + pop3delete => 0, + pop3uidlcache => "uidlcache", + pop3server_req => "localhost", + pop3port_req => 110, + pop3user_req => "default", + pop3pass_req => "default", + pop3delete_req => 0, + pop3uidlcache_req => "uidlcache_req", + pop3server_bounce => "localhost", + pop3port_bounce => 110, + pop3user_bounce => "default", + pop3pass_bounce => "default", + pop3delete_bounce => 0, + pop3uidlcache_bounce => 'uidlcache_bounce', + smtp => 0, + smtpserver => 'localhost', + smtpport => 25, + smtphelo => hostfqdn(), + fqdn => hostfqdn(), + smtpauth => 0, + smtpuser => '', + smtppass => '', + name_re => '[a-zA-ZäöüÄÖÜß-]{2,} +.*[a-zA-ZäöüÄÖÜß]{2,}', + ja_stimme => '(J\s*A|J|(D\s*A\s*)?F\s*U\s*E\s*R)', + nein_stimme => '(N\s*E\s*I\s*N|N|(D\s*A\s*)?G\s*E\s*G\s*E\s*N)', + enth_stimme => '(E|E\s*N\s*T\s*H\s*A\s*L\s*T\s*U\s*N\s*G)', + ann_stimme => 'A\s*N\s*N\s*U\s*L\s*L\s*I\s*E\s*R\s*U\s*N\s*G', + condition1 => '$yes>=2*$no', # twice as many yes as no + condition2 => '$yes>=60', # min 60 yes votes + prop_formula => '$yes/$no', + tpl_ack_mail => 'ack-mail', + tpl_bouncelist => 'bouncelist', + tpl_mailheader => 'mailheader', + tpl_result_multi => 'result-multi', + tpl_result_single => 'result-single', + tpl_result_prop => 'result-proportional', + tpl_votes_multi => 'votes-multi', + tpl_votes_single => 'votes-single', + tpl_voterlist => 'voterlist', + tpl_ballot => 'ballot', + tpl_ballot_request => 'ballot-request', + tpl_ballot_personal => 'ballot-personal', + tpl_addr_reg => 'address-not-registered', + tpl_no_ballotid => 'no-ballotid', + tpl_wrong_ballotid => 'wrong-ballotid', + tpl_bdsg_error => 'bdsg-error', + tpl_cancelled => 'cancelled', + tpl_invalid_account => 'invalid-account', + tpl_invalid_name => 'invalid-name', + tpl_multiple_votes => 'multiple-votes', + tpl_no_ballot => 'no-ballot', + tpl_no_votes => 'no-votes', + tpl_rule_violated => 'rule-violated', + begin_divider => 'Alles vor dieser Zeile bitte loeschen', + end_divider => 'Alles nach dieser Zeile bitte loeschen', + nametext => 'Dein Realname, falls nicht im FROM-Header:', + nametext2 => 'Waehlername:', + addresstext => 'Waehleradresse:', + ballotidtext => 'Wahlscheinkennung:', + bdsgtext => 'Datenschutzklausel - Zustimmung', + bdsgfile => 'bdsgtext.cfg', + rightmargin => 72, + usevote_version => $usevote_version); # needed for use in templates + + # read config + read_file($cfgfile); + + # read message file + open (RES, "<$config{messagefile}") + or die "Could not read message file $config{messagefile}!\n\n"; + my @lines = ; + close(RES); + + foreach my $line (@lines) { + chomp($line); + $line =~ s/^#.*//; # Delete comments + if ($line =~ m/^\s*([A-Za-z0-9_-]+)\s*=\s*(.+)\s*$/){ + $messages{$1} = $2; + } + } + + # missing "groupX =" lines in config file? + die UVmessage::get("CONF_NOGROUPS", CONFIGFILE=>$cfgfile) . "\n\n" unless (@groups); + + # redirect errors to a file if desired by calling script + open (STDERR, ">$config{errorfile}") if ($redir_errors); + + # check for data protection law? read text for ballot + parse_bdsgtext() if ($config{bdsg}); + + # personalized ballots? read ballot IDs + read_ballot_ids() if ($config{personal}); + + load_formats() if ($config{formats}); + +} + + +############################################################################## +# read config file # +############################################################################## + +sub read_file { + + my $cfgfile = shift; + my $CONFIG; + open ($CONFIG, "<$cfgfile") or die "Could not find config file $cfgfile!\n\n"; + + while (<$CONFIG>) { + next if (/^#/); # line is a comment + chomp; # delete \n + s/\r//; # delete \r if present + s/([^\\])#.*$/$1/; # Remove comments not starting at beginning of line. + # (ignore escaped comment sign \#) + + + if (/^include (\S+)$/) { + # include other config file + read_file($1); + + } elsif (my($key, $value) = split (/\s*=\s*/, $_, 2)) { + # delete trailing spaces + $value =~ s/\s*$//; + + # evaluate quotation marks + $value =~ s/^\"([^\"]+[^\\\"])\".*$/$1/; + $value =~ s/\\"/"/g; + + if ($key =~ /^group(\d+)$/) { + my $num = $1; + $groups[$num-1] = $value; # internal index starts at 0 + } else { + $config{$key} = $value; + } + } + } + + close ($CONFIG); + +} + + +############################################################################## +# parse data protection law texts # +############################################################################## + +sub parse_bdsgtext { + + open (BDSG, "<$config{bdsgfile}") or die UVmessage::get("CONF_NOBDSGFILE", + ('BDSGFILE' => "$config{bdsgfile}")) . "\n\n"; + my @bdsg = ; + close BDSG; + + $config{bdsginfo} = ''; + + foreach my $line (@bdsg) { + $config{bdsginfo} .= $line unless ($line =~ /^\s*#/); + } + + my $bdsgtmp = $config{bdsginfo}; + $bdsgtmp =~ s/\"/\\\"/g; + $bdsgtmp =~ s/\'/\\\'/g; + $bdsgtmp =~ s/\(/\\\(/g; + $bdsgtmp =~ s/\)/\\\)/g; + $bdsgtmp =~ s/\[/\\\[/g; + $bdsgtmp =~ s/\]/\\\]/g; + $bdsgtmp =~ s/\./\\\./g; + $bdsgtmp =~ s/\!/\\\!/g; + my @bdsgtext = split(' ', $bdsgtmp); + + # Build Regular Expression from single words. + # There has to be at least a space between two words, additional characters + # are allowed, e.g. quotation marks (but no letters) + $bdsg_regexp = join('\s\W*?', @bdsgtext); + + # Build Regular Expression from $config{bdsgtext} + $bdsg2_regexp = join('\s\W*?', split(' ', $config{bdsgtext})); +} + + +############################################################################## +# Read suspicious mail addresses (normally mailpatterns.cfg) # +############################################################################## + +sub read_badaddr { + + my @bad_addr = (); + + open (BADADDR, "<$config{badaddrfile}") or die + UVmessage::get("CONF_NOBADADDR",(BADADDRFILE => $config{badaddrfile})) . "\n\n"; + + while () { + chomp; + # Comment line? Not only whitespaces? + if (/^[^#]/ && /[^\s]/) { + push(@bad_addr, $_); + } + } + + close (BADADDR); + return @bad_addr; +} + + +############################################################################## +# Read ballot IDs # +############################################################################## + +sub read_ballot_ids { + # open file with ballot ids + open(FILE, "<$config{idfile}") or return 1; + while () { + chomp; + # Format: mailaddress (whitespace) ballot ID + if (/^(.+@.+)\s+([a-z0-9]+)/) { + # $1=mailadresse, $2=ballot ID + $ids{$1} = $2; + } + } + close(FILE); + return 0; +} + + +############################################################################## +# Funktionen für Templates laden # +############################################################################## + +sub load_formats { + my $modules = $config{formats}; + + my @modules = split(/\s*,\s*/, $modules); + + foreach my $module (@modules){ + if (-r $module){ + require $module; + } + } +} + + +############################################################################## +# config test # +############################################################################## + +sub test_config { + print UVmessage::get("CONF_CONFIG"), "\n\n"; + foreach my $option (keys %config) { + print "$option = $config{$option}\n"; + } + + print "\n", UVmessage::get("CONF_TEST_RULES"); + if (@rules) { + print "\n\n"; + for (my $n=0; $n<@rules; $n++) { + my $text = UVrules::rule_print($n); + print $text; + } + print "\n"; + } else { + print UVmessage::get("CONF_NO_RULES"), "\n\n"; + } +} + +1; diff --git a/UVformats.pm b/UVformats.pm new file mode 100644 index 0000000..09e79c5 --- /dev/null +++ b/UVformats.pm @@ -0,0 +1,566 @@ +#---------------------------------------------------------------------- + package UVformats; +#---------------------------------------------------------------------- + +=head1 NAME + +UVformats - Methoden zur Stringformatierung + +=head1 SYNOPSIS + + value + append + + fill-left + fill-right + fill-center + + justify + justify-before + justify-behind + + first-words + drop-words + create-lines + + multi-graph + multi-line + + quote + replace + sprintf + + generate_date_header + +=head1 DESCRIPTION + +Dieses Modul stellt verschiedenste Methoden bereit, um die Strings in +den Templates auf die unterschiedlichste Art zu formatieren. + +Dieses Modul beschraenkt sich auf die Beschreibung der Funktionen. Ihre +Einbindung wird in UVtemplates beschrieben. + +=head1 FUNCTIONS + +=over 3 + +=cut + +#---------------------------------------------------------------------- + +use strict; +use vars qw(@ISA @EXPORT $VERSION $functions); + +use Exporter; +$VERSION = 0.01; + +@ISA = qw(Exporter); +@EXPORT = qw( getFunctions ); + +use Text::Wrap; +#use POSIX qw(strftime); +use Email::Date; + +#---------------------------------------------------------------------- + +sub getFunctions{ + return $functions; +} + +#---------------------------------------------------------------------- +=item value + +Gibt den Wert eines Schluessel zurueck. + + new-key := value 'old-key' | ... + +Diese Funktion sollte dann eingesetzt werden, wenn man einen virtuellen +Schluessel erzeugen will. D.h. der Bezeichner nicht im Template als +Schluessel vorhanden ist. Durch den Einsatz von value wird der Wert eines +anderen Schluessel kopiert und kann dann weiter formatiert werden. + +=cut + +sub value{ + my ($data, $value, $key) = @_; + return $data->getKey($key); +} + +#---------------------------------------------------------------------- + +=item append + +Den Wert eines anderen Schluessels an den bisherigen String anhaengen. + + ... | append 'other-key' | ... + +Per default wird als Trenner der beiden String ein Leerzeichen verwendet. +Soll dieses entfallen oder ein anderes Zeichen benutzt werden, so kann +ein dementsprechender drittere Parameter angegeben werden. + + ... | append 'other-key' '' | ... + ... | append 'other-key' '_' | ... + +Im ersten Beispiel wird der Wert von C nahtlos hinzugefuegt. +Im zweiten statt des Leerzeichens '_' benutzt. + +=cut + +sub append{ + my ($data, $value, $key, $sep) = @_; + + $sep = ' ' unless defined($sep); + + return $value. $sep. $data->getConvKey($key); +} + +#---------------------------------------------------------------------- + +=item fill-left, fill-right, fill-center + +Fuellt den String entsprechend mit Zeichen auf bis die gewuenschte +Laenge erreicht ist. Bei C werden die Zeichen vorranggestellt, +bei C angehaengt. C verteilt die Zeichen +gleichmaessig vor und nach dem String. + + ... | fill-left 72 '.' | ... + +Wird kein zweiter Parameter angegeben, wird automatisch das Leerzeichen +benutzt. + + ... | fill-right 60 | ... + +Ist der String bereits laenger als gewuenscht, wird er nicht weiter +veraendert und auch nicht verkuerzt. + +=cut + +sub fill_left{ + my ($data, $value, $width, $char) = @_; + + $width ||= 72; + $char = ' ' unless (defined($char) && length($char) == 1); + + my $fill = $width - length($value); + + $value = $char x $fill . $value if ($fill > 0); + + return $value; +} + +sub fill_right{ + my ($data, $value, $width, $char) = @_; + + $width ||= 72; + $char ||= ' '; + + my $fill = $width - length($value); + + $value .= $char x $fill if ($fill > 0); + + return $value; +} + +sub fill_both{ + my ($data, $value, $width, $char) = @_; + + $width ||= 72; + $char ||= ' '; + + my $fill = $width - length($value); + + if ($fill > 0){ + my $left = int($fill / 2); + my $right = $fill - $left; + + $value = $char x $left . $value . $char x $right; + } + + return $value; +} + +#---------------------------------------------------------------------- + +=item justify, justify-before, justify-behind + +Fuegt zwischen den existierenden String und dem Wert des angegebenen +Schluessel genau so viele Leerzeichen ein, damit die gewuenschte +Stringlaenge erreicht wird. + + ... | justify-behind 'key' 72 | ... + +C haengt den Wert des Schluessel an das Ende des Strings, +C stellt es davor. + + justify-behind: existing-string.........value-of-key + justify-before: value-of-key.........existing-string + +C ist lediglich ein Alias auf C. + +Sind die beiden Strings zusammen länger als die gewuenschte +Zeilenlaenge, wird automatisch einen Zeilenbruch eingefuegt +und beide Zeilen entsprechend mit Leerzeichen gefuellt. + + very-very-very-long-existing-string.........\n + ...................and-a-too-long-new-string + +=cut + +sub justify_behind{ + my ($data, $value, $key, $width) = @_; + return _justify( $value, $data->getConvKey($key), $width); +} + +sub justify_before{ + my ($data, $value, $key, $width) = @_; + return _justify( $data->getConvKey($key), $value, $width); +} + +sub _justify{ + my ($lval, $rval, $width) = @_; + + my $sep = ' '; + + if (length($lval.$rval) >= $width ){ + # wir basteln zwei zeilen + $lval .= $sep x ($width - length($lval)); + $rval = $sep x ($width - length($rval)) . $rval; + + return $lval."\n".$rval; + + }else{ + my $fill = $width - length($lval) - length($rval); + return $lval . $sep x $fill . $rval; + } +} + +#---------------------------------------------------------------------- + +=item first-words + +Gibt nur die ersten Worte eines Strings zurueck, die vollstaendig +innerhalb der angegebenen Laenge liegen. + +=cut + +sub first_words{ + my ($data, $value, $width) = @_; + + my @words = split('\s+', $value); + my $string; + + $string .= shift(@words); + + while(@words && (length($string) + length($words[0]) + 1) < $width){ + $string .= ' ' . shift(@words); + } + + return $string; +} + +=item drop-words + +Alle Woerter am Anfang des Strings entfernen, die komplett innerhalb +der angegebenen Laenge liegen. + +=cut + +sub drop_words{ + my ($data, $value, $width) = @_; + + my @words = split('\s+', $value); + + # das erste "Wort" immer verwerfen, egal wie lang es ist + my $first = shift(@words); + my $length = length($first); + + while (@words && ( $length + length($words[0]) + 1 ) < $width ){ + $length += length($words[0]) + 1; + shift(@words); + } + + return join(' ', @words); +} + +=item create-lines + +Zerlegt einen String in einen Array, in dem die einzelnen Zeilen nicht +laenger als die gewuenschte Anzahl Zeichen sind. + + absatz := value 'key' | create-lines 72 + +Mit Hilfe dieser Funktion ist es moeglich, ueberlange Zeilen zu Absatzen +umzuformatieren. + +Die Funktion erzeugt intern eine Liste, die jeweils den Schluessel C +mit dem entsprechenden String als Wert enthaelt. + +Im Template wird der so Absatz dann mit Hilfe des Schleifen-Syntax +eingebunden: + + [@absatz|[line]\n] + +Achtung! Da die Funktion keinen String zurueckgibt, sollte sie am Ende +der Kette stehen, da die normalen Formatierungsfunktionen einen String +als Input erwartern! + +=cut + +sub create_lines{ + my ($data, $value, $width) = @_; + + my @words = split('\s+', $value); + + my @lines; + + while (@words){ + my $string .= shift(@words); + + while(@words && (length($string) + length($words[0]) + 1) < $width){ + $string .= ' ' . shift(@words); + } + + my $new = $data->new( line => $string ); + push(@lines, $new); + } + + return \@lines; +} + +#---------------------------------------------------------------------- + +=item multi-graph, multi-line + +Spezielle Funktionen, um eine bestimmte graphische Ausgabe fuer +Votings mit mehreren Abstimmungspunkten zu erzeugen: + + Punkt 1 --------------------------+ + Punkt 2a ------------------------+| + Punkt 2b -----------------------+|| + Punkt 3 -----------------------+||| + |||| + Name of Voter 1 jjnn + Name of Voter 2 nnjj + +C ist hierbei für die Formatierung der einzelnen Abstimmungspunkte +zustaendig. + + multi-graph 'key' 'width' 'pos-key' 'max-key' + +Der erste Parameter gibt den Schluessel an, dessen Wert als Abstimmungspunkt +ausgegeben werden soll. C die Laenge des zu erzeugenden Strings. +C und C sind die Namen der Schluessel, in denen stehen +muss, um den wievielten Abstimmungspunkt es sich handelt (per default 'pos') +und wieviele Abstimmungspunkte es insgesamt gibt ('anzpunkte'). + +C erzeugt einfach nur einen String in der gewuenschten +Laenge, der entsprechend der Anzahl der Abstimmungspunkte mit '|' +abschliesst. + +=cut + +sub mgraph{ + my ($data, $value, $width, $pkey, $okey) = @_; + return unless $data; + + my $pos = $data->getKey($pkey || 'pos'); + my $of = $data->getKey($okey || 'anzpunkte'); + + my $gfx = ''; + + $gfx = ' ---'.'-' x ($of-$pos) .'+'. '|' x ($pos - 1) if ($pos && $of); + + if (length($value.$gfx) < $width){ + $value = ' ' x ($width - length($value.$gfx)) . $value . $gfx; + + }elsif (length($value.$gfx) > $width){ + my @lines = _wrap($value, $width - length($gfx)); + + $value = shift(@lines) . $gfx; + $value = ' ' x ($width - length($value)) . $value; + + # Hilfzeile erzeugen + $gfx = ' '.' ' x ($of-$pos) . '|' x ($pos) if ($pos && $of); + + foreach my $line (@lines){ + $value .= "\n".' ' x ($width - length($line.$gfx)) . $line . $gfx; + } + } + + return $value; +} + +sub mgline{ + my ($data, undef, $width, $okey) = @_; + return unless $data; + + my $of = $data->getKey($okey || 'anzpunkte') || 0; + + return ' ' x ($width - $of) . '|' x $of; +} + + +sub _wrap{ + my ($string, $width) = @_; + + my @words = split('\s+', $string); + + my @lines; + + while (@words){ + my $line .= shift(@words); + + while(@words && (length($line) + length($words[0]) + 1) < $width){ + $line .= ' ' . shift(@words); + } + + push(@lines, $line); + } + + return @lines; +} + + +#---------------------------------------------------------------------- + +=item quote + +Stellt in einem (mehrzeiligem) String jeder Zeile den gewuenschten +Quotestring voran. + + body := value 'body' | quote '> ' + +=cut + +sub quote{ + my ($data, $value, $quotechar) = @_; + + $quotechar = '> ' unless defined($quotechar); + + $value =~ s/^/$quotechar/mg; + return $value; +} + + +#---------------------------------------------------------------------- + +=item replace + +Ersetzt in einem String ein oder mehrere Zeichen durch eine beliebige +Anzahl anderer Zeichen. Diese Funktion kann z.B. genutzt werden, um +beim Result die Mailadressen zu verfremden (Schutz vor Adress-Spidern). + + mail := value 'mail' | replace '@' '-at-' + +=cut + +sub replace{ + my ($data, $value, $original, $replacement) = @_; + + $original = ' ' unless defined($original); + $replacement = ' ' unless defined($replacement); + + $value =~ s/\Q$original\E/$replacement/g; + return $value; +} + + +#---------------------------------------------------------------------- + +=item sprintf + +Gibt Text oder Zahlen mittels der Funktion sprintf formatiert aus +(siehe "man 3 sprintf" oder "perldoc -f sprintf"). + + proportion := value 'proportion' | sprintf '%6.3f' + +=cut + +sub sprintf{ + my ($data, $value, $format) = @_; + + $format = '%s' unless defined($format); + + return sprintf($format, $value); +} + + +#---------------------------------------------------------------------- + +=item generate_date_header + +Gibt ein Datum im RFC822-Format zur Verwendung im Date:-Header einer +Mail aus. + + date := generate_date_header + +=cut + +sub generate_date_header{ + my ($data, $value, $format) = @_; + #return strftime('%a, %d %b %Y %H:%M:%S %z', localtime); + return format_date; +} + +#---------------------------------------------------------------------- + +=item generate_msgid + +Gibt eine Message-ID im RFC822-Format zur Verwendung im Message-ID:-Header +einer Mail aus. + + msgid := generate_msgid + +=cut + +sub generate_msgid{ + return ("<".$$.time().rand(999)."\@".$UVconfig::config{fqdn}.">"); +} + + +#---------------------------------------------------------------------- + +BEGIN{ + %UVconfig::functions = ( %UVconfig::functions, + value => \&value, + append => \&append, + + 'fill-left' => \&fill_left, + 'fill-right' => \&fill_right, + 'fill-both' => \&fill_both, + + justify => \&justify_behind, + 'justify-behind' => \&justify_behind, + 'justify-before' => \&justify_before, + + 'first-words' => \&first_words, + 'drop-words' => \&drop_words, + + 'create-lines' => \&create_lines, + + 'multi-graph' => \&mgraph, + 'multi-line' => \&mgline, + + 'quote' => \"e, + 'replace' => \&replace, + 'sprintf' => \&sprintf, + + 'generate-date-header' => \&generate_date_header, + 'generate-msgid' => \&generate_msgid + ); +} + +1; + +#---------------------------------------------------------------------- + +=back + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Cornell Binder +Marc Langer diff --git a/UVmenu.pm b/UVmenu.pm new file mode 100644 index 0000000..d90da9c --- /dev/null +++ b/UVmenu.pm @@ -0,0 +1,380 @@ +# UVmenu: menu for interaction with the votetaker +# Used by uvvote.pl, uvcfv.pl, uvcount.pl + +package UVmenu; + +use strict; +use UVconfig; +use UVmessage; +use UVrules; +use vars qw($VERSION); + +use Text::Wrap qw(wrap $columns); + +# Module version +$VERSION = "0.4"; + +############################################################################## +# Menu for interaction with the votetaker # +# Parameters: votes list and header (references to arrays) # +# Body, Mailadress, Name, Ballot ID (references to strings) # +# List of newly set fields (reference to array) # +# List of errors to correct (Array-Ref) # +# Return Values: 'w': proceed # +# 'i': ignore (don't save vote) # +############################################################################## + +sub menu { + my ($votes, $header, $body, $addr, $name, $ballot_id, $set, $errors) = @_; + my $input = ""; + my $voter_addr = $$addr || ''; + my $voter_name = $$name || ''; + my @newvotes = @$votes; + my $mailonly = 0; + my %errors; + $$ballot_id ||= ''; + + foreach my $error (@$errors) { + + # unrecognized vote: extract group number und display warning + if ($error =~ /^UnrecognizedVote #(\d+)#(.+)$/) { + $errors{UnrecognizedVote} ||= UVmessage::get("MENU_UNRECOGNIZEDVOTE"); + $errors{UnrecognizedVote} .= "\n " . UVmessage::get("MENU_UNRECOGNIZED_LIST") + . " #$1: $2"; + + # violated rule: extract rule number and display warning + } elsif ($error =~ /^ViolatedRule #(\d+)$/) { + $errors{ViolatedRule} ||= UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$1")); + + } else { + # special handling if called from uvballot.pl + $mailonly = 1 if ($error =~ s/Ballot$//); + + # get error message for this error from messages.cfg + $errors{$error} = UVmessage::get("MENU_" . uc($error)); + } + } + + # This loop is only left by 'return' + while (1) { + + system($config{clearcmd}); + print UVmessage::get("MENU_PROBLEMS") . "\n"; + + foreach my $error (keys %errors) { + print "* $errors{$error}\n"; + } + + my $menucaption = UVmessage::get("MENU_CAPTION"); + print "\n\n$menucaption\n"; + print "=" x length($menucaption), "\n\n"; + print "(1) ", UVmessage::get("MENU_SHOW_MAIL"), "\n\n", + UVmessage::get("MENU_CHANGE_PROPERTIES"), "\n", + "(2) ", UVmessage::get("MENU_ADDRESS"), " [$voter_addr]\n"; + + # don't print these options if called from uvcfv.pl + unless ($mailonly) { + print "(3) ", UVmessage::get("MENU_NAME"), " [$voter_name]\n"; + print "(4) ", UVmessage::get("MENU_VOTES"), " [", @$votes, "]\n"; + print "(5) ", UVmessage::get("MENU_BALLOT_ID"), " [$$ballot_id]\n" + if ($config{personal}); + print "(6) ", UVmessage::get("MENU_BDSG"), "\n" if ($config{bdsg}); + } + + print "\n", + "(i) ", UVmessage::get("MENU_IGNORE"), "\n", + "(w) ", UVmessage::get("MENU_PROCEED"), "\n\n", + UVmessage::get("MENU_PROMPT"); + + do { $input = ; } until ($input); + chomp $input; + print "\n"; + + # only accept 1, 2, i and w if called from uvcfv.pl + next if ($mailonly && $input !~ /^[12iw]$/i); + + if ($input eq '1') { + system($config{clearcmd}); + # ignore SIGPIPE (Bug in more and less) + $SIG{PIPE} = 'IGNORE'; + open (MORE, "|$config{pager}"); + print MORE join("\n", @$header), "\n\n", $$body, "\n"; + close (MORE); + + print "\n", UVmessage::get("MENU_GETKEY"); + $input = ; + + } elsif ($input eq '2') { + my $sel; + do { + print "[a] ", UVmessage::get("MENU_ADDRESS_OK"), "\n", + "[b] ", UVmessage::get("MENU_ADDRESS_CHANGE"), "\n", + "[c] ", UVmessage::get("MENU_ADDRESS_INVALID"), "\n\n", + UVmessage::get("MENU_PROMPT"); + $sel = ; + } until ($sel =~ /^[abc]$/i); + if ($sel =~ /^a$/i) { + delete $errors{SuspiciousAccount}; + delete $errors{InvalidAddress}; + next; + } elsif ($sel =~ /^c$/i) { + delete $errors{SuspiciousAccount}; + $errors{InvalidAddress} = UVmessage::get("MENU_INVALIDADDRESS") . " " . + UVmessage::get("MENU_INVALIDADDRESS2"); + next; + } + + do { + print "\n", UVmessage::get("MENU_ADDRESS_PROMPT"), " "; + $voter_addr = ; + chomp ($voter_addr); + } until ($voter_addr); + $$addr = $voter_addr; + push (@$set, 'Adresse'); + delete $errors{SuspiciousAccount}; + delete $errors{InvalidAddress}; + check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids); + + } elsif ($input eq '3') { + my $sel; + do { + print "[a] ", UVmessage::get("MENU_NAME_OK"), "\n", + "[b] ", UVmessage::get("MENU_NAME_CHANGE"), "\n", + "[c] ", UVmessage::get("MENU_NAME_INVALID"), "\n\n", + UVmessage::get("MENU_PROMPT"); + $sel = ; + } until ($sel =~ /^[abc]$/i); + if ($sel =~ /^a$/i) { + delete $errors{InvalidName}; + next; + } elsif ($sel =~ /^c$/i) { + $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME"); + next; + } + print UVmessage::get("MENU_NAME"), ": "; + $voter_name = ; + chomp ($voter_name); + $$name = $voter_name; + push (@$set, 'Name'); + delete $errors{NoName}; + delete $errors{InvalidName}; + + $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME") + unless ($voter_name =~ /$config{name_re}/); + + } elsif ($input eq '4') { + # set votes + + my $sel; + do { + print "[a] ", UVmessage::get("MENU_VOTES_OK"), "\n", + "[b] ", UVmessage::get("MENU_VOTES_RESET"), "\n", + "[c] ", UVmessage::get("MENU_VOTES_INVALID"), "\n", + "[d] ", UVmessage::get("MENU_VOTES_CANCELLED"), "\n\n", + UVmessage::get("MENU_PROMPT"); + $sel = ; + } until ($sel =~ /^[abcd]$/i); + if ($sel =~ /^[ad]$/i) { + delete $errors{NoVote}; + delete $errors{UnrecognizedVote}; + delete $errors{ViolatedRule}; + delete $errors{DuplicateVote}; + if ($sel =~ /^d$/i) { + # cancelled vote: replace all votes with an A + @$votes = split(//, 'A' x scalar @groups); + push @$set, 'Stimmen'; + # some errors are irrelevant when cancelling a vote: + delete $errors{InvalidName}; + delete $errors{NoName}; + delete $errors{InvalidBDSG}; + delete $errors{InvalidAddress}; + delete $errors{SuspiciousAccount}; + } + next; + } elsif ($sel =~ /^c$/i) { + $errors{NoVote} = UVmessage::get("MENU_INVALIDVOTE"); + next; + } + + # Set columns for Text::Wrap + $columns = $config{rightmargin}; + print "\n", wrap('', '', UVmessage::get("MENU_VOTES_REENTER_ASK")), "\n\n"; + print UVmessage::get("MENU_VOTES_REENTER_LEGEND"), "\n"; + + for (my $n=0; $n<@groups; $n++) { + my $voteinput = ""; + $votes->[$n] ||= 'E'; + + # repeat while invalid character entered + while (!($voteinput =~ /^[JNE]$/)) { + my $invalid = $#groups ? 0 : 1; + print UVmessage::get("MENU_VOTES_REENTER", (GROUP => $groups[$n])); + $voteinput = ; + chomp $voteinput; + $voteinput ||= $votes->[$n]; + $voteinput =~ tr/jne/JNE/; + } + + # valid input, save new votes + $newvotes[$n] = $voteinput; + } + + print "\n\n"; + my $oldvotes = UVmessage::get("MENU_VOTES_REENTER_OLD"); + my $newvotes = UVmessage::get("MENU_VOTES_REENTER_NEW"); + my $oldlen = length($oldvotes); + my $newlen = length($newvotes); + my $maxlen = 1 + (($newlen>$oldlen) ? $newlen : $oldlen); + print $oldvotes, ' ' x ($maxlen - length($oldvotes)), @$votes, "\n", + $newvotes, ' ' x ($maxlen - length($newvotes)), @newvotes, "\n\n"; + + do { + print "[a] ", UVmessage::get("MENU_VOTES_REENTER_ACK"), " ", + "[b] ", UVmessage::get("MENU_VOTES_REENTER_NACK"), "\n\n", + UVmessage::get("MENU_PROMPT"); + $sel = ; + } until ($sel =~ /^[ab]$/i); + + next if ($sel =~ /^b$/i); + @$votes = @newvotes; + push @$set, 'Stimmen'; + delete $errors{UnrecognizedVote}; + delete $errors{DuplicateVote}; + delete $errors{NoVote}; + delete $errors{ViolatedRule}; + + if (my $rule = UVrules::rule_check($votes)) { + $errors{ViolatedRule} = UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$rule")); + } + + } elsif ($input eq '5' && $config{personal}) { + print "\n", UVmessage::get("MENU_BALLOT_ID"), ": "; + $$ballot_id = ; + chomp ($$ballot_id); + push (@$set, 'Kennung'); + check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids); + + } elsif ($input eq '6' && $config{bdsg}) { + my $sel; + do { + print "[a] ", UVmessage::get("MENU_BDSG_ACCEPTED"), "\n", + "[b] ", UVmessage::get("MENU_BDSG_DECLINED"), "\n\n", + UVmessage::get("MENU_PROMPT"); + $sel = ; + } until ($sel =~ /^[ab]$/i); + + if ($sel =~ /^a$/i) { + delete $errors{InvalidBDSG}; + } else { + $errors{InvalidBDSG} = UVmessage::get("MENU_INVALIDBDSG"); + } + + } elsif ($input =~ /^i$/i) { + my $ignore = UVmessage::get("MENU_IGNORE_STRING"); + # Set columns for Text::Wrap + $columns = $config{rightmargin}; + print wrap('', '', UVmessage::get("MENU_IGNORE_WARNING", + (MENU_IGNORE_STRING => $ignore) + )); + if ( eq "$ignore\n") { + print "\n"; + return "i"; + } + + } elsif ($input =~ /^w$/i) { + + if (keys %errors) { + if ((keys %errors)==1 && $errors{UnrecognizedVote}) { + # unrecognized vote lines aren't errors if votetaker + # did not change them + @$errors = (); + } else { + # Set columns for Text::Wrap + $columns = $config{rightmargin}; + @$errors = keys %errors; + my $warning = ' ' . UVmessage::get("MENU_ERROR_WARNING") . ' '; + my $length = length($warning); + print "\n", '*' x (($config{rightmargin}-$length)/2), $warning, + '*' x (($config{rightmargin}-$length)/2), "\n\n", + wrap('', '', UVmessage::get("MENU_ERROR_TEXT")), "\n\n", + '*' x $config{rightmargin}, "\n\n", + UVmessage::get("MENU_ERROR_GETKEY"); + my $input = ; + next if ($input !~ /^y$/i); + print "\n"; + } + } else { + @$errors = (); + } + + system($config{clearcmd}); + print "\n", UVmessage::get("MENU_PROCESSING"), "\n"; + return "w"; + } + } + + sub check_ballotid { + my ($errors, $voter_addr, $ballot_id, $ids) = @_; + + return 0 unless ($config{personal}); + + delete $errors->{NoBallotID}; + delete $errors->{WrongBallotID}; + delete $errors->{AddressNotRegistered}; + + if ($$ballot_id) { + if ($ids->{$$voter_addr}) { + if ($ids->{$$voter_addr} ne $$ballot_id) { + # ballot id incorrect + $errors->{WrongBallotID} = UVmessage::get("MENU_WRONGBALLOTID"); + } + } else { + $errors->{AddressNotRegistered} = UVmessage::get("MENU_ADDRESSNOTREGISTERED"); + } + } else { + $errors->{NoBallotID} = UVmessage::get("MENU_NOBALLOTID"); + } + } + +} + + +############################################################################## +# Menu for sorting out duplicate votings manually # +# Parameters: References to hashes with the paragraphs from the result file # +# and the default value # +# Return value: selected menu item (1, 2 or 0) # +############################################################################## + +sub dup_choice { + my ($vote1, $vote2, $default) = @_; + + print STDERR "\n", UVmessage::get("MENU_DUP_VOTE"), "\n\n"; + print STDERR UVmessage::get("MENU_DUP_FIRST"), "\n"; + print STDERR "A: $vote1->{A}\n"; + print STDERR "N: $vote1->{N}\n"; + print STDERR "D: $vote1->{D}\n"; + print STDERR "K: $vote1->{K}\n"; + print STDERR "S: $vote1->{S}\n\n"; + print STDERR UVmessage::get("MENU_DUP_SECOND"), "\n"; + print STDERR "A: $vote2->{A}\n"; + print STDERR "N: $vote2->{N}\n"; + print STDERR "D: $vote2->{D}\n"; + print STDERR "K: $vote2->{K}\n"; + print STDERR "S: $vote2->{S}\n\n"; + print STDERR "1: ", UVmessage::get("MENU_DUP_DELFIRST"), "\n", + "2: ", UVmessage::get("MENU_DUP_DELSECOND"), "\n", + "0: ", UVmessage::get("MENU_DUP_DELNONE"), "\n\n"; + + my $input; + + do { + print STDERR UVmessage::get("MENU_PROMPT"), "[$default] "; + $input = ; + chomp $input; + } until ($input eq '' || ($input >= 0 && $input<3)); + + return $input || $default; +} + +1; diff --git a/UVmessage.pm b/UVmessage.pm new file mode 100644 index 0000000..a52d8d5 --- /dev/null +++ b/UVmessage.pm @@ -0,0 +1,32 @@ +# UVmessages: parses resource strings and substitutes variables +# Used by all components + +package UVmessage; + +use strict; +use vars qw(@ISA @EXPORT_OK $VERSION); + +require Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(get); + +# Module version +$VERSION = "0.1"; + +sub get { + my ($key, %param) = @_; + + my $string = $UVconfig::messages{$key} || return ''; + + while ($string =~ m/\$\{([A-Za-z0-9_-]+)\}/) { + my $skey = $1; + my $sval = $param{$skey}; + $sval = '' unless defined($sval); + + $string =~ s/\$\{$skey\}/$sval/g; + } + + return $string; +} + +1; diff --git a/UVreadmail.pm b/UVreadmail.pm new file mode 100644 index 0000000..e1e599c --- /dev/null +++ b/UVreadmail.pm @@ -0,0 +1,225 @@ +# UVreadmail: functions for reading and processing mailfiles +# Used by uvvote.pl, uvcfv.pl, uvbounce.pl + +package UVreadmail; + +use strict; +use UVconfig; +use UVmessage; +use MIME::QuotedPrint; +use MIME::Base64; +use MIME::Parser; +use POSIX qw(strftime); + +use vars qw($VERSION); + +# Module version +$VERSION = "0.11"; + +sub process { + + # $filename: file containing bounces or (if POP3 is enabled) where + # mails should be saved + # $callsub: reference to a sub which should be called for each mail + # $caller: 0 = uvvote.pl, 1 = uvcfv.pl, 2 = uvbounce.pl + # 3 = uvbounce.pl but POP3 disabled (overrides $config{pop3} + # + + my ($filename, $callsub, $caller) = @_; + my ($voter_addr, $voter_name, $body); + my $count = 0; + my ($pop3server, $pop3user, $pop3pass, $pop3delete, $pop3uidlcache); + my @mails = (); + $caller ||= 0; + + if ($config{pop3} && $caller<3) { + + if ($caller == 1) { + # Ballot request (personal = 1 set in usevote.cfg) from uvcfv.pl + $pop3server = $config{pop3server_req} . ':' . $config{pop3port_req}; + $pop3user = $config{pop3user_req}; + $pop3pass = $config{pop3pass_req}; + $pop3delete = $config{pop3delete_req}; + $pop3uidlcache = $config{pop3uidlcache_req}; + } elsif ($caller == 2) { + # called from uvbounce.pl + $pop3server = $config{pop3server_bounce} . ':' . $config{pop3port_bounce}; + $pop3user = $config{pop3user_bounce}; + $pop3pass = $config{pop3pass_bounce}; + $pop3delete = $config{pop3delete_bounce}; + $pop3uidlcache = $config{pop3uidlcache_bounce}; + } else { + $pop3server = $config{pop3server} . ':' . $config{pop3port}; + $pop3user = $config{pop3user}; + $pop3pass = $config{pop3pass}; + $pop3delete = $config{pop3delete}; + $pop3uidlcache = $config{pop3uidlcache}; + } + + # read list of seen mails (UIDLs) + my %uidls = (); # hash for quick searching + my @uidls = (); # array to preserve order + my $cacheexist = 1; + open (UIDLCACHE, "<$pop3uidlcache") or $cacheexist = 0; + if ($cacheexist) { + while (my $uidl = ) { + chomp ($uidl); + $uidls{$uidl} = 1; + push (@uidls, $uidl); + } + close (UIDLCACHE); + } + + print UVmessage::get("READMAIL_STATUS"), "\n" unless ($caller == 2); + + # open POP3 connection and get new mails + use Net::POP3; + my $pop = Net::POP3->new($pop3server) + or die UVmessage::get("READMAIL_NOCONNECTION") . "\n\n"; + + my $mailcount = $pop->login($pop3user, $pop3pass); + + die UVmessage::get("READMAIL_NOLOGIN") . "\n\n" unless ($mailcount); + + for (my $n=1; $n<=$mailcount; $n++) { + my $uidl = $pop->uidl($n); + if ($uidl) { + next if ($uidls{$uidl}); + $uidls{$uidl} = 1; + push (@uidls, $uidl); + } + my $mailref = $pop->get($n) + or print STDERR UVmessage::get("READMAIL_GET_PROBLEM", (NR => $n)) . "\n"; + my $mail = join ('', @$mailref); + my $fromline = 'From '; + if ($mail =~ /From: .*?<(.+?)>/) { + $fromline .= $1; + } elsif ($mail =~ /From:\s+?(\S+?\@\S+?)\s/) { + $fromline .= $1; + } else { + $fromline .= 'foo@bar.invalid'; + } + $fromline .= ' ' . strftime ('%a %b %d %H:%M:%S %Y', localtime) . "\n"; + push (@mails, $fromline . $mail); + if ($pop3delete) { + $pop->delete($n) + or print STDERR UVmessage::get("READMAIL_DEL_PROBLEM", (NR => $n)) . "\n"; + } + } + + # save UIDLs + my $uidlerr = 0; + open (UIDLCACHE, ">$pop3uidlcache") or $uidlerr = 1; + if ($uidlerr) { + print STDERR UVmessage::get("READMAIL_UIDL_PROBLEM") . "\n"; + print STDERR UVmessage::get("READMAIL_UIDL_PROBLEM2") . "\n"; + } else { + print UIDLCACHE join("\n", @uidls); + close (UIDLCACHE) or print STDERR UVmessage::get("READMAIL_UIDL_CLOSE") . "\n"; + } + + # make archive of all mails + my $fileproblem = 0; + open (VOTES, ">$filename") or $fileproblem = 1; + if ($fileproblem) { + print STDERR UVmessage::get("READMAIL_ARCHIVE_PROBLEM", + (FILE => $filename)) . "\n"; + } else { + print VOTES join ("\n", @mails); + close (VOTES) + or print STDERR UVmessage::get("READMAIL_ARCHIVE_CLOSE", + (FILE => $filename)) . "\n"; + } + + $pop->quit(); + + } else { + # open mail file + open(VOTES, "<$filename") + or die UVmessage::get("READMAIL_NOMAILFILE", (FILE => $filename)) . "\n\n"; + + # read all mails + my $i = 0; + while () { + if (/$config{mailstart}/) { + $i++; + } + $mails[$i] = ($mails[$i] || "") . $_; + } + + # close mail file + close(VOTES); + } + + foreach my $mail (@mails) { + next unless $mail; + + # split mail into array and remove first line (from line) + my @mail = split(/\n/, $mail); + shift (@mail) if ($mail[0] =~ /^From /); + + # generate MIME-Parser object for the mail + my $parser = new MIME::Parser; + # headers are to be decoded + $parser->decode_headers(1); + # don't write into file + $parser->output_to_core(1); + + # read mail + my $entity = $parser->parse_data(join("\n", @mail)); + my $head = $entity->head; + + # extract address and name + my $from = $head->get('From') || ''; + + if ($from =~ /\s*([^<]\S+\@\S+[^>]) \((.+)\)/) { + ($voter_addr, $voter_name) = ($1, $2); + } elsif ($from =~ /\s*\"?([^\"]+)\"?\s*<(\S+\@\S+)>/) { + ($voter_name, $voter_addr) = ($1, $2); + $voter_name =~ s/\s+$//; # kill spaces at the end + } elsif ($from =~ /\s*]+)>?[^\(\)]*/) { + ($voter_addr, $voter_name) = ($1, ''); + } else { + # initialize with empty value + $voter_addr = ''; + $voter_name = ''; + } + + # look at reply-to? + if ($config{replyto}) { + + my $replyto = Mail::Field->new('Reply-To', $head->get('Reply-To')); + + # Address in Reply-To? + ($voter_addr) = $replyto->addresses() if ($replyto->addresses()); + + # Name in reply-to? + if ($replyto->names()) { + my ($nametmp) = $replyto->names(); + $voter_name = $nametmp unless ($nametmp =~ /^\s*$/); + } + + } + + # decode body + my $encoding = $head->get('Content-Transfer-Encoding') || ''; + if ($encoding =~ /quoted-printable/i) { + $body = decode_qp($entity->stringify_body); + } elsif ($encoding =~ /base64/i) { + $body = decode_base64($entity->stringify_body); + } else { + $body = $entity->stringify_body; + } + + my $h_date = $head->get('Date') || ''; + chomp $h_date; + + # call referred sub and increase counter + &$callsub($voter_addr, $voter_name, $h_date, $entity, \$body); + $count++; + } + + return $count; +} + +1; diff --git a/UVrules.pm b/UVrules.pm new file mode 100644 index 0000000..76e260f --- /dev/null +++ b/UVrules.pm @@ -0,0 +1,409 @@ +# UVrules: Module with rule functions for usevote +# Used by uvvote.pl, UVconfig.pm + +package UVrules; + +use strict; +use vars qw (@ISA @EXPORT $VERSION @rules); +use UVconfig; +use UVmessage; + +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(@rules); + +# Module version +$VERSION = "0.3"; + +# --------------------------------------------------------------------- +# Erlaeuterung zur Regelpruefung (to be translated) +# --------------------------------------------------------------------- +# Um Stimmen mit multiplen Abstimmungspunkten auf ihre Sinnfaelligkeit +# pruefen zu koennen, koennen in Usevote verschiedenste Regeln +# fuer solche Pruefungen definiert werden. +# +# Die Regeln bestehen aus zwei Teilen. Einer IF-Klausel und einer THEN- +# Klausel. Die IF-Klausel bestimmt, ob die Stimme mit der THEN-Klausel +# verglichen werden soll. Passt sie auf diese, ist die Stimme in Ordnung, +# wenn nicht liegt ein Fehler vor. +# +# Ein kleines Beispiel: "IF S.. THEN .SS" +# Wenn beim ersten Punkt mit Ja oder Nein gestimmt wurde, dann muss +# bei den anderen beiden Punkten auch ein Ja oder Nein vorliegen. +# +# Die Stimmabgabe JNE wuerde also gegen die obige Regel verstossen, +# JJN nicht. EEJ wuerde ebenfalls gueltig sein, da die Regel nicht unter +# die IF-Klausel faellt und somit keine Ueberpruefung der THEN-Klausel +# erfolgt. +# +# +# --------------------------------------------------------------------- +# Implementierung +# --------------------------------------------------------------------- +# Um eine moeglichst einfache Ueberpruefung der Stimmen vorzunehmen, +# bietet es sich an, aus den beiden Klauseln regulaere Ausdruecke zu +# generieren. Diese wuerden dann auf die Stimme angewandt werden. +# +# Bei der Umwandlung in regulaere Audruecke kommt uns die Notation +# der Regeln bereits entgegen. So kann der Punkt als beliebige Stimme +# beibehalten werden. Die grossen Buchstaben bleiben ebenfalls bis +# auf S erhalten, da die zu pruefenden Stimmen aus den Buchstaben +# 'JNE' bestehen. +# +# So muessen wir zur Ueberpruefung von direkten Stimmen nur 'S' in +# eine Klasse mit [JN] und I in eine Klasse mit [EN] umwandeln. +# +# 'J..' => 'J..', 'NNE' => 'NNE', 'S..' => '[JN]..' +# +# Bei den indirekten Stimmabgaben wird es schon schwieriger. Hier +# muessten alle Moeglichkeiten eines Strings gebaut werden, um zu +# testen ob mindestens eine Version matcht. +# +# '.jjj' => '.(j..|.j.|..j) +# +# Je komplexer die Regeln, um so mehr Moeglichkeiten muessten +# konstruiert werden, um einen geschlossenen regulaeren Ausdruck +# zu erhalten. +# +# Wir koennen den Regex aber auch einfach aufbauen, in dem wir +# nicht alle Faelle betrachten die moeglich sind, sondern nur die +# Faelle die nicht erlaubt sind. +# +# D.h. soll an einer Stelle ein Ja stehen, erlauben wir dort +# nur Nein und Enthaltungen. Passt eine Stimme auf diesen Regex, +# kann sie unmoeglich die Vorgabe enthalten. +# +# 'nnnn' => '[JE][JE][JE][JE]' +# +# Besteht eine Stimme also nur aus Ja und Enthaltung, wissen wir +# das kein einziges Nein enthalten seien kann. Die Stimme passt +# also nicht auf unser Muster. +# +# Tritt hingegen nur ein einziges J auf, passt der regulaere Ausdruck +# nicht mehr, und wir wissen, dass die Stimme die Regel erfuellt. +# +# Wie wir sehen koennen, ist der negative Ausdruck leichter zu +# bilden als der positive. +# +# +# Da eine Stimme nun sowohl aus direkten, als auch indirekten +# Stimmen bestehen kann (z.B. 'Jnnn..') muessen wir die Stimme +# zerlegen. Wir bilden einen positiven Regex fuer die Grossbuch- +# staben und einen negativen Regex fuer die kleinen. +# +# Passt eine Stimme dann auf den positiven Regex und nicht auf +# den negativen Regex, so entspricht sie der urspruenglichen +# Regel. +# +# Ein Beispiel: 'Sss..' (Der erste Punkt und der zweite oder dritte +# Punkt muessen ein Ja oder Nein sein.) +# +# positiver Regex: '[JN]...' muss erfuellt werden +# negativer Regex: '.EE.' darf nicht erfuellt werden +# +# JJNN => positiv matcht => negativ matcht nicht => Regel erfuellt +# ENJE => positiv matcht nicht => Regel nicht erfuellt +# NEEJ => positiv matcht => negativ matcht => Regel nicht erfuellt +# +# +# Mit Hilfe dieser Technik, lassen sich einfach Regex bilden, die +# ebenso einfach ueberprueft werden koennen. + + +############################################################################## +# Read usevote.rul and check rules for correct syntax # +############################################################################## + +sub read_rulefile { + @rules = (); + + open (RULES, "<$config{rulefile}") + or die UVmessage::get("RULES_ERROPENFILE", (FILE => $config{rulefile})) . "\n\n"; + + while () { + chomp; + s/#.*$//; # delete comments + + # does line match correct if-then syntax? + if (/^\s*if\s+(\S+)\s+then\s+(\S+)\s*$/) { + my $if = $1; + my $then = $2; + + # $num contains the rule's array index + my $num = @rules; + + # check for correct length of condition + my $errortext; + if (length($if) < @groups) { + $errortext = UVmessage::get("RULES_TOOSHORT", (NUM=>$num+1, TYPE=>"if")); + + } elsif (length($if) > @groups) { + $errortext = UVmessage::get("RULES_TOOLONG", (NUM=>$num+1, TYPE=>"if")); + + } elsif (length($then) < @groups) { + $errortext = UVmessage::get("RULES_TOOSHORT", (NUM=>$num+1, TYPE=>"then")); + + } elsif (length($then) > @groups) { + $errortext = UVmessage::get("RULES_TOOLONG", (NUM=>$num+1, TYPE=>"then")); + } + die $errortext . ": $_\n\n" if ($errortext); + + # check for correct characters in conditions + if ($if !~ /^[JjNnEeSsHhIi\.]+$/) { + die UVmessage::get ("RULES_INVCHARS", (NUM=>$num+1, TYPE=>"if")) . ": $if\n\n"; + + } elsif ($then !~ /^[JjNnEeSsHhIi\.]+$/) { + die UVmessage::get ("RULES_INVCHARS", + (NUM=>$num+1, TYPE=>"if")) . ": $then\n\n"; + } + + # Zur Speicherung der Regeln (to be translated): + # - if_compl und then_compl sind die kompletten Bedingungen als Strings, + # werden fuer die Sprachausgabe der Regeln benoetigt + # - zusaetzlich werden der if- und then-Teil fuer die einfachere + # Verarbeitung in zwei Teile gesplittet: Eine Positiv-Regex, die auf + # die Grossbuchstaben (explizite Forderungen, UND-Verknuepfungen) + # matched, und eine Negativ-Regex, die bei den Kleinbuchstaben + # (optionale Felder, ODER-Verknuepfungen) verwendet wird. + + my %rule = ( if_compl => $if, + if_pos => make_regex_pos($if), + if_neg => make_regex_neg($if), + then_compl => $then, + then_pos => make_regex_pos($then), + then_neg => make_regex_neg($then) ); + + push (@rules, \%rule); + + } + } +} + + +############################################################################## +# Generates a RegEx for positive matching of the rules # +# # +# All lower case characters are replaced with dots, as they are to be # +# matched by the negativ RegEx. Furthermore the symbol S is replaced by [JN] # +# and I is replaced by [EN] (for use in combined votings when only one # +# option may be accepted and the others must be rejected or abstained. # +# As a result we have a regular expression that can be matched against the # +# received votes. # +############################################################################## + +sub make_regex_pos { + my $pat = $_[0]; + + $pat =~ s/[hijens]/./g; + $pat =~ s/S/[JN]/g; + $pat =~ s/H/[EJ]/g; + $pat =~ s/I/[EN]/g; + + return $pat; +} + + +############################################################################## +# Generates a RegEx for negative matching of the rules # +# # +# All upper case characters are replaced with dots, as they are to be # +# matched by the positiv RegEx. If lower case characters are found the # +# condition is reversed, so that we are able to match votes *not* # +# corresponding to this rule # +############################################################################## + +sub make_regex_neg { + my $pat = $_[0]; + + # upper case characters are replaced with dots + # (are covered by make_regex_pos) + $pat =~ s/[HIJENS]/./g; + + # reverse lower case characters + $pat =~ s/j/[NE]/g; + $pat =~ s/n/[JE]/g; + $pat =~ s/e/[JN]/g; + $pat =~ s/s/E/g; + $pat =~ s/h/N/g; + $pat =~ s/i/J/g; + + # If the string contained only upper case characters they are now all + # replaced with dots and the RegEx would match everything, i.e. declare + # every vote as invalid. In this case an empty pattern is returned. + $pat =~ s/^\.+$//; + + return $pat; +} + + +############################################################################## +# Check a voting for rule compliance # +# Parameters: Votes (Reference to Array) # +# Return value: Number of violated rule or 0 (everything OK) # +# (Internally rules are saved with indexes starting at 0) # +############################################################################## + +sub rule_check { + my ($voteref) = @_; + + # Turn array reference into a string + my $vote = join ('', @$voteref); + + # For compliance with the rules every rule has to be matched against the + # the vote. If the IF clause matches but not the THEN clause the vote is + # invalid and the rule number is returned. + + for (my $n = 0; $n < @rules; $n++) { + return $n+1 if ($vote =~ m/^$rules[$n]->{if_pos}$/ && + $vote !~ m/^$rules[$n]->{if_neg}$/ && + not($vote =~ m/^$rules[$n]->{then_pos}$/ && + $vote !~ m/^$rules[$n]->{then_neg}$/ )); + } + + return 0; +} + + +############################################################################## +# Print rules in human readable format # +# Parameter: rule number # +# Return value: rule text # +############################################################################## + +sub rule_print { + my ($n) = @_; + + my $and = UVmessage::get ("RULES_AND"); + my $or = UVmessage::get ("RULES_OR"); + my $yes = UVmessage::get ("RULES_YES"); + my $no = UVmessage::get ("RULES_NO"); + my $abst = UVmessage::get ("RULES_ABSTAIN"); + + $n++; + my $text = UVmessage::get ("RULES_RULE") . " #$n:\n"; + $text .= " " . UVmessage::get ("RULES_IF") . "\n"; + + my @rule = split (//, $rules[$n-1]->{if_compl}); + my $firstrun = 1; + my $fill = ""; + + for (my $i=0; $i<@rule; $i++) { + my $text1 = ""; + + if ($rule[$i] eq 'J') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", (VOTE=>$yes, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'N') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", (VOTE=>$no, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'E') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", (VOTE=>$abst, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'S') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", + (VOTE=>"$yes $or $no", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'H') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", + (VOTE=>"$abst $or $yes", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'I') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", + (VOTE=>"$abst $or $no", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'j') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", (VOTE=>$yes, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'n') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", (VOTE=>$no, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'e') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", (VOTE=>$abst, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 's') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", + (VOTE=>"$yes $or $no", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'h') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", + (VOTE=>"$abst $or $yes", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'i') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_IFCLAUSE", + (VOTE=>"$abst $or $no", GROUP=>$groups[$i])); + } + + if ($text1) { + if ($firstrun) { + $text .= " " . $text1 . "\n"; + $firstrun = 0; + } else { + $text .= $fill . $text1 . "\n"; + } + } + } + + @rule = split (//, $rules[$n-1]->{then_compl}); + $text .= " ..." . UVmessage::get ("RULES_THEN") . "\n"; + $firstrun = 1; + + for (my $i=0; $i<@rule; $i++) { + my $text1 = ""; + if ($rule[$i] eq 'J') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", (VOTE=>$yes, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'N') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", (VOTE=>$no, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'E') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", (VOTE=>$abst, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'S') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", + (VOTE=>"$yes $or $no", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'H') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", + (VOTE=>"$abst $or $yes", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'I') { + $fill = " $and "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", + (VOTE=>"$abst $or $no", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'j') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", (VOTE=>$yes, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'n') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", (VOTE=>$no, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'e') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", (VOTE=>$abst, GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 's') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", + (VOTE=>"$yes $or $no", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'h') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", + (VOTE=>"$abst $or $yes", GROUP=>$groups[$i])); + } elsif ($rule[$i] eq 'i') { + $fill = " $or "; + $text1 = UVmessage::get ("RULES_THENCLAUSE", + (VOTE=>"$abst $or $no", GROUP=>$groups[$i])); + } + + if ($text1) { + if ($firstrun) { + $text .= " " . $text1 . "\n"; + $firstrun = 0; + } else { + $text .= $fill . $text1 . "\n"; + } + } + } + return $text . "\n"; +} + +1; diff --git a/UVsendmail.pm b/UVsendmail.pm new file mode 100644 index 0000000..a773733 --- /dev/null +++ b/UVsendmail.pm @@ -0,0 +1,336 @@ +# UVsendmail: functions for sending mails +# Used by uvvote.pl, uvcfv.pl + +package UVsendmail; + +use strict; +use UVconfig; +use UVtemplate; +use MIME::Words; +use Text::Wrap qw(wrap $columns); + +# Set columns for Text::Wrap +$columns = $config{rightmargin}; + +use vars qw($VERSION); + +# Module version +$VERSION = "0.9"; + +my $num = 0; + +############################################################################## +# generation of acknowledge and error mails (don't sends them out yet) # +# each mail is saved in a different file and a control file containing # +# filename and envelope-to address is generated. # +# Parameters: mail address, fixed part of subject and body of mail (strings) # +############################################################################## + +sub mail { + my ($addr, $subject, $text, $reference, $replyto) = @_; + + # address set? + if ($addr) { + # generate mail to sender + + my $template = UVtemplate->new(); + $template->setKey('from' => mimeencode($config{mailfrom})); + $template->setKey('subject' => mimeencode("$config{votename} - $subject")); + $template->setKey('address' => $addr); + $template->setKey('reference' => $reference) if ($reference); + $template->setKey('reply-to' => $replyto) if ($replyto); + $template->setKey('usevote-version' => $usevote_version); + + my $message = $template->processTemplate($config{'tpl_mailheader'}); + $message .= "\n" . $text; + + # get envelope-to addresses + my $envaddr = $addr; + $envaddr .= " $config{mailcc}" if ($config{mailcc}); + + my $mailfile = ''; + + # search for file names + do { + $num++; + $mailfile = "$config{tmpdir}/ack.$num"; + } while (-e $mailfile); + + # write mail in a file and append a line at the control file + + open (CONTROL, ">>$config{controlfile}") or print STDERR "\n\n", + UVmessage::get("SENDMAIL_ERROPENCONTROL", (FILE => $config{controlfile})), "\n"; + print CONTROL "$mailfile\t$envaddr\n"; + close (CONTROL) or print STDERR "\n\n", + UVmessage::get("SENDMAIL_ERRCLOSECONTROL", (FILE => $config{controlfile})), "\n"; + + open (MAIL, ">$mailfile") or print STDERR "\n\n", + UVmessage::get("SENDMAIL_ERROPENMAIL", (FILE => $config{controlfile})), "\n"; + print MAIL $message; + close (MAIL) or print STDERR "\n\n", + UVmessage::get("SENDMAIL_ERRCLOSEMAIL", (FILE => $config{controlfile})), "\n"; + + } +} + + +############################################################################## +# Send previously generated acknowledge or error mails. # +# Depending on configuration mails are piped to your MTA or send via SMTP. # +############################################################################## + +sub send { + unless (-e $config{controlfile}) { + print "\n", UVmessage::get("SENDMAIL_NOMAILS", (FILE => $config{controlfile})), + "\n\n"; + return 0; + } + + open (CONTROL, "<$config{controlfile}") or die "\n\n", + UVmessage::get("SENDMAIL_ERROPENCONTROL", (FILE => $config{controlfile})), "\n"; + my @mailinfo = ; + close (CONTROL); + + print UVmessage::get("SENDMAIL_SENDING"), "\n"; + + if ($config{smtp}) { + # send mails via SMTP + use Net::SMTP; + my $smtp = Net::SMTP->new("$config{smtpserver}:$config{smtpport}", + Hello => $config{smtphelo}); + die UVmessage::get("SENDMAIL_SMTP_CONNREFUSED") . "\n\n" unless ($smtp); + if ($config{smtpauth}) { + $smtp->auth($config{smtpuser}, $config{smtppass}) + or die UVmessage::get("SENDMAIL_SMTP_CONNREFUSED") . "\n" . + $smtp->code() . ' ' . $smtp->message() . "\n"; + } + + my $errors = 0; + my $missingfiles = 0; + + open (CONTROL, ">$config{controlfile}") or die "\n\n", + UVmessage::get("SENDMAIL_ERROPENCONTROL", (FILE => $config{controlfile})), "\n"; + + foreach my $mail (@mailinfo) { + + chomp ($mail); + next unless $mail; + + my ($file, $envelope) = split(/\t/, $mail); + my $notfound = 0; + open (MAIL, "<$file") or $notfound = 1; + if ($notfound) { + print STDERR UVmessage::get("SENDMAIL_ERRNOTFOUND") . "\n"; + $missingfiles++; + next; + } + my $message = join('', ); + close (MAIL); + + next unless $message; + + $smtp->reset(); + $smtp->mail($config{envelopefrom}); + unless ($smtp->ok()) { + print STDERR UVmessage::get("SENDMAIL_SMTP_INVRCPT", (RCPT => $envelope)), + "\n", $smtp->code(), ' ', $smtp->message(), "\n"; + $errors++; + next; + } + + my $onesent = 0; + my $onefail = 0; + foreach my $addr (split(/ +/, $envelope)) { + $smtp->to($addr); + if ($smtp->ok()) { + $onesent = 1; + } else { + print CONTROL ($onefail ? " " : "$file\t"); + print CONTROL $addr; + print STDERR UVmessage::get("SENDMAIL_SMTP_INVRCPT", (RCPT => $envelope)), + "\n", $smtp->code(), ' ', $smtp->message(), "\n"; + $errors++; + $onefail = 1; + next; + } + } + + print CONTROL "\n" if ($onefail); + next unless $onesent; + + $smtp->data(); + if ($smtp->ok()) { + $smtp->datasend($message); + $smtp->dataend(); + } + unless ($smtp->ok()) { + print STDERR UVmessage::get("SENDMAIL_SMTP_INVRCPT", (RCPT => $envelope)), + "\n", $smtp->code(), ' ', $smtp->message(), "\n"; + $errors++; + next; + } + unlink ($file) unless ($onefail); + } + + $smtp->quit(); + close (CONTROL) or die "\n\n", + UVmessage::get("SENDMAIL_ERRCLOSECONTROL", (FILE => $config{controlfile})), "\n"; + + if ($errors) { + print STDERR "\n".wrap('', '', "$errors ".UVmessage::get("SENDMAIL_ERROCCURED"))."\n\n"; + } + + if ($missingfiles) { + print STDERR wrap('', '', "$missingfiles " . + UVmessage::get("SENDMAIL_MISSINGFILES")), "\n\n"; + } + + } else { + + foreach my $mail (@mailinfo) { + next unless $mail; + chomp($mail); + my ($file, @rcpt) = split(/\s+/, $mail); + open (DOMAIL, ">>$config{domailfile}"); + print DOMAIL "$config{mailcmd} "; + foreach my $rcpt (@rcpt) { + print DOMAIL "'$rcpt' "; + } + print DOMAIL "<$file && rm $file ; $config{sleepcmd}\n"; + close (DOMAIL) + or print STDERR "\n\n", UVmessage::get("SENDMAIL_ERRCLOSEDOMAIL"), "\n"; + } + chmod(0700, $config{domailfile}); + system($config{domailfile}); + + } + + opendir (DIR, $config{tmpdir}); + my @files = grep (/^ack\.\d+/, readdir (DIR)); + closedir (DIR); + return 0 if (@files); + + unlink $config{controlfile} or print STDERR "\n\n", + UVmessage::get("SENDMAIL_ERRDELCONTROL", (FILE => $config{controlfile})), "\n"; + + unless ($config{smtp}) { + unlink $config{domailfile} or print STDERR "\n\n", + UVmessage::get("SENDMAIL_ERRDELCONTROL", (FILE => $config{domailfile})), "\n"; + } + +} + + +############################################################################## +# Encodes a string for use in mail headers # +# # +# Parameters: $text = string to encode. # +# Returns: $newtext = encoded string. # +############################################################################## + +sub mimeencode { + my ($text) = @_; + my @words = split(/ /, $text); + my $line = ''; + my @lines; + + foreach my $word (@words) { + my $sameword = 0; + $word =~ s/\n//g; + my $encword; + if ($word =~ /[\x7F-\xFF]/) { + $encword = MIME::Words::encode_mimeword($word, 'Q', 'ISO-8859-1'); + } elsif (length($word) > 75) { + $encword = MIME::Words::encode_mimeword($word, 'Q', 'us-ascii'); + } else { + $encword = $word; + } + + # no more than 75 chars per line allowed + if (length($encword) > 75) { + while ($encword) { + if ($encword =~ /(^=\?[-\w]+\?\w\?)(.{55}.*?)((=.{2}|[^=]{3}).*\?=)$/) { + addword($1 . $2 . '?=', \$line, \@lines, $sameword); + $encword = $1 . $3; + } else { + addword($encword, \$line, \@lines, $sameword); + $encword = ''; + } + $sameword = 1; + } + } else { + addword($encword, \$line, \@lines, $sameword); + } + } + + my $delim = (@lines) ? ' ' : ''; + push(@lines, $delim . $line) if ($line); + return join('', @lines); +} + + +############################################################################## +# Adds a word to a MIME encoded string, inserts linefeed if necessary # +# # +# Parameters: # +# $word = word to add # +# $line = current line # +# $lines = complete text (without current line) # +# $sameword = boolean switch, indicates that this is another part of # +# the last word (for encoded words > 75 chars) # +############################################################################## + +sub addword { + my ($word, $line, $lines, $sameword) = @_; + + # If the passed fragment is a new word (and not another part of the + # previous): Check if it is MIME encoded + if (!$sameword && $word =~ /^(=\?[^\?]+?\?[QqBb]\?)(.+\?=[^\?]*)$/) { + + # Word is encoded, save without the MIME header + # (e.g. "t=E4st?=" instead of "?iso-8859-1?q?t=E4st?=") + my $charset = $1; + my $newword = $2; + + if ($$line =~ /^(=\?[^\?]+\?[QqBb]\?)(.+)\?=$/) { + # Previous word was encoded, too: + # Delete the trailing "?=" and insert an underline character (=space) + # (space between to encoded words is ignored) + if ($1 eq $charset) { + if (length($1.$2)+length($newword)>75) { + my $delim = (@$lines) ? ' ' : ''; + push(@$lines, "$delim$1$2_?=\n"); + $$line = $word; + } else { + $$line = $1 . $2 . '_' . $newword; + } + } else { + if (length("$$line $word")>75) { + my $delim = (@$lines) ? ' ' : ''; + push(@$lines, "$delim$1$2_?=\n"); + $$line = $word; + } else { + $$line = "$1$2_?= $word"; + } + } + return 0; + } + } + + # New word is not encoded: simply append it, but check for line length + # and add a newline if necessary + if (length($$line) > 0) { + if (length($$line) + length($word) >= 75) { + my $delim = (@$lines) ? ' ' : ''; + push(@$lines, "$delim$$line\n"); + $$line = $word; + } else { + $$line .= " $word"; + } + } else { + # line is empty + $$line = $word; + } +} + +1; diff --git a/UVtemplate.pm b/UVtemplate.pm new file mode 100644 index 0000000..35f28db --- /dev/null +++ b/UVtemplate.pm @@ -0,0 +1,810 @@ +#---------------------------------------------------------------------- +package UVtemplate; +#---------------------------------------------------------------------- + +=head1 NAME + +UVtemplate - Templateverarbeitung und String-Formatierungen + +=head1 SYNOPSIS + + use UVtemplate; + + $plate = UVtemplate->new([%keys]); + + $plate->setKey(%keys); + $item = $plate->addListItem($name, %keys); + + $string = $plate->processTemplate($file); + +=head1 DESCRIPTION + +Mit Hilfe von UVtemplate, wird die komplette Aufbereitung und +Formatierung der Programmausgaben nicht nur ausgelagert sondern +auch so flexibiliert, dass sie jederzeit angepasst werden kann, +ohne das im eigentlichen Programmcode veraendert werden muss. + +Auf Programmseite wird eine Datenstruktur mit Schluessel-Wert +Paaren erzeugt. In den Template-Dateien werden dann spaeter die +jeweiligen Schluessel, durch ihre im Programm festgelegten +Werte ersetzt. Zusaetzlich ist es moeglich Schluessel zu Listen +zusammenzufassen. + +Da es sich bei den Templates um Ascii-Texte handelt, gibt es +zusaetzlich die Moeglichkeit die Werte der Schluessel zuformatieren +um eine einheitliche Ausgabe zu ermoeglichen. D.h. es kann z.B. durch +das Anhaengen von Leerzeichen dafuer gesorgt werden, das ein Schluessel +einer Liste immer 60 Zeichen lang ist um ansehnliche Tabellen auszugeben. + +=head1 FUNCTIONS + +=over 3 + +=cut + +#---------------------------------------------------------------------- + +use strict; +use vars qw( $VERSION $functions @dirs); +use UVconfig; + +$VERSION = 0.1; + +#---------------------------------------------------------------------- + +=item new + +Eine neues Objekt vom Typ UVtemplate anlegen. + + my $plate = UVtemplate->new(); + +Als Parameter koennen gleich beliebig viele Schluessel-Wert-Paare +uebergeben werden. + +=cut + +sub new{ + my $class = shift; + my $self = {}; + + if (ref($class)){ + $self->{FATHER} = $class; + bless($self, ref($class)); + + }else{ + bless($self, $class); + } + + $self->setKey(@_); + + return $self; +} + +=item setKey + +Schluessel und zugehoerige Werte im Objekt speichern. + + $plate->setKey( vote-addr => 'to-vote@dom.ain' ); + $plate->setKey( datenschutz => 1); + +Ist der zu speichernde Schluessel bereits vorhanden, wird er +durch den neuen Wert ueberschrieben. + +=cut + +sub setKey{ + my $self = shift; + my %param = @_; + + foreach my $key (keys(%param)){ + $self->{KEYS}->{$key} = $param{$key}; + } +} + +=item addListItem + +Erzeugt ein neues Objekt vom Typ UVtemplate und fuegt es der +angebenen Liste hinzu. + + $plate->addListItem(name => 'Musterman', email => 'em@il'); + +Da sich Listen wie normale Schluessel-Wert Paare verhalten, +wird die Liste als Array ueber UVtemplate-Objekte unter dem +definiertem Schluessel abgelegt. Ist dieser Schluessel bereits +gesetzt und enthaehlt keinen Array, so bricht die Funktion ab. + +=cut + +sub addListItem{ + my $self = shift; + my $list = shift; + + # pruefen ob key angegeben ist und falls key vorhanden + # eine liste vorliegt + return unless ($list && (not($self->{KEYS}->{$list}) || + UNIVERSAL::isa($self->{KEYS}->{$list}, 'ARRAY'))); + + # neues Element erzeugen + my $new = $self->new( @_ ); + + # an listen anhaengen + push(@{$self->{KEYS}->{$list}}, $new); + + # referenz zurueckgeben + return $new; +} + +=item getKey($key) + +Den Wert eines Schluessel ermitteln. + + my $value = $plate->getKey('email'); + +Ist der Wert im Objekt nicht gesetzt, wird - falls es sich um ein +Element einer Liste handelt - rekursiv beim Vater weiter gesucht. + +So stehen allen Kindern auch die Schluessel-Wert Paare ihrer Eltern +zur Verfuegung. + +Zum Schluss wird noch geprueft, ob der Schluessel in usevote.cfg +gesetzt wurde. Dadurch sind alle Konfigurationsoptionen direkt +in Templates nutzbar. + +=cut + +sub getKey{ + my $self = shift; + my $key = $_[0]; + + my $value; + + do{ + $value = $self->{KEYS}->{$key}; + $self = $self->{FATHER}; + + }while(!defined($value) && $self); + + if (!defined($value) && defined($config{$key})) { + $value = $config{$key}; + } + + return $value; +} + +#---------------------------------------------------------------------- + +sub getRules{ + my $self = shift; + + do{ + return $self->{RULES} if ($self->{RULES}); + $self = $self->{FATHER}; + }while($self); + + return; +} + +=item getConvKey{ + +Einen Format ermitteln. + + my $value = $plate->getConvKey('email-adresse'); + +Diese Funktion ueberprueft ob eine Formatierung mit den entsprechenden +Schluessel definiert ist und ruft dementsprechend die dort definierten +Funktionen ueber der Datenstruktur auf. + +Ist kein solches Format definiert, wird der Wert des Schluessel mit +einem solchen Namen zurueckgegeben. (Es wird intern getKey aufgerufen). + +=cut + +sub getConvKey{ + my $self = shift; + my $key = $_[0] || return; + + my $rules = $self->getRules(); + my $value = $self->getKey($key); + + $value = '' unless (defined($value)); + + if ($rules && ($rules->{$key})){ + my @funcs = @{$rules->{$key}}; + + foreach my $func (@funcs){ + my ($name, @params) = @$func; + + if ($functions->{$name}){ + $value = $functions->{$name}->($self, $value, @params); + + }else{ + print STDERR "format function '$name' not found!\n"; + } + } + } + + return $value; +} + +#---------------------------------------------------------------------- + +=item processTemplate + +Daten des Objekts in ein Template einfuegen. + + my $string = $plate->processTemplate('template/info.txt'); + +Die angebene Datei wird eingelesen, zerlegt und danach +die enstprechenden Platzhalter durch die (formatierten) +Werte aus der Datenstruktur ersetzt. + +=cut + +sub processTemplate{ + my $self = shift; + my $file = $_[0] || return; + + my ($rules, $body) = _split_file($file); + + # konvertierungsregeln parsen + $self->{RULES} = _parse_rules($rules); + + # template zerlegen (zuerst fuehrende leerzeilen entfernen!) + $body =~ s/^\n+//s; + my $token = UVtemplate::scan->new(string => $body); + + # daten einsetzen + return $token->processData($self); +} + +sub _split_file{ + my $file = $_[0] || return; + + my $fname = _complete_filename($file); + + unless ($fname){ + print STDERR "couldnt find '$file'\n"; + return; + } + + my (@rules, @body); + + open(PLATE, $fname); + my @lines = ; + close(PLATE); + + my $body = 0; + + foreach my $line (@lines){ + if ($line =~ m/^== TEMPLATE/){ + $body = 1; + + }else{ + if ($body){ + push(@body, $line); + + }else{ + push(@rules, $line); + } + } + } + + # falls kein Separator definiert war, wird der komplette Text + # als Body interpretiert. Es gibt keine Regeln! + + unless ($body){ + @body = @rules; + @rules = (); + } + + # und nun wieder zu Strings zusammenpappen + return (join('', @rules), join('', @body)); +} + +sub _complete_filename{ + my $file = $_[0] || return; + + my $dirs = $UVconfig::config{templatedir}; + @dirs = split(/\s*,\s*/, $dirs) if $dirs; + + my $fname; + + foreach my $dir (@dirs, '.'){ + $fname = "$dir/$file"; + return $fname if (-r $fname); + } +} + +#---------------------------------------------------------------------- +# Konvertierungs-Regeln +#---------------------------------------------------------------------- + +sub _parse_rules{ + my $string = $_[0] || return; + + my @stack; + my $rules = {}; + + my $this = []; + + while (length($string) > 0){ + _strip_chars(\$string); + + my $token = _parse_token(\$string); + + if ($token){ + push(@stack, $token); + + _strip_chars(\$string); + + if ($string =~ s/^:=//){ + # neuen Schluessel vom Stack holen + my $key = pop(@stack); + + # restlichen Stack auf alten Schluessel packen + push(@$this, [ @stack ]); + @stack = (); + + # neuen Schluessel anlegen + $rules->{$key} = $this = []; + + }elsif($string =~ s/^\|//){ + # stack auf schluessel packen + push(@$this, [ @stack ]); + @stack = (); + } + + }else{ + # fehlermeldung ausgeben (nacharbeiten!) + print STDERR "Syntaxerror in Definition\n"; + return; + } + } + + # den Rest vom Stack abarbeiten + push(@$this, [ @stack ]) if @stack; + + return $rules; +} + +sub _strip_chars{ + my $line = $_[0] || return; + + # führenden whitespace entfernen + $$line =~ s/^\s+//; + + # kommentare bis zum nächsten Zeilenumbruch entfernen + $$line =~ s/^#.*$//m; +} + + +sub _parse_token{ + my $string = shift; + + if ($$string =~ s/^(["'])//){ + return _parse_string($string, $1); + + }else{ + return _parse_ident($string); + } +} + + +sub _parse_string{ + my ($string, $limit) = @_; + + my $value; + + while ($$string){ + if ($$string =~ s/^$limit//){ + $$string =~ s/^\s*//; + return $value; + + }elsif($$string =~ s/^\\(.)//){ + $value .= $1; + + }else{ + $$string =~ s/^[^$limit\\]*//; + $value .= $&; + } + } + + # end of line + return $value; +} + + +sub _parse_ident{ + my $string = shift; + + if ($$string =~ s/^([A-Za-z0-9-]+)\s*//){ + return $1; + } + + return; +} + +#---------------------------------------------------------------------- + +BEGIN{ + $functions = \%UVconfig::functions; +} + +#---------------------------------------------------------------------- +#---------------------------------------------------------------------- +package UVtemplate::scan; +#---------------------------------------------------------------------- +#---------------------------------------------------------------------- + +sub new{ + my $class = shift; + my %param = @_; + + my $self = {}; + bless($self, $class); + + $self->parseFile($param{file}) if defined($param{file}); + $self->parseString($param{string}) if defined($param{string}); + + return $self; +} + +#---------------------------------------------------------------------- + +sub processData{ + my $self = shift; + my $data = $_[0]; + + return _process_data($self->{toks}, $data); +} + +sub _process_data{ + my ($toref, $data) = @_; + + my $string = ''; + my $length = 0; + my $empty = 0; + + foreach my $token (@$toref){ + if (ref($token)){ + my $before = length($string); + $empty = 0; + + if ($token->[0] eq 'VAR'){ + my $value = $data->getConvKey(_process_data($token->[1], $data)); + + if (defined($value) && length($value)){ + $string .= $value; + + }else{ + $string .= _process_data($token->[2], $data); + } + + }elsif($token->[0] eq 'IF'){ + if ($data->getConvKey(_process_data($token->[1], $data))){ + $string .= _process_data($token->[2], $data); + + }else{ + $string .= _process_data($token->[3], $data); + } + + }elsif($token->[0] eq 'LOOP'){ + my $nodes = $data->getConvKey(_process_data($token->[1], $data)); + my @block; + + if ($nodes && (UNIVERSAL::isa($nodes, 'ARRAY'))){ + foreach my $node (@$nodes){ + push(@block, _process_data($token->[2], $node)); + } + + $string .= join(_process_data($token->[3], $data), @block); + } + } + + $length = length($string); + $empty = 1 if ($before == $length); + + }else{ + if ($empty && ($string =~ m/(\n|^)$/s)){ + $empty = 0; # Falls die letzte Zeile nur aus einem Token + $token =~ s/^\n//s; # ohne Inhalt bestand, wird die Zeile entfernt + } + + $string .= $token; + } + } + + return $string; +} + +#---------------------------------------------------------------------- +# Den String in einen Syntaxbaum abbilden + +sub _parse_token_string{ + my $self = shift; + my ($string, $intern) = @_; + + my (@token, $toref); + my $data = ''; + + while ($string){ + if ($intern && $string =~ m/^(\]|\|)/){ + last; + + }elsif ($string =~ s/^\[//){ + my $orig = $string; + + ($toref, $string) = $self->_parse_token($string); + + if (@$toref){ + push (@token, $data) if $data; + $data = ''; + + push(@token, $toref) + } + + if ($string !~ s/^\]//){ + my $pos = $self->{lines} - _count_lines($orig) + 1; + + print STDERR "Scanner: [$pos] missing right bracket\n"; + return (\@token, $string); + } + + }elsif($string =~ s/^\\n//s){ + $data .= "\n"; + + }elsif($string =~ s/^\\(.)//s){ + $data .= $1; + + }elsif($intern){ + $string =~ s/^([^\]\[\|\\]+)//s; + $data .= $1; + + }else{ + $string =~ s/^([^\[\\]+)//s; + $data .= $1; + } + } + + push (@token, $data) if length($data); + return (\@token, $string) +} + + +sub _parse_token{ + my $self = shift; + my $string = $_[0]; + + my @token = (); + + if ($string =~ s/^\$//s){ + # Variablen - Syntax: [$key[|]] + push (@token, 'VAR'); + + }elsif ($string =~ s/^\?//s){ + # Bedingung - Syntax: [?if|[|]] + push (@token, 'IF'); + + }elsif ($string =~ s/^\@//s){ + # Schleifen - Syntax: [@key|[|]] + push (@token, 'LOOP'); + + }elsif ($string =~ s/^#//s){ + # Kommentare - Syntax: [# ... ] + $string = _parse_comment($string); + + return (\@token, $string); + + }else{ + print STDERR "unknown token in template\n"; + } + + my $toref; + + ($toref, $string) = $self->_parse_token_string($string, 1); + push(@token, $toref); + + while ($string =~ s/^\|//){ + ($toref, $string) = $self->_parse_token_string($string, 1); + push(@token, $toref); + } + + return (\@token, $string); +} + + +sub _parse_comment{ + my $string = $_[0]; + my $count = 1; + + while($string && $count) { + $string =~ s/^[^\[\]\\]+//s; # alles außer Klammern und Backslash wegwerfen + $string =~ s/^\\.//; # alles gesperrte löschen + + $count++ if $string =~ s/^\[//; + $count-- if $string =~ s/^\]//; + } + + $string = ']'.$string if !$count; + return $string; +} + +#---------------------------------------------------------------------- + +sub parseString{ + my $self = shift; + my $text = $_[0]; + + $self->{lines} = _count_lines($text); + my ($toref, $rest) = $self->_parse_token_string($text); + + $self->{toks} = $toref; +} + + +sub _count_lines{ + return 0 unless defined($_[0]); + + my ($string, $count) = ($_[0], 1); + $count++ while($string =~ m/\n/sg); + + return $count; +} + +#---------------------------------------------------------------------- +#---------------------------------------------------------------------- +#---------------------------------------------------------------------- + +1; + +=back + +=head1 SYNTAX + +Eine Templatedatei besteht aus zwei Teilen. Am Anfang werden die +Formatierungen bestimmter Schluessel definiert und nach einem +Trenner folgt der eigentlich Template-Koerper, der dann von Programm +bearbeitet und ausgegeben wird. + + format-key := function1 param | function2 param + + == TEMPLATE ==================================== + + Ich bin nun das eigentliche Template: + + format-key: [$format-key] + +Der Trenner beginnt mit den Zeichen '== TEMPLATE' danach koennen +beliebige Zeichen folgen um die beiden Sektionen optisch voneinander +abzugrenzen. + +Wenn es keine Formatierungsanweisungen gibt, kann der Trenner auch +weggelassen werden. D.h. wenn kein Trenner gefunden wird, wird der +komplette Text als Template-Koerper betrachtet. + +=head2 Template-Koerper + +Im Template-Koerper werden die zu ersetzenden Token durch eckige +Klammern abgegrenzt. Sollen eckige Klammern im Text ausgegeben werden +muessen diese durch einen Backslash freigestellt werden. + + [$termersetzung] [@schleife] nur eine \[ Klammer + +=over 3 + +=item $ - Termersetzung + +Ersetzt den Token durch den Wert des angegeben Schluessels. + + [$formatierung] [$schluessel] + +Es wird zuerst nach einer Formatierung mit den entsprechenden +Bezeichner gesucht. Ist dies der Fall werden die entsprechenden +Funktionen ausgefuehrt. + +Kann kein Format gefunden, wird direkt in der Datenstruktur +nach einem Schhluessel mit dem angegeben Bezeichner gesucht +und sein Wert eingesetzt. + +Schlussendlich ist es noch moeglich einen default-Wert zu +definieren, der eingesetzt wird, wenn keiner der obigen Wege +erfolgreich war. + + Hallo [$name|Unbekannter]! + +=item ? - bedingte Verzeigung + +Ueberprueft ob der Wert des angegebenen Formats/Schluessel +boolsch WAHR ist. Dementsprechend wird der then oder else +Block eingefuegt. + + [?if|then|else] oder auch nur [?if|then] + +Die then/else Bloecke werden natuerlich auch auf Tokens +geparst und diese dementsprechend ersetzt. + +=item @ - Schleifen/Listen + +Der nachfolgende Textblock wird fuer alle Elemente des durch +den Schluessel bezeichneten Arrays ausgefuehrt und eingefuegt. + + [@schluessel|block] oder [@schluessel|block|sep] + +Als zweiter Parameter kann ein Separtor definiert werden, mit +dem sich z.B. kommaseparierte Listen erzeugen lassen, da der +Separator eben nur zwischen den Element eingefuegt wird. + +Auch fuer Schleifen koennen Formatierungen genutzt werden. +Allerdings darf kein String zurueckgegeben werden, sondern +ein Array mit einer Menge von UVtemplate-Objekten. + +=item # - Kommentare + +Token die nach der Bearbeitungen entfernt werden. + + [# mich sieht man nicht] + +=item Sonstiges + +Um in Listen einen Zeilenumbruch zu erzwingen, muss +lediglich ein '\n' eingefuegt werden, falls eine kompakte +Definition der Liste erfolgen soll. + + [@names|[name] [email]\n] + +=back + +=head2 Formatierungen + +Eine Formatierung besteht eigentlich nur aus dem entsprechenden +Namen und einer beliebigen Anzahl von Funktionsaufrufen: + + format := funktion param1 "param 2" | funktion param + +Aehnlich der Unix-Shell-Funktionalitaet, wird dabei die Ausgabe +einer Funktion an die folgende weitergeleitet. So ist es moeglich +verschiedenste simple Formatierungen zu kombinieren um nicht fuer +jeden Spezialfall eine neue Funktion schreiben zu muessen. + +Die jeweilige Formatierungsfunktion erhaelt als Input die Datenstruktur, +den Output der vorherigen Funktion und die definierten Parameter in der +entsprechenden Reihenfolge. + +Zahlen und einfache Bezeichner koennen direkt definiert werden. Sollen +Sonderzeichen oder Leerzeichen uebergeben werden muessen diese gequotet +werden. Dazu kann ' also auch " verwendet werden. + +Die Funktionen geben im Allgemeinen einen String zurueck. Im Rahmen +von Listen können auch Arrays uebergeben werden. + +Die erste Funktion duerfte ueblicherweise 'value' sein. Sie gibt den +des angegeben Schluessel zurueck, der dann von den folgenden Funktionen +definiert wird. + + name-60 := value name | fill-right 60 + +Das Format "name-60" definiert also den Wert des Schluessel "name" der +um Leerzeichen aufgefuellt wird, bis eine Laenge von 60 Zeichen +erreicht wird. + + name-email := value name | justify-behind mail 72 + +"name-email" resultiert in einem String, der zwischen den Werten +von "name" und "email" genau so viele Leerzeichen enthaelt, damit +der gesamte String 72 Zeichen lang ist. + +Wird dieses Format in einer Liste angewandt, erhaelt man eine Tabelle +in der die linke Spalte linksbuendig und die rechte Spalte entsprechend +rechtsbuendig ist. + +Soweit ein kleiner Ueberblick ueber die Formatierungen. +Ausfuehrliche Funktionsbeschreibungen und weitere Beispiele finden +sich in der Dokumentation des Moduls UVformat. + +=head1 SEE ALSO + +L + +=head1 AUTHOR + +Cornell Binder diff --git a/bdsgtext.cfg b/bdsgtext.cfg new file mode 100644 index 0000000..28bc3b5 --- /dev/null +++ b/bdsgtext.cfg @@ -0,0 +1,13 @@ +# Diese Datei enthaelt den Hinweistext auf die Datenschutzklausel, +# der im Wahlschein erscheint und auch bei eingegangenen Wahlscheinen +# wieder abgeprueft wird. Alle Zeilen, die mit dem Kommentarzeichen # +# anfangen, werden ignoriert. +# +Zur Verarbeitung des Wahlscheines und inbesondere der Veroeffentlichung +des Ergebnisses ist deine Zustimmung zur Speicherung, Auswertung und +Veroeffentlichung deiner Stimmdaten (Name und E-Mail-Adresse in +Verbindung mit dem Stimmverhalten) im Rahmen dieses Verfahrens +erforderlich. Wenn du im Feld unterhalb dieses Absatzes "JA" +eintraegst, erklaerst du dich damit einverstanden. In allen anderen +Faellen wird der Wahlschein mit Ruecksicht auf das deutsche +Bundesdatenschutzgesetz verworfen und nicht gewertet. diff --git a/mailpatterns.cfg b/mailpatterns.cfg new file mode 100644 index 0000000..0fbbd31 --- /dev/null +++ b/mailpatterns.cfg @@ -0,0 +1,42 @@ +# Mailadressen, die auf diese Kriterien zutreffen, werden als verdaechtig +# angesehen und zurueckgewiesen. Im interaktiven Modus kann der Wahlleiter +# im Einzelfall entscheiden. +# +# Bitte ein Pattern (Regular Expression) pro Zeile eingeben. +# Achtung, es wird so benutzt, wie es hier steht, also auf fuehrende oder +# abschliessende Leerzeichen aufpassen! +# +# Alle Zeilen, die mit dem Kommentarzeichen # anfangen, werden ignoriert. + +@anon\.penet\.fi +@anon\.sub\.net +anonymous@mixmaster\.nullify\.org +me@privacy.net +admin@ +daemon@ +anon@ +audit@ +bin@ +decnet@ +default@ +field@ +guest@ +hostmaster@ +install@ +mailer.*@ +maint.*@ +news@ +newsmaster@ +nobody@ +operator@ +postmaster@ +root@ +shutdown@ +sync@ +sys@ +sysop@ +system@ +test@ +tutor@ +usenet@ +uucp@ diff --git a/messages.cfg b/messages.cfg new file mode 100644 index 0000000..6b1dd5d --- /dev/null +++ b/messages.cfg @@ -0,0 +1,194 @@ +# Messages used in Usevote. Variables are enclosed in ${... }. +# Mostly these is interactive output for the votetaker, but texts from +# uvbounce.pl and uvrules.pl are also partly mailed back to the voters. +# +# Comments are allowed, but the # sign must be at the beginning of the line +# (no leading spaces or others characters allowed). +# +# General format: +# Identifier = message +# (Identifier has to start at the beginning of the line, without leading space) +# +################################################################################### +# +# UVconfig.pm +# +CONF_NOGROUPS = Kein Abstimmungsgegenstand definiert (siehe ${CONFIGFILE})! +CONF_NOBDSGFILE = Datei mit Datenschutzhinweis ("${BDSGFILE}") nicht lesbar! +CONF_NOSIG = Abschnitt [Signatur] nicht in Datei ${FILE} gefunden! +CONF_NOBADADDR = Datei mit verdaechtigen Adressen "${BADADDRFILE}" nicht lesbar! +CONF_TEST_RULES = Regeln aus usevote.rul: +CONF_NO_RULES = (keine Regeln definiert) +CONF_CONFIG = Konfiguration: +# +# UVmenu.pm +# +MENU_INVALIDNAME = Ungueltiger Name. +MENU_NONAME = Kein Name angegeben. +MENU_INVALIDBDSG = Datenschutzhinweis fehlerhaft oder nicht bestaetigt. +MENU_DUPLICATEVOTE = Doppelte Stimmabgabe gefunden. +MENU_NOVOTE = Keine Stimmabgabe gefunden. +MENU_INVALIDVOTE = Ungueltige Stimmabgabe. +MENU_NOBALLOTID = Keine Scheinkennung gefunden. +MENU_WRONGBALLOTID = Scheinkennung falsch. +MENU_ADDRESSNOTREGISTERED = Adresse nicht registriert. +MENU_INVALIDADDRESS = Ungueltige Mail-Adresse. +MENU_INVALIDADDRESS2 = Es wird keine Mail verschickt! +MENU_SUSPICIOUSACCOUNT = Verdaechtige Adresse gefunden. +MENU_UNRECOGNIZEDVOTE = Stimmen nicht vollstaendig erkannt. Im Zweifelsfall "Enthaltung" angenommen. +MENU_UNRECOGNIZED_LIST = Stimme +MENU_VIOLATEDRULE = Regel ${RULE} verletzt. +MENU_PROBLEMS = Die folgenden Probleme muessen beseitigt werden: +MENU_CAPTION = Auswahlmenue: +MENU_SHOW_MAIL = Anzeigen der Wahlmail +MENU_CHANGE_PROPERTIES = Bestaetigen oder Aendern von Wahlschein-Eigenschaften: +MENU_ADDRESS = Mailadresse +MENU_ADDRESS_CHANGE = Adresse aendern +MENU_ADDRESS_OK = Adresse OK +MENU_ADDRESS_INVALID = Adresse ungueltig +MENU_ADDRESS_PROMPT = Waehleradresse: +MENU_NAME = Waehlername +MENU_NAME_CHANGE = Namen aendern +MENU_NAME_OK = Name OK +MENU_NAME_INVALID = Name ungueltig +MENU_VOTES = Stimmen +MENU_VOTES_RESET = Stimmen neu setzen +MENU_VOTES_OK = Stimmen OK +MENU_VOTES_INVALID = Stimmen ungueltig +MENU_VOTES_CANCELLED = Stimmen vom Waehler annulliert +MENU_VOTES_REENTER = Stimme fuer ${GROUP} (J, N oder E): +MENU_VOTES_REENTER_ASK = Bitte die Stimmen neu eingeben. Die aus dem Wahlschein erkannten Stimmen sind jeweils der Standardwert und werden in [ ] aufgefuehrt. +MENU_VOTES_REENTER_LEGEND = J: Ja | N: Nein | E: Enthaltung +MENU_VOTES_REENTER_OLD = Bisherige Stimmen: +MENU_VOTES_REENTER_NEW = Neue Stimmen: +MENU_VOTES_REENTER_ACK = Stimmen uebernehmen +MENU_VOTES_REENTER_NACK = bisherige Stimmen belassen +MENU_BALLOT_ID = Scheinkennung +MENU_BDSG = Datenschutzklausel +MENU_BDSG_ACCEPTED = Datenschutzklausel wurde akzeptiert +MENU_BDSG_DECLINED = Datenschutzklausel nicht akzeptiert bzw. Text veraendert +MENU_IGNORE = Diese Stimme ignorieren (ohne Benachrichtigung verwerfen) +MENU_IGNORE_WARNING = Die Stimme wird nicht aufgezeichnet, und es wird keine Bestaetigung verschickt. ${MENU_IGNORE_STRING} eingeben, wenn Du sicher bist: +MENU_IGNORE_STRING = JA +MENU_PROCEED = Weiter +MENU_PROMPT = Eingabe: +MENU_GETKEY = *** Return druecken, um fortzufahren *** +MENU_PROCESSING = Verarbeite Mails... +MENU_ERROR_WARNING = WARNUNG +MENU_ERROR_TEXT = Es wurden nicht alle Fehler behoben. Der Waehler wird eine Fehlermail erhalten und die Stimme wird ungueltig gewertet. +MENU_ERROR_GETKEY = Bitte mit 'y' bestätigen oder mit jeder anderen Eingabe zurück: +MENU_DUP_VOTE = Moeglicherweise doppelte Stimmabgabe! +MENU_DUP_FIRST = Erste Stimme: +MENU_DUP_SECOND = Zweite Stimme: +MENU_DUP_DELFIRST = Erste loeschen +MENU_DUP_DELSECOND = Zweite loeschen +MENU_DUP_DELNONE = Keine loeschen +# +# UVreadmail.pm +# +READMAIL_STATUS = Abruf neuer Mails vom POP3-Server... +READMAIL_NOCONNECTION = Verbindung zum POP3-Server fehlgeschlagen! +READMAIL_NOLOGIN = Anmeldung am POP3-Server nicht moeglich! +READMAIL_NOMAILFILE = Maildatei ${FILE} nicht lesbar! +READMAIL_GET_PROBLEM = Warnung! Konnte Mail Nr. ${NR} nicht abrufen. +READMAIL_DEL_PROBLEM = Warnung! Konnte Mail Nr. ${NR} nicht loeschen. +READMAIL_UIDL_PROBLEM = Warnung! Konnte Liste mit UIDLs nicht speichern. +READMAIL_UIDL_PROBLEM2 = Beim naechsten Lauf keine Erkennung bereits abgerufener Mails moeglich! +READMAIL_UIDL_CLOSE = Warnung! Konnte UIDL-Cachedatei nicht ordnungsgemaess schliessen. +READMAIL_ARCHIVE_PROBLEM = Warnung! Wahlmails konnten nicht in Datei ${FILE} gesichert werden. +READMAIL_ARCHIVE_CLOSE = Warnung! Konnte Wahlmail-Archivdatei ${FILE} nicht schliessen. +# +# UVrules.pm +# +RULES_ERROPENFILE = Kann Regeldatei ${FILE} nicht oeffnen +RULES_TOOSHORT = Regel ${NUM}: '${TYPE}'-Bedingung zu kurz +RULES_TOOLONG = Regel ${NUM}: '${TYPE}'-Bedingung zu lang +RULES_INVCHARS = Regel ${NUM}: '${TYPE}'-Bedingung enthaelt nicht erlaubte Zeichen +RULES_RULE = Wahlregel +RULES_IF = Wenn Du +RULES_THEN = musst Du +RULES_AND = und +RULES_OR = oder +RULES_YES = JA +RULES_NO = NEIN +RULES_ABSTAIN = ENTHALTUNG +RULES_IFCLAUSE = ${VOTE} stimmst fuer ${GROUP} +RULES_THENCLAUSE = ${VOTE} stimmen fuer ${GROUP} +# +# UVsendmail.pm +# +SENDMAIL_ERROPENCONTROL = FEHLER! Steuerungsdatei ${FILE} konnte nicht geoeffnet werden. +SENDMAIL_ERRCLOSECONTROL = FEHLER! Steuerungsdatei ${FILE} konnte nicht geschlossen werden. +SENDMAIL_ERRDELCONTROL = FEHLER! Steuerungsdatei ${FILE} konnte nicht geloescht werden. +SENDMAIL_ERROPENMAIL = FEHLER! Mail-Datei ${FILE} konnte nicht geoeffnet werden. +SENDMAIL_ERRCLOSEMAIL = FEHLER! Mail-Datei ${FILE} konnte nicht geschlossen werden. +SENDMAIL_NOMAILS = Keine Mails zu verschicken (Datei ${FILE} nicht gefunden). +SENDMAIL_SMTP_CONNREFUSED = Keine Verbindung zum SMTP-Server moeglich! +SENDMAIL_SMTP_AUTHERR = Anmeldung am SMTP-Server fehlgeschlagen! +SENDMAIL_ERRNOTFOUND = Datei ${FILE} nicht gefunden. +SENDMAIL_SMTP_INVRCPT = Mail an ${RCPT} konnte nicht verschickt werden: +SENDMAIL_SENDING = Mails werden verschickt... +SENDMAIL_ERROCCURED = Fehler aufgetreten. Bitte kontrollieren und "uvvote.pl clean" ggf. noch einmal starten! +SENDMAIL_MISSINGFILES = Dateien konnten nicht gefunden werden. Dieser Fehler kann auftreten, falls bei einem vorherigen Lauf nur einzelne Mails verschickt werden konnten (diese Dateien wurden dann bereits geloescht). +SENDMAIL_ERRCLOSEDOMAIL = FEHLER! domail-Datei konnte nicht geschlossen werden. +SENDMAIL_ACKTXT_MISSING = KONFIGURATIONSFEHLER: Abschnitt [${SECTION}] in ${FILE} existiert nicht! Mails an die Waehler sind moeglicherweise unvollstaendig. +# +# uvballot.pl +# +BALLOT_NO_PERSONAL = Die Option -t kann nur in Zusammenhang mit persoenlichen Wahlscheinen verwendet werden (Option "personal" in ${CFGFILE}). +# +# uvbounce.pl +# +BOUNCE_BALLOT = ! Wahlschein nicht zustellbar (Adresse ungueltig) +BOUNCE_ACKMAIL = ! Bestaetigung nicht zustellbar (Adresse ungueltig) +# +# uvcfv.pl +# +CFV_NUMBER = Es werden ${COUNT} CfVs verschickt. +CFV_ERRWRITE = Kann nicht in Scheinkennungsdatei ${FILE} schreiben! +CFV_ERRCLOSE = Kann Scheinkennungsdatei nicht schliessen! +CFV_ERROPENCFV = Kann CfV-Datei ${FILE} nicht lesen! +CFV_SUBJECT = Wahlschein +# +# uvcount.pl +# +COUNT_ERR_OPEN = Kann Ergebnisdatei ${FILE} nicht oeffnen! +COUNT_ERR_RESULT = Fehler in ${FILE} Zeile ${LINE} +COUNT_ERR_GROUPCOUNT = Bei Stimme von <${ADDR}>: ${NUM1} statt ${NUM2} Stimmen gefunden (${RESULTFILE} kontrollieren!) +COUNT_DELETED = ${NUM} Stimme(n) geloescht. +# +# uvvote.pl +# +VOTE_RENAMING_MAILBOX = Benenne Stimmdatei um... +VOTE_WRITE_RESULTS = Ergebnisdatei ${FILE} nicht schreibbar! +VOTE_CLOSE_RESULTS = Ergebnisdatei ${FILE} konnte nicht erfolgreich geschlossen werden! +VOTE_NO_VOTES = Keine Stimmen zu verarbeiten. +VOTE_NUM_VOTES = ${COUNT} Stimmen bearbeitet. +VOTE_NOT_SAVED = ${COUNT} Stimmen bearbeitet, aber nicht gespeichert. +VOTE_FIRSTRUN = 'uvvote clean' aufrufen, um Ergebnisse zu speichern und Bestaetigungen zu verschicken. +VOTE_ERRORS = Folgende Fehler sind aufgetreten (siehe auch ${FILE}): +VOTE_INVALID_BALLOTID = Scheinkennung ungueltig +VOTE_MISSING_BALLOTID = Scheinkennung fehlt +VOTE_UNREGISTERED_ADDRESS = Adresse nicht registriert +VOTE_INVALID_VOTE = Ungueltige Stimmabgabe +VOTE_VIOLATED_RULE = Regel ${RULE} verletzt +VOTE_NO_VOTES = Keine Stimmen abgegeben +VOTE_INVALID_ACCOUNT = Ungueltiger Account +VOTE_INVALID_ADDRESS = Ungueltige Adresse +VOTE_INVALID_REALNAME = Ungueltiger Realname +VOTE_MISSING_NAME = Kein Name angegeben +VOTE_DUPLICATES = Verschiedene Stimmen fuer gleichen Abstimmungspunkt +VOTE_FILE_COMMENT = Wahlleiter setzte ${FIELDS} +VOTE_NO_NEW_RESULTS = Keine noch nicht verarbeiteten Ergebnisdateien gefunden! Bitte uvvote.pl zunaechst ohne die 'clean'-Option starten. +VOTE_MOVE_RESULTFILE = Fehler! Konnte Ergebnisdatei ${FILE} nicht verschieben: +VOTE_MOVE_VOTEFILE = Fehler! Konnte Stimmdatei ${FILE} nicht verschieben: +VOTE_CREATING_RESULTS = Erstelle neue Gesamtergebnisdatei ${FILENAME}... +# +# Allgemeine Meldungen +# +ERR_MKDIR = Kann Verzeichnis '${DIR}' nicht anlegen: +ERR_NOTPERSONAL = Usevote ist nicht fuer die Verwendung personalisierter Wahlscheine konfiguriert (Option "personal" in ${CFGFILE}) +ERR_FILE_CREATION = Fataler Fehler: Konnte keine Datei anlegen. +ERR_RENAME_MAILFILE = Konnte Maildatei nicht umbenennen: +ERR_LOCK = Lockfile ${FILE} bereits vorhanden! Wurde UseVoteGer mehrfach gestartet? +INFO_TIDY_UP = Aufraeumen... diff --git a/templates/ack-mail b/templates/ack-mail new file mode 100644 index 0000000..150df0b --- /dev/null +++ b/templates/ack-mail @@ -0,0 +1,46 @@ +ballotid-line := value ballotidtext | fill-right 20 | append ballotid +address-line := value addresstext | fill-right 20 | append address +name-line := value nametext2 | fill-right 20 | append name +pos := value pos | fill-right 2 +group-first := value group | first-words 50 +group-more := value group | drop-words 50 | create-lines 50 +vote := value vote | fill-both 10 +votetaker := value mailfrom | fill-left 65 + +== TEMPLATE ================================================================= +Diese automatische Nachricht wurde dir nach Zaehlung deiner Stimme +zugesandt. Wenn alles stimmt, gibt es keinen Anlass fuer eine Reaktion. +Wenn deine Stimme falsch registriert wurde, stimme bitte erneut ab, +indem du diese Mail komplett zitierst und die falschen Wertungen +korrigierst (zwischen die eckigen Klammern schreiben). Dabei darf +keinesfalls die laufende Nummer am Zeilenanfang entfernt werden. + +Diese Wahl ist oeffentlich, und die Adressen aller Waehlerinnen und +Waehler werden am Ende bekanntgegeben. Wenn du deine Adresse & Stimme +loeschen willst, kannst du erneut abstimmen und dabei 'ANNULLIERUNG' +anstelle von 'JA' oder 'NEIN' angeben. \[Doppel-N, Doppel-L :-)\] + +[?personal|Da diese Abstimmung mit personalisierten Wahlscheinen durchgefuehrt +wird, sind auch saemtliche Aenderungen nur mit Angabe der folgenden +Wahlscheinkennung gueltig! + +[$ballotid-line]] +[$address-line] +[$name-line] + (Real-Namen sind fuer diese Abstimmung vorgeschrieben. Wenn hier + nicht Dein wirklicher Name steht, dann korrigiere die Zeile + bitte und sende die Nachricht zurueck; sonst kann die Stimme + spaeter als ungueltig gewertet werden.) + + +Deine Stimmabgabe wurde wie folgt erkannt: + +[@groups|#[$pos] \[[$vote]\] fuer [$group-first] +[@group-more| [$line]\n]|\n] + + +Danke fuer deine Stimmabgabe. Eine Kopie des CfV kannst du von mir er- +halten (bitte Namen der Abstimmung angeben, falls mehrere laufen). + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/address-not-registered b/templates/address-not-registered new file mode 100644 index 0000000..f12ae79 --- /dev/null +++ b/templates/address-not-registered @@ -0,0 +1,25 @@ +votetaker := value mailfrom | fill-left 65 +head := value head | quote "> " +body := value body | quote "> " + +== TEMPLATE ================================================================= +Deine Adresse ist beim Wahlleiter bisher nicht mit einer Wahlschein- +kennung registriert, mit einer EMail an die im CfV genannten Adresse +kannst du dir eine Kennung zuteilen lassen. + +Solltest du bereits eine Kennung erhalten haben, so achte bitte darauf, +diese nur mit genau der Mailadresse zu verwenden, fuer die sie +registriert ist. + +Achtung, die Mailadresse wird unter Beachtung von Gross- und Klein- +schreibung ausgewertet, Meier und meier sind daher _nicht_ identisch! + +Hier die Mail, die ich erhalten habe: + +[$head] +> +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/ballot b/templates/ballot new file mode 100644 index 0000000..5e3242a --- /dev/null +++ b/templates/ballot @@ -0,0 +1,34 @@ +votename-first := value votename | first-words 55 +votename-more := value votename | drop-words 55 | create-lines 55 + +pos := value pos | fill-right 2 + +group-first := value group | first-words 50 +group-more := value group | drop-words 50 | create-lines 50 + +bdsginfo := value bdsginfo | create-lines 72 +bdsgtext-first := value bdsgtext | first-words 50 +bdsgtext-more := value bdsgtext | drop-words 50 | create-lines 50 + +== TEMPLATE ================================================================= + +=-=-=-=-=-=-=-=- Alles vor dieser Zeile bitte loeschen =-=-=-=-=-=-=-=- + +WAHLSCHEIN fuer [$votename-first] +[@votename-more| [$line]\n] + + +[$nametext] + +Wenn du keinen Real-Namen angibst, wird deine Stimme fuer +ungueltig erklaert werden. + + +Nr \[Deine Stimme\] Gruppe/Abstimmungsgegenstand +======================================================================== +[@groups|#[$pos] \[ \] [$group-first] +[@group-more| [$line]\n]|\n] +[?bdsg|[@bdsginfo|[$line]|\n]\n\n#a \[ \] [$bdsgtext-first] +[@bdsgtext-more| [$line]|\n]] + +=-=-=-=-=-=-=-=- Alles nach dieser Zeile bitte loeschen =-=-=-=-=-=-=-=- diff --git a/templates/ballot-personal b/templates/ballot-personal new file mode 100644 index 0000000..13aef2a --- /dev/null +++ b/templates/ballot-personal @@ -0,0 +1,63 @@ +votename-first := value votename | first-words 55 +votename-more := value votename | drop-words 55 | create-lines 55 + +pos := value pos | fill-right 2 + +group-first := value group | first-words 50 +group-more := value group | drop-words 50 | create-lines 50 + +bdsginfo := value bdsginfo | create-lines 72 +bdsgtext-first := value bdsgtext | first-words 50 +bdsgtext-more := value bdsgtext | drop-words 50 | create-lines 50 + +votetaker := value mailfrom | fill-left 65 + +== TEMPLATE ================================================================= + +[?alreadysent| +!! Dieser CfV wurde dir bereits einmal zugeschickt, sollte der erste +!! Wahlschein nicht angekommen oder die Anforderung nicht auf deinen +!! Wunsch geschehen sein, so wende dich bitte an den Wahlleiter. +] + + + +************************************ +*** hier den CfV-Text einfuegen! *** +************************************ + + +=-=-=-=-=-=-=-=- Alles vor dieser Zeile bitte loeschen =-=-=-=-=-=-=-=- + +WAHLSCHEIN fuer [$votename-first] +[@votename-more| [$line]\n] + + +[$ballotidtext] [$ballotid] +[$addresstext] [$address] + +Dies ist deine persoenliche und _geheime_ Wahlschein-Kennung! +Halte deine Wahlscheinkennung zwecks Missbrauchs- und Manipulations- +vermeidung bitte moeglichst geheim, da jeder, der sie kennt, in +deinem Namen abstimmen kann! + +[$nametext] + +Wenn du keinen Real-Namen angibst, wird deine Stimme fuer +ungueltig erklaert werden. + + +Nr \[Deine Stimme\] Gruppe/Abstimmungsgegenstand +======================================================================== +[@groups|#[$pos] \[ \] [$group-first] +[@group-more| [$line]\n]|\n] +[?bdsg|[@bdsginfo|[$line]|\n]\n\n#a \[ \] [$bdsgtext-first] +[@bdsgtext-more| [$line]|\n]] + +=-=-=-=-=-=-=-=- Alles nach dieser Zeile bitte loeschen =-=-=-=-=-=-=-=- + + +Danke fuer Deine Stimmabgabe. + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/ballot-request b/templates/ballot-request new file mode 100644 index 0000000..b1b0c69 --- /dev/null +++ b/templates/ballot-request @@ -0,0 +1,10 @@ +=-=-=-=-=-=-=-=- Alles vor dieser Zeile bitte loeschen =-=-=-=-=-=-=-=- + +Zur Wahlscheinanforderung einfach formlos *per Mail* auf dieses +Posting antworten. Falls nach einigen Tagen noch kein Wahlschein +eingetroffen ist, kannst du unter folgender Adresse mit dem +Wahlleiter Kontakt aufnehmen: + +[$mailaddress] + +=-=-=-=-=-=-=-=- Alles nach dieser Zeile bitte loeschen =-=-=-=-=-=-=-=- diff --git a/templates/bdsg-error b/templates/bdsg-error new file mode 100644 index 0000000..bd44741 --- /dev/null +++ b/templates/bdsg-error @@ -0,0 +1,29 @@ +votetaker := value mailfrom | fill-left 65 + +== TEMPLATE ================================================================= +Zur Wertung deiner Stimme ist die Zustimmung zur Speicherung, +Verarbeitung und Veroeffentlichung der Stimmabgabe erforderlich. +Diese Zustimmung konnte nicht automatisch erkannt werden. +deine Stimme wurde daher geloescht und wird nicht gewertet. + +Dieses kann folgende Gruende haben: + +* In deinem Wahlschein fehlt der Abschnitt, der ueber die Art + der Verarbeitung deiner Stimmdaten informiert, oder er wurde + als nicht vollstaendig erkannt. Um bezueglich des deutschen + Bundesdatenschutzgesetzes keine Risiken einzugehen, muss ich + darauf bestehen, dass dieser Absatz unveraendert mitgeschickt + und akzeptiert wird. + +* Du hast nicht der Speicherung/Verarbeitung/Veroeffentlichung + zugestimmt. Im dazugehoerigen Feld muss "JA" eingetragen + werden. + +Bitte stimme erneut ab, lasse dabei den Hinweistext im +Wahlschein intakt und akzeptiere die Klausel, indem du im +vorgesehen Feld "JA" eintraegst. Sollte das Problem aufgrund eines +Uebermittlungsfehlers bestehen bleiben, melde Dich bitte unter +der unten angegebenen Adresse bei mir. + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/bouncelist b/templates/bouncelist new file mode 100644 index 0000000..6f570bd --- /dev/null +++ b/templates/bouncelist @@ -0,0 +1,10 @@ +multi-line := value name | justify-before mail 72 +bouncetext := value bouncetext | create-lines 72 + +== TEMPLATE ================================================================= + +Als ungueltig erkannte Stimmen: +------------------------------------------------------------------------ +[@bounces|[$multi-line] +[@bouncetext|[$line]]|\n] + diff --git a/templates/cancelled b/templates/cancelled new file mode 100644 index 0000000..07cb9b2 --- /dev/null +++ b/templates/cancelled @@ -0,0 +1,12 @@ +votetaker := value mailfrom | fill-left 65 + +== TEMPLATE ================================================================= +Diese automatische Nachricht wurde dir nach Erhalt Deiner ANNULLIERUNG +zugesandt. Jede vorherige Stimmabgabe in dieser Abstimmung ist dadurch +annulliert und wird weder gezaehlt noch veroeffentlicht. + +Wenn dies ein Fehler war und du doch waehlen moechtest, musst du erneut +abstimmen. + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/invalid-account b/templates/invalid-account new file mode 100644 index 0000000..909dc01 --- /dev/null +++ b/templates/invalid-account @@ -0,0 +1,27 @@ +votetaker := value mailfrom | fill-left 65 +head := value head | quote "> " +body := value body | quote "> " + +== TEMPLATE ================================================================= +Deine Stimme kann nicht akzeptiert werden, da du eine kritische +Absenderadresse verwendest. Nichtpersoenliche Accounts wie root@, +operator@, postmaster@, guest@ usw. sind im Allgemeinen nicht zu +Abstimmungen zugelassen, insbesondere, wenn kein Realname angegeben +wird. Stimmen von Anon-Servern werden ebenfalls nicht akzeptiert. +Bitte stimme erneut von einem persoenlichen Account aus ab. + +Wenn das ein Fehler ist und dein wahrer Name/Account faelschlicherweise +fuer einen dieser unpersoenlichen oder Anon-Accounts gehalten wurde, so +schicke bitte eine eMail an meine persoenliche Adresse. Sorry fuer das +umstaendliche Verfahren, aber nur so ist die zuverlaessige automatische +Stimmenzaehlung moeglich. + +Hier die Mail, die ich erhielt: + +[$head] +> +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/invalid-name b/templates/invalid-name new file mode 100644 index 0000000..ce7a28d --- /dev/null +++ b/templates/invalid-name @@ -0,0 +1,19 @@ +votetaker := value mailfrom | fill-left 65 +head := value head | quote "> " +body := value body | quote "> " + +== TEMPLATE ================================================================= +Dieser Call for Votes (CfV) erfordert die Angabe eines Real-Namens ent- +weder im "From:"-Feld des Headers oder im dafuer vorgesehehen Feld des +Wahlscheins. Bitte wiederhole Deine Stimmabgabe mit Angabe Deines wirk- +lichen Namens. + +Hier die Mail, die ich erhalten habe: + +[$head] +> +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/mailheader b/templates/mailheader new file mode 100644 index 0000000..93746ba --- /dev/null +++ b/templates/mailheader @@ -0,0 +1,16 @@ +date := generate-date-header +msgid := generate-msgid + +== TEMPLATE ================================================================= +From: [$from] +Subject: [$subject] +To: [$address] +[?reply-to|Reply-To: [$reply-to]] +[?reference|In-Reply-To: [$reference]] +[?reference|References: [$reference]] +Message-ID: [$msgid] +Date: [$date] +X-Automated-Message: generated by [$usevote-version] +MIME-Version: 1.0 +Content-Type: text/plain; charset\=iso-8859-1 +Content-Transfer-Encoding: 8bit diff --git a/templates/multiple-votes b/templates/multiple-votes new file mode 100644 index 0000000..7da6433 --- /dev/null +++ b/templates/multiple-votes @@ -0,0 +1,19 @@ +votetaker := value mailfrom | fill-left 65 +body := value body | quote "> " + +== TEMPLATE ================================================================= +Dein Wahlschein enthielt widerspruechliche Stimmen (z.B. sowohl JA- als +auch NEIN-Stimme fuer dieselbe Gruppe). + +Das passiert meistens, wenn du mehrere Zeilen schickst, die als Stimme +interpretiert werden, z. B. wenn du vergisst, eine Beispielzeile im CfV +zu loeschen. Bitte lies den CfV noch einmal und wiederhole deine Stimm- +abgabe. + +Hier die Mail, die ich erhalten habe: + +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/no-ballot b/templates/no-ballot new file mode 100644 index 0000000..4a2b71e --- /dev/null +++ b/templates/no-ballot @@ -0,0 +1,16 @@ +votetaker := value mailfrom | fill-left 65 +body := value body | quote "> " + +== TEMPLATE ================================================================= +In deiner Mail wurde kein gueltiger Wahlschein gefunden. Bitte lies die +Hinweise im Call for Votes (CfV) und stimme nochmal ab. +Dieser Fehler tritt meistens dann auf, wenn du Zeilen loeschst, die du +laut CfV nicht haettest loeschen sollen. + +Hier die Mail, die ich erhalten habe: + +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/no-ballotid b/templates/no-ballotid new file mode 100644 index 0000000..2628944 --- /dev/null +++ b/templates/no-ballotid @@ -0,0 +1,19 @@ +votetaker := value mailfrom | fill-left 65 +head := value head | quote "> " +body := value body | quote "> " + +== TEMPLATE ================================================================= +In deinem Wahlschein konnte keine Wahlscheinkennung gefunden werden, +diese wurde dir zusammen mit dem Wahlschein zugeschickt. Solltest +du noch keine eigenen Wahlscheinkennung haben, so kannst du mit +einer Mail an die im CfV genannte Adresse eine anfordern. + +Hier die Mail, die ich erhalten habe: + +[$head] +> +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/no-votes b/templates/no-votes new file mode 100644 index 0000000..1a6de00 --- /dev/null +++ b/templates/no-votes @@ -0,0 +1,20 @@ +votetaker := value mailfrom | fill-left 65 +body := value body | quote "> " + +== TEMPLATE ================================================================= +Offenbar hast du keinerlei Stimmen abgegeben. Wenn das ein Irrtum war, +lies bitte nochmal die Hinweise im CfV und wiederhole Deine Stimmabgabe. + +Dieser Fehler tritt oft auf, wenn du dich vertippst oder dich nicht an +das im Wahlschein vorgegebene Format haeltst. + +Wenn du dich enthalten willst, dann setze bitte ENTHALTUNG an die +Stelle von JA oder NEIN. + +Hier die Mail, die ich erhalten habe: + +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/result-multi b/templates/result-multi new file mode 100644 index 0000000..cc3659b --- /dev/null +++ b/templates/result-multi @@ -0,0 +1,31 @@ +votename-first := value votename | first-words 60 +votename-more := value votename | drop-words 60 | create-lines 60 +numinvalid := value numinvalid | fill-left 4 +numabstain-formatted := value numabstain | fill-left 4 +yes := value yes | fill-left 4 +no := value no | fill-left 4 +group-first := value group | first-words 40 +group-more := value group | drop-words 40 | create-lines 40 + +== TEMPLATE ============================================================ + +Ergebnisse [$votename-first] +[@votename-more| [$line]\n] +([$numvalid] gueltige Stimmen) + + Ja Nein : 2/3? >=60? : ang.? : Gruppe +---- ---- : ---- ----- : ----- : --------------------------------------- +[@count|[$yes] [$no] : [?cond1| Ja |Nein] [?cond2| Ja |Nein] : [?result| Ja |Nein] : [$group-first] +[@group-more| : : : [$line]\n]\n] +[?numabstain|[$numabstain-formatted] Enthaltungen] +[?numinvalid|[$numinvalid] ungueltige Stimme(n)] + +Gegen dieses Ergebnis kann innerhalb einer Woche nach seiner +Veroeffentlichung Einspruch erhoben werden. Der Einspruch ist per +E-Mail bei der Moderation von de.admin.news.announce (Adressen +siehe Signatur) einzulegen. +Wenn es keine ernsthaften Einsprueche gibt oder diese abgelehnt +werden, wird die Moderation von de.admin.news.announce das +Ergebnis danach umsetzen. + + diff --git a/templates/result-proportional b/templates/result-proportional new file mode 100644 index 0000000..53685d1 --- /dev/null +++ b/templates/result-proportional @@ -0,0 +1,31 @@ +votename-first := value votename | first-words 60 +votename-more := value votename | drop-words 60 | create-lines 60 +proportion := value proportion | sprintf '%6.3f' +numinvalid := value numinvalid | fill-left 4 +numabstain-formatted := value numabstain | fill-left 4 +yes := value yes | fill-left 4 +no := value no | fill-left 4 +group-first := value group | first-words 40 +group-more := value group | drop-words 40 | create-lines 40 + +== TEMPLATE ============================================================ + +Ergebnisse [$votename-first] +[@votename-more| [$line]\n] +([$numvalid] gueltige Stimmen) + + Ja Nein : J>=N? Ja/Nein : ang.? : Gruppe +---- ---- : ----- ------- : ----- : --------------------------------------- +[@count|[$yes] [$no] : [?cond1| Ja |Nein] [$proportion] : : [$group-first] +[@group-more| : : : [$line]\n]\n] +[?numinvalid|[$numinvalid] ungueltige Stimme(n)] + +Gegen dieses Ergebnis kann innerhalb einer Woche nach seiner +Veroeffentlichung Einspruch erhoben werden. Der Einspruch ist per +E-Mail bei der Moderation von de.admin.news.announce (Adressen +siehe Signatur) einzulegen. +Wenn es keine ernsthaften Einsprueche gibt oder diese abgelehnt +werden, wird die Moderation von de.admin.news.announce das +Ergebnis danach umsetzen. + + diff --git a/templates/result-single b/templates/result-single new file mode 100644 index 0000000..1be10d2 --- /dev/null +++ b/templates/result-single @@ -0,0 +1,28 @@ +votename-first := value votename | first-words 60 +votename-more := value votename | drop-words 60 | create-lines 60 + +votename-text-first := value votename | first-words 30 +votename-text-more := value votename | drop-words 30 | create-lines 72 + +== TEMPLATE ============================================================ + +Ergebnisse [$votename-first] +[@votename-more| [$line]\n] +([$numvalid] gueltige Stimmen) + +Es gab [$yes] Ja-Stimmen und [$no] Nein-Stimmen[?numabstain| bei [$numabstain] Enthaltungen]. +[?numinvalid|[$numinvalid] Stimme(n) wurden als ungueltig gewertet.] + +Es wurde [?cond1|die|keine] 2/3-Mehrheit erreicht und es gingen [?cond2|mehr|weniger] als +60 Ja-Stimmen ein. Damit ist die [$votename-text-first] +[@votename-text-more|[$line] |\n][?cond1|[?cond2|angenommen|abgelehnt]|abgelehnt]. + +Gegen dieses Ergebnis kann innerhalb einer Woche nach seiner +Veroeffentlichung Einspruch erhoben werden. Der Einspruch ist per +E-Mail bei der Moderation von de.admin.news.announce (Adressen +siehe Signatur) einzulegen. +Wenn es keine ernsthaften Einsprueche gibt oder diese abgelehnt +werden, wird die Moderation von de.admin.news.announce das +Ergebnis danach umsetzen. + + diff --git a/templates/rule-violated b/templates/rule-violated new file mode 100644 index 0000000..ef95b33 --- /dev/null +++ b/templates/rule-violated @@ -0,0 +1,20 @@ +votetaker := value mailfrom | fill-left 65 +body := value body | quote "> " + +== TEMPLATE ================================================================= +Diese Abstimmung unterliegt bestimmten Bedingungen, fuer welche Gruppen +wie abgestimmt werden kann. Diese Bedingungen wurden im Call for Votes +erklaert. Deine Stimme verletzt eine oder mehrere der Regeln. Bitte lies +den CfV noch einmal durch und wiederhole Deine Stimmabgabe. + +Die folgenden Regeln wurden nicht eingehalten: + +[$rules] + +Hier die Mail, die ich erhalten habe: + +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/templates/voterlist b/templates/voterlist new file mode 100644 index 0000000..4187ce6 --- /dev/null +++ b/templates/voterlist @@ -0,0 +1,12 @@ +multi-line := value name | justify-before mail 72 + +== TEMPLATE ============================================================ + +Folgende Personen haben sich bislang an der Abstimmung beteiligt: +======================================================================== +[@multi|[$multi-line]|\n] + + +Als ungueltig erkannte Stimmen: +======================================================================== +[@invalid|[$multi-line]\n[$reason]|\n] diff --git a/templates/votes-multi b/templates/votes-multi new file mode 100644 index 0000000..6029098 --- /dev/null +++ b/templates/votes-multi @@ -0,0 +1,36 @@ +grafix := value group | multi-graph 72 pos groupcount +grafix-line := multi-line 72 groupcount +multi-line := value name | append vote | justify-before mail 72 +invalid-line := value name | justify-before mail 72 +numinvalid := value numinvalid | fill-left 4 +yes := value yes | fill-left 4 +no := value no | fill-left 4 +condition1 := value cond1 | fill-both 4 +condition2 := value cond2 | fill-both 5 +result := value result | fill-both 5 + +== TEMPLATE ============================================================ + +======================================================================== + +Wichtiger Hinweis: + +Die unten aufgefuehrten Personen haben der Speicherung, Verarbeitung +und Veroeffentlichung ihrer Adressen und Stimmdaten nur im Rahmen dieses +Verfahrens zugestimmt. Eine Verwendung darueber hinaus wurde nicht +erlaubt. Damit ist insbesondere die Nutzung oder Uebermittlung der Daten +fuer Werbezwecke oder fuer die Markt- oder Meinungsforschung verboten. + +======================================================================== + + +Liste der gezaehlten Stimmen: + +[@groups|[$grafix]|\n] +[$grafix-line] +[@multi|[$multi-line]|\n] + + +Ungueltige Stimmen: +------------------------------------------------------------------------ +[@invalid|[$invalid-line]\n[$reason]|\n] diff --git a/templates/votes-single b/templates/votes-single new file mode 100644 index 0000000..64e1d37 --- /dev/null +++ b/templates/votes-single @@ -0,0 +1,33 @@ +mail-name := value mail | justify name 72 +invalid-line := value name | justify-before mail 72 + +== TEMPLATE ============================================================ + +======================================================================== + +Wichtiger Hinweis: + +Die unten aufgefuehrten Personen haben der Speicherung, Verarbeitung +und Veroeffentlichung ihrer Adressen und Stimmdaten nur im Rahmen dieses +Verfahrens zugestimmt. Eine Verwendung darueber hinaus wurde nicht +erlaubt. Damit ist insbesondere die Nutzung oder Uebermittlung der Daten +fuer Werbezwecke oder fuer die Markt- oder Meinungsforschung verboten. + +======================================================================== + + +Ja gestimmt: +------------------------------------------------------------------------ +[@yes|[$mail-name]\n] + +Nein gestimmt: +------------------------------------------------------------------------ +[@no|[$mail-name]\n] + +[?abstain|Enthaltung: +------------------------------------------------------------------------ +[@abstain|[$mail-name]\n]] + +Ungueltige Stimmen: +------------------------------------------------------------------------ +[@invalid|[$invalid-line]\n[$reason]|\n] diff --git a/templates/wrong-ballotid b/templates/wrong-ballotid new file mode 100644 index 0000000..0eb49f7 --- /dev/null +++ b/templates/wrong-ballotid @@ -0,0 +1,23 @@ +votetaker := value mailfrom | fill-left 65 +head := value head | quote "> " +body := value body | quote "> " + +== TEMPLATE ================================================================= +Die Wahlscheinkennung, die du in deinem Wahlschein benutzt hast, +entspricht nicht der Kennung, die beim Wahlleiter fuer deine EMail- +Adresse registriert ist, bitte benutzte die Wahlscheinkennung +aus deinem persoenlichen Wahlschein. + +Solltest du wirklich eine andere Wahlscheinkennung zugeschickt +bekommen haben (oder du hast gar keinen Wahlschein angefordert), +so setzte dich bitte mit mir in Verbindung. + +Hier die Mail, die ich erhalten habe: + +[$head] +> +[$body] + + +[$votetaker] +\[mit [$usevote_version]\] diff --git a/tmp/ergebnis-1191790177 b/tmp/ergebnis-1191790177 new file mode 100644 index 0000000..e69de29 diff --git a/tmp/stimmen-1191790177 b/tmp/stimmen-1191790177 new file mode 100644 index 0000000..e69de29 diff --git a/uidlcache b/uidlcache new file mode 100644 index 0000000..e69de29 diff --git a/usevote.cfg b/usevote.cfg new file mode 100644 index 0000000..cf801f5 --- /dev/null +++ b/usevote.cfg @@ -0,0 +1,293 @@ +######################################################################## +# Diese Einstellungen muessen fuer jede Abstimmung angepasst werden +######################################################################## + +# Name der Abstimmung +votename = Einrichtung von xyz + +# Abstimmungsgegenstaende (beliebig viele groupX moeglich, +# von 1 an durchzunummerieren) +group1 = Einrichtung von abc + +# Bei den folgenden Fragen bedeutet jeweils: +# 0: nein +# 1: ja + +# Persoenliche Wahlscheine generieren und Scheinkennung erzwingen? +personal = 0 + +# Verhaeltniswahl durchfuehren? (z.B. fuer Moderationsnachwahlen) +proportional = 0 + +# Formel fuer die Berechnung des Verhaeltnisses. Kann z.B. +# $yes/$no oder $yes-$no sein, letzteres wird bei Moderationsnachwahlen +# verwendet. Als Variablen sind $yes und $no zulaessig, es kann +# beliebiger Perlcode angegeben werden, dessen Rueckgabewert im +# Result erscheinen soll. Ausserdem wird bei der Auswertung die +# Bedingung aus "condition1" weiter unten in dieser Datei ausgewertet. +prop_formula = $yes/$no + +# Vote-Account (diese Adresse muss unbedingt korrekt sein, wird +# in das Reply-To uebernommen) +voteaccount = vote-xyz@foo.bar + +# Absender fuer den From-Header der Bestaetigungsmails +mailfrom = Vorname Nachname + +# Absender fuer den Envelope (Return-Path) der Bestaetigungsmails +# bei Verwendung von SMTP (bitte einfach nur die Adresse eintragen, +# ohne Klammern und Zusaetze). Bei smtp=0 muss das in "mailcmd" +# eingestellt werden, z.B. "-fadresse" für Sendmail +envelopefrom = gvv@foo.bar + +# Nur bei persoenlichen Wahlscheinen: Datei mit Anforderungsmails +requestfile = anforderung + +# Datenschutzklausel generieren und auf deren Existenz pruefen? +# Konfiguration siehe unten (bdsgtext) +bdsg = 1 + +# Reply-To beachten? +# Nicht empfohlen, da jemand fuer andere Personen abstimmen und +# die Bestaetigungen zu sich umlenken koennte. +replyto = 0 + +# Stimmen einzeln bestaetigen? Empfohlen! +voteack = 1 + +# Bcc-Adresse fuer alle Mails (Backup fuer alle Faelle) +# +#mailcc = + +# Alles in einem Schritt durchfuehren (gesonderter Aufruf von +# "uvvote.pl clean" entfaellt)? Fuehrt zu geringeren +# Eingreifmoeglichkeiten! (Mails werden automatisch verschickt) +onestep = 0 + +# Fuer das Ergebnis (xx Ja-Stimmen, xx Nein-Stimmen, xx Enthaltungen) +# auch bei Eingruppenabstimmung des Mehrgruppenformat waehlen? +multigroup = 1 + +# Bedingungen fuer einen Erfolg der Abstimmung (genau zwei erforderlich). +# Es muss sich jeweils um gueltigen Perl-Code handeln, Rueckgabewert +# wird boolesch ausgewertet (true/false). Als Variablen sind $yes +# und $no zugelassen. Falls oben "proportional = 1" gesetzt wurde, +# wird nur Bedingung 1 ausgewertet und kann z.B. auf "$yes>$no" gesetzt +# werden. +condition1 = $yes>=2*$no +condition2 = $yes>=60 + +# Ergebnisdatei, in der alle Einzelergebniss zusammengeschrieben werden +# (wird bei jedem Programmlauf neu erstellt!) +resultfile = ergebnis.alle + +# Datei fuer Scheinkennungen +idfile = scheinkennungen + +# POP3 benutzen? (falls nicht, wird eine lokale Mailbox eingelesen) +pop3 = 1 + +# Mailbox, in der die zu verarbeitenden Mails liegen (falls pop3=0) +votefile = votes + +# POP3-Einstellungen fuer Abruf der eingehenden Wahlscheine: +# Server, Port, Benutzername, Passwort +pop3server = 127.0.0.1 +pop3port = 110 +pop3user = test +pop3pass = test +# Mail nach dem Abrufen vom Server loeschen? +pop3delete = 0 +# Dateiname zum Speichern der bereits abgerufenen Mail-IDs (UIDL) +pop3uidlcache = uidlcache + +# POP3-Einstellungen fuer Abruf von Wahlschein-Anforderungen bei Abstimmungen +# mit personalisierten Wahlscheinen (Punkt 6a der Wahlregeln in de.*) +# Diese zweite Mailbox ist notwendig, um Wahlschein-Anforderungen und die +# eigentliche Abstimmung voneinander zu trennen (nicht noetig, wenn +# personal = 0 gesetzt ist) +pop3server_req = 127.0.0.1 +pop3port_req = 110 +pop3user_req = test +pop3pass_req = test +# Mail nach dem Abrufen vom Server loeschen? +pop3delete_req = 0 +# Dateiname zum Speichern der bereits abgerufenen Mail-IDs (UIDL) +pop3uidlcache_req = uidlcache_req + +# POP3-Einstellungen fuer uvbounce.pl (Verarbeitung von Bounces +# und Generierung einer Liste mit ungueltigen Stimmen). Alle +# zurueckgekommenen Mails an Waehler sollten in dieser Mailbox landen +pop3server_bounce = 127.0.0.1 +pop3port_bounce = 110 +pop3user_bounce = test2 +pop3pass_bounce = test2 +# Mail nach dem Abrufen vom Server loeschen? +pop3delete_bounce = 0 +# Dateiname zum Speichern der bereits abgerufenen Mail-IDs (UIDL) +pop3uidlcache_bounce = uidlcache_bounce + +######################################################################## +# Alles ab hier braucht i.d.R. nur einmal festgelegt werden. +# Es ist moeglich, diese Einstellungen in eine globale Konfigurations- +# datei auszulagern. Das Einbinden erfolgt mit der Zeile +# +# include /pfad/zur/globalen_datei +# +# Falls Einstellungen aus der globalen Konfigurationsdatei hier +# fuer einzelne Abstimmungen ueberschrieben werden sollen, muessen +# diese *hinter* dem Include-Befehl stehen! +# (der letzte Wert ueberschreibt vorhergehende Definitionen) +######################################################################## + +# SMTP benutzen? (falls nicht, wird der weiter unten einstellbare +# MTA direkt aufgerufen; unter Windows kann nur SMTP benutzt werden!) +smtp = 1 + +# SMTP-Server (falls smtp = 1) +smtpserver = localhost +smtpport = 25 + +# SMTP-Authentifizierung benutzen? (RFC 2554) +# Das entsprechende Perlmodul (Net::SMTP) kann derzeit nur AUTH PLAIN, +# funktioniert also moeglicherweise nicht mit jedem Server +#smtpauth = 0 +#smtpuser = +#smtppass = + +# Falls als HELO etwas anderes als der Hostname verwendet werden soll: +#smtphelo = + +# Falls ein anderer Fully Qualified Domain Name als der Hostname fuer +# die Message-ID verwendet werden soll: +#fqdn = + +# Verzeichnis fuer fertig verarbeitete Mails und Ergebnisse +archivedir = fertig + +# Temporaeres Verzeichnis +tmpdir = tmp + +# Pfad zu den Templates (kommaseparierte Liste mit Verzeichnissen) +templatedir = templates + +# Konvertierungsfunktionen für die Templates +# (kommaseparierte Liste mit Funktions-Modulen) +formats = UVformats.pm + +# Dateiname der Steuerungsdatei fuer den Mailversandt +controlfile = tmp/ack.control + +# Dateiname des Shellscripts zum Versenden der Bestaetigungsmails (falls smtp=0) +domailfile = tmp/domail + +# MTA-Aufruf zum Verschicken der Bestaetigungsmails +# nuetzlich ist die Sendmail-Option -f zum Setzen des Absenders +#mailcmd = sendmail -oi -oem -femail@adresse + +# Weiteres Kommando, welches nach jeder Mail aufgerufen werden soll (falls smtp=0). +# Sinnvoll ist ein "sleep x", wobei x bei langsamen Systemen hoeher +# gewaehlt werden sollte, um die Belastung gering zu halten. +sleepcmd = sleep 1 + +# Shellbefehl zum Loeschen des Bildschirms +# Unix: i.d.R. "clear" +# Windows: "cls" +# Falls das Betriebssystem bzw. die Shell keinen solchen Befehl zur Verfuegung +# stellt, sollte ein Kommando verwendet werden, welches eine Trennlinie +# oder aehnliches auf dem Bildschirm ausgibt, z.B. mit "echo" +clearcmd = clear + +# Shellbefehl zum seitenweisen Darstellen von Mails auf dem Bildschirm +# Empfohlene Einstellung: "less", da more Probleme mit der Umleitung von +# STDERR in eine Datei Probleme hat. Unter Windows ist "less" nicht +# vorinstallirt, kann man sich aber herunterladen und einfach in das +# Windows-Verzeichnis kopieren (URL siehe README-Datei) +pager = less + +# Datei mit diversen Meldungen und Textfragmenten (Resourcendatei) +messagefile = messages.cfg + +# Datei mit Wahlregeln +rulefile = usevote.rul + +# Datei mit verdaechtigen Mailadressen +badaddrfile = mailpatterns.cfg + +# Datei fuer Fehlermeldungen beim Programmlauf +errorfile = errors.log + +# Lockdatei (Verhinderung von mehrfachen Programmstarts) +lockfile = usevote.lock + +# Einleitungszeile fuer naechste Mail (RegExp) +mailstart = "^From " + +# Trennlinien vor und nach dem Wahlschein +begin_divider = Alles vor dieser Zeile bitte loeschen +end_divider = Alles nach dieser Zeile bitte loeschen + +# Text fuer die Namens-Angabe im Wahlschein. Achtung, muss im +# Wahlschein genauso stehen! +nametext = Dein Realname, falls nicht im FROM-Header: + +# Text fuer Namens-Angabe in Bestaetigungsmails +nametext2 = Waehlername: + +# Text fuer die Adress-Angabe im Wahlschein +addresstext = Waehleradresse: + +# Text für die Angabe der Wahlscheinkennung (siehe Option "personal") +ballotidtext = Wahlscheinkennung: + +# Text fuer Datenschutzklausel (siehe Option "bdsg"), erscheint als Abstimmungspunkt +bdsgtext = Datenschutzklausel - Zustimmung: Ich bin mit der Verarbeitung meiner Daten wie oben beschrieben einverstanden + +# Datei mit Erklaerungstext fuer BDSG-Klausel +bdsgfile = bdsgtext.cfg + +# Rechter Rand fuer einige Bildschirmausgaben (Terminalbreite) +rightmargin = 72 + +# Regular Expression fuer Erkennung eines gueltigen Realnamens +name_re = [-a-zA-ZäöüáàâéèêíìîóòôúùûÄÖÜÁÁÂÉÈÊÍÌÎÓÒÔÚÙÛß]{2,} +.*[a-zA-ZäöüáàâéèêíìîóòôúùûÄÖÜÁÁÂÉÈÊÍÌÎÓÒÔÚÙÛß]{2,} + +# RegExp fuer JA-Stimmen (case-insensitive) +# Standardmaessig wird J, JA, FUER und DAFUER erkannt +ja_stimme = (J\s*A|J|(D\s*A\s*)?F\s*U\s*E\s*R) + +# RegExp fuer NEIN-Stimmen (case-insensitive) +# Standardmaessig wird N, NEIN, GEGEN und DAGEGEN erkannt +nein_stimme = (N\s*E\s*I\s*N|N|(D\s*A\s*)?G\s*E\s*G\s*E\s*N) + +# RegExp fuer ENTHALTUNG (case-insensitive) +enth_stimme = (E|E\s*N\s*T\s*H\s*A\s*L\s*T\s*U\s*N\s*G) + +# RegExp fuer ANNULLIERUNG (case-insensitive) +# Achtung, sollte auch in den Templates im Bestaetigungstext angepasst werden +ann_stimme = A\s*N\s*N\s*U\s*L\s*L\s*I\s*E\s*R\s*U\s*N\s*G + +# Template files (these files are in the template directory defined above) +tpl_mailheader = "mailheader" # generally used mail header +tpl_bouncelist = "bouncelist" # used by uvbounce.pl +tpl_result_multi = "result-multi" # used by uvcount.pl -r -m +tpl_result_single = "result-single" # used by uvcount.pl -r -o +tpl_result_prop = "result-proportional" # used by uvcount.pl -r (proportional = 1) +tpl_votes_multi = "votes-multi" # used by uvcount.pl -v (multiple groups) +tpl_votes_single = "votes-single" # used by uvcount.pl -v (single group only) +tpl_voterlist = "voterlist" # used by uvcount.pl -l (2nd CfV) +tpl_ballot = "ballot" # used by uvballot.pl (personal = 0) +tpl_ballot_request = "ballot-request" # used by uvballot.pl (personal = 1) +tpl_ballot_personal = "ballot-personal" # used by uvcfv.pl (personal = 1) +tpl_addr_reg = "address-not-registered" # used by uvvote.pl (personal = 1) +tpl_no_ballotid = "no-ballotid" # used by uvvote.pl (personal = 1) +tpl_wrong_ballotid = "wrong-ballotid" # used by uvvote.pl (personal = 1) +tpl_bdsg_error = "bdsg-error" # used by uvvote.pl (bdsg = 1) +tpl_ack_mail = "ack-mail" # used by uvvote.pl (voteack = 1) +tpl_cancelled = "cancelled" # used by uvvote.pl +tpl_invalid_account = "invalid-account" # used by uvvote.pl +tpl_invalid_name = "invalid-name" # used by uvvote.pl +tpl_multiple_votes = "multiple-votes" # used by uvvote.pl +tpl_no_ballot = "no-ballot" # used by uvvote.pl +tpl_no_votes = "no-votes" # used by uvvote.pl +tpl_rule_violated = "rule-violated" # used by uvvote.pl (c.f. usevote.rul) diff --git a/usevote.rul b/usevote.rul new file mode 100644 index 0000000..4d9a6c6 --- /dev/null +++ b/usevote.rul @@ -0,0 +1,68 @@ +# UseVote (c) 1993,94 Ron Dippold, alle Rechte vorbehalten +# uebersetzt von Frederik Ramm +# +# Mit dieser Datei koennen spezielle Regeln fuer eine gueltige Stimmabgabe +# bei Mehrgruppenabstimmungen erstellt werden. +# Beispiel: Eine Gruppe soll aufgeteilt werden, und Du willst erzwingen, +# dass jemand, der fuer eine der neuen Untergruppen stimmt, auch fuer +# die .misc-Gruppe stimmen muss. Man kann eine Menge komplexe Sachen hier- +# mit machen, wenn man Programmierer ist :-) +# +# Das allgemeine Format sieht so aus: +# (a) alles, was mit # anfaengt, ist ein Kommentar. +# (b) "echte" Regeln sehen etwa so aus (natuerlich ohne #): +# if .jjjjj then J..... +# +# Eine Regel beginnt immer mit "if", und danach folgen eine Anzahl Symbole; +# diese Anzahl muss gleich der Anzahl der Gruppen sein, ueber die abgestimmt +# wird. +# Oben geht es also um eine Abstimmung ueber sechs Gruppen. +# Die Symbole zwischen 'if' und 'then' geben an, welche Bedingungen erfuellt +# sein muessen, damit das System auch die Bedingungen hinter then prueft. +# +# Nach 'then' folgen nochmal so viele Symbole, die angeben, welche Bedin- +# gungen erfuellt sein muessen, falls die Bedingungen zwischen 'if' und +# 'then' erfuellt waren. Jeder Wahlschein, auf den das nicht zutrifft, ist +# ungueltig. +# +# Folgende Symbole sind erlaubt: +# J eine JA-Stimme +# N eine NEIN-Stimme +# E eine Enthaltung +# S eine JA- oder NEIN-Stimme +# H eine Enthaltung oder JA-Stimme +# I eine Enthaltung oder NEIN-Stimme +# . egal (Ja, nein oder Enthaltung) +# j eine oder mehrere der markierten Gruppen hat JA-Stimme +# n "" "" "" "" "" "" "" NEIN-Stimme +# e "" "" "" "" "" "" "" Enthaltung +# s "" "" "" "" "" "" "" Ja- oder Nein-Stimme +# h "" "" "" "" "" "" "" Enthaltung oder Ja-Stimme +# i "" "" "" "" "" "" "" Enthaltung oder Nein-Stimme +# +# Alles klar? Jede Stimme wird mit den Symbolen verglichen, und wenn alle +# Kriterien passen, ist der Ausdruck wahr. Wenn der erste Ausdruck (if x) +# wahr ist, muss auch der zeite (then y) wahr sein, sonst ist der Wahlschein +# ungueltig. Ein Beispiel: +# if .jjjjj then J..... +# Das heisst: Wenn der Waehler fuer *irgendeine* ausser der ersten Gruppe mit +# JA stimmt, dann *muss* er JA fuer die erste Gruppe stimmen, oder das ganze +# ist ungueltig. +# Das koennte z.B. eine Gruppenaufteilung sein, wo fuer die erste (die .misc)- +# Gruppe gestimmt werden *muss*, wenn fuer eine der anderen gestimmt wird. +# +# Hier noch ein Beispiel: +# if S... then .ss. +# if .S.. then ..E. +# if ..S. then .E.. +# Diese Regeln sagen: Wer fuer die erste Gruppe abstimmt, der muss auch fuer +# die zweite und dritte Gruppe abstimmen - egal wie. Ausserdem muss er (Re- +# geln 2 und 3) sich bei 3 enthalten, wenn er bei 2 eine Stimme abgibt und +# umgekehrt. Die vierte Gruppe wird hier gar nicht betroffen. +# +# Also: es gibt einfache Regeln, aber es sind auch sehr komplizierte Kom- +# binationen denkar. Das Programm macht alles mit... die Frage ist, ob +# es die Waehler tun :-) +# +# Fuege Deine Regeln hier an. Mit "uvvote.pl -t" kannst Du sie testen. + diff --git a/uvballot.pl b/uvballot.pl new file mode 100644 index 0000000..4085a7f --- /dev/null +++ b/uvballot.pl @@ -0,0 +1,116 @@ +#!/usr/bin/perl -w + +############################################################################### +# UseVoteGer 4.09 Wahlscheingenerierung +# (c) 2001-2005 Marc Langer +# +# This script package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by the +# Free Software Foundation. +# +# Use this script to create the ballot which can be inserted into the CfV. +# Not for personal ballots (personal=1 in usevote.cfg), use uvcfv.pl instead. +# +# Many thanks to: +# - Ron Dippold (Usevote 3.0, 1993/94) +# - Frederik Ramm (German translation, 1994) +# - Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99) +# - Cornell Binder for some good advice and code fragments +# +# This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were +# written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented! +############################################################################### + +use strict; +use Getopt::Long; +use Text::Wrap qw(wrap $columns); +use FindBin qw($Bin); +use lib $Bin; +use UVconfig; +use UVtemplate; + +my %opt_ctl = (); + +print STDERR "\n$usevote_version Wahlscheingenerierung - (c) 2001-2005 Marc Langer\n\n"; + +# unknown parameters remain in @ARGV (for "help") +Getopt::Long::Configure(qw(pass_through bundling)); + +# Put known parameters in %opt_ctl +GetOptions(\%opt_ctl, qw(t|template c|config-file=s)); + +# Additional parameters or invalid options? Show help and exit. +help() if (@ARGV); + +# Get name auf config file (default: usevote.cfg) and read it +my $cfgfile = $opt_ctl{c} || "usevote.cfg"; +UVconfig::read_config($cfgfile); + +# Set columns for Text::Wrap +$columns = $config{rightmargin}; + +if ($config{personal}) { + print_ballot_personal(); +} else { + print_ballot(); +} + +exit 0; + + +############################################################################## +# Print out a proper ballot # +############################################################################## + +sub print_ballot { + + my $template = UVtemplate->new(); + + $template->setKey('votename' => $config{votename}); + $template->setKey('nametext' => $config{nametext}); + $template->setKey('bdsg' => $config{bdsg}); + $template->setKey('bdsgtext' => $config{bdsgtext}); + $template->setKey('bdsginfo' => $config{bdsginfo}); + + for (my $n=0; $n<@groups; $n++) { + $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]); + } + + print $template->processTemplate($config{'tpl_ballot'}); + +} + + +############################################################################## +# Generate a ballot request (if personalized ballots are activated) # +############################################################################## + +sub print_ballot_personal { + my $template = UVtemplate->new(); + $template->setKey('mailaddress' => $config{mailfrom}); + print $template->processTemplate($config{'tpl_ballot_request'}); +} + + +############################################################################## +# Print help text (options and syntax) on -h or --help # +############################################################################## + +sub help { + print STDERR < +# +# This script package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by the +# Free Software Foundation. +# +# Use this script to process bounce messages and generate a list of +# undeliverable voter adresses. +# +# Many thanks to: +# - Ron Dippold (Usevote 3.0, 1993/94) +# - Frederik Ramm (German translation, 1994) +# - Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99) +# - Cornell Binder for some good advice and code fragments +# +# This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were +# written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented! +############################################################################### + +use strict; +use Getopt::Long; +use FindBin qw($Bin); +use lib $Bin; +use UVconfig; +use UVreadmail; +use UVtemplate; + +my %opt_ctl = (); +my %bounces = (); +my $pop3 = 0; + +print STDERR "\n$usevote_version Bounce-Verarbeitung - (c) 2001-2005 Marc Langer\n\n"; + +# unknown parameters remain in @ARGV (for "help") +Getopt::Long::Configure(qw(pass_through bundling)); + +# Put known parameters in %opt_ctl +GetOptions(\%opt_ctl, qw(h|help f|file config-file=s c=s)); + +# Get name auf config file (default: usevote.cfg) and read it +my $cfgfile = $opt_ctl{'config-file'} || $opt_ctl{c} || "usevote.cfg"; +UVconfig::read_config($cfgfile); + +# check POP3 settings in usevote.cfg and combination with -f parameter +$pop3 = 1 if ($config{pop3} && !$opt_ctl{f}); + +# Additional parameters or invalid options? Show help and exit. +help() if ($opt_ctl{h} || !(@ARGV || $pop3)); + +# check for lock file +if (-e $config{lockfile}) { + my $lockfile = $config{lockfile}; + + # don't delete lockfile in END block ;-) + $config{lockfile} = ''; + + # exit + die UVmessage::get("ERR_LOCK", (FILE=>$lockfile)) . "\n\n"; +} + +# safe exit (delete lockfile) +$SIG{QUIT} = 'sighandler'; +$SIG{INT} = 'sighandler'; +$SIG{KILL} = 'sighandler'; +$SIG{TERM} = 'sighandler'; +$SIG{HUP} = 'sighandler'; + +# create lock file +open (LOCKFILE, ">$config{lockfile}"); +close (LOCKFILE); + +# read and process mails +# for each mail pass a reference to the sub to be called + +if ($pop3) { + unless (-d $config{archivedir}) { + mkdir ($config{archivedir}, 0700) + or die UVmessage::get("ERR_MKDIR", (DIR => $config{archivedir})) . "$!\n\n"; + } + + # mails are saved in file + # normally unixtime is sufficient for a unique file name, else append PID + my $ext = time; + + opendir (ARCHIVE, $config{archivedir}); + my @fertigfiles = readdir (ARCHIVE); + closedir (ARCHIVE); + + # append PID if file name already exists + $ext .= "-$$" if (grep (/$ext/, @fertigfiles)); + + my $file = "$config{archivedir}/bounces-" . $ext; + UVreadmail::process($file, \&process_bounce, 2); # 2 = POP3 + +} else { + foreach my $file (@ARGV) { + UVreadmail::process($file, \&process_bounce, 3); # 3 = existing file + } +} + +my $template = UVtemplate->new(); + +foreach my $address (sort keys %bounces) { + my $name = $bounces{$address}; + my $request = 0; + my $text; + if ($name eq '***request***') { + $name = ''; + $text = UVmessage::get ("BOUNCE_BALLOT") . "\n"; + } else { + $text = UVmessage::get ("BOUNCE_ACKMAIL") . "\n"; + } + $name = ' ' unless($name); + $template->addListItem('bounces', name=>$name, mail=>$address, bouncetext=>$text); +} + +print $template->processTemplate($config{'tpl_bouncelist'}); +exit 0; + + +############################################################################## +# Evaluate a bounce # +# This sub is called from UVreadmail::process() for every mail # +# Parameters: voter address and name, date header (strings) # +# complete header and body (references to strings) # +############################################################################## + +sub process_bounce { + # last element of @_ is the body, other stuff not needed here + my $body = pop; + my ($address, $name); + + # search body for voter name and address + if ($$body =~ /$config{addresstext}\s+(\S+@\S+)/) { + $address = $1; + if ($$body =~ /$config{nametext2}\s+(.*)$/m) { + $name = $1; + $bounces{$address} = $name; + } elsif ($$body =~ /$config{nametext}/) { + # Text from this config option does only appear in ballots, + # not in acknowledge mails. So this has to be a bounced ballot request + $bounces{$address} = '***request***'; + } else { + $bounces{$address} = ''; + } + } +} + + +END { + # delete lockfile + unlink $config{lockfile} if ($config{lockfile}); +} + +sub sighandler { + my ($sig) = @_; + die "\n\nSIG$sig: deleting lockfile and exiting\n\n"; +} + + +############################################################################## +# Print help text (options and syntax) on -h or --help # +############################################################################## + +sub help { + print < +# +# This script package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by the +# Free Software Foundation. +# +# Use this script to read mails and send back a CfV with unique ballot id +# +# Many thanks to: +# - Ron Dippold (Usevote 3.0, 1993/94) +# - Frederik Ramm (German translation, 1994) +# - Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99) +# - Cornell Binder for some good advice and code fragments +# +# This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were +# written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented! +############################################################################### + +use strict; +use Getopt::Long; +use Digest::MD5 qw(md5_hex); +use Text::Wrap qw(wrap $columns); +use FindBin qw($Bin); +use lib $Bin; +use UVconfig; +use UVmenu; +use UVmessage; +use UVreadmail; +use UVsendmail; +use UVtemplate; + +my %opt_ctl = (); + +print "\n$usevote_version Personalisierte Wahlscheine - (c) 2001-2005 Marc Langer\n\n"; + +# unknown parameters remain in @ARGV (for "help") +Getopt::Long::Configure(qw(pass_through bundling)); + +# Put known parameters in %opt_ctl +GetOptions(\%opt_ctl, qw(test t config-file=s c=s)); + +# test mode? (default: no) +my $test_only = $opt_ctl{test} || $opt_ctl{t} || 0; + +# # Additional parameters or invalid options? Show help and exit. +help() if (@ARGV); + +# Get name auf config file (default: usevote.cfg) and read it +my $cfgfile = $opt_ctl{'config-file'} || $opt_ctl{c} || "usevote.cfg"; +UVconfig::read_config($cfgfile); + +# Set columns for Text::Wrap +$columns = $config{rightmargin}; + +# read list of suspicious mail addresses from file +my @bad_addr = UVconfig::read_badaddr(); + +# exit if option "personal=1" in config file not set +unless ($config{personal}) { + die wrap ('', '', UVmessage::get("ERR_NOTPERSONAL", (CFGFILE => $cfgfile))) . "\n\n"; +} + +# option -t used? +if ($test_only) { + print_ballot(); + exit 0; +} + +# check for lock file +if (-e $config{lockfile}) { + my $lockfile = $config{lockfile}; + + # don't delete lockfile in END block ;-) + $config{lockfile} = ''; + + # exit + die UVmessage::get("ERR_LOCK", (FILE=>$lockfile)) . "\n\n"; +} + +# safe exit (delete lockfile) +$SIG{QUIT} = 'sighandler'; +$SIG{INT} = 'sighandler'; +$SIG{KILL} = 'sighandler'; +$SIG{TERM} = 'sighandler'; +$SIG{HUP} = 'sighandler'; + +# create lock file +open (LOCKFILE, ">$config{lockfile}"); +close (LOCKFILE); + +# check for tmp directory and domail file +unless (-d $config{tmpdir}) { + mkdir ($config{tmpdir}, 0700) + or die UVmessage::get("ERR_MKDIR", (DIR => $config{tmpdir})) . "\n$!\n\n"; +} + +# generate filename for mail archive +# normally unixtime is sufficient, if it is not unique append a number +my $file = my $base = "anforderung-" . time(); +my $count = 0; +while ($count<1000 && (-e "$config{archivedir}/$file" || -e "$config{tmpdir}/$file")) { + $file = "$base-" . ++$count; +} +die UVmessage::get("ERR_FILE_CREATION") . "\n\n" if ($count == 1000); + +unless ($config{pop3}) { + rename ($config{requestfile}, "$config{tmpdir}/$file") + or die UVmessage::get("ERR_RENAME_MAILFILE") . "$!\n\n"; +} + +# wait, so that current mail deliveries can finalize +sleep 2; + +# initiliaze random number generator +srand; + +# read votes and process them +# for each mail pass a reference to the sub to be called +# The third parameter "1" shows that it is called from uvcfv.pl +$count = UVreadmail::process("$config{tmpdir}/$file", \&process_request, 1); +print "\n", UVmessage::get("CFV_NUMBER", (COUNT => $count)), "\n\n"; +UVsendmail::send(); + +print UVmessage::get("INFO_TIDY_UP") . "\n\n"; +rename("$config{tmpdir}/$file", "$config{archivedir}/$file"); +chmod (0400, "$config{archivedir}/$file"); + +exit 0; + + +############################################################################## +# Evaluate a ballot request # +# Called from UVreadmail::process() for every mail # +# Parameters: Voter address and name, date header of vote mail (strings), # +# complete header and body (references to Strings) # +############################################################################## + +sub process_request { + my ($voter_addr, $voter_name, $h_date, $entity, $body) = @_; + + my @header = split(/\n/, $entity->stringify_header); + my $head = $entity->head; + my $msgid = $head->get('Message-ID'); + chomp($msgid) if ($msgid); + + # found address? + if ($voter_addr) { + # check for suspicious addresses + foreach my $element (@bad_addr) { + if ($voter_addr =~ /^$element/) { + my (@votes, @set, $ballot_id); # irrelevant, but necessary for UVmenu::menu() + my @errors = ('SuspiciousAccountBallot'); + my $res = UVmenu::menu(\@votes, \@header, $body, \$voter_addr, \$voter_name, \$ballot_id, \@set, \@errors); + + # "Ignore": don't deliver a ballot + return 0 if ($res eq 'i'); + if (@errors) { + # send error mail if address hasn't been accepted + my $template = UVtemplate->new(); + $template->setKey('head' => $entity->stringify_header); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($config{tpl_invalid_account}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid); + return 0; + } + last; + } + } + } else { + + # no address found in mail (non-RFC compliant?) + my (@votes, @set, $ballot_id); # irrelevant, but necessary for UVmenu::menu() + my @errors = ('InvalidAddressBallot'); + my $res = UVmenu::menu(\@votes, \@header, $body, \$voter_addr, \$voter_name, \$ballot_id, \@set, \@errors); + + # "ignore" or address not ok: no ballot can be sent + return 0 if (@errors || $res eq 'i'); + } + + my $subject = UVmessage::get("CFV_SUBJECT"); + my $template = UVtemplate->new(); + my $ballot_id = ""; + + #if ($ballot_id ne $ids{$voter_addr}) { + if ($ids{$voter_addr}) { + $ballot_id = $ids{$voter_addr}; + $template->setKey('alreadysent' => 1) if ($ballot_id = $ids{$voter_addr}); + } else { + # generate new ballot id from the MD5 sum of header, body and a random value + $ballot_id = md5_hex($entity->stringify_header . $body . rand 65535); + $ids{$voter_addr} = $ballot_id; + + # write ballot id to file + open(IDFILE, ">>$config{idfile}") + or die UVmessage::get("CFV_ERRWRITE", (FILE => $config{idfile})) . "\n\n"; + print IDFILE "$voter_addr $ballot_id\n"; + close(IDFILE) or die UVmessage::get("CFV_ERRCLOSE") . "\n\n"; + } + + $template->setKey('ballotid' => $ballot_id); + $template->setKey('address' => $voter_addr); + $template->setKey('bdsginfo' => $config{bdsginfo}); + + for (my $n=0; $n<@groups; $n++) { + $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]); + } + + my $msg = $template->processTemplate($config{'tpl_ballot_personal'}); + + # $config{voteaccount} is the Reply-To address: + UVsendmail::mail($voter_addr, $subject, $msg, $msgid, $config{voteaccount}); + +} + + +############################################################################## +# Print dummy personalized ballot in STDOUT for checking purposes # +# Called if command line argument -t is present # +############################################################################## + +sub print_ballot { + my $template = UVtemplate->new(); + + # generate new ballot id + my $ballot_id = md5_hex(rand 65535); + + $template->setKey('ballotid' => $ballot_id); + $template->setKey('address' => 'dummy@foo.invalid'); + $template->setKey('bdsginfo' => $config{bdsginfo}); + + for (my $n=0; $n<@groups; $n++) { + $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]); + } + + my $msg = $template->processTemplate($config{'tpl_ballot_personal'}); + + print $msg, "\n"; +} + + +############################################################################## +# Handle Signals and delete lock files when exiting # +############################################################################## + +END { + # delete lockfile + unlink $config{lockfile} if ($config{lockfile}); +} + + +sub sighandler { + my ($sig) = @_; + die "\n\nSIG$sig: deleting lockfile and exiting\n\n"; +} + + +############################################################################## +# Print help text (options and syntax) on -h or --help # +############################################################################## + +sub help { + print < +# +# This script package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by the +# Free Software Foundation. +# +# Use this script to create voter lists and results. +# +# Many thanks to: +# - Ron Dippold (Usevote 3.0, 1993/94) +# - Frederik Ramm (German translation, 1994) +# - Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99) +# - Cornell Binder for some good advice and code fragments +# +# This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were +# written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented! +############################################################################### + +use strict; +use Getopt::Long; +use Digest::MD5 qw(md5_hex); +use Date::Parse; +use FindBin qw($Bin); +use lib $Bin; +use UVconfig; +use UVmenu; +use UVmessage; +use UVtemplate; + +my %opt_ctl = (); + +print STDERR "\n$usevote_version Stimmauswertung - (c) 2001-2005 Marc Langer\n\n"; + +# unrecognized parameters remain in @ARGV (for "help") +Getopt::Long::Configure(qw(pass_through bundling)); + +# recognized parameters are written into %opt_ctl +GetOptions(\%opt_ctl, qw(l|list v|voters r|result n|nodup m|multigroup o|onegroup c|config-file=s f|result-file=s)); + +if (!$opt_ctl{r} && ($opt_ctl{m} || $opt_ctl{o})) { + print STDERR "Die Optionen -m bzw. -o koennen nur in Verbindung mit -r verwendet werden!\n\n"; + help(); # show help and exit +} elsif (@ARGV || !($opt_ctl{l} || $opt_ctl{v} || $opt_ctl{r})) { + # additional parameters passed + help(); # show help and exit +} elsif ($opt_ctl{l} && $opt_ctl{v}) { + print STDERR "Die Optionen -l und -v duerfen nicht zusammen verwendet werden!\n\n"; + help(); # show help and exit +} elsif ($opt_ctl{m} && $opt_ctl{o}) { + print STDERR "Die Optionen -m und -o duerfen nicht zusammen verwendet werden!\n\n"; + help(); # show help and exit +} + +# get config file name (default: usevote.cfg) and read it +my $cfgfile = $opt_ctl{c} || "usevote.cfg"; +UVconfig::read_config($cfgfile); + +# Overwrite result file if started with option -f +$config{resultfile} = $opt_ctl{f} if ($opt_ctl{f}); + +read_resultfile($opt_ctl{n}); + +exit 0; + + +############################################################################## +# Read result file and (optionally) sort out duplicate votes # +# Parameters: 1 if no duplicates should be deleted, else 0 # +############################################################################## + +sub read_resultfile { + my ($nodup) = @_; + my $num = 0; + my $invalid = ''; + my $inv_count = 0; + my $validcount = 0; + my $vote = {}; + my @votes = (); + my @deleted = (); + my @votecount = (); + my %vnames = (); + my %vaddr = (); + my %lists = (J => '', N => '', E => ''); # for one-group format + my $list = ''; # for multiple-group format + my %varname = (J => 'yes', N => 'no', E => 'abstain'); + + # Initialization of the sum array + for (my $group=0; $group<@groups; $group++) { + $votecount[$group]->{J} = 0; + $votecount[$group]->{N} = 0; + $votecount[$group]->{E} = 0; + } + + open(FILE, "<$config{resultfile}") + or die UVmessage::get("COUNT_ERR_OPEN", (FILE=>$config{resultfile})) . "\n\n"; + + # Read file + while() { + chomp; + $num++; + + unless (/^(\w): (.*)$/) { + print STDERR UVmessage::get("COUNT_ERR_RESULT", + (FILE=>$config{resultfile}, LINE=>$num)) . "\n"; + next; + } + + my $field = $1; + my $content = $2; + $vote->{$field} = $content; + + # End of a paragraph reached? + if ($field eq 'S') { + + # The array @votes countains references to the hashes + push (@votes, $vote); + + # For sorting and duplicate detection indexes are build from address and name. + # These are hashes containing references to an array of index numbers of + # the @votes array. + # + # Example: $vnames{'marc langer'}->[0] = 2 + # $vnames{'marc langer'}->[1] = 10 + # Meaning: $votes[2] und $votes[10] contain votes of Marc Langer + + push (@{$vnames{lc($vote->{N})}}, $#votes); + + # Conversion in lower case, so that words with an upper case first + # letter are not at the top after sorting + push (@{$vaddr{lc($vote->{A})}}, $#votes); + + # reset $vote, begin a new vote + $vote = {}; + } + } + + close(FILE); + + # delete cancelled votes + foreach my $addr (keys %vaddr) { + # Run through all votes belonging to a mail address and search for cancellation + for (my $n=0; $n<=$#{$vaddr{$addr}}; $n++) { + if ($votes[$vaddr{$addr}->[$n]]->{S} =~ /^\*/) { + # delete from array + push(@deleted, splice(@{$vaddr{$addr}}, 0, $n+1)); + $n=-1; + } + } + } + + # sort out duplicates? + unless ($nodup) { + + # search for duplicate addresses + foreach my $addr (keys %vaddr) { + + # Run through all votes belonging to a mail address. + # If one vote is deleted it has also to be deleted from the array + # so that the following addresses move up. In the other case the + # counter is incremented as long as further votes are to be compared. + + my $n=0; + while ($n<$#{$vaddr{$addr}}) { + + my $ask = 0; + + if ($votes[$vaddr{$addr}->[$n]]->{S} =~ /!/ || + $votes[$vaddr{$addr}->[$n+1]]->{S} =~ /!/) { + + # One of the votes is invalid: Ask votetaker + $ask = 1; + + } else { + + # Convert date into unixtime (str2time is located in Date::Parse) + my $date1 = str2time($votes[$vaddr{$addr}->[$n]]->{D}); + my $date2 = str2time($votes[$vaddr{$addr}->[$n+1]]->{D}); + + # compare dates + my $order = $date1 <=> $date2; + + # first date is earlier + if ($order == -1) { + push(@deleted, $vaddr{$addr}->[$n]); + # delete first element from the array + splice(@{$vaddr{$addr}}, $n, 1); + + # second date is earlier + } elsif ($order == 1) { + push(@deleted, $vaddr{$addr}->[$n+1]); + # delete second element from the array + splice(@{$vaddr{$addr}}, $n+1, 1); + + # both are equal (ask votetaker) + } else { + $ask = 1; + } + + } + + # Has votetaker to be asked? + if ($ask) { + my $default = 0; + my $res = UVmenu::dup_choice($votes[$vaddr{$addr}->[0]], + $votes[$vaddr{$addr}->[1]], + $default); + + if ($res == 1) { + push(@deleted, $vaddr{$addr}->[0]); + # delete first element from the array + splice(@{$vaddr{$addr}}, $n, 1); + + } elsif ($res == 2) { + push(@deleted, $vaddr{$addr}->[1]); + # delete second element from the array + splice(@{$vaddr{$addr}}, $n+1, 1); + + } else { + # don't delete anything: increment counter + $n++; + } + } + } + } + + # the same for equal names: + foreach my $name (keys %vnames) { + my $n = 0; + while ($n<$#{$vnames{$name}}) { + + # check if vote was already deleted by prior address sorting + if (grep(/^$vnames{$name}->[$n]$/, @deleted)) { + # delete first element from the array + splice(@{$vnames{$name}}, $n, 1); + next; + + } elsif (grep(/^$vnames{$name}->[$n+1]$/, @deleted)) { + # delete second element from the array + splice(@{$vnames{$name}}, $n+1, 1); + next; + } + + # Convert date into unixtime (str2time is located in Date::Parse) + my $date1 = str2time($votes[$vnames{$name}->[$n]]->{D}); + my $date2 = str2time($votes[$vnames{$name}->[$n+1]]->{D}); + + # Set default for menu choice to the earlier vote + my $default = ($date2 < $date1) ? 2 : 0; + + my $res = UVmenu::dup_choice($votes[$vnames{$name}->[$n]], + $votes[$vnames{$name}->[$n+1]], + $default); + + # delete first + if ($res == 1) { + push(@deleted, $vnames{$name}->[$n]); + splice(@{$vnames{$name}}, $n, 1); + + # delete second + } elsif ($res == 2) { + push(@deleted, $vnames{$name}->[$n+1]); + # delete second element from the array + splice(@{$vnames{$name}}, $n+1, 1); + + # don't delete anything: increment counter + } else { + $n++; + } + } + } + + print STDERR UVmessage::get("COUNT_DELETED", (NUM=>scalar @deleted)), "\n\n"; + } + + # Count votes and generate voter list + + my $list_tpl = UVtemplate->new(); + $list_tpl->setKey('groupcount' => scalar @groups); + + # reversed order as caption string for last column comes first + for (my $n=$#groups; $n>=0; $n--) { + $list_tpl->addListItem('groups', pos=>@groups-$n, group=>$groups[$n]); + } + + # loop through all addresses + foreach my $addr (sort keys %vaddr) { + + # loop through all votes for every address + for (my $n=0; $n<@{$vaddr{$addr}}; $n++) { + + # Ignore vote if already deleted. + # If $nodup is not set one single vote should remain + unless (grep(/^$vaddr{$addr}->[$n]$/, @deleted)) { + + # extract $vote for simplier code + my $vote = $votes[$vaddr{$addr}->[$n]]; + + # vote is invalid if there is an exclamation mark + if ($vote->{S} =~ /!/) { + $inv_count++; + } else { + # split vote string into single votes and count + my @splitvote = split(//, $vote->{S}); + if (@groups != @splitvote) { + die UVmessage::get("COUNT_ERR_GROUPCOUNT", (ADDR=>$addr, NUM1=>scalar @splitvote, + NUM2=>scalar @groups), RESULTFILE=>$config{resultfile}), "\n\n"; + } + for (my $group=0; $group<@splitvote; $group++) { + $votecount[$group]->{$splitvote[$group]}++; + } + $validcount++; + } + + if ($opt_ctl{l} || $opt_ctl{v}) { + + # vote is invalid if there is an exclamation mark + if ($vote->{S} =~ /!/) { + $list_tpl->addListItem('invalid', (name=>$vote->{N}, mail=>$vote->{A}, reason=>$vote->{S})); + + # in other cases the vote is valid: generate list of votes + } else { + + # one-group or multiple-group format? + # must use multiple-group data structure for voter list (2. CfV)! + if ($#groups || $opt_ctl{l}) { + $list_tpl->addListItem('multi', (name=>$vote->{N}, mail=>$vote->{A}, vote=>$vote->{S})); + } else { + my ($votestring) = split(//, $vote->{S}); + $list_tpl->addListItem($varname{$votestring}, (name=>$vote->{N}, mail=>$vote->{A})); + } + + } + } + } + } + } + + if ($opt_ctl{r}) { + + my $tplname; + my $result_tpl = UVtemplate->new(); + $result_tpl->setKey('votename' => $config{votename}); + $result_tpl->setKey('numvalid' => $validcount); + $result_tpl->setKey ('numinvalid', $inv_count); + + # proportional vote? + if ($config{proportional}) { + $tplname = $config{'tpl_result_prop'}; + for (my $group=0; $group<@votecount; $group++) { + # calculate conditions + my $yes = $votecount[$group]->{J}; + my $no = $votecount[$group]->{N}; + my $cond1 = eval $config{condition1}; + my $proportion = 0; + + # don't evaluate if division by zero + unless ($config{prop_formula} =~ m#.+/(.+)# && eval($1)==0) { + $proportion = eval $config{prop_formula}; + } + + # generate result line + $result_tpl->addListItem('count', (yes => $votecount[$group]->{J}, + no => $votecount[$group]->{N}, + cond1 => $cond1, + proportion => $proportion, + result => '', # must be set manually + group => $groups[$group])); + } + + } else { + # use one-group or multiple-group format? + if (@groups == 1 && (!($config{multigroup} || $opt_ctl{m}) || $opt_ctl{o})) { + $tplname = $config{'tpl_result_single'}; + my $yes = $votecount[0]->{J}; + my $no = $votecount[0]->{N}; + my $acc1 = eval $config{condition1}; + my $acc2 = eval $config{condition2}; + $result_tpl->setKey('yes' => $votecount[0]->{J}); + $result_tpl->setKey('no' => $votecount[0]->{N}); + $result_tpl->setKey('numabstain' => $votecount[0]->{E}); + $result_tpl->setKey('cond1' => $acc1); + $result_tpl->setKey('cond2' => $acc2); + + } else { + $tplname = $config{'tpl_result_multi'}; + $result_tpl->setKey('numabstain' => 0); + + for (my $group=0; $group<@votecount; $group++) { + # calculate conditions + my $yes = $votecount[$group]->{J}; + my $no = $votecount[$group]->{N}; + my $cond1 = eval $config{condition1}; + my $cond2 = eval $config{condition2}; + + # generate result line + $result_tpl->addListItem('count', (yes => $votecount[$group]->{J}, + no => $votecount[$group]->{N}, + cond1 => $cond1, + cond2 => $cond2, + result => ($cond1 && $cond2), + group => $groups[$group])); + + } + } + + $result_tpl->setKey ('numabstain', $votecount[0]->{E}) if (@votecount == 1); + } + + print $result_tpl->processTemplate($tplname); + + } + + if ($opt_ctl{v}) { + + # one-group or multiple-group format? + if ($#groups) { + print $list_tpl->processTemplate($config{'tpl_votes_multi'}); + } else { + print $list_tpl->processTemplate($config{'tpl_votes_single'}); + } + + } elsif ($opt_ctl{l}) { + print $list_tpl->processTemplate($config{'tpl_voterlist'}); + } + +} + + +############################################################################## +# Print help text (options and syntax) on -h or --help # +############################################################################## + +sub help { + print STDERR < +# +# This script package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Public License as published by the +# Free Software Foundation. +# +# The script reads usenet vote ballots from mailbox files. The format +# can be set by changing the option "mailstart". +# +# Many thanks to: +# - Ron Dippold (Usevote 3.0, 1993/94) +# - Frederik Ramm (German translation, 1994) +# - Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99) +# - Cornell Binder for some good advice and code fragments +# +# This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were +# written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented! +############################################################################### + +use strict; +use Getopt::Long; +use Text::Wrap qw(wrap $columns); +use FindBin qw($Bin); +use lib $Bin; +use UVconfig; +use UVmenu; +use UVmessage; +use UVreadmail; +use UVsendmail; +use UVrules; +use UVtemplate; + +my $clean = 0; +my %opt_ctl = (); + +print "\n$usevote_version Wahldurchfuehrung - (c) 2001-2005 Marc Langer\n\n"; + +# unknown parameters remain in @ARGV (for "help") +Getopt::Long::Configure(qw(pass_through bundling)); + +# Put known parameters in %opt_ctl +GetOptions(\%opt_ctl, qw(test t config-file=s c=s)); + +# Get name auf config file (default: usevote.cfg) and read it +my $cfgfile = $opt_ctl{'config-file'} || $opt_ctl{c} || "usevote.cfg"; + +# test mode? (default: no) +my $test_only = $opt_ctl{test} || $opt_ctl{t} || 0; + +if (@ARGV){ + # additional parameters passed + + if ($ARGV[0] eq "clean") { + $clean = 1; + } else { + # print help and exit program + help(); + } +} + +UVconfig::read_config($cfgfile, 1); # read config file, redirect errors to log +UVrules::read_rulefile(); # read rules from file + +# read list of suspicious mail addresses from file +my @bad_addr = UVconfig::read_badaddr(); + +# option -t used? +if ($test_only) { + UVconfig::test_config(); + exit 0; +} + +# check for lock file +if (-e $config{lockfile}) { + my $lockfile = $config{lockfile}; + + # don't delete lockfile in END block ;-) + $config{lockfile} = ''; + + # exit + die UVmessage::get("ERR_LOCK", (FILE=>$lockfile)) . "\n\n"; +} + +# safe exit (delete lockfile) +$SIG{QUIT} = 'sighandler'; +$SIG{INT} = 'sighandler'; +$SIG{KILL} = 'sighandler'; +$SIG{TERM} = 'sighandler'; +$SIG{HUP} = 'sighandler'; + +# create lock file +open (LOCKFILE, ">$config{lockfile}"); +close (LOCKFILE); + +# Set columns for Text::Wrap +$columns = $config{rightmargin}; + +# check for tmp and archive directory +unless (-d $config{archivedir}) { + mkdir ($config{archivedir}, 0700) + or die UVmessage::get("ERR_MKDIR", (DIR=>$config{archivedir})) . "$!\n\n"; +} + +unless (-d $config{tmpdir}) { + mkdir ($config{tmpdir}, 0700) + or die UVmessage::get("ERR_MKDIR", (DIR=>$config{tmpdir})) . "$!\n\n"; +} + +if ($clean) { + # Program has been startet with "clean" option: + # save votes and send out acknowledge mails + make_clean(); + +} else { + # normal processing + + # generate file names for result file + # normally unixtime is sufficient, if it is not unique append our PID + my $ext = time; + + opendir (TMP, $config{tmpdir}); + my @tmpfiles = readdir (DIR); + closedir (TMP); + opendir (FERTIG, $config{archivedir}); + my @fertigfiles = readdir (FERTIG); + closedir (FERTIG); + + # append PID if necessary + $ext .= "-$$" if (grep (/$ext/, @tmpfiles) || grep (/$ext/, @fertigfiles)); + + my $thisresult = "ergebnis-" . $ext; + my $thisvotes = "stimmen-" . $ext; + + # POP3 not activated: rename votes file + unless ($config{pop3}) { + print UVmessage::get("VOTE_RENAMING_MAILBOX"), "\n"; + rename ($config{votefile}, "$config{tmpdir}/$thisvotes") + or die UVmessage::get("ERR_RENAME_MAILFILE") . "$!\n\n"; + + # wait, so that current mail deliveries can finalize + sleep 2; + } + + # open results file + open (RESULT, ">>$config{tmpdir}/$thisresult") + or die UVmessage::get("VOTE_WRITE_RESULTS", (FILE=>$thisresult)) . "\n\n"; + + # read votes and process them + # for each mail pass a reference to the sub to be called + my $count = UVreadmail::process("$config{tmpdir}/$thisvotes", \&process_vote, 0); + + close (RESULT) + or print STDERR UVmessage::get("VOTE_CLOSE_RESULTS", (FILE=>$thisresult)) . "\n"; + + # no mails: exit here + unless ($count) { + print UVmessage::get("VOTE_NO_VOTES") . "\n\n"; + exit 0; + } + + if ($config{onestep}) { + # everything should be done in one step + print "\n" . UVmessage::get("VOTE_NUM_VOTES", (COUNT=>$count)) . "\n"; + make_clean(); + + } else { + + print "\n", UVmessage::get("VOTE_NOT_SAVED", (COUNT=>$count)), "\n", + wrap('', '', UVmessage::get("VOTE_FIRSTRUN")), "\n\n"; + } +} + +exit 0; + +END { + close (STDERR); + + # delete lockfile + unlink $config{lockfile} if ($config{lockfile}); + + if (-s $config{errorfile}) { + # errors ocurred + print '*' x $config{rightmargin}, "\n", + UVmessage::get("VOTE_ERRORS",(FILE => $config{errorfile})), "\n", + '*' x $config{rightmargin}, "\n\n"; + open (ERRFILE, "<$config{errorfile}"); + print ; + close (ERRFILE); + print "\n"; + } else { + unlink ($config{errorfile}); + } +} + + +sub sighandler { + my ($sig) = @_; + die "\n\nSIG$sig: deleting lockfile and exiting\n\n"; +} + + +############################################################################## +# Evaluation of a vote mail # +# Called from UVreadmail::process() for each mail. # +# Parameters: voter address and name, date header of the vote mail (strings) # +# complete header (reference to array), body (ref. to strings) # +############################################################################## + +sub process_vote { + my ($voter_addr, $voter_name, $h_date, $entity, $body) = @_; + + my @header = split(/\n/, $entity->stringify_header); + my $head = $entity->head; + my $msgid = $head->get('Message-ID'); + chomp($msgid) if ($msgid); + + my @votes = (); # the votes + my @set; # interactively changed fields + my @errors = (); # recognized errors (show menu for manual action) + my $onevote = 0; # 0=no votes, 1=everything OK, 2=vote cancelled + my $voteerror = ""; # error message in case of invalid vote + my $ballot_id = ""; # ballot id (German: Wahlscheinkennung) + + # found address? + if ($voter_addr) { + # search for suspicious addresses + foreach my $element (@bad_addr) { + if ($voter_addr =~ /^$element/) { + push (@errors, 'SuspiciousAccount'); + last; + } + } + } else { + # found no address in mail (perhaps violates RFC?) + push (@errors, 'InvalidAddress'); + } + + # personalized ballots? + if ($config{personal}) { + if ($$body =~ /$config{ballotidtext}\s+([a-z0-9]+)/) { + $ballot_id = $1; + # Address registered? ($ids is set in UVconfig.pm) + if ($ids{$voter_addr}) { + push (@errors, 'WrongBallotID') if ($ids{$voter_addr} ne $ballot_id); + } else { + push (@errors, 'AddressNotRegistered'); + } + } else { + push (@errors, 'NoBallotID'); + } + } + + # evaluate vote strings + for (my $n=0; $n<@groups; $n++) { + + # counter starts at 1 in ballot + my $votenum = $n+1; + my $vote = ""; + + # a line looks like this: #1 [ VOTE ] Group + # matching only on number and vote, because of line breaks likely + # inserted by mail programs + + # duplicate vote? + if ($$body =~ /#$votenum\W*?\[\s*?(\w+)\s*?\].+?#$votenum\W*?\[\s*?(\w+)\s*?\]/s) { + push (@errors, "DuplicateVote") if ($1 ne $2); + } + + # this matches on a single appearance: + if ($$body =~ /#$votenum\W*?\[(.+)\]/) { + # one or more vote strings were found + $onevote = 1; + my $votestring = $1; + if ($votestring =~ /^\W*$config{ja_stimme}\W*$/i) { + $vote = "J"; + } elsif ($votestring =~ /^\W*$config{nein_stimme}\W*$/i) { + $vote = "N"; + } elsif ($votestring =~ /^\W*$config{enth_stimme}\W*$/i) { + $vote = "E"; + } elsif ($votestring =~ /^\s*$/) { + # nothing has been entered between the [ ] + $vote = "E"; + } elsif ($votestring =~ /^\W*$config{ann_stimme}\W*$/i) { + $vote = "A"; + $onevote = 2; # Cancelled vote: set $onevote to 2 + } elsif (!$votes[$n]) { + # vote not recognized + $vote = "E"; + push (@errors, 'UnrecognizedVote #' . $votenum . "#$votestring"); + } + push (@votes, $vote); + } else { + # vote not found + push (@votes, 'E'); + push (@errors, 'UnrecognizedVote #' . $votenum . '#(keine Stimmabgabe fuer "' + . $groups[$n] . '" gefunden)'); + } + } + + if ($onevote == 0) { + push (@errors, "NoVote") unless ($onevote); + } elsif ($onevote == 1) { + # check rules + my $rule = UVrules::rule_check(\@votes); + push (@errors, "ViolatedRule #$rule") if ($rule); + } else { + # cancelled vote: replace all votes with an A + @votes = split(//, 'A' x scalar @votes); + } + + # Evaluate Data Protection Law clause (not on cancelled votes) + if ($config{bdsg} && $onevote<2) { + + # Text in ballot complete and clause accepted? + # Should read like this: #a [ STIMME ] Text + # (Text is configurable in usevote.cfg) + unless ($$body =~ /$bdsg_regexp/s && + $$body =~ /#a\W*?\[\W*?$config{ja_stimme}\W*?\]\W*?$bdsg2_regexp/is) { + + push (@errors, 'InvalidBDSG'); + } + } + + # Name in body? + if ($$body =~ /($config{nametext}|$config{nametext2})( |\t)*(\S.+?)$/m) { + $voter_name = $3; + $voter_name =~ s/^\s+//; # strip leading spaces + $voter_name =~ s/\s+$//; # strip trailing spaces + } + + if ($voter_name) { + # Name invalid? + push (@errors, 'InvalidName') unless ($voter_name =~ /$config{name_re}/); + } else { + # no name found: + push (@errors, 'NoName') unless ($voter_name); + } + + # Errors encountered? + if (@errors) { + my $res = UVmenu::menu(\@votes, \@header, $body, \$voter_addr, \$voter_name, + \$ballot_id, \@set, \@errors); + return 0 if ($res eq 'i'); # "Ignore": Ignore vote, don't save + + my $tpl; + + # Check Ballot ID stuff + if ($config{personal}) { + if ($ballot_id) { + if ($ids{$voter_addr}) { + if ($ids{$voter_addr} ne $ballot_id) { + $voteerror = UVmessage::get("VOTE_INVALID_BALLOTID"); + $tpl = $config{tpl_wrong_ballotid}; + } + } else { + $voteerror = UVmessage::get("VOTE_UNREGISTERED_ADDRESS"); + $tpl = $config{tpl_addr_reg}; + } + } else { + $voteerror = UVmessage::get("VOTE_MISSING_BALLOTID"); + $tpl = $config{tpl_no_ballotid}; + } + + # generate error mail (if error occurred) + if ($tpl) { + my $template = UVtemplate->new(); + $template->setKey('head' => $entity->stringify_header); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($tpl); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } + } + } + + # Check rules and send error mail unless rule violation was ignored in the use menu + # or another error was detected + if (grep(/ViolatedRule/, @errors) && !$voteerror && (my $rule = UVrules::rule_check(\@votes))) { + $voteerror = UVmessage::get("VOTE_VIOLATED_RULE", (RULE=>$rule)); + my $template = UVtemplate->new(); + $template->setKey('body' => $$body); + $template->setKey('rules' => UVrules::rule_print($rule-1)); + my $msg = $template->processTemplate($config{tpl_rule_violated}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } + + if (!$voteerror && @errors) { + + # turn errors array into hash + + my %error; + foreach my $error (@errors) { + $error{$error} = 1; + } + + # Check uncorrected errors + if ($error{InvalidBDSG}) { + my $template = UVtemplate->new(); + my $msg = $template->processTemplate($config{tpl_bdsg_error}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + return 0; + } elsif ($error{NoVote}) { + $voteerror = UVmessage::get("VOTE_NO_VOTES"); + my $template = UVtemplate->new(); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($config{tpl_no_votes}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } elsif ($error{SuspiciousAccount}) { + $voteerror = UVmessage::get("VOTE_INVALID_ACCOUNT"); + my $template = UVtemplate->new(); + $template->setKey('head' => $entity->stringify_header); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($config{tpl_invalid_account}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } elsif ($error{InvalidAddress}) { + $voteerror = UVmessage::get("VOTE_INVALID_ADDRESS"); + } elsif ($error{InvalidName}) { + $voteerror = UVmessage::get("VOTE_INVALID_REALNAME"); + my $template = UVtemplate->new(); + $template->setKey('head' => $entity->stringify_header); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($config{tpl_invalid_name}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } elsif ($error{DuplicateVote}) { + $voteerror = UVmessage::get("VOTE_DUPLICATES"); + my $template = UVtemplate->new(); + $template->setKey('head' => $entity->stringify_header); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($config{tpl_multiple_votes}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } + } + + # check voter name + unless ($voter_name || $voteerror) { + $voteerror = UVmessage::get("VOTE_MISSING_NAME"); + my $template = UVtemplate->new(); + $template->setKey('head' => $entity->stringify_header); + $template->setKey('body' => $$body); + my $msg = $template->processTemplate($config{tpl_invalid_name}); + UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid) if ($config{voteack}); + } + + # set mark for cancelled vote + $onevote = 2 if ($votes[0] eq 'A'); + + # create comment line for result file + my $comment; + if ($config{personal}) { + # Personalized Ballots: insert ballot id + $comment = "($ballot_id)"; + } else { + $comment = "()"; + } + + if (@set) { + $comment .= ' '.UVmessage::get("VOTE_FILE_COMMENT", (FIELDS => join(', ', @set))); + } + + # write result file + print RESULT "A: $voter_addr\n"; + print RESULT "N: $voter_name\n"; + print RESULT "D: $h_date\n"; + print RESULT "K: $comment\n"; + + # invalid vote? + if ($voteerror) { + print RESULT "S: ! $voteerror\n"; + + # cancelled vote? + } elsif ($onevote == 2) { + print RESULT "S: * Annulliert\n"; + + if ($config{voteack}) { + # send cancellation acknowledge + my $template = UVtemplate->new(); + my $msg = $template->processTemplate($config{tpl_cancelled}); + UVsendmail::mail($voter_addr, "Bestaetigung", $msg, $msgid); + } + + } else { + print RESULT "S: ", join ("", @votes), "\n"; + + # send acknowledge mail? + if ($config{voteack}) { + + my $template = UVtemplate->new(); + $template->setKey(ballotid => $ballot_id); + $template->setKey(address => $voter_addr); + $template->setKey(name => $voter_name); + + for (my $n=0; $n<@groups; $n++) { + my $vote = $votes[$n]; + $vote =~ s/^J$/JA/; + $vote =~ s/^N$/NEIN/; + $vote =~ s/^E$/ENTHALTUNG/; + $template->addListItem('groups', pos=>$n+1, vote=>$vote, group=>$groups[$n]); + } + + my $msg = $template->processTemplate($config{'tpl_ack_mail'}); + UVsendmail::mail($voter_addr, "Bestaetigung", $msg, $msgid); + } + } +} + + +############################################################################## +# Send out acknowledge mails and tidy up (we're called as "uvvote.pl clean") # +############################################################################## + +sub make_clean { + + # send mails + UVsendmail::send(); + + print UVmessage::get("INFO_TIDY_UP"), "\n"; + + # search unprocessed files + opendir (DIR, $config{tmpdir}); + my @files = readdir DIR; + closedir (DIR); + + my @resultfiles = grep (/^ergebnis-/, @files); + my @votefiles = grep (/^stimmen-/, @files); + + unless (@resultfiles) { + print wrap('', '', UVmessage::get("VOTE_NO_NEW_RESULTS")), "\n\n"; + return 0; + } + + foreach my $thisresult (@resultfiles) { + chmod (0400, "$config{tmpdir}/$thisresult"); + rename "$config{tmpdir}/$thisresult", "$config{archivedir}/$thisresult" + or die UVmessage::get("VOTE_MOVE_RESULTFILE", (FILE=>$thisresult)) . "$!\n\n"; + } + + foreach my $thisvotes (@votefiles) { + chmod (0400, "$config{tmpdir}/$thisvotes"); + rename "$config{tmpdir}/$thisvotes", "$config{archivedir}/$thisvotes" + or die UVmessage::get("VOTE_MOVE_VOTEFILE", (FILE=>$thisvotes)) . "$!\n\n"; + } + + print UVmessage::get("VOTE_CREATING_RESULTS", (FILENAME=>$config{resultfile})), "\n"; + + # search all result files + opendir (DIR, "$config{archivedir}/"); + @files = grep (/^ergebnis-/, readdir (DIR)); + closedir (DIR); + + # Create complete result from all single result files. + # The resulting file (ergebnis.alle) is overwritten as there could have been + # made changes in the single result files + open(RESULT, ">$config{resultfile}"); + foreach my $file (sort @files) { + open(THISRESULT, "<$config{archivedir}/$file"); + print RESULT join('', ); + close(THISRESULT); + } + close(RESULT); + + print "\n"; + +} + + +############################################################################## +# Print help text (options and syntax) on -h or --help # +############################################################################## + +sub help { + print <