]>
Commit | Line | Data |
---|---|---|
a9f52ec5 IB |
1 | import os |
2 | import secrets | |
3 | from flask import Flask, abort, request, Response, url_for | |
4 | from pygments.formatters.html import HtmlFormatter | |
5 | from pygments import highlight | |
6 | import pygments.lexers as lexers | |
7 | import base64 | |
8 | import magic | |
9 | import mimetypes | |
10 | ||
11 | magic = magic.Magic(mime=True) | |
12 | ||
1a64deeb IB |
13 | mit_license = """ |
14 | Copyright (c) 2022 Immae | |
15 | ||
16 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
17 | of this software and associated documentation files (the "Software"), to deal | |
18 | in the Software without restriction, including without limitation the rights | |
19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
20 | copies of the Software, and to permit persons to whom the Software is | |
21 | furnished to do so, subject to the following conditions: | |
22 | ||
23 | The above copyright notice and this permission notice shall be included in all | |
24 | copies or substantial portions of the Software. | |
25 | ||
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
32 | SOFTWARE. | |
33 | """ | |
34 | ||
a9f52ec5 IB |
35 | config = { |
36 | "directory": os.environ["PASTE_DIRECTORY"], | |
37 | "self_paste_id": "abcd123", | |
1a64deeb | 38 | "license_paste_id": "license", |
a9f52ec5 IB |
39 | "max_content_length": 16 * 1000 * 1000 |
40 | } | |
41 | ||
42 | app = Flask(__name__) | |
43 | app.config['MAX_CONTENT_LENGTH'] = config["max_content_length"] | |
44 | ||
45 | def file_location(paste_id): | |
46 | if paste_id == config["self_paste_id"]: | |
47 | return os.path.realpath(__file__) | |
48 | else: | |
49 | return os.path.join(config['directory'], paste_id + ".dat") | |
50 | ||
51 | def read_paste(paste_id): | |
52 | file = file_location(paste_id) | |
53 | if os.path.isfile(file): | |
54 | content = open(file, "rb").read() | |
55 | mime = magic.from_buffer(content) | |
56 | if mime.startswith("text/x-script."): | |
57 | mime="text/plain" | |
58 | return (content, mime) | |
1a64deeb IB |
59 | elif paste_id == config["license_paste_id"]: |
60 | return (mit_license, "text/plain") | |
a9f52ec5 IB |
61 | else: |
62 | abort(404) | |
63 | ||
64 | def generate_paste_id(n=3, attempts=0): | |
65 | path = secrets.token_hex(n)[:n] | |
66 | file = file_location(path) | |
67 | if os.path.isfile(file): | |
68 | attempts = attempts + 1 | |
69 | if attempts > 5: | |
70 | n = n + 1 | |
71 | return generate_paste_id(n, attempts) | |
72 | return path | |
73 | ||
74 | @app.route('/', methods=["GET"]) | |
75 | def index(): | |
76 | return Response('''<pre> | |
77 | $ curl -X POST --data-binary @{self} {host} | |
78 | {paste} | |
79 | ||
80 | -> GET {paste} | |
81 | guesses mimetype | |
82 | -> GET {paste}/raw | |
83 | text/plain | |
84 | -> GET {paste}/bin | |
85 | application/octet-stream | |
86 | -> GET {paste}/b64 | |
87 | base64 encoded | |
88 | -> GET {paste}/ub64 | |
89 | tries to decode base64 | |
90 | -> GET {paste}/python | |
91 | pretty-print python (replace with anything known by pygments) | |
92 | -> GET {paste}/guess | |
93 | pretty-print (language guessed by pygments) | |
94 | -> GET {paste}/download | |
95 | force download of file | |
96 | </pre> | |
1a64deeb IB |
97 | <a href="{paste}/py">Get the source</a><br /> |
98 | Software licensed under the terms of the <a href="{host}/license">MIT license</a> | |
a9f52ec5 IB |
99 | '''.format(host=url_for('post_paste', _external=True, _scheme="https"), |
100 | paste=url_for('get_paste', _external=True, _scheme="https", paste_id=config["self_paste_id"]), | |
1a64deeb | 101 | license=url_for('get_paste', _external=True, _scheme="https", paste_id=config["license_paste_id"]), |
a9f52ec5 IB |
102 | self=os.path.basename(__file__) |
103 | ), mimetype="text/html") | |
104 | ||
105 | @app.route('/', methods=["POST"]) | |
106 | def post_paste(): | |
107 | content = request.get_data() | |
108 | paste_id = generate_paste_id() | |
109 | with open(file_location(paste_id), "wb") as f: | |
110 | f.write(content) | |
111 | return url_for('get_paste', _external=True, _scheme="https", paste_id=paste_id) + "\n" | |
112 | ||
113 | ||
114 | @app.route('/<paste_id>', methods=["GET"]) | |
115 | def get_paste(paste_id): | |
116 | content, mime = read_paste(paste_id) | |
117 | return Response(content, mimetype=mime) | |
118 | ||
119 | @app.route('/<paste_id>/<lang>', methods=["GET"]) | |
120 | def get_paste_with(paste_id, lang): | |
121 | content, mime = read_paste(paste_id) | |
122 | if lang == "raw": | |
123 | return Response(content, mimetype="text/plain") | |
124 | elif lang == "bin": | |
125 | return Response(content, mimetype="application/octet-stream") | |
126 | elif lang == "b64": | |
127 | return Response(base64.encodebytes(content), mimetype="text/plain") | |
128 | elif lang == "download": | |
129 | extension = mimetypes.guess_extension(mime, strict=False) | |
130 | if extension is None: | |
131 | cd = "attachment" | |
132 | else: | |
133 | cd = 'attachment; filename="{}{}"'.format(paste_id, extension) | |
134 | return Response(content, mimetype=mime, | |
135 | headers={"Content-Disposition": cd}) | |
136 | elif lang == "ub64": | |
137 | try: | |
138 | return base64.b64decode(content) | |
139 | except: | |
140 | abort(400) | |
141 | try: | |
142 | if lang == "guess": | |
143 | lexer = lexers.guess_lexer(content) | |
144 | else: | |
145 | lexer = lexers.find_lexer_class_by_name(lang)() | |
146 | except: | |
147 | abort(400) | |
148 | fmter = HtmlFormatter( | |
149 | noclasses=True, full=True, style="colorful", linenos="table") | |
150 | ||
151 | return Response(highlight(content, lexer, fmter), mimetype="text/html") |