onionshare_cli 2.6.1__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 +538 -0
- onionshare_cli/censorship.py +311 -0
- onionshare_cli/common.py +624 -0
- onionshare_cli/meek.py +199 -0
- onionshare_cli/mode_settings.py +150 -0
- onionshare_cli/onion.py +964 -0
- onionshare_cli/onionshare.py +91 -0
- onionshare_cli/resources/static/css/style.css +432 -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 +188 -0
- onionshare_cli/resources/static/js/jquery-3.5.1.min.js +2 -0
- onionshare_cli/resources/static/js/receive.js +141 -0
- onionshare_cli/resources/static/js/send.js +79 -0
- onionshare_cli/resources/static/js/socket.io.js +9 -0
- onionshare_cli/resources/static/js/socket.io.min.js +7 -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/500.html +21 -0
- onionshare_cli/resources/templates/chat.html +47 -0
- onionshare_cli/resources/templates/denied.html +14 -0
- onionshare_cli/resources/templates/listing.html +60 -0
- onionshare_cli/resources/templates/receive.html +66 -0
- onionshare_cli/resources/templates/send.html +70 -0
- onionshare_cli/resources/templates/thankyou.html +27 -0
- onionshare_cli/resources/torrc_template +12 -0
- onionshare_cli/resources/torrc_template-meek_lite_azure +2 -0
- onionshare_cli/resources/torrc_template-obfs4 +12 -0
- onionshare_cli/resources/torrc_template-snowflake +3 -0
- onionshare_cli/resources/version.txt +1 -0
- onionshare_cli/resources/wordlist.txt +7776 -0
- onionshare_cli/settings.py +209 -0
- onionshare_cli/web/__init__.py +21 -0
- onionshare_cli/web/chat_mode.py +190 -0
- onionshare_cli/web/receive_mode.py +630 -0
- onionshare_cli/web/send_base_mode.py +360 -0
- onionshare_cli/web/share_mode.py +599 -0
- onionshare_cli/web/web.py +409 -0
- onionshare_cli/web/website_mode.py +122 -0
- onionshare_cli-2.6.1.dist-info/METADATA +44 -0
- onionshare_cli-2.6.1.dist-info/RECORD +48 -0
- onionshare_cli-2.6.1.dist-info/WHEEL +4 -0
- onionshare_cli-2.6.1.dist-info/entry_points.txt +3 -0
onionshare_cli/meek.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
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 os
|
|
21
|
+
import subprocess
|
|
22
|
+
import time
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Meek(object):
|
|
26
|
+
"""
|
|
27
|
+
The Meek object starts the meek-client as a subprocess.
|
|
28
|
+
This process is used to do domain-fronting to connect to
|
|
29
|
+
the Tor APIs for censorship circumvention and retrieving
|
|
30
|
+
bridges, before connecting to Tor.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, common, get_tor_paths=None):
|
|
34
|
+
"""
|
|
35
|
+
Set up the Meek object
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
self.common = common
|
|
39
|
+
self.common.log("Meek", "__init__")
|
|
40
|
+
|
|
41
|
+
# Set the path of the meek binary
|
|
42
|
+
if not get_tor_paths:
|
|
43
|
+
get_tor_paths = self.common.get_tor_paths
|
|
44
|
+
(
|
|
45
|
+
self.tor_path,
|
|
46
|
+
self.tor_geo_ip_file_path,
|
|
47
|
+
self.tor_geo_ipv6_file_path,
|
|
48
|
+
self.obfs4proxy_file_path,
|
|
49
|
+
self.snowflake_file_path,
|
|
50
|
+
self.meek_client_file_path,
|
|
51
|
+
) = get_tor_paths()
|
|
52
|
+
|
|
53
|
+
self.meek_proxies = {}
|
|
54
|
+
self.meek_url = "https://moat.torproject.org.global.prod.fastly.net/"
|
|
55
|
+
self.meek_front = "cdn.sstatic.net"
|
|
56
|
+
self.meek_env = {
|
|
57
|
+
"TOR_PT_MANAGED_TRANSPORT_VER": "1",
|
|
58
|
+
"TOR_PT_CLIENT_TRANSPORTS": "meek",
|
|
59
|
+
}
|
|
60
|
+
self.meek_host = "127.0.0.1"
|
|
61
|
+
self.meek_port = None
|
|
62
|
+
|
|
63
|
+
def start(self):
|
|
64
|
+
"""
|
|
65
|
+
Start the Meek Client and populate the SOCKS proxies dict
|
|
66
|
+
for use with requests to the Tor Moat API.
|
|
67
|
+
"""
|
|
68
|
+
# Abort early if we can't find the Meek client
|
|
69
|
+
if self.meek_client_file_path is None or not os.path.exists(
|
|
70
|
+
self.meek_client_file_path
|
|
71
|
+
):
|
|
72
|
+
raise MeekNotFound(self.common)
|
|
73
|
+
|
|
74
|
+
# Start the Meek Client as a subprocess.
|
|
75
|
+
self.common.log("Meek", "start", "Starting meek client")
|
|
76
|
+
|
|
77
|
+
if self.common.platform == "Windows":
|
|
78
|
+
env = os.environ.copy()
|
|
79
|
+
for key in self.meek_env:
|
|
80
|
+
env[key] = self.meek_env[key]
|
|
81
|
+
|
|
82
|
+
# In Windows, hide console window when opening meek-client.exe subprocess
|
|
83
|
+
startupinfo = subprocess.STARTUPINFO()
|
|
84
|
+
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
85
|
+
self.meek_proc = subprocess.Popen(
|
|
86
|
+
[
|
|
87
|
+
self.meek_client_file_path,
|
|
88
|
+
"--url",
|
|
89
|
+
self.meek_url,
|
|
90
|
+
"--front",
|
|
91
|
+
self.meek_front,
|
|
92
|
+
],
|
|
93
|
+
stdout=subprocess.PIPE,
|
|
94
|
+
stderr=subprocess.PIPE,
|
|
95
|
+
startupinfo=startupinfo,
|
|
96
|
+
bufsize=1,
|
|
97
|
+
env=env,
|
|
98
|
+
text=True,
|
|
99
|
+
)
|
|
100
|
+
else:
|
|
101
|
+
self.meek_proc = subprocess.Popen(
|
|
102
|
+
[
|
|
103
|
+
self.meek_client_file_path,
|
|
104
|
+
"--url",
|
|
105
|
+
self.meek_url,
|
|
106
|
+
"--front",
|
|
107
|
+
self.meek_front,
|
|
108
|
+
],
|
|
109
|
+
stdout=subprocess.PIPE,
|
|
110
|
+
stderr=subprocess.PIPE,
|
|
111
|
+
bufsize=1,
|
|
112
|
+
env=self.meek_env,
|
|
113
|
+
# Using universal_newlines instead of text because the snap package using python < 3.7
|
|
114
|
+
universal_newlines=True,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Obtain the host and port that meek is running on
|
|
118
|
+
for line in iter(self.meek_proc.stdout.readline, b""):
|
|
119
|
+
if "CMETHOD meek socks5" in line:
|
|
120
|
+
self.meek_host = line.split(" ")[3].split(":")[0]
|
|
121
|
+
self.meek_port = line.split(" ")[3].split(":")[1]
|
|
122
|
+
self.common.log(
|
|
123
|
+
"Meek",
|
|
124
|
+
"start",
|
|
125
|
+
f"Meek running on {self.meek_host}:{self.meek_port}",
|
|
126
|
+
)
|
|
127
|
+
break
|
|
128
|
+
|
|
129
|
+
if "CMETHOD-ERROR" in line:
|
|
130
|
+
self.cleanup()
|
|
131
|
+
raise MeekNotRunning(self.common, line)
|
|
132
|
+
break
|
|
133
|
+
|
|
134
|
+
if self.meek_port:
|
|
135
|
+
self.meek_proxies = {
|
|
136
|
+
"http": f"socks5h://{self.meek_host}:{self.meek_port}",
|
|
137
|
+
"https": f"socks5h://{self.meek_host}:{self.meek_port}",
|
|
138
|
+
}
|
|
139
|
+
else:
|
|
140
|
+
self.cleanup()
|
|
141
|
+
raise MeekNotRunning(self.common, "Could not obtain the meek port")
|
|
142
|
+
|
|
143
|
+
def cleanup(self):
|
|
144
|
+
"""
|
|
145
|
+
Kill any meek subprocesses.
|
|
146
|
+
"""
|
|
147
|
+
self.common.log("Meek", "cleanup")
|
|
148
|
+
|
|
149
|
+
if self.meek_proc:
|
|
150
|
+
self.meek_proc.terminate()
|
|
151
|
+
time.sleep(0.2)
|
|
152
|
+
if self.meek_proc.poll() is None:
|
|
153
|
+
self.common.log(
|
|
154
|
+
"Meek",
|
|
155
|
+
"cleanup",
|
|
156
|
+
"Tried to terminate meek-client process but it's still running",
|
|
157
|
+
)
|
|
158
|
+
try:
|
|
159
|
+
self.meek_proc.kill()
|
|
160
|
+
time.sleep(0.2)
|
|
161
|
+
if self.meek_proc.poll() is None:
|
|
162
|
+
self.common.log(
|
|
163
|
+
"Meek",
|
|
164
|
+
"cleanup",
|
|
165
|
+
"Tried to kill meek-client process but it's still running",
|
|
166
|
+
)
|
|
167
|
+
except Exception:
|
|
168
|
+
self.common.log(
|
|
169
|
+
"Meek", "cleanup", "Exception while killing meek-client process"
|
|
170
|
+
)
|
|
171
|
+
self.meek_proc = None
|
|
172
|
+
|
|
173
|
+
# Reset other Meek settings
|
|
174
|
+
self.meek_proxies = {}
|
|
175
|
+
self.meek_port = None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class MeekNotRunning(Exception):
|
|
179
|
+
"""
|
|
180
|
+
We were unable to start Meek or obtain the port
|
|
181
|
+
number it started on, in order to do domain fronting.
|
|
182
|
+
"""
|
|
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
|
+
|
|
191
|
+
|
|
192
|
+
class MeekNotFound(Exception):
|
|
193
|
+
"""
|
|
194
|
+
We were unable to find the Meek Client binary.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(self, common):
|
|
198
|
+
self.common = common
|
|
199
|
+
self.common.log("MeekNotFound", "__init__", "Could not find the meek binary")
|
|
@@ -0,0 +1,150 @@
|
|
|
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 os
|
|
21
|
+
import json
|
|
22
|
+
import platform
|
|
23
|
+
|
|
24
|
+
if platform.system() == "Darwin":
|
|
25
|
+
import pwd
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ModeSettings:
|
|
29
|
+
"""
|
|
30
|
+
This stores the settings for a single instance of an OnionShare mode. In CLI there
|
|
31
|
+
is only one ModeSettings, and in the GUI there is a separate ModeSettings for each tab
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, common, filename=None, id=None):
|
|
35
|
+
self.common = common
|
|
36
|
+
|
|
37
|
+
self.default_settings = {
|
|
38
|
+
"onion": {
|
|
39
|
+
"private_key": None,
|
|
40
|
+
"client_auth_priv_key": None,
|
|
41
|
+
"client_auth_pub_key": None,
|
|
42
|
+
},
|
|
43
|
+
"persistent": {"mode": None, "enabled": False},
|
|
44
|
+
"general": {
|
|
45
|
+
"title": None,
|
|
46
|
+
"public": False,
|
|
47
|
+
"autostart_timer": False,
|
|
48
|
+
"autostop_timer": False,
|
|
49
|
+
"service_id": None,
|
|
50
|
+
},
|
|
51
|
+
"share": {"autostop_sharing": True, "filenames": []},
|
|
52
|
+
"receive": {
|
|
53
|
+
"data_dir": self.build_default_receive_data_dir(),
|
|
54
|
+
"webhook_url": None,
|
|
55
|
+
"disable_text": False,
|
|
56
|
+
"disable_files": False,
|
|
57
|
+
},
|
|
58
|
+
"website": {"disable_csp": False, "custom_csp": None, "filenames": []},
|
|
59
|
+
"chat": {},
|
|
60
|
+
}
|
|
61
|
+
self._settings = {}
|
|
62
|
+
|
|
63
|
+
self.just_created = False
|
|
64
|
+
if id:
|
|
65
|
+
self.id = id
|
|
66
|
+
else:
|
|
67
|
+
self.id = self.common.build_password(3)
|
|
68
|
+
|
|
69
|
+
self.load(filename)
|
|
70
|
+
|
|
71
|
+
def fill_in_defaults(self):
|
|
72
|
+
"""
|
|
73
|
+
If there are any missing settings from self._settings, replace them with
|
|
74
|
+
their default values.
|
|
75
|
+
"""
|
|
76
|
+
for key in self.default_settings:
|
|
77
|
+
if key in self._settings:
|
|
78
|
+
for inner_key in self.default_settings[key]:
|
|
79
|
+
if inner_key not in self._settings[key]:
|
|
80
|
+
self._settings[key][inner_key] = self.default_settings[key][
|
|
81
|
+
inner_key
|
|
82
|
+
]
|
|
83
|
+
else:
|
|
84
|
+
self._settings[key] = self.default_settings[key]
|
|
85
|
+
|
|
86
|
+
def get(self, group, key):
|
|
87
|
+
return self._settings[group][key]
|
|
88
|
+
|
|
89
|
+
def set(self, group, key, val):
|
|
90
|
+
self._settings[group][key] = val
|
|
91
|
+
self.common.log(
|
|
92
|
+
"ModeSettings", "set", f"updating {self.id}: {group}.{key} = {val}"
|
|
93
|
+
)
|
|
94
|
+
self.save()
|
|
95
|
+
|
|
96
|
+
def build_default_receive_data_dir(self):
|
|
97
|
+
"""
|
|
98
|
+
Returns the path of the default Downloads directory for receive mode.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
if self.common.platform == "Darwin":
|
|
102
|
+
# We can't use os.path.expanduser() in macOS because in the sandbox it
|
|
103
|
+
# returns the path to the sandboxed homedir
|
|
104
|
+
real_homedir = pwd.getpwuid(os.getuid()).pw_dir
|
|
105
|
+
return os.path.join(real_homedir, "OnionShare")
|
|
106
|
+
elif self.common.platform == "Windows":
|
|
107
|
+
# On Windows, os.path.expanduser() needs to use backslash, or else it
|
|
108
|
+
# retains the forward slash, which breaks opening the folder in explorer.
|
|
109
|
+
return os.path.expanduser("~\\OnionShare")
|
|
110
|
+
else:
|
|
111
|
+
# All other OSes
|
|
112
|
+
return os.path.expanduser("~/OnionShare")
|
|
113
|
+
|
|
114
|
+
def load(self, filename=None):
|
|
115
|
+
# Load persistent settings from disk. If the file doesn't exist, create it
|
|
116
|
+
if filename:
|
|
117
|
+
self.filename = filename
|
|
118
|
+
else:
|
|
119
|
+
self.filename = os.path.join(
|
|
120
|
+
self.common.build_persistent_dir(), f"{self.id}.json"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if os.path.exists(self.filename):
|
|
124
|
+
try:
|
|
125
|
+
with open(self.filename, "r") as f:
|
|
126
|
+
self._settings = json.load(f)
|
|
127
|
+
self.fill_in_defaults()
|
|
128
|
+
self.common.log("ModeSettings", "load", f"loaded {self.filename}")
|
|
129
|
+
return
|
|
130
|
+
except Exception:
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
# If loading settings didn't work, create the settings file
|
|
134
|
+
self.common.log("ModeSettings", "load", f"creating {self.filename}")
|
|
135
|
+
self.fill_in_defaults()
|
|
136
|
+
self.just_created = True
|
|
137
|
+
|
|
138
|
+
def save(self):
|
|
139
|
+
# Save persistent setting to disk
|
|
140
|
+
if not self.get("persistent", "enabled"):
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
if self.filename:
|
|
144
|
+
with open(self.filename, "w") as file:
|
|
145
|
+
file.write(json.dumps(self._settings, indent=2))
|
|
146
|
+
|
|
147
|
+
def delete(self):
|
|
148
|
+
# Delete the file from disk
|
|
149
|
+
if os.path.exists(self.filename):
|
|
150
|
+
os.remove(self.filename)
|