onionshare_cli 2.3.2__tar.gz → 2.5__tar.gz
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-2.3.2 → onionshare_cli-2.5}/PKG-INFO +8 -7
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/__init__.py +56 -91
- onionshare_cli-2.5/onionshare_cli/censorship.py +200 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/common.py +257 -199
- onionshare_cli-2.5/onionshare_cli/meek.py +189 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/mode_settings.py +5 -7
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/onion.py +251 -112
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/onionshare.py +6 -5
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/css/style.css +7 -6
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/js/chat.js +20 -14
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/js/send.js +14 -10
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/403.html +1 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/404.html +1 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/405.html +1 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/500.html +1 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/send.html +9 -9
- onionshare_cli-2.5/onionshare_cli/resources/torrc_template +12 -0
- onionshare_cli-2.5/onionshare_cli/resources/torrc_template-meek_lite_azure +2 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/torrc_template-obfs4 +9 -9
- onionshare_cli-2.5/onionshare_cli/resources/torrc_template-snowflake +2 -0
- onionshare_cli-2.5/onionshare_cli/resources/version.txt +1 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/settings.py +24 -20
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/__init__.py +1 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/chat_mode.py +62 -52
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/receive_mode.py +9 -13
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/send_base_mode.py +13 -11
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/share_mode.py +39 -29
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/web.py +74 -136
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/web/website_mode.py +1 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/pyproject.toml +7 -7
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/setup.py +8 -8
- onionshare_cli-2.3.2/onionshare_cli/resources/templates/401.html +0 -21
- onionshare_cli-2.3.2/onionshare_cli/resources/torrc_template +0 -8
- onionshare_cli-2.3.2/onionshare_cli/resources/torrc_template-meek_lite_amazon +0 -2
- onionshare_cli-2.3.2/onionshare_cli/resources/torrc_template-meek_lite_azure +0 -2
- onionshare_cli-2.3.2/onionshare_cli/resources/version.txt +0 -1
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/img/ajax.gif +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/img/favicon.ico +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/img/logo.png +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/img/logo_large.png +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/img/web_file.png +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/img/web_folder.png +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/js/jquery-3.5.1.min.js +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/js/receive.js +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/static/js/socket.io.min.js +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/chat.html +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/denied.html +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/listing.html +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/receive.html +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/templates/thankyou.html +0 -0
- {onionshare_cli-2.3.2 → onionshare_cli-2.5}/onionshare_cli/resources/wordlist.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: onionshare-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5
|
|
4
4
|
Summary: OnionShare lets you securely and anonymously send and receive files. It works by starting a web server, making it accessible as a Tor onion service, and generating an unguessable web address so others can download files from you, or upload files to you. It does _not_ require setting up a separate server or using a third party file-sharing service.
|
|
5
5
|
License: GPLv3+
|
|
6
6
|
Author: Micah Lee
|
|
@@ -13,23 +13,24 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
|
|
|
13
13
|
Classifier: License :: Other/Proprietary License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.6
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.7
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
20
21
|
Classifier: Topic :: Communications :: File Sharing
|
|
21
22
|
Classifier: Topic :: Security :: Cryptography
|
|
23
|
+
Requires-Dist: cepa (==1.8.3)
|
|
22
24
|
Requires-Dist: click
|
|
23
|
-
Requires-Dist: colorama
|
|
25
|
+
Requires-Dist: colorama
|
|
24
26
|
Requires-Dist: eventlet
|
|
25
|
-
Requires-Dist: flask (
|
|
26
|
-
Requires-Dist: flask-httpauth
|
|
27
|
+
Requires-Dist: flask (==1.1.4)
|
|
27
28
|
Requires-Dist: flask-socketio (==5.0.1)
|
|
29
|
+
Requires-Dist: gevent-websocket
|
|
28
30
|
Requires-Dist: psutil
|
|
29
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: pynacl
|
|
30
32
|
Requires-Dist: pysocks
|
|
31
|
-
Requires-Dist: requests[socks]
|
|
33
|
+
Requires-Dist: requests[socks]
|
|
32
34
|
Requires-Dist: setuptools
|
|
33
|
-
Requires-Dist: stem
|
|
34
35
|
Requires-Dist: unidecode
|
|
35
36
|
Requires-Dist: urllib3
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
OnionShare | https://onionshare.org/
|
|
4
4
|
|
|
5
|
-
Copyright (C) 2014-
|
|
5
|
+
Copyright (C) 2014-2022 Micah Lee, et al. <micah@micahflee.com>
|
|
6
6
|
|
|
7
7
|
This program is free software: you can redistribute it and/or modify
|
|
8
8
|
it under the terms of the GNU General Public License as published by
|
|
@@ -28,24 +28,11 @@ from datetime import timedelta
|
|
|
28
28
|
|
|
29
29
|
from .common import Common, CannotFindTor
|
|
30
30
|
from .web import Web
|
|
31
|
-
from .onion import
|
|
32
|
-
TorErrorProtocolError,
|
|
33
|
-
TorTooOldEphemeral,
|
|
34
|
-
TorTooOldStealth,
|
|
35
|
-
Onion,
|
|
36
|
-
)
|
|
31
|
+
from .onion import TorErrorProtocolError, TorTooOldEphemeral, TorTooOldStealth, Onion
|
|
37
32
|
from .onionshare import OnionShare
|
|
38
33
|
from .mode_settings import ModeSettings
|
|
39
34
|
|
|
40
35
|
|
|
41
|
-
def build_url(mode_settings, app, web):
|
|
42
|
-
# Build the URL
|
|
43
|
-
if mode_settings.get("general", "public"):
|
|
44
|
-
return f"http://{app.onion_host}"
|
|
45
|
-
else:
|
|
46
|
-
return f"http://onionshare:{web.password}@{app.onion_host}"
|
|
47
|
-
|
|
48
|
-
|
|
49
36
|
def main(cwd=None):
|
|
50
37
|
"""
|
|
51
38
|
The main() function implements all of the logic that the command-line version of
|
|
@@ -102,18 +89,13 @@ def main(cwd=None):
|
|
|
102
89
|
help="Filename of persistent session",
|
|
103
90
|
)
|
|
104
91
|
# General args
|
|
105
|
-
parser.add_argument(
|
|
106
|
-
"--title",
|
|
107
|
-
metavar="TITLE",
|
|
108
|
-
default=None,
|
|
109
|
-
help="Set a title",
|
|
110
|
-
)
|
|
92
|
+
parser.add_argument("--title", metavar="TITLE", default=None, help="Set a title")
|
|
111
93
|
parser.add_argument(
|
|
112
94
|
"--public",
|
|
113
95
|
action="store_true",
|
|
114
96
|
dest="public",
|
|
115
97
|
default=False,
|
|
116
|
-
help="Don't use a
|
|
98
|
+
help="Don't use a private key",
|
|
117
99
|
)
|
|
118
100
|
parser.add_argument(
|
|
119
101
|
"--auto-start-timer",
|
|
@@ -129,20 +111,6 @@ def main(cwd=None):
|
|
|
129
111
|
default=0,
|
|
130
112
|
help="Stop onion service at schedule time (N seconds from now)",
|
|
131
113
|
)
|
|
132
|
-
parser.add_argument(
|
|
133
|
-
"--legacy",
|
|
134
|
-
action="store_true",
|
|
135
|
-
dest="legacy",
|
|
136
|
-
default=False,
|
|
137
|
-
help="Use legacy address (v2 onion service, not recommended)",
|
|
138
|
-
)
|
|
139
|
-
parser.add_argument(
|
|
140
|
-
"--client-auth",
|
|
141
|
-
action="store_true",
|
|
142
|
-
dest="client_auth",
|
|
143
|
-
default=False,
|
|
144
|
-
help="Use client authorization (requires --legacy)",
|
|
145
|
-
)
|
|
146
114
|
# Share args
|
|
147
115
|
parser.add_argument(
|
|
148
116
|
"--no-autostop-sharing",
|
|
@@ -182,7 +150,13 @@ def main(cwd=None):
|
|
|
182
150
|
action="store_true",
|
|
183
151
|
dest="disable_csp",
|
|
184
152
|
default=False,
|
|
185
|
-
help="Publish website: Disable Content Security Policy header (allows your website to use third-party resources)",
|
|
153
|
+
help="Publish website: Disable the default Content Security Policy header (allows your website to use third-party resources)",
|
|
154
|
+
)
|
|
155
|
+
parser.add_argument(
|
|
156
|
+
"--custom_csp",
|
|
157
|
+
metavar="custom_csp",
|
|
158
|
+
default=None,
|
|
159
|
+
help="Publish website: Set a custom Content Security Policy header",
|
|
186
160
|
)
|
|
187
161
|
# Other
|
|
188
162
|
parser.add_argument(
|
|
@@ -215,35 +189,18 @@ def main(cwd=None):
|
|
|
215
189
|
public = bool(args.public)
|
|
216
190
|
autostart_timer = int(args.autostart_timer)
|
|
217
191
|
autostop_timer = int(args.autostop_timer)
|
|
218
|
-
legacy = bool(args.legacy)
|
|
219
|
-
client_auth = bool(args.client_auth)
|
|
220
192
|
autostop_sharing = not bool(args.no_autostop_sharing)
|
|
221
193
|
data_dir = args.data_dir
|
|
222
194
|
webhook_url = args.webhook_url
|
|
223
195
|
disable_text = args.disable_text
|
|
224
196
|
disable_files = args.disable_files
|
|
225
197
|
disable_csp = bool(args.disable_csp)
|
|
198
|
+
custom_csp = args.custom_csp
|
|
226
199
|
verbose = bool(args.verbose)
|
|
227
200
|
|
|
228
|
-
if receive:
|
|
229
|
-
mode = "receive"
|
|
230
|
-
elif website:
|
|
231
|
-
mode = "website"
|
|
232
|
-
elif chat:
|
|
233
|
-
mode = "chat"
|
|
234
|
-
else:
|
|
235
|
-
mode = "share"
|
|
236
|
-
|
|
237
201
|
# Verbose mode?
|
|
238
202
|
common.verbose = verbose
|
|
239
203
|
|
|
240
|
-
# client_auth can only be set if legacy is also set
|
|
241
|
-
if client_auth and not legacy:
|
|
242
|
-
print(
|
|
243
|
-
"Client authentication (--client-auth) is only supported with with legacy onion services (--legacy)"
|
|
244
|
-
)
|
|
245
|
-
sys.exit()
|
|
246
|
-
|
|
247
204
|
# Re-load settings, if a custom config was passed in
|
|
248
205
|
if config_filename:
|
|
249
206
|
common.load_settings(config_filename)
|
|
@@ -254,18 +211,26 @@ def main(cwd=None):
|
|
|
254
211
|
if persistent_filename:
|
|
255
212
|
mode_settings = ModeSettings(common, persistent_filename)
|
|
256
213
|
mode_settings.set("persistent", "enabled", True)
|
|
257
|
-
mode_settings.set("persistent", "mode", mode)
|
|
258
214
|
else:
|
|
259
215
|
mode_settings = ModeSettings(common)
|
|
260
216
|
|
|
217
|
+
if receive:
|
|
218
|
+
mode = "receive"
|
|
219
|
+
elif website:
|
|
220
|
+
mode = "website"
|
|
221
|
+
elif chat:
|
|
222
|
+
mode = "chat"
|
|
223
|
+
else:
|
|
224
|
+
mode = "share"
|
|
225
|
+
|
|
261
226
|
if mode_settings.just_created:
|
|
262
227
|
# This means the mode settings were just created, not loaded from disk
|
|
263
228
|
mode_settings.set("general", "title", title)
|
|
264
229
|
mode_settings.set("general", "public", public)
|
|
265
230
|
mode_settings.set("general", "autostart_timer", autostart_timer)
|
|
266
231
|
mode_settings.set("general", "autostop_timer", autostop_timer)
|
|
267
|
-
|
|
268
|
-
|
|
232
|
+
if persistent_filename:
|
|
233
|
+
mode_settings.set("persistent", "mode", mode)
|
|
269
234
|
if mode == "share":
|
|
270
235
|
mode_settings.set("share", "autostop_sharing", autostop_sharing)
|
|
271
236
|
if mode == "receive":
|
|
@@ -276,7 +241,15 @@ def main(cwd=None):
|
|
|
276
241
|
mode_settings.set("receive", "disable_text", disable_text)
|
|
277
242
|
mode_settings.set("receive", "disable_files", disable_files)
|
|
278
243
|
if mode == "website":
|
|
279
|
-
|
|
244
|
+
if disable_csp and custom_csp:
|
|
245
|
+
print("You cannot disable the CSP and set a custom one. Either set --disable-csp or --custom-csp but not both.")
|
|
246
|
+
sys.exit()
|
|
247
|
+
if disable_csp:
|
|
248
|
+
mode_settings.set("website", "disable_csp", True)
|
|
249
|
+
mode_settings.set("website", "custom_csp", None)
|
|
250
|
+
if custom_csp:
|
|
251
|
+
mode_settings.set("website", "custom_csp", custom_csp)
|
|
252
|
+
mode_settings.set("website", "disable_csp", False)
|
|
280
253
|
else:
|
|
281
254
|
# See what the persistent mode was
|
|
282
255
|
mode = mode_settings.get("persistent", "mode")
|
|
@@ -352,11 +325,6 @@ def main(cwd=None):
|
|
|
352
325
|
try:
|
|
353
326
|
common.settings.load()
|
|
354
327
|
|
|
355
|
-
if mode_settings.get("general", "public"):
|
|
356
|
-
web.password = None
|
|
357
|
-
else:
|
|
358
|
-
web.generate_password(mode_settings.get("onion", "password"))
|
|
359
|
-
|
|
360
328
|
# Receive mode needs to know the tor proxy details for webhooks
|
|
361
329
|
if mode == "receive":
|
|
362
330
|
if local_only:
|
|
@@ -381,7 +349,7 @@ def main(cwd=None):
|
|
|
381
349
|
sys.exit()
|
|
382
350
|
|
|
383
351
|
app.start_onion_service(mode, mode_settings, False)
|
|
384
|
-
url =
|
|
352
|
+
url = f"http://{app.onion_host}"
|
|
385
353
|
schedule = datetime.now() + timedelta(seconds=autostart_timer)
|
|
386
354
|
if mode == "receive":
|
|
387
355
|
print(
|
|
@@ -394,21 +362,21 @@ def main(cwd=None):
|
|
|
394
362
|
"what you are doing."
|
|
395
363
|
)
|
|
396
364
|
print("")
|
|
397
|
-
if mode_settings.get("general", "
|
|
365
|
+
if not mode_settings.get("general", "public"):
|
|
398
366
|
print(
|
|
399
|
-
f"Give this address and
|
|
367
|
+
f"Give this address and private key to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
|
400
368
|
)
|
|
401
|
-
print(app.auth_string)
|
|
369
|
+
print(f"Private key: {app.auth_string}")
|
|
402
370
|
else:
|
|
403
371
|
print(
|
|
404
372
|
f"Give this address to your sender, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
|
405
373
|
)
|
|
406
374
|
else:
|
|
407
|
-
if mode_settings.get("general", "
|
|
375
|
+
if not mode_settings.get("general", "public"):
|
|
408
376
|
print(
|
|
409
|
-
f"Give this address and
|
|
377
|
+
f"Give this address and private key to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
|
410
378
|
)
|
|
411
|
-
print(app.auth_string)
|
|
379
|
+
print(f"Private key: {app.auth_string}")
|
|
412
380
|
else:
|
|
413
381
|
print(
|
|
414
382
|
f"Give this address to your recipient, and tell them it won't be accessible until: {schedule.strftime('%I:%M:%S%p, %b %d, %y')}"
|
|
@@ -426,7 +394,6 @@ def main(cwd=None):
|
|
|
426
394
|
sys.exit()
|
|
427
395
|
except (TorTooOldEphemeral, TorTooOldStealth, TorErrorProtocolError) as e:
|
|
428
396
|
print("")
|
|
429
|
-
print(e.args[0])
|
|
430
397
|
sys.exit()
|
|
431
398
|
|
|
432
399
|
if mode == "website":
|
|
@@ -447,7 +414,7 @@ def main(cwd=None):
|
|
|
447
414
|
sys.exit(1)
|
|
448
415
|
|
|
449
416
|
# Warn about sending large files over Tor
|
|
450
|
-
if web.share_mode.download_filesize >=
|
|
417
|
+
if web.share_mode.download_filesize >= 157_286_400: # 150mb
|
|
451
418
|
print("")
|
|
452
419
|
print("Warning: Sending a large share could take hours")
|
|
453
420
|
print("")
|
|
@@ -458,21 +425,14 @@ def main(cwd=None):
|
|
|
458
425
|
t.start()
|
|
459
426
|
|
|
460
427
|
try: # Trap Ctrl-C
|
|
461
|
-
# Wait for web.generate_password() to finish running
|
|
462
428
|
time.sleep(0.2)
|
|
463
429
|
|
|
464
430
|
# start auto-stop timer thread
|
|
465
431
|
if app.autostop_timer > 0:
|
|
466
432
|
app.autostop_timer_thread.start()
|
|
467
433
|
|
|
468
|
-
# Save the web password if we are using a persistent private key
|
|
469
|
-
if mode_settings.get("persistent", "enabled"):
|
|
470
|
-
if not mode_settings.get("onion", "password"):
|
|
471
|
-
mode_settings.set("onion", "password", web.password)
|
|
472
|
-
# mode_settings.save()
|
|
473
|
-
|
|
474
434
|
# Build the URL
|
|
475
|
-
url =
|
|
435
|
+
url = f"http://{app.onion_host}"
|
|
476
436
|
|
|
477
437
|
print("")
|
|
478
438
|
if autostart_timer > 0:
|
|
@@ -490,21 +450,21 @@ def main(cwd=None):
|
|
|
490
450
|
)
|
|
491
451
|
print("")
|
|
492
452
|
|
|
493
|
-
if mode_settings.get("general", "
|
|
494
|
-
print("Give this address
|
|
453
|
+
if mode_settings.get("general", "public"):
|
|
454
|
+
print("Give this address to the sender:")
|
|
495
455
|
print(url)
|
|
496
|
-
print(app.auth_string)
|
|
497
456
|
else:
|
|
498
|
-
print("Give this address to the sender:")
|
|
457
|
+
print("Give this address and private key to the sender:")
|
|
499
458
|
print(url)
|
|
459
|
+
print(f"Private key: {app.auth_string}")
|
|
500
460
|
else:
|
|
501
|
-
if mode_settings.get("general", "
|
|
502
|
-
print("Give this address
|
|
461
|
+
if mode_settings.get("general", "public"):
|
|
462
|
+
print("Give this address to the recipient:")
|
|
503
463
|
print(url)
|
|
504
|
-
print(app.auth_string)
|
|
505
464
|
else:
|
|
506
|
-
print("Give this address to the recipient:")
|
|
465
|
+
print("Give this address and private key to the recipient:")
|
|
507
466
|
print(url)
|
|
467
|
+
print(f"Private key: {app.auth_string}")
|
|
508
468
|
print("")
|
|
509
469
|
print("Press Ctrl+C to stop the server")
|
|
510
470
|
|
|
@@ -513,13 +473,13 @@ def main(cwd=None):
|
|
|
513
473
|
if app.autostop_timer > 0:
|
|
514
474
|
# if the auto-stop timer was set and has run out, stop the server
|
|
515
475
|
if not app.autostop_timer_thread.is_alive():
|
|
516
|
-
if mode == "share"
|
|
476
|
+
if mode == "share":
|
|
517
477
|
# If there were no attempts to download the share, or all downloads are done, we can stop
|
|
518
478
|
if web.share_mode.cur_history_id == 0 or web.done:
|
|
519
479
|
print("Stopped because auto-stop timer ran out")
|
|
520
480
|
web.stop(app.port)
|
|
521
481
|
break
|
|
522
|
-
|
|
482
|
+
elif mode == "receive":
|
|
523
483
|
if (
|
|
524
484
|
web.receive_mode.cur_history_id == 0
|
|
525
485
|
or not web.receive_mode.uploads_in_progress
|
|
@@ -528,6 +488,11 @@ def main(cwd=None):
|
|
|
528
488
|
web.stop(app.port)
|
|
529
489
|
break
|
|
530
490
|
web.receive_mode.can_upload = False
|
|
491
|
+
else:
|
|
492
|
+
# website or chat mode
|
|
493
|
+
print("Stopped because auto-stop timer ran out")
|
|
494
|
+
web.stop(app.port)
|
|
495
|
+
break
|
|
531
496
|
# Allow KeyboardInterrupt exception to be handled with threads
|
|
532
497
|
# https://stackoverflow.com/questions/3788208/python-threading-ignores-keyboardinterrupt-exception
|
|
533
498
|
time.sleep(0.2)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
OnionShare | https://onionshare.org/
|
|
4
|
+
|
|
5
|
+
Copyright (C) 2014-2022 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
|
+
import requests
|
|
21
|
+
|
|
22
|
+
from .meek import MeekNotRunning
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CensorshipCircumvention(object):
|
|
26
|
+
"""
|
|
27
|
+
Connect to the Tor Moat APIs to retrieve censorship
|
|
28
|
+
circumvention recommendations or the latest bridges.
|
|
29
|
+
|
|
30
|
+
We support reaching this API over Tor, or Meek
|
|
31
|
+
(domain fronting) if Tor is not connected.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, common, meek=None, onion=None):
|
|
35
|
+
"""
|
|
36
|
+
Set up the CensorshipCircumvention object to hold
|
|
37
|
+
common and meek objects.
|
|
38
|
+
"""
|
|
39
|
+
self.common = common
|
|
40
|
+
self.common.log("CensorshipCircumvention", "__init__")
|
|
41
|
+
self.api_proxies = {}
|
|
42
|
+
if meek:
|
|
43
|
+
self.meek = meek
|
|
44
|
+
if not self.meek.meek_proxies:
|
|
45
|
+
raise MeekNotRunning()
|
|
46
|
+
else:
|
|
47
|
+
self.common.log(
|
|
48
|
+
"CensorshipCircumvention",
|
|
49
|
+
"__init__",
|
|
50
|
+
"Using Meek with CensorShipCircumvention API",
|
|
51
|
+
)
|
|
52
|
+
self.api_proxies = self.meek.meek_proxies
|
|
53
|
+
if onion:
|
|
54
|
+
self.onion = onion
|
|
55
|
+
if not self.onion.is_authenticated:
|
|
56
|
+
return False
|
|
57
|
+
else:
|
|
58
|
+
self.common.log(
|
|
59
|
+
"CensorshipCircumvention",
|
|
60
|
+
"__init__",
|
|
61
|
+
"Using Tor with CensorShipCircumvention API",
|
|
62
|
+
)
|
|
63
|
+
(socks_address, socks_port) = self.onion.get_tor_socks_port()
|
|
64
|
+
self.api_proxies = {
|
|
65
|
+
"http": f"socks5h://{socks_address}:{socks_port}",
|
|
66
|
+
"https": f"socks5h://{socks_address}:{socks_port}",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
def request_map(self, country=False):
|
|
70
|
+
"""
|
|
71
|
+
Retrieves the Circumvention map from Tor Project and store it
|
|
72
|
+
locally for further look-ups if required.
|
|
73
|
+
|
|
74
|
+
Optionally pass a country code in order to get recommended settings
|
|
75
|
+
just for that country.
|
|
76
|
+
|
|
77
|
+
Note that this API endpoint doesn't return actual bridges,
|
|
78
|
+
it just returns the recommended bridge type countries.
|
|
79
|
+
"""
|
|
80
|
+
if not self.api_proxies:
|
|
81
|
+
return False
|
|
82
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/map"
|
|
83
|
+
data = {}
|
|
84
|
+
if country:
|
|
85
|
+
data = {"country": country}
|
|
86
|
+
|
|
87
|
+
r = requests.post(
|
|
88
|
+
endpoint,
|
|
89
|
+
json=data,
|
|
90
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
91
|
+
proxies=self.api_proxies,
|
|
92
|
+
)
|
|
93
|
+
if r.status_code != 200:
|
|
94
|
+
self.common.log(
|
|
95
|
+
"CensorshipCircumvention",
|
|
96
|
+
"censorship_obtain_map",
|
|
97
|
+
f"status_code={r.status_code}",
|
|
98
|
+
)
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
result = r.json()
|
|
102
|
+
|
|
103
|
+
if "errors" in result:
|
|
104
|
+
self.common.log(
|
|
105
|
+
"CensorshipCircumvention",
|
|
106
|
+
"censorship_obtain_map",
|
|
107
|
+
f"errors={result['errors']}",
|
|
108
|
+
)
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
def request_settings(self, country=False, transports=False):
|
|
114
|
+
"""
|
|
115
|
+
Retrieves the Circumvention Settings from Tor Project, which
|
|
116
|
+
will return recommended settings based on the country code of
|
|
117
|
+
the requesting IP.
|
|
118
|
+
|
|
119
|
+
Optionally, a country code can be specified in order to override
|
|
120
|
+
the IP detection.
|
|
121
|
+
|
|
122
|
+
Optionally, a list of transports can be specified in order to
|
|
123
|
+
return recommended settings for just that transport type.
|
|
124
|
+
"""
|
|
125
|
+
if not self.api_proxies:
|
|
126
|
+
return False
|
|
127
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/settings"
|
|
128
|
+
data = {}
|
|
129
|
+
if country:
|
|
130
|
+
data = {"country": country}
|
|
131
|
+
if transports:
|
|
132
|
+
data.append({"transports": transports})
|
|
133
|
+
r = requests.post(
|
|
134
|
+
endpoint,
|
|
135
|
+
json=data,
|
|
136
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
137
|
+
proxies=self.api_proxies,
|
|
138
|
+
)
|
|
139
|
+
if r.status_code != 200:
|
|
140
|
+
self.common.log(
|
|
141
|
+
"CensorshipCircumvention",
|
|
142
|
+
"censorship_obtain_settings",
|
|
143
|
+
f"status_code={r.status_code}",
|
|
144
|
+
)
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
result = r.json()
|
|
148
|
+
|
|
149
|
+
if "errors" in result:
|
|
150
|
+
self.common.log(
|
|
151
|
+
"CensorshipCircumvention",
|
|
152
|
+
"censorship_obtain_settings",
|
|
153
|
+
f"errors={result['errors']}",
|
|
154
|
+
)
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
# There are no settings - perhaps this country doesn't require censorship circumvention?
|
|
158
|
+
# This is not really an error, so we can just check if False and assume direct Tor
|
|
159
|
+
# connection will work.
|
|
160
|
+
if not "settings" in result:
|
|
161
|
+
self.common.log(
|
|
162
|
+
"CensorshipCircumvention",
|
|
163
|
+
"censorship_obtain_settings",
|
|
164
|
+
"No settings found for this country",
|
|
165
|
+
)
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
def request_builtin_bridges(self):
|
|
171
|
+
"""
|
|
172
|
+
Retrieves the list of built-in bridges from the Tor Project.
|
|
173
|
+
"""
|
|
174
|
+
if not self.api_proxies:
|
|
175
|
+
return False
|
|
176
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/builtin"
|
|
177
|
+
r = requests.post(
|
|
178
|
+
endpoint,
|
|
179
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
180
|
+
proxies=self.api_proxies,
|
|
181
|
+
)
|
|
182
|
+
if r.status_code != 200:
|
|
183
|
+
self.common.log(
|
|
184
|
+
"CensorshipCircumvention",
|
|
185
|
+
"censorship_obtain_builtin_bridges",
|
|
186
|
+
f"status_code={r.status_code}",
|
|
187
|
+
)
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
result = r.json()
|
|
191
|
+
|
|
192
|
+
if "errors" in result:
|
|
193
|
+
self.common.log(
|
|
194
|
+
"CensorshipCircumvention",
|
|
195
|
+
"censorship_obtain_builtin_bridges",
|
|
196
|
+
f"errors={result['errors']}",
|
|
197
|
+
)
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
return result
|