diff --git a/pastee3.py b/pastee3.py
new file mode 100755
index 0000000..a86502e
--- /dev/null
+++ b/pastee3.py
@@ -0,0 +1,209 @@
+#!/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()