Commit | Line | Data |
---|---|---|
ac7e2c54 TH |
1 | #!/usr/bin/perl -w |
2 | ||
3 | ############################################################################### | |
4 | # UseVoteGer 4.09 Personalisierte Wahlscheine | |
5 | # (c) 2001-2005 Marc Langer <uv@marclanger.de> | |
6 | # | |
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. | |
10 | # | |
11 | # Use this script to read mails and send back a CfV with unique ballot id | |
12 | # | |
13 | # Many thanks to: | |
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 | |
18 | # | |
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 | ############################################################################### | |
22 | ||
23 | use strict; | |
24 | use Getopt::Long; | |
25 | use Digest::MD5 qw(md5_hex); | |
26 | use Text::Wrap qw(wrap $columns); | |
27 | use FindBin qw($Bin); | |
28 | use lib $Bin; | |
29 | use UVconfig; | |
30 | use UVmenu; | |
31 | use UVmessage; | |
32 | use UVreadmail; | |
33 | use UVsendmail; | |
34 | use UVtemplate; | |
35 | ||
36 | my %opt_ctl = (); | |
37 | ||
38 | print "\n$usevote_version Personalisierte Wahlscheine - (c) 2001-2005 Marc Langer\n\n"; | |
39 | ||
40 | # unknown parameters remain in @ARGV (for "help") | |
41 | Getopt::Long::Configure(qw(pass_through bundling)); | |
42 | ||
43 | # Put known parameters in %opt_ctl | |
44 | GetOptions(\%opt_ctl, qw(test t config-file=s c=s)); | |
45 | ||
46 | # test mode? (default: no) | |
47 | my $test_only = $opt_ctl{test} || $opt_ctl{t} || 0; | |
48 | ||
49 | # # Additional parameters or invalid options? Show help and exit. | |
50 | help() if (@ARGV); | |
51 | ||
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); | |
55 | ||
56 | # Set columns for Text::Wrap | |
57 | $columns = $config{rightmargin}; | |
58 | ||
59 | # read list of suspicious mail addresses from file | |
60 | my @bad_addr = UVconfig::read_badaddr(); | |
61 | ||
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"; | |
65 | } | |
66 | ||
67 | # option -t used? | |
68 | if ($test_only) { | |
69 | print_ballot(); | |
70 | exit 0; | |
71 | } | |
72 | ||
73 | # check for lock file | |
74 | if (-e $config{lockfile}) { | |
75 | my $lockfile = $config{lockfile}; | |
76 | ||
77 | # don't delete lockfile in END block ;-) | |
78 | $config{lockfile} = ''; | |
79 | ||
80 | # exit | |
81 | die UVmessage::get("ERR_LOCK", (FILE=>$lockfile)) . "\n\n"; | |
82 | } | |
83 | ||
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'; | |
90 | ||
91 | # create lock file | |
92 | open (LOCKFILE, ">$config{lockfile}"); | |
93 | close (LOCKFILE); | |
94 | ||
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"; | |
99 | } | |
100 | ||
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(); | |
104 | my $count = 0; | |
105 | while ($count<1000 && (-e "$config{archivedir}/$file" || -e "$config{tmpdir}/$file")) { | |
106 | $file = "$base-" . ++$count; | |
107 | } | |
108 | die UVmessage::get("ERR_FILE_CREATION") . "\n\n" if ($count == 1000); | |
109 | ||
110 | unless ($config{pop3}) { | |
111 | rename ($config{requestfile}, "$config{tmpdir}/$file") | |
112 | or die UVmessage::get("ERR_RENAME_MAILFILE") . "$!\n\n"; | |
113 | } | |
114 | ||
115 | # wait, so that current mail deliveries can finalize | |
116 | sleep 2; | |
117 | ||
118 | # initiliaze random number generator | |
119 | srand; | |
120 | ||
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"; | |
126 | UVsendmail::send(); | |
127 | ||
128 | print UVmessage::get("INFO_TIDY_UP") . "\n\n"; | |
129 | rename("$config{tmpdir}/$file", "$config{archivedir}/$file"); | |
130 | chmod (0400, "$config{archivedir}/$file"); | |
131 | ||
132 | exit 0; | |
133 | ||
134 | ||
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 | ############################################################################## | |
141 | ||
142 | sub process_request { | |
143 | my ($voter_addr, $voter_name, $h_date, $entity, $body) = @_; | |
144 | ||
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); | |
149 | ||
150 | # found address? | |
151 | if ($voter_addr) { | |
152 | # check for suspicious addresses | |
153 | foreach my $element (@bad_addr) { | |
154 | if ($voter_addr =~ /^$element/) { | |
155 | my (@votes, @set, $ballot_id); # irrelevant, but necessary for UVmenu::menu() | |
156 | my @errors = ('SuspiciousAccountBallot'); | |
157 | my $res = UVmenu::menu(\@votes, \@header, $body, \$voter_addr, \$voter_name, \$ballot_id, \@set, \@errors); | |
158 | ||
159 | # "Ignore": don't deliver a ballot | |
160 | return 0 if ($res eq 'i'); | |
161 | if (@errors) { | |
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); | |
168 | return 0; | |
169 | } | |
170 | last; | |
171 | } | |
172 | } | |
173 | } else { | |
174 | ||
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); | |
179 | ||
180 | # "ignore" or address not ok: no ballot can be sent | |
181 | return 0 if (@errors || $res eq 'i'); | |
182 | } | |
183 | ||
184 | my $subject = UVmessage::get("CFV_SUBJECT"); | |
185 | my $template = UVtemplate->new(); | |
186 | my $ballot_id = ""; | |
187 | ||
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}); | |
192 | } else { | |
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; | |
196 | ||
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"; | |
202 | } | |
203 | ||
204 | $template->setKey('ballotid' => $ballot_id); | |
205 | $template->setKey('address' => $voter_addr); | |
206 | $template->setKey('bdsginfo' => $config{bdsginfo}); | |
207 | ||
208 | for (my $n=0; $n<@groups; $n++) { | |
209 | $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]); | |
210 | } | |
211 | ||
212 | my $msg = $template->processTemplate($config{'tpl_ballot_personal'}); | |
213 | ||
214 | # $config{voteaccount} is the Reply-To address: | |
215 | UVsendmail::mail($voter_addr, $subject, $msg, $msgid, $config{voteaccount}); | |
216 | ||
217 | } | |
218 | ||
219 | ||
220 | ############################################################################## | |
221 | # Print dummy personalized ballot in STDOUT for checking purposes # | |
222 | # Called if command line argument -t is present # | |
223 | ############################################################################## | |
224 | ||
225 | sub print_ballot { | |
226 | my $template = UVtemplate->new(); | |
227 | ||
228 | # generate new ballot id | |
229 | my $ballot_id = md5_hex(rand 65535); | |
230 | ||
231 | $template->setKey('ballotid' => $ballot_id); | |
232 | $template->setKey('address' => 'dummy@foo.invalid'); | |
233 | $template->setKey('bdsginfo' => $config{bdsginfo}); | |
234 | ||
235 | for (my $n=0; $n<@groups; $n++) { | |
236 | $template->addListItem('groups', pos=>$n+1, group=>$groups[$n]); | |
237 | } | |
238 | ||
239 | my $msg = $template->processTemplate($config{'tpl_ballot_personal'}); | |
240 | ||
241 | print $msg, "\n"; | |
242 | } | |
243 | ||
244 | ||
245 | ############################################################################## | |
246 | # Handle Signals and delete lock files when exiting # | |
247 | ############################################################################## | |
248 | ||
249 | END { | |
250 | # delete lockfile | |
251 | unlink $config{lockfile} if ($config{lockfile}); | |
252 | } | |
253 | ||
254 | ||
255 | sub sighandler { | |
256 | my ($sig) = @_; | |
257 | die "\n\nSIG$sig: deleting lockfile and exiting\n\n"; | |
258 | } | |
259 | ||
260 | ||
261 | ############################################################################## | |
262 | # Print help text (options and syntax) on -h or --help # | |
263 | ############################################################################## | |
264 | ||
265 | sub help { | |
266 | print <<EOF; | |
267 | Usage: uvcfv.pl [-c config_file] [-t] | |
268 | uvcfv.pl -h | |
269 | ||
270 | Liest Mailboxen ein und beantwortet alle Mails mit personalisierten CfVs. | |
271 | ||
272 | -c config_file liest die Konfiguration aus config_file | |
273 | (usevote.cfg falls nicht angegeben) | |
274 | ||
275 | -t, --test gibt einen Dummy-Wahlschein fuer Pruefzwecke aus | |
276 | ||
277 | -h, --help zeigt diesen Hilfetext an | |
278 | ||
279 | EOF | |
280 | ||
281 | exit 0; | |
282 | } |