7373class Plugins (DaemonThread ):
7474
7575 pkgpath = os .path .dirname (plugins .__file__ )
76- # TODO: use XDG Base Directory Specification instead of hardcoding /etc
77- keyfile_posix = '/etc/electrum/plugins_key'
76+ # https://specifications.freedesktop.org/basedir-spec/latest/#variables
77+ if os .environ .get ('XDG_CONFIG_HOME' ):
78+ base_path = os .environ .get ('XDG_CONFIG_HOME' )
79+ elif os .environ .get ('HOME' ):
80+ base_path = os .environ .get ('HOME' ) + '/.config'
81+ else :
82+ base_path = '/etc'
83+ keyfile_posix = base_path + '/electrum/plugins_key'
7884 keyfile_windows = r'HKEY_LOCAL_MACHINE\SOFTWARE\Electrum\PluginsKey'
7985
8086 @profiler
@@ -204,10 +210,30 @@ def get_keyfile_path(self, key_hex: Optional[str]) -> Tuple[str, str]:
204210 _ ("To set it you can also use the Auto-Setup or run "
205211 "the following terminal command" ),
206212 ":\n \n " ,
207- f"sudo sh -c \" { self ._posix_plugin_key_creation_command (key_hex )} \" " ,
213+ self ._get_user_facing_posix_plugin_key_setup_command (key_hex ),
208214 ])
209215 return keyfile_path , keyfile_help
210216
217+ def _get_user_facing_posix_plugin_key_setup_command (self , key_hex : str ) -> str :
218+ electrum_key_dir = os .path .dirname (self .keyfile_posix )
219+ def mkdir_needs_root (parent_dir : str ):
220+ # Keep checking parent directories until we find one that exists
221+ while parent_dir and not os .path .exists (parent_dir ):
222+ parent_dir = os .path .dirname (parent_dir )
223+ # If we found a parent directory, check if we have write permissions
224+ if parent_dir :
225+ return not os .access (parent_dir , os .W_OK )
226+ return True
227+ if mkdir_needs_root (electrum_key_dir ):
228+ # puts the mkdir inside the sudo sh command
229+ command = (f"sudo sh -c \" mkdir -p { electrum_key_dir } && "
230+ f"{ self ._posix_plugin_key_creation_command (key_hex )} \" " )
231+ else :
232+ # dir can be created without sudo
233+ command = (f"mkdir -p { electrum_key_dir } && "
234+ f"sudo sh -c \" { self ._posix_plugin_key_creation_command (key_hex )} \" " )
235+ return command
236+
211237 def try_auto_key_setup (self , pubkey_hex : str ) -> bool :
212238 """Can be called from the GUI to store the plugin pubkey as root/admin user"""
213239 try :
@@ -241,12 +267,9 @@ def try_auto_key_reset(self) -> bool:
241267
242268 def _posix_plugin_key_creation_command (self , pubkey_hex : str ) -> str :
243269 """creates the dir (dir_path), writes the key in file, and sets permissions to 644"""
244- dir_path : str = os .path .dirname (self .keyfile_posix )
245270 sh_command = (
246- f"mkdir -p { dir_path } " # create the /etc/electrum dir
247- f"&& printf '%s' '{ pubkey_hex } ' > { self .keyfile_posix } " # write the key to the file
248- f"&& chmod 644 { self .keyfile_posix } " # set read permissions for the file
249- f"&& chmod 755 { dir_path } " # set read permissions for the dir
271+ f"printf '%s' '{ pubkey_hex } ' > { self .keyfile_posix } " # write the key to the file
272+ f"&& chmod 644 { self .keyfile_posix } " # set read permissions for the file
250273 )
251274 return sh_command
252275
@@ -355,9 +378,14 @@ def _write_key_to_root_file_linux(self, key_hex: str) -> None:
355378 This will open an OS dialog asking for the root password. Can only succeed if
356379 the system has polkit installed.
357380 """
358- assert os .path .exists ("/etc" ), "System does not have /etc directory"
359-
360381 sh_command : str = self ._posix_plugin_key_creation_command (key_hex )
382+ # create base directory
383+ base_dir_path = os .path .dirname (self .keyfile_posix )
384+ try :
385+ os .makedirs (base_dir_path , exist_ok = True )
386+ except OSError :
387+ # fallback to creating the dir with root permissions too
388+ sh_command = f"mkdir -p { base_dir_path } && " + sh_command
361389 commands = ['pkexec' , 'sh' , '-c' , sh_command ]
362390 self ._execute_commands_in_subprocess (commands )
363391
@@ -373,21 +401,27 @@ def _delete_linux_plugin_keyfile(self) -> None:
373401 if not os .path .exists (self .keyfile_posix ):
374402 self .logger .debug (f'file { self .keyfile_posix } does not exist' )
375403 return
376- if not self . _has_root_permissions ( self . keyfile_posix ) :
404+ try :
377405 os .unlink (self .keyfile_posix )
378- return
406+ except OSError :
407+ self .logger .debug (f"unlinking { self .keyfile_posix } failed, retrying with root" )
408+ # use pkexec to delete the file as root user
409+ commands = ['pkexec' , 'rm' , self .keyfile_posix ]
410+ self ._execute_commands_in_subprocess (commands )
379411
380- # use pkexec to delete the file as root user
381- commands = ['pkexec' , 'rm' , self .keyfile_posix ]
382- self ._execute_commands_in_subprocess (commands )
383412 assert not os .path .exists (self .keyfile_posix ), f'file { self .keyfile_posix } still exists'
384413
385414 def _write_key_to_root_file_macos (self , key_hex : str ) -> None :
386- assert os .path .exists ("/etc" ), "System does not have /etc directory"
387-
388415 sh_command : str = self ._posix_plugin_key_creation_command (key_hex )
389- macos_commands = self ._get_macos_osascript_command (["sh" , "-c" , sh_command ])
416+ # create base directory
417+ base_dir_path = os .path .dirname (self .keyfile_posix )
418+ try :
419+ os .makedirs (base_dir_path , exist_ok = True )
420+ except OSError :
421+ # fallback to creating the dir with root permissions too
422+ sh_command = f"mkdir -p { base_dir_path } && " + sh_command
390423
424+ macos_commands = self ._get_macos_osascript_command (["sh" , "-c" , sh_command ])
391425 self ._execute_commands_in_subprocess (macos_commands )
392426 with open (self .keyfile_posix , 'r' ) as f :
393427 assert f .read () == key_hex , f'file content mismatch: { f .read ()} != { key_hex } '
@@ -397,12 +431,12 @@ def _delete_macos_plugin_keyfile(self) -> None:
397431 if not os .path .exists (self .keyfile_posix ):
398432 self .logger .debug (f'file { self .keyfile_posix } does not exist' )
399433 return
400- if not self . _has_root_permissions ( self . keyfile_posix ) :
434+ try :
401435 os .unlink (self .keyfile_posix )
402- return
403- # use osascript to delete the file as root user
404- macos_commands = self ._get_macos_osascript_command (["rm" , self .keyfile_posix ])
405- self ._execute_commands_in_subprocess (macos_commands )
436+ except OSError :
437+ # use osascript to delete the file as root user
438+ macos_commands = self ._get_macos_osascript_command (["rm" , self .keyfile_posix ])
439+ self ._execute_commands_in_subprocess (macos_commands )
406440 assert not os .path .exists (self .keyfile_posix ), f'file { self .keyfile_posix } still exists'
407441
408442 def _write_key_to_regedit_windows (self , key_hex : str ) -> None :
0 commit comments