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.
244 lines
6.4 KiB
Python
Executable file
244 lines
6.4 KiB
Python
Executable file
#! /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 SEVIOR", 1), \
|
|
("RICHARD STALLMAN", 2), \
|
|
("MIGUEL DE ICAZA", 3), \
|
|
("BILL HANEMAN", 4), \
|
|
("MIKE NEWMAN", 5), \
|
|
("GLYNN FOSTER", 6), \
|
|
("BASTIEN NOCERA", 7), \
|
|
("MALCOLM TREDINNICK", 8), \
|
|
("MICHAEL MEEKS", 9), \
|
|
("DANIEL VEILLARD", 10), \
|
|
("JAMES HENSTRIDGE", 11), \
|
|
("JEFF WAUGH", 12), \
|
|
("JIM GETTYS", 13), \
|
|
("LESLIE PROCTOR", 14), \
|
|
("NAT FRIEDMAN", 15), \
|
|
("JONATHAN BLANDFORD", 16), \
|
|
("JODY GOLDBERG", 17), \
|
|
("TIM NEY", 18), \
|
|
("LUIS VILLA", 19), \
|
|
("AMY KAHN", 20), \
|
|
("MARTIN BAULIG", 21), \
|
|
("SRI RAMKRISHNA", 22), \
|
|
("FEDERICO MENA QUINTERO", 23) ]
|
|
|
|
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 0
|
|
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)
|
|
|
|
|
|
|
|
|
|
|