Skip to content

Commit 05ad6bd

Browse files
committed
change plugins keyfile path to XDG spec
changes the plugins keyfile to either XDG_CONFIG_HOME/electrum/plugins_key, or if empty to HOME/.config/electrum/plugins_key or if HOME is not set either (which should not happen in practice), to /etc/electrum/plugins_key. Also adds a `bring_to_front()` call after installing external plugin so the PluginsDialog is brought back in front.
1 parent 27599ac commit 05ad6bd

2 files changed

Lines changed: 59 additions & 24 deletions

File tree

electrum/gui/qt/plugins_dialog.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ def add_external_plugin(self, path):
322322
if self.gui_object:
323323
self.gui_object.reload_windows()
324324
self.show_list()
325+
self.bring_to_front()
325326
return True
326327

327328
def show_list(self):
@@ -377,4 +378,4 @@ def bring_to_front(self):
377378
def _bring_self_to_front():
378379
self.activateWindow()
379380
self.setFocus()
380-
QTimer.singleShot(100, _bring_self_to_front)
381+
QTimer.singleShot(250, _bring_self_to_front)

electrum/plugin.py

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,14 @@
7373
class 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

Comments
 (0)