From bebcf7f01c58d3656c9b5fedae1b4192d4c87264 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 2 Nov 2025 08:14:01 -0800 Subject: [PATCH 01/72] chore: tests --- .github/workflows/build.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9e2b8fd7..0a46354c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,4 +48,8 @@ jobs: uses: ilammy/msvc-dev-cmd@v1 - name: Build - run: cargo build --bin plumeimpactor --release + run: | + cargo install patch-crate + cargo fetch --locked + cargo patch-crate --force + cargo build --bin plumeimpactor --release From 4d9ac915b9c1dc5f6858a3e076a2209ee3b5d353 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 2 Nov 2025 08:43:08 -0800 Subject: [PATCH 02/72] tests --- .github/workflows/build.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0a46354c..06997aa9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,6 +50,13 @@ jobs: - name: Build run: | cargo install patch-crate - cargo fetch --locked + cargo fetch --locked || true cargo patch-crate --force cargo build --bin plumeimpactor --release + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: plumeimpactor-${{ matrix.platform }} + path: | + target/release/plumeimpactor* From d9a79ce90f9966d19c27b15cb53c5824a5cc8a90 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 2 Nov 2025 08:59:23 -0800 Subject: [PATCH 03/72] fix? --- .github/workflows/build.yml | 6 ++++++ Cargo.lock | 33 --------------------------------- apps/plumeimpactor/Cargo.toml | 20 +++++++------------- 3 files changed, 13 insertions(+), 46 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06997aa9..fa561c95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,12 @@ jobs: with: targets: ${{ (matrix.platform == 'macos-latest') && 'aarch64-apple-darwin' || '' }} + - name: Install Linux dependencies + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt install libdbus-1-dev + + - name: Setup Windows dependencies if: runner.os == 'Windows' run: choco install strawberryperl make --no-progress diff --git a/Cargo.lock b/Cargo.lock index 9393537e..53c166f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1004,27 +1004,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" -[[package]] -name = "dbus" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190b6255e8ab55a7b568df5a883e9497edc3e4821c06396612048b430e5ad1e9" -dependencies = [ - "libc", - "libdbus-sys", - "windows-sys 0.59.0", -] - -[[package]] -name = "dbus-secret-service" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" -dependencies = [ - "dbus", - "zeroize", -] - [[package]] name = "der" version = "0.7.10" @@ -2187,12 +2166,9 @@ version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ - "byteorder", - "dbus-secret-service", "log", "security-framework 2.11.1", "security-framework 3.5.1", - "windows-sys 0.60.2", "zeroize", ] @@ -2252,15 +2228,6 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" -[[package]] -name = "libdbus-sys" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f" -dependencies = [ - "pkg-config", -] - [[package]] name = "libloading" version = "0.8.8" diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index 984ab387..57e14768 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -17,20 +17,14 @@ wxdragon = "0.8.30" image = { version = "0.25.1", default-features = false, features = ["png"] } futures = "0.3.31" -# Keyrings -[target.'cfg(all(debug_assertions, target_os = "macos"))'.dependencies] -keyring = { version = "3.6.3", features = ["apple-native"] } +keyring = { version = "3.6.3", features = ["apple-native"], optional = true } +keyring-core = { version = "0.7.0", optional = true } +apple-native-keyring-store = { version = "0.2.1", features = ["protected"], optional = true } -[target.'cfg(all(not(debug_assertions), target_os = "macos"))'.dependencies] -keyring-core = { version = "0.7.0" } -apple-native-keyring-store = { version = "0.2.1", features = ["protected"] } -# keyring-core requires notarization with entitlements to work properly on macOS (release) - -[target.'cfg(target_os = "windows")'.dependencies] -keyring = { version = "3.6.3", features = ["windows-native"] } - -[target.'cfg(target_os = "linux")'.dependencies] -keyring = { version = "3.6.3", features = ["sync-secret-service"] } +[features] +keyring = ["dep:keyring"] +keyring_core = ["dep:keyring-core", "dep:apple-native-keyring-store"] +default = ["keyring"] [build-dependencies] embed-manifest = "1.4" From cee990ef01f09b3575b9f7dcc31e4be926a6c6bd Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 2 Nov 2025 09:15:18 -0800 Subject: [PATCH 04/72] linux deps --- .github/workflows/build.yml | 4 ++-- apps/plumeimpactor/src/keychain.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa561c95..d76d5b46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,8 +42,8 @@ jobs: - name: Install Linux dependencies if: matrix.platform == 'ubuntu-22.04' run: | - sudo apt install libdbus-1-dev - + sudo apt-get update + sudo apt-get install libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev - name: Setup Windows dependencies if: runner.os == 'Windows' diff --git a/apps/plumeimpactor/src/keychain.rs b/apps/plumeimpactor/src/keychain.rs index 0a316d1c..873eee99 100644 --- a/apps/plumeimpactor/src/keychain.rs +++ b/apps/plumeimpactor/src/keychain.rs @@ -1,7 +1,7 @@ -#[cfg(all(target_os = "macos", not(debug_assertions)))] -pub use keyring_core::Entry; -#[cfg(not(all(target_os = "macos", not(debug_assertions))))] -pub use keyring::Entry; +// #[cfg(all(target_os = "macos", not(debug_assertions)))] +// pub use keyring_core::Entry; +// #[cfg(not(all(target_os = "macos", not(debug_assertions))))] +// pub use keyring::Entry; const KEYRING_SERVICE: &'static str = "Plume Impactor Credentials"; const KEYRING_USER: &'static str = "Apple ID"; From 252e70eb47cf520c4c54ad998b8f27305c0672cf Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 3 Nov 2025 06:29:41 -0800 Subject: [PATCH 05/72] test build --- .github/workflows/build.yml | 2 +- Cargo.lock | 84 ++-- Makefile | 0 README.md | 24 ++ apps/plumeimpactor/Cargo.toml | 11 +- apps/plumeimpactor/src/frame.rs | 490 ++++++++++++++++-------- apps/plumeimpactor/src/handlers.rs | 203 +++++++--- apps/plumeimpactor/src/keychain.rs | 46 ++- apps/plumeimpactor/src/main.rs | 2 + apps/plumeimpactor/src/pages/install.rs | 14 +- apps/plumeimpactor/src/pages/login.rs | 138 +++---- crates/grand_slam/src/auth/mod.rs | 8 +- crates/grand_slam/src/developer/mod.rs | 2 +- package/entitlements.plist | 10 + 14 files changed, 665 insertions(+), 369 deletions(-) create mode 100644 Makefile create mode 100644 package/entitlements.plist diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d76d5b46..be00d856 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,4 +65,4 @@ jobs: with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/plumeimpactor* + target/release/plumeimpactor diff --git a/Cargo.lock b/Cargo.lock index 53c166f3..a78399d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,17 +237,6 @@ dependencies = [ "thiserror 2.0.16", ] -[[package]] -name = "apple-native-keyring-store" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f9955235ce557bd0ea2c64d7ff09a887885f515e98572d2640a29520d9c98c" -dependencies = [ - "keyring-core", - "log", - "security-framework 3.5.1", -] - [[package]] name = "apple-xar" version = "0.20.0" @@ -1315,9 +1304,9 @@ checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "libz-rs-sys", @@ -2166,21 +2155,15 @@ version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ + "byteorder", + "linux-keyutils", "log", "security-framework 2.11.1", "security-framework 3.5.1", + "windows-sys 0.60.2", "zeroize", ] -[[package]] -name = "keyring-core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ad182c4841eb5795af9d20e6e020b65a895517f6a41e6358ed8af74ba35d98" -dependencies = [ - "log", -] - [[package]] name = "konst" version = "0.3.16" @@ -2264,6 +2247,16 @@ dependencies = [ "zlib-rs", ] +[[package]] +name = "linux-keyutils" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" +dependencies = [ + "bitflags 2.9.4", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -2873,14 +2866,12 @@ dependencies = [ name = "plumeimpactor" version = "0.0.1" dependencies = [ - "apple-native-keyring-store", "embed-manifest", "futures", "grand_slam", "idevice", "image", "keyring", - "keyring-core", "ldid2", "tokio", "types", @@ -3184,6 +3175,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "rawzip" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70171b805bd97a69690df1b634104649ad193430aa60d8305dfb9b470fe690a8" + [[package]] name = "rayon" version = "1.11.0" @@ -4050,17 +4047,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tar" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "tempfile" version = "3.22.0" @@ -5108,12 +5094,12 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wxdragon" -version = "0.8.30" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a33d6cc76a0ac87ae24ea6595c344c13d3a704486757ca4d3713e08379c7a09" +checksum = "387582e9b06f1665c4807211e8a768911dfca14bd5fd4920456b4ceb96b82720" dependencies = [ "bitflags 2.9.4", - "lazy_static", + "log", "paste", "wxdragon-macros", "wxdragon-sys", @@ -5121,9 +5107,9 @@ dependencies = [ [[package]] name = "wxdragon-macros" -version = "0.8.30" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fb6c47a6840ead6fb9976fe67d31bd81eae4e9899f007ee43291a9267653f2" +checksum = "22c0e49dbb26c84bba760b56b858982f93e0effcb521e8a6b598ba082fb2616c" dependencies = [ "proc-macro2", "quick-xml", @@ -5133,15 +5119,17 @@ dependencies = [ [[package]] name = "wxdragon-sys" -version = "0.8.30" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b3de839d51d2be358782f4077902784ef2355f1e2bc81837a17b54761bdfe4a" +checksum = "39c289d767e8334f4d5792b887bad08283b1596240b257db2fae9301cdd925ae" dependencies = [ "bindgen", + "cmake", "flate2", "pkg-config", + "rawzip", "reqwest 0.12.23", - "tar", + "sha2", ] [[package]] @@ -5182,16 +5170,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "xattr" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" -dependencies = [ - "libc", - "rustix", -] - [[package]] name = "xmas-elf" version = "0.9.1" diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index ff8fb091..e0802cfd 100644 --- a/README.md +++ b/README.md @@ -1 +1,25 @@ # PlumeImpactor + +[![GitHub Release](https://img.shields.io/github/v/release/khcrysalis/PlumeImpactor?include_prereleases)](https://github.com/khcrysalis/PlumeImpactor/releases) +[![GitHub License](https://img.shields.io/github/license/khcrysalis/PlumeImpactor?color=%23C96FAD)](https://github.com/khcrysalis/PlumeImpactor/blob/main/LICENSE) +[![Sponsor Me](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/khcrysalis) + +PlumeImpactor is an open-source alternative to tools like CydiaImpactor, Sideloadly, and AltStore. Made for sideloading to iOS / tvOS devices. + +## Structure + +The project is seperated in multiple modules, all serve single or multiple uses depending on their importance. + +| Module | Description | +| -------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `apps/plumeimpactor` | GUI interface for the crates shown below, backend using wxWidgets (with a rust ffi wrapper, wxDragon) | +| `apps/plumesign` | CLI interface for the crates shown below, using `clap`. | +| `crates/grand_slam` | Handles all api request used for communicating with Apple developer services, along with providing auth for Apple's grandslam | +| `crates/ldid2` | Wrapper for applecodesign-rs with additional features, specifically made to support iOS sideloading and app modifications | + +## Acknowledgements + +- [Samara](https://github.com/khcrysalis) - ME! +- [apple-private-apis](https://github.com/SideStore/apple-private-apis) - Grandslam auth & Omnisette. +- [apple-codesign-rs](https://github.com/indygreg/apple-platform-rs) - Open-source alternative to codesign. +- [idevice](https://github.com/jkcoxson/idevice) - Used for communication with `installd`, specifically for sideloading the apps to your devices. diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index 57e14768..06adcf2c 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -13,18 +13,11 @@ tokio.workspace = true ldid2 = { path = "../../crates/ldid2" } grand_slam = { path = "../../crates/grand_slam", features = ["vendored-botan"] } types = { path = "../../crates/types"} -wxdragon = "0.8.30" +wxdragon = "0.9.2" image = { version = "0.25.1", default-features = false, features = ["png"] } futures = "0.3.31" -keyring = { version = "3.6.3", features = ["apple-native"], optional = true } -keyring-core = { version = "0.7.0", optional = true } -apple-native-keyring-store = { version = "0.2.1", features = ["protected"], optional = true } - -[features] -keyring = ["dep:keyring"] -keyring_core = ["dep:keyring-core", "dep:apple-native-keyring-store"] -default = ["keyring"] +keyring = { version = "3.6.3", default-features = false, features = ["windows-native", "apple-native", "linux-native"] } [build-dependencies] embed-manifest = "1.4" diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 939337f0..68869fd6 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -5,7 +5,6 @@ use std::{env, ptr, thread}; use grand_slam::AnisetteConfiguration; use grand_slam::auth::Account; -use grand_slam::developer::DeveloperSession; use wxdragon::prelude::*; use futures::StreamExt; @@ -13,20 +12,21 @@ use idevice::usbmuxd::{UsbmuxdConnection, UsbmuxdListenEvent}; use tokio::runtime::Builder; use tokio::sync::mpsc; -use types::{Device, Package}; -use crate::pages::login::{LoginDialog, create_single_field_dialog}; -use crate::pages::{DefaultPage, InstallPage, create_default_page, create_install_page, create_login_dialog}; use crate::APP_NAME; use crate::handlers::{PlumeFrameMessage, PlumeFrameMessageHandler}; +use crate::keychain::AccountCredentials; +use crate::pages::login::LoginDialog; +use crate::pages::{ + DefaultPage, InstallPage, create_default_page, create_install_page, create_login_dialog, +}; +use types::{Device, Package}; pub struct PlumeFrame { pub frame: Frame, pub default_page: DefaultPage, pub install_page: InstallPage, - pub settings_button: Button, pub usbmuxd_picker: Choice, - - pub apple_id_picker: Choice, + pub apple_id_button: Button, pub login_dialog: LoginDialog, } @@ -38,41 +38,36 @@ impl PlumeFrame { .with_size(Size::new(530, 410)) .with_style(FrameStyle::CloseBox | FrameStyle::MinimizeBox) .build(); - + let sizer = BoxSizer::builder(Orientation::Vertical).build(); let top_panel = Panel::builder(&frame).build(); let top_row = BoxSizer::builder(Orientation::Horizontal).build(); - let device_picker = Choice::builder(&top_panel) - .build(); - - let apple_id_picker = Choice::builder(&top_panel) - .build(); - - let apple_id_button = Button::builder(&top_panel) - .with_label("+") - .build(); - - let settings_button = Button::builder(&top_panel) - .with_label("Settings") - .build(); + let device_picker = Choice::builder(&top_panel).build(); + let apple_id_button = Button::builder(&top_panel).with_label("+").build(); top_row.add(&device_picker, 1, SizerFlag::Expand | SizerFlag::All, 0); - top_row.add_spacer(8); - top_row.add(&apple_id_picker, 1, SizerFlag::Expand | SizerFlag::All, 0); - top_row.add_spacer(8); + top_row.add_spacer(12); top_row.add(&apple_id_button, 0, SizerFlag::All, 0); - top_row.add_spacer(8); - top_row.add(&settings_button, 0, SizerFlag::All, 0); top_panel.set_sizer(top_row, true); let default_page = create_default_page(&frame); let install_page = create_install_page(&frame); - sizer.add(&top_panel, 0, SizerFlag::Expand | SizerFlag::All, 8); - sizer.add(&default_page.panel, 1, SizerFlag::Expand | SizerFlag::All, 0); - sizer.add(&install_page.panel, 1, SizerFlag::Expand | SizerFlag::All, 0); + sizer.add(&top_panel, 0, SizerFlag::Expand | SizerFlag::All, 12); + sizer.add( + &default_page.panel, + 1, + SizerFlag::Expand | SizerFlag::All, + 0, + ); + sizer.add( + &install_page.panel, + 1, + SizerFlag::Expand | SizerFlag::All, + 0, + ); frame.set_sizer(sizer, true); install_page.panel.hide(); @@ -80,15 +75,13 @@ impl PlumeFrame { frame: frame.clone(), default_page, install_page, - settings_button, usbmuxd_picker: device_picker, - apple_id_picker, apple_id_button, login_dialog: create_login_dialog(&frame), }; s.setup_event_handlers(); - + s } @@ -97,17 +90,27 @@ impl PlumeFrame { self.frame.centre(); self.frame.set_extra_style(ExtraWindowStyle::ProcessIdle); } +} +// MARK: - Event Handlers + +impl PlumeFrame { fn setup_event_handlers(&mut self) { let (sender, receiver) = mpsc::unbounded_channel::(); - - let message_handler = Rc::new( - RefCell::new(PlumeFrameMessageHandler::new( - receiver, - unsafe { ptr::read(self) }, - )) - ); - + let message_handler = self.setup_idle_handler(receiver); + Self::spawn_background_threads(sender.clone()); + self.bind_widget_handlers(sender, message_handler); + } + + fn setup_idle_handler( + &self, + receiver: mpsc::UnboundedReceiver, + ) -> Rc> { + let message_handler = Rc::new(RefCell::new(PlumeFrameMessageHandler::new( + receiver, + unsafe { ptr::read(self) }, + ))); + let handler_for_idle = message_handler.clone(); self.frame.on_idle(move |event_data| { if let WindowEventData::Idle(event) = event_data { @@ -115,83 +118,135 @@ impl PlumeFrame { } }); - // --- Usbmuxd Listener --- - - thread::spawn({ - let sender = sender.clone(); - move || { - let rt = Builder::new_current_thread().enable_io().build().unwrap(); + message_handler + } - rt.block_on(async move { - let mut muxer = match UsbmuxdConnection::default().await { - Ok(muxer) => muxer, - Err(e) => { - sender.send(PlumeFrameMessage::Error(format!("Failed to connect to usbmuxd: {}", e))).ok(); - return; - } - }; + fn spawn_background_threads(sender: mpsc::UnboundedSender) { + Self::spawn_usbmuxd_listener(sender.clone()); + Self::spawn_auto_login_thread(sender); + } - match muxer.get_devices().await { - Ok(devices) => { - for dev in devices { - sender.send(PlumeFrameMessage::DeviceConnected(Device::new(dev).await)).ok(); - } - } - Err(e) => { - sender.send(PlumeFrameMessage::Error(format!("Failed to get initial device list: {}", e))).ok(); + fn spawn_usbmuxd_listener(sender: mpsc::UnboundedSender) { + thread::spawn(move || { + let rt = Builder::new_current_thread().enable_io().build().unwrap(); + rt.block_on(async move { + let mut muxer = match UsbmuxdConnection::default().await { + Ok(muxer) => muxer, + Err(e) => { + sender + .send(PlumeFrameMessage::Error(format!( + "Failed to connect to usbmuxd: {}", + e + ))) + .ok(); + return; + } + }; + + match muxer.get_devices().await { + Ok(devices) => { + for dev in devices { + sender + .send(PlumeFrameMessage::DeviceConnected(Device::new(dev).await)) + .ok(); } } + Err(e) => { + sender + .send(PlumeFrameMessage::Error(format!( + "Failed to get initial device list: {}", + e + ))) + .ok(); + } + } - let mut stream = match muxer.listen().await { - Ok(stream) => stream, - Err(e) => { - sender.send(PlumeFrameMessage::Error(format!("Failed to listen for events: {}", e))).ok(); - return; - } - }; + let mut stream = match muxer.listen().await { + Ok(stream) => stream, + Err(e) => { + sender + .send(PlumeFrameMessage::Error(format!( + "Failed to listen for events: {}", + e + ))) + .ok(); + return; + } + }; - while let Some(event) = stream.next().await { - let msg = match event { - Ok(dev_event) => match dev_event { - UsbmuxdListenEvent::Connected(dev) => { - PlumeFrameMessage::DeviceConnected(Device::new(dev).await) - } - UsbmuxdListenEvent::Disconnected(device_id) => { - PlumeFrameMessage::DeviceDisconnected(device_id) - } - }, - Err(e) => { - PlumeFrameMessage::Error(format!("Failed to listen for events: {}", e)) + while let Some(event) = stream.next().await { + let msg = match event { + Ok(dev_event) => match dev_event { + UsbmuxdListenEvent::Connected(dev) => { + PlumeFrameMessage::DeviceConnected(Device::new(dev).await) + } + UsbmuxdListenEvent::Disconnected(device_id) => { + PlumeFrameMessage::DeviceDisconnected(device_id) } - }; - if sender.send(msg).is_err() { - break; + }, + Err(e) => { + PlumeFrameMessage::Error(format!("Failed to listen for events: {}", e)) } + }; + if sender.send(msg).is_err() { + break; } - }); - } + } + }); }); + } - // --- GUI Handlers --- + /// Spawns the automatic account login thread. + fn spawn_auto_login_thread(sender: mpsc::UnboundedSender) { + thread::spawn(move || { + let creds = AccountCredentials; - let handler_for_choice = message_handler.clone(); - let picker_clone = self.usbmuxd_picker.clone(); - self.usbmuxd_picker.on_selection_changed(move |_event_data| { - let mut handler = handler_for_choice.borrow_mut(); - - if let Some(index) = picker_clone.get_selection() { - if let Some(selected_item) = handler.usbmuxd_device_list.get(index as usize) { - handler.usbmuxd_selected_device_id = Some(selected_item.usbmuxd_device.device_id.to_string()); + let (email, password) = match (creds.get_email(), creds.get_password()) { + (Ok(email), Ok(password)) => (email, password), + _ => { + return; + } + }; + + match run_login_flow(sender.clone(), email, password) { + Ok(account) => { + sender.send(PlumeFrameMessage::AccountLogin(account)).ok(); + } + Err(e) => { + sender + .send(PlumeFrameMessage::Error(format!("Login error: {}", e))) + .ok(); + sender.send(PlumeFrameMessage::AccountDeleted).ok(); } - } else { - handler.usbmuxd_selected_device_id = None; } }); - - self.settings_button.on_click(|_| { - println!("Settings"); - }); + } + fn bind_widget_handlers( + &mut self, + sender: mpsc::UnboundedSender, + message_handler: Rc>, + ) { + // --- Device Picker --- + + let handler_for_choice = message_handler.clone(); + let picker_clone = self.usbmuxd_picker.clone(); + self.usbmuxd_picker + .on_selection_changed(move |_event_data| { + let mut handler = handler_for_choice.borrow_mut(); + + if let Some(index) = picker_clone.get_selection() { + if let Some(selected_item) = handler.usbmuxd_device_list.get(index as usize) { + handler.usbmuxd_selected_device_id = + Some(selected_item.usbmuxd_device.device_id.to_string()); + } + } else { + handler.usbmuxd_selected_device_id = None; + } + }); + + // --- Apple ID / Login Dialog --- + let login_dialog_rc = Rc::new(self.login_dialog.clone()); self.apple_id_button.on_click({ let login_dialog = login_dialog_rc.clone(); @@ -199,7 +254,7 @@ impl PlumeFrame { login_dialog.show_modal(); } }); - + self.login_dialog.set_cancel_handler({ let login_dialog = login_dialog_rc.clone(); move || { @@ -207,68 +262,103 @@ impl PlumeFrame { login_dialog.hide(); } }); - - let frame_clone = self.frame.clone(); - self.login_dialog.set_next_handler({ - let login_dialog = login_dialog_rc.clone(); - move || { - let email = login_dialog.get_email(); - let password = login_dialog.get_password(); - login_dialog.clear_fields(); - login_dialog.hide(); - - - println!("Email: {}, Password: {}", email, password); - - let anisette_config = AnisetteConfiguration::default() - .set_configuration_path(env::temp_dir()); + // --- Login Dialog "Next" Button --- + + self.bind_login_dialog_next_handler(sender.clone(), login_dialog_rc); - thread::spawn({ - let email = email.clone(); - let password = password.clone(); - let anisette_config = anisette_config; - move || { - let rt = Builder::new_current_thread().enable_all().build().unwrap(); - rt.block_on(async { + // --- File Drop/Open Handlers --- + + self.bind_file_handlers(sender.clone()); - let get_2fa_code = || { + // --- Install Page Handlers --- + + let sender_for_cancel = sender.clone(); + self.install_page.set_cancel_handler(move || { + sender_for_cancel + .send(PlumeFrameMessage::PackageDeselected) + .ok(); + }); - Err("2FA code request not implemented in background thread".into()) - }; + let sender_for_install = sender.clone(); + self.install_page.set_install_handler(move || { + sender_for_install + .send(PlumeFrameMessage::PackageInstallationStarted) + .ok(); + }); + } - let a = Account::login( - || Ok((email.clone(), password.clone())), - get_2fa_code, - anisette_config, - ).await.unwrap(); - - let session = DeveloperSession::with(a); - let a = session.qh_list_teams().await.unwrap(); - println!("{:#?}", env::temp_dir()); - println!("{:#?}", a); + fn bind_login_dialog_next_handler( + &self, + sender: mpsc::UnboundedSender, + login_dialog: Rc, + ) { + login_dialog.clone().set_next_handler(move || { + let email = login_dialog.get_email(); + let password = login_dialog.get_password(); + let creds = AccountCredentials; + + match creds.credentials_exist(email.clone(), password.clone()) { + Ok(true) => { + creds.delete_password().ok(); + sender + .send(PlumeFrameMessage::Error( + "Account already exists.".to_string(), + )) + .ok(); + } + Ok(false) => { + let sender_for_login_thread = sender.clone(); + + thread::spawn(move || { + match run_login_flow( + sender_for_login_thread.clone(), + email.clone(), + password.clone(), + ) { + Ok(account) => { + if let Err(e) = + creds.set_credentials(email.clone(), password.clone()) + { + sender_for_login_thread + .send(PlumeFrameMessage::Error(format!( + "Keychain error: {}", + e + ))) + .ok(); + } - }); - } - }); + sender_for_login_thread + .send(PlumeFrameMessage::AccountLogin(account)) + .ok(); + } + Err(e) => { + sender_for_login_thread + .send(PlumeFrameMessage::Error(format!("Login error: {}", e))) + .ok(); + } + } + }); + } + Err(e) => { + sender + .send(PlumeFrameMessage::Error(format!("Keychain error: {}", e))) + .ok(); + } } + + login_dialog.clear_fields(); + login_dialog.hide(); }); - - - + } + + fn bind_file_handlers(&self, sender: mpsc::UnboundedSender) { let handler_for_import = self.frame.clone(); - let sender_for_file = sender.clone(); - let sender_for_dialog = sender.clone(); + self.default_page.set_file_handlers( - move |file_path| { - match Package::new(PathBuf::from(file_path)) { - Ok(package) => { - sender_for_file.send(PlumeFrameMessage::PackageSelected(package)).ok(); - } - Err(e) => { - sender_for_file.send(PlumeFrameMessage::Error(format!("Failed to open package: {}", e))).ok(); - } - } + { + let sender = sender.clone(); + move |file_path| Self::process_package_file(sender.clone(), PathBuf::from(file_path)) }, move || { let dialog = FileDialog::builder(&handler_for_import) @@ -281,20 +371,90 @@ impl PlumeFrame { return; } if let Some(file_path) = dialog.get_path() { - match Package::new(PathBuf::from(file_path)) { - Ok(package) => { - sender_for_dialog.send(PlumeFrameMessage::PackageSelected(package)).ok(); - } - Err(e) => { - sender_for_dialog.send(PlumeFrameMessage::Error(format!("Failed to open package: {}", e))).ok(); - } - } + Self::process_package_file(sender.clone(), PathBuf::from(file_path)); } }, ); + } - self.install_page.set_cancel_handler(move || { - sender.send(PlumeFrameMessage::PackageDeselected).ok(); - }); + /// This de-duplicates logic from the file handlers. + fn process_package_file( + sender: mpsc::UnboundedSender, + file_path: PathBuf, + ) { + match Package::new(file_path) { + Ok(package) => { + sender + .send(PlumeFrameMessage::PackageSelected(package)) + .ok(); + } + Err(e) => { + sender + .send(PlumeFrameMessage::Error(format!( + "Failed to open package: {}", + e + ))) + .ok(); + } + } + } + + pub fn create_single_field_dialog(&self, title: &str, label: &str) -> Result { + let dialog = Dialog::builder(&self.frame, title) + .with_style(DialogStyle::DefaultDialogStyle) + .build(); + + let sizer = BoxSizer::builder(Orientation::Vertical).build(); + sizer.add_spacer(16); + + let field_label = StaticText::builder(&dialog).with_label(label).build(); + let text_field = TextCtrl::builder(&dialog).build(); + sizer.add(&field_label, 0, SizerFlag::All, 12); + sizer.add(&text_field, 0, SizerFlag::Expand | SizerFlag::All, 8); + + dialog.set_sizer(sizer, true); + + dialog.show_modal(); + let value = text_field.get_value().to_string(); + dialog.destroy(); + println!("2FA code entered: {}", value); + Ok(value) } } + +fn run_login_flow( + sender: mpsc::UnboundedSender, + email: String, + password: String, +) -> Result { + let anisette_config = + AnisetteConfiguration::default().set_configuration_path(env::temp_dir()); + + let rt = match Builder::new_current_thread().enable_all().build() { + Ok(rt) => rt, + Err(e) => return Err(format!("Failed to create Tokio runtime: {}", e)), + }; + + let account_result = rt.block_on(Account::login( + || Ok((email.clone(), password.clone())), + || { + let (tx, rx) = tokio::sync::oneshot::channel::>(); + + if sender + .send(PlumeFrameMessage::AwaitingTwoFactorCode(tx)) + .is_err() + { + return Err("Failed to send 2FA request to main thread.".to_string()); + } + + match rt.block_on(rx) { + Ok(Ok(code)) => Ok(code), + Ok(Err(e)) => Err(e), + Err(_) => Err("2FA process cancelled or main thread error.".to_string()), + } + }, + anisette_config, + )); + + account_result.map_err(|e| e.to_string()) +} diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 27003e80..9294cfae 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -2,17 +2,23 @@ use wxdragon::prelude::*; use tokio::sync::mpsc; use tokio::sync::mpsc::error::TryRecvError; +use tokio::sync::oneshot; +use grand_slam::{auth::Account, developer::DeveloperSession}; use types::{Device, Package, PlistInfoTrait}; use crate::frame::PlumeFrame; -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum PlumeFrameMessage { DeviceConnected(Device), DeviceDisconnected(u32), PackageSelected(Package), PackageDeselected, + PackageInstallationStarted, + AccountLogin(Account), + AccountDeleted, + AwaitingTwoFactorCode(oneshot::Sender>), Error(String), } @@ -24,6 +30,8 @@ pub struct PlumeFrameMessageHandler { pub usbmuxd_selected_device_id: Option, // --- ipa --- pub package_selected: Option, + // --- account --- + pub account_credentials: Option, } impl PlumeFrameMessageHandler { @@ -37,9 +45,10 @@ impl PlumeFrameMessageHandler { usbmuxd_device_list: Vec::new(), usbmuxd_selected_device_id: None, package_selected: None, + account_credentials: None, } } - + pub fn process_messages(&mut self) -> bool { let mut processed_count = 0; let mut has_more = false; @@ -54,7 +63,7 @@ impl PlumeFrameMessageHandler { Err(TryRecvError::Disconnected) => return false, } } - + if processed_count == 10 { has_more = true; } @@ -62,80 +71,170 @@ impl PlumeFrameMessageHandler { has_more } - fn handle_message(&mut self, message: PlumeFrameMessage) { match message { - PlumeFrameMessage::DeviceConnected(device) => { - println!("Device connected: {}", device); - if !self.usbmuxd_device_list.iter().any(|d| d.usbmuxd_device.device_id == device.usbmuxd_device.device_id) { - self.usbmuxd_device_list.push(device.clone()); - self.usbmuxd_picker_rebuild_contents(); + fn handle_message(&mut self, message: PlumeFrameMessage) { + match message { + PlumeFrameMessage::DeviceConnected(device) => { + println!("Device connected: {}", device); + if !self + .usbmuxd_device_list + .iter() + .any(|d| d.usbmuxd_device.device_id == device.usbmuxd_device.device_id) + { + self.usbmuxd_device_list.push(device.clone()); + self.usbmuxd_picker_rebuild_contents(); - if self.usbmuxd_device_list.len() == 1 { - self.usbmuxd_picker_select_item(&device.usbmuxd_device.device_id); - } else { + if self.usbmuxd_device_list.len() == 1 { + self.usbmuxd_picker_select_item(&device.usbmuxd_device.device_id); + } else { + self.usbmuxd_picker_reconcile_selection(); + } + } + } + PlumeFrameMessage::DeviceDisconnected(device_id) => { + println!("Device disconnected: {}", device_id); + if let Some(index) = self + .usbmuxd_device_list + .iter() + .position(|d| d.usbmuxd_device.device_id == device_id) + { + self.usbmuxd_device_list.remove(index); + self.usbmuxd_picker_rebuild_contents(); self.usbmuxd_picker_reconcile_selection(); } } - } - PlumeFrameMessage::DeviceDisconnected(device_id) => { - println!("Device disconnected: {}", device_id); - if let Some(index) = self.usbmuxd_device_list.iter().position(|d| d.usbmuxd_device.device_id == device_id) { - self.usbmuxd_device_list.remove(index); - self.usbmuxd_picker_rebuild_contents(); - self.usbmuxd_picker_reconcile_selection(); + PlumeFrameMessage::PackageSelected(package) => { + if self.package_selected.is_some() { + return; + } + + let package_name = package.get_name().unwrap_or_else(|| "Unknown".to_string()); + let package_id = package + .get_bundle_identifier() + .unwrap_or_else(|| "Unknown".to_string()); + println!("Package selected: {}", package_name); + self.package_selected = Some(package); + self.plume_frame + .install_page + .set_top_text(format!("{} - {}", package_name, package_id).as_str()); + self.plume_frame.default_page.panel.hide(); + self.plume_frame.install_page.panel.show(true); + self.plume_frame.frame.layout(); } - } - PlumeFrameMessage::PackageSelected(package) => { - if self.package_selected.is_some() { - return; + PlumeFrameMessage::PackageDeselected => { + println!("Package deselected"); + self.package_selected = None; + self.plume_frame.install_page.panel.hide(); + self.plume_frame.default_page.panel.show(true); + self.plume_frame.frame.layout(); } - - let package_name = package.get_name().unwrap_or_else(|| "Unknown".to_string()); - let package_id = package.get_bundle_identifier().unwrap_or_else(|| "Unknown".to_string()); - println!("Package selected: {}", package_name); - self.package_selected = Some(package); - self.plume_frame.install_page.set_top_text(format!("{} - {}", package_name, package_id).as_str()); - self.plume_frame.default_page.panel.hide(); - self.plume_frame.install_page.panel.show(true); - self.plume_frame.frame.layout(); - } - PlumeFrameMessage::PackageDeselected => { - println!("Package deselected"); - self.package_selected = None; - self.plume_frame.install_page.panel.hide(); - self.plume_frame.default_page.panel.show(true); - self.plume_frame.frame.layout(); - } - PlumeFrameMessage::Error(error_msg) => { - println!("Error: {}", error_msg); - let dialog = MessageDialog::builder(&self.plume_frame.frame, &error_msg, "Error") - .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning) + PlumeFrameMessage::PackageInstallationStarted => { + let package = match &self.package_selected { + Some(pkg) => pkg.clone(), + None => { + self.handle_message(PlumeFrameMessage::Error( + "No package selected for installation.".to_string(), + )); + return; + } + }; + + let account = match &self.account_credentials { + Some(acc) => acc.clone(), + None => { + self.handle_message(PlumeFrameMessage::Error( + "Installation failed: No account logged in.".to_string(), + )); + return; + } + }; + + // Show a progress dialog before starting the async task + let progress_dialog = ProgressDialog::builder( + &self.plume_frame.frame, + "Installing package...", + "Please wait while the installation is in progress.", + 100, + ) + .with_style(ProgressDialogStyle::AppModal) .build(); - dialog.show_modal(); + progress_dialog.show(true); + + tokio::spawn(async move { + let package_name = package.get_name().unwrap_or_else(|| "Unknown".to_string()); + + println!("--- Install Task ---"); + println!("Package: {}", package_name); + println!("-----------------------------------"); + + let session = DeveloperSession::with(account); + match session.qh_list_teams().await { + Ok(teams) => println!("Successfully listed teams: {:?}", teams), + Err(e) => println!("Failed to list teams: {:?}", e), + } + }); + } + PlumeFrameMessage::AccountLogin(account) => { + self.account_credentials = Some(account); + println!("Account logged in"); + } + PlumeFrameMessage::AccountDeleted => { + self.account_credentials = None; + println!("Account deleted"); + } + PlumeFrameMessage::AwaitingTwoFactorCode(tx) => { + let result = self.plume_frame.create_single_field_dialog( + "Two-Factor Authentication", + "Enter the verification code sent to your device:", + ); + + if let Err(e) = tx.send(result) { + println!("Failed to send 2FA code back to background thread: {:?}", e); + } + } + PlumeFrameMessage::Error(error_msg) => { + println!("Error: {}", error_msg); + let dialog = MessageDialog::builder(&self.plume_frame.frame, &error_msg, "Error") + .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning) + .build(); + dialog.show_modal(); + } } - }} + } // --- Device Picker Helpers --- - + fn usbmuxd_picker_rebuild_contents(&self) { self.plume_frame.usbmuxd_picker.clear(); for item_string in &self.usbmuxd_device_list { - self.plume_frame.usbmuxd_picker.append(&item_string.to_string()); + self.plume_frame + .usbmuxd_picker + .append(&item_string.to_string()); } } fn usbmuxd_picker_select_item(&mut self, device_id: &u32) { - if let Some(index) = self.usbmuxd_device_list.iter().position(|d| d.usbmuxd_device.device_id == *device_id) { + if let Some(index) = self + .usbmuxd_device_list + .iter() + .position(|d| d.usbmuxd_device.device_id == *device_id) + { self.plume_frame.usbmuxd_picker.set_selection(index as u32); self.usbmuxd_selected_device_id = Some(device_id.to_string()); } else { self.usbmuxd_selected_device_id = None; } } - + fn usbmuxd_picker_reconcile_selection(&mut self) { if let Some(selected_item) = self.usbmuxd_selected_device_id.clone() { - if let Some(new_index) = self.usbmuxd_device_list.iter().position(|d| d.usbmuxd_device.device_id.to_string() == selected_item) { - self.plume_frame.usbmuxd_picker.set_selection(new_index as u32); + if let Some(new_index) = self + .usbmuxd_device_list + .iter() + .position(|d| d.usbmuxd_device.device_id.to_string() == selected_item) + { + self.plume_frame + .usbmuxd_picker + .set_selection(new_index as u32); } else { self.usbmuxd_picker_default_selection(); } diff --git a/apps/plumeimpactor/src/keychain.rs b/apps/plumeimpactor/src/keychain.rs index 873eee99..86acbffe 100644 --- a/apps/plumeimpactor/src/keychain.rs +++ b/apps/plumeimpactor/src/keychain.rs @@ -1,7 +1,41 @@ -// #[cfg(all(target_os = "macos", not(debug_assertions)))] -// pub use keyring_core::Entry; -// #[cfg(not(all(target_os = "macos", not(debug_assertions))))] -// pub use keyring::Entry; +use keyring::{Entry, Error}; -const KEYRING_SERVICE: &'static str = "Plume Impactor Credentials"; -const KEYRING_USER: &'static str = "Apple ID"; +const KEYRING_SERVICE: &str = env!("CARGO_PKG_NAME"); +const KEYRING_EMAIL: &str = "Apple ID Email"; +const KEYRING_PASS: &str = "Apple ID Password"; + +pub struct AccountCredentials; + +impl AccountCredentials { + pub fn set_credentials(&self, email: String, password: String) -> Result<(), Error> { + let entry_email = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; + let entry_pass = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; + entry_email.set_secret(email.as_bytes())?; + entry_pass.set_secret(password.as_bytes())?; + Ok(()) + } + + pub fn get_email(&self) -> Result { + let entry = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; + entry.get_password() + } + + pub fn get_password(&self) -> Result { + let entry = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; + entry.get_password() + } + + pub fn delete_password(&self) -> Result<(), Error> { + let entry_email = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; + let entry_pass = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; + entry_email.delete_credential()?; + entry_pass.delete_credential()?; + Ok(()) + } + + pub fn credentials_exist(&self, email: String, password: String) -> Result { + let stored_email = self.get_email().unwrap_or_default(); + let stored_password = self.get_password().unwrap_or_default(); + Ok(stored_email == email && stored_password == password) + } +} diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index d3ce6f7d..df55a582 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + mod frame; mod keychain; mod pages; diff --git a/apps/plumeimpactor/src/pages/install.rs b/apps/plumeimpactor/src/pages/install.rs index 6fabd01d..a1b43046 100644 --- a/apps/plumeimpactor/src/pages/install.rs +++ b/apps/plumeimpactor/src/pages/install.rs @@ -4,6 +4,7 @@ use wxdragon::prelude::*; pub struct InstallPage { pub panel: Panel, pub cancel_button: Button, + pub install_button: Button, pub top_text: StaticText, } @@ -15,7 +16,7 @@ pub fn create_install_page(frame: &Frame) -> InstallPage { let top_text = StaticText::builder(&panel) .with_label("Unknown") .build(); - main_sizer.add(&top_text, 0, SizerFlag::Left | SizerFlag::Top, 10); + main_sizer.add(&top_text, 0, SizerFlag::Left | SizerFlag::Top, 14); main_sizer.add_stretch_spacer(1); @@ -30,16 +31,17 @@ pub fn create_install_page(frame: &Frame) -> InstallPage { .build(); button_sizer.add_stretch_spacer(1); - button_sizer.add(&cancel_button, 0, SizerFlag::Right, 8); + button_sizer.add(&cancel_button, 0, SizerFlag::Right, 12); button_sizer.add(&install_button, 0, SizerFlag::All, 0); - main_sizer.add_sizer(&button_sizer, 0, SizerFlag::Right | SizerFlag::Bottom | SizerFlag::Expand, 10); + main_sizer.add_sizer(&button_sizer, 0, SizerFlag::Right | SizerFlag::Bottom | SizerFlag::Expand, 14); panel.set_sizer(main_sizer, true); InstallPage { panel, cancel_button, + install_button, top_text, } } @@ -50,6 +52,12 @@ impl InstallPage { on_cancel(); }); } + + pub fn set_install_handler(&self, on_install: impl Fn() + 'static) { + self.install_button.on_click(move |_evt| { + on_install(); + }); + } pub fn set_top_text(&self, text: &str) { self.top_text.set_label(text); diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 07b6e895..5ed6184f 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -10,53 +10,64 @@ pub struct LoginDialog { } pub fn create_login_dialog(parent: &Window) -> LoginDialog { - let dialog = Dialog::builder(parent, "Sign in with your Apple ID") - .with_style(DialogStyle::DefaultDialogStyle) - .build(); - - let sizer = BoxSizer::builder(Orientation::Vertical).build(); - - sizer.add_spacer(16); - - let email_label = StaticText::builder(&dialog) - .with_label("Email:") - .build(); - let email_field = TextCtrl::builder(&dialog).build(); - sizer.add(&email_label, 0, SizerFlag::All, 8); - sizer.add(&email_field, 0, SizerFlag::Expand | SizerFlag::All, 8); - - let password_label = StaticText::builder(&dialog) - .with_label("Password:") - .build(); - let password_field = TextCtrl::builder(&dialog) - .with_style(TextCtrlStyle::Password) - .build(); - sizer.add(&password_label, 0, SizerFlag::All, 8); - sizer.add(&password_field, 0, SizerFlag::Expand | SizerFlag::All, 8); - - let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); - let cancel_button = Button::builder(&dialog) - .with_label("Cancel") - .build(); - let next_button = Button::builder(&dialog) - .with_label("Next") - .build(); - button_sizer.add(&cancel_button, 0, SizerFlag::All, 8); - button_sizer.add_spacer(16); - button_sizer.add(&next_button, 0, SizerFlag::All, 8); - - sizer.add_spacer(16); - sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 8); - - dialog.set_sizer(sizer, true); - - LoginDialog { - dialog, - email_field, - password_field, - cancel_button, - next_button, - } + let dialog = Dialog::builder(parent, "Sign in with your Apple ID") + .with_style(DialogStyle::DefaultDialogStyle) + .build(); + + let sizer = BoxSizer::builder(Orientation::Vertical).build(); + sizer.add_spacer(12); + + let description = StaticText::builder(&dialog) + .with_label("In order to use the main features for PlumeImpactor, you will\nneed to sign into your Apple ID. But don't worry! Your\ncredentials are sent to Apple and not anywhere else!") + .build(); + sizer.add(&description, 0, SizerFlag::All, 12); + + let email_row = BoxSizer::builder(Orientation::Horizontal).build(); + let email_label = StaticText::builder(&dialog) + .with_label(" Email:") + .build(); + let email_field = TextCtrl::builder(&dialog).build(); + email_row.add( + &email_label, + 0, + SizerFlag::AlignCenterVertical | SizerFlag::All, + 8, + ); + email_row.add(&email_field, 1, SizerFlag::Expand | SizerFlag::All, 12); + sizer.add_sizer(&email_row, 0, SizerFlag::Expand | SizerFlag::All, 0); + + let password_row = BoxSizer::builder(Orientation::Horizontal).build(); + let password_label = StaticText::builder(&dialog).with_label("Password:").build(); + let password_field = TextCtrl::builder(&dialog) + .with_style(TextCtrlStyle::Password) + .build(); + password_row.add( + &password_label, + 0, + SizerFlag::AlignCenterVertical | SizerFlag::All, + 8, + ); + password_row.add(&password_field, 1, SizerFlag::Expand | SizerFlag::All, 12); + sizer.add_sizer(&password_row, 0, SizerFlag::Expand | SizerFlag::All, 0); + + let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); + let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); + let next_button = Button::builder(&dialog).with_label("Next").build(); + button_sizer.add(&cancel_button, 1, SizerFlag::Expand | SizerFlag::All, 0); + button_sizer.add_spacer(12); + button_sizer.add(&next_button, 1, SizerFlag::Expand | SizerFlag::All, 0); + + sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 12); + + dialog.set_sizer(sizer, true); + + LoginDialog { + dialog, + email_field, + password_field, + cancel_button, + next_button, + } } impl LoginDialog { @@ -67,52 +78,29 @@ impl LoginDialog { pub fn get_password(&self) -> String { self.password_field.get_value().to_string() } - + pub fn clear_fields(&self) { self.email_field.set_value(""); self.password_field.set_value(""); } - + pub fn show_modal(&self) { self.dialog.show_modal(); } - + pub fn hide(&self) { self.dialog.end_modal(0); } - + pub fn set_cancel_handler(&self, on_cancel: impl Fn() + 'static) { self.cancel_button.on_click(move |_evt| { on_cancel(); }); } - + pub fn set_next_handler(&self, on_next: impl Fn() + 'static) { self.next_button.on_click(move |_evt| { on_next(); }); } } - -pub fn create_single_field_dialog(parent: &Window, title: &str, label: &str) -> Result { - let dialog = Dialog::builder(parent, title) - .with_style(DialogStyle::DefaultDialogStyle) - .build(); - - let sizer = BoxSizer::builder(Orientation::Vertical).build(); - sizer.add_spacer(16); - - let field_label = StaticText::builder(&dialog) - .with_label(label) - .build(); - let text_field = TextCtrl::builder(&dialog).build(); - sizer.add(&field_label, 0, SizerFlag::All, 8); - sizer.add(&text_field, 0, SizerFlag::Expand | SizerFlag::All, 8); - - dialog.set_sizer(sizer, true); - - dialog.show_modal(); - let value = text_field.get_value().to_string(); - dialog.destroy(); - Ok(value) -} diff --git a/crates/grand_slam/src/auth/mod.rs b/crates/grand_slam/src/auth/mod.rs index f387b641..90c48f51 100644 --- a/crates/grand_slam/src/auth/mod.rs +++ b/crates/grand_slam/src/auth/mod.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use omnisette::AnisetteConfiguration; use reqwest::{Certificate, Client, ClientBuilder}; use tokio::sync::Mutex; +use std::sync::Arc; use errors::Error; @@ -13,9 +14,9 @@ use crate::auth::anisette_data::AnisetteData; const GSA_ENDPOINT: &str = "https://gsa.apple.com/grandslam/GsService2"; const APPLE_ROOT: &[u8] = include_bytes!("./apple_root.der"); +#[derive(Debug, Clone)] pub struct Account { - //TODO: move this to omnisette - pub anisette: Mutex, + pub anisette: Arc>, // pub spd: Option, //mutable spd pub spd: Option, @@ -36,9 +37,8 @@ impl Account { .http1_title_case_headers() .connection_verbose(true) .build()?; - Ok(Account { - anisette: Mutex::new(anisette), + anisette: Arc::new(Mutex::new(anisette)), spd: None, client, }) diff --git a/crates/grand_slam/src/developer/mod.rs b/crates/grand_slam/src/developer/mod.rs index d6c92184..998dbc09 100644 --- a/crates/grand_slam/src/developer/mod.rs +++ b/crates/grand_slam/src/developer/mod.rs @@ -58,7 +58,7 @@ impl SessionRequestTrait for DeveloperSession { let code = response_data.result_code.as_signed().unwrap_or(0); return Err(Error::DeveloperSession(code, msg.to_string())); } - + Ok(response) } diff --git a/package/entitlements.plist b/package/entitlements.plist new file mode 100644 index 00000000..56e89b3b --- /dev/null +++ b/package/entitlements.plist @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)$(CFBundleIdentifier) + + + \ No newline at end of file From 77437a56ea55ebfbdcfb805feb1ce8dda1693ace Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 3 Nov 2025 06:57:27 -0800 Subject: [PATCH 06/72] add some packaging stuff for macos --- .github/workflows/build.yml | 2 +- .gitignore | 1 + .../PlumeImpactor.app/Contents/Info.plist | 52 +++++++++++++++++++ package/{ => macos}/entitlements.plist | 0 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 package/macos/PlumeImpactor.app/Contents/Info.plist rename package/{ => macos}/entitlements.plist (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be00d856..d76d5b46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,4 +65,4 @@ jobs: with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/plumeimpactor + target/release/plumeimpactor* diff --git a/.gitignore b/.gitignore index 84361743..2a709e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ state.plist .zsign_cache *.DS_Store /tests +/build diff --git a/package/macos/PlumeImpactor.app/Contents/Info.plist b/package/macos/PlumeImpactor.app/Contents/Info.plist new file mode 100644 index 00000000..2d9bf7c2 --- /dev/null +++ b/package/macos/PlumeImpactor.app/Contents/Info.plist @@ -0,0 +1,52 @@ + + + + + BuildMachineOSBuild + 23A344011 + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Plume Impactor + CFBundleExecutable + plumeimpactor + CFBundleIconFile + AppIcon + CFBundleIconName + AppIcon + CFBundleIdentifier + com.feather.impactor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Plume Impactor + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.3 + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 50.5.4 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + + DTPlatformName + macosx + DTPlatformVersion + 15.6 + DTSDKBuild + 24F62 + DTSDKName + macosx15.5.internal + DTXcode + 1630 + DTXcodeBuild + 16E6052g + LSMinimumSystemVersion + 10.8 + + diff --git a/package/entitlements.plist b/package/macos/entitlements.plist similarity index 100% rename from package/entitlements.plist rename to package/macos/entitlements.plist From d40a2cfe850ad50f1915bf7eb5d9be134398580f Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 3 Nov 2025 07:04:24 -0800 Subject: [PATCH 07/72] very shit makefile --- Makefile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Makefile b/Makefile index e69de29b..62b0fbba 100644 --- a/Makefile +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY: macos clean + + +macos: +# env /usr/bin/arch -x86_64 cargo build --bin plumeimpactor --release --target x86_64-apple-darwin + cargo build --bin plumeimpactor --release --target aarch64-apple-darwin + + mkdir -p build/macos + cp -R package/macos/PlumeImpactor.app build/macos/PlumeImpactor.app +# lipo -create -output build/macos/PlumeImpactor.app/Contents/MacOS/plumeimpactor \ +# target/x86_64-apple-darwin/release/plumeimpactor \ +# target/aarch64-apple-darwin/release/plumeimpactor + cp target/aarch64-apple-darwin/release/plumeimpactor build/macos/PlumeImpactor.app/Contents/MacOS/plumeimpactor + +clean: + rm -rf build/ From 8ea06e384e1634deaa88e556b56c9801a8f99ea2 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 4 Nov 2025 06:32:05 -0800 Subject: [PATCH 08/72] test --- .github/workflows/build.yml | 1 + apps/plumeimpactor/src/frame.rs | 16 ++++---- apps/plumeimpactor/src/handlers.rs | 30 ++------------ apps/plumeimpactor/src/pages/login.rs | 58 +++++++++++---------------- 4 files changed, 35 insertions(+), 70 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d76d5b46..e13f8120 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: - ubuntu-22.04 - windows-latest - macos-latest + - macos-15-intel runs-on: ${{ matrix.platform }} steps: diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 68869fd6..2395a61e 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -45,7 +45,7 @@ impl PlumeFrame { let top_row = BoxSizer::builder(Orientation::Horizontal).build(); let device_picker = Choice::builder(&top_panel).build(); - let apple_id_button = Button::builder(&top_panel).with_label("+").build(); + let apple_id_button = Button::builder(&top_panel).with_label("Account").build(); top_row.add(&device_picker, 1, SizerFlag::Expand | SizerFlag::All, 0); top_row.add_spacer(12); @@ -429,27 +429,25 @@ fn run_login_flow( ) -> Result { let anisette_config = AnisetteConfiguration::default().set_configuration_path(env::temp_dir()); - + let rt = match Builder::new_current_thread().enable_all().build() { Ok(rt) => rt, Err(e) => return Err(format!("Failed to create Tokio runtime: {}", e)), }; + let (code_tx, code_rx) = std::sync::mpsc::channel::>(); + let account_result = rt.block_on(Account::login( || Ok((email.clone(), password.clone())), || { - let (tx, rx) = tokio::sync::oneshot::channel::>(); - if sender - .send(PlumeFrameMessage::AwaitingTwoFactorCode(tx)) + .send(PlumeFrameMessage::AwaitingTwoFactorCode(code_tx.clone())) .is_err() { return Err("Failed to send 2FA request to main thread.".to_string()); } - - match rt.block_on(rx) { - Ok(Ok(code)) => Ok(code), - Ok(Err(e)) => Err(e), + match code_rx.recv() { + Ok(result) => result, Err(_) => Err("2FA process cancelled or main thread error.".to_string()), } }, diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 9294cfae..8d34105a 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -2,7 +2,7 @@ use wxdragon::prelude::*; use tokio::sync::mpsc; use tokio::sync::mpsc::error::TryRecvError; -use tokio::sync::oneshot; +use std::sync::mpsc as std_mpsc; use grand_slam::{auth::Account, developer::DeveloperSession}; use types::{Device, Package, PlistInfoTrait}; @@ -18,7 +18,7 @@ pub enum PlumeFrameMessage { PackageInstallationStarted, AccountLogin(Account), AccountDeleted, - AwaitingTwoFactorCode(oneshot::Sender>), + AwaitingTwoFactorCode(std_mpsc::Sender>), Error(String), } @@ -147,31 +147,7 @@ impl PlumeFrameMessageHandler { return; } }; - - // Show a progress dialog before starting the async task - let progress_dialog = ProgressDialog::builder( - &self.plume_frame.frame, - "Installing package...", - "Please wait while the installation is in progress.", - 100, - ) - .with_style(ProgressDialogStyle::AppModal) - .build(); - progress_dialog.show(true); - - tokio::spawn(async move { - let package_name = package.get_name().unwrap_or_else(|| "Unknown".to_string()); - - println!("--- Install Task ---"); - println!("Package: {}", package_name); - println!("-----------------------------------"); - - let session = DeveloperSession::with(account); - match session.qh_list_teams().await { - Ok(teams) => println!("Successfully listed teams: {:?}", teams), - Err(e) => println!("Failed to list teams: {:?}", e), - } - }); + // } PlumeFrameMessage::AccountLogin(account) => { self.account_credentials = Some(account); diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 5ed6184f..402ef3ee 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -10,29 +10,24 @@ pub struct LoginDialog { } pub fn create_login_dialog(parent: &Window) -> LoginDialog { - let dialog = Dialog::builder(parent, "Sign in with your Apple ID") - .with_style(DialogStyle::DefaultDialogStyle) - .build(); + let dialog = Dialog::builder(parent, "Sign in with your Apple ID") + .with_style(DialogStyle::DefaultDialogStyle) + .build(); - let sizer = BoxSizer::builder(Orientation::Vertical).build(); - sizer.add_spacer(12); + let sizer = BoxSizer::builder(Orientation::Vertical).build(); + sizer.add_spacer(12); - let description = StaticText::builder(&dialog) + let description = StaticText::builder(&dialog) .with_label("In order to use the main features for PlumeImpactor, you will\nneed to sign into your Apple ID. But don't worry! Your\ncredentials are sent to Apple and not anywhere else!") .build(); - sizer.add(&description, 0, SizerFlag::All, 12); + sizer.add(&description, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 12); let email_row = BoxSizer::builder(Orientation::Horizontal).build(); let email_label = StaticText::builder(&dialog) .with_label(" Email:") .build(); let email_field = TextCtrl::builder(&dialog).build(); - email_row.add( - &email_label, - 0, - SizerFlag::AlignCenterVertical | SizerFlag::All, - 8, - ); + email_row.add(&email_label, 0, SizerFlag::AlignCenterVertical | SizerFlag::All, 8); email_row.add(&email_field, 1, SizerFlag::Expand | SizerFlag::All, 12); sizer.add_sizer(&email_row, 0, SizerFlag::Expand | SizerFlag::All, 0); @@ -41,33 +36,28 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { let password_field = TextCtrl::builder(&dialog) .with_style(TextCtrlStyle::Password) .build(); - password_row.add( - &password_label, - 0, - SizerFlag::AlignCenterVertical | SizerFlag::All, - 8, - ); + password_row.add(&password_label, 0, SizerFlag::AlignCenterVertical | SizerFlag::All, 8); password_row.add(&password_field, 1, SizerFlag::Expand | SizerFlag::All, 12); sizer.add_sizer(&password_row, 0, SizerFlag::Expand | SizerFlag::All, 0); - let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); - let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); - let next_button = Button::builder(&dialog).with_label("Next").build(); - button_sizer.add(&cancel_button, 1, SizerFlag::Expand | SizerFlag::All, 0); - button_sizer.add_spacer(12); - button_sizer.add(&next_button, 1, SizerFlag::Expand | SizerFlag::All, 0); + let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); + let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); + let next_button = Button::builder(&dialog).with_label("Next").build(); + button_sizer.add(&cancel_button, 1, SizerFlag::Expand | SizerFlag::All, 0); + button_sizer.add_spacer(12); + button_sizer.add(&next_button, 1, SizerFlag::Expand | SizerFlag::All, 0); - sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 12); + sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 12); - dialog.set_sizer(sizer, true); + dialog.set_sizer(sizer, true); - LoginDialog { - dialog, - email_field, - password_field, - cancel_button, - next_button, - } + LoginDialog { + dialog, + email_field, + password_field, + cancel_button, + next_button, + } } impl LoginDialog { From 64562d2630bc816c2afd238ea50dc2bf48833522 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 6 Nov 2025 18:46:21 -0800 Subject: [PATCH 09/72] more alerts --- apps/plumeimpactor/src/frame.rs | 176 +++++++++++++++++++---------- apps/plumeimpactor/src/handlers.rs | 8 +- 2 files changed, 125 insertions(+), 59 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 2395a61e..d35a764e 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -246,12 +246,59 @@ impl PlumeFrame { }); // --- Apple ID / Login Dialog --- - + let login_dialog_rc = Rc::new(self.login_dialog.clone()); self.apple_id_button.on_click({ let login_dialog = login_dialog_rc.clone(); + let handler_for_account = message_handler.clone(); + let frame_for_dialog = self.frame.clone(); + let sender_for_logout = sender.clone(); move |_| { - login_dialog.show_modal(); + let logged_in = handler_for_account.borrow().account_credentials.is_some(); + if logged_in { + // Show account status with email and logout option + let creds = AccountCredentials; + let email = creds.get_email().unwrap_or_else(|_| "(unknown)".to_string()); + + let dialog = Dialog::builder(&frame_for_dialog, "Account") + .with_style(DialogStyle::DefaultDialogStyle) + .build(); + + let sizer = BoxSizer::builder(Orientation::Vertical).build(); + sizer.add_spacer(12); + let label = StaticText::builder(&dialog) + .with_label(&format!("Logged in as {}", email)) + .build(); + sizer.add(&label, 0, SizerFlag::All, 12); + + let buttons = BoxSizer::builder(Orientation::Horizontal).build(); + let logout_btn = Button::builder(&dialog).with_label("Log out").build(); + let close_btn = Button::builder(&dialog).with_label("Close").build(); + buttons.add(&logout_btn, 0, SizerFlag::All, 8); + buttons.add_spacer(8); + buttons.add(&close_btn, 0, SizerFlag::All, 8); + sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); + + dialog.set_sizer(sizer, true); + + let dlg_close = dialog.clone(); + close_btn.on_click(move |_| { dlg_close.end_modal(ID_CANCEL as i32); }); + + let dlg_logout = dialog.clone(); + let sender_clone = sender_for_logout.clone(); + logout_btn.on_click(move |_| { + let creds = AccountCredentials; + let _ = creds.delete_password(); + let _ = sender_clone.send(PlumeFrameMessage::AccountDeleted); + dlg_logout.end_modal(ID_OK as i32); + }); + + dialog.show_modal(); + dialog.destroy(); + } else { + // Not logged in, open login dialog + login_dialog.show_modal(); + } } }); @@ -293,62 +340,51 @@ impl PlumeFrame { sender: mpsc::UnboundedSender, login_dialog: Rc, ) { + let frame_for_errors = self.frame.clone(); login_dialog.clone().set_next_handler(move || { let email = login_dialog.get_email(); let password = login_dialog.get_password(); - let creds = AccountCredentials; - match creds.credentials_exist(email.clone(), password.clone()) { - Ok(true) => { - creds.delete_password().ok(); - sender - .send(PlumeFrameMessage::Error( - "Account already exists.".to_string(), - )) - .ok(); - } - Ok(false) => { - let sender_for_login_thread = sender.clone(); - - thread::spawn(move || { - match run_login_flow( - sender_for_login_thread.clone(), - email.clone(), - password.clone(), - ) { - Ok(account) => { - if let Err(e) = - creds.set_credentials(email.clone(), password.clone()) - { - sender_for_login_thread - .send(PlumeFrameMessage::Error(format!( - "Keychain error: {}", - e - ))) - .ok(); - } - - sender_for_login_thread - .send(PlumeFrameMessage::AccountLogin(account)) - .ok(); - } - Err(e) => { - sender_for_login_thread - .send(PlumeFrameMessage::Error(format!("Login error: {}", e))) - .ok(); - } - } - }); - } - Err(e) => { - sender - .send(PlumeFrameMessage::Error(format!("Keychain error: {}", e))) - .ok(); - } + if email.trim().is_empty() || password.is_empty() { + let dialog = MessageDialog::builder( + &frame_for_errors, + "Please enter both email and password.", + "Missing Information", + ) + .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning) + .build(); + dialog.show_modal(); + return; } + // Save credentials to keyring + let creds = AccountCredentials; + if let Err(e) = creds.set_credentials(email.clone(), password.clone()) { + let _ = sender + .send(PlumeFrameMessage::Error(format!( + "Failed to save credentials: {}", + e + ))); + return; + } + + // Hide dialog before starting login login_dialog.clear_fields(); login_dialog.hide(); + + let sender_for_login_thread = sender.clone(); + thread::spawn(move || { + match run_login_flow(sender_for_login_thread.clone(), email, password) { + Ok(account) => { + let _ = sender_for_login_thread + .send(PlumeFrameMessage::AccountLogin(account)); + } + Err(e) => { + let _ = sender_for_login_thread + .send(PlumeFrameMessage::Error(format!("Login failed: {}", e))); + } + } + }); }); } @@ -403,22 +439,46 @@ impl PlumeFrame { let dialog = Dialog::builder(&self.frame, title) .with_style(DialogStyle::DefaultDialogStyle) .build(); - + let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(16); - + let field_label = StaticText::builder(&dialog).with_label(label).build(); let text_field = TextCtrl::builder(&dialog).build(); sizer.add(&field_label, 0, SizerFlag::All, 12); sizer.add(&text_field, 0, SizerFlag::Expand | SizerFlag::All, 8); - + + // Buttons row + let buttons = BoxSizer::builder(Orientation::Horizontal).build(); + let cancel_btn = Button::builder(&dialog).with_label("Cancel").build(); + let ok_btn = Button::builder(&dialog).with_label("OK").build(); + buttons.add(&cancel_btn, 0, SizerFlag::All, 8); + buttons.add_spacer(8); + buttons.add(&ok_btn, 0, SizerFlag::All, 8); + sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); + dialog.set_sizer(sizer, true); - - dialog.show_modal(); - let value = text_field.get_value().to_string(); + + // Wire buttons to end the modal with appropriate return codes + let dlg_for_cancel = dialog.clone(); + cancel_btn.on_click(move |_| { + dlg_for_cancel.end_modal(ID_CANCEL as i32); + }); + let dlg_for_ok = dialog.clone(); + ok_btn.on_click(move |_| { + dlg_for_ok.end_modal(ID_OK as i32); + }); + + text_field.set_focus(); + + let rc = dialog.show_modal(); + let result = if rc == ID_OK as i32 { + Ok(text_field.get_value().to_string()) + } else { + Err("2FA cancelled".to_string()) + }; dialog.destroy(); - println!("2FA code entered: {}", value); - Ok(value) + result } } diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 8d34105a..13873715 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -151,7 +151,13 @@ impl PlumeFrameMessageHandler { } PlumeFrameMessage::AccountLogin(account) => { self.account_credentials = Some(account); - println!("Account logged in"); + let creds = crate::keychain::AccountCredentials; + let email = creds.get_email().unwrap_or_else(|_| "(unknown)".to_string()); + let msg = format!("Logged in as {}", email); + let dialog = MessageDialog::builder(&self.plume_frame.frame, &msg, "Signed In") + .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconInformation) + .build(); + dialog.show_modal(); } PlumeFrameMessage::AccountDeleted => { self.account_credentials = None; From ffe75791d7a1ed0a4432a1f19fc34be9784532a2 Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Mon, 10 Nov 2025 04:08:47 -0800 Subject: [PATCH 10/72] Update main.rs tt --- Cargo.lock | 63 +---------------------- apps/plumeimpactor/Cargo.toml | 1 - apps/plumeimpactor/resources/install.png | Bin 6540 -> 0 bytes apps/plumeimpactor/src/frame.rs | 31 +++++------ apps/plumeimpactor/src/handlers.rs | 4 +- apps/plumeimpactor/src/keychain.rs | 6 --- apps/plumeimpactor/src/pages/default.rs | 35 +------------ apps/plumeimpactor/src/pages/login.rs | 4 -- apps/plumesign/Cargo.toml | 2 + apps/plumesign/src/main.rs | 12 +++-- 10 files changed, 33 insertions(+), 125 deletions(-) delete mode 100644 apps/plumeimpactor/resources/install.png diff --git a/Cargo.lock b/Cargo.lock index a78399d8..15ae9fb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,12 +596,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - [[package]] name = "bytes" version = "1.10.1" @@ -1245,15 +1239,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - [[package]] name = "ff" version = "0.13.1" @@ -2007,19 +1992,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "image" -version = "0.25.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" -dependencies = [ - "bytemuck", - "byteorder-lite", - "moxcms", - "num-traits", - "png", -] - [[package]] name = "indexmap" version = "2.11.4" @@ -2397,16 +2369,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "moxcms" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" -dependencies = [ - "num-traits", - "pxfm", -] - [[package]] name = "native-tls" version = "0.2.14" @@ -2870,7 +2832,6 @@ dependencies = [ "futures", "grand_slam", "idevice", - "image", "keyring", "ldid2", "tokio", @@ -2887,22 +2848,11 @@ dependencies = [ "ldid2", "openssl", "plist", + "rustls 0.23.32", + "tokio", "types", ] -[[package]] -name = "png" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" -dependencies = [ - "bitflags 2.9.4", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "portable-atomic" version = "1.11.1" @@ -2983,15 +2933,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "pxfm" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde" -dependencies = [ - "num-traits", -] - [[package]] name = "quick-xml" version = "0.38.3" diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index 06adcf2c..b42c8619 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -14,7 +14,6 @@ ldid2 = { path = "../../crates/ldid2" } grand_slam = { path = "../../crates/grand_slam", features = ["vendored-botan"] } types = { path = "../../crates/types"} wxdragon = "0.9.2" -image = { version = "0.25.1", default-features = false, features = ["png"] } futures = "0.3.31" keyring = { version = "3.6.3", default-features = false, features = ["windows-native", "apple-native", "linux-native"] } diff --git a/apps/plumeimpactor/resources/install.png b/apps/plumeimpactor/resources/install.png deleted file mode 100644 index 2915b1ff6e434b2893bed5f049fadf5a8cb82de1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6540 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 OK}AUeSFCU=;Qs*fpD_{u diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index d35a764e..d152c409 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -27,6 +27,7 @@ pub struct PlumeFrame { pub install_page: InstallPage, pub usbmuxd_picker: Choice, + pub add_ipa_button: Button, pub apple_id_button: Button, pub login_dialog: LoginDialog, } @@ -44,9 +45,12 @@ impl PlumeFrame { let top_panel = Panel::builder(&frame).build(); let top_row = BoxSizer::builder(Orientation::Horizontal).build(); + let add_ipa_button = Button::builder(&top_panel).with_label("+").build(); let device_picker = Choice::builder(&top_panel).build(); let apple_id_button = Button::builder(&top_panel).with_label("Account").build(); + top_row.add(&add_ipa_button, 0, SizerFlag::All, 0); + top_row.add_spacer(12); top_row.add(&device_picker, 1, SizerFlag::Expand | SizerFlag::All, 0); top_row.add_spacer(12); top_row.add(&apple_id_button, 0, SizerFlag::All, 0); @@ -76,6 +80,7 @@ impl PlumeFrame { default_page, install_page, usbmuxd_picker: device_picker, + add_ipa_button, apple_id_button, login_dialog: create_login_dialog(&frame), }; @@ -255,8 +260,8 @@ impl PlumeFrame { let sender_for_logout = sender.clone(); move |_| { let logged_in = handler_for_account.borrow().account_credentials.is_some(); + if logged_in { - // Show account status with email and logout option let creds = AccountCredentials; let email = creds.get_email().unwrap_or_else(|_| "(unknown)".to_string()); @@ -267,23 +272,17 @@ impl PlumeFrame { let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(12); let label = StaticText::builder(&dialog) - .with_label(&format!("Logged in as {}", email)) + .with_label(&format!("Logged in as {:?} ({})", handler_for_account.borrow().account_credentials.clone().unwrap().get_name(), email)) .build(); sizer.add(&label, 0, SizerFlag::All, 12); let buttons = BoxSizer::builder(Orientation::Horizontal).build(); let logout_btn = Button::builder(&dialog).with_label("Log out").build(); - let close_btn = Button::builder(&dialog).with_label("Close").build(); buttons.add(&logout_btn, 0, SizerFlag::All, 8); - buttons.add_spacer(8); - buttons.add(&close_btn, 0, SizerFlag::All, 8); sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); dialog.set_sizer(sizer, true); - let dlg_close = dialog.clone(); - close_btn.on_click(move |_| { dlg_close.end_modal(ID_CANCEL as i32); }); - let dlg_logout = dialog.clone(); let sender_clone = sender_for_logout.clone(); logout_btn.on_click(move |_| { @@ -296,7 +295,6 @@ impl PlumeFrame { dialog.show_modal(); dialog.destroy(); } else { - // Not logged in, open login dialog login_dialog.show_modal(); } } @@ -389,14 +387,17 @@ impl PlumeFrame { } fn bind_file_handlers(&self, sender: mpsc::UnboundedSender) { - let handler_for_import = self.frame.clone(); - self.default_page.set_file_handlers( { let sender = sender.clone(); move |file_path| Self::process_package_file(sender.clone(), PathBuf::from(file_path)) }, - move || { + ); + + self.add_ipa_button.on_click({ + let sender = sender.clone(); + let handler_for_import = self.frame.clone(); + move |_| { let dialog = FileDialog::builder(&handler_for_import) .with_message("Open IPA File") .with_style(FileDialogStyle::default() | FileDialogStyle::Open) @@ -409,8 +410,8 @@ impl PlumeFrame { if let Some(file_path) = dialog.get_path() { Self::process_package_file(sender.clone(), PathBuf::from(file_path)); } - }, - ); + } + }); } /// This de-duplicates logic from the file handlers. @@ -459,7 +460,6 @@ impl PlumeFrame { dialog.set_sizer(sizer, true); - // Wire buttons to end the modal with appropriate return codes let dlg_for_cancel = dialog.clone(); cancel_btn.on_click(move |_| { dlg_for_cancel.end_modal(ID_CANCEL as i32); @@ -489,6 +489,7 @@ fn run_login_flow( ) -> Result { let anisette_config = AnisetteConfiguration::default().set_configuration_path(env::temp_dir()); + // println!("Anisette config path: {:?}", env::temp_dir()); let rt = match Builder::new_current_thread().enable_all().build() { Ok(rt) => rt, diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 13873715..ec306bfe 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -4,7 +4,7 @@ use tokio::sync::mpsc; use tokio::sync::mpsc::error::TryRecvError; use std::sync::mpsc as std_mpsc; -use grand_slam::{auth::Account, developer::DeveloperSession}; +use grand_slam::auth::Account; use types::{Device, Package, PlistInfoTrait}; use crate::frame::PlumeFrame; @@ -153,7 +153,7 @@ impl PlumeFrameMessageHandler { self.account_credentials = Some(account); let creds = crate::keychain::AccountCredentials; let email = creds.get_email().unwrap_or_else(|_| "(unknown)".to_string()); - let msg = format!("Logged in as {}", email); + let msg = format!("Logged in as {:?} ({})", self.account_credentials.clone().unwrap().get_name(), email); let dialog = MessageDialog::builder(&self.plume_frame.frame, &msg, "Signed In") .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconInformation) .build(); diff --git a/apps/plumeimpactor/src/keychain.rs b/apps/plumeimpactor/src/keychain.rs index 86acbffe..ebf57672 100644 --- a/apps/plumeimpactor/src/keychain.rs +++ b/apps/plumeimpactor/src/keychain.rs @@ -32,10 +32,4 @@ impl AccountCredentials { entry_pass.delete_credential()?; Ok(()) } - - pub fn credentials_exist(&self, email: String, password: String) -> Result { - let stored_email = self.get_email().unwrap_or_default(); - let stored_password = self.get_password().unwrap_or_default(); - Ok(stored_email == email && stored_password == password) - } } diff --git a/apps/plumeimpactor/src/pages/default.rs b/apps/plumeimpactor/src/pages/default.rs index 4e6c136d..e4358c35 100644 --- a/apps/plumeimpactor/src/pages/default.rs +++ b/apps/plumeimpactor/src/pages/default.rs @@ -1,7 +1,5 @@ use wxdragon::prelude::*; -const INSTALLER_IMAGE_BYTES: &[u8] = include_bytes!("../../resources/install.png"); -const INSTALLER_IMAGE_DIMENSIONS: u32 = 100; const WELCOME_TEXT: &str = "Drop your .ipa here"; #[derive(Clone)] @@ -14,8 +12,7 @@ impl DefaultPage { path.ends_with(".ipa") || path.ends_with(".tipa") } - // it seems that image is on top of the panel... - pub fn set_file_handlers(&self, on_drop: impl Fn(String) + 'static, on_click: impl Fn() + 'static) { + pub fn set_file_handlers(&self, on_drop: impl Fn(String) + 'static) { _ = FileDropTarget::builder(&self.panel) .with_on_drop_files(move |files, _, _| { if files.len() != 1 || !DefaultPage::is_allowed_file(&files[0]) { @@ -27,13 +24,7 @@ impl DefaultPage { .with_on_drag_over(move |_, _, _| DragResult::Move) .with_on_enter(move |_, _, _| DragResult::Move) .build(); - - self.panel.on_mouse_left_down(move |_evt| { - on_click(); - }); } - - } pub fn create_default_page(frame: &Frame) -> DefaultPage { @@ -41,28 +32,6 @@ pub fn create_default_page(frame: &Frame) -> DefaultPage { let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_stretch_spacer(1); - - if let Ok(img) = image::load_from_memory_with_format(INSTALLER_IMAGE_BYTES, image::ImageFormat::Png) { - let resized = img.resize_exact( - INSTALLER_IMAGE_DIMENSIONS, - INSTALLER_IMAGE_DIMENSIONS, - image::imageops::FilterType::Lanczos3, - ); - let rgba = resized.to_rgba8(); - let bitmap = Bitmap::from_rgba( - rgba.as_raw(), - INSTALLER_IMAGE_DIMENSIONS, - INSTALLER_IMAGE_DIMENSIONS, - ); - let static_bitmap = StaticBitmap::builder(&panel) - .with_bitmap(bitmap) - .with_size(Size::new( - INSTALLER_IMAGE_DIMENSIONS as i32, - INSTALLER_IMAGE_DIMENSIONS as i32, - )) - .build(); - sizer.add(&static_bitmap, 0, SizerFlag::AlignCenterHorizontal | SizerFlag::All, 20); - } let welcome_text = StaticText::builder(&panel) .with_label(WELCOME_TEXT) @@ -71,7 +40,7 @@ pub fn create_default_page(frame: &Frame) -> DefaultPage { sizer.add(&welcome_text, 0, SizerFlag::AlignCenterHorizontal | SizerFlag::All, 0); - sizer.add_stretch_spacer(2); + sizer.add_stretch_spacer(1); panel.set_sizer(sizer, true); diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 402ef3ee..7cd50e8e 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -17,10 +17,6 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(12); - let description = StaticText::builder(&dialog) - .with_label("In order to use the main features for PlumeImpactor, you will\nneed to sign into your Apple ID. But don't worry! Your\ncredentials are sent to Apple and not anywhere else!") - .build(); - sizer.add(&description, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 12); let email_row = BoxSizer::builder(Orientation::Horizontal).build(); let email_label = StaticText::builder(&dialog) diff --git a/apps/plumesign/Cargo.toml b/apps/plumesign/Cargo.toml index d937973e..2a413a3e 100644 --- a/apps/plumesign/Cargo.toml +++ b/apps/plumesign/Cargo.toml @@ -8,9 +8,11 @@ repository.workspace = true [dependencies] plist.workspace = true +tokio.workspace = true ldid2 = { path = "../../crates/ldid2" } grand_slam = { path = "../../crates/grand_slam", features = ["vendored-botan"] } types = { path = "../../crates/types"} +rustls = { version = "0.23.32", features = ["ring"] } clap = { version = "4.5", features = ["derive"] } openssl = { version = "0.10", features = ["vendored"] } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index 68a37f13..eb14e228 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -1,11 +1,15 @@ use std::path::PathBuf; use std::process::exit; +use std::sync::Arc; use clap::Parser; +use grand_slam::AnisetteConfiguration; +use grand_slam::auth::Account; use ldid2::certificate::Certificate; use ldid2::signing::signer::Signer; use ldid2::signing::signer_settings::SignerSettings; +use rustls::crypto::CryptoProvider; use types::Bundle; #[derive(Debug, Parser)] @@ -19,9 +23,12 @@ pub struct Cli { // bundle: PathBuf, } -fn main() { +#[tokio::main] +async fn main() { let cli = Cli::parse(); + rustls::crypto::ring::default_provider().install_default().expect("Failed to install rustls crypto provider"); + // if cli.pem_files.len() < 2 { // eprintln!("Please provide at least two PEM files (certificate and key) using --pem."); // exit(1); @@ -65,7 +72,6 @@ fn main() { // exit(1); // } // } - - + } From 0eeca911563d33b6a3044afedb4f28951694fdcb Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Mon, 10 Nov 2025 04:33:27 -0800 Subject: [PATCH 11/72] disallow drag on linux --- apps/plumeimpactor/src/frame.rs | 1 + apps/plumeimpactor/src/pages/default.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index d152c409..a6e655ae 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -387,6 +387,7 @@ impl PlumeFrame { } fn bind_file_handlers(&self, sender: mpsc::UnboundedSender) { + #[cfg(not(target_os = "linux"))] self.default_page.set_file_handlers( { let sender = sender.clone(); diff --git a/apps/plumeimpactor/src/pages/default.rs b/apps/plumeimpactor/src/pages/default.rs index e4358c35..a3f2945b 100644 --- a/apps/plumeimpactor/src/pages/default.rs +++ b/apps/plumeimpactor/src/pages/default.rs @@ -1,6 +1,9 @@ use wxdragon::prelude::*; +#[cfg(not(target_os = "linux"))] const WELCOME_TEXT: &str = "Drop your .ipa here"; +#[cfg(target_os = "linux")] +const WELCOME_TEXT: &str = "Press '+' and select an .ipa to get started"; #[derive(Clone)] pub struct DefaultPage { @@ -8,10 +11,12 @@ pub struct DefaultPage { } impl DefaultPage { + #[cfg(not(target_os = "linux"))] fn is_allowed_file(path: &str) -> bool { path.ends_with(".ipa") || path.ends_with(".tipa") } + #[cfg(not(target_os = "linux"))] pub fn set_file_handlers(&self, on_drop: impl Fn(String) + 'static) { _ = FileDropTarget::builder(&self.panel) .with_on_drop_files(move |files, _, _| { From a6e6c410e8301b7e320c4dd46e53eecaf6b44e10 Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Mon, 10 Nov 2025 10:39:59 -0800 Subject: [PATCH 12/72] picky --- Cargo.lock | 1 + apps/plumeimpactor/Cargo.toml | 1 + apps/plumeimpactor/src/handlers.rs | 6 ++++-- apps/plumeimpactor/src/main.rs | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15ae9fb5..2403cba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2834,6 +2834,7 @@ dependencies = [ "idevice", "keyring", "ldid2", + "rustls 0.23.32", "tokio", "types", "wxdragon", diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index b42c8619..598c787a 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -15,6 +15,7 @@ grand_slam = { path = "../../crates/grand_slam", features = ["vendored-botan"] } types = { path = "../../crates/types"} wxdragon = "0.9.2" futures = "0.3.31" +rustls = { version = "0.23.32", features = ["ring"] } keyring = { version = "3.6.3", default-features = false, features = ["windows-native", "apple-native", "linux-native"] } diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index ec306bfe..d85d28bb 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -182,9 +182,11 @@ impl PlumeFrameMessageHandler { } } } +} - // --- Device Picker Helpers --- +// USBMUXD HANDLERS +impl PlumeFrameMessageHandler { fn usbmuxd_picker_rebuild_contents(&self) { self.plume_frame.usbmuxd_picker.clear(); for item_string in &self.usbmuxd_device_list { @@ -232,4 +234,4 @@ impl PlumeFrameMessageHandler { self.usbmuxd_selected_device_id = None; } } -} +} \ No newline at end of file diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index df55a582..ff3f06b0 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -9,6 +9,9 @@ pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env! #[tokio::main] async fn main() { + // its very picky + _ = rustls::crypto::ring::default_provider().install_default().unwrap(); + let _ = wxdragon::main(|_| { frame::PlumeFrame::new().show(); }); From 17a7c2449184159b2e91098cce5aeb35574a61b2 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 11 Nov 2025 06:33:30 -0800 Subject: [PATCH 13/72] cli --- .editorconfig | 5 +- Cargo.lock | 11 -- apps/plumeimpactor/src/frame.rs | 162 +++++++++--------- apps/plumeimpactor/src/handlers.rs | 30 +--- apps/plumeimpactor/src/pages/default.rs | 11 +- apps/plumeimpactor/src/pages/install.rs | 35 ++-- apps/plumeimpactor/src/pages/login.rs | 58 ++++--- apps/plumesign/Cargo.toml | 1 - apps/plumesign/src/main.rs | 150 +++++++++------- crates/grand_slam/src/certificate/mod.rs | 1 + crates/grand_slam/src/lib.rs | 1 + crates/ldid2/src/provision.rs | 51 +++++- crates/ldid2/src/signing/signer.rs | 126 ++++++++++++-- crates/ldid2/src/signing/signer_settings.rs | 10 +- crates/types/src/bundle.rs | 37 +++- crates/types/src/lib.rs | 1 + .../PlumeImpactor.app/Contents/Info.plist | 90 +++++----- 17 files changed, 463 insertions(+), 317 deletions(-) create mode 100644 crates/grand_slam/src/certificate/mod.rs diff --git a/.editorconfig b/.editorconfig index bb8ea53d..df9a904b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,7 @@ root = true [*] -indent_style = tab +indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 @@ -10,4 +10,5 @@ insert_final_newline = true [*.{yml,yaml}] indent_style = space -indent_size = 2 \ No newline at end of file +indent_size = 2 + diff --git a/Cargo.lock b/Cargo.lock index 2403cba4..e213cdd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2584,15 +2584,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" -[[package]] -name = "openssl-src" -version = "300.5.3+3.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.109" @@ -2601,7 +2592,6 @@ checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] @@ -2847,7 +2837,6 @@ dependencies = [ "clap", "grand_slam", "ldid2", - "openssl", "plist", "rustls 0.23.32", "tokio", diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index a6e655ae..e0b5d2c1 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -193,6 +193,7 @@ impl PlumeFrame { PlumeFrameMessage::Error(format!("Failed to listen for events: {}", e)) } }; + if sender.send(msg).is_err() { break; } @@ -201,7 +202,6 @@ impl PlumeFrame { }); } - /// Spawns the automatic account login thread. fn spawn_auto_login_thread(sender: mpsc::UnboundedSender) { thread::spawn(move || { let creds = AccountCredentials; @@ -233,25 +233,19 @@ impl PlumeFrame { message_handler: Rc>, ) { // --- Device Picker --- - + let handler_for_choice = message_handler.clone(); let picker_clone = self.usbmuxd_picker.clone(); - self.usbmuxd_picker - .on_selection_changed(move |_event_data| { - let mut handler = handler_for_choice.borrow_mut(); - - if let Some(index) = picker_clone.get_selection() { - if let Some(selected_item) = handler.usbmuxd_device_list.get(index as usize) { - handler.usbmuxd_selected_device_id = - Some(selected_item.usbmuxd_device.device_id.to_string()); - } - } else { - handler.usbmuxd_selected_device_id = None; - } - }); + self.usbmuxd_picker.on_selection_changed(move |_| { + let mut handler = handler_for_choice.borrow_mut(); + handler.usbmuxd_selected_device_id = picker_clone + .get_selection() + .and_then(|i| handler.usbmuxd_device_list.get(i as usize)) + .map(|item| item.usbmuxd_device.device_id.to_string()); + }); // --- Apple ID / Login Dialog --- - + let login_dialog_rc = Rc::new(self.login_dialog.clone()); self.apple_id_button.on_click({ let login_dialog = login_dialog_rc.clone(); @@ -263,7 +257,9 @@ impl PlumeFrame { if logged_in { let creds = AccountCredentials; - let email = creds.get_email().unwrap_or_else(|_| "(unknown)".to_string()); + let email = creds + .get_email() + .unwrap_or_else(|_| "(unknown)".to_string()); let dialog = Dialog::builder(&frame_for_dialog, "Account") .with_style(DialogStyle::DefaultDialogStyle) @@ -272,7 +268,16 @@ impl PlumeFrame { let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(12); let label = StaticText::builder(&dialog) - .with_label(&format!("Logged in as {:?} ({})", handler_for_account.borrow().account_credentials.clone().unwrap().get_name(), email)) + .with_label(&format!( + "Logged in as {:?} ({})", + handler_for_account + .borrow() + .account_credentials + .clone() + .unwrap() + .get_name(), + email + )) .build(); sizer.add(&label, 0, SizerFlag::All, 12); @@ -287,8 +292,8 @@ impl PlumeFrame { let sender_clone = sender_for_logout.clone(); logout_btn.on_click(move |_| { let creds = AccountCredentials; - let _ = creds.delete_password(); - let _ = sender_clone.send(PlumeFrameMessage::AccountDeleted); + creds.delete_password().ok(); + sender_clone.send(PlumeFrameMessage::AccountDeleted).ok(); dlg_logout.end_modal(ID_OK as i32); }); @@ -300,36 +305,30 @@ impl PlumeFrame { } }); - self.login_dialog.set_cancel_handler({ - let login_dialog = login_dialog_rc.clone(); - move || { - login_dialog.clear_fields(); - login_dialog.hide(); - } - }); - // --- Login Dialog "Next" Button --- - + self.bind_login_dialog_next_handler(sender.clone(), login_dialog_rc); // --- File Drop/Open Handlers --- - + self.bind_file_handlers(sender.clone()); // --- Install Page Handlers --- - - let sender_for_cancel = sender.clone(); - self.install_page.set_cancel_handler(move || { - sender_for_cancel - .send(PlumeFrameMessage::PackageDeselected) - .ok(); + + self.install_page.set_cancel_handler({ + let sender = sender.clone(); + move || { + sender.send(PlumeFrameMessage::PackageDeselected).ok(); + } }); - let sender_for_install = sender.clone(); - self.install_page.set_install_handler(move || { - sender_for_install - .send(PlumeFrameMessage::PackageInstallationStarted) - .ok(); + self.install_page.set_install_handler({ + let sender = sender.clone(); + move || { + sender + .send(PlumeFrameMessage::PackageInstallationStarted) + .ok(); + } }); } @@ -355,32 +354,29 @@ impl PlumeFrame { return; } - // Save credentials to keyring let creds = AccountCredentials; if let Err(e) = creds.set_credentials(email.clone(), password.clone()) { - let _ = sender + sender .send(PlumeFrameMessage::Error(format!( "Failed to save credentials: {}", e - ))); + ))) + .ok(); return; } - // Hide dialog before starting login login_dialog.clear_fields(); login_dialog.hide(); let sender_for_login_thread = sender.clone(); thread::spawn(move || { match run_login_flow(sender_for_login_thread.clone(), email, password) { - Ok(account) => { - let _ = sender_for_login_thread - .send(PlumeFrameMessage::AccountLogin(account)); - } - Err(e) => { - let _ = sender_for_login_thread - .send(PlumeFrameMessage::Error(format!("Login failed: {}", e))); - } + Ok(account) => sender_for_login_thread + .send(PlumeFrameMessage::AccountLogin(account)) + .ok(), + Err(e) => sender_for_login_thread + .send(PlumeFrameMessage::Error(format!("Login failed: {}", e))) + .ok(), } }); }); @@ -388,12 +384,10 @@ impl PlumeFrame { fn bind_file_handlers(&self, sender: mpsc::UnboundedSender) { #[cfg(not(target_os = "linux"))] - self.default_page.set_file_handlers( - { - let sender = sender.clone(); - move |file_path| Self::process_package_file(sender.clone(), PathBuf::from(file_path)) - }, - ); + self.default_page.set_file_handlers({ + let sender = sender.clone(); + move |file_path| Self::process_package_file(sender.clone(), PathBuf::from(file_path)) + }); self.add_ipa_button.on_click({ let sender = sender.clone(); @@ -408,6 +402,7 @@ impl PlumeFrame { if dialog.show_modal() != ID_OK { return; } + if let Some(file_path) = dialog.get_path() { Self::process_package_file(sender.clone(), PathBuf::from(file_path)); } @@ -415,11 +410,7 @@ impl PlumeFrame { }); } - /// This de-duplicates logic from the file handlers. - fn process_package_file( - sender: mpsc::UnboundedSender, - file_path: PathBuf, - ) { + fn process_package_file(sender: mpsc::UnboundedSender, file_path: PathBuf) { match Package::new(file_path) { Ok(package) => { sender @@ -445,29 +436,35 @@ impl PlumeFrame { let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(16); - let field_label = StaticText::builder(&dialog).with_label(label).build(); + sizer.add( + &StaticText::builder(&dialog).with_label(label).build(), + 0, + SizerFlag::All, + 12, + ); let text_field = TextCtrl::builder(&dialog).build(); - sizer.add(&field_label, 0, SizerFlag::All, 12); sizer.add(&text_field, 0, SizerFlag::Expand | SizerFlag::All, 8); - // Buttons row - let buttons = BoxSizer::builder(Orientation::Horizontal).build(); - let cancel_btn = Button::builder(&dialog).with_label("Cancel").build(); - let ok_btn = Button::builder(&dialog).with_label("OK").build(); - buttons.add(&cancel_btn, 0, SizerFlag::All, 8); - buttons.add_spacer(8); - buttons.add(&ok_btn, 0, SizerFlag::All, 8); - sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); + let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); + + let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); + let ok_button = Button::builder(&dialog).with_label("OK").build(); + + button_sizer.add(&cancel_button, 0, SizerFlag::All, 8); + button_sizer.add_spacer(8); + button_sizer.add(&ok_button, 0, SizerFlag::All, 8); + + sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 8); dialog.set_sizer(sizer, true); - let dlg_for_cancel = dialog.clone(); - cancel_btn.on_click(move |_| { - dlg_for_cancel.end_modal(ID_CANCEL as i32); + cancel_button.on_click({ + let dialog = dialog.clone(); + move |_| dialog.end_modal(ID_CANCEL as i32) }); - let dlg_for_ok = dialog.clone(); - ok_btn.on_click(move |_| { - dlg_for_ok.end_modal(ID_OK as i32); + ok_button.on_click({ + let dialog = dialog.clone(); + move |_| dialog.end_modal(ID_OK as i32) }); text_field.set_focus(); @@ -483,14 +480,13 @@ impl PlumeFrame { } } -fn run_login_flow( +pub fn run_login_flow( sender: mpsc::UnboundedSender, email: String, password: String, ) -> Result { let anisette_config = - AnisetteConfiguration::default().set_configuration_path(env::temp_dir()); - // println!("Anisette config path: {:?}", env::temp_dir()); + AnisetteConfiguration::default().set_configuration_path(PathBuf::from(env::temp_dir())); let rt = match Builder::new_current_thread().enable_all().build() { Ok(rt) => rt, diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index d85d28bb..f6d2f34c 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -74,7 +74,6 @@ impl PlumeFrameMessageHandler { fn handle_message(&mut self, message: PlumeFrameMessage) { match message { PlumeFrameMessage::DeviceConnected(device) => { - println!("Device connected: {}", device); if !self .usbmuxd_device_list .iter() @@ -91,7 +90,6 @@ impl PlumeFrameMessageHandler { } } PlumeFrameMessage::DeviceDisconnected(device_id) => { - println!("Device disconnected: {}", device_id); if let Some(index) = self .usbmuxd_device_list .iter() @@ -111,7 +109,6 @@ impl PlumeFrameMessageHandler { let package_id = package .get_bundle_identifier() .unwrap_or_else(|| "Unknown".to_string()); - println!("Package selected: {}", package_name); self.package_selected = Some(package); self.plume_frame .install_page @@ -121,33 +118,13 @@ impl PlumeFrameMessageHandler { self.plume_frame.frame.layout(); } PlumeFrameMessage::PackageDeselected => { - println!("Package deselected"); self.package_selected = None; self.plume_frame.install_page.panel.hide(); self.plume_frame.default_page.panel.show(true); self.plume_frame.frame.layout(); } PlumeFrameMessage::PackageInstallationStarted => { - let package = match &self.package_selected { - Some(pkg) => pkg.clone(), - None => { - self.handle_message(PlumeFrameMessage::Error( - "No package selected for installation.".to_string(), - )); - return; - } - }; - - let account = match &self.account_credentials { - Some(acc) => acc.clone(), - None => { - self.handle_message(PlumeFrameMessage::Error( - "Installation failed: No account logged in.".to_string(), - )); - return; - } - }; - // + todo!() } PlumeFrameMessage::AccountLogin(account) => { self.account_credentials = Some(account); @@ -161,7 +138,6 @@ impl PlumeFrameMessageHandler { } PlumeFrameMessage::AccountDeleted => { self.account_credentials = None; - println!("Account deleted"); } PlumeFrameMessage::AwaitingTwoFactorCode(tx) => { let result = self.plume_frame.create_single_field_dialog( @@ -171,10 +147,10 @@ impl PlumeFrameMessageHandler { if let Err(e) = tx.send(result) { println!("Failed to send 2FA code back to background thread: {:?}", e); + } } PlumeFrameMessage::Error(error_msg) => { - println!("Error: {}", error_msg); let dialog = MessageDialog::builder(&self.plume_frame.frame, &error_msg, "Error") .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning) .build(); @@ -234,4 +210,4 @@ impl PlumeFrameMessageHandler { self.usbmuxd_selected_device_id = None; } } -} \ No newline at end of file +} diff --git a/apps/plumeimpactor/src/pages/default.rs b/apps/plumeimpactor/src/pages/default.rs index a3f2945b..c11e1426 100644 --- a/apps/plumeimpactor/src/pages/default.rs +++ b/apps/plumeimpactor/src/pages/default.rs @@ -43,13 +43,16 @@ pub fn create_default_page(frame: &Frame) -> DefaultPage { .with_style(StaticTextStyle::AlignCenterHorizontal) .build(); - sizer.add(&welcome_text, 0, SizerFlag::AlignCenterHorizontal | SizerFlag::All, 0); + sizer.add( + &welcome_text, + 0, + SizerFlag::AlignCenterHorizontal | SizerFlag::All, + 0, + ); sizer.add_stretch_spacer(1); panel.set_sizer(sizer, true); - DefaultPage { - panel - } + DefaultPage { panel } } diff --git a/apps/plumeimpactor/src/pages/install.rs b/apps/plumeimpactor/src/pages/install.rs index a1b43046..ce8e1f96 100644 --- a/apps/plumeimpactor/src/pages/install.rs +++ b/apps/plumeimpactor/src/pages/install.rs @@ -13,28 +13,27 @@ pub fn create_install_page(frame: &Frame) -> InstallPage { let main_sizer = BoxSizer::builder(Orientation::Vertical).build(); - let top_text = StaticText::builder(&panel) - .with_label("Unknown") - .build(); - main_sizer.add(&top_text, 0, SizerFlag::Left | SizerFlag::Top, 14); + let top_text = StaticText::builder(&panel).with_label("Unknown").build(); + + main_sizer.add(&top_text, 0, SizerFlag::Left, 14); main_sizer.add_stretch_spacer(1); let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); - let cancel_button = Button::builder(&panel) - .with_label("Cancel") - .build(); - - let install_button = Button::builder(&panel) - .with_label("Install") - .build(); + let cancel_button = Button::builder(&panel).with_label("Cancel").build(); + let install_button = Button::builder(&panel).with_label("Install").build(); button_sizer.add_stretch_spacer(1); button_sizer.add(&cancel_button, 0, SizerFlag::Right, 12); button_sizer.add(&install_button, 0, SizerFlag::All, 0); - main_sizer.add_sizer(&button_sizer, 0, SizerFlag::Right | SizerFlag::Bottom | SizerFlag::Expand, 14); + main_sizer.add_sizer( + &button_sizer, + 0, + SizerFlag::Right | SizerFlag::Bottom | SizerFlag::Expand, + 14, + ); panel.set_sizer(main_sizer, true); @@ -52,12 +51,12 @@ impl InstallPage { on_cancel(); }); } - - pub fn set_install_handler(&self, on_install: impl Fn() + 'static) { - self.install_button.on_click(move |_evt| { - on_install(); - }); - } + + pub fn set_install_handler(&self, on_install: impl Fn() + 'static) { + self.install_button.on_click(move |_evt| { + on_install(); + }); + } pub fn set_top_text(&self, text: &str) { self.top_text.set_label(text); diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 7cd50e8e..9c651319 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -5,7 +5,6 @@ pub struct LoginDialog { pub dialog: Dialog, pub email_field: TextCtrl, pub password_field: TextCtrl, - pub cancel_button: Button, pub next_button: Button, } @@ -17,24 +16,33 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(12); - - let email_row = BoxSizer::builder(Orientation::Horizontal).build(); - let email_label = StaticText::builder(&dialog) - .with_label(" Email:") - .build(); - let email_field = TextCtrl::builder(&dialog).build(); - email_row.add(&email_label, 0, SizerFlag::AlignCenterVertical | SizerFlag::All, 8); - email_row.add(&email_field, 1, SizerFlag::Expand | SizerFlag::All, 12); - sizer.add_sizer(&email_row, 0, SizerFlag::Expand | SizerFlag::All, 0); - - let password_row = BoxSizer::builder(Orientation::Horizontal).build(); - let password_label = StaticText::builder(&dialog).with_label("Password:").build(); - let password_field = TextCtrl::builder(&dialog) - .with_style(TextCtrlStyle::Password) - .build(); - password_row.add(&password_label, 0, SizerFlag::AlignCenterVertical | SizerFlag::All, 8); - password_row.add(&password_field, 1, SizerFlag::Expand | SizerFlag::All, 12); - sizer.add_sizer(&password_row, 0, SizerFlag::Expand | SizerFlag::All, 0); + let email_row = BoxSizer::builder(Orientation::Horizontal).build(); + let email_label = StaticText::builder(&dialog) + .with_label(" Email:") + .build(); + let email_field = TextCtrl::builder(&dialog).build(); + email_row.add( + &email_label, + 0, + SizerFlag::AlignCenterVertical | SizerFlag::All, + 8, + ); + email_row.add(&email_field, 1, SizerFlag::Expand | SizerFlag::All, 12); + sizer.add_sizer(&email_row, 0, SizerFlag::Expand | SizerFlag::All, 0); + + let password_row = BoxSizer::builder(Orientation::Horizontal).build(); + let password_label = StaticText::builder(&dialog).with_label("Password:").build(); + let password_field = TextCtrl::builder(&dialog) + .with_style(TextCtrlStyle::Password) + .build(); + password_row.add( + &password_label, + 0, + SizerFlag::AlignCenterVertical | SizerFlag::All, + 8, + ); + password_row.add(&password_field, 1, SizerFlag::Expand | SizerFlag::All, 12); + sizer.add_sizer(&password_row, 0, SizerFlag::Expand | SizerFlag::All, 0); let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); @@ -47,11 +55,15 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { dialog.set_sizer(sizer, true); + cancel_button.on_click({ + let dialog = dialog.clone(); + move |_| dialog.end_modal(ID_CANCEL as i32) + }); + LoginDialog { dialog, email_field, password_field, - cancel_button, next_button, } } @@ -78,12 +90,6 @@ impl LoginDialog { self.dialog.end_modal(0); } - pub fn set_cancel_handler(&self, on_cancel: impl Fn() + 'static) { - self.cancel_button.on_click(move |_evt| { - on_cancel(); - }); - } - pub fn set_next_handler(&self, on_next: impl Fn() + 'static) { self.next_button.on_click(move |_evt| { on_next(); diff --git a/apps/plumesign/Cargo.toml b/apps/plumesign/Cargo.toml index 2a413a3e..fb10a033 100644 --- a/apps/plumesign/Cargo.toml +++ b/apps/plumesign/Cargo.toml @@ -15,4 +15,3 @@ types = { path = "../../crates/types"} rustls = { version = "0.23.32", features = ["ring"] } clap = { version = "4.5", features = ["derive"] } -openssl = { version = "0.10", features = ["vendored"] } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index eb14e228..9d8ac3b6 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -1,77 +1,109 @@ use std::path::PathBuf; use std::process::exit; -use std::sync::Arc; use clap::Parser; -use grand_slam::AnisetteConfiguration; -use grand_slam::auth::Account; +use clap::{Args, Subcommand}; use ldid2::certificate::Certificate; +use ldid2::provision; use ldid2::signing::signer::Signer; -use ldid2::signing::signer_settings::SignerSettings; -use rustls::crypto::CryptoProvider; -use types::Bundle; +use ldid2::signing::signer_settings::{SignerSettings, SignerMode}; #[derive(Debug, Parser)] #[command(author, version, about, disable_help_subcommand = true)] pub struct Cli { - // #[arg(short = 'w', help = "Shallow (sign only the top-level bundle)")] - // shallow: bool, - // #[arg(long = "pem", value_name = "PEM", num_args = 1.., help = "Paths to PEM files")] - // pem_files: Vec, - // #[arg(value_name = "BUNDLE", required = true, value_parser = clap::value_parser!(PathBuf), help = "Path to bundle to sign")] - // bundle: PathBuf, + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Debug, Subcommand)] +pub enum Commands { + Sign(SignArgs), +} + +#[derive(Debug, Args)] +pub struct SignArgs { + #[arg(long = "pem", value_name = "PEM", num_args = 1.., required = true)] + pub pem_files: Vec, + + #[arg(long = "provision", value_name = "PROVISION", num_args = 1.., required = true)] + pub provisioning_files: Vec, + + #[arg(value_name = "PACKAGE", long = "package", required = false)] + pub package: Option, + + #[arg(value_name = "BUNDLE", long = "bundle", required = false)] + pub bundle: Option, + + #[arg(short = 'w', long = "shallow", default_value_t = false)] + pub shallow: bool, + + #[arg(long = "bundle-id", value_name = "BUNDLE_ID")] + pub bundle_identifier: Option, + + #[arg(long = "name", value_name = "NAME")] + pub name: Option, + + #[arg(long = "version", value_name = "VERSION")] + pub version: Option, } #[tokio::main] async fn main() { let cli = Cli::parse(); - rustls::crypto::ring::default_provider().install_default().expect("Failed to install rustls crypto provider"); - - // if cli.pem_files.len() < 2 { - // eprintln!("Please provide at least two PEM files (certificate and key) using --pem."); - // exit(1); - // } - - // let signing_key = match Certificate::new(cli.pem_files.clone().into()) { - // Ok(cert) => cert, - // Err(e) => { - // eprintln!("Failed to create Certificate: {}", e); - // exit(1); - // } - // }; - - // let mut signer_settings = SignerSettings::default(); - // signer_settings.sign_shallow = cli.shallow; - - // let signer = Signer::new(Some(signing_key), signer_settings); - // if let Err(e) = signer.sign(vec![cli.bundle.clone()]) { - // eprintln!("Failed to sign bundle {:?}: {}", cli.bundle, e); - // exit(1); - // } - - // println!("{:?}", cli.bundle); - - // let bundle = Bundle::new(cli.bundle.clone()); - // match bundle { - // Ok(b) => { - // match b.get_embedded_bundles() { - // Ok(embedded_bundles) if !embedded_bundles.is_empty() => { - // for embedded_bundle in embedded_bundles { - // println!("{:?}", embedded_bundle.get_dir()); - // } - // } - // _ => { - // println!("No embedded bundles found."); - // } - // } - // } - // Err(e) => { - // eprintln!("Failed to open bundle {:?}: {}", cli.bundle, e); - // exit(1); - // } - // } - - + rustls::crypto::ring::default_provider() + .install_default() + .expect("Failed to install rustls crypto provider"); + + match &cli.command { + Commands::Sign(args) => { + if args.pem_files.len() < 2 { + eprintln!("Error: At least two PEM files (certificate and key) are required via --pem."); + exit(1); + } + + let target = args.bundle.as_ref().or(args.package.as_ref()).cloned(); + if target.is_none() { + eprintln!("Error: Either --bundle or --package must be specified."); + exit(1); + } + + let signing_key = Certificate::new(args.pem_files.clone().into()).unwrap_or_else(|e| { + eprintln!("Failed to create Certificate: {e}"); + exit(1); + }); + + let provisioning_files = args.provisioning_files.iter() + .map(provision::MobileProvision::new) + .collect::, _>>() + .unwrap_or_else(|e| { + eprintln!("Failed to load provisioning profiles: {e}"); + exit(1); + }); + + let signer_settings = SignerSettings { + sign_shallow: args.shallow, + sign_mode: if provisioning_files.len() == 1 { + SignerMode::Zsign + } else { + SignerMode::Default + }, + custom_name: args.name.clone(), + custom_identifier: args.bundle_identifier.clone(), + custom_build_version: args.version.clone(), + ..Default::default() + }; + + let signer = Signer::new(Some(signing_key), signer_settings, provisioning_files); + + let target_path = target.unwrap(); + if let Err(e) = signer.sign(target_path.clone()) { + eprintln!("Failed to sign target: {e}"); + exit(1); + } + + println!("Signed target: {:?}", target_path); + } + } } diff --git a/crates/grand_slam/src/certificate/mod.rs b/crates/grand_slam/src/certificate/mod.rs new file mode 100644 index 00000000..db53a0c9 --- /dev/null +++ b/crates/grand_slam/src/certificate/mod.rs @@ -0,0 +1 @@ +pub mod identity; diff --git a/crates/grand_slam/src/lib.rs b/crates/grand_slam/src/lib.rs index 020b686b..a7dfe95c 100644 --- a/crates/grand_slam/src/lib.rs +++ b/crates/grand_slam/src/lib.rs @@ -1,5 +1,6 @@ pub mod auth; pub mod developer; +pub mod certificate; use plist::Dictionary; use serde_json::Value; diff --git a/crates/ldid2/src/provision.rs b/crates/ldid2/src/provision.rs index d97f8e6b..b491998d 100644 --- a/crates/ldid2/src/provision.rs +++ b/crates/ldid2/src/provision.rs @@ -7,7 +7,7 @@ use errors::Error; pub struct MobileProvision { provision_file: PathBuf, - provision_entitlements_dictionary: Value, + provisioning_plist: Value, } impl MobileProvision { @@ -18,11 +18,11 @@ impl MobileProvision { return Err(Error::ProvisioningEntitlementsUnknown); } - let provision_entitlements_dictionary = Self::extract_entitlements_from_provision_file(&path)?; + let provisioning_plist = Self::extract_plist_from_provision_file(&path)?; Ok(Self { provision_file: path.clone(), - provision_entitlements_dictionary, + provisioning_plist, }) } @@ -30,31 +30,64 @@ impl MobileProvision { &self.provision_file } - fn extract_entitlements_from_provision_file(provision_file: &PathBuf) -> Result { + fn extract_plist_from_provision_file(provision_file: &PathBuf) -> Result { let data = fs::read(provision_file)?; let start = data.windows(6).position(|w| w == b"").ok_or(Error::ProvisioningEntitlementsUnknown)? + 8; let plist_data = &data[start..end]; - let plist = plist::Value::from_reader_xml(plist_data)?; + Ok(plist) + } + + fn extract_entitlements_from_provision_file(&self) -> Result { + let plist = self.provisioning_plist.clone(); let dict = plist .as_dictionary() .and_then(|d| d.get("Entitlements")) .and_then(|v| v.as_dictionary()) .cloned() .ok_or(Error::ProvisioningEntitlementsUnknown)?; - Ok(Value::Dictionary(dict)) } pub fn get_entitlements_as_bytes(&self) -> Result, Error> { let mut buf = Vec::new(); - self.provision_entitlements_dictionary.to_writer_xml(&mut buf)?; + let provisioning_entitlements_dictionary = self.extract_entitlements_from_provision_file()?; + provisioning_entitlements_dictionary.to_writer_xml(&mut buf)?; Ok(buf) } - pub fn get_entitlements_dictionary(&self) -> Result<&Dictionary, Error> { - self.provision_entitlements_dictionary.as_dictionary().ok_or(Error::ProvisioningEntitlementsUnknown) + pub fn get_entitlements_dictionary(&self) -> Result { + let provisioning_entitlements_dictionary = self.extract_entitlements_from_provision_file()?; + provisioning_entitlements_dictionary + .as_dictionary() + .cloned() + .ok_or(Error::ProvisioningEntitlementsUnknown) } } +impl MobileProvision { + pub fn get_apple_team_id(&self) -> Option { + let dict = self.get_entitlements_dictionary().ok()?; + let team_id_opt = dict.get("application-identifier").and_then(|v| v.as_string()); + + let prefix_opt = self.provisioning_plist + .as_dictionary() + .and_then(|d| d.get("ApplicationIdentifierPrefix")) + .and_then(|v| v.as_array()) + .and_then(|arr| arr.get(0)) + .and_then(|v| v.as_string()); + + match (team_id_opt, prefix_opt) { + (Some(team_id), Some(prefix)) => { + let mut rest = &team_id[prefix.len()..]; + if rest.starts_with('.') { + rest = &rest[1..]; + } + Some(rest.to_string()) + } + (Some(team_id), None) => Some(team_id.to_string()), + _ => None, + } + } +} diff --git a/crates/ldid2/src/signing/signer.rs b/crates/ldid2/src/signing/signer.rs index cf9071f4..520d1a16 100644 --- a/crates/ldid2/src/signing/signer.rs +++ b/crates/ldid2/src/signing/signer.rs @@ -4,43 +4,131 @@ use std::path::PathBuf; use apple_codesign::{SigningSettings, UnifiedSigner}; use errors::Error; -use crate::certificate::Certificate; -use super::signer_settings::SignerSettings; -use types::Bundle; +use crate::{certificate::Certificate, provision::MobileProvision}; +use super::signer_settings::{SignerSettings, SignerMode}; +use types::{Bundle, PlistInfoTrait}; pub struct Signer { certificate: Option, settings: SignerSettings, + provisioning_files: Vec, } impl Signer { - pub fn new(certificate: Option, settings: SignerSettings) -> Self { - Signer { certificate, settings } + pub fn new( + certificate: Option, + settings: SignerSettings, + provisioning_files: Vec, + ) -> Self { + Self { + certificate, + settings, + provisioning_files, + } } - pub fn sign(&self, paths: Vec) -> Result<(), Error> { - let mut settings = SigningSettings::default(); - - if let Some(certificate) = &self.certificate { - certificate.load_into_signing_settings(&mut settings)?; + pub fn sign(&self, path: PathBuf) -> Result<(), Error> { + let bundle = Bundle::new(path.clone())?; + let bundles = bundle.get_embedded_bundles()?; + + if let Some(new_identifier) = self.settings.custom_identifier.as_ref() { + if let Some(old_identifier) = bundle.get_bundle_identifier() { + for embedded_bundle in &bundles { + embedded_bundle.set_matching_identifier( + &old_identifier, + &new_identifier, + )?; + } + + bundle.set_bundle_identifier(new_identifier)?; + } } - settings.set_team_id_from_signing_certificate(); - settings.set_shallow(self.settings.sign_shallow); - settings.set_for_notarization(false); - - let signer = UnifiedSigner::new(settings); - + if let Some(new_name) = self.settings.custom_name.as_ref() { + bundle.set_name(new_name)?; + } + if let Some(new_version) = self.settings.custom_build_version.as_ref() { + bundle.set_version(new_version)?; + } + + match self.settings.sign_mode { + SignerMode::Zsign => { + if let Some(prov) = self.provisioning_files.get(0) { + if self.settings.embed_mobileprovision { + fs::copy(prov.get_file_path(), bundle.get_dir().join("embedded.mobileprovision"))?; + } + + let mut settings = self.build_base_settings(false)?; + if let Ok(ent_xml) = prov.get_entitlements_as_bytes() { + settings + .set_entitlements_xml(apple_codesign::SettingsScope::Main, String::from_utf8_lossy(&ent_xml)) + .ok(); + } + + UnifiedSigner::new(settings).sign_path_in_place(bundle.get_dir())?; + } + } + SignerMode::Default => { + let mut sorted_bundles = bundles.clone(); + sorted_bundles.push(bundle.clone()); + sorted_bundles.sort_by_key(|b| b.get_dir().components().count()); + sorted_bundles.reverse(); - // signer.sign_path_in_place(&paths)?; + for bundle in &sorted_bundles { + + let mut settings = self.build_base_settings(true)?; - if let Some(certificate) = &self.certificate { - if let Some(key) = &certificate.key { + if bundle._type == types::BundleType::AppExtension || bundle._type == types::BundleType::App { + let mut matched_prov = None; + + println!("hii"); + println!("bundleid: {}", bundle.get_bundle_identifier().unwrap_or("no bundle id".to_string())); + + for prov in &self.provisioning_files { + println!("Checking provision: {:?}", prov.get_file_path()); + println!("teamid: {}", prov.get_apple_team_id().unwrap_or("no team id".to_string())); + if let (Some(bundle_id), Some(team_id)) = (bundle.get_bundle_identifier(), prov.get_apple_team_id()) { + if team_id == bundle_id { + matched_prov = Some(prov); + break; + } + } + } + + let prov = matched_prov.unwrap_or_else(|| &self.provisioning_files[0]); + fs::copy(prov.get_file_path(), bundle.get_dir().join("embedded.mobileprovision"))?; + println!("Moved {:?} to {:?}", prov.get_file_path(), bundle.get_dir().join("embedded.mobileprovision")); + + if let Ok(ent_xml) = prov.get_entitlements_as_bytes() { + settings + .set_entitlements_xml(apple_codesign::SettingsScope::Main, String::from_utf8_lossy(&ent_xml)) + .ok(); + } + } + + UnifiedSigner::new(settings).sign_path_in_place(bundle.get_dir())?; + } + } + } + + if let Some(cert) = &self.certificate { + if let Some(key) = &cert.key { key.finish()?; } } Ok(()) } + + fn build_base_settings(&self, shallow_override: bool) -> Result, Error> { + let mut settings = SigningSettings::default(); + if let Some(cert) = &self.certificate { + cert.load_into_signing_settings(&mut settings)?; + settings.set_team_id_from_signing_certificate(); + } + settings.set_for_notarization(false); + settings.set_shallow(shallow_override || self.settings.sign_shallow); + Ok(settings) + } } diff --git a/crates/ldid2/src/signing/signer_settings.rs b/crates/ldid2/src/signing/signer_settings.rs index c76c6004..8d24f954 100644 --- a/crates/ldid2/src/signing/signer_settings.rs +++ b/crates/ldid2/src/signing/signer_settings.rs @@ -13,13 +13,10 @@ pub enum SignerMode { pub struct SignerSettings { pub sign_shallow: bool, pub sign_mode: SignerMode, + pub embed_mobileprovision: bool, pub custom_name: Option, pub custom_identifier: Option, - pub custom_version: Option, pub custom_build_version: Option, - pub support_file_sharing: Option, - pub support_older_versions: Option, - pub support_more_devices: Option, } impl Default for SignerSettings { @@ -27,13 +24,10 @@ impl Default for SignerSettings { Self { sign_shallow: false, sign_mode: SignerMode::Default, + embed_mobileprovision: true, custom_name: None, custom_identifier: None, - custom_version: None, custom_build_version: None, - support_file_sharing: None, - support_older_versions: None, - support_more_devices: None, } } } diff --git a/crates/types/src/bundle.rs b/crates/types/src/bundle.rs index 6ebe40fb..7d089b55 100644 --- a/crates/types/src/bundle.rs +++ b/crates/types/src/bundle.rs @@ -7,7 +7,7 @@ use crate::PlistInfoTrait; use errors::Error; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum BundleType { App, AppExtension, @@ -29,7 +29,7 @@ impl BundleType { #[derive(Debug, Clone)] pub struct Bundle { dir: PathBuf, - _type: BundleType, + pub _type: BundleType, info_plist_file: PathBuf, } @@ -77,6 +77,16 @@ impl Bundle { Ok(()) } + pub fn set_name(&self, new_name: &str) -> Result<(), Error> { + self.set_info_plist_key("CFBundleDisplayName", new_name) + } + + pub fn set_version(&self, new_version: &str) -> Result<(), Error> { + self.set_info_plist_key("CFBundleShortVersionString", new_version)?; + self.set_info_plist_key("CFBundleVersion", new_version)?; + Ok(()) + } + pub fn set_bundle_identifier(&self, new_identifier: &str) -> Result<(), Error> { self.set_info_plist_key("CFBundleIdentifier", new_identifier) } @@ -177,8 +187,24 @@ fn collect_embeded_bundles_from_dir(dir: &PathBuf) -> Result, Error> let path = entry.path(); if let Some(name) = path.file_name().and_then(|n| n.to_str()) { + if name.ends_with(".storyboardc") { + continue; + } + if is_bundle_dir(name) { + if name.ends_with(".storyboardc") { + continue; + } + if let Ok(bundle) = Bundle::new(&path) { + if bundle.info_plist_file.parent() + .and_then(|p| p.file_name()) + .and_then(|n| n.to_str()) + .map_or(false, |n| n.ends_with(".storyboardc")) + { + continue; + } + if let BundleType::App = bundle._type { bundles.push(bundle); } else { @@ -195,6 +221,13 @@ fn collect_embeded_bundles_from_dir(dir: &PathBuf) -> Result, Error> } if path.is_dir() { + if path.file_name() + .and_then(|n| n.to_str()) + .map_or(false, |n| n.ends_with(".storyboardc")) + { + continue; + } + if let Ok(mut sub_bundles) = collect_embeded_bundles_from_dir(&path) { bundles.append(&mut sub_bundles); } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 4a2fd551..e66bd4ef 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -3,6 +3,7 @@ mod device; mod package; pub use bundle::Bundle; +pub use bundle::BundleType; pub use device::Device; pub use package::Package; diff --git a/package/macos/PlumeImpactor.app/Contents/Info.plist b/package/macos/PlumeImpactor.app/Contents/Info.plist index 2d9bf7c2..c8f2b36b 100644 --- a/package/macos/PlumeImpactor.app/Contents/Info.plist +++ b/package/macos/PlumeImpactor.app/Contents/Info.plist @@ -1,52 +1,46 @@ - - BuildMachineOSBuild - 23A344011 - CFBundleDevelopmentRegion - en - CFBundleDisplayName - Plume Impactor - CFBundleExecutable - plumeimpactor - CFBundleIconFile - AppIcon - CFBundleIconName - AppIcon - CFBundleIdentifier - com.feather.impactor - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Plume Impactor - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.3 - CFBundleSupportedPlatforms - - MacOSX - - CFBundleVersion - 50.5.4 - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformBuild - - DTPlatformName - macosx - DTPlatformVersion - 15.6 - DTSDKBuild - 24F62 - DTSDKName - macosx15.5.internal - DTXcode - 1630 - DTXcodeBuild - 16E6052g - LSMinimumSystemVersion - 10.8 - + + BuildMachineOSBuild + 23A344011 + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Plume Impactor + CFBundleExecutable + plumeimpactor + CFBundleIconFile + AppIcon + CFBundleIconName + AppIcon + CFBundleIdentifier + com.feather.impactor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Plume Impactor + CFBundlePackageType + APPL + CFBundleSupportedPlatforms + + MacOSX + + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformName + macosx + DTPlatformVersion + 15.6 + DTSDKBuild + 24F62 + DTSDKName + macosx15.5.internal + DTXcode + 1630 + DTXcodeBuild + 16E6052g + LSMinimumSystemVersion + 10.8 + From 8a2a83166bdc16776e7df0f96ac5d37ef5899324 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 11 Nov 2025 06:34:08 -0800 Subject: [PATCH 14/72] Update mod.rs --- crates/grand_slam/src/certificate/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/grand_slam/src/certificate/mod.rs b/crates/grand_slam/src/certificate/mod.rs index db53a0c9..e69de29b 100644 --- a/crates/grand_slam/src/certificate/mod.rs +++ b/crates/grand_slam/src/certificate/mod.rs @@ -1 +0,0 @@ -pub mod identity; From 591f10ea29c0e61c42c5b4ac483b1d81fe7b6e85 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 13 Nov 2025 01:45:23 -0800 Subject: [PATCH 15/72] refactor --- Cargo.lock | 54 +------ Cargo.toml | 7 +- apps/plumeimpactor/Cargo.toml | 7 +- apps/plumeimpactor/src/frame.rs | 2 +- apps/plumeimpactor/src/handlers.rs | 3 +- apps/plumeimpactor/src/main.rs | 21 ++- .../plumeimpactor/src/utils}/device.rs | 2 +- apps/plumeimpactor/src/utils/mod.rs | 5 + .../plumeimpactor/src/utils}/package.rs | 10 +- apps/plumesign/Cargo.toml | 2 - apps/plumesign/src/main.rs | 44 ++---- crates/errors/Cargo.toml | 19 --- crates/errors/src/lib.rs | 55 ------- crates/grand_slam/Cargo.toml | 5 +- crates/grand_slam/src/auth/account/login.rs | 2 +- crates/grand_slam/src/auth/account/mod.rs | 2 +- crates/grand_slam/src/auth/account/request.rs | 2 +- crates/grand_slam/src/auth/account/token.rs | 2 +- .../src/auth/account/two_factor_auth.rs | 2 +- crates/grand_slam/src/auth/anisette_data.rs | 2 +- crates/grand_slam/src/auth/mod.rs | 2 +- crates/grand_slam/src/developer/mod.rs | 2 +- crates/grand_slam/src/developer/qh/account.rs | 2 +- .../grand_slam/src/developer/qh/app_groups.rs | 2 +- crates/grand_slam/src/developer/qh/app_ids.rs | 2 +- crates/grand_slam/src/developer/qh/certs.rs | 2 +- crates/grand_slam/src/developer/qh/devices.rs | 2 +- crates/grand_slam/src/developer/qh/teams.rs | 2 +- crates/grand_slam/src/developer/v1/app_ids.rs | 2 +- .../src/developer/v1/capabilities.rs | 2 +- crates/grand_slam/src/lib.rs | 58 +++++++- .../src => grand_slam/src/utils}/bundle.rs | 75 +++++----- .../src/utils}/certificate.rs | 27 +--- .../src => grand_slam/src/utils}/macho.rs | 10 +- crates/grand_slam/src/utils/mod.rs | 48 ++++++ crates/grand_slam/src/utils/provision.rs | 138 ++++++++++++++++++ crates/grand_slam/src/utils/signer.rs | 114 +++++++++++++++ crates/ldid2/Cargo.toml | 15 -- crates/ldid2/src/lib.rs | 4 - crates/ldid2/src/provision.rs | 93 ------------ crates/ldid2/src/signing/mod.rs | 2 - crates/ldid2/src/signing/signer.rs | 134 ----------------- crates/ldid2/src/signing/signer_settings.rs | 33 ----- crates/types/Cargo.toml | 16 -- crates/types/src/lib.rs | 16 -- 45 files changed, 486 insertions(+), 565 deletions(-) rename {crates/types/src => apps/plumeimpactor/src/utils}/device.rs (98%) create mode 100644 apps/plumeimpactor/src/utils/mod.rs rename {crates/types/src => apps/plumeimpactor/src/utils}/package.rs (94%) delete mode 100644 crates/errors/Cargo.toml delete mode 100644 crates/errors/src/lib.rs rename crates/{types/src => grand_slam/src/utils}/bundle.rs (83%) rename crates/{ldid2/src => grand_slam/src/utils}/certificate.rs (62%) rename crates/{ldid2/src => grand_slam/src/utils}/macho.rs (69%) create mode 100644 crates/grand_slam/src/utils/mod.rs create mode 100644 crates/grand_slam/src/utils/provision.rs create mode 100644 crates/grand_slam/src/utils/signer.rs delete mode 100644 crates/ldid2/Cargo.toml delete mode 100644 crates/ldid2/src/lib.rs delete mode 100644 crates/ldid2/src/provision.rs delete mode 100644 crates/ldid2/src/signing/mod.rs delete mode 100644 crates/ldid2/src/signing/signer.rs delete mode 100644 crates/ldid2/src/signing/signer_settings.rs delete mode 100644 crates/types/Cargo.toml delete mode 100644 crates/types/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e213cdd0..1c0d8ce1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1217,22 +1217,6 @@ dependencies = [ "windows-sys 0.61.0", ] -[[package]] -name = "errors" -version = "0.0.1" -dependencies = [ - "apple-codesign", - "idevice", - "omnisette", - "pem", - "plist", - "reqwest 0.11.27", - "serde_json", - "thiserror 2.0.16", - "x509-certificate", - "zip 4.6.1", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -1514,13 +1498,14 @@ name = "grand_slam" version = "0.0.1" dependencies = [ "aes", + "apple-codesign", "base64 0.22.1", "botan", "cbc", - "errors", "hmac", "omnisette", "pbkdf2", + "pem", "plist", "rand 0.9.2", "reqwest 0.11.27", @@ -1529,8 +1514,10 @@ dependencies = [ "serde_json", "sha2", "srp", + "thiserror 2.0.16", "tokio", "uuid", + "x509-certificate", ] [[package]] @@ -2165,18 +2152,6 @@ dependencies = [ "spin", ] -[[package]] -name = "ldid2" -version = "0.0.1" -dependencies = [ - "apple-codesign", - "errors", - "pem", - "plist", - "types", - "x509-certificate", -] - [[package]] name = "libc" version = "0.2.177" @@ -2823,11 +2798,13 @@ dependencies = [ "grand_slam", "idevice", "keyring", - "ldid2", + "plist", "rustls 0.23.32", + "thiserror 2.0.16", "tokio", - "types", + "uuid", "wxdragon", + "zip 4.6.1", ] [[package]] @@ -2836,11 +2813,9 @@ version = "0.0.1" dependencies = [ "clap", "grand_slam", - "ldid2", "plist", "rustls 0.23.32", "tokio", - "types", ] [[package]] @@ -4343,19 +4318,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "types" -version = "0.0.1" -dependencies = [ - "errors", - "idevice", - "plist", - "thiserror 2.0.16", - "tokio", - "uuid", - "zip 4.6.1", -] - [[package]] name = "typewit" version = "1.14.2" diff --git a/Cargo.toml b/Cargo.toml index 7198bbd3..8e6944bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,8 @@ resolver = "3" members = [ "apps/plumeimpactor", - "apps/plumesign", - "crates/errors", - "crates/ldid2", - "crates/grand_slam", - "crates/types" + "apps/plumesign", + "crates/grand_slam" ] [workspace.package] diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index 598c787a..04cacc1f 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -9,10 +9,13 @@ repository.workspace = true [dependencies] idevice.workspace = true +zip.workspace = true tokio.workspace = true -ldid2 = { path = "../../crates/ldid2" } +thiserror.workspace = true +uuid.workspace = true +plist.workspace = true grand_slam = { path = "../../crates/grand_slam", features = ["vendored-botan"] } -types = { path = "../../crates/types"} + wxdragon = "0.9.2" futures = "0.3.31" rustls = { version = "0.23.32", features = ["ring"] } diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index e0b5d2c1..348f3b94 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -19,7 +19,7 @@ use crate::pages::login::LoginDialog; use crate::pages::{ DefaultPage, InstallPage, create_default_page, create_install_page, create_login_dialog, }; -use types::{Device, Package}; +use crate::utils::{Device, Package}; pub struct PlumeFrame { pub frame: Frame, diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index f6d2f34c..6435ac3a 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -5,7 +5,8 @@ use tokio::sync::mpsc::error::TryRecvError; use std::sync::mpsc as std_mpsc; use grand_slam::auth::Account; -use types::{Device, Package, PlistInfoTrait}; +use crate::utils::{Device, Package}; +use grand_slam::utils::PlistInfoTrait; use crate::frame::PlumeFrame; diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index ff3f06b0..bf947327 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -4,15 +4,34 @@ mod frame; mod keychain; mod pages; mod handlers; +mod utils; pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env!("CARGO_PKG_VERSION")); #[tokio::main] async fn main() { - // its very picky _ = rustls::crypto::ring::default_provider().install_default().unwrap(); let _ = wxdragon::main(|_| { frame::PlumeFrame::new().show(); }); } + +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum Error { + #[error("Info.plist not found")] + PackageInfoPlistMissing, + + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), + #[error("Plist error: {0}")] + Plist(#[from] plist::Error), + #[error("Zip error: {0}")] + Zip(#[from] zip::result::ZipError), + #[error("Idevice error: {0}")] + Idevice(#[from] idevice::IdeviceError), + #[error("GrandSlam error: {0}")] + GrandSlam(#[from] grand_slam::Error), +} diff --git a/crates/types/src/device.rs b/apps/plumeimpactor/src/utils/device.rs similarity index 98% rename from crates/types/src/device.rs rename to apps/plumeimpactor/src/utils/device.rs index 4dd9d320..8ddc4571 100644 --- a/crates/types/src/device.rs +++ b/apps/plumeimpactor/src/utils/device.rs @@ -4,7 +4,7 @@ use idevice::usbmuxd::{Connection, UsbmuxdAddr, UsbmuxdDevice}; use idevice::lockdown::LockdownClient; use idevice::IdeviceService; -use errors::Error; +use crate::Error; pub const CONNECTION_LABEL: &str = "plume"; diff --git a/apps/plumeimpactor/src/utils/mod.rs b/apps/plumeimpactor/src/utils/mod.rs new file mode 100644 index 00000000..7fb115ff --- /dev/null +++ b/apps/plumeimpactor/src/utils/mod.rs @@ -0,0 +1,5 @@ +mod device; +mod package; + +pub use device::Device; +pub use package::Package; diff --git a/crates/types/src/package.rs b/apps/plumeimpactor/src/utils/package.rs similarity index 94% rename from crates/types/src/package.rs rename to apps/plumeimpactor/src/utils/package.rs index 3c0ec6b5..af4cbb49 100644 --- a/crates/types/src/package.rs +++ b/apps/plumeimpactor/src/utils/package.rs @@ -7,9 +7,9 @@ use plist::Dictionary; use uuid::Uuid; use zip::ZipArchive; -use crate::bundle::Bundle; -use crate::PlistInfoTrait; -use errors::Error; +use grand_slam::Bundle; +use grand_slam::utils::PlistInfoTrait; +use crate::Error; #[derive(Debug, Clone)] pub struct Package { @@ -45,7 +45,7 @@ impl Package { .filter_map(Result::ok) .map(|e| e.path()) .find(|p| p.is_dir() && p.extension().and_then(|e| e.to_str()) == Some("app")) - .ok_or_else(|| Error::BundleInfoPlistMissing)?; + .ok_or_else(|| Error::PackageInfoPlistMissing)?; Ok(Bundle::new(app_dir)?) } @@ -58,7 +58,7 @@ impl Package { .find(|name| name.starts_with("Payload/") && name.ends_with(".app/Info.plist") && name.matches('/').count() == 2) - .ok_or(Error::BundleInfoPlistMissing)? + .ok_or(Error::PackageInfoPlistMissing)? .to_string() }; let mut entry = archive.by_name(&info_name)?; diff --git a/apps/plumesign/Cargo.toml b/apps/plumesign/Cargo.toml index fb10a033..1736b636 100644 --- a/apps/plumesign/Cargo.toml +++ b/apps/plumesign/Cargo.toml @@ -9,9 +9,7 @@ repository.workspace = true [dependencies] plist.workspace = true tokio.workspace = true -ldid2 = { path = "../../crates/ldid2" } grand_slam = { path = "../../crates/grand_slam", features = ["vendored-botan"] } -types = { path = "../../crates/types"} rustls = { version = "0.23.32", features = ["ring"] } clap = { version = "4.5", features = ["derive"] } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index 9d8ac3b6..4148103e 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -4,10 +4,10 @@ use std::process::exit; use clap::Parser; use clap::{Args, Subcommand}; -use ldid2::certificate::Certificate; -use ldid2::provision; -use ldid2::signing::signer::Signer; -use ldid2::signing::signer_settings::{SignerSettings, SignerMode}; +use grand_slam::utils::Certificate; +use grand_slam::utils::MobileProvision; +use grand_slam::utils::Signer; +use grand_slam::utils::SignerSettings; #[derive(Debug, Parser)] #[command(author, version, about, disable_help_subcommand = true)] @@ -23,28 +23,22 @@ pub enum Commands { #[derive(Debug, Args)] pub struct SignArgs { - #[arg(long = "pem", value_name = "PEM", num_args = 1.., required = true)] + #[arg(long = "pem", value_name = "PEM", num_args = 1.., required = true, help = "PEM files for certificate and private key")] pub pem_files: Vec, - #[arg(long = "provision", value_name = "PROVISION", num_args = 1.., required = true)] + #[arg(long = "provision", value_name = "PROVISION", num_args = 1.., required = true, help = "Provisioning profile files to embed")] pub provisioning_files: Vec, - #[arg(value_name = "PACKAGE", long = "package", required = false)] - pub package: Option, + #[arg(value_name = "BUNDLE", long = "bundle", required = true, help = "Path to the app bundle to sign")] + pub bundle: PathBuf, - #[arg(value_name = "BUNDLE", long = "bundle", required = false)] - pub bundle: Option, - - #[arg(short = 'w', long = "shallow", default_value_t = false)] - pub shallow: bool, - - #[arg(long = "bundle-id", value_name = "BUNDLE_ID")] + #[arg(long = "custom-identifier", value_name = "BUNDLE_ID", help = "Custom bundle identifier to set")] pub bundle_identifier: Option, - #[arg(long = "name", value_name = "NAME")] + #[arg(long = "custom-name", value_name = "NAME", help = "Custom bundle name to set")] pub name: Option, - #[arg(long = "version", value_name = "VERSION")] + #[arg(long = "custom-version", value_name = "VERSION", help = "Custom bundle version to set")] pub version: Option, } @@ -63,19 +57,13 @@ async fn main() { exit(1); } - let target = args.bundle.as_ref().or(args.package.as_ref()).cloned(); - if target.is_none() { - eprintln!("Error: Either --bundle or --package must be specified."); - exit(1); - } - let signing_key = Certificate::new(args.pem_files.clone().into()).unwrap_or_else(|e| { eprintln!("Failed to create Certificate: {e}"); exit(1); }); let provisioning_files = args.provisioning_files.iter() - .map(provision::MobileProvision::new) + .map(MobileProvision::load) .collect::, _>>() .unwrap_or_else(|e| { eprintln!("Failed to load provisioning profiles: {e}"); @@ -83,12 +71,6 @@ async fn main() { }); let signer_settings = SignerSettings { - sign_shallow: args.shallow, - sign_mode: if provisioning_files.len() == 1 { - SignerMode::Zsign - } else { - SignerMode::Default - }, custom_name: args.name.clone(), custom_identifier: args.bundle_identifier.clone(), custom_build_version: args.version.clone(), @@ -97,7 +79,7 @@ async fn main() { let signer = Signer::new(Some(signing_key), signer_settings, provisioning_files); - let target_path = target.unwrap(); + let target_path = args.bundle.clone(); if let Err(e) = signer.sign(target_path.clone()) { eprintln!("Failed to sign target: {e}"); exit(1); diff --git a/crates/errors/Cargo.toml b/crates/errors/Cargo.toml deleted file mode 100644 index b60e9e80..00000000 --- a/crates/errors/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "errors" -edition.workspace = true -version.workspace = true -authors.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -idevice.workspace = true -plist.workspace = true -thiserror.workspace = true -zip.workspace = true -pem.workspace = true -x509-certificate.workspace = true -apple-codesign.workspace = true -reqwest.workspace = true -omnisette.workspace = true -serde_json.workspace = true diff --git a/crates/errors/src/lib.rs b/crates/errors/src/lib.rs deleted file mode 100644 index 4e2f2a04..00000000 --- a/crates/errors/src/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -use thiserror::Error as ThisError; - -#[derive(Debug, ThisError)] -pub enum Error { - #[error("Info.plist not found")] - BundleInfoPlistMissing, - #[error("Unknown bundle type")] - BundleTypeUnknown, - - #[error("Entitlements not found")] - ProvisioningEntitlementsUnknown, - - #[error("Developer session error {0}: {1}")] - DeveloperSession(i64, String), - #[error("Request to developer session failed")] - DeveloperSessionRequestFailed, - - #[error("Authentication SRP error {0}: {1}")] - AuthSrpWithMessage(i64, String), - #[error("Authentication SRP error")] - AuthSrp, - #[error("Authentication extra step required: {0}")] - ExtraStep(String), - #[error("Bad 2FA code")] - Bad2faCode, - #[error("Failed to parse")] - Parse, - - #[error("I/O error: {0}")] - Io(#[from] std::io::Error), - #[error("Plist error: {0}")] - Plist(#[from] plist::Error), - #[error("Zip error: {0}")] - Zip(#[from] zip::result::ZipError), - #[error("Codesign error: {0}")] - Codesign(#[from] apple_codesign::AppleCodesignError), - #[error("Certificate PEM error: {0}")] - Pem(#[from] pem::PemError), - #[error("X509 certificate error: {0}")] - X509(#[from] x509_certificate::X509CertificateError), - #[error("Idevice error: {0}")] - Idevice(#[from] idevice::IdeviceError), - #[error("Reqwest error: {0}")] - Reqwest(#[from] reqwest::Error), - #[error("Anisette error: {0}")] - Anisette(#[from] omnisette::AnisetteError), - #[error("Serde JSON error: {0}")] - SerdeJson(#[from] serde_json::Error), - - #[error("Missing certificate PEM data")] - CertificatePemMissing, - - #[error("Device not found")] - DeviceNotFound, -} diff --git a/crates/grand_slam/Cargo.toml b/crates/grand_slam/Cargo.toml index 9197052b..0dd504e2 100644 --- a/crates/grand_slam/Cargo.toml +++ b/crates/grand_slam/Cargo.toml @@ -16,7 +16,10 @@ uuid.workspace = true reqwest.workspace = true omnisette.workspace = true serde_json.workspace = true -errors = { path = "../errors" } +pem.workspace = true +x509-certificate.workspace = true +apple-codesign.workspace = true +thiserror.workspace = true rustls = { version = "0.23.32", features = ["ring"] } serde = { version = "1", features = ["derive"] } diff --git a/crates/grand_slam/src/auth/account/login.rs b/crates/grand_slam/src/auth/account/login.rs index 3b7dbc77..15f9fd0c 100644 --- a/crates/grand_slam/src/auth/account/login.rs +++ b/crates/grand_slam/src/auth/account/login.rs @@ -5,7 +5,7 @@ use sha2::{Digest, Sha256}; use srp::client::{SrpClient, SrpClientVerifier}; use srp::groups::G_2048; -use errors::Error; +use crate::Error; use crate::auth::account::{check_error, parse_response}; use crate::auth::anisette_data::AnisetteData; diff --git a/crates/grand_slam/src/auth/account/mod.rs b/crates/grand_slam/src/auth/account/mod.rs index 44159443..2647be99 100644 --- a/crates/grand_slam/src/auth/account/mod.rs +++ b/crates/grand_slam/src/auth/account/mod.rs @@ -9,7 +9,7 @@ use reqwest::Response; use sha2::Sha256; use srp::client::SrpClientVerifier; -use errors::Error; +use crate::Error; pub async fn parse_response( res: Result, diff --git a/crates/grand_slam/src/auth/account/request.rs b/crates/grand_slam/src/auth/account/request.rs index 5d87d00c..f9934c0b 100644 --- a/crates/grand_slam/src/auth/account/request.rs +++ b/crates/grand_slam/src/auth/account/request.rs @@ -2,7 +2,7 @@ use plist::Dictionary; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use serde_json::Value; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, auth::Account}; diff --git a/crates/grand_slam/src/auth/account/token.rs b/crates/grand_slam/src/auth/account/token.rs index 7ef9b5dd..635bd1d0 100644 --- a/crates/grand_slam/src/auth/account/token.rs +++ b/crates/grand_slam/src/auth/account/token.rs @@ -2,7 +2,7 @@ use botan::Cipher; use hmac::{Hmac, Mac}; use reqwest::header::{HeaderMap, HeaderValue}; -use errors::Error; +use crate::Error; use sha2::Sha256; use crate::auth::{Account, AppToken, AuthTokenRequest, AuthTokenRequestBody, GSA_ENDPOINT, RequestHeader}; diff --git a/crates/grand_slam/src/auth/account/two_factor_auth.rs b/crates/grand_slam/src/auth/account/two_factor_auth.rs index 2b92d5b0..f4491e4a 100644 --- a/crates/grand_slam/src/auth/account/two_factor_auth.rs +++ b/crates/grand_slam/src/auth/account/two_factor_auth.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use base64::{Engine, engine::general_purpose}; -use errors::Error; +use crate::Error; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use crate::auth::{Account, AuthenticationExtras, LoginState, PhoneNumber, VerifyBody, VerifyCode}; diff --git a/crates/grand_slam/src/auth/anisette_data.rs b/crates/grand_slam/src/auth/anisette_data.rs index 44add79a..fb74a59e 100644 --- a/crates/grand_slam/src/auth/anisette_data.rs +++ b/crates/grand_slam/src/auth/anisette_data.rs @@ -3,7 +3,7 @@ use std::time::SystemTime; use omnisette::{AnisetteConfiguration, AnisetteHeaders}; -use errors::Error; +use crate::Error; #[derive(Debug, Clone)] pub struct AnisetteData { diff --git a/crates/grand_slam/src/auth/mod.rs b/crates/grand_slam/src/auth/mod.rs index 90c48f51..551f45bd 100644 --- a/crates/grand_slam/src/auth/mod.rs +++ b/crates/grand_slam/src/auth/mod.rs @@ -7,7 +7,7 @@ use reqwest::{Certificate, Client, ClientBuilder}; use tokio::sync::Mutex; use std::sync::Arc; -use errors::Error; +use crate::Error; use crate::auth::anisette_data::AnisetteData; diff --git a/crates/grand_slam/src/developer/mod.rs b/crates/grand_slam/src/developer/mod.rs index 998dbc09..9bb7cdc9 100644 --- a/crates/grand_slam/src/developer/mod.rs +++ b/crates/grand_slam/src/developer/mod.rs @@ -4,7 +4,7 @@ pub mod v1; use plist::{Dictionary, Value}; use uuid::Uuid; -use errors::Error; +use crate::Error; use crate::SessionRequestTrait; use crate::auth::{Account, account::request::RequestType}; diff --git a/crates/grand_slam/src/developer/qh/account.rs b/crates/grand_slam/src/developer/qh/account.rs index 137730da..bdd78d47 100644 --- a/crates/grand_slam/src/developer/qh/account.rs +++ b/crates/grand_slam/src/developer/qh/account.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use plist::{Dictionary, Value}; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, developer_endpoint}; use super::{DeveloperSession, ResponseMeta}; diff --git a/crates/grand_slam/src/developer/qh/app_groups.rs b/crates/grand_slam/src/developer/qh/app_groups.rs index 6b55a00f..139bafbd 100644 --- a/crates/grand_slam/src/developer/qh/app_groups.rs +++ b/crates/grand_slam/src/developer/qh/app_groups.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use plist::{Dictionary, Value}; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, developer_endpoint}; use super::{DeveloperSession, ResponseMeta}; diff --git a/crates/grand_slam/src/developer/qh/app_ids.rs b/crates/grand_slam/src/developer/qh/app_ids.rs index 1107bf93..c45fadcd 100644 --- a/crates/grand_slam/src/developer/qh/app_ids.rs +++ b/crates/grand_slam/src/developer/qh/app_ids.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use plist::{Dictionary, Integer, Value}; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, developer_endpoint}; use super::{DeveloperSession, ResponseMeta}; diff --git a/crates/grand_slam/src/developer/qh/certs.rs b/crates/grand_slam/src/developer/qh/certs.rs index 4c89cdd4..12fa2962 100644 --- a/crates/grand_slam/src/developer/qh/certs.rs +++ b/crates/grand_slam/src/developer/qh/certs.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use plist::{Data, Date, Dictionary, Integer, Value}; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, developer_endpoint}; use super::{DeveloperSession, ResponseMeta}; diff --git a/crates/grand_slam/src/developer/qh/devices.rs b/crates/grand_slam/src/developer/qh/devices.rs index 4dff1890..e0991584 100644 --- a/crates/grand_slam/src/developer/qh/devices.rs +++ b/crates/grand_slam/src/developer/qh/devices.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use plist::{Dictionary, Date, Value}; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, developer_endpoint}; use super::{DeveloperSession, ResponseMeta}; diff --git a/crates/grand_slam/src/developer/qh/teams.rs b/crates/grand_slam/src/developer/qh/teams.rs index d610dc52..568a7080 100644 --- a/crates/grand_slam/src/developer/qh/teams.rs +++ b/crates/grand_slam/src/developer/qh/teams.rs @@ -1,7 +1,7 @@ use serde::Deserialize; use plist::{Date, Integer, Value}; -use errors::Error; +use crate::Error; use crate::{SessionRequestTrait, developer_endpoint}; use super::{DeveloperSession, ResponseMeta}; diff --git a/crates/grand_slam/src/developer/v1/app_ids.rs b/crates/grand_slam/src/developer/v1/app_ids.rs index 818a8f4b..fdf0ff08 100644 --- a/crates/grand_slam/src/developer/v1/app_ids.rs +++ b/crates/grand_slam/src/developer/v1/app_ids.rs @@ -6,7 +6,7 @@ use crate::SessionRequestTrait; use crate::auth::account::request::RequestType; use crate::developer_endpoint; -use errors::Error; +use crate::Error; impl DeveloperSession { pub async fn v1_list_app_ids(&self, team: &str) -> Result { diff --git a/crates/grand_slam/src/developer/v1/capabilities.rs b/crates/grand_slam/src/developer/v1/capabilities.rs index 290e6789..4e10b363 100644 --- a/crates/grand_slam/src/developer/v1/capabilities.rs +++ b/crates/grand_slam/src/developer/v1/capabilities.rs @@ -6,7 +6,7 @@ use crate::SessionRequestTrait; use crate::auth::account::request::RequestType; use crate::developer_endpoint; -use errors::Error; +use crate::Error; impl DeveloperSession { pub async fn v1_list_capabilities(&self, team: &str) -> Result { diff --git a/crates/grand_slam/src/lib.rs b/crates/grand_slam/src/lib.rs index a7dfe95c..7ca15089 100644 --- a/crates/grand_slam/src/lib.rs +++ b/crates/grand_slam/src/lib.rs @@ -1,17 +1,71 @@ pub mod auth; pub mod developer; pub mod certificate; +pub mod utils; use plist::Dictionary; use serde_json::Value; -use errors::Error; - use crate::auth::account::request::RequestType; pub use omnisette::AnisetteConfiguration; +pub use utils::MachO; +pub use utils::MobileProvision; +pub use utils::Certificate; +pub use utils::Signer; +pub use utils::Bundle; +pub use utils::BundleType; trait SessionRequestTrait { async fn qh_send_request(&self, endpoint: &str, payload: Option) -> Result; async fn v1_send_request(&self, url: &str, body: Option, request_type: Option) -> Result; } + +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum Error { + #[error("Info.plist not found")] + BundleInfoPlistMissing, + #[error("Executable not found")] + BundleExecutableMissing, + + #[error("Entitlements not found")] + ProvisioningEntitlementsUnknown, + + #[error("Missing certificate PEM data")] + CertificatePemMissing, + + #[error("Developer session error {0}: {1}")] + DeveloperSession(i64, String), + #[error("Request to developer session failed")] + DeveloperSessionRequestFailed, + + #[error("Authentication SRP error {0}: {1}")] + AuthSrpWithMessage(i64, String), + #[error("Authentication SRP error")] + AuthSrp, + #[error("Authentication extra step required: {0}")] + ExtraStep(String), + #[error("Bad 2FA code")] + Bad2faCode, + #[error("Failed to parse")] + Parse, + + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), + #[error("Plist error: {0}")] + Plist(#[from] plist::Error), + #[error("Codesign error: {0}")] + Codesign(#[from] apple_codesign::AppleCodesignError), + #[error("Certificate PEM error: {0}")] + Pem(#[from] pem::PemError), + #[error("X509 certificate error: {0}")] + X509(#[from] x509_certificate::X509CertificateError), + #[error("Reqwest error: {0}")] + Reqwest(#[from] reqwest::Error), + #[error("Anisette error: {0}")] + Anisette(#[from] omnisette::AnisetteError), + #[error("Serde JSON error: {0}")] + SerdeJson(#[from] serde_json::Error), +} diff --git a/crates/types/src/bundle.rs b/crates/grand_slam/src/utils/bundle.rs similarity index 83% rename from crates/types/src/bundle.rs rename to crates/grand_slam/src/utils/bundle.rs index 7d089b55..44fee577 100644 --- a/crates/types/src/bundle.rs +++ b/crates/grand_slam/src/utils/bundle.rs @@ -3,9 +3,9 @@ use std::path::PathBuf; use plist::Value; -use crate::PlistInfoTrait; +use super::PlistInfoTrait; -use errors::Error; +use crate::Error; #[derive(Debug, Clone, PartialEq)] pub enum BundleType { @@ -55,14 +55,23 @@ impl Bundle { }) } - pub fn get_dir(&self) -> &PathBuf { + pub fn dir(&self) -> &PathBuf { &self.dir } - pub fn get_embedded_bundles(&self) -> Result, Error> { + pub fn collect_nested_bundles(&self) -> Result, Error> { collect_embeded_bundles_from_dir(&self.dir) } + pub fn collect_bundles_sorted(&self) -> Result, Error> { + let mut bundles = self.collect_nested_bundles()?; + bundles.push(self.clone()); + bundles.sort_by_key(|b| b.dir().components().count()); + bundles.reverse(); + + Ok(bundles) + } + pub fn set_info_plist_key>( &self, key: &str, @@ -77,14 +86,15 @@ impl Bundle { Ok(()) } + // TODO: we need to support changing lproj infoplist strings so localized names change as well pub fn set_name(&self, new_name: &str) -> Result<(), Error> { - self.set_info_plist_key("CFBundleDisplayName", new_name) + self.set_info_plist_key("CFBundleDisplayName", new_name)?; + self.set_info_plist_key("CFBundleName", new_name) } pub fn set_version(&self, new_version: &str) -> Result<(), Error> { self.set_info_plist_key("CFBundleShortVersionString", new_version)?; - self.set_info_plist_key("CFBundleVersion", new_version)?; - Ok(()) + self.set_info_plist_key("CFBundleVersion", new_version) } pub fn set_bundle_identifier(&self, new_identifier: &str) -> Result<(), Error> { @@ -171,31 +181,30 @@ impl PlistInfoTrait for Bundle { } } -fn is_bundle_dir(name: &str) -> bool { - if let Some((_, ext)) = name.rsplit_once('.') { - BundleType::from_extension(ext).is_some() - } else { - false - } -} - fn collect_embeded_bundles_from_dir(dir: &PathBuf) -> Result, Error> { let mut bundles = Vec::new(); + + fn is_bundle_dir(name: &str) -> bool { + if let Some((_, ext)) = name.rsplit_once('.') { + BundleType::from_extension(ext).is_some() + } else { + false + } + } for entry in fs::read_dir(dir)? { - let entry = entry.map_err(|e| Error::Io(e))?; + let entry = entry.map_err(Error::Io)?; let path = entry.path(); - if let Some(name) = path.file_name().and_then(|n| n.to_str()) { - if name.ends_with(".storyboardc") { - continue; - } + if path.file_name() + .and_then(|n| n.to_str()) + .map_or(false, |n| n.ends_with(".storyboardc")) + { + continue; + } + if let Some(name) = path.file_name().and_then(|n| n.to_str()) { if is_bundle_dir(name) { - if name.ends_with(".storyboardc") { - continue; - } - if let Ok(bundle) = Bundle::new(&path) { if bundle.info_plist_file.parent() .and_then(|p| p.file_name()) @@ -205,14 +214,11 @@ fn collect_embeded_bundles_from_dir(dir: &PathBuf) -> Result, Error> continue; } - if let BundleType::App = bundle._type { - bundles.push(bundle); - } else { - if let Ok(embedded) = bundle.get_embedded_bundles() { - bundles.push(bundle); + bundles.push(bundle.clone()); + + if bundle._type != BundleType::App { + if let Ok(embedded) = bundle.collect_nested_bundles() { bundles.extend(embedded); - } else { - bundles.push(bundle); } } continue; @@ -221,13 +227,6 @@ fn collect_embeded_bundles_from_dir(dir: &PathBuf) -> Result, Error> } if path.is_dir() { - if path.file_name() - .and_then(|n| n.to_str()) - .map_or(false, |n| n.ends_with(".storyboardc")) - { - continue; - } - if let Ok(mut sub_bundles) = collect_embeded_bundles_from_dir(&path) { bundles.append(&mut sub_bundles); } diff --git a/crates/ldid2/src/certificate.rs b/crates/grand_slam/src/utils/certificate.rs similarity index 62% rename from crates/ldid2/src/certificate.rs rename to crates/grand_slam/src/utils/certificate.rs index 46e5fce6..106ee460 100644 --- a/crates/ldid2/src/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -3,10 +3,10 @@ use std::path::PathBuf; use apple_codesign::{cryptography::{InMemoryPrivateKey, PrivateKey}, SigningSettings}; use x509_certificate::CapturedX509Certificate; -use errors::Error; +use crate::Error; pub struct Certificate { - pub cert: Option, + cert: Option, pub key: Option>, } @@ -26,22 +26,18 @@ impl Certificate { Ok(cert) } - pub fn resolve_certificate_from_path(&mut self, path: &PathBuf) -> Result<(), Error> { - println!("reading PEM data from {}", path.display()); + fn resolve_certificate_from_path(&mut self, path: &PathBuf) -> Result<(), Error> { let pem_data = std::fs::read(path)?; for pem in pem::parse_many(pem_data).map_err(Error::Pem)? { match pem.tag() { "CERTIFICATE" => { - println!("adding certificate from {}", path.display()); self.cert = Some(CapturedX509Certificate::from_der(pem.contents())?); } "PRIVATE KEY" => { - println!("adding private key from {}", path.display()); self.key = Some(Box::new(InMemoryPrivateKey::from_pkcs8_der(pem.contents())?)); } "RSA PRIVATE KEY" => { - println!("adding RSA private key from {}", path.display()); self.key = Some(Box::new(InMemoryPrivateKey::from_pkcs1_der(pem.contents())?)); } tag => println!("(unhandled PEM tag {}; ignoring)", tag), @@ -58,23 +54,8 @@ impl Certificate { let signing_cert = self.cert.clone().ok_or(Error::CertificatePemMissing)?; let signing_key = self.key.as_ref().ok_or(Error::CertificatePemMissing)?; - if !signing_cert.time_constraints_valid(None) { - println!( - "Warning: signing certificate expired as of {}; signatures may not be valid", - signing_cert.validity_not_after().to_rfc3339() - ); - } - settings.set_signing_key(signing_key.as_key_info_signer(), signing_cert); - - if let Some(certs) = settings.chain_apple_certificates() { - for cert in certs { - println!( - "Automatically registered Apple CA certificate: {}", - cert.subject_common_name().unwrap_or_else(|| "default".into()) - ); - } - } + settings.chain_apple_certificates(); Ok(()) } diff --git a/crates/ldid2/src/macho.rs b/crates/grand_slam/src/utils/macho.rs similarity index 69% rename from crates/ldid2/src/macho.rs rename to crates/grand_slam/src/utils/macho.rs index 165a7bdc..9933b644 100644 --- a/crates/ldid2/src/macho.rs +++ b/crates/grand_slam/src/utils/macho.rs @@ -2,8 +2,9 @@ use std::fs; use std::path::PathBuf; use apple_codesign::MachFile; +use plist::{Dictionary, Value}; -use errors::Error; +use crate::Error; pub struct MachO<'a> { macho_file: MachFile<'a>, @@ -21,11 +22,14 @@ impl<'a> MachO<'a> { }) } - pub fn get_entitlements(&self) -> Result, Error> { + pub fn entitlements(&self) -> Result, Error> { let macho = self.macho_file.nth_macho(0)?; if let Some(embedded_sig) = macho.code_signature()? { if let Ok(Some(slot)) = embedded_sig.entitlements() { - return Ok(Some(slot.to_string())); + let value = Value::from_reader_xml(slot.to_string().as_bytes())?; + if let Value::Dictionary(dict) = value { + return Ok(Some(dict)); + } } } Ok(None) diff --git a/crates/grand_slam/src/utils/mod.rs b/crates/grand_slam/src/utils/mod.rs new file mode 100644 index 00000000..ee01c7ca --- /dev/null +++ b/crates/grand_slam/src/utils/mod.rs @@ -0,0 +1,48 @@ +mod certificate; +mod provision; +mod macho; +mod signer; +mod bundle; + +pub use macho::MachO; +pub use provision::MobileProvision; +pub use certificate::Certificate; +pub use signer::Signer; +pub use bundle::Bundle; +pub use bundle::BundleType; + +pub struct SignerSettings { + pub should_embed_provisioning: bool, + pub custom_name: Option, + pub custom_identifier: Option, + pub custom_build_version: Option, + pub support_minimum_os_version: Option, + pub support_file_sharing: Option, + pub support_pro_motion: Option, + pub support_ipad_fullscreen: Option, + pub remove_url_schemes: Option, +} + +impl Default for SignerSettings { + fn default() -> Self { + Self { + should_embed_provisioning: true, + custom_name: None, + custom_identifier: None, + custom_build_version: None, + support_minimum_os_version: None, + support_file_sharing: None, + support_pro_motion: None, + support_ipad_fullscreen: None, + remove_url_schemes: None, + } + } +} + +pub trait PlistInfoTrait { + fn get_name(&self) -> Option; + fn get_executable(&self) -> Option; + fn get_bundle_identifier(&self) -> Option; + fn get_version(&self) -> Option; + fn get_build_version(&self) -> Option; +} diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs new file mode 100644 index 00000000..efee6cdf --- /dev/null +++ b/crates/grand_slam/src/utils/provision.rs @@ -0,0 +1,138 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use plist::{Dictionary, Value}; +use crate::Error; + +use super::MachO; + +#[derive(Clone)] +pub struct MobileProvision { + provision_file: PathBuf, + provisioning_plist: Value, + entitlements: Dictionary, +} + +impl MobileProvision { + pub fn load>(provision_path: P) -> Result { + let path = provision_path.as_ref(); + + if !path.exists() { + return Err(Error::ProvisioningEntitlementsUnknown); + } + + let provisioning_plist = Self::extract_plist_from_file(path)?; + let entitlements = Self::extract_entitlements(&provisioning_plist)?; + + Ok(Self { + provision_file: path.to_path_buf(), + provisioning_plist, + entitlements, + }) + } + + pub fn file_path(&self) -> &Path { + &self.provision_file + } + + pub fn entitlements(&self) -> &Dictionary { + &self.entitlements + } + + pub fn replace_wildcard_in_entitlements(&mut self, new_application_id: &str) { + for value in self.entitlements.values_mut() { + match value { + Value::String(s) => { + if s.contains('*') { + *s = s.replace('*', new_application_id); + } + } + Value::Array(arr) => { + for item in arr.iter_mut() { + if let Value::String(s) = item { + if s.contains('*') { + *s = s.replace('*', new_application_id); + } + } + } + } + _ => {} + } + } + } + + pub fn merge_entitlements(&mut self, binary_path: PathBuf) -> Result<(), Error> { + let macho = MachO::new(&binary_path)?; + let binary_entitlements = macho.entitlements()?.ok_or(Error::ProvisioningEntitlementsUnknown)?; + + if let Some(Value::Array(other_groups)) = binary_entitlements.get("keychain-access-groups") { + self.entitlements.insert("keychain-access-groups".to_string(), Value::Array(other_groups.clone())); + } + + let new_team_id = self.entitlements + .get("com.apple.developer.team-identifier") + .and_then(Value::as_string) + .map(|s| s.to_owned()); + let old_team_id = binary_entitlements + .get("com.apple.developer.team-identifier") + .and_then(Value::as_string) + .map(|s| s.to_owned()); + + if let (Some(new_id), Some(old_id)) = (new_team_id.as_ref(), old_team_id.as_ref()) { + if let Some(Value::Array(groups)) = self.entitlements.get_mut("keychain-access-groups") { + for group in groups.iter_mut() { + if let Value::String(s) = group { + if s.contains(old_id) { + *s = s.replace(old_id, new_id); + } + } + } + } + } + + Ok(()) + } + + pub fn entitlements_as_bytes(&self) -> Result, Error> { + let mut buf = Vec::new(); + Value::Dictionary(self.entitlements.clone()).to_writer_xml(&mut buf)?; + Ok(buf) + } + + pub fn bundle_id(&self) -> Option { + let app_id = self.entitlements.get("application-identifier")?.as_string()?; + + let prefix = self.provisioning_plist + .as_dictionary()? + .get("ApplicationIdentifierPrefix")? + .as_array()? + .get(0)? + .as_string(); + + if let Some(prefix) = prefix { + app_id.strip_prefix(prefix) + .map(|rest| rest.trim_start_matches('.').to_string()) + .or_else(|| Some(app_id.to_string())) + } else { + Some(app_id.to_string()) + } + } + + fn extract_plist_from_file(path: &Path) -> Result { + let data = fs::read(path)?; + let start = data.windows(6).position(|w| w == b"").ok_or(Error::ProvisioningEntitlementsUnknown)? + 8; + let plist_data = &data[start..end]; + let plist = plist::Value::from_reader_xml(plist_data)?; + Ok(plist) + } + + fn extract_entitlements(plist: &Value) -> Result { + plist + .as_dictionary() + .and_then(|d| d.get("Entitlements")) + .and_then(|v| v.as_dictionary()) + .cloned() + .ok_or(Error::ProvisioningEntitlementsUnknown) + } +} diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs new file mode 100644 index 00000000..03069a71 --- /dev/null +++ b/crates/grand_slam/src/utils/signer.rs @@ -0,0 +1,114 @@ +use std::fs; +use std::path::PathBuf; + +use apple_codesign::{SigningSettings, UnifiedSigner}; + +use crate::Error; + +use super::{Certificate, MobileProvision}; +use super::SignerSettings; +use super::{Bundle, BundleType, PlistInfoTrait}; + +pub struct Signer { + certificate: Option, + settings: SignerSettings, + provisioning_files: Vec, +} + +impl Signer { + pub fn new( + certificate: Option, + settings: SignerSettings, + provisioning_files: Vec, + ) -> Self { + Self { + certificate, + settings, + provisioning_files, + } + } + + pub fn sign(&self, path: PathBuf) -> Result<(), Error> { + let bundle = Bundle::new(path.clone())?; + let bundles = bundle.collect_bundles_sorted()?; + + if let Some(new_name) = self.settings.custom_name.as_ref() { + bundle.set_name(new_name)?; + } + + if let Some(new_version) = self.settings.custom_build_version.as_ref() { + bundle.set_version(new_version)?; + } + + if let Some(new_identifier) = self.settings.custom_identifier.as_ref() { + if let Some(old_identifier) = bundle.get_bundle_identifier() { + for embedded_bundle in &bundles { + embedded_bundle.set_matching_identifier( + &old_identifier, + &new_identifier, + )?; + } + } + } + + for bundle in &bundles { + let mut settings = self.build_base_settings()?; + + if bundle._type == BundleType::AppExtension || bundle._type == BundleType::App { + let mut matched_prov = None; + + for prov in &self.provisioning_files { + if let (Some(bundle_id), Some(team_id)) = (bundle.get_bundle_identifier(), prov.bundle_id()) { + if team_id == bundle_id { + matched_prov = Some(prov); + break; + } + } + } + + let mut prov = matched_prov.unwrap_or_else(|| &self.provisioning_files[0]).clone(); + + if let Some(bundle_id) = bundle.get_bundle_identifier() { + prov.replace_wildcard_in_entitlements(&bundle_id); + } + + if let Some(bundle_executable) = bundle.get_executable() { + let binary_path = bundle.dir().join(bundle_executable); + prov.merge_entitlements(binary_path)?; + } + + if self.settings.should_embed_provisioning { + fs::copy(prov.file_path(), bundle.dir().join("embedded.mobileprovision"))?; + } + + if let Ok(ent_xml) = prov.entitlements_as_bytes() { + settings.set_entitlements_xml( + apple_codesign::SettingsScope::Main, + String::from_utf8_lossy(&ent_xml) + )?; + } + } + + UnifiedSigner::new(settings).sign_path_in_place(bundle.dir())?; + } + + if let Some(cert) = &self.certificate { + if let Some(key) = &cert.key { + key.finish()?; + } + } + + Ok(()) + } + + fn build_base_settings(&self) -> Result, Error> { + let mut settings = SigningSettings::default(); + if let Some(cert) = &self.certificate { + cert.load_into_signing_settings(&mut settings)?; + settings.set_team_id_from_signing_certificate(); + } + settings.set_for_notarization(false); + settings.set_shallow(false); + Ok(settings) + } +} diff --git a/crates/ldid2/Cargo.toml b/crates/ldid2/Cargo.toml deleted file mode 100644 index 841f17df..00000000 --- a/crates/ldid2/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "ldid2" # sinners_road -edition.workspace = true -version.workspace = true -authors.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -pem.workspace = true -x509-certificate.workspace = true -apple-codesign.workspace = true -plist.workspace = true -errors = { path = "../errors" } -types = { path = "../types" } diff --git a/crates/ldid2/src/lib.rs b/crates/ldid2/src/lib.rs deleted file mode 100644 index 9a229df4..00000000 --- a/crates/ldid2/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod certificate; -pub mod signing; -pub mod macho; -pub mod provision; diff --git a/crates/ldid2/src/provision.rs b/crates/ldid2/src/provision.rs deleted file mode 100644 index b491998d..00000000 --- a/crates/ldid2/src/provision.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::fs; -use std::path::PathBuf; - -use plist::{Dictionary, Value}; - -use errors::Error; - -pub struct MobileProvision { - provision_file: PathBuf, - provisioning_plist: Value, -} - -impl MobileProvision { - pub fn new>(provision_path: P) -> Result { - let path = provision_path.into(); - - if !path.exists() { - return Err(Error::ProvisioningEntitlementsUnknown); - } - - let provisioning_plist = Self::extract_plist_from_provision_file(&path)?; - - Ok(Self { - provision_file: path.clone(), - provisioning_plist, - }) - } - - pub fn get_file_path(&self) -> &PathBuf { - &self.provision_file - } - - fn extract_plist_from_provision_file(provision_file: &PathBuf) -> Result { - let data = fs::read(provision_file)?; - let start = data.windows(6).position(|w| w == b"").ok_or(Error::ProvisioningEntitlementsUnknown)? + 8; - let plist_data = &data[start..end]; - let plist = plist::Value::from_reader_xml(plist_data)?; - Ok(plist) - } - - fn extract_entitlements_from_provision_file(&self) -> Result { - let plist = self.provisioning_plist.clone(); - let dict = plist - .as_dictionary() - .and_then(|d| d.get("Entitlements")) - .and_then(|v| v.as_dictionary()) - .cloned() - .ok_or(Error::ProvisioningEntitlementsUnknown)?; - Ok(Value::Dictionary(dict)) - } - - pub fn get_entitlements_as_bytes(&self) -> Result, Error> { - let mut buf = Vec::new(); - let provisioning_entitlements_dictionary = self.extract_entitlements_from_provision_file()?; - provisioning_entitlements_dictionary.to_writer_xml(&mut buf)?; - Ok(buf) - } - - pub fn get_entitlements_dictionary(&self) -> Result { - let provisioning_entitlements_dictionary = self.extract_entitlements_from_provision_file()?; - provisioning_entitlements_dictionary - .as_dictionary() - .cloned() - .ok_or(Error::ProvisioningEntitlementsUnknown) - } - -} -impl MobileProvision { - pub fn get_apple_team_id(&self) -> Option { - let dict = self.get_entitlements_dictionary().ok()?; - let team_id_opt = dict.get("application-identifier").and_then(|v| v.as_string()); - - let prefix_opt = self.provisioning_plist - .as_dictionary() - .and_then(|d| d.get("ApplicationIdentifierPrefix")) - .and_then(|v| v.as_array()) - .and_then(|arr| arr.get(0)) - .and_then(|v| v.as_string()); - - match (team_id_opt, prefix_opt) { - (Some(team_id), Some(prefix)) => { - let mut rest = &team_id[prefix.len()..]; - if rest.starts_with('.') { - rest = &rest[1..]; - } - Some(rest.to_string()) - } - (Some(team_id), None) => Some(team_id.to_string()), - _ => None, - } - } -} diff --git a/crates/ldid2/src/signing/mod.rs b/crates/ldid2/src/signing/mod.rs deleted file mode 100644 index 9fc1d765..00000000 --- a/crates/ldid2/src/signing/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod signer_settings; -pub mod signer; diff --git a/crates/ldid2/src/signing/signer.rs b/crates/ldid2/src/signing/signer.rs deleted file mode 100644 index 520d1a16..00000000 --- a/crates/ldid2/src/signing/signer.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::fs; -use std::path::PathBuf; - -use apple_codesign::{SigningSettings, UnifiedSigner}; - -use errors::Error; -use crate::{certificate::Certificate, provision::MobileProvision}; -use super::signer_settings::{SignerSettings, SignerMode}; -use types::{Bundle, PlistInfoTrait}; - -pub struct Signer { - certificate: Option, - settings: SignerSettings, - provisioning_files: Vec, -} - -impl Signer { - pub fn new( - certificate: Option, - settings: SignerSettings, - provisioning_files: Vec, - ) -> Self { - Self { - certificate, - settings, - provisioning_files, - } - } - - pub fn sign(&self, path: PathBuf) -> Result<(), Error> { - let bundle = Bundle::new(path.clone())?; - let bundles = bundle.get_embedded_bundles()?; - - if let Some(new_identifier) = self.settings.custom_identifier.as_ref() { - if let Some(old_identifier) = bundle.get_bundle_identifier() { - for embedded_bundle in &bundles { - embedded_bundle.set_matching_identifier( - &old_identifier, - &new_identifier, - )?; - } - - bundle.set_bundle_identifier(new_identifier)?; - } - } - - if let Some(new_name) = self.settings.custom_name.as_ref() { - bundle.set_name(new_name)?; - } - - if let Some(new_version) = self.settings.custom_build_version.as_ref() { - bundle.set_version(new_version)?; - } - - match self.settings.sign_mode { - SignerMode::Zsign => { - if let Some(prov) = self.provisioning_files.get(0) { - if self.settings.embed_mobileprovision { - fs::copy(prov.get_file_path(), bundle.get_dir().join("embedded.mobileprovision"))?; - } - - let mut settings = self.build_base_settings(false)?; - if let Ok(ent_xml) = prov.get_entitlements_as_bytes() { - settings - .set_entitlements_xml(apple_codesign::SettingsScope::Main, String::from_utf8_lossy(&ent_xml)) - .ok(); - } - - UnifiedSigner::new(settings).sign_path_in_place(bundle.get_dir())?; - } - } - SignerMode::Default => { - let mut sorted_bundles = bundles.clone(); - sorted_bundles.push(bundle.clone()); - sorted_bundles.sort_by_key(|b| b.get_dir().components().count()); - sorted_bundles.reverse(); - - for bundle in &sorted_bundles { - - let mut settings = self.build_base_settings(true)?; - - if bundle._type == types::BundleType::AppExtension || bundle._type == types::BundleType::App { - let mut matched_prov = None; - - println!("hii"); - println!("bundleid: {}", bundle.get_bundle_identifier().unwrap_or("no bundle id".to_string())); - - for prov in &self.provisioning_files { - println!("Checking provision: {:?}", prov.get_file_path()); - println!("teamid: {}", prov.get_apple_team_id().unwrap_or("no team id".to_string())); - if let (Some(bundle_id), Some(team_id)) = (bundle.get_bundle_identifier(), prov.get_apple_team_id()) { - if team_id == bundle_id { - matched_prov = Some(prov); - break; - } - } - } - - let prov = matched_prov.unwrap_or_else(|| &self.provisioning_files[0]); - fs::copy(prov.get_file_path(), bundle.get_dir().join("embedded.mobileprovision"))?; - println!("Moved {:?} to {:?}", prov.get_file_path(), bundle.get_dir().join("embedded.mobileprovision")); - - if let Ok(ent_xml) = prov.get_entitlements_as_bytes() { - settings - .set_entitlements_xml(apple_codesign::SettingsScope::Main, String::from_utf8_lossy(&ent_xml)) - .ok(); - } - } - - UnifiedSigner::new(settings).sign_path_in_place(bundle.get_dir())?; - } - } - } - - if let Some(cert) = &self.certificate { - if let Some(key) = &cert.key { - key.finish()?; - } - } - - Ok(()) - } - - fn build_base_settings(&self, shallow_override: bool) -> Result, Error> { - let mut settings = SigningSettings::default(); - if let Some(cert) = &self.certificate { - cert.load_into_signing_settings(&mut settings)?; - settings.set_team_id_from_signing_certificate(); - } - settings.set_for_notarization(false); - settings.set_shallow(shallow_override || self.settings.sign_shallow); - Ok(settings) - } -} diff --git a/crates/ldid2/src/signing/signer_settings.rs b/crates/ldid2/src/signing/signer_settings.rs deleted file mode 100644 index 8d24f954..00000000 --- a/crates/ldid2/src/signing/signer_settings.rs +++ /dev/null @@ -1,33 +0,0 @@ - - - -// for app extensions, there should be three options -// - default (sign and add mobileprovisions to everything) -// - default-remove-plugins (just like default, but some extensions are removed) -// - zsign (sign extensions with the main apps mobileprovision) -pub enum SignerMode { - Default, - Zsign, -} - -pub struct SignerSettings { - pub sign_shallow: bool, - pub sign_mode: SignerMode, - pub embed_mobileprovision: bool, - pub custom_name: Option, - pub custom_identifier: Option, - pub custom_build_version: Option, -} - -impl Default for SignerSettings { - fn default() -> Self { - Self { - sign_shallow: false, - sign_mode: SignerMode::Default, - embed_mobileprovision: true, - custom_name: None, - custom_identifier: None, - custom_build_version: None, - } - } -} diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml deleted file mode 100644 index 7b1cedfa..00000000 --- a/crates/types/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "types" -edition.workspace = true -version.workspace = true -authors.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -idevice.workspace = true -thiserror.workspace = true -plist.workspace = true -zip.workspace = true -tokio.workspace = true -uuid.workspace = true -errors = { path = "../errors" } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs deleted file mode 100644 index e66bd4ef..00000000 --- a/crates/types/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -mod bundle; -mod device; -mod package; - -pub use bundle::Bundle; -pub use bundle::BundleType; -pub use device::Device; -pub use package::Package; - -pub trait PlistInfoTrait { - fn get_name(&self) -> Option; - fn get_executable(&self) -> Option; - fn get_bundle_identifier(&self) -> Option; - fn get_version(&self) -> Option; - fn get_build_version(&self) -> Option; -} From 04f5616d2eb85dfa67fb2b2a3cf6a6c6652720ab Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 13 Nov 2025 08:28:52 -0800 Subject: [PATCH 16/72] reorganize and add some scaffolding for the install button --- apps/plumeimpactor/src/frame.rs | 280 +++++++++++-------------- apps/plumeimpactor/src/handlers.rs | 73 +++++-- apps/plumeimpactor/src/pages/login.rs | 114 ++++++++++ apps/plumeimpactor/src/pages/mod.rs | 2 +- apps/plumeimpactor/src/utils/device.rs | 2 + apps/plumesign/src/main.rs | 12 +- 6 files changed, 297 insertions(+), 186 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 348f3b94..5f656e63 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -5,20 +5,22 @@ use std::{env, ptr, thread}; use grand_slam::AnisetteConfiguration; use grand_slam::auth::Account; +use grand_slam::developer::DeveloperSession; +use grand_slam::utils::PlistInfoTrait; +use idevice::IdeviceService; +use idevice::lockdown::LockdownClient; use wxdragon::prelude::*; use futures::StreamExt; -use idevice::usbmuxd::{UsbmuxdConnection, UsbmuxdListenEvent}; +use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdListenEvent}; use tokio::runtime::Builder; use tokio::sync::mpsc; use crate::APP_NAME; use crate::handlers::{PlumeFrameMessage, PlumeFrameMessageHandler}; use crate::keychain::AccountCredentials; -use crate::pages::login::LoginDialog; -use crate::pages::{ - DefaultPage, InstallPage, create_default_page, create_install_page, create_login_dialog, -}; +use crate::pages::login::{AccountDialog, LoginDialog}; +use crate::pages::{DefaultPage, InstallPage, create_account_dialog, create_default_page, create_install_page, create_login_dialog}; use crate::utils::{Device, Package}; pub struct PlumeFrame { @@ -30,6 +32,7 @@ pub struct PlumeFrame { pub add_ipa_button: Button, pub apple_id_button: Button, pub login_dialog: LoginDialog, + pub account_dialog: AccountDialog, } impl PlumeFrame { @@ -83,6 +86,7 @@ impl PlumeFrame { add_ipa_button, apple_id_button, login_dialog: create_login_dialog(&frame), + account_dialog: create_account_dialog(&frame), }; s.setup_event_handlers(); @@ -138,12 +142,7 @@ impl PlumeFrame { let mut muxer = match UsbmuxdConnection::default().await { Ok(muxer) => muxer, Err(e) => { - sender - .send(PlumeFrameMessage::Error(format!( - "Failed to connect to usbmuxd: {}", - e - ))) - .ok(); + sender.send(PlumeFrameMessage::Error(format!("Failed to connect to usbmuxd: {}", e))).ok(); return; } }; @@ -151,30 +150,18 @@ impl PlumeFrame { match muxer.get_devices().await { Ok(devices) => { for dev in devices { - sender - .send(PlumeFrameMessage::DeviceConnected(Device::new(dev).await)) - .ok(); + sender.send(PlumeFrameMessage::DeviceConnected(Device::new(dev).await)).ok(); } } Err(e) => { - sender - .send(PlumeFrameMessage::Error(format!( - "Failed to get initial device list: {}", - e - ))) - .ok(); + sender.send(PlumeFrameMessage::Error(format!("Failed to get initial device list: {}", e))).ok(); } } let mut stream = match muxer.listen().await { Ok(stream) => stream, Err(e) => { - sender - .send(PlumeFrameMessage::Error(format!( - "Failed to listen for events: {}", - e - ))) - .ok(); + sender.send(PlumeFrameMessage::Error(format!("Failed to listen for events: {}", e))).ok(); return; } }; @@ -208,9 +195,7 @@ impl PlumeFrame { let (email, password) = match (creds.get_email(), creds.get_password()) { (Ok(email), Ok(password)) => (email, password), - _ => { - return; - } + _ => { return; } }; match run_login_flow(sender.clone(), email, password) { @@ -218,9 +203,7 @@ impl PlumeFrame { sender.send(PlumeFrameMessage::AccountLogin(account)).ok(); } Err(e) => { - sender - .send(PlumeFrameMessage::Error(format!("Login error: {}", e))) - .ok(); + sender.send(PlumeFrameMessage::Error(format!("Login error: {}", e))).ok(); sender.send(PlumeFrameMessage::AccountDeleted).ok(); } } @@ -247,63 +230,28 @@ impl PlumeFrame { // --- Apple ID / Login Dialog --- let login_dialog_rc = Rc::new(self.login_dialog.clone()); + let account_dialog_rc = Rc::new(self.account_dialog.clone()); + let handler_for_account = message_handler.clone(); self.apple_id_button.on_click({ let login_dialog = login_dialog_rc.clone(); - let handler_for_account = message_handler.clone(); - let frame_for_dialog = self.frame.clone(); - let sender_for_logout = sender.clone(); + let account_dialog = account_dialog_rc.clone(); move |_| { - let logged_in = handler_for_account.borrow().account_credentials.is_some(); - - if logged_in { - let creds = AccountCredentials; - let email = creds - .get_email() - .unwrap_or_else(|_| "(unknown)".to_string()); - - let dialog = Dialog::builder(&frame_for_dialog, "Account") - .with_style(DialogStyle::DefaultDialogStyle) - .build(); - - let sizer = BoxSizer::builder(Orientation::Vertical).build(); - sizer.add_spacer(12); - let label = StaticText::builder(&dialog) - .with_label(&format!( - "Logged in as {:?} ({})", - handler_for_account - .borrow() - .account_credentials - .clone() - .unwrap() - .get_name(), - email - )) - .build(); - sizer.add(&label, 0, SizerFlag::All, 12); - - let buttons = BoxSizer::builder(Orientation::Horizontal).build(); - let logout_btn = Button::builder(&dialog).with_label("Log out").build(); - buttons.add(&logout_btn, 0, SizerFlag::All, 8); - sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); - - dialog.set_sizer(sizer, true); - - let dlg_logout = dialog.clone(); - let sender_clone = sender_for_logout.clone(); - logout_btn.on_click(move |_| { - let creds = AccountCredentials; - creds.delete_password().ok(); - sender_clone.send(PlumeFrameMessage::AccountDeleted).ok(); - dlg_logout.end_modal(ID_OK as i32); - }); - - dialog.show_modal(); - dialog.destroy(); + if let Some(creds) = handler_for_account.borrow().account_credentials.as_ref() { + let (first, last) = creds.get_name(); + account_dialog.set_account_name((first, last)); + account_dialog.show_modal(); } else { login_dialog.show_modal(); } } }); + + self.account_dialog.set_logout_handler({ + let sender = sender.clone(); + move || { + sender.send(PlumeFrameMessage::AccountDeleted).ok(); + } + }); // --- Login Dialog "Next" Button --- @@ -322,16 +270,92 @@ impl PlumeFrame { } }); + + + + + + + + + + + let message_handler_for_install = message_handler.clone(); self.install_page.set_install_handler({ + let frame = self.frame.clone(); let sender = sender.clone(); move || { - sender - .send(PlumeFrameMessage::PackageInstallationStarted) - .ok(); + let binding = message_handler_for_install.borrow(); + + let Some(selected_device) = binding.usbmuxd_selected_device_id.as_deref() else { + sender.send(PlumeFrameMessage::Error("No device selected for installation.".to_string())).ok(); + return; + }; + + let Some(selected_package) = binding.package_selected.as_ref() else { + sender.send(PlumeFrameMessage::Error("No package selected for installation.".to_string())).ok(); + return; + }; + + let Some(selected_account) = binding.account_credentials.as_ref() else { + sender.send(PlumeFrameMessage::Error("No Apple ID account available for installation.".to_string())).ok(); + return; + }; + + let package = selected_package.clone(); + let account = selected_account.clone(); + let device_id = selected_device.to_string(); + let sender_clone = sender.clone(); + + thread::spawn(move || { + let rt = Builder::new_current_thread().enable_all().build().unwrap(); + + let install_result = rt.block_on(async { + let anisette_config = AnisetteConfiguration::default() + .set_configuration_path(PathBuf::from(env::temp_dir())); + + let session = DeveloperSession::with(account.clone()); + + sender_clone.send(PlumeFrameMessage::InstallProgress(0, Some("Ensuring device is registered...".to_string()))).ok(); + + let mut usbmuxd = UsbmuxdConnection::default().await + .map_err(|e| format!("usbmuxd connect error: {e}"))?; + let usbmuxd_device = usbmuxd.get_devices().await + .map_err(|e| format!("usbmuxd device list error: {e}"))? + .into_iter() + .find(|d| d.device_id.to_string() == device_id) + .ok_or_else(|| format!("Device ID {device_id} not found"))?; + + let mut lockdown = LockdownClient::connect( + &usbmuxd_device.to_provider(UsbmuxdAddr::default(), "plume_install") + ) + .await + .map_err(|e| format!("lockdown connect error: {e}"))?; + + Ok::<_, String>(()) + }); + + if let Err(e) = install_result { + sender_clone.send(PlumeFrameMessage::InstallProgress(100, Some(format!("Install failed: {}", e)))).ok(); + return; + } + }); } }); + + + + + + + + + + + } + fn bind_login_dialog_next_handler( &self, sender: mpsc::UnboundedSender, @@ -356,12 +380,7 @@ impl PlumeFrame { let creds = AccountCredentials; if let Err(e) = creds.set_credentials(email.clone(), password.clone()) { - sender - .send(PlumeFrameMessage::Error(format!( - "Failed to save credentials: {}", - e - ))) - .ok(); + sender.send(PlumeFrameMessage::Error(format!("Failed to save credentials: {}", e))).ok(); return; } @@ -371,12 +390,8 @@ impl PlumeFrame { let sender_for_login_thread = sender.clone(); thread::spawn(move || { match run_login_flow(sender_for_login_thread.clone(), email, password) { - Ok(account) => sender_for_login_thread - .send(PlumeFrameMessage::AccountLogin(account)) - .ok(), - Err(e) => sender_for_login_thread - .send(PlumeFrameMessage::Error(format!("Login failed: {}", e))) - .ok(), + Ok(account) => sender_for_login_thread.send(PlumeFrameMessage::AccountLogin(account)).ok(), + Err(e) => sender_for_login_thread.send(PlumeFrameMessage::Error(format!("Login failed: {}", e))).ok(), } }); }); @@ -413,71 +428,13 @@ impl PlumeFrame { fn process_package_file(sender: mpsc::UnboundedSender, file_path: PathBuf) { match Package::new(file_path) { Ok(package) => { - sender - .send(PlumeFrameMessage::PackageSelected(package)) - .ok(); + sender.send(PlumeFrameMessage::PackageSelected(package)).ok(); } Err(e) => { - sender - .send(PlumeFrameMessage::Error(format!( - "Failed to open package: {}", - e - ))) - .ok(); + sender.send(PlumeFrameMessage::Error(format!("Failed to open package: {}", e))).ok(); } } } - - pub fn create_single_field_dialog(&self, title: &str, label: &str) -> Result { - let dialog = Dialog::builder(&self.frame, title) - .with_style(DialogStyle::DefaultDialogStyle) - .build(); - - let sizer = BoxSizer::builder(Orientation::Vertical).build(); - sizer.add_spacer(16); - - sizer.add( - &StaticText::builder(&dialog).with_label(label).build(), - 0, - SizerFlag::All, - 12, - ); - let text_field = TextCtrl::builder(&dialog).build(); - sizer.add(&text_field, 0, SizerFlag::Expand | SizerFlag::All, 8); - - let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); - - let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); - let ok_button = Button::builder(&dialog).with_label("OK").build(); - - button_sizer.add(&cancel_button, 0, SizerFlag::All, 8); - button_sizer.add_spacer(8); - button_sizer.add(&ok_button, 0, SizerFlag::All, 8); - - sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 8); - - dialog.set_sizer(sizer, true); - - cancel_button.on_click({ - let dialog = dialog.clone(); - move |_| dialog.end_modal(ID_CANCEL as i32) - }); - ok_button.on_click({ - let dialog = dialog.clone(); - move |_| dialog.end_modal(ID_OK as i32) - }); - - text_field.set_focus(); - - let rc = dialog.show_modal(); - let result = if rc == ID_OK as i32 { - Ok(text_field.get_value().to_string()) - } else { - Err("2FA cancelled".to_string()) - }; - dialog.destroy(); - result - } } pub fn run_login_flow( @@ -485,14 +442,11 @@ pub fn run_login_flow( email: String, password: String, ) -> Result { - let anisette_config = - AnisetteConfiguration::default().set_configuration_path(PathBuf::from(env::temp_dir())); - - let rt = match Builder::new_current_thread().enable_all().build() { - Ok(rt) => rt, - Err(e) => return Err(format!("Failed to create Tokio runtime: {}", e)), - }; + let anisette_config = AnisetteConfiguration::default() + .set_configuration_path(PathBuf::from(env::temp_dir())); + let rt = Builder::new_current_thread().enable_all().build().unwrap(); + let (code_tx, code_rx) = std::sync::mpsc::channel::>(); let account_result = rt.block_on(Account::login( diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 6435ac3a..880b5464 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -5,10 +5,11 @@ use tokio::sync::mpsc::error::TryRecvError; use std::sync::mpsc as std_mpsc; use grand_slam::auth::Account; -use crate::utils::{Device, Package}; use grand_slam::utils::PlistInfoTrait; use crate::frame::PlumeFrame; +use crate::keychain::AccountCredentials; +use crate::utils::{Device, Package}; #[derive(Debug)] pub enum PlumeFrameMessage { @@ -16,16 +17,17 @@ pub enum PlumeFrameMessage { DeviceDisconnected(u32), PackageSelected(Package), PackageDeselected, - PackageInstallationStarted, AccountLogin(Account), AccountDeleted, AwaitingTwoFactorCode(std_mpsc::Sender>), + InstallProgress(i32, Option), Error(String), } pub struct PlumeFrameMessageHandler { pub receiver: mpsc::UnboundedReceiver, pub plume_frame: PlumeFrame, + pub installation_progress_dialog: Option, // --- device --- pub usbmuxd_device_list: Vec, pub usbmuxd_selected_device_id: Option, @@ -47,6 +49,7 @@ impl PlumeFrameMessageHandler { usbmuxd_selected_device_id: None, package_selected: None, account_credentials: None, + installation_progress_dialog: None, } } @@ -106,14 +109,20 @@ impl PlumeFrameMessageHandler { return; } - let package_name = package.get_name().unwrap_or_else(|| "Unknown".to_string()); + let package_name = package + .get_name() + .unwrap_or_else(|| "Unknown".to_string()); let package_id = package .get_bundle_identifier() .unwrap_or_else(|| "Unknown".to_string()); + let package_version = package + .get_version() + .unwrap_or_else(|| "Unknown".to_string()); + self.package_selected = Some(package); self.plume_frame .install_page - .set_top_text(format!("{} - {}", package_name, package_id).as_str()); + .set_top_text(format!("{} - {} ({})", package_name, package_id, package_version).as_str()); self.plume_frame.default_page.panel.hide(); self.plume_frame.install_page.panel.show(true); self.plume_frame.frame.layout(); @@ -123,21 +132,30 @@ impl PlumeFrameMessageHandler { self.plume_frame.install_page.panel.hide(); self.plume_frame.default_page.panel.show(true); self.plume_frame.frame.layout(); - } - PlumeFrameMessage::PackageInstallationStarted => { - todo!() } PlumeFrameMessage::AccountLogin(account) => { - self.account_credentials = Some(account); - let creds = crate::keychain::AccountCredentials; - let email = creds.get_email().unwrap_or_else(|_| "(unknown)".to_string()); - let msg = format!("Logged in as {:?} ({})", self.account_credentials.clone().unwrap().get_name(), email); - let dialog = MessageDialog::builder(&self.plume_frame.frame, &msg, "Signed In") - .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconInformation) - .build(); + let (first, last) = account.get_name(); + let dialog = MessageDialog::builder( + &self.plume_frame.frame, + &format!("Logged in as {} {}", first, last), + "Signed In" + ) + .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconInformation) + .build(); dialog.show_modal(); + self.account_credentials = Some(account); } PlumeFrameMessage::AccountDeleted => { + if self.account_credentials.is_none() { + return; + } + + let creds = AccountCredentials; + if let Err(e) = creds.delete_password() { + self.handle_message(PlumeFrameMessage::Error(format!("Failed to delete account credentials: {}", e))); + return; + } + self.account_credentials = None; } PlumeFrameMessage::AwaitingTwoFactorCode(tx) => { @@ -147,8 +165,31 @@ impl PlumeFrameMessageHandler { ); if let Err(e) = tx.send(result) { - println!("Failed to send 2FA code back to background thread: {:?}", e); - + self.handle_message(PlumeFrameMessage::Error(format!("Failed to send two-factor code response: {}", e))); + } + } + PlumeFrameMessage::InstallProgress(progress, message_opt) => { + let Some(selected_package) = &self.package_selected else { + return; + }; + + if self.installation_progress_dialog.is_none() { + let progress_dialog = ProgressDialog::builder( + &self.plume_frame.frame, + &format!("Installing {}", selected_package.get_name().unwrap_or("Unknown".to_string())), + "Waiting...", + 100 + ) + .show_estimated_time().show_remaining_time().smooth().build(); + self.installation_progress_dialog = Some(progress_dialog); + } + + if let Some(dialog) = &mut self.installation_progress_dialog { + dialog.update(progress, message_opt.as_deref()); + + if progress >= 100 { + self.installation_progress_dialog = None; + } } } PlumeFrameMessage::Error(error_msg) => { diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 9c651319..204d617f 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -1,5 +1,7 @@ use wxdragon::prelude::*; +use crate::frame::PlumeFrame; + #[derive(Clone)] pub struct LoginDialog { pub dialog: Dialog, @@ -96,3 +98,115 @@ impl LoginDialog { }); } } + +// MARK: - AccountDialog + +#[derive(Clone)] +pub struct AccountDialog { + pub dialog: Dialog, + pub logout_button: Button, + pub label: StaticText, +} + +pub fn create_account_dialog(parent: &Window) -> AccountDialog { + let dialog = Dialog::builder(parent, "Account") + .with_style(DialogStyle::DefaultDialogStyle) + .build(); + + let sizer = BoxSizer::builder(Orientation::Vertical).build(); + sizer.add_spacer(12); + + let label = StaticText::builder(&dialog) + .with_label("") + .build(); + sizer.add(&label, 0, SizerFlag::All, 12); + + let buttons = BoxSizer::builder(Orientation::Horizontal).build(); + let logout_button = Button::builder(&dialog).with_label("Log out").build(); + buttons.add(&logout_button, 0, SizerFlag::All, 8); + sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); + + dialog.set_sizer(sizer, true); + + AccountDialog { + dialog, + logout_button, + label, + } +} + +impl AccountDialog { + pub fn show_modal(&self) { + self.dialog.show_modal(); + } + + pub fn hide(&self) { + self.dialog.end_modal(0); + } + + pub fn set_logout_handler(&self, on_logout: impl Fn() + 'static) { + let dialog = self.dialog.clone(); + self.logout_button.on_click(move |_| { + on_logout(); + dialog.end_modal(ID_OK as i32); + }); + } + + pub fn set_account_name(&self, account_name: (String, String)) { + self.label.set_label(&format!("Logged in as {} {}", account_name.0, account_name.1)); + } +} + +// MARK: - Single Field Dialog +impl PlumeFrame { + pub fn create_single_field_dialog(&self, title: &str, label: &str) -> Result { + let dialog = Dialog::builder(&self.frame, title) + .with_style(DialogStyle::DefaultDialogStyle) + .build(); + + let sizer = BoxSizer::builder(Orientation::Vertical).build(); + sizer.add_spacer(16); + + sizer.add( + &StaticText::builder(&dialog).with_label(label).build(), + 0, + SizerFlag::All, + 12, + ); + let text_field = TextCtrl::builder(&dialog).build(); + sizer.add(&text_field, 0, SizerFlag::Expand | SizerFlag::All, 8); + + let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); + + let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); + let ok_button = Button::builder(&dialog).with_label("OK").build(); + + button_sizer.add(&cancel_button, 0, SizerFlag::All, 8); + button_sizer.add_spacer(8); + button_sizer.add(&ok_button, 0, SizerFlag::All, 8); + + sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 8); + + dialog.set_sizer(sizer, true); + + cancel_button.on_click({ + let dialog = dialog.clone(); + move |_| dialog.end_modal(ID_CANCEL as i32) + }); + ok_button.on_click({ + let dialog = dialog.clone(); + move |_| dialog.end_modal(ID_OK as i32) + }); + + text_field.set_focus(); + + let rc = dialog.show_modal(); + let result = if rc == ID_OK as i32 { + Ok(text_field.get_value().to_string()) + } else { + Err("2FA cancelled".to_string()) + }; + dialog.destroy(); + result + } +} diff --git a/apps/plumeimpactor/src/pages/mod.rs b/apps/plumeimpactor/src/pages/mod.rs index fe9846c9..71cbbec6 100644 --- a/apps/plumeimpactor/src/pages/mod.rs +++ b/apps/plumeimpactor/src/pages/mod.rs @@ -5,4 +5,4 @@ pub mod install; pub use install::{InstallPage, create_install_page}; pub mod login; -pub use login::create_login_dialog; +pub use login::{create_login_dialog, create_account_dialog}; diff --git a/apps/plumeimpactor/src/utils/device.rs b/apps/plumeimpactor/src/utils/device.rs index 8ddc4571..71e58091 100644 --- a/apps/plumeimpactor/src/utils/device.rs +++ b/apps/plumeimpactor/src/utils/device.rs @@ -11,6 +11,7 @@ pub const CONNECTION_LABEL: &str = "plume"; #[derive(Debug, Clone)] pub struct Device { pub name: String, + pub uuid: String, pub usbmuxd_device: UsbmuxdDevice, } @@ -19,6 +20,7 @@ impl Device { let name = get_name_from_usbmuxd_device(&usbmuxd_device).await.unwrap_or_default(); Device { name, + uuid: usbmuxd_device.udid.clone(), usbmuxd_device } } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index 4148103e..e1ec4948 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -48,17 +48,17 @@ async fn main() { rustls::crypto::ring::default_provider() .install_default() - .expect("Failed to install rustls crypto provider"); + .expect("--x failed to install rustls crypto provider"); match &cli.command { Commands::Sign(args) => { if args.pem_files.len() < 2 { - eprintln!("Error: At least two PEM files (certificate and key) are required via --pem."); + eprintln!("--x at least two PEM files (certificate and key) are required via --pem."); exit(1); } let signing_key = Certificate::new(args.pem_files.clone().into()).unwrap_or_else(|e| { - eprintln!("Failed to create Certificate: {e}"); + eprintln!("--x failed to create Certificate: {e}"); exit(1); }); @@ -66,7 +66,7 @@ async fn main() { .map(MobileProvision::load) .collect::, _>>() .unwrap_or_else(|e| { - eprintln!("Failed to load provisioning profiles: {e}"); + eprintln!("--x failed to load provisioning profiles: {e}"); exit(1); }); @@ -81,11 +81,11 @@ async fn main() { let target_path = args.bundle.clone(); if let Err(e) = signer.sign(target_path.clone()) { - eprintln!("Failed to sign target: {e}"); + eprintln!("--x failed to sign: {e}"); exit(1); } - println!("Signed target: {:?}", target_path); + println!("--> signed: {:?}", target_path); } } } From c22d8cabc8f26869722840ce741591f39416ae52 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Fri, 14 Nov 2025 07:06:36 -0800 Subject: [PATCH 17/72] the basics for now --- Cargo.toml | 2 +- LICENSE | 20 +++ README.md | 10 +- apps/plumeimpactor/src/frame.rs | 155 ++++++++++++++++-- apps/plumeimpactor/src/handlers.rs | 7 +- apps/plumeimpactor/src/pages/login.rs | 4 - apps/plumeimpactor/src/utils/device.rs | 47 +++--- crates/grand_slam/Cargo.toml | 2 +- .../grand_slam/src/developer/qh/app_groups.rs | 26 ++- crates/grand_slam/src/developer/qh/app_ids.rs | 23 ++- crates/grand_slam/src/developer/qh/devices.rs | 9 + crates/grand_slam/src/developer/qh/mod.rs | 1 + crates/grand_slam/src/developer/qh/profile.rs | 55 +++++++ crates/grand_slam/src/developer/qh/teams.rs | 2 +- crates/grand_slam/src/utils/bundle.rs | 6 + crates/grand_slam/src/utils/provision.rs | 19 ++- 16 files changed, 326 insertions(+), 62 deletions(-) create mode 100644 LICENSE create mode 100644 crates/grand_slam/src/developer/qh/profile.rs diff --git a/Cargo.toml b/Cargo.toml index 8e6944bd..7e7bab66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ edition = "2024" version = "0.0.1" authors = ["khcrysalis "] -license = "GPL-3.0-or-later" +license = "MIT" repository = "https://github.com/khcrysalis/plumestore" [workspace.dependencies] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..1392705a --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright 2025 Samara M + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index e0802cfd..40c926e3 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ The project is seperated in multiple modules, all serve single or multiple uses | `apps/plumeimpactor` | GUI interface for the crates shown below, backend using wxWidgets (with a rust ffi wrapper, wxDragon) | | `apps/plumesign` | CLI interface for the crates shown below, using `clap`. | | `crates/grand_slam` | Handles all api request used for communicating with Apple developer services, along with providing auth for Apple's grandslam | -| `crates/ldid2` | Wrapper for applecodesign-rs with additional features, specifically made to support iOS sideloading and app modifications | ## Acknowledgements -- [Samara](https://github.com/khcrysalis) - ME! -- [apple-private-apis](https://github.com/SideStore/apple-private-apis) - Grandslam auth & Omnisette. -- [apple-codesign-rs](https://github.com/indygreg/apple-platform-rs) - Open-source alternative to codesign. -- [idevice](https://github.com/jkcoxson/idevice) - Used for communication with `installd`, specifically for sideloading the apps to your devices. +- [SAMSAM](https://github.com/khcrysalis) – The maker. +- [SideStore](https://github.com/SideStore/apple-private-apis) – Grandslam auth & Omnisette. +- [Sideloader](https://github.com/Dadoum/Sideloader) – Apple Developer API references. +- [idevice](https://github.com/jkcoxson/idevice) – Used for communication with `installd`, specifically for sideloading the apps to your devices. +- [apple-codesign-rs](https://github.com/indygreg/apple-platform-rs) – Open-source alternative to codesign. diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 5f656e63..cb93453e 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -1,9 +1,9 @@ use std::cell::RefCell; use std::path::PathBuf; use std::rc::Rc; -use std::{env, ptr, thread}; +use std::{env, fs, ptr, thread}; -use grand_slam::AnisetteConfiguration; +use grand_slam::{AnisetteConfiguration, BundleType, MachO, MobileProvision}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; use grand_slam::utils::PlistInfoTrait; @@ -311,12 +311,9 @@ impl PlumeFrame { let rt = Builder::new_current_thread().enable_all().build().unwrap(); let install_result = rt.block_on(async { - let anisette_config = AnisetteConfiguration::default() - .set_configuration_path(PathBuf::from(env::temp_dir())); - let session = DeveloperSession::with(account.clone()); - sender_clone.send(PlumeFrameMessage::InstallProgress(0, Some("Ensuring device is registered...".to_string()))).ok(); + sender_clone.send(PlumeFrameMessage::InstallProgress(10, Some("Ensuring current device is registered...".to_string()))).ok(); let mut usbmuxd = UsbmuxdConnection::default().await .map_err(|e| format!("usbmuxd connect error: {e}"))?; @@ -326,17 +323,151 @@ impl PlumeFrame { .find(|d| d.device_id.to_string() == device_id) .ok_or_else(|| format!("Device ID {device_id} not found"))?; - let mut lockdown = LockdownClient::connect( - &usbmuxd_device.to_provider(UsbmuxdAddr::default(), "plume_install") - ) - .await - .map_err(|e| format!("lockdown connect error: {e}"))?; + let device = Device::new(usbmuxd_device).await; + + // TODO: Handle multiple teams properly + let teams = session.qh_list_teams().await.map_err(|e| format!("Failed to list teams: {}", e))?; + + session.qh_ensure_device( + &teams.teams.get(0) + .ok_or("No teams available for the Apple ID account.")? + .team_id, + &device.name, + &device.uuid, + ).await.map_err(|e| format!("Failed to ensure device is registered: {}", e))?; + + let bundle = package.get_package_bundle() + .map_err(|e| format!("Failed to get package bundle: {}", e))?; + let bundles = bundle.collect_bundles_sorted() + .map_err(|e| format!("Failed to collect bundles: {}", e))?; + + let team_id = &teams.teams.get(0) + .ok_or("No teams available for the Apple ID account.")? + .team_id; + + let bundle_identifier = bundle.get_bundle_identifier() + .ok_or("Failed to get bundle identifier from package.")?; + + let new_id = new_identifier(&bundle_identifier, team_id); + + fn new_identifier(original: &str, team_id: &str) -> String { + format!("{}.{}", original, team_id) + } + + if let Some(old_identifier) = bundle.get_bundle_identifier() { + for embedded_bundle in &bundles { + embedded_bundle.set_matching_identifier( + &old_identifier, + &new_id, + ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; + } + } + + let mut provisionings: Vec = Vec::new(); + for bundle in &bundles { + if + bundle._type != BundleType::AppExtension && + bundle._type != BundleType::App + { + continue; + } + + sender_clone.send(PlumeFrameMessage::InstallProgress( + 20, + Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())) + )).ok(); + + let bundle_executable_name = bundle.get_executable() + .ok_or("Failed to get executable from bundle.")?; + + let bundle_executable_path = bundle.dir().join(&bundle_executable_name); + + let macho = MachO::new(&bundle_executable_path) + .map_err(|e| format!("Failed to read Mach-O binary: {}", e))?; + + let macho_entitlements = macho.entitlements() + .map_err(|e| format!("Failed to get entitlements from Mach-O binary: {}", e))?; + + let id = bundle.get_bundle_identifier() + .ok_or("Failed to get bundle identifier from bundle.")?; + + let app_groups: Vec = macho_entitlements + .as_ref() + .and_then(|dict| dict.get("com.apple.security.application-groups")) + .and_then(|val| val.as_array()) + .map(|arr| { + arr.iter() + .filter_map(|v| v.as_string().map(|s| format!("{}.{}", s, team_id))) + .collect() + }) + .unwrap_or_else(Vec::new); + + session.qh_ensure_app_id(team_id, &bundle.get_name().unwrap_or_default(), &id) + .await + .map_err(|e| format!("Failed to ensure app ID: {}", e))?; + + let capabilities = session.v1_list_capabilities(team_id).await + .map_err(|e| format!("Failed to list capabilities: {}", e))?; + + println!("Mach-O Entitlements: {:?}", &macho_entitlements); + + let mut capabilities_to_enable = Vec::new(); + + if let Some(entitlements) = &macho_entitlements { + for (ent_key, _) in entitlements { + for cap in &capabilities.data { + if let Some(ent_list) = &cap.attributes.entitlements { + if ent_list.iter().any(|e| e.profile_key == *ent_key) { + capabilities_to_enable.push(cap.id.clone()); + } + } + } + } + } + + println!("Enabling capabilities: {:?}", &capabilities_to_enable); + + let app_id_id = session.qh_get_app_id(team_id, &id).await + .map_err(|e| e.to_string())? + .ok_or("Failed to get ensured app ID.")?; + + if !capabilities_to_enable.is_empty() { + session.v1_update_app_id(team_id, &id, capabilities_to_enable) + .await + .map_err(|e| format!("Failed to enable capabilities: {}", e))?; + } + + for group in &app_groups { + let group_id = session.qh_ensure_app_group(team_id, group, group) + .await + .map_err(|e| format!("Failed to ensure app group: {}", e))?; + + println!("{:#?}", group_id); + + session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) + .await + .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + } + + let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id).await + .map_err(|e| format!("Failed to list profiles: {}", e))?; + + let profile_data = profiles.provisioning_profile.encoded_profile; + + let mobile_provision = MobileProvision::load_from_bytes(profile_data.as_ref()) + .map_err(|e| format!("Failed to load mobile provision: {}", e))?; + + provisionings.push(mobile_provision); + } + + sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some("Downloading Certificates...".to_string()))).ok(); + Ok::<_, String>(()) }); if let Err(e) = install_result { - sender_clone.send(PlumeFrameMessage::InstallProgress(100, Some(format!("Install failed: {}", e)))).ok(); + sender_clone.send(PlumeFrameMessage::InstallProgress(99, Some(format!("{}", e)))).ok(); return; } }); diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 880b5464..f676903c 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -180,7 +180,12 @@ impl PlumeFrameMessageHandler { "Waiting...", 100 ) - .show_estimated_time().show_remaining_time().smooth().build(); + .show_elapsed_time() + .show_estimated_time() + .show_remaining_time() + .smooth() + .build(); + self.installation_progress_dialog = Some(progress_dialog); } diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 204d617f..4c0ee0f6 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -140,10 +140,6 @@ impl AccountDialog { self.dialog.show_modal(); } - pub fn hide(&self) { - self.dialog.end_modal(0); - } - pub fn set_logout_handler(&self, on_logout: impl Fn() + 'static) { let dialog = self.dialog.clone(); self.logout_button.on_click(move |_| { diff --git a/apps/plumeimpactor/src/utils/device.rs b/apps/plumeimpactor/src/utils/device.rs index 71e58091..1c76a4af 100644 --- a/apps/plumeimpactor/src/utils/device.rs +++ b/apps/plumeimpactor/src/utils/device.rs @@ -8,6 +8,17 @@ use crate::Error; pub const CONNECTION_LABEL: &str = "plume"; +macro_rules! get_dict_string { + ($dict:expr, $key:expr) => { + $dict + .as_dictionary() + .and_then(|dict| dict.get($key)) + .and_then(|v| v.as_string()) + .map(|s| s.to_string()) + .unwrap_or_else(|| "".to_string()) + }; +} + #[derive(Debug, Clone)] pub struct Device { pub name: String, @@ -17,13 +28,24 @@ pub struct Device { impl Device { pub async fn new(usbmuxd_device: UsbmuxdDevice) -> Self { - let name = get_name_from_usbmuxd_device(&usbmuxd_device).await.unwrap_or_default(); - Device { - name, + let name = Self::get_name_from_usbmuxd_device(&usbmuxd_device) + .await + .unwrap_or_default(); + + Device { + name, uuid: usbmuxd_device.udid.clone(), usbmuxd_device } } + + async fn get_name_from_usbmuxd_device( + device: &UsbmuxdDevice, + ) -> Result { + let mut lockdown = LockdownClient::connect(&device.to_provider(UsbmuxdAddr::default(), CONNECTION_LABEL)).await?; + let values = lockdown.get_value(None, None).await?; + Ok(get_dict_string!(values, "DeviceName")) + } } impl fmt::Display for Device { @@ -40,22 +62,3 @@ impl fmt::Display for Device { ) } } - -macro_rules! get_dict_string { - ($dict:expr, $key:expr) => { - $dict - .as_dictionary() - .and_then(|dict| dict.get($key)) - .and_then(|v| v.as_string()) - .map(|s| s.to_string()) - .unwrap_or_else(|| "".to_string()) - }; -} - -async fn get_name_from_usbmuxd_device( - device: &UsbmuxdDevice, -) -> Result { - let mut lockdown = LockdownClient::connect(&device.to_provider(UsbmuxdAddr::default(), CONNECTION_LABEL)).await?; - let values = lockdown.get_value(None, None).await?; - Ok(get_dict_string!(values, "DeviceName")) -} diff --git a/crates/grand_slam/Cargo.toml b/crates/grand_slam/Cargo.toml index 0dd504e2..ded0a19d 100644 --- a/crates/grand_slam/Cargo.toml +++ b/crates/grand_slam/Cargo.toml @@ -3,7 +3,7 @@ name = "grand_slam" edition.workspace = true version.workspace = true authors.workspace = true -license.workspace = true +license = "MPL-2.0" repository.workspace = true [package.metadata.patch] diff --git a/crates/grand_slam/src/developer/qh/app_groups.rs b/crates/grand_slam/src/developer/qh/app_groups.rs index 139bafbd..8a8a5b39 100644 --- a/crates/grand_slam/src/developer/qh/app_groups.rs +++ b/crates/grand_slam/src/developer/qh/app_groups.rs @@ -32,8 +32,26 @@ impl DeveloperSession { Ok(response_data) } + + pub async fn qh_get_app_group(&self, team_id: &str, app_group_identifier: &str) -> Result, Error> { + let response_data = self.qh_list_app_groups(team_id).await?; + + let app_group = response_data.application_group_list.into_iter() + .find(|group| group.identifier == app_group_identifier); + + Ok(app_group) + } + + pub async fn qh_ensure_app_group(&self, team_id: &str, name: &str, identifier: &str) -> Result { + if let Some(app_group) = self.qh_get_app_group(team_id, identifier).await? { + Ok(app_group) + } else { + let response = self.qh_add_app_group(team_id, name, identifier).await?; + Ok(response.application_group) + } + } - pub async fn qh_assign_app_group(&self, team_id: &str, app_id_id: &str, app_group_id: &str) -> Result { + pub async fn qh_assign_app_group(&self, team_id: &str, app_id_id: &str, app_group_id: &str) -> Result { let endpoint = developer_endpoint!("/QH65B2/ios/assignApplicationGroupToAppId.action"); let mut body = Dictionary::new(); @@ -42,7 +60,7 @@ impl DeveloperSession { body.insert("applicationGroups".to_string(), Value::String(app_group_id.to_string())); let response = self.qh_send_request(&endpoint, Some(body)).await?; - let response_data: AppGroupResponse = plist::from_value(&Value::Dictionary(response))?; + let response_data: ResponseMeta = plist::from_value(&Value::Dictionary(response))?; Ok(response_data) } @@ -70,9 +88,9 @@ pub struct AppGroupResponse { #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct ApplicationGroup { - pub application_group: String, + pub application_group: String, // this is the actual identifier pub name: String, pub status: String, prefix: String, - pub identifier: String, + pub identifier: String, // this is the group.identifier } diff --git a/crates/grand_slam/src/developer/qh/app_ids.rs b/crates/grand_slam/src/developer/qh/app_ids.rs index c45fadcd..ce457eee 100644 --- a/crates/grand_slam/src/developer/qh/app_ids.rs +++ b/crates/grand_slam/src/developer/qh/app_ids.rs @@ -18,7 +18,7 @@ impl DeveloperSession { Ok(response_data) } - + pub async fn qh_add_app_id(&self, team_id: &str, name: &str, identifier: &str) -> Result { let endpoint = developer_endpoint!("/QH65B2/ios/addAppId.action"); @@ -26,7 +26,7 @@ impl DeveloperSession { body.insert("teamId".to_string(), Value::String(team_id.to_string())); body.insert("name".to_string(), Value::String(name.to_string())); body.insert("identifier".to_string(), Value::String(identifier.to_string())); - + let response = self.qh_send_request(&endpoint, Some(body)).await?; let response_data: AppIDResponse = plist::from_value(&Value::Dictionary(response))?; @@ -71,6 +71,15 @@ impl DeveloperSession { Ok(app_id) } + + pub async fn qh_ensure_app_id(&self, team_id: &str, name: &str, identifier: &String) -> Result { + if let Some(app_id) = self.qh_get_app_id(team_id, identifier).await? { + Ok(app_id) + } else { + let response = self.qh_add_app_id(team_id, name, identifier).await?; + Ok(response.app_id) + } + } } #[allow(dead_code)] @@ -95,20 +104,20 @@ pub struct AppIDResponse { #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct AppID { - app_id_id: String, + pub app_id_id: String, name: String, app_id_platform: String, prefix: String, - identifier: String, + pub identifier: String, is_wild_card: bool, is_duplicate: bool, features: Features, enabled_features: Option>, is_dev_push_enabled: bool, is_prod_push_enabled: bool, - associated_application_groups_count: Integer, - associated_cloud_containers_count: Integer, - associated_identifiers_count: Integer, + associated_application_groups_count: Option, + associated_cloud_containers_count: Option, + associated_identifiers_count: Option, } #[allow(dead_code)] diff --git a/crates/grand_slam/src/developer/qh/devices.rs b/crates/grand_slam/src/developer/qh/devices.rs index e0991584..c6a52957 100644 --- a/crates/grand_slam/src/developer/qh/devices.rs +++ b/crates/grand_slam/src/developer/qh/devices.rs @@ -41,6 +41,15 @@ impl DeveloperSession { Ok(device) } + + pub async fn qh_ensure_device(&self, team_id: &str, device_name: &str, device_udid: &str) -> Result { + if let Some(device) = self.qh_get_device(team_id, device_udid).await? { + Ok(device) + } else { + let response = self.qh_add_device(team_id, device_name, device_udid).await?; + Ok(response.device) + } + } } #[allow(dead_code)] diff --git a/crates/grand_slam/src/developer/qh/mod.rs b/crates/grand_slam/src/developer/qh/mod.rs index 84df09d3..ae1384b3 100644 --- a/crates/grand_slam/src/developer/qh/mod.rs +++ b/crates/grand_slam/src/developer/qh/mod.rs @@ -4,6 +4,7 @@ pub mod app_ids; pub mod certs; pub mod devices; pub mod teams; +pub mod profile; use serde::Deserialize; use plist::Integer; diff --git a/crates/grand_slam/src/developer/qh/profile.rs b/crates/grand_slam/src/developer/qh/profile.rs new file mode 100644 index 00000000..e527962a --- /dev/null +++ b/crates/grand_slam/src/developer/qh/profile.rs @@ -0,0 +1,55 @@ +use serde::Deserialize; +use plist::{Data, Date, Dictionary, Value}; + +use crate::Error; + +use crate::{SessionRequestTrait, developer_endpoint}; +use super::{DeveloperSession, ResponseMeta}; + +impl DeveloperSession { + pub async fn qh_get_profile(&self, team_id: &str, app_id_id: &str) -> Result { + let endpoint = developer_endpoint!("/QH65B2/ios/downloadTeamProvisioningProfile.action"); + + let mut body = Dictionary::new(); + body.insert("teamId".to_string(), Value::String(team_id.to_string())); + body.insert("appIdId".to_string(), Value::String(app_id_id.to_string())); + + let response = self.qh_send_request(&endpoint, Some(body)).await?; + let response_data: ProfilesResponse = plist::from_value(&Value::Dictionary(response))?; + + Ok(response_data) + } +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ProfilesResponse { + pub provisioning_profile: Profile, + #[serde(flatten)] + pub meta: ResponseMeta, +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Profile { + provisioning_profile_id: String, + name: String, + status: String, + #[serde(rename = "type")] + _type: String, + distribution_method: String, + pro_pro_platorm: Option, + #[serde(rename = "UUID")] + uuid: String, + pub date_expire: Date, + managing_app: Option, + // app_id: AppID, + app_id_id: String, + pub encoded_profile: Data, + pub filename: String, + is_template_profile: bool, + is_team_profile: bool, + is_free_provisioning_profile: Option, +} diff --git a/crates/grand_slam/src/developer/qh/teams.rs b/crates/grand_slam/src/developer/qh/teams.rs index 568a7080..9eeb5ac7 100644 --- a/crates/grand_slam/src/developer/qh/teams.rs +++ b/crates/grand_slam/src/developer/qh/teams.rs @@ -34,7 +34,7 @@ pub struct Team { pub name: String, pub team_id: String, #[serde(rename = "type")] - pub team_type: String, + pub _type: String, team_agent: Option, memberships: Vec, current_team_member: TeamMember, diff --git a/crates/grand_slam/src/utils/bundle.rs b/crates/grand_slam/src/utils/bundle.rs index 44fee577..d15dba79 100644 --- a/crates/grand_slam/src/utils/bundle.rs +++ b/crates/grand_slam/src/utils/bundle.rs @@ -181,6 +181,12 @@ impl PlistInfoTrait for Bundle { } } +impl Bundle { + pub fn is_sidestore(&self) -> bool { + matches!(self.get_bundle_identifier().as_deref(), Some("com.SideStore.SideStore")) + } +} + fn collect_embeded_bundles_from_dir(dir: &PathBuf) -> Result, Error> { let mut bundles = Vec::new(); diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs index efee6cdf..2ded897c 100644 --- a/crates/grand_slam/src/utils/provision.rs +++ b/crates/grand_slam/src/utils/provision.rs @@ -21,15 +21,27 @@ impl MobileProvision { return Err(Error::ProvisioningEntitlementsUnknown); } - let provisioning_plist = Self::extract_plist_from_file(path)?; + let profile_data = fs::read(path)?; + let provisioning_plist = Self::extract_plist_from_file(&profile_data)?; let entitlements = Self::extract_entitlements(&provisioning_plist)?; - + Ok(Self { provision_file: path.to_path_buf(), provisioning_plist, entitlements, }) } + + pub fn load_from_bytes(data: &[u8]) -> Result { + let provisioning_plist = Self::extract_plist_from_file(data)?; + let entitlements = Self::extract_entitlements(&provisioning_plist)?; + + Ok(Self { + provision_file: PathBuf::new(), + provisioning_plist, + entitlements, + }) + } pub fn file_path(&self) -> &Path { &self.provision_file @@ -118,8 +130,7 @@ impl MobileProvision { } } - fn extract_plist_from_file(path: &Path) -> Result { - let data = fs::read(path)?; + fn extract_plist_from_file(data: &[u8]) -> Result { let start = data.windows(6).position(|w| w == b"").ok_or(Error::ProvisioningEntitlementsUnknown)? + 8; let plist_data = &data[start..end]; From 2ce499ee2d03afa8f719c4c39d011a0906239193 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Fri, 14 Nov 2025 07:35:42 -0800 Subject: [PATCH 18/72] Update frame.rs --- apps/plumeimpactor/src/frame.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index cb93453e..eae3f2d2 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -40,7 +40,7 @@ impl PlumeFrame { let frame = Frame::builder() .with_title(APP_NAME) .with_size(Size::new(530, 410)) - .with_style(FrameStyle::CloseBox | FrameStyle::MinimizeBox) + .with_style(FrameStyle::CloseBox | FrameStyle::MinimizeBox | FrameStyle::Caption | FrameStyle::SystemMenu) .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); From 4d778851060577d2e115fd2404daa7bc2879e689 Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Fri, 14 Nov 2025 08:13:10 -0800 Subject: [PATCH 19/72] Update frame.rs --- apps/plumeimpactor/src/frame.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index eae3f2d2..646734c0 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -35,11 +35,16 @@ pub struct PlumeFrame { pub account_dialog: AccountDialog, } +#[cfg(target_os = "linux")] +const WINDOW_SIZE: (i32, i32) = (700, 660); +#[cfg(not(target_os = "linux"))] +const WINDOW_SIZE: (i32, i32) = (530, 410); + impl PlumeFrame { pub fn new() -> Self { let frame = Frame::builder() .with_title(APP_NAME) - .with_size(Size::new(530, 410)) + .with_size(Size::new(WINDOW_SIZE.0, WINDOW_SIZE.1)) .with_style(FrameStyle::CloseBox | FrameStyle::MinimizeBox | FrameStyle::Caption | FrameStyle::SystemMenu) .build(); From edc3d606cff5330edf5f35c7242082c21fca2d53 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 09:39:18 -0800 Subject: [PATCH 20/72] certs --- Cargo.lock | 100 +++++-- README.md | 10 +- apps/plumeimpactor/src/handlers.rs | 1 + crates/grand_slam/Cargo.toml | 11 +- crates/grand_slam/src/certificate/identity.rs | 268 ++++++++++++++++++ crates/grand_slam/src/certificate/mod.rs | 3 + crates/grand_slam/src/developer/qh/certs.rs | 63 +++- crates/grand_slam/src/lib.rs | 2 + crates/grand_slam/src/utils/macho.rs | 63 +++- crates/grand_slam/src/utils/provision.rs | 2 +- crates/ldid2/src/signing/signer.rs | 0 11 files changed, 473 insertions(+), 50 deletions(-) create mode 100644 crates/grand_slam/src/certificate/identity.rs create mode 100644 crates/ldid2/src/signing/signer.rs diff --git a/Cargo.lock b/Cargo.lock index 1c0d8ce1..e68d36e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,7 +181,7 @@ dependencies = [ "once_cell", "p12", "p256", - "pem", + "pem 3.0.6", "pkcs1", "pkcs8", "plist", @@ -190,7 +190,7 @@ dependencies = [ "rayon", "regex", "reqwest 0.12.23", - "ring", + "ring 0.17.14", "rsa", "scroll", "security-framework 2.11.1", @@ -447,6 +447,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -948,9 +954,9 @@ dependencies = [ "bytes", "chrono", "hex", - "pem", + "pem 3.0.6", "reqwest 0.12.23", - "ring", + "ring 0.17.14", "signature", "x509-certificate", ] @@ -1502,16 +1508,21 @@ dependencies = [ "base64 0.22.1", "botan", "cbc", + "hex", "hmac", "omnisette", "pbkdf2", - "pem", + "pem 3.0.6", + "pem-rfc7468", "plist", - "rand 0.9.2", + "rand 0.8.5", + "rcgen", "reqwest 0.11.27", + "rsa", "rustls 0.23.32", "serde", "serde_json", + "sha1", "sha2", "srp", "thiserror 2.0.16", @@ -2149,7 +2160,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin", + "spin 0.9.8", ] [[package]] @@ -2686,6 +2697,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "pem" version = "3.0.6" @@ -2937,7 +2957,7 @@ dependencies = [ "getrandom 0.3.3", "lru-slab", "rand 0.9.2", - "ring", + "ring 0.17.14", "rustc-hash", "rustls 0.23.32", "rustls-pki-types", @@ -3116,6 +3136,18 @@ dependencies = [ "cipher", ] +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem 1.1.1", + "ring 0.16.20", + "time", + "yasna", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -3280,6 +3312,21 @@ dependencies = [ "webpki-roots 1.0.3", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + [[package]] name = "ring" version = "0.17.14" @@ -3290,7 +3337,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.16", "libc", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -3364,7 +3411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring", + "ring 0.17.14", "rustls-webpki 0.101.7", "sct", ] @@ -3378,7 +3425,7 @@ dependencies = [ "aws-lc-rs", "log", "once_cell", - "ring", + "ring 0.17.14", "rustls-pki-types", "rustls-webpki 0.103.6", "subtle", @@ -3444,8 +3491,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.14", + "untrusted 0.9.0", ] [[package]] @@ -3455,9 +3502,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" dependencies = [ "aws-lc-rs", - "ring", + "ring 0.17.14", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -3531,8 +3578,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.14", + "untrusted 0.9.0", ] [[package]] @@ -3801,6 +3848,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -4360,6 +4413,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -5055,8 +5114,8 @@ dependencies = [ "chrono", "der", "hex", - "pem", - "ring", + "pem 3.0.6", + "ring 0.17.14", "signature", "spki", "thiserror 1.0.69", @@ -5098,6 +5157,9 @@ name = "yasna" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] [[package]] name = "yoke" diff --git a/README.md b/README.md index 40c926e3..85b44ba4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ [![GitHub License](https://img.shields.io/github/license/khcrysalis/PlumeImpactor?color=%23C96FAD)](https://github.com/khcrysalis/PlumeImpactor/blob/main/LICENSE) [![Sponsor Me](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/khcrysalis) -PlumeImpactor is an open-source alternative to tools like CydiaImpactor, Sideloadly, and AltStore. Made for sideloading to iOS / tvOS devices. +PlumeImpactor is an open-source, cross-platform, and feature rich iOS/tvOS sideloading application. Supporting macOS, Linux, and Windows. + +### Features +- User friendly and clean UI. +- Sign and install applications. ## Structure @@ -23,3 +27,7 @@ The project is seperated in multiple modules, all serve single or multiple uses - [Sideloader](https://github.com/Dadoum/Sideloader) – Apple Developer API references. - [idevice](https://github.com/jkcoxson/idevice) – Used for communication with `installd`, specifically for sideloading the apps to your devices. - [apple-codesign-rs](https://github.com/indygreg/apple-platform-rs) – Open-source alternative to codesign. + +## License + +Project is licensed under the MIT license. You can see the full details of the license [here](https://github.com/khcrysalis/PlumeImpactor/blob/main/LICENSE). diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index f676903c..1b3c6571 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -183,6 +183,7 @@ impl PlumeFrameMessageHandler { .show_elapsed_time() .show_estimated_time() .show_remaining_time() + .can_abort() .smooth() .build(); diff --git a/crates/grand_slam/Cargo.toml b/crates/grand_slam/Cargo.toml index ded0a19d..7cbea0ab 100644 --- a/crates/grand_slam/Cargo.toml +++ b/crates/grand_slam/Cargo.toml @@ -27,13 +27,20 @@ serde = { version = "1", features = ["derive"] } cbc = { version = "0.1.2", features = ["std"] } hmac = "0.12.1" pbkdf2 = "0.11" -sha2 = "0.10" -rand = "0.9" +sha2 = "0.10.9" +rand = "0.8.5" # cert needs this version instead of 0.9 apparently srp = "0.6.0" aes = "0.8.2" botan = "0.12.0" base64 = "0.22" +# certs +sha1 = "0.10.6" +rcgen = "0.9.3" +pem-rfc7468 = "0.7.0" +rsa = "0.9.8" +hex = "0.4.3" + [features] default = [] vendored-botan = ["botan/vendored"] diff --git a/crates/grand_slam/src/certificate/identity.rs b/crates/grand_slam/src/certificate/identity.rs new file mode 100644 index 00000000..072a7ac2 --- /dev/null +++ b/crates/grand_slam/src/certificate/identity.rs @@ -0,0 +1,268 @@ +use sha1::{Sha1, Digest}; +use hex; + +use rsa::{ + RsaPrivateKey, + RsaPublicKey, + pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey}, + pkcs8::{DecodePrivateKey, EncodePrivateKey}, +}; +use rand::rngs::OsRng; +use rcgen::{CertificateParams, Certificate, KeyPair, DnType, PKCS_RSA_SHA256}; +use x509_certificate::X509Certificate; + +use pem_rfc7468::{encode_string, LineEnding}; + +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use crate::Error; +use crate::developer::{DeveloperSession}; + +#[derive(Debug, Clone)] +pub struct CertificateIdentity { + pub certificate: Option, + pub private_key: RsaPrivateKey, + pub key_file: PathBuf, + pub cert_file: PathBuf, + pub machine_name: String, +} + +impl CertificateIdentity { + pub async fn new( + configuration_path: &Path, + dev_session: &DeveloperSession, + apple_id: String, + machine_name: String, + team: &str, + ) -> Result { + let mut hasher = Sha1::new(); + hasher.update(apple_id.as_bytes()); + let hash_string = hex::encode(hasher.finalize()).to_lowercase(); + + let key_path = configuration_path.join("keys").join(hash_string); + fs::create_dir_all(&key_path)?; + + let key_file = key_path.join("key.pem"); + let cert_file = key_path.join("cert.pem"); + + // --- Load or generate key --- + let private_key = if key_file.exists() { + let pem = fs::read_to_string(&key_file) + .map_err(|e| Error::Certificate(format!("Failed to read key: {e}")))?; + RsaPrivateKey::from_pkcs8_pem(&pem) + .map_err(|e| Error::Certificate(format!("Failed to parse private key: {e}")))? + } else { + let mut rng = OsRng; + let key = RsaPrivateKey::new(&mut rng, 2048) + .map_err(|e| Error::Certificate(format!("Failed to generate key: {e}")))?; + + let pem = key + .to_pkcs8_pem(Default::default()) + .map_err(|e| Error::Certificate(format!("Failed to encode key: {e}")))? + .to_string(); + + fs::write(&key_file, pem)?; + + key + }; + + let mut ci = CertificateIdentity { + certificate: None, + private_key, + key_file, + cert_file, + machine_name, + }; + + // --- Try to find existing certificate --- + if let Ok(cert) = ci.find_matching_certificate(dev_session, team).await { + let pem = encode_string( + "CERTIFICATE", + LineEnding::LF, + cert.encode_der().map_err(|e| Error::Certificate(format!("{e}")))?.as_slice(), + ).unwrap(); + + fs::write(&ci.cert_file, pem)?; + + ci.certificate = Some(cert); + return Ok(ci); + } + + // --- Request new certificate --- + ci.request_new_certificate(dev_session, team).await?; + + Ok(ci) + } + + async fn find_matching_certificate( + &self, + dev_session: &DeveloperSession, + team: &str, + ) -> Result { + let certs = dev_session + .qh_list_certs(team) + .await? + .certificates; + // Our RSA public key (PKCS#1 DER) + let our_pub_pkcs1_der = self.private_key + .to_public_key() + .to_pkcs1_der() + .map_err(|e| Error::Certificate(format!("Failed to encode public key (pkcs1): {e}")))? + .as_bytes() + .to_vec(); + + for cert_meta in certs.iter().filter(|c| c.machine_name == Some(self.machine_name.clone())) { + if let Ok(cert) = X509Certificate::from_der(&cert_meta.cert_content) { + // Extract BIT STRING containing PKCS#1 public key + let bit_string = &cert.tbs_certificate().subject_public_key_info.subject_public_key; + let raw = bit_string.octet_slice().unwrap_or_default(); + if raw.is_empty() { + continue; + } + // First byte is number of unused bits (should be 0 for public key bit strings) + let unused_bits = raw[0]; + if unused_bits != 0 { + continue; + } + let pkcs1_bytes = &raw[1..]; + if let Ok(cert_pub) = RsaPublicKey::from_pkcs1_der(pkcs1_bytes) { + let cert_pub_pkcs1_der = cert_pub + .to_pkcs1_der() + .map_err(|e| Error::Certificate(format!("Failed to re-encode cert public key: {e}")))? + .as_bytes() + .to_vec(); + if cert_pub_pkcs1_der == our_pub_pkcs1_der { + return Ok(cert); + } + } + } + } + + Err(Error::Certificate("No matching certificate found".into())) + } + + async fn request_new_certificate( + &mut self, + dev_session: &DeveloperSession, + team: &str, + ) -> Result<(), Error> { + // Convert RSA private key → PKCS8 DER → rcgen KeyPair + let pkcs8 = self.private_key + .to_pkcs8_der() + .map_err(|e| Error::Certificate(format!("Failed to encode pkcs8: {e}")))?; + + let keypair = KeyPair::from_der(pkcs8.as_bytes()) + .map_err(|e| Error::Certificate(format!("Failed to load rcgen key: {e}")))?; + + // --- Build CSR --- + let mut params = CertificateParams::new(vec![]); + // Use an RSA signature algorithm to match the RSA key pair + params.alg = &PKCS_RSA_SHA256; + params.key_pair = Some(keypair); + + let dn = &mut params.distinguished_name; + dn.push(DnType::CountryName, "US"); + dn.push(DnType::StateOrProvinceName, "STATE"); + dn.push(DnType::LocalityName, "LOCAL"); + dn.push(DnType::OrganizationName, "ORGNIZATION"); + dn.push(DnType::CommonName, "CN"); + + let csr = Certificate::from_params(params) + .map_err(|e| Error::Certificate(format!("Failed to build CSR params: {e}")))?; + + let csr_pem = csr.serialize_request_pem() + .map_err(|e| Error::Certificate(format!("Failed to serialize CSR: {e}")))?; + + let cert_id = loop { + match dev_session + .qh_submit_cert_csr( + team, + csr_pem.clone(), + &self.machine_name.clone(), + ) + .await + { + Ok(id) => break id, + Err(e) => { + if let Error::DeveloperSession(code, _) = &e { + if *code == 7460 { + let certs = dev_session + .qh_list_certs(team) + .await? + .certificates; + + if let Some(target) = certs.iter().find(|c| c.machine_name.as_deref() == Some(self.machine_name.as_ref())) { + let cid = target.serial_number.clone(); + dev_session + .qh_revoke_cert(team, &cid) + .await + .map_err(|err| { + Error::Certificate(format!( + "Failed to revoke certificate {cid}: {err:?}" + )) + })?; + // Retry submit after revocation + continue; + } else { + return Err(Error::Certificate( + "Too many certificates".into(), + )); + } + } + } + return Err(Error::Certificate(format!("Submit CSR failed: {:?}", e))); + } + } + }; + + // --- Fetch new certificate from Apple --- + let certs = dev_session + .qh_list_certs(team) + .await?.certificates; + + let found = certs + .iter() + .find(|c| c.certificate_id == cert_id.cert_request.certificate_id) + .ok_or_else(|| Error::Certificate("Certificate not found after submission".into()))?; + + let parsed = X509Certificate::from_der(&found.cert_content) + .map_err(|e| Error::Certificate(format!("Failed to parse DER: {e}")))?; + + // Save PEM + let pem = encode_string( + "CERTIFICATE", + LineEnding::LF, + found.cert_content.as_ref(), + ).unwrap(); + + fs::write(&self.cert_file, pem)?; + + self.certificate = Some(parsed); + + Ok(()) + } + + pub fn get_certificate_file_path(&self) -> &Path { + &self.cert_file + } + + pub fn get_private_key_file_path(&self) -> &Path { + &self.key_file + } + + pub fn get_serial_number(&self) -> Result { + let cert = self.certificate.as_ref() + .ok_or_else(|| Error::Certificate("No certificate loaded".into()))?; + + let serial = cert + .tbs_certificate() + .serial_number + .clone() + .into_bytes(); + + Ok(hex::encode(serial).trim_start_matches('0').to_string()) + } +} diff --git a/crates/grand_slam/src/certificate/mod.rs b/crates/grand_slam/src/certificate/mod.rs index e69de29b..9a7fd750 100644 --- a/crates/grand_slam/src/certificate/mod.rs +++ b/crates/grand_slam/src/certificate/mod.rs @@ -0,0 +1,3 @@ +mod identity; + +pub use identity::CertificateIdentity; diff --git a/crates/grand_slam/src/developer/qh/certs.rs b/crates/grand_slam/src/developer/qh/certs.rs index 12fa2962..06650d81 100644 --- a/crates/grand_slam/src/developer/qh/certs.rs +++ b/crates/grand_slam/src/developer/qh/certs.rs @@ -1,5 +1,6 @@ use serde::Deserialize; use plist::{Data, Date, Dictionary, Integer, Value}; +use uuid::Uuid; use crate::Error; @@ -32,22 +33,20 @@ impl DeveloperSession { Ok(response_data) } - // pub async fn qh_submit_cert_csr(&self, team_id: &str, csr_data: &[u8], machine_name: &str) -> Result { - // let endpoint = developer_endpoint!("/QH65B2/ios/submitDevelopmentCSR.action"); + pub async fn qh_submit_cert_csr(&self, team_id: &str, csr_data: String, machine_name: &str) -> Result { + let endpoint = developer_endpoint!("/QH65B2/ios/submitDevelopmentCSR.action"); - // let mut body = Dictionary::new(); - // body.insert("teamId".to_string(), Value::String(team_id.to_string())); - // body.insert("csrContent".to_string(), Value::Data(Data::from(csr_data.to_vec()))); - // body.insert("machineId".to_string(), Value::String(Uuid::new_v4().to_string().to_uppercase())); - // body.insert("machineName".to_string(), Value::String(machine_name.to_string())); + let mut body = Dictionary::new(); + body.insert("teamId".to_string(), Value::String(team_id.to_string())); + body.insert("csrContent".to_string(), Value::String(csr_data)); + body.insert("machineId".to_string(), Value::String(Uuid::new_v4().to_string().to_uppercase())); + body.insert("machineName".to_string(), Value::String(machine_name.to_string())); - // let response = self.send_request(&endpoint, Some(body)).await?; - // println!("{:#?}", response); - // // let response_data: Cert = plist::from_value(&Value::Dictionary(response))?; + let response = self.qh_send_request(&endpoint, Some(body)).await?; + let response_data: CsrResponse = plist::from_value(&Value::Dictionary(response))?; - // // Ok(response_data) - // todo!("Implement CSR submission") - // } + Ok(response_data) + } } #[allow(dead_code)] @@ -59,6 +58,15 @@ pub struct CertsResponse { pub meta: ResponseMeta, } +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct CsrResponse { + pub cert_request: Csr, + #[serde(flatten)] + pub meta: ResponseMeta, +} + #[allow(dead_code)] #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -73,7 +81,34 @@ pub struct Cert { pub cert_type: Option, pub cert_content: Data, machine_id: Option, - machine_name: Option, + pub machine_name: Option, +} + +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Csr { + cert_request_id: String, + name: String, + status_code: Integer, + status_string: String, + csr_platform: String, + date_requested_string: String, + date_requested: Date, + date_created: Date, + owner_type: String, + owner_name: String, + owner_id: String, + pub certificate_id: String, + certificate_status_code: Integer, + cert_request_status_code: Integer, + certificate_type_display_id: String, + serial_num: String, + serial_num_decimal: String, + type_string: String, + pub certificate_type: Option, + machine_id: Option, + pub machine_name: Option, } #[allow(dead_code)] diff --git a/crates/grand_slam/src/lib.rs b/crates/grand_slam/src/lib.rs index 7ca15089..9f4fc525 100644 --- a/crates/grand_slam/src/lib.rs +++ b/crates/grand_slam/src/lib.rs @@ -35,6 +35,8 @@ pub enum Error { #[error("Missing certificate PEM data")] CertificatePemMissing, + #[error("Certificate error: {0}")] + Certificate(String), #[error("Developer session error {0}: {1}")] DeveloperSession(i64, String), diff --git a/crates/grand_slam/src/utils/macho.rs b/crates/grand_slam/src/utils/macho.rs index 9933b644..2aa475c9 100644 --- a/crates/grand_slam/src/utils/macho.rs +++ b/crates/grand_slam/src/utils/macho.rs @@ -1,29 +1,34 @@ -use std::fs; -use std::path::PathBuf; +use std::{collections::HashSet, fs}; +use std::path::Path; use apple_codesign::MachFile; use plist::{Dictionary, Value}; -use crate::Error; - -pub struct MachO<'a> { - macho_file: MachFile<'a>, +use crate::{Error, developer::v1::capabilities::Capability}; + +/// Represents a Mach-O file and its entitlements. +pub struct MachO { + macho_file: MachFile<'static>, + pub entitlements: Option, } -impl<'a> MachO<'a> { - pub fn new(path: impl Into) -> Result { - let path = path.into(); - let macho_data = fs::read(&path)?; +impl MachO { + pub fn new>(path: P) -> Result { + let macho_data = fs::read(path)?; + // Leak the data for 'static lifetime required by MachFile. let macho_data = Box::leak(macho_data.into_boxed_slice()); let macho_file = MachFile::parse(macho_data)?; + let entitlements = Self::extract_entitlements(&macho_file)?; Ok(MachO { macho_file, + entitlements, }) } - - pub fn entitlements(&self) -> Result, Error> { - let macho = self.macho_file.nth_macho(0)?; + + fn extract_entitlements(macho_file: &MachFile<'_>) -> Result, Error> { + let macho = macho_file.nth_macho(0)?; + if let Some(embedded_sig) = macho.code_signature()? { if let Ok(Some(slot)) = embedded_sig.entitlements() { let value = Value::from_reader_xml(slot.to_string().as_bytes())?; @@ -32,6 +37,38 @@ impl<'a> MachO<'a> { } } } + Ok(None) } + + pub fn app_groups_for_entitlements(&self) -> Option> { + self.entitlements + .as_ref() + .and_then(|e| e.get("com.apple.security.application-groups")?.as_array()) + .map(|arr| arr.iter().filter_map(|v| v.as_string().map(|s| s.to_string())).collect()) + } + + pub fn capabilities_for_entitlements(&self, capabilities: &[Capability]) -> Option> { + let entitlements = self.entitlements.as_ref()?; + let ent_keys: HashSet<_> = entitlements.keys().collect(); + + let capabilities_to_enable: Vec = capabilities + .iter() + .filter_map(|cap| { + cap.attributes.entitlements.as_ref().and_then(|ent_list| { + if ent_list.iter().any(|e| ent_keys.contains(&e.profile_key)) { + Some(cap.id.clone()) + } else { + None + } + }) + }) + .collect(); + + if capabilities_to_enable.is_empty() { + None + } else { + Some(capabilities_to_enable) + } + } } diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs index 2ded897c..e944c539 100644 --- a/crates/grand_slam/src/utils/provision.rs +++ b/crates/grand_slam/src/utils/provision.rs @@ -75,7 +75,7 @@ impl MobileProvision { pub fn merge_entitlements(&mut self, binary_path: PathBuf) -> Result<(), Error> { let macho = MachO::new(&binary_path)?; - let binary_entitlements = macho.entitlements()?.ok_or(Error::ProvisioningEntitlementsUnknown)?; + let binary_entitlements = macho.entitlements.ok_or(Error::ProvisioningEntitlementsUnknown)?; if let Some(Value::Array(other_groups)) = binary_entitlements.get("keychain-access-groups") { self.entitlements.insert("keychain-access-groups".to_string(), Value::Array(other_groups.clone())); diff --git a/crates/ldid2/src/signing/signer.rs b/crates/ldid2/src/signing/signer.rs new file mode 100644 index 00000000..e69de29b From 18d3378b89898f3d3fc408b4348a1c34df3ae0d5 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 09:39:48 -0800 Subject: [PATCH 21/72] Update frame.rs --- apps/plumeimpactor/src/frame.rs | 117 +++++++++++++++----------------- 1 file changed, 54 insertions(+), 63 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 646734c0..26c82082 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -1,8 +1,9 @@ use std::cell::RefCell; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::rc::Rc; use std::{env, fs, ptr, thread}; +use grand_slam::certificate::CertificateIdentity; use grand_slam::{AnisetteConfiguration, BundleType, MachO, MobileProvision}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; @@ -320,9 +321,11 @@ impl PlumeFrame { sender_clone.send(PlumeFrameMessage::InstallProgress(10, Some("Ensuring current device is registered...".to_string()))).ok(); - let mut usbmuxd = UsbmuxdConnection::default().await + let mut usbmuxd = UsbmuxdConnection::default() + .await .map_err(|e| format!("usbmuxd connect error: {e}"))?; - let usbmuxd_device = usbmuxd.get_devices().await + let usbmuxd_device = usbmuxd.get_devices() + .await .map_err(|e| format!("usbmuxd device list error: {e}"))? .into_iter() .find(|d| d.device_id.to_string() == device_id) @@ -331,15 +334,19 @@ impl PlumeFrame { let device = Device::new(usbmuxd_device).await; // TODO: Handle multiple teams properly - let teams = session.qh_list_teams().await.map_err(|e| format!("Failed to list teams: {}", e))?; + let teams = session.qh_list_teams() + .await + .map_err(|e| format!("Failed to list teams: {}", e))?; session.qh_ensure_device( - &teams.teams.get(0) - .ok_or("No teams available for the Apple ID account.")? - .team_id, + &teams.teams.get(0).ok_or("No teams available for the Apple ID account.")?.team_id, &device.name, &device.uuid, - ).await.map_err(|e| format!("Failed to ensure device is registered: {}", e))?; + ) + .await + .map_err(|e| format!("Failed to ensure device is registered: {}", e))?; + + sender_clone.send(PlumeFrameMessage::InstallProgress(20, Some("Extracting package...".to_string()))).ok(); let bundle = package.get_package_bundle() .map_err(|e| format!("Failed to get package bundle: {}", e))?; @@ -367,9 +374,12 @@ impl PlumeFrame { ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; } } - + + sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); + let mut provisionings: Vec = Vec::new(); - + + // TODO: handle requests on seperate threads to speed this up for bundle in &bundles { if bundle._type != BundleType::AppExtension && @@ -378,11 +388,6 @@ impl PlumeFrame { continue; } - sender_clone.send(PlumeFrameMessage::InstallProgress( - 20, - Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())) - )).ok(); - let bundle_executable_name = bundle.get_executable() .ok_or("Failed to get executable from bundle.")?; @@ -391,71 +396,45 @@ impl PlumeFrame { let macho = MachO::new(&bundle_executable_path) .map_err(|e| format!("Failed to read Mach-O binary: {}", e))?; - let macho_entitlements = macho.entitlements() - .map_err(|e| format!("Failed to get entitlements from Mach-O binary: {}", e))?; - let id = bundle.get_bundle_identifier() .ok_or("Failed to get bundle identifier from bundle.")?; - - let app_groups: Vec = macho_entitlements - .as_ref() - .and_then(|dict| dict.get("com.apple.security.application-groups")) - .and_then(|val| val.as_array()) - .map(|arr| { - arr.iter() - .filter_map(|v| v.as_string().map(|s| format!("{}.{}", s, team_id))) - .collect() - }) - .unwrap_or_else(Vec::new); + + println!("{}", id); session.qh_ensure_app_id(team_id, &bundle.get_name().unwrap_or_default(), &id) .await .map_err(|e| format!("Failed to ensure app ID: {}", e))?; - let capabilities = session.v1_list_capabilities(team_id).await + let capabilities = session.v1_list_capabilities(team_id) + .await .map_err(|e| format!("Failed to list capabilities: {}", e))?; - - println!("Mach-O Entitlements: {:?}", &macho_entitlements); - - let mut capabilities_to_enable = Vec::new(); - - if let Some(entitlements) = &macho_entitlements { - for (ent_key, _) in entitlements { - for cap in &capabilities.data { - if let Some(ent_list) = &cap.attributes.entitlements { - if ent_list.iter().any(|e| e.profile_key == *ent_key) { - capabilities_to_enable.push(cap.id.clone()); - } - } - } - } - } - - println!("Enabling capabilities: {:?}", &capabilities_to_enable); - let app_id_id = session.qh_get_app_id(team_id, &id).await + let app_id_id = session.qh_get_app_id(team_id, &id) + .await .map_err(|e| e.to_string())? .ok_or("Failed to get ensured app ID.")?; - if !capabilities_to_enable.is_empty() { - session.v1_update_app_id(team_id, &id, capabilities_to_enable) + if let Some(caps) = macho.capabilities_for_entitlements(&capabilities.data) { + session.v1_update_app_id(team_id, &id, caps) .await .map_err(|e| format!("Failed to enable capabilities: {}", e))?; } - for group in &app_groups { - let group_id = session.qh_ensure_app_group(team_id, group, group) - .await - .map_err(|e| format!("Failed to ensure app group: {}", e))?; - - println!("{:#?}", group_id); - - session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) - .await - .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + if let Some(app_groups) = macho.app_groups_for_entitlements() { + for group in &app_groups { + let group = format!("{group}.{team_id}"); + let group_id = session.qh_ensure_app_group(team_id, &group, &group) + .await + .map_err(|e| format!("Failed to ensure app group: {}", e))?; + + session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) + .await + .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + } } - let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id).await + let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id) + .await .map_err(|e| format!("Failed to list profiles: {}", e))?; let profile_data = profiles.provisioning_profile.encoded_profile; @@ -466,8 +445,20 @@ impl PlumeFrame { provisionings.push(mobile_provision); } - sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some("Downloading Certificates...".to_string()))).ok(); + sender_clone.send(PlumeFrameMessage::InstallProgress(40, Some("Downloading Certificates...".to_string()))).ok(); + let cert = CertificateIdentity::new( + &PathBuf::from("/tmp"), + &session, + "PLUME".to_string(), + "AltStore".to_string(), + team_id + ) + .await + .map_err(|e| format!("Failed to create certificate identity: {}", e))?; + + println!("Using cert: {}", cert.get_serial_number().unwrap_or_default()); + Ok::<_, String>(()) }); From 644cad28488363ef378c0f2fbdde2536a4d10d23 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 09:49:47 -0800 Subject: [PATCH 22/72] WHY WERE YOU HERE --- apps/plumeimpactor/src/frame.rs | 2 +- crates/ldid2/src/signing/signer.rs | 0 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 crates/ldid2/src/signing/signer.rs diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 26c82082..a2335cb7 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -445,7 +445,7 @@ impl PlumeFrame { provisionings.push(mobile_provision); } - sender_clone.send(PlumeFrameMessage::InstallProgress(40, Some("Downloading Certificates...".to_string()))).ok(); + sender_clone.send(PlumeFrameMessage::InstallProgress(40, Some("Obtaining Certificates...".to_string()))).ok(); let cert = CertificateIdentity::new( &PathBuf::from("/tmp"), diff --git a/crates/ldid2/src/signing/signer.rs b/crates/ldid2/src/signing/signer.rs deleted file mode 100644 index e69de29b..00000000 From 7fb3313bd3263bda0c6eaf70130dbf11e17f3331 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 13:51:37 -0800 Subject: [PATCH 23/72] some fixes --- .vscode/extensions.json | 5 +++ apps/plumeimpactor/src/frame.rs | 55 +++++++++++++++++------- apps/plumeimpactor/src/utils/package.rs | 10 ++--- apps/plumesign/src/main.rs | 2 +- crates/grand_slam/src/utils/provision.rs | 9 +++- crates/grand_slam/src/utils/signer.rs | 11 ++++- 6 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..b85de749 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "rust-lang.rust-analyzer" + ] +} diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index a2335cb7..319e0697 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -4,10 +4,10 @@ use std::rc::Rc; use std::{env, fs, ptr, thread}; use grand_slam::certificate::CertificateIdentity; -use grand_slam::{AnisetteConfiguration, BundleType, MachO, MobileProvision}; +use grand_slam::{AnisetteConfiguration, BundleType, Certificate, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; -use grand_slam::utils::PlistInfoTrait; +use grand_slam::utils::{PlistInfoTrait, SignerSettings}; use idevice::IdeviceService; use idevice::lockdown::LockdownClient; use wxdragon::prelude::*; @@ -338,7 +338,7 @@ impl PlumeFrame { .await .map_err(|e| format!("Failed to list teams: {}", e))?; - session.qh_ensure_device( + let device = session.qh_ensure_device( &teams.teams.get(0).ok_or("No teams available for the Apple ID account.")?.team_id, &device.name, &device.uuid, @@ -346,6 +346,8 @@ impl PlumeFrame { .await .map_err(|e| format!("Failed to ensure device is registered: {}", e))?; + println!("Device ensured: {:#?}", device); + sender_clone.send(PlumeFrameMessage::InstallProgress(20, Some("Extracting package...".to_string()))).ok(); let bundle = package.get_package_bundle() @@ -377,6 +379,16 @@ impl PlumeFrame { sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); + let identity = CertificateIdentity::new( + &PathBuf::from("/tmp"), + &session, + "PLUME".to_string(), + "AltStore".to_string(), + team_id + ) + .await + .map_err(|e| format!("Failed to create certificate identity: {}", e))?; + let mut provisionings: Vec = Vec::new(); // TODO: handle requests on seperate threads to speed this up @@ -444,20 +456,33 @@ impl PlumeFrame { provisionings.push(mobile_provision); } + + sender_clone.send(PlumeFrameMessage::InstallProgress(50, Some(format!("Signing {}...", bundle.get_name().unwrap_or_default())))).ok(); + + let certificate_paths = vec![ + identity.get_certificate_file_path().to_path_buf(), + identity.get_private_key_file_path().to_path_buf(), + ]; + + let certificate = Certificate::new(Some(certificate_paths)) + .map_err(|e| format!("Failed to create Certificate: {}", e))?; + + let signer_settings = SignerSettings { + ..Default::default() + }; + + let signer = Signer::new( + Some(certificate), + signer_settings, + provisionings, + ); + + signer.sign_bundle(&bundle) + .map_err(|e| format!("Failed to sign bundle: {}", e))?; + + sender_clone.send(PlumeFrameMessage::InstallProgress(70, Some("Installing to device...".to_string()))).ok(); - sender_clone.send(PlumeFrameMessage::InstallProgress(40, Some("Obtaining Certificates...".to_string()))).ok(); - let cert = CertificateIdentity::new( - &PathBuf::from("/tmp"), - &session, - "PLUME".to_string(), - "AltStore".to_string(), - team_id - ) - .await - .map_err(|e| format!("Failed to create certificate identity: {}", e))?; - - println!("Using cert: {}", cert.get_serial_number().unwrap_or_default()); Ok::<_, String>(()) }); diff --git a/apps/plumeimpactor/src/utils/package.rs b/apps/plumeimpactor/src/utils/package.rs index af4cbb49..757f7e8c 100644 --- a/apps/plumeimpactor/src/utils/package.rs +++ b/apps/plumeimpactor/src/utils/package.rs @@ -101,8 +101,8 @@ impl PlistInfoTrait for Package { } } -impl Drop for Package { - fn drop(&mut self) { - fs::remove_dir_all(&self.stage_dir).ok(); - } -} +// impl Drop for Package { +// fn drop(&mut self) { +// fs::remove_dir_all(&self.stage_dir).ok(); +// } +// } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index e1ec4948..8edbf54b 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -80,7 +80,7 @@ async fn main() { let signer = Signer::new(Some(signing_key), signer_settings, provisioning_files); let target_path = args.bundle.clone(); - if let Err(e) = signer.sign(target_path.clone()) { + if let Err(e) = signer.sign_path(target_path.clone()) { eprintln!("--x failed to sign: {e}"); exit(1); } diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs index e944c539..7a683fc2 100644 --- a/crates/grand_slam/src/utils/provision.rs +++ b/crates/grand_slam/src/utils/provision.rs @@ -2,6 +2,7 @@ use std::fs; use std::path::{Path, PathBuf}; use plist::{Dictionary, Value}; +use uuid::Uuid; use crate::Error; use super::MachO; @@ -32,12 +33,18 @@ impl MobileProvision { }) } + // TODO: make this better.... pub fn load_from_bytes(data: &[u8]) -> Result { let provisioning_plist = Self::extract_plist_from_file(data)?; let entitlements = Self::extract_entitlements(&provisioning_plist)?; + + let temp_file_path = std::env::temp_dir().join(format!("plume_provision_{:08}", Uuid::new_v4().to_string().to_uppercase())); + fs::create_dir_all(&temp_file_path)?; + let provision_file = temp_file_path.join(format!("{}.mobileprovision", Uuid::new_v4().to_string().to_uppercase())); + fs::write(&provision_file, data)?; Ok(Self { - provision_file: PathBuf::new(), + provision_file, provisioning_plist, entitlements, }) diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs index 03069a71..3c917992 100644 --- a/crates/grand_slam/src/utils/signer.rs +++ b/crates/grand_slam/src/utils/signer.rs @@ -28,8 +28,12 @@ impl Signer { } } - pub fn sign(&self, path: PathBuf) -> Result<(), Error> { - let bundle = Bundle::new(path.clone())?; + pub fn sign_path(&self, path: PathBuf) -> Result<(), Error> { + let bundle = Bundle::new(path)?; + self.sign_bundle(&bundle) + } + + pub fn sign_bundle(&self, bundle: &Bundle) -> Result<(), Error> { let bundles = bundle.collect_bundles_sorted()?; if let Some(new_name) = self.settings.custom_name.as_ref() { @@ -88,6 +92,8 @@ impl Signer { )?; } } + + println!("Signing {}...", bundle.dir().to_string_lossy()); UnifiedSigner::new(settings).sign_path_in_place(bundle.dir())?; } @@ -109,6 +115,7 @@ impl Signer { } settings.set_for_notarization(false); settings.set_shallow(false); + settings.set_team_id_from_signing_certificate(); Ok(settings) } } From 2d54c1c725d478bd7490961bd83638121ef6f224 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 14:08:41 -0800 Subject: [PATCH 24/72] more actions --- .github/workflows/build.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e13f8120..51d5d5ea 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: "Build" +name: "Build & Bundle" on: push: branches: @@ -54,16 +54,28 @@ jobs: if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 - - name: Build + - name: Install cargo-bundle + run: cargo install cargo-bundle + + - name: Build & Bundle run: | cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force - cargo build --bin plumeimpactor --release + + if [[ "${{ runner.os }}" == "Linux" ]]; then + cargo bundle --bin plumeimpactor --package plumeimpactor --format rpm + cargo bundle --bin plumeimpactor --package plumeimpactor --format deb + cargo bundle --bin plumeimpactor --package plumeimpactor --format appimage + elif [[ "${{ runner.os }}" == "macOS" ]]; then + cargo bundle --bin plumeimpactor --package plumeimpactor --format dmg + elif [[ "${{ runner.os }}" == "Windows" ]]; then + cargo bundle --bin plumeimpactor --package plumeimpactor --format exe + fi - - name: Upload Build Artifacts + - name: Upload Bundles uses: actions/upload-artifact@v4 with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/plumeimpactor* + target/bundle/*/*.{rpm,deb,AppImage,dmg,exe} From 7057d5c91c04834e900a08cc94b221a0e6b1e516 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 14:12:47 -0800 Subject: [PATCH 25/72] remove this stuff --- .vscode/extensions.json | 3 +- .../PlumeImpactor.app/Contents/Info.plist | 46 ------------------- 2 files changed, 2 insertions(+), 47 deletions(-) delete mode 100644 package/macos/PlumeImpactor.app/Contents/Info.plist diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b85de749..d0bb9c36 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "rust-lang.rust-analyzer" + "rust-lang.rust-analyzer", + "editorconfig.editorconfig" ] } diff --git a/package/macos/PlumeImpactor.app/Contents/Info.plist b/package/macos/PlumeImpactor.app/Contents/Info.plist deleted file mode 100644 index c8f2b36b..00000000 --- a/package/macos/PlumeImpactor.app/Contents/Info.plist +++ /dev/null @@ -1,46 +0,0 @@ - - - - - BuildMachineOSBuild - 23A344011 - CFBundleDevelopmentRegion - en - CFBundleDisplayName - Plume Impactor - CFBundleExecutable - plumeimpactor - CFBundleIconFile - AppIcon - CFBundleIconName - AppIcon - CFBundleIdentifier - com.feather.impactor - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Plume Impactor - CFBundlePackageType - APPL - CFBundleSupportedPlatforms - - MacOSX - - DTCompiler - com.apple.compilers.llvm.clang.1_0 - DTPlatformName - macosx - DTPlatformVersion - 15.6 - DTSDKBuild - 24F62 - DTSDKName - macosx15.5.internal - DTXcode - 1630 - DTXcodeBuild - 16E6052g - LSMinimumSystemVersion - 10.8 - - From 16cadc4964d353deacaaf80546f3ef64b8788aff Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 14:12:58 -0800 Subject: [PATCH 26/72] this too --- Makefile | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 62b0fbba..00000000 --- a/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -.PHONY: macos clean - - -macos: -# env /usr/bin/arch -x86_64 cargo build --bin plumeimpactor --release --target x86_64-apple-darwin - cargo build --bin plumeimpactor --release --target aarch64-apple-darwin - - mkdir -p build/macos - cp -R package/macos/PlumeImpactor.app build/macos/PlumeImpactor.app -# lipo -create -output build/macos/PlumeImpactor.app/Contents/MacOS/plumeimpactor \ -# target/x86_64-apple-darwin/release/plumeimpactor \ -# target/aarch64-apple-darwin/release/plumeimpactor - cp target/aarch64-apple-darwin/release/plumeimpactor build/macos/PlumeImpactor.app/Contents/MacOS/plumeimpactor - -clean: - rm -rf build/ From b87523dcaa055c0145b636932d01519e6c94a993 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 14:20:54 -0800 Subject: [PATCH 27/72] asd --- .github/workflows/build.yml | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 51d5d5ea..ca3451c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,24 +54,29 @@ jobs: if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 - - name: Install cargo-bundle - run: cargo install cargo-bundle - - - name: Build & Bundle + - name: Build Dependencies run: | cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force - - if [[ "${{ runner.os }}" == "Linux" ]]; then - cargo bundle --bin plumeimpactor --package plumeimpactor --format rpm - cargo bundle --bin plumeimpactor --package plumeimpactor --format deb - cargo bundle --bin plumeimpactor --package plumeimpactor --format appimage - elif [[ "${{ runner.os }}" == "macOS" ]]; then - cargo bundle --bin plumeimpactor --package plumeimpactor --format dmg - elif [[ "${{ runner.os }}" == "Windows" ]]; then - cargo bundle --bin plumeimpactor --package plumeimpactor --format exe - fi + cargo install cargo-bundle + + - name: Build & Bundle (Linux) + if: runner.os == 'Linux' + run: | + cargo bundle --bin plumeimpactor --package plumeimpactor --format rpm + cargo bundle --bin plumeimpactor --package plumeimpactor --format deb + cargo bundle --bin plumeimpactor --package plumeimpactor --format appimage + + - name: Build & Bundle (macOS) + if: runner.os == 'macOS' + run: | + cargo bundle --bin plumeimpactor --package plumeimpactor --format dmg + + - name: Build & Bundle (Windows) + if: runner.os == 'Windows' + run: | + cargo bundle --bin plumeimpactor --package plumeimpactor --format exe - name: Upload Bundles uses: actions/upload-artifact@v4 From 31826e6a71deceb14d3799a98920304009631a9b Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 14:49:41 -0800 Subject: [PATCH 28/72] Update build.yml --- .github/workflows/build.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca3451c2..8290978d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,23 +64,25 @@ jobs: - name: Build & Bundle (Linux) if: runner.os == 'Linux' run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --format rpm - cargo bundle --bin plumeimpactor --package plumeimpactor --format deb - cargo bundle --bin plumeimpactor --package plumeimpactor --format appimage + cargo bundle --bin plumeimpactor --package plumeimpactor --release --format appimage - name: Build & Bundle (macOS) if: runner.os == 'macOS' run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --format dmg + cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx + mkdir build + cp -R target/release/bundle/*/*.app build/ + ln -s /Applications build/Applications + hdiutil create -volname plumeimpactor -srcfolder build -ov -format UDZO target/release/bundle/plumeimpactor.dmg - name: Build & Bundle (Windows) if: runner.os == 'Windows' run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --format exe + cargo bundle --bin plumeimpactor --package plumeimpactor --release --format msi - name: Upload Bundles uses: actions/upload-artifact@v4 with: name: plumeimpactor-${{ matrix.platform }} path: | - target/bundle/*/*.{rpm,deb,AppImage,dmg,exe} + target/release/bundle/*/*.{rpm,deb,AppImage,dmg,exe,msi} From 89ce3fb934fdc09a26fc92bef5799a49aa8fd621 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 15:12:43 -0800 Subject: [PATCH 29/72] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8290978d..262ed60a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,4 +85,4 @@ jobs: with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/bundle/*/*.{rpm,deb,AppImage,dmg,exe,msi} + target/release/bundle/*.{rpm,deb,AppImage,dmg,exe,msi} From 9c862e3f4817615320a464875766f1985621a617 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 15:22:54 -0800 Subject: [PATCH 30/72] fix again --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 262ed60a..8290978d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,4 +85,4 @@ jobs: with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/bundle/*.{rpm,deb,AppImage,dmg,exe,msi} + target/release/bundle/*/*.{rpm,deb,AppImage,dmg,exe,msi} From 27b168854493ec3f4152af4871fc3ee6a9726b5c Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 15:35:48 -0800 Subject: [PATCH 31/72] please --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8290978d..6f3a18ff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -73,7 +73,7 @@ jobs: mkdir build cp -R target/release/bundle/*/*.app build/ ln -s /Applications build/Applications - hdiutil create -volname plumeimpactor -srcfolder build -ov -format UDZO target/release/bundle/plumeimpactor.dmg + hdiutil create -volname plumeimpactor -srcfolder build -ov -format UDZO target/release/bundle/osx/plumeimpactor.dmg - name: Build & Bundle (Windows) if: runner.os == 'Windows' @@ -85,4 +85,4 @@ jobs: with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/bundle/*/*.{rpm,deb,AppImage,dmg,exe,msi} + target/release/bundle/**/*.{rpm,deb,AppImage,dmg,exe,msi} From 6255cbc5f2e6039805587e56b36ca79ea20aae79 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 15:44:44 -0800 Subject: [PATCH 32/72] Update build.yml --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6f3a18ff..a094752b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,4 +85,6 @@ jobs: with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/bundle/**/*.{rpm,deb,AppImage,dmg,exe,msi} + target/release/bundle/appimage/*.AppImage + target/release/bundle/osx/*.dmg + target/release/bundle/msi/*.msi From b066d853c75d1142a7a674eb39cf8830bfbf6b82 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sat, 15 Nov 2025 16:21:20 -0800 Subject: [PATCH 33/72] install --- apps/plumeimpactor/src/frame.rs | 43 +++++++++++++++------------------ 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 319e0697..4fc01f18 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -10,6 +10,7 @@ use grand_slam::developer::DeveloperSession; use grand_slam::utils::{PlistInfoTrait, SignerSettings}; use idevice::IdeviceService; use idevice::lockdown::LockdownClient; +use idevice::utils::installation; use wxdragon::prelude::*; use futures::StreamExt; @@ -275,16 +276,6 @@ impl PlumeFrame { sender.send(PlumeFrameMessage::PackageDeselected).ok(); } }); - - - - - - - - - - let message_handler_for_install = message_handler.clone(); self.install_page.set_install_handler({ @@ -331,7 +322,7 @@ impl PlumeFrame { .find(|d| d.device_id.to_string() == device_id) .ok_or_else(|| format!("Device ID {device_id} not found"))?; - let device = Device::new(usbmuxd_device).await; + let device = Device::new(usbmuxd_device.clone()).await; // TODO: Handle multiple teams properly let teams = session.qh_list_teams() @@ -480,9 +471,25 @@ impl PlumeFrame { signer.sign_bundle(&bundle) .map_err(|e| format!("Failed to sign bundle: {}", e))?; - sender_clone.send(PlumeFrameMessage::InstallProgress(70, Some("Installing to device...".to_string()))).ok(); + let provider = usbmuxd_device.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "baller"); + + let bundle_name = bundle.get_name().unwrap_or_default(); + let callback = { + let sender_clone = sender_clone.clone(); + move |(progress, _): (u64, ())| { + let sender = sender_clone.clone(); + let bundle_name = bundle_name.clone(); + async move { + sender.send(PlumeFrameMessage::InstallProgress(progress as i32, Some(format!("Installing {}... {}%", bundle_name, progress)))).ok(); + } + } + }; + let state = (); + installation::install_package_with_callback(&provider, bundle.dir(), None, callback, state) + .await + .map_err(|e| format!("Failed to install package: {}", e))?; Ok::<_, String>(()) }); @@ -494,17 +501,7 @@ impl PlumeFrame { }); } }); - - - - - - - - - - - + } From 926c8e18056d8d79bb546759a37cda22153206b5 Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Sun, 16 Nov 2025 00:35:02 -0800 Subject: [PATCH 34/72] lkibux ui --- apps/plumeimpactor/src/frame.rs | 13 +++---------- apps/plumeimpactor/src/pages/login.rs | 4 ++++ apps/plumeimpactor/src/pages/mod.rs | 10 ++++++++++ crates/grand_slam/src/utils/macho.rs | 4 ++-- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 4fc01f18..c094d241 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -1,15 +1,13 @@ use std::cell::RefCell; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::rc::Rc; -use std::{env, fs, ptr, thread}; +use std::{env, ptr, thread}; use grand_slam::certificate::CertificateIdentity; use grand_slam::{AnisetteConfiguration, BundleType, Certificate, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; use grand_slam::utils::{PlistInfoTrait, SignerSettings}; -use idevice::IdeviceService; -use idevice::lockdown::LockdownClient; use idevice::utils::installation; use wxdragon::prelude::*; @@ -22,7 +20,7 @@ use crate::APP_NAME; use crate::handlers::{PlumeFrameMessage, PlumeFrameMessageHandler}; use crate::keychain::AccountCredentials; use crate::pages::login::{AccountDialog, LoginDialog}; -use crate::pages::{DefaultPage, InstallPage, create_account_dialog, create_default_page, create_install_page, create_login_dialog}; +use crate::pages::{DefaultPage, InstallPage, create_account_dialog, create_default_page, create_install_page, create_login_dialog, WINDOW_SIZE}; use crate::utils::{Device, Package}; pub struct PlumeFrame { @@ -37,11 +35,6 @@ pub struct PlumeFrame { pub account_dialog: AccountDialog, } -#[cfg(target_os = "linux")] -const WINDOW_SIZE: (i32, i32) = (700, 660); -#[cfg(not(target_os = "linux"))] -const WINDOW_SIZE: (i32, i32) = (530, 410); - impl PlumeFrame { pub fn new() -> Self { let frame = Frame::builder() diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 4c0ee0f6..9023633a 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -1,6 +1,7 @@ use wxdragon::prelude::*; use crate::frame::PlumeFrame; +use super::DIALOG_SIZE; #[derive(Clone)] pub struct LoginDialog { @@ -13,6 +14,7 @@ pub struct LoginDialog { pub fn create_login_dialog(parent: &Window) -> LoginDialog { let dialog = Dialog::builder(parent, "Sign in with your Apple ID") .with_style(DialogStyle::DefaultDialogStyle) + .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); @@ -111,6 +113,7 @@ pub struct AccountDialog { pub fn create_account_dialog(parent: &Window) -> AccountDialog { let dialog = Dialog::builder(parent, "Account") .with_style(DialogStyle::DefaultDialogStyle) + .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); @@ -158,6 +161,7 @@ impl PlumeFrame { pub fn create_single_field_dialog(&self, title: &str, label: &str) -> Result { let dialog = Dialog::builder(&self.frame, title) .with_style(DialogStyle::DefaultDialogStyle) + .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); diff --git a/apps/plumeimpactor/src/pages/mod.rs b/apps/plumeimpactor/src/pages/mod.rs index 71cbbec6..8286caef 100644 --- a/apps/plumeimpactor/src/pages/mod.rs +++ b/apps/plumeimpactor/src/pages/mod.rs @@ -6,3 +6,13 @@ pub use install::{InstallPage, create_install_page}; pub mod login; pub use login::{create_login_dialog, create_account_dialog}; + +#[cfg(target_os = "linux")] +pub const WINDOW_SIZE: (i32, i32) = (700, 660); +#[cfg(not(target_os = "linux"))] +pub const WINDOW_SIZE: (i32, i32) = (530, 410); + +#[cfg(target_os = "linux")] +pub const DIALOG_SIZE: (i32, i32) = (500, 300); +#[cfg(not(target_os = "linux"))] +pub const DIALOG_SIZE: (i32, i32) = (400, 200); diff --git a/crates/grand_slam/src/utils/macho.rs b/crates/grand_slam/src/utils/macho.rs index 2aa475c9..9d1d9799 100644 --- a/crates/grand_slam/src/utils/macho.rs +++ b/crates/grand_slam/src/utils/macho.rs @@ -8,7 +8,7 @@ use crate::{Error, developer::v1::capabilities::Capability}; /// Represents a Mach-O file and its entitlements. pub struct MachO { - macho_file: MachFile<'static>, + _macho_file: MachFile<'static>, pub entitlements: Option, } @@ -21,7 +21,7 @@ impl MachO { let entitlements = Self::extract_entitlements(&macho_file)?; Ok(MachO { - macho_file, + _macho_file: macho_file, entitlements, }) } From e99e85e4745fa3531fc57c5ab7a99c4958f96377 Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Sun, 16 Nov 2025 04:06:07 -0800 Subject: [PATCH 35/72] Update mod.rs --- apps/plumeimpactor/src/pages/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/plumeimpactor/src/pages/mod.rs b/apps/plumeimpactor/src/pages/mod.rs index 8286caef..500c21de 100644 --- a/apps/plumeimpactor/src/pages/mod.rs +++ b/apps/plumeimpactor/src/pages/mod.rs @@ -13,6 +13,6 @@ pub const WINDOW_SIZE: (i32, i32) = (700, 660); pub const WINDOW_SIZE: (i32, i32) = (530, 410); #[cfg(target_os = "linux")] -pub const DIALOG_SIZE: (i32, i32) = (500, 300); +pub const DIALOG_SIZE: (i32, i32) = (500, 500); #[cfg(not(target_os = "linux"))] -pub const DIALOG_SIZE: (i32, i32) = (400, 200); +pub const DIALOG_SIZE: (i32, i32) = (400, 300); From 6360a8ba7e73273ad4a0c7d777495eb9b2974a0a Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Sun, 16 Nov 2025 04:34:49 -0800 Subject: [PATCH 36/72] Update Windows build command and upload artifacts Changed the build command for Windows to use 'cargo build' instead of 'cargo bundle'. Added .exe files to the upload artifacts. --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a094752b..e80efbdb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: - name: Build & Bundle (Windows) if: runner.os == 'Windows' run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --release --format msi + cargo build --bin plumeimpactor --release - name: Upload Bundles uses: actions/upload-artifact@v4 @@ -88,3 +88,4 @@ jobs: target/release/bundle/appimage/*.AppImage target/release/bundle/osx/*.dmg target/release/bundle/msi/*.msi + target/release/*.exe From c420518281a7d0ea4fce112c127fc4fff95c19da Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 16 Nov 2025 05:38:53 -0800 Subject: [PATCH 37/72] style changes + config dir --- apps/plumeimpactor/src/frame.rs | 12 +++---- apps/plumeimpactor/src/main.rs | 17 +++++++++- apps/plumeimpactor/src/pages/default.rs | 2 +- apps/plumeimpactor/src/pages/login.rs | 44 ++++++++++++------------- apps/plumeimpactor/src/utils/device.rs | 2 +- 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index c094d241..76375fe2 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -1,7 +1,7 @@ use std::cell::RefCell; use std::path::PathBuf; use std::rc::Rc; -use std::{env, ptr, thread}; +use std::{ptr, thread}; use grand_slam::certificate::CertificateIdentity; use grand_slam::{AnisetteConfiguration, BundleType, Certificate, MachO, MobileProvision, Signer}; @@ -16,7 +16,7 @@ use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdListenEvent}; use tokio::runtime::Builder; use tokio::sync::mpsc; -use crate::APP_NAME; +use crate::{APP_NAME, get_data_path}; use crate::handlers::{PlumeFrameMessage, PlumeFrameMessageHandler}; use crate::keychain::AccountCredentials; use crate::pages::login::{AccountDialog, LoginDialog}; @@ -48,9 +48,9 @@ impl PlumeFrame { let top_panel = Panel::builder(&frame).build(); let top_row = BoxSizer::builder(Orientation::Horizontal).build(); - let add_ipa_button = Button::builder(&top_panel).with_label("+").build(); + let add_ipa_button = Button::builder(&top_panel).with_label("Import").build(); let device_picker = Choice::builder(&top_panel).build(); - let apple_id_button = Button::builder(&top_panel).with_label("Account").build(); + let apple_id_button = Button::builder(&top_panel).with_label("Settings").build(); top_row.add(&add_ipa_button, 0, SizerFlag::All, 0); top_row.add_spacer(12); @@ -364,7 +364,7 @@ impl PlumeFrame { sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); let identity = CertificateIdentity::new( - &PathBuf::from("/tmp"), + &get_data_path(), &session, "PLUME".to_string(), "AltStore".to_string(), @@ -585,7 +585,7 @@ pub fn run_login_flow( password: String, ) -> Result { let anisette_config = AnisetteConfiguration::default() - .set_configuration_path(PathBuf::from(env::temp_dir())); + .set_configuration_path(get_data_path()); let rt = Builder::new_current_thread().enable_all().build().unwrap(); diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index bf947327..16590e9b 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -6,6 +6,12 @@ mod pages; mod handlers; mod utils; +use std::{ + env, + fs, + path::{Path, PathBuf} +}; + pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env!("CARGO_PKG_VERSION")); #[tokio::main] @@ -23,7 +29,6 @@ use thiserror::Error as ThisError; pub enum Error { #[error("Info.plist not found")] PackageInfoPlistMissing, - #[error("I/O error: {0}")] Io(#[from] std::io::Error), #[error("Plist error: {0}")] @@ -35,3 +40,13 @@ pub enum Error { #[error("GrandSlam error: {0}")] GrandSlam(#[from] grand_slam::Error), } + +pub fn get_data_path() -> PathBuf { + let dir = Path::new(&env::var("HOME").unwrap()) + .join(".config") + .join("PlumeImpactor"); + + fs::create_dir_all(&dir).ok(); + + dir +} diff --git a/apps/plumeimpactor/src/pages/default.rs b/apps/plumeimpactor/src/pages/default.rs index c11e1426..471f70bc 100644 --- a/apps/plumeimpactor/src/pages/default.rs +++ b/apps/plumeimpactor/src/pages/default.rs @@ -3,7 +3,7 @@ use wxdragon::prelude::*; #[cfg(not(target_os = "linux"))] const WELCOME_TEXT: &str = "Drop your .ipa here"; #[cfg(target_os = "linux")] -const WELCOME_TEXT: &str = "Press '+' and select an .ipa to get started"; +const WELCOME_TEXT: &str = "Press 'import' and select an .ipa to get started"; #[derive(Clone)] pub struct DefaultPage { diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 9023633a..059670e0 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -13,7 +13,7 @@ pub struct LoginDialog { pub fn create_login_dialog(parent: &Window) -> LoginDialog { let dialog = Dialog::builder(parent, "Sign in with your Apple ID") - .with_style(DialogStyle::DefaultDialogStyle) + .with_style(DialogStyle::SystemMenu) .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); @@ -25,28 +25,18 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { .with_label(" Email:") .build(); let email_field = TextCtrl::builder(&dialog).build(); - email_row.add( - &email_label, - 0, - SizerFlag::AlignCenterVertical | SizerFlag::All, - 8, - ); - email_row.add(&email_field, 1, SizerFlag::Expand | SizerFlag::All, 12); - sizer.add_sizer(&email_row, 0, SizerFlag::Expand | SizerFlag::All, 0); + email_row.add(&email_label, 0, SizerFlag::AlignCenterVertical | SizerFlag::All, 4); + email_row.add(&email_field, 1, SizerFlag::Expand | SizerFlag::Right, 8); + sizer.add_sizer(&email_row, 0, SizerFlag::Expand | SizerFlag::All, 4); let password_row = BoxSizer::builder(Orientation::Horizontal).build(); let password_label = StaticText::builder(&dialog).with_label("Password:").build(); let password_field = TextCtrl::builder(&dialog) .with_style(TextCtrlStyle::Password) .build(); - password_row.add( - &password_label, - 0, - SizerFlag::AlignCenterVertical | SizerFlag::All, - 8, - ); - password_row.add(&password_field, 1, SizerFlag::Expand | SizerFlag::All, 12); - sizer.add_sizer(&password_row, 0, SizerFlag::Expand | SizerFlag::All, 0); + password_row.add(&password_label, 0, SizerFlag::AlignCenterVertical | SizerFlag::All, 4); + password_row.add(&password_field, 1, SizerFlag::Expand | SizerFlag::Right, 8); + sizer.add_sizer(&password_row, 0, SizerFlag::Expand | SizerFlag::All, 4); let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); @@ -112,24 +102,32 @@ pub struct AccountDialog { pub fn create_account_dialog(parent: &Window) -> AccountDialog { let dialog = Dialog::builder(parent, "Account") - .with_style(DialogStyle::DefaultDialogStyle) + .with_style(DialogStyle::SystemMenu) .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(12); - let label = StaticText::builder(&dialog) - .with_label("") - .build(); - sizer.add(&label, 0, SizerFlag::All, 12); + let label = StaticText::builder(&dialog).with_label("").build(); + sizer.add(&label, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 12); let buttons = BoxSizer::builder(Orientation::Horizontal).build(); + + let close_button = Button::builder(&dialog).with_label("Close").build(); + buttons.add(&close_button, 0, SizerFlag::All, 8); let logout_button = Button::builder(&dialog).with_label("Log out").build(); + buttons.add(&logout_button, 0, SizerFlag::All, 8); + sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); dialog.set_sizer(sizer, true); + + close_button.on_click({ + let dialog = dialog.clone(); + move |_| dialog.end_modal(ID_OK as i32) + }); AccountDialog { dialog, @@ -160,7 +158,7 @@ impl AccountDialog { impl PlumeFrame { pub fn create_single_field_dialog(&self, title: &str, label: &str) -> Result { let dialog = Dialog::builder(&self.frame, title) - .with_style(DialogStyle::DefaultDialogStyle) + .with_style(DialogStyle::SystemMenu) .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); diff --git a/apps/plumeimpactor/src/utils/device.rs b/apps/plumeimpactor/src/utils/device.rs index 1c76a4af..815f684c 100644 --- a/apps/plumeimpactor/src/utils/device.rs +++ b/apps/plumeimpactor/src/utils/device.rs @@ -6,7 +6,7 @@ use idevice::IdeviceService; use crate::Error; -pub const CONNECTION_LABEL: &str = "plume"; +pub const CONNECTION_LABEL: &str = "plume_info"; macro_rules! get_dict_string { ($dict:expr, $key:expr) => { From ced2ee162b4cf258ab6754fec800b76b63f6ceb4 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 16 Nov 2025 05:47:05 -0800 Subject: [PATCH 38/72] for now disallow multiple teams --- apps/plumeimpactor/src/frame.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 76375fe2..a951605a 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -320,10 +320,18 @@ impl PlumeFrame { // TODO: Handle multiple teams properly let teams = session.qh_list_teams() .await - .map_err(|e| format!("Failed to list teams: {}", e))?; + .map_err(|e| format!("Failed to list teams: {}", e))?.teams; + + if teams.len() != 1 { + return Err("Multiple teams detected for the Apple ID account.".to_string()); + } + + let team_id = &teams.get(0) + .ok_or("No teams available for the Apple ID account.")? + .team_id; let device = session.qh_ensure_device( - &teams.teams.get(0).ok_or("No teams available for the Apple ID account.")?.team_id, + team_id, &device.name, &device.uuid, ) @@ -339,10 +347,6 @@ impl PlumeFrame { let bundles = bundle.collect_bundles_sorted() .map_err(|e| format!("Failed to collect bundles: {}", e))?; - let team_id = &teams.teams.get(0) - .ok_or("No teams available for the Apple ID account.")? - .team_id; - let bundle_identifier = bundle.get_bundle_identifier() .ok_or("Failed to get bundle identifier from package.")?; @@ -488,7 +492,8 @@ impl PlumeFrame { }); if let Err(e) = install_result { - sender_clone.send(PlumeFrameMessage::InstallProgress(99, Some(format!("{}", e)))).ok(); + sender_clone.send(PlumeFrameMessage::InstallProgress(100, None)).ok(); + sender_clone.send(PlumeFrameMessage::Error(format!("{}", e))).ok(); return; } }); From 994f40fffa87f87fdef901275f5af5c5264481ed Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 16 Nov 2025 05:58:02 -0800 Subject: [PATCH 39/72] remove some useless stuff --- apps/plumeimpactor/src/frame.rs | 7 ++----- apps/plumeimpactor/src/handlers.rs | 1 + crates/grand_slam/src/utils/signer.rs | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index a951605a..8ccf8ef8 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -330,16 +330,14 @@ impl PlumeFrame { .ok_or("No teams available for the Apple ID account.")? .team_id; - let device = session.qh_ensure_device( + session.qh_ensure_device( team_id, &device.name, &device.uuid, ) .await .map_err(|e| format!("Failed to ensure device is registered: {}", e))?; - - println!("Device ensured: {:#?}", device); - + sender_clone.send(PlumeFrameMessage::InstallProgress(20, Some("Extracting package...".to_string()))).ok(); let bundle = package.get_package_bundle() @@ -501,7 +499,6 @@ impl PlumeFrame { }); } - fn bind_login_dialog_next_handler( &self, diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 1b3c6571..f0aca31d 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -169,6 +169,7 @@ impl PlumeFrameMessageHandler { } } PlumeFrameMessage::InstallProgress(progress, message_opt) => { + println!("Progress: {} - {:?}", progress, message_opt); let Some(selected_package) = &self.package_selected else { return; }; diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs index 3c917992..5f4ae001 100644 --- a/crates/grand_slam/src/utils/signer.rs +++ b/crates/grand_slam/src/utils/signer.rs @@ -93,8 +93,6 @@ impl Signer { } } - println!("Signing {}...", bundle.dir().to_string_lossy()); - UnifiedSigner::new(settings).sign_path_in_place(bundle.dir())?; } From 135c27ad85c82553aa8606bb729c6a9c82d3c302 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 16 Nov 2025 16:05:53 -0800 Subject: [PATCH 40/72] basic modifications --- apps/plumeimpactor/src/frame.rs | 237 +++++++++++++----------- apps/plumeimpactor/src/handlers.rs | 33 ++-- apps/plumeimpactor/src/pages/install.rs | 201 ++++++++++++++++++-- apps/plumeimpactor/src/pages/login.rs | 10 +- apps/plumeimpactor/src/utils/package.rs | 12 +- apps/plumesign/src/main.rs | 2 +- crates/grand_slam/src/utils/mod.rs | 37 ++-- crates/grand_slam/src/utils/signer.rs | 19 -- 8 files changed, 381 insertions(+), 170 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 8ccf8ef8..d71b2726 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -45,24 +45,21 @@ impl PlumeFrame { let sizer = BoxSizer::builder(Orientation::Vertical).build(); - let top_panel = Panel::builder(&frame).build(); let top_row = BoxSizer::builder(Orientation::Horizontal).build(); - let add_ipa_button = Button::builder(&top_panel).with_label("Import").build(); - let device_picker = Choice::builder(&top_panel).build(); - let apple_id_button = Button::builder(&top_panel).with_label("Settings").build(); + let add_ipa_button = Button::builder(&frame).with_label("Import").build(); + let device_picker = Choice::builder(&frame).build(); + let apple_id_button = Button::builder(&frame).with_label("Settings").build(); top_row.add(&add_ipa_button, 0, SizerFlag::All, 0); - top_row.add_spacer(12); + top_row.add_spacer(13); top_row.add(&device_picker, 1, SizerFlag::Expand | SizerFlag::All, 0); - top_row.add_spacer(12); + top_row.add_spacer(13); top_row.add(&apple_id_button, 0, SizerFlag::All, 0); - top_panel.set_sizer(top_row, true); - let default_page = create_default_page(&frame); let install_page = create_install_page(&frame); - sizer.add(&top_panel, 0, SizerFlag::Expand | SizerFlag::All, 12); + sizer.add_sizer(&top_row, 0, SizerFlag::Expand | SizerFlag::All, 13); sizer.add( &default_page.panel, 1, @@ -272,7 +269,7 @@ impl PlumeFrame { let message_handler_for_install = message_handler.clone(); self.install_page.set_install_handler({ - let frame = self.frame.clone(); + // let frame = self.frame.clone(); let sender = sender.clone(); move || { let binding = message_handler_for_install.borrow(); @@ -291,6 +288,9 @@ impl PlumeFrame { sender.send(PlumeFrameMessage::Error("No Apple ID account available for installation.".to_string())).ok(); return; }; + + let mut signer_settings = binding.signer_settings.clone(); + binding.plume_frame.install_page.update_fields(&mut signer_settings); let package = selected_package.clone(); let account = selected_account.clone(); @@ -348,16 +348,36 @@ impl PlumeFrame { let bundle_identifier = bundle.get_bundle_identifier() .ok_or("Failed to get bundle identifier from package.")?; - let new_id = new_identifier(&bundle_identifier, team_id); + if let Some(new_name) = signer_settings.custom_name.as_ref() { + bundle.set_name(new_name).map_err(|e| format!("Failed to set new name: {}", e))?; + } - fn new_identifier(original: &str, team_id: &str) -> String { - format!("{}.{}", original, team_id) + if let Some(new_version) = signer_settings.custom_version.as_ref() { + bundle.set_version(new_version).map_err(|e| format!("Failed to set new version: {}", e))?; } - if let Some(old_identifier) = bundle.get_bundle_identifier() { + if signer_settings.support_file_sharing { + bundle.set_info_plist_key("UIFileSharingEnabled", true).map_err(|e| format!("Failed to set file sharing: {}", e))?; + bundle.set_info_plist_key("UISupportsDocumentBrowser", true).map_err(|e| format!("Failed to set document opening: {}", e))?; + } + + if signer_settings.support_ipad_fullscreen { + bundle.set_info_plist_key("UIRequiresFullScreen", true).map_err(|e| format!("Failed to set iPad fullscreen: {}", e))?; + } + + if signer_settings.support_game_mode { + bundle.set_info_plist_key("GCSupportsGameMode", true).map_err(|e| format!("Failed to set game mode: {}", e))?; + } + + if signer_settings.support_pro_motion { + bundle.set_info_plist_key("CADisableMinimumFrameDurationOnPhone", true).map_err(|e| format!("Failed to set document opening: {}", e))?; + } + + if !signer_settings.export_ipa && signer_settings.custom_identifier.is_none() { + let new_id: String = format!("{bundle_identifier}.{team_id}"); for embedded_bundle in &bundles { embedded_bundle.set_matching_identifier( - &old_identifier, + &bundle_identifier, &new_id, ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; } @@ -377,114 +397,123 @@ impl PlumeFrame { let mut provisionings: Vec = Vec::new(); - // TODO: handle requests on seperate threads to speed this up - for bundle in &bundles { - if - bundle._type != BundleType::AppExtension && - bundle._type != BundleType::App - { - continue; - } - - let bundle_executable_name = bundle.get_executable() - .ok_or("Failed to get executable from bundle.")?; - - let bundle_executable_path = bundle.dir().join(&bundle_executable_name); - - let macho = MachO::new(&bundle_executable_path) - .map_err(|e| format!("Failed to read Mach-O binary: {}", e))?; - - let id = bundle.get_bundle_identifier() - .ok_or("Failed to get bundle identifier from bundle.")?; - - println!("{}", id); - - session.qh_ensure_app_id(team_id, &bundle.get_name().unwrap_or_default(), &id) - .await - .map_err(|e| format!("Failed to ensure app ID: {}", e))?; - - let capabilities = session.v1_list_capabilities(team_id) - .await - .map_err(|e| format!("Failed to list capabilities: {}", e))?; - - let app_id_id = session.qh_get_app_id(team_id, &id) - .await - .map_err(|e| e.to_string())? - .ok_or("Failed to get ensured app ID.")?; + if !signer_settings.export_ipa { + for sub_bundle in &bundles { + if signer_settings.should_only_use_main_provisioning && sub_bundle.dir() != bundle.dir() { + continue; + } + + if + sub_bundle._type != BundleType::AppExtension && + sub_bundle._type != BundleType::App + { + continue; + } - if let Some(caps) = macho.capabilities_for_entitlements(&capabilities.data) { - session.v1_update_app_id(team_id, &id, caps) + let bundle_executable_name = sub_bundle.get_executable() + .ok_or("Failed to get executable from bundle.")?; + + let bundle_executable_path = sub_bundle.dir().join(&bundle_executable_name); + + let macho = MachO::new(&bundle_executable_path) + .map_err(|e| format!("Failed to read Mach-O binary: {}", e))?; + + let id = sub_bundle.get_bundle_identifier() + .ok_or("Failed to get bundle identifier from bundle.")?; + + println!("{}", id); + + session.qh_ensure_app_id(team_id, &sub_bundle.get_name().unwrap_or_default(), &id) .await - .map_err(|e| format!("Failed to enable capabilities: {}", e))?; - } - - if let Some(app_groups) = macho.app_groups_for_entitlements() { - for group in &app_groups { - let group = format!("{group}.{team_id}"); - let group_id = session.qh_ensure_app_group(team_id, &group, &group) - .await - .map_err(|e| format!("Failed to ensure app group: {}", e))?; + .map_err(|e| format!("Failed to ensure app ID: {}", e))?; + + let capabilities = session.v1_list_capabilities(team_id) + .await + .map_err(|e| format!("Failed to list capabilities: {}", e))?; + + let app_id_id = session.qh_get_app_id(team_id, &id) + .await + .map_err(|e| e.to_string())? + .ok_or("Failed to get ensured app ID.")?; - session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) + if let Some(caps) = macho.capabilities_for_entitlements(&capabilities.data) { + session.v1_update_app_id(team_id, &id, caps) .await - .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + .map_err(|e| format!("Failed to enable capabilities: {}", e))?; + } + + if let Some(app_groups) = macho.app_groups_for_entitlements() { + for group in &app_groups { + let group = format!("{group}.{team_id}"); + let group_id = session.qh_ensure_app_group(team_id, &group, &group) + .await + .map_err(|e| format!("Failed to ensure app group: {}", e))?; + + session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) + .await + .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + } } - } - - let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id) - .await - .map_err(|e| format!("Failed to list profiles: {}", e))?; - let profile_data = profiles.provisioning_profile.encoded_profile; - - let mobile_provision = MobileProvision::load_from_bytes(profile_data.as_ref()) - .map_err(|e| format!("Failed to load mobile provision: {}", e))?; - - provisionings.push(mobile_provision); + let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id) + .await + .map_err(|e| format!("Failed to list profiles: {}", e))?; + + let profile_data = profiles.provisioning_profile.encoded_profile; + + let mobile_provision = MobileProvision::load_from_bytes(profile_data.as_ref()) + .map_err(|e| format!("Failed to load mobile provision: {}", e))?; + + provisionings.push(mobile_provision); + } } sender_clone.send(PlumeFrameMessage::InstallProgress(50, Some(format!("Signing {}...", bundle.get_name().unwrap_or_default())))).ok(); - let certificate_paths = vec![ - identity.get_certificate_file_path().to_path_buf(), - identity.get_private_key_file_path().to_path_buf(), - ]; + let mut certificate: Option = None; - let certificate = Certificate::new(Some(certificate_paths)) - .map_err(|e| format!("Failed to create Certificate: {}", e))?; - - let signer_settings = SignerSettings { - ..Default::default() - }; + if !signer_settings.export_ipa { + let certificate_paths = vec![ + identity.get_certificate_file_path().to_path_buf(), + identity.get_private_key_file_path().to_path_buf(), + ]; + + certificate = Some(Certificate::new(Some(certificate_paths)) + .map_err(|e| format!("Failed to create Certificate: {}", e))?); + } let signer = Signer::new( - Some(certificate), - signer_settings, + certificate, + signer_settings.clone(), provisionings, ); signer.sign_bundle(&bundle) .map_err(|e| format!("Failed to sign bundle: {}", e))?; - - let provider = usbmuxd_device.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "baller"); - - let bundle_name = bundle.get_name().unwrap_or_default(); - let callback = { - let sender_clone = sender_clone.clone(); - move |(progress, _): (u64, ())| { - let sender = sender_clone.clone(); - let bundle_name = bundle_name.clone(); - async move { - sender.send(PlumeFrameMessage::InstallProgress(progress as i32, Some(format!("Installing {}... {}%", bundle_name, progress)))).ok(); - } - } - }; - - let state = (); - installation::install_package_with_callback(&provider, bundle.dir(), None, callback, state) - .await - .map_err(|e| format!("Failed to install package: {}", e))?; + if !signer_settings.export_ipa { + let provider = usbmuxd_device.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "baller"); + + let bundle_name = bundle.get_name().unwrap_or_default(); + let callback = { + let sender_clone = sender_clone.clone(); + move |(progress, _): (u64, ())| { + let sender = sender_clone.clone(); + let bundle_name = bundle_name.clone(); + async move { + sender.send(PlumeFrameMessage::InstallProgress(progress as i32, Some(format!("Installing {}... {}%", bundle_name, progress)))).ok(); + } + } + }; + + let state = (); + + installation::install_package_with_callback(&provider, bundle.dir(), None, callback, state) + .await + .map_err(|e| format!("Failed to install package: {}", e))?; + } else { + todo!("Export IPA functionality"); + } Ok::<_, String>(()) }); diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index f0aca31d..8ddd37d1 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -5,7 +5,7 @@ use tokio::sync::mpsc::error::TryRecvError; use std::sync::mpsc as std_mpsc; use grand_slam::auth::Account; -use grand_slam::utils::PlistInfoTrait; +use grand_slam::utils::{PlistInfoTrait, SignerSettings}; use crate::frame::PlumeFrame; use crate::keychain::AccountCredentials; @@ -35,6 +35,8 @@ pub struct PlumeFrameMessageHandler { pub package_selected: Option, // --- account --- pub account_credentials: Option, + // --- signer settings --- + pub signer_settings: SignerSettings, } impl PlumeFrameMessageHandler { @@ -42,6 +44,7 @@ impl PlumeFrameMessageHandler { receiver: mpsc::UnboundedReceiver, plume_frame: PlumeFrame, ) -> Self { + let signer_settings = SignerSettings::default(); Self { receiver, plume_frame, @@ -50,6 +53,7 @@ impl PlumeFrameMessageHandler { package_selected: None, account_credentials: None, installation_progress_dialog: None, + signer_settings, } } @@ -92,6 +96,8 @@ impl PlumeFrameMessageHandler { self.usbmuxd_picker_reconcile_selection(); } } + + self.plume_frame.install_page.install_button.enable(true); } PlumeFrameMessage::DeviceDisconnected(device_id) => { if let Some(index) = self @@ -103,35 +109,34 @@ impl PlumeFrameMessageHandler { self.usbmuxd_picker_rebuild_contents(); self.usbmuxd_picker_reconcile_selection(); } + + if self.usbmuxd_device_list.is_empty() { + self.plume_frame.install_page.install_button.enable(false); + } } PlumeFrameMessage::PackageSelected(package) => { if self.package_selected.is_some() { return; } - let package_name = package - .get_name() - .unwrap_or_else(|| "Unknown".to_string()); - let package_id = package - .get_bundle_identifier() - .unwrap_or_else(|| "Unknown".to_string()); - let package_version = package - .get_version() - .unwrap_or_else(|| "Unknown".to_string()); - + package.load_into_signer_settings(&mut self.signer_settings).ok(); + self.package_selected = Some(package); - self.plume_frame - .install_page - .set_top_text(format!("{} - {} ({})", package_name, package_id, package_version).as_str()); + self.plume_frame.install_page.set_settings(&self.signer_settings, Some(self.package_selected.as_ref().unwrap())); self.plume_frame.default_page.panel.hide(); self.plume_frame.install_page.panel.show(true); self.plume_frame.frame.layout(); + + self.plume_frame.add_ipa_button.enable(false); } PlumeFrameMessage::PackageDeselected => { self.package_selected = None; self.plume_frame.install_page.panel.hide(); self.plume_frame.default_page.panel.show(true); self.plume_frame.frame.layout(); + self.signer_settings = SignerSettings::default(); + self.plume_frame.install_page.set_settings(&self.signer_settings, None); + self.plume_frame.add_ipa_button.enable(true); } PlumeFrameMessage::AccountLogin(account) => { let (first, last) = account.get_name(); diff --git a/apps/plumeimpactor/src/pages/install.rs b/apps/plumeimpactor/src/pages/install.rs index ce8e1f96..f658a644 100644 --- a/apps/plumeimpactor/src/pages/install.rs +++ b/apps/plumeimpactor/src/pages/install.rs @@ -1,11 +1,28 @@ +use grand_slam::utils::{PlistInfoTrait, SignerSettings}; use wxdragon::prelude::*; +use crate::utils::Package; + #[derive(Clone)] pub struct InstallPage { pub panel: Panel, pub cancel_button: Button, pub install_button: Button, - pub top_text: StaticText, + + custom_name_textfield: TextCtrl, + custom_identifier_textfield: TextCtrl, + custom_version_textfield: TextCtrl, + support_older_versions_checkbox: CheckBox, + support_file_sharing_checkbox: CheckBox, + ipad_fullscreen_checkbox: CheckBox, + game_mode_checkbox: CheckBox, + pro_motion_checkbox: CheckBox, + should_embed_pairing_checkbox: CheckBox, + skip_registering_extensions_checkbox: CheckBox, + + original_name: Option, + original_identifier: Option, + original_version: Option, } pub fn create_install_page(frame: &Frame) -> InstallPage { @@ -13,26 +30,99 @@ pub fn create_install_page(frame: &Frame) -> InstallPage { let main_sizer = BoxSizer::builder(Orientation::Vertical).build(); - let top_text = StaticText::builder(&panel).with_label("Unknown").build(); + let settings_sizer = BoxSizer::builder(Orientation::Horizontal).build(); + + let textfields_sizer = BoxSizer::builder(Orientation::Vertical).build(); + let bundle_name_label = StaticText::builder(&panel) + .with_label("Name:") + .build(); + let custom_name_textfield = TextCtrl::builder(&panel) + .with_value("") + .build(); + let bundle_identifier_label = StaticText::builder(&panel) + .with_label("Identifier:") + .build(); + let custom_identifier_textfield = TextCtrl::builder(&panel) + .with_value("") + .build(); + let bundle_version_label = StaticText::builder(&panel) + .with_label("Version:") + .build(); + let custom_version_textfield = TextCtrl::builder(&panel) + .with_value("") + .build(); + textfields_sizer.add(&bundle_name_label, 0, SizerFlag::Bottom, 6); + textfields_sizer.add(&custom_name_textfield, 0, SizerFlag::Expand | SizerFlag::Left, 8); + textfields_sizer.add(&bundle_identifier_label, 0, SizerFlag::Top | SizerFlag::Bottom, 6); + textfields_sizer.add(&custom_identifier_textfield, 0, SizerFlag::Expand | SizerFlag::Left, 8); + textfields_sizer.add(&bundle_version_label, 0, SizerFlag::Top | SizerFlag::Bottom, 6); + textfields_sizer.add(&custom_version_textfield, 0, SizerFlag::Expand | SizerFlag::Left, 8); + + let checkbox_sizer = BoxSizer::builder(Orientation::Vertical).build(); + let general_label = StaticText::builder(&panel) + .with_label("General:") + .build(); + let support_older_versions_checkbox = CheckBox::builder(&panel) + .with_label("Try to support older versions (7+)") + .build(); + let support_file_sharing_checkbox = CheckBox::builder(&panel) + .with_label("Force File Sharing") + .build(); + let ipad_fullscreen_checkbox = CheckBox::builder(&panel) + .with_label("Force iPad Fullscreen") + .build(); + let game_mode_checkbox = CheckBox::builder(&panel) + .with_label("Force Game Mode") + .build(); + let pro_motion_checkbox = CheckBox::builder(&panel) + .with_label("Force Pro Motion") + .build(); + let advanced_label = StaticText::builder(&panel) + .with_label("Advanced:") + .build(); + let should_embed_pairing_checkbox = CheckBox::builder(&panel) + .with_label("Embed Pairing File") + .build(); + should_embed_pairing_checkbox.enable(false); + let skip_registering_extensions_checkbox = CheckBox::builder(&panel) + .with_label("Only Register Main Bundle") + .build(); + checkbox_sizer.add(&general_label, 0, SizerFlag::Bottom, 6); + checkbox_sizer.add(&support_older_versions_checkbox, 0, SizerFlag::Expand | SizerFlag::Left, 8); + checkbox_sizer.add(&support_file_sharing_checkbox, 0, SizerFlag::Expand | SizerFlag::Top | SizerFlag::Left, 8); + checkbox_sizer.add(&ipad_fullscreen_checkbox, 0, SizerFlag::Expand | SizerFlag::Top | SizerFlag::Left, 8); + checkbox_sizer.add(&game_mode_checkbox, 0, SizerFlag::Expand | SizerFlag::Top | SizerFlag::Left, 8); + checkbox_sizer.add(&pro_motion_checkbox, 0, SizerFlag::Expand | SizerFlag::Top | SizerFlag::Left | SizerFlag::Bottom, 8); + checkbox_sizer.add(&advanced_label, 0, SizerFlag::Top | SizerFlag::Bottom, 6); + checkbox_sizer.add(&should_embed_pairing_checkbox, 0, SizerFlag::Expand | SizerFlag::Left, 8); + checkbox_sizer.add(&skip_registering_extensions_checkbox, 0, SizerFlag::Expand | SizerFlag::Top | SizerFlag::Left, 8); + + settings_sizer.add_sizer(&textfields_sizer, 1, SizerFlag::Expand | SizerFlag::Right, 13); + settings_sizer.add_sizer(&checkbox_sizer, 1, SizerFlag::Expand, 13); - main_sizer.add(&top_text, 0, SizerFlag::Left, 14); + main_sizer.add_sizer(&settings_sizer, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 13); main_sizer.add_stretch_spacer(1); let button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); - let cancel_button = Button::builder(&panel).with_label("Cancel").build(); - let install_button = Button::builder(&panel).with_label("Install").build(); + let cancel_button = Button::builder(&panel) + .with_label("Cancel") + .build(); + let install_button = Button::builder(&panel) + .with_label("Install") + .build(); + install_button.enable(false); button_sizer.add_stretch_spacer(1); - button_sizer.add(&cancel_button, 0, SizerFlag::Right, 12); + button_sizer.add(&cancel_button, 0, SizerFlag::Right, 13); button_sizer.add(&install_button, 0, SizerFlag::All, 0); main_sizer.add_sizer( &button_sizer, 0, SizerFlag::Right | SizerFlag::Bottom | SizerFlag::Expand, - 14, + 13, ); panel.set_sizer(main_sizer, true); @@ -41,7 +131,98 @@ pub fn create_install_page(frame: &Frame) -> InstallPage { panel, cancel_button, install_button, - top_text, + + custom_name_textfield, + custom_identifier_textfield, + custom_version_textfield, + support_older_versions_checkbox, + support_file_sharing_checkbox, + ipad_fullscreen_checkbox, + game_mode_checkbox, + pro_motion_checkbox, + should_embed_pairing_checkbox, + skip_registering_extensions_checkbox, + + original_name: None, + original_identifier: None, + original_version: None, + } +} + + +impl InstallPage { + pub fn set_settings(&mut self, settings: &SignerSettings, package: Option<&Package>) { + self.support_older_versions_checkbox.set_value(settings.support_minimum_os_version); + self.support_file_sharing_checkbox.set_value(settings.support_file_sharing); + self.ipad_fullscreen_checkbox.set_value(settings.support_ipad_fullscreen); + self.game_mode_checkbox.set_value(settings.support_game_mode); + self.pro_motion_checkbox.set_value(settings.support_pro_motion); + self.should_embed_pairing_checkbox.set_value(settings.should_embed_pairing); + self.skip_registering_extensions_checkbox.set_value(settings.should_only_use_main_provisioning); + + if let Some(package) = package { + if let Some(ref name) = package.get_name() { + self.custom_name_textfield.set_value(name); + self.original_name = Some(name.clone()); + } else { + self.custom_name_textfield.set_value(""); + self.original_name = None; + } + + if let Some(ref identifier) = package.get_bundle_identifier() { + self.custom_identifier_textfield.set_value(identifier); + self.original_identifier = Some(identifier.clone()); + } else { + self.custom_identifier_textfield.set_value(""); + self.original_identifier = None; + } + + if let Some(ref version) = package.get_version() { + self.custom_version_textfield.set_value(version); + self.original_version = Some(version.clone()); + } else { + self.custom_version_textfield.set_value(""); + self.original_version = None; + } + } else { + self.custom_name_textfield.set_value(""); + self.custom_identifier_textfield.set_value(""); + self.custom_version_textfield.set_value(""); + self.original_name = None; + self.original_identifier = None; + self.original_version = None; + } + } + + pub fn update_fields(&self, settings: &mut SignerSettings) { + settings.support_minimum_os_version = self.support_older_versions_checkbox.get_value(); + settings.support_file_sharing = self.support_file_sharing_checkbox.get_value(); + settings.support_ipad_fullscreen = self.ipad_fullscreen_checkbox.get_value(); + settings.support_game_mode = self.game_mode_checkbox.get_value(); + settings.support_pro_motion = self.pro_motion_checkbox.get_value(); + settings.should_embed_pairing = self.should_embed_pairing_checkbox.get_value(); + settings.should_only_use_main_provisioning = self.skip_registering_extensions_checkbox.get_value(); + + if let Some(ref original_name) = self.original_name { + let current_name = self.custom_name_textfield.get_value(); + if ¤t_name != original_name { + settings.custom_name = Some(current_name.to_string()); + } + } + + if let Some(ref original_identifier) = self.original_identifier { + let current_identifier = self.custom_identifier_textfield.get_value(); + if ¤t_identifier != original_identifier { + settings.custom_identifier = Some(current_identifier.to_string()); + } + } + + if let Some(ref original_version) = self.original_version { + let current_version = self.custom_version_textfield.get_value(); + if ¤t_version != original_version { + settings.custom_version = Some(current_version.to_string()); + } + } } } @@ -57,8 +238,4 @@ impl InstallPage { on_install(); }); } - - pub fn set_top_text(&self, text: &str) { - self.top_text.set_label(text); - } } diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 059670e0..4cf7654c 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -18,7 +18,7 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); - sizer.add_spacer(12); + sizer.add_spacer(13); let email_row = BoxSizer::builder(Orientation::Horizontal).build(); let email_label = StaticText::builder(&dialog) @@ -42,10 +42,10 @@ pub fn create_login_dialog(parent: &Window) -> LoginDialog { let cancel_button = Button::builder(&dialog).with_label("Cancel").build(); let next_button = Button::builder(&dialog).with_label("Next").build(); button_sizer.add(&cancel_button, 1, SizerFlag::Expand | SizerFlag::All, 0); - button_sizer.add_spacer(12); + button_sizer.add_spacer(13); button_sizer.add(&next_button, 1, SizerFlag::Expand | SizerFlag::All, 0); - sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 12); + sizer.add_sizer(&button_sizer, 0, SizerFlag::AlignRight | SizerFlag::All, 13); dialog.set_sizer(sizer, true); @@ -107,10 +107,10 @@ pub fn create_account_dialog(parent: &Window) -> AccountDialog { .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); - sizer.add_spacer(12); + sizer.add_spacer(13); let label = StaticText::builder(&dialog).with_label("").build(); - sizer.add(&label, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 12); + sizer.add(&label, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 13); let buttons = BoxSizer::builder(Orientation::Horizontal).build(); diff --git a/apps/plumeimpactor/src/utils/package.rs b/apps/plumeimpactor/src/utils/package.rs index 757f7e8c..9f6459a6 100644 --- a/apps/plumeimpactor/src/utils/package.rs +++ b/apps/plumeimpactor/src/utils/package.rs @@ -8,7 +8,7 @@ use uuid::Uuid; use zip::ZipArchive; use grand_slam::Bundle; -use grand_slam::utils::PlistInfoTrait; +use grand_slam::utils::{PlistInfoTrait, SignerSettings}; use crate::Error; #[derive(Debug, Clone)] @@ -101,6 +101,16 @@ impl PlistInfoTrait for Package { } } +impl Package { + pub fn load_into_signer_settings<'settings, 'slf: 'settings>( + &'slf self, + settings: &'settings mut SignerSettings, + ) -> Result<(), Error> { + + Ok(()) + } +} + // impl Drop for Package { // fn drop(&mut self) { // fs::remove_dir_all(&self.stage_dir).ok(); diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index 8edbf54b..86865179 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -73,7 +73,7 @@ async fn main() { let signer_settings = SignerSettings { custom_name: args.name.clone(), custom_identifier: args.bundle_identifier.clone(), - custom_build_version: args.version.clone(), + custom_version: args.version.clone(), ..Default::default() }; diff --git a/crates/grand_slam/src/utils/mod.rs b/crates/grand_slam/src/utils/mod.rs index ee01c7ca..86050171 100644 --- a/crates/grand_slam/src/utils/mod.rs +++ b/crates/grand_slam/src/utils/mod.rs @@ -11,30 +11,39 @@ pub use signer::Signer; pub use bundle::Bundle; pub use bundle::BundleType; +#[derive(Clone, Debug)] pub struct SignerSettings { - pub should_embed_provisioning: bool, pub custom_name: Option, pub custom_identifier: Option, - pub custom_build_version: Option, - pub support_minimum_os_version: Option, - pub support_file_sharing: Option, - pub support_pro_motion: Option, - pub support_ipad_fullscreen: Option, - pub remove_url_schemes: Option, + pub custom_version: Option, + pub support_minimum_os_version: bool, + pub support_file_sharing: bool, + pub support_ipad_fullscreen: bool, + pub support_game_mode: bool, + pub support_pro_motion: bool, + pub should_embed_provisioning: bool, + pub should_embed_pairing: bool, + pub should_only_use_main_provisioning: bool, + pub remove_url_schemes: bool, + pub export_ipa: bool, } impl Default for SignerSettings { fn default() -> Self { Self { - should_embed_provisioning: true, custom_name: None, custom_identifier: None, - custom_build_version: None, - support_minimum_os_version: None, - support_file_sharing: None, - support_pro_motion: None, - support_ipad_fullscreen: None, - remove_url_schemes: None, + custom_version: None, + support_minimum_os_version: false, + support_file_sharing: false, + support_ipad_fullscreen: false, + support_game_mode: false, + support_pro_motion: false, + should_embed_provisioning: true, + should_embed_pairing: false, + should_only_use_main_provisioning: false, + remove_url_schemes: false, + export_ipa: false, } } } diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs index 5f4ae001..881cc4c1 100644 --- a/crates/grand_slam/src/utils/signer.rs +++ b/crates/grand_slam/src/utils/signer.rs @@ -36,25 +36,6 @@ impl Signer { pub fn sign_bundle(&self, bundle: &Bundle) -> Result<(), Error> { let bundles = bundle.collect_bundles_sorted()?; - if let Some(new_name) = self.settings.custom_name.as_ref() { - bundle.set_name(new_name)?; - } - - if let Some(new_version) = self.settings.custom_build_version.as_ref() { - bundle.set_version(new_version)?; - } - - if let Some(new_identifier) = self.settings.custom_identifier.as_ref() { - if let Some(old_identifier) = bundle.get_bundle_identifier() { - for embedded_bundle in &bundles { - embedded_bundle.set_matching_identifier( - &old_identifier, - &new_identifier, - )?; - } - } - } - for bundle in &bundles { let mut settings = self.build_base_settings()?; From 865847aa451821318cf73d2dca9c6ad435a0149b Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 16 Nov 2025 16:50:40 -0800 Subject: [PATCH 41/72] add this back --- apps/plumeimpactor/src/pages/login.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/login.rs index 4cf7654c..fe2a8a13 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/login.rs @@ -13,7 +13,7 @@ pub struct LoginDialog { pub fn create_login_dialog(parent: &Window) -> LoginDialog { let dialog = Dialog::builder(parent, "Sign in with your Apple ID") - .with_style(DialogStyle::SystemMenu) + .with_style(DialogStyle::SystemMenu | DialogStyle::Caption) .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); @@ -102,7 +102,7 @@ pub struct AccountDialog { pub fn create_account_dialog(parent: &Window) -> AccountDialog { let dialog = Dialog::builder(parent, "Account") - .with_style(DialogStyle::SystemMenu) + .with_style(DialogStyle::SystemMenu | DialogStyle::Caption) .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); @@ -158,7 +158,7 @@ impl AccountDialog { impl PlumeFrame { pub fn create_single_field_dialog(&self, title: &str, label: &str) -> Result { let dialog = Dialog::builder(&self.frame, title) - .with_style(DialogStyle::SystemMenu) + .with_style(DialogStyle::SystemMenu | DialogStyle::Caption) .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); From 9ba021b8b6aadd24ea095c959b8736d502461902 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Sun, 16 Nov 2025 17:04:17 -0800 Subject: [PATCH 42/72] Update main.rs --- apps/plumeimpactor/src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index 16590e9b..8448b063 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -18,6 +18,12 @@ pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env! async fn main() { _ = rustls::crypto::ring::default_provider().install_default().unwrap(); + // windows dark mode baller + #[cfg(target_os = "windows")] + { + wxdragon::app::set_appearance(wxdragon::Appearance::System); + } + let _ = wxdragon::main(|_| { frame::PlumeFrame::new().show(); }); From 6f2b307285db4ac51baea6f301949bd88f6a6c02 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 17 Nov 2025 09:49:54 -0800 Subject: [PATCH 43/72] temporary fix for LC --- apps/plumeimpactor/src/handlers.rs | 2 +- crates/grand_slam/src/certificate/identity.rs | 4 ++++ crates/grand_slam/src/utils/provision.rs | 7 ++++--- crates/grand_slam/src/utils/signer.rs | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 8ddd37d1..3b8e0fe9 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -199,7 +199,7 @@ impl PlumeFrameMessageHandler { if let Some(dialog) = &mut self.installation_progress_dialog { dialog.update(progress, message_opt.as_deref()); - if progress >= 100 { + if progress >= 90 { self.installation_progress_dialog = None; } } diff --git a/crates/grand_slam/src/certificate/identity.rs b/crates/grand_slam/src/certificate/identity.rs index 072a7ac2..a9f6e153 100644 --- a/crates/grand_slam/src/certificate/identity.rs +++ b/crates/grand_slam/src/certificate/identity.rs @@ -189,6 +189,10 @@ impl CertificateIdentity { Err(e) => { if let Error::DeveloperSession(code, _) = &e { if *code == 7460 { + // TODO: this isn't really the ideal way to revoke certificiates + // trying to match the machine id with another existing id + // because if they dont exist and still taking up slots, it would + // most likely error with too many certificates let certs = dev_session .qh_list_certs(team) .await? diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs index 7a683fc2..917a58d0 100644 --- a/crates/grand_slam/src/utils/provision.rs +++ b/crates/grand_slam/src/utils/provision.rs @@ -83,7 +83,7 @@ impl MobileProvision { pub fn merge_entitlements(&mut self, binary_path: PathBuf) -> Result<(), Error> { let macho = MachO::new(&binary_path)?; let binary_entitlements = macho.entitlements.ok_or(Error::ProvisioningEntitlementsUnknown)?; - + if let Some(Value::Array(other_groups)) = binary_entitlements.get("keychain-access-groups") { self.entitlements.insert("keychain-access-groups".to_string(), Value::Array(other_groups.clone())); } @@ -95,7 +95,8 @@ impl MobileProvision { let old_team_id = binary_entitlements .get("com.apple.developer.team-identifier") .and_then(Value::as_string) - .map(|s| s.to_owned()); + .map(|s| s.to_owned()) + .or(Some("AAAAA11111".to_string())); if let (Some(new_id), Some(old_id)) = (new_team_id.as_ref(), old_team_id.as_ref()) { if let Some(Value::Array(groups)) = self.entitlements.get_mut("keychain-access-groups") { @@ -108,7 +109,7 @@ impl MobileProvision { } } } - + Ok(()) } diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs index 881cc4c1..b40ea227 100644 --- a/crates/grand_slam/src/utils/signer.rs +++ b/crates/grand_slam/src/utils/signer.rs @@ -59,7 +59,7 @@ impl Signer { if let Some(bundle_executable) = bundle.get_executable() { let binary_path = bundle.dir().join(bundle_executable); - prov.merge_entitlements(binary_path)?; + prov.merge_entitlements(binary_path).ok(); // if it fails we can ignore } if self.settings.should_embed_provisioning { From 52727a78444543ff5ffbdd3d05b96e2254dac7f2 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 17 Nov 2025 17:46:18 -0800 Subject: [PATCH 44/72] notoar --- .github/workflows/build.yml | 27 ++++++++++++++++++++------- apps/plumeimpactor/src/frame.rs | 4 ++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e80efbdb..7483b534 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,6 +46,13 @@ jobs: sudo apt-get update sudo apt-get install libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev + - name: Certificates + uses: apple-actions/import-codesign-certs@v5 + if: runner.os == 'macOS' + with: + p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} + p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} + - name: Setup Windows dependencies if: runner.os == 'Windows' run: choco install strawberryperl make --no-progress @@ -65,27 +72,33 @@ jobs: if: runner.os == 'Linux' run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format appimage + mkdir build + cp -R target/release/bundle/appimage/plumeimpactor.AppImage build/ - name: Build & Bundle (macOS) if: runner.os == 'macOS' run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx mkdir build - cp -R target/release/bundle/*/*.app build/ - ln -s /Applications build/Applications - hdiutil create -volname plumeimpactor -srcfolder build -ov -format UDZO target/release/bundle/osx/plumeimpactor.dmg + cp -R target/release/bundle/osx/plumeimpactor.app build/dmg + codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app + ln -s /Applications build/dmg/Applications + hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/PlumeImpactor.dmg + xcrun notarytool submit build/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait + xcrun stapler staple build/PlumeImpactor.dmg - name: Build & Bundle (Windows) if: runner.os == 'Windows' run: | cargo build --bin plumeimpactor --release + mkdir build + cp target/release/plumeimpactor.exe build/ - name: Upload Bundles uses: actions/upload-artifact@v4 with: name: plumeimpactor-${{ matrix.platform }} path: | - target/release/bundle/appimage/*.AppImage - target/release/bundle/osx/*.dmg - target/release/bundle/msi/*.msi - target/release/*.exe + build/plumeimpactor.AppImage + build/PlumeImpactor.dmg + build/plumeimpactor.exe diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index d71b2726..771b6034 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -356,6 +356,10 @@ impl PlumeFrame { bundle.set_version(new_version).map_err(|e| format!("Failed to set new version: {}", e))?; } + if signer_settings.support_minimum_os_version { + bundle.set_info_plist_key("MinimumOSVersion", "7.0").map_err(|e| format!("Failed to set minimum OS version: {}", e))?; + } + if signer_settings.support_file_sharing { bundle.set_info_plist_key("UIFileSharingEnabled", true).map_err(|e| format!("Failed to set file sharing: {}", e))?; bundle.set_info_plist_key("UISupportsDocumentBrowser", true).map_err(|e| format!("Failed to set document opening: {}", e))?; From fb1d50c939ee75c99bccbe9a5e3b29892141f2f7 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 17 Nov 2025 18:08:33 -0800 Subject: [PATCH 45/72] Update build.yml --- .github/workflows/build.yml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7483b534..9b1dab7c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: sudo apt-get update sudo apt-get install libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev - - name: Certificates + - name: Setup macOS Certificates uses: apple-actions/import-codesign-certs@v5 if: runner.os == 'macOS' with: @@ -57,7 +57,7 @@ jobs: if: runner.os == 'Windows' run: choco install strawberryperl make --no-progress - - name: Setup MSVC environment + - name: Setup Windows MSVC environment if: runner.os == 'Windows' uses: ilammy/msvc-dev-cmd@v1 @@ -67,38 +67,34 @@ jobs: cargo fetch --locked || true cargo patch-crate --force cargo install cargo-bundle + mkdir -p build/out - name: Build & Bundle (Linux) if: runner.os == 'Linux' run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format appimage - mkdir build - cp -R target/release/bundle/appimage/plumeimpactor.AppImage build/ + cp -R target/release/bundle/appimage/*.AppImage build/out/ - name: Build & Bundle (macOS) if: runner.os == 'macOS' run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx - mkdir build cp -R target/release/bundle/osx/plumeimpactor.app build/dmg codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app ln -s /Applications build/dmg/Applications - hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/PlumeImpactor.dmg - xcrun notarytool submit build/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait - xcrun stapler staple build/PlumeImpactor.dmg + hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg + xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait + xcrun stapler staple build/out/PlumeImpactor.dmg - name: Build & Bundle (Windows) if: runner.os == 'Windows' run: | cargo build --bin plumeimpactor --release - mkdir build - cp target/release/plumeimpactor.exe build/ + cp target/release/plumeimpactor.exe build/out - name: Upload Bundles uses: actions/upload-artifact@v4 with: name: plumeimpactor-${{ matrix.platform }} path: | - build/plumeimpactor.AppImage - build/PlumeImpactor.dmg - build/plumeimpactor.exe + build/out/* From 01d5c0219366b3970135f724f87ade8db26e8e97 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 17 Nov 2025 18:48:18 -0800 Subject: [PATCH 46/72] lets try to debug this issue --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b1dab7c..869122ed 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -80,7 +80,7 @@ jobs: run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx cp -R target/release/bundle/osx/plumeimpactor.app build/dmg - codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app + codesign --deep --force --options runtime --sign "Developer ID Application: Lakhan Lothiyi (566RT33SA2)" build/dmg/plumeimpactor.app ln -s /Applications build/dmg/Applications hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait From 9bb4ed44fb8213bfabd40ba8cfb89c7078622009 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 17 Nov 2025 18:57:04 -0800 Subject: [PATCH 47/72] Revert "lets try to debug this issue" This reverts commit 01d5c0219366b3970135f724f87ade8db26e8e97. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 869122ed..9b1dab7c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -80,7 +80,7 @@ jobs: run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx cp -R target/release/bundle/osx/plumeimpactor.app build/dmg - codesign --deep --force --options runtime --sign "Developer ID Application: Lakhan Lothiyi (566RT33SA2)" build/dmg/plumeimpactor.app + codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app ln -s /Applications build/dmg/Applications hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait From 48711241fdf1de9d8e6e63a8f9cdeb954ac24841 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Mon, 17 Nov 2025 19:06:58 -0800 Subject: [PATCH 48/72] a --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9b1dab7c..c6fac5ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,8 +47,8 @@ jobs: sudo apt-get install libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev - name: Setup macOS Certificates - uses: apple-actions/import-codesign-certs@v5 if: runner.os == 'macOS' + uses: apple-actions/import-codesign-certs@v5 with: p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} @@ -63,6 +63,7 @@ jobs: - name: Build Dependencies run: | + security find-certificate -a -c "${{ secrets.DEV_ID_IDENTITY_NAME }}" cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force From 93d76b106e21f6f32efc0d9479e72e0561e056fa Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 03:05:29 -0800 Subject: [PATCH 49/72] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c6fac5ad..2a4e3051 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,6 @@ jobs: - name: Build Dependencies run: | - security find-certificate -a -c "${{ secrets.DEV_ID_IDENTITY_NAME }}" cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force @@ -79,6 +78,7 @@ jobs: - name: Build & Bundle (macOS) if: runner.os == 'macOS' run: | + certtool y | grep Developer \ID cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx cp -R target/release/bundle/osx/plumeimpactor.app build/dmg codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app From 4daa099fc66a92e04ce18f42fd55badddbff7e1d Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 03:15:11 -0800 Subject: [PATCH 50/72] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2a4e3051..10e24526 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: - name: Build & Bundle (macOS) if: runner.os == 'macOS' run: | - certtool y | grep Developer \ID + certtool y | grep 'Developer ID' cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx cp -R target/release/bundle/osx/plumeimpactor.app build/dmg codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app From bf96c5b68f04811704d5573f75c643898e732a14 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 03:33:30 -0800 Subject: [PATCH 51/72] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 10e24526..a1db5ddb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: - name: Build & Bundle (macOS) if: runner.os == 'macOS' run: | - certtool y | grep 'Developer ID' + certtool y | grep 'Developer ID' | tee /dev/stdout cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx cp -R target/release/bundle/osx/plumeimpactor.app build/dmg codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app From 9af8a1b13d7d4455685fd3bc719d391631b8d2d6 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 03:46:57 -0800 Subject: [PATCH 52/72] seperate workflow? --- .github/workflows/build.yml | 175 +++++++++++++++++++++++++--------- apps/plumeimpactor/Cargo.toml | 13 +++ 2 files changed, 144 insertions(+), 44 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1db5ddb..d7b8725d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,27 +3,15 @@ on: push: branches: - main - workflow_dispatch: - release: types: [published] jobs: - build: - strategy: - fail-fast: false - matrix: - platform: - - ubuntu-22.04 - - windows-latest - - macos-latest - - macos-15-intel - - runs-on: ${{ matrix.platform }} + build-linux: + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - name: Cache Rust dependencies uses: actions/cache@v4 with: @@ -31,36 +19,61 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ matrix.platform }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - + key: linux-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: linux-cargo- + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ (matrix.platform == 'macos-latest') && 'aarch64-apple-darwin' || '' }} - + - name: Install Linux dependencies - if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update sudo apt-get install libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev + + - name: Build Dependencies + run: | + cargo install patch-crate + cargo fetch --locked || true + cargo patch-crate --force + cargo install cargo-bundle + mkdir -p build/out + + - name: Build & Bundle (Linux) + run: | + cargo bundle --bin plumeimpactor --package plumeimpactor --release --format appimage + cp -R target/release/bundle/appimage/*.AppImage build/out/ + + - name: Upload Bundles + uses: actions/upload-artifact@v4 + with: + name: plumeimpactor-linux + path: build/out/* + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: macos-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: macos-cargo- + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-darwin + - name: Setup macOS Certificates - if: runner.os == 'macOS' uses: apple-actions/import-codesign-certs@v5 with: p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} - - name: Setup Windows dependencies - if: runner.os == 'Windows' - run: choco install strawberryperl make --no-progress - - - name: Setup Windows MSVC environment - if: runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1 - - name: Build Dependencies run: | cargo install patch-crate @@ -68,17 +81,56 @@ jobs: cargo patch-crate --force cargo install cargo-bundle mkdir -p build/out - - - name: Build & Bundle (Linux) - if: runner.os == 'Linux' + + - name: Build & Bundle (macOS) run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --release --format appimage - cp -R target/release/bundle/appimage/*.AppImage build/out/ + cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx + cp -R target/release/bundle/osx/plumeimpactor.app build/dmg + codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app + ln -s /Applications build/dmg/Applications + hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg + xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait + xcrun stapler staple build/out/PlumeImpactor.dmg + + - name: Upload Bundles + uses: actions/upload-artifact@v4 + with: + name: plumeimpactor-macos + path: build/out/* - - name: Build & Bundle (macOS) - if: runner.os == 'macOS' + build-macos-intel: + runs-on: macos-15-intel + steps: + - uses: actions/checkout@v4 + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: macos-intel-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: macos-intel-cargo- + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Setup macOS Certificates + uses: apple-actions/import-codesign-certs@v5 + with: + p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} + p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} + + - name: Build Dependencies + run: | + cargo install patch-crate + cargo fetch --locked || true + cargo patch-crate --force + cargo install cargo-bundle + mkdir -p build/out + + - name: Build & Bundle (macOS Intel) run: | - certtool y | grep 'Developer ID' | tee /dev/stdout cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx cp -R target/release/bundle/osx/plumeimpactor.app build/dmg codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app @@ -86,16 +138,51 @@ jobs: hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait xcrun stapler staple build/out/PlumeImpactor.dmg + + - name: Upload Bundles + uses: actions/upload-artifact@v4 + with: + name: plumeimpactor-macos-intel + path: build/out/* + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Cache Rust dependencies + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: windows-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: windows-cargo- + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Setup Windows dependencies + run: choco install strawberryperl make --no-progress + + - name: Setup Windows MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + + - name: Build Dependencies + run: | + cargo install patch-crate + cargo fetch --locked || true + cargo patch-crate --force + cargo install cargo-bundle + mkdir -p build/out + - name: Build & Bundle (Windows) - if: runner.os == 'Windows' run: | cargo build --bin plumeimpactor --release cp target/release/plumeimpactor.exe build/out - + - name: Upload Bundles uses: actions/upload-artifact@v4 with: - name: plumeimpactor-${{ matrix.platform }} - path: | - build/out/* + name: plumeimpactor-windows + path: build/out/* diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index 04cacc1f..a0f3b678 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -7,6 +7,19 @@ authors.workspace = true license.workspace = true repository.workspace = true +# We use `opt-level = "s"` as it significantly reduces binary size. +# We could then use the `#[optimize(speed)]` attribute for spot optimizations. +# Unfortunately, that attribute currently doesn't work on intrinsics such as memset. +[profile.release] +codegen-units = 1 # reduces binary size by ~2% +debug = "full" # No one needs an undebuggable release binary +lto = true # reduces binary size by ~14% +opt-level = "s" # reduces binary size by ~25% +panic = "abort" # reduces binary size by ~50% in combination with -Zbuild-std-features=panic_immediate_abort +split-debuginfo = "packed" # generates a separate *.dwp/*.dSYM so the binary can get stripped +strip = "symbols" # See split-debuginfo - allows us to drop the size by ~65% +incremental = true # Improves re-compile times + [dependencies] idevice.workspace = true zip.workspace = true From ebda81188c21d1e77e4582a60102213114aa0648 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 04:47:03 -0800 Subject: [PATCH 53/72] oh my god?? --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7b8725d..5fffa2ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,11 +81,12 @@ jobs: cargo patch-crate --force cargo install cargo-bundle mkdir -p build/out + mkdir -p build/dmg - name: Build & Bundle (macOS) run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx - cp -R target/release/bundle/osx/plumeimpactor.app build/dmg + cp -R target/release/bundle/osx/plumeimpactor.app build/dmg/plumeimpactor.app codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app ln -s /Applications build/dmg/Applications hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg @@ -128,11 +129,12 @@ jobs: cargo patch-crate --force cargo install cargo-bundle mkdir -p build/out + mkdir -p build/dmg - name: Build & Bundle (macOS Intel) run: | cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx - cp -R target/release/bundle/osx/plumeimpactor.app build/dmg + cp -R target/release/bundle/osx/plumeimpactor.app build/dmg/plumeimpactor.app codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app ln -s /Applications build/dmg/Applications hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg From 95c44835e0dfdebc71b9e7469f25cb82750e083e Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 09:47:04 -0800 Subject: [PATCH 54/72] improve actions --- .github/workflows/build.yml | 199 ++++++++++-------- apps/plumeimpactor/Cargo.toml | 2 +- apps/plumeimpactor/build.rs | 11 +- .../macos/Impactor.app/Contents/Info.plist | 30 +++ package/macos/entitlements.plist | 14 +- 5 files changed, 162 insertions(+), 94 deletions(-) create mode 100644 package/macos/Impactor.app/Contents/Info.plist diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5fffa2ad..aa526bf0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,8 +4,10 @@ on: branches: - main workflow_dispatch: - release: - types: [published] + +env: + BINARY_NAME: plumeimpactor + BUNDLE_NAME: Impactor jobs: build-linux: @@ -21,15 +23,15 @@ jobs: target key: linux-cargo-${{ hashFiles('**/Cargo.lock') }} restore-keys: linux-cargo- - + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - + - name: Install Linux dependencies run: | sudo apt-get update sudo apt-get install libgtk-3-dev libpng-dev libjpeg-dev libgl1-mesa-dev libglu1-mesa-dev libxkbcommon-dev libexpat1-dev libtiff-dev - + - name: Build Dependencies run: | cargo install patch-crate @@ -37,20 +39,20 @@ jobs: cargo patch-crate --force cargo install cargo-bundle mkdir -p build/out - + - name: Build & Bundle (Linux) run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --release --format appimage + cargo bundle --bin ${{ env.BINARY_NAME }} --package ${{ env.BINARY_NAME }} --release --format appimage cp -R target/release/bundle/appimage/*.AppImage build/out/ - + - name: Upload Bundles uses: actions/upload-artifact@v4 with: - name: plumeimpactor-linux + name: ${{ env.BINARY_NAME }}-linux path: build/out/* - build-macos: - runs-on: macos-latest + build-windows: + runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Cache Rust dependencies @@ -60,47 +62,38 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: macos-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: macos-cargo- - + key: windows-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: windows-cargo- + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - with: - targets: aarch64-apple-darwin - - - name: Setup macOS Certificates - uses: apple-actions/import-codesign-certs@v5 - with: - p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} - p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} - + + - name: Setup Windows dependencies + run: choco install strawberryperl make --no-progress + + - name: Setup Windows MSVC environment + uses: ilammy/msvc-dev-cmd@v1 + - name: Build Dependencies run: | cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force - cargo install cargo-bundle mkdir -p build/out - mkdir -p build/dmg - - - name: Build & Bundle (macOS) + + - name: Build & Bundle (Windows) run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx - cp -R target/release/bundle/osx/plumeimpactor.app build/dmg/plumeimpactor.app - codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app - ln -s /Applications build/dmg/Applications - hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg - xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait - xcrun stapler staple build/out/PlumeImpactor.dmg - + cargo build --bin ${{ env.BINARY_NAME }} --release + cp target/release/${{ env.BINARY_NAME }}.exe build/out + - name: Upload Bundles uses: actions/upload-artifact@v4 with: - name: plumeimpactor-macos + name: ${{ env.BINARY_NAME }}-windows path: build/out/* - build-macos-intel: - runs-on: macos-15-intel + build-macos-arm: + runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Cache Rust dependencies @@ -110,45 +103,35 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: macos-intel-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: macos-intel-cargo- - + key: macos-arm-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: macos-arm-cargo- + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - - - name: Setup macOS Certificates - uses: apple-actions/import-codesign-certs@v5 with: - p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} - p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} - + targets: aarch64-apple-darwin + - name: Build Dependencies run: | cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force - cargo install cargo-bundle mkdir -p build/out - mkdir -p build/dmg - - - name: Build & Bundle (macOS Intel) + + - name: Build (macOS ARM) run: | - cargo bundle --bin plumeimpactor --package plumeimpactor --release --format osx - cp -R target/release/bundle/osx/plumeimpactor.app build/dmg/plumeimpactor.app - codesign --deep --force --options runtime --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/plumeimpactor.app - ln -s /Applications build/dmg/Applications - hdiutil create -volname plumeimpactor -srcfolder build/dmg -ov -format UDZO build/out/PlumeImpactor.dmg - xcrun notarytool submit build/out/PlumeImpactor.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait - xcrun stapler staple build/out/PlumeImpactor.dmg - - - name: Upload Bundles + cargo build --bin ${{ env.BINARY_NAME }} --release + strip target/release/${{ env.BINARY_NAME }} + cp target/release/${{ env.BINARY_NAME }} build/out/${{ env.BINARY_NAME }}-arm + + - name: Upload ARM Slice uses: actions/upload-artifact@v4 with: - name: plumeimpactor-macos-intel - path: build/out/* + name: ${{ env.BINARY_NAME }}-macos-slice-arm + path: build/out/${{ env.BINARY_NAME }}-arm - build-windows: - runs-on: windows-latest + build-macos-intel: + runs-on: macos-15-intel steps: - uses: actions/checkout@v4 - name: Cache Rust dependencies @@ -158,33 +141,83 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: windows-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: windows-cargo- - + key: macos-intel-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: macos-intel-cargo- + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - - - name: Setup Windows dependencies - run: choco install strawberryperl make --no-progress - - - name: Setup Windows MSVC environment - uses: ilammy/msvc-dev-cmd@v1 - + - name: Build Dependencies run: | cargo install patch-crate cargo fetch --locked || true cargo patch-crate --force - cargo install cargo-bundle mkdir -p build/out - - - name: Build & Bundle (Windows) + + - name: Build (macOS Intel) run: | - cargo build --bin plumeimpactor --release - cp target/release/plumeimpactor.exe build/out - - - name: Upload Bundles + cargo build --bin ${{ env.BINARY_NAME }} --release + strip target/release/${{ env.BINARY_NAME }} + cp target/release/${{ env.BINARY_NAME }} build/out/${{ env.BINARY_NAME }}-intel + + - name: Upload Intel Slice uses: actions/upload-artifact@v4 with: - name: plumeimpactor-windows - path: build/out/* + name: ${{ env.BINARY_NAME }}-macos-slice-intel + path: build/out/${{ env.BINARY_NAME }}-intel + + build-macos-universal: + runs-on: macos-latest + needs: [build-macos-arm, build-macos-intel] + steps: + - uses: actions/checkout@v4 + + - name: Get ARM Slice + uses: actions/download-artifact@v4 + with: + name: ${{ env.BINARY_NAME }}-macos-slice-arm + path: build/slices + + - name: Get Intel Slice + uses: actions/download-artifact@v4 + with: + name: ${{ env.BINARY_NAME }}-macos-slice-intel + path: build/slices + + - name: Setup Certificates + uses: apple-actions/import-codesign-certs@v5 + with: + p12-file-base64: ${{ secrets.DEV_ID_P12_BASE64 }} + p12-password: ${{ secrets.DEV_ID_P12_PASSWORD }} + + - name: Create Universal Binary + run: | + mkdir -p build/universal + lipo -create -output build/universal/${{ env.BINARY_NAME }} build/slices/${{ env.BINARY_NAME }}-arm build/slices/${{ env.BINARY_NAME }}-intel + + - name: Bundle + run: | + cp -R package/macos/${{ env.BUNDLE_NAME }}.app build/dmg/${{ env.BUNDLE_NAME }}.app + mv build/universal/${{ env.BINARY_NAME }} build/dmg/${{ env.BUNDLE_NAME }}.app/Contents/MacOS/${{ env.BINARY_NAME }} + + - name: Codesign + run: | + codesign --deep --force --options runtime \ + --entitlements package/macos/entitlements.plist \ + --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/${{ env.BUNDLE_NAME }}.app + + - name: Create DMG + run: | + ln -s /Applications build/dmg/Applications + hdiutil create -volname ${{ env.BUNDLE_NAME }} -srcfolder build/dmg -ov -format UDZO build/out/${{ env.BUNDLE_NAME }}.dmg + + - name: Notarize DMG + run: | + xcrun notarytool submit build/out/${{ env.BUNDLE_NAME }}.dmg --apple-id "${{ secrets.APPLE_ID_EMAIL }}" --password "${{ secrets.APPLE_ID_PASSWORD }}" --team-id "${{ secrets.APPLE_ID_TEAM }}" --wait + xcrun stapler staple build/out/${{ env.BUNDLE_NAME }}.dmg + + - name: Upload Universal DMG + uses: actions/upload-artifact@v4 + with: + name: ${{ env.BINARY_NAME }}-macos-universal + path: build/out/${{ env.BUNDLE_NAME }}.dmg diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index a0f3b678..f7fb7db1 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -35,5 +35,5 @@ rustls = { version = "0.23.32", features = ["ring"] } keyring = { version = "3.6.3", default-features = false, features = ["windows-native", "apple-native", "linux-native"] } -[build-dependencies] +[target.'cfg(windows)'.build-dependencies] embed-manifest = "1.4" diff --git a/apps/plumeimpactor/build.rs b/apps/plumeimpactor/build.rs index d6aa5aae..d4319d03 100644 --- a/apps/plumeimpactor/build.rs +++ b/apps/plumeimpactor/build.rs @@ -1,6 +1,4 @@ -use embed_manifest::manifest::{ActiveCodePage, Setting, SupportedOS::*}; -use embed_manifest::{embed_manifest, new_manifest}; - +#[cfg(windows)] fn main() { println!("cargo:rerun-if-changed=build.rs"); let target = std::env::var("TARGET").unwrap_or_default(); @@ -11,7 +9,14 @@ fn main() { } } +#[cfg(not(windows))] +fn main() {} + +#[cfg(windows)] fn embed_windows_manifest(name: &str) { + use embed_manifest::manifest::{ActiveCodePage, Setting, SupportedOS::*}; + use embed_manifest::{embed_manifest, new_manifest}; + let manifest = new_manifest(name) .supported_os(Windows7..=Windows10) .active_code_page(ActiveCodePage::Utf8) diff --git a/package/macos/Impactor.app/Contents/Info.plist b/package/macos/Impactor.app/Contents/Info.plist new file mode 100644 index 00000000..3b971bdb --- /dev/null +++ b/package/macos/Impactor.app/Contents/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + plumeimpactor + CFBundleExecutable + plumeimpactor + CFBundleIdentifier + bucket.plumeimpactor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + plumeimpactor + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.1 + CFBundleVersion + 1 + CSResourcesFileMapped + + LSRequiresCarbon + + NSHighResolutionCapable + + + diff --git a/package/macos/entitlements.plist b/package/macos/entitlements.plist index 56e89b3b..4f388397 100644 --- a/package/macos/entitlements.plist +++ b/package/macos/entitlements.plist @@ -1,10 +1,10 @@ - - keychain-access-groups - - $(AppIdentifierPrefix)$(CFBundleIdentifier) - - - \ No newline at end of file + + keychain-access-groups + + 566RT33SA2.bucket.plumeimpactor + + + From c31bf5c0e7ab21c8cec09eec2972994443a4ce4f Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 10:11:47 -0800 Subject: [PATCH 55/72] fix ts --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa526bf0..15886ae0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -153,6 +153,7 @@ jobs: cargo fetch --locked || true cargo patch-crate --force mkdir -p build/out + mkdir -p build/dmg - name: Build (macOS Intel) run: | From f4977feb99aedd4a651d5b6a5b166bd5c5a75f0d Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 10:19:03 -0800 Subject: [PATCH 56/72] FIX TS AGAIN --- .github/workflows/build.yml | 2 +- apps/plumeimpactor/src/frame.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 15886ae0..725a9703 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -153,7 +153,6 @@ jobs: cargo fetch --locked || true cargo patch-crate --force mkdir -p build/out - mkdir -p build/dmg - name: Build (macOS Intel) run: | @@ -198,6 +197,7 @@ jobs: - name: Bundle run: | + mkdir -p build/dmg cp -R package/macos/${{ env.BUNDLE_NAME }}.app build/dmg/${{ env.BUNDLE_NAME }}.app mv build/universal/${{ env.BINARY_NAME }} build/dmg/${{ env.BUNDLE_NAME }}.app/Contents/MacOS/${{ env.BINARY_NAME }} diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 771b6034..a0186264 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -7,7 +7,7 @@ use grand_slam::certificate::CertificateIdentity; use grand_slam::{AnisetteConfiguration, BundleType, Certificate, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; -use grand_slam::utils::{PlistInfoTrait, SignerSettings}; +use grand_slam::utils::PlistInfoTrait; use idevice::utils::installation; use wxdragon::prelude::*; From c875093b144758f09040db303da46163e156d3ed Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 10:33:24 -0800 Subject: [PATCH 57/72] FIX TS AGAINN --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 725a9703..8313ce99 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -199,6 +199,7 @@ jobs: run: | mkdir -p build/dmg cp -R package/macos/${{ env.BUNDLE_NAME }}.app build/dmg/${{ env.BUNDLE_NAME }}.app + mkdir -p build/dmg/${{ env.BUNDLE_NAME }}.app/Contents/MacOS mv build/universal/${{ env.BINARY_NAME }} build/dmg/${{ env.BUNDLE_NAME }}.app/Contents/MacOS/${{ env.BINARY_NAME }} - name: Codesign From 32edb2185159995af4b1d22c27e0c1745be5c0dc Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 10:54:51 -0800 Subject: [PATCH 58/72] bryh --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8313ce99..da535a1f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -204,7 +204,7 @@ jobs: - name: Codesign run: | - codesign --deep --force --options runtime \ + codesign --deep --force --options runtime \ --entitlements package/macos/entitlements.plist \ --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/${{ env.BUNDLE_NAME }}.app From b120994c34c1daa0a57e6af3c012d0ee314d7dcc Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 11:06:32 -0800 Subject: [PATCH 59/72] HOPEFULLYLYY --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index da535a1f..f7f3aa62 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -210,6 +210,7 @@ jobs: - name: Create DMG run: | + mkdir -p build/out ln -s /Applications build/dmg/Applications hdiutil create -volname ${{ env.BUNDLE_NAME }} -srcfolder build/dmg -ov -format UDZO build/out/${{ env.BUNDLE_NAME }}.dmg From 0e7a3cf996a3fd960171b755dd9087fdbc636293 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 11:30:55 -0800 Subject: [PATCH 60/72] chmod ts --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7f3aa62..80d8766d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -194,6 +194,7 @@ jobs: run: | mkdir -p build/universal lipo -create -output build/universal/${{ env.BINARY_NAME }} build/slices/${{ env.BINARY_NAME }}-arm build/slices/${{ env.BINARY_NAME }}-intel + chmod +x build/universal/${{ env.BINARY_NAME }} - name: Bundle run: | From 57a9c5741d4da23868a3e15ce5d64c51dc45a16b Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Tue, 18 Nov 2025 15:17:53 -0800 Subject: [PATCH 61/72] last times the charm! --- .github/workflows/build.yml | 1 - apps/plumeimpactor/Cargo.toml | 2 +- apps/plumeimpactor/src/keychain.rs | 44 +++++++++++++++--------------- apps/plumeimpactor/src/main.rs | 6 ---- package/macos/entitlements.plist | 10 ------- 5 files changed, 23 insertions(+), 40 deletions(-) delete mode 100644 package/macos/entitlements.plist diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80d8766d..480a94db 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -206,7 +206,6 @@ jobs: - name: Codesign run: | codesign --deep --force --options runtime \ - --entitlements package/macos/entitlements.plist \ --sign "${{ secrets.DEV_ID_IDENTITY_NAME }}" build/dmg/${{ env.BUNDLE_NAME }}.app - name: Create DMG diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index f7fb7db1..8f78d9fe 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -35,5 +35,5 @@ rustls = { version = "0.23.32", features = ["ring"] } keyring = { version = "3.6.3", default-features = false, features = ["windows-native", "apple-native", "linux-native"] } -[target.'cfg(windows)'.build-dependencies] +[target.'cfg(target_os = "windows")'.build-dependencies] embed-manifest = "1.4" diff --git a/apps/plumeimpactor/src/keychain.rs b/apps/plumeimpactor/src/keychain.rs index ebf57672..8b40681e 100644 --- a/apps/plumeimpactor/src/keychain.rs +++ b/apps/plumeimpactor/src/keychain.rs @@ -7,29 +7,29 @@ const KEYRING_PASS: &str = "Apple ID Password"; pub struct AccountCredentials; impl AccountCredentials { - pub fn set_credentials(&self, email: String, password: String) -> Result<(), Error> { - let entry_email = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; - let entry_pass = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; - entry_email.set_secret(email.as_bytes())?; - entry_pass.set_secret(password.as_bytes())?; - Ok(()) - } + pub fn set_credentials(&self, email: String, password: String) -> Result<(), Error> { + let entry_email = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; + let entry_pass = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; + entry_email.set_secret(email.as_bytes())?; + entry_pass.set_secret(password.as_bytes())?; + Ok(()) + } - pub fn get_email(&self) -> Result { - let entry = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; - entry.get_password() - } + pub fn get_email(&self) -> Result { + let entry = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; + entry.get_password() + } - pub fn get_password(&self) -> Result { - let entry = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; - entry.get_password() - } + pub fn get_password(&self) -> Result { + let entry = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; + entry.get_password() + } - pub fn delete_password(&self) -> Result<(), Error> { - let entry_email = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; - let entry_pass = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; - entry_email.delete_credential()?; - entry_pass.delete_credential()?; - Ok(()) - } + pub fn delete_password(&self) -> Result<(), Error> { + let entry_email = Entry::new(KEYRING_SERVICE, KEYRING_EMAIL)?; + let entry_pass = Entry::new(KEYRING_SERVICE, KEYRING_PASS)?; + entry_email.delete_credential()?; + entry_pass.delete_credential()?; + Ok(()) + } } diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index 8448b063..16590e9b 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -18,12 +18,6 @@ pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env! async fn main() { _ = rustls::crypto::ring::default_provider().install_default().unwrap(); - // windows dark mode baller - #[cfg(target_os = "windows")] - { - wxdragon::app::set_appearance(wxdragon::Appearance::System); - } - let _ = wxdragon::main(|_| { frame::PlumeFrame::new().show(); }); diff --git a/package/macos/entitlements.plist b/package/macos/entitlements.plist deleted file mode 100644 index 4f388397..00000000 --- a/package/macos/entitlements.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - keychain-access-groups - - 566RT33SA2.bucket.plumeimpactor - - - From 3b6ea9d45f572908e26674b219d8b6176b920486 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Wed, 19 Nov 2025 15:15:16 -0800 Subject: [PATCH 62/72] refactor certificate --- apps/plumeimpactor/src/frame.rs | 49 ++-- apps/plumeimpactor/src/main.rs | 2 - crates/grand_slam/src/certificate/identity.rs | 272 ------------------ crates/grand_slam/src/certificate/mod.rs | 3 - crates/grand_slam/src/developer/qh/certs.rs | 4 +- crates/grand_slam/src/lib.rs | 11 +- crates/grand_slam/src/utils/certificate.rs | 188 +++++++++++- crates/grand_slam/src/utils/mod.rs | 2 +- crates/grand_slam/src/utils/signer.rs | 6 +- 9 files changed, 212 insertions(+), 325 deletions(-) delete mode 100644 crates/grand_slam/src/certificate/identity.rs delete mode 100644 crates/grand_slam/src/certificate/mod.rs diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index a0186264..7cf7e2fa 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -3,11 +3,11 @@ use std::path::PathBuf; use std::rc::Rc; use std::{ptr, thread}; -use grand_slam::certificate::CertificateIdentity; -use grand_slam::{AnisetteConfiguration, BundleType, Certificate, MachO, MobileProvision, Signer}; +use grand_slam::CertificateIdentity; +use grand_slam::{AnisetteConfiguration, BundleType, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; -use grand_slam::utils::PlistInfoTrait; +use grand_slam::utils::{PlistInfoTrait}; use idevice::utils::installation; use wxdragon::prelude::*; @@ -16,13 +16,15 @@ use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection, UsbmuxdListenEvent}; use tokio::runtime::Builder; use tokio::sync::mpsc; -use crate::{APP_NAME, get_data_path}; +use crate::get_data_path; use crate::handlers::{PlumeFrameMessage, PlumeFrameMessageHandler}; use crate::keychain::AccountCredentials; use crate::pages::login::{AccountDialog, LoginDialog}; use crate::pages::{DefaultPage, InstallPage, create_account_dialog, create_default_page, create_install_page, create_login_dialog, WINDOW_SIZE}; use crate::utils::{Device, Package}; +pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env!("CARGO_PKG_VERSION")); + pub struct PlumeFrame { pub frame: Frame, pub default_page: DefaultPage, @@ -329,7 +331,20 @@ impl PlumeFrame { let team_id = &teams.get(0) .ok_or("No teams available for the Apple ID account.")? .team_id; - + + let cert_identity: CertificateIdentity = if signer_settings.export_ipa { + CertificateIdentity { cert: None, key: None } + } else { + let cert_identity = CertificateIdentity::new_with_session( + &session, + get_data_path(), + None, + team_id, + ).await.map_err(|e| e.to_string())?; + + cert_identity + }; + session.qh_ensure_device( team_id, &device.name, @@ -389,16 +404,6 @@ impl PlumeFrame { sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); - let identity = CertificateIdentity::new( - &get_data_path(), - &session, - "PLUME".to_string(), - "AltStore".to_string(), - team_id - ) - .await - .map_err(|e| format!("Failed to create certificate identity: {}", e))?; - let mut provisionings: Vec = Vec::new(); if !signer_settings.export_ipa { @@ -473,21 +478,9 @@ impl PlumeFrame { } sender_clone.send(PlumeFrameMessage::InstallProgress(50, Some(format!("Signing {}...", bundle.get_name().unwrap_or_default())))).ok(); - - let mut certificate: Option = None; - - if !signer_settings.export_ipa { - let certificate_paths = vec![ - identity.get_certificate_file_path().to_path_buf(), - identity.get_private_key_file_path().to_path_buf(), - ]; - - certificate = Some(Certificate::new(Some(certificate_paths)) - .map_err(|e| format!("Failed to create Certificate: {}", e))?); - } let signer = Signer::new( - certificate, + Some(cert_identity), signer_settings.clone(), provisionings, ); diff --git a/apps/plumeimpactor/src/main.rs b/apps/plumeimpactor/src/main.rs index 16590e9b..0a2304aa 100644 --- a/apps/plumeimpactor/src/main.rs +++ b/apps/plumeimpactor/src/main.rs @@ -12,8 +12,6 @@ use std::{ path::{Path, PathBuf} }; -pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env!("CARGO_PKG_VERSION")); - #[tokio::main] async fn main() { _ = rustls::crypto::ring::default_provider().install_default().unwrap(); diff --git a/crates/grand_slam/src/certificate/identity.rs b/crates/grand_slam/src/certificate/identity.rs deleted file mode 100644 index a9f6e153..00000000 --- a/crates/grand_slam/src/certificate/identity.rs +++ /dev/null @@ -1,272 +0,0 @@ -use sha1::{Sha1, Digest}; -use hex; - -use rsa::{ - RsaPrivateKey, - RsaPublicKey, - pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey}, - pkcs8::{DecodePrivateKey, EncodePrivateKey}, -}; -use rand::rngs::OsRng; -use rcgen::{CertificateParams, Certificate, KeyPair, DnType, PKCS_RSA_SHA256}; -use x509_certificate::X509Certificate; - -use pem_rfc7468::{encode_string, LineEnding}; - -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use crate::Error; -use crate::developer::{DeveloperSession}; - -#[derive(Debug, Clone)] -pub struct CertificateIdentity { - pub certificate: Option, - pub private_key: RsaPrivateKey, - pub key_file: PathBuf, - pub cert_file: PathBuf, - pub machine_name: String, -} - -impl CertificateIdentity { - pub async fn new( - configuration_path: &Path, - dev_session: &DeveloperSession, - apple_id: String, - machine_name: String, - team: &str, - ) -> Result { - let mut hasher = Sha1::new(); - hasher.update(apple_id.as_bytes()); - let hash_string = hex::encode(hasher.finalize()).to_lowercase(); - - let key_path = configuration_path.join("keys").join(hash_string); - fs::create_dir_all(&key_path)?; - - let key_file = key_path.join("key.pem"); - let cert_file = key_path.join("cert.pem"); - - // --- Load or generate key --- - let private_key = if key_file.exists() { - let pem = fs::read_to_string(&key_file) - .map_err(|e| Error::Certificate(format!("Failed to read key: {e}")))?; - RsaPrivateKey::from_pkcs8_pem(&pem) - .map_err(|e| Error::Certificate(format!("Failed to parse private key: {e}")))? - } else { - let mut rng = OsRng; - let key = RsaPrivateKey::new(&mut rng, 2048) - .map_err(|e| Error::Certificate(format!("Failed to generate key: {e}")))?; - - let pem = key - .to_pkcs8_pem(Default::default()) - .map_err(|e| Error::Certificate(format!("Failed to encode key: {e}")))? - .to_string(); - - fs::write(&key_file, pem)?; - - key - }; - - let mut ci = CertificateIdentity { - certificate: None, - private_key, - key_file, - cert_file, - machine_name, - }; - - // --- Try to find existing certificate --- - if let Ok(cert) = ci.find_matching_certificate(dev_session, team).await { - let pem = encode_string( - "CERTIFICATE", - LineEnding::LF, - cert.encode_der().map_err(|e| Error::Certificate(format!("{e}")))?.as_slice(), - ).unwrap(); - - fs::write(&ci.cert_file, pem)?; - - ci.certificate = Some(cert); - return Ok(ci); - } - - // --- Request new certificate --- - ci.request_new_certificate(dev_session, team).await?; - - Ok(ci) - } - - async fn find_matching_certificate( - &self, - dev_session: &DeveloperSession, - team: &str, - ) -> Result { - let certs = dev_session - .qh_list_certs(team) - .await? - .certificates; - // Our RSA public key (PKCS#1 DER) - let our_pub_pkcs1_der = self.private_key - .to_public_key() - .to_pkcs1_der() - .map_err(|e| Error::Certificate(format!("Failed to encode public key (pkcs1): {e}")))? - .as_bytes() - .to_vec(); - - for cert_meta in certs.iter().filter(|c| c.machine_name == Some(self.machine_name.clone())) { - if let Ok(cert) = X509Certificate::from_der(&cert_meta.cert_content) { - // Extract BIT STRING containing PKCS#1 public key - let bit_string = &cert.tbs_certificate().subject_public_key_info.subject_public_key; - let raw = bit_string.octet_slice().unwrap_or_default(); - if raw.is_empty() { - continue; - } - // First byte is number of unused bits (should be 0 for public key bit strings) - let unused_bits = raw[0]; - if unused_bits != 0 { - continue; - } - let pkcs1_bytes = &raw[1..]; - if let Ok(cert_pub) = RsaPublicKey::from_pkcs1_der(pkcs1_bytes) { - let cert_pub_pkcs1_der = cert_pub - .to_pkcs1_der() - .map_err(|e| Error::Certificate(format!("Failed to re-encode cert public key: {e}")))? - .as_bytes() - .to_vec(); - if cert_pub_pkcs1_der == our_pub_pkcs1_der { - return Ok(cert); - } - } - } - } - - Err(Error::Certificate("No matching certificate found".into())) - } - - async fn request_new_certificate( - &mut self, - dev_session: &DeveloperSession, - team: &str, - ) -> Result<(), Error> { - // Convert RSA private key → PKCS8 DER → rcgen KeyPair - let pkcs8 = self.private_key - .to_pkcs8_der() - .map_err(|e| Error::Certificate(format!("Failed to encode pkcs8: {e}")))?; - - let keypair = KeyPair::from_der(pkcs8.as_bytes()) - .map_err(|e| Error::Certificate(format!("Failed to load rcgen key: {e}")))?; - - // --- Build CSR --- - let mut params = CertificateParams::new(vec![]); - // Use an RSA signature algorithm to match the RSA key pair - params.alg = &PKCS_RSA_SHA256; - params.key_pair = Some(keypair); - - let dn = &mut params.distinguished_name; - dn.push(DnType::CountryName, "US"); - dn.push(DnType::StateOrProvinceName, "STATE"); - dn.push(DnType::LocalityName, "LOCAL"); - dn.push(DnType::OrganizationName, "ORGNIZATION"); - dn.push(DnType::CommonName, "CN"); - - let csr = Certificate::from_params(params) - .map_err(|e| Error::Certificate(format!("Failed to build CSR params: {e}")))?; - - let csr_pem = csr.serialize_request_pem() - .map_err(|e| Error::Certificate(format!("Failed to serialize CSR: {e}")))?; - - let cert_id = loop { - match dev_session - .qh_submit_cert_csr( - team, - csr_pem.clone(), - &self.machine_name.clone(), - ) - .await - { - Ok(id) => break id, - Err(e) => { - if let Error::DeveloperSession(code, _) = &e { - if *code == 7460 { - // TODO: this isn't really the ideal way to revoke certificiates - // trying to match the machine id with another existing id - // because if they dont exist and still taking up slots, it would - // most likely error with too many certificates - let certs = dev_session - .qh_list_certs(team) - .await? - .certificates; - - if let Some(target) = certs.iter().find(|c| c.machine_name.as_deref() == Some(self.machine_name.as_ref())) { - let cid = target.serial_number.clone(); - dev_session - .qh_revoke_cert(team, &cid) - .await - .map_err(|err| { - Error::Certificate(format!( - "Failed to revoke certificate {cid}: {err:?}" - )) - })?; - // Retry submit after revocation - continue; - } else { - return Err(Error::Certificate( - "Too many certificates".into(), - )); - } - } - } - return Err(Error::Certificate(format!("Submit CSR failed: {:?}", e))); - } - } - }; - - // --- Fetch new certificate from Apple --- - let certs = dev_session - .qh_list_certs(team) - .await?.certificates; - - let found = certs - .iter() - .find(|c| c.certificate_id == cert_id.cert_request.certificate_id) - .ok_or_else(|| Error::Certificate("Certificate not found after submission".into()))?; - - let parsed = X509Certificate::from_der(&found.cert_content) - .map_err(|e| Error::Certificate(format!("Failed to parse DER: {e}")))?; - - // Save PEM - let pem = encode_string( - "CERTIFICATE", - LineEnding::LF, - found.cert_content.as_ref(), - ).unwrap(); - - fs::write(&self.cert_file, pem)?; - - self.certificate = Some(parsed); - - Ok(()) - } - - pub fn get_certificate_file_path(&self) -> &Path { - &self.cert_file - } - - pub fn get_private_key_file_path(&self) -> &Path { - &self.key_file - } - - pub fn get_serial_number(&self) -> Result { - let cert = self.certificate.as_ref() - .ok_or_else(|| Error::Certificate("No certificate loaded".into()))?; - - let serial = cert - .tbs_certificate() - .serial_number - .clone() - .into_bytes(); - - Ok(hex::encode(serial).trim_start_matches('0').to_string()) - } -} diff --git a/crates/grand_slam/src/certificate/mod.rs b/crates/grand_slam/src/certificate/mod.rs deleted file mode 100644 index 9a7fd750..00000000 --- a/crates/grand_slam/src/certificate/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod identity; - -pub use identity::CertificateIdentity; diff --git a/crates/grand_slam/src/developer/qh/certs.rs b/crates/grand_slam/src/developer/qh/certs.rs index 06650d81..ec5d9478 100644 --- a/crates/grand_slam/src/developer/qh/certs.rs +++ b/crates/grand_slam/src/developer/qh/certs.rs @@ -68,7 +68,7 @@ pub struct CsrResponse { } #[allow(dead_code)] -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct Cert { pub name: String, @@ -112,7 +112,7 @@ pub struct Csr { } #[allow(dead_code)] -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct CertType { certificate_type_display_id: String, diff --git a/crates/grand_slam/src/lib.rs b/crates/grand_slam/src/lib.rs index 9f4fc525..6aa808bd 100644 --- a/crates/grand_slam/src/lib.rs +++ b/crates/grand_slam/src/lib.rs @@ -1,6 +1,5 @@ pub mod auth; pub mod developer; -pub mod certificate; pub mod utils; use plist::Dictionary; @@ -11,7 +10,7 @@ use crate::auth::account::request::RequestType; pub use omnisette::AnisetteConfiguration; pub use utils::MachO; pub use utils::MobileProvision; -pub use utils::Certificate; +pub use utils::CertificateIdentity; pub use utils::Signer; pub use utils::Bundle; pub use utils::BundleType; @@ -70,4 +69,12 @@ pub enum Error { Anisette(#[from] omnisette::AnisetteError), #[error("Serde JSON error: {0}")] SerdeJson(#[from] serde_json::Error), + #[error("RSA error: {0}")] + Rsa(#[from] rsa::Error), + #[error("PKCS1 RSA error: {0}")] + PKCS1(#[from] rsa::pkcs1::Error), + #[error("PKCS8 RSA error: {0}")] + PKCS8(#[from] rsa::pkcs8::Error), + #[error("RCGen error: {0}")] + RcGen(#[from] rcgen::RcgenError), } diff --git a/crates/grand_slam/src/utils/certificate.rs b/crates/grand_slam/src/utils/certificate.rs index 106ee460..3e94612e 100644 --- a/crates/grand_slam/src/utils/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -1,17 +1,21 @@ -use std::path::PathBuf; +use std::{fs, path::PathBuf, vec}; use apple_codesign::{cryptography::{InMemoryPrivateKey, PrivateKey}, SigningSettings}; -use x509_certificate::CapturedX509Certificate; +use pem_rfc7468::{LineEnding, encode_string}; +use rand::rngs::OsRng; +use rcgen::{DnType, KeyPair, PKCS_RSA_SHA256}; +use rsa::{RsaPrivateKey, pkcs1::EncodeRsaPublicKey, pkcs8::{DecodePrivateKey, EncodePrivateKey}}; +use x509_certificate::{CapturedX509Certificate, X509Certificate}; -use crate::Error; +use crate::{Error, developer::{DeveloperSession, qh::certs::Cert}}; -pub struct Certificate { - cert: Option, +pub struct CertificateIdentity { + pub cert: Option, pub key: Option>, } -impl Certificate { - pub fn new(paths: Option>) -> Result { +impl CertificateIdentity { + pub async fn new_with_paths(paths: Option>) -> Result { let mut cert = Self { cert: None, key: None @@ -19,17 +23,77 @@ impl Certificate { if let Some(paths) = paths { for path in &paths { - cert.resolve_certificate_from_path(path)?; + let pem_data = fs::read(path)?; + cert.resolve_certificate_from_contents(pem_data)?; } } Ok(cert) } - fn resolve_certificate_from_path(&mut self, path: &PathBuf) -> Result<(), Error> { - let pem_data = std::fs::read(path)?; + pub async fn new_with_session( + session: &DeveloperSession, + config_path: PathBuf, + machine_name: Option, + team_id: &String, + ) -> Result { + let machine_name = machine_name.unwrap_or_else(|| "AltStore".to_string()); - for pem in pem::parse_many(pem_data).map_err(Error::Pem)? { + let key_path = Self::key_dir(config_path, &team_id)?.join("key.pem"); + + let certs = session + .qh_list_certs(&team_id) + .await? + .certificates; + + let key_pair: [Vec; 2] = if key_path.exists() { + let key_string = fs::read_to_string(&key_path)?; + let priv_key = RsaPrivateKey::from_pkcs8_pem(&key_string)?; + + if let Some(cert) = Self::find_certificate(certs.clone(), &priv_key, &machine_name).await? { + let cert_pem = encode_string("CERTIFICATE", LineEnding::LF, cert.cert_content.as_ref()).unwrap(); + let key_pem = priv_key.to_pkcs8_pem(Default::default())?.to_string(); + + [cert_pem.into_bytes(), key_pem.into_bytes()] + } else { + let (cert, priv_key) = Self::request_new_certificate(session, team_id, &machine_name, certs).await?; + let cert_pem = encode_string("CERTIFICATE", LineEnding::LF, cert.cert_content.as_ref()).unwrap(); + let key_pem = priv_key.to_pkcs8_pem(Default::default())?.to_string(); + + fs::write(&key_path, &key_pem)?; + [cert_pem.into_bytes(), key_pem.into_bytes()] + } + } else { + let (cert, priv_key) = Self::request_new_certificate(session, team_id, &machine_name, certs).await?; + let cert_pem = encode_string("CERTIFICATE", LineEnding::LF, cert.cert_content.as_ref()).unwrap(); + let key_pem = priv_key.to_pkcs8_pem(Default::default())?.to_string(); + + fs::write(&key_path, &key_pem)?; + [cert_pem.into_bytes(), key_pem.into_bytes()] + }; + + let mut cert = Self { + cert: None, + key: None + }; + + for pem in key_pair { + cert.resolve_certificate_from_contents(pem)?; + } + + Ok(cert) + } + + fn key_dir(path: PathBuf, team_id: &String) -> Result { + let dir = path.join("keys").join(team_id); + + fs::create_dir_all(&dir)?; + + Ok(dir) + } + + fn resolve_certificate_from_contents(&mut self, contents: Vec) -> Result<(), Error> { + for pem in pem::parse_many(contents).map_err(Error::Pem)? { match pem.tag() { "CERTIFICATE" => { self.cert = Some(CapturedX509Certificate::from_der(pem.contents())?); @@ -46,7 +110,7 @@ impl Certificate { Ok(()) } - + pub fn load_into_signing_settings<'settings, 'slf: 'settings>( &'slf self, settings: &'settings mut SigningSettings<'slf>, @@ -60,3 +124,103 @@ impl Certificate { Ok(()) } } + +impl CertificateIdentity { + async fn find_certificate( + certs: Vec, + priv_key: &RsaPrivateKey, + machine_name: &str, + ) -> Result, Error> { + let pub_key_der_obj = priv_key + .to_public_key() + .to_pkcs1_der()? + .as_bytes() + .to_vec(); + + for cert in certs { + if cert.machine_name.as_deref() == Some(machine_name) { + let parsed_cert = X509Certificate::from_der(&cert.cert_content)?; + if pub_key_der_obj == parsed_cert.public_key_data().as_ref() { + return Ok(Some(cert)); + } + } + } + + Ok(None) + } + + async fn request_new_certificate( + session: &DeveloperSession, + team_id: &String, + machine_name: &String, + certs: Vec, + ) -> Result<(Cert, RsaPrivateKey), Error> { + let priv_key = RsaPrivateKey::new(&mut OsRng, 2048)?; + let priv_key_der = priv_key.to_pkcs8_der()?; + let priv_key_pair = KeyPair::from_der(priv_key_der.as_bytes())?; + + let mut params = rcgen::CertificateParams::new(vec![]); + params.alg = &PKCS_RSA_SHA256; + params.key_pair = Some(priv_key_pair); + + let dn = &mut params.distinguished_name; + dn.push(DnType::CountryName, "US"); + dn.push(DnType::StateOrProvinceName, "STATE"); + dn.push(DnType::LocalityName, "LOCAL"); + dn.push(DnType::OrganizationName, "ORGNIZATION"); + dn.push(DnType::CommonName, "CN"); + + let cert_csr = rcgen::Certificate::from_params(params)? + .serialize_request_pem()?; + + let cert_serial_numbers = certs + .iter() + .map(|c| c.serial_number.clone()) + .collect::>(); + + let cert_id = loop { + match session + .qh_submit_cert_csr( + &team_id, + cert_csr.clone(), + machine_name, + ).await { + Ok(id) => break id, + Err(e) => { + if matches!(&e, Error::DeveloperSession(code, _) if *code == 7460) { + // Try to revoke certificates from the candidate list + let mut revoked_any = false; + for cid in &cert_serial_numbers { + if session + .qh_revoke_cert(&team_id, cid) + .await + .is_ok() + { + revoked_any = true; + } + } + + if revoked_any { + continue; + } else { + return Err(Error::Certificate( + "Too many certificates and failed to revoke any".into(), + )); + } + } + + return Err(e) + } + } + }.cert_request; + + let certs = session + .qh_list_certs(&team_id) + .await? + .certificates + .into_iter() + .find(|c| c.certificate_id == cert_id.certificate_id); + + Ok((certs.ok_or(Error::Certificate("Failed to find newly created certificate".into()))?, priv_key)) + } +} diff --git a/crates/grand_slam/src/utils/mod.rs b/crates/grand_slam/src/utils/mod.rs index 86050171..9cb0be70 100644 --- a/crates/grand_slam/src/utils/mod.rs +++ b/crates/grand_slam/src/utils/mod.rs @@ -6,7 +6,7 @@ mod bundle; pub use macho::MachO; pub use provision::MobileProvision; -pub use certificate::Certificate; +pub use certificate::CertificateIdentity; pub use signer::Signer; pub use bundle::Bundle; pub use bundle::BundleType; diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs index b40ea227..fe5e672b 100644 --- a/crates/grand_slam/src/utils/signer.rs +++ b/crates/grand_slam/src/utils/signer.rs @@ -5,19 +5,19 @@ use apple_codesign::{SigningSettings, UnifiedSigner}; use crate::Error; -use super::{Certificate, MobileProvision}; +use super::{CertificateIdentity, MobileProvision}; use super::SignerSettings; use super::{Bundle, BundleType, PlistInfoTrait}; pub struct Signer { - certificate: Option, + certificate: Option, settings: SignerSettings, provisioning_files: Vec, } impl Signer { pub fn new( - certificate: Option, + certificate: Option, settings: SignerSettings, provisioning_files: Vec, ) -> Self { From 207b54d1548b0067cad21531c866b952eb39d61a Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Wed, 19 Nov 2025 15:53:42 -0800 Subject: [PATCH 63/72] fix name changing + readd back bundle mods for plumesign --- apps/plumeimpactor/src/frame.rs | 11 ++++-- apps/plumesign/src/main.rs | 43 +++++++++++++++++++++- crates/grand_slam/src/utils/certificate.rs | 26 +++++++++++-- crates/grand_slam/src/utils/provision.rs | 3 +- 4 files changed, 74 insertions(+), 9 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 7cf7e2fa..e1e2192c 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -392,12 +392,17 @@ impl PlumeFrame { bundle.set_info_plist_key("CADisableMinimumFrameDurationOnPhone", true).map_err(|e| format!("Failed to set document opening: {}", e))?; } - if !signer_settings.export_ipa && signer_settings.custom_identifier.is_none() { - let new_id: String = format!("{bundle_identifier}.{team_id}"); + if !signer_settings.export_ipa { + if signer_settings.custom_identifier.is_none() { + signer_settings.custom_identifier = Some(format!("{bundle_identifier}.{team_id}")); + } + } + + if let Some(new_identifier) = signer_settings.custom_identifier.as_ref() { for embedded_bundle in &bundles { embedded_bundle.set_matching_identifier( &bundle_identifier, - &new_id, + &new_identifier, ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; } } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index 86865179..fa492db6 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -4,7 +4,8 @@ use std::process::exit; use clap::Parser; use clap::{Args, Subcommand}; -use grand_slam::utils::Certificate; +use grand_slam::Bundle; +use grand_slam::utils::{CertificateIdentity, PlistInfoTrait}; use grand_slam::utils::MobileProvision; use grand_slam::utils::Signer; use grand_slam::utils::SignerSettings; @@ -57,7 +58,7 @@ async fn main() { exit(1); } - let signing_key = Certificate::new(args.pem_files.clone().into()).unwrap_or_else(|e| { + let signing_key = CertificateIdentity::new_with_paths(args.pem_files.clone().into()).await.unwrap_or_else(|e| { eprintln!("--x failed to create Certificate: {e}"); exit(1); }); @@ -77,6 +78,44 @@ async fn main() { ..Default::default() }; + let bundle = Bundle::new(args.bundle.clone()).unwrap_or_else(|e| { + eprintln!("--x failed to load bundle: {e}"); + exit(1); + }); + + if let Some(new_name) = signer_settings.custom_name.as_ref() { + if let Err(e) = bundle.set_name(new_name) { + eprintln!("--x Failed to set new name: {}", e); + exit(1); + } + } + + if let Some(new_version) = signer_settings.custom_version.as_ref() { + if let Err(e) = bundle.set_version(new_version) { + eprintln!("--x Failed to set new version: {}", e); + exit(1); + } + } + + if let Some(new_identifier) = &signer_settings.custom_identifier { + let original_identifier = bundle.get_bundle_identifier().unwrap(); + + match bundle.collect_bundles_sorted() { + Ok(bundles) => { + for b in bundles { + if let Err(e) = b.set_matching_identifier(&original_identifier, new_identifier) { + eprintln!("--x Failed to set new identifier: {}", e); + exit(1); + } + } + } + Err(e) => { + eprintln!("--x Failed to collect bundles: {}", e); + exit(1); + } + } + } + let signer = Signer::new(Some(signing_key), signer_settings, provisioning_files); let target_path = args.bundle.clone(); diff --git a/crates/grand_slam/src/utils/certificate.rs b/crates/grand_slam/src/utils/certificate.rs index 3e94612e..86fb7d05 100644 --- a/crates/grand_slam/src/utils/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -15,10 +15,11 @@ pub struct CertificateIdentity { } impl CertificateIdentity { + // Use for cli context or if you actually store pems? why would you do that though pub async fn new_with_paths(paths: Option>) -> Result { let mut cert = Self { - cert: None, - key: None + cert: None, + key: None }; if let Some(paths) = paths { @@ -41,11 +42,16 @@ impl CertificateIdentity { let key_path = Self::key_dir(config_path, &team_id)?.join("key.pem"); + // To same some unnecessary requests, we're going to list our certificates first here + // then pass them into the necessary functions that need it, if the functions absolutely + // need to request certificates (after submitting a CSR, for example), they can do so let certs = session .qh_list_certs(&team_id) .await? .certificates; + // Only the key will be written to disk, certificate can just be gotten via the request + // request we've made, by trying to match our public key with the requests public key let key_pair: [Vec; 2] = if key_path.exists() { let key_string = fs::read_to_string(&key_path)?; let priv_key = RsaPrivateKey::from_pkcs8_pem(&key_string)?; @@ -84,6 +90,7 @@ impl CertificateIdentity { Ok(cert) } + // /keys/ fn key_dir(path: PathBuf, team_id: &String) -> Result { let dir = path.join("keys").join(team_id); @@ -92,16 +99,20 @@ impl CertificateIdentity { Ok(dir) } + // applecodesign-rs needs our contents as strings to sign fn resolve_certificate_from_contents(&mut self, contents: Vec) -> Result<(), Error> { for pem in pem::parse_many(contents).map_err(Error::Pem)? { match pem.tag() { "CERTIFICATE" => { + println!("CERTIFICATE loaded!"); // TODO: REMOVE SOME DEBUG STATEMENTS IF THIS WORKS WONDERFULY self.cert = Some(CapturedX509Certificate::from_der(pem.contents())?); } "PRIVATE KEY" => { + println!("PRIVATE KEY loaded!"); // TODO: REMOVE SOME DEBUG STATEMENTS IF THIS WORKS WONDERFULY self.key = Some(Box::new(InMemoryPrivateKey::from_pkcs8_der(pem.contents())?)); } "RSA PRIVATE KEY" => { + println!("RSA PRIVATE KEY loaded!"); // TODO: REMOVE SOME DEBUG STATEMENTS IF THIS WORKS WONDERFULY self.key = Some(Box::new(InMemoryPrivateKey::from_pkcs1_der(pem.contents())?)); } tag => println!("(unhandled PEM tag {}; ignoring)", tag), @@ -178,6 +189,12 @@ impl CertificateIdentity { .map(|c| c.serial_number.clone()) .collect::>(); + // When we submit a CSR theres a high chance of it failing, at least + // on free developer accounts, we put it in a loop so whenever it does + // fail, we also look through all of our existing certificates through + // the api until we have a success on a single revokage, then we can + // successfully submit our csr, but if we just cannot at all, return + // an error let cert_id = loop { match session .qh_submit_cert_csr( @@ -187,6 +204,7 @@ impl CertificateIdentity { ).await { Ok(id) => break id, Err(e) => { + // 7460 is for too many certificates (I think) if matches!(&e, Error::DeveloperSession(code, _) if *code == 7460) { // Try to revoke certificates from the candidate list let mut revoked_any = false; @@ -214,6 +232,8 @@ impl CertificateIdentity { } }.cert_request; + // We request again, and hope this has our new certificate + // ready.... if not then woops... thats too bad isnt it let certs = session .qh_list_certs(&team_id) .await? @@ -221,6 +241,6 @@ impl CertificateIdentity { .into_iter() .find(|c| c.certificate_id == cert_id.certificate_id); - Ok((certs.ok_or(Error::Certificate("Failed to find newly created certificate".into()))?, priv_key)) + Ok((certs.ok_or(Error::CertificatePemMissing)?, priv_key)) } } diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs index 917a58d0..97534c9e 100644 --- a/crates/grand_slam/src/utils/provision.rs +++ b/crates/grand_slam/src/utils/provision.rs @@ -96,7 +96,8 @@ impl MobileProvision { .get("com.apple.developer.team-identifier") .and_then(Value::as_string) .map(|s| s.to_owned()) - .or(Some("AAAAA11111".to_string())); + .or(Some("AAAAA11111".to_string())); + // TODO: ^^ not hardcode livecontainers placeholder team identifier if let (Some(new_id), Some(old_id)) = (new_team_id.as_ref(), old_team_id.as_ref()) { if let Some(Value::Array(groups)) = self.entitlements.get_mut("keychain-access-groups") { From 08e66374d3785ee356e8c51c8329d8986e0f03f3 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Wed, 19 Nov 2025 17:39:13 -0800 Subject: [PATCH 64/72] stuggd --- Cargo.toml | 13 ++++++++++++ apps/plumeimpactor/Cargo.toml | 13 ------------ apps/plumeimpactor/src/frame.rs | 3 +-- apps/plumeimpactor/src/handlers.rs | 2 +- apps/plumeimpactor/src/utils/package.rs | 23 +++++++++++++--------- apps/plumesign/src/main.rs | 7 ++----- crates/grand_slam/Cargo.toml | 2 +- crates/grand_slam/src/utils/certificate.rs | 1 + 8 files changed, 33 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7e7bab66..ae9fa63c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,19 @@ authors = ["khcrysalis "] license = "MIT" repository = "https://github.com/khcrysalis/plumestore" +# We use `opt-level = "s"` as it significantly reduces binary size. +# We could then use the `#[optimize(speed)]` attribute for spot optimizations. +# Unfortunately, that attribute currently doesn't work on intrinsics such as memset. +[profile.release] +codegen-units = 1 # reduces binary size by ~2% +debug = "full" # No one needs an undebuggable release binary +lto = true # reduces binary size by ~14% +opt-level = "s" # reduces binary size by ~25% +panic = "abort" # reduces binary size by ~50% in combination with -Zbuild-std-features=panic_immediate_abort +split-debuginfo = "packed" # generates a separate *.dwp/*.dSYM so the binary can get stripped +strip = "symbols" # See split-debuginfo - allows us to drop the size by ~65% +incremental = true # Improves re-compile times + [workspace.dependencies] tokio = { version = "1.43", features = ["macros", "rt-multi-thread"] } diff --git a/apps/plumeimpactor/Cargo.toml b/apps/plumeimpactor/Cargo.toml index 8f78d9fe..97317334 100644 --- a/apps/plumeimpactor/Cargo.toml +++ b/apps/plumeimpactor/Cargo.toml @@ -7,19 +7,6 @@ authors.workspace = true license.workspace = true repository.workspace = true -# We use `opt-level = "s"` as it significantly reduces binary size. -# We could then use the `#[optimize(speed)]` attribute for spot optimizations. -# Unfortunately, that attribute currently doesn't work on intrinsics such as memset. -[profile.release] -codegen-units = 1 # reduces binary size by ~2% -debug = "full" # No one needs an undebuggable release binary -lto = true # reduces binary size by ~14% -opt-level = "s" # reduces binary size by ~25% -panic = "abort" # reduces binary size by ~50% in combination with -Zbuild-std-features=panic_immediate_abort -split-debuginfo = "packed" # generates a separate *.dwp/*.dSYM so the binary can get stripped -strip = "symbols" # See split-debuginfo - allows us to drop the size by ~65% -incremental = true # Improves re-compile times - [dependencies] idevice.workspace = true zip.workspace = true diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index e1e2192c..e4206533 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -3,8 +3,7 @@ use std::path::PathBuf; use std::rc::Rc; use std::{ptr, thread}; -use grand_slam::CertificateIdentity; -use grand_slam::{AnisetteConfiguration, BundleType, MachO, MobileProvision, Signer}; +use grand_slam::{AnisetteConfiguration, BundleType, CertificateIdentity, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; use grand_slam::developer::DeveloperSession; use grand_slam::utils::{PlistInfoTrait}; diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 3b8e0fe9..f9e91cdf 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -119,7 +119,7 @@ impl PlumeFrameMessageHandler { return; } - package.load_into_signer_settings(&mut self.signer_settings).ok(); + package.load_into_signer_settings(&mut self.signer_settings); self.package_selected = Some(package); self.plume_frame.install_page.set_settings(&self.signer_settings, Some(self.package_selected.as_ref().unwrap())); diff --git a/apps/plumeimpactor/src/utils/package.rs b/apps/plumeimpactor/src/utils/package.rs index 9f6459a6..cb1b73de 100644 --- a/apps/plumeimpactor/src/utils/package.rs +++ b/apps/plumeimpactor/src/utils/package.rs @@ -102,17 +102,22 @@ impl PlistInfoTrait for Package { } impl Package { + // TODO: custom per-app settings pub fn load_into_signer_settings<'settings, 'slf: 'settings>( &'slf self, settings: &'settings mut SignerSettings, - ) -> Result<(), Error> { - - Ok(()) + ) { + if let Some(identifier) = self.get_bundle_identifier() { + match identifier.as_str() { + "com.kdt.livecontainer" => { + settings.should_only_use_main_provisioning = true; + } + _ => {} + } + } } + // depending on the apps pairing file placement, theres going to be different paths... + // Feather, StikDebug, Protokolle, Antrag, use ./pairingFile.plist + // LiveContainers uses ./SideStore/Documents/ALTPairingFile.mobiledevicepairing + // SideStore uses ./ALTPairingFile.mobiledevicepairing } - -// impl Drop for Package { -// fn drop(&mut self) { -// fs::remove_dir_all(&self.stage_dir).ok(); -// } -// } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index fa492db6..d842043c 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -4,11 +4,8 @@ use std::process::exit; use clap::Parser; use clap::{Args, Subcommand}; -use grand_slam::Bundle; -use grand_slam::utils::{CertificateIdentity, PlistInfoTrait}; -use grand_slam::utils::MobileProvision; -use grand_slam::utils::Signer; -use grand_slam::utils::SignerSettings; +use grand_slam::{CertificateIdentity, Bundle, MobileProvision, Signer}; +use grand_slam::utils::{PlistInfoTrait, SignerSettings}; #[derive(Debug, Parser)] #[command(author, version, about, disable_help_subcommand = true)] diff --git a/crates/grand_slam/Cargo.toml b/crates/grand_slam/Cargo.toml index 7cbea0ab..770978d5 100644 --- a/crates/grand_slam/Cargo.toml +++ b/crates/grand_slam/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "grand_slam" +name = "grand_slam" # TODO: rename to sinners_road edition.workspace = true version.workspace = true authors.workspace = true diff --git a/crates/grand_slam/src/utils/certificate.rs b/crates/grand_slam/src/utils/certificate.rs index 86fb7d05..93ca63f9 100644 --- a/crates/grand_slam/src/utils/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -1,6 +1,7 @@ use std::{fs, path::PathBuf, vec}; use apple_codesign::{cryptography::{InMemoryPrivateKey, PrivateKey}, SigningSettings}; +// TODO: why do we have pem and pem_rfc7468 deps again? use pem_rfc7468::{LineEnding, encode_string}; use rand::rngs::OsRng; use rcgen::{DnType, KeyPair, PKCS_RSA_SHA256}; From 8af688df879221f9f28cf13f21ed01838e45fc2b Mon Sep 17 00:00:00 2001 From: SAMSAM Date: Wed, 19 Nov 2025 17:52:58 -0800 Subject: [PATCH 65/72] todos --- apps/plumeimpactor/src/pages/mod.rs | 2 ++ apps/plumeimpactor/src/utils/mod.rs | 3 +++ apps/plumesign/src/main.rs | 3 +++ 3 files changed, 8 insertions(+) diff --git a/apps/plumeimpactor/src/pages/mod.rs b/apps/plumeimpactor/src/pages/mod.rs index 500c21de..0b35949f 100644 --- a/apps/plumeimpactor/src/pages/mod.rs +++ b/apps/plumeimpactor/src/pages/mod.rs @@ -7,11 +7,13 @@ pub use install::{InstallPage, create_install_page}; pub mod login; pub use login::{create_login_dialog, create_account_dialog}; +// TODO: investigate why github actions messes up weird sizing shit #[cfg(target_os = "linux")] pub const WINDOW_SIZE: (i32, i32) = (700, 660); #[cfg(not(target_os = "linux"))] pub const WINDOW_SIZE: (i32, i32) = (530, 410); +// TODO: investigate why github actions messes up weird sizing shit #[cfg(target_os = "linux")] pub const DIALOG_SIZE: (i32, i32) = (500, 500); #[cfg(not(target_os = "linux"))] diff --git a/apps/plumeimpactor/src/utils/mod.rs b/apps/plumeimpactor/src/utils/mod.rs index 7fb115ff..70f74914 100644 --- a/apps/plumeimpactor/src/utils/mod.rs +++ b/apps/plumeimpactor/src/utils/mod.rs @@ -3,3 +3,6 @@ mod package; pub use device::Device; pub use package::Package; + +// TODO: make utils a shared package between the CLI and GUI apps +// or combine the GUI and CLI? like checkra1n maybe.. --gui --cli diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index d842043c..142d81a1 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -38,6 +38,9 @@ pub struct SignArgs { #[arg(long = "custom-version", value_name = "VERSION", help = "Custom bundle version to set")] pub version: Option, + + // TODO: add support for p12, but for that to happen we need to patch + // the P12 crate to support SHA256 hashes... } #[tokio::main] From 6e3d268bcdf855af7e4513f882dd774aff4a0e08 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 07:29:53 -0800 Subject: [PATCH 66/72] improve ui code --- apps/plumeimpactor/src/frame.rs | 669 +++++++++--------- apps/plumeimpactor/src/handlers.rs | 4 + apps/plumeimpactor/src/pages/mod.rs | 5 +- .../src/pages/{login.rs => settings.rs} | 80 +-- 4 files changed, 381 insertions(+), 377 deletions(-) rename apps/plumeimpactor/src/pages/{login.rs => settings.rs} (74%) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index e4206533..ae85e25e 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -18,8 +18,7 @@ use tokio::sync::mpsc; use crate::get_data_path; use crate::handlers::{PlumeFrameMessage, PlumeFrameMessageHandler}; use crate::keychain::AccountCredentials; -use crate::pages::login::{AccountDialog, LoginDialog}; -use crate::pages::{DefaultPage, InstallPage, create_account_dialog, create_default_page, create_install_page, create_login_dialog, WINDOW_SIZE}; +use crate::pages::{LoginDialog, DefaultPage, InstallPage, SettingsDialog, WINDOW_SIZE, create_default_page, create_install_page, create_login_dialog, create_settings_dialog}; use crate::utils::{Device, Package}; pub const APP_NAME: &str = concat!(env!("CARGO_PKG_NAME"), " – Version ", env!("CARGO_PKG_VERSION")); @@ -32,8 +31,9 @@ pub struct PlumeFrame { pub add_ipa_button: Button, pub apple_id_button: Button, + pub login_dialog: LoginDialog, - pub account_dialog: AccountDialog, + pub settings_dialog: SettingsDialog, } impl PlumeFrame { @@ -84,7 +84,7 @@ impl PlumeFrame { add_ipa_button, apple_id_button, login_dialog: create_login_dialog(&frame), - account_dialog: create_account_dialog(&frame), + settings_dialog: create_settings_dialog(&frame), }; s.setup_event_handlers(); @@ -196,70 +196,103 @@ impl PlumeFrame { _ => { return; } }; - match run_login_flow(sender.clone(), email, password) { + match run_login_flow(sender.clone(), &email, &password) { Ok(account) => { sender.send(PlumeFrameMessage::AccountLogin(account)).ok(); } Err(e) => { - sender.send(PlumeFrameMessage::Error(format!("Login error: {}", e))).ok(); sender.send(PlumeFrameMessage::AccountDeleted).ok(); + sender.send(PlumeFrameMessage::Error(format!("Login error: {}", e))).ok(); } } }); } +} +// MARK: - Button Handlers + +impl PlumeFrame { fn bind_widget_handlers( &mut self, sender: mpsc::UnboundedSender, message_handler: Rc>, ) { - // --- Device Picker --- - - let handler_for_choice = message_handler.clone(); - let picker_clone = self.usbmuxd_picker.clone(); - self.usbmuxd_picker.on_selection_changed(move |_| { - let mut handler = handler_for_choice.borrow_mut(); - handler.usbmuxd_selected_device_id = picker_clone - .get_selection() - .and_then(|i| handler.usbmuxd_device_list.get(i as usize)) - .map(|item| item.usbmuxd_device.device_id.to_string()); + // MARK: Device Picker + + self.usbmuxd_picker.on_selection_changed( { + let message_handler = message_handler.clone(); + let picker_clone = self.usbmuxd_picker.clone(); + move |_| { + let mut handler = message_handler.borrow_mut(); + handler.usbmuxd_selected_device_id = picker_clone + .get_selection() + .and_then(|i| handler.usbmuxd_device_list.get(i as usize)) + .map(|item| item.usbmuxd_device.device_id.to_string()); + } }); - // --- Apple ID / Login Dialog --- + // MARK: Apple ID / Login Dialog - let login_dialog_rc = Rc::new(self.login_dialog.clone()); - let account_dialog_rc = Rc::new(self.account_dialog.clone()); - let handler_for_account = message_handler.clone(); self.apple_id_button.on_click({ - let login_dialog = login_dialog_rc.clone(); - let account_dialog = account_dialog_rc.clone(); + let account_dialog = Rc::new(self.settings_dialog.clone()); move |_| { - if let Some(creds) = handler_for_account.borrow().account_credentials.as_ref() { - let (first, last) = creds.get_name(); - account_dialog.set_account_name((first, last)); - account_dialog.show_modal(); - } else { - login_dialog.show_modal(); - } + account_dialog.dialog.show(true); } }); - self.account_dialog.set_logout_handler({ + self.settings_dialog.set_logout_handler({ + let message_handler = message_handler.clone(); let sender = sender.clone(); + let login_dialog = self.login_dialog.clone(); move || { - sender.send(PlumeFrameMessage::AccountDeleted).ok(); + if message_handler.borrow().account_credentials.is_some() { + sender.send(PlumeFrameMessage::AccountDeleted).ok(); + } else { + login_dialog.dialog.show(true); + } } }); - // --- Login Dialog "Next" Button --- + // MARK: File Drop/Open Handlers - self.bind_login_dialog_next_handler(sender.clone(), login_dialog_rc); + fn process_package_file(sender: mpsc::UnboundedSender, file_path: PathBuf) { + match Package::new(file_path) { + Ok(package) => { + sender.send(PlumeFrameMessage::PackageSelected(package)).ok(); + } + Err(e) => { + sender.send(PlumeFrameMessage::Error(format!("Failed to open package: {}", e))).ok(); + } + } + } - // --- File Drop/Open Handlers --- + #[cfg(not(target_os = "linux"))] + self.default_page.set_file_handlers({ + let sender = sender.clone(); + move |file_path| process_package_file(sender.clone(), PathBuf::from(file_path)) + }); - self.bind_file_handlers(sender.clone()); + self.add_ipa_button.on_click({ + let sender = sender.clone(); + let handler_for_import = self.frame.clone(); + move |_| { + let dialog = FileDialog::builder(&handler_for_import) + .with_message("Open IPA File") + .with_style(FileDialogStyle::default() | FileDialogStyle::Open) + .with_wildcard("IPA files (*.ipa;*.tipa)|*.ipa;*.tipa") + .build(); + + if dialog.show_modal() != ID_OK { + return; + } + + if let Some(file_path) = dialog.get_path() { + process_package_file(sender.clone(), PathBuf::from(file_path)); + } + } + }); - // --- Install Page Handlers --- + // MARK: Install Page Handlers self.install_page.set_cancel_handler({ let sender = sender.clone(); @@ -268,353 +301,321 @@ impl PlumeFrame { } }); - let message_handler_for_install = message_handler.clone(); self.install_page.set_install_handler({ - // let frame = self.frame.clone(); + let message_handler = message_handler.clone(); let sender = sender.clone(); move || { - let binding = message_handler_for_install.borrow(); - - let Some(selected_device) = binding.usbmuxd_selected_device_id.as_deref() else { - sender.send(PlumeFrameMessage::Error("No device selected for installation.".to_string())).ok(); - return; - }; - - let Some(selected_package) = binding.package_selected.as_ref() else { - sender.send(PlumeFrameMessage::Error("No package selected for installation.".to_string())).ok(); - return; - }; + let binding = message_handler.borrow(); - let Some(selected_account) = binding.account_credentials.as_ref() else { - sender.send(PlumeFrameMessage::Error("No Apple ID account available for installation.".to_string())).ok(); - return; - }; - - let mut signer_settings = binding.signer_settings.clone(); - binding.plume_frame.install_page.update_fields(&mut signer_settings); + let Some(selected_device) = binding.usbmuxd_selected_device_id.as_deref() else { + sender.send(PlumeFrameMessage::Error("No device selected for installation.".to_string())).ok(); + return; + }; + + let Some(selected_package) = binding.package_selected.as_ref() else { + sender.send(PlumeFrameMessage::Error("No package selected for installation.".to_string())).ok(); + return; + }; - let package = selected_package.clone(); - let account = selected_account.clone(); - let device_id = selected_device.to_string(); - let sender_clone = sender.clone(); + let Some(selected_account) = binding.account_credentials.as_ref() else { + sender.send(PlumeFrameMessage::Error("No Apple ID account available for installation.".to_string())).ok(); + return; + }; + + let mut signer_settings = binding.signer_settings.clone(); + binding.plume_frame.install_page.update_fields(&mut signer_settings); - thread::spawn(move || { - let rt = Builder::new_current_thread().enable_all().build().unwrap(); + let package = selected_package.clone(); + let account = selected_account.clone(); + let device_id = selected_device.to_string(); + let sender_clone = sender.clone(); - let install_result = rt.block_on(async { - let session = DeveloperSession::with(account.clone()); - - sender_clone.send(PlumeFrameMessage::InstallProgress(10, Some("Ensuring current device is registered...".to_string()))).ok(); + thread::spawn(move || { + let rt = Builder::new_current_thread().enable_all().build().unwrap(); - let mut usbmuxd = UsbmuxdConnection::default() - .await - .map_err(|e| format!("usbmuxd connect error: {e}"))?; - let usbmuxd_device = usbmuxd.get_devices() - .await - .map_err(|e| format!("usbmuxd device list error: {e}"))? - .into_iter() - .find(|d| d.device_id.to_string() == device_id) - .ok_or_else(|| format!("Device ID {device_id} not found"))?; - - let device = Device::new(usbmuxd_device.clone()).await; - - // TODO: Handle multiple teams properly - let teams = session.qh_list_teams() - .await - .map_err(|e| format!("Failed to list teams: {}", e))?.teams; - - if teams.len() != 1 { - return Err("Multiple teams detected for the Apple ID account.".to_string()); - } - - let team_id = &teams.get(0) - .ok_or("No teams available for the Apple ID account.")? - .team_id; - - let cert_identity: CertificateIdentity = if signer_settings.export_ipa { - CertificateIdentity { cert: None, key: None } - } else { - let cert_identity = CertificateIdentity::new_with_session( - &session, - get_data_path(), - None, - team_id, - ).await.map_err(|e| e.to_string())?; + let install_result = rt.block_on(async { + let session = DeveloperSession::with(account.clone()); + + sender_clone.send(PlumeFrameMessage::InstallProgress(10, Some("Ensuring current device is registered...".to_string()))).ok(); - cert_identity - }; + let mut usbmuxd = UsbmuxdConnection::default() + .await + .map_err(|e| format!("usbmuxd connect error: {e}"))?; + let usbmuxd_device = usbmuxd.get_devices() + .await + .map_err(|e| format!("usbmuxd device list error: {e}"))? + .into_iter() + .find(|d| d.device_id.to_string() == device_id) + .ok_or_else(|| format!("Device ID {device_id} not found"))?; - session.qh_ensure_device( - team_id, - &device.name, - &device.uuid, - ) - .await - .map_err(|e| format!("Failed to ensure device is registered: {}", e))?; - - sender_clone.send(PlumeFrameMessage::InstallProgress(20, Some("Extracting package...".to_string()))).ok(); - - let bundle = package.get_package_bundle() - .map_err(|e| format!("Failed to get package bundle: {}", e))?; - let bundles = bundle.collect_bundles_sorted() - .map_err(|e| format!("Failed to collect bundles: {}", e))?; - - let bundle_identifier = bundle.get_bundle_identifier() - .ok_or("Failed to get bundle identifier from package.")?; - - if let Some(new_name) = signer_settings.custom_name.as_ref() { - bundle.set_name(new_name).map_err(|e| format!("Failed to set new name: {}", e))?; - } + let device = Device::new(usbmuxd_device.clone()).await; + + // TODO: Handle multiple teams properly + let teams = session.qh_list_teams() + .await + .map_err(|e| format!("Failed to list teams: {}", e))?.teams; + + if teams.len() != 1 { + return Err("Multiple teams detected for the Apple ID account.".to_string()); + } + + let team_id = &teams.get(0) + .ok_or("No teams available for the Apple ID account.")? + .team_id; + + let cert_identity: CertificateIdentity = if signer_settings.export_ipa { + CertificateIdentity { cert: None, key: None } + } else { + let cert_identity = CertificateIdentity::new_with_session( + &session, + get_data_path(), + None, + team_id, + ).await.map_err(|e| e.to_string())?; + + cert_identity + }; - if let Some(new_version) = signer_settings.custom_version.as_ref() { - bundle.set_version(new_version).map_err(|e| format!("Failed to set new version: {}", e))?; - } - - if signer_settings.support_minimum_os_version { - bundle.set_info_plist_key("MinimumOSVersion", "7.0").map_err(|e| format!("Failed to set minimum OS version: {}", e))?; - } - - if signer_settings.support_file_sharing { - bundle.set_info_plist_key("UIFileSharingEnabled", true).map_err(|e| format!("Failed to set file sharing: {}", e))?; - bundle.set_info_plist_key("UISupportsDocumentBrowser", true).map_err(|e| format!("Failed to set document opening: {}", e))?; - } - - if signer_settings.support_ipad_fullscreen { - bundle.set_info_plist_key("UIRequiresFullScreen", true).map_err(|e| format!("Failed to set iPad fullscreen: {}", e))?; - } + session.qh_ensure_device( + team_id, + &device.name, + &device.uuid, + ) + .await + .map_err(|e| format!("Failed to ensure device is registered: {}", e))?; + + sender_clone.send(PlumeFrameMessage::InstallProgress(20, Some("Extracting package...".to_string()))).ok(); + + let bundle = package.get_package_bundle() + .map_err(|e| format!("Failed to get package bundle: {}", e))?; + let bundles = bundle.collect_bundles_sorted() + .map_err(|e| format!("Failed to collect bundles: {}", e))?; + + let bundle_identifier = bundle.get_bundle_identifier() + .ok_or("Failed to get bundle identifier from package.")?; - if signer_settings.support_game_mode { - bundle.set_info_plist_key("GCSupportsGameMode", true).map_err(|e| format!("Failed to set game mode: {}", e))?; - } + if let Some(new_name) = signer_settings.custom_name.as_ref() { + bundle.set_name(new_name).map_err(|e| format!("Failed to set new name: {}", e))?; + } - if signer_settings.support_pro_motion { - bundle.set_info_plist_key("CADisableMinimumFrameDurationOnPhone", true).map_err(|e| format!("Failed to set document opening: {}", e))?; - } + if let Some(new_version) = signer_settings.custom_version.as_ref() { + bundle.set_version(new_version).map_err(|e| format!("Failed to set new version: {}", e))?; + } + + if signer_settings.support_minimum_os_version { + bundle.set_info_plist_key("MinimumOSVersion", "7.0").map_err(|e| format!("Failed to set minimum OS version: {}", e))?; + } + + if signer_settings.support_file_sharing { + bundle.set_info_plist_key("UIFileSharingEnabled", true).map_err(|e| format!("Failed to set file sharing: {}", e))?; + bundle.set_info_plist_key("UISupportsDocumentBrowser", true).map_err(|e| format!("Failed to set document opening: {}", e))?; + } + + if signer_settings.support_ipad_fullscreen { + bundle.set_info_plist_key("UIRequiresFullScreen", true).map_err(|e| format!("Failed to set iPad fullscreen: {}", e))?; + } - if !signer_settings.export_ipa { - if signer_settings.custom_identifier.is_none() { - signer_settings.custom_identifier = Some(format!("{bundle_identifier}.{team_id}")); + if signer_settings.support_game_mode { + bundle.set_info_plist_key("GCSupportsGameMode", true).map_err(|e| format!("Failed to set game mode: {}", e))?; } - } - if let Some(new_identifier) = signer_settings.custom_identifier.as_ref() { - for embedded_bundle in &bundles { - embedded_bundle.set_matching_identifier( - &bundle_identifier, - &new_identifier, - ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; + if signer_settings.support_pro_motion { + bundle.set_info_plist_key("CADisableMinimumFrameDurationOnPhone", true).map_err(|e| format!("Failed to set document opening: {}", e))?; } - } - - sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); - - let mut provisionings: Vec = Vec::new(); - - if !signer_settings.export_ipa { - for sub_bundle in &bundles { - if signer_settings.should_only_use_main_provisioning && sub_bundle.dir() != bundle.dir() { - continue; - } - - if - sub_bundle._type != BundleType::AppExtension && - sub_bundle._type != BundleType::App - { - continue; + + if !signer_settings.export_ipa { + if signer_settings.custom_identifier.is_none() { + signer_settings.custom_identifier = Some(format!("{bundle_identifier}.{team_id}")); } + } - let bundle_executable_name = sub_bundle.get_executable() - .ok_or("Failed to get executable from bundle.")?; - - let bundle_executable_path = sub_bundle.dir().join(&bundle_executable_name); - - let macho = MachO::new(&bundle_executable_path) - .map_err(|e| format!("Failed to read Mach-O binary: {}", e))?; - - let id = sub_bundle.get_bundle_identifier() - .ok_or("Failed to get bundle identifier from bundle.")?; - - println!("{}", id); + if let Some(new_identifier) = signer_settings.custom_identifier.as_ref() { + for embedded_bundle in &bundles { + embedded_bundle.set_matching_identifier( + &bundle_identifier, + &new_identifier, + ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; + } + } + + sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); + + let mut provisionings: Vec = Vec::new(); + + if !signer_settings.export_ipa { + for sub_bundle in &bundles { + if signer_settings.should_only_use_main_provisioning && sub_bundle.dir() != bundle.dir() { + continue; + } + + if + sub_bundle._type != BundleType::AppExtension && + sub_bundle._type != BundleType::App + { + continue; + } - session.qh_ensure_app_id(team_id, &sub_bundle.get_name().unwrap_or_default(), &id) - .await - .map_err(|e| format!("Failed to ensure app ID: {}", e))?; - - let capabilities = session.v1_list_capabilities(team_id) - .await - .map_err(|e| format!("Failed to list capabilities: {}", e))?; - - let app_id_id = session.qh_get_app_id(team_id, &id) - .await - .map_err(|e| e.to_string())? - .ok_or("Failed to get ensured app ID.")?; + let bundle_executable_name = sub_bundle.get_executable() + .ok_or("Failed to get executable from bundle.")?; + + let bundle_executable_path = sub_bundle.dir().join(&bundle_executable_name); + + let macho = MachO::new(&bundle_executable_path) + .map_err(|e| format!("Failed to read Mach-O binary: {}", e))?; + + let id = sub_bundle.get_bundle_identifier() + .ok_or("Failed to get bundle identifier from bundle.")?; + + println!("{}", id); - if let Some(caps) = macho.capabilities_for_entitlements(&capabilities.data) { - session.v1_update_app_id(team_id, &id, caps) + session.qh_ensure_app_id(team_id, &sub_bundle.get_name().unwrap_or_default(), &id) .await - .map_err(|e| format!("Failed to enable capabilities: {}", e))?; - } - - if let Some(app_groups) = macho.app_groups_for_entitlements() { - for group in &app_groups { - let group = format!("{group}.{team_id}"); - let group_id = session.qh_ensure_app_group(team_id, &group, &group) - .await - .map_err(|e| format!("Failed to ensure app group: {}", e))?; + .map_err(|e| format!("Failed to ensure app ID: {}", e))?; + + let capabilities = session.v1_list_capabilities(team_id) + .await + .map_err(|e| format!("Failed to list capabilities: {}", e))?; + + let app_id_id = session.qh_get_app_id(team_id, &id) + .await + .map_err(|e| e.to_string())? + .ok_or("Failed to get ensured app ID.")?; - session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) + if let Some(caps) = macho.capabilities_for_entitlements(&capabilities.data) { + session.v1_update_app_id(team_id, &id, caps) .await - .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + .map_err(|e| format!("Failed to enable capabilities: {}", e))?; + } + + if let Some(app_groups) = macho.app_groups_for_entitlements() { + for group in &app_groups { + let group = format!("{group}.{team_id}"); + let group_id = session.qh_ensure_app_group(team_id, &group, &group) + .await + .map_err(|e| format!("Failed to ensure app group: {}", e))?; + + session.qh_assign_app_group(team_id, &app_id_id.app_id_id, &group_id.application_group) + .await + .map_err(|e| format!("Failed to add app group to app ID: {}", e))?; + } } - } - let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id) - .await - .map_err(|e| format!("Failed to list profiles: {}", e))?; + let profiles = session.qh_get_profile(team_id, &app_id_id.app_id_id) + .await + .map_err(|e| format!("Failed to list profiles: {}", e))?; - let profile_data = profiles.provisioning_profile.encoded_profile; - - let mobile_provision = MobileProvision::load_from_bytes(profile_data.as_ref()) - .map_err(|e| format!("Failed to load mobile provision: {}", e))?; - - provisionings.push(mobile_provision); + let profile_data = profiles.provisioning_profile.encoded_profile; + + let mobile_provision = MobileProvision::load_from_bytes(profile_data.as_ref()) + .map_err(|e| format!("Failed to load mobile provision: {}", e))?; + + provisionings.push(mobile_provision); + } } - } - sender_clone.send(PlumeFrameMessage::InstallProgress(50, Some(format!("Signing {}...", bundle.get_name().unwrap_or_default())))).ok(); - - let signer = Signer::new( - Some(cert_identity), - signer_settings.clone(), - provisionings, - ); - - signer.sign_bundle(&bundle) - .map_err(|e| format!("Failed to sign bundle: {}", e))?; - - if !signer_settings.export_ipa { - let provider = usbmuxd_device.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "baller"); - - let bundle_name = bundle.get_name().unwrap_or_default(); - let callback = { - let sender_clone = sender_clone.clone(); - move |(progress, _): (u64, ())| { - let sender = sender_clone.clone(); - let bundle_name = bundle_name.clone(); - async move { - sender.send(PlumeFrameMessage::InstallProgress(progress as i32, Some(format!("Installing {}... {}%", bundle_name, progress)))).ok(); - } - } - }; + sender_clone.send(PlumeFrameMessage::InstallProgress(50, Some(format!("Signing {}...", bundle.get_name().unwrap_or_default())))).ok(); - let state = (); + let signer = Signer::new( + Some(cert_identity), + signer_settings.clone(), + provisionings, + ); + + signer.sign_bundle(&bundle) + .map_err(|e| format!("Failed to sign bundle: {}", e))?; - installation::install_package_with_callback(&provider, bundle.dir(), None, callback, state) - .await - .map_err(|e| format!("Failed to install package: {}", e))?; - } else { - todo!("Export IPA functionality"); - } - - Ok::<_, String>(()) - }); - - if let Err(e) = install_result { - sender_clone.send(PlumeFrameMessage::InstallProgress(100, None)).ok(); - sender_clone.send(PlumeFrameMessage::Error(format!("{}", e))).ok(); - return; - } - }); - } - }); + if !signer_settings.export_ipa { + let provider = usbmuxd_device.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "baller"); + + let bundle_name = bundle.get_name().unwrap_or_default(); + let callback = { + let sender_clone = sender_clone.clone(); + move |(progress, _): (u64, ())| { + let sender = sender_clone.clone(); + let bundle_name = bundle_name.clone(); + async move { + sender.send(PlumeFrameMessage::InstallProgress(progress as i32, Some(format!("Installing {}... {}%", bundle_name, progress)))).ok(); + } + } + }; + + let state = (); + + installation::install_package_with_callback(&provider, bundle.dir(), None, callback, state) + .await + .map_err(|e| format!("Failed to install package: {}", e))?; + } else { + todo!("Export IPA functionality"); + } - } - - fn bind_login_dialog_next_handler( - &self, - sender: mpsc::UnboundedSender, - login_dialog: Rc, - ) { - let frame_for_errors = self.frame.clone(); - login_dialog.clone().set_next_handler(move || { - let email = login_dialog.get_email(); - let password = login_dialog.get_password(); - - if email.trim().is_empty() || password.is_empty() { - let dialog = MessageDialog::builder( - &frame_for_errors, - "Please enter both email and password.", - "Missing Information", - ) - .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning) - .build(); - dialog.show_modal(); - return; - } + Ok::<_, String>(()) + }); - let creds = AccountCredentials; - if let Err(e) = creds.set_credentials(email.clone(), password.clone()) { - sender.send(PlumeFrameMessage::Error(format!("Failed to save credentials: {}", e))).ok(); - return; + if let Err(e) = install_result { + sender_clone.send(PlumeFrameMessage::InstallProgress(100, None)).ok(); + sender_clone.send(PlumeFrameMessage::Error(format!("{}", e))).ok(); + return; + } + }); } - - login_dialog.clear_fields(); - login_dialog.hide(); - - let sender_for_login_thread = sender.clone(); - thread::spawn(move || { - match run_login_flow(sender_for_login_thread.clone(), email, password) { - Ok(account) => sender_for_login_thread.send(PlumeFrameMessage::AccountLogin(account)).ok(), - Err(e) => sender_for_login_thread.send(PlumeFrameMessage::Error(format!("Login failed: {}", e))).ok(), - } - }); }); - } - fn bind_file_handlers(&self, sender: mpsc::UnboundedSender) { - #[cfg(not(target_os = "linux"))] - self.default_page.set_file_handlers({ - let sender = sender.clone(); - move |file_path| Self::process_package_file(sender.clone(), PathBuf::from(file_path)) - }); + + // MARK: Login Dialog "Next" Button - self.add_ipa_button.on_click({ - let sender = sender.clone(); - let handler_for_import = self.frame.clone(); - move |_| { - let dialog = FileDialog::builder(&handler_for_import) - .with_message("Open IPA File") - .with_style(FileDialogStyle::default() | FileDialogStyle::Open) - .with_wildcard("IPA files (*.ipa;*.tipa)|*.ipa;*.tipa") + self.login_dialog.set_next_handler({ + let frame = self.frame.clone(); + let login_dialog = self.login_dialog.clone(); + move || { + let email = login_dialog.get_email(); + let password = login_dialog.get_password(); + + if email.trim().is_empty() || password.is_empty() { + let dialog = MessageDialog::builder( + &frame, + "Please enter both email and password.", + "Missing Information", + ) + .with_style(MessageDialogStyle::OK | MessageDialogStyle::IconWarning) .build(); - - if dialog.show_modal() != ID_OK { + dialog.show_modal(); return; } - if let Some(file_path) = dialog.get_path() { - Self::process_package_file(sender.clone(), PathBuf::from(file_path)); - } + login_dialog.clear_fields(); + + thread::spawn({ + let email = email.clone(); + let password = password.clone(); + let sender = sender.clone(); + move || { + match run_login_flow(sender.clone(), &email, &password) { + Ok(account) => { + sender.send(PlumeFrameMessage::AccountLogin(account)).ok(); + + if let Err(e) = AccountCredentials.set_credentials(email, password) { + sender.send(PlumeFrameMessage::Error(format!("Failed to save credentials: {}", e))).ok(); + return; + } + }, + Err(e) => { + sender.send(PlumeFrameMessage::Error(format!("Login failed: {}", e))).ok(); + }, + } + } + }); } }); - } - fn process_package_file(sender: mpsc::UnboundedSender, file_path: PathBuf) { - match Package::new(file_path) { - Ok(package) => { - sender.send(PlumeFrameMessage::PackageSelected(package)).ok(); - } - Err(e) => { - sender.send(PlumeFrameMessage::Error(format!("Failed to open package: {}", e))).ok(); - } - } } } +// MARK: - Login flow + pub fn run_login_flow( sender: mpsc::UnboundedSender, - email: String, - password: String, + email: &String, + password: &String, ) -> Result { let anisette_config = AnisetteConfiguration::default() .set_configuration_path(get_data_path()); diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index f9e91cdf..962660ec 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -149,6 +149,9 @@ impl PlumeFrameMessageHandler { .build(); dialog.show_modal(); self.account_credentials = Some(account); + + self.plume_frame.login_dialog.dialog.hide(); + self.plume_frame.settings_dialog.set_account_name(Some((first, last))); } PlumeFrameMessage::AccountDeleted => { if self.account_credentials.is_none() { @@ -162,6 +165,7 @@ impl PlumeFrameMessageHandler { } self.account_credentials = None; + self.plume_frame.settings_dialog.set_account_name(None); } PlumeFrameMessage::AwaitingTwoFactorCode(tx) => { let result = self.plume_frame.create_single_field_dialog( diff --git a/apps/plumeimpactor/src/pages/mod.rs b/apps/plumeimpactor/src/pages/mod.rs index 0b35949f..542a2ecd 100644 --- a/apps/plumeimpactor/src/pages/mod.rs +++ b/apps/plumeimpactor/src/pages/mod.rs @@ -4,8 +4,9 @@ pub use default::{DefaultPage, create_default_page}; pub mod install; pub use install::{InstallPage, create_install_page}; -pub mod login; -pub use login::{create_login_dialog, create_account_dialog}; +pub mod settings; +pub use settings::{LoginDialog, create_login_dialog}; +pub use settings::{SettingsDialog, create_settings_dialog}; // TODO: investigate why github actions messes up weird sizing shit #[cfg(target_os = "linux")] diff --git a/apps/plumeimpactor/src/pages/login.rs b/apps/plumeimpactor/src/pages/settings.rs similarity index 74% rename from apps/plumeimpactor/src/pages/login.rs rename to apps/plumeimpactor/src/pages/settings.rs index fe2a8a13..ae10863a 100644 --- a/apps/plumeimpactor/src/pages/login.rs +++ b/apps/plumeimpactor/src/pages/settings.rs @@ -76,14 +76,6 @@ impl LoginDialog { self.password_field.set_value(""); } - pub fn show_modal(&self) { - self.dialog.show_modal(); - } - - pub fn hide(&self) { - self.dialog.end_modal(0); - } - pub fn set_next_handler(&self, on_next: impl Fn() + 'static) { self.next_button.on_click(move |_evt| { on_next(); @@ -94,63 +86,69 @@ impl LoginDialog { // MARK: - AccountDialog #[derive(Clone)] -pub struct AccountDialog { +pub struct SettingsDialog { pub dialog: Dialog, pub logout_button: Button, - pub label: StaticText, + pub account_label: StaticText, } -pub fn create_account_dialog(parent: &Window) -> AccountDialog { - let dialog = Dialog::builder(parent, "Account") - .with_style(DialogStyle::SystemMenu | DialogStyle::Caption) +pub fn create_settings_dialog(parent: &Window) -> SettingsDialog { + let dialog = Dialog::builder(parent, "Settings") .with_size(DIALOG_SIZE.0, DIALOG_SIZE.1) .build(); let sizer = BoxSizer::builder(Orientation::Vertical).build(); sizer.add_spacer(13); - let label = StaticText::builder(&dialog).with_label("").build(); - sizer.add(&label, 0, SizerFlag::Expand | SizerFlag::Left | SizerFlag::Right, 13); + let account_row = BoxSizer::builder(Orientation::Horizontal).build(); + let account_label = StaticText::builder(&dialog).with_label("Not logged in").build(); + let logout_button = Button::builder(&dialog).with_label("Login").build(); + account_row.add(&account_label, 4, SizerFlag::Expand, 0); + account_row.add_stretch_spacer(1); + account_row.add(&logout_button, 1, SizerFlag::Expand, 0); + + sizer.add_sizer(&account_row, 0, SizerFlag::Right | SizerFlag::Left, 13); + + sizer.add(&StaticLine::builder(&dialog).build(), 0, SizerFlag::Expand | SizerFlag::All, 13); - let buttons = BoxSizer::builder(Orientation::Horizontal).build(); - - let close_button = Button::builder(&dialog).with_label("Close").build(); - buttons.add(&close_button, 0, SizerFlag::All, 8); - let logout_button = Button::builder(&dialog).with_label("Log out").build(); - - buttons.add(&logout_button, 0, SizerFlag::All, 8); - - sizer.add_sizer(&buttons, 0, SizerFlag::AlignRight | SizerFlag::All, 8); + let cert_button_sizer = BoxSizer::builder(Orientation::Horizontal).build(); + let import_cert_button = Button::builder(&dialog).with_label("Import P12").build(); + import_cert_button.enable(false); + let export_cert_button = Button::builder(&dialog).with_label("Export P12").build(); + export_cert_button.enable(false); + cert_button_sizer.add(&import_cert_button, 1, SizerFlag::Expand, 0); + cert_button_sizer.add_spacer(13); + cert_button_sizer.add(&export_cert_button, 1, SizerFlag::Expand, 0); + + sizer.add_sizer(&cert_button_sizer, 0, SizerFlag::Right | SizerFlag::Left, 13); dialog.set_sizer(sizer, true); - - close_button.on_click({ - let dialog = dialog.clone(); - move |_| dialog.end_modal(ID_OK as i32) - }); - AccountDialog { + SettingsDialog { dialog, logout_button, - label, + account_label, } } -impl AccountDialog { - pub fn show_modal(&self) { - self.dialog.show_modal(); - } - +impl SettingsDialog { pub fn set_logout_handler(&self, on_logout: impl Fn() + 'static) { - let dialog = self.dialog.clone(); self.logout_button.on_click(move |_| { on_logout(); - dialog.end_modal(ID_OK as i32); }); } - - pub fn set_account_name(&self, account_name: (String, String)) { - self.label.set_label(&format!("Logged in as {} {}", account_name.0, account_name.1)); + + pub fn set_account_name(&self, account_name: Option<(String, String)>) { + match account_name { + Some((first, last)) => { + self.account_label.set_label(&format!("Logged in as {} {}", first, last)); + self.logout_button.set_label("Logout"); + } + None => { + self.account_label.set_label("Not logged in"); + self.logout_button.set_label("Sign In"); + } + } } } From 2f469dd322eaf57572d9e5b718207c0f9de0bee6 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 09:33:04 -0800 Subject: [PATCH 67/72] add p12 gen support --- Cargo.lock | 1 + apps/plumeimpactor/src/frame.rs | 18 +++++- apps/plumeimpactor/src/utils/package.rs | 3 + apps/plumesign/src/main.rs | 2 +- crates/grand_slam/Cargo.toml | 1 + crates/grand_slam/src/developer/qh/certs.rs | 6 +- crates/grand_slam/src/utils/certificate.rs | 65 ++++++++++++++++++--- crates/grand_slam/src/utils/mod.rs | 4 ++ crates/grand_slam/src/utils/provision.rs | 27 +++------ crates/grand_slam/src/utils/signer.rs | 2 +- 10 files changed, 94 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e68d36e1..500d2ad5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1511,6 +1511,7 @@ dependencies = [ "hex", "hmac", "omnisette", + "p12", "pbkdf2", "pem 3.0.6", "pem-rfc7468", diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index ae85e25e..539b96f6 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -1,7 +1,8 @@ use std::cell::RefCell; +use std::fmt::write; use std::path::PathBuf; use std::rc::Rc; -use std::{ptr, thread}; +use std::{fs, ptr, thread}; use grand_slam::{AnisetteConfiguration, BundleType, CertificateIdentity, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; @@ -364,7 +365,7 @@ impl PlumeFrame { .team_id; let cert_identity: CertificateIdentity = if signer_settings.export_ipa { - CertificateIdentity { cert: None, key: None } + CertificateIdentity { cert: None, key: None, machine_id: None, p12_data: None, serial_number: None } } else { let cert_identity = CertificateIdentity::new_with_session( &session, @@ -437,6 +438,17 @@ impl PlumeFrame { ).map_err(|e| format!("Failed to set matching identifier: {}", e))?; } } + + if signer_settings.should_embed_p12 { + if let Some(p12_data) = &cert_identity.p12_data { + if let Some(serial_number) = &cert_identity.serial_number { + bundle.set_info_plist_key("ALTCertificateID", &**serial_number) + .map_err(|e| format!("Failed to set cert serial: {}", e))?; + fs::write(bundle.dir().join("ALTCertificate.p12"), p12_data) + .map_err(|e| format!("Failed to write p12: {}", e))?; + } + } + } sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); @@ -506,7 +518,7 @@ impl PlumeFrame { let profile_data = profiles.provisioning_profile.encoded_profile; - let mobile_provision = MobileProvision::load_from_bytes(profile_data.as_ref()) + let mobile_provision = MobileProvision::load_with_bytes(profile_data.as_ref().to_vec()) .map_err(|e| format!("Failed to load mobile provision: {}", e))?; provisionings.push(mobile_provision); diff --git a/apps/plumeimpactor/src/utils/package.rs b/apps/plumeimpactor/src/utils/package.rs index cb1b73de..527580d3 100644 --- a/apps/plumeimpactor/src/utils/package.rs +++ b/apps/plumeimpactor/src/utils/package.rs @@ -112,6 +112,9 @@ impl Package { "com.kdt.livecontainer" => { settings.should_only_use_main_provisioning = true; } + "com.SideStore.SideStore" => { + settings.should_embed_p12 = true; + } _ => {} } } diff --git a/apps/plumesign/src/main.rs b/apps/plumesign/src/main.rs index 142d81a1..f119262f 100644 --- a/apps/plumesign/src/main.rs +++ b/apps/plumesign/src/main.rs @@ -64,7 +64,7 @@ async fn main() { }); let provisioning_files = args.provisioning_files.iter() - .map(MobileProvision::load) + .map(MobileProvision::load_with_path) .collect::, _>>() .unwrap_or_else(|e| { eprintln!("--x failed to load provisioning profiles: {e}"); diff --git a/crates/grand_slam/Cargo.toml b/crates/grand_slam/Cargo.toml index 770978d5..05d77c42 100644 --- a/crates/grand_slam/Cargo.toml +++ b/crates/grand_slam/Cargo.toml @@ -40,6 +40,7 @@ rcgen = "0.9.3" pem-rfc7468 = "0.7.0" rsa = "0.9.8" hex = "0.4.3" +p12 = "0.6.3" [features] default = [] diff --git a/crates/grand_slam/src/developer/qh/certs.rs b/crates/grand_slam/src/developer/qh/certs.rs index ec5d9478..068aef29 100644 --- a/crates/grand_slam/src/developer/qh/certs.rs +++ b/crates/grand_slam/src/developer/qh/certs.rs @@ -80,7 +80,7 @@ pub struct Cert { certificate_platform: Option, pub cert_type: Option, pub cert_content: Data, - machine_id: Option, + pub machine_id: Option, pub machine_name: Option, } @@ -103,11 +103,11 @@ pub struct Csr { certificate_status_code: Integer, cert_request_status_code: Integer, certificate_type_display_id: String, - serial_num: String, + pub serial_num: String, serial_num_decimal: String, type_string: String, pub certificate_type: Option, - machine_id: Option, + pub machine_id: Option, pub machine_name: Option, } diff --git a/crates/grand_slam/src/utils/certificate.rs b/crates/grand_slam/src/utils/certificate.rs index 93ca63f9..392cc92f 100644 --- a/crates/grand_slam/src/utils/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -7,12 +7,16 @@ use rand::rngs::OsRng; use rcgen::{DnType, KeyPair, PKCS_RSA_SHA256}; use rsa::{RsaPrivateKey, pkcs1::EncodeRsaPublicKey, pkcs8::{DecodePrivateKey, EncodePrivateKey}}; use x509_certificate::{CapturedX509Certificate, X509Certificate}; +use p12::*; use crate::{Error, developer::{DeveloperSession, qh::certs::Cert}}; pub struct CertificateIdentity { pub cert: Option, pub key: Option>, + pub machine_id: Option, + pub serial_number: Option, + pub p12_data: Option> } impl CertificateIdentity { @@ -20,7 +24,10 @@ impl CertificateIdentity { pub async fn new_with_paths(paths: Option>) -> Result { let mut cert = Self { cert: None, - key: None + key: None, + machine_id: None, + p12_data: None, + serial_number: None, }; if let Some(paths) = paths { @@ -43,6 +50,14 @@ impl CertificateIdentity { let key_path = Self::key_dir(config_path, &team_id)?.join("key.pem"); + let mut cert = Self { + cert: None, + key: None, + machine_id: None, + p12_data: None, + serial_number: None, + }; + // To same some unnecessary requests, we're going to list our certificates first here // then pass them into the necessary functions that need it, if the functions absolutely // need to request certificates (after submitting a CSR, for example), they can do so @@ -57,13 +72,13 @@ impl CertificateIdentity { let key_string = fs::read_to_string(&key_path)?; let priv_key = RsaPrivateKey::from_pkcs8_pem(&key_string)?; - if let Some(cert) = Self::find_certificate(certs.clone(), &priv_key, &machine_name).await? { + if let Some(cert) = cert.find_certificate(certs.clone(), &priv_key, &machine_name).await? { let cert_pem = encode_string("CERTIFICATE", LineEnding::LF, cert.cert_content.as_ref()).unwrap(); let key_pem = priv_key.to_pkcs8_pem(Default::default())?.to_string(); [cert_pem.into_bytes(), key_pem.into_bytes()] } else { - let (cert, priv_key) = Self::request_new_certificate(session, team_id, &machine_name, certs).await?; + let (cert, priv_key) = cert.request_new_certificate(session, team_id, &machine_name, certs).await?; let cert_pem = encode_string("CERTIFICATE", LineEnding::LF, cert.cert_content.as_ref()).unwrap(); let key_pem = priv_key.to_pkcs8_pem(Default::default())?.to_string(); @@ -71,7 +86,7 @@ impl CertificateIdentity { [cert_pem.into_bytes(), key_pem.into_bytes()] } } else { - let (cert, priv_key) = Self::request_new_certificate(session, team_id, &machine_name, certs).await?; + let (cert, priv_key) = cert.request_new_certificate(session, team_id, &machine_name, certs).await?; let cert_pem = encode_string("CERTIFICATE", LineEnding::LF, cert.cert_content.as_ref()).unwrap(); let key_pem = priv_key.to_pkcs8_pem(Default::default())?.to_string(); @@ -79,10 +94,10 @@ impl CertificateIdentity { [cert_pem.into_bytes(), key_pem.into_bytes()] }; - let mut cert = Self { - cert: None, - key: None - }; + // TODO: this may be horrendious + if let Some(p12_data) = cert.create_pkcs12(&key_pair) { + cert.p12_data = Some(p12_data); + } for pem in key_pair { cert.resolve_certificate_from_contents(pem)?; @@ -100,6 +115,24 @@ impl CertificateIdentity { Ok(dir) } + fn set_machine_id(&mut self, machine_id: String) { + self.machine_id = Some(machine_id); + } + + fn set_serial_number(&mut self, serial_number: String) { + self.serial_number = Some(serial_number); + } + + // TODO: cleanest p12 code of them all + pub fn create_pkcs12(&self, data: &[Vec; 2]) -> Option> { + let machine_id = self.machine_id.as_ref()?; + let cert_der = pem::parse(&data[0]).ok()?.contents().to_vec(); + let key_der = pem::parse(&data[1]).ok()?.contents().to_vec(); + + let p12 = PFX::new(&cert_der, &key_der, None, &machine_id, "PLUME")?; + Some(p12.to_der()) + } + // applecodesign-rs needs our contents as strings to sign fn resolve_certificate_from_contents(&mut self, contents: Vec) -> Result<(), Error> { for pem in pem::parse_many(contents).map_err(Error::Pem)? { @@ -139,6 +172,7 @@ impl CertificateIdentity { impl CertificateIdentity { async fn find_certificate( + &mut self, certs: Vec, priv_key: &RsaPrivateKey, machine_name: &str, @@ -153,6 +187,13 @@ impl CertificateIdentity { if cert.machine_name.as_deref() == Some(machine_name) { let parsed_cert = X509Certificate::from_der(&cert.cert_content)?; if pub_key_der_obj == parsed_cert.public_key_data().as_ref() { + // We need to save the machine_id for our P12 + if let Some(ref machine_id) = cert.machine_id { + self.set_machine_id(machine_id.clone()); + } + + self.set_serial_number(cert.serial_number.clone()); + return Ok(Some(cert)); } } @@ -162,6 +203,7 @@ impl CertificateIdentity { } async fn request_new_certificate( + &mut self, session: &DeveloperSession, team_id: &String, machine_name: &String, @@ -233,6 +275,13 @@ impl CertificateIdentity { } }.cert_request; + // We need to save the machine_id for our P12 + if let Some(ref machine_id) = cert_id.machine_id { + self.set_machine_id(machine_id.clone()); + } + + self.set_serial_number(cert_id.serial_num.clone()); + // We request again, and hope this has our new certificate // ready.... if not then woops... thats too bad isnt it let certs = session diff --git a/crates/grand_slam/src/utils/mod.rs b/crates/grand_slam/src/utils/mod.rs index 9cb0be70..ea8c0f50 100644 --- a/crates/grand_slam/src/utils/mod.rs +++ b/crates/grand_slam/src/utils/mod.rs @@ -16,6 +16,7 @@ pub struct SignerSettings { pub custom_name: Option, pub custom_identifier: Option, pub custom_version: Option, + pub support_minimum_os_version: bool, pub support_file_sharing: bool, pub support_ipad_fullscreen: bool, @@ -23,6 +24,7 @@ pub struct SignerSettings { pub support_pro_motion: bool, pub should_embed_provisioning: bool, pub should_embed_pairing: bool, + pub should_embed_p12: bool, pub should_only_use_main_provisioning: bool, pub remove_url_schemes: bool, pub export_ipa: bool, @@ -34,6 +36,7 @@ impl Default for SignerSettings { custom_name: None, custom_identifier: None, custom_version: None, + support_minimum_os_version: false, support_file_sharing: false, support_ipad_fullscreen: false, @@ -41,6 +44,7 @@ impl Default for SignerSettings { support_pro_motion: false, should_embed_provisioning: true, should_embed_pairing: false, + should_embed_p12: false, should_only_use_main_provisioning: false, remove_url_schemes: false, export_ipa: false, diff --git a/crates/grand_slam/src/utils/provision.rs b/crates/grand_slam/src/utils/provision.rs index 97534c9e..ab51cd98 100644 --- a/crates/grand_slam/src/utils/provision.rs +++ b/crates/grand_slam/src/utils/provision.rs @@ -2,58 +2,47 @@ use std::fs; use std::path::{Path, PathBuf}; use plist::{Dictionary, Value}; -use uuid::Uuid; use crate::Error; use super::MachO; #[derive(Clone)] pub struct MobileProvision { - provision_file: PathBuf, + pub provision_data: Vec, provisioning_plist: Value, entitlements: Dictionary, } impl MobileProvision { - pub fn load>(provision_path: P) -> Result { + pub fn load_with_path>(provision_path: P) -> Result { let path = provision_path.as_ref(); if !path.exists() { return Err(Error::ProvisioningEntitlementsUnknown); } - let profile_data = fs::read(path)?; - let provisioning_plist = Self::extract_plist_from_file(&profile_data)?; + let provision_data = fs::read(path)?; + let provisioning_plist = Self::extract_plist_from_file(&provision_data)?; let entitlements = Self::extract_entitlements(&provisioning_plist)?; Ok(Self { - provision_file: path.to_path_buf(), + provision_data, provisioning_plist, entitlements, }) } - // TODO: make this better.... - pub fn load_from_bytes(data: &[u8]) -> Result { - let provisioning_plist = Self::extract_plist_from_file(data)?; + pub fn load_with_bytes(provision_data: Vec) -> Result { + let provisioning_plist = Self::extract_plist_from_file(&provision_data)?; let entitlements = Self::extract_entitlements(&provisioning_plist)?; - - let temp_file_path = std::env::temp_dir().join(format!("plume_provision_{:08}", Uuid::new_v4().to_string().to_uppercase())); - fs::create_dir_all(&temp_file_path)?; - let provision_file = temp_file_path.join(format!("{}.mobileprovision", Uuid::new_v4().to_string().to_uppercase())); - fs::write(&provision_file, data)?; Ok(Self { - provision_file, + provision_data, provisioning_plist, entitlements, }) } - pub fn file_path(&self) -> &Path { - &self.provision_file - } - pub fn entitlements(&self) -> &Dictionary { &self.entitlements } diff --git a/crates/grand_slam/src/utils/signer.rs b/crates/grand_slam/src/utils/signer.rs index fe5e672b..47b357df 100644 --- a/crates/grand_slam/src/utils/signer.rs +++ b/crates/grand_slam/src/utils/signer.rs @@ -63,7 +63,7 @@ impl Signer { } if self.settings.should_embed_provisioning { - fs::copy(prov.file_path(), bundle.dir().join("embedded.mobileprovision"))?; + fs::write(bundle.dir().join("embedded.mobileprovision"), &prov.provision_data)?; } if let Ok(ent_xml) = prov.entitlements_as_bytes() { From 38cd247005ba955728393493c0b29208eef4800a Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 09:40:32 -0800 Subject: [PATCH 68/72] add some keeps --- package/linux/.keep | 0 package/windows/.keep | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 package/linux/.keep create mode 100644 package/windows/.keep diff --git a/package/linux/.keep b/package/linux/.keep new file mode 100644 index 00000000..e69de29b diff --git a/package/windows/.keep b/package/windows/.keep new file mode 100644 index 00000000..e69de29b From c96ae3940b8216e776a9899a6cbecfab7e510872 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 10:30:50 -0800 Subject: [PATCH 69/72] remove some stuff for now --- apps/plumeimpactor/src/frame.rs | 20 +++++------ crates/grand_slam/src/utils/certificate.rs | 39 +++++++++++----------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 539b96f6..946df2e0 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -439,16 +439,16 @@ impl PlumeFrame { } } - if signer_settings.should_embed_p12 { - if let Some(p12_data) = &cert_identity.p12_data { - if let Some(serial_number) = &cert_identity.serial_number { - bundle.set_info_plist_key("ALTCertificateID", &**serial_number) - .map_err(|e| format!("Failed to set cert serial: {}", e))?; - fs::write(bundle.dir().join("ALTCertificate.p12"), p12_data) - .map_err(|e| format!("Failed to write p12: {}", e))?; - } - } - } + // if signer_settings.should_embed_p12 { + // if let Some(p12_data) = &cert_identity.p12_data { + // if let Some(serial_number) = &cert_identity.serial_number { + // bundle.set_info_plist_key("ALTCertificateID", &**serial_number) + // .map_err(|e| format!("Failed to set cert serial: {}", e))?; + // fs::write(bundle.dir().join("ALTCertificate.p12"), p12_data) + // .map_err(|e| format!("Failed to write p12: {}", e))?; + // } + // } + // } sender_clone.send(PlumeFrameMessage::InstallProgress(30, Some(format!("Registering {}...", bundle.get_name().unwrap_or_default())))).ok(); diff --git a/crates/grand_slam/src/utils/certificate.rs b/crates/grand_slam/src/utils/certificate.rs index 392cc92f..7d9b5488 100644 --- a/crates/grand_slam/src/utils/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -7,7 +7,6 @@ use rand::rngs::OsRng; use rcgen::{DnType, KeyPair, PKCS_RSA_SHA256}; use rsa::{RsaPrivateKey, pkcs1::EncodeRsaPublicKey, pkcs8::{DecodePrivateKey, EncodePrivateKey}}; use x509_certificate::{CapturedX509Certificate, X509Certificate}; -use p12::*; use crate::{Error, developer::{DeveloperSession, qh::certs::Cert}}; @@ -95,9 +94,9 @@ impl CertificateIdentity { }; // TODO: this may be horrendious - if let Some(p12_data) = cert.create_pkcs12(&key_pair) { - cert.p12_data = Some(p12_data); - } + // if let Some(p12_data) = cert.create_pkcs12(&key_pair) { + // cert.p12_data = Some(p12_data); + // } for pem in key_pair { cert.resolve_certificate_from_contents(pem)?; @@ -116,22 +115,24 @@ impl CertificateIdentity { } fn set_machine_id(&mut self, machine_id: String) { + println!("Setting machine id: {}", machine_id); self.machine_id = Some(machine_id); } fn set_serial_number(&mut self, serial_number: String) { + println!("Setting serial number: {}", serial_number); self.serial_number = Some(serial_number); } // TODO: cleanest p12 code of them all - pub fn create_pkcs12(&self, data: &[Vec; 2]) -> Option> { - let machine_id = self.machine_id.as_ref()?; - let cert_der = pem::parse(&data[0]).ok()?.contents().to_vec(); - let key_der = pem::parse(&data[1]).ok()?.contents().to_vec(); + // pub fn create_pkcs12(&self, data: &[Vec; 2]) -> Option> { + // let machine_id = self.machine_id.as_ref()?; + // let cert_der = pem::parse(&data[0]).ok()?.contents().to_vec(); + // let key_der = pem::parse(&data[1]).ok()?.contents().to_vec(); - let p12 = PFX::new(&cert_der, &key_der, None, &machine_id, "PLUME")?; - Some(p12.to_der()) - } + // let p12 = p12::PFX::new(&cert_der, &key_der, None, &machine_id, "PLUME")?; + // Some(p12.to_der()) + // } // applecodesign-rs needs our contents as strings to sign fn resolve_certificate_from_contents(&mut self, contents: Vec) -> Result<(), Error> { @@ -188,11 +189,11 @@ impl CertificateIdentity { let parsed_cert = X509Certificate::from_der(&cert.cert_content)?; if pub_key_der_obj == parsed_cert.public_key_data().as_ref() { // We need to save the machine_id for our P12 - if let Some(ref machine_id) = cert.machine_id { - self.set_machine_id(machine_id.clone()); - } + // if let Some(ref machine_id) = cert.machine_id { + // self.set_machine_id(machine_id.clone()); + // } - self.set_serial_number(cert.serial_number.clone()); + // self.set_serial_number(cert.serial_number.clone()); return Ok(Some(cert)); } @@ -276,11 +277,11 @@ impl CertificateIdentity { }.cert_request; // We need to save the machine_id for our P12 - if let Some(ref machine_id) = cert_id.machine_id { - self.set_machine_id(machine_id.clone()); - } + // if let Some(ref machine_id) = cert_id.machine_id { + // self.set_machine_id(machine_id.clone()); + // } - self.set_serial_number(cert_id.serial_num.clone()); + // self.set_serial_number(cert_id.serial_num.clone()); // We request again, and hope this has our new certificate // ready.... if not then woops... thats too bad isnt it From e40ab9cd080a30629c9223b67841e116394bb8f7 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 10:33:35 -0800 Subject: [PATCH 70/72] delete some cache after deselect --- apps/plumeimpactor/src/handlers.rs | 5 +++++ apps/plumeimpactor/src/utils/package.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/apps/plumeimpactor/src/handlers.rs b/apps/plumeimpactor/src/handlers.rs index 962660ec..b8e2abcd 100644 --- a/apps/plumeimpactor/src/handlers.rs +++ b/apps/plumeimpactor/src/handlers.rs @@ -130,6 +130,11 @@ impl PlumeFrameMessageHandler { self.plume_frame.add_ipa_button.enable(false); } PlumeFrameMessage::PackageDeselected => { + // TODO: should it be this way? + if let Some(package) = self.package_selected.as_ref() { + package.clone().remove_package_stage(); + } + self.package_selected = None; self.plume_frame.install_page.panel.hide(); self.plume_frame.default_page.panel.show(true); diff --git a/apps/plumeimpactor/src/utils/package.rs b/apps/plumeimpactor/src/utils/package.rs index 527580d3..b24b1222 100644 --- a/apps/plumeimpactor/src/utils/package.rs +++ b/apps/plumeimpactor/src/utils/package.rs @@ -66,6 +66,10 @@ impl Package { entry.read_to_end(&mut buf)?; Ok(plist::from_bytes(&buf)?) } + + pub fn remove_package_stage(self) { + fs::remove_dir_all(&self.stage_dir).ok(); + } } macro_rules! get_plist_dict_value { From 57380706db0ef52067076e6721ca9d05bba5350c Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 10:46:58 -0800 Subject: [PATCH 71/72] comment out more stuff --- apps/plumeimpactor/src/frame.rs | 6 +++--- crates/grand_slam/src/utils/certificate.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/plumeimpactor/src/frame.rs b/apps/plumeimpactor/src/frame.rs index 946df2e0..b08f09ac 100644 --- a/apps/plumeimpactor/src/frame.rs +++ b/apps/plumeimpactor/src/frame.rs @@ -1,8 +1,7 @@ use std::cell::RefCell; -use std::fmt::write; use std::path::PathBuf; use std::rc::Rc; -use std::{fs, ptr, thread}; +use std::{ptr, thread}; use grand_slam::{AnisetteConfiguration, BundleType, CertificateIdentity, MachO, MobileProvision, Signer}; use grand_slam::auth::Account; @@ -303,6 +302,7 @@ impl PlumeFrame { }); self.install_page.set_install_handler({ + let frame = self.frame.clone(); let message_handler = message_handler.clone(); let sender = sender.clone(); move || { @@ -336,7 +336,7 @@ impl PlumeFrame { let install_result = rt.block_on(async { let session = DeveloperSession::with(account.clone()); - + sender_clone.send(PlumeFrameMessage::InstallProgress(10, Some("Ensuring current device is registered...".to_string()))).ok(); let mut usbmuxd = UsbmuxdConnection::default() diff --git a/crates/grand_slam/src/utils/certificate.rs b/crates/grand_slam/src/utils/certificate.rs index 7d9b5488..625f17da 100644 --- a/crates/grand_slam/src/utils/certificate.rs +++ b/crates/grand_slam/src/utils/certificate.rs @@ -114,15 +114,15 @@ impl CertificateIdentity { Ok(dir) } - fn set_machine_id(&mut self, machine_id: String) { - println!("Setting machine id: {}", machine_id); - self.machine_id = Some(machine_id); - } + // fn set_machine_id(&mut self, machine_id: String) { + // println!("Setting machine id: {}", machine_id); + // self.machine_id = Some(machine_id); + // } - fn set_serial_number(&mut self, serial_number: String) { - println!("Setting serial number: {}", serial_number); - self.serial_number = Some(serial_number); - } + // fn set_serial_number(&mut self, serial_number: String) { + // println!("Setting serial number: {}", serial_number); + // self.serial_number = Some(serial_number); + // } // TODO: cleanest p12 code of them all // pub fn create_pkcs12(&self, data: &[Vec; 2]) -> Option> { From d71a590de24d9c2b0bae43fb2d844b03ef9967b0 Mon Sep 17 00:00:00 2001 From: khcrysalis Date: Thu, 20 Nov 2025 11:35:59 -0800 Subject: [PATCH 72/72] add simple icon for mac --- package/macos/Impactor.app/Contents/Info.plist | 4 ++++ .../Contents/Resources/AppIcon.icns | Bin 0 -> 31829 bytes .../Impactor.app/Contents/Resources/Assets.car | Bin 0 -> 611976 bytes 3 files changed, 4 insertions(+) create mode 100644 package/macos/Impactor.app/Contents/Resources/AppIcon.icns create mode 100644 package/macos/Impactor.app/Contents/Resources/Assets.car diff --git a/package/macos/Impactor.app/Contents/Info.plist b/package/macos/Impactor.app/Contents/Info.plist index 3b971bdb..a63c4ecb 100644 --- a/package/macos/Impactor.app/Contents/Info.plist +++ b/package/macos/Impactor.app/Contents/Info.plist @@ -26,5 +26,9 @@ NSHighResolutionCapable + CFBundleIconFile + AppIcon + CFBundleIconName + AppIcon diff --git a/package/macos/Impactor.app/Contents/Resources/AppIcon.icns b/package/macos/Impactor.app/Contents/Resources/AppIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..892464e4a5826501813328e7e5d527e07125202c GIT binary patch literal 31829 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

Pt7C<>Uh``! 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%0A6j*-6$DtDHhddzcem#R&)GyPs{Tg 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

d)Htqf25j`ZfM*n4z9KThp!91xcC`E;bQ zVPD?K((oeJws%F4cCQ9<$I1&Z3Aqr(0Ag6m$stj0*aAYmkT>WU$vR}3Fp=mE+C`m=`$&m02{bv$2kFrk% zQ@#{W)b$1jK>e9Hjfb7Kn=c>C`vEB;LmdIV4-Iq{g1emgNNJJ&tH3yYk+yJ ztZLvUWbEcFQn;%Db`kJY_c{Gb@G#!=l((2b>E1&60?(6okbqqOUacwi$=8(LJ8JOd zXp~zlv`&>><*?&g@2m33##SO@sEPn=BtcgjKlve}o4i0?iY7 zpi4L2oi$M{=~(Wogg)UZR$sd52FeMt zx^;d=Q&BHK1m=1$$NDyew05${?nC>;>`jUUp)Hub@6>6IsRNQ{9Qv|&8X&Dhn<4$( z$ZNF#<^zE)hpKXBOBh?bQQ?*Us^DB5%G7B6@-TX%IArnopF_e~JX?aH79n7Ysvj=+u!NbE&}RG&S|Xy>*OtI{ z-;)kv!OVK>qXMTp_}Jr`57{L(5QMU?XKaJPud^7 z4aiS%ih4l_n=_i<&h^phpNA}j9;C%bzZ5?HTwL;S-CM)z6)1_#{}vng?2v_F;)^lo}yqdwBQTyO)wS2vU&QG zhw`P&)FYihq@h_${%?3Yzi%`E?LOJu#qsgb;4cx$i@%JitJtYCpKt8!$ul6le6A); z$mPl|a>1N6<-A#4$|oeHj@%}qB=mW1a*tHYP}1U_=E%`&2?}3*E-1q!to`qG=@y##=l`guKmj$*2U_Gu9Lxez}*x{LQebm z3-eM6F%^M^w03sMh|_-dvQ)saX*Xbbi_i;7#ifO?$7{l|iBHs}_!wSx^UsPCSe(^@ z=)pMAhr6=AhkLOfoxH7DNp4lEO=-Vtx3Q7i(vAu2{EmZ(%Et?9oi~d|qtC%_#-pWP znG;v@>OI6zFCHgnI+oO`Moy^}rG_Su1C9R5)y&lL{x=_holIHT5i>IEJ;J zz3z(>^wPro=Z_Y<_4^j<)^VuCe4W_;^yEBujy&RJ=w1%|gEaqbT{6wjfnh`6Ndh85 z$1L4ZjplX}t%wLPUwA$%=TkapsWwTZG}bfOXSf!Z4{LJErN}#OeaS~ZsluJ~`c^pV zky=Mm^%Q!xcrxPQ{>;V=owdPZ_Bk7A@|DW|-Ig&@Ouw%HwoD^yfBiTn-7pyxI*bMAc?xD-I8t#@y>Z(8NmVAP~AUG-AEa!Z$Zf@qY>R!iPrw-d0r||!aGuN9t{#(nP zQ{!crZTc;eSz@kMi*_P-fTJxbtti8!?0p0=6yqq&&Qe$eGa~|0;<(r+yMTG`!slu^ zrSu1Vzm55|5;I(30arDJ%+uBm93)o0H$ZkN_mqtqhjCN)jm&ff?pCwL&l*Ur;S51smB5FFZxHT@P_WR&06; zgg=<2_UJy9%FPBly@F&AI^JC+IbLT$0-xN?NM1AORU#>X`*56S`O|I$fA)CB^JQ^V zgxdws)?20|8DMUiVF&jR_mT3I84CZ<)L=knW?q5YyOu|d_GW(;1l z*KNDx_O>(}&|6z!pIcJ+u`6IQULI;vBWDQ&QfKHUQzc^9e>A;f>#ST*+QbK6Z9Fq* zUL>{VXp4s>+$Hs(rM$fqSZ_u8i228fbu+l1wsCYKa`=#aTIHS+>>OSv-{>qA#Hw1X zXRD;Y`8w@rv^{W?WB3IBMkKRM-AWxu3`J5gz>&Xr%uLL8B z8(#fsqH46=&Os2Q?)>yoQ9!8s?>f_g{Z7?g9nRz)I*)7rj+hCSI>aJUAzkB{wJ>__r^C$*o>y>zWHpuR*~&BO`aq-f*8Ig^$^h`=opiwU9xVaF^WLAPn_|!I#Tx>75=}9`t{4>8F!T6mkXQoM z?R;4LK!*Ftv9wMXMT9xC=60A~=F)TaiiL1Zv=uG963g#WGP^ST3T-_w(5dW^cPpP_ zebgvZ?KQi>hBe!9A>-|8#fJ;x7S69#Dwyvbm5~N+x&cs;6a7=o>NC^YvGE)+xeqWJm9RT$F5E5hMxA8#sW-Z!U(7R5Kw( z&@3wb8xw>yL6tj5y4yevxIzum*+sq^{O4lpv3`os)o72{xV@{j=bE_D z-0{naIyY_B2rD{lf?uLlHwMzDJ8C&)H_n+`?UI)uj0#8%u)2{1)?+Zpe#LtA)7)ge z?#IF|M>ho=8YJLY7m?Alb`X1g#tBKc_esT5L@qsl>cZ*^$j44UKzEI9c< z|Hiq=1S!~aOnx8t-!_R~7t-J7_1E>!Sa_?zbwmVq-iFvN;D%F8YB1zpn@HS{rZ=46 zycvJ%I404OY2UXxb-{q|!-nLT24ua-7Ynv74%(h{c4kw|PGO1JTO3`5uc?Gz_1S3# z?qJe-ogqsXZ+LWvU=W58FIVCPP%dhRP>fv38yE7_)xhB_46R_Rp%1v*838@0ge(Jc z=MMEJACSlHY{$&9{9VnMA-|Iq%BR@}?-1lEFDoy#QSIpQqLoYhZl8|Od^TWv4WP$c z_1dY1@x2`T54w2vK_za34dr!VG)sK54lYKT`!8A}N#~0*0U3Hmx3CV_ zs^>y1$Wph5dH*%uPxUIzgW37uDM@uvyJN6?zm?<=-8!fgKHsvDq?f%=+iUHLVO(pA zk(!i|_G>HHh@!6+wK|pKc9j;Ks{drbTTfthCJ+?sG`_;a>NH+KW?rcw=+*D(H3OzJ8BwY7nD82c&~&?SUo`c@+DHFnZW|2Oj5 z4CR7(i|RJ4C`jEykD>dQBqUp$XKqmh7068?O90n9HqH-c5%@>p-OE%&crW#Z?qeN| z0M&n&Kjeevh7Fp=*-R{|7Z^Qu2DHTg4m{ERg)>+JCY&~8nCbC2&MZOzD_=-5R2`Nj z#K31_+DKB?A2{CrcTxY(#E5`3vPB$?;(WP3R3@!E()v4*JnOjl|K!J{roa#| zK3m#58mp*@H0qM;NBedNWc|M@>FEFR_y3Qd3mNwt6BYm*y=#YrI8I$?07FkI8XE=e44*uEy;9pMb0j#Ve}YQ8+K->dBQ@1$6cPN@>(G9!Lzs zp*W>+yZCWCb@v8AY5VzA?%j_Q{7&6YVjG4Dc!A!nb3-OyF)y2=7?dgia(Az!g7KLf zpOP2(OUwC_0fv1*3Mhm3ZX||R*&vH2Fhz;wYpGkyHAJ@lkFn_tF}#6hDXp#_{_f-w zV2fG1a58npj-O@EJeP^{A9Mbb;dI;nHVy^X`fwlO!tKA}bf*FX5Ff@=kl0QZmCQ3v z+zRZ*FN!8e`B91NA^(rW2idd#Q5ztBC#~xbp;&;||1#({FzRHfw8Tj?r{l@)DU_S? z_AR4vekC%e`-3Ei1tM-A@z{DK*% z*p50VXs<%y%bF=Xkr#yjfS(RSgf2*h;?|A=TyoO?SS46jXLR$1IQ5VK>2STKtEKY6 z9T~?bMF7@1CP-;d4jYPg*k*>3knf11V-4e?N(`17dW{!;_5^_sAIr^pR?X*l3f3MR z$t~(&jAd+vaQ~A@cv-Ya=c${j2W5gI5m2_p*KEUwZNXbTOY|L<9W&^I90XW2`Wyn- zKeGCC-TvR z7{`AUcp5S2Ul|kvaIKU83|qS7#nz9QVxT_iK>%$K>@q(Lpo$%8T72l6Mq$>dCS?$W z%;4hhvgyY8_fq(ea2^db+du&l>o2_rv=tL0VY@(6G$D7Njrx^?hW0?*IEb zB=tr}5S*s$m1>@`e@ZU#N4zAX0Jsb3xfRsq4#Dep2tb^pH*NpHMI${9AZWn@1l}xd=;dT4FneO5*tHb^LNWkN z?D{OoFa|znQ4w^RS&qh0035h|k8pzg5ihY|0Kw3gv@nbV2B8%Ajvi z0AX`LE*`WMrQEeu@qd@4|Ex7SKULKcipe9nwBi&9Ut7M%0lfQXb)~J*(r8G8`fo(i>b*x z9cXs&NlCJ_3kr!;7jnviFmoCG_2OtVe@GWf2T3j;TBgh_6#Da0Qya{{a)4Cv~HyU=zA#&rNrBQL9{Qo#*O(Y2u zZavU{tcl?AlC)lMOqGwt!ox_jJ#hXB%`S-GRzX3up@WK|h}+Go$n3JQe=YAwpWES} z+g{dDJ*08|4B*A zxbgE2o3DN3H!BkBJXVL*yy12qrZwlI?MF*guBAD4julp=!scWkgR~^tjd#Qq%dtsd zhkP*pzmvlyH7F z22GTZ0P%pHBzzBJE@Cp&?W~<_P~Qly`9FK9$5MP+Lhi#b1=D&Rpb4btTz(zbPERz1 z_BA$i1PTt8G`mUEC_|YMqkIF7eLRVeO>$3j$Z)`vqi9%M615S+Lj^sA_n7Y!iAW$Q zEfdhBY^DmYDY`7j{;|tkH6yMSXCszQOiSnvL;I@q+K}1#NACP~6fm6)m_{b;xAGA)>n4EO=XypS)Bo$?qm8_G(k6(MIO*H*-^{-KgvlrFJm;W6K zwEaz1)jUc}2{UeqQ{%hnh{*bGdD?h~&ZD(U zJfn;E`2?rQkUy(BeUO9&4guuAqWfSlm>eAV3cdml{r?_N1c3u-4-tsp$uGg^-&W8e zz-bT3(Ip(RH5ETpc4Ex8DCWwzKViR_a-&Q5;S1(ZB%rww48EBie#fW=xeo*MANj{fDm(Lq^1qqly!;h#Z9&CKLN>x2m8$21 zfwPyUg^vtzcRJcFkY{8mvSE)u%|R}y*s}@Ex%*wID^J=>=d-gNaC{hVHYdC-_lQo& zQ0yuiu=apq47~>q+S+$_nwH$U2-R$L3{SeqMM(%vqDsx`D}F;A=`On1w($LMfcgFt z!youJ1yFw|4o#q5jhI5L4qq2@ zR1NXlun4>`ktqI8I?x1oC?l{{w3BpRr?>;s%%Y-@<>lJWFzyh$P~8&y$K8(6K`nV_ zlg^s^4}k+>hlY=gbKW~9vddP%7$+qgT8Y_Y?nsul(ac;#83qs}ba>lQi@{i3jE7Hi zL7zG44J_;ZA1;azU>W7ch-C|x@hxl8Lq0i?6F{m#tbi^BFQ!;aS)x-tVEfO(!33WS zeZ}IrR-$%9NF1gZbXsu92{~GeLpzGkx9Ze{Zjse;!g2^+`~RN(rSA;*>Q;aiNDFY4 z0;_i(r-K3PN9?)~(YTg`n;Kh_1PyLzD=9eo_^ikbg@lg1`=k7)X3mn>rU*4WMoe!r z8?2tbr4lMwIXY6Nh5*g00I4*R1A^t-;QYhGuXq@?iC+1~cgUatg?(FP_znD6ff$1{{dA89Ad^`nVuX z0^W;*`om8Pc(2V$HT_{j(PKc+Lqq_*m zj>brk%r$1%&2Zm7O1-!;C4&sviR^wd4~fS8)2`p#PQqR*n`DOCwwap;hK$G0`T$l_ zEesLa7&C4}A zK`JO%NGp13MM()sz<+MqPyoQNG8EvS7WG|ySb$fFg0C<@Ix|3)u?(5K1E3%BKI~gD zsB*eIB5iGLxJ!Nn{Hl}XAC@rdP@vyvRYg@fGQ}phh|{5=p}(9}(C$|z20OEg=^SgC z7Y!Wac^367KkLQ9bp00qy=VLIGMf8i4tKPCO4a~%bo7iA z&y19I@$UgU3vODXc-omPf(6Ly@=Dws@>tlZw9&Nkv)qIqQ+e&@TFwbRu6e~nI@m+L zgX8x<8)WwAmfA!qM-hiHb_lpD^@6)O>B4B_&nxf>rZB22hM9#~$smhaoGUjODC>0w zmUNCJ-g9bOn*yN<3VKmlQ7~b#H9LS@k^U42D%|6pM;vaic)w1WV&di{rU}*dPeRhs z8-?s6yXnr-8Y@s}-CzGy1*#hxWkMs;1~tGpMGU+-1b5=XIU)Pg+&j=Nkv8RiHaw5b zI_kVwT`EY7%+$}K@`qvenKqB03X$NDMsX85Lya$p0OQ^^*&h&TF$b@8x?C@Rn1044 z0_?2s{J0S}#?msUeBJF8@vT!Mf-84PguldK-xOUFm}2@7$Pu0)EKw+3A^}W+6XXZk znvK3&+HrbFg%pcD2fUao_(at&RvnYswpTp~gEZx~pdzsqejc@7$7 zgihuJnJhiKlGG#?nBW@+Pivum9+e9kBgq>5J<7V1AMsd|@CU=S-oN@L!fY`po#X=@ zNhCa^_{~_cNr!O<6StSf&vH+CtoHVx-_TxYUz)&9JGxQ;t!cSXWLelkbtQ)0?c2~V z4vnvO@YOyJydMH@ce5)IxSutRV_&SGwS}K3K9<>Sf;zQ^VAYXp=i}}H(G$h zq-|NfqTY!3HAnxSHz90dJkQnHOyWLoYd6A`)QC?@pPT$sX}dyRc)qN zi#i(|l$9b)%`!b&@LUi9#+#AVcoCZ7GUwWp0dWhZv!+w8>IG^D_|mY=779dr{>x0} zw?*{_(pGt=w{PMkj+g9iq}eY5VC-&XK6O=*`~_+ztmZ(zjr8@h#S>N{sOV1msL&wH z>ML-(vZj2sCpB}XuCw*EFH~)hnG}BVr*n$9qL5KX%lj9KKcF&E90?h0+3w-rtno`7 zFa*7 zX$oY(4+pk*CLCj>ZX=8?b{4U6C^HE7d&}Zc!S-XK8uwx8F~5UBMfd&so5<*^Ng+V; z2Be>%k4oQ+F*AogRAH`4MHsl=FO6u4_SH0la?dvxtaMArz|$nw`2ox^P~PglOe@1Y z3G$z476qE>5~g6WBIbJ&u!dVf!~mK**L8>9eE4QyfZLe%(qDRK&#&;C{OHUebC||f zvGmKD%G?N!LN3S2!<~kE2eIqQc{a8$Li?(a)n_KqqY7!tst}M$a-^=D?};CH<~Z^P zKOX=mfn-@nWCkRJHi=?Ewo6J02ai-B;>}2s>-oSSY@;geS2`HYnk++*vSsLAFBw|( z)U7a@iO}YZUobf0&GBLuf`eT5oIh>*bv6Qe2&=Y>3Bkzd!xV5> zBF#ZLGW;Mn6|{arYvq!imIo`wkji;J)h|LNIY|J+U==h`MLaN*OO{WArpD{}W?^I z`T+wt2}78wxBxip>~-jOY9}#`9sfu+zN0hvh5$4RnlUvVs6^_ub~hWZK_^F;=l7*` z?&v`S*`Y0sF`hyAnX>DriFw&Skq7$~ZU^^gv2&Y7ncTP)%OmrX) zTRDSQBY2T?(?D3|?jakh1WIsYK(P8D*}>wsoPKv{0Hw2~z#n2}>K7o?1r087tc_)K z_ZnZmm0zA&GZjt-sl41|4gQT$^}B9??40qWb36~k;&b7>YsbNP42H$x_h+kEI7SR+ z|Ix%u?RMI@3@)W%Pn>i70_}WE{&|M*umb0GFYp^#kxA0ykS07G&gfs|JzFMia{!YZ z(QXZ=SDL}QUOILtRv@E7JW6{wG;}UX#h~i+>&JrWC>+g4ZjQ18!Q!?2vbO5XqYny; zqNK}bb0(k)vJZt1__xCf zD8?~fwGjVg2mz1yVG%p~C<3hww|i&E2AvtF8^lL6JZh};m|vg0iW1w@2ATaD1Jcma zjdagvjczt0pp5v-bk0;Q=Pj8bWMce0fnA>RV*F4iWbp%;ExNNIf;P*H8r>_8;m>n< zE+SH!`_=2UtxD95TCpaV2q~En?#Rx1ZW^Q)rr_k|O$4%Hv*;=L9b|)g?4dsAiG>-y zD~UZ)5d&JE`)eFPE zAd{twQCRBtePppP|Dv>XU|qNB4zbqqC>g?jlrH;qi*o{Zf&9An4N=GvHy}p3ha|Fx zbe*w!`=vP1Pxbb-xFgfFs~`zr*QlL2XuQG;#CS!o9Zy2m-;G+a^5ME3J&N$W6lCNV z1PZV(90f!OpQpn8uFA(~@2u^vnCY;&vp+zGg<~{oz$}q#X-P>y!kcfBorpv7XiAO` zx}`3#MR|RX7WR~(S-oj^0aIQ!e0ANv&~AQbQA)y_Y+7&RyP34t=kK+%*c! zD-}nhEvdvC*CoY3#0v>3zZl9tmiQKb@-63l|8fg4#b`_Db8Bv@@)ozM%-P9uitbXE z#H)OJP@7nfGP<4kDpfc-ob?r8EtN>%!AEv#BHz=r;zy2+x$G#n&XW_0+NH?1=Iern zr^Zek6D0j+6lIm(DDr2g?TJuUcp?&6K)lyf?i~q}3{4=}#V4R$7(A0v3#QdD4#S@7 zvr1Oj5!x^5D`8(O0Um9$D~z~?`*}n_M5AfY%^rurUaLeEc;@{l8J@$@Mki-U*@>wb zv~9LQ+1|$xgJZts1>(5FnsE!12LZF$!VvP zXAye49V!AYKl*Sb2W+AooJ@uf2sa|Q2V>&H299X9s0|3K@lGC@`JlV^Ksn>sF2 zP9<5iyYbj-6ZUZgr7cpV2Bcbpf$)U2GCyRt+7AVwi5NnTzd=#DN+Z*22L$UlPyM}C zG6jaKB^mMbp9LT%8ES-lDI0nEu5CDA@pUbqCVFp9v_gAQ9uY9`#AfC@n@5=so4o$+ zdIoQbXG7cr#kbJ=+)2k^d_(ujYkFK3HMXUx&!2u#*7k`laIbL{P6J0sr#oH;#%ceQ zcAXqChRX;K@sp+1Z>mDXeGwcPx3XL|9BRrY7xwKYm4JQbnI@$E3g$J>?acB=7C=ma@#&_W(hZIgz0$(N_dkniIUdlO_=`(7~fGKM^lT1%=qSlZDkFH<_MR7p^uA}kVzJHZ-mO@8YI_tcqeCWMzutKUi z2aU2*Wu?6+EKj1_|GcF_V0Ig+!t8`=S@S6QNyi10VCANE#SN|{v%1Y+Q{4e;U@uFr zvz}WIfBEf2bJgIxE!ZnO`c9!LJfEtnI#!Co5ntJAZW6F!V*6p+vat4RMxzUPW{#@dSJDF(O+4RM&5R`50 zg}rjy&a+w5IX|@IJl#nTVO#0DH*3ww`bGm{vP`+4l%?eP*$sr&2i+KxgIY}LrXa;i zu#jNa%Cse%nysn;0!n4)hI52x+_!%7(XkA)?}#JdR3x0aP=^XV+juiNg{>pBEKky7 z=89BQ7-ofKY@hD6>=qH@1LCt~{x5f_7OAmKr8_JUvctCv=7WwzwY9}_({Ijvm9*8Ng3kVlUtE6pxrZe%7gugP!11pOHkqt>T)4ZxzeNS5C> z@-$fjj3PQ=ca&?J3RYd|(WG3??2^K)4}w{aU+LX^fc+G=OY*@%Pcam0xB>=cyyh`* zvcmC`&c~U@TXcuU;yO%gW_{7D~bh3Z`1Fx6rPg8zQY9HilQZox0!{cNh z(ad4`w7pWyIxm**HST0tyhZ+M=UP606`h4TNdU+M>gOl&$ViK{RTIneRFS}Qx!19;Uce1#iH#r*twLWu5=E*VC#Vb6ScpNqpB_`4PX z2NASz!I9>DSdFX~I=9MUTYONayy4!eObi%Y&*X>_@JGTZBmk(*_;SW=(1+vOmlzt1J zY~j;9dPY>9dURLQk~hR1=lA2f){IZIiSZ zd;&JdNXA3-=oi}@i}jtYLzrv+8wIEc(s%LwvS zggsnD`=h-BXzAVubfixw~M7bS8t^VeCq z@tBi!?7?-(DpyN=W#nc(oYs0)9N^Zp6L|_R%cb6pNmc$KeQzR5;rDdTmoFz~F5>Od zzO<1;aFXAkK}kH&iflnP60cKuIEM%+b-j)V z6x^_#b2DRypB^BXR5jw`26gekh+gZ?V>_dyhL!I zP8(wg2`l0ZaCS3zv{jvac%z+#A#c*k)%-)1f71P$fBoFm7NaKxP zvp6m`#A)9Rm*@`v9$zc{rTXsVn!4iEB*Vsz9S(Rb7U?3g`?8!FGrw{*35(MJw}tjE2OGoBYluBCYANL96X-7sd2lG$ zw5QpS(OXiI^9-=JIbD+hRsWg1o z!|Y5fUUq1FKK}=C4UMs0cX6{2{H_Ts9CxU3NDcl0m2kdWcVCbOphYHA@kOS;*x_m# zudFDKcJ~%I@~%@aNW@3-nF|q-jk<-?{IWGM;AX`<)>)p@T36911m+cuK zGGCk1D5ZlUJp+>AgZrLhvdL{)OEg@5VlPqMH_woTQ}zs6L^Ajn5HX0+iCQj*DIX&t zlwgK*iAt&Q$0~mM;LY#QGAxWrj|&JO(t6ynlF2z7RqV7Xqs+<*t#TM2{5wLakn0L% zegfWVS%2Qq^myoZ^kf_ipE^nn&r;D(C@`_j`AgeRhp&Yk@}dcAHV!;nhN4oRwu;2n z3@bA`Yx8x{e;F1!C#=I?(v~ARd|OuRl+I))E-M>DkM}b7dg2QQqNsI|NBW~8-gBI7 zViA?M-ZZv->1v?!D7jh^N=nRH!siO@Jf2lP(3X6 zTg~uU9Qy+*Yly+KsXBe_WW!Sl5>K7*)~<;?VJb6SzxBj?wCJgab_59HukeM9Ew$>A zc!&Mdu{F@+?=D_l8QG`Ie2B;Hb4&U@)fL%8BkiPhB2EXu(Pj+&CYWzh=v;YLV)`-y%;k#zE(ukkvD}FMH!uaj&YI*NCaGU%1x#JJ`sRL^ zr%)|wxp#FK-OAT_Vn$(hV)aQ`ZY+0tiF-4X=}UKDo6}vTxOb4{lWPK5uHrVd;Tu%? zN=bP?m$qm+lk)ds08VuC1mK+ty+#bTvs$ua+T%Xy%4|jfzsZ zrL(Z-SByxcsfE2Qo1ZETa#GlIB%D3o@T`E|Zce{CbQIo;!v4927!{pC-LN&L3 zzi1|z#esv3WJ%XIVDWZ|(rmLwwyD#GTC3IHZh+#!z(|r>TX4wPR*!4BS$%`*^mcQ> z5R^1WT!po> zs-)9kWAbru8!SGPsn=y8Pd`8bG{S=7D2IE)C4K5(2dfA>UxGreKO&Tu!=i4InKx1C zww|2?BjDP%qBXR|X(^B?&h){B;JJ8mpPvK0_2=t`msg0}e8dnFx;5s%Q5 zOG^j;9@<cp*Nha+_KSC-pXy$5{r-mtT`@$6EubFaE$DXOc(DfkNm$Xh2U zo2Kk_=M71xBTI#D@%!fS>L8>*RX=o?7<& zGEM-OUv~L?Qn4db>TW#3$NGeKL45Bny5tH3HK?nn2GCs3f=;vvLFGei9FVZVM%BH>rjicROqpJe5Txl=eMO z%|C8*C2Be2wftEXAmA^;l&Cu0?({H%0evXA<5czHeRyFKUF1s{e@}gfr(_ha`?=9+ zdzC#kVdPPQLBN;IA`=5Xi}5dQ8d`wpLJy*65~c+x7a0`ED5~ zXYmTn(0C9nmZjV}9xDh2hVtkM3WrE5(RmIHA2vKLMvf=3tCyN*&J@JsDMJ>_)0y=o z`Y23fG^9b>EwsIg=IsZ#I*%F2B5x979G%Pzk@nv?4XQR&A+u~(EcvO9k{$=@G)Pl> zifGS5GKzSDXBA8rvp1H*T8AM8ebKUQMR3=Cta=4R(Uij z^Yz9U<{^W34R4JizzD|kmID#>ec_X}*JL&3^UcM$*91#5?;&^iN7gaL$k2D{pSqP^ z2C(UUOuM8Du9A11)5eu<_&>V`Ih8iypn@=Z(?CfAp?}EG7Bt3Y#GnS?(})a98LPh$ z$`=tUwU5mQwFc@{8WbmX<~|dVxQUBZ*_JjF7A+H*_)_4+Z+&Rs6}WvU+zjD~VX0Gn zL;uKk%=-obmo4uMLHz=n(z)YT@cP?u4j-k?FZ?_4kzVwpkB%_n@KUZ_-@hn2R<)~R zV~cx-8yK%W9~513GmshDHo&m!`SeG2)p0d&syznfVtpwfy!gT)EB?i2$~!V*`e;yS zt9fc!tTAZTvKOy5DUqt7zMqq$^SVZ2Y}SPD!=>3-S){PjlJF!i8^3Ar7Yf&#?9Lk< zzuf>5Ffg?b8K>(cV0%Cbh<8So#QNRfGb|1pR3M>HQVG0qbrN2rNE8I1Jc?8XzM9(SoclBWQYc%p zv=6#tLozfA6)wwvDN4;|xHyxjH-oG5PXCx#w&k`Or$i=F2YkRC7zw8jA{{e>KG7C0 z;)yL>zhmBx7qg8ME?mXpemg-9DS332_}p4rXXJ7KTE8XWJeV%vQjW;R&C?caHePQ{ z%im5^fRYL<2qffBDnZb1NRo*XVIt_}o3PUI?D2t|V+1ubbwU=96=$pNT2QqIXNgVW z<0#6$qb|?DPg*(@lp5NnRQ^JPYkk*s10UF^w&6q|PuBH)Oq_q1qg2SqejRcC+Rg;> zIF{pS{Ja;7Re8Q%)ShGkJF+_!tt0 zbmcV@TSEN8RxoHTgE~}Nu7#v9NuWaes%QTW%ooJqF>{1~a62(;4DIf7)X>|ZtJaJy zo*FCK^2jQvkGL|P^5b5rvKCtF^mH0W_&vcbs$M+l_G(gBx6}+%1P|(Pbw}t|&b?4n ze8Uz|an6qU{HHYR_YJq~T-vW3xWqF7GE!qo#V!m-}|P<`_p+bWzN0 zdElPx_>ew5I@}d{%ro@bIB`uJ0*Vk42G9$jDKH;i_USG)dh1*FWYh6paqqTGR!b(s znnCmscxm?Hmj@4_SLozgJR3KXF7;K9C#^hSH-b+RxWjaPe1(9l=Vc4!UT(tI(>+5t zF4`VgJu4*7{=@mXr^8XY)ETO!tLK*00)FRm(zGh1d4p{JvNOC1{t48}Oj=g1MS5>L+n+hdOgvm|( z2$MxE)WAD#q@$gbjL}h?-bkB9MQz@Dh-4!NlxN0g1!ZD=u%w_ANugL34n_j-7-=S) z8T*h4EBGst)te~qmzC6O{nt19#Gj1ErNuiWOy&0Gtvjww_<1`b2(T4eYa5kOuku*! zB14`9Yviw+MKCh2W=MF$IOh1VlAd}#U#-lyvCCTd>pwUk4{Pvmcf3Wmo4}&_Q7+Pq zZa|5OsCG>SBVyv>Lw6+_Kg_D&v5Aw*afxNT4c!@j8rzC~Cz?fH)7LJOLM&9>$N9PB z__fsbCNR;U`I!kn-dD5qbmatXGH{C!B`5?jgkFl>w1lb&ZiKN6N_+_mGOYWPc(YSyr` z)5MwX@?j67ioD8|%MfSZT1Apiy}hGpf$m%2Ef+l?93M70tTGoZf-gx-6#Cc1b0mAM zkDZ_=+Oq@!-UW;={flg zKk^qfWEtw5Q+w4%96DF^X`EA>A_L-(a8@%-80HudQE>HT3{7_^`gvX}n?55*Gx}*Rpam zk+IuLtTV+r4tKcc_(mrDmv>tVM%G`Kn~Uodl{q$vC~^Pp3SEtpJB0V9(X(HNib#pU zL(jb;v?cm+S@&KABMVztmlM;-N}DTUdQ7(zEyRYf&~X&gEb!qQtE6-E8*)e%+H!Y`p$Dc;fl9wQz4#Rh!R>3N`=2y6EQFR37F}Mf3&2^y{ytK$V+XxS2s_ z$6mf=@nj63k;S~5Mn)d=gi6+w;FrLycN4QvA%(zRyxY`5lqW1={WCb|enI4gK3p{2 zJ%2sEGr!P>@NPfYm~lkjkXWwhIjXy%8o$(zLF$Su6TecxmB=E4D2%Gq}s!5t6hwy@TSaW60cz z!5ZYMvmlEUuKY*aYK9G>P1i3*UiK!}3V+`lbPJdj^9JPYiSlz3TzB`P0}xbQ6~`C_ zc|OC6+w-c=hlYECp75AcK1+~J`6Y29CfNmfD{A8lkcz$6H$JfUM&XAZ)ky{yOSYO8 z28qA&Yb{O+e;M2FH-!?|5rsdk^>^^Qmmmc z7x_R*SiJRWiWhY$Vrr|qfx zlDVzCbnlHrx(`m~yx%_B^C&wej8|exRmj~|GrU)+5qmA@GnN*9Z3^#O3Pz@F@S|+Q znqc|^Ql=#8_y7{3f-#|DhYOa2US1(Ddv81>HYH0nh;rO$==+%7WTv_9=VeczFF@rK;kk25WOILdWO4~Xk<6NjplX~=lh%qE55>gMq>gr^pVit|dR(w) zSVS?#0_W3RCVg1Us!mj~n8oh@(MFx1ug_jBG)~l=Xc-Vi6rjn5o+fsvkVji8AucK)n@@dMwD1H-a?hx|$>$-;~!4T5xC$Sb#8m2X$ZPp00 zgv>9}-aFAPDz3ZHyWT`)P8=v{xX*L`Bv2YyxlBUr$VBPSlop?arwnp?<3A>oM#drN zg!iqb>kn7-t0!tD{(y8o!Pog+vsAljiU~bffrzc|vPGv==9b6=AmPXU>n}EOX3xkT z-Dxyb(9j*tr>jY7hpDE9b%omk`PC_46w-Vx@iO3u;8ZCYl`152-2z?^;@ zy5R(hA@pEuBIPnly%?TueT|>S)5LrAt#R(mz}xMxnBwIqTl`HWsCY`BtI$ezDUEQE zv;d-|mB@F)jeJ+syGlX3&o-8ys);J1O4EHhV9lj@U>*M?EvhoxNW`#!RQWo*e&RWj zk38chK_O|VNnr3PsmGhw2U}bK`o3~|7I}ZcyI+Z~&3_jD^!~vQcBryyuY^ry;emjR zQHfb`;Jct*L%3vy83KZNE8qnB3%GJCu=U;X;D;cO)j&3%BR*>rjBl#W^1R4&r;hdq z_V(kAIy*l22~GQO{S_jV+>moHI0!-J5g?0~2B18}wRa#fnHjfR$kmUfOy}tNEh#x5 z(?{YcQJ;qYBypuo6L-9w&Sw|WS4cff5z0!*Zvpk~O}?{M?BF^KM=Q4>@vZ2Otg+ss z`1#`AC$$Ubm>=A~68lu@p*IwwTjVSL$kT)b{OX?QuJylV3W7ox$?8QCFUZR=k5&Da zV6Rb`yP)xfX*+ux&BT_k=fpo(6w+|P5}z?#ICU5gXzA^XW%+X_~q1$&v*{oAyZsrf95Qae=W#!q6ncWM8;$zH1S34 zz8kWAE);Dc-)`0BC7F}5cWHv9o1-~=1ScbQ$h~xK+1gp3YtK{-j9lQk#CJd!h5dZi z>^kAP)zTo&ADW{&R;G<_anH}X)xuS-wh13Yem;Y?$?DdA^&J?K%rha0F&@caF_`NK zJ9~R`xU*S<$%a7)tUX(yzzWl_-ray0F#`h9R1w`(#;btB38j~**6(-^-?<3P33-)Q z1}orBq9Q+UT%scVrrusaV{R)@MVMsIpUXmBeYezkx;+BkotP%5cn zaAZN#-byQmqx5MtbeD-#@Rt-beWXSF9dsE3pCLCL2`vdt3dgV)l7aL}sX+Jhe#}Cr z=Ke1InxSFB;H_-p^C3m|*T(4#&F>H37C6cRJD7x(loU0s*oxR=F6`naeT&UB;86-O z0k_}|Guj2`L0qov@!lYXumdw`;WimwgKG!}VYNkYDH?D!?Y1{Q0CPGwK9J4B$vXdR zv^vK&a;wh--{dtp^rOy)`jUymhRfZS&;eB3NPU97Mw%CKedwdlOv~x_#DXKbtY6~N zWoX`JG*Sk<4Us}xwQ@0GqRg?RdzI`z0tt=UvN8*Ih7$4RsZoDe)zi5lcHESC=&9RIXR_@!r!M zYPGY07|QQTu!Q?I`XUQ&OxtD3;QGTM3ds5-skW>?Uf}K`33_9?IepYXG{UQ>oN1o% zy;$v6x}MvqajQT5TE~|7?`Uc}*g^cm;0yEY@fce)e&uOG?I+b3UX*`mznJk=E1^9Xf3(pws)>J6Y|z397X)FqFQ!*)FFi)$Zp)7Z+$NDc(K&S10i6Gac@X#=TAO1=@ttai?JW) zezY2qd|<&ToeoweT&I0a>d$ngdr-jw+(&UAz(haslSkAgTVB+u$o9wc9+iSo=wuYN zw~^hzhA!_LpgEF|+LTmeoZ!L*^UBYYy%<%`9hF4-(71=f_^Hf09A0PZ7f9Yj{Yg!s z@|%=9$6jA`L-@95#Gv|u93mJsk@6bv9)67=?V{kd0G+;5;Ws5k0XTIxu&V3f255l< z(D`|Bg{m;&gL3T0aMWmKD!>qFXZE)*Oj9maUiL!phe+S=@_@#KZ-ljLe()G;xYm^F z4)f>jq&83W0h>PI`{cNY(Nr`VKvx=ftZVN*!B2$(;nDOMD{2Q%v?q-=iGB@glVT3J zW^THkZj{q@u^OZ#(&4|#ujdWF=u(zX{~h+eqr9G1$lc{x?dLQ5xKzlFPnA=Fc=;s> z6*&Ydm;qHvAt?7Xbq-weUUR5Ef`hBcG`Gp z_Od`&Td%HswxMkZZ95;Ex9UQUs7x24RHq0ILtj_Gj^huBUW&77&X;d zty_STC_Q2mKKYri|K0dKRvXYh6gnj%-jlx02GOqbc?iEWnqQtAx z$3rb1)hE1X9-cry=+C?4JzZ%a)k(BIAhfqOH57FK`nig8U7Sb~PRTJI8O)zM$5($TL+1 zQ<-_}MgY9G>G_YC+odvXmCmL9G9PPMyAS7ObsJT*p*Sf0F23h3e7YjY@RQ_n65ft! z%-SuHU*kSA$7Ll^j_xL8mUqbdD~^$w{LsD`wtw6-N@ZN4XvD31WAt#}DewC@h&Fj? zm1iQC=e2PcKJ~EdKCZv5hw+r4Y8ILt;Ks&?ewiHRt{>|IjO*KP99lF;hxn4T~!p80-krA6#!1yIa_2wtPvE6D3J^c6T!nV>BLH3u}PWrqA zwFG>V4`=!v!nW^i6nRG0g0o!PqW139q;Zta0uPI>b4kWJzrONJ0eM5tAN1Bf%iy!|Ky2@ZrKO|Y{2Lm=8M`3i?U{hQTMiey~)V-Scm=I^2O5b z;(A6KbK)Y+vX&*WDVKQ;5Azn{#y1*oRqMOH>cBl{BF}56XA?Z7UvcF01*Rab&%9_1 zo+Vd)#Z3(zAL6ZrqjOiy&>8mK`!5rb4hYQkIbmuar!VIa%bFrP^XV8*H+J=zB-^_4 z>TOspe;UhUTcT+n_tgU}E_B&emvp%T9BFiY=kVSh<)}<{HjfQz~V<*Yi zQTX{~e)+afc77&44Z$R12MT|6_-A!l7d#%UIHnwoSXtm!b7G$;(oy^x;L-ilkr&V{ zha2_xQ}R(q*MsU|M$`XC*Lz1bwRT^l&rU)}AV4Tef|LNEiW-`96ME<<7WAkDsaBfj zU?rO(MG!+#0TF_tU^$BA9MPjD6s1U0P(e`wB1KVYN-^ZQIq&B-hVcU*tua`bC-=32>}5*D@3W94tF3%S=O z7072zWc?ooZAqc?4ACy;*zt`|O#*Xe&F>u>(DDA67piqc{`{qv{;?J-9wuJ5ncLvJ zgg6SrJ$Sr+g}WM(y$(tk5=&5IBJ5H9=2cGsROxqeK4IbW_#%UfgF z-!GN+2FaMN_`|`jZedeb%(U6g$JUHnm?IOd+v-Wpm-rmHxxaL*WWvp*{K1Rknzx{&qi-PYXQB`G6Oir4-rcBFcx{`S}2H#$Uo zK>ek-k}qmM;7ZobjvaN^u*%YIaJph&QhmrUCk-w)d`KKK| zTDvk?Lw3uS$8G*QWG`m;E#-TmRUdjBVz0&Y?dky9sk^t_Ue_~n9esJH)GWcfE4;Ae z=u{iJ%l_kq5+-FWU&k2t;j3%@QK#n_ zNSB>N^h67M^N%20BO2p5tG;sixL--gKHFr&v5!OL?Q9?N6Lj~#!zBC+bX{rNq8I}+uNGPV9vObO~~(H}d?E%D<$y(|7^Z+YBp zcuMVcNi^% zo8`YxQw;Ks)YspJJO4HdJ94?9@M+6kIHbI9^U1zw+r0j`Tb!ip1?an9pNW@nMHW4> z4%od$+H2J+EsxqCbJY6sHaY3**!1_;cMp!b2Ix_gr_C$4#gkRCe*Z+<{(L=U^{3p2 zxl?jO%`Zwa_8m0EUPsQjI~h%Gy{_4qym^YXRpa@?XtvAi^pf2-1sUn`1}>fj;jEQ~ zvrX@hu730O&V^1%-tc|-*BhNSD=@4#1Y!AA@@X1ln?k!DOOHr;-i&#sBdq1m6b53axW05{KfE_bnoUt>l# z$&&9@*cIBNaw9p%|BMgz*W&v=*c7$$GG^mYjH21=+0v^i+P2FUSAt7g=@TWa^-a$A z^~fD=T{j4sjsAoivi%}!{wBOQNyNSOOYYUvr0vb~$p%*<>#Mt2;O4UDCmA_Br8$S> znwP&>MPfD%>IJ{T?p&0Mu|{kh2^n9&F{@Q+J7rnhV;Uyy!Da{Y!&|tT4>$M%6=B+g zJ{N>?rc_`8AUf_S=6 zMy)@KmFH(3EM+gYr!?)8IDOuDe4(~rQZeyy-dJE?O6c7Q;VacHKmVCw`(2Misvi8= zyj5YS@2Zle`}+Smiwb}-Zo_!(z6F7iT%JoHQRqCqhSgje(8t8Ia7$+eslx)#aHy&0 zwYg7#YgyXjHXYzHERmuADkrxHaQQGsQT~bL8IB5PZ<3J9_mFFB<9vh#ingIh z;X{!M=RSSBI}Srq+pjdKbFqu7uuF2(x1=`QtJmaW%6vmIACDgy@br^AI{{NGx!G*@ zbK=wQi)P=3LryJo6(T2snhTv(l7#8+q_1 z1%Jx|J(!SZswgG_f9a%Sx2jMsA$9#3r*0xEd285OzwS#f^+!!uYw?6CkLPBzut=+R zyE&u6_bpz^&jLr)XB=eq}*!Xk~kZ5ekoWQ7eU zHEx*srPi%4W@@@@jlHj*5!0=Cj|WT7e=a8Xf_mbF4+IWIn)Dp}J3Z_RdI-Xzw+ ze_w;tNo>g5qIKOz3wyoc{Yoyk1$VkiI-32$)=6Hp!t7++kU1X(3d^&jV;^=i ztt*e>h6k>Qx1|60dHK=KaKS1`h}_V?a?;+SHM0a3YL~j+UHkL+Hnl5i_c9p|x4J1I z1cqg7P_buFg?Cc(OCB~SA?&A`a2l(^^r_y}WfWt{hAsz|qn{$t z2QE~s+FwPAG+&(rlt^m@AttE17sgEUaAkoz2Se7wlJtMqKWEg&0-DX%V{JLU+Xxb! zW^pI`4p+haIaF#Fe+75$alFO9_%E5cO;4Bky7`K^*-Lx3y)D-nx@A3hpY(G;_m{fy zTvMz+WY?_bg$g5sLJoY`+a@`)Ald!oSJC+DkKEX?`cDlMSLtfT!}osIbYOX=_8YT0 zQleS%cyc;cQ;6B_ov}^?PMEAu#i$h^>za|wmmt|ec8t0DpO)m(1duBp?XU$V9Q3^q zIoY*QmMjN~M+?kEZmmEo{$2Y%pX(H1Sf~|>wPjI6SaTtHn$zyauLlTDn7j#dDloek zX!GbDUMQN+C8W>ltlq5=0kur$=8)J0SkWiQU=y#WjTPqpIjXzO&i%$(o&|>LnQ%z34j@mGn^s`)sw6sEXXZR2|LZVvGAEUUo#PuHK z?s6ohB~;Bw>Gy|*a+bfgH#6)XxRrx`tilKU$oj0n6F71L>e*J*fb3(VD-`q+s8gNPdsC361CLuERByy}JJLMzrWuuNpi)|P}B;d!PmN^8DVFHL; zJ?tS=X+_F}2=6>*f{yf>V|k`Bu?lfu1jXzt3=VFI^^G`Gh#0!(iWG$sva_&OcRnRGqlX%&`S9*wS1>xAV?;it+;4QI-~t0IGbeEOA4+ zjL8)sTTm`jK(qc8$5?W1192j?&7+_oTR4jj9>=*Uq z`HlPSlHw_@^0hC`340m+h8h1qy*1Ug-O<$gwFQe&ptmtNFUuJ8Xo?&PF?>5=l$^*) z?w|u*xGEN%tOQnG!mnFn3$V7DSWzmH$zWM3`FQ+&ss2ZjN*5~}(M2u)Is2ya##OzR z$muHpi!0d8LYK?LvSzOHfV1%@i!Zy;xMZRF?rpinNoFYlq-2o6djYW*)_RX~WxXh8 z!~=*ytBYimuM)1iGeVE+g#FuwAI=Z1&4+PB1V)f2{r-hN6TE%Xab|8+0|)Bc75TA9dzA?yg~K~xm7lXV=E2t4f%CxW zsz7#D>4MmyL4pLr!Dhj*2p;$>2*bi!VfX+`!qyQTwu z-l%|bjhvy03J>rt8J~cis;0AK63iHAN~DYYN02xl%hDjL@zgjU>F7}_V?=p_A>yow z?X3f=#Z}h$%rp!GK{D&5>!XAYfxb~#nd%xgE1iv0hT9ZVX)GyxumH|pQPxy`N;pW- zlc|ml7YgY@vR!<03Ag$sBo!`}Gg_gd?`P<$zc9eKDp1o~;_});bTgFS6#u%!M)zH1 zD*SrmyJX!Zz%3-Qej&mW70zjKq9vJFkOFm7p1d1@hNB^Lp+sOeWWPs?Z_b}N7F|L` zX^NtH!idV9l}gjn->+aIjo}a_D5Be>E6g+rrifezYDi>-eZcO0+5N$o@7ls`pz{k{ zap{xh;B@oeG`dU?pTu`=UXI0Ck!Z;XK~Ig3S&G1`wT6G7Dlinu^Ozl-Ht?gH*pr|O z?KEcvmx`ff32w~Hugs`L`(%F#&8He_MlW{qpnJsSEOgBtXF!1ME)tuiaY%d?aY*vg$GpYOYreXd{8;OqcuQ+2TJeP*Kt@O&%MMpp?Ok6q%`ly& zygycv0dPgsuB(lg&a1K5#NbNTwK-}cSs7s?Kn$E$q#*Z-a1-A2=8YN+RiIN3;Bb>H zZa*2w;K5#e&A1vXiepZsnFfOuhS*1W)R(~kBQIMEI4cw}nT1U`p;Qu4x|j}VJO?yQ z9UOs%8*{8C0-A?pMUM)Hdl4#NBX01xS}9V*%AN|D7;H>TcY~K|^n1$f;2XAN2W?cR zd38TjQT8a6_0eAnA^doQol(#I-DHT{9qy?4w zW6#|v4GiZvpuT6}38(OC`7-;SrZ2B-7z`(T2*4ZXk$@(N^VE#6QikC0tHlXB*#=VF zffWNV0?UG3(dEVgOD6bW8MdM?X><*dz#q1zVPiLX35jh$n@j|ScEQ%uY7`u+)`z!u zcRML(8^OL@1d%~V+9mhuB_tUCt}@M)HNM=sVX@Qs6IZ59Voknk{@XNjoGiLuu(;)7 zec>?o%@{LV3iBnY9U(!;Zx zWfx6k(0H_*B+~RpTAzVK%_zqJbHfd}=Z4ZF0LKU|$69^C%~ydxM%MX}V%Zq00tM~l zPlYXUk0Jrd8M7@d7(dC21Z4dtPbvyNOH+ltF$9mv=wTBE0gXM^_1Cek(IMopmM4=nU==D?&5eNS2j*dd6q5vn}P4 zX016~x*Ib8YvESMk-?cAbBgiBuEYNaN>jMbYaAPvKaPo2vZ`K@>3Lb85uO=NK^<4C z`63#p+g9^v0fd@JP#VVCAZDSZxJi9H%NXls1kB_BX#+enpCrfeE%nFXx7wl#&%fl6 zYLI445i0`4=OZjH6g#q?hRtm4b;pw!2;Q}@>(#;f)=xn{zawtr-1HGi1lTG`3|1Ys zX<-6y=sfH`yH%QuGcKd`d>?-}B+4{Ab>YoWV`5vz`kF!M*-jq(izZn~hx1kN%gjL6 z@D^iOwfk1o>vwB0cG60ZW7PeN)0BQ3TqPIu;XyO);ni>@#Ft@tH^}br!Ga1S60mbY zNZ}xN1B#IYld2wiECamg3}(y{_~Atiq&Cv2j4BM5tZ|G-JCZS(jeuJ4K35aCwE?on zZqjy)=7t0IDgZqbfZ_;rABhcGz?Ih+XVwi%_GAuAnE(j#v;+FgtHj^WNG$Dg%J)w* zjXwvRTe>xsmT=P~h zWJHZ0H8qfoZB~%719Dw~95l%`fPf+U=?pc;N1Y6_Xjr92a~b}-029~>bn-!N8r}k- zw<7-YlPIE+PoSkZgP}MPS9S%wvJL(=%{397h>717 z+{XR9uYZKt%*8Wy6;*SiE%v22*)oz@4cLV_-Y?ITrIOUS?nAlrTvn!C8S8H%Cx!aI z20W`9FIfk>o3$}ToX9__I-HTR7KVdw^Ofaz1}kOAL}+b^XQV8lbyBnhtRkEa4MJlT z9gS8?(voNTY_+a*c-N2>tv5efhz;hE_!Z4E#qks@piP`&0Jk*oD>LfKBp~7*DO>-d zR+sMLNSL(gTVc~h29Vh}A9|+<}HG#{`Sm@>i z;8-B3QJr2j26r3)Rs9JVJRoV*U07>&eLFc}p zoxj-$W%6EdECBYU5uVIvupSKF(#j!ogj1XewRO_$b!!B)=t1pWW^ACAysTrInQXCe znnTVYNTl&r0?SZJfs~2cwBZg{hYnq7(;pqiVO0MO1&_u2$_U&0DI`hV66;>fqWC7&zaFfbZRw^68oh6Yubig-W zP;&M`>sp{fjyNP?wjAqP%%mi&6bsNAu9oeLjhq+!r_$3>a;_oM_@0;K%zCb*4azn` zm1(66$sB{bZNAGz0%PG?$KdpI$JsSl@tbS?O<4;c?rIk({{H{%1*he_bNolTjKCZV z`zZO)#-X5wv}!LDhHSzLZk*0x6Amgg?OQrR{plC)|>*cACK*hD%Mf z%6qF3Hcu>1?i0A`!M)KcqyEwfKrGOQmRID@i92$za_~c`^F5xSboJtyz2`LT#QRw;eNaZr$8p?ML(98<)Vu1qWR9_NdeU zJgdUaYKkt%wp0PPJoHK|O+${QgJHSI@g1nFa}%cv5?X+18@z6Uo@@H!t@AbgkX^@C zB1a?v%k6~jbzuEPKsLLM#G@x$qfYJ9PO!^I%-G8v;v3rlPEV%D)&hXY@xOSIy8g`V zp1^KbIzTT%O-z<)MOU2UG;czH*M*)%v(P16-dIx3=RplyaRC0uobDeZcvVy07?b#; zs&vtnW>8-!3F+egeR()t>G@1(mveftbK2wtq!JYgShvZLq~ZI%BU-vs?-keEWQBLg zBlRnVW4ixsdm5X0&Ih}jv@!EajOKqkdc6i-%1fLH?vg{$9x|%SJmv*xv~@Xy8Au@J zQ(q|$C;V1e6$N&8zt_Xt2Abh2Mt}l}gbz0w;%<4F2-CumCF9DHHF(+ypoFK%AZFrI zh9IVKcJF#F%P4Ozk(_o|tizuXkR^i58G%dDuO?|+YZRje%IiZ__P6f^6ZiZLH}ZL0 zlShIH>1%90Qo%K%HtMh1a97W{-aXa5o?e}lNQHy-LHIre#Rmv+(q$Yo8dmIT=HQ8Z!ksdOk${HsGo=`%JRnaD? z4AE%Q4m5K?Bi~^pd9t>`LD9;ypCmHWe@uP$%^uItwPT&;y~u0@hWzb{79cC%{frvQ zX$52&>ZYXR!o^%=n@)lVJ!T?bH6xgznXcgp;HyFaz zJU!^b2eZk@Ba@`(wy0bxu1ndgy?`Vt!gG?Fk%MlA^7)8nAjZrBC3=I3#`v}$v#dBS zOFD8tS!i;}d}@B<$Z8D1wtUrsPD(gj=29y0q&&3hYlV+D3~&B<+AC?uuvl;DRz2)# z^SKo`0vXL*Gp{&Ju?|k>?}~3%464~|Bq2P_YTN|edOiGWMu0d(Sw`8QI=*#FiX zaE$sJb=8rb_i(~R9OiLy+QzrrjD;3NmqMgH8DTeY|Mmd$mFs*OmjdNDF&PVq?)hc@{n}P?aOi=2{~YYy5cYq>>tu z)#VqsFvsdXsf*;cLM6ifZ;50ya~w($)=aeF^AO1#tq)$X+FB`Qn!pgd(%UUuuO!X2 zlB#S;h)d4OdBJ|1C^i$TbJlQ0I&Hz|klyyS%QK(D$0jwapp7>Cgjsl{2Rdg?X#a1+ z8_;jl$l3EV&GDcV)CN-5LH0@V#`7g2X*xT5$xFFC?Ag4_Yqu+h& z?eu(&qDWmTc+?pouUK{Xr3O2V>Oc3RDV9=&>ohmgB8*M-;g;wWcHLL|Wp)#PQ- z(U~no>%g^%-P~jYXoM~d)a8NFkB~qL_~4v>od-|hlA1@FH?%;(P*eG7NkQ`|)sF0y zOI02x4O}vHj`fx++&xVmC* zV9`wMW#QNe*$F4o=KO6l$^UL9#r?5q7 zh9pE0-#~OATpGWEj-t{;#HJ|n3T%;YR9Yn<=eO*v1f;?SO=3=@WU~c0SqSjPp~r|Q zGaqMB%KSdWm7%#n>%d__da?Ld;ZjDS?n5J?>4c67(?4`qm+{=3D+~&NGU4#>F4%t= zUR+?FTYOhc2)I($KOwM03{^!RhSf^|qU*it(#;eeH&c&PLswViBw)=vG~{%GXyM8yKl-FFY+PrdDZ^L2qO%0qn@E|h^TW+ zgfek>iVzeam?M&W;m)+!bM@Q(RXOZJ@jn~aLM=DR$I74}Nd{|oB@NCP3f-iikJHkd z^+Q}lf&QQYmMWcVUpp?)Sm65qI^(kEMn+?B?L5qmW4 z5>+`2_#<+0TSD`t1($3o*YQ7?$^h6~Q{4MpFGw$$i-P*UsEwJy-RCjK3XyagQ|OFX z#iE8YW$JQWCy?emOmtnMn1R-lP*H6MsZ2i&#FVX~ng~-G5xq%@0$uh{panRs=NWAg z+!o?-=S|?Z4HoBuFuoQjmb}6X<06y6Dx+eBN*`44lU5j7$GvNxbTD3kngd2L5^S%+lEP7}AwKxEO1%xlB9 z{Q-=NJcgyB>M*Ye`PKl|_8a+qR#lLTmHlGUc?4~`hQ-*XH5Gl~7Xe$#juS-TCkim8 z18J~Yzw9?P+9peo+PGVH1A3weVerkC7dBiW0T&e^fyzo7&c~3th@23|%#Ai0o7^w@ z)^JH`5~Jj9ScQw;Ai34tjg#?t%)+%!aw8VYJz&hZdE|i!UwAib3f)?C(`3*iT<^FI zyk9P}zJMbhE@v+u)$9`pn^$ORG`XUa1je>zODE(8!-le{&+-D8_(+<^550(}+oQ&@ zLUw)R<}EV3hOpV0O2&sf32*lXSz=l0c$xyf3y0^eLOeKN&I=scP#S6k2F|m=Q9QP& znS%Pr0hDgJAs%(Os76B~f$N?7ZNShCLphZPJ@4VV3dUfa5Xvu~9Y{|8^$x9rV)115 zX!G-k=ALQp{KUNZlB|Kp+Qk8){9f$Rxm1;MB}sWowv8{PJ@w4tn(T4GUedmBVSfJ7 z^+RLJR=;Q}vR0!%(nNhBysAwB__a=#7SRlW|Z5I zXlaNje~KgoC|M}Ftp&oXP9OwrWS$kD7OOd+Z)-FeAZr2Q+!4cg6&@BN!#20*;fg@4ka6@Q`p znO4%&M7bGqbN&{yWj)qdC5R6xh&}Ad|OQuJ!NHz7l_~Ru<&tUv+m;1VMN7jS4{d2`QTjDS<=628!%Untn$vUTOgJkMpi39e6z3EOKQj#tayv__oNP_~KAG6toPo z@HNh#{B;vfv;rI*drVUWjhWuMM=6bY$Y=dbd4>0CyrIxJd;uaQ>zLyZTv!UQEkv9t z-p97?oi0}>tRiOUe=de0`QkUK=OA&Cshu_Tq*kypf7j?jNK6|X$%jWrUF<%8PppKt z6HrmfW1FPP6^$VXb(9_pb)~2<8`KRmP8MX??@&Ea9J86d>)`Z>bx5sj$qxm(V+f8= z1av;Qd{@nyt=Cs%o3G<_sohKev= z%&Z@T+W6`1srC+2++3^BbuVE~K0N0sTvGx+vDb4Gcpra?*SPGQZG^{o%kIYg@j?-jZ^RiQ30cxyc^Edm18IMp6N#tKpdAjjH0DQDcVo z!CwE0am791O*Az=nZ)m>2rsBr87Y^3hyZn0Yz7ins65SHVeEtsl-r|4I63knI3+Db;G>v}Rb&Q*I z2p3L;UUvsx69Wd)E{Ep)k+7VBO9M+P&w++Lg6f8qVOy0x;5%o_;9B+{<1H}hq{Jta zB4-c^4{gkBK>r%>!rCm|qRn#=7r4XiYze8Hm0wXW+0+eBO>n9tKR&8*qMk~6xlSA4 zmFA2GcSEx57LUR67?4(_)|079^ z|F#KZHE2`(%jzNs%v2(LAJ6;Y%|3ihMO$<%s^A+Z+-UtX+2k?GWjke(kzIlftnMedL|W6h zlWX-7#Zk%xU^dT9Db~$Zmvn}Xa+MfMO$iS_GN)~)VC^vTv-o?)19ypPYo^~Q++lGr zNH^*yw;2DpDaD}%nn{(`7ut0f`=l!NzbICU$ZP8RIU6H74@w7;*R=r&lhta69G8?m zj!V57?KeEn?e0`lWi7)8W+T!I^!N)y+oQ@5(u(IMsl&b%wy!B7XDXVGz8_JeMS_Zy zEHVdEJE;Iby4SXdX_3}|~WB=t=`{xPjM&{d*N6feTO!#P}ihG&aea zD#U+UL#DEveNpF*l6HV)6@rb?7*eVpEa!J~wK->@=ViZYu>czZ52JUuU#B-dw3DG1 zpIp-KhMu#`47jy+N_(+WrA6|NEfuAImix?>ds~E$UYONY(be{ptQlG4ycSk1xa6Ae zHPF3%gOl~bTXWaHE||2t|GHqTlw`@Y0!@+9;LZpT)a#duy;m?F&Oo0L1Du@?FHTOjVRo>=($Ru!4AqEsFur7T{|EPxC+w97Xng`5T^Q2}Q>J_798L3mfn*#(N9+x+DrHD<^vv-O-zzhm2I|&*Z128sR~fiqy1cFJ5XN$;x~%C*$HW$j zW@gj78$l*UjpOnRRBk{O0`UdSfONA|ONC=R-s=U>a8%i@hrB>!V4xPuLL$A%2 znCQzrJc&-BxEyVUYdjWX5HHBJQi)oP__hdE(L+D8p;O?sW8r zNn;;KUmqv(wAf zb29jmC=1_RowCV#fn9Z{W{RnsMv<9|aay|HenP>pzFVx*mhyWf?O|P6MQ&Ufjv#e| zWH|P8SsS-eZ_(1n40D}ulRBKAHZ;-t=f_K9qx_Ef26y5}s2T(9!l4##sESDM#*9>8 zkVX`m;Y4;=&`;z%>2}TU&tz!Mb8D8DxeXw?;8CAx1`T^;1c{!(V%FKa%c9P54mg9U9xdV&IyQl|k%O#%7 z8SZQmTyb=E_wu~3wrx0qVaEDW#~valZENcKcI`bxA(ILe`&V8`NLlfvC#vXC0XkE2 z*^#=h>2(8%Q#>m41Jd2kdQxYL=wOu%t>sh;4iq6~C5JoM2q6?N%)7f)5mOWoXmlA= z*#ZT01jq0J%hF4rZ1e*2H;JIo`=4npK2P$?c3NPw0A)Tw9o?n-v~j_ji~t88e~;Yl zezWEAKV!-lVcv?MTxxdnlC06npHcA1mB#Ua%)|ojRD!ve@Mz1p!i9@8h zkpqsBBN(l*;Zb8O+wXX-*U^21wR;`5&Lw=Jur$J(SQz7W;L`TDau8^^Vrw5(aS<0q z`a>Elet^O{Q9P_Oywo7fLR34Kl1oip# zr*J}|k0b`nF9>8#0?KSTuHt#wf*?#Hpa*d}FYjvU0+P;Cx-pYs>+Xids?fY7Z)UjO zn;+#Yb}HxeD(NP*r$?`Ltb$-xuv&g5o-uG~&8`l{x^qs**{JLnE?RrWG}J~m`WmGE z=kpgj2|=uAXGHmlLo2(>EcBeZq%v>LCy-eaBx%rc5c(CIShgj;p=V{; z1lG*%@u*}vs+qqKM{{_R1G#le3+a@Bze;etN(hP4KRw&Kd`~=BfEGwAKB2r=!*(^Rjn^3q4J;-_8Bd@<;D(oCLyfha}%Bohz<9q)WHdg2Yj(iyO4D>&F+V z;Ms$C$9x7>WZK*2CI@gWfbFJu*8mInxJ-2mpb&`(r*Kj@K-n0y3nP>Gei+RoMUs`| zM;l8_^A22wwsgc^rPcv*onaL@(=|o%i@(5isq|UZ_n9urtMF>YvD@>G$}XJF(@~QB z<56ipyMX$>BU^mw>DEU0_#zCQ))nuYY(H^)ky{|AWG=tm*XqpwyouoGqN4r#o!-ad zci(c)o9*Q|mQnc5Pp~E?Ad!L2xVbWbhXtTh(BpiY@S;=Y5_c_q-KiO_qF1TT z?z@(||NghWt=?Y=9TJw^U%*)vyX9LUX#|(yrusW!2fDdD#z%eHGx>Z;x9XBW9dh#| z@*R>hmm40Pf)0hUN;#_c({<_$-u@qx!pTjIlnag*1OD~KBxC1He-twUU)#Q(!Ox`o zCiW+PpAZ5?-VWA5<1S4N%~9M_yW}AZrppj1q9LFVv|Rv%M9eXabVi8VYM{nKyunEl zYMGo@nW;=$O%ckLnJ>dhQ<(5lZPii15+xHEYQ=9Ny|_c2d9f)v7p{DovC=2Q<(MOP zCtTtrr^}TJ z=U1;Y>r8*XU8DgAjFf-2)E`e-=c5(oNF?tBZc0aQOqn6m2pa97y%#sxPf_#=KJP3V zeiKwq{18-vu*1PZPLwT{R`#`M)PBgZ>ZjXiM|=lS$UO;hv@#&^*uRf)R2N)14=y}9 z&oc*lXuCKHEGJXv;CmxrWf#jC?7ZtbKMvm&Hh=JRPn$d(!}{ zv!*=lb3Fyb*i>QOQTKy)sVv%8gneW%r|QB)*D>4}dJ0F@<}0IYAQ`o3R7GtbP&fP* zF@Y-t1jZ{{>M3fuT{H=KIHOTgL5Jtq z*~d#kka)lkLzsqRL{WR%{0t{*d6Mbzeab@Ij8q?X?0G2#NPSmSTn?)KR}D9;&Yhp< zJM}Ygv6ET{FqJCkx-0r_sD4XUlr1EQW_m`Z`_s$7TLMb~!#V*f^IlLuU|ob36_sfj zGC~gJBMh=7VBYva?r;GG1HOhci~)C109Y^11;BS-Ygn{DQ37eL(y^ND-RT9~}UQwD|} zCBsb5Ox37Kxc^vlmO|e+H&U_HQYp<&;>i8qbnxnK>-9xfgLTod%Dm%JNy~wnRNo#i&$iAWPx*L6`mR?Fu0Y?_s?~DYM;}3m* zr>I`&chreoXiF?1qUQhD8G?3wpg<5FA_#vHQ0&$?ZxTwnOD%lhOTh!$22@cG8%XO& z)@Nc@4(-c{jhGz{XYn(p(gpb8O}f5DwqLCI7_e_;FJ zU8VWzHVl)ID{e)~s=2U=)39oLb6Z-6V=Q+5@aNtcO$^Qlec|@svjQ6v46rZ!)Q=KG z$yBWKiKALS{9F-&{nd2=t(U;|WHPYC(`=txi)_bj=2kG`9LX!%Hv*X(5hOIWsUt$hCSlCYHoucTWv_Ny_$d(jj7DWzqNEf zQmG1xK=>6vGC;SwB`DsxA*{{P;Q`juy+_;u-R=!h$idenJ5HFd3wsus8!A1slpV)w zxEIM_S)hcL9XrEs3jfgcMn434RzB&jHif4-T~SV$S#sq9E#jMA&Tfg${TJcCpWS{x zcKFvcmz^}*DT#^KFVRSR27S7?9h2N$dMX~M$~v(HJEKoje;QYdNTL^e^-$sn$!*!u z8%8K~1b@-=@C{WV?k+wkKzqa0@BWV#`Wvfn=$sHKi4(ZiG$_N^Gw;yQK+(aaR952B<0#ZsPMBEmpk)e zGGX7Djslb2@tR%_FG4Nd<|a+3LrV)@Vr-8II;Ft%+*jhxb~;(P9v2 znWY)_ItRQMVeI)Io^g-qM#=@>|DcDou*;Sczv7m6<&XX5GVe~Xwb5T#uKLHTwWZH< zZ^&naH{_M=k^>GT5vkLo_M5K#Dw0qMYwY=RpIm$p@j-I?z5^rHbXC@F4C)Iqv+m8X zktDV4KMnO%x4GRZdPf3yESWD_g$ecH#UY4_gSmi@gglJOJbPE?W__0;6F>Pp&NPiscsd~ese@ZcE3 zMpa?(J&jAJ9eZ9_kClStm2$$xepfm5sAt~WR6=fUIBFcsjG=NVcrp`as2v!b$@t@O zh3I9E!7;_uT6Ux#Q@Vvgs$kmLyO9$|*63_qh z1d96gXome^sZ&6V#acgitns9oaPh^7yN`?Dgb4b;^#i za{)&q^U4luwN_lz_Xjq(z;v*OAS6rwL*)=Uj*|fz4-r7=jYtK4ZyOHN zn1k>p?MQNXERnm_1!P*H7{@3Z3hGh!y0O$y80!nP|40Qc3NR5^E}lMhaB;w`z<$uH z^!dFOgQcjTMOcUfGLPOl7Dv8eV#oy@=SGzQ4<~AZQJHb-hG4a{iW)^`L8IY+5A|uS zZZtr~CiOKJHyd%Y!SOF_r5;xYp7)c_y~HDBL+U?VigE;1zCu3&n(M6`vh25;?SQE@bdjS!glmw;V^aUk{#0A8p=rk(L=g zP!U^L2mU<*@hxAEgKOpk0uGNV*PK~?^G+$)q+Kg@DaD_6#)$8wh>umj;2JA3T_ry(+e7J{ zcQ_77rYCveuc9ZJz@XBmy;s2yF>7uC-W&e8CVf_SUl(9#>uM)DeDR8>x8^@(L;!^S zGoB4n3-8pPk$O}){&?wVpHtTiS01<;v~_5ED~$UDX>O7=DqY)0v)XRpdh_6igI!lV zr_KX(fhnBls)?B-gfRnmVM=F0$ z)|Bet^eVfiDSveTksxPquCFl?C>el_WVFOYMt4eT#9`rMt80DbNt{#z^rHnTSN_ii z2EeBPQls$tH=|dhCYLi{0zq7^M{e1z@wFHxX5#jE0ax!5g$2Q%;_~&v!e579c1Ys7 z{>8iErnrUQ5cbvr_{LMNlCF43@6`gR z>SAd$yXS>Dt7cvm?z1igYM)F-i43yh;%Rw`>7N$SM^dB%Oa+Sc%SYZiZ0 z#R)_Hzj@`eV7`Z=7Nuk@z4VMWmMQy^x=?mAEeLv^0R0>EKVGEbYy9FDDz}i93Z5nI z(q8aacy~%l&Z=k;->)0AyAs8XjEd`SSV5i;(=LM!N1KT_}MVSir z-KmJBrzU%&>YrABmD=uxf*dS0gpcegqH3u1un&j^d`AH#@DxMG#f=kblTe1J$ z`tGVxDX?m5MaJKmp8<#k5BhGkWC;l^SJPngx5vi(5mgHPoN>}*n+8BM5-rhaB?w3+ ze+duGlf z|EZ=c`KQ|l%m{2Vc=@{Ip-xs|@NvC2>VzZgb@oxfm$$FYid8u0rMtuWqEF6NR8ICi zGO5Sk8kOB?amEwzH)HOd%=3!x{h0nivnC)7M7J85wjM7ljG+K-(oZb5nIM$+1;GRu z3Ce!iyk|nufRzJV@epqskd(wF;L9HAWrS^akoYQ1@{MSJH)f4yvXd{L^&i1@>0u-3 zSD*`S$Eq?!C=!jYz(DUM4#V$&Jb|S5OvQk=SErbm()>}cbQW$5%&tHZC_rL$e-UA) zXfkzMuVF7dg*2dCCsf$IPUeP9o~q z@1Se9DkCM#5wQ>!#!}q>X1M5QYaG+URw3Y#53s?|-M%4AK*=XsJD7w40AIg0jle}L z!r*CY09zc8oOCx081tEMq980w0h|~cVJ39(`5_6qQbC>bP=OQ$&j};uvbizeHBSDoX!&QTs**e&(usuB>-|{^PAxH)K#+4so03Q^>j3t}rf1^5? zje1G%4O4M(cZ=-L8^AO^zAJ3;zIx|yiQ)i#U>+{93HM3o%!9ku?+$3eQ2f}-@jxUT z=MNJW0)8O@LmEsTT$nL8f70otnj~yd0!tU$E@OA43ISMVorEVVK&cWL!Z)cD^Z@R7 z$qzEjQU&h;uvJ-?$Rs7Fp|v_VfUwIt4>h4l06LwZC+j4Jw98q&g9!-#J1m!skJkB@ zXAdSL)tTLqcCl@0HF`m~iCMo-?B+&YGQUCSRfw2Y4~8UwVF6Hyn+>Bx79<0kr_;o)v?jS5tdp>M_|yE1UbwS_ z=|OHajU8DAmm7%oN!^Rx#L`@%ss$#dP+01I6Q}-_ekk{nhCHLwlaQBboX7KBol_<>MB9CLSQ3&D zYJimjgUt~kbnjQxu>>lKkbiY^(%{~!h-%$noYAjL}Hwc_yNp; zs=CvMoIb?~qH0uzo(BKHE!YiRW?dK!W(?9EW*28K4CLNa#mt9uYkx?C8qt`zb5;NF z&aLp#*&hW!-hQ|mfx&KqFR%(tPYg0&9F-Vaa zx0VnvIyN0q^@G8;wz#2*+gwa~!48O}W-hb%2?>LrQ$_5F&8R2R8URU%#mf&+X#h1C z;I9*){UB-*pxT2x&P&tb2+0RI(*=N7Y-9!qAEBW{`oM3U*JIZprkuXAOj+g3H~g-? zlk+bqi=d4r)o!CzXj{rDUP1W-*Gag1TjSXz&F5ptIFxk0{iQcOHy)28X98}aj)-L> z7H9Kc7U#dZ#-s~QQ;WNUAvZGU+dP!cc7%7j(j&3D-`FLBsSoQEo;WGMDIMkqn>+^T z=j^K$R_)t@5daN4odzI0R6Kx-VZ>5fCcDGf@DMG+Clf%CK@d7OI0d+_?rC|W6})OQ;SkG@K^v0fE3l34 zHlX*+XWrz72UXBL%+ST7E;9Hu@(42(PIX?2WvASOfOC3GBG&=5Mu1*M(Q6QTUo&uP zV2RM$JCCrq2S~-zN`{n^g>NVIQ^qQF=sdTu;xJPci?8bf6#Gx`g7R6e3%3l4quPEj z6>H}-8vri{ya0`#lx@p`Z${vDPfx-hNdviH1T@ZwWk3PuT7v!(+xy~33@J;}3gindob>%1$q)D3?W;j68H6*#?95DHb-!MWuYK(N*8p?e!3zDZt+$^?H`I=0XWD|6_EgEtb_a(b z`%7d;J$-Q(ZvaFp1eIX+#sBZP8xuQdT_lo+92ZEpEDgx0UU;@t9R7tyPriPlBI)qE zYcV_082r>-2*_t_TBERB@+makq!q0)6w;sofj*Z!XfmH&-vF>%x#l?p$g7?66isiY zBmkEwh5#F03|5E<-{a|m!sbAaXwHo_46y1M&<06n;CK)&7UzE5{FWf}F$HR%A^6ka zV|Pn&@U}-As^Na`kN`h)dtFl+Bn*sJ3;(T$Ag0IrzmFB0uX8Npm=$* zR!pUC9S%-dd4?mU9>?mi)d`)~-Y|kWV=)$5vfB@bPsOr88^5N02#$>EW}dTQZYxM- zG63T9s%~jR=eMsYOn5&q(1&)`AqE%Cc_K8L6(kMAk;ZG7?l8a!kn*6D@xvs#qE~TK zLmM6+;>$~SN%3%5dP8O^a9d^q*wj)3KJ0n8B1l;wj;38~Q7{82faA%{0Gr4jz${Np zfd@84W2=+_G7G{90d&nd-I!k6cXmKC3e(4qr7Z=5Xl&UBZ#iv7!(b?EF7z|z#>WY$ zO%Eq{qRJx-O=6o^obc?gtLPL-Wj3HcjdZm=BI{o4&X>2IGK@fqjB>Gc> z@D3EVj7OC@Dnf0I@_(OZ{D+B6d~%PGnUwt{!yeB(aN90Gpx?J2oQ4Z!t;2B0f?lHdJY2K(8}}gd`|0 zs}LK=2j|6hJ?xMJM3#54h-ui~?~b*Ea8ubIwfO3VwI_IL^D9`SLvp z;gy!MUrab^Fz-m5tV+J%tqzvF$*^=q8@PPt3QMyC^7`twNH!5VV3mnVs|3hwm(k8P z420A$8v_w-;k=qE4;x8J-(;Yza09x$Ih&94job@2I}U%QP!Xzl!Bi{ae93jV`ygC^ z4-;`V%6|THa#yF6^HE#+c^75&aRQ*tj=(r<->2C|(!w#+cFNhn88q-^ev@v_{3F+? zd8BE+mTMC`=3L>Pu%^i(uk{oY`kCM!5*@t`==n>+vp^X*Sksu7bW17rD-9k_l3*i{z{ovW0ZgJK308T>NPbs=icC0Kw}u_7 zhHStvGHbzrTO$fepNtoIZop8FE=Ivn}k`(uU zOqCar;^#pt#}>0Vj<*ys3hZJ-Q>|O{M^jt9Gy-hSI+NbYi0EngwG#fq{y;Bj z0K*TIf{T+ri$e0TOu*jitdMNtVcjIa*u_p(!wUeI9Ze*b%jYZkQxFZXW5gKgq6Ep@ zfMJ`d)hm?FSE{A3(P5IX&Ab%fU-_bk23hr)=XYVc6mh2)3wT!y&&Be?dkQ&M#?LRf z4A2`wKMf&&!TvZTdL7DYt9AmSO0FIvFcJS+$dqh#BGPODmaAXMuKm_ZRl8o&&1lHU zLxsfoO9WfJ4dCGYWliBDNr5l0&{n-H>qLR_jLa+`a2j|5{)N3`fA9TKj9gtC5!p(Jz;D}SQ_wp5x5zVw>+q#nur zb#;nIElZ`dcDZU18&<#KRZ4{s3EO0WqK8r@AGQDZe1BGKZkJW)5iJ|}m zhQW(0dXcl&1N%%)W8P4n6MII0Ha3y?tY=laC#s5L0MJbXuqniKM|HEgB`{!O+lagv zDhd-mUf>%++Hb4vN#q%5S>=AR&O^NnNy5%c$%YUZOfe*vN3^Bizsi6y!F^Ay{+5J* zS-!^YO&2|Wr+G_h^@hsfe&nFt(4qsL;1q-tSDdcoxR;4~ZI!^_s_-G+n6++Fr~b9c z^NKlIJ5QJXROdk#Uk}1K>=^6t@uyDnpA`wXjvnR`7g*#telyldv=(8)!0A5<;O+C~ z$g=l^a(0B|!La#C_|*5~a1`0&Z|Wsi%O>=X!wnACncV|=KfU;K1q(?JK%pJAs-vK# zH^_+JWHVSgV0A~J3fi5VuLum^RjQ=bpJ0>)Iinw{0XVZgh{iSfWVllRvSow3eQrWD zw^M+d{3NkBwseF){>D?_ZvnKh*Eq1aYLf~5OoAS@);@~ub$sb8xypd;+spmO`RWHB zRtssMD(z)IHxj@R}T<&(oY>)%IRJQ&{CO;UeJSkT^bB=gE|kPd@VHP zf_As4Qhud)3wzL~y^B^x%{{PEn-ppMsE?pTXJa1!<63cQ^4T;v8o1g>8+4)ycC7kG zEhEtpYBAFrb$n|u{>F8M^woA*b0g4 zhqGP&k37^$Z=fbEu5_{!L!I01_|xBjrXo?H1=|7D5tqgROpl74-O5qu+j<{kEgIf( zK^SxPm*a-wA~@OsFhXOhnemaJjsXamU_m1f46yBE(r*2o2`3zI0#PPGxgHLtRloGj zXs>BLCZqsiiXYCiR0gsbq=8nK;S)}L{v}rSrU4q1gurI76D|IC& z;AoO=)&rv>oxs8hga5x8-oM$-Kf{-{dHt^w3)Q{6m_M|D51Si$dMv*=GzT2L)&cm& zPlPa&-YT!tmH*iilHFs+E5gtMz)&ynR|KE|gA8FoBi%qB3nS}LzKMZq0MKJDD~QRFbfZ;8l97W5b+rltkq42D*%^WuK&A~+HPWm3O-zd|fE;20#-# z){(+(z5fH8AO%h#`2+o=Emc2LJ}a$8I1^a`~PrED;EZg_l)*b@7nHs%5mO1?`-uc@chwtVNJsy z!Y!VPDqr%y4dwyrSHwryjAY?)FfzB;Pk`MUpF7e6074f_5@u7jz1}z&2gD^XoKGUt z2boh;zoh`m0ULi4|ElFBVX9R%_&%~_0$phWb1CbPJp&a&@i8x6Cfkq!Nyo8;et*}* zO#ohS|F49=Tl_a|@ zg!Cr}@VI$;q5?LkJ_Z+Tsle^HGrZYS=sOeA+IcU^lSr34q=xWu_{DQb=zG8jq>BY{ z%x64sCc@09?dKdoyN>p&*~ZI$MFjp^SmlpTBV2Wt;MV+D&_TWv>wxbzp`ZW}_#jXPec|!H(C#QQ27C&SGP8g^{93%xGj?4seW=PJ7duGmZ?C~Z)3|@rr?sELjUbjeAYaItw zAn_Xa-LU8R!>V*do@F}-v8f84Rg+6!JXR&YESL$9BeAAiDuHC`qx>OC@c0yP<*cYijSrObp%>!dgWv+-({LhVjvSh$} zNo!5@{@rg?RT&hrkeVig2dRqg41U*PRkHW;n;*a4^euk^G0D$D1G0IYmX*-dqt1;- z;OE|CP-+p^(`a|cFT9(Q!Y_1=HYElHl1O0!!9!eg9=RKc@`12HEuluvrjKSg5pq=??MBK^k1 zsPCRkgg5}OSLB8_7-g^`=&tNi6k7MS#OArHuS4j&oN#m$)FlBpBomOv$_mqA$5J$=E<=$L*pbL?h;}p;fsSAq zOW-mY+B0#u)wJ(aE0Z9l|B;wl1r_Xv-%N+3Z3B}6&zI({_myn}hM(3Wfl}SZ83aCA zz03}!Fv5NvZuc;N?J~UlKh6!3uZA4G%&$!Ko`)6LDxP=VgoRy-;1_UW`zGJ{q@Y-< zY80oCpzs6lEjyG9f1tV-LVyHGAtQ`f8VS813D45aOM9UhwEq=Q*ko`KqIpbnv&tZ= zY%tTc_+%-MFFpVMxvR@y{0Za@NAzA`4U$v@fh53K7q@i^#?<(oX(C{H-6B z8i4c|&1ygj<#zN$?p(GL--+Cf^RP9@ES+bzk5+TBYK;jmUS(deo|N`2>#*?0s>?~= z`dJASS^}AuLKTyc*d)}u0c9PA@11n{oQHFk`INW9v3!6g67g_lD?n80^|R?4!Lu2_ z^^1?ojZ0WfA6@hJ^KL1*KUMq>Qw>8 z+px4a!`0Ai!xoA5t9gS20vOkn&VP=mflOHJ2}uHrZ!82%v;e++$#{V&lC6muyjZkFw-g;6w9G?6xi2r@p+J>z|t;Ua;(X{bM;2P=l zhl#Js6=yuMRe}MTq%=nu*?^E5r-y!Cf#S5NpstK5CACHb`mac9e3uOWnX+GQPXi2m z!deGYG2ibqf9E3dSP$D@F=V{~KBh6xkut621HLq^lEM_XM1zogK%NiF2E#@Gv#tYF zUi^R(0>cFEVt^?Lv=hQQ$S$UO92x-nnR9V-P@ho9R*h^ZMm=&C%V%5%=crIIW}3o=^U zYA|4tyr6!%z(<9HpUj`mi-DI+z$y|WFGpa&GQn}#fQpsmUq)Oe@Y~2F zNIITH4vf71uZ6_509tUW&G(|-p-J)FS`_4|`@2g`k0lM#(53Ooy54cuR@VqeV6-dV z;-7>SvvY!M@ySbYLgdn}5Tr^Dcgukmx(lIZEVM&;ZktL2JQA+sb%F_(+&&mH*rt&M zwGQ9>ITEQdKnnrYd0`G(@J%t0$d8Q$79}0GQhbNe@jF@P&4N0LH-;F}{{1h0B=8wn(<&kXk z0!F$JYY^Ba3CMsCl=j2Pgs>IFF1q8caG~6VWrT_Hr3ab3EFO1W^Uv9t*E*J9u!Qod zuZ)`ta35U$j6C7H)#i+WZUmTifj$k;*bjflK!780Ot4)%3M(ns3r8^lQeKaayFVx z7mh37EdwFR(T6&|Ju`(te*UL@17S@fe>8yDg`;XMRiqApCc~G4*7AetK=Hud2~i+& z8sY<+BK~nw0)4U?cVm~(7_4Mi49WV)r3b6AE}$=k0|oFl@u1;#b2moJ8@Qb{FV+|V zEFbFuYZ4B428_jMG!=LZCEtCuuV^tDL>#iEAo}OQj}0iG^tj+pu&#@)3^=RcE@L(c zB&Ot0VBQeZv2<$$#nC)UG@5%8?p;MIUvYGGroZ3X}DXAV|n0{8Dvr}Eb) z1Dg%$AYx+>O@^6NJ~~|e!Hx}ecqR=%R{Kw2I@$mSf{HR~$kb?_0q^sH?bU2qY@M3@ zgyi!fK2N5rQyRN_d*Mm>ebhvuP{vOX(uC%rbbnx?p+?|Hq=8V>S~LnT-InyM31TjZ{LCu#iZzQ(>Mg~l1|HO=7N&o z5Hstwz}lO&W|ex`As0ZZQGmENE=(y;vP@yZb?svcXwjes90toKR8&(+bJwHAi(&4#{%N;uBd<$b6y+SH#PhM6wl^FRY8ThbeC7#*$S9j4l#R_4m6daQL3aJ^LqOv4DTty{&fWZ9*%};QP2nUzLw4<8BMLGYhP2$%GKN1h z_AtiqO-@axf1zogh9mTR%XO+FG_j9XuW8GgGfRQuHOiQ0a6BEwRIgAJAEpk4O(pF) z8aQE&O@EmYi*&D>aDh=6BM~t1NQ67=DDHxHga)r8AdJSdOs~>`oBTjo2MrdXS}+7y zFURpq(O{9125eZ>mq8=CFuRPO;lc>&?A=2&CI}poZ{fb~jv{nb_RM2`VJ@GYpwIlL zkTo9)!(WIv4_R_1b{LKdT{kzVmK;5)nMsZh`Srkp^eP^FkZY~WKul%1!3i1ym@6WO zvSgQqrP=ZP2z)N^n>RhP>#c3ANWcyX2P4~y1BC6ExfjdEg)lyFwpXj9?aK?O$f0_l zbJx5-S2|YBSq+a0AGeTV_h&>ZYYn%TFE9Uv3zs4YxfzB=Nk9yIkEBVGy~36NFxR+D z5M$)r40n)Oj3C}Hak$99Q^K)?2-SJvwIoCDSqq4#gQ zUm=QCbGxyr1b{j(^ib4MK4_z-KQlc3Nga}*L8LA)OKfRfc07vMLY`c4v`P~uZP8&zv zOOoHLmzrM~I^{`@fBiDdzeGR?W^o&~Cr9pBantA$=3$NbnaKP4<8ZWDQ6?0schwBG ztFOqQC3pl6s*w&;fDylae{^4242ilRqu})vh*}-uq$SH6O(m_1TJDI+9=J?{m8Y>o zim~(nDGB_AfRM1bctNhUy#{uzyS&0A(Bq{V7Rnj1A^W89`e=g>3JZLjc}7-?SF7YJ zdgX-j`5o~aMq9k=^3)LfggH*A^ZkZwRELpZVb8HU3M=s`+u1bRtd|!<|A!I$oFMDb zU4g*rmjoT&yJI}cj5n>FeL?m5KhH-m8)0Jj4k{BCx2`otU{u)Rjeh?(LhcA?qTJ@E zw|zJ1br4<%>EAxMKZ?}1mwv~)@xp?1pw?F2dU@N3^n{rb^PiN8RQ$eF z-+pJ!;uUgg^>il;X15}2(5g{L@)wa~oBOYDMDg0D`Y?CRArH^nQl-Kv#(kb1=-y57 zQ(HE^WZ}OqBi}h*8DkawE>6l|pChKOfW!=%X*im#j-Cw6o2t8?|AzrxD-(i2iwMY0VvL-AwhOLp7@Y2AFlb19t~Tr&Iq1FV`SifZWuS&4svhlZiG8J z6qh47*#J?UybuN(#6|lVN)vp?ZN_zk$8AQ60jig zWFVRaM|eN~T;H(rLwo3r;+wbOH3ENqDbfVV8j=M^rLegQO|DO?*+`G#xfGTsY{kf{ z;~^hxFXQJQobaMGhw;k>(%XH1>Dko7-4{amaPOE`5#LF5)xKU)!EWJxMku;z)-UIx zS!^Ct5BZDvTY^s<#;fE@lGd zSW=HEEeRN3J>{nAQ5QHx*hS`d*XaFg4u-`3uuF4pApI-?O6xrZ+Ld`rEhJB}Td25WolzuTyHmh!LM1)hTksYjxpn>e4emr8P6bfbwBZcDzpt zQH}{?$A(cR4&{R#geYYanCL_n$;Qftp}X}m3M^tC{zJyceCx^_k4N%Ojf7%myyCs5 zBwP@nwhzT=WxMWmWv!O-5#2wVC8o5cBNns5nye&?opVOhzC~Bd?Jv5hHZG@tKqheQ z6YWnX99f^%TqXdQrvS`gs{*T6(tz1ZPV;Q%WdmMcF<8~qNhSuM1OYVNxI_El?L`#@ zQ)TckG`CXQc->?)lDK(QgBbh``X{F->x*`kU*q8lD-*7{))suY7#vKal&ez2i)($pvkk@KIo9`J}$0|oEkVlPIbba9O0S$Cb}m@JJH z!Nf{AiN#ozEd?e`ELoZ%`h0Cjh5<%Q71Ym{ttChEW*;e^2(w z42AV6osZGVs*km@M!&vd1q-&+5VMk{|7vI4?#Y(0KY$N;?@C;oQZ^Zlc0E>gAk7R3 z@3+QC9#F9%6_z)eS?TVl3GcKs;BuV~DSzJetF{q;Q7(FuA9mDOQuV_EQ@*AH$6vi! zzAjiS7z4;u4acbikLoVvkYv+Dwgf@d3{HBGA-adw&}A&bNi>t@<8|h5#IpVv zp!>qV$z{mf9AG|x!VK|+?6n1f9s1oEUMC78-t`pePvio64cmm;t@`|?$y;v12`DhE zu`u{2d=!or^8I|<6^1~)&D8;@n5A5mI@F*W5pX=DplvbwQ<#gXWEo;xYc5YGUiRV3 z%DDNoEpPbf!LWf57J1}4@hCV{i$@1Yyf0;62Y2ItyK9;NkZ%kIpzz`slI)&{XR}rM zIBL3t5&9Qj&R?(4=3Pa#!O{3JV5e!Hh6ENoaTIl};K+k27|@|2THn$tY zvqN%p7Q~3&ti6HNV_aeV{MrfDvWE_sZTuJoER$o78U{IFdG;=Q&kmJMTzi{|JVFqs zCO|8g3|!La(hHPEp!3$yI6=(Ct#R|nQU+4|*z{FR04_}Ui%10EV;(TJVsU@_721%$ z*q6_vF4y0BPTCGR+%a+2XZ}}_4~}sq6DlG^a47Ebg$EP;YvJ;w`fi#2rpQAYaEdzW zwkS|#DZ6tMUW2JKjsOhYydFHMnlh;1!!NcTi0Wa<3MMXrxDoQt41E4D6FV(f$Iy7< zBD*9NZ`_dcSOEfkUti(@^2X(R|Bf$^b$qIRtg3ilb!C;&8t&%gFsPBcSGSuY*)vDe zw(j%(?jq+)dMZ8(0eyUX=j@`!&{f)q84_OoT<+Y%W{Fs?2)Q#r7#cp4!vRQ(Y`BRu`=@yHn{AKK|ou_CZ`7D@?}s@l6DN{&LmiDoCKT75RQ- z)RrC4Wm0E*#E;9rTu>OjcHG}nr|DIS-z^M!JgsdOjGKKBrZTMW)YQj+>8`_jJ)810 z0m1hl88d6#6BJ&oeZTNn!hDvoVU4P^hLRwojI7tTccpurWK}3L+t@=E^*&p^Z~B_B z_Nxm)m?2lAizo3MKQ5py7RvI-5W!M!jRH*emwm~%>LqzkN@9Bfro_)IBBNqJ~j-}t%3bI@Z!@&lV= zvaiHPc}xFSZNIJEZFbw}e}APElB1M!&6Rr;_bAsJPw5l%yC@*}n%1CRMjn5Gpl-P? zqhkht<3LS<0=GIG!+f6OEZ}V$SJRi8F_4@svKs(yCPSy4+ zexu%7UmuH;Yr_Nenapk zJQ9|eo)wETcPU?tX1jRqu5`mK^`H9o3XS0FZCneFLz`RX)J0_@xu(9rzk8=Hd|A9} z@~}Opc*GE=^~U!CM?ul-u75idr`x-u?A@rO7*U{5em< z+V%b)T$&E+iUq3Q*pw~anQyzt7e}3gV@t0n_nGb(muk^j(N4f zL)_BGF@-K~oTUQK5#Nr4(ZQ^6usbJbcEOE8DYLZN$;rOmuf|2(Qgz9~FH7|o%BQG^ zWe2ntU**0(3sS4Ncc>>}GVpYA;{6BOhKWq6h%~6^S!uol^R0S14gQ-;rDl~ zty@zhct|yfi{HHB>hh1fCaIoFri0`vyGf$^7}S*)@9x*!;I)go=1I0o=2Q8;PBHFp z`-ijOoTV33%PN5h`YM1s;ndn&I}+_4wDZZ+(#8AM zCyg`@iuwYR=H(a-Z=4@P5H(F@MnwY|`GmGwAdV^#_W9-f4VKh5nmLckhCGrCeI{*hRRgD9JjscT zV(w-YmG9LtjpL)e^-`#&%_yXq!p7_C@%IF?fzddJ;ST0by`Z*twRs!Oo6k#G8Sc}i z8SwZWwO!UNn`R)XT^Z@cX}dERImnk$H0b>$v;Er#PXxZ-R)_fD5*7gq`3pOLY!N51 zUX#KHNwOuT-O?jLTDfuo;&8$w+Z)uMN}IxLb}_Pnd^xX-wP-PMb>~dQ^o0jzR3MG zt|dV$5xY8{7T@proK@5rWMo4ZFQ+lm<H0O`Lz1;KrQN!6uz-G{uKvht~_Pq{cGVtCF zm39OS)6va+!CCq6FjAcmuJAqT_B$W_mHjgtx~FPc1k|1R?5{}+Jq~KQdhg-c`&~^d zp1+|r0UqL?6Ze0Lv0FD)4OxHDR_kwS$+vuE(>uiOUcB36?BXrD6m;~>Cby&6Bquk^ z+qDyj`mp|d0J{YbryaX|)z9x`)r@l5udJEPG2V-Jjv{0=+AE(}X_u6;&2mQN`Q~x= z@@P%|(lEAn6QVnd)|kb0v|l3_ja}gQc-KmM^%`?qoD}`UZ~vU9i=yaTp{*TVbLT8J zFMn55(Mu~CY-+^NJ9FLiwqwD*0@>BU&-5QYb947d zy1u=2_uh_mY09QRr1sd@{T8VzCD`v%D<8p_>AZ-oOj!?#>YA_C+qE$tP&a3N8)Oo- z#XLzO|Lx6CU-c;yHwlW){xUO84*LA*dp)sP>Q?SO=Hk5d zfhZEx&tIdWBSX#~ASF3{692`0@heVoyg&JO=`QF>FC2@pYq=o_=pEk;v}{b%3aGe3Uz=NO-1Wh+&Qor$MAANS(@Xf)4B z8dl~pmpI*A*4dXN^y@e^{7*95JG+Za;&;VR3jZ{GY-+2iHIB{*#i~4thjjov~aSb;h9oE3oW){{{`3DRt@q(+7sDsYp} zisCPa^=-S_yon5m{rhL4F?dHiF7S^w!$QGdvxQf@GzG3%Fw0dA%|y)v!vdW*@2wF4 zFQaKfD=e-v+MdE*Uauxk=R&ENAhCeW(bDH;vOUMB?gN1vV1IPnu#9AX-b)8QF3Pe7 z-{>d~L>R-=b%(Pl+s2OSTYTJw`%^w$Ri~1D+y6Wj8@-$JQPGJ0Yu^yq@3nt9iBw6a6T^BBfr^KU$ z%YE{v+iCBWs*jiV!2FE%FF};1HD-1~&19|C0-8@FBNPp5n0Fop)bm9&n}{qhqH;e! zB}VmdnTuRhOno>|Adq%R+}_Me7jI6LSZt&!upn`EuxDb8RT^`QIFIt9c)1hKbvpd| z=VhY%S6e1!nVm9T9-6%luMKC#tK-25MyE;-IlSM}xrQ!$hK1bm+|7(1M$huN?G6t6 zCQYdZ|8wVVWN?i{u3iJ~kID90>Zg%tmiovNp3$-|)y7}TR)Rm|`OZ1*on-`GEZ?R1 zob6;b_H(vaBVN>sL&U|6W6g(axpHgg{lj(rp=&gF?_Z2z%KT2A;_bI?H#dG!v+I(J zta0Ma^z-mo!BR))B9zqV>31Zua!tHgmQtK{&s{s>J}Aq~Xf)qe^Pby&`=%x6j`$Ie z)i{0&L1ulFxLiU^5X-oz?m6A3qNp+0YB?A^h@$7`7|!ZrNpP0~QsIBN>@} zWRtufo;kYt*Tvy@Q7gt5IV!`049dv=$c9P-daoxSw*fvOo^!j)#^iJ&u|W)7`dLZE zvzc#344=oFuwV~g(&?zbs%@*kyFe}W*Cjx0Yrk`R@E7OKSIgdK6B&si`m;(j3k1p= zyRh1k*2ECamwXy+s1e=cb?^;Ec_Yu(6vRWr<@{DB$NAL4Vsg2EbiFxXj}D-QvB96< z4moP~!#00)EWGL;-xc8VLTqk|Ct@> z$KbUILE&6nuWncU(fgU~9+BV4>F;@~A;S=~v;Nj`x~(5@-Ws>qs~s1zz-((j;OYsYqI;Zg%L~C6AM~8WWUj;|lSIOZDK~8Oxu$pB(ZlhT`5f%w%L_`WP3;BzTYo zw@U^Mq|vNu*sW;QOR$IZ{^07n#@WzH;yKHn>;HXiXH4B?zShBZP~hZQDSQKe)dYy%3gm8JbT~x51r#8 zK+SQh)uX3h;1+WaatOlx&EP z$(rCj;-~@e=JpH5C*})Xd80AYE$nSJoBry_Lc(?QC8Xe@Wvu?zn|9+{-`7R*p51zq zP2}%k0xrq27n$RUFk?&p9^}Zee9=FlgN12E4G+bJvjE%Aj7{vb6mh{`ubfXOd3TI& ziZ#Y8JdG8IiPnEkec$Tlwaw;J>$-lKvdVux195R|1CI94n+1>V^-Q-XH<}qd^?9Xb z9Tn0pq$Qr#GD=0j#oDRQiN5sjL{E;2OKZNDtMVp zqQ0}P2wUo@v{v}$sFAaw${ZTsCNW>4NK~tJPt&djDvsG*^V_ge5|y~ZNCtO!t5!|7 z0hu=}XH%fu^$A_&=}uHS|4LND6|b0@*RY!i1lR5qMM)r4C3+(mqN>yK$d`Nh@$&A7 zNZ*vU`jrojU@Of=o*E778=r{83j}5(%a>G-;N$_wX&6O3k>=STOO0F~xolBq_V7Z5 zJ3p&1o7+|h&%=l_2UE)whT6dPV8{GUSMt}_=i*+@`4{;wV0iJVF^A7pv?)$C?~2U> zt}+jou86%zl-#^QR*b+RiP}?-oWjbskr6h=KaLuzc7IPa>pd4!*v|}@NTm(dLRrUN zyEE0FxT1Xj`wzNEb`QHho5czoW;k@FC=Dg&P}Er;)GCeTUk$v|20Hy760^~|$~(SK z-e*^|eRbSNl8eDY!PMm+Clxv07_X=uY}t|A?!!SDmy|cQ4I%=c`G=;}w)JiYzQHH+ zBYN_Z9DfSI-f_2lykGLQH8E7=xA%!SF==#VPEAePt1x!$9p1-eOfAZ$K{KkrRZ^wM z>b^m09w_NN@$Qnquz2aD(3L$QRe9H?0k0*ly$gynY~-)#hwX%OM;<^S=HO`N!^Ctm^x?8S*@j zD+j)JgAb)XitCTQN`)PKiq&?NXQ#z7dFSsjF{kxqA~0T?{77nBy-IR1jZ<9t(^U$_Q zQAVp`i-qzGexaX)jZ{qmDk5Luw$YtY{GvGdvJ*Ql?Bk}F@e zzMqH^vc9n*>GOa0Kl%9idWM0Z^m9&0S{v6#2}T?*bFNx{FI^lzp|~{ogJ*1eO8BV_ zZqY@nrl1Gq8>zG-9QXUxmsFff4a&7t^qlhbXm`*Oy+K1-21Rs|9Jx-|P7&hn5_OeN z`filG6m=no*>=&s+obvJe3qZaJ#z0ROEKw&mmqW2yC3LoqLqORN`v*v{8(m}LCZl= z+#;2@PK_c_(b19S0;-s>&Z|-!Si&ljvYr>Mb0wryDJ^5^GxJgKY~ODlar;f!{~_rs zyG3U=P6@=THDHriIu?NqIS>uK+ODo;Zr^E6^QK)H%dtuKBE zjrMTWg!?~O5Via&F{ZP>R`%~V4{nw1*Z52;ExOh1VSEW5@J_e;!lOGzS`|i*EuF7X z2V?>aOm57p+SHxT)9hRHuz&FPOK@8yRu<@-(1ut?_IsqNd_*q9e{4TyEfn>hD)QtX z!jZB0=*rpZwy~NkZH0GF0pg z*BX+7FEHs8DS(%%lYSb2VPNPF>fR6C z4;zp3ndaZAaWT$(?CVm#Kb)F2D)XP~OXPnz>)DtTM7ZEY{?YA&mvX=U$-07#o)w<4 zLu=A~Bd?sf+brC7SiHOtK&ED{R=)-8qwa$eX0D9{_EXB}Qu~ z+G#NTwVJ$)ARvh}m+wkR(1&%GW2{56odad5=lL_25*= zi!8_OJP@#qX|dZ{6?*z|^u3=K<9zoGMmPVu7H}&1-^pMzJ=!m%<%uJGB^?AK{fm9U zr0FEW$7?xPbUq3QUXdFowLMS4Gf&Yg2mb!Ma9mj}&+GbfTzYQUzhigC+P(0(kx0Me z%{d=zJzeS_ef$04$+Lig7lL1IY9>X>^fYFW2O6}hMVb>N+LsTDmRE27(|Vd+l|65Z zN&FN3{U-BGJh0+>=;4Iz-Ww*3*2ii}>}t2hZYcyeXg5Y)x^Fc<_%%TQbtm)W?u#2$ zp>)Qn^{553mH5$4EvoG~v}<#$B8U!n9Mm{kEO`(p^IWWcw_6=4RuUQoPLToXo5k z#jH!01HQ-f%{;wlD|kjg^42Fx23s=b@w5pzF@E!JSeH|5W2y?5GH?cK_bFMp{`b1wW<;qe@w!_~HK2mLVHPAk{d*GzbP&qk!|B@S zz~zSD;qtHAyb*wyV;=)nF5b}G-kSSf3(kvcd=*$+0G%CuJC*sdKjg1);no9NwNi^O zAWn18T?5mBj-wU(n?Y{TM;+->Vhi_xHv^Ag<)EtrVIO66UEa-{wfNvO6L!$=mEz;O z`HH?*PLGELK4yDJPf@Zv;15EPrt_Mgc81xGf)wfN zOD&z+J6hB)^7~)Lr#20hgon^Dh2yq<+*zZ-g){r*IBX;A`oqg!x;M!^AFLLib|Gv{ zekR{>smrCY9M1(rN9|)3I^Fm)!+M>irc!p-mk)u)xe z{V<=YcX}=ut>hki9dSH#;?BcR9s8JbmuFAcR9XEkQHxc0qB@?QU?UL%FkgI|wb?v& zBT_2zKu}ctoRb|gEciW$Y3aupz@lF|K)mvrOQ0|y5TM_N_rpDFha!jU^zXm znuazEye(C&d!?=5P{`w@b90AQdbd9(2RZD!7&`@}1(%1*=A};j*xgp4LJd|LxrdfJf4Y?rAfv%=4HrUuz%z z`C2h|C}?(yM|fcHP%WJ2SDmcq!0{_&&TR(F-g^JHmMnBuv!Ns|edZS{=ztA0!VL;q zJks+eZ6lKRLLdn7;ce#SZwiN~^EZ2K-HherIrlHeN6`N~h6O)OkBqI=-~94LDeT6- zJNZh6FPp14B0Vyy{*M-Oc{S%dKv>xK)1SsgGjp8ujsU_t=0j{8NiqlYFaV1^;?kkI zvw2sX1Fk)?=zQ|^a=d(lMPJGD zQ>e7gd1eZ*<8`C^8DMv8_gu~Xk4*C1??V^QDkeAs-=68I$gyn}Uoh$oRMAvHB>+YF zzJ768irx;xFuac6jF7d#oof}3zx2V(Eaktcm>4_gA5tsirV2nZl3y6K6>bl{xO1i* zYc3g%KS`q$WX`vQH|}4zD_Q8@?3@TAW;&iVm9Q2t#j`=scC;T*H=R9}sgXJQZr?(! z(r?v=+IN10qO68uLvv$t8L>@&v(n{%4nmf|X=i%F-+c3URv`wMG?>WJD}bhf67;-y z*B1LpjQh_M25?m$EU1QFocTB2x$yXsJ@I6cSq{Ywz>EQ}2b2H=QG=Kyl`r_S7Jpl= z$_|vQVayAY1ZT#@mrrEM$^W?-DEIlekzG1izi5(+_M86o;rM@=(n`&ccUP`H?>J}Y zaWd;(dGFHKpQCwOf7D5<=A5I9$uR@rs}A*cT+hyN`$Y49#ZcRaKdwTQPDIEX8zpS# ze6mVzI&u}?ku-h@=BZP?$CD1akx|+EUSJb-FHd*+=|}kd!J+sZF@@LyK{I|&=xy~- zL&Y}A4rCmKEEAEi)?sP}g;z>B7=5XoI(@eg5?#UB&gP~z+}J86+#ESaX}SvY53e_b zDJ8#-138(3#PpEmFx{J!j3mF(59ZJ8heq~qPtR)hjN&;%Mds|!-Z>I2Gkt$%G;p6` z5XbsKAtOZa`u=yxWsDZgTu(v`LEfiU56`}wS+r>lIyqR9e+UI zmeh*C@HVsNdn(+cDG$xY_|y6!ghVJsqD{zQ$!&rp5me&e+7(cmi}9-sYs7qgWBOoF zM7deLrSe3m-JMHv*RL-|r?xyY&K$c@e5Je8Efg5=WBS9=bly&g)YtoQ-P;gm+05fF zKg|vpAHvJMYbLGVjf9N@jAorSb`g@~+nW>2Uh?OK)Z{%__qH`WJNt7qJJ}#-t|iJhaKpeE>d^HwR9AP^_nh({z@mZ^|vT%SyNrK>~37cl# zge~E<(Q>fzRd&4W;f19{NpjtM+VzK(H?dLcQ`HbB7sc)XM}URYh;yB zQZJ!moUS14?~+PJwe%$3OkqO_VBx}q#{y$-rwI{ z6`0!&6BRKP{FBMd6U3loy;0pUrf!Nl^+22+*%IM+iNpemgd-uZ_6gz=AL=aFH~smCGA|X!m4-?^44U$O9oAmriye%y#689wV#wd-cg&VnZ0QfKsp1y&$+>y0 z2JWqiRQhh3@@Zp^C~z@AV%0O`C#-hF9<{8RxsFKZ3a`srawKdvFNXLkhX_9`P{Y?- z05#G@wt?;i@3@v5h|HL-TweURxm6dqaS9LzgTisuN=!;k$8KA;)-T97guV=)HYdMK z%Dn~%k;M!DwY9to%vb8A1mlu}$x+S)7DeNU{x85kYJ|l%*!N(EuK?L%yRE{CvGig+ zz@R&*eqJ9u19tm%2yAX@rD#(g9*3E2#9R}RhTjx~W|lx^62O%S=@p5>@;ZhgHpbrY z%`b&puzx_~Ce6k2cvu(6S3QJIiA%y;A_XNOY9W|Isw&2bVi+X~QZPpGlBJ3S1Y^8Z zZ_4g?b#D>?^deW9GNTjURZePp34tB+ay=*cBWY&S*{gf!eXNwk%o3#;uLP27@F=ZM<+^K22+#DJqM zk`LNBP|H}M(nVDyO)`Q9?28euh!Id74M%kAj!W%%?u^9FI)WgO1 zvF{55uCe*2*|XqNQE|rsW90&F4XwSWSR_#p84NgKh&r#Qb5{}>{=7&?fR$(!@#H6B zjQ9p3o=lh2IUMqlqW={g2v?Clf#p{{#+s@!(cC@)OlP21fR($QOS$b^X_y_b%5H|gSn>@zJ){&3=!d0mRhGgeI`tje z>uo7qa5>WZgQi8_(^ty=H&*LUlf6!S7|lgi>s)d+8Sof)>&+_j>i8p8W0twF^s-}Gtk}$HVX3KOTr9;*WMQe*Qkl>ZlYGF|y&BbG zd`c6}Kq7!42QrS0*biCNv@Pcv3Uum};-90lEEq)&X)q-SY&Ze*DuAQ`5VvpjT9Ovc|JDhka_Jp@sWt`Ie>a zE#H06@IdTCf3}=*qDwH>IFrD~5hy{R%Zvzs2y5cJQniCA|F5k))3?L#QYP=AkoUWoa7s)6mxTNYzNffNb3CqE~e?jqMZl($wF6GAMPdF zM#S>6^o@D2+lp|fM^XddJ%VptOX1t+(JEDyy>wDr%gcT7i8Sy87235cC^xpJ>VWq9 zv{*|)m-kL)%C zLZnRcvv0>+4aY6~MDArj!Ik}iVgG(D;t^t=M;*Og=5@s6mDA@n;VW-;5z?>)5VStG&pwg2I zNDSu0{sM75!)vH;^4i1DbifuNWqcNGu^^7*re*F(BUO91atqebz&TguEX;KMbmuKP z6^tc-{0@CrCHM#Oj=!O5{fzIm0J0;E)@Ag;w_}TP-e@P9NGUi`rYHZ{W z8;T?nY*u~7b`KmH`T+ZtkOY&)#|*fysN}O%;}&a2RaQ;6?MPQiycUMne+D=FB9G&z z%~ZIBF}L!T4you0;ItDZH0(tFFK1Bm=|yb^BgZjH!1FK0I+P$KBCG95uPl?2JpJ0k zhpndUrOL&TVsYVd!v6)lHj~3MO>~2R2*#sA2+W|(&I)9WuuRDRE_5#m4+2gI2zG#_ zy0F6H7)8@eu~7Wayb2(!VWY|t0!0Y}M8pG8+JFwcsIX}!>@dm&P=UHvLAKa)0aqP2 zl|-R>eV^*&0`W}uh!0Ufm?8>l+U^u*XFwd%Jf5FrXtN(Y&*Gb)|eq4+(=ZB0LWVPAWggiSEMRRv{1=5@B|ri>7Xr!JMZ z#G$67#zp~uv?aFF;+0$B-({By+c|=nTQCvZ-X<81k-Y=55ms^$QTr#l|0M-3)te3R zw7ZWG+4}BQQ+qDf&$G_x@7%trO*r}-^%jLD+~ca!Q^#2MbRxv^KH|Ry?wlH?<{86D zpyFE6aMn7 z33->0ckG|^9k_&+k`vPth)9IEEIoUyq$a*=Kk%*Fq(HY*VPAcHZ@h|syil^Zx9Yl! z|0xy`OBN9~MIy8@1t5qN98mDD1gerMN`}KEogo;U%pfk=-yeujYQhV#N}4esk$@_^ zBuZlTtC|oN<5tB|$tObU>7o#}4R(nof=6|2h(hSCnAq83kbz~qC^ zqR3o|r6l8if#`aDQo$*8)=gIE# zSf0d?CXXD7$F8kVvCoG7HnVLdk3PnPQVq2PBC%kX~0C@HQzwoBqNbiTi)x9{v(f9EJrT=hA`rZ77 zp)wpt;XwV!007AquApXC)~(5Q?ErBc>HXVZXJHcCQaCOkeJL3AcL4u;5jWk6TS%zS z!Sr@^wg87>1;%3Gnae5dZM)DB%88o9oZna_dE~cmQ$a~`)*hLfdd5;?*W$)KwU%>8 zluu#yo5!)+9|LzS87;;0Wj#d!u^CS_@)uP5I432&41Jvq`zd7^KZ7%8_@r&MgH*<3 zN*@1i6IIv6dz3IC-mzo0tyC()H_I-Q|FKCjL#b|^80RZ!>aD0MZ9K^xiz$2PK;mG& zStOj&miw>%rM5!O3&hk^!(#S|cB2Ugo{ePCcEZLj(^^R+KFG50Bf0Gf@vQrY%RrUORwZnA zamB365vd=R@ql2XIz_lmiAd5Od@~IK(=T2EXp?`@77+2C?Q84B2*@wZCmo!+j`sBk;vNi=%w;P(M05TeoHDzoVITG$X-jh1DYvHLw%b5QGr_rXRIy(Ef1rLM!w(|mA)#e^yISvd0 zNv5hZGs;iJq1CHTG6;j$9E8lrVm(Bf$* zx%?nAT!vnIofRL1n?H=I0b?yOH@seM z8_uoP6cfMao<2cO&ICa-fuaF9f5w%Vwf2}^_eP_Urx2dL)#bj#F`rOYFTWw5PgJD| zWbsl~c_LYN?)viuhVr~PV4&d9SwhLCMxL)Vn}aC1guNMN=_U$&ukkJipMFQR1jrHX z-c2o+mUnaxLAUi*1rXS~f_9txlR1X-!L>Q~adwC`v$G}6sherNeM0nP^{LX8KmejO z%P6GPtH{?;tBSD`AjyxKr*-b#cv@ub#tv5T)kgV@%^W0l^#^uMFPrl;FLpnP;7Aoe zGOaT%UH;l2N)2D=; z3-)+y`0r@LBeC~rL0(CWO#bdS{p6X}zFpF=pqe&um`YkIIJ{d{Nszg$HnFB)a0PoY zwr`!tQ3{Yl06wIk3Wg>Q;D^3B4tLJ|e`s@@-Ejtqms77tWc(c$J?$ld5pzr|pGo^; z;}$MP!6&^Eb4GH-LS4J6U! z8LA`A0$VALEfMVzAW>^k5kxo=i@^_t9gw7BybFc!fT~*+^bZZJWDj;k1RY$fNe&S+ zj^PP)*~F-1-ct;&m0QvcS?SSml47#A!al8`qrN0@f z$5Ql5sQBV&_lgjS1P&Ia)i4N>#$P(Bn!s}Womze)67@?Hd&+w zqFr3fLQ?1LAKCmlImuOtsJX}vXH<;btT#+vc<8{m9b`G2llME16o_{+&H?`(@V;CYB6Rk-LIlM9HKn7{Z~OQ%!1XrqPl+WrfS@wc^Veg<(&0_c+U|3hbRd$`IDriYXRo~Mm<$S0Ku zgL6y#bZeP(m3X+PtLO#Q`1EL2g4`!#ob#6~NQY6@s7f5%vv)vWGZx&~moF(GzMkaeI z0c|)J-J_GidTx9Z?cA-GfxFfoWhM{AojJaev?D=iEuS*SON8PowAym8VCvcrjosng z0gbmW_Wj#bS5#g8F}ZpC9{)&ktC_jwUcUn*4q(5(?cT->FRT5qKSyyg?YWU|^QLcB z^&GZ?z<3Yce>2gbcOXsIQx42nEAFH^Xc6+0E4j!;M1fA=O4dPqmmN^n@LJXGkq@t{ z);uHkb&n*GPPI9pDbbfKWAZ`8qhD3pa0W>-+y{#$@db;yGy zRx0~NV`QAzc__s@ObDNzyWB(scwmTunD`313?KC=qmrH=Z~{kGKCRFw-Tp!o>420Q z#3RFzeS5OcW40g>nb-Nir23GL-QU#ZJmKfv?*QkVy^Pp0ylCre=;~4spca(Bc|m1yJo- zb_Zc=3QE66QD0pB){N4J_gOhNh2gbEYjZF;;x~0FlC3?V&T~eXmI0B4jma6%-{91l zAHoy9i~-leA0?}f>m3gKWBsFy&9p#kT%SH;?iNFc(D(PG+C&W_rud8M=6|ZxjHh+# z>%PMlg)QD?gl8u5jjifjo$F<4wRiLJ)Y~B~IuDE0)cP9Y&cg(v#k>GVOpSC8x(V(5XSAFJd2Qz1LRZ2II|AjuV}`z9A)faoDlfSnItS z&R8;FyqB8N0$j%n%4VPUro1(|B(DBCsOpEPn#KX|+q%xUnx2bfm3rI>nHg4^C#9g) z0(kW+6M$0@*`M_1ND`<6V1w%JNHClYsRx-0Kdo{Fb`5Ls@H>`gmhKu(3bIq0G$AVwD-7y+8o z(vzLASp{;or@yUtk7*{(2dOOY_OMGew*{0(XiaW%YTe3(lG`lB#oX^@i zVoLywKeF005n1zfAc)3u%u=~1J3{hCfW#Uhf~sgk(hwPT&7}epH!eIaB2n!nTWzR` z4qS0W->5@!u7B6;ekR(FXA?nq6q%mehl|9OLY-T!zvm#oiQ8`2uTYDBYN*e+W^)}R zXs(CRCuT=`7niX+s@uF`W16}kKlj}kyA`h1{Q>My8Foj1FScJa7@G}&Xe?rw!Le^F zlRUI?A@!50M+_|}YHXi`J2e{F=fFAW!*}qfSE-RE5TsAwN^1vom#Vzfh__H+gy#Sq zi2cTsZzbd}-K%mV6&)GRbG6@+ykDJvje^Zl{8zXfi#EeGg4Ai9HZ+;|@>SXau*OZ< zlm(fUtbC*Ck!r+1xZqcH%u0_T+~leFYoX`t#B)}1=G47qjdUC)DiHR?j{d7m{NIDP z?j=8It7^n$cS~Zaw~7dBFS^#XA#U_7>%T)pL<5*AX&U-O*JvTln8c&o>Sojg4jrh6+mh=-G7b+yLoAQnhu!;8x}w@gF5UX zFHExMo?e4ZE|wi9SAT7EpW+k-zz4^S-?=3FWi22%5^Q{Swu7&=o^S ziNsB(S@DxNa3~w=FDA=8U{hSn=tg2Dx7+>x3JX%IGV#JY!jJ_pZ7oA(1ZEps|l{-nX1}}M&Tigc?1NncVNegEg3r9$k0gfL=8SDYZ zHi=Qkm`;w-qB|flO=t6Z!_fZ;@IY>*w`s zGMjjjoXBx8r*U5oLPX4(Wl@Krzd^H}%~b!A<%%Mj63Efy75n$j~c6C{w|z zd&GFbZ#T1nbMPS0K4WRTH}6ih@3EjDiq44)L5#wZjk?ZPR9H60%lO_< zPXAV<%CdYyoABo%>0R<8Yr#ma{s%O8D=e4RLFVF8OC9HfC2q&0=0UeYGHKw&9-PGu zyLN$vVDj3iTb<^&nz%J!f1zfpC4wr~!2#dgl1nEm{&rP3$nj{l)0zl9$^_wcUfXh_Wh9 zd=;;_YA>PHOIsPy;Z-|63|_<29JSRrEZSCXKHnE~e5;|wc40B^!+)fWo_jN9R8&@p zVIDR{ne;KdB6`P7KWBV#Lw(UXa;Q_fBv(mGrwlkAT^ViaLsLX3>{mwxO@=6Z6F$pR zZ#&+1&7Tp&aBps=#ARH^)-t7b^lx989L@7_vYP|jsW3uou!^?=la84Dwn`0e3P@4L zo9?^0SF3-pYryLU_n_xacS@pE1Pmb z2!#YA9qCD{bxfZuKhgS|dnKig*;Ra|WW3dsBW1h;P3J)Z5I6b6rQ<#?quIO^z}XFx zRQ^$Ap?p5r^d^Iw6u;>zG3QFZ+zPSX1!rzt4y^Jmuh^Bsb3{uH%!PT&IJVQ2Jgyo= z`1?yq?&ZZOms92G5S}E19 ziY0%}8g!rG0oELgBEnVyRIQCFI_q40&6(TCaJCNMYo#}{Ee|MS4Q8cr$`4~yCyU&F zLi-DWX0Q=Fi={ZYglDCL<;llP>6Qg1M}^>fR=u`Q%6(42&2GRy`#58a9(tatENdw~ zb#t6%9bGd~Hc-O_99;a$x=(8t6u!lMu~wCzJp8U*;qZ-=Qs=Ms3uMX#EUW(j1o2#IykXYEi3TWNeVxws@1h$pY(iy z@iRyD$Bf##!rPcb!`zdNqeUy%NW2Wrt{k;@ka~Nqrs|nmL#yN6#iF&G$yEwtev`3% zY=z2*;^km(j;W1}GB{@%ld-`L9xZu^?tQz)eKfhD9@!tXjLP4rs=&yddMsD@x;}PP zwJ%!xrG38Wymw{HAx(>jPe)Y5UfJ9bF6BGvSn2$uz`HlraIRJr_Br=|v9L+Swq&3U zF~lhh`0ta{N3nnUzr?##<$r<^LpI4X@*?BCXMrv=9jfN|SJ75IN2Lzy9DxX;cLdH2 z`bZ@n`rjyZ-qY_*)a}9P44cP-O12-v4aXcy{jPYqMTt8kTE01qOSuab9&8})zad#` z-2-;43IIPJCaN6F#T*{QUghck0Uw?NXjMKbgqafMLHSGfad?2}2yXnwIid;*gApkf zB_)e9?7y~PPWzEE?po-A3c@D`)NS8dP&v+#x`UrW8+KD?-XQ>7U zsV^qCL;BYG7^_C4T`x&)6PBkY*sAl*!cDA?m;b3wwM{%fl>hW_8SZ7UeM__mO|8!Y zHCNoXbEz@M@I=n@R9~ZZef76B4q_=-TDQZ_IYYwHKLLH;q7uuyV{V0-_aPa^a-@H9 z3{^G36O2vuK3bn}(L+?b2e%}8Vme>bR~nYkw$a2l)*%qTfR}sj4-(^H*DE%3$rC|H%_2!Qe{&kl z)urdbFW4tpg_Wn)3&=j3X+tW?7X({)55)H{(^$(6WH?ydI5+$Ku_-!W1+d!dz|MNE z91zpTg2>d??_RhAL1G|9hgpR&uQV}g(1wr?cV+;hRjfR)yc0v_@_$Z}_(2cyfXbx2 zAX!2M~+wa|lFsi~(Bh+VCyZ4;G#w6Y&I#ETD9sl@RhfnHr!D)R(y8-C*0 z9bCVy1C14EW`3&7Fw=3xmc7!+mzm(J&cHObAN5fkRkz6!GiBN99~oQ4l^YZCFU1KY z>M6&)7_QY#D&2`x;~rh1He{lYY}f|+@;@z3a})2a#6mmxMjO|MWA$IEyOMY})?#~b zw?bMbO2qRxW-qxU#xv~Sx~hK7mo0gdH6s?If@O+tdQ`_J43&!pl%`(f4vV8d`)0ui z_u|&0W(FiSh?6{xIr~Ol);C1_HK&m?@a58>Xmcsr{`JGQZP)cPiuVLsHu4bH@&)Vp zd@szsQ7b7wsJOULd1aeWl#W|wwqBKtAXHk`33qsjsAJ(h1;D8bT1>^&wS7MzrG_Nm zmNu`Jc9k}yE(7cvKp=0K+*?o`JS-gdw?5JZ_f=B;cGM660v31(K$(Jrw)Q88hl8mLQ_if~mIA0Oe zpE$34>vZgPdex4D;q_9+_DM!HcGNz9BPDaCc2x{E`IYbRZrq2skXeI+?`@l}PP>v5lG>08ndbM_P{`TR6u1zP-ztSFN0 zA9p^=>A1F>07wwv$Bs)G%5Qq42(VP5s0D&t*%jwvtmE1Pks71v14)Ui272-iooB&^ zZ$g^#efwU0(;4gsHgpn2jK2;~0i3nM{jL)sM;;%!%u+cRngN!4dN>!Gi>RkV)ul(JpLrk*y)R;V*6ZGmsf4J-OINM9b(nfyd+6~GWvM^ zA!ylO_NdE$415)S&@@)_om?ILEv)YTKD&xhpSWP}c4@wvsHNx6aV-<4i>T{F?L@1)`x-x!TPIVx4}gMBYc8V?v{PRv-hCSO}(J= zdE~tx)+HE46S6c+1}W|!lJ=n45r$-dst4=SN(0;el0nqB08f^>?|BunLonX$wg}A)=mdwX0#w*>vD|mgL<~nm$YMEze?c zoWpO&lqI|=dy`1G{0?nctxtPOIB;OjLiIk?Ba)CmDa(j0_>{xg>7BJ9z|zpf#=0}( zu8f^0Ra~OvE`>xWo#YKP5aj;j=Pe4XNB9#!OI#gy{{9Mr?H^kHM*hTRYxnjA!(z38 zVyFH^!}g_$ZOQKWD>(~q?HFZ9HByV(m}=kJf21wPX(jCg8|>d{wdUAiUTWWei6fQQ z|IExRX4UeqP)()C{LK)eR$jz14)11LIDs$GNQNt8O?qncT}{ zQH2{@mEo;d8fk%RN?kt?T(m(fJ7CApVW(%38Z^vf!!7#l2-Nvjm6^J{`Dndm@jh;7 z6*pvj=W01x{u@<)yZMgV`WLmn7f1Z>Rb3$+Dsz{l>Zg@Cu5?s-n0;{y7)>dO2}xgz zRV}+1bgy|g(vxnBu*2o=AFL|2q;IPhKvO-4pxJMRMnC@Znvf&1 zkZ$!N-lhgqK0Y{2A=_jG*|KiSBxaH}_R%rRQ?P<3?#P4fjZU~f`tAZ>x(JDfbpLSh zd~kZ>r(7Dba~GIcKdr(?e(+_b5?D(NLESrpJpDa3Mirs0-t8re04~adu4}AJ63zw^ zQb$_lU%ML2@lFpDvTa&51Zg3qz7EN*F2k`+lf3ZBbsYBV=E-cdHYxTBX@ff*{*?cn z(AT0;No@?F=K16Z?7h-@@!T`URv~tM5IeO8vz5HA zYcvtW(-Q>ldO7f{l^6AMcX15v!9HGJV{3NjsPi8U#!ke*5_r(&CwP>rIHk(GhDuV6RoT_7_R|5`z78%R1KF&ST#ufV= zj2b~d*w^>t~1-7o!h@F*2gceILK~i=aGJ-#td@9)CC6l1j$^Sg_8H~EM3|) z{%OPk(}R1&bY0M4rez#T{P5w-(f6=5ce-V$Olr|}*2l{;woQ>@w{wI{rDen2SLpO< z0Q9ziVaQedJD>kY-h00_k%eu*GwDEp009D`2@oI@B_Jv)k^w?TDY^=)E1@W;DA=(v zLx+eEiilW3v#u+ssJOZ&bWoHct6&2G5j#x?A({EI@B4nge*eJ7`6Ux6Xw3{iJJtz7< zCT(ZWp0sR%t*XKge7k>2{-SEYq#s@UdBuy>qlTvMte8_*ykJ+yV=LOHXNwgbj0xN9 zFZYca?GG;N#_;WkaQsy|`Xv-z{lzN(IoCQMwGD~cOom>7k2yiKW!hPmCK_XRaqkkdAurlNADpq3eiyJ` z{eS@%DC|G&yE3~Tu+x#=a*|eT#~XB+NE>{m?$3ce>WKmp1My4#Q5YP%#6Z|*w_S8B zLrxqsHdZi@AOhC})3}hd`n1AUrMr?bAsPK3S@hPvZNylyAYmxa;SrYX0G}MaW8e7u z>Rz6Clnu{i8Kl(AsF+FQ0npllEEou`xcV)I+n*R!*)Xs1La7z0w7WE5`RlN+;+k91 zR@5P5b~yk2;qy7zo^OV&MSNw|UP(?gljDmO)QU?xB+*4oNi6(_6V_7_zOLmCTT#G% z=s&Y9XZlL}^c%mM*-cA)C2RchqZC#nlWX2@GV1qcPNZgi(ldV_Xddo(t9n%4oe#*d zPW*;N%%!n;__k5-NIPLGw3J+DMqHFK2&$)CLH>@!#cP}X-gX1gid$DY^7-^~JasK@ zyjR|)*&q#*wm3@N3XhxvfdKqHU5=T4$t|jEJw)ia=d-HVvDOHvo``PDqJN@1-v@Yh zAT1%%57eL_m)?^P>a2XWB9~dKX266AdsQ8nw9&y19p12WQ$rl_Ef&=dSDK>tOYHK| z1rx1VGOWCsIuyu#w9H%S08D-Y=)C%&frccPLOtiTUKNAG`IE;u&j+Z*;u0O$r9Z}d zw6kE!yjW51qxiQ;_Oszva;O@4k}o6;1DZlo61xa~=hV6cUlf+l14hs6Xf3$B4R5`Z!R;`md^)o$bh%iD*`C^A zmjNj?&QAS3QSNW!Hz~_nF^oe5EM|97m5-K)Odb5>-BT0+%(Lukt&uu?{ZmeLQnLcUW_^ELE z&*ZWBT-ogPNaZrq9&H1?w#Qau z*YV&CmWQtr<&le((GKrKt4KF$#~MM&SN;CvQNJxW?>O~;dj z_jBdM?W(k`5rPaQy}t?vp}fn7)iJ_4xTYP|8Nrf^eh(=vf}Q4Vh3#Ww@nuR&E(-Ta z+~Rn2%Itdj2l2`3jdi?6neF{xbTv{*l63}+N@6G75rOWnK@_;-VGncHoIFxtJxl2s86a5Y7OA6eGPx@|~m+{?XE zTu_=Oky1i+rd_hcG1>v*MCf9z<%l~{zIsqnUnveG6k; zA&$e+t8tkyp*!h!#fq`7L)g`-d@Q-_Pa3KhAln7WUK!7GWsMooCVEVH#r#b-tIe5$(A_XPFfZzH*)1_{$U7~A3EYc^toN$pY(PN7!9ygvckgROT!;iRgkWM?V zdo|ezx{Ua~uf2?4(nY1%sg+2xzbx!z(EjZx1L-u#VGz;;NSLSo8?l zVw!5tLGvrZ%f2n($PH~0J9*-@D9{X>nq#MgaLTPjz08xkv3!BfOhvhpYV!kQOt$v* zY44nKYm2s8_4ZNEiNIXN2P=vZw%nT6Q-Fy^47K)ID9(<|`}m_B=Ja-n`L8_xx$t%+ zv+IkU?uLLN{ZgH^IEM6zj>H-&GfD{BypTu|0OdCv z1)L2uFe7eLb2t29LQ>Yo#1A{~emejS&42e93p}OJ&uYIXyYDfS2kxD<9)hKhc)gaiW)UlV z4q)wL@kYZ{$EIp2APO^2x3?Y&<8ipcljJzzMV$JVXQ_Cvl{$S--FnTu1IU_np;fPFc*^`S&Rn>YHxTJ#Ik&SRFZZ(Yd(+Dar4}3A-P4(U zo+nn^XTN!MmLDG4qdXYJJ2w5N?Uc9geD;Ohq|GB_qmPEnQS6Uzts>yS5!d|oxft(< zyNmwxO_~~mby^dR_|yI>N`xlBFb-5Ymne0YamsG49L(Or7DVYu7vR5?t$X!}FeA-| zG>b{0pAcez#@4giP079azC@~sj;L@swClK&02F|l%}9D`gp@`NGQH#32^?v*^_9-L zPvUMhQ-;p3#5w6;8EFdp<-Ia{i(lHn5AERd;d!GUI;-WCayfNC-u|UAYALfGm>+AG@N+ zUCL0xBidf0V%q$XGUmeh;2!PURpG?}lIOQ2#>0vtK1OaE)6=W&QdSF+Ug%vU&&H(8 zh;_bm8kD9Vj1G(jjwtC*xy)&D{zVPdl+1FV>#8Za?G66c33e8%hmNYSr%v*>5<g<1<(9y^Lo~Yy~s$7k2K@S9jTZQa`1<35knM-Tb=>tUP3|* z!nPqLI7aNDq2~>VsVQh2VToHN#6t(@H9=9E)oD@y=P9}Qwz_Gld+!_z+RC}C=B60q zO)!wMc3#A=c%22Dn*;ydj%wtfSHn(;l!s7qhkP3qBMF<2>!40%lg8=OQ(3aW{PEd_ zE!VW`#r;|GV{`V?uRhGCHcF1kB`LEW4KU$*oh15!xFl|1@#nlISQj?cIezjPL-Gd% z^QUd|&vaDDdoAa882;QOnt$PDkfA%bxB{ksyxpSsIGFT?Er!|^)+Gb0X=NC81ZaL9 z$j9ho4&=B~C4?KEt>gw9kJvQ}|8W__Sq1HJcFKoZ`=uHOq(U=LSL?UQlXx458XBgR z0FYdS1n1hMZP3XKFm?a6Ce{pehd!D#|ilk6FzHf(fFYQ!Sp~^fd8!?%T^hKa3J1UVPfZGB0D6f-vyAqr+ zjl|-RViQOVM@{-yT+6I`i%QZPSKUm-Ei_+$phae%u1u+bp)o6((u(ssIDd)tH4G2Q zd1Ygr=jFCgQ#k$Tg0{H0=|jL=Y7MM4ho_%=7YqX|m>3D0ywqmN0%-@2$h-+tuk4=v z%xa%}XH{}SckP5m$%n6o<0u^Qk3ZC-t(K#i4LeO{_OXw^d#xp2m*D~zA9;4WZ1<_+ zy_54sdAaXzB^8g0FUVInOjJG_YbDRYW{Wl?w(>{sw;UTxR=o+W_^W$e)j6xR5(9~L zLi}~U2hJ$9669_i1U_a^Lml{1TnA`YI{&2w0XR@5xbg8?Dpz>TRZjq#_;ZWOLAnv1 zMkVqYHBmSqBtQgBp0rD$j16 zJpM!K=fEO~2=kucF@AIg=ScAS^P7TtH!`lMk99I;U~^2nOhTOUrf7My#?$29f@vOO zNW5i(s9K37_vz=vXI?wMi#&!)U6BUx4`3EBd8WWga zKitu0Z`imgS!ci4N$la}etcqH+vw|n)!&_8`a3Ph1M>s;Jd2UcW{qP6)%AOIQKIXQ zgD14c4cKbYv|KXO*9(1qMt!}Sunle`t_^5MO)X=`#ACoMk=J+A5i~R=s@!!XF@V@nJbP7m11(RE_)Cuj*$v+H~ zIF`ZJfh zjCAh1sQs7Q)SJAI4`+SR`Gn&$5i-=1fME@o?Hn(Lx9YQZEAQUEq=N;L{OOanAFlZR z*i`(jb4`KqYWZLJXO0#i?D=NM&$PnKnhG7;sK^UYV!GU0Su>X}H=-WvgKMyzl442&8D^Y_AOLL2l{);J58 zw&w<%(QIGPB<6d}AB??>6G)^eNu)ICcdcP#hCsIR59KBh04x?-wxUw-_&8K!g3#mL!Yr>Qf|> zr>))vHKLX%-r29=8@}af9~{nCg&m#X22Y+^s7OqP!mEJKe*>HOQoYM--|R#5Np{7m z4e$W4(3AeVoq)w*B~#m=Ac)WnXtv;NggDC$8yz70>)TWop!u~T$DZ-82DMe=mqwkz z>a6{vvUs*TB@NiS7*p4ul0YxD3C)UsNADL;A6oV6KI=Uk*~Uwo|0Lph zx1fd|D0E!BnlKtGQW|N#in$pBcMQ#CJ$I5Q!yX^Y8|+nDxUAQ>{iSr`|mLRiUeH4TGtH zTzydgNCgy4Nwf@IM&{Gf(ywRS)@pTKMlwFGbi>i4kZEYs8poxKd zarXz6G~kO^dYoy)ooVHa+BeKqkE09S<_`|Sw2_}~Z^ac1-ecmA$Sx&Avmaj43A=s9 zSQWggXxJrS>QAw1wfms+8N+urIUL8VQ!rTOhpoi8{qc+DJap#f3hx>RZ=Wed_s20u z-42`0q#jxmW(r`~UO>M)5N9wl40e^pAW}0z)89~Oht@VkTGv_oXt6VbyCx-B`u~2@ zbA-*6h1gsZH4=jbK z(dvBhhgGY9!z{4+DY!^eBP#$@r~25DoocAER2$n(=i?aCJAh`QI{pzI3cPIwIE0i* z`MZ9a_xOuyxw{+gDlGn$xpyVr45TC8^mw zO4^(7TNtPZGaFFev^M2S9<& z%w*y?9sex;r|MO1o#@9$$`I%;I2|ADv%!z0bf*8rI3&1aMmJ8=|O6ib|5G1yCfcJYG*dcXsk z_qF*h8Q(J%PJ;m{9JtZy!+@Y;`5{#QI2<@FE)TwI*OJ+h6j5XQwbi+2Ag=3p%hHdS zM-FpAL->*bZ_@0mniKn1V6{)Cm3%&|Omt0rp_qx7`_VYjpSoR!TBTqErq}=lHzIL= z>Lp@hy0Z91DpNtY)-zm}p*RBI>lmyPi)cixRlt`%s6QGw7Yy3ja+A%=n{=89)FLWm z=U)(cJ>BxQ5l|KiG>QPqDNsYG;Y5{4 zig>oSdFO6RhWa0?Q{*p>j#PK?;Ww&5iAV3-KRqn|DEsE1I+Q)I>XB;NTZJFGSj2N2 z>(o)1OXd--mbfk&M_gAyJC_IDWJpRV${T|RC}S_42LzF(!P#wp{d_dR?Uxt+D6{&dDzoA#{i6L1pCiiERr z6`SeU@u$ZYy!0@|0OwDc&Dz%}0rcVt0^Y*6!?g%E`ALL7r}@Y^8o$kvkc)=AQk628q^y_h=RRuT-(-!V;aL#nzwZeW$-fiqx}ZrG6#KIw@WLBAUy$h zbk%KxExX3XEyz8?+ifO!I>*tUL0>w-J(FTL9c-aX%}0#V&lJk7xR}{YJP>VGsmww za56w)1wzgshSKpkFPs@i3lRiEnO=yrMmDf!bR%N0#dc(gGpYjxn{7wQbfsb1yt(t- znYxl{Lk!89-^Edg_rh937)w>rd_VoeT?fUAoag^c!W88Tlc~Hf#YZk`zyBorzJQJKRHsBMb^t;!Y#7`S#KZ#& zHL}LWfOmaTMl`URRAZ_xI?50@s>e!I;!OpbRIU@dtiBwGrnYI@5S{qvtD^uZwairI zKc)D-YQHU{aZ~@m8FL?Jl9{XdZ_vIVY)a$ZHw0wY|zVlc9v&cKfl&V*{IeNo_(IF!UWIuq2IE8 zE*<*vp##gRnA2Z8D!K5j7Y{11JaW))N;dFe3^^DlFmXim^`(bmrBExN%U91zCO-aW zDI~BAGT$cE*J0JO2U(X0>)8TU1*l8YqKO=k``ZBbvZIxpr79B3ObWbsnO|G8vwoO) zg`(>%V=^3mqhn-)d5;Z>bHO#l`9SLI6~@q|Vx^G}&na!nJ9XQ`yKNG(>SwXQ)SH#7 zIbW)SzVVvjH0kA*1zjWI$A`3^&+ib=Xs>-4X!n_yJGH=L?oNcFFG2EcTs*ma8|rRb zok1DIFc;*>=O{pt2=xY%p(fH37QSz9vR#R^I|7L_NhXVI?4-jC?lw@z7so0w{7W~= zn#|ZPE!LD(>_DLzZWQPa2$2n-^MT?gJW zlZWefC|b*V!_6bGoE$~epyEz9?AF9fE5MlO>gjNY%?xx!)4(31?-!3dj>Ed=rv2M* zh*$N%)RmIpBHn_@7?V%-#=X3Vv7h?o;hR+P0FOkga5b*!?FQR!B|EkOhqkbuItaMN z%Y%yP44{*!DnkH@hyippq%KVA{sO$r)k1>p6kL{-qu5fthV}B~N&u%4;#_iAErCw; z>AJma@kWZsVB?OPG#<`w@WViN)s|N5ybqS1t<-Tuwbvcp{4LN!89(+`qBS9b6q&NW zQc-@><#j0j*$&T~$9%Lpr7+1lYi5)E%RrOQJo)*7zA8n3K5vSKMrO;s9ne1de;X}G zxRyt}kNxR>rYa45q_ z(8oS#k(dvV{m+S)-oxD!{JuZKIeuKRVwJK-u^<{XEP|0KUhoSZ;hB=R@BPW=X!2XE z;$!$|;!QV4T)Xd7;D`H~54uBktqPM+3fG@jyy)hg9cR)ke|1)8V-D{-Gj?DJF9V%E z^NObytbQWa9B$n%Gvs3ZbZ{EWBL%HJaRAsCSEgF$3k};+)JztVvRuuAwg)8>WQ&TQ zUWL>?;r_5&@!$YpWU=r`M_QsI@Z7V26s8K(3z76oszdG)lD>WQMVu2GPrvnw$`Jgf zR`aGuX3vs!1lmZgoTLDTkhB z!jiX?(VOy>`!wpZeA&D|6Ie-@a_(m2ukpVjE0v}9(3u6IndY-G8pn#^Yk=wK`w$VHj@^D%uJd`>Ct z&0b;|?#C-90*lTMXhDaTj_}Msce}d}#U6FeJ0B?5)tl1M| zTqny+Q-V;0vXJxMe&Kg}sXTl2XWnvW$=)(W7K_m|Loe+YSJ<0-63<8?&Xk1$cH7^|%$NQ?pVCE-z{WPTuhca7IB$ z`|f3UYL)d%PvD+GfdPLp4j77p$|*I2b&`~3NkfUM&ax}qUfx0~9S%O~xO$7!;_$_yV9fk>zS3J8>d`wUpg z^s?k52(c8;?xhc2APAc1G;4CG$l3yNxbrgE@vi9p;12UlCP%I#3_|VM=%%qeffuU+ z#$lWv`{jTB*}7v2^G;NtIs#Fkh}0-2?#+K&B<+MOrLVuz`A+i=^QbH&AOFs8BS$D@bSX6izD=!3E#+B0vKC z%>Be4;i;9FP>3ab3B(UcOq<}$TN1auSWYCIZMMeb2dq=Xv}Rpw^qfD*O4)RJeku21 ztH+bxEKDanaH5)W8 z8XoU7Vw;4MTIhrpUODG_M%j)rSkMbaVH8NHIg>~GCIP!KPZcrdg|V6>P}cxq<_fEh zN9vZ)KQ44vBujl3#q8jbo>dD&&+6X73Y;ak`SMm|?#`*N@fQf8-dI$Y!dB;lIOeU` za#$REi&yDhD6_Xp7#9NbSSxV*Wh>t9R32V-ku=R=1o>Q*_HE^)s2yKtR?~bN(P?V4 z(GYzExdJCfCIRnEsZ{YBP*f<8IRq@y2k+Gl3goSTkq(jCh-3tFaNjOv04H}z;eVc@ z+6#|ZVj1}`aD{0-zxTn<5*e8@6DW`^F`Uqr&R=54V}`?_*%w;`N<9ZVMN|XI^(zt= zwJCPANi431&sL)EyLpRewTYwi9II~|Ri$af9xNeORlU#(g!Fmi4S1`{4s{|8O~+NP z6JFaJHmZxF!Nri}hO?9ssY~d37KrDwfeH@L5W1Kg3owELM^-Y(p;>AO;CQY)uvY8d zU_X>JLTam7`Y>L!_^vX)ZA`v6GI-zK`T!ymyL;2CU8^LAC%5? z<)4Kxh-)<2{6%JX5fyC>7|8NPdC}+tnSHbDyMbI>DA%Y8$X8NR3s% z+pdcQzQ>ae7z++i>xhP)pw210iwQBI%Y?ODv(5AcGf2HF8f1lP0X03+l&a zoL&kDNL@i&{fz{oxFgrs7x;+oiFWMIbdLXV{x_rtL$0L^Dxwzr$^;W${N+p4AAxcG z82$xJeujdjx=;0H2RzWl%b)zz^*|k*zH}BzZ|S4f58%>7-pB;ZE=YP(1>e}FED|YR zaeGeexBQfs84`@Ge=Y&%&X}yZh`IO24K`p|io9V}^%6{?-Agj4kzWBt$5ouKF73_& z1Ef#XV64VSP|vE_^3pLo!inVxfhL^yr}r-+5S^%aeoa8hP;xSu+4$}Jt)sz3gIgd< z5M}csn!m^a!t#lWEi z6w4A=zzXw96dS?jWbu~sBg_6fVL?K*m}Hlh2VOsk=kMPFycAH^c>JI1!)~fh92Z* zyJFZZnYF{T(CbIa6{abU5+t`3vQ58;D@HJ@NL>L&$eh<>n1xP=O;N5u@zw6vCvbYe z<5>AO*b`gvh?jNw=pX&!*@D}%{<*{vgVxk z#>e7Q?g@?7zp6V+?4H$O^sV--DtHM~SqUfDf?r(#pNa2SPR?o#2LC>VZ~DVLUE6@g zvM$4|kI>X$K_P=t>Wq>yJG{Dj89-93R8Oe09_Nq}5?nQ2KR9WiM)1`*>vTA4ze1cC z?RMVUMzt9#4S}OUe8u{bI*Bk%v6(Rps}EyZ*fkNRog=qwlq8o(v`pW`shK4KdF=l8lu|x|>{b;kq6s)yoV^tKsEDqSw-Ygr_@fmQ^a+MeAif^ zXyJ-XH5Eu^BTv3o63nzU`xY_;t5!lCb(+LjeQm~GCxMMJjH(yfXauRCO(;MycNNg3 z0H+g24r28c^A95USSpbf%c$W*-e;5rb{%wgS#rip$468f$ zj^Rrbn^@m4a2Q?V*Z}Ldq=fgXHdh`nXQnW`1!qN~%QMDzjJcZ5<_R2?al<EC=6Px1k4Bgl!&u>qVT>kkv>0XH9lT?cpA>Nwu1)CgR>>B<%p)<@y5b`w=0#Rdv|boHF)B@nd3K49Y83m^! zqCZSkPuUH8smTMMG!fl|z?^_er1B8xOA_`=TEl4j~&tFzS!>ta76y(Q-4$~UCDg1t=#^Lq-=WZzc5iu3PP0BAViGk=j0N{ z?%?oWd>}R+@CWy@Wj-Kdl^)PS1D$ekBO*kf2nsnYTqHs!E++|SZ@e7(!O+Q+a{UUT zzuH-i=bCX-E}@_hllBE7a3=kSp{?;(-pEb! z`o)2$QK@|X?J+q7s8A>*#Ct}(y7(>44gW_271VfgTbFmb4 zI?Fq>LlQXInT!1>^TpT;{*lFd$%yGrCv*FLd~4YIp+KHj@UobFFHg}TTcNq-f~l&+ z+5rie*^4yiT9w0E5HrJQP?%*WeQ1tzp^`-ck|0*S?PJDiJmidL*lCow?ML#qt18y< z(QnWD#;9)Uj8wjM#!*W356&?6ufGanhFUX$tk{`qNT-6SG^c%^RV55nM&I+1m`T%# zEluS5V_wtP=I7W_qdd)R*sD3QdxCx|&;6cuqAr{ijZNGhhJA|U$j-xW*qi6UzG@q0wTVB0{p*J0;j%6!J#O;b$#;uS-@7HUUo)icN1n&clsnm-0FaMMA{Z6OZ>N@LW>wx_{s5|Kh^=qb9>HKinRam1EV@=w-KiAI^Un{?gnHfp? z{N_L8f3rWMkbK4r-!s>J$;-GGp**Gb9dn)Tv_ZG|?f>Ps@00GTFEG1l=7PHv^=^_J z7q2l#(4P(ncV<6A{1uFN6(Y{+B6qu2L{iggVk|{(ka&l=P(73rDy3wBgM7e9SFOa9 zDrml_hIh`nzm$|f4OnP~Xtz5~64`eA5CYKRjp^%Swz(E*p#`cW{4mp7rI%Ay0TWKn z#%N>KtRI1D7%vNFbqmu(64&)e(!#t0pIaF4CK7*Wd%n&)N)WYRN50vwvCYORDiny> z>u|D8QePf+u3PdqYN4C0bhDj1RP-MamE2XN|TAds}-bCZToSZtN)(MeFe%Hcegd?t6g^A z?>TQtKYUQ}aBpW^xSLdBJA)Zb1zIUngSOQFt2?t|jI;amrH5pK3_GzQf;me}T4X>Y zX(rnKX6&aWr4F3O2`MToks@E|E7itGY|7fuG_;$o5@`ce>~vA`{vT@-gGl2E75h7Z ze5W(d0K;bqE7bsS-zt)fyKoJaLq%PTNi@iG_t-$nC-|RBFkw*Q=}C!OMNNe146}eU zieu2XaQCY}m&~3KyuXvVbXieBO$e+Q59jM9(F?I<53rv{uowu~s=DDCtvqk_v3ki6 zllgNtqE;;L3*S!fWVUbP@2oC-l{y&8@t-hC8D!P69nDl5##+ z3Iwp;^f|7(*;@dz&QAK0NbXqXN4$MBpialWIz)P+-qg83hCz;L zz3GU!aV-3%BeRPh0?4egCpWoY5TTtd7*r>@Sb`aL!9mC2;1+a#0zG0j6K-~U_0k!# zQR7GNgnFNZO@;qfRXLfqKMFzLu~f^yQg=SfetaZWONT?f*Hlaw{IZj-3Q)^AbQ|z; z6r3_)tz!e$RDrq2lLAw{_PpW|?+lg#ust1^mxhI;Et*Ah5fuMKBme|e)5IK zo@<|gFxk+2mRIzjSS8A3V?$A}WyJ&8#O9QdLzdXjHZ-iqcX4t_y?%?);T1S)_3mJy zLD!4nTtFa^sn@Z4q=MJ_uoD1i$kIh`Kq%P*{XZvN4&?P{&tH!}h2gX3t`uP! zO$yscOv9z`@Y1~!i@voZR|^j2Soew*AE7^`G7$GWXqvj=zM;h)_X3_Enn zl&3V7o?PNGEIQz;OO&-6?Ac3d2a$HXK`yZ-yMyx87njhEdu?E;nZPE;7N|>$db2F7 zuUn{%Z&PI&T-0U4$P{9nrcIZ@V%v*gGq6-k+E~msv2mG|o8=QZUEr|~ ziJGh!O6-oKc3D|8u`c4n;lJ%gn1!N)JMy{dPp6i`i8c-90sPfiO<99o)F2Dhx ztGBV#fn*vY4%;h3gPqgIF+}DaG#wr>A(r;RDHFVMNik4SaPEP&j@4VOWMJEQAl5?)WsLE4?}{qd~qf* zwU+_}uLjr5JaSbn=VxQC{mIe$OF`h-a9AY(2tXnoPwl!hp`^0E8q9Qwj=`Z~N ze2f$xmJK{Nwjr=`R4f;qeNwrZuCxU*wfE-3Gy~#q`QT2c&l^0d&_*-#UOUhKd7jl- z-XAJW0q=cibiw@aqEDSR6^daMyQjK7>Q?%$wVOAVo8>H^Wy zA2%27D1H)p@}*Px{|Lq=UG@p9#Pb+mSAbl-N~mzG;YFm6ZkI~<(_XvIXCL8B4OLBR z1K=V$-e&=TVrYJLe(tf0*!H- zH&hUR5o+o`?yMaq9w5X@mh{AJ{aSy}z=peW{Ok{g$FC*xoG94N$q;G zc6*_&ljhPx7m3CU=ZPQ%o=iEdf}ZND=YuK*Y@!I*Kd8L zr0oFm?`|qYtEmwJUYuu`(~P~24eH(2QId-p(B@QxISlbb8&JJOxc!NnG6FHaxNrH) zz?t&{@1;Bv3g=Ig&(7uH&%*jg;9fQs!pEj>C3)nye!=)H53Tx-wIA^6*Si zaevLrhO_lrEowo@Pn%-{HO9s!7L+9R35y(7O{O~FHVhuR@N)mQBMcpi9TZ~J@nNw(wq{6KZiEe+D{Z*));zhPP6;2g z8hTjY#>?8*Bgr0Yv05Pj$`-yAJYuQ=7dT!Pze9m}R?=069c-pjR^q04hPwlZ#_8{~WJwnLAq_&jlXi0NMuC|9~xr z?*~GO;E%#1U5wv|AGT8md4K>Iraw*tG4NQNGr1(8k@}!tstLfj6`%Cl2+ILI$~b{0 z0M0kQ2cmG=fK*?k`Brm~gaiV)WRYfqT1^C@ZZ%U%lv=z6xTJ9as4>O&>Gc7R2yfQ_ zdIH>A@U4_8WpWYC1boMT&)^yS+rgywEnalpL)8A@ethLfsI1dJJ@Ux_FX5$7AEJhA7^2dw5=(0_vyr6Wu-eLV0v4|#`xXNt2Q)h z3w9@o)yu2jJDnH3P=8$9kyph(?o&u{ED`R$h91BZhyNlX)bXLbC)|3)d6|J8T6IJfue=Fo3f>hI^T zZGK#};%>{4ImNyfx4t>cHG3Z%bqPK3UH8J~f_n4Q`wka&2()t!AXQH;{5>Xg`^|1G zJi03Ry2kSMl_paf@N>}z@X$|=#x)$)`UZPxv)u-&j_{g0o%^+wlMzDcEsy)H)W@Cl z=w&{TLrQXWJUd9vem(F)v-k3bEoZjw2wGKF)eyIQ{XLiXCbiW+ibci7fm^%|C~7^{ zzj3fMH6lwoielOFFa;66VCJ~Z*RM5&cBAYGvm0+EIXgU0B|Q%t$tUffRy;a7AG(~r zM-@$J8ie^7FEm{6-;eS?f6%xGnulU2ru@Gq$L9a1{laGdr~SnKw+&$b+a%ckHW>Tg zrt12??f?CP|Ls2if8A&Qulvvc>pt**-6j9m9mf8D`UN)sP2>OPBvxVrTya%-)`owv z7eQjzN|PBJ_Nn}s5}O9U*WhI^^Y5?N746u}g~`}#*B=-!3Of84R7l<=rEE~Z)o%mS zgEjXgV^hU>j(A^4=gK4&;MjdHqwQgu{x+AVsQ8|u$jApLROz+ESZ3c_#(!^dgEJg9 zgGE2hyWyS{OWkq6*n`m$#p>tBH+)k+c6kCbUjI?$#ZYAGl(CbMmzB?nvjhQHJx*6ww}g+jBKZTW&&>nF5gCGY1iYtt@r zQDafGnQe|WJ0v22n@@RK<#0QZfyKzPx7&^XUR%%D?`fR#z`CdG8o3?P!5N;6#A$e* zeQ+E5w2^5+Fhe34-@QVzME+IoCGKX{Icks;XS&o@J(eIoa?EHllM2hFpW`y`Ce ztIkk44e5LikN;~iL1!Mz4b9tBN%xG(KO_PoWjcQT0l}$`!9@{mE3(YeV$AlB`)uFp z<7oK{<1eLzSJVw>N2ZmVX+HJNccC@~zg^h;ApiVG75%qzx^BM*iF4ikwln>gK+kLf zThraH<`xij)oW@MK`W)~+6$d@%WGFJ0DP`#RcUfc-1!B;`_>|6brxo1(04<+iw;3= zjnl0PIksmqgt{AYD7sqFW^{E}#ua8JD_DcyxarP!`TKX$l0$+vXoFoF(yR7zzSOh$ zli0C_J9_Sw4z=Bnv`(e6V!s^VGwDg{ag58AJ_e)_Xa?=Ax&XZL(lFr>cW^99>oc^@si9tn^+WseKWzaTZq9pzL%l3qW!cq5y6*!J zH3e&f?^P_&B%u3v>ia(k{(l6HrAhB;Swz>d z({y)Oc>U9wEPm=7Y<{ElUhoNP#4HV2C9302dh}id=nIZc2BoEliIphufpmv8NbuSn zRAgdNL!f)sF^B6S>#n)W;eDf@oWjlbMNlhma{kU3$~u-xF)k@dMsgJAHGigmwl7Ki zQ1$WS8#&iVU-(4q1vOAwgk_Z@wEj#E`!@Zy)kI-%&KQ~0eybvt~nw|_e<{fIKj=_2i{0f6n*iWjxO{=&-Wj^ zS2j=jV9C{z!(iXRb)aZ3?UdB@byy=2a0yE?wNRlW>6ci8%iV}n&NRkX9 zC`u3z5y?5t4qos5UVU$WpZ9h@yZg_c&vexZ6;7Y3uCD3ouCsu=KlBItOf)zVG%f+BlB!P4mGp|v5i9A<2u^a;7_3aZ436b3_h z&&V`N)hzC?^E`~nMEYzDvq9R#c9zI(X7xdXWZL@&VMZ(I!TXSiXsXPdtTiX`$Cqyg z4||AC$_o|(i-e&ONV(c}KfAC%S;tE*YzDb!e;_2Rxq#2p(xNirf+hv#i?3c6@W>BQ zDo~W83#DRXolyauhr)E+5yboR!62s!G*OkcDGhH{BE;0XlTM25jxu*~Tmr#$C|giy zu8bfb&ARmHklP50Nf$s|19`|!y$!fa16oDEH8NGVg(D=6QHZhXn-@4q zwR}Vkd~5|s!zhX1?AIc|^UiHUf~dv #@2x>*&hj3K}*(-W#SFcoZm5LJ{XiU=dq zkuXn(YCIOe%0d`g;P6g@FG($^(*z=qhuE>xPTh!PAM;`Qom!TCVTCK=h#0xe9>sMK z>9Y^v39tkb-K?c)AqjW5)8##>uwckgr$)nC!$&CD_hFsy z04wNy#0c?Z=us##59}&H$9RP25A-@f{NQ>_n;{!zTPqsVns*o;LZ!OZ;Q?0jxtG&` zm@7<&N7~NlbwMDV521jWHIjrTGm}$60T~O1&+|!8%~Q5TNj&`agxf(VDkoUVo)b_! z5K0cvWTel}c)tM4=EaEeYM(=AD{ z>-dp0LJ!H-u29*BBEv|xppM&u2q`A&%ua0id)0{o(zG_Z}quTq`h z=2t*y!Xbr|sysK+227D`tUU++zJXIQt4M%SRW$PVVL_yVNnDRq3Z-(Smn7@TfPIXy zgqn##7_Kt#KG*!+rB@UVIi1vdsc&CHN;G zLC?e{CUBkskpXMrMPsd{nC^E417=t(lyd3=vhJF5LavQV#F#UGdYR1|nj*tDJYVtS zlvKo{2=zovS>7yhqP|3Z=&S(vQ9F2@s-kIkBCzL?J&L;T6|e`cWwbv10|hp7Q=Ona z9|mlYKKjh%MoevDX%~Ikgifl-CCN9hO?>8v%h?y0L>}f|fDJxpY|12rZ$Myn58yL<5dvwiSP5ECX$gwz|baF0!RSUVv8Wq=WA043L0En zfD4DbDg>IK5*n~dbf;Yt3@)VeoN}k(oez$f#i`LKLCIK>b3rB#n6kj_G(KB4a2Svu ziiX}*cfG{?Hd!^5Gzaf^Mv2!{4RH&TtNb0te|-0w72P&xo4_%ZDn5jL39p>CM0r4z zfI`%qf8vg5)7}n6?hsQ~4{|%@sE)>4dm(uiVL$`y-PZ=9sMEYU&CeMly;YM5&!YE( z3&nT8Mz5YS#P}tQ<_|MNDbxmE8bO8ci&c4j!D~Xd9%3Y@>`k?mTgY@Z>8=@7=B|N> zt5qcJm7{2p`jhH@Xni?q(rOhVB|lyY4*p4j86Q!EekfE(7Y1o@Gl0SA`Q@2X-sgkm zsF3dc?&x$n#L1uMU?L;Jm;W;;>Xc44nE}9-+<^3=u-9?UkJUtL3YE5_;4-Q#JY--6 zd5yfD8rX_xZYK^f9bx1-qMTJ`3fo{c z{0x;c5NspE?hsdg(s)iKob3%1N{=-&p|}HX1yMcK%H=@Yg;o8G~KLYa-*3 znw#$K{H^LgsIi8sNKL^gl?4elRvIL^r;R+HJ@x?!vAT9TF(tPu&v-6~L#Y#`h)?lk zfcC(cde=`o1J*H3w!xqd{}!?LzSa0SYqf#;_C@Dmk?13id2=a*SkW^*vL3El5;(A!$_;GBUO@EYS7e_M_ zzTSxaA)e4L4-6682U35dn%aQ2z@&aSAElxcln0r^2H7At9SLu2;(cG;F}zV2Le5x8 z3sNVUjvCPpLz1Uc!YaAAnAw8KD2kteXpwD(V467rDA7!4coA5;ttEk6DN`4&1uz;) zPywEb3lbNr^tE}waYFh(0@MlH_FC)D#1mX-uvyC_NN_kIXaFcN;NjWkiszMp6T>$^ zopSWNdA@_;!3%ijNUh2Mw9%4ulF>GmZD2YcHn3t%z8^nk1sLIB)hOh~HSAF{G{(s) zTnfeD2(4w5*dyX5nWF2%tK2MX>p35Nm(_5cdO9e9L~k)zNTQ8Xccfq209_{#>c^>N zPel~ne$6^aaE;6lgZx3caSI*MS7$v$!qi7glwh?LVw4}BE|Z=sPiaq_NP8Eqee4EZ z-?a&|cA*QIRkv`G6(C;$1B!@FqBr*LP%#;E&;=%>%o*=h)e6)u4w61F92|Ci0BZ_j zQ-}LO$G=ZQBuph~krLFR`jmiKg0(6MZ!kzcA-$0`TgDxrT*}^*{t`}4GsUY!9OJH+ z@01Zr=clhFFawSSh?sm0k}PSfzSXKfwy;-$pWHBMlY;Dt?4iMfGKT<2^jjy28)v&M z5`_u*#vgh}mSD@?{1ol7A8(8yUZP6ale%tws2Ns!wL;Dz_zuB$E;K_yTkqa|?JX0* zk6%KyCFYxX7h%?x*uFMS-uf?ls$qH5YrzeSNj^a3anyBR>TjX>o^S}x8-2XXB)dPS zj($T>sulQ{0Rran?8_YLtm9O~VYnT-r#;PHW_DhJvSOj*og=X%?**A zz1nqwhV5D~88Vm{#EVTR92KVFPL&MKxyB+k24c|dm zpnZ|(H)u}H{`F)w&ccT+*mk^DJX`jMIE!stM;~kP#YFu`upAv>-$Ssuwmyvb`(0Y1 z>oR)2{1Ut&+#YpippbPHdLAUZe$-tK>P$5v4sE#0;{q_W!dg<%FJH&dnjZ_lnPZPd z6Hqa%@)_2FJT`fC2txhi%V>y)k9LUaFaWN^g-*d#!n`5}Iza6RCfK%+qe{7Ja_k2U zy0CnFF}r2a=W3*cryYCkfhLLPi z;6ME%yzETWrWSJ+C$b`ay+!i8bym=##l0m%@haZ|f3Ovc5@xZWx;WP`$|kEE(AGro zg@Lmwy_nMNl!PS0$jIix1=_<%2lSD;VbIm^a4w9%HUY(UgGu9%X)1uQHBI^R{L=?@flilJEspcF!lpv3b%S~ zWFt~p(Hp`0X)qFkw1pRwxihKA#FMH*V1a+BnCs+2#cG1!qK3 zFqkmCzOafMnPMLb=CBDNbkI1 z_Z4K@=A1FXie;LFIjA)FH8M%YCoDT^XWh2$-VvN3R-bBv%w~gbeuy71%~G`)3cpP& z6Sao+ZMJ*PkljaktFk+705Y7W($BfQu!I~tS3!HGQh+{IqPxJhc>ztHKoM@QjXbC` ziyc7li}JSEO>AZ~t4rAPaFFY%g@o!85moJkC+^DqNc9T4X@+Er7^@!+RRlyBsCm;3 z$<#HOIw3@lLSzMZbXnX5?P_vC#2#3k&968jGGrJyes)uhRFtr{ucWmRB{C#w8T+`1 zreDS;BVS+fZf}_2nXmXylpK3ED36E>REfGKLxxaJL*jRHmDKQt>K_*(c^`~LMo+@4 zL@Z|*d6tNA2h!;MH8|yXKT@(bnf1n%^&6wUTgUh=)EzEe(#V8Ol#cXlp6_Fu85i3=f^iU7O@>Cjv_ej-9RR zykeBcWkC4s#hP1iK~nLy1d@@IRGnWnjDi=}1Y?{L;rn!6VHOHbfJL?55M&0sw?Wxe z69GYHdMQwXM!ZyT{8DW zdCPjld$!6G>QZ*Chf#_0g09j6&%jvfrh$Y>K0`}OM!^04?J39sNja&I4s#z0u;6e zKs}}sy{4FPuyrIGnvEGtR0&;AB!+?Ia}yHM;7iSP@^Cu1XGJz2 zL}`;_eI=#h8C;lRu{eFF14kZ{rWC9g{r!Pz+hFA4UAsD&`>5#TMs|(eHNka?iO-@f z2OQc&DeHXLePDi18sQE7O51h_45!doVb`E69nq&d&p>t?s0piByn1S?YQL5q01yF5 zBOE8ITL2OOpeF(t;C>PTRDgRJA(Cj2pjg4tUp`^|3|3hKPBoq6{5L4=|C{=s!B-{m zc>H10iK@RBa}gLPFs=lgaR01c$K(RRzt?{aYY+fXad+3TbGs?7=62o9(arGU4^-pRoEX{);*Z|FTd0 z&*J{B<8SHzn0;jb&i=nG{-3nZRNqeZe_@{&g!AYn++zRJ27jN}-}XVAFiskPaM%+f zq`ZpOFWUp;1lq>a-o{f((ob4SQq29PBZ2e37n}&y{oS@#Nde#+p})}mi}zETU;2M1 z$o_55-!%P|=J)=GMg6<~t^DeX0Kq8d^flB@@{!*DF{y(6z74AOLho;2Cl6XYMj{Db|MNYfwE$v^1d$lrDU ze|DVT5))Kf!9RE<=d!x`aF%ZKMD~UBKPirX^}>HRe-{3a;CJND7Wv&|zo*6gPX1-8 zKY0I;KReelz8Gbq$-3`5L9U33d_Qyh%*W>=j}psUAN$(te=2z3%XB3)(UkVgcn%Aqn6|S(h#ek6pnKW^kO>+?L+V;@%G?nmS2rUQiIP~Lv6*JnCVv+ zp8Qw+1n$Y%{8i>j_`3?2{|HX76BU|Hke|@>GyEsRe@^OzKlxMmk5zuv@XHoEcqrim zkR$4sN%P7SMSWe~*J!>_KTR0B)M@WH1W)_kZ5ONcoZIFKS8}!vk9oOByd=k9j7+*% zlt{A0wg`_TM{`-*dSk5cL8tu?A5Z-CJUzLS>81zahIcX(wmyX*-17+crzsEuK*y!M zn*yVw3HJnL1VCjKa&pH*0)hbiHudLBik}k(GB9D%MfhRCO8)UJ1#5!;+(7<^5M06P z<3BPQLVsD{mzDnP#Xqg}-wFO>*l&2!e=Pbd@$Vh+Clm8eIZgjylmB3NJk)v`e}-_+ zvcj+AMB*P$|0VjT9siZ`ubza*qyFMy{vECS-HQn7&Eb2^S$8G3UyDE;3?PUM~3+a`)A1iclCcu3F`il^Tdi6a5BE*aI%pVzOc+TN%fAR z?qo>#g4aUr|DEw?Oi25$LYsb*{YDTH{?$4JE)8^()CX!f&Yg8uk&+H8~eY0go(Q6|A8606G7>}b*Gj5KQljv5b8bA;bb1X zA@P?LPv+;4pYwA7`h~zd8RkxgvHP?GTp&1^gjjA!H~NU(n($^nz=E@eiDQY0ElNuA zg^?vCnckRVaiyOV{rr*;z@JR>obAW*7j-*Vh&}JUpP8{Qe<$01-m%@(F7{mw50A4} z2h)v@lpPC9(#{VC@1-Xu4#wU~Of0&=#;zmXv#_wh#8axH_~G6?ufi^E#dd8i*P%g4 z#U$QyNrldK*R`}h@ajC^zZV1W; zE{O^Jb6ra22W8zW6W>J)S?exLcG>zy4TE0R)R&++2Zr)^1fH0YCzWu2Grm;_dxRdjJ=5XWGANO?m=j{~+F zf&3ultEpua3D;~GF+6C_STd?3da}LJGJ9DhVw@c{;TQhg>pM0suEiOm!aehb$ug#! zPGeu_NWIO%+y?aEE#CuKxA|u^sYQE4@f2LQhNJZJUrX^>d%wDQDx?{kv08NFt*#2S zX5-DbuIW2o7otq=%V^&#e|L6%tXfmn;ylQNlc8aCu(It(4s6vl`_WzOlOLMVaCo<1F>C2XVJfFo!vB#*>J3z(9c#V{Ly1v&vMy+P~26{PW z^^CjvVwOrhJ+X{#D$SX$qodt(e0(Y@XM8i$+wMBPO=Xbl*~GLmog?A!yQKsac^fEIz$uXBb`W6_Bb+`MDlbYb=UJd%whIt1>c5hKC%DOHu7EC(BWPV zAXwAJAr^ETy{lyX0ojH>M!JKnfH;%jx&6&$C(3Nii%OL#(TE75>ltBi=%V9urxkMlTWhZB(d#+IFPWhuU|#L*U=Uz5;3_gci{Q(3YJ1^& zg&7rOD=#T<`lb)jg$pcd=gSIo*U0!S>z?brl1kxM7EbCVqweh3{lGw{Uhz=O<{rD+ zvI`DfczrxZPA}CG10#}Bzne6Va;=}lb5JLCdAnCsMr#fhxJ@41(IO-5ww09Z?W;SZ zFgV12mH)v=09}`lb1+Y$fwgsWx95mhOfcr_0Ui|`yR<%KLyMUN!k?VZP*f^WTHlt_ z3==e=&3)-B5p|cRdvWCD7o9kAfLqhafqP(}r>5*Wn1;JAE0~lAn`H2aKL3aklzDzD3hwXk_%$@N)^lb4vC)ghMjlJ^ z5z7m7T&gc$duk0loSOWA%u{YG6@fxMX1Td1Cnap^>Ps&$vnjbFi9%b=u3o(=BErhL zvmFuP;<~dv|J5LS@teX~G1aj2u|l2ZkcEAUsJA9e3 zy?W(Jz>SHp$S+?82QnWxJ5dtfBV!qP_b$0OCT5h2=gVO8^xW3ALqCT_-RLMw?~t(t zd*D93NKv88b?vZ<3NHw8b!9W|m6t+}!Fe%DbFro-fyucmtTGBwp*&i*;yEHBv=cel zpPKJ|qh&~SsdV0u7bvcg1JF9rNyb+)^Ye>}N?li{-oO9y<=eNlwe9UA0&IS(uaA$v zvr}FsD#FULznh-+{v8Y&7A%<~F7D}Ze8^0bBxVs5WMVSk^R>TNThbwxPp`ZC=txS+ zccm|D{28aLOG(L{ZC*ZQKZ(Hoxw)-woMCBd^#-Q9>y)_k_bun)n{Hn_o2||38Dt)- zM|a-`f!C+<%Fik&u*KZ%?+uSwU)u{<7`W{}G>i$rhWM{exi1X+?n|%86Wlddt=~L3%)yE zn?sF3PEJo9oOU+^o-c!E zAUe9Vv^%A^Sb#S@9f!T;k*@-Qk&=da?IT4 zl5%3crR8J8fjr1-Q^NkkQ$7NL4!zPWQ6(iPe!jxOA0Og0&z{|?%g%P_<?^>ZdPr0B^vCeKL zOSYBiOl(={Ow`omv7w=Rx1Vgj6H#wF8#q!D9!X5xpD8Q0{6W^;U9I&KW#aYEGG~?A za9C(pp$;5D1o7SJ?|*DmP;l{y?X_@nv!~_d478zPZOtSkgu!(%H%}+2Ka%2LkmpHV z-95gNOW}z8L1a#rg2mMseA{GHa2EsfA+rKmtH6)KHg9#S*lpA zxN>lQ|6q~0T)xsb)4x^GXCz0Lo{CC-HNBg;f4g}M<+J52vIX^iP4uEss2}@BB^W?yedAvFVD-bxq20a z;^${2-}a|xkm~PBem6D6y1mtV^AvmURc+x5x##L&?JQ(^LA#etvBAd1si_`wii*p1 zd1?a}DJdw*N*|C>b88b*kb-+xw`-)ISzK6Z^yehItFP-!*cvsK@WJltCm%bT?1#F# zAF4})(;FLGc?1}#jh;QM_B^d|w`OaPeRYWyNtZrp-PX z3-Y^M9A{;~ucrsZgau46O{*rB@P-FBZ)T>{xt1>>bI}2fx9`5diuBH|teD2c#5u|1 zqg1iMYYSf;+vs#V4Qt7nKl*Rd7>y3UAyLe@zA!P~c>5^zoRU(_C-vy7hPm3tQq^2) zGX`ZTiq=mvEvsuB`n6#&YG?kb=2eBE=}&j0ps5OV53)cvXVNY&4uwuxQ!o&fbfvhG z34aH#gShFn)hICX88V6Wk*{LD&+W1JALXsMjn0S3U&F`9>`Q#08LB^`K&Hoc5ZX!wj}tKq)z z%@h~b%jICe)XdCGCvHxP!tSbwa0ThLdVdqix80+Zv^70hLbN1Z zb>6zV?(SVmXG*EWOD~nz{-;26G}VNv_HpS7$o`z{{(UAIXasyOUTM*Bj>|EdNS_OtMlXltYj&{24bBdKxRq@W9&k*wi$-Ep)7v{s~p&)kNhNPz1kwObElUDHUTd~$A zGnWtj0>fc3+gdAS_e@nCoQ3qFTppmIn*#ENbr(Oh2rcxfjFWFGKHLksVUQb7&ZfoCemWwc z)QEN6pEGx-Jd%5dL2piCKu5lO=i{UD(NfTT#_uB|Dp*-5!ou%8+pMAID{sOB0(#y* zn@cy@Dfl+^tV!G^zA9BgsjvK!@~7m-9!v>V0WUMa58DP#wIxcRfdxNCZu^z#>&K0n zTjzV5Y-XCD`0>M^U%byr)Lv7QKyf1^2B58~Ra_8y+0tf8LYf&$Hqg4xqmvRLmX<|+ z$LkObu?}Qj-KtDp8<0Rs*9cLVnUj|X>m@zCPX)GNHP~-ZzFkveX`cDwYNBGmc09MU zD`_n#0+%B(nvJ}+>wlXX8<<_JM6&B|H|Ji}jg)&;kaix*mhvQ~WaK8{U6Blx!Y4w8 z=4{SF%7Oe>uSULjUa+*NC?Vb3BVG4>erWIZv!<;hz68c;o03%CT&J--u%tah*EjF`7j|&nNCNyZ52B%#3kUfn=+$xJ8Y+D7x`t(`P~I z#Pkr0nh}&vwNO@*vP1=sR@Hb>QBMypSVJAH5^QyL&%uE{`el~d%a~HKyH7++=GmQ< zm7N}^f4}r8s;Qxee=YlbrHHgjE`EG`tL(8T8FxjM?gN<&!NfS%VL97t^ayx3VGa5h z@}{M^^Za}WNeT86=NeJAimggIz5nmV^Z9<|Pah z(am0A_KQuA%=_}zcW^zKADOTR3CW<UCy-Z1s4?4y^&-FPTUnS8#7ES>@ z?jN1;a8TQH^b>6N&xyAu=IR%FpvZ}gDS5g|Wx8aUVQ$8YDTt-Q>W(L!Re12V3o@#$ z(z?gR&p)r9D(cdd7*B8qozv7=fY%NscW@$IH%olh(O4LNeEX#n-=IWMRT);UO|od*j9tT(A#0ruWAKErOWj6Q}|V4kkg z;toHt!~yfq{LR6pP45e6aQhb%^?>g?8vbCWrddq*I^6xRQAZ2?G3^`&==x+qQ_Le8 z!|iw7TIgZlQW-nPuv1*$K5i-CGRmzoghl0H7{_#JOyP>{alykiKJKpji-!=WGg^wdknT1S z-X*{sHyxu0t6n+yXN0=m6BhBm6IP_Q2(N1F;|~d%{r?dQ_LSvqBG6| z7vv-3XZUl=+z*@7JQLSLgsclCdfS*fTj(ZV*g>DuwxJYd$kyxgqiNlBt-Xav8Xz32 z_3jb49c?U=O3rwr4&9VBi4(WI%-R`>GAyL!&@G=o;4|lAvwD@YPTJ-l|`N zEB!&lAZ&UK&U~SP1+p0G|h* z>5R!HH9i8<0NUG6R)uQLF5R2v#vZOboM8Xbzz@DO2|hRLMZ!G#^>zlB617x4FbDTL znCv&X%!B)eLal_DKCM?rcYXz(8oU!gyhy@C@}>Hw5$Nips?J*-zE^<#-fBEo7lXAqzIX@qR)a{-ExuFi0#0a> zSa#6K8eF4Nt*rN62uOGqWF*C+ZTQHk(cy<8tYNLUpH~w?A;4XkX~5@Mp9-ivJQ6p* zV4m=X=6m-GZ&F}djq5QZ#Sxt=?VYZ!Tb;-f1ek8@##(MTTf%Ze!C~lSZD8XHRY$G7 zA>CWe58jLMD)gX|GsnucovxW`wM9f#-r8ZaB48F@uALxxmJ(RqmEOyOSM!{5s^Cl? zwer5dTyyKK$Q0qX{YVOfKXrERexpV`)yeW^k1MlGnZ473dZ4Oa<0k0zhe8;S>jgHD z2$*iWrn9o*yIjTxI_;O93}o_`J@1BrYO-!MniM?xBFbP`_EIZ~^HT7|Q2&N??54|o zVsR1h1@CNa&F7^T-#`DV7nKI%rSdQ*b-J+i#z$2ww{7n1(Pw(N;jnZpZ`QT2z9rMW zhr(i0I`1W7!ik&`(UELTU+jdCI?9NWm-A;Mjg}$PwLLsk;7%AdHbk?c$)a;cof5`F z33?Nvz!RyOWe<(GWBr*6FamK{| z*CgK66myih&C{v?L&DdARJLHdHpYAD`dZJzVE2h5!hIDjVHa{)W)OaN>2p~OMb3O? z-7gIGHr8=TjHA9?p$1szu0*j9@W)Xd+6OHmBpCeUlej*}!x!`izsJ!|sbO_;ccPRb z`m!E@A~+=~4^l(;Cg)ml;!FoJmkA*XWr>A3KWXzM=g|-wM^uoS*3CQx(J;R%ZA=wX zLo-<`WC_z4*@=vHuiM(v!0k!V;92gqr_@WQ;DSJ5sF1epm8+hN#G`yoPS<@Z!Xhn1T28EQBk0$IKMW>0u1xFvSF`?x3M z$d&GJo1*u9I5_6aovyW(RG%%whZ;CLk&@~2_X4(D!|IwfXPa^fUb*bHNm8kPj8TJ+ z96>^_VZ5IFX=3x$TCNXeLuk&KyJ-|Zr!nlVo9@dr^SX6BhlPOR0vv{O zgYUgFMN=C<9!_1aqQ1F1%WU%OESbEu&8Nd}q$a~;TgZvkwUyP9!_^#|>ki_>O%7EhmS;Xlb8PM>|#mZj+ikXL2ZX%cIr&Tf*x$BN7j zdn?R7(;8w|GmpbbE_ivm(INwI5kwuN?q_z@J#T^8D9MG1M!_j-aD=R&tve@KyD>dT zlZd$@UFM7?;HMJEec)IldYRhL)O{CgaVeNj7;H?sSo?-P;xO!@%tneJF2x$xTQVM< zP^jAULi@b-*#qj%`Gp^(JH?)_1E$33AQ8LeF*t6NotI^SSM41f5$|rkXS?7B=;t+3 z8-<>;*>tqsI!9EhqKctsuHP}6if9sl%d@$hTk+hp;v5V4 z^uu!7et?*L0v|g+{(m|R7I}|V$jzv5j5>W3vyR()2a@rU&(}>bs5;a zItGUZGKDwSPRRu~-rvPq1N8k#oJ(38_hJ8 z9(22jpz%Vls;;3o3C+iP6>phs&I$uCa90X>tbM>NsX9#@ufFvxjWb>!I{UhHjP~2~ zd%`{oC?w{7{54ccQ5(R)_bHIkwc+Zy0b8%z*a^n%#n$loy!^BKHQWkogCc>mbG&4m zK{8R>xcCAE9JC2_U})_aMc$v1;;`k0oNNbb9M0=xJy049shn+K0_p5_cw15pV| zq2SlA;#Qu-jvqhF0+zprGSA##|J1(lncIN~pj}miSce%OUAPQ}3{!vTHX~jHQ?7LR zR@r>Mb^TQ&COvlT77yUo&iHWzF^z<+WY$e4Y9W#hb|2_vyC+FKFof(F44VPL4S^MgF&=lP9dzG}g_A!b^VNni@I|rLgltAQ$91=J z*f5b-aGaVqN8dA)Rd~i+@Lk4bNc(-k+?V3y3p#;q*21^?`;EvHCDsT1N2({OMCEe% zB?LQn<&%by)j84MYBQe&n*?jy_QzZ4jShc#P1y=A1C20)Hwthh5xoo|EiJD}0RcHg z?5zkk2hfl$=V=!nisx=_b0O}Jo(cEiW+d3>5kse*Fa&z=c1=6vW5x6}2Ff=W019TU zoby=@N72+oFY?gbJ*?C9_blic5-u0$GBL}}kDfu2G%Q>iB=uus@6+Ong6hdsjn^6| zRtZ)Zm-o&hcdv`s7tri2ShWojS>I;ti`?79Rvd+hJ<}$g?ILl^DciA{TbKFNVNES5 z%?;Ij@~YG@oVM<2;f(j~b791)O{ijqgL36h&v&R~UO&|;q$mWQuvPf^LIj?m369o2 zt~>-iq0SYX>=t|L81w3EB*Rvi9UvYw)BLqlf-JNW<;dUN>m;csi^y@I0|AFFE;r{~ z?p`XL^${M@NOuk{4P|#BqFc})-{pEz&UQo7W0t(*5TQ#uDub!W3nDe)YGGfa=*`>P z5_9V#0f&nwv>yaA-8NT(fj{UjDSb)5^c5WwvVPW4eH$Cl)ldBK?!qnIB$;RC&QaJD zXW=g1q1vdMV8(6x#|Yg3BAP2qm$C!Pjo5<`Z=e;ZuOIPd??qwkW z&7ki&WBNIm{@q8e*;lCuR{G5k&5to?@R9X4TBk8?!w~uUrr>s_RCtc?d*7THPiw)? z@l~z2;)Q}PqXQ{{(}64Kl#bOrcLoS5Bu?k>0+znkD=)5a@nxKxx`$G)2Tg@p+=G;7 zj1t3;tx;8*Mag?aT#vfbh%(If+e7L*X~MH~-*GCp)K1A(j1c_i_$%`7T9KUcV?JA+ z$Wp;^>q7{sDCN;jMgHJ5hIiPuRENN~HQc)F^*vM*yMY=bITLf7(E94qMMvTKXr9x`oB;zZ&&M!zc3cRk0$+_{wTsw)bJBAo@q17UQ zsK)70zGOS$o46eeHzV~LXIW>hHcPk8&|YQh1rnupbbD~0rkpm+zw>U&rYd0zjJMAN zU}cgC(5)i!0d-R(v+fepJYwUMpvo7HYae3uH9*XAA*+0qxM#Uc^tcIUtq2o@x0g+HbihW{WWl(&MpB&&bCOWFZ_|~o?wNg5m+ST zx-XLyZPI10h+*4kp1iCWW`&J3>8L1NVzLIl-e#_I?Ty2$niYlnLb|a&2~1abVau}p zE--MUrZHZOExdOA6$j@a@0`npB8}h<7C`Vi)9u11l&vw>t*WOV<8HI1mO5x@3r9ro zzqv&7Ve;^o+?jVO;Gs`6d%jb5;thuyg7qcj+#Rggm~`oai2X@{L?T%X}769GdlJ z2V!(~e%;8U>WpC+)&}OBvv_xZ*MlEVMB>7pAn*h)ma61Kjm#Cga^5SAJD9N}>&^P~ zu_%(pAYvd;0xf%s%ibM4@TgM$eyJ*r;aawjIN1 zfU!l}`*bWEWf=dJPZBNeK1fR}SF6s`*KU)8^4l=L90x71%%}mx2#}5FC{OJ z)cN83525A=RbGXV*0(`y`D`|eektbypl9K>_?ND?Jo}=Y^7Fe+WyN3~JZ_hQaXxxY z)xDPkhn9{#ac!(uBRuS)_GlHx?cA4I_q?<=U&|E+P~dORjpcrJG6~rXn&kn{+7-*= zvUqmGNcXJ@)YOLMO+K&23QlBWV$Kk}mkgSBd^0*L&qDtQfTcdbc5;0EhG>x8CilzG z;JmRaT1k8vR32AR`8b46L_IrB00K8!)6vv>IM}LctjwhQ7(bxBdp)&cYWu}Py^0{y z4RvZobXdCnMv6zzTMcc%HLeH>`*20S_n@s;+LHeE;ZqAV0w*zp0Q!G|MExMmsYg-ALQ^&w8-^N3`zOzuy{eX0JSnY12svzee4n(X7k zWxBCs?kHdSqB^;_mxUI(KX}sliulMg4c&D$Ot$hlK!*4P;kW z@@^#?9mw4Z}FY?@pxJCPtbmi!W0aG$NV(4KcSU3+c;=PDMg^ zkn|cKnP8f@YnNv-GXs9K?yNA%twcB9nKm%8TFmrIyQn=Z0!-T%dP7@i^WyS`V(J!( z41wW?=|Ub52@=G;#Ui3KhotTbzZ4dd zCK+r^DVUTDKW0@XLdH%-OiPyYaq6)XtDNn8%MD@IXCtgjF5dfQGvnnW8=g-0J8y_psWx_XkgrIBOv% z8}0P_8NJ1uGzaq6ZPOGH$oskAA zKE^;ybt_Hi0m|jSvG?9lO*QZS=uRbp03iej5Fk_oB7`O&EunWqQ$)njK~ZT^6d|Ga zUIpm_78Df)MJ4pAf+C`#hF(MkK?I~+-uHapb-w4`bANyQ?p^2pbDx!!S@Z0f%skI} zX78Pu`Fv(AC$0dZ+B?fD&`NI<>_2qM@kyq^cVGFYTokHK$An9TJ=NFknq#ob!`vw` zw%AdX6IGup+Fxp_I{iA$Qrk5>1~I=OFSAA@%c#+pVbEv?pHqG2?GUd z`e+6Ki(#f09z+RCt2E-56&4h&$%=fo7{ApPUCa zZ>8S5lCZI|oAR|a@zFav^YjI3_gV0U>(*IrzeTqD452+)KmyB?NqMB;bJFUe=?+`l zI=959;*mVs_QM1GyC&NDzv|CBB}510zZAS>xp8yv8TWL}p}L2snUo6KqSdkd(GNek zTYofaoYzs1VxO)ZKYmgaz~A19LU34uzUS?o`Ixcy<8(m#xDS+WH|oNzPN?@k{`Ggn z^#t^wx#q*As7*Z&LwYI5iO25X^-sJam=0!STM$FmqD_g7Kc(r7HgH6Z1ShZ z#FDPr4a|5TqlAEi=u_XEZvBa&wnJYakJhW#o(}8nYR&Jc4T+81Kt>+Ut%A zGk-F2Nv#ZY8`D}{;&f|P_*smb1v|A1Cz0L3FIO%dJZT$k_x0S@)?}lbLvf94h!Y>Z z*`Z@<%F|Rr{zD%3OT+r@H{z6HQs=7*oR*ar5n>f1U{*jY6oQ(Exr)q<9=)E9(%0MB>llUqH)hk5;j>vc3s2UBd%y7rm&w;&$! z=cXR39IEHv83jvz8PFR3UH3WB|5GM7_@U*j<~8WXhc6~xvsZ9Isw_IaFXMU5FsnOc z2{9cTH|!U~$+IA(eL=%zS6NxibUV=K&v^}^uF$!&3j<`E5g1_dnHY3z7m?JzBlo_f36|kNeq!<`3Cy8KrG*~VYY8= zu>8M4w&d?h>wf1YE&jX~(YN#K%C(by+Z1xp>HEEV2tc8ul#+4&rktNj;n&wL9jA_* z;J7G6M5PH46D%$kpFjTfaInc<#^1{m|NnaA|FdOU{{Mn2`DY>cKk)H|?tkU~!D#$* z?H_!}Kl%SI{lD@D>_aI2hvnb#+Op?yANuc z{3~Ti!598Y_vOFi6Zo*NZxj0K>i>?ve(uBR{>8WO*H8R^BLD6$+HepTK)7_+z>uV z{T)aD=>Ll?|KTt7cYi7W-|^Qzu<3tT{@q`M{kH#C@$=7k`)ljTe{vA~Gp_zg|I+{m zV0=H*CiItY{+IswS1fw$m(w?~rTkO&FFuNYjjz*wzTQ5;{~zPsR`;1>{wMh;{_?}W zTm$?&sg1+_1!frNZ;abj2_#_2M^gYH4$wFn8^|1>qv-t&Z2)1L1w=8g-P{*&^@8Q- zXh10K&f#sqIWQzJC?Mp158jH`A*IG$BsWGyq^c(A=1ulFy|&F?3!h%}wY?Z_(|#ZH zGDefvLG6rv+D&5zK8G|pA%}m7+D)OVzcXsxg-eBxZi!UAaCA>o>ON!AP_rIserNFh`=HzSHqrTr;V0kXV z$?E5q75jvi$6VQKHpq~vJi90S_tf1}?kl~QID4;aY_hkw@b2AYvvao6wdtq-gw%m#H7OdtE| zaDwo0$;B!5`xt-kyG6R3yEmR=g)_N8wdWrLVniaphyY2lk55M-@9Nzv# zv9|XM{o}GnYu(sXs3JjEr3%)k6w97w$;bFrrjB^<;-$OPid>U(=3*~2AvGvpj&}s} z|5OC0v^T4r`7Ui+{xq6P9xc~X7!gvKA0~QA6mNgQ<$0FT`$|8X&mL!9AL$7Q(8LYs zkLyTlX2``$)idB%(LF>N6M4M)&gSRX?PUSEj0G!J&r*?$WQtX{+SD{hOPN^c*LUij znMz`wUp_lz+zYy$aNvXEY<=TRMU5Ng?>gKj?#Uu8z9$w3PEV^s?zz&o%O^XXlS|Ku z&u-R71iiGB)J?4s@iTb9d)c)AB5zWi!9(t*v~S*h>qqZu;ZwQ0l&w>;yJgY>Ev>FT zd9lxrFZZPBsm-^`2ZhgLuPBEYtE-&IMyMgWI5iqie7ag4rK8R#&~JJAYxt#${ljml zu~kZx8C9F0hLP#i;dY@?>(EE~N1Fp`UJZyI9r}E4A<&HRQbil$3!jhiL_B|R`QdeHW9QK{p93pJq6<7XiX{!vXgc8)Z$+pUpN!FF}bux7Fq!dA=9xH>9m0H}xOHDt!PsuH z$|N#8vEGSufC=c1;H-kBT{8BS2Mdj`Ti_<8_gBJFG(}GS75&EwmWvq z)fw)kx32Q_gsev?HYA6(y$nD2T&`j{?O?f|%;~~uj=?leAKtR;o_JbSh%P6$JKj_5 zaZQPGuZPjvqacE_<<(_u<#PpAarOCG*Weg%PVa}-(z%2;F1sBX*SxGK1l9RXMD_k$|OFy7DD3fEQynjNG-~C3SN{-ciev@vE#hMa_vAhBc8HGF7T*Q=A z)K8v#c}Iy7H*dJ2YXb|}43+LUIE6GzD^*7P$ z<_kJ$GOankQq5cIuBF{RPQ1I+ICEBP@^*A_V#6(Df0l}5*VVwS?-AJ^9uwd8if*LL z9+o-$tvfGnv|#m|PetmK{S%1NT$yC~jJO~Dj;uO=<;QBm&Yj4G%Q_V<>uVTI`LwRr zR)Z(XB(*Q?^i@BN)2>-a;kPQ(q#SF6{X6+ zY^Bj>*L+p(+1$V%Wbet4{wf{a0wH*=&3VR6{A|% zA3XeGey@~?N!40}PxG{~?Iw;N-OG8JlNPq_tvXfE^{YK(TvfQ}c)gqP<&^u14z+dJ z{l3_R8~InlFYtTT-^ZUz(>FPEh-bhq#pr{ZuhKc4I91M&Cr(j4kA&J5-+z);wrKHV z!P1D|FEqG0_%e_Bn#XWJr{d~3ubkg* z#b@m4vbqxPe%0q%ZnP6F)=x#|q(iHWivv}>^q=Xj8@FqD`da6oJaFoY+wh&&lP_<& z96vZNub?nS-llJ#2?^l~4LSDfqS~z|$y&l+pSZYt_jt~y^1ofV=At-cvLLII_VJeA zmmhaS325ZwX4JVD^pb-_c`vUz5?1?pg#IzBy{ z*m5~+u(wfP(aIKjnWM!0Y|HBKFU{u1XT?5!=_qcAXl?za{pmD|rhP2^LZ7UPHvj07 ztYGorZ$%c=!6M@2g?j@kAJvcd^3!PXDP`ZQVnNq9Q#IG63N^kXZgRFjOWsDJ`nN!p z+8f28kB_C)4>}tAgq+FAE>a{z6ug9`dH+2dO*-mmvJuL_H~;t=_kAmJVVRQ3>GBe;U>BrQ7|hRppyjQdZ0)dY z-qq4%or%`K2c(|Msm2Ia@1?lZ)69piiLr(8*J+yC4cFxTti8{_J8?juU{yW%NKv@(IfbYTO&J59hW?B@e;)tx zwa@yowp;l4Gv`l}d$9gZC@ed+8&cywWp+cJtt|dpMgY5s-fHmV;lhz8HsRNgD0k{~ zk5RvE@99v2H~m7CFr&jx6DA1K_ZoyyY?NVp_tPI*t)GQ|e_e^Y@btN^N;v7Vj;z(7 zmvL)GWYqJyDNikzS4ZEI63MyRR4G!3oOf4pgsOF2SkYKB$u?SZen-jgv~=PZZKSl$ zLuJBUdC6A;H4G5N$#ckNiyX$PHS86uYx3Uy0r|*bjurL%_3_WwD^H)3T>WR3vl3=D zcH?$q$&szgVu-ajzxQ_7v;~C|`9-tEZLS}srXK4}Wi;Zg?tX~8*ns%*WKpK}E@#JN z+M&-}Pc{>XcbnhjO|#G9zU4mtab0@!wH)$r%7Vz@p4AHKW-UJN4@{*?v0gDufAYY! zh`S|F7+4?d8V%NmQlejz>8$5yPz8pIv|4(tKMuM_5AM|~)+eN^UFDt)x}-gDr1@8Z zdQo1(o$06M1+3$2dev>}>S-Gm!NVtdc$HL=NzYNa4kOQ!-n$%gIb`XC7g5B&WZ$S_ zwmD!$NB7_e?oC-)o$b#?=j!lWf{6ZZ10(ZCeuHlc-<4Tg2rM0Y$^Sd(!=T&Ip5=nF zx9uH@e-Kjjx7xR_GCr8*sIIBI#%9L^ccUKcU5Wa)tM+(vtE6@-Ivri`;Rd3} zHn!$-qpy_q>~%sZf0{vOj^yXULx?*2%l?5V!BX8Top&iA)2yq6C-)Kltp5_Y(-zQW}!#^@T{m&yE7 zBjK#|a-$%Ed~)lQjeUni81b-|esjrKujNiLx?povB<=vVuC)GO{b0+amL@jitlzPAPl89BI4Yv(Z8;V9sq;*#YHMlqErmP5 z=6Qiv6R1aP&z$R04SuMmAi4W`uBzpHP)!;B$R`s-lH`!JIbT{eU+Ni!lpXyDNZE^t zHfJ^Q;^*UKZSfaKYf_=j+bNBk zzj`j2nLoppX?!(fLymN8;73^@#sD9el+kBudW+_&r*6x-CA)_Liay9L7 zVWkV!we7nVq9REmtt2~%{90_reBR)g*41a?(R^Yn!SA!ySJRH9VJ^)xcQsev?d|AR z*i7h_DGK}w#rE{Ms_GTSg?9$Y$!v$!xouO|l2>DIseRb>ImJ^WM5~(=vaGLo5`E@? znd*!^2IkRgBp!S6=6Y4-G1iPyHRFAdTY{pQ*zoxS9yvb@gEUjekuvG#SaE2Ut$N%V z#_5Kac`~lR#_c*-moLlg;F7X>xLh98_i1-W@w)lqcY*?ch0f(unc4 zS)qC`a8c>{dWYhPbeq%{S5Zg2i`lPUNO|O-=ltPI?Bn)4;X$?WR(-`lXw$pLufUc} z9NQQ<%)S8;eTT$m$FIab=GRG#ZA*Eqv3uUKB80;vt+>|1llIs&a`KsuiJSUaR#Jnb}BF0SP#z>jb=UoZg58_TeeFncp zI4dF_&5jtQMS%V22YOHMR^THi&JM8-?ux@tuS8;>^?sqSx}H62{(9m`$_(q9FDo3wG9Qkl9zcAv5X(JeT9S!3_SR7q2{7=r2_Hk`8Pj;w{)Uj zeOcX%&@F47Tm8MQQ}$|Zb@Q_B-^y*BsMa?D?$ZtI-LpE6ek)JzmPHsAW`>`e;-8vP z8bZ$n%!)1-M087cIIE*RHsIpVIiflqIm(B8#PPp%yMAqYS74&+i3_fqwy(#bQo2aS zEKhU<%RT0_v3$6gQhMB#FPm4Iezf>!Oz~9n49@yx33h2MC;319arxXIV_C=n)lk9aqMf)ET zhi7rTF^Q`_OQ()Eee2#?IvF|@xnAEAptt_wiP}2OF6G3HhiB`%3!QG7zq37C;AD~a z*XNdfvD4Wb4{x5Szdq{Z@Nm@jZ2e8AvkTw9e-8=$Pigw@@~QsG$(GA4|H#R|r@8)` z>RRFkfSoVm!2D(g35ZXcyCBA?S)ND1krQ^Va@p%ZZoz}#>w-`jG=hw0JcuWcNR-`z z8`B|}Oi>~^sBoB<>`te%6`4T=q|6S+b8_9L3i|=x+}wCc_x;f`G|x_pigRj)RYTZt zI6#Tuoeinhkay-M{S-g-nr`or?TK!I4bj5`xxO|_S4;kPg;WoqExo|ih#&zSPXeEF zrc^!duttOaxdsCWHd;m0+6r2zfI%ye+(<8_*i0wlVFUb$Dso%CXSPqq?Yeh~d2VHB zZQI~CGq~3ZIJQ5f#r=K+nQs-~>N5u7@n>!r!9yS{&?H4^9E?at&430-Exo6`Rl4)U zX^}UF7c#@4f{8L5WC2XMK_+4k8w$N2LE=qj zZ&rTeTB6L(^$;PN2;?M1$N1HH;0mO@0(ct^Ix6Spr_Q?q(vk>PCbspnxKIy|l}7o{ zOiU5go|#v~HI-}QUpKRYToF1PLnGB80B>c$Wz^WlP=yN-yh)S6^7%_2WV-IAELKPr z6=a}0ctM8@2_LQ+9_fBBPEVqO&y&+GqJ&Vh658L0pRH;u(p?&&?@@Nz~)ZKv2JGSA#PDp<_LaD6FM7659Z63 z%LrhKnlX2<<-dsypEC(NEc)dE$hZmnSXMECd0MqO*9vDs4xo?2ig@r1+ENnY{qB4i zdZIAVHJ%?GDn}@lIxyLYAefCV4Qwmvw8`QS<|#mtUwarh88WTVo64fki1Q$AL~msR zhT~+UN+Z454zwbr$>T1l7DS$P-6FHRdK?<;B&jeOv4eZ%GWJyb+PvCzE=hT!Z{QdJw_oD2mBK&)j22 zk~=wbNd_E=T72lE*}O{mHY#i$qWoM}hp&iJHGtr-Y=>@kFuy*)okJ=xUKz@D^C_EjB zUT)&1w>9WpnmBzLfb0LM;sqKI&PG*ePi`=pu+yRu1qFr0aI9rt%D@~0wdYBSzNu0Y zZ}8tuiESF#JcXg5!07N?Zxkf?I22#iSvk)R)^lSP889m-gnk-XbcTL06*>+B@Ba(e z5D;exF~d(&&VdfJxhZIaNTtv^?yojFQ!jLEkGyeZ_M;QQl}f>+GHQv5fn_>ieHc8> z7C6H1kCE@z57;3WR*(}qxLAV)0fq3p7`$0yn{suV5M5g&M~i9JfV*V~bD1OBW9R+H zkg1!>=r^{~Pbu*ONEWSgsPNqTVlgz_BXI!1&6WAHkP8x9PAxBMmjIFlrP&LS_i(o? zVXO}}H;Deu$_dOf@Tg+*txuokTNC_G|LKj?x%WJTf3>546?s{Z&Y`mBL)u-Z?79z} zmfIarDFQZt%_=^TM*uK#sMeG897}BU`kFiuy?M8xDo=Zgw*&h+T3Pq|d;XJfb8&!@ zNo~zVbW#{*f+!-que_x=3W4Bb%Y1`&#Bmx5z!td}9V~LA)K^8;NU7n)c`w5nK+uiK zGlT>AF;+fFSG3LOQ~YuLMbgs>0^BDs9w6hoGyyOiR=#u3Aq;*Y6ZlEwcJG5iioN8m z0Zw7?^5L+$U?PIcK=VQD_{^lZMYKE<@o+_-Sq?PnJ(6P@2!r$3VQsENZ#YlEE5w65 zL=ybQopc`yK$;t*`;-qUk@1t1Z)I3w?%zi`T0h~TO&G3{v>(z=n3*(1;!&bO`47$j z1hLO^#o;c0D5uvr5}z##U`MzkXCt+xCXNbBf3YrX)$aV_0>mTAD^G7-cHK4Y+@pYS z1V8)ic9~89m8ANp`dF714@toZAq8Vjqv;3XWJzdxulyN0Urrae3xF(27%&A52}11} zEG{re9RP*m(bDgx==+c+Q8G=9K7R%JmWQ{U$Ll?sKEp%f1Zli^U+zeSb5$HLh6qc7 z_aWz8Ba_RbwEZo4#tF*)2puJ%_c$Ky#6-#1H{x^?Ibdh6#vUX?9}toms42?bJY0w; zla&JW#UVA>C!BL^R0M{9ZHTWOH3EWC$V`|tdx+o*ZU&=Jbr~e6tgk4JxHmn_JMP?( zfWXlJJL)hcDO8z&(T0U#a9{Vb;hxAw6Wiz>s$$Q@C+Z4j=Lh@lcYu_- zJA5v)%C$5|en=Z26B5mJ{yd)KE^6xAnDF)k<8K4e<7{`XvMFfz)kUae+|HufaV7)O z=U?ZtVZ9wgc}}_DGbvF_&?Z5iWvom-gffglJH|j?UIX=^4Xp}6&xQSAE=&%BEM4+o zTh0LF_WLs+@$d;dW*5lBx*O3l!^T8PfeH!Wd9Mf`?*FbN9pHkHXxvZf#|expu*8~I zv!GGGhXAM-4g3ffk$KDBMVAJaBIC4B_dsCmCC?eFz>RNAx)fB`m_7hChRAaIdq{@` zhSSwf9wJC7MzA+)uzLG&u1yiK=fR^K4yUBKDQcxjW~x{(M9O`)d$b2+%trmgSD1cXrkt68+AR)lMR_gU|=@ zRgq(pgAFhB*V*s#%sQ3LQxqu)zQ51dNJ4uPlaJwEOFP4#vz3S|Hy*klP&(x5 z^7UEU(elg{Z5o+}O}~6&f}HqI8`!jlYKvx61hgD6Ucnm2f6);XX~T* zTGiafI|(SQ+&(I}bNLHoJ;QAK<4SaE^h(s=xQC--2Y+{TA3bb-vgnFRyLjyAJcf#B zZXq+3gI`CJ_X%N61b{-j5UWavwO;ruBv><+u?lBgWoPh`(ISu`Huxw6=0hR*$HFV| z5KJ80QJ8?By*)tY1pC98aqSbR8vwd|3_=zlgZ1HLNF7%6*Qu!+VxLB5;Enxu{=oS*wtz~Dtj@7smV{223NL4E9@mcEO;n$ zsrbQ_lR|%fByNsxF11?Jaf`tBF86JYA6)L}+5Se~%bCy(jostyi%Mna{_fXvZ{I58 z*=?p7fCCx<9J7pGMxDBpcngLJ{vt!iGh?Zu=v^?e@^ja)dvSlmMTqyB#-)K!_zG zqb=L$;!3lx_~$FCSelmoloO`(r(+qlw*CIz0uD}ztw-lW4d=yLCKUyDGw54ARqBJM zOM1B<-}~}%@~Gri#)+Xh7nPVL0fIoUm*O4RrhBK@@GyjcW#j^gcUnKw$Cvq9K{W1H z3EMdQ$8$WSS`5GgB@hv0!FS<2#5@dEQm$siS1A#)E)MnU1AD_@J^*B{05)m}S&IjZ zl!6H5q=wIk+cIzo1Oq}wTRDP4RSi)7Ix9(6Kt*lTL1uz8sWPe9n8^7AmS?-!Fgdx}&!qUooUQ@i5d?3tse>O`oVhlw>aRRi+)2uL1NCB(RK z1^ACHcmM$ieVCeUzEUy_GYo=4vD;cqHjBnm7&2qFU+m@dx~u*P;xJ1 zHT2wCR6f^)A|eg8E5lqD-xYOMbmW2pHY2uR__t?4zf9wIR=Sr5<$_R|3?;Z}JR=Cj zc*|Dm6DJ@LqdLSbPE|r0W_5xC7u@JBV8*Xl5QjYa9Ffo=<$njE@q#RM0&s#dAVjoI z5~d`Dqd7{PNbD3mORPO4il#tjQSzJ&JXnE`tVJ)LWy;X-Fbc4KfIe$-@8M8m8$`l~ zS;DxUAfl0)>nEoJlKq1;1U7L4p`smwAnc{C85VuODJ zFwuSDi7A48C(RPMOV#nGJvPLl$TCUUB~^reGA)^>RLty4{>B?%?*S!B7#*ov9i__i zr^hySlcOV2ihrZeVA8v6p-E|6or@PJsNNZd?VZ#O2he=Gm_gOuO_L#@(SnQ94X01{ z@KM0dxelscc0TQHmzh_zDu{rBF`Ns7!vQv@z{M0D=@e(AU7tS6J;gm=r&@*D_7W`W)9_xqIl_H%EkCN7sU!DqXw46AcGlPX2OeNh>?GhN9xRngpA;{^V4P$^^ znQG!(J~rGy2jJ+;1oAW&d&zP3+wxHBB(S%L7aVFxwl)O&xUp3+)dnHcnb7Z`HXgdq z+d}Gw2o$);p=RUS=_(I$q;NC@&D;>_k+873;Ak-F!MWCUm!yui%n3-P=cD!NgEk7G z{*R&M>Ilcxk2gfRq2;v022<)1M4GyD46YEnoa7}KXnZkXFqOU6pHKM8M)xDc-YIZN zeRtzpG@pmrC|OK9S@dR}0jDf&F&0#4IR|zv#8vre4H@?aP5aKQmpNF5U zn?7wYcSX<|5;@UxzwfEuEk+Vlh4)$j`C$J~G=w2bcy@d9hzorl1ZTGeY@shz&>$4K z3G2B3g5N+MafTtJ%s^%oR-r{McFZtlp`6a%S%i@qu{E=$8}_JbIm@CRLh>!MaYPB9 z#U;Ob0s!%UzI{~nH;91L=dU6&AWHl;f^w&MfB_=#N8{*201=(_wvIYYjy1B!V1Z%M z3$Ekr2H)t?Ia2MC_!xEpW?HQyX7flFedbG89ntO}qgfvY8V5j%nbf5Ta?Jm!)`K|F zGg$6dr0_X`NUC5a&%#&s@f?$)(kqN-m-BapHNR-sw(i7y;Y`jSR>Y?4uwq+LsO=uk z-z{0K9+i7ZJS?%}3V`rJhQ=O%17UVuJl35Z^cE@HA;`kh+eI@9IeK>3x-)$P}cw?G>wVtge#-UhkNfm&Y|;k zz`Pv~QDPhZgAa2UrQ(=qFCBSDEpThM-KWTz#bupN~(!G9+6HIC{!WFeHsr zvg}4JqaHi;zh3S3a| z9&&*WkqUvexA<=iwuxDMjAc<#=gmw&X-+9K@v6pBHxUsa)EliSMH?g(PDyt+Ln!iD z`MnyoGU<3hn`JsU79hh`kId7aV0;%2sEE@or0Cy|w$-38U@C9SCmhJt8ghb^08I@@ zA9Z0d)|^@G1>DYI6t#*-hs?5L{(8C-kyq~h7@aUlUeS55v%wK7);6qnm^~^ub5EA_ zMG6q5|EZ9jZ*pI(J-Y2%vWMf@J&G0JT**L^&(4>#lfy2=sDgD3xcu23owQO#u_@sE zlz0bK_m5BoL_ix*g%bX_*{?XFalmapc5EJ*Ea6W$LV%6pqj}M2I{*Vc91QoO09=63 zVV4A)#v7WGHuBiDk%~;nO%eF9D&G5^bz?^C;QQT~--#eo^zy=m%#JHZq%X%OpQQrNs z>H+G#6s*+EQwDw(WsGBN# zIc|E_4Cf0303qDnC1lBp8xw@+>;&wgG_if~CtRWsF{D~}5EkM?Iz(_xBPXU~Zzr=2 zH^$Kw%&M9x^w^=^VZ0{+LxyJeid;XaUOCM}j)9l$${ha0wyxxN{%IkM2$m0?1IkRR zE^^DbS@F^;*+>`W>W(yIxCTub$&-0#3>~&zZK3+hB-!%eO0#}4l#Y5g_CYReMI5gkg7T8GBLrxRBK7rKEj(<3KAz3OCFFpICouFvy47I z*CrjYg8emv-aMbRF$MhmsPeu_G5UENbI))(RBz}9{AbH1rPXYIi}qTE#iEJ(xT1;N zpHG<8Du(WEd)ks|_(Th=?O9<|r)o#*eJG(6>7=2g=K>U{^a|`29xi^zS$03#^bBGU zgmx#egBG%pFHqZB$wTbI8|Ra=SS zATKdN&TN#jTP*dWm9n(W?+33)F}7ZjBbuUHk;{5IUt4&#FoAoTLnbP1U-El1;)gOs zhkuwitZQU$xUaygc9~D7GhSKjd}D0RKz7$ye_rvdGq^E&ddyZZ5cW#!2=HC7f`hz$ zDzV*IYLjoYb34GwJLE;D0A@AOfj8eqF=qo^C?OBKodxtB-o-G*03o;rH5nyuuFB{t zpeG2H1LbX(4Dw+Umfc`$3=EPBw#_E!6E)~C8@KjN;|J#xA(d2rbe2W{DU*l%owqyo z1*$3%Og*7TU1hb6(OUW9nekhSc&Je~?c2C<&V5Aeh2w@mzxSCM(v9Ea8t_l=o z|NYaJzGhCuVqOIOK-R=SrrSK%ISuyc+Ic%Yo8IhblZh0*rE3{F{YyRH9lk^2Bha=x zxQe4+&|;jJ4Po3jou1()&_)kD@^^fsz096Q;?6hN_IZ_x)>8^UR|UMEa0t>j*CAEn zEJeqPQ_h#t1rV3cB!?#sq^_!q00V}$-S;yQhBjIEJv+(t!OTYc`Cpf0$p9j~xAQ5! zA#nhZ#dT(ZHF)nU%P`<|SW_cPoRU8sYIBdZq@Ea zmIP<9>4FeYBJo-~Di7e+r*9t2SUg1#Qh}hMQx=$)2!y>ktPe(q8*&gDtQ1l1ZRN;u zwnu;*m2UQTLvrp}s_7cXZU$Mi{oO{}h8rA*cvHAFxgh{{HWU|w;%ZZC66r!i0vV?C z=w&yIMlcv`oCr|BTMUXW_{KZfZVKebE8OOH*S3h@@*l>GomHz=h1Ih7$QDMFC3xeL zY*g&AEiq;ky3~;~GP-YvRTR2ho>lD)h3Di80t9fs*O_$7Y`SO1DnsOBC}dh|L!)NM`*|Edo*`pj2BnP#7;= z`NcKU4nbZ=HD^}4 zxEur@5n` zV5ro)M3`HBF9Z66&JIa1ZnJOBMhe8l`TC3FGWq4n%Djk>ZR6n-_iPs5GdfaY)*Ge2 zJ+JSU`a!U9PJEe{{`(d0=6T|V4}I6Cd#9P)YQ)NT7GJH%{yR%zn;5uPG@Su#mbGuq z3-3Dg?M)+gzNPBv%lAj|3&Y8xQnt?F0Ev68l5Tp%AC{>5tjA8SDsUYFeB!P=QqS&YHpk51hl*RB6h-|nNCS=NC zXu{<#I&F6eG%9sfAam~2Fr2llec}wXpQ;PzAis#~I?{efuIs8w?)kt#OKOa! zGS4Xmq1z$APqRRdVC7?N0l?9xeE^H9EqDeRNCG#h3gNCskim4&kYp;1T^vw}$J@eh zN@~OicfdMxheZnFi3^6(#*S#x*& zOlc64Tz2=^1OMg%qNKKt@)Z7%SByCBx|c-P6&fxOSvFbrS?lW31rLkaMt_?NTxeQQ zLOX79Gc(XJp+s9}0Uy>o$nLo^b~1fN(Qf;E`}}m65?_jwx9%8ZW2cI9a%BW?6DoSI zQ8U?Ri5ScR_bLM%Ak%S*0CW$M%I74-!imA^DF_6K2)uPooz0v2WgEmIh*}Cz8~X7j zZpeA3BoNjZWCSpY4cQEmI2O;x75z}Nkc31-k13mi!Oa9Du|z`LMU{+T=m+bOyXpOn6}!OaIb6 zRu$V8nL2cVJ`u&cWlh$7GJHXJkI3B-Nha>*X72K`Y6UjRV96X&_iDt3HxiDueG3ZT zJc*diA~Y$Fd1^%g7)h=3mRa3YCJtGc@)}8pN6Vi70f&n`*Dm(qoHy^Fq@0U)#0w0oU#OEI1d5Dj{-Cs z-1#9b9aIVl8<#`?VF>u#I0TO+9k8a^qPQ^o&pgOanidLHQFY^FA?+^iTHy32U@m^% zA6z#9?7wi8+m076$-u2Z5tioV4NE^KWmZL*ZV1#!>a#XwebRy3+?`-1@Pu)i-k4#O z56MDFBH7!fOHL#^q#qs<9t>Bio$$v`NSjo|$`Z~K+9@W=!Yn=V;LgDL$ssH=p_($| zs_l0E18745S;&Xd$7gy7no*%LbAz_G16A$7$sKI%2c^lLsZt=V2_H_2W2k|v2PH*a zJb2H`W|uBy(IfL1TF9_>xf0gtl=+{!RzE2_yq83bP5gs?C`&}*S?aL)vhs!tY|AG* z_wphV=a0j#kVc~Jui3Qi)TW9uPSchp`Aqvyt9FUGD%?q1`5DUfYfa>u9>68Ampp9J zHs8r~BiU@^KkL*U>{1c!?3dM*DDfL9v?VDQMDy^W7`VyT1RS>@JwHq6!*Lu_Ji+of zi|w8{p%*|QunP^BDzd=f29}(-1~IKG&z!=pQOL{DKnANG(O^GbsEUf8q(%GDxp3A= zv4}VR)J}bS(Q*jX%=|SOjq-xg&n0ONiD(xBoGP(s|3e!v)cm6n_;}A{_JL-ud*_t6 zIQ8QcGfyEvBLIB0vNJ$@E6_1Jz z(i~x&y=O}#TG}1p$XCKHhz7~L;ut7WloS=sTxd_9atcVvGJTzUCN4tkYWZCO@m^Gf z7M|~!#xzgHPV-QP(6%_R_qg2smEd+PPt*agRO5X2$)YDEL|r z;zyJ4pPA6zI%@nLO7Q)Zx|E(+I}Df%>eVC9_X-|4RvF)R+cNXy_OHbXg~MNNJxvDr z-*(L-6f&iOc&HTAIG)iLO`2X#4{D&{3_nhCpMv{PGEu;Z3SxOQ%Yli&lYH^P}QC(c1}PJl#U@{l2!yfFqu3= zY3OwvJv$!>Pi@QPIsDiZrOJ%v>J;KU>(dM~XL3(x;u9_{V>@!_XICP?^6F5R(g4O0 zCEmDHGSQXT1>`nbl4z)!)P_DIw{j4FaQU16+xCv?zc%fW@hN2xeT!&qI7>m-9FC;d z>@`nBogKN7`byZoKLR>WXjWNV3a%FD8Ix2Eb#MzsEB&ZOS?ZF<^A2CrF=5}5$_&MP z@4-y>V7`7d1b;cCu-roy)BIWHOO4WIwSaz=#fd4ci&<_?DGe1QY@0Z(=w+>61FD|m z_h)JSUqmSze@zwG6`mBp{Sa&{UOtJX*Tj%#@Op9d0U{;a7Geu;>%vH!(G)b!>77dZ zik9W39anfucPmj%MkGi`QfY8P8h~QJ7(9Mf+>NbHj%;*SWoS(QRo3iSKRYG=EQuW@ zBN_`x!)@W{SP@S6Lps0%;3#3L5F$nuBaRwWs)Mi((fw61mCz1BDG6RjA|;hD-Eb|# zi2sR%JHrqhtWns}-iZM-)83k@=%NN~dEHS|27tmoO;#&4wD#|fJ8?wml>xw2R$LDF zcb%f)mBp)Rn&Jn-#F-HZJRdti0$#qIcy5qJ#QA72lkYq87!@fV=h>%fMpwLlJ4u$H zp5|T$Y2=ALQVK;@S~W!ff|u>Zz56h=Sz z9XyqZ6o(BUHQU_%(k+pXUGWf+@-)K8ij*n(T(orC(lW;&DPkB|EoC}}zLn!CYx)!X z!r9DVX|{gs5rj#dG2T_ormpeP{4;q@7u_Rs{^ouV3b{0QVO|ok2fNfH{9R;IITBTO zfp=SJ2i)q`d(>j9sv#=zxiEM$-SCe)di@h=^%LRuM~;iz?aU0n6_e~~zgJdKtAg?3 zyV3Dwk$m!q^gWq1E1S$lERR8gSO=OrQ4pJ@uB4$-!0_v+ky2(d3-ceiRl5q}AP%-1 zT=5`cT}-N}0+g~56OVWFL4hIrQLX1j>c_#)S{b*Q=A7euoC~mJRR16By=hPr?bkQh zojnjB35x*&Bq2aRgs|_BgaBdFu&AJ@VHFV-5CsvnvjAZc0wRJUhDE^@aJz9OKtNDb z+)+^jqN0CqS=>+&hG*Un^UgChQ#Bvn`7~YCU#>pq^r^1uy3RS*IlmubWqTSx5FfmN z$0*p(0c)APe_gP0nwb!78M#hN?|dA@X#`I-_jnrfq|44z`oo**Rbdl3vmnDIP2uUT zd<$Uhctk{<1SuovRPCx?0=#K`V2qc*zfv>Z2j2=M&uQu+Yd-@usaMiLK6++DN|N5=x(DqB37Vp?r8e@lgxTQ?B{X%>FB9KH-Rce zoK>j$l2EH$uLrh5;{|Q=13C{I-{l25cG(hUS2Vm1Y}|_*+SVjm0Hi+4lRLMJWvPAk z37g{y777(*#*oRn>^EMcS?Ir@a4x#~?-7M9YksU17B}a_B6>bAkMF?nKJmnArV1;2 zt@yMNM~?TOz;u6{{SvSTDo~*E;}vS6Kqeyj7E9aIzgZvd*23tgaMc#86?p&dhR1zE z=2aP1SH~X~LXnB6acCqX5CSQhRe!zPI<#3W7>$4`r z7)&O^2}_sfyXjRD7ypnc*ocM8nG{w1l-Fh0@gO~|4QiSxR%1gMkk*(kHbYMIz6%d? zKOUck1PmMu?kkij7}l_%-ornh;}B5fQFhR2{Wq>BaSc^q3`kV#Qgwq4TGy>MsAxhc zqkWoK1Q7w5+;471@!N2(2ws^?`HWJT-4n z9~azKkkq`{csz#QMyZ2s2kjJeULXroq7uLmQ9yUgb^LV!(}>w1NOs2g>lrMz#$j3K z1+h9Rgto8^LrMWU`WPrnN;w9(c2@czUc+aBHq3)WelI=T%R9O`W- z12z;D0M$DI=zDBZTYRntSY;At+bMIATN;X$V*CSP9$))9!x-<=ZNDQ;s|J>BQCOol z=qi?d8N^?(D*O-2FD{%Sus`YLDtX1-aSrCKm0S(GYr3*4Tznrz?>{uAk!(Jkzn?Y6 z#!M(UE!g!sT2hnP8uyNZ5b*i|C@bY9D1oeY$uaf~b zt~$lO9qr)iV12gVfz*>f%ILBk+~fLn8jp)gs~>fDhQkHjnr|t@H*!Z5oB`3`R*QJd zO>7h<6DZN>CfQik)6|e5J2%i$YYcGeCs{g!1|{bSm=1TtCT4&{q(PP)99ecf^IJ~a z>c`jN0FjbIZhC6%w(OdNNM@^|~QZ{D| z|JaYTXvgIB=ErZDCsd?5jKgQeQz4cSu#inJ#?niP*+fSVJs_gmShtAgX#5Q@egzP2 zAy)9zKRq(GDMsB5PSLM`Nah_+uy}$u$ss1nyy*1_l&}9z-BohZ@}2?P)u42v)PvJ@ zjXN5m7&Ofg3cRDOha17~6Fr_moW&Bpi8i$?U$JdsUDLCbU!ouD%mpg~Xk%L*Dc*$o z*#Ex7G1%2o?sAPRf0I6kZJq???#+@#*K7}Juzs|U$~A??dtWY51UqfQIC+ONDQ*KF z>}OMiQ){iK!Chr4r0Pf=6oxO*fx}I4T3@nTG*1Zdfy4G>`c3wbhQSE%v6~6}3&YUa zNQobcOF3spwlUj%4i}+7Trq`vg#0MD?#vS`xJ;ftyzZUP_+838eCY69lWr4?8g<*ht3G2NW??mJ==}wz-uly8 zf%Al}>tdD3&;h}Q9q&79CJ3{fZ(`+cdVeQ!{+YQoI?nu+Qq}fX?v|=-)rY%OEqF!8 z^A9c_X~uP~>mH0^x;(qHQ*^&uwmZRY2Q=w=9lrMwu32+0YH#3WFFT`6IhZ152`mB| zYsD3OQ~0r>;M^`Kz_$Wyj>MUBb`3?+;?7a~;f1@PY%6+6xgumkjy(Px{9yTQuS`BbZN~_3%L;9x;-m;w(L>VgV{O_8G}Uam~P2b&v-Bx^#v=?>HkDi8&1Q;$bkcjy)v051b%x1St$^e*H?Pw z?`9E9qWMwwos20=+eF#d|Hk1#y$W_eS`49dTkiYOiRtjG)-Z4Virl z@MdiWM#BaL)!$+!+EvLYEBD8RTmvEaFYefS11s2vk) zRI1I{H_q*L6Bu8_;aD2WH8TsQ;mv?*);cw<1pk|ALA(ckl_xG9%c$P7vN#X-6V>4t zfih)x=y`L01t*BJgO=rp8wjVQNhtr5bV^!=+@r+A6+GVno-EhsH~ts3oWsSO!wcI& zr%1ya$Liy(OXN;=G(G+zM|47NV;Zs_(K1{-ml;3NBbmd0N}e$`%U#`=C02rz30!vh z@uL8HL3hMR74>r9zTwOOabF~~VD;f>@NefO!-@~G-}JXW%s+Le>1CRSeol*QZq)b9 z>;Mi>j95~v_RxBMMpOSWACsMb!e5g1c?eSbjrQbFPU*chbL=}o7LoRUbM61?BZ?h^ z7^QIE^v4{F*HHZrgG6eLzr1?lvqoWhs%K&&u?*pPT(-izi1Q)MP9-8cPSJo zLw9zFSRfA@=n6L$L=IvQXQt9fxOBcm&90+u0S-?NV$F7=-Lt*NB$9Bb~kr8I^#6|Vg;iYj#bcJ%gVpT@LseVZNM#!R&;J*n)VMuR< z`G?;v*0U^G6dQkYe~&y9o+UuOf$@dXnOt3@SsR{ilnnqGRW$jssf@93`5~B(&btbN z3Bb2d8Jf+7jb$NsxHL?v<+^~qZuVujuV`WBmVV_lv=XNr zOb;%+wdZ+mCR2A0P7&_yE(&_^2Ff3La)N<7-;{%uCjF~*Pke?(i<%Q35PJB&ZgWg1 z5|vv99anDHt%*8c!+8;iLI4Dq1A`$tW8Ur9cP3=09)G7nR*Iq3Q}8g@Ej){Y*1$x{ zs!Jkl)a_=xG~Wv^Vd1R$fo7nyb>?p^jM4d;oy~OLnznke7;s5C0SlWY|pa(+_B{RnN#vH~j&~yhn-I2>yEP zO^N*U^4E6Lyz*yQfF-MK+1^B^C>UtUddcPuOU2-yS4cDq8^FOiTe2;j9l-zq7{apZ-oY5)x5n8AeLSAvYuxiMkavOb%rB&4h^Cw3f{xVV(utgI^c{iPV5eDZ0 zqY52bgg|@=Dh9svlI7Y_0VW$I!EM|esWLm}0bAEpfXR{EF67hOM1?w6xF;k1C(-*n zV&+=13q6=yU3?d}eY7$glY1QSn}IdSVd@mfuCuvI;ia0}2cPgFJ#nkN$~hEFf3%HR zIHsQ&=wrI$(YdgIU-m|KQ_F|{l z3HEAGssewLwI@+K>BER!$r>%dK&i{NUxO{1q5WvXL-kMak%9@kw8*|2Tvci0Rlj>A zXXQFm(d`lOxES-B;;Z{1zx@HK12vn~l{J8doHleD%@~mDM(EejvZcGfsD9YJ%rknJ z+^S=)hR5zt=!p+MNsklmDg*NMs;#;u-_jU|OrsaxeulwCFtcl+L!K-${}N$`>B^)daMb2^Y~gcCNA|d!Z;f!u=jD4X zF&Q;#Ld5!A zR`VY7k30z9-HIc~VMY5UidVOZMt>O=btA8C6Nz4+zgMFGN9o}3w0bY*ON`Rv9dgz; z#O2E3*JB)Pf_w0ibr|~N0_OBJu0wU z-xZ6gEjb`*{1l}NObMBmUtpdgzJ5((!|(Po)(xpCBI1xg9>wopn+knHw0qf=q%)eR zn`M+*p*Ex#a1C@lRl10rXM#B}W~1nFjO1FCuKQB(kcwTxw26w1B>_&`FJxyaPev)d z4HM&i3)}KK*%{a6%fiKHO-`s`)oi*V+{2f~#)!1CB)}2y!YSQIlUyZqXJSwWp>|pN z8AlbeQ~KVc z#h4?yAH_5=T~-`KoiIabTW{|RsBWLANOY-~Gqd}KYCd5;wADbC1%X?D`zZZt4Z(9r zqYvdN(Vz@dAdRt?z${5>4DX6(E%ww7Romeca6q;lV1sgQAwfEDVVp`>2$JY1fs_Q{ z;rh9_5>2?Z3l0I**w5T#@nzvJw5jK2TNxT#(`SliLB$i2uM&DrQ>_gngol; z-q=T_B;YN1e7u1h(I7&n7&FFzC&;M?Gp?<6g}R&o=s`AQin`mu(-mBOzU4z5O0|Vv zugpO`U!n85CLM#(=c*BPB>v}1q^=Iy6lr4;J|xb7t6bUSLBf;nX_OOXMq}u<7tZ%w zzZ$UgZ`v7i&e=ZCerTe3_0?kXV#rAnY>Rz3t{f(Or1Vx!rKvapZTQ>sutSpT`re77 z{s?}*mQmk_Xji4phFv>gmH2qVfZ2*26}nn(W_t&I0)fx>_a4|8)Ii1^9bga25qns7 zc|a24jDE1;k$T_f09D`V2%LqjSgXmFMsSi-Sp&~o9E=D)!M%_Ak|y}hES~?}_W)ls zUge7)tHc`^@-v)2g#;+?hfiN@*LSbfz*mG_a04nSSKi_21<69%B&Mf4^9a{&o`nw0 z*@FYRZ|gNISC&VHETu0-j6T(@q{quEJ<|UJEsVpP<|}n13J%FB;OoV@&=CLm*&fo( z_6u9{+?=%6+Jv@Dh`tDD?+cJD>hB;RlAzFIQEr;0VVop@YhgW~1zc*D=Z`&bH}UQi zbFZ?2Y3Y;oL}r<<9M8u`Cy@aWXguT>*q$za=o93X0=|+Pt_hGOSFlsufS5z`?hxasm~ennDhXoluhwVB%yElw@b- z?D2fU*+u1IK;s<~_jbH+!*OFXe}dP+K$JD1a~v)29^b<~vihc2e9$sj4Tg)3OX}x^ zh!o4aJ~~`*Hbb?oU{1&ePx*anOlUf=-?G@>i7?QF|9!=vfb9Aq%_7gO5-@8Az#v`IMzm8 z?LF8Z4)OT!)Eqm2sp)1SBDL~)*KFeuQzo-GkJx{coR>z{YQg26V+qv(t$kK(mVb>= ziI18F9J`AaJT!4940CH5x~=%2rn6{ZYSBQ#|6xF+#|OBGt`D{3ICuj-uA0r{F00IQ zP4*T(cp@OszPi%93s2XwtZoa@ohKb7C=()*W7IcLq3AZ{AFRgqd__Ui82WW+SNY+Q z5z4#OsqP$JOWr~kiLi8~ba%f$j|rpqn=ApvtTfK7gTTH^{p^rNeoMC}LYp2tOOEg3 zbm{Bt=igz)#j^5Y#D-X#g5Vay%`SYHv>-u5#~q6wjAFQo8we@-JZoG(pT%(;e3yoG z>UJT~)TU)2GRf%n5`A^#M!q9w861-c1NHB}*GGyW_`Y4Nc_u{+gpKvOe=1y$T2AD#z+)RUl=LzzJtm!+ZgF7~{)_4&6jk3Cr+<4A0rd zx7WLWRtRvxEj~T)YQnZPbWsN&xCz2~ux52kak^##Dny=?grhMjYS}I6Q+qWG{LASI zyZeA}R03NeWxK-d(dK*y2NZ#XM|wVX&F(~LH1de5>Wz>hjU%*BQfk2(xVBUTusT{; z5DB|p2x{%Tm9Xa9VvIW|G$=Foz3(~p1=-Zl3TsOKVQUt+6{D4hG}3&bVLzbLYf+*< zDU^J?KJ{`{y^ehVd$VoBu7bb#xEP8(K!UR+v+P=27eyA8?G8<-8ue~&e6j_FWBHCb zlb{P2B9o^UVsLld7Cj(1aueST`8txNT{OhGDo%pURv9?2m#P_*J` zpM)t9rmrms6!YQW(q{h9*#wS7$n45%dTV&O17_kM?xb0L=5C9}sld$w3@k)ouV-T! zmh#r;XkH!4$zi8Sdmm#Ksd29^wXJGzr*snW`BG=vC+pK;;|gh}w3FaIbH_?R!MdtcMTCBbLsR8m}8j|&_J8o>P zjx-esg)2Uhs-zqmx`L^?@f+##$TWV88IqQ&Mo5C`s5#4d0~G$mR?{d#JpFp*zfxE*)J9Lc=szB|bMflb2b9fDnx(Gv+;_ux{|dU^L5sB18E0A-Wbz;`RB7_Rmf~xG_ev>;`H*@set?Hp3NLCrkWH@LRSHJ!7UZDjM(wv|^SpQNCVpCqvMg1D9 zTP3-!zex>eaeKSmI0@%nQ9<;~yaruRi8+jKvV61zPTPpB%DC>)s96oBr{gJ{@?VB= zcua-)El!Q(9RDJ1kv3Udt|5+A}n^~LO&I_mO^&r-01i~n%e-Bli!bysqVTx4`U_tvtcs^>>7*q zsi>O=FoS0KFytNnRua+uyk%v$OyhpwIH7GTDxH*{>iUUuX7l(GIN9rX$+7DVI>vgV zBTRZ`LImSD#jx%`zp3CAZj6b0p7uB|>RG=n$~7lQ-)EPfrKffGl~~?H{|SB&+u75i zJV0?(Ul~g}KiZ<+ey)52F6~>T{oa|P@a!nb_8!?#d1!s5C^dWe8Z9Ryih^7oGOkcj z=ycTJJ8ArenSbF?3Ne~#j}vysGcaP>^1@$o4O5&zu1$$W zY7g($w4-UQFmZMC@|zGoh_lC5Q!BCKUInD`f@Ur9;X2wkMY3HDVi12DF;n+ecpy{H zm&K}z@6BDxph zf|OHfO~!Ml#4MYehzevm%8sliZ_7a1Q$pB<7_#{vd<5Q9wiqP; zdW=S|CkUz#KL2VgooCMBOU-4Zm&+}u{hkSQq@+c@+SPJfr$zLW|E01nv$iq0_bp%; zm`EzbNWqt=KjLCpUW^sOx2yugNRPs6AaK&|!%B>%c%xQ6RXBhKY9E*rbkr3k|DTKT zkwDK>_oH8LKlZNP{tchGLm?K9ln_q+1IVwlB^NyLe*5f8DifwPA8?uvCr`t(t!b=> z3}9#Bt_(v$W`P2+~?#^^h~p82G#NJ*R8 zXiJxaZ^b{~@U1v%<+QP8e1EciNn4~TIfF91%a5#?Uyz2g`_h7m)$I-wZ_KA}`$%1{ z&tbgvBKOH2B#>fH`n!&^+P)UKl5tw2q_~kVLbsKq5p^FI`1+ymb$bcFZ?|g+wQ+#i z9lk%UUnuyXo9;?7?zU5QU@E&yT75KU7na{L&f8uA+k>m44r79zGMy2JWuV`X2+!sg z2hgkW1sx{xD0KQT7 zYevlOD-cJ7kYzEBkJf01C+ zcE7sA3!A?#?3-V>ztH(>!DnIN{lc5Mg(D05zAkK=U%2~Ui!T5F;{S^eVz{sXTjVUV z|B-+I<}}hS0~ia|ohRp}(r&JxTm=BY5Z8u#qo_G`$_fKiXF6}cmG1VPl6FTwD`1I5 zR+h!rgdHX3VgGD8y{7E3xTJ)>|J141d%%AJTmqIX*}e$$_o)^gf9Ao7{e}Mf&HY!d ze9~gI$XVgEc!w=+ThxaCV!@D~YPh=f7$i0?O+G>ojVG>Lxj&^Lh5XM7h4!>>=Ee9+ z+yA}PmwGsRo=z^idb?WagS~xM@9ITd;N-V|4r*l0SzSN1T*Z0i{1MBI@l)cpUz$I)b-J>~`P!zJ*Uew;*|6}Pbmiyv zk2^;6oquFs-ux}Lbd~f<%(yLZ;E=_bgg}goi{nJb6a|%!e6QP@uD0w}yv0X9Vc#oV@^Y~! z%yq9($l*0xe%*eu&Ddl?jZ*P)W0{8Ssb<(U;|NX6A!Frm{PYp&{<=q29;4L%*vA0S z$-kmgPs)on##L0jh-V!WKAMbvcFDE(9yKH5ICm6G90v&rHQTRzz_ruO~k-~7kF zj%y|DdKWe7w&D$Xw@%MNGtjSva7VukxwG@*&d)su%6CtXy#IRf?k=sf z=3dzAwWsXw_Wz7twGUbICzaj&Gc5UMYvHyJ7p4ZW9cT2c1+L9h{6@2(&`UpIz3s4% zF{k&|T{^qx@3))JLQB60c7HSkaQhykw#;XKCSp3;3I??nuq#hpnJW^_=KTKT(`hM5 z!xw04{Cbf;o0Y$y+2d5Uw)erOoQ%u6G!7rR{`>TUXrI*e2D2x7kA%q?q@I5}o|YuG z{wiFW7`!c(+rz9KrQC}kcRP^(eZJ~TfAx#G^5`r7?l>2-C-c~f{piVO&L5v*GHU0y zG$)ja@{o&2(YqMOgT7+rz~_3cebog&rw+M|-yzRW9fOVTJ{Pc_(27(&D1pB-xNdWJ zbKB>OO+UHc17-Ys-~aHH-ro=67IOCHZoO~4u;xSc?sQz{m+^t`&mO3Nmp93s`mfGa zP6(>h29exBFfj6dZ*|7>Gr+qn#84+v%jkV!?&-o~wntlOAI{rOE?f_boHu08oijC$ z2=1&KjBwf9{?+fh(@o=gc5kI2{I1(0o6jc{u{AA;M+eWB)`E`G`H*M70k-FrLUa=jNcRgS-*Xs6G-JumT%)8I?AKc3I zKK`EFdk%UZwvUB2o1 zhWz{)13!Uk?Dq~4U|c2I;&T8PVT$Y6>J!H{^r&j(U(^I zz8!Bl+tjFNd90s)5(?PME>ja>&#j97QSguV_oXg1Ff9xwXT1z6(st=q_5r+`R1w6~ zYamf6c+yZK`7)`=FMq`RYvr@`ZC@^5S$60d?IGIEdP1mfaT`ChNvc zmwyjjtd7aqSALUtb6bKR#p$*wr7bnJGGk4jLW;F_>Ahe=*S5&e9bu$(`F-@ccRmC4 z)8Nwc5{pmAcO3H?#O*#6r_3>H6R4XfLxMXMm=8%F8c?-$FDdT+KU%-DmVF`C6dfq# z)|us>U(40f{Xu`fZs*lc)~8a6uPt9m=_p#W4K>VPopg)RN%Zw>-J0@B?cHmq$o&2| z>$T&qd8EhhWsf3je9r$tq;ju1(k^!Rr8Oe5zPNkIasTRHw^@G7LcZ9n zwm)tOJZY^+)2Qyu^~uN+e^3|wb#+r~9-Wq4!+ssFtR6X;X%p#L`*H{J-!pC3HkLpY zW?yc_-rK`2^4mI5qPZo{&D`%+ZjJyYhos#6gZEB8{AZQSwA8CR)bqpV-BGcYk;^u` zL5BZ{3gDa?vAC#aK|1OZ+jxAb_`YA6)2>N7)P&s6M%_Q|`O3n3rbNA>DW+*hJJb5; z+Q~}}pHzpe?BW4+De0a6*ooaQS|?`RR^aO1)NZxzNWZ_t(_%R`Hr_qhE?9nYYHft? zUc#5chgXVQ3HQR5X&ii37O{dwzPdCoP%^3)%at74vrz1z%>4VMZEAbD*VBLY4f<17 z37Xc5;wh7_CzEK>FYKZ#7~2r*L%+^VS3WGZ#x0^Y=l@9?f6iRiaU~&)X5FnY3n)F> zv(DNl!RG4tN>-QV?nv|J8eh38M`frdb7^JmgNzhgJuaXB^Gws%;lkW6`6Go%(us+f z7{>6Gy=6&S2LrM98JZ0#JMo3zZQ^fcbTyol9N6xp1+YpD*2C41I$bsKc1@~>lb4=< z=p?+F<5^2zO{R_TPZ`Ov;T`>yC>^8S@22Lu|C@9`yTHX(H#vDf%=$Bbu} zusv$+T6$lI{}%icx;DzJeo9(wnRRU_u|8!iE7$Jr!C3oL`NWC8-P*hN<~m-v-+IKY zckf;A`N5x*8{+~ynAXq`&vZ-@$p5ulOX^6IHt@PD+>Tva*8~?# zb?3EXtM)$hnasNO@ia!B_TnG(&Y^$2i0%WIeoj2PRgN24dhcUX)fjz8u}bg3#@UxI zYHVFikL<2YCvcynZR)(gijesw|K9$*hoC_s!ZBX^(?v((CYvdhLQDS9cd$ z_wF~(KJUI-UAoWxguG+}cIUwCbW9z&ynP;V zk&GMaJz+Yl7Bu$N$0+2wAt@=q@rFw~#qHIbCmsFmXWG^|$B?epeAQhHv;W66Y{h3a z3FF>{p-4vlm8v?Pf}xMzP8Z)MQePYof?L~5F|IRzs)rgQW<%O?PEj#%{{V{lKtbe!CE0!smBu=mVOM=YFEz`jCinf8b2v z!n*A>D|WhMcv+1SDJDYfkxBE*c#w~QQC`?6tKPip zs~f_I9=d|*I&17SVC2H_vi+hD^O4mA%p;;y%GJj1GKxZyk zA-uj6+!hQU6n~M*V)9M1RXjR|Co?OX#paszU1m`N52`3+KR{Li6eCpnrB!UR>d&|9 zJ!o{Bw;0bfA_xobe`q?V3_>bO*OM8#lrsSe$w zVBu;^37AF#FtMVA*ChzxR>Q((?v%-V62Lvt2@}XM0)seRQO4+Hv0fdb+VQe#{LW0g z->*;w)PSM*DmeI|OE}g!okLj0do|zGzjw$ z&;S^N(zpl@`tR=v-+)ydC?I$53zu{pOk>fN)|TiB;CNnAmMIGF?72LdoKwGv>wbC3 ze|Z@0ZKJoMuL<7Hv(N&ZHQH&O z=TsJY2Y)~pL*O9=4qXHW&Im9!l~YTzF)dE;n9{(rG$tDlA@C|LB9{dqSu8YeNS{#+ zIZ&Xk7lS~gbi1V*;W&;HXnhYD7eC*m(qR3D5a~&er80ZWGP`sb#bnSIFvBWVH34UU zCQtlDdUuz#pBw35He#|nHiv=u#=3KhY-6e>+1V|pKD9;0v99Zlkq=H|UgyQ;@G+UJ zo?GN3aMTF4c%ZBWUJXw!sUQ$;QzGNW5G>>B{V2~sm)~Yapt*AroF=h=DezUGW9ltid^$oVD8caTgsp-j2NQ_^aFc9F}7EDHp?3 zk7Yi)o&?YqVdx;h7z3uTs1P|BB@pA}s9qrTE{0!b03wERTQeaKKXr`VJOpr>5plpq zEQ)S@L}yvgWdS~F{U9;;0d6=7(V{bR%YI<{Kgmd{-ijH3QyxOMGDUm{3c2G4fX9L1 z!vRMcog^erP1hV5+KIB%alF%*Tr@b^-M~bK*f>?Bz08-V!(y=3J=b8yCMJ-rR=G_%s&`CC1+ttc~qMf$rN z4Fcfoq=q&?dw2w3;y_Tf1}8kKzW(V190%sIAYT$_!~wAI?56$}7i%R9!-R*hF+9^q z2Ne&2csIp*#A0#)5-x2|3;I01)=u|fCh0x=1}ginbC;gIWl~$^Sp+&nMi=4-^6&r+ z9`oDllJ|j101>LPGl4B`EWRf{CZ{PQd5>aaJBsjia z?}lCt-XZGtDlz3j&(QqO?`goHdJ2(k7+A&!NT0*T?kBmfd<3LhPoTLKFQgDLT_Dk}81xQ=3Qvr3ID zM)x~NXEk-JG#xBhHW+#Rtw9}ief>?ryXki2a!UpztC9z&SdYj7&gE=mb;T0Vm zfICV*P$+;3J)_h*Sqb$Os+ZFef8bA@j}Ovj=|upvkYj61mYzS{;1RS}R%sxq?Z#Xs z8UEM%@{=bhgp~|4>|>GStb^4QLgT#~21{wjQ20#-z<{uP&t(VA8B+*w zJmAD`0YgcH=^{d4cOj=Cj7*`+fv}{sUW5#UE#=$6O0XRb7r@ZFl!Wxp**@3-kh0`z zuE~UMUfW^?hP9kXvDqR=TIIWAX`_%rCKV63GmnMQo(?5MyVC|_#1E`YMjwzEx25T; z2XHO^tMMB7Sc@;7V2r^Px=PQ$zv`RCX@piCH?6=~mJ_7-)8K983yh48D9Q5pxBmZZ zIN^6HmhP_yIS);ltdYmH9H0<2F?4ZvZ62xHl2(o5z}90e(-D&}v%=dUGCZ`kr*4CJq|d@_)-=>!hC zmt4IJW4C4Y4<0;hp(=fHP!}{0P&uO*x*(maP(x%!&&QxzI2cznMxa%Oe_9BtkXi&n z3#F_m3b6xFpmp%`)d+J>2CNje_#_NRU?waM0`fEj&t_iu*6v(!7_?~6c$EyReRR|d zUZz0Hmq61#W0!Xm{i~)$j=2gX{uqJHqN!DPOgozVhhuAMy#x;c>0iCiz>j4UDHZ;Q zV)#P}Y_hM*VQ711qQ^DEFW5D% zTYiaYdOA$+9$=)NaqDrABfW;LZiA$1-8`+tl&jOFmWTJ@S;lq?UIkvSXF>*M%lAB} zW|SM#(HOKhOoo&4Jeg5@?qJ;A3*05LsPzcykBLZihw}!BppM5|W=RN?s{?hk_pI`a zApVy#1hW$b|GP7gjP3hBoN4;MIKygp&?NZ@qM#6=XpJUCH!@ zZN3Ye!e=^-aezZtXO;qhYFjITsLF*9KoNt9A_0$KtbM)HQ=G#tTtxF~>zNC?Y+$TJ zsF(w}v!E;iWJrM)tDk&h&>W1}y@?KcqtYZ-Epqb=Z^kGBh$arXK}$d^2$Xj{@|2Jt zRf97p>TfwC36^C{DR@=_+UglOzcmL;?{ab^ZYQd#;~@d}x4>xO#<6(#8Z5l-I`C5) z5LyckzJKJ|O^#U9_Y}b{8vW?kqTbR;N=8uF6+JzLl>sCrw5l6gQmlC-U7Pfl6wwp4 z3l1hGD#r{*ggxO}s#Ht>U_TUh4YseAsoi7Oi=(<|cqKiO zRXksK@_wJ^aHl>-idv{b196UsY&Z}!KryLB0)b%bA+Sikaa^?6~)(Zs=k#SA(R`WsRKgdw*o0wiP- zViDmD9S$SAfhE4;M+rsbLL`0y4#j5T0+4>dmTdAb1>ZpCUw(Lned9{k!jNUz6zlZC zmkN+GAY@Yj%sn~&yotF!P4WG7regZfGjCb%)7OwUef~c-3y=Kf(2W3}+PBX(d^4yT zK0M!AboDLJMuyG-e58E!VPbfy!kCAZph&L2&Om0K)=O;>!1|@M7 zc9yEED45Cs+{nO0Re`4{EWI|e=iwEJn*x{N8G6>LW!+uZ>Q02iV6m+8b{_Q-V_s>0 z#}U`dU>DM`y#%dxH&&=25`J?;uGboJ-d(gBn=zM$WerK#I~$~B|re7aQ&o@Rf0H1;oStro8K1+3MedjN|M zc(wpZgLw^`g%3zQrtPhKZbPjII%@=`(n}hT1jsoG9IB%d_P7}EWGOO|fXysGKTHVI zR8#q}|6_+qTyKHEF~@)Tpdz&P!Sb+_738T3RkF+4Iz)R{;J0nwh5Z|Ox1=QP#-_N? zQ{!Y-BwMvFX%?}GM2M$s%l=Mf#gHk)$>c-J>ger?#rgD}qS@!44#fHbH1dlyN=<+B z|D+uv`QHe8@2IA>w_S9um6c=#0we(;^dtm`h!Bbp6k!3OiW2PIP()Nzu%aTYK!8xB ziHeFCii%|`maXV^ODF;=DkyHTZ9`G9VL(JcAUD77-0$9V?iu&qk^g4qTjm%mS#!Sg zd7meIglE)!Nh{#kGxS(`Wo;y<;3cYFQ@ceh@}>Y*Z!HCp z3iGL`mQU+GQdV(HfMC@*;GoSCT2B>#O$PJ38}aj4w|#mLAnp&CSTxVFQ?(}H`e-H` z52XPr0g=rZZ1ifql@BXn5`>aq8ZRR^C}gmt@EGusgc8y-E0+6`7f&;2ACe)Ws~?*j z`%!3h2vFGU3V@Xw4mLFHKPEzM$lkVsV<>9R%|-AcqsmI*}h~PisP-CAHREho5d9<01h}Pfr+Az-Q zQwNnN^QaeXWvP3JN)`|n3H;8d2Saw+Jp%#{1=yhEeznr{*R ziX!fzVV=DBtDV*)f{>dkFi3<^MYb8 z5f7f)KV1yTH~9e7pN|ymt|L#GKMsFyaTeC(25TAz#0Y$VCTGC6OUR~$K?cTr2}^9o zRCuARqX)qaedYb!3y85C1`DMh_(dfkPp-5P3g*GICZ*6TkVa5(swrX#YG<*+V7d_* zNJmZ%>U}kX-Dan@Qc306*NuDO#?hz}X!$AGvpt=G;&Sk+RR1G8{R6<;@4BcJ%}(O8 zR#4|MQb z3iz(Uv(kH4{`F8d8P*ckNQF`X!g4$`?@&apw$6{t2vXz$PQbfxOG$eBlTEWBMdpG| zj0!Y;F8Um^zl81HRiS2p_G(A7cg+s8s~_dKFr{`+V{)~rz4=Sp;{iUWw5QHt$lDw! zG?G510G~gxu1C3U5?1J%IJyFiV7;CF<}%v@X21hDcn=xI5o#8Rk;Nmlhr;a@CoWRh_6Hdg z{vo@ESKAE& z)wB7EF#I4~DKE-gVN<04HhMl9v9Va`xbSKvxxQrFNgwAZA>K2?<+K50Rz-b6_P;Smqxv* zC}iDc(x{4M1i*Dt|AjZC@I~oG>P!~ME}Z(Crhc0_)j86aGWW44UI_z(V*D?wRX$*` z$n<#vvA{rhVmgdkLiWVd(J;!!-co4oZ)x$qB>Q>}mP=iJQHYn&Yf7P$9DL_Kf>yJ6 z>Q^j?+e9{!Fzca660IqaLO1y=xJrhtI?Z_@U?hv!*Rt=`78?`KAz96vrnT{_H6km* z2~!y{~;4L`s#j#aDeQ)0rr6XJ6hM1fL< zeQXK!qN0^XAgDG7hy~()>I0#aDIq|&j8gkKE0we_c@y8W2A}~VMy{#*h5i1Q(2_D~ z1wyxbZ<{ro5@-&nd)HckUyUCv25)YGbwd~BF@uB}c`juXaW30mnSCh6Pd1CCxRHFv zS)tUg@9wc5SwKQmLJtD`3s%86^i=W~V2n@~KFZ`aN8!8Sw*Ou~1VPWc4qn#xIf z9F1MmF!-O+Kf5J0*U1l1p6?W}C3G+tY0&MPia&0SHwf?DJ&{(#{I75-1Ci_#B;(NH z$CcXs`Tt8eXoJs?!S%eMAv51!a>a+vEftoZ>S)rbf7luojPYf9GWA#1;O4CeMk-9% z)){hKojDCyDtEq>t@6;pMKvF44q3}wXR5jfA32mF20ht<3KTAp(!>uVk6AEUD0T?S z#VJefQC(pg8+X!z)NR+EdO?PqJ;`6IG7+qcj0`v4!r!`)A1rC_Ql^LLEQxf-5!W+Pb;3UwV*?Z$Y>gNIf1Fb#*ZG5@kW|z zON^A$wNUHBxel5Q2&aXj@G;KjJNMZbcuuDgbma1d3q1mHfBZo%@-KZeaaUD*P^(G+ z2n$ee>YjL>O@Nco8SqX2ZV2}zvyoz8H|I;nNR9d z?idFF0rTtk+aX(5E7YbijK4DQ8th!@lnVZ!1gve;nRgd>p4$^*qXik#L~{_H-aSP# z8)AuJN*90)u$8cu6gQkM|2?WN(W1ALBo4PLH;PXxE4jYni{;hWk)vHSA;A-3aMkHY zCnQ&RtyFsbA`rWP^-6LG(3cA*@z!?c!9=$$3FP9PqPRN~!c+(<1})qwba&_1y zN)%^}>fKT1=lc7^$j~wv+W~jzmI`GG*U$HmCQ2eVL-U7AdgjGD>KrzTX3?0N7cMU7 zwsu20Ko~&k)!o)9VtaO4Glb97zJI0n+-d4w5>Uu~C+dy9AOm^xkQV^1GPT)N0Ghtq_e`>o;5`>^TXju)z zhX|qkmpSVh`6errTFEafnOjQp%e&p);5Bohs&HKAlknow_}K4eR$J4r2Gd?i9dQt$ z%Ly+@-i}jj_FoC@3Ach`_kjZ4`nf)u2!HR$t)2G3#wS+)E`X&TGGWB{eaObXYCQD;b5b;NR>^tLtHzw-~S=TA+0MuY6tl&@f z0sYXC$YV{F0Zj4xx`9tElm2~koTgo=YkIGqQ#GZ;l)wKydVFFqv+MG|sp{WpzyF%^ zCF(Ld^?T`LuL}e;`=)swBTb7JxGHuN8mMMLWrkcXLL*jv;9&n4$dMAr;RE(qBFmuH zu%OsJ`~jO~hm16oYSb!=&eUbFJ zRuPC-I$G9Z)v?1;6rs57tgWD-z=$4i^Z6-&OtA5W-?PlR; zFO!R&6$HGkPn=Di6DtJU_3@ueT1NOQdgA{_fVJ4ERN0V^Hi;<1oMIN>UJG()aQyI~ zc)Q6{NorSrlp|}g>pov3l}8m?vA7jy^BbyV9>d=5ReV*T2~6^^drfSiWu{?V^iZYk zb&D?j^SUJF^%>VNmdBAyN7_`gx-nQ%8=ag^zTH@B8 zcV>T!t4`LDBQB%pE|ygz2f9TEXAs={B%X!NLQa+H`i9%3b}>B^huEkaTzrR->s3Rz z9&CDiKxff)DsA=5_6^o+%b%}K+CCfgUyMXV^&w#znMhPL6v7`@=(gL;^r@q?Uj%Lr zo{yre*H%6^^V;!0W?F5>$$pwX2A&ee$rBfIEiYiC^w_QBa#=RZUMu)C?+6976~3;h zbo_rLO8MjKcD^qJYov{G6@Yvs@!--Oqn`cnCekKiNd151X|SyQP5OrNZP&qS>umM1 z!mG(jpQ$hFM#mRF>SzpT_@|p1CI9S$PyKI7+`uO)@it_|Ak99O6<`xtF7j zmEUHv`Q4SURs0&Nl=+zH0jG>v!mzuBp=W_*LZktIdBzIvYw>zuejK=6L28Nq&QAVV zL!f`ahuZ>_U+T0SnHus&kgcQwQ0mpw#8Uw3O0$TYhy-<+)5vPsHV|s zjWIZWJP5>v;n#vd){&?$Gufcq$3LLww&oEjbsFLjdiwEJVA8u`I!sTt<2%2&=Yw|} z1T0I^f;%7=AvxzjgH8m;My8>auQ7$`8w_pQMYY=1|oKT-0xUzLN7s>t>9*pBk=8 z6xFKsG(v`C1lKLWe~+8 z=I|jj*hd*mgRdb5^zjTuDaovvX1v6IdhGnPv^l~IgM!)C8ejwTrm@v_sFQzS-Tq1ghOQtf}A~z7C;qW9 zK0Z@}pr|cb36~#ge5~x9(dbA0 zS7*IOFK>wOR+XnrzC5*$4P-vR6T!#F0bY)L*aK!b&58-C$SBP>^S-9Heu;)^xc#>S zLUqE~gxfK|7*4iKUVniE`N^;Y%o-U?DzLPKjCKUWK7A>vP2C*aHcj^3GLGX2IO`Z7 zULQ}8gAPCwZ>b}CItbU^q6lKlvBJGdOuKLw{Y`n8n1A9vfMWvzIg zl)a&&z&BQD77eN%H}YSL9&QNahx_2#>hJhh2&MwmI72T;4p&M7g)ipjJoN5yb zKjqe}Z)NW0>VL)jYac<3E`8z&frQbgvC~g|?rbxD#RNZdH16Lu9l@$)*d_AZ#*~zf zQ?JJ?m-SmxyS*K(rrN?2?NVrvl6_!o4FNZyE0#I|YO0Z}$YA+Fk1KDSe`{Tcl#?KR zDl%XU8!({tFZX!Nr;5Y51$Qz??^~7^sBw6hNQ-r+=DJgBuF;gFyZelqzI4!nO3aRv zi1nCG5jE_Qm`Ve2W?O?gh(}Hf0Y#(W`sJmkD*_$LJX6l%i~Nd|D_n3N!jK8%Kv(G_cJxRYp;)rTkoe<}Kd=pz)~7KK zcc{{gR!1r0o54Nf7u`YIqucJ`cIAb|Ak(H2r}=61-6N;Av%5!LPs5N>He>oxFqGSE z+U+y#aI7lFXmx~=|Nmpz{Om?!det%i`lPE&r8KK1kygntnNa#KV+u;0cA*o-UU zR=R>>&hV6$g~x(| zcH8;dX{j3Lp?cE)&H30-TH_{)IFke2i7*RBXIB*fR(aTQLlSW#I#^2^;| z_^0ghNoqO)KyromWq2g}X51dA_Pw{hy;|9|u=c6rX<05Qb%CC&a#rZlAojEzD3u{+ z5*Fi_?btG#E0@cP5M4*YrcA)0s?2F!TR1owxj2Zu)zVhzHM-vQr?5Y~t};2k18hT7 z8jV9;R^abj2263l6en@NKOM9 z$JD8Mz>BVGq+xmjNMN~I*@3`p=J*Z@t?g>XSZl$p&W4Z-L1vB87jDl?OAMQUI6*Zh zM!SizqE%Ary_ZEDo*%47-XFoUuzv)I`sUo>RcfF)oIh>2`$3}iWSluW_93P2{z;|D z7zNnI$K?AJ*x7BUI$?XV%BQFns^dt zZAf#mp#-AA6x4Njk)=ICmsg}kOukd}8B_2Q(xgbM? z{R(S;*~=F*7mJk#n!o`C7?rcWSKvu)$W<3U$~ialG1R1EkZ8tQqEDk{7R-|xmve`L zfh+0MyhHy^PKU`DeB8m*fNx;!rCq;7p!o3O#fI$CG$HpXwe++r_B&wTXFa@YKZ#bQiz-n4Ij ze%}*8V7Efhrv-24V{?%Sm-+|(P$CnlY0M{-XXi-vr8|BKOpHUC|EK}tY24lL*B)GF zYbGgg`SI!ync`5Igj&W8kL6%e%vjee($)IBMCR$C6~t0lrF&;!k0Zc+nMth|B+{Z3 zG-g`0#L7R@B(`M8*fY37EFiMD%}iE2NpB3EeqFc1NjJf*TuEAu%@UN_$Q zd%v7_D81I?Mn|T9axB!da<82}M`U4EypcrX!F@J;57(nij8sj4qf_yF{y4)-IV%|S z-7rq~-XDHc0Arruw{7Z*6NNsscupXNRzVOfwYZtLUPm782K_4$-$Rl1iGZ(KQ0Z(Ek*t$1&)*$YD zAobd zq-$5=ji5k4I|bK1if>On8*4$o-(=8(lqj)7h%MbhS_$NU898cDZ^b%R;>*{yB@~I| zp+el!qbYz99m@+bOisF%5N`xB@|kclpB{2J%!*5S!xHgW&P|XBRh%W{>MxR6iBr=^ zBULjfxKV9L;rL`^*E@&d%O}2WTN^$#VEg;a8r4T#SRQcwrn{NR)HE<34O(`7yE5fr zm6hMtT_++}tlBmD<8V&{`D&DIS0k#h z7AMf;eLc!mui@rGt;1<`pIFyuSLB~AL;9iwt2s1}7?jDh0k{yGoxLOH9L-}iO_#4t z*FJ8+VF4HE^u}DR`&m|ba@{wcP;m`HbML-3UC}#Np3CtAQJ*SfurtIcGiM`vLP|Rn z6A|h2Y#qTy-V?B!wY^PrZ$#qc6A|PZxXBZqiJh4^PS0q}8QT>x{vK+Gj4XI}S$pcc zo$Bu33i-s~vG*4om6x|Q)s>08eviD30FNPs0lpvr-^4o|N+x5JMZJ2f2PPi{G#VI7 zVh~sCCY@ynAKwwQCIB)-p+6JwaUam^qhR=^_SjOI79pG-$GZAEGP_g`>hNSyT0;y5 zEn3E^mKh<`WnFEHP$r91 zO*T+km=*kT0v6%;s#LH$;cFU;X9jAEW%sN=mbE&msO)zsEqzVV*3U$g(~9OUvq;jN zl4Vr?$OIEl;P!_HUPbx3?xwu0c4r0QNq)v-Cus-Qz~%$)P*6~yAS)r&c0}>l7-mq8 z_EOSCzZ+!}sYP;u?()V)m33YNb-B`YBxan8g?(liK@V$CiX83R|DaF5d-?0;@!z^r zgUHmQy?|q)o>xDLS7Kw|QySK(B^STue#_ogc zHDfzuK(68M-H$1#{*RKH@bYwgvoXfeRb8UvG$?2AujR_FlztWm=t-^LC}L%oJrNaG zNAN4*TcJxC5bLuuP61vn9ZMoFE_k$ zJv&Xo&v*wjvmA;En@{tUs7-Ob4?-fwN(Z}XbbhgRenGvh=g_+Nl=r63S8PqgYZ~9s zjif(>{I?$8KF;W|{Lb`NwXbYaWB7pqov9xuf1h)jd_kSK5Y=#M@{g}|fpflu0wBmY z4!&8-nPG=&Wd9u2S?=gCL<|g|bT5KIE}`{fUuw6zR+|;EQ=8C9(%DHqC*EHM2}aj4 zU2c0=XhocjI6PBUe51jeU)D87^H%jnS#)$qJ5?5gK+Co@Ee zWqzLu)O}l)SyaYAGxrnBdIQJm=?Naoz;`%sLa?a|F-iV)n44KJbc(aC@zF;_mvo zvvildx zypsmJs373mwYSJO(M9qrnUzK?@-HNmd!4DrPHJ05G+a-J^#QXuT8dk$xPI1=Kv*^@ z2p3Rwnt;7VI?RHYY|z^QBE3Ugp;~@EcxAd1Eg{x34`27YB3eYpItmmJ1soE9V}Ug_ zP`AgNq?TU@pOr?p&|!V36!!96+oK!jR-2aB<)4}SY@0f%%8NPh`RK%Z`d7X}3za?^;_8u4abJD>5!+;PUYU&E_Za8r)L&0l%u%xvSP?-T} z*ECYW4iAMx3@{)in$x4VA=f)j=K&`zC0@YV)BB=NgnnB@^##5(g(2(Rr0yG{Zvv*wZq6bWZELq~RwydB0qq^yzskQp}3Cd6w7RP4iOe0i5(HrTYP_lmWLhe`shC z_(8jMUy6`Jr%tg%r9yoMW{8RqiLNJ9MKr4k^?`l`LqA(7*TQIBmr+IPn}QH9oNhlV z62>b`6E#Tb&0wVV?2Hma2Hn@@@sC8f&G@c7Z4;aBBj8||Nlkgr#>d2N{}t8y&59~* z_K(8iP$kXVNMT$6v4TP1MMSQT?2=hiX?L5a#hV2QrK`=J4CbC}CuEcV#t@75bNXXE zrG??#9ti2CTt8^*YMi#QcnCdu*nft3yL(^V&5eHj+j#fuN`YbqP!#oDp3{AIPZTMC? z)OQcxR1db{E3F6%6=_YRp-ds2#Zp*4L7v>K6onEpH6`H;bl%Pksf9tWY}XMRfX-9w zJJ`E496gie7YstZ5+|Ci4(qeZ6`WGBeKu>C4e`&)z-bSRI`2k{;O;-}pjnOV?T}0V zraUKc2Dv*jw0nl;svim~Z06u@<*7L)i)n z;moePBT`In0LQP%=8p2_17=d%9+g~omVq1nJh2`P5Cm>zU#Eub6wi&M+}WZn78h-N zQoIR@ZtgcXG!8Cgt|+WG=@fMds|^q0S)9m)zU}Mcpa3`8~5V z<$LMxZl9^%z}%D>x|8)8lb;`scdi;Z>Nv40=xG#q&~s`GR*ZK@&U-~?OGj&Ky8931 z6Z#qLA60v{w(GgHXPz46XDXP@?x*u82$`Gf$dC6n5amO}bH5JJoA%J!H&Em4N8y?X zzD?JjtmVEGN8ODXH<8v2;QDt99bb{G@jm!ScDd(WAjF3mKm)t^n4pu`$xt}>wEVIK z6qk34i}GUhc9do{5u0dq5h)EVr6u^$npvD9qN_)hd>+nI1+_VA&^q;!8du*1p{==k zKcG(si^jDH>cP~KQ?tmE-*nXj?8!f%lo(=CkNd>;Q_n6oo!JRNAh%)Wtf^gD<>Ej5 zut$TPw(m;zt%|)_KY#IRPGctQg8KJ{z3I^FjWrgNU+MbXgwmB*uzL_!Bq)p?}`qCQW3Aw<>WcvE)A^+vkS1wClg$A6$ln5|ji~U=3 z^;HPJy|kt0|8yF}Y9$k!UgG2wOI^I&>h41Tjy0=`GN;7k9~W$AjJ?+X2BnA>CCmQq zb+HF5AO0e}lbStO3s*3vbP0+tkrbiT4Eb!h`2Ifka}LN?{>l$Ch}2f-vk)5aC7xTR zr641n8Z|2+p;Zgsbq}iECdPkn{HtL4c?#&GCYRdH{Y8^Xm<2AeP0P%@0>hiix;(k5 z0OrBVlUC1xBY-#_)>WgT)TZ)05i&tq$S>*J$s&|owTcZ;ZSHpA$P;<2YL(BdS+j#V z3aNDhdphl!>35%O1E0wIVnTR?w^sc+i_Uiwps86sgr#hz0{yFMjWQ;!zb?16?%$eo z2Ld$gwXnQs%Q;A{H+AS_%Xz4s@8vPDAz0c|B0cmjjF`$|Wy=ci`foT6F%`P=MuOZe zVecjo$yaDM0yhiEc#)#093-IyPI%JhD|;6^bo858iI&EbecUqYEUm6G?rZ7QYpv*s z;bM;Tn!n$7CE3k$-FcGEn^EgikUX7IE5JMz=|2|T5>{@`m0oQ)*hG`#s5t3rV`5fx zdZzgB#dyD`^v`{HZ3^=;nMBltnwUoG>_8)wUAI9?U#d{}!dP-O)fD-3vuyrdnicxqV#4<$zO+PKIFwc&<<9pjkb)%nh0Zzr6derHU%YxQxDkyld9Jr z8(%>me-SbD<>>EKA6rtAmV=8nnEy<`l-1R2JlI=Q+VqL2;S8TONjdUc<*U#jC}Fd> z2WDExG6v0^L(@m?)8W|&{MipOiUoVqpr3`1J)&q#UGRIg6xt^Q_Qp`6HYC$aFn5}F zWV*+`_hDv|KZ$>YBl7EHg}$fPhNbq4whuq}n4t9ot}Xxu{b2Xaz#rj@Sa*X+W@iap zkhqTD?bT^e;wUs3C<9sIO{O=q_Fnb7->#ja47xZo_8x(Yp3Bfpzy5V~U-kAKn|Qtf2*M_hpeGmA zodZe5>h(I{uljW__2c=v?^*4Txv9++f!p)GoSrDEe+YmK8u3R|@loGz&5d7a3gBKq zyh?;CaD_(r<$)I37lnLv5g&7{(FQ&A+Ut6Ub2Ch|^uBdd6jnHxH~2HKzSpZAx7Lwe zKJLUPMvq-FX(Bseb>_9M$E`strD`Ga|y zn_?$CI2^1ZX;o`5Pelp_N>u6)ltS#7&gu_L+>zKxELg6PPsXQ-Y>pku2EG=!YNU(% z>;=Z!WM)A!0k?3g&d_z`1qG@-FpFiROzw@fFtvc9pi!2y@(BsD@b78h8338V13BaC zC6Yw6Be82EUMM$6SEp6AXb}XbUYl z_WGE4q3Lh@zCMnf!$k1(L;I+4}njiEVhL_NQSFs&7hZ~PXg zt5Zt+JAMU6jY;amYYv1)%kn-3QpU6iWa2HN4j8}R*xY621=8+FPigs zkQFQ4uv(Sln63S{^C2`^(S*J=x@joD(PrmB;c5HZnLfXE^2@HrCq5|f zUWqVM*V+aGKB>lBlI*6vcnIUK<%lUzP7G#ju-MQr{)K^T1;-`kp0*Po> zpQ=#1b%%EXkkYM^n7nLYc={gJ^eDb(?#*!ht85G81Tzq{!jPTQ_LruCZ5cma{TFdd67^@Zu)iBMmTBM>(y zKP0x=uEi#Yf*XudMn7KY-*$3z!=BF@o(p!(%`nkZt}_BPt0rdXGvW9WZ?U=TaBDAd%6M_fBA~ zfk=;v(=qEtN%9RLP1Ccek+hKJely;m;0Lj11CDz2$DJgtI0EduUR~R8Zzucu2PQ$3 zuL2XIV}P*%US4eRt=9D#8Vzw842|~>E`-=7|7~Rkny7<$Vfw#(BIP-oHttVY^q(&y zw^@aAz^S0C+-_OeE$?O9>2q71s|~cRsLW4DnrZULrJ{Id&WhrLM9qs_0xjSX;?dOD z-D009r}oR4U6$+GEgg^%vN`y3aO%C$_(#)E50fjs?*au!{g4!OJvr!1g(ZHGJoaK1 zun%{8zX1R6iLduK0Wz@6(`!6hEPmIKe`POzlLj)1UQ)L+SrRs%q$B21gVbs?7)i@^EtXOgJFavTwY z0I#=-OoJ~OhwiXlG^0GFF(ftUT%~;>ecvlgm|fO3GepjSu}$pbFpHc` z^EquODc{#*zQ>2VH_6)D4n&17OkG5+Arn2fi*-je{nlU5$+z|VA;RY4>Tx_}Mo5aP zqHcx4@=fMdyd?&>Jy2gAy7})jz%R(X@BHb^UEcizord)tpZvXM<@rPBu3p*0A`tws zKgR4@P%361S1m7?{l|5dmw`k_15!ddO;H7h#w8e3JJPuuGD)32)FxdRf$o@V_)%o( zrAR>mMaG7XKq}Y7Xi{ysfTGa;8y{!qpYf~X8WV%lPDdw&v^+DRCI~*@jie69OpCk= zD_uh~_L_9O=4~x-rYD7>4;F%drk4~i|M=j|z03Xu*-=>X$URkJVt}&s#s+*v;i`2z z&@T`|Vx>OFz@PJKat&}YgXMTnGB8h4B8IMdfPdwYVB7+oy^L;qGceRCN&CjUY7=NR z0=FAdWSI%C{5ldUjONGX$JvNo6nRjecebwWz7AwR!s=t;HY8Q@FV`?iIAgY$yxlq# zQbs8u?;ct{DV2=jPv8^Xo-9Acz2bZpjrNG^xr1~}uUn$!6u-a8z)!s#XA%?<3{lNJ z_`5WtnYt&7x};cpbe*8BaXh% zel#`mWVpIc88!>F85FM%fS?b9ey(%x=Y5)&dd{tN7Cm%;X?(4OBw&LakU^1Oou=7O zVJ}bt5q|hl(^fBI2;bETIqm7$mral;{M?uO3%}-{CybUms|Im9Mq#+=V0U!vq<==P^ z%g9*}Cb|azfG`&FL~;21`D{B-^=;FO(0%T`{@q7rY~8xmVIWUeck77BW^TA%WWeIj zh5MAd51K4l(x)7X`Tq9@X4JtOYs`a>yKQ_bT;i|!{D<57e0BDDyYm`u?`i%A&;N(r zJNt`jf-JDKy3aJPG>^)?5qzkL8(b00*s=3K(ge}$omtzT|E%0G@OSgdEr+TqzYJ)P z!UK8^4kPvT|L!pVdVv3{Z2x7Cr+G$@CRbJN6LR_AnIk~2_4nADZqwJ74ljqNZGW|C zRrckj&QIs3E`>40S%Hw~(i64jnriU!zj4W759)&QAG?Q{-@Wtd(>AS-%?rXk7|2^! z;#PTls(e1-fXkcE^Pm3Ea*KX8Yt}5IDIMe{F4=SXn_XgObF0Mo+mF^SySf^gnx=Fcdp%9XEF^C% z#aYoAJ%qa`3S5oce|vJrwxws+&!k=QygpWAx46KvD(0MV6}!6C&pjmN?ujHP-(T~z zPQEgKdN6VCKoRRNL-MyXiNfQ!o}tame-wWC3)Tvgx-zDfmbT7Y^`~3Uu?LyY4Lvta zZKgc$A#GwABIVsSQ`t=oKGSx-oV}xQ^0aO8=^Go0fW+7WT4`&w-{I#%LHe_AFFKI} z?iHU)o$qsA&zG3*mJ0f$Hyy$o+!Oj$49=^N$pgaa2`1LJ2P{6weSBltkIW+9AKq5J zZJ{*aa(g-ct*_E6Adu#4e)`FuvG)pB8ys{unYp2L(CENMV%bO)W7UjleQ}H9Jc{!V z_$zvkTp0?M?!9ddY15W8b!7hd=h^s6)<^y>&wWh?(zl=0P7}@x`-a{1(2Cs^vt|0# zNi9FFW69R?*wjb4D}xd)>1NK++i=6&uybIeHJ`w6-6dlPI zjD}qi>HoaE`0s(v0*7e>JIBrkGHW?pHr)W~;4Yspn&0)$_D$tl=Pj&HjsE2CN?Sii zeQV{9l*2z*?>lA%y}8uxKfm>M6ltC%uur7bFScz(1eboZcn;X5E7b*0qFzn%7;F1i ztmCImeY|L$<4HM8u(RS~O6M_?mql0GPt2crrn2N|`^wjCcJFTeQcm}}s`DwzN9>!E zP%6HE`bdBgr9_k^A^VU14B0Z^9!%jv8|l}JmMwPjI9vV0V)gp1CXZLy@gxgHg-z+7 zA3VHi_V7T^rqkKhTmJld@qqB2<4<8|>0WbtW|;*e@iz<#xeDRfjxsO}FFfXl=+ArXJNuz8j`?RR{=r;X7b)JN$x}IR6 z%sW~=ckVyD@Th+CuPr)tF< z3W~LLWai&R^Qt~texLiZrP#*ds46GkBzl$em!bUZ*Cgi^{^12)RRJ%T{}R;R6|B}& zX2javX^cc>?;mTfop3RHXTGRGicRzVLFO&z+^TY%1l^nP3q1};4%A*CJ>Ic?USMdo zRs@~*e=zo*QB7=N-0z-Yl1xH?NeB>-l7s-Eh@lz~VG>#>QpAFa8j1=Q42TLIl%aPB zMMXpmMMXsqV8b4Yh>D6GJ?e>wi1lDS=&{_q?|avM?_KxHn-5u8S!?g?J;~nL^F06O z_h(rf+x7Yh=3Pur_i2p$cQ5K?f3Cy-<@r1N{SD>Q^-(d^Pss?(xRnrcXwA?AU3PVK zZPTru3p))~G$(sQH*spCx>k^BmA&Vv0vn&ZhrNBM|nVl59+veCV85A`{{k7JxI(n7}GDg$nV_c-QE=+^Ta zgBc|&4@P@2GiA5GNRPksXx7Pp8}r`bGv(1IePu$v_UzZ6Psjf`AHG!Da-l$BoAMxS zo=bUAn(jRMCqJKSe`|T)46ywLQx7YKSSxh@-8Xb_^wXu|j7ZI2Qux9>tM+)Oei2UM zq!KK~X#Kp?>9!T`YNKssMYB5-ex6gPq3`eS8ItWPXSTS_(o|JRIh@JtX)&Mwtn=EI z&Fc_qg7qg>56yQR7bO|})vvqEmRq01+D7zpQ4M60qj}1S259<-W=H3|%ieh)=uvC` z_JuRIjfAMHrgK(Q^ld4KIoA8*WBwM;)9uFIm(q)tE?>{cye;aO`oneehq$=;YiLUu zWZJ=#C!1(4n>*;pN481RkNynjydm95#XgMTNl7?_7L^LIUo_jzLfHFamzD&IBs?%~02H``g=|8Jhhy)$Dy|E>Ixdh=V+r|*w{ zhV4X``r$@R8rVfPwl z#-w~3Tvi2*|LB_6xa|}FKdZj>b%C3|3+FB}d--E?O{HlaJ*R4Xwh>Wij1LdDXkyr0$2wAo9K`ef>TrQtZt z3EdUBd+W)j^ayQ5wVT`3XSH9!ij_a(?*Em#Jx_MnpbJ^oajN#4cSCZT&+@aVilXfm z>QwM$=Ajog(@pMa@5{1b?^yYMcCH@n>b4CD*Ddz;wa%~6-5;bK@}z5=^z^p>g-vxw zvMd+BIgTLgAtJD7-OHsW4fApW4|J?)+5flif(_=Ud7W3!k4e4nL6D5H?Q^HgANvQh zLg=6OU43<6(YlXQnU7_ouU-4cit#bbuM~nFgWDz@$3iA zv(@$>5_=kkIH7Zc6=H0IZO)K*s1 zq&3;Zhbun?rDdEO7ltV9jbHeWE*~p@Wt3vJWzXaic}7C*sqmjGss%0K(bKAxdB%0a zmV4}$jIphqFwcg!-&lG-{#jUbp|H^P@cq!h)7HbXOU9cl{I|7T{G9YKj&gz2@{v%| zToYm=*Iw~JQb5$Qi_?83Kl?BE{>y)5!REo>-`#k9_xP7HJzE#I4N;@cv!>q!dotr7H<@L0PO@e9;d+U*xuu9{zH(We7Nudegn{Oi2X!tUsd7GBcVwn{I?%o|4@ z1YI*Sd~3zw2v=mfkB7eY@88!monq(Jb~#`1;@i-^thtoY-O~KjsnDcHQg@x}L$;NF ztviO^Qy{pksU+6EfX{7j59vCPeSBlM`qcLAXI~y&zL|kJq|f%czpKx4fq3h~!XK9R z>w^{_m%1vAi|U5!=R1p!S}*(d(Q#z4diC$C|LDB>vgml>+oVfR7ucq?ik95AmL^(k zm1f{O?%Op!%*pz62t7id6yrFEh08wf00q)@7L+%a3tk{XtxbQI!+qavl5{dlD@#(_ zd?UB4Ng|zTWF0gJei3i=tonC=xq&6Zsmi<@HM{y9T=0C`_S(Z0u_lq^Ie8$unpzP2)6C{b-h9h5lF^tJY)2qx-ie{0$Q6TwJ-q#TRpK*^ z%>f1iFZJ~TwCQ?Q4qXrD9`DUcni2n9ifTUznvpVd&D(?9Jld-;wx@Co`Vh<5&9*cz ze12(QXp0r|&wtPNXZO!qJR_we+FHN0#9MyIHuudBH}C6fr~7HnA%{Re=WWCvCb_Ra z`W{)@DRg?9%8QQI`-TR^abwT7?(8WkoN`cvrtJJD^M;G7dE0DFRZbBJ! zx4fN+A6pzF#3R(|h0~TdDy*Fceuljq#a7225(=GGbzwhyzNWqov`NsJP$YZQnCEuF zvlcEMH@TXYq;s|$D+$nskLkh9h_=3~*Zj~;7kg7C!;Rg8w-rw{)D9}*a@Y981+Lej z1rINKWE32Fz$?l&=~#l_XZqb3o9w5Wn5a3YiSl6X+2FOa{?>U^^T(?>JyVm}DIaM; zjk2f9#m`NXwvMIGv<|m(GCfjVIs2gh`?xdDNe~Zu>-rZ6d;RmPujjJaYgjeD&a=Oc z;#k&|f6Jo6{=R!u^Rnunsw>B4PB^9J{Vspi`Jhhcs9w*c(Kqv5UT1Z`PWH%0*mF;^ zZm#jKdq~xucj@PU90d7vw}4}~m~!Ug_Hu(k^||&lw@+@iKR$h~LC%H19dReQwH04% z;zmy&dY@9I*=)QM~u*tsab=}Sxx&4Fm zpJ!DR!-OZajUPFBd#?0+k551_$`Tv{SPx=e=JV&szJhY|pB?z8z+E5fZyQ%1v|ht_ zcPh)rz3#>GjYsj49|kY3(G220y}P`z?RFiex_w3nRq+qdIWIgvO&D!my-1o$YiT&P z_XT}L@XAf;uR3n#`>aiScleL-(8!!>bYp2kj_UFhQOHWxGf6eQWc%}5cx>aqU6#(* zuPLDiLqbfZz1OwTlfGzO|IIn>4P!;%Ev!gSTh8lx7(M+bf0ZnBSBY(ISpL)PpLLk$ z_l@gI*7Fwrmv-y=f)jbCM^=nBEn2=M=GX(7|lyvY=q`xj(E#0oSN6zULR>a_i~1o zCLZv=9t7bB3GdBdi7H#*$D}&PDNT_n0^J~(kLGzrgZYTEQ`+kRC{5EoxC|QM?R$pE zq2j_tIMuDb#ORgGx510i4?M8xNI@W$SJF=^-RK9hEsz!P6G9KGBJE=>F1MkE(S6elE#T z0YYHmWy)`)d(BCeihBgib1;lQvlU9P&+6TR32_g-Y)ONA7CO0tiY-K@n7gkEcxp|V zOvnvHSzl|ZGKFQZKat2Kb}|5xx!%Q#zDI@^7E|z2POucN`v8|VHJ0F&h(%M1yd|5e z?7X)8pC`z0SVX|%ZgaZqKk5^8-V#DGNRx3kOj9bDvBx_T3sYmQxY(Y&5d)dF>`+6N zqEDqIF2z|0d)~)uj@?PKf8)BjlpI4bOA51}lMH>&Z=XM97Q=g~D0V9)la#1V;W97y zsDg>MQ^SF%P*>G$HyNHPNC%0GW+H*0E|WRkU9u?u?;fjE-Cfbg5+gv#uw0Puqe?r( zLzCajql}i%NN6-znsJTZAV-5hylE_nJ%|j>f(q(^wiKRlhTHyGcN*>X0~+=4>zOek zn7(G-LkQpgr`KY^`BKiZZ_KXFzk{@f=AtR<{jaIJBPR{hNqVP4Z~qW}4gXeXjcyA2Ci z4aTh{pFL{V!FW#gB4gJ%ZkfQc%z76*7anyR8>K^`5b@9%?|nl_hv%cVpCuZnBU1$u zR?1KrxiHS=DGr1A?FY6R5aZJgv2IZFFf@2(gXefJ)91- z$M(=OEq1Ky6&7Z9OyoytY7u4n3c7f7S(_)vnEYSSG*yfco@-E(AiCAbfG$wN7^*y= z+Rjv&AnG0*Oe!EeCSk|V>{#cgVpS|EY+7ymco2&dFCUtSsMt%uRAqtJ*?R~Fz=S@c zTF6w*EP(rWuUhjf75tAl4Cz- zK%)PaEWaFaudfi9?3hcOKO5@7NdY1m?Id6&s` z`bsYJgN?}Okf09OS^8MwSE_;J4!!#@br^skfre}ZF8~qjy&irJmtxS^n&mfA<~Jno zcs;GYeS9!q$`RQe&c3Dgdr{_s|E8}fFT44Z0g>$ZW*!Xf1HbRnIS9Kdg4 z?+mH-U?1<(LX<5x_lCzjda2)Wt8k4f^5uue(`iw8ViW!k8^2wL-uZKQ_7V51Q#|;) z;)_;yWJF)NCGzs77il9ozx-x`dx6T6PC7aD7>5RNC{X{RIbWublb@+)oX-_O9Aa7a zjBVb~vW?r|1(LE9Hiq-~#hVG0+5fzLCy}0YB6alX;=C1gf94 z%Iq|;c6PS!WV~h7hnumrRP2&^XQ4c?@R^Lbdo}ojtH%!;*LT|TFwbS5VsS**y!1OV4+56Cm9iS!#x40-aE6B}u1zD)^#qir0r&@2s*{E&R;f3Soem#!u z*4k~;zYq0glc5iVx`qr1c4+PCYXchwDL-ZmK%=8O>01Q;FxnNH$QCmc>Q~NAuMt)R z;!2mOMetiIsHIX^z~c>B9nQ?x{z^^mnmPjJ12pppR}I? zgkZ*^yhbi@9Z#{0^~y;Ijh+SwDH{0X-pN;n}SSs9mIHMFfMB7~Z zn>zgpFm>E}m|}|#n~^5|o3<~*PNjm?XT~!##m4jkenY5|D<1N_Lie%8XX*^Q7`MF? zLR8ZPiwo%mMHHQ(I`o!Tm|7}$X|Y_+)|X%#uOwM1SKt*8T1cVvix+Zs$+&YquOLi% zmTj~ok*Hwu2q3ayFo`vl0LvdY@9MC9Lq~u4B3AtNdUM{{(X2s8{BKk}HUc1uw8xR`vxCSgWRD>t>j6 zreP9hy|2$$d{)tpZO+7a-;cisG7Q`Ru@|u_+AB)Y$o_iRXM&94V)js z@lvuvCr^P)A59*r;Vq8Vt_59EnQ@8O$z=oqbSED8XE~uwnMuHWX8E#bysc$c-3F)v zx!z>)t*+=0iOC*oe;$st2`Q%3OBK0^37hTAgiCim&W4gG2__yh-)UPS+D@wxWyx2v zgF``HBKhaw<)IQWbG_SM422WzKJL@bZzxZ7>e1iC^u*6NYD`vcH!5?}xUMA}K~Tgt z_PDR_AQ_RCtQ@Y)9BOCtKP>n}&E`mNQD`$gJq$_lEb~K$5v+XdOos#{&dSk!l`|~J ziEtKA#SR(iaHgSLYJJo4k4~L&(yL1@*&scjJ|}Zy5O-BmjP-}S20u#OQQu^l;<)96 z!jVeS7$25m-mloM!n(yJ5U$(INQ50oK#QfsWQ$>+7XIqviyKYyqp2wrIIywjnFkd zl5VMq%!!iQShTDZ>fc+7MZfili!q=hK=G#y*o%38ADjCO&Do3BE9O-wdpsHW6CkU& z`j4j8uYg)7(9Pf2))84l648vTbKj?1g6a|%u=yzE_~i=C;$f=1?nieZJ$+5xea2c{ z7OJIvd6n11dBbI%vgPn0FZ@?x+YIna&Yf{F#>JgqCWW2m%^WI+9y{VdyaqY*E$Py@%jcS1(~k-!7Xj?7cK+XLk>C<9zsd|2>OfK+u}@6Q<^TdwFUnDA@Y-NCr3KVBVPUxmWdatN zYZ(x zXxk$+@@D_(3Kjj-XFMTZ&vs;f(Z2oSx+rYpFdID}b`D_CY9ts~E&cm!tbfsKrb(Ch z-BT^oJnYtei8>rmKL6tWkVi&;o0GS>H z^spv&VPtalQYIZM;atjzo=}5`gJ5?%*$K zaKMj~Mhwzg=jgP4{klEfJAEgYa)>a&E3qkDpQ8P6b6H0VUDh~k4>a{NxSN^kiPPLl zgAe_o69^HRqx_4ZGDL<$jb3xpyNyjfN3RTpncmQEBZ({QIM>V{8+dc!my)6;w5T=j z-L8u&nip4J%|B@U%>ML95m{bXuxj2G?o;DA7I#SfdA^C`>Z?}AZ6HEdvU?>EYA)jH z-rqas9PAnTt)qaH-j^M=U_h>M=&&C7sC+C`pS*DZPaSCgfz*;1^HWhAKr&M#n&l=6 zK{>TR76>XkQ4mAz(8v!Pz#f>GHNWq7ThrjL_4rM)J&&x2$_BT@3Kgm7g{VB$;F?D~ z1AboX(-m!65uH7+c&G=PT!q1{kEWsXx>ot~0tlb9k6|`a+uG>U!7Dg^#?I$z^t!{i zkJ1O&1A7ZV%Y5$#Ml(NeQ5lj}I>g-A6})Ke%H@yq&%u!3NmsMj&=w2ZqjA@`w7{zh zP-la+$IQ|^WZJNkq2tTYeGcx&X1tdy6Zhq?R_RPfM~_D-%a0~rxpIrOH2cH^GBseI zJY}A%?Ts6=d23QMz^lD$P~~*!M<^gIfWeW4T-KDeVXJ5(4Svt8v!8-@ za}#E47L*pHVi9CL_;nFF96A~hs0=D2Bc_AbySVyUG#o*Wl+@U_j9;9=7XWc*s52K>&sh~sAmbnzmt=%>mM4GQA4h|R5mQMKl z|I$xufd2Xb)bwEes{?f>hE?%|AaS!_T1u_z=rf+KB;kGdMAI#WNC@W!wwZL4YoonJ z#r%k&yf7kpKcHFNyyqlhPt^P|NE+V5MyEdq#X9^(PbT8x83qQ}ts!jzw_|WlW_C3H z`Tp1~$aTpY$y$eFh9(&Zwqk}m<)vq@4R&JmpE3*j_p$%aJRak7dyKkeb=h}p!ai*+ zB0@j>oq&KXC4_W_Br=HT`}F(G{9BVGI#tPs*ku5x9iG9r+U#;J0c13Z%#-$QhQ~3L z4)jDYpB(?WJOJ2ZN<^cSa{g`wAlNg63*F_&R@ifKlGTLm3GqPt`t%pN1}ZUw7^myr zls~U>N&jGJxF0WUBxX@BpqS-5uGSmvW!)iGat>x@7|%%TG5mrMiRo^h5kHb{IS&3L>I775_thMs4}$zG}C8Qc1MSrfzhatpDL{UR6TFvWn> z+vb@t_&smodyO=U1ts24Pts?ad*mAV#KqFmCaB$&95#hR3PJpu6O58n5Buh$ezmo1 z>hIA$AK!cXp82MSbBL$gK{5k3rh)=72q$Y+DHt7K$^zl`MXNYU1$YbpWe{>2iE%WR zS^WbKHg5cg)H2|byhH6&4j&fGn7>lWlv_i>?It@w2pz671Hz8F>?rw9g~P+r zeILLq84+y+DfKx7X^Ltx*~4dw{D{ap2`|7lHUWCinWLY;yrBghS#R!yR(_*;KmQU% zNHjD=JuEcO7+9$0oEw@M$Yl_?4)*URw*o!I;0AJbvrs1%Rr?UR8JR>;!!eX)6oJ=O zG4O}cV9SB1txP=Lu6*XLb&dQxl-}ZWpHZz4zPt#u2uT`#moPMs4feL?Lif0AKOl{L z!1?^%7^Fc!Squ(kY$t+icVWsh!9~m(&j!i_SdGbkK50i|TvF<3Dbrpi&WcNzv*_EW z>Gx4iA+bes^$elaT-C=7ij`Cq6FO-PVo-G?A~?a5;I{|@k6)+TOdv%0DXuE_2w%Y1T${t16naG_k#GmQlaL-w>WNRc%R4rmw?6J)Q_!ws= zl$VpWRi8j0*|USQ5o9_s*)u-3lv;tF^Fjtpd9n8SfwddoY(fdIloaI7qF@d)qe08d z)3{d$Hrp~)p#kzt1kEfW*vvxG+(5_CS)oRT%#xYZ?d7pq=Fjf?ZQ|h;mJ|LGI{q-f z?*C+@{;JD=ptC8FIBZQX;4pCM)B_{l+&hL#U=lFpOrc)jd(CE^=KVDT?cNKhh*K+<;V2@$vHN+t&GUk+z@J{Y5l)@iou#67d2m<-4&oTZ=c$gyBh&s zX!_1`^B8Ix_z~ktT?D0a$E>~Lb#!p3Zthe3h$+Os8?|z?6mFPrshW>*-ebTW)yPa$ zp+vnyfK`vO-br;ak*_<|QFfV%JsW+{^sF^up7tTugt;DevU zed-|B;$1fV7;3W6Of~JeoBvVyyQO7J3CSFS-nfkI6j5#C{L~ISP!-WVi^^rX%XEA zWR^fZBG-l7oq%w9#Wd(XqfQ5r)z`%=I`!NUWa-4gVA-T9$h6Wn-eEODtK9xEj5r@; zr-u-ZTfymp0eAxsgd#gUT)70z4tCXt%y9r>h`n@=uOThN@=Z0_7~$n7 zg;c6n`8jG+hs*VMHY+@2=X7V*IlysC2JaZXtveORE^_UO&zRC$Jwe}FY;!kZFgDB8 zL}%4ZV-&jL+wU=fa^UfuT*u=(mnm}BbX(b`DVEHg$%%2^l8`(I?Gc2H?o})3b?uc- zS7Fbng4wrMyEZ&Y;N;z!Mpt}nRy)F-~0og+l%#+uh)a zn`dod^Ru_sT3k*>kwQXb-_|DectlsY9r%Z{L6vTnEL?>2pHv)}G2*oTk`PPEx%_dG za(CF+TR*(*ilXmVqN~Y`WM>SnE<|k+w%~jO){y%mdc${JXG+o?E$siD0;&1#OF@mdFwu9Q-!E&sWU3kXJUVj!5-E1llkGl+>HHi0Bqd z=)(>aePT<2F;v!r#83peWr?hkQTeT)mMu4O{&g_j-A~$H^33LgL#EjZKdKki7B-89 z<^@xamA!S|DKH;`EZ#5LyKiWCm-(E@S{q_qf1**w^7lyi?Uzr~9}fY=J@@a#*ljCt zZuV=GkcxEI7mYG5hiac4w;WISPK)u4oa>jB6Qa>LiGG8svH+($oMLc%0BWg`d}(+# z`+$eF9L%Eka9Ep|qJ>Jg>GaR$%NCN|BqV<7T!eZ+Z+@i2!Sd{oRS9K-pI2W04o|Tg zlYC9Fn~W4;Yc=HD z)yG#|TJp-@*U8j;{Bm@y&Ogy2_hM~3gTC<5MN^(vipmp3bA5+Y?Q;KfpabpR~e0VGp&Ji7YL^+4dFBu z2e7{_y9qX~LLg&PKX&fNA3N0XJG8tgIjCtstJai|pb{-W;duWls>L*|6_Q^N1s7gU zoy%O}i|;4cmLR2h7IzHV+Bvkan5O)ZqkzZG;WMi#6 z%56Qek3>LkBLhJrHG!lP%AYBBSt+uE6|s)$@6QGF3&8b7@$HupfS&m9$bWfw>tmIc zv8hAy>!4S?ZNL|Z|F}>)Ls(Z(T|Q9eP|fo?Vp!i$;-y1K)VTIZ%)E(l)aOL4<6odM zMfxEdr1v7?BhC&M?&zX3d7yKqqVK@(@lgSmBEh_0SfIKlp*9 zQ=nQ3lt>|5$yT16(IALiCo@YsaHsMhw|@q+CmO1m$SpO@6WQWG-$@1cb!@A6QW)Sa zKW}4UnRWKaPpo|f=+)94&@$i=898=JSbcO@D`Ku1k-sF#G1J+4v+0Jzs6MomYKx#z zd`jQ9X^V1X`-?Dg%?F>=gYrk|;Ib^2KZ zT6JY;J21*g`PYo&JD^~G=a3a2dat%DUaGt_2gLdUe@o(#HZ;tXIYFL*V3LbJ=7J!N z9T1G?0X?#J9i4o9Z-bO=iuN4P=aid_4 znOtabAsAh~Z5P?&_{9d%|EpO-$`5uO)Mp-Syu;x7hgp!{F%X(p|Dqw5;2?W7MhP0` zROh7+@oEZvssLS7#_-7~bxVGE4#JmKlFTHU=j^fhIg_ii>ZSZ;%hNlgzn$clYBJ>M!<0RR#bZZWTCM*m|qbX+~8kH zNW}BEpo$RU5trycgS(Lke4!a8?Bu0G=0L};mQ78Um& zKn<59AV-K%C&FdN)8`twr2#{@Jo!*%&$_gdIEtaJ8ImWv1FTRW$o?)$SnF9^rRgxDNJ|yoU)$e;?wGz3*(cb>-_0?QMZQ-B`vLn>@M_$8 z%^hFk296RUnSyNrp?q&GE!AH|d2{qusPhPGmKsbMJ8_L*D-~BP)}2EMgYqeNQLUz` z1zI)?d;G?$`bO}lyWU{kbwyhGvolo(k|NA)&_oGD$=-|U7l$((P7b1rA(Gy(Kuc_S zq^5zl4cC1YOo5#tf63Z_o99O3|0NYZAiN)o;e=?)s( z0%8vE|JD)I_6zJq-Tq_2F%iYuk#RJrhz_N+Bhd#Wg_at0?uZnLut$p^7%zhysqhF@ z6K+K&aR6ry=z`XNS=T_q1gcY)W&Dc!EK{H3OhseC{lDg<&2f@o#_Ou0jf3(k>G!k) zEawoJAb9;=o^iH={4Q_P>s?jsV0$Vj3Ssq5U86h>x(O|&C0ak906hn12nTfO(Eaiz zr;@#?bTgfLYfqDTP7x6jxcH7C&+{Kzm`+(N&PexO!hM}37)_r(ILWwNv{yEkoO|o( zK7pm8spZnVwN-i|q6t|Udr*Al&X2}u2$+eUBEa%f)^3?Y^Ng) zu*wmt9X^A-{HhzMl4A6$xmC;Zw#P1UMWmnew@Yv_I^4bwBmPA@YLOyMHPXLayrCxu zxNuZ%JkX9EoIP*MS9@Q=Dxsd7>O`}co5E2t#8$r|%<3FP6amy-98%1MqrVPYS2DL> zTUWfJ!>x69R$4kV`-5ei%NJXgOWxslgFx@}!A=7(^OD?5+NvYmY3KifxunHvtZEI(5Mc(?@PGPZp$os??pKv>`6; zOgEDblmBNosrG9zr#_=1trr+B2}t$ih5M|CE0Aw|8#hRSTr4uZrVha!hny`I3#?}4 zW)LAokyDBi%OtI#eYD8#q5Fx{CBA3A1r{0&>-+HBqxMRie7ijtWFK@kPPReL>l`jz zZ{Y+mQOKbq0~8lvdNL!~7~XL;G7JMP>6+aKe%D!RvSG=~2vd(HQ8vdh-1rbQ=ueQ3 zf5Mdbt`jR{OiLKLAoD|_nGhRB>Oespv8D)kq8)VfFAdF=oGBw4Br5u5oKGNm!#Mc- zV(Lrai<}jt03Gt%{Z7}n(#pQoXh)E^m{kXDQj;lCaFV=ul(H?U(S?wxt-v$|V zh+jsNZj8w~QmhSB>abrl&xw?19oW@apkTW6=%&Y3_7+e_HsAKSvKmPo{JoP5)=ons zEcIpOp}6KyM<76zb4PSQF=F#oFpF zRykb_EKo)Y*jbjiwHD|2w0l*cKtFC+)!igcKvZoJpS+p_5u`^8S7KHz(?5562VHy)ZdtTF1a6OnsG;(|O1sjYviA zS`si4F$+#FU;M)+;BI%3tv&(BD3SU%70NFFO$A^z513qvljcST0a&>Eb*>$moVPYp9gbbUURTVY3)M!< zgp_eieyIA{*zAh@IGW-r?nyq*E}%_dUfz(>0#dDklq(BC2U8}d-PLi1JU=#A??|oA z-HmBf7E(TSqNKGoH_@v)ul)URdCH5nzNMQvYB?-?$-x&ogCBli4i)Gi4t_jP$A->T zM;05OR6j0g$LlkHYL1!C4go=8biqJA)SPAM9TX}LS-_cdVOtO*i?^;kn<3jx9{y#N zF`Uy6C@5R%#r$AMMeV5OfT^K|zo{fyYNc5krdQXg3)=lO9q#zw9lG^jUiqB-m|9m` z{We9%4KxeaC_^Y)Ot<(0M+`69^dcxOcUl)S>Eo9rC6j|4Y%3Bs+V}xxvAcYl+(tGx zxa+dpU0BO9C)ks_lVTt7sbLXXY9qMw!^~WrK^<`}JfGf}{d6*kc6D@^D{707JDdZf<+n zj97$|8bCNR)Leu}m`#OB{D~cyY8I+A0s77-3d6NV3QXC0f{A$4!|D-KLt*^ecK}-Z zvYk^h$U^D@0PJwo>tM&Do5EbWUci@WdCmkW|7K?EjnzI{xdS9jJ1-aTX3I+756{8^ z{y7dB#VP&N9a_l|P#vSJ+vk72_r{PYLX}B6ib>nDdC~7y7KsZeg}48b*C~RtPB>69 zO;mQo?UsCEkt#@fbS^gBrnAmQpauHOBdQw$74zI4Clo~K~2TNaW7G3zP zb2abfH(&hSIkK>gkS#>P_uVzkkP5hfvtha<@1wfAn_|y(M zFolO=%RRHNxxZRheS5+k%67!DPX!gEZ6-qB9ax|G;3a3(DJ1)Sb|4ZFt7^TbG(V{z z6@xGrG3F%?A(4jidapIlQSDQ){)x9=#8;@Vy6V>Xo8@(O(|S5-no0|dW98RVTqiw+ z3eupQx|wMXlXnQ?t~@q5e0qCvxM&V(%)R`%F013A;ANRh%nm=kGde{n8vQy|baNi= z7+=}()b?_b;p-ZJ47FU>Xqc`eusT~)93|timKmR;0`;OYAgythZOi=!z)e_8tsI4q2Vjb@Zn>{`o?y;yi8GX`ga+|qNe6^T4|BpO z9i4MKuu-`xAL^$BK^;^cB|bGIpv2T93;~-J$u12Th=JwJuWKv5Og&H@e;pi%$>5IqapqDvfAouz@~f>84NP0GKXd^p%*HYDw; z@>#~vI+>`@gIPhtZ+IHdP*gzP`E0XTo$`wp9< z9Fev!voC}X1|gK>HtEahiDomSbuIG|#cJ5~SN=*ru7|#AkG7Y)xoShQl13;MiaKV| z{5IxHc+eM&HGwLx_%E?oH4YR-#Lrb&AVpL2tmXbVKl%UZTM8nb`n{xqIe4{;Y*LSv zT|}8|nAQe!1bmJaP8sG*;<%ygrEp?;?dq5cNZk!O_9I}@7YqTJlMu9Ne!W`wm+Wl9 z5?*`guapTo-|O?Gp=jeXmsSzyu39T~p|`L4{D`g~c!4qO(CMeK(p0U%G~ktMR>%Yo z3I_M@!2Yq3>4;4f6%^6D`t}LK;}RE37{=Z?Heq9{lR8z1mp+W&wW@5+^$WW#Mg+Mo z{-5Vtbv)~!CWeCjG6x8nr#9Bj|AXdxhgqUI+k2((7KW6Ptkt_P&}=}W>$_Cc8sd%V zB3HxgKzUcKX46->#LzzR3-(*Fs`gq&FP4HQwY4Y|ETF?!noWTXG({p0)#w-$_)iu! zthLf49wzC9y9xMl!~bEgy{>OwB*t zmAUg#KQCE>_hjw{(^-+d4q$xpvjiDNLH#;kS8tMG;_vuiTYD+nf=KntHcVOkf(v-= z3Sd&ae5I;!R9sZTw;3+ID0?jclEAs}*@Mx02<;4KA!#sv;=rY^)PVggduL9*TV8q$h+s<-m0 z78^~u3QMK4sv;EHT{>ds{Bb(3VenQp#Lq{0g%kzAfrXc482!W zt!F&*tJp!u2PP~#FV*EPTj~FaQ$$PGxiqx>Zs?%pJ1SPCRkCDWs9|kHNW(ipW42mE zlK0hVMX5&PHWij-mZCPP#}j~afJO220n^XU_VpC;JPi+Xb%QBRaF6CQ)cWIEq%4B_ z5eu}kyS>7wbk%1+vAhA!9kJHkruyeP4Sc6UR9IE53)#j0T0R$Wy!F8$E#H_y_^hsr<2eWoXn_1B_o1k2>P;V#_qVMyDY`nm}-2g*CODpPEF3;MF44?G#;d3z0CPYs3KBuJB+`T zF}xST<6JcnJ6P%PE$bi?i=SR-qzfq4vQ8v=44qhFuxC*IQF5`FqY`b4SU_39QPR$K z_=3|3#8v+;18M}p!HOwyHt2t7}Y?S|2Z)0}J84UnodK{%x( zr(zrthdg#bpNge1_G)*nRoN3>>2&c|hT_uX0o`t`pRf2ZeQE#89EWLgGMHZ9qs}!M zT#AGoi5_1RuAtvyh-FQ)yR}>_9f+w0vWq>v%G710DsZg6ORhB$2Gr!g-WJTi_ZtluGhBgC)U_;M?J61_Q<37a9QX%F8>qQha@&xaiS7q z$0NvU9fS*s^u0icMfq|2&EW;AbBwAE`ZeNS7@G5yEhG3yAdut*d5$x+M zyj15D(T-_XYaIKwDWY+jQ_)2q^sUc@*h{uHRW_h_Cs>?!k?*U!u@Ys4x@i^e+%K!u z5pDoUn4o>oqf=>V6V19R>68ehuq84^#W*=)0>;$FLg^8cY%}szJLN z&O@VJk%@UsA5i4XJ!5Ri*#PD~nI%1V=j*Ea7uZ3Y2$-U^<(uZWmn0H9EfQ?S#Z^kN*bK zE_V5|vLuGQRElG4Q)_|}-4OAi*m>UM+5v$rr@o+B)2vK$%*w}RX@j5hP$_T5k~tY# zpuy=w*2qZAe>s2-abhQGfDyIZ{IKcAP482XW$Tb*sB z{*AJIH(uf?hw@Do5;pG4py>I@3_h20O&pVKKb{*4yV+A(?7i)ny)u6ti}h7S#TzCB z62XU^tw(W~+7Uc(+oOY+dvbVirw)!@7+c_#18^)eO6&8$WlR^DADYEZ79#ZDIYIx< zfiWjW@Lp(2wP*aOPQ^fjR5JV{ar8ISY6!)|WTtC!d}Jn=PE=n)v9f&2u0WVZI!u?~ zeBx`{Zo@uZ<7$Z^KwNWaT1Mr2 z^3ZJfE0Q>x>qy^{i2-aSY|hu4+$j!V_zE+-%T!kbnT1slKo7v<48YNdu9(=FP}n~s zw^@Cn*t%%cupY$+szTD~fI>p(p-OUWOSsz&j6{O(5EKpIMtAtR6!cP?sUjy(M3Vk( zd21|0DdLtDE))mGVdO7?9my19EFhPFh3zIHarVr~4IIH>DBd1S?M)(@dVjqO(3xZ6 zwO_~kVwlybC;X5Z8oEvsbh5$_qAG}%A)*W_l#|3g97isahw_CI5KFOZKm323)0rxd z`6UfUyRwO0qAHw+;stv2wvta!N>^KWxVrtJxFjI3VE|SJLYu_;c8A$}Aq-eHgJPli z))LTj6}}ugwfP@L)Bgm*0ks8Ev~m_;0Yu;4@gjAQ78?6E1~8NHeOgt5Lt^~Tro8=?t7{+*Fl}ag1e9} zQ@L+gibq&LEvm9~TppvdCUM6Sm}bmGc!_@$g~oGaJe;5fe5=Z}nL? zAIBk$q3LLCdl2s}8Eei_bdxACIkLO>{*c0-E<4yQv8yv;6UkdBQ98s}ptoB68`{hM zKI}i}kZ|H7Ibc`lf%L^!M}epS0B<~7=rw=P98iiDWS{ewKBH4hWEh;&7VeXT1vK*s z2|fj7M<*mx11)NT*e|)4beYM;KDi*-9JL5*9Nk#rwuO7sRu#qE(IlU!U1)=}n4Nd4Rund#3-h8#WHLs#gMSMkyC zNhJg=#x@4XH2(L%vVR9!Wodw*Zlv;n6j|JL@$XmEJaFp^_-eR*L`VIXhv24Y?oDL0 zHxi$r7;*ZoK)sGa4K1egjGFrJ$=&E%5t=i2gLZ`q;GJ^~HGw0s_9j(>sZPlqwYEwEe$$GlMjml$A z1E#fnA-}E|YBU#+?jx1Hs8N%%2ER?GcJ7kWfhpEgRzDS{s6jpZ$$k{4=Y;`kR77-0 z{5%q_OJdTlHP!@9I=mUswhby?7}KdHNoTBXmpEinqUJGFtgD)91GTrMvy<8~9d|=J zOkR_m1iJx6o#5_5Q?@5@@Y<<(mM6qHwXjNjg^8gBD*$Lz0)eNNM80xyCZL=P82A>J zwje+W5^#_VqvYV)%X^EA5Q(;~$TF?gy0!ftd9i?$0hO*tda)3*5UlV>R&oWbOz?dT z(7?BhB6M3q%ON-BDF(B`t3SiKbpR&IS#w!~c*<Nsnb%Bt76gGAgBA9uvhrFy&NYVFTlf` ze6c2K+!d`SRywG-30DGE3`T;)rM_YyOA_$-=s9s}b*&M#k={X~hTkAR{uQZs4z^TN zOnOC>An-jRn9kd{?)59RAai@LG1kS-p!^m|-_tVU8Qr(ABwTsI^Kq@2ucddt+YpQ7 zHBaf+MmtVy+uLAm=v+;^m%rl_*~Tq5I72@y`-b&BYr>$vJnXUpy)m8h{~bb!56fR7 z-7S^&AjG#!h?8BY0mVy7p%uTTTjoG~U6_THmokriNWxrMP7Z&;i)<)q>cX!y!|mO{ zyW)c2M@e_tHm-vG$)5wWtUc>gpRwY1=PR}mZUju~wZO!aemnAUZqbYQxZ}TW-Yi{u zVQh+Yl>lUG6e*(b0h z3F#(yY*}c=s>W_y)r}OS8bvMJoE&K24CS`h7KX}2b^E{w^At*)VT4_n$g*?UvYVMm0%gjXoT2yNyQ!WWqK|ar*Qxz>ClCE*VI~UZJ44#t&Iu^ok z0*Zl6J(0%<2vQw4VD}>MH=Uvn9r($T6@`?z?waTCAsuGFhujeFkw+1V>3=K!Cl_uG zXDL&f8#&qv1g5ssTwzkv2$sL*VKl;3_%XG4S3XeHaiw84>1^a-zkOADd<8$*7A`vz zL5nUV&SltbBl(|}#iN^%SRUAO&e=i5DRtckh#8V0|1+T^5l?aIoVmUHNAD(CGGyVF z>!IA~mV&4=98n4*Y-Y863)VXDB0FcW?{S>YT=)g&*g=fLGHkyPEtBldE}&}~&7+QP zyf=fQ0z71q>$)b|@y)C>_Pfc_O?S{M>N>~)Qtc49>&j$Q?ko=hy%Y%0)qPMZ_!wX) z&%fZ*=lb__e-u(0%w$TKnlQ=%tfZ&`5tS{(H$3Q(zb8V^@gc=QNJkd7Q7$w>;32r( zDgQbs(VKtT%`{4=lx4^~Cm1r7aW&5vUe(t-UHDa)X|6{FhKNuJ&mU21%69q&9km;n z2iqSMq`9yRhgp$ps2oW_ho|C02G5({%`OUSarLwMqH@E+w73y35G!ID`u^c(j8|d@ zYgj@>X1ufX4a{SO;V==1>xHOh)D)C;Fb3@rOOJKb!!$8(&0GNR)S|82*9 zi?65|?}0Eqahc%y%s)!MZoaiXnGXv+NhFKEA!^x6JNOWA?mxJRYqc69#6WJ6w&nSmnE#7_B&}m`h(rTwfH0=(9b_Gff4kEJY_zuyx$Wx$Rmzd%C=a~g*)8(>Xh6_ zv=H`4XJlsde7!}b=2J<3uJqC_9E{Cm4}VxR_yyAoZkH z&FNo)|M&|)`t3u_$#a+}E*W^u`a8O_?7en}xVynOYbjIC?ocurSW* z6O^4>lB$m)>qsbjOejUU_jY)NraST!Ov<|r7HQI$jP7n-=8ACsM6r1&`q(pwD^1a0 z<$!GVHA)Y|g=&(X%ybXmq{=+qE#pFt@!Y8}2Sg$7gohe(S_UQ%?kv zzhXOw$7FZdkK@n-itrd7lvgdS#R7537aYQ~*TbAJ#0c8UQ3knGk<8lUmD6nIvcPwo z!}c~6$LJ)UsI2w zset`O6EGm}bZGQgbE-#1=oq0QZ}ZCR%A)#wkjL*)XNCDw7&OiX=;_`=P_}1WETc>|VO&R;?ERdJ{fjSPgI%|yPlrK zSOque{Lb}7B&q?IX4moxx!-7SgC2s@I60Y4VuKlhQ$rM(zHa_8lRa@H-~0*Mn#I`v z^SN}N7~dkv+`Jia_Xr7-dJOJrvX;W?q9I7z4;&GaKZ=K^Af^x*38d%0KLjGNqY8sL zDh%45s5qjrQ-4R78if(kh7BDx9~H3ETV1lmI`>1BIT9HM+-Ie6zY`I1xDysVq)iDL zT9Vo)-i49nIA!7eGr03e;8AOyGhta^$8IfXGHlM8#0GdgAD%LiqxeyoRd^tSg~a4^ zmq?;)a4TCGLpESKt2S!P@Pgzr?yDc4vIo8rISc8gc%wL?G(6)0H(jQ+D&+4$)mAuyYTEFM4Zj@6Jq{S72NfG8+}Q4GaBccdH)LUP=*Sa@e$puvT|G8MFtO@#KP8_^#Jp{IJvbB%Pf0OVv~-| zhVS~U2?_2!5zaduNe%lZUmR&i6_9ZFWkj&6Tnf&opf*5E;7-FZ@<59@n&N2xpnk6; zIIw87rgt3KHJP8KV}E*PGA}Me(^0v^uUtZ2V-kt`u|oZuPp2HJOJO42KPR2t4~Ppe z3nps@1OJ_JvH_u`^%5rt=AC5dsvNkJe7G9Ua7??cV4>7?M4c$&3aR#DA5+12YM_D$ zsoVp%3j_U#r|GZL=fr++C2Rj5KQeP66wB*EipZ+MM#ekrME>8(@1M8lKk*~t$Hj`CakHC990yb=B@aPlK6GCi{=`Q|h=NE2 zm4?Sl0Ot-6(U`9218!*-lYBH?F4}hPa1n^qYbSqE$4e&9s!9bRsE6h8yCnPX^{?jJ z@NKm!)^$ZDt-Li~XqJ?vnL^{eF&|8>=rUc9=%AYM`pv(Bk+#~E)^)NQ!;b?mY{{4U z&GS!6w{jbpd3b>)B9c+OS}zyPOdFLBjvW_j4JyTSWBR)&aT?h4#QcJEf~7|9m(X0( z2Lha35~g~~U$=wP5pr9c&Ej0NLc+1fCq1Ll(t*L1M29D};8xrG2baF3nuVN0OJd!| zpmm2=y~HeihZa}0eUm*dn8mz#4@0N|=*OZfUCrYVW6(UcZROm`b04N1F+4(C9ttZk z*TVnQ!1UwmXKxTVaxldc4st@x@NlUTL_dMPJO=DQUOy!V>5@*BYNxzsN>xI5wwKs{%d)Bw5x%((%;oL>r`dAA%eFrW=Tmk+$4s}h>V zuC19atyV3u)0jYnL?`Qt*76}q1`!10kK-vha`u~10BgdD;x_H&AetuD77$^OL;!J^ zh<$ch&PW^%%a#4DYGNAXZfusu((AJcLg?D_7uBw`{jHQ*b^pqbFGkPN>3FY89UAnD zUXsywSQ`_P@Dz?b(_2!Iy62o`!o*hFM~-P-4ni}gLJ!=prKc@_!(LAheOX+MhuoQP zupUHHfMy)7WcKkT)RE5-mZ2P1x?Fm+l3VSJy!Xv%9cyH&OU5|7*H0_0PT}M0^&Qad z*QK%XcI=q44E%cKMUr^O1}?SmmO>qHQOX#Rw`t!JocUxaxqsnc$S>Jlx)ZT;dz`Fx zigAwIDl?d2FtiG?7JBXjXWhRL*YOe!FGX^Yy8XF8{O7jk`IC`)6`^A#0KDc`@x}R%!SmhfU~OubqQQK z{X+^BSswFU1I>uP<6~oxKWa5MisF3%QyGm2ZJZkxC!%}*x6l7$isUl&)I!Qs`W?7f z!vFTPc@V1$R(7SxM**fNI{1!We>L@EvFQ^!#M!?v;~oV|VbuCC=S zBq&i6N(4az@nf~v@_L&EMbW`b7}k~xB4e{|PMQr~_>;XD7qvyh!Z=u%Zo@0Vzcs$= zY2ILbM>G^YxJwBo&pWTSjn0{_4QnaP(rKdOz|DnM>Jv9N03mC}$7^zlE54~-M$iXj zP}ADAA8@GXE`q=s9BUw+-%Wq^Vi3NSeb;HPEVsKUg2lAKcLcFG0jen99dv2~ZH%!b z{H_n=1)U}V-?dpzTBV8IuHZc)o5LUzb4WN;c69;M;j|z=R}ryOjdX*jeCiS_U9DX> z6xS|_xxJL0j|%Z0_8370ZeDH=HwbZWAh~WZ7j|0iw7!z1ZwW6>KmE1`+UZ#;(}vH+ z6C3DcTGrOChFPAImY3}W3*E!Zb|I6AE_4hkTkqCT(A@TX;v zMDsip9iA_cY{DDXH5u~ja+`AN_)Sf2?6qs_#R38EetrKqORnGEu2KJy{4v_%NTsEf zRn8T$gf4WqsPK(rWy0OoH!ruJHGibG&8|NvqT%B#W)5QyNl56csHpr7|9nMOM#fa; z$J6Z_tN%!fqot7W2$v}mo}%IOK=ZCzcYne9qcxE`=e%l-ipHgH`l6fm;-a=!M0#(y zJIs%G_d8b3-ojzz=-8QcOHcg+8;y-RrYh|p3@OGm6yv7V2ilCoX69IZL4T?@Jw^J= z=5XKLt1Q)*`X3^!4(B0fli6PoOm`gB&aRy%r*^s?Do-a}B0n3aT=Z7Aws@>m4 z9+aT98Pev@3%`EvTx#6%?Ap+~74I(TZt&bDN4>&U@*}b`qu#9cIsc?QtU^jIxU=!9 zt(pIuOA)sku1m(x>XffzR`4Pe`tB^)as&_234J|PG6edbd6M1Ds8y?Y|=yg?s(B} zOySCgANTe9BoK3NBO%V_ab1AdV(~hVw*w4w-1_2 zjAK5pp|~*Cuj?nRzxPNwoZ)!X`eN(;JUIC*#Emjqv9?;!`^CLPE$@ASCfIPod|aZ! z5K)gS6#dz1ykRW4H`M=JwRXvYn-!(qe|a02-#qnyRVcPhOS-b%C(oyDQXAOcbt4vDz45xCGq+1G*}OvdXU)_zth|m! zqsjAdi4kwh!viUHDhg6~kG}1}XN0Ez1XjQQ$M*bi;RExK;)5Xzud^t^6Zivoed`19 zC$}^hR`6uL#U^VdleZ1ECNxaz46j>NrA+7QUrjn-FrNMpCp5p^d?&Bf@Y*MpIp-^% zZi>2>BMM6^D&wYf?j{)>P{V)t>!4=&NQ=1PXD7O9O7Z!*hR`5qkFJlAkN&`aM?-n% zJdYi>5*_Y4ZyrFqQkp9)NdnhYZJMhNMtr<9rF(q!WaUvK{^^Pz#-|m&O3a+_Ub5R9 zlxFkxc)&-!+K11^V_L?p|M~sr^tn4bR|@PjFZo}3F6k7<8JcZaJ8sq*IQz+b>k-Yp zt#5s7hA#fv{^!o_KdYaoCGL2lD0}Y&{_K1DlgZTPp>+?hWtxQSe4##F5oWO|b*kaI z^n>_uV#m~u=?X&4o^f*z5h52^STiZHr|ELE?I`u~K1SDRebPzs)pl<`Rgy<7d3V)x zCH~B@HK$fCA-TVgY`iIh-+Ov`5%Hr0in;v(>iWclwk#G~CG(!g5=^ z+>W_hI_k2O6YhOhJw!Md-)B{G zH8x-%&6mlO%Xe|A%(}PX!O*(T=ZjM>`~LNbx^VJg$82iJyC*w7xh{Jd&UTIw(S8&j zW9`w>JC;j5*zoz`-<47G6)9g2u$S6PXS1Iz)h_Ll)ZIwF?$Z-%e41TGktC%qFeds0 z1SB@L_cei4@3Hi^^xEju(q|LWQkHdYsXl2%!YK+QL7j3t+)FwXtrm0e!GWN=c9|#7 zyfhohzTvnc`D5j~)W-RPHEH{l&V9di*g0h46uUSxghG-@MW-@U_Y2m176b;F8#R); zy0`Y%j{VYk`>gMu`4#&yds$E`-b*9lxY?6dv#*tJ6srDa$}|6roPGBs$@0g6{sP{X z{*^PQHYJwJuOpFC+1=@79ThS%^Lx$&$aubzG&C^$^&}!oD?8;xOx>T0UzA8?G5de- z-)!>ZJM6U2+hZd=E_rs*mT<~VtnzEQ8uB7Gj z3sD=G?PhB^O{COo+s;KsgsP_uYODUe>)Lc)$L7T*UGVzDHowU~Q$0WYPVU;d?Zqnh z7@%M%Wj&g(Ma(n)<$7;;>yt=xz>~D{ zQd%2maARFejHK?ZFo-M7^62U}%I$qJY?WzilP^Lol8 zbyB)gB<+tG{{%)xV3AXB^u&wJJNL%@x!ye&^}_Q8$)SE_&Ud3--pAgS>0Ahs93LWD z=f3-U3|)I|+?}-SbG)lo~ zHj|vcb*P*)$=OLgWaE86u5x6O=^M=Lx-Cz; zZEK}eA0(d}i*~xmWqrE^8$>4DZfkneSbT!Cdwy%P-L%bJ>GkrJOH*U(!|s=z)W>SE zy+|A<85fTd>f>MLGTZ1iQO32zcXVDzrhO|d`@5yg>Lc&R_s;x?`5ye-rpnSL?2U8e z_33vtW&P;1%V!hfnjw=1F41pQa@J#EME5ZTD?PODZEKgXs_;%Ahkjz=Y<$$Y&iOs( zt&RykjvxFY=k6HcGy#)ysFNFAJ>#W2o;R%8d$-pRPiholl%*Z!t2t-F^`E=>@-GU~ zda`f#HIwe0Je$yN7!srN)bvW^UFCwcwr@g0$2!+nw@d6ves#}t@}tLyYRdD{b%vx= zAxp-5yG*>R%Goq1JGgDPw*Q88MTd{A)+n95Jao4Ddtm?lyi2RqN=_T-Q>Xrd9$4cKil<#wuW{$zg%|p%-b^v zIO|@ZSf$q9>WGJ%WZz!e``pj20d-84;+}UclIfywKGf$}8k$?_GR*z3xPL`@;+0Oiyrz^}S~|efy9ToguS*P0Y#a zST3Yw{PV_SN|fO%zhWQN!*6%{#Rp&g@PP0sa@8+djb%dGPU+LeSEepJ)UMF2_;z8h z{LPDDcTUkSw(EVE$-Ub_D~sFsRG0S4Xw95bQLCUcm0Jf>16k5XW`CYLwHghlWaVXw zq0if8(@N};?kE;)9V#nxj1ODa+EV&D>2mjKLRAy(ZFqp^HI>(ixoLy1k&?hPc3R(q zp1@Vc$H~=M@ke#{cIy5JxifiU+(MzruvnAQ<35vaJE!|~?I%Ndi#C^#O%oZbXo`?z z&n4-kpUSvx`qO^6xv- zlSCZ}NV3gLgP(e|h-{kU@2*=hlDq4?L}Jb8oQwP0oQH3dAKec))vOWxNO^@bZZ5Da zH*C7PZ5>sLbs|G{VE2Q3tMu!}pQnqCj45Zkm~HP*k;scwUH4UYf22{VGH_+9O25P(U6QP`OS1fRf5C@cWgjx^-kRsw3MS+|~< zu`2>MTx~C%xiH^naCOx)!;u=9nuar`p6~Wv!3`7zJ$8P}9acJRd6zgekf&nB2aD@?Z@IjOYUd2yZaz})9;m#|k&D>uaQ$eeDe z4Fc)`lEEkGuW7B7^45C{<(_=lUiEJ2a$2Xd`s!_~uGlaxA5advKUwM7)O^3WMZ0WC zZdL1kn{dZD*{#HT!8I7q#0~NkrcmPagrclTNeUO0`cwW21}oj!T0#{OPc@=o7B$#tS_tIw>cs!!N2_t_w;6<9oLU< zz2>l9&*8mYqNtfuNxDML4UoR6_o&2wYe~XIdH%u2hTEpGJD(bGWjpk=2wrnsyfSGd z?n83n0sB9d{%-y5*5x+~%%~EWZ?9OnMZbz{G{5*hF)$6V`Sz@Mb7gS-5vlyCQ_?l- z{GWQ^5yKX=H>ZNPtUP$`(pi@q0aUGLn|2iw zCM4QfsjR?%@^93^^EVNO>6822rZKyEL2s4r#P5jpr7v&%{INF^I-+8%u-|s6wFG@p z48Htv`Q=o1#r}Q$ByjSsHu5dI%L#c!F}<^Q<>|O5D-MWXLw=SNZ|Kwj)`r^%pXbm+* zb&QV%W{3>t#0|a&2SJ*;K-gR7MF{}w15c5Dg%hjVa^$(_&snIT9b%gEOP$M zbbg0$f+EkVbh@{yS@l8GAEk3`tT8Q~@k>m7=<<`-TEybU(G2r@H21ZxI4V8PW~T%? zksxz3+Qyy$ zy2lA?tzE8M&BAGAcsXTd1{)7@BG`OmdF+uL<&tZPf%ZSL$)w2EMna2M!~zn;M_QWi zHsU$62-vBeec<7Cm_*i0S`!maUgyj-{hjo9loFbt+Z-=YCVVM z%p$pQYR=1&r|gf_r*gwIxv~al;pC=z-aWsao^s8`!y8yL+?|61_$iRtUxWTpmreiB zu~F!x_&ha>MO@2hBiL<9h3uo0pt%Z9>|>tHD|^U>Pv(SukIfGz9Wtllbr`3zat#6= zLS)O)VuvkGo>>{xL4qGd5Hs5Ra0{7HpH?7#!KZp}QyL7=P-?EvUVn4i=sl43FM+{aPx^7}ohlJ+YRuW>28HG<;JRn!`aYG%*04 zQ3Gzokc5Xo=~bRDEEYeoPJ+|QC56&%FOYEe+@J1fh%*qm0Be187X@5$@nQj2c(fEG zWh0Htp&QQNHGBhu?&#F?k9y9DUpTtkD6w&?-kj$J7$%s%#1n}G$&}0-%rF^`Yc<<~ z^@RD#pz0~B8khXC~by+mnG(-y15R6T4l#09pBucC@#XY$Me(&*G);i ztYFpZ-@u&o0F~kVm|)lLnikv|Bewz1tTvVG)0A=b;5iz&{mWD$WeabOwh^UR3@2t5 zI-q-b8EFD>D1O8KX_N&LxJiMU)w6bW)(E;ZPvx5|6zpsvUFBi_lD!amzPq6}rzeRR zX2oW4BK|x4f1#=WA^bnYme*Pxu6Ru{IecF>u+v8`sIp(=BOS$nWRL~=4g=C0iGAHW z<91>H>D-TU4v~w=I8L@%+tv_!0-K32j)5B}9{g6q3pbHaX&uouq<9hG=s?3i>OGl# ztr`0V6|Q|z=~t6Q8>};Kdt~TLB$ESw_Tky`yy_( z00YIL-8}XRrs6I0E_v(cPLy}<9CooLG!qx?3J z?_12hdeF*w6z0RBr<8NO-lW$1N3TZLj(b0$?rROpnLESYqOg2G=4nR66~&FHdVDQ2 zVMiB@D3R~CyMQQ8g@5ukQNboF7X9gl8`!kl1l9xKM;}0v!&ff$1x-+k5t)hI@)LOL zn$@F9gK{d8mXDOZSz^XfwQpt1GSuFx9B%*R`@HxSp5vZ2py_xWxi`c^qb%xu}3JZX4?pS^?RB_7BzWXDV9LT2K z5l28EyE}^}D*JYV+l*zxzBJ@g1&NbQVA_aY`x?JAoux zNqd4!)f|gbQfzZJ(s~$rhXgK*DY0NORipxq$)PIx;6uyu2!g9C>i(uSsss#dhGl#} zR2k4jXYUjQPR(2SM=o28-|5xfLnmAH zRMl91cU9lCpx$s`r1xN-gV0ubr-ZzRg!|60bxu%VsHFjPRkD3h7Iw(h^K(&A_x=d{ zzravO@=F3$2Ng}BIP0R+S@eVFW^^JoBZL7GCiPDaDR^WSqJ1+Wc}I60QC3IjgB_iKr!7Hv1gXpcF_fFgDF^;`dxDk+ zh|mr1CNNxAqq}cl$fd#CUt=QC&hFCO_^PNW4~%W5OuHBydTrG=#msMD*>eW;GJ|4H z@p?v-`0_@yQ6V(860ecG$YYq;xE>@)f!B-DO^mB4J(!}4 zp{u)%N{rz-Q%KUBAXO?_!2=uAE~1t;|f>Ei)P3 zLOn0!Ik#8b(~R+&a{BFZuM4NSE|y|#na>qSpt}_PtuED>jm~I6QFbml5|Q+Nq{wyA z^p?!oug70hQlthoF zsjK@alD#V>4VGP+=dr#L*zJwjNWXjmd9A5&D#KYn61MWw7Rk_LK%**KTlT4bu8h@O zOE{*hHjIckfYkcp3$^hxl(<{mA3(vt1O`{Oqf=xnqst=OW)gvoU7D?a+$!n zMhAF%qFV`~?Ihq1roX+Eoax4o)LSc*vZL`UNyp~GI)x6bxR$CUyo9p|pRd4=lol$7 zc}xV-u#Sqc8aH1j!&-yx!ec|bRJR`))E)kyar(TQeDjaN;dtGR20F_U4HKmXwQe)= zJUnSV3QE|4pL@i}mJ8X-$nLLQ7*s8&VcDc;IiMG)vY7RAy`mM487j}6@tRf`*|17f z{{jZpptBlBNup9SH)dGEaY8}Naztn?p%gq6hlwWM#p@!{6FAn^RK7|_1xY>jP?F;D zp+re=DkIrP+-|##By8g&jq71=8ft-v9AF_;k_Tihk4U&u3lc*pJ(%(Pze(D2Bn}qH z1$*XC-cMWBOjDF=o7xCdrjfUL7DJ`BzX#){48w#(KEQ=ZpCy zx=ZnuT@f1vRR3H}H~kj8G6A03k0{dMMi&CR9$8R?^L>CTUa(R(^*{0U~ZCND)Y&S_;3(pXJd(KmEe&n}Rns_;^x38M+Ny-OjlBE zSk}+cZJUfYF`3(8Z>oK{JfHMGEBH?~7^-|8`nWVNM7{jLF#BH{eWY6MlQ} z0BlxOkECQli}>3Uq+&m=Y%?opS?CHqpcZ()%lFou7^;@xw%8&`?Dpq1=_D z9=DvdWaJeL-7QF!- zMkX|%gomE;LJe4;-VI)ONAsp1viBU>ie&A>4}Fp&I#b zFXC_>FFKU`$+&2G)lbT~tA6AZ;rm)kV1xvC`e$ShK!K-*>^NWtx!x!<4OfVFNa5VK{ zP7d(9TL6yfip&T)|J73~2i$%Ak8tW7boJ^fyd%Z&=o&XNo;Vq)=T3_Sk6BLW$;wEj z$WiD*0i86Ggjg3VC3;6m1o9*mGHxq+6w$pi)jvVbwF`o?>`a&z7~M{|18pg)q|%(P zKU~NxTvM5)W)_E&tW!X(_Q2;ZkoD~8{wYwe!cL?jg6NET=Vw{zi3JwQBvSgCTEKMc zsi^VgH%4`9)v1{vIjiz3*W-en3`Akd;A7GWs>>t&@OHKGft8fdlP(sLUTg(gF-sNJ|&o=P-Vn{Thzq|IB6n^_Vc6LXV^U z`?JCRbhM^`a13PP!8{d(w9(I&?Lm4%efp+s=7repVY2z77B=DYCl1sZGew8MC0*vB zG#}XTR3`~e>cvJNEk}WPN0KjYt(0dbDEl_K3l-xurg`e76Pb+;3+%96bMr>tl`FTK zE1%9M`OU!*xx0wn+Hw{V(k+IbP_|h?Xoq{ZOBd6~0CD!c#MQ`lwO3`-2aV>r+)$oz z)fNoo&0OIm$u_bzMe#x7LPzbT+xq|1Yk4XS+O=rMZg{G^mJ4yVw$WF;I-}zz+IE;3 zDkFH#HdfMre&uZ3kuiCAr5sUs)S<%3qN$a#*>ziEyq55fxtYAOByNLKZM}VQk8UUF zqJ);=2kA{|nS`%fO!~FUalF42I0eyITbjAkqpMm;pzmJAJ}GFS;`xybE-!tZ z{~%p2s%2QwB zI-{`!)ZB?qw~oeUh7Vq`SVkoNx`u*l02wWiXE?+3bkJ!Dk)6;HL#R>#61Cvx?ri7d zIqGu=BMWWwmjV*4!CQybGK!3Lzmemxp<+{b%meWtBaHQ~LU&7u)g-Y9IXxM*xQDDsnbseCx!f%A@Q$t&SWvA#e}@n4QdPud05oFv0O zgditd`x2=}!l3rL$%EKC1XAPcp4(G2g?8@hYxzhh$>(O!qGZgaEYfmpilftce&Ocj z78j6PazYu1z4y$Tz2lfr(ykxfq+W2(nY*s_+OMU!(BqLs$2uhtJE!-a#1!JgIk9)^ zmZK%86w2@z$oAfSEzf+y1$rxzCXF9X&ab-Hy|2aIXE~zZ7bCy2I}A3KOP~CN z`$s-YL)&D|`(W^@-fYw@>}=IFp~@6S&UbrKD5RkNw9iAtWB&qcLl@Ur&S@BL4j_UW zy3j)F0>!w+|D)sZf*L$Xk@NvrPw;d()I^#ylcSV<{f*Fz@YejZiobJqHw3M)x@6F`W0<($p={aQS|s}Ux2)3QH!~y<|{O;65H`Ozg3{*pQ5)| zE&SL}rD$&QST3e4NpiRFFlV$?B_1j3nT#iC47};`>je5{k$jp9s>?~780}EJPu>*I z$R3>>&I}Wl>pO1I4K7j65cc6Rk6yHwIT{WN8kL@%KwQti1eN*u30IKX!rrb?&WP;v zH3QP?&zQAOZ+)8IsqmuL$%hG&Bd&j(UD<$kg3*^^8F!@TQ^2#HJCcfZV z$M_h^q`FdZe5|SZ5XK|PzPo=kDHW3yrYs6y-p^a-F_MWd; zBL6`$)5~5O&zTC}@Hp~N7=Vv$BJb0cL308EnKiU!kieY3`$L8&vGL}WK5eiO=%BSE zVHE{!N9IltfCR@H&Q}obzLKc{(W!9D-(dJw&{Grby$y3k7N^4a;HN&tQcSeWyhWm@ zLNAXeYBRUdzzjhcDu;UNqMwg~a{s=Gu6v=@$Ikaam_L`US9KeZjh4$hpy~gMwfBr_ zDtg;K_c@$E5?T^kC_)lKM|xGn&^v^tpa`Kz6BQ5@6!9eVCZQKm1EQj$BBG+AhAJQ` zASxs>Rm=6p#$oW1Y8&&t{-``*|6yNGH){P_A}^gAiG zSqwEqhU)Er|6~f3jM%9Z(OMbQUh&X+2iEBfTGz)4+RCo_0tO#R@= zWc+>iB1dTft5=NQJ?Ep&yNRfNW11zciDIzq#rw@l#RlMt>+hfl9A{Y;GXRYWtcw2^ z15X`y7)_$KsCq1ZY>$qX|q@97VTe8%nVJ5MI!`-lG5f%BlR z`-Z8L2q%?0jKT6kSl2lq?;wd$6bc)iMOYe;Ck?oARITLeBxkUiRG0DJDk@oK6;Kb>EVH97=_ypdEj zZq`N1xl+`uD)CJwHDb9H^IddxFcUvGPE+B_zDKhiDs$bkYUXh3=+rY?am4`#bB*Sx zc;6S@bGruqk-V<&)q5==pu4)nB_0KL9e%XYHg|n@708dQ7QHHx6=Xk|9-%a;Iy+Xd zA2TAa@c86rKVCrD={I5y5=|Xb;@sEPO_G;dL7)|16B08BwSrw`p~>b5>p3nOr$6;N zlWGs^swfSR+Y55L<+49e&%cB0+%Q~?Yvf##44GEpE2nBejA+!tQy51Cx$5^6!XXGx z7ihbKK7RiI!ongp$vB=wf2uN$seyjPE#%gQ;JBB#$8v9 zW566n2HDWMmI0v1%XBR~2m;70r7O-sgy9zKJ>P&QPYhw=*!_ z&CPe@=f*p9YX+Po^hWwO#LBI&Cy*HnHTvS$8zq@z?(5DCD0!y3^9LpJNMjEUx65fl zrxXr@>_BNMEa8P>NCIC#N4ULd4&sx@!{d7Eec<#I#L7@ZaJ$Vzvl`_@!Sp?dkv<$& zgql%=?8Xv?;?lBwQ!^%YT<^>jA--|qmrwV~&n<7BZ$J5wYc6+`3*@m{7#n3V+Ae5e zzjcin!nF^M1b5yoM^Ai8mPvScYt9<}j9&+F#Tbp)qAOxS2~wKYu)Z;fJgXQHQkZ|H zW@h5Ty-wW;J<~WG#BjYpftOEU89xqzWd?{O3B<9ebuFsw2{b5ZKC)~AnQ38Lb%YEN zBL9Zj7ttXd^&ep7;zz|HdT+6WM74Q*&9y?*cf|b`fy)N@T6_;IBe@QlGsN*s`jClR zcpNhx2iCp78L(#bnD~V5QbV^6#6Z6~8xYqpEAP_Z+Y?^Pk5!c{M2%bD>GN{!Va5hR zW;(H{hRb>#Pt{3F+zee2GYqeGCc7oeZKi_6OU#sQzmA!UNIW|rftG3M7TNvvTbPp% zTT(1ctw8iASSNMR@7>6@Je+I8=8ge2w6&}|=e#)SqOfF5Eh=^7_>is+f$OMfdej)m zcSPw*F;QG=x?F|mVMZ9?<3;m!W{5oGIA<3qRn=Tb$$CD}*NxpXxX;UU6r+)@G@S?J zT|p1g1L%>^&rG5)1-+67G-Zga%r+b|n-d8a`QJ!^>2_(#C$AwN-iWYDj9X~NYb=(2 z?n=O-3=03r7#_kybLqKnTVN>1C+Xqpd?dsHREZ-ahYHcUuR|3jVxMPx$M0l{xE`yg zimi}$u=R^rt6^qE*EtXE8_>{J@w^AmWd8kvXyQE+1o1pj+0_tZe@4Xzz9TO&9!se3 zvuxFtPzRuS6fiGIXJx5lHMmM z0qgtuqV}#lMjxBNdPV@V3ET5|Xu}!!{H%2O!5r<>Nkk0DmZ7VupcWZ%!W=>rs--P;J<%)CN zXcsn_LW5IC_ItpjW`b1__u?aqj0y^EPLR{m$DP2@y)Yt*@J|Zgo*Nle&d%AGE0xA4 zkjv{h!fFDQtzRoxak2RtW|a}om~u)t%Ga1S1$l{4%Se0OqL{#{LQZv; zi%~bjPlJ2r9rW9^;gu&%a*Y=ZyKe^* zQxQspER$#&>T8M_?8D(7K};wUyQa4;F0!aqRw1pCoMzWm&R;WUrI1A{Zpl*BhGCKa zxTo-#aCw@=FAa2;4%Dnr2F7!}!AH6X`YT0I>{Tx^%S?eP0_OP-+oWTT zAq`3@CQD*BoE0Gf8LlY=`DRe41Db^_SI25?);&&DI!59jV>_ej-&^8_u-y``L94LUQ?0(vG@ula13rzP97OK#yNUf-^XVpk)V0whb#iZwy#FtykFcL3jKbv-zw5} zLr|fbRxkI*s$G6Yxw0V_N8~6xz-o)f7|GtP%ERlXCM?1#Qr!zR)*3Yq`13(?cwqsj zQSfD$T(H%{5fjie?ob6A-^>`Ovm5C?PLF4<%Uia#&4B4b7&T>EJY00KM1c~wKh;Hu zKT+~VcOtF;>eoU!bbdX;e6r7M%h11nh3KCr6Nt70hz)rp%`m~bDqrPiI;0Ov$^6vd zMP~w+q^&Pa?`dM0RkAnFA=>DXYsWa6PCx%0B(e^|=r*DIwI=_WCSSxpxmr4P%vP!= zQ&ZaFiIRUM&I$#3zs`HYpE|_7PAvWVN8`Vq&U}Q>Nqkziu@)MV^ES}%p^}7@A?ePe zb8)6tkfeFB>);zqAOj7xcGJF+$Sej_#*H|kfXqAkF%ApJPKPZW2GOA)7b~>+}Aw2$+=~Lrn z(M3|C&Rw=i{b?wtUH_iu=UrHO;$uHokwXAZt^^_mG69&r8sxM4&QJ zo#!5jRL_wj=E*a*R6I^nHEYWFiZDYHc-hIyElAKz_1R2$ZI!={38zBa9A?1-2Wn~T zdoi)Z6h&^2$&@jwToJK@z~d#ZDc?dlDhl#>ZHkA^V<^m|0^BpY3k94PXvOi6PO$mz zEVzTsDaVqnR-Khj9e5CJMHPX_ldD}UD`HxK1kp?|-MnDMmvkpu;+agVBZE>+hBU`S z~E^amRnl`8PUTEs~blp#au!Wju!k-h$viyxh_D(n|j zmYqN|36`ZpH6m_%d_Z3XXt{>4Y!Mb7y6ucc5d?Zrx;iiv_aER~Z*{+4;=Ak;&n4M@ z6qFil7Bi*6 zL8;xR&OmqmmZ}Yu0OtA)xY;{sY-9?;l@}Rh*Bj+_E!r}zV)&h zK?I4Vw;%_Ucr%ENs17?aZANTHOSozT7W_1!JR`4yn)(Q><>38J@RyOru{4S048wx!KzKh|D?!6W4m(It82q7v31e8mba>Ctj^+!DpTNq{)w zaC#n&JLdsHQ(ztjst|{4oniXWZ=|9Ud`LYiv`Uhv57uR*hD?Mh?2JapOwi;6(=m|h zm=C%}SUZO7Ch#3_j88Zyh%VTO)Ocdq@mx?r7ss&>UX-rhl!xE3E@PGatu5?#X<~Vq z4csd&?$SKok5Jf=EpmlX5VCO6iXqWWSZH)eH?`x{uFR8&Z#B;D z`y?VVa6we82Jd=)_WK%fo26jkFwL_6MzTy`O|8hUZTi8nf-r$%gEW6BLS{HNb&jyUjw8;u&lhQXy(dki zEu6D;{tMMCHK&Ix4$sIT3_qM;#SkK6(9auSIvd2((W|+U!kod0x9>FtR077fxihg<3Uc0I@% zHC~GV_aV4&PJDoysKJ2n)8cts^7TRq-c6V$<#hZhRmnUGKQ>>I?V>B&A!3Ne6yeMi zS6o4rGjPs27(-?<<%1l<7&)jOjMqj+E*T${bMMEf`VBmjugmlb!VTQ5k@ERQb)30s z4_pl$WCfJ-iwrh778fTYX+jfZk{Hg}T_WgG8?fG*%V4+#vxo;EMbddho6(?O?+hIU znt47?DTM#TDZ0(W{h{zLjEkHhQi#q@4y+uzO|I9oeWu1oIaoNWYJ!l;vqx_%z{|Zh z*`*!O(bq-a9SFRpme#yms03~!EXy;xg-%p6$h=CxcV!gJ1e?iaZdCkPARuF(`4@ zh6=O*=+jk48J@tICeSJcWkSR45iU{SF|RUEVz4k;q%%Z;TkHM)B%q+ ziD=@LlIgrxT{iO(q4-+vcagMK(@T!Qu25`U-E=1c@Wg)_^eI}ZHgHv$m#Q@ZpX4W7 zIT;Y%4r`>~bDEdCX-cCM7rUM-X36R<1!U)<7XIpSMA4TI$zJDVTz6D*9obJMXa#pA z5*z_mL2x{+pl;d*LGUw{WQlAsH($K3as~q&(tJix*;>N)(wj2E*@c#EH7aa9pyIM3 zH5;z*#cv?1U2P!qNUg?@J)ZG!T;;6>VCF3wQXBOu>=|4u3(Cj;eO4G1ga3I}rqq98 zX&k`X{8A4=70;O4(f?mG{+}L+1X|~>R$e2~Z5-Z&lT1r4dbj zw>g$c7DyyKnch4f#a~ztAz75jf^jQ^;*2=Nc;A?mElKFOY~njZAc3}fDzOO8YI^g#KNx}M;=_mHWT#|lKg`?m<^BQ zdFS&jMV>aU?f!?~Nsu?{eG>)?I5<`%j3x@99?pPy5~-nsWhEm`ek>l`<>Q2j2-%+7 zL}pg&WSYaSOVhodRyp199qA7{a&;#xpqenT42l9gpaXGfs0ka)TR_nfFxi2B3UPG< zpIMLn=ei#V)QT=p9+OlYvys~1AbXV$+B~U2rqs{GdL>QYQe8EmIL_G;@2M|}Hi-AS zf3haIviBG1aj<+%?c27QmlWT6lI>FfA>dp$Rr?pZ9+!L<%shC3FLj^-0i%Utm+dm& zRChqTIU)cKfe~{?M#O{^Rd$qXHzx0GZyO>dGD2-4-?xixq&SSp*Gt%HRsq>k^=g0F zrXgY7{AHd;@v*-3${Og?S_ zIQmul&efl4O=ABPJ2OzHEW%R3xe8xfm0*^8(23D7pqC9`Gi-AOC~yZ4X#5U@>55~) zP~4UaEj*-9xbH*DQ!=;Fr!_pL(KJZEz(I=p0^eJJMl&Q@zTC zSrq>?iE)bUzQMnym(Y!_$EWDs|8k9@*4fr)6^4E3mk()i_k?XomQ*JdL`#jQ;WQ0`|R1` zn?_X-%bf58;8Lb0*R5inFFsReORHAi68q(KnnP42X9%QLb8-`?6$;CD?ZbA62 z>4Zk@J??!nV4+*#HTqz5I5i1Ep8OgmAQ^-Im^B(;=xa3gPQy=Z%bMu556}Y0H50+v*E)+ z%ESA(^YYWaht2p)M#Mq_7tOUwZh9TA`%0+$rFbKZorxjmGem<0k``#r_Nj4rMCMvx zk<0cmaJR=%-<|=hz8)wo$aS+8ymjU5k=dLnNS9ZgnT9k*v-i4gkhE1T=uf;3{}wAd zg>8+5(N7R~BmD1;158bMFzFx(tN_?m6r*(}nuc1W4f^lGMVM-!_aZul8_3j*!0)s@ zAp+^e>l7ANhI{{rJ3L+}PEiK|aAG|e)_^9;fKW}IPvj?%ApIji7c8^$J}w>^5hd`~ z-0~`l$z^WE_t)Upx=w#ZT_DGJ80zZ53PzS5v1;z=D4vp0;#9OUZsWrr`5hXON9*0u_ z@-Tuj$$4fe#sS?BdY8cn1nANUAz+;@aCQRO_v*neBq+=J`EaB3QfvEGy%M)J7^c=F3ixLqSsDPh zyUE?=55CCPEh}r*9t`Zlf%h^Zpu-yamZvLxwabFJpMa6N56AR^Z~35EWnn>NSg zN}i0ItRX}|Yw*Y_CGP`Rk=&jE#IV_Qx6YI8|L~m5@QX(@SwcrM*mtswe^T(b>Tlrk zMQprtV)*$clWReed{^yo;0l)dZ%x!0$y!L3#N{Z8ak}g~!?PI26;Pu@t9;p@Ah#0n zMygzC;Um!6v}y&x@Kuzp#Q*PZWrpNyF+>>b?3U7@|BO{4PT~I;>;IE!N$)ZRmveDJ zo`vnU0NMh;Rsa$=V_DxZ;KWV+0-#A3p!DiCqCnb_GS%dnYs-k}s6GHoWbGgkd4%u# zk${s|0|w;GuS5v>W5V-ZJ-EgPuEAk>Dg?$~uxB>)2*&`0=pKUL6{V(CJPa|*D+ClV zus;fkb58XsU!=9A zD7Fb3E#~=`?~`-c*w4Vg97x?6`ijWrM32!K4GZsH(3(lC#|4GL&4!gYRAF|O#L{Oq zK>VCT8+dE%3F|P4NhF9^M5msQ$9yA48OY@Z3 z$;M0kXp}F_rP8Iiv`aW!IeRU}OHAw5VBO$zI9I7w1A6Ozcw9a9O{X5zEX$}nqbMCD z_7%BLQR2Zp>vWuvto`1J=x)Z&|B7lfS&nst8y!(mET6n%<$HbeHddycC36)|AyIU6 z*A@XMg5 zvvF{uh~3ae#&=9$Lh;mY621f1VD)YyW*Idym&TvqUq8L4%xo}T4m~ptCCbS#vOx_i z-NLi;i8s%;pH-Yg+5v(@YQirA?9YwNOE{&;FQ#jeXKg0U$v{~9;%a2fH)pn@cIMJb zkCdVgD|@0rj_A_3O3As4oth2kT582Os+|QbUo_9r5;Ec#6dVHw$d^KxjRj)aeykS( zYndT6OMRmVUsB?9Xxm9bB7fHjt2}P+IQc>;v%8ou>*{WHVwy0i;K|A&X!I!B`*jLtd z7Gw+r?P=Vjx%pv9xdWg0%*vZNa=UN2TUDDf+K=nC7{isJ*mCD|FUjfPS7&(WxAXo#zpIZO({xQ5`T~L zqE?^(x#AdG;grbk(CT~`z>+qv3&LB_QQxJIO>fPyVttf=2F3ckN)`7~Xu{rLgDDa% z8nsBeo`0UCKO!r-sYY0x{n>gA6_1`=E)RDVNf5Yw1yW8CM2ux>rjQt9=jK>PM<1!O z^P#7mSvwlfg2X&d2uxGX7dvcytu!j|yia;h@wddXyn;EgIuy3Qfq$t7<^yJUgc- z7q`J#Gu9=m!q)6QHuoKACN>*T8uExN5=1+`X!r}>Ds6V>rRPxXNaQ4}T`rVa7Henx z-0@7@05X3nE=iX5Z^=XY`rnBhv?JHk+GaCI*IS)x#W3Z`7GKFHBffISA@tH-r8*!2^S7!RZtIZ|yp+>^rztZ{Hn-j}bNeBoRe`pBmc#>a&HCFHL?CE}dIPs-NtM@BT;KhJq8?%2zWfZi5;ME_vP`PUQfq-6-=VpxcuU zC0IyS@^;V=nMSD@DK5GkYg^vPrlM|zT-eBvu<-z(R9=n9+D9Kgmj+5JtWCRy&-7F#HmErBgS)Z#8_LYI# zS)eR81fEBnn=&v=!s8Gf;5GOkyq!`hn#L9edh*UItl6>RF#a@->3%=Xt*JN&lgCv8B+-7FS2xg|=7-wQ zE^zFaTqA9#xFw!Dgi5$Pm%#DrbApPEQndV2Xj+JdQEbNvxTma-NF`ChGBl(F zD+Wx9lTbR6sG1KZ=#XH5PDH7Wa9#;%;9^SRBau3zf2nI;7C4sF-a}<$DJb_Cr|Euy!aTOjTt$5r% zmNbMIz?1;H=L-m<_~3x%9O4M@8&0G9M@sByHqKw$`##A^<_URUo;8MVIwmBt&|DQ? zYCBuM3)8K0N4$%fn@iO-3%>h|Zr~(xVp|(aT*N`=01@nrdCEvGv9$3aWL;rY_GXUc z7otON1ZFKv_I}>_Oh&5b*H~Uku|#l0Ya^$W`Es|0=+k^)0xLczH$+={?PcM(Q8sL)i@gq-z_gnBIi z)EQe~o70_PoQga{>4Vc+Fd-A@Xz|s@I)rtIfPc762Pc%rdbpr2L<&xZCE7?AL-yYh zbL<|qTT-a;(qemdIq>r$d+ak~VFjAZW6pN`Yqcw`t>x!nII??=0(~%y(g5RHr~~IF z@XsdR$-#(@A_lISEtuzsc&c1j(Wz~DTmcj?OXHt&fTr#o)?4ZrljLoeOuQhJ3SeZ< z+|yutcK=^uz(#7=Kxma3C64QsnnzAb2#O_m30&J4LSAs4q8I}hi1HE>LkaV8YDPF8 zG4|zKU9(y42Yeg3=91l(XJT3PKTiWHl z;0YxvoF5Ld*5uVmR0qKo74=Ux8Q>vdA5YZL6A05Y>hUEx836#yz)%Z(uM{hs=?$=l zX+wlQhw!nE@S@!0BRu(>!U~}e{YfYuQ@G=5UOj;L+3`iTCxMjMD8|ppsZavkFkOB!3~C z_aE;j80oNSi4NWpK7__lD;q^foX_k8}HZlEKn|5 zGzt9p(~1^#7AwzOt&QWqx|W4CG6nh`!UQ8~j|E1Cur85Ndixk^zp1Yi3` zwd@#fM9M07Wdw00AvLl%o)nxa4huF_WP`n$f+!r11TXXhXV(sGLS+%_a3;107Xr33 zm-!M2^U~cEvbvpLN6ev|jMr$=mcQaZ9m=N})f<_yej*YTwV>32A;x&OWNwG_OBgVr zrSY=_NcoQ&qornsYfpzpN^=y7cw%z$`#7`Yj~o?_ui+`_yfawGGx@IP?aF`QR!-pG zP;rx{E?TSB#4Cp?ld}IL&LKh3h_(o1JC=>f)Q&=)BaTs?0ys9aTP&Itd5|F5MaKDo ziHMqHTbf}YM@A&FSB9nTc&({lE|#HUcE0Ce>H63umAM6cw)1)c;);A)x!jL|V zzrF$tE{Ll0UL!${2&z!SLdYX!GCW;KXlIeb-e|qn9*`Vt9utmZp&l8o5}dD2*^0RK zBg@@D^MeEUwZ4h6nIXzA7C)~$598Qtx`{HNY{*iZ#LD~P z64G4fy%1aFMT~y_%gDCdv@^JtH_!mM zD6)}d2+4{mXmZ*UV*h^_a`t*Yt0lV(dA&|3ZE%uusHZ1aZ0kT_?A79w*xEq3Q@b|9b0n>f31aAZ zb(q2iJmhI9Fnw=g2;6P56pK-hjE%>H>IH2WL~C2GkA1eHeet#dAQydJA}}Af+o3cH zXu>T(P^Tdg_#;f?t3=?y07)G5y%t?E5b9A5-}cn9i@o3)qGYYge%9 zLOn35BPM1888Cwg8ecZrsK@uOmv?%dki%NYA`=-A_uqTvMRoMzBgyR7(%``REA4($ z!e}Oyk#i0l<9+xe7tisq@a1xgzHUQfu+A4UPQ6lrD& zTb2DU$H=-aM>^4j0Jg9;iO0z_v30EL=)}Ci8sx@!oBzC|?G>C+JD9a%j}GL4iaaB8 zyugmB7TO{0lYYpAYM3W{B%%MGABm{dOp;itECz$?P0jNtP@KYaG&XwjEAJ){$v|rW zSS=>LqCfUTgl9)I_cITKATlxW#X9Qhr`f}HlJ<-vBO%T>?I4&w{f){qK!TnjeBq8M z72$@#t%V?3E^>3ix=$nFzU~XO+Nd!BiAfAMTSYiR68x-s>iDuhaE-P*E|gyGsnn4r zq}l+Ptd*(XnFI7>_BM!*ATlWlVxs@(vg|Q0Q(#F|5`$J2W8h>$3&SP z0#8gBdoL*7QDxRan@H3ObyNf5Jjl74fA$@(g4XG(StHva8sJFYu8yVcpTgaw%-AOl zNt%7B_(ub~+)9 zKu?gT0CdG+-I@!I7Z2+|^SU5i9Ci6Qw>R9rLlkQhHsMkLOn1OGG<4=DeJuy&&=_N7 z<)%&=TVb1hIQ~-);7<_jmoZY8IosmMP}SfQq9>1X_*kr!OrHt)M3sQ#K(M|0Yb;0G zxz;dczQRR?ZcuEfy9HiQ)l2DG{II6#p07&)Lx+cG6pUnzlR{-dgv$n4hBb>pPrNxep8_##tpA<01OinNFv)}m7XO(~D$@n+;*TB{bIbm$ z%h&?5oIwks?Sw`l&}=dVwV?ucBL8Z>(7+BFTcI)-5K*8{qd@30hY2YUXoWD56@isi z`B`Oy@#i=bB^xW`zbL^j@d8Ixa)F5p8NALyP#Z@DN}8CE*n&tMT8Z;qRU?PmkNwhM z1n#HAbLkpI(0$3bR#BL~o;2t%fh>YGOi}nxC%S<3P8M~gQv5`knWA?u%JQYzUfLXf zSLx0{e*2Q0Cs3et`qZbBtuLoN`%+8!+Qx{1+lR5YzfG{)y*4tLf0hL86P6mRQV2F)_>iyDj2U zG|&33Qa!EZy>VMWVC9D!g@xA6&zk~^pEWxNK0^LSxc}29BQS7hD7s#dc*wk8C9-|H z%kzG+d0ZJi=~xDk=KmB++?|xz`lL%`+dcCqD7q`^a%V-E@go z**7|el;aK11D!k1u3KD~F*bgas(F_&&>QswJGD7fNqNuO1yk|M*MCNanI)cUIx^!^ z{md4&z>t4@R?3e4_85)OuyFWhTlx9)U7sCkKObhSA1|r*;zZ{TOhT2hDyrvACD|89 z2d}ip96wT4mAm&ql|xzA)gQB|9Y=n3441f@=6V$DT;W*^T$eE+?fB5VqdA`2c5~y? zc>|@dg+JYO&S5FPZ@>Kf@~TO|c9R0D(wCTNQgR2Wqa5pfy?$hmbfk&>mW3+ymX}U> z_d;&El(D??4vYfY7R^ppKN;e%GG{iXIxg`SRU9tq{ugu1vp6$@>q4@ZnIF z@8IA2=W?USLE=yQRNm;WfkrNG$3M=txEG^qyXyAFT|de`dmX*o$KAzDeQr>LmkD=M zIsSwy_d z+)GZ~!~Dlsy0~z+?sw&}blRa4AB)#Ma5tmo_Z~VUAwHU$o_)2~$W5_)+{|mMUr)`r zr{BW>W6wtK3F^^PX_X=|af2C7Ln+nAjDN;1pt1Jeg%``3yq!$qR5#_wNoT&B-*lU} ze>7yer(OS-@uq01=Rnrc$j!3S=Y~EMxUI=gNgj4{doOExt$xQ#Y)ZPm>E?5OKc98i zIy0v(ulqh7=@pUX@yD!xz1iQ}Dp#QPHE7_KJf_9^W7kbrVy|9_O#2igtCPA3+_3#f!18(Yrb9&_&J;C250e7=wEymPbiPyMKFq_O>A@}I%;)OFa; zn5}I8Yut0MCodPZyTAIl*ORvL{1YywJhcP@}bJ-^GGd+)uFwfJ;{xrtCHy9_0JXSt>wYulxJ<+z`F$)=Sm7aY-nGXkhR zA5R`#RDM5xro7Pl=Q}1ogPx-D0OwPq=K;Cf$S5R|`h?i0$0)A0F58NXBwT$a&PldCdH48nU*>`KBAfmD+kVcSdXp#aHu*fV zGP*n3`P-Kdo$Q+q&jKCgxuec&%lFpQU-_lEDnl+Oa0dehx`GeTHot=XMnS@}&*%m7Y*bc9>0qL999U{ioQW ze*7MzO|x+Namu#MN>5)Z%SM;_ef(DSs97&<+gV55g^>P&1=7FI>)UqTrFQLW?3ms=v42hPs}rIQHkhjiJ-?alF?)IAAR}K_&r8qnLh>k|!zr2ytaUFIIFg@foGy~xS$}fs z+U?<#BS-Nuza)+EFHVQ*UNW{b`)I3VVD<9vd2x%JF>^zM(hlCIOR*(^D+wB7vp0i| zUtIKA_eLM#&zi|-&T7HY1f4xCOWd+Zk-`C8y@5S>w>FC>LaB-8T=&EIx2W&_I1$Yb z=81b8%WFz5{p}e;m-&Xd7euctoFDc-`mbB`?2W5!gLO~0ef*O*A3gNYx;-M4L)|D5 z;Vrr4{zF4Iwe9PdGTv)0$^7(gGdRQI&h2?V>*_*B=#LnR1H^Ml-xN{Xy#atQ!c%}$7f5-(#}oqf-c;SAgz1%Pdm+H zXvO2Sp-!pR-Fc?pzaPgwZrsXJ@bya>4nKa`BKdr@CpC>u6U~xqil6FXsKmYqx;_vP z=9N~i%AT_<-VwU%!tF(tEuyL~rk?iY&eohUOaDfl2iZf(-Wn< z?~yL@~P?xB;zScDPN&3E; zbV$pYm+8@Sou1)RZ>J8uyVUKInVESYTrnp7!RontKfGjeGumHy9&|ZxaU}bB{c~+E z#h%y*Z`PSF@9g+HAN3hu*;=qM_C}KtUb$cP`^95=c&k-1zquz5?*IG#s@mEl!UMtZ zz1EwZHcI=A8<>Rad9ui`{lwst{uAA;LS2f-&9uco)eNP zQD*UHLOb@Ypp@+3@q6*n&m38ZrOtUxu=``*_u;kH@SYs2+>U{PoAu{(b>tZ0J?9iY ztBf)(LF^7wV+X|>9jL?aEs5e{Q>WU~BQLF$zkv&$oaz&`LhFufst*y!N(e1i|k6NUj3%kC&4Oo-EpVBPC46+hD0j_(5Qe#zWb{C5| zICDk*#Zk8P7VvK8X(_aZd}aGs?!#T%Oj8^z?pgVq+gdAamSkeLZTHZsgx!xqr?<>p z)SlfC{aVzow`TR<;h7Y_qvvUxBGsK{KYCxd@cV*SO2E8gc0{h|7DUQgv`lluWxn}F zirPKeAOEB4Iv?o(kujop83HCQ!kziID7v2Z$>>`2mXk~Aqx&rM{1 zI_&=;D@!`~3cone82tFP=)26Xcb@ppTRXMWR=O=89&gy@GILMz4YBn1-jSQTu6&M?oc%7J zVpV&{`+M*anaAhGzyWk~-?l}kfqxlkze!U4rR}%X5%~&beG*%Y`g#`r)jq6;ybiU( z*eYc#F--fK?=2~8n*HhisT0JsJv{izm^QVjXR&GN&Cp<9(;rV_YFZqt3!myQza4g5 z_vDmL!%|H3#+^y^xp(vx|0-Q}TJNQgx%z0Eo%!IOeo>1_yZl?tF$ZqxS#SLp@_EOF zAAhE5L|xS)W0U39X}r>NQni0-V*+K*U5-C75^T`H5aCpc;-#wR!|p91vU7@@2Kh)u z_@%;N@h9Rv?at)bksRm7!x=xGSnki;XrpY~JYIcMEbzT)rkbdK6171&zIVJg%g|*d zV{Hh1q2Z&*sr*f626#`aD;f%nO$%Le!l;%Oh*ZECf|6L3k_`ynw>NimlV0Sx-Tm;k zbC!>j+l00fjZ&TI>3qt;;wr1#+U)IkVo=5c8oiwn3H8)tu#R#E*Gs&3+pskwS0a7w z@Vl)8Lw(Fm2Ag7VR={G6)&GB%Bz z-bgf-(f#{J%4h2f=lxHv)K+2eX>*Q3&wBp45S4|7>ra7gtM$CXl=F#Xp{||4??lW@$vhJb0_*Xi5Fge{AiQM(5C;5v-b{bA`kn1e>0hoNkT~jLJ=nr zAoNf~K*ULCp@@Kp*aKJ)Q6o0iIteWxB@`7E-B46i)&)exvKl%nt3lDlx&cwK?Si^4 zw)M@v&-0w?ea|`1b>8dznOyU`fA>t1nLBs+d{3=T{%KxoB57e`_r!|i|0~r(*b?Pq zYYggZX>}*=E3CJzcp=JO+hnG$+Rk-Y{EWr7D83%1ow8agysb7coL}<9<;*#zT9Ls6 z$`vAjGca3t$RYY6s#%BX#{mR&#t?&Fvt-UX!4~JCGR2iZ1T`kQ#`k78lNm@81iM!1 z<@6%MctkLtFk|D_&Q&6W9kd4j>1$4Zfz)M7mw!2}pO+_X$S|JF7CjNAXB;#7u$;wb zI?@x{7*zpnES@m#53o4JVrGPCe`;n@$1RP?l`gO=l|E}QFB4ULqD~z%AKU#;*@xE? zD3Tkpn||Iw@i*J6Y+2yQ_t*{-@X>JS2-xN%nm(_?AO+34{+yV_9i39&v+@Bx$3eu} z&?3#Hr*2``i>0-XptdfrZRO5YlzuDOuniMXx_=q|E=W!b105C;>vOtQ!>ovu>jF1n zO=o0k44T?xce5-(YV3&wuh^)WYWU7LKGl1z+ELH5^IOo=k?T1_`TycRxxA|~ZlP36 zc>phU`bZnLx<6G6Q-<{en9&+BqA#2;Jc#0NJ36Pq!nyFE$ADa)a*HBLEnrT^%kKPx z|BQsGixJn9gbE|5a3;i<8uHhgBh*-1SZ6@h+DZt5ss^vQm3d9rN^VGr$%KzgF&E^;D(=V^rX3xmN4R%i5sbe2 z^_@$e0?JL}cy83tB{}B+DtkPxZz}C`A){~#4ksm2)#Sx{8H5`GA9Nr(^04RHi8|{! ztLdf2JATOD4l94P18#wc56r~x>U+%)2?J_?Ax@Yk3{@4eG?y7WFZsNRScTQT%lRz# zyC{lo;LvKREp*akyxEVxfrZQPg?7MBq!xGm<#^3Nc@LI{J!SdwYeaEpp&RqO&jnw5!jk7e7Hyo9uvW3Z$AZMEkbzupCsxwH>egOl18p9%9S@ znFLeh@jdf8rI9PbT}7@POCB<%tz{tD4N9;l@=$y+3z+4CE}}jj9uhz~-tb^HC}gOQ z1Aa(knz{5P)L9&;?MN&q7NxETfH)gq!VylJx04E{L3+T7Ejm<;;a*I92mDcd&9*vE zHWB`G>DPxe@nrBBoZRI#@*ZHz26wFffua67I%jfS5(D4h6WYFOug9{g(o+X8{!8T8 zk8gkdlKa&-wtxBFG800IO6;NXCo6WE3K-YPE+cyOWsRCS)5$XPs1Na6?(;oCjXRk) zDx-ijM-es_QOgAN(Cv<%i_Ce^L5QOJcf-F4X3H49*1E$~?cT^7H}O(~@@xj}QFHMf zZfr<=He9o~$<`erm^90Qidx=$8k)B0Rk)a6hdg8&F4Rd3$6|$YduG)N$D}vv4(eDg z;Y-yC6o3&$7ztrUFl;FX*PS-*Ly~fV3Uo|`3Gu%ERX$ys)VjLRWfScel2N6-6B{si zV4Zb>rw%1(aO1o#Du{ryc=#(s8@X*mIq~~b%7;JGXkSwm8#Eksl3$dNM6#a8op-by z55$hPV+I^XMl5FHs=Z!k^D5irH|nhAl($w&2dAHp7jIsM^}ZjBkJul)P%FeI@=enl0ZQEi|rzIrMxt5otB+5U12{ zlO0qgC+HK;w|#)VSsgU^UL~~_k_BcrS^XLiZW=akwUA*VS?#l2XU*bnC7J#{Mz_y; z(Q)k1i621YS)>8Gt_cI6tpfoB2(txeqk!M?wLGX_!UiYw;MuWJxB8U|tIUsQi?=gx z13z-rwfkn%_A+!gQLR0S8@fQ8463vuy#*oCV4V#TTS1*zIzX$RbPFET`PZgU8F)lg z1Wcm->HqfAH^ti*(=z7YxWhNAjC%DJjfu`bMKqSwc7tb>G;*_-)=s(!_SbR&2_2ppv?Sp`IhZSLq5t-xX}TEWCJ=@$Oi_9(1s13pp1U>x+#&Rl`m1QpcDnX z>vnr1FqeSBhBCH$m3dSz4_H$sI(Vd{bp{{ z%)+DMW}j9uV?%a1k*b5vjr^8VNBPL;lg0yM9rQ}`O=k->Yz50qMz&kI&sK9y%JwCC z5OWs=7jWrc{D4P*+qc}auY(*Mr2^+{MWP*8W1umf{+eS~ypwh=yg5N|FZGa&r#Y=V z`lD@q^PF}?@wEDcvQltTKTgtY@X_#BnTOyAtDiivPs?Y8P181H`-PuBQMP?vX{5`a z0G$$gbJx)%Z&ARPgpAUZ6S1yFKQTfcsRJ)N9UOiVYiO^GlG<;rIIiqTPXE;w|F&Q@ z5I{+PAkz5=F&msO0e|tq2D>T?2EHg39>m!gY9MGHn|!I9H*=4%+vd^tW(Ej{ww_h) zwyq(xoXEuRc&|OvVe9Zt-5dFyW2o>LW?d}w7xeDb+$S+Pz@nim#_ZI&->!6FU zNFufQgJ#^0_PL|%sd?4HBBOEDaEp(>ZJ?Q?bd{Iqw6*X4-eGuLGH00YXtI@@ zFGas6Rt?Ckj__dptVVadCqetBa+o`Jz~F86=-2^z2o^yb>?V)>mXpOYfj{a(vkF`3nvp+lnMQeXc>AstC zenJ}lsaXa#X_jeClRwY^7binHrtm3Z3*%kHP77~+V#ANREy85eDP zznlPBTIJ&*dib>)RaG>)dA(|Bb=brt!B!uq8}|eVAV2GuX#`;pE2I7P^|}W;wRTPy zY9MPercg@WN8OMslENsI>LCU8H1lykoBU}-W?i6h)VfeuE5$yDTw9zsnkC_?FkhP{-|eyo+*qS$6>@73Ph+XVhl3>k!65t%Nhz;0TzeZ z9mR&aSVZC@+F2AkrN(8+4Q)qMifgIa(`j`&zzHWkw;o)|9)9D})h#+kt3IcB&W#YN zvi*3HuMuP9))`x@Q#J=8HT!$B7ox7V@rTJUnt!5i=y|VY%Srt;fA&djJ6+#=zcz5{ zYw8uLKdG|pb!|E_ZfR?-3r~omE-AW69S$>)31I#~bx>GCiFWVl6=}ovp8LZ0OdO1p zfxh zE-qNn`=fEh)%9xkTf?=iDuvs`v?aLLB@hb8Lg3=_=3V3DQ!GEPj}j zPEiwJZ*8UVUNf|~5TB)6%SpWynAzmf)sFwc7JGcSJ4Ak%WO;V8jY0Ij{MO&?j4#i} z^IsKg$jdD<^(`@nKBNm43b;1oFSdPtqL`sEKLFaMLj{5JQe=MQK>Xu!c)a&&$htH| zrYmZ}lHII*>m93*>N#HW2;$f(6CPz&2i4YeaUN2AChd!1CCETt5LKhNzUP3gqqey0Fj{$!D9bl#KHO4BX`8NY z&ANsi$xhV-FE?=jyT3+_<~ay@6)GO|!u3HA$sRoiA7xaHVlxX2_h!$1U|Fj!n6`X$ zID0bz!wH3PtM6O>&=8uU0C@&bFdLr@Yn`EnYEqweyOrMFKVT4d@QE~40M{-!8`UuFLV z(Vx;c`+*6E-OxntcL`lL$59ih{ILu!ee89&mZHF~gTmw}ovwMR_yK02oPBHDpI;!+ z@F-N>6f#)RvB3M-gAcI`i3RSNLA4J27%Z{klbT;z1clfB5jbdX8a#b5Fkq_tUZ|>8 zx{INFf1hVbhw8>lPU~+8Bv^G*M7&}(4O8P_B8(6r&^glyJ?H7#Vm-;pXA%{9)-i$g z>kg(GgVO5t`0Y!@aZG!u81}QB5`xoU?tWlI#TGDu_)YeHH^}Gc*00W1`?3PBxGiqq zQhh_gkLdK1s(FnSoL(c{J8g}-$004QQczFS2S8^UA#-;rC$>$pIB_f;;fg%Q-d@f& zwA-s*BO-f{7)@$3S(;aLN%CM4=}T(4pRVA;srg?eqnjPa{zl->xlfJ;^m8vT34zLL zO^D0#qMu_)dw3mC9 zS6SVlEY+G=Jark=q$pGUjIjrj$)eWuJ`jEk)@v6&ecPWnhW(-CmUtaKwpcimoczq) zqRHZyx$MwHD*h)Ti2@HD5UouK=>HiRs%|iZ8iT=om42>QIu}6?s`Z_433Wp+-o{w6?Gcs@`2>KoMmPz{yowh|V);KY@)L zo%^+v6+hN~U=dDG46*(VOJY>-vTIs7=g#Cod2ydh$Laq#mExiE+PK1XZ(&t6TmnJbVmm3 zyJd@F^nA(R5)Ei+ekl*n7}$&IXnUNC0|n8LpJeqN^nTYZpIy;X z^EG@sr0oG4mQ?P@>-lZA;hAVV@RRHAc}U4>VTyzj`5#8SH}MA;H0k&_Y@9Ps>i7|` ztd+@CIp#cKn>~083j5PnaGL8$Ji2tIyminbpgavF3$wByN{4e3dpn>ScL}3MR>i)W zVI4!_i30>I=x4FITyn*M_{_o=vekzX%vc2U{LqGdz!b$#PzgRX_z=}nh}PPQ^cSW! zt3U6d<0i9-(2aLr=}|rg7Uc}kHs1omH4r2Wp>iD|0mXNr9=qPL^%}|KyN1qg7coJR z!?KUYeC12NUl{$%D&`z zW^X(!@3-b;Fe>*FLLbg_UACf&D{{CSZg(){-vvpGU}-uxz$|2YTuE(URJXHh-!f!* zs#B}>AElou(ojL@ScE?Qy~F@=Q0W7;((q!WiLOs>AXNbRk}bJ8(UaT8+h^_cfrlbK znl=ass(;$YRHG9SKqDKqfvt-`GD~VFB2X6y((r*f6Al%Hbm`}C*h;g4E(7g+cWwOI zMDNm<;}ggZSJGdmm}FmwL2i?<0olGh>Sk$Q8*fR2U@G+wmCl~&#wwVt{&}&st5oPP zxCYLkjtB2QB;Bq^oaSFo8h)Jf`R1r$(49l7(dn&rn#I+pnrQ-f4?gDt9(8uXuYt!p zd)d2Tm#jf6L4k6|v``m~%DModNqMmmAN%%IPc7RJd@(jIh$WmM(eP_SZJVJFd+DtS zhwk>L2J8r&aBNb9|Dw;z_06B#D()|5ONGJcbii7$+~sCAdf`93teV1?vIcC+hQR0R z!s$|l zSks*1g=iY6+I{hrdL=06vWgI?0F-rFPs{LxkJ%Eou+Qpi>OWVs6vm5AjOyge9=O>9 zULuSIgJ-s2A|(oBB1#-=We|Km{_I*12t%8*icrnHoVN}cB zaiDgKk_}ih9Mbdb*{azFTka|D!z=CupC%cA<86$PA1YTX$e+xpm(R?Xn`t8MtsJ96 znG2$|RRzsM3k%E5OeB5`=9&pF(VJ2u^DVwTMSz@xJux5K_ovmZ->o|L+@lIDX5?ns zFYj}}>5)wnV*&RWxUA#aTm)I=*CX$uF)a4)|8bhd5ELcqJWd!G9&ZExZrWkiN;)3a zS3a9CtCT(+*xbS+bU78rQS_#)s8e7N9l*UTW-V}m+>;L$U!$$QC;p!fj~~)7oRbeW zk>7TmV6-e}tI765S(^@LpqStAs1vO4wtkAIkd;L@ZrnO(_@U?dV zhJgLiUzVk77fpf3R`W3g5-{O3M<^i22&U20%K!B*j%3*1E3SGvyPG>!t)a?H{>q{$;^1x zd|0CUN*c8t3QXxCe_tRoeI|kIm4ELc-dHi}4K3ggyOKMZIyN~;)%*V!-X}94k1-^Vo4rQhE+^#TmOW@BO znGy^P;FAlvIvt9#qZW=K4Au2_a~Cqe)2}VW26WLZeTI`zQPpV(b~6G@J0zF`{6vBJ z{LL8xc!Lc;BS5;piQe)?O(Jx`OI5XqUuF7lw)#kc?E@7a0=3$es4TNR7Dx5a2{Kj3 z3L#L{Px9fwHL1%c*Rex%vj*$$bS&vo=C(rL7u09(e7|Hb_d$|HFF1$DDtK1ul5Nln zlBs~|f0Mb;>spaV$Vb*cU!;FB8G?ltHl?4x>Dk?*YaIGgcQ-6Jo_k5jq^}-lHvp$H zN@2*hCeow~x(Avsn=<@;`Mpd16+$$dy=DPz&n4Fb<(RL;Wy?Xmex~Xx%xIW>Ja@+b z_i8)YV$tG=|5&XieSv*qmT{-ndfYyJr(;T&axXvcs;5A{X7Y#$c_31?Arwq218XBS zd5DT*i?3lcCF`NORIM1Uw~N}&kQyM%0k}QNR0*$TZH>z~F*>O!-KwlGYqJD583Yf} z#zcXU8Bi-#V<4}iL#tv+5CWWI00`P-KK|k*h7i5<;q$T7YQCV=?oy>D*<@BVh@Ik@ zqSQkV4Zr&t?%9y59-yt>cPO$s?NOiY^$V7TAkTv^och-x*rKIU>yeJZZIk&1{_a_A zU>9j`M}$H=qa})Jy~XfXidM*=*B~DUEN$ciVf#l>?hn3GB|Ph10W&s?q0C_N43g&~ zY6;}f4U^3~*4M=QiDJ{jw>Zf=OKLs=OM?_%r6y=$r=9S@Sh+Q1ck4OHDd#EI6X!@c zW;i%83pFL3;5sCsnv{(pBoDoi6!>u3{ zaR26o{=Oy0{VQo*%>z;5IPH)ICN_0pSH6wuNrux3eyqCp@x@aTzuh9K9GaC79yJvWx1-2W?_ICNFZU?Am=b zIPI}j+E`RNJVR61q(z=!XS_G{kZ_tEKRCuqP}nt7RbdQbvy*Bn7J4H>I+Vwgnjk8K zN^q7f5Cd;A;&YG@>zAz3ZE5~6uK97xB!ZgDQrPOiB=zPO=JR4*p%FeXmV(%+z%(=J z4b|;jMb5Z=wj}PmHeyi!sw`)cbuA%N)81g21y$IF3IQh&O@k+H&vK~QIlTqi79?37 zM|^E(2B1XI9P#5-lEt%Lp279Q-&uUkmP5%i0}9r4&%DphrYv@A?fcT6iT-8{Ulb8b zjS1em#u1%gS8TbF5)udwxEE1)W8yBMC8c={^`maVCYN=|CdZbmOpBr`5f?_v1S#D) zhV(%eD6;wIzL%yLF-cldY4<=(EJ8-Z02PKCGO0sKK(z%UWayEc{AWcNsGtg$&nN}D zz8hup{Gbb?GWpa;ZV-SN`e*8GmU`-{cPxBjHqZz0n`6K@71C3z0$?bu66oT3#Uc8P!IDDcap(B9NGW!96Q1ir{KKkVI) zLu2Ha$Psib&C-^xa#YHD(cDvgw1)e==rL*ol34lx2Rnb*@o8AR`yomBk}~ZS{wMYA zw~@6Qe=b2JKcRJYai<+z)GMdjRXN~HuFamQHY6IFBkHYA&IHz-B=VNHB>&sC+oAMV z$D^1=rA+7fWu~!bN}18#u^?l3JXo(BA{UNTc}E))saa}`JB@ygB^4lyn8%BmwZj|4 z!_F;m`bSU2YM3J*Zk`QKNGbuBx*byI#J#vCJ{>5Iu%LoeK4@+SX79Of6x zh<|p;$8i52H8j|#9CYYDgqe-uJB#c$#MI{Lr~zgKN2<8J3hisM!TtYUU~C#(t0j|k zt|{{E4QLA!zl;JkZ_ACSuNcExz&O#dVbl4xU~DCm91G4Q6wYZ;dtxFB2tyu^Ym6N> zo^s}tNVM&t6jA`r;h@bP zFuf9*yOAtM>UqruciIj*d_L)#8G4c|Zq+bxCV*x$39;G% zCK3XepsKeC1&iT*d~md5{eE!;7A4IykK}4k2I_c5>3LJN&*$v#drR!%Yi28VjJA_c zFrq31w{N4VTUb`KIvEM1=(tK zwhnt1K`X0yF6#`>+Mm45jBo%s0%!{(o_>|np-g-RnXHcW{z~{dVs6o*eNw0)>d457 z7;c$o!$<@fLb5hM72Rur_f3`Q^sob!_o9qPE=_h7#98P<89lAeCRvi~>l4APXQ9t{%&E#y2K0!f@o*FSd56EVaW( z=2uo^E0@qtO~~PZZMpVOEvgt?(e)3Gg*7h1&C+cr5teN4{x-vD^oXu;l3F`joRMc& zju~jy=qF!?XzPca<&;7a`YkwA0<|n{Hs_{CxUusU?y5}|qd7zmDr+hSttvuaH^*QG z1r``^&kGQ>m$PLCL;t95GM=#@F(HbTcEiYb@_|@-97*Eev4W)IRL0NPX=+tI2uGl_ zG?=5GspLX1gJAK1p(E7svg3qKwKwPTF_*r+m5=@NRGl91jYrjoE%4@go8MHavXls< z$oNW(Q1?EKPmIK;1BGmM)g6VM?>Oyx>D9?!iwVo2Lm*oC`)J&j?#8&!w@&)N_vB2$ zqkpek%NH1|o2B{PhANArYkGbg$lMWH(tE%@S7?8moNYE9*UA96)b8(33hQik6iu35 zPd9KInF8%)5u2M@=fr4c zZSe@||6o1`zr}32e2fi}U|m=>cwY=g=upNBR2ze8M-y!Uaxs<)$S*I%X-9W!JhOqp z{3(1&_VO|`!9dxtcg_c;-?WEqFY_3lh&1BUu?M(S%s<|?e!H_8&U9*ER#5PRza@(% zZFNVqg-yjRD$Vj~glKt!7iEjGyn`9)~hq0hNWh!L5=+3}}WUUE%6EAew>6ctkuE zUyP=aq1aj;Zn_lq&FM<5PZhgofnV>;_V~=#s1?NWtG(OC=<}hE21~!4Fq*!+;nvHNc<$5{Y@%I8Jjh)& z5O-s33VGCSzsyt2O@qtqj#tAlw1pZ~P0S!Z4h5%mk|nJAp34Got9ZI@>TemGzUGth zFBtuPrrG_X8OIB_G-lH!;+_-a91IpHfG!zc$tPM6b+H(bgU^M4hj-36!Ad>5%n5l` ze8I0`dFXx47+*rVy1vk(i&UA)ol>~$l3KXX^)jzQyy=vJx zQmCzv*FC|@(;oJJ`Ewx)2S->kRUkDpWtLh>3+w}Ff^vpzT!HCavsZBBy%>jMzgx|^ zZ?~)nJ8A2qtXQWH6Jd)8&r((GNU&c`aOY%a6ms@X?*UX(qDc!_(|ZHJ*1rbbU-rAP zSHIOPe(j{~qov|=aZM{hau79T=YY+MMpq`HbAl+>Dlo^7B-$}ewjq&cONA&ND16f^ zDU6wAC>F)Nqd1q%gyu)qZQisx=JTmqgh3oftod^k)k7wND%iveSQhdT>&qDGSBUvD zs$&^uOsHtnk>HqZsL@EhRS_u1!{Ynx>ivSc7#BSqzs-w-QO#V~xZF_`^M=BSmQyT% zQ78*GUdlkMLsiyjjGry==lsK*`L3H<>nzUpdubr;y2z`(;GCOS6)urOUI(XKWLnl~ zFEmw)U2?!NmJop|8Tc9$FXBV3OoZ^9%Rw~fFkr3UOS(;y8G4<5$l8tb&_Mo;A@xMG z0+=zt$9ZoOf`9{TtpsAdW=bNAVw!tYOo|M}qr^U3F`MCX1vfPLFo%KQvkM)V$RW(^ z3jY42@lSkkD&wLiEw6;x+HZf;;X={2`n`)Ci)Zy_2DOBsjr9??DzBeJZ87!Z&dxsIC{GgoCR|ygB z7G^mDHXp32~F3z&WY7~qi|sG**RtI*#)JkP-!nb6O8Hnjs>qAYeGMwmKN zJ`oZO(!=rBPAH@f18uqBm0C}`Rc6}))!4J7xIDggpOxGA`4!HqP8%kvq1d7X_ZXZ5 zH`*fVC1!9m3CcJGPV&G85#g{_46BIV(^&WsX{KHdiQkcX zG%Rl0A)qD0vBD2-aclQDoN*Mc&Mr30DPW!a+_CwWt&46>O73KME}nDWZqeu4p=4Il zV~S1(&1d)aMalCZ64QOhoEd+V07fVf?(ezLY%+HynL}BCL8P2{GiNtt$4j1itBU$uAHEeEQz%+i%Zp4Lu4bIeep}Igss2R44n$Z0 zmKfCT1!|Na!GipxMZ+vjKO46^I|CvTz@*6W#$uD>M+!?l)+2TXPhZ0r2a4DC)g>Ur z9&oN2%-*ZlXyZzeu1E-Ob3kMege04c4o?;s&n=W-V&|T>O_(tGBRb%B7u~slJTncc zmnc>nKGuF2lJ9tPz^J>-;IYqPi~Nl>Zy6s$lbkXqg30gboZP$F&-h+zmB0oB9U3}A zRvj5+)H6B!{=bqhh4-#&opQ7EVsOD~`!{J!gj{ne=Vl*tk}AqcD0;X;B<*;^tle;u z`MHI*O^Zs(kUQLqHGemgJ`H(W71|kHTpi$T{6hSY_hwI1{EHFCfM_RMIeLP7zEh#e zxo;QpG{Nq}3eyj2rplIj%=2;V^<5!x*9;j|paloFJCaP3X~ucyg6~gHuDRJgXH!*t z2+WN4T$fLlQ_qAedrpLn-EbZg*iC%wj_skbr&B|c-vUiJ?&YtniCvMbhg zE?r?|dzM9uk{-03Q~Nwe`YoY&DvL1dAVYu&n*pl2ghd!&VpA4jgyD;LnlJ`561@7u z%&L^T{fc1vvWXXuWL=7$MG}}(jf~0JhZve+s(LmxmIOB_z)pTk}mcnd*jO;h3B)fj~X^`Y}F{?4uvuTXPx6?dsB79`CDQa|IwTZ^=eERAKB z!9Bd|dVDIpbI8^K>WuBRI7jPlMvli?2t+ka!m|+S#H>&G;n;;0$7`&FkT`6Q<-zN7 zt1FW!*9sSfZpxndC|`d=1oQd8kB786LzNJeLKf(2zo=eqBhYrMJ$3}@<2c%F;c0kh zO8qb`2cp|vL)u>Oi3tU0Dgo#6L3$W?h=YXdIA}tIE2(MoIHUPlym5h3LEMgO-{8L) zbXQ&HH4z!mP6mfdJQwzcMvF?p^l@fMTblGh!k|HMu`uR5iygk7%-Pn3Y z@Y0ZlG~+;nTzd#sYly&VM=FQM{u? zf5||eDO8B*1<}<@5Gw-UN00^;IA;qs2jf;DAeXBC6=iCv-PO68iT$_G;U7&Ub*(@~9C~{@At*yz;pH zs*l}KhkYH%R?@BVgk5qwR|{*cBmREZJ7Qdb&iX3qA|FG3C5Bj-0xWUT1P;31q97r> zX2cL}usc~jg>{Y9sb8({(~ZH~!)H>YwA7o(vp@ zBMEY=93>(N`+dNtE6x!bN{3r7>svezzU^L;a(_VnbJjmbO~b^|OD7P9tTu%QJ(ENi z0~wRBQqI!<67|~3V3^v(q^;S{vfYj$={n!V)puKKk8w@zqzQY27`F{4F&P&nRyA*C zv@?Bq6jwhhDZB;oAGb^_FWr2Ign{c>~A*7@gW7F)&{U{LB^C2 zS+;jK6CQ3m7b7o8nvyF&6_{7(@~g4jzOMNfH7GI#Wt| zS-i&cHmkZ^N8{T($-Hu5Q=O-PL2X#IhBo_Posb+*{IOGLI6D{kRoC$A6|Vcx9h*^38CWh^n^iZ&cF zHeYAkU$tT}ZTc}Rgk`YB$AD}L{gpAhCSoJ!7=ClJJgiq0<>wr zf0w2V#K{qr6%PK4Y_Nj#ndNsFE&_yj;M&^ac=B}TH*0RJth#`}3Kb}aP&hjOVKA{8 z)z~wzGChAD#r3gy5Hf_4y+gd z{@>m1wOd1OKXin@!R~c0^UI;(`zc@e$h;GrX=jWpLig5f{m{P5Cu~su$x-t*nFVTk zA08U=mpB{{J~*TM{>Aqe^up?s`SgHoMTa_P5=VxfxoiV$EcJ}1)Yq1ggc;^Cx=0O1 zBuSr9oR{5;tK9)r7R?CQ$h)W9JYjBp6u$keJ%}ngnl+_d@|F3&c$>WP46ll)qVklK zSStgS%Ifyfeu>j?oJX}tN2QMU)*P-#d$!S9Os>C?y_2PgoCx5TP1>C_oVgl;CqRog zJ$R)ZO^$TLqM|_Jfsn{~#IX0y{AW>PYKcZam3I$*tuCD=MNJtSCbUy?)y_xpp3SZS zeu<1yzZjw>l6b*~l`c>Q0S51$69KMa43jAKyD+=1C>u!k!Lk` zo)UzH+8QCaob&p}H{KH76H;SSe%;9>e$Dbm)dzCYRz8b&a-DphQC7o_&>|$dDbO6J zd~Dls6=B%jC9YGPTIZ04Gq@f{DI=^j%(Lguu%-pY-SADYze)qsW>8C$uQ05q+bIOo zFT2Vec0Mc6Oo=-)p%wQ286sRnwYj}%O89TC&|WbZWnvhV;GkEB16($-5Cb7dQ)mJB zDfrrnG-hmmJ#qF|W3!!IWcB`~9}p0N5;Inv7-E5tD6n`hP`N@g{NQ@SU-%e*j$som z_4VZZbc-LfW6Un+Z6Q7#-q6e{ZX~Hz73kUSob%I!hjBsJsp^CFod-@bK> zoha+BXv!l|vA=Tv{;OW6-59PJqQAL+FWy%+cL4mo>DId?*Q1REDsIhdw$8}_rVR zinU}Qqa8&FKmsY-e*YI-ESX`8zt;m6aXB4KgBMl81*(-IDz1dsMpdJBdZ-}yi+QRWV3h<|og57~A64XCqG#ec zQb?y$KsSb+#M3fMm&8qHga$jJ)e3jZ%0pakBXjsDBDzUTtKlOO7rUn<0UK(#vDBA; zrq%AoJ3^aBA}V6|*k4g1Teorncfj%NrDvsHpDw>DQL)>vajw>8)~sp*GFdzEY@AbL zd7<}ogO#$jhKwj+ zq%h(WDW-9x0$`}rA5meev?UFlp{2|_T zFfR=llSL6Vv|87!WKIC>gWSdXq(hcZo;p`MXo!X~_AKiPiJLwd{&j{l=}-Fh{M2se z(V}F3S#sXC_0!@}66;&X2Tk3T1~W1W>R!Sb2~*sy-reyDfn)-1ByApJMLWGthyXQ! z4pTm@wycHxILM0JX(8b3iSZPh8d~v5s?ATlG@e=76EUYxOinnJ0-Ve~8U9|cn8it= za88T=6zw{axBP#1v*8HXIG%P;7nkM5<>q2hcOIoF4Bp@s=Sw;I#ZP$&>zjoPd>ZD=>OTFM77Xr4#{>mac8v|SQ7I@N17EXoJ z_J9!Rmx~_6)tiKbl#{&v%^V^7wSEXi600Hm!tdCBwVVCGq2xQZIHbz=Yut{HaczmD zrWcDS)~)8yJsmDw?)XG$%2@CXhJW!`a5LMo@F7)s?a$)3bpg5ioBxitgTYQiTdYiB zxci|w{x)>vq&IdOO&*%@h27s3!0|p&XoA^lrLU)hf8Vvzzl$syGKFLJwNZX9A?^Dl z-4j@t-N{~1(X5Hm?VJ0?C#~Qw#mpX6X}W~RpiTXtrgZi@g!svS(R19H9Zl0euBs~7 zELiEq7wBLsz0~~Y|1{7%L-BT_QP5EuR%y63ylI-g=$!*@1NcD!pkcsM0PET!ogjTT z{3?AGehKUo=_^P&l+q4mUu|2+VjKWQ0?6keND+Z^5KogrRog-f9iV6${K_2`%pnq8 z+L1Prv^x8Tx0ge`Ptf{|V&+C%Go#cvI8(3Bh{QX53Lk{#jZytOHv!sLdG4C!78iM( z_43txjnBd0V-lxHlfsr-Yp%rT#&a8?Or6cq-sA&9h$(mVTXM; zghcqt#~^I#oB_j=aHjlDLVQcNsG%TI@velW5*F8PDdTMu-xU6bg@6S5^0~?pH(_y3 z(XgH9=#<5?%eG_c02mViOI!W7E!F^Tu%O~^>~R+iI;UjOQQ>qxjvr_aovhlMJh(++ zllUe3c|#M=QDXo-MzI4&z)5!z-wi6ELvSTwgsRLCbtqFkr4;~-5%k_#FL)N+V~_V) z$uoTj7IyeTqyG{dOH}XQ-DvxGGW@iSa#hkn z(vPo$PL>j(=BkRdGY?n5^`?L4E}Kb6&h)6fEd)9+Sn+Dq%oOzZC z^y#bxp_btrZ7Gky)OkOGDA4OU|f**v#~9F9Xt{`VBq)z-fjSj$K|5{k_c_u=CIwBd@~)EG#w2SR4C7d+oV zcVzyt?_w}(BemMTvah|}E+qEQ;(TJgzQ?3-+#PZ_m3V9T>0#%TcZVy4fEASuA{sY+*S~2O$DJl{h2;>Hd%BqAL<(A-x!US0TiE_N<`QLI6n4bBQxQA!%&gzG>WSgpPj_n? z+XHRzs6Pz2V&i>M^OE%!rB@xSfBd!9Glk`9gxgg;(*J&;^YaL12;QN!dj$Fl( z*7P5TL!6g#Oz%Z>QTq#l#D1l%lmdm2fYhV-MsSd_MNqh?(qv#fa*5JiGRB)pxfmB+ zjkfP86Pk`c^#yt9cOvJIx|3N}LuCgnE%em7PVHF^BZzfs>}gvY-teq~Fk}J$JwWaS z37YNkVEEi3L}CbsB=lfF9bJqE^|Q_U)0umxcn)oN?wTrR#&V&qPJJ2tK2zug3sehB z0FHy#$s%8vlXt7J#9K2*-A_$o1hpFn5iyVQW*OguG(_{Pckc}jEE9i;>-+wMQXMY& z9`i}6)ojl@D|gvI;CHaQ=Fj$8 z2Y5>Qm%^DZ@|b&p`(mW4S>xpAOdz;SiU2>BCM%e@K*g&W+MXKCCj}L?EdDxFU?BAB z@$e@?zxA$)vvT-UI&eW#J8-S^a2ilpzyB0WEut(bUKm}tnTzu*#`yA&!1Zbav1_~~ zcz~zKLTP+|(&qN)KTDlCujszl6A3;kd|}AK^6&anqVgD7Ben zVGa;~(O3sNX~~jYinJ%s9)ktW)Yvbr@jDF8CYOKb9;Ulm9{$&>oFd(HD@J%u57W9} zEpzyfG_N86`x=On-YwrF=SMgM0+P|a2|U5wZ%ZZIS3ZfPck+7+p3juD8+=;#DDUs8 z8K|@r^d#Yc%ObM)zr2_rPTNJVLczwss04>HoSptNKcxyBxmDnBN{ zz#K`^SHvs-E$}d)!4PmT)*iy=fI94_7}@7>G|z8`$TMeFa_7EJrQyP^r}Tr#|L}(v zhkxIfbJm7eT)#eR`u%w?Ziuij1KeoC)G97~Vw>;zx6^$KMY#jo+@xQ&wYGgdWVG|# zESq-5eh-0xw4~6}RmWE)vioyyuF!YLJN@ibT-g&@2a|G0Ik^^=JWlpC-z~wz+WiW52RE77iZefs+R3 zr&gKBx@OtkqsEO(JwU8P(XvsEguO2>4;kmq2DI4_d zB8LoE`Ihb9W7GM(@=}%jiLQ4UP0rZ=KckHKzAypj42~>$bC6U z!%3}+scrAw8$tpn6Cj2nji*50rI@~i%H+y}j$HZd#_szwNX}i`#aA0Nq$Dj8c3xMP zOe{QpF=r-TMSeMRN*33UYa*}nUm`+qTLhRar`9sgNvsEq+r#Sd@P_j4&r&#D=EWQ* z3G*aRbAs*}ea)~gFmM<5%qV{L8x>u7 zB&$!hsworJA26`rWtvZla+e|Lw@q5^uexvW&GSU#%ms~q*V-GZ&(a5gTpn3-0)N_5Utc; z6R0@TmCBZCsNbK=nR_ERW${hlYKkDES6D;xFsB&6s>rxI3pi8rKT@%aF+Db}tfb9DMH$c3=W)pO=`Icthf#F&38 zP3|5it54x?1gOrL#e_X3)a{-n-aF8nqEMsDOW;7WX0XO7yi8tCm`qMqfOr^~-dEQ| zPE9R)-ntu7wwr&Z%ed2VXdWLZD`M7g1Iw8=it%lU#4Iq0lcgvdSYVScPzfZhOZ@DF zN65L>46aeLa&Q}bbckU0>8uZMOVFOnbRtT*W&X+bGK} zST0ekuv{0}y_wdA7LGg{%SbXi(S5)IMS4n)T#w~K=wu335(lb)t>?5`jL=+M3ivB{Fh~P! z5Io2O!(<_*OAHtwF#xRSx)fHuA?*#*-Xs)ug)Tq$e&*FHs>2 zL)#p_q~{EiIXF9SxdElPDPvqGe{2z<2!QWOY5gtcID^1Cv4?13(d%4wOQT?uH4TYR zIj$|nPS*-yU4=s0V8M;CYRrIa&DrAtHsocm>Zgz>actg+D#T2POs(ss7$efBi&Bi8 zx+oQB5swK4ce4{AUHlFVUU+HSb0@I(ULD&5QKxM#{3VRoisZKYk3e}O-(8c-e$PgP zVWQ;Pg?+L%Ic&jywRsX@?3)TmlR9rs+wgE{ns$d=vBL!Y{X8Xd8vix{X^|yPNkRyA zua_;o8Q_I%4LXFA84G~{lBAe>kY1BRQ8>`1+EdYAsGJJA!con`o04=Tr+8??g&;EE z8~{B5FoOp^2g9=3GkoYc2%_X0WI4HRtu|>Tl~L^t=;k)0T}(_IDZ4MJX?@$01^i=C zc`HxFlBk2|!$+BO-AYm=tgnWv62?Sjs`xN-3s*@iW(54U{>NsV1S(987*0&8BuN8W`^;*=BKH`ThTBNRlODC&^*T#5rnM4U+ zOaE5w-l^Y{>gGUp@+#BQ>C5eh>?ah<()1?4o51D<<}5t`zcXWwzDmgC7$h#`u1jDn zxZ;KwN~JS_Hxu{Gk*{Ek<3*e(x)Yv=ct}~kl7?Pgn!2RJSV}E=Zk zIGMv=G(41spgELgJ$;^~CX6qF#_`UVPZ!DUg&j`3`Ri{a&WulEL(v5`@oZkW7rJy@ zPc5vNE#blJ4B%W$=t6)IRKb*1K+^+*_onv!MebR2LoRP3B_m)z85bp&(S4)p1_|HC zBf|_Cg)(dMiW zw@{dOo{lw2QeBB}$juU@H*aASr1>1Qr1*Vn!w_aaD93eg4++F0jNqbOkzHU{b?*s@=a>1(VKr+njzYbF1XLgzjKpP_ zUB<)m3^eLOJ`;pg0yY5lY#6{5Ipe1dlsbxpN4a@3PruGX`&^Q^S+KG0c}Bj<>i&jt z10U<2L~`t(s3#`BuLv)0mtRh)k@Tssd~bYAb96~I69bxgA~dPA$~i)%s<6x{x-0fZ zb2fPM^+37>JCvMzNP-4IvNOLTduqLTlgB3>yw>Q+lDZa7!pXHG_0enz-PAY&A!6DsE zZScNY{UTrK4tYS9R%)v>SCA6rU|z87*cu)ONMH%L=pGCqzz--fVDSY~^GZ*_^!SIM zWBO@Y@n00K9qsz!0EZzavcmYCxsM7!bSW}Kf-*c&Qx6a!#1Ba?QUR#vT9LY)diiE} zY%eXPW%EYLQ?HA?IL~0)*2c2@hy&eYgoSUW_lUW7bjsuVMD$|u0sYI{b{%$3?$nu` zyvj4-U6Pir$3SWi3jaL%Jbyxm9dDK))dF?x`T;rTSB@C_ZhRSElo#dbdY1=TYL_sq zgUlx6_%ObNvYEMkHR|{LpFY~d4ncp`{j!oElk-)JMLIW+9^?-{thY3EAn8&pM%kCn zaLcT#EzOfl6nIbzw&s-Pp{}pRrY)l4GR4z|di9|(7TaLwy}mZ-EtE;f8_==|5*zY= z2AWyhFjcm*xHJsN7n#mSJsxM@*uSw@F~4DBoWG%Zw`JaO>fDDlC)8CAI~$FbU@TZ< zEC1xzxt*25#og;>Gp?~q)~4OYLPm!Pc#w}YdtO)qN!Z{__=#R0uttOqCYGooEIIgX zLSp2d8yEda!Yn{c*~J9E2@=P)6D#$>kSUa3N9GlnLupgS`!X;cFP@@Uuu}VO(UlI{ zaro}vmI!U>cJ#iM=!VjU~1l7Bl>-(->4x%}4 zIe0*vaAolzM4SbD7(5IjAVaO{P}jNbn$7O^deU5>dxxE?Na?pXyLZiadu_JZcEf25 zkT|pz4QzhI+ASs}96}eYO9#S(VWW}S^2{`!lrQT zDh$G9l@#s0Rze0&GvWkkA+$$Sw{CoC2j|4E>4Nq#wG^pimXlhlj-1UU60jvPj`{+b z;itGPlmNDy$d-DE!ld2R;{_;QfO-9I#;{usx*e(sT@sE<86Bq)aVHj*Y!Fc+$sFK&1t zG41ZwV;C-8O%4|;QGAeZF?;ps(Hl$IeFYsbu|w&>f)kJvaKC-XR^8oIv_g7V5#Roo z!UmwpjhCtKlt{_=QeDOn;XpYoIw) zR|d?o5CM3(dR9;B4kwct%$fKL(~?l0Q+3&7^##KUZSJ@9=bQsywKWKMxHcNU7^|l<|XZNQt0t<81kjoRyWt_<6ouY$RU$V>PEbBx}Z`G{QaDtCbf= zvoFD>eUl%&Jeg>I%mCh1yte3ReQ2PTN1D#@gt3TJX~UL|vT`W>{7y1_79Ps5+3JT1 zhUBG@b(Wdj{Njx|35i9;WIR{WeK+VfAj`_^oat}@CfzEZM|WSL!X&%7Ygqxsom}Pa zvsV_~yS39;(Yh z^V^O8I(TeTwx1Cn%F8Rgda^#fKK=5F4P(*wa}%xw1x%dLKXKy3>yOW)bOsBG{+ZZvB91%FDt{&3i$o8X9)DzoiK-j=i_DLF~z-;ul}Ipw?FzkP5c$5V*uXTK!g z8Y(qjl~B6<-d@p$=1|9b^ZTC7Zb)QIzTS!%_iasa+LL*~*8L6`p5XW_JNYfdE0@}) zYIIB4m<9D6?R{Im2^7vW<)LT z-pZsVY4Hp0m200m_gh3CxTWXTmX?-F%~wG2v%2o|NJs=x$*L#eFmS}Fux~`KHYb+HQ_{n=JEdW z+eNb$hewyK&*VpM*z{=DX6xIry)RDjU%yN|SCJx&`{$SAan5A^wwsY!Z>Q$sT^(O7 z8yG{c-FI^|Txl~Q73nP;dHkmLPyzp^{O=PjERtQHxnk;Nr@9|@qJYP0Ya$@?FGu8m z*D0>|>WlkY(-?!y_Z$@_6I`yZdOzsUbwT{ko_mZ*PEQYV3hpfU6?}Zt-*<1|UaVIa zi7qLe8z$a8Z7|vI(!0yG!12u9-tvb#K9q%8z7Py_wbw4FzHw|(HgDe0LSQe`rS{-W z=}E&}1$lN$V(FD#ZlUEl!CpfyZa*H}SM{sQ*$&^DsTKe3kP2Y0%{ z>y4ft-8MGOW9!<1Z66GS7*Pg~mdt#*#Q%9CtZ65=qi#E=%p*J%U9@!De&^6Z6Sm3d z4R#dweQ_+`{?qoWrekRqrM5ft?S0GDP7L0Dx~Tx0e(KZ2-c3B*z1M5?664N!9$fyB z=yQ&4d39pTrX5G9pO4sP{jDu8FtX4#&iwLghn>NLjeo8B8=FdN9&tB4c7K)MVlAHi zW{(c7hVdikw>-Nr;1b)v&Z+$bb24HR!%Sf)Uv`1%X^(Bzj*3i`bnE@12;!}O>vq1yau!xe$2D(Z}};oe_FHR=;;*$*Fz^R zXXrTOJ}Qe0k2sgR%HX>=V%h3wgH^98>ke1{`3l>=Fuu8q80%QJCvqj_WC88O>IdNt zmmbl-&}kE-H+AD6js4EN26lTN^|&zWbo@_J4kh+^6rNt8 zuOCOWIwZLkeA4z5|F73)^}HVW3Mc&OS7Ty7Y(BoGX$^i+Dq+eWm#6zOK8f5RIVGkn zo7i&qLq@*fDL;5FU}Dsw-{R4mduQRxRvFIo)ek(!Bky*us^9#gwsXaUsTs}Mz`5wK z%ESY2Ir+QGnzoU8o(6ZNW;Gz1UWIQfuZ)!S-JIhsdNs{VRCTjC|NNxI-?wtei#^GD zlt!B7o$4F0+BN=~n;TA*54&5edh=q*81=z2w%_y5_+sm6|HU_5v{y1iVzva)*3@}3zST+ZmO&F`tqh!a5Q@n zYJcoPM3m6esyF3CHa>nNP<&1y-h0w&af~J1^bNErggB!z^MUVUoXPm|M*UhnTDbkq z;?Xsq%`>*z$_3=~tQ9*KH(i*ijaid)>e%>?=%<_db}B}^De0%@&%AAZFp?wGUTT~{_BHpPwmqx&iNy&*F4^u$|{%dOG_*j#AYiIt}fkkAy*TLM)6n*(y4cU=}(^mPP#y@o!bASIGBk38z{79ugyN7+^R&!^q zua|z2WatL3zx!!z3u{%3FEn&BVYgv2T`JecQ{=d(K6yc6-Eiz8qlX-5ZXsn?rH;12aMqA!W~_!X{sh+V$NBk2cD{P| zel51kHMYKrqL@?qcG3Qz<%L!LYzMO!y{9z%@xk5aK&I^B)4A=iIJ|ejne= zU1lZKI2-n?m$B`$xnYQy{Ibm(v3c)LJ0KYde}3;Yz{oee@O=?qb^q)R!Klq>853vf zZDr5yc2&>5_A2w*aNN$4BW3R+VibBrM^CQmsCt-|Bz&Imd}_#j?3QsPA+~%bZ0i)< zuT+1_KMVTta%0r;nSzJ9*UYVczS7jti74T(zPx)BSho(c_8y5qtnJ?1zEtqfMB3-U zwqcI>hi^&`aK>jm9dza8d85&)Dwx3HC1JKNk2D2`jGx6vtJqdrUO(8eE^FfE@;2Xs zcvU5}hgTHxjf@V2t_?kj%h|WZR+u!lreWqMt@5I~!Y$_G!_GS{^eFAU8L9c`Jb7Kg z$L)TWXBJe3o<}>sfwvB?S*ya!Inc*nwkU@bT*+bOg`ziUX11+So?EPM`eytUdQRrG zn&o#*G15B2-eAV6I@W zw(fh^;==0GO7C*o_FMb8Q``zU$$31V|8A{i$6(Oc98Z$Br`Cou)Nh$0)}!<p38zFn8J-KmXr1)+?)CQ;Vm^UU3QgANtla<2zR2o)7y{mezJg??_ z)4WZw&YqNYUK-DOmG_zb=#0tu{Bnm9HBUs_PJeLrVaAVRovsOIk~1nF_}I-hc)hS$fyEu=q0 z8*Xctq|;!DMMU-ST=3jY={eVOv(e|jJQ6r33az7;@q$$7!P`7JhpuD>r`7PfF6>qHj&Qbd<<>FF z6*AV}e0V};z5#Lw%p79k>3#NkdpZw`pB7c_bJ(odAzu(YsPO@nE{hadukOA2q?Xlh z^nGoeBVPCS2bTJt1lL-Lpq^z@%W;=zKGtYj*L~=*;hdog_S+uAcRx2}9V=D0r5Ry1 zwtlJf={j{+{x)LXjhVUpRjJGG`HN+{7M)4&`xDEYzPE0;xSEXBeB_<6_M^T@sb0dJ zK+eZ-#g8S|(k#-KdOyFX5n&V-7>JD6`M$rSrUBy0d)+MMXqlhvTDUtd-{T#qF6+y- zasI9jwy$Yb)TDp$7!JSBV?Y;3NIOV-89CoB-<)c7*Z;4g#gR!{(=O_D{do{x?;q=r zzbU}4-o57GCAx;Z=P^-H1?H!!ndMD9{+^vb?j}3q4S@dlK99%%Tmt9#5Bq=tfydjB zO!U0gvNE~DqzTd*#SieEnrxyB^r!%yoJ&!$=5our+$Ri-_v_^g@s+-fD>kO!0&3yE zGjbRGi!`V60g7*Cz@U^R-6}&jjM>YMt3`mwW}KDj+@bGer{&A^NfD0Y8%!N}-=Vmc z+wwydRK`2aWGa|M@G#V>i+~caS`Req^SY7dFt}B5GF6}{BQ)pyDaWWC8CJW(K`Hl>)(9FIYUgzHTh*NzNg)>MY5XE;!Sw6E+F(|#mW^tydsm1D zcw*EMalHKf@q$#$!MY?OA8Ey_jx;#*9cF|)=PR4^`sbo0>_p@w>Zix*;$z%*bjD|R7C2qFGT4Q}LF z1??@!9WkB)DNq!>@kspqIAt|{vw zT#$bj17-~VxaANb`;|AaiLvQz4ts{_XQj|y{0K?~b67ah_vsj{=cXT#1wPR?2AKLP zZOO1hM9nNOXDuGTSJ`$tJZqT{lONqn?{jssu8phiNaBzxD9OJfN{Y@q%`ohI>>Ur* zhp%hr%i3h*RTV^-*bS1O6lL-i!xtxu`B4i6nh)hW<4{FGQ5$jq5(nz6P4}}-wb#GD(V(e19RRFhVM*=yJ1TFADxt8@XSPt2o~Zy7 zA2jeIX%elElPrHMp3Xo>>GpRj(OdW3D~4J~q#rqK zd%QxxsW=5i>#R73L3xD`m9%<2E#_Bl{k}5n%^z`Y&Sv5$2Jm7XsjiA2m~^F2GWsF$G!xZpaiRU|9jdCaB30}R<|G8zihGq!mW6HQ$%rpQKI+*( z>L%4!1(`$g->;T?7214VKH?Gv#0O?ITI58Wp7ya^!H z1H3{YD=3tMPF%h%j8L{3GF)6oz#W1v91tgLhA%oP#K3zOJE`XFEYzo4nK7(+8k%EM z*`CDini30WH;qC3#lyX!u+~31-VztUv+-a_4tbZ|YgOM!stRX_6`3-4dX)4B#l^%? z0RCX1S4%veThQP1-R@ZZ>9NV9Ki*&N7U34dh!)WdOWdQci|cw zW;Zu|>2T1&$#d#~hjN;dsltJC4C>Ue_GQggOPWZE6rML7o&G$Lbf{gZ9MeEu>`K+L zbxW|KF5e7qbf87Cep;u<(>`hWb7R7Iaj3OzQ>StPJ=a+;68>Vs!~dLu`{3eBLzk<* zkps0MHVz+1BnbIrNd`Dc<=u~qgn=`3(GEftJCd`|Z){}qlFfsckBw-I9 zCRzb5(l<>j@WIn29Z2FXl*<7=fQ?6YhKqWrgR9<9pEjF5$H>K&WQu_acXc;UrIDow z2}Qh;c7F(6THAOCbwDU_#i1W z+p5$2ng5z4siF2|Au>aOjY2XHD*}p!xAeg^QuC%(_-FN~)3u55lJ$D;I?wCVKyKFA z5rT}1wTd8_l)b6+P}jgbJTUYYpsJ*1vMN=&8XxKatw6Qz&x)!JX$N!tKPPLwYQj`I z*Vc+JaV)$kw5(Y-|4yosO zK(>ZR>lL_LGsk+KYvJ|`;RaeCaTz)v^;+?ug|kEcbr^`{en0fj0FcT~*B&=_SMA9+ z1Z@wKH;mF0$4qbQF6K~Xt(Ia0py__{(j?QQ6JgRLs(n$!KJ->T>jld^egSvpQyo4ulEGjJ}ttcc`rFFHdb zqO&Ao%Dr(;Ek4oyV?Fyp;lm%Kv*z2i|0SGOZGc-5C=P-K{hyTr;Ds{qk^{0#B#NC) zyW5os#8j#5WfrOEIPvbD0n&E2TI7#rkr;u9#JNO91d5|Fjw8SaZgK~SGM)wM4K^gV ztrh22Nu8CFkTm%NF_Vf0O;lzHZfOUdK(dV*|XxBYcz(*Dl0zMRpsyP=-+ zB8_XMzM~B4&n^<0a$BR3kDt}qZx8b`{jBD*sAGIyqhmn;ck9e9BrfWkT3TW3C#0oH zuiW&N5kBhL;35iWCJDpilt#h3MA(MRiW9P{i(w1Q5u)zq_VpdlLbLvPPAr@;*_`=eGu!CLVB$+f7uI0;<{E%u=9(-f3;8{s+nw4 z^hJ>NUO}E;B*-YTrfW7)2cv*;PwqcDEY#gh)1TP-@UA zUNc{r=uk0r!y$dfKJSoLQ`wS8^>ALo{Log{ijv3d*I4!ohE;h^HfwbW?1>`^+!%0DXK#B(I#ar|{z9M?RLsL#{S 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