Skip to content

Commit 4320358

Browse files
author
Kishore kumar J
committed
Added Captcha to Registration Page
1 parent 0758df7 commit 4320358

20 files changed

+721
-0
lines changed

captcha/__init__.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
VERSION = (0, 1, 7)
2+
3+
def get_version(svn=False):
4+
"Returns the version as a human-format string."
5+
v = '.'.join([str(i) for i in VERSION])
6+
if svn:
7+
from django.utils.version import get_svn_revision
8+
import os
9+
svn_rev = get_svn_revision(os.path.dirname(__file__))
10+
if svn_rev:
11+
v = '%s-%s' % (v, svn_rev)
12+
return v

captcha/conf/__init__.py

Whitespace-only changes.

captcha/conf/settings.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import os
2+
from django.conf import settings
3+
4+
CAPTCHA_FONT_PATH = getattr(settings,'CAPTCHA_FONT_PATH', os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'fonts/Vera.ttf')))
5+
CAPTCHA_FONT_SIZE = getattr(settings,'CAPTCHA_FONT_SIZE', 22)
6+
CAPTCHA_LETTER_ROTATION = getattr(settings, 'CAPTCHA_LETTER_ROTATION', (-35,35))
7+
CAPTCHA_BACKGROUND_COLOR = getattr(settings,'CAPTCHA_BACKGROUND_COLOR', '#ffffff')
8+
CAPTCHA_FOREGROUND_COLOR= getattr(settings,'CAPTCHA_FOREGROUND_COLOR', '#001100')
9+
CAPTCHA_CHALLENGE_FUNCT = getattr(settings,'CAPTCHA_CHALLENGE_FUNCT','captcha.helpers.random_char_challenge')
10+
CAPTCHA_NOISE_FUNCTIONS = getattr(settings,'CAPTCHA_NOISE_FUNCTIONS', ('captcha.helpers.noise_arcs','captcha.helpers.noise_dots',))
11+
CAPTCHA_FILTER_FUNCTIONS = getattr(settings,'CAPTCHA_FILTER_FUNCTIONS',('captcha.helpers.post_smooth',))
12+
CAPTCHA_WORDS_DICTIONARY = getattr(settings,'CAPTCHA_WORDS_DICTIONARY', '/usr/share/dict/words')
13+
CAPTCHA_FLITE_PATH = getattr(settings,'CAPTCHA_FLITE_PATH',None)
14+
CAPTCHA_TIMEOUT = getattr(settings, 'CAPTCHA_TIMEOUT', 5) # Minutes
15+
CAPTCHA_LENGTH = int(getattr(settings, 'CAPTCHA_LENGTH', 4)) # Chars
16+
CAPTCHA_IMAGE_BEFORE_FIELD = getattr(settings,'CAPTCHA_IMAGE_BEFORE_FIELD', True)
17+
CAPTCHA_DICTIONARY_MIN_LENGTH = getattr(settings,'CAPTCHA_DICTIONARY_MIN_LENGTH', 0)
18+
CAPTCHA_DICTIONARY_MAX_LENGTH = getattr(settings,'CAPTCHA_DICTIONARY_MAX_LENGTH', 99)
19+
if CAPTCHA_IMAGE_BEFORE_FIELD:
20+
CAPTCHA_OUTPUT_FORMAT = getattr(settings,'CAPTCHA_OUTPUT_FORMAT', u'%(image)s %(hidden_field)s %(text_field)s')
21+
else:
22+
CAPTCHA_OUTPUT_FORMAT = getattr(settings,'CAPTCHA_OUTPUT_FORMAT', u'%(hidden_field)s %(text_field)s %(image)s')
23+
24+
25+
# Failsafe
26+
if CAPTCHA_DICTIONARY_MIN_LENGTH > CAPTCHA_DICTIONARY_MAX_LENGTH:
27+
CAPTCHA_DICTIONARY_MIN_LENGTH, CAPTCHA_DICTIONARY_MAX_LENGTH = CAPTCHA_DICTIONARY_MAX_LENGTH, CAPTCHA_DICTIONARY_MIN_LENGTH
28+
29+
30+
def _callable_from_string(string_or_callable):
31+
if callable(string_or_callable):
32+
return string_or_callable
33+
else:
34+
return getattr(__import__( '.'.join(string_or_callable.split('.')[:-1]), {}, {}, ['']), string_or_callable.split('.')[-1])
35+
36+
def get_challenge():
37+
return _callable_from_string(CAPTCHA_CHALLENGE_FUNCT)
38+
39+
40+
def noise_functions():
41+
if CAPTCHA_NOISE_FUNCTIONS:
42+
return map(_callable_from_string, CAPTCHA_NOISE_FUNCTIONS)
43+
return list()
44+
45+
def filter_functions():
46+
if CAPTCHA_FILTER_FUNCTIONS:
47+
return map(_callable_from_string, CAPTCHA_FILTER_FUNCTIONS)
48+
return list()
49+

captcha/fields.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from django.forms.fields import CharField, MultiValueField
2+
from django.forms import ValidationError
3+
from django.forms.widgets import TextInput, MultiWidget, HiddenInput
4+
from django.utils.safestring import mark_safe
5+
from django.utils.translation import ugettext_lazy as _
6+
from django.core.urlresolvers import reverse
7+
from captcha.models import CaptchaStore
8+
from captcha.conf import settings
9+
from captcha.helpers import *
10+
import datetime
11+
12+
class CaptchaTextInput(MultiWidget):
13+
def __init__(self,attrs=None):
14+
widgets = (
15+
HiddenInput(attrs),
16+
TextInput(attrs),
17+
)
18+
19+
for key in ('image','hidden_field','text_field'):
20+
if '%%(%s)s'%key not in settings.CAPTCHA_OUTPUT_FORMAT:
21+
raise KeyError('All of %s must be present in your CAPTCHA_OUTPUT_FORMAT setting. Could not find %s' %(
22+
', '.join(['%%(%s)s'%k for k in ('image','hidden_field','text_field')]),
23+
'%%(%s)s'%key
24+
))
25+
26+
super(CaptchaTextInput,self).__init__(widgets,attrs)
27+
28+
def decompress(self,value):
29+
if value:
30+
return value.split(',')
31+
return [None,None]
32+
33+
def format_output(self, rendered_widgets):
34+
hidden_field, text_field = rendered_widgets
35+
return settings.CAPTCHA_OUTPUT_FORMAT %dict(image=self.image_and_audio, hidden_field=hidden_field, text_field=text_field)
36+
37+
def render(self, name, value, attrs=None):
38+
challenge,response= settings.get_challenge()()
39+
40+
store = CaptchaStore.objects.create(challenge=challenge,response=response)
41+
key = store.hashkey
42+
value = [key, u'']
43+
44+
self.image_and_audio = '<img src="%s" alt="captcha" class="captcha" />' %reverse('captcha-image',kwargs=dict(key=key))
45+
if settings.CAPTCHA_FLITE_PATH:
46+
self.image_and_audio = '<a href="%s" title="%s">%s</a>' %( reverse('captcha-audio', kwargs=dict(key=key)), unicode(_('Play captcha as audio file')), self.image_and_audio)
47+
#fields = super(CaptchaTextInput, self).render(name, value, attrs=attrs)
48+
49+
return super(CaptchaTextInput, self).render(name, value, attrs=attrs)
50+
51+
class CaptchaField(MultiValueField):
52+
widget=CaptchaTextInput
53+
54+
def __init__(self, *args,**kwargs):
55+
fields = (
56+
CharField(show_hidden_initial=True),
57+
CharField(),
58+
)
59+
if 'error_messages' not in kwargs or 'invalid' not in kwargs.get('error_messages'):
60+
if 'error_messages' not in kwargs:
61+
kwargs['error_messages'] = dict()
62+
kwargs['error_messages'].update(dict(invalid=_('Invalid CAPTCHA')))
63+
64+
65+
super(CaptchaField,self).__init__(fields=fields, *args, **kwargs)
66+
67+
def compress(self,data_list):
68+
if data_list:
69+
return ','.join(data_list)
70+
return None
71+
72+
def clean(self, value):
73+
super(CaptchaField, self).clean(value)
74+
response, value[1] = value[1].strip().lower(), ''
75+
CaptchaStore.remove_expired()
76+
try:
77+
store = CaptchaStore.objects.get(response=response, hashkey=value[0], expiration__gt=datetime.datetime.now())
78+
store.delete()
79+
except Exception:
80+
raise ValidationError(getattr(self,'error_messages',dict()).get('invalid', _('Invalid CAPTCHA')))
81+
return value

captcha/fonts/COPYRIGHT.TXT

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
Bitstream Vera Fonts Copyright
2+
3+
The fonts have a generous copyright, allowing derivative works (as
4+
long as "Bitstream" or "Vera" are not in the names), and full
5+
redistribution (so long as they are not *sold* by themselves). They
6+
can be be bundled, redistributed and sold with any software.
7+
8+
The fonts are distributed under the following copyright:
9+
10+
Copyright
11+
=========
12+
13+
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
14+
Vera is a trademark of Bitstream, Inc.
15+
16+
Permission is hereby granted, free of charge, to any person obtaining
17+
a copy of the fonts accompanying this license ("Fonts") and associated
18+
documentation files (the "Font Software"), to reproduce and distribute
19+
the Font Software, including without limitation the rights to use,
20+
copy, merge, publish, distribute, and/or sell copies of the Font
21+
Software, and to permit persons to whom the Font Software is furnished
22+
to do so, subject to the following conditions:
23+
24+
The above copyright and trademark notices and this permission notice
25+
shall be included in all copies of one or more of the Font Software
26+
typefaces.
27+
28+
The Font Software may be modified, altered, or added to, and in
29+
particular the designs of glyphs or characters in the Fonts may be
30+
modified and additional glyphs or characters may be added to the
31+
Fonts, only if the fonts are renamed to names not containing either
32+
the words "Bitstream" or the word "Vera".
33+
34+
This License becomes null and void to the extent applicable to Fonts
35+
or Font Software that has been modified and is distributed under the
36+
"Bitstream Vera" names.
37+
38+
The Font Software may be sold as part of a larger software package but
39+
no copy of one or more of the Font Software typefaces may be sold by
40+
itself.
41+
42+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
44+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
45+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
46+
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
47+
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
48+
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
49+
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
50+
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
51+
52+
Except as contained in this notice, the names of Gnome, the Gnome
53+
Foundation, and Bitstream Inc., shall not be used in advertising or
54+
otherwise to promote the sale, use or other dealings in this Font
55+
Software without prior written authorization from the Gnome Foundation
56+
or Bitstream Inc., respectively. For further information, contact:
57+
fonts at gnome dot org.
58+
59+
Copyright FAQ
60+
=============
61+
62+
1. I don't understand the resale restriction... What gives?
63+
64+
Bitstream is giving away these fonts, but wishes to ensure its
65+
competitors can't just drop the fonts as is into a font sale system
66+
and sell them as is. It seems fair that if Bitstream can't make money
67+
from the Bitstream Vera fonts, their competitors should not be able to
68+
do so either. You can sell the fonts as part of any software package,
69+
however.
70+
71+
2. I want to package these fonts separately for distribution and
72+
sale as part of a larger software package or system. Can I do so?
73+
74+
Yes. A RPM or Debian package is a "larger software package" to begin
75+
with, and you aren't selling them independently by themselves.
76+
See 1. above.
77+
78+
3. Are derivative works allowed?
79+
Yes!
80+
81+
4. Can I change or add to the font(s)?
82+
Yes, but you must change the name(s) of the font(s).
83+
84+
5. Under what terms are derivative works allowed?
85+
86+
You must change the name(s) of the fonts. This is to ensure the
87+
quality of the fonts, both to protect Bitstream and Gnome. We want to
88+
ensure that if an application has opened a font specifically of these
89+
names, it gets what it expects (though of course, using fontconfig,
90+
substitutions could still could have occurred during font
91+
opening). You must include the Bitstream copyright. Additional
92+
copyrights can be added, as per copyright law. Happy Font Hacking!
93+
94+
6. If I have improvements for Bitstream Vera, is it possible they might get
95+
adopted in future versions?
96+
97+
Yes. The contract between the Gnome Foundation and Bitstream has
98+
provisions for working with Bitstream to ensure quality additions to
99+
the Bitstream Vera font family. Please contact us if you have such
100+
additions. Note, that in general, we will want such additions for the
101+
entire family, not just a single font, and that you'll have to keep
102+
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
103+
glyphs to the font, they must be stylistically in keeping with Vera's
104+
design. Vera cannot become a "ransom note" font. Jim Lyles will be
105+
providing a document describing the design elements used in Vera, as a
106+
guide and aid for people interested in contributing to Vera.
107+
108+
7. I want to sell a software package that uses these fonts: Can I do so?
109+
110+
Sure. Bundle the fonts with your software and sell your software
111+
with the fonts. That is the intent of the copyright.
112+
113+
8. If applications have built the names "Bitstream Vera" into them,
114+
can I override this somehow to use fonts of my choosing?
115+
116+
This depends on exact details of the software. Most open source
117+
systems and software (e.g., Gnome, KDE, etc.) are now converting to
118+
use fontconfig (see www.fontconfig.org) to handle font configuration,
119+
selection and substitution; it has provisions for overriding font
120+
names and subsituting alternatives. An example is provided by the
121+
supplied local.conf file, which chooses the family Bitstream Vera for
122+
"sans", "serif" and "monospace". Other software (e.g., the XFree86
123+
core server) has other mechanisms for font substitution.
124+

captcha/fonts/README.TXT

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Contained herin is the Bitstream Vera font family.
2+
3+
The Copyright information is found in the COPYRIGHT.TXT file (along
4+
with being incoporated into the fonts themselves).
5+
6+
The releases notes are found in the file "RELEASENOTES.TXT".
7+
8+
We hope you enjoy Vera!
9+
10+
Bitstream, Inc.
11+
The Gnome Project

captcha/fonts/Vera.ttf

64.4 KB
Binary file not shown.

captcha/helpers.py

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# -*- coding: utf-8 -*-
2+
import random
3+
from captcha.conf import settings
4+
5+
def math_challenge():
6+
operators = ('+','*','-',)
7+
operands = (random.randint(1,10),random.randint(1,10))
8+
operator = random.choice(operators)
9+
if operands[0] < operands[1] and '-' == operator:
10+
operands = (operands[1],operands[0])
11+
challenge = '%d%s%d' %(operands[0],operator,operands[1])
12+
return u'%s=' %(challenge), unicode(eval(challenge))
13+
14+
def random_char_challenge():
15+
chars,ret = u'abcdefghijklmnopqrstuvwxyz', u''
16+
for i in range(settings.CAPTCHA_LENGTH):
17+
ret += random.choice(chars)
18+
return ret.upper(),ret
19+
20+
def unicode_challenge():
21+
chars,ret = u'äàáëéèïíîöóòüúù', u''
22+
for i in range(settings.CAPTCHA_LENGTH):
23+
ret += random.choice(chars)
24+
return ret.upper(), ret
25+
26+
def word_challenge():
27+
fd = file(settings.CAPTCHA_WORDS_DICTIONARY,'rb')
28+
l = fd.readlines()
29+
fd.close()
30+
while True:
31+
word = random.choice(l).strip()
32+
if len(word) >= settings.CAPTCHA_DICTIONARY_MIN_LENGTH and len(word) <= settings.CAPTCHA_DICTIONARY_MAX_LENGTH:
33+
break
34+
return word.upper(), word.lower()
35+
36+
def noise_arcs(draw,image):
37+
size = image.size
38+
draw.arc([-20,-20, size[0],20], 0, 295, fill=settings.CAPTCHA_FOREGROUND_COLOR)
39+
draw.line([-20,20, size[0]+20,size[1]-20], fill=settings.CAPTCHA_FOREGROUND_COLOR)
40+
draw.line([-20,0, size[0]+20,size[1]], fill=settings.CAPTCHA_FOREGROUND_COLOR)
41+
return draw
42+
43+
def noise_dots(draw,image):
44+
size = image.size
45+
for p in range(int(size[0]*size[1]*0.1)):
46+
draw.point((random.randint(0, size[0]),random.randint(0, size[1])), fill=settings.CAPTCHA_FOREGROUND_COLOR )
47+
return draw
48+
49+
def post_smooth(image):
50+
import ImageFilter
51+
return image.filter(ImageFilter.SMOOTH)

captcha/management/__init__.py

Whitespace-only changes.

captcha/management/commands/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from django.core.management.base import BaseCommand, CommandError
2+
import sys
3+
4+
from optparse import make_option
5+
6+
class Command(BaseCommand):
7+
help = "Clean up expired captcha hashkeys."
8+
9+
def handle(self, **options):
10+
from captcha.models import CaptchaStore
11+
import datetime
12+
verbose = int(options.get('verbosity'))
13+
expired_keys = CaptchaStore.objects.filter(expiration__lte=datetime.datetime.now()).count()
14+
if verbose >= 1:
15+
print "Currently %s expired hashkeys" % expired_keys
16+
try:
17+
CaptchaStore.remove_expired()
18+
except:
19+
if verbose >= 1 :
20+
print "Unable to delete expired hashkeys."
21+
sys.exit(1)
22+
if verbose >= 1:
23+
if expired_keys > 0:
24+
print "Expired hashkeys removed."
25+
else:
26+
print "No keys to remove."
27+
28+

0 commit comments

Comments
 (0)