1
0
mirror of https://github.com/altlinux/gpupdate.git synced 2025-02-02 05:47:28 +03:00
gpupdate/gpoa/gpt/shortcuts.py
Valery Sinelnikov 4be89029aa Updated dates
2024-11-14 13:54:36 +04:00

287 lines
8.7 KiB
Python

#
# GPOA - GPO Applier for Linux
#
# Copyright (C) 2019-2024 BaseALT Ltd.
#
# 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 3 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/>.
from pathlib import Path
import stat
from enum import Enum
from xdg.DesktopEntry import DesktopEntry
import json
from util.windows import transform_windows_path
from util.xml import get_xml_root
from util.paths import get_desktop_files_directory
from .dynamic_attributes import DynamicAttributes
class TargetType(Enum):
FILESYSTEM = 'FILESYSTEM'
URL = 'URL'
def __str__(self):
return self.value
def get_ttype(targetstr):
'''
Validation function for targetType property
:targetstr: String representing link type.
:returns: Object of type TargetType.
'''
ttype = TargetType.FILESYSTEM
if targetstr == 'URL'or targetstr == TargetType.URL:
ttype = TargetType.URL
return ttype
def ttype2str(ttype):
'''
Transform TargetType to string for JSON serialization
:param ttype: TargetType object
'''
result = 'FILESYSTEM'
if ttype == TargetType.URL:
result = 'URL'
return result
def read_shortcuts(shortcuts_file):
'''
Read shortcut objects from GPTs XML file
:shortcuts_file: Location of Shortcuts.xml
'''
shortcuts = list()
for link in get_xml_root(shortcuts_file):
props = link.find('Properties')
# Location of the link itself
dest = props.get('shortcutPath')
# Location where link should follow
path = transform_windows_path(props.get('targetPath'))
# Arguments to executable file
arguments = props.get('arguments')
# URL or FILESYSTEM
target_type = get_ttype(props.get('targetType'))
sc = shortcut(dest, path, arguments, link.get('name'), props.get('action'), target_type)
sc.set_changed(link.get('changed'))
sc.set_clsid(link.get('clsid'))
sc.set_guid(link.get('uid'))
sc.set_usercontext(link.get('userContext', False))
sc.set_icon(props.get('iconPath'))
if props.get('comment'):
sc.set_comment(props.get('comment'))
shortcuts.append(sc)
return shortcuts
def merge_shortcuts(storage, sid, shortcut_objects, policy_name):
for shortcut in shortcut_objects:
storage.add_shortcut(sid, shortcut, policy_name)
def find_desktop_entry(binary_path):
desktop_dir = get_desktop_files_directory()
binary_name = ''.join(binary_path.split('/')[-1])
desktop_file_path = Path(f"{desktop_dir}/{binary_name}.desktop")
if desktop_file_path.exists():
desktop_entry = DesktopEntry()
desktop_entry.parse(desktop_file_path)
return desktop_entry
return None
class shortcut(DynamicAttributes):
def __init__(self, dest, path, arguments, name=None, action=None, ttype=TargetType.FILESYSTEM):
'''
:param dest: Path to resulting file on file system
:param path: Path where the link should point to
:param arguments: Arguemnts to eecutable file
:param name: Name of the application
:param type: Link type - FILESYSTEM or URL
'''
self.dest = self.replace_slashes(dest)
self.path = path
self.expanded_path = None
self.arguments = arguments
self.name = self.replace_name(name)
self.action = action
self.changed = ''
self.icon = None
self.comment = ''
self.is_in_user_context = self.set_usercontext()
self.type = ttype
self.desktop_file_template = None
def replace_slashes(self, input_path):
if input_path.startswith('%'):
index = input_path.find('%', 1)
if index != -1:
replace_path = input_path[:index + 2] + input_path[index + 2:].replace('/','-')
return replace_path
return input_path.replace('/','-')
def replace_name(self, input_name):
if input_name.startswith('%'):
index = input_name.find('%', 1)
if index != -1:
replace_name = input_name[index + 2:]
return replace_name
return input_name
def __str__(self):
result = self.to_json()
return result
def set_changed(self, change_date):
'''
Set object change date
'''
self.changed = change_date
def set_clsid(self, clsid):
self.clsid = clsid
def set_guid(self, uid):
self.guid = uid
def set_icon(self, icon_name):
self.icon = icon_name
def set_comment(self, comment):
self.comment = comment
def set_type(self, ttype):
'''
Set type of the hyperlink - FILESYSTEM or URL
:ttype: - object of class TargetType
'''
self.type = ttype
def set_usercontext(self, usercontext=False):
'''
Perform action in user context or not
'''
ctx = False
if usercontext in [1, '1', True]:
ctx = True
self.is_in_user_context = ctx
def set_expanded_path(self, path):
'''
Adjust shortcut path with expanding windows variables
'''
self.expanded_path = path
def is_usercontext(self):
return self.is_in_user_context
def desktop(self, dest=None):
'''
Returns desktop file object which may be written to disk.
'''
if dest:
self.desktop_file = DesktopEntry(dest)
else:
self.desktop_file_template = find_desktop_entry(self.path)
self.desktop_file = DesktopEntry()
self.desktop_file.addGroup('Desktop Entry')
self.desktop_file.set('Version', '1.0')
self._update_desktop()
return self.desktop_file
def _update_desktop(self):
'''
Update desktop file object from internal data.
'''
if get_ttype(self.type) == TargetType.URL:
self.desktop_file.set('Type', 'Link')
else:
self.desktop_file.set('Type', 'Application')
self.desktop_file.set('Name', self.name)
desktop_path = self.path
if self.expanded_path:
desktop_path = self.expanded_path
if get_ttype(self.type) == TargetType.URL:
self.desktop_file.set('URL', desktop_path)
else:
str2bool_lambda = (lambda boolstr: boolstr if isinstance(boolstr, bool)
else boolstr and boolstr.lower() in ['True', 'true', 'yes', '1'])
if self.desktop_file_template:
terminal_state = str2bool_lambda(self.desktop_file_template.get('Terminal'))
self.desktop_file.set('Terminal', 'true' if terminal_state else 'false')
self.desktop_file.set('Exec', '{} {}'.format(desktop_path, self.arguments))
self.desktop_file.set('Comment', self.comment)
if self.icon:
self.desktop_file.set('Icon', self.icon)
elif self.desktop_file_template and self.desktop_file_template.get('Icon', False):
self.desktop_file.set('Icon', self.desktop_file_template.get('Icon'))
def _write_desktop(self, dest, create_only=False, read_firstly=False):
'''
Write .desktop file to disk using path 'dest'. Please note that
.desktop files must have executable bit set in order to work in
GUI.
'''
sc = Path(dest)
if sc.exists() and create_only:
return
if sc.exists() and read_firstly:
self.desktop(dest).write(dest)
else:
self.desktop().write(dest)
sc.chmod(sc.stat().st_mode | stat.S_IEXEC)
def _remove_desktop(self, dest):
'''
Remove .desktop file fromo disk using path 'dest'.
'''
sc = Path(dest)
if sc.exists():
sc.unlink()
def apply_desktop(self, dest):
'''
Apply .desktop file by action.
'''
if self.action == 'U':
self._write_desktop(dest, read_firstly=True)
elif self.action == 'D':
self._remove_desktop(dest)
elif self.action == 'R':
self._remove_desktop(dest)
self._write_desktop(dest)
elif self.action == 'C':
self._write_desktop(dest, create_only=True)