diff --git a/appmenus-scripts/qubes-core-appmenus.py b/appmenus-scripts/qubes-core-appmenus.py index 84d658d..d1caaa1 100644 --- a/appmenus-scripts/qubes-core-appmenus.py +++ b/appmenus-scripts/qubes-core-appmenus.py @@ -28,9 +28,9 @@ import os.path import shutil import dbus -from qubes.qubes import QubesVm,QubesHVm -from qubes.qubes import QubesException,QubesHost,QubesVmLabels -from qubes.qubes import vm_files,system_path,dry_run +from qubes.qubes import QubesVm, QubesHVm +from qubes.qubes import QubesException, QubesHost, QubesVmLabels +from qubes.qubes import vm_files, system_path, dry_run import qubes.imgconverter @@ -40,9 +40,12 @@ vm_files['appmenus_icons_subdir'] = 'apps.icons' vm_files['appmenus_template_templates_subdir'] = 'apps-template.templates' vm_files['appmenus_whitelist'] = 'whitelisted-appmenus.list' -system_path['appmenu_start_hvm_template'] = '/usr/share/qubes-appmenus/qubes-start.desktop' -system_path['appmenu_create_cmd'] = '/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh' -system_path['appmenu_remove_cmd'] = '/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh' +system_path['appmenu_start_hvm_template'] = \ + '/usr/share/qubes-appmenus/qubes-start.desktop' +system_path['appmenu_create_cmd'] = \ + '/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh' +system_path['appmenu_remove_cmd'] = \ + '/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh' def QubesVm_get_attrs_config(self, attrs): @@ -56,12 +59,14 @@ def QubesVm_get_attrs_config(self, attrs): 'os.path.join(self.dir_path, vm_files["appmenus_icons_subdir"])' } return attrs + def QubesTemplateVm_get_attrs_config(self, attrs): attrs['appmenus_templates_dir'] = { 'eval': 'os.path.join(self.dir_path, vm_files["appmenus_templates_subdir"])' } attrs['appmenus_template_icons_dir'] = { 'eval': 'os.path.join(self.dir_path, vm_files["appmenus_template_icons_subdir"])' } return attrs -def QubesVm_appmenus_create(self, verbose=False, source_template = None): + +def QubesVm_appmenus_create(self, verbose=False, source_template=None): if source_template is None: source_template = self.template @@ -84,18 +89,20 @@ def QubesVm_appmenus_create(self, verbose=False, source_template = None): self.name, vmtype, self.label.icon], stdout=msgoutput, stderr=msgoutput) elif self.appmenus_templates_dir is not None: - subprocess.check_call ([system_path["appmenu_create_cmd"], - self.appmenus_templates_dir, self.name, - vmtype, self.label.icon], - stdout=msgoutput, stderr=msgoutput) + subprocess.check_call([system_path["appmenu_create_cmd"], + self.appmenus_templates_dir, self.name, + vmtype, self.label.icon], + stdout=msgoutput, stderr=msgoutput) else: # Only add apps to menu - subprocess.check_call ([system_path["appmenu_create_cmd"], - "none", self.name, vmtype, - self.label.icon], - stdout=msgoutput, stderr=msgoutput) + subprocess.check_call([system_path["appmenu_create_cmd"], + "none", self.name, vmtype, + self.label.icon], + stdout=msgoutput, stderr=msgoutput) except subprocess.CalledProcessError: - print >> sys.stderr, "Ooops, there was a problem creating appmenus for {0} VM!".format (self.name) + print >> sys.stderr, "Ooops, there was a problem creating appmenus " \ + "for {0} VM!".format(self.name) + def QubesVm_appmenus_remove(self): vmtype = None @@ -105,8 +112,9 @@ def QubesVm_appmenus_remove(self): vmtype = 'vm-templates' else: vmtype = 'appvms' - subprocess.check_call ([system_path["appmenu_remove_cmd"], self.name, - vmtype], stderr=open(os.devnull, 'w')) + subprocess.check_call([system_path["appmenu_remove_cmd"], self.name, + vmtype], stderr=open(os.devnull, 'w')) + def QubesVm_appicons_create(self, srcdir=None): if srcdir is None: @@ -136,51 +144,64 @@ def QubesVm_appicons_create(self, srcdir=None): src_icon = os.path.join(srcdir, icon) dst_icon = os.path.join(self.appmenus_icons_dir, icon) if not os.path.exists(dst_icon) or \ - os.path.getmtime(src_icon) > os.path.getmtime(dst_icon): + os.path.getmtime(src_icon) > os.path.getmtime(dst_icon): qubes.imgconverter.tint(src_icon, dst_icon, self.label.color) + def QubesVm_appicons_remove(self): - if not os.path.exists(self.appmenus_icons_dir): return + if not os.path.exists(self.appmenus_icons_dir): + return for icon in os.listdir(self.appmenus_icons_dir): os.unlink(os.path.join(self.appmenus_icons_dir, icon)) + def QubesVm_pre_rename(self, new_name): self.appmenus_remove() + def QubesVm_post_rename(self, old_name): old_dirpath = os.path.join(os.path.dirname(self.dir_path), old_name) if self.appmenus_templates_dir is not None: - self.appmenus_templates_dir = self.appmenus_templates_dir.replace(old_dirpath, self.dir_path) + self.appmenus_templates_dir = self.appmenus_templates_dir.replace( + old_dirpath, self.dir_path) self.appmenus_create() + def QubesVm_create_on_disk(self, verbose, source_template): if isinstance(self, QubesHVm) and source_template is None: if verbose: - print >> sys.stderr, "--> Creating appmenus directory: {0}".format(self.appmenus_templates_dir) - os.mkdir (self.appmenus_templates_dir) - shutil.copy (system_path["appmenu_start_hvm_template"], self.appmenus_templates_dir) + print >> sys.stderr, "--> Creating appmenus directory: {0}".format( + self.appmenus_templates_dir) + os.mkdir(self.appmenus_templates_dir) + shutil.copy(system_path["appmenu_start_hvm_template"], + self.appmenus_templates_dir) source_whitelist_filename = 'vm-' + vm_files["appmenus_whitelist"] if self.is_netvm(): source_whitelist_filename = 'netvm-' + vm_files["appmenus_whitelist"] - if source_template and os.path.exists(os.path.join(source_template.dir_path, source_whitelist_filename)): + if source_template and os.path.exists( + os.path.join(source_template.dir_path, source_whitelist_filename)): if verbose: - print >> sys.stderr, "--> Creating default whitelisted apps list: {0}".\ + print >> sys.stderr, "--> Creating default whitelisted apps list: {0}". \ format(self.dir_path + '/' + vm_files["whitelisted_appmenus"]) - shutil.copy(os.path.join(source_template.dir_path, source_whitelist_filename), - os.path.join(self.dir_path, vm_files["whitelisted_appmenus"])) + shutil.copy( + os.path.join(source_template.dir_path, source_whitelist_filename), + os.path.join(self.dir_path, vm_files["whitelisted_appmenus"])) if source_template and self.updateable: if verbose: - print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\ - format(source_template.appmenus_templates_dir, self.appmenus_templates_dir) + print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \ + format(source_template.appmenus_templates_dir, + self.appmenus_templates_dir) if os.path.isdir(source_template.appmenus_templates_dir): - shutil.copytree (source_template.appmenus_templates_dir, self.appmenus_templates_dir) + shutil.copytree(source_template.appmenus_templates_dir, + self.appmenus_templates_dir) else: os.mkdir(self.appmenus_templates_dir) if os.path.isdir(source_template.appmenus_template_icons_dir): - shutil.copytree (source_template.appmenus_template_icons_dir, self.appmenus_template_icons_dir) + shutil.copytree(source_template.appmenus_template_icons_dir, + self.appmenus_template_icons_dir) else: os.mkdir(self.appmenus_template_icons_dir) @@ -188,23 +209,27 @@ def QubesVm_create_on_disk(self, verbose, source_template): self.appicons_create() self.appmenus_create(verbose=verbose) + def QubesVm_clone_disk_files(self, src_vm, verbose): - if src_vm.updateable and src_vm.appmenus_templates_dir is not None and self.appmenus_templates_dir is not None: + if src_vm.updateable and src_vm.appmenus_templates_dir is not None and \ + self.appmenus_templates_dir is not None: if verbose: - print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\ - format(src_vm.appmenus_templates_dir, self.appmenus_templates_dir) - shutil.copytree (src_vm.appmenus_templates_dir, self.appmenus_templates_dir) + print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \ + format(src_vm.appmenus_templates_dir, + self.appmenus_templates_dir) + shutil.copytree(src_vm.appmenus_templates_dir, + self.appmenus_templates_dir) if src_vm.updateable and src_vm.appmenus_template_icons_dir is not None \ and self.appmenus_template_icons_dir is not None and \ os.path.isdir(src_vm.appmenus_template_icons_dir): if verbose: print >> sys.stderr, "--> Copying the template's appmenus " \ - "template icons dir:\n{0} ==>\n{1}".\ - format(src_vm.appmenus_template_icons_dir, - self.appmenus_template_icons_dir) - shutil.copytree (src_vm.appmenus_template_icons_dir, - self.appmenus_template_icons_dir) + "template icons dir:\n{0} ==>\n{1}". \ + format(src_vm.appmenus_template_icons_dir, + self.appmenus_template_icons_dir) + shutil.copytree(src_vm.appmenus_template_icons_dir, + self.appmenus_template_icons_dir) for whitelist in ( vm_files["appmenus_whitelist"], @@ -212,18 +237,20 @@ def QubesVm_clone_disk_files(self, src_vm, verbose): 'netvm-' + vm_files["appmenus_whitelist"]): if os.path.exists(os.path.join(src_vm.dir_path, whitelist)): if verbose: - print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\ + print >> sys.stderr, "--> Copying whitelisted apps list: {0}". \ format(whitelist) shutil.copy(os.path.join(src_vm.dir_path, whitelist), - os.path.join(self.dir_path, whitelist)) + os.path.join(self.dir_path, whitelist)) # Create appmenus self.appicons_create() self.appmenus_create(verbose=verbose) + def QubesVm_remove_from_disk(self): self.appmenus_remove() + def QubesVm_label_setter(self, _): self.appicons_create() @@ -231,14 +258,18 @@ def QubesVm_label_setter(self, _): # see #751 for details if os.environ.get("DESKTOP_SESSION", "") == "kde-plasma": try: - os.unlink(os.path.expandvars("$HOME/.kde/cache-$HOSTNAME/icon-cache.kcache")) + os.unlink(os.path.expandvars( + "$HOME/.kde/cache-$HOSTNAME/icon-cache.kcache")) except: pass try: - notify_object = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications") + notify_object = dbus.SessionBus().get_object( + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications") notify_object.Notify( "Qubes", 0, self.label.icon, "Qubes", - "You will need to log off and log in again for the VM icons to update in the KDE launcher menu", + "You will need to log off and log in again for the VM icons " + "to update in the KDE launcher menu", [], [], 10000, dbus_interface="org.freedesktop.Notifications") except: @@ -247,6 +278,7 @@ def QubesVm_label_setter(self, _): self.appmenus_remove() self.appmenus_create() + def QubesVm_appmenus_recreate(self): """ Force recreation of all appmenus and icons. For example when VM label @@ -257,6 +289,7 @@ def QubesVm_appmenus_recreate(self): self.appicons_create() self.appmenus_create() + def QubesVm_appmenus_update(self): """ Similar to appmenus_recreate, but do not touch unchanged files @@ -266,6 +299,7 @@ def QubesVm_appmenus_update(self): self.appicons_cleanup() self.appmenus_create() + def QubesVm_set_attr(self, name, newvalue, oldvalue): if name == 'internal': if newvalue and not oldvalue: diff --git a/appmenus-scripts/qubes-receive-appmenus b/appmenus-scripts/qubes-receive-appmenus index 1110c52..71bc327 100755 --- a/appmenus-scripts/qubes-receive-appmenus +++ b/appmenus-scripts/qubes-receive-appmenus @@ -29,15 +29,15 @@ import shutil import pipes from optparse import OptionParser -from qubes.qubes import QubesVmCollection,QubesException,system_path +from qubes.qubes import QubesVmCollection, QubesException, system_path from qubes.qubes import QubesHVm from qubes.qubes import vm_files import qubes.imgconverter # fields required to be present (and verified) in retrieved desktop file -required_fields = [ "Name", "Exec" ] +required_fields = ["Name", "Exec"] -#limits +# limits appmenus_line_size = 1024 appmenus_line_count = 100000 @@ -98,12 +98,14 @@ def sanitise_categories(untrusted_value): return ';'.join(categories) + ';' + def fallback_hvm_appmenulist(): - p = subprocess.Popen(["grep", "-rH", "=", "/usr/share/qubes-appmenus/hvm"], - stdout=subprocess.PIPE) + p = subprocess.Popen(["grep", "-rH", "=", "/usr/share/qubes-appmenus/hvm"], + stdout=subprocess.PIPE) (stdout, stderr) = p.communicate() return stdout.splitlines() + def get_appmenus(vm): global appmenus_line_count global appmenus_line_size @@ -137,12 +139,13 @@ def get_appmenus(vm): row_no = 0 appmenus = {} - line_rx = re.compile(r"([a-zA-Z0-9.()_-]+.desktop):([a-zA-Z0-9-]+(?:\[[a-zA-Z@_]+\])?)=(.*)") + line_rx = re.compile( + r"([a-zA-Z0-9.()_-]+.desktop):([a-zA-Z0-9-]+(?:\[[a-zA-Z@_]+\])?)=(.*)") ignore_rx = re.compile(r".*([a-zA-Z0-9._-]+.desktop):(#.*|\s+)$") for untrusted_line in untrusted_appmenulist: # Ignore blank lines and comments if len(untrusted_line) == 0 or ignore_rx.match(untrusted_line): - continue + continue # use search instead of match to skip file path untrusted_m = line_rx.search(untrusted_line) if untrusted_m: @@ -171,9 +174,11 @@ def get_appmenus(vm): if not appmenus.has_key(filename): appmenus[filename] = {} - appmenus[filename][key]=value + appmenus[filename][key] = value else: - print >>sys.stderr, "Warning: ignoring key %r of %s" % (untrusted_key, filename) + print >> sys.stderr, \ + "Warning: ignoring key %r of %s" % \ + (untrusted_key, filename) # else: ignore this key return appmenus @@ -183,7 +188,9 @@ def create_template(path, values): # check if all required fields are present for key in required_fields: if not values.has_key(key): - print >>sys.stderr, "Warning: not creating/updating '%s' because of missing '%s' key" % (path, key) + print >> sys.stderr, "Warning: not creating/updating '%s' " \ + "because of missing '%s' key" % ( + path, key) return desktop_entry = "" @@ -200,11 +207,11 @@ def create_template(path, values): else: desktop_entry += "Icon=%XDGICON%\n" - for key in ["Name", "GenericName" ]: + for key in ["Name", "GenericName"]: if values.has_key(key): desktop_entry += "{0}=%VMNAME%: {1}\n".format(key, values[key]) - - for key in [ "Comment", "Categories" ]: + + for key in ["Comment", "Categories"]: if values.has_key(key): desktop_entry += "{0}={1}\n".format(key, values[key]) @@ -215,30 +222,37 @@ def create_template(path, values): desktop_file.write(desktop_entry) desktop_file.close() + def main(): env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN") - usage = "usage: %prog [options] \n"\ - "Updates desktop file templates for given StandaloneVM or TemplateVM" + usage = "usage: %prog [options] \n" \ + "Update desktop file templates for given StandaloneVM or TemplateVM" - parser = OptionParser (usage) - parser.add_option ("-v", "--verbose", action="store_true", dest="verbose", default=False) - parser.add_option ("--force-root", action="store_true", dest="force_root", default=False, - help="Force to run, even with root privileges") - parser.add_option ("--force-rpc", action="store_true", dest="force_rpc", default=False, - help="Force to start a new RPC call, even if called from existing one") + parser = OptionParser(usage) + parser.add_option("-v", "--verbose", action="store_true", dest="verbose", + default=False) + parser.add_option("--force-root", action="store_true", dest="force_root", + default=False, + help="Force to run, even with root privileges") + parser.add_option("--force-rpc", action="store_true", dest="force_rpc", + default=False, + help="Force to start a new RPC call, " + "even if called from existing one") - (options, args) = parser.parse_args () - if (len (args) != 1) and env_vmname is None: - parser.error ("You must specify at least the VM name!") + (options, args) = parser.parse_args() + if (len(args) != 1) and env_vmname is None: + parser.error("You must specify at least the VM name!") if env_vmname: - vmname=env_vmname + vmname = env_vmname else: - vmname=args[0] + vmname = args[0] if os.geteuid() == 0: if not options.force_root: - print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems." + print >> sys.stderr, "*** Running this tool as root is strongly " \ + "discouraged, this will lead you into " \ + "permissions problems." print >> sys.stderr, "Retry as unprivileged user." print >> sys.stderr, "... or use --force-root to continue anyway." exit(1) @@ -251,15 +265,19 @@ def main(): vm = qvm_collection.get_vm_by_name(vmname) if vm is None: - print >>sys.stderr, "ERROR: A VM with the name '{0}' does not exist in the system.".format(vmname) + print >> sys.stderr, "ERROR: A VM with the name '{0}' " \ + "does not exist in the system.".format( + vmname) exit(1) if vm.template is not None: - print >>sys.stderr, "ERROR: To sync appmenus for template based VM, do it on template instead" + print >> sys.stderr, "ERROR: To sync appmenus for template based VM, " \ + "do it on template instead" exit(1) if not vm.is_running(): - print >>sys.stderr, "ERROR: Appmenus can be retrieved only from running VM - start it first" + print >> sys.stderr, "ERROR: Appmenus can be retrieved only from " \ + "running VM - start it first" exit(1) new_appmenus = {} @@ -270,7 +288,7 @@ def main(): new_appmenus = get_appmenus(None) if len(new_appmenus) == 0: - print >>sys.stderr, "ERROR: No appmenus received, terminating" + print >> sys.stderr, "ERROR: No appmenus received, terminating" exit(1) if not os.path.exists(vm.appmenus_templates_dir): @@ -281,10 +299,12 @@ def main(): # Create new/update existing templates if options.verbose: - print >> sys.stderr, "--> Got {0} appmenus, storing to disk".format(str(len(new_appmenus))) + print >> sys.stderr, "--> Got {0} appmenus, storing to disk".format( + str(len(new_appmenus))) for appmenu_file in new_appmenus.keys(): if options.verbose: - if os.path.exists(os.path.join(vm.appmenus_templates_dir, appmenu_file)): + if os.path.exists( + os.path.join(vm.appmenus_templates_dir, appmenu_file)): print >> sys.stderr, "---> Updating {0}".format(appmenu_file) else: print >> sys.stderr, "---> Creating {0}".format(appmenu_file) @@ -294,11 +314,14 @@ def main(): # del new_appmenus[appmenu_file]['Icon'] icondest = os.path.join(vm.appmenus_template_icons_dir, - os.path.splitext(appmenu_file)[0] + '.png') + os.path.splitext(appmenu_file)[0] + '.png') try: - icon = qubes.imgconverter.Image.get_xdg_icon_from_vm(vm, - new_appmenus[appmenu_file]['Icon']) + icon = qubes.imgconverter.Image. \ + get_xdg_icon_from_vm(vm, + new_appmenus[ + appmenu_file][ + 'Icon']) if os.path.exists(icondest): old_icon = qubes.imgconverter.Image.load_from_file(icondest) else: @@ -306,15 +329,17 @@ def main(): if old_icon is None or icon != old_icon: icon.save(icondest) except Exception, e: - print >> sys.stderr, '----> Failed to get icon for {0}: {1!s}'.format(appmenu_file, e) + print >> sys.stderr, '----> Failed to get icon for {0}: {1!s}'.\ + format(appmenu_file, e) if os.path.exists(icondest): - print >> sys.stderr, '-----> Found old icon, using it instead' + print >> sys.stderr, '-----> Found old icon, ' \ + 'using it instead' else: del new_appmenus[appmenu_file]['Icon'] create_template(os.path.join(vm.appmenus_templates_dir, appmenu_file), - new_appmenus[appmenu_file]) + new_appmenus[appmenu_file]) # Delete appmenus of removed applications if options.verbose: @@ -330,8 +355,10 @@ def main(): if isinstance(vm, QubesHVm): if not os.path.exists(os.path.join(vm.appmenus_templates_dir, - os.path.basename(system_path['appmenu_start_hvm_template']))): - shutil.copy(system_path['appmenu_start_hvm_template'], vm.appmenus_templates_dir) + os.path.basename(system_path[ + 'appmenu_start_hvm_template']))): + shutil.copy(system_path['appmenu_start_hvm_template'], + vm.appmenus_templates_dir) vm.appmenus_recreate() if hasattr(vm, 'appvms'): @@ -340,10 +367,11 @@ def main(): try: child_vm.appmenus_update() except Exception, e: - print >> sys.stderr, "---> Failed to recreate appmenus for " \ + print >> sys.stderr, "---> Failed to recreate appmenus for " \ "'{0}': {1}".format(child_vm.name, str(e)) if 'KDE_SESSION_UID' in os.environ: subprocess.call(['kbuildsycoca4']) os.unsetenv('SKIP_CACHE_REBUILD') + main()