APP PERMISSIONS TOGGLES ARE DONE
This commit is contained in:
parent
c5cbaf45d4
commit
cf594464a5
3 changed files with 614 additions and 73 deletions
|
|
@ -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)}")
|
||||
|
|
|
|||
539
main.py
539
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"<b>{section_title}</b>",
|
||||
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"<b>{section_title}</b>",
|
||||
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"<b>{section_title}</b>",
|
||||
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"<b>{section_title}</b>",
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue