75909de3ed
To fix bug 629334. On the way, some old .cvsignore files were deleted. Also, Makefiles and headers were adapted to show the new URL. Calendars have not been updated.
254 lines
6.7 KiB
Python
254 lines
6.7 KiB
Python
#! /usr/bin/python
|
|
|
|
import re
|
|
import sys
|
|
import string
|
|
import md5
|
|
|
|
class Ballot:
|
|
def __init__ (self):
|
|
self.email = 0
|
|
self.member = 0
|
|
self.token = 0
|
|
self.votes = []
|
|
|
|
def add_vote (self, name, id):
|
|
self.votes.append ((name, id))
|
|
|
|
class Candidate:
|
|
def __init__ (self, name, id):
|
|
self.name = name
|
|
self.id = id
|
|
self.count = 0
|
|
self.voters = []
|
|
|
|
candidates = {}
|
|
|
|
candidate_tuples = [ \
|
|
("MARTIN BAULIG", 1), \
|
|
("CHEMA CELORIO", 2), \
|
|
("KENNETH CHRISTIANSEN", 3), \
|
|
("RHETT CREIGHTON", 4), \
|
|
("BART DECREM", 5), \
|
|
("MIGUEL DE ICAZA", 6), \
|
|
("CHRISTOPHER GABRIEL", 7), \
|
|
("JUANTOMAS GARCIA", 8), \
|
|
("JIM GETTYS", 9), \
|
|
("BERTRAND GUIHENEUF", 10), \
|
|
("THOMPSON HAYNER", 11), \
|
|
("JOHN HEARD", 12), \
|
|
("JAMES HENSTRIDGE", 13), \
|
|
("KEVIN KNERR", 14), \
|
|
("TUOMAS KUOSMANEN", 15), \
|
|
("GEORGE LEBL", 16), \
|
|
("ELLIOT LEE", 17), \
|
|
("RAPH LEVIEN", 18), \
|
|
("KJARTAN MARAAS", 19), \
|
|
("MICHAEL MEEKS", 20), \
|
|
("DAN MUETH", 21), \
|
|
("ESKIL HEYN OLSEN", 22), \
|
|
("HAVOC PENNINGTON", 23), \
|
|
("ETTORE PERAZZOLI", 24), \
|
|
("BRUCE PERENS", 25), \
|
|
("LESLIE PROCTOR", 26), \
|
|
("FEDERICO MENA QUINTERO", 27), \
|
|
("ARIEL RIOS", 28), \
|
|
("ARLO ROSE", 29), \
|
|
("JOE SHAW", 30), \
|
|
("MACIEJ STACHOWIAK", 31), \
|
|
("OWEN TAYLOR", 32), \
|
|
("DANIEL VEILLARD", 33) ]
|
|
|
|
for c in candidate_tuples:
|
|
cand = Candidate (c[0], c[1])
|
|
candidates[cand.id] = cand
|
|
|
|
from_line_re = re.compile ("^From: *(.*)")
|
|
member_address_re = re.compile (">? *Member Address: *([^ ]*)")
|
|
auth_token_re = re.compile (">? *Validation Token: *(.*)")
|
|
vote_re = re.compile (">? *([A-Z ]+) *\(ID# *([0-9]+)\)")
|
|
|
|
ballots = []
|
|
current_ballot = 0
|
|
|
|
filename = sys.argv[1] # mail archive file
|
|
secret_cookie = sys.argv[2] # secret cookie
|
|
voter_list = sys.argv[3] # list of valid voter addresses
|
|
|
|
# hash from valid addresses to whether they have sent in a ballot yet
|
|
valid_addresses = {}
|
|
|
|
voter_handle = open (voter_list)
|
|
for voter_addr in voter_handle.readlines ():
|
|
valid_addresses[string.strip (voter_addr)] = 0
|
|
|
|
handle = open (filename)
|
|
lines = handle.readlines ()
|
|
for line in lines:
|
|
|
|
match = from_line_re.match (line)
|
|
if match:
|
|
email = string.strip (match.group (1))
|
|
if current_ballot:
|
|
ballots.append (current_ballot)
|
|
current_ballot = Ballot ()
|
|
current_ballot.email = email
|
|
|
|
continue
|
|
|
|
match = member_address_re.match (line)
|
|
if match:
|
|
member = string.strip (match.group (1))
|
|
if (current_ballot.member):
|
|
print "Duplicate member address in ballot from '%s' - duplicates ''%s', '%s'" % (current_ballot.email, current_ballot.member, member)
|
|
else:
|
|
current_ballot.member = member
|
|
|
|
continue
|
|
|
|
match = auth_token_re.match (line)
|
|
if match:
|
|
token = string.strip (match.group (1))
|
|
if (current_ballot.token):
|
|
print "Duplicate auth token in ballot from '%s' - duplicates '%s', '%s'" % (current_ballot.email, current_ballot.token, token)
|
|
else:
|
|
current_ballot.token = token
|
|
|
|
continue
|
|
|
|
match = vote_re.match (line)
|
|
if match:
|
|
name = string.strip (match.group (1))
|
|
id = string.strip (match.group (2))
|
|
|
|
id = int(id)
|
|
|
|
if not candidates.has_key (id):
|
|
print "Unknown candidate '%s' ID %d in ballot from '%s'" % (name, id, current_ballot.email)
|
|
elif not candidates[id].name == name:
|
|
print "Candidate name '%s' for ID '%s' doesn't match, expected '%s'" % (name, id, candidates[id].name)
|
|
else:
|
|
current_ballot.add_vote (name, id)
|
|
|
|
continue
|
|
|
|
if current_ballot:
|
|
ballots.append (current_ballot)
|
|
|
|
handle.close ()
|
|
|
|
def contains_dups (b):
|
|
dups = {}
|
|
for v in b.votes:
|
|
id = v[1]
|
|
if dups.has_key (id):
|
|
return 1
|
|
dups[id] = 1
|
|
return 0
|
|
|
|
dup_tokens = {}
|
|
def md5_is_bad (b):
|
|
key = b.member + secret_cookie
|
|
m = md5.new (key)
|
|
digest = m.digest ()
|
|
# convert to hex, python 2.0 has hexdigest() but this one I'm using
|
|
# apparently does not
|
|
token = ""
|
|
for num in digest:
|
|
token = token + ("%02x" % (ord(num),))
|
|
if token == b.token:
|
|
if dup_tokens.has_key (token):
|
|
print "Auth token occurs twice, someone voted more than once"
|
|
return 1
|
|
else:
|
|
dup_tokens[token] = 1
|
|
return 0
|
|
else:
|
|
print "Bad auth token is %s hashed from '%s'" % (token, key)
|
|
return 1
|
|
|
|
def valid_voter (addr):
|
|
return valid_addresses.has_key (addr)
|
|
|
|
valid_ballots = {}
|
|
|
|
i = 0
|
|
for b in ballots:
|
|
error = 0
|
|
if not b.member:
|
|
error = "missing member address"
|
|
elif not b.token:
|
|
error = "missing auth token"
|
|
elif len (b.votes) > 11:
|
|
error = "too many votes (%d votes)" % len (b.votes)
|
|
elif len (b.votes) == 0:
|
|
error = "didn't list any candidates"
|
|
elif contains_dups (b):
|
|
error = "contains duplicate votes for the same candidate"
|
|
elif md5_is_bad (b):
|
|
error = "bad authentication token"
|
|
elif not valid_voter (b.member):
|
|
error = "ballot from someone not on the list of valid voters"
|
|
else:
|
|
if valid_ballots.has_key (b.token):
|
|
old = valid_ballots[b.token]
|
|
print "Overriding previous valid ballot %d from %s with new ballot %d" % (old[1], old[0].email, i)
|
|
valid_ballots[b.token] = (b, i)
|
|
|
|
if error:
|
|
print "Ignoring ballot %d from '%s' due to: %s" % (i, b.email, error)
|
|
|
|
i = i + 1
|
|
|
|
def tupcmp (a, b):
|
|
return cmp (a[1], b[1])
|
|
|
|
## Print results only after all errors have been printed, so
|
|
## we don't lose any errors.
|
|
valids = valid_ballots.values ()
|
|
valids.sort (tupcmp)
|
|
for (b, i) in valids:
|
|
print "Ballot %d:" % i
|
|
|
|
print " From: " + b.email
|
|
print " Member: " + b.member
|
|
print " Token: " + b.token
|
|
print " Voted for %d candidates:" % len (b.votes)
|
|
|
|
voted_for = []
|
|
|
|
valid_addresses[b.member] = 1
|
|
|
|
for v in b.votes:
|
|
id = v[1]
|
|
candidates[id].count = candidates[id].count + 1
|
|
candidates[id].voters.append (b.member)
|
|
voted_for.append (candidates[id].name)
|
|
|
|
for v in voted_for:
|
|
print " " + v
|
|
|
|
print "The following members did not vote:"
|
|
for addr in valid_addresses.keys ():
|
|
if not valid_addresses[addr]:
|
|
print addr
|
|
|
|
def cmpcand (a, b):
|
|
return cmp (a.count, b.count)
|
|
|
|
cand_list = candidates.values ()
|
|
cand_list.sort (cmpcand)
|
|
|
|
print ""
|
|
print ""
|
|
print "ELECTION RESULTS:"
|
|
|
|
print " %d of %d members cast a valid ballot" % (len (valids), len (valid_addresses.keys()))
|
|
|
|
for c in cand_list:
|
|
print " %s (%d votes)" % (c.name, c.count)
|
|
|
|
|
|
|
|
|
|
|