#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-


# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
#     Romain Chantereau <rchantereau@entrouvert.com>
#     Nicolas Clapiès <nclapies@easter-eggs.org>
#     Pierre-Antoine Dejace <padejace@entrouvert.be>
#     Thierry Dulieu <tdulieu@easter-eggs.com>
#     Florent Monnier <monnier@codelutin.com>
#     Cédric Musso <cmusso@easter-eggs.org>
#     Frédéric Péters <fpeters@entrouvert.be>
#     Benjamin Poussin <poussin@codelutin.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#     Sébastien Régnier <regnier@codelutin.com>
#     Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
#     Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
#     Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
#     Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
#     Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


__doc__ = """Glasnost Ballots Server"""

__version__ = '$Revision$'[11:-2]


import sys
import whrandom

glasnostPythonDir = '/usr/local/lib/glasnost-devel' # changed on make install
sys.path.insert(0, glasnostPythonDir)

import glasnost

import glasnost.common.faults as faults
import glasnost.common.tools_new as commonTools

from glasnost.server.ObjectsServer import Server, VirtualServer
from glasnost.server.tools import *

import glasnost.proxy.VotesProxy # Do not remove!


applicationName = 'BallotsServer'
applicationRole = 'ballots'
dispatcher = None


class BallotsVirtualServer(VirtualServer):
    electionIds = None
    voteIds = None
    voterIds = None

    def convertIds(self, sourceDispatcherId, destinationDispatcherId):
        VirtualServer.convertIds(
            self, sourceDispatcherId, destinationDispatcherId)
        for token, id in self.electionIds.items():
            newToken = token.replace(sourceDispatcherId,
                                     destinationDispatcherId)
            if newToken != token:
                del self.electionIds[token]
                token = newToken
                self.electionIds[token] = id
            newId = id.replace(sourceDispatcherId, destinationDispatcherId)
            if newId != id:
                self.electionIds[token] = newId
        for token, id in self.voteIds.items():
            newToken = token.replace(sourceDispatcherId,
                                     destinationDispatcherId)
            if newToken != token:
                del self.voteIds[token]
                token = newToken
                self.voteIds[token] = id
            newId = id.replace(sourceDispatcherId, destinationDispatcherId)
            if newId != id:
                self.voteIds[token] = newId
        for token, id in self.voterIds.items():
            newToken = token.replace(sourceDispatcherId,
                                     destinationDispatcherId)
            if newToken != token:
                del self.voterIds[token]
                token = newToken
                self.voterIds[token] = id
            newId = id.replace(sourceDispatcherId, destinationDispatcherId)
            if newId != id:
                self.voterIds[token] = newId
        
    def init(self):
        VirtualServer.init(self)
        self.electionIds = {}
        self.voteIds = {}
        self.voterIds = {}

    def initFromOldData(self, data):
        VirtualServer.initFromOldData(self, data)
        (self.electionIds, self.voteIds, self.voterIds) = data

    def removeIds(self, rolesToKeep):
        VirtualServer.removeIds(self, rolesToKeep)
        for token, id in self.electionIds.items():
            if not commonTools.extractRole(id) in rolesToKeep:
                del self.electionIds[token]
        for token, id in self.voteIds.items():
            if not commonTools.extractRole(id) in rolesToKeep:
                del self.voteIds[token]
        for token, id in self.voterIds.items():
            if not commonTools.extractRole(id) in rolesToKeep:
                del self.voterIds[token]


class BallotsServer(Server):
    VirtualServer = BallotsVirtualServer

    def abstainForVote(self, electionId):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        identitiesProxy = getProxyForServerRole('identities')
        try:
            previousVoteToken = identitiesProxy.getElectionVoteToken(
                    electionId)
        except faults.MissingItem:
            previousVoteToken = ''
        if previousVoteToken \
           and virtualServer.voteIds.has_key(previousVoteToken):
            voteId = virtualServer.voteIds[previousVoteToken]
            votesProxy = getProxyForServerRole('votes')
            previousVote = votesProxy.getObject(voteId)
            del virtualServer.electionIds[previousVote.electionToken]
            del virtualServer.voterIds[previousVote.voterToken]
            del virtualServer.voteIds[previousVote.token]
            identitiesProxy.abstainForVote(electionId)
            getProxyForServerRole('elections').abstainForVote(
                    electionId, previousVoteToken)
            votesProxy.deleteObject(voteId)

        virtualServer.markAllAsDirtyFIXME()

    def canGetVoteVoterId(self, voteId):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        userId = getProxyForServerRole('identities').getUserId()
        vote = getProxyForServerRole('votes').getObject(voteId)
        if not virtualServer.voterIds.has_key(vote.voterToken):
            return 0
        if not virtualServer.electionIds.has_key(vote.electionToken):
            return 0
        voterId = virtualServer.voterIds[vote.voterToken]
        electionId = virtualServer.electionIds[vote.electionToken]
        if voterId == userId:
            voteIsSecret = 0
        else:
            electionBallotKind = getProxyForServerRole(
                    'elections').getBallotKind(electionId)
            voteIsSecret = electionBallotKind == 'secret' \
                           or electionBallotKind == 'voterChoice' \
                           and vote.ballotKind == 'secret'
        return not voteIsSecret

    def getElectionIdFromToken(self, electionToken):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        if not virtualServer.electionIds.has_key(electionToken):
            raise faults.UnknownElectionToken(electionToken)
        return virtualServer.electionIds[electionToken]

    def getElectionVotes(self, voteTokens, electionVoterIds):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        votesExport = []
        identitiesProxy = getProxyForServerRole('identities')
        votesProxy = getProxyForServerRole('votes')
        for voteToken in voteTokens:
            if virtualServer.voteIds.has_key(voteToken):
                voteId = virtualServer.voteIds[voteToken]
                if votesProxy.hasObject(voteId):
                    vote = votesProxy.getObject(voteId)
                    if virtualServer.voterIds.has_key(vote.voterToken):
                        voterId = virtualServer.voterIds[vote.voterToken]
                        if voterId in electionVoterIds \
                               and identitiesProxy.hasObject(voterId):
                            votesExport.append(vote.exportToXmlRpc())
        return votesExport

    def getElectionWeightings(self, voteTokens, weightingsById):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        electionVoterIds = weightingsById.keys()
        weightings = {}
        identitiesProxy = getProxyForServerRole('identities')
        votesProxy = getProxyForServerRole('votes')
        for voteToken in voteTokens:
            if virtualServer.voteIds.has_key(voteToken):
                voteId = virtualServer.voteIds[voteToken]
                if votesProxy.hasObject(voteId):
                    vote = votesProxy.getObject(voteId)
                    if virtualServer.voterIds.has_key(vote.voterToken):
                        voterId = virtualServer.voterIds[vote.voterToken]
                        if voterId in electionVoterIds \
                               and identitiesProxy.hasObject(voterId):
                            weightings[vote.voterToken] = weightingsById[
                                voterId]
        return weightings

    def getVote(self, voteId):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        userId = getProxyForServerRole('identities').getUserId()
        vote = getProxyForServerRole('votes').getObject(voteId)
        if not virtualServer.voterIds.has_key(vote.voterToken):
            raise faults.UnknownVoterToken(vote.voterToken)
        if not virtualServer.electionIds.has_key(vote.electionToken):
            raise faults.UnknownElectionToken(vote.electionToken)
##         voterId = virtualServer.voterIds[vote.voterToken]
##         electionId = virtualServer.electionIds[vote.electionToken]
##         if voterId == userId:
##             voteIsSecret = 0
##         else:
##             electionBallotKind = getProxyForServerRole('elections').getBallotKind(
##                 electionId)
##             voteIsSecret = electionBallotKind == 'secret' \
##                            or electionBallotKind == 'voterChoice' \
##                            and vote.ballotKind == 'secret'
##         if voteIsSecret:
##             voteExport = 'secret'
##         else:
##             voteExport = vote.exportToXmlRpc()
        voteExport = vote.exportToXmlRpc()
        return voteExport

    def getVoteFromToken(self, voteToken):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        if voteToken == 'secret':
            return 'secret'
        userId = getProxyForServerRole('identities').getUserId()
        if not virtualServer.voteIds.has_key(voteToken):
            raise faults.UnknownVoteToken(voteToken)
        voteId = virtualServer.voteIds[voteToken]
        vote = getProxyForServerRole('votes').getObject(voteId)
        if not virtualServer.voterIds.has_key(vote.voterToken):
            raise faults.UnknownVoterToken(vote.voterToken)
        voterId = virtualServer.voterIds[vote.voterToken]
        if not virtualServer.electionIds.has_key(vote.electionToken):
            raise faults.UnknownElectionToken(vote.electionToken)
        electionId = virtualServer.electionIds[vote.electionToken]
        if voterId == userId:
            voteIsSecret = 0
        else:
            electionBallotKind = getProxyForServerRole(
                    'elections').getBallotKind(electionId)
            voteIsSecret = electionBallotKind == 'secret' \
                           or electionBallotKind == 'voterChoice' \
                           and vote.ballotKind == 'secret'
        if voteIsSecret:
            voteExport = 'secret'
        else:
            voteExport = vote.exportToXmlRpc()
        return voteExport

    def getVotesFromTokens(self, voteTokens):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        userId = getProxyForServerRole('identities').getUserId()
        votesExport = {}
        electionsProxy = getProxyForServerRole('elections')
        votesProxy = getProxyForServerRole('votes')
        for electionId, voteToken in voteTokens.items():
            if voteToken == 'secret':
                votesExport[electionId] = 'secret'
                continue
            if not electionsProxy.hasObject(electionId):
                continue
            if not virtualServer.voteIds.has_key(voteToken):
                continue
            voteId = virtualServer.voteIds[voteToken]
            if not votesProxy.hasObject(voteId):
                continue
            vote = votesProxy.getObject(voteId)
            if not virtualServer.voterIds.has_key(vote.voterToken):
                continue
            voterId = virtualServer.voterIds[vote.voterToken]
            if voterId == userId:
                voteIsSecret = 0
            else:
                electionBallotKind = getProxyForServerRole(
                        'elections').getBallotKind(electionId)
                voteIsSecret = electionBallotKind == 'secret' \
                               or electionBallotKind == 'voterChoice' \
                               and vote.ballotKind == 'secret'
            if voteIsSecret:
                votesExport[electionId] = 'secret'
            else:
                votesExport[electionId] = vote.exportToXmlRpc()
        return votesExport

    def getVoteVoterId(self, voteId):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        userId = getProxyForServerRole('identities').getUserId()
        vote = getProxyForServerRole('votes').getObject(voteId)
        if not virtualServer.voterIds.has_key(vote.voterToken):
            raise faults.UnknownVoterToken(vote.voterToken)
        if not virtualServer.electionIds.has_key(vote.electionToken):
            raise faults.UnknownElectionToken(vote.electionToken)
        voterId = virtualServer.voterIds[vote.voterToken]
        electionId = virtualServer.electionIds[vote.electionToken]
        if voterId == userId:
            voteIsSecret = 0
        else:
            electionBallotKind = getProxyForServerRole(
                    'elections').getBallotKind(electionId)
            voteIsSecret = electionBallotKind == 'secret' \
                           or electionBallotKind == 'voterChoice' \
                           and vote.ballotKind == 'secret'
        if voteIsSecret:
            raise faults.UserAccessDenied()
        return voterId

    def init(self):
        self.randomGenerator = whrandom.whrandom()
        Server.init(self)

    def isVoteSecret(self, voteToken):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        if voteToken == 'secret':
            return 'secret'
        userId = getProxyForServerRole('identities').getUserId()
        if not virtualServer.voteIds.has_key(voteToken):
            raise faults.UnknownVoteToken(voteToken)
        voteId = virtualServer.voteIds[voteToken]
        vote = getProxyForServerRole('votes').getObject(voteId)
        if not virtualServer.voterIds.has_key(vote.voterToken):
            raise faults.UnknownVoterToken(vote.voterToken)
        voterId = virtualServer.voterIds[vote.voterToken]
        if not virtualServer.electionIds.has_key(vote.electionToken):
            raise faults.UnknownElectionToken(vote.electionToken)
        electionId = virtualServer.electionIds[vote.electionToken]
        if voterId == userId:
            voteIsSecret = 0
        else:
            electionBallotKind = getProxyForServerRole(
                    'elections').getBallotKind(electionId)
            voteIsSecret = electionBallotKind == 'secret' \
                           or electionBallotKind == 'voterChoice' \
                           and vote.ballotKind == 'secret'
        return voteIsSecret

    def registerPublicMethods(self):
        Server.registerPublicMethods(self)
        self.registerPublicMethod('abstainForVote')
        self.registerPublicMethod('canGetVoteVoterId')
        self.registerPublicMethod('getElectionIdFromToken')
        self.registerPublicMethod('getElectionVotes')
        self.registerPublicMethod('getElectionWeightings')
        self.registerPublicMethod('getVote')
        self.registerPublicMethod('getVoteFromToken')
        self.registerPublicMethod('getVotesFromTokens')
        self.registerPublicMethod('getVoteVoterId')
        self.registerPublicMethod('isVoteSecret')
        self.registerPublicMethod('vote')

    def repairVirtualServer(self, virtualServer, version):
        changed = 0
        if version < 4000:
            for token, id in virtualServer.electionIds.items():
                newId = repairId(id)
                if newId:
                    changed = 1
                    virtualServer.electionIds[token] = newId
            for token, id in virtualServer.voteIds.items():
                newId = repairId(id)
                if newId:
                    changed = 1
                    virtualServer.voteIds[token] = newId
            for token, id in virtualServer.voterIds.items():
                newId = repairId(id)
                if newId:
                    changed = 1
                    virtualServer.voterIds[token] = newId
        if version < 1007000:
            for token, id in virtualServer.voteIds.items():
                if not token.startswith('glasnost://'):
                    changed = 1
                    del virtualServer.voteIds[token]
                    token = '%s/votes/%s' % (
                        commonTools.extractDispatcherId(
                            virtualServer.virtualServerId), token)
                    virtualServer.voteIds[token] = id
        if changed:
            virtualServer.markAllAsDirtyFIXME()

    def upgradeVirtualServer_0001_0028(self, virtualServer):
        # Convert voter ids from "people" to "identities".
        dirty = 0
        for token, id in virtualServer.voterIds.items():
            if commonTools.extractRole(id) == 'people':
                dirty = 1
                virtualServer.voterIds[token] = '%s/%s' % (
                        commonTools.makeApplicationId(id, 'identities'),
                        commonTools.extractLocalId(id))
        if dirty:
            virtualServer.markAllAsDirtyFIXME()

    def vote(self, electionId, voteImport):
        virtualServerId = context.getVar('applicationId')
        virtualServer = self.getVirtualServer(virtualServerId)
        identitiesProxy = getProxyForServerRole('identities')
        try:
            previousVoteToken = identitiesProxy.getElectionVoteToken(
                    electionId)
        except faults.MissingItem:
            previousVoteToken = ''

        if previousVoteToken \
           and virtualServer.voteIds.has_key(previousVoteToken):
            voteId = virtualServer.voteIds[previousVoteToken]
        else:
            voteId = ''

        vote = commonTools.importThing(voteImport)
        if voteId:
            vote.id = voteId

        while 1:
            electionToken = str(self.randomGenerator.uniform(0.1, 1))[2:]
            if not virtualServer.electionIds.has_key(electionToken):
                break
        vote.electionToken = electionToken

        while 1:
            voterToken = str(self.randomGenerator.uniform(0.1, 1))[2:]
            if not virtualServer.voterIds.has_key(voterToken):
                break
        vote.voterToken = voterToken

        while 1:
            voteLocalToken = str(self.randomGenerator.uniform(0.1, 1))[2:]
            voteToken = '%s/votes/%s' % (
                commonTools.extractDispatcherId(virtualServerId),
                voteLocalToken)
            if not virtualServer.voteIds.has_key(voteToken):
                break
        vote.token = voteToken

        if voteId:
            previousVote = getProxyForServerRole('votes').getObject(voteId)
            getProxyForServerRole('votes').modifyObject(vote)
            del virtualServer.electionIds[previousVote.electionToken]
            del virtualServer.voterIds[previousVote.voterToken]
            del virtualServer.voteIds[previousVote.token]
        else:
            voteId = getProxyForServerRole('votes').addObject(
                vote, serverId = commonTools.extractServerId(electionId))

        getProxyForServerRole('identities').vote(electionId, voteToken)
        getProxyForServerRole('elections').vote(
                electionId, voteToken, previousVoteToken)

        virtualServer.electionIds[electionToken] = electionId
        userId = getProxyForServerRole('identities').getUserId()
        virtualServer.voterIds[voterToken] = userId
        virtualServer.voteIds[voteToken] = voteId

        virtualServer.markAllAsDirtyFIXME()
        return voteId


ballotsServer = BallotsServer()


if __name__ == "__main__":
    ballotsServer.launch(applicationName, applicationRole)
