mirror of
https://github.com/altlinux/gpupdate.git
synced 2025-02-02 05:47:28 +03:00
287 lines
8.7 KiB
Python
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)
|