diff options
author | 2005-09-04 16:48:35 +0000 | |
---|---|---|
committer | 2005-09-04 16:48:35 +0000 | |
commit | d0149e1cb4b6d718c5a996d0d7f3849dfa322b68 (patch) | |
tree | d0eb42bdf5b959ea594f032cc05098472c685468 /www-apps/pyblosxom-plugins/files | |
parent | Fixed digests (diff) | |
download | overlay-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.py | 231 | ||||
-rw-r--r-- | www-apps/pyblosxom-plugins/files/session.py | 431 |
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 + + + |