def uninstall(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult: if not self._make_exports_dir(watcher): return TransactionResult.fail() uninstalled, _ = ProcessHandler(watcher).handle_simple(flatpak.uninstall(pkg.ref, pkg.installation)) if uninstalled: if self.suggestions_cache: self.suggestions_cache.delete(pkg.id) self.revert_ignored_update(pkg) return TransactionResult(success=True, installed=None, removed=[pkg]) return TransactionResult.fail()
def uninstall(self, pkg: AppImage, root_password: Optional[str], watcher: ProcessWatcher, disk_loader: DiskCacheLoader = None) -> TransactionResult: if os.path.exists(pkg.get_disk_cache_path()): handler = ProcessHandler(watcher) if not handler.handle(SystemProcess(new_subprocess(['rm', '-rf', pkg.get_disk_cache_path()]))): watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.uninstall.error.remove_folder'].format(bold(pkg.get_disk_cache_path()))) return TransactionResult.fail() de_path = self._gen_desktop_entry_path(pkg) if os.path.exists(de_path): os.remove(de_path) self.revert_ignored_update(pkg) if pkg.symlink and os.path.islink(pkg.symlink): self.logger.info(f"Removing symlink '{pkg.symlink}'") try: os.remove(pkg.symlink) self.logger.info(f"symlink '{pkg.symlink}' successfully removed") except: msg = f"could not remove symlink '{pkg.symlink}'" self.logger.error(msg) if watcher: watcher.print(f"[error] {msg}") self._add_self_latest_version(pkg) # only for self installation return TransactionResult(success=True, installed=None, removed=[pkg])
def _gen_installation_response(self, success: bool, pkg: SnapApplication, installed: Set[str], disk_loader: DiskCacheLoader): if success: new_installed = [pkg] if installed: try: current_installed = self.read_installed( disk_loader=disk_loader, internet_available=internet.is_available()).installed except: current_installed = None if current_installed and ( not installed or len(current_installed) > len(installed) + 1): for p in current_installed: if p.name != pkg.name and (not installed or p.name not in installed): new_installed.append(p) return TransactionResult(success=success, installed=new_installed, removed=[]) else: return TransactionResult.fail()
def _gen_installation_response(self, success: bool, pkg: SnapApplication, installed: Set[str], disk_loader: DiskCacheLoader): if success: new_installed = [] try: net_available = self.context.internet_checker.is_available() current_installed = self.read_installed( disk_loader=disk_loader, internet_available=net_available).installed except: new_installed = [pkg] traceback.print_exc() current_installed = None if current_installed: for p in current_installed: if p.name == pkg.name or (not installed or p.name not in installed): new_installed.append(p) return TransactionResult(success=success, installed=new_installed, removed=[]) else: return TransactionResult.fail()
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult: uninstalled = ProcessHandler(watcher).handle( SystemProcess( subproc=snap.uninstall_and_stream(pkg.name, root_password))) if uninstalled: if self.suggestions_cache: self.suggestions_cache.delete(pkg.name) return TransactionResult(success=True, installed=None, removed=[pkg]) return TransactionResult.fail()
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult: if snap.is_installed() and snapd.is_running(): uninstalled = ProcessHandler(watcher).handle_simple( snap.uninstall_and_stream(pkg.name, root_password))[0] if uninstalled: if self.suggestions_cache: self.suggestions_cache.delete(pkg.name) return TransactionResult(success=True, installed=None, removed=[pkg]) return TransactionResult.fail()
def install(self, pkg: FlatpakApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult: flatpak_config = self.configman.get_config() install_level = flatpak_config['installation_level'] if install_level is not None: self.logger.info( "Default Flaptak installation level defined: {}".format( install_level)) if install_level not in ('user', 'system'): watcher.show_message( title=self.i18n['error'].capitalize(), body=self.i18n['flatpak.install.bad_install_level.body']. format(field=bold('installation_level'), file=bold(CONFIG_FILE)), type_=MessageType.ERROR) return TransactionResult(success=False, installed=[], removed=[]) pkg.installation = install_level else: user_level = watcher.request_confirmation( title=self.i18n['flatpak.install.install_level.title'], body=self.i18n['flatpak.install.install_level.body'].format( bold(pkg.name)), confirmation_label=self.i18n['no'].capitalize(), deny_label=self.i18n['yes'].capitalize()) pkg.installation = 'user' if user_level else 'system' remotes = flatpak.list_remotes() handler = ProcessHandler(watcher) if pkg.installation == 'user' and not remotes['user']: handler.handle_simple(flatpak.set_default_remotes('user')) elif pkg.installation == 'system' and not remotes['system']: if user.is_root(): handler.handle_simple(flatpak.set_default_remotes('system')) else: valid, user_password = watcher.request_root_password() if not valid: watcher.print('Operation aborted') return TransactionResult(success=False, installed=[], removed=[]) else: if not handler.handle_simple( flatpak.set_default_remotes( 'system', user_password))[0]: watcher.show_message( title=self.i18n['error'].capitalize(), body=self. i18n['flatpak.remotes.system_flathub.error'], type_=MessageType.ERROR) watcher.print("Operation cancelled") return TransactionResult(success=False, installed=[], removed=[]) # retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds flatpak_version = flatpak.get_version() installed = flatpak.list_installed(flatpak_version) installed_by_level = { '{}:{}:{}'.format(p['id'], p['name'], p['branch']) for p in installed if p['installation'] == pkg.installation } if installed else None if not self._make_exports_dir(handler.watcher): return TransactionResult(success=False, installed=[], removed=[]) installed, output = handler.handle_simple( flatpak.install(str(pkg.id), pkg.origin, pkg.installation)) if not installed and 'error: No ref chosen to resolve matches' in output: ref_opts = RE_INSTALL_REFS.findall(output) if ref_opts and len(ref_opts) > 1: view_opts = [ InputOption(label=o, value=o.strip()) for o in ref_opts if o ] ref_select = SingleSelectComponent(type_=SelectViewType.RADIO, options=view_opts, default_option=view_opts[0], label='') if watcher.request_confirmation( title=self.i18n['flatpak.install.ref_choose.title'], body=self.i18n['flatpak.install.ref_choose.body']. format(bold(pkg.name)), components=[ref_select], confirmation_label=self.i18n['proceed'].capitalize(), deny_label=self.i18n['cancel'].capitalize()): ref = ref_select.get_selected() installed, output = handler.handle_simple( flatpak.install(ref, pkg.origin, pkg.installation)) pkg.ref = ref pkg.runtime = 'runtime' in ref else: watcher.print('Aborted by the user') return TransactionResult.fail() else: return TransactionResult.fail() if installed: try: fields = flatpak.get_fields(str(pkg.id), pkg.branch, ['Ref', 'Branch']) if fields: pkg.ref = fields[0] pkg.branch = fields[1] except: traceback.print_exc() if installed: new_installed = [pkg] current_installed = flatpak.list_installed(flatpak_version) current_installed_by_level = [ p for p in current_installed if p['installation'] == pkg.installation ] if current_installed else None if current_installed_by_level and (not installed_by_level or len(current_installed_by_level) > len(installed_by_level) + 1): pkg_key = '{}:{}:{}'.format(pkg.id, pkg.name, pkg.branch) net_available = self.context.is_internet_available() for p in current_installed_by_level: current_key = '{}:{}:{}'.format(p['id'], p['name'], p['branch']) if current_key != pkg_key and (not installed_by_level or current_key not in installed_by_level): new_installed.append( self._map_to_model(app_json=p, installed=True, disk_loader=disk_loader, internet=net_available)) return TransactionResult(success=installed, installed=new_installed, removed=[]) else: return TransactionResult.fail()
def install(self, pkg: SoftwarePackage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher) -> TransactionResult: watcher.change_substatus(self._i18n['debian.simulate_operation']) transaction = self.aptitude.simulate_installation((pkg.name, )) if transaction is None or not transaction.to_install: return TransactionResult.fail() if transaction.to_remove or (transaction.to_install and len(transaction.to_install) > 1): watcher.change_substatus(self._i18n['debian.transaction.get_data']) deps = tuple(p for p in transaction.to_install or () if p.name != pkg.name) removal = tuple(p for p in transaction.to_remove or ()) all_pkgs = [*deps, *removal] pkgs_data = self.aptitude.show(pkgs=(f'{d.name}={d.version}' for d in all_pkgs), attrs=self.install_show_attrs) if pkgs_data: for p in all_pkgs: fill_show_data(p, pkgs_data.get(p.name)) if not self.view.confirm_transaction(to_install=deps, removal=removal, watcher=watcher): return TransactionResult.fail() watcher.change_substatus(self._i18n['debian.installing_pkgs']) handler = ProcessHandler(watcher) targets = (p.name for p in transaction.to_install) with self.output_handler.start(watcher=watcher, targets=targets, action=AptitudeAction.INSTALL) as handle: installed, _ = handler.handle_simple(self.aptitude.install(packages=(pkg.name,), root_password=root_password), output_handler=handle) if installed: self._refresh_apps_index(watcher) watcher.change_substatus(self._i18n['debian.install.validating']) currently_installed = set(self.aptitude.read_installed_names()) installed_instances = [] if currently_installed: for p in transaction.to_install: instance = p if p != pkg else pkg if instance.name in currently_installed: instance.installed = True instance.bind_app(self.apps_index.get(instance.name)) installed_instances.append(instance) removed = None if transaction.to_remove: removed = [p for p in transaction.to_remove if p.name not in currently_installed] not_removed = set(transaction.to_remove).difference(removed) if not_removed: not_removed_str = ' '.join(p.name for p in not_removed) self._log.warning(f"The following packages were not removed: {not_removed_str}") return TransactionResult(installed=installed_instances, removed=removed, success=bool(installed_instances and pkg in installed_instances)) else: watcher.change_substatus('') return TransactionResult.fail()
def uninstall(self, pkg: DebianPackage, root_password: str, watcher: ProcessWatcher, disk_loader: Optional[DiskCacheLoader], purge: bool = False) -> TransactionResult: config_ = self.configman.get_config() purge_ = purge or config_.get('remove.purge', False) watcher.change_substatus(self._i18n['debian.simulate_operation']) transaction = self.aptitude.simulate_removal((pkg.name,), purge=purge_) if not transaction or not transaction.to_remove: return TransactionResult.fail() if pkg not in transaction.to_remove: watcher.show_message(title=self._i18n['popup.title.error'], body=self._i18n['debian.remove.impossible'].format(pkg=bold(pkg.name)), type_=MessageType.ERROR) return TransactionResult.fail() watcher.change_substatus('') deps = tuple(p for p in transaction.to_remove if p.name != pkg.name) if deps: # updates are required to be filled in case the dependencies are currently displayed on the view updates = dict() fill_updates = Thread(target=self._fill_updates, args=(updates,)) fill_updates.start() deps_data = self.aptitude.show((p.name for p in deps), attrs=('description', 'maintainer', 'section')) if deps_data: for p in deps: fill_show_data(p, deps_data.get(p.name)) if not self.view.confirm_removal(source_pkg=pkg.name, dependencies=deps, watcher=watcher): return TransactionResult.fail() fill_updates.join() if updates: for p in deps: latest_version = updates.get(p.name) if latest_version is not None and p.version != latest_version: p.latest_version = latest_version p.update = True watcher.change_substatus(self._i18n['debian.uninstall.removing']) handler = ProcessHandler(watcher) to_remove = tuple(p.name for p in transaction.to_remove) with self.output_handler.start(watcher=watcher, targets=to_remove, action=AptitudeAction.REMOVE) as handle: removed, _ = handler.handle_simple(self.aptitude.remove(packages=to_remove, root_password=root_password, purge=purge_), output_handler=handle) if not removed: return TransactionResult.fail() watcher.change_substatus(self._i18n['debian.uninstall.validating']) current_installed_names = set(self.aptitude.read_installed_names()) watcher.change_substatus('') all_removed, apps_removed, not_removed_names = [], set(), set() for p in transaction.to_remove: if p.name not in current_installed_names: instance = p if p != pkg else pkg all_removed.append(instance) if instance.app: apps_removed.add(instance.app) instance.installed = False instance.version = instance.latest_version instance.update = False instance.bind_app(None) else: not_removed_names.add(p.name) if apps_removed: # updating apps index watcher.print(self._i18n['debian.app_index.updating'] + ' ...') watcher.change_substatus(self._i18n['debian.app_index.updating']) indexed_apps = set(self.app_indexer.read_index()) if indexed_apps: new_index = indexed_apps.difference(apps_removed) try: self.app_indexer.update_index(new_index, update_timestamp=False) self._update_apps_index(new_index) self._log.info(f"Debian applications removed from the index: " f"{', '.join((a.name for a in apps_removed))}") except ApplicationIndexError: pass watcher.change_substatus('') success = True if not_removed_names: success = pkg.name not in not_removed_names not_removed_str = ', '.join((bold(p) for p in sorted(not_removed_names))) watcher.show_message(title=self._i18n[f"popup.title.{'warning' if success else 'error'}"], body=self._i18n['debian.uninstall.failed_to_remove'].format(no=len(not_removed_names), pkgs=not_removed_str), type_=MessageType.WARNING if success else MessageType.ERROR) return TransactionResult(success=success, installed=None, removed=all_removed)
def _install(self, pkg: AppImage, watcher: ProcessWatcher, pre_downloaded_file: Optional[Tuple[str, str]] = None) \ -> TransactionResult: handler = ProcessHandler(watcher) out_dir = f'{INSTALLATION_DIR}/{pkg.get_clean_name()}' counter = 0 while True: if os.path.exists(out_dir): self.logger.info(f"Installation dir '{out_dir}' already exists. Generating a different one") out_dir += f'-{counter}' counter += 1 else: break Path(out_dir).mkdir(parents=True, exist_ok=True) pkg.install_dir = out_dir if pkg.imported: downloaded, file_name = True, pkg.local_file_path.split('/')[-1] install_file_path = out_dir + '/' + file_name try: moved, output = handler.handle_simple(SimpleProcess(['mv', pkg.local_file_path, install_file_path])) except: output = '' self.logger.error(f"Could not rename file '{pkg.local_file_path}' as '{install_file_path}'") moved = False if not moved: watcher.show_message(title=self.i18n['error'].capitalize(), body=self.i18n['appimage.install.imported.rename_error'].format(bold(pkg.local_file_path.split('/')[-1]), bold(output)), type_=MessageType.ERROR) return TransactionResult.fail() else: download_data = pre_downloaded_file if pre_downloaded_file else self._download(pkg, watcher) if not download_data: return TransactionResult.fail() file_name, download_path = download_data[0], download_data[1] install_file_path = f'{out_dir}/{file_name}' try: shutil.move(download_path, install_file_path) except OSError: watcher.show_message(title=self.i18n['error'], body=self.i18n['error.mvfile'].formmat(src=bold(download_path), dest=bold(install_file_path))) return TransactionResult.fail() watcher.change_substatus(self.i18n['appimage.install.permission'].format(bold(file_name))) permission_given = handler.handle(SystemProcess(new_subprocess(['chmod', 'a+x', install_file_path]))) if permission_given: watcher.change_substatus(self.i18n['appimage.install.extract'].format(bold(file_name))) try: res, output = handler.handle_simple( SimpleProcess([install_file_path, '--appimage-extract'], cwd=out_dir)) if 'Error: Failed to register AppImage in AppImageLauncherFS' in output: watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.install.appimagelauncher.error'].format( appimgl=bold('AppImageLauncher'), app=bold(pkg.name)), type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() except: watcher.show_message(title=self.i18n['error'], body=traceback.format_exc(), type_=MessageType.ERROR) traceback.print_exc() handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() watcher.change_substatus(self.i18n['appimage.install.desktop_entry']) extracted_folder = f'{out_dir}/squashfs-root' if os.path.exists(extracted_folder): desktop_entry = self._find_desktop_file(extracted_folder) with open(f'{extracted_folder}/{desktop_entry}') as f: de_content = f.read() if de_content: de_content = replace_desktop_entry_exec_command(desktop_entry=de_content, appname=pkg.name, file_path=install_file_path) extracted_icon = self._find_icon_file(extracted_folder) if extracted_icon: icon_path = out_dir + '/logo.' + extracted_icon.split('/')[-1].split('.')[-1] shutil.copy(extracted_icon, icon_path) if de_content: de_content = RE_DESKTOP_ICON.sub(f'Icon={icon_path}\n', de_content) pkg.icon_path = icon_path if not de_content: de_content = pkg.to_desktop_entry() Path(DESKTOP_ENTRIES_DIR).mkdir(parents=True, exist_ok=True) with open(self._gen_desktop_entry_path(pkg), 'w+') as f: f.write(de_content) try: shutil.rmtree(extracted_folder) except: traceback.print_exc() SymlinksVerifier.create_symlink(app=pkg, file_path=install_file_path, logger=self.logger, watcher=watcher) return TransactionResult(success=True, installed=[pkg], removed=[]) else: watcher.show_message(title=self.i18n['error'], body=f'Could extract content from {bold(file_name)}', type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail()
def install(self, pkg: AppImage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher) -> TransactionResult: handler = ProcessHandler(watcher) out_dir = INSTALLATION_PATH + pkg.get_clean_name() counter = 0 while True: if os.path.exists(out_dir): self.logger.info( "Installation dir '{}' already exists. Generating a different one" .format(out_dir)) out_dir += '-{}'.format(counter) counter += 1 else: break Path(out_dir).mkdir(parents=True, exist_ok=True) pkg.install_dir = out_dir if pkg.imported: downloaded, file_name = True, pkg.local_file_path.split('/')[-1] file_path = out_dir + '/' + file_name try: moved, output = handler.handle_simple( SimpleProcess(['mv', pkg.local_file_path, file_path])) except: self.logger.error("Could not rename file '' as '{}'".format( pkg.local_file_path, file_path)) moved = False if not moved: watcher.show_message( title=self.i18n['error'].capitalize(), body=self.i18n['appimage.install.imported.rename_error']. format(bold(pkg.local_file_path.split('/')[-1]), bold(output)), type_=MessageType.ERROR) return TransactionResult.fail() else: appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download file_name = appimage_url.split('/')[-1] pkg.version = pkg.latest_version pkg.url_download = appimage_url file_path = out_dir + '/' + file_name downloaded = self.file_downloader.download( file_url=pkg.url_download, watcher=watcher, output_path=file_path, cwd=str(Path.home())) if downloaded: watcher.change_substatus( self.i18n['appimage.install.permission'].format( bold(file_name))) permission_given = handler.handle( SystemProcess(new_subprocess(['chmod', 'a+x', file_path]))) if permission_given: watcher.change_substatus( self.i18n['appimage.install.extract'].format( bold(file_name))) try: res, output = handler.handle_simple( SimpleProcess([file_path, '--appimage-extract'], cwd=out_dir)) if 'Error: Failed to register AppImage in AppImageLauncherFS' in output: watcher.show_message( title=self.i18n['error'], body=self. i18n['appimage.install.appimagelauncher.error']. format(appimgl=bold('AppImageLauncher'), app=bold(pkg.name)), type_=MessageType.ERROR) handler.handle( SystemProcess( new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() except: watcher.show_message(title=self.i18n['error'], body=traceback.format_exc(), type_=MessageType.ERROR) traceback.print_exc() handler.handle( SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() watcher.change_substatus( self.i18n['appimage.install.desktop_entry']) extracted_folder = '{}/{}'.format(out_dir, 'squashfs-root') if os.path.exists(extracted_folder): desktop_entry = self._find_desktop_file(extracted_folder) with open('{}/{}'.format(extracted_folder, desktop_entry)) as f: de_content = f.read() de_content = replace_desktop_entry_exec_command( desktop_entry=de_content, appname=pkg.name, file_path=file_path) extracted_icon = self._find_icon_file(extracted_folder) if extracted_icon: icon_path = out_dir + '/logo.' + extracted_icon.split( '/')[-1].split('.')[-1] shutil.copy(extracted_icon, icon_path) de_content = RE_DESKTOP_ICON.sub( 'Icon={}\n'.format(icon_path), de_content) pkg.icon_path = icon_path Path(DESKTOP_ENTRIES_PATH).mkdir(parents=True, exist_ok=True) with open(self._gen_desktop_entry_path(pkg), 'w+') as f: f.write(de_content) try: shutil.rmtree(extracted_folder) except: traceback.print_exc() SymlinksVerifier.create_symlink(app=pkg, file_path=file_path, logger=self.logger, watcher=watcher) return TransactionResult(success=True, installed=[pkg], removed=[]) else: watcher.show_message( title=self.i18n['error'], body='Could extract content from {}'.format( bold(file_name)), type_=MessageType.ERROR) else: watcher.show_message( title=self.i18n['error'], body=self.i18n['appimage.install.download.error'].format( bold(pkg.url_download)), type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail()
def install(self, pkg: SnapApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult: # retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds if not snap.is_installed(): watcher.print("'snap' seems not to be installed") return TransactionResult.fail() if not snapd.is_running(): watcher.print("'snapd' seems not to be running") return TransactionResult.fail() installed_names = { s['name'] for s in SnapdClient(self.logger).list_all_snaps() } client = SnapdClient(self.logger) snap_config = read_config() try: channel = self._request_channel_installation( pkg=pkg, snap_config=snap_config, snapd_client=client, watcher=watcher) pkg.channel = channel except: watcher.print('Aborted by user') return TransactionResult.fail() res, output = ProcessHandler(watcher).handle_simple( snap.install_and_stream(app_name=pkg.name, confinement=pkg.confinement, root_password=root_password, channel=channel)) if 'error:' in output: res = False if 'not available on stable' in output: channels = RE_AVAILABLE_CHANNELS.findall(output) if channels: opts = [ InputOption(label=c[0], value=c[1]) for c in channels ] channel_select = SingleSelectComponent( type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0]) body = '<p>{}.</p>'.format( self.i18n['snap.install.available_channels.message']. format(bold(self.i18n['stable']), bold(pkg.name))) body += '<p>{}:</p>'.format( self.i18n['snap.install.available_channels.help']) if watcher.request_confirmation( title=self. i18n['snap.install.available_channels.title'], body=body, components=[channel_select], confirmation_label=self.i18n['continue'], deny_label=self.i18n['cancel']): self.logger.info( "Installing '{}' with the custom command '{}'". format(pkg.name, channel_select.value)) res = ProcessHandler(watcher).handle( SystemProcess( new_root_subprocess( channel_select.value.value.split(' '), root_password=root_password))) return self._gen_installation_response( success=res, pkg=pkg, installed=installed_names, disk_loader=disk_loader) else: self.logger.error( "Could not find available channels in the installation output: {}" .format(output)) return self._gen_installation_response(success=res, pkg=pkg, installed=installed_names, disk_loader=disk_loader)