@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Terminal=false
|
|
||||||
Name=Command Prompt
|
|
||||||
Comment=Use the command line
|
|
||||||
Categories=GNOME;GTK;Utility;TerminalEmulator;System;
|
|
||||||
Exec=cmd /c start cmd
|
|
@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Terminal=false
|
|
||||||
Name=Explorer
|
|
||||||
Comment=Browse files
|
|
||||||
Categories=Utility;Core;
|
|
||||||
Exec=explorer
|
|
@ -1,8 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Terminal=false
|
|
||||||
Name=Internet Explorer
|
|
||||||
Comment=Browse the Web
|
|
||||||
Categories=Network;WebBrowser;
|
|
||||||
Exec=C:\\Program Files\\Internet Explorer\\iexplore.exe
|
|
@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Exec=qubes-vm-settings %VMNAME% applications
|
|
||||||
Icon=qubes-appmenu-select
|
|
||||||
Terminal=false
|
|
||||||
Name=%VMNAME%: Add more shortcuts...
|
|
||||||
GenericName=%VMNAME%: Add more shortcuts...
|
|
||||||
StartupNotify=false
|
|
||||||
Categories=System;X-Qubes-VM;
|
|
@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Exec=sh -c 'echo firefox | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
|
|
||||||
Icon=dispvm-red
|
|
||||||
Terminal=false
|
|
||||||
Name=DispVM: Firefox web browser
|
|
||||||
GenericName=DispVM: Web browser
|
|
||||||
StartupNotify=false
|
|
||||||
Categories=Network;X-Qubes-VM;
|
|
@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Exec=sh -c 'echo xterm | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
|
|
||||||
Icon=dispvm-red
|
|
||||||
Terminal=false
|
|
||||||
Name=DispVM: xterm
|
|
||||||
GenericName=DispVM: Terminal
|
|
||||||
StartupNotify=false
|
|
||||||
Categories=Network;X-Qubes-VM;
|
|
@ -1,5 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Encoding=UTF-8
|
|
||||||
Type=Directory
|
|
||||||
Name=DisposableVM
|
|
||||||
Icon=dispvm-red
|
|
@ -1,5 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Encoding=UTF-8
|
|
||||||
Type=Directory
|
|
||||||
Name=ServiceVM: %VMNAME%
|
|
||||||
Icon=%XDGICON%
|
|
@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Exec=qvm-start --quiet --tray %VMNAME%
|
|
||||||
Icon=%XDGICON%
|
|
||||||
Terminal=false
|
|
||||||
Name=%VMNAME%: Start
|
|
||||||
GenericName=%VMNAME%: Start
|
|
||||||
StartupNotify=false
|
|
||||||
Categories=System;X-Qubes-VM;
|
|
@ -1,5 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Encoding=UTF-8
|
|
||||||
Type=Directory
|
|
||||||
Name=Template: %VMNAME%
|
|
||||||
Icon=%XDGICON%
|
|
@ -1,5 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Encoding=UTF-8
|
|
||||||
Type=Directory
|
|
||||||
Name=Domain: %VMNAME%
|
|
||||||
Icon=%XDGICON%
|
|
@ -1,20 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
SRC=$1
|
|
||||||
DSTDIR=$2
|
|
||||||
VMNAME=$3
|
|
||||||
VMDIR=$4
|
|
||||||
XDGICON=$5
|
|
||||||
|
|
||||||
DST=$DSTDIR/$VMNAME-$(basename $SRC)
|
|
||||||
|
|
||||||
if ! [ -r "$SRC" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
sed \
|
|
||||||
-e "s/%VMNAME%/$VMNAME/" \
|
|
||||||
-e "s %VMDIR% $VMDIR " \
|
|
||||||
-e "s/%XDGICON%/$XDGICON/" \
|
|
||||||
<$SRC >$DST
|
|
||||||
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
SRC=$1
|
|
||||||
DST=$2
|
|
||||||
VMNAME=$3
|
|
||||||
VMDIR=$4
|
|
||||||
XDGICON=$5
|
|
||||||
|
|
||||||
sed \
|
|
||||||
-e "s/%VMNAME%/$VMNAME/" \
|
|
||||||
-e "s %VMDIR% $VMDIR " \
|
|
||||||
-e "s/%XDGICON%/$XDGICON/" \
|
|
||||||
<$SRC >$DST
|
|
||||||
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# The Qubes OS Project, http://www.qubes-os.org
|
|
||||||
#
|
|
||||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
|
||||||
#
|
|
||||||
# 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, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
SRCDIR=$1
|
|
||||||
VMNAME=$2
|
|
||||||
VMTYPE=$3
|
|
||||||
if [ -z "$VMTYPE" ]; then
|
|
||||||
VMTYPE=appvms
|
|
||||||
fi
|
|
||||||
XDGICON=$4
|
|
||||||
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
|
|
||||||
APPSDIR=$VMDIR/apps
|
|
||||||
|
|
||||||
if [ $# -lt 2 ]; then
|
|
||||||
echo "usage: $0 <apps_templates_dir> <vmname> [appvms|vm-templates|servicevms]"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
mkdir -p $APPSDIR
|
|
||||||
|
|
||||||
if [ "$SRCDIR" != "none" ]; then
|
|
||||||
echo "--> Converting Appmenu Templates..."
|
|
||||||
if [ -r "$VMDIR/whitelisted-appmenus.list" ]; then
|
|
||||||
cat $VMDIR/whitelisted-appmenus.list | xargs -I{} /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh $SRCDIR/{} $APPSDIR $VMNAME $VMDIR $XDGICON
|
|
||||||
else
|
|
||||||
find $SRCDIR -name "*.desktop" $CHECK_WHITELISTED -exec /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh {} $APPSDIR $VMNAME $VMDIR $XDGICON \;
|
|
||||||
fi
|
|
||||||
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh /usr/share/qubes-appmenus/qubes-appmenu-select.desktop $APPSDIR $VMNAME $VMDIR $XDGICON
|
|
||||||
|
|
||||||
if [ "$VMTYPE" = "vm-templates" ]; then
|
|
||||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-templatevm.directory.template
|
|
||||||
elif [ "$VMTYPE" = "servicevms" ]; then
|
|
||||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-servicevm.directory.template
|
|
||||||
else
|
|
||||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-vm.directory.template
|
|
||||||
fi
|
|
||||||
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh $DIR_TEMPLATE $APPSDIR/$VMNAME-vm.directory $VMNAME $VMDIR $XDGICON
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "--> Adding Apps to the Menu..."
|
|
||||||
LC_COLLATE=C xdg-desktop-menu install --noupdate $APPSDIR/*.directory $APPSDIR/*.desktop
|
|
||||||
|
|
||||||
if [ -n "$KDE_SESSION_UID" -a -z "$SKIP_CACHE_REBUILD" ]; then
|
|
||||||
xdg-desktop-menu forceupdate
|
|
||||||
kbuildsycoca$KDE_SESSION_VERSION
|
|
||||||
fi
|
|
@ -1,394 +0,0 @@
|
|||||||
#!/usr/bin/python2
|
|
||||||
#
|
|
||||||
# The Qubes OS Project, http://www.qubes-os.org
|
|
||||||
#
|
|
||||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
|
||||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
|
||||||
#
|
|
||||||
# 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, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
||||||
# USA.
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import shutil
|
|
||||||
import dbus
|
|
||||||
|
|
||||||
from qubes.qubes import QubesVm, QubesHVm
|
|
||||||
from qubes.qubes import vm_files, system_path
|
|
||||||
|
|
||||||
import qubes.imgconverter
|
|
||||||
|
|
||||||
vm_files['appmenus_templates_subdir'] = 'apps.templates'
|
|
||||||
vm_files['appmenus_template_icons_subdir'] = 'apps.tempicons'
|
|
||||||
vm_files['appmenus_subdir'] = 'apps'
|
|
||||||
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'
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_get_appmenus_templates_dir(self):
|
|
||||||
if self.updateable:
|
|
||||||
return self.absolute_path(vm_files["appmenus_templates_subdir"], None)
|
|
||||||
elif self.template is not None:
|
|
||||||
return self.template.appmenus_templates_dir
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
QubesVm.appmenus_templates_dir = property(QubesVm_get_appmenus_templates_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_get_appmenus_template_icons_dir(self):
|
|
||||||
if self.updateable:
|
|
||||||
return self.absolute_path(vm_files["appmenus_template_icons_subdir"],
|
|
||||||
None)
|
|
||||||
elif self.template:
|
|
||||||
return self.template.appmenus_template_icons_dir
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
QubesVm.appmenus_template_icons_dir = \
|
|
||||||
property(QubesVm_get_appmenus_template_icons_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_get_appmenus_dir(self):
|
|
||||||
return self.absolute_path(vm_files["appmenus_subdir"], None)
|
|
||||||
|
|
||||||
QubesVm.appmenus_dir = property(QubesVm_get_appmenus_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_get_appmenus_icons_dir(self):
|
|
||||||
return self.absolute_path(vm_files["appmenus_icons_subdir"], None)
|
|
||||||
|
|
||||||
|
|
||||||
QubesVm.appmenus_icons_dir = property(QubesVm_get_appmenus_icons_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def get_whitelist_names(vm):
|
|
||||||
return (whitelist for whitelist in (
|
|
||||||
vm_files["appmenus_whitelist"],
|
|
||||||
'vm-' + vm_files["appmenus_whitelist"],
|
|
||||||
'netvm-' + vm_files["appmenus_whitelist"])
|
|
||||||
if os.path.exists(os.path.join(vm.dir_path, whitelist)))
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appmenus_create(self, verbose=False, source_template=None):
|
|
||||||
if source_template is None:
|
|
||||||
source_template = self.template
|
|
||||||
|
|
||||||
if self.internal:
|
|
||||||
return
|
|
||||||
if self.is_disposablevm():
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.is_netvm():
|
|
||||||
vmtype = 'servicevms'
|
|
||||||
elif self.is_template():
|
|
||||||
vmtype = 'vm-templates'
|
|
||||||
else:
|
|
||||||
vmtype = 'appvms'
|
|
||||||
|
|
||||||
try:
|
|
||||||
msgoutput = None if verbose else open(os.devnull, 'w')
|
|
||||||
if source_template is not None:
|
|
||||||
subprocess.check_call([system_path["appmenu_create_cmd"],
|
|
||||||
source_template.appmenus_templates_dir,
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
print >> sys.stderr, "Ooops, there was a problem creating appmenus " \
|
|
||||||
"for {0} VM!".format(self.name)
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appmenus_remove(self):
|
|
||||||
if self.is_netvm():
|
|
||||||
vmtype = 'servicevms'
|
|
||||||
elif self.is_template():
|
|
||||||
vmtype = 'vm-templates'
|
|
||||||
else:
|
|
||||||
vmtype = 'appvms'
|
|
||||||
subprocess.check_call([system_path["appmenu_remove_cmd"], self.name,
|
|
||||||
vmtype], stderr=open(os.devnull, 'w'))
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appmenus_cleanup(self):
|
|
||||||
srcdir = self.appmenus_templates_dir
|
|
||||||
if srcdir is None:
|
|
||||||
return
|
|
||||||
if not os.path.exists(srcdir):
|
|
||||||
return
|
|
||||||
if not os.path.exists(self.appmenus_dir):
|
|
||||||
return
|
|
||||||
|
|
||||||
for appmenu in os.listdir(self.appmenus_dir):
|
|
||||||
if not os.path.exists(os.path.join(srcdir, appmenu)):
|
|
||||||
os.unlink(os.path.join(self.appmenus_dir, appmenu))
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appmenus_replace_entry(self, old_name, new_name):
|
|
||||||
for whitelist in get_whitelist_names(self):
|
|
||||||
whitelist_path = os.path.join(self.dir_path, whitelist)
|
|
||||||
with open(whitelist_path) as f:
|
|
||||||
old_lines = f.readlines()
|
|
||||||
new_lines = [
|
|
||||||
(new_name + '\n' if l == old_name + '\n' else l)
|
|
||||||
for l in old_lines]
|
|
||||||
if new_lines != old_lines:
|
|
||||||
with open(whitelist_path, 'w') as f:
|
|
||||||
f.write(''.join(new_lines))
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appicons_create(self, srcdir=None, force=False):
|
|
||||||
if srcdir is None:
|
|
||||||
srcdir = self.appmenus_template_icons_dir
|
|
||||||
if srcdir is None:
|
|
||||||
return
|
|
||||||
if not os.path.exists(srcdir):
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.internal:
|
|
||||||
return
|
|
||||||
if self.is_disposablevm():
|
|
||||||
return
|
|
||||||
|
|
||||||
whitelist = os.path.join(self.dir_path, vm_files['appmenus_whitelist'])
|
|
||||||
if os.path.exists(whitelist):
|
|
||||||
whitelist = [line.strip() for line in open(whitelist)]
|
|
||||||
else:
|
|
||||||
whitelist = None
|
|
||||||
|
|
||||||
if not os.path.exists(self.appmenus_icons_dir):
|
|
||||||
os.mkdir(self.appmenus_icons_dir)
|
|
||||||
elif not os.path.isdir(self.appmenus_icons_dir):
|
|
||||||
os.unlink(self.appmenus_icons_dir)
|
|
||||||
os.mkdir(self.appmenus_icons_dir)
|
|
||||||
|
|
||||||
for icon in os.listdir(srcdir):
|
|
||||||
desktop = os.path.splitext(icon)[0] + '.desktop'
|
|
||||||
if whitelist and desktop not in whitelist:
|
|
||||||
continue
|
|
||||||
|
|
||||||
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 force or \
|
|
||||||
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
|
|
||||||
for icon in os.listdir(self.appmenus_icons_dir):
|
|
||||||
os.unlink(os.path.join(self.appmenus_icons_dir, icon))
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appicons_cleanup(self):
|
|
||||||
srcdir = self.appmenus_template_icons_dir
|
|
||||||
if srcdir is None:
|
|
||||||
return
|
|
||||||
if not os.path.exists(srcdir):
|
|
||||||
return
|
|
||||||
if not os.path.exists(self.appmenus_icons_dir):
|
|
||||||
return
|
|
||||||
|
|
||||||
for icon in os.listdir(self.appmenus_icons_dir):
|
|
||||||
if not os.path.exists(os.path.join(srcdir, icon)):
|
|
||||||
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):
|
|
||||||
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)
|
|
||||||
|
|
||||||
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 verbose:
|
|
||||||
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"]))
|
|
||||||
|
|
||||||
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)
|
|
||||||
if os.path.isdir(source_template.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)
|
|
||||||
else:
|
|
||||||
os.mkdir(self.appmenus_template_icons_dir)
|
|
||||||
|
|
||||||
# Create appmenus
|
|
||||||
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 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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
for whitelist in get_whitelist_names(src_vm):
|
|
||||||
if verbose:
|
|
||||||
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))
|
|
||||||
|
|
||||||
# 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(force=True)
|
|
||||||
|
|
||||||
# Apparently desktop environments heavily caches the icons,
|
|
||||||
# see #751 for details
|
|
||||||
if "plasma" in os.environ.get("DESKTOP_SESSION", ""):
|
|
||||||
try:
|
|
||||||
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.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",
|
|
||||||
[], [], 10000,
|
|
||||||
dbus_interface="org.freedesktop.Notifications")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif "xfce" in os.environ.get("DESKTOP_SESSION", ""):
|
|
||||||
self.appmenus_remove()
|
|
||||||
self.appmenus_create()
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appmenus_recreate(self):
|
|
||||||
"""
|
|
||||||
Force recreation of all appmenus and icons. For example when VM label
|
|
||||||
color was changed
|
|
||||||
"""
|
|
||||||
self.appmenus_remove()
|
|
||||||
self.appmenus_cleanup()
|
|
||||||
self.appicons_remove()
|
|
||||||
self.appicons_create()
|
|
||||||
self.appmenus_create()
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_appmenus_update(self):
|
|
||||||
"""
|
|
||||||
Similar to appmenus_recreate, but do not touch unchanged files
|
|
||||||
"""
|
|
||||||
self.appmenus_remove()
|
|
||||||
self.appmenus_cleanup()
|
|
||||||
self.appicons_create()
|
|
||||||
self.appicons_cleanup()
|
|
||||||
self.appmenus_create()
|
|
||||||
|
|
||||||
|
|
||||||
def QubesVm_set_attr(self, name, newvalue, oldvalue):
|
|
||||||
if name == 'internal':
|
|
||||||
if newvalue and not oldvalue:
|
|
||||||
self.appmenus_remove()
|
|
||||||
elif not newvalue and oldvalue:
|
|
||||||
self.appmenus_create()
|
|
||||||
|
|
||||||
|
|
||||||
# new methods
|
|
||||||
QubesVm.appmenus_create = QubesVm_appmenus_create
|
|
||||||
QubesVm.appmenus_remove = QubesVm_appmenus_remove
|
|
||||||
QubesVm.appmenus_cleanup = QubesVm_appmenus_cleanup
|
|
||||||
QubesVm.appmenus_recreate = QubesVm_appmenus_recreate
|
|
||||||
QubesVm.appmenus_update = QubesVm_appmenus_update
|
|
||||||
QubesVm.appmenus_replace_entry = QubesVm_appmenus_replace_entry
|
|
||||||
QubesVm.appicons_create = QubesVm_appicons_create
|
|
||||||
QubesVm.appicons_cleanup = QubesVm_appicons_cleanup
|
|
||||||
QubesVm.appicons_remove = QubesVm_appicons_remove
|
|
||||||
|
|
||||||
# hooks for existing methods
|
|
||||||
QubesVm.hooks_pre_rename.append(QubesVm_pre_rename)
|
|
||||||
QubesVm.hooks_post_rename.append(QubesVm_post_rename)
|
|
||||||
QubesVm.hooks_create_on_disk.append(QubesVm_create_on_disk)
|
|
||||||
QubesVm.hooks_clone_disk_files.append(QubesVm_clone_disk_files)
|
|
||||||
QubesVm.hooks_remove_from_disk.append(QubesVm_remove_from_disk)
|
|
||||||
QubesVm.hooks_label_setter.append(QubesVm_label_setter)
|
|
||||||
QubesVm.hooks_set_attr.append(QubesVm_set_attr)
|
|
@ -1,392 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# The Qubes OS Project, http://www.qubes-os.org
|
|
||||||
#
|
|
||||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
|
||||||
#
|
|
||||||
# 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, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
import optparse
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
import pipes
|
|
||||||
|
|
||||||
from optparse import OptionParser
|
|
||||||
from qubes.qubes import QubesVmCollection, QubesException, system_path
|
|
||||||
from qubes.qubes import QubesHVm
|
|
||||||
from qubes.qubes import vm_files
|
|
||||||
import qubes.imgconverter
|
|
||||||
from qubes.qubes import vmm
|
|
||||||
|
|
||||||
# fields required to be present (and verified) in retrieved desktop file
|
|
||||||
required_fields = ["Name", "Exec"]
|
|
||||||
|
|
||||||
# limits
|
|
||||||
appmenus_line_size = 1024
|
|
||||||
appmenus_line_count = 100000
|
|
||||||
|
|
||||||
# regexps for sanitization of retrieved values
|
|
||||||
std_re = re.compile(r"^[/a-zA-Z0-9.,:&()_ -]*$")
|
|
||||||
fields_regexp = {
|
|
||||||
"Name": std_re,
|
|
||||||
"GenericName": std_re,
|
|
||||||
"Comment": std_re,
|
|
||||||
"Categories": re.compile(r"^[a-zA-Z0-9/.;:'() -]*$"),
|
|
||||||
"Exec": re.compile(r"^[a-zA-Z0-9()_%&>/{}\"'\\:.= -]*$"),
|
|
||||||
"Icon": re.compile(r"^[a-zA-Z0-9/_.-]*$"),
|
|
||||||
}
|
|
||||||
|
|
||||||
CATEGORIES_WHITELIST = {
|
|
||||||
# Main Categories
|
|
||||||
# http://standards.freedesktop.org/menu-spec/1.1/apa.html 20140507
|
|
||||||
'AudioVideo', 'Audio', 'Video', 'Development', 'Education', 'Game',
|
|
||||||
'Graphics', 'Network', 'Office', 'Science', 'Settings', 'System',
|
|
||||||
'Utility',
|
|
||||||
|
|
||||||
# Additional Categories
|
|
||||||
# http://standards.freedesktop.org/menu-spec/1.1/apas02.html
|
|
||||||
'Building', 'Debugger', 'IDE', 'GUIDesigner', 'Profiling',
|
|
||||||
'RevisionControl', 'Translation', 'Calendar', 'ContactManagement',
|
|
||||||
'Database', 'Dictionary', 'Chart', 'Email', 'Finance', 'FlowChart', 'PDA',
|
|
||||||
'ProjectManagement', 'Presentation', 'Spreadsheet', 'WordProcessor',
|
|
||||||
'2DGraphics', 'VectorGraphics', 'RasterGraphics', '3DGraphics', 'Scanning',
|
|
||||||
'OCR', 'Photography', 'Publishing', 'Viewer', 'TextTools',
|
|
||||||
'DesktopSettings', 'HardwareSettings', 'Printing', 'PackageManager',
|
|
||||||
'Dialup', 'InstantMessaging', 'Chat', 'IRCClient', 'Feed', 'FileTransfer',
|
|
||||||
'HamRadio', 'News', 'P2P', 'RemoteAccess', 'Telephony', 'TelephonyTools',
|
|
||||||
'VideoConference', 'WebBrowser', 'WebDevelopment', 'Midi', 'Mixer',
|
|
||||||
'Sequencer', 'Tuner', 'TV', 'AudioVideoEditing', 'Player', 'Recorder',
|
|
||||||
'DiscBurning', 'ActionGame', 'AdventureGame', 'ArcadeGame', 'BoardGame',
|
|
||||||
'BlocksGame', 'CardGame', 'KidsGame', 'LogicGame', 'RolePlaying',
|
|
||||||
'Shooter', 'Simulation', 'SportsGame', 'StrategyGame', 'Art',
|
|
||||||
'Construction', 'Music', 'Languages', 'ArtificialIntelligence',
|
|
||||||
'Astronomy', 'Biology', 'Chemistry', 'ComputerScience',
|
|
||||||
'DataVisualization', 'Economy', 'Electricity', 'Geography', 'Geology',
|
|
||||||
'Geoscience', 'History', 'Humanities', 'ImageProcessing', 'Literature',
|
|
||||||
'Maps', 'Math', 'NumericalAnalysis', 'MedicalSoftware', 'Physics',
|
|
||||||
'Robotics', 'Spirituality', 'Sports', 'ParallelComputing', 'Amusement',
|
|
||||||
'Archiving', 'Compression', 'Electronics', 'Emulator', 'Engineering',
|
|
||||||
'FileTools', 'FileManager', 'TerminalEmulator', 'Filesystem', 'Monitor',
|
|
||||||
'Security', 'Accessibility', 'Calculator', 'Clock', 'TextEditor',
|
|
||||||
'Documentation', 'Adult', 'Core', 'KDE', 'GNOME', 'XFCE', 'GTK', 'Qt',
|
|
||||||
'Motif', 'Java', 'ConsoleOnly',
|
|
||||||
|
|
||||||
# Reserved Categories (not whitelisted)
|
|
||||||
# http://standards.freedesktop.org/menu-spec/1.1/apas03.html
|
|
||||||
# 'Screensaver', 'TrayIcon', 'Applet', 'Shell',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def sanitise_categories(untrusted_value):
|
|
||||||
untrusted_categories = (c.strip() for c in untrusted_value.split(';') if c)
|
|
||||||
categories = (c for c in untrusted_categories if c in CATEGORIES_WHITELIST)
|
|
||||||
|
|
||||||
return ';'.join(categories) + ';'
|
|
||||||
|
|
||||||
|
|
||||||
def fallback_hvm_appmenulist():
|
|
||||||
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
|
|
||||||
untrusted_appmenulist = []
|
|
||||||
if vm is None:
|
|
||||||
while appmenus_line_count > 0:
|
|
||||||
untrusted_line = sys.stdin.readline(appmenus_line_size)
|
|
||||||
if untrusted_line == "":
|
|
||||||
break
|
|
||||||
untrusted_appmenulist.append(untrusted_line.strip())
|
|
||||||
appmenus_line_count -= 1
|
|
||||||
if appmenus_line_count == 0:
|
|
||||||
raise QubesException("Line count limit exceeded")
|
|
||||||
else:
|
|
||||||
p = vm.run('QUBESRPC qubes.GetAppmenus dom0', passio_popen=True,
|
|
||||||
gui=False)
|
|
||||||
while appmenus_line_count > 0:
|
|
||||||
untrusted_line = p.stdout.readline(appmenus_line_size)
|
|
||||||
if untrusted_line == "":
|
|
||||||
break
|
|
||||||
untrusted_appmenulist.append(untrusted_line.strip())
|
|
||||||
appmenus_line_count -= 1
|
|
||||||
p.wait()
|
|
||||||
if p.returncode != 0:
|
|
||||||
if isinstance(vm, QubesHVm):
|
|
||||||
untrusted_appmenulist = fallback_hvm_appmenulist()
|
|
||||||
else:
|
|
||||||
raise QubesException("Error getting application list")
|
|
||||||
if appmenus_line_count == 0:
|
|
||||||
raise QubesException("Line count limit exceeded")
|
|
||||||
|
|
||||||
appmenus = {}
|
|
||||||
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
|
|
||||||
# use search instead of match to skip file path
|
|
||||||
untrusted_m = line_rx.search(untrusted_line)
|
|
||||||
if untrusted_m:
|
|
||||||
filename = untrusted_m.group(1)
|
|
||||||
assert '/' not in filename
|
|
||||||
assert '\0' not in filename
|
|
||||||
|
|
||||||
untrusted_key = untrusted_m.group(2)
|
|
||||||
assert '\0' not in untrusted_key
|
|
||||||
assert '\x1b' not in untrusted_key
|
|
||||||
assert '=' not in untrusted_key
|
|
||||||
|
|
||||||
untrusted_value = untrusted_m.group(3)
|
|
||||||
# TODO add key-dependent asserts
|
|
||||||
|
|
||||||
# Look only at predefined keys
|
|
||||||
if untrusted_key in fields_regexp:
|
|
||||||
if fields_regexp[untrusted_key].match(untrusted_value):
|
|
||||||
# now values are sanitized
|
|
||||||
key = untrusted_key
|
|
||||||
if key == 'Categories':
|
|
||||||
value = sanitise_categories(untrusted_value)
|
|
||||||
else:
|
|
||||||
value = untrusted_value
|
|
||||||
|
|
||||||
if filename not in appmenus:
|
|
||||||
appmenus[filename] = {}
|
|
||||||
|
|
||||||
appmenus[filename][key] = value
|
|
||||||
else:
|
|
||||||
print >> sys.stderr, \
|
|
||||||
"Warning: ignoring key %r of %s" % \
|
|
||||||
(untrusted_key, filename)
|
|
||||||
# else: ignore this key
|
|
||||||
|
|
||||||
return appmenus
|
|
||||||
|
|
||||||
|
|
||||||
def create_template(path, values):
|
|
||||||
# check if all required fields are present
|
|
||||||
for key in required_fields:
|
|
||||||
if key not in values:
|
|
||||||
print >> sys.stderr, "Warning: not creating/updating '%s' " \
|
|
||||||
"because of missing '%s' key" % (
|
|
||||||
path, key)
|
|
||||||
return
|
|
||||||
|
|
||||||
desktop_entry = ""
|
|
||||||
desktop_entry += "[Desktop Entry]\n"
|
|
||||||
desktop_entry += "Version=1.0\n"
|
|
||||||
desktop_entry += "Type=Application\n"
|
|
||||||
desktop_entry += "Terminal=false\n"
|
|
||||||
desktop_entry += "X-Qubes-VmName=%VMNAME%\n"
|
|
||||||
|
|
||||||
if 'Icon' in values:
|
|
||||||
icon_file = os.path.splitext(os.path.split(path)[1])[0] + '.png'
|
|
||||||
desktop_entry += "Icon={0}\n".format(os.path.join(
|
|
||||||
'%VMDIR%', vm_files['appmenus_icons_subdir'], icon_file))
|
|
||||||
else:
|
|
||||||
desktop_entry += "Icon=%XDGICON%\n"
|
|
||||||
|
|
||||||
for key in ["Name", "GenericName"]:
|
|
||||||
if key in values:
|
|
||||||
desktop_entry += "{0}=%VMNAME%: {1}\n".format(key, values[key])
|
|
||||||
|
|
||||||
# force category X-Qubes-VM
|
|
||||||
values["Categories"] = values.get("Categories", "") + "X-Qubes-VM;"
|
|
||||||
|
|
||||||
for key in ["Comment", "Categories"]:
|
|
||||||
if key in values:
|
|
||||||
desktop_entry += "{0}={1}\n".format(key, values[key])
|
|
||||||
|
|
||||||
desktop_entry += "Exec=qvm-run -q --tray -a %VMNAME% -- {0}\n".format(
|
|
||||||
pipes.quote(values['Exec']))
|
|
||||||
if not os.path.exists(path) or desktop_entry != open(path, "r").read():
|
|
||||||
desktop_file = open(path, "w")
|
|
||||||
desktop_file.write(desktop_entry)
|
|
||||||
desktop_file.close()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN")
|
|
||||||
usage = "usage: %prog [options] <vm-name>\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")
|
|
||||||
# Do not use any RPC call, expects data on stdin (in qubes.GetAppmenus
|
|
||||||
# format)
|
|
||||||
parser.add_option("--offline-mode", dest="offline_mode",
|
|
||||||
action="store_true", default=False,
|
|
||||||
help=optparse.SUPPRESS_HELP)
|
|
||||||
|
|
||||||
(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
|
|
||||||
else:
|
|
||||||
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 into " \
|
|
||||||
"permissions problems."
|
|
||||||
print >> sys.stderr, "Retry as unprivileged user."
|
|
||||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if options.offline_mode:
|
|
||||||
vmm.offline_mode = True
|
|
||||||
qvm_collection = QubesVmCollection()
|
|
||||||
qvm_collection.lock_db_for_reading()
|
|
||||||
qvm_collection.load()
|
|
||||||
qvm_collection.unlock_db()
|
|
||||||
|
|
||||||
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)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if vm.template is not None:
|
|
||||||
print >> sys.stderr, "ERROR: To sync appmenus for template based VM, " \
|
|
||||||
"do it on template instead"
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if not options.offline_mode and not vm.is_running():
|
|
||||||
print >> sys.stderr, "ERROR: Appmenus can be retrieved only from " \
|
|
||||||
"running VM - start it first"
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if not options.offline_mode and env_vmname is None or options.force_rpc:
|
|
||||||
new_appmenus = get_appmenus(vm)
|
|
||||||
else:
|
|
||||||
options.verbose = False
|
|
||||||
new_appmenus = get_appmenus(None)
|
|
||||||
|
|
||||||
if len(new_appmenus) == 0:
|
|
||||||
print >> sys.stderr, "ERROR: No appmenus received, terminating"
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
os.umask(002)
|
|
||||||
|
|
||||||
if not os.path.exists(vm.appmenus_templates_dir):
|
|
||||||
os.mkdir(vm.appmenus_templates_dir)
|
|
||||||
|
|
||||||
if not os.path.exists(vm.appmenus_template_icons_dir):
|
|
||||||
os.mkdir(vm.appmenus_template_icons_dir)
|
|
||||||
|
|
||||||
# Create new/update existing templates
|
|
||||||
if options.verbose:
|
|
||||||
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)):
|
|
||||||
print >> sys.stderr, "---> Updating {0}".format(appmenu_file)
|
|
||||||
else:
|
|
||||||
print >> sys.stderr, "---> Creating {0}".format(appmenu_file)
|
|
||||||
|
|
||||||
# TODO: icons support in offline mode
|
|
||||||
if options.offline_mode:
|
|
||||||
new_appmenus[appmenu_file].pop('Icon', None)
|
|
||||||
if 'Icon' in new_appmenus[appmenu_file]:
|
|
||||||
# the following line is used for time comparison
|
|
||||||
icondest = os.path.join(vm.appmenus_template_icons_dir,
|
|
||||||
os.path.splitext(appmenu_file)[0] + '.png')
|
|
||||||
|
|
||||||
try:
|
|
||||||
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:
|
|
||||||
old_icon = None
|
|
||||||
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)
|
|
||||||
|
|
||||||
if os.path.exists(icondest):
|
|
||||||
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])
|
|
||||||
|
|
||||||
# Delete appmenus of removed applications
|
|
||||||
if options.verbose:
|
|
||||||
print >> sys.stderr, "--> Cleaning old files"
|
|
||||||
for appmenu_file in os.listdir(vm.appmenus_templates_dir):
|
|
||||||
if not appmenu_file.endswith('.desktop'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if appmenu_file not in new_appmenus:
|
|
||||||
if options.verbose:
|
|
||||||
print >> sys.stderr, "---> Removing {0}".format(appmenu_file)
|
|
||||||
os.unlink(os.path.join(vm.appmenus_templates_dir, appmenu_file))
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
vm.appmenus_update()
|
|
||||||
if hasattr(vm, 'appvms'):
|
|
||||||
os.putenv('SKIP_CACHE_REBUILD', '1')
|
|
||||||
for child_vm in vm.appvms.values():
|
|
||||||
try:
|
|
||||||
child_vm.appmenus_update()
|
|
||||||
except Exception, e:
|
|
||||||
print >> sys.stderr, "---> Failed to recreate appmenus for " \
|
|
||||||
"'{0}': {1}".format(child_vm.name, str(e))
|
|
||||||
subprocess.call(['xdg-desktop-menu', 'forceupdate'])
|
|
||||||
if 'KDE_SESSION_UID' in os.environ:
|
|
||||||
subprocess.call(['kbuildsycoca' + os.environ.get('KDE_SESSION_VERSION', '4')])
|
|
||||||
os.unsetenv('SKIP_CACHE_REBUILD')
|
|
||||||
|
|
||||||
|
|
||||||
main()
|
|
@ -1 +0,0 @@
|
|||||||
/usr/libexec/qubes-appmenus/qubes-receive-appmenus
|
|
@ -1,56 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# The Qubes OS Project, http://www.qubes-os.org
|
|
||||||
#
|
|
||||||
# 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, write to the Free Software
|
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
import sys
|
|
||||||
from qubes.qubes import QubesVmCollection
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if len(sys.argv) != 4:
|
|
||||||
print >> sys.stderr, \
|
|
||||||
'Usage: qvm-appmenu-replace VM_NAME OLD_NAME.desktop NEW_NAME.desktop'
|
|
||||||
sys.exit(1)
|
|
||||||
vm_name = sys.argv[1]
|
|
||||||
old_name = sys.argv[2]
|
|
||||||
new_name = sys.argv[3]
|
|
||||||
|
|
||||||
qvm_collection = QubesVmCollection()
|
|
||||||
qvm_collection.lock_db_for_reading()
|
|
||||||
qvm_collection.load()
|
|
||||||
qvm_collection.unlock_db()
|
|
||||||
|
|
||||||
vm = qvm_collection.get_vm_by_name(vm_name)
|
|
||||||
|
|
||||||
if vm is None:
|
|
||||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' " \
|
|
||||||
"does not exist in the system.".format(
|
|
||||||
vm_name)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if vm.template is not None:
|
|
||||||
print >> sys.stderr, "ERROR: To replace appmenu for template based VM, " \
|
|
||||||
"do it on template instead"
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
vm.appmenus_replace_entry(old_name, new_name)
|
|
||||||
if hasattr(vm, 'appvms'):
|
|
||||||
for child_vm in vm.appvms.values():
|
|
||||||
child_vm.appmenus_replace_entry(old_name, new_name)
|
|
||||||
|
|
||||||
main()
|
|
@ -1,2 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
exec /usr/libexec/qubes-appmenus/qubes-receive-appmenus $@
|
|
@ -1,23 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
VMNAME=$1
|
|
||||||
VMTYPE=$2
|
|
||||||
if [ -z "$VMTYPE" ]; then
|
|
||||||
VMTYPE=appvms
|
|
||||||
fi
|
|
||||||
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
|
|
||||||
APPSDIR=$VMDIR/apps
|
|
||||||
|
|
||||||
if [ $# -lt 1 ]; then
|
|
||||||
echo "usage: $0 <vmname> [appvms|vm-templates|servicevms]"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ls $APPSDIR/*.directory $APPSDIR/*.desktop > /dev/null 2>&1; then
|
|
||||||
LC_COLLATE=C xdg-desktop-menu uninstall $APPSDIR/*.directory $APPSDIR/*.desktop
|
|
||||||
rm -f $APPSDIR/*.desktop $APPSDIR/*.directory
|
|
||||||
rm -f $HOME/.config/menus/applications-merged/user-$VMNAME-vm.menu
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$KDE_SESSION_UID" -a -z "$SKIP_CACHE_REBUILD" ]; then
|
|
||||||
kbuildsycoca$KDE_SESSION_VERSION
|
|
||||||
fi
|
|
Before Width: | Height: | Size: 169 KiB |
Before Width: | Height: | Size: 181 KiB |
@ -1,10 +0,0 @@
|
|||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
This copyright and license notice covers the images in this directory.
|
|
||||||
************************************************************************
|
|
||||||
|
|
||||||
TITLE: Crystal Project Icons
|
|
||||||
AUTHOR: Everaldo Coelho
|
|
||||||
SITE: http://www.everaldo.com
|
|
||||||
CONTACT: everaldo@everaldo.com
|
|
||||||
|
|
||||||
Copyright (c) 2006-2007 Everaldo Coelho.
|
|
@ -1 +0,0 @@
|
|||||||
dom0-update-avail icon from gnome-packagekit project distributed under GPLv2
|
|
@ -1 +0,0 @@
|
|||||||
Color padlock images downloaded from www.openclipart.org
|
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 188 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 185 KiB |
@ -1,257 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import subprocess
|
|
||||||
from qubes.qubes import vmm
|
|
||||||
from qubes.qubes import QubesVmCollection
|
|
||||||
import qubes.guihelpers
|
|
||||||
import libvirt
|
|
||||||
from optparse import OptionParser
|
|
||||||
import fcntl
|
|
||||||
|
|
||||||
POLICY_FILE_DIR="/etc/qubes-rpc/policy"
|
|
||||||
# XXX: Backward compatibility, to be removed soon
|
|
||||||
DEPRECATED_POLICY_FILE_DIR="/etc/qubes_rpc/policy"
|
|
||||||
QREXEC_CLIENT="/usr/lib/qubes/qrexec-client"
|
|
||||||
QUBES_RPC_MULTIPLEXER_PATH="/usr/lib/qubes/qubes-rpc-multiplexer"
|
|
||||||
|
|
||||||
class UserChoice:
|
|
||||||
ALLOW=0
|
|
||||||
DENY=1
|
|
||||||
ALWAYS_ALLOW=2
|
|
||||||
|
|
||||||
def line_to_dict(line):
|
|
||||||
tokens=line.split()
|
|
||||||
if len(tokens) < 3:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if tokens[0][0] == '#':
|
|
||||||
return None
|
|
||||||
|
|
||||||
dict={}
|
|
||||||
dict['source']=tokens[0]
|
|
||||||
dict['dest']=tokens[1]
|
|
||||||
|
|
||||||
dict['full-action']=tokens[2]
|
|
||||||
action_list=tokens[2].split(',')
|
|
||||||
dict['action']=action_list.pop(0)
|
|
||||||
|
|
||||||
for iter in action_list:
|
|
||||||
paramval=iter.split("=")
|
|
||||||
dict["action."+paramval[0]]=paramval[1]
|
|
||||||
|
|
||||||
# Warn if we're ignoring extra data after a space, such as:
|
|
||||||
# vm1 vm2 allow, user=foo
|
|
||||||
if len(tokens) > 3:
|
|
||||||
print >>sys.stderr, "Trailing data ignored in %s" % line
|
|
||||||
|
|
||||||
return dict
|
|
||||||
|
|
||||||
|
|
||||||
def read_policy_file(service_name):
|
|
||||||
policy_file = os.path.join(POLICY_FILE_DIR, service_name)
|
|
||||||
if not os.path.isfile(policy_file):
|
|
||||||
# fallback to policy without specific argument set (if any)
|
|
||||||
policy_file = os.path.join(POLICY_FILE_DIR, service_name.split("+")[0])
|
|
||||||
if not os.path.isfile(policy_file):
|
|
||||||
policy_file = os.path.join(DEPRECATED_POLICY_FILE_DIR, service_name)
|
|
||||||
if not os.path.isfile(policy_file):
|
|
||||||
return None
|
|
||||||
print >>sys.stderr, "RPC service '%s' uses deprecated policy location, please move to %s" % (service_name, POLICY_FILE_DIR)
|
|
||||||
policy_list=list()
|
|
||||||
f = open(policy_file)
|
|
||||||
fcntl.flock(f, fcntl.LOCK_SH)
|
|
||||||
for iter in f.readlines():
|
|
||||||
dict = line_to_dict(iter)
|
|
||||||
if dict is not None:
|
|
||||||
policy_list.append(dict)
|
|
||||||
f.close()
|
|
||||||
return policy_list
|
|
||||||
|
|
||||||
def is_match(item, config_term):
|
|
||||||
return (item != "dom0" and config_term == "$anyvm") or item == config_term
|
|
||||||
|
|
||||||
def get_default_policy():
|
|
||||||
dict={}
|
|
||||||
dict["action"]="deny"
|
|
||||||
return dict
|
|
||||||
|
|
||||||
|
|
||||||
def find_policy(policy, domain, target):
|
|
||||||
for iter in policy:
|
|
||||||
if not is_match(domain, iter["source"]):
|
|
||||||
continue
|
|
||||||
if not is_match(target, iter["dest"]):
|
|
||||||
continue
|
|
||||||
return iter
|
|
||||||
return get_default_policy()
|
|
||||||
|
|
||||||
def validate_target(target):
|
|
||||||
# special targets
|
|
||||||
if target in ['$dispvm']:
|
|
||||||
return True
|
|
||||||
|
|
||||||
qc = QubesVmCollection()
|
|
||||||
qc.lock_db_for_reading()
|
|
||||||
qc.load()
|
|
||||||
qc.unlock_db()
|
|
||||||
|
|
||||||
return qc.get_vm_by_name(target)
|
|
||||||
|
|
||||||
def spawn_target_if_necessary(vm):
|
|
||||||
if vm.is_running():
|
|
||||||
return
|
|
||||||
# use qvm-run instead of vm.start() to make sure that nothing is written
|
|
||||||
# to stdout and nothing is read from stdin
|
|
||||||
null = open("/dev/null", "r+")
|
|
||||||
subprocess.call(["qvm-run", "-a", "--tray", "-q", vm.name, "true"],
|
|
||||||
stdin=null, stdout=null)
|
|
||||||
null.close()
|
|
||||||
|
|
||||||
def do_execute(domain, target, user, service_name, process_ident, vm=None):
|
|
||||||
if target == "$dispvm":
|
|
||||||
cmd = "/usr/lib/qubes/qfile-daemon-dvm " + service_name + " " + domain + " " +user
|
|
||||||
os.execl(QREXEC_CLIENT, "qrexec-client",
|
|
||||||
"-d", "dom0", "-c", process_ident, cmd)
|
|
||||||
else:
|
|
||||||
if isinstance(vm, qubes.qubes.QubesVm):
|
|
||||||
spawn_target_if_necessary(vm)
|
|
||||||
if target == "dom0":
|
|
||||||
cmd = QUBES_RPC_MULTIPLEXER_PATH + " " + service_name + " " + domain
|
|
||||||
else:
|
|
||||||
cmd = user + ":QUBESRPC "+ service_name + " " + domain
|
|
||||||
# stderr should be logged in source/target VM
|
|
||||||
null = open(os.devnull, 'w')
|
|
||||||
os.dup2(null.fileno(), 2)
|
|
||||||
os.execl(QREXEC_CLIENT, "qrexec-client",
|
|
||||||
"-d", target, "-c", process_ident, cmd)
|
|
||||||
|
|
||||||
def confirm_execution(domain, target, service_name):
|
|
||||||
text = "Do you allow domain \"" +domain + "\" to execute " + service_name
|
|
||||||
text+= " operation on the domain \"" + target +"\"?<br>"
|
|
||||||
text+= " \"Yes to All\" option will automatically allow this operation in the future."
|
|
||||||
return qubes.guihelpers.ask(text, yestoall=True)
|
|
||||||
|
|
||||||
def add_always_allow(domain, target, service_name, options):
|
|
||||||
policy_file=POLICY_FILE_DIR+"/"+service_name
|
|
||||||
if not os.path.isfile(policy_file):
|
|
||||||
return None
|
|
||||||
f = open(policy_file, 'r+')
|
|
||||||
fcntl.flock(f, fcntl.LOCK_EX)
|
|
||||||
lines = []
|
|
||||||
for l in f.readlines():
|
|
||||||
lines.append(l)
|
|
||||||
lines.insert(0, "%s\t%s\tallow%s\n" % (domain, target, options))
|
|
||||||
f.seek(0)
|
|
||||||
f.write("".join(lines))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def info_dialog(msg_type, text):
|
|
||||||
if msg_type not in ['info', 'warning', 'error', 'entry']:
|
|
||||||
raise ValueError("Invalid msg_type value")
|
|
||||||
if msg_type in ['info', 'warning', 'error']:
|
|
||||||
try:
|
|
||||||
subprocess.call(["/usr/bin/zenity", "--{}".format(msg_type), "--text",
|
|
||||||
text])
|
|
||||||
except OSError:
|
|
||||||
kdialog_msg_type = {
|
|
||||||
'info': 'msgbox',
|
|
||||||
'warning': 'sorry',
|
|
||||||
'error': 'error'
|
|
||||||
}[msg_type]
|
|
||||||
subprocess.call(["/usr/bin/kdialog", "--{}".format(kdialog_msg_type), text])
|
|
||||||
else:
|
|
||||||
response = subprocess.check_output(["/usr/bin/zenity", "--{}".format(msg_type), "--text",
|
|
||||||
text])
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def policy_editor(domain, target, service_name):
|
|
||||||
text = "No policy definition found for " + service_name + " action. \n"
|
|
||||||
text+= "Type YES if you want to create a default policy file"
|
|
||||||
response = info_dialog('entry', text)
|
|
||||||
if response.strip() == 'YES':
|
|
||||||
create_policy(service_name)
|
|
||||||
|
|
||||||
|
|
||||||
def create_policy(service_name):
|
|
||||||
policyFile = "/etc/qubes-rpc/policy/"+service_name
|
|
||||||
policy = open(policyFile, "w")
|
|
||||||
policy.write("## Note that policy parsing stops at the first match,\n")
|
|
||||||
policy.write("## so adding anything below \"$anyvm $anyvm action\" line will have no effect\n")
|
|
||||||
policy.write("\n")
|
|
||||||
policy.write("## Please use a single # to start your custom comments\n")
|
|
||||||
policy.write("\n")
|
|
||||||
policy.write("$anyvm $anyvm ask\n")
|
|
||||||
policy.close()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
usage = "usage: %prog [options] <src-domain-id> <src-domain> <target-domain> <service> <process-ident>"
|
|
||||||
parser = OptionParser (usage)
|
|
||||||
parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False,
|
|
||||||
help="Allow run of service without confirmation if policy say 'ask'")
|
|
||||||
parser.add_option ("--just-evaluate", action="store_true", dest="just_evaluate", default=False,
|
|
||||||
help="Do not run the service, only evaluate policy; retcode=0 means 'allow'")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args ()
|
|
||||||
domain_id=args[0]
|
|
||||||
domain=args[1]
|
|
||||||
target=args[2]
|
|
||||||
service_name=args[3]
|
|
||||||
process_ident=args[4]
|
|
||||||
|
|
||||||
# Add source domain information, required by qrexec-client for establishing
|
|
||||||
# connection
|
|
||||||
process_ident+=","+domain+","+domain_id
|
|
||||||
|
|
||||||
vm = validate_target(target)
|
|
||||||
if vm is None:
|
|
||||||
print >> sys.stderr, "Rpc failed (unknown domain):", domain, target, service_name
|
|
||||||
text = "Domain '%s' doesn't exist (service %s called by domain %s)." % (
|
|
||||||
target, service_name, domain)
|
|
||||||
info_dialog("error", text)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
policy_list=read_policy_file(service_name)
|
|
||||||
if policy_list==None:
|
|
||||||
policy_editor(domain, target, service_name)
|
|
||||||
policy_list=read_policy_file(service_name)
|
|
||||||
if policy_list==None:
|
|
||||||
policy_list=list()
|
|
||||||
|
|
||||||
policy_dict=find_policy(policy_list, domain, target)
|
|
||||||
|
|
||||||
if policy_dict["action"] == "ask" and options.assume_yes_for_ask:
|
|
||||||
policy_dict["action"] = "allow"
|
|
||||||
|
|
||||||
if policy_dict["action"] == "ask":
|
|
||||||
user_choice = confirm_execution(domain, target, service_name)
|
|
||||||
if user_choice == UserChoice.ALWAYS_ALLOW:
|
|
||||||
add_always_allow(domain, target, service_name, policy_dict["full-action"].lstrip('ask'))
|
|
||||||
policy_dict["action"] = "allow"
|
|
||||||
elif user_choice == UserChoice.ALLOW:
|
|
||||||
policy_dict["action"] = "allow"
|
|
||||||
else:
|
|
||||||
policy_dict["action"] = "deny"
|
|
||||||
|
|
||||||
if options.just_evaluate:
|
|
||||||
if policy_dict["action"] == "allow":
|
|
||||||
exit(0)
|
|
||||||
else:
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if policy_dict["action"] == "allow":
|
|
||||||
if policy_dict.has_key("action.target"):
|
|
||||||
target=policy_dict["action.target"]
|
|
||||||
if policy_dict.has_key("action.user"):
|
|
||||||
user=policy_dict["action.user"]
|
|
||||||
else:
|
|
||||||
user="DEFAULT"
|
|
||||||
print >> sys.stderr, "Rpc allowed:", domain, target, service_name
|
|
||||||
do_execute(domain, target, user, service_name, process_ident, vm=vm)
|
|
||||||
|
|
||||||
print >> sys.stderr, "Rpc denied:", domain, target, service_name
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
main()
|
|