CaptchasDotNet.py 5.23 KB
Newer Older
1
# -*- coding: utf-8 -*-
Aurel's avatar
Aurel committed
2
#---------------------------------------------------------------------
3 4 5
# Python module for easy utilization of http://captchas.net
#
# For documentation look at http://captchas.net/sample/python/
Aurel's avatar
Aurel committed
6
#
7 8 9 10 11 12
# Written by Sebastian Wilhelmi <seppi@seppi.de> and
#            Felix Holderied <felix@holderied.de>
# This file is in the public domain.
#
# ChangeLog:
#
13 14
# 2010-06-20: Add ssl choice.
#
15 16 17
# 2010-01-15: Adapt to ERP5 : a lot of code had to be removed or changed.
#            Most of the work must be done in another class.
#
Aurel's avatar
Aurel committed
18 19 20
# 2006-09-08: Add new optional parameters alphabet, letters
#             height an width. Add audio_url.
#
21 22 23 24 25 26 27 28 29 30
# 2006-03-01: Only delete the random string from the repository in
#             case of a successful verification.
#
# 2006-02-14: Add new image() method returning an HTML/JavaScript
#             snippet providing a fault tolerant service.
#
# 2005-06-02: Initial version.
#
#---------------------------------------------------------------------

31
from hashlib import md5
32 33 34 35 36 37 38
import random

class CaptchasDotNet:
    def __init__ (self, client, secret,
                  alphabet = 'abcdefghkmnopqrstuvwxyz',
                  letters = 6,
                  width = 240,
39 40
                  height = 80,
                  use_ssl = False,
41 42 43 44 45 46 47
                  ):
        self.__client = client
        self.__secret = secret
        self.__alphabet = alphabet
        self.__letters = letters
        self.__width = width
        self.__height = height
48
        self.__protocol = use_ssl and 'https' or 'http'
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

    # Return a random string
    def random_string (self):
        # The random string shall consist of small letters, big letters
        # and digits.
        letters = "abcdefghijklmnopqrstuvwxyz"
        letters += letters.upper () + "0123456789"

        # The random starts out empty, then 40 random possible characters
        # are appended.
        random_string = ''
        for i in range (40):
            random_string += random.choice (letters)

        # Return the random string.
        return random_string

Aurel's avatar
Aurel committed
66
    def image_url (self, random, base = 'image.captchas.net/'):
67
        url = '%s://%s?client=%s&amp;random=%s' % (self.__protocol,base,self.__client,random)
68 69 70 71 72 73 74 75 76 77
        if self.__alphabet != "abcdefghijklmnopqrstuvwxyz":
            url += '&amp;alphabet=%s' % self.__alphabet
        if self.__letters != 6:
            url += '&amp;letters=%s' % self.__letters
        if self.__width != 240:
            url += '&amp;width=%s' % self.__width
        if self.__height != 80:
            url += '&amp;height=%s' % self.__height
        return url

78 79
    def audio_url (self, random, base = 'audio.captchas.net/'):
        url = '%s://%s?client=%s&amp;random=%s' % (self.__protocol,base,self.__client,random)
80 81 82 83 84 85 86 87 88
        if self.__alphabet != "abcdefghijklmnopqrstuvwxyz":
            url += '&amp;alphabet=%s' % self.__alphabet
        if self.__letters != 6:
            url += '&amp;letters=%s' % self.__letters
        return url

    def image (self, random, id = 'captchas.net'):
        return '''
        <a href="http://captchas.net"><img
89
            class="captchas_dot_net"
90
            id="%(id)s" src="%(source)s" width="%(width)d" height="%(height)d"
91 92 93
            alt="The CAPTCHA image" /></a>
        <script type="text/javascript">
          <!--
Aurel's avatar
Aurel committed
94
          function captchas_image_error (image)
95 96
          {
            if (!image.timeout) return true;
Aurel's avatar
Aurel committed
97
            image.src = image.src.replace (/^%(protocol)s:\/\/image\.captchas\.net/,
98
                                           '%(protocol)s://image.backup.captchas.net');
99 100 101 102 103 104 105 106 107 108 109
            return captchas_image_loaded (image);
          }

          function captchas_image_loaded (image)
          {
            if (!image.timeout) return true;
            window.clearTimeout (image.timeout);
            image.timeout = false;
            return true;
          }

110
          var image = document.getElementById ('%(id)s');
111 112
          image.onerror = function() {return captchas_image_error (image);};
          image.onload = function() {return captchas_image_loaded (image);};
113
          image.timeout = window.setTimeout(
114
               "captchas_image_error (document.getElementById ('%(id)s'))",
115 116
               10000);
          image.src = image.src;
Aurel's avatar
Aurel committed
117 118 119 120
          //-->
        </script>''' % ( {'id':id,
                          'source': self.image_url(random),
                          'width': self.__width,
121 122
                          'height': self.__height,
                          'protocol': self.__protocol})
Aurel's avatar
Aurel committed
123

124 125 126 127 128 129 130 131 132 133
    def get_answer (self, random ):
        # The format of the password.
        password_alphabet = self.__alphabet
        password_length = self.__letters

        # Calculate the MD5 digest of the concatenation of secret key and
        # random string.
        encryption_base = self.__secret + random
        if (password_alphabet != "abcdefghijklmnopqrstuvwxyz") or (password_length != 6):
            encryption_base += ":" + password_alphabet + ":" + str(password_length)
134
        digest = md5(encryption_base).digest()
135 136 137 138 139 140

        # Compute password
        correct_password = ''
        for pos in range (password_length):
            letter_num = ord (digest[pos]) % len (password_alphabet)
            correct_password += password_alphabet[letter_num]
Aurel's avatar
Aurel committed
141

142
        return correct_password