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.
Files changed (55) hide show
  1. {onionshare_cli-2.5 → onionshare_cli-2.6}/PKG-INFO +8 -7
  2. onionshare_cli-2.6/onionshare_cli/censorship.py +311 -0
  3. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/common.py +48 -8
  4. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/meek.py +14 -4
  5. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/onion.py +0 -14
  6. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/css/style.css +47 -16
  7. onionshare_cli-2.6/onionshare_cli/resources/static/img/favicon.ico +0 -0
  8. onionshare_cli-2.6/onionshare_cli/resources/static/img/logo.png +0 -0
  9. onionshare_cli-2.6/onionshare_cli/resources/static/img/logo_large.png +0 -0
  10. onionshare_cli-2.6/onionshare_cli/resources/static/js/socket.io.js +9 -0
  11. onionshare_cli-2.6/onionshare_cli/resources/static/js/socket.io.min.js +7 -0
  12. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/listing.html +2 -2
  13. onionshare_cli-2.6/onionshare_cli/resources/torrc_template-meek_lite_azure +2 -0
  14. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/torrc_template-obfs4 +0 -1
  15. onionshare_cli-2.6/onionshare_cli/resources/torrc_template-snowflake +2 -0
  16. onionshare_cli-2.6/onionshare_cli/resources/version.txt +1 -0
  17. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/settings.py +9 -7
  18. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/send_base_mode.py +8 -10
  19. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/web.py +1 -7
  20. {onionshare_cli-2.5 → onionshare_cli-2.6}/pyproject.toml +6 -6
  21. {onionshare_cli-2.5 → onionshare_cli-2.6}/setup.py +12 -11
  22. onionshare_cli-2.5/onionshare_cli/censorship.py +0 -200
  23. onionshare_cli-2.5/onionshare_cli/resources/static/img/favicon.ico +0 -0
  24. onionshare_cli-2.5/onionshare_cli/resources/static/img/logo.png +0 -0
  25. onionshare_cli-2.5/onionshare_cli/resources/static/img/logo_large.png +0 -0
  26. onionshare_cli-2.5/onionshare_cli/resources/static/js/socket.io.min.js +0 -7
  27. onionshare_cli-2.5/onionshare_cli/resources/torrc_template-meek_lite_azure +0 -2
  28. onionshare_cli-2.5/onionshare_cli/resources/torrc_template-snowflake +0 -2
  29. onionshare_cli-2.5/onionshare_cli/resources/version.txt +0 -1
  30. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/__init__.py +0 -0
  31. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/mode_settings.py +0 -0
  32. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/onionshare.py +0 -0
  33. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/img/ajax.gif +0 -0
  34. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/img/web_file.png +0 -0
  35. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/img/web_folder.png +0 -0
  36. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/chat.js +0 -0
  37. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/jquery-3.5.1.min.js +0 -0
  38. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/receive.js +0 -0
  39. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/static/js/send.js +0 -0
  40. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/403.html +0 -0
  41. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/404.html +0 -0
  42. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/405.html +0 -0
  43. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/500.html +0 -0
  44. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/chat.html +0 -0
  45. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/denied.html +0 -0
  46. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/receive.html +0 -0
  47. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/send.html +0 -0
  48. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/templates/thankyou.html +0 -0
  49. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/torrc_template +0 -0
  50. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/resources/wordlist.txt +0 -0
  51. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/__init__.py +0 -0
  52. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/chat_mode.py +0 -0
  53. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/receive_mode.py +0 -0
  54. {onionshare_cli-2.5 → onionshare_cli-2.6}/onionshare_cli/web/share_mode.py +0 -0
  55. {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.5
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.6,<4.0
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 (==1.1.4)
28
- Requires-Dist: flask-socketio (==5.0.1)
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, "Tor", "tor.exe")
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, "Tor", "tor.exe")
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, "Tor", "obfs4proxy.exe")
346
- snowflake_file_path = os.path.join(base_path, "Tor", "snowflake-client.exe")
347
- meek_client_file_path = os.path.join(base_path, "Tor", "meek-client.exe")
348
- tor_geo_ip_file_path = os.path.join(base_path, "Data", "Tor", "geoip")
349
- tor_geo_ipv6_file_path = os.path.join(base_path, "Data", "Tor", "geoip6")
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: #fcfcfc;
25
- background: -webkit-linear-gradient(top, #fcfcfc 0%, #f2f2f2 100%);
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: 45px;
32
- height: 45px;
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.5rem;
41
- color: #666666;
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: 1rem;
47
+ font-size: .875rem;
46
48
  }
47
49
 
48
50
  .button {
49
51
  color: #ffffff;
50
52
  background-color: #4e064f;
51
- padding: 10px;
53
+ padding: .875rem 1rem;
52
54
  border: 0;
53
- border-radius: 5px;
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.5rem;
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: 0.5rem 0.8rem 0.5rem 0.8rem;
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.8rem;
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.5rem;
162
- font-size: 0.85rem;
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
- padding: 1rem 0;
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