diff --git a/lodgeit/controllers/pastes.py b/lodgeit/controllers/pastes.py index fb104cb..572f15f 100644 --- a/lodgeit/controllers/pastes.py +++ b/lodgeit/controllers/pastes.py @@ -11,14 +11,13 @@ from werkzeug import redirect, Response from werkzeug.exceptions import NotFound from lodgeit import local -from lodgeit.lib import antispam from lodgeit.i18n import list_languages as i18n_list_languages, _ from lodgeit.utils import render_to_response, url_for from lodgeit.models import Paste from lodgeit.database import session from lodgeit.lib.highlighting import list_languages, STYLES, get_style from lodgeit.lib.pagination import generate_pagination -from lodgeit.lib.captcha import check_hashed_solution, Captcha + class PasteController(object): @@ -32,7 +31,7 @@ class PasteController(object): language = local.request.session.get('language', 'text') code = error = '' - show_captcha = private = False + private = False parent_id = None req = local.request getform = req.form.get @@ -41,18 +40,6 @@ class PasteController(object): code = getform('code', u'') language = getform('language') parent_id = getform('parent') - spam = getform('webpage') or antispam.is_spam(code) - - if spam: - error = _('your paste contains spam') - captcha = getform('captcha') - if captcha: - if check_hashed_solution(captcha): - error = None - else: - error = _('your paste contains spam and the ' - 'CAPTCHA solution was incorrect') - show_captcha = True if code and language and not error: paste = Paste(code, language, parent_id, req.user_hash, @@ -81,7 +68,6 @@ class PasteController(object): code=code, language=language, error=error, - show_captcha=show_captcha, private=private ) @@ -191,9 +177,5 @@ class PasteController(object): return redirect(local.request.headers.get('referer') or url_for('pastes/new_paste')) - def show_captcha(self): - """Show a captcha.""" - return Captcha().get_response(set_cookie=True) - controller = PasteController diff --git a/lodgeit/i18n/de/LC_MESSAGES/messages.po b/lodgeit/i18n/de/LC_MESSAGES/messages.po index 2da49c9..6499f1d 100644 --- a/lodgeit/i18n/de/LC_MESSAGES/messages.po +++ b/lodgeit/i18n/de/LC_MESSAGES/messages.po @@ -21,10 +21,6 @@ msgstr "" msgid "your paste contains spam" msgstr "Dein Paste beinhaltet Spam" -#: lodgeit/controllers/pastes.py:51 -msgid "your paste contains spam and the CAPTCHA solution was incorrect" -msgstr "Dein Paste beinhaltet Spam und die CAPTCHA-Eingabe war falsch" - #: lodgeit/controllers/static.py:20 msgid "Pasting" msgstr "Pasting" @@ -556,14 +552,6 @@ msgstr "Ein Fehler ist aufgetreten" msgid "Could not submit your paste because %(error)s." msgstr "Konnte den Paste nicht senden, da Fehler aufgetreten sind: %(error)s" -#: lodgeit/views/new_paste.html:12 -msgid "Please fill out the CAPTCHA to proceed:" -msgstr "Bitte fülle das CAPTCHA Feld aus um fortzufahren" - -#: lodgeit/views/new_paste.html:13 -msgid "a captcha you can't see. Sorry :(" -msgstr "ein Captcha das du nicht sehen kannst. Tut mir leid :(" - #: lodgeit/views/new_paste.html:17 msgid "hide this message" msgstr "verberge diese Nachricht" diff --git a/lodgeit/i18n/messages.pot b/lodgeit/i18n/messages.pot index 636d4a0..8ac149f 100644 --- a/lodgeit/i18n/messages.pot +++ b/lodgeit/i18n/messages.pot @@ -21,9 +21,6 @@ msgstr "" msgid "your paste contains spam" msgstr "" -#: lodgeit/controllers/pastes.py:51 -msgid "your paste contains spam and the CAPTCHA solution was incorrect" -msgstr "" #: lodgeit/controllers/static.py:20 msgid "Pasting" @@ -499,14 +496,6 @@ msgstr "" msgid "Could not submit your paste because %(error)s." msgstr "" -#: lodgeit/views/new_paste.html:12 -msgid "Please fill out the CAPTCHA to proceed:" -msgstr "" - -#: lodgeit/views/new_paste.html:13 -msgid "a captcha you can't see. Sorry :(" -msgstr "" - #: lodgeit/views/new_paste.html:17 msgid "hide this message" msgstr "" diff --git a/lodgeit/lib/antispam.py b/lodgeit/lib/antispam.py deleted file mode 100644 index 26bf9da..0000000 --- a/lodgeit/lib/antispam.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" - lodgeit.lib.antispam - ~~~~~~~~~~~~~~~~~~~~ - - Fight stupid spammers. - - :copyright: 2007-2008 by Armin Ronacher, Christopher Grebs. - :license: BSD -""" -from __future__ import division -import re -from operator import sub -from itertools import starmap - - -_url_pattern = ( - r'(?:(?:https?|ftps?|file|ssh|mms|irc|rsync|smb)://|' - r'(?:mailto|telnet|s?news|sips?|skype):)' -) - -_link_re = re.compile(r'''(?xi) - (%(url)s[^\s\'"]+)| - (.*?) -''' % dict(url=_url_pattern)) - - -# maximum number of links in percent -MAX_LINK_PERCENTAGE = 30 - -# maximum number of links in the text (hard limit) -MAX_LINK_NUMBER = 15 - - -def check_for_link_spam(code): - """It's spam if more than 30% of the text are links.""" - spans = [x.span() for x in _link_re.finditer(code)] - if len(spans) > MAX_LINK_PERCENTAGE: - return True - return (sum(starmap(sub, spans)) * -100) / (len(code) or 1) \ - > MAX_LINK_PERCENTAGE - - -def is_spam(code): - """Check if the code provided contains spam.""" - return check_for_link_spam(code) diff --git a/lodgeit/lib/captcha.py b/lodgeit/lib/captcha.py deleted file mode 100644 index 7ec2111..0000000 --- a/lodgeit/lib/captcha.py +++ /dev/null @@ -1,412 +0,0 @@ -# -*- coding: utf-8 -*- -""" - lodgeit.captcha - ~~~~~~~~~~~~~~~ - - A module that produces image and audio captchas. Uses some code of - PyCAPTCHA by Micah Dowty and was originally used in inyoka. - - - :copyright: Copyright 2007-2008 by Armin Ronacher, Micah Dowty. - :license: BSD -""" -import random -import colorsys -import math -from os import listdir -from os.path import abspath, join, dirname, pardir -from PIL import ImageFont, ImageDraw, Image, ImageChops, ImageColor -from werkzeug import Response -from lodgeit import local - -try: - from hashlib import sha1 -except ImportError: - from sha import new as sha1 - -resource_path = abspath(join(dirname(__file__), pardir, 'res')) - - -def check_hashed_solution(solution, hashed_solution=None, secret_key=None): - """Check a solution against the hashed solution from the first - request by using the secret key. - """ - if hashed_solution is None: - hashed_solution = local.request.session.get('captcha_id') - if hashed_solution is None: - return False - return hashed_solution == calculate_hash(solution, secret_key) - - -def calculate_hash(solution, secret_key=None): - """Calculate the hash.""" - if secret_key is None: - secret_key = local.application.secret_key - return sha1('%s|%s' % ( - secret_key, - solution.encode('utf-8') - )).hexdigest() - - -def generate_word(): - """This function returns a pronounceable word.""" - consonants = 'bcdfghjklmnprstvwz' - vowels = 'aeiou' - both = consonants + vowels - length = random.randrange(8, 12) - return ''.join( - random.choice(consonants) + - random.choice(vowels) + - random.choice(both) for x in xrange(length // 3) - )[:length] - - -def get_random_resource(type, prefix=None): - """Return a random resource of a given type.""" - path = join(resource_path, type) - choices = (x for x in listdir(path) if not x.startswith('.')) - if prefix is not None: - choices = (x for x in choices if x.startswith(prefix)) - return join(path, random.choice(tuple(choices))) - - -def random_color(saturation=0.5, lumination=None): - """Return a random number with the given saturation.""" - hue = random.random() - if lumination is None: - lumination = random.random() - r, g, b = colorsys.hls_to_rgb(hue, lumination, saturation) - return '#%02x%02x%02x' % ( - int(r * 255) & 0xff, - int(g * 255) & 0xff, - int(b * 255) & 0xff - ) - - -class Captcha(object): - """Represents a captcha.""" - default_size = (300, 100) - - def __init__(self, solution=None): - if solution is None: - solution = generate_word() - self.solution = solution - self.layers = [ - RandomBackground(), - RandomDistortion() - ] - text_layer = TextLayer(self.solution, bg=self.layers[0].bg) - self.layers.extend((text_layer, SineWarp())) - - def hash_solution(self, secret_key=None): - """Return the solution as hashed value.""" - return calculate_hash(self.solution, secret_key) - - def render_image(self, size=None): - if size is None: - size = self.default_size - image = Image.new('RGBA', size) - for layer in self.layers: - image = layer.render(image) - return image - - def get_response(self, size=None, set_cookie=False): - response = Response(mimetype='image/png') - self.render_image(size=None).save(response.stream, 'PNG') - if set_cookie: - local.request.session['captcha_id'] = self.hash_solution() - return response - - -class Layer(object): - """Baseclass for a captcha layer.""" - bg = 'dark' - - def render(self, image): - return image - - -class TextLayer(Layer): - """Add text to the captcha.""" - bg = 'transparent' - - def __init__(self, text, min_size=32, max_size=48, bg='dark'): - self.text = text - self.alignment = (random.random(), random.random()) - if bg == 'dark': - color = random_color(saturation=0.3, lumination=0.8) - else: - color = random_color(saturation=0.1, lumination=0.1) - self.text_color = color - self.transparency = random.randint(20, 60) - self.font = ImageFont.truetype(get_random_resource('fonts'), - random.randrange(min_size, max_size)) - - def render(self, image): - text_layer = Image.new('RGB', image.size, (0, 0, 0)) - alpha = Image.new('L', image.size, 0) - - # draw grayscale image white on black - text_image = Image.new('L', image.size, 0) - draw = ImageDraw.Draw(text_image) - text_size = self.font.getsize(self.text) - x = int((image.size[0] - text_size[0]) * self.alignment[0] + 0.5) - y = int((image.size[1] - text_size[1]) * self.alignment[1] + 0.5) - draw.text((x, y), self.text, font=self.font, - fill=255 - self.transparency) - - # colorize the text and calculate the alpha channel - alpha = ImageChops.lighter(alpha, text_image) - color_layer = Image.new('RGBA', image.size, self.text_color) - mask = Image.eval(text_image, lambda x: 255 * (x != 0)) - text_layer = Image.composite(color_layer, text_layer, mask) - - # paste the text on the image with the correct alphachannel - image.paste(text_layer, alpha) - return image - - -class CombinedLayer(Layer): - """Combines multiple layers.""" - - def __init__(self, layers): - self.layers = layers - if layers: - self.bg = layers[0].bg - - def render(self, image): - for layer in self.layers: - image = layer.render(image) - return image - - -class RandomBackground(CombinedLayer): - """Selects a random background.""" - - def __init__(self): - layers = [random.choice([SolidColor, DarkBackground, - LightBackground])()] - for x in xrange(random.randrange(1, 4)): - layers.append(random.choice([ - NoiseBackground, - GridBackground - ])()) - CombinedLayer.__init__(self, layers) - self.bg = layers[0].bg - - -class RandomDistortion(CombinedLayer): - """Selects a random distortion.""" - background = 'transparent' - - def __init__(self): - layers = [] - for x in xrange(random.randrange(1, 3)): - layers.append(random.choice(( - WigglyBlocks, - SineWarp - ))()) - CombinedLayer.__init__(self, layers) - - -class Picture(Layer): - """Add a background to the captcha.""" - - def __init__(self, picture): - self.image = Image.open(picture) - self.offset = (random.random(), random.random()) - - def render(self, image): - tile = self.image - for j in xrange(-1, int(image.size[1] / tile.size[1]) + 1): - for i in xrange(-1, int(image.size[0] / tile.size[0]) + 1): - dest = (int((self.offset[0] + i) * tile.size[0]), - int((self.offset[1] + j) * tile.size[1])) - image.paste(tile, dest) - return image - - -class LightBackground(Picture): - bg = 'light' - - def __init__(self): - Picture.__init__(self, get_random_resource('backgrounds/light')) - - -class DarkBackground(Picture): - - def __init__(self): - Picture.__init__(self, get_random_resource('backgrounds/dark')) - - -class NoiseBackground(Layer): - """Add some noise as background. You can combine this with another - background layer. - """ - bg = 'transparent' - - def __init__(self, saturation=0.1, num_dots=None): - self.saturation = saturation - self.num_dots = random.randrange(300, 500) - self.seed = random.random() - - def render(self, image): - r = random.Random(self.seed) - for i in xrange(self.num_dots): - dot_size = random.randrange(1, 5) - bx = int(r.uniform(0, image.size[0] - dot_size)) - by = int(r.uniform(0, image.size[1] - dot_size)) - image.paste(random_color(self.saturation, 0.4), - (bx, by, bx + dot_size - 1, - by + dot_size - 1)) - return image - - -class GridBackground(Layer): - """Add a grid as background. You can combine this with another - background layer. - """ - bg = 'transparent' - - def __init__(self, size=None, color=None): - if size is None: - size = random.randrange(10, 50) - if color is None: - color = random_color(0, 0.4) - self.size = size - self.color = color - self.offset = (random.uniform(0, self.size), - random.uniform(0, self.size)) - - def render(self, image): - draw = ImageDraw.Draw(image) - for i in xrange(image.size[0] / self.size + 1): - draw.line((i * self.size + self.offset[0], 0, - i * self.size + self.offset[0], image.size[1]), - fill=self.color) - for i in xrange(image.size[0] / self.size + 1): - draw.line((0, i * self.size + self.offset[1], - image.size[0], i * self.size+self.offset[1]), - fill=self.color) - return image - - -class SolidColor(Layer): - """A solid color background. Very weak on its own, but good - to combine with other backgrounds. - """ - - def __init__(self, color=None): - if color is None: - color = random_color(0.2, random.random() > 0.5 and 0.3 or 0.7) - self.color = ImageColor.getrgb(color) - if colorsys.rgb_to_hls(*[x / 255.0 for x in self.color])[1] > 0.5: - self.bg = 'light' - - def render(self, image): - image.paste(self.color) - return image - - -class WigglyBlocks(Layer): - """Randomly select and shift blocks of the image""" - bg = 'transparent' - - def __init__(self, block_size=None, sigma=0.01, iterations=None): - if block_size is None: - block_size = random.randrange(15, 25) - if iterations is None: - iterations = random.randrange(250, 350) - self.block_size = block_size - self.sigma = sigma - self.iterations = iterations - self.seed = random.random() - - def render(self, image): - r = random.Random(self.seed) - for i in xrange(self.iterations): - # Select a block - bx = int(r.uniform(0, image.size[0] - self.block_size)) - by = int(r.uniform(0, image.size[1] - self.block_size)) - block = image.crop((bx, by, bx + self.block_size - 1, - by + self.block_size - 1)) - - # Figure out how much to move it. - # The call to floor() is important so we always round toward - # 0 rather than to -inf. Just int() would bias the block motion. - mx = int(math.floor(r.normalvariate(0, self.sigma))) - my = int(math.floor(r.normalvariate(0, self.sigma))) - - # Now actually move the block - image.paste(block, (bx+mx, by+my)) - return image - - -class WarpBase(Layer): - """Abstract base class for image warping. Subclasses define a function - that maps points in the output image to points in the input image. This - warping engine runs a grid of points through this transform and uses PIL's - mesh transform to warp the image. - """ - bg = 'transparent' - filtering = Image.BILINEAR - resolution = 10 - - def get_transform(self, image): - """Return a transformation function, subclasses should override this""" - return lambda x, y: (x, y) - - def render(self, image): - r = self.resolution - x_points = image.size[0] / r + 2 - y_points = image.size[1] / r + 2 - f = self.get_transform(image) - - # Create a list of arrays with transformed points - x_rows = [] - y_rows = [] - for j in xrange(y_points): - x_row = [] - y_row = [] - for i in xrange(x_points): - x, y = f(i * r, j * r) - - # Clamp the edges so we don't get black undefined areas - x = max(0, min(image.size[0] - 1, x)) - y = max(0, min(image.size[1] - 1, y)) - - x_row.append(x) - y_row.append(y) - x_rows.append(x_row) - y_rows.append(y_row) - - # Create the mesh list, with a transformation for - # each square between points on the grid - mesh = [] - for j in xrange(y_points - 1): - for i in xrange(x_points-1): - mesh.append(( - # Destination rectangle - (i * r, j * r, (i + 1) * r, (j + 1) * r), - # Source quadrilateral - (x_rows[j][i], y_rows[j][i], - x_rows[j + 1][i], y_rows[j+1][i], - x_rows[j + 1][i + 1], y_rows[j + 1][i + 1], - x_rows[j][i+1], y_rows[j][i + 1]), - )) - return image.transform(image.size, Image.MESH, mesh, self.filtering) - - -class SineWarp(WarpBase): - """Warp the image using a random composition of sine waves""" - - def __init__(self, amplitude_range=(3, 6.5), period_range=(0.04, 0.1)): - self.amplitude = random.uniform(*amplitude_range) - self.period = random.uniform(*period_range) - self.offset = (random.uniform(0, math.pi * 2 / self.period), - random.uniform(0, math.pi * 2 / self.period)) - - def get_transform(self, image): - return (lambda x, y, a=self.amplitude, p=self.period, - o=self.offset: (math.sin((y + o[0]) * p) * a + x, - math.sin((x + o[1]) * p) * a + y)) diff --git a/lodgeit/static/style.css b/lodgeit/static/style.css index 4644c89..99d6122 100644 --- a/lodgeit/static/style.css +++ b/lodgeit/static/style.css @@ -179,19 +179,6 @@ div.related h3 a { text-decoration: none; } -div.notification div.captcha { - margin: 10px; -} - -div.notification div.captcha p { - padding: 0; -} - -div.notification div.captcha img { - display: block; - margin: 8px 0 8px 0; -} - div.related h3 a:hover, div.paste_filter h3 a:hover { background-color: #739BA5; diff --git a/lodgeit/urls.py b/lodgeit/urls.py index db91380..a52f421 100644 --- a/lodgeit/urls.py +++ b/lodgeit/urls.py @@ -21,9 +21,6 @@ urlmap = Map([ Rule('/unidiff///', endpoint='pastes/unidiff_paste'), Rule('/tree//', endpoint='pastes/show_tree'), - # captcha for new paste - Rule('/_captcha.png', endpoint='pastes/show_captcha'), - # paste list Rule('/all/', endpoint='pastes/show_all'), Rule('/all//', endpoint='pastes/show_all'), diff --git a/lodgeit/views/new_paste.html b/lodgeit/views/new_paste.html index 2a44ef4..6a3547f 100644 --- a/lodgeit/views/new_paste.html +++ b/lodgeit/views/new_paste.html @@ -7,15 +7,7 @@ {% trans %}Error While Pasting{% endtrans %} {% trans error=error|e %}Could not submit your paste because {{ error }}.{% endtrans %} - {% if show_captcha %} - - {% trans %}Please fill out the CAPTCHA to proceed:{% endtrans %} - - - - {%- else %} {% trans %}hide this message{% endtrans %} - {%- endif %} {% endif %} {% if parent %}
{% trans error=error|e %}Could not submit your paste because {{ error }}.{% endtrans %}
{% trans %}Please fill out the CAPTCHA to proceed:{% endtrans %}
{% trans %}hide this message{% endtrans %}