cubicweb/web/captcha.py
changeset 11057 0b59724cb3f2
parent 10609 e2d8e81bfe68
child 11461 f5a4e14d1dd2
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """Simple captcha library, based on PIL. Monkey patch functions in this module
       
    19 if you want something better...
       
    20 """
       
    21 
       
    22 __docformat__ = "restructuredtext en"
       
    23 
       
    24 from random import randint, choice
       
    25 from io import BytesIO
       
    26 
       
    27 from six.moves import range
       
    28 
       
    29 from PIL import Image, ImageFont, ImageDraw, ImageFilter
       
    30 
       
    31 
       
    32 from time import time
       
    33 
       
    34 from cubicweb import tags
       
    35 from cubicweb.web import ProcessFormError, formwidgets as fw
       
    36 
       
    37 
       
    38 def pil_captcha(text, fontfile, fontsize):
       
    39     """Generate a captcha image. Return a PIL image object.
       
    40 
       
    41     adapted from http://code.activestate.com/recipes/440588/
       
    42     """
       
    43     # randomly select the foreground color
       
    44     fgcolor = randint(0, 0xffff00)
       
    45     # make the background color the opposite of fgcolor
       
    46     bgcolor = fgcolor ^ 0xffffff
       
    47     # create a font object
       
    48     font = ImageFont.truetype(fontfile, fontsize)
       
    49     # determine dimensions of the text
       
    50     dim = font.getsize(text)
       
    51     # create a new image slightly larger that the text
       
    52     img = Image.new('RGB', (dim[0]+5, dim[1]+5), bgcolor)
       
    53     draw = ImageDraw.Draw(img)
       
    54     # draw 100 random colored boxes on the background
       
    55     x, y = img.size
       
    56     for num in range(100):
       
    57         draw.rectangle((randint(0, x), randint(0, y),
       
    58                         randint(0, x), randint(0, y)),
       
    59                        fill=randint(0, 0xffffff))
       
    60     # add the text to the image
       
    61     draw.text((3, 3), text, font=font, fill=fgcolor)
       
    62     img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
       
    63     return img
       
    64 
       
    65 
       
    66 def captcha(fontfile, fontsize, size=5, format='JPEG'):
       
    67     """Generate an arbitrary text, return it together with a buffer containing
       
    68     the captcha image for the text
       
    69     """
       
    70     text = u''.join(choice('QWERTYUOPASDFGHJKLZXCVBNM') for i in range(size))
       
    71     img = pil_captcha(text, fontfile, fontsize)
       
    72     out = BytesIO()
       
    73     img.save(out, format)
       
    74     out.seek(0)
       
    75     return text, out
       
    76 
       
    77 
       
    78 class CaptchaWidget(fw.TextInput):
       
    79     def render(self, form, field, renderer=None):
       
    80         # t=int(time()*100) to make sure img is not cached
       
    81         src = form._cw.build_url('view', vid='captcha', t=int(time()*100),
       
    82                                  captchakey=field.input_name(form))
       
    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)
       
    86 
       
    87     def process_field_data(self, form, field):
       
    88         captcha = form._cw.session.data.pop(field.input_name(form), None)
       
    89         val = super(CaptchaWidget, self).process_field_data(form, field)
       
    90         if val is None:
       
    91             return val # required will be checked by field
       
    92         if captcha is None:
       
    93             msg = form._cw._('unable to check captcha, please try again')
       
    94             raise ProcessFormError(msg)
       
    95         elif val.lower() != captcha.lower():
       
    96             msg = form._cw._('incorrect captcha value')
       
    97             raise ProcessFormError(msg)
       
    98         return val