|
|
@@ -0,0 +1,184 @@
|
|
|
+#!/usr/bin/python
|
|
|
+import httplib
|
|
|
+import optparse
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import threading
|
|
|
+import urllib
|
|
|
+import urlparse
|
|
|
+
|
|
|
+
|
|
|
+__version__ = (0, 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 = urlparse.urlsplit(url)
|
|
|
+ self._scheme = parse[0]
|
|
|
+ self._netloc = parse[1]
|
|
|
+ self._lock = threading.Semaphore()
|
|
|
+
|
|
|
+ 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 = httplib.HTTPSConnection(self._netloc)
|
|
|
+ else:
|
|
|
+ self._conn = httplib.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.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 = urlparse.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."))
|
|
|
+ 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()
|