django-simple-captcha-0.5.1/0000700000076500000240000000000012630556666016100 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/.pep80000644000076500000240000000011212367460356016756 0ustar marcostaff00000000000000[flake8] ignore = E501 exclude = south_migrations,migrations,.venv_*,docs django-simple-captcha-0.5.1/captcha/0000700000076500000240000000000012630556666017503 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/__init__.py0000600000076500000240000000115712630244616021607 0ustar marcostaff00000000000000import re VERSION = (0, 5, 1) def get_version(svn=False): "Returns the version as a human-format string." return '.'.join([str(i) for i in VERSION]) def pillow_required(): def pil_version(version): try: return int(re.compile('[^\d]').sub('', version)) except: return 116 try: from PIL import Image, ImageDraw, ImageFont except ImportError: try: import Image import ImageDraw # NOQA import ImageFont # NOQA except ImportError: return True return pil_version(Image.VERSION) < 116 django-simple-captcha-0.5.1/captcha/conf/0000700000076500000240000000000012630556666020430 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/conf/__init__.py0000644000076500000240000000000012140410773022521 0ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/conf/settings.py0000600000076500000240000000563112627522275022644 0ustar marcostaff00000000000000import os from django.conf import settings CAPTCHA_FONT_PATH = getattr(settings, 'CAPTCHA_FONT_PATH', os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'fonts/Vera.ttf'))) CAPTCHA_FONT_SIZE = getattr(settings, 'CAPTCHA_FONT_SIZE', 22) CAPTCHA_LETTER_ROTATION = getattr(settings, 'CAPTCHA_LETTER_ROTATION', (-35, 35)) CAPTCHA_BACKGROUND_COLOR = getattr(settings, 'CAPTCHA_BACKGROUND_COLOR', '#ffffff') CAPTCHA_FOREGROUND_COLOR = getattr(settings, 'CAPTCHA_FOREGROUND_COLOR', '#001100') CAPTCHA_CHALLENGE_FUNCT = getattr(settings, 'CAPTCHA_CHALLENGE_FUNCT', 'captcha.helpers.random_char_challenge') CAPTCHA_NOISE_FUNCTIONS = getattr(settings, 'CAPTCHA_NOISE_FUNCTIONS', ('captcha.helpers.noise_arcs', 'captcha.helpers.noise_dots',)) CAPTCHA_FILTER_FUNCTIONS = getattr(settings, 'CAPTCHA_FILTER_FUNCTIONS', ('captcha.helpers.post_smooth',)) CAPTCHA_WORDS_DICTIONARY = getattr(settings, 'CAPTCHA_WORDS_DICTIONARY', '/usr/share/dict/words') CAPTCHA_PUNCTUATION = getattr(settings, 'CAPTCHA_PUNCTUATION', '''_"',.;:-''') CAPTCHA_FLITE_PATH = getattr(settings, 'CAPTCHA_FLITE_PATH', None) CAPTCHA_TIMEOUT = getattr(settings, 'CAPTCHA_TIMEOUT', 5) # Minutes CAPTCHA_LENGTH = int(getattr(settings, 'CAPTCHA_LENGTH', 4)) # Chars # CAPTCHA_IMAGE_BEFORE_FIELD = getattr(settings, 'CAPTCHA_IMAGE_BEFORE_FIELD', True) CAPTCHA_DICTIONARY_MIN_LENGTH = getattr(settings, 'CAPTCHA_DICTIONARY_MIN_LENGTH', 0) CAPTCHA_DICTIONARY_MAX_LENGTH = getattr(settings, 'CAPTCHA_DICTIONARY_MAX_LENGTH', 99) CAPTCHA_IMAGE_SIZE = getattr(settings, 'CAPTCHA_IMAGE_SIZE', None) CAPTCHA_IMAGE_TEMPLATE = getattr(settings, 'CAPTCHA_IMAGE_TEMPLATE', 'captcha/image.html') CAPTCHA_HIDDEN_FIELD_TEMPLATE = getattr(settings, 'CAPTCHA_HIDDEN_FIELD_TEMPLATE', 'captcha/hidden_field.html') CAPTCHA_TEXT_FIELD_TEMPLATE = getattr(settings, 'CAPTCHA_TEXT_FIELD_TEMPLATE', 'captcha/text_field.html') CAPTCHA_FIELD_TEMPLATE = getattr(settings, 'CAPTCHA_FIELD_TEMPLATE', 'captcha/field.html') CAPTCHA_OUTPUT_FORMAT = getattr(settings, 'CAPTCHA_OUTPUT_FORMAT', None) CAPTCHA_TEST_MODE = getattr(settings, 'CAPTCHA_TEST_MODE', getattr(settings, 'CATPCHA_TEST_MODE', False)) # Failsafe if CAPTCHA_DICTIONARY_MIN_LENGTH > CAPTCHA_DICTIONARY_MAX_LENGTH: CAPTCHA_DICTIONARY_MIN_LENGTH, CAPTCHA_DICTIONARY_MAX_LENGTH = CAPTCHA_DICTIONARY_MAX_LENGTH, CAPTCHA_DICTIONARY_MIN_LENGTH def _callable_from_string(string_or_callable): if callable(string_or_callable): return string_or_callable else: return getattr(__import__('.'.join(string_or_callable.split('.')[:-1]), {}, {}, ['']), string_or_callable.split('.')[-1]) def get_challenge(): return _callable_from_string(CAPTCHA_CHALLENGE_FUNCT) def noise_functions(): if CAPTCHA_NOISE_FUNCTIONS: return map(_callable_from_string, CAPTCHA_NOISE_FUNCTIONS) return [] def filter_functions(): if CAPTCHA_FILTER_FUNCTIONS: return map(_callable_from_string, CAPTCHA_FILTER_FUNCTIONS) return [] django-simple-captcha-0.5.1/captcha/fields.py0000600000076500000240000001564012627522275021326 0ustar marcostaff00000000000000from captcha.conf import settings from captcha.models import CaptchaStore, get_safe_now from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse, NoReverseMatch from django.forms import ValidationError from django.forms.fields import CharField, MultiValueField from django.forms.widgets import TextInput, MultiWidget, HiddenInput from django.utils.translation import ugettext_lazy from django.template.loader import render_to_string from django.utils.safestring import mark_safe from six import u class BaseCaptchaTextInput(MultiWidget): """ Base class for Captcha widgets """ def __init__(self, attrs=None): widgets = ( HiddenInput(attrs), TextInput(attrs), ) super(BaseCaptchaTextInput, self).__init__(widgets, attrs) def decompress(self, value): if value: return value.split(',') return [None, None] def fetch_captcha_store(self, name, value, attrs=None): """ Fetches a new CaptchaStore This has to be called inside render """ try: reverse('captcha-image', args=('dummy',)) except NoReverseMatch: raise ImproperlyConfigured('Make sure you\'ve included captcha.urls as explained in the INSTALLATION section on http://readthedocs.org/docs/django-simple-captcha/en/latest/usage.html#installation') key = CaptchaStore.generate_key() # these can be used by format_output and render self._value = [key, u('')] self._key = key self.id_ = self.build_attrs(attrs).get('id', None) def id_for_label(self, id_): if id_: return id_ + '_1' return id_ def image_url(self): return reverse('captcha-image', kwargs={'key': self._key}) def audio_url(self): return reverse('captcha-audio', kwargs={'key': self._key}) if settings.CAPTCHA_FLITE_PATH else None def refresh_url(self): return reverse('captcha-refresh') class CaptchaTextInput(BaseCaptchaTextInput): def __init__(self, attrs=None, **kwargs): self._args = kwargs self._args['output_format'] = self._args.get('output_format') or settings.CAPTCHA_OUTPUT_FORMAT self._args['field_template'] = self._args.get('field_template') or settings.CAPTCHA_FIELD_TEMPLATE self._args['id_prefix'] = self._args.get('id_prefix') if self._args['output_format'] is None and self._args['field_template'] is None: raise ImproperlyConfigured('You MUST define either CAPTCHA_FIELD_TEMPLATE or CAPTCHA_OUTPUT_FORMAT setting. Please refer to http://readthedocs.org/docs/django-simple-captcha/en/latest/usage.html#installation') if self._args['output_format']: for key in ('image', 'hidden_field', 'text_field'): if '%%(%s)s' % key not in self._args['output_format']: raise ImproperlyConfigured('All of %s must be present in your CAPTCHA_OUTPUT_FORMAT setting. Could not find %s' % ( ', '.join(['%%(%s)s' % k for k in ('image', 'hidden_field', 'text_field')]), '%%(%s)s' % key )) super(CaptchaTextInput, self).__init__(attrs) def build_attrs(self, extra_attrs=None, **kwargs): ret = super(CaptchaTextInput, self).build_attrs(extra_attrs, **kwargs) if self._args.get('id_prefix') and 'id' in ret: ret['id'] = '%s_%s' % (self._args.get('id_prefix'), ret['id']) return ret def id_for_label(self, id_): ret = super(CaptchaTextInput, self).id_for_label(id_) if self._args.get('id_prefix') and 'id' in ret: ret = '%s_%s' % (self._args.get('id_prefix'), ret) return ret def format_output(self, rendered_widgets): hidden_field, text_field = rendered_widgets if self._args['output_format']: return self._args['output_format'] % { 'image': self.image_and_audio, 'hidden_field': self.hidden_field, 'text_field': self.text_field } elif self._args['field_template']: context = { 'image': mark_safe(self.image_and_audio), 'hidden_field': mark_safe(self.hidden_field), 'text_field': mark_safe(self.text_field) } return render_to_string(settings.CAPTCHA_FIELD_TEMPLATE, context) def render(self, name, value, attrs=None): self.fetch_captcha_store(name, value, attrs) context = { 'image': self.image_url(), 'name': name, 'key': self._key, 'id': u'%s_%s' % (self._args.get('id_prefix'), attrs.get('id')) if self._args.get('id_prefix') else attrs.get('id') } if settings.CAPTCHA_FLITE_PATH: context.update({'audio': self.audio_url()}) self.image_and_audio = render_to_string(settings.CAPTCHA_IMAGE_TEMPLATE, context) self.hidden_field = render_to_string(settings.CAPTCHA_HIDDEN_FIELD_TEMPLATE, context) self.text_field = render_to_string(settings.CAPTCHA_TEXT_FIELD_TEMPLATE, context) return super(CaptchaTextInput, self).render(name, self._value, attrs=attrs) class CaptchaField(MultiValueField): def __init__(self, *args, **kwargs): fields = ( CharField(show_hidden_initial=True), CharField(), ) if 'error_messages' not in kwargs or 'invalid' not in kwargs.get('error_messages'): if 'error_messages' not in kwargs: kwargs['error_messages'] = {} kwargs['error_messages'].update({'invalid': ugettext_lazy('Invalid CAPTCHA')}) kwargs['widget'] = kwargs.pop('widget', CaptchaTextInput( output_format=kwargs.pop('output_format', None), id_prefix=kwargs.pop('id_prefix', None) )) super(CaptchaField, self).__init__(fields, *args, **kwargs) def compress(self, data_list): if data_list: return ','.join(data_list) return None def clean(self, value): super(CaptchaField, self).clean(value) response, value[1] = (value[1] or '').strip().lower(), '' CaptchaStore.remove_expired() if settings.CAPTCHA_TEST_MODE and response.lower() == 'passed': # automatically pass the test try: # try to delete the captcha based on its hash CaptchaStore.objects.get(hashkey=value[0]).delete() except CaptchaStore.DoesNotExist: # ignore errors pass elif not self.required and not response: pass else: try: CaptchaStore.objects.get(response=response, hashkey=value[0], expiration__gt=get_safe_now()).delete() except CaptchaStore.DoesNotExist: raise ValidationError(getattr(self, 'error_messages', {}).get('invalid', ugettext_lazy('Invalid CAPTCHA'))) return value django-simple-captcha-0.5.1/captcha/fonts/0000700000076500000240000000000012630556666020634 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/fonts/COPYRIGHT.TXT0000644000076500000240000001350212140410773022600 0ustar marcostaff00000000000000Bitstream Vera Fonts Copyright The fonts have a generous copyright, allowing derivative works (as long as "Bitstream" or "Vera" are not in the names), and full redistribution (so long as they are not *sold* by themselves). They can be be bundled, redistributed and sold with any software. The fonts are distributed under the following copyright: Copyright ========= Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Copyright FAQ ============= 1. I don't understand the resale restriction... What gives? Bitstream is giving away these fonts, but wishes to ensure its competitors can't just drop the fonts as is into a font sale system and sell them as is. It seems fair that if Bitstream can't make money from the Bitstream Vera fonts, their competitors should not be able to do so either. You can sell the fonts as part of any software package, however. 2. I want to package these fonts separately for distribution and sale as part of a larger software package or system. Can I do so? Yes. A RPM or Debian package is a "larger software package" to begin with, and you aren't selling them independently by themselves. See 1. above. 3. Are derivative works allowed? Yes! 4. Can I change or add to the font(s)? Yes, but you must change the name(s) of the font(s). 5. Under what terms are derivative works allowed? You must change the name(s) of the fonts. This is to ensure the quality of the fonts, both to protect Bitstream and Gnome. We want to ensure that if an application has opened a font specifically of these names, it gets what it expects (though of course, using fontconfig, substitutions could still could have occurred during font opening). You must include the Bitstream copyright. Additional copyrights can be added, as per copyright law. Happy Font Hacking! 6. If I have improvements for Bitstream Vera, is it possible they might get adopted in future versions? Yes. The contract between the Gnome Foundation and Bitstream has provisions for working with Bitstream to ensure quality additions to the Bitstream Vera font family. Please contact us if you have such additions. Note, that in general, we will want such additions for the entire family, not just a single font, and that you'll have to keep both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add glyphs to the font, they must be stylistically in keeping with Vera's design. Vera cannot become a "ransom note" font. Jim Lyles will be providing a document describing the design elements used in Vera, as a guide and aid for people interested in contributing to Vera. 7. I want to sell a software package that uses these fonts: Can I do so? Sure. Bundle the fonts with your software and sell your software with the fonts. That is the intent of the copyright. 8. If applications have built the names "Bitstream Vera" into them, can I override this somehow to use fonts of my choosing? This depends on exact details of the software. Most open source systems and software (e.g., Gnome, KDE, etc.) are now converting to use fontconfig (see www.fontconfig.org) to handle font configuration, selection and substitution; it has provisions for overriding font names and subsituting alternatives. An example is provided by the supplied local.conf file, which chooses the family Bitstream Vera for "sans", "serif" and "monospace". Other software (e.g., the XFree86 core server) has other mechanisms for font substitution. django-simple-captcha-0.5.1/captcha/fonts/README.TXT0000644000076500000240000000050012140410773022157 0ustar marcostaff00000000000000Contained herin is the Bitstream Vera font family. The Copyright information is found in the COPYRIGHT.TXT file (along with being incoporated into the fonts themselves). The releases notes are found in the file "RELEASENOTES.TXT". We hope you enjoy Vera! Bitstream, Inc. The Gnome Project django-simple-captcha-0.5.1/captcha/fonts/Vera.ttf0000644000076500000240000020061412140410773022245 0ustar marcostaff00000000000000OS/2_cpVPCLTъ^6cmaplXcvt 9fpgm&`gaspH glyf tA&~hdmx4!Hhead݄T6hheaEoL$hmtx Ǝ0kernRՙ-loca=maxpG:, nameټȵpostZ/prep; h::_:: dM0l   p t  &   Y &  &   c . 5 `  s 0 & {Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com5fqu-J3T99NR7s`s3VV9s3D{o{RoHT3fs +b-{T#\q#H99`#fy```{w``b{{Rffw;{J/}oo5jo{-{T7fD)fs@%2%%A:B2SAS//2ݖ}ٻ֊A}G}G͖2ƅ%]%]@@%d%d%A2dA  d   A(]%]@%..%A  %d%@~}}~}}|d{T{%zyxw v utsrqponl!kjBjSih}gBfedcba:`^ ][ZYX YX WW2VUTUBTSSRQJQP ONMNMLKJKJIJI IH GFEDC-CBAK@?>=>=<=<; <@; :987876765 65 43 21 21 0/ 0 / .- .- ,2+*%+d*)*%)('%(A'%&% &% $#"!! d d BBBdB-B}d       -d@--d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ /10!%!!fsr)5 @@ <2991/0K TX @ 878Y P ]%3#3#5qeM@1<20KTKT[X@878Y@0 @ P ` p ]#!#o$++`@1      91/<<<<<<<2220@   ]!! !3!!!!#!#!5!!5!T%Dh$ig8R>hggh`TifaabbNm!(/@U" '&( /)/))/B" ) *!#*- ) " & 0<<<1/299990KSX99Y"K TX0@00878YK TKT[KT[X000@878Y#.'5.546753.'>54&dijfod]SS\dtzq{---@A$*.U# jXV`OnZXhq) #'3@6$%&%&'$'B .$ &($4'!%   ! + 1 49912<0KSXY"K TK T[K T[KT[KT[K T[X4@44878Y"32654&'2#"&546"32654&%3#2#"&546WccWUccUVcbWWcd1Zܻۻa ۻۼ 0@      !         B  (('+'$ .  .'.'!!199999991/9990KSX99999999Y"2]@ " ) **&:4D ^YZ UZZY0g{ "-  ' (   2'') #**(/2; 49?2J LKFO2VZ Y UY\_2j i`2uy z 2229]]3267 >73#'#"5467.54632.#"[UԠ_I{;B h]hΆ02޸SUWDi;#QX?@Yr~YW׀c?}<$$/1oX3goB@ 10KTKT[X@878Y@ @P`p]#o+{ O@  29910KTX@878YKTX@878Y#&547{>;o @ <99103#654<:=JN@,       <2<2991<22990%#'%%73%g:r:g:PrPbybcy #@   <<1/<<0!!#!5!-Ө-Ӫ--@ 1073#ӤR@d10!!d1/073#B-@B/9910KSXY"3#m #@  10"32'2#"  P3343ssyzZ K@B  1/20KSXY"KTX  @878Y]7!5%3!!JeJsHHժJ@'B   91/20KSX9Y"KTKT[KT[X@878Y@2UVVzzvtvust]]%!!567>54&#"5>32Ls3aM_xzXE[w:mIwBC12\ps({@.    #)&  )99190KTKT[X)@))878Y@ daa d!]!"&'532654&+532654&#"5>32?^jTmǹSrsY %Đ%%12wps{$& Ѳ|d @   B    <291/<290KSXY"K TK T[X@878Y@* *HYiw+&+6NO O Vfuz ]] !33##!55^%3`du@#    190KTKT[X@878YKTX@878Y!!>32!"&'532654&#",X,$^hZkʭQTժ 10$& $X@$  "% " !%190@]]"32654&.#">32# !2 LL;kPL;y$&W]ybhc@B991/0KSXY"KTX@878Y@X9Hg]]!#!3V+ #/C@% '-'0 $*$ !0991990"32654&%&&54632#"$54632654&#"HŚV г "Əُattt$X@# %!"" %190@]]7532#"543 !"&2654&#"LK:lL>$& V\s[#@<21/073#3### %@  <2103#3#ӤR#٬@^M@*B$#29190KSXY" 5Ѧ`@ #<210!!!!^O@+B$#<9190KSXY"55//m$p@+$     &%99991/9990K TX%@%%878Yy z z ]%3##546?>54&#"5>32ſ8ZZ93lOa^gHZX/'eVY5^1YnFC98ŸLVV/5<4q L@2  L4307$7CM34( (+(I+*(I,=M<9912990K TK T[KT[KT[KT[XMMM@878Y@ NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32|{zy!orqp ˘s'6@   0210].# !267# !2'ffjzSb_^^_HHghG.@   2 99991/0`]3 !%! )5BhPa/w.,~ .@   21/0 ]!!!!!!9>ժF# )@ 21/0 ]!!!!#ZpPժH7s9@ 43 1990%!5!# !2&&# !26uu^opkSUmnHF_`%; ,@ 8  221/<20P ]3!3#!#"d+991/0KTX@878Y@ 0@P`]3#+f M@  9 991990KTX  @878Y@ 0 @ P ` ]3+53265M?nj @(B  291/<290KSXY"]@ ((764GFCUgvw    (+*66650 A@E@@@ b`hgwp  ,]q]q3! !#3wH1j%@ :1/0@ 0P]3!!_ժ @4  B    >  91/<290KSXY"p]@V   && & 45 i|{y   #,'( 4<VY ej vy ]]! !###-}-+3 y@B6 991/<2990KSXY" ]@068HGif FIWXeiy ]]!3!#j+s #@  310"32' ! ':xyLHH[[bb:@   ? 291/0@ ?_]32654&#%!2+#8/ϒs R@*  B     39991990KSX9Y""32#'# ! '? !#y;:xLHHab[T@5  B    ?  299991/<9990KSX9Y"@]@Bz%%%&'&&& 66FFhuuw]]#.+#! 32654&#A{>ٿJx~hb؍O'~@<    B %( "-"(9999190KSX99Y")])/)O)].#"!"&'532654&/.54$32Hs_wzj{r{i76vce+ٶ0/EF~n|-&J@@@1/20K TX@878Y@  @ p ]!!#!ժ+)K@   8A1299990KTX@878Y]332653! ˮ®u\*$h@'B91/290KSXY"P]@b*GGZ} *&&))% 833<<7HEEIIGYVfiizvvyyu)]]!3 3J+D {@I      B     91/<2290KSXY"]@  ($ >>4 0 LMB @ Yjkg ` {|      !   # $ %  <:5306 9 ? 0FFJ@E@BBB@@ D M @@XVY Pfgab```d d d wv{xwtyywpx   []]3 3 3# #D:9:9+=; ]@F      B    91/<290KSXY"K TK T[KT[X  @878Y@ '' 486 KX[fkww       &()&(' ) 54<;:;4 4 8 ? H O X _ eejjhiil l xyyx}  x   @]]3 3 # #su \Y+3{@(B@@ 91/290KSXY" ]@<5000F@@@QQQe &)78@ ghxp ]]3 3#f9\ @BB 991/0KSXY"K TK T[X @ 878Y@@ )&8HGH    / 59? GJO UYfio wx ]]!!!5!sP=g՚oXS@C210K TX@878YKTKT[X@878Y!#3!XB-@B/9910KSXY"#mo<@C<10KTKT[X@878Y!53#5oXޏ@ 91290##HHu-10!5f1@ D10K TKT[X@878Y #ofv{-{ %@'   #   E&22991/9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p' !"'''000 0!@@@ @!PPP P!``` `!ppp p! !]]"326=7#5#"&5463!54&#"5>32߬o?`TeZ3f{bsٴ)Lfa..'' 8@  G F221/0`]4&#"326>32#"&'#3姒:{{:/Rdaadq{?@  HE210@ ].#"3267#"!2NPƳPNM]-U5++++$$>:#qZ8@G E221/0`]3#5#"3232654&#":||ǧ^daDDaq{p@$   KE9190@)?p?????,// , ooooo ]q]!3267# 32.#" ͷjbck)^Z44*,8 Cė/p@     L<<991/22990K TX@878YKTX@878Y@P]#"!!##535463cM/ѹPhc/яNqVZ{ (J@#  &#' & G E)221/990`***]4&#"326!"&'5326=#"3253aQQR9||9=,*[cb::bcd4@  N  F21/<90`]#4&#"#3>32d||Bu\edy+@F<21/0@  @ P ` p ]3#3#`Vy D@   O  F<2991990@ @P`p]3+532653#F1iL`a( @)B F 291/<90KSXY" ]@_ ')+Vfgsw    ('(++@ h` ]q]33 ##%kǹi#y"F1/0@ @P`p]3#{"Z@&   PPF#291/<<<290@0$P$p$$$$$$$ ]>32#4&#"#4&#"#3>32)Erurw?yz|v\`gb|d{6@  N  F21/<90`]#4&#"#3>32d||Bu\`edqu{ J@  QE10@#?{{   {  {]"32654&'2#"s98V{>@ GF2210@ `]%#3>32#"&4&#"326s:{{8 daaqVZ{ >@   GE2210@ `]32654&#"#"3253#/s:||:/daDDadJ{0@    F21/90P].#"#3>32JI,:.˾`fco{'@<  S  SB %( R"E(9999190KSX99Y"']@m   . , , , ; ; ; ; $( ( *//*(() )!$'      '/)?)_))))))]]q.#"#"&'532654&/.54632NZb?ĥZlfae@f?((TT@I!*##55YQKP%$78@  F<<2991/<2990]!!;#"&5#53w{KsբN`>X`6@    NF21/290`]332653#5#"&||Cua{fc=`@'B91/290KSXY"K TX@878YKTKT[X@878Y@Hj{  &&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz>]]3 3#=^^\`TV5` @IU U U U   B     91/<2290KSXY"K TKT[KT[KT[K T[X  @878YK TK T[KT[X @ 878Y@" 5 IIF @ [[U P nnf yy          %%#'!%""%' $ ! # 9669 0FHF@B@@@D D D @@VVVPQRRPS T U cdejejjjn a g ouuy}x}zzxy  { v } @/   y]]333# #V`jjj;y` Z@F      B   91/<290KSXY"K TKT[KT[KT[X  @878YKTX @ 878Y@   & =1 UWX f vzvt        )&% * :9746 9 0 IFE J @ YVYYWVYVV Y P o x  /]] # # 3 dkr))`HJq=V`@C        B     9129990KSX2Y"K TKT[X@878YKTX@878Y@     # 5 I O N Z Z j        '$$  )( % $ $ ' ** 755008 6 6 8 990A@@@@@@@@B E G II@TQQUPPVUVW W U U YYPffh ii`{xx   e]]+5326?3 3N|lLT3!;^^hzHTNlX` @B 2991/0KSXY"K TK T[X @ 878YKTX  @878Y@B&GI  + 690 @@E@@CWY_ ``f``b ]]!!!5!qjL}e`ۓ%$@4 %   !  % $  C %<<29999999199999990K TX%%%@878Y&]#"&=4&+5326=46;#"3>l==k>DV[noZVtsݓXX10#$@6%   #%#C %<2<9999999199999990K TX%@%%878YKTX%%%@878Y&]326=467.=4&+532;#"+FUZooZUF?l>>l?VWstݔ1#@  1990#"'&'&'&#"56632326ian ^Xbian ^V1OD;>MSOE<>LhN'$uhm !@T   !!  ! !!!B     !  VV!"2299999991/<9990KSXY" #]@  s P#f iu {yyv v!# ]]4&#"326!.54632#!#TY?@WX??Y!X=>sr?<҈_Z?YWA?XXN)sIsrFv)su''&-k'(u3^'1usN'2'u)N'8u{-f'DR{-f'DCR{-f'DR{-'DR{-7'DR{-'DRqu{'Fqf'Hqf'HCqf'Hq'Hof'f'C\f'F'd7'Qquf'Rsquf'RCsquf'Rsqu'Rsqu7'RsXf'X{Xf'XC{Xf'X{X'X{9; '@  YW Y <<1<203!!#!5!oo\]u=  @  Z[Z10"32654&'2#"&546PnnPPnoO@v+..ooPOmmOOp1.-rB#!Q@+     "  "<<<221<9990%&&'667#&73JDFHAMf fIX⸹)**'# 32!b`@!    <<1/2<2990K TX@878Y66].#"!!!!53#535632NL=ty-=))׏/я\= >@54&.#"#"&'532654/.5467.54632{?>?>S8alӃ\]>9̭IXW:fqր][;;ȦI.Z.L-[.K''PGZsweZ54m@''TLf{xf[1,pE3!   \ 104632#"&3~|}}||};9 %@]] 91290!###&&54$yfNݸ/@0-'!  **.  !' $'$-F099991/990@@'(     ! "&  : :!MM I!I"jj  ]]4632#"&'532654&/.5467.#"#:A9`@IPAtx;e\`Wqqs`/Q*%jd_[?T>7;[gp/8L`@6EBC?2H09JC 9 $HE301BKL?gwyVpMI`3D/IC@&=>:A$104G$ 7aD=0^* D^ J21/02#"$'&5476$"32676654&'&&&&#"3267#"&54632mmllmmmmllmm^^``^^⃄^]]^\^BB@zBCFInmmmmnnmmmmng^^^傁^^__^]⃅]^^! "'F >@!    b b cbc91<<2<<903#######5Jq7rqr/B^^sRf1@ D10K TKT[X@878Y3#fF)@dd1<20K TK T[X@878YK TK T[KT[KT[X@878YKTKT[X@878Y@````pppp]3#%3#^y'>@"     <291<2<<990!!!!!'7!5!7!}/H{};fըfӪH@9  B     <291/<0KSXY"]@gww  ]!!!!!!#!59=qժF՞f +@< +,  )&  *&& &,+,* # )#3,99999999199999990@*WZWU!je!{vu! FYVjddj(|svz( ]] 324&'.#"&5!27!"&''3>_'y=_''NOy;WfNPƀ[gX@CHp@CpDfbMKYg[KKX /@- !$'!!0 $*0999919990@     $$$   $$ $ ***///***55500055 5 :::???:::EEE@@@EE E JJJOOOJJJV !"&'()]]32654&#".#"326#"&54632>32#"&1TevYR1UfvYRF^_HDa^/XZie7XXjeߦ~᧯w .@     <2<21/<<0!!#!5!!!-Ө-}} T@.B $# <2291/90KSXY" 5!!@po V@/B$ # <<291/90KSXY"55!5AǪR@F  B     fe f e<2299991/2<2<290KSXY"K TX@878Y@(' ' ')((79  ]]!#!5!5'!5!3 3!!!c`Tþ{yT9{3{JD{3V` M@%  !   NF!2912<990"`""]3326533267#"&'#"&'#% )I#ER2bf*V H<9 NPOONNh-)b@'! '!* $$*9991990K TK T[KT[KT[KT[X*@**878Y>54&#"#"&54632#"&54324&#"32IH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{w; ]@    91990@0QVPZ spvupz  Z pp{ t  ]]!! !!5 7AJI3!wq@gg120!#!# }/#@1 " $ #" #h#$9999991/<229990K TX$$$@878Y@V             ##(]]#3267#"&5467!##"#>3!i/7.%7vy"Pµ)6< yJ\:1fd.xo@E}/%&@  & iji&1026732#"&'&&#"#"&546327j Pd@7*8  kOeD=!0 l9TA6?&#Hn!bSA8?Ss;)_@3(%%  * "(kl"k *22999199990!!#5#"&5463354&#"56632"32655P,]uu>DIE~bRhP{@p?Dq[[""CO@Mr`d.@  klk 9910!!2#"&546"32654&PXγгi~hi}|P{ݿܾsN@@"   mm  9991/<20%!5654#"!5!&5! Dz?1/aL"a*>w؍{o{3>@C'-%= 4%:.-*1 %?47&%7& =&-7"E?<9999912<<29990@0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0+0@@@@@@@@@??? ??0,0-0.0/@,@-@.@/P,P-P.P/ooo oo`,`-`.`/p,p-p.p/,-./]q].#">32!3267#"&'#"&5463!54&#"5>32"326=DJԄ ̷hddjMI؏`TeZ߬o0Z^Z55*,ywxx..''`f{bsٴ)H +@<+,&  )&  *&& &,+,* # #Q)E,22999999199999990@p(?-YVUV jf!{    { z{ {!"#$%{&%--&YVUZ(ifej(ztvz($$]] 32654&'.#".5327#"&'')gA\*g>}66]C_56`?`!*(Ou))Hn.Mw834OMx43N $@/  !# #%" " "!& %999919990KTKT[KT[X%%%@878Y@ ttttv]33267#"&546?>7>5#537ZZ:3mN`^gIYX0&DeWX5^1YnFC98ŸLVV/5<65 b@ <2991/0K TX @ 878YKTKT[KT[X  @878Y P ]#53#3+e^@ 10!#!^=} *@    91903##'%\sB}}`s-Pb;V#@@   B   !$  $912299990KSX29Y"K TX$$$@878Y.#"!!#"&'53267#5!>32&P,`r<::d/4a/am"?$Ɨ5dzɏ!!J;?@.9*-" *19" <-<<219999990#"'&'&'&#"56632326#"'&'&'&#"56632326ian ^Xbian ^Vgian ^Xbian ^VoNE;=LTNE;=KڲOE;=LSNE;=K`8@91/90@cmpxyvn]] !3!^DC?%# @I    B   o o n<2991<2990KSXY"55%-+#-+#RRH# @I  B   o op<<991<2990KSXY"5%5+-+-#^R^  ^R^   #@   1/<<220%3#%3#%3#hk'$uh^'$us^'2'us ;@   299991/220!!!!! !# !39OAg@AժF|pm|q{'3@1 . ("%4"1 K1 Q+E499912<2290@%?5_5p55555????? ooooo ]q].#"!3267#"&'#"32>32%"32654& H ̷jbdjQGьBN5Z44*,nmnm98olkp݇y/10!!yy/10!!ym '@   1<20#53#53ӤRӤR??m '@   1<203#%3#ӤRӤRլ@@@ 10#53ӤR?@ q103#ӤR՘?o )@ r <<103#3#!!oA#u"@91990  9%-=V'\^N'<su+@B10KSXY"3#-\^R#/@I -'! - -'!0 *$0* $ $(st*(s099999999919999999907'#"&''7&&5467'766324&#"326{r%$&(r;t=:x=q%%&&s7t@?s9q(&%%s>v:@t8s'%$|pprs#G@%Bon29190KSXY"5s-+#R#I@&Bop<9190KSXY"5+-#^R^  /J@(   L<2<2991/<22990K TX@878YKTX@878Y@0P]]#!##53546;#"3#JcM`/яNPhc/J@!    L<<991/<22990K TX@878YKTX@878Y@0P ]!#!"!!##53546JcM/ѹ{Phc/яN9;>@   Y W Y <<2<<2122220%!#!5!!5!3!!!oooo\\HF103#F@ 10%3#ӤR@m '@    1<20%3#%3#ӤRfӤR@@q L #'3?K@D$%&%&'$'B@ .(F4 :&$L%IC'1+C =  1 =I 7+ ! L9912<<2220KSXY"KTK T[K T[K T[K T[KT[XL@LL878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&WddWUccUt%ZVcbWWcdWccWUccܻۻۻۼܻۻhm'$um'(uhk'$uN'(uk'(uk',/u`m',/uXN',/u;k',/usk'2'usm'2'usk'2'u)k'8u)m'8u)k'8uy` F1/0@ @P`p]3#`?f7@ u91290K TKT[X@878Y3#'#fJ7c@$   VwVv99991<<99990K TK T[X@878Y'.#"#>3232673#"&9! &$}f[&@%9! &$}f[&@Z7IR!7IRb+/10K TKT[X@878Y!!V)9H W@ VV1<0K TX@878YKTKT[KT[X@878Y332673#"&v aWV` v HKKJLDf,@ d10K TX@878Y3# _@ V xV10K TK T[X@878YK TK T[K T[X@878Y4&#"3267#"&54632X@AWWA@Xzssss?XW@AWX@sss#u@  ' 1/90!#"&'532654&'T76xv.W+"J/;<+->i0Y[ 0.W=fB@991<20K TKT[X@878Y3#3#߉fxLu @   '1/90!33267#"&546w-+76 >&Dzs5=X.. W]0i?f7@ u91<90K TKT[X@878Y373xu ?@   : y<<991/900P]3%!!'79Pw^Mo;jnH ^@  z z <<991/90KTX @ 878Y@ @ P ` sz p ]37#'7Ǹ}Lɸ{JZjXjm'6uof'V\m'=uXf']@ <210##    g@    2  y<291/220@(   ]]! )#53!!3 !iP`P5~.,qu('@^%{&%#${##{#({'(#&'('%$%(('"#" ! B('&%"! ## #)&' ! (%#" QE)999999919990KSXY"?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{     {zzv v!x"**']].#"32654&#"5432''%'3%F2X)6 ~r4*!M!ü޼z&77kc\̑oabk'<su=Vf'\^ =@   ? 2291/0@ ?_]332+#32654&#'ђV>@ GF2210@ `]%#3>32#"&4&#"326s:{{8daa-10!!ת? @M    B   <291<290KSXY" '77w55v8vL57y5yy5 ,@   |]|| 12035733! c)t'+n^J@$}}B ~9190KSX2Y"!!56754&#"56632 "?XhU4zHM98rn81^BQ##{l0b(H@'    #)~&~ )999190#"&'532654&##532654&#"56632 \e9}F4wCmxolV^^ad_(fQI7Z`mR|yOFJLl?<:=svcE`''5 d?''5db''5 dsm'* uqVZH'JP', /uu'6ou{'Vs'k'&-uqf'Fs'm'&-uqf'Fq$J@$ "    GE%<<1/<20`&&&]!5!533##5#"3232654&#"F:||ǧN}}daDDad10!!dHF103#F1@: "+ /) 2+"!)#&  , & &*!/<29999999999122<20K TK T[K T[KT[KT[KT[X222@878Y@z  1Ti lnooooiko o!o"o#n$l%i'i-  !"#$%&'()*+,-2   USjg ]].#"!!!!3267#"#734&5465#7332[f A78 ʝf[Y`(77(6bbiZȻ{.# .{ZiHH"{/ #/{"G)@ dd1<20KTKT[X@878YKTK T[KT[X@878YKTKT[X@878YKTX@878Y@````pppp]3#%3#^ys@B10KSXY"K TX@878YKTX@878Y@ %%6FVjg //]]3#7Ju@!  VV 99991<2990K TX@878YKTX@878Y ]'.#"#4632326=3#"&9 $(}gV$=09" (}gT";9! 2-ev 3)dw @B10KSXY"K TX@878YKTX@878Y@*$$5CUU//]]#ę1w@ 91<90K TX@878YKTX@878YKTX@878Y@ //- ]3#'#Ӌ1@ 91290K TK T[K T[K T[X@878YKTX@878YKTX@878Y@ "  ]373Ӌ ? @   ] <291<290KTKT[KT[KT[K T[K T[X@878YKTKT[X@878Y@T /9IFYi       "5GK S[ e]] !33##5!55bf]my9 j@ VV120K TX@878YKTX@878YKTKT[X@878Y332673#"&v cSRav 6978w{zf103#  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f55q=3=dd?y}s)3s\\?uLsLsyD{={\{fqqq/q999qqJ+o#7=V;=3XyysLs{{{{{{fqqqqq9999qqqqq9\3 'sLfR#hd+/s`N{H?55=ZyyLss/q%%=V^33 / /9% qyy\\\\;LsLsLs9#LF+o{\3X3 q=55^5bb3sq\+osfqsfqqds 5?+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""XO!nE~Le  R s  X : i  = z /Eu)pP@"m#{CwRw [ r !5!B!!!" ""#"0"="J"W"d"q"~"""""""""## ##'#4#A#N#[#h##$4$%3%S%&&'K''((X()_*%*\**+z+,D,,-P-..R./0A011!1P12H2z23F3p3p3}3334z44445595g55556[667C77888J999)969C9P9]9j9w99999999::{::;;^;;;<"<_<<<<<<=c>;>H>U>>>?a??@:@K@\@m@z@@@@@@@@A@AVAkBEBBC_CCDUDE*E?- x$%&')*K+-r./2934K57D9:;< =IQR&UYZ\bdg9xy&z&{&|&}&9 999 K$$$$$9$&$*$2$4$6$7a$8$9}$:$;$= settings.CAPTCHA_DICTIONARY_MIN_LENGTH and len(word) <= settings.CAPTCHA_DICTIONARY_MAX_LENGTH: break return word.upper(), word.lower() def huge_words_and_punctuation_challenge(): "Yay, undocumneted. Mostly used to test Issue 39 - http://code.google.com/p/django-simple-captcha/issues/detail?id=39" fd = open(settings.CAPTCHA_WORDS_DICTIONARY, 'rb') l = fd.readlines() fd.close() word = '' while True: word1 = random.choice(l).strip() word2 = random.choice(l).strip() punct = random.choice(settings.CAPTCHA_PUNCTUATION) word = '%s%s%s' % (word1, punct, word2) if len(word) >= settings.CAPTCHA_DICTIONARY_MIN_LENGTH and len(word) <= settings.CAPTCHA_DICTIONARY_MAX_LENGTH: break return word.upper(), word.lower() def noise_arcs(draw, image): size = image.size draw.arc([-20, -20, size[0], 20], 0, 295, fill=settings.CAPTCHA_FOREGROUND_COLOR) draw.line([-20, 20, size[0] + 20, size[1] - 20], fill=settings.CAPTCHA_FOREGROUND_COLOR) draw.line([-20, 0, size[0] + 20, size[1]], fill=settings.CAPTCHA_FOREGROUND_COLOR) return draw def noise_dots(draw, image): size = image.size for p in range(int(size[0] * size[1] * 0.1)): draw.point((random.randint(0, size[0]), random.randint(0, size[1])), fill=settings.CAPTCHA_FOREGROUND_COLOR) return draw def noise_null(draw, image): return draw def post_smooth(image): try: import ImageFilter except ImportError: from PIL import ImageFilter return image.filter(ImageFilter.SMOOTH) def captcha_image_url(key): """ Return url to image. Need for ajax refresh and, etc""" return reverse('captcha-image', args=[key]) django-simple-captcha-0.5.1/captcha/locale/0000700000076500000240000000000012630556666020742 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/bg/0000700000076500000240000000000012630556666021332 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/bg/LC_MESSAGES/0000700000076500000240000000000012630556666023117 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/bg/LC_MESSAGES/django.mo0000644000076500000240000000126612630244743024723 0ustar marcostaff00000000000000<\pq=0W-Invalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.4.1 Report-Msgid-Bugs-To: POT-Creation-Date: 2014-02-10 14:43+0200 PO-Revision-Date: 2014-02-10 15:00+0200 Last-Translator: Venelin Stoykov Language-Team: bg Language: bg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1) Сгрешен текстЧуй текста като аудио файлТова поле е задължителноdjango-simple-captcha-0.5.1/captcha/locale/bg/LC_MESSAGES/django.po0000600000076500000240000000204412560061626024710 0ustar marcostaff00000000000000# django-simple-captcha Bulgarian translation # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the django-simple-captcha package. # Venelin Stoykov , 2014. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.4.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-02-10 14:43+0200\n" "PO-Revision-Date: 2014-02-10 15:00+0200\n" "Last-Translator: Venelin Stoykov \n" "Language-Team: bg \n" "Language: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #: fields.py:91 msgid "Play CAPTCHA as audio file" msgstr "Чуй текста като аудио файл" #: fields.py:106 fields.py:135 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "Сгрешен текст" #: tests/tests.py:125 msgid "This field is required." msgstr "Това поле е задължително" django-simple-captcha-0.5.1/captcha/locale/cs/0000700000076500000240000000000012630556666021347 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/cs/LC_MESSAGES/0000700000076500000240000000000012630556666023134 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/cs/LC_MESSAGES/django.mo0000644000076500000240000000117612630244743024740 0ustar marcostaff00000000000000<\pq|1#CgInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: 0.3.5 Report-Msgid-Bugs-To: POT-Creation-Date: 2012-10-09 07:05+0200 PO-Revision-Date: 2012-10-09 07:08+0200 Last-Translator: Beda Kosata Language-Team: Czech <> Language: cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; Neplatná CAPTCHAPřehrát captchu jako audio souborToto pole je povinné.django-simple-captcha-0.5.1/captcha/locale/cs/LC_MESSAGES/django.po0000600000076500000240000000166612560061626024736 0ustar marcostaff00000000000000# Czech translation of django-simple-captcha. # Copyright (C) 2012 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Beda Kosata , 2012. # msgid "" msgstr "" "Project-Id-Version: 0.3.5\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-10-09 07:05+0200\n" "PO-Revision-Date: 2012-10-09 07:08+0200\n" "Last-Translator: Beda Kosata \n" "Language-Team: Czech <>\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" #: fields.py:50 msgid "Play CAPTCHA as audio file" msgstr "Přehrát captchu jako audio soubor" #: fields.py:67 fields.py:99 tests/__init__.py:62 msgid "Invalid CAPTCHA" msgstr "Neplatná CAPTCHA" #: tests/__init__.py:88 msgid "This field is required." msgstr "Toto pole je povinné." django-simple-captcha-0.5.1/captcha/locale/de/0000700000076500000240000000000012630556666021332 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/de/LC_MESSAGES/0000700000076500000240000000000012630556666023117 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/de/LC_MESSAGES/django.mo0000644000076500000240000000124312630244743024716 0ustar marcostaff00000000000000<\pqQ!eInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha Report-Msgid-Bugs-To: POT-Creation-Date: 2013-07-16 12:06+0200 PO-Revision-Date: 2013-07-16 12:10+0100 Last-Translator: Patrick Lauber Language-Team: DE Language: de MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.5.7 Ungültiges CAPTCHACAPTCHA als Audiodatei abspielen.Dieses Feld wird benötigt.django-simple-captcha-0.5.1/captcha/locale/de/LC_MESSAGES/django.po0000600000076500000240000000175212560061626024715 0ustar marcostaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-07-16 12:06+0200\n" "PO-Revision-Date: 2013-07-16 12:10+0100\n" "Last-Translator: Patrick Lauber \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.5.7\n" #: fields.py:90 msgid "Play CAPTCHA as audio file" msgstr "CAPTCHA als Audiodatei abspielen." #: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "Ungültiges CAPTCHA" #: tests/tests.py:125 msgid "This field is required." msgstr "Dieses Feld wird benötigt." django-simple-captcha-0.5.1/captcha/locale/en/0000700000076500000240000000000012630556666021344 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/en/LC_MESSAGES/0000700000076500000240000000000012630556666023131 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/en/LC_MESSAGES/django.po0000600000076500000240000000161012560061747024724 0ustar marcostaff00000000000000# django-simple-captcha French translation. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Patrick Samson , 2010. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-07-25 11:44+0300\n" "PO-Revision-Date: 2010-09-16 12:16+0200\n" "Last-Translator: Marco Bonetti \n" "Language-Team: en \n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n>1;\n" #: fields.py:49 msgid "Play CAPTCHA as audio file" msgstr "Play CAPTCHA as audio file" #: fields.py:66 fields.py:89 tests/__init__.py:62 msgid "Invalid CAPTCHA" msgstr "" #: tests/__init__.py:88 msgid "This field is required." msgstr "" django-simple-captcha-0.5.1/captcha/locale/es/0000700000076500000240000000000012630556666021351 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/es/LC_MESSAGES/0000700000076500000240000000000012630556666023136 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/es/LC_MESSAGES/django.mo0000644000076500000240000000120112630244743024727 0ustar marcostaff00000000000000<\pq7JfInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha Report-Msgid-Bugs-To: POT-Creation-Date: 2013-07-16 12:06+0200 PO-Revision-Date: 2014-05-20 21:22+0100 Last-Translator: https://github.com/dragosdobrota Language-Team: es Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.6.5 CAPTCHA no válidoReproducir CAPTCHA de audioEste campo es obligatorio.django-simple-captcha-0.5.1/captcha/locale/es/LC_MESSAGES/django.po0000600000076500000240000000171012560061626024726 0ustar marcostaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-07-16 12:06+0200\n" "PO-Revision-Date: 2014-05-20 21:22+0100\n" "Last-Translator: https://github.com/dragosdobrota\n" "Language-Team: es\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.6.5\n" #: fields.py:90 msgid "Play CAPTCHA as audio file" msgstr "Reproducir CAPTCHA de audio" #: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "CAPTCHA no válido" #: tests/tests.py:125 msgid "This field is required." msgstr "Este campo es obligatorio." django-simple-captcha-0.5.1/captcha/locale/fr/0000700000076500000240000000000012630556666021351 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/fr/LC_MESSAGES/0000700000076500000240000000000012630556666023136 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/fr/LC_MESSAGES/django.mo0000644000076500000240000000120212630244743024730 0ustar marcostaff00000000000000<\pq6HhInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.2.0 Report-Msgid-Bugs-To: POT-Creation-Date: 2012-07-25 11:44+0300 PO-Revision-Date: 2010-09-16 12:16+0200 Last-Translator: Patrick Samson Language-Team: fr Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=n>1; Réponse invalideJouer en tant que fichier audioCe champ est obligatoire.django-simple-captcha-0.5.1/captcha/locale/fr/LC_MESSAGES/django.po0000600000076500000240000000167012560061626024733 0ustar marcostaff00000000000000# django-simple-captcha French translation. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Patrick Samson , 2010. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.2.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-07-25 11:44+0300\n" "PO-Revision-Date: 2010-09-16 12:16+0200\n" "Last-Translator: Patrick Samson \n" "Language-Team: fr \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n>1;\n" #: fields.py:49 msgid "Play CAPTCHA as audio file" msgstr "Jouer en tant que fichier audio" #: fields.py:66 fields.py:89 tests/__init__.py:62 msgid "Invalid CAPTCHA" msgstr "Réponse invalide" #: tests/__init__.py:88 msgid "This field is required." msgstr "Ce champ est obligatoire." django-simple-captcha-0.5.1/captcha/locale/it/0000700000076500000240000000000012630556666021356 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/it/LC_MESSAGES/0000700000076500000240000000000012630556666023143 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/it/LC_MESSAGES/django.mo0000644000076500000240000000124012630244743024737 0ustar marcostaff00000000000000<\pqFdInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.6 Report-Msgid-Bugs-To: POT-Creation-Date: 2012-11-14 02:53+0000 PO-Revision-Date: 2012-11-14 02:53+0000 Last-Translator: Arjuna Del Toso MIME-Version: 1.0 Language-Team: Arjuna Del Toso Language: it Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=n>1; Parola di controllo sbagliataAscolta la parola di controlloQuesto campo è obbligatoriodjango-simple-captcha-0.5.1/captcha/locale/it/LC_MESSAGES/django.po0000600000076500000240000000174212560061626024740 0ustar marcostaff00000000000000# django-simple-captcha Italian translation. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Arjuna Del Toso , 2012 # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-11-14 02:53+0000\n" "PO-Revision-Date: 2012-11-14 02:53+0000\n" "Last-Translator: Arjuna Del Toso \n" "MIME-Version: 1.0\n" "Language-Team: Arjuna Del Toso \n" "Language: it\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n>1;\n" #: .\fields.py:56 msgid "Play CAPTCHA as audio file" msgstr "Ascolta la parola di controllo" #: .\fields.py:71 .\fields.py:96 .\tests\__init__.py:70 msgid "Invalid CAPTCHA" msgstr "Parola di controllo sbagliata" #: .\tests\__init__.py:97 msgid "This field is required." msgstr "Questo campo è obbligatorio" django-simple-captcha-0.5.1/captcha/locale/ja/0000700000076500000240000000000012630556666021334 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/ja/LC_MESSAGES/0000700000076500000240000000000012630556666023121 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/ja/LC_MESSAGES/django.mo0000644000076500000240000000124512630244743024722 0ustar marcostaff00000000000000<\pq":+]Invalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha Report-Msgid-Bugs-To: POT-Creation-Date: 2014-02-13 07:11+0900 PO-Revision-Date: 2014-02-13 07:11+0900 Last-Translator: Keisuke URAGO Language-Team: Keisuke URAGO Language: ja MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; CAPTCHAの値が違っていますCAPTCHAをオーディオで読み上げるこの項目は必須ですdjango-simple-captcha-0.5.1/captcha/locale/ja/LC_MESSAGES/django.po0000600000076500000240000000175712560061626024724 0ustar marcostaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Keisuke URAGO , 2014. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-02-13 07:11+0900\n" "PO-Revision-Date: 2014-02-13 07:11+0900\n" "Last-Translator: Keisuke URAGO \n" "Language-Team: Keisuke URAGO \n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: fields.py:90 msgid "Play CAPTCHA as audio file" msgstr "CAPTCHAをオーディオで読み上げる" #: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "CAPTCHAの値が違っています" #: tests/tests.py:125 msgid "This field is required." msgstr "この項目は必須です" django-simple-captcha-0.5.1/captcha/locale/nl/0000700000076500000240000000000012630556666021353 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/nl/LC_MESSAGES/0000700000076500000240000000000012630556666023140 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/nl/LC_MESSAGES/django.mo0000644000076500000240000000123712630244743024742 0ustar marcostaff00000000000000<\pq%@!fInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.6 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-02-15 13:26+0100 PO-Revision-Date: 2013-02-15 13:26+0100 Last-Translator: Leon de Rijke MIME-Version: 1.0 Language-Team: Leon de Rijke Language: nl Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1) CAPTCHA ongeldig, probeer het opnieuwSpeel CAPTCHA als audiobestand afDit veld is verplicht.django-simple-captcha-0.5.1/captcha/locale/nl/LC_MESSAGES/django.po0000600000076500000240000000200012560061626024721 0ustar marcostaff00000000000000# django-simple-captcha Dutch translation. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Leon de Rijke , 2013. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-02-15 13:26+0100\n" "PO-Revision-Date: 2013-02-15 13:26+0100\n" "Last-Translator: Leon de Rijke \n" "MIME-Version: 1.0\n" "Language-Team: Leon de Rijke \n" "Language: nl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #: fields.py:50 msgid "Play CAPTCHA as audio file" msgstr "Speel CAPTCHA als audiobestand af" #: fields.py:67 fields.py:94 tests/__init__.py:64 tests/__init__.py:186 #: tests/__init__.py:193 msgid "Invalid CAPTCHA" msgstr "CAPTCHA ongeldig, probeer het opnieuw" #: tests/__init__.py:90 msgid "This field is required." msgstr "Dit veld is verplicht." django-simple-captcha-0.5.1/captcha/locale/pl/0000700000076500000240000000000012630556666021355 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/pl/LC_MESSAGES/0000700000076500000240000000000012630556666023142 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/pl/LC_MESSAGES/django.mo0000644000076500000240000000132012630244743024735 0ustar marcostaff00000000000000<\pqu'Invalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.6 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-08-18 18:49+0200 PO-Revision-Date: 2013-08-18 18:52+0200 Last-Translator: Sławomir Zborowski Language-Team: Polisch Language: pl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) Niepoprawnie wpisana CAPTCHAOdtwórz CAPTCHĘ jako plik dźwiękowyTo pole jest wymaganedjango-simple-captcha-0.5.1/captcha/locale/pl/LC_MESSAGES/django.po0000600000076500000240000000212212560061626024730 0ustar marcostaff00000000000000# Polish translation for django-simple-captcha. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the django-simple-captcha package. # Sławomir Zborowski , 2013. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-08-18 18:49+0200\n" "PO-Revision-Date: 2013-08-18 18:52+0200\n" "Last-Translator: Sławomir Zborowski \n" "Language-Team: Polisch\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2)\n" #: fields.py:90 msgid "Play CAPTCHA as audio file" msgstr "Odtwórz CAPTCHĘ jako plik dźwiękowy" #: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "Niepoprawnie wpisana CAPTCHA" #: tests/tests.py:125 msgid "This field is required." msgstr "To pole jest wymagane" django-simple-captcha-0.5.1/captcha/locale/pt_BR/0000700000076500000240000000000012630556666021750 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/pt_BR/LC_MESSAGES/0000700000076500000240000000000012630556666023535 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/pt_BR/LC_MESSAGES/django.mo0000644000076500000240000000121612630244743025334 0ustar marcostaff00000000000000<\pqEXrInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.7 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-05-18 10:58-0300 PO-Revision-Date: 2013-05-18 13:12-0300 Last-Translator: Alisson Patricio Language-Team: Alisson Patricio Language: pt_br MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Resposta inválidaOuça o arquivo de áudioEste campo é obrigatório.django-simple-captcha-0.5.1/captcha/locale/pt_BR/LC_MESSAGES/django.po0000600000076500000240000000200112560061626025317 0ustar marcostaff00000000000000# django-simple-captcha Portuguese (Brazilian) translation. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Alisson Patricio , 2013. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.7\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-05-18 10:58-0300\n" "PO-Revision-Date: 2013-05-18 13:12-0300\n" "Last-Translator: Alisson Patricio \n" "Language-Team: Alisson Patricio \n" "Language: pt_br\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: fields.py:49 msgid "Play CAPTCHA as audio file" msgstr "Ouça o arquivo de áudio" #: fields.py:66 fields.py:93 tests/__init__.py:69 #: tests/__init__.py:198 tests/__init__.py:205 msgid "Invalid CAPTCHA" msgstr "Resposta inválida" #: tests/__init__.py:95 msgid "This field is required." msgstr "Este campo é obrigatório." django-simple-captcha-0.5.1/captcha/locale/ru/0000700000076500000240000000000012630556666021370 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/ru/LC_MESSAGES/0000700000076500000240000000000012630556666023155 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/ru/LC_MESSAGES/django.mo0000644000076500000240000000137512630244743024762 0ustar marcostaff00000000000000<\pqXDtCInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.7 Report-Msgid-Bugs-To: POT-Creation-Date: 2012-07-25 11:17+0300 PO-Revision-Date: 2012-07-25 11:17+0300 Last-Translator: Language-Team: ru Language: ru MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) Неверный ответВоспроизвести CAPTCHA в виде аудио файлаЭто поле обязательно для заполнения.django-simple-captcha-0.5.1/captcha/locale/ru/LC_MESSAGES/django.po0000600000076500000240000000203512560061626024746 0ustar marcostaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.7\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-07-25 11:17+0300\n" "PO-Revision-Date: 2012-07-25 11:17+0300\n" "Last-Translator: \n" "Language-Team: ru \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" #: fields.py:49 msgid "Play CAPTCHA as audio file" msgstr "Воспроизвести CAPTCHA в виде аудио файла" #: fields.py:66 fields.py:89 tests/__init__.py:62 msgid "Invalid CAPTCHA" msgstr "Неверный ответ" #: tests/__init__.py:88 msgid "This field is required." msgstr "Это поле обязательно для заполнения." django-simple-captcha-0.5.1/captcha/locale/sk/0000700000076500000240000000000012630556666021357 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/sk/LC_MESSAGES/0000700000076500000240000000000012630556666023144 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/sk/LC_MESSAGES/django.mo0000644000076500000240000000120712630244743024743 0ustar marcostaff00000000000000<\pq<!NpInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.7 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-10-15 17:16+0200 PO-Revision-Date: 2013-10-15 17:16+0200 Last-Translator: Pavol Otto Language-Team: SK Language: sk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; Neplatná CAPTCHAPrehrať captchu ako audio súborToto pole je povinné.django-simple-captcha-0.5.1/captcha/locale/sk/LC_MESSAGES/django.po0000600000076500000240000000174212560061626024741 0ustar marcostaff00000000000000# Slovak translation of django-simple-captcha. # Copyright (C) 2013 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Pavol Otto , 2013. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.7\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-10-15 17:16+0200\n" "PO-Revision-Date: 2013-10-15 17:16+0200\n" "Last-Translator: Pavol Otto \n" "Language-Team: SK\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" #: fields.py:90 msgid "Play CAPTCHA as audio file" msgstr "Prehrať captchu ako audio súbor" #: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "Neplatná CAPTCHA" #: tests/tests.py:125 msgid "This field is required." msgstr "Toto pole je povinné." django-simple-captcha-0.5.1/captcha/locale/tr/0000700000076500000240000000000012630556666021367 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/tr/LC_MESSAGES/0000700000076500000240000000000012630556666023154 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/tr/LC_MESSAGES/django.mo0000644000076500000240000000120612630244743024752 0ustar marcostaff00000000000000<\pq@ QrInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.6 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-01-19 16:33-0200 PO-Revision-Date: 2013-01-19 20:52+0200 Last-Translator: Gokmen Gorgen Language-Team: TR Gokmen Gorgen Language: tr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0 Geçersiz değerDeğeri ses dosyası olarak çalBu alan zorunludur.django-simple-captcha-0.5.1/captcha/locale/tr/LC_MESSAGES/django.po0000600000076500000240000000172212560061626024747 0ustar marcostaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-01-19 16:33-0200\n" "PO-Revision-Date: 2013-01-19 20:52+0200\n" "Last-Translator: Gokmen Gorgen \n" "Language-Team: TR Gokmen Gorgen \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0\n" #: fields.py:50 msgid "Play CAPTCHA as audio file" msgstr "Değeri ses dosyası olarak çal" #: fields.py:67 fields.py:94 tests/__init__.py:64 tests/__init__.py:186 #: tests/__init__.py:193 msgid "Invalid CAPTCHA" msgstr "Geçersiz değer" #: tests/__init__.py:90 msgid "This field is required." msgstr "Bu alan zorunludur." django-simple-captcha-0.5.1/captcha/locale/uk/0000700000076500000240000000000012630556666021361 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/uk/LC_MESSAGES/0000700000076500000240000000000012630556666023146 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/uk/LC_MESSAGES/django.mo0000644000076500000240000000136412630244743024751 0ustar marcostaff00000000000000<\pq*h6)Invalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.6 Report-Msgid-Bugs-To: POT-Creation-Date: 2014-02-21 22:05+0200 PO-Revision-Date: 2014-02-21 22:05+0200 Last-Translator: @FuriousCoder Language-Team: uk @FuriousCoder Language: uk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); Неправильна відповідь.Відтворити CAPTCHA як аудіо файл.Це поле є обов'язковим.django-simple-captcha-0.5.1/captcha/locale/uk/LC_MESSAGES/django.po0000600000076500000240000000207212560061626024740 0ustar marcostaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-02-21 22:05+0200\n" "PO-Revision-Date: 2014-02-21 22:05+0200\n" "Last-Translator: @FuriousCoder\n" "Language-Team: uk @FuriousCoder\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: fields.py:91 msgid "Play CAPTCHA as audio file" msgstr "Відтворити CAPTCHA як аудіо файл." #: fields.py:106 fields.py:135 tests/tests.py:99 tests/tests.py:239 #: tests/tests.py:246 msgid "Invalid CAPTCHA" msgstr "Неправильна відповідь." #: tests/tests.py:125 msgid "This field is required." msgstr "Це поле є обов'язковим." django-simple-captcha-0.5.1/captcha/locale/zh_CN/0000700000076500000240000000000012630556666021743 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/zh_CN/LC_MESSAGES/0000700000076500000240000000000012630556666023530 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/locale/zh_CN/LC_MESSAGES/django.mo0000644000076500000240000000120312630244743025323 0ustar marcostaff00000000000000<\pq8!HjInvalid CAPTCHAPlay CAPTCHA as audio fileThis field is required.Project-Id-Version: django-simple-captcha 0.3.6 Report-Msgid-Bugs-To: POT-Creation-Date: 2013-03-01 05:04+0800 PO-Revision-Date: 2013-03-01 05:04+0800 Last-Translator: Ming Chen Language-Team: zh_cn Ming Chen Language: zh_cn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0 认证码错误使用语音方式播放认证码这个字段是必须的django-simple-captcha-0.5.1/captcha/locale/zh_CN/LC_MESSAGES/django.po0000600000076500000240000000175512560061626025331 0ustar marcostaff00000000000000# django-simple-captcha Chinese Simplified translation. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Ming Chen , 2013. # msgid "" msgstr "" "Project-Id-Version: django-simple-captcha 0.3.6\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-03-01 05:04+0800\n" "PO-Revision-Date: 2013-03-01 05:04+0800\n" "Last-Translator: Ming Chen \n" "Language-Team: zh_cn Ming Chen \n" "Language: zh_cn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0\n" #: fields.py:49 msgid "Play CAPTCHA as audio file" msgstr "使用语音方式播放认证码" #: fields.py:66 fields.py:93 tests/__init__.py:69 tests/__init__.py:198 #: tests/__init__.py:205 msgid "Invalid CAPTCHA" msgstr "认证码错误" #: tests/__init__.py:95 msgid "This field is required." msgstr "这个字段是必须的" django-simple-captcha-0.5.1/captcha/management/0000700000076500000240000000000012630556666021617 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/management/__init__.py0000644000076500000240000000000012140410773023710 0ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/management/commands/0000700000076500000240000000000012630556666023420 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/management/commands/__init__.py0000644000076500000240000000000012140410773025511 0ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/management/commands/captcha_clean.py0000600000076500000240000000154212552733655026541 0ustar marcostaff00000000000000from django.core.management.base import BaseCommand from captcha.models import get_safe_now import sys class Command(BaseCommand): help = "Clean up expired captcha hashkeys." def handle(self, **options): from captcha.models import CaptchaStore verbose = int(options.get('verbosity')) expired_keys = CaptchaStore.objects.filter(expiration__lte=get_safe_now()).count() if verbose >= 1: print("Currently %d expired hashkeys" % expired_keys) try: CaptchaStore.remove_expired() except: if verbose >= 1: print("Unable to delete expired hashkeys.") sys.exit(1) if verbose >= 1: if expired_keys > 0: print("%d expired hashkeys removed." % expired_keys) else: print("No keys to remove.") django-simple-captcha-0.5.1/captcha/migrations/0000700000076500000240000000000012630556666021657 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/migrations/0001_initial.py0000644000076500000240000000136612367461046024334 0ustar marcostaff00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='CaptchaStore', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('challenge', models.CharField(max_length=32)), ('response', models.CharField(max_length=32)), ('hashkey', models.CharField(unique=True, max_length=40)), ('expiration', models.DateTimeField()), ], options={ }, bases=(models.Model,), ), ] django-simple-captcha-0.5.1/captcha/migrations/__init__.py0000644000076500000240000000114212367461124023767 0ustar marcostaff00000000000000""" Django migrations for django-simple-captcha app This package does not contain South migrations. South migrations can be found in the ``south_migrations`` package. """ SOUTH_ERROR_MESSAGE = """\n For South support, customize the SOUTH_MIGRATION_MODULES setting like so: SOUTH_MIGRATION_MODULES = { 'captcha': 'captcha.south_migrations', } """ # Ensure the user is not using Django 1.6 or below with South try: from django.db import migrations # noqa except ImportError: from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured(SOUTH_ERROR_MESSAGE)django-simple-captcha-0.5.1/captcha/models.py0000600000076500000240000000410412552733655021337 0ustar marcostaff00000000000000from captcha.conf import settings as captcha_settings from django.db import models from django.conf import settings from django.utils.encoding import smart_text import datetime import random import time import hashlib # Heavily based on session key generation in Django # Use the system (hardware-based) random number generator if it exists. if hasattr(random, 'SystemRandom'): randrange = random.SystemRandom().randrange else: randrange = random.randrange MAX_RANDOM_KEY = 18446744073709551616 # 2 << 63 def get_safe_now(): try: from django.utils.timezone import utc if settings.USE_TZ: return datetime.datetime.utcnow().replace(tzinfo=utc) except: pass return datetime.datetime.now() class CaptchaStore(models.Model): challenge = models.CharField(blank=False, max_length=32) response = models.CharField(blank=False, max_length=32) hashkey = models.CharField(blank=False, max_length=40, unique=True) expiration = models.DateTimeField(blank=False) def save(self, *args, **kwargs): self.response = self.response.lower() if not self.expiration: self.expiration = get_safe_now() + datetime.timedelta(minutes=int(captcha_settings.CAPTCHA_TIMEOUT)) if not self.hashkey: key_ = ( smart_text(randrange(0, MAX_RANDOM_KEY)) + smart_text(time.time()) + smart_text(self.challenge, errors='ignore') + smart_text(self.response, errors='ignore') ).encode('utf8') self.hashkey = hashlib.sha1(key_).hexdigest() del(key_) super(CaptchaStore, self).save(*args, **kwargs) def __unicode__(self): return self.challenge def remove_expired(cls): cls.objects.filter(expiration__lte=get_safe_now()).delete() remove_expired = classmethod(remove_expired) @classmethod def generate_key(cls): challenge, response = captcha_settings.get_challenge()() store = cls.objects.create(challenge=challenge, response=response) return store.hashkey django-simple-captcha-0.5.1/captcha/south_migrations/0000700000076500000240000000000012630556666023101 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/south_migrations/0001_initial.py0000644000076500000240000000276312140410773025546 0ustar marcostaff00000000000000# encoding: utf-8 import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding model 'CaptchaStore' db.create_table('captcha_captchastore', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('challenge', self.gf('django.db.models.fields.CharField')(max_length=32)), ('response', self.gf('django.db.models.fields.CharField')(max_length=32)), ('hashkey', self.gf('django.db.models.fields.CharField')(unique=True, max_length=40)), ('expiration', self.gf('django.db.models.fields.DateTimeField')()), )) db.send_create_signal('captcha', ['CaptchaStore']) def backwards(self, orm): # Deleting model 'CaptchaStore' db.delete_table('captcha_captchastore') models = { 'captcha.captchastore': { 'Meta': {'object_name': 'CaptchaStore'}, 'challenge': ('django.db.models.fields.CharField', [], {'max_length': '32'}), 'expiration': ('django.db.models.fields.DateTimeField', [], {}), 'hashkey': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'response': ('django.db.models.fields.CharField', [], {'max_length': '32'}) } } complete_apps = ['captcha'] django-simple-captcha-0.5.1/captcha/south_migrations/__init__.py0000644000076500000240000000000012140410773025172 0ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/templates/0000700000076500000240000000000012630556666021501 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/templates/captcha/0000700000076500000240000000000012630556666023104 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/templates/captcha/field.html0000600000076500000240000000005012627522275025045 0ustar marcostaff00000000000000{{image}}{{hidden_field}}{{text_field}} django-simple-captcha-0.5.1/captcha/templates/captcha/hidden_field.html0000600000076500000240000000011012627522275026355 0ustar marcostaff00000000000000 django-simple-captcha-0.5.1/captcha/templates/captcha/image.html0000600000076500000240000000034712627522275025055 0ustar marcostaff00000000000000{% load i18n %} {% spaceless %} {% if audio %}{% endif %}captcha{% if audio %}{% endif %} {% endspaceless %}django-simple-captcha-0.5.1/captcha/templates/captcha/text_field.html0000600000076500000240000000011012627522275026106 0ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/tests/0000700000076500000240000000000012630556666020645 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/captcha/tests/__init__.py0000600000076500000240000000007212552733655022755 0ustar marcostaff00000000000000from .tests import CaptchaCase, trivial_challenge # NOQA django-simple-captcha-0.5.1/captcha/tests/tests.py0000600000076500000240000004357312627522275022372 0ustar marcostaff00000000000000# -*- coding: utf-8 -*- from captcha.conf import settings from captcha.fields import CaptchaField, CaptchaTextInput from captcha.models import CaptchaStore, get_safe_now from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse from django.test import TestCase, override_settings from django.utils.translation import ugettext_lazy import datetime import json import re import six import os try: from cStringIO import StringIO except ImportError: from io import BytesIO as StringIO from six import u try: from PIL import Image except ImportError: import Image # NOQA @override_settings(ROOT_URLCONF='captcha.tests.urls') class CaptchaCase(TestCase): def setUp(self): self.stores = {} self.__current_settings_output_format = settings.CAPTCHA_OUTPUT_FORMAT self.__current_settings_dictionary = settings.CAPTCHA_WORDS_DICTIONARY self.__current_settings_punctuation = settings.CAPTCHA_PUNCTUATION tested_helpers = ['captcha.helpers.math_challenge', 'captcha.helpers.random_char_challenge', 'captcha.helpers.unicode_challenge'] if os.path.exists('/usr/share/dict/words'): settings.CAPTCHA_WORDS_DICTIONARY = '/usr/share/dict/words' settings.CAPTCHA_PUNCTUATION = ';-,.' tested_helpers.append('captcha.helpers.word_challenge') tested_helpers.append('captcha.helpers.huge_words_and_punctuation_challenge') for helper in tested_helpers: challenge, response = settings._callable_from_string(helper)() self.stores[helper.rsplit('.', 1)[-1].replace('_challenge', '_store')], _ = CaptchaStore.objects.get_or_create(challenge=challenge, response=response) challenge, response = settings.get_challenge()() self.stores['default_store'], _ = CaptchaStore.objects.get_or_create(challenge=challenge, response=response) self.default_store = self.stores['default_store'] def tearDown(self): settings.CAPTCHA_OUTPUT_FORMAT = self.__current_settings_output_format settings.CAPTCHA_WORDS_DICTIONARY = self.__current_settings_dictionary settings.CAPTCHA_PUNCTUATION = self.__current_settings_punctuation def __extract_hash_and_response(self, r): hash_ = re.findall(r'value="([0-9a-f]+)"', str(r.content))[0] response = CaptchaStore.objects.get(hashkey=hash_).response return hash_, response def test_image(self): for key in [store.hashkey for store in six.itervalues(self.stores)]: response = self.client.get(reverse('captcha-image', kwargs=dict(key=key))) self.assertEqual(response.status_code, 200) self.assertTrue(response.has_header('content-type')) self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'image/png')) def test_audio(self): if not settings.CAPTCHA_FLITE_PATH: return for key in (self.stores.get('math_store').hashkey, self.stores.get('math_store').hashkey, self.default_store.hashkey): response = self.client.get(reverse('captcha-audio', kwargs=dict(key=key))) self.assertEqual(response.status_code, 200) self.assertTrue(len(response.content) > 1024) self.assertTrue(response.has_header('content-type')) self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'audio/x-wav')) def test_form_submit(self): r = self.client.get(reverse('captcha-test')) self.assertEqual(r.status_code, 200) hash_, response = self.__extract_hash_and_response(r) r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r.status_code, 200) self.assertTrue(str(r.content).find('Form validated') > 0) r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r.status_code, 200) self.assertFalse(str(r.content).find('Form validated') > 0) def test_modelform(self): r = self.client.get(reverse('captcha-test-model-form')) self.assertEqual(r.status_code, 200) hash_, response = self.__extract_hash_and_response(r) r = self.client.post(reverse('captcha-test-model-form'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r.status_code, 200) self.assertTrue(str(r.content).find('Form validated') > 0) r = self.client.post(reverse('captcha-test-model-form'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r.status_code, 200) self.assertFalse(str(r.content).find('Form validated') > 0) def test_wrong_submit(self): for urlname in ('captcha-test', 'captcha-test-model-form'): r = self.client.get(reverse(urlname)) self.assertEqual(r.status_code, 200) r = self.client.post(reverse(urlname), dict(captcha_0='abc', captcha_1='wrong response', subject='xxx', sender='asasd@asdasd.com')) self.assertFormError(r, 'form', 'captcha', ugettext_lazy('Invalid CAPTCHA')) def test_deleted_expired(self): self.default_store.expiration = get_safe_now() - datetime.timedelta(minutes=5) self.default_store.save() hash_ = self.default_store.hashkey r = self.client.post(reverse('captcha-test'), dict(captcha_0=hash_, captcha_1=self.default_store.response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r.status_code, 200) self.assertFalse('Form validated' in str(r.content)) # expired -> deleted try: CaptchaStore.objects.get(hashkey=hash_) self.fail() except: pass def test_custom_error_message(self): r = self.client.get(reverse('captcha-test-custom-error-message')) self.assertEqual(r.status_code, 200) # Wrong answer r = self.client.post(reverse('captcha-test-custom-error-message'), dict(captcha_0='abc', captcha_1='wrong response')) self.assertFormError(r, 'form', 'captcha', 'TEST CUSTOM ERROR MESSAGE') # empty answer r = self.client.post(reverse('captcha-test-custom-error-message'), dict(captcha_0='abc', captcha_1='')) self.assertFormError(r, 'form', 'captcha', ugettext_lazy('This field is required.')) def test_repeated_challenge(self): CaptchaStore.objects.create(challenge='xxx', response='xxx') try: CaptchaStore.objects.create(challenge='xxx', response='xxx') except Exception: self.fail() def test_repeated_challenge_form_submit(self): __current_challange_function = settings.CAPTCHA_CHALLENGE_FUNCT for urlname in ('captcha-test', 'captcha-test-model-form'): settings.CAPTCHA_CHALLENGE_FUNCT = 'captcha.tests.trivial_challenge' r1 = self.client.get(reverse(urlname)) r2 = self.client.get(reverse(urlname)) self.assertEqual(r1.status_code, 200) self.assertEqual(r2.status_code, 200) if re.findall(r'value="([0-9a-f]+)"', str(r1.content)): hash_1 = re.findall(r'value="([0-9a-f]+)"', str(r1.content))[0] else: self.fail() if re.findall(r'value="([0-9a-f]+)"', str(r2.content)): hash_2 = re.findall(r'value="([0-9a-f]+)"', str(r2.content))[0] else: self.fail() try: store_1 = CaptchaStore.objects.get(hashkey=hash_1) store_2 = CaptchaStore.objects.get(hashkey=hash_2) except: self.fail() self.assertTrue(store_1.pk != store_2.pk) self.assertTrue(store_1.response == store_2.response) self.assertTrue(hash_1 != hash_2) r1 = self.client.post(reverse(urlname), dict(captcha_0=hash_1, captcha_1=store_1.response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r1.status_code, 200) self.assertTrue(str(r1.content).find('Form validated') > 0) try: store_2 = CaptchaStore.objects.get(hashkey=hash_2) except: self.fail() r2 = self.client.post(reverse(urlname), dict(captcha_0=hash_2, captcha_1=store_2.response, subject='xxx', sender='asasd@asdasd.com')) self.assertEqual(r2.status_code, 200) self.assertTrue(str(r2.content).find('Form validated') > 0) settings.CAPTCHA_CHALLENGE_FUNCT = __current_challange_function def test_output_format(self): for urlname in ('captcha-test', 'captcha-test-model-form'): settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s

Hello, captcha world

%(hidden_field)s%(text_field)s') r = self.client.get(reverse(urlname)) self.assertEqual(r.status_code, 200) self.assertTrue('

Hello, captcha world

' in str(r.content)) def test_invalid_output_format(self): for urlname in ('captcha-test', 'captcha-test-model-form'): settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s') try: self.client.get(reverse(urlname)) self.fail() except ImproperlyConfigured as e: self.assertTrue('CAPTCHA_OUTPUT_FORMAT' in str(e)) def test_per_form_format(self): settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s testCustomFormatString %(hidden_field)s %(text_field)s') r = self.client.get(reverse('captcha-test')) self.assertTrue('testCustomFormatString' in str(r.content)) r = self.client.get(reverse('test_per_form_format')) self.assertTrue('testPerFieldCustomFormatString' in str(r.content)) def test_issue31_proper_abel(self): settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s %(hidden_field)s %(text_field)s') r = self.client.get(reverse('captcha-test')) self.assertTrue('' in six.text_type(r.content)) self.assertTrue('id="form1_id_captcha1_1"' in six.text_type(r.content)) self.assertTrue('' in six.text_type(r.content)) self.assertTrue('id="form2_id_captcha2_1"' in six.text_type(r.content)) def test_image_size(self): __current_test_mode_setting = settings.CAPTCHA_IMAGE_SIZE for key in [store.hashkey for store in six.itervalues(self.stores)]: settings.CAPTCHA_IMAGE_SIZE = (201, 97) response = self.client.get(reverse('captcha-image', kwargs=dict(key=key))) self.assertEqual(response.status_code, 200) self.assertTrue(response.has_header('content-type')) self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'image/png')) self.assertEqual(Image.open(StringIO(response.content)).size, (201, 97)) settings.CAPTCHA_IMAGE_SIZE = __current_test_mode_setting def test_multiple_fonts(self): vera = os.path.join(os.path.dirname(__file__), '..', 'fonts', 'Vera.ttf') __current_test_mode_setting = settings.CAPTCHA_FONT_PATH settings.CAPTCHA_FONT_PATH = vera for key in [store.hashkey for store in six.itervalues(self.stores)]: response = self.client.get(reverse('captcha-image', kwargs=dict(key=key))) self.assertEqual(response.status_code, 200) self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'image/png')) settings.CAPTCHA_FONT_PATH = [vera, vera, vera] for key in [store.hashkey for store in six.itervalues(self.stores)]: response = self.client.get(reverse('captcha-image', kwargs=dict(key=key))) self.assertEqual(response.status_code, 200) self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'image/png')) settings.CAPTCHA_FONT_PATH = False for key in [store.hashkey for store in six.itervalues(self.stores)]: try: response = self.client.get(reverse('captcha-image', kwargs=dict(key=key))) self.fail() except ImproperlyConfigured: pass settings.CAPTCHA_FONT_PATH = __current_test_mode_setting def test_template_overrides(self): __current_test_mode_setting = settings.CAPTCHA_IMAGE_TEMPLATE settings.CAPTCHA_IMAGE_TEMPLATE = 'captcha_test/image.html' for urlname in ('captcha-test', 'captcha-test-model-form'): settings.CAPTCHA_CHALLENGE_FUNCT = 'captcha.tests.trivial_challenge' r = self.client.get(reverse(urlname)) self.assertTrue('captcha-template-test' in six.text_type(r.content)) settings.CAPTCHA_IMAGE_TEMPLATE = __current_test_mode_setting def trivial_challenge(): return 'trivial', 'trivial' django-simple-captcha-0.5.1/captcha/tests/urls.py0000600000076500000240000000125412627522275022203 0ustar marcostaff00000000000000from django.conf.urls import url, include from .views import ( test, test_model_form, test_custom_error_message, test_per_form_format, test_non_required, test_id_prefix ) urlpatterns = [ url(r'test/$', test, name='captcha-test'), url(r'test-modelform/$', test_model_form, name='captcha-test-model-form'), url(r'test2/$', test_custom_error_message, name='captcha-test-custom-error-message'), url(r'test3/$', test_per_form_format, name='test_per_form_format'), url(r'test-non-required/$', test_non_required, name='captcha-test-non-required'), url(r'test-id-prefix/$', test_id_prefix, name='captcha-test-id-prefix'), url(r'', include('captcha.urls')), ] django-simple-captcha-0.5.1/captcha/tests/views.py0000600000076500000240000000707212627522275022357 0ustar marcostaff00000000000000from django import forms from captcha.fields import CaptchaField from django.http import HttpResponse from django.contrib.auth.models import User from six import u import django try: from django.template import engines, RequestContext __is_18 = True except ImportError: from django.template import RequestContext, loader __is_18 = False TEST_TEMPLATE = r''' captcha test {% if passed %}

Form validated

{% endif %} {% if form.errors %} {{form.errors}} {% endif %}
{{form.as_p}}

''' def _get_template(template_string): if __is_18: return engines['django'].from_string(template_string) else: return loader.get_template_from_string(template_string) def _test(request, form_class): passed = False if request.POST: form = form_class(request.POST) if form.is_valid(): passed = True else: form = form_class() t = _get_template(TEST_TEMPLATE) if django.VERSION >= (1, 9): return HttpResponse( t.render(context=dict(passed=passed, form=form), request=request)) else: return HttpResponse( t.render(RequestContext(request, dict(passed=passed, form=form)))) def test(request): class CaptchaTestForm(forms.Form): subject = forms.CharField(max_length=100) sender = forms.EmailField() captcha = CaptchaField(help_text='asdasd') return _test(request, CaptchaTestForm) def test_model_form(request): class CaptchaTestModelForm(forms.ModelForm): subject = forms.CharField(max_length=100) sender = forms.EmailField() captcha = CaptchaField(help_text='asdasd') class Meta: model = User fields = ('subject', 'sender', 'captcha', ) return _test(request, CaptchaTestModelForm) def test_custom_error_message(request): class CaptchaTestErrorMessageForm(forms.Form): captcha = CaptchaField( help_text='asdasd', error_messages=dict(invalid='TEST CUSTOM ERROR MESSAGE') ) return _test(request, CaptchaTestErrorMessageForm) def test_per_form_format(request): class CaptchaTestFormatForm(forms.Form): captcha = CaptchaField( help_text='asdasd', error_messages=dict(invalid='TEST CUSTOM ERROR MESSAGE'), output_format=( u( '%(image)s testPerFieldCustomFormatString ' '%(hidden_field)s %(text_field)s' ) ) ) return _test(request, CaptchaTestFormatForm) def test_non_required(request): class CaptchaTestForm(forms.Form): sender = forms.EmailField() subject = forms.CharField(max_length=100) captcha = CaptchaField(help_text='asdasd', required=False) return _test(request, CaptchaTestForm) def test_id_prefix(request): class CaptchaTestForm(forms.Form): sender = forms.EmailField() subject = forms.CharField(max_length=100) captcha1 = CaptchaField(id_prefix="form1") captcha2 = CaptchaField(id_prefix="form2") return _test(request, CaptchaTestForm) django-simple-captcha-0.5.1/captcha/urls.py0000600000076500000240000000065212627522275021042 0ustar marcostaff00000000000000from django.conf.urls import url from captcha import views urlpatterns = [ url(r'image/(?P\w+)/$', views.captcha_image, name='captcha-image', kwargs={'scale': 1}), url(r'image/(?P\w+)@2/$', views.captcha_image, name='captcha-image-2x', kwargs={'scale': 2}), url(r'audio/(?P\w+)/$', views.captcha_audio, name='captcha-audio'), url(r'refresh/$', views.captcha_refresh, name='captcha-refresh'), ] django-simple-captcha-0.5.1/captcha/views.py0000600000076500000240000001272512552733655021221 0ustar marcostaff00000000000000from captcha.conf import settings from captcha.helpers import captcha_image_url from captcha.models import CaptchaStore from django.http import HttpResponse, Http404 from django.core.exceptions import ImproperlyConfigured import random import re import tempfile import os import subprocess import six try: from cStringIO import StringIO except ImportError: from io import BytesIO as StringIO try: from PIL import Image, ImageDraw, ImageFont except ImportError: import Image import ImageDraw import ImageFont try: import json except ImportError: from django.utils import simplejson as json NON_DIGITS_RX = re.compile('[^\d]') # Distance of the drawn text from the top of the captcha image from_top = 4 def getsize(font, text): if hasattr(font, 'getoffset'): return [x + y for x, y in zip(font.getsize(text), font.getoffset(text))] else: return font.getsize(text) def makeimg(size): if settings.CAPTCHA_BACKGROUND_COLOR == "transparent": image = Image.new('RGBA', size) else: image = Image.new('RGB', size, settings.CAPTCHA_BACKGROUND_COLOR) return image def captcha_image(request, key, scale=1): try: store = CaptchaStore.objects.get(hashkey=key) except CaptchaStore.DoesNotExist: # HTTP 410 Gone status so that crawlers don't index these expired urls. return HttpResponse(status=410) text = store.challenge if isinstance(settings.CAPTCHA_FONT_PATH, six.string_types): fontpath = settings.CAPTCHA_FONT_PATH elif isinstance(settings.CAPTCHA_FONT_PATH, (list, tuple)): fontpath = random.choice(settings.CAPTCHA_FONT_PATH) else: raise ImproperlyConfigured('settings.CAPTCHA_FONT_PATH needs to be a path to a font or list of paths to fonts') if fontpath.lower().strip().endswith('ttf'): font = ImageFont.truetype(fontpath, settings.CAPTCHA_FONT_SIZE * scale) else: font = ImageFont.load(fontpath) if settings.CAPTCHA_IMAGE_SIZE: size = settings.CAPTCHA_IMAGE_SIZE else: size = getsize(font, text) size = (size[0] * 2, int(size[1] * 1.4)) image = makeimg(size) try: PIL_VERSION = int(NON_DIGITS_RX.sub('', Image.VERSION)) except: PIL_VERSION = 116 xpos = 2 charlist = [] for char in text: if char in settings.CAPTCHA_PUNCTUATION and len(charlist) >= 1: charlist[-1] += char else: charlist.append(char) for char in charlist: fgimage = Image.new('RGB', size, settings.CAPTCHA_FOREGROUND_COLOR) charimage = Image.new('L', getsize(font, ' %s ' % char), '#000000') chardraw = ImageDraw.Draw(charimage) chardraw.text((0, 0), ' %s ' % char, font=font, fill='#ffffff') if settings.CAPTCHA_LETTER_ROTATION: if PIL_VERSION >= 116: charimage = charimage.rotate(random.randrange(*settings.CAPTCHA_LETTER_ROTATION), expand=0, resample=Image.BICUBIC) else: charimage = charimage.rotate(random.randrange(*settings.CAPTCHA_LETTER_ROTATION), resample=Image.BICUBIC) charimage = charimage.crop(charimage.getbbox()) maskimage = Image.new('L', size) maskimage.paste(charimage, (xpos, from_top, xpos + charimage.size[0], from_top + charimage.size[1])) size = maskimage.size image = Image.composite(fgimage, image, maskimage) xpos = xpos + 2 + charimage.size[0] if settings.CAPTCHA_IMAGE_SIZE: # centering captcha on the image tmpimg = makeimg(size) tmpimg.paste(image, (int((size[0] - xpos) / 2), int((size[1] - charimage.size[1]) / 2 - from_top))) image = tmpimg.crop((0, 0, size[0], size[1])) else: image = image.crop((0, 0, xpos + 1, size[1])) draw = ImageDraw.Draw(image) for f in settings.noise_functions(): draw = f(draw, image) for f in settings.filter_functions(): image = f(image) out = StringIO() image.save(out, "PNG") out.seek(0) response = HttpResponse(content_type='image/png') response.write(out.read()) response['Content-length'] = out.tell() return response def captcha_audio(request, key): if settings.CAPTCHA_FLITE_PATH: try: store = CaptchaStore.objects.get(hashkey=key) except CaptchaStore.DoesNotExist: # HTTP 410 Gone status so that crawlers don't index these expired urls. return HttpResponse(status=410) text = store.challenge if 'captcha.helpers.math_challenge' == settings.CAPTCHA_CHALLENGE_FUNCT: text = text.replace('*', 'times').replace('-', 'minus') else: text = ', '.join(list(text)) path = str(os.path.join(tempfile.gettempdir(), '%s.wav' % key)) subprocess.call([settings.CAPTCHA_FLITE_PATH, "-t", text, "-o", path]) if os.path.isfile(path): response = HttpResponse() f = open(path, 'rb') response['Content-Type'] = 'audio/x-wav' response.write(f.read()) f.close() os.unlink(path) return response raise Http404 def captcha_refresh(request): """ Return json with new captcha for ajax refresh request """ if not request.is_ajax(): raise Http404 new_key = CaptchaStore.generate_key() to_json_response = { 'key': new_key, 'image_url': captcha_image_url(new_key), } return HttpResponse(json.dumps(to_json_response), content_type='application/json') django-simple-captcha-0.5.1/CHANGES0000600000076500000240000001200712630556475017073 0ustar marcostaff00000000000000Version 0.5.1 ------------- * Fine tuning MANIFEST.in * Prevent testproject from installing into site-packages Version 0.5.0 ------------- * Adds missing includes in MANIFEST.in Version 0.4.7 ------------- * Supported Django versions are now 1.7, 1.8 and 1.9 * Trying to fix the TravisCI build errors * Use Django templates to render the individual fields, as well as the assembled Captcha Field (Issue #31) Version 0.4.6 ------------- * Fixes an UnicodeDecodeError which was apparently only triggered during testing on TravisCI (I hope) * Support for Django 2.0 urlpatterns syntax (PR #82, Thanks @R3v1L) * settings.CAPTCHA_FONT_PATH may be a list, in which case a font is picked randomly (Issue #51 fixed in PR #88, Thanks @inflrscns) Version 0.4.5 ------------- * Test with tox * Test against Django 1.8 final * Added ability to force a fixed image size (PR #76, Thanks @superqwer) Version 0.4.4 ------------- * Added id_prefix argument (fixes issue #37) Version 0.4.3 ------------- * Add null noise helper (Thanks @xrmx) * Test against Django 1.7b4 * Added Spanish translations (Thanks @dragosdobrota) * Massive cleanup (pep8, translations) * Support for transparent background color. (Thanks @curaloucura) * Support both Django 1.7 migrations and South migrations. Please note, you *must* add the following to your settings, if you are using South migrations and Django 1.6 or lower. * Make sure autocomplete="off" is only applied to the text input, not the hidden input (Issue #68, thanks @narrowfail) * Fixed some grammar in the documentation. (Thanks @rikrian) * Return an HTTP 410 GONE error code for expired captcha images, to avoid crawlers from trying to reindex them (PR #70, thanks @joshuajonah) * Fixed title markup in documentation (#74, thanks @pavlov99) * Test against Django 1.7.1 Version 0.4.2 ------------- * Added autocomplete="off" to the input (Issue #57, thanks @Vincent-Vega) * Fixed the format (msgfmt -c) of most PO and MO files distributed with the project * Added Bulgarian translations. (Thanks @vstoykov) * Added Japanese translations. (Thanks, Keisuke URAGO) * Added Ukrainian translations. (Thanks, @FuriousCoder) * Added support for Python 3.2. (Thanks, @amrhassan) Version 0.4.1 ------------- * Dropped support for Django 1.3 * Fixed support of newer versions of Pillow (2.1 and above. Pillow 2.2.2 is now required) Thanks @viaregio (Issue #50) Version 0.4.0 ------------- * Perfom some tests at package installation, to check whether PIL or Pillow are already installed. (Issue #46) * Added Slovak translations. (Thanks @ciklysta) Version 0.3.9 ------------- * Run most tests both with a regular Form and a ModelForm, to avoid regressions such as Issue #40 * Handle the special case where CaptchaFields are instantiated with required=False (Issue #42, thanks @DrMeers) * Fixed a misspelled setting, we now support both spellings, but the docs suggest the correct one (Issue #36, thanks @sayadn) * Added Django 1.6b to testrunner and adapted the test cases to support Django 1.6's new test discovery * Added German translations. (Thanks @digi604) * Frozen the version of Pillow to 2.0.0, as 2.1.0 seems to be truncating the output image -- Issue #44, Thanks @andruby * Added Polish translations. (Thanks @stilzdev) Version 0.3.8 ------------- * Fixed a critical bug (Issue #40) that would generate two captcha objects, and the test would always fail. Thanks @pengqi for the heads-up. Version 0.3.7 ------------- * Improved Django 1.5 and Django HEAD (1.6) compatibility (thanks @uruz) * Python3 compatibility (requires six and Pillow >= 2.0) * Added zh_CN localization (thanks @mingchen) * Make sure the generated challenge is a string type (the math challenge was probably broken -- Issue #33, thanks @YDS19872712) * Massive cleanup and refactoring (Issue #38, thanks @tepez) * Test refactoring to test a couple generators that weren't tested by default Version 0.3.6 ------------- * Django 1.5 compatibility (only affects tests) * Italian localization (thanks @arjunadeltoso) * Russian localization (thanks @mikek) * Fixed issue #17 - Append content-length to response (thanks @shchemelevev) * Merged PR #19 - AJAX refresh of captcha (thanks @artofhuman) * Merged PR #22 - Use op.popen instead of subprocess.call to generate the audio CAPTCHA (thanks @beda42) * Fixed issue #10 - uniformize spelling of "CAPTCHA" (thanks @mikek) * Fixed issue #12 - Raise error when try to initialize CaptchaTextInput alone and/or when try to initialize CaptchaField with widget keyword argument (thanks @vstoykov) * Merged PR #15 - Allow a 'test mode' where the string 'PASSED' always validates the CAPTCHA (thanks @beda42) * Dutch translation (thanks @leonderijke) * Turkish translation (thanks @gkmngrgn) Version 0.3.5 ------------- * Fixes issue #4: Fixes id_for_label malfunction with prefixed forms (thanks @lolek09) Version 0.3.4 ------------- * Fixes issue #3: regression on Django 1.4 when USE_TZ is False Version 0.3.3 ------------- * Django 1.4 Time zones compatibility * PEP 8 love Version 0.3.2 ------------- * Added a test project to run tests * Added South migrations django-simple-captcha-0.5.1/django_simple_captcha.egg-info/0000700000076500000240000000000012630556666024070 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/django_simple_captcha.egg-info/dependency_links.txt0000644000076500000240000000000112630556650030141 0ustar marcostaff00000000000000 django-simple-captcha-0.5.1/django_simple_captcha.egg-info/not-zip-safe0000644000076500000240000000000112140410773026310 0ustar marcostaff00000000000000 django-simple-captcha-0.5.1/django_simple_captcha.egg-info/PKG-INFO0000644000076500000240000000146212630556650025173 0ustar marcostaff00000000000000Metadata-Version: 1.1 Name: django-simple-captcha Version: 0.5.1 Summary: A very simple, yet powerful, Django captcha application Home-page: https://github.com/mbi/django-simple-captcha Author: Marco Bonetti Author-email: mbonetti@gmail.com License: MIT Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Security Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Django django-simple-captcha-0.5.1/django_simple_captcha.egg-info/requires.txt0000644000076500000240000000006412630556650026473 0ustar marcostaff00000000000000setuptools six >=1.2.0 Django >= 1.7 Pillow >=2.2.2 django-simple-captcha-0.5.1/django_simple_captcha.egg-info/SOURCES.txt0000644000076500000240000000532412630556657025772 0ustar marcostaff00000000000000.pep8 CHANGES LICENSE MANIFEST.in README.rst setup.cfg setup.py tox.ini captcha/__init__.py captcha/fields.py captcha/helpers.py captcha/models.py captcha/urls.py captcha/views.py captcha/conf/__init__.py captcha/conf/settings.py captcha/fonts/COPYRIGHT.TXT captcha/fonts/README.TXT captcha/fonts/Vera.ttf captcha/locale/bg/LC_MESSAGES/django.mo captcha/locale/bg/LC_MESSAGES/django.po captcha/locale/cs/LC_MESSAGES/django.mo captcha/locale/cs/LC_MESSAGES/django.po captcha/locale/de/LC_MESSAGES/django.mo captcha/locale/de/LC_MESSAGES/django.po captcha/locale/en/LC_MESSAGES/django.po captcha/locale/es/LC_MESSAGES/django.mo captcha/locale/es/LC_MESSAGES/django.po captcha/locale/fr/LC_MESSAGES/django.mo captcha/locale/fr/LC_MESSAGES/django.po captcha/locale/it/LC_MESSAGES/django.mo captcha/locale/it/LC_MESSAGES/django.po captcha/locale/ja/LC_MESSAGES/django.mo captcha/locale/ja/LC_MESSAGES/django.po captcha/locale/nl/LC_MESSAGES/django.mo captcha/locale/nl/LC_MESSAGES/django.po captcha/locale/pl/LC_MESSAGES/django.mo captcha/locale/pl/LC_MESSAGES/django.po captcha/locale/pt_BR/LC_MESSAGES/django.mo captcha/locale/pt_BR/LC_MESSAGES/django.po captcha/locale/ru/LC_MESSAGES/django.mo captcha/locale/ru/LC_MESSAGES/django.po captcha/locale/sk/LC_MESSAGES/django.mo captcha/locale/sk/LC_MESSAGES/django.po captcha/locale/tr/LC_MESSAGES/django.mo captcha/locale/tr/LC_MESSAGES/django.po captcha/locale/uk/LC_MESSAGES/django.mo captcha/locale/uk/LC_MESSAGES/django.po captcha/locale/zh_CN/LC_MESSAGES/django.mo captcha/locale/zh_CN/LC_MESSAGES/django.po captcha/management/__init__.py captcha/management/commands/__init__.py captcha/management/commands/captcha_clean.py captcha/migrations/0001_initial.py captcha/migrations/__init__.py captcha/south_migrations/0001_initial.py captcha/south_migrations/__init__.py captcha/templates/captcha/field.html captcha/templates/captcha/hidden_field.html captcha/templates/captcha/image.html captcha/templates/captcha/text_field.html captcha/tests/__init__.py captcha/tests/tests.py captcha/tests/urls.py captcha/tests/views.py django_simple_captcha.egg-info/PKG-INFO django_simple_captcha.egg-info/SOURCES.txt django_simple_captcha.egg-info/dependency_links.txt django_simple_captcha.egg-info/not-zip-safe django_simple_captcha.egg-info/requires.txt django_simple_captcha.egg-info/top_level.txt docs/Makefile docs/advanced.rst docs/conf.py docs/index.rst docs/usage.rst docs/_static/captcha3.png docs/_static/dict.png docs/_static/math.png docs/_static/random_chars.png testproject/.coveragerc testproject/__init__.py testproject/coverage.sh testproject/forms.py testproject/manage.py testproject/settings.py testproject/urls.py testproject/views.py testproject/templates/home.html testproject/templates/captcha_test/image.htmldjango-simple-captcha-0.5.1/django_simple_captcha.egg-info/top_level.txt0000644000076500000240000000001012630556650026614 0ustar marcostaff00000000000000captcha django-simple-captcha-0.5.1/docs/0000700000076500000240000000000012630556666017030 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/docs/_static/0000700000076500000240000000000012630556666020456 5ustar marcostaff00000000000000django-simple-captcha-0.5.1/docs/_static/captcha3.png0000644000076500000240000002264112505533655022663 0ustar marcostaff00000000000000PNG  IHDR(}9DpiCCPICC Profilex{4T{fZ0'aEm:]\rLZ鶲r wg+yVV^x~R+9{kbj_Zǯߠ(wfíwߍGoѢz-C#ܣ>^Y/x/C_9wwfuvw^O}&;w ?d~L3W~~\W`^}p_9qWKt(Gp7%poq8O+$t$\$%r%Hde6 % {De"A|(%Nꊴ4S~1qc^˸ȔN"B^._/OU0TQx`l%#ZTSwDU_r'lF@S3>D<}4(OkG5-鈯%b$Ct qc4sωCj#)!Zux4! D=-Quu;S42Ȼ{4Mf56o)yl718y-\`,rҴ S|Y;?ݮYp:hLXx;S*o``uű/Vq”B /c8G^vŌ!Ѩ.8i̘f vBCĻzcpRUkXĄYo^<,d1iq#S+4ݺCz/ZV=C]s\Dx!>@L@if{kj:ձ̅?Yw(%zկDyB#`U_.;[V{IeWuj/On![?]8019f9ht%,\Pн?gwWNIĎ6?_'+ӫDYB# )@[K}]/jZ"Xw+G6瞳xf[hk[$2oZX: u!@"=P6;=DN?{ @ijfG`ċ݋O{@mF@Yhk˨=B_ZEq-YP:.D{m2Fq ukK90D<8Ե^[́ 9šu\xڒeρ/u"@ז,s`x|qkQ^*---WiZ~u 9ss%J!kA:|YMtt鸍9O2G&ѫڈ$U?bO͂-#!iT%Ϝ<׺~h,̵u*/08~* dBb׌;4"!49͡5o-s矙4;,9Ō%MԄM湫 uSwww>(Tq&/JŸGWy,U`|gK.0}&$1vϿ:D@5"cXg`hEzz'#|9u$3K&cZ4ka5seThҗ̊^:oָ⬍;Q,yキxr|aGtĄ(beL/8*vUTyO*U+i j/]%=gpH!^XؗIYw[gr9SBsՃyʵ u 1U3Օl }7䄹S"L5Lo,.`Ǖ9WOO?4v !J$(vaDi#Q]cXܿx 'w7Ud A@IBo~(n\Ry߼ҫ)7JʊFt\-:F3ԝ8S2ـ)L5JU8jy}ZUiBʹmogr:)0kcd-XV,$cɑUQp[<{}L2=>$be7r5'F Mힱlጩ)?p+Z6k=RG颚QCw2yOV-% 1Io;ÆbIpDy1`VU^0w4Z7ZWUJ-䯟EFh5QS(Ʉ"NVt,cѨʙkNRU6BŔwQHnXρ~0^SdJ lpA2"8'M&¡D􇹍+uţ#6›qD@<&p&xqO1$ 8=YK˯v.33q,vG=#q<ޮ]Һ:5zN9BCOkL2+P'++9=phAct`W̍6tHV_|IQyzzZ2AU_}'mɣǏAM&㓫SKZBFykP/nybuO ͑$Y\w7di+~dN4#XR,kF폀ØonOg-Y;5O,a:r2fFp\&)hMă@uuum]sTwb(SUdInjO@Sy;ᦺtk 2TV\ŲP=msgΖu é+eW zS`g^_ZTߘkmī6~̰1Kg O 'ֽi$W1>-k: ax"fp`pltw;yBo/o>R?VUUUtל_l $hRbn= Tākӳr@n \HWW5u `d*Bgx)qwμH ]?] {_ 7=I5tL8ikfp?}a}1rbFI<pN^B&!`#j)ۯ::>Up2pki..`&\bB~9Sz ֍(&XOb)MW1G mgȀUU 3&~52#탿6|&c~ń'b\j_IhE®"yfIi/'~ρ'xv-۶_-[+(=|T|T"]v蜜9^10VP7\_XhՂu2n/ooSfbUu 5S._9Q:ͷ8+:w80ȶa7|IkSЅxO*8SO  T|i7QO>ݻvo"# 6sS+=Sx؉c`,@0~KA`AzAO6ok#}vƯHO m)ܴ|r~VMa" VoNLlqCdxB1O-Daa}S1ܾk{3PJN//@X:|R0Q\a"[  񕱷EdE."G<:7,+z{R[ IliM-Agf 1E[gm-zĘ˧$ߐ0~-{6<¨B1jtle':Mػ/B#o)R **Z:'ā e!x'V%'%B'Rх"xѷ癍*|Vd>X~i?6EJ.wfNRpTafj~*1V!lmÚ ӼG>e┞=4PiF|GuGAt[لZ5_jq!C#0)P^I&MgkfzcEM/wn`*pkb$nn 7>oiE*1-\ X) [sR腕Y#4>1.61AK_w=5*mߨpЁ(ʡR#%i[U0][$\!^F5*4=P̴"w?~ n9x^3~tQš<nR 4Cߠlli('8um/o((/t A_PV`Ayxjauadx`O:'BE*ă<a}A< r"nӈ3F!X@bȹJʡ !VBBqhvFז.bipؑþm@ *C P~b)-;G9_x@RWFs:KbH2@*~/xuY@ˡ` YB?_|-` !rCM b˒Y'+"摒ybpwWZ![R}χ(-XKsE@|<`] @ZLI?p"SxP !`uxV L bB%V&5N۰>%NEooo|J}#Y NgϞϝ;bԚ>]x^ѵk!Cs"vFNA#FqݺuC78?ĉk׮%EEE{]iO\<;_?7~\Dψ%77{ׯ]s<`1pbʕ&M[]ej3L'g'VϜ^3\__[Wصk0@,7NkCQܦMqkc+Z˸O]R_a OOv1ڰCa250mٲ Ki;A;-<!OF$,~A~^wgNCK=<^?Z)_Wsx#RZP"nD";yf\\b!xx  (gM|#vMpS+/yiʜs;3JeYI~LeUڇh1gµ2xVܩkfЃqJT"@uUU]}ub\xx!c SX!Spl7caf_S9soZUǚ?VqS}KO|lNm>_.5yE]f${W֨u-gԨjj_?Aɾ?n0= 66aᖛ{Fɪ6?XqQ":[Ka/Ʃ{<"^#yzz"oĻ~DSub}9Ffn fE2jHJ6}{obLݻ/pH_pP7$ TR"qug9 만 nn;xaGL7Xq)UJ$zDV8(̟ilS*b +!,mRE;HxјXPπ,'uҒZNVoZпܟ`) 4{?{MJ\p$WʼnIe _(=Iz*'Mf Hbਗ਼CB}y=-|FL\i0 X V}ڎagԀ!fh~ M1GQo~Qpȑ F=.O ww8 **[|c;[.hœߘGb͊ _|b=*;ю9=y.]̲glDXjKNΨ3.Pȑ#LJJ {p@D+UZ&qWzbbِjZ+zcA3͒wN#3QUT֪{`)$m=5U(u{\msgQNkS"n>p@QQ?ԯKlNMI(Fy.\:Sm%DJdD<$U ]/lsL B6l3B @3 @ij !`: l68S+D<8 Dt:R2wgUWt_x~R+ٕ{bZ\S\#nPR/]sfݭ wݍGo٤|%C#чOnzzY}m//^9+gvuv_Oz&;w >d|٫kf|/J_㿹|w="o;?' DTx8^,Н UpSӡKR?|!Tx0EL3bۂ B{}eqM|sTot\brHte.HD&{$Nz(-Z抬,KvĔGs;#?^>NB"AXd=rGe)+W,WwjjccގM7kK}5p zOJ"[4'OܬRBWAٔҩVh PN˝mHӘdo8dYjsT玞nIҝohmnco n}CņINM.Y437}w+OW+5KW#J"&-8D'`SG[D>{- g.]?zōZm}ᎊ I%)λTK8iuV}#+&[.'+ci'';)*yAIzɭKkuQSnKÚϨmv/i:ޚ [mُ1K%t;)"˳](*nBI8Q[g qi^I_L0&a6fH !ͪ!M sXaNM(oGO%@`@ [~_cA%m;#[I!/b1S&FFoT* pHYs  IDATx XSWO  @B*2NkvZkZ?N #ZTZ$` .!{ {{qo0ڈe^l" Q;  @Jse*Ҩ w 2AHi;Ȼ(t{Ҭu4$@DtM\Fb&C'@J)Y$b41! Iϔ,1R  ҤgJ)M̄bHi3%D@L&fB1D@z4陒E" &@J3" =RL" P )Mzd L(HO&=SHHib&C'@J)Y$b41!Ti&FMfkj:{&͒="&t-ǷyfeoZa|\iDOH4Wko/Ufi~n\x]銯֭:U΍s2q/=b+:=Ug~aQUۑчdp)BZ(2P׽[H\jYk[ƆfEYW@ڈ`XĬ 2\~P38h6VU~ݣWӹwhqwg`b)Q mUs ֽ鵕+ù~'m^0< ]"Rwa׶lِRVaU T!}M@>bm9#I&GC uk2NB3iCDZjjbX f>~f"NꡭYL1OѰmD*~#;f}x0c?UPLzI|>@ibm,7jon^)S*  j}X@k6L_uPՔz6y6nm+OZ=r#"@!{Y)}7f~,8#{Գx>1[:r;Ǚ mصkڬbCֆi &WO2UYɄj_m|v]Xy+;vT{Jk6W=z6o0}Fof 2}Ɲ4z}eR[8}y{ft_nfmZ.\~E&mDc#H1g{u:N•&Ia7?)SN?X:p)SV? ySK_nOaXֵGnm a#>ܾfv>w2/߅doszۇ{NMD6")d---ꂷ:^15ə2XhI5+UJzJi*ݵt>o֚Je{:X:11lwۮIe 8^ovb%n w͸V'%W"1 Uۜ z5S !u UYF) 2a>g" ? mDS X6mwQ9 fG( (:T^^jJٳgwqy 7/^ :޽q/L@apiBb(MRY3 %vg'/@ *^2NҨ)w 2A$^U]p[[!B-p"FY!W -F^,{A8$- #L PT`K'@,-9pR+MS^jSZk\TV?ƫ@2o/]0l -հYU5P+wGP4]{âG>>}a׫{`Y4=JCcN>R\.o"P&z  0%.h8*|yb4 +_7~fO38ϯMl,˥Xi A*,.D_F{o8y[GM1ⰫоВZVuXvQH|@𨹹`\q9+9MMM .ؼ:BR`viYWWw0oL☠ .GcB{HcF ~~kiuLu(cbF kLFN;-cڧ}zء_MJLT6P(6<@WF3kڬ% ޮVPI?|>hcKjjk0CDIMJl3Cy?Ub@VP+N4Edj{ڒp ó!q~RQQQQj/+ Ya" Yew.j R &߬i0p-61r`BMJXΞ9mfg=vWr~J|檺@T܊`0Xl47ZVC G`i}NMG7+04fhc2xBѸ Ykm88Rc KCb̙9gmXp> 'r fbq:54?t]! v]$ޮ Sab2[Ƈ铏oXyhOuI9_6LP H&@ v߾}ʏW̍ȈH{S) 9yW nϝ5oKhA ݛƵku:<Ӳc}*A$ ; 3Ok ꜄V R rHrR2." c'873DD⿕;Lf/c gM1 G-MY&"6B1E= pR{8S))m=HiL t 32 z CT@' ͕k t}%P@v9J"%H&C4()Md8$@Js $H&C4()Md8$@Js $H&C4()Md8$ -CD` p({ $xޣ=O&qҬ|OHD L(HO&=SHHib&C' oYj:/m[Gڪ^E9{x9h%quvɓ'_¡Z=jlB8AxUPLLLFFZ{u-/J-&PVV_Bc3f̘?!C4.syfLss3W;w.'QbbbkKzp@[8;޿0~xĂ?bA=/N42 4>qn22~[Ouڛ+gU_|nlE0xd-[CC|<~@T*ۥOs %n#dNZ2FDXuf\l77{$5FX_oМBKkS_Knٷ6VYʌA!W^e PoyrT!r +r/eqFOWCsK3fbnԧu͈>  ",, 9 8B1Dx87xbxǏeL\t?5h9 |z_3nPy`F:"ƪ|X~"Ň~w)/I0_?\g>Q|\Y þ7~58f{O/ơivB=67A|2IMsс?'bZ1գ1Onֱ9[Oc̘rPBJ- yys)roh(cr?kXƑ7+=;WĬUyMuշ%+}\hr'68!1G0]#7L.a @+&+4 46m*Ok~3+ԼһK6&:5 my!C)͔Lϧ2p1,'5.X&2U)32Ѿ-1QC:G4gIQ:#ČS@ O+\V&.eرMA59e&ę2[dLry:/5NG EJ.{qET/kTu?o>? Py | ^ \ \E9hs`ЅE-Z6bn?cpbF\7fCo|b8--=juK.? =]dDŽVJ5#6TO:>բ/^/ E=% lS#B/7uf>Qӧ񙞞!L u5U3bض:YkvbS/gxsȠYXKjqa̙3-٠ ;;枩(ӬЮchjN'NTVVnw[GC"!a]7>m'Ĺrą2㯪_k2[cHiP|W 脄/}\Z]e'ǰ+**JJJFc=~nI8cԨQft' BCCC)vHi*ďqIpZ74GllRt8;88lp*&Jdѣe .jH [ON6rO$ eak\`o-Mqhh5n4LzCp9K(n8.uHi]N@sDDφ#9#K;uVC:$CtDEf  Ҥ&J-4[T(HM&5QGl ٢BqD@j4="`)#R IM[HiPp%="0 tȃ|&CFÕHiyJV@gt:R2wgUWt_x~R+ٕ{bZ\S\#nPR/]sfݭ wݍGo٤|%C#чOnzzY}m//^9+gvuv_Oz&;w >d|٫kf|/J_㿹|w="o;?' DTx8^,Н UpSӡKR?|!Tx0EL3bۂ B{}eqM|sTot\brHte.HD&{$Nz(-Z抬,KvĔGs;#?^>NB"AXd=rGe)+W,WwjjccގM7kK}5p zOJ"[4'OܬRBWAٔҩVh PN˝mHӘdo8dYjsT玞nIҝohmnco n}CņINM.Y437}w+OW+5KW#J"&-8D'`SG[D>{- g.]?zōZm}ᎊ I%)λTK8iuV}#+&[.'+ci'';)*yAIzɭKkuQSnKÚϨmv/i:ޚ [mُ1K%t;)"˳](*nBI8Q[g qi^I_L0&a6fH !ͪ!M sXaNM(oGO%@`@ [~_cA%m;#[I!/b1S&FFoT* pHYs  zIDATx \TeǟaEV}Lm͖ʒf^-Ӻ]OakWOַ4\_2[2R#$EeS``w80039ӌ~syy~fFxFt"@x$9DHr.M7#$9DHr.M7#$9DHr.M7#v"`@rr.$kbcc^ID8"PZZ5,t" $X(pD$ I$JF#$9Gd(HB$' V2J 9"CD@$9IQ"IJ' IGHrP:INd8"@sD҉$Hr`%D#2N$!@+%t" $X(pD$ I-9#ju$vygͽn,#.% u8co^3[}=S{uLP8i.' $\`k:5vUyXrdkq()5_,ݚ'#Q7Tcٗ[EoN35QIQW}//}<^̇qfΜo`l\g'.يly>{Nƺe'!gl|kqoƛy\xeKR빻ValPY3^~ F܌8}9cƶC ^+'Moxo̕r h[lH`1oye0>+漴ފYtHjc+֭JO,|7UrC”V"ĉr**dLe#Gʌ=Mjͦ^H_=ˏ''%Ňf^`#~؝<;qԠKz>Gp9>*IػA45@%A1]z%nF@E Ȧog3qlv~m=6kM71=qN 0Pn< _V|DCܖ8 Ku0YA?ogU/~]7 gU7O|̃$f XUӌ[yvڵ"}đ<'YjFLΕ\!AD"vŔsBg2p7?bܥZcSg2.kZ+/>Slo:Ig2г c+6i#nFs\Y^'3L߻xػʵ-`ęe}vv.s|eIse]peO+Vl]}:dlhڶP_o\{[͜zOur{DOI@g6"^d---ѓwLNkl3ouF&gNgRk8tB6ĚtFZݞ/YLaNB47阂,.R ;U'Wk": z@ ~k:&,e}>Q59ih#M_jz$E6E@eggԭ^ӧOw^}8[G}dK'OĴ{ˬDܭ QW3Jyߏr>N$+j$9Wq$>^}WY皚~ R)ɜ񲲪R[YX#j.4$4,$,,4t-Ȓknn\|ȱ#?kAP4:i1c4A^^V+9K]J뀅ݍȒ/sM&_Ψ^y|a~A G ل+7͎J"nL@di?CjQ#GIq\~Q?##"J5W zCD63[ai>"Ė\6 ˹MMjDxoCc#6luMA"Nv:I&uHC"Dc#"r974NR SST` ?]`L`HpH``kZloKC/L(E""K@h  U ]^r>DGEwrڰu4#8`ۧLHXj6hŜCC/f#5%p1k,4 g544@u|_+*.&:`8/}ďrIzX;uԓN])Q{l6OuرN!ȒCjjK ˄ ܺsG~H^r ?'pGsn**$(zSl(joSȒGlA3/xL XN UP|,β]%'{4UU` Ɔ  ,9L `˾2G9LXI9~ U7_EwQQ(!t/1|b6:H'PD1^F-,¢,jĄDL[ 4\wىuC Cr!@ccF^ߚ.$Yr].Eƾ 3 RbI ZB.:jij 1ώD<1,\B,9O=ȆٳظQ~= _AG^ =:lVij/,nni 2ZWQ p"K1 C翢U *b:ZR<DDDCDN5sFtH$" PJ(ʯuÊǞD8eO܄@￝&P1g yEx$9 "@JHrE$Y <-$<Iγ/*m*c=4kt{H~/=!@ K$@$@.&6Hr6H(HI$'%]MllPINJd " %t6!@AB D@J$9)m"`C$gđ\ii) ID/pR2d'&v‡Nѓl8QήiJ$DIΖ  $K--J![L{Nk655u>2nx0ᄧ ۾V+W|7W^t%w 7n &bIr"컦JJJ>mԩf8p $'y"<';v|п?x//4/'ɾkٳ۶mKHH{cJ G}tԨQwy9rH9;@@bM6͝;wƌ _KZ z9FJ Ehh(^@)=}WG9fW+|afN祌_p3K{5ԩ׏;tSotڶ7wc-;9hju ;5V Ɗcf+;ziFG _؝09R1$tЊ.i ~78q f&xO/)gI:!5 ' `BP*7/^VfM>bف_hgWx赦IW|Gi#[^disGȽ!£uB9y"'yayNfrq3S8IQ*N'i<7Px 蠚v䜡wEC1*emU]6L^P䀡wk;_(/|?B *XF;݀'4RN^NQwG§FƬ!/|H'4,gw?1p5|w捧_ע}"@AD@j]/p A@!o)p jX3݅ QU .%@s)n Q .%@s)n Q .%CN/IENDB`django-simple-captcha-0.5.1/docs/_static/random_chars.png0000644000076500000240000001771012505533466023636 0ustar marcostaff00000000000000PNG  IHDR0ˮs[oiCCPICC Profilex{4T{9 yRI5(Ly"QifIwHN#)BB/ \&w/{~s6R6 l^Ȥf e[SNH 빏c_c`R&km+$[(ȡ<0byH"fF=q,b ǝf(bA.s ׈9, @TfHo[2 X 8q}xxx*Ask f~|EEc[t Jh?vdj" Hԥ п[$H=ƈD"5vR(9D`Ȁ&l%B0&ȼDਯs֌MT_a5c&Ӟ0 ) ǚdP60{EO-IV]cmN~mfecS’BLjkmZwG=2i̧'~(3N1:BB)+ Wi{X-q B R%_SS[q6l۲mv,wi`LLՙҴ$5zE)Izފ}W?e(geVPK8pYeGB86X L ΢uV**>t:R2wgUWt_x~R+ٕ{bZ\S\#nPR/]sfݭ wݍGo٤|%C#чOnzzY}m//^9+gvuv_Oz&;w >d|٫kf|/J_㿹|w="o;?' DTx8^,Н UpSӡKR?|!Tx0EL3bۂ B{}eqM|sTot\brHte.HD&{$Nz(-Z抬,KvĔGs;#?^>NB"AXd=rGe)+W,WwjjccގM7kK}5p zOJ"[4'OܬRBWAٔҩVh PN˝mHӘdo8dYjsT玞nIҝohmnco n}CņINM.Y437}w+OW+5KW#J"&-8D'`SG[D>{- g.]?zōZm}ᎊ I%)λTK8iuV}#+&[.'+ci'';)*yAIzɭKkuQSnKÚϨmv/i:ޚ [mُ1K%t;)"˳](*nBI8Q[g qi^I_L0&a6fH !ͪ!M sXaNM(oGO%@`@ [~_cA%m;#[I!/b1S&FFoT* pHYs  IDATx XTǿaW b,ŲrviLtt?N;zxzZ2+V3,/03X00 :katؕ Ү0"` 2:KJiWTLi%v%@+n*X&@̇ A7F, AZCg] 튛 # -DHvMHY"`W$Hˆe$H|,+UɯrJAړ{w_:[ʏ8@'~*w?&+{T{ӟ^\865LrQr"\^_"zkHUݨ,+"T1/`ƕ+8\] ;Շ')8T[~p} ˘|jj.OǞ6oӊUrnS0'3myOffnx{ciY _-1=zKV? +?rU\~YK&0Oo߱䃜SФ̅ [/a,&kNMbLgɜ#o4g `>q)j~[K]'*pR:LJ[) snȗbe%y6wp`U9!YK>Xμ!fW-)U>@F^,+<} uGEta6W W6駡Aebc"}f8+g?y C%LoΫS9G4',t[kB2}{'> O׫BWA2]z'@Ƃrv.a2SOC޾-=E7n35! SUd2 T_Fdo"oU\HsqUOS`BAylܚl(q0tSn{^Px0m청[.ΊiitL$ R9$V}r߯p榬b[z*MBj=44!eerIRTWlʓT%'KM?r2U9Q*}= \g `h:ڪ>aZxNK SťLLbKW6ɒ&eת #>ټfob&e-cތjK&-<\~KK2$;!y;jق==,ѴеZ&ev~/k&_vQFɚjqtz^k]/iQVe8R)TdE?/ٸ2^Њex\}0eC|^/}lZI*Qb)؁~%:U{?d(Zo/BMS2V3D6qZ45ۭDϟ`6"z{W_eƇv l,}uҒ&Cۡ* Mpm۶׹5N:bp,l "~Nam+1?f貹xe9pHd8 Aڇ3B"@ E"!@g*XE@AFF7eRY``KfВDQN- `8Ijy5l 1k4v %gh6 $! uAiY鑼#gTVUj5ZD?qԈQqAXhٴZORTRTyRxzz 7p䰑}@x^C+>\ ^]Ю =w/#"080x@€ c&$I1ZOF@@Atk~:m]En"7N{>5)h,"<굫jW%kbbK_tryiN?z|td4 fMpqCx?jVל?3g̙='%)%?$J5Ǐ~ ګb{+:u݅ ,XpMG6&Cn\m~2b{|zEE 2~ /ڿo u(>WK绸8o>*-j.^uBvYOΊ 'LIN24{scA'eevnZ=8O !IcS#|ሀ֬ !P#2 +FX)Y(ZHJ%țn=V\L}||lB13s+Wu11MM؁'wy{>cg8-ǢmC:N"`ȝ81U464>lU#$L b">T ҇GEDO'&^>J(?f-&CW+ 1(Y[")y*&$}[$m'-loL=^l]wyxz Il/{JP /, &<SA~>~0P'6 GW$!7ʚt: 5 }XClId:$! rZh/!de\059IXl`8K;D$`0]`#>bF#WźP*tLܼy$t@I~@: ק&Q.W\F{BɾILbN9P" 4)"{Db͹si)iMA8 y &/bzuR)$+w;(F^se#˜RjA>zlJ%h1r""b">^ѥD ciĐw rޠ {1D\ǜ)9wB #F^Ն>!֚s"h:b1 /7.<)JIоł;̎`͆,Liy!g!U\PMVFfMrQ덖!X:9?fD#X'?Z֬B3 byRaRDT$zðKOwEKӤh!董kw>3 f!\ۖ[ʯY!@8XX\qCXCG0CLNnݱK$ڪJ}}|C9ih WsNxz ml[PL;8jnn.4k`;662,ӈWJoyMuao5j 1t!+)>0 ܣjcn@b^]&:frFY]|,Y_5k r5\}HRツM&+k,!HF!Wd2EAf4;A@Rݺu =(bwU:~VN|>c7+kS5{oN-?_fvI BNmY0gm?gKf&~L#|Z,=O"¡a?oޡF؂+.JSw&!mѕ] wt9npD{HҤXa7 lg m>Eo3zݱ~K] =<'4D0DW1-M{sIM Xx27`j_z͑V5ѡBK:J/qaMB+< ^QUHp-9H1BpZ/):s܂os1==?zˉ|^o pܗgv?0"¬jy5F ^X m~i͑VhAǏ{ZZZhh3 VYS37 ԖMQjb,h h?P-fA#k^PW$4UÆᢪ'Nc}}} ֵSo;6mPH'D ȏ9RQQCNogh|ܼO_qsF^v Xݡa CDP`=[\\v`uAJg8T_P^^^TTԻwo  QQʳG~й’~uu)4V`)=:.q*nÇc w-ht)TL6%%%pbhnSHBPu