"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/persistent_session.py $
$Id: persistent_session.py 25483 2004-10-28 19:12:25Z nascheme $

Provides persistent versions of Quixote's session management classes.
"""

from time import time

from durus.persistent_dict import PersistentDict
from durus.persistent_list import PersistentList
from quixote.session import SessionManager as QuixoteSessionManager
from quixote.session import Session as QuixoteSession
from dulcinea.attachable import Attachable
from dulcinea.base import DulcineaPersistent
from dulcinea.user import DulcineaUser
from dulcinea import local

class DulcineaSession (DulcineaPersistent, QuixoteSession, Attachable):
    """
    Instance attributes:
      _form_tokens : PersistentList [string]
        (overrides Session's attribute for persistence)
      user : DulcineaUser
        (overrides Session's attribute for stronger typing)
        the user object used for access control checks.  Usually
        user == actual_user, but when an admin user is acting as some other
        user, user is the person being impersonated, and actual_user is
        the person actually doing the work (ie.  the admin user)
      actual_user : DulcineaUser
        (see user attribute)
    """
    user_is = DulcineaUser # not None
    actual_user_is = DulcineaUser # not None

    def __init__ (self, id):
        QuixoteSession.__init__(self, id)
        Attachable.__init__(self)
        self.user = local.get_user_db().get_null_user()
        self.actual_user = self.user
        self._form_tokens = PersistentList()

    def __str__ (self):
        # override Session.__str__ to take actual_user into account
        if not self.actual_user:
            return "session %s (no user)" % self.id
        elif self.user == self.actual_user:
            return "session %s (user %s)" % (self.id, self.user)
        else:
            return "session %s (user %s acting as %s)" % (self.id,
                                                          self.actual_user,
                                                          self.user)
    def clear_app_state(self):
        """Override to clear any session data when acting as another user
        """
        pass

    def has_info (self):
        if self.actual_user or self.user or self._form_tokens:
            return 1
        else:
            return 0

    def set_actual_user (self, user):
        self.user = self.actual_user = user


class DulcineaSessionManager (DulcineaPersistent, QuixoteSessionManager):
    """
    Instance attributes:
      sessions : PersistentDict { session_id:string : DulcineaSession }
        (same as in SessionManager, just made persistent)
    """

    ACCESS_TIME_RESOLUTION = 900 # in seconds (don't dirty the session on
                                 # every hit)

    def __init__ (self, session_class=DulcineaSession, session_mapping=None):
        if session_mapping is None:
            session_mapping = PersistentDict()
        QuixoteSessionManager.__init__(self,
                                       session_class=session_class,
                                       session_mapping=session_mapping)

    def get_sessions(self):
        return self.sessions.values()

    def sorted_keys (self):
        keys = list(self.sessions.keys())
        keys.sort()
        return keys

    def forget_changes (self, session):
        local.get_connection().abort()

    def commit_changes (self, session):
        local.get_connection().commit()

    def del_sessions (self, ip=None, age=None):
        """Delete a subset of open sessions selected according to the
        keyword arguments.  Default values delete all sessions.  Sessions
        are selected by combining non-None keyword args with "and": eg. if
        'ip' and 'age' are non-None then any session matching *both* the
        supplied IP address and age (hours since session was accessed) is
        deleted.

        'ip' specifies an IP address (dotted-quad string); it must match
        the IP address of a session exactly.  'age' is the session's age
        (since last access) in hours; any session older than 'age' will
        pass the age test.

        The number of sessions deleted is returned.
        """
        deleted = 0
        now = time()
        for s in list(self.sessions.values()):
            if ip is not None and ip != s.get_remote_address():
                continue
            if age is not None and s.get_access_age(_now=now) < age*3600:
                continue

            # passed all the tests: we'll delete this sucker
            deleted += 1
            del self.sessions[s.id]

        return deleted




