diff --git a/po/pardus-software.pot b/po/pardus-software.pot
index c962ac9..4d43039 100644
--- a/po/pardus-software.pot
+++ b/po/pardus-software.pot
@@ -17,6 +17,22 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+#: src/MainWindow.py:767
+msgid "Invalid file"
+msgstr ""
+
+#: src/MainWindow.py:767
+msgid "The file does not exist or is not a .deb package."
+msgstr ""
+
+#: src/MainWindow.py:774
+msgid "Cannot read package"
+msgstr ""
+
+#: src/MainWindow.py:774
+msgid "This file is not a valid Debian package."
+msgstr ""
+
#: ui/MainWindow.glade:48
msgid "Settings"
msgstr ""
diff --git a/po/tr.po b/po/tr.po
index aa69d7a..b8bd7e4 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -19,6 +19,22 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Gtranslator 48.0\n"
+#: src/MainWindow.py:767
+msgid "Invalid file"
+msgstr "Geçersiz dosya"
+
+#: src/MainWindow.py:767
+msgid "The file does not exist or is not a .deb package."
+msgstr "Dosya bulunamadı veya bir .deb paketi değil."
+
+#: src/MainWindow.py:774
+msgid "Cannot read package"
+msgstr "Paket okunamadı"
+
+#: src/MainWindow.py:774
+msgid "This file is not a valid Debian package."
+msgstr "Bu dosya geçerli bir Debian paketi değil."
+
#: ui/MainWindow.glade:48
msgid "Settings"
msgstr "Ayarlar"
diff --git a/setup.py b/setup.py
index 7cc5195..562659a 100644
--- a/setup.py
+++ b/setup.py
@@ -46,7 +46,8 @@ def pythonic_version(version):
("/usr/bin", ["pardus-software"]),
("/usr/share/applications",
["tr.org.pardus.software.desktop",
- "tr.org.pardus.software-open.desktop"]),
+ "tr.org.pardus.software-open.desktop",
+ "tr.org.pardus.software-deb.desktop"]),
("/usr/share/polkit-1/actions",
["tr.org.pardus.pkexec.pardus-software.policy"]),
("/usr/share/pardus/pardus-software/css",
diff --git a/src/Actions.py b/src/Actions.py
index 0904e26..b76be18 100755
--- a/src/Actions.py
+++ b/src/Actions.py
@@ -54,6 +54,22 @@ def install(packages):
cmd = ["apt", "install", "-yq", "-o", "APT::Status-Fd=2"] + apt_options + ["--"] + valid_packages
subprocess.call(cmd, env={**os.environ, 'DEBIAN_FRONTEND': 'noninteractive'})
+ def install_local(debfile):
+ debfile = os.path.abspath(debfile)
+
+ if not os.path.isfile(debfile):
+ print(f"File not found: {debfile}", file=sys.stderr)
+ return
+
+ if not debfile.endswith(".deb"):
+ print("Only .deb files can be installed.", file=sys.stderr)
+ return
+
+ # a path (with a slash) makes apt treat it as a local file
+ cmd = ["apt", "install", "-yq", "-o", "APT::Status-Fd=2",
+ "-o", "Dpkg::Options::=--force-confnew", "--", debfile]
+ subprocess.call(cmd, env={**os.environ, 'DEBIAN_FRONTEND': 'noninteractive'})
+
def remove(packages):
apt_options, valid_packages = parse_packages(packages)
@@ -156,6 +172,8 @@ def kill(pid):
if control_lock():
if sys.argv[1] == "install":
install(sys.argv[2])
+ elif sys.argv[1] == "installlocal":
+ install_local(sys.argv[2])
elif sys.argv[1] == "remove":
remove(sys.argv[2])
elif sys.argv[1] == "upgrade":
diff --git a/src/Main.py b/src/Main.py
index 55279e2..04cf000 100755
--- a/src/Main.py
+++ b/src/Main.py
@@ -44,6 +44,15 @@ def __init__(self, *args, **kwargs):
None,
)
+ self.add_main_option(
+ "install-deb",
+ ord("i"),
+ GLib.OptionFlags(0),
+ GLib.OptionArg(1),
+ "Install a local .deb package",
+ None,
+ )
+
# self.add_main_option(
# "remove",
# ord("r"),
diff --git a/src/MainWindow.py b/src/MainWindow.py
index ab45045..fcdd99d 100644
--- a/src/MainWindow.py
+++ b/src/MainWindow.py
@@ -10,6 +10,7 @@
import locale
import os
import re
+import shutil
import subprocess
import threading
import json
@@ -398,6 +399,9 @@ def __init__(self, application):
self.queue = []
self.inprogress = False
+ self.localdeb_active = False
+ self._localdeb_tmp = None
+
self.isbroken = False
self.connection_error_after = False
@@ -697,6 +701,10 @@ def control_available_apps(self):
self.set_available_apps(available=self.UserSettings.config_only_available)
def control_args(self):
+ if "install-deb" in self.Application.args.keys():
+ debfile = self.Application.args["install-deb"]
+ GLib.idle_add(self.open_localdeb, debfile)
+ return
if "details" in self.Application.args.keys():
pardus_found = False
myapps_found = False
@@ -749,6 +757,156 @@ def control_args(self):
except Exception as e:
self.Logger.exception("{}".format(e))
+ def open_localdeb(self, debfile):
+ """Open a local .deb file for installation (--install-deb option)."""
+ import apt.debfile as aptdeb
+
+ debfile = os.path.abspath(debfile)
+
+ if not os.path.isfile(debfile) or not debfile.endswith(".deb"):
+ self.localdeb_error_dialog(_("Invalid file"), _("The file does not exist or is not a .deb package."))
+ return
+
+ try:
+ deb = aptdeb.DebPackage(debfile)
+ except Exception as e:
+ self.Logger.exception(f"open_localdeb: {e}")
+ self.localdeb_error_dialog(_("Cannot read package"), _("This file is not a valid Debian package."))
+ return
+
+ pkgname = deb.pkgname or os.path.basename(debfile)
+
+ def field(key):
+ try:
+ return deb[key] if key in deb else ""
+ except Exception:
+ return ""
+
+ maintainer = field("Maintainer")
+ maintainer_name, maintainer_mail = maintainer, ""
+ try:
+ m = re.match(r"^(.*?)(?:\s*<([^>]+)>)?$", maintainer)
+ if m:
+ maintainer_name = (m.group(1) or "").strip()
+ maintainer_mail = (m.group(2) or "").strip()
+ except Exception as e:
+ self.Logger.exception(f"open_localdeb maintainer: {e}")
+
+ try:
+ installed_size_kb = int(field("Installed-Size") or 0)
+ except Exception:
+ installed_size_kb = 0
+
+ try:
+ deb_size = os.path.getsize(debfile)
+ except Exception:
+ deb_size = 0
+
+ # get the .desktop launcher if any, for "Open" button after install
+ desktop = ""
+ try:
+ for f in deb.filelist:
+ if "/applications/" in f and f.endswith(".desktop"):
+ desktop = os.path.basename(f)
+ break
+ except Exception as e:
+ self.Logger.exception(f"open_localdeb filelist: {e}")
+
+ # check() gives an early reason if can't install, still let apt try since it can pull missing deps
+ check_msg = ""
+ try:
+ if not deb.check():
+ check_msg = getattr(deb, "_failure_string", "") or ""
+ except Exception as e:
+ self.Logger.exception(f"open_localdeb check: {e}")
+
+ details = {
+ "name": pkgname,
+ "icon_name": "package-x-generic",
+ "description": field("Description"),
+ "version": field("Version"),
+ "section": field("Section"),
+ "homepage": field("Homepage"),
+ "license": "",
+ "maintainer_name": maintainer_name,
+ "maintainer_mail": maintainer_mail,
+ "depends": field("Depends"),
+ "installed_size_kb": installed_size_kb,
+ "deb_size": deb_size,
+ "check_msg": check_msg,
+ "localdeb": debfile,
+ "desktop": desktop,
+ }
+
+ # show it on the normal details page (source=2)
+ self.set_app_details_page({pkgname: details}, source=2)
+
+ def localdeb_error_dialog(self, title, message):
+ dialog = Gtk.MessageDialog(transient_for=self.MainWindow, modal=True,
+ message_type=Gtk.MessageType.ERROR,
+ buttons=Gtk.ButtonsType.CLOSE, text=title)
+ dialog.format_secondary_text(message)
+ dialog.run()
+ dialog.destroy()
+
+ def set_localdeb_sizes(self, details):
+ """Populate the details page size/dependency panel from a local .deb."""
+ self.ui_ad_sizetitle_label.set_text(_("Package Size"))
+ self.ui_ad_required_sizetitle_label.set_text(_("Installed Size"))
+
+ deb_size = details.get("deb_size") or 0
+ installed_size = (details.get("installed_size_kb") or 0) * 1024
+
+ self.ui_ad_size_label.set_text(
+ self.Package.beauty_size(deb_size) if deb_size else _("None"))
+ self.ui_ad_required_size_label.set_text(
+ self.Package.beauty_size(installed_size) if installed_size else _("None"))
+ self.ui_ad_top_size_label.set_text(
+ self.Package.beauty_size(installed_size) if installed_size else _("None"))
+
+ deps_raw = details.get("depends") or ""
+ deplist = [d.strip().split()[0] for d in re.split(r"[,|]", deps_raw) if d.strip()]
+ if deplist:
+ self.ui_ad_install_list_label.set_text(", ".join(deplist))
+ self.ui_ad_install_list_count_label.set_text(f"({len(deplist)})")
+ self.ui_ad_install_list_box.set_visible(True)
+ self.ui_ad_top_depends_count_label.set_text(f"{len(deplist)}")
+ else:
+ self.ui_ad_install_list_box.set_visible(False)
+ self.ui_ad_top_depends_count_label.set_text("0")
+
+ self.ui_ad_remove_list_box.set_visible(False)
+ self.ui_ad_broken_list_box.set_visible(False)
+
+ def set_localdeb_action_button(self, is_installed, details=None):
+ """Set the details action button for a local .deb.
+ Open (name 3) if installed with a launcher, else Install (name 1).
+ """
+ if details is None:
+ dic = self.ui_myapp_name_dic if isinstance(self.ui_myapp_name_dic, dict) else {}
+ details = next(iter(dic.values()), {}) if dic else {}
+ desktop = (details or {}).get("desktop", "")
+
+ self.ui_ad_action_button.remove(self.ui_ad_action_button.get_children()[0])
+ label = Gtk.Label.new()
+ label.set_line_wrap(False)
+ label.set_justify(Gtk.Justification.LEFT)
+ label.set_max_width_chars(6)
+ label.set_ellipsize(Pango.EllipsizeMode.END)
+ self.ui_ad_action_button.add(label)
+ if is_installed and desktop:
+ self.set_button_class(self.ui_ad_action_button, 4)
+ label.set_markup("{}".format(_("Open")))
+ self.ui_ad_action_button.name = 3
+ else:
+ self.set_button_class(self.ui_ad_action_button, 0)
+ label.set_markup("{}".format(_("Install")))
+ self.ui_ad_action_button.name = 1
+ self.ui_ad_action_button.set_sensitive(True)
+ self.ui_ad_action_button.show_all()
+ self.ui_ad_remove_button.set_visible(bool(is_installed))
+ self.ui_ad_remove_button.set_sensitive(bool(is_installed))
+
def set_initial_home(self):
self.Logger.info("in set_initial_home")
@@ -1532,8 +1690,11 @@ def app_widget_action_clicked(self, button):
if button.name == 9:
self.Logger.info("{} opening details page".format(app_name))
- repo_app = details.get("repo_app", "")
- self.set_app_details_page(app, source=1 if not repo_app else 2)
+ if details.get("localdeb"):
+ self.set_app_details_page(app, source=2)
+ else:
+ repo_app = details.get("repo_app", "")
+ self.set_app_details_page(app, source=1 if not repo_app else 2)
return
self.Logger.info("app_name: {}".format(app_name))
@@ -1543,18 +1704,22 @@ def app_widget_action_clicked(self, button):
command = self.get_action_command(app_name, details)
+ # install action (name 1) carries .deb path, remove (name 0) uses normal path
+ localdeb = details.get("localdeb") if button.name == 1 else None
+
desktop_id = details.get("desktop", "")
self.ui_header_queue_button.set_visible(True)
self.ui_queue_stack.set_visible_child_name("inprogress")
- self.queue.append({"name": app_name, "command": command, "desktop_id": desktop_id, "upgrade": button.name == 2})
+ self.queue.append({"name": app_name, "command": command, "desktop_id": desktop_id,
+ "upgrade": button.name == 2, "localdeb": localdeb})
self.add_to_queue_ui(app_name, button.name == 2, details.get("icon_name"))
self.Logger.info(f"queue_add: app: {app_name}")
self.Logger.info(f"queue: {self.queue}")
if not self.inprogress:
- self.action_package(app_name, command, desktop_id, button.name == 2)
+ self.action_package(app_name, command, desktop_id, button.name == 2, localdeb)
self.Logger.info("action_package app: {}, command: {}, desktop_id: {}, upgrade: {}".format(
app_name, command, desktop_id, button.name == 2))
@@ -1610,7 +1775,9 @@ def get_action_command(self, app_name, details):
return command
def on_ui_ad_action_button_clicked(self, button):
- if self.ui_app_name in self.apps_full.keys():
+ if self.localdeb_active:
+ name = self.ui_myapp_name_dic
+ elif self.ui_app_name in self.apps_full.keys():
name = self.ui_app_name
else:
name = self.ui_myapp_name_dic
@@ -1618,7 +1785,9 @@ def on_ui_ad_action_button_clicked(self, button):
self.app_widget_action_clicked(button)
def on_ui_ad_remove_button_clicked(self, button):
- if self.ui_app_name in self.apps_full.keys():
+ if self.localdeb_active:
+ name = self.ui_myapp_name_dic
+ elif self.ui_app_name in self.apps_full.keys():
name = self.ui_app_name
else:
name = self.ui_myapp_name_dic
@@ -1639,8 +1808,9 @@ def launch_desktop_file(self, desktop):
def add_to_queue_ui(self, app_name, upgrade=False, icon_name=None):
listbox = self.create_queue_widget(app_name, upgrade, icon_name)
- GLib.idle_add(self.ui_queue_flowbox.insert, listbox, -1)
- GLib.idle_add(self.ui_queue_flowbox.show_all)
+ # insert synchronously so the widget exists before the process can exit
+ self.ui_queue_flowbox.insert(listbox, -1)
+ self.ui_queue_flowbox.show_all()
def start_highlight_queue(self):
if self.UserSettings.config_animations:
@@ -1652,7 +1822,7 @@ def remove_highlight_queue(self):
self.ui_header_queue_button.get_style_context().remove_class("queue-highlight")
return False
- def action_package(self, app_name, command, desktop_id="", upgrade=False):
+ def action_package(self, app_name, command, desktop_id="", upgrade=False, localdeb=None):
self.inprogress = True
self.inprogress_app_name = app_name
@@ -1662,7 +1832,14 @@ def action_package(self, app_name, command, desktop_id="", upgrade=False):
self.isinstalled = self.Package.isinstalled(app_name)
self.isupgrade = self.Package.is_upgradable(app_name) and upgrade
- if self.isinstalled is True:
+ if localdeb:
+ # install a local .deb, treat as install for the progress UI
+ self.isinstalled = False
+ self.isupgrade = False
+ localdeb = self.stage_localdeb(localdeb)
+ command = ["/usr/bin/pkexec", os.path.dirname(os.path.abspath(__file__)) + "/Actions.py",
+ "installlocal", localdeb]
+ elif self.isinstalled is True:
if self.isupgrade:
command = ["/usr/bin/pkexec", os.path.dirname(os.path.abspath(__file__)) + "/Actions.py", "upgrade",
self.inprogress_command]
@@ -1682,6 +1859,22 @@ def action_package(self, app_name, command, desktop_id="", upgrade=False):
self.pid = self.action_process(command)
self.Logger.info(f"started pid : {self.pid} with command: {command}")
+ def stage_localdeb(self, debfile):
+ """Copy the .deb to /tmp so the root apt can read it since the file may be on a mount root can't access."""
+ try:
+ tmpdir = os.path.join("/tmp", "pardus-software")
+ os.makedirs(tmpdir, exist_ok=True)
+ os.chmod(tmpdir, 0o755)
+ dest = os.path.join(tmpdir, os.path.basename(debfile))
+ if os.path.abspath(debfile) != dest:
+ shutil.copyfile(debfile, dest)
+ os.chmod(dest, 0o644)
+ self._localdeb_tmp = dest
+ return dest
+ except Exception as e:
+ self.Logger.exception(f"stage_localdeb: {e}")
+ return debfile
+
def update_app_widget_label(self, app_name, from_queue_cancelled=False):
self.Logger.info("inprogress_app_name: {}, app_name: {}".format(self.inprogress_app_name, app_name))
def to_spinner(action_button):
@@ -1818,6 +2011,9 @@ def to_normal(action_button):
if (self.inprogress_app_name != app_name) != from_queue_cancelled:
to_spinner(self.ui_ad_action_button)
self.ui_ad_remove_button.set_sensitive(False)
+ elif self.localdeb_active:
+ # keep the local .deb button state
+ self.set_localdeb_action_button(self.Package.isinstalled(app_name))
else:
to_normal(self.ui_ad_action_button)
self.ui_ad_remove_button.set_sensitive(True)
@@ -2255,7 +2451,8 @@ def create_queue_widget(self, app, upgrade=False, icon_name=None):
version_label.set_line_wrap(False)
version_label.set_max_width_chars(21 if self.display_width >= 1920 else 13)
version_label.set_ellipsize(Pango.EllipsizeMode.END)
- version_label.set_markup("{}".format(self.Package.candidate_version(app)))
+ queue_version = self.Package.candidate_version(app) if self.Package.control_package_cache(app) else ""
+ version_label.set_markup("{}".format(queue_version or ""))
box_version = Gtk.Box.new(Gtk.Orientation.VERTICAL, 6)
box_version.props.halign = Gtk.Align.END
@@ -2301,9 +2498,9 @@ def create_queue_widget(self, app, upgrade=False, icon_name=None):
listbox.set_selection_mode(Gtk.SelectionMode.NONE)
# listbox.connect("row-activated", self.on_app_listbox_row_activated)
listbox_row = Gtk.ListBoxRow()
- GLib.idle_add(listbox_row.add, box)
+ listbox_row.add(box)
listbox_row.name = app
- GLib.idle_add(listbox.add, listbox_row)
+ listbox.add(listbox_row)
GLib.idle_add(listbox.get_style_context().add_class, "pardus-software-listbox-mostdown")
@@ -2711,6 +2908,9 @@ def set_app_details_page(self, app, source=1):
self.ui_app_name = app_name
+ # local .deb uses the source=2 layout but is not in the apt cache
+ self.localdeb_active = bool(isinstance(details, dict) and details.get("localdeb"))
+
self.clear_app_details()
self.ui_ad_image_scrolledwindow.set_visible(source == 1)
@@ -2811,7 +3011,17 @@ def set_app_details_page(self, app, source=1):
is_openable = self.get_desktop_filename_from_app_name(app_name) != ""
else:
is_openable = details.get("id", False)
- if is_installed is not None:
+ if self.localdeb_active:
+ # show Install, then Open (if it has a launcher) and Remove once installed
+ self.set_localdeb_action_button(is_installed, details)
+ self.set_localdeb_sizes(details)
+ if details.get("check_msg"):
+ self.ui_ad_important_label.set_text(details["check_msg"])
+ self.ui_ad_important_label.set_tooltip_text("")
+ self.ui_ad_important_label.set_visible(True)
+ else:
+ self.ui_ad_important_label.set_visible(False)
+ elif is_installed is not None:
command = self.get_action_command(app_name, details)
threading.Thread(target=self.app_detail_requireds_thread, args=(command,), daemon=True).start()
if is_installed:
@@ -2862,7 +3072,8 @@ def set_app_details_page(self, app, source=1):
if self.ui_ad_remove_button.get_visible():
self.ui_ad_remove_button.set_sensitive(False)
- origin_info = self.Package.origins(app_name)
+ # local .deb is not in the cache, skip the origin lookup
+ origin_info = None if self.localdeb_active else self.Package.origins(app_name)
component = getattr(origin_info, "component", "")
origin = getattr(origin_info, "origin", "")
if component == "non-free" or (details.get("component") or {}).get("name", "") == "non-free":
@@ -2898,20 +3109,29 @@ def set_app_details_page(self, app, source=1):
app_section = ""
app_version = ""
app_license = ""
- record = self.Package.get_record(app_name)
- if record:
- app_section = record.get("Section", "")
- app_version = record.get("Version", "")
- maintainer = record.get("Maintainer", "")
- maintainer_web = record.get("Homepage", "")
- app_license = record.get("License", "")
- try:
- match = re.match(r"^(.*?)(?:\s*<([^>]+)>)?$", maintainer)
- if match:
- maintainer_name = (match.group(1) or "").strip()
- maintainer_mail = (match.group(2) or "").strip()
- except Exception as e:
- self.Logger.exception("{}".format(e))
+ if self.localdeb_active:
+ # local .deb is not in the cache, read fields directly
+ app_section = details.get("section", "")
+ app_version = details.get("version", "")
+ maintainer_web = details.get("homepage", "")
+ app_license = details.get("license", "")
+ maintainer_name = details.get("maintainer_name", "")
+ maintainer_mail = details.get("maintainer_mail", "")
+ else:
+ record = self.Package.get_record(app_name)
+ if record:
+ app_section = record.get("Section", "")
+ app_version = record.get("Version", "")
+ maintainer = record.get("Maintainer", "")
+ maintainer_web = record.get("Homepage", "")
+ app_license = record.get("License", "")
+ try:
+ match = re.match(r"^(.*?)(?:\s*<([^>]+)>)?$", maintainer)
+ if match:
+ maintainer_name = (match.group(1) or "").strip()
+ maintainer_mail = (match.group(2) or "").strip()
+ except Exception as e:
+ self.Logger.exception(f"{e}")
self.ui_ad_maintainer_name_label.set_markup(maintainer_name)
self.ui_ad_maintainer_mail_label.set_markup("{}".format(
@@ -2931,7 +3151,7 @@ def set_app_details_page(self, app, source=1):
self.set_ad_description_text(details["description"])
- if not app_license:
+ if not app_license and not self.localdeb_active:
app_license = self.Package.get_license_from_file(app_name)
self.ui_ad_license_label.set_text("{}".format(app_license if app_license else "-"))
@@ -4898,6 +5118,16 @@ def on_action_process_stderr(self, source, condition):
def on_action_process_exit(self, pid, status):
+ # remove the staged .deb from the finished install
+ staged = self._localdeb_tmp
+ if staged:
+ self._localdeb_tmp = None
+ try:
+ if os.path.isfile(staged):
+ os.remove(staged)
+ except Exception as e:
+ self.Logger.exception(f"on_action_process_exit cleanup: {e}")
+
if not self.error:
if status == 0:
self.ui_queue_flowbox.get_children()[0].get_children()[0].get_children()[0].get_children()[0].get_children()[0].get_children()[3].set_fraction(1)
@@ -4952,7 +5182,7 @@ def on_action_process_exit(self, pid, status):
if len(self.queue) > 0:
self.action_package(self.queue[0]["name"], self.queue[0]["command"], self.queue[0]["desktop_id"],
- self.queue[0]["upgrade"])
+ self.queue[0]["upgrade"], self.queue[0].get("localdeb"))
self.Logger.info("action_package app: {}, command: {}, desktop_id: {}, upgrade: {}".format(
self.queue[0]["name"], self.queue[0]["command"], self.queue[0]["desktop_id"],
self.queue[0]["upgrade"]))
diff --git a/tr.org.pardus.software-deb.desktop b/tr.org.pardus.software-deb.desktop
new file mode 100644
index 0000000..b80aff7
--- /dev/null
+++ b/tr.org.pardus.software-deb.desktop
@@ -0,0 +1,18 @@
+[Desktop Entry]
+Name=Pardus Software Center Package Installer
+Name[pt]=Instalador de Pacotes do Centro de Software Pardus
+Name[tr]=Pardus Yazılım Merkezi Paket Kurucu
+GenericName=Package Installer
+GenericName[pt]=Instalador de Pacotes
+GenericName[tr]=Paket Kurucu
+Comment=Install local .deb packages
+Comment[pt]=Instalar pacotes .deb locais
+Comment[tr]=Yerel .deb paketlerini kurun
+Exec=pardus-software --install-deb %f
+Icon=pardus-software
+Terminal=false
+Type=Application
+Categories=GTK;System;PackageManager;
+StartupNotify=true
+MimeType=application/vnd.debian.binary-package;
+NoDisplay=true