|
1 | 1 | #!/usr/bin/env python3
|
2 | 2 | # Project: hastebin
|
3 |
| -# Author: kevr <kevin.morris@codestruct.net> |
| 3 | +# Author: Kevin Morris <kevr@0cost.org> |
4 | 4 | # Description: A tool which uploads data from stdin to hastebin.com
|
5 |
| -# Copyright (C) 2017 Kevin Morris |
| 5 | +# Copyright (C) 2017 Kevin Morris <kevr@0cost.org> |
6 | 6 | # Copyright (C) 2020 Denis Zheleztsov <difrex@lessmore.pw>
|
7 |
| - |
8 | 7 | import sys
|
9 | 8 | import os
|
10 | 9 | import requests
|
11 | 10 | import json
|
12 | 11 | from select import select
|
| 12 | +import argparse |
| 13 | +from subprocess import Popen |
13 | 14 |
|
14 |
| - |
15 |
| -# Hastebin server url |
16 | 15 | url = os.environ.get("HASTEBIN_SERVER_URL", "https://hastebin.com")
|
17 |
| -# Connection timeout |
18 | 16 | timeout = int(os.environ.get("HASTEBIN_SERVER_TIMEOUT", 5))
|
19 | 17 |
|
20 |
| - |
21 | 18 | def has_data(fd):
|
22 | 19 | """Immediately timed out select poll"""
|
23 | 20 | return select([fd], [], [], 0.0) == ([fd], [], [])
|
24 | 21 |
|
25 |
| - |
26 | 22 | def quit(code, msg):
|
27 | 23 | """Print message to the STDERR and returns exit code"""
|
28 | 24 | sys.stderr.write(msg + "\n")
|
29 | 25 | return code
|
30 | 26 |
|
| 27 | +def get_paste(id): |
| 28 | + paste_url = f"{url}/raw/{id}" |
| 29 | + response = requests.get(paste_url, timeout=timeout) |
31 | 30 |
|
32 |
| -## Sorry for the C-style functions, I prefer them |
33 |
| -def main(): |
34 |
| - if not has_data(sys.stdin): |
35 |
| - return quit(1, "hastebin: no data given via stdin") |
| 31 | + if response.status_code != requests.codes.ok: |
| 32 | + return 3, "error: bad response from '{}'".format(paste_url) |
36 | 33 |
|
37 |
| - try: |
38 |
| - stdin = sys.stdin.read() |
39 |
| - except UnicodeDecodeError: |
40 |
| - return quit(2, "hastebin: an error occured reading stdin") |
41 |
| - |
| 34 | + return 0, response.content.decode().rstrip() |
| 35 | + |
| 36 | +def post_paste(text): |
42 | 37 | response = requests.post("%s/documents" % url, headers={
|
43 |
| - "Accept": "application/json"}, data=stdin) |
| 38 | + "Accept": "application/json"}, data=text, |
| 39 | + timeout=timeout) |
| 40 | + |
44 | 41 | if response.status_code != requests.codes.ok:
|
45 |
| - return quit(3, "hastebin: error submitting POST data to hastebin") |
| 42 | + return 3, "error: bad response from '{}'".format(paste_url) |
| 43 | + |
| 44 | + return 0, response.content.decode().rstrip() |
46 | 45 |
|
47 |
| - content = response.content.decode("UTF-8").rstrip() |
48 |
| - data = json.loads(content) |
| 46 | +def main(): |
| 47 | + help_description = "Upload text from stdin to HASTEBIN_SERVER_URL. If " |
| 48 | + help_description += "[id] is provided, the corresponding paste is fetched " |
| 49 | + help_description += "and displayed." |
| 50 | + parser = argparse.ArgumentParser(description=help_description) |
| 51 | + parser.add_argument("--clip-command", "-cc", |
| 52 | + metavar="clip_command", |
| 53 | + default="xclip -sel primary", |
| 54 | + help="clipboard pipe command (default: 'xclip -sel primary')") |
| 55 | + parser.add_argument("--clipboard", "-c", |
| 56 | + metavar="clipboard", |
| 57 | + action="store_const", |
| 58 | + const=True, |
| 59 | + default=False, |
| 60 | + help="pipe stdout to --clip-command") |
| 61 | + parser.add_argument("id", |
| 62 | + nargs="?", |
| 63 | + help="when provided, fetches and displays a hastebin paste") |
| 64 | + args = parser.parse_args() |
| 65 | + |
| 66 | + if args.id is not None: |
| 67 | + if args.id[:4] == "http" and args.id[:len(url)] != url: |
| 68 | + return quit(1, |
| 69 | + f"error: invalid id provided; URLs must begin with '{url}'") |
| 70 | + |
| 71 | + paste_id = args.id.split("/")[-1] |
| 72 | + if not paste_id: |
| 73 | + return quit(1, "error: no id provided") |
| 74 | + |
| 75 | + return_code, response = get_paste(paste_id) |
| 76 | + if return_code != 0: |
| 77 | + return quit(return_code, response) |
| 78 | + print(response) |
| 79 | + else: |
| 80 | + if not has_data(sys.stdin): |
| 81 | + return quit(1, "error: no data given via stdin") |
| 82 | + |
| 83 | + try: |
| 84 | + stdin = sys.stdin.read() |
| 85 | + except UnicodeDecodeError: |
| 86 | + return quit(2, "error: an error occured reading stdin") |
| 87 | + |
| 88 | + return_code, response = post_paste(stdin) |
| 89 | + if return_code != 0: |
| 90 | + return quit(return_code, response) |
| 91 | + |
| 92 | + data = json.loads(response) |
| 93 | + uri = f'{url}/{data["key"]}' |
| 94 | + print(uri) |
49 | 95 |
|
50 |
| - print("%s/%s" % (url, data["key"])) |
| 96 | + if args.clipboard: |
| 97 | + proc = Popen([ |
| 98 | + "/bin/sh", "-c", |
| 99 | + f'echo -n "{uri}" | {args.clip_command}' |
| 100 | + ]) |
| 101 | + proc.wait() |
51 | 102 |
|
52 | 103 | return 0
|
53 | 104 |
|
54 |
| - |
55 | 105 | # main execution
|
56 | 106 | if __name__ == "__main__":
|
57 | 107 | e = main()
|
|
0 commit comments