From cf594464a59d8aa03aa4d825ef8fe9280a70b389 Mon Sep 17 00:00:00 2001 From: GloriousEggroll Date: Thu, 3 Apr 2025 18:38:45 -0600 Subject: [PATCH] APP PERMISSIONS TOGGLES ARE DONE --- libflatpak_query.py | 50 ++-- main.py | 539 +++++++++++++++++++++++++++++++++++++++- subcategories_data.json | 98 ++++---- 3 files changed, 614 insertions(+), 73 deletions(-) diff --git a/libflatpak_query.py b/libflatpak_query.py index 9602424..3f4471a 100755 --- a/libflatpak_query.py +++ b/libflatpak_query.py @@ -1197,7 +1197,7 @@ def get_perm_key_file(app_id: str | None, override=False, system=False) -> GLib return key_file -def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, str]: +def add_file_permissions(app_id: str, path: str, perm_type=None, system=False) -> tuple[bool, str]: """ Add filesystem permissions to a Flatpak application. @@ -1214,7 +1214,7 @@ def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, st try: key_file = get_perm_key_file(app_id, system) - + perm_type = perm_type or "filesystem" # Handle special case for home directory if path.lower() == "host": filesystem_path = "host" @@ -1229,32 +1229,35 @@ def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, st filesystem_path = path.rstrip('/') if not key_file.has_group("Context"): - key_file.set_string("Context", "filesystems", "") + key_file.set_string("Context", perm_type, "") # Now get the keys context_keys = key_file.get_keys("Context") # Check if perm_type exists in the section - if "filesystems" not in str(context_keys): + if perm_type not in str(context_keys): # Create the key with an empty string - key_file.set_string("Context", "filesystems", "") + key_file.set_string("Context", perm_type, "") # Get existing filesystem paths - existing_paths = key_file.get_string("Context", "filesystems") - if existing_paths is None: + existing_paths = key_file.get_string("Context", perm_type) + if existing_paths is None or existing_paths == "": # If no filesystems entry exists, create it - key_file.set_string("Context", "filesystems", filesystem_path) + key_file.set_string("Context", perm_type, filesystem_path) else: # Split existing paths and check if our path already exists + print(existing_paths) existing_paths_list = existing_paths.split(';') - + print(existing_paths_list) # Normalize paths for comparison (remove trailing slashes, convert to absolute paths) normalized_new_path = os.path.abspath(filesystem_path.rstrip('/')) normalized_existing_paths = [os.path.abspath(p.rstrip('/')) for p in existing_paths_list] + print(normalized_new_path) + print(normalized_existing_paths) # Only add if the path doesn't already exist if normalized_new_path not in normalized_existing_paths: - key_file.set_string("Context", "filesystems", + key_file.set_string("Context", perm_type, existing_paths + filesystem_path + ";") # Write the modified metadata back @@ -1269,7 +1272,7 @@ def add_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, st return False, f"Failed to modify permissions: {str(e)}" -def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, str]: +def remove_file_permissions(app_id: str, path: str, perm_type=None, system=False) -> tuple[bool, str]: """ Remove filesystem permissions from a Flatpak application. @@ -1285,6 +1288,7 @@ def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, """ try: key_file = get_perm_key_file(app_id, system) + perm_type = perm_type or "filesystem" # Handle special case for home directory if path.lower() == "host": @@ -1300,7 +1304,7 @@ def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, filesystem_path = path.rstrip('/') # Get existing filesystem paths - existing_paths = key_file.get_string("Context", "filesystems") + existing_paths = key_file.get_string("Context", perm_type) if existing_paths is None: return True, f"No filesystem permissions to remove for {app_id}" @@ -1322,9 +1326,9 @@ def remove_file_permissions(app_id: str, path: str, system=False) -> tuple[bool, new_permissions = ";".join(filtered_paths_list) if new_permissions: # Save changes - key_file.set_string("Context", "filesystems", new_permissions) + key_file.set_string("Context", perm_type, new_permissions) else: - key_file.remove_key("Context", "filesystems") + key_file.remove_key("Context", perm_type) # Write the modified metadata back try: @@ -1577,6 +1581,9 @@ def add_permission_value(app_id: str, perm_type: str, value: str, system=False) key, val = parts + if val not in ['talk', 'own']: + return False, "Value must be in format 'key=value' with value as 'talk' or 'own'" + # Set the value key_file.set_string(perm_type, key, val) @@ -2141,10 +2148,7 @@ def portal_get_app_permissions(app_id: str): # Format and return the results if app_permissions: - success_message = f"\nFound permissions for {app_id}:\n" - for portal, permission in app_permissions.items(): - success_message += f"- {portal}: {permission}\n" - return True, success_message.strip() + return True, app_permissions return False, f"No permissions found for {app_id} in any portal" @@ -2239,11 +2243,11 @@ def main(): parser.add_argument('--list-file-perms', action='store_true', help='List configured file permissions for an app') parser.add_argument('--list-other-perm-toggles', type=str, metavar='PERM_NAME', - help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features", "persistent")') + help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features")') parser.add_argument('--toggle-other-perms', type=str, metavar=('ENABLE/DISABLE'), help='Toggle other permissions on/off (True/False)') parser.add_argument('--perm-type', type=str, - help='Type of permission to toggle (shared, sockets, devices, features)') + help='Type of permission to toggle (shared, sockets, devices, features, persistent)') parser.add_argument('--perm-option', type=str, help='Specific permission option to toggle (e.g. network, ipc)') parser.add_argument('--list-other-perm-values', type=str, metavar='PERM_NAME', @@ -2262,7 +2266,7 @@ def main(): parser.add_argument('--global-list-file-perms', action='store_true', help='List configured file permissions for an app') parser.add_argument('--global-list-other-perm-toggles', type=str, metavar='PERM_NAME', - help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features", "persistent")') + help='List configured other permission toggles for an app (e.g. "shared", "sockets", "devices", "features")') parser.add_argument('--global-toggle-other-perms', type=str, metavar=('ENABLE/DISABLE'), help='Toggle other permissions on/off (True/False)') parser.add_argument('--global-list-other-perm-values', type=str, metavar='PERM_NAME', @@ -2538,14 +2542,14 @@ def handle_subcategories(args, searcher): def handle_add_file_perms(args, searcher): try: - success, message = add_file_permissions(args.id, args.add_file_perms, args.system) + success, message = add_file_permissions(args.id, args.add_file_perms, args.perm_type, args.system) print(f"{message}") except GLib.Error as e: print(f"{str(e)}") def handle_remove_file_perms(args, searcher): try: - success, message = remove_file_permissions(args.id, args.remove_file_perms, args.system) + success, message = remove_file_permissions(args.id, args.remove_file_perms, args.perm_type, args.system) print(f"{message}") except GLib.Error as e: print(f"{str(e)}") diff --git a/main.py b/main.py index 6a8c433..54f5272 100755 --- a/main.py +++ b/main.py @@ -313,6 +313,7 @@ class MainWindow(Gtk.Window): self.create_panels() self.refresh_data() + #self.refresh_local() # Select Trending by default self.select_default_category() @@ -1372,7 +1373,7 @@ class MainWindow(Gtk.Window): condition=lambda x: True ) add_rm_icon = "list-remove" - add_rm_style = "dark-remove-buton" + add_rm_style = "dark-remove-button" else: button = self.create_button( self.on_install_clicked, @@ -1389,6 +1390,23 @@ class MainWindow(Gtk.Window): button.get_style_context().add_class(add_rm_style) buttons_box.pack_end(button, False, False, 0) + # Install/Remove button + if is_installed: + button = self.create_button( + self.on_app_options_clicked, + app, + None, + condition=lambda x: True + ) + add_options_icon = "system-run" + add_options_style = "dark-remove-button" + + if button: + use_icon = Gio.Icon.new_for_string(add_options_icon) + button.set_image(Gtk.Image.new_from_gicon(use_icon, Gtk.IconSize.BUTTON)) + button.get_style_context().add_class(add_options_style) + buttons_box.pack_end(button, False, False, 0) + # Add Update button if available if is_updatable: update_button = self.create_button( @@ -1649,6 +1667,525 @@ class MainWindow(Gtk.Window): dialog.destroy() + def _add_bus_section(self, app_id, app, listbox, section_title, perm_type): + """Helper method to add System Bus or Session Bus section""" + # Add separator + sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) + listbox.add(sep) + + # Add section header + row_header = Gtk.ListBoxRow(selectable=False) + box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + label_header = Gtk.Label(label=f"{section_title}", + use_markup=True, xalign=0) + box_header.pack_start(label_header, True, True, 0) + row_header.add(box_header) + listbox.add(row_header) + + # Get permissions + success, perms = libflatpak_query.list_other_perm_values(app_id, perm_type, self.system_mode) + if not success: + perms = {"paths": []} + + # Add Talks section + talks_row = Gtk.ListBoxRow(selectable=False) + talks_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + talks_row.add(talks_box) + + talks_header = Gtk.Label(label="Talks", xalign=0) + talks_box.pack_start(talks_header, False, False, 0) + + # Add separator between header and paths + talks_box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), + False, False, 0) + + # Add talk paths + for path in perms["paths"]: + if "talk" in path: + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=path.split("=")[0], xalign=0) + vbox.pack_start(label, True, True, 0) + + btn = Gtk.Button(label="Remove") + btn.connect("clicked", self._on_remove_path, app_id, app, path, perm_type) + hbox.pack_end(btn, False, True, 0) + + talks_box.add(row) + + listbox.add(talks_row) + + # Add Owns section + owns_row = Gtk.ListBoxRow(selectable=False) + owns_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + owns_row.add(owns_box) + + owns_header = Gtk.Label(label="Owns", xalign=0) + owns_box.pack_start(owns_header, False, False, 0) + + # Add separator between header and paths + owns_box.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), + False, False, 0) + + # Add own paths + for path in perms["paths"]: + if "own" in path: + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=path.split("=")[0], xalign=0) + vbox.pack_start(label, True, True, 0) + + btn = Gtk.Button(label="Remove") + btn.connect("clicked", self._on_remove_path, app_id, app, path, perm_type) + hbox.pack_end(btn, False, True, 0) + + owns_box.add(row) + + owns_row.show_all() + listbox.add(owns_row) + + # Add add button + add_path_row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + add_path_row.add(hbox) + + btn = Gtk.Button(label="Add Path") + btn.connect("clicked", self._on_add_path, app_id, app, perm_type) + hbox.pack_end(btn, False, True, 0) + + listbox.add(add_path_row) + + def _add_path_section(self, app_id, app, listbox, section_title, perm_type): + """Helper method to add sections with paths (Persistent, Environment)""" + # Add separator + sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) + listbox.add(sep) + + # Add section header + row_header = Gtk.ListBoxRow(selectable=False) + box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + label_header = Gtk.Label(label=f"{section_title}", + use_markup=True, xalign=0) + box_header.pack_start(label_header, True, True, 0) + row_header.add(box_header) + listbox.add(row_header) + + # Get permissions + if perm_type == "persistent": + success, perms = libflatpak_query.list_other_perm_toggles(app_id, perm_type, self.system_mode) + else: + success, perms = libflatpak_query.list_other_perm_values(app_id, perm_type, self.system_mode) + if not success: + perms = {"paths": []} + + # Add paths + for path in perms["paths"]: + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=path, xalign=0) + vbox.pack_start(label, True, True, 0) + + btn = Gtk.Button(label="Remove") + btn.connect("clicked", self._on_remove_path, app_id, app, path, perm_type) + hbox.pack_end(btn, False, True, 0) + + listbox.add(row) + + # Add add button + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + btn = Gtk.Button(label="Add Path") + btn.connect("clicked", self._on_add_path, app_id, app, perm_type) + hbox.pack_end(btn, False, True, 0) + + listbox.add(row) + + def _add_filesystem_section(self, app_id, app, listbox, section_title): + """Helper method to add the Filesystems section""" + # Add separator + sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) + listbox.add(sep) + + # Add section header + row_header = Gtk.ListBoxRow(selectable=False) + box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + label_header = Gtk.Label(label=f"{section_title}", + use_markup=True, xalign=0) + box_header.pack_start(label_header, True, True, 0) + row_header.add(box_header) + listbox.add(row_header) + + # Get filesystem permissions + success, perms = libflatpak_query.list_file_perms(app_id, self.system_mode) + if not success: + perms = {"paths": [], "special_paths": []} + + # Add special paths as toggles + special_paths = [ + ("All user files", "home", "Access to all user files"), + ("All system files", "host", "Access to all system files"), + ("All system libraries, executables and static data", "host-os", "Access to system libraries and executables"), + ("All system configurations", "host-etc", "Access to system configurations") + ] + + for display_text, option, description in special_paths: + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=display_text, xalign=0) + desc = Gtk.Label(label=description, xalign=0) + vbox.pack_start(label, True, True, 0) + vbox.pack_start(desc, True, True, 0) + + switch = Gtk.Switch() + switch.props.valign = Gtk.Align.CENTER + switch.set_active(option in perms["special_paths"]) + switch.set_sensitive(True) + switch.connect("state-set", self._on_switch_toggled, app_id, "filesystems", option) + hbox.pack_end(switch, False, True, 0) + + listbox.add(row) + + # Add normal paths with remove buttons + for path in perms["paths"]: + if path != "": + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=path, xalign=0) + vbox.pack_start(label, True, True, 0) + + btn = Gtk.Button(label="Remove") + btn.connect("clicked", self._on_remove_path, app_id, app, path) + hbox.pack_end(btn, False, True, 0) + + listbox.add(row) + + # Add add button + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + btn = Gtk.Button(label="Add Path") + btn.connect("clicked", self._on_add_path, app_id, app) + hbox.pack_end(btn, False, True, 0) + + listbox.add(row) + + + def on_app_options_clicked(self, button, app): + """Handle the app options click""" + details = app.get_details() + app_id = details['id'] + + # Create window (as before) + self.options_window = Gtk.Window(title=f"{details['name']} Settings") + self.options_window.set_default_size(500, 700) + + # Set subtitle + header_bar = Gtk.HeaderBar(title=f"{details['name']} Settings", + subtitle="List of resources selectively granted to the application") + header_bar.set_show_close_button(True) + self.options_window.set_titlebar(header_bar) + + # Create main container with padding + box_outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + box_outer.set_border_width(20) + self.options_window.add(box_outer) + + # Create scrolled window for content + scrolled = Gtk.ScrolledWindow() + scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + + # Create list box for options + listbox = Gtk.ListBox() + listbox.set_selection_mode(Gtk.SelectionMode.NONE) + + # Add Portals section first + self._add_section(app_id, listbox, "Portals", section_options=[ + ("Background", "background", "Can run in the background"), + ("Notifications", "notifications", "Can send notifications"), + ("Microphone", "microphone", "Can listen to your microphone"), + ("Speakers", "speakers", "Can play sounds to your speakers"), + ("Camera", "camera", "Can record videos with your camera"), + ("Location", "location", "Can access your location") + ]) + + # Add other sections with correct permission types + self._add_section(app_id, listbox, "Shared", "shared", [ + ("Network", "network", "Can communicate over network"), + ("Inter-process communications", "ipc", "Can communicate with other applications") + ]) + + self._add_section(app_id, listbox, "Sockets", "sockets", [ + ("X11 windowing system", "x11", "Can access X11 display server"), + ("Wayland windowing system", "wayland", "Can access Wayland display server"), + ("Fallback to X11 windowing system", "fallback-x11", "Can fallback to X11 if Wayland unavailable"), + ("PulseAudio sound server", "pulseaudio", "Can access PulseAudio sound system"), + ("D-Bus session bus", "session-bus", "Can communicate with session D-Bus"), + ("D-Bus system bus", "system-bus", "Can communicate with system D-Bus"), + ("Secure Shell agent", "ssh-auth", "Can access SSH authentication agent"), + ("Smart cards", "pcsc", "Can access smart card readers"), + ("Printing system", "cups", "Can access printing subsystem"), + ("GPG-Agent directories", "gpg-agent", "Can access GPG keyring"), + ("Inherit Wayland socket", "inherit-wayland-socket", "Can inherit existing Wayland socket") + ]) + + self._add_section(app_id, listbox, "Devices", "devices", [ + ("GPU Acceleration", "dri", "Can use hardware graphics acceleration"), + ("Input devices", "input", "Can access input devices"), + ("Virtualization", "kvm", "Can access virtualization services"), + ("Shared memory", "shm", "Can use shared memory"), + ("All devices (e.g. webcam)", "all", "Can access all device files") + ]) + + self._add_section(app_id, listbox, "Features", "features", [ + ("Development syscalls", "devel", "Can perform development operations"), + ("Programs from other architectures", "multiarch", "Can execute programs from other architectures"), + ("Bluetooth", "bluetooth", "Can access Bluetooth hardware"), + ("Controller Area Network bus", "canbus", "Can access CAN bus"), + ("Application Shared Memory", "per-app-dev-shm", "Can use shared memory for IPC") + ]) + + # Add Filesystems section + self._add_filesystem_section(app_id, app, listbox, "Filesystems") + self._add_path_section(app_id, app, listbox, "Persistent", "persistent") + self._add_path_section(app_id, app, listbox, "Environment", "environment") + self._add_bus_section(app_id, app, listbox, "System Bus", "system_bus") + self._add_bus_section(app_id, app, listbox, "Session Bus", "session_bus") + + # Add widgets to container + box_outer.pack_start(scrolled, True, True, 0) + scrolled.add(listbox) + + # Connect destroy signal + self.options_window.connect("destroy", lambda w: w.destroy()) + + # Show window + self.options_window.show_all() + + def _add_section(self, app_id, listbox, section_title, perm_type=None, section_options=None): + """Helper method to add a section with multiple options""" + # Add separator + sep = Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL) + listbox.add(sep) + + # Add section header + row_header = Gtk.ListBoxRow(selectable=False) + box_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) + label_header = Gtk.Label(label=f"{section_title}", + use_markup=True, xalign=0) + box_header.pack_start(label_header, True, True, 0) + row_header.add(box_header) + listbox.add(row_header) + + # Handle portal permissions specially + if section_title == "Portals": + success, perms = libflatpak_query.portal_get_app_permissions(app_id) + if not success: + perms = {} + elif section_title in ["Persistent", "Environment", "System Bus", "Session Bus"]: + success, perms = libflatpak_query.list_other_perm_toggles(app_id, perm_type, self.system_mode) + if not success: + perms = {"paths": []} + else: + success, perms = libflatpak_query.list_other_perm_toggles(app_id, perm_type, self.system_mode) + if not success: + perms = {"paths": []} + + if section_options: + # Add options + for display_text, option, description in section_options: + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=display_text, xalign=0) + desc = Gtk.Label(label=description, xalign=0) + vbox.pack_start(label, True, True, 0) + vbox.pack_start(desc, True, True, 0) + + switch = Gtk.Switch() + switch.props.valign = Gtk.Align.CENTER + + # Handle portal permissions differently + if section_title == "Portals": + if option in perms: + switch.set_active(perms[option] == 'yes') + switch.set_sensitive(True) + else: + switch.set_sensitive(False) + else: + switch.set_active(option in [p.lower() for p in perms["paths"]]) + switch.set_sensitive(True) + + switch.connect("state-set", self._on_switch_toggled, app_id, perm_type, option) + hbox.pack_end(switch, False, True, 0) + + listbox.add(row) + + def _on_switch_toggled(self, switch, state, app_id, perm_type, option): + """Handle switch toggle events""" + if perm_type is None: # Portal section + success, message = libflatpak_query.portal_set_app_permissions( + option.lower(), + app_id, + "yes" if state else "no" + ) + else: + success, message = libflatpak_query.toggle_other_perms( + app_id, + perm_type, + option.lower(), + state, + self.system_mode + ) + + if not success: + switch.set_active(not state) + print(f"Error: {message}") + + def _on_remove_path(self, button, app_id, app, path, perm_type=None): + """Handle remove path button click""" + if perm_type: + if perm_type == "persistent": + success, message = libflatpak_query.remove_file_permissions( + app_id, + path, + "persistent", + self.system_mode + ) + else: + success, message = libflatpak_query.remove_permission_value( + app_id, + perm_type, + path, + self.system_mode + ) + else: + success, message = libflatpak_query.remove_file_permissions( + app_id, + path, + None, + self.system_mode + ) + if success: + # Refresh the current window + self.options_window.destroy() + self.on_app_options_clicked(None, app) + + def _on_add_path(self, button, app_id, app, perm_type=None): + """Handle add path button click""" + dialog = Gtk.Dialog( + title="Add Filesystem Path", + parent=self.options_window, + modal=True, + destroy_with_parent=True, + ) + + # Add buttons separately + dialog.add_button("Cancel", Gtk.ResponseType.CANCEL) + dialog.add_button("Add", Gtk.ResponseType.OK) + + entry = Gtk.Entry() + entry.set_placeholder_text("Enter filesystem path") + dialog.vbox.pack_start(entry, True, True, 0) + dialog.show_all() + + response = dialog.run() + if response == Gtk.ResponseType.OK: + path = entry.get_text() + if perm_type: + if perm_type == "persistent": + success, message = libflatpak_query.add_file_permissions( + app_id, + path, + "persistent", + self.system_mode + ) + else: + success, message = libflatpak_query.add_permission_value( + app_id, + perm_type, + path, + self.system_mode + ) + else: + success, message = libflatpak_query.add_file_permissions( + app_id, + path, + None, + self.system_mode + ) + if success: + # Refresh the current window + self.options_window.destroy() + self.on_app_options_clicked(None, app) + message_type = Gtk.MessageType.INFO + else: + message_type = Gtk.MessageType.ERROR + if message: + error_dialog = Gtk.MessageDialog( + transient_for=None, # Changed from self + modal=True, + destroy_with_parent=True, + message_type=message_type, + buttons=Gtk.ButtonsType.OK, + text=message + ) + error_dialog.run() + error_dialog.destroy() + dialog.destroy() + + def _add_option(self, parent_box, label_text, description): + """Helper method to add an individual option""" + row = Gtk.ListBoxRow(selectable=False) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) + row.add(hbox) + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + hbox.pack_start(vbox, True, True, 0) + + label = Gtk.Label(label=label_text, xalign=0) + desc = Gtk.Label(label=description, xalign=0) + vbox.pack_start(label, True, True, 0) + vbox.pack_start(desc, True, True, 0) + + switch = Gtk.Switch() + switch.props.valign = Gtk.Align.CENTER + hbox.pack_end(switch, False, True, 0) + + parent_box.add(row) + return row, switch + def on_update_clicked(self, button, app): """Handle the Remove button click with removal options""" details = app.get_details() diff --git a/subcategories_data.json b/subcategories_data.json index 9b4db0c..8e09ba9 100644 --- a/subcategories_data.json +++ b/subcategories_data.json @@ -2227,7 +2227,7 @@ } ], "query": "", - "processingTimeMs": 7, + "processingTimeMs": 8, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -3812,7 +3812,7 @@ } ], "query": "", - "processingTimeMs": 6, + "processingTimeMs": 5, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -10295,7 +10295,7 @@ } ], "query": "", - "processingTimeMs": 16, + "processingTimeMs": 19, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -12125,7 +12125,7 @@ } ], "query": "", - "processingTimeMs": 4, + "processingTimeMs": 3, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -13672,7 +13672,7 @@ } ], "query": "", - "processingTimeMs": 2, + "processingTimeMs": 3, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -14134,7 +14134,7 @@ } ], "query": "", - "processingTimeMs": 3, + "processingTimeMs": 2, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -17220,7 +17220,7 @@ "verification_website": null, "verification_timestamp": null, "runtime": "org.freedesktop.Sdk/x86_64/24.08", - "updated_at": 1743670880, + "updated_at": 1743689559, "arches": [ "x86_64", "aarch64" @@ -18256,7 +18256,7 @@ } ], "query": "", - "processingTimeMs": 3, + "processingTimeMs": 2, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -18992,7 +18992,7 @@ } ], "query": "", - "processingTimeMs": 4, + "processingTimeMs": 3, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -34567,7 +34567,7 @@ "project_license": "MPL-2.0", "is_free_license": true, "app_id": "info.cemu.Cemu", - "icon": "https://dl.flathub.org/media/info/cemu/Cemu/c2ee1c87418d1055b5efda32173e2eeb/icons/128x128/info.cemu.Cemu.png", + "icon": "https://dl.flathub.org/media/info/cemu/Cemu/87640c667068bea4e270477be92997a3/icons/128x128/info.cemu.Cemu.png", "main_categories": "game", "sub_categories": [ "Emulator" @@ -34581,7 +34581,7 @@ "verification_website": "cemu.info", "verification_timestamp": "1679779906", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1742475302, + "updated_at": 1743689882, "arches": [ "x86_64" ], @@ -35141,7 +35141,7 @@ "project_license": "GPL-3.0", "is_free_license": true, "app_id": "io.github.gopher64.gopher64", - "icon": "https://dl.flathub.org/media/io/github/gopher64.gopher64/364d7e1b846fb507371416d5721b81a2/icons/128x128/io.github.gopher64.gopher64.png", + "icon": "https://dl.flathub.org/media/io/github/gopher64.gopher64/459074e53a887ab9af8f43da34f41274/icons/128x128/io.github.gopher64.gopher64.png", "main_categories": "game", "sub_categories": [ "Emulator" @@ -35155,7 +35155,7 @@ "verification_website": null, "verification_timestamp": "1699221966", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1742988449, + "updated_at": 1743677523, "arches": [ "x86_64", "aarch64" @@ -42183,7 +42183,7 @@ } ], "query": "", - "processingTimeMs": 13, + "processingTimeMs": 12, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -52249,7 +52249,7 @@ "project_license": "LicenseRef-proprietary", "is_free_license": false, "app_id": "com.wireframesketcher.WireframeSketcher", - "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/63c0b229d59a15a516bb37f18c159575/icons/128x128/com.wireframesketcher.WireframeSketcher.png", + "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/08fc4f4f982674baf2112878c7fa2e3f/icons/128x128/com.wireframesketcher.WireframeSketcher.png", "main_categories": "graphics", "sub_categories": [ "VectorGraphics", @@ -52263,8 +52263,8 @@ "verification_login_is_organization": "false", "verification_website": "wireframesketcher.com", "verification_timestamp": "1728995861", - "runtime": "org.gnome.Platform/x86_64/47", - "updated_at": 1738504410, + "runtime": "org.gnome.Platform/x86_64/48", + "updated_at": 1743686066, "arches": [ "x86_64" ], @@ -54042,7 +54042,7 @@ "project_license": "LicenseRef-proprietary=https://www.popcornfx.com/terms-and-conditions", "is_free_license": false, "app_id": "com.popcornfx.PopcornFXEditor", - "icon": "https://dl.flathub.org/media/com/popcornfx/PopcornFXEditor/bbc16f228a468060e76c2bcf1f055c11/icons/128x128/com.popcornfx.PopcornFXEditor.png", + "icon": "https://dl.flathub.org/media/com/popcornfx/PopcornFXEditor/8950264504787fde2a41a89e6cc59a6b/icons/128x128/com.popcornfx.PopcornFXEditor.png", "main_categories": "graphics", "sub_categories": [ "3DGraphics" @@ -54056,7 +54056,7 @@ "verification_website": "popcornfx.com", "verification_timestamp": "1740390969", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1740561877, + "updated_at": 1743670231, "arches": [ "x86_64" ], @@ -60274,7 +60274,7 @@ "project_license": "LicenseRef-proprietary", "is_free_license": false, "app_id": "com.wireframesketcher.WireframeSketcher", - "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/63c0b229d59a15a516bb37f18c159575/icons/128x128/com.wireframesketcher.WireframeSketcher.png", + "icon": "https://dl.flathub.org/media/com/wireframesketcher/WireframeSketcher/08fc4f4f982674baf2112878c7fa2e3f/icons/128x128/com.wireframesketcher.WireframeSketcher.png", "main_categories": "graphics", "sub_categories": [ "VectorGraphics", @@ -60288,8 +60288,8 @@ "verification_login_is_organization": "false", "verification_website": "wireframesketcher.com", "verification_timestamp": "1728995861", - "runtime": "org.gnome.Platform/x86_64/47", - "updated_at": 1738504410, + "runtime": "org.gnome.Platform/x86_64/48", + "updated_at": 1743686066, "arches": [ "x86_64" ], @@ -63677,7 +63677,7 @@ "project_license": "AGPL-3.0-only", "is_free_license": true, "app_id": "org.signal.Signal", - "icon": "https://dl.flathub.org/media/org/signal/Signal/2566ff6b14efe7a97e76b251a00480c5/icons/128x128/org.signal.Signal.png", + "icon": "https://dl.flathub.org/media/org/signal/Signal/7958a28cf241175ba36c885f9577b5f3/icons/128x128/org.signal.Signal.png", "main_categories": "network", "sub_categories": [ "InstantMessaging", @@ -63692,7 +63692,7 @@ "verification_website": null, "verification_timestamp": null, "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1743062228, + "updated_at": 1743667211, "arches": [ "x86_64" ], @@ -69733,7 +69733,7 @@ } ], "query": "", - "processingTimeMs": 3, + "processingTimeMs": 2, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -69918,7 +69918,7 @@ "project_license": "AGPL-3.0-only", "is_free_license": true, "app_id": "org.signal.Signal", - "icon": "https://dl.flathub.org/media/org/signal/Signal/2566ff6b14efe7a97e76b251a00480c5/icons/128x128/org.signal.Signal.png", + "icon": "https://dl.flathub.org/media/org/signal/Signal/7958a28cf241175ba36c885f9577b5f3/icons/128x128/org.signal.Signal.png", "main_categories": "network", "sub_categories": [ "InstantMessaging", @@ -69933,7 +69933,7 @@ "verification_website": null, "verification_timestamp": null, "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1743062228, + "updated_at": 1743667211, "arches": [ "x86_64" ], @@ -73160,7 +73160,7 @@ } ], "query": "", - "processingTimeMs": 12, + "processingTimeMs": 11, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -74158,7 +74158,7 @@ } ], "query": "", - "processingTimeMs": 2, + "processingTimeMs": 3, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -76180,7 +76180,7 @@ "project_license": "LGPL-3.0-only", "is_free_license": true, "app_id": "org.tribler.Tribler", - "icon": "https://dl.flathub.org/media/org/tribler/Tribler/a2f52ea30ba1a4929dbb202e03d3e570/icons/128x128/org.tribler.Tribler.png", + "icon": "https://dl.flathub.org/media/org/tribler/Tribler/c0501155d99aa6ac0b25612ca1cb8d81/icons/128x128/org.tribler.Tribler.png", "main_categories": "network", "sub_categories": [ "P2P" @@ -76194,7 +76194,7 @@ "verification_website": "tribler.org", "verification_timestamp": "1706271488", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1742060332, + "updated_at": 1743669027, "arches": [ "x86_64", "aarch64" @@ -78324,7 +78324,7 @@ "project_license": "BSD-3-Clause and LGPL-2.1+ and Apache-2.0 and IJG and MIT and GPL-2.0+ and ISC and OpenSSL and (MPL-1.1 or GPL-2.0 or LGPL-2.0)", "is_free_license": true, "app_id": "io.github.ungoogled_software.ungoogled_chromium", - "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/39894c6a7477629d3ab86d3d95ef4aeb/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png", + "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/7c63ca7b5da5a57b933e1111746511e1/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png", "main_categories": "network", "sub_categories": [ "WebBrowser", @@ -78340,7 +78340,7 @@ "verification_website": null, "verification_timestamp": "1713606111", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1743133926, + "updated_at": 1743673160, "arches": [ "x86_64", "aarch64" @@ -79779,7 +79779,7 @@ "project_license": "LicenseRef-proprietary=https://wavebox.io/eula", "is_free_license": false, "app_id": "io.wavebox.Wavebox", - "icon": "https://dl.flathub.org/media/io/wavebox/Wavebox/2b3f6b5b0cd051bf329fad85e03875cc/icons/128x128/io.wavebox.Wavebox.png", + "icon": "https://dl.flathub.org/media/io/wavebox/Wavebox/7456cfd08230164fd31c4dc164e96a2a/icons/128x128/io.wavebox.Wavebox.png", "main_categories": "network", "sub_categories": [ "WebBrowser" @@ -79793,7 +79793,7 @@ "verification_website": null, "verification_timestamp": null, "runtime": "org.freedesktop.Platform/x86_64/21.08", - "updated_at": 1742817444, + "updated_at": 1743674289, "arches": [ "x86_64" ], @@ -79928,7 +79928,7 @@ } ], "query": "", - "processingTimeMs": 6, + "processingTimeMs": 7, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -79959,7 +79959,7 @@ "project_license": "BSD-3-Clause and LGPL-2.1+ and Apache-2.0 and IJG and MIT and GPL-2.0+ and ISC and OpenSSL and (MPL-1.1 or GPL-2.0 or LGPL-2.0)", "is_free_license": true, "app_id": "io.github.ungoogled_software.ungoogled_chromium", - "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/39894c6a7477629d3ab86d3d95ef4aeb/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png", + "icon": "https://dl.flathub.org/media/io/github/ungoogled_software.ungoogled_chromium/7c63ca7b5da5a57b933e1111746511e1/icons/128x128/io.github.ungoogled_software.ungoogled_chromium.png", "main_categories": "network", "sub_categories": [ "WebBrowser", @@ -79975,7 +79975,7 @@ "verification_website": null, "verification_timestamp": "1713606111", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1743133926, + "updated_at": 1743673160, "arches": [ "x86_64", "aarch64" @@ -82634,7 +82634,7 @@ } ], "query": "", - "processingTimeMs": 2, + "processingTimeMs": 3, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -84203,7 +84203,7 @@ } ], "query": "", - "processingTimeMs": 7, + "processingTimeMs": 6, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -84436,7 +84436,7 @@ "project_license": "MIT", "is_free_license": true, "app_id": "com.super_productivity.SuperProductivity", - "icon": "https://dl.flathub.org/media/com/super_productivity/SuperProductivity/53bb571f02820641926d8f042929a3c8/icons/128x128/com.super_productivity.SuperProductivity.png", + "icon": "https://dl.flathub.org/media/com/super_productivity/SuperProductivity/be0a31f47f3837c8b137110fea0bb11c/icons/128x128/com.super_productivity.SuperProductivity.png", "main_categories": "office", "sub_categories": [ "ProjectManagement" @@ -84450,7 +84450,7 @@ "verification_website": "super-productivity.com", "verification_timestamp": "1733677286", "runtime": "org.freedesktop.Platform/x86_64/24.08", - "updated_at": 1741964323, + "updated_at": 1743686508, "arches": [ "x86_64" ], @@ -85457,7 +85457,7 @@ "verification_website": null, "verification_timestamp": "1681472057", "runtime": "org.kde.Platform/x86_64/6.8", - "updated_at": 1743633547, + "updated_at": 1743677378, "arches": [ "x86_64", "aarch64" @@ -85666,7 +85666,7 @@ "project_license": "Zlib", "is_free_license": true, "app_id": "com.strlen.TreeSheets", - "icon": "https://dl.flathub.org/media/com/strlen/TreeSheets/a56dee5995e3e5b4742dfc3313966697/icons/128x128/com.strlen.TreeSheets.png", + "icon": "https://dl.flathub.org/media/com/strlen/TreeSheets/fa00efbb2fc13f353508162cb663dba9/icons/128x128/com.strlen.TreeSheets.png", "main_categories": "office", "sub_categories": [ "Spreadsheet", @@ -85682,7 +85682,7 @@ "verification_website": null, "verification_timestamp": null, "runtime": "org.freedesktop.Platform/x86_64/23.08", - "updated_at": 1743492010, + "updated_at": 1743674544, "arches": [ "x86_64", "aarch64" @@ -87983,7 +87983,7 @@ "verification_website": null, "verification_timestamp": null, "runtime": "org.gnome.Platform/x86_64/46", - "updated_at": 1743647957, + "updated_at": 1743681287, "arches": [ "x86_64", "aarch64" @@ -89321,7 +89321,7 @@ } ], "query": "", - "processingTimeMs": 2, + "processingTimeMs": 3, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -90915,7 +90915,7 @@ } ], "query": "", - "processingTimeMs": 3, + "processingTimeMs": 2, "hitsPerPage": 250, "page": 1, "totalPages": 1, @@ -93377,7 +93377,7 @@ } ], "query": "", - "processingTimeMs": 4, + "processingTimeMs": 5, "hitsPerPage": 250, "page": 1, "totalPages": 1,