onionshare_cli 2.5__tar.gz → 2.6__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.5 → onionshare_cli-2.6}/PKG-INFO +8 -7
- onionshare_cli-2.6/onionshare_cli/censorship.py +311 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/common.py +48 -8
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/meek.py +14 -4
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/onion.py +0 -14
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/css/style.css +47 -16
- onionshare_cli-2.6/onionshare_cli/resources/static/img/favicon.ico +0 -0
- onionshare_cli-2.6/onionshare_cli/resources/static/img/logo.png +0 -0
- onionshare_cli-2.6/onionshare_cli/resources/static/img/logo_large.png +0 -0
- onionshare_cli-2.6/onionshare_cli/resources/static/js/socket.io.js +9 -0
- onionshare_cli-2.6/onionshare_cli/resources/static/js/socket.io.min.js +7 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/listing.html +2 -2
- onionshare_cli-2.6/onionshare_cli/resources/torrc_template-meek_lite_azure +2 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/torrc_template-obfs4 +0 -1
- onionshare_cli-2.6/onionshare_cli/resources/torrc_template-snowflake +2 -0
- onionshare_cli-2.6/onionshare_cli/resources/version.txt +1 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/settings.py +9 -7
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/send_base_mode.py +8 -10
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/web.py +1 -7
- {onionshare_cli-2.5 → onionshare_cli-2.6}/pyproject.toml +6 -6
- {onionshare_cli-2.5 → onionshare_cli-2.6}/setup.py +12 -11
- onionshare_cli-2.5/onionshare_cli/censorship.py +0 -200
- onionshare_cli-2.5/onionshare_cli/resources/static/img/favicon.ico +0 -0
- onionshare_cli-2.5/onionshare_cli/resources/static/img/logo.png +0 -0
- onionshare_cli-2.5/onionshare_cli/resources/static/img/logo_large.png +0 -0
- onionshare_cli-2.5/onionshare_cli/resources/static/js/socket.io.min.js +0 -7
- onionshare_cli-2.5/onionshare_cli/resources/torrc_template-meek_lite_azure +0 -2
- onionshare_cli-2.5/onionshare_cli/resources/torrc_template-snowflake +0 -2
- onionshare_cli-2.5/onionshare_cli/resources/version.txt +0 -1
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/__init__.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/mode_settings.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/onionshare.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/img/ajax.gif +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/img/web_file.png +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/img/web_folder.png +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/chat.js +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/jquery-3.5.1.min.js +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/receive.js +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/send.js +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/403.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/404.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/405.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/500.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/chat.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/denied.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/receive.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/send.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/thankyou.html +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/torrc_template +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/wordlist.txt +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/__init__.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/chat_mode.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/receive_mode.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/share_mode.py +0 -0
- {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/website_mode.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: onionshare-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6
|
|
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
|
|
7
7
|
Author-email: micah@micahflee.com
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.7,<4.0
|
|
9
9
|
Classifier: Environment :: Web Environment
|
|
10
10
|
Classifier: Framework :: Flask
|
|
11
11
|
Classifier: Intended Audience :: End Users/Desktop
|
|
@@ -13,24 +13,25 @@ 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
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.7
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
21
|
Classifier: Topic :: Communications :: File Sharing
|
|
22
22
|
Classifier: Topic :: Security :: Cryptography
|
|
23
|
-
Requires-Dist: cepa (==1.8.3)
|
|
24
23
|
Requires-Dist: click
|
|
25
24
|
Requires-Dist: colorama
|
|
26
25
|
Requires-Dist: eventlet
|
|
27
|
-
Requires-Dist: flask (==
|
|
28
|
-
Requires-Dist: flask-socketio (==5.
|
|
26
|
+
Requires-Dist: flask (==2.0.3)
|
|
27
|
+
Requires-Dist: flask-socketio (==5.3.1)
|
|
29
28
|
Requires-Dist: gevent-websocket
|
|
30
29
|
Requires-Dist: psutil
|
|
31
30
|
Requires-Dist: pynacl
|
|
32
31
|
Requires-Dist: pysocks
|
|
33
32
|
Requires-Dist: requests[socks]
|
|
34
33
|
Requires-Dist: setuptools
|
|
34
|
+
Requires-Dist: stem (==1.8.1)
|
|
35
35
|
Requires-Dist: unidecode
|
|
36
36
|
Requires-Dist: urllib3
|
|
37
|
+
Requires-Dist: werkzeug (>=2.0.3,<2.1.0)
|
|
@@ -0,0 +1,311 @@
|
|
|
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
|
+
|
|
23
|
+
class CensorshipCircumventionError(Exception):
|
|
24
|
+
"""
|
|
25
|
+
There was a problem connecting to the Tor CensorshipCircumvention API.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CensorshipCircumvention(object):
|
|
30
|
+
"""
|
|
31
|
+
Connect to the Tor Moat APIs to retrieve censorship
|
|
32
|
+
circumvention recommendations or the latest bridges.
|
|
33
|
+
|
|
34
|
+
We support reaching this API over Tor, or Meek
|
|
35
|
+
(domain fronting) if Tor is not connected.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, common, meek=None, onion=None):
|
|
39
|
+
"""
|
|
40
|
+
Set up the CensorshipCircumvention object to hold
|
|
41
|
+
common and meek objects.
|
|
42
|
+
"""
|
|
43
|
+
self.common = common
|
|
44
|
+
self.common.log("CensorshipCircumvention", "__init__")
|
|
45
|
+
self.api_proxies = {}
|
|
46
|
+
if meek:
|
|
47
|
+
self.meek = meek
|
|
48
|
+
self.common.log(
|
|
49
|
+
"CensorshipCircumvention",
|
|
50
|
+
"__init__",
|
|
51
|
+
"Using Meek with CensorshipCircumvention API",
|
|
52
|
+
)
|
|
53
|
+
self.api_proxies = self.meek.meek_proxies
|
|
54
|
+
if onion:
|
|
55
|
+
self.onion = onion
|
|
56
|
+
if not self.onion.is_authenticated:
|
|
57
|
+
return False
|
|
58
|
+
else:
|
|
59
|
+
self.common.log(
|
|
60
|
+
"CensorshipCircumvention",
|
|
61
|
+
"__init__",
|
|
62
|
+
"Using Tor with CensorshipCircumvention API",
|
|
63
|
+
)
|
|
64
|
+
(socks_address, socks_port) = self.onion.get_tor_socks_port()
|
|
65
|
+
self.api_proxies = {
|
|
66
|
+
"http": f"socks5h://{socks_address}:{socks_port}",
|
|
67
|
+
"https": f"socks5h://{socks_address}:{socks_port}",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def request_map(self, country=False):
|
|
71
|
+
"""
|
|
72
|
+
Retrieves the Circumvention map from Tor Project and store it
|
|
73
|
+
locally for further look-ups if required.
|
|
74
|
+
|
|
75
|
+
Optionally pass a country code in order to get recommended settings
|
|
76
|
+
just for that country.
|
|
77
|
+
|
|
78
|
+
Note that this API endpoint doesn't return actual bridges,
|
|
79
|
+
it just returns the recommended bridge type countries.
|
|
80
|
+
"""
|
|
81
|
+
self.common.log("CensorshipCircumvention", "request_map", f"country={country}")
|
|
82
|
+
if not self.api_proxies:
|
|
83
|
+
return False
|
|
84
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/map"
|
|
85
|
+
data = {}
|
|
86
|
+
if country:
|
|
87
|
+
data = {"country": country}
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
r = requests.post(
|
|
91
|
+
endpoint,
|
|
92
|
+
json=data,
|
|
93
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
94
|
+
proxies=self.api_proxies,
|
|
95
|
+
)
|
|
96
|
+
if r.status_code != 200:
|
|
97
|
+
self.common.log(
|
|
98
|
+
"CensorshipCircumvention",
|
|
99
|
+
"request_map",
|
|
100
|
+
f"status_code={r.status_code}",
|
|
101
|
+
)
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
result = r.json()
|
|
105
|
+
|
|
106
|
+
if "errors" in result:
|
|
107
|
+
self.common.log(
|
|
108
|
+
"CensorshipCircumvention",
|
|
109
|
+
"request_map",
|
|
110
|
+
f"errors={result['errors']}",
|
|
111
|
+
)
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
except requests.exceptions.RequestException as e:
|
|
116
|
+
raise CensorshipCircumventionError(e)
|
|
117
|
+
|
|
118
|
+
def request_settings(self, country=False, transports=False):
|
|
119
|
+
"""
|
|
120
|
+
Retrieves the Circumvention Settings from Tor Project, which
|
|
121
|
+
will return recommended settings based on the country code of
|
|
122
|
+
the requesting IP.
|
|
123
|
+
|
|
124
|
+
Optionally, a country code can be specified in order to override
|
|
125
|
+
the IP detection.
|
|
126
|
+
|
|
127
|
+
Optionally, a list of transports can be specified in order to
|
|
128
|
+
return recommended settings for just that transport type.
|
|
129
|
+
"""
|
|
130
|
+
self.common.log(
|
|
131
|
+
"CensorshipCircumvention",
|
|
132
|
+
"request_settings",
|
|
133
|
+
f"country={country}, transports={transports}",
|
|
134
|
+
)
|
|
135
|
+
if not self.api_proxies:
|
|
136
|
+
return False
|
|
137
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/settings"
|
|
138
|
+
data = {}
|
|
139
|
+
if country:
|
|
140
|
+
self.common.log(
|
|
141
|
+
"CensorshipCircumvention",
|
|
142
|
+
"request_settings",
|
|
143
|
+
f"Trying to obtain bridges for country={country}",
|
|
144
|
+
)
|
|
145
|
+
data = {"country": country}
|
|
146
|
+
if transports:
|
|
147
|
+
data.append({"transports": transports})
|
|
148
|
+
try:
|
|
149
|
+
r = requests.post(
|
|
150
|
+
endpoint,
|
|
151
|
+
json=data,
|
|
152
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
153
|
+
proxies=self.api_proxies,
|
|
154
|
+
)
|
|
155
|
+
if r.status_code != 200:
|
|
156
|
+
self.common.log(
|
|
157
|
+
"CensorshipCircumvention",
|
|
158
|
+
"request_settings",
|
|
159
|
+
f"status_code={r.status_code}",
|
|
160
|
+
)
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
result = r.json()
|
|
164
|
+
self.common.log(
|
|
165
|
+
"CensorshipCircumvention",
|
|
166
|
+
"request_settings",
|
|
167
|
+
f"result={result}",
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if "errors" in result:
|
|
171
|
+
self.common.log(
|
|
172
|
+
"CensorshipCircumvention",
|
|
173
|
+
"request_settings",
|
|
174
|
+
f"errors={result['errors']}",
|
|
175
|
+
)
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
# There are no settings - perhaps this country doesn't require censorship circumvention?
|
|
179
|
+
# This is not really an error, so we can just check if False and assume direct Tor
|
|
180
|
+
# connection will work.
|
|
181
|
+
if not "settings" in result or result["settings"] is None:
|
|
182
|
+
self.common.log(
|
|
183
|
+
"CensorshipCircumvention",
|
|
184
|
+
"request_settings",
|
|
185
|
+
"No settings found for this country",
|
|
186
|
+
)
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
return result
|
|
190
|
+
except requests.exceptions.RequestException as e:
|
|
191
|
+
raise CensorshipCircumventionError(e)
|
|
192
|
+
|
|
193
|
+
def request_builtin_bridges(self):
|
|
194
|
+
"""
|
|
195
|
+
Retrieves the list of built-in bridges from the Tor Project.
|
|
196
|
+
"""
|
|
197
|
+
if not self.api_proxies:
|
|
198
|
+
return False
|
|
199
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/builtin"
|
|
200
|
+
try:
|
|
201
|
+
r = requests.post(
|
|
202
|
+
endpoint,
|
|
203
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
204
|
+
proxies=self.api_proxies,
|
|
205
|
+
)
|
|
206
|
+
if r.status_code != 200:
|
|
207
|
+
self.common.log(
|
|
208
|
+
"CensorshipCircumvention",
|
|
209
|
+
"request_builtin_bridges",
|
|
210
|
+
f"status_code={r.status_code}",
|
|
211
|
+
)
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
result = r.json()
|
|
215
|
+
|
|
216
|
+
if "errors" in result:
|
|
217
|
+
self.common.log(
|
|
218
|
+
"CensorshipCircumvention",
|
|
219
|
+
"request_builtin_bridges",
|
|
220
|
+
f"errors={result['errors']}",
|
|
221
|
+
)
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
return result
|
|
225
|
+
except requests.exceptions.RequestException as e:
|
|
226
|
+
raise CensorshipCircumventionError(e)
|
|
227
|
+
|
|
228
|
+
def save_settings(self, settings, bridge_settings):
|
|
229
|
+
"""
|
|
230
|
+
Checks the bridges and saves them in settings.
|
|
231
|
+
"""
|
|
232
|
+
self.common.log(
|
|
233
|
+
"CensorshipCircumvention",
|
|
234
|
+
"save_settings",
|
|
235
|
+
f"bridge_settings: {bridge_settings}",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
bridges_ok = False
|
|
239
|
+
self.settings = settings
|
|
240
|
+
|
|
241
|
+
# @TODO there might be several bridge types recommended.
|
|
242
|
+
# Should we attempt to iterate over each type if one of them fails to connect?
|
|
243
|
+
# But if so, how to stop it starting 3 separate Tor connection threads?
|
|
244
|
+
# for bridges in request_bridges["settings"]:
|
|
245
|
+
bridges = bridge_settings["settings"][0]["bridges"]
|
|
246
|
+
bridge_strings = bridges["bridge_strings"]
|
|
247
|
+
|
|
248
|
+
self.settings.set("bridges_type", "custom")
|
|
249
|
+
|
|
250
|
+
# Sanity check the bridges provided from the Tor API before saving
|
|
251
|
+
bridges_checked = self.common.check_bridges_valid(bridge_strings)
|
|
252
|
+
|
|
253
|
+
if bridges_checked:
|
|
254
|
+
self.settings.set("bridges_custom", "\n".join(bridges_checked))
|
|
255
|
+
bridges_ok = True
|
|
256
|
+
|
|
257
|
+
# If we got any good bridges, save them to settings and return.
|
|
258
|
+
if bridges_ok:
|
|
259
|
+
self.common.log(
|
|
260
|
+
"CensorshipCircumvention",
|
|
261
|
+
"save_settings",
|
|
262
|
+
"Saving settings with automatically-obtained bridges",
|
|
263
|
+
)
|
|
264
|
+
self.settings.set("bridges_enabled", True)
|
|
265
|
+
self.settings.save()
|
|
266
|
+
return True
|
|
267
|
+
else:
|
|
268
|
+
self.common.log(
|
|
269
|
+
"CensorshipCircumvention",
|
|
270
|
+
"save_settings",
|
|
271
|
+
"Could not use any of the obtained bridges.",
|
|
272
|
+
)
|
|
273
|
+
return False
|
|
274
|
+
|
|
275
|
+
def request_default_bridges(self):
|
|
276
|
+
"""
|
|
277
|
+
Retrieves the list of default fall-back bridges from the Tor Project.
|
|
278
|
+
|
|
279
|
+
These are intended for when no censorship settings were found for a
|
|
280
|
+
specific country, but maybe there was some connection issue anyway.
|
|
281
|
+
"""
|
|
282
|
+
if not self.api_proxies:
|
|
283
|
+
return False
|
|
284
|
+
endpoint = "https://bridges.torproject.org/moat/circumvention/defaults"
|
|
285
|
+
try:
|
|
286
|
+
r = requests.get(
|
|
287
|
+
endpoint,
|
|
288
|
+
headers={"Content-Type": "application/vnd.api+json"},
|
|
289
|
+
proxies=self.api_proxies,
|
|
290
|
+
)
|
|
291
|
+
if r.status_code != 200:
|
|
292
|
+
self.common.log(
|
|
293
|
+
"CensorshipCircumvention",
|
|
294
|
+
"request_default_bridges",
|
|
295
|
+
f"status_code={r.status_code}",
|
|
296
|
+
)
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
result = r.json()
|
|
300
|
+
|
|
301
|
+
if "errors" in result:
|
|
302
|
+
self.common.log(
|
|
303
|
+
"CensorshipCircumvention",
|
|
304
|
+
"request_default_bridges",
|
|
305
|
+
f"errors={result['errors']}",
|
|
306
|
+
)
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
return result
|
|
310
|
+
except requests.exceptions.RequestException as e:
|
|
311
|
+
raise CensorshipCircumventionError(e)
|
|
@@ -28,6 +28,7 @@ import sys
|
|
|
28
28
|
import threading
|
|
29
29
|
import time
|
|
30
30
|
import shutil
|
|
31
|
+
import re
|
|
31
32
|
from pkg_resources import resource_filename
|
|
32
33
|
|
|
33
34
|
import colorama
|
|
@@ -312,7 +313,6 @@ class Common:
|
|
|
312
313
|
"""
|
|
313
314
|
Returns the absolute path of a resource
|
|
314
315
|
"""
|
|
315
|
-
self.log("Common", "get_resource_path", f"filename={filename}")
|
|
316
316
|
path = resource_filename("onionshare_cli", os.path.join("resources", filename))
|
|
317
317
|
self.log("Common", "get_resource_path", f"filename={filename}, path={path}")
|
|
318
318
|
return path
|
|
@@ -332,21 +332,27 @@ class Common:
|
|
|
332
332
|
# In Windows, the Tor binaries are in the onionshare package, not the onionshare_cli package
|
|
333
333
|
base_path = self.get_resource_path("tor")
|
|
334
334
|
base_path = base_path.replace("onionshare_cli", "onionshare")
|
|
335
|
-
tor_path = os.path.join(base_path, "
|
|
335
|
+
tor_path = os.path.join(base_path, "tor.exe")
|
|
336
336
|
|
|
337
337
|
# If tor.exe isn't there, mayber we're running from the source tree
|
|
338
338
|
if not os.path.exists(tor_path):
|
|
339
|
+
self.log(
|
|
340
|
+
"Common", "get_tor_paths", f"Cannot find tor.exe at {tor_path}"
|
|
341
|
+
)
|
|
339
342
|
base_path = os.path.join(os.getcwd(), "onionshare", "resources", "tor")
|
|
340
343
|
|
|
341
|
-
tor_path = os.path.join(base_path, "
|
|
344
|
+
tor_path = os.path.join(base_path, "tor.exe")
|
|
342
345
|
if not os.path.exists(tor_path):
|
|
346
|
+
self.log(
|
|
347
|
+
"Common", "get_tor_paths", f"Cannot find tor.exe at {tor_path}"
|
|
348
|
+
)
|
|
343
349
|
raise CannotFindTor()
|
|
344
350
|
|
|
345
|
-
obfs4proxy_file_path = os.path.join(base_path, "
|
|
346
|
-
snowflake_file_path = os.path.join(base_path, "
|
|
347
|
-
meek_client_file_path = os.path.join(base_path, "
|
|
348
|
-
tor_geo_ip_file_path = os.path.join(base_path, "
|
|
349
|
-
tor_geo_ipv6_file_path = os.path.join(base_path, "
|
|
351
|
+
obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy.exe")
|
|
352
|
+
snowflake_file_path = os.path.join(base_path, "snowflake-client.exe")
|
|
353
|
+
meek_client_file_path = os.path.join(base_path, "meek-client.exe")
|
|
354
|
+
tor_geo_ip_file_path = os.path.join(base_path, "geoip")
|
|
355
|
+
tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6")
|
|
350
356
|
|
|
351
357
|
elif self.platform == "Darwin":
|
|
352
358
|
# Let's see if we have tor binaries in the onionshare GUI package
|
|
@@ -467,6 +473,40 @@ class Common:
|
|
|
467
473
|
r = random.SystemRandom()
|
|
468
474
|
return "-".join(r.choice(wordlist) for _ in range(word_count))
|
|
469
475
|
|
|
476
|
+
def check_bridges_valid(self, bridges):
|
|
477
|
+
"""
|
|
478
|
+
Does a regex check against a supplied list of bridges, to make sure they
|
|
479
|
+
are valid strings depending on the bridge type.
|
|
480
|
+
"""
|
|
481
|
+
valid_bridges = []
|
|
482
|
+
self.log("Common", "check_bridges_valid", "Checking bridge syntax")
|
|
483
|
+
for bridge in bridges:
|
|
484
|
+
if bridge != "":
|
|
485
|
+
# Check the syntax of the custom bridge to make sure it looks legitimate
|
|
486
|
+
ipv4_pattern = re.compile(
|
|
487
|
+
"(obfs4\s+)?(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]):([0-9]+)(\s+)([A-Z0-9]+)(.+)$"
|
|
488
|
+
)
|
|
489
|
+
ipv6_pattern = re.compile(
|
|
490
|
+
"(obfs4\s+)?\[(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\]:[0-9]+\s+[A-Z0-9]+(.+)$"
|
|
491
|
+
)
|
|
492
|
+
meek_lite_pattern = re.compile(
|
|
493
|
+
"(meek_lite)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)(\s)+url=(.+)(\s)+front=(.+)"
|
|
494
|
+
)
|
|
495
|
+
snowflake_pattern = re.compile(
|
|
496
|
+
"(snowflake)(\s)+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+)(\s)+([0-9A-Z]+)"
|
|
497
|
+
)
|
|
498
|
+
if (
|
|
499
|
+
ipv4_pattern.match(bridge)
|
|
500
|
+
or ipv6_pattern.match(bridge)
|
|
501
|
+
or meek_lite_pattern.match(bridge)
|
|
502
|
+
or snowflake_pattern.match(bridge)
|
|
503
|
+
):
|
|
504
|
+
valid_bridges.append(bridge)
|
|
505
|
+
if valid_bridges:
|
|
506
|
+
return valid_bridges
|
|
507
|
+
else:
|
|
508
|
+
return False
|
|
509
|
+
|
|
470
510
|
def is_flatpak(self):
|
|
471
511
|
"""
|
|
472
512
|
Returns True if OnionShare is running in a Flatpak sandbox
|
|
@@ -69,7 +69,7 @@ class Meek(object):
|
|
|
69
69
|
if self.meek_client_file_path is None or not os.path.exists(
|
|
70
70
|
self.meek_client_file_path
|
|
71
71
|
):
|
|
72
|
-
raise MeekNotFound()
|
|
72
|
+
raise MeekNotFound(self.common)
|
|
73
73
|
|
|
74
74
|
# Start the Meek Client as a subprocess.
|
|
75
75
|
self.common.log("Meek", "start", "Starting meek client")
|
|
@@ -128,7 +128,7 @@ class Meek(object):
|
|
|
128
128
|
|
|
129
129
|
if "CMETHOD-ERROR" in line:
|
|
130
130
|
self.cleanup()
|
|
131
|
-
raise MeekNotRunning()
|
|
131
|
+
raise MeekNotRunning(self.common, line)
|
|
132
132
|
break
|
|
133
133
|
|
|
134
134
|
if self.meek_port:
|
|
@@ -137,9 +137,8 @@ class Meek(object):
|
|
|
137
137
|
"https": f"socks5h://{self.meek_host}:{self.meek_port}",
|
|
138
138
|
}
|
|
139
139
|
else:
|
|
140
|
-
self.common.log("Meek", "start", "Could not obtain the meek port")
|
|
141
140
|
self.cleanup()
|
|
142
|
-
raise MeekNotRunning()
|
|
141
|
+
raise MeekNotRunning(self.common, "Could not obtain the meek port")
|
|
143
142
|
|
|
144
143
|
def cleanup(self):
|
|
145
144
|
"""
|
|
@@ -182,8 +181,19 @@ class MeekNotRunning(Exception):
|
|
|
182
181
|
number it started on, in order to do domain fronting.
|
|
183
182
|
"""
|
|
184
183
|
|
|
184
|
+
def __init__(self, common, info=None):
|
|
185
|
+
self.common = common
|
|
186
|
+
msg = "Meek experienced an error starting up"
|
|
187
|
+
if info:
|
|
188
|
+
msg = msg + f": {info}"
|
|
189
|
+
self.common.log("MeekNotRunning", "__init__", msg)
|
|
190
|
+
|
|
185
191
|
|
|
186
192
|
class MeekNotFound(Exception):
|
|
187
193
|
"""
|
|
188
194
|
We were unable to find the Meek Client binary.
|
|
189
195
|
"""
|
|
196
|
+
|
|
197
|
+
def __init__(self, common):
|
|
198
|
+
self.common = common
|
|
199
|
+
self.common.log("MeekNotFound", "__init__", "Could not find the meek binary")
|
|
@@ -954,20 +954,6 @@ class Onion(object):
|
|
|
954
954
|
"update_builtin_bridges",
|
|
955
955
|
f"Obtained bridges: {builtin_bridges}",
|
|
956
956
|
)
|
|
957
|
-
if builtin_bridges["meek"]:
|
|
958
|
-
# Meek bridge needs to be defined as "meek_lite", not "meek",
|
|
959
|
-
# for it to work with obfs4proxy.
|
|
960
|
-
# We also refer to this bridge type as 'meek-azure' in our settings.
|
|
961
|
-
# So first, rename the key in the dict
|
|
962
|
-
builtin_bridges["meek-azure"] = builtin_bridges.pop("meek")
|
|
963
|
-
new_meek_bridges = []
|
|
964
|
-
# Now replace the values. They also need the url/front params appended
|
|
965
|
-
for item in builtin_bridges["meek-azure"]:
|
|
966
|
-
newline = item.replace("meek", "meek_lite")
|
|
967
|
-
new_meek_bridges.append(
|
|
968
|
-
f"{newline} url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com"
|
|
969
|
-
)
|
|
970
|
-
builtin_bridges["meek-azure"] = new_meek_bridges
|
|
971
957
|
# Save the new settings
|
|
972
958
|
self.settings.set("bridges_builtin", builtin_bridges)
|
|
973
959
|
self.settings.save()
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
body {
|
|
11
11
|
margin: 0;
|
|
12
12
|
font-family: Helvetica, sans-serif;
|
|
13
|
+
background-color: #fafafa;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
.d-flex {
|
|
@@ -21,15 +22,14 @@ body {
|
|
|
21
22
|
|
|
22
23
|
header {
|
|
23
24
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
24
|
-
background: #
|
|
25
|
-
|
|
26
|
-
padding: 0.8rem;
|
|
25
|
+
background: #fff;
|
|
26
|
+
padding: 1rem;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
header .logo {
|
|
30
30
|
vertical-align: middle;
|
|
31
|
-
width:
|
|
32
|
-
height:
|
|
31
|
+
width: 3rem;
|
|
32
|
+
height: 3rem;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
header h1 {
|
|
@@ -37,23 +37,26 @@ header h1 {
|
|
|
37
37
|
margin: 0 0 0 0.5rem;
|
|
38
38
|
vertical-align: middle;
|
|
39
39
|
font-weight: normal;
|
|
40
|
-
font-size: 1.
|
|
41
|
-
color: #
|
|
40
|
+
font-size: 1.375rem;
|
|
41
|
+
color: #333;
|
|
42
|
+
position: relative;
|
|
43
|
+
top: .125rem;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
header .information {
|
|
45
|
-
font-size:
|
|
47
|
+
font-size: .875rem;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
.button {
|
|
49
51
|
color: #ffffff;
|
|
50
52
|
background-color: #4e064f;
|
|
51
|
-
padding:
|
|
53
|
+
padding: .875rem 1rem;
|
|
52
54
|
border: 0;
|
|
53
|
-
border-radius:
|
|
55
|
+
border-radius: .25rem;
|
|
54
56
|
text-decoration: none;
|
|
55
57
|
margin-left: 1rem;
|
|
56
58
|
cursor: pointer;
|
|
59
|
+
font-weight: bold;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
a.button:visited {
|
|
@@ -102,6 +105,9 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
|
|
|
102
105
|
width: 100%;
|
|
103
106
|
margin: 0 auto;
|
|
104
107
|
border-collapse: collapse;
|
|
108
|
+
font-size: .875rem;
|
|
109
|
+
color: #333;
|
|
110
|
+
background-color: #fff;
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
.file-list .heading {
|
|
@@ -109,7 +115,9 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
|
|
|
109
115
|
text-transform: uppercase;
|
|
110
116
|
font-weight: normal;
|
|
111
117
|
color: #666666;
|
|
112
|
-
padding: 0.
|
|
118
|
+
padding: 0.75rem;
|
|
119
|
+
font-size: .75rem;
|
|
120
|
+
letter-spacing: .0625rem;
|
|
113
121
|
}
|
|
114
122
|
|
|
115
123
|
.file-list div.d-flex {
|
|
@@ -119,16 +127,18 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
|
|
|
119
127
|
|
|
120
128
|
.file-list div.d-flex div {
|
|
121
129
|
white-space: nowrap;
|
|
122
|
-
padding:
|
|
130
|
+
padding: .625rem .75rem;
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
.file-list div.d-flex div img {
|
|
126
134
|
vertical-align: middle;
|
|
127
135
|
margin-right: 0.5rem;
|
|
136
|
+
width: 1.25rem;
|
|
137
|
+
height: 1.25rem;
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
.file-list div.d-flex div:last-child {
|
|
131
|
-
padding-right: 0.
|
|
141
|
+
padding-right: 0.75rem;
|
|
132
142
|
flex-basis: 0;
|
|
133
143
|
flex-grow: 1;
|
|
134
144
|
}
|
|
@@ -146,6 +156,12 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
|
|
|
146
156
|
}
|
|
147
157
|
|
|
148
158
|
@media (max-width: 655px) {
|
|
159
|
+
.header {
|
|
160
|
+
padding: 0 1rem;
|
|
161
|
+
}
|
|
162
|
+
.file-list {
|
|
163
|
+
font-size: .9375rem;
|
|
164
|
+
}
|
|
149
165
|
.file-list div.d-flex {
|
|
150
166
|
display: block;
|
|
151
167
|
}
|
|
@@ -153,17 +169,32 @@ ul.breadcrumbs li a:link, ul.breadcrumbs li a:visited {
|
|
|
153
169
|
max-width: 100%;
|
|
154
170
|
word-break: break-all;
|
|
155
171
|
white-space: normal;
|
|
172
|
+
vertical-align: top;
|
|
156
173
|
}
|
|
157
174
|
.file-list div.d-flex #size-header {
|
|
158
175
|
display: none;
|
|
159
176
|
}
|
|
160
177
|
.file-list div.d-flex div:last-child {
|
|
161
|
-
padding-left: 3.
|
|
162
|
-
font-size: 0.
|
|
178
|
+
padding-left: 3.75rem;
|
|
179
|
+
font-size: 0.75rem;
|
|
163
180
|
padding-top: 0;
|
|
181
|
+
margin-top: -.5rem;
|
|
182
|
+
color: #666;
|
|
183
|
+
}
|
|
184
|
+
.d-flex .row div:first-of-type {
|
|
185
|
+
padding-bottom: 0;
|
|
164
186
|
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@media (max-width: 500px) {
|
|
165
190
|
header .information {
|
|
166
|
-
|
|
191
|
+
width: 100%;
|
|
192
|
+
align-items: end;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.file-list div.d-flex div {
|
|
196
|
+
white-space: nowrap;
|
|
197
|
+
padding: .625rem 1rem;
|
|
167
198
|
}
|
|
168
199
|
}
|
|
169
200
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|