#!/usr/bin/env python3 ## Based on https://pastee.org/api with 2 modifications by thedod: ## 1. Standard 2to3 run ## 2. Added sloppy_get() [use at your own risk] despite advice against ## such practices at http://stackoverflow.com/a/1732454 import http.client import optparse import os import sys import threading import urllib.request, urllib.parse, urllib.error import urllib.parse import re import html __version__ = (3, 1, 0) PASTEE_URL = "https://pastee.org" DEFAULT_LEXER = "text" DEFAULT_TTL = 30 # days class Paste: """Class representing a paste that has been submitted.""" def __init__(self, content, lexer, url): """Constructor. Args: content: paste content lexer: lexer used for this paste url: URL to access the paste """ self.content = content self.lexer = lexer self.url = url def __unicode__(self): return self.url def __str__(self): return str(self.__unicode__) class PasteClient: """Pasting client for a Pastee application. Instances of this class can be used to programmatically create new pastes on an installation of Pastee (https://pastee.org). This class is thread-safe. """ def __init__(self, url=PASTEE_URL): """Constructor. Args: url: URL to Pastee installation (defaults to https://pastee.org) """ parse = urllib.parse.urlsplit(url) self._scheme = parse[0] self._netloc = parse[1] self._lock = threading.Semaphore() def sloppy_get(self,paste_id): """Assumes a paste with no lexer that doesn't contain "". Sometimes you're *sure* it won't ;)""" if self._scheme == "https": self._conn = http.client.HTTPSConnection(self._netloc) else: self._conn = http.client.HTTPConnection(self._netloc) self._conn.request('GET','/{}'.format(paste_id)) try: response = self._conn.getresponse() return html.unescape( re.findall( b'
(.*)
', response.read(), re.MULTILINE|re.DOTALL )[0].decode('unicode_escape')) except: pass # Todo: error handling :p return None def paste(self, content, lexer=None, ttl=None, key=None): """Create a new paste. Args: content: string of text to paste lexer: lexer to use (defaults to text) ttl: time-to-live in days (defaults to 30) key: encrypt paste with this key; if not specified, paste is not encrypted Returns: Paste object """ if lexer is None: lexer = DEFAULT_LEXER if ttl is None: ttl = DEFAULT_TTL if self._scheme == "https": self._conn = http.client.HTTPSConnection(self._netloc) else: self._conn = http.client.HTTPConnection(self._netloc) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} params = {"lexer": lexer, "content": content, "ttl": int(ttl * 86400)} if key is not None: params["encrypt"] = "checked" params["key"] = key self._lock.acquire() self._conn.request("POST", "/submit", urllib.parse.urlencode(params), headers) response = self._conn.getresponse() self._lock.release() return self._make_paste(response, content, lexer) def paste_file(self, filename, lexer=None, ttl=None, key=None): """Create a new paste from a file. Args: filename: path to file lexer: lexer to use (defaults to extension of the file or text) ttl: time-to-live in days (defaults to 30) key: encrypt paste with this key; if not specified, paste is not encrypted Returns: Paste object """ _, ext = os.path.splitext(filename) if lexer is None and ext: lexer = ext[1:] # remove leading period first # TODO(ms): need exception handling here fd = open(filename, "r") content = fd.read() fd.close() return self.paste(content, lexer=lexer, ttl=ttl, key=key) def _make_paste(self, response, content, lexer): for (key, value) in response.getheaders(): if key.lower() == "location": return self._clean_url(value) return Paste(content, lexer, self._clean_url(value)) def _clean_url(self, url): p = urllib.parse.urlsplit(url) scheme = p[0] netloc_split = p[1].split(":") hostname = netloc_split[0] if len(netloc_split) > 1: port = int(netloc_split[1]) else: port = scheme == "https" and 443 or 80 path = p[2] port_str = "" if port != 80 and scheme == "http": port_str = ":%d" % port elif port != 443 and scheme == "https": port_str = ":%d" % port return "%s://%s%s%s" % (scheme, hostname, port_str, path) def die_with_error(message): """Print a message and exit with exit code 1. Args: message: message to print before exiting """ print("error: %s" % message) sys.exit(1) def main(): parser = optparse.OptionParser() parser.add_option("-l", "--lexer", dest="lexer", metavar="LEXERNAME", help=("Force use of a particular lexer (i.e. c, py). " "This defaults to the extension of the supplied " "filenames, or 'text' if pasting from stdin.")) parser.add_option("-t", "--ttl", dest="ttl", metavar="DAYS", help=("Number of days before the paste will expire.")) ## I'd rather not promote server side crypto. Sorry. #parser.add_option("-k", "--key", dest="key", metavar="PASSPHRASE", # help=("Encrypt pastes with this key.")) (options, filenames) = parser.parse_args() lexer = options.lexer key = options.key try: ttl = float(options.ttl) except ValueError: die_with_error("floating point number must be passed for TTL") except TypeError: ttl = None client = PasteClient() if filenames: # paste from multiple files for filename in filenames: print(client.paste_file(filename, lexer=lexer, ttl=ttl, key=key)) else: # paste from stdin print(client.paste(sys.stdin.read(), lexer=lexer, ttl=ttl, key=key)) if __name__ == "__main__": main()