# SPDX-License-Identifier: GPL-3.0-or-later # Haketilo proxy data and configuration (instantiatable HaketiloState subtype). # # This file is part of Hydrilla&Haketilo. # # Copyright (C) 2022 Wojtek Kosior # # 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 . # # # I, Wojtek Kosior, thereby promise not to sue for violation of this # file's license. Although I request that you do not make use this code # in a proprietary program, I am not going to enforce this in court. """ This module contains logic for keeping track of all settings, rules, mappings and resources. """ # Enable using with Python 3.7. from __future__ import annotations import sqlite3 import typing as t import dataclasses as dc from pathlib import Path from ...exceptions import HaketiloException from ...translations import smart_gettext as _ from ... import url_patterns from ... import item_infos from .. import state as st from .. import policies from .. import simple_dependency_satisfying as sds from . import base from . import mappings from . import repos from . import _operations here = Path(__file__).resolve().parent @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteRepoIterationRef(st.RepoIterationRef): pass @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteResourceRef(st.ResourceRef): pass @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteResourceVersionRef(st.ResourceVersionRef): pass @dc.dataclass class ConcreteHaketiloState(base.HaketiloStateWithFields): def __post_init__(self) -> None: sqlite3.enable_callback_tracebacks(True) self._prepare_database() self.rebuild_structures() def _prepare_database(self) -> None: """....""" cursor = self.connection.cursor() try: cursor.execute( ''' SELECT COUNT(name) FROM sqlite_master WHERE name = 'general' AND type = 'table'; ''' ) (db_initialized,), = cursor.fetchall() if not db_initialized: cursor.executescript((here.parent / 'tables.sql').read_text()) else: cursor.execute( ''' SELECT haketilo_version FROM general; ''' ) (db_haketilo_version,) = cursor.fetchone() if db_haketilo_version != '3.0b1': raise HaketiloException(_('err.proxy.unknown_db_schema')) cursor.execute('PRAGMA FOREIGN_KEYS;') if cursor.fetchall() == []: raise HaketiloException(_('err.proxy.no_sqlite_foreign_keys')) cursor.execute('PRAGMA FOREIGN_KEYS=ON;') finally: cursor.close() def import_packages(self, malcontent_path: Path) -> None: with self.cursor(transaction=True) as cursor: _operations._load_packages_no_state_update( cursor = cursor, malcontent_path = malcontent_path, repo_id = 1 ) self.rebuild_structures() def recompute_dependencies( self, extra_requirements: t.Iterable[sds.MappingRequirement] = [] ) -> None: with self.cursor() as cursor: assert self.connection.in_transaction _operations._recompute_dependencies_no_state_update( cursor = cursor, extra_requirements = extra_requirements ) self.rebuild_structures() def repo_store(self) -> st.RepoStore: return repos.ConcreteRepoStore(self) def get_repo_iteration(self, repo_iteration_id: str) -> st.RepoIterationRef: return ConcreteRepoIterationRef(repo_iteration_id) def mapping_store(self) -> st.MappingStore: return mappings.ConcreteMappingStore(self) def mapping_version_store(self) -> st.MappingVersionStore: return mappings.ConcreteMappingVersionStore(self) def get_resource(self, resource_id: str) -> st.ResourceRef: return ConcreteResourceRef(resource_id) def get_resource_version(self, resource_version_id: str) \ -> st.ResourceVersionRef: return ConcreteResourceVersionRef(resource_version_id) def get_payload(self, payload_id: str) -> st.PayloadRef: raise NotImplementedError() def get_settings(self) -> st.HaketiloGlobalSettings: return st.HaketiloGlobalSettings( mapping_use_mode = st.MappingUseMode.AUTO, default_allow_scripts = True, repo_refresh_seconds = 0 ) def update_settings( self, *, mapping_use_mode: t.Optional[st.MappingUseMode] = None, default_allow_scripts: t.Optional[bool] = None, repo_refresh_seconds: t.Optional[int] = None ) -> None: raise NotImplementedError() def select_policy(self, url: url_patterns.ParsedUrl) -> policies.Policy: """....""" with self.lock: policy_tree = self.policy_tree try: best_priority: int = 0 best_policy: t.Optional[policies.Policy] = None for factories_set in policy_tree.search(url): for stored_factory in sorted(factories_set): factory = stored_factory.item policy = factory.make_policy(self) if policy.priority > best_priority: best_priority = policy.priority best_policy = policy except Exception as e: return policies.ErrorBlockPolicy( builtin = True, error = e ) if best_policy is not None: return best_policy if self.get_settings().default_allow_scripts: return policies.FallbackAllowPolicy() else: return policies.FallbackBlockPolicy() @staticmethod def make(store_dir: Path) -> 'ConcreteHaketiloState': connection = sqlite3.connect( str(store_dir / 'sqlite3.db'), isolation_level = None, check_same_thread = False ) return ConcreteHaketiloState( store_dir = store_dir, connection = connection )