diff data_generation/transformations/pycaptcha/Captcha/Base.py @ 167:1f5937e9e530

More moves - transformations into data_generation, added "deep" folder
author Dumitru Erhan <dumitru.erhan@gmail.com>
date Fri, 26 Feb 2010 14:15:38 -0500
parents pycaptcha/Captcha/Base.py@4775b4195b4b
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data_generation/transformations/pycaptcha/Captcha/Base.py	Fri Feb 26 14:15:38 2010 -0500
@@ -0,0 +1,127 @@
+""" Captcha.Base
+
+Base class for all types of CAPTCHA tests. All tests have one or
+more solution, determined when the test is generated. Solutions
+can be any python object,
+
+All tests can be solved by presenting at least some preset number
+of correct solutions. Some tests may only have one solution and require
+one solution, but other tests may require N correct solutions of M
+possible solutions.
+"""
+#
+# PyCAPTCHA Package
+# Copyright (C) 2004 Micah Dowty <micah@navi.cx>
+#
+
+import random, string, time, shelve
+
+__all__ = ["BaseCaptcha", "Factory", "PersistentFactory"]
+
+
+def randomIdentifier(alphabet = string.ascii_letters + string.digits,
+                     length = 24):
+    return "".join([random.choice(alphabet) for i in xrange(length)])
+
+
+class BaseCaptcha(object):
+    """Base class for all CAPTCHA tests"""
+    # Subclasses can override these to set the solution criteria
+    minCorrectSolutions = 1
+    maxIncorrectSolutions = 0
+
+    def __init__(self):
+        self.solutions = []
+        self.valid = True
+
+        # Each test has a unique identifier, used to refer to that test
+        # later, and a creation time so it can expire later.
+        self.id = randomIdentifier()
+        self.creationTime = time.time()
+
+    def addSolution(self, solution):
+        self.solutions.append(solution)
+
+    def testSolutions(self, solutions):
+        """Test whether the given solutions are sufficient for this CAPTCHA.
+           A given CAPTCHA can only be tested once, after that it is invalid
+           and always returns False. This makes random guessing much less effective.
+           """
+        if not self.valid:
+            return False
+        self.valid = False
+
+        numCorrect = 0
+        numIncorrect = 0
+
+        for solution in solutions:
+            if solution in self.solutions:
+                numCorrect += 1
+            else:
+                numIncorrect += 1
+
+        return numCorrect >= self.minCorrectSolutions and \
+               numIncorrect <= self.maxIncorrectSolutions
+
+
+class Factory(object):
+    """Creates BaseCaptcha instances on demand, and tests solutions.
+       CAPTCHAs expire after a given amount of time, given in seconds.
+       The default is 15 minutes.
+       """
+    def __init__(self, lifetime=60*15):
+        self.lifetime = lifetime
+        self.storedInstances = {}
+
+    def new(self, cls, *args, **kwargs):
+        """Create a new instance of our assigned BaseCaptcha subclass, passing
+           it any extra arguments we're given. This stores the result for
+           later testing.
+           """
+        self.clean()
+        inst = cls(*args, **kwargs)
+        self.storedInstances[inst.id] = inst
+        return inst
+
+    def get(self, id):
+        """Retrieve the CAPTCHA with the given ID. If it's expired already,
+           this will return None. A typical web application will need to
+           new() a CAPTCHA when generating an html page, then get() it later
+           when its images or sounds must be rendered.
+           """
+        return self.storedInstances.get(id)
+
+    def clean(self):
+        """Removed expired tests"""
+        expiredIds = []
+        now = time.time()
+        for inst in self.storedInstances.itervalues():
+            if inst.creationTime + self.lifetime < now:
+                expiredIds.append(inst.id)
+        for id in expiredIds:
+            del self.storedInstances[id]
+
+    def test(self, id, solutions):
+        """Test the given list of solutions against the BaseCaptcha instance
+           created earlier with the given id. Returns True if the test passed,
+           False on failure. In either case, the test is invalidated. Returns
+           False in the case of an invalid id.
+           """
+        self.clean()
+        inst = self.storedInstances.get(id)
+        if not inst:
+            return False
+        result = inst.testSolutions(solutions)
+        return result
+
+
+class PersistentFactory(Factory):
+    """A simple persistent factory, for use in CGI or multi-process environments
+       where the state must remain across python interpreter sessions.
+       This implementation uses the 'shelve' module.
+       """
+    def __init__(self, filename, lifetime=60*15):
+        Factory.__init__(self, lifetime)
+	self.storedInstances = shelve.open(filename)
+
+### The End ###