My favorites
▼
|
Sign in
django-captchas
A ImageCaptcha and ReverseCaptcha solution for the django framework
Project Home
Downloads
Wiki
Issues
Source
Checkout
Browse
Changes
Source path:
svn
/
trunk
/
captchas
/
imagecaptcha.py
r4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
"""Captcha (Completely Automated Public Turing test to tell Computers and
Humans Apart) module for django
Author: Martin Winkler, June 2007
License: BSD
"""
from os import listdir, sep, access, mkdir, W_OK, R_OK, remove, chmod, path, rmdir, stat
import md5
import tempfile
import datetime
from PIL import Image, ImageColor, ImageFont, ImageDraw
from django.newforms import Widget, Field, ValidationError
from django.conf import settings
from django.utils.translation import gettext
from django.utils.datastructures import MultiValueDict
from django.contrib.captcha import captchasettings, errormsg
def clean_old_entries(captchas_dir, max_age=1200):
"""maintainance function for deleting all expired captchas
captchas_dir: parent directory of the individual image-directories
default: [settings.MEDIA_ROOT]/captchas
max_age: maximum allowed age of directories in seconds.
defaults to 1200 seconds (20 minutes)
"""
if access(captchas_dir, W_OK):
basetime = datetime.datetime.now() - datetime.timedelta(seconds=max_age)
for dname in listdir(captchas_dir):
d = path.join(captchas_dir, dname)
if basetime > datetime.datetime.fromtimestamp(stat(d).st_mtime):
try:
for f in listdir(d):
remove(path.join(d, f))
rmdir(d)
except:
pass
def mycrypt(value, salt):
return md5.new(value.lower() + salt + settings.SECRET_KEY).hexdigest()
class CaptchaWidget(Widget):
"""generate a captcha image and display the image along with
a hidden field and a input field"""
def __init__(self, options=None, seednum=None, *args, **kwargs):
self.csettings = captchasettings(options, seednum=seednum)
super(CaptchaWidget, self).__init__(*args, **kwargs)
def render(self, name, value, attrs=None):
img = self.generate_image(Image.new('RGB',
self.csettings['imagesize'],
self.csettings['bgcolor'])
)
self.save_image(img)
return u'''<input type="hidden" name="%(name)s" value="captcha.%(hiddentext)s"
/><img width="%(imagewidth)s" height="%(imageheight)s" src="%(imageurl)s" alt="" /><br
/><input type="text" name="%(name)s" id="id_%(name)s" />''' % {'name':name,
'hiddentext': self.hiddentext,
'imagewidth': self.csettings['imagesize'][0],
'imageheight': self.csettings['imagesize'][1],
'imageurl': self.imageurl }
def generate_image(self, img, seednum=None):
""" create a image file.
the filename looks like TMPNAME.HASH.EXT
where TMPNAME is a system generated temporary name and
HASH is the hashed solution.
"""
r = self.csettings['R']
if self.csettings['auto_cleanup']:
clean_old_entries(self.csettings['captchas_dir'])
cs = self.csettings
imagesize = cs['imagesize']
fontdir = path.join(cs['captchaconf_dir'], 'fonts')
fontnames = [path.join(fontdir, x) for x in listdir(fontdir) ]
for dummy in range(self.csettings['iterations']):
posnew = 7
if dummy != 0:
cs.generate_solution()
# render characters
for c in self.csettings['solution']:
fgimage = Image.new('RGB', imagesize, cs['fgcolor'])
font = ImageFont.truetype(r.choice(fontnames), r.randrange(*cs['minmaxheight']))
charimage = Image.new('L', font.getsize(' %s ' % c), '#000000')
draw = ImageDraw.Draw(charimage)
draw.text((0,0), ' %s' % c, font=font, fill='#ffffff')
if cs['eraser']:
eraserline = ( 0, r.choice(range(0, charimage.size[1])),
charimage.size[0], r.choice(range(0, charimage.size[1])))
draw = ImageDraw.Draw(charimage)
draw.line(eraserline, width=cs['eraser_width'] , fill='#000000')
charimage = charimage.rotate(r.randrange(*cs['minmaxrotations']), expand=1,
resample=Image.BILINEAR)
charimage = charimage.crop(charimage.getbbox())
maskimage = Image.new('L', imagesize)
ypos = r.randrange(*cs['minmaxvpos'])
maskimage.paste(charimage,
(posnew, ypos,
charimage.size[0]+posnew,
charimage.size[1]+ypos)
)
img = Image.composite(fgimage, img, maskimage)
posnew += charimage.size[0] + r.randrange(*cs['minmaxkerning'])
# draw line(s)
for dummy in range(cs.get('num_lines')):
linex = r.choice( range(2, cs['minmaxheight'][1]) )
minmaxliney = ( cs['minmaxvpos'][0],
cs['minmaxvpos'][1] + cs['minmaxheight'][0])
linepoints = [linex, r.randrange(*minmaxliney)]
while linex < posnew:
linex += r.randrange(*cs['minmaxheight']) * 0.8
linepoints.append(linex)
linepoints.append(r.randrange(*minmaxliney))
draw = ImageDraw.Draw(img)
draw.line(linepoints, width=cs['line_width']
, fill=cs['fgcolor'])
return img
def save_image(self, img):
""" save file """
cs = self.csettings
if not access(cs['captchas_dir'], W_OK):
mkdir(cs['captchas_dir'])
chmod(cs['captchas_dir'], 0755)
dirpath = tempfile.mkdtemp(prefix='c', dir=cs['captchas_dir'])
chmod(dirpath, 0755)
self.hiddentext = dirpath[dirpath.rfind(sep)+1:]
plainfilename = mycrypt(self.csettings['solution'], self.hiddentext)
self.imageurl = '%s/%s/%s' % (cs['captchabase_url'],
self.hiddentext, plainfilename)
imagepath = path.join(dirpath, plainfilename)
try:
img.convert('P', palette=Image.ADAPTIVE, colors=4).save(imagepath + '.gif')
ext = '.gif'
except:
img.save(imagepath + '.jpg')
ext = '.jpg'
self.imageurl += ext
chmod(imagepath + ext, 0644)
def value_from_datadict(self, data, name):
if isinstance(data, MultiValueDict):
return data.getlist(name)
return data.get(name, None)
class CaptchaField(Field):
"""Field definition for a captcha
Special options:
fgcolor: color to use for the letters and line
bgcolor: color to use for the background of the image
captchas_dir: file system directory for the captchas
defaults to [settings.MEDIA_ROOT]/captchas
captchabase_url: absolute URL of captchas_dir
captchaconf_dir: file system directory for the captcha module
contains a "fonts" directory with all available fonts
auto_cleanup: clean up captchas_dir deleting all entries older
than 30 minutes
"""
def __init__(self, required=True, label=None, help_text=None,
options=None, *args, **kwargs):
super(CaptchaField, self).__init__(required=required,
widget=CaptchaWidget(options=options),
label=label, help_text=help_text, *args, **kwargs
)
def clean(self, value):
if not isinstance(value, (list, tuple)):
raise ValidationError(errormsg)
hiddenval = None
val = None
for v in value:
if v.startswith('captcha.'):
hiddenval = v[8:]
else:
val = ''.join(v.split())
if not hiddenval:
raise ValidationError(errormsg)
imagedir = path.join(self.widget.csettings['captchas_dir'], hiddenval)
success = False
for ext in ('.gif', '.jpg', '.png'):
if access(path.join(imagedir, mycrypt(val, hiddenval) + ext), R_OK):
success = True
try:
for f in listdir(imagedir):
remove(path.join(imagedir, f))
rmdir(imagedir)
except OSError:
pass
if not success:
raise ValidationError(errormsg)
return True
Show details
Hide details
Change log
r4
by abecede on Aug 21, 2007
Diff
checkin
Go to:
/trunk/captchas
/trunk/captchas/__init__.py
/trunk/captchas/doc
.../captchas/doc/reversecaptcha.txt
/trunk/captchas/dynimagecaptcha.py
/trunk/captchas/fonts
...as/fonts/FreeMonoBoldOblique.ttf
...as/fonts/FreeSerifBoldItalic.ttf
/trunk/captchas/fonts/Vera.ttf
/trunk/captchas/fonts/VeraBI.ttf
/trunk/captchas/fonts/VeraIt.ttf
/trunk/captchas/fonts/VeraMoBI.ttf
/trunk/captchas/fonts/VeraSe.ttf
/trunk/captchas/imagecaptcha.py
/trunk/captchas/pyDes.py
/trunk/captchas/reversecaptcha.py
/trunk/captchas/urls.py
/trunk/captchas/views.py
Project members,
sign in
to write a code review
Older revisions
All revisions of this file
File info
Size: 8303 bytes, 201 lines
View raw file
Powered by
Google Project Hosting