constance

Scripts for generating (an earlier obsolete version of) my personal web site
git clone https://code.djc.id.au/git/constance/
commit e69813d6d6227ee7ccefc42ae208e979304cf678
parent 2174d3a069ac25f12709afea97e1822bdb965555
Author: Dan Callaghan <djc@djc.id.au>
Date:   Tue, 16 Sep 2008 20:15:13 +1000

Python reCAPTCHA client library from http://pypi.python.org/packages/source/r/recaptcha-client/recaptcha-client-1.0.2.tar.gz

Diffstat:
Alib/recaptcha/__init__.py | 1+
Alib/recaptcha/client/__init__.py | 0
Alib/recaptcha/client/captcha.py | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/recaptcha/client/mailhide.py | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 161 insertions(+), 0 deletions(-)
diff --git a/lib/recaptcha/__init__.py b/lib/recaptcha/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/lib/recaptcha/client/__init__.py b/lib/recaptcha/client/__init__.py
diff --git a/lib/recaptcha/client/captcha.py b/lib/recaptcha/client/captcha.py
@@ -0,0 +1,92 @@
+import urllib2, urllib
+
+API_SSL_SERVER="https://api-secure.recaptcha.net"
+API_SERVER="http://api.recaptcha.net"
+VERIFY_SERVER="api-verify.recaptcha.net"
+
+class RecaptchaResponse(object):
+    def __init__(self, is_valid, error_code=None):
+        self.is_valid = is_valid
+        self.error_code = error_code
+
+def displayhtml (public_key,
+                 use_ssl = False,
+                 error = None):
+    """Gets the HTML to display for reCAPTCHA
+
+    public_key -- The public api key
+    use_ssl -- Should the request be sent over ssl?
+    error -- An error message to display (from RecaptchaResponse.error_code)"""
+
+    error_param = ''
+    if error:
+	error_param = '&error=%s' % error
+
+    if use_ssl:
+        server = API_SSL_SERVER
+    else:
+        server = API_SERVER
+
+    return """<script type="text/javascript" src="%(ApiServer)s/challenge?k=%(PublicKey)s%(ErrorParam)s"></script>
+
+<noscript>
+  <iframe src="%(ApiServer)s/noscript?k=%(PublicKey)s%(ErrorParam)s" height="300" width="500" frameborder="0"></iframe><br />
+  <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
+  <input type='hidden' name='recaptcha_response_field' value='manual_challenge' />
+</noscript>
+""" % {
+        'ApiServer' : server,
+        'PublicKey' : public_key,
+        'ErrorParam' : error_param,
+        }
+
+
+def submit (recaptcha_challenge_field,
+            recaptcha_response_field,
+            private_key,
+            remoteip):
+    """
+    Submits a reCAPTCHA request for verification. Returns RecaptchaResponse
+    for the request
+
+    recaptcha_challenge_field -- The value of recaptcha_challenge_field from the form
+    recaptcha_response_field -- The value of recaptcha_response_field from the form
+    private_key -- your reCAPTCHA private key
+    remoteip -- the user's ip address
+    """
+
+    if not (recaptcha_response_field and recaptcha_challenge_field and
+            len (recaptcha_response_field) and len (recaptcha_challenge_field)):
+        return RecaptchaResponse (is_valid = False, error_code = 'incorrect-captcha-sol')
+    
+    
+    
+    params = urllib.urlencode ({
+	    'privatekey': private_key,
+            'remoteip' : remoteip,
+	    'challenge': recaptcha_challenge_field,
+	    'response' : recaptcha_response_field,
+	    })
+
+    request = urllib2.Request (
+        url = "http://%s/verify" % VERIFY_SERVER,
+        data = params,
+        headers = {
+            "Content-type": "application/x-www-form-urlencoded",
+            "User-agent": "reCAPTCHA Python"
+            }
+        )
+    
+    httpresp = urllib2.urlopen (request)
+
+    return_values = httpresp.read ().splitlines ();
+    httpresp.close();
+
+    return_code = return_values [0]
+
+    if (return_code == "true"):
+        return RecaptchaResponse (is_valid=True)
+    else:
+        return RecaptchaResponse (is_valid=False, error_code = return_values [1])
+
+
diff --git a/lib/recaptcha/client/mailhide.py b/lib/recaptcha/client/mailhide.py
@@ -0,0 +1,68 @@
+import base64
+import cgi
+
+try:
+    from Crypto.Cipher import AES
+except:
+    raise Exception ("You need the pycrpyto library: http://cheeseshop.python.org/pypi/pycrypto/")
+
+MAIL_HIDE_BASE="http://mailhide.recaptcha.net"
+
+def asurl (email,
+                 public_key,
+                 private_key):
+    """Wraps an email address with reCAPTCHA mailhide and
+    returns the url. public_key is the public key from reCAPTCHA
+    (in the base 64 encoded format). Private key is the AES key, and should
+    be 32 hex chars."""
+    
+    cryptmail = _encrypt_string (email, base64.b16decode (private_key, casefold=True), '\0' * 16)
+    base64crypt = base64.urlsafe_b64encode (cryptmail)
+
+    return "%s/d?k=%s&c=%s" % (MAIL_HIDE_BASE, public_key, base64crypt)
+
+def ashtml (email,
+                  public_key,
+                  private_key):
+    """Wraps an email address with reCAPTCHA Mailhide and
+    returns html that displays the email"""
+
+    url = asurl (email, public_key, private_key)
+    (userpart, domainpart) = _doterizeemail (email)
+
+    return """%(user)s<a href='%(url)s' onclick="window.open('%(url)s', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;" title="Reveal this e-mail address">...</a>@%(domain)s""" % {
+        'user' : cgi.escape (userpart),
+        'domain' : cgi.escape (domainpart),
+        'url'  : cgi.escape (url),
+        }
+    
+
+def _pad_string (str, block_size):
+    numpad = block_size - (len (str) % block_size)
+    return str + numpad * chr (numpad)
+
+def _encrypt_string (str, aes_key, aes_iv):
+    if len (aes_key) != 16:
+	raise Exception ("expecting key of length 16")
+    if len (aes_iv) != 16:
+	raise Exception ("expecting iv of length 16")
+    return AES.new (aes_key, AES.MODE_CBC, aes_iv).encrypt (_pad_string (str, 16))
+
+def _doterizeemail (email):
+    """replaces part of the username with dots"""
+    
+    try:
+        [user, domain] = email.split ('@')
+    except:
+        # handle invalid emails... sorta
+        user = email
+        domain = ""
+
+    if len(user) <= 4:
+        user_prefix = user[:1]
+    elif len(user) <= 6:
+        user_prefix = user[:3]
+    else:
+        user_prefix = user[:4]
+
+    return (user_prefix, domain)