From b2dc6229bf2375b573e72f087913b08a7c10aab6 Mon Sep 17 00:00:00 2001 From: Ali Mirjamali Date: Fri, 16 Aug 2024 13:34:31 +0330 Subject: [PATCH 1/2] Allow recent:/// in Nautilus for Copy/Move/Open fixes: https://github.com/QubesOS/qubes-issues/issues/8589 --- qubes-rpc/nautilus/qvm_copy_nautilus.py | 28 ++++++++++++++++++--- qubes-rpc/nautilus/qvm_dvm_nautilus.py | 33 +++++++++++++++++++------ qubes-rpc/nautilus/qvm_move_nautilus.py | 33 ++++++++++++++++++++----- 3 files changed, 77 insertions(+), 17 deletions(-) mode change 100755 => 100644 qubes-rpc/nautilus/qvm_dvm_nautilus.py diff --git a/qubes-rpc/nautilus/qvm_copy_nautilus.py b/qubes-rpc/nautilus/qvm_copy_nautilus.py index d0a89023..4dbbe2af 100755 --- a/qubes-rpc/nautilus/qvm_copy_nautilus.py +++ b/qubes-rpc/nautilus/qvm_copy_nautilus.py @@ -28,10 +28,30 @@ def get_file_items(self, *args): def on_menu_item_clicked(self, menu, files): '''Called when user chooses files though Nautilus context menu. ''' - cmd = [file_obj.get_location().get_path() - for file_obj in files - # Check if file is not gone - if not file_obj.is_gone()] + paths = [] + for file_obj in files: + file_location = file_obj.get_location() + file_uri = file_location.get_uri() + if file_uri.startswith('file:///'): + if not file_obj.is_gone(): + # Check if file is not gone + paths.append(file_location.get_path()) + elif file_uri.startswith('recent:///'): + try: + file_info = file_location.query_info( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) + target_uri = file_info.get_attribute_string( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) + if target_uri.startswith('file://'): + paths.append(target_uri[7:]) + except GLib.GError: + # TODO: Decide what to do if the recent item does not exist + pass + else: + # TODO: Decide what to do with other weird URIs (eg. smb:///) + pass + # Double-check if the file is not gone in the meantime + cmd = [path for path in paths if os.path.exists(path)] cmd.insert(0, '/usr/lib/qubes/qvm-copy-to-vm.gnome') pid = GLib.spawn_async(cmd)[0] GLib.spawn_close_pid(pid) diff --git a/qubes-rpc/nautilus/qvm_dvm_nautilus.py b/qubes-rpc/nautilus/qvm_dvm_nautilus.py old mode 100755 new mode 100644 index 452c01c9..514ab0ec --- a/qubes-rpc/nautilus/qvm_dvm_nautilus.py +++ b/qubes-rpc/nautilus/qvm_dvm_nautilus.py @@ -1,4 +1,5 @@ -from gi.repository import Nautilus, GObject, GLib +import os.path +from gi.repository import Nautilus, GObject, GLib, Gio class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider): @@ -39,17 +40,35 @@ def on_menu_item_clicked(self, menu, files, view_only=False): '''Called when user chooses files though Nautilus context menu. ''' for file_obj in files: - - # Check if file still exists - if file_obj.is_gone(): + file_location = file_obj.get_location() + file_uri = file_location.get_uri() + if file_uri.startswith('file:///'): + if not file_obj.is_gone(): + # Check if file still exists + file_path = file_location.get_path() + else: + return + elif file_uri.startswith('recent:///'): + try: + file_info = file_location.query_info( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) + target_uri = file_info.get_attribute_string( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) + if target_uri.startswith('file://'): + file_path = target_uri[7:] + if not os.path.exists(file_path): + return + except GLib.GError: + #TODO: Decide what to do if the recent item does not exist + return + else: + # TODO: Decide what to do with other weird URIs (eg. smb:///) return - gio_file = file_obj.get_location() - command = ['/usr/bin/qvm-open-in-dvm'] if view_only: command.append('--view-only') - command.append(gio_file.get_path()) + command.append(file_path) pid = GLib.spawn_async(command)[0] GLib.spawn_close_pid(pid) diff --git a/qubes-rpc/nautilus/qvm_move_nautilus.py b/qubes-rpc/nautilus/qvm_move_nautilus.py index 5defb689..a1b1949a 100755 --- a/qubes-rpc/nautilus/qvm_move_nautilus.py +++ b/qubes-rpc/nautilus/qvm_move_nautilus.py @@ -1,5 +1,5 @@ -from gi.repository import Nautilus, GObject, GLib - +import os.path +from gi.repository import Nautilus, GObject, GLib, Gio class MoveToAppvmItemExtension(GObject.GObject, Nautilus.MenuProvider): '''Move file(s) to AppVM. @@ -28,10 +28,31 @@ def get_file_items(self, *args): def on_menu_item_clicked(self, menu, files): '''Called when user chooses files though Nautilus context menu. ''' - cmd = [file_obj.get_location().get_path() - for file_obj in files - # Check if file is not gone - if not file_obj.is_gone()] + paths = [] + for file_obj in files: + file_location = file_obj.get_location() + file_uri = file_location.get_uri() + if file_uri.startswith('file:///'): + if not file_obj.is_gone(): + # Check if file is not gone + paths.append(file_location.get_path()) + elif file_uri.startswith('recent:///'): + try: + file_info = file_location.query_info( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) + target_uri = file_info.get_attribute_string( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) + if target_uri.startswith('file://'): + paths.append(target_uri[7:]) + except GLib.GError: + # TODO: Decide what to do if the recent item does not exist + pass + else: + # TODO: Decide what to do with other weird URIs (eg. smb:///) + pass + # Double-check if the file is not gone in the meantime + cmd = [path for path in paths if os.path.exists(path)] cmd.insert(0, '/usr/lib/qubes/qvm-move-to-vm.gnome') pid = GLib.spawn_async(cmd)[0] GLib.spawn_close_pid(pid) + # TODO: Refresh Nautilus to remove moved files from recents list From 91374a177b05e8227c9b83b898844e96240b3bc5 Mon Sep 17 00:00:00 2001 From: Ali Mirjamali Date: Fri, 23 Aug 2024 20:37:49 +0330 Subject: [PATCH 2/2] Hide Copy/Move/Edit/View to qube for network items It does not make sense to show the 'Copy/Move to other qube' or 'Edit/View in disposable qube' menu if it is a network item --- qubes-rpc/nautilus/qvm_copy_nautilus.py | 48 ++++++++++++++++++------- qubes-rpc/nautilus/qvm_dvm_nautilus.py | 48 +++++++++++++++++-------- qubes-rpc/nautilus/qvm_move_nautilus.py | 44 +++++++++++++++++------ 3 files changed, 103 insertions(+), 37 deletions(-) mode change 100755 => 100644 qubes-rpc/nautilus/qvm_copy_nautilus.py mode change 100755 => 100644 qubes-rpc/nautilus/qvm_move_nautilus.py diff --git a/qubes-rpc/nautilus/qvm_copy_nautilus.py b/qubes-rpc/nautilus/qvm_copy_nautilus.py old mode 100755 new mode 100644 index 4dbbe2af..0ac7aabd --- a/qubes-rpc/nautilus/qvm_copy_nautilus.py +++ b/qubes-rpc/nautilus/qvm_copy_nautilus.py @@ -1,5 +1,5 @@ -from gi.repository import Nautilus, GObject, GLib - +import os.path +from gi.repository import Nautilus, GObject, GLib, Gio class CopyToAppvmItemExtension(GObject.GObject, Nautilus.MenuProvider): '''Copy file(s) to AppVM. @@ -17,6 +17,34 @@ def get_file_items(self, *args): if not files: return + # Do not attach context menu to anything other than local items + # - or recent items which point to actual local items + for file_obj in files: + file_uri_scheme = file_obj.get_uri_scheme() + if file_uri_scheme == 'file': + # Check if file is not gone in the meantime + if file_obj.is_gone(): + return + else: + continue + elif file_uri_scheme == 'recent': + # Ensure recent item is actually a local item & it still exists + try: + file_location = file_obj.get_location() + file_info = file_location.query_info( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) + target_uri = file_info.get_attribute_string( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) + if not target_uri.startswith('file://'): + # Maybe a network item in recents. Hide menu. + return + except GLib.GError: + # Item in recents points to a file which is gone. Hide menu. + return + else: + # Not a local file (e.g. smb://). Hide menu. + return + menu_item = Nautilus.MenuItem(name='QubesMenuProvider::CopyToAppvm', label='Copy to other qube...', tip='', @@ -31,25 +59,21 @@ def on_menu_item_clicked(self, menu, files): paths = [] for file_obj in files: file_location = file_obj.get_location() - file_uri = file_location.get_uri() - if file_uri.startswith('file:///'): + file_uri_scheme = file_obj.get_uri_scheme() + if file_uri_scheme == 'file': if not file_obj.is_gone(): - # Check if file is not gone + # Check yet another time if file is not gone paths.append(file_location.get_path()) - elif file_uri.startswith('recent:///'): + elif file_uri_scheme == 'recent': try: file_info = file_location.query_info( Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) target_uri = file_info.get_attribute_string( Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) - if target_uri.startswith('file://'): - paths.append(target_uri[7:]) + paths.append(target_uri[7:]) except GLib.GError: - # TODO: Decide what to do if the recent item does not exist pass - else: - # TODO: Decide what to do with other weird URIs (eg. smb:///) - pass + # Double-check if the file is not gone in the meantime cmd = [path for path in paths if os.path.exists(path)] cmd.insert(0, '/usr/lib/qubes/qvm-copy-to-vm.gnome') diff --git a/qubes-rpc/nautilus/qvm_dvm_nautilus.py b/qubes-rpc/nautilus/qvm_dvm_nautilus.py index 514ab0ec..ee62be62 100644 --- a/qubes-rpc/nautilus/qvm_dvm_nautilus.py +++ b/qubes-rpc/nautilus/qvm_dvm_nautilus.py @@ -19,6 +19,34 @@ def get_file_items(self, *args): if not files: return + # Do not attach context menu to anything other than local items + # - or recent items which point to actual local items + for file_obj in files: + file_uri_scheme = file_obj.get_uri_scheme() + if file_uri_scheme == 'file': + # Check if file is not gone in the meantime + if file_obj.is_gone(): + return + else: + continue + elif file_uri_scheme == 'recent': + # Ensure recent item is actually a local item & it still exists + try: + file_location = file_obj.get_location() + file_info = file_location.query_info( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) + target_uri = file_info.get_attribute_string( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) + if not target_uri.startswith('file://'): + # Maybe a network item in recents. Hide menu. + return + except GLib.GError: + # Item in recents points to a file which is gone. Hide menu. + return + else: + # Not a local file (e.g. smb://). Hide menu. + return + menu_item1 = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm', label='Edit in disposable qube', tip='', @@ -31,9 +59,7 @@ def get_file_items(self, *args): tip='', icon='') - menu_item2.connect('activate', - self.on_menu_item_clicked, - files, True) + menu_item2.connect('activate', self.on_menu_item_clicked, files, True) return menu_item1, menu_item2, def on_menu_item_clicked(self, menu, files, view_only=False): @@ -42,28 +68,22 @@ def on_menu_item_clicked(self, menu, files, view_only=False): for file_obj in files: file_location = file_obj.get_location() file_uri = file_location.get_uri() - if file_uri.startswith('file:///'): + file_uri_scheme = file_obj.get_uri_scheme() + if file_uri_scheme == 'file': if not file_obj.is_gone(): - # Check if file still exists + # Check yet another time if file is not gone file_path = file_location.get_path() else: return - elif file_uri.startswith('recent:///'): + elif file_uri_scheme == 'recent': try: file_info = file_location.query_info( Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) target_uri = file_info.get_attribute_string( Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) - if target_uri.startswith('file://'): - file_path = target_uri[7:] - if not os.path.exists(file_path): - return + file_path = target_uri[7:] except GLib.GError: - #TODO: Decide what to do if the recent item does not exist return - else: - # TODO: Decide what to do with other weird URIs (eg. smb:///) - return command = ['/usr/bin/qvm-open-in-dvm'] if view_only: diff --git a/qubes-rpc/nautilus/qvm_move_nautilus.py b/qubes-rpc/nautilus/qvm_move_nautilus.py old mode 100755 new mode 100644 index a1b1949a..a30298b1 --- a/qubes-rpc/nautilus/qvm_move_nautilus.py +++ b/qubes-rpc/nautilus/qvm_move_nautilus.py @@ -17,6 +17,34 @@ def get_file_items(self, *args): if not files: return + # Do not attach context menu to anything other than local items + # - or recent items which point to actual local items + for file_obj in files: + file_uri_scheme = file_obj.get_uri_scheme() + if file_uri_scheme == 'file': + # Check if file is not gone in the meantime + if file_obj.is_gone(): + return + else: + continue + elif file_uri_scheme == 'recent': + # Ensure recent item is actually a local item & it still exists + try: + file_location = file_obj.get_location() + file_info = file_location.query_info( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) + target_uri = file_info.get_attribute_string( + Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) + if not target_uri.startswith('file://'): + # Maybe a network item in recents. Hide menu. + return + except GLib.GError: + # Item in recents points to a file which is gone. Hide menu. + return + else: + # Not a local file (e.g. smb://). Hide menu. + return + menu_item = Nautilus.MenuItem(name='QubesMenuProvider::MoveToAppvm', label='Move to other qube...', tip='', @@ -31,28 +59,22 @@ def on_menu_item_clicked(self, menu, files): paths = [] for file_obj in files: file_location = file_obj.get_location() - file_uri = file_location.get_uri() - if file_uri.startswith('file:///'): + file_uri_scheme = file_obj.get_uri_scheme() + if file_uri_scheme == 'file': if not file_obj.is_gone(): - # Check if file is not gone + # Check yet another time if file is not gone paths.append(file_location.get_path()) - elif file_uri.startswith('recent:///'): + elif file_uri_scheme == 'recent': try: file_info = file_location.query_info( Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, 0, None) target_uri = file_info.get_attribute_string( Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI) - if target_uri.startswith('file://'): - paths.append(target_uri[7:]) + paths.append(target_uri[7:]) except GLib.GError: - # TODO: Decide what to do if the recent item does not exist pass - else: - # TODO: Decide what to do with other weird URIs (eg. smb:///) - pass # Double-check if the file is not gone in the meantime cmd = [path for path in paths if os.path.exists(path)] cmd.insert(0, '/usr/lib/qubes/qvm-move-to-vm.gnome') pid = GLib.spawn_async(cmd)[0] GLib.spawn_close_pid(pid) - # TODO: Refresh Nautilus to remove moved files from recents list