pastee 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/python
  2. import httplib
  3. import optparse
  4. import os
  5. import sys
  6. import threading
  7. import urllib
  8. import urlparse
  9. __version__ = (0, 1, 0)
  10. PASTEE_URL = "https://pastee.org"
  11. DEFAULT_LEXER = "text"
  12. DEFAULT_TTL = 30 # days
  13. class Paste:
  14. """Class representing a paste that has been submitted."""
  15. def __init__(self, content, lexer, url):
  16. """Constructor.
  17. Args:
  18. content: paste content
  19. lexer: lexer used for this paste
  20. url: URL to access the paste
  21. """
  22. self.content = content
  23. self.lexer = lexer
  24. self.url = url
  25. def __unicode__(self):
  26. return self.url
  27. def __str__(self):
  28. return str(self.__unicode__)
  29. class PasteClient:
  30. """Pasting client for a Pastee application.
  31. Instances of this class can be used to programmatically create new pastes on
  32. an installation of Pastee (https://pastee.org).
  33. This class is thread-safe.
  34. """
  35. def __init__(self, url=PASTEE_URL):
  36. """Constructor.
  37. Args:
  38. url: URL to Pastee installation (defaults to https://pastee.org)
  39. """
  40. parse = urlparse.urlsplit(url)
  41. self._scheme = parse[0]
  42. self._netloc = parse[1]
  43. self._lock = threading.Semaphore()
  44. def paste(self, content, lexer=None, ttl=None, key=None):
  45. """Create a new paste.
  46. Args:
  47. content: string of text to paste
  48. lexer: lexer to use (defaults to text)
  49. ttl: time-to-live in days (defaults to 30)
  50. key: encrypt paste with this key; if not specified, paste is not
  51. encrypted
  52. Returns:
  53. Paste object
  54. """
  55. if lexer is None:
  56. lexer = DEFAULT_LEXER
  57. if ttl is None:
  58. ttl = DEFAULT_TTL
  59. if self._scheme == "https":
  60. self._conn = httplib.HTTPSConnection(self._netloc)
  61. else:
  62. self._conn = httplib.HTTPConnection(self._netloc)
  63. headers = {"Content-type": "application/x-www-form-urlencoded",
  64. "Accept": "text/plain"}
  65. params = {"lexer": lexer,
  66. "content": content,
  67. "ttl": int(ttl * 86400)}
  68. if key is not None:
  69. params["encrypt"] = "checked"
  70. params["key"] = key
  71. self._lock.acquire()
  72. self._conn.request("POST", "/submit", urllib.urlencode(params), headers)
  73. response = self._conn.getresponse()
  74. self._lock.release()
  75. return self._make_paste(response, content, lexer)
  76. def paste_file(self, filename, lexer=None, ttl=None, key=None):
  77. """Create a new paste from a file.
  78. Args:
  79. filename: path to file
  80. lexer: lexer to use (defaults to extension of the file or text)
  81. ttl: time-to-live in days (defaults to 30)
  82. key: encrypt paste with this key; if not specified, paste is not
  83. encrypted
  84. Returns:
  85. Paste object
  86. """
  87. _, ext = os.path.splitext(filename)
  88. if lexer is None and ext:
  89. lexer = ext[1:] # remove leading period first
  90. # TODO(ms): need exception handling here
  91. fd = open(filename, "r")
  92. content = fd.read()
  93. fd.close()
  94. return self.paste(content, lexer=lexer, ttl=ttl, key=key)
  95. def _make_paste(self, response, content, lexer):
  96. for (key, value) in response.getheaders():
  97. if key.lower() == "location":
  98. return self._clean_url(value)
  99. return Paste(content, lexer, self._clean_url(value))
  100. def _clean_url(self, url):
  101. p = urlparse.urlsplit(url)
  102. scheme = p[0]
  103. netloc_split = p[1].split(":")
  104. hostname = netloc_split[0]
  105. if len(netloc_split) > 1:
  106. port = int(netloc_split[1])
  107. else:
  108. port = scheme == "https" and 443 or 80
  109. path = p[2]
  110. port_str = ""
  111. if port != 80 and scheme == "http":
  112. port_str = ":%d" % port
  113. elif port != 443 and scheme == "https":
  114. port_str = ":%d" % port
  115. return "%s://%s%s%s" % (scheme, hostname, port_str, path)
  116. def die_with_error(message):
  117. """Print a message and exit with exit code 1.
  118. Args:
  119. message: message to print before exiting
  120. """
  121. print "error: %s" % message
  122. sys.exit(1)
  123. def main():
  124. parser = optparse.OptionParser()
  125. parser.add_option("-l", "--lexer", dest="lexer", metavar="LEXERNAME",
  126. help=("Force use of a particular lexer (i.e. c, py). "
  127. "This defaults to the extension of the supplied "
  128. "filenames, or 'text' if pasting from stdin."))
  129. parser.add_option("-t", "--ttl", dest="ttl", metavar="DAYS",
  130. help=("Number of days before the paste will expire."))
  131. parser.add_option("-k", "--key", dest="key", metavar="PASSPHRASE",
  132. help=("Encrypt pastes with this key."))
  133. (options, filenames) = parser.parse_args()
  134. lexer = options.lexer
  135. key = options.key
  136. try:
  137. ttl = float(options.ttl)
  138. except ValueError:
  139. die_with_error("floating point number must be passed for TTL")
  140. except TypeError:
  141. ttl = None
  142. client = PasteClient()
  143. if filenames:
  144. # paste from multiple files
  145. for filename in filenames:
  146. print client.paste_file(filename, lexer=lexer, ttl=ttl, key=key)
  147. else:
  148. # paste from stdin
  149. print client.paste(sys.stdin.read(), lexer=lexer, ttl=ttl, key=key)
  150. if __name__ == "__main__":
  151. main()