Exemplo n.º 1
0
    def download_package_signature(self, pkg: dict, file_url: str,
                                   output_path: str, root_password: str,
                                   watcher: ProcessWatcher):
        try:
            self.logger.info("Downloading package '{}' signature".format(
                pkg['n']))

            sig_downloaded = self.downloader.download(
                file_url=file_url + '.sig',
                watcher=None,
                output_path=output_path + '.sig',
                cwd='.',
                root_password=root_password,
                display_file_size=False,
                max_threads=1)

            if not sig_downloaded:
                msg = "Could not download package '{}' signature".format(
                    pkg['n'])
                self.logger.warning(msg)
                watcher.print('[warning] {}'.format(msg))
            else:
                msg = "Package '{}' signature successfully downloaded".format(
                    pkg['n'])
                self.logger.info(msg)
                watcher.print(msg)
        except:
            self.logger.warning(
                "An error occurred while download package '{}' signature".
                format(pkg['n']))
            traceback.print_exc()
Exemplo n.º 2
0
    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])
Exemplo n.º 3
0
    def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool:

        config = read_config()

        install_level = 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 False

            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:
                user_password, valid = watcher.request_root_password()
                if not valid:
                    watcher.print('Operation aborted')
                    return False
                else:
                    if not handler.handle_simple(flatpak.set_default_remotes('system', user_password)):
                        watcher.show_message(title=self.i18n['error'].capitalize(),
                                             body=self.i18n['flatpak.remotes.system_flathub.error'],
                                             type_=MessageType.ERROR)
                        watcher.print("Operation cancelled")
                        return False

        res = handler.handle(SystemProcess(subproc=flatpak.install(str(pkg.id), pkg.origin, pkg.installation), wrong_error_phrase='Warning'))

        if res:
            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()

        return res
Exemplo n.º 4
0
    def _pre_download_source(self, pkgname: str, project_dir: str,
                             watcher: ProcessWatcher) -> bool:
        if self.context.file_downloader.is_multithreaded():
            srcinfo = self.aur_client.get_src_info(pkgname)

            pre_download_files = []

            for attr in SOURCE_FIELDS:
                if srcinfo.get(attr):
                    if attr == 'source_x86_x64' and not self.context.is_system_x86_64(
                    ):
                        continue
                    else:
                        for f in srcinfo[attr]:
                            if RE_PRE_DOWNLOADABLE_FILES.findall(f):
                                pre_download_files.append(f)

            if pre_download_files:
                downloader = self.context.file_downloader.get_default_client_name(
                )

                for f in pre_download_files:
                    fdata = f.split('::')

                    args = {'watcher': watcher, 'cwd': project_dir}
                    if len(fdata) > 1:
                        args.update({
                            'file_url': fdata[1],
                            'output_path': fdata[0]
                        })
                    else:
                        args.update({
                            'file_url': fdata[0],
                            'output_path': None
                        })

                    file_size = self.context.http_client.get_content_length(
                        args['file_url'])
                    file_size = int(file_size) / (1024**
                                                  2) if file_size else None

                    watcher.change_substatus(
                        bold('[{}] ').format(downloader) +
                        self.i18n['downloading'] + ' ' +
                        bold(args['file_url'].split('/')[-1]) +
                        ' ( {0:.2f} Mb )'.format(file_size)
                        if file_size else '')
                    if not self.context.file_downloader.download(**args):
                        watcher.print(
                            'Could not download source file {}'.format(
                                args['file_url']))
                        return False

        return True
Exemplo n.º 5
0
    def _make_exports_dir(self, watcher: ProcessWatcher) -> bool:
        if not os.path.exists(EXPORTS_PATH):
            self.logger.info("Creating dir '{}'".format(EXPORTS_PATH))
            watcher.print('Creating dir {}'.format(EXPORTS_PATH))
            try:
                Path(EXPORTS_PATH).mkdir(parents=True, exist_ok=True)
            except:
                watcher.print('Error while creating the directory {}'.format(EXPORTS_PATH))
                return False

        return True
Exemplo n.º 6
0
    def downgrade(self, pkg: SnapApplication, root_password: str,
                  watcher: ProcessWatcher) -> bool:
        if not snap.is_installed():
            watcher.print("'snap' seems not to be installed")
            return False
        if not snapd.is_running():
            watcher.print("'snapd' seems not to be running")
            return False

        return ProcessHandler(watcher).handle_simple(
            snap.downgrade_and_stream(pkg.name, root_password))[0]
Exemplo n.º 7
0
    def download_package(self, pkg: Dict[str, str], root_password: str, substatus_prefix: str, watcher: ProcessWatcher, size: int) -> bool:
        if self.mirrors and self.branch:
            pkgname = '{}-{}{}.pkg'.format(pkg['n'], pkg['v'], ('-{}'.format(pkg['a']) if pkg['a'] else ''))

            if {f for f in glob.glob(self.cache_dir + '/*') if f.split('/')[-1].startswith(pkgname)}:
                watcher.print("{} ({}) file found o cache dir {}. Skipping download.".format(pkg['n'], pkg['v'], self.cache_dir))
                return True

            arch = pkg['a'] if pkg.get('a') and pkg['a'] != 'any' else 'x86_64'

            url_base = '{}/{}/{}/{}'.format(self.branch, pkg['r'], arch, pkgname)
            base_output_path = '{}/{}'.format(self.cache_dir, pkgname)

            for mirror in self.mirrors:
                for ext in self.extensions:
                    url = '{}{}{}'.format(mirror, url_base, ext)
                    output_path = base_output_path + ext

                    watcher.print("Downloading '{}' from mirror '{}'".format(pkgname, mirror))

                    pkg_downloaded = self.downloader.download(file_url=url, watcher=watcher, output_path=output_path,
                                                              cwd='.', root_password=root_password, display_file_size=True,
                                                              substatus_prefix=substatus_prefix,
                                                              known_size=size)
                    if not pkg_downloaded:
                        watcher.print("Could not download '{}' from mirror '{}'".format(pkgname, mirror))
                    else:
                        self.logger.info("Package '{}' successfully downloaded".format(pkg['n']))
                        t = Thread(target=self.download_package_signature, args=(pkg, url, output_path, root_password, watcher), daemon=True)
                        t.start()
                        self.async_downloads_lock.acquire()
                        self.async_downloads.append(t)
                        self.async_downloads_lock.release()
                        return True
        return False
Exemplo n.º 8
0
    def _pre_download_source(self, pkgname: str, project_dir: str,
                             watcher: ProcessWatcher) -> bool:
        if self.context.file_downloader.is_multithreaded():
            srcinfo = self.aur_client.get_src_info(pkgname)

            pre_download_files = []

            for attr in SOURCE_FIELDS:
                if srcinfo.get(attr):
                    if attr == 'source_x86_x64' and not self.context.is_system_x86_64(
                    ):
                        continue
                    else:
                        for f in srcinfo[attr]:
                            if RE_PRE_DOWNLOADABLE_FILES.findall(f):
                                pre_download_files.append(f)

            if pre_download_files:
                for f in pre_download_files:
                    fdata = f.split('::')

                    args = {'watcher': watcher, 'cwd': project_dir}
                    if len(fdata) > 1:
                        args.update({
                            'file_url': fdata[1],
                            'output_path': fdata[0]
                        })
                    else:
                        args.update({
                            'file_url': fdata[0],
                            'output_path': None
                        })

                    if not self.context.file_downloader.download(**args):
                        watcher.print(
                            'Could not download source file {}'.format(
                                args['file_url']))
                        return False

        return True
Exemplo n.º 9
0
    def _refresh_apps_index(self, watcher: ProcessWatcher):
        watcher.change_substatus(self._i18n['debian.app_index.checking'])
        self._log.info("Reading the cached Debian applications")
        indexed_apps = self.app_indexer.read_index()
        self._log.info("Mapping the Debian applications")
        current_apps = self.app_mapper.map_executable_applications()

        if current_apps != indexed_apps:
            watcher.print(self._i18n['debian.app_index.updating'] + '...')
            watcher.change_substatus(self._i18n['debian.app_index.updating'])

            try:
                self.app_indexer.update_index(current_apps)
                self._update_apps_index(current_apps)

                if indexed_apps is not None:
                    new_apps = current_apps.difference(indexed_apps)

                    if new_apps:
                        self._log.info(f"Debian applications added to the index: "
                                       f"{','.join((a.name for a in new_apps))}")

            except ApplicationIndexError:
                pass
Exemplo n.º 10
0
    def create_symlink(app: AppImage, file_path: str, logger: logging.Logger, watcher: ProcessWatcher = None):
        logger.info("Creating a symlink for '{}'".format(app.name))
        possible_names = (app.name.lower(), '{}-appimage'.format(app.name.lower()))

        if os.path.exists(SYMLINKS_DIR) and not os.path.isdir(SYMLINKS_DIR):
            logger.warning("'{}' is not a directory. It will not be possible to create a symlink for '{}'".format(SYMLINKS_DIR, app.name))
            return

        available_system_dirs = (SYMLINKS_DIR, *(l for l in ('/usr/bin', '/usr/local/bin') if os.path.isdir(l)))

        # checking if the link already exists:

        available_name = None
        for name in possible_names:
            available_name = name
            for sysdir in available_system_dirs:
                if os.path.exists('{}/{}'.format(sysdir, name)):
                    available_name = None
                    break

            if available_name:
                break

        if not available_name:
            msg = "It was not possible to create a symlink for '{}' because the names {} are already available on the system".format(app.name,
                                                                                                                                     possible_names)
            logger.warning(msg)
            if watcher:
                watcher.print('[warning] {}'.format(msg))
        else:
            try:
                Path(SYMLINKS_DIR).mkdir(parents=True, exist_ok=True)
            except:
                logger.error("Could not create symlink directory '{}'".format(SYMLINKS_DIR))
                return

            symlink_path = '{}/{}'.format(SYMLINKS_DIR, available_name)

            try:
                os.symlink(src=file_path, dst=symlink_path)
                app.symlink = symlink_path

                msg = "symlink successfully created at {}".format(symlink_path)
                logger.info(msg)

                if watcher:
                    watcher.print(msg)
            except:
                msg = "Could not create the symlink '{}'".format(symlink_path)
                logger.error(msg)

                if watcher:
                    watcher.print('[error] {}'.format(msg))
Exemplo n.º 11
0
    def install(self, pkg: WebApplication, root_password: str,
                watcher: ProcessWatcher) -> bool:

        continue_install, install_options = self._ask_install_options(
            pkg, watcher)

        if not continue_install:
            watcher.print("Installation aborted by the user")
            return False

        watcher.change_substatus(self.i18n['web.env.checking'])
        handler = ProcessHandler(watcher)

        env_settings = self.env_updater.read_settings()
        local_config = read_config()

        if local_config['environment'][
                'system'] and not nativefier.is_available():
            watcher.show_message(
                title=self.i18n['error'].capitalize(),
                body=self.i18n['web.install.global_nativefier.unavailable'].
                format(n=bold('Nativefier'), app=bold(pkg.name)) + '.',
                type_=MessageType.ERROR)
            return False

        env_components = self.env_updater.check_environment(
            app=pkg,
            local_config=local_config,
            env=env_settings,
            is_x86_x64_arch=self.context.is_system_x86_64())

        comps_to_update = [c for c in env_components if c.update]

        if comps_to_update and not self._ask_update_permission(
                comps_to_update, watcher):
            return False

        if not self.env_updater.update(components=comps_to_update,
                                       handler=handler):
            watcher.show_message(title=self.i18n['error'],
                                 body=self.i18n['web.env.error'].format(
                                     bold(pkg.name)),
                                 type_=MessageType.ERROR)
            return False

        Path(INSTALLED_PATH).mkdir(parents=True, exist_ok=True)

        app_id, treated_name = self._gen_app_id(pkg.name)
        pkg.id = app_id
        app_dir = '{}/{}'.format(INSTALLED_PATH, app_id)

        watcher.change_substatus(
            self.i18n['web.install.substatus.checking_fixes'])
        fix = self._get_fix_for(
            url_no_protocol=self._strip_url_protocol(pkg.url))
        fix_path = '{}/fix.js'.format(app_dir)

        if fix:
            # just adding the fix as an installation option. The file will be written later
            self.logger.info('Fix found for {}'.format(pkg.url))
            watcher.print('Fix found for {}'.format(pkg.url))
            install_options.append('--inject={}'.format(fix_path))

        # if a custom icon is defined for an app suggestion:
        icon_path, icon_bytes = None, None
        if pkg.icon_url and pkg.save_icon and not {
                o
                for o in install_options if o.startswith('--icon')
        }:
            download = self._download_suggestion_icon(pkg, app_dir)

            if download and download[1]:
                icon_path, icon_bytes = download[0], download[1]
                pkg.custom_icon = icon_path

                # writting the icon in a temporary folder to be used by the nativefier process
                temp_icon_path = '{}/{}'.format(TEMP_PATH,
                                                pkg.icon_url.split('/')[-1])
                install_options.append('--icon={}'.format(temp_icon_path))

                self.logger.info("Writing a temp suggestion icon at {}".format(
                    temp_icon_path))
                with open(temp_icon_path, 'wb+') as f:
                    f.write(icon_bytes)

        watcher.change_substatus(
            self.i18n['web.install.substatus.call_nativefier'].format(
                bold('nativefier')))

        electron_version = str(
            next((c for c in env_components if c.id == 'electron')).version)
        installed = handler.handle_simple(
            nativefier.install(url=pkg.url,
                               name=app_id,
                               output_dir=app_dir,
                               electron_version=electron_version,
                               system=bool(
                                   local_config['environment']['system']),
                               cwd=INSTALLED_PATH,
                               extra_options=install_options))

        if not installed:
            msg = '{}.{}.'.format(
                self.i18n['wen.install.error'].format(bold(pkg.name)),
                self.i18n['web.install.nativefier.error.unknown'].format(
                    bold(self.i18n['details'].capitalize())))
            watcher.show_message(title=self.i18n['error'],
                                 body=msg,
                                 type_=MessageType.ERROR)
            return False

        inner_dir = os.listdir(app_dir)

        if not inner_dir:
            msg = '{}.{}.'.format(
                self.i18n['wen.install.error'].format(bold(pkg.name)),
                self.i18n['web.install.nativefier.error.inner_dir'].format(
                    bold(app_dir)))
            watcher.show_message(title=self.i18n['error'],
                                 body=msg,
                                 type_=MessageType.ERROR)
            return False

        # bringing the inner app folder to the 'installed' folder level:
        inner_dir = '{}/{}'.format(app_dir, inner_dir[0])
        temp_dir = '{}/tmp_{}'.format(INSTALLED_PATH, treated_name)
        os.rename(inner_dir, temp_dir)
        shutil.rmtree(app_dir)
        os.rename(temp_dir, app_dir)

        # injecting a fix
        if fix:
            self.logger.info('Writting JS fix at {}'.format(fix_path))
            with open(fix_path, 'w+') as f:
                f.write(fix)

        # persisting the custom suggestion icon in the defitive directory
        if icon_bytes:
            self.logger.info(
                "Writting the final custom suggestion icon at {}".format(
                    icon_path))
            with open(icon_path, 'wb+') as f:
                f.write(icon_bytes)

        pkg.installation_dir = app_dir

        version_path = '{}/version'.format(app_dir)

        if os.path.exists(version_path):
            with open(version_path, 'r') as f:
                pkg.version = f.read().strip()
                pkg.latest_version = pkg.version

        watcher.change_substatus(self.i18n['web.install.substatus.shortcut'])

        desktop_entry_path = self._gen_desktop_entry_path(app_id)

        entry_content = self._gen_desktop_entry_content(pkg)

        Path(DESKTOP_ENTRIES_DIR).mkdir(parents=True, exist_ok=True)

        with open(desktop_entry_path, 'w+') as f:
            f.write(entry_content)

        pkg.desktop_entry = desktop_entry_path

        if '--tray=start-in-tray' in install_options:
            autostart_dir = '{}/.config/autostart'.format(Path.home())
            Path(autostart_dir).mkdir(parents=True, exist_ok=True)

            with open(pkg.get_autostart_path(), 'w+') as f:
                f.write(entry_content)

        if install_options:
            pkg.options_set = install_options

        return True
Exemplo n.º 12
0
    def downgrade(self, pkg: ArchPackage, root_password: str,
                  watcher: ProcessWatcher) -> bool:

        handler = ProcessHandler(watcher)
        app_build_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time()))
        watcher.change_progress(5)

        try:
            if not os.path.exists(app_build_dir):
                build_dir = handler.handle(
                    SystemProcess(
                        new_subprocess(['mkdir', '-p', app_build_dir])))

                if build_dir:
                    watcher.change_progress(10)
                    watcher.change_substatus(self.i18n['arch.clone'].format(
                        bold(pkg.name)))
                    clone = handler.handle(
                        SystemProcess(subproc=new_subprocess(
                            ['git', 'clone',
                             URL_GIT.format(pkg.name)],
                            cwd=app_build_dir),
                                      check_error_output=False))
                    watcher.change_progress(30)
                    if clone:
                        watcher.change_substatus(
                            self.i18n['arch.downgrade.reading_commits'])
                        clone_path = '{}/{}'.format(app_build_dir, pkg.name)
                        pkgbuild_path = '{}/PKGBUILD'.format(clone_path)

                        commits = run_cmd("git log", cwd=clone_path)
                        watcher.change_progress(40)

                        if commits:
                            commit_list = re.findall(r'commit (.+)\n', commits)
                            if commit_list:
                                if len(commit_list) > 1:
                                    for idx in range(1, len(commit_list)):
                                        commit = commit_list[idx]
                                        with open(pkgbuild_path) as f:
                                            pkgdict = aur.map_pkgbuild(
                                                f.read())

                                        if not handler.handle(
                                                SystemProcess(
                                                    subproc=new_subprocess(
                                                        [
                                                            'git', 'reset',
                                                            '--hard', commit
                                                        ],
                                                        cwd=clone_path),
                                                    check_error_output=False)):
                                            watcher.print(
                                                'Could not downgrade anymore. Aborting...'
                                            )
                                            return False

                                        if '{}-{}'.format(
                                                pkgdict.get('pkgver'),
                                                pkgdict.get(
                                                    'pkgrel')) == pkg.version:
                                            # current version found
                                            watcher.change_substatus(self.i18n[
                                                'arch.downgrade.version_found']
                                                                     )
                                            break

                                    watcher.change_substatus(
                                        self.
                                        i18n['arch.downgrade.install_older'])
                                    return self._make_pkg(pkg.name,
                                                          pkg.maintainer,
                                                          root_password,
                                                          handler,
                                                          app_build_dir,
                                                          clone_path,
                                                          dependency=False,
                                                          skip_optdeps=True)
                                else:
                                    watcher.show_message(
                                        title=self.
                                        i18n['arch.downgrade.error'],
                                        body=self.
                                        i18n['arch.downgrade.impossible'].
                                        format(pkg.name),
                                        type_=MessageType.ERROR)
                                    return False

                        watcher.show_message(
                            title=self.i18n['error'],
                            body=self.i18n['arch.downgrade.no_commits'],
                            type_=MessageType.ERROR)
                        return False

        finally:
            if os.path.exists(app_build_dir):
                handler.handle(
                    SystemProcess(
                        subproc=new_subprocess(['rm', '-rf', app_build_dir])))

        return False
Exemplo n.º 13
0
    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()
Exemplo n.º 14
0
    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)
Exemplo n.º 15
0
    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)
Exemplo n.º 16
0
    def download(self,
                 file_url: str,
                 watcher: ProcessWatcher,
                 output_path: str = None,
                 cwd: str = None,
                 root_password: Optional[str] = None,
                 substatus_prefix: str = None,
                 display_file_size: bool = True,
                 max_threads: int = None,
                 known_size: int = None) -> bool:
        self.logger.info(f'Downloading {file_url}')
        handler = ProcessHandler(watcher)
        file_name = file_url.split('/')[-1]

        final_cwd = cwd if cwd else '.'

        success = False
        ti = time.time()
        try:
            if output_path:
                if os.path.exists(output_path):
                    self.logger.info(
                        f'Removing old file found before downloading: {output_path}'
                    )
                    os.remove(output_path)
                    self.logger.info(f'Old file {output_path} removed')
                else:
                    output_dir = os.path.dirname(output_path)

                    try:
                        Path(output_dir).mkdir(exist_ok=True, parents=True)
                    except OSError:
                        self.logger.error(
                            f"Could not make download directory '{output_dir}'"
                        )
                        watcher.print(
                            self.i18n['error.mkdir'].format(dir=output_dir))
                        return False

            client = self.get_available_multithreaded_tool()
            if client:
                threads = self._get_appropriate_threads_number(
                    max_threads, known_size)

                if client == 'aria2':
                    ti = time.time()
                    process = self._get_aria2c_process(file_url, output_path,
                                                       final_cwd,
                                                       root_password, threads)
                    downloader = 'aria2'
                else:
                    ti = time.time()
                    process = self._get_axel_process(file_url, output_path,
                                                     final_cwd, root_password,
                                                     threads)
                    downloader = 'axel'
            else:
                ti = time.time()
                process = self._get_wget_process(file_url, output_path,
                                                 final_cwd, root_password)
                downloader = 'wget'

            name = file_url.split('/')[-1]

            if output_path and not RE_HAS_EXTENSION.match(
                    name) and RE_HAS_EXTENSION.match(output_path):
                name = output_path.split('/')[-1]

            if watcher:
                msg = StringIO()
                msg.write(f'{substatus_prefix} ' if substatus_prefix else '')
                msg.write(
                    f"{bold('[{}]'.format(downloader))} {self.i18n['downloading']} {bold(name)}"
                )

                if display_file_size:
                    if known_size:
                        msg.write(f' ( {get_human_size_str(known_size)} )')
                        watcher.change_substatus(msg.getvalue())
                    else:
                        Thread(target=self._concat_file_size,
                               args=(file_url, msg, watcher)).start()
                else:
                    msg.write(' ( ? Mb )')
                    watcher.change_substatus(msg.getvalue())

            success, _ = handler.handle_simple(process)
        except:
            traceback.print_exc()
            self._rm_bad_file(file_name, output_path, final_cwd, handler,
                              root_password)

        tf = time.time()
        self.logger.info(
            f'{file_name} download took {(tf - ti) / 60:.2f} minutes')

        if not success:
            self.logger.error(f"Could not download '{file_name}'")
            self._rm_bad_file(file_name, output_path, final_cwd, handler,
                              root_password)

        return success