Fix UVreadmail.pm
[usenet/usevote.git] / UVmenu.pm
1 # UVmenu: menu for interaction with the votetaker
2 # Used by uvvote.pl, uvcfv.pl, uvcount.pl
3  
4 package UVmenu;
5  
6 use strict;
7 use UVconfig;
8 use UVmessage;
9 use UVrules;
10 use vars qw($VERSION);
11
12 use Text::Wrap qw(wrap $columns);
13  
14 # Module version
15 $VERSION = "0.4";
16
17 ##############################################################################
18 # Menu for interaction with the votetaker                                    #
19 # Parameters: votes list and header (references to arrays)                   #
20 #             Body, Mailadress, Name, Ballot ID,                             #
21 #             Voting (references to strings)                                 #
22 #             List of newly set fields (reference to array)                  #
23 #             List of errors to correct (Array-Ref)                          #
24 # Return Values: 'w': proceed                                                #
25 #                'i': ignore (don't save vote)                               #
26 ##############################################################################
27
28 sub menu {
29   my ($votes, $header, $body, $addr, $name, $ballot_id, $voting, $set, $errors) = @_;
30   my $input = "";
31   my $voter_addr = $$addr || '';
32   my $voter_name = $$name || '';
33   my @newvotes = @$votes;
34   my $mailonly = 0;
35   my %errors;
36   $$ballot_id ||= '';
37
38   foreach my $error (@$errors) {
39
40     # unrecognized vote: extract group number und display warning
41     if ($error =~ /^UnrecognizedVote #(\d+)#(.+)$/) {
42       $errors{UnrecognizedVote} ||= UVmessage::get("MENU_UNRECOGNIZEDVOTE");
43       $errors{UnrecognizedVote} .= "\n  " . UVmessage::get("MENU_UNRECOGNIZED_LIST")
44                                           . " #$1: $2";
45
46     # violated rule: extract rule number and display warning
47     } elsif ($error =~ /^ViolatedRule #(\d+)$/) {
48       $errors{ViolatedRule} ||= UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$1"));
49
50     } else {
51       # special handling if called from uvballot.pl
52       $mailonly = 1 if ($error =~ s/Ballot$//);
53
54       # get error message for this error from messages.cfg
55       $errors{$error} = UVmessage::get("MENU_" . uc($error));
56     }
57   }
58
59   # This loop is only left by 'return'
60   while (1) {
61
62     system($config{clearcmd});
63     print UVmessage::get("MENU_PROBLEMS") . "\n";
64
65     foreach my $error (keys %errors) {
66       print "* $errors{$error}\n";
67     }
68
69     my $menucaption = UVmessage::get("MENU_CAPTION");
70     print "\n\n$menucaption\n";
71     print "=" x length($menucaption), "\n\n";
72
73     # don't print this option if called from uvcfv.pl
74     unless ($mailonly) {
75       print "(0) ", UVmessage::get("MENU_DIFF_BALLOT"), "\n";
76     }
77
78     print "(1) ", UVmessage::get("MENU_SHOW_MAIL"), "\n\n",
79           UVmessage::get("MENU_CHANGE_PROPERTIES"), "\n",
80           "(2) ", UVmessage::get("MENU_ADDRESS"), " [$voter_addr]\n";
81
82     # don't print these options if called from uvcfv.pl
83     unless ($mailonly) {
84       print "(3) ", UVmessage::get("MENU_NAME"), " [$voter_name]\n";
85       print "(4) ", UVmessage::get("MENU_VOTES"), " [", @$votes, "]\n";
86       print "(5) ", UVmessage::get("MENU_BALLOT_ID"), " [$$ballot_id]\n"
87         if ($config{personal});
88       print "(6) ", UVmessage::get("MENU_BDSG"), "\n" if ($config{bdsg});
89       print "(7) ", UVmessage::get("MENU_VOTING"), " [", $$voting, "]\n";
90     }
91
92     print "\n",
93           "(i) ", UVmessage::get("MENU_IGNORE"), "\n",
94           "(w) ", UVmessage::get("MENU_PROCEED"), "\n\n",
95           UVmessage::get("MENU_PROMPT");
96
97     do { $input = <STDIN>; } until ($input);
98     chomp $input;
99     print "\n";
100
101     # only accept 1, 2, i and w if called from uvcfv.pl
102     next if ($mailonly && $input !~ /^[12iw]$/i);
103
104     if ($input eq '0') {
105       # ignore SIGPIPE (Bug in more and less)
106       $SIG{PIPE} = 'IGNORE';
107       open (DIFF, "|$config{diff} - $config{sampleballotfile} | $config{pager}");
108       print DIFF $$body, "\n";
109       close (DIFF);
110       
111     } elsif ($input eq '1') {
112       system($config{clearcmd});
113       # ignore SIGPIPE (Bug in more and less)
114       $SIG{PIPE} = 'IGNORE';
115       open (MORE, "|$config{pager}");
116       print MORE join("\n", @$header), "\n\n", $$body, "\n";
117       close (MORE);
118       
119       print "\n", UVmessage::get("MENU_GETKEY");
120       $input = <STDIN>;
121
122     } elsif ($input eq '2') {
123       my $sel;
124       do {
125         print "[a] ", UVmessage::get("MENU_ADDRESS_OK"), "\n",
126               "[b] ", UVmessage::get("MENU_ADDRESS_CHANGE"), "\n",
127               "[c] ", UVmessage::get("MENU_ADDRESS_INVALID"), "\n\n",
128               UVmessage::get("MENU_PROMPT");
129         $sel = <STDIN>;
130       } until ($sel =~ /^[abc]$/i);
131       if ($sel =~ /^a$/i) {
132         delete $errors{SuspiciousAccount};
133         delete $errors{InvalidAddress};
134         next;
135       } elsif ($sel =~ /^c$/i) {
136         delete $errors{SuspiciousAccount};
137         $errors{InvalidAddress} = UVmessage::get("MENU_INVALIDADDRESS") . " " .
138                                   UVmessage::get("MENU_INVALIDADDRESS2");
139         next;
140       }
141         
142       do {
143         print "\n", UVmessage::get("MENU_ADDRESS_PROMPT"), " ";
144         $voter_addr = <STDIN>;
145         chomp ($voter_addr);
146       } until ($voter_addr);
147       $$addr = $voter_addr;
148       push (@$set, 'Adresse');
149       delete $errors{SuspiciousAccount};
150       delete $errors{InvalidAddress};
151       check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids);
152
153     } elsif ($input eq '3') {
154       my $sel;
155       do {
156         print "[a] ", UVmessage::get("MENU_NAME_OK"), "\n",
157               "[b] ", UVmessage::get("MENU_NAME_CHANGE"), "\n",
158               "[c] ", UVmessage::get("MENU_NAME_INVALID"), "\n\n",
159               UVmessage::get("MENU_PROMPT");
160         $sel = <STDIN>;
161       } until ($sel =~ /^[abc]$/i);
162       if ($sel =~ /^a$/i) {
163         delete $errors{InvalidName};
164         next;
165       } elsif ($sel =~ /^c$/i) {
166         $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME");
167         next;
168       }
169       print UVmessage::get("MENU_NAME"), ": ";
170       $voter_name = <STDIN>;
171       chomp ($voter_name);
172       $$name = $voter_name;
173       push (@$set, 'Name');
174       delete $errors{NoName};
175       delete $errors{InvalidName};
176
177       $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME")
178         unless ($voter_name =~ /$config{name_re}/);
179  
180     } elsif ($input eq '4') {
181       # set votes
182
183       my $sel;
184       do {
185         print "[a] ", UVmessage::get("MENU_VOTES_OK"), "\n",
186               "[b] ", UVmessage::get("MENU_VOTES_RESET"), "\n",
187               "[c] ", UVmessage::get("MENU_VOTES_INVALID"), "\n",
188               "[d] ", UVmessage::get("MENU_VOTES_CANCELLED"), "\n\n",
189               UVmessage::get("MENU_PROMPT");
190         $sel = <STDIN>;
191       } until ($sel =~ /^[abcd]$/i);
192       if ($sel =~ /^[ad]$/i) {
193         delete $errors{NoVote};
194         delete $errors{UnrecognizedVote};
195         delete $errors{ViolatedRule};
196         delete $errors{DuplicateVote};
197         if ($sel =~ /^d$/i) {
198           # cancelled vote: replace all votes with an A
199           @$votes = split(//, 'A' x scalar @groups);
200           push @$set, 'Stimmen';
201           # some errors are irrelevant when cancelling a vote:
202           delete $errors{InvalidName};
203           delete $errors{NoName};
204           delete $errors{InvalidBDSG};
205           delete $errors{InvalidAddress};
206           delete $errors{SuspiciousAccount};
207         }
208         next;
209       } elsif ($sel =~ /^c$/i) {
210         $errors{NoVote} = UVmessage::get("MENU_INVALIDVOTE");
211         next;
212       }
213
214       # Set columns for Text::Wrap
215       $columns = $config{rightmargin};
216       print "\n", wrap('', '', UVmessage::get("MENU_VOTES_REENTER_ASK")), "\n\n";
217       print UVmessage::get("MENU_VOTES_REENTER_LEGEND"), "\n";
218
219       for (my $n=0; $n<@groups; $n++) {
220         my $voteinput = "";
221         $votes->[$n] ||= 'E';
222
223         # repeat while invalid character entered
224         while (!($voteinput =~ /^[JNE]$/)) {
225           my $invalid = $#groups ? 0 : 1;
226           print UVmessage::get("MENU_VOTES_REENTER", (GROUP => $groups[$n]));
227           $voteinput = <STDIN>;
228           chomp $voteinput;
229           $voteinput ||= $votes->[$n];
230           $voteinput =~ tr/jne/JNE/;
231         }
232         
233         # valid input, save new votes
234         $newvotes[$n] = $voteinput;
235       } 
236
237       print "\n\n";
238       my $oldvotes = UVmessage::get("MENU_VOTES_REENTER_OLD");
239       my $newvotes = UVmessage::get("MENU_VOTES_REENTER_NEW");
240       my $oldlen = length($oldvotes);
241       my $newlen = length($newvotes);
242       my $maxlen = 1 + (($newlen>$oldlen) ? $newlen : $oldlen);
243       print $oldvotes, ' ' x ($maxlen - length($oldvotes)), @$votes, "\n",
244             $newvotes, ' ' x ($maxlen - length($newvotes)), @newvotes, "\n\n";
245
246       do {
247         print "[a] ", UVmessage::get("MENU_VOTES_REENTER_ACK"), "    ",
248               "[b] ", UVmessage::get("MENU_VOTES_REENTER_NACK"), "\n\n", 
249                UVmessage::get("MENU_PROMPT");
250         $sel = <STDIN>;
251       } until ($sel =~ /^[ab]$/i);
252
253       next if ($sel =~ /^b$/i);
254       @$votes = @newvotes;
255       push @$set, 'Stimmen';
256       delete $errors{UnrecognizedVote};
257       delete $errors{DuplicateVote};
258       delete $errors{NoVote};
259       delete $errors{ViolatedRule};
260
261       if (my $rule = UVrules::rule_check($votes)) {
262         $errors{ViolatedRule} = UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$rule"));
263       }
264
265     } elsif ($input eq '5' && $config{personal}) {
266       print "\n", UVmessage::get("MENU_BALLOT_ID"), ": ";
267       $$ballot_id = <STDIN>;
268       chomp ($$ballot_id);
269       push (@$set, 'Kennung');
270       check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids);
271
272     } elsif ($input eq '6' && $config{bdsg}) {
273       my $sel;
274       do {
275         print "[a] ", UVmessage::get("MENU_BDSG_ACCEPTED"), "\n",
276               "[b] ", UVmessage::get("MENU_BDSG_DECLINED"), "\n\n",
277               UVmessage::get("MENU_PROMPT");
278         $sel = <STDIN>;
279       } until ($sel =~ /^[ab]$/i);
280
281       if ($sel =~ /^a$/i) {
282         delete $errors{InvalidBDSG};
283       } else {
284         $errors{InvalidBDSG} = UVmessage::get("MENU_INVALIDBDSG");
285       }
286
287     } elsif ($input eq '7') {
288       my $sel;
289       do {
290         print "[a] ", UVmessage::get("MENU_VOTING_CORRECT"), "\n",
291               "[b] ", UVmessage::get("MENU_VOTING_WRONG"), "\n\n",
292               UVmessage::get("MENU_PROMPT");
293         $sel = <STDIN>;
294       } until ($sel =~ /^[ab]$/i);
295
296       if ($sel =~ /^a$/i) {
297         delete $errors{NoVoting};
298         delete $errors{WrongVoting};
299       } else {
300         $errors{WrongVoting} = UVmessage::get("MENU_WRONGVOTING");
301       }
302
303     } elsif ($input eq '7') {
304       my $sel;
305       do {
306         print "[a] ", UVmessage::get("MENU_VOTING_CORRECT"), "\n",
307               "[b] ", UVmessage::get("MENU_VOTING_WRONG"), "\n\n",
308               UVmessage::get("MENU_PROMPT");
309         $sel = <STDIN>;
310       } until ($sel =~ /^[ab]$/i);
311
312       if ($sel =~ /^a$/i) {
313         delete $errors{NoVoting};
314         delete $errors{WrongVoting};
315       } else {
316         $errors{WrongVoting} = UVmessage::get("MENU_WRONGVOTING");
317       }
318
319     } elsif ($input =~ /^i$/i) {
320       my $ignore = UVmessage::get("MENU_IGNORE_STRING");
321       # Set columns for Text::Wrap
322       $columns = $config{rightmargin};
323       print wrap('', '', UVmessage::get("MENU_IGNORE_WARNING",
324                                         (MENU_IGNORE_STRING => $ignore)
325                                        ));
326       if (<STDIN> eq "$ignore\n") {
327         print "\n";
328         return "i";
329       }
330
331     } elsif ($input =~ /^w$/i) {
332
333       if (keys %errors) {
334         if ((keys %errors)==1 && $errors{UnrecognizedVote}) {
335           # unrecognized vote lines aren't errors if votetaker
336           # did not change them
337           @$errors = ();
338         } else {
339           # Set columns for Text::Wrap
340           $columns = $config{rightmargin};
341           @$errors = keys %errors;
342           my $warning = ' ' . UVmessage::get("MENU_ERROR_WARNING") . ' ';
343           my $length = length($warning);
344           print "\n", '*' x (($config{rightmargin}-$length)/2), $warning,
345                 '*' x (($config{rightmargin}-$length)/2), "\n\n",
346                 wrap('', '', UVmessage::get("MENU_ERROR_TEXT")), "\n\n",
347                 '*' x $config{rightmargin}, "\n\n",
348                 UVmessage::get("MENU_ERROR_GETKEY");
349           my $input = <STDIN>;
350           next if ($input !~ /^y$/i);
351           print "\n";
352         }
353       } else {
354         @$errors = ();
355       }
356  
357       system($config{clearcmd});
358       print "\n", UVmessage::get("MENU_PROCESSING"), "\n";
359       return "w";
360     }
361   }
362
363   sub check_ballotid {
364     my ($errors, $voter_addr, $ballot_id, $ids) = @_;
365
366     return 0 unless ($config{personal});
367
368     delete $errors->{NoBallotID};
369     delete $errors->{WrongBallotID};
370     delete $errors->{AddressNotRegistered};
371
372     if ($$ballot_id) {
373       if ($ids->{$$voter_addr}) {
374         if ($ids->{$$voter_addr} ne $$ballot_id) {
375           # ballot id incorrect
376           $errors->{WrongBallotID} = UVmessage::get("MENU_WRONGBALLOTID");
377         }
378       } else {
379         $errors->{AddressNotRegistered} = UVmessage::get("MENU_ADDRESSNOTREGISTERED");
380       } 
381     } else {
382       $errors->{NoBallotID} = UVmessage::get("MENU_NOBALLOTID");
383     }
384   }
385
386 }
387
388
389 ##############################################################################
390 # Menu for sorting out duplicate votings manually                            #
391 # Parameters: References to hashes with the paragraphs from the result file  #
392 #             and the default value                                          #
393 # Return value: selected menu item (1, 2 or 0)                               #
394 ##############################################################################
395
396 sub dup_choice {
397   my ($vote1, $vote2, $default) = @_;
398
399   print STDERR "\n", UVmessage::get("MENU_DUP_VOTE"), "\n\n";
400   print STDERR UVmessage::get("MENU_DUP_FIRST"), "\n";
401   print STDERR "A: $vote1->{A}\n";
402   print STDERR "N: $vote1->{N}\n";
403   print STDERR "D: $vote1->{D}\n";
404   print STDERR "K: $vote1->{K}\n";
405   print STDERR "S: $vote1->{S}\n\n";
406   print STDERR UVmessage::get("MENU_DUP_SECOND"), "\n";
407   print STDERR "A: $vote2->{A}\n";
408   print STDERR "N: $vote2->{N}\n";
409   print STDERR "D: $vote2->{D}\n";
410   print STDERR "K: $vote2->{K}\n";
411   print STDERR "S: $vote2->{S}\n\n";
412   print STDERR "1: ", UVmessage::get("MENU_DUP_DELFIRST"), "\n",
413                "2: ", UVmessage::get("MENU_DUP_DELSECOND"), "\n",
414                "0: ", UVmessage::get("MENU_DUP_DELNONE"), "\n\n";
415
416   my $input;
417
418   do {
419     print STDERR UVmessage::get("MENU_PROMPT"), "[$default] ";
420     $input = <STDIN>;
421     chomp $input;
422   } until ($input eq '' || ($input >= 0 && $input<3));
423
424   return $input || $default;
425 }
426
427 1;
This page took 0.021974 seconds and 3 git commands to generate.