3 ###############################################################################
4 # UseVoteGer 4.09 Personalisierte Wahlscheine
5 # (c) 2001-2005 Marc Langer <uv@marclanger.de>
7 # This script package is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Public License as published by the
9 # Free Software Foundation.
11 # Use this script to read mails and send back a CfV with unique ballot id
14 # - Ron Dippold (Usevote 3.0, 1993/94)
15 # - Frederik Ramm (German translation, 1994)
16 # - Wolfgang Behrens (UseVoteGer 3.1, based on Frederik's translation, 1998/99)
17 # - Cornell Binder for some good advice and code fragments
19 # This is a complete rewrite of UseVoteGer 3.1 in Perl (former versions were
20 # written in C). Not all functions of Usevote/UseVoteGer 3.x are implemented!
21 ###############################################################################
25 use Digest::MD5 qw(md5_hex);
26 use Text::Wrap qw(wrap $columns);
38 print "\n$usevote_version Personalisierte Wahlscheine - (c) 2001-2005 Marc Langer\n\n";
40 # unknown parameters remain in @ARGV (for "help")
41 Getopt::Long::Configure(qw(pass_through bundling));
43 # Put known parameters in %opt_ctl
44 GetOptions(\%opt_ctl, qw(test t config-file=s c=s));
46 # test mode? (default: no)
47 my $test_only = $opt_ctl{test} || $opt_ctl{t} || 0;
49 # # Additional parameters or invalid options? Show help and exit.
52 # Get name auf config file (default: usevote.cfg) and read it
53 my $cfgfile = $opt_ctl{'config-file'} || $opt_ctl{c} || "usevote.cfg";
54 UVconfig::read_config($cfgfile);
56 # Set columns for Text::Wrap
57 $columns = $config{rightmargin};
59 # read list of suspicious mail addresses from file
60 my @bad_addr = UVconfig::read_badaddr();
62 # exit if option "personal=1" in config file not set
63 unless ($config{personal}) {
64 die wrap ('', '', UVmessage::get("ERR_NOTPERSONAL", (CFGFILE => $cfgfile))) . "\n\n";
74 if (-e $config{lockfile}) {
75 my $lockfile = $config{lockfile};
77 # don't delete lockfile in END block ;-)
78 $config{lockfile} = '';
81 die UVmessage::get("ERR_LOCK", (FILE=>$lockfile)) . "\n\n";
84 # safe exit (delete lockfile)
85 $SIG{QUIT} = 'sighandler';
86 $SIG{INT} = 'sighandler';
87 $SIG{KILL} = 'sighandler';
88 $SIG{TERM} = 'sighandler';
89 $SIG{HUP} = 'sighandler';
92 open (LOCKFILE, ">$config{lockfile}");
95 # check for tmp directory and domail file
96 unless (-d $config{tmpdir}) {
97 mkdir ($config{tmpdir}, 0700)
98 or die UVmessage::get("ERR_MKDIR", (DIR => $config{tmpdir})) . "\n$!\n\n";
101 # generate filename for mail archive
102 # normally unixtime is sufficient, if it is not unique append a number
103 my $file = my $base = "anforderung-" . time();
105 while ($count<1000 && (-e "$config{archivedir}/$file" || -e "$config{tmpdir}/$file")) {
106 $file = "$base-" . ++$count;
108 die UVmessage::get("ERR_FILE_CREATION") . "\n\n" if ($count == 1000);
110 unless ($config{pop3}) {
111 rename ($config{requestfile}, "$config{tmpdir}/$file")
112 or die UVmessage::get("ERR_RENAME_MAILFILE") . "$!\n\n";
115 # wait, so that current mail deliveries can finalize
118 # initiliaze random number generator
121 # read votes and process them
122 # for each mail pass a reference to the sub to be called
123 # The third parameter "1" shows that it is called from uvcfv.pl
124 $count = UVreadmail::process("$config{tmpdir}/$file", \&process_request, 1);
125 print "\n", UVmessage::get("CFV_NUMBER", (COUNT => $count)), "\n\n";
128 print UVmessage::get("INFO_TIDY_UP") . "\n\n";
129 rename("$config{tmpdir}/$file", "$config{archivedir}/$file");
130 chmod (0400, "$config{archivedir}/$file");
135 ##############################################################################
136 # Evaluate a ballot request #
137 # Called from UVreadmail::process() for every mail #
138 # Parameters: Voter address and name, date header of vote mail (strings), #
139 # complete header and body (references to Strings) #
140 ##############################################################################
142 sub process_request {
143 my ($voter_addr, $voter_name, $h_date, $entity, $body) = @_;
145 my @header = split(/\n/, $entity->stringify_header);
146 my $head = $entity->head;
147 my $msgid = $head->get('Message-ID');
148 chomp($msgid) if ($msgid);
152 # check for suspicious addresses
153 foreach my $element (@bad_addr) {
154 if ($voter_addr =~ /^$element/) {
155 my (@votes, @set, $ballot_id, $voting); # irrelevant, but necessary for UVmenu::menu()
156 my @errors = ('SuspiciousAccountBallot');
157 my $res = UVmenu::menu(\@votes, \@header, $body, \$voter_addr, \$voter_name, \$ballot_id, \$voting, \@set, \@errors);
159 # "Ignore": don't deliver a ballot
160 return 0 if ($res eq 'i');
162 # send error mail if address hasn't been accepted
163 my $template = UVtemplate->new();
164 $template->setKey('head' => $entity->stringify_header);
165 $template->setKey('body' => $$body);
166 my $msg = $template->processTemplate($config{tpl_invalid_account});
167 UVsendmail::mail($voter_addr, "Fehler", $msg, $msgid);
175 # no address found in mail (non-RFC compliant?)
176 my (@votes, @set, $ballot_id); # irrelevant, but necessary for UVmenu::menu()
177 my @errors = ('InvalidAddressBallot');
178 my $res = UVmenu::menu(\@votes, \@header, $body, \$voter_addr, \$voter_name, \$ballot_id, \@set, \@errors);
180 # "ignore" or address not ok: no ballot can be sent
181 return 0 if (@errors || $res eq 'i');
184 my $subject = UVmessage::get("CFV_SUBJECT");
185 my $template = UVtemplate->new();
188 #if ($ballot_id ne $ids{$voter_addr}) {
189 if ($ids{$voter_addr}) {
190 $ballot_id = $ids{$voter_addr};
191 $template->setKey('alreadysent' => 1) if ($ballot_id = $ids{$voter_addr});
193 # generate new ballot id from the MD5 sum of header, body and a random value
194 $ballot_id = md5_hex($entity->stringify_header . $body . rand 65535);
195 $ids{$voter_addr} = $ballot_id;
197 # write ballot id to file
198 open(IDFILE, ">>$config{idfile}")
199 or die UVmessage::get("CFV_ERRWRITE", (FILE => $config{idfile})) . "\n\n";
200 print IDFILE "$voter_addr $ballot_id\n";
201 close(IDFILE) or die UVmessage::get("CFV_ERRCLOSE") . "\n\n";
204 $template->setKey('ballotid' => $ballot_id);
205 $template->setKey('address' => $voter_addr);
206 $template->setKey('bdsginfo' => $config{bdsginfo});
208 for (my $n=0; $n<@groups; $n++) {
209 $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]);
212 my $msg = $template->processTemplate($config{'tpl_ballot_personal'});
214 # $config{voteaccount} is the Reply-To address:
215 UVsendmail::mail($voter_addr, $subject, $msg, $msgid, $config{voteaccount});
220 ##############################################################################
221 # Print dummy personalized ballot in STDOUT for checking purposes #
222 # Called if command line argument -t is present #
223 ##############################################################################
226 my $template = UVtemplate->new();
228 # generate new ballot id
229 my $ballot_id = md5_hex(rand 65535);
231 $template->setKey('ballotid' => $ballot_id);
232 $template->setKey('address' => 'dummy@foo.invalid');
233 $template->setKey('bdsginfo' => $config{bdsginfo});
235 for (my $n=0; $n<@groups; $n++) {
236 $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]);
239 my $msg = $template->processTemplate($config{'tpl_ballot_personal'});
245 ##############################################################################
246 # Handle Signals and delete lock files when exiting #
247 ##############################################################################
251 unlink $config{lockfile} if ($config{lockfile});
257 die "\n\nSIG$sig: deleting lockfile and exiting\n\n";
261 ##############################################################################
262 # Print help text (options and syntax) on -h or --help #
263 ##############################################################################
267 Usage: uvcfv.pl [-c config_file] [-t]
270 Liest Mailboxen ein und beantwortet alle Mails mit personalisierten CfVs.
272 -c config_file liest die Konfiguration aus config_file
273 (usevote.cfg falls nicht angegeben)
275 -t, --test gibt einen Dummy-Wahlschein fuer Pruefzwecke aus
277 -h, --help zeigt diesen Hilfetext an