From addd00364d5053ca7850577982ccc75003a27c59 Mon Sep 17 00:00:00 2001 From: TheMajesticMagician Date: Sun, 25 Oct 2026 16:15:50 -0600 Subject: [PATCH] BoltClam 1.0.0: ClamAV desktop protection with Bolt sign-in Adds the PyQt6 Windows client (Homepage, Scan & results, About & updates, Settings), ClamAV scanning and quarantine, browser-download watch with an exclusive scan gate, Bolt loopback/PKCE sign-in with DPAPI key storage, Gitea release feed with checksum verification, opt-in diagnostics, and Windows/portable build tooling. Co-Authored-By: Claude Code --- .gitattributes | 5 + .gitea/workflows/windows-release.yml | 58 ++ .gitignore | 32 + BoltClam.spec | 79 ++ CHANGELOG.md | 23 + README.md | 199 ++++ THIRD_PARTY_NOTICES.md | 23 + build_windows.bat | 3 + build_windows.ps1 | 99 ++ docs/ARCHITECTURE.md | 107 ++ docs/SECURITY.md | 114 +++ installer/BoltClam.iss | 53 + packaging/BoltClam-launcher.exe | Bin 0 -> 176640 bytes packaging/README.md | 78 ++ packaging/build_portable_windows.py | 176 ++++ packaging/launcher.c | 79 ++ packaging/publish_release.ps1 | 85 ++ pyproject.toml | 27 + requirements-dev.txt | 3 + requirements.txt | 1 + resources/boltclam.ico | Bin 0 -> 44830 bytes resources/boltclam.png | Bin 0 -> 24611 bytes resources/check.png | Bin 0 -> 366 bytes resources/chevron_down.png | Bin 0 -> 285 bytes resources/fonts/AtkinsonHyperlegible-Bold.ttf | Bin 0 -> 55256 bytes .../fonts/AtkinsonHyperlegible-Regular.ttf | Bin 0 -> 54348 bytes resources/fonts/BricolageGrotesque.ttf | Bin 0 -> 408496 bytes resources/fonts/JetBrainsMono-Medium.ttf | Bin 0 -> 270316 bytes resources/fonts/JetBrainsMono-Regular.ttf | Bin 0 -> 270224 bytes resources/fonts/OFL-Atkinson.txt | 92 ++ resources/fonts/OFL-Bricolage.txt | 93 ++ resources/fonts/OFL-JetBrainsMono.txt | 93 ++ resources/radio.png | Bin 0 -> 212 bytes resources/version_info.txt | 28 + scripts/generate_assets.py | 113 +++ src/boltclam/__init__.py | 7 + src/boltclam/__main__.py | 4 + src/boltclam/api_client.py | 267 +++++ src/boltclam/app.py | 193 ++++ src/boltclam/auth.py | 448 +++++++++ src/boltclam/clamav.py | 380 ++++++++ src/boltclam/config.py | 222 +++++ src/boltclam/context.py | 72 ++ src/boltclam/logging_setup.py | 111 +++ src/boltclam/logs.py | 184 ++++ src/boltclam/models.py | 191 ++++ src/boltclam/paths.py | 284 ++++++ src/boltclam/quarantine.py | 240 +++++ src/boltclam/realtime.py | 580 +++++++++++ src/boltclam/scanner.py | 347 +++++++ src/boltclam/storage.py | 375 +++++++ src/boltclam/system_info.py | 448 +++++++++ src/boltclam/ui/__init__.py | 0 src/boltclam/ui/about_page.py | 585 +++++++++++ src/boltclam/ui/brand.py | 110 +++ src/boltclam/ui/components.py | 790 +++++++++++++++ src/boltclam/ui/home_page.py | 570 +++++++++++ src/boltclam/ui/login_dialog.py | 348 +++++++ src/boltclam/ui/main_window.py | 512 ++++++++++ src/boltclam/ui/scan_page.py | 912 ++++++++++++++++++ src/boltclam/ui/settings_page.py | 651 +++++++++++++ src/boltclam/ui/theme.py | 637 ++++++++++++ src/boltclam/ui/threads.py | 26 + src/boltclam/updates.py | 171 ++++ src/boltclam/winlock.py | 197 ++++ tests/__init__.py | 0 tests/conftest.py | 32 + tests/test_auth.py | 198 ++++ tests/test_clamav.py | 157 +++ tests/test_config.py | 70 ++ tests/test_logs.py | 79 ++ tests/test_quarantine.py | 100 ++ tests/test_realtime.py | 70 ++ tests/test_storage.py | 65 ++ tests/test_ui_smoke.py | 86 ++ tests/test_updates.py | 121 +++ 76 files changed, 12503 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitea/workflows/windows-release.yml create mode 100644 .gitignore create mode 100644 BoltClam.spec create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 THIRD_PARTY_NOTICES.md create mode 100644 build_windows.bat create mode 100644 build_windows.ps1 create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/SECURITY.md create mode 100644 installer/BoltClam.iss create mode 100755 packaging/BoltClam-launcher.exe create mode 100644 packaging/README.md create mode 100644 packaging/build_portable_windows.py create mode 100644 packaging/launcher.c create mode 100644 packaging/publish_release.ps1 create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt create mode 100644 requirements.txt create mode 100644 resources/boltclam.ico create mode 100644 resources/boltclam.png create mode 100644 resources/check.png create mode 100644 resources/chevron_down.png create mode 100644 resources/fonts/AtkinsonHyperlegible-Bold.ttf create mode 100644 resources/fonts/AtkinsonHyperlegible-Regular.ttf create mode 100644 resources/fonts/BricolageGrotesque.ttf create mode 100644 resources/fonts/JetBrainsMono-Medium.ttf create mode 100644 resources/fonts/JetBrainsMono-Regular.ttf create mode 100644 resources/fonts/OFL-Atkinson.txt create mode 100644 resources/fonts/OFL-Bricolage.txt create mode 100644 resources/fonts/OFL-JetBrainsMono.txt create mode 100644 resources/radio.png create mode 100644 resources/version_info.txt create mode 100644 scripts/generate_assets.py create mode 100644 src/boltclam/__init__.py create mode 100644 src/boltclam/__main__.py create mode 100644 src/boltclam/api_client.py create mode 100644 src/boltclam/app.py create mode 100644 src/boltclam/auth.py create mode 100644 src/boltclam/clamav.py create mode 100644 src/boltclam/config.py create mode 100644 src/boltclam/context.py create mode 100644 src/boltclam/logging_setup.py create mode 100644 src/boltclam/logs.py create mode 100644 src/boltclam/models.py create mode 100644 src/boltclam/paths.py create mode 100644 src/boltclam/quarantine.py create mode 100644 src/boltclam/realtime.py create mode 100644 src/boltclam/scanner.py create mode 100644 src/boltclam/storage.py create mode 100644 src/boltclam/system_info.py create mode 100644 src/boltclam/ui/__init__.py create mode 100644 src/boltclam/ui/about_page.py create mode 100644 src/boltclam/ui/brand.py create mode 100644 src/boltclam/ui/components.py create mode 100644 src/boltclam/ui/home_page.py create mode 100644 src/boltclam/ui/login_dialog.py create mode 100644 src/boltclam/ui/main_window.py create mode 100644 src/boltclam/ui/scan_page.py create mode 100644 src/boltclam/ui/settings_page.py create mode 100644 src/boltclam/ui/theme.py create mode 100644 src/boltclam/ui/threads.py create mode 100644 src/boltclam/updates.py create mode 100644 src/boltclam/winlock.py create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_auth.py create mode 100644 tests/test_clamav.py create mode 100644 tests/test_config.py create mode 100644 tests/test_logs.py create mode 100644 tests/test_quarantine.py create mode 100644 tests/test_realtime.py create mode 100644 tests/test_storage.py create mode 100644 tests/test_ui_smoke.py create mode 100644 tests/test_updates.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a9e147d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +*.exe binary +*.ico binary +*.png binary +*.ttf binary +*.zip binary diff --git a/.gitea/workflows/windows-release.yml b/.gitea/workflows/windows-release.yml new file mode 100644 index 0000000..e951d97 --- /dev/null +++ b/.gitea/workflows/windows-release.yml @@ -0,0 +1,58 @@ +name: Windows release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag: + description: "Release tag (for a manual run, e.g. v1.0.0)" + required: false + +jobs: + build-windows: + # Gitea Actions needs a runner with these labels. The stock Gitea + # act_runner is Linux-only; register this repository on a Windows runner, + # or run build_windows.ps1 on a Windows PC and upload the release manually. + runs-on: windows-latest + steps: + - name: Check out + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + architecture: x64 + + - name: Install Inno Setup when available + shell: pwsh + continue-on-error: true + run: | + if (Get-Command choco -ErrorAction SilentlyContinue) { + choco install innosetup -y --no-progress + } + + - name: Build + shell: pwsh + run: ./build_windows.ps1 + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: BoltClam-windows + path: | + dist/*.zip + dist/*.exe + dist/SHA256SUMS.txt + if-no-files-found: error + + - name: Publish Gitea release + if: startsWith(github.ref, 'refs/tags/v') + shell: pwsh + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: ./packaging/publish_release.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4178a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Byte-compiled / caches +__pycache__/ +*.py[cod] +*.pyo +.pytest_cache/ +.coverage +htmlcov/ + +# Local environments +.venv/ +.venv-build/ +env/ +venv/ + +# Build outputs +build/ +dist/ +*.egg-info/ +*.spec.bak +*.log + +# OS/editor noise +.DS_Store +Thumbs.db +desktop.ini +.idea/ +.vscode/ + +# Local runtime data +*.db +*.db-wal +*.db-shm diff --git a/BoltClam.spec b/BoltClam.spec new file mode 100644 index 0000000..5f40f42 --- /dev/null +++ b/BoltClam.spec @@ -0,0 +1,79 @@ +# -*- mode: python ; coding: utf-8 -*- +"""PyInstaller spec for BoltClam. + +Default: a one-folder Windows build (dist/BoltClam/BoltClam.exe), which is +what the Inno Setup installer packages. Set BOLTCLAM_ONEFILE=1 for a single +BoltClam.exe instead. +""" + +from pathlib import Path +import os +import sys + +ROOT = Path(SPECPATH).resolve() +ONE_FILE = os.environ.get("BOLTCLAM_ONEFILE") == "1" +VERSION_FILE = ROOT / "resources" / "version_info.txt" +ICON_FILE = ROOT / "resources" / "boltclam.ico" + +a = Analysis( + [str(ROOT / "src" / "boltclam" / "__main__.py")], + pathex=[str(ROOT / "src")], + binaries=[], + datas=[(str(ROOT / "resources"), "resources")], + hiddenimports=[ + "PyQt6.QtCore", + "PyQt6.QtGui", + "PyQt6.QtWidgets", + "PyQt6.sip", + ], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=["tkinter", "unittest", "pydoc"], + noarchive=False, + optimize=1, +) +pyz = PYZ(a.pure) + +exe_kwargs = dict( + name="BoltClam", + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=False, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=str(ICON_FILE) if ICON_FILE.exists() else None, + version=str(VERSION_FILE) if sys.platform == "win32" and VERSION_FILE.exists() else None, +) + +if ONE_FILE: + exe = EXE( + pyz, + a.scripts, + a.binaries, + a.datas, + [], + **exe_kwargs, + ) +else: + exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + **exe_kwargs, + ) + coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=False, + upx_exclude=[], + name="BoltClam", + ) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..66f1c28 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +## 1.0.0 — 2026-10-25 + +Initial BoltClam release. + +- PyQt6 Windows desktop app with Homepage, Scan & results, About & updates, + and Settings screens. +- ClamAV full-disk and all-user-home scanning, live progress, scan history, + detection actions, and quarantine restore/delete. +- Browser-download watcher for Chrome, Edge, Brave, Firefox, Vivaldi and + Opera download folders, with an exclusive-handle scan-before-open gate + and configurable automatic response. +- Bolt browser sign-in using the desk API loopback/PKCE flow, with the key + stored in Windows DPAPI. +- Account/subscription display and update notifications from the BoltClam + Gitea release feed, including release/beta channels and checksum + verification. +- Opt-in diagnostics upload with token/password/path redaction. +- Portable Windows build, PyInstaller spec, Inno Setup installer, and Gitea + release workflow. +- Additive Bolt API endpoints: branded sign-in page, account, release proxy, + diagnostics intake, and owner-only log access. diff --git a/README.md b/README.md new file mode 100644 index 0000000..512c22e --- /dev/null +++ b/README.md @@ -0,0 +1,199 @@ +# BoltClam + +BoltClam is a Windows desktop front-end for **ClamAV**, built with PyQt6 and +signed in with a Bolt account. It scans every user's home or the full disk, +quarantines or deletes what ClamAV finds, watches browser downloads, and +receives update notifications from the private Gitea release feed. + +![BoltClam icon](resources/boltclam.png) + +## What it does + +- **Homepage** — a live protection summary: a shell gauge for engine, + definitions, real-time protection and system firewall; security concerns; + network details; recent detections; the signed-in user and Bolt plan. +- **Scan & results** — user-home or full-disk scans, live progress, scan + history, per-detection actions, and a quarantine manager with restore. +- **About & updates** — app version, account and subscription, ClamAV + version/definitions, release notes, beta/release channels, and + checksum-verified downloads. +- **Settings** — update channel, ClamAV install location, extra clamscan + options, real-time protection, automatic response, logging/debug, and + opt-in diagnostics upload to Bolt. +- **Browser download protection** — watches the configured download folder + of Chrome, Edge, Brave, Firefox, Vivaldi and Opera (plus every readable + user's Downloads folder), waits for the file to settle, holds it with an + exclusive Windows handle while ClamAV scans a copy, then quarantines or + deletes it according to the setting. +- **Bolt sign-in** — opens a Bolt-hosted sign-in page in the default + browser, receives a one-time code on a loopback listener, and exchanges it + through PKCE for an ordinary `bolt_live_...` API key. The key is stored + with Windows DPAPI; the password is never seen or stored by BoltClam. + +## Requirements + +- Windows 10 or 11, x64. +- [ClamAV for Windows](https://www.clamav.net/downloads) installed separately. + BoltClam does not bundle ClamAV binaries. +- A Bolt account (the same account used by the iOS/Android/Boltville clients). +- Internet access for sign-in, updates, and the first signature download. +- Administrator rights are optional but improve coverage of other users' + profiles and protected files. + +## Install + +### Portable build + +1. Download `BoltClam-1.0.0-windows-x64-portable.zip` and extract the whole + folder (not just `BoltClam.exe`). +2. Run `BoltClam.exe`. +3. Sign in with your Bolt account in the browser. +4. Install ClamAV, open **Settings → ClamAV engine**, choose + `clamscan.exe` (or use **Auto-detect**), and click **Update definitions**. +5. Run a scan from **Scan & results**. + +The portable build is assembled by +`packaging/build_portable_windows.py` from official Python Embeddable, +PyQt6 wheels, and the tiny `packaging/launcher.c` bootstrap. It is not signed; +Windows SmartScreen may warn about an unknown publisher. Compare the SHA-256 +in `SHA256SUMS.txt` with the release. + +### Installer / PyInstaller + +On a Windows build machine with Python 3.11 x64: + +```powershell +.\build_windows.ps1 +``` + +This creates a one-folder build under `dist\BoltClam\`, a zip, and — when +Inno Setup 6 is installed — `dist\BoltClam-1.0.0-Setup.exe`. +Use `.\build_windows.ps1 -OneFile` for a single-file `BoltClam.exe`. +`build_windows.bat` is a double-click wrapper. + +## Updates + +Release notifications are read from: + + + +BoltClam calls the Bolt API (`/boltclam/updates`) rather than the Gitea API +directly, so the Gitea token stays server-side. The API normalizes releases, +filters them by channel, and proxies asset downloads: + +- **Release** channel: stable releases only. +- **Beta** channel: prereleases and stable releases. +- Assets are downloaded through the API with the user's Bolt key. +- If the release publishes `SHA256SUMS.txt`, the downloaded installer/zip is + verified before it is offered for installation. + +The Gitea repo is currently public and starts empty. The first release is +created by pushing a `v*` tag once a Windows build runner is available (see +`.gitea/workflows/windows-release.yml`) or by running `build_windows.ps1` on +Windows and uploading the assets with `packaging/publish_release.ps1`. + +## Server API + +The BoltClam client uses the desk API at +`https://api.themajesticnetwork.com` by default: + +| Endpoint | Purpose | +|---|---| +| `GET /desk/auth/boltclam/start` | BoltClam-branded browser sign-in page; validates loopback redirect + PKCE. | +| `POST /desk/auth/boltclam/login` | Email/password form handler; returns a one-time code to the app. | +| `GET /desk/auth/oauth/{google\|discord}/start` | Existing provider flow, reused by the page's OAuth buttons. | +| `POST /desk/auth/oauth/exchange` | Existing PKCE exchange; mints the `bolt_live_...` key. | +| `GET /boltclam/account` | User, username/email, plan/subscription, balance. | +| `GET /boltclam/updates?channel=release\|beta` | Normalized Gitea release feed. | +| `GET /boltclam/updates//assets/` | Authenticated asset proxy. | +| `POST /boltclam/logs` | Opt-in diagnostics zip upload. | +| `GET /boltclam/admin/logs` | Owner-only log listing (master desk key). | + +The server implementation lives in +`/home/themajesticmagician/Documents/Server/tmn-api/ai/boltclam_api.py` +(plus the small registration in `ai/desk_api.py` and the `boltclam` client +name in `ai/desk_auth.py`). See `docs/BOLTCLAM.md` in that repository for +deployment notes. + +## Security and privacy + +- The Bolt password is sent only to the Bolt-hosted sign-in page/API. + BoltClam receives a device-scoped API key, stored with Windows DPAPI. +- Signing out revokes only this PC's key. +- No API key is written to settings or diagnostics. +- Diagnostics upload is off by default. When enabled and explicitly sent, + the zip contains app logs, settings, scan/quarantine summaries, a system + summary and the account email. Tokens and passwords are redacted, emails + in logs are redacted, and user-profile path prefixes become + `%USERPROFILE%`. File contents are never included. +- Quarantine files are renamed to `.quarantine`, made read-only/hidden where + possible, and recorded with SHA-256 plus a JSON sidecar. +- ClamAV extra options that would delete or move files (`--remove`, `--move`, + `--copy`) are refused; remediation stays in BoltClam so it is recorded and + reversible. + +**Real-time protection is user-mode, not a kernel filter driver.** Holding a +file with `dwShareMode = 0` makes Windows refuse other opens while ClamAV +scans a copy, which is a strong speed bump, but it is not a guarantee against +a kernel-level bypass. If another process already has the file open, BoltClam +falls back to scanning a copy and records that in the event detail. Read +[`docs/SECURITY.md`](docs/SECURITY.md) before relying on it as the only +anti-malware layer. + +## Development + +```bash +python3 -m venv .venv +.venv/bin/pip install -r requirements-dev.txt +.venv/bin/pip install -e . +.venv/bin/python -m boltclam +QT_QPA_PLATFORM=offscreen .venv/bin/python -m pytest tests -q +``` + +The app reads `BOLTCLAM_API_URL` to override the API base (useful for a +local desk API). Tests redirect config/data with `BOLTCLAM_CONFIG_DIR` and +`BOLTCLAM_DATA_DIR`; never point those at a real user profile. + +## Repository + +```text +BoltClam/ +├── src/boltclam/ application, services and PyQt pages +├── tests/ unit and offscreen UI tests +├── resources/ icon, stylesheet rasters and OFL fonts +├── packaging/ Windows portable builder, launcher, release helper +├── installer/ Inno Setup script +├── build_windows.ps1 PyInstaller + installer build on Windows +├── BoltClam.spec PyInstaller definition +└── .gitea/workflows/ Windows release workflow +``` + +## Push / release + +The remote is already configured: + +```bash +git remote -v +# origin https://git.themajesticnetwork.com/TheMajesticNetwork/BoltClam.git +``` + +Push the initial commit (Git will ask for your Gitea credentials/token): + +```bash +git push -u origin main +git push origin v1.0.0 +``` + +Pushing the tag triggers the Windows release workflow **if** the repository +has a Windows Actions runner and a `GITEA_TOKEN` secret. Without such a +runner, build on a Windows PC with `build_windows.ps1` and upload the +artifacts with `packaging/publish_release.ps1`. + +## Licensing + +BoltClam is internal software for The Majestic Network; no public license is +granted by this repository. **PyQt6 is GPLv3-or-commercial**: distributing +BoltClam binaries under proprietary terms requires a commercial PyQt license, +or GPLv3 compliance. ClamAV is GPLv2 and is installed separately, not +bundled. Fonts are under the SIL Open Font License. See +[`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md). diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md new file mode 100644 index 0000000..ee70b1b --- /dev/null +++ b/THIRD_PARTY_NOTICES.md @@ -0,0 +1,23 @@ +# Third-party notices + +BoltClam is built with: + +| Component | Version | License | Notes | +|---|---|---|---| +| Python | 3.11.9 | PSF License | Embedded in the portable Windows build. | +| PyQt6 | 6.11.0 | GPLv3 or commercial (Riverbank) | The application's distribution model must comply with GPLv3 or hold a commercial PyQt license. | +| Qt 6 | 6.11.2 | LGPLv3 / GPLv3 | Qt libraries are distributed inside the PyQt6 wheel. | +| PyQt6-sip | 13.13.0 | BSD-2-Clause | Runtime support for PyQt6. | +| ClamAV | user-installed | GPLv2 | Not bundled. BoltClam invokes the separate `clamscan.exe` / `freshclam.exe`. | +| Bricolage Grotesque | Google Fonts | SIL Open Font License 1.1 | `resources/fonts/OFL-Bricolage.txt`. | +| Atkinson Hyperlegible | Google Fonts | SIL Open Font License 1.1 | `resources/fonts/OFL-Atkinson.txt`. | +| JetBrains Mono | JetBrains | SIL Open Font License 1.1 | `resources/fonts/OFL-JetBrainsMono.txt`. | + +The portable build includes the official Python embeddable distribution and +the Windows PyQt6 wheels unchanged apart from placing them in a directory +layout. Their license files are retained in the package. + +**BoltClam itself is internal software for The Majestic Network; this +repository grants no public license.** If BoltClam is distributed outside an +organization that complies with the GPLv3, obtain a commercial PyQt license +from Riverbank Computing. diff --git a/build_windows.bat b/build_windows.bat new file mode 100644 index 0000000..7f9b93b --- /dev/null +++ b/build_windows.bat @@ -0,0 +1,3 @@ +@echo off +setlocal +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0build_windows.ps1" %* diff --git a/build_windows.ps1 b/build_windows.ps1 new file mode 100644 index 0000000..035e9f6 --- /dev/null +++ b/build_windows.ps1 @@ -0,0 +1,99 @@ +#requires -Version 5.1 +[CmdletBinding()] +param( + [switch]$OneFile, + [switch]$SkipInstaller +) + +$ErrorActionPreference = "Stop" +$Root = Split-Path -Parent $MyInvocation.MyCommand.Path +Set-Location $Root + +function Get-Python { + $candidates = @("py -3.11", "py -3", "python") + foreach ($candidate in $candidates) { + try { + $parts = $candidate.Split(" ") + $exe = $parts[0] + $exeArgs = @($parts | Select-Object -Skip 1) + $output = & $exe @exeArgs -c "import sys; print(sys.executable); print(sys.version_info[:2])" 2>$null + if ($LASTEXITCODE -eq 0 -and $output -match "3\.11") { + return $parts + } + } catch { } + } + throw "Python 3.11 x64 is required. Install it from python.org and retry." +} + +$version = "1.0.0" +$venv = Join-Path $Root ".venv-build" +$python = Get-Python + +if (-not (Test-Path $venv)) { + Write-Host "Creating build environment..." + $pythonExe = $python[0] + $pythonArgs = @($python | Select-Object -Skip 1) + & $pythonExe @pythonArgs -m venv $venv +} +$py = Join-Path $venv "Scripts\python.exe" +if (-not (Test-Path $py)) { + throw "The build virtual environment could not be created." +} + +& $py -m pip install --upgrade pip +& $py -m pip install -r (Join-Path $Root "requirements-dev.txt") +if ($LASTEXITCODE -ne 0) { throw "Dependency installation failed." } + +if ($OneFile) { + $env:BOLTCLAM_ONEFILE = "1" +} else { + Remove-Item Env:BOLTCLAM_ONEFILE -ErrorAction SilentlyContinue +} + +Write-Host "Building BoltClam $version..." +& $py -m PyInstaller --noconfirm --clean (Join-Path $Root "BoltClam.spec") +if ($LASTEXITCODE -ne 0) { throw "PyInstaller failed." } + +$dist = Join-Path $Root "dist" +$artifacts = @() +if ($OneFile) { + $exe = Join-Path $dist "BoltClam.exe" + if (-not (Test-Path $exe)) { throw "Expected $exe was not produced." } + $artifacts += $exe +} else { + $folder = Join-Path $dist "BoltClam" + $exe = Join-Path $folder "BoltClam.exe" + if (-not (Test-Path $exe)) { throw "Expected $exe was not produced." } + $zip = Join-Path $dist "BoltClam-$version-windows-x64.zip" + if (Test-Path $zip) { Remove-Item $zip -Force } + Compress-Archive -Path (Join-Path $folder "*") -DestinationPath $zip -CompressionLevel Optimal + $artifacts += $zip + + if (-not $SkipInstaller) { + $isccCandidates = @( + "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe", + "$env:ProgramFiles\Inno Setup 6\ISCC.exe" + ) + $iscc = $isccCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1 + if ($iscc) { + Write-Host "Building installer with $iscc..." + & $iscc "/DMyAppVersion=$version" (Join-Path $Root "installer\BoltClam.iss") + if ($LASTEXITCODE -ne 0) { throw "Inno Setup failed." } + $setup = Join-Path $dist "BoltClam-$version-Setup.exe" + if (Test-Path $setup) { $artifacts += $setup } + } else { + Write-Warning "Inno Setup 6 was not found; skipping the installer." + } + } +} + +$checksumPath = Join-Path $dist "SHA256SUMS.txt" +$lines = foreach ($artifact in $artifacts) { + $hash = (Get-FileHash -Algorithm SHA256 $artifact).Hash.ToLowerInvariant() + "$hash $(Split-Path -Leaf $artifact)" +} +$lines | Set-Content -Encoding ASCII $checksumPath +Write-Host "" +Write-Host "Built:" +$artifacts | ForEach-Object { Write-Host " $_" } +Write-Host "Checksums: $checksumPath" diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..589b84d --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,107 @@ +# BoltClam architecture + +## Process layout + +BoltClam is one PyQt6 process on the user's Windows desktop. It starts no +Windows service and installs no driver. ClamAV remains an external program. + +``` +BoltClam.exe (PyInstaller or portable launcher) + └─ boltclam.app + ├─ MainWindow ── HomePage / ScanPage / AboutPage / SettingsPage + ├─ RealtimeProtection ── DownloadWatcher + QThreadPool scan tasks + ├─ ScanWorker / DefinitionWorker (QThread) + ├─ Storage (SQLite, WAL) + ├─ QuarantineService + ├─ ClamAVService → clamscan.exe / freshclam.exe + └─ ApiClient → https://api.themajesticnetwork.com +``` + +Settings are JSON under `%APPDATA%\BoltClam`; history and quarantine live +under `%LOCALAPPDATA%\BoltClam`. `BOLTCLAM_CONFIG_DIR` and +`BOLTCLAM_DATA_DIR` override both for tests and portable/development use. + +## Page responsibilities + +- **HomePage** calls `system_info.collect()` on a worker. PowerShell supplies + Defender/firewall/network facts on Windows; missing data is "not reported". + The shell gauge and concern list are derived from ClamAV state, definition + age, real-time state, firewall, last scan, active detections and quarantine + count. It never labels an unknown state as protected. +- **ScanPage** persists scope/auto-response preferences immediately, runs + `ScanWorker`, records each detection in SQLite before remediation, and + exposes manual quarantine/delete/ignore after a scan. Quarantine management + restores to the original path or a chosen path and refuses overwrites. +- **AboutPage** calls `/boltclam/account` for user/plan data and + `/boltclam/updates` for the release feed. Downloads use the API asset proxy + and optionally verify `SHA256SUMS.txt`. +- **SettingsPage** validates ClamAV paths and extra options, applies logging + changes, updates the real-time watcher, and creates/uploads diagnostics + bundles only when the user has opted in. + +## Sign-in sequence + +``` +BoltClam Browser Bolt desk API + |-- PKCE verifier/challenge --| | + |-- loopback listener :port/cb| | + |-- open /desk/auth/boltclam/start?... ------------------->| + | |<-- branded login page -----| + | |-- email/password or OAuth->| + |<-- 302 /cb?code=...&state=... ----------------------------| + |-- POST /desk/auth/oauth/exchange (code+verifier) -------->| + |<-- {api_key, user} --------------------------------------| + |-- DPAPI-store key, GET /boltclam/account ---------------->| +``` + +The one-time code is bound to the PKCE challenge, the exact loopback URI and +a short TTL. The app rejects a callback whose state does not match. The API +burns an exchange code on the first attempt, successful or not. + +## Scan sequence + +1. `ClamAVService.info()` locates `clamscan.exe`, runs `--version`, and + chooses the managed database if it contains signatures. +2. `scan_targets("homes")` reads the Windows `ProfileList` registry and + falls back to `C:\Users`; `"full"` enumerates fixed local drives. +3. `ScanWorker` starts clamscan with an argv list, no shell, and parses + detections/progress/summary. Each `path: Signature FOUND` becomes a + `Detection` row first, then quarantine/delete/notify is applied. +4. The scan row is finished even on cancellation or process failure. The UI + reloads history and the Homepage aggregates. + +## Real-time sequence + +`DownloadWatcher` polls each watched download folder at 1.5 s intervals (and +refreshes browser preferences every 30 s), ignores in-progress suffixes, and +waits for a stable size/mtime. `RealtimeScanTask` runs in a QThreadPool: + +``` +stable file → exclusive handle (share=0) → copy → ClamAV scans copy + → clean: release → detection: release + quarantine/delete +``` + +The exclusive handle is held for the entire scan. A fallback copy is used +when the file is already open, with that fact recorded in the event detail. + +## Update flow + +- The Bolt API reads Gitea's `/api/v1/repos/TheMajesticNetwork/BoltClam/releases`, + caches for five minutes, filters draft/prerelease by channel, and returns + normalized release metadata. +- Asset downloads go through the API, so a future private repository needs + no desktop token. The API streams the Gitea asset with its own token. +- The client compares semantic versions, selects a Windows `.exe`/`.zip` + asset, downloads it to `%LOCALAPPDATA%\BoltClam\updates`, and verifies the + release checksum when published. + +## Storage schema + +- `scans` — scope, status, paths JSON, counts, engine/DB versions, duration. +- `detections` — scan id, path, signature, action, quarantine id, timestamp. +- `quarantine` — id, original/quarantine path, signature, SHA-256, status. +- `realtime_events` — download path, signature, action, detail, timestamp. + +All writes open their own SQLite connection; WAL mode lets the UI read while +a scan thread writes. Quarantine sidecars are JSON, so the record survives a +database rebuild. diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..8e3f91e --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,114 @@ +# BoltClam security model + +BoltClam is a **signature-based second opinion**, not a replacement for +Windows Defender or a kernel-level endpoint protection platform. This file +states exactly what the user-mode protections do, where they stop, and what +the app stores. + +## Trust boundaries + +- **Bolt API** (`api.themajesticnetwork.com`) is the only network service + BoltClam talks to. It proxies Gitea release metadata and assets, so a + Gitea token is never shipped to a desktop. +- **ClamAV** is a separate, user-installed executable (`clamscan.exe` / + `freshclam.exe`). BoltClam runs it as the current user and parses its + output. BoltClam does not load ClamAV DLLs into its own process. +- **Windows** is trusted for DPAPI, file handles, and security-center facts + reported by PowerShell. If PowerShell or Defender cmdlets are unavailable, + the UI says "not reported" rather than inventing a pass. +- **Gitea** is trusted for release metadata only. Downloads are either + proxied through the Bolt API or checked against a release SHA-256 when a + checksum asset exists. + +## Credentials + +- Sign-in uses the existing desk API browser/PKCE flow. The password is + entered on a Bolt-hosted page and is never sent to BoltClam. +- The resulting `bolt_live_...` API key is stored in + `%APPDATA%\BoltClam\credentials.dat` encrypted with Windows DPAPI + (current-user scope, with an app-specific entropy value). +- On non-Windows development hosts the credential store falls back to a + mode-0600 local file and logs a warning. That fallback is for development + only. +- A key is never written to `settings.json`, logs, or diagnostics bundles. +- Sign-out revokes only the key this PC used, then deletes the local copy. + +## Scan and remediation + +- Scans run `clamscan` with `--infected --verbose --stdout` and explicit + argument lists (no shell). Extra options come from the user's settings and + are parsed; `--remove`, `--move`, and `--copy` are refused so every action + goes through BoltClam's recorded quarantine/delete path. +- A detection is written to SQLite **before** remediation is attempted. If + quarantine or delete fails, the record stays visible as `failed` instead of + disappearing. +- Quarantine moves the original file into + `%LOCALAPPDATA%\BoltClam\quarantine`, renames it to `.quarantine`, records + its SHA-256 and original path, and writes a JSON sidecar. Restore refuses to + overwrite an existing file. Delete is permanent and explicitly confirmed. +- The quarantine folder is user-writable. A malicious process already running + as that user could tamper with it; strong tamper resistance would require a + privileged service and kernel driver, which this app does not install. + +## Real-time download gate + +BoltClam watches browser download folders and waits for a file's size and +mtime to stop changing. It then: + +1. opens the file with `CreateFileW` and `dwShareMode = 0` (deny other + opens) where possible; +2. copies it to a private temp path through that handle; +3. scans the copy with ClamAV while the original remains locked; +4. on a clean result, releases the lock; +5. on a detection, closes the lock and moves the file to quarantine (or + deletes it if that setting was chosen). + +This blocks ordinary `CreateFile` opens during the scan, including a +double-click, which is the meaningful user-mode speed bump. It is **not**: + +- a kernel minifilter, so a driver or a process using backup semantics can + bypass it; +- a guarantee when another process already holds the file open — BoltClam + reports "File was in use; scanned a copy" and still remediates afterward; +- protection for downloads made outside a watched browser download folder + (curl, scripts, existing files, network shares); +- a replacement for Windows Defender's real-time engine. + +The **Notify only** response leaves the detected file in place. Use +Quarantine (the default) if the machine must not retain detected downloads. +Files larger than `BOLTCLAM_REALTIME_MAX_MB` (default 1024 MiB) are reported +and not scanned in real time. + +## Definitions and updates + +- `freshclam` writes to BoltClam's own `%LOCALAPPDATA%\BoltClam\clamav-db`, + avoiding a Program Files write. `clamscan` uses that database when present + and otherwise falls back to the database beside the installed binary. +- The signed-in user can see definition age in the Homepage and Settings. + "Fresh" means no older than seven days; older than 30 days is critical. +- Update downloads require a valid Bolt key. If the release includes + `SHA256SUMS.txt`, the SHA-256 is verified before the installer is offered. + Without a checksum asset the UI says so rather than claiming verification. +- Run installers only from the BoltClam Gitea releases page. The app itself + does not silently replace its own executable. + +## Diagnostics + +Uploading diagnostics is **opt-in and explicit**. The bundle contains: + +- redacted app logs; +- settings (API key absent); +- scan/quarantine summaries and real-time event metadata (paths redacted to + `%USERPROFILE%`); +- ClamAV and system summary; +- account email, so support can identify the upload. + +It never contains file contents, browser history, passwords, API keys, or +the DPAPI credential file. Logs cap size and rotate locally. + +## Reporting a problem + +For a security issue in BoltClam or the BoltClam API, contact The Majestic +Network directly rather than opening a public issue with exploit details. +Include the app version, Windows version, and the relevant redacted log +excerpt. diff --git a/installer/BoltClam.iss b/installer/BoltClam.iss new file mode 100644 index 0000000..898d24e --- /dev/null +++ b/installer/BoltClam.iss @@ -0,0 +1,53 @@ +#define MyAppName "BoltClam" +#ifndef MyAppVersion + #define MyAppVersion "1.0.0" +#endif +#define MyAppPublisher "The Majestic Network" +#define MyAppURL "https://git.themajesticnetwork.com/TheMajesticNetwork/BoltClam" +#define MyAppExeName "BoltClam.exe" + +[Setup] +AppId={{8C7F4C7A-6BB2-47B1-93F6-9A6D1D3B1F5E} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={localappdata}\Programs\{#MyAppName} +DefaultGroupName={#MyAppName} +DisableProgramGroupPage=yes +PrivilegesRequired=lowest +OutputDir=..\dist +OutputBaseFilename=BoltClam-{#MyAppVersion}-Setup +SetupIconFile=..\resources\boltclam.ico +UninstallDisplayIcon={app}\{#MyAppExeName} +Compression=lzma2 +SolidCompression=yes +WizardStyle=modern +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible +CloseApplications=yes +RestartApplications=no +VersionInfoVersion={#MyAppVersion} +VersionInfoCompany={#MyAppPublisher} +VersionInfoDescription={#MyAppName} installer +VersionInfoProductName={#MyAppName} +VersionInfoProductVersion={#MyAppVersion} + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "Create a desktop shortcut"; GroupDescription: "Additional icons:"; Flags: unchecked + +[Files] +Source: "..\dist\BoltClam\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs + +[Icons] +Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{group}\Uninstall {#MyAppName}"; Filename: "{uninstallexe}" +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#MyAppExeName}"; Description: "Launch {#MyAppName}"; Flags: nowait postinstall skipifsilent diff --git a/packaging/BoltClam-launcher.exe b/packaging/BoltClam-launcher.exe new file mode 100755 index 0000000000000000000000000000000000000000..0bdcd6b4f6956d9d727cd82b6ac3d2e1203ac8dd GIT binary patch literal 176640 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�cqAob;5+7iSQu_e#6X*MRgn1K0Y^{%`)` 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 tngh->fBsAM@A@d%UJab;<6)jRR z;V$GHRoH}#P<76t)jBh0LWn;_1Ws6{K2>j%=pUxbR=I^x6bSRbq0=-bp5)pgt1~mi zAbaWuq6f#S65kI!`0k&jlin{q*hy+Z_zUWJLWDm_G7uh@$MF#US5?$82>*@bIU&NA z>de^)zd?P{2>({P?EVO!qti5IlPt%hCu8=s6hg9j@g#I>j62~Nrs+sA{N^r=7qkX1 zF&>XYM&?pFD;#9xDn_-wg++T_laW76e=3!;>M{(NK}}-+R@PZfQMQN9Nq&K3tWx<+ z570d@9z{Z`RFbVjc3-d3a?XLoj163zy;z^M7w22ZE(`L{)Ke|UEYMdYEG9U;&2IbdH&=%9$ezGNf%ShDNFpA`|jg6zH8rpC%sThmfOPyyj2R)sU=a!g(rd z86;{opo%e^nu(<PbIBv=DF%w4ZVbsi>tAxVG%zAsHYTCP;BP!@NhI|Z|9vxQ>XC5U~XaGj%a8a zi<8y`u_SD=i5-%8&K_Q$`=ft8 zZ_z)eQr7=~{(0eE1xk_i9HXCj$WgWouNIm_(LX#zNS>c_;$KuAeK37Uhx;8&f5*4% zgQ>iemo)ltYQ>B4y|C9%&M(2#Nj1+hh#3)>cTT)Tl`#i`r55vZibxScMh>Y0o0z17 zXsH=LDJ;qyV6eZ;Uj*xM_P%EwT3MA?d zJ|Gcr8g?OnA#r8#L^v;P#l*hbl;=QwVxSa8@ceRt@_O>xP|hbz(F)2D0;O0e6DX1Y zz^VsPC392~1!;g_2GWap(~wpY>Idn0@}Vpauuovd^L*ZPc{Yq1J}pzEcgSq}=kq5D zm<8k#FxfInh7o3+Z(nxs-BUpJ8Q6)toJiRmY*2|VdGlA2td{NJ{E{T$1*h{>Hi)AA zyrs^fsQgWYh!@>R&?<2+s4rO90NlS&X#RRW41o9Kea`>&`51qqs~N(_s6Q)P=b z_9)xd`SsHof#HuqNg-{5#Lo|hAghvpH?MQLmCRyMfxIBPctZtDrM`EkF|NjojLR`b z$MO1sc*-9FeSOK7fdE;9fBq5?$AnAh@b!r
F6SWnN~B_TyC#_19A7Ii%Su%nK< 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

$E}~cyu)j_*8|r*2uOGyq)(+MFq7{!3+9@a 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 G0{;t-8C6jL literal 0 HcmV?d00001 diff --git a/packaging/README.md b/packaging/README.md new file mode 100644 index 0000000..f5038f0 --- /dev/null +++ b/packaging/README.md @@ -0,0 +1,78 @@ +# Packaging + +## Windows-native build + +On Windows 10/11 with Python 3.11 x64: + +```powershell +.\build_windows.ps1 +``` + +The script creates `.venv-build`, installs PyInstaller, builds +`dist\BoltClam\BoltClam.exe`, zips it, and runs Inno Setup when it is +installed. `-OneFile` builds a single executable instead. + +## Portable cross-build (Linux/macOS) + +`build_portable_windows.py` downloads the official Python embeddable +distribution and Windows PyQt6 wheels, copies `src/boltclam` into `app/`, +adds `resources/`, and places a tiny launcher at the root. It does not run +the Windows app on the build host. + +```bash +.venv/bin/python packaging/build_portable_windows.py +``` + +The build uses the checked-in `BoltClam-launcher.exe` unless `--zig` (or a +`zig` on `PATH`) is available, in which case it compiles `launcher.c` from +source. The prebuilt launcher is a 176,640-byte GUI-subsystem PE32+ binary: + +``` +SHA-256 95cdb1e0a652f0b2e3870220cf1ec79c1e2972bc56e307bcb1381b2b9591a24b +``` + +To rebuild it with Zig 0.14.1: + +```bash +zig cc -target x86_64-windows-gnu -O2 -Wl,--subsystem,windows \ + packaging/launcher.c -o packaging/BoltClam-launcher.exe +``` + +The output portable package is written to +`dist/BoltClam-1.0.0-windows-x64-portable.zip` with `dist/SHA256SUMS.txt`. +It is unsigned; the release checksum is the integrity signal. + +## Layout of the portable package + +``` +BoltClam/ + BoltClam.exe GUI launcher + pythonw.exe embedded Python 3.11.9 + python311.dll + python311.zip stdlib + python311._pth + app/boltclam/ app source + resources/ icon, fonts, QSS rasters + Lib/site-packages/ Windows PyQt6 6.11 wheels +``` + +Keep the whole folder together. The launcher refuses to start if +`pythonw.exe` or `app/` is missing rather than showing a silent failure. +If the app exits before a window appears, run `python.exe -m boltclam` from +the extracted folder in a command prompt to see the import/startup error. + +## Release upload + +The `Windows release` workflow runs `build_windows.ps1` and uploads the +installer/zip to a Gitea release. On a machine without the workflow, after +building: + +```powershell +$env:GITEA_TOKEN = "…" +.\packaging\publish_release.ps1 -Tag v1.0.0 +``` + +`publish_release.ps1` creates (or reuses) the release and uploads +`dist/BoltClam-*.exe`, `dist/BoltClam-*.zip`, and `dist/SHA256SUMS.txt`. +Gitea's asset endpoint expects multipart form data, so the script uses +`curl.exe -F attachment=@…`. diff --git a/packaging/build_portable_windows.py b/packaging/build_portable_windows.py new file mode 100644 index 0000000..f6874c2 --- /dev/null +++ b/packaging/build_portable_windows.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +"""Build the portable Windows BoltClam package from a Linux/macOS host. + +The result is a folder and zip containing: + + BoltClam.exe tiny GUI launcher (packaging/launcher.c) + pythonw.exe official Windows embeddable Python 3.11 + python311._pth points at app/ and the Windows PyQt6 wheels + app/boltclam/ BoltClam source + resources/ fonts and icon assets + Lib/site-packages/ Windows PyQt6 wheels + +This is the no-Windows-machine path. On a Windows build host, prefer +`build_windows.ps1` / PyInstaller for a branded one-file EXE and Inno Setup +installer. This script downloads public build inputs and never uses a +platform-specific Python interpreter to run the Windows app. +""" + +from __future__ import annotations + +import argparse +import hashlib +import os +import shutil +import subprocess +import sys +import tarfile +import urllib.request +import zipfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +BUILD = ROOT / "build" / "portable" +CACHE = ROOT / "build" / "cache" +DIST = ROOT / "dist" +VERSION = "1.0.0" +PYTHON_VERSION = "3.11.9" +PYTHON_URL = (f"https://www.python.org/ftp/python/{PYTHON_VERSION}/" + f"python-{PYTHON_VERSION}-embed-amd64.zip") +PYTHON_SHA256 = "009d6bf7e3b2ddca3d784fa09f90fe54336d5b60f0e0f305c37f400bf83cfd3b" +PYQT_PACKAGES = ("PyQt6==6.11.0", "PyQt6-Qt6==6.11.2", "PyQt6-sip==13.13.0") +ZIG_VERSION = "0.14.1" +ZIG_URL = f"https://ziglang.org/download/{ZIG_VERSION}/zig-x86_64-linux-{ZIG_VERSION}.tar.xz" + + +def _sha256(path: Path) -> str: + digest = hashlib.sha256() + with open(path, "rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def download(url: str, destination: Path, expected: str = "") -> Path: + destination.parent.mkdir(parents=True, exist_ok=True) + if destination.is_file() and (not expected or _sha256(destination) == expected): + return destination + print(f"Downloading {url}") + temp = destination.with_suffix(destination.suffix + ".part") + with urllib.request.urlopen(url, timeout=120) as response, open(temp, "wb") as handle: + shutil.copyfileobj(response, handle) + if expected and _sha256(temp) != expected: + temp.unlink(missing_ok=True) + raise RuntimeError(f"Checksum mismatch for {url}") + os.replace(temp, destination) + return destination + + +def find_zig(explicit: str) -> Path | None: + if explicit: + return Path(explicit) + found = shutil.which("zig") + if found: + return Path(found) + return None + + +def download_zig() -> Path: + archive = download(ZIG_URL, CACHE / f"zig-{ZIG_VERSION}.tar.xz") + target = CACHE / f"zig-x86_64-linux-{ZIG_VERSION}" + if not (target / "zig").is_file(): + print("Extracting Zig") + with tarfile.open(archive, "r:xz") as tar: + tar.extractall(CACHE) + return target / "zig" + + +def build(use_zig: str = "", keep_build: bool = True) -> Path: + if BUILD.exists(): + shutil.rmtree(BUILD) + BUILD.mkdir(parents=True) + CACHE.mkdir(parents=True, exist_ok=True) + + python_zip = download(PYTHON_URL, CACHE / f"python-{PYTHON_VERSION}-embed-amd64.zip", + PYTHON_SHA256) + with zipfile.ZipFile(python_zip) as archive: + archive.extractall(BUILD) + + app = BUILD / "app" + app.mkdir() + shutil.copytree(ROOT / "src" / "boltclam", app / "boltclam", + ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyo")) + # paths.resource_dir() resolves to parents[2]/resources, i.e. the + # portable root, for app/boltclam/paths.py. Keep resources there. + shutil.copytree(ROOT / "resources", BUILD / "resources") + + site_packages = BUILD / "Lib" / "site-packages" + site_packages.mkdir(parents=True) + print("Installing Windows PyQt6 wheels") + subprocess.check_call([ + sys.executable, "-m", "pip", "install", "--quiet", "--target", str(site_packages), + "--platform", "win_amd64", "--python-version", "3.11", + "--implementation", "cp", "--abi", "cp311", "--only-binary=:all:", + *PYQT_PACKAGES, + ]) + (BUILD / "python311._pth").write_text( + "python311.zip\n.\nLib\\site-packages\napp\nimport site\n", encoding="utf-8") + + zig = find_zig(use_zig) + launcher_source = ROOT / "packaging" / "launcher.c" + launcher_binary = ROOT / "packaging" / "BoltClam-launcher.exe" + if zig is None and launcher_binary.is_file(): + print("Using the checked-in portable launcher") + shutil.copy2(launcher_binary, BUILD / "BoltClam.exe") + else: + if zig is None: + zig = download_zig() + print(f"Compiling launcher with {zig}") + subprocess.check_call([ + str(zig), "cc", "-target", "x86_64-windows-gnu", "-O2", + "-Wl,--subsystem,windows", str(launcher_source), + "-o", str(BUILD / "BoltClam.exe"), + ]) + + (BUILD / "README.txt").write_text( + "BoltClam portable for Windows x64\r\n" + "=================================\r\n\r\n" + "Double-click BoltClam.exe. Sign in with your Bolt account when the\r\n" + "browser opens. ClamAV is installed separately: install it from\r\n" + "https://www.clamav.net/downloads, then open Settings and point BoltClam\r\n" + "at clamscan.exe (Auto-detect usually finds it).\r\n\r\n" + "The first definition update downloads ClamAV signatures into\r\n" + "%LOCALAPPDATA%\\BoltClam\\clamav-db. No administrator rights are needed\r\n" + "for BoltClam's own data.\r\n\r\n" + "This build is unsigned. Windows SmartScreen may warn about a new\r\n" + "publisher; use the checksum published with the release.\r\n\r\n" + "Keep the whole folder together. BoltClam.exe is only a launcher for\r\n" + "pythonw.exe and app/.\r\n\r\n" + "Troubleshooting: if BoltClam.exe does nothing, open a command prompt in\r\n" + "this folder and run `python.exe -m boltclam` to see the startup error.\r\n", + encoding="utf-8") + + DIST.mkdir(parents=True, exist_ok=True) + zip_path = DIST / f"BoltClam-{VERSION}-windows-x64-portable.zip" + print(f"Writing {zip_path}") + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED, compresslevel=6) as archive: + for path in sorted(BUILD.rglob("*")): + if path.is_file(): + archive.write(path, arcname="BoltClam/" + path.relative_to(BUILD).as_posix()) + digest = _sha256(zip_path) + (DIST / "SHA256SUMS.txt").write_text(f"{digest} {zip_path.name}\n", encoding="utf-8") + print(f"{zip_path} ({zip_path.stat().st_size / (1024 * 1024):.1f} MiB)") + print(f"SHA-256 {digest}") + return zip_path + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--zig", default="", help="path to a Zig executable") + args = parser.parse_args() + build(use_zig=args.zig) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/packaging/launcher.c b/packaging/launcher.c new file mode 100644 index 0000000..66a38f3 --- /dev/null +++ b/packaging/launcher.c @@ -0,0 +1,79 @@ +/* + * BoltClam portable Windows launcher. + * + * This is a tiny GUI-subsystem bootstrap for the portable package. It finds + * the runtime beside itself, starts pythonw.exe with the bundled app, and + * exits immediately. It contains no product logic; the Qt application lives + * in app/boltclam. + * + * Build (from the repository root): + * zig cc -target x86_64-windows-gnu -O2 -mwindows packaging/launcher.c \ + * -o build/portable/BoltClam.exe + */ +#define UNICODE +#define _UNICODE +#include +#include + +static void fail(const wchar_t *message) { + MessageBoxW(NULL, message, L"BoltClam", MB_OK | MB_ICONERROR); +} + +int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR command_line, int show) { + (void)instance; + (void)previous; + (void)command_line; + (void)show; + + wchar_t exe[MAX_PATH]; + DWORD length = GetModuleFileNameW(NULL, exe, MAX_PATH); + if (length == 0 || length >= MAX_PATH) { + fail(L"BoltClam could not locate its own executable."); + return 1; + } + wchar_t *slash = wcsrchr(exe, L'\\'); + if (slash == NULL) { + fail(L"BoltClam could not locate its runtime folder."); + return 1; + } + *slash = L'\0'; + + wchar_t pythonw[MAX_PATH * 2]; + _snwprintf(pythonw, MAX_PATH * 2, L"%s\\pythonw.exe", exe); + if (GetFileAttributesW(pythonw) == INVALID_FILE_ATTRIBUTES) { + fail(L"BoltClam's runtime is incomplete: pythonw.exe is missing.\n\n" + L"Extract the whole portable folder, not just BoltClam.exe."); + return 1; + } + + wchar_t appdir[MAX_PATH * 2]; + _snwprintf(appdir, MAX_PATH * 2, L"%s\\app", exe); + if (GetFileAttributesW(appdir) == INVALID_FILE_ATTRIBUTES) { + fail(L"BoltClam's runtime is incomplete: the app folder is missing.\n\n" + L"Extract the whole portable folder, not just BoltClam.exe."); + return 1; + } + SetCurrentDirectoryW(appdir); + + wchar_t cmdline[MAX_PATH * 4]; + _snwprintf(cmdline, MAX_PATH * 4, L"\"%s\" -m boltclam", pythonw); + + STARTUPINFOW startup; + PROCESS_INFORMATION process; + ZeroMemory(&startup, sizeof(startup)); + ZeroMemory(&process, sizeof(process)); + startup.cb = sizeof(startup); + if (!CreateProcessW(pythonw, cmdline, NULL, NULL, FALSE, + CREATE_UNICODE_ENVIRONMENT, NULL, appdir, + &startup, &process)) { + wchar_t error[512]; + _snwprintf(error, 512, + L"BoltClam could not start its runtime (error %lu).", + (unsigned long)GetLastError()); + fail(error); + return 1; + } + CloseHandle(process.hThread); + CloseHandle(process.hProcess); + return 0; +} diff --git a/packaging/publish_release.ps1 b/packaging/publish_release.ps1 new file mode 100644 index 0000000..451c5d2 --- /dev/null +++ b/packaging/publish_release.ps1 @@ -0,0 +1,85 @@ +#requires -Version 5.1 +[CmdletBinding()] +param( + [string]$Tag = "", + [string]$Token = "" +) + +$ErrorActionPreference = "Stop" +$Root = Split-Path -Parent $PSScriptRoot +Set-Location $Root + +$repo = $env:GITHUB_REPOSITORY +if (-not $repo) { $repo = "TheMajesticNetwork/BoltClam" } +$server = $env:GITHUB_SERVER_URL +if (-not $server) { $server = "https://git.themajesticnetwork.com" } +$server = $server.TrimEnd("/") +$api = "$server/api/v1/repos/$repo" + +if (-not $Tag) { + $Tag = $env:GITHUB_REF_NAME +} +if (-not $Tag) { $Tag = "v1.0.0" } +if (-not $Token) { $Token = $env:GITEA_TOKEN } +if (-not $Token) { $Token = $env:GITHUB_TOKEN } +if (-not $Token) { + throw "Set GITEA_TOKEN (a Gitea token with repo write access) before publishing." +} + +$headers = @{ + Authorization = "token $Token" + Accept = "application/json" +} +$releaseBody = @{ + tag_name = $Tag + name = "BoltClam $Tag" + body = "BoltClam $Tag`n`nSee SHA256SUMS.txt for checksums." + draft = $false + prerelease = ($Tag -match "-") +} | ConvertTo-Json + +try { + $release = Invoke-RestMethod -Method Post -Uri "$api/releases" ` + -Headers $headers -ContentType "application/json" -Body $releaseBody + Write-Host "Created release $($release.tag_name)" +} catch { + $status = $_.Exception.Response.StatusCode.value__ + if ($status -ne 409) { throw } + $release = Invoke-RestMethod -Uri "$api/releases/tags/$Tag" -Headers $headers + Write-Host "Release $Tag already exists; adding assets" +} + +$existing = @() +try { + $existing = @(Invoke-RestMethod -Uri "$api/releases/$($release.id)/assets" -Headers $headers) +} catch { + $existing = @() +} +foreach ($asset in $existing) { + if ($asset.name -match '^BoltClam-.*\.(exe|zip)$' -or $asset.name -eq "SHA256SUMS.txt") { + Invoke-RestMethod -Method Delete ` + -Uri "$api/releases/$($release.id)/assets/$($asset.id)" -Headers $headers | Out-Null + Write-Host "Removed old asset $($asset.name)" + } +} + +$files = Get-ChildItem -Path (Join-Path $Root "dist") -File | Where-Object { + $_.Name -match '^BoltClam-.*\.(exe|zip)$' -or $_.Name -eq "SHA256SUMS.txt" +} +if (-not $files) { throw "No release artifacts found in dist." } + +foreach ($file in $files) { + $name = [uri]::EscapeDataString($file.Name) + # Gitea's attachment endpoint is multipart/form-data, unlike GitHub's + # binary upload. Windows 10/11 ships curl.exe; use it for the file field. + & curl.exe --fail --silent --show-error -X POST ` + -H "Authorization: token $Token" ` + -F "attachment=@$($file.FullName)" ` + "$api/releases/$($release.id)/assets?name=$name" + if ($LASTEXITCODE -ne 0) { + throw "Upload failed for $($file.Name)." + } + Write-Host "Uploaded $($file.Name)" +} + +Write-Host "Release published: $($release.html_url)" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3376dc3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" + +[project] +name = "boltclam" +version = "1.0.0" +description = "A Windows desktop antivirus front-end for ClamAV, with Bolt account sign-in." +readme = "README.md" +requires-python = ">=3.10" +license = { text = "Proprietary" } +authors = [{ name = "The Majestic Network" }] +dependencies = ["PyQt6>=6.6,<7"] + +[project.optional-dependencies] +dev = ["pytest>=7.4", "pyinstaller>=6.6"] + +[project.gui-scripts] +BoltClam = "boltclam.app:main" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-q" +filterwarnings = ["ignore::DeprecationWarning"] diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..a3cff69 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements.txt +pytest>=7.4 +pyinstaller>=6.6 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e026139 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +PyQt6>=6.6,<7 diff --git a/resources/boltclam.ico b/resources/boltclam.ico new file mode 100644 index 0000000000000000000000000000000000000000..6f905438c2a6ce0d08e3c38d36395574335f5b8f GIT binary patch literal 44830 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�ZWTz0*>G}MBhRzeGb@abF;KLxI=Ab{Oho@7 z3f4;HF=;p<0Aj1NRK3<)N2D)-H0N#aGOxXk(&th%i+2r8p8h$tpR{Nrr$Z&{_;{iq zAZocEG)W&~x;+Q|-`b`TDybH!Bkcf_X^&r|K4Rt7B{RTO(ghuT)yrQ=MuhFDXN|qWwXaOk5 zkqu?Ej`mb;guKG7$OeMngJgOR+4ZgTu*DpS*jzr-w(_q)DET)>ILi9bx2<wZ+(M>h`>c>pFLH0)7ECTXi0nl1mgxchPQRu}z^Bg@myfMP(>auMiXL>hspZ+V7i zb7K-y`h__gpdq6chnxi8WM@g=dkL)dcgE+kf5(9lfzki5T@rM^qWKKDT0~y)3upCL z&8_0_$<)kp2C(;xl{!Vr_hEz>V~h+{pQEBxTe+jfKY#;FLXwpTN8=0OAEkb0jpq+X zbBzyPsf~gu_)7sdviK-J+J|;2zjS}ANTL_9<2$VM~MKym?wFJ=zage#ymUa2Uj67#e zK9MArh$sQ@ro;~aAukqfuoRAApI@}NxTC^Hy{lJ02F9&x=s==^0W&Y<>>>ww_d^xh z<^Sdp4=~+D(N5F-^IP4>)+d}&>;4b%UvXx;MK%^GI9liP%^`OPS**@*q`4So(qp={ zA1nVqd%?D~{#k=a>dp%>iUm^8`*z|tVxSndtsu%V6M)ex%M-I-|6l0AQfxAhv_y&S zr)-f#EBkz>(j)->;Z>2>$tmhF2(Rk;e+@%msa(15-@_oi1|MgU)j}a0QMc=o_@GY~ z{VnqpvVe1~Y}@&N5Wr0tu>Lu=>yhsA`KmG15C5CkVPR(U7_{`@8(1por$6NXojmnt zH!c-a!HYOvkSXxkW6pdA5S>7x)8F{s?2o{H{udd%*-tIsA+S_j@Q2rR>dzl4<}#2# zCYKRH3kuH%WuD}z2`}+g3;grk(O`t7#xo3v<>AMyY@1UkgYAlF_Yja`{;;N<19N6& z88!W1%yP$XEfIjEI=x-Yzd{m3>EiDA4KVM3SiC^^n2`T+5jQ*WHH;bbLly`0!RXkF zP|KY^n1T&RGo^DW#(>ww{f`N7$Jexzs}U9naZtxa$gIEYD@b0e@vk;BH z#49BO{A5WAxb^it2)6G{`Z9qu9uZ1t_^v{(@V7T8RS34y3Li+&NYLEzC%}wlO+a}* z4?t?8FXd{`2z-p?APL%I7BC^Udk`pxwA}4%gf@h!1VO1bV1m@=VjeDM&cudf;_4v;Dfyfx9;`z zeM;H!{BB^t!rOR4wfFtZNv3_eNtm`v#lR3z4VqKY< zzyzj4DOueU-Ep38zE{$V3qrY{W!07%BhJs|`EFrAOM4s0f&v0iTdK0nTw_5HD=9j1 z$AgX5TIpJIA4GzFR7DD3Q^ieevvw3;(yPDcs_nR5s$J82#?09$_x{A@udBeXb$88fs&FE}@C01PY z>r!29wUKks;FK1<3Ape+SRz2S)Ghp2s%r_^UBn9ORVgAE&l9x?p&+$5a}NjQIL*4E zy4)83>iei|?tCMw&CGNf(raCL`NZ^H{LW#0p6P=HKRQhGe%5um*KuMw$Hhbt^M~}n zNv%q$fTmBVDz4EJ znN&@kvwZRD;@0#wpkghkNSdmw9eVtfYxM=7u!!UT6folZ>7mYVWSvYq*9Hr>wHt|( z3mr%S%$b@SeWUhs)MeMJx+@m}?4k9b)%V7Rw?)~Y>mRb=tW(U`h8C5=#U{B)7$Z~Y zhu5~VD-rOg%LH2D$Z|4_{@q5G+eLa#@+*5&s%>xT63ZQ;Q4o!U$tCwaNzu;A!rd({ z4gZv70+6bz2wiAmEM#nKDZbYlr?uBfez;e&5-vvHiLQ4O+xGETCg3{2QyVmap$Ewd zj!Cd2%LNHRs%-pl?B~x`m3pI>7mA=lP{Hm)_eo zb1^rhrB}H}#Km~(W|bvBNQYx}OZ|PjsG}T^jCNS>;v;?L2fn8_lWse(1Mz z;MZn z|88aq3UJN{?ae>nX^BBWkRM)~t?aD7O)2ROZV_1k-M=fJPKtzb7vm9|_mva*d!o|x z@UPE@Tf$qfp$nZ~c6X`U$w3}hc%6u@d4$Ch0o`%y2^*c}$ULwZ>?ixcgF|?PCVu8zq8?XZuBHTMJ)|~9b9aFcW|`MlAjyDVq^28tsFzd5FP!< zVUiqM61=`9;N_(t zVO&z^U)33yDnW7n=FXQ*+W^O}ci?`;jABQuOkMs`%CKgGeBcmU%UYSQV15K_Y1(Di zqr>pfrSa<8=Ao58J3xqb7l!~|t?FZhEdSwX&!L!W2mgkcKiy!X=kwH*MNu?ZnhG}x zj=jr~xG+qa)Rix?Fe1RN&WO=SXT|)bjQHKav@7@gV(aTTTk_`HPX}SvWajeD5O4J# zZ&_j{OiQxysigYQJ>B<->~0s-ly|z=oKo za*K{mrc~D$xs)`QvyB8#alD?{SJd@ya2WB8lTuy1FMQN*qi)Y%$jO zinOr2=0268xqCP4p#HIcg8tpRkL@*iF-W0fFQM`N&9SNS`wdMc>cbap!!A6&sx2{V zP4;irEvR8?2fG;o`uX4|FTp2U!dV(@CocMV(bIZ%iww4KyBBy6dQ=6OO&Dg{D$}LhtEv<29K`>nqyFD4m!uJM;_yzN{iYn zK2h*f&Lk!Y4(D>gdQrbpl+rDFV1mrp65Imon~hfJ$(!&!ZSZx#UWiu|c6?fF0fq)~ zMbL?Ry@pen>XAv7x00>md^rKA(XY`@9pW)hQ3W&1=i~Fs z;u2CsJroez9)r>0{Z8+*99*vV2|3I0yvGf28ei26+|F+)?h)UHp_)8ZIyB8vD8t`Q zBu$r(8Aw0IJ{P0u9D`wG{`$aj_N+;uYQOGobOl2FDgj58fjvxqZ_Tzo;7K6ddga;M zTYvI)GrFwA6g$LtK0Ut>a}hXr9&JdAuF^n@Rg|GTmVkKpjRd>tmA4Krh=$056Yd0O zb4CrUOmc15$_!nqzT${Ns5L|m34wC^;ooxj&!J3_!?WmNAPGOfGGe;;f$bw4R4RAVi^NM?@FgC~Ukf@dCCBKe z)VmUgt3CErJ&Sv=^hB^}`xX?`wJn{+5;TP0Ne!cLhB#k-fon~aO;Y{cZGs=uDWnmlK0pX&T zJ2@_*$1-h!l6|GHPK^Y^>{zUR=ZRsJc-BFmpIZ9lX`2$Fq8GrqozJ1Z90)5);Gucx^3x zZrOqBSi6u3E^Q!E=Av&&=iT5Zg~P5)BP@b(z`iX|}N&(=%%Kehvp!)`0( zdQhd@O*%V9uK1AQv#|qZJTBph&V4&OnWM7b+4mNwKOK(RGA=vM*TGyhQ?3t%t(+G| ztp9nszsm%D?jSx0Mqi(mbYw3gH!CUTgKBBx2-OdUQ!;qYk;6m5-30ku2D$_DbOt9sM--S@+w!jZ9 zA^Pffo10xu2JFauYm54PdhaCL<7|($s|I~U`xV7 zl!%f&RQ`czm<*mJOMrR;KzL%=3{h_G&6hz-=cf{2;b1DGCq>Ano{(V^>HU_eE~ zypQ9!K0G{EmIaBnBa?{lB$KyzS7zR4sR7#cyv@F)xlx<AMRPw`%5A z^;ELa=PL(*`->ry?_x{P-8};EWgHu;oWan+s|`_awT$M88PVM}^@xKb+Ld0h(B+YU z9Z^uFQ=Gs5-@g$S&SuqrxR&eEL@@?RIP`T z3j{p(7-ZE!g~tmmMWYYCfgP(IgEUF5IV;43%ZgxH zU%O9Mc6XmUF3g3@q>1F^EpHPdrs5tPYBtffvzFQFgwK zSZ(Q5Q+P`a<3n?f_@Mc>gq2Xln?f(8&I!NF=m-3ZjhW0C>3oP9&4FDTxdN@{8{6AC znQ*S*SrLbYsM@Q`S5M1W(O}yY)XJ(-WlCN?e(6_36J2~Lh|r%tFuceJr?YLW=xN7D zXzS?THfDc&a(r~aTdZ_rr>`R|yA4;o69`({ z`<)n@cdM9;N)Zo8ng{mfS^1=kx{M+6>3~eZ`ZT_#zB6*awV6@euNg@}P)In~2sNXK z$m17amNlUGvQF@Je1h|(mk<+SzHJr5(*8e`*#7|XrMWCh$lQ8wEtu4~(cZ;(>I z+@7aggmjlXg2A-3LJm)7FYW+D=MO3RV`pX}3e$8P!B4qb;JD9po~Sx=NbSfo`Ki%^ zdpI#U`x5Qi1@}(uAO}&{9<8)KyC*KpO`;mSYh|EVXAq)WTJ}pBHeJ%xcd{)hl(q zjaIk))?;2jW#zkMO-CciZjv0Uxy)hZoM0GKtPh-`>zuHUBa~V$Dnl}I;axyoo zZcGc=5_KSy8_3R-{aw{-ZJ`y+9#>bhtNpv0Vay-cV%hkNlTJ#VM1bAq;sJ!d5XE0d zPOl}>e3fYQ&{fwtUS1Yn2uNx(jhczPG&$&@AXrb41o9eS-TveJ=x^)tp3)3>ZLJ*!VF zz!$0bLin1Wk&=0o0smMt2`AR?yDy8DdN_d%>)`}ORX1GV_cWFwh|dI zHS>}i3G3F#aAH~^&n?ce$z%q&^x2SfISuA8-NS)FQ}^w?m+s7x_||T2-=vP)Ged0t z{J}fFw?cPNK(zrw134M^OBj&ya=>8?}+@d{1nki_scY1qEQ=!uC{!D}l9m9K%YkDrFaLi}FT zFT28+?Ph0%9IHQq-aH;RvVtGybY!NR5n|EJkm=Fwkhvk`p+G|GkrH!9C83-qUFOLP zien>8wfA{>-*)S(B`>e)oU(G5j`4RvEw&LW&fau4AiMB=ote}uZ0)?zw#EIu7S=rQ zYc*l!?#4tYlv|0M$#nAzm6mLT06Mg3BUb}m^~7%%AFWL12HKEvZ8EjKe}#f@^#=(K z|F(l(mNP9zb&Ai9%>4=e6p^))o7sW)Nsd3Y+ogz5x*DCuPi1Ik$Q_u;A0>28n%qj;UukHerMmj_gns>Bk@ULkZVKIFw&bLx0TL2R!eA(o6p%q(`Fa^ zANBDm{L$o8HmT=fpOG{Xa$fvmo1-o7Zi&~c=A__s;%$239kJs(XLn$@@6a=3Le9Yq zw(+d_JVb6r(f1fU>5$`YdgN_deDbrX*M)F2G4Zob$du;MP1B_62~jl z;(tFAnPHs9)YPqRG#7VvmMine9$F^x75o4u#tTf$W0fdqOhZ~Rem<~etUxLF4;M$P z?61T4g7-Wt3*24|5}25;Jsi<}j$Ca3yWzx~9&o@5e6rYZZ&FLrYS_ z=-QSbAb9K%tEtElRnbzwkwFoxY>!Q(->4fvoJvdO_KQT?Tayq^xY%aCP^x~SkSm9lQRG3hMl`&t=^dLd z6+N|Vy5o{?WyEk z*}HeV=(RX0hh6t~`7b!?W>t4z=sgz1oUMW;k(vA?c8v{(m7F+~l-lk4g~jrJCw~W( z0I2aO>Cv^lH#qR%v)Ci&DeALxfS(jloF@yqds$lQ=)7KS0cq>tHlb10Y3#Wgbmzx{ zxvam8EAmTuhokkNKz`ifN^Q=S_>A?}!U;9toj1L4mLR!`Ul6it8ai5e}0GAxJS<^Bo(cwqpmJNkJWxn_=0fi-G$ zE95z*KpDfxdi}WaJItc|F#@>fXHyk7$=%gkAZ&aW_-Vl9tG_f%l=5c1Hubgpzl$?EHS>P!|Q=A!ifoz?n?r+a~vVH!?1g0_z_Lo~4e)WWE=!hj3C|}Srb$!r9FdhL>q878&H)on{6nGA8MEJ|Z zo$YNKx;W_G6Rr_s$qh>3)9eZZnW_+W5xqkuVL#S{3|~`Hre1u7MX?OKY0S#-AOt55 z@m8?jzP>9bSJ^D4J7cPGB=$U37ZIK0T011i6F|5cUA0s;8Q2vb^Vy;M932hFtP1e8 zF`wI!jHIM^%T@hAZvQ3a?)wzW_Cet#!^W+OoH`mIY8>2?X}Is=bq}RP5<~zkbAXxE z4tbiQHWoM6V6=kAxV;(Kss|+8qV#Vsxs+jz22JI#O`H|zP(GXcsvJq`cbAp{h9EFh zElzMm7Z6|Jrgj=cP&_0c|7A$%Q=oF(8P65DV>3B)sy!IQ67*$?$>GvORn(~|f#Kkm zd6m(>3Z32g&wWbG%E-(Fhpz>}&$Ghf|L#d0m6U%KIU>FD$1?dIqU9?6l2dFQsw z#TboI?kTO&Vtj;-RoNo4cMsW^!-##rV?#dkr+R2L01|*pewmqxat6uGVXySy z7()Lic|x3EAWQ#to57Z9)KW=C86R0Wl0e(>i6Wxgb?sQ#!R{<+!g*ehuu5<$|J;T5 zBc$i4W94G6li+;{mA~1K&Sv?9#EsX=(siZR$UDDnNCwc;Boia6N34bS1&VY3vp;y) z&k1`#AuaMF2M6HmWUr@M@cq(hheGk)nV=CsoS(SQ13nmE?VpCPiGx5xc5?j-0l!|x zbC4rS`y&=5XQG`9?~pKua%i>8s@Axt%BWUBj8!~$HqR19+4#lvKnGF0A3Td?p}#(? z;zAZRiJ3}YrLV8#DUC>d!CU`KaU%=K=LjJv4E5X5=Wg^T+42m`C&RBY!QQl2f5v)~J~fHJtZ(jC$dU=~I0uN?DqkKPc+V zBY=mgr|I8HiFnC>v03nTWa{HT3J+632YwuqzUgi8XN?(Eg~kUgq3d8GxNV14%O>K0o&(k9uNPJ^UCfBHrw-e1JBQdF$!D&=cE@AJ zsSqabrZM-Xda|7S_m`Tnavr~5&N$F}0>t~0Y4-~uy4Sw*B7R5sL4tz+DU-kg(BKfk z4?wGn24_fK82LjHW1>$CWXGQ+@Irb0D>eA|8C6YBbr$X{mpvY@uA$vTT&M3&i;TRF z@g)upW*%nTAc98C;325IMQM8$2lem25q9KlUa3QW7UYRs5P9915MI7<@9yaI*SVV= zeH8K40fxk6lcFtCTHNKxf|ea@q%Xg`NWFN=m6$}xo>PCTrtCo&wNkK`vr~5+jJeHJ zmoym7C5>0I8gN~Or5K73V)!mj^J7sZjY9cVtNiAp)~)rXzbG@GZYWSNSe|ljgN>6# zXLEVs2Z0f0A30tbDil}to^CnN#*0ur`8SNKiyGNnMoc&V>ACgcTN~|v*#JP8 z0=dLM5ctVMDW;z3`YH;!&1|Q|I`Xn49gp~+>?GnOLN7)!+)xXYYD+UYkfou-KIo&4 zF#8)9P>_Xv+gJ+6eX-SuHENSD)7#%_D=5Ks8HFEMNHVzXdVMX^#Vib9?qK z9UXzhdAeYV<0iPww;ac!Bc&o_FL%8p5zkX6|MfX`S376+3qwZ=i~d5kww@ylfQY0Q zI8Nl{f8@Ph@IR|b!hFpHnsD%enqpt9Y&)KW_*BU?%g zY1K%p^we1Uojyl6_=` zQy&*=q4%xmb4Ii=@8N(XNEue2bk?ktc{KDuKDV&vg9Pci^WvlzTnI3%Rv?2HyE&m| z&vW)b^TWtU1xu^5=H_OgI9~5ywp{;=q3o8060cm&Bk=v&pmoWF@{Jdu$x&pF;B9|j z3x?~(MD=P*9Cy&2pY^1oz!(>S;AgWgTz9%xLL#}?XK#S5O_etT9MEaTOPUBxWvfTS`AR6sT2>;TQeL;pI_(zhiK!9<> z#;yLW^_AX_mn5RQG??WS2%U}aJC)1lV;6NtzPF{KxIy?=8{Qs zi2&7&A~m!B%7hDg&-z>TS+pigN*mLdZuwW`3!r(cCg*GR!v_<9KZ2DT+oG%VxmCkf zODlQF2pVD-`6BQX2vK2+GqJ{L5`|s@zwDqg!g~+u-UdhoS0PiIzl+m%Tp!iJ%*~~c z`HXXBmFu25sV%8zJLRWUw}Z+kd(4I${(KXYh)T?Ez67G)>^FQgnA*$q=7?68hx^78 z?JImFD}0(<>gvAu^pNq8koKNOGqK0)+jZt!i367u${r&rR=g-54HM%@gEU5p)C?{@ zQ=lkh#z2!y3b-W1lMBIL!uh4X3q@6l^t8Er{xbvRe-@7FFe+>2`c>(c%+Mze$cqg> zUyR&rs(mms#|997Pi8l7x{K~de{);^wt8<7*IxJwAW{9$NxGeyMgRiJN8hn7Ob5Ay z0!NRYMaq5qz_Dg1_k@aThQ!)UkmqrMKZCxWR`h!%I@4<;ty2}LIhR2KtjOh*O_{gA zFtN@pdSLG@7?%hR|AW}X z_DmJ!P?=BP-EXJ8ZUzJCDI6zO3JuyUq{3YRsx4itX{Gxu+&%rOU4e6mgR^J+R37Tp zd-IWl`%MoD1_GXi6|`dVvL87WP+y_TZfQxI-z2zmZiz>%%J<+fQ zlk|KEfGx};%w=ZnN@ca#!XWePsy}-uX&-Y4KYzNUYuh8%PX1=?-#%#r#jhaEF})@} z-aCIvbdDLIWUJCJTfqRybuRnb89^*wNY^WZ_Pdq}PF@91WNGu-76NI;>y-h8Di3G! zjzsV$2MT2-lISqhKEjvui2F{p${c?COl4dMM}9es_liS?(D!u{*$`{J8s9==*wVOG z?_#;pTi04J;#tnu&w)S_orVX{aauoDw~Epo>Shz#omT(FR{8zCv`NWTZL{C=_ZLhd z-&Uzn;h*hGb6UVgR_uRvoWDEU3vSI@jkpV07|w^i$++BC2}X%zN_Es+epHn*qpP+G zJp{?Q*b&7wl@1W)`K&({O-ozZ7du-`_NfPYlXms2(ScuYPDaiSNbx>+vCuqGM;dzW z4>_0@ImMHT;(%xhh)>ckwYRepxWtG-g^u>gh!-)Brso%fL+RZ-#0jjSAU%2FZW_(+ z?#@hzlV#)jmWx3Mf1}+>?chR_{-KwPuyD4IDqD#zS4j8~?V;!1(Ng$+z25F$5^R^0CIos|;#(1K;NbR@ov+Zm3M;_|81#N? z{ig=^XU=m%H=f!77n8C>^q*xXqO7$sfB)(+-UUmWxr&W`A!N-K`q>&k+hpSH=d1Pf z9o_?wmwcGfatm*1p;uaN$~Bk#byPjRZ7t1xe9^uFV^qBlMLKYWsR#mAz4-fhzvXjq3@?6D`+u+{k4ZE`E(b0iFb%9o2 zoS})(qW5>LomKuiLlL=!Q}d%l@|yAf5`IVdTfNmlx+6%5PKaRb9?tCFVYdNzON=!L zs#Xi}o>?x~dc_CA=!chO?2hD81K8+s9AjSJ110XGq$wpAzleslaQQ+;XT8Zm*zCK>xh92aqz1CQ_D5_hbfId z+|I&K_ENvoHWL^*h;VSw?4TVov6%fsRf}~FxU&&Q1AFujTwC}Ze2+8%b@#TvOi(dU z-4snrjW|wbmXhIOijOrs+ZjylUk$UiwxoRnve~fdGIgfhGG=>Uq$cMD?=WZ+H4vcj z`qQbnG&G3$0@bbtf57p4ED6^;KKVRYe?sQZ8rU02tM>MVCIq{+JMp$Zq3G8?ixQ#7 zMxhS8t==gsip4U5JGlaCWS_Tn%>VZDa=vE!#4vMmt#OVs{Gc*n&boZjUdAGwl;mTU z4BnS6LP%LYWK5C3sk8#^Z}a%5DWkZ5$Gtr*+>}JYtWnnFZfWweXfNc+gZSi@4>|0# z;b6qcsa?HTAJ0v=p)MK*T9973_|muB?)8}7`uco(p;^vO>1==fSs%a6Onp_MfA7A@ z$HAvmq%}0}d(~d5E{3_)bg+L%3E8y|=OdReO^1zYS(fXs&yA(1y?N$ab;lPb1ArSr zjR6lH$k|gLwzeky$BMRi1j_;5pqndY0SJ6pfM~MX6Kfftzx&Sg zS-cFp1iJ?a7Z3u~)=gRRI~u~UC8cDpf4cR4$7IIfaDv|t@UmQgu41E1tzAzN;Dh={ zxy#h|$5z$fik_=g+>Pm?x@6Wmq`dpjcy-!zovc|PSfeesAm*$kO+{tnde#DWCUKo( z4t}rovXWr~9-HX~xj5!j7zk874K7y zMu)_5lIhlF^>N?hIa!&JN^#mPaejgD@6bB6TOfney|^hM^vlpl9nP)ua1!*>or$-_ zOT}+t$irQ=(g7q)DNhfvX0WH8WqZl;Ihr(L28#(eaj2;&Oq{xcXi?qHfPD}H3F8Ue z4bEtU>W;8bx0jsG1^&bW0$ed(tFKM(gdL4H2P+2tx`QnHcy^hawjnRrON%5&vVwY>o7 zw!=#n%dQbU!t2c~R&ITw89@a6eqquTr$++$m)tSzpRktiWFU3r`un8 z2cC83|BPe^z2uPQG<9tp{Yd^SPw#uY-=mGajo|t1#Ms8cTi>UJY`Ac^h*Cum-77(+ zPCuH%Ik5AH=Fo&HlhGJV9noonm|R8Nxp*D*VvgmoiQeyzN)^uTRP9AUUt0fT9QH`& zk!@nN7RIlM0EUV-E}s{a8_SD54k2`=XBhZF_TkOu!2?1cfuq&+w?ri4-wKyCD8|fe zMpfx3MZIX5h6)Z~P@nhr$gS1KX0vTx*f^DaCj-f_7EP~=jgBDm+nRJgx3pbeD(DQ_a*-aGZ8J!2^vUMHUW?G$^>abF>+>nv)nPZLkXHE_q#z^K%-?z zFGcgic0&TRpMR76L$DZ7zHLSdcOD{%sN*kZ2ViDFpI% zq2tPrLNM|jVP_T~q|PXQv)KTAFL0E5Vr}FJM|dN;FV!speS_;_d3cMUpy2kW1gk4Y zlX4sx&hD&SWj!*|G!YAXRS3OZ>Cz8e4rckH8{~xYZ?}8@3ctR5M?isldnn0jrI9{t zZEdxCoZJC_xw8CW5yK({h`!b#aFDwTLa40F5r)ObOw1Lp0v#5}Bt9he301_+sEI(4 zZ*zNUl^V(Gch>PH9>w%3C)!qiet}0}oFX$0Z}ptx)&iWKQ+MIg-EmgWK_KSlo8JW( zq|M5H0h~bl=4T)vVcG+r`d--BpPi{DYo*vg>|=@nPG(XxQc-#PFbC+`$df1c2(W~}N&RYmlOmZp zj{kd!+Yq3i{Q1DnH+IGKFE3a~z|?X)lHMQ?9jemCB~4n>OqwcU56h-YkYr91#m0y> z9V#gtTybgJ^6c*kSEJapp(^Q(x(NJzx)w>!6cm9&_+lh39JrY^rdx(23Ibmn03Jt# z5T50N3tVt`xq99XlxRC7FcyD7cqKJ8Hov(XX)4huurvq01j%nUCYdM5jxMRFW+vC} zck*twnE5C?SM|HP6)tGzAzaQP=4q0bo}$@$eaaHTPIGMKgvYVb_nRtA+gfRWg+&KJ zEjp;!v@~u!YTpF_l$31|mfSqW#x+Sl)5@7Rk`Ri_l;>W&#C4p!lGpG0n7iK){N4IN z$sBflcuu%R*);~u%ecOG)P%)}*Hj1vWQZM`h4{H)=e2GR2(|{`cuxEn=~^H>@#P%; zX(K1f{X059B*7VDdimT|rsBRBfk@9glse}r!t)n7iE@U@r&sl^j)8xEfADRB>ngbt zEIBv?R|(XiZ4O$hi(M}R-~DMlamU-3^0f)2Fv2LqQU9IL*etWT1eC13vL zQz7*A^F>qe%%ibPO(A&^H79T9qw1-FT%3~Gzy|FY)V;P5|bMRvD8Ki5meqyfC| zZpOd@u4^W9KY5~hkan8!rkA$1$P!Gmi;FGs90$mk=hkN0$U$0E0s&9gdB#0api>f| zoKO|%sLRzx=(@x6pyE#N-{EIHgr*)q<2^OCbLEVw=SuBy%=r}sT4?LiEgqpn&J0w& z49HhbL7!TD<(?AS9Eb=lO<5c}ECG_+86Ln+YX_^|LAm3)!23danW zToceKkrp0Ol(Isu{yy890XY12$jl`Xdp9_CdX4VR2b^nHNmj z0HKQC!DAu&?cvllMB3f<7Ad4&;l*eo^$!RbNI==^xS`7Rc2&gK0wof3@#CB(#cu&V zFi?{M%6($X!hbnODtA_yo*ON$^OASEmvQi^~exdIX*EWNlZ z+=Jh9@BN-<|2})p%=~8Fncw?PWnSDcXTG-LoxadNu-Snfsw2r zsOjhMnm8Dek>DbP6oYcdvB=Plfd78ghuEnu035S?2nFSzQ9TqgyAh1HnIKM&RO*`@ zW$g#CX_+KE0@flTP|RW>5LBm0r3Q^1E{p&;%I+si;T7jtf_EkM?{za{N7L9gy*F(> z@5WqiQ{!K}9p(2e@vhsu4)&1Vc?`sGU=I0?$>$cKV1G~OAF&(FYn7XK52i)OC%y#) zaioX3kN~mbBNOsKch0yyqnVE~7c{Cvk>O-WMmoNNwFA(43a5{MoSsNKehX7{3zT%V zrySng_-)FL2kPTYY=tp$Sv|EEuG4Z%^`}H*Jo&ASW2=4G>cu#^Nd-Ajh+aKIQ%*+NK9 z)ALxXf6bI0#&)d-WNedcxCFJ)2335d=yq-pJQOyl#9e;gW!aHF!+$Q#YdI#%Dv)b~ zhS@lF+XXUh=pQc#-37g7B?$5?d(2kAp4*{yPn=}|1SXZ)EGPv3SXd}E+GeCCJ zcW%<^@&4J-%8BGn|66GgS`#)!;l(bI1mOOMIk1EH@{|bn{M*%pv?&m({pekp*^SR}65BUi>n)JO;eI$J#zbu-7#mUaN*uC2&?;ZIi~!oa zJ4x=P5a|UG$G%v9$X~%*ga9ny7;u5b`hS1b*D4j8S+MG~=* zT71;?t=8|4C_JUPVUYTP4M@>*)zAe6HaU*1PiuToV*z})2$=pESm3WAc0gnxDB-Ji z`;FdYNT)=Y(X%TMl+&sT^1}8j*B)6M{=@^>+1ASx$gJEOavxrgIA*=Bqy^I$=#u&B zjV=Ok1RIHWJOhg2>IzTdkT1$gsRRNT0VS%08B=^Ew#nT&p`{bKM&2`=A&GG9jj}aT z3lD3pf%4$L7Y*WNCHGwZ(n*_h7e59geA!mx(;vuI^=Ooxo#G@BSkjj>Rf9DnmFAe4 zpQjG>)U)UXZFeFrNV=VyqaEbX2aKQwCGp%0kHdG96IABj6(z^z#C!bzBk17(Xb-S2 zq+T7-DXy!@l(dH-q z?dz}WZh@fYoJy0->&Y#nike3e6zTQuyi;4;#3vm0 zl63?{d|Tzw$k)XKSjblGe?Em09>sNACIQ41A>G7J7sxr&39>__w66B!D3jpNdJz@r z^jiJ8y9iRi7?s@(6hqwGetPhj9MR1dv*5$45`Rb}NBpmiH?JeiBDhPzH8A#!kPOS& zQNMfwSYM2|_QO~Q6%rK*+8AHwvhO=Oye7A<@F2s>O53{!EMQ%kTIU64pk|tyoYxdG z()nMFm9e8_N#XH$BVK=ctj4r7N}(Za)!d~sW-VFq473j2$aFM5-9DM~m1n_xrhILa zHMc;Gi;-Y7SD6u#wZ?<7BoY>S2X(fjNV12b+S(S0is;PUx{(#hMV$YbR9AtuIS>QO z+ht}_kzkA+KC&Nog=XPd(8s#;_>oyWWdfUTDl*4FXN%7K_cKiBx~ddbLg>@M{4xGD zl7KQv=2&|q{R@+lm<*|GpG*(wnI-|78c;3UnYs>gzjz=ji6uY=(*;cyp@W5yiaHl# zlyB?UF4*17xV(@Fl?F!@VjN_dzU<#86;CdHEfgd zlo_L-Pypm3IJmz#)^Pq$x-I0yJ)aF16Q;pmr-*vC$p+SklbNH8o}v-k&_i0TqO5K{ zf>>UFL|y3_ID`w#^8Du@P(;^VPu5-khyzO>#rvpNqMR92EgKz=l3re}EawonJX#9I zVV7^65|Bjk0EH!5NhJn4sr(PJs0A`8=k!-+Hg6-!0|bdE~{HTE10(lSd@!MPP2W78=lExh@=Zc0&1_RvYNoNJ==B z@1tt=;g?B1v0Zl1Bh0S*Pi;^PTu z)MK7P17IGqo|4~TXqo4YF~0$iingS8>=6g|ifQxVsY0)(x@Mh!K#9(4!ZTk+vv=>_ zcrH&GmDw+D;OAWUun#$W@;x45hKhzqrMKmE$3kO?i4HN%C{ZD_U^KfH>~LJy5eo1- zjXjnvz<*7fFSg&t!F-`n`;8dP!R>+gt3cG4H_44IxF;!68s|c2s}E9o_3a6>{hy1w z-qW$Lu016KUWKZ9RVjT(Gt|ux!hl#`@~D`_hg{07mu9J7y)mQLJ|=@|!8`W}?p`yY z^ot$jmu!30Smg=@LbU5qlYX6DSNGAfWJJ>Y(P9v9L9af$c+d<&l(6op6nsA!8H%WS z#%BA$j}JZ&kVa38S3?`;#9BtZ#-(x9ba0MNZwyWzND+S}(AlaSHyn=)z>*2G7MVaX=Gt-lG&mS!OXJ#DYIHDo9MaMh+_mEc9{p1NF>7(e zg6h(^@&z}{gRZmmOm*lzMlKc?E;`5C4>h=eRlh28mgJA__nwm@4y=|yjuhLo)$z?j zHLu%=?tWy*zq2i`HMt2FHeZq>wojQZvJN772q-EcJ=?qZQDt;V=mPmPb;IKe74yF{ zHs3=qq_$bEU94W1r{8uG5N45eT`@v+UAZ4}!yCP>jFyWc?yi;~DL2}|EfMJLFNX5m zX}s}V&n$3vmk3DE9Tyw(d)QYr+LIfFI1LzlMUEg4uwilC4L-MH1DKs3ZYJq{L6t;(8R2B=mm7PYY}RH59}#E-X|<3@Nn{GC|3XFrQqi#0svx5 zH+&VE_92`+oMD3W-&csyG&WAiDQl5p_0`0 zC6;nKHs#OI$a2|u){iasj@uEwC0U-l=OVsbcGj?BKu(I`rR;K!0EO979u1XzK+2Kc{IJN0=fqTVbz^ zZY{xW;ioYuTuT+W+xn`bd7HfVzN}#W86gfZ@?XV6OVasoc!=hvMiPu0DniO9iaJWA zaib`nH5cvEc`LWvnRdYH;`oslCbRE(2B>u-Xt^F+>gT(FBjOgG zo&O>(wJ<2!)ELjq{#Wa4F7D}C#y6V+GJR22eqXz5xHYqoG0`_VgW|kKaX-@{X&chT zW3fJte`YyqOx|uix4dJ7Mvb{LU#O;#O!h6k$6J0UV$^u^Q8%ZqOS}y;7^)_}2?Nni z%rJOUUNps|?}O)ni8%a$)8p!`>WIFnXNLDvpSa}Ccm5>*C=DLRgQxqYKZ})I+UJuxFcR(^2y=Mzas z&}yXu@t5B*)NkNV{9Xt+M)b@DsX#E=N9p}ZU5%O1ksa_zPt3UC(-~X$QYYSHm)~H3 z&?uU}HXMM4`0@QTjGAEBkZYBZ>glW^jn7gKKud$(O{5@n=>HKH@Orf`2R#1sDIuoO zjm1P0@jyO#;Oe@UrzO3hoLpSG|5D;2%W$~QX}u@dqCAf<1vt^L#e+MX&WC*Cn$$-N zl7jehA*CzGV7CqQgH1z$yF2!dUMyo)C4HVO7nT}IjOtAYlD%<#bntF$UyhvcD>~7q z=q8`o%0O;FxhVH;fLP+zXZ|0hYhumk`wirXyD**DqWe7j@D-M0rr_!~uido%@O|g0icH zII)79=Zep(g;2wf@`41i8y1{M?AoHySefoIt^{vSM$im@XZ)mQiVsH8E&}_${I!8C z1g%m&6?s2Hp`b=JBP9N07p$Ns71o(^UOlY^lrjlzRm zw^{9VhB`sNDhX@-K>r~!k)(BX#t}b)KLG@POm6@6<)LuRz24EeW$I8c5&k-4$ps@| zh)2P0l{(hwwn8Jig{ibTtnfU!h#GqT}3~rJR?U&UdX3kIPl!;)g`cnp+ys$hvTf)p7^V= zP7}e_cRt}x3FCD%oIY2UXc=faxRWwA$QoNg?&|4Y*77p&B;inIZ1CPkt1eU@xuVUT zWt~Ys3O>npA)zxMVPhb0>x#9K#eJXSDZ%GXJf-9e3Kt%-B=mR&rk{*DYC2|SqqE^2 zq-Vt8)_GA&U_KEili7SVK22|HSFq1*g)n7sFF}K5)j>0#+Sl!zeJm}A&7Y-kN=p#s zp;KD?`%K=sZ3W`lH? zS-F1|mj?B&kL8{_c>R>Ze?@3<1SfV)eqtO|QB6ptIyFR6;K|KGNXNQLz}eYQ_M|9$ zoRq?-2hV`ETbnCHqi`YY?TeN&D7PZDT7Vw`7+U>An)W;y8dxx+diNUg$65VKP%~|p zQFw%rP&ss!#zb4213))p=;_q_6Z}fiCd$r>iC`!0H6A4v;aWaR!v}fj%}gUy?Cz23 zsi|u1scKEFb?fNi(r1YXrTz9bm#CHL``c|V8jcMk0vvx%!E^BI@G*F0$X7d%nybLm zUqj(5ivAASGN~naj-}Bd@jov7{qAzu*5!T2nW?L#AunIASY~7R&^&rFTd9C;!O*5C z$DNm_fcEW_wn|MvnyJCbGfG-jiLchUwVWqSQ9Do z=rh1qMhS$0@oF!7MyXlek{P6E2crO%W~`l-qe0JfO_C^w@<} z_ttWfI%Kf@u3GMjd=vI<*7rJ{!Ep3jg@R_(^yF1$T99agVfXGufJJasx%TlTntFU)_Jx)1_p_Yd=B30GG8o$#VC$( z9?fubXVvJ-6ez;0^}!yx6;{%-{@FfFE&uTT)A7Br5y0NMeTMJNgwa<%ipb3DSoeVn za*oAalNfTu-oM)Ig}F|H;$M8n+@o#WbiVDhLTlnS+!|0ltWV+kwx6MNw4lIiA=@zC zI`HJys91RO!kT7?_Kyry6>hK|B`T6nju{+sFi=^LHC8K<7 zW?@RHh_+s9P-93EzC4Iq>bTB!HfcU$nDKFLn(c?)q3x(-T2Ymk=ebPP6n?YPnIk>( zg>mmZI-4$p72I}G6K%Z_9qhIc{2^k;$xWkYC$ynT(ZnO|Q`n2$avQ9sfOxnJ;%Ajz zNA9m8bI6QkW)wL>0QC<58=%ZRBK(@YLb`q*O@1_xYCMY@CuXSoETl8qAWXq05=b%Z6Rb_n^-CN!- zjT@NX1p8IwEUHq0(%he0J&i_3e|5tf5qPK>vqL5q(9kiB#hsfRXGX=n-3(vR^_IuY z)f;VY{>3iT@!YKNsi-Io*YJ(AANOK$==1$&Z35(0VBgnvs?=*zJ6Izu%o{tEl+CKZ zBqmA`kbM|ySKRJt4ji4#kM7Uj82hgDsH$#n_Ok1ovg*}rt)T{#wN=qKt;f;j>S^(j z@yM6aMVacd%)da`v1(dgxv}kZX*IJfT|={Wi13D)O!pQV2N+U=F093xd#L(-pvg3` zzC8ETyos%ak4%yKD#%iJJAWK@%#+4}2V%|*({Xl%-=_b7_(UaCX?>|}1N&|ge%O-Y z^c74=0P4u03@vaej67b={v=M8!FWyQ8r zFg1_Hn_g}F^ZtY{n_?hJx5^@-B}d}*vlMP|@tjhQs;gAS1h$@gthiWXt(lib9+9gA z#4K;8Z2Y%+*r(YQ{hm>$#J0T^y8QM%?AzdKpD}GB)v|hE5+`cRo0rBvk34|HQYLPAXtVFvxih&prbug{KS*j$BUX-KSqj*o;5z) zpy{q*_$7o&YS|kj`NyHp zF7-)G0tx48fnb>+Wtom)dluK^Ga;$26%6}c$kL@fmILou?oFS7y*Ziosnto36P_q} zuA+{hPuUU}@{|T0SF18m^A((ct%c>17$O)ecqIp)Cl974_rKP36#HS7dTGIYkpiDX zXRobJ=&FJpJV7N5h@KO43q;D^CfQEzFD*zT8xGbqJ4iK|IGv|@nE#4;DappWoU3IS zR*U0}ZBP1k@eX9@h~T~B9y4i9dVc%(XHI7W>WgBPpL1HyvG0U^ROwazn_GBxQm$?i5WnH6sFv2Imer2!s~R(P{h{7q=hx9bJ@~!$&yZ4tO6}oI zMwBDjY`x&1?`7G!RtXNay60}n7j=H;Vz0wTuwo}s85G4igSU6H`{Z5NOU!3aK7m|1B1S!%TvsQ5IaTOTYpU=Q zfBF*O!mHDh1R-gALRzfOS0ctETM(&Jfb4KxDXdNY@bj}?-e7>|qR!m}d3iP=ugo#J z*<5YlC@^BRDW%nTcc5WEdudAJOR0yjCw|dG_S-jbamSmYEQ3vTL_C zU(>!}yBaTr1UD0cz5t+N1&q#9&>B+ySG(XaL&Sf7zkSvjVo|fku<|&0dPB#m{nL>N z9{1Z3;g`_&`7@H)N57kWBvz|3jR0% literal 0 HcmV?d00001 diff --git a/resources/check.png b/resources/check.png new file mode 100644 index 0000000000000000000000000000000000000000..d49e75136289885665f1c8d83c92f0f324b7c910 GIT binary patch literal 366 zcmV-!0g?WRP)@!ITyA)BA=bILiK@6MSEEHq1Q-aswDdZp-{ zRLZXO2fx?pobo`z5Nn>{7{CUA?nBRnsprfK2?8v6hFb{)u++@61OT88a3p*g3EN26 zYT@Il0WI9r!f{OsoBzh7#63+Rk+^4=brR+Cb0ln)@SFAvTL7S{e3U|zaH|e*R(9mq zR*LjQL=6H;mhAp&nKW5COzf*~$L!t=@RbD`TTxftJ!|QFpE0QIR&iqFZGC=o z)>O8yKXnpPo98g>%Q7iy%3 zZ>1JYk)L~mPigXjUmI72b*=ap>@6@epzLkU`=^Jm-F9vKn(x3iGgEC<75{`td$wN> z#CmzX|?eSbx`q(79;+ahwH gX30a%UDN(B>3(&~*3|i~1oRq%r>mdKI;Vst05!38ApigX literal 0 HcmV?d00001 diff --git a/resources/fonts/AtkinsonHyperlegible-Bold.ttf b/resources/fonts/AtkinsonHyperlegible-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ca3f613847de62b8da83d2dab500ec391c8fe477 GIT binary patch literal 55256 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 Ca=@Jc literal 0 HcmV?d00001 diff --git a/resources/fonts/AtkinsonHyperlegible-Regular.ttf b/resources/fonts/AtkinsonHyperlegible-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..b3cc4200fcf09d600eac74002ab4ea65b404a536 GIT binary patch literal 54348 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 N)RR(%LbP=Ae*iZmk*xp# literal 0 HcmV?d00001 diff --git a/resources/fonts/BricolageGrotesque.ttf b/resources/fonts/BricolageGrotesque.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9a4ca271ff84357bb8f8830ce71b85ea3571f731 GIT binary patch literal 408496 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

)8 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�d5Y2F7v!O7mRWcGA-dkgam zB$XLud-yx82*o><&O(1lvb#&@&qiTHBEdG;@QYu*dbA_V^pyJ$ez+tUola)g8br2w-XCMO!2&3 zwzv-Cpqfye0{~1}8Rf}j?yp~GH`;j9<|coz#LwTk90d-I6#_}G6SqqRBSxsddk8TR zidzA5g_KY@T_&rZo9BL}$0ufzjSfarW?kIJ339AO3bq``!{wG1wJ|mYTMF!g^ks-+ z6x(iIw(uNlf}h2?u1R^Ee3tn=t)dZOevsD z(yC2fb7tP2SG=eMk5fY?qc*zo^Bj5+CnGeFo}CQKak93#B8km7`RJ068sJ+rWn&;Z zojHv;*=3BKD{cx)sbXL@Btp)m4jkkyz>0`Z0>Yj|XQF2ghG@he#wPqYdA>#F!HBnE;c z-C%{J04=xpygEZm92yuOhafFz80_sICES80QJ{fEBu{8e>qTS}&TElhfKJHlTs*>= z-cd-)fkB)}OiE1Q!sR|R_WXRkAZgR;=M?%P@8s;n;JBm-1R69Jhto{bIiDssJ+0Qt z(@Av#ks7VuYRNAxbm%C>BzgLC0%`~}E~gapw zq)Yud=DS<`lC;#Xl$QIo(kjNC6@KP&Gua7QM}S~mQJTv{(8S?q-t;fIQ^3dMMv%t= z#gyc-* zl@&`6BdPUr<}c!uRYyfWq!!>6OP1D2>jGur@I?Qqoma<@k(okmQ>#|U31)2;RWX5- z1QOf0TvN1`v66yg?3;qApDrNHOL}c8ITMC&EliQg0GCCpjb#`X>9Z5F8J)$tXV>0K z!w%L&u6W8-Mq{@ZmbhGg5f{mfhx*UHd#<9wnVh@?h0c*nm#g_sBw?Ei1jSIVh29L=qkDg;4G8DI#Rr(9e5SO^!P*1b!)d;^#TGklN zWos}6D-Dss!J*-CB*Vkd)Dl?oBq18q_Vi4oWlKqARaKQ;ISWZ@uwmbuM>=APRIK;- zThBiH@WWe!-+STgD36o~cXiFAIO#faX#ew%fB%Ump5K2O+ifn?(R6a>liy@}?598d z!MDAfJn`zmJ-__!cfb4OZ}uL&hC`m(Z$JZ@g2LRYDF#mbse5E>>C z5m8zsSd&nzgyxO7E)pAhjZ%;`Cp0R9)8n$~Jtz^ooD9GYy&_ATk$(;*joY-1$1IQ!&~HY$4zM!O{| zhHOOEkg_(xFRGjmHgpch6tJ~PiSc2PCeka#hkC=**Ua$1K<_v`KNacgJbQ5Wj-98v zI>-2riLUO8?;Zg1zaL7j#69OVC~B^O_a%=-8Yge+3@4MFmS%*SH!R?0&WaOlhU2IInW^eGh#0mZ~Kc ztYV6*Yu4X(2NDGzs>MBb>n64pi#OcCw!Xq)b0+#fbS#4)dr_i1K;b(5#i;@hRijT$b61m-Gf63>Wt7%yJ&+iysPT*n3K>E1q)vuL zrjiR*_T!9#Li>3Squ>u21;5WI_(Mj)ABs`1tkf%1X>57EvdWt5dT&`pS!FGbo!ZJW zABsY@ywb`=C74vc%2KMk#4@oco)Z>!oxKzBZ8tUbWYqu+iSDMi zpMI1<2kW9v7!v~+b7e~aW)v?!@y0E0D9&!Sw|dg*H4=mdV!ME8lkEPW2_+kfFrygR#IJ=~QN zIf}E|fvcl4Ol-jh$i_vUi^w1H7G)!&2#*%{S?ZPMGbYnZ6*`AS_yf$=yo#F25`S5B zp+~HktY(YdyMVkb4sbm%+$EBB=j~h!t1aF$MMdov7G5b z@tL?&3uUR4m}E5qAJNn_q@Ee>>DYnhA?|Xh<>0Z?dw=zv?|kQ1`Yw#v*xP>N#Jlkb8iSzv=)%r0Vap)py#I_gv9FsL>7uNVHxMa#C~&bvFBZvObbsWsJbBsGmDd9hpT)1bcLfidKl`K>@eS{J7!Lu<-4thc6v{i51IBM=zZbnlW^dE|#@jy}f78u--mJ@Z!X~ zCr5^d2D_mUK6}2KHEh>~bBCno10OP(^iqs&-rqkIj!Ak`TU*~$Og7VY6tRi65z#}i zVhH4=5yG1k2}UCVsh8B+3s{Q=4ZAN18rs=QyT6y~<6okme_i>#5B$3L@!dmwXsEgQ zxu23;^yD+o{Om_Rc;v~af4O5fgW9XFKKIj~{`A)`J%5zm|CfOcz&4=>$EVA85=^1o zM6(vZ)2NMBv3|$T1PDaWq@NMOQ&uhxcX2B9V2SYKvXaDaAPh*>V!1_tB1cRPza5sn z_-ibDKrVDcIt4-yylJshz-i==1i^Wh08KD=>5Yv``8?MoZ%BbaBb&Ge_&zLvEDxc8 zDIzeORYC|h81az8$lt0S84a^hDV>={tf}j&-Z{d%{<}nh) zSqZL6fV*oS$jCq+&hftP?%uAB?kg8BU23CC_YHI+hu1yOKSk!k zhnP127ztn;s^f(vrG>;KgpN7{+4=553;u`FqGB%|s)9upOZ5_N%#w9e;DzBBY_MkC zHVW3XAfkMQ0$NAI2F1jvii5*#Hc*)42;y-ky-jorM(7MHJ78cYD-eZxN)(3qY!iT9 zQl+9GHEB|@Mn-1RI;R!$avC0G673`<+N!vyOjgYF$ZS%c$85>PS%kvm68h&#`sWh* zr%;$&<5#d6)kxfXQIWy0Y*|CYmXCerPyXb-`#$=SO`8Bh2U#3Tnnno|g2Z>@w!B4) zy1MN4*;%Xg`F888uTD+98)<5ie!vGe2c*r7(&Ittj|0-@wwc=7FJI1NcJ11^^XGxh z@*l`|@%ohfDfQL>GOZgk5HSc0kE>A{4Dy7MZyP#`-QDBk7x1heJpS&nW2a7?U=Pz% zP{0jR3@t$-r&3~D9-gVO+1a6?mX^J*?|J#=^YRK=uW~7%jH|-H5~s$enkFMqcka!`oS(MMkU>b4I4L>mw)!N_ec-( z`WfrRgVHHphk3ms{R&pq?{3{lp53=d>k^UMYy8Z&Qob80-w*jE=~l{jBjvk+@oyvL z>w^H7kpuJ=?tW_5VTUl+YQ+>1j(Ok{^pOlp2Bg1exO7sy>txMpwFtbWgT6`DdyHzJ zVuyv8RbP05j23-zqO-NNwF~^bNPa7FkP=U&Cl$8R#T7F%Mppq|P?jz;L6J|= z!oto;3@b7@MiLw<^3zbDCuawo^oZQ1$iPMz#Tu4;xDs;B6ITW85Qngc3d0@AEeY}l zVuO-m9%|D@78~=DJRi`<6Tg9;oufgpY4{f(sOG-=a9zrr^k%gW3r z2oeY#GMJGCH*y9MpmaJ;rcg28E3se{WkMDtQ5Gy{1iitXlW&{rGrue9Z>CN`IDnEv zu!>Y3k%OWPYMez!L>mNLKq{anHaRpQl2mg4hf7N#8gVYp4;CUV!n@huR4KBNv+W23 zfN)(%x`n@bAItEceP09-~$c5s_T6UG$YH4qE2n!1hKiyZ!wu zTKqX=j>V7f#GlJ(wD<{ji5wN5?-u@&%Akmydt0_D$ao2bu82gZ?0Y)BZ7bdifE_1xhx_leL^7a%=8H-V~&}m+g!~ua#N8i#znrgQSIC- zzERRQf(zm6Rce8RyrP89IUsm1_+|;+1Lisv>WVNkh>XeX=V+2Dt&$E9$mT7?)bQ9| z!-EK&z?JmODn>rR6kWw#R8fmUo4}E$RFVLnmshZKX`U@FuaHC~u?{i{mq&I}D2ze4 z&Xmok1~$rHQf%M0nPBC;^1tDA`$uJa3v=|n0b61HiQcXqJGy#LEH5g(_g-JnaXuDn zI(=#PJ8wVw=&s%So6blNq6MgrX68FPQ|YKdt*xw72OT15T(3o20~JT2$!Y*lRDvQC z)WQm(lp>LBj+jD`P{d*r1d(INN~9UAY~(KXiIQO#4I$SSbhsd4DRHc-t-byBwS?je zq`Lz)UtwMOqT1TZiu%GrI5YBt4m>zA?Bsg-88TyF7Gz8*qapjGZi6vnOs9>xmcN-Y z`yfW}th_gK}9#VRAo-RexVNTC5p@FwBoc}flIi&7(_e4g}QQW?x(jTQe< zbM)ldc1ooKk6tUQzafbcP&L=a(_g&Y3*dHq;PR=%hmIaOc;qmXXQNtlgTTLlIZ7QRWin9$ z=F^d$o69P zqp=dc)eax2#)NQD4Wq%PZG$l|;f0Ka;~)R;Du-$n+Aq zz^ExL)!TGdwG|Gc{6K*U_YQbO9Gw~sq8D~;!emNlaAX5jQYTZ|+{kh*!%suY_$|g= zhQ3m_(r7GbRN8FbMrAxc*N8#O8B=~Ev4)h`#Tv6MOt46Z+^4VkC0d$N`V1}XbF?tQ zYEt(lHlHu=&QILAdGn`=97TWk#V_*C-+u9L|L*Hw|LRx2`VU|KJ8}5>SN`U2*`$Ag zk&3Mm1bq-&oEhamH^%BN{^A!Goz6#z3+&ukMONS5gH5b00T1kX{Wq*RU)}f4DZ*wa z-`Vl=pa1+tR+OsZ;@fX;+yn^b3&Bm&Bhn*V{|j@ixQJOeXR|UjV!(dDZrdc^Bv&Xx z@7#`u>7(+k^5+%ZU7rA1vj&@XiM&XzVqeQqylOaGlYQ06rA+4mX?3G(pHP#_Ztoj; zbqpkDFz344y1Kf$nTgI{zI5Tj1y)0>oiQ?7&YW-IaQ5649`zE5#t#bov{5KX{T}15 zE<2W~y|}0ihA8F&H20X2db8O80}hySwLvhmsP#w#Y0W@e9#jkr1WO_F`Ta&=UC*UU zJu1~E#`jQYKg+VK?P6Jkb?e(k2i|uRJh%+Qn?!yp%6S(|23%drhH$|P(nUFTSvcG{_H*HD8W95t@N+{D(3@H>g=UVN?CNWs z30*qU*LCuSp`bPa)D=H0kVq*JtSwk8{}n9srV28v7FSi2SCCG)xV(b?a)a>DtkE6~ z=<(WMY6&^p8^(!>EWtmg-vsD4e@?&o0{up?27Q5kv%J!d9C@X)sH(P_7^us)WJ#$_ zYhj=*^Ll)XDvEGDl`dUeyJFS4WsCg&3V^CxRS+Wy_;WxzHyTPAZ2HCm3tS#v7k8!7 zoAvVC9HXS12oDSrj96Y;boAn)(hDt7y+>%(dO`B_v^2N%ZCqcWPfniMEx=@bQSFl3 zw+!^OT^VE*Rwo7;MqOH5Vm6X7m_@h=RA@=%%G>X`=bqb_JCePJiCP`#oS%49|O>&B{DYKSO_;^c(Pw;Irp^`P`I*j3`o~)=a4F)Yn*7Xk z=)zmiKKtyE;c2;K)lF^ZPMqw(<54@KCEri2M#?1}Lv>dHEzDHPnqN>>2UvW~5__WK zsF3ZuI55yV7FPw`rw;AgdGOTEeXkv83o$;2+77<<^M`Mz8wfe{f3bY~nOFB+7$khr zd+e2`SbB?h|CaXtZ_4jIk4^tYlGfSeNCerf=$yz8qe0KiOhIro&65CAkr5U{EJULP zsxu!Wr&-3GoK8ihsj$o=A#|rQIW*CX(&aUMF|1 z%Zf;p*HZ1kzpQzM`PH@c)dkX`KrLTtBcegvU2isN(kWrE$gy~#TeJ+b!z826C}col zZl^`0EWdT=*xAExwH|A}JWM)qIx^CJp=JL|&prR*zUK3--4X6)r0MkGSAX%uvpZhj zcN{91+KT!4ip2)QV#ZuqX)dl1K)50=V*tv6P77Ly`T6*w@i}JIT!QtrmFw#&YL>0{ z`SbO-%`|AXloa`wEnns_d;CR$460~l_3DP(Hm$5*xw;NFRPA&@!E|>z-HiZ@MyEmB zgBy!xJP9=#TN$;^sGpqV9fRF8Zw3~SvxWYiG5u?d>EBrx)4xjV{_;ZWUUv)8SLTVR zySm!#^Q~NY<6}GIug2`8EyzK{gnC0m{JMAM!uysi3IpdlaAayp#q^C2^@{51Mny|Y zK;6ax8pvPwxO&sD7@O$q0o&E z|CCgtilN*n_;Z&s^W5+(@F-7wW5fSCVSZVNE+Jp<6~@txPXiyQRffA8oDJ3uoI5{o z{`}xTTifVZS95dMjn4|f1#?A;1_4yw(8RFLMTSr9iWRjLix*dvlz>&e;R)ig|0nap zR(cN1O45(%IgQKE-W98tzTuI27~oWF4CYX3wet=Q%|vga2+-gD%~zj*%jQ{8ilwpS1A`pq|QXGZxV|N9S?z~ejKI^9P+cj(;y*9hQ#4Pc~r z{hQ)xCY#S*v#2WFs20tu^hw1WIE(Ji&N11MUsSo+=L8=YgW@59{#UBeN>+nWk%&z6 zws#KA#%6;JlQZyqOe)fek%1|d#c9vXBxlBn;!PzH5XRs#In8sBiGu$h56{hI3>1}K z4NP}VOmb6zE>>K#Q_^XDUYXzK7W2NEU@id|Fv${KSXdP^2pS7{IiPwOsF_nqrp9O_ zkp=*w)mXJD?ks{|k+?B&M@hjXV*u{VI6ptHfX~?C#Jial9ut=U>^ZLx;VXRYFahO^ zv9*lM-bEEIh022S+-`B(4S>jifSL*6A{DBT0c&L21|C!N#H=t}5(6^^8{RtH-hcS5 zf%Xf{-EqCFG(@|*Ti!%$?@0eRiQlxY*zkpuZ|~gk>tDWdMy%Wlx z*F=bZ*|jn-n~RoIxyq^*0lO-4*-7ysJk5RLu*D0dQcKa9Sff>Gv=ebIEy=T!hNv?K zNe!qfSyW$KwfNSX3K619x|UTg--JQlu&lzL$CRy5y6ZP=*?h+xAG_<86)SwYMzg87 ztgveR-FM%8`x;bGK19>Y8ghw5f78+P{T*G@AWWZQ!{ec$zJbxn(Fu{%l1PS?W_)@= z40mQaJU%1qFhNp@+|2h4`tR52zdxk^euMt|4f^jl=)Yg5|E_m{>MYAg#no(fl~$CM zRjpmU5}YU7`kNpdDle~FvkKry-SQRni^m(O zWEr@qqlb>=FJ4q$;_XZo;rPG{-3k6UZ-#MoXCVrl%$ZMQ(3v zb93)xXsoaA)<7X~6s6*gH!dag(OCb0Tyg8Ix2`Li?>YR&#f#emo8)iE-!Sey(GM6P zbmpxWzVk6Gzkinh=fclFbNu`4>wjk7`*+P_FYJ2Z2V{T(ea^oA-Sq9JUfOkXfWF*& z?8qki@fQ_G=&S!6$fqh%BG;-#mJGl;>?@5bGam}w8YrHdn~EvPWtpEHnwtvG0q4&c z4D+*sGKP%J^;YZEt8-H+ieH5C!m zW#rZ4dUFF4#)odXX+?#XbPbKhoJU-(6i7n; zM1Ma?(KF$cL7S~ty@6DmkSyO5EF7O;C7?7Lzzte-B1zIwpr4tDn9UUx3n-;)UWBa+ z7Uu`N2zkDnSDD>6)!+0XjNgiix(yrZOY=%KXf+5gLT#N_;H5?4vy8yJ@FGm+ya)|Dpqt9{D3;gc^u`Q($kJEo+{TQ_yJpKI+V)>oIha3LkEcaXv}coATOz>ARQ z_bpnr<)a_{=;jrUWcNYpXaCjYf)^o`-b8frU#+S1cOD7+XkP~n^~#{8?SXbdhz>#aVIdLM4vEsss;Ff0-I1${&q_V&UsyCT|7B=57KW{3mEHNoEI0nI<@C@Eq*1P;Q^~k%C#K&>=r?Stk)b%H+9@*4CbB#)oiM>&51y`*y$l>z5B- zxY9P3A^11nd#U-&XMXnVv%BBDC_NppqcJ&lH5|Umzyk&-=R{B?CMWs^7n}&$oJQr$ zM9kv!rwu!euL5pvVc&Gpj-hol4CkKX0)h-~l2&y=_fZ&Bd!VRUH=# zEBzwk?lr`_Pq%cBZEjdSFBi8Rz!%p=9)DKX~L|e z)59JmTYQUrmGztG0k?P)T}R$RHME6Q*wmDnycN4iqX;@*dG?WQ&%Uzlksth~oj({m z_3Y!{6yyS*!GE?9FWKGl=Z#w*|IGzaHSa$2t&ht807_{y|M9Q9Zq2?vDu0;Qy|54n zAnx+O)@3->(M>}EiVz|S^U+v>@3fmN`Ks&`k^sZP6c8YN&1+W%*R*wxjCZz$ zM#2LkygfHMFcwQrv|ns%Z|@6D2}$~NY;2(Y;^oWaT%0_9q_uqzvhAio%{4oRYdMi6 z#XBp{^jx?!EdfZMSD=##Bm&e}VhWnGM1Z*ht?b|$z0*R<}MKpKmOPj&$_^JEJP4)I=nc zMF|R4`RSN=v`iusEO}*5Yx|y8I$C!g6U_UQLzhmUIll8}PyG0&I}SFtO*guyE+5?g z!V}+p_!qx;{PD+wkAC}G5C2$t?D{85C6&gkV>H~|6P}y{RCIhS#>nihTI|71!4NEGEK1V4W3kzU;N*v!V{%e!UhuvJ z={b+mb9T^kw$pQf(@5K>_-$g9wYs8+FpbLu1ldJ#7M4`6Ucc$X_iS8OyST>fzV}}7 zbNAgJzUz+7n>XLOa@o>FrNxzX^)>Y$x_#rujUV}xB>h<+uu5xCCj>RfOavT)#0U<( z;>3mav6w{UKgvzJ-6V5l!Ho0*qr&;&p|MK zD^qzUN=_y)Q6ix6-Xl1t-)Z&S3Cu`0$56eCuKlIUTg9v>K{@ zdr6ND*y8|2U({v?d1?A7lf3JKvk=P_JmKVb| zkv*#3dF$!G7M5m2?RniNf6egV_g^~v?!}hZzV{z@$o~tZ-rd>Phvaq4N~HoXr;%nN z^Z&cJ_W+NpKJ$Oi^xj7rjq0e`vMqNT3>b`US_&x;NV4fSyUA{L_vYD_Ezf3Gqa!fs zkO~P9N&=ySfq)Gd_kw$uyR6ryM`uB9*f;RIx73TT1RTAtE;PTBAX|fHIpdlvpwf~dU^&&74S}!S7tI^ z0FVp8g@^!~7%Y9I-uZJY?1;=*l>$FoFuJ@hu&~mLQt`|3cdbY^={uL#~;- z{HkSh%CVrxQuPFaWwi|p7KO6eh6dS5dlv4{vS3A3xz9$fjnNbEVxX4;IU)LZ9d6rn z21;xIv|hK}rjv*&q@oS8%58aJ%4P(ntRV3u-?qp%V=%@hYqI2(i+eZd8<8!f*swO_~1A&hzIw!TO{i5r~{WaOm~d4-P6UL%tJt7(9ApU?|F3c6?+ao&n&QQB9D6;f;=4 z)n-I0YGhtBo81+Qm(`Zq{nb@GCtg_+D6gzSKHfzCD{g2=STdmQ@v8liFJ7lJ2ZLs{ zITFdEuw7B%%ul8Y!w0s#y7@FBB_(hW)R&hdlvTQE6INurzaNawW-dE#Fz5PuN0P11 z>{w@KgpnRlC#nY8yOqwczH=}b98`g-jSCS)Ukqugb7v-WE^RED)Fl|)`^F+egQrjX z;odhIp|yzu0JRv964$!w>&vXI&XNU7YfG#&2XD}0BNLbAfEQZtxA*ngr`KS6Xo)9i ziM_PM!?eT`w8VZ|;&GW(VIC7Oyv>!51Bw&cP!bL7ZVN-V!C3~>9@3xsSs;jB4 zs1)pd2^6zvNZEZ3pcYMhN+;FS$iHe~v#8^K*1S6K)1*dBL@g-d$WB~QYF-WGS$<0z z1KF!gzQD7OB$=I2v?sK2J^OkGdd-x>Q;^gGnGn12Vy?Nx= zh7Hd?yDwyimnP(A?O|=u3JKQsrDqXU2g&T+>>G~pER~CmkBky&E@rjlVICNR*Vbrs_&}rTbwWaiJED-6DTZWp zwJHIRbvzc0oZa7*(UHVj$O~6`KBqUtCekFO3Q4z>6f0|aO|UG0;yjz*W@VLB4%anx z=d$7%f>$^iPiA2g5?v%&E7@(1f+? z9%4OaCE=ME?HVD=Hl@!eNu!-mvWd7B_LI_l3@Qths#JOvjh59wu3}DYC^}UDjk({(ZTcW7teQG zI8ACcGoU#)(aRLsJsjhI!>11)dgIaW+|zpZ&tBR+B1MpaY$Qo&@~6Mv-hRf^(b02Z z|N2LNa1(9tUy3(Lz5IJkxaEd-+vYbt_gJlC5vvsrbaExK`N2UNfn*)l!cH#vwpaLzY&&uKr zG+zTu7=ZVMzbu{>_&Hlvi@}!DkS$3n{Gt%!+LtY}%dY%mSkFYtkZfk{mSAzLwX3F; zO-(D^*{oaD1HSy(-K$x4{B_{2yV}0@%)tnzUS!?6h;?|_DmzL*x^bdKs+m>JE3a=q z**mV9-h6OtXZ!2t^Q!i)yY5giL=9@qG?mD%#rKJU;ww;sWUi+p7`ueGR zTEjQP`VyaAuW2mxd*ngm6Q z*>!Bn;=CfHS5{HGH&eTk5w(iiT}AD#qIRJcfyb1T5r#CK>{^4nw6vmT?&77(mtV1L z@w`xJP%ZX& ziaCP0VJwQW6@sB@*PN9rS5{?{_gHDkoqJz<{?*_7`0llMc?3-IA8xAkI}bkp>f7x& zO84w}^ik^b@A;b9hW@^&SRJnNhS^~Y+IdAVMxgc{2`_>37lu4+0xSY2qkusJo{g*s z;KS&O+D$rw4%8h3o&gKV1ZyMBG^aP+1Cj)iFz9M3<~3Dg`!RV~by};}fu+v)RU+y@ zc0}EVB$9IOovnvQFahAnH>&bM*t)iUA;odQC{HtOyz5vmui( zWPn0}%PVeFDz(edUUlP*AN{D^9tz1WkE^RoZu;0wYuA3f+*3}rpsQ})yt=Vua%|?l zh{MLbY;&LanQ^Oe>zSWvT1=+L{0~0(kZH)5pCvmfPhV-hp5*G)xD`ruu`RJJ#YdX+ z&#h%$wFVdW9h$?3t}j?kHh-{Ac9Nx9VZT(W5(y77Ra4+mc?@S{WDtUn@$%|&3~v{d z)^QeK#+mz80XvHZsF&63C0EDwc^a4JbU9t)a!;erUur<8bctz_V5{t5rMp!#dv+Ms znwgJbZ2{s+1g z?YF(&0|#dApMJW-M##q1@$}PO7f&ApQ4W7{Ny)R%l9d*`+Uvbq_SuEPY}psEmdKYc z!&%pA!d~~)q8qxdqWpKK$T>>;;q;0J9w)ZTdU^58y|(k04Deqod`)+M=VRZ#0mY-Q z78ieC*L~~pVN$i492tE^KN9DL*H&k0C2&G)eqy3^uEkPc5B(Lf;;DOsp-%`A`$i0g zo_-`fMo0VlCNM&Dq^Jg^CCs!2X%XX$@UU93k!6e<2la&Ub#n=F*jBIp_{V4NN!^q_ zW1|^s>D(;P+$`rWsfAVj@WUf#!}N~iThAV!V|KN zD$8WtZXEQ}_Zm&(BC2f39E}Ec)#cCuUAeeMOi)&J=o*Q(QF#!1&?~z@#=LGURb#6O z={{3wO(?W(D$j2ziV?c+>-$0Uf{_G7voS^ z9wbz`blJkDs*1Iii3v(#s-+q784J)<;X)19Y4Bn2g{ZH z?I(@KU;a|rANJf>RdpkX+XeG!Sh?U*;`yqPf)8QutP7M*okFoSO}D4wNf^@4rH7CH za4o@QAr$^M?RSp;sg#NWQvqCTbX{E{QC=R8%Qtu{ z$R?=rmm}{|Ig7}CU8$d$&Wp05hk;up8%cr(<1yVxz{e_+(n#v^`R&^YLS!6{^Lt-? z_0@yD4oAoNqdRcTZ8>z7mSoKEH$u%FAm?4I?8=Ddk~LePd&NT|)ze9A#zh=|%wavB}5kdR=ks5!P+dAhgm3 zOtgU+rOYYD_dAt;he=y|MtM1W7d3!F*@yFc!XiUh6dtsb1J1wqDq*=>q zzV%Cd>%S=qeAikYPvKv`M9W$@A92&tpus@HEN%fGDI6}BU!If&AWd_5gQ58b!~Eh7 zAy-Mfv%4P~rL(gW$FC4sGG{|p?1~Nb4-9m6c3kL;Bdrk?1f06?@4ouipIX(F%jx}B z-2RoXeC5+?n*(k)q(YM2ftp1+J<>Zo9?j+I>SV{E5N@)}0^T5%m3LM*Uw6xgZ@qQR zjkm78azRsLr4PmeUqt}0y34ib*06G`TKe5bo_O)~HzJYefBnF{-~H})@B7(ni(_KG#R=Dps0aaZSB z{elL$Rd*|9D%sC6?a9sll*g=*KcK!SeW{Om!5p` znU`MCXa+iu?}O1{_o4Qlk&#oU+D{76>HfV35ANEwngOig{e9V1= zqY3Efi+(-){9*X>31tJH3~`#6g>o(6vhc>}8IH5?vfyx$3@Agf4vPI{11)wK)7~#7M_OW-p|q7(RPSXT zFpJ_csjK?lY-l2@t|~&iMc)ChR%xlzErVal`|@hp1A*%5Tu1|+ zq9Rc0g|6u`&(_$or8KH(ZK!Ej=T%jl|P4Fx=h z_&8Kj740S@<5W#L7j~kgonZB=tw=(|1Jw#Wqty$!D*p1k+h}&h`^GXr5TnpGi?eD1 zVrL>$XHIobXelMj`*K#{4|ExSZ2RR~C(d-@usZPahze z1=_FZq=Id!IFf5^Re#h;l}*NQQaQ?8P;pES^d*8LJ%e$s>=H-SWWnqu6J`~|9kCyn zeNrQc;6Y`;LNxcjoKxEFZoOe)s1~Y*tYWKZoZEDngX;Q?n|phehHz_j9)3g2Lh(sq zOvN)(KB=@;Q}jt?TCa*-=901);T=+RV*nsDX5HA}1c^wOd!-z#$w#zPzKUQ-kIo8w z9*oAK-W3cPc@C+msCyKxN5XSt1{WQZ#@br#D($M(#yt9R{(xefy-Dd-y1R|T`QgM&jsr&3qd6oKwzIeMTaqunxTEANf3~;<`hHuwuE(keS*8zcAA^2^9?tsMER84RrGN9 z{M0q4!m04)*Av1v`wgApoRM5tyJ9-U7ImKN(Z#K4XIPU{OetMTS0Mh$v|d&xuPC08 z_3KAu?<87aG8MUcOkgh3yibpyuPFPP7Oq{mwla0?6$n&Er%tdqORnMSK0>{GhI)~e z#Tu?|4fV2`S+e%F+dli*8OMS=F0t7?BV6Xq`FsMwmf=$;cMF5tG`8byhTyNF5l3QZ&p4( zYv#UH^Uye&r$S8SrI zCpqkLH$tWJ2mBVLrD07eLe1mjZMD~4d+V)$f52-P4Dy)V%?0zDXE!u1U%aTcc}^1x zQV+YwvQl<=?78uSp=w8y*E=v$pRO!l4v&$mcVwiO zWd`&&Q}?YJpD)Z_JO`X{(0U8d9G(_mGTF6%@3CVe{dTj-p7eGP9Y^WOC5B{?^lkdS zRWmjgW>*Pww@tP95N%=!E&fVce26v?qD?HN#p8c^*VDpiC7aHf{CGmS>0@4tLd-)t2#q4Vcof1TLuciP)$Kl{WJ$}3?<{=|v=$c`N& zvSZ|FUVJeuY){_y_MiMjz*@U@{rJbf{jCz+@Wx#*!)RZ7jUo3R!$y%nR}zUZi7sIz ztRQOTuFYxNVK=|CbHJz_9z3wG|A=ukakl4dk6V<;*cE4;Qr|qT@#U;6zUqA;QD9H(8g>^9sF;jBGtfJBt^|M)|raUl)DO z$5_b8z}RyMYfrGu6IzTgrB;DBPKJoaGoCTsg~A@6_U+d|C;U(08~R_(KR;3A=VAQN zlcd>uMjz`LgKitj9(f5&wvyDRz4n5lztERFv|B$}Qv#(NUg!Gx`i7+0r%ctiH3W+g z=QE?eZKl4bHOg>eY2lP|yK8He#V`?xN#TxY_S7p=1C_Ru(Kz072Gtftn<^w_L99(3 z+^zLw!G|`?*D}l&uep?K_A|~@am{|(R4Knv$~BiV&R9Gig8v-&2@~q{X8BW1=RI{} zwVn&Oy~p0%TNo?6vr*L!B)@lk;eWK>O%8I$^6VR7Q-%FEuUEYOq*XP7pc4&K2e+ZHj(-mvY=_}Hf8&yl0zXVc9 zsQu@MO_R|~ZX%P5Pu18`N@yi#FD&EYXlFDoy%QI923sS z0PY>`7l$1Ph(Cod+;`u7 zPi#Ec^_kDyvaALpsb=ZTpAo8YEdk5o?@b?+1kqr}>zg)jLP+Cv?m=!)H15v$!GovH zTA-T-3#2nx6tW0}2_jg`E zPKy~nn`<@0q{FHWKD?QSDN4736NItH0RUY#I~q)q`VQ6r>O!?Dl{$E^3(Dc{-Yy(S z7dzkc_#(AXk@a|x=OUGbuo`(J;=|H2RDr8P_D&6&vUQ z`bHpTQ+=SCbExDT8fZn;oI?ezsG3&9(pYimgY$9x)YeXSHCuZ-Au2X;PpMN7CYqPl zG_lIDMb69A)U?9 zrygu%uaImorwMm##Za?{?SKO<%8RbTVi0*ZFfQDE2jLq=TrYlGifX z4~=YJ0pDBVM?Xpwhn)YN?)6pK?1i(sq-uH3WtptHiIKzGFT(184;itR;^jbx7oQyI z>_*-omD;&;`btdKUa8S-+omgEpIBzkp1uOpM7GSk^tuN7rDsot70{o0b`A|0Vno93vt}4n1Fxl{I`Vx}5vt;vDp}H`A4b+Uu zI&pFP;gJcQsxMXtl>XERfX41lbdB09#fs2sr7ld@7UTjnSE@8LD!^!zIxtPx8?IaG zEt}2&F19&FuGw6Hw7Kf{wz-+|pJ{WGS-sm-QlS_6E)^!lsd^~Zg>B>qZ*Ly|(LLEK z&z-ySlKPmrwBkkC21vI%eq^k>h{$rx)Xhw@TfSgP^NM+)C4|T8RL4vmP1lnxz^qzh zj5!JBXXU?Bbv4uSuD!A(FgY3UEm*sDfeIpNs?KIw-Wx}IPMnGkAKS$?Rd+L2-c05F z-j&bPo!nD@wn=?OiOH?cPF?v-9m>6G8_C}Dqw&pe|KLh4UEqdd_sCGMovPQH7#04v z&^0qYSO&VsjvVhFvc?t1Oufpz^t$CsLi1KMFIiB${OS57YQptX$2LMh#u}`Psmq_K zXt|e;cdbvZzU0aUT)*m=sc^ZMj`zl~;pnLoJx7a|KQ-Ej%4I2|&CH6cEcE*;E~D5P zSQ^HqwyIh{PLN{Cg=r15&U(i@qlQDh8}-90wQs(Xl3;yO%nsw*_A`-+tKPG!!foG+ z@nP(~aN*3`Z~yKeVSUn+EF_Kn!;!Aj`xIm6VB#cJh%r~?D+E!|^7$>5*2ek9#r1QR z-EyZgb=HCqHPBuyv{x2K?C;tlzP(y({?M8~EO<`QEGfUSSWtnqR9Z_xs4cXTBy3QxXgzLQz&x z1wVPHhF=UaEy(qSBgBQMxe+hof^D&{A?tbC?qDUmM$rG2K|PtUSqL#9NK$Q z4dR?65>X(k9oa|v#A_P~8oc<*E4xRGYSGK=`^8?kv}2P1g}@x0d{Sj=m2&=PkTU;K z>`p-;U0?!WYMJO~^6bHjK7fC8cru|@M3a+)T|K;ja`;eRN9Ra9HV%4_&^9F!B#~HV zL$q7~+Y#BoSy@Q&RV1la=9enh<9Ztz;ANJ&wIIHN0( zMfLmD3cuM_QHy&Ed{a3oX|pT+=qL!|Zz=UeS(zno87P9O2!}&1!Cq2TRcbd**Wde^ zzZ;mpvjsOc?v&B&j@%H@?J6H@nwr1wE6(5NqtQh&e|Ir|kB?p$koh~u{B5_!6Ec6l zb#S-7L<>tb^LHcjH<4`D?`+so+E6KN==bI_gRf!f-1<^GQ4pmrtgoxHSn8Nsu72_K zbraEFC|zOGRc`lHSAFSASE-i#u8sG6chg%Fa8@c;h4oimrPJ}MdGNxn106jYRve~7 zk;tJ#{r!iaC-UU=b_2;t@Rsfh>v!$S<#wr-?Dh*!J$2%kPWg4%v?~(XwQFRASB;+9 z=EkxbgR&%STJ3hPUhVa+o-V)pY5(_g?r(AK_tXCGr~Thg`!8w}%xf%ldb|z{IETmQ zE^Y8HS<)P^dwdc`aD#d;YhJQs<=T&Z^7cFa3asR(@3`aBA6s+#^5xfj^fRCQ#HV=0 z^2tx!`Pq-HT)v#R60$)uf)S__%4IXGu+`O`BDpm25^0f1V6hqmSj;5Yz`D$PNr~?4 z9Aml$*@HW%_+}3pHLDiSuhn(!K^Ez`XSeles^%VHNrU`Q&V+$!$;Brmn|bH{Gn4M6 zA0%DkhWVa}lY1M}#5IV=jsUNGM$?O7A?Oc#XuXGWs4W`!!mqBKS? z9NM>U-yV2VB15P6Ul$TYR^DqIk8atreGmUTbRlXqmGb6_(5zXi>!Dw--?ICdsH^lI z-n(u6bi2iF z%6P}elV+E#D6VL;Ib4<$oqnj}%=xpY;OIVe_Ux&S;iHq2V;v_?qmgj(q~*-HGbhfI zf{j}BDs4gXJn#fc%#MtG9qfrkBRoG9`$L5*0P(L3Kya(eFbaofAOO^na|gp?D!w_6 z`(ag9T8+iDD6`$?MuEs>$)sb`pj!3*Ab&pe`S#7HO&>QOq_U=7)zN@#7z+^5qG|+ka$lkp@7u$EP zfAB{SJn+EJAAfmAd*{>dytCzvm!Ey+(fjYe-}3kiFRXuk^E>ar;@z^U1P>Fa)BNxV zzuiAbJ!V8WCXp_fq)c3xv&!%w{~H+wg2Hlq^6lK_VBh<|St(Ybp;R8cDl_DLv%ZtilCWiL9SUx>W=LDF$seg3qh+ za6s@0mM@YJP09$XRfm*>c8XUvIy5klFUbE0YJ((ArJ@r#G6sH2^&(jD0ldjcSX;apTrp-{uw^vuUs|)g3t%b3`VOCNx7>Nc3#xt6v zCUpIsciy>nwzZIE@PIP%l_O&f=EK9xZQ(iMIFAet0QTzZ8H{QyMsgm0yY+m+*|d!G z%PSYxnI_I|`|WyVV4%%<-!Hdb#JO|f&7XhoW~PqM(}@=rZ)*!**0z2B?wdfzd<5xL zxqYDUWqbI(`=8r+hO;=a@fU&Bq>MFb`suz6Ico5*r@@m< zjx&3YfL-n#8k>ks=+B?mt9~CjVBo67$^=R>y~_8|7}#`@tR{=fHY*m&x)VLw?(4P4k2GO+d{ zTcM4TE?V#l_MPZUVh~PTJh=az*B-+m@r!4-9De7epMl@}>2q6l>^wQFp#rqS?R$Ux z+L-bqxm3H)nJ3AX0W{#qPa2$7Em(YKpuuZI{sI)YuVHbW*X52aSrSuA;7;TEgr6D5 zi4M*7qH*m9tH+hHlLZ6vRhOy_B{eO)sTGkXT!mm24f)RM>dvW|xjCd)meb-E(Bhhz z-I{4}i)eAp^n6^F)89J9^Qm>(G*(*;X$x~^)!0n7xjMW?y?4t|2_8K$n4O4@^bgt{ zLkG6L3LNOg7oPj>cb-w64ufVgr(S;LmDe||sAwhi`OZ7<`1pqpk6P4Vq@VQu@t*VN zjvhI@f8Wlx-`;ib$WgeJfrf+`Or(><*U2eEbJ!L%naD#SzlWiV9l#5h52Le*98pQl znA*NWXxpd;9S#(!5iP(pVq+{NCq{G)=(J`!Q91pkq2W6KW6ITFv>*ho6lF9LOQy)_ zjz!0#cwoqJCt#>2C4+4pfrGFXaJ-6XnPRO`ls#k4%8+X!`UluRydN-4IwIS5M0Rni zSBpBj@B@lE`sXO)&r`-;(x& zMVV>h!v4L7NQ0oD_rWfyeu6W&3q z+3n19?|Fy2W`S1P>h&l4rTCUHZ z3#;Dr@TAv@1|2=hQd(7F$R@LGUaQ^7ESFL!Dt#)Nw~`9cwwNTeq(-2wOkZRf3#yeN z1Bf|5ezODTB^yJXILGuEtR7O(TKz;tVU|Q3OnNLtXa?BkGIu%k4BKW}T3lmB*jRt} z;#jPsBf|H{2w*OxqLJ~5*ksyLS&4Es?}TNAhi z=Y)qH2WCako+t4=op$(*cKn(Cw$e!Cz=6H}3`~VdV6Bs}-s8LXymJcT<#XqThK_IE zfUwiH6TOpWTv=`-qO4%aRIj_rhQFtn-yQ3{IGEA9oo(KD0VUXiA&dEuG&o@|EX3Ou zTP%wg6TpN#6Inm_C+E&-nmwyhH#Ag*Gc#qc17Ewa&aX%2bW&G}_eq&W$rzMG+K?s2 z%IOziN&`(-FsR<)fu6pYHj-p6O%OpI9vB>+j(7xV+bFs!b+qjuZ97QY4$`)Rv~B#y zP0jOa0}ihj=G-g7ZD^N;Sq!#0%#{y3S9AKz1MAm=;d+&SeB>ix&GXNM_!`Ijt#ce4$Vg)sk| zzB2IwNZeJ{EN;27&>!#utXW-MWt{(o76YlAT)A=83GY*{So&KGRyh7|S5_k}a=muA z`>1duH@6tP#6GQxv8UH$E{l$|7&Md=yeIF*UrS2RTdk)Y!;IZ5^fzqL+3Mcf-eQO= zax<>DGLsx%xunG){l_>pg4c1Lb)4tCkitDY)tqM?-&-dmcumc`nwkX*5FVZbvY$H8 z4JJk>!jQ#hp_1wyRC&jTYidlUjT?J6Ze&pPg&o!xHFsvSzsAv$SWvN#D z8oo|zp0G^RCe6v(Ds#eWQ!>yxDX#JH(j+3dD$V4@vyvl7*lXCG6bCycAi zgOx7V+__F?jng@IE}96A0^)rd(5$u$qR)ltRa+IMQbnmyDBJ_}rno97l`3hMi)Jra za@_}RTdmd3|2yTgFh-P@V-D(&&O0spHTw^ z!yfJA{=;oVPz=zq@3&y1{0}LuG-;hKorW^Dv#+b`?BU&~ozBw_BahsoLpYspMx8nW zf9?2qdNN-a(rCJ#LgpNp7~!m>g%s7XTxsm=;)30gPVEdn`gBp zqoZ@fLa|X40XF*mhpcMt!~`=Vbnxv*H$VIE!w>)Tr$7Dq&wugg6VJW2?eLk4o#W%m zU17x=woZ&kK%quQE^K{<2z*#~;zU=x)sW~uc5KY6){@VNfn6B8czo-Ija#;C-MVeV z%NsZDK~aHJ8_|3@5EiWelW$!RPN3}Y$z59QDY)@RB54|5iskJJjapmu7Uqze>pe#t z=`?dtUvI~`?#OsNIWdZc9pv0}Lu+^cq=@=j{j-*amC~?o)+`71Vj7j3MOrmn!w+aC z&u)L|kzYRc*keyW{p?du{Q9x=2hLthIz8wdDbI)9aTwX3ZqHysmNZ=ZF|W5iYR12fZ@zf@CqDaEH!hf0DS7v`wKPyS6V)fKt2Bsh zac^`_ZE@mK!;vJ{CJT9UT?^ZededM=SVyTERci3cgM&_y=0Sa^i$Fp}Hb< zm$$SeSPeuvR8vz0+$vZlk9k%}7|$!*Ue1#0>YCb62yaLWiGM=qluBhYxmLg4Mg&|K z)7Z?gs$n9-`bl#4ln$4H|EkLuFI^bY^q+g1#L>6T4H_GlEMI=rs_U)@Is48X*}tEx z%cEy|hDQrd@p!?N0RiAu*P!sa^qq5_HDCVnmv5hE?Ab<2|B(r8BC4&q?)KXX1}{Fv zU}d=%+AJ+XLXNU|G{R;p&dW783jU?=zxI27wSG?z6JYN-F{+&+N^;a}}Pec_dTy;uj`yWV)=-Ze#s!#9{~ZVKyqhU3}RW^Haf zA`46=Lt}O_q1ccy%Dbwo9Etv+B-IBjEMu1yVx=dSRhN%OhFKS-g?UI%A_4$S5CsJ? z4YjOaB+@-8q^&vlo5_C5jrLsVi6n`^+p1l79V8nODdSdbdaaU?V1&{TFOC&5IY#LR--qw*7EtPQ0Hax*=Tb&ubjO ztV`WiT)4^6j-&6q_{`QGev2esr--GMqeMJCPe>-%z4RH;U;Y|>=9~1Hm+3RYqY^Ar zSY2rVSz*R;H}g=d(UnoWX2P?2v|?wY%{&W-=d8I+ZrA+#S7i}2VQrG0jKTjwE^s2H zt7eT}mYEzlgv{j8v0Vp`9yxMk{|;2mUs}I@{p(w|9~hwcx^`}U<-u=%``f06H}=ok z^Ic^_conH)qy1-(pJa48efHu=bmY0`HXiHA_^4x7yywWq=NcMjHP-m-PRZ8tl{L(1 z=d?059(m-E=eF+Kww|u}^3Fr;gS78~ z6Kh~T`P;%bb^Nya39RtHgIDr)d~J)0w`-wH`7F=Z6s{=V))wyM|9@NfFYVoT-+cpc z@UIk((4{`A+0Sf9k}Zy*L~J5TON~S(Cg}&19IMaCGcaRH2t30>u| z%p7>#P0=w|XQv$w^?O;etHrEhO)C#Cd%A8_eTC?-o7}^uYUdOU;o+{KXuy;ywP;yZCC!) z$C{P@47buQw0GfG_@3BG;SdL$5FFV^JHBL-uB%xWI&wl%e+sP7 zzhxrXaOBt-j2`{i$u;1z{-*HXx_j@vx9xV6S^pX?w9lg6I#m@PrXv0?D(^<9)8uvw z@BT7rM=ekq-9X6cD}{@E;!B#d)XbIPRfKEtQo89U9uJ6HGY%j(b7>J1ZigY$#he?9 zQ>+a} z6U-Orl@9fsX1RH)v$y-=dA5svgHfswin&um@jm5PxD_X3uFys)QF5BX-LvS^6cdvg z99@#3$oj89;x)fke8TH&`TTTd&xh%`Cl#H>7gEp;)+*g0qegHsyXd(M^?$hMUfPN4 z|8OUs)iir{V|_z?RU?{Y0_X|N1ZQ^>yRje`Z`@Soq)vis4xCk>Nk$NIevf#dazQu)JE_ z{_M-z(&7-)cZ%`lIT>4C-YFx?y90~n+i;l4up&dswU-Sg3?!Pn@BW;OBl$^|H6?{Q zZlq}EPvm#!kLQQ-rv4`5VSj$>)Q}?s4vF}YsiEfGfreqm z&ImI-z`$NW2t+KH_r9!m$?I)@@u<@jMUr7aypsJry*(EhwJ-Mc^mNepJNux5!d=kc zQyi8$|IDy-`6zWW0#!5BKur~^2Bg}1>g@fz$_MAwSNv|UyQXm|<6L!djQiaP_w`SI zmBH<{Yi_*l4&_nh0i}i8UGSj)48_SOmDiLl+%_nWD=%?;MR{6zkpKQbc~B>#;PuM3 zFt*@b3Y}CFp2rVPjl7z7hd(*zKlp0f|CfW|-^gHyD??W(hhtK1bu=UZBKdT_Cx5JX z8>C4o`Aohmf3SG#<^7t1nZ`CXxK^V5`0j9;KmL~wr*+BXpBZrf-xzP3qJ!J_93lF2 z{NUc5n_ndz{gt=2y!{r!b{pQ@x?_LQ5GfAGFZ|wsyy|@p$c)Z^c*HinJ7gD;j0;(x zEW_5i0$b|}M(l;yT30Y)Um>=ZSOf3fa2vR!m*U*j_Kv348Pj9Ey`vdTtCk09aLbMz zo8NltReClw!4QBB4@?|6eSGH@8ld3|U--&D{nvkm&HwdZ|Ma!L{VLTee)_x`eE#{3 z`4wZATtPIW#S=i-MvgRGnn(bM|iA8A+YsYe^7R_XkRwX8}d24yutx?r<; z>=urAOp)kt<4h?H8<2*9;Uvt0DVH4U=^0Dp!n%BNv}fAxT{c}8A7=JkMqMnUE+kWa z8Ff)sGkXCFtu`Bn1x+Hys!li&iP@t~gbNKMlc1(iv!=pXMlslk$Ju(H&m`Dr@{3&#o24 z>g!i1*Ggd$y_9PKQX+Q}I%S)R8Z3BEZ;={WKn*RM=`GlgmuxztRhEi?o~N3Z!RRG* z*Pv>-S6fPOc%$>Qe&fbBw`?QGa{T0hvEE^1U*8TJ9)0xZ-@p6ru=(!0zyH$*9>6S> zu%T9h97Cr>d+p%C9WP^=KKaIhg9i?DLe=$fm{pJX)de%3$QH6j6Mk~tqA0a+k?)6`nUr+PR7?$OP=U*|Ap7<#tSH=lL_h1SFy)fukV|IMFYcQc|;{#o4khOCTUNn-N zlEtXG&ZHTSz{ICS!-fj4kpF}M!D`JU!=p`4$LG;y{p3o79pqlXXgsjsLA`n;-rERrD#!Eh9bB=ZRNK}E$7kWXUP|7)>J;wdOLpm%>qT2UFl zMCi@pK=;%x5EV(fwmL|SDRv3FSG3PB-SAOrpj$x;0oOEHdtnX5sW+JPQ(dC?=Hh2; zQ_NEuzCpFy6fs4FsSKM0;uvYjnL=kF-Yq>qg7dN|Wpqxc17Rj0-cn!{H)|FlS?ami z6N;x^EH2Ph@`RB642GPZAXycQ8LO7k6P8YoRiY2>=p7>VG}K#E*Q0QZxuh6^Q8m!m z*jQimYt`f9Y;2f*+}NlR1Z1F@vU$ab(tqdSS6}`1YSvO;)SMg$Yqvc8G^zmFQ|}BD zJ}BO~oO}ZK1)nOK$Xe42zkTC{CmwkMZrkWkPtOo!=P-LESl~hdHa?kjd}uXsyVW25 z6m+kjavEBVMzAi=PrfnV5wQ#YJ@_a&7lUe zBB3C2Y(~~U!ssWDGg-zWPD_k*PD>723nDze5~SQs#<13C_Xv`l@37%|H$dQLcAOUk zd50*-pKQ-cVaZ9vlPcNcUa$Z}%n*+v`+EBEyIvA|XbKT2SY*jT6a2MAc`B#@#&4Zo ztLhqYfe-_rH}m&Sm-507e`tURXxr%#jp*1GiUHGV}mc|47y0 zN;avwn9_Gqo#J2ArIb<+kNf+R^14f}w$MM?bmNMju!A8Cp`b3PjPW6-*X#V_zezxa z-!&+@e#I@n$&aeKCzXQyCO@j`MwGPtCZvz5u1v|uZ}Ow6&aF)9Rq2!bf-+Q4iF@6p z8&%5W7x~HG{bC8drG(y6LS5Op$`VGbk`N(JqNB8AGgP9r`pOj$XDg;%N@o3o#}D1D zGN|_T$*w9Ur!tzVbYzSwStU?U%9)Tqr{pGM*(7XK2^3^ySM~TXEoOaEFyo48Ldi{> z?(I6GVoC{-4X_r)1nx!C4@yJD8cf4)HWL*WeET261h(KA0=9!^Ml~;5^Px2>Rx~y& z;_`$Rb^ZF+URTX&lL?-^-bq62bV&%a#wSv$IcZRE?{^F*d9ToX6C%%&0ubqs1~z}f zxHxwgEy+O{yJ$%+%GgD%x~SE#&QV_OPz@C|H`Y|FRmopGaG(zW&c302np#yxN^Og# zcb|4_2w`#y{f?(&;*=mo|Z0LSgIN_aa%m& z;Pi-X{i*Y4DV|50VWofN3hxTFrFb4~hW_3CRrxB;1J_B5W?!#%G?8g3o=2NuBtH^S zD39WKv>B3yWV8_FshmffVZt;)l#{1&9&Lt7Yh|sycIrGDC=cOXYM^I{j(G#+QBQd^ zP#!L59<1ea7A(GE@%)A$vJ9#VDPPDss_W{iu#?esR$aMcC%SriVu^#h_MYh-C->5I z{=AG*$IqW1$?0^u{HW^E;sHeR=i=$p=XxUXTtOvTSQKU(htrgS5g8j|-?_1g)W~qp zz&P;VM4QX!3oxu9Asz5A6?y`y3w3LKCKin)lCjAw)u95cu`@to(i6;pZ_uqe8CAzd zMhFaD=!3p6-i9&+{2c}mgb2hk#T)sWOvYt5RaTUjI>2w)IUDi{@o$k{c)lACWOv7O zTUkQc2y^dyXRoD{jm%z}T)GvJK5*4Wr4OCRXN5PCy1*a(FV^t8GfbXg( zWwOOHBV#?S9#*an<;d2@w-Z%4a^jH9d}!0APIG@>=edjh*r~i@>Lzd3Lda$t8$5Y( zXrQZWV02={nNLu{=ac!d(ZRmH!7)u;u+@v+A()VPjkA_4Spq#NHd&n-n3&KB`NpCe zj}GA$lPgePUmtXv^D2{0tG7_(m0>oemT=Cp8PxztL_-lZCB*Nz&t?8M17T6gw`}#g zyd_OdO|U}q{Wz}K0xB5{W`7;#UtNh&gMYFlKqgXYiPrx^kc1t8D8ojAFA_7nczouhX*tP6a zc(#Sqx)p3S*u5giFKpJsrY;F{8nvV{02fSE-R!yZ7cE~jf9@=_ysFD9)sjJc+&D@gdlut( zP6!1>;-bv&HnA{Ht0h`ZG5S#;w4_)G0unDV8}w?4#^UmlH&9NbKU7(Y-jue$5=Sio z_bbkQkEdDY=z~ongWUUUc!lm;$Eow=3@4x^?)@Zz-RVKPo4?B1M z!i5gC#TOkM9^pR&{k?cH2S%dVOnkDfSl(rncNyhfMtPS}-h|B9HSAc1sVu#pTwKI0 zEi2}gmsVBHUbt+9s{hG@kNx`BVQpflSGDww3i4Qsq6o%>*tWbsO>!DUHbi)`6aBmn zJ3C$$trB0APMyJoC>fr#D z@7LyIswFKpOOhH(Pq)KLuB4JH5gWIXD-j#FoGYp1O28ADbs$0^?e&$bn$ZL#-3AY1 zw2M}}D{P+ZMbP`cZ#}*p^Hw1gHQd|Lq48T)1dz-VJ?BriZ`*OaUsaN=rhE#*U2};+ zC2iYmE2R?3tDRZZ<^wQBEK#RRW%*bvY&JRE{<6w)FpoBO~0yl5i}nF7jWQ?FT!swO(os&$NGmKa&Q021LhL#4ofhLzCtnaUWjm@{3%nJ(r` zmvE*_IMc?DEu1)X@E3~F7_qNmj3=?aBMI| z5WgCgS}$;Pp?V6N4LF!gZlAAIZSjxA3Ko`K7U6XBjbCr=$m?*70&eu9o7IyTYKNoII$B1TIFYM|QA#)#i#^Ex`dNv;A9*u>aCr+gv~ zsD^E9q`yz2&qR4R*h7lma6-@t5XF$^g!pxIGL-}ZGpUCC>AaEiC@r(6GnzaqddSFF z^=j#w1@q@&8kG4<1B@rl^Xh76FI-C17Dt0-O1_zrpHIopr{w2T^7ASA`INlYYtyTa zbUc;92_g2DztpSx_a8rVzGrk~7^$+#_}jMa!v(c}@2;Jy z-{eBi&t@@bb%{wOJu*PNx2H?h^$$kVt(p||)88)?i1G2kesVBfWJrk*wvdd6fpgoz z2e?&`Sm{D5kTHitdOsS?LxT|&c2Ov&Np`{HwjP^b?CK;1V93Oyzoor zReuOO%wYsxq36wZ)LlFtf2pd25-`v7j$BeYEmG52xMx*=J_|3`c#{8FYzj0z!W@`a zb>$@vJ>TrEsR7JEVYsS8<*L6AH4j!fgcr)#E}nh~7$JZh$y4>nUXzRNB2*`)=S>bu-ZzxhA@<7U-& zxux!#{l-Ssce%AbaPHiI>bu-x&sidas_$~Ez314m9^QMY<*p2lo3yH!hL@P{+Gtd( zdM`KOBUxLU?{X`?yuQ9%MFsK_OI~U3%k-+Sh+k^XlXs<2eV3c`mAa@^^<8e(cXVTZ z6$@kB}Q6jjyfHrPTlYG z^QFpF)c**d8Rh0vlAOVaGNA;N)YR{n(^OZN{mE z>?c+e=etY!gc>%buAMXvDWQ7||xtNKk zR=Fh5+xQMy=|&Ma;^pFMw*U_mFUv~vXlMMQYMEN~CduIBWm$37niDS7GPU|OfdPD9 zb)cgRO1SC)Wegz)nXdBGMU>{BeqVJ^AkEdn=e3lenbHJWO?^ThGV}SRl;MgH`4g1k zjBj#gb>|3rSmAhrByyHjYO}3IeB|_Xv-l?G)K%l0tgf4bZ}NAKRY-F&-HSo_>3hR3 zpMLx8hwqzW^zIl6>&_o}>n+TE?T7$0-@V7DDV{(+$*M-Dahy7Jq5af}LnrWeCn5<_ z!UcoI6kR+z$|^arieNeJF~qiTPg*1`mlI>+Q^fykR>*G!b35}`#Twxqem6=$W#D33 zfwmLyBFQu`jC`0F^|3L@0w6_49!Ca-RY$q}N@YbAYu73RA7OBnSxyZIcu-Q&NMz&T z?;fj;=m`GE(eZH>Ie2_~3TAn>tRR(viF z)W=mrKGm1X_w&>q4BAzL&u{blj6Chwy0t?!q>;Z&PqPNhL~UfXFYyBLRZG-H;%fRb zzfG@QXVuwDN`(is42lgDjw`YI%NXeD;0%`~cSBQCL%Bx{yar@s$KeC}q8eqO{qWI~ zM|ST&_~e84KOyBOvk`46c@C;^nIL!V*>f_6SHdZhKFnJ3C*9F`rVnUdS}7#^fg>D0 z;S>_3IJ5uMGIJu3&uI-h7J>yJN`%}pf;YrBRFEGLwmeJ#&=UApStf$K(&|VnhJsE5 z9cDIcCwT%k$E2b&FoLQuzsttD6hU4#9w$I#BRn6_7Elg_MV=)v`BVlNAam&6<2J_A zfg%M)aCH@u)HO87T3jBai{kgjJCbE;(!;`|C4n;wkMIPNNNS+Bw~J-JBv_tijdGE7 z));$V@3j5*XIGTf1+YDG!CQHMI} zPhp4Bhi7&$flHFMN@s@`xNhD~DxrsFuUY@N_nTSlf7f*?<3A+IZs+<=qK6 zxa6RdgI*5#nQ!?2!+R}fjiZoVVV9e%&{>6Y9c`CvQ=_Sr)gQacurf!kXPq2Oa)`*G zAcs?O=nqe=DF5_#l*?NW$YGxxir*2JM|{0D$e~LPIXM*DL`)t{zOPauFD9%!E(aQ5Sh-ma#n#j$kIucX?0Y z_pnO{A;ihip>yQu5K^Jzgd8Ds`9I%#?VaJ9^ZoyR-|w#;&tB`Z)_bk>UYF13vp#F@ z{Yi|7H0B>GjRuzYACNP;`7{ad-Xao=8aU#}qi4;$^BM{7EfG2Jj)6yyE}Ply+AAe| zX`RUU=0_fVaF;G0)yoz!3n-;(!s+9uo)Iexu+0%E8#m#+Y4M^34J$;>0D1VNsVAR) z;=$piA}34}X;AOv@#jn>z7h3pK(kJsa=}S2UjEu@kvUT&c(vQ)N#iHR${xRp@>`MK zeKHYJQn!HPQ5+XcK7HEsqM~l6alBI`H#X(W3FEU)dt$N3nIz{6D z5%Dv|pFXMjw%0C}aA>uNTQv2|bEcJ#tyL)DNNk2Kn0of4sdN6aFeY;POzi(I+Bd=Q z?5n>$`K}Wh_WoWPhFgh|jdh;=Qgzb@6CU`f_nxW2ZhHfVkp!t6R3{Sd+Cv`&6+iXf zJ2u$u^JyOcCwf|Kq7Te+IYX}2oWcC?2e}Tfsv6XjkW?FtG0VhJ#+*QVZ-`fOymT?8 z#ER7RWmVf?Zxi|H7h-yA%n@brxU7`=d-r-D>~&^dT+BV6yicyy!3UVcC8Eq@q2vEU zT2i)B2^Iehbx2pn|1WS@x{PDcu`udCpuTCns*|8a8V&!S&=z|o)6SWM&jHm8*@VkM z{n{V@$or1cX^NFSeXC^rqQv@glnh6J<#-dw0Fm^;Z&Snv;L?3@kjZ!5AxHfO#;*p zjn_VyQiD9ge^id1C-*|}e)!)!FZNef7wXL258BU~_TQnt{~nz?`(uCSTqkV*h#K>K z?=Q@+{UAE_vHeg*Tn6=}|L^$k+NJ6H>#zUYIBmP0N9xNH@C;}l>-^Mf$;=vPdk%oi zz0DHt44Jyqb3Jv9)9bG0*Ey$UWzw`REnjI5dhOHnfuMb+=&?dL)we$r?h0C_mU9GT z?rD0j63~2FwuXm-)~jt(`hea)^<1v$v)ZWdT6Zm|Or!A`AGJKaj*bMadsdp6=)uqu zGUrb{!fKmot6m?J9LV^twwZLDN8KSa=9xM*UE8Q_QuJ8ctKl37p%-Xb>Z`W5=Cw_I zR$FbihBd#o^UyTvOBHDTnm+5X+G*YDTNbEYhK!$Drute5Gt*(66Y7h`sj9zPui9yS zTJCVrG#y(_(|UBw;*gmOnL23W-fL>`7~u;6O_i;Av<$6JISTZA*EVW9bWA6M+Gw3x zkB&vgZ>>l3sBH^SpVUWiM(tH~{%IK*)HZ{bq3N1e>(x3H&D#K~AP2Nu0gWF5 z+J4p1p!H{twY*%=w27egp8%QkjOtv|G(FZbwQM~{vta;q2Q5QUTa8myGI5!7EvI2R zUhOjJ9n#@Uzv^+O9u2GQV9@oKwpqjd(hNd%4h{#szGztOw7uF_npfjBd^OwxHv!`= z*Tc0i3pDOZ(D?tqc6yG~peZ;uR+DGIdBHyOM!X1=WUMP0JTq|ZP6ovvQn%hZaLxC(37Zhr>}d@ zb;K!@@ykyrd49Q*^eyn#f8d|k-KbBXzuNpldv||B{B4v|AEht$GzEPA<$LPY_D%5Y zVlAX~XuZ4idZ+#O7x+79Kaxj2NkbdtOi-JpkN~yOIe0$M9=QTE9-F;Cpaav?Mk#ae z1<;c4T%e7qvC}aA%hRA^rRVJNkSRNJOh3!5a0=Xgvq01=0eT6s9~K4dn~#X9S``k=biL%8?TzS z>om&vH^+DUmOWQ-%pCfO=ZxLky#1c}*wfcPU&!%iloz5e{?110NSCE~Ghsc4_VoYt z_?Pc|I8`@(sNEj*i+FAC?k{0G;Y#ADch8|dK2_&yq%kJHT!wA{Js;=`c?zxpfzmY0 z5uH=}BQ>8oVwa(<52xlJ^`-7*T2_XkgpYip5Ut>?in zX^uph=c)5j{W%b{94)gcFmLzqD&bSpXjtv`L#qF~lD0pz4n@l)Z_4&4!r6q!!kh32 z$8~^tuk76m+X$;&jrue$L+bpENY`>{bE@0|!hgguqzwQqPy3Gfomw}IMzs&iL2a5q zXV88=0@OYZYNs|jW-Z}v&@t0%ApWNA-I`Vi+Sj>oPddzbm%3KXMf20XqVfQ2f$5;v zJjPsFgO;n~r1N|@(8j&oXHtEnWw!@awNuT&xTgA{G@YjD^s}}*6|cT2dfsHh>a+Ty z^FZVEe96RVy!xYYiu#ucYg#6s)~9Wo1QS8+YT9Ph0^itvmxwLA&^=UHpYWqLH zaP9^5eHj}5eOc!B4xMsqtsb|KFzqfL%WR7cS%9i8bc|||%vUp<@nL_eKBVeMYsMyZjn?{V#Mca`@~B^Fo0MOfaEBQ~+Ght0{Qv)rF&*XlC=G%`Csrr9!t3GKN z?b38e>;C*xJM~e^P#?7pt&7hP)yGshdhfkJHpyMGg-_AWk!xVKEJ3f4x8#}Mr2U$5 zuN)%x@^pBLblJP!f11X>NAp+zSO}rOha2oneJ-cRq155?%bj8y@EM<3XhdqsLa8qY zOAon37Mn}WJadz|)7)#4W`p^{{L6fA_6E5@*Wl2gG8h{?AFK%e6ucRH6nq!_7}~I2 z*f4Ao=7x!|O;{418(tF53Fn6M!h6Fgzahj*(2>ZdxE{$-f8c* zOYF1u1^W;CiT$VjzRs7iD`R)Zeu|rT-FTyTcD#8!KVA@T9q$zH7VjB9EIuGUHGV<- z!uX8%HSxvqC*sTEFUDWa3-apcHO*_D*D~+Gy!^a^ypDOLdDrIMo_BX%b>97X59K|Y z_juk*d2i;e%lk0zlf2LJw&s1AUoXFDezW|R`LX=M{Py`h@_Xm^&F`1rKYwukko;l! zqw>e(Ps%?he@6Z_`M2jkkiRT{dH##}tMcE-|8xG^`D^nx<$s%SiMolL#6gMHi8hJ$ zi3=0gCT>YQo_IU4ry#GOpm2WSJ%#rcK32G<@SVc<3O_IWSK&8>KeRr*^_i`&Zhdc2 z)1qcYEsDAqomDiy=`HT77d}(%=T|rLJB|SRNF*h$O|Wpr}VqthkcBi=h+&ge`_jn2&YW4{}nW_btH7@awJ^YZRubneT0An%d% z=&WUQK4x^jV00wE0i%<{=p^!s^1JODoq_vC=Y;%I^DoT5JpcOqJ5!_cLjJ3a&g%5& ze4PjqS&UArL_unFx+kt-bnawyekqWGM4=SkQdnL1XyMYrw+q)XI$zWno$7s~a|WZc zu;_J0=kucPQ=@Y=qvIHz>W#^oqZ4xW2TK^E)SsaDUOw^v{hvLX_bl2|%}Lpqlk!{6 zx?ZM4ZNjs|so~^sd{`A8Eo1k-%=O?aShMFX{#~0XW%rw0$B6%Ajn)wzTy%Z2srwt((8#?+2ULZRYy9`6YOC^HZB%OO^F5N3>%zSCP#JZhC&x zf=zc3D%kYkrn#Ga0qK7?ov`W1RF1pxpv9)#O}%`=rwdg-o&D(*svBQ`FEpfoHs){a zy0N{6KH2ff)=##7vi_5GA5Hvd!bjhH^!`UDeKd*i0FjUDe9X1&qu7TPA3eUIB2t58Xc|w(itA5pCu=bBrBrSLwGD%pLGd&^I_H+^MC8KZHBOpKT7;qgFP?oNuSG zj7;laAyjO;`S@S|wOq+<&y+3ya;6NoBkg2Dm%zjJCA-f4opa@5yU~7bx7si5Hv4b8 zi!^Y0HBSF=e8WTkxaVPod(|K9@8682PjmOVMedp3+POz5BbiQL>{fBK2Hu8!v&a1{ zlgGX7K5*OIcDE;DX7CM;f{43fl)lo^*H|b3!J2U)C_*l3&yv@bK>%s@QLRE8(y1^B> z0@pgc*_sI=RP-D#>jDUyiAl!^qF^TCRq3?B&ridxiO1_$OCvXGJgDt6aM-GxvSpB|Cr6oPGSt+O{-%YD zGP!b$InczU!o=lRlaNZ2FJn!Cj5CFDf@ve;O?#;_?Mz#lU^>VJrk|W-I>~fXCYP8& za)lWtSDGVbmKiQro1j6 znv-OKnIN~DiE@XTBz;UE7n*YE#iio_`B-L{0dlPwEjOBRvfNw|y%W71Jr}(Yt%#nE zUW{IeUW%4S=SAm77e<#vmqs(9bE9d|%;@51dUR2AR&-8qZrD3GFE~G#9$Xk)6f6v8 z1Q!SQ1@{Mwf;)nHf;)q|gX-X};NIYp;KAUb-~sbd5SsPo7xS~(ZT6VGW~cdyuVrmE zUvZaTXWlb^H}9JdS(SZkJ~123r)HD++I(ZSn{Um(&3EQU^8;(OU4aP#Rs;tGEm^f4 z6vTu4AQ2P6!I43|U<6ODM+FUn zkwL@Y=%7(BN}e3JjrJ<~odh%CRt8el>vbR`? zu8}PH3*Q|3vvigpq^s9!z80e(z3qom_YOgZ;`iw_m%-u9@BL4z-(IiOX}=RoG8l5Br7vr|a#`up8VEH<&vlrn4N5ov*T?y?xTHdC3ow7cAA}F&#}GjL_5l!ZTs5_dy?&I zOYIQb!%nr6?4kB(dlqZdDfS3^h%K{2?HP6;_xi)^x%PB>3QrYP_HcWq9b`}D-rU6w zu!DKp=xiUe)%GTLnCs*Ex-wVl4tK`xb>c?5qunUG+wQWz*e&iTH^LpsdiMqUBKPv= z?CbUo*1-4M2kgbHhcB^@vKn5)eSW@u&A!U|_%3^oz1uG231+oj#PiI(_D}Xf`;dLa zuC#C2RXpF^XqQJlqNdS+C>|Xa9T1g9Eu-F1^QceMf_rSO$V5$|Lm9TcQRAq8R23Z? z9Tb&E$43>>fzjY-N>mvI(J4_@bV8Jf21TbuVqr;=V(IHWGlpEzl4WnLBS=20wxyRiD?m_pEd)PhV9(9ko z#qMdh%su6nx+N~@o^;Q+SKVuFm3zW%a2wrLRwp01Pu)M=S8j{@+kTub=Vb)sKu*nwz$PpB)m zxF=+9ft5sx@J2M>6S8*VZkQruO`CdlE?@=sSHguJ*AHc_sMwikkth5M%4&)`yM&LS zx~hWkHdNQM;NobpC%g{r;0Yf@J9@&o=)s<_8fDeVibsM^QEdZj=k?F z7?Nf*IyB7~bQlZ=_H{&`43vp5B8~cX6pVz+K+96Df>CL--?UuiCO8JP>_?#jDnVDo z$EJB69qS37M8|o;dr|cRLbX5M<8&NO@Pt}sm4_81Um8#I26`e)fKNc%pnMLK(tL@Y z1Si8DnCvkTdWy%?Mo;ya1JKhvsqc$VAq?gq^mI6baBK8Tk5NCTdW??4S-{%cXy2dX zG1{-X5(lGwbFRl|yUzpmrHsZ+hYJW_gkI<|SE70h;p6BGkG%%H*b_d1UgB{b&}vWE z9o7DYP{-&#k9`Nd-(&xdF7nv5=mQ?R9(~Yb=b#UH?B(dgo@g2Rh{s-m>NtV<8~T_h z{1dv^--{(=G&U) zNA#a*{)K*-=4(`sl^w9nW165}c}!hY?Y?1NH9)tg(el3a7@hzB_L!dNcOIi-r{#j_ zf`0EYI(9#JQu|cuAGid%(_{LhKY5If(a#=!;UV%1?4r!^=x&d>7~SJBIv@6Wcs|u_ zniM>#3eTGnCeT$jpy1FJRu9uiYau=wSQB6%zWdyJB5y?Y2?wh1J5HVbZm8; zQe)K(ZR*iJ_Jfq7d1T{rM#@6kTy z8j-q3m7&@;r531vp!ap2{XN>&b{@U|voqk)zG1$m=IkJ}LmC~Ejviej=-f@A{ipo} zy4K*i-lJp7oKDT_{%F@U+OIkuplb@A7d_g4-95Ud;Q3K>?x*^$2MW5r(DNrn9PQ=N zwFdhO9_^<-9$ky@yy?-t)P4kAkC@Ul+LwoWbZx?3h)4TX`xkUQ!k&aj`%>Ejx*lPF z!lV5+&?8l-_MM`AJUET^{Sh8ryXaaWh4!)b9i-L)!w4%{|8S44W!RhXXn*UNfvz3c ztMRDsI>w;u0`_h^>WB6<$Q9`5G+KxDHOQ6dm^50C_BF^XRQpj;U$n1*-<=72LLRMO z`wnCddR!W<^LP)RqzU^)9`&or!{=y%!rQof&vg3Lp;j})zEqDO8(C#6wev|m83 zM^8?pzE1Y&I)%Lk&ouN@kFIgpfAE}zPVvZm^z=09?-?Fl`>;phIR~BU(RCAhVIGY; z+oS6y_QpIKr*(roj-H!F$L>6ju65X_@#xrT8tA%*y_*!eW??_aa}|1#N7pQ7MjGwU zi#@uoVL#5J<8^5o9cS%FV0TW8_BF6iC*~$k_%?cH8oee}ds1uMdkHJO;Yp9&gC^5x zAOFR}j-MDU3v@kV-tp+Oi&>kdKKfUWuGe)PnW8iLu19V`*QKdK-}8w2`1dqA7vA^C z0(5;EokRcd$nEF{X>?9~=#e|n4QX_KeeTg`JM%@F0R7UV>vZ$4H09_Hk3Juo@6*s^ zv&+M7rkLFxUGJDZ9$nX)y=eyUoSEa1Yf(TQq=;F~eZ_w3aILCuS zJ$jD{Dm}Uu2*!GJjSxKNi5QdMc~8U`1uHxeV;H>QiPZK_p6CViO;4ox=w~I;eE;x7 zPop1rq8HH*J<%)Z22b=7`jID6|G)Ftj_8jbdoXKm`Y|+=&$$vZ21;}xNAmqRY`-Peeb52~RW& zErd3deu66eZqOH1Pf8-iauut$D#MbBZSXDABD#VYhNvfCkWq%KIsYWKslF{fb%+B z0&f!5e)zK|xD$QL6Wooi@dVW<^F|5oLf`fT_>pQqeGq8--viFu;6apgRtX;9SOAIWl<~KWx1MK~akh-H{ zdLG+G&=i|>Xf`w>{2|&LS`z*kWe(T`;VozZv?csC+RkH`zqY-{{D2mF^x9xMc=Xz! zb6w|npzZDidJgBH-QWdxR&@^dX*rJa(uj=z$LN z1f}S3IFh{m(W5*;9y-z!3`CFi1g+3fp5QQav?u6`9s?DWb2wTF#}e+1j`alU<2X-H zgdXn+I-@6ef)aF|N3YYCd8-5mqBnW;nr(0P1TpjuPhinIJ;5OKK2MejeTvw_9-$v%k9c&?BKD|9 z=!e*29^J!;X&XT5p^tlX?<1yd0Ac>bwEduaEivYTBFvK*^Fh(Qme^8{Fi&Dnd34_; zw#>t)W)geaqkAwh=8wWBX%b`pD7t?WTkhf0HHkgv(P#Y_^Ge|pH;FN?6n)l@t?=+E zoWx%A=(Bz7C6DfL$6og69z<-VNB6m7uXuDHBKE3B_q=1Td2~-Aw#p+N(bqk?KM{Mw zqxDFVJV}n6?dck2$9O1^O%=(>?;-H;rk(fIiE`-uCFeY3v=3 zKF`H;>_JXJ|LW0a$C!>i$Z6=i9({I<=@^6V)yCfQ=yPQ3?;hQ|jlJ*DXUdq)InaII z80V;>&!VvpJi2!q`_QA$kulC+Mb1SzcNKkxjB)NNx{n*<{8daTy3wP1yRlC_`g|GN z~oLYjegYk=F3^=r%hf1zOVPDbya)6o{3hBPm|x+YDDyxG??Dgqgp6Zc%Yx9M z{b2y%1?W^ysJ>5w3y4pk7kV6a@fjY=_{ABI_%+xxLmz|1_{O;uf5KzmN0)i*I`l=4 zeGh%vV>w^*0#87`y!xKtDU@+h>|B&O6n(bHV~iAg1Iqa1F*d;qDC3|6)RRZwD)vgW zz!SWVcJ$cm(Nd2)1)bxuTx;?eC&kjAd5nSLE=Ov5COdLGAo%5UJY+tG#|$F(BAk;iUE8+%*{+Qj4X(54=z{%TpE>!$o1 zkE=kLpNg)<@|mZK>w(5R?qD?G(e+n;fk)R|`Gp?GIh|hw?aA92?dEZ3poe&LJ(#ck z0&WPZZ2&hIRlmV9Ciz-7=sGT6%LP}7_Vw7EXg`l-?DPA3>?U-e$7w$e@;H3T*E+yu zqgpq(Gtprl`yD#UV>hB>JWlllk2?ab@;Ghxc#r)zdZNc^|4s0?N$5n6Ym4eJxHjmi z9;fr=G>>!W6pyZz^L1{4Yk_J%g3CqKZ*V!N&R1})(6c=50Q78+)A@Uj$Nq?3=yBRl z7kS*t=nRk3KD*rGPC~EnIQ4fHTuEIzFF616uO>VcoekFz*759NBLxO36F zJ??n)9*;W?t@bz_=X*VF9D1L}9fRKQaXLPWJWj{$0gu!EUgmLn&Mo)2TIdTNr{l1~ zs?;fv<{X%o8y@QQ&cRp@klOeoeIY=reAjjmIX@ z_8xr>PIUL!PUxW?%Qz-VJo*fu=;5(r(4HQB)=%{E=retyw@3F35`8>Y%joN|TF+q~ z%ej*%_2?c);&6{W5iRrRK18CQNB0R5}%LRKjs{II7+ob&qwgOFKPa>>7fz`g$@c^s+r+ops$CJ=80IOrA?FTy* z)xHBe3FUlNtd4V)NB4CS+DBl|LUjy4_m&dcHn3ArZ3F1uQbNlD-IGeFe_(aoPWI?N zQ$oiB>>21O9;@S{V-LEAmC$(xR_BG*0rp(O#opwm58=gS2i-4{z-=&_yAYdrQbbdJYXqt|+L zk1BDC$Lbup)8n*_3*j!VX*%ET_Bidgdw?=rDSEHR8T3B5AD{Q4i#%>L`T#tLy^h^O z9=jWT*kgC0k9h1a=%XIpPfDoY;EqBUdz||DxW{RG-}cz2QO!?z_IXt6*hBb5|J9KK zA^aR+d@TqFzmA4r3FCW#^H}^Xh&=XUw3bKr`3kZ;_7b$V$Kr269glqmt?RLC(0U$A zTMO!Y?0mF=$G(O(^w?L?MjqWGENJYpccD!@mcB1&>amPTLAJ*-Rt3#G_F1&K$F4?O zcr0U5kmIp8qX&2_V_4AAWB-KaLM!@&F)KLGV;P@ z$QUV6ZLX*MM2tfrW28ifqKuIe zsb87~j`?4x?SV+s86zd?k1|F|RE4Tf5FLxE9}v;6g^ZCBm805Dh>l0KZ4gzUjFA#C zK81{x5)DT67@{dCG*5=A<{lmUm>cGYM(-+{ip4Rr~#^Ff%^_spTPZy zj`T$J(4#$WH#*AW{*7vzA*zdN|AO0v>Kp?1EvoYyq6w(ZU5K;{Jx{>>fNI+yIuX?| zgGlStxdrZf^mtEnCaQB9qLa`nkNX)N?}^SpwJeAxp;|Y%pU{b(s1B-W;C?~1EO6UV z9W#iuJe~ItX@8vJaXZjcJ<;jtX`V>Sp5k#kQB8xW1FAkjRE%mHrV{Rmp5=*jzMt)h zI-}=!qE6^EPo#79Tu-EP?L1GUbMt&pbO<`#6J?_pc%oeNLQj-~UgU`yqBA^EFZ5zh zr1SR@Pt**()Dy+fnI88zdKt{7-yT4(@wlhaIUctRz1HKNLg#wiQuI2HTY_HiaY^(B zk9!iG=W)-VH^R--^(s2wZ9vrza2rwe58PH% z{RX!U)%w7Fglf6qK1G*%+&|IhJnk#>d5_zIzTk16qboe_WAsIj+l0R4abKb@d)(LP zN{{;leZ}K8quOS0|3Y8$xNp!^9`_mgy2pKiz5%PTe+PZjm_PD>HZ+V>BY8p6= zd)wpwf@*$nTJ}1iFCFC;YTLl6t=5NM?tN5!0{18M2alu9)~9>IzoKV)LVRd_wa1=^ zGIp&QJ9|Dqr)cW2%-^DB9?SeKYT>cW-=gjw%ls`m!xJ)=MU=0Ej9(GwnG!PIMQ3?J z#<*y{$K8!C1k^EyifE@2GPXtZixM)1MH@W!XOwmq(cZ8x`aS$e-uw8;M{|!GjvnE$ zKcTd-E$2wc__q^}tBpE{$Xkb>g;aPfb#0^{Hd0=+7EJ=aNAL2r*%LjkKf2cw_TVeD zV?80~k-jTK37PAAcYEwBeC@1J3JD(LD@!FnzF;xh3us61Bw7mPgqNWMQt0<_%zH<` z2;yHtN5W}@-$+VvJSn3q`d7x|LzYW}qlP4-qmQjfcFRq+sjNCFK4)}AGAJDXToxZm zPMDB5F*h$SDV0g-mndH~8 zrlu)riKL9Em{hs6xlzjwNn4x@+b5g#tI+zA&HMFB8|20(#*-^YByHSaURFHLy|EK#fFSu z-bf~>#d0SnR#r|NUzs%RD=X72sEkjfJ&Cf)4oO!W?;lUv!tu1JR=*Jy$y$lBWLBb# z5g%U1?)qg_LwaX*1Uwlq{4s|W*hsm$z@?)Ghw)M5^aYG3{}o zA5E`{_n(s(uY;QEY{}IDNyc+=GUFvfn;2j2tEm46*^>q2BJ;kc)y&jD=YOK?srvj^ z2mKkM+(cewJ0@|%;w3@QKRI!Hd52`9Vww<-CmZ%VLR&_F5lS}FqmdjnN{v@z3Tl+< zlsJ7b0mqY#`&Gr~RK=5x>9!8ZCdETWRV=X+%PR|#^(Q5!cStrZ9&%L0kfVKMZXWSX zQ}NlwOQcD^(G^RYH0hT#DkgDSzIHr`M$)nd}4{-6Q-%~Qq6GQOlk^3dXrEqZrImi%|7Oza7m_h5*md11U`e4t(u=-R<^<_t^> zx&@AHhuHLm}O0pm+@6Vxz(b|R@-C~rTN;D}z5BQ7Eg zhGW8U2l<^RlsI1RR_a8^zv(OKuXQn5dT06~jt8!Fx-SyF>83vWl9F|}n8f1)5(BkT z9mT$xv{Yk!PbXzmMaOt=Zs_WL;3Fw?-%uo@LXHNLv~TU=2l2noysAWcw7BwjNFJ7{ zE!F$!;i}RF&hML?8RAl|A04$92P9kcs~C~XJvH9Dvg49YCY$s2@ZY44%pLKY)Uw~C zX7bi7@Aog8 z(8_$%bvh?F(>ikI`34OrUQ&m9jn4km%>O@|(gXK5!TxV%vG%uK$9gA9a`S4;m%Pff zp98r7_iUf(l|dZ!YM-ZLru~v`&Au)eOcylsr#-6^&hVxklifMXkNAW5Arxn_n+HiHOER2vOUQX37Nq&6BjIptS5p~)$~G;~VJFAbfV z@=HUfrTo&+l$2i@Iz8o=hR#U&rJ*zF_opjVl-XhbNH%J`hQc?BFUTV6s z@yhxuYp;w-#YtVv8kfB!rThMB+;b9gPhV1CW*^1V!t4q?o>;!5jUF%05+5PumAOlb zH0t@RnPP0|>(SBtc9(7FI*r8Isy6X&Vz^f>hvJM|N8h^e<)CXeSEEtuk{zhD&A*F zz6J{*DbmmaWjDg65jKsmX@pIq4X{I`aXl!2-Y^QL0P#(TZ!#6|tqJ}##h<3wG{vSV zHce*%{$yvtOxOauM4DwoF_gn*2TBFupKuozYW_RX7Y+q;zLXNrxkfyt>OPGEQO7Hm$eF}12+0S^@BJ* zh~tAej$;?!0=xLW9p&b&=es4nVHA)zekT z1#+Mh3A?|U^%RX zt-=)-Xy4)WM9Mm`NU+cfromj;&PABA%5z{U%w{!!O@D0qcY_L8&B}%Nf!GYBjDe#7 zUkA>Dg{%xX9<&a&i3}$1VDb(o@8F?;-C*nnV>fsitbr}Ci&sfx1N9G~{votwNC}L9 z$v|6%ED#w={liEbM%pmahR@?wGgZ81rjviY%u(1Jl>~e|Y9l{OCw=6@u#(r!Z0Bd; zls$_4qo{Xu4$S5?GmQZMj@jy8H#3Iv#!LtDjv?9&~Uqv-6hqWS= zQ-F3=(yq#7K>3xltCDsdn+=O$6>Jb0OI>40A4~dJ(#MfLt{AB6xGccG<4RxzOoo}T z0Ftm;gk>TpRRC!xFBX|x0waL>C(nchK%P@5=ae|i=VuV5 zK%P^l!fYV@G}2FN1Eifs+G(VnM%roBu$-S-41y|{4)fq)SPAQ4JFm-G2itf>h=o?r z4Ti!*n88m$$a^Mv&m`~EY#?sxPF|t2L*#72XU`QmCkt|*0;vC-4I}CIip(7< zavlC%Hy!4|HhvawVJ_?vxq#z8eC0pI44J}(=JVGHaKxe>b?7r{{toQ#B<{{-A`2UV z>KdTlh4^<@HY9<(cWo58n|yb#fOR7Gl)?y@EK-eKbpdpPa(Eb)1No}g^V5~-FdG)Y zR@liakg{L~%!P%pm{%cD&i#~gKjqv{IronOd|X8OqGf<@i?+ZnUXO(B1I2*d1LI&C zuSv2%{DalJCTR+B<06|G<~uRE5S19LIY+q{x^LWLPg$=ykr~=S8&_7l48lzSGtZphleZ%IhiGYuPX#>Q+4ezWK zSxfv{^lxz>{axx_w_4;q{QLVdUNe*wS&z@_*YIi~eEI;p5Af^50-)RtodDa9@bjaU zyiSO^KgPFD2EjHy$D7RSgUJ6W;Z1XSWf1u{Z{&4Bi$p$~#p{8(!6=vpl>7NISO+_J zT~Ic3gHbRIsPBu#uo||cUm?^AEdgw{P6g`Nx{6l{E#p-}GkJZ`I@rPEK{j-Q5ikX2 z!^42>HvHMPi`NR(gJKv8*nBk?7Q-6YCh~PXD2Aah1-6TPQx0oHw&UA&j=!D3>xLS^ zT#@f4!cLJLIe?ElDuBAaujUm)RWOrR3t{u)EPif?Z#zf9hW`fq{HY$0@26EFKNJ3$ zd_U7CzZ3)Y|FVTw4-JLId|pc3d#Gn`9A>cp!6T5Yfh}T!3Naz!aGMyOZ;V|l#&rYY zqhctBi7*?gVHIo;Q;RZcVOMLYnA+pS)F}n*>R?v~yE@p_!LH6OF?FZ&I-vsS4WnQR z%oWpsG8#}vgL0V6>x3w)(KddzNZuy+k&O?{Tk#s8Hezxpv*k=4?c%Ur%z@Y(xK&Jy zbbc3W_??|O2>XMk!VWRL>Pu`9Q!q^o-#asfvtg&0 z)@#HRJq)YGur@bsR*Gp$dRy$;u7Gu7+RflqJ#pv+)YpCyY!FjC3P>;BA*Mq%(4G$X z)`5H-D7Rxh7yS-8YIkl=wqwM+x~^f148gDA~o&Kk=={P*@|Trv;9C;#03~JT_BD z-+E$7@%wPfD8sI7l^E8&hIOxD-D}DzznpOY#caxr0Kx+)e^4W!oWa;05eNKZU22Bn z@33)VhEx7<(vB2Z&g*lg^4gp>K=~s};bAdHmjJd$&xEyNMoohSVn*jc5{NqnACJLy zOe4T<%yuyq*+3bUaiEOK3fRD_a%S-Q8ji} zD%i^FX4dkm8SGdW8P-7Nf(kJg#$l0|i>UJ=>}PO110OH$B<2zU@?JvSmy$MfoS4hn zz#1`^&xDO)u2?8$RslRL=E`C*SCs5W{-K zT+8v?iGXj{QRelu>H2ChH)H`m&YLUd#uC^n=B6oPZZ3!IV&-FeOCzA(TULv?b+(w> zTER9kw`T)&+%X7tin((>+buaTL(E<1UD)0o2gVp#q@ZM~cNficgP~zydMc*UjUr#XPZA%#$1^DU*A- zNlpdIOzsr3WQ3Td0@KAj)r~D7j-SqfSz?~y_*vStT&V!sv3$Ll=dyw0=NrLdF)vIO zvw}P?(l;+r$4k4!yu4P-O4{=ZKD}B`%xjdpity`gV5OKhc8dAaY%!~+z$!6sPJ_ zFcikYaxvfG(|7o|gY@rnV7{0iN?|JC%a8c*Bk4P5zy>itm5ceAxS!YYsuA-3LK(ZV zU?!{;vzs!w$C=&p0GmCOySErrw@V-uuvG#xTY_LH%!iE03M(XNK;DK)2^w{ipm8G!nxIWKNYIqBo9>bzyB?GS{dsr9*t0m}3d@t%?e<$dJzkMjX zFL@8chtet_?QqIDobpz;*;_BX&w~)Ks9Hk=3wOf}=aZ zatTJ2N-!EfM)wA6M&sA$^%5LY4@f(Pa=CW}$57swY$ylf#$Y!Fy9(^Mj|COPRbW?v zU1cM{u5vn1$FbNPI~3-^N?s|I0~Nexh%!#Zj{8kXRiSApECu>H?15NN^maqpF0b%Kd%I^VLu@_ zf0qQ)$vgdFAbbJkUoaQ8NN`~rpv((5N^nszOoi1F%%}$wVU+~jV}pxl12&fszJxk1 zSuVk)7AWgd%DNQ0ODTIM$1@3EMtzqNf7x~kE}sYsV2cD-w1Fv*1deBw!aShtE6ICh zHSCn&s&TMPf~&J&GOU$gHhE{yg!K|!(+#k_hJ174FcqkG4tcLFhuN@3g1HuY1IO19 zzHUAce|;xd1eA3{E1-@W7Q=Q4<`uv!SSP`agm0V+TO_!Na&Dr|o2cXFp@9ATY?uNo zCAfv~Ez^MTtvNs&Ze0f0-&O&OV3!07N?`$Pkl^-mSO_~LxPx-;pq+P?zK>mAj0AH$!V_gtb)|P|Sb|5|z#<7Aoi4#+952RhF?k=SJx^fs z1Z6)#9Zz!nB;`N(Fzk|mwM39iO0Xnbf~B2cy9BHuf~V$5u&fcFPmhz}nNE zVlGh5ie<1GHo$fXUbH~jFBU^7P~VG_VFt_t+Wz8JzScSjDC6Zuuu_7R^I?qyuTbAB zpH+)r z6@?Nuc2SG=T^r{$E;RgiWsQ$Di}qY^iubI`=09`%V0Y`&gI>Gm@b0WYKbQ#GQAaIl zB*mp|>l-UnK#PnGCR0(#Fh`Z9hZ!#V!CEb;)wprvsP+NvyEg6GG*OhO)wF9->()h4 zRIB5QSFD)S@$VfEfAWw9O@g3F1Fh(#mrSD%dXH+@a8xe}*)fYa)XOsF|Tq{}- zoVefkYMzvTwcjO0(R~^dM4C^_+1n;)$2YHwqLk&n@@D-u-@f>J{~&(RAH?7PoA_V- z)&FnySHFIZzoSO{nc=Q|^)AfB@AEfhxi9^X{Jk@ik2V+AC@=MY;_mCmU+cN6hJCy6 zseR?&l8M*)1N}_c_q+c8rTc5gZ2<$lul)D+*+2Xn`&;(KKcw+GkM?#61~QLYNL+f9 zc0bTWl5LnnLz%nQM3(bG!XTWGno_k~W=b89Lyi{B^IA7*8R}mPqG$4{J;Yb=rgiMzeL;L&$pmuanok4%DT60b?AXF?tkuW+2{KMziHF2 zefD6wO)Y6yZ`#z5k7f@mQ9|3IlrDOoZXf9)dIg7$pMnDy`5#~(4qL}Y~Z*ogul9FPKa zS9+C}aH--7;%4Go=d$%3(z@**miOUv8h-~Lf2;IR z*kWczeH~*)dE3ad_8n7UucYVoRe8T2}X{yZ=_$w*~z%?v3BG0aMi!tTNR^m=1d zG8+4hW2IrR^L4%@t-S5mro&c@r&Vgn&#$e{_Y^oS=88s#PhA|p`|9*@RlAI0O}{-{ zSQhXWkL<}H*CJ!)(x7KB@DD*pPJT&b!!^6tlCMOz*Oa>2*Q4Q|5v>xm{S3~OpzWia zhGYwZS7VQ6Np{H-#x0Y7IWZ`MKLG~U#HeE^cQSX;&ADV&A?Q?hLy_V9L-3)+Um4x|BJG} zia*b4;;%Bufa&i|{9&5bgg=|FNiAQK$K-4HT4lC>1Se*_{vWU(aQuc>ag9 zcE~zQ;=LZjC#fBKBK3RCDc^fs`yOcFOKE>HrQMR!{zO_k$qn)TN7CBqOo;YJwRRd8 zm%*Z)k82$|eL$W}p$Exe(f=>C=g^udB!fl!Ur;;vRup^-T9;HNb%*_lwKQPn##qLZ znoXLlLhmvXzw{sRm(*o-Ws2gIb#7CoaA2^yPF?ea@GbS1(|GzJ2Rx7i&C*zS#K`nI zl2N$9SJtgaM%*A~1QXRsdZUiZEKMvJ3{up@RI$)d@2PcGR+K?H*f!)!&8*ohL<83h zd|-i{T_hqBGD1}xxCYVD<@e^w%(vxO-)LWFBoK=X9tKnL;D|mKR3p|DJH@v41Oq*>lZyvVo?7e;{3KvGJ^$5PdLogY z=*bfc@#p7H?;H%@bW?Z`qnO2h$-}-dOZ8!QVqY*#dBOc;xblqHlUXv;HZYh=lH0&K z%Pz)0$%65}moLr!cqaaL=!a*HW6oKabEVW5h8=;sCuI_ekn)DK)g&RWPMD@!Qk)R; zm8{bAc4FQKrVFivwPlF<`+mKite>0un}w5DBR(#E_Pa68q5v^)#Zb3Z1vg? zJn{&;?~zAlWcSR>>ZKVxTb3NyXC3^RrP6SC2z&CH<0kkD1R`GMNm~ zEM~y{cVju(l2qg<$ls8cU7Ayxpl~)Mmp|p5j%v5ZTgdfF(7eFl*t?gN?cH0kaJ>>! z20OclVmJPF7mMuL75`tmt{j?b3v3R_#??O@xE`E~#`XkkPz7eOO>#-g;e%LNCf%8* zFEnU}x?^@h0|3=w)qcD5yIWfn`nWz1e+GU2G8K4)18 zM(wh@Og0ml5qY^e*;y8|&ZyHY`+R6hHow>C#SeR%nVdR&&GG-h&+5yMH5No8Sy04#B zD#>o$O%hYvy~H`!N+V~#pY66)~VRR4;}gR7mh!@2zK^0 z))jv$ekU7^e-`hZ0#C*Bu_k-?dq1V}&K0~Dx`m{dD#K;?1ak#Xna9j=ku1q8+^T7{ z>dZypWTdvN#MnlFPAfBb<|+Cvp9^#;k^}4~e2(ff@h;JR1A9xfV+?p#qWx2#F42Vg z3)}?#nNhib`+#kd87T;#XSQd3jYOXU^r^=uk(VKyFr^YHZH_%FZ&8SPZrj_2E?mT+TUwgOuUj|Kt= zO=AUOoIpAqlGFuJz~u|E1VV^=Z3HyHjPCL)E(L0mb?Cy({`bv+pPU@O7W?^9{AJby z(X!ol>#d#fd)d(~pFJ4=G{%khPT;)?-fNX=!p>|3`wW*>^Vea5+Uu;6)mmuPb2G_n zYmt4JkxPN`X`NWP1k->2t=}V+@6zgHC8N7***&Xn`B2Hou9`Sj2=wLe;On#>?@Y?; z%yx~ROkkbxG^~^8f0v5ki2ihLM7wrwCXgRNb10Eu-{cwe*gR|-?I6CHf}?n;E=n3u zh-*M4QgNZR$VtQ#hBluGgp)cmGEM(8?Ctr#nA1KnKi~fF#o6u-d5@SCX8M!T(cLwy zd)41N)eaJJokRy+DF-$(PEZm8(PJEmvzwR8q{4#S(!5e&AUQ0@kggo53Lu%p0t#VG zi>FU7(oZ1N8w~b_ET<1M@ABzWhvVNoe0nO}%Z4>51av0-M91m8m`3N!c8#BBwrljy zY}fcXwF}%+JYT~-$6@m%-%GX_=|yCV;cs*LEW@H< z@}1D`RrAFF96`c{;_TQ_$fSz0LYKpplZ{2SGkY4a<$yj971zjN1=8*_7k6h@;m(e- z%msEEOGdW$m3X&YZJkT_IurOs=Sib;os98gUZP#YooT+Q{XZZNCDx@xAv%cuf2rca zqCe4Dw0|t6y)b2eeK7G}+IJ*x1wA#qcpSDWorx-V1VB&VGGrYpzylR2M%au1(I9OE zW&7D!wG(2{<7}yJsVukVSxizDtI{k8z{EJ2NyHGR#h5T&m2}cx7Z|LmkSzvre46X3 zDvQgC`<_%!(LZ$6JAx%lC*VXfrFv&eO9ZUU%2H1i=&MLoSH3F;W6lwoI_3EQwU zq-tl8BbxyqEMT%;0dQCZ;IJH9Lsss>R*|B_Mf*lc8RYZqmjMwv!ExE0DV^~A^gh$! zf(jVc<*o`>X))etcjvoJV7v~$iUc~K67tRDs`eOdRvWPj8%WXZ+|<|=e?E4iZj&60e~X=O4##CoxHNuWE#DJ- zu6&JeRQJ|GVz20w zk6nZZ;GSt<>x2`-O8}ra%On#fV6@2mP@z%@ku#ummCRkc8HW}$)$=Le`7rL%neE$Q%a3t`6eChoiv!Q)QKf}zSuC6}3a}=`QhW%%g zoKhg{%Li`(kI7)wcys?KO*SOM3PXllNdZtzvXyIHMod`mF;r(T7Dom{dHRrI(jC0v zhJy!h=)EuaC;np3MvRaAI5k*< zT-fp6a2sq`;(@~VO8B(85`b1z501lb1u8A&S{?A;aJmSp7`cX>Ak6d+1{VV(eGeaf z?}?Q_WWc{7U-Hd`w;nExzr@<(|H$qc3IvBBd-?nMdA=>}JZH9RvVR)77sd--F8Xub zi}X{-kOI)Tf%|)OdP4eEu%AHH^O%Mb#lgbqB3&op@2P+?(&_fV zsk|22b2!A-V{J!PVq5!rhg%ws4Da^M_lKih3z5#w{s#B4k@3J1d_mmewT@At~4Abwyu8J(!Z~B{-2D;rz7)SQ=59a2X}uj99aDwwg~nBpkUdBaTz6NxPovz zHU|+97}aUK3!hMkgfNH%vh~(0?3XXgD*&~yt|`yRBTlGiK#Jf&lsIOm%wc;302HgZ zU5XdkOkQN}iPG>gm-Vgt?xSblcOP@@Vp%gY@!#%>{~O=ie4Ko5zYp)!{9zbB^e!5I zF=<`EqYZ4E5d#<@u?vL-q=gGeiKWa89~**HD;3`Ht&jzN zKHHqqVBL^^de~9w_ZEw(*9d1KcQn%Cw)jfU$aGcyY7Cq-DBYGOxV2NlT4ph*t6f`kb*YGb^6k~-)&#AY1Z$1 z?V2%=UcR<(ooZ`~d1y96=DaoANwkyAAo>g22{;1e!>qCyHaKFK95BF1$}!!YDNCyw zObnqDd17%(SV_VHPA)F?7@154BNY6^Di3e4?QE@2UuxTik?r+9L);fT!S|XN1KE2Y z&RFw08e5`Wvkx=-r_OPf+eP9z8m=h%)0&HRt^=#3)m{eWmD70DWI7luAbwwP8q%o8voV6fn53z&= zbTZZx2=vCrg$a~2edwrA1O@$lqhGat5_D7);@4u_S`3YtPjb)m`GeQBfL{VXg8~VD zX<$hRDT(4iw=5DUp5nC?7CL}c0W|#gFCM%h+p1G+S!WKu0GO(vb<*LOYGprPbrTP@ z;klde+zRrml;jyWCI(Yn1;aGQN`+KmEh-XVSjy9ruW>0NdQDjxpX!1-4H3R67Ew&V z?-l>VpGezhne7^{ z5bZ=a(O=^gnf>|x!@OaM$zIIcA-TgYTMq2wBuP_HIRsVZ7v*yyWK+CQn~17@f@0NZ zJ!9>+UOsWeY}4!W%}0;E{H^2HoAdE+%k}IOTl!YM+hTF&e=GiE{5_ZRJ=xhF>+5)T z72X}dyK^O1xC*aUlCMs#Ce+iSd;xE!5L~go^{>l!XXnXEUd~+yFR?uK_4wtI2D7=L zgjL0T@m~||`13jK2!Kptftl?Z?L<4#PW0DkR|U)o<4m+`XFBsa+L;#Z#J9wAv@PP6)EWyIkK zSFwBJXPx-hU?mQYoBd}zm!A_p7C!bbrOhd`U0Z9>PBa(&wYARdFV2aut;lDg$7;a7 z1@8p9vQ3>NG#sul!pfHnW&;p=awX&&;8}AjX*lV^6R)K#%^N!<4?zL z$E4h}9%Kgzf5lh$*oD7>aNcbk(<+kOq+R6$lLz~TRh!USIp-Fp&^(E}kTg~TG{jk1 z>@IOT?AaEnh!tsUoT3}l062oPL`-Cc>3Q#_U0cTq1)keoRdV9nDu#7&`#@#hu^9~w zo3BG8R{Sa zPLm48z?8pgSJ-|*wZ9tBF&!=~E2a6C)|S;4+VeN$=46?TdI`ebq9Py&ckuc;p`ROF zJhHH}&mG*Nf?YAg*B%bp?qJ91Grhcz2 z<$Dh%zV|!1T}h7Xp_Fz>`B>sT52v+fCE8CX+VOt5Ez!>Dm7s5OerM&~DRl2n^j}Fl zhxY56iS}Elo$uog*@u1H0PE~i0v)t?n8|Esl8JD}Ec%6Qvm^9x1VAI9hpj2Q)@)*C zz%2;Y!R-NRtWG2oG^DuC#F1G48|%P+;mW2)CbhRV`kH+1+Nz53(!`br>Y;&tYqz{N zBLw=Lj(}WUK@rh`Ay=L4s;8k$4UI;0R?5Lhtiy1Et~aOBTggqdpXc;eq)PCOF3`JK z>XIVTUxh7pr~-}52x5;sYXx;xTAh?kK;ca%EsP=J!Gut#EC@y6Zi|fOWg|F6S}^Bo zoWz%)FurCm8CTxb_ol<1fS*aB9)Bbd>Fj82LBv>1byay8g*xZuW?R5s5H_|UjlFOm znux4|VniXvKw1;g#en;e&|m`_;{Ax_`Uo&6DEv%h*Pfwm)s^Knd$xtzh79^hOJwJ( zu8IoR%+7GrfYG$+`rGKYKdY?WT2bS+>>t^>cWYgvr*dFddtF`o^zehro<>jA!0wK^ zx(>E|#NE{7j$dqYFMAssynOF-+H&3kJ03jcI(dQbaf)Fk-J(FMmaciuf*3lz#;nV+ zX<;F&KwAWE1@2-%L=ZvG*?qQc8_P6T|^g*v`Hk=7pGS-r2WnVd0EF6!QD}`Ye6^YvfB$u5|Ssn|pHZ*hDb= zp>S}Z_o3ba%!f&PFrPf^p|^_>kG!iGalnLFK!?lC7fY<79|s6A;^)pCh+d~GC=nlF zvRz^PlVZmnUO2Hd-Qkb)Jlq@Z>X=?4UeCwF>3&(I`(eHwne8{q^Xm91M!XHt+5htx zam??35+k1WwResYzZ!j#;NL&0G2#>JkSi+7nQ`Ko}?C*&V5}gPp-PJ@%K&eEzLJK=A}rzn9m$Pr=$hm`W$AI zRI6&t9eL&ek@c&vInnU+z8H=eq?n zUiS6LZ5J=bYa-oG1S65)6WtL^8#0*e|0&G73{v>^=ZY;dNn9aoYhgjd8iI^KU^$Y= zn0XlhZeWdst-ngYb>(VBIc#B9c|%1*hBK{VgB+5F}DahbK4!<6Y2;Lfn5CI>eScgP(Z)G_)BLXgYtb>C& zG~c{f28dW^k~u2~I^}~$Eb|RF#0Fyi_+JeK+xiSK%h~_^yKqlW*mCyZZ3oZ#=K5o8 zy+busn|86Kl9K)*@>}uoabEp1j8~PB>$Kmb#vo+&zws*V=Th3waeY`k|A9n%ooq_H zm-K!S%aZ!N#+2_pnD`#WvXCAvp7&5nyNT=3qW$5tcH)(y{q$AZ&m`J0F0NmT{u)l3 zIq$Q;gNYZ5cG9m!|CPjZXx&M_7VWoCJNCGU)o6p2*dlEYZ-d8zd@*`h(c~~NN{Zg7 zU{{#o)<=|w$s|+k1{dCZpKzqIxv|buQ(au-uoJ=yK3vOc$>gJR+UYlthgIwozD>Lx zB*9Y<%fxm$6&M^en8~RcI}?BQ`o+AMp~G#jDnkTE{FndHi~oRPdb@O|=O3OseR^)8 z{kBbwVOze4-;f(iOZx{0i3VzJ0`f__QfNZDu9*9Ib?zM3#91hi%B8{ZRb%@j>9PH! zP2n(<7nV~_8!TKM$(_{Lepj_--vZ61qnwQF-*z^Z8?&66jGaCm>+OEC8>SdXk`>eB*M}c(?vj~hw0FgVeZ?1o+8XZwoEQ|8ufzez@nD)vui{~NE;elD&3IQEx# z{%vXPbk;@t8EOYraq2tO5L&tBC0RIu2=+c17+!PiNzqpWcz@pjD0Eb+VtkqXc=szB)J<|y8BZN`ZR zp+>C7?!Wsa+wi#=c5)`(NwK`*9l#0HcU&i*Skc%7{r#j0=2MoySUQf8x7p`z_Rtv$q?*AiM{;3vTI_k!SN@ zSvP>)nvA9x#T9Yp8m1kA+k$nv9v;PFn9DLFTnY}`(lFx0(GQ{F%j&nT))m1?+1U&s zKG|+cwn_#k!FJAKS)@-f;?vwxhT|=m+y8} zRMyl~R=8GgP!~5n{5?q{@L`n}=Q%BO(sp307ETKXd}AKzbi6y!?`!>ZNm zdq%Sn?}f--oeso+Y8?VCMxJd4^=iyV1k+;Ar&V)JGzstG1@5s3-@7 ztMSx$+_gonqUy?u(qfx6+mYiCq{{*6Y$>F3ru-XZ1P=q!3~uJZUyoILJk>6bhw1T$ zi)eWF$9p2N>ks^OEJ-?zg#Z2WOr%@Zul_N5>PvDNd7pIB5@@tnvBC4Jnxv8gD4oH|aNO7!EG2{`&x^>B>cX|!+_`9wFn37`QbIj| zFT`_DAUVZI(7vNQD~giv+S&P$nHN}PZPi~)ZDHS^S^cWhLwuG$mwYm;lE42k8R-pp zpYY+n0a*mUmK8brNaYv#4N&>EqDx>D`2bo74up}tkUIcvNt*=$YOrD8#ZX$l$v$#N z^K$7^cmM11&Dl9RbJjVzLbqdl;NaUooixDH^}{>9kk;4 z8tPc@l8^9qGiY-oZQu}Z^ya6B9;HGRFWPS}2 z)F~_o#x0lm4i%)MoDYtNCI1l|S9I;}+KwzJ%$?zx%94_b9Tg=dm9l%p-P%;^Zrt7< z-_M?^tgWev&uM@2@e5sqbl`2DgO&$XL=4{s`bbfR9S7N?CZLpt;gQ4d_=wA=q@FSz z&cP=IQi01>lTcOGYAOq*--S|l(Q%LlROgb0t08#4EO~3&dQ~^&slj$i=asv z&Z80~H!o;_2wLJudW1dd^&veLwhDB?2e6}or&kKvE6z5dba!TWp2Bx-%Hx=zZbT< z+McxC>HYA5sqIPIo!Uv46z@;k?qs94B-)cUdip$)HafMFt|*?vZFF`0s^t|(_gd-1 z=ki(74d6s{vvMbwLx7dl!@Q2ugSHms;oL=Pn5YvwmaLhd}G)jDO-ciChdW0zSh0!-oWYO?R7Hd3|ffx zRNAHXKP&7O^*Ka4(SJoAOYE^~@@J{-=Tq7XQ})>P2|NUA!1Z#m2AOzBB|Koj$JWnX z5qLFU1^^(=DkjyrtD2q76=fT8f%2tfzf-tYD*lm~x{^d`*~!3Qh1+NnnJcXgYQ{=76r2%A{>*$7np84pr3z|7rKieMR)R;RCZljRA(BzBIVc&$U*O=`D0WIm z@pXEX!s_kirx!^VU(~H}?>N@0?Dw?|^H+DyquU1x^=-3E1gR}pPcgw@E6E%UK z=4Rv0vw`mTuOmGV**3&~?7+U@^Wf`-c+xezgw|QCo4|4u$pu`K{KR#@eEOvaDF4L7 zYG5Ie-;%OZ8VJ{fY=yJj=Dk-uTSo3hW>R27w2@X;xxQ&JUk3KooD@kY=wusxKW`ZQQgkSSgUK|`7 z8?5UnK?F*%w{CFkRabejOE+yA4b~w7S0|Trl$X0)<&`ei^S9Sjmb)wyTUIwul+;+Q zH6;_9A3IRnP+8KyRs6oAp?0;Vp`m8Awjn-7{{VTxd!``s^q?R4AP+rfP4_`kWecGS z5YOQdU_a03fF@2pNUjv5F`V>1NNy^$L-KJSB&i9|ZFq81LboAC10r}_z!1~GpQ#0A z+Cjd@U7;OjSw5wDATP?74wLusiTPuFUAKIgp;A_g3lj4gzcK;UOZc9SJcn={!A!|g zr15f}Y6>4sA1}@6YSR&k8_L+)In^fSq=7W*q}r|iv^kB(6lUHT+Px5?Nu8!Cb%jD7 zSMEr7RQvq#pZ_jpN`w~(okXk;^qHh^DCc8^jt z{Kq6{^zf~;!Iqs#(>qHjXNXskNzp&C=>RE}hAR>uW~iZ}gU~{e-BZ0*3$?p=;E~;W z1RH8Jyk~#xM6gG8V-qdUwY8oa2*v-L%Mm^<&UXltBmML2y6rcrHi;9uCFTKsB>LaQ z{+7>A)5YrECU1tTh$3$WdKuD5OUcD#rX2q|=FvjMDBpv}->Z2u_(gccdDsHdW@wkO z!u^L9e63CGzJd4mgcoO;#t$ira$9{nZ#8O&eJml+Us)ZSgbOxo9k zkGxw7)_3H0cU&!B<{jy$l~nsb8hkH6L}G7y0M^R zQm5UZ>W-qF;{u@XB+f?}&$~ipvuuPJVt*lh1HQUtoMe7{d8{Fe8WEZ)3a&{FQbUci z#@SpWJONtyBnmKGnwPCVyb#p8ig6|4`TJFEG}p$TkD7&622=1Jh~`k<&nwEdb2vbcwiO z)iZ&De^1SC|ID+lM!9ojcCx8v^_$pscvcghl_mcj_yS_&F<<3QxTJspkzXB54DpHc zmXw5gRw!kbZ&&{)A3;<8Y!LwBddP@#}XE4c2EA^bQ-A<7ECcy5v+P-_mpyP%1N zUMtm5Eg-AaUf|5a!1amj9*Rj#OhKSY+PPpxelW_~0~#S8&rUf6BRM016uPSEKzv@{ zj~KI<7q`9N&)1oJZ!$11UVnlBUT#t+3MMe=9rJ)1qo-pbUl)=vkq5-~#-E`qFZ!F; zisH}be0(3?m-HiNwrjR%X1ivKX0~hRR-8g%2lOM& zzjC#{^zL`tmu$-Q`+j@P7|6z1+qaI^Au$il{>q%UX1j`ZvcJT}R1YiDc6>G}DSf6w;CpQa*dY#+?f7JvNTqbG$giN?H-?}fwa zUYO%DeFDB7j2Uwg?RUtCuE$x-?0;4sNol9Ki~cLJke#AG*(9Rz;$feRlRuDs~uKCTX4bA7lZEi6SVT&=T13cV=vz?I_ zL>@v-BCMYm^yU|q&sa9-bh(z(%U_h;@efpm^7BJgY;Z+^z?X_0#0er4eTG#8D&%%4eZ!=ID~&Pv#uW_xTm-~Kfk+}U59@KZMGo(wR(x2 zEFG}f22eXe(1(wU?=R?zDZD1LUE>|1op^`nuknt|{`|~g-mr9$XXOSil$jUQGx{gx z1+{$Txl>EV9G#M5TsrmKi>Hqpb9MS$({WbHvYyH=F_}uTpNjt`{^L((l>>Ut`Xt`n z0lS0NGDoToR~oT?K++-Mahz!}kynoyBb#5O1v&gFX+i8$JHEMmlWBvZ%dvYI1}x6$nkZbukkVQ z96HP5Ia-_yZ|7%IwDYr>7$==^(XQ=v($g5|vtC{h7isU57c|LQQA*(hr~=qO&yG&d z#~sUML%=_dbbDOFK`UmK%cA6 z`73HyF0eXgQ+PYDeOwuosKcd=+bW@k8`aH&&Zpo48>q#qgK+I6P`=8J|M6;B(995I zr}=n*JM_PF_uZJzJ@>pa^TyQl)W6TfS5e6${^|^-0y`kG055-eViM)!P>HdvzP@#S!4jKbKB28rdB}CO zRc&05tv=uG1wOQwUlxO#m^_eBTSMCLViO@+KTa#IyFP&7lJ06>qq|l?wh=v>r9I(Y zW@a|JQKVYaT}`r4Mos{(zeA;M(qXd@Az)d|&|%F{sNelu{ce_;YOAX?Etb^px6xvg z)5_3hi-VCA@=xe>h=xXHyRhqqtg}Oz%b`U>^Eq#z`CyM@&I!F%k=m{dAa}J5Q5_vp znB|5os1J!CVY8laNXkxWK-hw$Lqqmz z8`$^+_^1(KA~YiPyM%Rhtobe?ZdKe1#<%KOPk(E;bbJHHOquuQ?+otRZ`r~0O|4rewl<8kw{5LwuX&rByxyi}Z~VIE+S+FNp}ox4kMrF} z_`32Ojdj{JyCJi`#*Z`GHTfml5i7>T^KVmiDbY^+TeNHZdk^*#<36V+_;}99Kg8dZ zV==(~6LQBcO^Ha5ij=~UYRnL+*jTLU#$hNw@DpH)PgUSQS@VDmr%Z6A8Jujh~kdx}#b7{q;LiITe=+Q?7 zwfD|0|9E=)f6l~zvoO8zw;Iitr*Fpo)Z!vs(jvu0P=NM#Wn1tvfq9k0Bi=rfI5>XNkHA*5QK9Yc6fpP^Waq&%SZH+g}(C+qy z_mvhGS4@-_7ndPQ!qd`FQ`3-)lK9%!M63i+3ibvrQo~s*qIgXQ3IJuKR;O7X?n#Q*t8=rv1@jy{uUS6n(8RLH}>fNxRw}>5w#W73pG@UCx z7JjZihK3}rk=d^C8_`aDMfBJ7<;?!V){v!nK3BSz$V?S%Oenu2ZY5H~peQn&!;M%x zNoUrXjRvyhp=9wpN32M&#{cJIe|Yn?bN}`$%Bp77Oc!6J(ls>xdFX)M{LFEr6FMMj zBT&1bG2)=0v4ZBL?`5H1A=$!Ix&cOq%xjn_qKpomF4G)PFcsD+lnSjP3|f@B=W&b~ zkY;6}W&R`$R_buE{?pTj$_)TQ({ z_Ak;)NgNj=J`8{)nLiYki7azO5UNp`F3tkPN5D4L?KK_F!wlgBvpM0QgU<-{cEn&Q z&d2XZ=#hZ}w>RE*=MUuUvEcln{>|T*VW+0=?%T{*Y{36((!Kdu5fA7Q9tzOsQ=E>; zcJ84_wuA08H{xk!m?!U#ISYG-HTV`*KNRT0xP{$CF?gQ^ z6_Wcmvt8T2qMi1y=&$YHq`fEF*V=oceXYGG+Sl59qJ6EsC)zc8k1!7jygOEHlNPdqk>cgYe0e@6S@C*+ac?$t{?WQrY+ ze*_^@A+{IvXQE6^xBcKH2mSsV)Q#ffgKt1P~^O5s}k(J?fw( zs=Kmp1_x{EQw~AQF0Lj4Xj$g3SfWgaDhs`JpPBiA*1oG2&XK z9+-Pt!L~mk>*pIYHI((^Ldpg9Wm2{}B={B~rc{ZF4FI`Dv4ZrTbm@wkLsmpvlf}(1 zl1Z1YTwc*F52|>5xZKI*>zPS6S^27*2Dj3a+Pf>tFFc$ zE-XLJhQN~v6T~WGTv!^!Fo;^EG%oF~IjCGu~xIxQkq)Mf9 zQ&hru=a+jy<95xkC*v=j-L!<-v764G?HL;CIV)e9`6h~aMi%_jn|e?t6=R`#q>u!M@$~SHVFacw6Bmo zsp$2kbgd#f+A&DFUc#E6WyA$GsBMP(4e&qhmI;^QQgpXnzp&K2qvhbjxjUk#nzpmn z zw^j(3NTka=-ZoxU5uvOo{LWi8G?!+rT#|r63vjm6YP9h>3l$;J$b3 zeq7&h30W$qYj=&5$cJF2-CMJJv}E-$tTBHFKSP)CPNU%?Dn^CK1YR4w$z4YYS5R>|_P6*Sb!CU!pd1*1GZsoy&?#V+Ftik0}e+is@u4dGq5TLXI) z-Uj>*UPJda;r9sto!ikbek67F9U%PIV8#PU*Y zFZRW)|8nGfcAg^VWF5OE{>K;Oiug0m5WoW`+q>$ohaZ3Oj?Vqz??&c*BO|`~$alm0J9m7P zy*eEFZtu`g?{`DPv`*k=RErVo_N>?&;JbdTQx@H`hLjIkKThTfjvXWup%++R+98Qs z6fsOa5{ftn1G8nX#czszBQ|p30yfKe`L@;bZ}8_1W6!PPxp@-hx)JY|C4TQ!39to~ ziSt6iE^!91`4cuGJ)8a6d*+VBLjId#EIT^E9PyvEbg;9cj;6riXv}!F%5rJY`gzJ( z5o|Ygyk12eEuP!WW>4%mK7+xYlkeg`Za&^1=snGPLeV>n2DcmB6JoyN&Gtpr+kx0Ph5462irT_X$V)|!XGqm zs#Tw=|6yyYOq*-!>KhPFFM%Ug(VRD_OS-L4&1;OsBjofb#lkJ)}s! zBsnWf=rZa;Yb|a}CpE}O;+m^LD(O^_lBAx&hH8d-R^7RE@7}HVoXW;WmHs;hH# z>h}bC<-+)PCO4nhGAZ91?_OHi(^PZ37VJ>+Vp3GQ)`mPU=w~=?7zYw7mMI1bYicy= z=FJAA8*ufkytGhU-G|!{U|De;Q0!1`NlC`|6HDryyfA@y$=Q*6FZ`ghw!WdJlXexW zi+^`w>&dMXY$5*Sjt2KhjD&cYTDu=S>@I$GwY!@uxgS}Jv%bo0ozQ|b=Qh`ZL{0-0 z_}~{gBgaM6g0wo8>P60E^{Om5^D^gGUtQ-q=fg9%C+>5`{@)6DDM4SWxgloOHG-;~1_{c3ExJ8xR)R=vRy6^N38G1)-D?O-1O~)&;s; z`d|N*#-qFv>7{blIVB<2Z8RVGmu81{myk>{LXb=XhLXL z?{P!F-JIUE!OO813Mpm*&{|ak7+0zmK7QI#qf{s0)Bi(6qO29Y( z$t2v?4iMdFRG?%jN{L#*6A{RggjFsL*C+bWr|8Hd_P9n|no{l)ePK%{uRz+6n~1}O z^UxyHW0XC?ps}4*s}-bAzv+ZMI5IUmGc!9iGPZZ`c27ft$KB9iSsZ^V{%LIMuf1na zEVd_p+c+|aI@n4_S64?Ie`0RXp}5Q>eg!WV)E1!g$b5i)LD!8SJX%B?hZZ}QSbHrOw1_WxaufVBPX=YuHpuA_jBNwS z6^dM;KpDw^($}aVLlMIC(grfV@IzzbY=}%RG8AEYS+~WOK6K5q_wAVK zeI|L$-)ld*Ko|XCMtJ%--br?Ep41j@ey6vks|PC6<5nr{nsHoG+A-OuU5|PTUV|%2 zamK2!xvBoMf!A$@aVCBT9#7sX?yrILq$8!5bce{3!3C*McHnfu?25$kLI_N%Cxh6Q z%XiZ)simyXpO3vbnqJL%^u-tXTtk>E^my=j)LX`$lj>yX%(3H5gsWdeX^9CKsH*Mu~B^?pJz!iqrbe)cj1kE%dda632jifPZ>r$FH z5%Wvh({ZYP6P<|vJ1cDIh~p+7jC(uy#JjlPxH6>%5)S`W?l;!X{oCDdT)%jh?F{?< z;n>;5#j|G@{o!AR5y1UVr>Tr1B6g$k=NC>+4RqgdL-zpITZie66R%~jVdjJ{2wuzW zdfxv1w02>;Qvb_o?c(k?sxiHoY{y9;zXZ)chu`oV3=@BWd78ZExDsd$IjTviNSk>9 z>S7?l4@DSE>b#Q$ofQ>B6v=EP4_VWJ6DJPfLg!0w_I7vo;&QGxLza`c)G~JBL@fTr zg;VSk*M=gIP`qy-bpCv3faD3c4fq_Xh=e+KjM)g9?8NG!fD6tVIH1J{_Xqa=Hm>C3 ziV+m;f+9&-A=%lG;*wAr4Os>x{Nih<3!f~9O*o4}sz3YH-FHpjao_55_un~l*Lxmk zyLfHvkH+s{=MfZ)ze#JPalwvOae%LIxyOIUUAZ2hZ43C!AfX*}Bz~|3<1~Z1kbtn1 zXpGpSm z5L_$riU%@zKX&26p0cWQWo+L<_9K@KgZMb)oZN-8BG~v)T{3f*2>d~nz zchnTs;HE2Fhi=f=1tP*wcLSC?ht1YQw-21T_L>(y`AGK8zPY6{2Lk>50e?7bIlcJb zH{5NuM)Pu>Xl)Oijz$+xPYv}(dp2(DiS`a*h@b{xRQWjdd5GR{O1aGh7L_X z_E3FGOZ`KSws^fQk0QP#+}ej&p^@I&Y#3h}-d&ag{$$|Y zcd+}hyWF#JC_!`<`S;2HD+6D`9=uLo!QTS!&Xey4uK?anj@AskyNuszv<}{#_~7bz zcl&-C!tzUe40u;7=1`3}Tm|ngyDHx8qiiGYJ%I3icyW>D_K8otG4s>e_szV4X<(0) zfQKMY6RS|T4&GgsiFcDv4t?_==It8Z-6#9~sdzWMizW5h#i*~qPHmZvKiKi+=VVrO z;fL96dj{W%xBu|Mg&(pTJ{H5K0DXBqMdWuHsCJcXHoC$5{J;*%LR+EqTYhORS7pevbdBxX|zMAKo3g@(1kk z=064Fb|x|IYQne?AzWH4Fm6geZDviHb(cs#O~JUKDRb`_sTm23+r`X#k1Y6G1MR_J z`;FI;ceAz8%NE$7sn*#ebSzHH8z;6n>l$l4&AU-Z%;PK@92*&G?bGRSGDgTjC~D(T zyg{B+6L#j3H$&tbU|RwQQGGzPz;>%Xfp2T@wqKdrrQ;#jl?(cs;{`>e3e(?+J8UAekPB(w&buuhUyt4@FCguv9%bALk zgBf8w>*3`1L^UVg9ZrtT?kci2*1#rNiM^mxc261i4(?$=P~{tM+>HDvjAIDzvSaS~ zq|4@JK_HWu7Pj^Zj@+SBkHjo|Hc@uhWv@4nDd1!BE z=U$-a^jGgpaA4pb`~)vvl)LtaZtLCGCDT9m_1+fRkAK1txiA=fi0%k|C^*RHBXlyF z-;JDRQz|ddz|e_kX~=Qv%BNxIKZu`={cnz;yW?K=&A9hR{JC4P=zrimBLhP(y9$O5 zQ(ud0Cc~3G`rzbZ%-eKt?2CK1fBAp8o0t^|XIc!aI|V-{!ThJ;=k9nD`)B&06gGM$ zXfV#lnvS1?!O#(t=Fv-d64DLi6gm*3BJlHLBgZC(fS)H`5^s8q_WzaJIbT^9Kj#c& zef&KAqj$j1pT;r#DDZQ4+{eDYdWrCJa5cFRbF;uIrh8|T_&J`OhM(K4IRLOFa{@oN z3sK@qpyvl-kL^Et?Y_qzMb2jjG^m&6cI}#*Zf zBK*;d`O-bR>)_@XMH+6NbQ-8}6VQIN*C*!v)IDz=ojY=L|Is6;_SSxceRDc7^JaRd z0=#@9-br}*hen?DKx$N>ISntzUINoj!OI1%PB2AAKO&_Hs}tO-{hLgYx^;S^hd|iz zy(GZCKEm#Rb=Bpw@yHPFoFJ49!YGNbE4JcF9dmSVnV#PKz9S0TF~G*Vt7~hkyY(}@ zP#j*`v#Dih4^GdoW{-~7S2tJJV@N$90?mJ2d>s^j7x+3!5>9upJ&ung@%5hRySE2v zJonuFogkI$49f18Uw(PdaO1{3%o_jIj(TU4vmssP>;N$c`}&=ngS?-2g{?#TdY9O` zfKC$^U=T>f-JARpjE)00+;AXzRu@ykzTm)TJ~I&P>Q`dAv*H?zgA=V?k)F~$H9cX( z5m0&lZ5X32F~)a@f=JO2VY{K^HMcV<^NxqRCGmfcw~!}@aAls`mx;(Jl#_Kw&nhvc zuS<;2hw&+AqZqekX`;;+?y1>R+7sz&oj8c1R{Z`>Am=Idch#E1kd2w`i8{Wh6}nyx zQ97VhlcF4zfw~|OQP~=L9E?*1(H?X)HKuQ%j17v$(GbvbshkQg!gzo$rlQdb>jXOm zG}j9H2uuw)G5#?X`}03%vT>VMc{zrQT5ylRm?+Bk4OY*q^nG83-idunczg$F|F(F1 z+1ujre0Os^p0CaS1dm5p5h+rl*e|v}FHSBn0m7)>yLo)?%*@{L&7(6j+g$baF1Wia zdq;QvhwT3Mt)qMQj{bCX?>2A4PaC}a51Lnq{=j)8{OsyjJUEgXM3I+^prj4Cg?WYe z3Syt!9EQpUXqK@Z1MaTs*&hvg3pEvimYB&c5=a3n)2D@z#XJlO44_ z7N7Q(bX9}Y3sScEOMEW-1U%&(Q=!*n_v^1i<+||mpW{6GL3bzVuJY}CZtCxEgTr&7 z480WH({Omud_5fgr9;&$_#0EZtPg{JYi({HuCN$9;&J$oYIkPb%Yj@y9k3i3j7lb-jUsO2z|mJLlU30ru~n62 zC299^08^t7WD;#WMn;9=i6tv)V2mqqF9(~{?&YYVYdL-=x2YF%gcsU-hH5G|?uvg9 z*K&l5F+{96$2bW4kgL&P6qy2O=lBC}ccrxpTbud|TU*QB5VfYz&h0hzIne0@7T?Bs zNhR@Z%svf^cfNZpKFQnj%+^b;p$awqHF?upp{_CMTrf+{_ z_4AM3F?0KaPwrqPEIa<&_>ZW9166Jy4He_#b_CB^TYE<)l9sd_E^-ZA{f^8RcnVkC z9n)UIACC2&#|yeSozpsGq@UOz2rr2g#>yML@ zExE%k3nQ=$pVSdX#OBFVt*5X>M8}g`KxaaVp3Q4xic?&S-14`#zYxESdUy-j`8s_* zc~aQV<44&;ZPU1p84tocqj*tjye}%`&|P&H;}^6sCSxk`8a1X8XSh`O#U&jAVBn1< z7RI7bxLR73GBmxutof!l`C9i5!unQ!PDs02f}_S(B)i zdU{d49JS)Fq5HDog%c+h;?KuUOleMx`18Ht-^|WpKCn?)YAgrwD7x>J$Fzd(>KcHe z2rc8Z1nj8N5sleakP)r`kddmMx3?Kk^&Dh8aNUOd{0-M}68>y(-@bu3xrFdOfvwT= zMGUGMzl-1Zqk}>*gG68a%Ltr-a!+I^FSwOyiPAB+X9CNK!lm%~==JsbF_Q@n3(1sa zLd1p6s6*{DzPM&At_@f^Q-8+T3-JZEf}a=UTV`kDuK;!W;w(JL7*kr@D}D#^O7V8s zS3l*S6HO8^te^>aA9y&$5h528y9)KxK@%BuU}UN!n;0ck93w|tJ$JML#mTo8=uHT< z@YxiC`E82RzV-Fj*{ksn{_qXJ!eCk(Ca1u@UdJ0T4scfTse?9%jm7U(_&wq4$M0&~ zD#rOs{&_7vPmR%}xH~{q$B>MI1CXoZGWqG#yi{JHQv=f>z(QOIKv%WxGVUhy#ts)%|I{xyMKQt`#9E7 zt9kT>;`f|eS~}6Z_IM3as zcH#l>|DL24tY1XyPiHNq9j5`$`2@E^K`ZqCChxz<+t1RIJ(I5Zz$henY zKCQkN-@-T#&{OD9l8Ip`J7?T@+`pnKCKxEeGWnQ+$lYRFN7VLofnIJ19(MDt7QgM?U?Ce{RO* zZ0M|gdXYkzSXcb1__wxA?w#mpQsZSX{xm;2`1!Otz70^2!{Ly{h{yyQBF>t^Pr?m9khSa+oi}`M)(3*BtA5-YwM(}cR zA8BJkMQtb8uRcX-LmyNir-}$=B?aIJh-A$MyQkWXD!bQYsfXKw>s;HoxVXD_yG52` z3(T^;gqNG`?-v(0hB}v4FCFUY756qqP!b#a5ObLXT`S?IE|uCT@(~5F^+m*EanBii z4{*kFGzMK;z$8>D)ykz*vpiN_CP_^V?i!b~ti8M)&nRuFF+&76D4hbRrSO0OM5jQl zWCde_5&51-9LdS9xqg(=?C)qCbmO0)QC!G)TVT_sps$vyXLR&$z9@gCf4-|_S65&> zTQPR+?uaY|dOL6YFw>8OLfdxqcC`0y4933}qUzn6y_GAu!c{qxvI*G_RxCAg-!TS` z`5^HJg}G?3zEl&-{#L4S1rR^YvbxA=ChQS9E45SsDF?}+*oqALIAU3`s`4CkjJout zO;(ntQX+^np~~`NUDarPy6li70Vfy9^G=j}7BTy{wJYZDnCdoEPX*Y6@z>|?9zJ`} zA1VBRbaTdL0FzRi;}?Dlt%W0ZOxXdTNmdSuDJ!Vq0fC`a{J1y zZQhD6v<&CvmRM@bVp})eH@!SF|IFyomTk4QxjmkSCeBw${?mOK2Ij#_HJy%fR0^Tf ztz(yE?1N-X{(Dm0- zy?c=}TL+1hc3%!S5bR~>B9c^3*k#InIT(hM5-2F72ofJU zjZQb!SBQZIsrT}<;CIMGya#m>5Qf7enMhWosD}h-@#E&TdAyADL-kgit;=jf@;0!1W^Fc+LX#Ly5YrM!)1DxX=VengG{GvcxuP00Ea`i)R-039V*)pRV_q!`9e{eX7v%lQ$z5*gLBzR_RgShcWd+ZPER}E z=W2~^%n7TD0XyJZX~8fGCFDj#W>{EYcCc`k@H`ug|ZvRpL9=T9;9kSL9?9$k5tC`!lB& z7EaEKt8!2HdwTr-?%s=X;oPxn=c&j(wgHnOxM~8($iuw@oL&>y19VSzrBog+sbuh! zV;|fF-XR7ik5%V4D$xJ+VoA9S(0Hd{`eMo11g+$mk#XEnP2PfjT1J|1GId# zjsu&{YxjDeuL`Ksq@DMld^rBlodcuJ$#^CV)BCr*^Qhha*Y>=V~y_?Hz zuGYG1s}PGWRXCiLR@DE5rl6(P`RxXS-N>z?WQLtq{NMFf&#bp5+)!A?27D8U5b(}+ zTYLlkUj4<3zI~zleN#c>L4C01vA{<5T~r9WKeW$xk;xkak9C4R7{B1B$=Cofe)4O= zFV2rvqX-LFAYW!Zx89n$^#(oMM%J5+TSE@2B`3HW{?x2Dqy4SDM`wG39=&xOR=NCwjmc0Qq8nr4hkfIWyX~lR& zX=!Cu>872VdO8MR?nf;9wts%=RMtU#chgIrrY6sqzC?e%R39)L%sMgg`R)56et!f@ z4cd{NT*vV@SYBA)6DmF{+6ix^cETv(HB0n=kJ^rcdH9st`5xk!8SNoF2Z`&+cEV?= zJ&ARS{&)^=Pd+DkcL4Fxp=Pz=;tCj3465>Jfwtlu3`}jhog?Gy7epAmQW3l=RlLCI0Kq{ zGY4Kuq!PLg*pBl|H3bOGpa3~;1gdr_Y!@w7u5oVGRo&1DMx6|qi`S~Asv0s&`#StngW>US|<*X<# zZYpUi$QK3tr6N|8m2#Z8a7-Q@)eID+2yxzAbhUB8#hxKg%}KfUgv&G3+um<9_LrAc zja8L(cfZnYK~==oA?x(Cb*S~(k*RL4w|i&3`^#T;*KhyncKo03^Lb1Jb=@#0!m%kH zk9cSUe6+~%Er6Kiad7w~O2#f~Eu%%;J&9rkB~BOJJ*oF8!qO`wS@!=i_Z{$UmFM2) zJx5!XWJ#7~TVAr3E$=DMSl+RnI6ICqa2#hZ$RH3xA&js`fKuj#Ko>1#hgJ41P%bT$ z(n2ZSW!;vxblwZGb@Kh6_Z$s74&&bM_xrwptZ~-+KJW8Bd$_5#LcQc-n#(h5i;LQp zWX^+?PBNQM+wcsi(4AE)BTZ8&Cl;w%cy0yHf&2~DIrfMJX=1#nl#(}3CN(T9qS%Su z!;mTnXca^+7I6l&I=x#i_zdsTE~Ti~0SwPo6-#$V=EQ^98W2_!t1ui7qWR>4|`ZIh6P$!eKJ zGN7F*J5c(QsL_eZBrHB~HijHlo{fN7(R{&S?9{`KLNWf_lp6Bs1NA8HP_G!K^;O@# z>)SWh{j`p)o^xZzWzY3wSVb{2gNzezyitmb%VAa)>9?2%5ru~*4r3kNkj<@#V9ZCp zTR9}K`OzU?L5>?619m@6T}5d~BkCpUp#cC>X9rRcaCI}}1QHoQaFiw*STLBfsgKTUR&W#J;2aNGs&-T)`_0MF%|S1SI9zVPA(eLqtsnkOt`JFlw9w#KOHK zWYNM_6yx{A-kVfBvhjUJy|~q2h~MrWsAMIT18%IoNR=xPJ59E2 z8(FvU3pc*QHkfE*0wb+zeQzP>HJPk%wwZ!N(4dndfML*3UjZP-GWL}G1~bSXvvPT7 zd%O5{sr;K#`I%CT0e8pl;kZihKz=Yi4}rxgV?Q&85fAn_r13r6~kPWcYj*(zX zgr|U_#e^{uzW{#Htqc^m%b%4`RqosXQC2nrb(iA~w-=8_Svw5~f$c%|SpA0O9;Du#?ODE|UY^S~$Nm=I$B~fNt3tdp z<=OHsNoJ*49}NjIF19iTq!|T7#4+q~h!g5A!1E$;oR-Y0kye8PrJ`hgR&8235LImJ!G;h!@e$LwS-dF#lka#>fgpb{D8uSxzby;>^#=2MKWm zO7s||HbmZuL>?fi1ny6P_H$dEIYhQM9q5^}>-7^re)JwG!S>~shqt=F`+5TGrQ!$( z2l0M45s)Ul)4a5thEUyK3oKH(La`8}-lXvOyTE)5aMfai3D{cLtD6v#f{cEhxCN#2 zVcG;ir#c|WCc#&%K+s>}<YrIlq!(G*B%d?mENA3MUI>Ls1eH_S9^ zcaQYm{mt9*Xi@f}OLpO2mLKpI2m5=jHk&JHb>?lf%MY=Bb%_kD!Ek3~pG*F`t6%PA zMc8K|&xLeD4hsogg#Vqe$<<*85}nXsU4(-W>|qK$hqfWP8o*bPI4}<8j`m08FpQOC z^6o}zp$ZHU(z)XNLj5ovNRq`>3dLUMOn%WT(x!5DM5pTtk1uZRtu5a%XYjlotG9(m zha%Cs@||7tF50v4jK;S59Zj9SqS}_u#_i2rq2lVuvqPivqW-9}Hh)vc+Fcu#_b%vc zDr#}o7VPfdw0{SCxOSPlE_X?F`B0YQlsT)1`o z*Z@RkC51WQ_my~yaaEB$Cr4p3X#;R3wNM}72u%I3sbfqmplsU)0XV=EU?yjmE5ntP_9NgJv564~Cxl6nvv~voHB#m=oTdXDSq;SrJPz~obM;6LSSIYiL z#F|Qe@S|@Xy0iAFx4w1N18jA_{74_`LCc&;rj>srkD}<+9~6+{*)TD2AHNl!AYW(M z;TuJ~8%L$t*AlKir1+AjRFM$UU@3v3H$q*0|MfesHQ6M|VY;^Z)s5{(yT1!k^rrmX z?6ApPkgdBjy7*GP4s^4-C zQ`daNB#@T_b~wHfJ<-~ep9>t;j)CB{z!(SVaGcz%REarS5b!f0TJ3&r+qdXuyDhV|-L!H(O1<|E^|cPIMd-t&OUmo&*n&ADrp!f+@;8S%Pm}&3 z+21N2f{I6Q90nZ8reNn0`(fcW1&-FRKhJ=zqlXy_0R*0C+7FQb6A<)a+NxHNuuFE$ z=VhN@#Gzt4fCFXw&vF(gdEgZ-Y2|h})Y8iBxDe~1`ZVnf>HeJlg-EXepHrPYB;-0( z6NTbzfw!1U(rjmBUgqJ$ne!qehzonXz5OGj!(eb2JBNm@Xoome6J7|pdn#PBef8a| zw$}l*wnHTRy(lgpx_fYWcG=pys|U!CD{>f7&I{QhAg6qqEkczDyAMaKjY}t5bczj89AW*$zJbWhUlcrs3Zy4XTYkXw2Rj#wH8dlsUJ?do?N(1uBUX8^;FNodP-!%`>gdO zp%QYjx}K_Efo#zvq${ZJOL$+9er?ZIWMo&?AF+%h>XKg6-u@8Qv6HW8Z+@|9`>MNF zZ?6e6O+2_TT*DW0`QY6{%SBNx6vb@zbk;!i-D?4#e!$0pwRA!5EKw{5{*wG06iW43 zC=wm47y>Z5oq(Le#HWRc57p60%Ey|LKv>PirbdznpAhOnB`gN&y1KkBGA>aH z6Jh`$>gbX8PKZgCCR*SsvgK?!AU_|hTe2P-`oPoQ2~^hw-vo9Z7t)q`<@k{>ksR7ZjDUH%lXJ z-KAHQ%3;vvOEuMAPE)!d+bjDe00`F!w<|QoR#EQvIYBISK?V;hh>F9<^g8_NgfM_c zSx`ojgoUaKiE!eHA`lRQ0bhbn9uEc^CXWXr!a^s3YGZBS79sS*6lXsh@mQWbK?s+X z6z2sz0c_?RXTV=;qIM4mV5*A%_egV477sshBODxUSTZb~9X4-R6B28;Bd#@RGKV4y zP|zzflzDi?@ZRXaK-86)`C@xJU%aEG?5(mYj+a$s^W#657kiy!q(&~(+*-Iuw8#L^5lqY2?bzTV9G#bO0MZq;%E!9$+x{gw~XFWR> z4dmolGMGR@V2(G(Ta2)2vOAyzjN9TyXoZ=Xt)Qa}OQz`6N|skbrH~XGl!CiH)>c+q zTShOp$K%H9ori8_;XCDzG4(G|M;vaGkE@VI8YR`Ix|p4{g@e|K?imJE=xcmb=67EjYYi*49ZG#{5d{? zfM?qLGd^Yh3^>hAVT8uuh(O(m88DEK-=3fS6q~FPRHq zGbCdrDN}4~&)|1h$D_Y%ABc*<%J`DX8u3fZg+cHS;vS{2TRf*U%dwW0 z7$uD!IN^Y6a2P^oX^Cjk8YK~aLFG49=qwX$np~ zlBoa#@`NaVLHNP3P&7;V>$6>D*zH5L zPJg(+O+0V}NR+;1=?W_boksPnkJ*7{_qY2vQiJCKD z?DoLh1H(z8ONU|sVvwz_-G*@^YcY9f17qoy}3iC_V&;rvA*(1|506j!WMqU|Wc8UT(lH;XG1>k-V6bLW~{CXSW;VvFU8vp~-zit!+^PYte^d3G!cy^N#*JS`WA?271087`zk zBvB*T5Kr+!RgR5)WQ$gh%ndhowzZx4Yj~r;<>!XVx3Bj3hL%I>d6qN$l)gtcPsEsY zqS`8(2dgKNfMz#(g4oP%^i(PaV1Fi!o{16%NjoRj>f}bx;jnhq+=lj&f~)J>&e*X0 zR8!cvprxU`F!!Ri-m|x@-B(^7I9y&^;I3$BtUMH|^5#{wT;0%K8O#qG%WaDz3s(*x@lgLRO! ziT-Gh3jubJLXqhyN`_6!8jc}uj5s?dvDCzsGgc_Ma6&4sIhDo2YDtz3mBp$#=GeRp zsqao=w%M$$v+c6W4nOzYlQLGMy}c;f!OknabmZV95GfMZImKIN6q zYCFYv$W8c#ZKstR&p7NaTRaxt_e*X&MNY)5)2hKFj#He{?6y-p z{)^a7e|g_c`|Mew=*m33;@T2 zrkPF^4wr5^4FySV#zkIxUT(VS6gA?I(#K7wK_0?G##0KGNwb}DQ|h{P*A7DaMG-;G zyoNEbU#MbC>a7~T(XTP1YrMAj^p0g6Y+=Vj9SQ=A8CrzV$-A<>E^8*rB_Ebg%Pw|h zWg4w^c6I#iI~`qx**!UTVp3p|W1gPu>>f`mhpA#irP@O$v7rL2pKe1%uL$@aAv%T- zTf=;<55D@~IhQwn>x~C5{4%QnQ7F) zm%&wL24i%hDIhN$&Eue4Svc3~$nxrl`B1tGuczs(hms26np~dFLUBQPb%(R?kGJ1+ zW0m~3oTW`iwV9^dy00>1IgggsmiLMd`Ri;|{FbK9PckZU*cSOwM+L>fiGpHBrQYfm z@(HMRRKjB(;!6QpssU9oj3;6xU_IqlkeIx>y?f88-(KSjM%F*u)6&vYxnQBoZK_+=RJo{h z%S(q2y||-nVR_TCy6;zYbyYRguUV7RfXC0r_{{J(Ss|pB3Ui`u0^DZ2G-yJyn**Vb z_PJ_YD=f&gF~MI_P+C~(cG>bX^I=`GGHb#Zlgg`|QI$KAra2;bEXvO>D9Fz*qS!WyZAAZh zxJ%)shqD18bwDU`=!jTHX)VRnDTP_#P(>CS#nchMLbG-fz+>Ng{u}2VSt*9DJ?#LR z&I2uQPf#7u;&1_8zh!vP&Exr5C&su|-)s zD2qU{J=~#suWQN2HD%q+t-TZCf%O~rH~0KI>tDrmbxTTH`uf|~@7p`xxTo(WbqqaN z59-6i;{-?ydN}@U4TLl0ED)y zpc{yWCkG3eN+41oHf1vh`wX~HB$ETx{9x{ZtD%wAGhX|HT_Hcp%%{m8d@ynA4eXn8 zcj!KOHNk!^=B>y5Jsbzgb%Br+#NeT%szgzLvj(Pc28jMie9CCHmgxVNK4praLVr=7 zpuj>j1LZ(K78tis&%_Cu?~#KgYmdP)Tt6Omr3wD zszqwH@(C+yk;?t3WIdWLc->xfb28BGRl(19FI-NFvPT%fw?)Ei!Yb!(Zx0lG@9gpJa6BRR`F(JlF$kGQ2(m}*k>ArzG!zgipxN31Fhh-HIgQ(Ij~2NP_1HVdAO($ z-LtC0jpdC2Utw8M8G_ymSb<7DIJlHH?zDmU4x$7KfGxdyI~)+)Oh|?GSw;W0_K~jU zp2)grbX}yUxof0-TYqDBcO$*#%%498FH_Taw0FH(%80eLtf;MB(b5`2`>XZ6(eWns zV6?tIN-z2AgRMktT1osM9KatB=14ez_(DJ!NRMU5&k}5K@F9-_6O?Ea1nRh<)hRfD zD_lXpo&tlMN=_8bD>)%4eJW2sB$;c9JJ&|qw+$$`pj8ooMQo0i4YceECLsE6J*_?A z0LGV2yR+dN#R&5~_Dmos?MyK_HkG-MV}ugu+=V%XE~i4XlOFD5q>N6oqkjP?7(v*M z7@icRetyc_Rnyvku(cl_rjuzjE%}&LhwBDqEeN(j{&BdD_B6#nW&?g+fokY{7A1x7 zcdPJffJQ3K}2Vrkg%6F;e zRl;3Tv(-qr0`z!<^P;SvEIUgLa|O|v&iE4gMoM3vk@r)-`K&WGuQ^>ep^etIbek7+ ztlqJIOKV?WD=J)?_D-Cmtrs{+{uF&`{AOkR zD!s&LfudT85|6oK73_*w_pIYj5KVRbp+vd0baj^6nxvdV+=)_3W7l+H?8cGK)!Wb9 zym|i_TUvX1`1se%^H=t^JyPGgXI;s}-n|p@vlDySzP?r;wQ2OV!tzddtI&@3_!>kA zZ)*!&`cK2#@-Y0mk@85uXG8rD=27vM6AK>QlH6JDHP3X5V5P0MO)mo+u4C@$JO z2h@W-YBt%sg4Jzp)uE_-xVtgj=ghP_`b(R-X?~37p%5=Crf6|DLq~Raxg?7GI0z07 z#&!+@SYW;6>y*yPlfEw0OeS9}ItueuDrUtqSBdWoy^nJ@Mb-d~WZ_ddV%0Twbu|Yf z8S6Nc%b44^YW*3eQx=s~b+-((bO#F9H7oz@FWb2e1x+X~=VJ8L`!Rb#(n}Dn+uV5{HOf-K;rnc@zeN}=LZwVr;Yy*uKx)iKlB4-{!;qSx9 z$EV%@V&-B$7Jq}d;3DBx1$xJCCFl|CP~_-$;@db@tQ=#HC68T;V=wdTP;-;6OFMQc z?)e(OuH=McSK+#6_%YuJ$1dk%q38KeICde9{ghu9P>zYy$D%whc*1puaNSS%I77-Y zDRteY7|W0NvC`@HOzDjWA*#d8itKjO$I|HyD8Ojc zYK^0U(Wo8EFqtK-QMS-DJIVjx$hik{AafyaQFe#_<%BpvV5eQD>?$y z&H6@4g(rTPl<4((BJK4qiMc0F*Eq~``d4m#8pHq9*9?shi^0ITj=C&yd2cV_&oFVHbOc(wj%2<{9)R!)C#3)~~f?SS7t#zt9uyQEmdV`ai!* zELv8C#7QO;g^R*vrJH@wcLm2*NHALX-)^8lj03daSBo05bqDGJ)zj zO=^(thbs*|5K* zq+kjff-FbSFQU(|%PI6}8vVK&^y_E*IIP06(C_nbT%pg1Mx5pNP1wV)@jYB|!rs3c zd;e$rSmpHnjd7iaV+#GMI^kG~rq(2$IgO?sVs_A!Uw}^42saY#15LS6rJ0xDzOQmR zRd>R8E@7EC_6Dx26K+sx<_+p~7qk8B$5M|-bXehQ?}D%0tMavN3T2kJCHdOL$M@lQ zR5>1xE63yU!?5(vDw?<1h2;!S`l4ektz%8owTVZPqoq z`&SKKFn86exfcws>S-#!puDjWe?U30Ex=#TVZW2+oL~o9m}UoB#M4(*JCHdS^~{oX zpzE)`V$b^B?3`ZKv3z7ez7SSBt3wM)qfC-lZ zpI^h=@`OE)DkPLNd?Pe`e}Z~WHwZ0M#C=k=0eBCb)w3Iprk@7~EqW_Yqjh?9jb_j} z6oTP&j#bQEHC!7C)z${fPJ4rScef3!V*3|&`>Lw_zN+5ky+bj>yyl)In8Q_o zBz7ZpzN7{LE=*9eFjdly071hn)1}G{iV2TZ53L-kEelmwhpuFWKkFYJjj{a;dV^u) zFI4`f`;?LH?j;zEC|roK{1kJ|g!K+KBeJ7%BU!>`v@or91jgY&)QW}F_#o^ zo&bA81WdSIwIC|xj7S85t)p_@8vnTq4{GyjJ4e?bR|p8TWFvASt$a2c(c&~45daekdh(c1Ssttn)uO{HqQ|Tm z0+z|BE>o&vl44?23`FTh756mz(9oKe>Tvz?{(;ekit5%iLmWPpmBF&c#xlIv{=wF& z4unqC`=}6%uO5{UJE~d-S%$A5-|x>a@V!#%EiNrB_JR!3I$w%;{s{AOC{~w~*o71_ zxJVqKw3)mdXJJ+$iC})e-kgKF0E%5G8A}C+P}-Q^{gTEU$e%M58ys4h?K;&Mtg9|7 ztFC6}JfE4hU{3GSrM*S%-Ur@FEH%jppw+K&S`8Ut7FvHbc*W27amWZWX*uz799MW5 zWQ3W=v5#-!c+`U#Q%8b?Ek^{I;^AlrMx}fvZW)&}$fm)Gg}Eh)Img-9Fh`RRqt)Os zs|KNtw|g7f8oU)Hfy&|0{+`j2hPFoUd+eO{>b5{$t}nkRSY8rcINVXz=*{(nia-=a z;cDR2&p2%kl6F1AeuHtTsma`qL-r6s8%r8-w8El%NO~p3o?w13X~ThRN7^-mT&FBY z(uOnZ7OWdwIXbUzg)~=N?GM*7_tNOnRYM5%3YXP_RW0qgesEb|Q?9qJe9^oGT|G;? z6N{wa&UqNW!drsEyy)O``%S|Bl(ORhz+x<6!-4fGHGJy&kPuDP;U`TvJ{?6QD3I1q zkJDqpzEO=j^z3C_OI8gHu41FZ7(0dQvHiUb?xNcAWy2#;GBn;exa^(EV4$ih5CB?` zOqG%uAlJ>58PfJV+1DbJ!IZM?AWH!{9Ca`SaftCG?Qw)E!0R=eeTSBVtbj>RK6j=l z4lrq&eMi~sDf`aQno_SXx2SBiuWvL|m|Ij*x@K2d*zTsN?%cL;Lrzw0eO68bZTsRX zD_VC{dm=qOk%DTA*`wt4np=9x8A9 zrGr$n055j=!oDx}Em(j*3%e_WUk+AO;15U^!To2<$j)OAKwiB9bjPVk|IC?`S6@25 zmR&&gjSPM2SoVr(9O!wV_`#z@L7dc%BTL%je8HzuE1PY_%65{DM z0_BOP=c^(0h60@__!0TZ^;_<{k1aq__|8gcaplxf>D;M%F>WS&>G+lGJ*gVrucBx^ z7$!<9L5{$ILu5bMhjVm-&ME2K~#4fW{=3fXN3dFPdnL@^+-MnBTymn zfnZz(82?j!W^uFtL8Mf#9`2TTWb0vogdps5AR9oVLqNHYp5bE3+dC0V7y-q**E-XkKyyD`Y=pX8-~8LO2@2Eph8nx-%Q&a}wABxUPh>a>zQEtf(9^ zrB79|+ov|O+gV`@|FGnfzf8NIne5t#+TpjPcidAg&5paCtk`ne-Kl}0jXiZNlRY)Y z9#bAdj}gRA(1V%%msE@#(GlUohvBJ)Or?ZI`_Z9P%s?quLPt;%n+2v4LBGKWRM((C zg+>$Qae?@RpOAL3WyS?qL$FJ%Bbs0GOBcnW5PeXPaHMqpKwocnduvOiybO;HdV}78 z-Q~?Rs(q#rb4A|eARISj79o#~9wk7OGL|S}1HXcoLOF0=6Z&#|%4u?aRb{;{N1uP} z%i5hqh8$geWmSD^tU{loFKa(|uq})a6)W1_UjOka3V$MwczrP`kvP2w*3=km}0Xwt{@8Mwc^`uN>! zjrbQ}A9`t0HeD7rEgU!kQ7EMnYO^Acg9f0Mka`f}0I!FQ?7nHeGfX|H*GG;WP<8!B)vFwf%GK=Un}$ZP%>&_8WSWf;O6Al zlF!Mb9K~Fxg-2$Pxi&S0%}Rg`FWQe;*lZ|&l4r|9jRWLMX4!pIzX$ai6VDA%BVqcA z&?)+S;Bl~k$(ZIF7{kMW@RN^cc|g0}^x2mf&IbhR4|5AO8gD-T%-59Q!2SzsY9gN` zFr8vN(uYPli3#Z93Njif{_sEupoLndce~kn-;2)?|NZ&B->qqQ#5Wl`_F-Gs2g=;l zdpl_EnHh#z=8m~R!%x5e z!L&!S5DW1k0HjLWv5sN@L@ta*JpQ z6y=3{<*nuYqbmbNo{&G>`c{9;P~tDFC@);pJ*T}WP*`4})QgkN-~l8@g`|7p&tWli z(mg-H{`lk^^n=iV{`pZset~f1qa*V@VCyLwh%)jS$daJ>S(w(Qvq~rwuz{$bgk(`k zifSkT7N01pL(*(9Prrav23!ZfuX-tHAOidlA2iV2742+ojx^R(Rh9>afl= z5M7*cptU3x;J>q`kbQBW9X)5;GEB=^+W~ zQAwR(4kU}tx1l+bVN5U>5Vd5nWQ^NcMn-oA7J{PC_%h6TgOgkqi&i((hr=k|Dzr7% zM;kiBmEp>Y@?cpICVT{^Ay_%NG65EFeCo=i1D&CqLj)KcAmA%H2oO^TCtg^?7E(J_X{9q96;su~;=~QZV_Rl6+4|iO1tR zetgOR{O?xqfAVSIe@MCp8W-(@YyN}emo6M1#Bsk+N}2}cA$lMT6d@x<;&CbjRuD9G ztCKD~icl(H%%}%t(h3U#KgywTtEo8s-ie**2v$~hXY*X6aWF!dCw*sXDP()O9&`CGKG%EW zZ}2(Z`vJ}ID1M)Z-${l?-aYh1vN!<2c&a-m)(KK-Fu(`M`&Z!XL>V|OdC2jik;+He zMQ9i6yKe&0d@Qcx5681F#WQZ_&%Tf73<}PLZng=A2N=8GrYIMZ+zJ`E5$8~*Mo2g% zx(f6ZvAWVGWZh|Dg0C1W+u&$5XYON(U$Yse#oy}ZnV zl3}N$#&8XU{{G5EtEiyYZI2^nM zucd3fr)&)l1#Sr%bAw zT@gRVVSdH^_hb~NvHMWsN(zn6#*OPyozh7r(H<8 zD*iFQ_9{NE7mxp!@O$O=$-m-vI!^c2OIICVi+37Z7b>WpFK(h^liOf{pmVB)2=YYy z$IrnjnTTz;KY|B6AS&;8-u>!WgEU6E8}D_bohrlc_hSxOLNV%ivp{Hn{>$7IoVMojv}YZ0zofo#xXAj!dC(2rY<76~ON=OfuH`2`6a%0Rl*`xp-Xk8c$1_?-7JnT+FB5b#8Pc)h(x7=hTW8$gbU?{P^n=0*;wUmsHCp{B9N7>nw3-b@ zEHH@4IF`1HfzJg6WUZq4wS)|JI#-7ZbLy$2aVaNK84F*2KI+IK7guRsgqnpOyC=hz zflLo24|irBD)B3oRdG&5-{m_Mt6kL}#NgBqQ=i$D6~Q2E-@yuFEGGXD#=XkQK(Mlh ztCrXc=V1Tbf&CNI25%J@?S;Fri$DvN-NN@aowHxrV{{aID|CD>5YUj=Te#1&u*KB# zy?V8}hp*;)_%ZC%chui$?}ntS`QFt_SMxnh`~Q3JS9fsQaZUVPoCALMJnij`pwoA7 zI(^OL#r)c9;%oTvdnv?+o`dAao0RHhh$he+0wY9pKQxcch>r!7kv6=Ex-_=%E{#gx zK85m{L3VwlwDDIxi3I$0bBDZ){jfPcCHgxDJK0y|j%GF)f4BYXu^VIG{x<%69Rq-_ z_%-kc4aVl7nuw6Mb%(4BMWil5o*w)_2yEEQa#V@}k{B7i%Z1T+zxY;_2QqV{#7r{nfhvU z=Y2~r$A2Gw$lvZG_+AORR>R?Y9rOi)?-j}47f$>As#od!l~|9bmEYsPa?5*Cl8@SUC0zjw9X(L?8#Y&^9&E7$-pc^cI6z(x99V zf>q;>&pOl>37S{Kv=vd^ouPQHb;bPUYx zxZwPO&8@Y|TVO|eGJapVNz9*mpG%O1USIwUy7iy2*xy6WqSzZr0!4ds@)lvEXpu6o z7B@^%g-g6+O^zUn{)V?`heD<~!eyG{$Fa9MMW?u(_PUk$-VG%E;P)Q@m+gF?;o9T( zBi7;w?xgVO`}xHZ&bb6O%DIq*D&gCMC!?@HGz^8HOAnfjqTXf^by~e{Go&GOS|kaS zHXaH8FyB!v*4mots!GJ$P-R2pK&$_d8U%!gn(x(*#2ma78ORG8fXhmK<&jZ^ zNSlU6uNKGId*|JfDc`{5o}qvu11a(lt99IbZs+Q{y49WWGgwT1N&W;P(U}F`IyWT^ zy>j5I1#4}K&YE|0-u^{47QRnH|9Qd(4hJpZP>0o@^a?ho zLvPy+yC~SSbxVdxG-PLqdZ$6Z8C1xjb2>J;?0^KkQs@S;NytP1cBZhw3=%{^`MgS% zC)!81aoQXQ|5x{kMH^w*Ck=HD8R;4u>T1eLecs~2Bw0}kW-67j;U5za-)N80D%H>*Z7Ak8kzf0xjX%v~{@@T54I@zeVQ3X}lovO(+ZOyqxTqD~B(S zvOerZtWQcN&k!tz49h0MKqtW+80g4MVIYi4%x1MsOv6O%kWDC%d zL*ga*lG%XJ2{}D}J=-BatW1@)rOlMpk~YH5L!lSY&G-+%9nJ;a^k8?DgZplYZiKN- zvcpVa1uGM5E@pMwtXndnX#?d@k`?NPGn+ZgP6x_1YBW$dETY-mVjiiipgvRewH1-d z2&xePAM!&5p;>v28(YsiW5cB@9qAGVUvhWGLL|W!Bv^olTn$)Ae3fg3kB0AeV&~P6+GR)gFygG4W>Es6$V z1hY3~I>X4|bXvx;9WI-OYhJ3r9LYjn7de3r&T<3M3Fdr~%cE_d_evMn_2~JIajc1> z+d)EdQ@~HIgo-kMZJ@?i;w{z`Y19%ciUK%&b7H=d-3@m@1i3@nPgxZw(Lbo_6q8z) zjr0iLi=(?woBG8`)}xMA!Rsl|X~VEfcva>VCa$IhQCNr}o zGr`KEpLq`jqj4w0I5Z-pG|ae9wD$|&!tD?-2D+1LJcqel-7Z?AnwDlkh;}shwe(Wh zFXmcb$N3Z$@(B77H4L*7CBO53xwGw;RHh0HpP-Iv)B{8{+Dcq^R~9Ud`_I@sH?cW8 z=ML}e*fP4dv2$Rgr*}mDIHA?^$okt*-XVQB^1`mK&%oLF)t_EqG4GmvW3zsBrF?X zG{)NHyPS57&DLUr34>sh01WO$WFT5CzaA7-maUxMukNi=j{WiBfCJj zI(h}tdH@lf6bksfMVWA<0VY8|0>CKW}(waPB~VU$4T8JKBXdp{=#0IUQ`D4YRUYV3sfiCEnAiGjtbKWK!Na457>r z4BhJcIl@SkLkdk4siOW0$l;T;gfb1 z?gZ~8!lxGS+jc8%Giq0<;#{XS-fZ=tq<0E<`RAJ z^=bSZ>+^ZmDY0&|tyf~*KG`~b-JI&mFh^iGcq?AaG0V1){}LPu-ijA}fJJa9ApbeF zQ&^T){xjwp&VWeStZ=^l`=xqGhCiiuF+Wto%(|T)x0ucHwpG$vy&0 zDf26wqQa`zN|N1WL%CVnQ;tld#Il{}mV?4#!lvm$3wU)NcziRFb@<9gNx*T8lC(4P z3gw2Pbxg2Y+mb>LuJ_{lR=S>qCL11@kdes7hUP=;@`wB&jKu5}OV0mH>>`~Jo0#E!2B%09vohFYQ#-Mk>~Z#3 zVx3fD@6)l^-ACoP5<9?knnLh^0>Mj`3X+1T2`h=WsQC|Cr<MPo&&Vj1Xh@<*j+7Rqo>k+MBwN_J&F6^M#OJrb)bx+daHz-+ zz#1n18MZKryZXQMoXI2f9F2VJw=LS(u|u2EpCdc*oVPHaOu-i|wwckgT!UQ8nXyut zLMG2N!|?SessV$uVsU1&6Zh^C+vO2f>+~BM487)%f)PtMjZ{WLpTM7o`JcC`-hB4fPF#umCU*fpH@dkFiBbQ*AU!Z#8n9=<5~Y2q^n~D2%-wcjzmwyZ-@y~7mWs!QhT^&t z?guLd?uVO-!XY4$Na1O?UzVNjr^o%=5&UVBZ`;g}=)?-P$hYHmaoymccnq#T+;6G` zWdQhHbGM-aO10M860d_blNL1lNkQUC2fYI{!7q4roL_BXo zJh*e2q@Vg^86E@s@VnSE`NuziZdDld;0FYI(eG4(1~FOqNJ_+4I>Rn_0M6w9VE>de*gxZjq%WCkY){5we~ZPwol??tQyI{e zQ{$vA9$sO66fw4D8w?lj$V{UR>2vTtWTcJQY&O9oZ!&e8Mo>lw-UnsW`MKz( zU7YF6M0$vVuqVvGkv0R0S)*C-_h}Z+sY*VR_s(SRO+L-&?bvrum=B-Pv^g>1XRuYD z2VW!_j-jL?Ks-*X&x<@O&JanlV39t>O%bd@Nq?v|Rr#qhpTCOMSa=2gh%<$2vBZrE zj$sQbOV2t8!R#mCLo8}aLkTzZ@K>5nA2@>cF0TUD-^94ihaZD#v?3;w`=!S*K6vdA zx`k3kqSz^7^_h@fL6s8M)9om#lS>_VxS}GQ_F^}L(Lpa<;tR`T^q0P2M53U^xx!j# z7gS@qJAD8YUqs>6yar$vy84mHVlQ~~7!@*%8oM@=(*uh5q#cTQh%}v@3Bc#J z=@|T*)Lx`#&BL?G`5L9;2izHH%k#Mw!O6G^U+D393+7r@SlIpaAfLxm62FyfH+*jA zU~YfF+^ApeX^N9Mh^m0F-OuJ^hI?6y%Gz3OH#L0*z2_(D4&qib+|lW0BI*nEtA&C* zS4p-v;q0L5$EZ%Fm^#Sj!863*+MN!tm#2F;q8xOyxHVEh6opd(!?!sMODLL+9Or1r zg#CpnA$~VhTDj|xe#FLLzY8>z4zGlVqOgQ*4!yODou^JJAah>PwfF>2j^PjjprLb~WOIu54+k-m1x}ag5=V=DfUS2aWY5$O2^3H&eAV zprab>1GMDK7KNaw1C&!Rwu%7*x^sUg8RxlV{C1Vb@6y?;SAiD za!@Ibyd+u#29*kmpCx{rD(};OXH0yJ0+A5-uf#wJ8?lG~fju0Qu7P_SUhlI20MM-q z@cUozdp07yAY;0q){+R9bae7cR3v&}697Pn=hSGrHK_I@2u1mBl(NQ^+1?QPwozy) zWveTtb&|W)CQc)6Z)c22RSg3jhsYhdeYLhexmjQP*P1mg#?G4N?$wB&Sg}*TG39;~ zfoB5njQkcw)VG1uZyVHa()X>Zt0JZBXR00wj1hL#Syf{@bV?|V(_DZFeKKK6UHmDA z?lesj<_g# z+|exh0htMkP@NDa-4K5V5`sG<-p_Ll$>HoNqIv<`I2#ZE@T~{AF|FtF6)51qqr;rh8b0%2P=yuD15^^B!ti|8*a(u$$j~_q2pBV)odw?OnOumzykoR*uj~S0YuO9E2c^q)NBAJ)* z8FD{mc3i=tlx#upUcd?wvP1bHpX0g(?7NBU1YEb`^IT`g^Kd4A9B@+|Cvfu{m^c&K zllsc%#7#Hu*Uo;+)fQa;uI>}aPKJ@plx&8KQ6bJd4-XGp{^rjzr zQ@(H-!yeq@Yw+{aJ39dCQwpuNxm&_VDX>2un z=_Jw6setKe(Wuknpq25S0=U~S-XLXhqa?Ia>JVg%0p&@qK%fc@m8icK<3$_DMfeyL z0?3J}KwnvpQnd85GhI&k92qP-KPyiq$usprX*56(2T&1z3YivlRGO!lJg&j4ow|8#I~_h7Tn{H{2n<`Fjh>MI4f)oIJyt9188yR^-dIshY#lwjq^JE`S;ItZ5y6@*Tw1ACgdEg;E#G?H(}yMxLd*^K_j3A0|4xH z4aT6X7gmcFYYO0`=TEx_{Xm&XA{SyrbhpDjXWoP4q6xak)II8YAH?4LEB1FlSUr5d z6%uwaYMWB6vb50DBvF#ViZ)Ph3+AyDouN1{8`#7OApu#He9 z10GePWcVt=2=$V>CpI)VTAX)!Bs8b-@HOl96f|DwbD^ZZPrT04zjR4ocvYlB^>8*&2GT+_Z*%}6?hh5VZbaY|AvfO3e!}2e`1RgIE$4t zpsJkYl-Q#L#!M|545LJqQjRKB&zz(<@-u_la19lT0DE}ChgJkG--V3@d)8fZxN%M> za(Z6z=-^NckSy>AJ^GMQ z*Fg69V)h&Krgf7^Pt6#o6nRo=^goXnP5HUov-7Ox$|DDy<+g$rXI4wTE0R0GY1R7U zYegfT7nGj4oMiE5&Q<2Ko_&k`9edse85cX3j8ST`C~Fo*GBXijUX)n`(VHGjwgYrr zB7CQs0>pX|)i(V*`+Kv|RFJ#fooh8$>`MDAzCC?d)4!1pxt`s}{w4kvcHd`?UoI|y zeDsX44LItcN^;@?GX6950|w88S21>qD?%M^UXM*FzKvnyXaY_%hs(|-K;`Y=fSNfZ zjN#|F=VhqXM9&t)K}C-h2a|ei>Y2_cfCFHG%HfgjOcC~B#(PNuEa=%qKw{@^t2A5l zvUlX=n~dyR6S)yreoL0KrNCD1Ou_%3D*PdFEAXe7Lb|8h%~LRE-c>*IDgRWSvOO11 zxodlc*_!L#o?CFj6A#Std{oI6n?wUB3C~C2Jt&E95%W4Bd03|d>X>!N4NC&CcRcaLu4kVu{p(-<@^^Y+q9Uv~rDFU4<~dX`T<_(7nf=M9 zc0KWgeCLx-o%+NRrGNYAuYaW%tp+f>l)c2C6Nr|icyNLdB~9a;6(`5fMqhk!$M=80 zhfM48#qSg0BfI|lKiKu+i={Mg;8G_0syJ7C1NeO9@gA{<-UXA`1H8Uccn#k@9Ity4 zc+EeEKydm%`{5FHFV#%Mo_z(ZStq@+LmAt@@cRUQ=XD54JyY!TjPO38rGdpual=kM zPOXrCs$3^P-=Z4O9?Cp|MVDMBBn`n!My=0a=!Zy!ZY#Xg6h3=|JiEi|vpc;;Bt^S8 zT_!$5ypK#qE|fe$4+g|&ZLe>;{q`k8Up>t8pX(s^`MYaJ_zJ=r^NwDpOY{LA(g z+&y;(yO|vje>`K{rKxcnkc~ zYDz17MMXZ7_^$lD((2KqYk2FRaQJ@i44E}QIT@k!|jr4Nj zx2}yFH%2du;xD~LFPlHm_+-;S)6we&zji@W+Gpl}a_*CSeDT*w&q8clDSXRkAx2#f zfq(e^Vlp=xnGW`I^o*vm_}Ih~SbR1PSRo7zjie~Gv5nfP__0WOouuD#@(VPux_iQ9 zOsK9bs|(kK0>y=>{$w*5umelgUYop;Hg{xzNl^_KMQq}I3<>aP;871xdL4czbjp(d zJbG?l_gTyKoxfrjC3HH=nvCM|`773KSuIt^^XJc>zjNS%70kNw{Qk3deXwBF=+N$= zC5QkWT)1na{P(e)%a~{7p4iIWyf(5X{=)J97AF7)ztATv64nSmjGC7Xc6$pwIy4;y zL?|eu%S3f-At4@w!z&Xp0?fJv;q)32nYFwdHK}kJ4!zEt|$yoT`wk!96m^=1AF54&L7 zs(^Qy=x;8oJS(rNp*j9eOKH`Cyvl}V)UR_$u-T1cf`2u#(J(%*K;Ez&~t>#gm~0){=Tgfw5kSYR$^605Cftv zs}(_4IoV>ClVxqOn-RDvj@yjrI0A&HrFD2L8F_kLu7Os!&|Tol*P^iMw4Z5-`#$Fl zbz_bUbpCBgyOqb1r_UwiAve|hgXiwuvw4%U;D;_a_p*bR?LTABIeX99v2D}t&AZo) zuU@%gbm`*x^ZGXqZ2WX2DEhyK1geYbi(^ST#=OEYreU3mG5!h#W45mX#!NP!fKKcJ zenc!JTmqt^UPwXWBzzznffy7+pwqOC8&G?G@qB0M}D6%!!1-s9I^ffYp1z>FODg`@u z&mOO9^2Za}0i%)1bnHP156!X1ed5S5qe&^%p)qJCIX{*kg$_+MBPp+aI65~2`ZHPK zU>MX;UOQ|wkQGq6BFz*>I=YtUwL?%vUOS8rJg*)1@Oc9;%29lco+PppjB+qYDAoua z6{+!2`IP3p6*Zzs(_B{{(NMC=-uS!SV<(>`17`ibT=p1Tg%&6>NeM=T4nO6)YGR^~(M`bF{0crG`wk<$oqNxuQC zO2}5Jx%7ERqnb;3#-uoP1bDKWDWRC)Dk5a z8`{6kIy|k4I5a$YWI}u{-WmUBB7r|r4+_snQ_mg0R`>wpBj1>rM@o>5O*o|1LSp+) zRtkx#Qqcp{*M$?4Z%?oH!)ATzXg7-v8XBAfez7H%t(!ma3r3E z8ROvs4=BpzS!qyn_)qDznPSmAbxalmB-6BD9V7G~K51o4%!RU6Fo?hu&ZhQEVD94N zgt#z%KYK7cWHNM7+VU$a&I$}Xn2WNEZ63o{F;HKKgt&B_`TCDw?r8#Sm< zUI3wFDA@qy7n;dgAD|F3ZQ9OOPoxu4J0KR#K3R|%ZOg=esg+Bu-f@KS9pkG9B$#>~ zc+|k*Yeew8mVBVQ72l=>KBj?8 zrIIS8+VEOFE>wrs$CQ&^p9~?x z?ts_Xn4q0h>ImWCu0}7+WQ1iAFDR(y{f$IFH%{hhnFV&wqoBxc$^+xCZUFl-&IZqY9iw|cy*C*3j4j- zggCsFiaMD`fl-uThOm`QCL}59MG#?hb|e+Dn5-{)aWwrLI*6v7I8)REF%cnwo2Amj zDQP{>NEQa{X}n-wfEEn#T(7&_%Hg3D+Pv_=uuDqtj31Cm6^0`Jxen%fsMKmcyPt z$n=5IV5c5e2AddfYRJlFCZMUF}_3sD+oA%`kGBWD;Gf=ZSQ?eZkh6RP5+6-QwS z1oHxd!~Z4nC=v05D3Qcq2wH(;I4}xO$S?lsPwW2lC&aqfI#Gkn$ID4n{w?;`zsCON zIORGc65W(K5r_#53+vbeX|*6uRn%kjLk86x6{uz8D02THx1baRG9rKv2)B`%!%>f2&Do9HTqPde#lsm9z_^{kZ{8HQ`A&T`IY$FVvt#< ze@&|bA@50KT=sCEkZ$sbunr?u>RdRLJPzRoWqt^B#8kvlyE#*$&Ksj9G}N{+onE(r zVoWojx25J;fs=XUDF`xUe=8RmjF`7_3CZKi>7?t``~Ah##FE#(P;w`VeZ{^KuO~OF z$W@dcg{r`Tsye05o9bQw6w_*9kTg!e$v;&A@JBK5$&F%w+0^e--xLF4A$~{$33514 z8Utl92r?**!E+}77vw;qO$k!SV;AY~s#(n31MObRirO5nU$}2O%pY9z_tC{F-j&W|NHbd0lXz$#Ur@&Au; zPyJrnnHcxfbyDHU#+|D7NOWRuw68D+K!>TE`s7h~9UPr02ohPQOfOYT#cF3EWGbsD z3+6h7uK}2(s6o;>0hrSYKyI9TTysNWfRkG_12n*CWguq)d3q&CK5kyC(FS=9v1%^6 zMN9m5#;|!*;EBWLZa#OW8x7OpH?x!2M#JW18~NB_P(;nfMD50j4U^x{xZ2t#Kh&(; z$SXH~D^bDm{MfvCvGb{TW3q@NLk$n47yK{k*eEq@0747{#Dc%%1?}-n$=*EmsI;YX zxKsRYJlHHbre2v|$cD)p>>rJ|3(_L$V-iGoNXG1z1RyTzb=vJY6j2gL7U*=-GiA-= zE{|4Qkp0N%-xFVoH$U%7tiZ9@B=+fp>ORe-TqL`ON+_oZ%d;96(0ttI$g<~f<3ifg zAonSvGNfd$e)2*7;6fK3thq{3Duj}`L7d#B4x+f=gJURIs+b*6y>t?K^f>friUSO3 zgR&Jdayse$*Lg*wL=8Bcb8(`!7|v+`fzwG>AsareHWK9RA02;~)rwE@xNpP5nO5j| zb&%00>4*eas&mB}11=P*`mLkHXhg}XVsTykHgVlo#_vEcUp=heUmN@NHo1H4ub{Jt zNjO=MchVD$=4LxVCqKVtk>j>mF%}Dz8`T0A0O8iB8Xk;BRGqOJq0Li95hg$ZtN}g&!fJ}jk*0o)QGZYR%ar4Jc`ZJ^ z_dXvekc`Gl8WFHmg(E%R0z!lR?yyl+CcfWi)d#0*(^hDNR;Sfz&rHIS_IqL|@Uu=I z&j!uB$&cIHHHOKDHl&YA+3l%;A$MyVYVc@zExq7W4n|7&htw40O$70c#s4n&*gyGx zMC2uyr?MY~Lp-JnAufryo3$XbJUEngq|d>3B$bXu+%^VBq1*CXirGz+c1(Sl_Jxm= z9Lb1U{MZcGlQ$>#{{Qw_QwP;&Y2P>&(i(NCXD!oa=+$Sjw=k~^f!h|5qe8dkQN`N1-#weT4R{kpMz?^hf~EtcBd8q4?Uwe&K|Mo{-otZa%LUv_miO zhaG$Lc@D#4#|Y0RVZG$f<9x+JbS>?9pJFsU@o^Dw4qwQtK2CG>^LyWW@AU6Y?oK}L zZTT&F9QNKtNtzUr>t~}$$QAto&bb@f2I@7cHKBM_H>z{_4PFx}QQJ+>e9{dWSHopo zyWkRh!n!CmyH+9~!OEqB=vZMi7`29jh&o4=UErZ+vnlCQpN|%FhH*02cN@?U*THQ* zg`RAmyTsvgptPCBuCY@;bvWk824tm>0=V9_99I*lN~X_-)P8$F@utav$YLZP3SnbstPZj{j9ED+ln3{|WoeD{d!P#Bah{qb}B;@H^;i zC)SPsK1s$R(6YZ#o+nk$!gl;W%)JSCTW6UzeBN`kOJ1~Dw!F%+EH9E5$+Eo2w(NLK z9NTf?#B!QAjQBZ7(Gm4X`#u(2( zj$-F%BqGGO`mK=mR95lY{KMkd?5w=j={Fkvc@u2kK6de$?;H}h%74jnYBdfDC`e~6 zL9Fwa>9M#Rvq`5-8^wPk7H0r?ni`Aa;nvK79hsf~)1t9aWN_K>X-<6vSHpcIou%(EmS>ELeNQSXy7aCsa}F zCW5h8q|s+LgUto8sQ?2=84(c7*0W0&ukf0aEd|J3d^n#%3$sj8_+!P<$P8#^X;inH}KmBZ`z zCvKzA+HG}BO+j|!F>NjCtGC{2w1`rc{(Ck%JEeE+AEqeVF~i>R+7|tvmJO^0x_Dqs zI{h8Q6aR!YhzgxTzt9`+?(d4V*Vk25lzWQZSOb7bY)tfG%bJnZpoL%}DNKQ+efk$DgYKw?i-CnEVH8+L5$C?EBS)AHJ6XledTEL=If zfr5@TH9-U&n}P^BF0(Ru7Y&c^FSR;cTd zV_eN|mhb%myN(DiKCNlWtn*P+a^m#cC-38N8xb-;C{`}S4s&y|5f*UZczpQSVN{K_ z^4MYeoq3su4&@yn6QJFPXLFUrcb~<#`0lc$ZnQ6 z5PL!YD-4bjCI8*PX8Pc?8BxxA{`9ArKmQq=dl^F&{}o4*4;#M8*OwdkO!DjzRW9ko zbYBORg|Me3VKwkv00kkE1aO&fsf&?S$NN(~8K0?zR8@EY9JCc(#^6)#2}&{`hT9>&vW|y+|^qE94znmCsl+}%rpNG=>g5Xr#C($Z^6^w|6Y;%6KBZ* zkTLbtc|s2ETz)_9tded&KfhlcigGd2#3{@n2Z4 zCzSOgL|NH~Wx_v9TZZ?d2SQr=XjT_gEtU<35Bi&~zgKrY_$jIprQ-knqbR2_7 z$}%Hn$c+0aFGoDI*`i?|{#nSLwt@c!Q2|`;2)&KzjdLIBeTjI*+O>kPc6{yl*eC`x zyn3(~omyI&Lk(WEv&4TMo1HR|v@GFJ3HrWJ@0avPrIg%TUMG3Wv+?*L^C z9O-ku1;`s5i4LKorMaSlueIN1QCC`O%sa1D7WGQx=e7K@qH-*07;FW0M@f;FFKJIZ3YAi3a}UeaJlked`hd4%>;gHNLkAOJZ&&~ zABMe(kk(;jn~Vs^NQ~HQMM0CGtbwrI0Ipk&fQ781mxJnQj$V%!%zf}1fV(bq4-Ks9 z?(Ar9ji7%{wZ8&%f$p`1Yt2X@Ce0J_#I+fwl9nG--opr>?3EAn{35e2)0R3jhvjt_ zW1)v)INgQC4o6X;r-4p1p4UwcwO59~(CKs(xjl_t>84_a#TBBpZmOzjA57ng2zFvH zxeIE%i{!>GFe;jD%%sTy?T;mSW~1Z~TrZe37RhuNdY;`t+^o6TqD9hJ_Ja8mb)M)B z#h*yRcJMJ;;Yrexx1`>wF$c{x5vi zH}F}-0(#pOwwAV^)2rZE9hfF7&V!_3cbJs|$5wN$S?DqmoO>vYpK zZ8a7C!cyPVrhYUr^ZMPTKG7zJ9sMs;12bP~tg~kwH8As+8WJ8Ka_j$&8lo_;(0yP# z{a*@(!dzc~`?d-L7SSyx`4cWCb%)`^m++n}OmCrTb9KJpUKY$6VK0!;085+E#?4^R z&H#}QSiaF{nnsg?4imsST}g?!^iRMkJNI8ee}jR3CX5XCuOC?7)e&opgwcn)qO{oM zv?AJwMe1XH1dPsDq~t1rbY3YKLXp!7)hlqEiAD$5EG?>5)p{ZnQ_P0?(1F-pQaM%K zAFS;N)`lxu)|gP*a^(0zY0HLDDs4G%07WnELH4w-s>+A2cMJXgLSJEf2Wp$>X?tT1 z3SN#Ml>hhW=ENYcc3L?lffPED6*>X@Lt4Bvfd?(phj_}ko=?6*rx8fcpX)6t zJ>T92YpUFiZ$8rV(P)~X#0@8`97uT<8(2{nrNT~ry`&0y+EHBWC1a?f2!;`eq8fd% zp{XEL4xT~L;J0>Y|F92Asi-Vp^Cm$OerqW4xRP3g9tYd2{gI@$r$^WWHYA_CN=A*z z3E?&NF#CoO7LNU2QsKOCiYxBI0&0G3f|jBd2BgB3)0lo^lgc8~^1Q$o0{OYUZfYB345Vn$D}vB@PEDwMa{Qi;B4e5cc`PzT#5)JvOX;C{ag76t`#j zZ%Zoh3UVFr3b+pHUvZWc`3k>8C!AGU^SxfzVuL^V^I&VWzbUDxmpuBsrWg3Z7v-pM z5Bt3M9#=1n$u+(??h-d!60Rw#$D_ylOAe>r3pfg7lGWg&c6B%}d8o zUjwvpGi+=D(Lw+@DXm>zsz+~M4MttDX9 z5+p5ExB)?%MALc!UEpLv=|lrj`G3uJbY>uFO{)jciLvtHp^ekyvm1BKuI`Er)K~8x z9N#h`9lzIHmVuzvR>d0p5p#*-lF{+~*$+NoxoEPjXH}%O+g$9}J2<+>Ov6ULV_4Ml zc>-PJDS?(m-cdt&4onRVPZ&*8DM$pS5*Cw&da2>iBG0iFfjMo5mdbpah30ZcaUD+{ zyB4QAGWZiXu6Ut1aA6VN*>`OIOj@4f-s`pxjA3>InBDBa#P;qcKKIoVTh^can!feb zWXRWScqG~#U6|Gv@6FvY)DTNeesmvt($QQ`e;<4LI_BC%5hKi15^Mdw3ZBnEQm3;L zabXGvV_QXZO-juY*&R)XhGy9%bcGr)OWkrAjis~9L{Bf;bNS@r`CdD_W|-!?e(T6= zdslMfjV3^-O~ZZa#&4cjGWqm9{o)6lg3*1}hcO9h9*_-jJMtDGFz5FQ7sM0Uq*#Xm zd;vea#7tVYn<&nBk9AaG=UQ?klL-X~*`gq{3L_n{rZ5xYow44IUhHOjQ#(kX;q#Fh zQ{c*T7?B_nLM&v3;_wiPGU~OGwSwYMI_GM!HYX|ZEdCkw+rDr_St?KR3A63FGWPs$ z_IzY4-r9d~+tl);T zF5ccdV~|Xf?TOh!TV1ety*|Xc zo!2^MF6axtIoGN_ow35F4yUiM`02ETKG$-uwD9Wc)0zBdtUqCY-GW!}3+v-!2uTns z0##`mJrP3b%?x!$=syNo%sDRBf*6>3ts(}8Wd|dCAqY@8BRm=vc#FK?n}kiBZ4une zO&$ICQC#K-qBPH+JZS`ske}FdETPL>g|4J^TJ=*t0ic zr2otxFlJ@2>TnYYAlV*0t7Z3|{RvVb*Na$0{KtfkpRHj(J^L`CTo(~YIvTZ~+iaCp%nH>CU&7^oDDaPhN5qI$`eDMVy5WPf2MVAd5JQILDJX$GjGg z3Z+mj#ZWs+#oe0-am9-!P(z6qrfQ%-Jk+SvBH}Otnq!y$EKZ_*ggq$E1%vZ*LH=ij z_}Ra}*KGhZY`L&DzS^2?LWM8x2q9%eEj)4r>m+#Q_%IFA?j_b)%$r0xXMKbJ4(cMI zxDfQR$c2JATPHY}!=O{h3b`|$hrTcz9MuT&pbQ7{z42YU#(NPdY}&kI|CV-LB32s? z*9O92^WII5Z`!+Q;8yuB^6Ot5*|woQ^sP`^8@{z;1L9}brN&%Ir|?{(Mnwn8G_Waq z1%5n{JVS*JXmj+VUN4Q`Tj@nVvBk46pWiG{^66Z0CZA+~{D8^42eNXNGZ1kdGe`2n zK7!{eX>PzBPcf&b)$6A3ussrn7YYB|?v-MXeQnRzo;-2ep4&i9$bKZZCgdaRM&P8* zz6-7l;AF*0=?mC&o%V;c^@0YSc|-X9qxf7GNG)!URu8HO9Wssy zB1hNEpiin!v#z>|Qr%2wtgnhzM?Iza{sMnGXSM|^r-H#a$E_kK6u&QX+=e8Nkzq30pCjS!}#P)>(<>gnYeM?x*HR#FYWEUbT$7j)qClm zOG~`T$9XCBI=m;8d?CF&IbZSbAwf&g11U-x0jrPO4a;m{uGC;&rvWJlEE6$73>gAw zmtn%K3kE!8RNtetW&;@rD833@xuw=pqaN_%9GW$leLrw^QKbZguL@m;Br_NbtT4&d z%|k=Y^yRIq^Oo1whgO{7^~&p-hwAFfl@|+=vd8M2tEn=(0yh}VpFDtXi^BvHMA8_p zCe{JQIqo-~H6Ac)2sX@`W=tJX(q~Hd`vdmP zc*b7pKh%#5kq!^^Z2lCU!REji*TD`}&X||{JX%>)tZxNsxt3!fD$AzAeTkiC%oqhgrfbo>&7v`hyOS*6|VObzV6y1U?CBFG2Q=uezB&z7Kd+d36 zkE`H^B;c<56+i}Tmo{`_*VX-b1A7{Y0G+(*SHCfMYl`pKPLYO+7xf(8;v%!>r zk>`NWLdO*(;nS#Vu%HF6j#hgdvq3cgl`}HkG>QV44horPN|GYm2&JrzcqOX+3`<_8 zBzf(2FSFM?{(9*3Z&wGU{dex)f8c=l>m57hD-W&cU2qH6L3(%oymq401Wg4iXu=7#ay-t7Fip^v8*S}L0s{Z!tcki#i z=%Umhe#pKj#~FCyFotTJ?RwZD5;BFkR1rWj5<|cd0_PKgsyOJNcz~BjT!|XgzeA0U zf=Wop%FP0>M@xAr$*RYZfzq=3JQ6M-ZX})Iy6+tOwtNjUY`54nTATUbV1Z#PCfVOL zZpwE}Hq7thhZXCfo}~h0K`N=j(SfvXgse1;HkpcqW;f`m4h)}95fqs7(Ea-aox#9? zGlprwpfe!19$jqRo##6f@nW|dRs%AGZZuZPwEDk=#-*o2Zi%#f*%=tVfOf`=Fcm>opwn8VYPP@I3jJUl91KHXIBpQ82Q5*I1#Rq-b zkAb^vmVf-_n{PHiXy6AhlfC)oAO0}^2P{17=_qUk4Oh#~uh7d=QMkzInz=|t*_<2J#W6$URBfq|j?={d<|`_$gh$g0q) zk3QJn%0g4|zR zUy&&c`l#2y3TVlLRsS+(%7V|_dG_qo*|T?=?z?vkd-3?#y%_&6#_tw`LO^H`cEx9L z`xSm5lJ876hl{4y8T4~_r4r{u{tsY5?s^Nbn)V{3ZZdA-`7jF@hfJfvpeO_zf@n2e zgOoq|cNXS5?Y7)36dwe{Ko%~Sc2VF507VdWdf{QI6tz4IgsmZlN(#}kHIXeB^$xUm z?V76FaFJ=Zwz;}4Q5U%Cctg0UK2qDN-DNr`Kf%7FpB(BOM3_Go>K_~FEh-x*uUcPM z=?#{+dxpP4ONo21o@#$2vxr&)96`PA2;Bz!K7!4x0%BDsew>b`5W%|OXVj%wNmAlB zs&phHfSR=}7y(MWd7-U{1%(z6mFToWW)_*wASCn zLba`y9kF6lEgIz2EdIihLCKrATEgy@Kn}J+4mzNhfDGpFb!ZbouK=)Tre_n0gfYjCj1G9Qjr0WG9(u1zQa7A=rlC%8o5pM|skuINQfp+;zn zN2^G#43`ue^crzBUlv#^__PGsNy3aqij1kGb;2v8 z>?sn@h?(S3>;#n!Dvv!R0St(f?Y$$b+{JB0Wg}%pZKXwhW4*D)F1^0XG`Z%-8xELv zXXg`qqVh6sI9B53kK3>K*#Gt%z#1&E6(B(_SSh!r&4!R-5*h~I#$-}fBCQgRr8uk);qrEIw@z0Rv&ipzk zRpU=-(mux`6bU|IB0lab1zRr>{H)5y$2`>*gAy@K8ZV1gdtkcAz>G_XTX2PlXIg-0VN%V0O!Q#+QN2JKk7CnTkLLdl&x zMVuL&6+?MU?yUT3b$NL;eaROjpDh|QadTnJ$W{P4F%0a426|}@n+sLO&lJ2Lk+3|# z{gLO!;PoJT$z4Rd4XsPiObk>h3Uib(x%3F?GLKaPR4nPIC5Ou1`OX#Jm9;t92jyQ~ zob6E0M>Klo0r9W%YhVR_$~D!*J}+-;LOL~Lti*Gb2d1mFISAeW2ajYuJR?9B#q{xN zFkTHYw;(k|SD24itBe<+D7)FD0n?RQ<(L{ToFluHrF<2Hq-nh0z2ZCaCCq;?+ls=a z?3Yq^Mw**iKCsC0kCxe7CW8PMuPgF8eh49^4}TGt78-BRg>F6rx<|^1%+H+)MFO zgpCp8=Eu^41*BsMz9WE@cMC?;h3brk^jn7fIc`}&KDHP4OfeD=(03?@G;PbXlUt6V zrXNjgzlVfl(rH)rKY38)f2GAt{-hoP`6_l?eldAYftfr0pUEGvv>9@*glfedNRz|l zM@d;mlnb&#v8z*;Z5xiP$WTFCQe2R4;dxuW&V~h{Nc7KouqsDETI`QdpRF)rd{(bA z?B^uWq%F+b_M>N}^NP_}QuAE$hRy#|UNB2nY+JYYzWer$*^2d9!91}mL!M?s=Ytu^ zVa=xOI9)QBp8(v>(VR4yqLut>60IuDdC1?-}k4#+6bow7+Q=2HnYxvi^juV<<~lJf2*vP_WzT0N7)$D z!oHw_X~`4uDMO=Wmyh#>-<`JZ^2bkIEq8yLeOX=i9oJ;aD<$h#DC~~!$YF?YNLqxg zaqP?%(;W7P%P+XH0P{niQqxorHd=IGq=mdPZ35VmO#_o)QpA0#l{9ggWv@sJWZ;s!ELu`x74}Pod9VWcc2iD&zv~HkM>u;y5wvZ!9xD>nujfjV6|D8L2uF<4%19<9_Ua&S1I=nY7_*! zS;)+oz_gnZUDIZ6CxDw9B6MJm_S#$ z*3He*-WST1inVeSIT>@h%S%jRG#)S=rFeN)s74fp=9cxtK+4SSiVL zzz|`C@RfbJXu))TNvb*XfK+|vmzaO1`~mVRmq|J*N+Xp5q7Dh^3W={23dwZ4iSP+7 z0Ui+i;Ytv?1@flQm51toW#dyyFl>bEw8-7-uacu4s*@?vT{X-pa4HP ze@L7>^BQ`{7PechZAJ56$0uftjd&4;;{5>WAOj8s4c-flb`C)sl0etci?LYg4i@pH zQKS`yGT3N|He>N}LW;uL>h=(*ZmObWYSe!k0+Z}h@HvdI9jG{0Fteym4 z-3tE%r!*V(5kp!K)%Q1GYLx6pJrA8!Wa)8IXAm%8>%gHUxbtnL_ENmh>htP(w=2b* zLqdi$lh@^hZKC&j$!iw|tOd)vjGk!wEBgzk=8 zV%^=bTViA{BS?Tn*w5pZKy3nYE20w-1D8pX9zfxH!{G2@o_6605A*=U8&N#RgDHBX zT$E0rh+0Hk`iR$xj5rTnus$Bfq~L}8IPLkz5MO#;e2vHKoWzWV^}*Sbu-UZ7nri?T zCFsR6DLk8v;&%#NN_vN8iUTmk)P;nhwV}E>a`)Lnm*!6)XLczY5?7}+YDW0|`$}wS zF+PBZ5k6?<$z&7F++~ND#s?x^BMsbk;-Bu{bN@GAzU=_JF$oZ$B^JJV_S5Xa4<9oi z4+Oa*?^l`Tg%c-|#}_Mhh4AZC{Lv#IXI4ZwrC~4)6lgRb*h%COyJ596W*tfJcDC z0eGaegye_MTT)$GUEs16+lxtxV8UkPGDy?VO~LdOXoNMI<|(GfmB%jIx%09RML55@ zC{)WfP-s-XpCv{vxL`#7@*7LywiqSmKx3Vha9`jDmr~Rg0b7j{uwB5wAo@hH0-nv( zYSgrJerZ1MhLf3?Rx*DJeyt~MupKF(jk&VmYwa%g;< zsa)51d=!O70wp*}BY=_?3NhO(Dmkw#(ForyVZx)4hnS7RIpdhO9_5ySr^Pj+Qwx^8}qXSq(C zpo|yE8D+i6H#76tB!QgpJ4E!5HeRgCcqE($o~hFT{)g~0_qpM;o|-)^=_jVJI_xYi z8vnbH+dRL;Q(L&LqI3h+1;!0b96SO__{O6yrX7sdRu6op9R~v=E$A*y#`xT1kgp1d>LF6aA24h$3?-+G3x*cq?pkE1w>T(jad=Z^6UJvvV45W zPpeN>#<)DsUq~-jzi2;!i6X2zofik<%&;wJwzx?+gi34~@J84^3d)gK{58TTp#iu_kTP3&-(u`Z?@SSzn5TSz6a0A7Ov7KhP+FQS1%oRnezWj>ZzTs_a z4_~!z*D(7;JkCbtM}A_j%FeE`kIny;jUGS#g$HlGnPMz~vuDIc%!6XF*Qv*S16cDK}qnH%5I|-DO>OkG6Gw9ZN%1}%a zYio@*LBe>;)_7Jcr4zYC_+s&p6K^Ls*XckshAdG(*%J^iNy+Z@G8pb?w#oa8FEl5` zd(;GbT7i)_@cnRiOS!+N>85KO?gGb0Kkh0nc72@X#>cdMVxYX#TV0et+1NJNRT>-7 z_i3t2(Q&RIZ?Y*ivWk6zjn-ARIt;ZX)lJ`X+3fkh%D3BG^S^Ctbh=&koIJ^-4OIps z;Xtn4o||pMj}d=kE6#x^)Tp(&RVWVvno()|hV!wgksFc`a8;<|8!*zy3-LDV)X1%| zzPh=l8K933YUK7orLa#uZID^f1vhD zjUV_n0^Ombm+~emf8d?GiYtAjuzU@b4P}ekvrz?SGQs(Q?b*`XEd6R}hown$T+3{) zw0vtNwV7I~vl5+!E~=-)T{1yBD7j8fJ<3|vg32FteW;l;qaw|mZ6@LhlVI|pmW=u` zL(!65%`@57`Hg$_Y}~ME;w1a`t;1t$x12dF-FyadI8hipyGzW)KC}x%6cf*bDN6wW zAX_v#M4kX~VQ>ind_x#OL@~Q4?8I@CW{?T()Q$ir9P)$SuC&OA{vAR)YokJSDEW*l z#^I@{8m8rLnqC2}dhL01ThR)M-$Lfta*?>3MT7N?t&NQrH1zvBqD>upr|QS|>UV1z zE9-+zUeD3h>wCj_t+fZHwSj1@cc3;}UH-Z8!P3frzr4YaYX}cjwGHY;%W&J8NdR6v zH5Fw6ohy5$qqQG_1y4_CbTgYYbTxS+x-3ISsIpmy^C<~I;ec3+b+N)(=@Rx*Ek%4Q zB5{Zq8s|_ot2aV^sRjgOGWP=n17(&B071aq1FjkmqOhG>3k&1E3cC$nSgfrjOjS%Z z6I##wTI*J!)s$xq0m%}fA2;iPHRhlZ<*U&Mqj=C2V(mNZy($rQy)V08(VL0 zooHy7z{AG+iFo9rP4RdWz51htWo3ntILY?(b!k8W(JWt?$y$hKHf#pv3?gt8w8qj9 z)*A0X$^;Qz158E(@{)XEaFGR|?D508iV7yQM#GI24HbYx1ySBrR^oChYeLmWR9mui zO?+4dNOm53QPu_CBNzKzb*19B8LJ`xF}W7BB8f|y;n)t1Ah6R9*lBKN~I!R5cm@dTGvtoFY;p$0_Fp z^}LB5(cu+8AbP_iXO)J^5)cO2ZRljJXl3#alU@{VF6H4UCr7~@1Scsr=_O?i-R3+z z%d|VLU+2-GLdu{m8*PC_o~Cdq*vdhI^pkMP%Sl(4? zzh>S+m;d_?_9gI5zr=g+-XZq#-n&4nr!+mmA7sL5@Q7?hyh9SujhTJ{ zsHOs&r=%k1DM7s9w9>Z=`?>J?58rD7CWjTK6aL!lEPIj?Kl4AM@AR>wuy;)uhf?=) z-f=Ckc)NIuUIi9)`u_W8@4x?mcrfw1-zDZhh4FR+lk^_;twgAzxM>*zN)&@NAkRe( z8{a^@3{r?+Hf8%@p8E3hEGA+U(W>59MDHhh47xp#HhR4xN}{f8QF2D!dpXezoXOqJ{?v1{)1C!{o+k0VQ$sTW2mAyW%zD(@@ zwY6;1c)u@g9qil}fmeJzpsRur{wpdKxrdMVJY5q!;FeIe1CMboZY$*Ccul3XkO~-c zVs-fXV1?dIu!6)N|Co?}pMVF11&G0~rJj#e9|@dCNg*T@k_LpGNT#6);LZ@>LP!Tl zEg*d${}WV}MC2_J;kL@lii=R7YXfPpfyyE&UI!(|r7*1=md>NZuA*UDHxIHbv}}Qa zi(;{h28J$-$;T~2(e@cfIJbB6_gKDVuq8TV>b`XC8zV=%yN`@sA2#pY!;tPf@Tt9&w^JG5w3`v%8?gx!qBIvT|UgD5`>(vQplqKW21**fKUJ|1EUsI z_N^KiMCA)FK)Hj6&d_+7Vl*aSl!%vB!-!`>LtRy*I+E?r_LILr`B?6>;F*K+FJLA`d0Zx@z-*1UjN#XiVHRsvro=fvhTPC*A#m!5+Lp}!9rY+@T_=HXI4XTg9n-##vzzSwM7l^LIxY-To!=T@Ug)pEz&Y(ApmX!%Y znXk-8M6ns}1VNC_RK~BV=2mPzd3OJ9x z%uoh_L#Y>ZuY@m6w*g(y6s=6gN<__^vi1Kq{r>E4$40Ln91m}5Y1tIU_tC3|hOTC= zm$$yZ4!3ftmas!BxzuS!97O4MSM zlpVc>YC&LJ80O3<>N6b2k}9%~M>pU$a6Vri=kOCtcT)^3t0N>IJg{+Vu zziB0V+!Is~@Cv{h!rhMZ4xqSK!eMv*G&~jzj)mK&at6%d(qJqWEDf6na;EY&JP6~b zcLLufc0+KaF*FkF3g;Kc8ydQb^TS>8N3iYh7x^1%_sWkjLs4Zz?S;7KG|z56&uVZB zTo~U|0lpT9aU|c;CYlAT5|w)Ll$I1+x6=GO8mdBuxofWZ zdZ9B9RL!I#OW6xzE99J3$d5Y_!xUCSPlBWu|411=D&(*;hCbJ&3}M7eZ~rWNf!XA@ z?!FuEeG+>9E#bR(VM)A@{ur(Y1w?U&L~x4maA+>IjgOKh^*lNIjQrwve@@ssChWs| zZ|Co=h~Ws{a5y zSJ0p;9ttL!hHTrtE)bZ8{_kOdw$}1^x zAc73FKv`=Il!>H@nzVo+&Nf(ODNsnBI0Bzky^ZjWW(F}+6wcdg4RAHxUOJ zBbaZYD9X!<-1#m?owM%THv&cy6ksym7>UA0bt+NF#T%L1JKh)YdFyJ+%h&&dJY3hh zVetxWUENmZFLk+!TbjH2n~YI^s9W9Ja&@f&Wbrr|j49xRfCgNAEP-Ywd=&yYguh;7 z@$h3AaDXEpGX(2LYH}+bOM3)xA&R3yD|!LJQxt=K$r=63oz6m^r=q4$8jHu*1S-~j zb@9w*yQi{Bt;L?|@~+iAE#5YJZ5K|YHm!!6V#bxw_^AI3)OyR2NGGrcOg^YSD(YZJ zye`yV0=}UcrSk2fLWey!&zh%{siSxHlHzTt96g6BD<$%%J#ejkO@0stP$)A0++if!mo$%e^tF(PD7;UxT@2lKfBLvtiI z1OYw`#5E<^Ag(FMO9~ORCxT${S$xp4kkue5O`;*PfQCpH%($zIA_xc{kp04@RS|vB zbhFVcX-uZ3=~a?xynh16h5bmC({d{tz0y@+wHD-mM1EwtciR8V58X|DO&CS`oV;`r zOAG8-5Pr&}GR?7!*(fQ)O+00oHXE-6wTiqaSaL3w#d84+A~hK@NtCi$I6cX!eDs=i zJ)q{$7|ro@AN}b1Zi@*8EaslH>D+hk#vE5xmMdrDSH3)vQ;?OFpR+MJmm$7Cc8cXd zGwO_FXDuB$u&fa8h{KToz^h~QI%PlX7P|toOzp;!86>mg$q~PM2RnRw+VEIzSC{-Y z4*J4*Q7S)?39>VQ9LTZH7?Xw025JxJrh||L4NMKN1r3A;)$6$}sGrVaz+Cb%BFOC} zfo3VN9REec)#ndb6FcpSUtTt(G)n6E#}e?IeF;wdf6 zvvdU?X~`VZ9VBILF*Sc^MASpcf`SH0V}P6lGr3WRw1nQI(~~s-Hvwu6<_z3cI{bh) zo1pHrT73c+R6B#iqSvmYRsfVQ^pqgemSu{Vn;>_M1e3_pxna?8Gzd)V50e^EgJ4;aZ?kB?KWImco`C>bD>7Ed{4+ov=<3?joQXy*iz z-K2^n^eI&5Ysr}J!pxP~98=~5vz-t3sPd@nNh#lx-#|C75q8G6W94Bx!niV93>GuC z#$dDMn)AmBDiuNX17Xl<61Z84p_n^+*^-Ww%r#TZonmE+s|VM#Sk3%j-U-x`3+qq zzP9Y}+CXq*{`7h5X8&Xl@Ez?w|Ed$3{pK=z*|6MNU%p>T|nMaD;XGB zh&mzBhst?L(oUOnMw~1RtS(d* zrBrvFXxDd&QLsoZ=FI6oG@!jKkzHVc(UrJVJ8)>*jqzQb+WAx3jvZZ}SY&c_Uf6p_ zUcC!8*B!kVcFJ%3Sr^|OkMD}>{sd_N9~jWa253?vF*ZQpAta<@1o%lh_k16Kp@s5< zfhl3!VQbVy<)ME4x>sI7Dc1OX_p!~t_=S8=h*|Hsht>{A8UfgqJSf{NWJ?N(TM0Dv z(rVgH{6VGU9Uwyl8O@-{#*NjJN(P2u6JZ#Tel`&2OLn$iuM}%%yRuylt3FGgWd)Eo zm)mb%Cw7$C{V=Y>5jze$ivAIPb>Szm_s&yyvixs->s$Nps`nILSLms~i@pBRORQ1; z%X81Y^pbq?SfR&Lc&zf!A-afXHw)e3C&e$~^5qHD@k+RwNr50*5N9BBf-_hMmtyB7 z)R5q+Vgo&ld0^lJAm9g9M(;;-5BL?aPQE~X9(3*}0vOHT-?{3}&kf%7*}l)*+06p1 zxj7(juZ`Y**IldbymR2L&-Q-yPJI3|`26{0Ki|0I^AT-UKcBLj1QEk9e1w}($52z- z++52}u(15RJTQ3YXZk*S*Wl+c3Q(A`zYl=cLw@o0fjjZZcMaTm=c>B|!FcvQeEviD zeCmkqLNwh2`@0%vzERi_M-UvW;m8035`_$kNDG3?w=H zH~hCKAam5mnrp>gEO21{^ZWOIX8(Tnk^PUY{_Xz#Z}6Y+3t(H{klo#}dUhLVxNGH^ zK!pAM+}s?Q7xW!Z`2+SJ`!0wh&?Qvy2F@ek>CsP(1T<7;3RF+=UrEVFf;A8#bN(^o0>~Gm$#aD2`y5cb#GYlcRAYBh? z61{;+?SV_g2O`D*bxbW(5$GmTIzbPx8R{3+|8mW`Qeq>)$Kj)p)=|Fngv`9~mRoMQ z0AIJ<{nYb!-@eB+HPahru1FUp|HO3 zt6qQoDhz@~BIuC491y1(f&-c=LCchkY9n4x86CV-d4eMf z0i&b=N#rEau_Su%2XyT7orUNrQRw~ho;_E0dJFLbQy=AjQvazlGB6P7TtXIhBuRMRQ_daUsF@xVSMZFZ|dx%@5+yTee`1=_6suQPvGd; zjWcaUMh~dQ0_3JOZYV6!;@gnvM4JW(YEq?em7&#Ofruin+ox!-MBrE(@QcLt;A2=c zz6k35^q^#c@V5~?D@L!m#$iw44J1$H{|vB!6JLHR#EMz*J-_6?y0p*}zmR=Zerx|f z@NsdSLw1`M(uS%kFb>=TI)zDvn)2w=0-Df2I;Ef`N0%%xVGdbUd`Ylrm7) z_5JgKl#Pb?8B{wr)o|Cz&Y&2B6&Ybffi0Le=s8i4HB2{9xZZ?c078ghpH_1{QYAc< zphdF=0)A7*Kx-Ja0kUo@vGK4UMS&f5{5h*N4>yBg37I(%Zhs`Pf`PQe$MH0j;L>yl z4f(i=6eJl%)Gh1o|&4Epns7Fpxd! z>Y0`q7Eh@vTpwz^L;4U|S^vg9!bkLNqWIa8p2xnJ=KbO? zV40wr0X?8d2qNhZaw>iUD4#9P0LmeW>jB#|ylFY}j&DRMO+ zS)5mMNq5iTO+bc7>b&+tgL|R3QG=1JiUgrNRT!s0Sj(_F`YN30rU|T7qpz{J&;iQ| z9Z2oWP6bQER~u>S1pJ&R6SGBBU6A~w2q&Lz8pevmUw|fvX{<^<#LQqERy6M9<8=Fuj*cDe%J+V6!AY7wzAfyRIxjSiQnPx?U~Xs*>7%FZI@jnwaJ(Np zPK@L)XHTg!oUgTJ;!TUS?q-N)PV{0&F1 zIyzC0Zw>yu*1x^3uD(uNQtNMMVD*apEoE`!5Q(?X$UB^C2?{Yx2uWV$2O+3r)nEhx zWUis?@B$A8)esV~fKfz}F~Z&gpT3f4Br^xBSxT}I9|T-r8n(HIa<#md`Y>b!%Cgbt zZf+^YS*syE1k?sj?8?yQd>6U_gc}>`YRga^=IY7srv5+JgDh(vfX!U8K~Vvsvk2n0 z2yDq?Q9w5@hi48h&e7(ahx1DkC?jb*zvY~MFL8waMWp%z2fWM(c}haEHWvz-h@zk= zb&{6q$`G*-iOEh6;}FFpFceWl1*3IcO%)7opi*igeop#MIte!z+{;QSiC=;dlPpZY z(&!}}j2|WZma<@AV5<;tZu_u6Mfr zvu#;ctGR|rUT;M?GuPJ2f3a3ry%WBlbeFnt#gLa+Uv`Wd7^B>P+!`)Y1#bQ@p$^K@AZAPCM zf~w0`bUojN|I_`GgbKtX<9z<@!T?3>R$Kw#J|PAWr9;RXkx10_6UZMAK>34z87Tjb zSP)CFsyjB&F#v#kd$2uo0f-J`3^>sgNzgNEENd*Ev_`NGgKc zh}aRLw{Z#{DQktZ660&t3~cez(?V1SDC$U^88gHvtr%Qnqs{~of*=~v3F@b*ke;8H zu8!7dpg8~taDDK9xL8W~pL~E6=Hq-8sq%@Ifq|BfT$x{1nt#F21Ms0ne<)Y=dThMy+5QIwRSmScL;|M@$V7HvD9 z)hObTxbs9_M)i>#G0bLSK=%O>nJ{H!SJ)GG8K9(_@KUs%bHWuBU)r{r8| zGFq}VLw&1y;<2_sYoKNEW%|&f_Z+#j^I5*4<j-p^x~f|wdp`6jTjXP&*Yc#fcIR{77CTe0Q7H-RgsoCU9w@qYfOds~qwtj& zz>t(%0xbgkX`mnl0ToCnm)-`ixs~c)DCP%JFsgnjVnx&q1H60Aw6Y9Jaf6vD;W`r+5* zz^=_Fs}|5`J~BYqnShaj=@+Jy2C!btU#4V`j^JK0BVB^frPSk*kj1W8DJcv z`b9k`N%~9GCH^ov{^^v*e93v3IPBVV4_dx*1xqJYl!pc|SYgWJSYXkD@e^wRwg1r)xn+PAQ2Iu+*sHR#s_Z`jPIMGdXayR8y=cNX)?*xmXYYx35&byMDs}rIun3NT z+T@4W+68dl@FqXh;%8}dA0rN{hll@CwOLLzKuDG7sj_(H%53bzVh5GJJ}dV0f#6M? zJtDfK-MCXe@R=i%iA)y=e#{0J6HF4Qdf+b+l{EmKH>6MtMq{$L4Vh_ys*+J*gcD6s zPoKB67=NwETEc_IS;k`EoIw~%0b_jea!&73=TlABi`2 zte#xk(H}X~5|2j@U)Isz-*MTEH;TW$>86`*&D}cKF`kvZb!cpF_NVd75DJ9a=U?n@ zX&aN@F}a%21e^SL4jcTpgWA21iBy5Gq|U+M;dKMMq(2pjqR%sRbaoNWo^ zk8IdE(XnwSo2{#@9Nw^h>egUQb=|G?P2qZW<1xd&P4#X1_w{)P){O5p96x5*J6_wO z|C7aaVD0*gjK{=BYdf-krE8J}eQRB9mnDn#PZBnseSq^C6PH?@@VUndsOp(<6h8Nf|m*C4s_);nP>Ir<{%K%SJOf@A@$3?0d_QMTac(%-UCZod53-^ z5%<^DmJ}x$fPG$YsMRRpVFmGos1Q^iHQ%KuB1O~@dem)UVzEfHt?h31KHjEEGN%6nS-3Wd+!Kc<>v%PUu{xlI3ZHT2@Q5qnH<;6lH{zR7q0LCsS4m{*%fu zn|5B-kN+;)x#P0gcuQ;itN1V8+7h3=%yekBs(+?0y#3JO8RDu4_s#TI#p3bUcJy`- z8Znz^w?YUP$WU@c9IW`#dl;48OPTC-7HhG7$*DvuUOL$|D6 zZOpCBuV`qm+pu?XsI`X9wFr46h}GP-@lY-YTZLE&_6zAk0Ifm6i$tZ#q@2UGXk%w+ zH9#47N(&3f#Iaf{!R~^zow0<-U9czkB72(z3{PPpkC+nv7mnh;-}LT?MtAgH_1oWG z)zaPFa@8FZ>o&51@RrW5E#Y+&cd%Q!TcX`GHb!Ae;F4d%jf0lOaEO#ltw~2x4`LDn zL{KkPl}?y%WIdH3;$1a>qS9i!Oni8q2sZmMJ|0(32Isi&!&PyjKNp4!J8?~Z$g9~m zlvCooY{!nHsC|w%wRYrtb57nR`kD*a;ft){XhFVnV?lkia>MT3Qw?n`Ax~p{$wsTa zb|92RDOrr2aw?R+a0$)vF#a^p`g2Y|i0)kKT|x0&)leYm*D8_Oi0&1@dXoI(aBhNh zV@U}d1yWf4b>;#5_cM__{~-Q*xPD#zHhleTa`KxE>l&u<^vBBQm*MkU@cBi=`)Yw5 z&DqwlsW{Dhux?1o8UWlzcQ*-bG7-pVHPYupxfi-=N3pfEt>`nYY%1D+DQ1(x@}=fw zV^2KsxySbYjU`8vaaH;wYuv0%zHW4I?C4QtTob6Z zB3s%+dkTVjQ7A)rjx*dK&%`uyFyA07xUCOxs(`T(_60@oYPVL8;IaA29UwqNc_#Jb zOw$cKb)XhvD=J(b*}QpVc4Vu#UFwdsu3FXF9GA9>TSrbll)ZoAmJR!}A9}ztI}(a^ zx8D+rH-|=MEe|N;?8P`8XiF3%9TpTRlb6RcoDc=-I8ouJO+#C0rD+VcLmELd*>Xp& z&RE2o=cXJHsIsKqTE1s4V`;5o*KC+xH@it`lUrBA8>>pkGrGeb8rg7Bsx@v)O;=iL zT-@s(^igz)9k6stg&Wp9Y=)LAMnWJM-D1ghmC(0_S}O9Mbv$c| z#x)^&3rf-;#ql1nq0XK2O^JA35rj%nX%XZ&8Cn%yTZPvIfUBJg>=GD-+{b`nQYU%Q zu8`DF&rr-C@)j051DjVX&m6%i_98np7#pO%?M(xN?L*3wXMuW<1UF=KC-6m7Gwl_? z+zW4j)4M4h<=`GBpkOqhL_+3roziV$X`czoLczmPHHkx~nBtHPkOflO?{}0F=&GuU z9Z;YAd-50eCwa7zkMio|Gc1)T{QcQy*bl`mIPdj>;Tr*8mLP_3ZkttKG)gT7T+Qe{ zKz1`_7V=z9^co=36S{%S8W^BoX{v6iuWzYt8f$ACOM6~L@Duuq2Ffst&DkC=VE$gs%{6nmdsN0AyJ(eUqqIaTY?g5i5hA;Iit`xCh9h zfc*2*FUi0C#Va6Tnr>9)6Fi zz%{^SY{zGW@b`20Sp=&Xx{bpQC$K1BZpmf`v|r#a)`Rm7)kfGjpf0g-MMc(J3+y80 zIJum;h1No))hLj(W+L9r+`uT#5=+}S`gvSsr9Z~D&YxO%&Ta}+(odBEBN9x{1{L!9 z;3@*uFyXed@3QA`zDk9Q73SPZj2Lo?2>CQxRE8=hIBrK~d(OgmmGVL@ymDR~x#CNC zC(j}{;#ABD1Fn>nQprSGTF(i}0m_B~2S&MH<=U{ufr*KMfr;5tx4V?SW+zM&15Bjf z`Wg$$$_nJ)(?3LN2GFU2_PIyshAg<3ufJC~p-2O26+_YhWdJKk1MCL6>Hs`vG;;8q zahl+CMu>&>q*&l@US2G$^lsp~%gRVXlzGd%<(@@yg5Y-_Bqzu+T0vIG0mzG22HOYS z{wjCVz);%|f71W>h7G^juz~i}$2ond1|$%#=Fx-eH4yA@(l^ngutIxNl=2b=gyTSb z@!6+m<0m&u;WN+C*Jk>=rvd!hDxJYfFUxbFRBVBnKz>nMb?Rl>6NbH@{6h^Q8TNQC z_-M_zg|K&oOPvg_XW6I_q0NVQ07?^JO)=Ix8@VVl(=&3kU#lOuY=r&TFmqz>>H^pH8|Q9v@s2k#FoSb7$Ki+$6E4I93zJYZ$` z9Pn)`^zzDcpanQROKri3D+1C(oDhEv4ofY6KeP#Z1MlZ+i(VQO6M?ygnNqDZ;UEZ; zhS&&sc_@3Z*=*FKD;h;m9|#sg{2c!HY;5>rv#}e0#;*DO=AX&e{T{1J^SM#D2mN^7 z;xeQm9t5M)!5~g6-~`A7?9c=x5;&%b^CU>Zs4EYM+)C<;omcIucGy73 zYY?2wNnHftlW{u*fJ&za*O|~@9;cst8Tc@;J=Na<;^!W|xTE9ZVW6LWw`OW;4J&>> z+SL_(|1D=hf%C1t*#6bqSMTpweZku8YcCkOD;5bvVnYSaGG_rWf2iLoK)&}F)`@f? z?+HoroK{K#Gy~EL8Q=e=W^|05pnlCDf#Z#qaSU+`9v8q^Hj*@{2 zFm>T3St7fUPNc%!29i;s0R_cV6}D@v_3 z*3ya~$!2(&?CAf&J3P!sXbwe`WFq$_S73a<+%bGGvZ$ zJNpUyf%q6+{~`HY%nB5Qg#GQTPJV&aiI;VC-Pfh&xi>(cX~gyX@0tbFC;~%{csMUZ zAqr(OEf(6I!E{BRaszK7vZMhxFImRN&_~hN2KKN#rhKupTyb#SAqthk#bQXTgcVat zC|3wK#NUm?wS?HI2s;K+7N45gTQnCa{oK;r(qh!rIjK<%r6_<*PIeZhT2=1g1MbO4 z5~yhlk_UgCIk>&O>B4aP_JiBx9~_*ya2tLOU)a>nj!dtwX=tcfKP{ggpPnACZfvY3 z*+ur;ZeU=8Tu!Qe%W(e{Nr`ms0mi~4zJMG=m2h?3MD0l6 zap;BBYaX`2$MVyZU{%3iMNuq#EYgFdS_lCRPm4+0A&@21$5qoSG5I<5rEslQcrENi zq%f+os(jwE62Ow2wp@}q(TLJ$#P5q}olNnq0Hsr{M1{Z=LUBE7w3i!=6;{A$zb+3A zb(Gipyu{!<6!SLt;C>Z+d+ROKfh5a#>TI7#5R%Bn zumi-*N0$kZhK zCZRI!<#Xh-Q*Z`Q?^tw1ov9NV$~QFlXzoIw*(qtDM<=(>08n_AUg zbqKJgAfl4(ebtqtxD;`BP^q&*0ELEuc8`}<&Q5IEh@oLGtzikkRCuk{(-pCU2V)hp zj737B2uOqspy%2Ya;gUHQ-C#r|i-+uVKS>-*oZ-)T?Y$DU|uF7!vzIZq&GKzf4lA+x6N zmJqRe*m-*P&I6i#r~iZ7nu_1>{)gcn!D3iX%Xi8zYW5{g{|8?KaFD^bQ{)k%AV??S zUXnagqzRr=O_VBnK#W7eXE|x&^F@5z_ zC~v8+A%XJJzL&P%YDBGt#dxdy|C0A6@KIG~|M)rg&XP>l$z(FwlgVVV5E8NvkU%C2 zfg})u$Py?^NLa!SK^7H@A{7;_TXCZmtG1T6iz1?}T3c-utyOD%ZELl)#n#qt*4Fkd zkelE4Ip^M)J4t|Gef#+WO^G1X^YvLdRGYz=4*#U(3=aJfIg9DMpAZ}m z5L$4KQ1ucp8I%A)RS-yns0K1IL+~JFDgFtX=Q2WBVR0ed=`{ZG@h6|`dGg7}l!BRW zbU*!c_ZtFtr$R3>59df4aMu&(5HcF2cz|Z8un;8OYW{;KzMPoraO5Vw%x4Tf__8xc zUbju{z7@xn#T9elQ@@;d()`DWJ`&@gbiw|hg@$c`7TRMq|AG1?kMkL#pB(4Q+F!ND zc-t$o*H@;VocGI59f)yREY8?lyUl-~pJdR?PMu~#TMFo5;XpINm5G`qAesmw5NWQJ z9?GH&nu)+4K{G*UJ6zT+f2$=cM@!*l?1z4fvlw^Jd=p^Mc1Jzuj(@S0hQdwJE z6e#xQnFyxBT0tC=zza||p5|Z>cxA)(M3@KVyMtsJ&8e&k8Kx*a77B2j(9(Xx)j7$@ z$vMe6w3^9?yV9egL{VHp^C)F6d2qn)jm4FLQmvJ9+AG`VRIcf*?OpTQ&YeX&cY=BN zbNie*?Ml+%pL6>LgQBco5yvX=a12qp z6!VGHoF(3pG*4oVGe^dI5~C*MOH;h(XpZ#aD8kl=OtR6wy`^P)yZn9LEaxwUvYSJf z0vJ~T#&(t)%o5zNnbE?4MGk!MSP1T>h#vg-mH1xmaiKv_K0SCP!NS}Q&Ni%+)EDSk z*T`Vcg~2|2c9R+cWt5!$UOt1`x)N;_P+JNl_Ac1i{|-)ARE>`%76lo+(1xP!ff@vf zK+&b)g-&r{x)*w)B{5WzixR2<6Hz6+%H>nB7qL~;V#TIKJ#zIUE4RjAw>e`rDfiBu zJNON%D#imj4Nhl6j`l*td|Y%^6D?M#Rt4A)qWOgVOrb*#DHVZTbfStv6omBv5}mEU zcc*{n*2!CU_O>?HHn;Lq8wLh8plIExrs>n0G@A)#E`q%YW+p5q)D^I&^5;Uv0LXOI z^~;8PMJGu3XK!w8ZRY>d>>CFLHWD10h7Q4RBo;QTa`s{{zKpAO4^My!!IGfD;!U=M zRB(ZDN}LgD2u1hyP)iAvW#Gb_f?koa@(CC;WsJ~6Ls3!@)(6_mh%1C8ixL6_WMu=& z<_fT{@{ohH4rhH9 zB*~Y84llQ%E+{IB0iMx-XQhY`+KkfVV1TwjFN<6=HpRLIlnY)>RA!D2#|8KRTNX{* zWOjzsOC#Sl6l)jsA}m41I}QpEJ%3-_u<9@bEMfStViP=_<?PkW=CjFg5gBl(`;I zL?IxrfF#*5T!hno#h=<;Ja@(9{?^ND+XL$kY&xyITf|$myNinJ0-F{GTQ_c8yeWR$ z{P9h_f!l6dq`kUm-QtZ((&T0vpD<0x{R+zh3sNB_CUxU`UCoDj?h< z9xOU4Dte0{VIuvrw0VN0!ZOd0+7xaX%P40*ivs$U8}GoMSrkd6GM8F||K0w5#k9V{lGcf@;Q zO)ZcsEta#>yM%afR}hq(A$n)F@Djay;*nKbbZXc4NQmCOAgLV>Xt5w;D(cijK??p; z1t*ipDAFH-FK7~RpkZ1|EFC9Dyu`S~Y|14_TnP0o*bo)uRx*}z-jR&umHa3%k*Jqx zTSt6kT$DC@A!>+n(5CMR+BB8D7tC-&4o!vq!f!`Fpp8ef0v;P(O9eA@GBukxHyr&i zUE1K;Va2(+6~ReJld+ZRYB*x!kl@CF1UE3qBVDAZCWP{tYcaK0)XqhS2;3v7_<>^u zTyq={+vkQ_gw1phHHl2ma1Fuqqht4oM)3~ifEjzM-EirGQ!AT_%;ZSJT~=D)OUiSR z*)&0rEh1DNu!UqS)yb9zW_U={h9R|}&@QXENJ@rN3q>Ww^DvgAr{ufyGKhwi*0@$Q zZ+9k>ku}*H=XGUfrOrP z{G>ne97{FWEERO^d!TEv(Bsmsyd~&Jz^=^Eccmk|D`}}iyE5WXKrK0ODA<<8MkzLC z3-3z14UxP0t~~cE-9bcdA-hbTHy0zxplwMcDvneTI@KS!DM967UKKgLq-({F0%n|S4w zyuTpmyk-8q&?uYXk;(GmfpSyOfda1JG$f@bc$5Q)83(1O_!4Xshv1Rdz^p)I*g{T4 zAu~Kh&;uMYl635E2xyEehAl#PYkCDNP+1btAT%OPH3Vx(spa&^EKz7zLBJ^V5)3z} z`i|-;GoeCX4G3KGrHQ{7oM<6!y3<)!`?*Oopj7wgR_>_3HFaGpDb)AuSqQ)^Q1?oF^ZsmvG5O`+KHJpjTgk*pG7l{J9?3ONU=RF4~3 z9KqV~fZhV!QsMZD52h|7$sew-8DNt1 zCoF_~BOd4u#-lsoh2%~HeMV73>>qd`VIo$5mL?)OBxFa?C$p;V-sejG%HtZE+tz<~ z2Kb_%`J40Kx4ARKtQJ2!d+GA4F6p`YsW)rZF8djsVZTrs;^fRYYF<4BfspbGzbHAO zYN4Xuj9f)Av0HG4L2(ULF@<;{(I@ed@Nq}A4Mgj~j<_s7PJuCzcp=(bRq`~yuvfu* z3BDmVCiXL9?GOAAk!+9}nX5INM#Cvdw&XP7xnyg{)(tvs!1?bhFaw|;5o;r^bT zgZ$EjB{@QOGXn261e3DU)2NOyX}v9K-2V>ZrMW|hS6JgW%|u5$V?J|Nbv(?yCC%B5 zt6IN$qESb@$sOgFNyPi*!nHSEwJ3LKy4?Icyh`k6v93gTAj@Z z@{1O1c${RzW5I(1`)nMHIFThV;vh@QPz?`+$>7AR1Xq25uL{l|hH*!u=a}Mmt#qW6E7%Iqh3Ey|Z{7 ze>cQ=Z{)cgvHv8u$n%{l7ebekPBseIHbB9Nj)*i6L`cZ&#vt{D&2F@*paRXxpfOg^ z;EJLVd@?AbQ>Dw3a z@u+j^QlGmO{5!3*G0ej)!tVw^9I8_%y(N0_CFBsvu2@QfGyqcm@cb zRiT=*`q%hE*vJ&?Y)Pt(43gvyNv@)XPU`d%Ppo+I$)(>RedBlFuW^89!-rxA{58IW zea#V4u$OBhV>C)?_x?XP=5pNfZyw5m8b!- z8t;)mm*Hao41kH`f^Ccxsb$jIl6yvko!W5K_zS=3gzmM$Uoy;!N9N7@0N3N_2UEi8 zUJRc{3;zp7d4cxk=&-$6Hvsd4-Q9ncJ|T2QeGf-aER;4>!;d72k5M-R8&itPBqNGS zxq|iDF?;LHwa@>yuetA5^U8)``XqE!qglZkSopJInwH>%lO4 zBgu6*>xT6ewqu;5uVF?DLy9z+C#XB~ytI?l9bgkge`(5OVDWqz!;uN0nN)&gj#kyW zSEfy=<& zHvEJQP5BDlkR+|x!a*2x3wqWF2Vn@*LL(_H-jZAy{=h`8sH~JMdo$7}cr+isp^?78 zm=MN2i(jn7fG>|_g`#>=CTYvKHFOZEurMmcfn)<6F1Cy6k zl}A500Km+SDU)Xdpn$J%rsDMKA*wMa--l?f@%iI(vnf!dFsYDCJXIuq3PXjiAEPJ{ zxB}q#hn^4$3j}k?BtV5~O7iT=x;Zy2oYgdC=EXHr<{aoQZud@%%FFt&q_Vyz@OpjE zM1RiTN_URCyuJItoZL@cG46_)3vZZ{)mxLwUd)qHdM~f)^7CxPehGr z*z{sqPVgK!LXw+V`5!PV{&D(^(D_HS3$q|_j|d6dhm9!w9f5nPqI?DMU{E>3$cH1~ zeY1j%+Ktq$g>57M|FtE6bin5}UD)EF8^js_IN%qZdZNKmEl$xat#}-enR01tC7fc>JQNOSPbhHd~5WYW(O<9gM0k@E!FML9z z05hXg7d%GclJK@)Jbp3U)u# zt@x)X9rOU2g-cT?t0Rn8Fb(4ia(8tm)6_gQ@R0z+%emJb;BngD_;0jZd93y!-*jvd z-@rF4TBO~o-MR=bVTcehm6<}{P!=p9w;|{jaEpSTP+UqO?-mh!t`Pr9@?SEdoNchU zc~xREQc~-thhwh3@3JJU_oBZYSgEy z%C=B=11ys|86zzdYD;hJSunN3u2jrkH11|g+0-e;+j=L~*H4^SSLYa5+1a~!Vr6F4 z@{5AWYWBp=zA4}9+qST2YG*?ueobv6Kjdz#2~!HN_6Gn1g?$cydlvH`9xyMM17JZV zgRpF@({_<@ilP&x=9@B3ktLCS`0`%uHEEh82j`FH(|vkqzfuC9kQkO2MC}#@kU70ygvi)BTpCbmbHV7h!Q-& zAO(j7yl+4{+;9$eo^Id2v|&jF-gvk7{w=915-#4|IS8+Mf{E^r4*v;wMxx!)q5Z-? zU5tyipcog*j6zvZgQz%R8nl%*EUE16*uRsSnoYy%yI;?LP%8XmXk3^VG?}E@XWJ8n zqdFNag_F9QFYT>d(y(-Y2aTx{Q(A%f^%BS7fX`efbUblL#+QSLsycRO$3tb1d&UFJ zC}javP|{No!4r`8U}K?#T0BDe)Sft7j4GnzGJ@5kJT3~V(K}!hCn7)*kBS#XPIX(8 z4WY9o6Uw}~NGgRe)7t8?x(Rhi-&yJ{O;1hAaplB2rpA14&0&s>?_6iu`^!I@?n2Xzbv;qp@)&pZO~PXtov;%*xBl%FN?=yqRt`1!v9- zW_k5nH?>V|oZ2RSX`5llF5qwYvKzB~;+N(Re>*+TxJ_zEW*hn!;5iV2=Oi|r&0~)p z4~$bBF{B72$W|nB3+79Z1q+LuRt|g2S`%ztv8ZedhQ3jVm#hISFpf_BfQDFD)hPP!PBCy8jZudl1G zk5}}!_TzuTFYFQ)fRF1!mc)>;SkixJxIy~&P?`dGLg7-d_N<2t(Id;8aXu_t8;&R-P1I4TeHp9wyO&ysTRcB zf!vu%NC=F&coDsl(mGDQq6o}%!^%;)TyUvKfdgr>F_P#}MnXQ!ql^ZDL$4eg zHlkxv_P4NFoQq!gyh}f|2{bmW7s}ir;}b?g+&@Csi0?(&Bjnkqpf!KKj5jPP&Y$3) zFf!_pg4WD>53flFqMEMF_xVfy2^0MO@^bAi{GtovhiB^F@C9Xlaam@3YWEmd#wV7_ z0zC=*=hY11ALV0(>`2E$`Jfqf$Pt6=eQ@L;{z$lG!|9c@dN!)zV1q{&D2b?FhAI#a zk^aoFES^Jk3By9Y{zylt!!pv6bKE)PER*le_a=}p zQ79i8E`%eoFG6KRDkNHFt<95}>G{ll%37S11y;!Aa=U*f9s3pUf-IDEs;jRopX3_= z%S3Eb@=f#Ry*&>Bb2vZWfOq0mN;!08Hv(P?Bq4@al_Q7=N@MjXY6akcvxk}hN&q-l znu84pAFQ)e5DS!&mV(ki;W!|22_8KTNT6j*YyV{EJ$bqIYOu96$jb-+p*Wrk^fr;g zbJ4VhqK0XUo?A=-QSgJnuYf+DNx|AMymfy&lNQz(58Umf}5MJq0{A_JOTN-G{L7K^&_1cW+1Of~BjuX$M@HyT*y*8q=T6lJ+P|J%ZR4wd*zxC4>b$3I!P|%i)6_&o% z4(V&{3|Qf7Z7uX$opv9D3j_*l|IC9*Bs6L5G*Iw zfEtt0i8$8)6zZK6@X~=h>~~lJ|7}Qa&R+hJ7NaaY)vZB0q|;L6b#XpWtUbjyLoTA& z2)HcBxFDEC5GXs$wvYH&822+XQ&ZzY@y)U#g}J!*@S}p{CwK`8r&w~E5wv4`prs{1 zKa>_7KTqiaOn(ekQ}y`jV8EN1k(ZZ|>D5k4DyOhx>@`&LenyPDB6zOc$22_ikc?E- z!hKE`>5L)q@zpIY)iuq{HNMhPAAVkwe^s{>i9c}*c%bVUA)i3w3205mdSokUSdU>o zlN1s}YXN6cT!q(yWHxdsoy_0u)qbs{4Zg>p)*6Sk73mvE%Ss6v;7$q!t>GIf#J|bC zJiAxRRMNC2{_Vl{&W7)uw?Lv-pQGNls3%L;yROHS~m3e6&kqpj}odO$aHY&(5 zsP<-288gr(ic(KqZfd3UJdw(qKENJb3zO_egJT1aOQN)>VuL7$cwMU93HR<*5;iiMqt+ZWI$ zD$l{W)Xow3OR_!XSdrnsuumg^ASCI>8N)(OGB4EfF8vcjyYB^F@&f*8|NX)Xa%9~< z{NWF^*q2{^8Dru&`xoNuuLtsx{t9=_rsN4@X8^SVu-@)GU{pIlzL395=1O3(!AH;x z>D&&rM(1{9(4suI!>g$yzc{z`=4tEeT@1dbq&+9Q(a&Y@2n#7_9PdF30z3dg!qU;v z6+vF*PVj>RKw(|45*Yj~#mXw_3(0_q1rPBW#+XB_;n{eN(Z?*?cS`O;^f35aCH*<- zMawkCe2l#bn)E7gPt+i0?QnGf>)$4{AW2EWr3QRKzym*&!jsvv7H4HGo|WD9W^e71 zCAGcHfG5%8>F_6k)fY1-JVNzVIDBH42;Ce`cM0gT(c{=y87L~zXhgWED8;LjU78F1 zm_kM=bX19HY&_W5csu@YYQm5N4s8bxEduX_*g5>IbB`b>w2g%5hX8Wqu?z)`ThGcY z@)3;U_DCiX?azk%<_pPhlDmL%kEtZTQ6vj41qYz^5SyXPZ%&sBn%4A^0J04Tg`HC6 zhCbdtJ4#Az$THCL#s0PT_Cy7`cD31T&D&-+^;B=D?QM~BG~&6(7~h$}(!B^2^pcN} zU_~Ln0|hDPfpDNT7}7jm0lzz~jX#!OUqB^6g8dC}%$v3#6vDl5axa3q`-nm-EUcq- zQsy3KJc>mi)CihdA|?V2V8kOGEj|#x12h&&+QbA@-;;`3yHM1+=y>(I;GTe5C92OA zICToj+GF@VQ)_J;9Be#utdifx4_20GE41}xW&93)m$pi*cNW`@y+`X!G$XWQ$k__t zn{?ii87IZ?=*;*CMtplD6OOf}NlRXa&Q1vCu*rmXxln%&KRczK`}AorF?`Hl!d)o4F9N?Tf0xu+~A*k3;!p4v|W(n)7imC z-2k@Xou<2IMIlcYd3K=}!~n-T6iN$+j#h)wu%ki|K2V{EO0A1!G;mv&_Ag%OZ_5v8PZhTn z@p03ptz7&a5pCSpH&_5iqOW}GTZ{f7<{O!ZNJxi}3LzNu6LYVq-S|)y3TFdMm2(EO znNWGI(HzaL<8d2Gi+PDuPKft=U| zTa(CmOVP?vHdOQ6ibALMXh+Oe79X1!7r!-`JL3`(oulN7LYi+I)p~^Mvpsq(IEKWy z*u;KROGt?8W^r)|E5HvWz-f9N^+Rc-L`T9(jGk<8`4nB*W1{y^U$OCtdqihwU=N@R zRSyL&lANs~JJr~F>kgJpoJjd)CeEEWx2v6OafJDF$-@h72F7 z!}#3cd^4lOPTK{;MxNCmPSUlSf=A#Xd-iXp%$Qm*xoli2BCKajEu2JGsf)*WkN+a6 zp(8CfEj2mY8EmHuclO&(dCQ$0`tBFvOQaOQQ~Kk8RyYRU4o`e=Zcv7!Y_lK%aJ7XA z1eC#I5$8YU^W$VSLLi=GTx=jh@JjK(-AOH!Cdz>C{U=wJzgE8TsovNmi!~{>@`&>0 zbI%PHd{Ew-_Gm6E2NzAG2C zL^^ZtD(TF<u;)ZUSWY6zPIX2Bkr z5=!dG%JX^q--y^fiNMXXael$x2>Dj61rY~WZ&a7IcpG)uk=Y%)l*M8yS3`V81e z;rl_KBi*EH@`TJ3Skj7C^SnL8z*Tq!Tf>x`|ZzK;0uIQuJ#4mN_ft zY>DTU_@%u}e@3hf@4aXs{>ML#AGoOd_4t9ga|hyy#u~9c@50XS4kjT|!C@mQTsR9P z3ul2sBv<#rL&a{d@mrP3hx2TURCnAlaG!R5y-|QhO!rec^8t zPxeic5%^5f>mXDf4h;B{q$$1(#~fk{afw1*P6~i<1-wo>Da?5xsG+ZAKEGoD-hR9M`8?~w=*$tdQ4e zPO&l2)rstXp*f)sK06~@$dl00qpcRy)E34obS+3@prk+x>&nnKA7Xa2JN00 zpOd>5X}f561+ZCG!UsmI19IbIt79F6(?^LObn zpE+>y-AfL~W=1QY8oVzVA=PNF9%C*QV=fJjld#d(K~#E$N(pOVN*7}+gx~IU1`u^_ z=N?oiu2PccX}hr!e0igGH&$Z)e16R$?N3+;-qWBRqJ=;>9M+*ltOIoOq_jGheZ+|r z;S}ZWM-HbdsM8^^16B(+|KjZ}pHE5Sk8928>E%3wr@fFFz0jIR?N0$tQC?;p{-O6u3PNJ`2 z4*!KYxLHlGGSLceNZ}_YI5DU?AymUlk~GJLD?gy@X6_`HD+#J&B)~*w4r4|Vt6=g9 zj7v{HwE69~wj9FDPNk(B+Va-hn-8U@^K5J0^ZFchL<7!=bt@Q}uT&!Efd{_ho>=nn zm#eOCKsRkiaCmNO0j<8V6R|68c@Jy*j&DJ;M0QTpZcapO|KOpv|13s~x{3+(8 zELcvQBG}>ZR+Ev~#qc@d$mF^ehd1e(;P8jo8HPf0Nbo9bXBK#mfpeIX5gaF;#r-|! zeOS6#dzlw0g;8|Z7mW(_VF%&>MPGZ|NF-^KnS~*yIc84wjn8s>oS9kk4*amSYgX0t zyz!YSsY%H>4^&L)@OWWwT;AFGlY5sp(sgcng~Y*SnA`K76V*9W=FXX zig7@~xziC1^QO(Z1=l$D?fp=T(RLJj@4E-;8ww zaiWw2gtVkIvo1%mKwx!OMlhEi5=RrzdbCI6l2HX)`sYGq4VDbW6UDSJHSAdY)qmB} zy?dAKz4FSQ{oJ8_w14@Pe>i}F9yoBI>j1g+3A$j6v*&n1Jdny5C)iX9cic?zS&VOy z+~Wd#sBAfjb_~PJ5mMMbdW3&wlqcXCuPcZ|1|L7g`GBI6-C07M|K-Xnm+#j;;*R}2 zS6+#s?p^u^F*q7##Q5S_&towdP#BOoIv{}w1jz8_Uk)!I0Y#ZV1xHOJanQe)j`K*9 zgZect5gBIykd#+p=yA4t&7ni9ck7^gvC zQo25RIW7ol6cK^=T>2h4Oon~k9gGIZU~my-oJoNOND;(C%iQ1v_)Kn6Y;3%s1UOG z##?SV)Oyz~w{-EfSFgPOx>b9%+q)vxBaSUU4s?a3O2=#A63W?T@(YHPj`#^fC$gMm z8O&QxsUjXQ>rCYLayh6ZD4~qdUXzYM&OG3CJ1*k4ib-FkZCJ+Fh-nKXHqRS&02w(f zHPc`;;7JcSOO6HzgX16bm1auvN=7gzHVu4~H}cDGx#h0bL$}^((LD zl&}C22)k05rX*vjk{CRO5l?>BgJ(I?+F84j5k{@AAWHODA^efAg>||e(OC-a@^%FzHo&j2?Ddku?^sz-UR20_$UfhBV{}0#AI?q z&^>t!fc!_o8gFN_>;i9oRgwT?ArKme6RF!^Q6@(UDckvoNA-O5i(g#X8{@V)up`n@l@9K2ycCvGU#j%7vec=~Kp1u&d zfZszOJ$p<&{pZG~&g>9#8TuLEd=b28cLbervCuBZV}+H(1VGwI%;Oiq-=NxH0dO`+ z!`~Wo8p+xOeIkxc`XQd}DT(TWc!T!P3RwkN)M=xeqQUv~E3_NB5_;-!=Gwo+$?oF8c6wEA!R+|jdAa}?V^}%84z`H&kM_l^;E;PC2s!b5&2Du>u2V{loKJc|01@pEv_)zWERkaa3c) z#Q_85grcr7d4Z7y1(uXNAxGjViT0yC1hP)kOwuu**lHZnb`O5>IcwkhUH@9XMeAy4 zfQQLveUPsT#bhzhhp-k8inVY7JEL8RkiNnA>k9%-p2*wgs1VVG){5Ja-pFDRB_~KB zZL?sEetMGa6Aubg)~L@3ZVBlZWNl^|a5NSsAcjj)7>;jGA+3@9b}{C0v@mPeYHzIl z3V#qwwXi`BUaZnJ*Zc$vW`b`b+x=)9F*X=|0>E1e_)*S($gMUbhsheyTVb?x|uIq%S%9(~?0-WQmvr{UZhmhETD^bPPkx@ilE)Ie)cH+r`_P zX(r7(JD1?Ud4yz?<2TJH8{T$SOyo2P!~>8Cdzg7tF&IT-P;AP>mjUwK9I@?WBB zaqf_B*m}`7><-j7V3!bsT4pGB0l`yh_9(r#Ve%#xW41HJ?-k-(4XCT|@~*1kNZaWq zY%BZ&b@m7Jb^7_|wYPYF<^A4_OEbLpYbT$0;#=zvp-~6q+)h3p_-uQ`Lh-E-Yps4a zFVTJp)XZ}HqyJy0pxzz&h<{z_(_=#7WIY@@qmV|c9oc(u=))6RoWA%Vg#r#`oWH`6 zAubBJ4kBD>R2YK1ZX6oF7+rg+EM&8sODg}Oj%_&5Rw!d{U43!j@Gn;GjWh(Y?}#`FT4H8pX;%@6Hd zlaPrDcreR@R5U%UK~=DEz5{I3j_D-Z@xJarR+M(u0X813VCXUf2x~ zM#&3xlK`s5g+?N+F`?l|+l&YK&-o>uy!a>#$5rIiKBCg|+@3xz*5ONb#^rgkoL~Q1 zVqcyoIWaoB5a+9!>+$4*dPV!(uEb8-d(`H3Gbl$yA_F z!~y0+HcrGe*0ISL_0?|9-(%IehS~#JxUwa4|-}69-L96kVwKW z1VLh0XZIUSVJY5PS>ZruYM^Tq+5Za)Q76&k`SL3-ul@}Ar4}dhceLEZV)9$Pwqt1M zf!K6AhpI?-&8S(5Urk7{J8-hNJAlp1X;cLe5j&j@Jdeb{f+!)?bC!KlT{Lay_yz`4Q(42+FMs`hhwmK&U3@m$3gYyf& zbFSJb#yjEKD2%UB7Q#pReEs0DFbvcM`a5G7!$mJbUV)f&W_>lJahhco*DLz5_jHrlsMBnG|=;`CC9!I zMW*x_acbba?|?svGgd{%GcpRXCy`b6NSJ|8Q~3L>T# z=Z0X!A^gC>nAmYWNjU9sWpNn~YjF~X_Q`HY8>T@xCmnowZ*@_Ec!U! z`ud&!(Mym5XhuyqF8$#D6S(wCc9Olv{=oji{>J{vPO~BS_1k$2Pv9=t?lO1|CwaMq zPe43$fYejCYMsV|>Qn)TVKd+BANQKIl2)+0pJ9@0#aE_l&X9yL3Go z{6>4%+=tNy!Nq9Dm^XIiG5tTjCVcr0F11gIzdw){;;)_ce7y0^&U)>2@c?e&T0V?h zc>Z;4vSYN{KQJD~Gg@vvzn}i5Z^c8@F~0Z!f8+Ifc`d#cy@(Fz#ndjOF4gbIJkNElG5Ph1$8rd|+ z<+Bj}JrBw4m#`k>l@M4o!UQ>TiT?4}>9>6-4F|3*#{AN|lh z`ZV4%o}qg{esO>Jv&P@X_ru>c{-*DZ_UPWZQ3NMV=zrkih(Cb#_#VHtJH&^4n4Xm1 z7@ztRp|(c0B)`J`3H1VmKIgwvKecSrAD~S{|Nh-i!X@yKh$)qIFrXO+TP%!!Y&$Y+ z+EFDfK{bM4Ox|V@j!Kng_bSr0huX^_9Vl9awnl|RAy{Yy4u#~L2hA6p^PF&PGdtyt z=1@4ih(<)$-6m(Rh+#x{dHsKWOj#nT5at4yB=!JaFey-y9|13X0A5IPpu>w2jV5L} zv(D}cC?Mu_!heJu~ZS4626#HGRj zEypvO3o{pEi5Q1=pY;7SV-ByS5eQ#G10S=%yQ6M;PBO(uKnV#iTMDZY=w!;h1h)b> z-(_dRN7$F`%ScPn{ZmKtlZ1aN9Im_~e!ymRBgkslz^1Hi;hDD9EvoUD1uq={7(6H3PA zc{9@C!;}zXbt$~1t{S#~(qcjsWgNk*_>+Rd)gIDL&zU~0u^}JIc~rUK?hmBkhP4{kw9s@d9c}}_5R{CjJAUz`J&;0)S@57Q@8|yUsD7Q8mxR>pKPYZb zLsiaIY3;@1eVW5jj*@(qa)(kWKOKYLAMcxz*3SE<_{JBv|0&MtO-jm3j6(~`ZTi#8 z>D4LA@pami?}QqT^QVTN^>?6?@x>}NRh*^w0mDNGX5=$Efwcu&4T~^&jAAofK}!v( z8reO>PB6Rm$}&8~g}D>F6U-E3jAeKtU6vypt4$Q8JA+WTX;jmkvWEEJtiR1qK8VS(IY$ac_ZxO_-84U(O2R!hVvfVVk3f1e6*bQ}P|{LZ*is^Xk8%DJjOXbsOy(H- zh~LkD1FNg&JZHhb72p2Ri1_x36^T{qMv0E8nBK?T#qQKY_aK=!f3*pwzR`pLqXwDD`Y= zg9<>CK4lVqW@Wa=B^{X>1wv2=&OA;~=l#m&}bZJ5c4|ehR~^;?Hu}EilJ8);;F$0G4Am5x;~7K2-O@65Az=ESs1qw z1aAbUQy{((056(D(7hPLLF7Yt=A5-{5!UTI$j84%YD2t&afIKwpm9(`h2RL$*r^jI zM0=Gds6F$%knbIkQJ^DSO?DpGP4R(PVPQB0nM80q&OTw-!IV@1pTP%}g_YVFnwQ8f zFuYB8)S{0Ihm}RPNeK$Xyu;(PF?ce`_$TTN`pA$)QFsPo3G^%k7Ci(X3m}IVg1&j) zHuCg_ML_o>esXpi-d4Lc1v-5DQESkCAk8#0}yk7gGlx zNP{GH(8%0?{8;3aVjv5Z#1UB=s+>Lqx+2JR@XcTSivRsrzcL0*#czmztyhSDRT%u; zVIB;n>qEi{a^flE$f1ZmkEaH|An;t$X9aWtTdWeDaAifMvR-=;<2Cr<+t8i^N)oo+ zNzU4S?xnq~tjD|JnF{O`>eHYtn9Rb#FDNT2pqZ(q^)ZdhO8z!9VKSl`22`?clM6fi z0x(eGvN2GV^**7?`T0Wfs-ds z>V4DmGza0`giZ|$8#?8)lce1ib8_j^(q25NA))6AErI$IXS19i^#`_8)GS1v38z&~ zvl1N=P{lyerGV>e$_ngV6I`vz3T&){;(N6DnzB&;UeM!GMYi?YK|DcoG~SPxqt%$> zjoMafZ2tWD)E5Ohjm3Bg#br10t&sTfhp7!>6oYHz=Yeh=vMECdto^z&zMrXUN> zL5wCmt})^W23lBEh_PZ`Vz@;|33k+u9Ipv>7&Fcx3|sr~=urc7^sF)VbK!>SwFi7O z5kUN_CwT3fqMz`5hQZt}W>s5sR0lcuyFVhI;(EYbWzmQWnigzw*pZxBl2Qz<#gX7Zc?&qoMq30yKpUzv2HTLb0f5}V6%(mvkj)}l*@LXD@kg}RkMMYY ze_!93my}og25(c=_YERv9*u!NgE5fb*Ti6r9htCTt05r*bcj-h!DpG=4b^4v)>3C1 z9&kj4q(LZ({K6>6Uhfduk8b2|i0-t_YIWb4HL@#O1AV^czAWTX3W15zUWB7j(XHhy zeYr$h5yZk{iU-lrjeRt`&FUS-%p~srk-eqE_5yS4>oBB1SHNy?kFtlLcA~G3+Qpht zyO!kwUJ0x@7zC7HD8m5|ZAA(PESR}4l0zgRNen@WH-5kbxE&0@jPY#h=mp$9jQ*P+`(S%|qn;L7p8Dbm8QsAfOi{ z=`1ir+#VdK{G!WbzO_#qcckyFJ~j8ua` zDX2!rivW5-UJCrKav$vDTeT1Sj=ZH_ede9M6JlNxC&YY+w;V$rTuv?G2SCzZ@6$G0 zKRA_0KUf=!+)p}eNXqOE&KEAq`x0W}Rm#hNwTiRGVwI4p2N9@nlS6qP@P(*Uh^U;| zNl9|0k~~SIL~zAAQGFfTEON~fv1Y0O9eJOja%4fqB@6Z!q1M&JU;L5D>o3K=~oHUd$wab!;HgGUr>9-s!u97vqLREN069c^NK;IyvOtNC{>{t0K?#!6W<)y-8X{YLU)!LC?%> zj^-YZCnMSnjg!A2hKa>6MyZceS&ueljxo^1i0KLaHR;7&vMhzdP zfVzl@(&>YRccQ`gzDm&~u7CoQ94VtGhwn)(70TC)`8XLjw1iv3s6c7uw0i2a% z!z(m49G`@`p^9R3GSW07#zx~34OZgx5KRINTj)g8=PYRah3CPQAk^EYd5hM8iQ(o$ zIR3_ktTE14x7T@!&IIj4fbI(?P7JQl$I6%TH$rec?;6udjdnncsg*N#>3icQ0Y{mi z*H~k$g29WAzCU1Z10a3W-diVViNA0{x$ex@X=JoMr^FhE$2NvF#@OhXz#^}r3F>$f z8XL|U9sfWmnL<9HV(%&flOvWG@({(lI8RlccuRszy=ZW|fZD&i#&X)CahkZ^Jfay;oMXZUxMgDoz;N`lagvF$V~@+YqkYEoh8Pii+6;xA}62D&56Y)PVPpD z06HJH9t7?-ef^e#hl{3Y8o52u!l;?lhHnI51N|p3B3{P|Gz+fHSU*#{f-@C*Sm-c_ zFC{%J_(EAdAOiZpR{Bq_Ce9sN(L2yC#;dRn9w3gysII2O>82?eON81R zKSx)_rZzS<;L*ohwQp$`@s--$+9tZD=1=e-r!((-`(>+&7{DAW<)DzzAn~O&?qq; z)CmfWM|k_I+wnwr9NR`cnCLl5nRAVr+<@b(B&7@`=U^@9*^pvyRDFOKT-2%g%` zQBazAYJ>+t+k(}AkR8cei^x6_O`=fr(cm*dgs2O_VVmGkNt~=9OT7`n7eER?4=_Vu zV?m5R8}kmHfM}}Z6y^C17#r>CI3xHCfp@@N3kWru1$|u&xQco|qF$w$@dl*^8cJGC zys;qGShyCV*I~HCuQDAB&;XG9MMatQ8z zBut2>Ax?=-8N{4Z$)dCIH00|egod{uB1uuJGR#j#z=VhvTBGD@XjUlFphsH<-VAyz zfXB=$kq?m|5(1fdd?g7B+fR&y2igQYs6gn*mW9F$4M)PmXxX$;geW4|;4r-780fkQ z4~04|65d4N#fBM%h6XF_0~!h`;H|d|Ie=ExETh~He213bB!^PfJqC75mgYdq-h5%n zr&2veRJHTej^v61E zRx0vC@x@3oh-^Bd923T^v;hiz1P&eq5U6E>2bv~a8CtHaM?v5O(r?5=j({GQqqLt< z;tduG>BUdww~|DWFFO_aszQlDH|K7jip(e|^wssj2VE%l6^GBmMf@H9PLZ~Nf2Byv z)pCpYS2P5WhWipZ?IFJaLNJ5j=rGK%I4+XN3~NRE+^u8|zS^afA69=?bS4#fg+pUK z@B0`b2A8aqcMZOJxJbMYc(GQ%F-h>|A5VVRdRVJIjOD>UT2gjuXfbLh>;{1`Q}__~ zakjVx7gmPYj8kZbKhD3e)j@uEUW2t-;wJdt^T`jVpr;xxKU|RRN6=~7dAeRe=)A-l z0j;Tjaju*894T$dIy*x5V@QEpK`>v1E?v&yLUlB{#7DEz&mh8i0f#Zl4_Fp4e%ga$ zeh>U==shW~NcmwTWd|o9#Z}YEFJ)jiX+`3R^94H(CZ6PnAmj&H!~cZ*An;MgNeggt z3UKm2DL)Vz)4#LH4`Wu1EddEQffCi#K*NS!Y|Kff+m0w+nbw3kSIsOw{CKM7v6aLh7@ zWV(`tIuiRt=psXV3~fo?KXlH4#-o~rpc%xggm{0+y@_V15t52Xu9V}EZQ#cFjy$ixmWXkzQhpH1WFs=HBmc>dl!DL=wKz?R_IjG$12XbX!4R1a-|iD z4ca95Rx+Stv?tLZrKU*oep1$9BQ?Bq-5zYK*awK2gx)3pfX* z9?dugBlhZr*55SrL6VY(?2aa_n1r9y^<9YmGJ% zcmk1c6UpG=bIL_tXvD-Ql(-Z^+Gng#WZ;Pf^a;^53U!^O@M^A>LP}3i{?K1YM8r~-3bYr7(d~er2D-I z-cRp?z$W?_MTtD>igP+0FeO?R@(+VMAJ|Ks9n}4w!Z7?eXM}MC4_6hM_)T4QA^!E_ z>C}muf3iAtIXW}`e4M$Th2=9*Pf71+KmB|105$o^$^+7)A-wi3M)osMyL}Fh-A7qu z$wLphjVUn{wh=yP7ZF!NJ9OkG65HR9kg#2KMp%#nxp4)1MqUDf-t>`C-`K-~=15+X z#t@OWaYR`+ykzFpg#KQqJ%YCvT@v3Tx{8FG?1MIvt`j$m#9=Blx=vgOesWqm9ir)A zD-kK8nQ*OK`Sj3E)d`+f@`RC!T4?iyqBe%YSyWQhQcNFIwKY}9wuzLWRmnE(_4Y-b z6AB8;I~OUR?rQR*(s5H4!HB)A?BfrhZI$IbZ0l!AFx}myT*H5< z?#EREqGxtcpECDkKE73vUKS216up6xYxu2~fm)%LfkH_nA2mX@*mFG99ZW}vhlMeZ zJ2AtV5fg1muqKERMVsd0F;&Sy%B)buoT1JI3%YXhQ3EPJe~951N<-zV*1dbLpdwL5 zX(;-HT0-+G*9={w0&SRwRS5_d5!a4Nx5Qf`4iq7xfashN(Nd`4PEb`%J)1}I#PAU) z*Np5QxN#l&&ldd`ir&fYYLwX(tUi_5g@ZGXQS}}PGmPp$+Ut5*tZ?gSbDFYmDEHhr zft97vb0l)rzPWAV1|2E5b^z}#AKSaY4DCSwIs>5;tp@LYTfA#$m0~ty^Tja+g9a(J zv-YQry#w0&8~O%lR5FL~&=RF!=(+RYs*0EIOIVbu6dXGSAZi&H&kY!ljp?uzDV?Qh z0p<%C;sryg(`kuLmGD4*Wn?$hGOk~Ipuek=`kyt6_Swj`NS|dGLvSrJ8lkPEa9e;a z&F0J0(z@%>)~s0vL|b8cP$8g1a^iVuf@$^7PZt!eTUjMfzV!|Qf9J}*>AH8(o>z;Emh-;3uS+DW^!IuPFc11%$`s!$9-sMhw?{s zT_B*BftyA|5Alt{ozx(bVk z2ke2Nb4CCGhD=7DA!b0R$Yl6v*VR|4oB!|!d<99f^)DnW94tD?Yrq297=2lNf5`S-G^LWi)9UM(T^11iYS#@F4P_5~6?bLi!Nb6=VJ8Md#QngW65=82hJZ)UpJdpb~B76_D;2C6br z($X?KDH&LczM;gSs-b(wj+<7ZLw(COulwNUoALg@Q035cmWpiYwE`=UF&*>=88rcT z1$$C>JJYJdnG`EaY+Vv6868KgsyZE<#ePHg&kmpiiw{S02mky#yi;V)?44qm6+#aLXuhg*p=`+-+46z^__Ws-`mRSu5o- z82#bypvMUlN=ABW3M#`Pu?PB0@a9K@1>gY_ZM7vRk#o4O)R8f{b$qoWD$i${N?lx> zCs>>tpB_^_44&o6)!_Rdz}Z*Gp6htXgAW3eOEywQ)<$mRZhu0J&ehipwrZh=Q!Y;0RRqq2Bh)r`ezi7VZe zot{!$m7M0v%19Yk`Q^5{9KS!O4k4Wi%F*v*dFs^|t%D(_HVRkEstVAJk&*x$9OBc+ z*;Y^e=&%6u;o(AfZwKBJ4+wHcT-=ib>vm}a*L~{xb)B=7|2c4AKr(ZpkD(vyeKZ8? z^*$u^!@AJIsHma<&t5WJqE<3F`Kr=|d_;f1b-lk^u3C4w)^?WuUI!k;quS9Wk0v7z z1!Y(#hQN<;!^utA1DP7QzM0Z;TafPsM8o9Flo2e5e9m&k=nr-W-3bXG2krznWr&fC zLlh8OH6ym&0MNr_j@;U*Wq~B0w%Zu?lz2yKaGcMRTA_nWt_k?)F9DBp*$*DgL2Mh1 zpU|@yytN$(-}gA86ubRu6HbTOx@gJPbv)$73kVU_dAhT`7-I2|jX|)3f#E0}*(V?~ zoH>+l0-du^CW@u!rURWbGa`{W<{ZeZujfcC*4~t0Qp;RvuAJ25ah0xAS9Y4axaunc zw}ys{vstkr2R`AmSDgU)@)5x=AZLK&zs4Y2j#;i$Cm8IB@riyRSE^-@E03aI=^J1m z)JU1q%;SgTNwq9Ln?&bq_$6!8)Nzm}j|0-9=0B#>#*ib`aZ+B>4&a^T=Y9t}RmhL} zJCGmo&bP%oWB0?-kGe>?ky_Npk%2aEK$|v#!#U+YoS0_WkJ=EjALxY|JM@ys|Lb6% zAicPA=en(0myS{UcI?;{qEBip^XUEkn>6s-{p&XxG*BJZ$It=vk7a z9W*c(b^=67{SqOXum)Vgrl(OlB!hFIcT_;a?V)=cmLawp5JEEzVAN z;;LNx(Uu#xtnHkw-k#z~jJMf}%HoqyqS$UPEW3WghPF1McUbS&0h>sAO%*ZDL9c}! zvWK`m9K+|L*QN(Am|jOEHZRC4c6zl-b+p-38Woc`#h0Dw%<0wf6}k#D&XZJiRy_sn zTiS-YjsiFz(ov}S;9ZW7U1y2qAWCB5H^VwhHQ%JORJ3t`?-c^SU1R5Q6BFWy$8}kg zfN?sHYvMC?MX66a-rKu$anr1}f?SlQZ0l?iSl>E@IE*Q+AdOhxPpp3vj~(j&w*Hly zCZD_iQq2D{vG>NYdBKaqdrz53arZXFMDGdjGN{&p@%^G?OgFRHEIky)ZJCpaZBa-? z-Lo^sWfq4whU1(Y!(h~m@s6@XZVh&{`?4lw=eNxaj$20+v+GbD(*(| zd11CzM@27>?mWa>1j zBU5vRYSjP6I<`u7_}|r!MaXZY8!Ju7?U-9W$>~#b8eFl-E#rJyX;p-`gYSbz4(PlF zveTbKFUCjc!~}bRbt(_hNci8wAs$K;FnxwTs|Qy#H3Oi=Tiy1JSQ%E}68{dZt&^@2WvXCXZs%xw4&<{4>aBMPXJv;$q+_ZqJrP|v*f zn!uf*Oy#zrZ>TeXyY&*LNkW>~jAH;n0eEJj(wPx-;wDFuk?=jKT|-tim(Z?}W=%gz zeeY%`x-P1(Qf_N?CF;sG@RuRo+WGiPv#u+|Uqn^pPhp*JgRo_H?38E|YY zI&SXsuutWL+fyF>S`Rrd2& zVEeG!t}xvj*~brtD)~Ju70+E0dhR&xT@4+G-L^k;ZNvpVIPTpZy7w;bJudFu5xRGSIu3B5=k5&M8^CkF5%=yI<=#D^d;8UVz<}O8 z9J=?gat1JX7xx|r-MdyT1q^7+UozdBhI^mGy)VmqphoxdAMwjn%3U=^dkf->)O)+G zy9)2=c_mp5zfs2DTJ++F&*-}LD(xVC@jIz)y?q+vUHmfTM;~L1cZJ5d z2%$9#)fdr5I^`-rRV!SQ!8{c41pOA&?xfzbq+8OHGW4u06dQ!v?x;#uQ{zJ=Txi{q zZwP^Bi+1iD@4q=MYvYk48;f%9PRr!$m8FBH2TS?x@!c8glS*AjjvP7W??@eRRcC28 z;#BG%YT|!UFNJ8A&87!iNM?cO88V_`7^_K#gEEzN_>O_sq*PtDWzg=}EH;^xDchEv zo`z&bD0FY6?4qnwF5yvx@Ff@0{Z>`VI5VG0i885zcz@U1e_e8AtlR3~QBfVA+7s)s zIjk|rNbtVa(=m7yA3x8W?kR7~;4Uo%RkfyjGUnuJf1=H=vdW={lmc}fAm(G0tR>i3 zS)fpjMN9=b{;d6U^a7c<9ZWJ(Vk`iYEId+d45i&lOZ?!GGc%Ht;$x$1kQ~z}Wi2@m z0Gw4-I6z<@#1P&1?WSsg9QTBo(^XX?^7~4Yr)MJ-cypA`sj8V%=Adis7bgOxrAS4a zR6w8pq%IVIJDTQuuWHt~@iQym!xNopKBeThc&2o4%VB#_UQtB_e%WCY`>9^sCjumCHdzjxkPHz$XEg7aT%`J3@88?W7>91PX5 z5$)^&k6~fpeu%d~|AYs`XOKk?S~?MO7p!n*A8wJbbannkuqK>PztyMz1nS8D{I6&=MGiUeXKcZB?D6ti_i-oQ8rAaZWa^~ za5tp-#+FGIa#u-b>D~g0W_CtLXNg+L@Qf!Hw~*P0N-bjl%>C}yc;cf!RC2O1)27ay z*qm3ih;Ln_RWDMc4XL&`Banp#li5FjdE63>>nHIeuQ?lszvQ(7zV)~IwaO;5zw6f) z(=%3fF)!2a+gK65O24+V6`b-DQ2S9VNukW0bREsw6{@vH*D=UINp+p*I@WX@$MclC z^!xFwRyD?x$THOr_4`g1Yl)ZFfK-tsUBBk6z|y2&E3C|-w~sBKYB|W7QIlsgY8Y&V zw7HtCVcS?P_FK95$z>Pg&Pv>A$M0>d2~YLlxs9melZ$Eyn{c%WS6#Tj3C4w$tXI6> zh^h$N@a87mrzeX1uMf_n@~$(!YI9rcF*Xyc$DYOqnd^B z^r4?i(Dp^RUX82u82?txt&CM+j+8yT9kZg}!{=x8Qi^$o+nJARno~bUyb&`qx0H(+ zZDXs@1I>ODW=D`BcoxB4UV%O*;-?xv0al8>`|z`ZjW&zX`yM$)^KAPCqYBR`aeJ z+gl@7IgCeUTyGZR9}w8yi!stJrJYasb&1&Nau?8ZRM$(w{cKMW?brldkZ@ngsB%gV z-X!`!vs*1-LD;nt_b$R;1Yz0c3j9UuxC)2A_&2l?J5QX9@^9m}?M+4%t0#q^z!n%11`#pB; zO4Nf&MN~!_Pe)y-OrFJFuj{eb6kFVEvXp3nW*!Q*%V`ybSdD&oa_94kTXsPSw( zFU2YF5~@g*qwdSg@cXY|ukcEo&(*w!l_Gm_84s`td?KI3C-XY?YtW+#UeBlSscaQ* z!2Dk2LEgxI#G6oms)bMEt!y=)&VI~i@HXC#dQ>y{EZ&K#R9$=yyP02v+G!7gs{IKR zriRbu7xQ_nmd|JJveT$qwGgJo0AGaaRiENZK!;vOT=X*3qFKY2^B%r}_wqjW24Bfn zp|aH)-p|*fzSVlZfp0{WtIf!rKACSpQ1bxa23lFq{=hF`Pw?%iezlYD;+L{1{4(}i zemURGuRt}dJ^V_372gXnBU8` z@Gqj;*8The{w4lp?Cy5{Ao~}JHXlL#t*;_S*w^^ivEzTjwxJr=L)eu&_{01ty9AZF zj`2tNasC*8oXz6j(-!hZ_M;b*7}^f0K-pK*@P z<8Shx^S9W1{x zeVYG~|B3&Z|Al|R|H?n)f8&4WAMtmn<1>$^BC`MqcRxCz}QKCeQ#t7{KF-Ck!L_}1?M42cT zuBZ@|qDoYYvEtKWocN3wFFq?Kh#FBV>cm7*FB-&!Vv@K>OctNRq~{*vUU4zLete0T zCO$8wi%UhLxJ=9t_^PS6Qp^-z5VOQpVz!tg=87iKEar(8(JI=+e6c{Zi-lqlZpB+H zIz^XQBCZzQqDSH7ICY%O>7XiV~C8iJK_$pS==e^5?>Qr#Mi~$;vR9Y_=eajzA3heZ;AWFx5akx z9enluyJClUKs+eECw7YOi(TR&v0MB=JS=`F9ubd<$Hb4s9`R%GxcDEjSNudgA$}_M ziJyu6;z@Bp{9HUGejyHuUy7&2uf#Lr*Wy|68*xZHC!QC-6)%Vv#bNQ1I3j*0j*8!l zW8x3uW${OGT>MGABK|C16@L*Y#9zfp@tSyD{7t+e-V|?%zl&4iAL4ECPjOnjBi?aoGLHDN0&b@r^`!a zqr6PckeACV@HRmv&P-(EY<3P-!}DsSXkc(xf?2=35)v_C7_P33H8mDEC?3I0Tsaz(P%WLEc`9-->eo3y9*UHuM zI=Mz(FW1T&}vg`E9vfen;Lfzbkjh2jqkDdvd4zzT71rlDp*(_(@)z=;{H1(a{z^V0e=VPtzmbRJ zbMkrlTls=~Q682r$s_W2@~Hg1JSP7jUzUHA$K{{oEAr3sRrwcrLjF~rl&{Ix<=^BR z@=f`c{JT6Q{~_PT$uf1yDj(n2w2W^V1;#dfVe`vIy8Nd+E#Hyv%J&RMo{|5;p})_{ z_vOFkS$PiU_I4Zi*gVE^shEw-Hs%`RjS2XKyufQ27A{i5XD=)dZV&h2*2y$09kY-9 z#*_FO$q$W3j7P126~s5tb{Kn&pI9Nx=6uF>D{N(0nO2sSZ4I(=tXwR147P??L#=#k zm^Iuw&nmFaw+gKhR*_X~jkHEtCDv%G)VjbLV|~hsSWzoxm09JMYgJg4Ru!g!W35kH zavztS6kgy zkJW4SSxc>D)^h6_YlZbiYo+xiYn64awc5JQT4P;rt+j5jZnVB^-DKTtt+T#jt+#Hm zZnbW+Hdwb?8?CQeo2)ym&DNdPUDnsEE!NkqyRCbyd#!I+Tdi+e+pKR{_gUYzwp-t^ z?zg^c?XVuO9<;t^?Xj~?p zSS#@wH{tV5UoqCIV~72S~h$ zXd7$sEj+xmW~bO5+iTol`;5(Ys-0%1+kQJ>2knp@wlnNZJIl_t2iZAxuAOHOwujh5 z?R>{h$Yo^LO(+wFz+BD=$0Y;J_7Ckx>__d#>>t^C>>t~Y+y7(lwSQtiVgJEkGt0TkneEJR<~mJIvop_Waax@=XTGz* zX?GSni<}N;vD4{vIZK?Yoo=Vc>2>;?rOq;ExpR%P!ug`J()p6J%DL8A?Of-qajtjP zIyX2sI$w5fa&C6kIbU(sJGVHuI=49+oZFp^&R3mH&K=HX=T7G?=WEUu=j+bh&OOe( z&NrN`&NrQH&bOTVoNqhZo$omJJKuG7I1e}vI^T14I^TD8IS)CzogX+4J3n+DaUOLZ zbAIIPaenMP?);Cl*ZGO_g!5BppYtLV>jImj+%t*9uqQ+|ohrc?; z*Y!^`r4-ss`mR5^X4VE~J=KFH8ulonkTHh5fNulbV~lQzs>O#6^=m7fqf+ z33V>&*EjS?AxG=$SWk6TDHpLs7io!{i+bBTT3S;lCqmOPmQ^@YbbwR(wGoMy)ml>^ zhci_>O-*ttcV&I&eCLv+=qERHtE6Isb%9}{6jJ4lT<;tCmy_oK%Za&jq7~Rb3Nr zP1s)zWeLKgEP=`4)Rd((u{xW$zBaXL70yd&$u}>N?iMN!PAduNwDy+{$P%x49A_QJ zQOB~@xxUu^aI~gtS*+UHlvWm_HL2N%xh!riB_|SdxnwoC&U}hFb$&uh6=WnDEA!3o zkHuP`nvDes9;dxuxsm9^h_`)AN7uZL#_6Ba^3-vBb=4{DEKfT(8tqiRMEgQd`$FC9 zP<*kn%G8C4@U;wOm1&FScel27b~JUiw9j)oNTb@Vy4LCFR~lNEI;#WXI-OeSolQ%+ zdV0INmMmy>I+NJSD`YDQc~`RZ@wOq};;=3!vbra7QP8y#4I&qbx(2%|QTrxxitFRP zm}??uW_>ihYe4PmV!h~=rZ%-JsioGUdGB;_YKv~3JTV@ zqQ3+O=C^h)Mk#9U=ovGwsiW8FP;EstRTn9?lj_LM4N4s$URAP~g>s4P4v}JBrFBJ! zrE1laUHh*nOCZPuLY(=^DANoLE+MJ&DRi%$wA3 zFH)p0^^_ONV_e8+lpQEwLeQ5f-S@jx6XNtKBbM6NA0_PZ9F4|;eTni*jQ+ml z=wmvTdQ}uuApOzD9Vq&kkE$T?um_5qnHr+WE~=zOu!q!szLVMN)M}k)7J-@+b{MBIQwPCSd9@PkB_gN#zkX^@ui7ULAMW zjZ=A4_sHduxPLvED39vOUarSi2v>Jyfu4BhB;Cm zV>x3SPmJY=#pPuF7|Ru7xne9&jOB^Z@C)IwJdt?#9Dj`EiN)oN^TqLadgJl1yb+Ek z!tq2no=7~rxO^O6gypa1a51k@T}-*2KSj!GIlX!w1)TY~Gbraypj;1;BjtLq36tf} z!^}u|gKlWbqk7P#$|RIpgR;T38aa`KR42Po1Ewocq&!lu%9E~Mz@nLYQix9i(!83{uhlf!oxld$yCUU%b?1Q4ia@8}x z9zmc8>F%c7tx^Sr%M%acb$KdFE;a6e@1kn9440ZfO!1_lc=E zZQ710V!K);5YyV_^aWk^rS0<9pr7W!NpP9lcE{t*nX_aFEKemsHWNRI!Lu(Lt)v;n&uQ&OWicJ*7+QLv}Cd;+SK0 z4=JS=O6g;zEP+Di;Q?xQsdRW%xH^%_78gF0jA&;Lf{*|sarVp);Mxa^M2L@K(^g8j zX5qvV;z44`qUGo5cbOjeN6IEv_$c|LJRfNcR*V$1I>_$yMJuW?_~?sHbjzx=LA4&1s3c%2LWWMJTdgr2p{pZwb%d^t(A5#T zI>HK_$O@gv3LRgCj;}(;SE1vp(D7C1_$qXK6*|5O9bbiRdTQ!xyenF}yT&Ns>0Q>P zGNsDX)4rT6-p=;URVh`5+#*jsYp)9`M1(<-6 z)!Ww9*UdKNmH1Q?#MgtB%1&+7aVV;em~13oXW!y(ii0dF4rSpulvT%}Z7L4pQ*jWV zj)SZ^4q~LlDO-A6Xi92&Tr|S`#HA$Bj`6q?tSW>g)_ydQNRD!@W<2g*?I%5Q$gzk< z*%c_In_QJmqvWbkRpDZhDqRF4Rn&h1r~U^f^*baK6T-GATo%QJAjnzGH9Sne+-#Szq+rrr?nVYkusT<3?@w%0pN+dbz zVTqom$0B-~9*aaPyxJ2Mku^uzL_Y?MSR7VGxCvRsWZkz$${KvSIL>S8Y0cKnpV~Rb zE?Nuim(PVQ%u2uXqs(&Xd{L$R>`q!cit z>b{cv=OmMp1qf@0WK!M$(ttE(YA4k-Ck@^-QMV&x*9zb;lQVGeK-z$?bK`D2V4|TB zg%pmvBs!LUQX;;*IF}ZOF$y3v?vzaImnH6)RF?b0in}GE84@Rbz|`46nn?<1P!b_o z5t+$!6gu*qlT75`$ssG*LD`8KAZkJY?dPHnG^&e5O3WzhQ+x>7d{KV2<_pBmwbqioYvKDDv=S-8q>R#GCB&ex zbOMOj?>)C4#Ry}-Y)S|50pbv1PY?$vY}f}K6MPiER%l{`pcSDTg5(rtC;TN>f^ZVK zNl)wsC6SUt$V;LptRzH10#8cufOP2i^$uWiqxcbpO|1)*>nunrn?%)x;6M2pn&>0@ zZ6u%a>lXLZ5?yP;mf*`x@Fhwgg^;DoQ4&=Rp#~>WlTy|{f{Mf%^u|G~K`*S7mFb0+ zvPwN9DdUBgGQEKkE7Kbvv9byts_2cDSef1k!QQJLWR&R*8t`#A`aBi*^uke@-Y5ai zLmjghoSw$FAiO#NN9jz_% zTjzCk;sj6!22hw@EWrX46P(5EojBI%^e93Lr8pWb^Qxta#aMibQz}xMTRS?~4{??2 zJ>ghcRc-pbrp|e-an~%IMd)sCn%_FTwYRUkGucm>-sg^$>3wIIoW>~YC|Z}*($w2D zrM+`eeb2n6C2=X!u_@Eq8^>~5T9>xZYpv5sYQRD?M>1%D!zG<@54i*AjcO75BYiY3 z>lz?#RZY0Hv*nVui(6aT`xdiPkVuWK%X^uSqY2$jojr@&dwQ_M!wy3~<;zxHKTU)qSi1|cF#4P^A=zm6iU6Qr6W$(m8nW6C|XsMiRQBR zgW~35UfJ7@Ls%cmD}R3vMBNUB7O8EpT5!D0s_%>M7u53ly*|wti`3M6)wG&A%2e(- zRaZ*>V|o`phSp5u+_=PWpjkEMI6kLMZhSG%_ac$=yZaC%b8z!qql-;Uudc^pbsPgX z3o&kvV*02EV&oX~8Rl3FC+1M6RW9jpd1fA~*BKm(Xfsk-mx`{neQ8riD;gQi%UwdO ztU9%Ic{^6(I(yrjIy^WmgssqcfZRXDxZkYi{;X1;>4=r-)2lGK9M|Y}JBDML)UWBY zB5>z+oZH)2rEb4t^}6kfar+zNel^DZaIBUST%{9I6VZyRiD=c==(8-bSgkJUF?|po ziq!GNxZjHLhE9w(bYh&*G0ylHj}u~%I-lOvNa&Q;A!6L+p^wn|;~_$fyRH~_g|YgW z7eT1<-O9OA<$78dFX_Do1Ybwjd{t1faN%WZS9e;wTERi*p&A(*dczw7RFL?jPDGz2 zj>WhYDXZq*x|-{AHP`oQ?g_YeE2}20HPo}H5gsMQ`c2jv_qH(}6=C?0*4?UJPSC0F z#}T#U!ilPmato)=Gb4vtCVh?>IlwKVKKBgwI6oJ0E*UW%fvD5%J?)Fzu@0fru8-ct zVmMJjx`>w?PGd}`Ef(Xn#`Jp&;OFeor@(-7N#jMSSVdXd5}e{iN|JJ@vewhnH*Z1j zf+n41(ej8t&Xtttn0^HWk#g~hlzVZc0qVhesur`n)(dgfI#-ik(u?WS^YFv1g+AK? z!E~m@I1^)>saQ5+1yuOe7$4QUucNnpNyjxzuJ&oNnjfZMl+d-TZwWa?t1Eqk(^>EXuhYWvxe9>ru|dC@Un&iHve? zN4Y(S>H|A?Jwdk%QEvF6`h}KQw60obJil>y=;7*f`7xL0eJ;;;U7mO1<3u_heE>4%>a(*kSD!VFxjayC zc`o7dJkE`=y!y;7_;vktBdjNV#x>@0KkM@R)pd3Kae4mi@?gW|`IuX&8$VYcyo5X~ zpFZOXd2}o8^1RsP!v!vHHMqRh;Brsq@_4}2XIjyzu$(-9b@ka@(BtXh6325Xm*@8` zkKbJ$fVn*Hc4M4g9`d=oZh&h{S+88fczopY5Ygqqfy;vkmj@9p4;EY=U%5OUa(Qs! zmUHFdbp)5!4cr*h^%f5F#5j+OTt0~6R>tYNGjVxf=<@oA%k81d<0qFlUEIoeI(2*L z^8Cc*p|7jY0>^OZOsAXMQ&*pfMmgZ}9Mj`9m)B=p9zVMJ;4S=f{_wcV<@E-a=l3qJ z$GE)S;_`Y9PQr1#91kB-b9pZ8@~Fh+^$3?oHm*J+jdI5E>NDBExt#Jki_7aSF3*@; z9*?>_f^vC;*C|}L-iJ0dQWn9Xo@;D<=fhfCROgNrDd&a6Wh-2aXaCB;f2;B0wBLP9P`^309?>$Oj1n zLRTCELRW+Vq2shO0`&LibaG_ngoFp=d9pjs_V!B_)$wX2M70ta(dr2$7m6g0F&&HM zMRp{DjOi?3m*mWe;kef;JOtfAz0$A~YW z7thrUNBiS9f0RFd%LZ1jdaF#klm5t>8h@z;3|b~V^^C;$wTGDQ>g#ciPPxORt_&ts z#QKPST>>W4^;tsTW%AN#b;0(yB4Mfx3qQZ+$f&x-6ZaJdV5%D;=})NdSX~NAC7x>B zg=OMyt7gC!+zMqH*Wzay*WsoP6K~PK3V70ZAMmW{7^dmL4P>U7V-5!_z-?ltIR`8?nYxOK|JJyS;jkD7l1{ImHNz`vS*1ANnb6Y%fm z-vLkK{wNbSN1ao*OXHp>^;I!1V5;x~21O7sEFc4Jg~|aOEbxs|+zOQsI9wD1juxW< zFA!0{GEoNT3Kwv!7z;RF)B)Cu2EdENMSxSpbihUd`Ek$F7XYuqy`H9M67v9CMH?V) zcLH1_76C36ive-N6JWRK2J90n09T6Z0B^t-qfFfA^ku-C#X7)S#4UigiQ5405O)CH zDeeT^BDMl<6W<2BU)&G)xOg1!uz;@AJu~oiSfjl>j)czYB zHuc}UfO{8EJ}mNY!2cSAwch!v5yV{%58&n_go4{3u6G`U&A{C`Ik=-vOl+9aXhg8? z*=gKuh{a9ai;TI87cE`{H!tq=DZ#CAV{u#0WV&^5sj(G4)h(tD?nd_D-X8;D-Oc7! zHt%Eeel{Oua~HUAu`YZFgkY)LSdk_Z_n;`cvTetsZTEwx-0;-QtRHTa3{gR6T8_5+f5W`M)!Kxeml+F%#P(I8^`U%*VN|bNoUJDdsmbneE*De5@ z!kQ7}*tg#*wWGwkmSfxq-aRM!sf8@KmUr&K`pYX~{;p z|BpGHnq8QklUuA<-`klynBkS#~GucMAlbwcN7}DjlTpdcX?Aq)}*=w_>W;bTf z&7KL{TA2FRmfexvoxMDJ6=>_AqcF8-hOrU1J>}v4rzedVwN_>3Rrp}g#pV*+8aUH@ z9Ie$H+-vt2qZ_x+`HdcGL3(lf+jOIk>ikmN-FA(!jOy@ms>9dd7PhU%3aXi_sJ>l` z_=X_9Yfx&wgkLsd-Hw~W?nm6E6!#dkrLW>%#W7}`5iu`BtJ#3|vK@B`E-|k*uB1B* zzkoZaHyKxvOwA-yAIY?oWLictEhm{E5oB6HGJTO`T1hf}iDbHtT8lN*T3k=9#ae1D zZXj82#ErH_7RpNhlLLD`-&SKG<Ws|)>Q|Ie1b)wd`OC~0{(tfFXM9pWl}06jHshfxJP$1b zMj~hf4BX@D!mPw^B*t44Q7SLQot-6AW-F-7PNDYVVrnm%QD%?A?WFOZu@Ymi_l@v0aeqR9B zX|fsjRS~MiZycUUC>0l@RH%IVu&uGya8A@fwZE|3+i-)3^(GDDD+TtA-neF*3)U!suymPjEfT%LQtD zZbs0yU5$HudvNRR!&u#XNR=GiKl=iH53BNnduacF-=lO3>|<)g zZtlT|{WaV~ixIo|gc_xrKgIYw#r!$$ZcQ_Psmhr7G{)zd=C4#qGoMu@&HN4SYCYe4 zPL((Fd38st`CD~ItNEfDpPMgXOg`Q`f^t}A{!W!c^B8Vloo4<)l}7Vr+`oF6`A2p4 zs(D=9y=uOqM&_m}nQi7@)yUj@O_fjcZ)#+2zM)3u=9_9{ZvI`3%*|73WNyBzM&{;O zH8RI7v>21)CR&WiF~`7|TnIHL$Bne;)sdcUxK-7o#^EAWm1mKr#@HfVjj@Gajj=^Q zm2eSOC0t~DC`Z+7O6BO6a4)5rO|783{36Y!Zlu}N%an8fpqzVO&8Bb%w92zgb2=oC-cwZXHr{D)X?KQ$gdHQ$fF&Q$eSgQ<;CJJUgl8RM08rRM08rRM4r)xzm($ z?@-RYM>+Q|HM26`S2HW~-)d$B{bFW?&@rdN&AymZ;YMG~sc@$+=2W=P7deOfe35gw z&lfp|8-0;;hzU7|dwr2}hz~i3+kKI9C^g7A-0h2;!>zu^ImC@Q6>j%c`Bz5yS5Eoo zQvOw_ITh~v#heP#U`~Zwele%QO}{EHC#u<$sHYsgkaBbq<>*C}qmwB|FQy!wYP_4a z{lEG>nD+6%{b~Q{w>#~B^S34KzxjQr;D^esSu8)c&9E@JHI$gSy`L zO8@Ve@09^@#zXpWj4|I^^g9W7y5CQ%-==*g{C$)y9(uw|_MfmPYzfYf(?>F=wkIU` zu-p1n6$k6k_kP@_%mkLOk^B186-mE=VJ63wY+s*RG~lOw-I$vETb+b8r>Y;M^Bq#Z zRDs+(pUI@{y1C*t}jTbyfsYA5{p>6e)lLM%0pf5Vfg312$QPWYaNNBt6RA7z{P zA$MIKOQ|d-;u@$EN;9Xolu|t~Y?bCJ`i<`o|HCoG%g6s&S|!iG(0dv0O`XNI8T~fR zISaX;@Ux=duJW)sbtB;_&-Cwu;m7Gz^ZL0G)co`$P#5CV#U)YzQa#B6& z=gWufFrH&6uK=6t+w2+dDfPr)7AXwen|BIb zIpuEenUrmy-=DHGUXF=l+7#=`TVfdzO zKHpp&ChLXt2-Ck%1r}V0D<6N74%e|J@=b5M;&t%UuuMf5vcxHK;JqtXY zzU7`?&kE0Km^XTZDQ&)~2z`TRy=NofM$cW)i=s8EpEn!N5O0BZq_5l8ozmug(7W4v z6z-e7C-Cg`KIXZXe)oBHcy@Un@m2Wl_B`&{r+%>G=XuKWjOTfnM?5e4#(7@#yiRFp z^PKgZ!gJR1uIDVmgUon*c(ghzk2TGmLa(@hs{j`silk|zOoGIQn z@Vw16LYF#Kv-EErnYwhwDLJ5_Hjs7u zugw=B+Xhgf>%@=pejK^grp!f%C_Vg}1m3CS(x~Edrl?x{AASiJB@c0t&*i?gzW&%? z@9?cn$OYSS;@a$6MRb&IpGy4(3gK?w-PrSaTx6uEUh6u>U}Y z0RKtwJ%Z0OyioB=?Bd{?Yp(;oB(W}ib|<;MESHe$YJ%S-h<%ZB!{l1n-y!&}1O*{r zbsumy;1BS{M-`WX?-6_(-;guU53n#@qj-dh&`CIFW^hRDt6`fd4ewy z=P}aNL4uEHJ&^qv*$*-Wn#4>E`#_kxDL6>!&@3iKnJLP(P3fOAvld-DZz`0 z^BwYANTC&|vSeOvVOP&AC1|p}6O_*rXQ?W6<{08HRk53;s@$3;xklw`*rO7q9S~*P zT&c>r*+}p*qC{0`hbwB9>8P4wR;XHIMpX?09d*i_L-cZnigOg%T~()G2gO)NX&m9J$a`6UFWk^H43 z|HZ^pph7d=A$u{!SU_=&q%hBjpCW|IiqiEc$@VCPHj-rf zH@Pk%$^;FG$0U1{Vr(S)D3atX*~bul6iM=VqLh*zN=dh+L|;U)S5Wva<@-Le4<}ea z@N(iQCVMH_`zSPOSIwDZA4mDPiXgr*iF7?m_RDp=1YHh6m99?_PciY75{wdMq^1*$ z5`Q__OUXVCc2#$2Z`K^5_hzlxYH!wRHbyt$gp1v+ZlJX{t78+^t@GwBUShAo#cWY) z6JX5R0$65k1uVC=1G?6OfECtmz)I^ez$$AmV70X$Z#hJ)gMd-%S-_a}f_m@4Itu7o z#{ny>6M&W08-P{T+kn;98Fk5!ZK!(|ZKn;_#@OrC<;`{)!7#xbg82js36>Cy5Ue0L z4zHqg+ADh6JKOBqo}Nh5o&*@Pr>d6&?M8Jis6A7i+qUQS;F>nOP2KopcbMPC*CGz# zix9^{wfOGCDf15j8ph%q4F##E@Fj*A?6uBhUje?NFcV)&SO{vj+U>+w4mRRT z2K(^!f*pXn@l}F-`0~IZd_&;4bJ98GoJkQWzLaoiN=ja6MoK|SNlGkba?040TEOWk zGgF#V7GeglB5hyF8jScirff;sm$EHoN6PM$J+K{2IfVJdk(A?rr&7*f1n%<`c*34M zz!Fc)GuBg^dJ1FS>7n(WnHcRtE>Abcx@$b^FtXi}e#o=UvjcFqXAj^$&q2>2j7yJu zPGT&2CUuWjczu9jZyrXxCEl2KZ2CfPt#>j;kTbo_-i5%ry(<9Mc-MJ1VywB%y949l zJ>Gp7IUe#JN!y2U<4Nx+@0qk6KH>9W?3Cv#@Rj&tY5RO*eYJp-ebX`CX@+f~uN!cM zZw=r&-$vgS-!|V4-)`R?-@a7gJBT^vN#7CQalli)GnixhQp1=B7o?V8emyp|7Ar5) zQ)gmsw=lIEGq5$O>oCjPlDaK*N9yj>J*oRr52l_jLl+LN{~?O@uW^h0S!(vAb3 zOgoi!CS9ca(!=Qs)AP~`087$i>0{Gt(-;6@8`HO>Z%f~i zzB?WIPT!Y)5b#L)@${4Fr_#@0G2G`5`}6z-{t|!8Kh|ICpX{IRpXqP*FZ6f&SNPZX z*ZDX4xA?dDcldYv_xShu5Bd-JkNA)KPx?>!&jduk7YGOP0tJDRKrApeP#c&Wm>!rJ zXbvn4bO%-h)&$lCHU_o?wgq+sb_ezZ_5}_G4h4<`jt5SLRs~Ll<_6ASao5-X3kSo& zykJ4FBp3^h#jiFvIXFGEFgP>R8*C0P40Z=s1lI)D1vdt_1h)lu1a}Ab1os6G1`h>~ z1dj(#22Taggha>}3WxGS1=tyhg~s}u4Z}a%@TwCQ{`=Jl3;)~dghgP2I$;r5uTEG5 z4o{sjakzhmI%g3qROc*$OVl}w;N#TGe}y28H_VM>|0=;v1n(fYnc$rS?;`j$f?EiF zo#5RB?;(g2253LOL2xUj|z0#JLS>j|JBA*TF8a3)6!y6)M|W^&LXk zwt&mRsTK8g-2hH5WMRKB4<`!pX{WmwJBFi;3$Sk=#g`Oad?)eK#%GMr;zZ3v>>sIb zhhBpH#769d&%|E%9DEUIp3#PHcP_-&3)Q!6`|y>|Yp|DiEp{TV$5)tc!dIAXF*ab& zag(tbCsMZH%XiBcmE1D zmuq_9EaRQbhcSra3%<+v8OAZLB>z)BmXGAf*{=C>b};`N9L`&epJM!NjR(~EAjF$~ zit(k4pJ2R`@ndWr<#6_DJop6T`xxKP_yNYBV*DWEPc!}u4rvg~R9a5WI=~O=b6kj5jd;1e<%=U$e%8EgH|}aI;Uc`wVu!g3X!C_kzZ=k1*d9 zcAv`lG{&bh-pDwY<8;F~2(frON48jc2dYc=j5`IlVXwMddL#g!%Is zAI^9INpBjL8y{wEN-p1V_J{VJGD$N3qkWqMqHHa+5KI{&oKTz<6Q3phQ3zj#-=z8d+|Jpo$M9-)?`5-_O>QTG-P)gX zucl}2VD}l?%wjWx^DQ_{)BQ8G8QjABTo3FLrdP1Jp51e_8QQ1KFpuACt_QZobgtJK zLgPW7HUq3D>$t`(&ew3fzHvPWoYC&-dR!@jyMnqV3d|FqC7p}l(cmez6$PuPmw*-Lo(7D_&jGGH`zGL0wd(>qS_RmD3z&0G!LV#l@a)e4 z)6PBxSbh#IXxJfF(QSd)| z4(}O-4xT##xL;zN5ZZsP5peIh%K#q}mn(Sg4}iPn1%R7OtaFE6HXj1qe)crrQ|2zf zEhgH^&={QR2M=nu8M+lU+{_$_cXQ0pUFTY1AAy99Xvv7Z2FW}Jli7EH9h!8c&y38Lj6gfN1~3^P~BF$TDbv54u= z?I5(G!Gmx|+Jk6uiT??vKL!)I6G96+7_=Z-OyWOlm_urTLssI4gt;BSH-Qh52iL>g z26H#u3t_H-JPTm106rY%7N*|^e=3~&4KwdJ%&ou?U+@a>EBZ9>d0--UGL)Ri;XxKm zq&%YnCU|o5VJbhpc)Ib7hg-xDIKw^?5btuyOPm)$E5!Rp&xa0ymWTI}UR0$eW4Iwg z`!jMt%Ld=)i7yj;j|9s>^AX>C@EtZpurM2MFl9Ugz7vWs{61)z;kOMDJ_XuyM0*!B zEBqE{Z-Dj^(OyR!ZRnE(>WzxG5q@7bMBtUs6`;KUz9Yo+KrHJD`d_N0A_3^+G^0&8zLtd*Z|rZqOApOIecwEsSp{R zM7s`gtc5Hcxl7@)0wJ#=S(YFj`*Q)qZJ=FCv{{D8T^PW5?=Yl*d{-kp53p&0D!{Wu zn~Cry8X{{`0J;y4CEAtXgPsF9S?drl^lS}M(r2V0HA-&*J?Ta6A)Mjr3%|d!lQ@krci-lCE(6Ia`(HcN2$c1)7<3M|x zXs196=Rl&+3CI^9+A+{XrjidO#mXTX%1dBOHnbCZ0<@5#p}i293(~i52L^Omq zXn#615kz>9Pt{0pWas(c1r6bW)(#rNOW%wv9EUkJFGXw9Il0^iZ}nV|I&O|@vyQpTZlq$7A2 z(dK|Q8?>j=$AZ>KwArA+SJt}p640g*?HFi~FKbPD9%#@X$tr=0{XNPK9|v6dmyj;LCqYOZ`y@dcp2jFz;6plSX6QOBFU zH}ER_s(kx5@g0HR?HQ0L@GNMHhz6|>nv}K~G?YKc*NN~DQre9HXb~$4LM|k}y@(?- zZ7FCE6W)EHE zgI80xp`>d)qwn`01NK5%GvM>&cM52S;I}HR5wxd>hThMA0JJe_4WK4ZT{iQa9k@J4&&ni^%`L}>p;YT|B8$c@{+Gfy-{I`OJ_S70qG;pM5``3YX6KLlV z4IHVCYB&6AK^s9daQII9p?g2dz2zgC;#lQ-6>ufV{0ia)hwlZf;tT(B@TC*qDqu@} z2LPcj~gs;J`Xwbi0MsZ9>EqK;f z0oo+uTM53Y;4AVsfHo0)ONnnXXxaW+&?bP^M>NGT-sb?0BfnpSUvPL&`^SP-1-==? zhtR#RdQXB@ZU}lOA8n~2@D|1_v{H{5(l^x~!T3Rh9>jabFUo1kSLk8T$NKX_J3zl* z(JefnZwI}|pB9XOzJ=&%L_Y|6*q0r;3-rB2&mcN>W_-fu1U;ayqVUfVeWf9>i+nop zF6inl^%pHK(K`(pIDtLpS3z%9bVxK8@h&uE`WEk@5cD3JP5fcvpM`Mt2A=Tl1AUqy zts%+>_@{!t)4N;M>rgGldzR>vLBH3#Ed)J=#)Ch|{NoVsO*qB45%dVhdmQ160spGN za_<_@3z&Ww^x>d)db`S$*)+Y)&;=uF1>lTd5?LoxD41f!?H$MCDv%`SE&7&hOKTJutr)h zSx10}4O_e^12QN-C6Uh#sn4Uc5A&LiKBCo)J|C<$$2bNG_5=DeJg*QQ_*_wu110*!jh>~U67&R*s zE-?xpBhC-c3~lVE2+S3H*lYcX@dWl;_nF(x@0jp!{vFh zK%OrPFn^e3xP}1BXC+S-IdrjaN4L6rN|8B0_%5{C;NaHy^dTWQ#GP2hF)!`%%^i^9mj zo3j6d_vLJ4Xy{Go&4 zw(#@eQrrfiM%+b7IsEJ7I%9qtZ(-hUZZyAYZc60+$7ww<#VE$Pq&u9=&YjL(&ew1< z=^M^g=iAP9=R3~*&Uc*$oCk5Q$M>CG&Op&d;3v&Xdjo z=jYB-&M$C^>6Z@X1ZX)%$#2PTf70QG=i<$KpL)Lz=K$`(JF0-6cpNivE<%-+ow~Fn zw^Qc#&0ToMb@%_jy#ENz>yoEReZ2Ik(tjn&|NW{4VBYl^YI{|Sd5BGRRyipWZ$8njddGv-hnph7}?MoBRtz)BOI z;lL4E7DlyUw8-I7*wNyKV|Wx#10IE|`E?825MLNAJn=7qiFQ4_6whir>+o#S{&N)% z)>1GwH?!vA>CkTBdtu&>XE&b5@f^VOEZk?|S%9Y(55mfV7PIccb06H1wyXzX?#F|+ zB#dzb;m2SeK$T^EO3{P$jU_mbUbI0XQ0i2PGaB`RAt_EC}y9MyR z+=mTwcn+Q-Jmpwrf~Inzf!u?5Uchr4&l`Asd$iMd2M*Q z@gSvn>+o#GvklKqJbUmQz;g)CF+3;noW|;zP-zzbl9o!Tss-XCY*uD(=IXpNgK@`D z=9+MvZl)2N8gRdCeHVk09MD5l=<_@?k@Wekbah%$%IHdoZ+( z@YYj(6LyjEKVsX6hesSRWd8fZ(ng@1qA$N$(Fcczt{avHfB9KA4n}Ul-!2Sg(}64gt=hB;;oX@>^0qR+a_`Jok$11ct!tToMjq-A_zP`}P%^qRR^@FW_unXeWvs}W zns*oDh(BX#W}7OF;2*BsGtXv?$=k%Za&OLDnzvEm@=o$UJIkN9fpO(NGqXAGR>HTE z|AU!tP%p6aO zF@`vQwFVOT@g4Bar|rV)1;R_-I=6?wCV zZi4Nx!6IYQP|$|nJ#@#2btAUGwsYv?Bess%1=sDlyTj9mJ^|YPp@&91IO1{GUKsky zhj;to&ep{)mGkj=)xs9~trTh_?(Y zZ&U8Z@R9s-@NLT5oVP2#3bxIm*xgSn68?$5s$v~5M}bDN8B zKU@Cx{D<@R6=D9EJ1cif?)LlxpdB2RUvzKLPS6@d<8tTbKV!(E9V1Vc2$;Kz_LZQ$ z$=^48c+o*a4xcc5YSAI!Pvk#SbOh$}`NxZn!#t6Hx(M^%{P%}%DmsOBRt$Tq=nPEH zun$!BwI{f@xT+ZERfcs3A5Xh+*s9`&Vw|2DwX?V}XV0j;qn;UsGY!K! zbN1vM$T?KpSln#b#m&WsieDaeXw-4T9@$vjGHmXsS4W*T>|xEJf}H)s4i}>wk33R3 zCg;S+6QkY$|E$u2(mgpxN1cLw_Nddv9VJdlHtd}xdBet+l$4A$?2_>%Q%mnJ-BbEB z=qx$QwY$@4+xJrY0Ia7vJ6@xykBWKC5s+@Jj zsy&eCXH*Ylm8>saU%a|hwP{2{tDZ zrLA;f${~P|dajqWY0{{CsUGE8?R^g0&FpwP>0$WZXKTsGbF+78;e4}Lip!||GC6hy7!S&12$>VGWbW@7`+GGiqgc6HO{0dtJ9%LTGszJ^&b?6|=v4+EJxqCYQx zD_^iy!){93@RoS|`%68e=Z=2gztwxBbZO~Y$aF>a^3g4$myBLH`leAwOILxiX7sJ2 zw~XFCdiUtP-X}^|fby8XxAZQm z{)MG#ykN^$ET5H%TT-xFiuIqriua_HL!~NRws=*X#)3|sEL34(escyREhXnS6;$h2 zSRqpP5EYo`n}xVvO@-&m_3{q6*}5HmMDp!{3O63WiF3nTiqrKrxnJ+xfZGMX?0f~Y zc+>FU9C^shf`0B6HMq?MYgkypq!Awa;o(>}(IdXsXzXWWX4MVfcR1gLG!Ho6!&QRc zcOEiwG2?w2C+g9jsI%mVugPqNeLhy6RIDk^)y~!Mr*1Yhk#}ni$GOqD$q3Q?hMCU8 z&X0^C&X1j+VCUb&ipWab$Fs`3)?95~XRa}?$4UOzabFJZH4@j0uP6@jC-E0?5_7+I z#2N8#X-Z2Dl7nSF)@(+|k%-X)u1S!2GS2Ex!fE|YMl1HN@5HJ6yNxG}O}Hy*kg*px z=d8x1Jr9Z}X-*e3E;DA}9Q{x;-#n&nX2NYdc(YCvi%GauXBBQ76^5Dz{#W5bm#>)< zFnYKTZ|$ko^BZCB#)_k|W910zmFE#e8!WTsaPdCOLGnEDZpqvl)*$E0mtg-h z?5|qsSRZrbOgRTwhg>X|8IF}_4Ytll3Wv&zjTE(0EU%Y0$gBUqvaU7OuA&OhKC^rK zfI^|Pr3r#2u%eEAq4>ukU$GqD3FSkS4pi#!9b8}tsxCqKw>K4k%QokinJJw zh7hAMkw}ao{h^g055Wp03L-%eDfoTgS~GiQ_cl41HEYdl&3et6Ij8Nx)H`HCUgd3S zd^=tmFNR+Mv+;v=R@ggU7%xUIgLZB^KYp&o@gbfa&x!vE z`|>_Lp4Y~9UVOjZJN_l^h^NKf@$~rL`0sWIx!e&a;>P&(_@8#)_P};O*@V|<72>Y= z*?4(;A^tV4i+_r%;+yfk_ON&p_c(5jo5ALXDl;lp6iB$oF}~dHAO9KOi0{O=+JoXt@z3qTcD&sS z_F$j*N;@U4Zx4wZF#cYqv*82$)31&}=f@$T({uuzDP7(lI5PI@@OJPM@zJ<7Zi|n# zx3m-OCis}4c`JCS0W@+tG9XAEAX(bLTFgPzI=`6@4Zp8J>;lVi3T)RWU|+}hn_{l- z)1>dy#kGCJwfW-OLE_p%aqUR-KAU<6UFCWQ(7z%29fxh3rqlJ_vM94;QD)1c%+cBp zg^oTS7E@2dQR2ff(APE`3!NPg$5C&?5^-iJ^!Lngy!dkhe&gyk{5sq~zYNRF=o>pQ=5yBiJ69P%d861bsC-_2{~ZO(Kz4Ck(|uc zisxy?2WiENwBjSR;^Va96YyQ;h;J_6j$a8LjNO3W3Bae@cCZ*T!o8y`y+OD&ETAJQ z2q*jkx444f0}I?oE9j&Of>$gk&#a&o6?9bvT~k356||v(HdPQZVa0ej!xj*7Uj>9L zS^?ctK@U|BPErNs;}!Ho1^u#u@UE+9_s0r)wt}9kpf@Y%tqOX(g5Im34@;=2b{cYD z%%wp*(9Ntlv4T#npk)~kR;pOwYIfq+)1r4$b)c#R|m8A*r1-8P*VdN95 z`90l(aYI?*@xG~o@rqERdw)%vVMd^#nc9O90ha9>ycqQMJdNO)U=Kg92hZ=(FX-V< z5}z%zZNHw5y}&+^7j`$v1tY;RDpD4EzEaQ`Z=pVjtf+g(@KS^_o=&cOP1}w)l`b|Cu&C{e1mOl5d zl{Abl&}uX5C7o#w{6=eI^w@2bbw+d=E8BXljG6F{^Snjn2A+t0T3haCgHuML*{(8c z^0+!>@}7cYi9T*&8J|5GnQ2#B}Z&p3-@KJvsKEFG5FFv>6 z8w&&6AKs78$+7RL{z26v`-l36SdYCJcKpYPX)iPf#AVuF%(kvST(YDW4TXK#jvn=L zLn{l2wp@R8BNY$_)%axzk5Q;~r}XfrC%j}ND2gfjzd7MGzkpxnulwJa@UGS0?BVat zcpIO*EBpUJ5C6j+erLjC2RZ-0*LX&-mHpbkhyO^%Qy)Tu2(=u)Nl}*D_XhECg+D&G z=WSkSPROuXvFmI4fLw7opYYWlo+suV=qXu2%1_CU2YPtCJE`zJxS3hw5ocHZcd+eW zb4-uE4kH z;bEz`f9rbqb&dxL(lu3DWqv6z>riOk49C$o;Y~?}JH&CcaUl#V+;1F5e-&`&UiQg6 z3}it*D};YkxOWWKPN?0xC9YvlY^N9l-=VM>^G+?UU&L*dS?aD2T(e$r2dM!)x! zR)kZc;YPw;Qnlj^GTSje2#vj%vY0hAs~u-@cZEZYPC~QBp}PcK?U4A{Tx*co>*hMg zNwS*tg6z%@-FPh0`AgnyvYtJ8T%vwbj51=qb~nWb^v!4iU(0(-FEj7WT5-IJzF`$| z|JIMkJ>|HR2CCx;hC+j1nF3Vj+uH9tB=ZEMCR33LQ zj$<3#BP%vhdrF^;E@@;O=2TOTs>+_83v?PZ@VWN= z_^fP)EZPnDR-Rb~(-lYw{|6!2pRVSzJ!;ke4$3BVE%sRes1sP`+CxmCM7w7eJ2FVhJ@MeWW zzQ|^+dq$u*0?W=pYGIZ-mqE8TSt9eyUQXvXZT`WF@WE+wo4-XB~CFs=2uI3_BbwNIf3zT~j;V!4~b}sKB8tDA~Hw*D(98 zQ!%U)eZ(;H!OG+%EScK`YBMz)wOR>3`3D+Ev+`TwcIUGwJ!#gI0`uo=LCs9^A^mng zE*#4?XMA$Z$uy2jrpXOC2zo_Ac$<^Y%8}&^k^4qBsTrim!S2M#wZ6aW&M8TU_SwUi z&D6S+=c0F+xko=Yc79X*3izL3+)GM6*y<+hhp^M!xJN(C)7v>D%?I}%tSP+7WqZ)* z8xo%j1&%{2qOG%Cgx0V(*`xmh>0*=)aie zFP#hlx6q(7yF~gg_2}VMjX%b}piH0JP0}+jQ|{bv6TN(O2v1u7^3tS#vPb{BOmANP zDW|9BCjC=A`qwkP`H?rAp5C1FZ}jNjPxS4C@Vkv(9-j2OrH}Fq?&rym78V^|-~7JI z$5yqUYwZ;Uu>IWBzw>jIS)^g@Qn#%Wsuz6*)M;1)5{1M>?3|3q~_zwmvqTqOkIo{sN24t<&KJ#x$ z!n-|PYOwr1+n?d|^!0?#aeDf9!pj||Pba+6Vft~x>zp4b0`qBMlPx?q^Va>gIA@gK zH%dQiO;)|UU^w?kFS^Ft*$%C{UYcwK{O^&0rJ7OcqvM? z6G)169m>%UA_-X&^rAb!9N|d=EPAoIcv(E6e`5=JH7j!#jAKSzh?ki!Ni-vs>7~eJ z{)y9Nq@y|cSjGc!+2XE_ADLI=l<`q}Y9}|!(Jb}O^Ez#MStGuv=0K;BCaGo(X2$GK zcZ%j5scY#}&9j?XBbP{VjN>3uM1Pg7!2hdT*_NFEpQ*Y?|^Hb~$8osY*`yUtdCav{)e(vKFNBUoMzQN^L z{Gv3a2)ML$_8+G@u%espEe769B)mlr`#RowyG_`{P5BzEP<}8RZ8*%F<9-KgwivRLT4ad#|;do=a0Elhc_wGfiqrdeXzFF*7|UNtj7A)kG);A%qZ;kSl}` zLI|gX5JHz+UAkB9bqT2uH*WU|A@zTM&pIc=<=+4A|NH&N>)mJV_3&Na^|1F|d!2JO zF(T5Of2=ecR54&+&M~c~NqFZPkzn+okw+fw4*%`}3GdDkIq<|mM;}u*v->qyNcjA8 zk@2mLJo@0SU7v2$Qp7C4O7+C4<7-ckl?CJ_f7#fH=T3_kRW?09bbde6y3 zNRNgE9FOL>VDi*y)7#xwK9J*GBDt|CXG|QQRr~U05ll$#l&RyV*EW4CyMpxTM8r=Y zKXuaW53lSg;qZkb?#kLT&Yo6re7!;mM`3gLg4(kt)z10j!kEa^ndJYAXx{`QvL9=f zJz!kZlCPy{xQ!Uu)ZnSlRX4vo@$T<>@2L%bwAXSNNsuZ*bt2*JJsT)o`Cae5#|J<9 za$3g!MNep+=v}i?PM52+WH3McMzG?ln>3P;R2x*9dE&4!C(zy-=GB}iT}>&mBK5tk zMK;*GpM3WNF(n!^v@9N%)zWzHUhjjw*365Gx$DEfQmS3>E?MFdVRH<0{r`~|wv`iL z?0=yF=}OK20cB|$+P-m6`5#cM4Ji(j+VlR7hVtgdQ5YubO~mo~L6hyMndzN)ML>3&kj_TC>D z+g~9y_W9KJOX}Vi)E_-B>hj;(rRjC*|6hl-&3YcGFOR{~pl#Rrsn?RsHD6Wdv|ej6 zZP0sfcgXmoeXL}IUU#*;&PBDANz=MAecPF^Ui&nC7?h;ZV};{P-%1aT)u!4R0hxQ6 z-m5srQ)PRl!$+Z2kQq;H+c?m3p{~#BoBFPGXYQRDpEEwH?Q%F4wC=0YXn$y)Tu{48 zQ_!*0e2QKl6z#K&@0u@@uJvnwWX8M;VYR90+QyPJdaUi$W3}a=ALx0j=d)g;>Yl@y z{?Kc$hPC|hkg+#}u#Vv`eb!^`53O7Em+P2}pK4S6%*+oBt3CB0(|_u()~jjScD1K| z)}0U9@7jJHr_5Z?IqsHm=PQw{rGVNAhGiZ7SwUM!>^{O9A7tlGOeRBY)UF|z9N{W+cC%normg^+E6mr8dW6|mq}MU>Z=}W*-W~g zUmDhSt6!P(P3zIH<{b{2rftx$#${}j5*`I*pkd9Yc`ys=hSjbzCym6joB|iY*`RZUd9bH@Q2E0~p4g5+@8_7g_~Va+xtC{jPjliMk>?W6?)n^aozt>= zo`vgxYs{VvLFI1xZTFdkx#p>@J%8f(HK-u`I9iM{#(OSJ^Lyf+Bdj`^I(PZH_h2Ul zHh%b?FlByNNczXH4qpEaJ|iD}@?Gzr(tn`6KYm60?bx9$KhPI@S^++*?cFU1pXAw{ zPu{JBsdsl1`bhil&+s?Uek_5H($L0KUuvEePym`o=YsaL_P36iUVq4wTG#3Mp)>$& z?mZt4AUqdzzU$a&SnGKj@I94Z&*_Y9Ew9I?gVv?>+z6VkXq_6bOa~o%wX+d4Zw76b z#;IzYo~MPNwv=qpbL%@$zwQIgtNw9a`0;Icg+9c#hW9)`nDo?~(0Q?iK3oj129@8x zmJa{r7x`Ln9H-n~Y|ux0IuOQRB{LW5&P5IXa5woHp^u~E;c9p@eXQk)S9VXu##e+H z_qu5QALys=n8QDQNtm(Qvw-~P;pfF1f5LG+^!Z=p(K^z$v}`7vIu8c@bo|5DKAfr> z+nO)cF5-3mX`RZR<49wS_Vk7ppmnY#jehunx%K0Rpyva9k(yuH?*g1lDVQTVr+$sp z7sSi?fR_AaTw1pyQ3fg|9HK@HK(#IO7>3Xi{+}87extK!xWFV+&-SkDu zMtfAxjd&V8zh4F|t7te=-!C-P|J_OdHTn^+s9nmX@*YK)y4X*v-1{0l$gy5KYSPgD z)cB0zxK3RfmqD0!skN;7nX#*V^jnM}ZHUjC>KmOCCjk8{9U-$uW6q}Lm-dTZleB)V zL-T1K?YjfvPS9~;UZ<4vA(f{6sC}IaccsG)&CES)rTRc^-UpnQsq5J-sLoZbPwndX z=sZ`|GD%eXNNsB#Rn4cGfjN+}t#d)s6dhB1PQ`0G6)m3$YkSleZLh|w51BZP&%|kb zT{JCIPV3Y8t@A|3ux`G(Va=zuHIHhhZJh6^xv29o(cmi8~3##Q?8seok!Xyv@;(Y6!L__9ycp61i~()m+qT2AM7y8PaED63@hYu^6_rZ}h!8haZW{-rJT;b-x6 z#zxcr8#+#P^ZaaVZ>B!wSNzv98vnDhb&fM>`!r)qRnL=O9PhiP5uXXGX7Xj?^!V4p zsl02+51I6g%J=^spNbpi)6Z0{g&44=S-eVTQkQQO~u!-UDwyTcIx)a z&+>dPqHC?xeflHxB904zPmuNug?Z3dWN$UvHcfYQET}3Mi2S(2>)sqx%WC>ba1M+o zeGa@%xD|A$gO-o~0xkC|KBVgZWnT43Z5;dye$BtMuX}UJt3GNSb$s;gN%g_Ua*u42 z8|6Hin_>kzSN6&W8|2WTK)}z&j$H)RGREc zeJ-cRp)^Px+a??Mux}wB<<*mg(pV0b-g2=lHJ6xq=6B|HbGJ#FjpkGHck{K`8{`Jv zf!(QPz;l<&caBes+ygPg_Oop$8 zZ-#%hjqCw7Vf)x}d!!v}$Jrb0?e<=~%syqGwSTi8+RyCQ4L*-u5xX0q z^79JvI_H(9c6S%GC~8@>e^Jk(GmGXI-C6WX(Z-^`7kztBtAmCf zG`oXzh&onox@XhldnWAJyZ89LC+z)k?_Nf*F`s&Nkv_6W9yQmeMrV2=OVm>!J z&F&y4=$am#rx~4B7@c=gqa$IoZ*&U7p5Zh`XBMM#U3f!yZ}?ESEc`?Gwl%hi&9w!# zudT30*%R$Ndz-z-K5Cz|&)9eDM!VJSWOQaSI*Zby(}K~-Nsms~c<*>gyn@l0mKvRz z@rQpgIxX{B*BPBTdGqq_V07-uyD#s-^ysW-bUt8oK4o+yzX_w0!{{XPi}Jhg8=XP> zMrU09sreV=UzUGe{_UyJc{cwgMrUn$biPaki7ZB^O`;$*Iz1CtGdj03IzJRhL84F! zZz^0=_)y{U!Z!*xFgl;s8J$J@M(1=!XJOIr8J$mxzD|wKRg8{fbQWz&)*YR&6ZgDj zj8W<@DA{{uCj67`*|KNxo<*F9%{eLm$ywLe^wQMutgtqm9F7mG!=vT+y)STW_yX4L z(W}<_O!*&Q<2pwCZ~cAyjmSq|ifrk*rPG#Mx6I!%i|_?oPW?C=moB0Ny!g9aaPZF@iVUgi6bZ0z;kWAFBSul{@PZ#C_@v`a)=xXv7FkFjg?TMFhj zcrxf092@RZtKm1{uJC)C!}X|*jWOrBFX*3b_Y;JQE!XXz|Jg3Y_t7~0mosIA9c3pI zx)_$&=j{gj7tWOr>?Zq(-DW?x+wH&XZqmT%)j0j|mD{=*{U3jCtgt0t8awSg+xl8qt!=*xw=1zBvS@|9-26Fw z)fL-W(F^uU*CEUc^P{I+UwdhIQ?xpao43NXVLh(BS>b%ykFUElkp20lP%Fuim~@qP zau9dh5i&@I$}sN86Qouq$z<;Hr@MT)Q0`zAaI@SZ%VnlHR{kol%A2xL-j?^|ec2-4 z%HQQn*=hDO4NVi%R6aAsrjI$;bTxxaFPAU_%w%(-8E+<-i_B1SiDb*)xaY5xmh!H& zk&mRUY~^n9u^cFyC0{<31gnnj@|_gPcJ47>OAq;8+RGQx&xBH9jFcKD{f(7!lO<)Q zo{TcBWTeTK!KRTMWm?K`(?AB8{bjVtm1E6;CMK08E@MnWs!YBdZwh3rDU@-hy^J>< zrP_2b2gyX!NzOOra+2vH(@mLNYzE8a<_NjM94WKR2)W7}C3EuC4w0KowcKt_k_Bd>+-hp%HZw^MGl5)SDx@!$j@I&l%rFDx z8gq=?V8+Tyb9wY;^hWe_^lY>$dLnu*dNF!FS{a=ioflmYT^wB!&4|v4rbRQOi=ye# zh0&SO*}*wsNpNm(UNAklAhneX^o))w;xclizGZSxoNSMwgLvJcFMW|R5IY&Kt-f0!NS zpXOiYEAy@ShPB%6zytv+g4V$StlHWJ@gP4)1O-7M-=FHrDz1CbBk0L$p>NP9=pB>> z1A>7;UN9(V6ZGdgd3aD3lm{ijVL?Su6dV%tGJgvWH17tnzy^Z@7YqraU}#V;7#3s& z!-M+45kZ4sM9?reGH4Wx1Mer+~w{f zcZs{yUFl}KTikqile^h1u#em2uGW6!`q;1CRM*AzwBOh-Tr2yfo9tTJ9qv%O#r1M| z&bmtbq3dlwwV$~Xce>r^hPff!S9ZFluA|F#LtUZ$mpi~satFEgE^^Mk@Ah}OF2}WT ztzBThb%Wf=Zm>Jao$6w)tsCx+aLrtEm*uM61UKGIbYtA{?m{=+o$t}7k}Q*AdpA+^3d-X38m+Y{_~+nxL9VYZ69 zb-A5pC)%@ZiLJ4t?OAq!t+XfEezw#Ov%PJton#NSN82-5qfW6yZ4X;!huhQbAnx`3 z?KyU;J%y)=YJ0dn!w$A5b8qfy2ihS#Z5(1Bwu|iVTz_|%>*vZ`sXN>myVr?3#vScO z+aK+2`-9!;j&dX2k*s&0wa;-cf7<@uzQP*#UVERti1qNr_90fo>$uO)w=dh5SRdbE z@3ME=g*?HmwTpS4x!b;K@3#-w2kmP6x?RKb%?);C)H`Ys4UFPZ|EP6T8XXXoM6II3 zqW!tY){9KEUvwzLwl``X4T!3vF;Uy7B04duj1G*3L{p-wD2PspvZ8TOA{rc>79AH! zG&zc*6Qca6XH*ysi!68Y5!|<%MdeYuXm~U>IyO2oIw)!r4T_4QBcfBIW1=uRIqDc4 z#l3!LR6jaC8X0wn@}h20S z-+kmhb6>cv?i2Tc+w4AfU%C(77Wa4e5BIVA)V=A}yFa;g?$7QO_qzL|`+c|~d@_7I zd?I{0T*bA7ziu^ttzieDjXj~R+~S_F2b$*zZ$R@sA!{e@hABeUw5ez30#uChii(|y7J0%yqO7L4vrG6es;eppZ$WiE3oecpd%|ncPM+|7w6iCii?Xkvgo{vC zove5y_z2ZDu!at=LrXkP>p#rn@Kto}22OqK?+I^2OFgWlgw>PBpk<(KIshutXx|J- zqwO9D+L!ntgVU&uA!)SS&@@M*!_w$Z#PBr7p+~?7I05v@K&gR|Y1Fr)U=&;mYD>8i zMyJt!Q@hIV;8;-G4?!hVfv$+hqO{smp^vFp+MJoX**evh4lKH#yJ zp-Vi`3iLsby&TnX0`q6|VNdufy42%}QS}wmw8+VbU8eZ|9Pm^ z2hmgL6CT$W)j0w7QdH9*ya`?DiB_Xed%`%Xa}ms2=(C=1ExO7R)xf z`U*Txh^$VtANpdN0DUP<1N7xI`=e{pG(&%%rWN{1njBR7ONoK{q;!SX(zHYWkmext z^)v^g>(Y!s|CnYF`UdEj4+R}#r2zDLs!RYKGo==^UzAC(A?blxbZ!(Tl- zGYRWTkIvb@rMUxrH_eUcd$191hWFDfM?Xk26aCP`a~GQtX|$e?(r6!TPNQwxl1A-r zO{4yPoJQ;XB#qYjX&NoJEzP&+XKDV9exBw_RF9RNu-#+!L%;BthN$NI2lJ{4x+9I+ z`=`g~{QsB7^g+M!7#%yc3#Kdjwa4h#ed9^(Q>lO866h|E8GwH0F*-)yd-#Qi$Pcg^ zo8!?RJ?0{GkH_eI*z4i>RJUnT@T4j{Z>G>On>x(b88BTqx55{FBvzUYg6!qEIccw&@tEkP4zMJ zjpyzZI-aJPN7oKKkEGDC)p1IVRd=+7NBfLt=M=hj;Q2X)jxEpCDfFJt^K=Rw6T|tb z@MJD}pGe&+=x=j?N7o%Zd#BKOq4$r}J)|G1ZBVrTVjf*P=zS%H_P4elq!i6dqy4Jw z1YJk)H1E;AFYxHPfhT*9_A%Fp)IF*U)wU`1K>Y)~uk-Bh(YAK*=>4Ca0gv_#^EEYR z2cwr*N6__%DNUn&dALW{ChUcH zv|qJ(IUixdJ^djn<=m z4KfSWepJ*K?Q7t7XTqM4N9)(V1DS)KkVfk~(IZ!*+K-C*Rqc`4sP?0xzEAMTJXHHg z(Ryk;ay>dJjryYf0&*RCavJq@vPah`>@|3%p{IIujl=$f=S*~pN9Lnb)2Oefdvxu? z9);&@wAQ2RCicQS8h4gQ*G=q=c{EPz26+TMCykEXxgK5XuutRBvC}lrbq{+tDRj-k zevao#^g@rWSylf=LErMoP3VR+ z)#%$EQ6K-3M(4s`J+c6OCymabzj@?V^xZT%r{43(ZRo}{I=?>g=(C;qG);hh?$LF+ z`FolQbf-t356#zUXtLSuVK-CEj~-p`m^~g{*PFd*2J)Plv~;@W}x*v(K%?AC*s@-nTtv^6K&v$EsDXr=TLoB4g)O%5!a7U%RqECdZj1O zGFPQ3K{>a>IrNM6!(2~rK6)KoPrUXC=dltjM42o4oE@Bi-U|;BJ{^4s9ww}PwGu?#oMp*md51!z5^mR{gC%VoPEJB$#N^l4Ih9|&}RQu_JK->Q| zaNY*@qnxu!a39Zd#$(<`8D}M6oNXFCr!8Zym>*E*G2f$%zh%7jJhP0mWsLQ=-InoH zFeR4pRm>K&iN}0_HudOvY@0y~@@zn}p(Ww>&{l8&;SW&efK3qIiWa~@gug^PcntH` zcJ!EU&|;5X8*C?!UK@0->l_cX-CaP>;T*I(^dQ_8?FoGd=c9e0oG|muR=^O#Tm$S- zPoU|;JOO3wa8J-1J;D=|q9fo)$__w}@&tM4C{Hj5J=znrK}UOn{^&8DpdWfHRAT3F zvj|`9 z-|+;@Ije1eK>K*3Ct&_rozoD|?^fp!1k6SIp(hxLZuJC>(T_dBQRpY0pb7e^C(yCl z<_VgjpLqfux1FA#8LD#*0>-ex=bm7bNNlD@=#SW?9$jC=F7xRAP3&@yu1#XIJVIZ_ zuJD-i(K|hSGfHBMJklJ!+oSshF~&ua7AWJQ=$=7rv4>BkBzB)i_bFmr7Zlw;jy>Sf zeTvu;kI)aX2R*uH5qroZ^h4}nkM3c_v<)DQ&__JF_Yu=JfG~ez+J4ZzmKbwE5#~vZ z`Jm`tOKiDEm?yEvJ-Y7_TjAkTGl@On(LII{P6y3jxt@QBen#7*==(B!| zd8P1)o5Yw`iazVdR(bdoPGZk_^w~c4yhr!AV=s7g4&!Vw+J-T-rd(WfKkulC+Mb1GvcNKkxjB)NNx{n*< z{8daTy2+z^yRnZv`g|GN?2&2c7LPug#=TdNiGJ$Q=a?AhyCRFw z&pi4Z6Z_nwd%dy0d-ORbw%w!qzOgSn`b-o1(xZF8F`YM{&o?n0W6-_hn9du}XQPiqXnLjaf!F{=z1&O#p5c_uF#E3%T*|IOVPDbyf^eA{2E#U%&+iv zlzE_pccJ|~A>$ZVTM#;Q01PC&0Il_e>iaY}pZElNfya?AKEq=fzc}L&znVNP(T8Cv zzHu(aANAP3qANUh1NxlDzKy=%v7E1YfhV9`USm)2ILf#vb}mXCiauN9F-D5L9%X#; z7@Oc(lyOi3>dB*T6?+9*;0b<@cJ|oo&{B^(1)bxuTx;?eC&kjAd5nSLE<@r7YX;c8Liu^*v<$1!*FLyxW}@~y{BMV-fW zK_idriPrPjZ_q4{{Q|A;ajnn>9{VNQ(BmefjXaL|l;6Z-cc4u@j%!7JGmqVZHutz* z=zboThqmxI^;d0yuAB06JgyRDek!^a%V(Y{t~VO}GV3$7w$e_BedX*E+yuqgpq(GteVE_A7L>$8JK8^ElOU9yb)N_Bd_#c#r)TI>F<# z|0a6eB(%ok4np-9Tzm9XkJI^bn#VbGibvPV`8v12?T>0dg3CqKZ*V!N&R1}4&@(-* zHF}oE>HIz0W4}c&@Hp+K3q9^+bcV-ipIzp0C!v>noccQpuAnZR7o7k3R}mhL&W5WA z>wLV<T%kp z+dXa?y3pg!MY&!n?i}<^k2?{)%i~T!7kQkH^W7dd7QM&gjz#bFI31tG9;f4WpT}u` zukbiM=T>@LJ@i?R({Whkae96~=W+X?FTufHG15ZfbBpP}``X-U%vGiY}jmI*!34B#7W1fgXTf)qRM1jZM zffjo7`8CnbqtCdB_8yx=J9_jvIMLH%yP$`9EaRBy<oJx%J z*omn63YPht(DsAX{y5rWYtYdi-8)IBU9e}N+K*thP1?U;E73IeB*N+wSnW$453t&Q z+83aEJP92GusT-Sez3Ku_8r(sDCe_cb)2g`y04SaJ_36ts$&4Ux0KMfft`YC8$kD# z5^4){Pb#7Qfz@$4*`xbR2^|lxr=zEMtd5V4J?I`*LgyJ+ofldM*mKaS9;>S5!K#fj zfHvt~Q9^wMdpN572v*Z|48ZF6>o|c`ztk@1URUB=kL`+{=dlCP=^m@|<$RCsizP1b z*hA2(J@#RAj>j%Sukq*}RpKU()j4vz$7vfE!W~@GbiUo`aoTTp0XAGIdbh_J^d7hu zpZB7RJ?edLG^9E6DQLi_!WX zi@ya8JoZVnp~tR68+j~kEokhq^U)?A`!d?pV_!m>d32Akpt;B1f$rzA^nF1Kk7Y~> zvOSiuDro7kPob?mb}hQU$1)}bIUai>+S+3o!-4}m_Ej_&+R!JAS;2uG%lH(;Ja#qO z)?;5s;~u*P&GYEKWU()!gitcMEz05 zNQv;hkTFuCQj{@LBHB^N7%5Q+$`~mT{ZYsmDbZmlW28j%O(A2XM7>bPNQu-QW28jt zBV(jQj6)$~q(q0JjFA$lUz!Gv`Cq8*fk@LCBPAMuGDb>NjjB%&jX~89i0Ics#z=`O zP;DnfC!*Rmh$>OWNQoGqLdHsohM;;3(G-;NQlctU?Lwq|tbGd6DJbKmMA~Qho@gA( z*eQ`}fhQV_7J8!7&~~2aIJC&aa7bZ$Pc#`l$P?*Ub?`(dpdCGt)>-U{dZL{?k=E1M z6AeQT_Cyx#;&FS>Lp;$4w5uo5`n!1|9jor1s2uI#iPU$k52E3yjwM86QJn`69gFt% zL`R~1Jdw7kuP4&JFY!c!(8D~D_D4TYbOhSp6P=2ddZJ^{!#z=mmU*I+QS}uf?WYP) zbQG#{2qGPmfu3k6s&zwDAJuV!=y-I9CmM-rK8QM?+NTid_-p$i(mqpPA!>|jpF*Vl zr|pNR397cheTAw|;J!sid7?(>(H{3BI@;s@g=(82YKUt8g4>Pi90K=GROdHD6H%SJ z5UCA4Pr!YHYTF>1fa;h*q;=}t0{1m~q9-~7)j18(NocjleUFa!M5m)_3!+J=)(!4E zw8j%PKs62A52)G#w*%ENgGlY^yoX5p;}nnEiJt0-rlO~LBDFom<94B%22m$eeS)YM z)i%@;?u?%4iFCf7<%teK&-O%J&}p7X=j=J2NaxzQo=E5Bd7h{TI^7dxqvv~~T=W7@ zl!IRAiJGD_JW*ftB2T3A_hL`f61~I|#n71^_Xv6^%%nb44vn3Pog)#jnwrLI^W|SMQ?(;h<^%Q1a}jD3w;P4Cj2(4 z^?}=nsvqDsq3R#FZK(PUZab>=fqNfSyWl=TS9;uM=+hqe1^SG~ZAG8;xKGek9`^zI zoX2fOpZB=W(HA`KOLVozeTcs3aa&MrGq}H_FMHfS&@~?SG5ULt`xJcz){_5C^fixL zhyKCi{*1ovahg}tz-inY9`{F7%Y#$f8-Tua*e%qyfz!NNAAY&NqUsa4SJ7`gjyl^- z^@M*y&+vr!(C#XaJr`x{+A((aJbq5m!eg1gMJ+v+`CGKV$1;D5dU`DLx9D_F$XFI( zUkMq%BF-}apLUwDBO$k&yB4ARbpAbr4au0Y3|=^jPZJL_ci8UbG%f0=`FY z@wM3r9yb8p>j``F724xHA?MNF6FebveeaJR`yyXEYnDQShxy7yiUom zI9~I3nB7v!%9Ab1W& zOxkv1mbW*J%Lhyxkc7#hEi>7zcoU2Dx#_5eY%U1?Jz!$eb;u*JJU%Br zhq{(^afS5IQI*vrbH|UWs!UYnRmGE~M^}=TtNoI8rBkwAak74S$K?|EVb0<(QI=r3 zB+AAog9#@k%|skY*6YwIS-&{0-Zr6GRwj@Ie@m;YG)r|w%G(CT%j-9h@&RQX^7c)f zhQ&XhNR7M|(-9ZSX?k^hz?{T*9n@53ORf$`GMy#P|wdMdRNno-Cjgh4(eB zZlNYR{}W}8H|BqJFn}S-P2^Q|U=lYiUKRuck~QNiIwhMG(}Z|D*|dD9wu}HHlx(I) zqd01o8n5OUYL@DhIDIe?$CJ&=tK)O3TAC0i5^ zJF0Tn(LORakN6g;`0V0kvS0ZzmCN?quRLkSmnEBb)KiYBRkp0D{?m+qlBN}75f+ZD zT&CwC-BmV+5vJN^9r6+s%Y>N^nyy7SlQp%9mJGuGLB#%iuzq6*xg@nn(8=XV>9^b% zlR67q7RxdT28^yu?w2Tw4@fp)`ZZ24<;vpKPvztoX)Z0KtgK9XAe%HZepz-_$K*8~ zbMu+rt!QJ*j-8VG7cVoaIrO(`>*8gh>H)>etZHuYGN;<6cv+-+VDYkgsi?G?X26rJlYq>uYB64nxK8E1+-7KkoKvzqkXDHv`@7??NdF7_NjKDeX5;{<0YvR z>EPmcbuyzo*mCOhk#?NXdPxMT8Q&c{PI6$UY5fAxe+jPI4u3;Tb}!F24D z?3x+kWlf?1I^&XubXew0%K?>K6m=B4)#;w!%G13#-XrCC53G4R1OELo!PVziz14VW z#d?6hihhY+%etGE+SZ=*2MzeuGS!?c<9l^V9$MUa|B_D0UjHpC6MG`rdox7RsxaO; zK1eSKbnTEia|R^_aS5vA#>Z8cJ5n!WT4vM3eYkeDO72fK%jL8%m2FugDN8mi?>K2r z=R`bSG6$P|f1WMg+1pD-iLy-Yc(PirDWykMK5pYKp8L3M=MJbU(<^NQF51+O2Z@2z z$%xVZ`RU4;Wjgx1k}a>U;pF3{%Eiu>kIyAsUB&g`mu1G|8rT2C!129u6Vxz}b|R@- zu=gvK;D}z5BQ7EghGW8U2l<63SRAi+D|I5|-}IIAr@ELdC7Hg6d#2~FyN3mZfE!7y`(@7a!**RXq4PCttd?ZHq4Mj34TQ|DR3iLBBS^{=dv(?Qgw~ zl_YxQ=GB=mc~xmY2XO)J(=pR4gE{KkF;B-#`z76)eO)kwE@HdYJMPrkpzqejw)Vm zQZb_lq+*WNJcCFYt$8$XjONk6v6@E%#})G=A?1WB32|>HR8{=A@o{4a`MBdXpV8E@ znlBYPLGz_TCu+V_Xq?tLfNa%Trv}Drof?>+b!uRu<{3<&M)PQ3lIGFCNt#CkC#U?X zAT&AUmxfMB`K6&#Q+{daw3J^Onv(KMLsL_JY3THnUm7}tJ}%iefVHWkWGM+}`rzRN z&(coUY{@c?&gNcAQ1eXlLCtecDvu^;o^vUD*gmW0rH)c1ru(3lINt{~`vurJ%$vN> z2Q|YCAJhyNk+E2@im4N3onV*Kx1&k9@Vuq+S_M;D;ZQ=cI!klr|c{%Q$*wwadz>=NhD=M)4Mz ze!M}VtxH8UDto@g>gKB(udcs3DitRU$=1B=c`4oZpT<2cA@}rU1!ne9JT1(w)Z?0p zW$pENWtR8|si?|bR-{qSWX%*~OJ`3Uoytjq{*^Ww$(w9?@!pIBoNLlyrP+H$(q7Ar zyy9_J6G;V6!vCYcF)~I*^3L0Lc{*;#llSmoD{o2dvUk_s;iUYO_HLcjtwHDilpfYk zmmeg9v;$1vy%(C&z2|b2Iu84pi^Fnre%j9(lsy{R&e?0B4V1uWm;$q4A+RPg?DZJ( z1>_6J7j%c=fQ?`V%!j4yS+JKF&VwcF%e045*uNck zNXrtSTo!fIr;hs6QJ*^MQ%8O3s6Pwvr9QsYUk6)ZH{S`*hGM9Iu_6uQu!FBnW3bT@ zAWvg_YrGWJz(&}~dn_A40pKt1xRj>Nphl#bg{?rj=GbnI?dBDL?dH>9E-Z#sut8+M z*+83GP_9KAm;v))J!})fgkC*z$4HS}j&nKA<+#l{ z{$GW?U@6~Y9S$6)zR$Z`q-{2=g-x)F@7Q$*^5oS3>G`Fw7*_H5>U5wTi5+~tmj!Xa zZUOZbOosWe8n&~qmjhj3FjT`4-`T9_% z4`upn7wK!E4RnX$Py;hWO2}J+t&*i8hY^1mZRocERM!G+>9>oYYd3@TKwAGLunxA0 zly-r^K>JFk!#p6pl=hWUwv@7mQ}%GmmW_nTED*?Bfj<@aQ!x$Z!Vd2KS&##@Fq>5h zc?ObaAU+SQgte?nh#yRz!Chc9Oaa<3c%jG;>UB)4IK=VVJ0kqB&-Ge z8@h{E%QS=SBEzX~IQ0#uzTv~624=u~egZI^*UXdvb&jCUBd76-nNnUYlMP$>89Mn! zk#E#eSi>u3NI!ZmEaoTS@4yaTFOvl;{Oe_otwpK(IO?lx1O+e)76N%H*TGiUEmGAU zMg#s=VZUk(Y=oV>l4c$(fz?1gW2onN>N!3RyEl+4RAaju+tuA+IMl)>enyVn3Dh;A zi^#+Z7%Ngke9bPANzI@=ltLv?X3}htlZZQsxRX}EI@k)kMNVEMGMTi=JNXI4OjrO( zSPPr@*#l)x#m=ddVIx1Mm<^OUZ6&PdXB4DQA$qG{Xwt8k{yLzlp6 zcn5ayI*=@gLoa^nK^xDYjc3rtGiqQ4%!j40hF6W?Z!P}T;_sO=0lQ~ai<~_ch(8HNV^36p{J z^EZm{QHEUD42ZjsxEVPx11NJ*4Q%IU^rT%v`AbN@1e-Ib^J=6<&<46g1*{giY#~3b zFMw72WWEc`hsFG?o_tr#hOHu3&I0mXxdPS!aaYZR1tPPldv<%EowJi7R~JADKc~+I z!gJP&T!SyyECK49OCQXgEplxZQ1)8tyml!+gU7$?-hmw=*AIsp*e)_}ERa5LmB-!Kj4@;avq7!BmR(LyDVe|~pZByv*$Ea3Gm3q@`rd<*fnP}hPPFdrzlV2#MF zBSmg&5BPdJ;oGsXuniQ$Vv#$j`;I9hccOQa@6J-7t~+Z*?rH?szN-W%w@3F4V(ctV z@){(3ypQzzn*qMvUka5#-uq_*`R-o{>v=U&H4y*6ZeESFR^&l^e30-%OLz^^bfEsF z7AUh6n~!7xemsgkngg3)hsa~IMUrt?%qx&e06WWQ>+-QM1-9}kqy>UfIyl^my7!q0E!^D3kY!1lBB&$FvUR?X%0N7*o) zS081;LZIvm)V(?fHi*19jn^Cv=k-R|T0`D7__t;|uQjUXbw<5l1+OteUq#pM<|l7) zSjSK3$g^%WuP4ICH)io#BI4J>pNfI_KcjEWU>QyN+tmG+0$wkKfA2KobwZ5*pWkf{ z`1Bt6-ovkrGXcBr&*c?DLU;@Lw^HuoQeF#W zVJoi$nh%uubOY?9Y(`6m=7yp15od0`12XQd{zO~fIpv6_h;n!Y&}rN=LNhr zC97FE_XYXBAm5kR`Vw1TV(Uw6eOU|hAPMC8a)-!2nn4#B3AHc}lCWN6 z$7rB!|Lg*5c+C*?{)@D)u=mw!k)71{bukQwDKM8;3uVIwUMVyW$h&JktPuIG24;zT z-{`+kOnM1W?)#l0KM?+bazE_iwL@cJDX$&M=5x}Sut}fDfX_B%G$h5C?yyTtz-Oc3 zEHRd_%YlusQ%qC|GsM&z36o(q@R?~n-Y8vfo0u#fp|T2qd|9<(8epR#`5Mjz>^EEs z+j)hM1@bjo#4Ch`Lk(b;=NQv?g_tIr#58RLGH4+`X{4T(UHof+ za>&zqmY7`1w^_w2X{L#Z5su9Uj@uT%T%aBCW`O-T_VX-^2J+b-oxSm+4{3d7!F(}& z8^KZ@o2jcGc~}dZveB@b&97d-aRv3U1~wJg8bEkp4y(_tDv~7|46{b}^$%V3U|*EX)#fY#Si% z*d=0)BkyrDfP9r>VJ2XsYBo?$)oNaoQvqAW9MADs!Y2&pbvV<-j3aL~<*VNjGrm1g zZo)`06LVmbn3`I^2iAaQQZ?Z7Nu-^;P|RfPomwMiN*w0$sv63({xYnuOfB}#q|Djt zdDToMubC+o!&=Fl*GtTF?4M8k1q;Mn*ap@Cd1r9UddFOZ4;Rk_;x67L=8|nFK z9M2{FT5MjsTg-K9#a!P8W{H_MTFebufSupfz&0^At`;+Y3Xu1vxngdnuA3{w++qRW z7fcp&>pU^HQO9j-#N0lOt(ImmA85xN=$+)fb0J{kF6zCD_(eO!+?@@@K=>YP-CGSS z#VpPN^4v%L_s<0E-cOzf+5>qWz}Au)F%K>l^Uy3Y4=)w7w2_!c;$j}{CFZeGG07~L z2T9l@W?3`9*JZ24EUyJ@KTi1-96vD|c8YnDiFYuF>kCC^X5ocFJ?XU{HYBP{__$sZ{foRjyFsO;`BQ$f2kJpSMt9D zf14rZ-Faf(n=fYLDlzZx7V{x~eMs3&#bQ3{0@&Y7-CIgvhnTIK#C%NLCmY0kx>(FM z%6~@Q&+zMW@_mktzZ1Wm@D~-ZUCfvG#r@HIN%$Y6?Wh&=&*?zizbxR>zwqH-YXG}n zb%BLqc2WMBAWR=1i z3F>#3paI(O9SIt3lYskF(1djEQ$e%#uvCKPvnALMZ82H`ZnHu5bRaH!wFE7(-;y$| zDAQ^RkZ=DSAf5YIkkbnmO3<3Jt;u)5A_;P5zy=B06vI5&A;E#9A6NscB#03oTOdK( z;eh?N_}%s$3F6sM36p`i_$CSR$(z_HK|yzzD?uUt6;ig{6bXtrE?Ou-d-AkjDZxR+ zAGA#Z?rT8@%6HK4E(tnPwqq@vIHeUEta5P zr3C#;B`CG9L4w2E!(71EGSbSHN>H8+)8QQnDzICzR)PVP9XL;dLA`+Z!L(h>Z#4o74!#0ya*XCBezWbMFc!kCfmPj!z}+w8^kl zf+^&kQUTO4H5&*|T>$GOIK2(j!Ws$AAngpwoI(5j4{Q_X5hFy-k8?l$*9%f^%|!`p*?0|G6t9IFCH%VQ2bu zpzQetK$v}m;DRiuhQ)x*3&+9|*e=103YZO>B)F&)mP&B31@c@>_!8>4q&v(6Y|X^h zO!Cdd_Dqg1C44FMT}J$6Q(%(>muEvYEQW0o%qj+sXRVUpibjC#D=2$q8<+;~NN`mb zm<3xUm^~aQJA1bTSC59I1artchjMck!gdL+q26mKd(CPI=2{pGGhw9!*K&L<;p>_K z@z*Vt;QBaV>-q&i9rN156j&s|4FV&9@b9vq8nAOCb>2uFH?EanKKbX*gKZMrR04!= z+9AQsgm0b?wBeR6K>k}cNU$Id(_xhaw`M~ntd`)m9H<5C+)g`hUm*c&fnXtZE=)?m zzC&;a;X4T5*&P^wmI56lMQSStie76Y~(EPzE4Jk$m#%N{|nl=_!a<`MEgIs@iP z@R)^>Fbh^nkfh!u$4Tm0hK*&LBv_7}mV zV0-G-4#9KO|6F@00epXs`q^6uo|_A6`C4ln#=<5EUYIArYSLF@YxPzMUMzqqX=X|A zQfCSIERP#4pYw$hNqsq_v~ztv9t{kSm)umj#_*On6CNjFXvSGW=17sOdi<&=l(4yr z_V3uOd0z8E!~d?V`53c!&vmAF&xUONXO8dw*ludj_s2QBJ1dZ`*lI@|^`w~;mmbvE zuvi!_mOHr(CR0(bBOFzl-e!bI{VcN8Yu>zhRKIn{ZY{dCNE9XNwdhvVu3b?S)$98F z`Oh8S<^3*YD|9LI)AKM>e{J+Ig^@UIJ*(6^Fkb2RAV8XA) zFA6H_#P61(=pKy;A}yzO_I3=~@y+X^D3x=cy;(mmw=e$g--uuQ8}ax4EdD2d@A{4S z+v>!h9PZv%@4`&{K7Ug=_oe@qzxw}Q{i(lxvX}aQ;_mCmpW^TMS^j1F?BA4$*ZKqf zOxXAPBbj&||2pGwOTa+yv%g_q{v|)lf8)OR2Q*&i(cT{XzgpgvR??Qu`vz@HBrObc zX!x*X%aN6(ZJEN>MAlEKdQPX*5iAqKaZKOC!V$TpZT95|Bn-ldsk!Gfnh{l{%|vqc z$9k()d4>rT>rZQs1FjraYm?LYPH{pq&H=zu=7y(wQO?O)2-FJV>?p3J&9vRTo| z_4yi$j7zmgtaalW)C-*Jt>4~nv0w9MlxoT;rGE|UA4se6S|(c3pyu6L=Jn9it9c@J zgSqC(JMMUL&(3Sx_q?Q6@b0LsqxQ0R10%h2&$&BWQjarp>;a?6a1a5Ht9=f~R|(0czC%_-M3uU>tQUDqQz zpE%Xr+pkBDem|})?^802OPh@4dkYPMt&%H!OS#VSf3^wSOneKSy;!emKFPAS;qwlUuKTtB#JLZ{EG9x|qPJ{HSGfrUZ_4%Z^O3%%g#YSjb_eSL`_%EP9>&iM$uwa8Y@RTN{F+01e zBHNeaw%bbUTz+kS0^Vzr$W!65$Z7rNa9&ZMC4Y1#g<{JFVJqzL4zzzG;L30m_HQ^i zeKiFO3^|pB70v6?>tUi-jJ}UyUrG8tz;sj7WHU|K*X@&d$}5m6d_<@qrcfa{KaN^m~IV?cuQj;pra( zji?{=KAvYc8Z2h!>1eWR{m4>Elt8Er8^R*MwUkv%mAZ&Y>!8JGShNa*q0@l5JT-;t ziG)x`RKDsv+0Ol1hfWV_vPsf|TA|iUP zxBC}@BdckKj9on)vm5;jeO;UT{p~~H@t&UX&0WDoKZX;V+xQxJgy%|CB3FJ3JK!g* zTMdRnP>%%8?K=w!m`wti-$2}!z?g!QknbjC2~3M*Wxh)&#YL{N{4z&YM!MZvYpPiCptm0iZ_@iljMZBG6>_UiWSf?X*Q{voJ!%v zkWcu}ga39%UWr6rQ7=cI6&-6{5stz=D&^o5p6OIvq?wTcn1&({c9Mj4n3#6!su5>C zc1)(*REwq4f~j>uKg9J;nqK~sVJnle5JYaJA854npQF|9**!CZ_tLLdUQsi~&*9Jb z+OO&D;#1dStUM;BaX-_TX@jvHz*v95W5qgy?JG2)3=yqdQ}RF>d;HL;p-&(9@Q20u z4}W-CZJ(ZA`}#DVtttf+JIl{3Rz^ZY;2Tzx#d;QxvqvhKONPpl2i! zSoyChF*-FB{oK@*)g3K86Cu^Q_B$vo3b+^b?_t=v2AJMXrBXQ%+K<^4%AHw;Oaso9 zy;DiErQ6cd&sv3XgRrF;ZL8pfCi7wjx(R(HJ>9{+ z((1I536hnWk)CF^87)Q~J;;WJ-$Nz6Hk@T*X?21PWwr=^M0cKT&bfq7Hik-sek$K>BWF!Bz9#^Iz-hr?*g z2lpdY=UhaL0SG%yI_?R&m&dY$pZSpZDUVTmCf+66&xqG#JNh6o8`=IDP?u`)HpBaSe*=<$})FJ`32V`2O(-}%eZm#UK#UDV&1GyIJ z{Eat89(@$2I&T~ckBLb1#be=P(Vv2GBC!U~2f_!Q)EKJEN;kk}!=r&pLOVGN(NDmk z5sCAXD3A!h9^!HU#$|kJyUu$eS2hq% zi}{n+pVxG9c|GkL*{<)K$dw7kbqf^OQCVga7LS;q6~v1Px;7a95@}7ntTh!V`FW0f zHxW--^gI#>G;L%+WPaK2559>Z?U2Jey!+C0Z=1R-$Aob{UobjdDJIwabJt^rNcT{n zTV+7|$L@(kEhh9Kwr{gCg_4((S(H@-tR+Kan3I($O<^RnTS6|Pe);IpW%}vv>+9|c zh3rQUiuQv?j~>TVWY5P+<*?Xi%k-dk%wSE<|{U-0zaaekuv;$?kj>F3L6AmlebsSc<)7;B;UGDJv zS-+F*x_pt>6V5E#b@?LONxsN-mM_{IW*cgOn0QD-wt)j~5HQGL0SWa^SOc25V+K~C zU@@?@lAQgcOa5`gA{{tuPd!q5d-1F4f6_UDanEKQwYucdH2+*Qyz zeyw@V=6OD<&9hGDIu-rLxMaJIgVT5uuYXV-ip@*5f#@Kw|Fniv%j=2Gvi*^S_6-T^ z>mv#CtFS#R>8az_bFhbLPn0Ni%3dj%-30qctPTQ35)@z^p;H7$_A0Q1OBDnDZBSO$ zyOwZkS6Qi0yp^SOWp%}cndugjQX)!pyQD2ZxU~jeVnbOhxJ2;E|Rjip}teO)cCsvJ8 z;m&uZ3t*Hv0-ORM-7Uw~kz$DNOC7_n#=7g%=Kn=G|MoQa)Cyo^s}n^b`d}*%kWLdjHN}mv1)PZu ze4nstcr*Z3r&(3P7c6#_Hyo;}5IH;aOBI_MUU|{_NEwI+4TcK+J zI5rq`G*Sa)I9PFM!O3BlXbUgT)k^jeS!noy&mmJy z-uq8)o|)O~X)H9DgzfQ%9(p|b-zGz0qrGCOuO-HB0&f1c2 zcdIzs+TGn6eSd2=$Pa!+`WopYc4!QwEka#@y0}U!Ou9x$_OPkHWIY32m zkF&~S!C+0k3Udlaa3#-$cx2URH|;xhYTv$7UH%h4CKBN1(w~bH%ObRV;>2?FZwB`k zeD(FQUJ(k8js~ND(+kvFWnYjR^v{G%-x}HodzR2L>E|VUTbl*IESeX{mFobitzvDZJbYJurd|h78Iu+@i*ryvn=Nk4I8BK)xoyA%LRd2^I3`^J@ z*dnCI#C%4j@Mq=aunkfpYIKpO*&uCR9~{_M!+b6mF9Nag;O&vAP;jiN{-#Y^+7^0) zi|vsizki_4cXV{?&G07mZra>c-=AKVvpU>0*WA3Lv!$=ErMfe%Jm;o~Jx58W;6C%% z&nJyNwVlV#WAyVFdo{n3p*_j3WHPIOcFqzgrW#^?C9_!r0YL!E`hKPLZdA_3{7P3p zLH8^5Dm_IN&iaa+b^J>2jEr$F(?3ct)0X+Spz3~un&@!~WnDk>)>PKKPwUWH*XuT8 zJz?EoJ;^!H*AwxjV(g1@+O>riRhY0Xq#=1AzAcF5E?LYm1EX9iuc&laWmj5l#b6nP z5x3@Ac)@^e=x#TUAMzdU$oaO#z860=wb;I=f3UM>c(|wGkM9WdtLeert#i+t4(|#r zwNG#E?HQi>(LmSQZ?HtrfdK6n^vj~SL#2f0iA6B~j!C1=x9|;xVn`!LB3xIV6|X$2 z-UV>$bkZ}jhzpto&?0ys6~^uiEE$Cdl;qi5fDpYV-~ zyy@xaE924cvOMK}^4h)!@6>&1=s)x+>VJW9Qz#9%v{_78(1CF&J1a?YLgzS}Ffx2? zhV4TRHuQ~S0-%A(%fP6B>#w9CCjLk-X$&=2W%voN9 z1(H@C>AGa+<|f*&ff(-pkKtTHepJ)MH~(+jW~LwO!|(sqOk6knN=V$mi=e z3AY>X(b|1xZWq$-{kCF#+#nwNHp2>fAJ@5--u+ImCEI5GYY$%42hz{4zP3)Yx8*o= z+aYz_x(y}U$##&}OB)JU0%XaIVY5<5u~jY@;Up1-@y>*))eI+rP>n1(xrL%IW(mh9 zms}_0O|U?VkIi!b2IqLAFL|n+8^*)FW^=T0`7rMv*$1-wK9Mr#b=0?5yKX0@UY{^V zTu)<^&(U#6c|Fa!Y-fE}&Z8>rBS{n8KEm}P%Jz|L*Kx}Ta7&6sD_6G5EnF;J0NAQ* zX~HlfB_d`R6=3T^?^0G$E+d=BDU`Aj^0%egVGosya$O{A_E4Oxj-0qjB%~2^I1=pY z?u~?{6{J}|w1o&Uj~Gg)i&pH?Q9djT=Nn?Cp>Nl z&n;EFAx|L$H*hOh>Kaa_`)Nv*Qb&Hi#K{sK9)F9+AcO4;*YB9=gm$S~jGLDRj8;`O z8N0XaJTidr64huL0p{})_egH;h+8dA_?K7pR`h3Q_gAc4#v0&0F~9lqq_vmYuJaGs zPBfF(>-;12dR~(lGb}O2Xk9dBX9kSuI5`v0I+x2%^;@3oX^( z{MvO#Y)-SuX*;&^wQsK8W^0SqSgn{ep0a{}p|%z+<&~3J^jW zi2j0P1V5i?_jnTRQrmUf$#$Zhyk4iB2Y3|v8Ee<~dFpfYeJ>1DWNfe&My6L~NYP0N7(Y(Pw@zAWLl&34Ec@(VYtMd8k!6{FEF ziBCj7CuSmQ<8);0o6|%WelFAH3rTcIZP#U$Y$rdAyk7Tf7NCE9=wCh|lrHQS3Gidv z7+7gwcvCz{La^|?;HUhWfVn~^WWgxP>?j9pDQJt;dB zeG88+#XkWfP|P+nkbH76WSilka~sH!;Rd)ZzVg~@tG|3b+7J=9iiPN>qIY0aUYZZG zqog0>Y3{r9V-N;B!FX4`;wAhr8*BpxE39H8A>)Xf&;dj;rS^~*1m0B$G{jw2;4SpJ za?|ZfzR1@(H$_S$;8-#!GsSehduV!Wh7jU~8F$f*|ES?tm$nR)X5ARkk>Wjv?M20& z={J(GD^(eUTzXU82*lc}-0?)dzzk|w6|e*_QW+0nF$+s99-@EJ29*-vr&R-E00mt0 zQb?~(?RqUZWM`lk_){Vjy4*& zpH%;xyflQ9J~HPti4XW8GDY;dVyT8HU5|b_-?DD@lnMJ2;X|tE(D@4tO(6YgJ6}e zy#C3obOg&N%eG9N8~I~1gWJqj%h`8y-_DS)BOsLC?m&M>e_KmaBO=%;E6PiXDP)^i z6jW-2v2BR4sMgk`NG~W?6e+p&9RpHx*+`cVk-WvPD_}H%Tng(Ba|JLT}>gJszA2?Xu zP+i_X-BMlMBBmyMjg7wOfu#$}TLb(0;hJDK1tOFP{3ho_^7p_^I<<>5NDywByj-D_6y;XrRb*vw(oBxX z(cCgV4@V1GS~W^(k#W+|N#Su5!LUU{BJihg4bChtAL$H*Is^Uv_TF~H5nQ~@AG~q# zlM6Ra^$cDd>K+b05TqD6@>8X;{JM6md;dF%b;lmT1iCz4cD%}23}6!>*8TM9y^ALh zRy5E-xNye+{&@ljJI3|@80((!1Raxnt62AIktmKr{;L-2zI`2vEheLn6z`r&!8qQSO0S!VUNQQT zZ22MRXEC2(I;zSJ)?@T_?Xa)6uGyg0*EK-i)OD?W57Yat)-{E7L0yTjYrjU8Rv(82 z>3T|hU8hg4EYAeDT7?>kpc+YeM<~>RwQav~;ggFu2K~2PT=O1i@15c$-92=1um@h8 zKCJ6`-lwEV{=t(NQCu~vw2{{kh6Ha&i$&|Woz*hr$Y=wrUjH&4pS(7vl9d$}c&f#qNRg97kdWz7p9YZOc?hZ#$Ak~Hs1YYfn6nuE%{TGNx&(rLPj7s z9!Yb;b^z*h$5nE+<{H|g#T!aSNFoWS^;wS5QyY!bs-Q*I$~ITqasG8CMgw0GT@&GsmC&Ye^O4Z|L&FEU!r}gY{^7aY zhA|nzn_U*3sqlItH9N z?vucp<8&e&U0#1S_8gjf($Qu6yQm%OT*iZJMAk&3a)(4bH!5U|!)XE6i@{_uAi>Y7 zQhW%@ZC)ZkL1A(3u?a0aq~WSt%=pfXSe~nIC1e!#b$zYRTUk+%@5;?~aJ)#Bs3Lol zHn_w`<@(Z>7m5A^kW^RV#EHOQw|(i@v8Cms(Z~9F4`fA5Exrw`I!Ay0yI`=l57=pw z|JdRKi^q;FF8TX6jn##m88UW6O)u#m9`48b(sC@2cZ+c3*m@%!S<>rvjb4nWVh`m4 z;T#NIGe@C6IY)s4l8OrRN?oNJG7*AIISL3$O36{sZEL{m(y%5Sj8YUPdV;H25!>M% z%ZHJp(DR#K+xzPJ)D+ZoEGuo@`| z@S>ueDxcdHiwuOr5&3Wlq5zVf5;l`$-zPuX)UFz=Q{A)6%d_26R(0p&%`Hevkr#xWVRALboe+weqvUv; zm3k5ODW#dta5DH zmFMWQ;qO(i07eJD+0Eh!u1i$pl-ye%s%}YVf*~FI5!=wr}$#|4)AL29jDYIO#jsOTQwXhwf&ZB zwBMZ69v{Q;Yqay&s6W8A<74(|W41tsNE|Em{U1X8FB`ed< z1;2(*xlPj0YgcUsq9XZ2L(t+(rtZmf#HX>B9(05nPdHPk5*P5bV5$eMb=l5PRb^#< zeg+6p>8td4tMWbh6=kJG1x`o0E5jv8kpWUT6G-7s_&3M`HU_X849$nX9@J7icLvi922(Mzg#?K0MlQ8@$*e9qv26+^i-E@d(_rUH3a z?z9_h21tlIuQiq&Et4k4l>;CFSv1*MnQ4jnGZ{eV1&j_iehex77WW`uCi-stnveDO zPD+${^J}SnEtVi z%u2|&zCp4!E=3MJy!!YT4_v$+%=N&F(buCt{ICDQBQVw__$u%Y zyqhvBEM|_oKs$Ch@Ha2`trzVGVa8{f+fW^<(#Ov1UcuNErIQK;00={X2?efGECwwk z$^)ZljMiLn>*$WBM83ED(b-|~^Xav(R``g2@^i`0Bg**w4{7g{KH;Azx#TNzu$p2} zIhO*;+!!wHm_+V@7B`2ha&z<8spyQ`FbJB1O@sIVL(4xHFP^X8Q}n0zd~eUYGty1g z^xM@ETR1#;WOODxvi4cA&(ofj)$WOYlxd=kHP)vyYL{#Wtw4iV`+4;cw?n$9CooSK z!+$^)Avff|MQUz{PCp(`E+vOx*~1#Y$^!_FhCFw-&*3ye#RwoXgh&XJj0_u6h+uQ> zJq=1;ya-x8I=bVf@KzxZ9dsys=)0il?C{5EzIxE7V(3Mc%63BX42V3GT2Y>vp*!}2 zcTZX^CZv(zg+(D+dst5RR&Zb~)JFYFE(L-|ReGErXH_MV?kQXe#xA3EuB+sEOtAM% zsitOcWlNf{G<*Gh+sccJ%E!u!ipy2+XjNlvWo7M_zUYGZBRA0N=)C?n^pw2$RJseSMq zbANb``tU^3zgV7L`7JsdHRNGUpdEV1dGP_m37HpUI6;aC)_Cf@x{n6=PS9!K^PqbZ z_MW_+WQJ^)n7B|Dz<jGNMxwMsh8{wW#SGfwv`TN6(UcIbk3!zXOzdO&oLO;FZ=w#-u8|@LUG3XX7K zPqunvtUYe4Cyyg;t5ZAafATqOt84RDte%CWu2POXkuBmb0XL$p4R>NOVQraA+QyT9 z0fPK7tI$%kYczmr{mbAPD8Rvx<)!&m1yz|DX*M9JsnCwAwM7f|0>-ocYS`40<{2+9 zu1r}I#h&qSS);vuYx2^deMxJhnAbp$wl-v*%r@xI7&B-g+Y@P*c>PIfi)haw+R5wB zs#DA}k) zDr0xZP0J4kcNOMni879X&;ztf@e8!4JeeOu-E8E;iVI=GmYCIhLE zn*Ty0is#6bLxm{wmHUbdNg+r`e&IpLx@2Mg!RN@;CbD&4zHngAm>%$H#BDH~eu z1klBDxkHoEWfsfJd+)P=BAqV+IDwY2{O%da~xTi&T3Ij+Py0_kH|X-s)?>h zLb5Q;UZ0kM5}($XYx2_I)YM>gYat@Z3R)rdENTv#-zE4Q$=9?UGFtkY4n2twAIUJA z;jwmOQLqm+fmbH?6@FALzw268ssJog$5Ry>PlEdtlj`$0lEKYul0KfnPd+4GbJciqs;X#yOzYul zj73VVgaFA;sRo{)m1*`4e^YawUDAfLcmuq{Yu?|n^Q6VBBhCgK^?9WJ& zZdpjVN?3ho6Lm~1Iuuf&tV8`vUU6|%Q(ho7O=J~S#DOkdY0C9*yi%^?a749CgtqUF zoahchPx1#>7n_<+4-v#g@`(Gz{DZJG(lL>czfSutn%&`nJ%Vw7ugL4qi!bx|W+B%M z(7CGLB43BqK$))tNe%n1h}=pxsu@r%e0qo(C7KWd(5B_<;DhvnbD$N{b!h8|y!m~R z_U8JQK>tG>!Id5L;eEzkYHLk>W1Ii|7xv9tXC}OL_0=P`JnLlN=IJe=5^rr~mo+En z1Cu~UFh1@tk2|c5dwJb<*2OS?ggw#ty7XL~)_0z(gK=xQIs>F8C)I#^XSq5FPtg71 zT}o$7OLkYwwQ_admV|maxNozI+!fyOR`kqB%+m>)w~kzsx#ws|L!c$r zkfc4^|I+?a^O5saOI6v@5u%7ShJ+C4h@7Ic1ln=RmsmoIj&6q6Dz%mFN_Rs-ijJ1R zmV0%c4iiPgf8kc_Tk!X{1*bYz2D&x|CpUL>Hu?t#{q2KlUhnR#P5|nYSOE%R@)xrvUb{_T*8uWxUmkP1H+Czqh{kAO2*Si7*D6ZV&4&Ms+9Lo?M17l zR0xj8qH3c}C6Q`vpKxxiWbh80iI$NF^=*xDgZP6}lwq6@JYJJij3X1U`5goS~KU0C3(kO}2&bCT*7k48d)4l0d#WQl@@ zdqzfj=x?rfCG#W*52V<|xZgRoUAI|N+jW~YwO!xyvYl*3`F!1Glxrp6yj$679iL82HA11(Vig|WPc_i_okP9Fg(Mfe()@4Daa4B0%+Y-CQG zk)=pgUBCetLf7n94?)2d-Uv_suxGrk>G<%%h+IqN6v%`6e z6qBm8OM6X_H(ss47*q0;$aAPmn)UOPe)Ywr(`g%wrcC?k#V@Mf==%k?Cn}wZOm!H*NKM8~kxfR6P|b#}v4xdBBS2@X zX#hRAHP7#G`18aa@qevd7Pl7nIUIe((L1ozum-qay!N1nCh(!ucAc-tcH%4YdY!MN zUe9Y1T|@lHFB4LcKm#%-K|NqOKsO_C)hIQlzP``pb7-H-=ji*K+j-y0cHXzKe$rZ(?fP1euuerj@~d-| zSlqo`u2Mv_p+LlaDDkx^B5t0JM9*`s5)2Lg~lnI_Q8&K7IIzFr{zn)9-)wlE{3PO0mhBVf-xiNxhHz_>h)!puGdMlI>@t z4?yXMo1aP(5FW|;O&=n`$AW~vDYUDAL< zexPn^4#ukofl^z!_>U`lLE}Tv9yCAp5dqWafA+!!4ClgyXQzL?7+(DKbo9URSM*<} zF%-~=FsDlBa0LogO(%U5`RT~uF`rFRHYtZk3jn2xR7sj{fhf?Ug(ziRYMh#!!v{Fi z4{uv}-~s5nEp>Ixb4&K+ZK9fW-tRO80!;?PHkfybeQ_(BLz`q@Y!<7C)fxX!c!WQ`l^ZwT}dX(;4PHoIAv0l<${jB zgc+PM!50~?Mn1G5GMGA}T68wAd%`6^6YLYvEv7dca9+WHIt<*!3(g->{xxJr{Y(Td z+kkgUM^-Ju8x$kz55UGm5U+{qYLa3g@g)j_P+&et4PyK;!qnhdOt}LgCK|p5)kCEU z;xd3DerKrZs+;g=JlBenQdi$?pkNZL^J*Wws-3MNf2gtE*HYb5R)RPRs^poWctw?l zvvM|#EMeT*;vy?dmbIxsogZlm%g8W^Vddn-%KCHS+K*eq4O>HPT{kW?7JUEnHLZu{ zZPrt5TN)d;wCyKst7Blm@`$&&+51RUOUqL=qZms?hi|C*KLc`cAYP$w(D|Cwjaja%jcid zbSl|S@=dnud^~bxK;!Qp=l+~fU&P;}PY#GzA)j-VIXTm%3B@k8&>}Xvv@}W5%Bg;q zfUgRLX@FYp!~Dw&F@qt8P^jo?nhj^QAly+aCjfo`#vnCx8Kz+tl|8#!BYn*+!RW8W zYU{42TIRaO#@a%mHt|o>Yd`FogVq8%qJQckrlail&9dKD-T;3s1J8FVEo2f4gP^G9 z7%NAgVwK^9f;zS`VglentiXX5F`;iwv1e}e5$G#TP3@8W_We7=|DIm^ZgWph^Z59V z{XD1goh+|D!8CjbdklSy^~DEk9#%c%629m54cy1LyrTP5zJ8MXUZzkysnLYUTl5}j zRa64{K*}%(2SM^rZVuu}3v$YG%Myo^AR#yaL?2FKq>)&fyADGOG&KbxtNm3C4ORGw z4Y8}Mxu#~ve&MP2R5dnMdFp9h#Ku&lpmt1=jH9qn#1v4hSS?JtQkHJP5;2n3yU#e~ zaU!O|#HE5`@=}}oxODK`jt)Kiu%!0Rxz(4#vuo4QSFa0S_xC!@_wTq1>$3+0qxcGs zvX|m3s1Ca1VL-&e^i2lHFlivFq*Nl@LkqKT>))f3so@s#8X0ZjQ9KlFQ5lQ2(84UV zT(Vg70)fjCMeEs$M1aOBL|ag>MQD3TVNrRwtgx^Q!4}?z8jq(Y9&GWC{~$vyh*I#n z;GjC}wR{D2MO|cnq$GYPnIZ2a@%zx&bdv#M%4*&;H)u*H1Eg_kd=(A|Z}g75&a|}7 zJdqXsRbC)HJ&-4Eh3T;v{Uq-z?hEg$N6@!8_L17I^BdVtd_`Wb>&>axOPfQbijHak zYuo@$5fyr@IGjbE?xclN#d5zfd5Y0yv{}q#?L*n(Gg%xm|9fvl7=QcHk)OUsDc_<( z7^7>^?{WY4LiW+~X>Z9rhq?{m7xKLnPX#+2G$*~!hU@YOXGHpj8ODdowb=}^Oc3oZ ziB5M~$r!;gJPsKmElc`yz+y_2G_=2u0@&O9!4HQ1?ce@wT6Bg_^pA$SPK2XhBdl>4 z@ARlXIKgoKKxugH3A_U{T||Fl{UYrZjiCa>nE~)5Gl-%$kpYi*X#{Ul)S-u2fcy%( zYTc`3c_292obE~>bsW$wJ4^nMKmZ|3W(w*azVAJkMaER;-j#t#^!42IUBOWyb`5r| zQCt}J6>)+-=>-9OKErg3x3d={-VVCc*of}M7$;wkF-yCLlmR-JVCxy+9rXlkHdAeX>1f z_i?VA7i)y#t_;{4THF-}TQQy^{jos65qG7x0uhfxJ%r(8VDt%bS3)M~lLj8g#KfC$ zuuk+%ugJJ>gotwJl{gyxLkhhT3oK_TAD-y>r6hV1MuT?DH)|@GZ+suW9_>uwly+E$ z64gv-r@V77+8I4eXxE?56i#XH#Pv*J*^YI_I2tg-V&&c^3aB(5=tfXtDR!1H9ws*_ zwiK^Bf{M)+m4&C4FkXVI0G?m#qLBNl?>U;9oOhv79?TJ^ zxgJM@a{(wzO=M}*-|QK0Xrk%h+LFQh59qJ^Qumm3%`m8L&)Zfa`D=!77aKoR0LEN zl^<}zf?id$BmsjYD_CX}f$Qm(2GtWSRA8zgjfjgYX}Bg|EU*tsQ@-TWTM{5mq>w<& zmgA_5{9GrgNs32!^vMCLNmG%$H&6{u&(Rm(q_fl#c1mOe$Djk`p`~mvwKfrb;6gSiDe*teFaR z8}b9SRZvj{LU?Dqu!>pPN=0fhJ+OC>GHFI}3FR!@vxrbo6K_Ke3GFsuIk+ocUZtv} zN-wX1EC8J9W>1SHL}02&p^T+~B^HOEsEh1|_KxO=r?jxr&|>Os_6M5`_SQUKeI!sJ z{Ve;ctG@L$Wu~3y(I%P|yk;r4w zKt9iccp&;2eG%4 zx>dx0G>Q=6oC6D0=*2r7=fs~yAIEdK^dz3g_p=@NZ9I=^2@(#kJ=f5QjEj-+$1|1p>%qptbQrS44a91ux*yQu=qIl0XI{eJB=Yj zSvTjekauqp=llU)m+ot;g$jBCVv&>t$2hllFOBWuMKK%}L(~5mj(!2NQgG!s*Qd(F zJfWU0ba!axGX&X?u~#^WTA_sYVmB925r4vF6so4{aO5~_7LIDBx^XDp1f$8F8=wOU za5zkZqY_nLP7a5oH+R4Bj7gMV_-^T$Ve#17vF~2E@LjbxdT|nT1bz2nY;-=D8GASO znd@5PGw99di-88%uIe;skgK$ZTFLl<&I#F%;v{r*6oS@iP1$W0gUM7xM^fU#Ty&C- z%W!0;S#XFCYO>+H2K=X=ngJ$iFrUA5;b7y$#_JYtd(Z9@_{VW^3+$$^ia!3Kb2N5D z|BQanhc@64dEhgAPM254Zva{bwstm#lOUc0=mb?FwovifGU>%8*teWQg33#a3iI=D z*iMGcVlwhMT^7!Xjh(JT3tjiXL#fBHR#YZ~f`Tksz=!Y*U*9Vt>P$Z!&9!|;=g4qJ z_gJKJ??7lNK=B^EH`@J+!LH48ELV7_-QT~t3y~ljXFGa!A>LzkB)H%QfuLXE7sYsR z9ncLH&`|no3Z7Qwz(P!Rg%iPDDD{qL`(Z?)h za{Mu3=`IK`ZEfNLb&0iQbd_Dmv2U=u$rv#%QR-oZQB$6=bREbeGF!A0C}`dCDlmI; zvg>_VO{XJ&Z&9~yH*MRpN4$XJH13L?JbxSY4;8uToDlRE90s59o6f7k=YIZ7^)nRp zC-{sN4cdbLalg6lZNlCMcZLUz7HHDWr*1eF#K$5pll80KwRT#g3FXF^m3K)R;TEYf+TY1<$5W`K$5AvRLe5lpv(PXOTH$}d%{%FD3(F-7#IDTJtAnYo-D5iccDICTakY9Uz2I#x2GU%Z zpRXuLhbhm;DRPAc=&Z}-aJU@l%)k(;OCj0qFrefa4p2)FI-d%l0sKR)hX?0>#*cU$;l;^ooM^SvV@z0Ze6X`ZzD zk(jr~tM}GGnmqNL=s1HAM=okl4wS=%xwIDCS;ZwBo1HGm-B^ z#xGpJVmYb4Z|&p@{M>P@IiM%lE0jw|d|Orc+^<6DdKv@g0?8h^2e9~Kwj(`TeB#2w z!AM^x&icvPvrV|7Kik+QZXxfhj?Gb@@obIVQm0J_KhRljVt-o=T5s{(pgMbM?Bq6d z_PBb1fAC0pCg?E3{e@gZ31`)^jSuOD)S)b-1WLru7PeVR5@|s+$yE)}&aJ11PjBsG zis&ynewp^+m3Q+#Oe6kb$J#7Ff3;N!iqp>ALor66cu^>yzT*Xz^3~2zX3BiH{K(+p z|2(-@J{fsA^2yt7!vIfX>w0g~^dDJ&HJ553q9>?-2z{SbAE@9cMW|^Yan;AnS_q!1 zdo}n#Sk`$mY&v@R_el)H}H$bP4)Q`^*+qER4CXki0OS= zb^eT)-bWhC)#HE9g_jOU0vy;&1lWs;*U1#%cVBsfYty#r*(@LQQ>CSJ~f@C@cm7?YYun~=9lUk5ods90An=D<81*aZ@{evH@ za}4%P)9;O)gN^k+#kd=ty-BsoG#3JB3foAW#h#%vzUbr*^Afs;J|g0obhdKK6waRZ zIGhz55RO?F&`t*6NKotrsgw~-W!TwNWT2~ikowa#5IMdqPvLx)`tjcBn`C$Bpivyv zO|pQ`(_@)E#B`PC>pc#=2cKEr1;UN%v@_{I?-`~yeyJ|(t2~NnKuHct7r+oOD7hT8 z#f`vWozVl_u5gmiS~msgaFhTklbojMsjyfaH{K{hr#DRO7@3XiXzprN@7}WIb;tD5 zbo48t;S}-~{+V72KK)yq3r~5&9_5%se*m5-!fXV%Zm}4kWEl*FTGdf#^NNC5E(+De zuAy&np^2jXSR*bv&*bqJ6plhmqrVpE-X%uxUtyat@9`7l(>Whl-pR#*ywWsFv8zh@GPOEv1;{ zoHRe3D?H9=$+_2Ly|1U*l|Neb4K~#@0LAxn}v82oH324n&SEFCRO$+&S>u0R(>k>&@LbiUN_oOHmwj z93Jj@*SmV?+#WNAJ59KP_&(@PxPs)fY}0f550cuY-AdR0FsYsGyo7VN_&L}KUXwzRH1Zr2-9Sh#3Orbog&=sGLBkDYN+rocoKA=$ zQTTcOKZD)f!86^#|Ln6LSr((z9R1wVQSs^Hp~1mWv~?(S=bb2Zhq1F=z+?PXd@Sg* z9WpHS>R?bJscd(8IwZIv6-INK841O>&6I76m+vNg zg_VBstGn->x$FM5FWrCFw)frri3z=wx42!*$4YurzX+?77{Jpk^Y}B)2=)PGgY}QS ziFVL&4toSA0ieH`B>i9|Q6I6OA<5x7pu9dP9 zcN7N#JLd~jW&D|NIJ%_Q8UHu&0vS_YyibpzrF=uwv!ktb2O#=2%-2wlGzLhV_ zlE8LE)x|_U02^}PU~67Zf?a*tIoUiDXic)M;o;=5$Lwo07Z29$Yw&~UbMOzniPh>|#fL$It6Tw()#O-N0vwjl4oYhBiH$i7_R+|**7cCpr5L;9Xf`J{gbCulBCHX z7=8jL2#YaHhz@B>EL-0`WhJi4{7Rheh2!AOI?I6L2X#GQxif6GZeIJb8xB7C>4(#| z1{YV4t#l0xbm7N-d}Zy-dAnn0R^}f!Yz!P#==;``uAGu#ZruU$K+2~&q z)?J(c{-j{t?-6%L|5^1)_YL+V-%oyF75Ea?;3@SiKldDu`(9W{z`Dr+n}T&0^Jz}& zVBN7Vu8noK&r%l-e34y9+TKIP)U~kg;%j2veprssb>KCC0Db^iH^%mv&;0xJ&!!)o z{&x%mvs45=(#7+~XUC;r-NmU`H~HZ(XIT*SxjNPzR09F95Mm^Zbz3skqS}lC)Nl|t zjEAFt(DvJ>RI$Nz;f3_(xy`RdYhSo<;RSKmUyjT*gT7qTk=Ohyyyy8d#bIkT2susx zG=Pc;or~ftu8DOQzb)2{(-2W%M6Xr^)ytA~%dzeI{9~=x%^tgD=YjUI_Jg8W*nf^c z(bu9sdINv(?%^x1VU0KZJ{Y$MjC3~p+j@H1PM>HBAb70auSV4UvrUl$v@>p6w9JlrY8!ktO;fd%o|=l1{>iaT z+U6MF$?F#TmE#byl**7hlROy`-^Q{84x;&gXo7PcrWn4h!`lJF{C0!&r-$hmVp6_S zH(w(D0YOzY_46fBaBIvbKcDvtvt$`H0U843fo@4TEx@%P^`*|t_zx+|SZEsffL_Mx zO94oL@*1D9)yVY^t^U=Q4t(N}>V5IW=rTYRQHVi!NIPKicClB3lX1l?DV){m# z0dw$ck+F7p_TGOPj($%#gMahjENHFIahifWAADY)%1Ov1QJI6MJh}6c|A<8Xg9yBK z`B%T9In`?G!0c+|@#xP_!tr?l=?e4JIR3o2NDn2lMC)mtz_vpz20Rmj1fRLqHuHfW z@;fAs?*)HNJR>j0JaJ9pJJ_Og@F8&GIUHdJ=8tD;FO@A4@*Fr?t<&%6;H;&d8KF)2)n7&H1(GFInE^~_#AdnE{-NA^xSeadfvOx zhC?SK!QE|byMr@5n>O8rpPoyX)b_>Rv%u8FrAwlH0smZVS1-LG9vbfc5an-vsCyXQ z$M{%3f@Cs_TbO3^8ZS@5(1~bC$Z_J#Ct>KXM2|*(z!- zFTMtb?&k~$j(8^HQ#|~^*}V~e!v`W?iA;R;tG-5&hlDg8Kldi!=OmbaKm6Pq^@_{% z!*&03{G9ZWWc(Zq=J&_XPY$2lItctc_L8XgJG8%mpHnQ?y7)OWkoEEN(mP+UY@fX$ItQPB>Y@EXV>Ni8(|1LSBjE&+`xgz$9G?U zX#V3LZwmz4KCXJdzI$tUVY;cIp$SutJ<)@A74y3Y1xmrr@wz1JoD2*?tz$+8UaVo~ z`io=GxkSzbJ6BhC3IXIiK;kTb4lIPj3&bEcQ=B&X06#o_k#3S2FM;JqxH)=3@iZ~q zJnl5m;w7N{=&z4?4gTc9OUG#7$Bxm!kBM)E6GslM6?pjo-br}*$HpJ?L28tvD@k}c z))JU@0$wh0b%H8Vt|L-vusXrL`oGB(sb1%5Tp1UH zqeO(gpcI1E5hR5DZnzvL`$afd>GM?vO|!u^vG>{8O|_e5#do7W+jVG5ePt~GcBXp} zbSE0Di?M_1?*L;bX_DBp1dKg6bN6U3b?N-w&-GT;)KvDW-e;eEc6PXWcvd*0KOe8J ztVU13-?aK7;BR5{OVIfWKvFlt>Z3fax53w;fxSa~UBaiaLpMlQW)~+PywS4v)TzCT z$8p+5e@FN5=RP;w-O-N^$K}x*`?fWA3S*4!kIQ2bT`|A$+Pzx6;&s|%HGoldA z`L)ac@-L$F^Ud{j%~;RC;PL*;Kp+M~C}B?!Ji;^)tZAi)9wd+gW}!+gaTwmVM3py6 zxqL{a;^CR5dXE%v#7ryZp&%qab@su?Uatyn;<}GGzT%NbWbMb(`!3B4_KFOB%H!+c zXH?vlWU{nl-x`yLCifPYJR2I6Oy~FDoS^y0^2OE9-!&0#zPQ5X#_Rv`!UdGG70NEW z3z^c;G1kZ8liqR_7Ei2^t!2C>cNe_mz~Wz4y+8daS;oKk1@>VN^ijxOIe|quObi2V|OFJ|FG;sLwacCJIte{N&3UR~QQY&GpU(B~u;zwz2wy!)MF@p1mP9u`l2hCN4*Uj9vAcUS1_?YAynOiR z;sZe9H+lMohWeuaXDH8i5Yl9NBTapP3uO z{oH}ijv^LS>%unx_EYh6^dW9HBWFkd%yzTP;gNQ8G7e7~8Rm{5__xQ_-yT>GXqgI{ za)wibNM=E35wL#ML1>FO9!&?=_s_L;4dFPn!A}3&epY$nwleuPNv5U5MB_8zxrir* zuK8_@L)C0+%HK;@ydjUB;v**TO*P>|Vh@?AH59Ulh-(1i`>2%!8B>ct5!tRmi z21&ov_*3>z^7{~J3VOk6Ao}887H|s6HBq6s-~^~u@fB9wceDvOb{||9AEIh&mU*6B7-zC!t{g8MXy%%w@_zY<3n+3AcoHmNq?R4z*u-i$uq~Dv4!kH(3UZtXyg($p6@d`7FM*7?58?Q&c?( z^U!RrAx+D;guT~585Fq7MLEY5A7i(cCdS7Qut7)%hsn%yn9N5z7UxS!8ak>HVrBku zWNv;-pizsLLI0Ed=-}tOwf=2@f*cC<+AWlIhAzTbM{W}Zp2D@y6(-3wU_`njAKm0| zCQ66MNJ;+#Tjnl*jQKxagPm&>zH>tZTuGeLee2}&ET zL4|Xwl2BZj1D1d&)^xCYsu!uTdtH|L*bX$y6St(t8iEx^2kzS|D-b6f5eYZ2c0}X> zq|NFlL=K^zHr4~^O7mWb^@dCez3Lj$=GX_(9Pw>a0J}#LB&r zXj}oyPqM5oG0mjUlj0UDsX9_R5=OBU1-KRBSP96bYBJTyQ%g}n(z(xPVp_<-pD?em z?NMBtBmq;4aECU~OR7wCE@AUrm$_m`hj?%Fg{Ak7o?Px6Eb{aPhBw)d?Oj6fUGYHe z^!VWE^E=Po7T&thH&-*>vlJRZpT^<)%vQ^QhfAOY)o|e7?k*4kY1&)=kZ)!wd=ZVt78;q@u=CSv%EK<7=q(j7lBkz?Bh_YX}g`59MYgl}q^tgg!F@%os@vtN$R;V=syUaISJl*4w7_^5T(Kyi@^`5FoFQCF#Q>)B+Q zD)&NyO$J>q#Jb#9mJf@Mp9l>MgrdQL&~3Mc251kEzCaj92F3ee&+?iC_kpd<=kU}J zUYT$X54z!|#0iQfg0P4F`0(hCr$vdk`~uG5>70(9kmvAdeqdl5WSqx!Le9@Y_zg!f zk(@|S4GGBNt=8)FwTgt(cEjgQBC^S(qn_VBsjmsJQQpv?OPxUPV5<6HJd;tj{v=Q&gP}177G?N4qjy(P}^_J zVQIsOO@X1I!2RzZm^ps$SZQs|a9yUo`0j;WQ^B_Wz;Y zQDQ0xsz>!Q<`+ewlBDlc6>~usP@**W2`9NHRf*>wgO049A}HQ|ow)VRK!cCnKt^W@lc zf)}H??q-_i`)LBJTq{H8^fScYUDcK8rMaa(u%5#HH>$@Eq?5eJ%qi=bEV8~7w2t_3 z>vYVgfEa=v!=?HW&xn zFF-gUV)a4~B3SW@Y14$xS+SO~$QB`ENoP)&HssVR3Nxq7bS+{&OJvce(Ag6(ddZ+} z7@GdH`0lseMGtVXY^_u_)~>|LXiq|m1RW$EQU{sgqpXQEfnbjbAOd4pmD;PRLW6=} z0lb^#HmkMXs`4`2RO)h<-q6-!?8AS=%bzmc$SahX(S8GMEI@ z5w&tizonC-3tTzQ4VjbyOcUn9+etehT457$l8#YN)y`& zpQZLV&MU9SbGSYJ9Es;5{*dw9_;Z-H+|Q55=jY%r=oD;Q@VSR{lF}Vd=42+G*-o=^ zC&F6|$i1f|6_S3<2xy`6XiYYt4^5ObT%UNE(FpcT*FxApuLDk^T~D#HxO``*v>+dL zwsx)(1qi0wjV=>%l7Wk*5p+TaCCa%Z4X<^5^W&9+dvEGp-vUu_gyPzfw=hFMZ`M

{Qq;dYoLXG*oL7!{wrG%~aKb=FoR7{s=k| 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

AzPi!HK1CYdMasJ%868x!dF;`ny|>LRT>eY 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 bytes: + data = QByteArray() + buffer = QBuffer(data) + buffer.open(QIODevice.OpenModeFlag.WriteOnly) + pixmap.save(buffer, "PNG") + buffer.close() + return bytes(data) + + +def _check() -> None: + size = 18 + pixmap = QPixmap(size, size) + pixmap.fill(Qt.GlobalColor.transparent) + painter = QPainter(pixmap) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + pen = QPen(QColor("#201704"), 2.4, Qt.PenStyle.SolidLine, + Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin) + painter.setPen(pen) + path = QPainterPath(QPointF(4.2, 9.3)) + path.lineTo(7.4, 12.5) + path.lineTo(13.8, 5.4) + painter.drawPath(path) + painter.end() + pixmap.save(str(RESOURCES / "check.png"), "PNG") + + +def _radio() -> None: + size = 17 + pixmap = QPixmap(size, size) + pixmap.fill(Qt.GlobalColor.transparent) + painter = QPainter(pixmap) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + painter.setPen(Qt.PenStyle.NoPen) + painter.setBrush(QColor("#201704")) + painter.drawEllipse(QRectF(5.0, 5.0, 7.0, 7.0)) + painter.end() + pixmap.save(str(RESOURCES / "radio.png"), "PNG") + + +def _chevron() -> None: + size = 14 + pixmap = QPixmap(size, size) + pixmap.fill(Qt.GlobalColor.transparent) + painter = QPainter(pixmap) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + painter.setPen(QPen(QColor("#8BA7B2"), 1.7, Qt.PenStyle.SolidLine, + Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin)) + path = QPainterPath(QPointF(3.2, 5.2)) + path.lineTo(7.0, 9.0) + path.lineTo(10.8, 5.2) + painter.drawPath(path) + painter.end() + pixmap.save(str(RESOURCES / "chevron_down.png"), "PNG") + + +def _ico() -> None: + sizes = (16, 24, 32, 48, 64, 128, 256) + images: list[tuple[int, bytes]] = [] + for size in sizes: + pixmap = brand.pixmap(size) + images.append((size, _png_bytes(pixmap))) + header = struct.pack("= 256 else size + height = 0 if size >= 256 else size + entries += struct.pack(" int: + app = QApplication.instance() or QApplication(sys.argv) + RESOURCES.mkdir(parents=True, exist_ok=True) + _check() + _radio() + _chevron() + _ico() + print("assets written to", RESOURCES) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/boltclam/__init__.py b/src/boltclam/__init__.py new file mode 100644 index 0000000..be38a5a --- /dev/null +++ b/src/boltclam/__init__.py @@ -0,0 +1,7 @@ +"""BoltClam — a Windows desktop antivirus front-end for ClamAV.""" + +__version__ = "1.0.0" +__build_date__ = "2026-10-25" +APP_NAME = "BoltClam" +ORG_NAME = "The Majestic Network" +APP_ID = "com.themajesticnetwork.boltclam" diff --git a/src/boltclam/__main__.py b/src/boltclam/__main__.py new file mode 100644 index 0000000..e4f017d --- /dev/null +++ b/src/boltclam/__main__.py @@ -0,0 +1,4 @@ +from boltclam.app import main + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/boltclam/api_client.py b/src/boltclam/api_client.py new file mode 100644 index 0000000..f90d41a --- /dev/null +++ b/src/boltclam/api_client.py @@ -0,0 +1,267 @@ +"""HTTP client for the Bolt API's BoltClam endpoints. + +Uses only the standard library so the Windows build needs no third-party +HTTP package beyond PyQt6. TLS verification is always on; the only allowed +non-HTTPS base is a loopback development URL. +""" + +from __future__ import annotations + +import hashlib +import json +import logging +import os +import platform +import ssl +import uuid +from pathlib import Path +from typing import Any, Callable +from urllib.error import HTTPError, URLError +from urllib.parse import urlparse +from urllib.request import Request, urlopen + +from . import __version__ + +logger = logging.getLogger(__name__) + + +class ApiError(Exception): + def __init__(self, message: str, *, status: int | None = None, + code: str | None = None, retry_after: int | None = None, + body: Any = None) -> None: + super().__init__(message) + self.status = status + self.code = code + self.retry_after = retry_after + self.body = body + + +class NetworkError(ApiError): + pass + + +def _local_base(base_url: str) -> bool: + host = (urlparse(base_url).hostname or "").lower() + return host in ("127.0.0.1", "localhost", "::1") + + +def _decode_body(raw: bytes) -> Any: + if not raw: + return None + try: + return json.loads(raw.decode("utf-8", "replace")) + except ValueError: + return raw[:2000].decode("utf-8", "replace") + + +class ApiClient: + def __init__(self, base_url: str, api_key: str = "", *, + device_name: str = "", install_id: str = "", + timeout: int = 30) -> None: + base = str(base_url or "").strip().rstrip("/") + parsed = urlparse(base) + if parsed.scheme not in ("https", "http") or not parsed.netloc: + raise ValueError("The Bolt API address must be an http(s) URL.") + if parsed.scheme != "https" and not _local_base(base): + raise ValueError("The Bolt API address must use HTTPS.") + self.base_url = base + self.api_key = api_key or "" + self.device_name = device_name or platform.node() or "Windows PC" + self.install_id = install_id or "" + self.timeout = timeout + + # -- low level --------------------------------------------------------- + + def _url(self, path: str) -> str: + return self.base_url + "/" + str(path).lstrip("/") + + def _headers(self, *, auth: bool, extra: dict | None = None) -> dict[str, str]: + headers = { + "Accept": "application/json", + "User-Agent": f"BoltClam/{__version__} ({platform.system() or 'Windows'})", + "X-BoltClam-Client": "windows", + "X-BoltClam-Version": __version__, + } + if self.install_id: + headers["X-BoltClam-Install-Id"] = self.install_id + if auth and self.api_key: + headers["X-Desk-Api-Key"] = self.api_key + if extra: + headers.update({str(k): str(v) for k, v in extra.items()}) + return headers + + def _request(self, method: str, path: str, *, payload: Any = None, + auth: bool = True, timeout: int | None = None, + extra_headers: dict | None = None) -> Any: + data = None + headers = self._headers(auth=auth, extra=extra_headers) + if payload is not None: + data = json.dumps(payload).encode("utf-8") + headers["Content-Type"] = "application/json" + request = Request(self._url(path), data=data, headers=headers, method=method.upper()) + context = ssl.create_default_context() + try: + with urlopen(request, timeout=timeout or self.timeout, context=context) as response: + raw = response.read() + content_type = response.headers.get("Content-Type", "") + except HTTPError as exc: + raw = exc.read() if exc.fp else b"" + body = _decode_body(raw) + code = body.get("error") if isinstance(body, dict) else None + retry_after = None + if isinstance(body, dict): + try: + retry_after = int(body.get("retry_after")) if body.get("retry_after") is not None else None + except (TypeError, ValueError): + retry_after = None + message = { + 401: "Your BoltClam session is no longer valid. Sign in again.", + 402: "This Bolt account is out of credits.", + 429: "Too many requests. Wait a moment and try again.", + 502: "The Bolt service is temporarily unavailable.", + }.get(exc.code, f"Bolt API request failed ({exc.code}).") + raise ApiError(message, status=exc.code, code=code, + retry_after=retry_after, body=body) from None + except (URLError, TimeoutError, OSError) as exc: + reason = getattr(exc, "reason", exc) + raise NetworkError(f"Could not reach the Bolt service: {reason}") from None + + if "application/json" in content_type or raw[:1] in (b"{", b"["): + body = _decode_body(raw) + else: + body = raw + if not 200 <= getattr(response, "status", 200) < 300: + raise ApiError("Bolt API request failed.", status=getattr(response, "status", None), + body=body) + return body if body is not None else {} + + # -- endpoints --------------------------------------------------------- + + def get_account(self) -> dict: + data = self._request("GET", "/boltclam/account") + if not isinstance(data, dict): + raise ApiError("The Bolt service returned an unexpected account response.") + return data + + def get_updates(self, channel: str = "release", force: bool = False) -> dict: + suffix = "?channel=" + ("beta" if channel == "beta" else "release") + if force: + suffix += "&force=1" + data = self._request("GET", "/boltclam/updates" + suffix) + if not isinstance(data, dict): + raise ApiError("The Bolt service returned an unexpected update response.") + return data + + def logout(self) -> None: + try: + self._request("POST", "/desk/auth/logout", payload={}) + except ApiError as exc: + logger.warning("Logout request failed: %s", exc) + + def download_asset(self, url: str, destination: Path, + progress: Callable[[int, int], None] | None = None, + timeout: int = 300) -> tuple[Path, str]: + parsed = urlparse(str(url or "")) + base = urlparse(self.base_url) + if parsed.scheme: + if parsed.scheme != base.scheme or parsed.netloc != base.netloc: + raise ApiError("Refusing to download an update from an unexpected host.") + full_url = str(url) + else: + full_url = self._url(str(url)) + destination = Path(destination) + destination.parent.mkdir(parents=True, exist_ok=True) + temp_path = destination.with_name(destination.name + ".part") + request = Request(full_url, headers=self._headers(auth=True), method="GET") + digest = hashlib.sha256() + try: + with urlopen(request, timeout=timeout, context=ssl.create_default_context()) as response: + total = int(response.headers.get("Content-Length") or 0) + received = 0 + with open(temp_path, "wb") as handle: + while True: + chunk = response.read(1024 * 256) + if not chunk: + break + handle.write(chunk) + digest.update(chunk) + received += len(chunk) + if progress: + try: + progress(received, total) + except Exception: + pass + os.replace(temp_path, destination) + except HTTPError as exc: + temp_path.unlink(missing_ok=True) + body = _decode_body(exc.read() if exc.fp else b"") + code = body.get("error") if isinstance(body, dict) else None + raise ApiError(f"Update download failed ({exc.code}).", status=exc.code, + code=code, body=body) from None + except (URLError, TimeoutError, OSError) as exc: + temp_path.unlink(missing_ok=True) + raise NetworkError(f"Update download failed: {getattr(exc, 'reason', exc)}") from None + return destination, digest.hexdigest() + + def upload_diagnostics(self, zip_path: Path, metadata: dict | None = None, + timeout: int = 180) -> dict: + zip_path = Path(zip_path) + if not zip_path.is_file(): + raise ApiError("The diagnostics bundle does not exist.") + boundary = "----BoltClam" + uuid.uuid4().hex + body = bytearray() + fields = {"client": "boltclam", "app_version": __version__} + fields.update({k: str(v) for k, v in (metadata or {}).items() if v is not None}) + for name, value in fields.items(): + body.extend(f"--{boundary}\r\n".encode()) + body.extend(f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode()) + body.extend(str(value).encode("utf-8")) + body.extend(b"\r\n") + filename = zip_path.name.replace('"', "_") + body.extend(f"--{boundary}\r\n".encode()) + body.extend( + f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n'.encode()) + body.extend(b"Content-Type: application/zip\r\n\r\n") + body.extend(zip_path.read_bytes()) + body.extend(b"\r\n") + body.extend(f"--{boundary}--\r\n".encode()) + + headers = self._headers(auth=True, extra={ + "Content-Type": f"multipart/form-data; boundary={boundary}", + "Content-Length": str(len(body)), + }) + request = Request(self._url("/boltclam/logs"), data=bytes(body), + headers=headers, method="POST") + try: + with urlopen(request, timeout=timeout, context=ssl.create_default_context()) as response: + raw = response.read() + except HTTPError as exc: + raw = exc.read() if exc.fp else b"" + parsed = _decode_body(raw) + code = parsed.get("error") if isinstance(parsed, dict) else None + raise ApiError(f"Log upload failed ({exc.code}).", status=exc.code, + code=code, body=parsed) from None + except (URLError, TimeoutError, OSError) as exc: + raise NetworkError(f"Log upload failed: {getattr(exc, 'reason', exc)}") from None + parsed = _decode_body(raw) + if not isinstance(parsed, dict): + raise ApiError("The Bolt service returned an unexpected log-upload response.") + return parsed + + # -- unauthenticated exchange ----------------------------------------- + + @classmethod + def exchange_code(cls, base_url: str, *, code: str, verifier: str, + redirect_uri: str, device_name: str, install_id: str) -> dict: + client = cls(base_url, install_id=install_id, device_name=device_name) + data = client._request("POST", "/desk/auth/oauth/exchange", auth=False, payload={ + "code": code, + "code_verifier": verifier, + "redirect_uri": redirect_uri, + "client": "boltclam", + "device_name": device_name, + "install_id": install_id, + }) + if not isinstance(data, dict) or not data.get("api_key"): + raise ApiError("Bolt did not return a device key.") + return data diff --git a/src/boltclam/app.py b/src/boltclam/app.py new file mode 100644 index 0000000..0c18282 --- /dev/null +++ b/src/boltclam/app.py @@ -0,0 +1,193 @@ +"""Application bootstrap: login/restore, dependency wiring, and window lifecycle.""" + +from __future__ import annotations + +import logging +import os +import sys +import time +from pathlib import Path + +from PyQt6.QtCore import QEventLoop, QLockFile, QRectF, Qt, QTimer +from PyQt6.QtGui import QColor, QFont, QPainter, QPixmap +from PyQt6.QtWidgets import QApplication, QDialog, QMessageBox, QSplashScreen + +from . import APP_ID, __build_date__, __version__, paths +from .api_client import ApiClient, ApiError, NetworkError +from .auth import AuthService, CredentialStore, Session, SessionExpired +from .clamav import ClamAVService +from .config import AccountCache, SettingsStore +from .context import AppContext +from .logging_setup import configure as configure_logging +from .quarantine import QuarantineService +from .storage import Storage +from .ui import brand, theme +from .ui.login_dialog import LoginDialog +from .ui.main_window import MainWindow + +logger = logging.getLogger(__name__) + + +def _set_windows_app_id() -> None: + if os.name != "nt": + return + try: + import ctypes + + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(APP_ID) + except Exception: + pass + + +def _splash_pixmap() -> QPixmap: + image = QPixmap(460, 250) + image.fill(Qt.GlobalColor.transparent) + painter = QPainter(image) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + painter.setBrush(QColor("#081820")) + painter.setPen(QColor("#1B3946")) + painter.drawRoundedRect(QRectF(1, 1, 458, 248), 22, 22) + brand.paint_mark(painter, QRectF(34, 34, 72, 72)) + painter.setPen(QColor(theme.TEXT)) + painter.setFont(theme.display_font(26, QFont.Weight.DemiBold)) + painter.drawText(QRectF(124, 36, 300, 40), Qt.AlignmentFlag.AlignLeft, + Qt.AlignmentFlag.AlignVCenter, "BoltClam") + painter.setPen(QColor(theme.MUTED)) + painter.setFont(theme.body_font(10.0)) + painter.drawText(QRectF(126, 76, 300, 24), Qt.AlignmentFlag.AlignLeft, + Qt.AlignmentFlag.AlignVCenter, "ClamAV protection, without the noise.") + painter.setPen(QColor(theme.TEXT_SOFT)) + painter.setFont(theme.body_font(10.5)) + painter.drawText(QRectF(36, 154, 390, 26), Qt.AlignmentFlag.AlignLeft, + Qt.AlignmentFlag.AlignVCenter, "Starting protection…") + painter.setPen(QColor(theme.DIM)) + painter.setFont(theme.mono_font(8.0)) + painter.drawText(QRectF(36, 206, 390, 20), Qt.AlignmentFlag.AlignLeft, + Qt.AlignmentFlag.AlignVCenter, f"Version {__version__} · {__build_date__}") + painter.end() + return image + + +def _install_exception_hook() -> None: + def handle(exc_type, exc_value, exc_traceback): + if issubclass(exc_type, KeyboardInterrupt): + sys.__excepthook__(exc_type, exc_value, exc_traceback) + return + logger.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback)) + try: + QMessageBox.critical( + None, "BoltClam encountered an error", + f"{exc_type.__name__}: {exc_value}\n\nDetails were written to the BoltClam log.") + except Exception: + pass + + sys.excepthook = handle + + +def _build_context(settings_store: SettingsStore, auth: AuthService, + settings, session: Session) -> AppContext: + storage = Storage() + quarantine = QuarantineService(storage) + clamav = ClamAVService(settings.clamav_path) + api = ApiClient(settings.api_base_url, session.api_key, + device_name=auth.device_name, install_id=settings.install_id) + return AppContext( + settings_store=settings_store, + settings=settings, + storage=storage, + quarantine=quarantine, + clamav=clamav, + auth=auth, + session_key=session.api_key, + account=session.account, + install_id=settings.install_id, + api=api, + offline=session.offline, + ) + + +def _restore_session(auth: AuthService, stored: str | None) -> tuple[Session | None, str]: + if not stored: + return None, "" + try: + return auth.restore(stored), "" + except SessionExpired: + return None, "Your previous BoltClam session expired. Sign in again to continue." + except Exception as exc: + cached = auth.account_cache.load() + if cached: + logger.info("Starting offline with the last verified account: %s", exc) + return Session(stored, cached, offline=True), ( + "Bolt is unreachable right now. BoltClam is using the last verified account " + "and local protection still works.") + logger.info("Could not restore the saved session: %s", exc) + return None, f"Could not restore your BoltClam session: {exc}" + + +def main() -> int: + _set_windows_app_id() + os.environ.setdefault("QT_ENABLE_HIGHDPI_SCALING", "1") + app = QApplication(sys.argv) + theme.setup_application(app) + app.setQuitOnLastWindowClosed(False) + app.setWindowIcon(brand.app_icon()) + paths.ensure_dirs() + + lock = QLockFile(str(paths.config_dir() / "boltclam.lock")) + if not lock.tryLock(150): + QMessageBox.information( + None, "BoltClam is already running", + "Only one BoltClam can watch downloads at a time. Open the copy in the " + "system tray, or quit it before starting another.") + return 0 + + settings_store = SettingsStore() + settings = settings_store.load() + env_api = os.environ.get("BOLTCLAM_API_URL", "").strip() + if env_api and env_api != settings.api_base_url: + settings = settings_store.update(api_base_url=env_api) + configure_logging(settings) + _install_exception_hook() + logger.info("BoltClam %s starting (%s)", __version__, paths.os_label()) + + splash = QSplashScreen(_splash_pixmap()) + splash.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True) + splash.show() + app.processEvents() + + credentials = CredentialStore() + account_cache = AccountCache() + auth = AuthService( + settings.api_base_url, install_id=settings.install_id, + credentials=credentials, account_cache=account_cache) + stored = auth.stored_key() + session, notice = _restore_session(auth, stored) + splash.close() + + if session is None: + dialog = LoginDialog(auth, notice=notice) + if dialog.exec() != QDialog.DialogCode.Accepted or dialog.session is None: + logger.info("Sign-in cancelled at startup") + return 0 + session = dialog.session + + while True: + context = _build_context(settings_store, auth, settings, session) + window = MainWindow(context) + loop = QEventLoop() + window.finished.connect(loop.quit) + window.show() + loop.exec() + logged_out = window.logged_out() + window.deleteLater() + app.processEvents() + if not logged_out: + break + dialog = LoginDialog(auth, notice="You signed out. Sign in again to use BoltClam.") + if dialog.exec() != QDialog.DialogCode.Accepted or dialog.session is None: + break + session = dialog.session + settings = settings_store.load() + + logger.info("BoltClam exiting") + return 0 diff --git a/src/boltclam/auth.py b/src/boltclam/auth.py new file mode 100644 index 0000000..614b9ae --- /dev/null +++ b/src/boltclam/auth.py @@ -0,0 +1,448 @@ +"""BoltClam sign-in: loopback PKCE, DPAPI credential storage, session restore. + +The desktop flow mirrors the iOS/Android app: + +1. BoltClam generates a PKCE verifier/challenge and a random state, then + listens on ``http://127.0.0.1:/cb``. +2. It opens the Bolt-hosted sign-in page in the default browser. +3. After the user signs in, Bolt redirects to the loopback listener with a + short-lived one-time code. +4. BoltClam posts that code + its verifier to ``/desk/auth/oauth/exchange`` + and receives an ordinary ``bolt_live_...`` API key, which is stored with + Windows DPAPI (never in settings or logs). +""" + +from __future__ import annotations + +import base64 +import ctypes +import hashlib +import html +import json +import logging +import os +import platform +import secrets +import socket +import threading +import time +import uuid +from dataclasses import dataclass +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Callable, Optional +from urllib.parse import parse_qs, urlencode, urlparse + +from .api_client import ApiClient, ApiError, NetworkError +from .config import AccountCache +from .paths import credentials_path + +logger = logging.getLogger(__name__) + +_DEFAULT_TIMEOUT = 300 +_ERROR_MESSAGES = { + "access_denied": "Sign-in was cancelled in the browser.", + "oauth_failed": "The sign-in provider could not complete the request.", + "unverified_email": "That account's email is not verified with the provider.", + "invalid_grant": "The sign-in code was not accepted. Please try again.", +} + + +class AuthError(Exception): + pass + + +class AuthCancelled(AuthError): + pass + + +class AuthTimeout(AuthError): + pass + + +class SessionExpired(AuthError): + pass + + +class CredentialError(AuthError): + pass + + +# ── PKCE ──────────────────────────────────────────────────────────────────── + +def pkce_pair() -> tuple[str, str]: + verifier = base64.urlsafe_b64encode(secrets.token_bytes(48)).rstrip(b"=").decode() + challenge = base64.urlsafe_b64encode( + hashlib.sha256(verifier.encode("ascii")).digest()).rstrip(b"=").decode() + return verifier, challenge + + +# ── Windows DPAPI ─────────────────────────────────────────────────────────── + +class _DATA_BLOB(ctypes.Structure): + _fields_ = [("cbData", ctypes.c_uint32), + ("pbData", ctypes.POINTER(ctypes.c_ubyte))] + + +def _blob(data: bytes) -> tuple[_DATA_BLOB, ctypes.Array]: + buffer = ctypes.create_string_buffer(data) + blob = _DATA_BLOB(len(data), ctypes.cast(buffer, ctypes.POINTER(ctypes.c_ubyte))) + return blob, buffer + + +def _dpapi(data: bytes, protect: bool) -> bytes: + crypt32 = ctypes.windll.crypt32 + kernel32 = ctypes.windll.kernel32 + in_blob, in_buffer = _blob(data) + entropy_blob, entropy_buffer = _blob(b"BoltClam/v1") + out_blob = _DATA_BLOB() + if protect: + function = crypt32.CryptProtectData + function.argtypes = [ctypes.POINTER(_DATA_BLOB), ctypes.c_wchar_p, + ctypes.POINTER(_DATA_BLOB), ctypes.c_void_p, + ctypes.c_void_p, ctypes.c_uint32, + ctypes.POINTER(_DATA_BLOB)] + else: + function = crypt32.CryptUnprotectData + function.argtypes = [ctypes.POINTER(_DATA_BLOB), ctypes.POINTER(ctypes.c_wchar_p), + ctypes.POINTER(_DATA_BLOB), ctypes.c_void_p, + ctypes.c_void_p, ctypes.c_uint32, + ctypes.POINTER(_DATA_BLOB)] + function.restype = ctypes.c_int + if protect: + ok = function(ctypes.byref(in_blob), "BoltClam device key", + ctypes.byref(entropy_blob), None, None, 0x1, ctypes.byref(out_blob)) + else: + description = ctypes.c_wchar_p() + ok = function(ctypes.byref(in_blob), ctypes.byref(description), + ctypes.byref(entropy_blob), None, None, 0x1, ctypes.byref(out_blob)) + # Keep the input buffers alive until the call returns. + _ = (in_buffer, entropy_buffer) + if not ok: + raise ctypes.WinError() + try: + return ctypes.string_at(out_blob.pbData, out_blob.cbData) + finally: + kernel32.LocalFree(out_blob.pbData) + + +class CredentialStore: + """DPAPI-protected API key on Windows; a 0600 file on dev machines. + + The fallback is deliberately *not* pretended to be secure: it exists so + running from source on Linux/macOS works. Windows always uses DPAPI. + """ + + def __init__(self, path: Path | None = None) -> None: + self.path = Path(path) if path else credentials_path() + self._lock = threading.RLock() + + @property + def is_secure(self) -> bool: + return os.name == "nt" + + def save(self, api_key: str) -> None: + if not api_key: + raise CredentialError("No API key was supplied.") + raw = api_key.encode("utf-8") + if self.is_secure: + payload = {"version": 1, "protection": "dpapi", + "data": base64.b64encode(_dpapi(raw, True)).decode("ascii")} + else: + logger.warning("Credential store is using a local-file fallback (not Windows DPAPI).") + payload = {"version": 1, "protection": "plain", + "data": base64.b64encode(raw).decode("ascii")} + with self._lock: + self.path.parent.mkdir(parents=True, exist_ok=True) + temp = self.path.with_name(self.path.name + ".tmp") + temp.write_text(json.dumps(payload), encoding="utf-8") + try: + os.chmod(temp, 0o600) + except OSError: + pass + os.replace(temp, self.path) + + def load(self) -> Optional[str]: + with self._lock: + try: + payload = json.loads(self.path.read_text(encoding="utf-8")) + except FileNotFoundError: + return None + except (OSError, ValueError) as exc: + raise CredentialError("Your saved BoltClam sign-in could not be read.") from exc + if not isinstance(payload, dict): + raise CredentialError("Your saved BoltClam sign-in is malformed.") + try: + raw = base64.b64decode(str(payload.get("data") or "")) + except (ValueError, TypeError) as exc: + raise CredentialError("Your saved BoltClam sign-in is malformed.") from exc + protection = payload.get("protection") + if protection == "dpapi": + if not self.is_secure: + raise CredentialError("This sign-in was saved with Windows DPAPI and cannot be read here.") + try: + return _dpapi(raw, False).decode("utf-8") + except OSError as exc: + raise CredentialError("Windows could not unlock your BoltClam sign-in.") from exc + if protection == "plain": + return raw.decode("utf-8", "replace") + raise CredentialError("Your saved BoltClam sign-in uses an unknown format.") + + def delete(self) -> None: + with self._lock: + try: + self.path.unlink() + except FileNotFoundError: + pass + except OSError: + logger.warning("Could not delete the saved credential file", exc_info=True) + + +# ── loopback listener ─────────────────────────────────────────────────────── + +_CALLBACK_CSS = """ +:root{color-scheme:dark} +body{margin:0;min-height:100vh;display:grid;place-items:center;background:#071018; +color:#eef7fb;font:15px/1.55 "Segoe UI",system-ui,sans-serif;padding:24px} +.card{max-width:420px;background:#0d1b26;border:1px solid #1d3444;border-radius:20px; +padding:32px;text-align:center;box-shadow:0 24px 70px rgba(0,0,0,.45)} +.mark{width:54px;height:54px;border-radius:16px;margin:0 auto 18px;display:grid; +place-items:center;background:rgba(57,224,200,.1);border:1px solid rgba(57,224,200,.35)} +h1{font-size:22px;margin:0 0 8px}p{color:#8fa9bb;margin:0} +""" + + +def _callback_page(title: str, message: str) -> bytes: + return f""" + +{html.escape(title)} +

+

{html.escape(title)}

{html.escape(message)}

""".encode() + + +class LoopbackAuthReceiver: + """A one-shot RFC 8252 loopback listener bound to 127.0.0.1.""" + + def __init__(self, state: str) -> None: + self.state = state + self.result: dict | None = None + self.event = threading.Event() + receiver = self + + class Handler(BaseHTTPRequestHandler): + def do_GET(self) -> None: # noqa: N802 + parsed = urlparse(self.path) + if parsed.path == "/favicon.ico": + self.send_response(204) + self.end_headers() + return + if parsed.path != "/cb": + self.send_response(404) + self.end_headers() + return + query = parse_qs(parsed.query) + state = (query.get("state") or [""])[0] + if state != receiver.state: + body = _callback_page("Sign-in state did not match", + "Return to BoltClam and start sign-in again.") + self.send_response(400) + self.send_header("Content-Type", "text/html; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + return + error = (query.get("error") or [""])[0] + code = (query.get("code") or [""])[0] + receiver.result = {"error": error, "code": code, "state": state} + body = _callback_page( + "BoltClam is signed in" if code and not error else "Sign-in needs attention", + "You can close this tab and return to BoltClam." + if code and not error else "Return to BoltClam to try again.", + ) + self.send_response(200 if code and not error else 400) + self.send_header("Content-Type", "text/html; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "no-store") + self.send_header("X-Frame-Options", "DENY") + self.end_headers() + self.wfile.write(body) + receiver.event.set() + + def log_message(self, fmt: str, *args) -> None: + return + + self._server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self._server.daemon_threads = True + port = int(self._server.server_address[1]) + if not (1024 <= port <= 65535): + self._server.server_close() + raise AuthError("Could not open a local sign-in port.") + self._thread = threading.Thread(target=self._server.serve_forever, + name="boltclam-auth-loopback", daemon=True) + + @property + def redirect_uri(self) -> str: + return f"http://127.0.0.1:{int(self._server.server_address[1])}/cb" + + def start(self) -> None: + self._thread.start() + + def wait(self, timeout: float, cancel_event: threading.Event | None = None) -> dict: + deadline = time.monotonic() + max(1.0, timeout) + while time.monotonic() < deadline: + if cancel_event is not None and cancel_event.is_set(): + raise AuthCancelled("Sign-in was cancelled.") + if self.event.wait(0.2): + return dict(self.result or {}) + raise AuthTimeout("BoltClam did not receive the browser sign-in in time.") + + def close(self) -> None: + try: + self._server.shutdown() + except Exception: + pass + try: + self._server.server_close() + except Exception: + pass + + +# ── auth service ──────────────────────────────────────────────────────────── + +@dataclass +class Session: + api_key: str + account: dict | None + offline: bool = False + + +def _default_device_name() -> str: + host = platform.node() or "Windows PC" + return f"BoltClam on {host}"[:200] + + +class AuthService: + def __init__(self, base_url: str, *, device_name: str = "", install_id: str = "", + credentials: CredentialStore | None = None, + account_cache: AccountCache | None = None) -> None: + self.base_url = base_url.rstrip("/") + self.device_name = (device_name or _default_device_name())[:200] + self.install_id = install_id or uuid.uuid4().hex + self.credentials = credentials or CredentialStore() + self.account_cache = account_cache or AccountCache() + + def stored_key(self) -> Optional[str]: + try: + return self.credentials.load() + except CredentialError as exc: + logger.warning("Stored credential could not be read: %s", exc) + return None + + def restore(self, api_key: str | None = None) -> Session: + key = api_key or self.stored_key() + if not key: + raise SessionExpired("No saved sign-in.") + client = ApiClient(self.base_url, key, device_name=self.device_name, + install_id=self.install_id) + try: + account = client.get_account() + except ApiError as exc: + if exc.status == 401: + raise SessionExpired("Your BoltClam session has expired.") from None + if isinstance(exc, NetworkError): + cached = self.account_cache.load() + if cached: + logger.info("Bolt API unreachable; opening with the last verified account.") + return Session(key, cached, offline=True) + raise + self.account_cache.save(account) + return Session(key, account) + + def browser_login(self, open_url: Callable[[str], bool | None], + timeout: float = _DEFAULT_TIMEOUT, + cancel_event: threading.Event | None = None) -> Session: + verifier, challenge = pkce_pair() + state = secrets.token_urlsafe(24) + receiver = LoopbackAuthReceiver(state) + try: + receiver.start() + query = urlencode({ + "redirect_uri": receiver.redirect_uri, + "state": state, + "code_challenge": challenge, + "code_challenge_method": "S256", + "client": "boltclam", + "device_name": self.device_name, + }) + url = f"{self.base_url}/desk/auth/boltclam/start?{query}" + logger.info("Opening BoltClam browser sign-in") + opened = open_url(url) + if opened is False: + raise AuthError("Could not open your browser for sign-in.") + callback = receiver.wait(timeout, cancel_event=cancel_event) + error = str(callback.get("error") or "") + code = str(callback.get("code") or "") + if error: + raise AuthError(_ERROR_MESSAGES.get(error, "Sign-in failed. Please try again.")) + if not code: + raise AuthError("The browser did not return a sign-in code.") + try: + exchanged = ApiClient.exchange_code( + self.base_url, code=code, verifier=verifier, + redirect_uri=receiver.redirect_uri, + device_name=self.device_name, install_id=self.install_id, + ) + except ApiError as exc: + raise AuthError(_ERROR_MESSAGES.get(str(exc.code or ""), str(exc))) from None + key = str(exchanged["api_key"]) + account = self._fetch_account(key) + self.credentials.save(key) + if account: + self.account_cache.save(account) + return Session(key, account) + finally: + receiver.close() + + def login_with_key(self, api_key: str) -> Session: + key = str(api_key or "").strip() + if not key: + raise AuthError("Enter a Bolt API key.") + client = ApiClient(self.base_url, key, device_name=self.device_name, + install_id=self.install_id) + try: + account = client.get_account() + except ApiError as exc: + if exc.status == 401: + raise AuthError("That API key was not accepted.") from None + raise + self.credentials.save(key) + self.account_cache.save(account) + return Session(key, account) + + def _fetch_account(self, key: str) -> dict | None: + client = ApiClient(self.base_url, key, device_name=self.device_name, + install_id=self.install_id) + try: + return client.get_account() + except ApiError as exc: + # The exchange already proved the key works. A transient account + # fetch failure should not throw away a successful sign-in; the + # app will refresh the account after the first successful call. + if isinstance(exc, NetworkError): + logger.warning("Signed in, but the account summary was unreachable: %s", exc) + return None + if exc.status == 401: + raise AuthError("Bolt issued a key that it then rejected. Try again.") from None + logger.warning("Signed in, but the account summary failed: %s", exc) + return None + + def logout(self, api_key: str | None = None) -> None: + key = api_key or self.stored_key() + if key: + ApiClient(self.base_url, key, device_name=self.device_name, + install_id=self.install_id).logout() + self.credentials.delete() + self.account_cache.clear() diff --git a/src/boltclam/clamav.py b/src/boltclam/clamav.py new file mode 100644 index 0000000..0befee1 --- /dev/null +++ b/src/boltclam/clamav.py @@ -0,0 +1,380 @@ +"""ClamAV discovery, command construction, target selection and output parsing. + +The app manages its own signature directory (``data/clamav-db``) so +``freshclam`` can update definitions without an administrator prompt. If +that directory is empty, the database shipped beside ``clamscan.exe`` is +used instead. Neither path is ever accepted from scan output or the network. +""" + +from __future__ import annotations + +import os +import re +import shlex +import shutil +import subprocess +import time +from dataclasses import dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Iterable + +from . import paths +from .models import ClamAVInfo + +_ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]") +_VERSION_RE = re.compile(r"ClamAV\s+([0-9][0-9A-Za-z.\-]*)(?:/([0-9]+)(?:/([^\r\n]+))?)?") +_QUOTES = ("'", '"') +_DANGEROUS_OPTIONS = ("--remove", "--move", "--copy") + + +class ClamAVError(Exception): + pass + + +def hidden_kwargs() -> dict: + """subprocess kwargs that keep a GUI launch from flashing a console.""" + if os.name != "nt": + return {} + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = 0 # SW_HIDE + return { + "creationflags": getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000), + "startupinfo": startupinfo, + } + + +def _candidate_names() -> list[str]: + return ["clamscan.exe", "clamscan"] + + +def find_clamscan(configured: str = "") -> tuple[Path | None, str]: + configured = os.path.expandvars(os.path.expanduser(str(configured or "").strip())) + if configured: + path = Path(configured) + if path.is_dir(): + for name in _candidate_names(): + candidate = path / name + if candidate.is_file(): + return candidate, "" + return None, "No clamscan executable was found in that folder." + if path.is_file(): + return path, "" + return None, "The configured ClamAV path does not exist." + + candidates: list[Path] = [] + env_path = os.environ.get("CLAMSCAN_PATH", "").strip() + if env_path: + candidates.append(Path(os.path.expandvars(os.path.expanduser(env_path)))) + for env_name in ("ProgramFiles", "ProgramFiles(x86)"): + base = os.environ.get(env_name) + if base: + candidates.append(Path(base) / "ClamAV" / "clamscan.exe") + candidates.append(Path(base) / "ClamAV" / "clamscan") + candidates.extend([Path(r"C:\ClamAV\clamscan.exe"), + Path(r"C:\Program Files\ClamAV\clamscan.exe")]) + for name in _candidate_names(): + found = shutil.which(name) + if found: + candidates.append(Path(found)) + candidates.extend([Path("/usr/bin/clamscan"), Path("/usr/local/bin/clamscan")]) + + for candidate in candidates: + try: + if candidate.is_file(): + return candidate, "" + except OSError: + continue + return None, "ClamAV was not found. Set its install folder in Settings." + + +def find_freshclam(clamscan: Path | None) -> tuple[Path | None, str]: + env_path = os.environ.get("FRESHCLAM_PATH", "").strip() + candidates: list[Path] = [] + if env_path: + candidates.append(Path(os.path.expandvars(os.path.expanduser(env_path)))) + if clamscan is not None: + candidates.append(clamscan.with_name("freshclam.exe")) + candidates.append(clamscan.with_name("freshclam")) + for name in ("freshclam.exe", "freshclam"): + found = shutil.which(name) + if found: + candidates.append(Path(found)) + for candidate in candidates: + try: + if candidate.is_file(): + return candidate, "" + except OSError: + continue + return None, "freshclam was not found next to clamscan." + + +def parse_version_output(text: str) -> tuple[str, str, str]: + match = _VERSION_RE.search(text or "") + if not match: + return "", "", "" + return match.group(1) or "", match.group(2) or "", (match.group(3) or "").strip() + + +def database_files(directory: Path | None) -> list[Path]: + if directory is None or not directory.is_dir(): + return [] + files: list[Path] = [] + for name in ("main.cvd", "main.cld", "daily.cvd", "daily.cld", + "bytecode.cvd", "bytecode.cld"): + candidate = directory / name + if candidate.is_file(): + files.append(candidate) + return files + + +def has_database(directory: Path | None) -> bool: + return bool(database_files(directory)) + + +def database_age(directory: Path | None) -> tuple[str, float | None]: + files = database_files(directory) + if not files: + return "", None + try: + newest = max(files, key=lambda path: path.stat().st_mtime) + timestamp = newest.stat().st_mtime + except OSError: + return "", None + age = max(0.0, (time.time() - timestamp) / 86400.0) + date = datetime.fromtimestamp(timestamp, tz=timezone.utc).strftime("%Y-%m-%d") + return date, age + + +def parse_extra_options(text: str) -> list[str]: + """Split user-entered extra ClamAV options into an argv list. + + Destructive options are refused because remediation belongs to the app's + quarantine/delete path, which records what happened. ``posix=False`` + keeps Windows paths with backslashes intact; surrounding quotes are + stripped manually afterward. + """ + raw = str(text or "").strip() + if not raw: + return [] + if "\x00" in raw: + raise ValueError("ClamAV extra options contain an invalid character.") + # shlex's POSIX mode understands quotes that wrap a whole value (including + # spaces), but it treats backslashes as escapes. Doubling them first keeps + # Windows paths intact while still letting POSIX quoting do its job. + protected = raw.replace("\\", "\\\\") + try: + tokens = shlex.split(protected, posix=True) + except ValueError as exc: + raise ValueError(f"ClamAV extra options could not be parsed: {exc}") from exc + result: list[str] = [] + for token in tokens: + token = token.strip() + if not token: + continue + if len(token) >= 2 and token[0] == token[-1] and token[0] in _QUOTES: + token = token[1:-1] + if any(ch in token for ch in ("\r", "\n", "\x00")): + raise ValueError("ClamAV extra options must be a single line.") + name = token.split("=", 1)[0].lower() + if name in _DANGEROUS_OPTIONS or any(name.startswith(opt + "=") for opt in _DANGEROUS_OPTIONS): + raise ValueError( + f"{name} is managed by BoltClam; use the quarantine/delete settings instead.") + if "=" in token: + option, _, value = token.partition("=") + if len(value) >= 2 and value[0] == value[-1] and value[0] in _QUOTES: + value = value[1:-1] + token = f"{option}={value}" + result.append(token) + return result + + +def build_scan_args(clamscan: Path, targets: Iterable[Path], *, + database_dir: Path | None = None, + quarantine_dir: Path | None = None, + temp_dir: Path | None = None, + extra_options: Iterable[str] = ()) -> list[str]: + args = [str(clamscan), "--recursive=yes", "--infected", "--verbose", "--stdout"] + if has_database(database_dir): + args.append(f"--database={database_dir}") + for excluded in (quarantine_dir, temp_dir): + if excluded: + text = str(excluded) + args.append(f"--exclude-dir={re.escape(text)}") + forward = text.replace("\\", "/") + if forward != text: + args.append(f"--exclude-dir={re.escape(forward)}") + args.extend(str(option) for option in extra_options) + args.extend(str(path) for path in targets) + return args + + +def build_freshclam_args(freshclam: Path, database_dir: Path) -> list[str]: + return [str(freshclam), "--foreground", "--stdout", f"--datadir={database_dir}"] + + +def scan_targets(scope: str, *, profiles: Iterable[Path] | None = None, + drives: Iterable[Path] | None = None) -> list[Path]: + roots = list(drives if drives is not None else paths.fixed_drive_roots()) \ + if scope == "full" else \ + list(profiles if profiles is not None else paths.windows_profile_dirs()) + if not roots: + roots = [Path.home()] + result: list[Path] = [] + seen: set[str] = set() + for root in roots: + try: + root = Path(root).expanduser() + key = os.path.normcase(str(root.resolve())) + if key in seen or not root.is_dir(): + continue + seen.add(key) + result.append(root) + except OSError: + continue + return result + + +def parse_clamscan_line(line: str) -> dict | None: + """Turn one clamscan output line into a small event dictionary. + + Detection paths are parsed with ``rpartition(": ")`` so a Windows drive + letter and any colon in earlier components stay in the path. + """ + text = _ANSI_RE.sub("", str(line or "")).rstrip("\r\n") + if not text: + return None + + stripped = text.strip() + if stripped.endswith(" FOUND"): + body = stripped[:-len(" FOUND")].rstrip() + path, separator, signature = body.rpartition(": ") + if separator and path: + return {"kind": "detection", "path": path, "signature": signature} + + for prefix, key in ( + ("Known viruses:", "known_viruses"), + ("Engine version:", "engine_version"), + ("Scanned directories:", "scanned_directories"), + ("Scanned files:", "scanned_files"), + ("Infected files:", "infected_files"), + ("Data scanned:", "data_scanned"), + ("Data read:", "data_read"), + ("Time:", "time"), + ("Start Date:", "start_date"), + ("End Date:", "end_date"), + ): + if stripped.startswith(prefix): + value = stripped[len(prefix):].strip() + return {"kind": "summary", "key": key, "value": value} + + if stripped.startswith("Scanning "): + return {"kind": "progress", "path": stripped[len("Scanning "):].strip()} + + skipped_suffixes = (": Empty file", ": Size limit exceeded", ": Symbolic link", + ": File name too long", ": Excluded") + for suffix in skipped_suffixes: + if stripped.endswith(suffix): + return {"kind": "skipped", "path": stripped[:-len(suffix)], + "reason": suffix[2:]} + + error_markers = ("Access denied", "Can't open file", "Can't access file", + "No such file or directory", "Permission denied") + for marker in error_markers: + if marker in stripped: + return {"kind": "error", "message": stripped} + + if stripped.startswith(("ERROR:", "LibClamAV Error:", "LibClamAV Error ")): + return {"kind": "error", "message": stripped} + if stripped.startswith(("WARNING:", "LibClamAV Warning:")): + return {"kind": "log", "message": stripped} + if stripped.endswith(": OK"): + return {"kind": "progress", "path": stripped[:-len(": OK")]} + return {"kind": "log", "message": stripped} + + +def summarize(events: Iterable[dict]) -> dict: + summary: dict[str, object] = {} + for event in events: + if event and event.get("kind") == "summary": + key = str(event.get("key") or "") + value = event.get("value") + if key in ("scanned_files", "infected_files", "scanned_directories", + "known_viruses"): + try: + summary[key] = int(str(value).split()[0]) + except (ValueError, IndexError): + pass + elif key == "engine_version": + summary[key] = str(value) + return summary + + +class ClamAVService: + def __init__(self, configured_path: str = "") -> None: + self.configured_path = configured_path + self._info: ClamAVInfo | None = None + self._info_at = 0.0 + + def info(self, force: bool = False) -> ClamAVInfo: + if not force and self._info is not None and time.time() - self._info_at < 30: + return self._info + clamscan, error = find_clamscan(self.configured_path) + if clamscan is None: + info = ClamAVInfo(available=False, error=error) + self._info, self._info_at = info, time.time() + return info + try: + completed = subprocess.run( + [str(clamscan), "--version"], capture_output=True, text=True, + timeout=15, errors="replace", **hidden_kwargs(), + ) + output = (completed.stdout or "") + "\n" + (completed.stderr or "") + engine, db_version, db_date = parse_version_output(output) + if completed.returncode != 0 and not engine: + info = ClamAVInfo(available=False, path=clamscan, + error="clamscan.exe could not run.") + else: + managed = paths.clamav_db_dir() + db_dir = managed if has_database(managed) else clamscan.parent + date, age = database_age(db_dir) + info = ClamAVInfo( + available=True, path=clamscan, + engine_version=engine or "ClamAV", + db_version=db_version, + db_path=db_dir if has_database(db_dir) else None, + db_date=db_date or date, + db_age_days=age, + ) + except (OSError, subprocess.SubprocessError) as exc: + info = ClamAVInfo(available=False, path=clamscan, + error=f"clamscan.exe could not run: {exc}") + self._info, self._info_at = info, time.time() + return info + + def invalidate(self) -> None: + self._info = None + self._info_at = 0.0 + + def freshclam(self) -> Path | None: + path, _error = find_freshclam(self.info().path) + return path + + def scan_arguments(self, targets: list[Path], extra_options: Iterable[str] = (), + *, exclude_temp: bool = True) -> list[str]: + info = self.info() + if not info.available or info.path is None: + raise ClamAVError(info.error or "ClamAV is not available.") + managed = paths.clamav_db_dir() + database = managed if has_database(managed) else ( + info.path.parent if has_database(info.path.parent) else None) + return build_scan_args( + info.path, targets, database_dir=database, + quarantine_dir=paths.quarantine_dir(), + temp_dir=paths.temp_dir() if exclude_temp else None, + extra_options=extra_options, + ) + + def version_summary(self) -> str: + return self.info().summary diff --git a/src/boltclam/config.py b/src/boltclam/config.py new file mode 100644 index 0000000..91e4b1f --- /dev/null +++ b/src/boltclam/config.py @@ -0,0 +1,222 @@ +"""Persistent BoltClam settings and the small non-secret account cache.""" + +from __future__ import annotations + +import json +import logging +import os +import tempfile +import threading +import time +import uuid +from dataclasses import asdict, dataclass, field +from pathlib import Path +from typing import Any + +from . import paths + +logger = logging.getLogger(__name__) + +UPDATE_CHANNELS = ("release", "beta") +SCAN_SCOPES = ("homes", "full") +AUTO_ACTIONS = ("quarantine", "delete", "notify") +DEFAULT_API_URL = "https://api.themajesticnetwork.com" + + +def _as_bool(value: Any, default: bool) -> bool: + if isinstance(value, bool): + return value + if isinstance(value, (int, float)): + return bool(value) + if isinstance(value, str): + lowered = value.strip().lower() + if lowered in ("1", "true", "yes", "on"): + return True + if lowered in ("0", "false", "no", "off"): + return False + return default + + +def _clean_choice(value: Any, choices: tuple[str, ...], default: str) -> str: + text = str(value or "").strip().lower() + return text if text in choices else default + + +def _clean_list(value: Any) -> list[str]: + if not isinstance(value, list): + return [] + cleaned: list[str] = [] + for entry in value: + if not isinstance(entry, str): + continue + text = os.path.expandvars(os.path.expanduser(entry.strip())) + if text and text not in cleaned: + cleaned.append(text) + return cleaned[:30] + + +@dataclass +class AppSettings: + update_channel: str = "release" + clamav_path: str = "" + extra_options: str = "" + logging_enabled: bool = True + debug_logging: bool = False + send_logs: bool = False + scan_scope: str = "homes" + auto_action: str = "quarantine" + realtime_enabled: bool = True + notify_on_detection: bool = True + close_to_tray: bool = True + api_base_url: str = DEFAULT_API_URL + extra_watch_dirs: list[str] = field(default_factory=list) + last_log_upload: str = "" + last_update_check: float = 0.0 + install_id: str = "" + + def normalized(self) -> "AppSettings": + data = asdict(self) + data["update_channel"] = _clean_choice(data["update_channel"], UPDATE_CHANNELS, "release") + data["scan_scope"] = _clean_choice(data["scan_scope"], SCAN_SCOPES, "homes") + data["auto_action"] = _clean_choice(data["auto_action"], AUTO_ACTIONS, "quarantine") + for key in ("logging_enabled", "debug_logging", "send_logs", "realtime_enabled", + "notify_on_detection", "close_to_tray"): + data[key] = _as_bool(data.get(key), getattr(AppSettings(), key)) + data["clamav_path"] = os.path.expandvars(os.path.expanduser( + str(data.get("clamav_path") or "").strip())) + data["extra_options"] = str(data.get("extra_options") or "").strip()[:4000] + data["api_base_url"] = (str(data.get("api_base_url") or DEFAULT_API_URL) + .strip().rstrip("/") or DEFAULT_API_URL) + data["extra_watch_dirs"] = _clean_list(data.get("extra_watch_dirs")) + try: + data["last_update_check"] = max(0.0, float(data.get("last_update_check") or 0.0)) + except (TypeError, ValueError): + data["last_update_check"] = 0.0 + data["last_log_upload"] = str(data.get("last_log_upload") or "")[:80] + data["install_id"] = str(data.get("install_id") or "").strip()[:64] or uuid.uuid4().hex + if data["debug_logging"] and not data["logging_enabled"]: + data["debug_logging"] = False + return AppSettings(**data) + + def public_snapshot(self) -> dict: + """Settings safe to include in a diagnostics bundle. + + The API key is never in this object. Extra ClamAV options are + included because they explain scan behaviour, but token-shaped + strings are redacted by the diagnostics writer. + """ + data = asdict(self.normalized()) + data.pop("install_id", None) + return data + + +class SettingsStore: + def __init__(self, path: Path | None = None) -> None: + self.path = Path(path) if path else paths.settings_path() + self._lock = threading.RLock() + self._settings: AppSettings | None = None + + def load(self) -> AppSettings: + with self._lock: + if self._settings is not None: + return self._settings + data: dict[str, Any] = {} + try: + raw = json.loads(self.path.read_text(encoding="utf-8")) + if isinstance(raw, dict): + data = raw + except FileNotFoundError: + pass + except (OSError, ValueError): + logger.warning("Settings file is unreadable; starting with defaults", exc_info=True) + try: + backup = self.path.with_suffix( + f".corrupt-{time.strftime('%Y%m%d-%H%M%S')}.json") + self.path.replace(backup) + except OSError: + pass + self._settings = AppSettings(**{k: v for k, v in data.items() + if k in AppSettings.__dataclass_fields__}).normalized() + if not self.path.exists(): + self.save(self._settings) + return self._settings + + def save(self, settings: AppSettings) -> AppSettings: + normalized = settings.normalized() + with self._lock: + self.path.parent.mkdir(parents=True, exist_ok=True) + payload = json.dumps(asdict(normalized), indent=2, sort_keys=True) + temp_path: str | None = None + try: + with tempfile.NamedTemporaryFile( + "w", dir=str(self.path.parent), suffix=".tmp", + delete=False, encoding="utf-8") as handle: + handle.write(payload) + handle.flush() + os.fsync(handle.fileno()) + temp_path = handle.name + os.replace(temp_path, self.path) + finally: + if temp_path: + try: + os.unlink(temp_path) + except OSError: + pass + self._settings = normalized + return normalized + + def update(self, **changes: Any) -> AppSettings: + current = self.load() + data = asdict(current) + data.update(changes) + return self.save(AppSettings(**data)) + + +class AccountCache: + """Last verified account payload, used only when the API is unreachable. + + This is deliberately a separate file from settings: it is not public + diagnostic data and it never contains a credential. + """ + + def __init__(self, path: Path | None = None) -> None: + self.path = Path(path) if path else paths.account_cache_path() + self._lock = threading.RLock() + + def load(self) -> dict | None: + with self._lock: + try: + data = json.loads(self.path.read_text(encoding="utf-8")) + except (OSError, ValueError): + return None + if not isinstance(data, dict): + return None + account = data.get("account") + return account if isinstance(account, dict) else None + + def save(self, account: dict) -> None: + with self._lock: + self.path.parent.mkdir(parents=True, exist_ok=True) + payload = {"verified_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "account": account} + temp_path: str | None = None + try: + with tempfile.NamedTemporaryFile( + "w", dir=str(self.path.parent), suffix=".tmp", + delete=False, encoding="utf-8") as handle: + json.dump(payload, handle, indent=1) + temp_path = handle.name + os.replace(temp_path, self.path) + finally: + if temp_path: + try: + os.unlink(temp_path) + except OSError: + pass + + def clear(self) -> None: + with self._lock: + try: + self.path.unlink() + except OSError: + pass diff --git a/src/boltclam/context.py b/src/boltclam/context.py new file mode 100644 index 0000000..b245c30 --- /dev/null +++ b/src/boltclam/context.py @@ -0,0 +1,72 @@ +"""The service bundle shared by every page in the main window.""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from .api_client import ApiClient +from .auth import AuthService +from .clamav import ClamAVService +from .config import AppSettings, SettingsStore +from .quarantine import QuarantineService +from .realtime import RealtimeProtection +from .storage import Storage + + +@dataclass +class AppContext: + settings_store: SettingsStore + settings: AppSettings + storage: Storage + quarantine: QuarantineService + clamav: ClamAVService + auth: AuthService + session_key: str + account: dict | None + install_id: str + api: ApiClient | None = None + realtime: RealtimeProtection | None = None + offline: bool = False + extra: dict = field(default_factory=dict) + + def rebuild_api(self) -> ApiClient: + self.api = ApiClient( + self.settings.api_base_url, self.session_key, + device_name=self.auth.device_name, install_id=self.install_id) + return self.api + + @property + def user(self) -> dict: + return (self.account or {}).get("user") or {} + + @property + def subscription(self) -> dict: + return (self.account or {}).get("subscription") or {} + + @property + def user_name(self) -> str: + user = self.user + return str(user.get("name") or user.get("username") or + (str(user.get("email") or "Bolt user").split("@")[0])) + + @property + def user_email(self) -> str: + return str(self.user.get("email") or "") + + @property + def plan_label(self) -> str: + subscription = self.subscription + label = str(subscription.get("label") or "Free") + status = str(subscription.get("status") or "none").lower() + if status in ("active", "trialing"): + return label + return "Free" + + @property + def licensed(self) -> bool: + return bool((self.account or {}).get("features", {}).get("licensed")) + + def update_settings(self, settings: AppSettings) -> None: + self.settings = settings + if self.realtime is not None: + self.realtime.set_settings(settings) diff --git a/src/boltclam/logging_setup.py b/src/boltclam/logging_setup.py new file mode 100644 index 0000000..7ced0a3 --- /dev/null +++ b/src/boltclam/logging_setup.py @@ -0,0 +1,111 @@ +"""Logging setup and redaction shared by the app and diagnostics bundles.""" + +from __future__ import annotations + +import logging +import re +import sys +from logging.handlers import RotatingFileHandler +from pathlib import Path + +from . import paths +from .config import AppSettings + +LOG_NAME = "boltclam.log" +_MAX_BYTES = 2_000_000 +_BACKUPS = 3 + +_REDACTIONS = ( + (re.compile(r"bolt_live_[A-Za-z0-9_-]+"), "bolt_live_[redacted]"), + (re.compile(r"(?i)(authorization\s*:\s*bearer\s+)\S+"), r"\1[redacted]"), + (re.compile(r"(?i)(x-desk-api-key\s*:\s*)\S+"), r"\1[redacted]"), + (re.compile(r"(?i)(password\s*[=:]\s*)\S+"), r"\1[redacted]"), + (re.compile(r"(?i)(api[_-]?key\s*[=:]\s*)\S+"), r"\1[redacted]"), + (re.compile(r"(?i)(identity_token\s*[=:]\s*)\S+"), r"\1[redacted]"), +) +_EMAIL_RE = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b") + + +def redact(text: str) -> str: + value = str(text or "") + for pattern, replacement in _REDACTIONS: + value = pattern.sub(replacement, value) + return value + + +def redact_emails(text: str) -> str: + return _EMAIL_RE.sub("[email]", redact(text)) + + +class RedactionFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + try: + message = record.getMessage() + record.msg = redact(message) + record.args = () + except Exception: + pass + return True + + +def log_path() -> Path: + return paths.logs_dir() / LOG_NAME + + +def configure(settings: AppSettings) -> None: + """Install one rotating file handler, or remove it when logging is off.""" + paths.ensure_dirs() + root = logging.getLogger() + for handler in list(root.handlers): + if getattr(handler, "_boltclam_handler", False): + root.removeHandler(handler) + try: + handler.close() + except Exception: + pass + + level = logging.DEBUG if (settings.logging_enabled and settings.debug_logging) else logging.INFO + root.setLevel(level if settings.logging_enabled else logging.WARNING) + formatter = logging.Formatter( + "%(asctime)s %(levelname)-7s %(name)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + if settings.logging_enabled: + try: + file_handler = RotatingFileHandler( + log_path(), maxBytes=_MAX_BYTES, backupCount=_BACKUPS, encoding="utf-8") + file_handler.setFormatter(formatter) + file_handler.addFilter(RedactionFilter()) + file_handler._boltclam_handler = True # type: ignore[attr-defined] + root.addHandler(file_handler) + except OSError: + # A GUI app must not fail to start because its log directory is + # read-only; diagnostics will simply be absent. + pass + + if not getattr(sys, "frozen", False): + console = logging.StreamHandler() + console.setFormatter(formatter) + console.addFilter(RedactionFilter()) + console._boltclam_handler = True # type: ignore[attr-defined] + root.addHandler(console) + + for noisy in ("urllib3", "PyQt6", "PIL"): + logging.getLogger(noisy).setLevel(logging.WARNING) + + +def tail_lines(limit: int = 250) -> list[str]: + try: + with open(log_path(), "r", encoding="utf-8", errors="replace") as handle: + lines = handle.readlines() + return [line.rstrip("\n") for line in lines[-limit:]] + except OSError: + return [] + + +def rotated_logs() -> list[Path]: + base = log_path() + files = [base] + files.extend(base.with_name(f"{base.name}.{index}") for index in range(1, _BACKUPS + 1)) + return [path for path in files if path.is_file()] diff --git a/src/boltclam/logs.py b/src/boltclam/logs.py new file mode 100644 index 0000000..12dd59d --- /dev/null +++ b/src/boltclam/logs.py @@ -0,0 +1,184 @@ +"""Opt-in diagnostics bundle creation and upload. + +The bundle is intentionally narrow: app logs, redacted scan/quarantine +history, system summary and settings. It never includes the API key, +passwords, browser contents, or non-detection file contents. User-profile +prefixes are replaced with ``%USERPROFILE%`` before the zip is written. +""" + +from __future__ import annotations + +import json +import logging +import re +import shutil +import tempfile +import time +import uuid +import zipfile +from datetime import datetime, timezone +from pathlib import Path + +from . import __build_date__, __version__, paths +from .api_client import ApiClient +from .config import AppSettings +from .logging_setup import redact, redact_emails, rotated_logs +from .models import ClamAVInfo + +logger = logging.getLogger(__name__) + + +class DiagnosticsError(Exception): + pass + + +def _profile_prefixes() -> list[str]: + prefixes: list[str] = [] + for profile in paths.windows_profile_dirs(): + try: + prefixes.append(str(profile.resolve())) + except OSError: + prefixes.append(str(profile)) + prefixes.extend([str(Path.home()), "C:\\Users\\Public", "/home", "/Users"]) + return sorted({prefix for prefix in prefixes if prefix}, key=len, reverse=True) + + +def redact_paths(text: str) -> str: + value = str(text or "") + for prefix in _profile_prefixes(): + value = re.sub(re.escape(prefix), "%USERPROFILE%", value, flags=re.IGNORECASE) + return value + + +def sanitize_text(text: str) -> str: + return redact_emails(redact(redact_paths(text))) + + +def _sanitize_value(value, key: str = ""): + if isinstance(value, str): + # Emails are preserved in the manifest (the consent screen says the + # account email is included); log text goes through sanitize_text(), + # which does redact them. + return redact(redact_paths(value)) + if isinstance(value, list): + return [_sanitize_value(item, key) for item in value] + if isinstance(value, dict): + return {str(k): _sanitize_value(v, str(k)) for k, v in value.items()} + return value + + +def _read_log(path: Path, max_chars: int = 300_000) -> str: + try: + with open(path, "r", encoding="utf-8", errors="replace") as handle: + text = handle.read() + except OSError: + return "" + if len(text) > max_chars: + text = "[earlier log lines omitted]\n" + text[-max_chars:] + return sanitize_text(text) + + +def _cleanup_old_bundles(days: int = 14) -> None: + cutoff = time.time() - days * 86400 + try: + for entry in paths.diagnostics_dir().glob("BoltClam-diagnostics-*.zip"): + try: + if entry.stat().st_mtime < cutoff: + entry.unlink() + except OSError: + pass + except OSError: + pass + + +def build_bundle(settings: AppSettings, clamav: ClamAVInfo, *, + system_snapshot: dict | None = None, + account: dict | None = None, + realtime_roots: list[str] | None = None) -> Path: + paths.ensure_dirs() + _cleanup_old_bundles() + stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") + bundle_path = paths.diagnostics_dir() / f"BoltClam-diagnostics-{stamp}-{uuid.uuid4().hex[:6]}.zip" + temp_root = Path(tempfile.mkdtemp(prefix="boltclam-diagnostics-", dir=str(paths.temp_dir()))) + try: + manifest = { + "app": "BoltClam", + "version": __version__, + "build_date": __build_date__, + "generated_at": datetime.now(timezone.utc).isoformat( + timespec="seconds").replace("+00:00", "Z"), + "os": paths.os_label(), + "hostname": paths.hostname(), + "is_admin": paths.is_admin(), + "account": { + "email": (account or {}).get("user", {}).get("email", ""), + "user_id": (account or {}).get("user", {}).get("id", ""), + "subscription": (account or {}).get("subscription", {}), + }, + "clamav": { + "available": clamav.available, + "path": str(clamav.path) if clamav.path else "", + "engine": clamav.engine_version, + "db_version": clamav.db_version, + "db_date": clamav.db_date, + "db_age_days": clamav.db_age_days, + "error": clamav.error, + }, + "settings": settings.public_snapshot(), + "realtime_watch_roots": realtime_roots or [], + "notes": [ + "API keys, passwords and account tokens are redacted.", + "User profile prefixes are replaced with %USERPROFILE%.", + "Email addresses in logs are redacted; the account email is in this manifest.", + "No file contents are included.", + ], + } + (temp_root / "manifest.json").write_text( + json.dumps(_sanitize_value(manifest), indent=1), encoding="utf-8") + + logs = [] + for path in rotated_logs(): + text = _read_log(path) + if text: + logs.append(f"===== {path.name} =====\n{text}\n") + (temp_root / "boltclam-logs.txt").write_text( + sanitize_text("\n".join(logs) or "No app log was available."), + encoding="utf-8") + + if system_snapshot: + (temp_root / "system-summary.json").write_text( + json.dumps(_sanitize_value(system_snapshot), indent=1), encoding="utf-8") + + (temp_root / "README.txt").write_text( + "BoltClam diagnostics bundle\n" + "==========================\n\n" + "This archive was created by BoltClam after you enabled 'Send logs to Bolt'.\n" + "It contains application logs, settings, scan/quarantine summaries and a system\n" + "summary. API keys, passwords, tokens and file contents are not included.\n" + "User profile paths are replaced with %USERPROFILE%.\n", + encoding="utf-8") + + with zipfile.ZipFile(bundle_path, "w", zipfile.ZIP_DEFLATED) as archive: + for path in sorted(temp_root.iterdir()): + if path.is_file(): + archive.write(path, arcname=path.name) + logger.info("Diagnostics bundle written to %s", bundle_path) + return bundle_path + except OSError as exc: + raise DiagnosticsError(f"Could not create the diagnostics bundle: {exc}") from exc + finally: + shutil.rmtree(temp_root, ignore_errors=True) + + +def upload_bundle(api: ApiClient, bundle_path: Path, settings: AppSettings, + *, note: str = "") -> dict: + try: + result = api.upload_diagnostics(bundle_path, metadata={ + "channel": settings.update_channel, + "note": note[:400], + }) + except Exception as exc: + raise DiagnosticsError(str(exc)) from exc + if not isinstance(result, dict) or not result.get("id"): + raise DiagnosticsError("The Bolt service did not confirm the upload.") + return result diff --git a/src/boltclam/models.py b/src/boltclam/models.py new file mode 100644 index 0000000..18633e6 --- /dev/null +++ b/src/boltclam/models.py @@ -0,0 +1,191 @@ +"""Small typed records shared by the storage, scanner and UI layers.""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import datetime, timezone +from pathlib import Path + + +def now_iso() -> str: + return datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z") + + +@dataclass +class Detection: + path: str + signature: str + action: str = "detected" + quarantine_id: str | None = None + size: int = 0 + detected_at: str = "" + id: int | None = None + + def __post_init__(self) -> None: + if not self.detected_at: + self.detected_at = now_iso() + + +@dataclass +class ScanRecord: + id: int | None + started_at: str + finished_at: str | None + scope: str + status: str + paths: list[str] + scanned_files: int = 0 + infected_files: int = 0 + errors: int = 0 + engine_version: str = "" + db_version: str = "" + duration_seconds: float = 0.0 + cancelled: bool = False + detections: list[Detection] = field(default_factory=list) + + @property + def scope_label(self) -> str: + return "Full disk" if self.scope == "full" else "User homes" + + @property + def threat_count(self) -> int: + return max(self.infected_files, len(self.detections)) + + +@dataclass +class QuarantineItem: + id: str + original_path: str + quarantine_path: str + signature: str + sha256: str + size: int + created_at: str + status: str = "quarantined" + scan_id: int | None = None + + +@dataclass +class RealtimeEvent: + created_at: str + path: str + signature: str + action: str + detail: str = "" + id: int | None = None + + +@dataclass +class ClamAVInfo: + available: bool + path: Path | None = None + engine_version: str = "" + db_version: str = "" + db_path: Path | None = None + db_date: str = "" + db_age_days: float | None = None + error: str = "" + + @property + def db_fresh(self) -> bool: + return self.db_age_days is not None and self.db_age_days <= 7 + + @property + def summary(self) -> str: + if not self.available: + return self.error or "ClamAV not found" + bits = [self.engine_version or "ClamAV"] + if self.db_version: + bits.append(f"signatures {self.db_version}") + if self.db_age_days is not None: + bits.append(f"{self.db_age_days:.0f}d old") + return " · ".join(bits) + + +@dataclass +class UpdateAsset: + id: str + name: str + size: int + content_type: str + download_url: str + + +@dataclass +class UpdateRelease: + id: str + tag: str + name: str + notes: str + prerelease: bool + published_at: str + html_url: str + assets: list[UpdateAsset] = field(default_factory=list) + + +@dataclass +class UpdateFeed: + channel: str + latest: UpdateRelease | None + releases: list[UpdateRelease] + checked_at: str + + @classmethod + def from_api(cls, payload: dict) -> "UpdateFeed": + def release_from(item: dict) -> UpdateRelease: + assets = [ + UpdateAsset( + id=str(asset.get("id") or ""), + name=str(asset.get("name") or "download"), + size=int(asset.get("size") or 0), + content_type=str(asset.get("content_type") or ""), + download_url=str(asset.get("download_url") or ""), + ) + for asset in (item.get("assets") or []) + if isinstance(asset, dict) + ] + return UpdateRelease( + id=str(item.get("id") or ""), + tag=str(item.get("tag") or ""), + name=str(item.get("name") or item.get("tag") or ""), + notes=str(item.get("notes") or ""), + prerelease=bool(item.get("prerelease")), + published_at=str(item.get("published_at") or item.get("created_at") or ""), + html_url=str(item.get("html_url") or ""), + assets=assets, + ) + + releases = [release_from(item) for item in (payload.get("releases") or []) + if isinstance(item, dict)] + latest_raw = payload.get("latest") + latest = release_from(latest_raw) if isinstance(latest_raw, dict) else ( + releases[0] if releases else None) + return cls( + channel=str(payload.get("channel") or "release"), + latest=latest, + releases=releases, + checked_at=str(payload.get("checked_at") or now_iso()), + ) + + +def display_name(path: str) -> str: + """Basename for a Windows or POSIX path, independent of host OS.""" + text = str(path or "") + name = Path(text.replace("\\", "/")).name + return name or text + + +def human_scope(scope: str) -> str: + return "Full disk" if scope == "full" else "User homes" + + +def human_action(action: str) -> str: + return { + "detected": "Detected", + "quarantined": "Quarantined", + "deleted": "Deleted", + "failed": "Action failed", + "ignored": "Ignored", + "allowed": "Allowed", + "blocked": "Blocked", + }.get(action, action.replace("_", " ").title()) diff --git a/src/boltclam/paths.py b/src/boltclam/paths.py new file mode 100644 index 0000000..bd2396a --- /dev/null +++ b/src/boltclam/paths.py @@ -0,0 +1,284 @@ +"""Filesystem locations and Windows helpers used across BoltClam. + +Everything is resolved per call so ``BOLTCLAM_DATA_DIR`` / ``BOLTCLAM_CONFIG_DIR`` +can redirect tests and the portable build without importing state at startup. +""" + +from __future__ import annotations + +import ctypes +import os +import shutil +import sys +from pathlib import Path + +APP_DIR_NAME = "BoltClam" + + +def is_windows() -> bool: + return os.name == "nt" + + +def _env_path(name: str) -> Path | None: + value = os.environ.get(name, "").strip() + return Path(os.path.expandvars(os.path.expanduser(value))) if value else None + + +def config_dir() -> Path: + override = _env_path("BOLTCLAM_CONFIG_DIR") + if override: + return override + if is_windows(): + base = Path(os.environ.get("APPDATA") or (Path.home() / "AppData" / "Roaming")) + else: + base = Path(os.environ.get("XDG_CONFIG_HOME") or (Path.home() / ".config")) + return base / APP_DIR_NAME + + +def data_dir() -> Path: + override = _env_path("BOLTCLAM_DATA_DIR") + if override: + return override + if is_windows(): + base = Path(os.environ.get("LOCALAPPDATA") or (Path.home() / "AppData" / "Local")) + else: + base = Path(os.environ.get("XDG_DATA_HOME") or (Path.home() / ".local" / "share")) + return base / APP_DIR_NAME + + +def logs_dir() -> Path: + return data_dir() / "logs" + + +def quarantine_dir() -> Path: + return data_dir() / "quarantine" + + +def updates_dir() -> Path: + return data_dir() / "updates" + + +def diagnostics_dir() -> Path: + return data_dir() / "diagnostics" + + +def temp_dir() -> Path: + return data_dir() / "tmp" + + +def clamav_db_dir() -> Path: + """App-managed ClamAV signature database. + + freshclam can write here without administrator rights, and clamscan is + pointed at it with ``--database``. When it is empty we fall back to the + database shipped beside clamscan.exe. + """ + return data_dir() / "clamav-db" + + +def db_path() -> Path: + return data_dir() / "boltclam.db" + + +def settings_path() -> Path: + return config_dir() / "settings.json" + + +def account_cache_path() -> Path: + return config_dir() / "account.json" + + +def credentials_path() -> Path: + return config_dir() / "credentials.dat" + + +def resource_dir() -> Path: + override = _env_path("BOLTCLAM_RESOURCES") + if override: + return override + # PyInstaller extracts datas into _MEIPASS. The portable runtime keeps + # resources next to the app package under runtime/resources. + meipass = getattr(sys, "_MEIPASS", None) + if meipass: + return Path(meipass) / "resources" + return Path(__file__).resolve().parents[2] / "resources" + + +def ensure_dirs() -> None: + for path in (config_dir(), data_dir(), logs_dir(), quarantine_dir(), + updates_dir(), diagnostics_dir(), temp_dir(), clamav_db_dir()): + path.mkdir(parents=True, exist_ok=True) + if not is_windows(): + for path in (config_dir(), data_dir(), logs_dir(), quarantine_dir()): + try: + os.chmod(path, 0o700) + except OSError: + pass + + +def is_admin() -> bool: + if not is_windows(): + try: + return os.geteuid() == 0 + except AttributeError: + return False + try: + return bool(ctypes.windll.shell32.IsUserAnAdmin()) + except Exception: + return False + + +def os_label() -> str: + if is_windows(): + version = sys.getwindowsversion() + return f"Windows {version.major}.{version.minor} (build {version.build})" + return f"{os.name} {sys.platform}" + + +def hostname() -> str: + import socket + + try: + return socket.gethostname() or "Windows PC" + except Exception: + return "Windows PC" + + +def open_path(path: Path) -> bool: + """Open a file/folder in the OS shell. Returns False instead of raising.""" + try: + if is_windows(): + os.startfile(str(path)) # type: ignore[attr-defined] + elif sys.platform == "darwin": + import subprocess + + subprocess.Popen(["open", str(path)]) + else: + import subprocess + + subprocess.Popen(["xdg-open", str(path)]) + return True + except Exception: + return False + + +def free_space(path: Path) -> int: + try: + return shutil.disk_usage(path).free + except OSError: + return 0 + + +def windows_profile_dirs() -> list[Path]: + """Every Windows user profile path we can discover. + + Registry ``ProfileList`` is authoritative and includes profiles outside + C:\\Users. When it cannot be read (ordinary non-admin process), fall back + to the children of C:\\Users. Public is included deliberately: downloads + there belong to every local account. + """ + profiles: list[Path] = [] + if is_windows(): + try: + import winreg + + key_path = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key_path) as key: + index = 0 + while True: + try: + subkey_name = winreg.EnumKey(key, index) + except OSError: + break + index += 1 + try: + with winreg.OpenKey(key, subkey_name) as subkey: + value, _kind = winreg.QueryValueEx(subkey, "ProfileImagePath") + expanded = os.path.expandvars(str(value or "")) + if expanded: + profiles.append(Path(expanded)) + except OSError: + continue + except Exception: + pass + users = Path(os.environ.get("SystemDrive", "C:") + "\\Users") + if users.is_dir(): + try: + for child in users.iterdir(): + if child.is_dir(): + profiles.append(child) + except OSError: + pass + public = users / "Public" + if public.is_dir(): + profiles.append(public) + else: + home = Path.home() + profiles.append(home) + if Path("/home").is_dir(): + try: + for child in Path("/home").iterdir(): + if child.is_dir(): + profiles.append(child) + except OSError: + pass + + home = Path.home() + profiles.append(home) + seen: set[str] = set() + result: list[Path] = [] + for profile in profiles: + try: + resolved = profile.expanduser().resolve() + except OSError: + continue + key = os.path.normcase(str(resolved)) + if key in seen or not resolved.is_dir(): + continue + seen.add(key) + result.append(resolved) + return result + + +def browser_download_roots() -> list[Path]: + """Default Downloads folders for every local profile we can read.""" + roots: list[Path] = [] + for profile in windows_profile_dirs(): + downloads = profile / "Downloads" + if downloads.is_dir(): + roots.append(downloads) + return roots + + +def fixed_drive_roots() -> list[Path]: + """Roots of fixed local disks (Windows), or the filesystem root elsewhere.""" + if not is_windows(): + return [Path("/")] if Path("/").is_dir() else [] + roots: list[Path] = [] + try: + kernel32 = ctypes.windll.kernel32 + mask = kernel32.GetLogicalDrives() + for index in range(26): + if not (mask & (1 << index)): + continue + root = f"{chr(ord('A') + index)}:\\" + # DRIVE_FIXED == 3; network/removable drives are skipped. + if kernel32.GetDriveTypeW(ctypes.c_wchar_p(root)) == 3: + path = Path(root) + if path.is_dir(): + roots.append(path) + except Exception: + system_drive = os.environ.get("SystemDrive", "C:") + path = Path(system_drive + "\\") + if path.is_dir(): + roots.append(path) + return roots + + +def is_under(path: Path, parent: Path) -> bool: + try: + path = path.resolve() + parent = parent.resolve() + except OSError: + return False + return path == parent or parent in path.parents diff --git a/src/boltclam/quarantine.py b/src/boltclam/quarantine.py new file mode 100644 index 0000000..c2efb04 --- /dev/null +++ b/src/boltclam/quarantine.py @@ -0,0 +1,240 @@ +"""Quarantine: move a detected file somewhere inert, record it, restore or delete it. + +A quarantine file is renamed with a ``.quarantine`` suffix (so Windows has +no executable association), made read-only where possible, and paired with +a JSON sidecar. The SQLite row is the source of truth for the UI; the +sidecar survives a database rebuild. +""" + +from __future__ import annotations + +import hashlib +import json +import logging +import os +import re +import shutil +import stat +import threading +import time +from pathlib import Path + +from . import paths +from .models import QuarantineItem, now_iso +from .storage import Storage, StorageError + +logger = logging.getLogger(__name__) + + +class QuarantineError(Exception): + pass + + +def hash_file(path: Path, chunk_size: int = 1024 * 1024) -> str: + digest = hashlib.sha256() + with open(path, "rb") as handle: + while True: + chunk = handle.read(chunk_size) + if not chunk: + break + digest.update(chunk) + return digest.hexdigest() + + +def delete_file(path: Path) -> None: + """Delete one file, clearing the Windows read-only bit if necessary. + + Directories are refused: a detection names a file, and recursively + deleting a folder on the strength of one signature would be far more + destructive than the finding warrants. + """ + path = Path(path) + if path.is_symlink(): + path.unlink() + return + if path.is_dir(): + raise QuarantineError("Refusing to delete a directory.") + try: + path.unlink() + except PermissionError: + try: + os.chmod(path, stat.S_IWRITE | stat.S_IREAD) + path.unlink() + except OSError as exc: + raise QuarantineError(f"Could not delete {path.name}: {exc}") from exc + except FileNotFoundError: + return + except OSError as exc: + raise QuarantineError(f"Could not delete {path.name}: {exc}") from exc + + +def _hide_on_windows(path: Path) -> None: + if os.name != "nt": + try: + os.chmod(path, 0o600) + except OSError: + pass + return + try: + import ctypes + + hidden = 0x2 + attributes = ctypes.windll.kernel32.GetFileAttributesW(str(path)) + if attributes != -1: + ctypes.windll.kernel32.SetFileAttributesW(str(path), attributes | hidden) + except Exception: + pass + + +class QuarantineService: + def __init__(self, storage: Storage, root: Path | None = None) -> None: + self.storage = storage + self.root = Path(root) if root else paths.quarantine_dir() + self.root.mkdir(parents=True, exist_ok=True) + self._lock = threading.RLock() + + def _metadata_path(self, item_id: str) -> Path: + return self.root / f"{item_id}.json" + + def _write_metadata(self, item: QuarantineItem, reason: str = "") -> None: + payload = { + "id": item.id, + "original_path": item.original_path, + "quarantine_path": item.quarantine_path, + "signature": item.signature, + "sha256": item.sha256, + "size": item.size, + "created_at": item.created_at, + "status": item.status, + "scan_id": item.scan_id, + "reason": reason, + } + temp = self._metadata_path(item.id).with_suffix(".json.tmp") + temp.write_text(json.dumps(payload, indent=1), encoding="utf-8") + os.replace(temp, self._metadata_path(item.id)) + + def _update_metadata_status(self, item: QuarantineItem) -> None: + metadata = self._metadata_path(item.id) + try: + payload = json.loads(metadata.read_text(encoding="utf-8")) + except (OSError, ValueError): + payload = {} + if not isinstance(payload, dict): + payload = {} + payload.update({ + "id": item.id, "original_path": item.original_path, + "quarantine_path": item.quarantine_path, "signature": item.signature, + "sha256": item.sha256, "size": item.size, "created_at": item.created_at, + "status": item.status, "scan_id": item.scan_id, + }) + try: + metadata.write_text(json.dumps(payload, indent=1), encoding="utf-8") + except OSError: + logger.warning("Could not update quarantine metadata for %s", item.id) + + def quarantine_file(self, path: Path, signature: str, *, + scan_id: int | None = None, reason: str = "") -> QuarantineItem: + path = Path(path) + if path.is_symlink(): + raise QuarantineError("Refusing to quarantine a symbolic link.") + if not path.is_file(): + raise QuarantineError("The detected file no longer exists.") + try: + size = path.stat().st_size + digest = hash_file(path) + except OSError as exc: + raise QuarantineError(f"Could not read the detected file: {exc}") from exc + + item_id = f"Q-{time.strftime('%Y%m%dT%H%M%S')}-{digest[:10]}" + safe_name = re.sub(r"[^A-Za-z0-9._-]", "_", path.name)[:80] or "detected" + destination = self.root / f"{item_id}-{safe_name}.quarantine" + with self._lock: + if destination.exists(): + raise QuarantineError("A quarantine file with that name already exists.") + try: + shutil.move(str(path), str(destination)) + except OSError as exc: + raise QuarantineError(f"Could not move the file into quarantine: {exc}") from exc + _hide_on_windows(destination) + item = QuarantineItem( + id=item_id, + original_path=str(path), + quarantine_path=str(destination), + signature=signature or "Unknown", + sha256=digest, + size=size, + created_at=now_iso(), + status="quarantined", + scan_id=scan_id, + ) + try: + self.storage.add_quarantine(item) + except StorageError: + # The database is what the UI reads. If it cannot record the + # move, put the file back rather than leaving an untracked + # quarantined item. + try: + shutil.move(str(destination), str(path)) + except OSError: + logger.exception("Quarantine rollback failed; file remains at %s", destination) + raise QuarantineError( + "The file was moved but could not be recorded; it was restored.") + try: + self._write_metadata(item, reason=reason) + except OSError: + # The SQLite row is authoritative; a missing sidecar is an + # inconvenience, not a reason to undo an otherwise successful + # quarantine. + logger.warning("Quarantine metadata could not be written for %s", + item.id, exc_info=True) + logger.info("Quarantined %s (%s)", path, item_id) + return item + + def restore(self, item_id: str, destination: Path | None = None) -> Path: + item = self.storage.get_quarantine(item_id) + if item is None or item.status != "quarantined": + raise QuarantineError("That quarantined file is no longer available.") + source = Path(item.quarantine_path) + if not source.is_file(): + raise QuarantineError("The quarantined file is missing from disk.") + target = Path(destination) if destination else Path(item.original_path) + with self._lock: + if target.exists(): + raise QuarantineError(f"A file already exists at {target}.") + try: + target.parent.mkdir(parents=True, exist_ok=True) + shutil.move(str(source), str(target)) + except OSError as exc: + raise QuarantineError(f"Could not restore the file: {exc}") from exc + item.status = "restored" + self.storage.set_quarantine_status(item.id, "restored") + self._update_metadata_status(item) + logger.info("Restored %s to %s", item.id, target) + return target + + def delete(self, item_id: str) -> None: + item = self.storage.get_quarantine(item_id) + if item is None: + raise QuarantineError("That quarantined file was not found.") + source = Path(item.quarantine_path) + with self._lock: + try: + delete_file(source) + except QuarantineError: + raise + item.status = "deleted" + self.storage.set_quarantine_status(item.id, "deleted") + self._update_metadata_status(item) + logger.info("Deleted quarantined item %s", item.id) + + def delete_original(self, path: Path) -> None: + delete_file(Path(path)) + + def delete_missing_or_file(self, path: Path) -> None: + try: + delete_file(Path(path)) + except QuarantineError: + raise + + def open_folder(self) -> bool: + return paths.open_path(self.root) diff --git a/src/boltclam/realtime.py b/src/boltclam/realtime.py new file mode 100644 index 0000000..262e4c9 --- /dev/null +++ b/src/boltclam/realtime.py @@ -0,0 +1,580 @@ +"""Browser-download watcher and the real-time scan-before-open gate. + +Watches the default Downloads folder of every readable Windows profile, +plus any custom download directory recorded by Chrome, Edge, Brave, Vivaldi, +Opera or Firefox. A completed download is copied while an exclusive handle +is held (where Windows allows it), scanned with ClamAV, and quarantined or +deleted before the handle is released. +""" + +from __future__ import annotations + +import json +import logging +import os +import re +import shutil +import subprocess +import time +import uuid +from pathlib import Path +from typing import Iterable + +from PyQt6.QtCore import QObject, QRunnable, QThread, QThreadPool, pyqtSignal + +from . import paths +from .clamav import ( + ClamAVService, + hidden_kwargs, + parse_clamscan_line, + parse_extra_options, +) +from .config import AppSettings +from .models import RealtimeEvent, now_iso +from .quarantine import QuarantineError, QuarantineService, delete_file +from .storage import Storage +from .winlock import LockError, copy_for_scan + +logger = logging.getLogger(__name__) + +_TEMP_SUFFIXES = ( + ".crdownload", ".part", ".partial", ".download", ".opdownload", ".tmp", + ".temp", ".filepart", ".aria2", ".td", ".!qb", ".!ut", ".partial~", +) +_JS_ESCAPE = re.compile(r"\\(.)") +_FIREFOX_DIR_RE = re.compile( + r'user_pref\("browser\.download\.dir",\s*"((?:\\.|[^"\\])*)"\)\s*;') + + +def _js_unescape(value: str) -> str: + def replace(match: re.Match) -> str: + char = match.group(1) + return {"n": "\n", "r": "\r", "t": "\t", '"': '"', "\\": "\\"}.get(char, char) + + return _JS_ESCAPE.sub(replace, value) + + +def _existing_dir(value: str | Path | None) -> Path | None: + if not value: + return None + text = os.path.expandvars(os.path.expanduser(str(value).strip().strip('"'))) + if not text: + return None + try: + path = Path(text) + if path.is_dir() and os.access(path, os.R_OK | os.X_OK): + return path + except OSError: + return None + return None + + +def _chromium_preference_files() -> list[Path]: + files: list[Path] = [] + local = os.environ.get("LOCALAPPDATA") + roaming = os.environ.get("APPDATA") + bases: list[Path] = [] + if local: + bases.extend([ + Path(local) / "Google" / "Chrome" / "User Data", + Path(local) / "Microsoft" / "Edge" / "User Data", + Path(local) / "BraveSoftware" / "Brave-Browser" / "User Data", + Path(local) / "Vivaldi" / "User Data", + Path(local) / "Chromium" / "User Data", + ]) + if roaming: + bases.append(Path(roaming) / "Opera Software" / "Opera Stable") + if os.name != "nt": + home = Path.home() + bases.extend([ + home / ".config" / "google-chrome", + home / ".config" / "chromium", + home / ".config" / "microsoft-edge", + home / ".config" / "BraveSoftware" / "Brave-Browser", + ]) + for base in bases: + try: + direct = base / "Preferences" + if direct.is_file(): + files.append(direct) + if base.is_dir(): + for child in base.iterdir(): + if child.is_dir() and (child.name == "Default" + or child.name.startswith("Profile ") + or child.name == "Guest Profile"): + pref = child / "Preferences" + if pref.is_file(): + files.append(pref) + except OSError: + continue + return files + + +def _chromium_download_dirs() -> list[Path]: + roots: list[Path] = [] + home = str(Path.home()) + for pref in _chromium_preference_files(): + try: + data = json.loads(pref.read_text(encoding="utf-8", errors="replace")) + download = data.get("download") if isinstance(data, dict) else None + value = download.get("default_directory") if isinstance(download, dict) else None + if not value: + continue + text = str(value).replace("${user_home}", home) + resolved = _existing_dir(text) + if resolved: + roots.append(resolved) + except (OSError, ValueError): + continue + return roots + + +def _firefox_download_dirs() -> list[Path]: + roots: list[Path] = [] + bases: list[Path] = [] + roaming = os.environ.get("APPDATA") + if roaming: + bases.append(Path(roaming) / "Mozilla" / "Firefox" / "Profiles") + bases.append(Path.home() / ".mozilla" / "firefox") + for base in bases: + try: + if not base.is_dir(): + continue + for profile in base.iterdir(): + prefs = profile / "prefs.js" + if not profile.is_dir() or not prefs.is_file(): + continue + text = prefs.read_text(encoding="utf-8", errors="replace") + for match in _FIREFOX_DIR_RE.finditer(text): + resolved = _existing_dir(_js_unescape(match.group(1))) + if resolved: + roots.append(resolved) + except OSError: + continue + return roots + + +def browser_download_roots(extra: Iterable[str] = ()) -> list[Path]: + """Every download directory we can justify watching, deduplicated.""" + roots: list[Path] = [] + roots.extend(paths.browser_download_roots()) + roots.extend(_chromium_download_dirs()) + roots.extend(_firefox_download_dirs()) + for entry in extra: + resolved = _existing_dir(entry) + if resolved: + roots.append(resolved) + result: list[Path] = [] + seen: set[str] = set() + for root in roots: + try: + key = os.path.normcase(str(root.resolve())) + except OSError: + continue + if key in seen: + continue + seen.add(key) + result.append(root) + return result[:50] + + +def _excluded_roots() -> list[str]: + excluded = [paths.quarantine_dir(), paths.logs_dir(), paths.updates_dir(), + paths.diagnostics_dir(), paths.temp_dir(), paths.clamav_db_dir()] + result: list[str] = [] + for root in excluded: + try: + result.append(os.path.normcase(str(root.resolve())) + os.sep) + except OSError: + continue + return result + + +def _is_temp_name(name: str) -> bool: + lowered = name.lower() + return lowered.endswith(_TEMP_SUFFIXES) or lowered.endswith("~") + + +def _iter_download_files(root: Path) -> Iterable[Path]: + """Files directly in a download root and one level below it.""" + try: + entries = list(os.scandir(root)) + except OSError: + return + for entry in entries: + try: + if entry.name.startswith(".") or entry.name.startswith("$"): + continue + if entry.is_file(follow_symlinks=False): + yield Path(entry.path) + elif entry.is_dir(follow_symlinks=False) and entry.name.lower() not in ( + "node_modules", "system volume information", "recycle.bin"): + try: + for child in os.scandir(entry.path): + if child.is_file(follow_symlinks=False) and not child.name.startswith("."): + yield Path(child.path) + except OSError: + continue + except OSError: + continue + + +def snapshot(roots: Iterable[Path]) -> dict[str, tuple[int, int, str]]: + excluded = _excluded_roots() + result: dict[str, tuple[int, int, str]] = {} + for root in roots: + for path in _iter_download_files(root): + if _is_temp_name(path.name): + continue + try: + key = os.path.normcase(str(path.resolve())) + except OSError: + continue + if any(key.startswith(prefix) for prefix in excluded): + continue + try: + stat = path.stat() + except OSError: + continue + if stat.st_size < 0: + continue + result[key] = (int(stat.st_size), int(stat.st_mtime_ns), str(path)) + return result + + +class DownloadWatcher(QThread): + file_ready = pyqtSignal(str) + watch_status = pyqtSignal(str) + roots_changed = pyqtSignal(list) + + def __init__(self, extra_dirs: Iterable[str] = (), interval: float = 1.5, + parent=None) -> None: + super().__init__(parent) + self.extra_dirs = list(extra_dirs) + self.interval = max(0.8, float(interval)) + self._stop = None + + def stop(self) -> None: + if self._stop is not None: + self._stop.set() + + def run(self) -> None: + import threading + + stop = threading.Event() + self._stop = stop + roots = browser_download_roots(self.extra_dirs) + self.roots_changed.emit([str(root) for root in roots]) + initial = snapshot(roots) + known = set(initial) + candidates: dict[str, dict] = {} + last_refresh = time.monotonic() + warned_empty = False + logger.info("Real-time watcher started on %s folder(s)", len(roots)) + while not stop.is_set(): + now = time.monotonic() + if now - last_refresh >= 30: + refreshed = browser_download_roots(self.extra_dirs) + if {str(p) for p in refreshed} != {str(p) for p in roots}: + roots = refreshed + for path in snapshot(roots): + if path not in known and path not in candidates: + known.add(path) + self.roots_changed.emit([str(root) for root in roots]) + last_refresh = now + current = snapshot(roots) + for path, (size, mtime, actual_path) in current.items(): + signature = (size, mtime) + if path in known: + continue + candidate = candidates.get(path) + if candidate is None: + candidates[path] = {"signature": signature, "stable": 0} + elif candidate["signature"] == signature: + candidate["stable"] += 1 + if candidate["stable"] >= 1: + candidates.pop(path, None) + known.add(path) + self.file_ready.emit(actual_path) + else: + candidate["signature"] = signature + candidate["stable"] = 0 + for path in list(candidates): + if path not in current: + candidates.pop(path, None) + for path in list(known): + if path not in current: + known.discard(path) + if not roots and not warned_empty: + warned_empty = True + self.watch_status.emit("No browser download folders found") + elif roots: + self.watch_status.emit(f"Watching {len(roots)} download folder(s)") + stop.wait(self.interval) + logger.info("Real-time watcher stopped") + + +class RealtimeScanSignals(QObject): + finished = pyqtSignal(str, object) + alert = pyqtSignal(str, str, str) + + +class RealtimeScanTask(QRunnable): + def __init__(self, path: Path, settings: AppSettings, clamav: ClamAVService, + storage: Storage, quarantine: QuarantineService) -> None: + super().__init__() + self.path = Path(path) + self.settings = settings + self.clamav = clamav + self.storage = storage + self.quarantine = quarantine + self.signals = RealtimeScanSignals() + self.setAutoDelete(True) + + def run(self) -> None: + temp_root = paths.temp_dir() / "realtime" / uuid.uuid4().hex + temp_root.mkdir(parents=True, exist_ok=True) + sample = temp_root / "download-sample.bin" + locker = None + exclusive = False + event: RealtimeEvent | None = None + try: + locker, exclusive = copy_for_scan(self.path, sample) + detections, scan_error = self._scan(sample) + detail = "Locked while scanning" if exclusive else "File was in use; scanned a copy" + if detections: + signature = detections[0] + action = self.settings.auto_action + if action == "delete": + if locker is not None and locker.mark_delete_on_close(): + action = "deleted" + detail += "; deleted on close" + else: + if locker is not None: + locker.close() + locker = None + try: + delete_file(self.path) + action = "deleted" + except QuarantineError as exc: + action = "failed" + detail += f"; delete failed: {exc}" + elif action == "quarantine": + if locker is not None: + locker.close() + locker = None + try: + item = self.quarantine.quarantine_file( + self.path, signature, reason="Browser download") + action = "quarantined" + detail += f"; quarantine {item.id}" + except QuarantineError as exc: + action = "failed" + detail += f"; quarantine failed: {exc}" + else: + action = "detected" + detail += "; left in place (notify only)" + event = RealtimeEvent( + created_at=now_iso(), path=str(self.path), signature=signature, + action=action, detail=detail) + self.storage.add_realtime_event(event) + title = { + "quarantined": "Download quarantined", + "deleted": "Download deleted", + "detected": "Malware detected in a download", + "failed": "Detection needs your attention", + }.get(action, "Malware detected") + self.signals.alert.emit( + title, f"{self.path.name}\n{signature}\n{detail}", action) + elif scan_error: + event = RealtimeEvent( + created_at=now_iso(), path=str(self.path), signature="", + action="error", detail=f"{scan_error}; {detail}") + self.storage.add_realtime_event(event) + self.signals.alert.emit( + "Could not scan a download", + f"{self.path.name}\n{scan_error}", "error") + except (LockError, OSError) as exc: + logger.warning("Real-time scan could not read %s: %s", self.path, exc) + event = RealtimeEvent( + created_at=now_iso(), path=str(self.path), signature="", + action="error", detail=f"Could not read the download: {exc}") + try: + self.storage.add_realtime_event(event) + except Exception: + pass + self.signals.alert.emit("Could not scan a download", + f"{self.path.name}\n{exc}", "error") + except Exception as exc: + logger.exception("Real-time scan failed for %s", self.path) + event = RealtimeEvent( + created_at=now_iso(), path=str(self.path), signature="", + action="error", detail=f"Real-time scan failed: {exc}") + try: + self.storage.add_realtime_event(event) + except Exception: + pass + self.signals.alert.emit("Real-time protection error", + f"{self.path.name}\n{exc}", "error") + finally: + if locker is not None: + try: + locker.close() + except Exception: + pass + shutil.rmtree(temp_root, ignore_errors=True) + self.signals.finished.emit(str(self.path), event) + + def _scan(self, sample: Path) -> tuple[list[str], str]: + info = self.clamav.info() + if not info.available: + return [], info.error or "ClamAV is not available" + try: + extra = parse_extra_options(self.settings.extra_options) + except ValueError: + extra = [] + try: + args = self.clamav.scan_arguments([sample], extra, exclude_temp=False) + except Exception as exc: + return [], str(exc) + detections: list[str] = [] + skipped = "" + try: + process = subprocess.Popen( + args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, encoding="utf-8", errors="replace", bufsize=1, + cwd=str(info.path.parent) if info.path else None, + env={**os.environ, "LC_ALL": "C", "LANG": "C"}, **hidden_kwargs(), + ) + assert process.stdout is not None + deadline = time.monotonic() + 300 + while True: + if time.monotonic() > deadline: + process.kill() + return detections, "Scan timed out" + line = process.stdout.readline() + if not line: + if process.poll() is not None: + break + continue + parsed = parse_clamscan_line(line) + if not parsed: + continue + if parsed.get("kind") == "detection": + detections.append(str(parsed.get("signature") or "Unknown")) + elif parsed.get("kind") == "skipped": + skipped = str(parsed.get("reason") or "skipped") + code = process.wait(timeout=20) + if code not in (0, 1) and not detections: + return detections, skipped or f"clamscan exited with code {code}" + if skipped and not detections: + return detections, skipped + return detections, "" + except (OSError, subprocess.SubprocessError) as exc: + return detections, str(exc) + + +class RealtimeProtection(QObject): + event_created = pyqtSignal(object) + status_changed = pyqtSignal(str) + alert = pyqtSignal(str, str, str) + + def __init__(self, clamav: ClamAVService, storage: Storage, + quarantine: QuarantineService, settings: AppSettings, + parent=None) -> None: + super().__init__(parent) + self.clamav = clamav + self.storage = storage + self.quarantine = quarantine + self.settings = settings + self._watcher: DownloadWatcher | None = None + self._pool = QThreadPool(self) + self._pool.setMaxThreadCount(2) + self._inflight: set[str] = set() + self._tasks: dict[str, RealtimeScanTask] = {} + self._roots: list[str] = [] + self._warned_missing = False + + @property + def running(self) -> bool: + return bool(self._watcher is not None and self._watcher.isRunning()) + + @property + def watched_roots(self) -> list[str]: + return list(self._roots) + + def set_settings(self, settings: AppSettings) -> None: + self.settings = settings + + def start(self) -> None: + self.stop() + self._warned_missing = False + if not self.settings.realtime_enabled: + self.status_changed.emit("Real-time protection is off") + return + if not self.clamav.info().available: + self.status_changed.emit("Real-time protection needs ClamAV") + return + watcher = DownloadWatcher(self.settings.extra_watch_dirs) + watcher.file_ready.connect(self._on_file) + watcher.watch_status.connect(self.status_changed) + watcher.roots_changed.connect(self._on_roots) + self._watcher = watcher + watcher.start() + self.status_changed.emit("Starting real-time protection…") + + def stop(self) -> None: + watcher = self._watcher + self._watcher = None + if watcher is not None: + watcher.stop() + watcher.wait(4000) + self._pool.waitForDone(5000) + + def _on_roots(self, roots: list) -> None: + self._roots = [str(root) for root in roots] + + def _on_file(self, path: str) -> None: + if not self.settings.realtime_enabled or not path: + return + if path in self._inflight: + return + try: + size = Path(path).stat().st_size + except OSError: + return + max_bytes = int(os.environ.get("BOLTCLAM_REALTIME_MAX_MB", "1024")) * 1024 * 1024 + if size > max_bytes: + event = RealtimeEvent( + created_at=now_iso(), path=path, signature="", + action="skipped", + detail=f"File is larger than the {max_bytes // (1024 * 1024)} MB real-time limit") + try: + self.storage.add_realtime_event(event) + except Exception: + pass + self.event_created.emit(event) + self.alert.emit("Large download not scanned", + f"{Path(path).name}\nIt exceeds the real-time scan size limit.", + "warning") + return + if not self.clamav.info().available: + if not self._warned_missing: + self._warned_missing = True + self.alert.emit("Real-time protection is not scanning", + "ClamAV is not available. Set its install folder in Settings.", + "error") + return + self._inflight.add(path) + task = RealtimeScanTask(Path(path), self.settings, self.clamav, + self.storage, self.quarantine) + self._tasks[path] = task + task.signals.finished.connect(self._on_finished) + task.signals.alert.connect(self.alert) + self._pool.start(task) + + def _on_finished(self, path: str, event) -> None: + self._inflight.discard(path) + self._tasks.pop(path, None) + if event is not None: + self.event_created.emit(event) diff --git a/src/boltclam/scanner.py b/src/boltclam/scanner.py new file mode 100644 index 0000000..442e8c5 --- /dev/null +++ b/src/boltclam/scanner.py @@ -0,0 +1,347 @@ +"""Background scan and definition-update workers. + +The scan worker owns no UI objects. It streams clamscan output, records +every detection before attempting remediation, and always finishes the +database row even when the process fails or the user cancels. +""" + +from __future__ import annotations + +import logging +import os +import subprocess +import time +from pathlib import Path + +from PyQt6.QtCore import QThread, pyqtSignal + +from . import paths +from .clamav import ( + ClamAVError, + ClamAVService, + build_freshclam_args, + hidden_kwargs, + parse_clamscan_line, + parse_extra_options, + scan_targets, +) +from .config import AppSettings +from .models import Detection, ScanRecord +from .quarantine import QuarantineError, QuarantineService +from .storage import Storage, StorageError + +logger = logging.getLogger(__name__) + + +class ScanWorker(QThread): + started_scan = pyqtSignal(int, list) + progress = pyqtSignal(str, int) + detection_found = pyqtSignal(object) + log_line = pyqtSignal(str) + finished_scan = pyqtSignal(object) + failed = pyqtSignal(str) + status = pyqtSignal(str) + + def __init__(self, clamav: ClamAVService, storage: Storage, + quarantine: QuarantineService, settings: AppSettings, + scope: str, parent=None) -> None: + super().__init__(parent) + self.clamav = clamav + self.storage = storage + self.quarantine = quarantine + self.settings = settings + self.scope = "full" if scope == "full" else "homes" + self._cancel = False + self._process: subprocess.Popen | None = None + self._last_progress = 0.0 + + def cancel(self) -> None: + self._cancel = True + process = self._process + if process is not None and process.poll() is None: + try: + process.terminate() + except OSError: + pass + + def run(self) -> None: # noqa: C901 - one linear process lifecycle + info = self.clamav.info(force=True) + if not info.available or info.path is None: + self.failed.emit(info.error or "ClamAV is not configured.") + return + targets = scan_targets(self.scope) + if not targets: + self.failed.emit("No scan targets were found for that scope.") + return + try: + extra = parse_extra_options(self.settings.extra_options) + except ValueError as exc: + self.failed.emit(str(exc)) + return + try: + args = self.clamav.scan_arguments(targets, extra) + except ClamAVError as exc: + self.failed.emit(str(exc)) + return + + scan_id = 0 + started = time.monotonic() + summary: dict = {} + detections: list[Detection] = [] + error_count = 0 + cancelled = False + returncode: int | None = None + last_lines: list[str] = [] + + try: + scan_id = self.storage.create_scan( + self.scope, [str(target) for target in targets], + engine_version=info.engine_version, db_version=info.db_version) + self.started_scan.emit(scan_id, [str(target) for target in targets]) + self.status.emit("Scanning") + logger.info("SCAN %s started (%s): %s", scan_id, self.scope, + ", ".join(str(target) for target in targets)) + self._process = subprocess.Popen( + args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, encoding="utf-8", errors="replace", bufsize=1, + cwd=str(info.path.parent), env={**os.environ, "LC_ALL": "C", "LANG": "C"}, + **hidden_kwargs(), + ) + assert self._process.stdout is not None + while True: + if self._cancel: + cancelled = True + try: + self._process.terminate() + except OSError: + pass + try: + self._process.wait(timeout=5) + except subprocess.TimeoutExpired: + self._process.kill() + break + line = self._process.stdout.readline() + if not line: + if self._process.poll() is not None: + break + continue + last_lines.append(line.rstrip()) + if len(last_lines) > 80: + last_lines.pop(0) + event = parse_clamscan_line(line) + if not event: + continue + kind = event.get("kind") + if kind == "detection": + detection = Detection(path=str(event["path"]), + signature=str(event["signature"])) + try: + detection.size = Path(detection.path).stat().st_size + except OSError: + detection.size = 0 + detection.id = self.storage.add_detection(scan_id, detection) + self._remediate(detection, scan_id) + detections.append(detection) + self.detection_found.emit(detection) + self.progress.emit(detection.path, len(detections)) + elif kind == "summary": + key = str(event.get("key") or "") + value = event.get("value") + summary[key] = value + if key in ("scanned_files", "infected_files", "engine_version"): + self.status.emit(self._summary_status(summary, len(detections))) + elif kind in ("error", "skipped"): + if kind == "error": + error_count += 1 + message = str(event.get("message") or event.get("reason") or "") + if message: + self.log_line.emit(message) + elif kind == "progress": + now = time.monotonic() + if now - self._last_progress >= 0.12: + self._last_progress = now + self.progress.emit(str(event.get("path") or ""), len(detections)) + elif kind == "log": + message = str(event.get("message") or "") + if message and ("error" in message.lower() or "warning" in message.lower()): + self.log_line.emit(message) + + returncode = self._process.wait(timeout=20) + if cancelled: + status = "cancelled" + elif returncode in (0, 1): + status = "completed" + elif returncode == 2 and summary.get("scanned_files"): + status = "completed_with_errors" + else: + status = "failed" + scanned_files = _int_value(summary.get("scanned_files")) + infected_files = _int_value(summary.get("infected_files")) + if infected_files == 0 and detections: + infected_files = len(detections) + duration = time.monotonic() - started + if summary.get("engine_version"): + engine = str(summary["engine_version"]).split()[0] + self.storage.set_scan_engine(scan_id, engine, info.db_version) + self.storage.finish_scan( + scan_id, status=status, scanned_files=scanned_files, + infected_files=infected_files, errors=error_count, + duration_seconds=duration, cancelled=cancelled, + ) + record = self.storage.get_scan(scan_id) or ScanRecord( + id=scan_id, started_at="", finished_at=None, scope=self.scope, + status=status, paths=[str(target) for target in targets], + scanned_files=scanned_files, infected_files=infected_files, + errors=error_count, duration_seconds=duration, + cancelled=cancelled, detections=detections, + ) + if not record.detections: + record.detections = detections + logger.info("SCAN %s finished: %s, %s files, %s detections, %s errors", + scan_id, status, scanned_files, infected_files, error_count) + self.status.emit("Cancelled" if cancelled else "Finished") + self.finished_scan.emit(record) + except Exception as exc: + logger.exception("Scan %s failed", scan_id) + try: + if self._process is not None and self._process.poll() is None: + self._process.kill() + except Exception: + pass + if scan_id: + try: + self.storage.finish_scan( + scan_id, status="failed", scanned_files=_int_value(summary.get("scanned_files")), + infected_files=max(_int_value(summary.get("infected_files")), len(detections)), + errors=error_count + 1, duration_seconds=time.monotonic() - started, + cancelled=cancelled, + ) + except StorageError: + logger.exception("Could not mark scan %s failed", scan_id) + detail = str(exc) + if last_lines: + detail += "\n" + "\n".join(last_lines[-5:]) + self.failed.emit(detail) + finally: + self._process = None + + def _remediate(self, detection: Detection, scan_id: int) -> None: + action = self.settings.auto_action + if action == "quarantine": + try: + item = self.quarantine.quarantine_file( + Path(detection.path), detection.signature, + scan_id=scan_id, reason="Full scan") + detection.action = "quarantined" + detection.quarantine_id = item.id + except (QuarantineError, OSError) as exc: + detection.action = "failed" + logger.warning("Quarantine failed for %s: %s", detection.path, exc) + self.log_line.emit(f"Quarantine failed: {exc}") + elif action == "delete": + try: + self.quarantine.delete_original(Path(detection.path)) + detection.action = "deleted" + except (QuarantineError, OSError) as exc: + detection.action = "failed" + logger.warning("Delete failed for %s: %s", detection.path, exc) + self.log_line.emit(f"Delete failed: {exc}") + else: + detection.action = "detected" + if detection.id: + try: + self.storage.update_detection_action( + detection.id, detection.action, detection.quarantine_id) + except StorageError: + logger.exception("Could not record remediation action") + + @staticmethod + def _summary_status(summary: dict, detection_count: int) -> str: + scanned = _int_value(summary.get("scanned_files")) + infected = max(_int_value(summary.get("infected_files")), detection_count) + if scanned: + return f"Scanned {scanned:,} files · {infected} detection(s)" + return "Scanning" + + +def _int_value(value) -> int: + try: + return int(str(value).split()[0]) + except (TypeError, ValueError, IndexError): + return 0 + + +class DefinitionWorker(QThread): + log_line = pyqtSignal(str) + finished_ok = pyqtSignal(str) + failed = pyqtSignal(str) + + def __init__(self, clamav: ClamAVService, parent=None) -> None: + super().__init__(parent) + self.clamav = clamav + self._cancel = False + self._process: subprocess.Popen | None = None + + def cancel(self) -> None: + self._cancel = True + if self._process is not None and self._process.poll() is None: + try: + self._process.terminate() + except OSError: + pass + + def run(self) -> None: + info = self.clamav.info(force=True) + if not info.available: + self.failed.emit(info.error or "Install ClamAV before updating definitions.") + return + freshclam, error = self.clamav.freshclam(), "" + if freshclam is None: + from .clamav import find_freshclam + + freshclam, error = find_freshclam(info.path) + if freshclam is None: + self.failed.emit(error or "freshclam was not found.") + return + db_dir = paths.clamav_db_dir() + db_dir.mkdir(parents=True, exist_ok=True) + args = build_freshclam_args(freshclam, db_dir) + last_lines: list[str] = [] + try: + self._process = subprocess.Popen( + args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, encoding="utf-8", errors="replace", bufsize=1, + cwd=str(freshclam.parent), env={**os.environ, "LC_ALL": "C", "LANG": "C"}, + **hidden_kwargs(), + ) + assert self._process.stdout is not None + while True: + if self._cancel: + self._process.terminate() + self.failed.emit("Definition update was cancelled.") + return + line = self._process.stdout.readline() + if not line: + if self._process.poll() is not None: + break + continue + text = line.rstrip() + last_lines.append(text) + if len(last_lines) > 40: + last_lines.pop(0) + if text: + self.log_line.emit(text) + code = self._process.wait(timeout=20) + self.clamav.invalidate() + if code == 0: + info = self.clamav.info(force=True) + self.finished_ok.emit(f"Definitions updated. {info.summary}") + else: + self.failed.emit( + "freshclam exited with code " + f"{code}." + ("\n" + "\n".join(last_lines[-5:]) if last_lines else "")) + except Exception as exc: + logger.exception("Definition update failed") + self.failed.emit(str(exc)) + finally: + self._process = None diff --git a/src/boltclam/storage.py b/src/boltclam/storage.py new file mode 100644 index 0000000..17fbd47 --- /dev/null +++ b/src/boltclam/storage.py @@ -0,0 +1,375 @@ +"""Local SQLite history for scans, detections, quarantine and real-time events. + +One connection per operation keeps this safe to call from scan threads, the +real-time worker and the Qt UI without sharing a sqlite object across +threads. WAL mode lets the UI read while a scan is writing. +""" + +from __future__ import annotations + +import json +import logging +import sqlite3 +from contextlib import contextmanager +from datetime import datetime, timedelta, timezone +from pathlib import Path +from typing import Iterator + +from . import paths +from .models import ( + Detection, + QuarantineItem, + RealtimeEvent, + ScanRecord, + now_iso, +) + +logger = logging.getLogger(__name__) + + +class StorageError(Exception): + pass + + +_SCHEMA = """ +CREATE TABLE IF NOT EXISTS scans ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + started_at TEXT NOT NULL, + finished_at TEXT, + scope TEXT NOT NULL, + status TEXT NOT NULL, + paths TEXT NOT NULL DEFAULT '[]', + scanned_files INTEGER NOT NULL DEFAULT 0, + infected_files INTEGER NOT NULL DEFAULT 0, + errors INTEGER NOT NULL DEFAULT 0, + engine_version TEXT NOT NULL DEFAULT '', + db_version TEXT NOT NULL DEFAULT '', + duration_seconds REAL NOT NULL DEFAULT 0, + cancelled INTEGER NOT NULL DEFAULT 0 +); +CREATE TABLE IF NOT EXISTS detections ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + scan_id INTEGER NOT NULL, + path TEXT NOT NULL, + signature TEXT NOT NULL, + action TEXT NOT NULL DEFAULT 'detected', + quarantine_id TEXT, + size INTEGER NOT NULL DEFAULT 0, + detected_at TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_detections_scan ON detections(scan_id); +CREATE INDEX IF NOT EXISTS idx_detections_action ON detections(action); +CREATE TABLE IF NOT EXISTS quarantine ( + id TEXT PRIMARY KEY, + original_path TEXT NOT NULL, + quarantine_path TEXT NOT NULL, + signature TEXT NOT NULL, + sha256 TEXT NOT NULL, + size INTEGER NOT NULL DEFAULT 0, + created_at TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'quarantined', + scan_id INTEGER +); +CREATE INDEX IF NOT EXISTS idx_quarantine_status ON quarantine(status); +CREATE INDEX IF NOT EXISTS idx_quarantine_sha ON quarantine(sha256); +CREATE TABLE IF NOT EXISTS realtime_events ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at TEXT NOT NULL, + path TEXT NOT NULL, + signature TEXT NOT NULL, + action TEXT NOT NULL, + detail TEXT NOT NULL DEFAULT '' +); +CREATE INDEX IF NOT EXISTS idx_realtime_created ON realtime_events(created_at); +""" + + +class Storage: + def __init__(self, path: Path | None = None) -> None: + self.path = Path(path) if path else paths.db_path() + self.path.parent.mkdir(parents=True, exist_ok=True) + self.initialize() + + @contextmanager + def _connect(self) -> Iterator[sqlite3.Connection]: + try: + connection = sqlite3.connect(str(self.path), timeout=30) + except sqlite3.Error as exc: + raise StorageError(f"Could not open the BoltClam database: {exc}") from exc + connection.row_factory = sqlite3.Row + try: + connection.execute("PRAGMA busy_timeout=30000") + connection.execute("PRAGMA foreign_keys=ON") + yield connection + connection.commit() + except sqlite3.Error as exc: + connection.rollback() + raise StorageError(f"BoltClam database error: {exc}") from exc + finally: + connection.close() + + def initialize(self) -> None: + with self._connect() as connection: + connection.execute("PRAGMA journal_mode=WAL") + connection.executescript(_SCHEMA) + + # -- scans ------------------------------------------------------------- + + def create_scan(self, scope: str, paths_list: list[str], *, + engine_version: str = "", db_version: str = "") -> int: + with self._connect() as connection: + cursor = connection.execute( + "INSERT INTO scans (started_at, scope, status, paths, engine_version, db_version) " + "VALUES (?, ?, 'running', ?, ?, ?)", + (now_iso(), scope, json.dumps(paths_list, ensure_ascii=False), + engine_version, db_version), + ) + return int(cursor.lastrowid) + + def finish_scan(self, scan_id: int, *, status: str, scanned_files: int, + infected_files: int, errors: int, duration_seconds: float, + cancelled: bool) -> None: + with self._connect() as connection: + connection.execute( + "UPDATE scans SET finished_at=?, status=?, scanned_files=?, infected_files=?, " + "errors=?, duration_seconds=?, cancelled=? WHERE id=?", + (now_iso(), status, int(scanned_files), int(infected_files), int(errors), + float(duration_seconds), 1 if cancelled else 0, int(scan_id)), + ) + + def set_scan_engine(self, scan_id: int, engine_version: str, db_version: str) -> None: + with self._connect() as connection: + connection.execute( + "UPDATE scans SET engine_version=?, db_version=? WHERE id=?", + (engine_version, db_version, int(scan_id)), + ) + + def _scan_from_row(self, row: sqlite3.Row) -> ScanRecord: + try: + paths_list = json.loads(row["paths"] or "[]") + except (TypeError, ValueError): + paths_list = [] + return ScanRecord( + id=int(row["id"]), + started_at=str(row["started_at"]), + finished_at=row["finished_at"], + scope=str(row["scope"]), + status=str(row["status"]), + paths=[str(path) for path in paths_list if isinstance(path, str)], + scanned_files=int(row["scanned_files"] or 0), + infected_files=int(row["infected_files"] or 0), + errors=int(row["errors"] or 0), + engine_version=str(row["engine_version"] or ""), + db_version=str(row["db_version"] or ""), + duration_seconds=float(row["duration_seconds"] or 0), + cancelled=bool(row["cancelled"]), + ) + + def list_scans(self, limit: int = 100) -> list[ScanRecord]: + with self._connect() as connection: + rows = connection.execute( + "SELECT * FROM scans ORDER BY id DESC LIMIT ?", (max(1, int(limit)),) + ).fetchall() + return [self._scan_from_row(row) for row in rows] + + def get_scan(self, scan_id: int) -> ScanRecord | None: + with self._connect() as connection: + row = connection.execute("SELECT * FROM scans WHERE id=?", (int(scan_id),)).fetchone() + if row is None: + return None + record = self._scan_from_row(row) + detections = connection.execute( + "SELECT * FROM detections WHERE scan_id=? ORDER BY id", (int(scan_id),) + ).fetchall() + record.detections = [ + Detection( + id=int(item["id"]), + path=str(item["path"]), + signature=str(item["signature"]), + action=str(item["action"]), + quarantine_id=item["quarantine_id"], + size=int(item["size"] or 0), + detected_at=str(item["detected_at"]), + ) + for item in detections + ] + return record + + def latest_scan(self) -> ScanRecord | None: + scans = self.list_scans(1) + return scans[0] if scans else None + + # -- detections -------------------------------------------------------- + + def add_detection(self, scan_id: int, detection: Detection) -> int: + with self._connect() as connection: + cursor = connection.execute( + "INSERT INTO detections (scan_id, path, signature, action, quarantine_id, size, " + "detected_at) VALUES (?, ?, ?, ?, ?, ?, ?)", + (int(scan_id), detection.path, detection.signature, detection.action, + detection.quarantine_id, int(detection.size), detection.detected_at or now_iso()), + ) + return int(cursor.lastrowid) + + def update_detection_action(self, detection_id: int, action: str, + quarantine_id: str | None = None) -> None: + with self._connect() as connection: + connection.execute( + "UPDATE detections SET action=?, quarantine_id=COALESCE(?, quarantine_id) WHERE id=?", + (action, quarantine_id, int(detection_id)), + ) + + def recent_detections(self, limit: int = 10) -> list[dict]: + with self._connect() as connection: + rows = connection.execute( + "SELECT d.*, s.started_at AS scan_started_at, s.scope AS scan_scope " + "FROM detections d LEFT JOIN scans s ON s.id=d.scan_id " + "ORDER BY d.id DESC LIMIT ?", (max(1, int(limit)),) + ).fetchall() + return [dict(row) for row in rows] + + # -- quarantine -------------------------------------------------------- + + def add_quarantine(self, item: QuarantineItem) -> None: + with self._connect() as connection: + connection.execute( + "INSERT OR REPLACE INTO quarantine " + "(id, original_path, quarantine_path, signature, sha256, size, created_at, " + "status, scan_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + (item.id, item.original_path, item.quarantine_path, item.signature, + item.sha256, int(item.size), item.created_at, item.status, item.scan_id), + ) + + def _quarantine_from_row(self, row: sqlite3.Row) -> QuarantineItem: + return QuarantineItem( + id=str(row["id"]), + original_path=str(row["original_path"]), + quarantine_path=str(row["quarantine_path"]), + signature=str(row["signature"]), + sha256=str(row["sha256"]), + size=int(row["size"] or 0), + created_at=str(row["created_at"]), + status=str(row["status"]), + scan_id=row["scan_id"], + ) + + def list_quarantine(self, status: str | None = None, limit: int = 500) -> list[QuarantineItem]: + query = "SELECT * FROM quarantine" + params: list = [] + if status: + query += " WHERE status=?" + params.append(status) + query += " ORDER BY created_at DESC LIMIT ?" + params.append(max(1, int(limit))) + with self._connect() as connection: + return [self._quarantine_from_row(row) + for row in connection.execute(query, params).fetchall()] + + def get_quarantine(self, item_id: str) -> QuarantineItem | None: + with self._connect() as connection: + row = connection.execute("SELECT * FROM quarantine WHERE id=?", + (str(item_id),)).fetchone() + return self._quarantine_from_row(row) if row is not None else None + + def set_quarantine_status(self, item_id: str, status: str) -> None: + with self._connect() as connection: + connection.execute("UPDATE quarantine SET status=? WHERE id=?", + (status, str(item_id))) + + def quarantine_count(self, status: str = "quarantined") -> int: + with self._connect() as connection: + row = connection.execute("SELECT COUNT(*) AS n FROM quarantine WHERE status=?", + (status,)).fetchone() + return int(row["n"] if row else 0) + + # -- real-time events -------------------------------------------------- + + def add_realtime_event(self, event: RealtimeEvent) -> int: + with self._connect() as connection: + cursor = connection.execute( + "INSERT INTO realtime_events (created_at, path, signature, action, detail) " + "VALUES (?, ?, ?, ?, ?)", + (event.created_at or now_iso(), event.path, event.signature, + event.action, event.detail or ""), + ) + return int(cursor.lastrowid) + + def list_realtime_events(self, limit: int = 100) -> list[RealtimeEvent]: + with self._connect() as connection: + rows = connection.execute( + "SELECT * FROM realtime_events ORDER BY id DESC LIMIT ?", + (max(1, int(limit)),) + ).fetchall() + return [ + RealtimeEvent( + id=int(row["id"]), created_at=str(row["created_at"]), + path=str(row["path"]), signature=str(row["signature"]), + action=str(row["action"]), detail=str(row["detail"] or ""), + ) + for row in rows + ] + + # -- dashboard aggregates --------------------------------------------- + + def stats(self, days: int = 30) -> dict: + cutoff = (datetime.now(timezone.utc) - timedelta(days=max(1, days))).isoformat( + timespec="seconds").replace("+00:00", "Z") + with self._connect() as connection: + latest_row = connection.execute( + "SELECT * FROM scans ORDER BY id DESC LIMIT 1").fetchone() + latest = self._scan_from_row(latest_row) if latest_row else None + active = connection.execute( + "SELECT COUNT(*) AS n FROM detections WHERE action IN ('detected','failed')" + ).fetchone() + quarantined = connection.execute( + "SELECT COUNT(*) AS n FROM quarantine WHERE status='quarantined'" + ).fetchone() + threats = connection.execute( + "SELECT COUNT(*) AS n FROM detections WHERE detected_at >= ?", (cutoff,) + ).fetchone() + blocked = connection.execute( + "SELECT COUNT(*) AS n FROM realtime_events WHERE created_at >= ? " + "AND action IN ('quarantined','deleted','blocked')", (cutoff,) + ).fetchone() + total_scans = connection.execute("SELECT COUNT(*) AS n FROM scans").fetchone() + return { + "latest_scan": latest, + "active_threats": int(active["n"] if active else 0), + "quarantined": int(quarantined["n"] if quarantined else 0), + "threats_30d": int(threats["n"] if threats else 0), + "blocked_30d": int(blocked["n"] if blocked else 0), + "scans": int(total_scans["n"] if total_scans else 0), + } + + def diagnostics_snapshot(self, scan_limit: int = 50, event_limit: int = 100) -> dict: + scans = self.list_scans(scan_limit) + return { + "scans": [ + { + "id": scan.id, "started_at": scan.started_at, + "finished_at": scan.finished_at, "scope": scan.scope, + "status": scan.status, "scanned_files": scan.scanned_files, + "infected_files": scan.infected_files, "errors": scan.errors, + "engine_version": scan.engine_version, "db_version": scan.db_version, + "duration_seconds": scan.duration_seconds, "cancelled": scan.cancelled, + } + for scan in scans + ], + "quarantine": [ + { + "id": item.id, "original_path": item.original_path, + "signature": item.signature, "sha256": item.sha256, + "size": item.size, "created_at": item.created_at, + "status": item.status, "scan_id": item.scan_id, + } + for item in self.list_quarantine(limit=200) + ], + "realtime": [ + { + "created_at": event.created_at, "path": event.path, + "signature": event.signature, "action": event.action, + "detail": event.detail, + } + for event in self.list_realtime_events(event_limit) + ], + } diff --git a/src/boltclam/system_info.py b/src/boltclam/system_info.py new file mode 100644 index 0000000..46b9414 --- /dev/null +++ b/src/boltclam/system_info.py @@ -0,0 +1,448 @@ +"""Security and network overview for the Homepage. + +Windows-specific data comes from PowerShell (Defender, firewall, adapters) +and is cached briefly because those cmdlets are much slower than the rest of +the dashboard. Every external call is best-effort: "unknown" is a legitimate +state, never a fabricated "protected". +""" + +from __future__ import annotations + +import json +import logging +import os +import platform +import socket +import subprocess +import time +import uuid +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +from . import paths +from .clamav import hidden_kwargs +from .config import AppSettings +from .models import ClamAVInfo +from .storage import Storage + +logger = logging.getLogger(__name__) + +_CACHE: dict[str, Any] = {"at": 0.0, "snapshot": None} +_CACHE_SECONDS = 45 +_SEVERITY_ORDER = {"critical": 0, "warning": 1, "info": 2} + + +def clear_cache() -> None: + _CACHE["at"] = 0.0 + _CACHE["snapshot"] = None + + +def _powershell(command: str, timeout: int = 12) -> str: + if os.name != "nt": + return "" + try: + completed = subprocess.run( + ["powershell.exe", "-NoProfile", "-NonInteractive", + "-ExecutionPolicy", "Bypass", "-Command", command], + capture_output=True, text=True, timeout=timeout, errors="replace", + **hidden_kwargs(), + ) + return (completed.stdout or "").strip() + except (OSError, subprocess.SubprocessError) as exc: + logger.debug("PowerShell query failed: %s", exc) + return "" + + +def _extract_json(text: str) -> Any: + if not text: + return None + starts = [index for index in (text.find("{"), text.find("[")) if index >= 0] + if not starts: + return None + start = min(starts) + opener = text[start] + closer = "}" if opener == "{" else "]" + end = text.rfind(closer) + if end <= start: + return None + try: + return json.loads(text[start:end + 1]) + except ValueError: + return None + + +def _as_bool(value: Any) -> bool | None: + if isinstance(value, bool): + return value + if value is None: + return None + if isinstance(value, (int, float)): + return bool(value) + text = str(value).strip().lower() + if text in ("true", "1", "yes"): + return True + if text in ("false", "0", "no"): + return False + return None + + +def _as_int(value: Any) -> int | None: + try: + return int(value) + except (TypeError, ValueError): + return None + + +def _defender_firewall() -> dict: + command = ( + "$ErrorActionPreference='SilentlyContinue';" + "$mp=Get-MpComputerStatus;" + "$profiles=@(Get-NetFirewallProfile);" + "$fw=@($profiles | Where-Object {$_.Enabled -eq $true} | ForEach-Object {$_.Name});" + "[pscustomobject]@{" + "antivirus_enabled=$mp.AntivirusEnabled;" + "realtime_enabled=$mp.RealTimeProtectionEnabled;" + "signature_age_days=$mp.AntivirusSignatureAge;" + "signature_version=$mp.AntivirusSignatureVersion;" + "firewall_enabled=($fw.Count -gt 0);" + "firewall_profiles=$fw" + "} | ConvertTo-Json -Compress" + ) + data = _extract_json(_powershell(command)) + if not isinstance(data, dict): + return { + "defender_available": False, + "antivirus_enabled": None, + "realtime_enabled": None, + "signature_age_days": None, + "signature_version": "", + "firewall_enabled": None, + "firewall_profiles": [], + } + return { + "defender_available": True, + "antivirus_enabled": _as_bool(data.get("antivirus_enabled")), + "realtime_enabled": _as_bool(data.get("realtime_enabled")), + "signature_age_days": _as_int(data.get("signature_age_days")), + "signature_version": str(data.get("signature_version") or ""), + "firewall_enabled": _as_bool(data.get("firewall_enabled")), + "firewall_profiles": [str(item) for item in (data.get("firewall_profiles") or [])], + } + + +def _network_details() -> dict: + mac = "" + try: + raw = uuid.getnode() + mac = ":".join(f"{(raw >> shift) & 0xff:02x}" for shift in range(40, -1, -8)).upper() + except Exception: + mac = "" + local_ips: list[str] = [] + try: + hostname = socket.gethostname() + _name, _aliases, addresses = socket.gethostbyname_ex(hostname) + local_ips.extend(addresses) + except Exception: + pass + try: + local_ips.append(socket.gethostbyname(socket.gethostname())) + except Exception: + pass + if os.name == "nt": + command = ( + "$ErrorActionPreference='SilentlyContinue';" + "$ips=@(Get-NetIPAddress -AddressFamily IPv4 | " + "Where-Object {$_.IPAddress -notlike '127.*' -and " + "$_.IPAddress -notlike '169.254.*'} | " + "Select-Object InterfaceAlias,IPAddress,PrefixLength);" + "$route=Get-NetRoute -DestinationPrefix '0.0.0.0/0' | " + "Sort-Object RouteMetric | Select-Object -First 1;" + "[pscustomobject]@{adapters=$ips;gateway=$route.NextHop;" + "interface=$route.InterfaceAlias} | ConvertTo-Json -Depth 3 -Compress" + ) + data = _extract_json(_powershell(command)) + if isinstance(data, dict): + adapters = data.get("adapters") + if isinstance(adapters, dict): + adapters = [adapters] + normalized = [] + for adapter in adapters or []: + if not isinstance(adapter, dict): + continue + ip = str(adapter.get("IPAddress") or "").strip() + if ip: + local_ips.append(ip) + normalized.append({ + "name": str(adapter.get("InterfaceAlias") or "Adapter"), + "ip": ip, + "prefix": _as_int(adapter.get("PrefixLength")), + }) + return { + "adapters": normalized, + "local_ips": sorted(set(local_ips)), + "gateway": str(data.get("gateway") or ""), + "gateway_interface": str(data.get("interface") or ""), + "mac": mac, + } + return { + "adapters": [{"name": "Local interface", "ip": ip, "prefix": None} + for ip in sorted(set(local_ips))], + "local_ips": sorted(set(local_ips)), + "gateway": "", + "gateway_interface": "", + "mac": mac, + } + + +def _uptime_seconds() -> int | None: + if os.name == "nt": + try: + import ctypes + + return int(ctypes.windll.kernel32.GetTickCount64() // 1000) + except Exception: + return None + try: + with open("/proc/uptime", "r", encoding="ascii") as handle: + return int(float(handle.read().split()[0])) + except (OSError, ValueError, IndexError): + return None + + +def human_uptime(seconds: int | None) -> str: + if seconds is None: + return "Unknown" + days, remainder = divmod(max(0, int(seconds)), 86400) + hours, remainder = divmod(remainder, 3600) + minutes = remainder // 60 + if days: + return f"{days}d {hours}h" + if hours: + return f"{hours}h {minutes}m" + return f"{minutes}m" + + +def _concern(severity: str, title: str, detail: str, action: str) -> dict: + return {"severity": severity, "title": title, "detail": detail, "action": action} + + +def _build_concerns(settings: AppSettings, clamav: ClamAVInfo, defender: dict, + stats: dict, is_admin: bool, + realtime_running: bool | None) -> list[dict]: + concerns: list[dict] = [] + if not clamav.available: + concerns.append(_concern( + "critical", "ClamAV is not available", + clamav.error or "Install ClamAV and point BoltClam at its folder in Settings.", + "settings")) + else: + if not clamav.db_path: + concerns.append(_concern( + "critical", "Virus definitions are missing", + "Update ClamAV definitions before relying on a scan.", "update_definitions")) + elif clamav.db_age_days is not None and clamav.db_age_days > 30: + concerns.append(_concern( + "critical", f"Definitions are {clamav.db_age_days:.0f} days old", + "New malware families will be missed until the database is refreshed.", + "update_definitions")) + elif clamav.db_age_days is not None and clamav.db_age_days > 7: + concerns.append(_concern( + "warning", f"Definitions are {clamav.db_age_days:.0f} days old", + "Refresh the ClamAV database to cover recent threats.", "update_definitions")) + if not settings.realtime_enabled: + concerns.append(_concern( + "warning", "Real-time protection is off", + "New browser downloads can be opened without a scan.", "settings")) + elif clamav.available and realtime_running is False: + concerns.append(_concern( + "warning", "Real-time watcher is not running", + "Turn real-time protection off and on again, or restart BoltClam.", "settings")) + if defender.get("firewall_enabled") is False: + concerns.append(_concern( + "warning", "Windows Firewall is disabled", + "Enable at least one Windows Firewall profile.", "settings")) + if defender.get("realtime_enabled") is False and not ( + settings.realtime_enabled and clamav.available): + concerns.append(_concern( + "warning", "No active real-time antivirus", + "Windows Defender real-time protection is off and BoltClam's watcher is not active.", + "settings")) + if int(stats.get("active_threats") or 0) > 0: + concerns.append(_concern( + "critical", f"{stats['active_threats']} detected file(s) still need action", + "Review the latest scan results and quarantine or delete them.", "scan")) + latest = stats.get("latest_scan") + if latest is None: + concerns.append(_concern( + "info", "No scan on record", + "Run a scan to establish a baseline for this PC.", "scan")) + else: + try: + started = datetime.fromisoformat(str(latest.started_at).replace("Z", "+00:00")) + age_days = (datetime.now(timezone.utc) - started).total_seconds() / 86400 + if age_days > 14: + concerns.append(_concern( + "info", f"Last scan was {age_days:.0f} days ago", + "A weekly scan keeps the history useful.", "scan")) + except (TypeError, ValueError): + pass + if latest.errors > 0: + concerns.append(_concern( + "info", f"Last scan reported {latest.errors} access error(s)", + "Some files could not be read. Running as administrator improves coverage.", + "scan")) + if int(stats.get("quarantined") or 0) > 0: + concerns.append(_concern( + "info", f"{stats['quarantined']} item(s) in quarantine", + "Restore anything that was a false positive, or delete it permanently.", + "quarantine")) + if not is_admin and settings.scan_scope == "homes": + concerns.append(_concern( + "info", "Limited access to other user profiles", + "Run BoltClam as administrator to scan every user's home completely.", + "none")) + concerns.sort(key=lambda item: _SEVERITY_ORDER.get(item["severity"], 3)) + return concerns + + +def _segments(settings: AppSettings, clamav: ClamAVInfo, defender: dict, + realtime_running: bool | None) -> list[dict]: + engine = ("good" if clamav.available else "bad", + "Engine", clamav.engine_version or "Not installed") + if not clamav.available: + definitions = ("bad", "Definitions", "Missing") + elif not clamav.db_path: + definitions = ("bad", "Definitions", "Missing") + elif clamav.db_age_days is not None and clamav.db_age_days > 30: + definitions = ("bad", "Definitions", f"{clamav.db_age_days:.0f} days old") + elif clamav.db_age_days is not None and clamav.db_age_days > 7: + definitions = ("warn", "Definitions", f"{clamav.db_age_days:.0f} days old") + else: + definitions = ("good", "Definitions", "Current") + if not settings.realtime_enabled: + realtime = ("warn", "Real-time", "Off") + elif not clamav.available: + realtime = ("bad", "Real-time", "No engine") + elif realtime_running is False: + realtime = ("warn", "Real-time", "Not running") + else: + realtime = ("good", "Real-time", "Watching downloads") + firewall = defender.get("firewall_enabled") + defender_rt = defender.get("realtime_enabled") + if firewall is False: + system = ("warn", "Firewall", "Disabled") + elif defender_rt is False and not (settings.realtime_enabled and clamav.available): + system = ("warn", "Defender", "Real-time off") + elif firewall is None and defender_rt is None: + system = ("unknown", "System", "Not reported") + else: + system = ("good", "System", "Firewall + Defender") + return [ + {"key": "engine", "state": engine[0], "label": engine[1], "detail": engine[2]}, + {"key": "definitions", "state": definitions[0], + "label": definitions[1], "detail": definitions[2]}, + {"key": "realtime", "state": realtime[0], + "label": realtime[1], "detail": realtime[2]}, + {"key": "system", "state": system[0], "label": system[1], "detail": system[2]}, + ] + + +def collect(settings: AppSettings, clamav: ClamAVInfo, storage: Storage, *, + realtime_running: bool | None = None, + force: bool = False) -> dict: + now = time.time() + if not force and _CACHE["snapshot"] is not None and now - _CACHE["at"] < _CACHE_SECONDS: + cached = dict(_CACHE["snapshot"]) + # These two are live values the cached network/Defender probes do not + # need to re-query, but the dashboard should still update. + cached["realtime"]["enabled"] = settings.realtime_enabled + cached["realtime"]["running"] = realtime_running + return cached + + stats = storage.stats() + stats_snapshot = dict(stats) + latest = stats_snapshot.get("latest_scan") + if latest is not None: + stats_snapshot["latest_scan"] = { + "id": latest.id, + "started_at": latest.started_at, + "finished_at": latest.finished_at, + "scope": latest.scope, + "status": latest.status, + "scanned_files": latest.scanned_files, + "infected_files": latest.infected_files, + "errors": latest.errors, + "duration_seconds": latest.duration_seconds, + "cancelled": latest.cancelled, + } + defender = _defender_firewall() + network = _network_details() + uptime = _uptime_seconds() + is_admin = paths.is_admin() + if os.name == "nt": + os_name = paths.os_label() + else: + os_name = f"{platform.system()} {platform.release()}".strip() or platform.platform() + concerns = _build_concerns(settings, clamav, defender, stats, is_admin, + realtime_running) + segments = _segments(settings, clamav, defender, realtime_running) + if not clamav.available: + overall = "setup" + headline = "ClamAV needs to be set up" + summary = "Point BoltClam at ClamAV to start scanning and protecting downloads." + elif int(stats.get("active_threats") or 0) > 0: + overall = "at_risk" + headline = "Detected files need your attention" + summary = f"{stats['active_threats']} file(s) are waiting for quarantine or deletion." + elif any(item["severity"] == "critical" for item in concerns): + overall = "at_risk" + headline = "Protection needs attention" + summary = concerns[0]["detail"] if concerns else "Review the concerns below." + elif any(item["severity"] == "warning" for item in concerns): + overall = "attention" + headline = "A few things need attention" + summary = concerns[0]["detail"] if concerns else "Review the concerns below." + else: + overall = "protected" + headline = "You're protected" + summary = "ClamAV definitions are current and BoltClam is watching new downloads." + + snapshot = { + "checked_at": datetime.now(timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z"), + "overall": overall, + "headline": headline, + "summary": summary, + "hostname": paths.hostname(), + "os": os_name, + "uptime": human_uptime(uptime), + "uptime_seconds": uptime, + "is_admin": is_admin, + "network": network, + "defender": defender, + "firewall": { + "enabled": defender.get("firewall_enabled"), + "profiles": defender.get("firewall_profiles") or [], + }, + "clamav": { + "available": clamav.available, + "engine": clamav.engine_version, + "db_version": clamav.db_version, + "db_date": clamav.db_date, + "db_age_days": clamav.db_age_days, + "db_path": str(clamav.db_path) if clamav.db_path else "", + "path": str(clamav.path) if clamav.path else "", + "summary": clamav.summary, + "error": clamav.error, + }, + "realtime": { + "enabled": settings.realtime_enabled, + "running": realtime_running, + }, + "concerns": concerns, + "segments": segments, + "stats": stats_snapshot, + } + _CACHE["snapshot"] = snapshot + _CACHE["at"] = now + return snapshot diff --git a/src/boltclam/ui/__init__.py b/src/boltclam/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/boltclam/ui/about_page.py b/src/boltclam/ui/about_page.py new file mode 100644 index 0000000..7b80ac1 --- /dev/null +++ b/src/boltclam/ui/about_page.py @@ -0,0 +1,585 @@ +"""About & updates: account, subscription, ClamAV details and release feed.""" + +from __future__ import annotations + +import logging +import time +from pathlib import Path + +from PyQt6.QtCore import QSignalBlocker, QSize, Qt, QThread, pyqtSignal +from PyQt6.QtGui import QDesktopServices, QFont +from PyQt6.QtWidgets import ( + QHBoxLayout, + QLabel, + QListWidget, + QListWidgetItem, + QMessageBox, + QProgressBar, + QPushButton, + QScrollArea, + QSizePolicy, + QVBoxLayout, + QWidget, +) + +from .. import __build_date__, __version__, paths +from ..context import AppContext +from ..models import UpdateAsset, UpdateFeed, UpdateRelease +from ..updates import ( + DownloadedUpdate, + UpdateError, + UpdateService, + choose_asset, + is_newer, +) +from . import brand, components, theme +from .threads import stop_thread + +logger = logging.getLogger(__name__) + + +class UpdateCheckWorker(QThread): + succeeded = pyqtSignal(object) + failed = pyqtSignal(str) + + def __init__(self, context: AppContext, channel: str, parent=None) -> None: + super().__init__(parent) + self.context = context + self.channel = channel + + def run(self) -> None: + try: + api = self.context.api or self.context.rebuild_api() + feed = UpdateService(api, self.channel).check(force=True) + self.succeeded.emit(feed) + except Exception as exc: + logger.warning("Update check failed: %s", exc) + self.failed.emit(str(exc)) + + +class UpdateDownloadWorker(QThread): + progress = pyqtSignal(int, int) + succeeded = pyqtSignal(object) + failed = pyqtSignal(str) + + def __init__(self, context: AppContext, channel: str, release: UpdateRelease, + asset: UpdateAsset, parent=None) -> None: + super().__init__(parent) + self.context = context + self.channel = channel + self.release = release + self.asset = asset + + def run(self) -> None: + try: + api = self.context.api or self.context.rebuild_api() + service = UpdateService(api, self.channel) + result = service.download( + self.release, self.asset, + progress=lambda done, total: self.progress.emit(done, total)) + self.succeeded.emit(result) + except Exception as exc: + logger.warning("Update download failed: %s", exc) + self.failed.emit(str(exc)) + + +class ReleaseItem(QWidget): + def __init__(self, release: UpdateRelease, parent=None) -> None: + super().__init__(parent) + self.setMinimumHeight(66) + layout = QVBoxLayout(self) + layout.setContentsMargins(10, 7, 10, 7) + layout.setSpacing(2) + top = QHBoxLayout() + tag = QLabel(release.tag or release.name or "Untagged") + tag.setFont(theme.body_font(9.3, QFont.Weight.DemiBold)) + top.addWidget(tag, 1) + if release.prerelease: + top.addWidget(components.Pill("Beta", "warning"), 0) + layout.addLayout(top) + when = QLabel(theme.format_datetime(release.published_at)) + when.setObjectName("Muted") + when.setFont(theme.body_font(8.4)) + layout.addWidget(when) + + def sizeHint(self): + return QSize(260, 78) + + +class AboutPage(QWidget): + toast_requested = pyqtSignal(str, str, str) + update_found = pyqtSignal(object) + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + self._check_worker: UpdateCheckWorker | None = None + self._download_worker: UpdateDownloadWorker | None = None + self._feed: UpdateFeed | None = None + self._selected: UpdateRelease | None = None + self._downloaded: DownloadedUpdate | None = None + self._build() + self.refresh() + + # -- construction ------------------------------------------------------ + + def _build(self) -> None: + outer = QVBoxLayout(self) + outer.setContentsMargins(0, 0, 0, 0) + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + outer.addWidget(scroll) + content = QWidget() + content.setObjectName("Content") + scroll.setWidget(content) + layout = QVBoxLayout(content) + layout.setContentsMargins(30, 24, 30, 30) + layout.setSpacing(16) + + header = components.PageHeader( + "About & updates", "BoltClam's build, account, subscription, and release feed.") + self.check_button = QPushButton("Check for updates") + self.check_button.setObjectName("Secondary") + self.check_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.check_button.clicked.connect(lambda: self.check_updates(manual=True)) + header.add_action(self.check_button) + layout.addWidget(header) + + top = QHBoxLayout() + top.setSpacing(16) + top.addWidget(self._build_identity(), 1) + top.addWidget(self._build_update_card(), 1) + layout.addLayout(top) + + releases = QHBoxLayout() + releases.setSpacing(16) + releases.addWidget(self._build_notes_card(), 2) + releases.addWidget(self._build_history_card(), 1) + layout.addLayout(releases, 1) + + links = components.Card(margins=(16, 12, 16, 12)) + link_row = QHBoxLayout() + link_row.setSpacing(18) + for label, url in ( + ("BoltClam repository", "https://git.themajesticnetwork.com/TheMajesticNetwork/BoltClam"), + ("Bolt account", "https://bolt.themajesticnetwork.com/account"), + ("ClamAV downloads", "https://www.clamav.net/downloads"), + ): + link = QLabel(f'{label}') + link.setOpenExternalLinks(True) + link.setFont(theme.body_font(9.2, QFont.Weight.DemiBold)) + link_row.addWidget(link) + link_row.addStretch(1) + links.body.addLayout(link_row) + note = QLabel( + "BoltClam is a desktop interface for ClamAV. ClamAV is licensed under GPLv2 and " + "installed separately; BoltClam does not bundle its binaries. Real-time protection " + "locks a download while ClamAV scans it in user mode — it is a strong speed bump, " + "not a kernel-level filter driver.") + note.setObjectName("Dim") + note.setWordWrap(True) + links.body.addWidget(note) + layout.addWidget(links) + layout.addStretch(1) + + def _build_identity(self) -> QWidget: + card = components.Card() + top = QHBoxLayout() + top.setSpacing(13) + mark = QLabel() + mark.setPixmap(brand.pixmap(58)) + mark.setFixedSize(58, 58) + top.addWidget(mark, 0) + text = QVBoxLayout() + text.setSpacing(2) + name = QLabel("BoltClam") + name.setFont(theme.display_font(21, QFont.Weight.Bold)) + text.addWidget(name) + version = QLabel(theme.version_line()) + version.setObjectName("Muted") + text.addWidget(version) + top.addLayout(text, 1) + self.licensed_pill = components.Pill("Free", "muted") + top.addWidget(self.licensed_pill, 0, Qt.AlignmentFlag.AlignTop) + card.body.addLayout(top) + self.rows = {} + for key, label, mono in ( + ("account", "Signed in as", False), + ("email", "Email", False), + ("plan", "BoltClam plan", False), + ("subscription", "Subscription", False), + ("clamav", "ClamAV", False), + ("definitions", "Definitions", False), + ("build", "Build", False)): + row = components.KeyValueRow(label, mono=mono) + self.rows[key] = row + card.body.addWidget(row) + return card + + def _build_update_card(self) -> QWidget: + card = components.Card() + header = QHBoxLayout() + title = QLabel("Updates") + title.setObjectName("CardTitle") + header.addWidget(title, 1) + self.update_pill = components.Pill("Not checked", "muted") + header.addWidget(self.update_pill, 0) + card.body.addLayout(header) + self.update_headline = QLabel("Checking the release feed…") + self.update_headline.setFont(theme.display_font(14.5, QFont.Weight.DemiBold)) + self.update_headline.setWordWrap(True) + card.body.addWidget(self.update_headline) + self.update_detail = QLabel("") + self.update_detail.setObjectName("Muted") + self.update_detail.setWordWrap(True) + card.body.addWidget(self.update_detail) + self.download_progress = QProgressBar() + self.download_progress.setRange(0, 100) + self.download_progress.setFixedHeight(6) + self.download_progress.hide() + card.body.addWidget(self.download_progress) + self.download_status = QLabel("") + self.download_status.setObjectName("Dim") + self.download_status.setWordWrap(True) + card.body.addWidget(self.download_status) + buttons = QHBoxLayout() + buttons.setSpacing(8) + self.download_button = QPushButton("Download update") + self.download_button.setObjectName("Primary") + self.download_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.download_button.clicked.connect(self.download_selected) + self.download_button.setEnabled(False) + buttons.addWidget(self.download_button) + self.install_button = QPushButton("Run installer") + self.install_button.setObjectName("Secondary") + self.install_button.clicked.connect(self.open_download) + self.install_button.hide() + buttons.addWidget(self.install_button) + buttons.addStretch(1) + card.body.addLayout(buttons) + card.body.addStretch(1) + channel = QLabel("Release channel is controlled in Settings.") + channel.setObjectName("Dim") + card.body.addWidget(channel) + return card + + def _build_notes_card(self) -> QWidget: + card = components.Card() + header = QHBoxLayout() + self.notes_title = QLabel("Release notes") + self.notes_title.setObjectName("CardTitle") + header.addWidget(self.notes_title, 1) + self.notes_tag = components.Pill("—", "muted") + header.addWidget(self.notes_tag, 0) + card.body.addLayout(header) + self.notes_meta = QLabel("") + self.notes_meta.setObjectName("Muted") + card.body.addWidget(self.notes_meta) + self.notes_view = components.MarkdownView() + self.notes_view.setMinimumHeight(210) + card.body.addWidget(self.notes_view, 1) + self.notes_empty = QLabel( + "Check for updates to load release notes from the BoltClam repository.") + self.notes_empty.setObjectName("Muted") + self.notes_empty.setWordWrap(True) + card.body.addWidget(self.notes_empty) + return card + + def _build_history_card(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "Release history", "Newest first from the Gitea release feed")[0]) + self.history = QListWidget() + self.history.setSpacing(2) + self.history.currentItemChanged.connect(self._release_selected) + card.body.addWidget(self.history, 1) + return card + + # -- data -------------------------------------------------------------- + + def refresh(self) -> None: + self._refresh_identity() + self._refresh_update_state() + if self._feed is None and self._check_worker is None: + last = self.context.settings.last_update_check + if last <= 0 or time.time() - last > 6 * 3600: + self.check_updates(manual=False) + + def refresh_account(self) -> None: + self._refresh_identity() + + def reload_settings(self) -> None: + self._refresh_identity() + if self._feed is not None and self._feed.channel != self.context.settings.update_channel: + self._feed = None + self.history.clear() + self.notes_view.clear() + self.notes_empty.show() + self._refresh_update_state() + if self._check_worker is None: + self.check_updates(manual=False) + else: + self._refresh_update_state() + + def _refresh_identity(self) -> None: + self.rows["account"].set_value(self.context.user_name) + self.rows["email"].set_value(self.context.user_email or "—") + self.rows["plan"].set_value(self.context.plan_label) + subscription = self.context.subscription + status = str(subscription.get("status") or "none") + if status in ("active", "trialing"): + end = theme.format_datetime(subscription.get("current_period_end")) + self.rows["subscription"].set_value( + f"{subscription.get('label', 'Bolt')} · {status}" + + (f" · renews {end}" if end != "—" else "")) + else: + self.rows["subscription"].set_value("No active Bolt subscription") + info = self.context.clamav.info() + self.rows["clamav"].set_value(info.summary if info.available else "Not configured") + if info.available and info.db_date: + self.rows["definitions"].set_value( + f"{info.db_version or 'database'} · {info.db_date}") + else: + self.rows["definitions"].set_value("Missing") + self.rows["build"].set_value(f"{__version__} · {__build_date__}") + self.licensed_pill.setText("Licensed" if self.context.licensed else "Free") + self.licensed_pill.set_severity("good" if self.context.licensed else "muted") + if self._feed is None: + channel = "Beta" if self.context.settings.update_channel == "beta" else "Release" + self.update_detail.setText( + f"Channel: {channel}. Current version: {__version__}.") + + def _refresh_update_state(self) -> None: + if self._feed is None: + return + latest = self._feed.latest + if latest is None: + self.update_pill.setText("No releases") + self.update_pill.set_severity("muted") + self.update_headline.setText("No releases have been published yet") + self.update_detail.setText( + f"Channel: {self._feed.channel}. A release will appear here when it is tagged.") + self.download_button.setEnabled(False) + self.update_found.emit(None) + return + newer = is_newer(latest.tag, __version__) + self.update_pill.setText("Update available" if newer else "Up to date") + self.update_pill.set_severity("warning" if newer else "good") + self.update_headline.setText( + f"{latest.name or latest.tag} is ready" if newer else + f"You're on the latest {('beta' if latest.prerelease else 'release')}") + asset = choose_asset(latest) + asset_text = f" · {asset.name}" if asset else " · no Windows asset attached" + self.update_detail.setText( + f"{latest.tag} · published {theme.format_datetime(latest.published_at)}" + f"{asset_text}") + self.download_button.setEnabled(bool(asset)) + self._select_release(latest) + self.update_found.emit(latest if newer else None) + + def _select_release(self, release: UpdateRelease) -> None: + self._selected = release + self._downloaded = None + self.install_button.hide() + self.download_button.setText("Download update") + self.notes_title.setText(release.name or release.tag or "Release notes") + self.notes_tag.setText(release.tag or "—") + self.notes_tag.set_severity("warning" if release.prerelease else "good") + self.notes_meta.setText( + f"Published {theme.format_datetime(release.published_at)}" + + (" · beta" if release.prerelease else "")) + notes = release.notes.strip() + self.notes_view.set_markdown(notes or "This release has no notes.") + self.notes_view.show() + self.notes_empty.hide() + asset = choose_asset(release) + self.download_button.setEnabled(bool(asset)) + if asset: + self.download_status.setText( + f"Asset: {asset.name} · {theme.format_size(asset.size)}") + else: + self.download_status.setText("No Windows .exe or .zip asset is attached to this release.") + self._sync_history_selection(release) + + def _sync_history_selection(self, release: UpdateRelease) -> None: + if not hasattr(self, "history"): + return + for row in range(self.history.count()): + item = self.history.item(row) + if item is not None and str(item.data(Qt.ItemDataRole.UserRole) or "") == release.id: + if self.history.currentRow() != row: + blocker = QSignalBlocker(self.history) + self.history.setCurrentRow(row) + del blocker + return + + def _populate_history(self, feed: UpdateFeed) -> None: + blocker = QSignalBlocker(self.history) + self.history.clear() + for release in feed.releases: + item = QListWidgetItem() + item.setData(Qt.ItemDataRole.UserRole, release.id) + widget = ReleaseItem(release) + item.setSizeHint(QSize(260, 82)) + self.history.addItem(item) + self.history.setItemWidget(item, widget) + del blocker + if feed.latest is not None: + self._sync_history_selection(feed.latest) + + def _release_selected(self, current: QListWidgetItem | None, previous=None) -> None: + if current is None or self._feed is None: + return + release_id = str(current.data(Qt.ItemDataRole.UserRole) or "") + for release in self._feed.releases: + if release.id == release_id: + self._select_release(release) + return + + # -- update check ------------------------------------------------------ + + def check_updates(self, manual: bool = True) -> None: + if self._check_worker is not None and self._check_worker.isRunning(): + return + if self.context.api is None: + try: + self.context.rebuild_api() + except Exception as exc: + self.update_pill.setText("Unavailable") + self.update_pill.set_severity("muted") + self.update_headline.setText("Cannot reach the Bolt update service") + self.update_detail.setText(str(exc)) + return + self.check_button.setEnabled(False) + self.check_button.setText("Checking…") + self.update_pill.setText("Checking") + self.update_pill.set_severity("info") + worker = UpdateCheckWorker( + self.context, self.context.settings.update_channel, self) + worker.succeeded.connect(self._check_succeeded) + worker.failed.connect(lambda message: self._check_failed(message, manual)) + worker.finished.connect(self._check_finished) + worker.finished.connect(worker.deleteLater) + self._check_worker = worker + worker.start() + + def _check_succeeded(self, feed: UpdateFeed) -> None: + self._feed = feed + self._refresh_update_state() + self._populate_history(feed) + try: + self.context.settings = self.context.settings_store.update( + last_update_check=time.time()) + except Exception: + logger.exception("Could not save the update-check timestamp") + if feed.latest is None: + self.update_detail.setText(f"Channel: {feed.channel}. No releases yet.") + + def _check_failed(self, message: str, manual: bool) -> None: + self.update_pill.setText("Check failed") + self.update_pill.set_severity("critical" if manual else "muted") + self.update_headline.setText("Could not check for updates") + self.update_detail.setText(message) + self.download_button.setEnabled(False) + if manual: + self.toast_requested.emit("Update check failed", message, "critical") + + def _check_finished(self) -> None: + self._check_worker = None + self.check_button.setEnabled(True) + self.check_button.setText("Check for updates") + + # -- download ---------------------------------------------------------- + + def download_selected(self) -> None: + release = self._selected or (self._feed.latest if self._feed else None) + if release is None: + return + asset = choose_asset(release) + if asset is None: + self.toast_requested.emit( + "No Windows asset", "That release does not include a .exe or .zip asset.", "warning") + return + answer = QMessageBox.question( + self, "Download update?", + f"Download {asset.name} ({theme.format_size(asset.size)}) for {release.tag}?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.Yes) + if answer != QMessageBox.StandardButton.Yes: + return + self.download_button.setEnabled(False) + self.check_button.setEnabled(False) + self.download_progress.setValue(0) + self.download_progress.show() + self.download_status.setText("Starting download…") + worker = UpdateDownloadWorker( + self.context, self.context.settings.update_channel, release, asset, self) + worker.progress.connect(self._download_progress) + worker.succeeded.connect(self._download_succeeded) + worker.failed.connect(self._download_failed) + worker.finished.connect(self._download_finished) + worker.finished.connect(worker.deleteLater) + self._download_worker = worker + worker.start() + + def _download_progress(self, done: int, total: int) -> None: + if total > 0: + self.download_progress.setRange(0, 100) + self.download_progress.setValue(int(done * 100 / total)) + self.download_status.setText( + f"Downloading {theme.format_size(done)} of {theme.format_size(total)}…") + else: + self.download_progress.setRange(0, 0) + self.download_status.setText(f"Downloading {theme.format_size(done)}…") + + def _download_succeeded(self, result: DownloadedUpdate) -> None: + self._downloaded = result + self.download_progress.setRange(0, 100) + self.download_progress.setValue(100) + verified = "checksum verified" if result.verified else "no checksum asset published" + self.download_status.setText( + f"Saved to {result.path} · SHA-256 {result.sha256[:16]}… · {verified}") + self.toast_requested.emit( + "Update downloaded", f"{result.path.name} is ready to install.", "good") + self.download_button.setText("Download again") + self.download_button.setEnabled(True) + self.install_button.setText( + "Run installer" if result.path.suffix.lower() in (".exe", ".msi") else "Show download") + self.install_button.show() + + def _download_failed(self, message: str) -> None: + self.download_progress.hide() + self.download_status.setText(message) + self.download_button.setEnabled(True) + self.toast_requested.emit("Update download failed", message, "critical") + + def _download_finished(self) -> None: + self._download_worker = None + self.check_button.setEnabled(True) + if not self._downloaded: + self.download_progress.hide() + + def open_download(self) -> None: + if self._downloaded is None: + return + path = self._downloaded.path + if path.suffix.lower() in (".exe", ".msi"): + answer = QMessageBox.question( + self, "Run the installer?", + f"Run {path.name}?\n\nOnly continue if the release came from the " + "BoltClam repository and the checksum was verified.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No) + if answer != QMessageBox.StandardButton.Yes: + return + target = path if path.suffix.lower() in (".exe", ".msi") else path.parent + if not paths.open_path(target): + self.toast_requested.emit( + "Could not open the download", str(target), "warning") + + def stop(self) -> None: + for worker in (self._check_worker, self._download_worker): + stop_thread(worker, wait_ms=3500) + self._check_worker = None + self._download_worker = None diff --git a/src/boltclam/ui/brand.py b/src/boltclam/ui/brand.py new file mode 100644 index 0000000..dc12a8f --- /dev/null +++ b/src/boltclam/ui/brand.py @@ -0,0 +1,110 @@ +"""The BoltClam mark: a scalloped shell with a bolt at its heart.""" + +from __future__ import annotations + +import math + +from PyQt6.QtCore import QPointF, QRectF, Qt +from PyQt6.QtGui import ( + QBrush, + QColor, + QIcon, + QLinearGradient, + QPainter, + QPainterPath, + QPen, + QPixmap, + QPolygonF, +) + + +def paint_mark(painter: QPainter, rect: QRectF, *, rounded: bool = True) -> None: + painter.save() + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + size = min(rect.width(), rect.height()) + square = QRectF(rect.center().x() - size / 2, rect.center().y() - size / 2, size, size) + + if rounded: + background = QLinearGradient(square.topLeft(), square.bottomRight()) + background.setColorAt(0.0, QColor("#123542")) + background.setColorAt(0.55, QColor("#0B202B")) + background.setColorAt(1.0, QColor("#08151D")) + painter.setBrush(QBrush(background)) + painter.setPen(QPen(QColor(89, 216, 201, 70), max(1.0, size * 0.012))) + painter.drawRoundedRect(square.adjusted(1, 1, -1, -1), size * 0.24, size * 0.24) + + # Scallop shell: ribs fan upward from the hinge near the bottom. + cx = square.center().x() + hinge = QPointF(cx, square.top() + size * 0.80) + spread = size * 0.36 + top = square.top() + size * 0.25 + ribs = 7 + for index in range(ribs): + fraction = index / (ribs - 1) + tip_x = cx - spread + fraction * spread * 2 + tip = QPointF(tip_x, top + abs(fraction - 0.5) * size * 0.11) + control_a = QPointF(hinge.x() + (tip_x - cx) * 0.10, hinge.y() - size * 0.18) + control_b = QPointF(tip_x + (cx - tip_x) * 0.25, top + size * 0.12) + path = QPainterPath(hinge) + path.cubicTo(control_a, control_b, tip) + alpha = 95 + int(70 * (1 - abs(fraction - 0.5) * 1.4)) + painter.setPen(QPen(QColor(122, 244, 255, max(40, alpha)), + max(1.0, size * 0.014 * (1.25 - abs(fraction - 0.5))), + Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.drawPath(path) + + # Hinge band. + painter.setPen(QPen(QColor(89, 216, 201, 140), max(1.0, size * 0.018), + Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) + painter.drawArc(QRectF(cx - spread * 0.7, hinge.y() - size * 0.04, + spread * 1.4, size * 0.12), 200 * 16, 140 * 16) + + # Bolt at the heart of the shell. + bolt = QPolygonF([ + QPointF(cx + size * 0.045, square.top() + size * 0.31), + QPointF(cx - size * 0.115, square.top() + size * 0.535), + QPointF(cx - size * 0.010, square.top() + size * 0.535), + QPointF(cx - size * 0.060, square.top() + size * 0.705), + QPointF(cx + size * 0.120, square.top() + size * 0.470), + QPointF(cx + size * 0.012, square.top() + size * 0.470), + ]) + bolt_gradient = QLinearGradient(QPointF(cx, square.top() + size * 0.30), + QPointF(cx, square.top() + size * 0.72)) + bolt_gradient.setColorAt(0.0, QColor("#FFE3A6")) + bolt_gradient.setColorAt(1.0, QColor("#F2B84B")) + painter.setBrush(QBrush(bolt_gradient)) + painter.setPen(QPen(QColor(255, 227, 166, 180), max(0.8, size * 0.008))) + painter.drawPolygon(bolt) + painter.restore() + + +def pixmap(size: int = 256) -> QPixmap: + image = QPixmap(size, size) + image.fill(Qt.GlobalColor.transparent) + painter = QPainter(image) + paint_mark(painter, QRectF(0, 0, size, size)) + painter.end() + return image + + +def app_icon() -> QIcon: + icon = QIcon() + for size in (16, 24, 32, 48, 64, 128, 256): + icon.addPixmap(pixmap(size)) + return icon + + +def bolt_path(rect: QRectF) -> QPainterPath: + cx = rect.center().x() + top = rect.top() + size = min(rect.width(), rect.height()) + path = QPainterPath() + path.moveTo(cx + size * 0.07, top + size * 0.05) + path.lineTo(cx - size * 0.23, top + size * 0.53) + path.lineTo(cx - size * 0.01, top + size * 0.53) + path.lineTo(cx - size * 0.11, top + size * 0.95) + path.lineTo(cx + size * 0.25, top + size * 0.42) + path.lineTo(cx + size * 0.02, top + size * 0.42) + path.closeSubpath() + return path diff --git a/src/boltclam/ui/components.py b/src/boltclam/ui/components.py new file mode 100644 index 0000000..44a9ba2 --- /dev/null +++ b/src/boltclam/ui/components.py @@ -0,0 +1,790 @@ +"""Reusable painted and composed widgets for the BoltClam interface.""" + +from __future__ import annotations + +import math +from typing import Iterable + +from PyQt6.QtCore import QPointF, QRectF, QSize, Qt, QTimer, pyqtSignal +from PyQt6.QtGui import ( + QBrush, + QColor, + QFont, + QFontMetrics, + QLinearGradient, + QPainter, + QPainterPath, + QPen, + QPolygonF, + QRadialGradient, +) +from PyQt6.QtWidgets import ( + QAbstractButton, + QFrame, + QHBoxLayout, + QLabel, + QPushButton, + QRadioButton, + QSizePolicy, + QTextBrowser, + QVBoxLayout, + QWidget, +) + +from . import brand, theme + + +class Card(QFrame): + def __init__(self, parent: QWidget | None = None, *, + margins: tuple[int, int, int, int] = (18, 18, 18, 18), + spacing: int = 12) -> None: + super().__init__(parent) + self.setObjectName("Card") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + self.body = QVBoxLayout(self) + self.body.setContentsMargins(*margins) + self.body.setSpacing(spacing) + + +def card_header(title: str, subtitle: str | None = None, + right: QWidget | None = None) -> tuple[QWidget, QVBoxLayout]: + holder = QWidget() + layout = QHBoxLayout(holder) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(12) + text_box = QVBoxLayout() + text_box.setContentsMargins(0, 0, 0, 0) + text_box.setSpacing(2) + title_label = QLabel(title) + title_label.setObjectName("CardTitle") + title_label.setWordWrap(True) + text_box.addWidget(title_label) + if subtitle: + subtitle_label = QLabel(subtitle) + subtitle_label.setObjectName("Muted") + subtitle_label.setWordWrap(True) + subtitle_label.setFont(theme.body_font(9.2)) + text_box.addWidget(subtitle_label) + layout.addLayout(text_box, 1) + if right is not None: + layout.addWidget(right, 0, Qt.AlignmentFlag.AlignTop) + return holder, text_box + + +class Pill(QLabel): + def __init__(self, text: str = "", severity: str = "info", parent=None) -> None: + super().__init__(text, parent) + self.setObjectName("Pill") + self.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.set_severity(severity) + + def set_severity(self, severity: str) -> None: + self.setProperty("severity", severity or "info") + self.style().unpolish(self) + self.style().polish(self) + + +class SeverityDot(QWidget): + def __init__(self, severity: str = "info", size: int = 9, parent=None) -> None: + super().__init__(parent) + self._severity = severity + self._size = size + self.setFixedSize(size, size) + + def set_severity(self, severity: str) -> None: + self._severity = severity + self.update() + + def paintEvent(self, event) -> None: + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + color = QColor(theme.severity_color(self._severity)) + glow = QColor(color) + glow.setAlpha(55) + painter.setPen(Qt.PenStyle.NoPen) + painter.setBrush(glow) + painter.drawEllipse(QRectF(0, 0, self._size, self._size)) + painter.setBrush(color) + inset = self._size * 0.22 + painter.drawEllipse(QRectF(inset, inset, self._size - inset * 2, + self._size - inset * 2)) + + +class Avatar(QWidget): + def __init__(self, name: str = "", size: int = 40, parent=None) -> None: + super().__init__(parent) + self._name = name or "?" + self._size = size + self.setFixedSize(size, size) + + def set_name(self, name: str) -> None: + self._name = name or "?" + self.update() + + def initials(self) -> str: + parts = [part for part in str(self._name).replace("@", " ").split() if part] + if not parts: + return "?" + if len(parts) == 1: + return parts[0][:1].upper() + return (parts[0][0] + parts[-1][0]).upper() + + def paintEvent(self, event) -> None: + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + rect = QRectF(1, 1, self._size - 2, self._size - 2) + gradient = QLinearGradient(rect.topLeft(), rect.bottomRight()) + gradient.setColorAt(0, QColor("#1C4A55")) + gradient.setColorAt(1, QColor("#0E2630")) + painter.setBrush(gradient) + painter.setPen(QPen(QColor(89, 216, 201, 90), 1)) + painter.drawEllipse(rect) + painter.setPen(QColor(theme.AMBER_BRIGHT)) + font = theme.display_font(self._size * 0.30, QFont.Weight.DemiBold) + painter.setFont(font) + painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, self.initials()) + + +class ProtectionDial(QWidget): + """The homepage hero: a scalloped shell whose four arcs encode state.""" + + _LABELS = ("Engine", "Definitions", "Real-time", "System") + + def __init__(self, parent=None) -> None: + super().__init__(parent) + self.setMinimumSize(248, 248) + self.setMaximumSize(286, 286) + self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) + self._overall = "setup" + self._segments: list[dict] = [] + self._headline = "Checking" + self.setToolTip("ClamAV engine, signature database, real-time watcher and system firewall") + + def sizeHint(self) -> QSize: + return QSize(270, 270) + + def set_snapshot(self, snapshot: dict | None) -> None: + self._overall = str((snapshot or {}).get("overall") or "setup") + self._segments = list((snapshot or {}).get("segments") or []) + self._headline = str((snapshot or {}).get("headline") or "Checking") + self.update() + + def paintEvent(self, event) -> None: + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + side = min(self.width(), self.height()) + rect = QRectF((self.width() - side) / 2 + 8, (self.height() - side) / 2 + 8, + side - 16, side - 16) + center = rect.center() + radius = rect.width() / 2 + overall = QColor(theme.overall_color(self._overall)) + + glow = QRadialGradient(center, radius) + glow.setColorAt(0.0, QColor(overall.red(), overall.green(), overall.blue(), 36)) + glow.setColorAt(0.62, QColor(overall.red(), overall.green(), overall.blue(), 8)) + glow.setColorAt(1.0, QColor(0, 0, 0, 0)) + painter.setBrush(glow) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawEllipse(rect) + + disk = QRadialGradient(center - QPointF(radius * 0.2, radius * 0.25), radius * 1.15) + disk.setColorAt(0.0, QColor("#13313D")) + disk.setColorAt(0.7, QColor("#0B1F29")) + disk.setColorAt(1.0, QColor("#08151D")) + painter.setBrush(disk) + painter.setPen(QPen(QColor(27, 57, 70), 1)) + painter.drawEllipse(rect) + + arc_rect = rect.adjusted(18, 18, -18, -18) + track_pen = QPen(QColor(28, 59, 71), 9) + track_pen.setCapStyle(Qt.PenCapStyle.RoundCap) + painter.setPen(track_pen) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.drawArc(arc_rect, 210 * 16, -300 * 16) + + states = [str(item.get("state") or "unknown") for item in self._segments] + states += ["unknown"] * (4 - len(states)) + for index in range(4): + color = QColor(theme.severity_color(states[index])) + if self._overall == "setup": + color = QColor(theme.DIM) + pen = QPen(color, 9) + pen.setCapStyle(Qt.PenCapStyle.RoundCap) + painter.setPen(pen) + start = 210 - index * 75 + 2 + span = -71 + painter.drawArc(arc_rect, int(start * 16), int(span * 16)) + + # Shell ribs inside the gauge. + painter.save() + painter.setClipPath(self._ellipse_path(rect.adjusted(10, 10, -10, -10))) + hinge = QPointF(center.x(), center.y() + radius * 0.52) + top = center.y() - radius * 0.60 + spread = radius * 0.57 + for index in range(9): + fraction = index / 8 + tip_x = center.x() - spread + fraction * spread * 2 + tip = QPointF(tip_x, top + abs(fraction - 0.5) * radius * 0.18) + path = QPainterPath(hinge) + path.cubicTo(QPointF(center.x() + (tip_x - center.x()) * 0.08, + hinge.y() - radius * 0.28), + QPointF(tip_x + (center.x() - tip_x) * 0.22, top + radius * 0.22), + tip) + painter.setPen(QPen(QColor(122, 244, 255, 20), 1.2)) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.drawPath(path) + painter.restore() + + core_radius = radius * 0.40 + core_rect = QRectF(center.x() - core_radius, center.y() - core_radius, + core_radius * 2, core_radius * 2) + core = QRadialGradient(center - QPointF(core_radius * 0.25, core_radius * 0.3), + core_radius * 1.6) + core.setColorAt(0.0, QColor("#143743")) + core.setColorAt(1.0, QColor("#0A1C25")) + painter.setBrush(core) + painter.setPen(QPen(QColor(overall.red(), overall.green(), overall.blue(), 130), 1.4)) + painter.drawEllipse(core_rect) + + # Shield outline with the amber bolt inside. + shield = QPainterPath() + left = center.x() - core_radius * 0.46 + right = center.x() + core_radius * 0.46 + top_y = center.y() - core_radius * 0.55 + bottom_y = center.y() + core_radius * 0.62 + shield.moveTo(center.x(), top_y) + shield.lineTo(right, top_y + core_radius * 0.14) + shield.lineTo(right, center.y() + core_radius * 0.10) + shield.cubicTo(right, center.y() + core_radius * 0.48, + center.x() + core_radius * 0.18, bottom_y, + center.x(), bottom_y) + shield.cubicTo(center.x() - core_radius * 0.18, bottom_y, + left, center.y() + core_radius * 0.48, + left, center.y() + core_radius * 0.10) + shield.lineTo(left, top_y + core_radius * 0.14) + shield.closeSubpath() + painter.setPen(QPen(QColor(overall.red(), overall.green(), overall.blue(), 190), 1.6)) + painter.setBrush(QColor(overall.red(), overall.green(), overall.blue(), 14)) + painter.drawPath(shield) + bolt_rect = QRectF(center.x() - core_radius * 0.44, top_y + core_radius * 0.08, + core_radius * 0.88, core_radius * 0.92) + painter.setBrush(QColor(theme.AMBER)) + painter.setPen(QPen(QColor(theme.AMBER_BRIGHT), 0.8)) + painter.drawPath(brand.bolt_path(bolt_rect)) + + # The hero copy already states the status; a second caption here + # collided with the shell's lower arc at 1360px. + + @staticmethod + def _ellipse_path(rect: QRectF) -> QPainterPath: + path = QPainterPath() + path.addEllipse(rect) + return path + + +class NavButton(QAbstractButton): + _ICONS = ("home", "scan", "updates", "settings") + + def __init__(self, icon_name: str, text: str, parent=None) -> None: + super().__init__(parent) + self.icon_name = icon_name + self.setText(text) + self.setCheckable(True) + self.setCursor(Qt.CursorShape.PointingHandCursor) + self.setAttribute(Qt.WidgetAttribute.WA_Hover, True) + self.setMinimumHeight(46) + self._hover = False + + def sizeHint(self) -> QSize: + return QSize(196, 46) + + def enterEvent(self, event) -> None: + self._hover = True + self.update() + super().enterEvent(event) + + def leaveEvent(self, event) -> None: + self._hover = False + self.update() + super().leaveEvent(event) + + def paintEvent(self, event) -> None: + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + rect = QRectF(0, 2, self.width(), self.height() - 4) + checked = self.isChecked() + if checked: + painter.setBrush(QColor(242, 184, 75, 20)) + painter.setPen(QPen(QColor(242, 184, 75, 65), 1)) + painter.drawRoundedRect(rect, 10, 10) + painter.setPen(Qt.PenStyle.NoPen) + painter.setBrush(QColor(theme.AMBER)) + painter.drawRoundedRect(QRectF(0, 13, 3, self.height() - 26), 1.5, 1.5) + elif self._hover: + painter.setBrush(QColor(255, 255, 255, 8)) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawRoundedRect(rect, 10, 10) + + color = QColor(theme.AMBER_BRIGHT if checked else theme.MUTED) + icon_rect = QRectF(18, (self.height() - 18) / 2, 18, 18) + _draw_nav_icon(painter, self.icon_name, icon_rect, color) + painter.setPen(QColor(theme.TEXT if checked else theme.TEXT_SOFT)) + font = theme.body_font(10.4, QFont.Weight.DemiBold if checked else QFont.Weight.Normal) + painter.setFont(font) + painter.drawText(QRectF(46, 0, self.width() - 52, self.height()), + Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft, + self.text()) + + +def _draw_nav_icon(painter: QPainter, name: str, rect: QRectF, color: QColor) -> None: + painter.save() + pen = QPen(color, 1.6, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, + Qt.PenJoinStyle.RoundJoin) + painter.setPen(pen) + painter.setBrush(Qt.BrushStyle.NoBrush) + if name == "home": + path = QPainterPath() + path.moveTo(rect.left() + 1, rect.top() + rect.height() * 0.45) + path.lineTo(rect.center().x(), rect.top() + 1) + path.lineTo(rect.right() - 1, rect.top() + rect.height() * 0.45) + painter.drawPath(path) + painter.drawRoundedRect(QRectF(rect.left() + 3, rect.top() + rect.height() * 0.44, + rect.width() - 6, rect.height() * 0.48), 2, 2) + painter.drawLine(QPointF(rect.center().x(), rect.bottom() - 3), + QPointF(rect.center().x(), rect.bottom() - 8)) + elif name == "scan": + inset = 2.0 + length = 5.0 + for x, y, dx, dy in ( + (rect.left(), rect.top(), 1, 1), + (rect.right(), rect.top(), -1, 1), + (rect.left(), rect.bottom(), 1, -1), + (rect.right(), rect.bottom(), -1, -1)): + path = QPainterPath(QPointF(x + dx * inset, y + dy * (inset + length))) + path.lineTo(x + dx * inset, y + dy * inset) + path.lineTo(x + dx * (inset + length), y + dy * inset) + painter.drawPath(path) + painter.drawEllipse(QRectF(rect.center().x() - 2.2, rect.center().y() - 2.2, 4.4, 4.4)) + painter.drawLine(QPointF(rect.center().x(), rect.top() + 6), + QPointF(rect.center().x(), rect.top() + 1)) + painter.drawLine(QPointF(rect.center().x(), rect.bottom() - 1), + QPointF(rect.center().x(), rect.bottom() - 6)) + elif name == "updates": + painter.drawArc(QRectF(rect.left() + 2, rect.top() + 2, + rect.width() - 4, rect.height() - 4), 40 * 16, 280 * 16) + arrow = QPolygonF([ + QPointF(rect.right() - 3, rect.top() + 3), + QPointF(rect.right() - 3, rect.top() + 9), + QPointF(rect.right() - 9, rect.top() + 3), + ]) + painter.setBrush(color) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawPolygon(arrow) + elif name == "settings": + for index, y in enumerate((rect.top() + 4, rect.center().y(), rect.bottom() - 4)): + painter.drawLine(QPointF(rect.left() + 1, y), QPointF(rect.right() - 1, y)) + knob_x = rect.left() + rect.width() * (0.34 if index == 0 else 0.66 if index == 1 else 0.46) + painter.setBrush(QColor(theme.BG)) + painter.setPen(QPen(color, 1.5)) + painter.drawEllipse(QPointF(knob_x, y), 2.3, 2.3) + painter.restore() + + +class ChoiceTile(QRadioButton): + def __init__(self, title: str, description: str, parent=None) -> None: + super().__init__(parent) + self.setObjectName("ChoiceTile") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + self.setCursor(Qt.CursorShape.PointingHandCursor) + layout = QVBoxLayout(self) + layout.setContentsMargins(36, 12, 14, 12) + layout.setSpacing(3) + title_label = QLabel(title) + title_label.setFont(theme.body_font(10.3, QFont.Weight.DemiBold)) + title_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True) + desc_label = QLabel(description) + desc_label.setObjectName("Muted") + desc_label.setFont(theme.body_font(8.8)) + desc_label.setWordWrap(True) + desc_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents, True) + layout.addWidget(title_label) + layout.addWidget(desc_label) + self._title_label = title_label + self._desc_label = desc_label + self.setMinimumHeight(82) + + def sizeHint(self) -> QSize: + return QSize(250, 82) + + +class ScanButton(QAbstractButton): + def __init__(self, parent=None) -> None: + super().__init__(parent) + self.setFixedSize(184, 184) + self.setCursor(Qt.CursorShape.PointingHandCursor) + self.setAttribute(Qt.WidgetAttribute.WA_Hover, True) + self._scanning = False + self._hover = False + self._angle = 0 + self._timer = QTimer(self) + self._timer.setInterval(45) + self._timer.timeout.connect(self._tick) + + def sizeHint(self) -> QSize: + return QSize(184, 184) + + def set_scanning(self, scanning: bool) -> None: + self._scanning = bool(scanning) + if scanning: + if not self._timer.isActive(): + self._timer.start() + self.setCursor(Qt.CursorShape.BusyCursor) + else: + self._timer.stop() + self.setCursor(Qt.CursorShape.PointingHandCursor) + self.update() + + def enterEvent(self, event) -> None: + self._hover = True + self.update() + super().enterEvent(event) + + def leaveEvent(self, event) -> None: + self._hover = False + self.update() + super().leaveEvent(event) + + def _tick(self) -> None: + self._angle = (self._angle + 12) % 360 + self.update() + + def paintEvent(self, event) -> None: + painter = QPainter(self) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + side = min(self.width(), self.height()) + rect = QRectF((self.width() - side) / 2 + 6, (self.height() - side) / 2 + 6, + side - 12, side - 12) + center = rect.center() + radius = rect.width() / 2 + + glow = QRadialGradient(center, radius) + glow.setColorAt(0, QColor(242, 184, 75, 40 if self.isEnabled() else 10)) + glow.setColorAt(1, QColor(242, 184, 75, 0)) + painter.setBrush(glow) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawEllipse(rect) + + painter.setBrush(QColor("#0B1E27")) + painter.setPen(QPen(QColor(theme.BORDER), 1)) + painter.drawEllipse(rect.adjusted(10, 10, -10, -10)) + + ring = rect.adjusted(20, 20, -20, -20) + painter.setBrush(Qt.BrushStyle.NoBrush) + painter.setPen(QPen(QColor(33, 66, 79), 7)) + painter.drawArc(ring, 0, 360 * 16) + if self._scanning: + pen = QPen(QColor(theme.AMBER), 7) + pen.setCapStyle(Qt.PenCapStyle.RoundCap) + painter.setPen(pen) + painter.drawArc(ring, -self._angle * 16, -86 * 16) + else: + pen = QPen(QColor(theme.TEAL if self.isEnabled() else theme.DIM), 3) + pen.setCapStyle(Qt.PenCapStyle.RoundCap) + painter.setPen(pen) + for index in range(4): + painter.drawArc(ring, (index * 90 + 14) * 16, 54 * 16) + + core = QRectF(center.x() - 57, center.y() - 57, 114, 114) + gradient = QLinearGradient(core.topLeft(), core.bottomRight()) + if self.isEnabled(): + gradient.setColorAt(0, QColor("#FFD37A" if self._hover else "#F6C35B")) + gradient.setColorAt(1, QColor("#E7A635")) + else: + gradient.setColorAt(0, QColor("#3A3A30")) + gradient.setColorAt(1, QColor("#2A2A22")) + painter.setBrush(gradient) + painter.setPen(QPen(QColor(255, 227, 166, 130 if self.isEnabled() else 30), 1)) + painter.drawEllipse(core) + + icon_rect = QRectF(center.x() - 21, center.y() - 34, 42, 42) + painter.setPen(QPen(QColor("#2A1A04" if self.isEnabled() else "#686250"), 1.7)) + painter.setBrush(Qt.BrushStyle.NoBrush) + shield = QPainterPath() + shield.moveTo(icon_rect.center().x(), icon_rect.top()) + shield.lineTo(icon_rect.right(), icon_rect.top() + icon_rect.height() * 0.16) + shield.lineTo(icon_rect.right(), icon_rect.top() + icon_rect.height() * 0.48) + shield.cubicTo(icon_rect.right(), icon_rect.top() + icon_rect.height() * 0.82, + icon_rect.center().x() + icon_rect.width() * 0.18, icon_rect.bottom(), + icon_rect.center().x(), icon_rect.bottom()) + shield.cubicTo(icon_rect.center().x() - icon_rect.width() * 0.18, icon_rect.bottom(), + icon_rect.left(), icon_rect.top() + icon_rect.height() * 0.82, + icon_rect.left(), icon_rect.top() + icon_rect.height() * 0.48) + shield.lineTo(icon_rect.left(), icon_rect.top() + icon_rect.height() * 0.16) + shield.closeSubpath() + painter.drawPath(shield) + bolt_rect = QRectF(center.x() - 16, icon_rect.top() + 7, 32, 30) + painter.setBrush(QColor("#2A1A04" if self.isEnabled() else "#686250")) + painter.setPen(Qt.PenStyle.NoPen) + painter.drawPath(brand.bolt_path(bolt_rect)) + + painter.setPen(QColor("#241704" if self.isEnabled() else "#807A68")) + painter.setFont(theme.body_font(11.2, QFont.Weight.Bold)) + painter.drawText(QRectF(core.left(), core.center().y() + 20, core.width(), 24), + Qt.AlignmentFlag.AlignCenter, + "Scanning…" if self._scanning else "Start scan") + + +class StatTile(QFrame): + def __init__(self, label: str, value: str = "—", note: str = "", + accent: str = theme.TEAL, parent=None) -> None: + super().__init__(parent) + self.setObjectName("Card") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + layout = QVBoxLayout(self) + layout.setContentsMargins(16, 14, 16, 14) + layout.setSpacing(4) + top = QHBoxLayout() + top.setSpacing(7) + dot = SeverityDot("good", 7) + dot.setStyleSheet(f"background: transparent;") + top.addWidget(dot) + label_widget = QLabel(label) + label_widget.setObjectName("StatLabel") + top.addWidget(label_widget, 1) + layout.addLayout(top) + self.value_label = QLabel(value) + self.value_label.setObjectName("StatValue") + layout.addWidget(self.value_label) + self.note_label = QLabel(note) + self.note_label.setObjectName("Dim") + self.note_label.setWordWrap(True) + layout.addWidget(self.note_label) + self._dot = dot + self.set_accent(accent) + + def set_accent(self, severity: str) -> None: + self._dot.set_severity(severity) + + def set_value(self, value: str, note: str | None = None) -> None: + self.value_label.setText(value) + if note is not None: + self.note_label.setText(note) + + +class ConcernRow(QFrame): + action_requested = pyqtSignal(str) + + _ACTION_LABELS = { + "settings": "Open settings", + "scan": "Review scan", + "update_definitions": "Update", + "quarantine": "Review", + "updates": "Check updates", + } + + def __init__(self, concern: dict, parent=None) -> None: + super().__init__(parent) + self.setObjectName("ConcernRow") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + self._action = str(concern.get("action") or "none") + layout = QHBoxLayout(self) + layout.setContentsMargins(4, 9, 2, 9) + layout.setSpacing(11) + dot = SeverityDot(str(concern.get("severity") or "info"), 9) + layout.addWidget(dot, 0, Qt.AlignmentFlag.AlignTop) + text_box = QVBoxLayout() + text_box.setContentsMargins(0, 0, 0, 0) + text_box.setSpacing(2) + title = QLabel(str(concern.get("title") or "Security note")) + title.setFont(theme.body_font(10.2, QFont.Weight.DemiBold)) + title.setWordWrap(True) + detail = QLabel(str(concern.get("detail") or "")) + detail.setObjectName("Muted") + detail.setFont(theme.body_font(8.9)) + detail.setWordWrap(True) + text_box.addWidget(title) + text_box.addWidget(detail) + layout.addLayout(text_box, 1) + label = self._ACTION_LABELS.get(self._action) + if label: + button = QPushButton(label) + button.setObjectName("Link") + button.setCursor(Qt.CursorShape.PointingHandCursor) + button.clicked.connect(lambda: self.action_requested.emit(self._action)) + layout.addWidget(button, 0, Qt.AlignmentFlag.AlignVCenter) + + +class PageHeader(QWidget): + def __init__(self, title: str, subtitle: str = "", parent=None) -> None: + super().__init__(parent) + layout = QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(18) + text_box = QVBoxLayout() + text_box.setContentsMargins(0, 0, 0, 0) + text_box.setSpacing(4) + self.title_label = QLabel(title) + self.title_label.setObjectName("PageTitle") + self.subtitle_label = QLabel(subtitle) + self.subtitle_label.setObjectName("PageSubtitle") + self.subtitle_label.setWordWrap(True) + text_box.addWidget(self.title_label) + text_box.addWidget(self.subtitle_label) + layout.addLayout(text_box, 1) + self.actions = QHBoxLayout() + self.actions.setSpacing(8) + layout.addLayout(self.actions, 0) + + def set_subtitle(self, text: str) -> None: + self.subtitle_label.setText(text) + + def add_action(self, widget: QWidget) -> None: + self.actions.addWidget(widget, 0, Qt.AlignmentFlag.AlignVCenter) + + +class KeyValueRow(QWidget): + def __init__(self, label: str, value: str = "—", *, mono: bool = False, + parent=None) -> None: + super().__init__(parent) + layout = QHBoxLayout(self) + layout.setContentsMargins(0, 3, 0, 3) + layout.setSpacing(12) + self.label = QLabel(label) + self.label.setObjectName("Muted") + self.label.setFont(theme.body_font(8.9)) + layout.addWidget(self.label, 0) + self.value_label = QLabel(value) + self.value_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) + self.value_label.setWordWrap(True) + if mono: + self.value_label.setObjectName("Mono") + else: + self.value_label.setFont(theme.body_font(9.4, QFont.Weight.DemiBold)) + layout.addWidget(self.value_label, 1) + + def set_value(self, value: str) -> None: + self.value_label.setText(value or "—") + + +class EmptyState(QWidget): + action_clicked = pyqtSignal() + + def __init__(self, title: str, body: str, action: str | None = None, parent=None) -> None: + super().__init__(parent) + layout = QVBoxLayout(self) + layout.setContentsMargins(20, 30, 20, 30) + layout.setSpacing(8) + layout.setAlignment(Qt.AlignmentFlag.AlignCenter) + title_label = QLabel(title) + title_label.setFont(theme.display_font(15, QFont.Weight.DemiBold)) + title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + body_label = QLabel(body) + body_label.setObjectName("Muted") + body_label.setWordWrap(True) + body_label.setAlignment(Qt.AlignmentFlag.AlignCenter) + body_label.setMaximumWidth(420) + layout.addWidget(title_label) + layout.addWidget(body_label, 0, Qt.AlignmentFlag.AlignHCenter) + if action: + button = QPushButton(action) + button.setObjectName("Secondary") + button.setCursor(Qt.CursorShape.PointingHandCursor) + button.clicked.connect(self.action_clicked) + layout.addWidget(button, 0, Qt.AlignmentFlag.AlignHCenter) + + +class MarkdownView(QTextBrowser): + def __init__(self, parent=None) -> None: + super().__init__(parent) + self.setOpenExternalLinks(True) + self.setReadOnly(True) + self.setFrameShape(QFrame.Shape.NoFrame) + self.setFont(theme.body_font(9.4)) + self.document().setDefaultStyleSheet( + f"h1,h2,h3{{font-family:'{theme.FONT_DISPLAY}';color:{theme.TEXT};}}" + f"a{{color:{theme.TEAL};}} code{{font-family:'{theme.FONT_MONO}';" + f"background:{theme.BG_DEEP};color:{theme.AMBER_BRIGHT};}}" + f"li{{margin-bottom:4px;}}" + ) + + def set_markdown(self, text: str) -> None: + self.setMarkdown(str(text or "")) + + +class ElidedLabel(QLabel): + def __init__(self, text: str = "", parent=None) -> None: + super().__init__(parent) + self._full_text = text + self.setToolTip(text) + self.setText(text) + + def setText(self, text: str) -> None: # noqa: N802 + self._full_text = str(text or "") + self.setToolTip(self._full_text) + self._elide() + + def resizeEvent(self, event) -> None: + super().resizeEvent(event) + self._elide() + + def _elide(self) -> None: + metrics = QFontMetrics(self.font()) + available = max(20, self.width()) + super().setText(metrics.elidedText(self._full_text, Qt.TextElideMode.ElideMiddle, + available)) + + +class Toast(QFrame): + def __init__(self, parent: QWidget) -> None: + super().__init__(parent) + self.setObjectName("Toast") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, True) + self.setFocusPolicy(Qt.FocusPolicy.NoFocus) + self.setFixedWidth(370) + layout = QHBoxLayout(self) + layout.setContentsMargins(14, 12, 14, 12) + layout.setSpacing(10) + self.dot = SeverityDot("info", 9) + layout.addWidget(self.dot, 0, Qt.AlignmentFlag.AlignTop) + text_box = QVBoxLayout() + text_box.setContentsMargins(0, 0, 0, 0) + text_box.setSpacing(2) + self.title_label = QLabel("") + self.title_label.setObjectName("ToastTitle") + self.body_label = QLabel("") + self.body_label.setObjectName("ToastBody") + self.body_label.setWordWrap(True) + text_box.addWidget(self.title_label) + text_box.addWidget(self.body_label) + layout.addLayout(text_box, 1) + self._timer = QTimer(self) + self._timer.setSingleShot(True) + self._timer.timeout.connect(self.hide) + self.hide() + + def show_message(self, title: str, body: str = "", severity: str = "info", + duration_ms: int = 7000) -> None: + self.title_label.setText(title) + self.body_label.setText(body) + self.dot.set_severity(severity) + self.adjustSize() + parent = self.parentWidget() + if parent is not None: + x = max(20, parent.width() - self.width() - 26) + y = max(20, parent.height() - self.height() - 26) + self.move(x, y) + self.raise_() + self.show() + self._timer.start(max(2500, duration_ms)) + + def mousePressEvent(self, event) -> None: + self.hide() + super().mousePressEvent(event) + + +class Divider(QFrame): + def __init__(self, parent=None) -> None: + super().__init__(parent) + self.setObjectName("Divider") + self.setFixedHeight(1) diff --git a/src/boltclam/ui/home_page.py b/src/boltclam/ui/home_page.py new file mode 100644 index 0000000..4025ad8 --- /dev/null +++ b/src/boltclam/ui/home_page.py @@ -0,0 +1,570 @@ +"""Homepage: the shell gauge, security concerns, network and account summary.""" + +from __future__ import annotations + +import logging +from pathlib import Path + +from PyQt6.QtCore import QThread, Qt, pyqtSignal +from PyQt6.QtGui import QFont +from PyQt6.QtWidgets import ( + QFrame, + QGridLayout, + QHBoxLayout, + QLabel, + QPushButton, + QScrollArea, + QSizePolicy, + QVBoxLayout, + QWidget, +) + +from ..context import AppContext +from ..models import display_name, human_action +from ..system_info import collect +from . import components, theme +from .threads import stop_thread + +logger = logging.getLogger(__name__) + + +class SystemInfoWorker(QThread): + finished_snapshot = pyqtSignal(object) + failed = pyqtSignal(str) + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + + def run(self) -> None: + try: + realtime_running = None + if self.context.realtime is not None: + realtime_running = self.context.realtime.running + snapshot = collect( + self.context.settings, self.context.clamav.info(), self.context.storage, + realtime_running=realtime_running, force=True) + self.finished_snapshot.emit(snapshot) + except Exception as exc: + logger.exception("System overview failed") + self.failed.emit(str(exc)) + + +class SegmentChip(QFrame): + def __init__(self, parent=None) -> None: + super().__init__(parent) + self.setObjectName("SegmentChip") + self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + layout = QHBoxLayout(self) + layout.setContentsMargins(10, 8, 10, 8) + layout.setSpacing(8) + self.dot = components.SeverityDot("unknown", 8) + layout.addWidget(self.dot, 0, Qt.AlignmentFlag.AlignTop) + text_box = QVBoxLayout() + text_box.setSpacing(1) + self.label = QLabel("—") + self.label.setFont(theme.body_font(8.8, QFont.Weight.DemiBold)) + self.detail = QLabel("") + self.detail.setObjectName("Dim") + self.detail.setFont(theme.body_font(8.0)) + self.detail.setWordWrap(True) + text_box.addWidget(self.label) + text_box.addWidget(self.detail) + layout.addLayout(text_box, 1) + + def set_segment(self, segment: dict) -> None: + self.dot.set_severity(str(segment.get("state") or "unknown")) + self.label.setText(str(segment.get("label") or "—")) + self.detail.setText(str(segment.get("detail") or "")) + + +class ActivityRow(QWidget): + def __init__(self, severity: str, title: str, subtitle: str, when: str, + parent=None) -> None: + super().__init__(parent) + layout = QHBoxLayout(self) + layout.setContentsMargins(0, 7, 0, 7) + layout.setSpacing(10) + layout.addWidget(components.SeverityDot(severity, 8), 0, Qt.AlignmentFlag.AlignTop) + text_box = QVBoxLayout() + text_box.setSpacing(1) + title_label = QLabel(title) + title_label.setFont(theme.body_font(9.5, QFont.Weight.DemiBold)) + title_label.setWordWrap(False) + subtitle_label = components.ElidedLabel(subtitle) + subtitle_label.setObjectName("Muted") + subtitle_label.setFont(theme.body_font(8.5)) + text_box.addWidget(title_label) + text_box.addWidget(subtitle_label) + layout.addLayout(text_box, 1) + time_label = QLabel(when) + time_label.setObjectName("Dim") + time_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop) + layout.addWidget(time_label, 0) + + +class HomePage(QWidget): + scan_requested = pyqtSignal() + settings_requested = pyqtSignal() + quarantine_requested = pyqtSignal() + updates_requested = pyqtSignal() + definitions_requested = pyqtSignal() + snapshot_changed = pyqtSignal(object) + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + self._worker: SystemInfoWorker | None = None + self._snapshot: dict | None = None + self._segments: list[SegmentChip] = [] + self._build() + self.refresh() + + # -- construction ------------------------------------------------------ + + def _build(self) -> None: + outer = QVBoxLayout(self) + outer.setContentsMargins(0, 0, 0, 0) + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + outer.addWidget(scroll) + content = QWidget() + content.setObjectName("Content") + scroll.setWidget(content) + layout = QVBoxLayout(content) + layout.setContentsMargins(30, 24, 30, 30) + layout.setSpacing(16) + + header = components.PageHeader( + "Homepage", "A live view of this PC's protection, network, and account.") + refresh_button = QPushButton("Refresh") + refresh_button.setObjectName("Secondary") + refresh_button.setCursor(Qt.CursorShape.PointingHandCursor) + refresh_button.clicked.connect(lambda: self.refresh(force=True)) + header.add_action(refresh_button) + layout.addWidget(header) + + self.update_banner = QFrame() + self.update_banner.setObjectName("Card") + self.update_banner.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + self.update_banner.setStyleSheet( + f"QFrame#Card {{ background: rgba(242,184,75,.08); border-color: rgba(242,184,75,.3); }}") + banner_layout = QHBoxLayout(self.update_banner) + banner_layout.setContentsMargins(16, 11, 16, 11) + banner_layout.setSpacing(12) + self.update_banner_text = QLabel("") + self.update_banner_text.setFont(theme.body_font(9.6, QFont.Weight.DemiBold)) + banner_layout.addWidget(self.update_banner_text, 1) + update_button = QPushButton("View update") + update_button.setObjectName("Link") + update_button.clicked.connect(self.updates_requested) + banner_layout.addWidget(update_button) + dismiss = QPushButton("Dismiss") + dismiss.setObjectName("Ghost") + dismiss.clicked.connect(self.update_banner.hide) + banner_layout.addWidget(dismiss) + self.update_banner.hide() + layout.addWidget(self.update_banner) + + layout.addWidget(self._build_hero()) + layout.addLayout(self._build_stats()) + middle = QHBoxLayout() + middle.setSpacing(16) + left = QVBoxLayout() + left.setSpacing(16) + left.addWidget(self._build_concerns()) + left.addWidget(self._build_activity()) + right = QVBoxLayout() + right.setSpacing(16) + right.addWidget(self._build_network()) + right.addWidget(self._build_account()) + right.addStretch(1) + middle.addLayout(left, 2) + middle.addLayout(right, 1) + layout.addLayout(middle, 1) + self.engine_footer = QLabel("") + self.engine_footer.setObjectName("Dim") + layout.addWidget(self.engine_footer) + + def _build_hero(self) -> QWidget: + hero = QFrame() + hero.setObjectName("Hero") + hero.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + layout = QHBoxLayout(hero) + layout.setContentsMargins(26, 22, 26, 22) + layout.setSpacing(26) + self.dial = components.ProtectionDial() + layout.addWidget(self.dial, 0, Qt.AlignmentFlag.AlignVCenter) + + text = QVBoxLayout() + text.setSpacing(8) + top = QHBoxLayout() + top.setSpacing(10) + self.status_pill = components.Pill("Checking", "muted") + top.addWidget(self.status_pill, 0) + self.checked_label = QLabel("Reading system status…") + self.checked_label.setObjectName("Dim") + top.addWidget(self.checked_label, 0) + top.addStretch(1) + text.addLayout(top) + self.hero_title = QLabel("Checking your protection") + self.hero_title.setObjectName("HeroTitle") + self.hero_title.setWordWrap(True) + text.addWidget(self.hero_title) + self.hero_summary = QLabel( + "BoltClam is reading the ClamAV engine, Windows security, and your last scan.") + self.hero_summary.setObjectName("HeroSummary") + self.hero_summary.setWordWrap(True) + self.hero_summary.setMaximumWidth(620) + text.addWidget(self.hero_summary) + segment_grid = QGridLayout() + segment_grid.setHorizontalSpacing(8) + segment_grid.setVerticalSpacing(7) + for index in range(4): + chip = SegmentChip() + self._segments.append(chip) + segment_grid.addWidget(chip, index // 2, index % 2) + text.addLayout(segment_grid) + actions = QHBoxLayout() + actions.setSpacing(9) + scan_button = QPushButton("Run a scan") + scan_button.setObjectName("Primary") + scan_button.setCursor(Qt.CursorShape.PointingHandCursor) + scan_button.clicked.connect(self.scan_requested) + actions.addWidget(scan_button) + settings_button = QPushButton("Open settings") + settings_button.setObjectName("Secondary") + settings_button.setCursor(Qt.CursorShape.PointingHandCursor) + settings_button.clicked.connect(self.settings_requested) + actions.addWidget(settings_button) + actions.addStretch(1) + text.addLayout(actions) + layout.addLayout(text, 1) + return hero + + def _build_stats(self) -> QHBoxLayout: + row = QHBoxLayout() + row.setSpacing(12) + self.stat_last_scan = components.StatTile("Last scan", "Never", "No scan recorded") + self.stat_threats = components.StatTile("Active threats", "0", "Nothing needs action") + self.stat_quarantine = components.StatTile("Quarantine", "0", "No files held") + self.stat_blocked = components.StatTile("Blocked downloads", "0", "Last 30 days") + for tile in (self.stat_last_scan, self.stat_threats, + self.stat_quarantine, self.stat_blocked): + tile.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) + row.addWidget(tile, 1) + return row + + def _build_concerns(self) -> QWidget: + card = components.Card() + header = QWidget() + header_layout = QHBoxLayout(header) + header_layout.setContentsMargins(0, 0, 0, 0) + title_box = QVBoxLayout() + title_box.setSpacing(1) + title = QLabel("Security concerns") + title.setObjectName("CardTitle") + subtitle = QLabel("What needs attention on this PC") + subtitle.setObjectName("Muted") + subtitle.setFont(theme.body_font(8.8)) + title_box.addWidget(title) + title_box.addWidget(subtitle) + header_layout.addLayout(title_box, 1) + self.concern_pill = components.Pill("Checking", "muted") + header_layout.addWidget(self.concern_pill, 0, Qt.AlignmentFlag.AlignTop) + card.body.addWidget(header) + self.concerns_layout = QVBoxLayout() + self.concerns_layout.setSpacing(0) + card.body.addLayout(self.concerns_layout) + self._concern_loading = QLabel("Reading Windows security status…") + self._concern_loading.setObjectName("Muted") + self.concerns_layout.addWidget(self._concern_loading) + return card + + def _build_activity(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "Recent activity", "Detections and download decisions from this PC")[0]) + self.activity_layout = QVBoxLayout() + self.activity_layout.setSpacing(0) + card.body.addLayout(self.activity_layout) + self._activity_loading = QLabel("Loading activity…") + self._activity_loading.setObjectName("Muted") + self.activity_layout.addWidget(self._activity_loading) + return card + + def _build_network(self) -> QWidget: + card = components.Card() + header = QWidget() + header_layout = QHBoxLayout(header) + header_layout.setContentsMargins(0, 0, 0, 0) + title = QLabel("Network") + title.setObjectName("CardTitle") + header_layout.addWidget(title, 1) + self.admin_pill = components.Pill("Checking", "muted") + header_layout.addWidget(self.admin_pill, 0) + card.body.addWidget(header) + self.network_rows = {} + for key, label in ( + ("hostname", "Hostname"), ("ip", "IPv4 address"), ("gateway", "Gateway"), + ("adapter", "Adapter"), ("os", "Operating system"), ("uptime", "Uptime"), + ): + row = components.KeyValueRow(label) + self.network_rows[key] = row + card.body.addWidget(row) + self.network_rows["ip"].value_label.setObjectName("Mono") + return card + + def _build_account(self) -> QWidget: + card = components.Card() + header = QHBoxLayout() + header.setSpacing(11) + self.account_avatar = components.Avatar(self.context.user_name, 38) + header.addWidget(self.account_avatar, 0) + text_box = QVBoxLayout() + text_box.setSpacing(1) + self.account_name = QLabel(self.context.user_name) + self.account_name.setFont(theme.body_font(10.5, QFont.Weight.DemiBold)) + self.account_email = components.ElidedLabel(self.context.user_email) + self.account_email.setObjectName("Muted") + self.account_email.setFont(theme.body_font(8.5)) + text_box.addWidget(self.account_name) + text_box.addWidget(self.account_email) + header.addLayout(text_box, 1) + card.body.addLayout(header) + plan_row = QHBoxLayout() + plan_label = QLabel("Plan") + plan_label.setObjectName("Muted") + plan_label.setFont(theme.body_font(8.8)) + plan_row.addWidget(plan_label, 0) + plan_row.addStretch(1) + self.plan_pill = components.Pill(self.context.plan_label, "info") + plan_row.addWidget(self.plan_pill, 0) + card.body.addLayout(plan_row) + self.subscription_label = QLabel("") + self.subscription_label.setObjectName("Dim") + self.subscription_label.setWordWrap(True) + card.body.addWidget(self.subscription_label) + manage = QLabel( + f'Manage Bolt account') + manage.setOpenExternalLinks(True) + manage.setFont(theme.body_font(9.0, QFont.Weight.DemiBold)) + card.body.addWidget(manage) + return card + + # -- refresh ----------------------------------------------------------- + + def refresh(self, force: bool = False) -> None: + self._refresh_stats() + self._refresh_account() + self._refresh_activity() + if self._worker is not None and self._worker.isRunning(): + return + worker = SystemInfoWorker(self.context, self) + worker.finished_snapshot.connect(self._apply_snapshot) + worker.failed.connect(self._system_failed) + worker.finished.connect(self._worker_finished) + worker.finished.connect(worker.deleteLater) + self._worker = worker + worker.start() + + def _refresh_stats(self) -> None: + stats = self.context.storage.stats() + latest = stats.get("latest_scan") + if latest is None: + self.stat_last_scan.set_value("Never", "No scan recorded") + self.stat_last_scan.set_accent("muted") + else: + self.stat_last_scan.set_value( + theme.relative_time(latest.started_at), + f"{latest.scope_label} · {latest.status.replace('_', ' ')}") + self.stat_last_scan.set_accent("good" if latest.status == "completed" else "warning") + active = int(stats.get("active_threats") or 0) + self.stat_threats.set_value(str(active), "Needs quarantine or deletion" if active else + "Nothing needs action") + self.stat_threats.set_accent("critical" if active else "good") + quarantined = int(stats.get("quarantined") or 0) + self.stat_quarantine.set_value(str(quarantined), + "Review in Scan & results" if quarantined else "No files held") + self.stat_quarantine.set_accent("warning" if quarantined else "good") + blocked = int(stats.get("blocked_30d") or 0) + self.stat_blocked.set_value(str(blocked), "Last 30 days") + self.stat_blocked.set_accent("good" if blocked == 0 else "info") + + def _refresh_account(self) -> None: + self.account_avatar.set_name(self.context.user_name) + self.account_name.setText(self.context.user_name) + self.account_email.setText(self.context.user_email or "Signed in with Bolt") + self.plan_pill.setText(self.context.plan_label) + self.plan_pill.set_severity("good" if self.context.licensed else "muted") + subscription = self.context.subscription + status = str(subscription.get("status") or "none") + if status in ("active", "trialing"): + period_end = theme.format_datetime(subscription.get("current_period_end")) + self.subscription_label.setText( + f"{subscription.get('label', 'Bolt')} · {status}" + + (f" · renews {period_end}" if period_end != "—" else "")) + else: + self.subscription_label.setText("No active Bolt subscription.") + + def _refresh_activity(self) -> None: + _clear_layout(self.activity_layout) + events: list[tuple[str, str, str, str]] = [] + for detection in self.context.storage.recent_detections(5): + action = str(detection.get("action") or "detected") + severity = "critical" if action in ("detected", "failed") else ( + "good" if action in ("quarantined", "deleted") else "info") + events.append(( + str(detection.get("detected_at") or ""), + str(detection.get("signature") or "Detection"), + f"{display_name(str(detection.get('path') or ''))} · {human_action(action)}", + severity, + )) + for event in self.context.storage.list_realtime_events(5): + severity = "critical" if event.action in ("detected", "failed") else ( + "good" if event.action in ("quarantined", "deleted", "blocked") else "info") + title = event.signature or { + "quarantined": "Download quarantined", + "deleted": "Download deleted", + "detected": "Download needs attention", + "error": "Download scan failed", + "skipped": "Large download not scanned", + "allowed": "Download scanned", + }.get(event.action, "Download activity") + events.append((event.created_at, title, + f"{display_name(event.path)} · {human_action(event.action)}", + severity)) + events.sort(key=lambda item: item[0], reverse=True) + if not events: + empty = QLabel("No activity yet. Scans and browser-download decisions show up here.") + empty.setObjectName("Muted") + empty.setWordWrap(True) + self.activity_layout.addWidget(empty) + return + for when, title, subtitle, severity in events[:6]: + self.activity_layout.addWidget( + ActivityRow(severity, title, subtitle, theme.relative_time(when))) + + def _apply_snapshot(self, snapshot: dict) -> None: + self._snapshot = snapshot + self.dial.set_snapshot(snapshot) + overall = str(snapshot.get("overall") or "setup") + severity = {"protected": "good", "attention": "warning", + "at_risk": "critical", "setup": "info"}.get(overall, "muted") + label = {"protected": "Protected", "attention": "Needs attention", + "at_risk": "At risk", "setup": "Setup needed"}.get(overall, "Checking") + self.status_pill.setText(label) + self.status_pill.set_severity(severity) + self.hero_title.setText(str(snapshot.get("headline") or "Protection status")) + self.hero_summary.setText(str(snapshot.get("summary") or "")) + checked = theme.relative_time(snapshot.get("checked_at")) + self.checked_label.setText(f"Checked {checked}") + segments = snapshot.get("segments") or [] + for index, chip in enumerate(self._segments): + chip.set_segment(segments[index] if index < len(segments) else {}) + self._render_concerns(snapshot.get("concerns") or []) + self._render_network(snapshot) + clamav = snapshot.get("clamav") or {} + engine = str(clamav.get("summary") or "ClamAV not configured") + self.engine_footer.setText( + f"Engine: {engine} · Last checked {theme.relative_time(snapshot.get('checked_at'))}" ) + self.snapshot_changed.emit(snapshot) + + def _render_concerns(self, concerns: list[dict]) -> None: + _clear_layout(self.concerns_layout) + count = len(concerns) + critical = sum(1 for item in concerns if item.get("severity") == "critical") + warning = sum(1 for item in concerns if item.get("severity") == "warning") + if critical: + self.concern_pill.setText(f"{critical} critical") + self.concern_pill.set_severity("critical") + elif warning: + self.concern_pill.setText(f"{warning} to review") + self.concern_pill.set_severity("warning") + elif count: + self.concern_pill.setText(f"{count} note(s)") + self.concern_pill.set_severity("info") + else: + self.concern_pill.setText("All clear") + self.concern_pill.set_severity("good") + if not concerns: + row = QHBoxLayout() + row.setContentsMargins(2, 10, 2, 8) + row.addWidget(components.SeverityDot("good", 9), 0) + label = QLabel("Nothing needs your attention. Definitions are current and " + "no detected files are waiting for action.") + label.setObjectName("Muted") + label.setWordWrap(True) + row.addWidget(label, 1) + holder = QWidget() + holder.setLayout(row) + self.concerns_layout.addWidget(holder) + return + for concern in concerns: + row = components.ConcernRow(concern) + row.action_requested.connect(self._on_concern_action) + self.concerns_layout.addWidget(row) + + def _render_network(self, snapshot: dict) -> None: + network = snapshot.get("network") or {} + adapters = network.get("adapters") or [] + ips = network.get("local_ips") or [] + self.network_rows["hostname"].set_value(str(snapshot.get("hostname") or "—")) + self.network_rows["ip"].set_value(", ".join(ips[:2]) if ips else "No IPv4 address") + self.network_rows["gateway"].set_value(str(network.get("gateway") or "Not reported")) + adapter_names = ", ".join(str(item.get("name") or "") for item in adapters[:2]) + self.network_rows["adapter"].set_value(adapter_names or "Not reported") + self.network_rows["os"].set_value(str(snapshot.get("os") or "—")) + self.network_rows["uptime"].set_value(str(snapshot.get("uptime") or "Unknown")) + self.admin_pill.setText("Administrator" if snapshot.get("is_admin") else "Standard user") + self.admin_pill.set_severity("good" if snapshot.get("is_admin") else "muted") + + def _on_concern_action(self, action: str) -> None: + if action == "settings": + self.settings_requested.emit() + elif action == "scan": + self.scan_requested.emit() + elif action == "quarantine": + self.quarantine_requested.emit() + elif action == "updates": + self.updates_requested.emit() + elif action == "update_definitions": + self.definitions_requested.emit() + + def _system_failed(self, message: str) -> None: + self._concern_loading.setText(f"System status unavailable: {message}") + self.checked_label.setText("Some system details could not be read") + + def _worker_finished(self) -> None: + self._worker = None + + def refresh_activity(self) -> None: + self._refresh_stats() + self._refresh_activity() + + def refresh_account(self) -> None: + self._refresh_account() + + def show_update(self, release_name: str, tag: str) -> None: + self.update_banner_text.setText(f"{release_name or tag} is available. {tag}") + self.update_banner.show() + + def hide_update(self) -> None: + self.update_banner.hide() + + def stop(self) -> None: + stop_thread(self._worker, wait_ms=5000) + self._worker = None + + +def _clear_layout(layout) -> None: + while layout.count(): + item = layout.takeAt(0) + widget = item.widget() + child = item.layout() + if widget is not None: + widget.deleteLater() + elif child is not None: + _clear_layout(child) diff --git a/src/boltclam/ui/login_dialog.py b/src/boltclam/ui/login_dialog.py new file mode 100644 index 0000000..fceb57d --- /dev/null +++ b/src/boltclam/ui/login_dialog.py @@ -0,0 +1,348 @@ +"""BoltClam's sign-in dialog and its background auth workers.""" + +from __future__ import annotations + +import logging +import threading + +from PyQt6.QtCore import QThread, Qt, QUrl, pyqtSignal +from PyQt6.QtGui import QDesktopServices, QFont, QPixmap +from PyQt6.QtWidgets import ( + QDialog, + QFrame, + QHBoxLayout, + QLabel, + QLineEdit, + QProgressBar, + QPushButton, + QVBoxLayout, + QWidget, +) + +from .. import paths +from ..auth import AuthCancelled, AuthError, AuthService, AuthTimeout, Session +from . import brand, theme +from .threads import stop_thread + +logger = logging.getLogger(__name__) + + +class BrowserLoginWorker(QThread): + url_ready = pyqtSignal(str) + succeeded = pyqtSignal(object) + failed = pyqtSignal(str) + + def __init__(self, auth: AuthService, parent=None) -> None: + super().__init__(parent) + self.auth = auth + self._cancel = threading.Event() + + def cancel(self) -> None: + self._cancel.set() + + def run(self) -> None: + try: + session = self.auth.browser_login( + self._open_url, cancel_event=self._cancel) + self.succeeded.emit(session) + except AuthCancelled: + self.failed.emit("Sign-in was cancelled.") + except AuthTimeout as exc: + self.failed.emit(str(exc)) + except AuthError as exc: + self.failed.emit(str(exc)) + except Exception as exc: + logger.exception("Browser sign-in failed") + self.failed.emit(f"Sign-in failed: {exc}") + + def _open_url(self, url: str) -> bool: + self.url_ready.emit(url) + return True + + +class KeyLoginWorker(QThread): + succeeded = pyqtSignal(object) + failed = pyqtSignal(str) + + def __init__(self, auth: AuthService, api_key: str, parent=None) -> None: + super().__init__(parent) + self.auth = auth + self.api_key = api_key + + def run(self) -> None: + try: + session: Session = self.auth.login_with_key(self.api_key) + self.succeeded.emit(session) + except AuthError as exc: + self.failed.emit(str(exc)) + except Exception as exc: + logger.exception("API-key sign-in failed") + self.failed.emit(f"Sign-in failed: {exc}") + + +class LoginDialog(QDialog): + def __init__(self, auth: AuthService, *, notice: str = "", parent=None) -> None: + super().__init__(parent) + self.auth = auth + self.session: Session | None = None + self._worker: QThread | None = None + self._notice = notice + self.setWindowTitle("BoltClam — Sign in") + self.setWindowIcon(brand.app_icon()) + self.setModal(True) + self.setMinimumSize(830, 550) + self.resize(880, 570) + self._build() + self._start_state() + + # -- construction ------------------------------------------------------ + + def _build(self) -> None: + root = QHBoxLayout(self) + root.setContentsMargins(18, 18, 18, 18) + root.setSpacing(16) + + left = QFrame() + left.setObjectName("LoginBrand") + left.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + left.setFixedWidth(332) + left_layout = QVBoxLayout(left) + left_layout.setContentsMargins(30, 30, 30, 26) + left_layout.setSpacing(12) + + mark = QLabel() + mark.setPixmap(brand.pixmap(72)) + mark.setFixedSize(72, 72) + left_layout.addWidget(mark) + brand_name = QLabel("BoltClam") + brand_name.setObjectName("BrandName") + brand_name.setFont(theme.display_font(21, QFont.Weight.Bold)) + left_layout.addWidget(brand_name) + tagline = QLabel("ClamAV protection, without the noise.") + tagline.setObjectName("Muted") + tagline.setWordWrap(True) + left_layout.addWidget(tagline) + left_layout.addSpacing(24) + + headline = QLabel("A calmer way to know your PC is clean.") + headline.setFont(theme.display_font(22, QFont.Weight.DemiBold)) + headline.setWordWrap(True) + left_layout.addWidget(headline) + left_layout.addSpacing(8) + for title, detail in ( + ("ClamAV under the hood", "Full-disk and user-home scans with a managed signature database."), + ("Downloads held for a scan", "New browser downloads are locked while ClamAV checks them."), + ("Quarantine, not guesswork", "Every finding is recorded and reversible until you delete it."), + ): + row = QHBoxLayout() + row.setSpacing(9) + dot = QLabel("•") + dot.setStyleSheet(f"color: {theme.AMBER}; font-size: 16px;") + dot.setFixedWidth(12) + row.addWidget(dot, 0, Qt.AlignmentFlag.AlignTop) + text_box = QVBoxLayout() + text_box.setSpacing(1) + label = QLabel(title) + label.setFont(theme.body_font(9.8, QFont.Weight.DemiBold)) + desc = QLabel(detail) + desc.setObjectName("Muted") + desc.setFont(theme.body_font(8.7)) + desc.setWordWrap(True) + text_box.addWidget(label) + text_box.addWidget(desc) + row.addLayout(text_box, 1) + left_layout.addLayout(row) + left_layout.addSpacing(4) + left_layout.addStretch(1) + footer = QLabel("The Majestic Network · Bolt") + footer.setObjectName("Dim") + left_layout.addWidget(footer) + root.addWidget(left) + + right = QFrame() + right.setObjectName("LoginForm") + right.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + right_layout = QVBoxLayout(right) + right_layout.setContentsMargins(34, 32, 34, 26) + right_layout.setSpacing(9) + title = QLabel("Sign in to BoltClam") + title.setFont(theme.display_font(23, QFont.Weight.DemiBold)) + right_layout.addWidget(title) + subtitle = QLabel("Use your Bolt account. Your password is sent only to Bolt's API — " + "BoltClam receives a device key instead.") + subtitle.setObjectName("Muted") + subtitle.setWordWrap(True) + right_layout.addWidget(subtitle) + right_layout.addSpacing(14) + + if self._notice: + notice = QLabel(self._notice) + notice.setWordWrap(True) + notice.setStyleSheet( + f"background: rgba(242,184,75,.1); border: 1px solid rgba(242,184,75,.35);" + f"border-radius: 9px; padding: 9px 11px; color: {theme.AMBER_BRIGHT};") + right_layout.addWidget(notice) + + self.error_label = QLabel("") + self.error_label.setWordWrap(True) + self.error_label.setStyleSheet(f"color: {theme.DANGER}; font-size: 12.5px;") + self.error_label.hide() + right_layout.addWidget(self.error_label) + + self.browser_button = QPushButton("Continue in browser") + self.browser_button.setObjectName("Primary") + self.browser_button.setMinimumHeight(42) + self.browser_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.browser_button.clicked.connect(self._browser_login) + right_layout.addWidget(self.browser_button) + + self.key_toggle = QPushButton("Use an API key instead") + self.key_toggle.setObjectName("Link") + self.key_toggle.setCursor(Qt.CursorShape.PointingHandCursor) + self.key_toggle.clicked.connect(self._toggle_key) + right_layout.addWidget(self.key_toggle, 0, Qt.AlignmentFlag.AlignHCenter) + + self.key_box = QWidget() + key_layout = QVBoxLayout(self.key_box) + key_layout.setContentsMargins(0, 4, 0, 0) + key_layout.setSpacing(7) + key_label = QLabel("Bolt API key") + key_label.setObjectName("Muted") + key_layout.addWidget(key_label) + self.key_input = QLineEdit() + self.key_input.setPlaceholderText("bolt_live_…") + self.key_input.setEchoMode(QLineEdit.EchoMode.Password) + self.key_input.returnPressed.connect(self._key_login) + key_layout.addWidget(self.key_input) + self.key_button = QPushButton("Connect with key") + self.key_button.setObjectName("Secondary") + self.key_button.clicked.connect(self._key_login) + key_layout.addWidget(self.key_button) + self.key_box.hide() + right_layout.addWidget(self.key_box) + + status_row = QHBoxLayout() + status_row.setSpacing(9) + self.busy = QProgressBar() + self.busy.setRange(0, 0) + self.busy.setFixedHeight(5) + self.busy.setFixedWidth(58) + self.busy.hide() + self.status_label = QLabel("") + self.status_label.setObjectName("Muted") + self.status_label.setWordWrap(True) + status_row.addWidget(self.busy, 0) + status_row.addWidget(self.status_label, 1) + right_layout.addLayout(status_row) + + right_layout.addStretch(1) + actions = QHBoxLayout() + create = QLabel( + f'Create or manage your account') + create.setOpenExternalLinks(True) + create.setObjectName("Dim") + actions.addWidget(create, 1) + quit_button = QPushButton("Quit") + quit_button.setObjectName("Ghost") + quit_button.clicked.connect(self.reject) + actions.addWidget(quit_button) + right_layout.addLayout(actions) + root.addWidget(right, 1) + + # -- state ------------------------------------------------------------- + + def _start_state(self) -> None: + self._set_busy(False) + self.status_label.setText("BoltClam opens Bolt's sign-in page in your default browser.") + self.browser_button.setFocus() + + def _set_busy(self, busy: bool, status: str = "") -> None: + self.browser_button.setEnabled(not busy) + self.key_toggle.setEnabled(not busy) + self.key_button.setEnabled(not busy) + self.key_input.setEnabled(not busy) + if busy: + self.busy.show() + self.status_label.setText(status) + else: + self.busy.hide() + self.error_label.setVisible(bool(self.error_label.text()) and not busy) + + def _show_error(self, message: str) -> None: + self.error_label.setText(message) + self.error_label.show() + self._set_busy(False) + self.status_label.setText("You can try again, or use an API key.") + self.browser_button.setFocus() + + def _toggle_key(self) -> None: + visible = not self.key_box.isVisible() + self.key_box.setVisible(visible) + if visible: + self.key_input.setFocus() + self.key_toggle.setText("Use browser sign-in instead") + else: + self.key_toggle.setText("Use an API key instead") + + # -- actions ----------------------------------------------------------- + + def _browser_login(self) -> None: + if self._worker is not None and self._worker.isRunning(): + return + self.error_label.hide() + self._set_busy(True, "Waiting for you to finish sign-in in the browser…") + worker = BrowserLoginWorker(self.auth, self) + worker.url_ready.connect(self._open_browser) + worker.succeeded.connect(self._on_success) + worker.failed.connect(self._on_failure) + worker.finished.connect(worker.deleteLater) + self._worker = worker + worker.start() + + def _open_browser(self, url: str) -> None: + if not QDesktopServices.openUrl(QUrl(url)): + if isinstance(self._worker, BrowserLoginWorker): + self._worker.cancel() + self._show_error("BoltClam could not open your default browser. " + "Use an API key below, or open the sign-in page manually.") + + def _key_login(self) -> None: + if self._worker is not None and self._worker.isRunning(): + return + key = self.key_input.text().strip() + if not key: + self._show_error("Enter your Bolt API key.") + return + self.error_label.hide() + self._set_busy(True, "Checking your API key…") + worker = KeyLoginWorker(self.auth, key, self) + worker.succeeded.connect(self._on_success) + worker.failed.connect(self._on_failure) + worker.finished.connect(worker.deleteLater) + self._worker = worker + worker.start() + + def _on_success(self, session: Session) -> None: + self.session = session + self._worker = None + self._set_busy(False) + self.accept() + + def _on_failure(self, message: str) -> None: + self._worker = None + self._show_error(message) + + # -- lifecycle --------------------------------------------------------- + + def reject(self) -> None: + worker = self._worker + if worker is not None and worker.isRunning(): + if isinstance(worker, BrowserLoginWorker): + worker.cancel() + stop_thread(worker, wait_ms=2500) + super().reject() + + def closeEvent(self, event) -> None: + self.reject() + event.accept() diff --git a/src/boltclam/ui/main_window.py b/src/boltclam/ui/main_window.py new file mode 100644 index 0000000..74e9152 --- /dev/null +++ b/src/boltclam/ui/main_window.py @@ -0,0 +1,512 @@ +"""Main window: navigation rail, page stack, tray, and service lifecycle.""" + +from __future__ import annotations + +import dataclasses +import logging +from pathlib import Path + +from PyQt6.QtCore import QSize, Qt, QThread, QTimer, pyqtSignal +from PyQt6.QtGui import QAction, QCloseEvent, QFont, QKeySequence, QShortcut +from PyQt6.QtWidgets import ( + QApplication, + QButtonGroup, + QFrame, + QHBoxLayout, + QLabel, + QMenu, + QPushButton, + QStackedWidget, + QSystemTrayIcon, + QVBoxLayout, + QWidget, +) + +from .. import __version__ +from ..api_client import ApiError, NetworkError +from ..context import AppContext +from ..realtime import RealtimeProtection +from . import brand, components, theme +from .threads import stop_thread +from .about_page import AboutPage +from .home_page import HomePage +from .scan_page import ScanPage +from .settings_page import SettingsPage + +logger = logging.getLogger(__name__) + + +class AccountRefreshWorker(QThread): + succeeded = pyqtSignal(object) + expired = pyqtSignal() + failed = pyqtSignal(str) + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + + def run(self) -> None: + try: + api = self.context.api or self.context.rebuild_api() + account = api.get_account() + self.succeeded.emit(account) + except ApiError as exc: + if exc.status == 401: + self.expired.emit() + else: + self.failed.emit(str(exc)) + except Exception as exc: + logger.exception("Account refresh failed") + self.failed.emit(str(exc)) + + +class LogoutWorker(QThread): + done = pyqtSignal() + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + + def run(self) -> None: + try: + self.context.auth.logout(self.context.session_key) + except Exception: + logger.exception("Logout request failed") + self.context.auth.credentials.delete() + self.context.auth.account_cache.clear() + finally: + self.done.emit() + + +class MainWindow(QWidget): + finished = pyqtSignal() + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + self._allow_close = False + self._cleaned = False + self._logged_out = False + self._account_worker: AccountRefreshWorker | None = None + self._logout_worker: LogoutWorker | None = None + self._last_update_tag = "" + self._tray_notice_shown = False + self._applied_settings = dataclasses.replace(context.settings) + self.setWindowTitle("BoltClam") + self.setWindowIcon(brand.app_icon()) + self.setMinimumSize(1180, 780) + self.resize(1380, 880) + self._build() + self._build_tray() + self._start_realtime() + self._install_shortcuts() + QTimer.singleShot(700, self._refresh_account) + + # -- construction ------------------------------------------------------ + + def _build(self) -> None: + root_layout = QHBoxLayout(self) + root_layout.setContentsMargins(0, 0, 0, 0) + root_layout.setSpacing(0) + root_layout.addWidget(self._build_sidebar()) + self.stack = QStackedWidget() + self.stack.setObjectName("Content") + self.home_page = HomePage(self.context) + self.scan_page = ScanPage(self.context) + self.about_page = AboutPage(self.context) + self.settings_page = SettingsPage(self.context) + for page in (self.home_page, self.scan_page, self.about_page, self.settings_page): + self.stack.addWidget(page) + root_layout.addWidget(self.stack, 1) + self.toast = components.Toast(self) + self._connect_pages() + self._navigate(0) + + def _build_sidebar(self) -> QWidget: + sidebar = QFrame() + sidebar.setObjectName("Sidebar") + sidebar.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + sidebar.setFixedWidth(238) + layout = QVBoxLayout(sidebar) + layout.setContentsMargins(16, 20, 16, 16) + layout.setSpacing(9) + brand_row = QHBoxLayout() + brand_row.setSpacing(10) + mark = QLabel() + mark.setPixmap(brand.pixmap(40)) + mark.setFixedSize(40, 40) + brand_row.addWidget(mark, 0) + brand_text = QVBoxLayout() + brand_text.setSpacing(0) + name = QLabel("BoltClam") + name.setObjectName("BrandName") + tag = QLabel("The Majestic Network") + tag.setObjectName("BrandTag") + brand_text.addWidget(name) + brand_text.addWidget(tag) + brand_row.addLayout(brand_text, 1) + layout.addLayout(brand_row) + layout.addSpacing(18) + + self.nav_group = QButtonGroup(self) + self.nav_group.setExclusive(True) + self.nav_buttons = [] + for index, (icon, label) in enumerate(( + ("home", "Homepage"), + ("scan", "Scan & results"), + ("updates", "About & updates"), + ("settings", "Settings"))): + button = components.NavButton(icon, label) + self.nav_group.addButton(button, index) + self.nav_buttons.append(button) + layout.addWidget(button) + self.nav_group.idClicked.connect(self._navigate) + layout.addStretch(1) + + status_card = components.Card(margins=(13, 11, 13, 11), spacing=4) + status_row = QHBoxLayout() + status_row.setSpacing(7) + self.side_pill = components.Pill("Starting", "muted") + status_row.addWidget(self.side_pill, 0) + status_row.addStretch(1) + status_card.body.addLayout(status_row) + self.side_detail = QLabel("Reading protection status…") + self.side_detail.setObjectName("Muted") + self.side_detail.setFont(theme.body_font(8.6)) + self.side_detail.setWordWrap(True) + status_card.body.addWidget(self.side_detail) + self.side_realtime = QLabel("Real-time watcher starting") + self.side_realtime.setObjectName("Dim") + self.side_realtime.setFont(theme.body_font(8.2)) + self.side_realtime.setWordWrap(True) + status_card.body.addWidget(self.side_realtime) + layout.addWidget(status_card) + layout.addSpacing(6) + + account_card = components.Card(margins=(13, 11, 13, 11), spacing=6) + account_row = QHBoxLayout() + account_row.setSpacing(9) + self.side_avatar = components.Avatar(self.context.user_name, 34) + account_row.addWidget(self.side_avatar, 0) + account_text = QVBoxLayout() + account_text.setSpacing(0) + self.side_name = components.ElidedLabel(self.context.user_name) + self.side_name.setFont(theme.body_font(9.6, QFont.Weight.DemiBold)) + self.side_email = components.ElidedLabel(self.context.user_email or "Bolt account") + self.side_email.setObjectName("Muted") + self.side_email.setFont(theme.body_font(8.0)) + account_text.addWidget(self.side_name) + account_text.addWidget(self.side_email) + account_row.addLayout(account_text, 1) + account_card.body.addLayout(account_row) + self.side_plan = components.Pill(self.context.plan_label, "info") + account_card.body.addWidget(self.side_plan, 0, Qt.AlignmentFlag.AlignLeft) + layout.addWidget(account_card) + version = QLabel(f"v{__version__}") + version.setObjectName("Dim") + version.setAlignment(Qt.AlignmentFlag.AlignCenter) + layout.addWidget(version) + return sidebar + + def _connect_pages(self) -> None: + home = self.home_page + home.scan_requested.connect(self._scan_from_home) + home.settings_requested.connect(lambda: self._navigate(3)) + home.quarantine_requested.connect(self._show_quarantine) + home.updates_requested.connect(lambda: self._navigate(2)) + home.definitions_requested.connect(self._update_definitions) + home.snapshot_changed.connect(self._home_snapshot) + + scan = self.scan_page + scan.scan_finished.connect(self._scan_finished) + scan.settings_requested.connect(lambda: self._navigate(3)) + scan.toast_requested.connect(self._toast) + scan.definitions_updated.connect(lambda: self.home_page.refresh(force=True)) + + about = self.about_page + about.toast_requested.connect(self._toast) + about.update_found.connect(self._update_found) + + settings = self.settings_page + settings.toast_requested.connect(self._toast) + settings.settings_saved.connect(self._settings_saved) + settings.sign_out_requested.connect(self._logout) + settings.definitions_requested.connect(self._update_definitions) + + def _build_tray(self) -> None: + self.tray: QSystemTrayIcon | None = None + self._tray_menu: QMenu | None = None + if not QSystemTrayIcon.isSystemTrayAvailable(): + return + tray = QSystemTrayIcon(brand.app_icon(), self) + tray.setToolTip("BoltClam — ClamAV protection") + menu = QMenu(self) + open_action = QAction("Open BoltClam", self) + open_action.triggered.connect(self._show_window) + scan_action = QAction("Run a scan", self) + scan_action.triggered.connect(self._scan_from_tray) + quit_action = QAction("Quit", self) + quit_action.triggered.connect(self._quit) + menu.addAction(open_action) + menu.addAction(scan_action) + menu.addSeparator() + menu.addAction(quit_action) + tray.setContextMenu(menu) + tray.activated.connect(self._tray_activated) + tray.messageClicked.connect(self._show_window) + tray.show() + self.tray = tray + self._tray_menu = menu + + def _install_shortcuts(self) -> None: + for index in range(4): + shortcut = QShortcut(QKeySequence(f"Ctrl+{index + 1}"), self) + shortcut.activated.connect(lambda index=index: self._navigate(index)) + quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self) + quit_shortcut.activated.connect(self._quit) + scan_shortcut = QShortcut(QKeySequence("Ctrl+R"), self) + scan_shortcut.activated.connect(self._scan_from_home) + + # -- real-time --------------------------------------------------------- + + def _start_realtime(self) -> None: + realtime = RealtimeProtection( + self.context.clamav, self.context.storage, self.context.quarantine, + self.context.settings, self) + self.context.realtime = realtime + realtime.status_changed.connect(self._realtime_status) + realtime.event_created.connect(self._realtime_event) + realtime.alert.connect(self._realtime_alert) + realtime.start() + + def _restart_realtime(self) -> None: + realtime = self.context.realtime + if realtime is None: + return + realtime.set_settings(self.context.settings) + realtime.stop() + realtime.start() + self.settings_page.refresh_account() + + def _realtime_status(self, text: str) -> None: + self.side_realtime.setText(text) + + def _realtime_event(self, event) -> None: + self.home_page.refresh_activity() + self.scan_page.refresh_quarantine() + + def _realtime_alert(self, title: str, body: str, severity: str) -> None: + if not self.context.settings.notify_on_detection: + return + self._notify(title, body, severity) + + # -- navigation -------------------------------------------------------- + + def _navigate(self, index: int) -> None: + index = max(0, min(3, int(index))) + self.stack.setCurrentIndex(index) + if 0 <= index < len(self.nav_buttons): + self.nav_buttons[index].setChecked(True) + if index == 0: + self.home_page.refresh() + elif index == 1: + self.scan_page.refresh() + elif index == 2: + self.about_page.refresh() + + def _show_quarantine(self) -> None: + self._navigate(1) + self.scan_page.tabs.setCurrentIndex(1) + self.scan_page.refresh_quarantine() + + def _scan_from_home(self) -> None: + self._navigate(1) + QTimer.singleShot(120, self.scan_page.start_scan) + + def _scan_from_tray(self) -> None: + self._show_window() + self._scan_from_home() + + def _update_definitions(self) -> None: + self._navigate(1) + QTimer.singleShot(120, self.scan_page.update_definitions) + + # -- events ------------------------------------------------------------ + + def _home_snapshot(self, snapshot: dict) -> None: + overall = str(snapshot.get("overall") or "setup") + severity = {"protected": "good", "attention": "warning", + "at_risk": "critical", "setup": "info"}.get(overall, "muted") + label = {"protected": "Protected", "attention": "Needs attention", + "at_risk": "At risk", "setup": "Setup needed"}.get(overall, "Checking") + self.side_pill.setText(label) + self.side_pill.set_severity(severity) + concerns = snapshot.get("concerns") or [] + self.side_detail.setText( + str(concerns[0].get("title")) if concerns else "No action needed") + + def _scan_finished(self) -> None: + self.home_page.refresh(force=True) + self.settings_page.refresh_account() + + def _update_found(self, release) -> None: + if release is None: + self.home_page.hide_update() + self._last_update_tag = "" + return + self.home_page.show_update(release.name, release.tag) + if release.tag and release.tag != self._last_update_tag: + self._last_update_tag = release.tag + self._notify("BoltClam update available", + f"{release.name or release.tag} is ready in About & updates.", + "info") + + def _refresh_account(self) -> None: + if self._account_worker is not None and self._account_worker.isRunning(): + return + worker = AccountRefreshWorker(self.context, self) + worker.succeeded.connect(self._account_succeeded) + worker.expired.connect(self._account_expired) + worker.failed.connect(self._account_failed) + worker.finished.connect(self._account_finished) + worker.finished.connect(worker.deleteLater) + self._account_worker = worker + worker.start() + + def _account_succeeded(self, account: dict) -> None: + self.context.account = account + self.context.offline = False + try: + self.context.auth.account_cache.save(account) + except Exception: + logger.exception("Could not cache the account summary") + self.home_page.refresh_account() + self.settings_page.refresh_account() + self.about_page.refresh_account() + self._update_sidebar_account() + + def _account_failed(self, message: str) -> None: + if self.context.account is None: + self._toast("Account details unavailable", message, "warning") + logger.info("Account refresh failed: %s", message) + + def _account_expired(self) -> None: + self._toast("Your BoltClam session expired", "Sign in again to continue.", "critical") + self._logout(expired=True) + + def _account_finished(self) -> None: + self._account_worker = None + + def _update_sidebar_account(self) -> None: + self.side_avatar.set_name(self.context.user_name) + self.side_name.setText(self.context.user_name) + self.side_email.setText(self.context.user_email or "Bolt account") + self.side_plan.setText(self.context.plan_label) + self.side_plan.set_severity("good" if self.context.licensed else "muted") + + def _settings_saved(self, settings) -> None: + previous = self._applied_settings + realtime_fields = ("realtime_enabled", "extra_watch_dirs", "clamav_path", + "auto_action") + realtime_changed = any(getattr(previous, field) != getattr(settings, field) + for field in realtime_fields) + self._applied_settings = dataclasses.replace(settings) + self.scan_page.reload_settings() + self.about_page.reload_settings() + self.home_page.refresh(force=True) + self.settings_page.reload_settings() + self.settings_page.refresh_account() + if realtime_changed: + QTimer.singleShot(0, self._restart_realtime) + + def _logout(self, expired: bool = False) -> None: + if self._logout_worker is not None and self._logout_worker.isRunning(): + return + if not expired: + self._toast("Signing out", "Revoking this PC's BoltClam key…", "info") + worker = LogoutWorker(self.context, self) + worker.done.connect(self._logout_done) + worker.finished.connect(worker.deleteLater) + self._logout_worker = worker + worker.start() + + def _logout_done(self) -> None: + self._logout_worker = None + self._logged_out = True + self._allow_close = True + self.close() + + def _toast(self, title: str, body: str, severity: str = "info") -> None: + self.toast.show_message(title, body, severity) + if severity in ("critical", "error") and self.tray is not None: + self._notify(title, body, severity) + + def _notify(self, title: str, body: str, severity: str = "info") -> None: + if self.tray is not None and self.tray.isVisible(): + icon = QSystemTrayIcon.MessageIcon.Warning + if severity in ("good", "info"): + icon = QSystemTrayIcon.MessageIcon.Information + elif severity in ("critical", "error"): + icon = QSystemTrayIcon.MessageIcon.Critical + self.tray.showMessage(title, body, icon, 7000) + if not self.isVisible(): + QApplication.alert(self, 2500) + elif severity in ("critical", "error"): + QApplication.alert(self, 2000) + + def _tray_activated(self, reason) -> None: + if reason in (QSystemTrayIcon.ActivationReason.Trigger, + QSystemTrayIcon.ActivationReason.DoubleClick): + self._show_window() + + def _show_window(self) -> None: + self.showNormal() + self.raise_() + self.activateWindow() + + def resizeEvent(self, event) -> None: + super().resizeEvent(event) + if hasattr(self, "toast") and self.toast.isVisible(): + self.toast.move(max(20, self.width() - self.toast.width() - 26), + max(20, self.height() - self.toast.height() - 26)) + + # -- lifecycle --------------------------------------------------------- + + def closeEvent(self, event: QCloseEvent) -> None: + if not self._allow_close and self.context.settings.close_to_tray and self.tray is not None: + self.hide() + if not self._tray_notice_shown: + self._tray_notice_shown = True + self.tray.showMessage( + "BoltClam is still protecting this PC", + "Real-time download scanning stays active in the tray. Use Quit to stop it.", + QSystemTrayIcon.MessageIcon.Information, 6000) + event.ignore() + return + self._cleanup() + event.accept() + + def _cleanup(self) -> None: + if self._cleaned: + return + self._cleaned = True + try: + if self.context.realtime is not None: + self.context.realtime.stop() + self.home_page.stop() + self.scan_page.stop() + self.about_page.stop() + self.settings_page.stop() + stop_thread(self._account_worker, wait_ms=3500) + stop_thread(self._logout_worker, wait_ms=3500) + if self.tray is not None: + self.tray.hide() + finally: + self.finished.emit() + + def _quit(self) -> None: + self._allow_close = True + self.close() + + def logged_out(self) -> bool: + return self._logged_out diff --git a/src/boltclam/ui/scan_page.py b/src/boltclam/ui/scan_page.py new file mode 100644 index 0000000..fbdfe11 --- /dev/null +++ b/src/boltclam/ui/scan_page.py @@ -0,0 +1,912 @@ +"""Scan & results: scope choice, live scan console, history and quarantine.""" + +from __future__ import annotations + +import logging +from pathlib import Path + +from PyQt6.QtCore import QSignalBlocker, QSize, Qt, pyqtSignal +from PyQt6.QtGui import QColor, QFont +from PyQt6.QtWidgets import ( + QAbstractItemView, + QButtonGroup, + QComboBox, + QFrame, + QHBoxLayout, + QHeaderView, + QLabel, + QListWidget, + QListWidgetItem, + QMessageBox, + QProgressBar, + QPushButton, + QRadioButton, + QSizePolicy, + QSplitter, + QStackedWidget, + QTableWidget, + QTableWidgetItem, + QVBoxLayout, + QWidget, +) + +from ..context import AppContext +from ..models import Detection, display_name, human_action, human_scope +from ..quarantine import QuarantineError, delete_file +from ..scanner import DefinitionWorker, ScanWorker +from . import components, theme +from .threads import stop_thread + +logger = logging.getLogger(__name__) + + +class ScanHistoryItem(QWidget): + def __init__(self, scan, parent=None) -> None: + super().__init__(parent) + self.setMinimumHeight(64) + layout = QVBoxLayout(self) + layout.setContentsMargins(10, 7, 10, 7) + layout.setSpacing(3) + top = QHBoxLayout() + top.setSpacing(8) + when = QLabel(theme.format_datetime(scan.started_at)) + when.setFont(theme.body_font(9.3, QFont.Weight.DemiBold)) + top.addWidget(when, 1) + status = components.Pill(scan.status.replace("_", " ").title(), + "good" if scan.status == "completed" else + "warning" if scan.status == "completed_with_errors" else + "critical" if scan.status == "failed" else "muted") + top.addWidget(status, 0) + layout.addLayout(top) + bottom = QHBoxLayout() + bottom.setSpacing(8) + scope = QLabel(human_scope(scan.scope)) + scope.setObjectName("Muted") + scope.setFont(theme.body_font(8.5)) + bottom.addWidget(scope) + bottom.addStretch(1) + threats = QLabel(f"{scan.threat_count} threat(s)" if scan.threat_count else "Clean") + threats.setFont(theme.body_font(8.5, QFont.Weight.DemiBold)) + threats.setStyleSheet(f"color: {theme.DANGER if scan.threat_count else theme.TEAL};") + bottom.addWidget(threats) + layout.addLayout(bottom) + + def sizeHint(self): + return QSize(260, 68) + + +class PillsTab(QWidget): + """Two pill-shaped tabs over a stack, styled entirely by BoltClam.""" + + def __init__(self, parent=None) -> None: + super().__init__(parent) + layout = QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(0) + self._buttons: list[QPushButton] = [] + self._group = QButtonGroup(self) + self._group.setExclusive(True) + bar = QHBoxLayout() + bar.setContentsMargins(0, 0, 0, 0) + bar.setSpacing(4) + self._bar = bar + self._bar_holder = QWidget() + self._bar_holder.setLayout(bar) + layout.addWidget(self._bar_holder) + layout.addSpacing(7) + layout.addWidget(components.Divider()) + self._stack = QStackedWidget() + layout.addWidget(self._stack, 1) + self._group.idClicked.connect(self.setCurrentIndex) + self._bar.addStretch(1) + + def add_widget(self, widget: QWidget, label: str) -> None: + index = self._stack.count() + button = QPushButton(label) + button.setObjectName("TabPill") + button.setCheckable(True) + button.setCursor(Qt.CursorShape.PointingHandCursor) + self._group.addButton(button, index) + self._buttons.append(button) + self._bar.insertWidget(self._bar.count() - 1, button) + self._stack.addWidget(widget) + if index == 0: + button.setChecked(True) + + def setCurrentIndex(self, index: int) -> None: # noqa: N802 + index = max(0, min(self._stack.count() - 1, int(index))) + self._stack.setCurrentIndex(index) + if 0 <= index < len(self._buttons): + self._buttons[index].setChecked(True) + + def currentIndex(self) -> int: # noqa: N802 + return self._stack.currentIndex() + + +class ScanPage(QWidget): + scan_finished = pyqtSignal() + settings_requested = pyqtSignal() + toast_requested = pyqtSignal(str, str, str) + definitions_updated = pyqtSignal() + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + self._worker: ScanWorker | None = None + self._definition_worker: DefinitionWorker | None = None + self._current_scan_id: int | None = None + self._build() + self.reload_settings() + self.refresh() + + # -- construction ------------------------------------------------------ + + def _build(self) -> None: + outer = QVBoxLayout(self) + outer.setContentsMargins(30, 24, 30, 30) + outer.setSpacing(15) + + header = components.PageHeader( + "Scan & results", "Scan every user's home or the full disk, then act on what ClamAV finds.") + self.definitions_button = QPushButton("Update definitions") + self.definitions_button.setObjectName("Secondary") + self.definitions_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.definitions_button.clicked.connect(self.update_definitions) + header.add_action(self.definitions_button) + outer.addWidget(header) + + top = QHBoxLayout() + top.setSpacing(15) + top.addWidget(self._build_console(), 2) + top.addWidget(self._build_scope_card(), 1) + outer.addLayout(top) + + self.tabs = PillsTab() + self.tabs.add_widget(self._build_results_tab(), "Results") + self.tabs.add_widget(self._build_quarantine_tab(), "Quarantine") + outer.addWidget(self.tabs, 1) + + def _build_console(self) -> QWidget: + card = QFrame() + card.setObjectName("ScanConsole") + card.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) + layout = QHBoxLayout(card) + layout.setContentsMargins(22, 20, 22, 20) + layout.setSpacing(22) + self.scan_button = components.ScanButton() + self.scan_button.clicked.connect(self.start_scan) + layout.addWidget(self.scan_button, 0, Qt.AlignmentFlag.AlignVCenter) + right = QVBoxLayout() + right.setSpacing(6) + self.scan_state = QLabel("Ready to scan") + self.scan_state.setFont(theme.display_font(20, QFont.Weight.DemiBold)) + right.addWidget(self.scan_state) + self.scan_detail = QLabel( + "Choose a scope on the right. User-home scans cover every profile; full-disk " + "scans cover every fixed drive.") + self.scan_detail.setObjectName("Muted") + self.scan_detail.setWordWrap(True) + right.addWidget(self.scan_detail) + self.progress = QProgressBar() + self.progress.setRange(0, 0) + self.progress.setFixedHeight(6) + self.progress.hide() + right.addWidget(self.progress) + self.current_file = components.ElidedLabel("") + self.current_file.setObjectName("PathLabel") + self.current_file.setFont(theme.mono_font(8.7)) + self.current_file.hide() + right.addWidget(self.current_file) + self.last_scan_label = QLabel("") + self.last_scan_label.setObjectName("Dim") + right.addWidget(self.last_scan_label) + self.engine_line = QLabel("") + self.engine_line.setObjectName("Muted") + self.engine_line.setFont(theme.body_font(8.7)) + self.engine_line.setWordWrap(True) + right.addWidget(self.engine_line) + right.addStretch(1) + right.addWidget(components.Divider()) + actions = QHBoxLayout() + actions.setSpacing(8) + self.start_button = QPushButton("Start scan") + self.start_button.setObjectName("Primary") + self.start_button.setCursor(Qt.CursorShape.PointingHandCursor) + self.start_button.clicked.connect(self.start_scan) + actions.addWidget(self.start_button) + self.cancel_button = QPushButton("Cancel scan") + self.cancel_button.setObjectName("Danger") + self.cancel_button.clicked.connect(self.cancel_scan) + self.cancel_button.hide() + actions.addWidget(self.cancel_button) + actions.addStretch(1) + self.scan_scope_label = QLabel("") + self.scan_scope_label.setObjectName("Dim") + actions.addWidget(self.scan_scope_label) + right.addLayout(actions) + layout.addLayout(right, 1) + return card + + def _build_scope_card(self) -> QWidget: + card = components.Card(margins=(16, 16, 16, 16)) + title = QLabel("Scan scope") + title.setObjectName("CardTitle") + card.body.addWidget(title) + subtitle = QLabel("What BoltClam hands to ClamAV.") + subtitle.setObjectName("Muted") + subtitle.setFont(theme.body_font(8.7)) + card.body.addWidget(subtitle) + self.homes_radio = components.ChoiceTile( + "User homes", "Every profile under C:\\Users, including the current user and Public.") + self.full_radio = components.ChoiceTile( + "Full disk", "Every fixed local drive. More complete, and potentially hours long.") + card.body.addWidget(self.homes_radio) + card.body.addWidget(self.full_radio) + self.homes_radio.toggled.connect(self._scope_changed) + card.body.addSpacing(4) + auto_label = QLabel("When something is found") + auto_label.setObjectName("Muted") + auto_label.setFont(theme.body_font(8.8, QFont.Weight.DemiBold)) + card.body.addWidget(auto_label) + self.auto_combo = QComboBox() + self.auto_combo.addItem("Quarantine (recommended)", "quarantine") + self.auto_combo.addItem("Delete permanently", "delete") + self.auto_combo.addItem("Notify only", "notify") + self.auto_combo.currentIndexChanged.connect(self._auto_action_changed) + card.body.addWidget(self.auto_combo) + self.auto_note = QLabel("") + self.auto_note.setObjectName("Dim") + self.auto_note.setWordWrap(True) + card.body.addWidget(self.auto_note) + card.body.addStretch(1) + return card + + def _build_results_tab(self) -> QWidget: + splitter = QSplitter(Qt.Orientation.Horizontal) + splitter.setChildrenCollapsible(False) + left = components.Card(margins=(12, 12, 12, 12)) + header = QLabel("Scan history") + header.setObjectName("CardTitle") + left.body.addWidget(header) + self.history = QListWidget() + self.history.setSpacing(2) + self.history.currentItemChanged.connect(self._history_selected) + left.body.addWidget(self.history, 1) + splitter.addWidget(left) + + right = components.Card(margins=(16, 14, 16, 14)) + self.detail_stack = QStackedWidget() + self.detail_empty = components.EmptyState( + "No scan selected", + "Run a scan or choose one from the history to see detections and remediation.", + "Run a scan") + self.detail_empty.action_clicked.connect(self.start_scan) + self.detail_stack.addWidget(self.detail_empty) + details = QWidget() + details_layout = QVBoxLayout(details) + details_layout.setContentsMargins(0, 0, 0, 0) + details_layout.setSpacing(9) + top = QHBoxLayout() + self.detail_title = QLabel("Scan details") + self.detail_title.setObjectName("CardTitle") + top.addWidget(self.detail_title, 1) + self.detail_status = components.Pill("—", "muted") + top.addWidget(self.detail_status, 0) + details_layout.addLayout(top) + self.detail_meta = QLabel("") + self.detail_meta.setObjectName("Muted") + self.detail_meta.setWordWrap(True) + details_layout.addWidget(self.detail_meta) + self.detail_summary = QLabel("") + self.detail_summary.setObjectName("Dim") + details_layout.addWidget(self.detail_summary) + self.detection_table = QTableWidget(0, 3) + self.detection_table.setHorizontalHeaderLabels(["File", "Signature", "Action"]) + self.detection_table.verticalHeader().setVisible(False) + self.detection_table.setSelectionBehavior( + QAbstractItemView.SelectionBehavior.SelectRows) + self.detection_table.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection) + self.detection_table.setEditTriggers( + QAbstractItemView.EditTrigger.NoEditTriggers) + self.detection_table.setShowGrid(False) + self.detection_table.setAlternatingRowColors(False) + self.detection_table.verticalHeader().setDefaultSectionSize(36) + header_view = self.detection_table.horizontalHeader() + header_view.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) + header_view.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents) + header_view.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents) + self.detection_table.itemSelectionChanged.connect(self._update_detection_buttons) + details_layout.addWidget(self.detection_table, 1) + buttons = QHBoxLayout() + buttons.setSpacing(8) + self.quarantine_button = QPushButton("Quarantine selected") + self.quarantine_button.setObjectName("Secondary") + self.quarantine_button.clicked.connect(self.quarantine_selected) + self.delete_button = QPushButton("Delete selected") + self.delete_button.setObjectName("Danger") + self.delete_button.clicked.connect(self.delete_selected) + self.ignore_button = QPushButton("Ignore") + self.ignore_button.setObjectName("Ghost") + self.ignore_button.clicked.connect(self.ignore_selected) + buttons.addWidget(self.quarantine_button) + buttons.addWidget(self.delete_button) + buttons.addWidget(self.ignore_button) + buttons.addStretch(1) + details_layout.addLayout(buttons) + self.detail_stack.addWidget(details) + right.body.addWidget(self.detail_stack, 1) + splitter.addWidget(right) + splitter.setStretchFactor(0, 1) + splitter.setStretchFactor(1, 3) + splitter.setSizes([300, 760]) + return splitter + + def _build_quarantine_tab(self) -> QWidget: + card = components.Card(margins=(16, 14, 16, 14)) + top = QHBoxLayout() + title = QLabel("Quarantine") + title.setObjectName("CardTitle") + top.addWidget(title, 1) + open_button = QPushButton("Open folder") + open_button.setObjectName("Ghost") + open_button.clicked.connect(self.open_quarantine_folder) + top.addWidget(open_button) + card.body.addLayout(top) + subtitle = QLabel("Held files are renamed so they cannot be opened normally. " + "Restore only if you trust the file.") + subtitle.setObjectName("Muted") + subtitle.setWordWrap(True) + card.body.addWidget(subtitle) + self.quarantine_stack = QStackedWidget() + empty = components.EmptyState( + "Quarantine is empty", + "Files that BoltClam quarantines will appear here, with restore and delete options.") + self.quarantine_stack.addWidget(empty) + table_holder = QWidget() + holder_layout = QVBoxLayout(table_holder) + holder_layout.setContentsMargins(0, 0, 0, 0) + holder_layout.setSpacing(8) + self.quarantine_table = QTableWidget(0, 4) + self.quarantine_table.setHorizontalHeaderLabels( + ["Original file", "Signature", "Quarantined", "Status"]) + self.quarantine_table.verticalHeader().setVisible(False) + self.quarantine_table.setSelectionBehavior( + QAbstractItemView.SelectionBehavior.SelectRows) + self.quarantine_table.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection) + self.quarantine_table.setEditTriggers( + QAbstractItemView.EditTrigger.NoEditTriggers) + self.quarantine_table.setShowGrid(False) + self.quarantine_table.verticalHeader().setDefaultSectionSize(36) + qheader = self.quarantine_table.horizontalHeader() + qheader.setSectionResizeMode(0, QHeaderView.ResizeMode.Stretch) + qheader.setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents) + qheader.setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents) + qheader.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents) + self.quarantine_table.itemSelectionChanged.connect(self._update_quarantine_buttons) + holder_layout.addWidget(self.quarantine_table, 1) + buttons = QHBoxLayout() + self.restore_button = QPushButton("Restore selected") + self.restore_button.setObjectName("Secondary") + self.restore_button.clicked.connect(self.restore_selected) + self.purge_button = QPushButton("Delete permanently") + self.purge_button.setObjectName("Danger") + self.purge_button.clicked.connect(self.delete_quarantined_selected) + buttons.addWidget(self.restore_button) + buttons.addWidget(self.purge_button) + buttons.addStretch(1) + holder_layout.addLayout(buttons) + self.quarantine_stack.addWidget(table_holder) + card.body.addWidget(self.quarantine_stack, 1) + return card + + # -- settings ---------------------------------------------------------- + + def reload_settings(self) -> None: + settings = self.context.settings + with QSignalBlocker(self.homes_radio), QSignalBlocker(self.full_radio), \ + QSignalBlocker(self.auto_combo): + (self.full_radio if settings.scan_scope == "full" else self.homes_radio).setChecked(True) + index = self.auto_combo.findData(settings.auto_action) + self.auto_combo.setCurrentIndex(index if index >= 0 else 0) + self._update_auto_note(settings.auto_action) + self._update_scope_note() + + def _scope_changed(self, checked: bool) -> None: + if not checked: + return + scope = "full" if self.full_radio.isChecked() else "homes" + try: + self.context.settings = self.context.settings_store.update(scan_scope=scope) + if self.context.realtime is not None: + self.context.realtime.set_settings(self.context.settings) + except Exception: + logger.exception("Could not save scan scope") + self._update_scope_note() + + def _auto_action_changed(self) -> None: + value = self.auto_combo.currentData() or "quarantine" + try: + self.context.settings = self.context.settings_store.update(auto_action=value) + if self.context.realtime is not None: + self.context.realtime.set_settings(self.context.settings) + except Exception: + logger.exception("Could not save auto action") + self._update_auto_note(value) + + def _update_auto_note(self, action: str) -> None: + if action == "delete": + self.auto_note.setText("Delete removes the original immediately. Quarantine is safer.") + self.auto_note.setStyleSheet(f"color: {theme.DANGER};") + elif action == "notify": + self.auto_note.setText("Notify leaves the file in place and marks it for review.") + self.auto_note.setStyleSheet(f"color: {theme.AMBER};") + else: + self.auto_note.setText("Files are moved to quarantine and can be restored from the Quarantine tab.") + self.auto_note.setStyleSheet(f"color: {theme.DIM};") + + def _update_scope_note(self) -> None: + if not hasattr(self, "scan_scope_label"): + return + scope = "full" if self.full_radio.isChecked() else "homes" + self.scan_scope_label.setText(f"Scope: {human_scope(scope)}") + self._refresh_engine_line() + + def _refresh_engine_line(self) -> None: + if not hasattr(self, "engine_line"): + return + info = self.context.clamav.info() + if not info.available: + self.engine_line.setText("ClamAV is not configured. Set its folder in Settings.") + self.engine_line.setStyleSheet(f"color: {theme.AMBER};") + elif info.db_path: + self.engine_line.setText( + f"Engine {info.engine_version or 'ClamAV'} · signatures " + f"{info.db_version or 'unknown'} · definitions {info.db_date or 'unknown'}") + self.engine_line.setStyleSheet(f"color: {theme.MUTED};") + else: + self.engine_line.setText( + f"Engine {info.engine_version or 'ClamAV'} · no signature database yet — " + "run Update definitions.") + self.engine_line.setStyleSheet(f"color: {theme.AMBER};") + + # -- scan lifecycle ---------------------------------------------------- + + def start_scan(self) -> None: + if self._worker is not None and self._worker.isRunning(): + return + if not self.context.clamav.info().available: + answer = QMessageBox.question( + self, "ClamAV is not configured", + "BoltClam needs ClamAV's clamscan.exe before it can scan.\n\nOpen Settings now?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.Yes) + if answer == QMessageBox.StandardButton.Yes: + self.settings_requested.emit() + return + scope = "full" if self.full_radio.isChecked() else "homes" + try: + self.context.settings = self.context.settings_store.update(scan_scope=scope) + except Exception: + logger.exception("Could not save scan scope") + self._set_scanning(True, scope) + worker = ScanWorker(self.context.clamav, self.context.storage, + self.context.quarantine, self.context.settings, scope, self) + worker.started_scan.connect(self._on_scan_started) + worker.progress.connect(self._on_progress) + worker.detection_found.connect(self._on_detection) + worker.log_line.connect(self._on_log) + worker.status.connect(self._on_status) + worker.finished_scan.connect(self._on_finished) + worker.failed.connect(self._on_failed) + worker.finished.connect(self._worker_finished) + worker.finished.connect(worker.deleteLater) + self._worker = worker + worker.start() + + def cancel_scan(self) -> None: + if self._worker is not None and self._worker.isRunning(): + self._worker.cancel() + self.scan_state.setText("Stopping scan…") + self.cancel_button.setEnabled(False) + + def _set_scanning(self, scanning: bool, scope: str | None = None) -> None: + self.scan_button.set_scanning(scanning) + self.scan_button.setEnabled(not scanning) + self.start_button.setEnabled(not scanning) + self.cancel_button.setVisible(scanning) + self.cancel_button.setEnabled(scanning) + self.homes_radio.setEnabled(not scanning) + self.full_radio.setEnabled(not scanning) + self.auto_combo.setEnabled(not scanning) + self.definitions_button.setEnabled(not scanning) + if scanning: + self.progress.show() + self.current_file.show() + self.scan_state.setText("Scanning") + self.scan_detail.setText("ClamAV is reading files. You can keep using BoltClam.") + if scope: + self.scan_scope_label.setText(f"Scope: {human_scope(scope)}") + else: + self.progress.hide() + self.current_file.hide() + self.scan_state.setText("Ready to scan") + self.scan_detail.setText( + "Choose a scope on the right. User-home scans cover every profile; " + "full-disk scans cover every fixed drive.") + + def _on_scan_started(self, scan_id: int, targets: list) -> None: + self._current_scan_id = scan_id + self.scan_detail.setText(f"Scanning {len(targets)} root(s) · scan #{scan_id}") + self.current_file.setText("Waiting for the first file…") + + def _on_progress(self, path: str, detections: int) -> None: + if path: + self.current_file.setText(path) + if detections: + self.scan_detail.setText( + f"{detections} detection(s) so far. Remediation follows your " + f"'{self.context.settings.auto_action}' setting.") + + def _on_detection(self, detection: Detection) -> None: + self.scan_detail.setText( + f"Found {detection.signature} in {display_name(detection.path)} — " + f"{human_action(detection.action)}") + + def _on_log(self, message: str) -> None: + if message: + self.current_file.setText(message[:300]) + + def _on_status(self, text: str) -> None: + if self._worker is not None and self._worker.isRunning(): + self.scan_detail.setText(text) + + def _on_finished(self, record) -> None: + self._set_scanning(False) + self.refresh_history(select_id=record.id) + if record.infected_files: + self.toast_requested.emit( + "Scan finished with detections", + f"{record.infected_files} threat(s) in {record.scanned_files:,} files. Review the results.", + "critical") + else: + self.toast_requested.emit( + "Scan finished", + f"{record.scanned_files:,} files scanned. No threats found.", + "good") + self.last_scan_label.setText( + f"Last scan {theme.relative_time(record.started_at)} · " + f"{record.scanned_files:,} files · {record.threat_count} threat(s)") + self.scan_finished.emit() + + def _on_failed(self, message: str) -> None: + self._set_scanning(False) + self.scan_detail.setText(message) + self.toast_requested.emit("Scan could not finish", message, "critical") + + def _worker_finished(self) -> None: + self._worker = None + + # -- history / details ------------------------------------------------- + + def refresh(self) -> None: + self.refresh_history() + self.refresh_quarantine() + latest = self.context.storage.latest_scan() + if latest: + self.last_scan_label.setText( + f"Last scan {theme.relative_time(latest.started_at)} · " + f"{latest.scanned_files:,} files · {latest.threat_count} threat(s)") + else: + self.last_scan_label.setText("No scan has been run yet.") + self._refresh_engine_line() + + def refresh_history(self, select_id: int | None = None) -> None: + scans = self.context.storage.list_scans(100) + current = select_id + if current is None and self._current_scan_id is not None: + current = self._current_scan_id + blocker = QSignalBlocker(self.history) + self.history.clear() + target_row = -1 + for row, scan in enumerate(scans): + item = QListWidgetItem() + item.setData(Qt.ItemDataRole.UserRole, scan.id) + widget = ScanHistoryItem(scan) + item.setSizeHint(QSize(260, 72)) + self.history.addItem(item) + self.history.setItemWidget(item, widget) + if current is not None and scan.id == current: + target_row = row + del blocker + if target_row >= 0: + self.history.setCurrentRow(target_row) + elif scans and select_id is None: + self.history.setCurrentRow(0) + elif not scans: + self._show_detail_empty() + self._current_scan_id = None + + def _history_selected(self, current: QListWidgetItem | None, previous=None) -> None: + if current is None: + return + scan_id = current.data(Qt.ItemDataRole.UserRole) + record = self.context.storage.get_scan(int(scan_id)) if scan_id is not None else None + if record is None: + self._show_detail_empty() + return + self._current_scan_id = record.id + self.detail_stack.setCurrentIndex(1) + self.detail_title.setText(f"Scan #{record.id}") + severity = "good" if record.status == "completed" else ( + "warning" if record.status == "completed_with_errors" else + "critical" if record.status == "failed" else "muted") + self.detail_status.setText(record.status.replace("_", " ").title()) + self.detail_status.set_severity(severity) + self.detail_meta.setText( + f"{theme.format_datetime(record.started_at)} · {record.scope_label} · " + f"{len(record.paths)} root(s)") + self.detail_summary.setText( + f"{record.scanned_files:,} files scanned · {record.threat_count} threat(s) · " + f"{record.errors} error(s) · {theme.format_duration(record.duration_seconds)}") + self._fill_detections(record.detections) + + def _fill_detections(self, detections: list[Detection]) -> None: + self.detection_table.setRowCount(0) + for detection in detections: + row = self.detection_table.rowCount() + self.detection_table.insertRow(row) + path_item = QTableWidgetItem(display_name(detection.path)) + path_item.setToolTip(detection.path) + path_item.setData(Qt.ItemDataRole.UserRole, detection) + self.detection_table.setItem(row, 0, path_item) + signature_item = QTableWidgetItem(detection.signature) + signature_item.setToolTip(detection.signature) + self.detection_table.setItem(row, 1, signature_item) + action_item = QTableWidgetItem(human_action(detection.action)) + action_item.setForeground(QColor(theme.status_color(detection.action))) + self.detection_table.setItem(row, 2, action_item) + self.detection_table.clearSelection() + self._update_detection_buttons() + + def _show_detail_empty(self) -> None: + self.detection_table.setRowCount(0) + self.detail_stack.setCurrentIndex(0) + + def _selected_detections(self) -> list[Detection]: + rows = self.detection_table.selectionModel().selectedRows() + result: list[Detection] = [] + for index in rows: + item = self.detection_table.item(index.row(), 0) + detection = item.data(Qt.ItemDataRole.UserRole) if item else None + if isinstance(detection, Detection): + result.append(detection) + return result + + def _update_detection_buttons(self) -> None: + selections = self._selected_detections() + actionable = [item for item in selections + if item.action not in ("quarantined", "deleted", "ignored")] + self.quarantine_button.setEnabled(bool(actionable)) + self.delete_button.setEnabled(bool(actionable)) + self.ignore_button.setEnabled(bool(actionable)) + + # -- detection actions ------------------------------------------------- + + def _record_detection_action(self, detection: Detection, action: str, + quarantine_id: str | None = None) -> None: + if detection.id is None: + return + try: + self.context.storage.update_detection_action(detection.id, action, quarantine_id) + except Exception: + logger.exception("Could not update detection action") + detection.action = action + if quarantine_id: + detection.quarantine_id = quarantine_id + + def quarantine_selected(self) -> None: + selections = [item for item in self._selected_detections() + if item.action not in ("quarantined", "deleted", "ignored")] + if not selections: + return + failures = 0 + for detection in selections: + try: + item = self.context.quarantine.quarantine_file( + Path(detection.path), detection.signature, + scan_id=self._current_scan_id, reason="Manual from scan results") + self._record_detection_action(detection, "quarantined", item.id) + except (QuarantineError, OSError) as exc: + failures += 1 + self._record_detection_action(detection, "failed") + logger.warning("Manual quarantine failed for %s: %s", detection.path, exc) + self._history_selected(self.history.currentItem()) + self.refresh_quarantine() + self.refresh_history(select_id=self._current_scan_id) + self.toast_requested.emit( + "Quarantine complete" if not failures else "Some files could not be quarantined", + f"{len(selections) - failures} quarantined, {failures} failed.", + "good" if not failures else "critical") + + def delete_selected(self) -> None: + selections = [item for item in self._selected_detections() + if item.action not in ("quarantined", "deleted", "ignored")] + if not selections: + return + answer = QMessageBox.warning( + self, "Delete detected files?", + f"Permanently delete {len(selections)} detected file(s)?\n\n" + "This cannot be undone. Quarantine is reversible and is the safer choice.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel, + QMessageBox.StandardButton.Cancel) + if answer != QMessageBox.StandardButton.Yes: + return + failures = 0 + for detection in selections: + try: + delete_file(Path(detection.path)) + self._record_detection_action(detection, "deleted") + except (QuarantineError, OSError) as exc: + failures += 1 + self._record_detection_action(detection, "failed") + logger.warning("Manual delete failed for %s: %s", detection.path, exc) + self.refresh_history(select_id=self._current_scan_id) + self.toast_requested.emit( + "Files deleted" if not failures else "Some files could not be deleted", + f"{len(selections) - failures} deleted, {failures} failed.", + "good" if not failures else "critical") + + def ignore_selected(self) -> None: + selections = self._selected_detections() + for detection in selections: + self._record_detection_action(detection, "ignored") + self.refresh_history(select_id=self._current_scan_id) + self.toast_requested.emit("Detection ignored", + f"{len(selections)} detection(s) marked ignored.", "warning") + + # -- quarantine -------------------------------------------------------- + + def refresh_quarantine(self) -> None: + items = self.context.storage.list_quarantine(status="quarantined") + self.quarantine_table.setRowCount(0) + for item in items: + row = self.quarantine_table.rowCount() + self.quarantine_table.insertRow(row) + original = QTableWidgetItem(display_name(item.original_path)) + original.setToolTip(item.original_path) + original.setData(Qt.ItemDataRole.UserRole, item.id) + self.quarantine_table.setItem(row, 0, original) + self.quarantine_table.setItem(row, 1, QTableWidgetItem(item.signature)) + self.quarantine_table.setItem(row, 2, QTableWidgetItem( + theme.format_datetime(item.created_at))) + status_item = QTableWidgetItem(item.status.title()) + status_item.setForeground(QColor(theme.TEAL)) + self.quarantine_table.setItem(row, 3, status_item) + self.quarantine_stack.setCurrentIndex(1 if items else 0) + self.quarantine_table.clearSelection() + self._update_quarantine_buttons() + + def _selected_quarantine(self) -> list[str]: + rows = self.quarantine_table.selectionModel().selectedRows() + result = [] + for index in rows: + item = self.quarantine_table.item(index.row(), 0) + if item is not None and item.data(Qt.ItemDataRole.UserRole): + result.append(str(item.data(Qt.ItemDataRole.UserRole))) + return result + + def _update_quarantine_buttons(self) -> None: + selected = bool(self._selected_quarantine()) + self.restore_button.setEnabled(selected) + self.purge_button.setEnabled(selected) + + def restore_selected(self) -> None: + ids = self._selected_quarantine() + if not ids: + return + answer = QMessageBox.warning( + self, "Restore quarantined files?", + f"Restore {len(ids)} file(s) to their original locations?\n\n" + "Only restore files you trust. A detected file can be malicious.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel, + QMessageBox.StandardButton.Cancel) + if answer != QMessageBox.StandardButton.Yes: + return + failures = 0 + for item_id in ids: + try: + self.context.quarantine.restore(item_id) + except QuarantineError as exc: + failures += 1 + logger.warning("Restore failed for %s: %s", item_id, exc) + self.refresh_quarantine() + self.toast_requested.emit( + "Files restored" if not failures else "Some files could not be restored", + f"{len(ids) - failures} restored, {failures} failed.", + "good" if not failures else "critical") + + def delete_quarantined_selected(self) -> None: + ids = self._selected_quarantine() + if not ids: + return + answer = QMessageBox.warning( + self, "Delete quarantined files?", + f"Permanently delete {len(ids)} quarantined file(s)? This cannot be undone.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel, + QMessageBox.StandardButton.Cancel) + if answer != QMessageBox.StandardButton.Yes: + return + failures = 0 + for item_id in ids: + try: + self.context.quarantine.delete(item_id) + except QuarantineError as exc: + failures += 1 + logger.warning("Quarantine delete failed for %s: %s", item_id, exc) + self.refresh_quarantine() + self.toast_requested.emit( + "Quarantined files deleted" if not failures else "Some files could not be deleted", + f"{len(ids) - failures} deleted, {failures} failed.", + "good" if not failures else "critical") + + def open_quarantine_folder(self) -> None: + if not self.context.quarantine.open_folder(): + self.toast_requested.emit("Could not open folder", + "The quarantine folder could not be opened.", "warning") + + # -- definitions ------------------------------------------------------- + + def update_definitions(self) -> None: + if self._definition_worker is not None and self._definition_worker.isRunning(): + return + if self._worker is not None and self._worker.isRunning(): + return + self.definitions_button.setEnabled(False) + self.start_button.setEnabled(False) + self.progress.show() + self.progress.setRange(0, 0) + self.current_file.show() + self.current_file.setText("Starting freshclam…") + self.scan_state.setText("Updating definitions") + self.scan_detail.setText("BoltClam is downloading the latest ClamAV signatures.") + worker = DefinitionWorker(self.context.clamav, self) + worker.log_line.connect(self._on_log) + worker.finished_ok.connect(self._definitions_ok) + worker.failed.connect(self._definitions_failed) + worker.finished.connect(self._definition_finished) + worker.finished.connect(worker.deleteLater) + self._definition_worker = worker + worker.start() + + def _definitions_ok(self, message: str) -> None: + self.context.clamav.invalidate() + self._refresh_engine_line() + self.scan_state.setText("Definitions updated") + self.scan_detail.setText(message) + self.toast_requested.emit("Definitions updated", message, "good") + self.definitions_updated.emit() + + def _definitions_failed(self, message: str) -> None: + self.scan_state.setText("Definition update failed") + self.scan_detail.setText(message) + self.toast_requested.emit("Definition update failed", message, "critical") + + def _definition_finished(self) -> None: + self._definition_worker = None + self.definitions_button.setEnabled(True) + self.start_button.setEnabled(True) + self.progress.hide() + self.current_file.hide() + self.progress.setRange(0, 0) + + def stop(self) -> None: + if self._worker is not None and self._worker.isRunning(): + self._worker.cancel() + stop_thread(self._worker, wait_ms=5000) + if self._definition_worker is not None and self._definition_worker.isRunning(): + self._definition_worker.cancel() + stop_thread(self._definition_worker, wait_ms=4000) + self._worker = None + self._definition_worker = None diff --git a/src/boltclam/ui/settings_page.py b/src/boltclam/ui/settings_page.py new file mode 100644 index 0000000..0086525 --- /dev/null +++ b/src/boltclam/ui/settings_page.py @@ -0,0 +1,651 @@ +"""Settings: every persisted option, validation, logging and log upload.""" + +from __future__ import annotations + +import dataclasses +import logging +import os +import time +from pathlib import Path + +from PyQt6.QtCore import Qt, QThread, pyqtSignal +from PyQt6.QtGui import QFont +from PyQt6.QtWidgets import ( + QApplication, + QCheckBox, + QComboBox, + QFileDialog, + QFrame, + QHBoxLayout, + QLabel, + QLineEdit, + QMessageBox, + QPlainTextEdit, + QPushButton, + QScrollArea, + QVBoxLayout, + QWidget, +) + +from .. import paths +from ..clamav import ClamAVService, find_clamscan, parse_extra_options +from ..config import AppSettings +from ..context import AppContext +from ..logging_setup import configure as configure_logging +from ..logs import DiagnosticsError, build_bundle, upload_bundle +from ..system_info import collect +from . import components, theme +from .threads import stop_thread + +logger = logging.getLogger(__name__) + + +class DiagnosticsWorker(QThread): + succeeded = pyqtSignal(object) + failed = pyqtSignal(str) + + def __init__(self, context: AppContext, settings: AppSettings, parent=None) -> None: + super().__init__(parent) + self.context = context + self.settings = settings + + def run(self) -> None: + try: + snapshot = collect( + self.settings, self.context.clamav.info(), self.context.storage, + realtime_running=(self.context.realtime.running + if self.context.realtime else None), + force=True) + bundle = build_bundle( + self.settings, self.context.clamav.info(), + system_snapshot=snapshot, account=self.context.account, + realtime_roots=(self.context.realtime.watched_roots + if self.context.realtime else [])) + api = self.context.api or self.context.rebuild_api() + result = upload_bundle(api, bundle, self.settings) + self.succeeded.emit(result) + except DiagnosticsError as exc: + self.failed.emit(str(exc)) + except Exception as exc: + logger.exception("Diagnostics upload failed") + self.failed.emit(str(exc)) + + +class SettingsPage(QWidget): + settings_saved = pyqtSignal(object) + sign_out_requested = pyqtSignal() + toast_requested = pyqtSignal(str, str, str) + definitions_requested = pyqtSignal() + + def __init__(self, context: AppContext, parent=None) -> None: + super().__init__(parent) + self.context = context + self._loading = False + self._dirty = False + self._diagnostics_worker: DiagnosticsWorker | None = None + self._build() + self.reload_settings() + + # -- construction ------------------------------------------------------ + + def _build(self) -> None: + outer = QVBoxLayout(self) + outer.setContentsMargins(0, 0, 0, 0) + outer.setSpacing(0) + scroll = QScrollArea() + scroll.setWidgetResizable(True) + scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + outer.addWidget(scroll, 1) + content = QWidget() + content.setObjectName("Content") + scroll.setWidget(content) + self.content_layout = QVBoxLayout(content) + self.content_layout.setContentsMargins(30, 24, 30, 22) + self.content_layout.setSpacing(15) + header = components.PageHeader( + "Settings", "Engine, protection, logging and account preferences.") + self.content_layout.addWidget(header) + + self.content_layout.addWidget(self._build_update_section()) + self.content_layout.addWidget(self._build_clamav_section()) + self.content_layout.addWidget(self._build_protection_section()) + self.content_layout.addWidget(self._build_scan_options()) + self.content_layout.addWidget(self._build_logging_section()) + self.content_layout.addWidget(self._build_account_section()) + self.content_layout.addStretch(1) + outer.addWidget(self._build_save_bar()) + + def _build_update_section(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "Updates", "Release receives stable builds. Beta includes prereleases.")[0]) + row = QHBoxLayout() + label = QLabel("Update channel") + label.setObjectName("Muted") + row.addWidget(label, 1) + self.channel_combo = QComboBox() + self.channel_combo.addItem("Release", "release") + self.channel_combo.addItem("Beta (includes prereleases)", "beta") + self.channel_combo.setMinimumWidth(250) + self.channel_combo.currentIndexChanged.connect(self._mark_dirty) + row.addWidget(self.channel_combo, 0) + card.body.addLayout(row) + return card + + def _build_clamav_section(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "ClamAV engine", "BoltClam calls clamscan.exe from this location.")[0]) + row = QHBoxLayout() + row.setSpacing(8) + self.clamav_path = QLineEdit() + self.clamav_path.setPlaceholderText(r"C:\Program Files\ClamAV") + self.clamav_path.textChanged.connect(self._mark_dirty) + row.addWidget(self.clamav_path, 1) + browse = QPushButton("Browse") + browse.setObjectName("Secondary") + browse.clicked.connect(self.browse_clamav) + row.addWidget(browse) + detect = QPushButton("Auto-detect") + detect.setObjectName("Ghost") + detect.clicked.connect(self.detect_clamav) + row.addWidget(detect) + card.body.addLayout(row) + status_row = QHBoxLayout() + self.clamav_status = QLabel("Not checked") + self.clamav_status.setObjectName("Muted") + self.clamav_status.setWordWrap(True) + status_row.addWidget(self.clamav_status, 1) + verify = QPushButton("Verify") + verify.setObjectName("Secondary") + verify.clicked.connect(self.verify_clamav) + status_row.addWidget(verify, 0) + card.body.addLayout(status_row) + divider = components.Divider() + card.body.addWidget(divider) + defs_row = QHBoxLayout() + self.definitions_status = QLabel("") + self.definitions_status.setObjectName("Muted") + self.definitions_status.setWordWrap(True) + defs_row.addWidget(self.definitions_status, 1) + update_defs = QPushButton("Update definitions") + update_defs.setObjectName("Secondary") + update_defs.clicked.connect(self.definitions_requested) + defs_row.addWidget(update_defs, 0) + card.body.addLayout(defs_row) + note = QLabel( + "BoltClam keeps freshclam signatures in its own app-data folder so updates do not " + "need administrator access. ClamAV itself is installed separately.") + note.setObjectName("Dim") + note.setWordWrap(True) + card.body.addWidget(note) + return card + + def _build_protection_section(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "Protection", "Real-time browser downloads and automatic remediation.")[0]) + self.realtime_checkbox = QCheckBox("Watch browser downloads and scan new files") + self.realtime_checkbox.toggled.connect(self._realtime_toggled) + card.body.addWidget(self.realtime_checkbox) + self.notify_checkbox = QCheckBox("Show a notification when a download is blocked") + self.notify_checkbox.toggled.connect(self._mark_dirty) + card.body.addWidget(self.notify_checkbox) + self.watched_label = QLabel("") + self.watched_label.setObjectName("Dim") + self.watched_label.setWordWrap(True) + card.body.addWidget(self.watched_label) + row = QHBoxLayout() + label = QLabel("When ClamAV finds something") + label.setObjectName("Muted") + row.addWidget(label, 1) + self.auto_combo = QComboBox() + self.auto_combo.addItem("Quarantine (recommended)", "quarantine") + self.auto_combo.addItem("Delete permanently", "delete") + self.auto_combo.addItem("Notify only", "notify") + self.auto_combo.setMinimumWidth(240) + self.auto_combo.currentIndexChanged.connect(self._auto_changed) + row.addWidget(self.auto_combo, 0) + card.body.addLayout(row) + self.auto_note = QLabel("") + self.auto_note.setObjectName("Dim") + self.auto_note.setWordWrap(True) + card.body.addWidget(self.auto_note) + dirs_label = QLabel("Additional download folders") + dirs_label.setObjectName("Muted") + card.body.addWidget(dirs_label) + self.extra_dirs = QLineEdit() + self.extra_dirs.setPlaceholderText("Optional; separate multiple paths with ;") + self.extra_dirs.textChanged.connect(self._mark_dirty) + card.body.addWidget(self.extra_dirs) + note = QLabel( + "BoltClam reads each browser's configured download folder (Chrome, Edge, Brave, " + "Firefox, Vivaldi, Opera) plus every user's Downloads folder. The watcher waits " + "for a download to settle, holds the file exclusively while ClamAV scans a copy, " + "then applies the setting above.") + note.setObjectName("Dim") + note.setWordWrap(True) + card.body.addWidget(note) + return card + + def _build_scan_options(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "ClamAV extra options", "Advanced flags passed to clamscan. Leave blank for defaults.")[0]) + self.extra_options = QPlainTextEdit() + self.extra_options.setObjectName("MonoInput") + self.extra_options.setPlaceholderText("--max-filesize=200M\n--exclude-dir=regex") + self.extra_options.setFixedHeight(92) + self.extra_options.textChanged.connect(self._mark_dirty) + card.body.addWidget(self.extra_options) + self.extra_options_error = QLabel("") + self.extra_options_error.setStyleSheet(f"color: {theme.DANGER}; font-size: 12px;") + self.extra_options_error.setWordWrap(True) + self.extra_options_error.hide() + card.body.addWidget(self.extra_options_error) + row = QHBoxLayout() + note = QLabel("Destructive options such as --remove and --move are refused; BoltClam " + "handles remediation and records every action.") + note.setObjectName("Dim") + note.setWordWrap(True) + row.addWidget(note, 1) + reset = QPushButton("Reset") + reset.setObjectName("Ghost") + reset.clicked.connect(lambda: self.extra_options.setPlainText("")) + row.addWidget(reset, 0) + card.body.addLayout(row) + return card + + def _build_logging_section(self) -> QWidget: + card = components.Card() + card.body.addWidget(components.card_header( + "Logging & diagnostics", "Logs stay on this PC unless you explicitly send them.")[0]) + self.logging_checkbox = QCheckBox("Enable application logging") + self.logging_checkbox.toggled.connect(self._logging_toggled) + card.body.addWidget(self.logging_checkbox) + self.debug_checkbox = QCheckBox("Include debug-level detail") + self.debug_checkbox.toggled.connect(self._mark_dirty) + card.body.addWidget(self.debug_checkbox) + logs_row = QHBoxLayout() + self.logs_location = QLabel(str(paths.logs_dir())) + self.logs_location.setObjectName("Mono") + logs_row.addWidget(self.logs_location, 1) + open_logs = QPushButton("Open logs folder") + open_logs.setObjectName("Ghost") + open_logs.clicked.connect(self.open_logs) + logs_row.addWidget(open_logs, 0) + card.body.addLayout(logs_row) + divider = components.Divider() + card.body.addWidget(divider) + self.send_logs_checkbox = QCheckBox( + "Send logs to Bolt's systems for analysis when I choose") + self.send_logs_checkbox.toggled.connect(self._send_logs_toggled) + card.body.addWidget(self.send_logs_checkbox) + privacy = QLabel( + "A diagnostics bundle includes app logs, scan/quarantine summaries, settings, " + "system summary and your account email. API keys, passwords and file contents are " + "never included; user-profile paths are replaced with %USERPROFILE%.") + privacy.setObjectName("Dim") + privacy.setWordWrap(True) + card.body.addWidget(privacy) + bottom = QHBoxLayout() + self.last_upload_label = QLabel("") + self.last_upload_label.setObjectName("Dim") + bottom.addWidget(self.last_upload_label, 1) + self.send_now_button = QPushButton("Send diagnostics now") + self.send_now_button.setObjectName("Secondary") + self.send_now_button.clicked.connect(self.send_logs_now) + bottom.addWidget(self.send_now_button, 0) + card.body.addLayout(bottom) + return card + + def _build_account_section(self) -> QWidget: + card = components.Card() + row = QHBoxLayout() + row.setSpacing(12) + self.account_avatar = components.Avatar(self.context.user_name, 40) + row.addWidget(self.account_avatar, 0) + text_box = QVBoxLayout() + text_box.setSpacing(1) + self.account_name = QLabel(self.context.user_name) + self.account_name.setFont(theme.body_font(10.5, QFont.Weight.DemiBold)) + self.account_email = QLabel(self.context.user_email) + self.account_email.setObjectName("Muted") + text_box.addWidget(self.account_name) + text_box.addWidget(self.account_email) + row.addLayout(text_box, 1) + self.account_plan = components.Pill(self.context.plan_label, "info") + row.addWidget(self.account_plan, 0) + sign_out = QPushButton("Sign out") + sign_out.setObjectName("Danger") + sign_out.clicked.connect(self.sign_out) + row.addWidget(sign_out, 0) + card.body.addLayout(row) + manage = QLabel( + f'Manage your Bolt account and subscription') + manage.setOpenExternalLinks(True) + manage.setFont(theme.body_font(9.2, QFont.Weight.DemiBold)) + card.body.addWidget(manage) + return card + + def _build_save_bar(self) -> QWidget: + bar = QFrame() + bar.setStyleSheet( + f"QFrame {{ background: {theme.SIDEBAR}; border-top: 1px solid {theme.BORDER}; }}") + layout = QHBoxLayout(bar) + layout.setContentsMargins(30, 11, 30, 11) + layout.setSpacing(9) + self.unsaved_label = QLabel("") + self.unsaved_label.setStyleSheet(f"color: {theme.AMBER}; font-size: 12px;") + layout.addWidget(self.unsaved_label, 1) + self.discard_button = QPushButton("Discard") + self.discard_button.setObjectName("Secondary") + self.discard_button.clicked.connect(self.reload_settings) + self.discard_button.setEnabled(False) + layout.addWidget(self.discard_button) + self.save_button = QPushButton("Save changes") + self.save_button.setObjectName("Primary") + self.save_button.clicked.connect(self.save) + self.save_button.setEnabled(False) + layout.addWidget(self.save_button) + return bar + + # -- state ------------------------------------------------------------- + + def reload_settings(self) -> None: + settings = self.context.settings_store.load() + self.context.update_settings(settings) + self._loading = True + try: + index = self.channel_combo.findData(settings.update_channel) + self.channel_combo.setCurrentIndex(index if index >= 0 else 0) + self.clamav_path.setText(settings.clamav_path) + self.realtime_checkbox.setChecked(settings.realtime_enabled) + self.notify_checkbox.setChecked(settings.notify_on_detection) + auto_index = self.auto_combo.findData(settings.auto_action) + self.auto_combo.setCurrentIndex(auto_index if auto_index >= 0 else 0) + self.extra_dirs.setText("; ".join(settings.extra_watch_dirs)) + self.extra_options.setPlainText(settings.extra_options) + self.logging_checkbox.setChecked(settings.logging_enabled) + self.debug_checkbox.setChecked(settings.debug_logging) + self.debug_checkbox.setEnabled(settings.logging_enabled) + self.send_logs_checkbox.setChecked(settings.send_logs) + self.send_now_button.setEnabled(settings.send_logs) + self.last_upload_label.setText( + f"Last sent: {settings.last_log_upload}" if settings.last_log_upload + else "Diagnostics have not been sent.") + self.extra_options_error.hide() + self._update_auto_note(settings.auto_action) + self._refresh_clamav_status() + self._refresh_account() + self._refresh_watched() + finally: + self._loading = False + self._set_dirty(False) + + def _refresh_clamav_status(self) -> None: + configured = self.clamav_path.text().strip() + if configured: + found, error = find_clamscan(configured) + if found is None: + self.clamav_status.setText(error) + self.clamav_status.setStyleSheet(f"color: {theme.DANGER};") + self.definitions_status.setText("Definitions unavailable until ClamAV is found.") + return + info = self.context.clamav.info() + if info.available: + self.clamav_status.setText( + f"Ready · {info.summary}" + (f" · {info.path}" if info.path else "")) + self.clamav_status.setStyleSheet(f"color: {theme.TEAL};") + if info.db_path: + self.definitions_status.setText( + f"Definitions: {info.db_version or 'present'} · updated {info.db_date or 'unknown'}" + + (f" · {info.db_age_days:.0f} days old" if info.db_age_days is not None else "")) + else: + self.definitions_status.setText( + "Definitions: not found. Use Update definitions to download them.") + else: + self.clamav_status.setText(info.error or "ClamAV is not configured.") + self.clamav_status.setStyleSheet(f"color: {theme.AMBER};") + self.definitions_status.setText( + "Install ClamAV, then set its folder here or use Auto-detect.") + + def _refresh_account(self) -> None: + self.account_avatar.set_name(self.context.user_name) + self.account_name.setText(self.context.user_name) + self.account_email.setText(self.context.user_email or "Signed in with Bolt") + self.account_plan.setText(self.context.plan_label) + self.account_plan.set_severity("good" if self.context.licensed else "muted") + + def refresh_account(self) -> None: + self._refresh_account() + + def _refresh_watched(self) -> None: + realtime = self.context.realtime + if realtime is None: + self.watched_label.setText("Real-time watcher is not initialized.") + return + roots = realtime.watched_roots + if not self.context.settings.realtime_enabled: + self.watched_label.setText("Real-time protection is off.") + elif realtime.running: + self.watched_label.setText( + f"Watching {len(roots)} folder(s): " + ", ".join(roots[:4]) + + ("…" if len(roots) > 4 else "")) + else: + self.watched_label.setText("Real-time watcher is not running.") + + def _logging_toggled(self, checked: bool) -> None: + self.debug_checkbox.setEnabled(checked) + if not checked: + self.debug_checkbox.setChecked(False) + self._mark_dirty() + + def _realtime_toggled(self, checked: bool) -> None: + self.notify_checkbox.setEnabled(checked) + self._mark_dirty() + + def _send_logs_toggled(self, checked: bool) -> None: + self.send_now_button.setEnabled(checked) + self._mark_dirty() + + def _auto_changed(self) -> None: + self._update_auto_note(self.auto_combo.currentData() or "quarantine") + self._mark_dirty() + + def _update_auto_note(self, action: str) -> None: + if action == "delete": + self.auto_note.setText("Delete removes the original immediately. Quarantine is safer.") + self.auto_note.setStyleSheet(f"color: {theme.DANGER};") + elif action == "notify": + self.auto_note.setText("Notify leaves the file in place and marks it for review.") + self.auto_note.setStyleSheet(f"color: {theme.AMBER};") + else: + self.auto_note.setText( + "Files are moved to quarantine and can be restored from Scan & results.") + self.auto_note.setStyleSheet(f"color: {theme.DIM};") + + def _mark_dirty(self, *args) -> None: + if self._loading: + return + self._set_dirty(True) + + def _set_dirty(self, dirty: bool) -> None: + self._dirty = bool(dirty) + self.save_button.setEnabled(self._dirty) + self.discard_button.setEnabled(self._dirty) + self.unsaved_label.setText("Unsaved changes" if self._dirty else "") + + # -- actions ----------------------------------------------------------- + + def browse_clamav(self) -> None: + start = self.clamav_path.text().strip() or str(Path.home()) + filename, _ = QFileDialog.getOpenFileName( + self, "Select clamscan.exe", start, + "ClamAV executable (clamscan.exe clamscan);;All files (*)") + if filename: + self.clamav_path.setText(filename) + + def detect_clamav(self) -> None: + QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor) + try: + service = ClamAVService("") + info = service.info(force=True) + finally: + QApplication.restoreOverrideCursor() + if info.available and info.path: + self.clamav_path.setText(str(info.path)) + self.clamav_status.setText(f"Detected · {info.summary}") + self.clamav_status.setStyleSheet(f"color: {theme.TEAL};") + else: + self.clamav_status.setText( + "ClamAV was not found in the usual locations. Install it, then browse to clamscan.exe.") + self.clamav_status.setStyleSheet(f"color: {theme.AMBER};") + + def verify_clamav(self) -> None: + path = self.clamav_path.text().strip() + QApplication.setOverrideCursor(Qt.CursorShape.WaitCursor) + try: + service = ClamAVService(path) + info = service.info(force=True) + finally: + QApplication.restoreOverrideCursor() + if info.available: + self.clamav_status.setText(f"Verified · {info.summary}") + self.clamav_status.setStyleSheet(f"color: {theme.TEAL};") + self.toast_requested.emit("ClamAV verified", info.summary, "good") + self._refresh_clamav_status() + else: + self.clamav_status.setText(info.error or "ClamAV could not be started.") + self.clamav_status.setStyleSheet(f"color: {theme.DANGER};") + self.toast_requested.emit( + "ClamAV verification failed", info.error or "clamscan.exe could not run.", "critical") + + def open_logs(self) -> None: + paths.ensure_dirs() + if not paths.open_path(paths.logs_dir()): + self.toast_requested.emit("Could not open logs", str(paths.logs_dir()), "warning") + + def save(self) -> bool: + settings, error = self._collect_settings() + if error: + self.extra_options_error.setText(error) + self.extra_options_error.show() + self.toast_requested.emit("Settings not saved", error, "critical") + return False + assert settings is not None + try: + saved = self.context.settings_store.save(settings) + except OSError as exc: + self.toast_requested.emit("Settings not saved", str(exc), "critical") + return False + self.context.update_settings(saved) + self.context.clamav.configured_path = saved.clamav_path + self.context.clamav.invalidate() + configure_logging(saved) + self._loading = True + try: + self._update_auto_note(saved.auto_action) + self._refresh_clamav_status() + finally: + self._loading = False + self.extra_options_error.hide() + self._set_dirty(False) + self.settings_saved.emit(saved) + self.toast_requested.emit("Settings saved", "Your changes are active.", "good") + return True + + def _collect_settings(self) -> tuple[AppSettings | None, str | None]: + configured = self.clamav_path.text().strip() + if configured: + found, error = find_clamscan(configured) + if found is None: + return None, error + try: + parse_extra_options(self.extra_options.toPlainText()) + except ValueError as exc: + return None, str(exc) + dirs = [entry.strip() for entry in + self.extra_dirs.text().replace("\n", ";").split(";") if entry.strip()] + settings = dataclasses.replace( + self.context.settings, + update_channel=self.channel_combo.currentData() or "release", + clamav_path=configured, + extra_options=self.extra_options.toPlainText().strip(), + logging_enabled=self.logging_checkbox.isChecked(), + debug_logging=(self.debug_checkbox.isChecked() + and self.logging_checkbox.isChecked()), + send_logs=self.send_logs_checkbox.isChecked(), + auto_action=self.auto_combo.currentData() or "quarantine", + realtime_enabled=self.realtime_checkbox.isChecked(), + notify_on_detection=self.notify_checkbox.isChecked(), + extra_watch_dirs=dirs, + ).normalized() + return settings, None + + def send_logs_now(self) -> None: + if not self.send_logs_checkbox.isChecked(): + QMessageBox.information( + self, "Enable diagnostics first", + "Tick 'Send logs to Bolt's systems for analysis' before sending.") + return + if self._dirty: + answer = QMessageBox.question( + self, "Save settings first?", + "Bolt sends the saved settings and logs. Save your changes before uploading?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.Yes) + if answer == QMessageBox.StandardButton.Yes and not self.save(): + return + if self._diagnostics_worker is not None and self._diagnostics_worker.isRunning(): + return + self.send_now_button.setEnabled(False) + self.send_now_button.setText("Preparing bundle…") + worker = DiagnosticsWorker(self.context, self.context.settings, self) + worker.succeeded.connect(self._diagnostics_succeeded) + worker.failed.connect(self._diagnostics_failed) + worker.finished.connect(self._diagnostics_finished) + worker.finished.connect(worker.deleteLater) + self._diagnostics_worker = worker + worker.start() + + def _diagnostics_succeeded(self, result: dict) -> None: + stamp = time.strftime("%Y-%m-%d %H:%M") + try: + self.context.settings = self.context.settings_store.update( + last_log_upload=stamp) + except Exception: + logger.exception("Could not record the diagnostics upload time") + self.last_upload_label.setText(f"Last sent: {stamp}") + log_id = result.get("id") if isinstance(result, dict) else "" + self.toast_requested.emit( + "Diagnostics sent", f"Bolt received the bundle{f' ({log_id})' if log_id else ''}.", + "good") + + def _diagnostics_failed(self, message: str) -> None: + self.toast_requested.emit("Diagnostics upload failed", message, "critical") + + def _diagnostics_finished(self) -> None: + self._diagnostics_worker = None + self.send_now_button.setText("Send diagnostics now") + self.send_now_button.setEnabled(self.send_logs_checkbox.isChecked()) + + def sign_out(self) -> None: + answer = QMessageBox.warning( + self, "Sign out of BoltClam?", + "This revokes this PC's BoltClam key. Your account and other devices are not affected.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel, + QMessageBox.StandardButton.Cancel) + if answer == QMessageBox.StandardButton.Yes: + self.sign_out_requested.emit() + + def showEvent(self, event) -> None: + self._refresh_clamav_status() + self._refresh_account() + self._refresh_watched() + super().showEvent(event) + + def stop(self) -> None: + stop_thread(self._diagnostics_worker, wait_ms=3500) + self._diagnostics_worker = None diff --git a/src/boltclam/ui/theme.py b/src/boltclam/ui/theme.py new file mode 100644 index 0000000..ab17581 --- /dev/null +++ b/src/boltclam/ui/theme.py @@ -0,0 +1,637 @@ +"""Visual system: colors, fonts, formatting and the application stylesheet.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from pathlib import Path + +from PyQt6.QtGui import QColor, QFont, QFontDatabase +from PyQt6.QtWidgets import QApplication + +from .. import __build_date__, __version__ +from .. import paths + +# Named palette — "abyssal teal with one warm bolt". Deliberately not the +# near-black + acid-green default: depth comes from layered sea-blues and a +# pearl-white text tone, while amber is reserved for actions and warnings. +BG = "#07131A" +BG_DEEP = "#050E14" +SIDEBAR = "#091A22" +SURFACE = "#0C202A" +SURFACE_RAISED = "#102A35" +SURFACE_HOVER = "#14323E" +BORDER = "#1B3946" +BORDER_SOFT = "#16303B" +TEXT = "#EDF6F4" +TEXT_SOFT = "#C6D9D8" +MUTED = "#8BA7B2" +DIM = "#5F7C88" +AMBER = "#F2B84B" +AMBER_BRIGHT = "#FFD37A" +AMBER_DARK = "#A97218" +TEAL = "#59D8C9" +TEAL_DARK = "#1F8F83" +DANGER = "#FF6B5E" +DANGER_DARK = "#B53C33" +WARNING = "#F2B84B" +INFO = "#7AA8FF" +SUCCESS = "#59D8C9" + +FONT_BODY = "Atkinson Hyperlegible" +FONT_DISPLAY = "Bricolage Grotesque" +FONT_MONO = "JetBrains Mono" +FALLBACK_BODY = "Segoe UI" + + +def load_fonts() -> None: + folder = paths.resource_dir() / "fonts" + if folder.is_dir(): + for font_file in sorted(folder.glob("*.ttf")): + QFontDatabase.addApplicationFont(str(font_file)) + + +def body_font(size: float = 10.5, weight: QFont.Weight = QFont.Weight.Normal) -> QFont: + font = QFont(FONT_BODY, 10) + font.setPointSizeF(size) + font.setWeight(weight) + return font + + +def display_font(size: float = 24, weight: QFont.Weight = QFont.Weight.DemiBold) -> QFont: + font = QFont(FONT_DISPLAY, 10) + font.setPointSizeF(size) + font.setWeight(weight) + font.setStyleStrategy(QFont.StyleStrategy.PreferAntialias) + return font + + +def mono_font(size: float = 9.5) -> QFont: + font = QFont(FONT_MONO, 10) + font.setPointSizeF(size) + return font + + +def setup_application(app: QApplication) -> None: + load_fonts() + app.setStyle("Fusion") + app.setFont(body_font(10.5)) + app.setApplicationName("BoltClam") + app.setApplicationDisplayName("BoltClam") + app.setApplicationVersion(__version__) + app.setOrganizationName("The Majestic Network") + app.setStyleSheet(stylesheet()) + + +def _resource_url(name: str) -> str: + path = (paths.resource_dir() / name).resolve() + return path.as_posix() + + +def stylesheet() -> str: + check = _resource_url("check.png") + radio = _resource_url("radio.png") + chevron = _resource_url("chevron_down.png") + return f""" +* {{ + outline: none; +}} +QWidget {{ + color: {TEXT}; + font-family: "{FONT_BODY}", "{FALLBACK_BODY}", sans-serif; + font-size: 14px; +}} +QMainWindow, QWidget#Root, QWidget#Content {{ + background: {BG}; +}} +QFrame#Sidebar {{ + background: {SIDEBAR}; + border-right: 1px solid {BORDER_SOFT}; +}} +QFrame#Card, QFrame#Hero, QFrame#ScanConsole, QFrame#SettingsSection {{ + background: {SURFACE}; + border: 1px solid {BORDER_SOFT}; + border-radius: 14px; +}} +QFrame#Hero {{ + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #0E2733, stop:0.55 #0C202A, stop:1 #0A1B24); + border: 1px solid {BORDER}; + border-radius: 20px; +}} +QFrame#SidebarRule {{ + background: {BORDER_SOFT}; + max-height: 1px; + border: none; +}} +QLabel {{ + background: transparent; +}} +QLabel#PageTitle {{ + font-family: "{FONT_DISPLAY}"; + font-size: 30px; + font-weight: 600; + color: {TEXT}; +}} +QLabel#PageSubtitle {{ + color: {MUTED}; + font-size: 14px; +}} +QLabel#HeroTitle {{ + font-family: "{FONT_DISPLAY}"; + font-size: 27px; + font-weight: 600; + color: {TEXT}; +}} +QLabel#HeroSummary {{ + color: {TEXT_SOFT}; + font-size: 14px; +}} +QLabel#CardTitle {{ + font-family: "{FONT_DISPLAY}"; + font-size: 17px; + font-weight: 600; + color: {TEXT}; +}} +QLabel#CardEyebrow {{ + color: {MUTED}; + font-size: 12px; + font-weight: 700; +}} +QLabel#StatValue {{ + font-family: "{FONT_DISPLAY}"; + font-size: 28px; + font-weight: 600; + color: {TEXT}; +}} +QLabel#StatLabel {{ + color: {MUTED}; + font-size: 12px; +}} +QLabel#Muted {{ + color: {MUTED}; +}} +QLabel#Dim {{ + color: {DIM}; + font-size: 12px; +}} +QLabel#Mono, QLabel#PathLabel {{ + font-family: "{FONT_MONO}"; + font-size: 12px; + color: {TEXT_SOFT}; +}} +QLabel#DangerText {{ color: {DANGER}; }} +QLabel#WarningText {{ color: {WARNING}; }} +QLabel#SuccessText {{ color: {SUCCESS}; }} +QLabel#BrandName {{ + font-family: "{FONT_DISPLAY}"; + font-size: 20px; + font-weight: 700; + color: {TEXT}; +}} +QLabel#BrandTag {{ + color: {MUTED}; + font-size: 10.5px; +}} +QLabel#Pill {{ + border-radius: 9px; + padding: 3px 9px; + font-size: 11px; + font-weight: 700; + background: rgba(89,216,201,0.12); + color: {TEAL}; +}} +QLabel#Pill[severity="warning"] {{ + background: rgba(242,184,75,0.13); + color: {AMBER}; +}} +QLabel#Pill[severity="critical"] {{ + background: rgba(255,107,94,0.14); + color: {DANGER}; +}} +QLabel#Pill[severity="info"] {{ + background: rgba(122,168,255,0.12); + color: {INFO}; +}} +QLabel#Pill[severity="muted"] {{ + background: rgba(139,167,178,0.1); + color: {MUTED}; +}} +QLabel#Pill[severity="good"] {{ + background: rgba(89,216,201,0.12); + color: {TEAL}; +}} +QPushButton {{ + font-family: "{FONT_BODY}"; + font-size: 13.5px; + border-radius: 9px; + padding: 9px 15px; + background: transparent; + color: {TEXT}; +}} +QPushButton#Primary {{ + background: {AMBER}; + color: #1B1304; + font-weight: 700; + border: 1px solid {AMBER_BRIGHT}; +}} +QPushButton#Primary:hover {{ background: {AMBER_BRIGHT}; }} +QPushButton#Primary:pressed {{ background: {AMBER_DARK}; }} +QPushButton#Primary:disabled {{ background: #4A412D; color: #9A8C6D; border-color: #4A412D; }} +QPushButton#Secondary {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + color: {TEXT}; + font-weight: 600; +}} +QPushButton#Secondary:hover {{ background: {SURFACE_HOVER}; border-color: #2A5060; }} +QPushButton#Ghost {{ + color: {MUTED}; + border: 1px solid transparent; + padding: 8px 10px; +}} +QPushButton#Ghost:hover {{ color: {TEXT}; background: {SURFACE_RAISED}; }} +QPushButton#Danger {{ + background: rgba(255,107,94,0.12); + border: 1px solid rgba(255,107,94,0.35); + color: #FFB3AC; + font-weight: 600; +}} +QPushButton#Danger:hover {{ background: rgba(255,107,94,0.2); }} +QPushButton#Secondary:disabled {{ + color: {DIM}; + background: #0A1A22; + border-color: {BORDER_SOFT}; +}} +QPushButton#Danger:disabled {{ + color: #6E4A47; + background: rgba(255,107,94,0.04); + border-color: rgba(255,107,94,0.14); +}} +QPushButton#Ghost:disabled, QPushButton#Link:disabled {{ + color: #40555E; +}} +QPushButton#Link {{ + color: {TEAL}; + font-weight: 700; + padding: 2px 0; + border: none; + text-align: left; +}} +QPushButton#Link:hover {{ color: #8CF5E8; }} +QPushButton#IconButton {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + border-radius: 8px; + padding: 6px; + min-width: 30px; + min-height: 30px; +}} +QPushButton#IconButton:hover {{ background: {SURFACE_HOVER}; }} +QPushButton:focus {{ + border: 1px solid {AMBER}; +}} +QLineEdit, QPlainTextEdit, QTextEdit, QComboBox {{ + background: {BG_DEEP}; + border: 1px solid {BORDER}; + border-radius: 9px; + padding: 9px 11px; + color: {TEXT}; + selection-background-color: {AMBER}; + selection-color: #1B1304; +}} +QLineEdit:hover, QPlainTextEdit:hover, QComboBox:hover {{ border-color: #285163; }} +QLineEdit:focus, QPlainTextEdit:focus, QComboBox:focus {{ border-color: {AMBER}; }} +QLineEdit:disabled, QPlainTextEdit:disabled, QComboBox:disabled {{ + color: {DIM}; background: #08151C; +}} +QPlainTextEdit#MonoInput {{ + font-family: "{FONT_MONO}"; + font-size: 12px; +}} +QComboBox::drop-down {{ + border: none; + width: 30px; +}} +QComboBox::down-arrow {{ + image: url("{chevron}"); + width: 12px; + height: 12px; +}} +QComboBox QAbstractItemView {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + selection-background-color: {SURFACE_HOVER}; + selection-color: {TEXT}; + padding: 4px; +}} +QCheckBox, QRadioButton {{ + color: {TEXT_SOFT}; + spacing: 9px; + background: transparent; +}} +QCheckBox::indicator {{ + width: 18px; + height: 18px; + border-radius: 5px; + border: 1px solid #2A4B5A; + background: {BG_DEEP}; +}} +QCheckBox::indicator:hover {{ border-color: {AMBER}; }} +QCheckBox::indicator:checked {{ + background: {AMBER}; + border-color: {AMBER_BRIGHT}; + image: url("{check}"); +}} +QRadioButton::indicator {{ + width: 17px; + height: 17px; + border-radius: 9px; + border: 1px solid #2A4B5A; + background: {BG_DEEP}; +}} +QRadioButton::indicator:checked {{ + background: {AMBER}; + border-color: {AMBER_BRIGHT}; + image: url("{radio}"); +}} +QRadioButton#ChoiceTile {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + border-radius: 11px; + padding: 12px; +}} +QRadioButton#ChoiceTile:hover {{ border-color: #2D5668; }} +QRadioButton#ChoiceTile:checked {{ + border-color: {AMBER}; + background: rgba(242,184,75,0.07); +}} +QFrame#SegmentChip {{ + background: rgba(8,22,30,0.55); + border: 1px solid {BORDER_SOFT}; + border-radius: 9px; +}} +QFrame#ConcernRow {{ + border-bottom: 1px solid {BORDER_SOFT}; +}} +QFrame#ConcernRow:hover {{ + background: rgba(255,255,255,0.02); +}} +QTabWidget {{ + background: transparent; +}} +QTabWidget::pane {{ + border: none; + background: transparent; +}} +QTabWidget::tab-bar {{ + background: transparent; +}} +QTabBar {{ + background: transparent; + qproperty-drawBase: 0; +}} +QTabBar::tab {{ + background: transparent; + color: {MUTED}; + padding: 8px 14px; + margin-right: 4px; + border: 1px solid transparent; + border-radius: 8px; + font-weight: 600; +}} +QTabBar::tab:selected {{ + background-color: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + color: {TEXT}; +}} +QTabBar::tab:hover {{ color: {TEXT}; }} +QPushButton#TabPill {{ + background: transparent; + border: 1px solid transparent; + color: {MUTED}; + padding: 7px 15px; + border-radius: 8px; + font-weight: 600; +}} +QPushButton#TabPill:hover {{ color: {TEXT}; }} +QPushButton#TabPill:checked {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + color: {TEXT}; +}} +QTableWidget, QTableView {{ + background: transparent; + alternate-background-color: transparent; + border: none; + gridline-color: transparent; + selection-background-color: rgba(242,184,75,0.13); + selection-color: {TEXT}; +}} +QTableWidget::item {{ padding: 8px 6px; border-bottom: 1px solid {BORDER_SOFT}; }} +QHeaderView {{ + background: transparent; + border: none; +}} +QHeaderView::section {{ + background-color: {SURFACE}; + color: {MUTED}; + border: none; + border-bottom: 1px solid {BORDER}; + padding: 7px 6px; + font-size: 11px; + font-weight: 700; +}} +QTableWidget QHeaderView::section {{ + background-color: {SURFACE}; +}} +QTableCornerButton::section {{ background: {SURFACE}; border: none; }} +QListWidget {{ + background: transparent; + border: none; + outline: none; +}} +QListWidget::item {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER_SOFT}; + border-radius: 10px; + margin: 0 0 7px 0; + padding: 10px; +}} +QListWidget::item:selected {{ + border-color: {AMBER}; + background: rgba(242,184,75,0.08); + color: {TEXT}; +}} +QListWidget::item:hover {{ border-color: #2A5060; }} +QScrollArea {{ + border: none; + background: transparent; +}} +QScrollBar:vertical {{ + background: transparent; + width: 10px; + margin: 2px; +}} +QScrollBar::handle:vertical {{ + background: #23414E; + min-height: 30px; + border-radius: 4px; +}} +QScrollBar::handle:vertical:hover {{ background: #2F5667; }} +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{ height: 0; }} +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {{ background: transparent; }} +QScrollBar:horizontal {{ + background: transparent; + height: 10px; +}} +QScrollBar::handle:horizontal {{ background: #23414E; border-radius: 4px; }} +QProgressBar {{ + background: {BG_DEEP}; + border: 1px solid {BORDER}; + border-radius: 4px; + height: 7px; + text-align: center; + color: transparent; +}} +QProgressBar::chunk {{ + background: {AMBER}; + border-radius: 3px; +}} +QSplitter::handle {{ background: transparent; width: 10px; }} +QToolTip {{ + background: {SURFACE_RAISED}; + color: {TEXT}; + border: 1px solid {BORDER}; + padding: 6px 8px; + border-radius: 6px; +}} +QMenu {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + border-radius: 8px; + padding: 5px; +}} +QMenu::item {{ padding: 7px 22px 7px 12px; border-radius: 5px; }} +QMenu::item:selected {{ background: {SURFACE_HOVER}; }} +QTextBrowser {{ + background: transparent; + border: none; + color: {TEXT_SOFT}; + selection-background-color: {AMBER}; + selection-color: #1B1304; +}} +QDialog {{ background: {BG}; }} +QFrame#LoginBrand {{ + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 #0D2B38, stop:0.6 #08202B, stop:1 #07131A); + border: 1px solid {BORDER}; + border-radius: 18px; +}} +QFrame#LoginForm {{ + background: {SURFACE}; + border: 1px solid {BORDER}; + border-radius: 18px; +}} +QFrame#Toast {{ + background: {SURFACE_RAISED}; + border: 1px solid {BORDER}; + border-radius: 11px; +}} +QLabel#ToastTitle {{ font-weight: 700; color: {TEXT}; }} +QLabel#ToastBody {{ color: {MUTED}; font-size: 12.5px; }} +QFrame#Divider {{ background: {BORDER_SOFT}; max-height: 1px; border: none; }} +""" + + +def overall_color(overall: str) -> str: + return { + "protected": TEAL, + "attention": AMBER, + "at_risk": DANGER, + "setup": INFO, + }.get(overall, MUTED) + + +def severity_color(severity: str) -> str: + return { + "critical": DANGER, + "warning": AMBER, + "info": INFO, + "good": TEAL, + "warn": AMBER, + "bad": DANGER, + "unknown": DIM, + }.get(str(severity or "").lower(), MUTED) + + +def status_color(status: str) -> str: + return { + "completed": TEAL, + "running": AMBER, + "cancelled": DIM, + "failed": DANGER, + "completed_with_errors": AMBER, + "quarantined": TEAL, + "deleted": TEAL, + "restored": INFO, + "detected": DANGER, + }.get(str(status or "").lower(), MUTED) + + +def parse_iso(value: str | None) -> datetime | None: + if not value: + return None + try: + parsed = datetime.fromisoformat(str(value).replace("Z", "+00:00")) + except ValueError: + return None + if parsed.tzinfo is None: + parsed = parsed.replace(tzinfo=timezone.utc) + return parsed + + +def relative_time(value: str | None) -> str: + parsed = parse_iso(value) + if parsed is None: + return "Never" + delta = (datetime.now(timezone.utc) - parsed).total_seconds() + if delta < 45: + return "just now" + if delta < 3600: + return f"{int(delta // 60)}m ago" + if delta < 86400: + return f"{int(delta // 3600)}h ago" + if delta < 86400 * 7: + return f"{int(delta // 86400)}d ago" + return parsed.astimezone().strftime("%d %b %Y") + + +def format_datetime(value: str | None) -> str: + parsed = parse_iso(value) + if parsed is None: + return "—" + return parsed.astimezone().strftime("%d %b %Y · %H:%M") + + +def format_size(size: int | float | None) -> str: + value = float(size or 0) + for unit in ("B", "KB", "MB", "GB", "TB"): + if value < 1024 or unit == "TB": + return f"{value:.0f} {unit}" if unit == "B" else f"{value:.1f} {unit}" + value /= 1024 + return f"{value:.1f} TB" + + +def format_duration(seconds: float | int | None) -> str: + total = int(float(seconds or 0)) + if total < 60: + return f"{total}s" + minutes, sec = divmod(total, 60) + if minutes < 60: + return f"{minutes}m {sec}s" + hours, minutes = divmod(minutes, 60) + return f"{hours}h {minutes}m" + + +def version_line() -> str: + return f"Version {__version__} · build {__build_date__}" diff --git a/src/boltclam/ui/threads.py b/src/boltclam/ui/threads.py new file mode 100644 index 0000000..7779fa6 --- /dev/null +++ b/src/boltclam/ui/threads.py @@ -0,0 +1,26 @@ +"""Bounded QThread shutdown for window close.""" + +from __future__ import annotations + +from PyQt6.QtCore import QThread + + +def stop_thread(worker: QThread | None, wait_ms: int = 4000) -> None: + """Let a worker finish, then terminate it as a last resort. + + QThread must never be destroyed while still running; Qt aborts the + process. Network and PowerShell calls can outlive a window close, so + request interruption, wait, and only then terminate. This runs during + application shutdown, never on a normal user action. + """ + if worker is None: + return + try: + if worker.isRunning(): + worker.requestInterruption() + if not worker.wait(max(250, int(wait_ms))): + worker.terminate() + worker.wait(1000) + except RuntimeError: + # The C++ object was already deleted by deleteLater(). + pass diff --git a/src/boltclam/updates.py b/src/boltclam/updates.py new file mode 100644 index 0000000..2b9180a --- /dev/null +++ b/src/boltclam/updates.py @@ -0,0 +1,171 @@ +"""Release checks, version comparison, asset selection and checksum verification.""" + +from __future__ import annotations + +import logging +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Callable + +from . import __version__, paths +from .api_client import ApiClient +from .models import UpdateAsset, UpdateFeed, UpdateRelease + +logger = logging.getLogger(__name__) + +_VERSION_RE = re.compile(r"^[vV]?(\d+(?:\.\d+)*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$") + + +class UpdateError(Exception): + pass + + +def parse_version(value: str) -> tuple[tuple[int, ...], tuple] | None: + match = _VERSION_RE.match(str(value or "").strip()) + if not match: + return None + numbers = tuple(int(part) for part in match.group(1).split(".")) + prerelease_raw = match.group(2) + if not prerelease_raw: + prerelease: tuple = () + else: + parts: list[tuple[int, object]] = [] + for token in prerelease_raw.split("."): + if token.isdigit(): + parts.append((0, int(token))) + else: + parts.append((1, token)) + prerelease = tuple(parts) + return numbers, prerelease + + +def _compare_numbers(left: tuple[int, ...], right: tuple[int, ...]) -> int: + length = max(len(left), len(right)) + left_padded = left + (0,) * (length - len(left)) + right_padded = right + (0,) * (length - len(right)) + return (left_padded > right_padded) - (left_padded < right_padded) + + +def is_newer(candidate: str, current: str = __version__) -> bool: + parsed_candidate = parse_version(candidate) + if parsed_candidate is None: + return False + parsed_current = parse_version(current) + if parsed_current is None: + return True + candidate_numbers, candidate_pre = parsed_candidate + current_numbers, current_pre = parsed_current + numbers = _compare_numbers(candidate_numbers, current_numbers) + if numbers != 0: + return numbers > 0 + # Same numeric version: a stable release outranks its prereleases. + if not candidate_pre and current_pre: + return True + if candidate_pre and not current_pre: + return False + return candidate_pre > current_pre + + +def choose_asset(release: UpdateRelease) -> UpdateAsset | None: + assets = [asset for asset in release.assets if asset.download_url] + if not assets: + return None + + def score(asset: UpdateAsset) -> tuple: + name = asset.name.lower() + executable = name.endswith(".exe") + setup = "setup" in name or "installer" in name + windows = "windows" in name or "win" in name + archive = name.endswith(".zip") + return ( + 0 if (executable and setup) else + 1 if (executable and windows) else + 2 if executable else + 3 if (archive and windows) else + 4 if archive else 9, + len(name), + ) + + usable = [asset for asset in assets + if asset.name.lower().endswith((".exe", ".zip", ".msi"))] + return min(usable, key=score) if usable else None + + +def checksum_asset(release: UpdateRelease) -> UpdateAsset | None: + wanted = {"sha256sums.txt", "sha256sum.txt", "checksums.txt", + "sha256sums", "checksums.sha256"} + for asset in release.assets: + if asset.name.lower() in wanted: + return asset + return None + + +def parse_checksums(text: str) -> dict[str, str]: + result: dict[str, str] = {} + for line in str(text or "").splitlines(): + match = re.match(r"^\s*([0-9a-fA-F]{64})\s+\*?(.+?)\s*$", line) + if match: + result[Path(match.group(2)).name] = match.group(1).lower() + return result + + +def _safe_name(name: str) -> str: + base = Path(str(name or "download")).name + base = re.sub(r"[^A-Za-z0-9._() +-]", "_", base)[:180] + return base or "download" + + +@dataclass +class DownloadedUpdate: + path: Path + sha256: str + verified: bool + expected_sha256: str = "" + + +class UpdateService: + def __init__(self, api: ApiClient, channel: str = "release") -> None: + self.api = api + self.channel = "beta" if channel == "beta" else "release" + + def check(self, force: bool = False) -> UpdateFeed: + payload = self.api.get_updates(self.channel, force=force) + return UpdateFeed.from_api(payload) + + def update_available(self, feed: UpdateFeed) -> bool: + return bool(feed.latest and is_newer(feed.latest.tag, __version__)) + + def download(self, release: UpdateRelease, asset: UpdateAsset, + progress: Callable[[int, int], None] | None = None) -> DownloadedUpdate: + target_dir = paths.updates_dir() / _safe_name(release.tag or release.id or "release") + target = target_dir / _safe_name(asset.name) + try: + path, digest = self.api.download_asset(asset.download_url, target, progress) + except Exception as exc: + raise UpdateError(f"The update could not be downloaded: {exc}") from exc + + expected = "" + checksum_file = checksum_asset(release) + if checksum_file is not None: + temp_checksum = target_dir / "SHA256SUMS.txt" + try: + checksum_path, _hash = self.api.download_asset( + checksum_file.download_url, temp_checksum) + text = checksum_path.read_text(encoding="utf-8", errors="replace") + expected = parse_checksums(text).get(asset.name, "") + except Exception as exc: + logger.warning("Could not download the checksum file: %s", exc) + finally: + try: + temp_checksum.unlink() + except OSError: + pass + if expected and digest.lower() != expected.lower(): + try: + path.unlink() + except OSError: + pass + raise UpdateError("The downloaded update did not match its SHA-256 checksum.") + return DownloadedUpdate(path=path, sha256=digest, verified=bool(expected), + expected_sha256=expected) diff --git a/src/boltclam/winlock.py b/src/boltclam/winlock.py new file mode 100644 index 0000000..596ecb0 --- /dev/null +++ b/src/boltclam/winlock.py @@ -0,0 +1,197 @@ +"""Best-effort file locking for the real-time download gate. + +Windows has no user-mode API that lets an ordinary process intercept every +``CreateFile`` (that is a kernel minifilter). What this module *can* do is +hold the download file open with ``dwShareMode = 0`` while ClamAV scans a +copy, which makes Windows refuse other opens for that period. If the file +was already open elsewhere, we fall back to scanning a copy and say so in +the event detail. This is a meaningful speed bump, not a guarantee against +a kernel-level bypass. +""" + +from __future__ import annotations + +import ctypes +import logging +import os +import shutil +from ctypes import wintypes +from pathlib import Path + +logger = logging.getLogger(__name__) + + +class LockError(Exception): + pass + + +if os.name == "nt": + _kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + _CreateFileW = _kernel32.CreateFileW + _CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD, + wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, + wintypes.HANDLE] + _CreateFileW.restype = wintypes.HANDLE + _ReadFile = _kernel32.ReadFile + _ReadFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD, + ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID] + _ReadFile.restype = wintypes.BOOL + _CloseHandle = _kernel32.CloseHandle + _CloseHandle.argtypes = [wintypes.HANDLE] + _CloseHandle.restype = wintypes.BOOL + _SetFileInformationByHandle = _kernel32.SetFileInformationByHandle + _SetFileInformationByHandle.argtypes = [wintypes.HANDLE, ctypes.c_int, + wintypes.LPVOID, wintypes.DWORD] + _SetFileInformationByHandle.restype = wintypes.BOOL + + _GENERIC_READ = 0x80000000 + _DELETE = 0x00010000 + _OPEN_EXISTING = 3 + _FILE_ATTRIBUTE_NORMAL = 0x80 + _FILE_DISPOSITION_INFO_CLASS = 4 + _INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value + + class _FILE_DISPOSITION_INFO(ctypes.Structure): + _fields_ = [("DeleteFile", wintypes.BOOL)] +else: + _kernel32 = None + _FILE_DISPOSITION_INFO = None + + +class ExclusiveFile: + """Open a file (share-deny on Windows) and copy it while held.""" + + def __init__(self, path: Path) -> None: + self.path = Path(path) + self._handle = None + self._file = None + self._delete_access = False + self._delete_on_close = False + self._open = False + + @property + def exclusive(self) -> bool: + return bool(self._open and os.name == "nt") + + def open(self) -> None: + if self._open: + return + if os.name == "nt": + handle = _CreateFileW( + str(self.path), _GENERIC_READ | _DELETE, 0, None, _OPEN_EXISTING, + _FILE_ATTRIBUTE_NORMAL, None, + ) + if handle == _INVALID_HANDLE_VALUE: + handle = _CreateFileW( + str(self.path), _GENERIC_READ, 0, None, _OPEN_EXISTING, + _FILE_ATTRIBUTE_NORMAL, None, + ) + self._delete_access = False + else: + self._delete_access = True + if handle == _INVALID_HANDLE_VALUE: + error = ctypes.get_last_error() + raise LockError(f"Could not open {self.path.name} exclusively (error {error}).") + self._handle = handle + else: + try: + self._file = open(self.path, "rb") + except OSError as exc: + raise LockError(f"Could not open {self.path.name}: {exc}") from exc + self._open = True + + def copy_to(self, destination: Path) -> None: + if not self._open: + self.open() + destination = Path(destination) + destination.parent.mkdir(parents=True, exist_ok=True) + try: + if os.name == "nt": + buffer = ctypes.create_string_buffer(1024 * 1024) + read = wintypes.DWORD() + with open(destination, "wb") as output: + while True: + ok = _ReadFile(self._handle, buffer, len(buffer), + ctypes.byref(read), None) + if not ok: + raise LockError( + f"Could not read {self.path.name} (error " + f"{ctypes.get_last_error()}).") + if read.value == 0: + break + output.write(buffer.raw[:read.value]) + else: + with open(destination, "wb") as output: + shutil.copyfileobj(self._file, output, length=1024 * 1024) + except OSError as exc: + raise LockError(f"Could not copy {self.path.name}: {exc}") from exc + + def mark_delete_on_close(self) -> bool: + if os.name != "nt" or not self._delete_access or self._handle is None: + return False + info = _FILE_DISPOSITION_INFO(True) + ok = _SetFileInformationByHandle( + self._handle, _FILE_DISPOSITION_INFO_CLASS, ctypes.byref(info), + ctypes.sizeof(info), + ) + if not ok: + logger.warning("Could not mark %s delete-on-close (error %s)", + self.path, ctypes.get_last_error()) + return False + self._delete_on_close = True + return True + + def close(self) -> None: + if not self._open: + return + if os.name == "nt": + if self._handle is not None: + _CloseHandle(self._handle) + self._handle = None + else: + if self._file is not None: + self._file.close() + self._file = None + self._open = False + + def __enter__(self) -> "ExclusiveFile": + self.open() + return self + + def __exit__(self, exc_type, exc, traceback) -> None: + self.close() + + +def copy_for_scan(path: Path, destination: Path, attempts: int = 8, + delay: float = 0.4) -> tuple[ExclusiveFile, bool]: + """Copy *path* while holding it where possible. + + Returns ``(locker, exclusive)``. The caller must keep ``locker`` open + until the scan finishes, then close it. If the file cannot be opened + exclusively (another process is still writing or reading it), this + retries briefly and then copies without the lock. + """ + import time + + last_error: Exception | None = None + for _ in range(max(1, attempts)): + locker = ExclusiveFile(path) + try: + locker.open() + locker.copy_to(destination) + return locker, locker.exclusive + except LockError as exc: + last_error = exc + try: + locker.close() + except Exception: + pass + time.sleep(delay) + # Last resort: a plain copy. The watcher already waited for the size to + # stop changing, so this is still normally a complete file. + try: + shutil.copy2(path, destination) + except OSError as exc: + raise LockError(f"Could not copy {path.name}: {exc}") from exc + logger.warning("Real-time scan could not exclusively lock %s: %s", path, last_error) + return ExclusiveFile(path), False diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..2c580f0 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,32 @@ +"""Test bootstrap: never touch the real BoltClam profile or Downloads.""" + +from __future__ import annotations + +import os +import sys +from pathlib import Path + +os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / "src")) + +import pytest # noqa: E402 + +from boltclam import paths # noqa: E402 + + +@pytest.fixture +def isolated_env(tmp_path: Path, monkeypatch) -> Path: + monkeypatch.setenv("BOLTCLAM_CONFIG_DIR", str(tmp_path / "config")) + monkeypatch.setenv("BOLTCLAM_DATA_DIR", str(tmp_path / "data")) + paths.ensure_dirs() + return tmp_path + + +@pytest.fixture(scope="session") +def qapp(): + from PyQt6.QtWidgets import QApplication + + app = QApplication.instance() or QApplication([]) + return app diff --git a/tests/test_auth.py b/tests/test_auth.py new file mode 100644 index 0000000..dd0b8cb --- /dev/null +++ b/tests/test_auth.py @@ -0,0 +1,198 @@ +from __future__ import annotations + +import base64 +import hashlib +import http.server +import json +import socket +import threading +import time +import urllib.error +import urllib.request +from pathlib import Path +from urllib.parse import parse_qs, urlparse + +import pytest + +from boltclam.auth import ( + AuthError, + AuthService, + CredentialStore, + LoopbackAuthReceiver, + pkce_pair, +) +from boltclam.config import AccountCache + +ACCOUNT = { + "user": {"id": "1", "email": "aaron@example.com", "name": "Aaron", + "username": "aaron"}, + "subscription": {"tier": "premium", "label": "Premium", "status": "active"}, + "balance": {"weekly_remaining": 100, "topup": 0, "allowed": True}, + "features": {"licensed": True}, +} + + +class _Handler(http.server.BaseHTTPRequestHandler): + def _json(self, status: int, payload: dict) -> None: + data = json.dumps(payload).encode() + self.send_response(status) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def do_GET(self) -> None: # noqa: N802 + if self.path.startswith("/boltclam/account"): + key = self.headers.get("X-Desk-Api-Key", "") + if key in ("bolt_live_good", "bolt_live_new"): + self._json(200, ACCOUNT) + else: + self._json(401, {"error": "invalid_credentials"}) + return + self._json(404, {"error": "not_found"}) + + def do_POST(self) -> None: # noqa: N802 + length = int(self.headers.get("Content-Length") or 0) + raw = self.rfile.read(length) if length else b"{}" + if self.path == "/desk/auth/oauth/exchange": + payload = json.loads(raw or b"{}") + self.server.exchanges.append(payload) # type: ignore[attr-defined] + self._json(200, {"api_key": "bolt_live_new", "user": ACCOUNT["user"]}) + return + if self.path == "/desk/auth/logout": + self._json(200, {}) + return + self._json(404, {"error": "not_found"}) + + def log_message(self, fmt: str, *args) -> None: + return + + +@pytest.fixture +def web_server(): + server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), _Handler) + server.exchanges = [] # type: ignore[attr-defined] + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + try: + yield server + finally: + server.shutdown() + server.server_close() + + +def _auth(tmp_path, base_url) -> AuthService: + return AuthService( + base_url, install_id="test-install", + credentials=CredentialStore(tmp_path / "credentials.dat"), + account_cache=AccountCache(tmp_path / "account.json")) + + +def test_pkce_pair_matches_s256(): + verifier, challenge = pkce_pair() + expected = base64.urlsafe_b64encode( + hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode() + assert challenge == expected + assert 43 <= len(verifier) <= 128 + + +def test_credential_store_fallback_round_trip(tmp_path): + store = CredentialStore(tmp_path / "credentials.dat") + store.save("bolt_live_secret") + assert store.load() == "bolt_live_secret" + store.delete() + assert store.load() is None + + +def test_key_login_restore_and_logout(tmp_path, web_server): + base = f"http://127.0.0.1:{web_server.server_address[1]}" + auth = _auth(tmp_path, base) + session = auth.login_with_key("bolt_live_good") + assert session.api_key == "bolt_live_good" + assert session.account["user"]["email"] == "aaron@example.com" + assert auth.stored_key() == "bolt_live_good" + + restored = auth.restore() + assert restored.account["subscription"]["tier"] == "premium" + assert restored.offline is False + + auth.logout() + assert auth.stored_key() is None + assert auth.account_cache.load() is None + + +def test_key_login_rejects_a_bad_key(tmp_path, web_server): + base = f"http://127.0.0.1:{web_server.server_address[1]}" + auth = _auth(tmp_path, base) + with pytest.raises(AuthError): + auth.login_with_key("bolt_live_wrong") + + +def test_browser_login_completes_the_loopback_pkce_handoff(tmp_path, web_server): + base = f"http://127.0.0.1:{web_server.server_address[1]}" + auth = _auth(tmp_path, base) + opened: dict[str, str] = {} + + def open_url(url: str) -> bool: + opened["url"] = url + parsed = urlparse(url) + query = parse_qs(parsed.query) + redirect_uri = query["redirect_uri"][0] + state = query["state"][0] + + def callback() -> None: + time.sleep(0.2) + with urllib.request.urlopen( + f"{redirect_uri}?code=one-time&state={state}", timeout=5) as response: + response.read() + + threading.Thread(target=callback, daemon=True).start() + return True + + session = auth.browser_login(open_url, timeout=10) + assert session.api_key == "bolt_live_new" + assert auth.stored_key() == "bolt_live_new" + exchange = web_server.exchanges[-1] # type: ignore[attr-defined] + assert exchange["client"] == "boltclam" + assert exchange["code"] == "one-time" + assert exchange["code_verifier"] + assert exchange["redirect_uri"].startswith("http://127.0.0.1:") + assert "device_name=BoltClam" in opened["url"] + + +def test_loopback_rejects_the_wrong_state(): + receiver = LoopbackAuthReceiver("expected-state") + receiver.start() + try: + try: + urllib.request.urlopen( + f"{receiver.redirect_uri}?code=bad&state=wrong", timeout=5).read() + assert False, "expected a 400" + except urllib.error.HTTPError as exc: + assert exc.code == 400 + assert receiver.event.is_set() is False + + with urllib.request.urlopen( + f"{receiver.redirect_uri}?code=good&state=expected-state", + timeout=5) as response: + assert response.status == 200 + result = receiver.wait(2) + assert result["code"] == "good" + finally: + receiver.close() + + +def test_restore_offline_uses_the_cached_account(tmp_path): + with socket.socket() as sock: + sock.bind(("127.0.0.1", 0)) + port = sock.getsockname()[1] + credential_path = tmp_path / "credentials.dat" + CredentialStore(credential_path).save("bolt_live_cached") + cache = AccountCache(tmp_path / "account.json") + cache.save(ACCOUNT) + auth = AuthService( + f"http://127.0.0.1:{port}", install_id="offline", + credentials=CredentialStore(credential_path), account_cache=cache) + session = auth.restore() + assert session.offline is True + assert session.account["user"]["email"] == "aaron@example.com" diff --git a/tests/test_clamav.py b/tests/test_clamav.py new file mode 100644 index 0000000..f248264 --- /dev/null +++ b/tests/test_clamav.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import os +import stat +import time +from pathlib import Path + +import pytest + +from boltclam.clamav import ( + ClamAVService, + build_freshclam_args, + build_scan_args, + database_age, + database_files, + find_clamscan, + parse_clamscan_line, + parse_extra_options, + parse_version_output, + scan_targets, +) + + +def _script(path: Path, body: str = "") -> Path: + path.write_text("#!/bin/sh\n" + body, encoding="utf-8") + path.chmod(path.stat().st_mode | stat.S_IXUSR) + return path + + +def test_parse_version_output(): + assert parse_version_output("ClamAV 1.4.3/27452/Wed Oct 22 07:17:23 2026") == ( + "1.4.3", "27452", "Wed Oct 22 07:17:23 2026") + assert parse_version_output("ClamAV 1.4.0") == ("1.4.0", "", "") + assert parse_version_output("nonsense") == ("", "", "") + + +def test_parse_extra_options_handles_windows_quotes(): + options = parse_extra_options( + '--max-filesize=200M --exclude-dir="C:\\Program Files\\Skip" ' + '--database="C:\\My DB"') + assert options == [ + "--max-filesize=200M", + "--exclude-dir=C:\\Program Files\\Skip", + "--database=C:\\My DB", + ] + + +def test_parse_extra_options_refuses_destructive_flags(): + for text in ("--remove=yes", "--move=C:\\Quarantine", "--copy=/tmp"): + with pytest.raises(ValueError): + parse_extra_options(text) + + +def test_parse_extra_options_rejects_malformed_quoting(): + with pytest.raises(ValueError): + parse_extra_options('--exclude-dir="unterminated') + + +def test_build_scan_args_uses_managed_database_and_excludes(tmp_path): + db = tmp_path / "db" + db.mkdir() + (db / "main.cvd").write_bytes(b"x") + quarantine = tmp_path / "quarantine" + temp = tmp_path / "temp" + args = build_scan_args( + Path("/usr/bin/clamscan"), [tmp_path / "target"], + database_dir=db, quarantine_dir=quarantine, temp_dir=temp, + extra_options=["--max-filesize=200M"]) + assert args[0].endswith("clamscan") + assert "--recursive=yes" in args + assert "--infected" in args + assert f"--database={db}" in args + assert any(arg.startswith("--exclude-dir=") for arg in args) + assert "--max-filesize=200M" in args + assert args[-1] == str(tmp_path / "target") + + +def test_build_scan_args_omits_database_when_no_signatures(tmp_path): + args = build_scan_args(Path("/usr/bin/clamscan"), [tmp_path]) + assert not any(arg.startswith("--database=") for arg in args) + + +def test_parse_clamscan_line_detection_windows_path(): + event = parse_clamscan_line( + r"C:\Users\aaron\Downloads\invoice.pdf.exe: Win.Trojan.Agent-123 FOUND") + assert event == { + "kind": "detection", + "path": r"C:\Users\aaron\Downloads\invoice.pdf.exe", + "signature": "Win.Trojan.Agent-123", + } + + +def test_parse_clamscan_line_summary_progress_and_errors(): + assert parse_clamscan_line("Scanned files: 18234")["key"] == "scanned_files" + assert parse_clamscan_line("Scanned files: 18234")["value"] == "18234" + assert parse_clamscan_line("Engine version: 1.4.3")["value"] == "1.4.3" + assert parse_clamscan_line("Scanning /home/a/file")["kind"] == "progress" + assert parse_clamscan_line("/home/a/file: Access denied")["kind"] == "error" + assert parse_clamscan_line("/home/a/file: Size limit exceeded")["kind"] == "skipped" + assert parse_clamscan_line("") is None + + +def test_scan_targets_deduplicates_injected_profiles(tmp_path): + one = tmp_path / "one" + two = tmp_path / "two" + one.mkdir() + two.mkdir() + targets = scan_targets("homes", profiles=[one, two, one, Path("/does/not/exist")]) + assert targets == [one.resolve(), two.resolve()] + + +def test_scan_targets_full_uses_injected_drives(tmp_path): + drive = tmp_path / "drive" + drive.mkdir() + assert scan_targets("full", drives=[drive]) == [drive.resolve()] + + +def test_database_detection_and_age(tmp_path): + db = tmp_path / "db" + db.mkdir() + assert database_files(db) == [] + main = db / "main.cvd" + main.write_bytes(b"signatures") + old = time.time() - 3 * 86400 + os.utime(main, (old, old)) + assert database_files(db) == [main] + date, age = database_age(db) + assert date + assert 2.8 < age < 3.2 + + +def test_clamav_service_reads_a_fake_engine(tmp_path): + fake = _script(tmp_path / "clamscan", 'echo "ClamAV 1.4.3/27452/Wed Oct 22 07:17:23 2026"\n') + service = ClamAVService(str(fake)) + info = service.info(force=True) + assert info.available is True + assert info.engine_version == "1.4.3" + assert info.db_version == "27452" + assert info.path == fake + assert service.info(force=True).summary.startswith("1.4.3") + + +def test_find_clamscan_accepts_a_file_or_directory(tmp_path): + fake = _script(tmp_path / "clamscan") + found, error = find_clamscan(str(fake)) + assert found == fake and error == "" + found, error = find_clamscan(str(tmp_path)) + assert found == fake and error == "" + missing, error = find_clamscan(str(tmp_path / "missing")) + assert missing is None and "does not exist" in error + + +def test_freshclam_arguments_target_managed_database(tmp_path): + args = build_freshclam_args(Path("/usr/bin/freshclam"), tmp_path / "db") + assert "--foreground" in args + assert "--stdout" in args + assert f"--datadir={tmp_path / 'db'}" in args diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..3fb4224 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import json + +from boltclam.config import AccountCache, AppSettings, SettingsStore + + +def test_defaults_are_normalized_and_install_id_is_generated(isolated_env): + settings = SettingsStore().load() + assert settings.update_channel == "release" + assert settings.scan_scope == "homes" + assert settings.auto_action == "quarantine" + assert settings.install_id + assert len(settings.install_id) == 32 + + +def test_invalid_values_fall_back_to_defaults(isolated_env): + settings = AppSettings( + update_channel="nightly", + scan_scope="somewhere", + auto_action="explode", + debug_logging=True, + logging_enabled=False, + api_base_url="", + ).normalized() + assert settings.update_channel == "release" + assert settings.scan_scope == "homes" + assert settings.auto_action == "quarantine" + assert settings.debug_logging is False + assert settings.api_base_url == "https://api.themajesticnetwork.com" + assert settings.install_id + + +def test_round_trip_and_atomic_save(isolated_env): + store = SettingsStore() + settings = store.load() + saved = store.update(update_channel="beta", extra_options="--max-filesize=200M", + extra_watch_dirs=["~/Downloads/Test", ""]) + reloaded = SettingsStore().load() + assert reloaded.update_channel == "beta" + assert reloaded.extra_options == "--max-filesize=200M" + assert len(reloaded.extra_watch_dirs) == 1 + assert saved.install_id == reloaded.install_id + assert json.loads(store.path.read_text())["update_channel"] == "beta" + + +def test_corrupt_settings_are_backed_up_and_defaults_load(isolated_env): + store = SettingsStore() + store.path.parent.mkdir(parents=True, exist_ok=True) + store.path.write_text("{not json", encoding="utf-8") + loaded = SettingsStore().load() + assert loaded.update_channel == "release" + backups = list(store.path.parent.glob("settings.corrupt-*.json")) + assert len(backups) == 1 + + +def test_public_snapshot_does_not_expose_install_id(isolated_env): + settings = SettingsStore().load() + public = settings.public_snapshot() + assert "install_id" not in public + assert public["update_channel"] == "release" + + +def test_account_cache_round_trip_and_clear(isolated_env): + cache = AccountCache() + account = {"user": {"email": "a@example.com"}, "subscription": {"tier": "premium"}} + cache.save(account) + assert cache.load() == account + cache.clear() + assert cache.load() is None diff --git a/tests/test_logs.py b/tests/test_logs.py new file mode 100644 index 0000000..597d86d --- /dev/null +++ b/tests/test_logs.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import json +import zipfile +from pathlib import Path + +from boltclam import paths +from boltclam.config import SettingsStore +from boltclam.logging_setup import redact +from boltclam.logs import build_bundle, redact_paths, sanitize_text, upload_bundle +from boltclam.models import ClamAVInfo, Detection +from boltclam.storage import Storage +from boltclam.system_info import collect + + +def test_redaction_covers_tokens_and_emails(): + text = "key=bolt_live_abcdef password=hunter2 user aaron@example.com" + redacted = sanitize_text(text) + assert "bolt_live_abcdef" not in redacted + assert "hunter2" not in redacted + assert "aaron@example.com" not in redacted + assert "[email]" in redacted or "[redacted]" in redacted + + +def test_path_redaction_uses_userprofile(isolated_env): + sample = str(Path.home() / "Downloads" / "bad.exe") + assert "%USERPROFILE%" in redact_paths(sample) + assert "Downloads" in redact_paths(sample) + + +def test_bundle_contains_redacted_logs_and_scan_summary(isolated_env): + settings = SettingsStore().load() + storage = Storage() + scan_id = storage.create_scan("homes", [str(Path.home() / "Downloads")]) + detection = Detection(path=str(Path.home() / "Downloads" / "bad.exe"), + signature="Win.Test", size=10) + detection.id = storage.add_detection(scan_id, detection) + storage.finish_scan(scan_id, status="completed", scanned_files=10, + infected_files=1, errors=0, duration_seconds=2, cancelled=False) + paths.logs_dir().mkdir(parents=True, exist_ok=True) + (paths.logs_dir() / "boltclam.log").write_text( + f"token=bolt_live_supersecret user aaron@example.com path={Path.home()}/x\n", + encoding="utf-8") + info = ClamAVInfo(available=True, path=Path("/fake/clamscan"), + engine_version="1.4.3", db_version="27452", + db_path=Path("/fake/db"), db_date="2026-10-22", + db_age_days=1.0) + snapshot = collect(settings, info, storage, realtime_running=False, force=True) + bundle = build_bundle(settings, info, system_snapshot=snapshot, + account={"user": {"email": "aaron@example.com"}}, + realtime_roots=[str(Path.home() / "Downloads")]) + assert bundle.is_file() + with zipfile.ZipFile(bundle) as archive: + names = set(archive.namelist()) + assert {"manifest.json", "boltclam-logs.txt", "system-summary.json", + "README.txt"} <= names + logs = archive.read("boltclam-logs.txt").decode() + assert "bolt_live_supersecret" not in logs + assert "aaron@example.com" not in logs + assert "%USERPROFILE%" in logs + manifest = json.loads(archive.read("manifest.json")) + assert manifest["app"] == "BoltClam" + assert manifest["account"]["email"] == "aaron@example.com" + summary = json.loads(archive.read("system-summary.json")) + assert summary["stats"]["latest_scan"]["id"] == scan_id + + +class FakeApi: + def upload_diagnostics(self, path, metadata=None): + return {"id": "42/test.zip", "bytes": Path(path).stat().st_size} + + +def test_upload_bundle_returns_server_id(isolated_env): + settings = SettingsStore().load() + storage = Storage() + info = ClamAVInfo(available=False, error="not installed") + bundle = build_bundle(settings, info) + result = upload_bundle(FakeApi(), bundle, settings) + assert result["id"] == "42/test.zip" diff --git a/tests/test_quarantine.py b/tests/test_quarantine.py new file mode 100644 index 0000000..87a5bb4 --- /dev/null +++ b/tests/test_quarantine.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +import json +import os +import stat +from pathlib import Path + +import pytest + +from boltclam.quarantine import QuarantineError, QuarantineService, delete_file, hash_file +from boltclam.storage import Storage + + +def _service(tmp_path) -> QuarantineService: + storage = Storage(tmp_path / "history.db") + return QuarantineService(storage, tmp_path / "quarantine") + + +def test_quarantine_moves_records_and_writes_metadata(tmp_path): + service = _service(tmp_path) + source = tmp_path / "malware.exe" + source.write_bytes(b"malware bytes") + digest = hash_file(source) + item = service.quarantine_file(source, "Win.Test", scan_id=7, reason="Unit test") + + assert not source.exists() + destination = Path(item.quarantine_path) + assert destination.is_file() + assert destination.name.endswith(".quarantine") + assert item.sha256 == digest + assert service.storage.get_quarantine(item.id).signature == "Win.Test" + metadata = json.loads((service.root / f"{item.id}.json").read_text()) + assert metadata["original_path"] == str(source) + assert metadata["reason"] == "Unit test" + + +def test_restore_to_original_or_chosen_path(tmp_path): + service = _service(tmp_path) + source = tmp_path / "bad.exe" + source.write_bytes(b"x") + item = service.quarantine_file(source, "Sig") + target = service.restore(item.id) + assert target.read_bytes() == b"x" + assert service.storage.get_quarantine(item.id).status == "restored" + + source.write_bytes(b"y") + item2 = service.quarantine_file(source, "Sig") + chosen = tmp_path / "restored" / "safe.exe" + restored = service.restore(item2.id, chosen) + assert restored == chosen + assert chosen.read_bytes() == b"y" + + +def test_delete_quarantined_item_and_missing_original(tmp_path): + service = _service(tmp_path) + source = tmp_path / "bad.exe" + source.write_bytes(b"x") + item = service.quarantine_file(source, "Sig") + service.delete(item.id) + assert not Path(item.quarantine_path).exists() + assert service.storage.get_quarantine(item.id).status == "deleted" + with pytest.raises(QuarantineError): + service.restore(item.id) + + +def test_refuses_symlink_and_directory(tmp_path): + service = _service(tmp_path) + directory = tmp_path / "folder" + directory.mkdir() + with pytest.raises(QuarantineError): + service.quarantine_file(directory, "Sig") + real = tmp_path / "real.exe" + real.write_bytes(b"x") + link = tmp_path / "link.exe" + try: + link.symlink_to(real) + except OSError: + pytest.skip("symlinks unavailable") + with pytest.raises(QuarantineError): + service.quarantine_file(link, "Sig") + with pytest.raises(QuarantineError): + delete_file(directory) + + +def test_delete_file_clears_readonly_bit(tmp_path): + target = tmp_path / "readonly.exe" + target.write_bytes(b"x") + os.chmod(target, stat.S_IREAD) + delete_file(target) + assert not target.exists() + + +def test_restore_refuses_to_overwrite(tmp_path): + service = _service(tmp_path) + source = tmp_path / "bad.exe" + source.write_bytes(b"x") + item = service.quarantine_file(source, "Sig") + source.write_bytes(b"replacement") + with pytest.raises(QuarantineError): + service.restore(item.id) diff --git a/tests/test_realtime.py b/tests/test_realtime.py new file mode 100644 index 0000000..5060ae3 --- /dev/null +++ b/tests/test_realtime.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import json +from pathlib import Path + +from boltclam import realtime +from boltclam.realtime import browser_download_roots, snapshot +from boltclam.winlock import copy_for_scan + + +def test_snapshot_finds_final_files_and_skips_partials(tmp_path): + root = tmp_path / "Downloads" + root.mkdir() + (root / "old.exe").write_bytes(b"abc") + (root / "new.crdownload").write_bytes(b"partial") + (root / ".hidden").write_bytes(b"hidden") + sub = root / "Sub" + sub.mkdir() + (sub / "inside.zip").write_bytes(b"zip") + + found = snapshot([root]) + names = {Path(value[2]).name for value in found.values()} + assert names == {"old.exe", "inside.zip"} + + +def test_chromium_download_preference_is_watched(tmp_path, monkeypatch): + local = tmp_path / "Local" + roaming = tmp_path / "Roaming" + monkeypatch.setenv("LOCALAPPDATA", str(local)) + monkeypatch.setenv("APPDATA", str(roaming)) + download = tmp_path / "Custom Downloads" + download.mkdir() + profile = local / "Google" / "Chrome" / "User Data" / "Default" + profile.mkdir(parents=True) + (profile / "Preferences").write_text(json.dumps({ + "download": {"default_directory": str(download)}}), encoding="utf-8") + monkeypatch.setattr(realtime.paths, "browser_download_roots", lambda: []) + + roots = browser_download_roots() + assert download.resolve() in [path.resolve() for path in roots] + + +def test_firefox_download_preference_is_watched(tmp_path, monkeypatch): + local = tmp_path / "Local" + roaming = tmp_path / "Roaming" + monkeypatch.setenv("LOCALAPPDATA", str(local)) + monkeypatch.setenv("APPDATA", str(roaming)) + download = tmp_path / "Firefox Downloads" + download.mkdir() + profile = roaming / "Mozilla" / "Firefox" / "Profiles" / "abc.default" + profile.mkdir(parents=True) + escaped = str(download).replace("\\", "\\\\") + (profile / "prefs.js").write_text( + f'user_pref("browser.download.dir", "{escaped}");\n', encoding="utf-8") + monkeypatch.setattr(realtime.paths, "browser_download_roots", lambda: []) + + roots = browser_download_roots() + assert download.resolve() in [path.resolve() for path in roots] + + +def test_copy_for_scan_preserves_bytes(tmp_path): + source = tmp_path / "download.exe" + source.write_bytes(b"sample bytes") + destination = tmp_path / "copy.bin" + locker, exclusive = copy_for_scan(source, destination, attempts=1, delay=0) + try: + assert destination.read_bytes() == b"sample bytes" + assert isinstance(exclusive, bool) + finally: + locker.close() diff --git a/tests/test_storage.py b/tests/test_storage.py new file mode 100644 index 0000000..b577ce9 --- /dev/null +++ b/tests/test_storage.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import json + +from boltclam.models import Detection, QuarantineItem, RealtimeEvent +from boltclam.storage import Storage + + +def _storage(tmp_path) -> Storage: + return Storage(tmp_path / "history.db") + + +def test_scan_and_detection_lifecycle(tmp_path): + storage = _storage(tmp_path) + scan_id = storage.create_scan("homes", [r"C:\Users"], engine_version="1.4.3", + db_version="27452") + assert scan_id == 1 + detection = Detection(path=r"C:\Users\a\bad.exe", signature="Win.Test", size=42) + detection.id = storage.add_detection(scan_id, detection) + storage.finish_scan(scan_id, status="completed", scanned_files=100, + infected_files=1, errors=2, duration_seconds=12.5, + cancelled=False) + + record = storage.get_scan(scan_id) + assert record is not None + assert record.scope == "homes" + assert record.scope_label == "User homes" + assert record.scanned_files == 100 + assert record.errors == 2 + assert record.paths == [r"C:\Users"] + assert record.detections[0].signature == "Win.Test" + + stats = storage.stats() + assert stats["active_threats"] == 1 + assert stats["latest_scan"].id == scan_id + + storage.update_detection_action(detection.id, "quarantined", "Q-1") + assert storage.stats()["active_threats"] == 0 + assert storage.recent_detections()[0]["action"] == "quarantined" + + +def test_quarantine_rows_and_counts(tmp_path): + storage = _storage(tmp_path) + item = QuarantineItem( + id="Q-1", original_path=r"C:\bad.exe", quarantine_path=str(tmp_path / "q.bin"), + signature="Win.Test", sha256="a" * 64, size=10, created_at="2026-10-25T10:00:00Z", + scan_id=1) + storage.add_quarantine(item) + assert storage.quarantine_count() == 1 + assert storage.get_quarantine("Q-1").signature == "Win.Test" + storage.set_quarantine_status("Q-1", "restored") + assert storage.quarantine_count() == 0 + assert storage.list_quarantine()[0].status == "restored" + + +def test_realtime_events_and_diagnostics_are_serializable(tmp_path): + storage = _storage(tmp_path) + storage.add_realtime_event(RealtimeEvent( + created_at="2026-10-25T10:00:00Z", path=r"C:\Users\a\bad.exe", + signature="Win.Test", action="quarantined", detail="Locked")) + events = storage.list_realtime_events() + assert events[0].action == "quarantined" + snapshot = storage.diagnostics_snapshot() + json.dumps(snapshot) + assert snapshot["realtime"][0]["signature"] == "Win.Test" diff --git a/tests/test_ui_smoke.py b/tests/test_ui_smoke.py new file mode 100644 index 0000000..6cf0eb2 --- /dev/null +++ b/tests/test_ui_smoke.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import time + +from PyQt6.QtCore import Qt + +from boltclam.api_client import ApiClient +from boltclam.auth import AuthService, CredentialStore +from boltclam.clamav import ClamAVService +from boltclam.config import AccountCache, SettingsStore +from boltclam.context import AppContext +from boltclam.quarantine import QuarantineService +from boltclam.storage import Storage +from boltclam.ui.login_dialog import LoginDialog +from boltclam.ui.main_window import MainWindow + +ACCOUNT = { + "user": {"id": "1", "email": "aaron@example.com", "name": "Aaron", + "username": "aaron"}, + "subscription": {"tier": "premium", "label": "Premium", "status": "active", + "current_period_end": "2026-12-01T00:00:00Z"}, + "balance": {"weekly_remaining": 100, "topup": 0, "allowed": True}, + "features": {"licensed": True}, +} + + +class FakeApi: + def get_account(self): + return ACCOUNT + + def get_updates(self, *args, **kwargs): + raise RuntimeError("offline") + + +def _context(tmp_path) -> AppContext: + store = SettingsStore() + store.save(store.load()) + settings = store.update(last_update_check=time.time(), logging_enabled=False, + realtime_enabled=False) + storage = Storage() + quarantine = QuarantineService(storage, tmp_path / "quarantine") + auth = AuthService( + "http://127.0.0.1:9", install_id=settings.install_id, + credentials=CredentialStore(tmp_path / "credentials.dat"), + account_cache=AccountCache(tmp_path / "account.json")) + return AppContext( + settings_store=store, settings=settings, storage=storage, + quarantine=quarantine, clamav=ClamAVService(settings.clamav_path), + auth=auth, session_key="bolt_live_fake", account=ACCOUNT, + install_id=settings.install_id, api=FakeApi()) + + +def test_main_window_builds_four_pages_and_switches_tabs(qapp, isolated_env): + context = _context(isolated_env) + window = MainWindow(context) + try: + window.show() + qapp.processEvents() + assert window.stack.count() == 4 + assert [button.text() for button in window.nav_buttons] == [ + "Homepage", "Scan & results", "About & updates", "Settings"] + assert window.scan_page.tabs.currentIndex() == 0 + window.scan_page.tabs.setCurrentIndex(1) + assert window.scan_page.tabs.currentIndex() == 1 + window._navigate(3) + assert window.stack.currentIndex() == 3 + assert window.settings_page.channel_combo.currentData() == "release" + assert window.about_page.rows["account"].value_label.text() == "Aaron" + finally: + window._allow_close = True + window.close() + qapp.processEvents() + + +def test_login_dialog_builds_without_starting_a_worker(qapp, isolated_env): + auth = AuthService( + "http://127.0.0.1:9", install_id="test", + credentials=CredentialStore(isolated_env / "credentials.dat"), + account_cache=AccountCache(isolated_env / "account.json")) + dialog = LoginDialog(auth, notice="Test notice") + try: + assert "BoltClam" in dialog.windowTitle() + assert dialog.browser_button.isEnabled() + assert dialog.session is None + finally: + dialog.reject() diff --git a/tests/test_updates.py b/tests/test_updates.py new file mode 100644 index 0000000..6378531 --- /dev/null +++ b/tests/test_updates.py @@ -0,0 +1,121 @@ +from __future__ import annotations + +import hashlib +from pathlib import Path + +import pytest + +from boltclam.models import UpdateAsset, UpdateFeed, UpdateRelease +from boltclam.updates import ( + UpdateError, + UpdateService, + choose_asset, + is_newer, + parse_checksums, + parse_version, +) + + +def test_version_parsing_and_comparison(): + assert parse_version("v1.2.3") == ((1, 2, 3), ()) + assert parse_version("1.2.3-beta.1") + assert is_newer("v1.2.0", "1.1.9") is True + assert is_newer("v1.0.0", "1.0.0") is False + assert is_newer("v1.0.0", "1.0.0-beta.2") is True + assert is_newer("v1.0.0-beta.2", "1.0.0") is False + assert is_newer("v1.0.0-beta.2", "1.0.0-beta.1") is True + assert is_newer("not-a-version", "1.0.0") is False + + +def _release(tag="v1.2.0", prerelease=False, assets=None) -> UpdateRelease: + return UpdateRelease(id="2", tag=tag, name="Test", notes="notes", + prerelease=prerelease, published_at="2026-10-20T00:00:00Z", + html_url="https://git.example", assets=assets or []) + + +def test_choose_asset_prefers_a_setup_executable(): + release = _release(assets=[ + UpdateAsset("1", "BoltClam-1.2.0.zip", 5, "application/zip", "u1"), + UpdateAsset("2", "BoltClam-1.2.0-Setup.exe", 5, "application/octet-stream", "u2"), + ]) + assert choose_asset(release).name == "BoltClam-1.2.0-Setup.exe" + + +def test_choose_asset_falls_back_to_zip(): + release = _release(assets=[ + UpdateAsset("1", "notes.txt", 5, "text/plain", "u1"), + UpdateAsset("2", "BoltClam-windows.zip", 5, "application/zip", "u2"), + ]) + assert choose_asset(release).name == "BoltClam-windows.zip" + assert choose_asset(_release(assets=[])) is None + + +def test_feed_from_api_preserves_release_fields(): + feed = UpdateFeed.from_api({ + "channel": "beta", + "latest": {"id": "3", "tag": "v2.0.0-beta", "name": "Beta", + "notes": "notes", "prerelease": True, + "published_at": "2026-10-22T00:00:00Z", + "assets": [{"id": "9", "name": "a.exe", "size": 12, + "content_type": "x", "download_url": "u"}]}, + "releases": [], + "checked_at": "2026-10-25T00:00:00Z", + }) + assert feed.channel == "beta" + assert feed.latest.tag == "v2.0.0-beta" + assert feed.latest.assets[0].name == "a.exe" + + +def test_parse_checksums(): + digest = "a" * 64 + assert parse_checksums(f"{digest} BoltClam-Setup.exe\n") == { + "BoltClam-Setup.exe": digest} + + +class FakeApi: + def __init__(self, checksum_text: str | None = None, payload: bytes = b"payload"): + self.checksum_text = checksum_text + self.payload = payload + + def download_asset(self, url, destination, progress=None): + destination = Path(destination) + destination.parent.mkdir(parents=True, exist_ok=True) + data = self.checksum_text.encode() if self.checksum_text and "SUM" in url else self.payload + destination.write_bytes(data) + return destination, hashlib.sha256(data).hexdigest() + + +def _download_release(checksum: str) -> UpdateRelease: + asset = UpdateAsset("1", "BoltClam-Setup.exe", 7, "application/octet-stream", + "https://api.example/asset") + checksum_asset = UpdateAsset("2", "SHA256SUMS.txt", 100, "text/plain", + "https://api.example/SUM") + return _release(assets=[asset, checksum_asset]) + + +def test_download_verifies_a_matching_checksum(isolated_env): + payload = b"payload" + digest = hashlib.sha256(payload).hexdigest() + release = _download_release(digest) + service = UpdateService(FakeApi(f"{digest} BoltClam-Setup.exe\n", payload)) + result = service.download(release, release.assets[0]) + assert result.path.read_bytes() == payload + assert result.verified is True + assert result.expected_sha256 == digest + + +def test_download_rejects_a_bad_checksum(isolated_env): + release = _download_release("b" * 64) + service = UpdateService(FakeApi(f"{'b' * 64} BoltClam-Setup.exe\n", b"payload")) + with pytest.raises(UpdateError): + service.download(release, release.assets[0]) + + +def test_check_returns_the_feed(isolated_env): + class Api: + def get_updates(self, channel, force=False): + return {"channel": channel, "latest": None, "releases": []} + + feed = UpdateService(Api(), "beta").check() + assert feed.channel == "beta" + assert feed.latest is None