Skip to content

Commit b4a3397

Browse files
committed
expose path tbb_browser_dir
ensure writable tbb_browser_dir fix: Your Tor Browser profile cannot be loaded add DEFAULT_TBB_BROWSER_DIR dont change workdir
1 parent 216a375 commit b4a3397

File tree

2 files changed

+102
-38
lines changed

2 files changed

+102
-38
lines changed

tbselenium/common.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
# DEFAULT TBB PATHS works for TBB versions v4.x and above
55
# Old TBB versions (V3.X or below) have different directory structures
66
DEFAULT_TBB_BROWSER_DIR = 'Browser'
7-
DEFAULT_TBB_TORBROWSER_DIR = join('Browser', 'TorBrowser')
8-
DEFAULT_TBB_FX_BINARY_PATH = join('Browser', 'firefox')
7+
8+
# all other paths are relative to DEFAULT_TBB_BROWSER_DIR
9+
DEFAULT_TBB_TORBROWSER_DIR = 'TorBrowser'
10+
DEFAULT_TBB_FX_BINARY_PATH = 'firefox'
911
DEFAULT_TOR_BINARY_DIR = join(DEFAULT_TBB_TORBROWSER_DIR, 'Tor')
1012
DEFAULT_TOR_BINARY_PATH = join(DEFAULT_TOR_BINARY_DIR, 'tor')
1113
DEFAULT_TBB_DATA_DIR = join(DEFAULT_TBB_TORBROWSER_DIR, 'Data')

tbselenium/tbdriver.py

Lines changed: 98 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import shutil
2-
from os import environ, chdir
2+
from os import environ
3+
import os
34
from os.path import isdir, isfile, join, abspath, dirname
45
from time import sleep
6+
import tempfile
57
from http.client import CannotSendRequest
68
from selenium.webdriver.support.ui import WebDriverWait
79
from selenium.webdriver.support import expected_conditions as EC
@@ -19,12 +21,14 @@
1921

2022
DEFAULT_BANNED_PORTS = "9050,9051,9150,9151"
2123
GECKO_DRIVER_EXE_PATH = shutil.which("geckodriver")
24+
DEFAULT_TBB_BROWSER_DIR = ""
2225

2326
class TorBrowserDriver(FirefoxDriver):
2427
"""
2528
Extend Firefox webdriver to automate Tor Browser.
2629
"""
2730
def __init__(self,
31+
tbb_browser_dir=DEFAULT_TBB_BROWSER_DIR,
2832
tbb_path="",
2933
tor_cfg=cm.USE_RUNNING_TOR,
3034
tbb_fx_binary_path="",
@@ -52,7 +56,10 @@ def __init__(self,
5256

5357
self.use_custom_profile = use_custom_profile
5458
self.tor_cfg = tor_cfg
55-
self.setup_tbb_paths(tbb_path, tbb_fx_binary_path,
59+
60+
self.remove_on_exit = []
61+
62+
self.setup_tbb_paths(tbb_browser_dir, tbb_path, tbb_fx_binary_path,
5663
tbb_profile_path, tor_data_dir)
5764
self.options = Options() if options is None else options
5865
install_noscript = False
@@ -100,10 +107,16 @@ def __init__(self,
100107
if headless:
101108
self.options.add_argument('-headless')
102109

103-
super(TorBrowserDriver, self).__init__(
104-
service=tbb_service,
105-
options=self.options,
106-
)
110+
# __exit__ is not called when this fails
111+
try:
112+
super(TorBrowserDriver, self).__init__(
113+
service=tbb_service,
114+
options=self.options,
115+
)
116+
except Exception as exc:
117+
self.quit()
118+
raise
119+
107120
self.is_running = True
108121
self.install_extensions(extensions, install_noscript)
109122
self.temp_profile_dir = self.capabilities["moz:profile"]
@@ -113,7 +126,7 @@ def install_extensions(self, extensions, install_noscript):
113126
"""Install the given extensions to the profile we are launching."""
114127
if install_noscript:
115128
no_script_xpi = join(
116-
self.tbb_path, cm.DEFAULT_TBB_NO_SCRIPT_XPI_PATH)
129+
self.tbb_browser_dir, cm.DEFAULT_TBB_NO_SCRIPT_XPI_PATH)
117130
extensions.append(no_script_xpi)
118131

119132
for extension in extensions:
@@ -147,51 +160,92 @@ def init_ports(self, tor_cfg, socks_port, control_port):
147160
self.socks_port = socks_port
148161
self.control_port = control_port
149162

150-
def setup_tbb_paths(self, tbb_path, tbb_fx_binary_path, tbb_profile_path,
151-
tor_data_dir):
163+
def setup_tbb_paths(self, tbb_browser_dir, tbb_path, tbb_fx_binary_path,
164+
tbb_profile_path, tor_data_dir):
152165
"""Update instance variables based on the passed paths.
153166
154167
TorBrowserDriver can be initialized by passing either
168+
0) path to TBB browser directory (tbb_browser_dir)
155169
1) path to TBB directory (tbb_path)
156170
2) path to TBB directory and profile (tbb_path, tbb_profile_path)
157171
3) path to TBB's Firefox binary and profile (tbb_fx_binary_path, tbb_profile_path)
158172
"""
159-
if not (tbb_path or (tbb_fx_binary_path and tbb_profile_path)):
160-
raise TBDriverPathError("Either TBB path or Firefox profile"
161-
" and binary path should be provided"
162-
" %s" % tbb_path)
163-
164-
if tbb_path:
165-
if not isdir(tbb_path):
166-
raise TBDriverPathError("TBB path is not a directory %s"
167-
% tbb_path)
168-
tbb_fx_binary_path = join(tbb_path, cm.DEFAULT_TBB_FX_BINARY_PATH)
173+
# all default paths are relative to tbb_browser_dir
174+
if tbb_browser_dir:
175+
pass
176+
"""
177+
if not tbb_fx_binary_path:
178+
tbb_fx_binary_path = join(tbb_browser_dir, cm.DEFAULT_TBB_FX_BINARY_PATH)
179+
if not tbb_profile_path:
180+
# fall back to the default profile path in TBB
181+
tbb_profile_path = join(tbb_browser_dir, cm.DEFAULT_TBB_PROFILE_PATH)
182+
if not tbb_path:
183+
tbb_path = dirname(tbb_browser_dir)
184+
"""
185+
elif tbb_path:
186+
# legacy
187+
tbb_browser_dir = join(tbb_path, cm.DEFAULT_TBB_BROWSER_DIR)
188+
189+
elif tbb_fx_binary_path and tbb_profile_path:
190+
tbb_browser_dir = dirname(tbb_fx_binary_path)
191+
169192
else:
170-
# based on https://github.com/webfp/tor-browser-selenium/issues/159#issue-1016463002
171-
tbb_path = dirname(dirname(tbb_fx_binary_path))
193+
raise TBDriverPathError("At least tbb_browser_dir or tbb_path or"
194+
" (tbb_fx_binary_path and tbb_profile_path)"
195+
" must be provided")
172196

173-
if not tbb_profile_path:
174-
# fall back to the default profile path in TBB
175-
tbb_profile_path = join(tbb_path, cm.DEFAULT_TBB_PROFILE_PATH)
197+
if not isdir(tbb_browser_dir):
198+
raise TBDriverPathError("Invalid TBB Browser dir %s"
199+
% tbb_browser_dir)
200+
201+
"""
202+
if not tbb_path:
203+
if not isfile(tbb_fx_binary_path):
204+
raise TBDriverPathError("Invalid Firefox binary %s"
205+
% tbb_fx_binary_path)
206+
# based on https://github.com/webfp/tor-browser-selenium/issues/159#issue-1016463002
207+
if not isdir(tbb_path):
208+
raise TBDriverPathError("Invalid TBB path directory %s" % tbb_path)
209+
"""
176210

211+
if not tbb_fx_binary_path:
212+
tbb_fx_binary_path = join(tbb_browser_dir, cm.DEFAULT_TBB_FX_BINARY_PATH)
177213
if not isfile(tbb_fx_binary_path):
178214
raise TBDriverPathError("Invalid Firefox binary %s"
179215
% tbb_fx_binary_path)
216+
217+
if not tbb_profile_path:
218+
# fall back to the default profile path in TBB
219+
tbb_profile_path = join(tbb_browser_dir, cm.DEFAULT_TBB_PROFILE_PATH)
180220
if not isdir(tbb_profile_path):
181221
raise TBDriverPathError("Invalid Firefox profile dir %s"
182222
% tbb_profile_path)
183-
self.tbb_path = abspath(tbb_path)
223+
224+
if not tor_data_dir:
225+
# fall back to default tor data dir in TBB
226+
tor_data_dir = join(tbb_browser_dir, cm.DEFAULT_TOR_DATA_PATH)
227+
# only relevant if we launch tor
228+
# if not isdir(tor_data_dir):
229+
# raise TBDriverPathError("Invalid Tor data directory %s" % tor_data_dir)
230+
231+
# tbb_browser_dir must be writable, but its contents can be read-only
232+
# fix: Your Tor Browser profile cannot be loaded. It may be missing or inaccessible.
233+
if not os.access(tbb_browser_dir, os.W_OK):
234+
temp_tbb_browser_dir = tempfile.mkdtemp(prefix="tbselenium-tbb_browser_dir-")
235+
# print(f"not writable tbb_browser_dir {tbb_browser_dir} - using tempdir {temp_tbb_browser_dir}")
236+
self.remove_on_exit.append(temp_tbb_browser_dir)
237+
# symlink the contents of tbb_browser_dir
238+
for name in os.listdir(tbb_browser_dir):
239+
src = tbb_browser_dir + "/" + name
240+
dst = temp_tbb_browser_dir + "/" + name
241+
os.symlink(src, dst, target_is_directory=os.path.isdir(src))
242+
tbb_browser_dir = temp_tbb_browser_dir
243+
244+
#self.tbb_path = abspath(tbb_path)
245+
self.tbb_browser_dir = abspath(tbb_browser_dir)
184246
self.tbb_profile_path = abspath(tbb_profile_path)
185247
self.tbb_fx_binary_path = abspath(tbb_fx_binary_path)
186-
self.tbb_browser_dir = abspath(join(tbb_path,
187-
cm.DEFAULT_TBB_BROWSER_DIR))
188-
if tor_data_dir:
189-
self.tor_data_dir = tor_data_dir # only relevant if we launch tor
190-
else:
191-
# fall back to default tor data dir in TBB
192-
self.tor_data_dir = join(tbb_path, cm.DEFAULT_TOR_DATA_PATH)
193-
# TB can't find bundled "fonts" if we don't switch to tbb_browser_dir
194-
chdir(self.tbb_browser_dir)
248+
self.tor_data_dir = abspath(tor_data_dir)
195249

196250
def load_url(self, url, wait_on_page=0, wait_for_page_body=False):
197251
"""Load a URL and wait before returning.
@@ -299,9 +353,9 @@ def export_env_vars(self):
299353
300354
We follow start-tor-browser script.
301355
"""
302-
tor_binary_dir = join(self.tbb_path, cm.DEFAULT_TOR_BINARY_DIR)
356+
tor_binary_dir = join(self.tbb_browser_dir, cm.DEFAULT_TOR_BINARY_DIR)
303357
environ["LD_LIBRARY_PATH"] = tor_binary_dir
304-
environ["FONTCONFIG_PATH"] = join(self.tbb_path,
358+
environ["FONTCONFIG_PATH"] = join(self.tbb_browser_dir,
305359
cm.DEFAULT_FONTCONFIG_PATH)
306360
environ["FONTCONFIG_FILE"] = cm.FONTCONFIG_FILE
307361
environ["HOME"] = self.tbb_browser_dir
@@ -345,6 +399,14 @@ def quit(self):
345399
self.clean_up_profile_dirs()
346400
except Exception as e:
347401
print("[tbselenium] Exception while quitting: %s" % e)
402+
for path in self.remove_on_exit:
403+
# todo: log.debug
404+
# print(f"tbdriver __exit__ removing {path}")
405+
try:
406+
shutil.rmtree(path)
407+
except Exception as exc:
408+
# todo: log error?
409+
pass
348410

349411
def __enter__(self):
350412
return self

0 commit comments

Comments
 (0)