web/captcha.py
changeset 4595 bb08a75832e6
child 4621 fe1c5a46a1cb
equal deleted inserted replaced
4594:7b9824d1b734 4595:bb08a75832e6
       
     1 """Simple captcha library, based on PIL. Monkey patch functions in this module
       
     2 if you want something better...
       
     3 
       
     4 :organization: Logilab
       
     5 :copyright: 2009-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
       
     6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
       
     8 """
       
     9 __docformat__ = "restructuredtext en"
       
    10 
       
    11 from random import randint, choice
       
    12 from cStringIO import StringIO
       
    13 
       
    14 import Image, ImageFont, ImageDraw, ImageFilter
       
    15 
       
    16 
       
    17 from time import time
       
    18 
       
    19 from cubicweb import tags
       
    20 from cubicweb.view import StartupView
       
    21 from cubicweb.web import httpcache, formwidgets as fw, formfields as ff
       
    22 
       
    23 
       
    24 def pil_captcha(text, fontfile, fontsize):
       
    25     """Generate a captcha image. Return a PIL image object.
       
    26 
       
    27     adapted from http://code.activestate.com/recipes/440588/
       
    28     """
       
    29     # randomly select the foreground color
       
    30     fgcolor = randint(0, 0xffff00)
       
    31     # make the background color the opposite of fgcolor
       
    32     bgcolor = fgcolor ^ 0xffffff
       
    33     # create a font object
       
    34     font = ImageFont.truetype(fontfile, fontsize)
       
    35     # determine dimensions of the text
       
    36     dim = font.getsize(text)
       
    37     # create a new image slightly larger that the text
       
    38     img = Image.new('RGB', (dim[0]+5, dim[1]+5), bgcolor)
       
    39     draw = ImageDraw.Draw(img)
       
    40     # draw 100 random colored boxes on the background
       
    41     x, y = img.size
       
    42     for num in range(100):
       
    43         draw.rectangle((randint(0, x), randint(0, y),
       
    44                         randint(0, x), randint(0, y)),
       
    45                        fill=randint(0, 0xffffff))
       
    46     # add the text to the image
       
    47     draw.text((3, 3), text, font=font, fill=fgcolor)
       
    48     img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
       
    49     return img
       
    50 
       
    51 
       
    52 def captcha(fontfile, fontsize, size=5, format='JPEG'):
       
    53     """Generate an arbitrary text, return it together with a buffer containing
       
    54     the captcha image for the text
       
    55     """
       
    56     text = u''.join(choice('QWERTYUOPASDFGHJKLZXCVBNM') for i in range(size))
       
    57     img = pil_captcha(text, fontfile, fontsize)
       
    58     out = StringIO()
       
    59     img.save(out, format)
       
    60     out.seek(0)
       
    61     return text, out
       
    62 
       
    63 
       
    64 class CaptchaView(StartupView):
       
    65     __regid__ = 'captcha'
       
    66 
       
    67     http_cache_manager = httpcache.NoHTTPCacheManager
       
    68     binary = True
       
    69     templatable = False
       
    70     content_type = 'image/jpg'
       
    71 
       
    72     def call(self):
       
    73         text, data = captcha.captcha(self._cw.vreg.config['captcha-font-file'],
       
    74                                      self._cw.vreg.config['captcha-font-size'])
       
    75         self._cw.set_session_data('captcha', text)
       
    76         self.w(data.read())
       
    77 
       
    78 
       
    79 class CaptchaWidget(fw.TextInput):
       
    80     def render(self, form, field, renderer=None):
       
    81         # t=int(time()*100) to make sure img is not cached
       
    82         src = form._cw.build_url('view', vid='captcha', t=int(time()*100))
       
    83         img = tags.img(src=src, alt=u'captcha')
       
    84         img = u'<div class="captcha">%s</div>' % img
       
    85         return img + super(CaptchaWidget, self).render(form, field, renderer)