[Zope-Checkins] CVS: Zope/lib/python/Products/CoreSessionTracking - AutoExpireMapping.py:1.1.2.1 CHANGES.txt:1.1.2.1 README.txt:1.1.2.1 SessionData.py:1.1.2.1 SessionDataContainer.py:1.1.2.1 SessionDataManager.py:1.1.2.1 SessionFanout.py:1.1.2.1 SessionIdManager.py:1.1.2.1 SessionStorage.py:1.1.2.1 SessioningInterfaces.py:1.1.2.1 TODO.txt:1.1.2.1 VERSION.txt:1.1.2.1 __init__.py:1.1.2.1 addDataManager.dtml:1.1.2.1 addExtDataContainer.dtml:1.1.2.1 addIdManager.dtml:1.1.2.1 manageDataManager.dtml:1.1.2.1 manageExtDataContainer.dtml:1.1.2.1 manageIdManager.dtml:1.1.2.1 manageImpExpSessionData.dtml:1.1.2.1 manageIntDataContainer.dtml:1.1.2.1 manageSessionData.dtml:1.1.2.1

Matthew T. Kromer matt@zope.com
Thu, 4 Oct 2001 15:32:28 -0400


Update of /cvs-repository/Zope/lib/python/Products/CoreSessionTracking
In directory cvs.zope.org:/tmp/cvs-serv6625/CoreSessionTracking

Added Files:
      Tag: matt-CoreSessionTracking-branch
	AutoExpireMapping.py CHANGES.txt README.txt SessionData.py 
	SessionDataContainer.py SessionDataManager.py SessionFanout.py 
	SessionIdManager.py SessionStorage.py SessioningInterfaces.py 
	TODO.txt VERSION.txt __init__.py addDataManager.dtml 
	addExtDataContainer.dtml addIdManager.dtml 
	manageDataManager.dtml manageExtDataContainer.dtml 
	manageIdManager.dtml manageImpExpSessionData.dtml 
	manageIntDataContainer.dtml manageSessionData.dtml 
Log Message:
Adding CoreSessionTracking to Products 


=== Added File Zope/lib/python/Products/CoreSessionTracking/AutoExpireMapping.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
import time, string
_marker = []
from Persistence import Persistent
import Acquisition
from zLOG import LOG

class AutoExpireMapping:
    """
    Pure mixin to a session data container or for subclassing.
    
    A mapping which self-manages the expiration of the objects
    it contains based on user-provided timeout value and resolution
    by using a ring of timed buckets.
    """

    def __init__(self, timeout_mins=20, ctype=None, onend='onEnd',
                 err_margin=.20):
        # we give ourselves a 20% error margin by default to reduce
        # the number of buckets necessary
        self._setTimeout(timeout_mins)
        if ctype is None:
            LOG('Session Tracking', 100,
                ('AutoExpireMapping container type set to dictionary... this'
                 ' is likely an error'))
            def ctype():
                return {}
        self._ctype = ctype
        self._setOnEnd(onend)
        self._err_margin = err_margin
        self._reset()
        
    def getTimeoutMinutes(self):
        """ """
        return self._timeout_secs / 60

    def _setTimeout(self, timeout_mins):
        # incoming timeout value is in minutes
        if type(timeout_mins) is not type(1):
            raise "Timeout value must be integer: %s" % timeout_mins
        # convert to seconds and store, timeout must be stored as integer
        self._timeout_secs = timeout_mins * 60
        
    def _setOnEnd(self, onend):
        if type(onend) is not type(''):
            raise "Onend function name must be string, %s" % onend
        if onend[0:1] not in string.letters:
            raise "Onend function name must start with a-z, %s" % onend
        self._onend = onend

    def _reset(self):
        self._resolution_secs = int(self._timeout_secs*self._err_margin) or 1
        now = int(time.time())
        ctype = self._ctype
        numbuckets = self._timeout_secs/self._resolution_secs or 1
        l = []
        i = 0
        for x in range(numbuckets):
            dump_after = now + i
            c = ctype()
            l.insert(0, [c, dump_after])
            i = i + self._resolution_secs
        index = ctype()
        self._ring = Ring(l, index)

    def _getCurrentBucket(self, get_dump=0, time=time.time):
        # no timeout always returns last bucket
        if not self._timeout_secs:
            b, dump_after = self._ring._data[0]
            return b
        index = self._ring._index
        now = int(time())
        i = self._timeout_secs
        # expire all buckets in the ring which have a dump_after time that
        # is before now, turning the ring as many turns as necessary to
        # get to a non-expirable bucket.
        while 1:
            l = b, dump_after = self._ring._data[-1]
            if now > dump_after:
                self._ring.turn()
                # mutate elements in-place in the ring
                new_dump_after = now + i
                l[1] = new_dump_after
                self._clean(b, index)
                i = i + self._resolution_secs
            else:
                break
        if get_dump:
            return self._ring._data[0], dump_after, now
        else:
            b, dump_after = self._ring._data[0]
            return b

    def _clean(self, b, index):
        for ob in b.values():
            d = last = None
            f = getattr(ob, self._onend, None)
            getDataMgr = getattr(ob, 'getDataMgr', None)
            if getDataMgr is not None:
                if callable(getDataMgr):
                    d = getDataMgr()
                if d != last:
                    mgr = self.aq_parent.unrestrictedTraverse(d)
                    last = d
            if callable(f): f(mgr)
        for k, v in list(index.items()):
            if v is b: del index[k]
        b.clear()
        
    def _show(self):
        """ debug method """
        b,dump,now = self._getCurrentBucket(1)
        ringdumps = map(lambda x: `x[1]`[-4:], self._ring)
        t = (
            "now: "+`now`[-4:],
            "dump_after: "+`dump`[-4:],
            "ring_dumps: "+`ringdumps`,
            "ring: " + `self._ring`
            )
        for x in t:
            print x

    def __setitem__(self, k, v):
        current = self._getCurrentBucket()
        index = self._ring._index
        b = index.get(k)
        if b is None:
            # this is a new key
            index[k] = current
        elif b is not current:
            # this is an old key that isn't in the current bucket.
            del b[k] # delete it from the old bucket
            index[k] = current
        # change the value
        current[k] = v
        
    def __getitem__(self, k):
        current = self._getCurrentBucket()
        index = self._ring._index
        # the next line will raise the proper error if the item has expired
        b = index[k]
        v = b[k] # grab the value before we potentially time it out.
        if b is not current:
            # it's not optimal to do writes in getitem, but there's no choice.
            # we accessed the object, so it should become current.
            index[k] = current # change the index to the current bucket.
            current[k] = v # add the value to the current bucket.
            del b[k] # delete the item from the old bucket.
        return v

    def get(self, k, default=_marker):
        try: v = self[k]
        except KeyError: v = _marker
        if v is _marker:
            if default is _marker:
                return None
            else:
                return default
        return v
        
    def __delitem__(self, k):
        self._getCurrentBucket()
        index = self._ring._index
        b = index[k]
        del index[k]
        del b[k]

    def __len__(self):
        self._getCurrentBucket()
        return len(self._ring._index)

    def has_key(self, k):
        self._getCurrentBucket()
        index = self._ring._index
        return index.get(k, _marker) is not _marker

    def values(self):
        return map(lambda k, self=self: self[k], self.keys())

    def items(self):
        return map(lambda k, self=self: (k, self[k]), self.keys())

    def keys(self):
        self._getCurrentBucket()
        index = self._ring._index
        return map(lambda x: x, index.keys())

    def update(self):
        raise "Unsupported"

    def clear(self):
        raise "Unsupported"

    def copy(self):
        raise "Unsupported"

    getLen = __len__
    
class Ring(Persistent):
    """ Instances of this class will be frequently written to the ZODB,
    so it's optimized as best possible for write-friendliness """
    def __init__(self, l, index):
        if not len(l):
            raise "ring must have at least one element"
        self._data = l
        self._index = index

    def __repr__(self):
        return repr(self._data)

    def __len__(self):
        return len(self._data)

    def __getitem__(self, i):
        return self._data[i]

    def turn(self):
        last = self._data.pop(-1)
        self._data.insert(0, last)
        self._p_changed = 1

    def _p_independent(self):
        return 1
    
class PersistentAutoExpireMapping(AutoExpireMapping, Persistent,
                                  Acquisition.Explicit):
    """ Used by SessionFanout """
    pass
    


=== Added File Zope/lib/python/Products/CoreSessionTracking/CHANGES.txt ===

Release 0.8

  - Release 0.8 is incompatible with prior releases due mostly to changing
    of persistent variable names and data structures and the author's
    lack of available resources to write a conversion routine.

  - Added onStart/onEnd events.

  - Improved documentation.

  - Fixed bug reported by Randy Kern in SessionData __setitem__.

  - Overhauled session data object expiration.  This fixed a number of
    problems related to session data objects disappearing unexpectedly
    and sticking around for longer than expected.

  - Fixed bug in SessionFanout __getitem__.

  - Added app-level conflict resolution for SessionData objects (only
    works under 2.3.1 or better).  On average, out of 10,000 requests
    simultaneously handled by 7 threads, there will be fewer than 60
    conflicts, which can usually be resolved readily by Zope's
    automatic request-retry feature.

Release 0.7

  - Added callback handler to close connections properly to handle
    kid-clicking for mounted RAM-based storages.  Thanks to Randall Kern
    for finding and reporting this problem.

Release 0.6

  - Moved all dtml files back to Product root dir because pre-2.3
    Zopes can't handle a dtml directory.

  - Fixed SessionStorage bug that could cause transaction errors.

  - Changed icons.

Release 0.5

  - Use a "SessionFanout" in lieu of having a BTree implementation which
    is not as susceptible to ConflictErrors.  This will need to go away
    when a BTree implementation exists that isn't so susceptible.

  - Did some module name changing

  - Moved dtml files to a subdirectory

  - Created SessionStorage, which is a packless RAM-based storage
    for internal session data containers.  Session Data Managers now
    use a SessionStorage for internal data containers.

  - Added an import/export facility to session data containers.

  - Made session data objects into full mappings.

  - Changed logic of AutoExpireMapping to time objects out faster.

  - Changed token composition so that random portion leads.  Token is
    composed of 8-char str rep of random integer + 11 char base64
    encoded timestamp for a total of 19 chars.

Release 0.4

  - Cleaned up docs and interfaces after r03.

Release 0.3 (Publically unreleased version)

  - Provided a management interface for the internal data container of
    session data managers.

  - Implemented AutoExpireMapping, which session data containers use as
    a base class.  Objects added to session data containers expire
    automatically using inline garbage collection.  Removed sessiongc.py
    from the distribution as a result.

  - Gave session data containers the responsibility for expiring
    session data objects (it had been the responsibility of session data
    managers).

  - Implemented Session Data Container as subobject of mounted
    MappingStorage (it had been implemented as a TransactionalMapping).
    There is now a new class "SessionDataContainerMount" which
    represents a mountpoint that is used for the in-memory
    MappingStorage when an external data container is not used.

Release 0.2

  - Removed dependency on Interface module to allow package to work with
    Zopes which don't have them (like 2.2.3 and 2.2.4).

  - Did some interface rearranging.

  - Slightly improved documentation.

Initial Release (0.1)

  Initial


=== Added File Zope/lib/python/Products/CoreSessionTracking/README.txt ===
 Introduction

  This is a development release of the Zope Core Session Tracking
  implementation.  The software allows you to associate "data objects"
  with anonymous Zope visitors, allowing you to keep state about an
  anonymous user between requests.

  As this is a development release, the API will likely change in a
  final release.  More general information about the design is
  available from
  http://dev.zope.org/Wikis/DevSite/Projects/CoreSesssionTracking/FrontPage.

 Features

  Core Session Tracking key features:

    - RAM-based storage of session data for modest requirements.

    - The storage of session data as a set of persistent Zope objects
      to allow the sessioning implementation to work with a cluster of
      Zope Enterprise Objects (ZEO) servers.

    - Interface-based API.  This means it's possible to implement
      alternate session data managers against things like SQL
      databases or filesystem stores.  The separation of the duties
      between a "session id manager" a "session data manager", and
      a "session data container" in the design makes the implementation
      extensible.

    - Use of cookies or form variables to pass sessioning information.

    - Secure cookie support.

 For installation and usage instructions, see help/CoreSessionTracking.stx.


=== Added File Zope/lib/python/Products/CoreSessionTracking/SessionData.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
"""
Core session tracking SessionData class.

$Id: SessionData.py,v 1.1.2.1 2001/10/04 19:32:27 matt Exp $
"""

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

import time, sys
import Globals
from Persistence import Persistent
import Acquisition
from zLOG import LOG
import ExtensionClass
try:
    from SessioningInterfaces import SessionDataInterface
except:
    # pre-2.3 Zopes don't have an interfaces module
    class SessionDataInterface:
        pass
_marker = []

class SessionData(Persistent, Acquisition.Explicit):
    """ simple session data object """

    __allow_access_to_unprotected_subobjects__ = 1

    __implements__ = SessionDataInterface

    def __init__(self, id, onstart=None, onend=None, datamgr=None,
                 time=time.time):
        self.id = id
        self._datamgr = datamgr
        self._container = {}
        self._created = self._last_accessed = time()
        self._onend = onend
        self._onend_called = 0
        self._onstart = onstart
        self._onstart_called = 0

    def getDataMgr(self):
        return self._datamgr
        
    def onStart(self, sdm):
        if self._onstart and not self._onstart_called:
            onstart = self._onstart.__of__(sdm)
            onstart(self)
            self._onstart_called = 1
        
    def onEnd(self, sdm):
        if self._onend and not self._onend_called:
            onend = self._onend.__of__(sdm)
            onend(self)
            self._onend_called = 1

    def __getitem__(self, k):
        return self._container[k]

    def __setitem__(self, k, v):
        # if the key or value is a persistent instance,
        # set up its _p_jar immediately
        if hasattr(v, '_p_jar') and v._p_jar is None:
            v._p_jar = self._p_jar
            v._p_changed = 1
        if hasattr(k, '_p_jar') and k._p_jar is None:
            k._p_jar = self._p_jar
            k._p_changed = 1
        self._container[k] = v
        self._p_changed = 1

    set = __setitem__

    def __len__(self):
        return len(self._container)
    
    def keys(self):
        return self._container.keys()

    def items(self):
        return self._container.items()

    def values(self):
        return self._container.values()

    def get(self, k, default=None):
        return self._container.get(k, default)
        
    def has_key(self, k, marker=_marker):
        if self._container.get(k, marker) is not _marker: return 1

    def delete(self, k):
        del self._container[k]
        self._p_changed = 1
        
    def getId(self):
        return self.id

    def clear(self):
        self._container.clear()
        self._p_changed = 1

    def update(self, d):
        for k in d.keys():
            self[k] = d[k]

    def copy(self):
        return self._container.copy()

    def getCreated(self):
        return self._created

    def getLastAccessed(self):
        return self._last_accessed

    def setLastAccessed(self, time=time.time):
        self._last_accessed = time()

    def invalidate(self):
        if hasattr(self, 'aq_parent'):
            self.onEnd(self.aq_parent)
        self._invalid = None

    def __repr__(self):
        return `self._container`

    def _p_independent(self):
        # My state doesn't depend on or materially effect the state of
        # other objects.
        return 1

Globals.default__class_init__(SessionData)




=== Added File Zope/lib/python/Products/CoreSessionTracking/SessionDataContainer.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
"""

Session data container

$Id: SessionDataContainer.py,v 1.1.2.1 2001/10/04 19:32:27 matt Exp $
"""

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

import Globals
from Globals import HTMLFile, MessageDialog
from SessionStorage import SessionStorage
from ZODB.DB import DB
from Persistence import Persistent, PersistentMapping
from Acquisition import Implicit
from OFS.SimpleItem import SimpleItem
import threading, os
lock = threading.Lock()
del threading

try:
    # Zope 2.3.1 and up
    from BTrees import OOBTree
    ctype = OOBTree.OOBTree
    del OOBTree
    from AutoExpireMapping import AutoExpireMapping
    SessionContainer = AutoExpireMapping
    del AutoExpireMapping

except ImportError:
    # Zopes 2.3.0 and under
    import BTree
    ctype = BTree.BTree
    del BTree
    from SessionFanout import SessionFanout
    SessionContainer = SessionFanout
    del SessionFanout

try:
    from SessioningInterfaces import SessionDataContainerInterface
except:
    # pre-2.3 Zopes don't have Interfaces module
    class SessionDataContainerInterface:
        pass    

# dbs is mapping of path to in-memory ZODB dbs which hold session data
# containers
dbs = {}

constructSessionDataContainerForm = HTMLFile('addExtDataContainer',
                                             globals())

def constructSessionDataContainer(self, id, title='',timeout_mins=20,
                                  REQUEST=None):
    """ """
    ob = SessionDataContainer(id, title, timeout_mins)
    self._setObject(id, ob)
    if REQUEST is not None:
        return self.manage_main(self, REQUEST, update_menu=1)

class SessionDataContainerErr(Exception): pass

class SessionDataContainer(SimpleItem, SessionContainer):
    """ """
    meta_type = 'Session Data Container'

    icon='misc_/CoreSessionTracking/datacontainer.gif'

    __implements__ = SessionDataContainerInterface

    manage_options=(
        {'label': 'Manage', 'action':'manage_container'},
        {'label': 'Security', 'action':'manage_access'},
        {'label': 'Advanced', 'action':'manage_advanced'}
        )
    
    __ac_permissions__=(
        ('Access session data',
         ['get', 'set', 'has_key', 'getLen'],
         ['Anonymous', 'Manager']),
        ('View management screens',
         ['manage_container', 'manage_access', 'manage_advanced',
          'getTimeoutMinutes'],
         ['Manager']),
        ('Manage Session Data Container',
         ['setTimeoutMinutes', 'setTitle','manage_changeSessionDataContainer',
          'manage_importSessionData', 'manage_exportSessionData'],
         ['Manager']),
        )
    
    def __init__(self, id, title='', timeout_mins=20, ctype=ctype,
                 onend='onEnd', err_margin=.20):
        SessionDataContainer.inheritedAttribute('__init__')(
            self, timeout_mins, ctype, onend, err_margin
            )
        self.id = id
        self.setTitle(title)
        self.setTimeoutMinutes(timeout_mins)
        
    def set(self, k, v):
        """ """
        self[k] = v

    def __len__(self):
        return self.getLen()

    manage_container = HTMLFile('manageExtDataContainer',globals())

    manage_advanced = HTMLFile('manageImpExpSessionData', globals())

    def manage_changeSessionDataContainer(
        self, title, timeout_mins, REQUEST=None
        ):
        """ """
        self.setTitle(title)
        self.setTimeoutMinutes(timeout_mins)
        if REQUEST is not None:
            return self.manage_container(self, REQUEST)

    def manage_exportSessionData(self, REQUEST=None):
        """ """
        f = os.path.join(Globals.data_dir, 'sessiondata.zexp')
        self.c = PersistentMapping()
        for k, v in self.items():
            self.c[k] = v
        get_transaction().commit()
        self.c._p_jar.exportFile(self.c._p_oid, f)
        del self.c
        if REQUEST is not None:
            return MessageDialog(title='Session data exported',
                                 message='Session data exported to %s' % f,
                                 action='manage_container')
        
    def manage_importSessionData(self, REQUEST=None):
        """ """
        f = os.path.join(Globals.data_dir, 'sessiondata.zexp')
        conn = self._p_jar
        ob = conn.importFile(f)
        for k, v in ob.items():
            self[k] = v
        if REQUEST is not None:
            return MessageDialog(title='Session data imported',
                                 message='Session data imported from %s' %f,
                                 action='manage_container')
            

    def setTitle(self, title):
        if not title:
            self.title = ''
        else:
            self.title = str(title)

    def getTimeoutMinutes(self):
        """ """
        return SessionDataContainer.inheritedAttribute('getTimeoutMinutes')(
            self
            )

    def setTimeoutMinutes(self, timeout_mins):
        """ """
        if timeout_mins != self.getTimeoutMinutes():
            # order of calling is important!
            self._setTimeout(timeout_mins)
            self._reset()

    def getSessionDataContainer(self):
        return self

class SessionDataContainerMount(Persistent, Implicit):
    """
    Singleton in-RAM SessionStorage mounted database which returns a
    SessionDataContainer as its single interesting object
    """

    def __init__(self):
        self._clear()
        
    def __setstate__(self, state):
        self._clear()

    def _clear(self):
        self._v_db = None
        self._v_conn = None
        self._v_parentpath = None
        self._v_dc_timeout = None

    def __of__(self, parent):
        parentpath = self._v_parentpath
        if parentpath is None:
            parentpath = self._v_parentpath = parent.getPhysicalPath()
        timeout = self._v_dc_timeout
        if timeout is None:
            timeout = self._v_timeout = parent.internal_dc_timeout
        return self._getContainer(parent, timeout).__of__(parent)
        
    def _getDB(self, parentpath):
        self._v_db = dbs.get(parentpath, None)
        if self._v_db is None:
            lock.acquire()
            try:
                s = SessionStorage()
                dbs[parentpath] = self._v_db = DB(s)
            finally:
                lock.release()
        return self._v_db
        
    def _getConnection(self, parent):
        db = self._v_db
        if db is None:
            db = self._getDB(self._v_parentpath)
        conn = self._v_conn
        if conn is None:
            conn = self._v_conn = db.open() # no versioning
            # if we don't have a jar yet, we steal our parent's
            jar = self._p_jar or parent._p_jar
            jar.onCloseCallback(ConnectionCloser(self, conn))
        return conn
    
    def _getContainer(self, parent, timeout):
        conn = self._getConnection(parent)
        root = conn.root()
        container = root.get('container', None)
        if container is None:
            root['container'] = SessionDataContainer(
                id='internal', timeout_mins=timeout
                )
        return root['container']
        
    def __del__(self):
        lock.acquire()
        try:
            try:
                dbs[self._v_parentpath] = None # in case of circ refs
                del dbs[self._v_parentpath]
            except:
                pass
        finally:
            lock.release()

class ConnectionCloser:
    def __init__(self, mount, conn):
        self.mount = mount
        self.conn = conn
        
    def __call__(self):
        """ This is called by the 'parent' connection's onCloseCallback
        handler, so that our 'spawned' connection will be closed when
        our parent connection is closed. """
        self.mount._v_conn = None
        self.conn.close()
            
Globals.default__class_init__(SessionDataContainer)
Globals.default__class_init__(SessionDataContainerMount)


=== Added File Zope/lib/python/Products/CoreSessionTracking/SessionDataManager.py === (425/525 lines abridged)
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment

[-=- -=- -=- 425 lines omitted -=- -=- -=-]

    references to functions across ZODB databases.
    """
    def __init__(self, fpath, ufolderpath=None, uid=None):
        self.f = fpath
        self.ufolder = ufolderpath
        self.uid = uid
        
    def __call__(self, sdo):
        """ This should never fail with an exception """
        user_changed = 0
        try:
            try:
                try:
                    f = self.unrestrictedTraverse(self.f)
                except:
                    fpath = string.join(self.f, '/')
                    raise SessionDataManagerErr, (
                        'the session event function named %s could not be '
                        'found in the ZODB' % fpath
                        )
                if self.ufolder is not None:
                    user = getSecurityManager().getUser()
                    ufolder = self.unrestrictedTraverse(self.ufolder)
                    u = ufolder.getUser(self.uid)
                    u = getattr(u, 'aq_base', u)
                    u = u.__of__(ufolder)
                    user_changed = 1
                    newSecurityManager(None, u) # switch to saved user
                try:
                    f(sdo)
                except:
                    if hasattr(f, 'id'):
                        if callable(f.id): id = f.id()
                        else: id = f.id
                    else:
                        id = None
                    LOG('Session Tracking',WARNING,'session event failed',
                        'The call to function %s failed.  Traceback:\n'
                        % id, sys.exc_info())
            except:
                LOG('Session Tracking', WARNING, 'session event failed',
                    'Uncaught error\n', sys.exc_info())
        finally:
            if user_changed:
                # switch back to the original user
                newSecurityManager(None, user)


Globals.default__class_init__(SessionDataManager)
Globals.default__class_init__(EventWrapper)


=== Added File Zope/lib/python/Products/CoreSessionTracking/SessionFanout.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
"""

Session fanout in lieu of a new BTree implementation.  This is lame and should
definitely go away after a new BTree implementation is finished.

$Id: SessionFanout.py,v 1.1.2.1 2001/10/04 19:32:27 matt Exp $
"""

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

from AutoExpireMapping import PersistentAutoExpireMapping
from Persistence import Persistent
import Acquisition
_marker = []
allowed_hashes = map(lambda x: "%02i" % x, range(0, 100))

class SessionFanout:
    """
    Pure mixin.
    
    A mapping which stores its items in a number of separate data structures
    in order to reduce the probability of conflict in high-write situations.
    Keys must be strings or integers.  If keys are strings, the first
    two characters of the key must be digits.
    """
    def __init__(self, timeout_mins=20, ctype=None, onend='onEnd',
                 err_margin=.20):
        self._initleaves(timeout_mins, ctype, onend, err_margin)
        self._timeout_secs = timeout_mins * 60
        self._ctype = ctype
        self._onend = onend
        self._err_margin = err_margin
        
    def _setTimeout(self, timeout_mins):
        for leaf in self._root:
            leaf = leaf.__of__(self)
            leaf._setTimeout(timeout_mins)
        self._timeout_secs = timeout_mins * 60
        
    def getTimeoutMinutes(self):
        """ """
        return self._timeout_secs / 60

    def _reset(self):
        for leaf in self._root:
            leaf = leaf.__of__(self)
            leaf._reset()

    def _initleaves(self, timeout_mins, ctype, onend, err_margin):
        self._root = []
        for x in range(0,100):
            m=PersistentAutoExpireMapping(timeout_mins,ctype,onend,err_margin)
            self._root.append(m)
            
    def _getleaf(self, key, allowed_hashes=allowed_hashes):
        if key is not None:
            if type(key) is not type(''):
                key = `key`
            if key[:2] in allowed_hashes:
                lkey = int(key[:2])
                leaf = self._root[lkey]
                return leaf.__of__(self)
            raise 'First two elements in key must be digits'
        else:
            return {}
    
    def __delitem__(self, key):
        leaf = self._getleaf(key)
        del leaf[key]

    def __getitem__(self, key):
        leaf = self._getleaf(key)
        return leaf[key]
        
    def __len__(self):
        l = 0
        for leaf in self._root:
            leaf = leaf.__of__(self)
            l = l + len(leaf)
        return l
        
    getLen = __len__

    # something is screwing up the inheritance of __len__ here, getLen
    # is a workaround

    def __setitem__(self, key, v):
        leaf = self._getleaf(key)
        leaf[key] = v
        
    def copy(self):
        raise 'Unsupported'

    def get(self, key, default=_marker):
        leaf = self._getleaf(key)
        if default is _marker:
            return leaf.get(key)
        else:
            return leaf.get(key, default)

    def has_key(self, key):
        leaf = self._getleaf(key)
        return leaf.has_key(key)
            
    def items(self):
        items = []
        for leaf in self._root:
            leaf = leaf.__of__(self)
            leafitems = map(lambda k,d=leaf: (k, d[k]), leaf.keys())
            items.extend(leafitems)
        items.sort()
        return items

    def keys(self):
        keys = []
        for leaf in self._root:
            leaf = leaf.__of__(self)
            keys.extend(map(lambda k: k, leaf.keys()))
        keys.sort()
        return keys
    
    def update(self, b):
        raise 'Unsupported'

    def values(self):
        values = []
        for leaf in self._root:
            leaf = leaf.__of__(self)
            values.extend(map(lambda k: k, leaf.values()))
        values.sort()
        return values



=== Added File Zope/lib/python/Products/CoreSessionTracking/SessionIdManager.py === (458/558 lines abridged)
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment

[-=- -=- -=- 458 lines omitted -=- -=- -=-]

        TimeStamp=TimeStamp.TimeStamp, translate=string.translate
        ):
        t=time()
        ts=split(b2a(`apply(TimeStamp,(gmtime(t)[:5]+(t%60,)))`)[:-1],'=')[0]
        return translate(ts, b64_trans)

    def _getB64TStampToInt(
        self, ts, TimeStamp=TimeStamp.TimeStamp, b64_untrans=b64_untrans,
        a2b=binascii.a2b_base64, translate=string.translate
        ):
        return TimeStamp(a2b(translate(ts+'=',b64_untrans))).timeTime()

    def _getTokenPieces(self, token):
        """ returns session token parts in a tuple consisting of rand_id,
        timestamp
        """
        return (token[:8], token[8:19])

    def _isAWellFormedToken(self, token, binerr=binascii.Error,
                            timestamperr=TimeStamp.error):
        try:
            rnd, ts = self._getTokenPieces(token)
            int(rnd)
            self._getB64TStampToInt(ts)
            return token
        except (TypeError, ValueError, AttributeError, IndexError, binerr,
                timestamperr):
            return None

    def _setId(self, id):
        if id != self.id:
            raise Globals.MessageDialog(
                title='Cannot rename',
                message='You cannot rename a session id manager, sorry!',
                action ='./manage_main',)


Globals.default__class_init__(SessionIdManager)














=== Added File Zope/lib/python/Products/CoreSessionTracking/SessionStorage.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################

"""
A storage implementation which uses RAM to persist objects, much like
MappingStorage, but unlike MappingStorage needs not be packed to get rid of
non-cyclic garbage.  This is a ripoff of Jim's Packless bsddb3 storage.

$Id: SessionStorage.py,v 1.1.2.1 2001/10/04 19:32:27 matt Exp $
"""

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

from struct import pack, unpack
from ZODB.referencesf import referencesf
from ZODB import POSException
from ZODB.BaseStorage import BaseStorage

class ReferenceCountError(POSException.POSError):
    """ An error occured while decrementing a reference to an object in
    the commit phase. The object's reference count was below zero."""

class SessionStorageError(POSException.POSError):
    """ A Session Storage exception occurred.  This probably indicates that
    there is a low memory condition or a tempfile space shortage.  Check
    available tempfile space and RAM consumption and restart the server
    process."""

class SessionStorage(BaseStorage):

    def __init__(self, name='SessionStorage'):
        """
        index -- mapping of oid to current serial
        referenceCount -- mapping of oid to count
        oreferences -- mapping of oid to a sequence of its referenced oids
        opickle -- mapping of oid to pickle
        """
        BaseStorage.__init__(self, name)

        self._index={}
        self._referenceCount={}
        self._oreferences={}
        self._opickle={}
        self._tmp = []
        self._oid = '\0\0\0\0\0\0\0\0'

    def __len__(self):
        return len(self._index)

##     def new_oid(self, last=None):
##         return BaseStorage.new_oid(self, last)

    def getSize(self):
        return 0

    def _clear_temp(self):
        self._tmp = []
        
    def close(self):
        """
        Close the storage
        """
    
    def load(self, oid, version):
        self._lock_acquire()
        try:
            try:
                s=self._index[oid]
                p=self._opickle[oid]
                return p, s # pickle, serial
            except:
                raise SessionStorageError, (
                    "%s (%s)" % (SessionStorageError.__doc__, `oid`)
                    )
        finally:
            self._lock_release()

    def store(self, oid, serial, data, version, transaction):
        if transaction is not self._transaction:
            raise POSException.StorageTransactionError(self, transaction)
        if version:
            raise POSException.Unsupported, "Versions aren't supported"

        self._lock_acquire()
        try:
            if self._index.has_key(oid):
                oserial=self._index[oid]
                if serial != oserial: raise POSException.ConflictError
                
            serial=self._serial
            self._tmp.append((oid, data))
            return serial
        finally:
            self._lock_release()


    def _finish(self, tid, u, d, e):
        zeros={}
        referenceCount=self._referenceCount
        referenceCount_get=referenceCount.get
        oreferences=self._oreferences
        serial=self._serial
        index=self._index
        opickle=self._opickle
        tmp=self._tmp
        oidlen=8 # length in bytes of oid string rep
        intlen=4 # length in bytes of struct.packed integer string rep
        for entry in tmp:
            oid, data = entry[:]
            referencesl=[]
            referencesf(data, referencesl)
            references={}
            for roid in referencesl: references[roid]=1
            referenced=references.has_key
            # Create refcnt
            if not referenceCount_get(oid):
                referenceCount[oid] = 0
                zeros[oid]=1
            # update stored references
            roids = oreferences.get(oid)
            if roids is not None:
                for roid in roids:
                    if referenced(roid):
                        # still referenced, so no need to update
                        del references[roid]
                    else:
                        # Delete the stored ref, since we no longer
                        # have it
                        roids.remove(roid)
                        # decrement refcnt:
                        rc = referenceCount_get(roid)
                        rc=rc-1
                        if rc < 0:
                            # This should never happen
                            raise ReferenceCountError, (
                                "%s (Oid %s had refcount %s)" %
                                (ReferenceCountError.__doc__,`roid`,rc)
                                )
                        referenceCount[roid] = rc
                        if rc==0: zeros[roid]=1

            # Now add any references that weren't already stored:
            if oreferences.get(oid) is None:
                oreferences[oid] = []
            for roid in references.keys():
                oreferences[oid].append(roid)
                # Create/update refcnt
                rc=referenceCount_get(roid)
                if rc is None:
                    referenceCount[roid] = 1
                elif rc==0:
                    try: del zeros[roid]
                    except: pass
                    referenceCount[roid] = rc+1
                else:
                    referenceCount[roid] = rc+1

            index[oid] =  serial
            opickle[oid] = data

        if zeros:
            for oid in zeros.keys():
                if oid == '\0\0\0\0\0\0\0\0': continue
                self._takeOutGarbage(oid)

        self._tmp = []

    def _takeOutGarbage(self, oid):
        # take out the garbage.
        referenceCount=self._referenceCount
        referenceCount_get=referenceCount.get
        del referenceCount[oid]
        del self._opickle[oid]
        del self._index[oid]

        # Remove/decref references
        roids = self._oreferences[oid]
        while roids:
            roid = roids.pop(0)
            # decrement refcnt:
            rc=referenceCount_get(roid)
            if rc:
                if rc < 0:
                    rce = ReferenceCountError
                    raise rce, (
                        "%s (Oid %s had refcount %s)" %
                        (rce.__doc__,`roid`,rc)
                        )
                if rc==0: self._takeOutGarbage(roid)
                else: referenceCount[roid] = rc

    def pack(self, t, referencesf):
        self._lock_acquire()
        try:
            rindex={}
            referenced=rindex.has_key
            rootl=['\0\0\0\0\0\0\0\0']

            # mark referenced objects
            while rootl:
                oid=rootl.pop()
                if referenced(oid): continue
                p = self._opickle[oid]
                referencesf(p, rootl)
                rindex[oid] = None

            # sweep unreferenced objects
            for oid in self._index.keys():
                if not referenced(oid):
                    self._takeOutGarbage(oid)
        finally:
            self._lock_release()



    



=== Added File Zope/lib/python/Products/CoreSessionTracking/SessioningInterfaces.py === (467/567 lines abridged)
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment

[-=- -=- -=- 467 lines omitted -=- -=- -=-]


        Permission required: Access session data
        """

    def __len__(self):
        """
        Returns number of items in container.

        Permission required: Access session data
        """

    def getLen(self):
        """
        Returns number of items in container.  Callable from TTW code.

        Permission required: Access session data
        """

    def __setitem__(self, key, v):
        """
        Sets item in container with key 'key' and value 'v'

        Permission required: Access session data
        """

    def get(self, key, default=None):
        """
        Returns value referred to by key or default if no value with key.

        Permission required: Access session data
        """

    def has_key(self, key):
        """
        Returns true if container has key.

        Permission required: Access session data
        """

    def set(self, k, v):
        """
        Set key k to value v.

        Permission required: Access session data
        """







=== Added File Zope/lib/python/Products/CoreSessionTracking/TODO.txt ===
Post release-8:

  - Test heavily with ZEO.

  - Figure out how to default-instantiate a sessioning environment for
    new Zopes.
 
  - Figure out how to better integrate CST into Zope in an out-of-the-box
    configuration.

  - Solve "None has no attribute 'load'" errors when accessing a
    mounted storage under load.  If you see this error, please help!

  - Put out-of-memory protection in.



=== Added File Zope/lib/python/Products/CoreSessionTracking/VERSION.txt ===
0.8


=== Added File Zope/lib/python/Products/CoreSessionTracking/__init__.py ===
##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################
"""
Core session tracking initialization routines

$Id: __init__.py,v 1.1.2.1 2001/10/04 19:32:27 matt Exp $
"""
import SessionIdManager, SessionDataManager, SessionDataContainer

def initialize(context):
    context.registerClass(
        SessionIdManager.SessionIdManager,
        permission='Add Session Id Manager',
        icon='www/idmgr.gif',
        constructors=(SessionIdManager.constructSessionIdManagerForm,
                      SessionIdManager.constructSessionIdManager)
        )
    context.registerClass(
        SessionDataManager.SessionDataManager,
        permission='Add Session Data Manager',
        icon='www/datamgr.gif',
        constructors=(SessionDataManager.constructSessionDataManagerForm,
                      SessionDataManager.constructSessionDataManager)
        )
    context.registerClass(
        SessionDataContainer.SessionDataContainer,
        permission='Add External Session Data Container',
        icon='www/datacontainer.gif',
        constructors=(SessionDataContainer.constructSessionDataContainerForm,
                      SessionDataContainer.constructSessionDataContainer)
        )
    context.registerHelp()








=== Added File Zope/lib/python/Products/CoreSessionTracking/addDataManager.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Add Session Data Manager</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<H2>Add Session Data Manager</H2>
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
<FORM ACTION="constructSessionDataManager" METHOD="POST">
<em>Zope Session Data Managers objects keep track of your users' session data objects.  Developers interact with a Session Data Manager in order to store and retrieve information during a user session.  A Session Data Manager communicates with a Session Id Manager to determine the session information for the current user, and hands out Session Data Objects related to that user obtained from a Session Data Container. </em><br><br> 
<TABLE CELLSPACING="2">
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Id</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="id" SIZE="20">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Title</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="title" SIZE="40">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>
    Internal Session Data Container Object Timeout (in minutes)
    </EM></STRONG>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="timeout_mins:int" SIZE="10" value="20">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>External Session Data Container Path (optional)<br></strong> (e.g. '/nonundo_db/session_data_container'.<br>  leave blank in order to use default internal <br>ram-based session data container)</EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="path" SIZE="60" value="">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session onStart method path (optional)<br></strong>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="onstart" SIZE="60" value="">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session onEnd method path (optional)<br></strong>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="onend" SIZE="60" value="">
  </TD>
</TR>
<tr>
</TR> 

<TR>
  <td align="left" valign="top">
	<em><strong>REQUEST Session Name</strong> (what name session is stored in REQUEST as)</em>
  </td>
  <td align="left" valign="top">
  	<input type="text" name="requestSessionName" value="SESSION">
  </td>
</TR>


<TR> 
  <TD>
  </TD> 
  <TD> <BR><INPUT TYPE="SUBMIT" VALUE=" Add "> </TD> 
</TR> 
</TABLE> 
</FORM> 
</BODY> 
</HTML>



=== Added File Zope/lib/python/Products/CoreSessionTracking/addExtDataContainer.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Add External Session Data Container</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<H2>Add External Session Data Container</H2>
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
<FORM ACTION="constructSessionDataContainer" METHOD="POST">
<em>External Session Data Containers can be used in conjunction with Session Data Managers to hold Session Data Objects.  Session Data Managers by default use a simple RAM-based "internal" Session Data Container.  The use of an External Session Data Container is recommended when dealing with large and performance-intensive sites.  They can store truly persistent session data (which lasts between Zope server restarts).  External Session Data Containers are <b>required</b> when using Zope's sessioning features in conjunction with Zope Enterprise Objects (ZEO).  It is recommended that External Session Data Containers be added to a non-undo ZODB database, as they are write-intensive.</em><br><br> 
<TABLE CELLSPACING="2">
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Id</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="id" SIZE="20">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Title</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="title" SIZE="40">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Data object timeout in minutes</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="timeout_mins:int" SIZE="10" value="20">
  </TD>
</TR>
<TR> 
  <TD>
  </TD> 
  <TD> <BR><INPUT TYPE="SUBMIT" VALUE=" Add "> </TD> 
</TR> 
</TABLE> 
</FORM> 
</BODY> 
</HTML>


=== Added File Zope/lib/python/Products/CoreSessionTracking/addIdManager.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Add Session Id Manager</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<H2>Add Session Id Manager</H2>

<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">

<FORM ACTION="constructSessionIdManager" METHOD="POST">
<input type=hidden name="id" value="session_id_mgr">
<em>Zope Session Id Manager objects perform the task of setting and retrieving Zope session ids for remote users.  They are used primarily by Session Data Manager objects.  A Session Id Manager's 'id' must always be 'session_id_mgr' in order for it to be found by Session Data Managers.</em><br><br>

<TABLE CELLSPACING="2">
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Id</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <strong>This object's Zope id will be<br>
    "session_id_mgr"</strong>
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Title</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="title" SIZE="40">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session Token Key</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="tokenkey" SIZE="20" value="_ZopeId">
  </TD>
</TR>
<tr>
<td>&nbsp;</td>
</tr>
<th align="left"><strong><em>Token Key Search Namespaces</strong></em></th>
<th align="left"><strong><em>Priority (1 is highest)</strong></em></th>
<tr>
  <th align=left>Cookies</th>
   <td>
   <table border=1>
   <tr>
   <td align=left>
    <input type="radio" name="cookiepri:int" value="1" CHECKED> 1
   </td>
   <td align=left>
    <input type="radio" name="cookiepri:int" value="2"> 2
   </td>
   <td align=left>
    <input type="radio" name="cookiepri:int" value="0"> Off
   </td>
   </tr>
   </table>
  </td>
</tr>
<tr>
  <th align=left>Form vars</th>
   <td align=left>
   <table border=1>
   <tr>
   <td align=left>
    <input type="radio" name="formpri:int" value="1"> 1
   </td>
   <td align=left>
    <input type="radio" name="formpri:int" value="2" CHECKED> 2
   </td>
   <td align=left>
    <input type="radio" name="formpri:int" value="0"> Off
   </td>   </tr>
   </table>
  </td>
</tr>
<td>&nbsp;</td>
<tr>
</tr>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Cookie Path <br></strong>(leave blank to provide no path info in the <br>session cookie)</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="cookiepath" SIZE="20" value="/">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Cookie Domain<br></strong>(leave blank to send cookies without domain <br>info -- however, if cookie domain is not blank,<br> it must contain at least two dots)</EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="cookiedomain" SIZE="20">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Cookie Lifetime In Days</STRONG><br>    (0 means send cookies which last only for the<br>lifetime of the browser)</STRONG></EM>
</EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="cookielifedays:int" SIZE="20" value="0">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Only Send Cookie Over HTTPS</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="checkbox" NAME="cookiesecure">
  </TD>
</TR>


</TR> <TR> <TD></TD> <TD> <BR><INPUT TYPE="SUBMIT" VALUE=" Add ">
</TD> </TR> </TABLE> </FORM> </BODY> </HTML>



=== Added File Zope/lib/python/Products/CoreSessionTracking/manageDataManager.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Manage Session Data Manager</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-var manage_tabs>
<table>
<tr>
<td>
<H2>Manage Session Data Manager</H2>
</td>
<td width="40%">
&nbsp;
</td>
<td align="right">
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
</td>
</tr>
</table>
<FORM ACTION="manage_changeSessionDataManager" METHOD="POST">
<TABLE CELLSPACING="2">
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Title</strong></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="title" SIZE="60" value="&dtml-title;">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>External Session Data Container Path <br></strong>(e.g. '/nonundo_db/session_data_container'.  <br>leave blank in order to use default internal <br>ram-based session data container)</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="path" SIZE="60" value="&dtml-getSessionDataContainerPath;">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session onStart method path<br></strong>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="onstart" SIZE="60" value="&dtml-getOnStartPath;">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session onEnd method path<br></strong>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="onend" SIZE="60" value="&dtml-getOnEndPath;">
  </TD>
</TR>

<TR>
  <td align="left" valign="top">
        <em><strong>REQUEST Session Name</strong> (what name session is stored in REQUEST as)</em>
  </td>
  <td align="left" valign="top">
        <input type="text" name="requestSessionName" value="&dtml-getRequestSessionName;">
  </td>
</TR>



</TR> <TR> <TD></TD> <TD> <BR><INPUT TYPE="SUBMIT" VALUE=" Change ">
</TD> </TR> </TABLE> </FORM> </BODY> </HTML>



=== Added File Zope/lib/python/Products/CoreSessionTracking/manageExtDataContainer.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>External Session Data Container</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-var manage_tabs>

<table>
<tr>
<td>
<H2>External Session Data Container</H2>
</td>
<td width="40%">
&nbsp;
</td>
<td align="right">
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
</td>
</tr>
</table>

<table>
<form action="manage_changeSessionDataContainer" method="post">
<tr>
  <td align="left" valign="top">
  <font color="green">
  <dtml-let l=getLen>
  <dtml-if l>
    <dtml-if "l == 1">1 item is in this session data container.
    <dtml-else><dtml-var l> items are in this session data container.
    </dtml-if>
  <dtml-else>
    There are no items in this session data container.
  </dtml-if>
  </dtml-let>
  </font>
  </td>
</tr>
<tr>
  <td>
   &nbsp;
  </td>
</tr>
<tr>
  <td align="left" valign="top">
    <em><strong>Title</em></strong>
  </td>
  <td align="left" valign="top">
    <input type="text" name="title" size=20 value=&dtml-title;>
  </td>
</tr>
<tr>
  <td align="left" valign="top">
    <em><strong>Data object timeout value in minutes</em></strong>
  </td>
  <td align="left" valign="top">
    <input type="text" name="timeout_mins:int" size=10
     value=&dtml-getTimeoutMinutes;>
  </td>
</tr>
<tr>
  <td>
   &nbsp;
  </td>
</tr>
<tr>
  <td align="center" valign="top">
  <input type=submit name=submit value=" Change ">
  </td>
</tr>
<tr>
  <td>
   &nbsp;
  </td>
</tr>
<tr>
  <td align="left" valign="top">
  <strong><font color="red">WARNING!</font></strong>
  The data objects currently existing in this session data container<br>
  will be deleted when the data object timeout is changed.
  </td>
</tr>
</form>
</table>


=== Added File Zope/lib/python/Products/CoreSessionTracking/manageIdManager.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Session Id Manager Settings</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-var manage_tabs>
<H2>Session Id Manager Settings</H2>

<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
<FORM ACTION="manage_changeSessionIdManager" METHOD="POST">
<TABLE CELLSPACING="2">
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session Id Mgr On</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="checkbox" NAME="on"
     <dtml-if isOn>CHECKED</dtml-if>>
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Title</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="title" SIZE="30" value="<dtml-var title html_quote>">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Session Token Key</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="tokenkey" SIZE="20"
     value="<dtml-var getTokenKey html_quote>">
  </TD>
</TR>
<th align=left><strong><em>Token Key Search Namespaces</strong></em></th>
<th align=left><strong><em>Priority</strong></em> (1 is highest)</th>
<tr>
  <th align=left>Cookies</th>
   <td align=left>
   <table border=1>
   <tr>
   <td align=left>
    <input type="radio" name="cookiepri:int" value="1"
     <dtml-if "getTokenKeyNamespaces().get(1, _.None) == 'cookies'">CHECKED</dtml-if>>1
   </td>
   <td align=left>
    <input type="radio" name="cookiepri:int" value="2"
     <dtml-if "getTokenKeyNamespaces().get(2, _.None) == 'cookies'">CHECKED</dtml-if>>2
   </td>
   <td align=left>
    <input type="radio" name="cookiepri:int" value="0"
    <dtml-if "'cookies' not in getTokenKeyNamespaces().values()">CHECKED</dtml-if>>Off
   </td>
   </tr>
   </table>
   </td>
</tr>
<tr>
  <th align=left>Form vars</th>
   <td align=left>
   <table border=1>
   <tr>
   <td align=left>
    <input type="radio" name="formpri:int" value="1"
     <dtml-if "getTokenKeyNamespaces().get(1, _.None) == 'form'">CHECKED</dtml-if>>1
   </td>
   <td align=left>
    <input type="radio" name="formpri:int" value="2"
     <dtml-if "getTokenKeyNamespaces().get(2, _.None) == 'form'">CHECKED</dtml-if>>2
   </td>
   <td align=left>
    <input type="radio" name="formpri:int" value="0"
    <dtml-if "'form' not in getTokenKeyNamespaces().values()">CHECKED</dtml-if>>Off
   </td>
   </tr>
   </table>
   </td>
</tr>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Cookie Path<br></strong>(leave blank to provide no path info in the <br>session cookie)</EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="cookiepath" SIZE="20"
     value="<dtml-var getCookiePath html_quote>">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Cookie Domain</strong><br>
    (leave blank to send cookies without domain <br>info -- however, if cookie domain is not blank,<br> it must contain at least two dots)</EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="cookiedomain" SIZE="20"
     value="<dtml-var getCookieDomain html_quote>">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Cookie Lifetime In Days<br></strong>
    (0 means send cookies which last only for the<br>lifetime of the browser)</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="TEXT" NAME="cookielifedays:int" SIZE="20"
     value="<dtml-var getCookieLifeDays html_quote>">
  </TD>
</TR>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <EM><STRONG>Only Send Cookie Over HTTPS</STRONG></EM>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="checkbox" NAME="cookiesecure"
     <dtml-if getCookieSecure>CHECKED</dtml-if>>
  </TD>
</TR>
<TR>
  <TD></TD>
  <TD><BR><INPUT TYPE="SUBMIT" VALUE=" Change "></TD>
</TR>
</TABLE>
</FORM>
</BODY>
</HTML>



=== Added File Zope/lib/python/Products/CoreSessionTracking/manageImpExpSessionData.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Advanced</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-var manage_tabs>
<table>
<tr>
<td>
<H2>Advanced Session Data Container Options</H2>
</td>
<td width="40%">
&nbsp;
</td>
<td align="right">
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
</td>
</tr>
</table>
<h3>Import/Export Session Data</h3>
<div>
Session data will be imported/exported to/from the file 
'var/sessiondata.zexp' on your server's hard disk in your Zope
directory.
</div>
<br>
<table>
<tr>
  <td align="left" valign="top">
    <form action="manage_exportSessionData" method="post">
      <input type=submit name=submit value="Export Session Data">
    </form>
  </td>
  <td width="20%">&nbsp;</td>
  <td align="left" valign="top">
    <form action="manage_importSessionData" method="post">
      <input type=submit name=submit value="Import Session Data">
    </form>
  </td>
</tr>
</table>
</html>



=== Added File Zope/lib/python/Products/CoreSessionTracking/manageIntDataContainer.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Internal Session Data Container</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-var manage_tabs>

<table>
<tr>
<td>
<H2>Internal Session Data Container</H2>
</td>
<td width="40%">
&nbsp;
</td>
<td align="right">
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
</td>
</tr>
</table>

<table>
<form action="manage_changeInternalDC" method="post">
<tr>
  <td align="left" valign="top">
  <font color="green">
  <dtml-let l=getInternalDCLen>
  <dtml-if l>
    <dtml-if "l == 1">1 item is in this session data container.
    <dtml-else><dtml-var l> items are in this session data container.
    </dtml-if>
  <dtml-else>
    There are no items in this session data container.
  </dtml-if>
  </dtml-let>
  </font>
  </td>
</tr>
<tr>
  <td>
   &nbsp;
  </td>
</tr>
<tr>
  <td align="left" valign="top">
    <em><strong>Data object timeout value in minutes</em></strong>
  </td>
  <td align="left" valign="top">
    <input type="text" name="timeout_mins:int" size=10
     value=&dtml-getInternalDCTimeoutMins;>
  </td>
</tr>
<tr>
  <td>
   &nbsp;
  </td>
</tr>
<tr>
  <td align="center" valign="top">
  <input type=submit name=submit value=" Change ">
  </td>
</tr>
<tr>
  <td>
   &nbsp;
  </td>
</tr>
<tr>
  <td align="left" valign="top">
  <strong><font color="red">WARNING!</font></strong>
  The data objects currently existing in this session data container<br>
  will be deleted when the data object timeout is changed.
  </td>
</tr>
</form>
</table>


=== Added File Zope/lib/python/Products/CoreSessionTracking/manageSessionData.dtml ===
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML lang="en">
<HEAD>
<TITLE>Manage Session Manager Data</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<dtml-var manage_tabs>
<table>
<tr>
<td>
<H2>Manage Session Manager Data</H2>
</td>
<td width="40%">
&nbsp;
</td>
<td align="right">
<dtml-var "HelpSys.HelpButton('CoreSessionTracking.stx','CoreSessionTracking')">
</td>
</tr>
</table>
<FORM ACTION="manage_deleteInvalidSessionData" METHOD="POST">
<TABLE CELLSPACING="2">
<tr>
  <td align="left" valign="top">
  <font color="green">&dtml-getNumberOfValidSessionDataObjects; session data objects are valid out of a total of &dtml-getNumberOfSessionDataObjects; that are currently in the session data container <dtml-if getSessionDataContainerPath><strong>(session data objects are currently being stored in the external session data container at &dtml-getSessionDataContainerPath;)</strong><dtml-else><strong>(session data objects are currently being stored in a ram-based data object container internal to this session manager)</strong></dtml-if>.</font><br><br>
  </td>
</tr>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <STRONG>Delete <em>invalid</em> session data objects</STRONG>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="submit" NAME="go" value=" Delete Invalid ">
  </TD>
</TR>
<tr>
<td>&nbsp;</td>
</tr>
<TR>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <STRONG>Delete <em>all</em> session data objects</STRONG>
  </TD>
  <TD ALIGN="LEFT" VALIGN="TOP">
    <INPUT TYPE="submit" NAME="deleteall" value="     Delete All     ">
  </TD>
</TR>
</table>
</form>