summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Wrobel <wrobel@gentoo.org>2005-09-04 16:48:35 +0000
committerGunnar Wrobel <wrobel@gentoo.org>2005-09-04 16:48:35 +0000
commitd0149e1cb4b6d718c5a996d0d7f3849dfa322b68 (patch)
treed0eb42bdf5b959ea594f032cc05098472c685468 /www-apps/pyblosxom-plugins/files
parentFixed digests (diff)
downloadoverlay-d0149e1cb4b6d718c5a996d0d7f3849dfa322b68.tar.gz
overlay-d0149e1cb4b6d718c5a996d0d7f3849dfa322b68.tar.bz2
overlay-d0149e1cb4b6d718c5a996d0d7f3849dfa322b68.zip
Added session plugin
svn path=/; revision=360
Diffstat (limited to 'www-apps/pyblosxom-plugins/files')
-rw-r--r--www-apps/pyblosxom-plugins/files/nospam.py231
-rw-r--r--www-apps/pyblosxom-plugins/files/session.py431
2 files changed, 662 insertions, 0 deletions
diff --git a/www-apps/pyblosxom-plugins/files/nospam.py b/www-apps/pyblosxom-plugins/files/nospam.py
new file mode 100644
index 0000000..284e50c
--- /dev/null
+++ b/www-apps/pyblosxom-plugins/files/nospam.py
@@ -0,0 +1,231 @@
+"""
+Human verification for the comments plugin.
+Based on a idea and ref impl of Jesus Roncero Franco <jesus at roncero.org>.
+Implemented as a pyblosxom plugin by Steven Armstrong <sa at c-area.ch>.
+
+Creates a random number, generates an image of it, and stores the number
+in the session.
+
+If you make any changes to this plugin, please send a patch to
+<sa+pyblosxom at c-area dot ch> so I can incorporate them.
+Thanks!
+
+
+To install:
+1) Put nospam.py in your plugin directory.
+2) In config.py add nospam to py['load_plugins']
+3) Add the following variables to config.py:
+ py['nospam_font'] = '/path/to/truetype/font.ttf' # required, no default
+ py['nospam_extension'] = '/nospam.png' # optional, this is the default
+
+
+Add something like this to your comment-form.html template:
+<label for="nospam">Secret Number:</label>
+<img src="$base_url/nospam.png" alt="Secret Number Image" title="Type this number into the field on the right" />
+<input name="nospam" id="nospam" type="text" value="" maxlength="5" style="width:5em" />
+
+
+Dependecies:
+ - My compatibility plugin if you're not using pyblosxom 1.2+.
+ - My session plugin.
+ - Python imaging library from http://www.pythonware.com/products/pil/
+
+
+$Id: nospam.py,v 1.3 2005/03/01 00:50:04 sar Exp $
+"""
+__author__ = "Steven Armstrong <sa at c-area dot ch>"
+__version__ = "$Revision: 1.3 $ $Date: 2005/03/01 00:50:04 $"
+__url__ = "http://www.c-area.ch/code/"
+__description__ = "Human verification system for the comments plugin"
+__license__ = "GPL 2+"
+
+
+# Python imports
+import sys
+import os
+import random
+
+# PIL imports http://www.pythonware.com/products/pil/
+import Image
+import ImageDraw
+import ImageFont
+import ImageOps
+
+# Pyblosxom imports
+from Pyblosxom import tools
+
+# parameters
+_xstep = 5
+_ystep = 5
+_imageSize = (61,21)
+_bgColor = (255,255,255) # White
+_gridInk = (200,200,200)
+_fontInk = (130,130,130)
+_fontSize = 14
+# set in cb_start callback
+_fontPath = None
+
+# the names of the fields used in the comment form
+_form_fields = ["title", "author", "email", "url", "body"]
+
+
+def verify_installation(request):
+ config = request.getConfiguration()
+ retval = 1
+
+ try:
+ import session
+ except ImportError:
+ print "Missing required plugin 'session.py'."
+ retval = 0
+
+ try:
+ import compatibility
+ except ImportError:
+ print "If you're not running the WSGI version of Pyblosxom"
+ print "you'll need the 'compatibility.py' plugin."
+
+ if not config.has_key('nospam_font'):
+ print "Missing required property: 'nospam_font'"
+ print "This must be the absolute path to a truetype font."
+ retval = 0
+
+ if not config.has_key('nospam_extension'):
+ print "Missing optional property: 'nospam_extension'"
+ print "Using the default of '/nospam.png'"
+
+ return retval
+
+
+# This function is (c) Jesus Roncero Franco <jesus at roncero.org>.
+def _generateImage(number):
+ font = ImageFont.truetype(_fontPath, _fontSize)
+ im = Image.new("RGB", _imageSize, _bgColor)
+ draw = ImageDraw.Draw(im)
+
+ xsize, ysize = im.size
+
+ # Do we want the grid start at 0,0 or want some offset?
+ x, y = 0,0
+
+ draw.setink(_gridInk)
+ while x <= xsize:
+ draw.line(((x, 0), (x, ysize)))
+ x = x + _xstep
+ while y <= ysize:
+ draw.line(((0, y), (xsize, y)))
+ y = y + _ystep
+
+ draw.setink(_fontInk)
+ draw.text((10, 2), number, font=font)
+
+ return im
+
+
+def _writeImage(request):
+ number = str(random.randrange(1,99999,1))
+
+ session = request.getSession()
+ session["nospam"] = number
+ session.save()
+
+ image = _generateImage(number)
+
+ response = request.getResponse()
+ response.addHeader('Content-Type', 'image/png')
+ image.save(response, "PNG")
+
+
+def _remember_comment(request):
+ """
+ Stores form fields in the data dict so they can be used to
+ refill the form in the template.
+
+ @param request: pyblosxom request object
+ @type request: L{Pyblosxom.pyblosxom.Request}
+ """
+ data = request.getData()
+ form = request.getForm()
+ for key in _form_fields:
+ data["cmt_%s" % key] = (form.has_key(key) and [form[key].value] or [''])[0]
+
+
+def _forget_comment(request):
+ """
+ Resets/forgets any stored form field values.
+
+ @param request: pyblosxom request object
+ @type request: L{Pyblosxom.pyblosxom.Request}
+ """
+ data = request.getData()
+ for key in _form_fields:
+ key = "cmt_%s" % key
+ if key in data:
+ del data[key]
+
+
+
+#******************************
+# Callbacks
+#******************************
+
+def cb_start(args):
+ request = args['request']
+ config = request.getConfiguration()
+ global _fontPath
+ _fontPath = config.get('nospam_font')
+
+
+def cb_handle(args):
+ request = args['request']
+ http = request.getHttp()
+ config = request.getConfiguration()
+ ext = config.get("nospam_extension", "/nospam.png")
+ if http['PATH_INFO'].endswith( ext ):
+ # write the image to the output stream
+ _writeImage(request)
+ # return True to tell pyblosxom that the request has been taken care of
+ return 1
+
+
+def cb_comment_reject(args):
+ """
+ Checks if the the nospam number of the incomming request
+ matches the one stored in the session.
+ Creates a template variable $cmt_nospam_error with a
+ error message if it didn't.
+
+ Also creates the following template variables:
+ $cmt_title, $cmt_author, $cmt_email, $cmt_url, $cmt_body
+ which can be used to populate the form with the values
+ provided by the user.
+
+ @param args: a dict containing: pyblosxom request, comment dict
+ @type config: C{dict}
+ @return: True if the comment should be rejected, False otherwise
+ @rtype: C{bool}
+ """
+ request = args['request']
+ session = request.getSession()
+ form = request.getForm()
+ data = request.getData()
+
+ try:
+ nospam = int(form["nospam"].value)
+ sess_nospam = int(session["nospam"])
+ except:
+ nospam = 0
+ sess_nospam = 1
+
+ if nospam != sess_nospam:
+ _remember_comment(request)
+ data["cmt_nospam_error"] = "Secret number did not match."
+ return True
+ else:
+ _forget_comment(request)
+ if "cmt_nospam_error" in data:
+ del data["cmt_nospam_error"]
+ return False
+
+
+
diff --git a/www-apps/pyblosxom-plugins/files/session.py b/www-apps/pyblosxom-plugins/files/session.py
new file mode 100644
index 0000000..d44a604
--- /dev/null
+++ b/www-apps/pyblosxom-plugins/files/session.py
@@ -0,0 +1,431 @@
+"""
+A simple session management plugin for pyblosxom.
+Inspired by the mod_python Session module.
+
+This plugin tries to set a session cookie. As an alternative you can get
+the session id manually and store it in a hidden form field or the QS.
+See below for examples.
+
+If you make any changes to this plugin, please send a patch to
+<sa+pyblosxom at c-area dot ch> so I can incorporate them.
+Thanks!
+
+
+To install:
+1) Put session.py in your plugin directory.
+2) In config.py add session to py['load_plugins']
+3) Add the following optional variable to config.py:
+ py['session_dbmfile'] = '/tmp/pb_session.dbm' # this is the default
+
+Note: check the dependency listing below.
+
+Usage with cookies:
+ Store variables in the session:
+ def cb_whatever(args):
+ request = args['request']
+ session = request.getSession()
+ session['hello'] = "world"
+ session.save()
+ ...
+
+ Retreive variables from the session:
+ def cb_whatever(args):
+ request = args['request']
+ session = request.getSession()
+ msg = session['hello']
+ ...
+
+Usage without cookies:
+ Create a session:
+ def cb_whatever(args):
+ request = args['request']
+ session = request.getSession()
+ session['hello'] = "world"
+ session.save()
+ sessId = session.id()
+ # store sessId in a hidden form field or add it to the QS or whatever.
+
+ Retreive a session:
+ def cb_whatever(args):
+ request = args['request']
+ form = request.getForm()
+ sessId = form['pbsid'].value
+ session = request.getSession(sessId)
+ msg = session['hello']
+ ...
+
+
+Todo:
+ write doc strings ... :(
+
+
+Dependecies:
+ - My compatibility plugin if you're not using pyblosxom 1.2+.
+
+
+Revisions:
+ $Log: session.py,v $
+ Revision 1.2 2005/02/10 15:47:44 sar
+ removed debugging statements
+
+ Revision 1.1 2005/02/08 17:51:21 sar
+ cvs server update
+
+ Revision 1.8 2005/01/18 02:14:29 sar
+ moved cgi stuff to the compatibility plugin
+
+ Revision 1.7 2005/01/16 14:08:00 sar
+ removed unnecessary local variables
+
+ Revision 1.6 2004/12/17 02:51:18 sar
+ fixed bugs in _init. indentation error caused that ._accessed was
+ not updated and .cleanup() was only called if session was new.
+
+ Revision 1.5 2004/12/16 03:09:54 sar
+ fixed a bug in the CGI version of _setCookie, thanks Will.
+ added config variable 'session_dbmfile'.
+
+ Revision 1.4 2004/12/14 20:17:31 sar
+ fixed bugs in _lock, _unlock and _setCookie
+
+ Revision 1.3 2004/12/11 19:12:43 sar
+ removed dependencies on my WSGI wrapper
+
+ Revision 1.2 2004/12/05 12:07:19 sar
+ changed callback from cb_renderer to cb_start,
+ added switch to handle mod_python session or fall back to the handmade Session.
+
+ Revision 1.1 2004/11/27 23:54:58 sar
+ created
+
+$Id: session.py,v 1.2 2005/02/10 15:47:44 sar Exp $
+"""
+__author__ = "Steven Armstrong <sa at c-area dot ch>"
+__version__ = "$Revision: 1.2 $ $Date: 2005/02/10 15:47:44 $"
+__url__ = "http://www.c-area.ch/code/"
+__description__ = "Simple session management"
+__license__ = "GPL 2+"
+
+
+# Python imports
+import sys
+import os
+import time
+import random
+import md5
+import anydbm
+import tempfile
+from Cookie import SimpleCookie as Cookie
+try: import cPickle as pickle
+except ImportError: import pickle
+
+# Pyblosxom imports
+from Pyblosxom import tools
+
+
+# parameters
+COOKIE_NAME = "pbsid" # mod_python uses 'pysid'. don't use the same.
+DFT_TIMEOUT = 30*60 # 30 min
+CLEANUP_CHANCE = 1000 # cleanups have 1 in CLEANUP_CHANCE chance
+DBM_NAME = "pb_session.dbm"
+DBM_FILE = os.path.join(tempfile.gettempdir(), DBM_NAME)
+
+# the mod_python session seems to be a bit buggy when running with a WSGI wrapper.
+# looks like issues with the Session object directly accessing the output-stream.
+# so here's a flag to use the handmade session even if running under mod_python.
+USE_MP_SESSION = False
+
+
+def verify_installation(request):
+ config = request.getConfiguration()
+ retval = 1
+
+ try:
+ import compatibility
+ except ImportError:
+ print "If you're not running the WSGI version of Pyblosxom"
+ print "you'll need the 'compatibility.py' plugin."
+
+ if not 'session_dbmfile' in config:
+ print "Missing optional property: 'session_dbmfile'"
+ print "Using the default of '%s'." % DBM_FILE
+
+ return retval
+
+class Session(dict):
+
+ def __init__(self, request, sid=None, dbmfile=None, timeout=None, lock=1):
+ dict.__init__(self)
+ self._request = request
+ self._http = self._request.getHttp()
+ self._sid = sid
+ self._use_lock = lock
+ self._new = 1
+ self._created = 0
+ self._accessed = 0
+ self._timeout = 0
+ self._locked = 0
+ self._invalid = 0
+ if dbmfile:
+ self._dbmfile = dbmfile
+ else:
+ self._dbmfile = self._request.getConfiguration().get('session_dbmfile', DBM_FILE)
+
+ self._init(timeout)
+
+
+ def __del__(self):
+ self._unlock()
+
+
+ def _init(self, timeout):
+ if not self._sid:
+ # check to see if cookie exists
+ cookie = self._getCookie()
+ if cookie:
+ self._sid = cookie.value
+
+ if self._sid:
+ # attempt to load ourselves
+ if self.load():
+ self._new = 0
+
+ if self._new:
+ # make a new session
+ self._sid = self._generateSID()
+ self._setCookie(self._makeCookie())
+ self._created = time.time()
+ if timeout:
+ self._timeout = timeout
+ else:
+ self._timeout = DFT_TIMEOUT
+
+ self._accessed = time.time()
+
+ # need cleanup?
+ if random.randint(1, CLEANUP_CHANCE) == 1:
+ self.cleanup()
+
+
+ def _generateSID(self):
+ """
+ Generates a new session ID. Uses different random sources to be
+ difficult to guess.
+ """
+ t = long(time.time()*10000)
+ pid = os.getpid()
+ rnd1 = random.randrange(1,999999999,1)
+ rnd2 = random.randrange(1,999999999,1)
+ ip = self._request.getHttp()['REMOTE_ADDR']
+ return md5.new("%d%d%d%d%s" % (t, pid, rnd1, rnd2, ip)).hexdigest()
+
+
+### cookie related methods
+
+ def _makeCookie(self):
+ m = Cookie()
+ m.load("%s=%s" % (COOKIE_NAME, self._sid))
+ c = m[COOKIE_NAME]
+ #c['path'] = "/"
+ c['path'] = self._http['SCRIPT_NAME']
+ return c
+
+
+ def _getCookie(self):
+ # check to see if cookie exists
+ cookies = Cookie()
+ if 'HTTP_COOKIE' in self._http:
+ cookies.load(self._http['HTTP_COOKIE'])
+ if cookies.has_key(COOKIE_NAME):
+ return cookies[COOKIE_NAME]
+ return None
+
+
+ def _setCookie(self, c):
+ k, v = c.output().split(":", 1)
+ self._request.getResponse().addHeader(k, v.strip())
+
+
+### dbm related methods
+
+ def _lock(self):
+ # ???: does this work as expected?
+ if self._use_lock:
+ self._dbmfile_lock = open(self._dbmfile, "r+")
+ tools.lock(self._dbmfile_lock, tools.LOCK_EX)
+ self._locked = 1
+
+
+ def _unlock(self):
+ # ???: does this work as expected?
+ if self._use_lock and self._locked:
+ tools.unlock(self._dbmfile_lock)
+ self._dbmfile_lock.close()
+ self._locked = 0
+
+
+ def _getDBM(self):
+ result = anydbm.open(self._dbmfile, 'c')
+ return result
+
+
+ def _do_load(self):
+ dbm = self._getDBM()
+ self._lock()
+ try:
+ if dbm.has_key(self._sid):
+ return pickle.loads(dbm[self._sid])
+ else:
+ return None
+ finally:
+ dbm.close()
+ self._unlock()
+
+
+ def _do_save(self, dict):
+ dbm = self._getDBM()
+ self._lock()
+ try:
+ dbm[self._sid] = pickle.dumps(dict)
+ finally:
+ dbm.close()
+ self._unlock()
+
+
+ def _do_delete(self):
+ dbm = self._getDBM()
+ self._lock()
+ try:
+ try:
+ del dbm[self._sid]
+ except KeyError: pass
+ finally:
+ dbm.close()
+ self._unlock()
+
+
+ def _do_cleanup(self):
+ dbm = self._getDBM()
+ self._lock()
+ try:
+ old = []
+ try:
+ s = dbm.first()
+ while 1:
+ key, val = s
+ dict = pickle.loads(val)
+ try:
+ if (time.time() - dict["_accessed"]) > dict["_timeout"]:
+ old.append(key)
+ except KeyError:
+ old.append(key)
+ try:
+ s = dbm.next()
+ except KeyError:
+ break
+ except DBNotFoundError:
+ pass
+
+ for key in old:
+ try:
+ del dbm[key]
+ except:
+ pass
+ finally:
+ dbm.close()
+ self._unlock()
+
+
+
+### public methods
+
+ def invalidate(self):
+ c = self._makeCookie()
+ c['expires'] = 0
+ self._setCookie(c)
+ self.delete()
+ self._invalid = 1
+
+ def load(self):
+ dict = self._do_load()
+
+ if dict == None:
+ return 0
+
+ if (time.time() - dict["_accessed"]) > dict["_timeout"]:
+ return 0
+ self._created = dict["_created"]
+ self._accessed = dict["_accessed"]
+ self._timeout = dict["_timeout"]
+ self.update(dict["_data"])
+ return 1
+
+
+ def save(self):
+ if not self._invalid:
+ dict = {"_data" : self.copy(),
+ "_created" : self._created,
+ "_accessed": self._accessed,
+ "_timeout" : self._timeout}
+ self._do_save(dict)
+
+
+ def delete(self):
+ self._do_delete()
+ self.clear()
+
+
+ def is_new(self):
+ return not not self._new
+
+
+ def id(self):
+ return self._sid
+
+
+ def created(self):
+ return self._created
+
+
+ def last_accessed(self):
+ return self._accessed
+
+
+ def timeout(self):
+ return self._timeout
+
+
+ def set_timeout(self, secs):
+ self._timeout = secs
+
+
+ def cleanup(self):
+ self._do_cleanup()
+
+
+
+#******************************
+# Callbacks
+#******************************
+
+def cb_start(args):
+ request = args['request']
+ http = request.getHttp()
+
+ if not request.getSession:
+ def _getSession(sessId=None):
+ if request._session == None:
+ # running on mod_python and using WSGI wrapper
+ if 'mod_python.request' in http and USE_MP_SESSION:
+ from mod_python.Session import Session as MPSession
+ request._session = MPSession(http['mod_python.request'], sid=sessId)
+
+ # CGI version
+ else:
+ request._session = Session(request, sid=sessId)
+ return request._session
+
+ request.getSession = _getSession
+
+
+