diff --git a/lodgeit/application.py b/lodgeit/application.py index cdf23c5..984302b 100644 --- a/lodgeit/application.py +++ b/lodgeit/application.py @@ -24,11 +24,12 @@ from lodgeit.controllers import get_controller from lodgeit.lib.antispam import AntiSpam - class LodgeIt(object): """The WSGI Application""" - def __init__(self, dburi): + def __init__(self, dburi, secret_key): + #: the secret key used by the captcha + self.secret_key = secret_key #: name of the error handler self.not_found = ('static/not_found', {}) self.engine = sqlalchemy.create_engine(dburi) @@ -67,10 +68,10 @@ class LodgeIt(object): [_local_manager.cleanup, session.remove]) -def make_app(dburi, debug=False, shell=False): +def make_app(dburi, secret_key, debug=False, shell=False): """Apply the used middlewares and create the application.""" static_path = os.path.join(os.path.dirname(__file__), 'static') - app = LodgeIt(dburi) + app = LodgeIt(dburi, secret_key) if debug: app.engine.echo = True if not shell: diff --git a/lodgeit/controllers/pastes.py b/lodgeit/controllers/pastes.py index e11ad24..5c2b221 100644 --- a/lodgeit/controllers/pastes.py +++ b/lodgeit/controllers/pastes.py @@ -16,6 +16,7 @@ from lodgeit.controllers import BaseController from lodgeit.database import session, Paste from lodgeit.lib.highlighting import LANGUAGES, STYLES, get_style from lodgeit.lib.pagination import generate_pagination +from lodgeit.lib.captcha import check_hashed_solution, Captcha MAX_LINE_LENGTH = 300 @@ -29,6 +30,7 @@ class PasteController(BaseController): code = error = '' language = 'text' pastes = session.query(Paste) + show_captcha = False if ctx.request.method == 'POST': code = ctx.request.form.get('code') @@ -41,7 +43,14 @@ class PasteController(BaseController): spam = ctx.request.form.get('webpage') or \ ctx.application.antispam.is_spam(code) if spam: - error = 'contains spam' + error = 'your paste contains spam' + captcha = ctx.request.form.get('captcha') + if captcha: + if check_hashed_solution(captcha): + error = None + else: + error += ' and the CAPTCHA solution was incorrect' + show_captcha = True if code and language and not error: paste = Paste(code, language, parent, ctx.request.user_hash) session.save(paste) @@ -60,7 +69,8 @@ class PasteController(BaseController): parent=parent, code=code, language=language, - error=error + error=error, + show_captcha=show_captcha ) def show_paste(self, paste_id, raw=False): @@ -122,9 +132,7 @@ class PasteController(BaseController): ) def compare_paste(self, new_id=None, old_id=None): - """ - Render a diff view for two pastes. - """ + """Render a diff view for two pastes.""" # redirect for the compare form box if old_id is new_id is None: old_id = ctx.request.form.get('old', '-1').lstrip('#') @@ -142,9 +150,7 @@ class PasteController(BaseController): ) def unidiff_paste(self, new_id=None, old_id=None): - """ - Render an udiff for the two pastes. - """ + """Render an udiff for the two pastes.""" pastes = session.query(Paste) old = pastes.filter(Paste.c.paste_id == old_id).first() new = pastes.filter(Paste.c.paste_id == new_id).first() @@ -153,8 +159,7 @@ class PasteController(BaseController): return Response(old.compare_to(new), mimetype='text/plain') def set_colorscheme(self): - """ - Minimal view that updates the style session cookie. Redirects + """Minimal view that updates the style session cookie. Redirects back to the page the user is coming from. """ style_name = ctx.request.form.get('style') @@ -163,4 +168,8 @@ class PasteController(BaseController): resp.set_cookie('style', style_name) return resp + def show_captcha(self): + """Show a captcha.""" + return Captcha().get_response(set_cookie=True) + controller = PasteController diff --git a/lodgeit/lib/antispam.py b/lodgeit/lib/antispam.py index 3b2cce0..1659825 100644 --- a/lodgeit/lib/antispam.py +++ b/lodgeit/lib/antispam.py @@ -24,7 +24,7 @@ LINK_RE = re.compile(r'%s[^\s\'"]+\S' % _url_pattern) def percentize(matched, length): - return matched * 100.0 / length + return matched * 100.0 / (length or 1) class AntiSpam(object): @@ -36,7 +36,7 @@ class AntiSpam(object): def check_for_link_spam(self, code): lengths = (x.span() for x in LINK_RE.finditer(code)) return percentize(sum(i[1]-i[0] for i in lengths), - len(code) or 1) > 50 + len(code)) > 50 def is_spam(self, code): """Check if one of the fields provides contains spam.""" diff --git a/lodgeit/lib/captcha.py b/lodgeit/lib/captcha.py new file mode 100644 index 0000000..bf21d06 --- /dev/null +++ b/lodgeit/lib/captcha.py @@ -0,0 +1,415 @@ +# -*- 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 by Armin Ronacher, Micah Dowty. + :license: GNU GPL. +""" +import random +import colorsys +import math +try: + from hashlib import sha1 +except ImportError: + from sha import new as sha1 +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.utils import ctx + + +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 = ctx.request.cookies.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 = ctx.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: + response.set_cookie('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) + f = get_random_resource('fonts') + 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/res/.DS_Store b/lodgeit/res/.DS_Store new file mode 100644 index 0000000..fe855a4 Binary files /dev/null and b/lodgeit/res/.DS_Store differ diff --git a/lodgeit/res/LICENSE b/lodgeit/res/LICENSE new file mode 100644 index 0000000..ec45add --- /dev/null +++ b/lodgeit/res/LICENSE @@ -0,0 +1,4 @@ +Background Image Patterns + + * 1-12 from PyCAPTCHA + * 12-15 from orgdot.com/3dstuff diff --git a/lodgeit/res/backgrounds/dark/1.jpeg b/lodgeit/res/backgrounds/dark/1.jpeg new file mode 100644 index 0000000..fea3b2f Binary files /dev/null and b/lodgeit/res/backgrounds/dark/1.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/10.jpeg b/lodgeit/res/backgrounds/dark/10.jpeg new file mode 100644 index 0000000..256936e Binary files /dev/null and b/lodgeit/res/backgrounds/dark/10.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/11.jpeg b/lodgeit/res/backgrounds/dark/11.jpeg new file mode 100644 index 0000000..105d5fa Binary files /dev/null and b/lodgeit/res/backgrounds/dark/11.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/12.jpeg b/lodgeit/res/backgrounds/dark/12.jpeg new file mode 100644 index 0000000..686392c Binary files /dev/null and b/lodgeit/res/backgrounds/dark/12.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/13.jpeg b/lodgeit/res/backgrounds/dark/13.jpeg new file mode 100644 index 0000000..06283ce Binary files /dev/null and b/lodgeit/res/backgrounds/dark/13.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/14.jpeg b/lodgeit/res/backgrounds/dark/14.jpeg new file mode 100644 index 0000000..9af1005 Binary files /dev/null and b/lodgeit/res/backgrounds/dark/14.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/2.jpeg b/lodgeit/res/backgrounds/dark/2.jpeg new file mode 100644 index 0000000..ae3c529 Binary files /dev/null and b/lodgeit/res/backgrounds/dark/2.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/3.jpeg b/lodgeit/res/backgrounds/dark/3.jpeg new file mode 100644 index 0000000..80a901c Binary files /dev/null and b/lodgeit/res/backgrounds/dark/3.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/4.jpeg b/lodgeit/res/backgrounds/dark/4.jpeg new file mode 100644 index 0000000..c957b0e Binary files /dev/null and b/lodgeit/res/backgrounds/dark/4.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/5.jpeg b/lodgeit/res/backgrounds/dark/5.jpeg new file mode 100644 index 0000000..8ab8dc8 Binary files /dev/null and b/lodgeit/res/backgrounds/dark/5.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/6.jpeg b/lodgeit/res/backgrounds/dark/6.jpeg new file mode 100644 index 0000000..3869d84 Binary files /dev/null and b/lodgeit/res/backgrounds/dark/6.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/7.jpeg b/lodgeit/res/backgrounds/dark/7.jpeg new file mode 100644 index 0000000..2840d4c Binary files /dev/null and b/lodgeit/res/backgrounds/dark/7.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/8.jpeg b/lodgeit/res/backgrounds/dark/8.jpeg new file mode 100644 index 0000000..f565d47 Binary files /dev/null and b/lodgeit/res/backgrounds/dark/8.jpeg differ diff --git a/lodgeit/res/backgrounds/dark/9.jpeg b/lodgeit/res/backgrounds/dark/9.jpeg new file mode 100644 index 0000000..87fbcc8 Binary files /dev/null and b/lodgeit/res/backgrounds/dark/9.jpeg differ diff --git a/lodgeit/res/backgrounds/light/1.jpeg b/lodgeit/res/backgrounds/light/1.jpeg new file mode 100644 index 0000000..c4b0921 Binary files /dev/null and b/lodgeit/res/backgrounds/light/1.jpeg differ diff --git a/lodgeit/res/backgrounds/light/2.jpeg b/lodgeit/res/backgrounds/light/2.jpeg new file mode 100644 index 0000000..f7b8f8a Binary files /dev/null and b/lodgeit/res/backgrounds/light/2.jpeg differ diff --git a/lodgeit/res/backgrounds/light/3.jpeg b/lodgeit/res/backgrounds/light/3.jpeg new file mode 100644 index 0000000..5254838 Binary files /dev/null and b/lodgeit/res/backgrounds/light/3.jpeg differ diff --git a/lodgeit/res/backgrounds/light/4.jpeg b/lodgeit/res/backgrounds/light/4.jpeg new file mode 100644 index 0000000..f20cc7c Binary files /dev/null and b/lodgeit/res/backgrounds/light/4.jpeg differ diff --git a/lodgeit/res/backgrounds/light/5.jpeg b/lodgeit/res/backgrounds/light/5.jpeg new file mode 100644 index 0000000..7686843 Binary files /dev/null and b/lodgeit/res/backgrounds/light/5.jpeg differ diff --git a/lodgeit/res/backgrounds/light/6.jpeg b/lodgeit/res/backgrounds/light/6.jpeg new file mode 100644 index 0000000..6294240 Binary files /dev/null and b/lodgeit/res/backgrounds/light/6.jpeg differ diff --git a/lodgeit/res/backgrounds/light/7.jpeg b/lodgeit/res/backgrounds/light/7.jpeg new file mode 100644 index 0000000..f7a3e16 Binary files /dev/null and b/lodgeit/res/backgrounds/light/7.jpeg differ diff --git a/lodgeit/res/fonts/GEMELLI_.TTF b/lodgeit/res/fonts/GEMELLI_.TTF new file mode 100644 index 0000000..3a40ddd Binary files /dev/null and b/lodgeit/res/fonts/GEMELLI_.TTF differ diff --git a/lodgeit/res/fonts/GEMERALD.TTF b/lodgeit/res/fonts/GEMERALD.TTF new file mode 100644 index 0000000..7fe87ae Binary files /dev/null and b/lodgeit/res/fonts/GEMERALD.TTF differ diff --git a/lodgeit/res/fonts/GOHAN___.TTF b/lodgeit/res/fonts/GOHAN___.TTF new file mode 100644 index 0000000..ed5d6d5 Binary files /dev/null and b/lodgeit/res/fonts/GOHAN___.TTF differ diff --git a/lodgeit/res/fonts/GOODFISH.TTF b/lodgeit/res/fonts/GOODFISH.TTF new file mode 100644 index 0000000..d200b83 Binary files /dev/null and b/lodgeit/res/fonts/GOODFISH.TTF differ diff --git a/lodgeit/res/fonts/GUNPLAY.TTF b/lodgeit/res/fonts/GUNPLAY.TTF new file mode 100644 index 0000000..3845b25 Binary files /dev/null and b/lodgeit/res/fonts/GUNPLAY.TTF differ diff --git a/lodgeit/res/fonts/Vera.ttf b/lodgeit/res/fonts/Vera.ttf new file mode 100644 index 0000000..58cd6b5 Binary files /dev/null and b/lodgeit/res/fonts/Vera.ttf differ diff --git a/lodgeit/res/fonts/VeraBI.ttf b/lodgeit/res/fonts/VeraBI.ttf new file mode 100644 index 0000000..b55eee3 Binary files /dev/null and b/lodgeit/res/fonts/VeraBI.ttf differ diff --git a/lodgeit/res/fonts/VeraBd.ttf b/lodgeit/res/fonts/VeraBd.ttf new file mode 100644 index 0000000..51d6111 Binary files /dev/null and b/lodgeit/res/fonts/VeraBd.ttf differ diff --git a/lodgeit/res/fonts/VeraMoBI.ttf b/lodgeit/res/fonts/VeraMoBI.ttf new file mode 100644 index 0000000..8624542 Binary files /dev/null and b/lodgeit/res/fonts/VeraMoBI.ttf differ diff --git a/lodgeit/res/fonts/VeraMoBd.ttf b/lodgeit/res/fonts/VeraMoBd.ttf new file mode 100644 index 0000000..9be6547 Binary files /dev/null and b/lodgeit/res/fonts/VeraMoBd.ttf differ diff --git a/lodgeit/res/fonts/VeraMoIt.ttf b/lodgeit/res/fonts/VeraMoIt.ttf new file mode 100644 index 0000000..2404924 Binary files /dev/null and b/lodgeit/res/fonts/VeraMoIt.ttf differ diff --git a/lodgeit/res/fonts/VeraMono.ttf b/lodgeit/res/fonts/VeraMono.ttf new file mode 100644 index 0000000..139f0b4 Binary files /dev/null and b/lodgeit/res/fonts/VeraMono.ttf differ diff --git a/lodgeit/res/fonts/VeraSe.ttf b/lodgeit/res/fonts/VeraSe.ttf new file mode 100644 index 0000000..4b4ecc6 Binary files /dev/null and b/lodgeit/res/fonts/VeraSe.ttf differ diff --git a/lodgeit/res/fonts/VeraSeBd.ttf b/lodgeit/res/fonts/VeraSeBd.ttf new file mode 100644 index 0000000..672bf76 Binary files /dev/null and b/lodgeit/res/fonts/VeraSeBd.ttf differ diff --git a/lodgeit/res/fonts/luximb.ttf b/lodgeit/res/fonts/luximb.ttf new file mode 100644 index 0000000..7473e1f Binary files /dev/null and b/lodgeit/res/fonts/luximb.ttf differ diff --git a/lodgeit/res/fonts/luximbi.ttf b/lodgeit/res/fonts/luximbi.ttf new file mode 100644 index 0000000..734201b Binary files /dev/null and b/lodgeit/res/fonts/luximbi.ttf differ diff --git a/lodgeit/res/fonts/luximr.ttf b/lodgeit/res/fonts/luximr.ttf new file mode 100644 index 0000000..6ad6e12 Binary files /dev/null and b/lodgeit/res/fonts/luximr.ttf differ diff --git a/lodgeit/res/fonts/luximri.ttf b/lodgeit/res/fonts/luximri.ttf new file mode 100644 index 0000000..9b02826 Binary files /dev/null and b/lodgeit/res/fonts/luximri.ttf differ diff --git a/lodgeit/res/fonts/luxirb.ttf b/lodgeit/res/fonts/luxirb.ttf new file mode 100644 index 0000000..89c1711 Binary files /dev/null and b/lodgeit/res/fonts/luxirb.ttf differ diff --git a/lodgeit/res/fonts/luxirbi.ttf b/lodgeit/res/fonts/luxirbi.ttf new file mode 100644 index 0000000..6a5fe68 Binary files /dev/null and b/lodgeit/res/fonts/luxirbi.ttf differ diff --git a/lodgeit/res/fonts/luxirr.ttf b/lodgeit/res/fonts/luxirr.ttf new file mode 100644 index 0000000..daa8ad8 Binary files /dev/null and b/lodgeit/res/fonts/luxirr.ttf differ diff --git a/lodgeit/res/fonts/luxirri.ttf b/lodgeit/res/fonts/luxirri.ttf new file mode 100644 index 0000000..3a0c406 Binary files /dev/null and b/lodgeit/res/fonts/luxirri.ttf differ diff --git a/lodgeit/res/fonts/luxisb.ttf b/lodgeit/res/fonts/luxisb.ttf new file mode 100644 index 0000000..1e1ee43 Binary files /dev/null and b/lodgeit/res/fonts/luxisb.ttf differ diff --git a/lodgeit/res/fonts/luxisbi.ttf b/lodgeit/res/fonts/luxisbi.ttf new file mode 100644 index 0000000..c7072a7 Binary files /dev/null and b/lodgeit/res/fonts/luxisbi.ttf differ diff --git a/lodgeit/res/fonts/luxisr.ttf b/lodgeit/res/fonts/luxisr.ttf new file mode 100644 index 0000000..c47fd20 Binary files /dev/null and b/lodgeit/res/fonts/luxisr.ttf differ diff --git a/lodgeit/res/fonts/luxisri.ttf b/lodgeit/res/fonts/luxisri.ttf new file mode 100644 index 0000000..8a3b012 Binary files /dev/null and b/lodgeit/res/fonts/luxisri.ttf differ diff --git a/lodgeit/res/fonts/zekton__.ttf b/lodgeit/res/fonts/zekton__.ttf new file mode 100644 index 0000000..76bf892 Binary files /dev/null and b/lodgeit/res/fonts/zekton__.ttf differ diff --git a/lodgeit/res/fonts/zektonbi.ttf b/lodgeit/res/fonts/zektonbi.ttf new file mode 100644 index 0000000..c89ecfb Binary files /dev/null and b/lodgeit/res/fonts/zektonbi.ttf differ diff --git a/lodgeit/static/style.css b/lodgeit/static/style.css index 9c2da3f..b131923 100644 --- a/lodgeit/static/style.css +++ b/lodgeit/static/style.css @@ -149,6 +149,21 @@ div.related h3 a { text-decoration: none; } +div.notification div.captcha { + background-color: #222; + margin: 10px; + padding: 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 { background-color: #1d89a4; color: white; diff --git a/lodgeit/urls.py b/lodgeit/urls.py index 83f149d..ea5fc0c 100644 --- a/lodgeit/urls.py +++ b/lodgeit/urls.py @@ -20,6 +20,9 @@ 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 d33ce57..138bdaf 100644 --- a/lodgeit/views/new_paste.html +++ b/lodgeit/views/new_paste.html @@ -2,23 +2,31 @@ {% set page_title = 'New Paste' %} {% set active_page = 'new' %} {% block body %} +
{%- if error %}

Error While Pasting

Could not submit your paste because {{ error|e }}.

+ {%- if show_captcha %} +
+

Please fill out the CAPTCHA to proceed:

+ a captcha you can't see.  Sorry :( + +
+ {%- else %}

hide this message

+ {%- endif %}
{%- endif %} - {% if parent %} {% endif %} diff --git a/runlodgeit.py b/runlodgeit.py index ef8ae6c..f30b043 100644 --- a/runlodgeit.py +++ b/runlodgeit.py @@ -1,18 +1,22 @@ -from lodgeit.application import make_app -from lodgeit.utils import ctx -from lodgeit.database import session +import os + from werkzeug import script from werkzeug.serving import run_simple from werkzeug.utils import create_environ, run_wsgi_app +from lodgeit.application import make_app +from lodgeit.utils import ctx +from lodgeit.database import session + dburi = 'sqlite:////tmp/lodgeit.db' +secret_key = os.urandom(50) def run_app(app, path='/'): - env = create_environ(path) + env = create_environ(path, secret_key) return run_wsgi_app(app, env) action_runserver = script.make_runserver( - lambda: make_app(dburi), + lambda: make_app(dburi, secret_key), use_reloader=True) action_shell = script.make_shell(