Эх сурвалжийг харах

Merge tag 'jm_0c62c58d'

Tag for commit 0c62c58d75a38c9b26b4c6bd1a8750fc149307ea

# gpg: Signature made Fri 07 Aug 2015 03:15:48 PM CEST using RSA key ID 5A4C6DAD
# gpg: Good signature from "Jason Mehring (Qubes OS Signing Key) <nrgaway@gmail.com>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: E0E3 2283 FDCA C1A5 1007  8F27 1BB9 B1FB 5A4C 6DAD

* tag 'jm_0c62c58d':
  debian: qubes-desktop-file-install: Add misssing depend to contol
  debian: Reformat depends in control for better readability
  qubes-desktop-file-install: Manages xdg desktop entry files
Marek Marczykowski-Górecki 8 жил өмнө
parent
commit
6da04600c6

+ 3 - 0
Makefile

@@ -108,6 +108,7 @@ install-rh: install-systemd install-systemd-dropins install-sysvinit
 	touch $(DESTDIR)/etc/yum.conf.d/qubes-proxy.conf
 
 	install -D -m 0644 misc/qubes-trigger-sync-appmenus.action $(DESTDIR)/etc/yum/post-actions/qubes-trigger-sync-appmenus.action
+	install -D -m 0644 misc/qubes-trigger-desktop-file-install.action $(DESTDIR)/etc/yum/post-actions/qubes-trigger-desktop-file-install.action
 
 	install -D -m 0644 misc/serial.conf $(DESTDIR)/usr/share/qubes/serial.conf
 	install -D misc/qubes-serial-login $(DESTDIR)/$(SBINDIR)/qubes-serial-login
@@ -166,6 +167,8 @@ install-common:
 	install network/qubes-netwatcher $(DESTDIR)/$(SBINDIR)/
 
 	install -d $(DESTDIR)/usr/bin
+	install -m 0755 misc/qubes-desktop-file-install $(DESTDIR)/usr/bin/qubes-desktop-file-install
+	install -m 0755 misc/qubes-trigger-desktop-file-install $(DESTDIR)$(LIBDIR)/qubes/qubes-trigger-desktop-file-install
 
 	install qubes-rpc/{qvm-open-in-dvm,qvm-open-in-vm,qvm-copy-to-vm,qvm-move-to-vm,qvm-run,qvm-mru-entry} $(DESTDIR)/usr/bin
 	install qubes-rpc/wrap-in-html-if-url.sh $(DESTDIR)$(LIBDIR)/qubes

+ 47 - 2
debian/control

@@ -9,8 +9,53 @@ Vcs-Git: git://git.qubes-os.org/marmarek/core-agent-linux.git
 
 Package: qubes-core-agent
 Architecture: any
-Depends: qubes-utils (>= 3.0.1), libvchan-xen, xenstore-utils, iptables-persistent, xserver-xorg-video-dummy, xen-utils-common, ethtool, python2.7, python-gi, init-system-helpers, xdg-user-dirs, iptables, net-tools, initscripts, imagemagick, fakeroot, systemd, locales, sudo, dmsetup, psmisc, ncurses-term, xserver-xorg-core, x11-xserver-utils, xinit, ${shlibs:Depends}, ${misc:Depends}
-Recommends: tinyproxy, gnome-themes-standard, xsettingsd, gnome-packagekit, chrony, ntpdate, network-manager (>= 0.8.1-1), network-manager-gnome, haveged, libnotify-bin, notify-osd, gnome-terminal, python-nautilus, yum, yum-utils, cups, system-config-printer
+Depends:
+    dmsetup,
+    ethtool,
+    fakeroot,
+    imagemagick,
+    init-system-helpers,
+    initscripts,
+    iptables,
+    iptables-persistent,
+    libvchan-xen,
+    locales,
+    ncurses-term,
+    net-tools,
+    psmisc,
+    python2.7,
+    python-gi,
+    qubes-utils (>= 3.0.1),
+    sudo,
+    systemd,
+    x11-xserver-utils,
+    xdg-user-dirs,
+    xen-utils-common,
+    xenstore-utils,
+    xinit,
+    xserver-xorg-core,
+    xserver-xorg-video-dummy,
+    ${shlibs:Depends},
+    ${misc:Depends}
+Recommends:
+    chrony,
+    cups,
+    gnome-packagekit,
+    gnome-terminal,
+    gnome-themes-standard,
+    haveged,
+    libnotify-bin,
+    network-manager (>= 0.8.1-1),
+    network-manager-gnome,
+    notify-osd,
+    ntpdate,
+    python-nautilus,
+    python-xdg,
+    system-config-printer,
+    tinyproxy,
+    xsettingsd,
+    yum,
+    yum-utils
 Conflicts: qubes-core-agent-linux, firewalld, qubes-core-vm-sysvinit
 Description: Qubes core agent
  This package includes various daemons necessary for qubes domU support,

+ 82 - 149
debian/qubes-core-agent.postinst

@@ -19,41 +19,10 @@ set -e
 # https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html or
 # the debian-policy package
 
-# Directory that modified desktop entry config files are stored in
-XDG_CONFIG_QUBES="/usr/share/qubes/xdg"
 
-remove_ShowIn() {
-    if [ -e "${1}" ]; then
-        sed -i '/^\(Not\|Only\)ShowIn/d' "${1}"
-    fi
-}
-
-showIn() {
-    desktop_entry="${1}"
-    shown_in="${2}"
-    message="${shown_in:-"Shown in All;"}" 
-    desktop_entry_qubes="${XDG_CONFIG_QUBES}/autostart/${desktop_entry##*/}"
-
-    # Make sure Qubes autostart directory exists
-    mkdir -p "${XDG_CONFIG_QUBES}/autostart"
-
-    # Desktop entry exists, so move to Qubes directory and modify it
-    if [ -e "${desktop_entry}" ]; then
-        echo "Desktop Entry Modification - ${message} ${desktop_entry##*/}..."
-        cp -pf "${desktop_entry}" "${desktop_entry_qubes}"
-
-        remove_ShowIn "${desktop_entry_qubes}"
-        sed -i '/^X-GNOME-Autostart-enabled.*[fF0]/d' "${desktop_entry_qubes}"
-
-        # Will only be '' if shown in all
-        if [ ! "${shown_in}x" == "x" ]; then
-            echo "${shown_in}" >> "${desktop_entry_qubes}" || true
-        fi
-
-    # Desktop entry must have been removed, so also remove from Qubes directory
-    else
-        echo "Desktop Entry Modification - Remove: ${desktop_entry##*/}..."
-        rm -f "${desktop_entry_qubes}"
+debug() {
+    if [ "${DEBDEBUG}" == "1" ]; then
+        echo -e ""$@""
     fi
 }
 
@@ -86,19 +55,76 @@ systemdPreload() {
     systemctl daemon-reload
 }
 
-# Manually trigger all triggers to automaticatly configure
-triggerTriggers() {
-    path="$(readlink -m ${0})"
-    triggers="${path/postinst/triggers}"
+installSerialConf() {
+    debug "Installing over-ridden serial.conf init script..."
+    if [ -e /etc/init/serial.conf ]; then
+        cp /usr/share/qubes/serial.conf /etc/init/serial.conf
+    fi
+}
 
-    awk '{sub(/[ \t]*#.*/,"")} NF' ${triggers} | while read line
-    do
-        /bin/bash -c "${0} triggered ${line##* }" || true
-    done
+disableSELinux() {
+    debug "Disabling SELinux..."
+    if [ -e /etc/selinux/config ]; then
+        sed -e s/^SELINUX=.*$/SELINUX=disabled/ </etc/selinux/config >/etc/selinux/config.processed
+        mv /etc/selinux/config.processed /etc/selinux/config
+        setenforce 0 2>/dev/null
+    fi
 }
 
 case "${1}" in
     configure)
+        # Initial installation of package only
+        # ($2 contains version number on update; nothing on initial installation)
+        if [ -z "${2}" ]; then
+
+            debug "FIRST INSTALL..."
+            # Create NetworkManager configuration if we do not have it
+            if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then
+                echo '[main]' > /etc/NetworkManager/NetworkManager.conf
+                echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf
+                echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf
+            fi
+            /usr/lib/qubes/qubes-fix-nm-conf.sh
+
+            # Location of files which contains list of protected files
+            PROTECTED_FILE_LIST='/etc/qubes/protected-files.d'
+
+            # ensure that hostname resolves to 127.0.1.1 resp. ::1 and that /etc/hosts is
+            # in the form expected by qubes-sysinit.sh
+            if ! grep -rq "^/etc/hostname$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then
+                for ip in '127\.0\.1\.1' '::1'; do
+                    if grep -q "^${ip}\(\s\|$\)" /etc/hosts; then
+                        sed -i "/^${ip}\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true
+                        sed -i "s/^${ip}\(\s\|$\).*$/\0 `hostname`/" /etc/hosts || true
+                    else
+                        echo "${ip//\\/} `hostname`" >> /etc/hosts || true
+                    fi
+                done
+            fi
+
+            # remove hostname from 127.0.0.1 line (in debian the hostname is by default
+            # resolved to 127.0.1.1)
+            if ! grep -rq "^/etc/hosts$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then
+                sed -i "/^127\.0\.0\.1\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true
+            fi
+
+            chown user:user /home_volatile/user
+
+            # Set default "runlevel"
+            rm -f /etc/systemd/system/default.target
+            ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
+
+            # Systemd preload-all
+            systemdPreload
+
+            # Maybe install overridden serial.conf init script 
+            installSerialConf
+
+            # Maybe disable SELinux
+            disableSELinux
+        fi
+
+        debug "UPDATE..."
         # disable some Upstart services
         for init in plymouth-shutdown \
                     prefdm \
@@ -109,14 +135,6 @@ case "${1}" in
         done
         dpkg-divert --divert /etc/init/serial.conf.qubes-orig --package qubes-core-agent --rename --add /etc/init/serial.conf
 
-        # Create NetworkManager configuration if we do not have it
-        if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then
-            echo '[main]' > /etc/NetworkManager/NetworkManager.conf
-            echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf
-            echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf
-        fi
-        /usr/lib/qubes/qubes-fix-nm-conf.sh
-
         # make sure locale is really generated
         current_locale=`grep 'LANG\|LC_ALL' /etc/default/locale|head -n 1|cut -f 2 -d =`
         if [ -n "$current_locale" ] && ! locale -a | grep -q "$current_locale"; then
@@ -131,43 +149,16 @@ case "${1}" in
             rm -f /lib/firmware/updates
         fi
 
-        # Location of files which contains list of protected files
-        PROTECTED_FILE_LIST='/etc/qubes/protected-files.d'
-
-        # ensure that hostname resolves to 127.0.1.1 resp. ::1 and that /etc/hosts is
-        # in the form expected by qubes-sysinit.sh
-        if ! grep -rq "^/etc/hostname$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then
-            for ip in '127\.0\.1\.1' '::1'; do
-                if grep -q "^${ip}\(\s\|$\)" /etc/hosts; then
-                    sed -i "/^${ip}\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true
-                    sed -i "s/^${ip}\(\s\|$\).*$/\0 `hostname`/" /etc/hosts || true
-                else
-                    echo "${ip//\\/} `hostname`" >> /etc/hosts || true
-                fi
-            done
-        fi
-
-        # remove hostname from 127.0.0.1 line (in debian the hostname is by default
-        # resolved to 127.0.1.1)
-        if ! grep -rq "^/etc/hosts$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then
-            sed -i "/^127\.0\.0\.1\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true
-        fi
-
-        chown user:user /home_volatile/user
-
         if ! dpkg-statoverride --list /var/lib/qubes/dom0-updates >/dev/null 2>&1; then
             dpkg-statoverride --update --add user user 775 /var/lib/qubes/dom0-updates
         fi
 
-        # Set default "runlevel"
-        rm -f /etc/systemd/system/default.target
-        ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
+        # Update Qubes App Menus"
+        /usr/lib/qubes/qubes-trigger-sync-appmenus.sh || true
 
-        ## Systemd preload-all
-        systemdPreload
+        ## Update all xdg autostart desktop entries
+        /usr/lib/qubes/qubes-trigger-desktop-file-install clean || true
 
-        ## Process all triggers which will set defaults to wanted values
-        triggerTriggers
         ;;
 
     abort-upgrade|abort-remove|abort-deconfigure)
@@ -178,85 +169,27 @@ case "${1}" in
         for trigger in ${2}; do
             case "${trigger}" in
 
-                # Update Qubes App Menus
                 /usr/share/applications)
-                    echo "Updating Qubes App Menus..."
+                    debug "Updating Qubes App Menus..."
                     /usr/lib/qubes/qubes-trigger-sync-appmenus.sh || true
+            
+                    debug "Updating XDG Config..."
+                    /usr/lib/qubes/qubes-trigger-desktop-file-install || true
+                    ;;
 
-                    ## Systemd preload-all
-                    #systemdPreload
+                /etc/xdg)
+                    debug "Updating XDG Config..."
+                    /usr/lib/qubes/qubes-trigger-desktop-file-install || true
                     ;;
 
                 # Install overridden serial.conf init script 
                 /etc/init/serial.conf)
-                    echo "Installing over-ridden serial.conf init script..."
-                    if [ -e /etc/init/serial.conf ]; then
-                        cp /usr/share/qubes/serial.conf /etc/init/serial.conf
-                    fi
+                    installSerialConf
                     ;;
 
                 # Disable SELinux"
                 /etc/selinux/config)
-                    echo "Disabling SELinux..."
-                    if [ -e /etc/selinux/config ]; then
-                        sed -e s/^SELINUX=.*$/SELINUX=disabled/ </etc/selinux/config >/etc/selinux/config.processed
-                        mv /etc/selinux/config.processed /etc/selinux/config
-                        setenforce 0 2>/dev/null
-                    fi
-                    ;;
-
-                # Desktop Entry Modification - Remove existing rules
-                /etc/xdg/autostart/gpk-update-icon.desktop | \
-                /etc/xdg/autostart/nm-applet.desktop | \
-                /etc/xdg/autostart/abrt-applet.desktop | \
-                /etc/xdg/autostart/notify-osd.desktop)
-                    showIn "${trigger}"
-                    ;;
-
-                # Desktop Entry Modification - Not shown in Qubes
-                /etc/xdg/autostart/pulseaudio.desktop | \
-                /etc/xdg/autostart/deja-dup-monitor.desktop | \
-                /etc/xdg/autostart/imsettings-start.desktop | \
-                /etc/xdg/autostart/krb5-auth-dialog.desktop | \
-                /etc/xdg/autostart/pulseaudio.desktop | \
-                /etc/xdg/autostart/restorecond.desktop | \
-                /etc/xdg/autostart/sealertauto.desktop | \
-                /etc/xdg/autostart/gnome-power-manager.desktop | \
-                /etc/xdg/autostart/gnome-sound-applet.desktop | \
-                /etc/xdg/autostart/gnome-screensaver.desktop | \
-                /etc/xdg/autostart/orca-autostart.desktop)
-                    showIn "${trigger}" 'NotShowIn=QUBES;'
-                    ;;
-
-                # Desktop Entry Modification - Not shown in in DisposableVM
-                /etc/xdg/autostart/gcm-apply.desktop)
-                    showIn "${trigger}" 'NotShowIn=DisposableVM;'
-                    ;;
-
-                # Desktop Entry Modification - Only shown in AppVM
-                /etc/xdg/autostart/gnome-keyring-gpg.desktop | \
-                /etc/xdg/autostart/gnome-keyring-pkcs11.desktop | \
-                /etc/xdg/autostart/gnome-keyring-secrets.desktop | \
-                /etc/xdg/autostart/gnome-keyring-ssh.desktop | \
-                /etc/xdg/autostart/gnome-settings-daemon.desktop | \
-                /etc/xdg/autostart/user-dirs-update-gtk.desktop | \
-                /etc/xdg/autostart/gsettings-data-convert.desktop)
-                    showIn "${trigger}" 'OnlyShowIn=GNOME;AppVM;'
-                    ;;
-
-                # Desktop Entry Modification - Only shown in Gnome & UpdateableVM
-                /etc/xdg/autostart/gpk-update-icon.desktop)
-                    showIn "${trigger}" 'OnlyShowIn=GNOME;UpdateableVM;'
-                    ;;
-
-                # Desktop Entry Modification - Only shown in Gnome & Qubes
-                /etc/xdg/autostart/nm-applet.desktop)
-                    showIn "${trigger}" 'OnlyShowIn=GNOME;QUBES;'
-                    ;;
-
-                *)
-                    echo "postinst called with unknown trigger \`${2}'" >&2
-                    exit 1
+                    disableSELinux
                     ;;
             esac
         done

+ 4 - 41
debian/qubes-core-agent.triggers

@@ -1,41 +1,4 @@
-interest-await /usr/share/applications
-interest-await /etc/init/serial.conf
-interest-await /etc/selinux/config
-
-# Desktop Entry Modification - Not shown in Qubes
-interest-await /etc/xdg/autostart/pulseaudio.desktop
-interest-await /etc/xdg/autostart/deja-dup-monitor.desktop
-interest-await /etc/xdg/autostart/imsettings-start.desktop
-interest-await /etc/xdg/autostart/krb5-auth-dialog.desktop
-interest-await /etc/xdg/autostart/restorecond.desktop
-interest-await /etc/xdg/autostart/sealertauto.desktop
-interest-await /etc/xdg/autostart/gnome-power-manager.desktop
-interest-await /etc/xdg/autostart/gnome-sound-applet.desktop
-interest-await /etc/xdg/autostart/gnome-screensaver.desktop
-interest-await /etc/xdg/autostart/orca-autostart.desktop
-
-# Desktop Entry Modification - Not shown in in DisposableVM
-interest-await /etc/xdg/autostart/gcm-apply.desktop
-
-# Desktop Entry Modification - Only shown in AppVM
-interest-await /etc/xdg/autostart/gnome-keyring-gpg.desktop
-interest-await /etc/xdg/autostart/gnome-keyring-pkcs11.desktop
-interest-await /etc/xdg/autostart/gnome-keyring-secrets.desktop
-interest-await /etc/xdg/autostart/gnome-keyring-ssh.desktop
-interest-await /etc/xdg/autostart/gnome-settings-daemon.desktop
-interest-await /etc/xdg/autostart/user-dirs-update-gtk.desktop
-interest-await /etc/xdg/autostart/gsettings-data-convert.desktop
-
-# Desktop Entry Modification - Remove existing rules
-#interest-await /etc/xdg/autostart/gpk-update-icon.desktop
-#interest-await /etc/xdg/autostart/nm-applet.desktop
-interest-await /etc/xdg/autostart/abrt-applet.desktop
-
-# Desktop Entry Modification - Only shown in Gnome & UpdateableVM
-interest-await /etc/xdg/autostart/gpk-update-icon.desktop
-
-# Desktop Entry Modification - Only shown in Gnome & Qubes
-interest-await /etc/xdg/autostart/nm-applet.desktop
-
-# Desktop Entry Modification - Show in all
-interest-await /etc/xdg/autostart/notify-osd.desktop
+interest-noawait /usr/share/applications
+interest-noawait /etc/xdg
+interest-noawait /etc/init/serial.conf
+interest-noawait /etc/selinux/config

+ 367 - 0
misc/qubes-desktop-file-install

@@ -0,0 +1,367 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: set ft=python ts=4 sw=4 sts=4 et :
+
+# Copyright (C) 2015 Jason Mehring <nrgaway@gmail.com>
+# License: GPL-2+
+# Authors: Jason Mehring
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+'''Installation and edition of desktop files.
+
+Description:
+  The desktop-file-install program is a tool to install, and optionally edit,
+  desktop files.  They are mostly useful for developers and packagers.
+
+  Various options are available to edit the desktop files. The edit options can
+  be specified more than once and will be processed in the same order as the
+  options passed to the program.
+
+  The original .desktop files are left untouched and left in place.
+
+  Qubes modifies the XDG_CONFIG_DIRS to first include the `/usr/share/qubes/xdg`
+  directory (XDG_CONFIG_DIRS=/usr/share/qubes/xdg:/etc/xdg).
+
+Usage:
+  qubes-desktop-file-install [--dir DIR] [--force]
+                             [--remove-show-in]
+                             [--remove-key KEY]
+                             [--remove-only-show-in ENVIRONMENT]
+                             [--add-only-show-in ENVIRONMENT]
+                             [--remove-not-show-in ENVIRONMENT]
+                             [--add-not-show-in ENVIRONMENT]
+                             [(--set-key KEY VALUE)]
+                             FILE
+  qubes-desktop-file-install -h | --help
+  qubes-desktop-file-install --version
+
+Examples:
+  qubes-desktop-file-install --dir /usr/share/qubes/xdg/autostart --add-only-show-in X-QUBES /etc/xdg/autostart/pulseaudio.desktop
+
+Arguments:
+  FILE     Path to desktop entry file
+
+Help Options:
+  -h, --help                         show this help message and exit
+
+Installation options for desktop file:
+  --dir DIR                          Install desktop files to the DIR directory (default: <FILE>)
+  --force                            Force overwrite of existing desktop files (default: False)
+
+Edition options for desktop file:
+  --remove-show-in                   Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file (default: False)
+  --remove-key KEY                   Remove the KEY key from the desktop files, if present
+  --set-key (KEY VALUE)              Set the KEY key to VALUE
+  --remove-only-show-in ENVIRONMENT  Remove ENVIRONMENT from the list of desktop environment where the desktop files should be displayed
+  --add-only-show-in ENVIRONMENT     Add ENVIRONMENT to the list of desktop environment where the desktop files should be displayed
+  --remove-not-show-in ENVIRONMENT   Remove ENVIRONMENT from the list of desktop environment where the desktop files should not be displayed
+  --add-not-show-in ENVIRONMENT      Add ENVIRONMENT to the list of desktop environment where the desktop files should not be displayed
+'''
+
+import argparse
+import codecs
+import io
+import locale
+import os
+import sys
+
+try:
+    import ConfigParser as configparser
+except ImportError:
+    import configparser
+
+from collections import OrderedDict
+
+# Third party libs
+import xdg.DesktopEntry
+
+__all__ = []
+__version__ = '1.0.0'
+
+# This is almost always a good thing to do at the beginning of your programs.
+locale.setlocale(locale.LC_ALL, '')
+
+# Default Qubes directory that modified desktop entry config files are stored in
+QUBES_XDG_CONFIG_DIR = '/usr/share/qubes/xdg'
+
+
+class DesktopEntry(xdg.DesktopEntry.DesktopEntry):
+    '''Class to parse and validate Desktop Entries (OVERRIDE).
+
+    xdg.DesktopEntry.DesktopEntry does not maintain order of content
+    '''
+
+    defaultGroup = 'Desktop Entry'
+
+    def __init__(self, filename=None):
+        """Create a new DesktopEntry
+
+        If filename exists, it will be parsed as a desktop entry file. If not,
+        or if filename is None, a blank DesktopEntry is created.
+        """
+        self.content = OrderedDict()
+        if filename and os.path.exists(filename):
+            self.parse(filename)
+        elif filename:
+            self.new(filename)
+
+    def parse(self, filename):
+        '''Parse a desktop entry file.'''
+        headers = [u'Desktop Entry', u'KDE Desktop Entry']
+        cfgparser = configparser.RawConfigParser()
+        cfgparser.optionxform = unicode
+
+        try:
+            cfgparser.readfp(codecs.open(filename, 'r', 'utf8'))
+        except configparser.MissingSectionHeaderError:
+            sys.exit('{0} missing header!'.format(filename, headers[0]))
+
+        self.filename = filename
+        self.tainted = False
+
+        for header in headers:
+            if cfgparser.has_section(header):
+                self.content[header] = OrderedDict(cfgparser.items(header))
+                if not self.defaultGroup:
+                    self.defaultGroup = header
+
+        if not self.defaultGroup:
+            sys.exit('{0} missing header!'.format(filename, headers[0]))
+
+    # Write support broken in Wheezy; override here
+    def write(self, filename=None, trusted=False):
+        if not filename and not self.filename:
+            raise ParsingError("File not found", "")
+
+        if filename:
+            self.filename = filename
+        else:
+            filename = self.filename
+
+        if os.path.dirname(filename) and not os.path.isdir(os.path.dirname(filename)):
+            os.makedirs(os.path.dirname(filename))
+
+        with io.open(filename, 'w', encoding='utf-8') as fp:
+
+            # An executable bit signifies that the desktop file is
+            # trusted, but then the file can be executed. Add hashbang to
+            # make sure that the file is opened by something that
+            # understands desktop files.
+            if trusted:
+                fp.write(u("#!/usr/bin/env xdg-open\n"))
+
+            if self.defaultGroup:
+                fp.write(unicode("[%s]\n") % self.defaultGroup)
+                for (key, value) in self.content[self.defaultGroup].items():
+                    fp.write(unicode("%s=%s\n") % (key, value))
+                fp.write(unicode("\n"))
+            for (name, group) in self.content.items():
+                if name != self.defaultGroup:
+                    fp.write(unicode("[%s]\n") % name)
+                    for (key, value) in group.items():
+                        fp.write(unicode("%s=%s\n") % (key, value))
+                    fp.write(unicode("\n"))
+
+        # Add executable bits to the file to show that it's trusted.
+        if trusted:
+            oldmode = os.stat(filename).st_mode
+            mode = oldmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
+            os.chmod(filename, mode)
+
+        self.tainted = False
+
+
+def delete(path):
+    '''Delete a file.
+    '''
+    if os.path.exists(path):
+        try:
+            os.unlink(os.path.abspath(path))
+        except IOError as error:
+            sys.exit('Unable to delete file: {0}\n{1}'.format(path, error))
+
+
+def set_key(entry, key, value):
+    '''Set a key with value within an desktop-file entry object.
+    '''
+    key = unicode(key)
+    if isinstance(value, list):
+        entry.set(key, u';'.join(value))
+    else:
+        entry.set(key, unicode(value))
+
+
+def remove_key(entry, key):
+    '''Remove a key within an desktop-file entry object.
+    '''
+    entry.removeKey(unicode(key))
+
+
+def add_value(entry, key, value):
+    '''Add a value to a desktop-file entry object.
+    '''
+    values = entry.getList(unicode(value))
+    for value in values:
+        entry_values = entry.get(key, list=True)
+        if value not in entry_values:
+            entry_values.append(value)
+            set_key(entry, key, entry_values)
+
+
+def remove_value(entry, key, value):
+    '''Remove a value to a desktop-file entry object.
+    '''
+    value = unicode(value)
+    entry_values = entry.get(key, list=True)
+    if value in entry_values:
+        entry_values.remove(value)
+        if entry_values:
+            set_key(entry, key, entry_values)
+        else:
+            remove_key(entry, key)
+
+
+def install(**kwargs):
+    '''Install a copy of a desktop-file entry file to a new location and
+    optionally edit it.
+    '''
+    paths = kwargs.get('path', [])
+    for path in paths:
+        if not path:
+            sys.exit('No path selected!')
+
+        filename, extension = os.path.splitext(path)
+        if extension.lower() not in ['.desktop']:
+            sys.exit("Invalid desktop extenstion '{0}'!  Was expecting '.desktop'.".format(extension))
+
+        new_path = os.path.join(kwargs['dir'], os.path.basename(path))
+
+        if os.path.exists(path) and os.path.isfile(path):
+            stat_info = os.stat(path)
+
+            # Don't update if file has previously been updated unless force is True
+            if os.path.exists(new_path) and not kwargs['force']:
+                if os.stat(new_path).st_mtime == stat_info.st_mtime:
+                    continue
+        else:
+            if os.path.exists(new_path) and os.path.isfile(new_path):
+                delete(new_path)
+            continue
+
+        entry = DesktopEntry(path)
+
+        if kwargs['remove_show_in']:
+            kwargs['remove_key'].append(u'OnlyShowIn')
+            kwargs['remove_key'].append(u'NotShowIn')
+
+        if kwargs['remove_key']:
+            for value in kwargs['remove_key']:
+                remove_key(entry, value)
+
+        if kwargs['remove_only_show_in']:
+            for value in kwargs['remove_only_show_in']:
+                remove_value(entry, u'OnlyShowIn', value)
+
+        if kwargs['add_only_show_in']:
+            for value in kwargs['add_only_show_in']:
+                add_value(entry, u'OnlyShowIn', value)
+
+        if kwargs['remove_not_show_in']:
+            for value in kwargs['remove_not_show_in']:
+                remove_value(entry, u'NotShowIn', value)
+
+        if kwargs['add_not_show_in']:
+            for value in kwargs['add_not_show_in']:
+                add_value(entry, u'NotShowIn', value)
+
+        if kwargs['set_key']:
+            for key, value in kwargs['set_key']:
+                set_key(entry, key, value)
+
+        entry.write(new_path)
+        if stat_info:
+            os.utime(new_path, (stat_info.st_atime, stat_info.st_mtime))
+
+
+
+def parse(args):
+    '''Argparse configuration.
+    '''
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('--version', action='version', version='%(prog)s (version {0})'.format(__version__))
+
+    parser.add_argument('--force', action='store_true', default=False, help='\
+        Force overwrite of existing desktop files.')
+
+    parser.add_argument('--dir', default=QUBES_XDG_CONFIG_DIR, help='\
+        Install desktop files to the DIR directory.')
+
+    parser.add_argument('--remove-show-in', action='store_true', default=False, help='\
+        Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file')
+
+    parser.add_argument('--remove-key', action='append', metavar='KEY', default=[], help='\
+        Remove the KEY key from the desktop file')
+
+    parser.add_argument('--remove-only-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\
+        Remove ENVIRONMENT from the list of desktop environments where the\
+        desktop files should be displayed (key OnlyShowIn). If ENVIRONMENT was\
+        not present in the list, this operation is a no-op.')
+
+    parser.add_argument('--add-only-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\
+        Add ENVIRONMENT to the list of desktop environments where the desktop\
+        files should be displayed (key OnlyShowIn). If ENVIRONMENT was already\
+        present in the list, this operation is a no-op. A non-registered desktop\
+        environment should be prefixed with X-. Note that an empty OnlyShowIn\
+        key in a desktop file means that the desktop file will be displayed in\
+        all environments.')
+
+    parser.add_argument('--remove-not-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\
+        Remove ENVIRONMENT from the list of desktop environments where the\
+        desktop files should not  be  displayed  (key  NotShowIn).  If\
+        ENVIRONMENT was not present in the list, this operation is a no-op.')
+
+    parser.add_argument('--add-not-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\
+        Add ENVIRONMENT to the list of desktop environments where the desktop\
+        files should not be displayed (key NotShowIn). If ENVIRONMENT was\
+        already present in the list, this operation is a no-op. A non-registered\
+        desktop environment should be prefixed with X-.  Note that an empty\
+        NotShowIn key in a desktop file means that the desktop file will be\
+        displayed in all environments.')
+
+    parser.add_argument('--set-key', action='append', nargs=2, metavar=('KEY', 'VALUE'), default=[], help='\
+        Set the KEY key to the VALUE passed.')
+
+    parser.add_argument('path', action='store', nargs='+', metavar='FILE', default=None,
+        help='Path to desktop entry file')
+
+    args = parser.parse_args(args)
+
+    if not os.path.isabs(args.dir):
+        args.dir = os.path.join(QUBES_XDG_CONFIG_DIR, args.dir)
+
+    return args
+
+
+def main(argv):
+    '''Main function.
+    '''
+    args = parse(argv[1:])
+    install(**vars(args))
+
+
+if __name__ == '__main__':
+    main(sys.argv)
+    sys.exit(0)

+ 97 - 0
misc/qubes-trigger-desktop-file-install

@@ -0,0 +1,97 @@
+#!/bin/bash -e
+# vim: set ts=4 sw=4 sts=4 et :
+
+#
+# qubes-trigger-desktop-file-install
+#
+# This trigger script calls qubes-desktop-file-install to installation and edit
+# desktop file overrides, leaving the original desktop file in-place and
+# untouched.
+#
+# 'qubes-desktop-file-install' options:
+#   --dir DIR                          Install desktop files to the DIR directory (default: <FILE>)
+#   --force                            Force overwrite of existing desktop files (default: False)
+#   --remove-show-in                   Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file (default: False)
+#   --remove-key KEY                   Remove the KEY key from the desktop files, if present
+#   --set-key (KEY VALUE)              Set the KEY key to VALUE
+#   --remove-only-show-in ENVIRONMENT  Remove ENVIRONMENT from the list of desktop environment where the desktop files should be displayed
+#   --add-only-show-in ENVIRONMENT     Add ENVIRONMENT to the list of desktop environment where the desktop files should be displayed
+#   --remove-not-show-in ENVIRONMENT   Remove ENVIRONMENT from the list of desktop environment where the desktop files should not be displayed
+#   --add-not-show-in ENVIRONMENT      Add ENVIRONMENT to the list of desktop environment where the desktop files should not be displayed
+
+QUBES_DESKTOP_FILE_INSTALL='/usr/bin/qubes-desktop-file-install'
+QUBES_XDG_CONFIG_DIR=/usr/share/qubes/xdg/autostart
+XDG_CONFIG_DIR=/etc/xdg/autostart
+
+INSTALL_CMD=""${QUBES_DESKTOP_FILE_INSTALL}" --force --dir "${QUBES_XDG_CONFIG_DIR}""
+
+# Remove all current Qubes desktop entry files
+if [ "${1}" == "clean" ]; then
+    rm -f "${QUBES_XDG_CONFIG_DIR}"/*
+fi
+
+generatePath () {
+    echo "${XDG_CONFIG_DIR}/${1}.desktop"
+
+}
+
+generateFileList () {
+    for key in "${!FILES[@]}"; do
+        FILES[${key}]="$(generatePath ${FILES[key]})"
+    done
+}
+
+install () {
+    local options="${@}"
+
+    # Install an edited version of desktop file in $QUBES_XDG_CONFIG_DIR
+    generateFileList
+    $INSTALL_CMD "${@}" "${FILES[@]}" || true
+
+}
+
+# Desktop Entry Modification - NotShowIn=QUBES
+FILES=(
+    'pulseaudio'
+    'deja-dup-monitor'
+    'imsettings-start'
+    'krb5-auth-dialog'
+    'restorecond'
+    'sealertauto'
+    'gnome-power-manager'
+    'gnome-sound-applet'
+    'gnome-screensaver'
+    'orca-autostart'
+); install --remove-show-in --add-not-show-in X-QUBES
+
+# Desktop Entry Modification - NotShowIn=DisposableVM
+FILES=('gcm-apply') 
+install  --remove-show-in --add-not-show-in X-DisposableVM 
+
+# Desktop Entry Modification - OnlyShowIn=GNOME;AppVM;
+FILES=(
+    'gnome-keyring-gpg'
+    'gnome-keyring-pkcs11'
+    'gnome-keyring-secrets'
+    'gnome-keyring-ssh'
+    'gnome-settings-daemon'
+    'user-dirs-update-gtk'
+    'gsettings-data-convert'
+); install --remove-show-in --add-only-show-in 'GNOME;X-AppVM'
+
+# Desktop Entry Modification - OnlyShowIn=GNOME;UpdateableVM
+FILES=('gpk-update-icon')
+install --remove-show-in --add-only-show-in 'GNOME;X-UpdateableVM'
+
+# Desktop Entry Modification - OnlyShowIn=GNOME;QUBES
+FILES=('nm-applet')
+    install --remove-show-in --add-only-show-in 'GNOME;X-QUBES'
+
+# Desktop Entry Modification - Remove existing rules
+FILES=(
+    'abrt-applet'
+); install --remove-show-in
+
+# Desktop Entry Modification - Misc
+# notify-osd
+$INSTALL_CMD --remove-show-in --remove-key X-GNOME-Autostart-enabled "$(generatePath notify-osd)"

+ 1 - 0
misc/qubes-trigger-desktop-file-install.action

@@ -0,0 +1 @@
+*:any:/usr/lib/qubes/qubes-trigger-desktop-file-install

+ 11 - 46
rpm_spec/core-vm.spec

@@ -51,6 +51,8 @@ Requires:   qubes-utils
 Requires:   initscripts
 # for qubes-desktop-run
 Requires:   pygobject3-base
+# for qubes-desktop-file-install
+Requires:   pyxdg
 %if %{fedora} >= 20
 # gpk-update-viewer required by qubes-manager
 Requires:   gnome-packagekit-updater
@@ -127,8 +129,7 @@ if [ -e /etc/init/serial.conf ]; then
 fi
 
 %triggerin -- pulseaudio-module-x11
-sed -i '/^\(Not\|Only\)ShowIn/d' /etc/xdg/autostart/pulseaudio.desktop
-echo 'NotShowIn=QUBES;' >> /etc/xdg/autostart/pulseaudio.desktop
+/usr/bin/qubes-desktop-file-install --force --dir /usr/share/qubes/xdg/autostart --remove-show-in --add-not-show-in X-QUBES /etc/xdg/autostart/pulseaudio.desktop
 
 %triggerin -- iptables
 if ! grep -q IPTABLES_DATA /etc/sysconfig/iptables-config; then
@@ -158,50 +159,9 @@ for F in plymouth-shutdown prefdm splash-manager start-ttys tty ; do
 	fi
 done
 
-remove_ShowIn () {
-	if [ -e /etc/xdg/autostart/$1.desktop ]; then
-		sed -i '/^\(Not\|Only\)ShowIn/d' /etc/xdg/autostart/$1.desktop
-	fi
-}
-
-# reenable if disabled by some earlier version of package
-remove_ShowIn abrt-applet.desktop imsettings-start.desktop
-
-# don't want it at all
-for F in deja-dup-monitor krb5-auth-dialog pulseaudio restorecond sealertauto gnome-power-manager gnome-sound-applet gnome-screensaver orca-autostart; do
-	if [ -e /etc/xdg/autostart/$F.desktop ]; then
-		remove_ShowIn $F
-		echo 'NotShowIn=QUBES;' >> /etc/xdg/autostart/$F.desktop
-	fi
-done
-
-# don't want it in DisposableVM
-for F in gcm-apply ; do
-	if [ -e /etc/xdg/autostart/$F.desktop ]; then
-		remove_ShowIn $F
-		echo 'NotShowIn=DisposableVM;' >> /etc/xdg/autostart/$F.desktop
-	fi
-done
-
-# want it in AppVM only
-for F in gnome-keyring-gpg gnome-keyring-pkcs11 gnome-keyring-secrets gnome-keyring-ssh gnome-settings-daemon user-dirs-update-gtk gsettings-data-convert ; do
-	if [ -e /etc/xdg/autostart/$F.desktop ]; then
-		remove_ShowIn $F
-		echo 'OnlyShowIn=GNOME;AppVM;' >> /etc/xdg/autostart/$F.desktop
-	fi
-done
-
-# remove existing rule to add own later
-for F in gpk-update-icon nm-applet ; do
-	remove_ShowIn $F
-done
-
-echo 'OnlyShowIn=GNOME;UpdateableVM;' >> /etc/xdg/autostart/gpk-update-icon.desktop || :
-%if %{fedora} >= 20
-echo 'OnlyShowIn=GNOME;QUBES;' >> /etc/xdg/autostart/nm-applet.desktop || :
-%else
-echo 'OnlyShowIn=GNOME;NetVM;' >> /etc/xdg/autostart/nm-applet.desktop || :
-%endif
+# Update all autostart xdg desktop configuration files (modified copies are
+# placed in /usr/share/qubes/xdg/autostart)
+/usr/lib/qubes/qubes-trigger-desktop-file-install clean
 
 # Create NetworkManager configuration if we do not have it
 if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then
@@ -352,6 +312,8 @@ if [ $1 -eq 0 ] ; then
     if [ -L /lib/firmware/updates ]; then
       rm /lib/firmware/updates
     fi
+
+    rm -rf /usr/share/qubes/xdg
 fi
 
 %posttrans
@@ -409,6 +371,7 @@ rm -f %{name}-%{version}
 %config(noreplace) /etc/yum.repos.d/qubes-r3.repo
 /etc/yum/pluginconf.d/yum-qubes-hooks.conf
 /etc/yum/post-actions/qubes-trigger-sync-appmenus.action
+/etc/yum/post-actions/qubes-trigger-desktop-file-install.action
 /usr/lib/systemd/system/user@.service.d/90-session-stop-timeout.conf
 /usr/sbin/qubes-serial-login
 /usr/bin/qvm-copy-to-vm
@@ -421,6 +384,7 @@ rm -f %{name}-%{version}
 /usr/bin/qubes-desktop-run
 /usr/bin/qrexec-fork-server
 /usr/bin/qrexec-client-vm
+/usr/bin/qubes-desktop-file-install
 %dir /usr/lib/qubes
 /usr/lib/qubes/vusb-ctl.py*
 /usr/lib/qubes/dispvm-prerun.sh
@@ -450,6 +414,7 @@ rm -f %{name}-%{version}
 /usr/lib/qubes/wrap-in-html-if-url.sh
 /usr/lib/qubes/iptables-updates-proxy
 /usr/lib/qubes/close-window
+/usr/lib/qubes/qubes-trigger-desktop-file-install
 /usr/lib/yum-plugins/yum-qubes-hooks.py*
 /usr/sbin/qubes-firewall
 /usr/sbin/qubes-netwatcher

+ 6 - 6
vm-systemd/misc-post.sh

@@ -27,24 +27,24 @@ INTERFACE=eth0 /usr/lib/qubes/setup-ip
 # Start services which haven't own proper systemd unit:
 
 # Start AppVM specific services
+INSTALL_CMD='/usr/bin/qubes-desktop-file-install --force --dir /usr/share/qubes/xdg/autostart'
+
 if [ ! -f /etc/systemd/system/cups.service ]; then
     if [ -f /var/run/qubes-service/cups ]; then
         /usr/sbin/service cups start
         # Allow also notification icon
-        sed -i -e '/^NotShowIn=.*QUBES/s/;QUBES//' /etc/xdg/autostart/print-applet.desktop
+        $INSTALL_CMD --remove-not-show-in X-QUBES /etc/xdg/autostart/print-applet.desktop
     else
         # Disable notification icon
-        sed -i -e '/QUBES/!s/^NotShowIn=\(.*\)/NotShowIn=QUBES;\1/' /etc/xdg/autostart/print-applet.desktop
+        $INSTALL_CMD --add-not-show-in X-QUBES /etc/xdg/autostart/print-applet.desktop
     fi
 fi
 if [ -f /var/run/qubes-service/network-manager ]; then
     # Allow also notification icon
-    sed -i -e '/QUBES/!s/^OnlyShowIn=.*/\0QUBES;/' /etc/xdg/autostart/nm-applet.desktop
-    sed -i -e '/^NotShowIn=.*/s/QUBES;//' /etc/xdg/autostart/nm-applet.desktop
+    $INSTALL_CMD --remove-not-show-in X-QUBES --add-only-show-in X-QUBES /etc/xdg/autostart/nm-applet.desktop
 else
     # Disable notification icon
-    sed -i -e '/^OnlyShowIn=.*/s/QUBES;//' /etc/xdg/autostart/nm-applet.desktop
-    sed -i -e '/QUBES/!s/^NotShowIn=.*/\0QUBES;/' /etc/xdg/autostart/nm-applet.desktop
+    $INSTALL_CMD --remove-only-show-in X-QUBES --add-not-show-in X-QUBES /etc/xdg/autostart/nm-applet.desktop
 fi
 
 exit 0