The Dod
10 years ago
1 changed files with 209 additions and 0 deletions
@ -0,0 +1,209 @@
@@ -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 "</pre></div>". |
||||
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'<div class="syntax"><pre>(.*)</pre></div>', |
||||
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() |
Loading…
Reference in new issue