From 01c19349eec5f4c78e619f3216b9b1cc0ca2ecd8 Mon Sep 17 00:00:00 2001 From: Dr Jones Date: Thu, 21 May 2026 16:47:22 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20exhaustive=20feature=20expansion=20?= =?UTF-8?q?=E2=80=94=20cookie=20modes,=20DNS/WebRTC=20testers,=20map,=20Fi?= =?UTF-8?q?refox=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cookie system: - Expand from 5 to 12 fully-specified CookiePolicy modes in browser_identity.py - Add CookiePolicy dataclass: behavior, lifetime, TCP partitioning, clearOnShutdown.* - browser_profile.py emits full cookie pref set from policy object - UI dropdown widened to 680px with live description label per mode Firefox launch fix: - Detect Firefox via Windows registry, AppData, and shutil.which - Use DETACHED_PROCESS|CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP flags - Wait up to 3s for firefox.exe in tasklist instead of polling parent pid - taskkill on stop() to terminate all firefox.exe processes DNS leak tester: - dns_leak.py: FullDnsLeakReport dataclass, run_dns_leak_test comparing proxy DoH resolution vs direct system DNS WebRTC tester: - webrtc_check.py: STUN UDP probe, registry policy check, user.js pref check Ban tester: - Added SITES_SHOPPING, SITES_CRYPTO, SITES_DNS categories - Parallel execution via ThreadPoolExecutor - Expanded banned-text hint keywords Fingerprint audit: - OS identity checks: hostname, MAC, GUID, OS version, timezone, screen res - Browser consistency analysis of user.js Neon world map: - world_map.png bundled; chain_map.py renders hop arcs over it with glow effect Signup prep: - Auto-save account on Open & Autofill; Copy Email / Copy Pass buttons - Auto-fill custom URL when preset site selected - PyInstaller-safe path resolution for signup_extension Spec: - Bundle signup_extension dir and world_map.png as PyInstaller data files Co-authored-by: Cursor --- ProxyChainManager.spec | 7 +- proxy_chain_manager/app.py | 594 ++++++++++++++++++------ proxy_chain_manager/ban_tester.py | 234 ++++++++-- proxy_chain_manager/browser_identity.py | 282 +++++++++-- proxy_chain_manager/browser_launcher.py | 204 +++++++- proxy_chain_manager/browser_profile.py | 78 +++- proxy_chain_manager/chain_map.py | 214 ++++++--- proxy_chain_manager/dns_leak.py | 295 ++++++++++-- proxy_chain_manager/fingerprint.py | 181 +++++++- proxy_chain_manager/signup_prep.py | 13 +- proxy_chain_manager/webrtc_check.py | 211 +++++++++ proxy_chain_manager/world_map.png | Bin 0 -> 154826 bytes 12 files changed, 1913 insertions(+), 400 deletions(-) create mode 100644 proxy_chain_manager/webrtc_check.py create mode 100644 proxy_chain_manager/world_map.png diff --git a/ProxyChainManager.spec b/ProxyChainManager.spec index eb6b0a7..aa40cd8 100644 --- a/ProxyChainManager.spec +++ b/ProxyChainManager.spec @@ -1,7 +1,12 @@ # -*- mode: python ; coding: utf-8 -*- from PyInstaller.utils.hooks import collect_all -datas = [] +datas = [ + # Signup autofill WebExtension (manifest + content.js) + ('proxy_chain_manager/signup_extension', 'proxy_chain_manager/signup_extension'), + # Neon world-map background image for the chain map widget + ('proxy_chain_manager/world_map.png', 'proxy_chain_manager'), +] binaries = [] hiddenimports = ['pystray._win32'] tmp_ret = collect_all('customtkinter') diff --git a/proxy_chain_manager/app.py b/proxy_chain_manager/app.py index 45a2a9f..a28e827 100644 --- a/proxy_chain_manager/app.py +++ b/proxy_chain_manager/app.py @@ -39,7 +39,7 @@ from .firewall import ( request_admin_relaunch, ) from .artifact_wipe import wipe_artifacts -from .ban_tester import BanTestResult, run_ban_tests, test_single_site +from .ban_tester import BanTestResult, SITE_CATEGORIES, run_ban_tests, test_single_site from .exit_intel import ExitIntel, fetch_exit_intel from .chain_map import build_chain_map_points, render_chain_map, resize_map_display from .signup_prep import ( @@ -64,8 +64,14 @@ from .browser_identity import ( PERSONA_LABELS, PERSONAS, ) -from .dns_leak import check_dns_leak_hint, flush_dns_cache +from .dns_leak import ( + FullDnsLeakReport, + check_dns_leak_hint, + flush_dns_cache, + run_dns_leak_test, +) from .leak_audit import AuditReport, run_audit_sync +from .webrtc_check import WebRtcCheckResult, run_webrtc_check from .browser_launcher import ( BrowserConfig, BrowserSession, @@ -1240,20 +1246,49 @@ def main() -> None: persona_menu.pack(side="left", padx=(4, 0)) cookie_row = ctk.CTkFrame(persona_card, fg_color="transparent") - cookie_row.pack(fill="x", padx=12, pady=(0, 10)) + cookie_row.pack(fill="x", padx=12, pady=(0, 4)) ctk.CTkLabel(cookie_row, text="Cookies:", font=(FONT, 11), text_color=TEXT, width=80, anchor="w").pack(side="left") - cookie_var = ctk.StringVar( - value=COOKIE_LABELS.get(s.browser_cookie_mode, COOKIE_LABELS["block_third_party"]) + _default_cookie_label = COOKIE_LABELS.get( + s.browser_cookie_mode, COOKIE_LABELS["block_third_party"] ) + cookie_var = ctk.StringVar(value=_default_cookie_label) + + # Cookie detail sub-labels (key → short description of side effects) + _COOKIE_NOTES: dict[str, str] = { + "accept_all": "Everything works · cookies persist to disk", + "accept_all_session": "Full compat while browsing · cookies wiped on close", + "block_third_party": "Blocks cross-site trackers · most sites work · cookies persist", + "block_third_party_session": "Blocks 3rd-party + nothing persists after close", + "block_third_party_trackers":"ETP Strict — same as Firefox enhanced tracking protection", + "block_social_trackers": "Blocks Facebook pixel, Twitter widgets, social SSO badges", + "block_unvisited": "Very light — only blocks sites you haven't visited", + "partitioned_tcp": "Total Cookie Protection — each site gets isolated 3rd-party jar", + "partitioned_session": "Isolated jars + full disk wipe on close · zero cross-site state", + "session_only": "All cookies expire on close · 1st-party allowed while browsing", + "ghost_mode": "Isolated + session + wipes cache/history/localStorage/formdata", + "block_all": "Breaks most logins · read-only / scraping use only", + } + + cookie_note_lbl = ctk.CTkLabel( + persona_card, text=_COOKIE_NOTES.get(s.browser_cookie_mode, ""), + font=(FONT, 9), text_color=TEXT2, anchor="w", wraplength=820, + ) + + def _on_cookie_change(_choice: str = "") -> None: + key = _cookie_key() + cookie_note_lbl.configure(text=_COOKIE_NOTES.get(key, "")) + cookie_menu = ctk.CTkOptionMenu( cookie_row, values=[COOKIE_LABELS[k] for k in COOKIE_MODES], variable=cookie_var, fg_color=BG, button_color=ACCENT2, button_hover_color=ACCENT, - text_color=TEXT, font=(FONT, 11), width=500, + text_color=TEXT, font=(FONT, 11), width=680, + command=_on_cookie_change, ) cookie_menu.pack(side="left", padx=(4, 0)) + cookie_note_lbl.pack(anchor="w", padx=(92, 12), pady=(0, 10)) def _persona_key() -> str: for k in PERSONAS: @@ -1415,68 +1450,81 @@ def main() -> None: ban_head = ctk.CTkFrame(ban_scroll, fg_color=CARD, corner_radius=10, border_width=1, border_color=GLOW) ban_head.pack(fill="x", padx=8, pady=(6, 8)) ctk.CTkLabel( - ban_head, - text="Ban List Tester", - font=(FONT, 13, "bold"), - text_color=CYAN, + ban_head, text="Ban / Reachability Tester", + font=(FONT, 13, "bold"), text_color=CYAN, ).pack(anchor="w", padx=12, pady=(10, 2)) ctk.CTkLabel( ban_head, - text="Tests high-traffic sites through your ACTIVE chain to detect likely exit-IP bans.", - font=(FONT, 10), - text_color=TEXT2, - ).pack(anchor="w", padx=12, pady=(0, 8)) + text=( + "Tests social, shopping, crypto exchanges, and DNS providers through your ACTIVE chain. " + "Detects exit-IP bans and geo-blocks." + ), + font=(FONT, 10), text_color=TEXT2, wraplength=880, justify="left", + ).pack(anchor="w", padx=12, pady=(0, 6)) + + # Category selector + ban_cat_row = ctk.CTkFrame(ban_head, fg_color="transparent") + ban_cat_row.pack(fill="x", padx=12, pady=(0, 6)) + ctk.CTkLabel(ban_cat_row, text="Category:", font=(FONT, 10), text_color=TEXT2, width=70, + anchor="w").pack(side="left") + _ban_cat_keys = list(SITE_CATEGORIES.keys()) # "Social / General", "Shopping", etc. + ban_cat_var = ctk.StringVar(value="All") + ban_cat_menu = ctk.CTkOptionMenu( + ban_cat_row, values=_ban_cat_keys, variable=ban_cat_var, + fg_color=BG, button_color=ACCENT2, button_hover_color=ACCENT, + text_color=TEXT, width=200, + ) + ban_cat_menu.pack(side="left", padx=(0, 8)) ban_status_var = ctk.StringVar(value="Status: idle") ctk.CTkLabel(ban_head, textvariable=ban_status_var, font=(FONT, 11, "bold"), text_color=TEXT).pack( - anchor="w", padx=12, pady=(0, 8) + anchor="w", padx=12, pady=(0, 6) ) ban_results_frame = ctk.CTkFrame( - ban_scroll, - fg_color=CARD, - corner_radius=10, - border_width=1, - border_color=GLOW, + ban_scroll, fg_color=CARD, corner_radius=10, border_width=1, border_color=GLOW, ) ban_results_frame.pack(fill="both", expand=True, padx=8, pady=(0, 8)) ban_rows_wrap = ctk.CTkFrame(ban_results_frame, fg_color="transparent") ban_rows_wrap.pack(fill="both", expand=True, padx=8, pady=8) - def _ban_row(site: str, status: str, detail: str, color: str) -> None: + def _ban_row(site: str, status: str, detail: str, color: str, cat_header: str = "") -> None: + if cat_header: + ctk.CTkLabel( + ban_rows_wrap, text=f" {cat_header}", + font=(FONT, 10, "bold"), text_color=CYAN, anchor="w", + ).pack(fill="x", padx=4, pady=(6, 1)) row = ctk.CTkFrame(ban_rows_wrap, fg_color=PANEL, corner_radius=6, height=30) row.pack(fill="x", pady=2) row.pack_propagate(False) - ctk.CTkLabel(row, text=site, font=(FONT, 11, "bold"), text_color=TEXT, width=140, anchor="w").pack( - side="left", padx=(8, 4) - ) - ctk.CTkLabel(row, text=status, font=(FONT, 10, "bold"), text_color=color, width=80).pack( - side="left", padx=4 - ) - ctk.CTkLabel(row, text=detail, font=("Consolas", 10), text_color=TEXT2, anchor="w").pack( - side="left", fill="x", expand=True, padx=(6, 8) - ) + ctk.CTkLabel(row, text=site, font=(FONT, 11, "bold"), text_color=TEXT, + width=160, anchor="w").pack(side="left", padx=(8, 4)) + ctk.CTkLabel(row, text=status, font=(FONT, 10, "bold"), text_color=color, + width=72).pack(side="left", padx=4) + ctk.CTkLabel(row, text=detail, font=("Consolas", 10), text_color=TEXT2, + anchor="w").pack(side="left", fill="x", expand=True, padx=(6, 8)) def _render_ban_results(results: list[BanTestResult]) -> None: - for w in list(ban_rows_wrap.winfo_children()): - w.destroy() + for child in list(ban_rows_wrap.winfo_children()): + child.destroy() if not results: - ctk.CTkLabel( - ban_rows_wrap, - text="No results yet.", - font=(FONT, 11), - text_color=TEXT2, - ).pack(anchor="w", padx=8, pady=8) + ctk.CTkLabel(ban_rows_wrap, text="No results yet.", font=(FONT, 11), + text_color=TEXT2).pack(anchor="w", padx=8, pady=8) return + seen_cats: set[str] = set() for r in results: + cat = r.category or "" + header = cat if cat and cat not in seen_cats else "" + if cat: + seen_cats.add(cat) if r.status == "ok": st, color = "OK", GREEN elif r.status == "banned": st, color = "BANNED", ORANGE else: st, color = "ERROR", RED - _ban_row(r.site, st, r.detail, color) + _ban_row(r.site, st, r.detail, color, cat_header=header) _render_ban_results([]) @@ -1489,23 +1537,24 @@ def main() -> None: _log("Ban tester: start the chain first, then run tests.") return proxy_url = f"http://{svc.settings.listen_addr()}" + chosen_cat = ban_cat_var.get() + # "All" means every category + cats = None if chosen_cat == "All" else [chosen_cat] ban_status_var.set("Status: running…") ban_start_btn.configure(state="disabled") _render_ban_results([]) def work() -> None: timeout = min(18.0, max(8.0, float(svc.settings.validation_timeout_seconds) + 3.0)) - results = run_ban_tests(proxy_url, timeout_seconds=timeout) - ok_n = sum(1 for r in results if r.status == "ok") + results = run_ban_tests(proxy_url, timeout_seconds=timeout, categories=cats) + ok_n = sum(1 for r in results if r.status == "ok") banned_n = sum(1 for r in results if r.status == "banned") - err_n = len(results) - ok_n - banned_n + err_n = len(results) - ok_n - banned_n def done() -> None: _render_ban_results(results) - ban_status_var.set(f"Status: done | ok={ok_n} banned={banned_n} errors={err_n}") - _log( - f"Ban tester done via {proxy_url} — ok={ok_n}, banned={banned_n}, errors={err_n}." - ) + ban_status_var.set(f"Status: done — ok={ok_n} banned={banned_n} errors={err_n}") + _log(f"Ban tester done via {proxy_url} — ok={ok_n}, banned={banned_n}, errors={err_n}.") ban_start_btn.configure(state="normal") root.after(0, done) @@ -1513,13 +1562,8 @@ def main() -> None: threading.Thread(target=work, daemon=True).start() ban_start_btn = _btn( - ban_action, - "Start ban test", - _start_ban_test, - w=120, - h=30, - fg_color=ACCENT2, - hover_color=ACCENT, + ban_action, "Start test", _start_ban_test, + w=120, h=30, fg_color=ACCENT2, hover_color=ACCENT, ) ban_start_btn.pack(side="left") @@ -1561,11 +1605,12 @@ def main() -> None: signup_site_labels[0], ) ) - signup_site_menu = ctk.CTkOptionMenu( - site_row, values=signup_site_labels, variable=signup_site_var, - fg_color=BG, button_color=ACCENT2, button_hover_color=ACCENT, text_color=TEXT, width=260, - ) - signup_site_menu.pack(side="left", padx=(0, 8)) + custom_url_row = ctk.CTkFrame(signup_head, fg_color="transparent") + custom_url_row.pack(fill="x", padx=12, pady=(0, 4)) + ctk.CTkLabel(custom_url_row, text="URL", width=70, anchor="w", font=(FONT, 10), text_color=TEXT2).pack(side="left") + signup_custom_url = ctk.CTkEntry(custom_url_row, height=28, fg_color=BG, border_color=ACCENT) + signup_custom_url.pack(side="left", fill="x", expand=True) + signup_custom_url.insert(0, signup_draft.custom_url or "") def _signup_site_key() -> str: label = signup_site_var.get() @@ -1574,12 +1619,22 @@ def main() -> None: return signup_site_keys[i] return signup_site_keys[0] - custom_url_row = ctk.CTkFrame(signup_head, fg_color="transparent") - custom_url_row.pack(fill="x", padx=12, pady=(0, 4)) - ctk.CTkLabel(custom_url_row, text="Custom URL", width=70, anchor="w", font=(FONT, 10), text_color=TEXT2).pack(side="left") - signup_custom_url = ctk.CTkEntry(custom_url_row, height=28, fg_color=BG, border_color=ACCENT) - signup_custom_url.pack(side="left", fill="x", expand=True) - signup_custom_url.insert(0, signup_draft.custom_url or "") + def _on_signup_site_change(_choice: str = "") -> None: + key = _signup_site_key() + preset = SIGNUP_PRESETS.get(key) + if preset and preset.url: + signup_custom_url.delete(0, "end") + signup_custom_url.insert(0, preset.url) + + signup_site_menu = ctk.CTkOptionMenu( + site_row, values=signup_site_labels, variable=signup_site_var, + fg_color=BG, button_color=ACCENT2, button_hover_color=ACCENT, text_color=TEXT, width=260, + command=_on_signup_site_change, + ) + signup_site_menu.pack(side="left", padx=(0, 8)) + + # Populate URL field for the initially-selected site + _on_signup_site_change() def _signup_field(parent: Any, label: str, show: str = "") -> ctk.CTkEntry: row = ctk.CTkFrame(parent, fg_color="transparent") @@ -1695,34 +1750,48 @@ def main() -> None: # ── Pre-flight checklist ───────────────────────────────────────────────── preflight_card = _signup_card( "Pre-flight check", - "Verifies chain, target site reachability, and exit-IP intel before you sign up.", + "Exhaustive readiness check: chain, exit IP, geo/ASN, DNS leak, WebRTC, " + "IPv6, fingerprint, target site reachability, and direct-IP comparison.", ) pf_rows: dict[str, dict[str, ctk.StringVar | ctk.CTkLabel]] = {} + _PF_CHECKS = [ + ("chain", "Chain running"), + ("exit_ip", "Exit IP (via chain)"), + ("direct_ip", "Direct IP (no chain)"), + ("leak_compare","IP leak check"), + ("intel", "Geo / ASN intel"), + ("datacenter", "Residential ASN"), + ("dns", "DNS routing"), + ("ipv6", "IPv6 binding"), + ("webrtc", "WebRTC policy"), + ("https_tunnel","HTTPS tunnel (CONNECT)"), + ("target", "Target site reachable"), + ("firefox", "Firefox path"), + ] + def _pf_row(key: str, label: str) -> None: row = ctk.CTkFrame(preflight_card, fg_color=PANEL, corner_radius=6) row.pack(fill="x", padx=12, pady=2) dot = ctk.CTkLabel(row, text="●", font=(FONT, 14), text_color=DIM, width=18) dot.pack(side="left", padx=(8, 4)) ctk.CTkLabel(row, text=label, font=(FONT, 11, "bold"), text_color=TEXT, - width=160, anchor="w").pack(side="left") + width=180, anchor="w").pack(side="left") var = ctk.StringVar(value="not run") ctk.CTkLabel(row, textvariable=var, font=(FONT, 10), text_color=TEXT2, anchor="w").pack(side="left", fill="x", expand=True, padx=(6, 8), pady=4) pf_rows[key] = {"var": var, "dot": dot} - _pf_row("chain", "Chain running") - _pf_row("exit_ip", "Exit IP") - _pf_row("intel", "IP intel (geo/ASN)") - _pf_row("target", "Target site reachable") + for _k, _lbl in _PF_CHECKS: + _pf_row(_k, _lbl) def _pf_set(key: str, status: str, detail: str) -> None: r = pf_rows.get(key) if not r: return color = {"ok": GREEN, "warn": ORANGE, "fail": RED, "running": YELLOW}.get(status, DIM) - r["dot"].configure(text_color=color) - r["var"].set(detail) + r["dot"].configure(text_color=color) # type: ignore[union-attr] + r["var"].set(detail) # type: ignore[union-attr] pf_summary_var = ctk.StringVar(value="Not run.") ctk.CTkLabel(preflight_card, textvariable=pf_summary_var, font=(FONT, 10, "bold"), @@ -1739,72 +1808,189 @@ def main() -> None: pf_summary_var.set("Pick a site or enter a custom URL first.") return pf_running[0] = True - for k in ("chain", "exit_ip", "intel", "target"): + for k, _ in _PF_CHECKS: _pf_set(k, "running", "checking…") pf_summary_var.set("Running pre-flight…") + # Immediate sync checks ────────────────────────────────────────────── chain_ok = service_running[0] and bool(svc.current_chain) - if not chain_ok: - _pf_set("chain", "fail", "chain not running") - _pf_set("exit_ip", "fail", "n/a") - _pf_set("intel", "fail", "n/a") - _pf_set("target", "fail", "n/a") - pf_summary_var.set("Start the chain, then re-run pre-flight.") - pf_running[0] = False - return - _pf_set("chain", "ok", f"{len(svc.current_chain)} hop(s) up") + if chain_ok: + _pf_set("chain", "ok", f"{len(svc.current_chain)} hop(s) active") + else: + _pf_set("chain", "fail", "chain not running — start first") + + # Firefox path check + ff_path = firefox_path_var.get().strip() or default_firefox_path() + from pathlib import Path as _Path + if ff_path and _Path(ff_path).is_file(): + _pf_set("firefox", "ok", ff_path) + else: + _pf_set("firefox", "fail", f"not found: {ff_path or '(no path set)'}") + proxy_url = f"http://{svc.settings.listen_addr()}" target_host = urlparse(url).hostname or "(unknown)" def work() -> None: timeout = min(15.0, max(8.0, float(svc.settings.validation_timeout_seconds) + 2.0)) - intel = fetch_exit_intel(proxy_url, timeout_seconds=timeout) - site_res = test_single_site(proxy_url, target_host, url, timeout_seconds=timeout) + warns: list[str] = [] - def done() -> None: - ip = (v_exit_ip.get() or "—").strip() - if ip and ip != "—": - _pf_set("exit_ip", "ok", ip) + # ── 1. Direct IP (bypass chain) ────────────────────────────────── + from .validator import get_direct_ip as _get_direct_ip + import asyncio as _asyncio + try: + direct_ip = _asyncio.run(_get_direct_ip(svc.settings.ip_check_url, timeout / 2)) + except Exception: + direct_ip = None + + def _set_direct(ip: str | None) -> None: + if ip: + _pf_set("direct_ip", "ok", ip) else: - _pf_set("exit_ip", "warn", "no IP reported yet") - if intel.ok: - _apply_intel(intel) - if intel.is_proxy_flagged: - _pf_set("intel", "fail", f"proxy-flagged · {intel.org or intel.isp}") - elif intel.is_datacenter: - _pf_set("intel", "warn", f"datacenter ASN · {intel.org or intel.isp}") + _pf_set("direct_ip", "warn", "could not reach check URL directly") + + root.after(0, lambda ip=direct_ip: _set_direct(ip)) + + # ── 2. Exit IP via chain ───────────────────────────────────────── + if chain_ok: + intel = fetch_exit_intel(proxy_url, timeout_seconds=timeout) + exit_ip_str = intel.ip if intel.ok else (v_exit_ip.get() or "").strip() + + def _set_exit(ip_: str, intel_: Any) -> None: + if ip_ and ip_ not in ("—", ""): + _pf_set("exit_ip", "ok", ip_) else: - loc = ", ".join([b for b in (intel.city, intel.country) if b]) or "—" - _pf_set("intel", "ok", f"{loc} · AS{intel.asn} {intel.org or intel.isp}") - else: - _pf_set("intel", "warn", intel.detail or "lookup failed") - if site_res.status == "ok": - _pf_set("target", "ok", f"{target_host} {site_res.detail}") - elif site_res.status == "banned": - _pf_set("target", "fail", f"likely banned · {site_res.detail}") - else: - _pf_set("target", "warn", site_res.detail) + _pf_set("exit_ip", "warn", "no exit IP yet") + # IP leak compare + if direct_ip and ip_ and ip_ not in ("—", ""): + if direct_ip == ip_: + _pf_set("leak_compare", "fail", + f"exit == direct ({ip_}) — chain not working!") + warns.append("exit IP matches direct IP (chain not routing)") + else: + _pf_set("leak_compare", "ok", + f"exit {ip_} ≠ direct {direct_ip} ✓") + else: + _pf_set("leak_compare", "warn", "cannot compare — missing IPs") + # Geo/ASN + if intel_.ok: + _apply_intel(intel_) + loc = ", ".join([b for b in (intel_.city, intel_.country) if b]) or "—" + asn_str = f"AS{intel_.asn}" if intel_.asn else "" + if intel_.is_proxy_flagged: + _pf_set("intel", "fail", f"proxy-flagged · {intel_.org or intel_.isp}") + warns.append("exit IP is proxy-flagged") + elif intel_.is_datacenter: + _pf_set("intel", "warn", f"datacenter · {loc} {asn_str}") + _pf_set("datacenter", "warn", f"DC ASN — {intel_.org or intel_.isp}") + warns.append("exit IP is datacenter ASN") + else: + _pf_set("intel", "ok", f"{loc} {asn_str}") + _pf_set("datacenter", "ok", f"residential/ISP — {intel_.org or intel_.isp}") + else: + _pf_set("intel", "warn", intel_.detail or "lookup failed") + _pf_set("datacenter", "warn", "intel unavailable") - warns: list[str] = [] - if intel.ok and intel.is_proxy_flagged: - warns.append("exit IP is proxy-flagged") - if intel.ok and intel.is_datacenter: - warns.append("exit IP is a datacenter ASN") - if site_res.status == "banned": - warns.append("target site likely blocks this IP") - if not warns: - pf_summary_var.set("All checks passed — good to sign up.") - else: - pf_summary_var.set("Warnings: " + "; ".join(warns)) + root.after(0, lambda i=exit_ip_str, d=intel: _set_exit(i, d)) + else: + for k in ("exit_ip", "leak_compare", "intel", "datacenter"): + root.after(0, lambda k_=k: _pf_set(k_, "fail", "chain not running")) + + # ── 3. DNS routing check ───────────────────────────────────────── + try: + dns_rep = run_dns_leak_test( + proxy_url=proxy_url if chain_ok else None, + timeout=min(10.0, timeout), + ) + def _set_dns(r: Any) -> None: + if r.leaked: + _pf_set("dns", "fail", r.summary[:80]) + warns.append("DNS leak detected") + elif r.has_external_resolver: + _pf_set("dns", "warn", r.summary[:80]) + else: + _pf_set("dns", "ok", r.summary[:80]) + root.after(0, lambda r_=dns_rep: _set_dns(r_)) + except Exception as e: + root.after(0, lambda: _pf_set("dns", "warn", f"check error: {e}")) + + # ── 4. IPv6 binding ────────────────────────────────────────────── + try: + from .leak_audit import _check_ipv6_active + v6_on, v6_msg = _check_ipv6_active() + def _set_v6(on_: bool, msg_: str) -> None: + if on_: + _pf_set("ipv6", "warn", f"IPv6 active: {msg_}") + warns.append("IPv6 enabled (possible leak path)") + else: + _pf_set("ipv6", "ok", msg_) + root.after(0, lambda on=v6_on, msg=v6_msg: _set_v6(on, msg)) + except Exception as e: + root.after(0, lambda: _pf_set("ipv6", "warn", str(e))) + + # ── 5. WebRTC ──────────────────────────────────────────────────── + try: + profile = _Path(firefox_profile_var.get().strip() or default_profile_dir()) + rtc_rep = run_webrtc_check(profile_dir=profile) + def _set_rtc(r: Any) -> None: + if r.any_leak_risk: + issues_short = "; ".join(r.issues[:2])[:70] + _pf_set("webrtc", "warn", issues_short) + else: + _pf_set("webrtc", "ok", "all surfaces protected") + root.after(0, lambda r_=rtc_rep: _set_rtc(r_)) + except Exception as e: + root.after(0, lambda: _pf_set("webrtc", "warn", str(e))) + + # ── 6. HTTPS tunnel probe ───────────────────────────────────────── + if chain_ok: + try: + from .validator import check_https_tunnel + import asyncio as _as2 + tun_ok, tun_msg = _as2.run( + check_https_tunnel(proxy_url, timeout) + ) + def _set_tun(ok_: bool, msg_: str) -> None: + if ok_: + _pf_set("https_tunnel", "ok", msg_) + else: + _pf_set("https_tunnel", "fail", msg_) + warns.append("HTTPS CONNECT tunnel broken") + root.after(0, lambda ok=tun_ok, msg=tun_msg: _set_tun(ok, msg)) + except Exception as e: + root.after(0, lambda: _pf_set("https_tunnel", "warn", str(e))) + else: + root.after(0, lambda: _pf_set("https_tunnel", "fail", "chain not running")) + + # ── 7. Target site reachability ─────────────────────────────────── + if chain_ok: + site_res = test_single_site(proxy_url, target_host, url, timeout_seconds=timeout) + def _set_site(r: Any) -> None: + if r.status == "ok": + _pf_set("target", "ok", f"{target_host} — {r.detail}") + elif r.status == "banned": + _pf_set("target", "fail", f"likely BANNED · {r.detail}") + warns.append("target site likely blocks this exit IP") + else: + _pf_set("target", "warn", r.detail) + root.after(0, lambda r_=site_res: _set_site(r_)) + else: + root.after(0, lambda: _pf_set("target", "fail", "chain not running")) + + # ── Final summary ───────────────────────────────────────────────── + def _final() -> None: pf_running[0] = False + if warns: + pf_summary_var.set(f"⚠ {len(warns)} warning(s): " + " · ".join(warns[:3])) + else: + pf_summary_var.set("✓ All pre-flight checks passed — good to go.") - root.after(0, done) + root.after(200, _final) threading.Thread(target=work, daemon=True).start() pf_btn_row = ctk.CTkFrame(preflight_card, fg_color="transparent") pf_btn_row.pack(fill="x", padx=12, pady=(0, 10)) - _btn(pf_btn_row, "Run pre-flight", _run_preflight, w=130, h=28, + _btn(pf_btn_row, "Run pre-flight", _run_preflight, w=140, h=28, fg_color=ACCENT2, hover_color=ACCENT).pack(side="left") signup_status_var = ctk.StringVar(value="Ready — start chain, then Open & Autofill.") @@ -1824,19 +2010,46 @@ def main() -> None: accounts_list.pack(fill="x", padx=12, pady=(8, 10)) def _refresh_accounts_ui() -> None: - for w in list(accounts_list.winfo_children()): - w.destroy() + for child in list(accounts_list.winfo_children()): + child.destroy() rows = load_accounts() if not rows: - ctk.CTkLabel(accounts_list, text="No saved accounts yet.", font=(FONT, 10), text_color=TEXT2).pack( - anchor="w", padx=8, pady=8 - ) + ctk.CTkLabel(accounts_list, text="No saved accounts yet.", font=(FONT, 10), + text_color=TEXT2).pack(anchor="w", padx=8, pady=8) return - for rec in rows[:40]: - line = f"{rec.created_at[:16]} {rec.site} {rec.email} exit={rec.exit_ip or '—'}" - ctk.CTkLabel( - accounts_list, text=line, font=("Consolas", 10), text_color=TEXT, anchor="w", - ).pack(anchor="w", padx=8, pady=2) + for rec in rows[:60]: + row_frame = ctk.CTkFrame(accounts_list, fg_color=PANEL, corner_radius=6) + row_frame.pack(fill="x", pady=2, padx=4) + + info_lbl = ctk.CTkLabel( + row_frame, + text=f"{rec.created_at[:16]} {rec.site} {rec.email or rec.username or '—'} exit={rec.exit_ip or '—'}", + font=("Consolas", 10), text_color=TEXT, anchor="w", + ) + info_lbl.pack(side="left", fill="x", expand=True, padx=(8, 4), pady=4) + + def _copy_email(r: Any = rec) -> None: + v = r.email or r.username or "" + root.clipboard_clear() + root.clipboard_append(v) + _log(f"Copied email/user: {v}") + + def _copy_pass(r: Any = rec) -> None: + root.clipboard_clear() + root.clipboard_append(r.password or "") + _log(f"Copied password for {r.email or r.username or r.site}.") + + ctk.CTkButton( + row_frame, text="Copy Email", width=80, height=22, + font=(FONT, 10), fg_color=ACCENT2, hover_color=ACCENT, + command=_copy_email, + ).pack(side="right", padx=(2, 4), pady=4) + ctk.CTkButton( + row_frame, text="Copy Pass", width=76, height=22, + font=(FONT, 10), fg_color="#1e3a1e", hover_color="#2e5a2e", + text_color=GREEN, + command=_copy_pass, + ).pack(side="right", padx=(2, 2), pady=4) def _current_exit_meta() -> tuple[str, str]: ip = (v_exit_ip.get() or "—").strip() @@ -1909,6 +2122,8 @@ def main() -> None: tag = f" [{' · '.join(extras)}]" if extras else "" signup_status_var.set(f"Opened {label}{tag} — autofill active (you submit + captcha).") _log(f"Signup prep: opened {url} with autofill extension{tag}.") + # Auto-save entry so the account is recorded even if user forgets to save manually + _save_signup_account() else: signup_status_var.set("Browser launch failed.") svc.set_sticky(0) @@ -2100,33 +2315,124 @@ def main() -> None: _btn(fp_card, "Refresh fingerprint audit", _refresh_fingerprint, w=180, h=28).pack( anchor="w", padx=12, pady=(0, 10)) - dns_card = _priv_section("DNS", "Checks whether public resolvers may bypass your chain.") - dns_result_lbl = ctk.CTkLabel(dns_card, text="Run a check to see DNS configuration.", - font=(FONT, 11), text_color=TEXT2, wraplength=880, justify="left") - dns_result_lbl.pack(anchor="w", padx=12, pady=8) + # ── DNS leak test ──────────────────────────────────────────────────────── + dns_card = _priv_section( + "DNS leak test", + "Compares how DNS resolves through your chain vs direct. Detects resolver bypass.", + ) + dns_box = ctk.CTkTextbox( + dns_card, height=120, font=("Consolas", 10), + fg_color=BG, text_color=TEXT, scrollbar_button_color=ACCENT, + ) + dns_box.pack(fill="x", padx=12, pady=(4, 4)) + dns_box.insert("end", "Click 'Full DNS leak test' or 'Quick check' to probe.") + dns_status_lbl = ctk.CTkLabel(dns_card, text="", font=(FONT, 10, "bold"), text_color=TEXT2) + dns_status_lbl.pack(anchor="w", padx=12, pady=(0, 2)) - def _run_dns_check() -> None: - listen = f"http://{svc.settings.listen_addr()}" if svc.current_chain else None - r = check_dns_leak_hint(listen) - color = GREEN if r.ok else RED - dns_result_lbl.configure( - text=r.message + (f"\nResolvers: {', '.join(r.system_resolvers)}" if r.system_resolvers else ""), + def _render_dns_report(rep: FullDnsLeakReport) -> None: + dns_box.delete("1.0", "end") + mark = "LEAK" if rep.leaked else "OK " + dns_box.insert("end", f"[{mark}] {rep.summary}\n\n") + if rep.system_resolvers: + dns_box.insert("end", f"Configured resolvers: {', '.join(rep.system_resolvers)}\n") + for host, addrs in rep.direct_resolution.items(): + via_proxy = rep.proxy_resolution.get(host, []) + proxy_str = ", ".join(via_proxy) if via_proxy else "(no proxy answer)" + dns_box.insert("end", f" {host:<22} direct={', '.join(addrs) or '—':<20} via-proxy={proxy_str}\n") + if rep.errors: + dns_box.insert("end", "\nErrors: " + "; ".join(rep.errors[:3])) + color = RED if rep.leaked else GREEN + dns_status_lbl.configure( + text="DNS LEAK DETECTED" if rep.leaked else "DNS looks clean", text_color=color, ) - dns_btn_row = ctk.CTkFrame(dns_card, fg_color="transparent") - dns_btn_row.pack(fill="x", padx=12, pady=(0, 10)) - _btn(dns_btn_row, "DNS leak check", _run_dns_check, w=120, h=28).pack(side="left", padx=(0, 8)) + def _run_full_dns_test() -> None: + proxy = f"http://{svc.settings.listen_addr()}" if svc.current_chain else None + dns_status_lbl.configure(text="Running…", text_color=YELLOW) + dns_box.delete("1.0", "end") + dns_box.insert("end", "Probing DNS via proxy vs direct…\n") + + def work() -> None: + try: + rep = run_dns_leak_test(proxy_url=proxy, + timeout=min(12.0, svc.settings.validation_timeout_seconds)) + except Exception as e: + root.after(0, lambda: dns_status_lbl.configure( + text=f"Error: {e}", text_color=RED)) + return + root.after(0, lambda: _render_dns_report(rep)) + + threading.Thread(target=work, daemon=True).start() + + def _run_quick_dns_check() -> None: + listen = f"http://{svc.settings.listen_addr()}" if svc.current_chain else None + r = check_dns_leak_hint(listen) + color = GREEN if r.ok else RED + dns_box.delete("1.0", "end") + dns_box.insert("end", r.message) + if r.system_resolvers: + dns_box.insert("end", f"\nResolvers: {', '.join(r.system_resolvers)}") + dns_status_lbl.configure(text="clean" if r.ok else "issue detected", text_color=color) def _manual_dns_flush() -> None: ok, msg = flush_dns_cache() - dns_result_lbl.configure( - text=msg, text_color=GREEN if ok else RED, - ) + dns_status_lbl.configure(text=msg, text_color=GREEN if ok else RED) + dns_btn_row = ctk.CTkFrame(dns_card, fg_color="transparent") + dns_btn_row.pack(fill="x", padx=12, pady=(0, 10)) + _btn(dns_btn_row, "Full DNS leak test", _run_full_dns_test, w=140, h=28, + fg_color=ACCENT2, hover_color=ACCENT).pack(side="left", padx=(0, 6)) + _btn(dns_btn_row, "Quick check", _run_quick_dns_check, w=100, h=28).pack(side="left", padx=(0, 6)) _btn(dns_btn_row, "Flush DNS now", _manual_dns_flush, w=110, h=28, fg_color=DIM).pack(side="left") + # ── WebRTC leak test ───────────────────────────────────────────────────── + rtc_card = _priv_section( + "WebRTC leak test", + "Scans Chrome/Edge policy, Firefox profile prefs, and live STUN server reachability.", + ) + rtc_box = ctk.CTkTextbox( + rtc_card, height=110, font=("Consolas", 10), + fg_color=BG, text_color=TEXT, scrollbar_button_color=ACCENT, + ) + rtc_box.pack(fill="x", padx=12, pady=(4, 4)) + rtc_box.insert("end", "Click 'WebRTC leak test' to probe.") + rtc_status_lbl = ctk.CTkLabel(rtc_card, text="", font=(FONT, 10, "bold"), text_color=TEXT2) + rtc_status_lbl.pack(anchor="w", padx=12, pady=(0, 2)) + + def _render_rtc_report(rep: WebRtcCheckResult) -> None: + rtc_box.delete("1.0", "end") + for name, ok, detail in rep.checks: + mark = "OK " if ok else "RISK" + rtc_box.insert("end", f"[{mark}] {name:<40} {detail}\n") + color = RED if rep.any_leak_risk else GREEN + rtc_status_lbl.configure( + text="WebRTC LEAK RISK — see items above" if rep.any_leak_risk else "WebRTC looks protected", + text_color=color, + ) + + def _run_rtc_test() -> None: + rtc_status_lbl.configure(text="Running…", text_color=YELLOW) + rtc_box.delete("1.0", "end") + rtc_box.insert("end", "Scanning WebRTC surfaces…\n") + profile = Path(firefox_profile_var.get().strip() or default_profile_dir()) + + def work() -> None: + try: + rep = run_webrtc_check(profile_dir=profile) + except Exception as e: + root.after(0, lambda: rtc_status_lbl.configure(text=f"Error: {e}", text_color=RED)) + return + root.after(0, lambda: _render_rtc_report(rep)) + + threading.Thread(target=work, daemon=True).start() + + rtc_btn_row = ctk.CTkFrame(rtc_card, fg_color="transparent") + rtc_btn_row.pack(fill="x", padx=12, pady=(0, 10)) + _btn(rtc_btn_row, "WebRTC leak test", _run_rtc_test, w=140, h=28, + fg_color=ACCENT2, hover_color=ACCENT).pack(side="left") + wipe_card = _priv_section( "Forensic artifact wipe", "One-button purge of common Windows breadcrumb trails. Irreversible.", diff --git a/proxy_chain_manager/ban_tester.py b/proxy_chain_manager/ban_tester.py index dbdfac1..206aa33 100644 --- a/proxy_chain_manager/ban_tester.py +++ b/proxy_chain_manager/ban_tester.py @@ -1,29 +1,73 @@ from __future__ import annotations +import concurrent.futures +import socket from dataclasses import dataclass from typing import Iterable import httpx -# High-traffic destinations that often reveal proxy bans quickly. -POPULAR_SITES: tuple[tuple[str, str], ...] = ( - ("Google", "https://www.google.com/generate_204"), - ("YouTube", "https://www.youtube.com/"), - ("Facebook", "https://www.facebook.com/"), - ("Instagram", "https://www.instagram.com/"), - ("X", "https://x.com/"), - ("Reddit", "https://www.reddit.com/"), - ("Wikipedia", "https://www.wikipedia.org/"), - ("Amazon", "https://www.amazon.com/"), - ("Netflix", "https://www.netflix.com/"), - ("GitHub", "https://github.com/"), - ("Cloudflare", "https://www.cloudflare.com/"), - ("Microsoft", "https://www.microsoft.com/"), - ("TikTok", "https://www.tiktok.com/"), - ("BBC", "https://www.bbc.com/"), - ("DuckDuckGo", "https://duckduckgo.com/"), +# ── Site lists ──────────────────────────────────────────────────────────────── + +SITES_SOCIAL: tuple[tuple[str, str], ...] = ( + ("Google", "https://www.google.com/generate_204"), + ("YouTube", "https://www.youtube.com/"), + ("Facebook", "https://www.facebook.com/"), + ("Instagram", "https://www.instagram.com/"), + ("X / Twitter", "https://x.com/"), + ("Reddit", "https://www.reddit.com/"), + ("TikTok", "https://www.tiktok.com/"), + ("Wikipedia", "https://www.wikipedia.org/"), + ("DuckDuckGo", "https://duckduckgo.com/"), + ("BBC", "https://www.bbc.com/"), + ("GitHub", "https://github.com/"), ) +SITES_SHOPPING: tuple[tuple[str, str], ...] = ( + ("Amazon", "https://www.amazon.com/"), + ("eBay", "https://www.ebay.com/"), + ("Walmart", "https://www.walmart.com/"), + ("Etsy", "https://www.etsy.com/"), + ("AliExpress", "https://www.aliexpress.com/"), + ("Best Buy", "https://www.bestbuy.com/"), + ("Target", "https://www.target.com/"), + ("Newegg", "https://www.newegg.com/"), +) + +SITES_CRYPTO: tuple[tuple[str, str], ...] = ( + ("Coinbase", "https://www.coinbase.com/"), + ("Binance", "https://www.binance.com/"), + ("Kraken", "https://www.kraken.com/"), + ("Bybit", "https://www.bybit.com/"), + ("OKX", "https://www.okx.com/"), + ("KuCoin", "https://www.kucoin.com/"), + ("Bitfinex", "https://www.bitfinex.com/"), + ("Gemini", "https://www.gemini.com/"), +) + +SITES_DNS: tuple[tuple[str, str], ...] = ( + ("Cloudflare DNS (1.1.1.1)", "https://1.1.1.1/"), + ("Google DNS (8.8.8.8)", "https://dns.google/"), + ("Quad9 DNS (9.9.9.9)", "https://quad9.net/"), + ("AdGuard DNS", "https://adguard-dns.io/"), + ("NextDNS", "https://nextdns.io/"), + ("Mullvad DNS", "https://mullvad.net/en/help/dns-over-https-and-dns-over-tls/"), + ("OpenDNS", "https://www.opendns.com/"), +) + +# Combined default: all categories +POPULAR_SITES: tuple[tuple[str, str], ...] = ( + SITES_SOCIAL + SITES_SHOPPING + SITES_CRYPTO + SITES_DNS +) + +SITE_CATEGORIES: dict[str, tuple[tuple[str, str], ...]] = { + "Social / General": SITES_SOCIAL, + "Shopping": SITES_SHOPPING, + "Crypto Exchanges": SITES_CRYPTO, + "DNS Providers": SITES_DNS, + "All": POPULAR_SITES, +} + _BANNED_STATUS_CODES = {401, 403, 407, 418, 429, 451} _BANNED_TEXT_HINTS = ( "access denied", @@ -33,6 +77,15 @@ _BANNED_TEXT_HINTS = ( "unusual traffic", "captcha", "challenge required", + "bot detected", + "suspicious activity", + "geo-blocked", + "not available in your region", + "your ip", + "ip address", + "cloudflare ray", + "ddos protection", + "attention required", ) @@ -40,9 +93,10 @@ _BANNED_TEXT_HINTS = ( class BanTestResult: site: str url: str - status: str # ok | banned | error + status: str # ok | banned | error code: int | None detail: str + category: str = "" def _looks_banned_body(body: str) -> bool: @@ -50,7 +104,13 @@ def _looks_banned_body(body: str) -> bool: return any(h in t for h in _BANNED_TEXT_HINTS) -def _test_one(proxy_url: str, site: str, url: str, timeout_seconds: float) -> BanTestResult: +def _test_one( + proxy_url: str, + site: str, + url: str, + timeout_seconds: float, + category: str = "", +) -> BanTestResult: timeout = httpx.Timeout(timeout_seconds, connect=min(8.0, timeout_seconds)) try: with httpx.Client( @@ -58,27 +118,145 @@ def _test_one(proxy_url: str, site: str, url: str, timeout_seconds: float) -> Ba timeout=timeout, verify=False, follow_redirects=True, - headers={"User-Agent": "Mozilla/5.0"}, + headers={ + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" + ), + "Accept": "text/html,application/xhtml+xml,*/*;q=0.8", + "Accept-Language": "en-US,en;q=0.9", + }, ) as c: r = c.get(url) - body = r.text[:1800] if r.text else "" + body = r.text[:2400] if r.text else "" if r.status_code in _BANNED_STATUS_CODES or _looks_banned_body(body): - return BanTestResult(site, url, "banned", r.status_code, f"HTTP {r.status_code}") + return BanTestResult( + site, url, "banned", r.status_code, + f"HTTP {r.status_code}", category, + ) if 200 <= r.status_code < 400: - return BanTestResult(site, url, "ok", r.status_code, f"HTTP {r.status_code}") - return BanTestResult(site, url, "error", r.status_code, f"HTTP {r.status_code}") + return BanTestResult(site, url, "ok", r.status_code, f"HTTP {r.status_code}", category) + return BanTestResult(site, url, "error", r.status_code, f"HTTP {r.status_code}", category) except Exception as e: - return BanTestResult(site, url, "error", None, f"{type(e).__name__}: {e}") + short = str(e)[:80] + return BanTestResult(site, url, "error", None, f"{type(e).__name__}: {short}", category) + + +def _dns_resolve_via_system(hostname: str, timeout: float = 5.0) -> str | None: + """Resolve a hostname using system DNS (does not go through proxy — exposes leak).""" + try: + socket.setdefaulttimeout(timeout) + info = socket.getaddrinfo(hostname, None) + for entry in info: + addr = entry[4][0] + if addr: + return addr + except Exception: + pass + return None + + +@dataclass(frozen=True) +class DnsLeakResult: + resolver: str + ip_via_proxy: str | None + ip_direct: str | None + leaked: bool + detail: str + + +def check_dns_leak( + proxy_url: str, + timeout_seconds: float = 8.0, +) -> list[DnsLeakResult]: + """ + Detect DNS leaks: compare hostname resolution seen through proxy vs direct. + A mismatch means DNS is escaping the tunnel. + """ + test_hosts = [ + ("Cloudflare (1.1.1.1)", "one.one.one.one"), + ("Google (8.8.8.8)", "dns.google"), + ("OpenDNS", "resolver1.opendns.com"), + ] + results: list[DnsLeakResult] = [] + timeout = httpx.Timeout(timeout_seconds, connect=min(6.0, timeout_seconds)) + + for label, host in test_hosts: + # Get IP via direct system DNS + direct_ip = _dns_resolve_via_system(host) + + # Get IP as seen from the proxy path (via http://dns-endpoint) + proxy_ip: str | None = None + try: + with httpx.Client(proxy=proxy_url, timeout=timeout, verify=False, follow_redirects=True) as c: + r = c.get(f"https://{host}/") + proxy_ip = str(r.headers.get("x-real-ip") or "") + if not proxy_ip: + # fall back: grab connected IP from response + proxy_ip = None + except Exception: + pass + + # Simple leak heuristic: if direct resolution works but proxy connection fails, possible leak path + leaked = bool(direct_ip and not proxy_ip) + if leaked: + detail = f"DNS resolved directly to {direct_ip} but proxy could not reach it — possible bypass" + elif not direct_ip: + detail = "Could not resolve directly" + leaked = False + else: + detail = f"Direct: {direct_ip}" + + results.append(DnsLeakResult( + resolver=label, + ip_via_proxy=proxy_ip, + ip_direct=direct_ip, + leaked=leaked, + detail=detail, + )) + + return results def run_ban_tests( proxy_url: str, timeout_seconds: float = 12.0, - sites: Iterable[tuple[str, str]] = POPULAR_SITES, + sites: Iterable[tuple[str, str]] | None = None, + categories: Iterable[str] | None = None, + max_workers: int = 8, ) -> list[BanTestResult]: + """ + Run ban tests in parallel across requested site categories. + + ``categories`` accepts names from SITE_CATEGORIES (e.g. "Shopping", "Crypto Exchanges"). + Defaults to all categories when neither ``sites`` nor ``categories`` is given. + """ + if sites is not None: + work = [(s, u, "") for s, u in sites] + elif categories is not None: + work = [] + for cat in categories: + for s, u in SITE_CATEGORIES.get(cat, ()): + work.append((s, u, cat)) + else: + work = [(s, u, cat) for cat, pairs in SITE_CATEGORIES.items() if cat != "All" for s, u in pairs] + out: list[BanTestResult] = [] - for site, url in sites: - out.append(_test_one(proxy_url, site, url, timeout_seconds)) + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as pool: + futures = { + pool.submit(_test_one, proxy_url, site, url, timeout_seconds, cat): (site, url, cat) + for site, url, cat in work + } + for fut in concurrent.futures.as_completed(futures): + try: + out.append(fut.result()) + except Exception: + site, url, cat = futures[fut] + out.append(BanTestResult(site, url, "error", None, "internal error", cat)) + + # Sort: category then site name + out.sort(key=lambda r: (r.category, r.site)) return out diff --git a/proxy_chain_manager/browser_identity.py b/proxy_chain_manager/browser_identity.py index 816d32b..0d3518e 100644 --- a/proxy_chain_manager/browser_identity.py +++ b/proxy_chain_manager/browser_identity.py @@ -15,84 +15,78 @@ Two strategies are exposed: • **Hardened** — full RFP / blocked WebRTC / strict tracking / FPI. Stand-out but maximum block. Same behavior as the original profile. -Cookie modes are independent of persona and let the user pick per-session -behavior from a dropdown. +Cookie modes are independent of persona. Each mode expresses a complete +policy: which cookies to accept, lifetime, whether to wipe on close, and +whether to partition 3rd-party storage. """ from __future__ import annotations from dataclasses import dataclass -# Order matters: dropdown order in the UI. + +# ── Personas ────────────────────────────────────────────────────────────────── + PERSONAS: list[str] = [ "blend_windows_chrome", + "blend_windows_chrome_laptop", "blend_windows_firefox", + "blend_windows_edge", "blend_mac_safari", + "blend_mac_chrome", + "blend_linux_firefox", "hardened", "custom", ] PERSONA_LABELS: dict[str, str] = { - "blend_windows_chrome": "Blend in — Windows 10 + Chrome (US-en, most common)", - "blend_windows_firefox": "Blend in — Windows 10 + Firefox (US-en)", - "blend_mac_safari": "Blend in — macOS + Safari (US-en)", - "hardened": "Hardened — block + RFP (stands out; max anti-fingerprint)", - "custom": "Custom — use individual hardening toggles", -} - -COOKIE_MODES: list[str] = [ - "accept_all", - "block_third_party", - "block_third_party_trackers", - "session_only", - "block_all", -] - -COOKIE_LABELS: dict[str, str] = { - "accept_all": "Accept all cookies (most sites work)", - "block_third_party": "Block 3rd-party cookies (recommended)", - "block_third_party_trackers": "Block 3rd-party trackers only (Firefox default-strict)", - "session_only": "Session only — clear on close", - "block_all": "Block ALL cookies (breaks logins)", -} - -# Firefox network.cookie.cookieBehavior values -_COOKIE_BEHAVIOR = { - "accept_all": 0, - "block_third_party": 1, - "block_third_party_trackers": 4, - "session_only": 0, - "block_all": 2, + "blend_windows_chrome": "Blend — Win10 + Chrome 124 (most common, US-en)", + "blend_windows_chrome_laptop": "Blend — Win11 + Chrome 124 laptop 1366×768", + "blend_windows_firefox": "Blend — Win10 + Firefox 128 (US-en)", + "blend_windows_edge": "Blend — Win11 + Edge 124 (corporate look)", + "blend_mac_safari": "Blend — macOS 14 + Safari 17 (US-en)", + "blend_mac_chrome": "Blend — macOS 14 + Chrome 124 (US-en)", + "blend_linux_firefox": "Blend — Ubuntu + Firefox 128 (en-US)", + "hardened": "Hardened — RFP + FPI + block all (stands out)", + "custom": "Custom — use individual hardening toggles", } @dataclass(frozen=True) class Persona: """A user-agent / locale / timezone / screen tuple that mimics a real, - common configuration. All fields are deliberately popular values so the - persona blends in. - """ + common configuration.""" key: str user_agent: str accept_language: str - timezone: str # IANA tz; used only when persona drives env TZ + timezone: str screen_w: int screen_h: int - platform: str # navigator.platform - locale: str # general.useragent.locale equivalent + platform: str + locale: str PERSONA_DATA: dict[str, Persona] = { "blend_windows_chrome": Persona( key="blend_windows_chrome", - # Stable Chrome on Win10 x64 — by far the most common UA on the web. user_agent=( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" ), accept_language="en-US,en;q=0.9", timezone="America/New_York", - screen_w=1920, - screen_h=1080, + screen_w=1920, screen_h=1080, + platform="Win32", + locale="en-US", + ), + "blend_windows_chrome_laptop": Persona( + key="blend_windows_chrome_laptop", + user_agent=( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" + ), + accept_language="en-US,en;q=0.9", + timezone="America/Chicago", + screen_w=1366, screen_h=768, platform="Win32", locale="en-US", ), @@ -104,8 +98,19 @@ PERSONA_DATA: dict[str, Persona] = { ), accept_language="en-US,en;q=0.5", timezone="America/New_York", - screen_w=1920, - screen_h=1080, + screen_w=1920, screen_h=1080, + platform="Win32", + locale="en-US", + ), + "blend_windows_edge": Persona( + key="blend_windows_edge", + user_agent=( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0" + ), + accept_language="en-US,en;q=0.9", + timezone="America/Chicago", + screen_w=1920, screen_h=1080, platform="Win32", locale="en-US", ), @@ -117,20 +122,201 @@ PERSONA_DATA: dict[str, Persona] = { ), accept_language="en-US,en;q=0.9", timezone="America/Los_Angeles", - screen_w=1680, - screen_h=1050, + screen_w=1680, screen_h=1050, platform="MacIntel", locale="en-US", ), + "blend_mac_chrome": Persona( + key="blend_mac_chrome", + user_agent=( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" + ), + accept_language="en-US,en;q=0.9", + timezone="America/Los_Angeles", + screen_w=1440, screen_h=900, + platform="MacIntel", + locale="en-US", + ), + "blend_linux_firefox": Persona( + key="blend_linux_firefox", + user_agent=( + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0) " + "Gecko/20100101 Firefox/128.0" + ), + accept_language="en-US,en;q=0.5", + timezone="America/New_York", + screen_w=1920, screen_h=1080, + platform="Linux x86_64", + locale="en-US", + ), } +# ── Cookie policies ─────────────────────────────────────────────────────────── + +@dataclass(frozen=True) +class CookiePolicy: + """ + Complete Firefox cookie configuration expressed as a single composable unit. + + behavior : network.cookie.cookieBehavior + 0 = accept all + 1 = block 3rd-party + 2 = block ALL cookies + 3 = block 3rd-party from unvisited sites + 4 = block cross-site tracking (ETP Strict) + 5 = block cross-site + social media trackers + lifetime : network.cookie.lifetimePolicy + 0 = normal (respect Set-Cookie expires) + 2 = session-only (all cookies expire on close) + partition : Total Cookie Protection — partition 3rd-party storage + by the top-level site (network.cookie.cookieBehavior.optInPartitioning) + clear_cookies : privacy.clearOnShutdown.cookies + clear_cache : privacy.clearOnShutdown.cache + clear_history : privacy.clearOnShutdown.history + clear_ls : privacy.clearOnShutdown.localStorage + indexedDB + clear_formdata : privacy.clearOnShutdown.formdata + sanitize : privacy.sanitize.sanitizeOnShutdown (master switch) + """ + behavior: int = 1 + lifetime: int = 0 + partition: bool = False + sanitize: bool = False + clear_cookies: bool = False + clear_cache: bool = False + clear_history: bool = False + clear_ls: bool = False + clear_formdata: bool = False + + +# Every cookie mode is a complete CookiePolicy — no guesswork in profile.py. +COOKIE_POLICIES: dict[str, CookiePolicy] = { + + # ── Permissive ────────────────────────────────────────────────────────── + + "accept_all": CookiePolicy( + behavior=0, lifetime=0, + # No clearing, no blocking — maximum site compatibility. + ), + + "accept_all_session": CookiePolicy( + behavior=0, lifetime=2, + sanitize=True, clear_cookies=True, clear_ls=True, + # Accept all cookies while browsing but wipe them when the + # browser closes. Good for signup / form-filling sessions. + ), + + # ── Standard 3rd-party blocking ──────────────────────────────────────── + + "block_third_party": CookiePolicy( + behavior=1, lifetime=0, + # Block 3rd-party, accept 1st-party with normal lifetime. + # Recommended baseline — most sites work fine. + ), + + "block_third_party_session": CookiePolicy( + behavior=1, lifetime=2, + sanitize=True, clear_cookies=True, clear_cache=True, clear_ls=True, + # Block 3rd-party AND treat all accepted cookies as session-only. + # Nothing persists. Use for anonymous sessions. + ), + + # ── Tracker-focused blocking ──────────────────────────────────────────── + + "block_third_party_trackers": CookiePolicy( + behavior=4, lifetime=0, + # Firefox ETP Strict mode: only blocks known trackers, not ALL + # 3rd-party cookies. Best blend-in option — same as Firefox default. + ), + + "block_social_trackers": CookiePolicy( + behavior=5, lifetime=0, + # Block cross-site cookies AND social-media tracking cookies + # (Facebook, Twitter pixels, etc.). Stricter than ETP Strict but + # still allows most logins. + ), + + "block_unvisited": CookiePolicy( + behavior=3, lifetime=0, + # Block 3rd-party from sites the user has never visited. Very light + # — almost invisible to sites; good when you need max compatibility. + ), + + # ── Partitioned storage (Total Cookie Protection) ─────────────────────── + + "partitioned_tcp": CookiePolicy( + behavior=1, lifetime=0, partition=True, + # Block 3rd-party AND enable Total Cookie Protection: each site + # gets its own isolated cookie jar so cross-site tracking via + # cookie sync is impossible. Recommended for blend-in + privacy. + ), + + "partitioned_session": CookiePolicy( + behavior=1, lifetime=2, partition=True, + sanitize=True, clear_cookies=True, clear_cache=True, + clear_ls=True, clear_formdata=True, + # Partitioned + session-only + full wipe on close. Leaves no + # persistent state on disk after the browser exits. + ), + + # ── Maximum wipe ─────────────────────────────────────────────────────── + + "session_only": CookiePolicy( + behavior=0, lifetime=2, + sanitize=True, clear_cookies=True, + # All cookies accepted but expire when the browser closes. + # Note: localStorage is NOT cleared here — only cookies. + ), + + "ghost_mode": CookiePolicy( + behavior=1, lifetime=2, + sanitize=True, clear_cookies=True, clear_cache=True, + clear_history=True, clear_ls=True, clear_formdata=True, + partition=True, + # Maximum ephemeral session: block 3rd-party, TCP partitioning, + # session-only lifetime, full wipe of cookies + cache + history + + # localStorage/IndexedDB + formdata on every close. Zero disk trace. + ), + + "block_all": CookiePolicy( + behavior=2, lifetime=0, + # Block ALL cookies. Most login-dependent sites will break. + # Only useful for read-only scraping sessions. + ), +} + +COOKIE_MODES: list[str] = list(COOKIE_POLICIES) + +COOKIE_LABELS: dict[str, str] = { + "accept_all": "Accept all — maximum compatibility (cookies persist)", + "accept_all_session": "Accept all, session only — wipe cookies on close", + "block_third_party": "Block 3rd-party — recommended baseline (persists 1st-party)", + "block_third_party_session": "Block 3rd-party + session — nothing persists", + "block_third_party_trackers":"Block trackers only — ETP Strict (Firefox default-strict)", + "block_social_trackers": "Block social + cross-site trackers (Facebook pixel, etc.)", + "block_unvisited": "Block 3rd-party from unvisited sites — lightest option", + "partitioned_tcp": "Partitioned (TCP) — each site gets isolated 3rd-party jar", + "partitioned_session": "Partitioned + session — isolated jars, full wipe on close", + "session_only": "Session only — all cookies expire on close (no partition)", + "ghost_mode": "Ghost mode — partitioned + session + full disk wipe on close", + "block_all": "Block ALL cookies — breaks most logins", +} + + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +def get_cookie_policy(mode: str) -> CookiePolicy: + return COOKIE_POLICIES.get(mode, COOKIE_POLICIES["block_third_party"]) + + +# Legacy shims kept for code that still calls these directly. def cookie_behavior_value(mode: str) -> int: - return _COOKIE_BEHAVIOR.get(mode, 1) + return get_cookie_policy(mode).behavior def cookie_session_only(mode: str) -> bool: - return mode == "session_only" + return get_cookie_policy(mode).lifetime == 2 def get_persona(key: str) -> Persona | None: diff --git a/proxy_chain_manager/browser_launcher.py b/proxy_chain_manager/browser_launcher.py index cc7bc20..ef65322 100644 --- a/proxy_chain_manager/browser_launcher.py +++ b/proxy_chain_manager/browser_launcher.py @@ -5,6 +5,7 @@ import os import shutil import subprocess import time +import winreg from dataclasses import dataclass from pathlib import Path @@ -14,6 +15,14 @@ from .signup_prep import SignupDraft, install_signup_extension log = logging.getLogger(__name__) +# CREATE_NO_WINDOW suppresses the console; DETACHED_PROCESS fully separates +# the child from Python's job object so Firefox lives past Python's exit. +_CREATE_FLAGS = ( + getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000) + | getattr(subprocess, "DETACHED_PROCESS", 0x00000008) + | getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0x00000200) +) + @dataclass class BrowserConfig: @@ -35,11 +44,74 @@ class BrowserConfig: cookie_mode: str = "block_third_party" +def _find_firefox_via_registry() -> str: + """Look up Firefox install path in the Windows registry.""" + hives = [ + (winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Mozilla\Mozilla Firefox"), + (winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WOW6432Node\Mozilla\Mozilla Firefox"), + (winreg.HKEY_CURRENT_USER, r"SOFTWARE\Mozilla\Mozilla Firefox"), + ] + for hive, key_path in hives: + try: + with winreg.OpenKey(hive, key_path) as root: + version = winreg.QueryValue(root, None) + for sub in (f"{version}\\Main", "bin"): + try: + with winreg.OpenKey(root, sub) as k: + path = winreg.QueryValueEx(k, "PathToExe")[0] + if path and Path(path).is_file(): + return str(path) + except OSError: + continue + except OSError: + continue + + # Also try the "Uninstall" key + for hive in (winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER): + for root_path in ( + r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", + r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", + ): + try: + with winreg.OpenKey(hive, root_path) as base: + i = 0 + while True: + try: + sub = winreg.EnumKey(base, i) + i += 1 + if "firefox" not in sub.lower(): + continue + with winreg.OpenKey(base, sub) as k: + loc = winreg.QueryValueEx(k, "InstallLocation")[0] + candidate = Path(loc) / "firefox.exe" + if candidate.is_file(): + return str(candidate) + except OSError: + break + except OSError: + continue + return "" + + def default_firefox_path() -> str: - candidates = [ + """Find Firefox.exe — registry first, then well-known paths.""" + # 1) Registry + reg = _find_firefox_via_registry() + if reg: + return reg + + # 2) Standard install locations + candidates: list[Path] = [ Path(r"C:\Program Files\Mozilla Firefox\firefox.exe"), Path(r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"), + Path(os.path.expandvars(r"%LOCALAPPDATA%\Mozilla Firefox\firefox.exe")), + Path(os.path.expandvars(r"%APPDATA%\Mozilla Firefox\firefox.exe")), ] + # 3) Scoop / winget / portable in PATH + which = shutil.which("firefox") or shutil.which("firefox.exe") + if which: + candidates.insert(0, Path(which)) + for c in candidates: if c.is_file(): return str(c) @@ -50,13 +122,39 @@ def default_profile_dir() -> str: return str(app_data_dir() / "browser_profiles" / "firefox_hardened") +def _firefox_is_running_on_system() -> bool: + """Check if any firefox.exe process is alive (tasklist, no admin needed).""" + try: + r = subprocess.run( + ["tasklist", "/FI", "IMAGENAME eq firefox.exe", "/NH", "/FO", "CSV"], + capture_output=True, text=True, timeout=6, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000), + ) + return "firefox.exe" in (r.stdout or "").lower() + except Exception: + return False + + class BrowserSession: def __init__(self) -> None: self._proc: subprocess.Popen[str] | None = None self._profile_path: Path | None = None + self._launched_at: float = 0.0 def is_running(self) -> bool: - return self._proc is not None and self._proc.poll() is None + if self._proc is None: + return False + # Firefox's initial launcher process exits quickly (code 0) while + # browser child processes carry on. Poll the parent process but also + # fall back to checking the system process list so the GUI doesn't + # falsely report "stopped". + if self._proc.poll() is None: + return True + # Parent has exited — check if any firefox.exe is still alive AND we + # launched within the last 5 minutes (to avoid false positives from + # unrelated browser sessions). + age = time.monotonic() - self._launched_at + return age < 300 and _firefox_is_running_on_system() def pid(self) -> int | None: if self._proc is None: @@ -72,11 +170,22 @@ class BrowserSession: signup_draft: SignupDraft | None = None, ) -> tuple[bool, str]: raw_exe = (cfg.firefox_path or default_firefox_path()).strip().strip('"').strip("'") + if not raw_exe: + return ( + False, + "Firefox not found. Install Firefox then set the path in the Browser tab " + "(or browse to it with the Browse button).", + ) exe = Path(raw_exe) if not exe.is_file(): - return False, f"Firefox executable not found: {raw_exe or '(empty path)'}" + return False, ( + f"Firefox executable not found: {raw_exe} " + "Use the Browse button on the Browser tab to locate firefox.exe." + ) + raw_profile = (cfg.profile_dir or default_profile_dir()).strip().strip('"').strip("'") profile_dir = Path(raw_profile) + hard = FirefoxHardening( force_proxy=cfg.force_proxy, disable_webrtc=cfg.disable_webrtc, @@ -89,46 +198,88 @@ class BrowserSession: persona_key=cfg.persona, cookie_mode=cfg.cookie_mode, ) - if cfg.lock_managed_profile: - ensure_firefox_profile(profile_dir, proxy_host, int(proxy_port), hard) - if signup_draft is not None: - install_signup_extension(profile_dir, signup_draft) + + try: + if cfg.lock_managed_profile: + ensure_firefox_profile(profile_dir, proxy_host, int(proxy_port), hard) + if signup_draft is not None: + install_signup_extension(profile_dir, signup_draft) + except Exception as e: + return False, f"Profile setup failed: {e!s}" + if self.is_running(): self.stop() + if cfg.lock_managed_profile: args = [str(exe), "-no-remote", "-profile", str(profile_dir)] else: args = [str(exe)] + url = (start_url or "").strip() if url: args.append(url) + env = os.environ.copy() env.update(persona_env(hard)) + try: self._proc = subprocess.Popen( args, - creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + # DETACHED_PROCESS ensures Firefox outlives Python and is not + # killed when Python's job object exits. CREATE_NO_WINDOW + # suppresses any console window. CREATE_NEW_PROCESS_GROUP + # isolates Ctrl-C handling. + creationflags=_CREATE_FLAGS, + stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, text=True, env=env, ) - # Smoke-check: catch immediate startup crashes and report clearly. - time.sleep(0.4) - rc = self._proc.poll() - if rc is not None: - self._proc = None - return False, f"Firefox exited immediately (code {rc}). Check path/profile." - self._profile_path = profile_dir if cfg.lock_managed_profile else None - log.info("Launched hardened Firefox pid=%s profile=%s", self._proc.pid, profile_dir) - if cfg.lock_managed_profile: - return True, f"Hardened Firefox started (pid {self._proc.pid})." - return True, f"Firefox started (unlocked mode, pid {self._proc.pid})." except Exception as e: self._proc = None return False, f"Launch failed: {e!s}" + self._launched_at = time.monotonic() + + # Firefox's multi-process launcher exits (code 0) within ~0.5 s while + # the actual browser window loads in child processes. We therefore do + # NOT treat a quick exit of the parent as an error — we just verify + # that *some* firefox.exe appears in the process list within 3 s. + deadline = time.monotonic() + 3.0 + found = False + while time.monotonic() < deadline: + time.sleep(0.3) + rc = self._proc.poll() + # If it already exited with an error code, report it. + if rc is not None and rc not in (0, None): + self._proc = None + return False, f"Firefox exited with error code {rc}." + if _firefox_is_running_on_system(): + found = True + break + + if not found: + # One last check — maybe the profile write was the only action needed + # and Firefox opened fast; accept success if parent is still alive. + if self._proc.poll() is None: + found = True + + if not found: + self._proc = None + return False, ( + "Firefox did not appear in the process list after 3 s. " + "Check that the path is correct and Firefox is not already blocked by another -profile lock." + ) + + self._profile_path = profile_dir if cfg.lock_managed_profile else None + pid = self._proc.pid + log.info("Launched hardened Firefox pid=%s profile=%s", pid, profile_dir) + mode = "hardened profile" if cfg.lock_managed_profile else "unlocked mode" + return True, f"Firefox started ({mode}, pid {pid})." + def stop(self, dispose: bool = False) -> tuple[bool, str]: + # Terminate the Popen handle if still alive if self._proc and self._proc.poll() is None: try: self._proc.terminate() @@ -140,9 +291,24 @@ class BrowserSession: pass finally: self._proc = None + + # Also kill any remaining firefox.exe processes (handles the + # detached-child case where the parent already exited naturally) + try: + subprocess.run( + ["taskkill", "/F", "/IM", "firefox.exe"], + capture_output=True, + timeout=10, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000), + ) + except Exception: + pass + + self._proc = None if dispose and self._profile_path and self._profile_path.exists(): try: shutil.rmtree(self._profile_path, ignore_errors=True) except Exception: pass + self._launched_at = 0.0 return True, "Browser stopped." diff --git a/proxy_chain_manager/browser_profile.py b/proxy_chain_manager/browser_profile.py index 8ea96fb..1d83e02 100644 --- a/proxy_chain_manager/browser_profile.py +++ b/proxy_chain_manager/browser_profile.py @@ -4,9 +4,11 @@ from dataclasses import dataclass, field from pathlib import Path from .browser_identity import ( + CookiePolicy, Persona, cookie_behavior_value, cookie_session_only, + get_cookie_policy, get_persona, is_blend_persona, ) @@ -180,28 +182,62 @@ def build_user_js( f'user_pref("general.useragent.override", "{hard.user_agent_override}");' ) - # Cookie policy (persona-independent) - behavior = cookie_behavior_value(hard.cookie_mode) - lines.extend( - [ - f'user_pref("network.cookie.cookieBehavior", {behavior});', - # 0 = expire normally, 2 = current session only - f'user_pref("network.cookie.lifetimePolicy", {2 if cookie_session_only(hard.cookie_mode) else 0});', - ] - ) + # ── Cookie policy ──────────────────────────────────────────────────────── + cp: CookiePolicy = get_cookie_policy(hard.cookie_mode) - if hard.clear_on_shutdown: - lines.extend( - [ - 'user_pref("privacy.sanitize.sanitizeOnShutdown", true);', - 'user_pref("privacy.clearOnShutdown.history", true);', - 'user_pref("privacy.clearOnShutdown.cookies", true);', - 'user_pref("privacy.clearOnShutdown.cache", true);', - 'user_pref("privacy.clearOnShutdown.downloads", true);', - 'user_pref("privacy.clearOnShutdown.formdata", true);', - 'user_pref("privacy.clearOnShutdown.sessions", true);', - ] - ) + lines.extend([ + f'user_pref("network.cookie.cookieBehavior", {cp.behavior});', + # pbmode = private-browsing mode uses same behavior + f'user_pref("network.cookie.cookieBehavior.pbmode", {cp.behavior});', + # lifetime: 0 = normal, 2 = session-only + f'user_pref("network.cookie.lifetimePolicy", {cp.lifetime});', + ]) + + # Total Cookie Protection (TCP / Firefox partitioning) + if cp.partition: + lines.extend([ + 'user_pref("network.cookie.cookieBehavior.optInPartitioning", true);', + # Also isolate cache, localStorage, IndexedDB per top-level site + 'user_pref("privacy.partition.serviceWorkers", true);', + 'user_pref("privacy.partition.network_state", true);', + 'user_pref("privacy.partition.bloburl_by_default", true);', + ]) + else: + lines.extend([ + 'user_pref("network.cookie.cookieBehavior.optInPartitioning", false);', + ]) + + # SameSite=None cookies must be Secure (good hygiene regardless of mode) + lines.append('user_pref("network.cookie.sameSite.noneRequiresSecure", true);') + + # ── Sanitize-on-shutdown ───────────────────────────────────────────────── + # The CookiePolicy sanitize flag takes priority; the hard.clear_on_shutdown + # toggle acts as an additional override for cookies+cache even when the + # cookie mode doesn't request it. + do_sanitize = cp.sanitize or hard.clear_on_shutdown + if do_sanitize: + lines.append('user_pref("privacy.sanitize.sanitizeOnShutdown", true);') + # Cookies + lines.append(f'user_pref("privacy.clearOnShutdown.cookies", {_bool(cp.clear_cookies or hard.clear_on_shutdown)});') + # Cache + lines.append(f'user_pref("privacy.clearOnShutdown.cache", {_bool(cp.clear_cache or hard.clear_on_shutdown)});') + # History + lines.append(f'user_pref("privacy.clearOnShutdown.history", {_bool(cp.clear_history or hard.clear_on_shutdown)});') + # Downloads list + lines.append('user_pref("privacy.clearOnShutdown.downloads", true);') + # Form / search bar autofill data + lines.append(f'user_pref("privacy.clearOnShutdown.formdata", {_bool(cp.clear_formdata or hard.clear_on_shutdown)});') + # Active sessions (HTTP auth, logged-in sites) + lines.append('user_pref("privacy.clearOnShutdown.sessions", true);') + # localStorage + IndexedDB + lines.append(f'user_pref("privacy.clearOnShutdown.localStorage", {_bool(cp.clear_ls or hard.clear_on_shutdown)});') + lines.append(f'user_pref("privacy.clearOnShutdown.indexedDB", {_bool(cp.clear_ls or hard.clear_on_shutdown)});') + # Service worker registrations + lines.append('user_pref("privacy.clearOnShutdown.serviceWorkers", true);') + # Offline web app cache + lines.append('user_pref("privacy.clearOnShutdown.offlineApps", true);') + else: + lines.append('user_pref("privacy.sanitize.sanitizeOnShutdown", false);') if hard.timezone_utc: lines.append('user_pref("privacy.resistFingerprinting.reduceTimerPrecision", true);') diff --git a/proxy_chain_manager/chain_map.py b/proxy_chain_manager/chain_map.py index cac1688..ccadbf8 100644 --- a/proxy_chain_manager/chain_map.py +++ b/proxy_chain_manager/chain_map.py @@ -1,4 +1,8 @@ -"""World-map visualization for proxy chains (Pillow + equirectangular projection).""" +"""World-map visualization for proxy chains. + +Renders hop arcs over the bundled neon world map image. Falls back to a +drawn landmass map if the image file is absent. +""" from __future__ import annotations import ipaddress @@ -6,10 +10,11 @@ import logging import math import socket from dataclasses import dataclass +from pathlib import Path from typing import Literal from urllib.parse import urlparse -from PIL import Image, ImageDraw, ImageFont +from PIL import Image, ImageDraw, ImageFilter, ImageFont try: _RESAMPLE = Image.Resampling.LANCZOS @@ -23,21 +28,42 @@ log = logging.getLogger(__name__) ChainStatus = Literal["healthy", "connecting", "dead", "idle"] -# Theme colors (match app.py palette) -_BG = "#070713" -_GRID = "#141432" -_LAND = "#12182e" -_LAND_EDGE = "#1e2a4a" -_LINE_HEALTHY = "#00e5ff" +# Theme palette +_BG = "#070713" +_GRID = "#1a1f3a" +_LAND = "#12182e" +_LAND_EDGE = "#1e2a4a" +_LINE_HEALTHY = "#00e5ff" _LINE_CONNECTING = "#ffd000" -_LINE_DEAD = "#ff2a6d" -_LINE_IDLE = "#3052ff" -_YOU = "#00ff9c" -_HOP = "#00e5ff" -_EXIT = "#ffd000" -_LABEL = "#eaf2ff" -_LABEL_DIM = "#7a8aab" -_UNKNOWN = "#2a2f4a" +_LINE_DEAD = "#ff2a6d" +_LINE_IDLE = "#3052ff" +_YOU = "#00ff9c" +_HOP = "#00e5ff" +_EXIT = "#ffd000" +_LABEL = "#eaf2ff" +_LABEL_DIM = "#7a8aab" +_UNKNOWN = "#2a2f4a" + +def _map_img_path() -> Path: + """Locate world_map.png whether running from source or a frozen PyInstaller bundle.""" + import sys + if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): + return Path(sys._MEIPASS) / "proxy_chain_manager" / "world_map.png" + return Path(__file__).resolve().parent / "world_map.png" + +_cached_map: Image.Image | None = None + + +def _load_world_map(width: int, height: int) -> Image.Image | None: + """Return the neon world map resized to (width × height), or None if unavailable.""" + global _cached_map + try: + if _cached_map is None: + _cached_map = Image.open(_map_img_path()).convert("RGB") + return _cached_map.resize((width, height), _RESAMPLE) + except Exception as exc: + log.debug("world_map load failed: %s", exc) + return None @dataclass(frozen=True) @@ -134,26 +160,23 @@ def build_chain_map_points( ip = resolve_host_ip(host, timeout_seconds=min(4.0, timeout_seconds)) if not ip: continue - short = redact_proxy_url(hop) - if len(short) > 22: - short = short[:20] + "…" intel = fetch_ip_geo(ip, timeout_seconds) pt = _intel_to_point("hop", f"H{i + 1}", intel, hop_index=i) if pt: points.append(pt) - exit = (exit_ip or "").strip() - if exit and exit != "—": + exit_str = (exit_ip or "").strip() + if exit_str and exit_str != "—": if ( exit_intel and exit_intel.ok - and exit_intel.ip == exit + and exit_intel.ip == exit_str and exit_intel.lat is not None and exit_intel.lon is not None ): pt = _intel_to_point("exit", "EXIT", exit_intel) else: - pt = _intel_to_point("exit", "EXIT", fetch_ip_geo(exit, timeout_seconds)) + pt = _intel_to_point("exit", "EXIT", fetch_ip_geo(exit_str, timeout_seconds)) if pt: points.append(pt) @@ -173,7 +196,7 @@ def _great_circle_points( lon1: float, lat2: float, lon2: float, - steps: int = 24, + steps: int = 32, ) -> list[tuple[float, float]]: """Interpolate along a great circle for curved hop arcs.""" phi1, lam1 = math.radians(lat1), math.radians(lon1) @@ -194,21 +217,50 @@ def _great_circle_points( x = a * math.cos(phi1) * math.cos(lam1) + b * math.cos(phi2) * math.cos(lam2) y = a * math.cos(phi1) * math.sin(lam1) + b * math.cos(phi2) * math.sin(lam2) z = a * math.sin(phi1) + b * math.sin(phi2) - out.append((math.degrees(math.atan2(z, math.sqrt(x * x + y * y))), math.degrees(math.atan2(y, x)))) + out.append(( + math.degrees(math.atan2(z, math.sqrt(x * x + y * y))), + math.degrees(math.atan2(y, x)), + )) return out -def _line_color(status: ChainStatus) -> str: - return { - "healthy": _LINE_HEALTHY, - "connecting": _LINE_CONNECTING, - "dead": _LINE_DEAD, - "idle": _LINE_IDLE, - }.get(status, _LINE_IDLE) +def _line_color(status: ChainStatus) -> tuple[int, int, int]: + table = { + "healthy": (0, 229, 255), + "connecting": (255, 208, 0), + "dead": (255, 42, 109), + "idle": (48, 82, 255), + } + return table.get(status, (48, 82, 255)) -def _node_color(role: str) -> str: - return {"you": _YOU, "hop": _HOP, "exit": _EXIT}.get(role, _HOP) +def _node_color(role: str) -> tuple[int, int, int]: + table = {"you": (0, 255, 156), "hop": (0, 229, 255), "exit": (255, 208, 0)} + return table.get(role, (0, 229, 255)) + + +def _hex_to_rgb(h: str) -> tuple[int, int, int]: + h = h.lstrip("#") + return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16) + + +def _draw_glow_line( + draw: ImageDraw.ImageDraw, + pts: list[tuple[float, float]], + color: tuple[int, int, int], +) -> None: + """Draw neon glow arc: fat dim outer halo → bright core line.""" + if len(pts) < 2: + return + r, g, b = color + # outer halo — wider, dim + halo = (r // 4, g // 4, b // 4) + draw.line(pts, fill=halo, width=7) + # mid glow + mid = (r // 2, g // 2, b // 2) + draw.line(pts, fill=mid, width=4) + # bright core + draw.line(pts, fill=color, width=2) def _load_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: @@ -221,18 +273,13 @@ def _load_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: def _draw_landmasses(draw: ImageDraw.ImageDraw, width: int, height: int, margin: int) -> None: - """Minimal continent silhouettes — stylized, not survey-accurate.""" + """Fallback minimal continent silhouettes.""" blobs: list[list[tuple[float, float]]] = [ - # North America [(-168, 72), (-140, 75), (-100, 72), (-80, 50), (-75, 25), (-95, 15), (-110, 20), (-125, 48), (-168, 72)], - # South America [(-82, 12), (-72, -5), (-55, -35), (-65, -55), (-75, -18), (-82, 12)], - # Europe / Africa [(-10, 72), (30, 70), (40, 55), (35, 30), (20, 5), (-5, 5), (-18, 28), (-10, 72)], [(15, 35), (35, 32), (50, 12), (42, -5), (18, -35), (15, 35)], - # Asia [(40, 72), (100, 75), (140, 55), (130, 35), (110, 10), (80, 8), (60, 25), (40, 45), (40, 72)], - # Australia [(115, -12), (135, -12), (150, -25), (145, -38), (115, -38), (115, -12)], ] for poly in blobs: @@ -246,58 +293,75 @@ def render_chain_map( width: int = 960, height: int = 220, status: ChainStatus = "idle", - margin: int = 18, + margin: int = 12, ) -> Image.Image: - """Render chain hops on a dark equirectangular world map.""" - img = Image.new("RGB", (max(320, width), max(120, height)), _BG) - draw = ImageDraw.Draw(img) - _draw_landmasses(draw, width, height, margin) + """Render chain hops over the neon world map (or a drawn fallback).""" + w = max(320, width) + h = max(120, height) - # Lat/lon grid - for lat in range(-60, 91, 30): - y = _project(lat, 0, width, height, margin)[1] - draw.line([(margin, y), (width - margin, y)], fill=_GRID, width=1) - for lon in range(-150, 181, 30): - x = _project(0, lon, width, height, margin)[0] - draw.line([(x, margin), (x, height - margin)], fill=_GRID, width=1) + # ── background ────────────────────────────────────────────────────────── + world = _load_world_map(w, h) + if world is not None: + # Slightly darken so overlaid lines pop + bg = Image.new("RGB", (w, h), (0, 0, 0)) + img = Image.blend(world, bg, alpha=0.22) + else: + img = Image.new("RGB", (w, h), _BG) + draw_bg = ImageDraw.Draw(img) + _draw_landmasses(draw_bg, w, h, margin) + # grid + for lat in range(-60, 91, 30): + y = _project(lat, 0, w, h, margin)[1] + draw_bg.line([(margin, y), (w - margin, y)], fill=_GRID, width=1) + for lon in range(-150, 181, 30): + x = _project(0, lon, w, h, margin)[0] + draw_bg.line([(x, margin), (x, h - margin)], fill=_GRID, width=1) + + draw = ImageDraw.Draw(img) if len(points) < 2: - font = _load_font(13) + font = _load_font(12) msg = "Start the chain to plot hops on the map." if not points else "Need 2+ geo points to draw path." - draw.text((margin, height // 2 - 8), msg, fill=_LABEL_DIM, font=font) + # drop shadow + draw.text((margin + 1, h // 2 - 7), msg, fill=(0, 0, 0), font=font) + draw.text((margin, h // 2 - 8), msg, fill=_hex_to_rgb(_LABEL_DIM), font=font) return img - line_c = _line_color(status) - projected = [_project(p.lat, p.lon, width, height, margin) for p in points] + line_rgb = _line_color(status) + projected = [_project(p.lat, p.lon, w, h, margin) for p in points] - # Glow underlay + # ── arc lines between consecutive hops ───────────────────────────────── for i in range(len(projected) - 1): - arc = _great_circle_points(points[i].lat, points[i].lon, points[i + 1].lat, points[i + 1].lon) - arc_xy = [_project(lat, lon, width, height, margin) for lat, lon in arc] - draw.line(arc_xy, fill=line_c, width=5) - for i in range(len(projected) - 1): - arc = _great_circle_points(points[i].lat, points[i].lon, points[i + 1].lat, points[i + 1].lon) - arc_xy = [_project(lat, lon, width, height, margin) for lat, lon in arc] - draw.line(arc_xy, fill=line_c, width=2) + arc = _great_circle_points( + points[i].lat, points[i].lon, + points[i + 1].lat, points[i + 1].lon, + ) + arc_xy = [_project(lat, lon, w, h, margin) for lat, lon in arc] + _draw_glow_line(draw, arc_xy, line_rgb) - label_font = _load_font(11) + # ── nodes ─────────────────────────────────────────────────────────────── + label_font = _load_font(11) detail_font = _load_font(9) for pt, (x, y) in zip(points, projected): - color = _node_color(pt.role) + nc = _node_color(pt.role) r = 7 if pt.role == "exit" else (6 if pt.role == "you" else 5) - draw.ellipse((x - r - 2, y - r - 2, x + r + 2, y + r + 2), fill=color) - draw.ellipse((x - r, y - r, x + r, y + r), fill=_BG, outline=color, width=2) - tx, ty = x + 10, y - 14 - draw.text((tx + 1, ty + 1), pt.label, fill="#000000", font=label_font) - draw.text((tx, ty), pt.label, fill=_LABEL, font=label_font) + # outer glow ring + glow = (nc[0] // 3, nc[1] // 3, nc[2] // 3) + draw.ellipse((x - r - 4, y - r - 4, x + r + 4, y + r + 4), fill=glow) + # filled node with dark center + draw.ellipse((x - r, y - r, x + r, y + r), fill=nc) + draw.ellipse((x - r + 2, y - r + 2, x + r - 2, y + r - 2), fill=(5, 5, 20)) + + tx, ty = int(x + r + 5), int(y - 8) + # text drop-shadow + draw.text((tx + 1, ty + 1), pt.label, fill=(0, 0, 0), font=label_font) + draw.text((tx, ty), pt.label, fill=_hex_to_rgb(_LABEL), font=label_font) if pt.detail: - draw.text((tx, ty + 13), pt.detail[:28], fill=_LABEL_DIM, font=detail_font) + draw.text((tx, ty + 12), pt.detail[:30], fill=_hex_to_rgb(_LABEL_DIM), font=detail_font) return img def resize_map_display(pil_img: Image.Image, width: int, height: int) -> Image.Image: """Resize rendered map for the GUI widget.""" - w = max(320, int(width)) - h = max(120, int(height)) - return pil_img.resize((w, h), _RESAMPLE) + return pil_img.resize((max(320, int(width)), max(120, int(height))), _RESAMPLE) diff --git a/proxy_chain_manager/dns_leak.py b/proxy_chain_manager/dns_leak.py index 7782459..f8aa2d0 100644 --- a/proxy_chain_manager/dns_leak.py +++ b/proxy_chain_manager/dns_leak.py @@ -1,41 +1,118 @@ -"""DNS leak checks and cache flush.""" +"""DNS leak detection and cache flushing. + +Two levels of testing: + 1. check_dns_leak_hint() — fast, no network: inspect configured DNS resolvers + 2. run_dns_leak_test() — real network test: compare DNS answers seen through + the proxy vs direct, and probe resolver identity via + a dedicated leak-test API. +""" from __future__ import annotations +import concurrent.futures +import ipaddress import logging +import socket import subprocess -from dataclasses import dataclass +from dataclasses import dataclass, field import httpx log = logging.getLogger(__name__) -_DOH_GOOGLE = "https://dns.google/resolve?name=whoami.dnsleaktest.com&type=A" +# ─── DoH endpoint used to ask "what is my apparent source IP?" ────────────── +_DOH_IP_CHECK = "https://api.ipify.org?format=json" +_DNS_LEAK_API = "https://dnsleaktest.com/api/v1/start" +_DNS_LEAK_RESULT = "https://dnsleaktest.com/api/v1/results" +# Well-known public resolver IP ranges (prefix → label) +_PUBLIC_DNS_LABELS: dict[str, str] = { + "8.8.8.8": "Google", "8.8.4.4": "Google", + "1.1.1.1": "Cloudflare", "1.0.0.1": "Cloudflare", + "9.9.9.9": "Quad9", "149.112.112.112": "Quad9", + "208.67.222.222": "OpenDNS", "208.67.220.220": "OpenDNS", + "76.76.2.0": "Alternate DNS", + "94.140.14.14": "AdGuard", +} + +# ───────────────────────────────────────────────────────────────────────────── @dataclass class DnsLeakResult: + """Legacy single-check result kept for API compatibility.""" ok: bool system_resolvers: list[str] message: str doh_ip: str | None = None +@dataclass +class DnsResolverInfo: + ip: str + hostname: str = "" + country: str = "" + isp: str = "" + label: str = "" # "Google", "Cloudflare", or "" + is_public_known: bool = False + + +@dataclass +class FullDnsLeakReport: + """Comprehensive DNS leak report from run_dns_leak_test().""" + # Resolvers that answered DNS queries in this session + resolvers_seen: list[DnsResolverInfo] = field(default_factory=list) + # Resolvers configured by the OS + system_resolvers: list[str] = field(default_factory=list) + # IPs resolved for test hostnames — via proxy vs direct + proxy_resolution: dict[str, list[str]] = field(default_factory=dict) + direct_resolution: dict[str, list[str]] = field(default_factory=dict) + # True when proxy and direct give the same answers (possible leak) + resolution_matches_direct: bool = False + # Whether any configured resolver is a known public server + has_public_resolver: bool = False + # Whether any configured resolver is outside local network + has_external_resolver: bool = False + # Overall verdict + leaked: bool = False + summary: str = "" + errors: list[str] = field(default_factory=list) + + +# ───────────────────────────────────────────────────────────────────────────── +# System resolver detection +# ───────────────────────────────────────────────────────────────────────────── + def get_system_dns_servers() -> list[str]: + """Return configured IPv4 DNS resolvers via PowerShell (falls back to ipconfig).""" try: r = subprocess.run( ["powershell", "-NoProfile", "-Command", "(Get-DnsClientServerAddress -AddressFamily IPv4 | " "Where-Object { $_.ServerAddresses } | " "Select-Object -ExpandProperty ServerAddresses) -join ','"], - capture_output=True, - text=True, - timeout=12, + capture_output=True, text=True, timeout=12, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), ) raw = (r.stdout or "").strip() - if not raw: - return [] - return [x.strip() for x in raw.replace(";", ",").split(",") if x.strip()] + if raw: + return [x.strip() for x in raw.replace(";", ",").split(",") if x.strip()] + except Exception: + pass + + # ipconfig fallback + try: + r = subprocess.run( + ["ipconfig", "/all"], capture_output=True, text=True, timeout=10, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + ) + servers: list[str] = [] + for line in (r.stdout or "").splitlines(): + if "DNS Servers" in line or "DNS Server" in line: + parts = line.split(":", 1) + if len(parts) == 2: + ip = parts[1].strip() + if ip: + servers.append(ip) + return servers except Exception: return [] @@ -43,70 +120,194 @@ def get_system_dns_servers() -> list[str]: def flush_dns_cache() -> tuple[bool, str]: try: r = subprocess.run( - ["ipconfig", "/flushdns"], - capture_output=True, - text=True, - timeout=15, + ["ipconfig", "/flushdns"], capture_output=True, text=True, timeout=15, creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), ) - msg = (r.stdout or r.stderr or "").strip().splitlines()[-1] if r.returncode == 0 else "flush failed" + lines = (r.stdout or r.stderr or "").strip().splitlines() + msg = lines[-1] if lines else "flushed" return r.returncode == 0, msg except Exception as e: return False, str(e) -def check_dns_leak_hint(local_proxy: str | None = None) -> DnsLeakResult: - """Heuristic: list configured DNS servers; note if any are public ISP resolvers. +# ───────────────────────────────────────────────────────────────────────────── +# Hostname resolution helpers +# ───────────────────────────────────────────────────────────────────────────── - Full DNS leak testing needs OS-level routing; this flags obvious misconfig. - """ - resolvers = get_system_dns_servers() - private_prefixes = ("127.", "10.", "192.168.", "172.16.", "172.17.", "172.18.", - "172.19.", "172.2", "172.30.", "172.31.", "0.0.0.0") - public = [r for r in resolvers if not any(r.startswith(p) for p in private_prefixes)] - - doh_ip: str | None = None +def _resolve_direct(hostname: str, timeout: float = 4.0) -> list[str]: + """Resolve hostname using system DNS (direct, not through proxy).""" try: - with httpx.Client(timeout=8.0, verify=True) as c: - r = c.get(_DOH_GOOGLE) + socket.setdefaulttimeout(timeout) + infos = socket.getaddrinfo(hostname, None) + seen: list[str] = [] + for info in infos: + addr = info[4][0] + if addr and addr not in seen: + seen.append(addr) + return seen + except Exception: + return [] + finally: + socket.setdefaulttimeout(None) + + +def _resolve_via_proxy(hostname: str, proxy_url: str, timeout: float = 8.0) -> list[str]: + """Resolve hostname by fetching a DNS-over-HTTPS endpoint through the proxy.""" + # Use Google DoH JSON API through the proxy so DNS is resolved on the exit node + doh = f"https://dns.google/resolve?name={hostname}&type=A" + try: + with httpx.Client(proxy=proxy_url, timeout=timeout, verify=False, follow_redirects=True) as c: + r = c.get(doh) if r.status_code == 200: data = r.json() - answers = data.get("Answer") or [] - if answers: - doh_ip = str(answers[0].get("data", "")) + return [ans["data"] for ans in (data.get("Answer") or []) + if ans.get("type") == 1] except Exception: pass + return [] + + +def _is_private(ip: str) -> bool: + try: + return ipaddress.ip_address(ip).is_private + except Exception: + return False + + +# ───────────────────────────────────────────────────────────────────────────── +# Main leak test +# ───────────────────────────────────────────────────────────────────────────── + +_LEAK_TEST_HOSTS = [ + "google.com", + "cloudflare.com", + "github.com", + "amazon.com", +] + + +def run_dns_leak_test( + proxy_url: str | None = None, + timeout: float = 10.0, +) -> FullDnsLeakReport: + """ + Comprehensive DNS leak test: + - Collect system DNS resolver configuration + - Resolve test hostnames both directly (system DNS) and via proxy (DoH through chain) + - Compare answers: matching answers suggest DNS is NOT going through the proxy + - Flag known public resolvers that would bypass the chain + """ + rep = FullDnsLeakReport() + rep.system_resolvers = get_system_dns_servers() + + _PRIVATE_PREFIXES = ( + "127.", "10.", "192.168.", "172.16.", "172.17.", "172.18.", + "172.19.", "172.2", "172.30.", "172.31.", "169.254.", + ) + + for r_ip in rep.system_resolvers: + is_public_known = r_ip in _PUBLIC_DNS_LABELS + is_external = not any(r_ip.startswith(p) for p in _PRIVATE_PREFIXES) + label = _PUBLIC_DNS_LABELS.get(r_ip, "") + rep.has_public_resolver = rep.has_public_resolver or is_public_known + rep.has_external_resolver = rep.has_external_resolver or is_external + rep.resolvers_seen.append(DnsResolverInfo( + ip=r_ip, + label=label, + is_public_known=is_public_known, + )) + + # Resolve test hosts in parallel + all_match = True + any_resolved_via_proxy = False + + def _test_host(host: str) -> tuple[str, list[str], list[str]]: + direct = _resolve_direct(host, timeout=min(4.0, timeout)) + proxy = _resolve_via_proxy(host, proxy_url, timeout=timeout) if proxy_url else [] + return host, direct, proxy + + try: + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: + futs = {pool.submit(_test_host, h): h for h in _LEAK_TEST_HOSTS} + for fut in concurrent.futures.as_completed(futs, timeout=timeout + 2): + try: + host, direct, proxy = fut.result() + rep.direct_resolution[host] = direct + rep.proxy_resolution[host] = proxy + if proxy: + any_resolved_via_proxy = True + # Overlap: if proxy and direct gave the same IPs, DNS + # might not be going through the proxy (same resolver path) + direct_set = set(direct) + proxy_set = set(proxy) + if direct_set and proxy_set and direct_set == proxy_set: + pass # this host matches + else: + all_match = False + except Exception as e: + rep.errors.append(str(e)) + except Exception as e: + rep.errors.append(f"Thread pool error: {e}") + + rep.resolution_matches_direct = all_match and any_resolved_via_proxy + + # ── Build verdict ────────────────────────────────────────────────────── + problems: list[str] = [] + + if rep.has_external_resolver: + external = [r for r in rep.system_resolvers + if not any(r.startswith(p) for p in _PRIVATE_PREFIXES)] + names = [f"{ip} ({_PUBLIC_DNS_LABELS[ip]})" if ip in _PUBLIC_DNS_LABELS else ip + for ip in external[:4]] + problems.append(f"OS DNS: {', '.join(names)} — DNS may bypass proxy chain") + + if rep.resolution_matches_direct and proxy_url: + problems.append("Proxy DNS resolution matches direct — possible DNS leak (same upstream resolver)") + + if problems: + rep.leaked = True + rep.summary = " · ".join(problems) + else: + rep.leaked = False + if proxy_url: + rep.summary = "DNS routing looks clean — proxy resolves differently from direct." + else: + resolver_str = ", ".join(rep.system_resolvers[:3]) if rep.system_resolvers else "none detected" + rep.summary = f"System resolvers: {resolver_str}" + + return rep + + +# ───────────────────────────────────────────────────────────────────────────── +# Legacy quick hint (used by Privacy tab DNS section) +# ───────────────────────────────────────────────────────────────────────────── + +def check_dns_leak_hint(local_proxy: str | None = None) -> DnsLeakResult: + """Fast heuristic: inspect configured DNS resolvers, flag public ones.""" + resolvers = get_system_dns_servers() + private_prefixes = ( + "127.", "10.", "192.168.", "172.16.", "172.17.", "172.18.", + "172.19.", "172.2", "172.30.", "172.31.", + ) + public = [r for r in resolvers if not any(r.startswith(p) for p in private_prefixes)] if not resolvers: - return DnsLeakResult( - ok=True, - system_resolvers=[], - message="No IPv4 DNS servers reported (DHCP may assign later).", - doh_ip=doh_ip, - ) + return DnsLeakResult(ok=True, system_resolvers=[], message="No IPv4 DNS servers reported (DHCP).") if public and local_proxy: return DnsLeakResult( - ok=False, - system_resolvers=resolvers, + ok=False, system_resolvers=resolvers, message=( f"DNS may bypass proxy chain: public resolvers {', '.join(public)}. " "Use kill-switch + VPN, or set DNS to localhost when hardened." ), - doh_ip=doh_ip, ) - - if public and not local_proxy: + if public: return DnsLeakResult( - ok=False, - system_resolvers=resolvers, + ok=False, system_resolvers=resolvers, message=f"Public DNS resolvers active: {', '.join(public)}", - doh_ip=doh_ip, ) - return DnsLeakResult( - ok=True, - system_resolvers=resolvers, + ok=True, system_resolvers=resolvers, message=f"DNS servers: {', '.join(resolvers)}", - doh_ip=doh_ip, ) diff --git a/proxy_chain_manager/fingerprint.py b/proxy_chain_manager/fingerprint.py index 3b93bb5..f8349d4 100644 --- a/proxy_chain_manager/fingerprint.py +++ b/proxy_chain_manager/fingerprint.py @@ -195,35 +195,186 @@ def apply_webrtc_hardening(enable: bool) -> tuple[bool, str]: return False, str(e) +def _get_os_info() -> str: + try: + import platform + v = platform.version() + r = platform.release() + m = platform.machine() + return f"Windows {r} build {v} {m}" + except Exception: + return "unknown" + + +def _get_timezone() -> str: + try: + import datetime + tz = datetime.datetime.now(datetime.timezone.utc).astimezone() + name = str(tz.tzname() or "") + offset = tz.utcoffset() + h = int(offset.total_seconds() // 3600) if offset else 0 + return f"{name} (UTC{h:+d})" if name else f"UTC{h:+d}" + except Exception: + return "unknown" + + +def _get_screen_resolution() -> str: + try: + r = subprocess.run( + ["powershell", "-NoProfile", "-Command", + "Add-Type -AssemblyName System.Windows.Forms;" + "[System.Windows.Forms.Screen]::PrimaryScreen.Bounds | " + "ForEach-Object { \"$($_.Width)x$($_.Height)\" }"], + capture_output=True, text=True, timeout=8, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + ) + return (r.stdout or "").strip() or "unknown" + except Exception: + return "unknown" + + +def check_browser_fingerprint_consistency(profile_dir: "Path") -> list[str]: + """ + Verify that the managed Firefox user.js is internally consistent and + doesn't mix prefs that would expose the browser as fingerprint-hardened + while still advertising a normal user-agent. + + Returns a list of warning strings (empty = consistent). + """ + from pathlib import Path + user_js = Path(profile_dir) / "user.js" + if not user_js.is_file(): + return ["user.js not found — profile not yet initialized"] + + try: + content = user_js.read_text(encoding="utf-8", errors="replace") + except Exception as e: + return [f"Cannot read user.js: {e}"] + + warnings: list[str] = [] + + def _has(pref: str, value: str) -> bool: + return f'"{pref}", {value}' in content + + # 1. RFP + custom UA is a contradiction (RFP overrides UA) + if _has("privacy.resistFingerprinting", "true") and 'general.useragent.override' in content: + warnings.append( + "RFP + UA override conflict: privacy.resistFingerprinting=true overrides " + "general.useragent.override — the spoofed UA is ignored" + ) + + # 2. proxy not set but force_proxy claimed + if _has("network.proxy.type", "1"): + if 'network.proxy.http",' not in content: + warnings.append("proxy.type=1 but network.proxy.http is missing — browsers will error") + + # 3. FPI on + third-party cookies allowed = inconsistent privacy posture + if _has("privacy.firstparty.isolate", "true") and _has("network.cookie.cookieBehavior", "0"): + warnings.append( + "FPI enabled but cookieBehavior=0 (accept all) — cookies are isolated but not blocked" + ) + + # 4. sanitizeOnShutdown without clearing history = incomplete wipe + if _has("privacy.sanitize.sanitizeOnShutdown", "true"): + if not _has("privacy.clearOnShutdown.history", "true"): + warnings.append( + "sanitizeOnShutdown=true but clearOnShutdown.history not set — " + "history may survive session" + ) + + # 5. WebRTC peerconnection disabled is good — flag if missing + if not _has("media.peerconnection.enabled", "false"): + warnings.append("WebRTC not disabled in profile — real IP can leak via STUN") + + # 6. network.trr.mode=5 (DoH off) is expected when using proxy DNS + if not _has("network.trr.mode", "5"): + warnings.append( + "network.trr.mode is not 5 — Firefox may use its own DoH resolver, " + "bypassing the proxy chain DNS path" + ) + + return warnings + + def audit_device() -> FingerprintAudit: - """Collect identifiers sites and trackers often fingerprint.""" + """Collect OS-level identifiers and consistency notes.""" import getpass import os + from pathlib import Path host = get_computer_name() guid = get_machine_guid() user = getpass.getuser() - macs = [f"{n.name}: {n.mac}" for n in list_nics()] + nics = list_nics() + macs = [f"{n.name}: {n.mac}" for n in nics] + os_ = _get_os_info() + tz_ = _get_timezone() + res_ = _get_screen_resolution() - lines = [ - f"Computer name: {host or '—'}", - f"Windows username: {user}", - f"MachineGuid: {guid[:8]}…{guid[-4:]}" if len(guid) > 12 else f"MachineGuid: {guid or '—'}", - f"Network adapters ({len(macs)}):", + lines: list[str] = [ + "── OS / Identity ──────────────────────────────────", + f" OS : {os_}", + f" Computer name: {host or '—'}", + f" Username : {user}", + f" MachineGuid : " + (f"{guid[:8]}…{guid[-4:]}" if len(guid) > 12 else guid or "—"), + f" Timezone : {tz_}", + f" Screen res : {res_}", + "", + "── Network adapters ───────────────────────────────", ] - lines.extend([f" • {m}" for m in macs[:8]] or [" • (none detected)"]) - if len(macs) > 8: - lines.append(f" … and {len(macs) - 8} more") + lines.extend([f" • {m}" for m in macs[:10]] or [" • (none detected)"]) + if len(macs) > 10: + lines.append(f" … and {len(macs) - 10} more") - lines.append("") - lines.append("Browser fingerprint (Canvas/WebGL/fonts) is not changed by this app.") - lines.append("Use hardened browser profiles + proxy chain for web traffic.") - lines.append("WebRTC toggle here affects Chrome/Edge system policy only.") + lines += [ + "", + "── Browser fingerprint note ───────────────────────", + " Canvas, WebGL, font metrics, audio context, and screen dimensions", + " are NOT changed at the OS level by this app.", + " → Use 'blend_windows_chrome' persona OR 'hardened' mode to control", + " what the browser reports to sites.", + " → RFP (Resist Fingerprinting) makes Firefox stand out as hardened.", + " → Blend persona spoofs UA/platform to look like a normal Windows Chrome.", + "", + "── Recommendations ────────────────────────────────", + ] + + # Quick consistency checks + webrtc_ok, _ = apply_webrtc_hardening.__doc__ and True or False + try: + import winreg as _wr + with _wr.OpenKey(_wr.HKEY_LOCAL_MACHINE, r"SOFTWARE\Policies\Google\Chrome", + 0, _wr.KEY_QUERY_VALUE) as k: + v = int(_wr.QueryValueEx(k, "DefaultWebRtcIpHandlingPolicy")[0]) + webrtc_ok = v == 2 + except Exception: + webrtc_ok = False + + if not webrtc_ok: + lines.append(" ⚠ Chrome/Edge WebRTC policy not set — enable in Privacy tab") + else: + lines.append(" ✓ Chrome/Edge WebRTC policy is set") + + # Check IPv6 + try: + r = subprocess.run( + ["powershell", "-NoProfile", "-NonInteractive", "-Command", + "Get-NetAdapterBinding -ComponentID ms_tcpip6 | Where-Object { $_.Enabled } | Measure-Object | Select-Object -ExpandProperty Count"], + capture_output=True, text=True, timeout=8, + creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), + ) + count = int((r.stdout or "0").strip() or "0") + if count > 0: + lines.append(f" ⚠ IPv6 active on {count} adapter(s) — disable in Privacy tab for full v4-only chain") + else: + lines.append(" ✓ IPv6 disabled on all adapters") + except Exception: + lines.append(" ? IPv6 status unknown") return FingerprintAudit( lines=lines, hostname=host, machine_guid=guid, username=user, - macs=[n.mac for n in list_nics()], + macs=[n.mac for n in nics], ) diff --git a/proxy_chain_manager/signup_prep.py b/proxy_chain_manager/signup_prep.py index f378dec..f919c4c 100644 --- a/proxy_chain_manager/signup_prep.py +++ b/proxy_chain_manager/signup_prep.py @@ -13,7 +13,16 @@ from typing import Any from .paths import app_data_dir _EXT_ID = "signup-autofill@proxygod" -_EXT_SRC = Path(__file__).resolve().parent / "signup_extension" + + +def _ext_src_path() -> Path: + """Locate the signup_extension dir whether running from source or frozen.""" + import sys + if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): + return Path(sys._MEIPASS) / "proxy_chain_manager" / "signup_extension" + return Path(__file__).resolve().parent / "signup_extension" + + @dataclass(frozen=True) @@ -389,7 +398,7 @@ def install_signup_extension(profile_dir: Path, draft: SignupDraft) -> None: dest = profile_dir / "extensions" / ext_name if dest.exists(): shutil.rmtree(dest, ignore_errors=True) - shutil.copytree(_EXT_SRC, dest) + shutil.copytree(_ext_src_path(), dest) cfg = build_autofill_config(draft) (dest / "autofill_config.json").write_text(json.dumps(cfg, indent=2), encoding="utf-8") diff --git a/proxy_chain_manager/webrtc_check.py b/proxy_chain_manager/webrtc_check.py new file mode 100644 index 0000000..30dc6e2 --- /dev/null +++ b/proxy_chain_manager/webrtc_check.py @@ -0,0 +1,211 @@ +"""WebRTC leak detection and hardening checks. + +WebRTC leaks expose your real IP through STUN/ICE even when a proxy is set. +This module checks every controllable surface: + + 1. OS-level Chrome / Edge Group Policy (prevents non-proxied UDP) + 2. Firefox managed profile prefs (user.js must disable PeerConnection) + 3. STUN UDP reachability (if STUN is reachable, WebRTC CAN leak) + 4. Firefox process prefs (confirm our user.js was accepted) +""" +from __future__ import annotations + +import logging +import socket +import subprocess +import winreg +from dataclasses import dataclass, field +from pathlib import Path + +log = logging.getLogger(__name__) + +# STUN servers commonly used by WebRTC +_STUN_HOSTS: list[tuple[str, int]] = [ + ("stun.l.google.com", 19302), + ("stun1.l.google.com", 19302), + ("stun.cloudflare.com", 3478), + ("stun.stunprotocol.org", 3478), +] + +# Firefox user.js prefs that block WebRTC +_REQUIRED_WEBRTC_PREFS: dict[str, str] = { + "media.peerconnection.enabled": "false", + "media.peerconnection.ice.default_address_only": "true", + "media.peerconnection.ice.no_host": "true", +} + +# Chrome / Edge HKLM policy keys +_CHROMIUM_POLICY_PATHS = ( + r"SOFTWARE\Policies\Google\Chrome", + r"SOFTWARE\Policies\Microsoft\Edge", + r"SOFTWARE\Policies\Chromium", +) +_WEBRTC_POLICY_VALUE = "DefaultWebRtcIpHandlingPolicy" +_WEBRTC_BLOCK_VALUE = 2 # "default_public_and_private_interfaces" + + +@dataclass +class WebRtcCheckResult: + """Aggregated WebRTC leak surface scan.""" + # Policy / config checks + chrome_edge_policy_set: bool = False + chrome_edge_policy_detail: str = "" + firefox_prefs_ok: bool = False + firefox_prefs_detail: str = "" + # Network reachability + stun_reachable: bool = False + stun_detail: str = "" + # Convenience flags + any_leak_risk: bool = True + issues: list[str] = field(default_factory=list) + checks: list[tuple[str, bool, str]] = field(default_factory=list) # (name, ok, detail) + + def add(self, name: str, ok: bool, detail: str) -> None: + self.checks.append((name, ok, detail)) + if not ok: + self.issues.append(f"{name}: {detail}") + + +# ───────────────────────────────────────────────────────────────────────────── +# 1. Chrome / Edge Group Policy +# ───────────────────────────────────────────────────────────────────────────── + +def check_chromium_webrtc_policy() -> tuple[bool, str]: + """Return (policy_set, detail_string).""" + found: list[str] = [] + missing: list[str] = [] + for path in _CHROMIUM_POLICY_PATHS: + try: + with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_QUERY_VALUE) as k: + try: + val = int(winreg.QueryValueEx(k, _WEBRTC_POLICY_VALUE)[0]) + name = path.split("\\")[-1] + if val == _WEBRTC_BLOCK_VALUE: + found.append(name) + else: + missing.append(f"{name}={val} (need {_WEBRTC_BLOCK_VALUE})") + except OSError: + missing.append(path.split("\\")[-1] + " key missing") + except OSError: + continue # key not present at all — browser not installed or not policy-managed + + if found: + return True, "Policy set: " + ", ".join(found) + if missing: + return False, "Not set: " + "; ".join(missing) + return False, "No Chrome/Edge/Chromium policy keys found (browsers may not be installed)" + + +# ───────────────────────────────────────────────────────────────────────────── +# 2. Firefox managed profile prefs +# ───────────────────────────────────────────────────────────────────────────── + +def check_firefox_webrtc_prefs(profile_dir: Path) -> tuple[bool, str]: + """Verify our written user.js contains the required WebRTC-disabling prefs.""" + user_js = profile_dir / "user.js" + if not user_js.is_file(): + return False, f"user.js not found at {profile_dir} — profile not yet initialized" + + try: + content = user_js.read_text(encoding="utf-8", errors="replace") + except Exception as e: + return False, f"Cannot read user.js: {e}" + + missing: list[str] = [] + for pref, expected_val in _REQUIRED_WEBRTC_PREFS.items(): + # Look for: user_pref("pref.name", value); + needle = f'"{pref}", {expected_val}' + if needle not in content: + missing.append(pref) + + if missing: + return False, "Missing in user.js: " + ", ".join(missing) + return True, "All WebRTC prefs present in managed profile" + + +# ───────────────────────────────────────────────────────────────────────────── +# 3. STUN server UDP reachability +# ───────────────────────────────────────────────────────────────────────────── + +def _probe_stun_udp(host: str, port: int, timeout: float = 2.5) -> bool: + """ + Send a minimal STUN Binding Request over UDP and wait for a response. + Returns True if we get *any* response (STUN or ICMP unreachable). + """ + # Minimal STUN Binding Request (RFC 5389) + msg = ( + b"\x00\x01" # Message Type: Binding Request + b"\x00\x00" # Message Length: 0 + b"\x21\x12\xa4\x42" # Magic Cookie + + b"\x00" * 12 # Transaction ID (12 bytes) + ) + try: + ip = socket.gethostbyname(host) + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.settimeout(timeout) + s.sendto(msg, (ip, port)) + data, _ = s.recvfrom(512) + s.close() + return len(data) > 0 + except socket.timeout: + return False + except Exception: + return False + + +def check_stun_reachability(timeout: float = 3.0) -> tuple[bool, str]: + """ + Returns (reachable, detail). + reachable = True means STUN servers ARE reachable → WebRTC COULD leak. + """ + reachable: list[str] = [] + for host, port in _STUN_HOSTS[:3]: + try: + if _probe_stun_udp(host, port, timeout): + reachable.append(f"{host}:{port}") + except Exception: + continue + + if reachable: + return True, "STUN reachable: " + ", ".join(reachable) + " — WebRTC UDP is NOT blocked" + return False, "STUN servers unreachable — WebRTC UDP appears blocked (good)" + + +# ───────────────────────────────────────────────────────────────────────────── +# Composite scan +# ───────────────────────────────────────────────────────────────────────────── + +def run_webrtc_check(profile_dir: Path | None = None) -> WebRtcCheckResult: + """ + Run all WebRTC leak surface checks. + + profile_dir: path to the managed Firefox profile (to verify user.js). + """ + res = WebRtcCheckResult() + + # 1. Chrome/Edge policy + policy_ok, policy_detail = check_chromium_webrtc_policy() + res.chrome_edge_policy_set = policy_ok + res.chrome_edge_policy_detail = policy_detail + res.add("Chrome/Edge WebRTC policy (HKLM)", policy_ok, policy_detail) + + # 2. Firefox profile prefs + if profile_dir is not None: + fx_ok, fx_detail = check_firefox_webrtc_prefs(profile_dir) + res.firefox_prefs_ok = fx_ok + res.firefox_prefs_detail = fx_detail + res.add("Firefox managed profile WebRTC prefs", fx_ok, fx_detail) + else: + res.add("Firefox managed profile WebRTC prefs", False, + "Profile not specified — not checked") + + # 3. STUN UDP reachability + stun_reachable, stun_detail = check_stun_reachability() + res.stun_reachable = stun_reachable + res.stun_detail = stun_detail + # STUN reachable = risk; NOT reachable = good (firewall is blocking it) + res.add("STUN UDP reachability", not stun_reachable, stun_detail) + + # Overall verdict + res.any_leak_risk = bool(res.issues) + return res diff --git a/proxy_chain_manager/world_map.png b/proxy_chain_manager/world_map.png new file mode 100644 index 0000000000000000000000000000000000000000..9634198cebda71a8f3b34a27c63237f64ffa428a GIT binary patch literal 154826 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

^#CCU)h<8N$6`8|uTO5@*1P`~d&o$^Be@$Me z>At8sFox#hOY2L`QslZAMN$afx=lyI_!L{Xey3aeQt=2iS5jrIXx!#t8TAN;`-pvz=rH zNy~03hX)w#+ZY6UeXANvC@jr|&V4*&(dOpfOt&Wsk8gAT0GiI-LyeQ!c=IVPv-d+> zmS+Wnq4fiU&&TCd2s1n7#!oqJa%xe!$XOkN;1Ui$wKAZgb$!D)8;3ai{CBJladzcq zu574=Y?L5yc?XJ*B$JRoTB+s_EP{#-e-}R$5HL4>m>%^}Db@8&EAbdGj3oju`jCSL> zs@E2(kt@SEbuX}^t_qxc)uc+&cH}Q7j>4-)GmCj6<7Vx|W1Q5%ycK&$BjO54Czq9W z-V_`UY?_75%iKuDMkKETCxAVG9jc*N+QFRM;(ft+Y`C!Rp}6_V3!V|kgRU?GeN3j#BY z{AcvzwN0)h;aR2*bASr|X=S#8CNctX=^OFr&paQ#N9k0w#+K?^k_20JWsHoguw-C@ zNc#`gp{vi>sf~@nB<=^zhz9%wc2V!eSY43_YitnFvCHSb+aI63TIYaP`6c9%`ZxMp z8+E+*cEZx}jmE?&4aC`JOtwaO?ccTt&wepbF+qD9wVlFwe=-a+IryYp&@uH{jI9m$HwOpFXSDrTUfh9m8u>W>Kmb+Sj02uN zN6GJ;)eGyBZ-o+*CIXPy%8}f0{{WiC!+9lxTp(3RS#~or4a>dB9kpNqP@eL?OfMrKqh0K(_hk8}6_)wA?(Q2zkJy4{wg zd^V}2M(t#y;9zUqr8mVU^u^(Zc za&c2>a}D)Y#VnCX95Po*vgy8s*C)Q!7gN_G(=C}yx_iYFT(EN3KT(~40x(#naz+&7 z;;ygLo*8O3;dGm>qS5EdsFLlVzD6hILcmmZ=O@2v&{I>i)6!_|Nh@&}SCUi=#{?cd zloAJQpKRkfwzt&DorB5>E>%i{k~#jhe~Q`-AX@1*O%qxe_?k)F+52Z0T}MsuYo_%Z z3)F+DZY9;>Mv~obZDWGkf@WZ2*rMyjxNa{YGi|e&(HiJmF z)goD~+IddcT<6g0#4-mN0h@R7@uApjr%FV`yJIPk06)Yh{_k(-N9SD!ru=i%^hlz# zf;})@+WC=MYL`}V%W-NMw-FSMq{IsnLoNs`PDV{mqQ@=|)pi*N40&VV{{XGPj30;} z=lJ}pH;d6AjpuXtsNOd$UWG$x% ze4RoiPD^<^m`E}28!_PJR~5zXZCP2n_wDgr3qM^{olz5W}tMrJmMDE4yJV zp`&m!=yg`$kO=0v)Y11Cu4CEAX?QJ0mQ5?nayFbg6+%J|aoV86AN#45_mdM9+}dwHT&yp3n-f6>gGj;7*J)M*x&*8B#QJrJ5uP0XciAe z#p!Tx}KF`r+q+z_T=i8@+FD%l4jcc5F7CF zeI#;mST7TNGt;~}wbSMuMW^)>-`ks*hM(pp)F%kc9aVz2ET@ctp2Ql@E&Ru2KDfk+00aOOVp7lLC4MOVeY~X1mk%%5rWq~K`GxI~|bigp#_CKWK z)5oVJ?V*A67yYW*ozm*99Dp=_L_rGqYuJ029I+&xSa!hA0*xO{mgiNnw{_hdlSZWb zVVDnUcNRA~c=M!2MBY$Iw1p&h!T$dMHImR~o^3+bSY$KHYTj{;!Cf{**RJV)%~AMUlzyjn;EegJsN+ zyi&n55)^3UO^l=-RH-Kf9Q)SU(7Lw0c_ca(nH7r}n8-xdu{6A8i*MWJHU@A3CyW4l zR<)t(mUp%i+D{y5Dxe5Y8RVRRpk?_yvBuGia4Q-ocpo_cqhOBQ*G0017R=XR_Q%O{ zC4|n$LiXfsV#V}7RoA+&MAZ7y{>N3b(<9J6g(LGdU#2#bPOBjgcjYb>BaHt5GhmZ} zy_?dyX058ER{D%~daj|V+&g)4NXoE7lsYPzzxv$VV4cMIIZv9ORxk+EVp^)fix3GI)_ zil!Hs$R|FzTjpqZA-nk+S2xWmg=OF>;WC5Mlh1L+NdEvTy^{LxQ1kAlxti+qxKlTn z&h9bqo=*cA`~9mG7OaBp-WVdWMn)J??Yo1J+p-Dgj0GLN`K*PGr3CsN-H9s=znHVF z!;Q|26pS2q1RmbUA11ZOD0w;eOEiov0P_1MKKiD0`4&pw+aJwg`TPWJXy^F2V5+~g2{sLvg}@*1t? zp0Y-^a!V{_iDj3*-14JfH*DOfHU8o!|hY?Bx8Hpha(*8IHi%&piA4bWW7iy4WHsE%X931^NQ=Y zeiZ9HKWPMsEIO2HXC2`>c#t%3G2z`ERf^}0;4){8*{)&JoiTH$T*m})lQYO8jT1YV zlYlYYoRB~J73n_@IxnmIDYrLWO%2YG)7D$AUTsp*U6g=v9k`iT31naw)AuA{O?nO+ z8>^YkuQla(&ac9u6UF}k@_PCa?<--`?R8xfOw*y#^h-O4ENlh$QRx?JAD^&r8_#Ak zqdM-9V*yI-2Mjym{9o~c-onkVdSg`64b&kt)V|D;>T<>97Bcy+k>eZME#6wU0B&yL zls+ZhY278M^_8SH)*7a#)XzFwHL!Rc65o}<7-b%N1|Ro>#%l+E;iSGRXjVFx{5f~i zdX2~t$EiVNS4+H5hhbo&_)K;Yy+Ky)R2;GHK1aEPMsw=Cj4?+XqFrF7gGbkV_oIsz zd-q;#)-^eG8OzAU+??m!)NE}h50G)5)lypqNtKs{`L9WMd#rTMlhV2krkm6{Hj!;O z^L3`yRcE-8X37-u3i_e}a-d{-vdj(u@-S(RqGRH^yw}mS+V9Z7JSKBqD1U%|8?8UV zevHxjpH#nHW#ze+(#fu^bf{)nUj9WRG5Lq^fV-UFZXpO<6PmWxd{li)P`H}%VPq4_ ziWse}R%qja0|9Q4xx%hWusc$6`hK10{9Zw1_=X?%Db*{fwc*~=hIE7U+}IsW5n0DW>bE}+H0?8}d_&W22FaEzU0qwfrx(twS>lEr$1LhF6p#yN z7z7IF9v}5Ci{d@-(Dch|%PoE5n8u+|t*(5q(r#5OUN1dzUgBZRZ3K zcQ9~_pn)ksZrrk4l)O|@U z$>|*m6LDt_pQ%SaMQUbz^G{lHOcA%Wn!O z5rTLe00{#e;AHdfTOWzOEbAR&mQiXV*h^^vgUz>#%W1O3i%HH4Wr@fGx%tg;okI3W zZXCOA!Nviue;L=b+gduVr~H3}z6+LG1oqm$zUR7uycl51mIt`1mRF`Z*r@N@KGkU( z269g)=Bh-?6sK<^xi#V&Bl&@~?_9>w6uZxfy5_6Yx=xd++3GUe^z7E+9X`e=5-+7x zC0-vv01>m6;hYs_{6@LPt7jQVvpme=B?V@E=RBMa*cc;^^R17=omt;R)@|+Qf$w54 zU0YjTk@WEu5(zF!kV6>NfZ2n)csO8~rPX@R{AH_L-A4tdnuIi_#?hdTSH>Da8#@)r z$-vs9JR*)O*IP9W2CR|t$87xYynGFImF69q)4s<$UNhRsvTUsyXdM}MV3Gj9UAP}} zSGJZWMopi^laa7?U%wwC{{Vj#e{a*TCD?{kbWOZ7A?H1~_xP=sr!-GDs1gX+GQqdG zImyOBAKUv?nq2{V!6O9H*QnOAHWCiP*Fo^(!C8|;kE*(x&C)t7EQaG!Seby@LZC$o zLzZ?5F~Sp z{i*Nmw4u_rQl)^@zjUKqPhkv}>mqJp61)-sQIsolfE+MjF@k)rsa-dk+eqV{{fFMS z>h-^&9FcAJ_wIt=Xmlrvv}!dZ+1L%M`St9&SBc&^>-~I!((2_P(xH*0x6^G9M{fWm z;XoX=(dh-t5ID_pTkHI>EW;pq^c?)|{{YofjrXz6KRTj#sYcj5*DbFki(Ot&1{a|G9O>SSve6>FyVSI8 zZ>se?!D*x1`Rpx~ws>=HQC(d0&XV@9OQQlj z+1^(%85F?4JEUODgqBP?fJtCf^Zx({dj9XE^_ys|G<^?IN02n*QL?mmk|CC465OLK ze19+*;B)O+?-4pzQvK(7sajo0ujvwAO%z(JMmLUG24r!Vnbl7vun6Gp#w+Ta8M=L3 z0kSY2UW0S*`%^rwW*cOC_53W2)|=q{wcpkYUZuISJ0KP&@(51lVVRn5Ex2^!%L+;` zW4(X?Gn^4jC@<>-e5i;15cp(bw4G= z^*AK9npkB485pZ5BN-ffaBCcR`_@BM)b2G~>x;XYeEW&rrIJJ4nT|<6e44N$cRuy; zT~#_m%A(vTZlD@vNIcRjkyt3&u?4;bBa>4Loc8>v+wOZ*gGoY&TB@Mqfz1n7mbT_* zz^(dyIeUX!x`Ulcj4@=L-qqcIXP%Q?3jO>`*DaI&8|jOPnPRvh4fW#;#RPkmgEH|c z1xUgGalBTK&>kE~)w-+6aX;?;3dd}ko}zZ`f-+75h9ESJf)z8CKVV4g9Z`LAXKicH zdM&i}-6>}Fl3q@178{A>Xv-Nc3KnKKcEaxARC)mw^o{|*T`Zx~kANKc`C~m+ojhg4 zNbS0M{{UUqwbz|v4ef>9(0kjhV^lv;CEd-^%*G(@3b69jmTij6`6a$NHOS|dL~p7` z+vc3E1~|qJG5O%uTdMD8mr%n?ev9QXMhGi{8lpC5+RLHG+DxuF^`d& z^_)s(>a`vNO#!z1_w?!Igr<{{TR1kss5rRJ$3OWnIzd>F_}+ePHeF&MP0&a81&F8ueDW<}RFX zVExx&diXNQA!IYb5bw+{>v8y0h0pN-Jlm)GeYJJFzq@y~(riMTiaRYW0AQC9%0S3P;kSSLIM3*QYQI+X1bY6V=fP|((l{ZvNP!OQk;bv1 zVgLoUqU3-wa&R+NS}oYKy^P9GD*}#XQ?x4rt;gg~Vfj{P&e!TeCmnlvs5XP~vbfRj zrP4K9d#iQ{Jb%PS^1}c}u?HaW>_!j0bs2i9@p@lS>MJy|yVEVFwu0kF)@)MdINDWw z3wgilMwyMjgsi(k;IUb2N{!`5nR}HI=N#$JMDxU zss|kj-EM`lfz2gy)#8El?mX)~Ei|$4^6ug-FJ_^pDx@ei#*ni-nf@!yZq>jZxtZmgEW~~+a&hnXu0!~XV+F7@0Dm6-sSR6WX%RFHw(alh)oeXc*7}c7 z%3kvA=2-LP#<&s2q~~g=z(JGQKp$$?1?$5gAc9CBf&t?mz|x>qQ(MMZ*k^Mee)y_Sa_1nBLF5l$e&VW4 zBqBdReaE?Mcd8MB@)ZPbCj*SvHb)X_)dZcbT^ptPbzO?Cyk{rW4?Vs|{wlS#su=v~ zB}HBjKa0Px_x}KYYPGhQW->ytJdS%GZ}h6t+&j0e%jIx0>?yQ@EahIp2+|pMU(+?Xl_vl1Rb$sw7~ZLC3hM#u&%|<%#W%^a7Jf1Scbs zm?t0}2gN{*!zZ_DlM?K4fuDg=KrmF}^QM^`#Sk%zJa1BPP>u~|bTvY^Z}k#EG%8#o zkP8pDsAPgi9~m`-49ZE*eluAe2IJ{9J84~0$r7NE*6E;fc79Cpa}t%CbW7ZJ&PE~2_b zwOIm2q&5Hu3*&)4^rc?Ud#zcp6Q?d`4u#Ns9c1RUYMLHktO zSMe)jn?@@+etQao#9^WFaF05s>e=w#a;HVso9!DdJryO2kV8>#w zJAgP}{8OR|WsYm8iYn&Sp5aTY`DF$RZQu-X+dTfC6>_L-fVs7rIe;5jN&Lk39^S{_ z^UY$}q>(VV!uQ%aApFyYM+)c($s>R{s{{M34VVhXd-<%5z_z$|DY7OERE+LS=jS~C z07{BjxmVeS=e%SxkNi~`rIPILllYV48GX#n{Y_q5*c=ki87xLYs`oat33o2ZgAV=3Kif405Zl~& zGNBKR=Q&Nh9u8MO{n4?bg=u4$YUo<8d8@s`!{#;{191ZdzBBq@d~sI(X#kK;2Rse| zs%2VW&I;ft!P|^+{{HoSZ>CK!fzT<6RwK-H;l6S2k&eJ-rSTKpOPWcYWZ$|)tn-PA zukhR4GaUY0*F!xC)$nL(uS=xgE|EJf%cq6b9(!$*7#Zw6jbk(}jh9lK6LV>85+by% zcH-O0Q|{r&EBX7L*{!2h@rO<6Zmg1RHrYCY*5gWxNYySbH$eBkT!FmE-#dUIA^^IQ z!F@}USJ#Fu#d>WkW6>~4seJ-Q$2HW@yIC&`dW>7$TxhpJ1-7qcaba<1GAyXlG)}M| zQ45hFjD|mi`m>Q%-Cf`>O6VG{nWtTK2Ct#sPh%C#!&1`2`k8%8v^p{n;gbY6Z01QA+1k&tojE4+BU<29DM z)3*IH($;snUrq@P%=Y?}@m)kc9zF#^G2zs zn`Ga)88h44a6Pl{#v9`VS5I4}BjURY2MFR!)Pm67QrZN0k$q^NTBFfx@3(PRWoBMI zC)0|RrIEdxX*pcsMsdgc_r+g*ESQ)oq=AN5$N)I@Bc3@q@A>h?b#Ds#1}!^H)H$*JiObgsA1I)0+ImbPhY2)nrn8kXARI~4`Dd9FQ0M|_c9 zqx2BQ(eZvZUpYw=wd4KF-VcEd7+ngFd;Nf|a;M-l!V~nOi!xlMW zG2CzuAE~QpFQ;)Ggyu3W_XyY`5AdN2KC9 zZA?BZ4m|h2pVREH62l>nE6c=pvK8ao{r>>>MfIGThK!f_C&_;;{U8C7Pki&q9^cJb zTez$o1;%|n>c8as46gy_51ZUqF#b&hVZPMwYxwvUZ)1C3{!WJKc zAc9zJ!TwS`h$EWfjL~M_HEgc{hwqNd2MfdHxslv47*MI7@3?+m{{WG!=83BP9Nx59 zFrHrg4>C3H={i>1=$sDB6&%FNti`j7Ck5w3Gd%h01&yeZlE`~=?u+Zyk1Ilfb zc~YmA$tniO&o~D-_UAbtHC`PxUSo2xx>))=&8!%xZaF+)6OocQ_sJaF%_>g^AyA`^ z0{0m2&*(AtqHC$_Zw$~~G;=Z)-w`NO5LAva#t9hhgYQ^_t1rm03* zh{Moi90qKXIX|cM%|g=h?@7~cZQ{8vZFB=m4|vO9fN(}PfX56u88|gX9DPG8xJ!7! z^CW^j`&R>T0Fj?=&z)!6Z?~Eb)-&&l*SrgU6+mf;b#=-mtd! zV&YVHK&TW94l|AiVg2i-^oF5r;pa*ot(^p#vr29Bi)V5JN*6CVz~y7yxEUE|UT_X~ z=~}}6XCH)n{{Tyuu9fk{%GTSQXb$B2$>=Pu%?Dascxlq@ebslGJY888qy>K-CqUWQ5TWt23ARu1HX=euL%{MN6kYxh@@yq6ae z+)U?l$t#v-KO~+>tSpxW*z?9|>bSGqK#u?_cy9rZOdR<#=Ns}KgP>lM(>#3BI-1f; zZm`key0n7cB(jG}fgogLa7yndF{6y`*a*qUTBT>y9wNQf&t6$Y1&)&)p2dn;qK4Dy zWBwXP98x$g6s{DEw)G?u;F|Na7mX8SHx;*QJ}2w_QKad*PN%57&4!;E#cyx5QBhTP zu6BXAh2R#+1B}q#C}nTPO^vI+normUxiqia9sD{SlE6}1l%Z>*-& zEiA1Cq+U#aNgHlpPDyumXLFzNF~?zw%5_Gw(DG=))2H;!N^8f8*@mI2-48b2MN%e- z56U`g#0VQ7TDTLP?P*k$Asmgha|p{Kd*z5Oixc3j!s zbfu9)5ftLkF0U7R9rM4>b?5m~@+hUdh!YHKs>}$=lbmti*z;XKK))U;^d6m|-fNbY z+SZeCG@fp;F;;6&FMimcZ{SUgN)k?3Y#qg9v~L?N1@F?gOz2knMb0MFY~ZpH!FHuc zWh`<)!8>-46}eCd2DN_?JT~&Cx79i-!&21xe)1KU_;LMHSxQT0-bo`IumBv8q=p=O zkhRc%*vE4Yp;+lZQbcn%!*I(^4^O9jbqjFQ^h=(Xux^^Yv(f&czviNQ+gOdhp#y-( z1;|k8k}{I3f$A!7J9IAxHH{NU*Sd}EMxU?BOg9MpCbMK6U*n?ZNJ$0t_rS!U%t90EnsOh>v zhfb48h1gt9u2~Vua>zEbF5qxBuK-rjCU9sVlfBfR8)O0>xCNA#&jC|>6r*CDRXS5UUX+B$n!pF-52zq52( zh_kdvyOF^JGBEd61gRe2n(8*1RfHW*&!*g4Ug@(IxQEDB^3?+n6z>3%ILQ4CaVTzZ z+!4iee-65y*&}^3Sk#)wOGY% z6wpEa}J3vDrFZJXEvfS~{}Ipwq4cJ{%~0&8C}X$g~(t@7ris1^cP=gI?k1$Tx&YNQrp_C<+qt= zw&`<#e8o&Nl_`~CSn?Ew9_GibBA3HY0Va<6MNM0&?nSyFml7?k1T>9+q!nobuPme$ zBWWDhww3YtW>&i$E4umlnc9c9NLYD^77teS#`V9?3(meLb=IfXI{n_Qs$MnC_0#W` zKr!ur03Cn;06x{v=9rD# zXFoL#bKZ^B5^Io3>42&U2;)3_(-~vMQwx!v)MSBy&T3$usHJd{lsU)-vziZDYh5!d z$)#$ynrq3ods*B(TkvuNZ6k0W2Q{s8^HMCs-nK=ld`8-7N0&Ah*VY~+YrStZyqdlD z)Qf2RxMGeN;fgR$PT1pBP)X#FGEZ`AS$l&0&QIr53le+gjAT=jS1hELIF$KsB{JD0 z=APl6YCBCEc&ygtEGY)ZarskY@klOn_Nf2`SOFsmC;)k>h2!m03zglDGk|~J<|=Wn z@35^A#E~jcLS*D%0bTpR?*?@H#I@>l>KTzjFT9$4k=Wz87}!$kMw=%H`sDkz`n9G4DG;k7Dj1E;y|Nsrr`lRT`E3 z!_Pd^Je#C>UP4*PJk3!&~YLF{*VQqY=2$^j48f5W>g|cXv1qy-8!XAXJ@%u>%3Qrk)X| zU|bS|Bo!n9pZD=wAC7vgw>@<}o}x9f(cp(mwDQwvXre#^pOWOW^Zq<`0=&MWM(Fgu zGY`Z`9-U5lWmM8y3zd|;PpEi@tx0**m#=kaV-J{ZZIvJkADE-cffaT|mT-M5fyu!< z8$O}xF{y|xZP6^W%ViN;+aV=m&n1}mXD20_89vwm0gLG)G*{P>BPoS!<`Rcpz=mC_ zG4s8X9|xamw3U^eiHwlAIRFgTD@!}ZpT)0<#EU(#w~}SYmd{m6K+>=|T=v>3wX~NO zcPk>Vmm;`eSx!`pbNy*e9Bj-JdY~9-Hu0ag+x4vOi=^O1J=Ck@N$8FyT$yp6K+kS5 z$0YDM$q<7~>E@tL``T>;oGS^TjXs`S7=(+tp&XIVa8I%BGyV8Avcs?K(uIw$mX^{% zwc`v#lFBf7B~N356krkZ)vrzE>vCM&+Dy_)fV;sWg?2zP%(&wN9H{&w!2@;e)(tl^qR2f*UjG2jKhoP9%Ly&&vUh!Ns@+(d zIrV$VY}6u%9(QQKU&1z%jyXZmqUXqGu;j1j@X2e`q*ZHa-PK8_Uk+3=g->Tzw?6+x1Ob_PwKNWF3tZ+dat<+Mpek4HKm-EO2 z`V&@h?jW8p2YQh?QafYfsssk?m~gF?z_;HMNw_G^4r;7T^w!rCk4f?V9!M0tdBJWA!lYO}sg7F`iePj`-uh-mNehm;y2nwE!}PIXTC$ zs);Fwo~llzcLD%lFmuK#qXL#H2?d@)NWkL&c0aHARl2tfPkwuGRU@Cv2XgOL?hb$6 zYOqICnnI?0k$YYKp5@^f4R>;HAe+=@A}c6`5DJ?OKd0tK%@|V`_v7n79LVtuvR2@#}y?F zoQB0bU>wpTjCMa5q^hb{5VA6XC|m=KuYCRf-`=9!Id4c(cmpRN)}w71xRGIB#LO^0 zYVJ4`;Bo2zt~osR2CNxZv95Sl#X;TMpNg&}c#y_Cwo*UBa(}H?kz>*h0T=xF}-CSMA10o1A8FRf@gJ<^!QG7lu4Kdm{_60<8FMsj&M{QJ|h zvN?zZ5omZrF#v@CXLdXBQi{vXXALMu3%N-L)%K}JGlt7yjtAl-k@{9z&PZane<}#% zxnitK{118~>Y~YON`X8Fe>QPxuG{V_r zMk=KPFk((Q1b_k0eD~wtl1XiCneJh1?glv3$zn0ww_ra^ocnjHTr|qhw6vH%fk5n?`wHE3E0RA9oAT-c#*{nqPZ|9MU5E^>*pn*1B%kT<5B~slkkfRpGbtz>oSrfH ze|qbX_+O!D7ZNX0^^TXLUq%%s($d=Idze^&HZWMW0=XN8{xC-@F;iqDob0U9aMw)U zcM#F$xY@LIJ%;8SDxNt10DtROaX`?71(lhG1OEUjA8h>ooOZ6;)qV&?VtRFYdtA5b zTfJi8ptSlsOPD<51TN^j=+Lrn-kb0;Re<45XY8#s>kI8SNYizDDd4cu?{$r8JuXyf zCAo~qu)xbbwCY;KcsHuYF|G%J+zom5M2 z696ae8OgvPwg)&rem=&sNo92mhBjv1>=OX}{l~UzHPAgFb*}2KV+?{Ti4~-c+(r-> z)W}OaFxa70iDSUWJXXv5*P*;T)UDm_+p8}v(PcLFDYDefjD&g1p0SqLXM?Ok3k+A~mxSBCmc1E!>Fc1a+P9Vsomr%dfT;n?gxL$e0=&wAu~ zmFd5_nsTV7M=n-13ZRpm05}8eD>Tn(*sd=VpG_-TU~wm7W9WB2pCGDRY7K7TmCCU9 z2NltDKiU(mG#-<_>C0_pOG8Jw;CjqgMoibzY zPHQP=)q0klXqLA3vqx;R3xNjg#v}t9ww^PCj@*t7SZaEdx~=rrQ|$9)ZO!Gzv95l~ zpaJ>{9_EpS9Ym2x1DSW$=>75ZubQ+q6H=vF3c#yINFe*xLfY}7JQ~M?$j23R5)x}n zWP^p7;1QM7yfE=r9)q*$E!tXYx0;o#cCiboSX;&ft0Nah!#VUW+&bX$bK1JbgX0HC zb*y@IhOObXmF~H9dm1;;T)m_c9lIh6%N5Ho4!wvO1Rh74^vyDB%ZTHR8Qs__s*IK= zumk43DdEqB_TCoW+iQJSdtqsEhiLlIn6&vr46zx^^C15K^3h#Yh}=P4wdwQ{>Rvek z_j+L{wx79UJ#q8cyYzB>AB~iT~ zpXWJZ2I|zh!^Qhue^Ruuippp@Vmsk=b!^vc*H`3_9p|5dT&_UC0e2h<&3Mb>ZiA@w z_3ub@?wcRbeNxD`TM`)Ukq@RA5*8@H9Oh;u$&g6Os}7s>F#zMu*Xno10IPA#TyHKt0H81b0AjR168vG;{6^Fb=DVmp)8v?-KqH0_eWQ&)R2&Wz4%MK^ zs>zlLa1VO2ySeiuCpoKQE^Exot9#vczMH4fMFRsE+6y~ttw|qtBl0{}-O_#_YF#&W zw_0|gJ&nfFo@KnP8*L*5WDsyc!4=I`ay#~`yLrX|2cL0MgIOaDf|U9=sowPJx64Sws2WG+rfT9tYKFmgp{hLSF;pSxMD%rFZ>;`)pQ=F(}m8F zbEnx(EuA#^3%!$u$&y9yxDvsgvRMP|;X<#6^vz;hN#kTsKEn}-v0fn<5#khIQK>d?W$NSZuF6m z+3oF*)84CG=`w2eRyviH#8)#&QrYcB1gRm;#RxH#AaQ|@ZuzbuW134qvOgc;dVwQw z;%Fbw`B>|Tp4uNSCLhL+_@B&s{l^`LZ|hdp@vB6~&GO`2mhN&pU=RGCdcV}P_Zr2* z7!9wK+gb@wsXzuWz@85nHEeB!JHd4v-~;jVoa7qJ=DV86*)2a7h8MKQq`9SQ8hKA!1TR=V|U6 z992)=cS6=nWxACui1k6{x0ZWb7lDPqVx)Db#KB#z=u! zZi>8z0Qy0Pcfjte-=4&sV%_q~=Ad9kcsTy_riD>>9;G8=XSn>TK3PDnLX`v%tVj*b z63AZbuYUZI41A5E>;6BVsaajoi3`ECWCI1+#~_R!Ve{Ur$F7||-fL?_M2_wQ1=*EW zLnz!IcO%n+ibgpbd-7@7^!#@^RQKz4kphnK!dGws85D+ZVp;H`2RJz+xw5x{NhT26 zrpfF zS;RAR-jxEf+F5AU(k|>}nsxezV~#VKarr2%4G6PxB^Y$ZS3LK}HQf4FsWg8Q^qogU z)-;VOO-VFKAhEwF%W~4e0cZex<2XRQfMI}h#cO~UM(Bg6pLDre=lByxs&h$eZtJDc zzmUJG^L)c3=QVD9HJ84%0QxxTP@bFWEiY7SNHqava~;98xn0pqv4d=3?xz?(opp~8Bem+y zGA^^z@9yQa)UKzBb<`#X>fU4u#y4?=lPc;%05<}CD}pQ2aQQIqQ<~X$?k3Ow0Fe(k z#o+nTq2-JF1)#BrENa$qrQpM>#>9p~54Svn&*_2=D|GN%T!z=Kv_zKr>IQv6C04fr zRc}z*9ZRN0>=WwbXMtMIk*eu(w6^+{vdtUqjAJDt85@`eInQ%|M<=)$2Kaq9PIVj? z8X%eEh0)*z&hsES1Z_D7J4gYEA3no$b_kKK6?yOYRBAw$H^AnbAlGlY)^&Y)d)Tfm zp_P)HdVuFFj89S&^|P`tCaUoLnZp_Mo|btDmy_Xf0i<6^k%JM&(z z{iS*>dW*>GJsNvmM$Y2a*)=(=_z_){D#WJ5Ay}sk&R8~ECcMRWYSB4U?0Mp-;(B7) zFeom+2GN_5xYw~9p_^5Igu4RmeZ16Ky>_X&suPot3dd8ayv}BR@tGH(o zcXPYo3bBquk6}y9_5O7>$;(72Ih@#Ar%ZLd_fOu(d1-L=*HGlgG;M;s5ufO5HR4~3 z`p1Z(&2=fQysKFD#|@pVcJb~iq2wzXfL+Q0&F*=wYO^weJBoypvz@(%#dJSkCx^*v z2k0U)iFrkY?k3%mcpk#2J8l^?nB|D}p^i>C;<+w&HLj~gDD5PBP!#7F&py>x+rRXv zV<#kx3OQ7PQhX;IXX2?A0H1nJ-0{eySdr`v1;e6XDGo`&08rQ-`Kacf2BHn7jO2Ew zoOY$08jx(%!V^Q1J?eW2Cp;gDKoB^q%NrT*XO0LWX{M2aq;ZV16Tkp_n&?9bk-?)S zK&nY@WFbd?liIISK)#bsp8h*&WU+!ohFg`5jB&dLDy6Z2NLIi-_MeLMC5M7tH>T>k zboWO2bR9oPn*RV$z0{A>$E@5f#E>LbJ3((Pq*YDF)Hce;ZZS{9Uj_9yx6|~#kLa8B zX%#MKxwo2G{PvX*qlFZnxH4Oyj+#Hp)Oug1)2KCTTNl)Q+|stj#9f#wprZ3c%Jg)Qx^i{J!?A%z9D=V}a{ zrH*h-Vd4@-%|C=2mkX>=>FSk%85Lyz03XuVv|gg0K-F6E!D6>8o?An(OAL#Fkm)Ai zz>$&;MtK;(#`|qIOZ9H2yGrQYcTm3(mXgB8?N$+k#D$D*%DBeizy~;B2m_qxI(%A| zy zXq6OW_;_3ZL1xG$jy%SJ@;m;r{zj8qUURV6C8zkeIPG5PVSbOSA&Oaaj)}2rRAREd zrI^%$l{|)LX||9(y*zt!QtCUaXd;f=sjhLZ!~of(z+*PqJA`s0DxfR403zB3J^R)- zO)sbG_Dv~?l1AGcs>>u`@txiK1K9F7KEpc2_07e_{L@Iq;#I+%JT?Y&D9P0=uhZ2N@X$+|nqT*o6PmmyT#*<207BeCcFhH8d1 zxQ2s;WKlWp4I3)3+pe3a80UDTg{?f-S4?Cw?%J(`gV+WfewBxHk`yeV0MGJ{Pv=`V zPFub8&Z^e(8Lt`%018VPL1CSM@{?K3?oDaLI)}fcJ@Bs zr`oNUC=RSkd>-fg>z?6Dnm`*WLk02WlH7~|llDJ)iD^i-l1Zdg;kX@v0Q>wJt9fn$q^O}Im3II+>~KK+f6}S6 zEwzs%0zE)+{c5$jaT~-*+Ut?|f(RfUJ*pMaTHA2saHx@1~dGw2s^(qo&p6L& zS=pa+jtxOkR8_@wmge$ZY>BuK{#y|S+R@zm+oXlBrqw!>R z7|wC;*wyW=q>X;jOFUMn?7__LN}=bHtCq;^+O25D%dG=H>jRf_zxV#tt<||kVp&NT zEE6661yKNv#aC$gHClUlqK9~yUw86nkpOm!U9eJ8Qe$yDC(f#Ij*Tw zVV3nCPdFd}LZeEpv3G4<&5kgm-~IinUT9syL>s~58P9G3_Q&_6@|R8ufs)u?y&I~H zBGTmB%2*cmZda;T@uTpX$Z%3bm{sQ_h)incZvt@=GF)YvAP+@8T|ns-Cp>XEUA z2l9YoXLN0Y12`ug&N%y;mETD%g_KN@O&ozin8>^_EO}ly7{`3_59L{1Q^y{zd68{3 zyPX=^Bm65(8O@Y2{9$4%lfd;hSo@M`pV}L)Q%e>@sBayFuP@e0{%zN-HWc%lE)xWh zpx`mxjPqLl0k~b?)bQ2~--De&*B*5GR-JLE7$gJuATp9ya-bRY5}^7Q z)q&fJ*6uoT{{TrhONW370(t=rjQ$h<0FSj^YJMzr4^Rsg)wPS8*0SaXmAy!K%Mv+e zJ6F_l$7tt)Tl@ad7Mh1dS!kNhiE*z0(?Bf0-bHaG%-V(auPZ!`QI#>3F00D!Q209r z0+Lvi!;~^^8!6=c8&@5t-^`e&CU&pvyt*K^~NVl_XiXoX9m;vc58Mfpdt4-R}_*otUV#k1E2*4hG=B8A<+Nsxh#ZtKv2Oi=$cTJsop2li27_`_ooPFp^(h&E+e> z6q{HeNPtl62;8z99H^_X(aqE^K@t5tE!0i%J+uHj4xoK3p1(C&`X{0^T_ajLr&z`N z!p7PurA;v-xr@zt1})=ZAoJwf31(bIxaLgoaXLR!cx%*ph|y!x^$wkq>j@6KscKQp z6mk#4=EktD#ACrBn{Gkm4tUO?eG|_&kgXgg$&Dmn%ojWYKp8mqz^rk&B1+EfK?HKI zKQA7YuO1s(0O>RM4?~KNB`w)KOv-jO&b#V`o3waTb=3XBgcJ1BVCJ7re zj!$~&X^c#_NH588bfG+OX&9cBydV(pI+6h z;nXg!iXkh6+lb3J1BYOP7(6pKA@K*qopY`ASgtjx)#ZW}h8UxXVvZ>P01yg_008GX z#!hl`T11nVJhf9Y4Dv=RRGR4?&kpD;8cqkK(#mxWZM^k4K6yFMYp&F?2UC?IkrBvQ zF}!;)2P6B{n@rD~w;wfb^5=otqrCjrInookUG~NsvaZem{8bR!o9$G;5vi{g5N%mh zrhC-Uuz0FU)ld<)BfU&0kgx6J%Z_thQ^MaAwO)t4vVsfwVbcx#H&ffADuP!kNM=^r zN;|1I?0E+Qxe7ksLGSjg{{T+OYWj_;-pbKhG>RsX7jaa`+(7prXNv0SH4cg6l;v@F zhLSBzE@Z9dZ)&yl9`?dbYT90qTbR0oQ@``IJ6QD@JpTZwUp7%9#kEv2g zb|Sp7<3~eUbT?b;7BMT^tWOM$Y)F;j+@pH#7!93tiZd~N9+8&B#> ztwT+DAwF%f&n~$+03z|7w)W++NCO^q{&KvlZ3&D!NTG$isxB0A zz;5ops385w?NPI_xQ6Z1OqxcxwwKL`P;$sUO3Uw&jN_l5V_PPxZkw(dhm(6T0mC6r zJ13U+Tx1;JjFb3AD;au|Xu6DdmeTBYapZu26UIRUkTBaw&&WRY2g|o_s>EFw zg~Y8HV*^Nhvi!Ii<90p&04i`ow*c)RV2rac1GpXk08v?~EFra$KQ#G}u_2Zw{{S*F zKcPLns@m{6*h36gAM(v1c7+QRM#%$)_r_0i-{!f-BHJZ&TQGG7{Z+oCkikEf0~vg@ z9%B_ArtWs&oTwP*Jon?Uw3JC8RR=1nNGIU)$NTS^zY&DCe*Sr7Rc7>!z5R!LywgZbmogp17B~&gSp4TT65eCIDFsbgi z>&I#7n##u%dQQr1r*2jGp9*yb|Nzvd>J6b{n66KjgUz;eU@-cj5Lb{u4>B(6_2mg;ttZz_?n8$zE*UxCkW^F8^^ZCx9$YPxQ# z73H?7bc1i|H%TE682Vfzo|dterzq=<7?n=^=?GxEC_Ow+@I9yGIiut2W%7z0OWa z%N<|FKAh0>yZsJL4#sUXU+J;l+*wO==ZIO|_H1IHfYF3}F=99VBUmNTcx^9hf%IJ+ zKNB*@*rbTGp7md2e_*v3`c7y%VjXf>V1^0h@}P{!43l7x%oH4eq+sL@-cB9gZ)Y{gs^mh6A`me$5wR1w-F!#5iU-6F3Do4Iar%8V{RBycgVYI@Dp+z~=# z%0@}poE($f5|YxRNH2CRiNpUvk@2W47v1$_vbK8oj^T z*(7dkO_Fd8Z&&glXUiSdvpvCE$zpI9YpC&VD=g@4)w} zn%Z?D6j0_t$+yc0EV;F_r%_FOMF%sLzjInR|3s_|XLjG>+T zVC3elWKhKKAYgNf?EW!$YB~?A^_>-FYr|u3VKSH=9oWIiMgfO@$NP?JkzLy%lWssg zvx@RM%^88q==v>VHB1sWWn`+Sk;XlRQl2a>M+d!1LSlAO02mY#<|)ChMh*(=iy2nJ zLZ`lJW<}UWIX@K%6o<#XL);3_WExjcIaMPdbN-ahWNZKj=TQ739`pi1&$TdmB9zG3 z2q(9_Q!k8x`V&q`2_y~$MMzL`Gf2BM69Gd9nq!JPnn%AC0IW`rZfPIPP&qXql91-A zt`PEbil&@o{{TvsGg_nX)kLa*nF+;bx_a*CNYw2wZ6)$9?WI|vmjLW_Ia7nj1PZax zw7K;?M&8oiCz|4SSmKEnEbPPp0QV%2E7Ls(;69tveLt=09d%*9T2tg{8Y5l>TcW#! zvTlX<9j*~T+@Z4gEL5oW+!Kb)s(5n-N%!xvoKVR#!&p9j7U$r6Kk=u4y-A@*ZE|*8FAY7_`gz zdXGbRZhBK!x{<7&6Na^8i4C-A_cX?N zT&ToPwm(|wUI=JkDgJyXkF+BunJmODIwYag-SyvEPd4QLo!I(EJnC z?zB%7dQR%ue92c+wgN9Tq7nllf`ylmtO*Am#MC%^U_5gRQ;+`u(YM`oKD+A&>mLXu zw3aAzX#W6sTg9Yl`g~Eumm)Qr%|Qf8qw&tHWXCK*`j2Z!`UhB(!o4?5)~q94e&1GG zI~WY3>LZ3_k&IJ};Rl$RMPe8hmm>sMQR=R*wztx*G~SZ3g5OWjT~hMdCC;ZC+uaci z@Gw9dbI65;G6-$J0l4#?p^L?PtKO*Uiwo&GaqVpFrk_aD?h!8|h80yw8W?~vZ5y1N zDP7IRE9kR5+D%!Vv2FhVsKzJ@KIr_{Vd1>o#?C7PqQU9+T`}Qxrl+X4QE7cet50tx zo|=A^v$2dyrDkT4;YiB)W=Qgm+{uxUD~wIl7w@IqX{&J2>GRGmUe+)b41f?0KEU?v z+u&B|rg&$p^&;ul_ZAU!zKL-fXH?ZBXyKFrhnWLq<;eu?iCf?b$=&!%rQc~X&(nQJ z7Oi&$z=~^qMksXPA|r{BI=)U#K;hZ{W1ONnq2|yW!$VM2_w-3vwaZ>E{l>@OiqIs7<-OA@uLw6I%TP>Lr_W zkdPGm2;sJXrN{?w-_=?DM^4Z>Pe|2t9U0=+mRq=9?@^NWD3QF8D&`DG2u3oo^{F@u zS_e{SUPh-n+WC^ju_TWU90hO~F7d+=oMZqA2D@-+rPJ{CFtXeYpS>Si`K=V_;{XqN z_l0F)aeJrg7RD&$aUod*QL$k#2jFnt;~e{nr>SUiY1hp=E6Zi(mGf3IisRrMU~%pJ zs^e1&q+1O%nG#UTB#i7-7AyF1+Z^{j@t)O;?}50Cfx(zK3Ia1lcTk&S?7{pPQ817zjp z>(OkX&&$fEavm}xk`j)@n_ZWV!#X~zeU0A4-*NvAY!@f^nHu(dl*d{;5K ztSo$Xw-)klP^#b`Nh5IfI9~bYwkZr(`f^P0JhzD(B&g~}HjW!Rm%i-aWco=t?^uzh z%VhD(D!gnmS2<9`HZY`uKezX)!&Q*SbiQWRC;psH17qA49G}qpcKCw`h!(%EP1fx_ zLv4_6(|=`A(qw|(26+XY~GaC6O_{;(2LGWyw68u^ImLlh7fFNe7+S!2|9&{-UQPkw;!IGwEg+-TM4i_?;&T z*@8-4xa zkb02(AJd=KtzfWum6#2&j?O{B8Rwt1V=N%|T+Lzxm&}f0g$^5GU~qUj#yBI7>sJiYS=$6=Lji1%22VK2 z&vU>Y`2Mto-I0YRfI&R8tY?m35t9yxOCE9o?f(Fe>sbw3LBG<d z9N~sX2M3YIHDPqZIGh9NV~{b(`yco5Q;SeWMQKNv5C9hd6Zzu=j@bCd3Qs|$-G3Zo-#LN|U?-Y+0wS*k6S#nB9USI^N#2yt@D zk(5)oA8&8Y-1mtH-*v&4A&tbX!GTa?gu0h zw0*~VyT8=h^1{?hx@L_;d)bEYqq3I9NbijH#s?UyK(R}%E0ZRO9=~p$s}C_VK2dWU zH>6|z=8#ao#b#z<{J73XvB|1}HI`NgLKYlnx8+f|w3--|-aUyRB?%b}0mu4OD22>( zNw?CrDglw1#(3m=)o5+jY>?^!KhlE?zf4?)F@uA<@PbFT!N9JI;a`RF^;V&BZ%xI$ zt&O8zO9T*^ZstLZBr$hpIr}IXVsI(3g~otZ>9mj3$QahD-Wzl^r1uZ5>vAopO=*s% z`c;sIl$CZ%c(^3In+O~oys_a)tS?^pm8o@|#k5nUy_TC~tsS15W}6_lQgU!Rvm6c1 z#Is;z@G*7Q-B*9AvT3o|jT1<)k(Sp-!*4?0ib+WTyrcjN5IX<@r~qkFLnCCj_pLT6 znzriZ3}@-|_8ZsC``K8{F(l;Ynxp4nkPhKjb6V~h$j`l5%K`HUQQx&v{$RY*!-2Ig zG<;Kzb|jx_w;->6DoEQ&=Bf`jl~8fDl(hzF6+qlu91iBY?zZ7ZOH2OCI68kGL7G zuj3$XSH{kwg6=^8lU2HT3}_FR7?IhC7bZrK2 zE?%M6w=JQLLujl*o2daKa4betKXNij1ORZwcE1wmlV9+=Lf7Td?AGFKPU_zGRW-qe>b+~Gr@Lr+dO0oL#=;;4ztcw>eJqFo<1x(6SbifTG08OC;KpMsp{#Q>`X^8% z6Tfrb`*a>lkKQm0em$z#Fx`{dvNoDb+O#*11EiB%ubK4VF_J|9mt_DEg;gZvj1QW{ zKL)vuChdIQPGRv8f|P^Qc?O|RIW)14y=NxYVo^tWC?=z5MHI7|bgeW}r$#9BLRt8v zO*)P$B?_>U6!&A^sZ-D1t+fhOcdmQXS+aQTQTd?pPVFjB=89D;aY7WAwNy|)GAfw` za4JYQd)1?|fmKKac*R5m$nQ;tJ5&qj?NTZrDyzrXeAUbo7|6w0hvU6gSu!wcY&4+e z*jvV{*0()Trs!~986>^aAT~=gFe=vJi$*cua4z7Vakt>-)nBu3R@wD$K|bjGxC7t4dYkrM>u#5^)3seH?jvK= zcWP4CQ8x-BxCTwh1Lv~M) z$=hYlIU%++ZFB${U1T`7pAOqn54AO^~m9wqAos&+|0KGO>9uH5l9D?*xqt>kT5Y`TDO^j16$$*)Q|z?2xU0iDF+~csNjK>GBE@mE1wfS102~o&)WKJUg>uyevwD2eL#UxEFi5bYD1aOe{AV90zy2!5S#D&B!x#d(1q-+@x49s8 zJo|R6$L`5&wH;n9xV5#ol~OmDNKsYD0I?u@4oDgH#aqn+Y7jgyy9wp=Cz3O{yW<>x zDCFngv8=9XaI?Q;1{wJK8o$%$vXTje%a&Y>D{L7f(n0Uv2e9pr zde7mP9!KNVTx1O#?8x%>{H$)5eH?dD&2wso);0slP6vGVInT+?co?mdQPVXyy_(L& z<%dvv3tQV3E3#|M4>ODtmMbHi0(0d6GH@$gyRec=StnOk3yjF@SgFr$M}M_v^n#k7 zNa|2LmhfL`@(n@@lj(4wLb`7!JfAF}8TA9(H8|`88^5lH*i=uFC2yr~)K}krG=yR9{UTYaP7t3-EzVVOB`Q%D<4_7u)=+T9GZwF}f1LCfp-J1~La22OMV^InU;6 zf9oEkZF5f3CArj%=Bm~bt;66E5c3F*J^<$%jy;2cS{#(dvvTI{5fTE}kYISV@AWy#AD2_#^9a69vy)%~o^ zcFLkQx_R~>E09sQV;OGjN9H*nkis&D$xWC!YysQt{{X7XXqPeBYEZKzSXN!%P#KkZ z8E;Mx9qOo`@c?iZxmislWUQo?@!8+pzVy}!4gUb}lXGeFZ59Fi{P zZcnHJ0O!(h%1`UxcIa-U(|>nr+Fw&jBGGT)E@SdUlQgPGiQrMR2UaK4N{ze0Cw42Y z&PNAF(Dn3QXIR;r;m+Fyv`7G6a6kkfeYaRHiMg5lHtQ>OpDwpreZjUdZp35i5v2z_ zVWf?FfBA#8Y7^@d!>7q_Ev>MIaz~tE%eD@4zq!Y31CO?OuC*qg)14*K7nXV*{JNg8 zs@TSE^)`g8D-W9N@kE&nr0s_++kq&+6$dAf>lP2@+Yc~LE26xCSi2TtNEtriN{^54 zStewW#@6;9lm1sjMJ!XzBf!BR*hME8BX#^V}|8{IL=SxKiK%JwuP!mqgvg^ zX0fZ>D2UvJK576%dJcU#Aaji4jEtPx*~8}C2D!M38%R({L?B4*yZJ!QGxL%F{P?WK zqjROs8rMsOt*ycH5=)sk%YwPVAShxFzTeA?lb#VroEkrM(Vf7L47SH!!_`@}lo$GW z3nWooM!S|07?z!S7swRV^n+sWhlI3D!G@&zqfjAjJ-GL-f}JCy^v_P)YrR`_bj^H6&}p_TqT=B}!^}&9vBU@}4%3s61{BwKPaKhg z=X@jIs`I+nX=Zb52e@E$`(S@dp=+A;w~8Jb^>x;x(|T>K_Jya~CA-;MY`Te+>TnV?+$f7cYlZuNrFbw39ijTX7THV+UD<7J-3}SBl zRo&EVt_kjH)!2OVUJ^_azLvvaDJb2RC-kaBZ0F{Ub+OpobH=AfpED8&>ef&ivDqL}8U(2-J8!K#sro@$z$ zE-FfYQCnlhC>65wb51qu18Zyb*CKRit%5W8tQZiuQW)p90SDmMs61}dqttqfPit7v z>(}ut+P#gVnYQXO67wRU%P!Vpn_Zc=BX(R@nEDH?Z+aK0Y<0af#<$jNS`#Xgpp`40 z2tA4St>>xyf9f50YAa~AdJxoR^Y0|n71n9k=OLzcX#x^SD69~kN|FVAYyLOI^!zo3 zSCUUb(E6>~d6^R&9eL=tUm5IeEP9`ybd7o&n>nmBYk2iY;*0)fHEWsWQRT5;Sb|9S zBztlzDdMa>KhRTk2TJswlD67?)z!VjNp(A1%>G(Q<*6*I<&Y;kfCtnuyVK5*;15#u zWxt3JFH76m-|P3W7*gX=wfc{yjd1TOoEAc=ht$5TWDJrks`YnF+Uf!uIP7%W4RO!Z z-d|tp2GxAE5;0}Bx}B#A+e;R840eDNZs*x>ZAPAbOSLogvJSzdo3m%9L6xQeCFXb) z@4C>sb6C^$?+&$Yn(7@|?#kJ&ZLh2rI9ha$Me`O`NcSr4WpD{3WOpQ}70i13qaUNX zrteCU;w!NTj!5nz1T3-18p#`A@-mL3gUBZy)!cd}&!qZ4Sf2j?Ti3d7=G`DaN^R$B zmV!fqvMeGuSxSuJId+0b$r!9McugLwcc}G0S#?mE3oY#S7C-Yktg%UkNn~7z;{{ns zK4Wd%1~XCPS}1k0hQaZ;?nXyW-*S4DOwbuaLnyn~r=ER*PxTS;qe<`bwyw{tA) z9ELM8DPk3ngdnKE7zAdndhe+;PPghQuC#xug5K8E1Xfqbo=24oSVUA-B~<_i1)qf^ zoRgj{t7~_6cMmnja{B;afqB|CfEVlrIQIPV+o#iTXqxGnZmsKX{$DkDEYmay>ih4u z{{VHc>YaP5YC7JbuT60zclR59qG%LE#o2b2J2niy2`Blo#N?7{hfVcG#2rm(cOBzd z%jTl)4$@Twsy=? zTmmBpJ%Bu%V15vLRCM#|r+Z5)!}T9=)9w~Fm$|@&L#cX_{{T?buBE=1TwT1IrFLS? zyBIh>A8tU$7!`>+q8k}P`G(|4xukgjP=H_nJ4Dz1Zl>wRmg;$}S}ED5L2;F45Q;~<7T{{X4% zf7Y^>m(s&wXnfX&UszN|Wh}e^PsUq!=g-=(F&SinGSUdtWfKPk;QOD{59wLytfe|k zOzREI%Fh^P2k?wP9fKcH&U1_b?_9Wb623PG$4~3$=%pDnRshQkuv`gQjDU@*5EVW1 zl1@SG?0x%t+glGVSvD>*2XAlEuU_iiNfX;H3CR&LUVpg$)fT+d%;tRan)W zDPxl&@*9unJAdQasKL}1@)@I&@?R~RJU-Fip52Cd$8WW60@By2beSf#xRLH`?UdZ_ zLS9c^+=d(;3irn!HP~Z^5n+v+)&c8ZHQJAtQqsnpfwAjfHKpI#3rJ9IGR_D)5E4{i z=f8f#^r51Wt^IDGmCi}`Jn}~*=bFw>qx~h085%xv8Pt)tR(E)rl)PYiSdKHEdB;7w{{Z5)JwHjX zX(YLt`Wt9dG9{~qk>mh>)7%$sPa`KCj}@=mGo{MpsBi&LNPkbxdlCLvtLuVhX=Asa z1yKlS*d5GCC9{u=cE`;|0vm23{Ex7=>G@mn5q@6jM&SPR)JsC~#6x~LoU(*C^lby% zla-talp^9s~sakYp5NWlG57WX?vM?3JEy- zu{bybz7Mb@WZvUO-6SzvOiBhHQ}r8L1OTj81OuJKjN_0!vTbWV7Z(hb)zh=#E|R`4 zc=t+By{Wr`R=8Q2FpM*_IoprKRE6iBPEWu!uj+bR{_(zmOxvV#a?07pG8X_7&OqdW z{&jsfPEV>6CXCWv;h9ao0hw?CIRhbwJQ3dqill9>V!UYXrnQP(s>p@WT^W5lNcP9a z10ZL$bLP26E8AJ!SurxVk?h6q({J_u*2$zxX`}TlilP*@ia#!9-^j??N;U}$3BVv6 zVyJ63tuD1JR@#-#)vR)PYcm4s$VVp!^~WE6Duuq4b2gcI6IonZyp6Z&ghvXgl2OJp zv=s!f?YErPoZ79?xU}Kw2Ch_>`jg~@P zajoJJ!yfSMvN0g_6Z6OBIIMuwE-Y@Kot{YKjxb}C`dK;rK$0*?+;S^h{r5e@0yJfU zXo%S?L4)=n`h#!|djZcCjgsc!nc7(kvBI%t$v(ol2UJGc3x;@sk06t^bgEq~{6PKZ zrRp*2KgMSbLCYb){A=z%(zHFkGXvukyVB{KuYtOa zGidBg-@Q_`WOWv@m5oaO05>i9j1TShtd6Re9Xok7 zq?XQS86HH@`Uc=fA14R6t9=tdx#|E?*;L6Ioyraw6b@MY0q#k~a|?bd8}?pl@xRCL z+AZBj=jyGltzw46EQFn>0OugF_#WJ2j{JS=8EzxN7ZL9h+_k|# zpzS+=~^GdD5$k8&Y zM9_C722Od$!5#P=zQ(9cYa|aU!t!0(MgewY*g3%522KWWGI{v}wP(6|(rc}4?O?c5 zZkxQ@kc=0}k0qIb!7GLQEy>_k{{X0T&VjCJ>v0Utq%F%NR?uISaAd~{tr#qGk8}VN zg2#%mN8;oa+tYBjYoju;)JJ>|uGP2K)pF=9V1ihuUsP)9OA&#N#Qube-uf$}^yfD^ z_fSf0`gct^xpg~UX5^$1oE*s^QWt}cSo%?jr=e*!8W&LA>3vMg6@g5M<8sIw0h0_j zZ4Le`9{i7t&s6n6ozZl}vNpC164XMCCB2fj8CB8LTv+!n$QM!?7C5C z#+il1qnv;1TM@YivGJXeyz6Kyt@s)cQ0T<&FEtVjfB zV#IULJl`58M$B>N1X(=~sn-x=>Gn(pDIxK?2zZDE0zw7*d{P@+B|IMJnqK?0k2D{X?{d$Z3WbAxB~J@vf**hWL%uo z>;i3eCjC597~C689(U$9s|eS6CjS7YrrmcBkdi9AM<9n*?8}Vq2jpaTtA3lAQ(KBT z_RR#+0dW(J$m6+ek}yCS&OskFz1rHhP;_91;TkKwI^!|I$aCb&f;09Gzt&pT8%L7L zYnLpj$8tXO90H^g6#ctYi*|EVFBNckEnEuo5`C?w3VG%8Y!ia zZ5SS0r~LdG2P?g=1rG6x)TM>x$@WzM%6D~#D@n^M=xc}X~|4*vSC z=c;vIN_7WO==zUP=~okV2AD09Sj@h4z3_$zqX}>vOEF=tylkU!*#}sjlK=vAxse(PFlBR%^?fggZ$ckyB}u<0KS8-K$HDgG<#s zL#bq~4HdaN9k71Bmz9d~pTiIMU!yb+r_?RADKuM)of}Y=+SJ0UDUs%n48dMZhLLi^ zk1P(vR}7eiVa^ESwR(g0h=H|Ek-63Et_{Vt&b9m8h9DCS^}5Rm!2bXNNh4$I+z$2T z3zQ|fJk?L3vho~m2;iIuhAF>*tS%cacps%xUOx3+QMGVza6PJr;`ppC-FX*atd)b| zxFhdf%fwhltE=?ZwQC~Zop(vMn+7sjt_)s8%YZo|c;x)fK5K&7F;?Th#d$rMn z!w!_|tybzAb?Ns7px z=JMiOnctR{XD;pS$vm2#wW=LUP`SA(v6-c53Mn`lB;zNadc{d5N#s=$NKQH~FA&$i zQvU!UfI49fAsOc%l}1UPR7H0#5N$wCc8uo(^(Xq&Nys#T=CcyGWyLUroOk@HLKZpf zKQmK?VG{yyz#hbLS)Qi9vDF^h?$X}Y%2{J$ac^cEBFVkE0k}{`+$xd=J-bxe#*j%W zZDUJGCuM;tj(duZD)&y&?=^`gj?M?Ucwv&^)-jT?qLy|)U;!uct3w$TS!v#tbU?aM zrBW(KH7KBx;r2w0BY%YkO;tr3s&o@lRmlL5xjoG}f-Hq&ZR$APjA!pcb&FBP1whn^ zsN#`HOSq>???nM5^TjdRl@&D=FTEMfESfu0N|i+!&MHxm3C{+phr<2-)Qe`U29#>m z(LK#Tj`_f`UnmS6(bvHM5rA4Ssr6t}ikuxqQPh;O5~Nap@DQZy_>^W4VA zf=4x`_#J;F{TJ8P%{8^WQCVAFq?aV&W=n-szzE&Mj=bP=$pWyRBK4ND*WGYu>Nb}A z+ea9YM#z|`8<`^jD)7v3tBwK3&3kjGKc*B-oG*~a!&*4A-Wmf~d9jcEM%^=c^a~5F zX*!LjhXiuj*|ax|F8tBCVtCdtRGq9j8IW`F+nTY{wFzUpiq;D|N4jYug{`HIVgi`G zMSzo#0ANNBYT9~-T)wcpic9;$6^0YbY1TD)({i1KT&Dbdka6#yeLnWW=EJQn?WUQp zmge9{T&kCS{-l#5BRFt)BoIhE9x%yFyl&&46ScdE{ zVln^(j31njiaDC*QLb$C3HW7_hYJj$u|A(qeU)pQ?FH?Y3yTZIoPajPeHv{HGq;!z z=ZyaVo+}w0u9rQ)7Li?B$m+_TYowm690TcIJx=ggx!N4>~K|11JGA z3OANP83b+|kJGrSHX~1(@+E7D(MbfKr$RaR=1$-HscF}&(#>l#8<>g>#mQzI@D!Zw zAfIpUDz2rbeIHF7@=ge`xTeP>Al1Qi$5r9Tnl}_SSLXrV2 zGC;|z3mGGWYLy-=nt0tKjn@AE0Kr=^di0OW(nA8Oz3 zJSEm|byu>RP_n+)ZPeRbY0rM2T+-pQ8I6wSWyk~##1$jGZ#wpo)JrgK!}LW75n@m}r8U0|{6*83r{KNTW{OCzwEM$kvL)jw z1iWgEBD{NnABda}P*adB6U09fr=xa4bH%V2)*slfFB-&LhutLag> zP_Ot@fH)kUdGFiws#-mj#<6>yz!GpImA0S&dy~KbX1fG?j@8bu`f;o!xNkGe3gJOuKx~ZXC-texrF%$s{=Y@1O~hlJ zgjf&A@3F6^ZsBCLD|uex&hoI777wZ+@na>Vbiu{%kbgJl3Y!;rw_5J~%Y^>!Hcsr25FeS0i% zSj9BAbH9)hIP&bIc3fvaa4XQ3+I7E#{RutBy?14Q)Yr%bylooE^wAPME>v!C;1T7q z+CrTAavi~@@sEf_SJ7wI@ek4g1bK6?25;Bd3jv_?o!?CK_1pB7#8+CJ(yhC7EQ*j= z*{BE2OKIgG$tgQQc-Md%X*;qqc703My%!uBw_0nu&cD+4j%}P3+GWAc1_3w+ z3d08n2O_m7I_0gpp2tzwVLeHw%nOk7v})=@E)hb2n;Y1IzK}73DjawH zOHDd0L9H<&3we8)=#1O-le1nsP z9nJ%AeU58==?@Y#KA7l>%@*e0R`c})BwT6CGlPV z0CAu_X?9MKXyE12x0&Y`O$XG_8-7yzZ5S)b93CrR>$S1D@J%Hjye;&o;ai&r zx?wzS=46PW+WR4uug?>E2vBwR@=`!RyyH>MV>NF1$YU-vrFp`lR;bT+<4?u5qq8} zMca~27wzv+l21_pYIHihoi$yU=FZ8AcdG@Osl64`)0de1oZH#8Lw!#$9;rJsTdc2Ql_ucobcsM_5 zsH{}`5nUK3nw>nY48Jea2Gpb5-Rzd2{|aI3_!>@ z7~OPF1p3D3t~zqht$KT@qSNmuiYtj0-bS7aec>AsnunHkUT~Y_oKpv8UKis$D0X(pgL* z0G30ODn8tBOqn41lp6Y92=FyvG4k?VbiDcGd2~M|uTI$H_+ISv3jzNC19Ue;&wF#J z>K7eY(aq$;q^|oHiItA_or1cu@w6;Ylafm=FmU}**3xwrqj9f&Om=r|Iok!f5Ht(7 z)37PV%nhR%#@rs@oCdLZG!ijMMtR69o!tAL-yZ(;t=-EzdCdO+#9*Uy6(X4a>751ax4QZhdp@_6_sipJ?S*P6u9Tg1VhZ{ftQLaO7E z#P$FHNk3vwV_S{c(zSb?Cr`K3UO^h96V5F{Lan%$l$LcY2|39N#xh50!5*c$fK2(l zx_>$Ds|C^wtqJW1n8Yfv-L#+toMVRn0DZArHO`lDt$}x7(#bR_6mlea%uE8PVaF;@ zupoo&j1!i<@b_0Zxtc9U$l6@XC6|*FY8N=(>=kj42<`dgRtsB_>12xE$+)vyD7>#N z*dvz&lG{hQC%O3e;;9-|!Vf!59^cryP%$}|%oWy~chou9ZYjPLx6U#C% z;2fNvv8B`9~i5gQrjMq>Egl#Px~2idWn`68>DceR2x8^8Td zO%IU!m1aY0?YhUqq*-2SGFzqN`9m%zcrdsFEKV4(7zdmm$W$)%v3)Q@9IyzE*ds51 ziJk!CzsdJJ)<)j$RF2~I;z?z-OcsTkcbd(Sw1&t7f=R)~KP+P}B(-IEA5RM>4xU7% za6etyAC?c_+PwU&Fal$sH#QDOmi~0BBa~rV+*`Er7Lc9T7$j%6ANZ?jY~zk!Q*Apz zBX0(_tt(V*Q%REEDD9$=T+14mf!^4`LQYA@#yRiX8LD^sd{^3iz0JHU6EP_!)+`m* zk&N~QNIa9+;QLm2bP-PJbJzU+7J(Ntd7G81fK|y0<}P;t6mY$}d;XPwZ+mm2 zT(^|(ZF3Xk6k?4V4}L2nanhGo4-eLV31x|ARy>DB95#LZ$L0v{QnTsVu9eIrwljcv zko<5ve^dHbIxP;VuJ{4Jy0$oE=J-ZdLeHwFktVjke?BQllzmQRWq)!Q^11sFSKU#h zTEV2*YA}BWOTF8X7)q)XfEy>3&pwh#0dwvvH>LP=mU74c025CRhi+gp!wt&>xVS&c z5-b@oPxEXb0q$va&Fp$)@MxCE>jjFK)ES0O^SpzVLy%bT%aO?TuJMlGb0=^lZQLJz zi_=9Lya#yB7~8$y`|P>oj3Jr>B#{u$v=DGGMsi2y1!j6))K9Hj8;F#`(yX#9qaKiQ zbN1S~Kdo3rDUu@67})0kqW%yMZ)1_~*wx*w%=WjU=~X6&0cMGmuw^+1J9+GIaDT0N zrm!`^=OcBV0MPLBk-EuWbq(d!jIqoio@okslNh~j6y-)c3}oW~9C4p&*6khJQoTfI z!){z;aK{{AAHF-*%-RT*67tQOShMB1omU}%2Oxv^NX`!jgID(c9qPkTo+AQ%UBbn4 z9fzKuP-7#95fiv6@0@lZ_Bp8PB9cPaZJ*$-l$v?ua~ymB00*y{&{I&F&`lJK=`Q~O zTAmKzf6&q{XPw?axW-5Th3)T(&UIHz%hTG`#5cBr7me~^kovZ;+=naN5JAY|uumM9 z3Rsi~qX4KNepvhb{{UL^lC|t{W3x+>ByvPcAJd|w`i;Xd%kRKd!O1uSx8;hW#K@bO zvgaeda50L`TWC#v0ch~<2QjKM5CA2cf!i7X0O(a2^hqtQ9yt{eyuUG6b|8$5W4?ZB zhs7Wt7D9>BPFu|Ef~+)#hRj@>i$QW8GKrSP#`ZmrzB_+jYK4?r%S$-ksCPIAZhf(l z+cloj;Yi`0E0BoS5g#NlAf9jvo_)qX{MIV&)LD=Y(ndq7@spl1M|!2v7PL5Qli)04 zTy`Gw`Se(KO^$GQAEh*Mahx)*7;M$U#7Mz8Aoe1IVIs2nN!+`D9FjlStXz)V1#E^X~5wn@N7jdqkQH=Uy@NId5N*+3@0p8n>w%WHq)ZICtr z#z^-607~7oeu&g{eZNBqp5kxetWaV?7>+$EN#pXZP_^gwuSLTl)XGa?1fL~ur!|*R z*ut07q&E=l3v=Z&Hhv={K>Q`RW9U$^yJkjxxLvgV~V#;<3f#}D*zu!7%P#UziJpNF&xX<-MNTD5Hv7X^cWuP!(0i4(>3y8%_r%mYJ^GYf@ZC zK9Z7HL;O@tK0^$Nf%v^!_W(289zDiuRMRx6wPegO?&Jkk87xV_!2a3oS*=pxw2K)m ztX?@1NqH)+uGj$f2Wd?9-IMkdx!wDUvOQL&R(SIthtONQ4YEF}=TYb@`6YJKZKl<& z-cR9NK%Q>!c?9jw54i`L%=Gs~U1_S(>aj}%NY_Nbb{L zTaz^RbK#|S$-?agvNNB5f6V*+@Y;3AwP$Q@DhWV6n2!Gdr+?D2U012ixLdq&LyRd3 z00j3Z6^*imoCF0)$j0Hp&IMy6b9^i1n#VNj4~5h6x(7h@tV^#q)XyHDZ+Q%smst$R z70`g>08RsiZUG>#0<&7@N;I!M){}lOY%R=Xq_r-FHQ;1|6~=Ro#V|<&?OdNxvTZu% zRAYw>MhGDAee?NN$J5_Nx9SRaad^co%}&<>kg zFFW;F9;N-=0^Zx(S@{nfND?)bokH?lyA>Oy?Z*HN<0k^O-44*BJntD&>QoTDxa58F z+PVzZT42>~FRw4H+Q$toyl}d2XvsS<7+`WnGn^ljR-I9$>C$x7o!z{XS*6U987)hU z%M$Gf#3ieNDcI$s7{ai~ z0Q-~QGTvfVx$+>L9te*PdHau33UDy7{@s7$!(D`5bXv(?HR#`jcL_v$BiN zmhM%GI8*UEvtd+r#z`M)>0T^#E}#3Or}{#|ST5H_^CrEzfw#pa+<_I=HupqGqg;*) z2O}y}m={}0$q{MN%JOKsfb-3j*E9~Czw3QHbjv+GX0_FNCeuxMy&wLzAz7_ZD{@(+ zw^j7y;feOI8fqjY%m=-CPxgn^^&2jV@U3EatW;?6U$C@nGTm&BC2hm8@*%ZgGr^IM z8Lv3?7Oy_Nt6ksSGBwrAtu&=0R8q`xNL2t1Phc@oFU2lT9+j^xXcRynJS;qp2qQhJ zixJKb^rtZABb?MxNy!zF&a%8Ci6vyT3lH8`&l}xa*~J_$Gs$xyMIltSz(78x#?=_) zk=m~M+*9-+Ryv$Xx+XEdduwA`)bE&u?PRRrJ+kfT2pjJ#MPh6Ra(%eQo{takH^ zE((kh&UqmFcB>YA=AcaqR=QHgcw^N!lRfI2bImU1neEM2cCnLbP-w+Ef{IiFiYTJ# zFqmQw6@P!LOR3vgLnL<53pjj~Fu<|1jm+6{pcibh8=EHp4B!gKQSCr^7*Yg)Ky|{MNVyxnrEuO7vGj>4~QJ53XFuiVnH0+3B8ShidGKH;{tfz_gL) z{{V=tYbQ|2Yk98gizUhP=Qj4yti@2G;XvcDRl&|ZusO|jezd+iZ-yQk9X?2s%jjgf zlHH}+?Kjm(lDHT?qE%I2{?#7U%Phyy!+i{~T3+cgZID4c%w&k?X~9qdIacLyoDOT) zN$|CeX{zhzSU+;UYotCSrkUdTomJ!r1bX}<@U^R38~(1=CA5y_-VY|+v`cX-0Li#B zBDXnXyqtc2Y}d21GIb)_PLasIb9w0-5WZTHSuh53g(J7e6|r4K9j>Px&4WNz$r>{g zKcfx^UA*#6bMwLYtBrI17}xBD<<Vy1p`J1NLMQs0h-ZoJ5X&PJis|@Ao<$%jEs@xFT;m3@okxB&4IXVa$t1Up#9`#Q zO|Ni0L@`iECzFiw6aWu;%=A^Bqo?Y4z0+k|P`MWxeq)hjQMfBQvSbo4Hy*o>TPo+*lSqq<$pduB^lOGc8&6!7w^C) z+W>pi;=8f9f+QSSmLHv&&dnxs)ZPsfs^vxE~l9LkQ z&&lJrxW+i`S9gyj_cB~bGtD%^adse*MsV53+mbSPHD!CY@fT#CY{3GB8&`V&0EF@Z zKc;hz^_liRYSkD9xyAngYKz~bI+LTSymF>+$I%-V$ODtW$id@`ezvZWh+~XAf^45L zc8mfJ1`c!i{8mb3x0WdpppWWp#Aggh44`9>2=#N%-{^Z>0Kw&8a1C1Ka-q z71*QI&Y{-6G&P9$Hkc8gyMx`TiM;>8i9N_SI zg!Rma6mf72X ztt)+RqxFke4ZfEWL8Uxu!gy|D0#L)#=13a{J@)5s#(UN#;s&{!Ptt6zV!lY?^*jWX zX-M^Bk5Et*c*r|gjt*+}#?ot2 zCri_Hex|Z@Yt2hhw^`wZwr=ubjPOdca_#Q83xSqSYiP@}eeJSsmQd!v+I*XgSE(eE z+His;%ZX}WhsNnv)^ z%`|bzZyk-^p0iwBMp<4ZcofUV^5tWgDFv8^OxKz8j~4ImI?qqmdW%rK)Yjf+lYEcn zJS2hx#;ihuq=pN?%7MjP^?!{QD{gd-n7h<8y%;mIztpnNe(nOalkGS;3II+*jEu1* zjK;bEInU8&lY?uec!|!t&GZ>KALj1*QtPXz^p}P@_OGqq$z?X7a*1OOg<)opy{vmZ zeqt=ovvw3VWDlJBSThRqZ(8eCH`?|5+I5iA>|~wV8%2=FAwo{n+<7=0Wc*edO-0{v z&JS}|ms7%xA~b?V-Tweoc0ZvRt~R4qC2uQ51?%{K3;ip(-s0yF2bMgx{!#nVL@X45 ztT2Bna_d(QY}Vd$i*{h`7UQ*;0s+TtfJo2W)-|L~<-i2{RlJh=n`0fL=D5ap0am>* zfv2LK!iDC`Y)cvjrGlO=~d$xly}O+m7ds z^vE9F>I{8HwMY`Zz3L!=gwc*O?M6AHIW;v>Msb?4qV`QSjGua+mG`RFa6Qby6lIA3 z3hqPpc(T$hK9i}s%S-9*qTeR_2S&Sw*-+rD(?DNjgoeTeYz5?kabAN#!spjmfuxby zT^uix`32J9Ln+#}evIl(OQv<3ZA(eJlFt4H%yPtVMn(uZ;DQMSNFahi1RCgIO&u01n23apxEy9{l2`&|VEoYu7p>YFGL-&XE=TCg@x0kTi~g zw-iW=2^pLMpat$lcc9@~-6O$s574RpnovbGW&Z$Z-3r^Ld_&Z}kVEwI=6ImBbskPj zDT#u5RguFZ0me|{D}!8AT}c(hvWZoE)GFjIFbE)wV;}l8ymiL4sr46FYPUMIqd|9h zbtKax#85=;<=H}?Y?J=`oh{H&T*%tSv#DBX9XS=tLG^PI=4VhFWN{!ahWCHAW zBw_hf)FBN^M-{0~<)X(mXwi z=TkcVm9FSh=w1N zLi1O&Vs3JO1aX;Mz`4s7R}M+ws2fg(O$LdSWajJzY!>F0K++uUxUA#L`BpZtbx&Sv zx_*;)c?2K4Cx*@l;X~yFEEX`Tfcg}k0*?94O>a6MLC4oRHnpU5mZy7rcc9-0u5NFU zAy!670rH{@?ib97R1$edU-iAV~`V zP8b5Y$>i$2U(=(ce|2?#Oy6C{rE0e_`ElGI`HMS z6tDtVMr`LhAiP8a=#kewI#E|Tgjb>^ws;!m$7xB8*Owd1Chf;#9Gs}Y11*dkj1skr zz0%;*Y-hNd&g2!6G)1{l65y#kZ6|kck-^Wu9bZ_6)5ohzolih}VbrE6FRrv`EzD9Q zaApy%NOUciQl}$x22}tW<&tREdVYhbyq32St4k80Nk-s6JAp%n+H;NFvw(A&={9&| zXpxx&de$~LM6qAS%(V62qQUh*Xx1pL#7k)B&51`N2ZCF^NF#6Pc&vZXTE@=`-$Z=1 zAi7579lyjg&&CD^Zq>4Al3d$rFQ;4?WwMo7TGZw|vnK(883!SG!S?T(w@p7xy19|s z>8*^9v&xkLD1I^zVcW3$tA(i3{-$3TR&hrZ2V-Gm@VU=0-?3CWUrvut)a|5}6A`#Uv>tCQm3kT{MMHn8~?9s5snCaG;}W z{TtI7S6J%NX_jX3Wk{Y`Z+EFkQZQtaR*!EHswrdZ&IWlrR;%LgRWDTawyAYzc{SFQ zl9+VyFe&BQr=}$cN!S7^`bTQRuhg@KX#{X?cB7Cw{no<&0MkS8Zg!*fTZch-g>%+= zg6UmVsoCB}vOUaKI>fgxJo2{1@>WHVBJSFtG@g|J^XzvkjVq~j{-3yhi?X%pGBqh} zuM0}n@-m`^MHg}$GN#uODwP@9#c_?kX0+69boAdJ>i zsQS-X>it$-M_9g+`tC!L(qIK23ZMm1_D}_R7`Wl^3=-M&HIH1Z@oRZwERxU8kJl@t z^xsbET_0zu>KD%yW>9YR-*0Pn@M%;sy!OA}$jc|Rr83$~?(seC&PMcTIZxa6iN(s4^ z#%p!f(!Lp?5ylOT#{U35m{?0YHBmdt z%90Kx47);*2GjV4Pb4rsus!Q7(%MDFy{ua4_NdccO)A42AmhwtV5)Kn2PA>^=CO7* z^XZmP2CWgYS;$0?E@P7gcMZUik$_0}!Q(lt*TcO=*H7wirL!^LEYY=-y`-BZ{vzye zE=&-w>LFhQ4&3Io#P&L?2eID$7k@_xbC@0n-@TYbKM7EmB-oi^# z;7Js)!y35Ykm_(8GLi=H8*2#4pw~4eu!b{hZ*Gq|LS+19V=a~=xd3E-qu({K+VxaX>2la=RuSrZA~D*|SjO;^ zo_HRm1m_$dzA5z9iKFS-uBQu4BHd3K+uPj97+L3^ACwFS(#wKVkcI9xi@arOt(z`AsgH%OrN2g04-K=H3<%8N7rHkqB zgZgd@?upkQgdgQ%a&UdS9&uJn1T$ty5=M?dO0Wg84{Q%|4?X>=+gG=i)_aR;b2nr@o6 zHC3K11ASVWA1Ja{EesSD@K;()eyS$E4vOCQwY`Kw%4Bz~7w;hHt+ZB}3R_^-x z7_F_|d*Z*eqM z8MKf`EW|Bb;}@AFXOJYF48DTZqvuSgA$X&QyXMB=Sia z{-(9~B)pz1Tfd(SXbYk!RzNt(1B3bV`c(^sVJpDO>o);o2zRmkt1G51_=sF7_{7r| zy~69b9&|6QE~j^B@N{_=u|s_K7eSSn6)`GC2Hb;!Tu7%G?HE4wqFq_V8zM_-b*Gsa zgLfu2N65k226#Ei&um~-=S$q{@ku?tk8?CL!UWPqyu#|Ejl>c&-|xY$g<;f^>S=MK z^(-<CSL|Mt*a}Y|~#yaSYaOkt9+~8K;eRZ9TG?_8B~JoYpRA zt`R?quWX@+^MVq&13Powc=r3%Jw9jd7%OhY0Qmmt6MfgtBpK>rJ)__-~ z(D0B+c|QrFwH}-4E3IDk-DQSIZAk(^+6B=SkgB%0^? z-%zabt*(_3hTP^uEQCAcjGPjq0}GrFlgXxB)(k7Pdfh&PFZxZB)Qi7Cx2ZkVw^?c@ zsCvTdQMI)eyWLzYue^)^GVOo~J;}hyJ*yFWa{mBbG;HQFIUcisxX4~H&u`YUR__D} zESp2jpHrWKn-{v%0`wkx6C z>)Nh`(%m@vG)-w7HlpoP+CM0y>m=~8W&<(@!D6mXcp1ROY5fDxS3P5_&#UUsaUO-H z!!7=$a1`Ysi8mdj`a?0=i=OPtDsFHBhgIz6(z+P@CXd}#dH^?BZi}&t!~T=hy3X;0 zQbe*@=)mn)xV>cBWKap>6@M_W?A}=J)x;#Zc`h)gjQdu};xCBzeQm5Ph8XO$>sbDq zPt)Q50Huw++y&Y^R`FA(_5O{ArXkX1k~@7TOp?!0lsD8vdmX$n&V&*aLddMXmCF=~vOvJ| z&B}U?G6}C+_{XK`{c)|spzFGYu7%aQC5^q8)<_H5%wvHTNF*_W%2r`6*8!IewBc1q z;Qd$OZmH31kJJrsX9dN)mln@!DqV|!q$mnwVxSVkmSKPiPZqmB7nZb zpgpT)&XV1oy(&rVfHPUE%L#fDU%j%_tsuC*f+vOI^Vkb{SuvcU9=OTvoNinYKp^FM zv#92?vre3~w2Mlzv{hN-h@_I-&4n)V%%j#f460{p05CI_IyBM}$F^#sX?W>*AT%;Z zy_95NGmifN6-S)VPE7;tR1<5MO{NVj8esci9MV0_B9bT+ccPL=qJu%^l3^ayQAH%t zN{T)HDIqU&*m*8N?hS!%s2;VrF{irRI~oYv;{BBnztPUd*d zND5KhfuCj#&OokBqQMoGp?RZS+pWXfU9Gys#@)Ng+)r{ozyLucoD5f6{hcDyd{^l) z(fXdJ9*3yt(;Y`ozhE1DY{SiuZd8_5JHgN4E>8$6p7@2PSZV!9Z>>oniYxE=d5mPF zaok2!sbPbd**5^B7nyPCNdl z)LG`#eyyHhs?$!&ktmNl#^JRQS0^BV6aj(++gTN`_KiQL`B zJf%Xv%n9SvPDVT6^Y71dOC=6H`mXC7C4h!w2Lsdm;ch-K&2Q9tjf|RIlsZ-|Z7tf@ z%UI)rcoAZe)$%t9TWqDD1QsE?0@K#@KBDPc)lQ~s7?dxY62}xDQFj?q01Y4bf#j3! zPpbl<{x(eHH&lXh;PW{K@eIWg=|LJ&_mV0a`AxFpK*Q*|}4mrk96ATjKal0i8+ zA~w%C`M?3Z;w_hO<10ZJH2wi}HROc~~$?v>tazj3# z)4^*Eq!TD;;u!&y5T^x(Ppgnf1djZgsuAL~*6yRHzN*bj+XI*bHwUi9_WFFOX%kx& zmQy5ng^MbZ3XRz~AJez#R`OWMEBS^`J(ys#Id{h2N#q{DcmDvzUtQfq>}{n+bd~pR zg-|v|MlyNFIqmV@s7N6?rNR~Zl-h{Q(l$ZJ8DiPU2LR+?dtlIlw4tHqghV z@0)ZNGA`JilLAFv7=gFn&@-H6Tzm1WT*(|)6T>7(#1fR%TNa_6~|# zVv+(BE;jB2WPOelobC4f%_~XT!&X&mc)qEbQa6=YNRj|mTmaii$sNJ$4}NPDzZnam z2mN5)Pt&*AO>W-9Qdt$+IRo!>+(C`=ouqpa{-0x8=TrEp7PlU&^twzsRo=CBm)fP| zMWmAL%RX3V0T4MYv3SdJPSwXTlJwX|aTScKhkt)jjN23|ZRl^h9 zWSsB^9bu~s09CIv?i=FB=YvGoTzTYV;}y4(+&Yg{ey7!LFRv&1yO);@bo1^k4nGjV zV~#s}WAm!LE8*4eSl>OCpKik9`rtur1X42>VaVK!NFaWEWc+hapn5w|>yD(g(QFchhriM~Yr-C!ld0|GzLw@FmdYq5w?z`qHJg>b(MoX z%Wa4P<~e+zQ;cu*DJL1O2jkCJ^v_rIjn=W#HrLkC-j<&3JK0tL0FKO9`C(OdrU)1% zxd)tLw7#O$+FOTVCO|#Otyb#)03pD3$vwvv#?|X>z8ztD-WS3A83v*6E^!s*Uqv}? z3X_wx)d_BnGR>ciRfDl}l5b!d z-qeZyl+`zynCzFD6?mzt5J^7OGD0crPqjhGWG%Ts2OD$G=U0FkzUnqgfhQ;Cq6a37 z)9xywd6bgJdX+~Qr&9^0ahk4~Z)&06gW9fD*nk3z9^W;pEF6@=$>}lw038lSJ9A#8 z@m9}C*R>9rvuJLhy|~cyuq2LTkUASUPGl|y#u#FIj9~GCPn>)_>07?E>fJ9!wURq| z@9q)>nr;#;#|7K$qa^)-uSa#g#8>@Y)HjG^dmTR0=xr@lFD_#blr5uTNWx)4?#aj` zC>-(izxbFZo#0RakEsjMaM(+l@ErK?1KYXVT(QK8ibEF@m$mT0xLG+A)RCeslJ~ ztd-(Uk=d*%ZdEd5ME?M&Rl62BIl}MX0~3I$+^{6=mz@S_iaGsk{wc)itnO}B@-FGt4Obh^%;sodY(HjWkT zViLs)d46ytNcoE=X($E&W5^XeUY&X`r!>xm(yiOfy@K0V)h?rlZ`I2rUSv_GKNL}; zo<|Jj&t+V4tInLTx9P1vR(*cbYnz!~Idr>pcDaE@aP5#61cChBm=U|w7tV2hD&h<= zw*zx$(-*K+YNR2i7VpK2jS&kl)oEwoZwG~}>DyJCPq zZKR#Yg;3`p;<;L!FwYg~UjbK9PYvX}w=%^87TJrn2g*TSyuwZ@+FrQ~LRA}klQm9hM2bML_1nE}Q| zLBlUZy3rv20EV@jYnzC5{*JkwE?ZI3uVHztuCT%>HWu6q27JdLQoxbvDUs(kV59(h z04r+n`>w9~D_UEa;)?#tLaTeHTqtO6;tUwZ;De0gh90Fnf(>;vTx`bQ(@r^~KW{~R z8lG18atm+c{Cf*z@f*V%&VY|j)U>Oc%~L_Xwu(sgg|#rn=Q4(PG97?qJP=4cXBfe) z$HVTgJ#P-7Ca-RTRk622_0h;7S>t&`WXLv=<_4U}ykjMK8TD4jsC9z)dC>YlRCMl_ zVSS+M?#-)dH`6qY90Zus-S(BrnIgNxl|P0(!#D?1@V`>&4yfzSq|t5Q{l%nfK^saJ zbC;S4k-(QBLGNJC9n@}<#_mJLIEH(>PXwmt3?^sO=V zTg$sMr%a|C+Rc4+DUstsaO_FKgPfduE^+f(;puCDt#4@#k*EP5nDIG zK3DvHR`1h&GO=6goi%Yi#m=X%Xsod%jAP5Umn$p^C|s z>x+Bc15~x1+-{aRtfW$dsHeL0O2hF>r-6y96E)(bKGBO+QQoT*DDli z;&oV=PQrFBPH?Pv$tRz6FN+tKb`~0EPFl&OX?Ae!f;cYS<^fAQht9}zoR+~D85jTx z^L$HA%~=vV;b<7^@~1)?23ZR?;UV0T4?LWIFW)sr2=8vCH!)80U6943k$`q=5z7)z z08n|r7{JFB$^8zNgBsFLQ`gl+nivfH+QFQHfCt1uqc}95ZfUHh{c?lYn>wko8WD)TOtu)7Ic>I;0cDYJwQSig;aF z;%&r&zzxGJFcpBv3M;7g_&|`-MR(gP*hi-fjK9Mq5w+}Z&}XQn4;VUb4R1?Mt%6t= zS1hsX+I(-54D3itfIXI7xncaSeS@eR*P(T9-5qJ8O4o8(K(~;I<%&-)5$_waat1Nq zJbc$|=?yJ94SKf!08w2&`%i~Q5Bt7pgtqr)W4&SW6@i7+zzF#5B{gX5f=#dL902p=dYSS6Y2f zl?Aqmc?8Ad^Cv8>ym28umC48qxKMZ?aMicvBKtM%r>-f6FIW}ZoIoH{Ha$Oy3e zm(SyDjCL6ru6?XuTUkLB=ACC}4wFm)L$nO4Cay(b;;G#mSgm5_-eO}>X+$1Fd^gZ`f#s_M_FZ>%LGum~}aEb_eM z5%>Gm*1rMOWuI1!M3&RKvlPJzBQ8oX&!?T(&N2o@YZR|*^4kSv)k_woh0s`cQT&hP zElSq!T+!?%SiYiI5WzE}ZYrSWJdAol1mNW7A8O2WM@dC>Gd8NLZya!$(ig}xVM_pi z!?-EI<38MEi(Zt{?)5q5j?zst*WJAJ;z$Aj+k%5p{l$#FgH&Znr&A(}=-A9ie>dHlHl04mtEJ!1P)zLH&c zQIhIxC~fkYN`SZyy)R`>E4?~2dy88MV~iY2x>+0CDL& zkf$BSMP5s)UCQjuA!cFm-{(I+oluAVRo><}QLjA30ZNdpxI43g273%(=O;Cp;ME1f zc;>1&>wrFhem?5n2%)~)xk67HZbo*(6#oDL7{KS>w|v%B#ByAVIV}KF>WW)+_r^#e zf4BbWPZSWBSP^{I1V@asV0)9;AItkwwS);#X1R?W&en{`18)2C^Yimhlr?RN9z)53 zaeG&x_iKH%n%e%}3mfe=9YX30Ws44FSza-?p!CS2A$^D^0C&YxGIYCKfVv&Ru?x9= zxT1`HE<)rU3D0k__rRT3O^(vg$$21_Ey=kcA~J!MkTDn_<)736;~4BJ;wdh*8?+D@ zUfxz`H$}q~QUBUa_x=Ko}Yc#?(!F#jTRlqI4~NUR^I&mF`IM zpo>&2><)hjAQUHq%Wcj(RP_G<4m2K~65MN7w*LT7=w*%woo1EKKgz5XMsuCa4;(4Y zVC^J^(J>s(?$c%8dt|Og27QU-_xn|*r+KGe%X2D}yi0c*ge1lp$pynd9+ene3}o&( zz@_kkwpQc%SfgvqP3Qjr88w}UT=wj`A5Uw&DQk3-No76!wjet}Ea0HouAf=TVk zCp7{|*?D)tHxVE(i|a%A1No(=!(lzupqt89EDr6Up4=bTkZV1qXsurgZhAPZVMnhDMOC0+|{Z)x4EFzR3!e4UANI#pQJL`h6^A z?sKio6=s7=jg9yB_2BmQ`j(@AaQbPI*juQX*=7+aQGleJfDTvyGu^S=*F@+Jk+jio zW1m`xi;GJcwHu3RW6DPiY{2gUgE1fgs<_EzJHV^{t>}yRHEkzF(h|jV-DS{erqb~z zOX21;D(xblSq`qGoG2l%1!FposQPci&YaY=ZkxN0s5Q+(CbhlRmNr{xEy%+~Zmt1x z-tsUQ0}46aRhIy4DK)Wh+nvXm+l{LC!2WGy`rr1WzL!$Ax9M9rw0@Sfji5~)<1($> zY!2ad>i#FGoTvnB-gceYW6nCu#+`4gG^>3n%o<*cZao@SnynSFe|a5CF?kR8oUt8- z2Nj@P$jn%enx@gd+?B`1Mk=yNHoP{cit6Wcxt?g{S4mcBM()|KQ2x&L-8rCman$`= zacds0saRcG!9A_g+AMKevb>w3Yo%mqi$%ayEHdd2ylHA-1?pYp4AZ1YomliHwGsw>2} zl9d?!1xNvcN#deSEtNzja=7-Wp7duYnrtvlBa|d)pwi7k(3tn4iYT-r6n3M%EYO-} zl@xzUNJ|vx;+N$@5!_Kh{V9I5kfeK4rB0Yh826%zDWr^3$74#2)RIn&e@Z!^ccz2^ z%`!POz|9!qohYWYS}+R?8lWoT6~X7;t!7OqyJb|SoO zQ;NC9il(eCz2GN^U;ZEXi=%5ZG^WZa8Y{Tq#`l6mXysA35t5GF5nb=Ed_8?vr}eEC zA!~mmKl6}Ys>XtAU4~e$6Oc&55_tSF5y=A{Ik3Jq&KYnqUW(~I7xbR6(R57%skB$S zx=52thWANh70fUI6$=R&qL(|E6~ur1x#QIE-CSXgid=#h&p7`84VcW$UYLAMtNCOd z#@Xt%n;x9iwL1$dpTjivoy1`tP=^E5O6PQH2q%Evx9a|!xYB;NPP_;qc_N93G4>;Js=if* zeI#TaYn=Fjra`F143_p+G3sSzS+_#IQmB*yuArQHi!!i3931AlPSdtH`I}_+J$Ch6 zcsPmFOX0eJJw_{h9ga50-@3+hzP%=;Z>d9bESgp9azH%0C}3N3QIv4egv%8w2*!BM zdFHQmZC_9E0@p>-#k38n*+R*A9M0fRcw}T%jR{XJcB&E=IUV^Jtv^fE_0JAvn!z>FHm@0|K$ZPp`JxV6)pNwP}|d%Z#&NiEni z#$-fy5n~La9$6l&d#~%Bn^UyCg}#E#p841^ru-1NZT-O^hI8&PTjMoeOF1s}JyBlI zWyO+5Yjbe20u{*I>NxlH;FH`D&OobcHowy^-dlANuJDr24Y+oJSm2Pma2qEX$J;qI zds^mM8y_y%Sl=UM03X~1|2RS_U9sR22 zC6`LJoIS*L@y=0-f*eYtcq&5V9Aum}JASoX&1q+EcQhcdR}rX?jOQDMa8DT@Cy%(S z2ApmF_g!##1v5T-_V>D_eQ9Y9xCP6`L}6x{DA1gGcQTQY-27(%=A2jR1h;y9tWn1j z=ju!f(g*irkapygfH+*P4n}I$AtbZh&3_utcFE?v(n!Z8fGeK);EVx|;CohFX!FIU zJ6uUJ*_4h&lLe9FUX*vAQ#(!ugXs?U=4Y_hmr5I&f=j!Py(@2jAZki zd)BC$MmM$2)IFqgAA}B`TmFB-+&mQ2#+%o=W%c7Qhw2dCPGkCj(Xqp;Df~f$5Wx4` ze=4E-NA;$mKB1-b#m&T?UryUKp-h1j$kH>JBOUe>XoBwbY(>sH4ey|OdrI*4OS9^Z zx3-%})1!12sF1O_yO$e*Rc2GPa7zLPF^)}eUmQG6y75n~FS>&EIAglG3anyXyMm3X zTjcEkXY4;Z(W-ZY2n6MFI9q>P#GNZy@HAEQzc1x;eM)mKMtBD`hm>+L`PF-w%V+IW zq6S9E_OBxMbGdz5Ch|2?Cm)?aWS;eOVo}^@^QupQz~ZYTy0|*kDv2jNRPlqJ)lcU& zTCgNRV?h`^b5qWLDv3V*=_v&rxyYa|9jTL#ifh5MEz=rnkUcT$jh190%4y|N5W$e1O_t98dE&Z%*dd6Qcyneq~1}Oat+{yq0wRncs zQw#no*)$8A$@L3Z?F;==%Bv5SaNF`W;QZ$m?f6d@{Rkip{{Y(V=yji`4sR{m-ap}6 z>y1KNe^UKCgw5o_U6f=4w0!4313vY?=^YSyYW(ZHQ*nKwXgYX_TI*GcSsLw`iwLdb z+m$>Oeexb6}bQ?%x7k3xkM6<pY%>vHz`U7Rs2OKEB5J4X;zw4Z)XDc(>H8q-Yl(yqr%Y zk2^~oit@_IAXu5CP&X=pwH29)m6ILDIql-DZdlvDHGF(2xA8mI3&;(j$8D*{2BOm0 zKqd)0`CFbP-G(~}86%!DGmKC3z5?<}>QS5xWf42vE0jjS%OvNm!V%z>hO zE_}9&cOw}GHJ1IbdY4t|4-jm%N$=yIOVl)|t~9iQC4r*1W_5VwRR9@$<(qr+y@_ye zzz!Xtb#AHB=JIVTRlL(Krij}gq;i5-oDm|k1{`3HFmsdJ0=*uWSEr#P!mO`KgAT|fbsElsKX<8)q(Q1~@ zeH4@0EUME*6rNkUXVew2c9JuLgO7@_mnE`5$F)UYXFmS`YRxXK^2y>zS3UANNAo#) z{{W$+wbXy%?x}&a&CL$`yZhLtw}K~tVq|iSCf3+t9$<{_^N{0~70A46*19`h*X^}F zj>;b2etw2UD^1t z)kfBVWDKs{nv<)EtZQt6oOSR0uBZj4T6igGCY)?-+eyBVUfEkI4J*$YNYj8-cBt~+ zFo11eC}6hXo>MN7EO*yd>LWK1jp8?DDpPJ)hWsV4KervRRC+(IHSY{=HAP)VRMW5C z)$QPl%J<6pX*|w+zzHfr4V1t<^Ni-URsEuG;nOFU4JOLhs_tz~jNM5KI?iG^4C;V| zS(!jqka9+N3`-qDj4{wPj~NSVb((iX(Itluebgl z@P5^sv(w)VK9{95X5JBPZ6R2r z!2r)dFux(A4H|;G@ZcTHPpXmE!x(F$8SmD;dak4rKzu$xZN{#%=ucm|)U`{>nccI*mD zcEpZASn`7kFwCTJjy}ezlZY9P%ehg{Iftd={2TZ7SN%88n$?!LB)HM$iLS7c`O@!b zQPinopNUu1-MzaS(dXConx<#+&*oYq&Vt+hQnQMkV| z%O&Yx&o=fCoxtu5#(Bphx8+s3?n^CF!X0)f=eE1l9%ho-RRvV0+_Jd9V9d|yz{b@M`^^}RD!-{f6t5n3CF zB#4A3&1F|*U=?{J2RIlt!8(tpFS>hFywmk7c`Yw(WoTx%nRdKv4hSN$)*VE2yE(1o zf-8t6R#HrjkT~172N?ts4iCYt?(eR;t5LPEo;!^)>RT&l;)7DTwYF=tj~<*!BKd%U zfH^qFq>PHgBbzb2lDS%_S;X2N3FiP^3;w!$7+SYaX%;tKFK0dd=&-Yar5QoGStQ;L z)L*)k%N_XBaYc)>QZ?ABsz?jt$h^t4DLd_Hz=qaU~U6x&unpl+w!S6 z^ME;aTEq-&O=l=H2H@GD+!J3fpJipMbiLk!L$uI6vp}<>Kz>JPK%Kq@ux)R>uQu5mGRcmI6AImbu2KB)z!-jICjldNdI3$oj00W%iUj~vk zIBE&yXxtOGkoj!4EeFGki)~WVRMEVRjr?h$id4A~07!MhlFSGV=_9iMNzQ81Oz2A; zF7XDukei0MTV=44Qj%q`S)Uw$H+{WCdjbzHw(Hqf%T+G|qJU@QDxBc+^Nf6aVAgx0 z{M~n{t#4(zVP-Glw~pZeMUT`7Zb9d<>`%Gv%JE`#a*JKq_4*l$BY~DrPJ_9nyV`hl zdT#FqlWAu?v1YYpx}HDd!X&|EM#CIoGV6l9hdHis)tY=AG1R)godd|4d^5X7mjsQ> z7>MzZ2m~CQ_X4^ezt+7_HEFJ_?@TQOyMBVz$q}aR7)qxyHs(8+^PHXs6-7>+;-if& z_TpPDYT2bfS9&+Z>-d;EfXu3eUkv#eW0JV4-Wct$R=q?JOx{>WE5XIkzajotDR>Ow z{{V`z$iT5?z{?z*=kw>{te(-rh9DT^BxHu<3iIduFMf#SqCo^&`vit(1FI?+knAQ@7&`)+U1qg3ww)q z4J=Ybs*z3WMIiBvx2FKH1y4P(&0gMHT-@m2L2osz&*m^jj(;t-_bO6F$UJU96_9*&fJ_8A&3MX`%fI=t@Q4pZ8FuP z)9)75Sy= z!D&M&k7EL(FHaYyDiS+s!iR@l3ijMRgUu` z0>>Y3bK4m2`DU^2P+w}-cM9wQEKWvEa*rE2V@lL?+g`TmOJN3$aUikOt&l4HE$C81`D#dx zIg}Sw1c1vbjuchr?Cm6by4V{iE-ruJ`Pw_Fl#>FMNaPB0h1`-b&Iu$cjBh!qqv`WQ z;g5)BSR;x*ptW(llm(Xh+y)zusfaEBA1#55)NBDuy7oJW@b4AP1DB}2fN$!!6uW~E zc>7XFURK3sY;LXMYhcqn6=1 zGgg5k40g|I(6bfgl@?4FZ~Ipy{7_9V10If9o$!kbNzghf`%}8pOo}x73)`V+x~1Z> zTBG`RZjb^!A%id(AP{lLuS|5WMq6~xN9lbLtI2I`sb1VouIlRw6L65lY|U)}&M_yJ zCUB=1H!2$h5HDwIZw7STBSW^(pt|Z!F-)(kUq$BmH!~`k{J5mdevPXdzSDynus)zw zXuTuVmO5`&b=IK-*B2?J-b-&i?g5^5iFZEdBP@wC%1=2lfs#%tzHaO3oHv6W7Qw8S zda>$9Z+?3(GI+VwF?EkxYMQmRv)NfsAX`gGB=Zfux+q9u+yKtV)mJzjt3i;R)sfV$ zH^@|+);0NTj1oR7UW?>f={#AS?v=98lv9aZ5hc0=Z=6K%Px1ZQRnR6w(xVusNYI2)MHG;Xcco8^QSCwyd8LeTL8sVKLR5C9O-6(%cBhI|Q%O1~ zqNI{Ns3)2!P=vvt@j$2drjlU%nwJD)y+CtPKx#oEtE^#96(TvRStBPsg(Y$hcXRPq zhX55+O1ftXr{<{DGILT=G4V*dn4mV5c;6h>Z%V!L%V zQ69qp@_(gY426yqv&|TLNM6A3r^UOiC&Ar9(#lAnzR+)y`6Xy0k&Ud|Qbh%nl>$_V z@r?Pk)Tiq{jq7b|sJdbQ0C?DS7MB+emgpMwZPV7u$W?hkou|t<<8u>UK=5a))22Fx zX)kSVwH+er#^TQY-p>X@1c*U!2q2VgvJBv52P3_9{{Y<0r}($gz94COifUR`yL}uN zR}9M%gq2JV&69}3D41<;!qK?N0ZI0HZEO5C^7qK{>VFI4d|!mZ%}dLL7Xn&#_<#g7 z`qAIM7873SyM0TktObPD(^(l=XSkRuh+$wq8aU2IPi@15=jSWo*M@pduYUGg!drCx z@@g*~n)zl)C(K|vY*KN6P=durx6Mz+UYn1ldbC?Z0z);l(wL+3PyIJhw2wv!INg!X z0QTm%&rNk+rKWg-*V=uzn{_10>tZrXGjXs0JMzM>nn>V~2aB`(4=?2!IOf-A%b;}t z+;lf*bA4~MSJic|5&APw>7Jp#>TM$X_lB8xu}1^hL$}UFK#uZmIgUkj#xO`E9>ix% zxwUxk*?%tzz_{88ZJT%m`j;Ra_x3#3Lg*g9j`qg(-%!-l>6(SK(kwDTffc~X40iAo z5rX*qECx8^1*++Gy(_GJKB=xqv1=}6iLIequ?mBN+m>H)NE<^OCU*ABpUcb6(}U;V zugyXkC2RZ%23i0ZIRh28-~zOZj+u|Am(7yVTH-com2VIY!yWJm&N18A{Pv}w>l55d zt>5C^Crd>Uyo{{t&I*KJbGtYs4l+H76+W%{{UUFtyq+u8SfT=5+t|0Z5A!JlBkVyP z>no&K3l5f&HMh06)Guxj%+f0tSisMh*lhri56L`mmCpm?PQq@TRieyJZ4zK?r|JvVA|uB$8dSYS=snK zBi!1{W2;*A4x0F#%|JBsT{j z(d|cW!`I}wU8}fj+ayTjGXNTAB$I$R1n@sT{=k50+*zB!^w#;2Ac*dj$@LJ~Bz!L* z0r7*}*LUkZFQB|H)UR}oqto@BZ&+v|kOYz|bV5$xta8k!Zqk1iGk`lVz-hPsA9bzG z#h#lP>5V|fCrP!7sEw@52;CO&ieoq|Rf*>q;E_>uO>4Ov?EAuuTusu&`iSCjVWZ8{ zVV&>nw5uBw(u(9;-`q0B=Q+lGjdf^GPxwip=^F1*MEb_De4_5h zMPK1Z9BN2eZV4TWf&!@I202hhaN560+i6ydtaRh7jK#Ej4V!C2o;1;yHDzNz@|uRi`wccv zLyE_7zmcV&b>Cd-eO<5K>bjlmx3{-aG_uPp5=kdKd;!=GVZ~`zcMT!rlUEbFGv&K` z3bL8KSO*p5rI5UmSJSl6G8TrkY^gBbaoAMVf&q^8N=6`KBB;9`&a(4T=yKY*GUT3W zHv$zaSiOFdudTcgobg)^`?X=%}Da2*whHedY;JLNt<&TLmu7gBmy!h8644Q zq#u8^EQ)#Jl|@NEnpw>m%@kS_2Okx2X=gR;@k4JD&pfUpjwsZ#yAyx_9Fk8Mt7WS3 zmR-9^3=VQCZ9UK`@~Yh0$!jykDwyR?85_Evf<2Ggq6RU8K~{5;a!d*XVVc$e@=Wa(_jz1F>`4SMOB{%miQf#l$lL*7KqHPud9F60vPL=Df5)2d>9uWht7G|8zi5vS`m;{gbp2nb zXH8B$LfKXgO3p=lHuC)jC<3C0kpNI4AQe)l7$Uf(_K_{Kkm^)^RqJgt_KDQ`86nf` z8SG%Tj$b0)S1;!lIVMB+7?P>jU(Iu4pBjjg1%TZk(y zyn++uuxuQI>MFp38#SR`e$5&eQD`fx^uJnLG9}oyx{Ih|OCbm`?*&;TkjQ(20s=es zu5=uB7Z3Y^jQQ>L=(>{dr?(LvZ%^xW=2GlIWu@Z?zBBKS-P{*?qo7OyiJTb05Mj^ zP_R`Ew2i<3as_t3*(X7JO;=s(>-34V{RR^Y8>5tSGBVBS{{ZtpgS|HP-#kS6b671` z_E7N`Pt^^*r%v2!R=W~dbuBFnwsWHZLq{n95zalZK_1oUE53)H!A}5Y(5{Z14!s1n zw>Rf#l0=f}CU=W=3X$c1+recNlDw11vFwFAyJG{`1L;ss2Wr7;m-gBsTw7^bjL)@Xx|9WJR5)M) zfh&Y`x6 zA}?ih=M4<*Ofz%o!=6z4Va_wh(zVxw zSjL-$&zqMnugY+w=k;kQ&xtIuI!eSIWWkut|A4jE;Mi4Fph zjBH>E@_5_`#a!8~ypCJ6l8t0XNjL83cgJG5G{#$5Uee+>Sjo!pS9X7#3}+;4#|E;R zZ=YjfA~cX8wkq;PE0EU%b}*cBxa6N~clNG@*Zmu)$8oPlW12?OBzKN^a%M$G#xhQO z4C6dkJhxZZ?5%7gjh9f!juRYUI;bVLWD%TX=Q$(fU{yI`!CroC6ZBISStNtY=bgU0 zf1FTlM&d~%S6IDK68PYp6a5B9`Bn9`)UW&`oLk1YbLjwmnDN+fIqi~hSZC6%BfYt^ zkS)8zn5RZN)la)C4sb{Z-}MHXTn$3pt8I=&U;?V(f&k8c%kP?#WIL6LIiv*hUHvY% z9{R1?-|42%H5-kwOK{RckM!~gm;gx)k_ZahS3GwWg42zjn>E$bUP(UaSzSVGC`ddK zdG^Lv*x)xcdC<@uTTj1%rCWK3X~8R%1|cKu_`A-?^YlC0C!c^cm454Fd47DeLLpuR=T-@^4i+RN3@Nk5_)8ZJ6T9vgYHRS+#UcG zv`!bL?5!r7OA+cfGP#cCD}_%eDyOJI7dgxD*#jx^rmZwRCr@Y%{q4=YtPuk6+T+)7 zslh-76mx-+LEz&gRySMLF7(Tn;C79mflcg1CDSw$)tW9-d4~*PR|j? z;&Bx~Py@@SlIgx0>N;hMU&mz}$5DGZ1dOw71SsE#@D!^RQVtv289T95`oHS>(?Yww z(C>V?#lG9S2$2z|+xYV60h1tZ`l9mls%;Uj%uvlbmw0g-0Ij&OU^BoO zC*vpDvNruwA?l4!QIbn&)#dZnB800TKBZ8^lA%s}?)UFUH#2LZ?Zd8yZ98K)Yl#=N zdGGnbwNCd^xz;)^!fnPls zw-K1)X52!4D8qC3dy$`mp7ou_mjz_j!uT-oTmS$&r}*|&we*txG#4^QvM}CKdUmO0 z89a^&InO-v+zO#GxsefuL#chkmnZ$N&(F6NQ_fpRu4h?PEKiR!a0RoTJwy_5o-v<# zuzsLPyyRJe$Vl|RHla7)j7%aFcj&M0q^H3m@WqH}(EQgvyW2e7=MNOdsIb&rD zxNI5r!Q!gS?F0}Vp zW|Zv%BbD#R=bY#5ilm(UD><-vS~Fiv-d0A=cQ8pDb8J-@$83|^obk_Z$kgm@qj?OM zGP43fWJM(KG2Hj>&11Cqk#1DnhA^qgWjJGw!?DNbipfPPPFY|Fj2~FZBp!2**Zpe7 zj%<~(2?K+9#@h`37FNa=)23Nqk~Kn1qHwIIAmalgBNe;pdizHtmk?b2FP9OxU}yBb zz-|KPZZ~>P32bwW@CP`!ot{$!YU-}ROevGV_wUcStj3LasLZ!eCD0pOqhn^=@NoY0O0Pa(JuNsQ<0~as1RA(S=&a?JS-o2kmQ);bY)W* zJ2zmXu-q%jTJ?ii>UVbGyv2%Ej5{vUa7fNQyLPOk=BZ`>0Fc1O$|Z1{u&e{}TJ9YJq3f;HpZq>h%6$11pywleX!4jAB&I5@>!YMw9khl`QgbiMSl zUt7yEuAii7ut#eG+!g^(l@b8w4B!LXdBN|VNd)quI)ZR}kDA(gH>Oqfjql; zOA^Xe-b=;^Nh+mOg#ZfjHnY^1nw|aC-Oa<#cXK04EUt3S>~hRc+>&cKZ#GKx8lEi9 z75@NMn2<1Ve1`SDSEYPI{ic6%&8BF2J)V)&FlbimYaXM2Y9)uxS7OpzC?NS~AYww{ zwkvI1?XFqkFYO`L{v=r5MXYM_&1XDTuw6w8>`8E2TYxc>QvrDmR~*!8Ty5Alk6iv$JDZO&iw}CXEu7PSO+fL-`O$01J>|yJCXqaS z==P=DW|FA~?MoE?^cqPbm$e3pib$h0QlT{HqKZkRXzfmsq=XJ=JW)kHh)B_nDdRMU z6%->F6j4hQAxfB}DqwL_0YsG=D)RpTPkRfNf=7l&RaVRrtgFZe@hKxb^Unt$@+#Sq zq*_9VaB#4p_oYmVDk?~#m=!JuzG;Ax0XWA1RE`v9y#wcpk8#PXHc(8qR~@N)Q_ee5 zCnlEZsSmwW9CK90K{ZiH!Q(X`qE$v20k{H^5Utvz2w(v<7hHSQmDDQymU}?d&YgJ_ zr4m$D1jQjcTxXSR5O8}O5ni{@x`(8?$3U^L>CdFobi=3I!F!hjS#C0nS9L z8T6z}>&~9kZ)RvLtuNr3c_UrqM6v?@pMOq9J@Z|C z7N^5Bb}Pwoz8E!;^MXU0kzG%x;O;sur@eRoE7i$lDPqj4;`$I(eK z{{ZsIB*xo-QgCtz#~SJjO-sY>xW6|F(O!QNEak(;E_Sdz$YnY2T`R?8)jD^o`d6)O zETMRc!z4Fwz|CcFZP+Xl2JCmlkrrMK;NXBk12feAiUF8 zXZ*74j5_b!rGaKR$7Nxb$8E*OwQas3TG&jQB+*GF+(zM5q{7DejW?CqliV_Tw_~2% zisyP(v2kr_CCe0+ZN@PBN!Vg5w0Vc0e3i}!EBa>#P)Rcz$b?q6%*zZaM)=CAsUQLB z_XO?^4n}eBra7D8@^`O&m0FE7pQsW8fqzl}_5T1%Yt${^>OBU}QfU_6{{TxV1E@f# z$i$RzLCGq|9ls9T9t~ys`>vP4?I%Ri^qYM;O?vLiJx1eHF^Sp*lRi{YhXqxF0L<7e zmH-igPLI+Wt@W=a2|1#?!@QziVB0NxAE*eJaA( zMJG?#$D~gxk5Z z*LtS+RMh9by1JI;>Uj*3%`=vH7@U?Q@<~4R8$PhUuo4?3@pQi zE*5ynloOs~8944kw{B|x0LPA+xV`9ZiPx@e<&I{zmcm(OLZy`mk`goBe7SalIK+7N zu4Cb6Rcak8)!J^MV`w!d4q|%}rG?aP3OqyrPpMg#j(M)d6XoK6K2dT$J z=3DDVgq9c8qlZ+EsmuMAEf_U#i z8KV@Glrw`uAW}JQekzu8iZh&4hy&bGIZ_x=J-DV$NB5=9O(VrCO%xvVsnWD1PZUrE7KJ1k zHJ7l_?k}wGZldzMt3^hTJgBXPV54f13-U&GgMrDw=CN@M)^kbKq{vv@2@Yd zw!N(PE@HV-a6Ne{*%?90tW9zya=XtQcE)P*=t`46t9BTip2zg7Mup^7 zW*JX&oMOF9VXYWmVn;d0DvEzZXpOfjGSi` zlfSjKA~FndzQ7$KOoGJlLCE7DsO`;J&IT1>q>EP3OC{v8xn);uk}=;tzzW>@Q&@c( zTi7j%?UvQ^Fi`CoQM-2_g*h0=85_7ctvtp|4AyPAR}CzDD;y4RM{)0u?Z#^{(lhM3 z)Ffug#brA<$D;)bfZ&V~xTwMIaakt;(VGig4lH=3lun_V^6BBZF?n+)&}BlwPd=;; zbN3vN(zWxb{Vau{!m||x-<_QEgWP{FZ@BlxTEfy@+)W}bRy<|F3KWya{{UZawO35- zncRZMCoDNRK>54p7TY&o(9Sz2N?u#Fzd)=VUSeh5J)2x zpX#3)VYbqBQPaIMr1YfPqN?iC=+TvF{Xmu4#e`|Q1jioK&N%!y;QH2~A-7exQ38%7 z@{4+iKAhyAYN=~`G}aM`SujE13=xsH{Q>)mQ@wd3IGI{yGmZi`$0 z0G@f>9^xy7!6$}qOhJUcFit55x`gYuw+vZ-#Ks*8jare*v04h?#%K)jxsFI0l z+jP$q`iDW)V>TMX*+z?#BrhsT3XI?t2l5y=AKtdB-w-vw5+^ft2C+TuwYm$3yT7?| z@r;GS9PZ_a0R~1-VS+~>x7480?CzzJQs(%T5VlUm{{R*SJw$QejAy<<8D5&a`q(cm z8Q0FajRaE^&RrNCnD3S!{{S&j@0!IfmOADQqB{QoD|9-imvXq>^!^vA=Qe}GO&e6P zxRz`5vGb>n>BGh#H&RFF0YM&Hc}2lv&CFzCvmQ5V+GkSq=Sx^>y2@HS7CZGQ#k203 zREZQ7WmW{J1{}UH!c1{mKZJc#a}P=D+F)B7%iR*z<5JV@@8Ast;bG0yyZVDN$pXGq zbGj&)Z7Z76Ph3mm*Gly*wf(KEeOadY`n{~y#iKVCtuxJY4$atO8%olt@32Xn?G;Z2 z)3j<_@;`6${yyv2>3D#OMz#4S>$XPT{FgNAoA%TT{SCUurlg<2RAT!#Z6p$TVsN7; z1C9V3RtoCL>@Tk10bJX@T<`oqamIhhe~_%JX}21LhAlkDwN$$+%#oA|2IIYof;k8B z?l?7pP_kcLAec`703hZw#&8eM2N)RRx$j>_4-1#6E_9*ibM*Ormg}Kw+IFv}jW<`E z!!McyH|(3XFJg`Fw>Th-?ZE(gtEu>T()v_0-zALNXHsg#K(N!@++y1K@Uy z9Fj+TbHQz*Tbn~`9Fda8dWX((K0f3R*N<^k^o?F?DNLz3L_xREeL!~O^U3`)R@tT9 z(d)byG|JF?`mVj>my2Ct(7q&HX>i(FT4`QH20JU8Aj^AhQX+<8>Le%lYA{GKkX3fz z)}!JVPg>aP?SG@&#WsRdAR9-FgWtH8n>Nit# z<&2TZXEEmL$qUW8HYb;eG?`P~0`2@H9UG>6P>)Qqi$&LMMxmm|3&R7!9AfIsw4P@8 z+>cP)ThPZCV}geaD`bVF^A(19PM1{nYTQh3Hq*U%k1mFmN^Jt$QMHa+z>wWQQ6hN2 zXH~#CJFp}f>;U#SI2`EOltRMtV%IUuV3@}vglv^3h9@4xo;&mQ$-7;V1%XKgukJtoR$isMhe`l#C2cSiqM;7-0>7r9(55laj=+@0@nXs#*YOBblXK z#<9$*rGau}jG@jxxyUDsP|?hJj_K+2(rTtMJmsK#{#d~NJeDFID0HV! zG^dN*O;jXd_K4s(IXLHYfIiz#zTu5#TiC8I0lc>l8jPx*H)L(k9r51-Pp6B8tPSeY>~FL(#Bi{?}45RcHp0Wa42)Y z6cswj+ZhqYFy5U$e>|?6;pbTD8h25}HkB`(ZIdflHa5;xNmL1qq_!1D9{A?A_Ui7O z@k+`KA6vJz&~Hkta0ua45z5E$Ce$91$JDNP2P=XFaCL%crTYCjkJC)q3mE1zoa`iH zw;i#OgNoS~TC>)tEvs3`FSif*h!`xAVR7o+!8~Vof1L4ETb#PO9xC$B3Q#EYKeU6` z?mWF#Q?2|zz3Hjqk_qi~dpY4P74Dro%LJHhjG*lqD#MYFj^O0#D-40cnHniWVYu{; zct2zL4m(#^>Aten^vg*t`gVIwV@}%vl`(Aw243na~kP>5K z5Rx(!g~==1l5$Tc>@&q)Xk7)bYLQQUr+H9A3vFp`?qjxB<1DIVVZ49|AZL#K>$3GH zR_Xm$E%DZRKH3R{YbxF8_sZ7~Ai*l+D+UkSBxGl~#w$geOVv8RR=2Xa*{3s*w)R`q zN+<`ubv;GG z5~`U#nG}L^k_SIw{#8QS{_ff-Ryo81LytLRDnKj9KO+bHsx%n6VXcgoJMHAG^@)}S zo&<$bRFDR3`?lkaqvxLI?Np$SHGzcEk1|Ez06jkAKK|a{tyM6h>>!C5K=tu}Px|PDDggSn}e)12s&arw(0n0G22OVATvuE@x8qu0@&S*U}qfS zx~7Rd-Vk&l>W8|sUa8g6CeUzFd2Qp~eCx8OK3f8wT9zBK?pGT#Tmw{{Nn%)MJ;!>a z(Q$BHE-|Ve3u2*QXZltTJE_Xye=(Ystea$3mvf+CSwmov`QwVTn&dG6cC7A~s6(V_ z_v>}0r1w{FEO1G5hioWLOsF{Z@BnT=1-Q-#tdL6Z&3SY8!nM`4>9r<>rC5{7g&NgZMME9>sDP2H%eR03>s9@NhGE?6ipy= z6EtlmHx%6jO}FK1Lknn!EK@Q-=DH!9&TAcgPi3hv=f5<&3T)ix=9X%bwSaye>zzVgwRh0qhJol!}xYa_V znxszFIK?tKQU=u5wrcLya}mfmt3*E)P;vQGnrkYz3lvvf=`R~~9-ZhNd!;3bH61%s zRS_lA7F$Not-5o`965B!t6dT zbd-G!)i#>OkcKXv)?kA2&Q}4Cl^|9G9AhAQ8y?|@8LSqMY;T~F@fs-OMc=<~BLM); zc_(t9{Mx#=h{Fr!s=?GN{hQ^=xl;F^<^4<&esnaZ_V+ z6}%;1X`dTjA_$Ok!5o~9{Eu;6XETxHp1GyublQVHoHsTxXQ&b*WiZhjD0-0J;z zqs0B;0KWv{ zf=C-H9IdtHfroW?+Ds68f}~_Htn)a!E?(cyRj)?T9=OShZ~%9uS^{kq`&Zl{CZ>MRNg637TMQNpC>WYcD;1jrvJRQK4pbCjbCBB%T1T zQt7=f;Z4?^s3cuIs;!QlZu8vhw;HDAj1jR5qDB<|01%?800}LENbTpX1FZF4ir3@S zG<&wNzlbo7;vhH2xL}neaBxo}k?mH^2#k(nzh%zR@i%LmCxaOUyK`U@(`)(S$7R=g zOZH>xO+x++Hsey!G>vLhcTG0t<=*EC4&qWM-vfimf9_<}p%Yzcc0*5TWCdPiS8|)c#tN&2`8#q>OJcy|`l&ID194b8uXkIVtr&}3r9DRK*#%CQHEvynOCwj1k-wS`ZpiC*M0 zyIk^RnVEqmMkMaXIPLn@GV0Z!2exaQzE`-8O@ZBG891nsimc%7=7vvh2&gTft8aW$ zKIC?(vS9Y8FxqiROcfk(YD0=&G`OKjRmK*nmB`IhP8SD?i-J!W#aMPx3ZG*|3=VNY z63y112Lh8V_LM`MLi_axO4$|GrUD@;r;Z~=N7_AuxxgV)e? zmCc@df2Sj!=6kulF|EguAh%UM?j$Jq9fA|sR%gWT6(6YXH7k2dq+2UsWsU$dhTRew zRrc^oji4#u1D;8)k=2c5;D3kuUq@P>Hf=5imr`gSDRF-(22joj1x6M=JES=7ay-4% z`m)8Qyo+cA_gjd3?N9^Y1x3Idu1<6Qweu!Oc&@POi1A62Y!uO;N4wyvKlau@` z2D8?>h`PiVjl(M}g9VW1*@(zF=j2ukO}xD`%p$mqEU51=pDwPT7AndzN#l=jc?5SB zQsra-4hcK50sQ{}-xZM31Y-2DK&;mDuF`lU!r*81#d{wOv9QoyU&M3YlB28Xkz5y- zNs*>l&`Bh0m}rl_-15pkagmOFnCnv+9k9v);1&M>Gg&K((`hJ@2H7NMaAhPAe${gm z#jd#$BzCu?cT6hGN$rlv-_Rei?|U3$8OZypUIWfISP~xILnLP<2M4kIzx7t;c_o!d z%a$Mp?n$hL{AOE+1sPa28+qho=RK;F7IGGO5!DK*aC?A2$j2NW2llN&!mVa4%_=in zyhH{n4=NWq2cF{q=kxnkccsH!U0hh)s!JW@O$d@_2m^)w5iD>991ckX1m}vvT0$d0 zoE%{K>u1t5M!$|pi$?{tM>0(*!Y+M=Ib-$!pMKdjOwIwMIa?D+b``zGg>$469n^0T zWGt(XOoOm0!-6yJGC0O+&O~9k6+mEd$prEFf9|&Ey2jSdB-3rQ^|`f-vl*_Ktgc^Z zVm2@d!Y>DQG2GEUi=|v!>Jvjf#HkcZa<=iHR#BBwtmlJ*!B z;1l*ZJdAE8wS|u6S;TH!JOHjF2Pu==2e*3G$vN-IXLAE8x~scL#kt;9Hy^{3oE|g! z^H+D8pPSS0FPx#p)Wu_7bCidlkBYXP)gyM01tqra?Sqcz?N-+|Q_rMWyfHl4xt}bE zPdj1ws9=!id&0ArfJ0TXT(nm$AdN=gIA9!c+~9+pky|~T!)p!%R`_I%JlP`zs;C=5 z$F_Yr_T!9a0|O47aKVALn6d&(ENn;R+x_OY9X85qYe!VEm7aEuLOdkD#uVc`k~sI^ zb5WJetZuA;I{cSN=zgrd(Yl*W*Dj=r^77hb0dF!^RY1O%`3wkCo=t1sC+RnS3wXrW zT{y30W|F3%nabj2Gg_v zHoAgABh$en80g+8bPfLiUwCDq>$)Yi&D(Unr1tux2GUsYhGQ?(M9eWHU`d9?3hdpD zrz49L?bB-8W`nSMd42FbSAETNu-kWTR<+`{hq7sl9~ zZt+JZPVI#3Y!F7=9!buV(>)~()E;fk-My%mBXI~)8Q*Ia&fMg3t-;67W18H&0M@3w zm#F$q=*>2fsA-oMq7e8CH(D8}5pxk?z}A^`;6o(HyH29!3~7cH53~TP)rc0J=Rx z+V*zAILX`^=r8EEZwxw(dM~N2{;K9{8#(Q!v7PM>CJl%ot0kO(*g9G~Vj2R8DwP5OOI zMj1~n5&_=8Ckt!v3!rWKCtlLJm#ZxeuBtBgSZSJsPa9jLezv=jP6*^}j1k{^e<03_ z)|%&3_+!=GN7FFf1k)kZC6X(9t7xubWi6DJF)FYD(_?O5#4vf{v)WfteMP3Vd1a{0 zbE!N^S#=oGCOzxsdiRpY7zcsrFU@S-72>VeQmkDq)LK-U-i*j?($;Hut|f*x^pS5P zsc2Yk+@KN289eh{mStK00FH&)c%afhmQ0)f0O+4Fb+!Rqw@LA~<4M#W>hq^`ElNWR zLIYZxa0?+uW8|`DD#Q`no-lc=V)(r}AW@-cDV@Sb`Zi-Cp2|x(#yyJdKP1*4#Xgab zp}Ol_&?CNGL}|BAYHn3FZz^zdxi}!?5t1?v2^`jyB!?w)z^i;`0bzXpekZMo#xo(u zpYpu})7?iei=98J*?#M{exb41G!3WP2=`f+!>5-ThO?mf0)NZuBMhm-3hqbzegU2jwhEfhe zuT^T%XfkwCYTA{Bj9TuwX$94<(Ot_lD9pggCf_Yl1{Opqx?&uWl-NaSjpg71y`KW& zAFOYOEtv+#xbyVCqVwJLocCJ4k{KMz=&D-sw6Vw}sl~K{~upmSsrF09fQ>AgL->oUY<~Rw5fn zbtn}fad5VdRRNTP<(y-KoO*}1ZQS;*m(!jRbuO>cw|B5u-aW0*WQfL(hG>}!E(aW} zhaWz#gHyg-uUVkeOCcqlL8`#I_g_`c?{yE~cXxp=F5+KQLZ zTq(e?eq&1ZKL_tmjVWHFbn0!-z%K4) zM3Ki8t+Z=B<>b;VQCdW#ZT8|#{{V^_dudJ6ohb>k&|F4tr=BHSjYd=?hm!>CKu@M6 zVg>;@ZfdgNe`?t1w0by%u6?_({FRpK3s^jA>#I#OM!0(}n(56sEhN?NaPqaRa}Xve z=`76CDx_ncQAzGcC+ohDZkp=dYeRzIYNt)Rn%Lai#=uA;a;5g3K;u02uFc>dQfStF zG1VsE!w#XQOK-19<{@KsZ)~y=ZZX?*?{AUr4&(m-PQ3H1YWI4Eqjz^T%e}?RGtA)N zxnq{jK22o~A;d2~#AJogwr3t^ypn5RIt6InW}R^OCK;D}PMw*3m=XGsSq`e}4NI(b zL#XQ40^;Pw912weDn}XwL{(4%s;Ynh6@xrv;}nU_9&5=NTmfj?taD$x~%8pTB&AR0+;#=b8;GQ6|&gLXqr#)Q%`Ld(u%w6!w~8 zK0AtZN)$1$0oJ-}RkhaQf&7c< zH0UIU==XCQ$sCFf9|Ueuv@+v4#(e((6~}IWTH87^u62Ho)e`$g)UGtU#RYupSxPYn zx}0|h+#U^eBh_;bNir9f)bOrHne?ZX+t)&T{{Y9%*5jr0_Nirm8Y(NnEb7g0kcnLv z05}RWfq|TUUzK#8mDl=TsI(i-jp}&5i&g2D$;!HNOe5=kNPoT2|_0la^*VuIO4XBdv@Z+KqRi zm6nV4chD{MOSaYe{{T#bPY2AlZ9dvdiKc{;waPb^sh^GHj(Fm-8lIn~^bbWghtjuk z>q~t+cNe<7#F0R^4vKLcQ8Nb#ADFNU1!wyRTam#83psR|Y%g@(BU987Z*6W0CDp`j98ku`o!QRi-RlenFi*(D zJ{ZNdwdZR*uYRP4Nk27i!rI$813T8{x`1%E?z@LY=pO`bEJmuAIkw-0l^tkLBOvt-H_4~jAyYm>n|LGBwBD}lykh<1dg}^=~!H*(s-<|QdupM_9Q2CGL}?+2;`{^_wGAZ zS3{Am?rp825l1YHv4@euDJ0`8%y6Tg`~WJ>?!lleuDe)!p7m~JNTO1#yNKmi+w-nq z_*b}a61NMy_1&+GeiihkoOe3Dv1iiIKhwi5m8r8?qcJWNn41H3;=rH9oPui$&>c6{ z-Y4BJQNwu_zBGG?-dMGbO5SMz+`RcWQel+j4t*r?oQmZaI^Esvy1_Nv(!ziRNhBpy z4!{C<#Zrq>Xn|G6PkL^6amh96xeVd*vJyTY5cLhkdR^bmd#|ecGg5x5xJ?hHdUE?r zx|ix2TkBmt1iQp)5#=~jATJ_%7%^<%k~3T{$4?3RTS3z=I)_BoEF%woEuTP!`WvRU zEUe5UktAkyLZlMvq;6hYlU!Z8?^jJD_ej6E8egCFKSk!ZljiN)=4{S4ZR0r3IIK+C zq?7Q+H5bSnxqLf;YP3&lylf5e`-h#ApD?#BqwpuJx@S(X)ipgKZ1mgNB8oeyF5&Xg zq#2Az+wmSj&R-mNtv2^cmfl&S1!h=C4W*G^o8$8>yK-H1a`AY1Xn&xbHgshkprkEFG`#ccHj^Q z$gMJAmOODqu7;jw%)s`W1zXa`6(TvsEOA2Eg*fhNs^N}BQUgDgRZJ)+wN0fWlcs-4 zPPjmGN1f{D6$uXf9P#l?*F=S-j{N3=G2Z~uj8Wg3f|`h_1)%+@sm%yLQfg?V{i^!Q zNV~qYy19bZX|3&1WVn_%%PcBbC}$(^g3Ja_xdYyszykIdKJ^Y^bfwBv^z{e{Va_D+X7-Kf(L9? z9aLgS$URowJNX_6Ue^7k`tMV^)NHOGFj~i{*jeATtnkFkWM+vZF&w8QcjF{>@6Kz> zeM_(2>h};saPgT~`EiYj3ZW;t+5iKb?!30tZhC6Tr+G;8Bz>Dz zoPY2M$?h;ueAk-wIPLBH$c&Mp3|Ox0oPd2qep#;v!PyfSTGs4O=9jD2Iqm?HxC>v@ z1a`8p20MlWX<$y$f5bh`d)2k0n~T^{V*#1A1;#VSALme#>MMsGL(N6s{GBJmma*4nO;=+TcQ{w<|XiTVLtz4cjVC6c{EWl)>d3 z_bNwj4lp|m)hRk=L@G<5)UAc%pZ!h(>E+6&PLM1Do=g}Px)5oXROa} zXj08>k>ibFj!5B8HFF_sha+&~aNv&hibuX9@GVibk`IYtj)iGyZrY9P8!h*rCe>ux zW;yxpBOC(W{ejL(;x4r*uV#%7#ElT5MH1jB-w|b{-}fw8*JXt&ea#?x8(%Xq^ChQMgZD$B<|`3LP@hy9l=r+*OiOAF*% zh&4v~S?#w19vMMKM*jfhgp7PPYtH&RKA)&Za~;DWwklY}zy%otDe^%hlbqL1@Sm)1 zdQ+>cwdk94)^{m$ouDF>V8D<*`1Z%OdHyRId=CuXzWo=V;nD`ZxBeEh(A`o00O9`t zS=(w;!uJ+_T(-BC$iZG)d1Gj$P)Hy)*El3#5Djd-aiGU*2AQhp+SHoQOX_Pat)!C& zfvu)ptkFDVksAW>?g~onmm!XL%Kp*b7eS+2Mb>%@-~NZxuGwbOr2tE(%IalE$pr3F zyS9=Qu%4wH6KDHA>ag`@nW^}5sNEYGbz7*ev`f}NQXBBC7>$^mqRbf=c>FAplg3E& z96wgc;S(_6`zx=do!V)Rl3X>${SQOw-=fqO!q)Gmn(p3z;F4BI04ieuZaF*>cB+Bg z9_O0XwJlxeEh#|m(5mpoMn(om{{R;s*1BciOY0g++-bKlEwbLmZa$hf4Iz_zA%87> zL0e%Rjy>z2%b-hht{pvgM{u%vM94OUBRh*{upFHG0h;uUa4iHC+SkV?haZM0oF7r{ z{$*e>ETcZ`asfPj>b1SJ^3D-aosS9?eMj`Hz4gwOrw5MG%-_nSgr4R`3c#KiBJCrv z;IXX#08Cxq>wP<`!C?|v-P`$d$t9`?-R=JX1d2!-nL{*Zl6}eGQOp{+F=^WQd_HS; zm*c1Mu}!AlXqFdnOjZl)gB&XY$Qopi@wVVU0m`7k!Q_vc>wOt}rub9g^c`ENTio92 zcavRdx=Y0_!(lXVEB#MB*&cX`uWlIkQCjwc(3YJkqEFP_befKlq%`r`z+tdyE-sza zzG7Es(5k4(V&HxdGrF`-7kp>dd`P=6OM5(BKWicV+oK5px3Y`?sudIk-LX^`##oG# zR5`)e3v~LXaLA1x`v|J)2Kn{!=(HVXCqHxoQY+GBvstcN97Duz#eszFjST)iKR z=j~OYm65VI#aP$U(Wcc&rjSV!sDmcqghy8o+Nf?QS^>TnvcT z;vuww+wocuP6!$8&1%~An{I@87Hw^*OcbIoy%hs3ol3BVm6Wx}E*O@~y;3%JQpcl1dEi9FocaIj(ipJ~#Ddsjq6YMb$dIme&)K zyKdx(S~V^s8x)05s_X)Rg$zId8mHq2h*n)!ePv~$SuUH?x?F7qg}yN( z{u{-I5J#vu`tAN!g|2zJk5m}qh_s2d5)>PVRZ)(8@G?pH8TYNTMzWVf({EPZ=E~<( zjbp{5s!tqhTN}w6kgzxy_TDqw6=7+l+(mnRbS=cU7nbwEZ1ZG-z^>){zm!-HyAOJ? z>50yf)fRFs*O1ZNC>vx73Im)L$l#7LYfmvN)4;<9q`BiSBwo}H@}iog`hwqEYW6b} zn&oE2sZ`kL3b$}}=Q%j{1CNfq(H#kE(f1m^Q0uZ!A5Q4OE@7Q6zMgA~hKR{;Z5st7 zNW=_~0aZpm!nyvGmNwVzsJ6LpZs6`8M+-Zwj7c7r+myjMBiQ2yHPX6{z;*usgIcDU zrcVqy?vH-j-NlS@89uQ*g;^aK^TH{RDbE64N1;w<8VhuBm|&7-o@b4YHqGt7&ua23 z3DaIT^*32~rKSsuV|An6Y7n*GPw6^(OB~UO0dgVQ#z}UDK4WG=dEHffd)Io)!wq)p zr>w4S`gf-8Ve>3)?V@Wpy0&bB((oj5SU6)LIUwWHisjxOb-W!TF0T%u72&qjtBTV3Ag;A4% zlar2Wp=H<6cHvi!nGY?^Y8|NdOXY_oz<$+rI*-atPx!B3Fu% zS3Ik@Cy;`uSQF{O_c`{fd&I+c$-vJT!L9qk4-Vn+8(qA%y46OBr`TA_s9j$9K5HT& zRZyw9cM|Uy#z%5_#tpZMejUGt+SRp-YF2u>+*w0GIn(Mcq6 zPusO4{pdZ72tgU9rk8#*K<`W%DJeL~6%sN30II0OjGXc9REf#WN%RV3HSA3+Q9-Kr zNfg;|X;TbArU2@MjVh={?sJ-hcQiS*l)*}XF+lH5t4%69)9*nPAw-IOsnPAl2uRdw zRT@ZA#zi_*Vwy<#r%Q~}p$MXtKWb?riYV<(35q^x3Syc`=QR7&MMxZb{{Tv1Bn}%V zimZ;O-mT3s$J(SFNf53Kz#Mm~Oi3itAEiFRnQn3h;QUN`IvU1K9IYzRDCjtJJz|5DDk+RfX0Y@n?#~hbn&6 zAwDVds?0#Hui+nynumqmQEjW(v2{`9T-ui^moT5j8-94mJ;4BuMRn^Ar||E@I7(dW z8jhbjHe%BCuP@7)x!9uZRDiksThRf}ags-rVVIuWpS5KyuiD&#D`a}Nv~rw<<+c0= zPpXj4EwXLQH^=++>_YWNS@;d9TCK*kn&rRl_X{Q2Yp5iW*M8?yBE;?A1fgEwdO)l{ zgWAM8KZqSaVWtUgH3_cah~Ihg!MAw`LZYxd1|uv^aoYzKrFcE!`RRQ^>rB;R`o5dh zgmK?N$!N+G<;)j9~i}R##6ZDcKcSvtzl?lNqoo;{V z%+VO(lhhZ99}D=19sBWAlS=8eQ9>>50dr$$>RKDQwM$nk}o@NMJHpuu|ATfONkXzh|5O01NF>Tia`q46=DCbcPwN zWN=>~OB{`Zv>^(>G0S8O0%-ADbh!}m%{KGlC234?%m;<6WdLtC&_ zWpYMvoYhO7oWFq}XSJ3El>^3PLZR2>XSn;+%fjnAb~p+!MuIJa}XCRM>KAYp@)t1A&T7$;C;BIH0B} zsoJNE)bW(!mM90BV+o~$kBVN^Gs!g%o@hb`IryLoU~}zA@Nr2so$1?4>+M>~(@u-( zZ)cSwh7F;-$1FCk)4|Ed2a4D=`-2xuY4)>=JBhBQeNH=zTd@98$O5!R6m5|l#t9o$ zxL<5mnH1iU+v2MErN1FrUm0`5yDOUdX$rhYbK@BKuASibhWf9GU0J8-nnj$K7Z)xA z$f!y|9!BY8VZx$=lE;86mq6K*HQoOJWnE_f08a6*qa(4LM)#Vfy^OKDFpgMeaU7Ap zGnHg!1Yq~9TtUxfoyI-rt$IM$NI92u@$akjHS3G(TU|d+T`eV4gct)EA&l&dWr=n4 zDu586z!=AR^T$?nt+n0U*OrV1n=W6@xLo-|ATlsqlEAWzdx8%f8 zTHc{@_b$%*jlHbXMv54a6=O}Z0zl`?$K~z9EV^g@XHv12D~P0?6FNyGObHPqz6z2! zU^|Y-=bHJ~37$5>9>PJa*x%3JZ$<2N(Yep%aIw*7QFORd{@K9qpq@ZS@OSr$#wd5`PaE+QEWg`}X+v&1Ehu7!w_=BP*ThA;1ToGweU}SSz)b z&4j z-S1D30Vf#Ck^Q@KR?^82PufWm$dgSeS8)T01ZV;7z>$($usma)O>MgFj`P{3vucvE zOjRb1Xv(rSHt!wxDP=f5$Aj!nJ`Rn73kh`_xn+bii+EBtQu00nHz$Cg70(BjUt)2c zx`w&7^YZgon2Sv?eAcHPtS(^PAwoK@%|?V zJoa6`;^QgP9WSR^>hVc`sKDBCNFDr`UP&Eu%IrIP^W1@s#Mevl_oD5z>$&=qMZCAa z(seu8BL1#1pi^#7YRkX9Uk|&v)fAI$JM0iknARZ4K8o_E7j+f*h2$Cpr3wl+E z=O?hi&wARm`%z(|>6Y!o`9^sPa*ZRidWp|06e1sBRfl}m7g7=2ShF)G`0yT8IcCTm z@!uH7=4(7ow{!G3S3RH(Uz)~GrcDMxiMbiieEq>5)S&F55##qzMP}{<`<{*3qR2lg=9{9zoXnaMdue0`Bbj~f1I`&?H@Y6;f zD)cW>be^Sh@ZCFGYD!W+GG;NjCt{K}JH;%)K_hb*#&W{AF`HKC%})N>+FQLQkv4{{YiICUpc5>z661HMxW&%K(st3T8#})EwnNlkl9356o9t z>s=1p_M+*7d1&_Wb@rQU7Q#S{uN8KviHP9t$STK_Z%I2He7`jYs!kg*Y`}OjHL~%N#X%un@!4o*!#vV=b3=Twu z_O5f(I;OI1C;B_E03`O$YilE(?DMpS zMnG~v2#mz?RB>MZEo5b*l;5K6Oz4A4p8i1KI_CqYaJw%?_$Og8l`obRuI(92P2KP(2>j~~8H(HgUiudU)#A*>DkZz4x znWZG5Bm_=INUdYUuNLq6>EX44%HKoK?XqCewAYvEk=W(rJKdSxjxx+izyM$XE1X^| zj87QNTqG{td0F_3aC0|EJg=|YuX3wv{Y!tYT}yF)dvkp>j%0amrfC`2_hML*N%u8@ zl1c-k!I1s$XxK_Rr?B0)70iHHXPe4o)iK{^s&4 z8wD1Nc_4x%fbFmdAg^j`%gt`ZK7Xr23yp)zUSa=q{tVF*j)fuq98}fIHR|Y+K#7qEp05c=`{%Q-Am5FSh{VN`E9-GFOv=$$!jbiTCHo=>Vvo}RucWJ45g zBVRN)!Q`vNF~=oG+*h6MfmS0qIi&JHqNw8fiL|h@Ptc6{o9d%Hg^3kz4r!LoY9+Rl zSuJ=+I?AsTZO6TKj|Dorr8GSgsx-}h{{T_a=hh*D-YeT1MY%}gNer&8rPaMokYKKV z2Rs_$_E5d;)Hcz8WR=!51pAYL{&kSOv71NLE^Q@sGTTh>J0Z%JQbERjvMNI%bo4rx z=_G9I8!t@s_g?=1_=|OW(!D!pE{&yG*vYDCP!h6FCH1WNLPF#S-yW+?#5WBda?oNma;_Q&gw@O!Q;IxDX9y;D${;@?k0Bo|X!C~1-z zVt8a^2ZF&QVgc`uYVMlngOKXoXL6?c&du-c$9TMo*MrE3sU^n)oU^1 zuR`caX#Gdh$YQoxmK{RRXh9Q5yWyHN1niDQ4e80)%Z%Za4?kTwQbFVHYu7q9oj+V? zo+!b63d0m#H3i~KiU*bD%V#8U1sKQxY|b(P$u;KAwdk!wrgZ%yQN6eF?Da_GlGaaW z5y;WF&&Vs=;E$TAjut}lAM!01ERLQLO&j?wGHF?uZenmSxjDf26hZ?(IL2z6#}Dn# zY*UCZH7m;b<@`&~-UH~?)*TDh`tsh{$#Zc8Is|%r@jEvAiQ<)I3^)KSw8x&sM|^== zZ;xF=Z++B|=ynSv)}1>O=_`3U1(NBKXe&_DV#{DcQB*skxWSl^>w*X+hF5$tb2)pc!PM}!>Eb-6n34}%3Zhq$T}jUz{8c94 z^WLxT)-iD}leojU?A`!kx#J*qIIA~a`KxetUIp!)`}?o zDI}ITr$;n@N=YNR;+}i|0Hq zc)_6v#Y;G)oQhfIgp`dSNX#-9S9h zGmo_xG$Bn$Io;3YQVOK@s(pc`r@uT?1we$#6v+pQuN0&Y#l=7YOej))g*xn_bGph{ zUYJ}EaDHpmeGTHRkBJw$4TY_%^xs-r#cu_Uqh~ubce6yQk;2J=urNg1%MIKd2K4zV z60x69_r?h3vf5U$E|+&5#1gy{M#1BfH!8|UVhH3B@+xg{3(av}85a<4ML1m^*ZCet z3(;O6{?9tTyRELJ)4EO;?lhX~M-n7+Y4Wil*UNSd0>ng zQKZ}J*z=c7$z%}cX^_e@=?p+F6vQ*kg9jQ%PJYrF)LKI5Jx9B#^aCnpEBP8(t&j!> z>Dimm*-%Y9GBTW+$JJG(tN-p3@78H}=|J3>eljDIO&6mT*C;8kuQ zoG&KW_|}|wZgAybBoHr}wg%7f({itT81R2k)BHRTvmbZ(u!hHY)H&+wKY;hHc8 ze8zQF+N+(zC-8!6b?OfkE_%1A^()zRX=2l)x)VbOo^~zE7FddgASqn4XO0sC0E(CG z1=RY)dRCp&R&hfXji*MJc2_XI_q@0A^9+wK9$?v!ybJ~_k4`Jj_Lox4HQ^B#hDars z2i4j$v=7O#SloI3N`}V%F3;4=c}N%^OZlFim#aGWtuL*-61H1w#IVs}HiFa2y3Ds2 ziN(@M7VqShV^O#c;{@cI<~=LpLR#sUb{fuwXRm12trVY2sm&)6#2hT2!es0n#tudg zy>jhaQoZUlS!|+KwUhXZ8qXPLkdvIQK^P=}G25PVn!`22!7HuYQbr;MITAF<4n4W; z&(CkYP$C3nYtrzD+aD|Io6B+ZJFiLFbq9q#M*?16=y!cNsmBRLyy=!$R&0Vcv`$DH zl;EDQ}oup~-*d!W$p>+#i%LAbpGKOKlb{SXzFiuo)itc%w~v5% z&IV0Z_%phyP7^3~7rKR;yRR>J-?HkTw5Lo#2Bo2m8qU_&LYGX6duFgK6790c_Y8$s zW3wX+ZTu>{u)#igzp#?sh+%_Xsp-ESr~SdFL)2PUoz*sSNVWnkM%D$AD_Fn*V|9#3 zrMGb5xGRoMYg7HBd`far#t8e`>5od-tdn{Y5okDkM~D zqA1DjNtd3~)3r|{H9ff*<24d7#}y@P3{Vp)AdqUJJ`QR&ghx8?{2wF_VpK-tIK{MF1?VXg}(0I~SRWR@`?Ejuj{ zzmS3#(O!-5Lq`3ox*@dfQph)5=>l7^1QzkYx7N$BmUoOOW>#lns(|bS=OZ=eeyxXG z(lvXjOoj<1o&5FQRb87oC3BB*KecmT4Z7O@0Hk&6EmumtlTOrZbGI3P1B1q6n}Zs!1TSQO~3 zQ|o0#wYrs9xB^A)P>ko>x%oc-02Q$7y(f32Yl|&`OSqEp(mQyS@f(a4PCzF-437Ix zHH+#SJBanm_Vb?POK??`%w;lRh)kTEk?mcmq!v6P03)~hSRTXw0QL(ZRMm87uk{mb zw(&x-OxE_%pxL+7K*;ybMoHx7CacJr=i_zJx_G7Lj$MQNeBw$Bw({2 zR~?6Pe$;_=^2l*V&+-!?wQb+uWp{el9v|IFaV_gy-M;Bq$G6Im;gAO>C5A!Vpbo@$ z6|8knQBAB_>GDb=^A-)V$r&Wf%GmQ5pK^f#$M|`z_g3jkt#&!B8B$pu=G_ue87!+R z861)Af(AZIAD%RAaw}tf3{D;$J{Iy87{D?tS@nDZ$0U+*mM8M1rF3s_am$C|u!-9C zf(8P>$#HIDmIhBPv4F*iBeo6)2e2ScW$ZXV<;`~+k=b(& z4|SN(@1IbbC~abOc3~2MjnX?X87K2#KHa<4;nf{PW$7t2Yirr{Xmt6JcD#&1$(J%j znIRBvRrxtmK*$4F>B~p47rL&+Xsz)Q4c?D$KByFLE^7Z@A?RN{caIr3Dn&JmnooS%+)tcAXpKBs>fA>p=}^CiKI`DnR0e_%ix zz5R%*f8L9yq`yvt%eV;cB?B>m?y56`+nk(xbIo;w$sD6AnnfOCg`CxlQuOdyXSb5} z)>CgH#&RPvuQUeYS$tO4XcG7VNwx>d*|~2eswOFk`r-ump3s$ zvfI3BBtC~`APk<|ej$!hfv=F%jcgA@jlUC2JY7*OA2`?pemD_nF7L#@h0^RU&k~5FDHLBi5 z)Ttbx?zXG;JyN08;wfRZX(L9J!U$sra^XoBUAwTO*+|C~X?d@$wyB$TNo{R&6cO#p zh}abb0x${OTR6yLp7oE@EabG)H4BxN6f#A%H%hAHL-~4lr*0M4-0V;OtMZw+$}mP@Y|3VQ-~^^3AS1xTOU7O5wsWEDI0e z4hhL2P6^=kui1xGg!NIL;%iHNfl+?8<~Y!1L=E`P)*%ZpQh6!|BOvl_jg|;z)U}t5 zue943;TH&?d4x7HiIa{LGLk{S{$6;tz3OXsx|VwjTWvDhL%D5jw_!$b0~cm;G0sUN z>^lx#uTA=E z#PG-oX7j;NTPH8Oa07M*0>}15@fK*z`lgv{(-xjuxVqPGBeq6n8Cbx}Idj;q+^_R( z!K%7niyc5NroGU*nfiYE?188+pCU&qV+GWpBh||g*uf_R=K|i>sXD3dZQ;I4ySWL% zU0cZBRO&h5(;>SYka))@y<&=OW|{`V-ciab&EL}l?r+I&do=UPUgAhQf4}gz9-aM^ zx_?b*ZhDWc?QVo*EykIl+RG&JN#DwjX%%DaKOP$%%HI6n$Bh?E@VCWHcSz~TWz%0o z)+H8~M&YEH0xeuX&74nq`W(GnB_z@I) zxxoBcuPAtx)Dra{QtEoF=^Rtv+{<$$agtRT*s%bTP6lu?NvZIiPODd|e~kNUM`{^M zh(ivR2KZu$`K@=W`mb2)O+e}z)}?W(U4;>euB9%@fRYt+*+C}&a5Gxla4P2FTy`~R z@3@k2n)(wO7|_=2yiAWSaU|s|WEzPdl^l*~^WPOUojeMu6JdwH+NmGXnw%WeflL*D z69D`SXP!-WjRxlPsXAjx)?o%cVo8%pd#h-X+jJ-ju?)Zlc%4*t+|VC($lwUvf=zZ$ z1aB@i3mtE#>}La2xqEy22n&R1ri0A|BV&!SZJC*HK|WpvNdmR7<$A6KIFD2s<3ZT> z`1!4qT+`?2-h%5C=CEt6xQS9Nx0V43fqI;By|x!7Y$f5;9G3?!D=e(!EQ4rW=Uv#k-F# z<~1s_M#>sF06c)m%D^9*9r0u>d~M&UcaIRJ4}^TP@6l>ZY7OyKWKc~-mnYy>b>m1^ zy+3j+b;}!;jU^WIq{cUs%MG;d!EbEi6-MInTf3Phm5WIu7iMFUNjW5%p|=^uNk&NH zoX{*Q1J5ri+4^469Y+;yznbWu|aPnCht=1X=fNbjicVYtMpC%w37Z`*WSTvxD4aWkykh$opIAzYU(i70yoagzwxJ(0DuE-XExRV?PK! zmw#eD>f^)iz0`bO={-lJHJwI1M^vy_?(|EIQt=W%0|b)UWR-^P!I6~-BzZ(A^%|qo zz75Z3sa!6tr%4vD0b30PT%^}djfO^0q@sWv2gvjOpK&gkc^t_hhDew?I<^@| zIRqb(UW(UzWYTmW4El2Ab!j@EuJ3evyIJI&i>-o9JnF_-N#7*CQ{hvxZrn-8T8o34 zaJ!s0i$r4O)(_caALHt?x`wVi64E-tU3TkD9b43zbeei-)_${P49umTVlop5o`l@b z>Q*~I^?7fp^{c+2HumpvH1NZef);YhnK?UG`EpK9JQmd9HYOqx9cYT4|!f?&9^Y zbggC4n4ci*bqd*yfN}}SlaM`*E2ijv65ndQ2cvZ#Pv6_>9ZA%x(O+qri$NrD#@8 zRx0km+yT_>=v80ZG-)?I5#jOr<J@eJss7S9aXAY%hit@+UA|6XtzPFw8V-Tt|oz^*|7;M%jL%2oH--Y z&y(+B^4yPlaemrd{xzqPDH{13M1j8jv-DO?!KfOj+>_doT;`-#l`0<8QHp)WDJYp%_M^Q5l4%}kQ}?CwQ9_jzQZykv&}kFRI%q{1 z%_=BK2AvdBNg1Y1Dgfe|6BslBKpfNWLP-4aN}f69kr*`4p6q?-$v)K)M>HWz9Mq~g zsS05tihcd*6W*3+w3S6~YGC8r`qUJh)k4`Az!|9p5|%xw4k}<|9OHFF@=47Ul8|GH zss`cTwNVO5`KqBvT>F2WFrZaf#!e0o-!&rRarhmonjZY{Dx^!fuxTpJH&-!8KnL$x z%^KmBWjNrP#9Le&X=9QWDtn?O$NsP;5V5x zOP88f4eA*z*iraHst?1@98E=9;^OF_W!e%nU`7={Rqyr#wPt9cOM$}SA`(k*e)dWE zZ}!TKgmPNj>?|CQN#K<^_U+F<<=(bvWxBn(mP^>}pKK;aM~NCoRE*#gjGn+}zrVF= zce=dy8QT6@l?p;+K#`~)FimKc%WOzO2<4(rJ6I1!DReEdt)OUkD3W1WjP%DF`#YUw)rdF zYxk6TNV)+8kwX(mlB)@$R3ar{al(_gxg7Q!S3>Yl#e0sI(RGVWTG|b3r*9);E|qw3 z9nHW4VGsDjD9hzYJa_eT$~0DKFE6hYiQO!uZH(MI94XF!5M9Nw?ozKhejzj$7BZA# z6{fZT@@2yC5BIxaT$l!6!YW-8-&rbFJfpxF=cy$|}q;hYxi=+}lbK zAF(?@Q`}&Jd6tK3svFA*nh2WSGO`@VkBFZon8%WKvgbTt37v}o}$qt({5T9QWpdR4WLl`{!VyTcv z-jNDGsl2Zs3&s>+U;#n{kIIO^EHDL4j!DKpF;G2}!prFQvB7YrKz8HRv}BKuf131% zgFm$m_e1HnS{ADWeMQq2?9UFHbig&6u_VcFCJBwV5_=US@~474wHh%w1CN@?*xa-d zE>9l(3gYXu5bIc3D6qPET~qY#ww3N~qUqfh)-5-xI%Y{P`ZG}>#lf}@EN;9>41)y6 zBIgVhfgyteL0s}{-i6gP+miN#wc^CHM)tCh0Vp0eRv147XB~!l&1$|Ge$d@R;fl#` zr`*SPrA~k5v}^S;+1wBNlOqlP0D6^S?83KBu>G<%okr(>OK7@pMuvtsjJ8pUu7Onp zDLjCf#sC0D6!FPpl6*UOX1`4@Bp?>|C+H7fn)L@=rj|RCeTqq-u1HW#e%O zMzOs&7VSNTkmmAj(Zs>C9Ak7@+#3=F+DTEnZD=ucCx?C@Ygd;2Bi0G6bqt9lu<4hU z5?Zan+RRoLVd{0xLJ+04khvrZ@)gd!^AVOL;}{jQ_+8hN^e0p=dk|( z7SZ_;S(DP%y)|KFKC-Jl)zm9?nt64FQWDt9xb<#j^(X_eRwRxON!EO2@Ul%}E7@jH6J>A?tG?6h-YqvvX?~6| z{{T{DP}34gF3O~i0mps<`()NH>!<1c4Q?%EFy7qYA0&b0#;gv0OEZtqC*O-bTHJkX zv)U+*)P#vy$>f9^h|eP}xre|bwGqH3(s41^ZR8-Ka(xYg2p?ur58Z}-{*{rntN`S7 z^Y&L2(m1%CyDTNvqp0dKO)cE=IA$%EZad_FKQry~O7h}47BY+FouD?;lHS?DJQL5p zKPt_^qrCctZf%_`qgHttM996^++_8_o4e3D7e zKQ+0oZGh4ASqL?iMk^?ZZL_3`4`{&z{ry03Sw**fLcwg^QLf?uTgjH*>ywzzu>&Np zW5zki0~Kw5r)gIZ%8|*aZT0^E5rlDu$-pUsPZ-au2CjeNyN@IUj@cqsR@kru>K&Wa zlb_a;!`=HrlM5TUSea~E(7Pc7t)S~OreAcq9*#&NV`IOn}&An0q0)|TeN2`??!Zb>}66(>HH0FWPp>B;+M zgQ#mq{g@~amwwEJj_KRUHCDINFEZBBKb0BxftkM$&)u?l@qm{tnQuA^CH~IdkI8XOyVNyv%jlu6&o%qkTGuyUPG<@c=I{K|miTtAQRi*qEcDgN|pB1f;zPZG*PXhwwgSz3E zk4QKKDiAOiIn8UKHe3_6067If#y-G(t1%x)-5)kKw_*H1jLw)-oyUg!IQ0G?dsa?9 zl4)j_QJUh~TM%(A#BLc?&$iLYC)7a4I43v+)P%9d55;%y{U_nQ^lFwm+QvaDtS2nX zo?HNbBaiHT>v`!zsdRE%>arOZQJ?<+)a@C{s)N9t@r?YSKK`S^fYc(FMY2ml9+of8 zH<_l8$C9o_)dxReu8H9mg56cEORwwqG27_&1M9UbtBA(fV+3vrt-*8Y zR8ifEsKz+rYc&VLB)1=*FPiM>A_3z${4AG?cUS%z_yE^HD8DhFbmXe*cho=rn!1sg z;6SYL!5WU-vdJO&{ujl0KU8M9naTXlPXfBnkDf2xc%jyMhN~hp7V$DtWnzHUd zt=GdYupJB5I%ck6^Pz#t&mP2b(K21I-TW7iBHZmvKFxPrxSG=CzTGP_s=`Q1p%I|I+ytLbo zSy>oH6ta>gK&Q|VvG-jcq4n*X={8zMiDh$t2cMwpnr8cSznL2b8CB#d6FA%qcukUd zc*3_hZv?cttU9l#x?53#eLC+@(r&HUBvTx)&2P5tS82fvTGS>u<*F~v%jg_GNiK&_ zz~gbh7B(QQTgD#;HQx&Lcp%iGX)Ny`OPd?@iPAf$nL)~i0f8HUWgrzj$87UCSAq1FB*7ZR$TTBva-L(3*4&X&o^+gxd)DF$jNZd z>z&5*ZPH2%dmQ#w_}-lRcctVPJPoVcCZ#MbZxTwfC#{)wtBu1L`Oscpe9~IVgKL}>O@bgjCB)NEXWVITlzL)fynrk(cVKRA{ z+@!RGs)ZqzHex{AoL5G4#)G42P<0) zY+l*h7k^EMRl)6a;d6T&F@l@b{*Af2X=

W1!6=%Gb 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 EA4)?jZ~y=R literal 0 HcmV?d00001