view libervia.tac @ 0:140cec48224a

Initial commit
author Goffi <goffi@goffi.org>
date Sun, 30 Jan 2011 21:50:22 +0100
parents
children 0a7c685faa53
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Libervia: a Salut à Toi frontend
Copyright (C) 2011  Jérôme Poisson (goffi@goffi.org)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from twisted.application import internet, service
from twisted.internet import glib2reactor
glib2reactor.install()
from twisted.internet import reactor, defer

from twisted.web import server
from twisted.web import error as weberror
from twisted.web.static import File
from twisted.web.resource import Resource
from txjsonrpc.web import jsonrpc
from txjsonrpc import jsonrpclib
from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService

TIMEOUT = 120 #Session's time out, after that the user will be disconnected


class MethodHandler(jsonrpc.JSONRPC):

    def __init__(self, sat_host):
        jsonrpc.JSONRPC.__init__(self)
        self.sat_host=sat_host

    def render(self, request):
        _session = request.getSession()
        try:
            profile = _session.sat_profile
        except AttributeError:
            #user is not identified, we return a jsonrpc fault
            parsed = jsonrpclib.loads(request.content.read())
            fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia
            return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))
        return jsonrpc.JSONRPC.render(self, request)
        

    def jsonrpc_getContacts(self):
        """Return all passed args."""
        d = defer.Deferred()
        reactor.callLater(10, d.callback, [unicode(contact[0]) for contact in self.sat_host.bridge.getContacts()])
        return d

class Register(jsonrpc.JSONRPC):
    """This class manage the registration procedure with SàT
    It provide an api for the browser, check password and setup the web server"""

    def __init__(self, sat_host):
        jsonrpc.JSONRPC.__init__(self)
        self.sat_host=sat_host
        self.profiles_waiting={}
        self.request=None

    def getWaitingRequest(self, profile):
        """Tell if a profile is trying to log in"""
        if self.profiles_waiting.has_key(profile):
            return self.profiles_waiting[profile]
        else:
            return None

    def render(self, request):
        """
        Render method with some hacks:
           - if login is requested, try to login with form data
           - except login, every method is jsonrpc
           - user doesn't need to be authentified for isRegistered, but must be for all other methods
        """
        if request.postpath==['login']:
            return self.login(request)
        _session = request.getSession()
        parsed = jsonrpclib.loads(request.content.read())
        if parsed.get("method")!="isRegistered":
            #if we don't call login or isRegistered, we need to be identified
            try:
                profile = _session.sat_profile
            except AttributeError:
                #user is not identified, we return a jsonrpc fault
                fault = jsonrpclib.Fault(0, "Not allowed") #FIXME: define some standard error codes for libervia
                return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))
        self.request = request
        return jsonrpc.JSONRPC.render(self, request)

    def login(self, request):
        """
        this method is called with the POST information from the registering form
        it test if the password is ok, and log in if it's the case,
        else it return an error
        @param request: request of the register formulaire, must have "login" and "password" as arguments
        @return: A constant indicating the state:
            - BAD REQUEST: something is wrong in the request (bad arguments, profile_key for login)
            - AUTH ERROR: either the profile or the password is wrong
            - ALREADY WAITING: a request has already be made for this profile
            - server.NOT_DONE_YET: the profile is being processed, the return value will be given by self._logged or self._logginError
        """ 
        try:
            _login = request.args['login'][0]
            if _login.startswith('@'):
                raise Exception('No profile_key allowed')
            _pass = request.args['password'][0]
        except KeyError:
            return "BAD REQUEST"

        _profile_check = self.sat_host.bridge.getProfileName(_login)
        _profile_pass = self.sat_host.bridge.getParamA("Password", "Connection", profile_key=_login)

        if not _profile_check or _profile_check != _login or _profile_pass != _pass:
            return "AUTH ERROR"
        
        if self.profiles_waiting.has_key(_login):
            return "ALREADY WAITING"
        
        if self.sat_host.bridge.isConnected(_login):
            return self._logged(_login, request)

        self.profiles_waiting[_login] = request
        self.sat_host.bridge.connect(_login) 
        return server.NOT_DONE_YET

    def __cleanWaiting(self, login):
        """Remove login from waiting queue"""
        try:
            del self.profiles_waiting[login]
        except KeyError:
            pass

    def _logged(self, login, request):
        """Set everything when a user just logged
        and return "LOGGED" to the requester"""
        self.__cleanWaiting(login)
        _session = request.getSession()
        _session.sat_profile = login
        return 'LOGGED'

    def _logginError(self, login, request, error_type):
        """Something went wrong during loggin, return an error"""
        self.__cleanWaiting(login)
        return error_type

    def jsonrpc_isConnected(self):
        _session = self.request.getSession()
        profile = _session.sat_profile
        return self.sat_host.bridge.isConnected(profile)
    
    def jsonrpc_connect(self):
        _session = self.request.getSession()
        profile = _session.sat_profile
        if self.profiles_waiting.has_key(profile):
            raise jsonrpclib.Fault('1','Already waiting') #FIXME: define some standard error codes for libervia
        self.profiles_waiting[profile] = self.request
        self.sat_host.bridge.connect(profile) 
        return server.NOT_DONE_YET
    
    def jsonrpc_isRegistered(self):
        """Tell if the user is already registered"""
        _session = self.request.getSession()
        try:
            profile = _session.sat_profile
        except AttributeError:
            return False
        return True
       
class SignalHandler(jsonrpc.JSONRPC):
    
    def __init__(self, sat_host):
        Resource.__init__(self)
        self.register=None
        self.sat_host=sat_host
    
    def plugRegister(self, register):
        self.register = register
        
    def presenceUpdate(self, entity, show, priority, statuses, profile):
        print "update de",entity

    def connected(self, profile):
        assert(self.register) #register must be plugged
        request = self.register.getWaitingRequest(profile)
        if request:
            self.register._logged(profile, request)

    def connectionError(self, error_type, profile):
        assert(self.register) #register must be plugged
        request = self.register.getWaitingRequest(profile)
        if request: #The user is trying to log in
            if error_type == "AUTH_ERROR":
                _error_t = "AUTH ERROR"
            else:
                _error_t = "UNKNOWN"
            self.register._logginError(profile, request, _error_t)


class Libervia(service.Service):
   
    def __init__(self):
        root = File("output/") 
        self.signal_handler = SignalHandler(self)
        root.putChild('test', self.signal_handler)
        _register = Register(self)
        self.signal_handler.plugRegister(_register)
        root.putChild('json_api', MethodHandler(self))
        root.putChild('register_api', _register)
        self.site = server.Site(root)
        self.sessions = {} #key = session value = user
        ## bridge ##
        try:
            self.bridge=DBusBridgeFrontend()
        except BridgeExceptionNoService:
            print(u"Can't connect to SàT backend, are you sure it's launched ?")
            import sys
            sys.exit(1)
        self.bridge.register("presenceUpdate", self.signal_handler.presenceUpdate)
        self.bridge.register("connected", self.signal_handler.connected)
        self.bridge.register("connectionError", self.signal_handler.connectionError)

    def startService(self):
        reactor.listenTCP(8080, self.site)

    def run(self):
        debug(_("running app"))
        reactor.run()
    
    def stop(self):
        debug(_("stopping app"))
        reactor.stop()



application = service.Application('Libervia')
service = Libervia()
service.setServiceParent(application)