get rid to spam checking and captcha since its just annoying

This commit is contained in:
Dan Colish 2011-04-11 14:03:40 -07:00
parent b03af04f78
commit 6cba016f0c
8 changed files with 2 additions and 527 deletions

View File

@ -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

View File

@ -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"

View File

@ -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 ""

View File

@ -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\'"]+)|
(<a(?:\s+\w+\s*=\s*
(?:"\s*%(url)s.*?"|'\s*%(url)s.*?'|%(url)s.*?)
)+\s*>.*?</a>)
''' % 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)

View File

@ -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))

View File

@ -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;

View File

@ -21,9 +21,6 @@ urlmap = Map([
Rule('/unidiff/<new_id>/<old_id>/', endpoint='pastes/unidiff_paste'),
Rule('/tree/<identifier>/', 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/<int:page>/', endpoint='pastes/show_all'),

View File

@ -7,15 +7,7 @@
<div class="notification">
<h3>{% trans %}Error While Pasting{% endtrans %}</h3>
<p>{% trans error=error|e %}Could not submit your paste because {{ error }}.{% endtrans %}</p>
{% if show_captcha %}
<div class="captcha">
<p>{% trans %}Please fill out the CAPTCHA to proceed:{% endtrans %}</p>
<img src="/_captcha.png" alt="{% trans %}a captcha you can't see. Sorry :({% endtrans %}">
<input type="text" name="captcha" size="20" />
</div>
{%- else %}
<p><a href="javascript:LodgeIt.hideNotification()">{% trans %}hide this message{% endtrans %}</a></p>
{%- endif %}
</div>
{% endif %}
{% if parent %}