import cgi try: from http.cookies import SimpleCookie except ImportError: from Cookie import SimpleCookie import datetime import db import mimetypes import os import random import settings import jinja2 try: import markdown has_markdown = True except ImportError: has_markdown = False import pygments from pygments import highlight from pygments.lexers import get_all_lexers, get_lexer_by_name from pygments.formatters import HtmlFormatter import six base62_alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' def b2t(s): '''Convert binary types to text types.''' if isinstance(s, six.binary_type): return s.decode('utf-8') return s def t2b(s): '''Convert text types to binary types.''' if isinstance(s, six.text_type): return s.encode('utf-8') return s class PasteError(Exception): pass class UnknownSyntaxError(PasteError): pass class CustomHtmlFormatter(HtmlFormatter): def wrap(self, source, outfile): yield 0, '
'
		line = 1
		for i, t in source:
			yield i, '%s' % (line, t)
			line += 1
		yield 0, '
' class Paste(object): def __init__(self): lexers = dict([(x[0], x[1][0]) for x in get_all_lexers()]) self.lexers = [] removed = [] for cat, ls in settings.categories: for l in ls: removed.append(l) self.lexers.append((cat, [(x, lexers[x]) for x in ls])) for l in removed: try: del lexers[l] except KeyError: pass rendered = [] if has_markdown: rendered.append(('Rendered markdown', 'md-render')) if len(rendered): self.lexers.append(('Rendered', rendered)) self.lexers.append(('Others' if settings.categories else 'Syntax', sorted(lexers.items(), key = lambda l: l[0].lower()))) self.formatter = CustomHtmlFormatter(linenos = 'table', lineanchors = 'line', anchorlinenos = True) random.seed() self.template_env = jinja2.Environment(loader = jinja2.FileSystemLoader('templates')) def render_template(self, name, args): template = self.template_env.get_template(name) data = template.render(**args).encode('utf-8') self.start_response('200 OK', [('Content-Type', 'text/html; charset=UTF-8'), ('Content-Length', str(len(data)))]) return [data] def message(self, msg, title = 'Message'): return self.render_template('message.html', { 'title': '%s – %s' % (settings.pastebin_name, title), 'header': title, 'text': msg, }) def paste(self): c = SimpleCookie(self.environ['HTTP_COOKIE'] if 'HTTP_COOKIE' in self.environ else None) if self.environ['REQUEST_METHOD'] == 'POST': mp = cgi.FieldStorage(fp = self.environ['wsgi.input'], environ = self.environ, keep_blank_values = True) if mp['type'].value == 'Preview': return self.preview(mp) elif mp['type'].value == 'Paste': return self.add_paste(mp) else: return self.message('Invalid type "%s".' % mp['type'].value, 'Error') return self.render_template('paste.html', { 'title': settings.pastebin_name, 'header': settings.pastebin_name, 'lexers': self.lexers, 'nick': b2t(c['nick'].value if 'nick' in c else 'Anonymous'), 'syntax': c['syntax'].value if 'syntax' in c else settings.default, 'remember_me': 'nick' in c, 'remember_syntax': 'syntax' in c, }) def preview(self, mp): try: lexername, text = self.get_formatted(mp['syntax'].value, mp['text'].value) except UnknownSyntaxError: return self.message('Could not find lexer "%s".' % mp['syntax'].value, 'Error') # These values to be unicode in py2; in py3 they will be str which is OK. nick = b2t(mp['nick'].value or 'Anonymous') title = b2t(mp['title'].value or 'Untitled') return self.render_template('view.html', { 'title': settings.pastebin_name, 'header': '%s – Preview' % settings.pastebin_name, 'hash': None, 'nick': nick, 'date': '%s UTC' % datetime.datetime.utcnow().ctime(), 'syntax': lexername, 'pastetitle': title, 'text': text, 'rendered': (lexername or '').startswith('Rendered '), }) def add_paste(self, mp): nick = b2t(mp['nick'].value or None) syntax = b2t(mp['syntax'].value or None) title = b2t(mp['title'].value or None) text = b2t(mp['text'].value.replace('\r', '') or None) hash = ''.join(random.choice(base62_alphabet) for x in range(5)) try: session = db.Session() paste = db.Paste(hash, nick, datetime.datetime.utcnow(), syntax, title, text, self.environ['REMOTE_ADDR']) session.add(paste) session.commit() finally: session.close() headers = [('Location', '/view/%s' % hash)] c = SimpleCookie() if six.PY2: # In py2 we need nick to be a str, or non-ascii characters will fail with UnicodeDecodeError. # In py3 it must be a str or we will get garbage in the cookie value. nick = t2b(nick) c['nick'] = nick dt = (datetime.datetime.utcnow() + datetime.timedelta(days = 30)) if 'remember_me' in mp else datetime.datetime.utcfromtimestamp(0) c['nick']['expires'] = dt.strftime('%a, %d-%b-%y %H:%M:%S GMT') c['syntax'] = syntax dt = (datetime.datetime.utcnow() + datetime.timedelta(days = 30)) if 'remember_syntax' in mp else datetime.datetime.utcfromtimestamp(0) c['syntax']['expires'] = dt.strftime('%a, %d-%b-%y %H:%M:%S GMT') headers.append(('Set-Cookie', c['nick'].OutputString())) headers.append(('Set-Cookie', c['syntax'].OutputString())) self.start_response('302 Found', headers) return [] def get_formatted(self, syntax, text): if syntax == 'md-render' and has_markdown: text = markdown.markdown(text, extensions = settings.markdown_extensions) lexername = 'Rendered markdown' else: try: lex = get_lexer_by_name(syntax or 'text') except pygments.util.ClassNotFound: raise UnknownSyntaxError(syntax) lexername = lex.name text = highlight(text, lex, self.formatter) return (lexername, text) def view(self): hash = self.path[1] try: session = db.Session() try: cache = session.query(db.Cache).filter_by(paste_hash = hash).one() paste = cache.paste except db.NoResultFound: # No cache found, generate it. try: paste = session.query(db.Paste).filter_by(hash = hash).one() except db.NoResultFound: self.start_response('404 Not Found', []) return [] try: lexername, text = self.get_formatted(paste.syntax, paste.text) except UnknownSyntaxError: return self.message('Could not find the lexer "%s".' % paste.syntax, 'Error') cache = db.Cache(hash, lexername, text) session.add(cache) session.commit() # Workaround for attribute refresh. paste = cache.paste finally: session.close() return self.render_template('view.html', { 'title': '%s – View paste – %s' % (settings.pastebin_name, paste.title or 'Untitled'), 'header': '%s – View paste' % settings.pastebin_name, 'hash': hash, 'date': '%s UTC' % paste.date.ctime(), 'nick': b2t(paste.nick or 'Anonymous'), 'syntax': cache.syntax_name, 'pastetitle': b2t(paste.title or 'Untitled'), 'text': b2t(cache.text), 'rendered': (cache.syntax_name or '').startswith('Rendered '), }) def raw(self): hash = self.path[1] try: session = db.Session() paste = session.query(db.Paste).filter_by(hash = hash).one() finally: session.close() self.start_response('200 OK', [('Content-Type', 'text/plain; charset=UTF-8')]) return [t2b(paste.text)] def highlight_stylesheet(self): self.start_response('200 OK', [('Content-Type', 'text/css')]) return [t2b(self.formatter.get_style_defs())] def static(self): filename = settings.static_root + os.path.sep + self.path[1] if not self.path[1] in ('paste.css', 'edit.js', 'view.js') or not os.path.exists(filename): self.start_response('404 Not Found', [('Content-Type', 'text/html; charset=UTF-8'), ('Location', '/')]) return ['asdf'] self.start_response('200 OK', [('Content-Type', mimetypes.guess_type(filename)[0] or 'text/plain; charset=UTF-8')]) return open(filename, 'rb') def __call__(self, environ, start_response): self.environ = environ self.start_response = start_response path = self.environ['PATH_INFO'].split('/')[1:] module = path[0] or 'paste' if module in ('list', 'paste', 'view', 'raw', 'static', 'highlight_stylesheet'): self.path = path return getattr(self, module)() else: return self.message('Invalid module requested.', 'Error') if __name__ == '__main__': from wsgiref.simple_server import make_server httpd = make_server('', 8000, Paste()) httpd.serve_forever() # vim: noet ts=4