11import shutil
2- from os import environ , chdir
2+ from os import environ
3+ import os
34from os .path import isdir , isfile , join , abspath , dirname
45from time import sleep
6+ import tempfile
57from http .client import CannotSendRequest
68from selenium .webdriver .support .ui import WebDriverWait
79from selenium .webdriver .support import expected_conditions as EC
1921
2022DEFAULT_BANNED_PORTS = "9050,9051,9150,9151"
2123GECKO_DRIVER_EXE_PATH = shutil .which ("geckodriver" )
24+ DEFAULT_TBB_BROWSER_DIR = ""
2225
2326class 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