onionshare_cli 2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- onionshare_cli/__init__.py +522 -0
- onionshare_cli/common.py +296 -0
- onionshare_cli/mode_settings.py +146 -0
- onionshare_cli/onion.py +845 -0
- onionshare_cli/onionshare.py +110 -0
- onionshare_cli/resources/static/css/style.css +393 -0
- onionshare_cli/resources/static/img/ajax.gif +0 -0
- onionshare_cli/resources/static/img/favicon.ico +0 -0
- onionshare_cli/resources/static/img/logo.png +0 -0
- onionshare_cli/resources/static/img/logo_large.png +0 -0
- onionshare_cli/resources/static/img/web_file.png +0 -0
- onionshare_cli/resources/static/img/web_folder.png +0 -0
- onionshare_cli/resources/static/js/chat.js +173 -0
- onionshare_cli/resources/static/js/jquery-3.5.1.min.js +2 -0
- onionshare_cli/resources/static/js/receive.js +130 -0
- onionshare_cli/resources/static/js/send.js +75 -0
- onionshare_cli/resources/static/js/socket.io.min.js +3 -0
- onionshare_cli/resources/templates/401.html +21 -0
- onionshare_cli/resources/templates/403.html +21 -0
- onionshare_cli/resources/templates/404.html +21 -0
- onionshare_cli/resources/templates/405.html +21 -0
- onionshare_cli/resources/templates/chat.html +46 -0
- onionshare_cli/resources/templates/denied.html +14 -0
- onionshare_cli/resources/templates/listing.html +59 -0
- onionshare_cli/resources/templates/receive.html +46 -0
- onionshare_cli/resources/templates/send.html +69 -0
- onionshare_cli/resources/templates/thankyou.html +27 -0
- onionshare_cli/resources/torrc_template +8 -0
- onionshare_cli/resources/torrc_template-meek_lite_amazon +2 -0
- onionshare_cli/resources/torrc_template-meek_lite_azure +2 -0
- onionshare_cli/resources/torrc_template-obfs4 +27 -0
- onionshare_cli/resources/version.txt +1 -0
- onionshare_cli/resources/wordlist.txt +7776 -0
- onionshare_cli/settings.py +201 -0
- onionshare_cli/web/__init__.py +21 -0
- onionshare_cli/web/chat_mode.py +169 -0
- onionshare_cli/web/receive_mode.py +488 -0
- onionshare_cli/web/send_base_mode.py +338 -0
- onionshare_cli/web/share_mode.py +406 -0
- onionshare_cli/web/web.py +424 -0
- onionshare_cli/web/website_mode.py +123 -0
- onionshare_cli-2.3.dist-info/METADATA +34 -0
- onionshare_cli-2.3.dist-info/RECORD +45 -0
- onionshare_cli-2.3.dist-info/WHEEL +4 -0
- onionshare_cli-2.3.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
OnionShare | https://onionshare.org/
|
|
4
|
+
|
|
5
|
+
Copyright (C) 2014-2020 Micah Lee, et al. <micah@micahflee.com>
|
|
6
|
+
|
|
7
|
+
This program is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
This program is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU General Public License
|
|
18
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import tempfile
|
|
23
|
+
import json
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
from flask import Request, request, render_template, make_response, flash, redirect
|
|
26
|
+
from werkzeug.utils import secure_filename
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ReceiveModeWeb:
|
|
30
|
+
"""
|
|
31
|
+
All of the web logic for receive mode
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, common, web):
|
|
35
|
+
self.common = common
|
|
36
|
+
self.common.log("ReceiveModeWeb", "__init__")
|
|
37
|
+
|
|
38
|
+
self.web = web
|
|
39
|
+
|
|
40
|
+
self.can_upload = True
|
|
41
|
+
self.uploads_in_progress = []
|
|
42
|
+
|
|
43
|
+
# This tracks the history id
|
|
44
|
+
self.cur_history_id = 0
|
|
45
|
+
|
|
46
|
+
self.define_routes()
|
|
47
|
+
|
|
48
|
+
def define_routes(self):
|
|
49
|
+
"""
|
|
50
|
+
The web app routes for receiving files
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
@self.web.app.route("/")
|
|
54
|
+
def index():
|
|
55
|
+
history_id = self.cur_history_id
|
|
56
|
+
self.cur_history_id += 1
|
|
57
|
+
self.web.add_request(
|
|
58
|
+
self.web.REQUEST_INDIVIDUAL_FILE_STARTED,
|
|
59
|
+
request.path,
|
|
60
|
+
{"id": history_id, "status_code": 200},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.web.add_request(self.web.REQUEST_LOAD, request.path)
|
|
64
|
+
r = make_response(
|
|
65
|
+
render_template(
|
|
66
|
+
"receive.html", static_url_path=self.web.static_url_path
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
return self.web.add_security_headers(r)
|
|
70
|
+
|
|
71
|
+
@self.web.app.route("/upload", methods=["POST"])
|
|
72
|
+
def upload(ajax=False):
|
|
73
|
+
"""
|
|
74
|
+
Handle the upload files POST request, though at this point, the files have
|
|
75
|
+
already been uploaded and saved to their correct locations.
|
|
76
|
+
"""
|
|
77
|
+
files = request.files.getlist("file[]")
|
|
78
|
+
filenames = []
|
|
79
|
+
for f in files:
|
|
80
|
+
if f.filename != "":
|
|
81
|
+
filename = secure_filename(f.filename)
|
|
82
|
+
filenames.append(filename)
|
|
83
|
+
local_path = os.path.join(request.receive_mode_dir, filename)
|
|
84
|
+
basename = os.path.basename(local_path)
|
|
85
|
+
|
|
86
|
+
# Tell the GUI the receive mode directory for this file
|
|
87
|
+
self.web.add_request(
|
|
88
|
+
self.web.REQUEST_UPLOAD_SET_DIR,
|
|
89
|
+
request.path,
|
|
90
|
+
{
|
|
91
|
+
"id": request.history_id,
|
|
92
|
+
"filename": basename,
|
|
93
|
+
"dir": request.receive_mode_dir,
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
self.common.log(
|
|
98
|
+
"ReceiveModeWeb",
|
|
99
|
+
"define_routes",
|
|
100
|
+
f"/upload, uploaded {f.filename}, saving to {local_path}",
|
|
101
|
+
)
|
|
102
|
+
print(f"\nReceived: {local_path}")
|
|
103
|
+
|
|
104
|
+
if request.upload_error:
|
|
105
|
+
self.common.log(
|
|
106
|
+
"ReceiveModeWeb",
|
|
107
|
+
"define_routes",
|
|
108
|
+
"/upload, there was an upload error",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
self.web.add_request(
|
|
112
|
+
self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE,
|
|
113
|
+
request.path,
|
|
114
|
+
{"receive_mode_dir": request.receive_mode_dir},
|
|
115
|
+
)
|
|
116
|
+
print(
|
|
117
|
+
f"Could not create OnionShare data folder: {request.receive_mode_dir}"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
msg = "Error uploading, please inform the OnionShare user"
|
|
121
|
+
if ajax:
|
|
122
|
+
return json.dumps({"error_flashes": [msg]})
|
|
123
|
+
else:
|
|
124
|
+
flash(msg, "error")
|
|
125
|
+
return redirect("/")
|
|
126
|
+
|
|
127
|
+
if ajax:
|
|
128
|
+
info_flashes = []
|
|
129
|
+
|
|
130
|
+
if len(filenames) == 0:
|
|
131
|
+
msg = "No files uploaded"
|
|
132
|
+
if ajax:
|
|
133
|
+
info_flashes.append(msg)
|
|
134
|
+
else:
|
|
135
|
+
flash(msg, "info")
|
|
136
|
+
else:
|
|
137
|
+
msg = "Sent "
|
|
138
|
+
for filename in filenames:
|
|
139
|
+
msg += f"{filename}, "
|
|
140
|
+
msg = msg.rstrip(", ")
|
|
141
|
+
if ajax:
|
|
142
|
+
info_flashes.append(msg)
|
|
143
|
+
else:
|
|
144
|
+
flash(msg, "info")
|
|
145
|
+
|
|
146
|
+
if self.can_upload:
|
|
147
|
+
if ajax:
|
|
148
|
+
return json.dumps({"info_flashes": info_flashes})
|
|
149
|
+
else:
|
|
150
|
+
return redirect("/")
|
|
151
|
+
else:
|
|
152
|
+
if ajax:
|
|
153
|
+
return json.dumps(
|
|
154
|
+
{
|
|
155
|
+
"new_body": render_template(
|
|
156
|
+
"thankyou.html",
|
|
157
|
+
static_url_path=self.web.static_url_path,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
# It was the last upload and the timer ran out
|
|
163
|
+
r = make_response(
|
|
164
|
+
render_template("thankyou.html"),
|
|
165
|
+
static_url_path=self.web.static_url_path,
|
|
166
|
+
)
|
|
167
|
+
return self.web.add_security_headers(r)
|
|
168
|
+
|
|
169
|
+
@self.web.app.route("/upload-ajax", methods=["POST"])
|
|
170
|
+
def upload_ajax_public():
|
|
171
|
+
if not self.can_upload:
|
|
172
|
+
return self.web.error403()
|
|
173
|
+
return upload(ajax=True)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class ReceiveModeWSGIMiddleware(object):
|
|
177
|
+
"""
|
|
178
|
+
Custom WSGI middleware in order to attach the Web object to environ, so
|
|
179
|
+
ReceiveModeRequest can access it.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def __init__(self, app, web):
|
|
183
|
+
self.app = app
|
|
184
|
+
self.web = web
|
|
185
|
+
|
|
186
|
+
def __call__(self, environ, start_response):
|
|
187
|
+
environ["web"] = self.web
|
|
188
|
+
environ["stop_q"] = self.web.stop_q
|
|
189
|
+
return self.app(environ, start_response)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class ReceiveModeFile(object):
|
|
193
|
+
"""
|
|
194
|
+
A custom file object that tells ReceiveModeRequest every time data gets
|
|
195
|
+
written to it, in order to track the progress of uploads. It starts out with
|
|
196
|
+
a .part file extension, and when it's complete it removes that extension.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def __init__(self, request, filename, write_func, close_func):
|
|
200
|
+
self.onionshare_request = request
|
|
201
|
+
self.onionshare_filename = filename
|
|
202
|
+
self.onionshare_write_func = write_func
|
|
203
|
+
self.onionshare_close_func = close_func
|
|
204
|
+
|
|
205
|
+
self.filename = os.path.join(self.onionshare_request.receive_mode_dir, filename)
|
|
206
|
+
self.filename_in_progress = f"{self.filename}.part"
|
|
207
|
+
|
|
208
|
+
# Open the file
|
|
209
|
+
self.upload_error = False
|
|
210
|
+
try:
|
|
211
|
+
self.f = open(self.filename_in_progress, "wb+")
|
|
212
|
+
except:
|
|
213
|
+
# This will only happen if someone is messing with the data dir while
|
|
214
|
+
# OnionShare is running, but if it does make sure to throw an error
|
|
215
|
+
self.upload_error = True
|
|
216
|
+
self.f = tempfile.TemporaryFile("wb+")
|
|
217
|
+
|
|
218
|
+
# Make all the file-like methods and attributes actually access the
|
|
219
|
+
# TemporaryFile, except for write
|
|
220
|
+
attrs = [
|
|
221
|
+
"closed",
|
|
222
|
+
"detach",
|
|
223
|
+
"fileno",
|
|
224
|
+
"flush",
|
|
225
|
+
"isatty",
|
|
226
|
+
"mode",
|
|
227
|
+
"name",
|
|
228
|
+
"peek",
|
|
229
|
+
"raw",
|
|
230
|
+
"read",
|
|
231
|
+
"read1",
|
|
232
|
+
"readable",
|
|
233
|
+
"readinto",
|
|
234
|
+
"readinto1",
|
|
235
|
+
"readline",
|
|
236
|
+
"readlines",
|
|
237
|
+
"seek",
|
|
238
|
+
"seekable",
|
|
239
|
+
"tell",
|
|
240
|
+
"truncate",
|
|
241
|
+
"writable",
|
|
242
|
+
"writelines",
|
|
243
|
+
]
|
|
244
|
+
for attr in attrs:
|
|
245
|
+
setattr(self, attr, getattr(self.f, attr))
|
|
246
|
+
|
|
247
|
+
def write(self, b):
|
|
248
|
+
"""
|
|
249
|
+
Custom write method that calls out to onionshare_write_func
|
|
250
|
+
"""
|
|
251
|
+
if self.upload_error or (not self.onionshare_request.stop_q.empty()):
|
|
252
|
+
self.close()
|
|
253
|
+
self.onionshare_request.close()
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
bytes_written = self.f.write(b)
|
|
258
|
+
self.onionshare_write_func(self.onionshare_filename, bytes_written)
|
|
259
|
+
|
|
260
|
+
except:
|
|
261
|
+
self.upload_error = True
|
|
262
|
+
|
|
263
|
+
def close(self):
|
|
264
|
+
"""
|
|
265
|
+
Custom close method that calls out to onionshare_close_func
|
|
266
|
+
"""
|
|
267
|
+
try:
|
|
268
|
+
self.f.close()
|
|
269
|
+
|
|
270
|
+
if not self.upload_error:
|
|
271
|
+
# Rename the in progress file to the final filename
|
|
272
|
+
os.rename(self.filename_in_progress, self.filename)
|
|
273
|
+
|
|
274
|
+
except:
|
|
275
|
+
self.upload_error = True
|
|
276
|
+
|
|
277
|
+
self.onionshare_close_func(self.onionshare_filename, self.upload_error)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class ReceiveModeRequest(Request):
|
|
281
|
+
"""
|
|
282
|
+
A custom flask Request object that keeps track of how much data has been
|
|
283
|
+
uploaded for each file, for receive mode.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
def __init__(self, environ, populate_request=True, shallow=False):
|
|
287
|
+
super(ReceiveModeRequest, self).__init__(environ, populate_request, shallow)
|
|
288
|
+
self.web = environ["web"]
|
|
289
|
+
self.stop_q = environ["stop_q"]
|
|
290
|
+
|
|
291
|
+
self.web.common.log("ReceiveModeRequest", "__init__")
|
|
292
|
+
|
|
293
|
+
# Prevent running the close() method more than once
|
|
294
|
+
self.closed = False
|
|
295
|
+
|
|
296
|
+
# Is this a valid upload request?
|
|
297
|
+
self.upload_request = False
|
|
298
|
+
if self.method == "POST":
|
|
299
|
+
if self.path == "/upload" or self.path == "/upload-ajax":
|
|
300
|
+
self.upload_request = True
|
|
301
|
+
|
|
302
|
+
if self.upload_request:
|
|
303
|
+
# No errors yet
|
|
304
|
+
self.upload_error = False
|
|
305
|
+
|
|
306
|
+
# Figure out what files should be saved
|
|
307
|
+
now = datetime.now()
|
|
308
|
+
date_dir = now.strftime("%Y-%m-%d")
|
|
309
|
+
time_dir = now.strftime("%H.%M.%S")
|
|
310
|
+
self.receive_mode_dir = os.path.join(
|
|
311
|
+
self.web.settings.get("receive", "data_dir"), date_dir, time_dir
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Create that directory, which shouldn't exist yet
|
|
315
|
+
try:
|
|
316
|
+
os.makedirs(self.receive_mode_dir, 0o700, exist_ok=False)
|
|
317
|
+
except OSError:
|
|
318
|
+
# If this directory already exists, maybe someone else is uploading files at
|
|
319
|
+
# the same second, so use a different name in that case
|
|
320
|
+
if os.path.exists(self.receive_mode_dir):
|
|
321
|
+
# Keep going until we find a directory name that's available
|
|
322
|
+
i = 1
|
|
323
|
+
while True:
|
|
324
|
+
new_receive_mode_dir = f"{self.receive_mode_dir}-{i}"
|
|
325
|
+
try:
|
|
326
|
+
os.makedirs(new_receive_mode_dir, 0o700, exist_ok=False)
|
|
327
|
+
self.receive_mode_dir = new_receive_mode_dir
|
|
328
|
+
break
|
|
329
|
+
except OSError:
|
|
330
|
+
pass
|
|
331
|
+
i += 1
|
|
332
|
+
# Failsafe
|
|
333
|
+
if i == 100:
|
|
334
|
+
self.web.common.log(
|
|
335
|
+
"ReceiveModeRequest",
|
|
336
|
+
"__init__",
|
|
337
|
+
"Error finding available receive mode directory",
|
|
338
|
+
)
|
|
339
|
+
self.upload_error = True
|
|
340
|
+
break
|
|
341
|
+
except PermissionError:
|
|
342
|
+
self.web.add_request(
|
|
343
|
+
self.web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE,
|
|
344
|
+
request.path,
|
|
345
|
+
{"receive_mode_dir": self.receive_mode_dir},
|
|
346
|
+
)
|
|
347
|
+
print(
|
|
348
|
+
f"Could not create OnionShare data folder: {self.receive_mode_dir}"
|
|
349
|
+
)
|
|
350
|
+
self.web.common.log(
|
|
351
|
+
"ReceiveModeRequest",
|
|
352
|
+
"__init__",
|
|
353
|
+
"Permission denied creating receive mode directory",
|
|
354
|
+
)
|
|
355
|
+
self.upload_error = True
|
|
356
|
+
|
|
357
|
+
# If there's an error so far, finish early
|
|
358
|
+
if self.upload_error:
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
# A dictionary that maps filenames to the bytes uploaded so far
|
|
362
|
+
self.progress = {}
|
|
363
|
+
|
|
364
|
+
# Prevent new uploads if we've said so (timer expired)
|
|
365
|
+
if self.web.receive_mode.can_upload:
|
|
366
|
+
|
|
367
|
+
# Create an history_id, attach it to the request
|
|
368
|
+
self.history_id = self.web.receive_mode.cur_history_id
|
|
369
|
+
self.web.receive_mode.cur_history_id += 1
|
|
370
|
+
|
|
371
|
+
# Figure out the content length
|
|
372
|
+
try:
|
|
373
|
+
self.content_length = int(self.headers["Content-Length"])
|
|
374
|
+
except:
|
|
375
|
+
self.content_length = 0
|
|
376
|
+
|
|
377
|
+
date_str = datetime.now().strftime("%b %d, %I:%M%p")
|
|
378
|
+
size_str = self.web.common.human_readable_filesize(self.content_length)
|
|
379
|
+
print(f"{date_str}: Upload of total size {size_str} is starting")
|
|
380
|
+
|
|
381
|
+
# Don't tell the GUI that a request has started until we start receiving files
|
|
382
|
+
self.told_gui_about_request = False
|
|
383
|
+
|
|
384
|
+
self.previous_file = None
|
|
385
|
+
|
|
386
|
+
def _get_file_stream(
|
|
387
|
+
self, total_content_length, content_type, filename=None, content_length=None
|
|
388
|
+
):
|
|
389
|
+
"""
|
|
390
|
+
This gets called for each file that gets uploaded, and returns an file-like
|
|
391
|
+
writable stream.
|
|
392
|
+
"""
|
|
393
|
+
if self.upload_request:
|
|
394
|
+
if not self.told_gui_about_request:
|
|
395
|
+
# Tell the GUI about the request
|
|
396
|
+
self.web.add_request(
|
|
397
|
+
self.web.REQUEST_STARTED,
|
|
398
|
+
self.path,
|
|
399
|
+
{"id": self.history_id, "content_length": self.content_length},
|
|
400
|
+
)
|
|
401
|
+
self.web.receive_mode.uploads_in_progress.append(self.history_id)
|
|
402
|
+
|
|
403
|
+
self.told_gui_about_request = True
|
|
404
|
+
|
|
405
|
+
self.filename = secure_filename(filename)
|
|
406
|
+
|
|
407
|
+
self.progress[self.filename] = {"uploaded_bytes": 0, "complete": False}
|
|
408
|
+
|
|
409
|
+
f = ReceiveModeFile(
|
|
410
|
+
self, self.filename, self.file_write_func, self.file_close_func
|
|
411
|
+
)
|
|
412
|
+
if f.upload_error:
|
|
413
|
+
self.web.common.log(
|
|
414
|
+
"ReceiveModeRequest", "_get_file_stream", "Error creating file"
|
|
415
|
+
)
|
|
416
|
+
self.upload_error = True
|
|
417
|
+
return f
|
|
418
|
+
|
|
419
|
+
def close(self):
|
|
420
|
+
"""
|
|
421
|
+
Closing the request.
|
|
422
|
+
"""
|
|
423
|
+
super(ReceiveModeRequest, self).close()
|
|
424
|
+
|
|
425
|
+
# Prevent calling this method more than once per request
|
|
426
|
+
if self.closed:
|
|
427
|
+
return
|
|
428
|
+
self.closed = True
|
|
429
|
+
|
|
430
|
+
self.web.common.log("ReceiveModeRequest", "close")
|
|
431
|
+
|
|
432
|
+
try:
|
|
433
|
+
if self.told_gui_about_request:
|
|
434
|
+
history_id = self.history_id
|
|
435
|
+
|
|
436
|
+
if (
|
|
437
|
+
not self.web.stop_q.empty()
|
|
438
|
+
or not self.progress[self.filename]["complete"]
|
|
439
|
+
):
|
|
440
|
+
# Inform the GUI that the upload has canceled
|
|
441
|
+
self.web.add_request(
|
|
442
|
+
self.web.REQUEST_UPLOAD_CANCELED, self.path, {"id": history_id}
|
|
443
|
+
)
|
|
444
|
+
else:
|
|
445
|
+
# Inform the GUI that the upload has finished
|
|
446
|
+
self.web.add_request(
|
|
447
|
+
self.web.REQUEST_UPLOAD_FINISHED, self.path, {"id": history_id}
|
|
448
|
+
)
|
|
449
|
+
self.web.receive_mode.uploads_in_progress.remove(history_id)
|
|
450
|
+
|
|
451
|
+
except AttributeError:
|
|
452
|
+
pass
|
|
453
|
+
|
|
454
|
+
def file_write_func(self, filename, length):
|
|
455
|
+
"""
|
|
456
|
+
This function gets called when a specific file is written to.
|
|
457
|
+
"""
|
|
458
|
+
if self.closed:
|
|
459
|
+
return
|
|
460
|
+
|
|
461
|
+
if self.upload_request:
|
|
462
|
+
self.progress[filename]["uploaded_bytes"] += length
|
|
463
|
+
|
|
464
|
+
if self.previous_file != filename:
|
|
465
|
+
self.previous_file = filename
|
|
466
|
+
|
|
467
|
+
size_str = self.web.common.human_readable_filesize(
|
|
468
|
+
self.progress[filename]["uploaded_bytes"]
|
|
469
|
+
)
|
|
470
|
+
print(f"\r=> {size_str} {filename} ", end="")
|
|
471
|
+
|
|
472
|
+
# Update the GUI on the upload progress
|
|
473
|
+
if self.told_gui_about_request:
|
|
474
|
+
self.web.add_request(
|
|
475
|
+
self.web.REQUEST_PROGRESS,
|
|
476
|
+
self.path,
|
|
477
|
+
{"id": self.history_id, "progress": self.progress},
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
def file_close_func(self, filename, upload_error=False):
|
|
481
|
+
"""
|
|
482
|
+
This function gets called when a specific file is closed.
|
|
483
|
+
"""
|
|
484
|
+
self.progress[filename]["complete"] = True
|
|
485
|
+
|
|
486
|
+
# If the file tells us there was an upload error, let the request know as well
|
|
487
|
+
if upload_error:
|
|
488
|
+
self.upload_error = True
|