| 1 |
#!/usr/bin/perl -w
|
| 2 |
# $Id: votify,v 1.5 2005/05/16 04:03:46 agriffis Exp $
|
| 3 |
#
|
| 4 |
# Copyright 2005 Gentoo Foundation
|
| 5 |
# Distributed under the terms of the GNU General Public License v2
|
| 6 |
#
|
| 7 |
# votify: generate, verify and submit voting ballots for trustee elections
|
| 8 |
#
|
| 9 |
|
| 10 |
BEGIN { push @INC, (getpwnam 'agriffis')[7].'/elections' }
|
| 11 |
|
| 12 |
use POSIX;
|
| 13 |
use Getopt::Long;
|
| 14 |
use List::Util;
|
| 15 |
use Votify 'user';
|
| 16 |
use strict;
|
| 17 |
|
| 18 |
######################################################################
|
| 19 |
# Global vars
|
| 20 |
######################################################################
|
| 21 |
|
| 22 |
(my $zero = $0) =~ s,.*/,,;
|
| 23 |
(my $version = '$Revision: 1.5 $') =~ s/.*?(\d.*\d).*/$zero version $1\n/;
|
| 24 |
my (%opt);
|
| 25 |
|
| 26 |
# Collect the open elections
|
| 27 |
my (@open_elections, $usage_elections);
|
| 28 |
opendir(D, "$Votify::datadir/") or die;
|
| 29 |
@open_elections = sort grep {
|
| 30 |
s/^start-// and do {
|
| 31 |
my ($starttime) = (stat _)[9] if stat("$Votify::datadir/start-$_");
|
| 32 |
my ($stoptime) = (stat _)[9] if stat("$Votify::datadir/stop-$_");
|
| 33 |
((not defined $starttime or $starttime < time) and
|
| 34 |
(not defined $stoptime or $stoptime > time))
|
| 35 |
}
|
| 36 |
} readdir D;
|
| 37 |
closedir D;
|
| 38 |
if (@open_elections) {
|
| 39 |
$usage_elections = join("\n ", @open_elections);
|
| 40 |
} else {
|
| 41 |
$usage_elections = "(no elections currently open)";
|
| 42 |
}
|
| 43 |
|
| 44 |
my $usage = <<EOT;
|
| 45 |
|
| 46 |
usage: $zero <command> <election>
|
| 47 |
|
| 48 |
where <command> is one of:
|
| 49 |
|
| 50 |
--new Generate a new ~/.ballot-<election> for editing
|
| 51 |
--verify Verify content of ~/.ballot-<election>
|
| 52 |
--submit Submit ~/.ballot-<election> to be counted
|
| 53 |
--help Show this help message
|
| 54 |
--version Show version information
|
| 55 |
|
| 56 |
and <election> is one of the elections currently in-progress. The following
|
| 57 |
elections are currently open:
|
| 58 |
|
| 59 |
$usage_elections
|
| 60 |
|
| 61 |
Instructions:
|
| 62 |
|
| 63 |
(1) Create a new ballot with the --new command. This generates a file in your
|
| 64 |
home directory called ~/.ballot-<election>. The file contains a shuffled
|
| 65 |
listing of the candidates. Note that the permissions on the file are set so
|
| 66 |
that only you have read+write permissions.
|
| 67 |
|
| 68 |
\$ $zero --new <election>
|
| 69 |
|
| 70 |
(2) Edit ~/.ballot-<election> and rearrange the candidates linewise in order of
|
| 71 |
preference. Candidates you consider equal can be listed on the same line.
|
| 72 |
Any candidates you omit are implied on the last line. For example, if tom,
|
| 73 |
jerry, sam, linford and karin are running, you could put:
|
| 74 |
|
| 75 |
karin
|
| 76 |
linford jerry
|
| 77 |
|
| 78 |
In this case, you prefer karin over everybody else. You prefer linford and
|
| 79 |
jerry over tom and sam.
|
| 80 |
|
| 81 |
The file format is case-insensitive, ignores empty lines and comments that
|
| 82 |
start with the hash '#' symbol.
|
| 83 |
|
| 84 |
(3) Verify your choices. The $zero program will explain in English if it
|
| 85 |
appears that you've made any errors in your ballot.
|
| 86 |
|
| 87 |
\$ $zero --verify <election>
|
| 88 |
|
| 89 |
(4) Submit your ballot. This renames your ballot to
|
| 90 |
~/.ballot-<election>-submitted so that it will be tallied when the votes
|
| 91 |
are collected.
|
| 92 |
|
| 93 |
\$ $zero --submit <election>
|
| 94 |
|
| 95 |
EOT
|
| 96 |
|
| 97 |
######################################################################
|
| 98 |
# Main
|
| 99 |
######################################################################
|
| 100 |
|
| 101 |
package main;
|
| 102 |
|
| 103 |
# Make sure umask is secure before we do anything
|
| 104 |
umask 077;
|
| 105 |
|
| 106 |
# Parse the options on the cmdline. Put the short versions first in
|
| 107 |
# each optionstring so that the hash keys are created using the short
|
| 108 |
# versions. For example, use 'q|qar', not 'qar|q'.
|
| 109 |
my ($result) = GetOptions(
|
| 110 |
\%opt,
|
| 111 |
'new', # generate new ~/.ballot-<election>
|
| 112 |
'verify', # verify content of ~/.ballot-<election>
|
| 113 |
'submit', # rename ~/.ballot to ~/.ballot-<election>-submitted
|
| 114 |
'help', # help message
|
| 115 |
'version', # version information
|
| 116 |
);
|
| 117 |
if ($opt{'help'} or not %opt) { print STDERR $usage; exit 0 }
|
| 118 |
if ($opt{'version'}) { print STDERR $version; exit 0 }
|
| 119 |
die "$zero: only one command allowed; use --help for help\n" if 1 < keys %opt;
|
| 120 |
die "$zero: election required; use --help for help\n" unless @ARGV == 1;
|
| 121 |
|
| 122 |
my ($election) = $ARGV[0];
|
| 123 |
my ($b) = Ballot->new($election);
|
| 124 |
|
| 125 |
# Check if the election is open. This should really happen in an
|
| 126 |
# Election class in Votify.pm eventually
|
| 127 |
my ($starttime, $stoptime);
|
| 128 |
if (stat("$Votify::datadir/start-$election")) { $starttime = (stat _)[9] }
|
| 129 |
if (stat("$Votify::datadir/stop-$election")) { $stoptime = (stat _)[9] }
|
| 130 |
if ($starttime && $starttime > time) {
|
| 131 |
print "\n", "*" x 75, "\n";
|
| 132 |
print "WARNING: Specified election doesn't start until ",
|
| 133 |
scalar(localtime $starttime), "\n";
|
| 134 |
print "*" x 75, "\n";
|
| 135 |
}
|
| 136 |
if ($stoptime && $stoptime < time) {
|
| 137 |
print "\n", "*" x 75, "\n";
|
| 138 |
print "WARNING: Specified election ended at ",
|
| 139 |
scalar(localtime $stoptime), "\n";
|
| 140 |
print "*" x 75, "\n";
|
| 141 |
}
|
| 142 |
|
| 143 |
if ($opt{'new'}) {
|
| 144 |
# Make sure the user is eligible
|
| 145 |
open(F, "<$Votify::datadir/voters-$election") or die "Failed to open voters file";
|
| 146 |
my (@voters) = <F>;
|
| 147 |
chomp(@voters);
|
| 148 |
close(F);
|
| 149 |
|
| 150 |
unless (grep { $_ eq getpwuid($>) } @voters) {
|
| 151 |
print STDERR <<EOT;
|
| 152 |
|
| 153 |
I'm sorry, you are not a registered voter for this election. Please
|
| 154 |
contact one of the election officials if you feel this is in error,
|
| 155 |
either on IRC or in email.
|
| 156 |
|
| 157 |
EOT
|
| 158 |
exit 1;
|
| 159 |
}
|
| 160 |
|
| 161 |
$b->populate();
|
| 162 |
$b->write();
|
| 163 |
|
| 164 |
# Let the user know what we did
|
| 165 |
print <<EOF;
|
| 166 |
|
| 167 |
Welcome to the $election election! Your ballot has been written to
|
| 168 |
|
| 169 |
$b->{filename}
|
| 170 |
|
| 171 |
Please edit this file with your preferred editor. Additional instructions can
|
| 172 |
be read at the top of the ballot.
|
| 173 |
|
| 174 |
EOF
|
| 175 |
exit 0;
|
| 176 |
}
|
| 177 |
|
| 178 |
if ($opt{'verify'} or $opt{'submit'}) {
|
| 179 |
$b->read();
|
| 180 |
$b->verify();
|
| 181 |
if ($opt{'verify'}) {
|
| 182 |
print <<EOF;
|
| 183 |
Your ballot checks out ok, congratulations! Your next step should be
|
| 184 |
|
| 185 |
$zero --submit $election
|
| 186 |
|
| 187 |
EOF
|
| 188 |
exit 0;
|
| 189 |
}
|
| 190 |
|
| 191 |
my ($s) = $b->{'filename'}.'-submitted';
|
| 192 |
die("File already exists, please remove $s") if -e $s;
|
| 193 |
rename($b->{'default_filename'}, $s) or die("Couldn't rename $b->{default_filename}");
|
| 194 |
print <<EOF;
|
| 195 |
Your ballot has been renamed to $s
|
| 196 |
so that it will be counted when the election is over. Please do not remove your
|
| 197 |
ballot until the election results have been announced. Thank you for
|
| 198 |
participating in the $election election!
|
| 199 |
|
| 200 |
EOF
|
| 201 |
exit 0;
|
| 202 |
}
|
| 203 |
|
| 204 |
__END__
|
| 205 |
|
| 206 |
$Log: votify,v $
|
| 207 |
Revision 1.5 2005/05/16 04:03:46 agriffis
|
| 208 |
add first pass at countify --rank
|
| 209 |
|
| 210 |
Revision 1.3 2005/05/09 23:12:02 agriffis
|
| 211 |
Add support for registered voters
|
| 212 |
|
| 213 |
Revision 1.2 2005/05/05 23:03:46 agriffis
|
| 214 |
Fix indentation (and some output as well)
|
| 215 |
|
| 216 |
Revision 1.1 2005/05/05 22:05:34 agriffis
|
| 217 |
first pass at Gentoo Foundation voting program
|
| 218 |
|
| 219 |
# vim:sw=4 et
|