pax_global_header00006660000000000000000000000064150772412520014517gustar00rootroot0000000000000052 comment=0d009668d3c12e1db735210f53d5427cb8cdcda7 duesee-imap-codec-0d00966/000077500000000000000000000000001507724125200152425ustar00rootroot00000000000000duesee-imap-codec-0d00966/.github/000077500000000000000000000000001507724125200166025ustar00rootroot00000000000000duesee-imap-codec-0d00966/.github/FUNDING.yml000066400000000000000000000000211507724125200204100ustar00rootroot00000000000000github: [duesee] duesee-imap-codec-0d00966/.github/actions/000077500000000000000000000000001507724125200202425ustar00rootroot00000000000000duesee-imap-codec-0d00966/.github/actions/cache_restore/000077500000000000000000000000001507724125200230505ustar00rootroot00000000000000duesee-imap-codec-0d00966/.github/actions/cache_restore/action.yml000066400000000000000000000012271507724125200250520ustar00rootroot00000000000000name: cache_restore runs: using: composite steps: - uses: actions/cache/restore@v4 with: path: | # See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ # See https://doc.rust-lang.org/cargo/guide/build-cache.html target key: ${{ runner.os }}|${{ github.job }}|${{ github.run_attempt }} restore-keys: | ${{ runner.os }}|${{ github.job }} ${{ runner.os }} duesee-imap-codec-0d00966/.github/actions/cache_save/000077500000000000000000000000001507724125200223235ustar00rootroot00000000000000duesee-imap-codec-0d00966/.github/actions/cache_save/action.yml000066400000000000000000000010611507724125200243210ustar00rootroot00000000000000name: cache_save runs: using: composite steps: - uses: actions/cache/save@v4 with: path: | # See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci ~/.cargo/.crates.toml ~/.cargo/.crates2.json ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ # See https://doc.rust-lang.org/cargo/guide/build-cache.html target key: ${{ runner.os }}|${{ github.job }}|${{ github.run_attempt }} duesee-imap-codec-0d00966/.github/dependabot.yml000066400000000000000000000004211507724125200214270ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" - package-ecosystem: "cargo" directory: "/" schedule: interval: "weekly" groups: dependencies: patterns: - "*" duesee-imap-codec-0d00966/.github/workflows/000077500000000000000000000000001507724125200206375ustar00rootroot00000000000000duesee-imap-codec-0d00966/.github/workflows/audit.yml000066400000000000000000000010251507724125200224660ustar00rootroot00000000000000name: audit on: push: branches: [ main ] pull_request: branches: [ main ] schedule: # 21:43 on Wednesday and Sunday. (Thanks, crontab.guru) - cron: '43 21 * * 3,0' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just audit - uses: ./.github/actions/cache_save duesee-imap-codec-0d00966/.github/workflows/main.yml000066400000000000000000000041421507724125200223070ustar00rootroot00000000000000name: main on: push: branches: [ main ] pull_request: branches: [ main ] workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just check - uses: ./.github/actions/cache_save test: strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just test - uses: ./.github/actions/cache_save # benchmark: # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@v5 # - uses: ./.github/actions/cache_restore # - run: cargo install just # - run: just bench_against_main # - uses: ./.github/actions/cache_save coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just coverage - uses: ./.github/actions/cache_save - uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: format: lcov file: target/coverage/coverage.lcov fuzz: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just fuzz - uses: ./.github/actions/cache_save check_msrv: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just check_msrv - uses: ./.github/actions/cache_save check_minimal_dependency_versions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: ./.github/actions/cache_restore - run: cargo install just - run: just check_minimal_dependency_versions - uses: ./.github/actions/cache_save duesee-imap-codec-0d00966/.github/workflows/release.yml000066400000000000000000000023121507724125200230000ustar00rootroot00000000000000name: release on: push: tags: - 'imap-codec/v*' - 'imap-types/v*' jobs: release: runs-on: ubuntu-latest steps: - name: Extract crate name from Git tag run: | set -euo pipefail tag_name=${GITHUB_REF#refs/tags/} crate_name=${tag_name%/v*} echo "Extracted crate name: $crate_name" echo "CRATE_NAME=$crate_name" >> "$GITHUB_ENV" - uses: actions/checkout@v5 - name: Assert release version matches crate version run: | set -euo pipefail # Get release version from Git tag tag_version=${GITHUB_REF#refs/tags/$CRATE_NAME/v} # Get crate version from Cargo.toml cd $CRATE_NAME crate_version=$(cargo read-manifest | jq -r .version) if [ "$tag_version" != "$crate_version" ]; then echo "Error: Release version in Git tag (${tag_version}) does not match crate version in Cargo.toml (${crate_version}) for crate $CRATE_NAME." exit 1 fi - name: Publish crate to crates.io env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} run: cargo publish -p $CRATE_NAME duesee-imap-codec-0d00966/.gitignore000066400000000000000000000001071507724125200172300ustar00rootroot00000000000000target .idea # direnv (https://direnv.net/) .envrc .direnv nohup.out duesee-imap-codec-0d00966/CHANGELOG.md000066400000000000000000000253231507724125200170600ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - YYYY-MM-DD ### Added * Implemented more IMAP extensions * ID * UNSELECT * SORT and THREAD * BINARY * METADATA * Implemented `AuthenticateData::Cancel` * Implemented `AuthMechanism::ScramSha3_512{,Plus}` * Implemented more common traits for types * Thanks, @jakoschiko! * Implemented missing tests * Added `arbitrary_simplified` feature * Added `Vec2` * Added short `README.md` to `assets` folder * Added quirk for trailing space in STATUS. Thanks, @nbdd0121! * Increased MSRV to 1.78. ### Changed * Changed `Status` to make it easier to use * Check only explicit features for SemVer violations * Renamed `NonEmptyVec` to `Vec1` * Updated `CONTRIBUTING.md` * Changed serde representation for many `enum`s from externally to adjacently tagged ### Fixed * Fixed examples in README (and test them in CI now) * Thanks, @coalooball! * Fixed broken links in README * Fixed iteration over sequence numbers * Thanks, @superboum! * Don't log `Rectified missing text to \"...\"` unnecessarily * Made `{DateTime,NaiveDate}::unvalidated` `panic!` in debug on wrong input * Mention `panic!` in `unvalidated` documentation * Fixed typo in `AuthMechanism` documentation ## [Version 1.0.0] - 2023-08-22 ### Changed * Use `'static` lifetime for `Decoder::Error` in `decode_static`. ## [Version 1.0.0-beta] - 2023-08-17 ### Added * Introduced `FlagNameAttributeExtension`. * Implemented `Display` for some `T` where `T`s `Display` implementation equals `Encode`. ### Changed * Inlined stable `ext_*` features to improve SemVer compatibility. * Re-exported `imap_codec::imap_types`. * Simplified module hierarchy. * Increased MSRV to 1.65. * Moved tokio implementation to demos. * Replaced `Decode` trait with `Decoder`. * Replaced `Encode` trait with `Encoder`. * Made `*Other` types merely technicalities. * Improved `Debug` print. * Updated `Swatinem/rust-cache`. * Simplified `Error`s. * Aligned type names with IMAP RFC. * Improved documentation. * Replaced `Capability::Literal(LiteralCapability)` with `Capability::LiteralPlus`, and `Capability::LiteralMinus`. ## [Version 0.10.0] - 2023-07-05 ### Added * Added `AuthMechanism::XOAUTH`. * Added more constructors. * Added (and improved) feature documentation. (Thanks, @jakoschiko!) * Added multiple `quirk_*` features to improve interoperability. * Added `DecodeStatic`. * Checking with `cargo-hack` and `--feature-powerset`. * Fuzz-testing with incomplete messages. ### Changed * Simplified module hierarchy. * Renamed types for better understandability (and to align them with the IMAP4rev1 standard). * Renamed constructors so they cannot be confused with `unsafe`. (Thanks, @jakoschiko!) * Resolved multiple SemVer hazards. * Use custom nom error. * Deduplicated (and added a new) fuzz-target(s). * Don't export nom parsers anymore. * Removed constant-time comparison support. * Simplified `Debug`ing of `NonEmptyVec`. ### Fixed * Fixed warnings and broken links in documentation. * Fixed `is_text_char`. * Fixed `condstore` identity. * Fixed usage of `complete` (instead of `streaming`). ### Removed * Removed `ansi_term` dev dependency. ## [Version 0.9.0] - 2023-05-30 ### Added - Implemented `MOVE` (RFC 6851). - Implemented `UNSELECT` (RFC 3691). - Implemented (some of) `CONDSTORE` / `QRESYNC` (RFC 7162). - Reworked (and enabled) coverage job in CI. - Added (spot-)fuzzing to CI. - Added `minimal-versions` job to CI. - Test MSRV. - Test lowest versions of dependencies. ### Changed - Migrated to Rust 2021. - Redesigned `Encode` trait. - Moved `Encode` trait from imap-types to imap-codec. ### Fixed - Made known-answer tests stronger. - Made it so that `Decode` is always tested during `Encode` and vice versa. - Made it so that random tests are reproducable through a seed. - Resolved remaining `TODO`s in `command_to_bytes_and_back` fuzz-target. - Resolved remaining `TODO`s in `{Single,Multi}PartExtensionData` - Fixed misuse of `{Single,Multi}PartExtensionData.` - Introduced `BodyExtension`. - Introduced `ContinueBasic` to prevent ambiguities. - Fixed `Eq` side effect of `Secret`. - Fixed `mbx_list_flags`. - Fixed `NaiveDate`. - Made `MyNaiveDate::arbitrary` really arbitrary. - Narrowed allowed values for `DateTime` and `NaiveDate`. - Fixed poor constant-time sanity check. - Fixed possible `panic!` in `response`. - Reactivated ignored tests. ## [Version 0.8.0] - 2023-04-16 ### Added * Community * Introduced a project board and a GitHub action that adds all opened issues to the project board. * Added a `CONTRIBUTING.md`. * Features * Implemented RFC 2088/RFC 7888 (LITERAL+). * Implemented RFC 2087/RFC 9208 (QUOTA). * Thanks, @MinisculeGirraffe! * Introduced usable error reporting. * Introduced `Encode::encode_detached`. * Implemented missing `From`, `TryFrom`, `AsRef`, ... conversions for various types. * Testing/Fuzzing * Improved debug workflow. * Introduced `ext` and `debug` features. * Security * Forbid `unsafe` and introduced `unchecked` feature. * Ensured that secret values are not `Debug`-printed and comparisons are made in constant time. * Wrapped `AuthenticateData` in `Secret`. * Wrapped `CommandBody::Login.password` in `Secret`. ### Changed * Refactoring * Feature-gated all existing extensions. * Simplified module/feature names for `tokio` support. * Changed naming schema to phase out `mod.rs`. * Renamed `MyDateTime` to `DateTime`, `SeqNo` to `SeqOrUid`, `SeqNo::Largest` to `SeqNo::Asterisk`. * CI * Added a job that checks for SemVer violations. * Improved CI runtime. * Made it so that superseded jobs are eagerly canceled. * Made it so that the `Coverage` job is started only after a successful `Build & Test`. * Inlined `--all-features` to reduce compilation time. * Chore * Allowed `Unicode-DFS-2016` and `BSD-3-Clause` dependencies. ### Fixed * Testing/Fuzzing * Made fuzz-targets tighter by not skipping (known) misuses. * Reactivated commented-out test code. * Restored trace generation for `README.md`. * Misuses * Fixed (known) misuses for `Capability{,Other}`, `Code{,Other}`, `Continue`, `Flag`, and `Body`. * Worked around ambiguities in IMAP. * Fixed various parsers that need to greedily consume tokens such as `Atom`s. * Fixed `text` parser by excluding `[` and `]`. ## [Version 0.7.0] - 2022-08-05 ### Added * Add tokio demos (client + server). * Introduce `ImapClientCodec` and implement `tokio_util::codec::{Encoder, Decoder}`. * Add tests to `tokio_compat`. * Add `greeting_to_bytes_and_back` fuzz target. * Introduce `Decode` trait and implement it for `Command` and `Response`. * Introduce `Greeting`, and `GreetingKind`. * Introduce `State::Greeting` variant. * Introduce `IdleDone`. * Introduce `CapabilityOther` and implement `Capability::other()`. * Implement `Decode` for `Greeting` and use it in the `tokio_compat` module. * Implement `AuthMechanism::other`. * Implement `Data::expunge`. * Implement `Code::{uidnext, uidvalidity, unseen}`. ### Changed * Improve CI. * Improve documentation. * Switch to new module layout in imap-codec. * Refactor creation of `Command`s and `CommandBody`s. * Use `Decode` trait in examples. * Use `Command::decode` instead of `command`. * Allow "Unicode-DFS-2016" license in "deny.toml". * Use `Tag` in `State::{IdleAuthenticated,IdleSelected}` instead of `String`. * Derive `Debug`, `Eq`, and `PartialEq` for `State`. * Feature-gate `Capability::LoginDisabled` with "starttls" feature. * Feature-gate `State::{Idle*}` variants with "ext_idle" feature. ### Removed * Remove `nom` feature. * Don't export `arbitrary`, and `rfc3501`. * Make `imap_types::{codec, state}` part of public API. Don't export `imap_types::Encode` directly. * Delete `greeting` constructor of `Status`. * Delete `PreAuth` variant (and constructor) of `Status`. ### Fixed * Fix missing doc test in CI. * Fix (and improve) examples. ## [Version 0.6.0] - 2022-06-14 ### Added - Introduce "starttls" feature. - Cleanup and document existing features. - Measure code coverage in CI. - Upload coverage report to Coveralls.io. - Add/Update code coverage badge in README.md. - Compile fuzzers in CI. - Implement benchmarks (Criterion.rs). - Compile benchmarks in CI. - Implement `Command::into_static()` and `Response::into_static()`. - Use `bounded-static` (thanks, @jakoschiko) - Add types to fix misuses - Introduce `AtomExt` (1*ASTRING-CHAR) to fix misuse. - Introduce `CapabilityEnable` to increase misuse-resistance. - Split imap-codec into imap-codec and imap-types. - Implement non-nom parsing in imap-types. - Add README.md to imap-types. ### Changed - Split crate into imap-codec and imap-types. - Make imap-codec the primary workspace member. - Re-export `imap-types`. - Make fuzz targets members of workspace to simplify workflow. - Rename "serdex"/"nomx" features to "serde"/"nom". - Reduce allocations during parsing. - Use `Cow` to abstract over owned and borrowed slices. - Do not check slices twice. - Introduce `new_unchecked()` functions. - Check `new_unchecked()` during debug builds. - Cleanup API for `AuthMechanism`. - Update to nom 7 and abnf-core 0.5. ### Removed - Remove `impl Display` for types in imap-types. - Remove `nom` feature in imap-types. - Remove/cleanup (unused) dependencies in imap-codec. - Remove/cleanup (unused) dependencies in imap-types. ### Fixed - Fix fuzz targets. - Fix benchmarks (thanks, @franziskuskiefer). - Fix misuses, e.g., `AtomExt` (1*ASTRING-CHAR). [Version 0.6.0]: https://github.com/duesee/imap-codec/compare/fcb400e508f74a8d88bbcbfd777bdca7cb75bdeb...63b6a2e4a94f2734d67a18039b3f6dae68994902 [Version 0.7.0]: https://github.com/duesee/imap-codec/compare/63b6a2e4a94f2734d67a18039b3f6dae68994902...16e34bce239840bc3a39c811f1ce3d36c6ea20b0 [Version 0.8.0]: https://github.com/duesee/imap-codec/compare/16e34bce239840bc3a39c811f1ce3d36c6ea20b0...f5138ac09b6e160256c8e6dc80db1597aee92394 [Version 0.9.0]: https://github.com/duesee/imap-codec/compare/f5138ac09b6e160256c8e6dc80db1597aee92394...3bb1b380a6f163a16732f9dd9c8382f2af73868c [Version 0.10.0]: https://github.com/duesee/imap-codec/compare/3bb1b380a6f163a16732f9dd9c8382f2af73868c...ca3ef319681d4e8ea2daf28b9a3650d2d74813c7 [Version 1.0.0-beta]: https://github.com/duesee/imap-codec/compare/ca3ef319681d4e8ea2daf28b9a3650d2d74813c7...1b8924dce7c943cd003a8316f384af97649feadf [Version 1.0.0]: https://github.com/duesee/imap-codec/compare/1b8924dce7c943cd003a8316f384af97649feadf...a5d8dff9e8047bda2c477a3a9d56e53274113b26 [Unreleased]: https://github.com/duesee/imap-codec/compare/a5d8dff9e8047bda2c477a3a9d56e53274113b26...HEAD duesee-imap-codec-0d00966/CONTRIBUTING.md000066400000000000000000000070471507724125200175030ustar00rootroot00000000000000# Welcome to imap-codec's (and imap-types') contributing guide Thanks for investing your time to help with this project! Keep in mind that this project is driven by volunteers. Be patient and polite, and empower others to improve. Always use your best judgment and be excellent to each other. ## Principles ### Misuse resistance We use strong-typing to [eliminate invalid state]. Ask yourself: Can I instantiate a type with an invalid variable setting? If yes, consider how to eliminate it. If you're unsure, let's figure it out together! ## Project management We use the [just](https://github.com/casey/just) command runner for Continuous Integration (CI). The GitHub Actions infrastructure merely calls `just` to execute jobs. This means that you can run all required tests for a PR using `just ci`. ### Code formatting Code is formatted using [`rustfmt`] with a custom `rustfmt.toml` and checked in CI. The config minimizes diffs and ensures a consistent style of imports. Sadly, some config options (still) require a nightly compiler. Thus, you need to use `cargo +nightly fmt`. Note: Code formatting and fuzzing should be the only routines requiring a nightly compiler. Everything else must work on stable! ### SemVer violations Breaking API changes (w/o a corresponding version bump) are detected in CI using the [cargo-semver-checks](https://github.com/obi1kenobi/cargo-semver-checks). ### MSRV See `Cargo.toml` for the Minimum Supported Rust Version (MSRV). This version is checked during CI. ### IMAP extensions > [!WARNING] > This is a best effort with various shortcomings. Please tell us if you have a better solution! IMAP is extensible. Thus, we use [Cargo features] to enable/disable extensions to the core IMAP protocol. Feature-gating helps to reduce the amount of exposed code and serves as documentation for the supported extensions. The current idea is: Every extension starts as a feature-gated "experimental" extension. This way, we can exclude it from SemVer checks. When we think an extension is "right", we can remove the feature gate. Note, however, that -- depending on the feature -- a major version bump might still be required to get it in. Thus, let's verify what features are ready close to major releases. ## Testing There are multiple forms of testing in `imap-codec`. ### Known-answer tests Known-answer tests are used to ensure that a specific IMAP message is *really* parsed into an expected object. We usually extract examples from a specific RFC and encode our expectations as unit tests. To implement this test, you can use `kat_inverse_{greeting,command,response,...}`. ### Fuzzing Fuzzing is used in `imap-types` and `imap-codec`. Fuzzing is used to test that parsing and serialization are inverses of each other (which already helped uncover a lot of bugs). For more information, see [imap-codec/fuzz/README.md](imap-codec/fuzz/README.md). The CI runs a limited number (25.000) of fuzz runs. ### Regressions & fixed bugs When fixing a bug, we should add a test to 1) show how to reproduce the bug and 2) show that a fix is effective. This also ensures that we refrain from reintroducing this bug in the future, e.g., during an incorrect refactoring. ### API tests & doc tests When explaining how the API should be used, we should write the example as a test. This way, we ensure that examples stay relevant and that we don't accidentally change the API. [`rustfmt`]: https://github.com/rust-lang/rustfmt [eliminate invalid state]: https://duesee.dev/p/type-driven-development/ [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html duesee-imap-codec-0d00966/Cargo.lock000066400000000000000000002704261507724125200171620ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "abnf-core" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec182d1f071b906a9f59269c89af101515a5cbe58f723eb6717e7fe7445c0dea" dependencies = [ "nom", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adler32" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] [[package]] name = "ahash" version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "getrandom 0.3.3", "once_cell", "serde", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "arbitrary" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "asn1-rs" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", "num-traits", "rusticata-macros", "thiserror 1.0.69", "time", ] [[package]] name = "asn1-rs-derive" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", "synstructure", ] [[package]] name = "asn1-rs-impl" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bincode" version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ "serde", ] [[package]] name = "bitflags" version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "bitpacking" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c1d3e2bfd8d06048a179f7b17afc3188effa10385e7b00dc65af6aae732ea92" dependencies = [ "crunchy", ] [[package]] name = "blake3" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bounded-static" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0beb903daa49b43bcafb5d5eebe633f9ad638d8b16cd08f95fb05ee7bd099321" [[package]] name = "bounded-static-derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0af050e27e5d57aa14975f97fe47a134c46a390f91819f23a625319a7111bfa" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "bumpalo" version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bzip2" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ "bzip2-sys", ] [[package]] name = "bzip2-sys" version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", "pkg-config", ] [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ "find-msvc-tools", "jobserver", "libc", "shlex", ] [[package]] name = "cedarwood" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d910bedd62c24733263d0bed247460853c9d22e8956bd4cd964302095e04e90" dependencies = [ "smallvec", ] [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "arbitrary", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", "windows-link 0.2.1", ] [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", ] [[package]] name = "clap" version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "constant_time_eq" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core2" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" dependencies = [ "memchr", ] [[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crc" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] [[package]] name = "criterion" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "itertools", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "dary_heap" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d2cd9c18b9f454ed67da600630b021a8a80bf33f8c95896ab33aaf1c26b728" [[package]] name = "dashmap" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "data-encoding" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "deflate64" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" [[package]] name = "der-parser" version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ "asn1-rs", "displaydoc", "nom", "num-bigint", "num-traits", "rusticata-macros", ] [[package]] name = "deranged" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", ] [[package]] name = "derive_arbitrary" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", "subtle", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encoding_rs" version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] name = "enum-as-inner" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ "heck", "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "event_macro" version = "0.1.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "fallible-iterator" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "farmhash" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f35ce9c8fb9891c75ceadbc330752951a4e369b50af10775955aeb9af3eee34b" [[package]] name = "fast-float" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" [[package]] name = "find-msvc-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "flate2" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ "byteorder", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "gethostname" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" dependencies = [ "libc", "windows-targets 0.48.5", ] [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", "wasi 0.14.4+wasi-0.2.4", "wasm-bindgen", ] [[package]] name = "h2" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", "http", "indexmap", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "half" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", ] [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hashlink" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ "hashbrown 0.14.5", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hickory-proto" version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" dependencies = [ "async-trait", "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", "futures-io", "futures-util", "idna", "ipnet", "once_cell", "rand 0.8.5", "ring", "rustls 0.21.12", "rustls-pemfile 1.0.4", "thiserror 1.0.69", "tinyvec", "tokio", "tokio-rustls 0.24.1", "tracing", "url", ] [[package]] name = "hickory-resolver" version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", "lru-cache", "once_cell", "parking_lot", "rand 0.8.5", "resolv-conf", "rustls 0.21.12", "smallvec", "thiserror 1.0.69", "tokio", "tokio-rustls 0.24.1", "tracing", ] [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest", ] [[package]] name = "http" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", ] [[package]] name = "http-body-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", "http", "http-body", "pin-project-lite", ] [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", "pin-utils", "smallvec", "tokio", "want", ] [[package]] name = "hyper-rustls" version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http", "hyper", "hyper-util", "rustls 0.23.31", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", "tower-service", "webpki-roots 1.0.2", ] [[package]] name = "hyper-util" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", "http", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", "socket2 0.6.0", "tokio", "tower-service", "tracing", ] [[package]] name = "iana-time-zone" version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "log", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "icu_collections" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_normalizer" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerotrie", "zerovec", ] [[package]] name = "idna" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "imap-codec" version = "2.0.0-alpha.6" dependencies = [ "abnf-core", "base64 0.22.1", "chrono", "imap-types", "log", "nom", ] [[package]] name = "imap-codec-bench" version = "0.0.0" dependencies = [ "criterion", "imap-codec", "imap-proto", "imap-types", "imap_proto", "tokio", ] [[package]] name = "imap-codec-fuzz" version = "0.0.0" dependencies = [ "arbitrary", "imap-codec", "imap-types", "libfuzzer-sys", ] [[package]] name = "imap-proto" version = "0.16.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba1f9b30846c3d04371159ef3a0413ce7c1ae0a8c619cd255c60b3d902553f22" dependencies = [ "nom", ] [[package]] name = "imap-types" version = "2.0.0-alpha.5" dependencies = [ "arbitrary", "base64 0.22.1", "bounded-static", "bounded-static-derive", "chrono", "criterion", "rand 0.8.5", "serde", "serde_json", "thiserror 2.0.17", ] [[package]] name = "imap-types-fuzz" version = "0.0.0" dependencies = [ "imap-types", "libfuzzer-sys", ] [[package]] name = "imap_proto" version = "0.1.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "ahash", "chrono", "jmap_proto", "mail-parser", "store", "trc", ] [[package]] name = "include-flate" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01b7cb6ca682a621e7cda1c358c9724b53a7b4409be9be1dd443b7f3a26f998" dependencies = [ "include-flate-codegen", "include-flate-compress", "libflate", "zstd", ] [[package]] name = "include-flate-codegen" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f49bf5274aebe468d6e6eba14a977eaf1efa481dc173f361020de70c1c48050" dependencies = [ "include-flate-compress", "libflate", "proc-macro-error", "proc-macro2", "quote", "syn 2.0.106", "zstd", ] [[package]] name = "include-flate-compress" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae6a40e716bcd5931f5dbb79cd921512a4f647e2e9413fded3171fca3824dbc" dependencies = [ "libflate", "zstd", ] [[package]] name = "indexmap" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", "hashbrown 0.15.5", ] [[package]] name = "inout" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "generic-array", ] [[package]] name = "ipconfig" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2 0.5.10", "widestring", "windows-sys 0.48.0", "winreg", ] [[package]] name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" dependencies = [ "memchr", "serde", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jieba-macros" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c676b32a471d3cfae8dac2ad2f8334cd52e53377733cca8c1fb0a5062fec192" dependencies = [ "phf_codegen", ] [[package]] name = "jieba-rs" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5dd552bbb95d578520ee68403bf8aaf0dbbb2ce55b0854d019f9350ad61040a" dependencies = [ "cedarwood", "fxhash", "include-flate", "jieba-macros", "lazy_static", "phf", "regex", ] [[package]] name = "jmap_proto" version = "0.10.6" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "ahash", "fast-float", "mail-parser", "serde", "serde_json", "store", "trc", "utils", ] [[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", ] [[package]] name = "js-sys" version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libflate" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45d9dfdc14ea4ef0900c1cddbc8dcd553fbaacd8a4a282cf4018ae9dd04fb21e" dependencies = [ "adler32", "core2", "crc32fast", "dary_heap", "libflate_lz77", ] [[package]] name = "libflate_lz77" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e0d73b369f386f1c44abd9c570d5318f55ccde816ff4b562fa452e5182863d" dependencies = [ "core2", "hashbrown 0.14.5", "rle-decode-fast", ] [[package]] name = "libfuzzer-sys" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", ] [[package]] name = "libsqlite3-sys" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", "vcpkg", ] [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "litemap" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lru-cache" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ "linked-hash-map", ] [[package]] name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lz4_flex" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" [[package]] name = "lzma-rs" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" dependencies = [ "byteorder", "crc", ] [[package]] name = "lzma-sys" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" dependencies = [ "cc", "libc", "pkg-config", ] [[package]] name = "mail-auth" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01e36ed6613c4c43f2d3e4ff3af1888b3612c74d56fe0eac2cf25706f6a85a6b" dependencies = [ "ahash", "flate2", "hickory-resolver", "lru-cache", "mail-builder", "mail-parser", "parking_lot", "quick-xml", "ring", "rustls-pemfile 2.2.0", "serde", "serde_json", "zip", ] [[package]] name = "mail-builder" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25f5871d5270ed80f2ee750b95600c8d69b05f8653ad3be913b2ad2e924fefcb" dependencies = [ "gethostname", ] [[package]] name = "mail-parser" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93c3b9e5d8b17faf573330bbc43b37d6e918c0a3bf8a88e7d0a220ebc84af9fc" dependencies = [ "encoding_rs", "serde", ] [[package]] name = "mail-send" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a575d25cf00ed68e5790b473b29242a47e991c6187785d47b45e31fc5816554" dependencies = [ "base64 0.22.1", "gethostname", "md5", "rustls 0.23.31", "rustls-pki-types", "smtp-proto", "tokio", "tokio-rustls 0.26.2", "webpki-roots 0.26.11", ] [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "md5" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "nlp" version = "0.10.6" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "ahash", "bincode", "farmhash", "jieba-rs", "lru-cache", "nohash", "parking_lot", "phf", "psl", "rust-stemmers", "serde", "siphasher", "tinysegmenter", "whatlang", "xxhash-rust", ] [[package]] name = "nohash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "oid-registry" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" dependencies = [ "asn1-rs", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oorandom" version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "parking_lot" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.52.6", ] [[package]] name = "pbkdf2" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest", "hmac", ] [[package]] name = "pem" version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64 0.22.1", "serde", ] [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", "phf_shared", ] [[package]] name = "phf_codegen" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ "phf_generator", "phf_shared", ] [[package]] name = "phf_generator" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", "rand 0.8.5", ] [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] name = "potential_utf" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "privdrop" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70722a5a3728c9603c8d9469b64b8d1ee54dae6d74e24146da7f501b4c76540f" dependencies = [ "libc", "nix", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn 1.0.109", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "psl" version = "2.1.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a33878b44e45231ecbc8c619cc8059e4adab882b25812192676fe08dcf352f" dependencies = [ "psl-types", ] [[package]] name = "psl-types" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "quick-xml" version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quinn" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", "rustls 0.23.31", "socket2 0.6.0", "thiserror 2.0.17", "tokio", "tracing", "web-time", ] [[package]] name = "quinn-proto" version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", "rand 0.9.2", "ring", "rustc-hash", "rustls 0.23.31", "rustls-pki-types", "slab", "thiserror 2.0.17", "tinyvec", "tracing", "web-time", ] [[package]] name = "quinn-udp" version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2 0.6.0", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "r2d2" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", "parking_lot", "scheduled-thread-pool", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", ] [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core 0.6.4", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core 0.9.3", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.16", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.3", ] [[package]] name = "rayon" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "rcgen" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" dependencies = [ "pem", "ring", "rustls-pki-types", "time", "yasna", ] [[package]] name = "redox_syscall" version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "reqwest" version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", "futures-core", "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", "quinn", "rustls 0.23.31", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", "tokio-rustls 0.26.2", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "webpki-roots 1.0.2", ] [[package]] name = "resolv-conf" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" [[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rle-decode-fast" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422" [[package]] name = "roaring" version = "0.10.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19e8d2cfa184d94d0726d650a9f4a1be7f9b76ac9fdb954219878dc00c1c1e7b" dependencies = [ "bytemuck", "byteorder", ] [[package]] name = "rtrb" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad8388ea1a9e0ea807e442e8263a699e7edcb320ecbcd21b4fa8ff859acce3ba" [[package]] name = "rusqlite" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ "bitflags", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", "smallvec", ] [[package]] name = "rust-stemmers" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" dependencies = [ "serde", "serde_derive", ] [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rusticata-macros" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ "nom", ] [[package]] name = "rustls" version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls" version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", "ring", "rustls-pki-types", "rustls-webpki 0.103.4", "subtle", "zeroize", ] [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64 0.21.7", ] [[package]] name = "rustls-pemfile" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ "rustls-pki-types", ] [[package]] name = "rustls-pki-types" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ "web-time", "zeroize", ] [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", ] [[package]] name = "rustls-webpki" version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scheduled-thread-pool" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ "parking_lot", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", ] [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "serde_json" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", "serde_core", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "siphasher" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smtp-proto" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8003eb09806ff2ae4661dd0dca27cbd9f65ba85de06cc0302c364b0d661ba368" [[package]] name = "socket2" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "socket2" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "store" version = "0.10.6" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "ahash", "arc-swap", "async-trait", "bincode", "bitpacking", "blake3", "farmhash", "flate2", "lru-cache", "lz4_flex", "nlp", "num_cpus", "parking_lot", "r2d2", "rand 0.8.5", "rayon", "regex", "roaring", "rusqlite", "serde", "tokio", "trc", "utils", "xxhash-rust", ] [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "unicode-ident", ] [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl 2.0.17", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "thiserror-impl" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "time" version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tinysegmenter" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1755695d17d470baf2d937a59ab4e86de3034b056fc8700e21411b0efca36497" dependencies = [ "lazy_static", "maplit", ] [[package]] name = "tinystr" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tinyvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "bytes", "libc", "mio", "pin-project-lite", "signal-hook-registry", "socket2 0.6.0", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls 0.21.12", "tokio", ] [[package]] name = "tokio-rustls" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls 0.23.31", "tokio", ] [[package]] name = "tokio-util" version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tower" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", "sync_wrapper", "tokio", "tower-layer", "tower-service", ] [[package]] name = "tower-http" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags", "bytes", "futures-util", "http", "http-body", "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "tracing-core" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] name = "trc" version = "0.10.6" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "ahash", "base64 0.22.1", "bincode", "event_macro", "mail-auth", "mail-parser", "parking_lot", "reqwest", "rtrb", "serde", "serde_json", "tokio", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utils" version = "0.10.6" source = "git+https://github.com/stalwartlabs/mail-server?rev=3b950cecec01b4b1083cc900d6742c11a665afab#3b950cecec01b4b1083cc900d6742c11a665afab" dependencies = [ "ahash", "base64 0.22.1", "blake3", "chrono", "dashmap", "form_urlencoded", "futures", "http-body-util", "lru-cache", "mail-auth", "mail-send", "parking_lot", "pem", "privdrop", "rand 0.8.5", "rcgen", "regex", "reqwest", "ring", "rustls 0.23.31", "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", "smtp-proto", "tokio", "tokio-rustls 0.26.2", "trc", "webpki-roots 0.26.11", "x509-parser", ] [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" version = "0.14.4+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" dependencies = [ "cfg-if", "js-sys", "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "web-time" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "webpki-roots" version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] [[package]] name = "whatlang" version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "471d1c1645d361eb782a1650b1786a8fb58dd625e681a04c09f5ff7c8764a7b0" dependencies = [ "hashbrown 0.14.5", "once_cell", ] [[package]] name = "widestring" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" [[package]] name = "winapi-util" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ "windows-sys 0.60.2", ] [[package]] name = "windows-core" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", "windows-link 0.1.3", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "windows-interface" version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "windows-link" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link 0.1.3", ] [[package]] name = "windows-strings" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link 0.1.3", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.3", ] [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link 0.2.1", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winreg" version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] [[package]] name = "wit-bindgen" version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" [[package]] name = "writeable" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "x509-parser" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ "asn1-rs", "data-encoding", "der-parser", "lazy_static", "nom", "oid-registry", "rusticata-macros", "thiserror 1.0.69", "time", ] [[package]] name = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "xz2" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" dependencies = [ "lzma-sys", ] [[package]] name = "yasna" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ "time", ] [[package]] name = "yoke" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", "synstructure", ] [[package]] name = "zerocopy" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", "synstructure", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "zerotrie" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", "yoke", "zerofrom", ] [[package]] name = "zerovec" version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", "syn 2.0.106", ] [[package]] name = "zip" version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ "aes", "arbitrary", "bzip2", "constant_time_eq", "crc32fast", "crossbeam-utils", "deflate64", "displaydoc", "flate2", "getrandom 0.3.3", "hmac", "indexmap", "lzma-rs", "memchr", "pbkdf2", "sha1", "thiserror 2.0.17", "time", "xz2", "zeroize", "zopfli", "zstd", ] [[package]] name = "zopfli" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ "bumpalo", "crc32fast", "log", "simd-adler32", ] [[package]] name = "zstd" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", ] duesee-imap-codec-0d00966/Cargo.toml000066400000000000000000000003021507724125200171650ustar00rootroot00000000000000[workspace] resolver = "2" members = [ "imap-codec", "imap-codec/benchmark", "imap-codec/fuzz", "imap-types", "imap-types/fuzz", ] [workspace.package] rust-version = "1.85" duesee-imap-codec-0d00966/LICENSE-APACHE000066400000000000000000000261351507724125200171750ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. duesee-imap-codec-0d00966/LICENSE-MIT000066400000000000000000000020621507724125200166760ustar00rootroot00000000000000MIT License Copyright (c) 2020 Damian Poddebniak 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. duesee-imap-codec-0d00966/README.md000066400000000000000000000401301507724125200165170ustar00rootroot00000000000000[![Build & Test](https://github.com/duesee/imap-codec/actions/workflows/main.yml/badge.svg)](https://github.com/duesee/imap-codec/actions/workflows/main.yml) [![Audit](https://github.com/duesee/imap-codec/actions/workflows/audit.yml/badge.svg)](https://github.com/duesee/imap-codec/actions/workflows/audit.yml) [![Coverage](https://coveralls.io/repos/github/duesee/imap-codec/badge.svg?branch=main)](https://coveralls.io/github/duesee/imap-codec?branch=main) [![Documentation](https://docs.rs/imap-codec/badge.svg)](https://docs.rs/imap-codec) # imap-{codec,types} This workspace contains [`imap-codec`] and [`imap-types`], two [rock-solid] and [well-documented] crates to build [IMAP4rev1] clients and servers. `imap-codec` provides parsing and serialization, and is based on `imap-types`. `imap-types` provides misuse-resistant types, constructors, and general support for IMAP implementations. The crates live here together, but `imap-types` is a perfectly fine standalone crate. If you are looking for a slightly more high-level library supporting your client or server implementation, take a look at [`imap-next`]. Let's talk on [Matrix]! ## Features * Complete [formal syntax] of IMAP4rev1 is implemented. Furthermore, several IMAP [extensions] are supported. * Correctness and misuse-resistance are enforced on the type level. It's not possible to construct a message that violates the IMAP specification. * Messages automatically use the most efficient representation. For example, atoms are preferred over quoted strings, and quoted strings are preferred over literals. It's equally easy to manually choose a representation. * Parsing works in streaming mode. `Incomplete` is returned when there is insufficient data to make a final decision. No message will be truncated. * Parsing is zero-copy by default. Allocation is avoided during parsing, but all messages can explicitly be converted into more flexible owned variants. * Fuzzing and property-based tests exercise the library. The library is fuzz-tested never to produce a message it can't parse itself. ## Usage ```rust use imap_codec::{decode::Decoder, encode::Encoder, CommandCodec}; fn main() { let input = b"ABCD UID FETCH 1,2:* (BODY.PEEK[1.2.3.4.MIME]<42.1337>)\r\n"; let codec = CommandCodec::new(); let (remainder, cmd) = codec.decode(input).unwrap(); println!("# Parsed\n\n{:#?}\n\n", cmd); let buffer = codec.encode(&cmd).dump(); // Note: IMAP4rev1 may produce messages that are not valid UTF-8. println!("# Serialized\n\n{:?}", std::str::from_utf8(&buffer)); } ``` ## Examples ### Simple parsing Try one of the `parse_*` examples, e.g., ... ```sh $ cargo run --example=parse_command ``` ... to parse some IMAP messages. ### Robust parsing Real-world implementations need to handle invalid messages. Thus, `imap-codec` provides a `Fragmentizer` (see `fragmentizer*` examples) for "two-stage parsing". The idea is to detect message boundaries (consuming raw lines and literals) first, and to apply an IMAP message parser second. If the message parser errors out, we can safely discard the erroneous message w/o affecting other messages because we know the message boundaries. You can try this out by running ... ```sh $ cargo run --example=fragmentizer_server ``` ... and connecting via ... ```sh $ netcat -C 127.0.0.1 12345 ``` ### Parsed and serialized IMAP4rev1 connection The following output was generated by reading the trace from [RFC 3501 section 8](https://tools.ietf.org/html/rfc3501#section-8), printing the input (first line), `Debug`-printing the parsed object (second line), and printing the serialized output (third line). ```rust,compile_fail // * OK IMAP4rev1 Service Ready Status(Ok { tag: None, code: None, text: Text("IMAP4rev1 Service Ready") }) // * OK IMAP4rev1 Service Ready // a001 login mrc secret Command { tag: Tag("a001"), body: Login { username: Atom(AtomExt("mrc")), password: /* REDACTED */ } } // a001 LOGIN mrc secret // a001 OK LOGIN completed Status(Ok { tag: Some(Tag("a001")), code: None, text: Text("LOGIN completed") }) // a001 OK LOGIN completed // a002 select inbox Command { tag: Tag("a002"), body: Select { mailbox: Inbox } } // a002 SELECT INBOX // * 18 EXISTS Data(Exists(18)) // * 18 EXISTS // * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) Data(Flags([Answered, Flagged, Deleted, Seen, Draft])) // * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) // * 2 RECENT Data(Recent(2)) // * 2 RECENT // * OK [UNSEEN 17] Message 17 is the first unseen message Status(Ok { tag: None, code: Some(Unseen(17)), text: Text("Message 17 is the first unseen message") }) // * OK [UNSEEN 17] Message 17 is the first unseen message // * OK [UIDVALIDITY 3857529045] UIDs valid Status(Ok { tag: None, code: Some(UidValidity(3857529045)), text: Text("UIDs valid") }) // * OK [UIDVALIDITY 3857529045] UIDs valid // a002 OK [READ-WRITE] SELECT completed Status(Ok { tag: Some(Tag("a002")), code: Some(ReadWrite), text: Text("SELECT completed") }) // a002 OK [READ-WRITE] SELECT completed // a003 fetch 12 full Command { tag: Tag("a003"), body: Fetch { sequence_set: SequenceSet([Single(Value(12))]+), macro_or_item_names: Macro(Full), uid: false } } // a003 FETCH 12 FULL // * 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700" RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)" "IMAP4rev1 WG mtg summary and minutes" (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) ((NIL NIL "imap" "cac.washington.edu")) ((NIL NIL "minutes" "CNRI.Reston.VA.US")("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL "") BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028 92)) Data(Fetch { seq: 12, items: [Flags([Flag(Seen)]), InternalDate(1996-07-17T02:44:25-07:00), Rfc822Size(4286), Envelope(Envelope { date: NString(Some(Quoted(Quoted("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)")))), subject: NString(Some(Quoted(Quoted("IMAP4rev1 WG mtg summary and minutes")))), from: [Address { name: NString(Some(Quoted(Quoted("Terry Gray")))), adl: NString(None), mailbox: NString(Some(Quoted(Quoted("gray")))), host: NString(Some(Quoted(Quoted("cac.washington.edu")))) }], sender: [Address { name: NString(Some(Quoted(Quoted("Terry Gray")))), adl: NString(None), mailbox: NString(Some(Quoted(Quoted("gray")))), host: NString(Some(Quoted(Quoted("cac.washington.edu")))) }], reply_to: [Address { name: NString(Some(Quoted(Quoted("Terry Gray")))), adl: NString(None), mailbox: NString(Some(Quoted(Quoted("gray")))), host: NString(Some(Quoted(Quoted("cac.washington.edu")))) }], to: [Address { name: NString(None), adl: NString(None), mailbox: NString(Some(Quoted(Quoted("imap")))), host: NString(Some(Quoted(Quoted("cac.washington.edu")))) }], cc: [Address { name: NString(None), adl: NString(None), mailbox: NString(Some(Quoted(Quoted("minutes")))), host: NString(Some(Quoted(Quoted("CNRI.Reston.VA.US")))) }, Address { name: NString(Some(Quoted(Quoted("John Klensin")))), adl: NString(None), mailbox: NString(Some(Quoted(Quoted("KLENSIN")))), host: NString(Some(Quoted(Quoted("MIT.EDU")))) }], bcc: [], in_reply_to: NString(None), message_id: NString(Some(Quoted(Quoted("")))) }), Body(Single { body: Body { basic: BasicFields { parameter_list: [(Quoted(Quoted("CHARSET")), Quoted(Quoted("US-ASCII")))], id: NString(None), description: NString(None), content_transfer_encoding: Quoted(Quoted("7BIT")), size: 3028 }, specific: Text { subtype: Quoted(Quoted("PLAIN")), number_of_lines: 92 } }, extension_data: None })]+ }) // * 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700" RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)" "IMAP4rev1 WG mtg summary and minutes" (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) (("Terry Gray" NIL "gray" "cac.washington.edu")) ((NIL NIL "imap" "cac.washington.edu")) ((NIL NIL "minutes" "CNRI.Reston.VA.US")("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL "") BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028 92)) // a003 OK FETCH completed Status(Ok { tag: Some(Tag("a003")), code: None, text: Text("FETCH completed") }) // a003 OK FETCH completed // a004 fetch 12 body[header] Command { tag: Tag("a004"), body: Fetch { sequence_set: SequenceSet([Single(Value(12))]+), macro_or_item_names: MessageDataItemNames([BodyExt { section: Some(Header(None)), partial: None, peek: false }]), uid: false } } // a004 FETCH 12 BODY[HEADER] // * 12 FETCH (BODY[HEADER] {342} // Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT) // From: Terry Gray // Subject: IMAP4rev1 WG mtg summary and minutes // To: imap@cac.washington.edu // cc: minutes@CNRI.Reston.VA.US, John Klensin // Message-Id: // MIME-Version: 1.0 // Content-Type: TEXT/PLAIN; CHARSET=US-ASCII // // ) Data(Fetch { seq: 12, items: [BodyExt { section: Some(Header(None)), origin: None, data: NString(Some(Literal(Literal { data: b"Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\r\nFrom: Terry Gray \r\nSubject: IMAP4rev1 WG mtg summary and minutes\r\nTo: imap@cac.washington.edu\r\ncc: minutes@CNRI.Reston.VA.US, John Klensin \r\nMessage-Id: \r\nMIME-Version: 1.0\r\nContent-Type: TEXT/PLAIN; CHARSET=US-ASCII\r\n\r\n" }))) }]+ }) // * 12 FETCH (BODY[HEADER] {342} // Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT) // From: Terry Gray // Subject: IMAP4rev1 WG mtg summary and minutes // To: imap@cac.washington.edu // cc: minutes@CNRI.Reston.VA.US, John Klensin // Message-Id: // MIME-Version: 1.0 // Content-Type: TEXT/PLAIN; CHARSET=US-ASCII // // ) // a004 OK FETCH completed Status(Ok { tag: Some(Tag("a004")), code: None, text: Text("FETCH completed") }) // a004 OK FETCH completed // a005 store 12 +flags \deleted Command { tag: Tag("a005"), body: Store { sequence_set: SequenceSet([Single(Value(12))]+), kind: Add, response: Answer, flags: [Deleted], uid: false } } // a005 STORE 12 +FLAGS (\Deleted) // * 12 FETCH (FLAGS (\Seen \Deleted)) Data(Fetch { seq: 12, items: [Flags([Flag(Seen), Flag(Deleted)])]+ }) // * 12 FETCH (FLAGS (\Seen \Deleted)) // a005 OK +FLAGS completed Status(Ok { tag: Some(Tag("a005")), code: None, text: Text("+FLAGS completed") }) // a005 OK +FLAGS completed // a006 logout Command { tag: Tag("a006"), body: Logout } // a006 LOGOUT // * BYE IMAP4rev1 server terminating connection Status(Bye { code: None, text: Text("IMAP4rev1 server terminating connection") }) // * BYE IMAP4rev1 server terminating connection // a006 OK LOGOUT completed Status(Ok { tag: Some(Tag("a006")), code: None, text: Text("LOGOUT completed") }) // a006 OK LOGOUT completed ``` # FAQ
How does imap-codec compare to imap-proto? `imap-codec` provides low-level parsing and serialization support for IMAP4rev1, similar to [`imap-proto`]. The most significant differences are server support, the split into `imap-codec` and `imap-types`, misuse resistance (affecting API design), and (real-world) test coverage. No matter if implementing a client- or a server, you need the full set of IMAP type definitions. When you send a command with a specific [`Tag`], you expect a command completion response with the same [`Tag`]. Thus, commands and responses must work well together (and are best provided by a single crate). As far as I know, `imap-proto` doesn't provide types that would be reusable in a generic server implementation. `imap-types` provides type definitions for client- and server implementations. As a client developer, you will never parse commands or serialize responses. As a server developer, you will never serialize commands or parse responses. Thus, you only need "half of" the set of parsers and serializers. As far as I know, `imap-proto` provides the "client half" only. `imap-codec` provides both the "client half" and the "server half". Separating types and codecs increases cohesion and (hopefully) paves the way for IMAP crates that operate at higher levels. However, the maintenance cost of two crates, `imap-types` and `imap-codec`, could be higher than for `imap-proto`. Generally, `imap-codec` has a more extensive API surface than `imap-proto` and could be [more challenging to use]. In return, it guarantees that you always construct valid messages and aims to make IMAP usable even for people with less IMAP experience. For example, `imap-codec` has [build-in support for IMAP literals] and ensures to always use [a correct representation for strings]. `imap-codec` has a high test coverage and is fuzz-tested to ensure properties such as invertibility, misuse-resistance, etc. You should be unable to crash the library or generate messages that can't be parsed. However, "interoperability can not be tested in a vacuum" [^1]. `imap-proto` already succeeded in production as it is (transitively) used in [`imap`], [`async-imap`], and [Delta Chat]. It could solve more real-world quirks, provide more IMAP extensions that matter in practice, or generally have a more mature interoperability story.
Have you considered contributing to imap-proto? I created `imap-codec` because I needed [server-side support](https://github.com/Email-Analysis-Toolkit/fake-mail-server). The intention was to eventually merge `imap-codec` into `imap-proto` as soon as it's "ready". I even did a bit of [preparation work](https://github.com/djc/tokio-imap/graphs/contributors). However, the different types (and philosophy, maybe), made merging non-trivial. Both projects can learn from each other and align on their goals. Still, joining forces would require a fair amount of work from everyone, and I wonder if we are willing (and have the resources) to start such an endeavor.
# License This crate is dual-licensed under Apache 2.0 and MIT terms. # Thanks Thanks to the [NLnet Foundation](https://nlnet.nl/) for supporting imap-codec through their [NGI Assure](https://nlnet.nl/assure/) program!
[rock-solid]: https://github.com/duesee/imap-codec/tree/main/imap-codec/fuzz [well-documented]: https://docs.rs/imap-codec/latest/imap_codec/ [Matrix]: https://matrix.to/#/#imap-codec:matrix.org [IMAP4rev1]: https://tools.ietf.org/html/rfc3501 [formal syntax]: https://tools.ietf.org/html/rfc3501#section-9 [extensions]: https://docs.rs/imap-codec/latest/imap_codec/#features [cargo fuzz]: https://github.com/rust-fuzz/cargo-fuzz [parse_command]: https://github.com/duesee/imap-codec/blob/main/examples/parse_command.rs [`imap-codec`]: imap-codec [`imap-types`]: imap-types [`imap-next`]: https://github.com/duesee/imap-next [`imap`]: https://github.com/jonhoo/rust-imap [`imap-proto`]: https://crates.io/crates/imap-proto [`async-imap`]: https://github.com/async-email/async-imap [Delta Chat]: https://delta.chat [core types]: https://docs.rs/imap-types/latest/imap_types/core/index.html [`Command`]: https://docs.rs/imap-types/latest/imap_types/command/struct.Command.html [`Response`]: https://docs.rs/imap-types/latest/imap_types/response/enum.Response.html [`Tag`]: https://docs.rs/imap-types/latest/imap_types/core/struct.Tag.html [`BodyStructure`]: https://docs.rs/imap-types/latest/imap_types/body/enum.BodyStructure.html [more challenging to use]: https://github.com/duesee/imap-codec/tree/main/imap-types#examples [a correct representation for strings]: https://github.com/duesee/imap-codec/tree/main/imap-types#examples [build-in support for IMAP literals]: https://docs.rs/imap-codec/latest/imap_codec/encode/struct.Encoded.html [IMAP servers with imap-codec]: https://github.com/Email-Analysis-Toolkit/fake-mail-server [^1]: https://datatracker.ietf.org/doc/html/rfc2683 duesee-imap-codec-0d00966/SECURITY.md000066400000000000000000000003521507724125200170330ustar00rootroot00000000000000# Security Policy ## Reporting a Vulnerability Feel free to open a public issue here if you found a security vulnerability. If, for whatever reason, you prefer to not make it public, please write an email to poddebniak@mailbox.org duesee-imap-codec-0d00966/assets/000077500000000000000000000000001507724125200165445ustar00rootroot00000000000000duesee-imap-codec-0d00966/assets/NGIR Imap-Codec penetration test report 2025 1.0.pdf000066400000000000000000071076071507724125200272770ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /Creator (,R"~>\(%fˉq|%NBQJ\\јe%?j39) /Producer (+r*pԃxZH-q`<^2{*ߧ\(V\(S84Uj>lG) /CreationDate (ʱV.4mmr0\np\\O>ǐ`#cƻ~0t?hb4) >> endobj 2 0 obj << /N 3 /Length 3 0 R /Filter /FlateDecode >> stream WŧKK|ޙaÄ1&F|T\H >PzEGA,aqo7]]=t$$S!MMo䒙#Uyŵt̋1_(&H-T~AN]~tȿZs/T}!1a6Q5hpzOPK&u{;&m%vuey`]Q()^Ejv p,p,fÝ 砺Jss?ifL/܄iV_M@^a-"E[n>ِW9: ';lv~#ܧ&\ IK토<iy( /݄R]CYU(*ka,˸15Hh$v[?0yǵt;bӖ9Phf+7[bFe˱!|¶my|9ZTRZ:%šhxeTA"\Ka5Vn$TnJ Lw{, ݆ok#Iڳ(K88r}[t,?ՀVL.% -QS܃oU:9!*j ʻ4.'o6P$(>4fZHՁ4<@X90KJx DcLCڭkꛭD&\y sd;( aC#p;J=;5Q6U^a.yu񱑤m>փ$m#Y "U ڪh-f)M^q[_M `sH]j \l~o_^ů3M\!H*p^,ډ_#E[ & V1b_VqW5[ vHCp6/pImCaJSd`b 8 ÖLG M8^#Ϣty^}zQ.=giJ_S]צY;§(BЍJ,dFßo9f2ӾVȏRJzFx;yGqM27JhDy/m$T^bѩ]l]bNف ʹ+lz<T {8ƛ߬۷JYNuSr^4W=U 2D GB_CVeI&(zc["U#;}zi b`'٣@2xTʉSp2W摍applCPɬS i5o͝:ÿʹR+X,R2:<&XOSuhs030 2Y%Acr}2z'v;nG^?.s>g,iݛ'$RxwD+x Wؑ-tƵVWܜ3%㡬Feepڒ5#'- PwݍWDGވ8Gشoܷ؉%X snLBMH~WC  ;9H+VJ^فm3P[sYa[#^MxwP! cfh7@!J }s_gfgkr'"P\톉"ygl\&yid%F:z־rNgZ,SChq$Jv O1e[mރ|]7 Ҽ HJA˿HC<,84-"#B6*~-TZZ$?v5q\Y'eÒgx3M_ ''Yta dᠵopx75OUELe Bigb h;YrE-K(-)1u 44ƽ,Y&%l endstream endobj 3 0 obj 2496 endobj 4 0 obj [/ICCBased 2 0 R] endobj 5 0 obj << /Type /Metadata /Subtype /XML /Length 6 0 R >> stream uǓhu|$(֗n; -=ϑ!&]v.8j/ȅpXe&UZvD>R[6Cҡnd:Ly=_ywN N:ddI`[) @.jlD!Z2R.qɟUbl;&q;4t Z oaLy>XHaoiZ6YE{3]N,ݟӧ+utEk#uh꡸N;U0{A-%q/;ޝ=Wg3RpH//аD %T&#zKWaNuy:]=ֲؐ⫐>?\ݰ.RöC:_ǤΠt_F7QJN;162d  =+1䣕Ig!)sޖNA`]*nMekߚ*x2M &I/e ,}⅗ⷄ? G#A-YNUen]n6L[2R:y)M.YJfgÝ"+ċUd@G՗qyX tl~u.=2ͽIRr>|3:iIPn2ma9½0SKA+|gbDqoRVqL"&}N(A`?L kRY!4ά-BlƔW~-`~ U Ymm^6.@-&%\V{Y% n$" }̎ߏH$碔;YO endstream endobj 6 0 obj 880 endobj 7 0 obj << /Name /Im1 /Type /XObject /Length 8 0 R /Filter /DCTDecode /Subtype /Image /Width 2480 /Height 3507 /BitsPerComponent 8 /ColorSpace /DeviceRGB >> stream SW mT!g=TJ67 m8SS8cXaqV,- o)WubBh &PE>`D[D`c]񖤫O] ya;TS6E2H+ ͜o&~{e/;1@Kk\q,6zF.ۑN|d>B|k,WqC"<z;&G7C?Dx-߲h?k`pmZ)U^_e$ANOdj |tA T4 \_(IW)dup\Oz.'5*^2#_~lKӧzÍd\intHB% k:Kbocz hFlkҀj >E>@ A]:tͬyc)T Tr 3ZDȃSN2?AfT4Z&:2{M@&̅;#)&YBNI^z늉e06?@TȥN(`u#,0\o#sT$sgj#)PGm`Շ 2 .4tA ?~Swck %h—;Ezt*`n60\3oL8?G:+kiiwĭ3:L]y)XSxZ޽F`gZw'BwR/ WhܿCНҵ+-ljl Y B9%YgqgGD{=Kˮ%v@;(jzW^:R*OޢǑLUWtΎttNy@z|ػJ"J+O.;SYwY EQ,[=%M׊L`&fRX#Q/au@=Wns;óiJP#qhd7~'zo'/W,/1*޾{BX[1Y2_`ah"9MA_c;Yp6=\Q &L~Tɳs2jCD}dbR:doe^(c\*T$s=?" }m{ { : 9Ӊo'ٯ[~ah'b`t˺GǮX H|wF>zMws.'H?wVEQCm-bv QeIgVJM!bS3Xr3y g}o${NI# mr BϢ>4z8)2EyqыtN:R_ #*}كcXkE..cBw$F]? [ugCjd WFY p<],\[ȁ.*ø p 3%n/+SbL5%z0KFi+ S}d &XCjvQcoxV9p4@{\iٮ³B^$YL!K4^n"t4X, 7#yk(7R%E^~mH"Z7JXkt\*Q9~@j@s3=! T6qeFeZf[eY$ 4Şr%:_9ʉ ) 8ԧi2ZPm60" $'N3Ja ge35_N[8ː !5w~b@a'o%{ ʤa^YbT#`r`uƩH1־1vM(c;,a_gf!h;v=Sk&9&T@ z놉2>2$+~[R2d'J_s%/&H G |ЖsI "ʓW@q.ldyɑ;/ͼhր("2>⛊7|w6vV?s+bT3F|Rd W) տ31<&!L&m|:jvs^=iLF1jn?~C5橐<_ ;8}Dqaz2Mv\B9?w bXOUɘ= ?5oչUjogQ…c#oV+"" [/@K7fYTq!U^\ #i|&=Tg1Xcӵ*ߠω'`/cOLg1$Y2"k ?Q "g+ m 3G“Ux{Ҫ,ЖВ~G*LA+&u+wo RO|+>rsW0odE7ڍE46sٜb mw]AĨԑ?A_b ijvQ, @]E@+=ʌ>>i[Ǻ5֤=P&ϱ0½|o/e%Nߵ\3ĖbWAAJKFc mxi`{Qk9xD))=9rڐRи婆"7՞%نg!P3҂{o5 MMǨZ!ϋg9Obm;ſxxq}Gh_w.Nrqvq Sוŧ`4[s.HhafHܭ 66D@dXgGJG[x{¾~lξ*`hͱ質Gz\ƼΊ-lrŵJ jƯT[> | JjZBwq?O¥ q fUz ДG&ߊ+#YymGuzTxv:joO%7e6'@3;.ڄ]`:+/ '=` Ok{{I"k"xPuvʂcAS4,vLg C-"C bnL&Uew:Y7V-EAMX{,[?ٜohja=f_WPm C)i=`!>lϲ)RGG6c vg/( ߰'80:*/ܜ5ί4HRhhRP[vCN\K x>{1;u0sKfn?<2kEªl=6#;!~@HLZ }<lp%*3 &TN]GP-Rs9C YV{Nkpd'G]LfO( 2G QPjK6p|p^i^&q^Fw<,%A:KnHyY7_*S?l`PَBp42eQA( T9v^ H1K*w_̕'*{"|o Bn1 TAm;ޝi#ۆuG'њDt,zhhݑf[P=c\6[ˠF?J"XHAQ GĈtrU]*B{.[HכsH£)dU(B[hlh{m}v\7 c+;Ԙ'v$rdg^U.zuSEPڶG i_:?R`*/ŹS=-3ghu0{Ta̷GLKذ!1@^ԧF`*}_%\D;AN*Jl)\_WDLj-9;)رi[nmqPzlfQIWihrץطQ_ ;?-Q[ f/=ؚH.&j;0tMyEdGp_gxʲ 'O)HJڵ񴽈3ccEGYI..R+8# Z[5'Buݵꡦ:(̌|htA9c]_e*sr-)4?m'(ib*7P*|{mPSL%\zr8({9w/=[qy_ f'^c:~"Lj~I wxXEChe=^(E,Eo%Dc؎kd-YGATng@)C7oW?*ۃ~\WA;>\:RKDdf <1XQH { L$z~oQ}Gbl CY} bΫv v~U_M[+g$߬|ʈU0tU f SEn)hY6+#72xe65AI?ٸw%Î "0B軌= QfWH"SU`{-޽+dF ܟ\ 6TUotYF?x^M{h90kZN6G ` |;p{ 吆RȡaLҧH(e/8꫶S`81p2yt6zdL!QWMIDPP>jg=3@]~(Qzg6|bTM-hwN!FP>^D ? o+U=ьd۷@>WG;(+H.8ы|cyK9/a4΢왪u 񰼸XS k7w= v.sK G('VFUmt=~X&ǰwP@aK"pǧ&('kFScU &SܭAP͚U^{D_u>j+,#KAi|7'Tfy/f4mt#RcBME7 ˏOPxV4U`> @T <ȹ#'cЈB懞Œ6EsIy"AOp002 sbp& 2Au~;#Ń8Lnd}9|% J`S LM{F 6 /um^t^%<:?#&դEI@x)1{P:NnT%&| nqD5A8%fQ|ƃjk:=N#?Oкsk#~ d_i "}N9~Px٬׹i킧|b'\Phh# ^^#Xle|rN>MFLbM3z1b`y|4Ǭ g56(_-)d5ݼ6!l?+BT:^ь$Ka WtH$i#s&7,sUQ}ߟfմ50~kK8ݽ-mRU .&؞ZKOr¿DF#rsh&K ܤn[QGQ+\7$\bayz=ud;@%Z^ \iDR/ҡRY/Hصe@fGx(n@T@_& "fv :؂qK?k[M@QL2?ߝLǥ7, wyc$X9״rasU|w괄)?2ҍks_2У1&oV.>"]k({东+}3LLK͘)k &yp:.:og<0KxB>qG9]5 d3bq :.E<ՊB\pr Y9י%[wO,'3ner- ;4_y?a]P8f& 22FhV 8Mo˝ֲ;oP; *^kZ a,Wi'7,R*P5g@mgF-u?ܞ\\mph.kGަm鰾?7\:T.%F = OZy*嶶HD;BeU88җ⑀]s*6x{ljb5FU~2$ z:#0UɪHfd*}/{ Dd|{"q[Xww" #sm?&74kLetH.a1ЃuޅCfCvOWjLBҾ󤮎ǁoz"VZWtcuBEX_`k=\ByGvj1lF2c#j͙IqE Cև{9B_"[bcuq]6.wc}7 y/x:flkMeyq00.S^{M<ԸuD}V6#VYR_F6n'e+q7Թiu;GT,bs)[5¡12hJeܢûU݋19FOt)"g@L5>yv;.#;_T ) f-ypXZk'oJױלKX-ؖsjas7♀>kTzu%c!=琊mXLʂ 5A _t!駬IYy`uSwqDLr>u3Gy%'- ƕXIyy$L:Q r߉oZu<+WR@d[DzJf5VtfQ"o{t'n 7L:o`v:rB*O*!HdT -|[r`ckCyCOR$(w5vL]$zj% uښf{,ACi˽RZ/Q!sic _ SbB0_I6𲶂rܲ-DKɺ =xssA.x_,QTX|M}[r@Jwn5 -{0*Ocڻr)u;srtS@`$xMmO #t u9fZL=͔s9HQQU}P^^!''LR (8O6 O Üo3dxfW)L1N>᭽nT,YRwr@ M[hS%zRU e[Nucň;~瓰رkB=ix<|k뼴&.-pkS&]ujqc榲DM@:Vų># R}CoU#=LwS̷z MݸaS/kבCVαѣp@UgiyvŒ2YrxY#'Zz8&p@f ވj("i{aNu.VhM<]Zx2؛%L%QļFw (JJ-TIq7)av+M!Z8ur8-54w J)rF׆m⢿I.c!ԿӘĜfEFa^)Ē^&hȪ3MKihs WM{!!KL#$X&`ho]TXvU -KP% )I]|oA͢Aa-LUMS{ahR:^iqadbX@lM;H2SQ{.SPjJA>v)$? 82yƊ- ϛmɴD|R!Oiy ogY.o]p |FV2g0»EC묈w4Ӆd9BJ<.j ,.1L2W^֝ne6h:?,x+-˹\Y+[gy㪉Lri5iu5̛Q4b d"PVܯiO@Eޕbg^a8ɶx/`f]݊?NG9hik1 \Rr^ OS0,\w x,,c<!FEi^d 5#ٓaXbDӭ'{Cs [_bf4o(ks%㈈ ضA? b}1x%fx|`httWw H֟UޝW}4@ܲ(nC[f[m&lc{rDѩw(Jb4SŢ&*UP D)e&Dm>f4TTX :Αo ?Q_ΚxΑVS0Q$NN!s r6^*&h-`Ư[r~ђ +*bϗ ZQZZ"LC b۵RTBG.*Xh=T,O(eϹA05z <j~ 0|H$uOOr ׉;aA[.#hI[g/D:Zkv خ kTp1G01QvQ s݋o QO2D 9/N`ܶ(3X;AjlL=D/){6r=(i$A @dˡVfJtvQgOZ$@kG䐰;cg5\Чrv[/B տϙ+#@?@!hWB?B,PO!4b swZd#ljd;M&:2tEJP eӿsOl\J+n?KF~*!+]?mC 4Li[@%sj3%*H( 1W(MS= GS;!pr v{ _^a%wё O'Zo)MQ-k2Ca*CnN7B탛}| ߴ4yP'C)e g'/2r` 8@ .P{J7Sxniڟz'E>e:M!t`"P0W ҷn3GO 6POEqyƒ$=>0{J@k{ë@Oږ@"Gچz(zAHxRtҞ#VmkOOiҚ}pahT]'睪.b~*!(3ӂo jh+ʷSEk[1 [(A-޵Ȕ@9o#b]%{ qVGg~օ%c4Y{t+e8xŌ忹1l_$ 3+5!n% 9YJݨ+s65nBmGԨ|CZ,߶'1Tx_Ÿ-st_,^Nr_Ѭ+yIąE|1Ex/S][ɑIB31Ӻ(S.׿ˋά%`=\ք9IӋ;HW>p.,  "~Oq1;wɇ_rsd(~P+EuB;BHYq..9pG'w0FRim.Yh-rN<sJ-&*U}UFG~fdfz5$ؘhݠ>lV[ik-5GԜIW9`Hp 6-RhL( q?zۦ5ih Q,8&tDnifk1+Opb\n $6ZUퟢz*X31}.6 zr*B8Sz fNө]Eo[xmρE}7s"a۠ܫ^cJ=E1IlCF, UVDjg'dѲUgyS|HM(mJnvrixK?TFxA>Gʽc6$RʯF8 ĵ|JߑR1Xwϝ8+,M~fQH7*˃.X d7 D}Ef|nuݺ?QtcSKHfs,"PA73R(6x"nk8]DNU6̏B/V@d+?]]Q.1e]ITokTpF}x=(vUhCNFSy~0I=qD3q{!2!sppཾOI̿_r1ٻm˳1'o Aj,&w{b ]9T8)2R2]ܲ(xKXe r՗z슣V5ױ#x^ yT5M.TUse.F2$ f>CʁD [mK繲[ApN'+r_KphGZ#4 H@4`5Ma [n#1O]߉qZ)_7!EocBLJfۡlHէ G:71 ]g  -6Ok9,OZ ,"RfvguX+.;PC( ߎ+NV~XS3ĂUn0hLn«W`x"0t;5)}.x .'nsi|{X_5duB9ؖp.Pɦ; G'庸-zВ~EFI۲̆(ThD[` +gT{x&IUžEX&qhIEE ٩fW sƁOw}QipCkJS*F#NK06Q !N0[G`!+jCQl*OBz8V:Ukm߱!ipk(1"ߕyh8, u [dsz*:1m:鵲a=w͒8Ugkʦ"'*;eY]կY&Ux 'u.g?g-6<u{p!9g75"b^wై2#vsMIR&u%@Wp~3 u'?|d.ETMA i-n6>RۃT1T|Ck6%:K_,U֥ P쁮N~5T!ZE{dD[d7+Z0XZS̑i3UVl a&#Jpqpgyu< ֖Y;Lvj*BA/th@oM>[!:5`Ba،pтXB*Beuck7O hO[hvDjKXH̺q p)?C,[~ι[e~(g`;Z)E%uXԍRt}74P%ßSz#|*%dO C!N,\$7,%0 vM\ȣ&+}X굈3T8+q%̲%JݼIKa(I AWu]#|" ~6pfm^#TP~`0d{*ab뺜[ g@>3o7ebp$C{"نV]Bb6IihX?UJsL40pt(>v+8;ߵ'B{ȗ"n:$HH1=cjBn{t&JסY&̢Lw56鍶R+>o!8% 0TaK|_1RY3ǃn‚CEEɌ4 L,@esZ"AG ݗ34\h ~6ʶ^׮e`pVx1Vٷ5glӉ|CYfHRjIN>6E$+bw3hv<=A^ܼBt7YPEsH8#uEq <Ї:F[J|򟬣c{^-Me?!yP23 W A@:Yd?V85RBxգoc.` m ȀYኆRr~ЌpDtZf˃}с}GA5{izTLRLk3!e]-Bl  FSci;zY=WW\ pR$Kfx-C~5I&lD-c 65(rj7B4A.ֆh1f=sRM0 Z/j`A'v(S\V#n+T~ K|,4ֱq& Y_ lIFcn6f_o$%s5?;ѵ؈,g}XB=s$pAl5nl͠%9Y-#gs /^h1.*Oc6fɦSx\Ymsmw*ofRqxp#29JDm9jjFhEq<\ YB}M0`&3V''>)4\5I,.S#1W8KMREBJXIeGi l|qe p 'Y܇w5g3wܹ:U/30jth\wN)0JsԗΓl-%{n5WWdx -`խt>!qcMrdGӢ\{ s Rs~ oO"<15RQ8P, X9PhyD'?ڪ$ѳe˿.u#Ҹ 9M3`(.,eef909 4(4R.pOJ,8 H/)eX=vX]pLD,[wb΢2dfmdP`dop}7Gdi}eY8^f2W`Cl~ ;[';<#.JS'.e@6 2If]܂)H,Almflb'DsT-3]_4&I y/g*z&?|P~Y+)d+E^\m61)pwn;{,j1'/cqtܬ<ٴ0VcAcPf({od(؁zz@,E .;EɿԮ'Օ+WRӤ@ԒEO;=MckՓ)MTJA4 2ػaEw#d0? f!(!ZnƅZC<ͪDlI{JK)b?tdOI\~x9 r.?̓7YGt^gEF`lC->7K^|n{3%``m>?pnMծkiȌ{Op/PХ{'d`y͑qHxjr]A'eK%[R8aB _?f1ț#'1.Kz.QXcRWpY$FB;ǎ|.x^SԨZpa9aq 1D); $Ȥ4nzeI.uI`m*F(B r 3ށhUNo#|v蓲B4\ O.Ӏ>ԨU =x:^ := V)(M×GDAw;$ kU (jl;f^=Fg"֊;x_+Dc*`,@ l>Z\@5F6I{Gb__~= ](Dy`4|U:2M(F^~-`F#Gן9&f=Ou[U`/X6f ~)`>3"3gd qS}\pvIBO;{מP~[ϭq,"g|J tדhӶSڹ(~~QP(Y?M10r9q?Nƴt*rs %+.ӡ Ilͅ\C=Oi.>Xb=Q6_1V6MvBA !ES_lqڮ`xu9óq9W gMGc^X4GYNH5z.=qy)T؈/'C fUx+~b.iG>d&:V5܈O=r<Q`XplX ƲCJ&:VSi 稕m=A>"A0oMp} Κ^2-DC4cϥ*|5# +M+ǿ#v>Sr%?|OhxMy, }0jcc]:yi y0ֹa5c&&b =`(*%MG.; }dk\AaIb8iRu|̈́IA8&rJ s [&܅9tΠ Y40% 4"F^|nwM |Xtۅq3_zuQ25S-⿻wv>-H [a<JٛWN͇I%9 dN`0(˟R*h|=$x+7K+gӹUtu ه!ζ 8C7D4~ƍ }RoUB]_Y9G1ёX~H{%mR0BuNҖx%ޱ K'w"oJRچ0'[Xj#0bYq1G q )gȑ+*'9 Vɶ bawӼەi-kmƠ~\?\k5ooxU7qF{HS1fsH2ff/Ǜ"_Z^. <>} *Piɺ1tv?"lJ8;e?DmZ yK,5u7Xvr? [&sۗ.JBRi8 a-ɦmS7 rCE[I܊k^-񔍴`ѮRV7ƇH73k%ѫ-<@jjZ(ko>ïX/efpQPR"L5JmYY[LK,K)cZPz̊{8vAV3U!m3@X;8|7B3 gB80BktⓂH' Nm_-$xDH?BX}X]J"(ޭ}عn~\ )B)izRbZ 9&Xqt|`"_*d~<i4{z> jM*Y:-*r09ɷcH gH-ۂǕ]u?`%´h'P 1l4) U)q.4IY0J%I6wiujvVGۼ3$]pe S<=M`QRşv{ġjɋ#n-p;/#͠BC!|+CiN, ^0E[).D/-s%P!1bn_ i2@E,5|/YfL!rl׼3zM+|dXn ~G$ұU^(*mUִ-$:C\4t,hnATOR:Y񢒹 ɼ6 yO ؎չL]b}vRsYPN"UDyqVо3Wh[]ڰGēHQx;n1E)Z)Кfs:_Ŧvi21Xn(v'Jt:9qؾt<[>Rp7dRH*"@~/N؏j=HH%Ab")PU!^%}nRSRɟx9 CZB/'C*sU.Oʵ{$[-^a@VeMN+qy^/jvϏ ='G7j픁NǓml ΁G˅F!w<9t=%cg\tB_]BYpݟa ';., :V{)QHk|e!WMЎ }zG@,綈w|Z/زZrkqgU@;Hx()l'`s뱍i-Oi;P>g8nJs?E̲7Y2|̫{WDATH+ʍ_d}Y[ 'Vp=*AMP2h`Fe{XKhɭtB&8`V8WԳuS4ý*;A; bB tShg家PÊs1-@U^X^u1Jr"+ʴPA2g=WK6PiD \;0=?o[ ,!̤B)/hP =>'>.px:_*f:{nW-\l5oҩ1`r؈iCGnSs̢flISD184Nb LE^A2RIx3A ug\ ,a|go i^TأyTNDv z:O|BDsSy;(_I^J-ʖrI熢]^[40z)}AF$h̚ݦ<_Z?G%7"tCHcⱸgv/mX6bx^>LsSsͰ{3{:튓.ZQ;G{_&ᴎz(m]w.H_ [ۛ-ov N|l]I@'zhVl[@H\W^fsM;J"u|dG*s 20,5l,`8Bpe̻s2存"P8;*dJdiZ"}}ڄbM}#y%t:DAF5:7HIB3++Uߞ_&Oχ\TXMsڊ)ɒu!5uelT#AdԚs*|V֡eK##Ui&H5(!nhv4i}4OA'-Y+Hv|_1d3d]7Xrn {^iYΎbbdEG cDݿ տO9NΪ (U é#a1.1豅G~ 1h9,J5| 3k.#ތ^ؔ^*(D0.؅oyAt'b=7Zwr pS>qu 3V8=$PFl'@Δ;n6jQ^D 1OCuj GC*b+0]LO &"F)זMުK:eE8sWu 7oY255&DR 1 bw\M;@ۘ,^ݜ !UQ!e 煢gIs4IKx'rc쮮F:Ti)'aŢ]T~1p'ft$ Y f{T+| Φc io u^ gyg0_t %&PA|›߽M^ {r_R#5N-B7. J<PqjvZ!|j߲<sMEqmBV8:F IyzJ+Fn' %ooT3? ?6\:^#!IߕJ%eYqH4S#6픦[M+O6Se݇wsS;BbjuSӬ{uoLRN@B'OI ue@;Ȓd}RChݎ/@ršq0w@ȕ^,YNB){--,.K/bo)R Niü3c?!9Cg@SK;E}V{fzq >1ĺ݃șX ҼLn-ksر WUX* CZJa/-?̬|B;!;d/H6ySG`~V5B& [Y'l]/x6N۴zy999kk # l{} h}]U+ۤ)UnZdA׽:*{ |lʑ*B1DP '%A¼GO/ԵSTP`e"8"qO`2c~\CL}#dSxHd0DBEn?~}OXiJ]^5RYKW>r++j:ߦ@6!3cTTN_;c$#^~gg//=Dfv8]ܮ[)Z0q+BTu^*yb<+C˛)m C? CFbh#̽㺥?Z hJ-~eStچ4ޣyIӅ\xfQ0~),?(6;.~`=5/j|H/Fq= /Q|yWSkXԄ/~VV7Ɲǀd shϼ3;B˩TXu#2G֠# aNCXvlQi,xNTBIhQ$!)sO&1!]v4,_"HRG%}xe^f7n ͘ N/dAuQÐzl}L&(C MBLP%;gR@!hXm9 %)oC#ҖXk$203q)&j&{ι\Pr2B@k!Vg_*vA'GtMo(uej+}wϸr(4p:YkMow00Jږ1 |Zo$,KCXV"{1{&.KV%`T5``5 0&Oh¡54_ϳη7`dQګ^۲5Tٕ77f<40me&Q\ٺsy:YGj=ҏx챥C.eoW4qVXnխEQ?Q_%ey~jԪtV J踪_I0Mr*? )R͊}~IXP0kw ~[DŽql >H` f0&f{,$ #$\v!8(~pDU yY;dlרA'%@Ĩ~gZWLtkN/*di. ?\>gQ/-!*Vܗ]$K2dKj BO $)X?<R̠ErgdTCsEv2ҫ*vUAE7$uE.x' L44Į~?8+48hXяD<+nhgYWxf,]sa42! d(F7pmt۫~(|`psG`֙y$tGhSF2; Z_`eGÈ[WWv*հ 9+vVJmVfێe6Go &l.{z,‚z,^smGHB! pDS-1+$Ңw'aaܚ=P믛3xN0CݕtdǎD0#:h9{ve@6?qv@ g$ӟʊ մvKM`ʻGfl{/xT%kkI; ƲEؒqEsiܵ#RXX?뎒doiìGj{ ~>]ĭ;RWXg]<=}&S4F>g;Md(HPNJ*+y-n^3͕qoS%KY |;q?}!\ 4[`#H^zc7DMϓ?bs&iC|»B;!1{ ̤5;Kʛ&iLZ7I$jϼ 'fT~Eÿ́ϫwvw%7E2?VRt\Ndwydn"O _E6^,#% iUS{Δ1Ǝ:]O[ezHu8;1#DBӒ*Q$6`dT!99H((Q6!brd^=gřP~'yZ8l 8ĕ%W -,7q;2-0;Q!l`uU\aj-rޗw&E8k `wa{yڵ,4=7$'(z?޽ dwLA=r>!iJ q8FhtZ [*ads<`va஬Zyǻ dҖn4٨+Y5qِGU  O\|Au]+3ȅio.q)<(wƉn檈'p AOul wC?E \r yGt,K45E) >qE E{wlY@!T ; bX l<iKOࠔΘq$8ZU7] t*E/N i5Eҹ}AnFGx3?i/c{>r/#6#.:wZI ` !b1-S//AFR;gQ^&A'L%Tx]Ws71ng% )"y8E6ia6 |JM~9 %c eQbODTe)e lq-dQ/z&9pw %:p[ ӵk߈z{P*"e1wj ՜+DEaMvwK{|GLsd%]~/P<_(:D7F}).Z{kSH^k )`l ( qQ앆(d3ӡ X ΀Ywi "cg͍s\pJV, nu.YߝRk$N#]%9 !@PM-;p3o |swr4BT4Mo"ņ0Jū}=搲C{'0J!ҞJN+>zIR+%*6!QP+4㘓{ Xbz[ #"&Y5&w@=PH[Hd4 (!|yj,)"{eA>X;[٨uMCl?Jm;uw:$m ω /]*;0V% "Ց3}GTf{T`6w&3(QK'J *`N| yD^5'Gͤ~?fb؀Ϯb'U*~O`+׈f4 /eK^)+D{HRʄXd\^g@E?a:v<"Ӽ޳([4:[]kߛsnfϭr 5C71 h,N!n2QّS%c3mUq.2p&Mo/~ 8:+͓A sޕ=A%>(%d[ICZ6,HGH_juFoiu.i=:dǵwZAO, $Z49 o@OZ[N'8A_WےVI#CY2BZ(g2(cW6+:/\x7Iv@9IR 9ME ;rtXN1J؃Irּ6W} ś> lZoB־VH: XT,IsM9X6|66S28AF9#IG{/ TVgifOK*|#Ug\2 d]pބ)HufZ%)3,4/U2EI-ZR0DY-:3QPyZ1FKcP("N;ӋѼh zاʅ)6H3a#)êb)%bP&+^6@o#ĭ s[1NDE44hG<9be46xMlYfa\m~aw OE{`wx2Hnb-UM =xFdle^ֳ|%@t M | äOҫ Th[ ܠ&t3b`wy>Zk4L?>H ypګlCf9G +Ta)hB8m ҴD9‡HtpPdۄѦ)2&pƓ?SYbfa3^ltL'"ޛ)){&8˨繆 v#X'.?Uݧȩ^#.1RZ\#Y7׺6ᒢ~:Bఴkev/Bw5]bLu\tapn=EgȢ|N]F(n}>z+3 .1Ol¹=y`Ol`Mt JdNYE#M.qQ"": B޼D0)xҳGLͯ$vܯ^ghT~,d-!cXu=01>h,ؼR[d_Vȸ ~[N3ҁq.(h[Mc#>; dXfqa6`}89uMEO1>֢0Kr?9 Gu_Zy(+?.mbX4^Q+KCJzDJoQ!̘$Wk_tP-ԛʚsA1 Fb'7[hiRCZ)6F٬o>-=˖CfP(j_犠x!9F :ߧ@RC<7a[__%Ns 4EYy=8;xe@2_$ǼB&G(&Q5 ?پ~Fd_TwFQù@x5cq !OVTӈP|To6"hz@@]6XjJb}Xٕ~J$Q[MgB/u5pIZ@qDN{`SW>fLV0?]/Mu^* %x1L,3a  4~[OLYRRHm߲9<_ JfJEw<ׯax%;ĪelK׮-, Ic;n]h'=\z` y '+\2}, SlJ|lj\Ȳ'МLA^2%p2daqǼb*Aw&4{DF ȃ(`:B%(,k{?,XXH /`O~;"w"\v/L.hD rM<(OxR-Xt0G%}}l¾}v<œ#4ᶵ;|HdI?cuӧȣYL>C|.UP 8sg Ƞͷ#͘%'(FI.ywUBd󞹽ܼ({"30 ".f3 lg4x1T2;Ä'h~U,Q\Hv.OU⚾>)Cm͊S ,eU{FȊf-ޚ>$zH3~VVe6|4?$[\_g_Th!QEDB0)rM=>sX]2e.\V0dcƪFݘ^Bj(FVK~u -j^Utm`7984 MA(eH Am{ Hs s{SϧEFL\؟G #Hw3ǺvܑMʼn9,J1⨪ڟۊ6|,꽐} Toe;3| @]gh& L~fK n'w y[;@ОIu-[m6.ַuUIK~!3g[֒<}=<~[54K9(ϝS<<~ FS?f?Rɷ$!wVBHnGb058 !*>' R9wXQU 7"RşEI-)[ ))-K0ܫF+1iP]wi \) UM)^OxI?F@t7dysz ;J0n$:x 2Q o[)aɛK\Z#~)cUhb[Iatr+O' 0XԜ \l9qܷ>tue:YvAbWgo3XnFzXӡ6LyD,9y>FQթmaHU/ f\Lױ~a \r=-CU4G[憭(䩪#z}!/f_#JָS#&s;ɴ ¾&M)z z}$8  +ܥwƑ7lW&[ 74tLTW׾ٛ6\*@X`Ke`0g)1جº+֚y$/T.U]$2qb_fULw 0<QY'(Q .dڗ,(J1y  sd7O;?#etzӓK0)]g'rtmMzN-+¨rY'x*]1\=hjR5e R 5L^3a@<W; a{Bt:HMٱS3O)"Ӽ4M7'ԇBAF =IຳSQ>@y4 .52 хCcg6T݇UAziM  Q@)hni-ڛ$_vIPڥ 1 W;̷ Ę *+22#IRQ qBVJ/]*GRvg*^3JB!MŧEWh?iy'H+;fB>f`m԰DMbwk\ۏE3>Y lb%pR`L>&4SDpD{߱:Oќ1܋!cH7\ D)&ĮS*`ݡ`oU_g217oH-Q |%,/p9K&SoE:fWS%4_m0ܰXD8s0&"Cw9D\P͡)Yl|kx ?~b*Je&@vC`D]E8P3b5Ԝg6u@1/Ÿ[$h,Tt,[7]RdE2Ú<\^RmEyWĴOX}Cg;#(%,D0R<5$.THO&C'0' m%(LZRb]]:ޢ!`5l,&̌~M,Ճ-) S#5kK&U%E] F?, NH>A•Xi{i~v@Z;}DɿNOK!e 1Zݯ|^_|@XW|m2YXdT([Y!Z`FnMQ/BZsiBpx lw\7nӠVKcC<9@@kȼFdԽa@( ~g%,ASQ)466؊~ B|Hev#z%fb'`aQj0,s}%`x8 7meB?3&aT[:B5y9sb,b0"ػ o1d7(+7+j=rFBd6ܣ8#1`H4Pl|SZ~U'e,X ) Ӝ"44CMvdj^TcHZ埫C8d*LoӺNm+ `).e0YțTck:}FS791NiVp<,{Ȉd$-aaLzgvLe}/ӕ\h kr0M3^sG%ƌ1Oc]:zlWqyy I _>&MVW fsu1I$e$X鿜qf&:6F\79`7iBܺ}xɝjI1]U$uw|$w?EiH7u x?\WSiF;]`́wN*a YPN?rQ!=\'AuϛN)b.P= txVRXN _`sB==okfqn9!S\e9*t;t2i̵; "jc6 pG_*!Z4'$لd ]_p m=&UZ̹^}iK׹!,,@!a }hxiuAD\b:-7#b6_o[2h/Z?Շt5A,`㺋8=spo\m i>v _p,7˖U !?gVda{5JtL)h[-JhP=9u V铀urOk"Ezp?TtePk"fEk]jX.GhÃ)͘[ZZG`tWסim0G}FSMI;7  l]NӿVe\mvCq}%tSa=\g챲j _&X>W)N$'Iﷹs NFhѴ4uHimJo|`{fmombO^HnR-Гx h,E52X 'n|}%&um=R޿xJ-}7NdŇk92RX_eIn|*3fntQI@+4_yILr5<VІ}6Ne$D(\YRC_U#kퟨX"nZIi鹿T[.ooSKkIfcώܳWKExV#df-'K,NȾCVQn{^?7L3v;[OY߶,<=Q z vƬg mz8qI>(Wgtx"~ņCjh^~TucӨ/i{k^_^wyzҐuaP@i-~F7鏫2Mrz=UӼ=".A70 j_UB~ K)ClN51&>71/U˒KqAV^c0'm:? F-_Z)[ /ъT*" (2 ]Ie n'6zɢkV )ğWs~Gsa$+njYQ/筰'9YL5A! ^xS<),|& YNI罍(vGP$0%3Bg :g;+M;7WzFl@gH-X DAO;M(p.%;f,C?&dMu@M(n ][cjQQ="HFK'4_ȵ?^p pA==ɸ?~h)N!iu?q|y"M u'УE+hLcZh?z;؛4s<fثV:B]@Tt땼lgWI%v XcsնLǸ¯U`:YKSI"do8dwAB-h4'ec_!R.A kZ)ܢRiP䏈L]AEvQ26*G? ACfwx.E+7 N[T5^aYo:r~||Q&ݩ aL=0-VQAݓ^=jZ' :)eh_oc읞01/\1HRRd1d&ݱC³1 d9D si`fF%إZ邋Tjnych?){ ,ض1$SFv[*&jv&~wu Y?~틵A+ǎޥŢ%hpF u8lPM 3iRX6էX{k4NܡidOvA}bBǭ]FOhh:SaU 07. ; TϟS-A>X*a|Ӻe DœhT2W&znޔ#t1+\23?6O iw{2d_@iy׵|'Ojz( ]PC!Eƨ#fbYXrm8 w $*pʻ&sYT;^g:Ϩ]&>;e\u1nW%|!zcP) V;rX!X&psuGq}ڸǼ 麗=ejS}|lYPlnQ,[a/|O`:kkC3fRpp6EA$CWp CSLHk#H,ecd=H9HwO&_3,Si:ɑp0h)R9{BԾnEŒ{rfKdѺϜ*$ O(-vx$h)=YQ9h`Jh7g *RK LBdEKmuP^ y&aB ߛWIh(~3评>pc+zf?XHcml*H\VyW# *"q|4ULVkp5`;L׼jȫ̏]H{hp u(Mm:C]J5bX ̹]<I#$q3'' &6:*oIŔ_6P$~D/--֡-u݀06Y%}183ٖ~saBgi&܂ ,`sxda'2Ak۲99Hq!3\ɏ%Gd (@rS[s/+lP/hSUr~󐖒q2—$rL -m%nu^1dEYm`@b0 I1%Vb$s[QV;Nk{a=DmWYQFšNZ $RT@ E^]䑞&iÒ]-:*`/r-~ LXObQH;g%O퀭^'qy_ w*nQOʧd.{$7x[ngRA-RU&:a["L?,. oqD)sE:Oq9uLi*2KiU\T ~)VY[@{\Gb:F6:uT%lwq4&4n3Nz2JPItGD1{|e@雼{x!>NMBjChu + k B(2iL Mch0V.ٴKeu`tӻN4—ꜩQ}vQoQzbjoP<w' Ss%kȇ k>ϪΞ7')GI=Njfp2͆ l7{LxGu/gq2˱ XŤ /ږ7+,P9_#u1 K:[VE<!cUL % SXuâL|1o2BY{Dq>QWc/ͳe/]]X {s[Hw3ٵqaω8 !W5}}~9l¶tka4s@(,Q%U㓦)4.d1Ėo?! Ry(zg_C )0XJ ŽZ\-#t ;t9 :/L )oFKi.xfv7hnaZÇaimpJB{&*]Fg&ZC? jbGuhQ8?KHl,bG˭;j[(gyFL{嘵pyՄ3s;4gC. @'t4WjR%6)8!ň񢬬Fܨ11a2.l< S|be4uMG3E9{{8GSzC|ΥeNZYgspP8f;ӯO5؇RPJrCyZ`g“ǰQ5&O;)brK;f"ԬuuGKFBl 3>#\"3ߴFkYQ7O4?|uw6X z-s*9@Q/m0nzz0<+KtUyfY͔J66aQRZ||~d>(^@ü+ "9ypq1dl((dCFz񓛞RGc>iGTEz==DE)%WI݈) " 5&_{ߨk'}ؒk/v˽hn!w sR,֥66S^k6wNFo:^ji–u# vGweCkkG? >jݹo %B6,3l,9~@{i4Ǐq~SŹ79қ"4 4yD3 ef)U+vȄ$,RWC4٭}|{]3m b̰ňjelM{\ ;h\sj |A@3oio2De</PWW d9徜6&#6$7z_},/9BiGwE>n(s~/NBNRt{qJ ^HPEQAC06.CSKB9]Rkg-.n7Beculj9U tsj_v܍ztUz"E|(5Yb2"p;rrwNJ4K[$F*v5sa`ށ$QCT G@dB|'l^TV)OcSk:eMQibT}[ifMka!Um,U9k"Ǟ=O"q-;Ib~ekX[͢3\Õ ,ݻ-eaNȻ$EŜx>>R6Rr>m!خpo|T>Ѻ˧3ƨ;j XhQG4>cW!$oA" M)FT 90V3-<-۽g7Z:$ w<~{h! hEe*oJNvņY}>)r:}jG(_`^q3E ܎7#k S9|eDw']s'􀊈.Qx"ҡ ?pQ%Bi  (.V3}8TtTm Ռrۃu4|)%yZU&iS^SuCNRhhf7yF*(hlF |?kH<_m C_< #UnêC/.Աݠ+OH0xL3BF%1i;@8~ITO2W鼶z"1ZM.8V u_|X_T>z̓ r@ihME5;$O}`xOhjf֮M% wsNe B=s=5+C[JjfR=-x PmN5D__,{JnykVE}RQL\pL!+ԎQ7("총НHHnS9HTRC{[oqo أRV*1ύߧfT8PxTӘ"_2'UoKx+2]%A8&`U<ͩ(\f؞gxSV;ρ]ҚX!i1re!\y+=>׽9`x 4m U/֔NF0hh9"G)\=o.gKMPdF4=Q @}k(^Sj|0ZSȘܳ]u8Fa/W\=psf/ݦ6LC$!9*P!I' _3-8uu(Jhwx'lX@d.!O…2دUꗴi@38  Ͻ-3 2h*eZ#|Ea^{=͚L%-Ұ A3^0\&|̼VY:+WE$SAT,y>iAuB` H[qћUD굉  T#H/(h#š3( ]-}ha,g $ApPл,xǣ!78]ɰ;yiz 6m7tq@>%~kPЧwT9 #]/,]YǟF[]r >b)*29VÝ~xRIMUq;ͷlM:F֓Fcp%)8452:c ZpWv !Qf}:[fII\qe tz5]+w+Jq1ȫ3&~t L1KLʪ"w :1c$q\txs ^\T[˨MQ>R,hЁ֗%( ծ//uU:|5H9{J#y_GD&ֶ$tȤezTJӎ- o7'JDj挧2 FY[N v~{IQ鏿K<$Dl,}U~{rn7Uu=R~\(xs.՝ˌ?ƫ)sGZ ϫTA '_rOH;"&h(M;eE"N)MG.HVEDֳy 3Kޫ ʞhAf%˵Y)5ϕn:I1M"ե{ς!TdXoe&<SOl|j8|Jc`(F[|oMGOcXͽư&91`E򎤈?zOMG(351{>V֚@KP~9 P;]UCZg!'ˣf=(pK6Tb QuP鮱\N4͘g[zmiTz,;t?W?1礢+Bnΐ`j h <%c}88s, FWr<> '/TU^7# -l{?۶?upP: ~֨j+hn|& IDNI礅5m#*52sk M#QA˟2 5P(O©m'"nNӎKSS}ܑm~ن\+%0_0aПI*uAԙfDʽ'ɻi1s\5뙍¼aY0(J7ə0%ub#yx&3'X(>2ߗ)r 9A0Ţ9~O^zbbB=7e8kLhl<(=5J%S1qwm>dSOd&#W&ίÔnP-8YWf-$oMRI2 g9>sۣVbLc;XKU8~&/AI^ڳb+JHx!3--؝k{oB,vx6Eï1v TF,,AfE;-PةT\$VU2UT| 㛖&Gr][(+fw(Y1ۨ͊,\r\rk3%+̉N=%0Wvx5*bDpB: SzWDv C58P r9G8Б`*$T;^GG(s TEl2Q{pn>?6oh@}eeU љd8u,Y8 @k 4?}:mIMD$i2(?ЉliZw`&ǻ~ڏxtkA9- c'ļLX&@#Nw ( DA¯M4`4+鼐>VY4S77xߧ4J:+!Tқo{>d3mD8ZmlЁ"gcrh/eGg uN:@t V fM 0Wl0 2a?W[V~?#E)>v?:] q_%0!> vE36[md8s7ImtT+} iLHXI$G P{i\o>A@4Bd͡3{oW+`6Pel31A(DK@ HG(sa5nhV$7L+$|mt/M7^|ʤD!YZWMB=)h{thWq{L_\,@S?&e SF}%xN%@u՜Z;-ZQɓRqm(l3fim6u~ˤA#VǛnke?ՙK%&!ZP7 h{+5%B)(P6VNQ̧N zBPh)_S~wnd}vrR#] RH mxwI>dӡC?)O*2@Y(E "(tW3Log9:B3($ 4S_e~Crdu}_Uѯ|ƿ}#|֬Vtډ4N1qbӳGhĩ%(1:ώOOs*+",#|tccs趈F;k%|AKEǹ^w%2"_uWy2ZϡbtT՟[eƚY}^=  zQl^Ai<9=E:E"p.CC6QfУ/aH0;CV*il60OhגėjH* #}ikæwlFzUY5t3#o[3W؀e6OZthֲ:!21nIϕz qʖuv@ 46u|b\z;ʥf/X`(x_!Ts3E"$lW!va>Q4mh"$7<R=;ЊQfy('fd8Yh_q?Kb.8ʲuLk{&nG/a6UƏ@X،9rI9kG4qHjVQ~6+twZpM,|CzܺBj~zɢe0ג"񮋾w|ܡ-:eCbP(rL]#Q2T3'Q.͟w'HB w*ľQ[%^ґBflϖI3SILZ_f3|^&IaWt+sk>Vo30e[E*5!d?r6ETR_ aLάukt6@cϰt][0 %hS˭E|Ѣ qUD_NnPi[uB#PnKiBZ@5ANuYӝI6D<_rYUd[<:8>RCĻᐦ-D4PIV8n[qꅀ=!"{wk +s;V}iAUwd:e" -tҮЍ 09e>F.~ Y#0NG n1ޫf0W֡1-qe)9w ؘmNf1RHwzIrOK;#7⟑,KT I[_>S.S٪f8&&e{A[.W֋V]>gȩ~טsD=h;Y"o뜮K!_VnNxڥ!7~9}9K~Dj]?3l#lsC@ݯ,k=xw*QÛ4s1tA ]T F-{?eSܓe,;2;$@XώuK JM7!A@ 8dCgp 2loqpZ k[xJWLbyΘ# Yk#V`m֍{XH5Xg p_E _Z-[z.(~W~Cѩs4d/W`Oɀ*ܸS5rݤ+?gwVk?8-=|q, *M*w:<ܴձW00)=k6? KW'ǣj2cf|ܲʞR`g@A>Cѻ)4!X0 |iR襂P+^%˩-$u"Џa"6Clϳ@;`e)=#,cVjݴ`::ᓏE[w, ,N6:jűUi겆k0gcؕΛ[;`Mhh@0l؅cxq)![r2Jb[~o;N\V /s5vTٚ?>\q\/(؛$` Jj{/ya)I7(eoεj>=u1bi0k$E\vs&g+T6M7kf7Dnh_%(tRW- ,daLl))g2q I͡قsxܷuL:]MGYG=uOfxpkwTʗ{"}8 hktq[nF?!B7XL:[R`O Fv* 9sӶ)L7\i&[(ѐr6[svvMꣻo(vax| G+ºOT=&DjOKE } Yo02sr;5L=ރ$@&[iin moF[x9*ޅ16*DK\s^#e^ J*:ގf Fn _gBSDܰozb9^dP;u`8.İ"EM`eC(OkvB cae(/*J.߇5 ~ -ۄg.=ɠrf/ovu˙!/fo$&WoC:wʏkP[e8x{G=4xdSdJ =\S_VOݬjInqlI1e! "pVJa l4fW(iBf͘GUj5o7[=Z|SsLl ӧ@àԉL& =^ 7{}1'w,,XtG hw"^D)PU%SwbZ}V-Gν=۽݈g}0L@ 1q*7+%ęiBH'II* z˓?+ _1(1]=†q;ԻB F<[ 8.?]_D>:̗{m:8YYMH̢)\[^\$<}_z']HaS گ0 , 1kszv^sP[yQȣtR"bn`QquJNWcph1HgV*xnhL/nf Sj)SۅD }4E΄qd(ճJ~DNvrg}(ipBuJ? |cʂ6aM^V4CoJ'mPR}νۍ-=Ebs :%;??b$ }:fP(JTН ko5@t~h\Uޱd$Cfx*4o¢ndn:Lg\X@OxV)>/eġdPG-g>={2szۨv/Q >7}uT>ʢ0#)]b9%}{\?: R- ^B_("RsYmRscXI$,:V*vk ƃ<~cz7˜R*|+iCe"~AtX T Kˢ8A8gvYjmqlʴb,pECaTaJ:quLl'qif5L PerrܭcS!mI`vV[Z!'V-LppDbb T.(V[S ?ڜ3TO$猋He&TBQSV[w3sَ$RF?s;qw8*6ټPZ:9\;UCng+q 췺(=k[CbT7`cZxjV 6|."o|Lb[<.VtT.YqRՎǠo)La&]3S(yL!>&AIkS;tz@:CNh~(SA||i1Phl:$BVOIV:yA5Y;iUN˒|`ڐ]I LReVGM+!vx )w}mgEX-t_ 7_XHesqys~[SC};!W9s[m;|-=zq#%&r6툎Z_8D\Dh%lrGK4zԘ,Ue>+ Y B|#kjC-sf67s蛰'j*AHlT֌]4Hx׉'\߅~wZ$ҎdPু!L=5-RV3IOGc[xzyhYׯ.H%cB,xpH' ")4FL>zTهVVaOkwȖpc섳Oh:vI˿CU*'<ީ P֊P~ Qӧ ~~q$ăj r/IXRarxuj#n~DDÔ+R;_/M6nؤmDfGi`P.OcUvxu_x9: c&:]~Da=`_+KoǺ&}AGKP`^$Pz1EF _烮^|%iJEdhGgBʳF1b!kx/>}(0K]gcdhKϻp:(D<>r27D<{3z@a7k4ugSk16xse T#L"jO~SK +ĎNhQx 蛷Q'™OUR=7p*{TȥQ#Ixu{E[!<"0O@5h2M>Ϩƽ0}@ιJS.K12P4]yxM,y,\^Go3aX)X֟o2tуjq:~xt&'E5AEHΛSA#UG4+-5Tg31] p&Ð.LDdQB߶ţW'yHK#WpmʯLnx^eFn {DUJKGd7T:#U}k E-+1q>MXT!Z@Qv1ۣ9jaz깓 ˙z1GȽ|qNnB$-ݗ(Qb,P T//!D ԥ>KVB2ԖxKcT5n$?1iG-VT dpu|Ɠ3Za`Vɜ򕎠dmolM6F^(ֹ@!SlN9mq QʱEJ/tSg\>ж\ѪGٖE90KIh@/@G6i2^h@q~]zPJPv#dujUՈ$pL4B}fK-Y}HMsۈ{+vȨZ# { $>R怺l@X&Oͣ3o5#H<[0 &84RQ`ey H)VO&hy) ೞ\4X@B)͛ ZSlFhe#L$b!nOzF\B C+?@"T:}gEg410 |e* C禯) &k[YJf0gtO7#ٖߥqj榴|k~'& +`HT*Q\9zuL'EHD9V{6%I,t WJX}}+yt*1-#^wvm\䓶ELJϕͲ1<0?w{t~༲EI Ҧ6g>`vj_c yxhǐwsR)Q2o%}8[8m_?zG8[y=ڞ Fo^=}֚@Ƌ BfNW Ŏw{IʍB4.*j,^:lRqNͻa!jEmCv֣76rMJ%KY蟒/Eˠ^ڙhoyx["%M4; SfO?8 iYAThB 6wzBR POM3Ԕ/ ^i\A>L)-AZ`hE-⹎Ie n} V\dxb #Al=^.+.BA4^M2hFvd0WGTm }ovÜ~f(Ly!(yьhWD\^EÕ5t4Bw/Jt0ZCUq,ah,?'#*2^,5ǽ|m,[$Dobl-(;!Xc~ b4FYc3c_ѐȺǞ"7o+ИS@w{(wDEPX P¾{NUf-5Ŭi%Ԁ,(q\MP'Sq\4-~'z?ًe.C O FCGw"AHt'rԒQb!8_KDYsH3wfa_-_r7zKuès0(O}Xۣ<*[B2ȃe$*Uiɤka)ȧ;ɷ{Oy!u}Х) BKEϥg%lu$)eh~Fv/V٪\0˗{PiG }W1-1H9Kѽ+㵔/҅:@Kv.ϿoR2\ Ym%9e nbGjw{H uC,sUp i[Ի͢uaFTc.zy evưlem}d 'Ue.{^ǀ*C8Z#^\VPSǰe=|*fiqGكKĦ4SjďB?>=CbE\U%|} ”SꊩLGF{^̟S阐ERȟ!Y#_nx䄏kA7E7x-N:zkꕞMÿk L}*e:nC?W4Qюw{Y,`7Њ¦׮}Gv>Qf$@.rΛk_˸%"^$Lߴ4 r)?b !,' Go@$A#++gÛsN"tΨ57WQ7-A:m{bSy1aLmo'm?+~W` :M?U=SAlz \yF>Q'{1ҬzfWP!:m#KdhVŏHXgc\!ͶI5r/ɹeS! =7y5utuUDeEG^ҡX)T"Fm7NK36y&.;#d94@~% nR# h6׳>:v?7HT4)ye) F+r։0Äkkv h:0',  2>O!m3C݊͠];/L7OWjV7}#W4DT?ʞv,fnPc3`P&$ob?;c5{{GA-eQ-1j5A xbm@A\]呹 B6.>*,9oᲟvϙxܕ BK}Jϧ S q,bZ'hd]LcCY# &wpC?Y6kTOE>1qR#Ắߺgِ/pCѐR Q" u) xp5eш_ul ʙ;=S(]P;iF,CWmf\@lNZ|?ms 3l9%ޏTVbW g6^mq$h4iN pbț4>G>Gh[l{xàCvD&4= $"X@[yǺ9Mm4Y?xmJYZ%'S!/r~?1Fj^~gP#\?C<(.y3MF:$$vQeqwIP5mS񐚴ߔL@,:ĽdZ)q}d-EDWU.rAo Ͻ"Hx@F4{ Kٴ'Y[cyM҇7R7/o ; X;Aq#Wު36DB+BvSCaOܼh ܿ}[b`d']kbX(A/W{7B6_HIIܱkZxLY*iZ951-g&O3DiI7hy ;gx"r;;s(8L}\P̑QJ1='zB4gXsE ȣrM |m]iNRkۨ1Cyw}"Rף:(x jomybqrbKChtWYr%+F;[KZa\_k~9Ps4{;!tDN9*ܐKl>Ѕb 5=uv4utBX̬Gh[ש8j!rӝg~{& )qc|A$DﻎZ!V5UҎ$l]qh~V` 2gOR˓_OedsD'^4ٗEՆc\A<]w%mIigB6S"l)J^L .D];2D%' wE$ӷq(᳎CNqu8NJ%d` ]%166mZw}râșv0VZŝ k{&ߴmzaĉm t{cVvmuaL+hqqI= `B![$ Jx . ^^dSٗQ; ;,ţa4cZirțocChsAm@x+Cp# \`e !C7P9PJ̯H Y&>dR(r_-ϾFԸ1Mu睥Lh -kIWCam D+'xɈ$>I ;NB ' bΆ<1Un(gCַM;ƔfxXpX?'_~zF(Uolק UtWe)*f2L&%Zo8 Mm]4'/y(SJv؟hZhVF0aX4ھGNg vZB I#z"p<C02K9N=.ɇ$Z\ooo eI X'BJb.u Y[vrn*4P xN6Oie}pY3`MnN8rl\bye_%#a#ȇ-7|&xNw-yĐ˴KVVHC91 BT‰\0,BҼ<//u.YI Y`ixXnECu.e}ZŢgq MA@ ++tK:X@*=3Һvo #5z'B)m~ePIw4!O>)] B  \6# ̵+i@z?,tPgI%$1]b>5g&9dѲ Z@Aq&l_Z~Oc`*0f6 )'|9i d@"HHXJ]cú%`TG{\Jz)f, e48A=뜱4ρc1Hث5e$!ٷE|;C4P'xWizTӑf)pnjUgU]$BN>m`!"])=nVeg?. ~7կ {DvxSW, " J Fޜ!ϧ:o;?!$f2?)Nv0|mG/$*Y y#T?,PPe\ W dWZ@1BznuU ֓`ȉ%nZ)y mm]| 6[[W> aQ9R0j( jb/|~GΑX3 ʶB X|M :E6W-Fk-8$*RMpaw}kw?81˄S{HlcJ]l~c-InP_mAu m_ZĻ&IvEUynvřbQI&jQu?8V(.HN6 ː3NqH3Q[| àB>š(AXv@DžgT4p;> ՉHEіjøZINo&j}TlW$`7iڧÉղ 2-TVsnN /D@@?sq=:VPqob"JǽrO%]&z5KLFyUL|*Q GQghi=W+XTrfI_WhSQ5ֲj h0Hd(OUyc!x>spNO$ Y8gkXD+i!,ظd'?Y(ٷwEA* 7{ 0$M/2~\+?itV d'ċ;S<5.k&,{8.߲q"P$eNйSk+,K[X9ۓ'(#xPEӨb+Ik'a1PDXcA]<=Vt KNi՜ծ|?SdG%-P)o(X~3*Czz{^\}*千첈ղ>nM$b0 z}ɒ -Em)u@m䨹,Ku;д %""1Tq#_/Eym' Rm;!KITt;%":H<'8"Wkz~K<_~S,#t8[OE$t%w.B1.]5YjVA~ _R C (I2$q%<$q̾VDϷ:Kȼ_<3hO"nۡf.]ԅa"\4D?S+(hmg(+Mn_Lخ.8 mC?;D;hgBI=GGay I0rq+x301aeӑIx1C:VP@DFM>?ԓ$|NBd'P eymXoT)JNp[,׺((oP=Q)f7mT0Rp<T yw <@Paj3C&ZDש2\C34a3d-WhfS}M^#@̑ Iipfa5Ds!֔`=w=ry]DsY}@%zOǢ]va"=&Ep.^6ff;eg[µw$s̻+_dI; ȓYd]bѩ`4ގF꟎s >\mU*AIcOwx ?FK/!$ K;A,] -` bTs~q3&'ɏg,ֲ*Wb3^5sai(۫Ǡ=BDbR/ |74}p-q[l(5HD9_to] LL(#(RzOhBYGpcvYlD@VL=l둞xL\oM5֠,AxpXŚ-=(~0CSÔU`|[ro~-_ꀒ}j[`L|'Ûxo$jhUm) =N3ݠ@7Yp|kHqoኰlua+y|=2XgP Zm*F׎}@u4ӟc`@^| ge|32;) H׵t(f#wڎYF^gj\QK{,{<?y<@1k;wJT^&N룠jqQɅ{PȄEG%)i}y阻O_+*i189Uu ׍Fͪ\Yo Ϸ_6S#h>Nh$mɼƸ6N\L/1nTowLq(vz X܇Û&Q̱2.>'&DE̍3v'^|GVmAe8lьp^ӑ2Qn%F?֠)%;҄"=Oh!3| QpgV?n:5'.yO+N`Ҳ={@0.;hm=}wMWbp)q 1H\v1tf`ٜ r 7z'$G=f[_셷EG?Ubx~҆K_]bO`rbsGh /18cS|`w"p,̴N| eu` [p8.m0(vy ju'z䅂r1r[+aPez(#?4 i~[zWS&t BFe)Wi%uxrJsR`2q'&~3Gmdz6 DZ}} (trdJY0u(WN| 4m:'(]睩ɊFy@oӠdP|"J\ 5 ,ޘj.m\yu4r^/TƆ:wun0ƿ]W;428Y&pJg Ni C3?Ơ62aK??BP u&i݌>ZsP%cn%#] ]4w\Q#RC gͯY]G[2D?H<~BFp*Ymh=0%5yS\;?w/H") ~x DB{!@9zxmd/Px!#<{Րwnxrթ0q %wrBWF랊/$96[yDa"/].}O{ܸ u*tawe 0"/v1k\ZG0_]gHl? cNa +ه"$n7r@gO#ZtR8D5ވ0|tzkʿIgi@=ɕ`hows [1gGM-I6&~2`,5(ąG iEխ _Vaߎ?6Tz:W 9bO[SY ԏr֨.YA$!2"3M:p-a'T/Tb&:Q3UBzX\R % ƹ#8>=ɞ3roOLJ+|yoBvN|#/+rK@x=6Uc {B1JCʗĭC!rSzuyjm':èӵOwN G a (,>4f^7#3-IT<z.`p0_6-]Vb`/]A \6YgŻ]yۻdOy[9z:f[ʥ/IG" ɗhK|čۓZB(«!r iO͗ڌb=5m8d)(eRMH`R?EZmv`pf8bzxxIKFSP. gQ˓44&r_ZR KXhI@)J9WE̡t>ii6ד!,w =k#_9m/J~fxCa6̻S3 BM ǦQ3tuxuy/bގi(X5=Ǯ&"H6 <Ѷjf4m 0^yNS~2=)%1q^qɤnB^x\ufW:q#ejsn #tyy4…Àcb?Ԏ0fT\ZSHUA{ջQYKZ)lJ wԆ ͞\elEppX@!N* #H16 jPj9/ֻW, RŔŨ6SDwci$@\};̺rJшe.l%u($*oE]jof]owwNSr>C1 B̖ d"#ޒ Nɦ )'3nL$" X<;NoXhRgH;+*~cJD/ T_TM&s{.rs}Nxe&Dn]w > /7]F"GzEDFMR9m\SůC*2ȋ@%@&y)@9p 91H\_BÜǗ(h ѹ+ֆꓱ)=m\H\QϻngGg2i7iW5 9]TpwVҤ@>Yc #㭰'ćo s=K}cHyF=No-H^06M~-6S +R^hks lsylzG=c&HS&mo͢rˣ1K_ 3]E1Ny⭇l og $6xJbTQ~pKVRt}#Wu$i1[.nf>iB 6a`Cy0nYjܰX/7q_yݦ9'd}V3r"H Uă=m{CK%nw/)Ҕs#)[1ilwn^S|YA7w'>FNB"!|6;2Sž"zJNJDUj9˟73>`z7fvier'rlCېmxD [׫ϗLGc7Tm+TQcp0ySKs'HJ,<&효 m緹ۏǀI#Iqb"{)ɛnC~n))r ja 2{KȕN8ԔzICFMA]E!$(T=^~!ĥ=J9M 5T5ՙhrѝ@`cس.T21$u(cgb*_7|v_êa sy25SF oj`11%HٜoT`5`{ޖe*1̅9k%9 4\J_"m5`B'[/b(>Ј5*@)8gNl(u+pl4WՉ9P: Wh{)U*m[=R>Cq>^O9oŸz;Go/p?[s;Rgn/"kEaYQ0˒;Qb%s4o+@^cH!^ʯY'P^ש;}-134 #]-]^~ Ńm=~JBCkcaDt[jw596m.D7Į8/󝦧'%*BIo?n* rx6<&f3n[mءp>Dhy qWs³UjB-1M; F"I VGyGHy&jPlҺn+Eoʩ ="IoJߞ7GggO95}1JLecU@ ;iҨ$!x+hicfQ|]D3ByvyitmHvBg6u[jUoJh$ 1.Iǐ>+1^Lb +2)tT'q皷.!' O\E12 F)t8v ~GH[x¯-.r>PIwZ޶@ΕmQ4a#J trR] 6^)4LF^>umCz S2q!ƚ%MC\ޠB }jhea!͋~rpm( G}VoO=Z7$RS1}gYJ|]3.ML?\̀ܩuڛćh蠄jN'n b{/3ŵ^*˾]4ߩ!iNLȦ$@lUN\L0ŽvFlJ`hGW*NV<2+~е7V @RƆ*oCSe*37;Ӻ6 eP@^,-^E@f,eyWa狊1= #KK "n >b]Qc]laO*I?e0V=V"xK!MKH*JnOVSqYVL2QK> ! 0;l$E{ 27tr)"3T٭l5M;kZ]\ pV{ Ӧo  R| ǀlSr>`58A+_ z"^uH7 \[&bP 4{' p5yi2$#cʕ]Fϡ5:7($bF8nX[0yK_E@U~]72͉|θbLV\Ktcx#LM1-z/ɍίethxŏV\,*]<y0Dhcn99<8ؠ284Ve27ŵu3Tt΃WLč͉p@Ҫ' [i}Rk%v6nAT |c?(墨b UQx#3×8U J!w?؝7cL#܂u1rZh:11i(s&+]VQ@߁ e7(> ú0I$@E8(GA$xb$R:#{aWO1G#vøUMEkU79kA_8)H3!QA}^ћ7\tIWZ:lOv$eM`n<1dBrgm*ۄM]A8( LD9j)!]qh?P"Vm55S@ dTU\׏vI I(d\X! /U K |ZrPsf.0~/BJ̷EY¤L-Eaunѥhh1Ϊ N4.7Ɲ{ R3{vzm㐳-ؑH%SgA8tɠë"K;Ǵ5J觿=a2$N ~H(FOkyڊߦE}U={8ݓPw5a @Yk[%[m)B8y>vx Zؑ!݌)t4z>Q+|j6]42:Zယ1\,\ocRǜ惹!> OUh&Zn.UA9ݟI?q6#ЯSzCή-j8k:J (,OYr`>Qɏ"LEP6=+[^X| KPSP6JGG%sGlD=i'B̼d Lwg,Lg{F)sntIYsN>No}*(i"Sgmaz|ZMHs v3Y8v=3+D ~e( tl*}R(L/V;N_إ RK`ic z~9g*54(1p9*T*p Edg L چʬpH_h!eU?Qy\Z:DBHZ\tX;AyI&GUX]||LFˋ@_l? j_'<.L`byd멸ڵTqkGT-nilR#,$K(a%Q.@}7'w9e]`s<w# e@VerKMջ kLxhHƲƸtאU[{qLb#{-<:uf б 1jg 7?V&miZY(ע(EdQ 2ű…rdk}̯zڗh{JaU9j}F{h12 [~]xi5[K"@Q۹ﺭ-.xEף[L3E R$t+" oӉus;xMՑV0|Lj<fo}nOd)Q# d]uxi9[XZGg %*q*NMK*aldg:'|e]Qoi2Mn,~kbL|Oz0$ϻzfKOu& /C ?2 #9ՠk3qts2l(`ḢyG Sɬ(56i֔A&.hEJY"k1kԨGR;<,I `A6YR l/m:j\`@|7 ,zRpPئyh!J:aI Ue(!ͷ,bwcC}w¹LbTuL&H<iеxQo]YdA[Kpg-V0")x7428M8i~F;^u~JiPj&z¡0}oc[9]RNu9OӺ걕} Zt멈Ӳ{wy ۇғ[<&+ײ_M2zx#ajavPU1JحQz|Cf( lac9:EXBjfnGaD$'LSz: ?8_oAZ-?69$ҘBVp7?EFY˵Z#[nhqak5DY4-g`C턛0mZm:7qcV($X`Li(bRd{qy8mi>3ZY9=x?+gg=gămahm yu@ [mhFCdH1%ru?bp@…[l{7(qr=4 olOg—=h(0@XO [Ǔ(%YPKޗ$GEM3!slt?ƣ 1Ι/3SvG|WQ:agEgw{o)$bahvt^$7fm :h5ā4f檩WP0""A}PF.7I$U;8jz]!ު8,$MmHm=#):qŎ<>[uʮP8S}ϭ7ǎoE1O[OE/' t!3 cp̈́i eG0|$C{Pyt "+I0>0I3z!s9̚MAf-Du楆X]pͫ  Ob[iS8QY&iIz%A93oY+6zգh.Y3%1~HC(!˰ 1 67.Fo7E'p8D/1_DzMOY} oj&xc %~@DAw3&Y> 8Z2=!P3q{t& 8 )Xzgu;Yi\Ozo^ͷ 8ȄEȞz teWLF/c0R-լ<KznDA=}ٹޱ0Ֆ|b= ט8TgʪNa(_d-k9Lpev8] u^؛/R^u*Cfel^T9-!2Ap DxۊfY[cb!F*W6K-)S%aXM ŭP!؄82gb1IM_&m툟*|:[<-vٚZu2ҥQi35aPϟAZ_(Q"YqfyQHp ~>[M-ZxM\I;ٵB_VJ`e2p"WsmFZ";cW,ky2 Q`Su!D7Kq?(8']tw#G?=m'WJ9,ڊ5mNdnAjMZi*|ZxMzNRI^C/@aJ8~87׽ H\T"%L4{?b~ :[Hj^)!s !弱KYWLڕu')I^ 27JmF)$rs:@$@\[ߪNm:ʳT{\;êP+AcзiJbDkGO~wyK9oB^ь*Yq P]1u-)crd2J2x]l{F8!P@T2ϑqW!O˧1\:YtgH i#1w \zn eAAs.?G/S6d+4?$vkb`dF ܑ0S>XdpD 9G}yD  c7|MQ\t"cSX"l*5|!/Vi[uAJH-^D±֡Uew^$Q8C2b}P1qfȱX)h3#9{m>ɂEF5w^6ń,dr=MZwNqLV8+ʒ\Z$칫@F3.;G>(F2:ZHyױ Va6$~Q>#JQ 3"ʞ 5L z\^/ /_/DO/AEnm.)/VXON%ㆇȩ%~:6̞+6Ax}x3J(>1VH(~%\F c A{m67 WVuf zn?+;c^L@%9UO[čK<f$߄+o &;.8gp֍0W^N&Ϸ9Kg%wYU{wP@,'N_s1G!A4,H vv7?o:@ ?QKCTYlx^Uvkzߠf+L ǟۙ^Yd)HHUZh%A~![7jEA5+j+PmH0˝:{1Xp#\>O!V 7Th+.ߘvʧ(h[ppmG>:25c'lrp.#zg(IbS0>Hp1 ǣNVUn?e`zǃ 'V͵wyU2OQ&oxN'Aj;74#TYTl̈ U8Z9#V-1/?Md(v|$%]1#F/O@W ]^ Ca;PBrd}~t[Gv'y-:'j)֎L*>iG -{_"F(&9/4,g57~j;MȪ"6'? t]XL'y nτ4`fyuKݣaZH7a4r?Y0_7!!~<c8$ D ں7 lVol"C*)Ͼy@E]]cu#98fmN\욢+Dծ+,Ozub R {1^W[O|ܒl r`bMrW/l|@ЎmVYi#ʹ[ rVٗ/\njCZ DJ'IάLPf>oq~Q:y"ܝm\Ul%tލ IiH(}Q|vzӕ8"T`X3 oq97Zd59k?ͱ4 p,DƊp8H?hM( !n _:`Y rN_ceuy)u1h{nac&\B v?Lq.&(LIPEH1h(2(e]$ zJ.}M]:KNe0C(. y:$3-+6ܶ(FOZ.K{ q eF7WoxUUD\bxM!IvyBiNRçۑ‹gb}%l)̆n H7ƘYԶҽ#3BBGkhJ\A 1Նi2!/&m+{7x_s&t* 5GMF"SM.uoqd9Sjmb.@9Mr\_‹w#NWxKWvD0@vC/,ҖeGjֺgɧen\Aj 5E`f0.HY6Y8bbX9ՌYh;l *&m0-ަrRMOحNj갹{ xC-ri^ FZTWom,~Jjb3Ubtܨ@RLAG—kͬ)U̹L4tIHiR%ϋ6ؗgtݡk_5172yXgXtF6a;}@WoQ ,B=tRf=<N;Ռ.s'[>%{!%d0b`[A{hωy%uZ}={`\N 'V[w;$m0e]N3d+K%琷yt_Ӝ qϯh$6XKYy^g_u^7DRBeOKǚ"4ɒ&fQmL y`fYX{'#"G ͽQ8 ֋mT9f@Wح1z%p}wuUd0vܭP̼b-(h Ӳ;)5[Smȸ n+ >7v9O3.Nf^33"z-91>72r|l'67}lفb19M]PrzIS٭ u8}<֋@dqHZЊ@kFf^WBF;"ºCcHV*g7Qvڝy%%wX(5x;\sXiܣ:soV.9SDa.u"姊A]D,!cVaK7*U5)CeeI&_=X2]"F˜2X6+HW\e?UU +p6jpCլj7$evMmSW璷iL\F9K/@#;̖ ܦ B ;8è&Lo*HuJN\'^U(LOs6GoWhnخ7hYK21cܻ,ߐЊeu6G% 3y|eyn y-Q6}jd^c. `jm Sh,"_h ?ί2dOQA&h!=tHv)MڸNaH7Vk`(r^Q~O!X!d/j%Ir8"QPML=Mi(N.N):yh1'e:mol)P)eZza*#w EYGL_~ ]s6mfl߼42/\ ?ThnrKʳMTᓗ֍,މ~ +-aނRGm避1Sy+:iCn^ Q RYRDBÙV^rs gFpcI/&VvaGn hX*{Rn pN`\2G.HX]m/'X9Oy^rv.{ {ݯ_̭r@\DܹX#l{ayi4I '鄏AN <.g I?Zui$;zqqz|!ЫQ؟ck(5s: CN.{˪Hv3&lRaz  (ٻ BM~n$5v;)lwﴓD@l"q7rSu\]xؒ *FLn),~ol )U28cGq`wXXo[^ngGTu\/=e GxhR_BKfJoqJ-}<8ѳ#4c=;,!tEQѮE• M#5NIV-*w^w{/ںI0˗C9AXv5W6M£]Iz/a&(ז,ZhhDΫY)AxZ9:AY!i קM}m`P\)K<3.oEXrp"%]0q FjF΁龖! G(,Ljt,@@ 9&2DpYp*H[J' ؂y'?k -YY紋6Q޽ݚgte,i̶U!#,EM䧓5j_YpkK9*<xz*_V>X*مɓF],(pn}'cQHNд|@E<3t2!@ktإPmb*,x8EDr|Jjz"Tרez8٢ʡCNہIj8 >+ 6q{IE(A yTog_" \ E }($?V"Z!r C֠8(=`%.Ab('jȁK/C;U%t;˒ d4: S+^2$dXeI0z?U&6G\bc\hݮ ՌO\2܈٫ITAhvkj pFk _Y1LݳG8:Vw,Sd'.RGpO\]~:}F߳H5 v=9;\cC/Ո% rG0WpʄGR}DY@b2pe9IỈ_T6ݱ`hoĎT 6FsVB-SrB>a!-fjfG zsGӝp r;r^xƥ$q}pY/jQYR}v_rpcu:~pX~r~J~\by'%#A(2$ Yԣ|} :1OCܶr7ScΆg_VMB1Kix>:Q)~b:08v-!#8ْF8nУ'_T%!G@VpsᬑEaD].mD~l:w-;gN#kbP|ZJ;\^@%쵻x1ɐf$l;szn sO94m%Ŵ (zv}ShW`J&%l2t Wt6 dIJ)XzbGIVѬA:׻o+!e{Xz^=oE|ot)cf#;D!-suXϏw̭CQ|$NO n(tKє:Bw?*pE$&`k-U3"B{S1S-i/yu]Ȣ!MR  :2ӜfwFyg^8EJsN ]8|N€ka4}IC0=g)숏՗&U&%Pf6 "l!ÈKcJӪmlR=y- D9-GA DNHzG"nQډd'`sEW%Y/?oo}ɥz<7a6pOP2bf>D|Qb>bWay瓿-%H!",/F 2SD--GB:iPg_m@`iCҸ%LE@J,^~5˭N'Hbu"Oc8Z^~q+.պ;8 4n)mxdz4ťdDۄ?CˬV**A#!K&HPvRK ͇Q8pHƓ2AU gr-Lu!jAJpJS#Yt ^+rܩM﹣-hwP#p3DYƻh#XA]΅Z'CYp;jwQ)$K** 1xUj!cEβ d\4fVHFJ+ i~Dub-WleܔފT'SНCq-3bțϵg0ЌpVaCX[ C:VEt?@ﳌAY0<Uy WIA=!Hɍ{b D'V),.dm]kfI|$ui},s/̰!bzBo+8ǢZ~˗w=l_殠Z>7mmXqXcf6n{֏;qnS*D\t $$7m-':׏uS AFKux Yg E=97oօfYi'9-3._49*깐TUR:"nm'VJn}ёNmN1faRzB2DOt 0Ob5)\X QQF,\mt;1c79ǂN ޘ/3WiO{$!TRY}b$/g"Ws(?(S'Ay}>jX b~"|paߔ& X!O&U,J-6Ȭ|d]U `ݿXB~-/a,IF'rM5}bXw-Zr\:>sKlʔB!F>Lm MsbYԲԂ 'AL`oh櫋rҖ`\w: p->׏ S6o0wëre\h5b7S3NJ{.et9D z[H_21񜋤Pj\'&GIgGJ''y;ٷ)'P{cjĨd#apĝHPo 71Ȉ#$(gDxz*5z2sn^-#[w Oau~f|1ٱGy@۫ LY^|2@Řf|Jp,UZBHܣ>A2ݙ-)htN-q}}l%Ў\ !ȫ:3'IY*b!ٸ)$ jl J ZQX' }XH00`c\#`W罒z_DՅHῌݠ?M.o `V$% "sڟg3B/J{ژ eҔv(AA ~cRlw?' @4uU+W4\bg4^{HpaSOYAT@z::9YL)(*;s ]e)PJU*NBl7oOE>VUQ]ûD~ѳGC.ܓ2]Ub#Ȁ]l`3̘eYȌt r zʦΰQo7`/\-\TbGR Z5נ;n6қ3R="xpϫF'@2F[fSVpUT Y9a.|nx!+,U tx @#:he?#u+ w˔D$-35O#g'*y5T%WNI'7rB <9XuY\PHi+BUigtE=aghDb0^/jSic([D +`<\Hշ&-a[mVl:ѻ;Jq9d1@rUʽq;]+ަTT=P; 6Xe dU={>S?͘$ I퍉豿ɕgan7C$ZlU6Ls_4 he>օwMW\GEzC$E@g@|JW>P|.?ܷ"BzdνWYGn ͝  t+c~yH&v_ލ$u$*Gz17+,!2 W7ҐDM-眅UyBri*6#C8(m L7GhuHϜGn{KwĊL];j 6Te>40ϰJɌFЏ>I!QgHn7i E@H f}{]܇iM1FNbUtX i~V51:SdpYCGqv!P$7r@D{vJAcʋYp`Yu[E %}AٝVJȋBPb.–K4q`U;YLTT"5 K}1 -ےuяL UӁN]WEy)P[)8F y=.h8dܚJF2¢T%z,0'\S* O+'v}: J/mX yV Xڀ8LOYqZ#/zOK6-{(Bv{`(U.흍32Wċ EU Z(__jL 0H#FAIT>}TMr 1=Dޚ˚fM(=>{Ppgm:Lti ]>?#EsţVNN>,: CvlGf0$6Q@6 zeEjia\H"0K#08Tt*/;x.:NߤS(A9PV@] ,cI]d]:G"|KXz!kbx\mF?$O=܉@ vۘ+ y7XIH Po/*Q%lQ`7Z1hA~/ت256% ,ER')1eg_1ruxa5(~b ҸtyL(Sk_G.+/{&$Ӑ.sxv<(d! e,-۠ J$'玿 /.q摀!c[KTl?i=L]Itlnv (•oaPSB:;In.\[ 3~v ũ d߅ß.*SAHXm>ۯA)m@!H&'txgZ't^y$_P\)G )%W=,>YzT^z@Ʋj "OUC7pj<č?ٞƾNH tp?)?ܸ6,J(cfAлu㸁b :rrrQdJNeFMZ;+١r*6(N-^8;%~Sˍ ["{Aӎ%P{گdgci: hzIsRm\8P@.X9=ؽNj= y[T4~^8x8ZE7Z9OV8A >,A?[ 25fX lbـJQRGo1*~*բ̳U2v G11kLʼ b)MdGE7(7asp3*}x!ު]V,3T ^S\*`J_cL{ۤ`UUhpHB88SKV5i$=`ט:RcE vS2RR/tWVҚxðڦ/"8|[p(ekc8#ѭW~l^4hk Ig"ݏ`Liъu"cV .)48@<8GS"i%Agu ^xV.P89E8z&,"d(Cɸ)Kp܍ŧb=6_\\Ch% M7@8ٙ =}Għd6dHCqdH} Gjn#d #+ Nh5\B;$d)VܐC9QQDhtÙ #g}PUA%[Ed{7? B‹QOo?zg֒,޾eyVoe F(>vMx5^!fKtM'cq9T92r/o7XΑU ÍKԗ0UKxyF%xC@9jŵQB_pЗmфr"/j922POT<@}%1fz텟8 {)w'F)i꒡`ϑ/ΣP7 1 )!Qv}?7>vtPNPGMΪp<|&$+ş.E|g20/풇n#$e0[[]`m51eQ{i;c˅'p)M|<~ T(67%ZЊ3`á+>&KMwg!kՕiUiT]eɒ-@rS~َO䗚tGWh:|e3!Ewap|w%UPTWz+<ُ玷 yԦL*nis]1n5_vbGJ] H{m[ŦLG0EY,Qh|@m kOC4\?mdw(b_\NRl)ey?N٢%7zU{K2?!fr4 @{ts&;z 9n:J$]߄C:-u5 9x蕔+ [8pY)ZyhvvCJ3+`v$mT֋"k~q;KxnѰhp#-G$V a8GXuOȇ?<4+NbuD<ۯAd N?;BMd{. 6YNvvhw M*ȡrN[lˮߘ I_ ow7Lk= DE[YOpn$=@ c=XYCt._#98Sß3]9Ƀ~ 4G{18Oj$ UyL/Jo*=΋ Yl@ M5G ުaB_LqNEUbB)WD$ڮXV%eR_r[u 7dIORPuE'ގs=r2LFcz 5u}n ,J<ϰ$m>b&1h_7{{Ei<,2ؕhqQr1ˑ^nΛ'k-JeXNIfC_-J^ՠBS'moqE̍ /t7^3Yc=Lsh 8W3%h6Y $ʼnC4pUӂUʻ IJ ZexlO4$33L{ꄂҀ|C:ByL[` & f9P^Z{P>0A~,h*!fڷ/i>]hű,F/]Rm[m65((,/ʎ@gw ,xO4E1Sui1K6?Oide[l6]+JVޝV,{`6peM_G{]-n0x#srsƝF{ʒ#,xc|S7(&b HUmtméxDBo;yHj;wyHa9)xdē˥{5;;ދԥ:1ߘNrl箎`e;IBg^HHb- :9 u {=FϮ-la`]Qhח#q,'p}j8\(TӝW5H$#ӼMPCo)cgc2X'`ta1ڜ |45ԉg{ O0]D>ͧQ#p;@_0eu+5 B﷙ tҳωe\R8q^zR05q X}Eόvl@$Q,H`/=;h~ƠxX{ ?HȀZQOy[둉\ (e$*݂.bji>;<9,SΏL;=] <G8&5BwVNʋk2ւGﲐKA"?Gz`G}`%c9TZ^#ޟM)F'돸jA=9u-ހ j:= x< eP!^Zz?Um삪v3 Wtu_@P :9Qjop{֥)g6F7.tF&5  i Kv͕D-hx(>X=D 1@~ Yg⊽묱6^[ti来}9YC#KD$iR%Xf;wK%HҪ1:EdAI [X8Q+f<:}'Jnٌ-PM^XRR[}^jzBA冮%;/jƾ ٲV;ݽKxPdg޻*Itcfƿɣ `޲W;5MDFK9W)ǫH-@WuvBy+O޼{"n8#z0B$ٙ»7& Xaߠՙ&::oԓ;~>ˤN<"70N|8Q^QhIL4h^@R˄lKj1N5>.8(3qfI-9>}78{9OZ;7~ m˹k4K~ވsS 4m\7gx*N5QUȂE/=CZ B2"G{G JCd*3= 6(l*Q~0Wz̆6tA ZY5CkHlfŲZdy+@",l,l[2r L{)^xk$y%G~M?VgfN̿Ug z&mH%WcqTûH+yt@Ad5eAF8HOؖ='`Qvw(ànYKeѺq,O>j5zNvm~'3T8uXz]3'=T#'tbՀV ,vy 5x9,tP9ѶZYh7 MH£^(9¥0gf.rĹA.><}XλiB't9BiSG{*ȁfq~<WeW=߇nTB|I$GRU5HWi>awr<&bD/!@*\7@Ym8[\zz?vjIVF;5kVi](y߮MN~Y5 =wrSgЂ|_ZdHxl$O*5^lƘ<o"->bEO兞89\\(Lge+xhI6VS@ߝKkd7ٿ@ Uns ؤU$7ɝiŴ1l(+ԉ s aAySV!'_N<>5ZW=(.K,OTX"Xkˬ#ck iZU 4ѝK =Z_C͏θW)~{,mlC] T@h|1-ɘyi츉>ڧn: |!KL(13H%vN\=ȵD_?&nTKk@^̛3BH[i}mO19fsww o ϼ{1l!S]!zʆae ]#s  [ej>WwScpӗ=*kNX`Č"FE_T>>t . {Y 䛍K19-%3.&,K ܿ> ҁyM3BEBszPj @'H-y`~Sq9%7v2ǐTvdŴYIJXݫĦ9W]lj;8]Խ Ϭj2|5z$aDJ^2g P;2~ G#:-YU?(-T$-=$Cv676"~Tb ɶb Zٖ~j=TsNAQM֨#ҵ)ߔ`f_2b.TZQP]qkZ5ٹp^yLTM_X݋C@~vST־XC^rzCgu-tr|~ɚWJ@+UFO)gtOx*Wi0vl=G1X ~RՁd1e⏪Qr\ѥyr}-x?+ tAEqV27B !6TzU֓7dcC !<-}pe1x_1Ty4YH%Pfs"'f%9y%q.e0Lj2z Hd>q  A |9)t!MUӜȩԈ5Nw߉Evp`һ5Zךzk)^ W۷3;|Xy-{;н;_t}m6z'"ry+РV-6";0O95S4`-wgFȟ +Tw DU|a U!j`D +3,$&oovN~5O Iy$&67Xz%ea}\(ME?:ҟB@ ]~׾e$X,v#xq8;4+=' d46;rELFhr5ϯrdҖ[H^Z.?bkjnc֊2,m4z5Ӥ~+w tpFyEPE\:u?e._FeL}>2~9jVPӛ&ir`*R?։?lTW60&Oq 7ojcQB^DTġП9MB+A W*0x>lE8"D9ͦ#"x ?y^vԫesS⠁*e_e˒ yɇsjԋT;@յtxΑuŵQzG<9륯pbLk"$|Z Y sFT ]1RC ɦ/t%V/!rh1Z0@5K0bb=W6( >3EpdXj=c,@0C1sg16K@Ć]c=R4@>%N$ '&8<=ߗȡ(>-g,bgReM頻 TƩ`'>X?ΚNЙxÿ!= μyW.kuZ*H7v% ROs\~Wܫe\G+ RY)hܸE>$[ޝNCMz>1 0"zX#CJPJZa_oYSQ@w ( q1neY*jNUb:0Mȩ mK&p֫LH~=9[eԬsYHqsM=-wf!N0fa* SmŎ玒+r|h9r22FpJtFsRu^FJig7pki܁! vz {r7Rm&s爊)oY+x5Ađ0~+!yÊ@Eh{1 #p%4":E|zY w!a\ghtWnS~{"$;%wYQa{*925yRCy:RVg|Wf Dtk񟯹FdkxpciY%qꖛD:P©&jbCDqׄ|\?zQsZ 6]=UD#֎+o>Yo]^}ٟol3g*̍9 LWkNE"B3<48W.Uz=̓ȁVe:~ sۗۺ("1L )ͭVK!;f d4>M0rQgEpI>z>oOM>8*QIZ+vp A°eZT_Di_҆^J \9hq.Y]},GLQGCjzBg3i _L]ppzKNP%-[K},W3E%RHkk6eHHqiq෵O"ӳT^W(ar22IV6űIȽT>ssGaJdʭOF:s.[8nu ,uM,rA`°ݶn[["<{7S`=&PHBR.EIѽFN5XÍЎx!pR5w#ɉQ!yu {ۤi4zc! Iu\ $8t";/ Ow (N4}!E0)L_[(\b ^CR-+fK/,{[a#Ʃ;ʹU(rb**WM(Z~)%W II( >IayX_U֪]E޶'}@aE1zSr3,Ef2A9@ӰM eiuW[SX[J]\ЖdEZ ;cb{Jz҇A&Ls&gX([众J |T^ m(!u))ToGBo0/B[97L6i哦2hE>HpY6>$ rr=Ji6s|)4U) =:+;tԾl&>}4fBĤ"j`QVbcn+F=Ӈq)f}%?)&ǠtM-y௰5_Nlӓrq>]eQG7GDd\lhNWki=S;7/&?s$ 5eyO>UC&#NZ%5h& -2C_e;gJݞ N)Zwu㜌)$gFXa?`_ӧGPxT%3T ~SohLP`rF:LԛδȤAfiƩu u!!F9$Ը!ww[XO(+ВݎnyMFP~QS*jkUF3>{0 R$g=1@w%3cLPr=%yDv݇IZy091 Ƃ|}]ޮ3qmp ʯN8 &3ڈS(׺+m?ne閫v4nv${R9^Zݱ K0MY |J2GíNZ%E=:l,dH:sJzX6<&A@_ܽ6؍#Rq4hF=x1,xdk9D=^xPؘ38䑷GnncٮO+߽O^ŒcE8ؙ.d8M7m11y4- \Z.Ia7lexv\q(|fg]툃oui>}=9A{JI֏5FPi.lP&1 ɒRBrtPsA'l {Q}CK(@%qz޹X*aKl兔 f=3O~*8L?/?HiJe :` y"l8I<>nro1AWJ7ꏂo 1;uce7^ג(MP5\l1v IAi*V 2`;3_e .(.av X@+Jkz.%d>SX43nSxuÿq=!JC I1l[{*E ;gR'}5FaX3Z-~kǟ6ޑ{ 1/9[1ߕTXNOw3B<W&6vw-r#;WW!Sԫ=9y~٢B.J I-DPoZ'|,3a1y= 3mqzW#" {j1Rd@CF嗑 6FV{Ҏx" >~ Iq]Ɍ<ț/u\ HޠC%49!|ub. +A)Q‚G~y'P'ιl4yB} `})cÔpCR '` Pis뢼UU$POMqQ5dmY 6Z#Ȳ%4k8ʵ.)! &5s޵ Iʺͻ_(>;'( U0Osӛϑz1eV(5dcDgC+]H' \XyNkSB#!oή^Q>^8 F_. f́0k ݂XF 8Dy 셍Fq&.2Y~t=iN ՄMXl͜uܜ1TFBuP,\MX{Sif0Q5H \bDѾ97#'o`31g)ݬy~U΄k(Q ~AM5:Ⱥ=]|imu$ DTki9Zݐi⒝G *ƝcSS$ c@OECh8WB{ 9N>#$}%5ZsAOrX98"Ihyz50Ǟ&e7=6EHd{ϋO;=\: 鴮<sռH3t,5 6몖!:/R, kҀB5oeo,t`A/;Qخ0G2Kg-Jx4„  LL|A:p3 !)ýBL"&&_ݬh `U@4T޳پ%Pۿ(0 aBoJslsRnlIb5!pd六4z^w=T3yGx(HQ2oXEM"hcjPԎm[a.+X”m\k@'P]!}wk^wM 3Kj=aP(;.b+f{jk 6bdNEzXsGA)/q9AKj2:kp PH@J3BͥƗW ~D٪LzHYE@3U F\گ<)bJOk 2億;'Zj)ѐng\1m1@3DʫKI#~Ѓ[5YVN"&_megM*3)~,p7Io Cn{Lh}c8ݓ4MCsCN_g8Ѯ&pyF s EC/h!YwR gwJ3P mXPcCU+ jnO2|eM>{%Wŋgm2\Nsb0EW.;+eǽ Ŵ q>xCci/5'KR[PJM@F ˔#oZq-' #Eb9C$:˜a7&n<؝IrȻV>I_rԸNs{ {q_*غM_S _RUXjK9F&}>F٪-iLUjlq0fχ?0_}$WӸOlHMh1iso÷ol\5i=9 Fz=ׅ6?oo]ysc(3nlYt]?Ha-1E w##MEa7j$C8P +K*\]"Lm[A]:]( Fu<CcFOY.'ݻkl̔s,3}&r$e- >v:ԘvMXߜWgOwP-6캰"){ S=<58!T}/,.<(&/7њzFd[lBT묕/0pՔˎ8S -%φglޣ>nUq2sOJlqnNtn~р7aOžq2 %@(zgDStdt.;AJP&ːhUVOli e6 -n xӛJxK3+tֵ?&ZYӂYPHdj-(?Ɋڞ[\ꚣ{VPM{/Eyn2GFqS7X-/wE]3TK ߓr\dWW){d~c!"j:kr Ԩy ^#ZR`B 敥=*g`R LXZzT6mM0ęC˯m<>)YDKal!*q,^D fu~(eЦm!提H9qXB(σ@g8Z'JթOc14-Vآ@=ک08&ӡұ,a+!yB\0Cj)4ɭ4vhW۞&$d$&mȶ/s3#B(ҙ])P_wLʔsz2{b'sOy۠C)eR!S03p6h,)8V]@4E8?W 6+P>[@gmYf-ڭ~UFyAKXjnXWi?ehv* aP- }O[x By.j Il~ptTd:bmM1ƿ#Lp Q{Q!t /A`x%* (N^WJJJ'V<%收FB59 0l,j숥i8rQFZf0#׫yCVUeaWbxe7hQNGGM%hZqt@3S6y.C)S)!t|[0X4wav!)`^ċw.A}j,?LB6RTq.2WPqWo4hnY\-vQ>Y1t:Q( `Xw)B o>160s/㗇RM+x'Xm:^x#ӋDkF]cYyijm)u.\;_0ҵQ©@pz! u?_ᚫ|SJ"R!=i0?5logU0]`O)EI@Io7vhkEh2|\@yĠ3M)M8V/J$6{].;2j߬Ƴ}{>21˶8M]6B^MkaP=P aצ2_l-Rv% 9AqZa4yc0m/A=6LUSB5@=)I2@>'6g"5v?\bqb6̑Ja&:Tw)Ts]oi(P3 M8s[. *XL,wB{g%Rw-CZ-;#,! Nx;4A_ WN\mp:4C{JJ̜"UX:9r$/PϣdJĊ_fZH:]@Qfm%JF! Ӧ^S2ciM߄H5L4 bEpyQ5Mx+5\ΥfCf3{'g\xȑ*F{x;RL2c-uFm{ -_Y1 ޞȞ?ttl2'+¿ܝlmv0'n{U_%PWFA_%+"CWZCa4(}ο=zv|@ƈ^Uҧ{OՑ|b<4;轱A0"Ei6 b? _G77eh`#Vq=V3yMكѥhRޕ FdKU-)5uy:I1R-L (_j-mvZq0}MAaNQ^ItsGƻyo'dY:*䷊& XNC-G`"[%6t+\6$LA(S/ /ܟ>3mez|^*M]F(< Gm 3t&*:j0(Z6kTNz)y󃝥BcZ1E1af$0_m v7TAH,zq O2<`j:6eu.{]o =@2 ur4_c=IH4ZZ*!Eۙ%sCQf?YBӪo܉' "X(;.ٻU,2ە H=tqh Ws} ە($\"E`RyQU.wvA[@P {&aq9pENL^cuK?K^ޙR5kʺgeLrq1$kzI+ #l(ucwI/Y5OxSi4w/8 Mal>ɣhN_Fw !RÎ-8' Xd%;k7[6r$X ꤲC<snvh LB)3עxyNV=^{\:7PgL]3/pj}ٸ&1i򳾣 ZX73CJS$#Lh/Z7կ|6ڬUutB.qeMԂhɲr!"еxK:*wZbuCB} N#Ofsv$3|*^Wd=8ي 51 %v؝JՊdDUE 5ȵ'65-{R4PL|SF|Dj)ǔWuVąثڑ+2cjyU_4f܀huLax pH.mecpMygDRKIS HG=\W GO;(yCTdnby* mjhp3vڄw]ּ3n{V9kpm?f/Jz?E: mOQ$Y-列(nId'E!+ʏ Wn}@<ҙ%3*іCBn:3<54 lakҀ[Qpw<* %-t<6BALa0Db&p H(s1.ȅ!lмPRsbm"z}ؤ.71[k7/r`PKx^lGУzR_ޏ9Ĕ{`"g`F T 4Mp- {VXR@ck Νc?5 aś&}ѻ܇@jZv2@4au Do!"̇j *vI=Z %JCW뒬GUt׳NN?X̵@&a Q?nw[^2)\·Q PETWևTK^GZB{{"0G\Rs?V-8WÛfGڙP&W~^ˮIֳY:hѧf-Pqfj!t{a)C![ϑHhB)#*8cC~1 et]^vvJKV*2ygc1=M([}ėfOD@8Da;=FHE2,]h p5]SCp[Rif!d\"ASo>rAtKz>~ގ@v.mqbiLvw:us3A8vX$W:f=S01vWd0ڼCirN~iiF\w-eyjۚ<ߟ+1.9/ wQŋݏwNYTQwkN֨@xYS!1"[x15{$1rKPDO QE)S5aLn8w31A AB+v^kM`C\cԠ5OUJ4#Jh:5ۜp p'HC QH!-qLeVGMU9>ra%3Ş:ʡEA\<}Tۛ+tkBջR܏}hqf#:MЕ=Om{҈nV{;Ű!ڞ8/Y)?0xuu\k*b?qrLC~X[8Fh+!?R2&v 2^O-dN}Sn9ĺVcdt‰Kz&^Pqm\x$0X!'ƕ+%=YlSʓT hK `yQs.Äk@эqFW `0 Em4#d}2KtcUp>qabhZw|\hv@5D8v /f0ca˞~\,,S9+5R3pdE^M0T+`ԡ8lZRG-GF n#+e8L2j GSA;njb^H ~()!LEu,{R"9+`HDPbOdԖ%3m "wc0;cJ] y^czWup4„(qւS[} ];nbA?3yS|ݭ(DXsjpH5k(Ʉg55/.!;KFE߯:r?/Ar:Ilvlش- W,okJSYmv^U"'1 l1cCZ3Y칼e[0}9DCna\E뢰N\fBvM'1%$4nU&r:^z%GoVf\~>*m v^Wj'eNjS'woPiwzSh[?>sFcRO1oN{wS 5O_=!6e1\WMڤ֯dng~]H|+qm~W'6yB ܌rlT%g̓Ib }]N msEϒT/}\)X!%`gUs Sǧe`ߧYw-*v)]鵪aͯCJ%ZFWݙJxc}6;;/_Sq3RFyqDh@F'k߄:W]v"Idj`oRuF Wl7/cx:/f68]$ {| DEDURu{_L%*?Phd7Gٻ=1ͥ)!ul<H yr[#9"TܣXC @ €䊪.ۍ^UpT:)stE!4XRX<1 fɓ>=+3JtSf2 Y!Yde5>s:7S)*#M~e(Ri̊"cr`8 .3vĞIT4iXT)Qdf)'pky-"\h qH@%L8٫zL eriwҡݎw`}>J@_QH}fesb)_ fC6&ht\!) ̭1<Щ^A8osEUjcZ_JB*[Rg[(0!]ڋ׬,H שϡsFB*V#M wQ}99 qpeglܶ+.LFq񢛭_xl2fgnXe.G1wYPa#r0Շ`ް:YM럠R67<Rj}`bD>7 Ao`9>{ zFUX"vgSi;ħ)/^B=T2U`=;yOVYN&#z=?dqdKh|N5. VوV. T{6J|^>A\\`՝)ct}PفW2hԱ2L+u|օn<-4 I*L{9NQZH( ؞iT2MK ښf#fe|!6gM5Nh+:J||ǹisO?/)QJ0-6 "Eo,:iԙkX"3vrk4G؁#~7,YbWGm^⠸Bl>=]k:"d44aAQ9ztT-1?ɁXl:#蘿p!LڸHL.3bK!)F̕{~S1Gy4hXit7}Qi!yyK \7ó4'ʷ J J8aagy s4 k؀*xrl%K(_l 8tӫh-B83ڐga1 2ɹރEچ5ģ H+y@[cȀǺvN u›#^bvj!nT(Md{=VρQ?ΞÔhX5Ҕ]->_D͵ U;]Uo%:+ X+!j^U  l>)$XZjIγ-%)",Fp.ofl±&ԋRw| bl"PratUө{n?>8$œP!Tk4V^ Tw%pBr؀iz@YXjH&,g4/b%Gof`qڸ\O`G\|Np6]c*Zᇲkj rt #r6L!,at2olefc(-zO \g^=Jt֤ 6iȆ',LRޤv;埾x%^ S,M\IEÄÏ⬺*:G\itι5Ц V}3dKÃ&Xϳ@LCOԼED s=FErT}Y`Hh|.FRps~;\_Q9YnC9Q().sn͕ ('LD,ˆbQQWhP{dPW?y%7;E 6q~urɅi=AC$:nkHQ8ES SA8SsMF5PKDQ͢yYHȴ!^H{@syzoj'%&ϺG$]GE/te'a'"&I!涀lb%uZ :F8~QCL{CMޛ;:!;B;ɄSlyf8=5 ?bKU9R`_ W \&Sr]RcXv`L/}?jCA_2{sQwêCYn },bYI]QBځa˭dE۸](l,|Q[`凕,kS˥`]3YZcŠUyiWK^^yHlWD=Km r")c[".ߍMi*+֕} VI{Q O˰1QObx[h=:WO~VEw\|xb v* DM ^#5hY2k ö>ƚϳ=Fi|սFd(\|B\Π=5jHԁ=iV%mY Q>m'!0MZ.ٗ_gUIOIy{ݼXE{ )ua2C;+5JVU(xF^*c0iZ-'TżiJR+>TGl`SBLӣt]kWP;ϜuTn PTңk"1^wKPZV3:aA?BhRB2odܵ_EQu9@ i.xɆW`M\HXr4[/ڞ_.3Cx"[i>0kho$OIN3qWBV_?Ovf/ofvJP.g®T3,V7@F8t'ƿ%7Rj:SB 2 9[fyY*DŽxz[1ܼb_2F{pϧzc+>ItLUh>~d66ܸe0v-_y't{`^6@Ĩw #u?ޱ'ՙ{Ė>UdU |$0\W~$34\n2dIX0RUXUWsyyM&X#[ @U!g*1nI_f '}o e2WʁZ(YF7,Ppf3h$;ZL$hi~2UIa6͗â3X0 "9X}{7WWЮ3eoڱxd*ZBMJ]!:vzﮆ| 1atQlo̘H6NjkyYmxBYAHƣk%(]_,)D;NtF۱Nckl.5(Q[՝e=Y ?rSڈt Ȏ8"L_|_†P}*`B@qH];?(u9 dGW$)3V 0Yn͚|\8nTx)ޜ/& &6)(4=S׺Tjr+^3'x߂w&j2E/hlQ|Η~g FA{RVi}`~X&H7_Z `7/ }H<a(ܯ_2evW `HwoS 25t&VuוplWǩpuo|Q @j^x9|Q 1̩Ln7,\x k̙opgkB1׊Sk{39OaS80T(l"lEzBߨҕqoF)N5 qD)6X aB[hkpϽpf8tD^UnC1҄Vwl X& Pe|4q gkF K*<^DF+٩ y{{ )O)`]@g-U(h_dvٟb\ٲڍSi+4ayNzPhlӘ)dTjӬ ̝{9EP=~손bLߤ2*u/V;ԜG"{ݟ$F\"UKc" Sh39$ c8deؤk+V:ba_K_BN0ҀXz=3ƧKP]^!1ym=txU"ޱ.ͅcV\{7*ׄ7P,2'\{#t(x5yS,(uqAf!`I !pr03kJuׇ tDpL?_#0D 0J[:XjSNMGzzh<(ԺvGvCe䝘X:~sܒpX?:[( V2oCa?ˬoZ:6ɏ 傸u$l9BamfԂWkę|@K%$BA=:?R e~:Ueu~J\$ =Pg0vh<9Ơp2ZnĢ<~`c8Eق%]l_/xTɱvX;(`7{| 4jc{ s ބT3qMjhʏ F̓$ɠґѐrqZ2}ڃ|$֧ bQV G^c_m;큝f2PPI6=“(1FK}~";Q8F|!<⇈t٤BdQ5OʯpQ;"y&+ N0Ͷ5n݉?1"ZAPhay~0ZYhۮ{w̖.vگU(I kIK EV:j3at }%΀XUPhj;_\ XU3e\S/6T92I'!y3*ijah|ZDپ߁ !MX86] dTa'gq]$F QjfUbhȄuxEZyju ;#߯2 - <ډPFF̕>Ҡ܂!2Xݼ\nMHa= W!ycnzϲƟ"d^~? zh mom)pvaoP+1̯:)&.GcH5]'sj cϯ!ѳ :i(#r2уZ&FբaHL C|)DQ6.,<;ڻ=;B{|W\۰z. *F@JqsT)gS`*Es(>Be  ^`[7f  1(ssn7ͽɧ:X; VYc^<̕q†xӜC% 5là1C6{ uM+tĄ +ڰ իsO%f&=ŗCsQ+܂+򌉦mF8!_FFlqyl4D9bv$StR!>=)rBu:o-HL\  q*"3I#*Y#y]M5Yk@CeuDBZl:&ب,Ƙꝵ$%w6P?ݥOs|?Ar%MN-`$γo-:n*6a. Є$CUT-̊:m]S dK_5aJ#GGRSO/Pe{;LjIJB Ҿv22'K"x0TB'-m`q-h$8+NSkN |>X4qѦ飬ZDtK*#c#Y:Q \]chh+Tr(+ SLiB}Wc#z~5'au4Px.q3EiSbmf~䥣y`=ߪÀ$޷$>[Q7_&c6A@ﳯ57ݜYl}dg$4X'B1,WT9ddB2@f$C%9|kOu76ҡ8skpAYKaR] waof{%(t @m$|&q]K#DuD`L#^ ^KJAHRC~ďpރs#ouV?Ԕ(݅ <<*l?bg-Tגg|;{s3)'ӅN`UʞG#T/]^ ذ} yT.)ix5D4JG3x$+)*"IBދ͞3j{egVUH(Lq(% ~SiK'qN[tÂo/EJDz^&:@Q+>&1 NEV)ﱘJ]Ҳ_6'?nDlHUr*Sm=\+"q`rI)+$,x;cڙ6bL*stJgdh'>4lP ݦ $#ke;mNHAmki2i3SluibEtv/'sq%\wAOr9h?M]ʗ@-#S}jHb_=ui9,ӃHڧ=p:%2(6=m?^l PP\<:8-n,֯IW|֧>!iʞi ܣvA,bxvh砿J-1wUjQP'*sr |m`@W4DB7@I9wUy'cB쬮M,=|GTYۏUHHoԆѶ֜Jت^P90{y>YY|0 k ǣC-j/Fz)NP &yqV N'S; ~>+@6cWi58(=C^ސ0dz7je5i$S u`Wmĭm^p:f8rd@C'5_覀,$|TLNSZF6] +E9T{/@I)VϫKx.(yeϤcӚ>y& x*L`DmG(?cخ uCGm~l2DpΚW7}naZiO(zi VYݽd>N#,-'@@5tuy?ܵi/Bd5(I -r;Lo?8 )YC Y 70PD+6V[! e$ٲv,z7fUp>UWvKA~`֐h1' lڬ^֘F6fq:voiNݴZÕb],Ӻ#S15=? 'P]&o3"?8 s$A8 :^U̾9̈[**nNJ|J$AA_i) ނ뢾ѪDOU64Hn'&̣6z@՘?(fx8s3/6 \\+5?=گ4qp į؉Qba1ЍǓS)6Jpsn+0ThH|H:dR}( QNd2k7inLVmN@ bF8?5@-,@9 L&Q;:1/T)'W^oԖYshր vߵuenY>,T(r}g6hN9SZ©Z)UAL}ܲ۳+w[Ap XJ-T6be:ܢm^,>Ⱥ4_>VHը?̭1(w<Y9jUn/NtO^WpYeܨP2h))?D4vlb/ A R@/Zl~_9`7oIgeh1܆Ȩ-ǴuK^ >ıc#,d6;ZL9۩=~OX39SԸR$,j$..w`ca;)bp7"@ FBl'GliyZ!t̾R ۥLje^1'toQ4`1( }Xk$+*V]`M{} e^v7ral>[^75R4)-^G5N.~#)y XrV{so6;˩KZZ'TG`MU n1wGw.'cO N-RK[?O"θ؟yn_1@Vgd@F៮:UqLiq BߧZhڠO,"äo,$λ;%{_6&nYFtx{56BL2MzQ@*Sx$׺wӒAD*`ͼBk;>2s/mljWa!1p͔etwgÅ,@|$Qq.{R)*kFbU_R^l6К-&W|m+0`㔐#!!ShR42 6Rb`[?69%@A˴ |BpVTB[uIBm8I(с4063b:.=wM&(i0` _ nb?nI;xq! m .q9R+2FF`ZXn7I=h;f`=q*&Z33-]#,%1i"!MxwW"8rۧn@n8fe@ߩYGsT:rhdCo|k+7|):m}zcxAJ;~{̦,fnk׉nfpk0$l 11j0BCg Vנ9ʙ)5B+uǨ{DU× Ej*.B1 @qԧy²zv಺ʾV ôΏM/mq- %zaTIbqq΂きr?zgC1Vi%9 蔥¶'!}e`Ô3Zp2ܢ]P[y;oP3s_dˑ7liϚQzs0[!"E|9@N({!mUe{%tĿLQKYhDrKgGIӚjf'o~(( :Bof(|BD<\W ZJJ*ˤwW@uDiwud[1:ykzE1Mݥ;տ,5N+7KN6C/+TƝ&y妿d\D[H>[W OT.HsJCe1C1NQwOř`dj/õFJ r8Q,pzD飨g!{i횶(rd0R9Khqqa5Re'iu*1\?'Ɩ-QcxXDh?Yִ!x/0-DW"=ULuWc~!E9<'g t1\)^#`gh=L,hdI ɑ<Kҏdw@=7}d+gO* [I7,bD5v.:)hFM)0L[| UЅ[/ 1'Ʒ~z' vًO#LSF>[S64Z,n{[1fE^m 7Kڽ'܎+߯L֊wexyύVngbPdS!HY¤30@MFb: 3sM 9ܟeN8aɱ߇~wfABuBoyZHW8y] W2g=W7@NU =x 8jTtUј q wkiH9a/a2 >g\%qu7hsOKE7rd4ZȐz:Z>]\X:t KT0 cۥcx 4EC/3&JUo"lɸB.k3(5LM{DY|W$]n9Y "1$2 S $,u,UM:z*wުHck5qtۍgTTBDiڍ]-Q 9?6~BDŽ"O̖bzr:W[5nf)kQ"A&Y.ZX mU(;gSOZc&OY$q'^ ?U @ CCU$W;r1RBe,v?%.=7q ^4F>S Q֛jǓJ2* *}-a'FvӸXTi)+F4jYrPM>F; >.T׳HS=1-#T7]K2b;^} 8 .R)!4Hl ՈȨ7c6EWkv"e7hrv#@^)9jR[<Zc[-όjkdf2JLMT ޺p%L@Ɍ.弸"_YCo '_jrѪ~ut{xU>XPu}!3a9%}^q=pAQa/][c?CѐbrlM rbnBi;կ "Md_S-X7j*xa>+%rteȊ..d~Ҭ>K@8!犢P5OzB䊚;RaLR(^gCh&@ܾ|U`=@hQvp^rȷ"K"o3&FrmE_ņ0F:֑`hzFGn"2ܑ!DnƕQR;TMxll)/ k{{guBq<~%ނ{qɥhC6QV#,jny=UT]XǶށnðǽ ig1(H+d (sa0'8)p#^37XIwM%ORV"~rIEiQ-M+/Z;Q+tp$?{Lqwd +C{<1ĕU2G:'ctHx*H;"j: v9ާ|U8+f^0DTQGM: a(P] ^ wg-0|2d~BP[Tӿ֫״oIrZ@s'oOk|6p/!*D^lSQJщdZ#W^NkZ;]t1},d#،9=/z,0|"w?GBI;UI{{'#eRLE|$3{2ƴr-X s_ma9G0*қPQ69 CF1E₄t \)Æs <(_\6=\$ͪ[`{^X{GkbNvz#4ߑ~֭At=$ L+ӏDO vB%TɈ}>eDVy]r0`G6n(Obn+1VA >iVGѱ8i/+T[`"TxcUxw N)F,Xvy:fX[Yk/4e-Ce%B$l14ܑ)zW>Is0ES e!W~F*~ 8ZemeHҎy"~u7ڹK&V<kR;Cq>5J"};>HXG[uZEpt|uk* \gR}6ӷpO:2HTZԧ DMR;ep?Ti׶0{mN-:B.+R8?G5wiXAz>*jl[ RLПg; U5PP5g.^-v2H"r F(8}Ao*ǖņ_/1MZ!m *`/"ݞUtB`bKKEh٫@S i~3Ea"eligf n;$S|"&寁భ .oVcQ d~b6qgq z\Mƾ{ S(ȏSoSca!z(TE8 5bsR&-j&d5+wX3ywfL?gxAyD|gK:'U+3%lUqK|솝W7$tLTU<_ɘ p1h$4m;_{FWL6 3̒4lju9j+9ѧߏfJfm_iH2 m^ <;akO%Z?b#e2B?'CC0}'avKDdxJ9첹ڐM G72r@dw'O^%Cmul"ǢX}4Oz"V2i|SR. ?Z2g7Kf?MkeKHSyvLoaŸ:=7g0Gpotׯ!IѷM|s1 8@-AF# :":em"A`݂մUcIJ^ْ6jiq1n {U'g?7cV^Īut}ƶ0fXoh֗Nxw_|7UI0Z@xGRra} l#5=5zS2C2R<<s3$_To)8d0jrӸ?s|Kqla1ӜD0Ǒ+;^?D#RR8d[KYx1^6D:ec`mPP%{GK E+BJSdpP3!Q@~A߷*m0LUɭ v>#؏2{7Z vTKa >d1 nD}poJ g2{7U s0̞:L NYm=R\El-'[ z Y߄=,[l&5jС#%=aՋ,$T3I8ֻyzVxBޗӽIGd' IyR>3S왅ہfq>gts~~+$кr߷R=Fj+Jzm&5ۇ5zW)#kMev?;'FDFw~ɃQТYo:ĕPhO03J\}M',i# + K:W4HtPܘ~DUS/1dbݐl%yf !F5\s3,`k>J1 vUVf_(F$d˾gvPv5^bRmӦ z=Qn1VoZUAF[L~"V@1>:ׯk!JeA~r-QZoH'ణyJz@ շ$י{|7zi}Lt9JK'kRQ,ec[5ph78"O[N4Jm*cAflL1v<%<'JaPJ@0VgC  o^^RlC2jJHʽwJ oKE݁F{6 =fv&؆PW7D'14SSKcY VHZpjwT\@EvWzE)I HeS7994sh: GT7,;Ir2d񠌷Ig*JҦ5 I-u_۽y zS/.b|%q @(gbeb-%':\?+Q#r8*/'>Lc^R?IЄl# "E=~ɟQ/أoǸT4> ;DX7fK˽HIu"t2`4MofY(u)R;=Y>Y ɗ Cal$"#^iӲ\P z+iqgpdfA=^o|Q-J U꺲u`=+æuݛ Jk Q{j@~:L}[+Ti\YMkw82$++${Uj-W AM"R#ZxCbe<ð<6مNmXeR5v{ PqT#1z IUp'v恑܏ⅻhPG[ܹ y}Ϭp|%iG2GTQH)x9D36>qhe_ cO`F?g?@7CLv FD-(-rR_o;MP.\Yf\nMz42og: % 1.L59|O#iu;~{[h|F[EN49}Wm:|cw,@wPw~ԞaaAEVh<^$"@5]B+]iYM#(e,RĖÎ5Q% UGR saA `]kSqdaޫ5a P-O,d?q溽1*A7D>DT{ˑl;ǯE劣2fX 7'E z!I]VIfw<7W % v߅u U"헌KRāٽpŃM&zό@ΌhGzť(I< ۡg^9 Г`6ؗoù8G[>)kamzgtVOF-isדi}f/k(.=tQ~; rs3~S!eSd)БXxe=q5o9R[\lHݘ[=)XƑo?j)a^ђ(_mTGSzF5I:m؉L&顱I鳰Ba90Mf}57p$ ?bPYѽ`h~K:FNFaU,P8AŝyEy6u,o I6&kx;290 n4ᡘ;EޓbPkgK&44b04fwK? g;Vv,8bPe-"ԘGB*m=xY凋7o`L!T%|w3}ʼȡ{Iγ&?mǩv8Vf[r-R} N35E!9V!p|H6_- n9E\tb*5;_K.@T.KM-ai^XwܖnA=n߫hե!:ĸÁLUw Ӹٞ0fՑ"\ xj^EQ3Z~#8-_3Pa/)EM} /t4/kRw&{QUӱˎZQב ֽgM̱qD$ +nŸ[RskK6<+'7kyy1Canx[ L * #We'Xx}Dc 0٨T޻a0&x^M%:)etlK`QS(|_P9]|bhcj }pe OrPNϪ kNsQ.ZF6u_t,in>Z?Clخ|/?b£xE^U;ǿo;t "k|nyQ16 N9G7*8N٦70<߱ĴXX<5M^6'2>0ސW@㲉ef=eR-Ck>T*2"gm݀++̂>Uƃdd~+zL˨@`r)ԑ-GM1]t[cBꀌgNٓoG8ړ:\. BR|0 8-g8״?!i΂yP,_oK>. zP%-oE)Op"n 3+ `kg)`\Q"[B#6 T>MJ5;,gp)<*wmۯPo']{\LTnL8KFv{F"N8rY t[ A!oX 6nm3ZicFDIt~(#>{`9c*`&Z%}=ZlE:t'QA@g.Yksi c^{wuӏň1 5"y B/@n9 Q.u!{9OP@=ʲa>o4F )sנ$0[YxǮ9W7}m_|q2w+o1e|%T#AoI1[Y;'$CB|7ݭH:l-u6%4x5I9Hꦾޛj̹5 ٰs ǖu&Fy'7lc7!{If40F$EnTFM1TOӧqj/ xcP֚Znzn0!wOU q٭oԒ?Ix a{wi洗 fG[Z@#N`?nРwϚٽ> 濔%VL"1,v;Nw:ѐ"0gGs%4 򝁼v 3&Eוտ!Ch&dԽZX>P5gJ|MЍk9 ?5*uR"N0#V4Gp[D5Es_ۼR%7#*+( :ɟ߹dZdv4e,ͥSvD# |f:Rsk0a z7B1gh@UXo@T. {UA继V`ؾJ2=>Q -QPG.RmD4 i`2{t"Ԓjg+y˻&|4º+CCЌLʟ A iM Cɐ& .ldAMfƯvlLP30X7OnΠc?TVzyd(ypVp;%:찅tA@'m쁥ߦ΁ḧՉ(]N?䞊e:IU0q:RXk1 ϲ>hȾ"+ttwth4N5s41'jוT_6$HwnZq>UPw 7@jWIU!h`RBb܊7Šzֹ"~v '(lJh}^w;Hi c6 Pi8N340>5F7Tū'J4o/3|r GLKl`BDyZ E.۔ P6Kݼ?1%hj=-{믞m]%!6,Uk,n-[2\6jƻ+zR "{K/ZU'PUZѿl/XS7X S 1}gaHkPEjF\JtR;Q?$41m3{8'f{"aAhYU{% p =u"h~(U#we˳¯ۮjSpTF||<_#Ρ^$c7TWQ#} !j&}*g};p׋^Q'ܒȺEM@ޑY *H vdf/[hۮL&nFl\Jwi&a@`lY<&.jE~ }ax&q~vTX_,Ywpפ:tʎA,3@KVcz=Вa2!jcb6e#d2ՠAܐу5cn&x["m*MlX.Bt}9,@W/DVЂ({M r!}Ctfb=%>;wF<  5&m HF5<p#E)1LDi|DAS7.:ͷby@yb geUÍp qG47[Q< rT8،ZɕZۿ]z\C4*Uaل9CzD<9tC`(|ڰ@e<-.ο6$-"ws\7;MȎ6&@3ψ4J`8v2g^VԡQџOFfGtµS]#;5Jj./)OH>#<ĭz}2~)˳ew/ԍ]#yghN4T1g &s챙Wҫ`:$_Or 3f?!xyJR-݄=P|Zk8$p a6ا(Nqi'z)Қo`*8d.y?Luí…8/yjXAgf j/*r׻E!mff]5B6W:ގOu|[ɐ74Wĸ># xT = =QmH)%MjG|@>1`d7)31/`Y bvۿ U'RB&[׮" gW*m᝹dg@xo\8Nki ^c7w+649\Vz-h'nBidnkObZ>;pZhrg[,fx{c~/+f?52v$#HT-DҖ e׸̼H\V+"^z9z-8ߗ. }Q^\qrqí&Y{ 'B;? RzvxH鬂gohB\!W<7SR|P"(zx66> ?'s;$H6)b#( J_>?Sm,TO;%$~@ Iʠ/q{B*^Hn,c]![2qs̔$RlJS*R=v8{r\tAOM~dqjUnZ-!׏y0/*>Cg R،+Mh(e7J_t[0<[ >(,\]0:xs>a`'Ǚe`B

0 =-%XuHz~.JΰVMl>꽔F]$`u,Qg}!T*Jњ@7ǸF85{x5'YF (7Y':vi&ejMg! ֖k/>Z}`eH㹛Sڲ]v[^NLXY!V&ޯ $&{ $E[a?|/I'{-9]az,&M{/W 0_Bh/$ܩpJ=V۞:n*@(23 ֑~9nb}X>. K ESOM2ȋ: zG2^ 4EI2zG4Z.@^qMgHfcJrY 4/&5=TQTJڪ3[ΰ"vh&ZD6sĤѨ*7ťHN'|wt?]L𴍵&+.iю)y+B,>gAjL1pnIJ&0q ;>GAٗN']\߃j{6(^!lQ(| νoHki_'cnWi߰JS;.i_AweZE&`G00P&T@ލhڰ\ZtnDv[ʱzLg5z@!tR6Z <6yhB֮WA]T 53;N%[]Qڻe EO2\1e΂я }Ԇ 3|@h\` bĬ-,6b@6?"~$Jdtג|q{*#(H6ZC:3Q=!+ENG@t۞Hm0Jl 7*#lBX׊ojX! bUь,V̤;:Q*'H>tEqэ*<<ita2wtlzz?k|*z&L4/Wҍ6[Ҥu6vܚ>\)5:Gqh#K1!{ִ*lF /Ok@?0AIk^AS7^-tg{ G:0 SڐZ)Mtrpc>yZc V=;]DW-HٷK:&EJ`Rq6M irWP8uo#UH`.y@ˌ^=P۾ >G;֠o0۔~PD?Tj̴u˫I JW9l`6'.h^r.^zIPQoI7eEm8 Y婈 n򌎧8ݝ^t'^]!v9+ȐdE؁yZaO>\ ™ F3sVm[e4`^^;^鵏KTTo!:x UngC>jXDn|4cD'co}<,@/7[%^`tcfytUKu~vS 04w$EK*ϥF')k"P1[x̹&¢)|&dmiuttgL%XQƒdVkRg]tsBn0L$06 oQ;;8>6S=ZaWh!A/Ji˕ A"%NDa }0Hi3ji!b~rW+9*e_UˮNWqwۋ ny8K{Mj@+j>"xY_cHC /0V[𥉚wr~7}o]5A/2=S[5plҟHgp&3FSvFM ̄Й]Nd‹n;dE F&W"UaIz$›\mET i0TLE>qU~"Y 1smȼnMthìӭJʷ_{ύB`=fX U4*7Ridy !C;;M7n~ʓsK1\y0%V89 q|,V@nf/V#>MnFwG^ 8׋62û{/r$X֍ ;׎6S)x,5AO}@t[hy_ɣ0 +cN2x/&l]%JvS+iyE S'U'pv`z ߋ>E &e׏w³HCNh//TMbyi&&taU4|;`=ģzhwGWblZu)Fe o+vD4hZfU]^$Z C~cعDӒ'8ANҗ]C y08`QQ7H_T-dKuq>. s$\`AgN%'#+BeukF!'J>:"-o{u &nuImi`\w" 4v SbFʉج9oR?/M"1Bڔ\ѷPe/$şU֘}Q͈\sRUw 0$^c$3 g>Ėɇ-SwC~Qn b:BOC6CAh 俐VOp/7TPQT/LBF/4˪HؓPc_5~8kpyiqI#"Ԃ8PP'XmtމϝifMo-ϰ@c!KtBD &Up:3/u{kcDGG0oihrQ+#>|@Ͷ{/P EY=}d]XK|(cR~ x[ 3yL?gOWDgHs"jv=s>l@Yݦus<` udq'TP3Jڀ?T/G[%գŕp$q.][w5]݄d 7kVKa/^m:Z$ιkT0WL;p Ubԗt(]i8yA%?` iTFgڅ҅}O|{dv /?c!*v=(5좢M}q@$ϸ9UWSZQm nkf5o r&~avC|'wa+] B%e?3B;5(BKsC+a?Ɇj tnsԫO[4F[wD\fl*[ ^߆ΜT,$S; ,jK:?* _u:M c=]kI{ҙM\+1#!f8.cט rk.̻K_|s'a4( [{-wmj] Y-HoWyGa69=DibLb“uCa>/ O䴿9ts%s6  1Ow)QAm:nw#Ϝ=MlU1ծdh)- [ 83s⼦[?ҦzoE*2.3zbu4[Sڣhuhs˲᤾d+Q7t71͟TDrJiP2J 5Gbpp>#BQem|21MM(ˡ$ oUl:c&X<9搖dߏʍ@pQ;2Fg~y"W,d'=Et'YZ]6Ь1E;r+ǓQmn2!omN=Tl>I㏅|k ð fZGĢa og gRIv iRq,ܹsqdMfY\SnX;`Sj::Yk# 1$*q?da\KQ:N}sqEKWng="S<2ZEhz*2$駱-Ac%=BP պaeɱU_gttMOnޯ||=V·p//̱p~0<*1ͭTG_60 r{9*'4&U]+z lg,*/+ҙG3 쒵inDf(:$Nb ̀.p&St O:\/!UFB ئ}\\qby'7X|J>9aE;`;/?@mل 1eLM"=^(U@tsOX2t%MVk ֘_4) O{ϏC`v CE_h<,݇WQn*ef?];WPa/锞k-V]63+^x* \VUf]Cf_p+VάzoBK"hN/D8~!>Q)$I;K9n/ HhgHs1)mW ~;z)6.*c/*Owu23jbU8 )[,$S1gC36 \`o>SV8K*Hؒ&SIf1ήdogR.f h QTcq_YYV f>r`攇d~Pkp>l /_:pJX;=Lj͛Hz~;FJ }bt|ʷw{P]u u߁{9([6(FLG",I /C{g'!Bv~2MD'o@PL89` @@,d)ߞ¥Ǔ_֭4Gbx|Ո|<ŒX An.ohAsjp)WH(ҝDYߏsp {u#Ru 3k-d}Snw4BSZz?[B3^~röNאv.Elj4$FC)6d _NytI_G~OXln L%& 9"64 U_81e}~W˶(1kQ[J[K)?y!ikZc_`r?5o_e%X3W㪌{b4>թ9`8pojj%cm|㉨$O6&$JnW$T&NGvBt৵3X:!υVE}ͬPt`Jo_Hy˕pO^{S8mS݀njr\^qϯ0=(WHS fyavѾxf~1:/Mms% Xmx˕ډtTHKK~ꝰڿg.f?ޞ!8$uN^MdzH|$)K\ఔ_cQ^$fJ8$`N )lxiR<7KARޤ, e/Hͨc'R\r~QZWżM^9m/\ZG[϶qRXq`d?&0_hZgdB}VpSLj|yi"љsFYwt+`sj.XoeQn6"EAwCQO Jc:f*LBfkTj@/LM{顒|<["VϽͤfʮQנOESݗ` SǨYNMj򏟦kqL\ 9 G^<3[͛n`By&2xlK5Nߩ em[_7!GN^޴-`u 7DS?AQc"~#KEQ4"P`ȅ̛~Y Cdy<.kA5D4Ĥ",uXiwXFZJ1O@衹~ܑl_ LusUzUI-L}c^L~ҙD{0Jp3ҪhR/ x/\gx?am_q;G(t/qkil;۲"ro Dx!ʶC`d&/n*hOӕ&ê 6nzˣCЃ {+SLŷGyRM߷^?䳚9ň\QI֒J(COD!.L9Ɉ[8/aMF8!@E~v[ dꥪ|#mZwlyLk%%oibCJ@P;Mw!iVc99T_Dj#i4z8+rDgS+=j6SF@φD'G?xan>fpօ"E6Zq=A%ß9gU=E~5$!lo)_0Ac1Øfm$&SI:>PybJQIW`^17tLP''ziVk"Mq#扐td8LgdF,lLcDceC8- FA[E~& ݩ lV] iz6w6Y  zRtqj َ^BbRLdj8M-ޙaoD~IzЛy0["kxJ}Y:>l #tiovg(1 x3"& 7lڑ14y H R:GF@_jx [Lubm?Y_qft/ lC*|B!=0 bou21ǏQwbV ~wqw/}R!ozNi\@w~4(3gN,˛=' 5"~6h i]B/z0 ^m Ӎhl11n59;:].ʦFα~鄏m:"Fvqю,K_rܷtP. I B>Bȵ(_at(OF+7|sP ~M(suf &g,On* ۲HTOLŚr6uپZkiTPޒD63% /߄8@>vXܒn9?ݰXA8~ҟ8 LY_˵fU#l$8k+ęq)>z氛}vzL^}t6q)瞜LRv諵8kKGrcM X9rJp.íz wVȲG%v"1yW U }m)J &.!97 @چǘZw@]eMu!O Qs+pF$^̜H+HYr7oxa`薯B0$ !i;辟D> ߲خmN8Nmtʀ{A>nx@a}],/EEJI TlPK>/sC%&(@7J lRmQxa-#U,*OH'Z#b"Ii(H\#SKHu/.#m['Yl ڡ*=}v#<-,Aa1mnJebp5 .,a$NHo_2w+1yԈRYfm֦$iE9|ŲqM͚2N [sWm_&!)^짖}Xbb9j%]{¥Z!Lmz`FΕ{>K?(!Psj}[w` i9{0֖DA.Y!J",}ґ>VZ 8TQT;9L1"_ 5 lw/L~ /ܬ5nO/<.NU j{H%J;"^Yk*b[iuTO$`eMNM74ȏ`=<_$G~ўԁ}T'w kus ;+T 0̫ Tb'n.~{nU"y6T#{k3; YXw2 $!4W^H<4dnpp!5S62DPiF? <79 $D$Ҿ bKC;['̞133Jui7d(%…B/ lKG,KW%ܮtxbRa_7 ChS<蠣c$gz}z,g?G'6ۊg1w;Ͷp]5.M%_kA$]ZGy,ig9+bN5Տ&~ 6[9Gyq>(?p2g;,͢$ړ r)\b4r lKcw%B}c#Wf#CтvPE :0ܙWZ9&oP nЈr[!(@2$,7e2rm*FP4N'N>c&8r]o%sCQQ ]Κ/tԤWdn@.CuVȍuieBvh]p7v =/3 ~-Jh"i1{;i~ ^Tjfdjʜӑ+gz*0Ēi=}IҜR0HG ճqXeQD;Iқc -ms=He^&7[Nx[8T]eG52]`GW[oy!,5;,Jdz?]+Vx$3wGwa S->l|0t*y.zYh D..aκNLSY>ᬧo0ZcV!M,r^+"4/z]Dbuv8 <6wK]h\:7;]' >i-P(ޖs('zelׁU?%B8YɏζLA^0/j`-UE N3_$6jߌ1Q$G !.cSNm!L" ~\N(gNpn[q,0W:+F^I`!L(v F ϰXld wG ZaVtx&5L3,MW@aߐ@4m=o-ZeXcƬ68Z|s@QvaD(ga$b9;4ZAJr6PȮ3 A+E`Qq^&7woH|G-$hgLFNA:CBD刓խ?ZCYjE'O* Jww5ɭ1gSVz9^KJ}HC'ϒsڭ\SZ v <4NC 㦩PXBs `LZĄϷcU5Tri "2D6\8)Hf?Ȟxg6Vm%ļպ<'%1_QbLG[;S}$i n=TSgs_2]bl 4w}6[NN/gVV)%@Mݒr@4 hnc qW=[Iܗz>T{RC9 IMS-{c#")@O+닅& jS{RۊXUxGS8?+#BPZiFkj j,=RZ̶/4_5j[{GfcdOdq ] $KҺa |ӳ@|-MTE-@Nۖ(=4Jԡ&ǸoB^䢑]JAVhV|4U+z*fv{}% 4G鱖Yk@LRT$YL+X:~D|mIbvo /3Fޭm@r{t)n jB$<43 zHjʯɋ4K:\} gƚƛzۙ4؝i+LW_+nVq(1"s-{\d+[ e;v{UC q֜0鑽{_"ϭd: *p0oӜ.YFP#/kw&/cvآFq>rDh6vX=vP<%,e];<+4f +nLD'FvgiNQ$rΝDֳ-:v 1J6IiTUlgW 0L{c>8X]ȋ5XZF񉼸 BK`;5N cVޔI vLqݭ#"_(smۓ@&DsN/ս,WA#SuƶHi {ՏkNMEpς4HD2}gyZѤ⩁@oi8 .bjTN=e#{{| ;ٹPE^Tc*7:4^GɢQ4K G TΞ¦Ɦ;ȾA$&eW!|1B(+o͇'6@$iuh~ϟeI5J>BEL%Sd^P4*$TF4F>ٺs;|*y͠$;؟KȜqfC4JE$}qZK"FHSp9\ 3J&pͫU?]dR*xxҞl&~< )`4m?2l*_9<3?ލ>ͧKri}ØJV?zSuȊ@Im]Rm[U1EI?.E$ !sCV֣6F ߝ'P ӒދXRj@: t!:D7tǜ9]vGo(N.1ķzYDRٙދK'/sED,||b ӕZOJ{(eS}E~ 08ȮB&h 257WiW -5 ,3;+Cko=AX^ƆJ9S"+ [uC9HMw= F{j\ xcߋ/Ӊ]kUj1s9X3T+ZqX ͼոW V^,ԪCΡ CyI<[k Fn|Pn$c>?Y*TgD? CڀX+VX'5U1c`y].,ol۫?=9"gJqF.ÅfJ%Wrz0xR=Ι_ɒA)<]>PHavx{\l6Z9N4jލ ?lG»Rl^Bqխ({;dDm2Ρ&uneهص%^pg~ʥܐ!-BќI^A|F8 ZJsbÍ;z[(D\+rܝ-c:qDsKq:Ղ0$#mtnOl~N+NHj VZza*џ$cN!,+#gH)U44ϦM7nKE[C.c\$K@I<$Y=X[V1pLPH% :N$Į7m%bƗU@eltZp.M\a& s'^Og8!.h.z@di"v;E)@$TG$=h=9 )#~6n PNǜ}NQ5gx-@nm>^8*wFS љMJal-3҅eȺV~+hQ-U=Vz$_; =R3RA0%=-qzj|ˢJu"!4cXAA+Rf hs/h+%9!R(~n:e<+&uт˾9H \\guQK_DH2%c]&tГVoDoݿ҄IY@CBYp }>}~)2W}4*Qxt,\{Apٮ:]hy/\B1s %A#\3l醙cfXn WؿlYvnt*Ծ}}=VG{ˌ(1]LM@B^C/He16e]ĸFVK5!+ss#+^z/\; mHyóm1~;RvUgxm|Nv[@{dY sl^oؾyhϰȧUU*}%G`7^]Ҡ> "~*uEo9 +<՗t* #PH̬k_N^wVfG|mW!n fjjP6}sO);, ZJĭ#.db%Ba{ig=(@q9l0 R`Y DmY5@qn#p,琻ﲓ4,%Iƞ=wzϾ3RKw[NGA:xD3 8\h*l#:zr1)|Y>pNx?W PY+HO-U}_w cIsLUac򄌺&}Ρ'o֤OLrO 6%*v!C۽o0ޫ^褎Gd_d2 Ι*2sf5oA;^Ma д'AՒ(v$}Bm`7ܧȃ*gsg zt+]tуCe2;WN>obRT qh{4V] L+UV¯ml2X܁K Rvz':6et-Ul'W|R6g'%s|zrQ-dnpAԝDXTVR6Íz[>‹쐰+Nt o8ba6NH |kIZ"EFyLc)eW.UVs3)!&;BzyN u"& hfp4J@8AҼbkK5.@8Q67хө5dI/%xNKVsYވ;Ud, fSMc`KVDt't'vm<{fOHI|]EBe]G:_^܎So'  BP>L .fPS" +鹻!NoNž bx#Qd/i@OI+pkcG== C5<40]¢JQ=KcQ XXmA /Κm|<=UIjHQu^C1[ G0fqAx'+s`lsn驦*䦾@pHv1E9U*8Ja$Jbqޓ Bف8|nE#E3ҽ "խz߰OZ9ܥVBHJ6NoUy{eޛbHw7B8xV"O41VXG( IGBz9 }i匲jvuD,k;?Ofbd?tqnL]ãGVq yAXk)?!x;Ř0IZك= scغ#*/Ŗ7tkoSp1(i Y+X[qZw;Hj 1 `eP {1)R Fj^%n2X y)ջZ&/B|GqI!Ce'j[G#L3[56^\6 kFMq+@PP02nbMF_H@g |xv z8,PjB\iCEm KӤ 6kh׻txpwQi݄ѿ/{I7N}kj'XȢ.٦]r`7!OlEPT< ωM#ų$'+T~s[NEN܈)։Уw8q๽e݄\}=EZ~S֧<}hV`]wl \e(WQ|>Ru]EVE ݔ~UDsJI4yA;s _ @_lGoD(]]"M#8~)AQ-F*ߗ407&pt1d~D%:ܔkxېd8nS"'o42.=qJٚ4yw^O)*WY=#_';ZPֈT;Uw?'[&QK[@IPZ`_H7unҀXXzΔK^gaׅ-lUycU]4kq4EBi>#;`c&U ѧÅX}kA{ <\TlrR&L"Ǚks.coI!~wQ.+rxҐ~(eԦp'gW0WO{Q >#cG2B%l= !5V$cuo3.d<^<:Qn-KBTX"FfWp9Kd&\`NNbHm_Q_h+iD ^~┸:x 9mѨ+(wFfM o5E)ݔ6I*>?ehmf96z{eal|H^x"QzyIVs;elk𧻓[&zY b>7O܂9dr;' >`_ZHRs]bHLڠLxC{~m6NDs5!DݨXLx5KMOX(މ-!܀x7}|߲#hxGX]*=&`N$*dZUu1NNVDo! ucJRz8i湊tTd*U(^W,>n" #;68燯xjcȠeX2k^j^/^2ܕK~j8OvI0;fWM仰Έ']0fobtUfU_+Pz<OBy:hdƏ4l՘P/ JeGao>k&Q1|Y&Ļ"η0<Ҝ]V+w i`BmYCMHXdVm۠l?Bpk@nT푗7ekwy^ L:z\RS&nmȗA7\s>Prh7F JΑMq&%x= eZ4DW[1ᲈ}|+6Kd"/?6Y\F2m@aʈ@ʼXNWu ~]+^ ]pi,qWB 16>D^:ƍ xs!f؍5 5c+$ ҋ%T8ƣ"QcSmly}4yU\x~iY*i.?Gx1B<2SԺÈծwJy rWN*c;GUvMcu ODf(׶/b^Vzq*7[*"8pc|kAw7%B< LZ--0=7щő$'bggFULT'RMf`*>Ş#i';k@ގ` f/$a]D"ӊ^U"}4;m-~Oט'I]= œI74BSO}clj%bт <2a.FCbguk 굕i>=$pZsȻ{U? vHhfB r?8oSl"C[hfh_'HdL.xJuz2 :UL:6o&, !j@+=k%ñ `D)$7G,vGvַ#RCKnnf(֤Fj|иM0T^#dC C|G yk/mxx)qwo.J2 B@,hdbhZʋGxˋL_jL=XI$ռ[}"Dp wshѣ?6M&ٚP Qi\g gi춲)<忿i 0aKHcJ6k[z9*dk<Փ rJZ.w, ne |4{mda=f LxRihdڰ/iޜqxSJ`pQ:ٱ|\T%um̮~4pD*MGJXnMo1;Mb+C |L , rGmi4P ]"{[mR^ T}_6ۤs|E*y^щ:l"q$9&T֫#e5Uo5|I'Wh"_D0iߒb1ԗI-/3ߛ Ae$*6HttDnadZfrw isu4X;iQzU *\ Ghln>GUuKSŏ<606ue&/3NW\ê2N9d>fX&nU#>糘l*pruNx !_d8[!w^L/,;n탡_0 /n IZE1ef YRĶ)`J q1Eĺ# ~yE+E< YBz<.E_TxYuX'{_$!p)X֯sd˦2sc[䰺(q7띝}|ԛxpaG_^sClMF|5 '*OћX(!R3'Z1Uq,+tl=zJB|!Vt 9 KOm(s_ۥc0*I0^7>TL"tGY1uR=}GMɷ{}nVK!eF^]|œJC `)x+i}մq7J1 "; w V..*V f£pN>YA0<&Mva"<9BObɂ.Hr: ؝Kw:&Oa?oJ?<(sJ_ a *AՎ[8o7IOc& Sd7^FֆEzyjpgX;@7ϩRp ,Eə+ZBS2ҟ>-VƇŢ2| sw&j$t!UBk4{G2-3B9 %W|UB̼ ]~fh\@.:N7M=-δ}Jk]T]:"y^(ESKF7 '7>)BD$x%iP \@o#i$܁Y Z9zkхKPࣹ{Nz33.WN_2#%&p;J@ A.i\ dfdV a߳G+N91Yh|D^PȏBcwܶe =uVRs;wt D:Gz,0]ã{5jıoڽ=LCJw!$b]Piͳ<Сg @ӡGw8M&m<*)}JUUG ]4O:Zi%A3d*aS$ʡ1w+e-!ڿ}G#&=Fd:(In(R-]M )$3v5طdtu"۹(C3{ %Q_v'NzQj=D8ȟ ea޳YR?V.҅Zo'_lu4!"rNv{ ؠvD7% Bqڹf0WV$=o^`7Rêf]yXHdE$XX-BX18Yacgx` )O#R#qW!kz/x÷CًD]@4, *mDr <#:Oo|̂s)36n.]U <^cz4ꙥ g]jl6ᙣ3uhjS0땖0 Eeȃ  $l+^W/Ko&UfU\Y<<'#'.8 ~kaJ[dsk8Lbɟ("O\ dQQ»+N&8WLzd6a1=-_]=4n!;` OjF'K{y .[$ ؉,9!%Ol޻ UOv𾭂tn7=Gp~sx__RׇCز|+b 'Lk.I\$bOc3c/;N44y7bEh+ T}8HԿ*W Xx7|qlA;WЎ24 ۬943H_1sYQ$[Y70m|#7O%{ g0QJe]@'9瓐Bl)_rɸX!,9b9z-혠/22 *Uu=OiJ ybLzVevYfL%QT*C=|Fk| `q\*tHߦܑ!1ʾ?(NrqݧqQ Ӝq( 馤6zRooqzO5:r 0?72Z0thrTz)*rm=t9 z] EI1[G1+ 2yʈe܂I6t'l;%]u/}ױ[j[.#N*):YLZX7j{`:x`1OV JqC'C䨒[~Nt70P5I:pTA=ٽ^%@(Lpn a6 i6ҊڴwIRGa\n&T[6vT6;R㡄45io|*d&N}*u TRm]b"X>ռbF$[;CJV̀j[^!{C5:j"3)SNϸ(yl#np *;cP`QzE5₲2tqetSo;g"k7| (阂eAT!/3rH[cw (XWh[T0nҟfGZRjsjRq R=)#:̊g{egwoV-ḀѼ`$}ۈռa(`Lyd_)X# y`C 13뢌ϫ *GO{_&{pGm˗Wi}*ݖ &w> f_B) (-ځ''O3'Ym1x|'9$"-n@8-p @ >v":MIMʮibW/.{V݄ TКʨ5H+=vXrd#Xc"ASVq$NhxC~Q-N!3V<2|KƬY,[C4HA"kZ NK@|oCu]a*"E𮳂^B̔;5 tU3R-k }69*AE}J=lb zX8yJȼ6 kq]EGbY|> iOs/A @zL1#\؁51(?5c ,,!@)e $OݐSIG 1OwХMg͚rn|/z6R7:h);p)?jam/#T2c_Pt="9W`NOxT:5Օ?rQEhƩ?&R$BC+ 8QQk)iNӝYY״vY*xwķ,Cƪ\6mZSgzM|z8ͨi1Zj !C!)H#Ԗbg6c0tC=#aNڝeI;fʓQ`*@6́#7N5W Bi &XC4&Oߪ--ѰzEN`V S^\G3KǮ2诱-7Y#!M%{7U *z*3V`K2 {J7=wcʢ3_E]F*ro&EoD DO  us7qHqC۸;8"'иld }Wȶ)kӷ7o'2 *`&]{g{ܩ+'(fg0wo4clmVӞ燳" `Ah[Q`'Pf9I~J;pG8ǁf^ -[y^?Dy 2z>lof} rGyF mRPdCXMGeR`!6kj4LJ jiGV M͠umFQ,Sc`hnj#14sf`:w1qP"5ppS ՚E{dg8b7:́ѵ= X7ɀ=*G^8/&V*K›:Y+-Yt}NNYw<@5:#轧YN4# o+?Ɗ5S>U#D> 2F:*+Q:|@k$Z#E(T$U#$1#X j@#6T\K6 b<$5ZS&d)Y$jMz^ ml[* ~^ݻt@!5Iuo}w }/yxsQ}L Vu&n$5>$\Xae0lj % ?kC\-%g]{"eƱ @+ E^o.X '+%1 s/e>! H4nyU@8*]mQVk[-r2cS9(k"ʝ)}&`! DE˶Q}l -!J oy-0=3 蹺&hNIi^m+JD&,H8(1okdfV~7kQBM9ZkL'M2,t$"BK3єzb dg;2a<`@<J(t/p`Pn"ՙ]4}a/y*"4< o9έ59(ܝk*!{xo yЖG? Rtzbo *t+ER\g(:Ȕ30Yax{Ofx `a} TՍ:P؝gH a_ Uf7לK]-KZ!gS 8Q yaȒb / oq+èE}$\́dT+Kod_aJi5=g;V*iջo7o^nxT $pUx5bT)L+slj5^Ѕ<0s7bu qirȦ e.x*<k 8X-PQ%-N*2bk~gĶ ԩVT~ş!coƖ߫{010"*Swq6Ez'VPaX>Z]?Me/_J%Kn&3_UkvݧjE `{0]4Se[D+ZG1%iͧƏ*gF8->(о^s&RZIrx㩋. V ljkX ڑ);-SwZȬnY8m nD>sn⏩́P0l/7s)XçIdRKJq*]L4Z-)|zs=~N<GxSWl~ l}]Z-x֎ >w^qX=QZR9f0+,jquKNo'/-BGʋh*CG񷻴%揃P^Lے+Lluij)ɱb1#nY\l)P+u5BPW!a~1OF23)&Rt"xvmᨗ}K0 ;B;^AtRC:keuȈ+>Ƒ̽1!>xE_o6qe&_irOX91(dMfYl߼YU52}V-N(9&;vg⹷NS});oi2Xvс(y:&!#)<<%s#6it?l,~S9џ,pIe1Q)*e›,]N_0ֽP P{III1#\1uD;s3}U0^ aP-%Y!u6Y2_=@ 9V +FѐxI &v籄T~}>݃oT~8@9Sw E+0hʩ}H[orYZqdHKEnrWbIQYbS^S|VB~پdpvv[Ϣw%]2ݍCe\b- JXHLz sgPKL=dCp]Y LDŽ"i:׵$XT.T*l1!Y36@.4)1<1}efs(? ~Qw~ڧf}/d|LT4k!4ml?hCsPOgA1~hbvN643pD8c#7Γ ه:<͡M ` =E~ dX{Y܌?fq]/@X}YϓO1S7ɟ:rm޸D:Po} jq8P43/ _ZuUMA\d,ee`-Gu|0B*>tnscը[((Ʊ t!c_L̋pd 84P>I~#7ܝAyݜ>trKdEy>nUn\>*=7P5Š*ȖF>t}.|' Fo^R,ˬ 7]rU6b))6`ORpygk* C7eCͱLSk?y\rUIlx=rp@Õ%,_X<Υ$BcyJ Jsǖ9#-ut`-#^2n毓zzgg]H-mq Gqn++.[nrrˤw43o'"eß ȋF'Omѐ_X% 3_ >fAyz͚lmc'F)f8Wk1FoC&W'K!QcvuV)7ۓ'E:tM/SiayX)d;x 601>xn5o!,Ҷ?L6S/xRͲ#WC`!J㲀5-CDi}"eQ uC {А4F@H&q=__qFMI[tDY~Vk?n!]Di+%ȷԳ} ! _.~tBxPҎ>ڂY>N:|v$S.ͅ ƺe\uR&kRR{IIXnJ)jD;~UD]hVElqœݻ8 GW\8U( *(5;q' ҟ=?!?!WQ|3_c˯`JE  <wfՐaK&/EHW&@GhNlXݍ3:Y:sɰ}.X$Ba=`tOJ$^_(XS ٩68#-vra llvtDGaaXi^QКc H/nE4<)H9PUؘF3Rsu\>zy[ׯ+@:zKB}Km朐}v[@G\=[cTY)Jf5fLD!O֙c6l|t"v"P8zW  4Ϭw&tɧ0BޱIh` 6A]i` [Y;WqQ#w8N+%z2d콯&YB]3˪g(XD=5+8Ub@HS'u&{a̽0~eyɠXFAU2{Lec.(%;{w%,Z/ᢉc(eX[y5>\i't7pF洮DH2α̠SMҞ+Pa"x$.fPD= ..]V٨t5G͂GHTcC/%ܺX&b LfDg_|@oz|J(JnON/lDe%T_J6C 4d HЫ_u:䜕nRfDA!+as Na7aE#S}%!VSX?G @qzޟHs 3~40RaU[NPP ¯y4<,8CB;=mxm/@#Mxu&ϑLq%W?/_?gwF]uA?9 `:"fNЕӻ(ͯ.]2C4;m}Y$vĦ9I pmrç#1kIDIesH&)]mJ06MLVKP=R Ey& Obь 1[/4IMk`iK|kUi>K~N>&Hx6LJiwFbt\Ġ [|UI8'x}0lP Uo.@;VKA>'dg}$ BCj%{<lIQFBr/j(h1JrKЛ֚9T \ir8Bd-ڣzy%S*.#Y%УW/, [3YKA[O.ɭsv_n=nj9S}YK a'+4UUsB '2iii.Ծ2ՠ/A겶әF\+`&>?2{ {/F@$msVTLzb(wmW {p'FȒ-#{,փ[k3fHK }#Ϊc{1rmxJD䤩Sb:xh`iXvd \X \zX{690?8Wj&ٲ֏Ẋ[  fWE)MF!I_𭷸5I@,] S&t$n%n<0ԭCݾ *>7vi?⽭r|DID~HrJ(mifz$F5`u؁?/`⟡ܛ4W%7/Aꬾ ')xDd.$ϻۏsQS8,Ō3vcŇ"ۍ;*`vh(8 mHBtpuݤ^~ Bьe,3sx 2<;+$"kG<-l=TjA dE_=19VpMw(dh. tkfjrU6 DwQg¨ۿjrg|XF)oS‚Ā|m>gʤ\j#:S]q`HhqD7KQ\'z;!,RkhhM&R* j0q/iOgTc$Rb݁6;MRcJS6CH)%5~g[+_:Hcr ?M94zkjRMFV&yB P%`-+۷kׯ3=l6P:k̼crow}4UAoy0"jp6d;DLMn#Hbml.š*ԟЈ`Kq%I/ J ,іN4 #]U2}&>nI1čAo@Y{.^`T̖l&KBPu'>1+cv1%%'qnY9<{]X^/kCwq0})s_AljܾMe7՞Ws}rʒry(eLgWg=pzDM-PBGTWL_R'GvsKA1S7YǢ,8H\i*HG; mAؤJpfw+i+p%Yǂ><$fZoKI9O$>x˭}SMQ5!w22+2bpL.̺DH#8d" nezxbFE1ldp4A|-seS>/*OEZk&zt8ELd{M#W52(9g ܻ5Zرvy/8ϳ0?3ȳ~ %Eg$o+XeQYbR~e5 %hlӣ-ZS2d endstream endobj 8 0 obj 191856 endobj 9 0 obj << /Name /Im2 /Type /XObject /Length 10 0 R /Filter /FlateDecode /Subtype /Image /Width 2085 /Height 1259 /BitsPerComponent 8 /ColorSpace /DeviceGray >> stream -:O0,j(_ [؜Kܘ{zuaWD rBxCnj х~Fr$g;~1=Kg_'C\(0G7_(ly4WX`'@8ʄYWFsdS8&#{?.W~ e\CK=MI07v纕HWL~JY5;RW&Wd*]s:p@ʺ@Wf^*-Fy А4^%Cb?yуIafܿR!Z`Aqj%(BCȶ YlrJA)=FmTִ)[#|ITH~ fp)SB9{ǝFv;kBgʍz!$l2yX7V5P9RFN~W5 CA9u!nL6Y&fxð`-~i9TŀA'dd͌L|]DĦ+d?~d˒rSVC3 vΒ%ZAFs)E}UV<8h:e/0 kEpRw~GmQ \!Y㥑 ǔ?\g,1U${$(ؖK~*N!X-&Ut˥?JKG*>|Vn 6n ^ ^sn|*kXT9bGwK(@XMxJK#_M:LQ8JF3r3>^HHےҙn*n8v&%s725Zx(_Gp2Gy᫂ ]rPSS a?% zf٪wx|OrUcBo|Wފ#xEݷ,@.,{dαSd>f+WWƕN8_Zi"c̍Y|a/9:g#h`^XtAbj#UҸ#R-WScziJXSMerko.uAdeRπWk閲X= MC͐rAJu"O zsa1Ҍ2ds̀ ^ FJ?xXA eeD6gkB>43TE:]I- @>o,ރg n+1x"W1Փ%ɣ"i|Nfmc3UزNMTᘗC 6M13/F9f1QY8ًFV60ҥ7X4$ġMͬIƼ*^ ψ Ұѡ4QJ odCCx3jXVU~Y3hէ˸(m,[|kM[.ע$V5b}qe:o$ *vC}@iR1 ҂ǸK2NmYrS'5-Z/Ou." ބIJyWjͯ QqjM;>[ Ta'E{U\M(vV"NkJ k(_"bЙ6*6D+q-gy En*q5_F?BK8!dLaǡY\8FǕcE>6q)@ㅩ{lg*i};GH>5߹F.l4 W3ѻ>]:6d].MQ?{.6BUR)V[K:Ka.4Đ?x_:5yV'p{w{H`MA'~ӤR]QO-&JTfyoq ?+ǖp"Ɇ[ک!)o[ne/+:_M.K&L  (贈he(=$ӹO ϩo{U˭5Y *ŝtC~>+Ov $~TrLLtNKԗ!InȁM  9!ym̠bB_De)|}I}A ;N4Yq*W i˱/Ox<_d K :Š.9_e 8WѯU'?pUc{euӚ(\u$1WvNT"WE/,0r4nʻ6]s4f$l<\hKM)yޱ,He 8<֢{'\K{`Fߔҭ4Ūth[`7}>wu$f˔b_|()!%kGJ? G<ϲs!L7] _IM/kZ|t,~Fǃ.Ma'Ԙ ,JtKb{i&vDJ+:gjwNkNS)?EG`?oR#%(r ٬:7U!KeQ'Ɩ[SoH G4Ie(۾%9xޏOcYfyځϹ])~A[Vsc.ef3 L&Ӭ1M]t<%RGA۸X+x L_)ߺ.Ňt{Hfw0Gd7(yx{<SlX\R!դhZ}Q͞8ҧy'|מ2>Xop Աo+;oX1,I4Ci3 d]Hj*(Xc|PϦk'%'Q]$9 Af'Swl/@;Fɴz\`;/~|IxY]Ǎ6Ò #iÛ):ULs~aU)qΑ^EvPhOޚ>oS \t&0W`Ox${-oF ox"0MF.Tx_"/_[t(^烷 ʔ88wmwE5cDF7uyO^Uf^I%Km=36Ie;y'1sJWv`[ 6-!4X=OFHK3+[Kh WW 7\.JV- eFMt,g7ulբuHmwKmɳM4`Mա2Y| >Ax1^~BHG`MH:ˤ@`MBsJRא5w(nzxiN[A}&vA,{nƪ^ɽOBNMp8W PÅUft!ӕ26i;Nҋ>PSok"¸O߻D6"%!)<7zc.֗)g7YITd$d8T %;y`N+:zyV=Yx9b VVn@_'wqu}b.gfp!BpJ,im徜k6!#8ڦmuaWZ]Y??k 1Sjq. EA\Ӏ*2H%~aŽbBuV٧Dr\'뿌9,8Љ*\FI:soMT`5L'@[:0RO/%slp3"DN܎MM tee+9vaw/ :U0 $B6F\iyNk{Jv֭b';Z,a>iʼnJmzGy[fz0ȗ*m>8(ep!YW- d\;و)F|n'f~"F;WMYSs线w#G66-{6n21ϴAH?h!~e1RL&ezE[?4AѓÞ*ϣ.DxYT3ҭ R?.X5'lB+LDC=7cg+A RK.K$'cYjcKnX\T\a)vZ)K0}@T|U0 R OPedt:荻k|:'y/1VTW28i }z5"g˗VWWJCylMUVDcaQc'{j:؄6cgl!l=..~[Ҧs=`265gDtۃӟ\bIw{Am;j{hY/)SpeOS{ KI}̅{:RBퟴ|«3.ӑ]`YV D٥a ӇiO]nZv_ǒmW.X$n$):'y% nu۴Ա j* EN'4(5r=) dN*~aFʈP3ky$fk0~si;!Q]BsGWB#^[rM!86j9_4h N՜3snVEk㌑R*YZ`dÑ];V[mRiCv`JcZoD 'rᗓUޏR*1G{?,S˙NuX3jrI u6d$6S("i1C>Yʶ'?MsMGrp䯍l^}Eq{A7yfvYqXI{Hsd'SJָH^nᬥ$-}ݷPr[xZ {{꼍vdn8SG?h]P\9tvz8.)F?vI5P†Q<%|\zfr{}{8uX%K:X:lp̣}$vRp*O2;!`@]n4'fy-aTt8U+#r&;NV4Y+Jnkkr¾ tG1+dzt{N!`KMG)`d"M@$E& r4i M_{cO+: ǥ/?ۉޕ{8,hE(8&hMW{|1=vvua&|Z<'x7|*4v2\gںJo09ˁsdI~!K"IXbFfCL.[9橂ke#ԩ0Ie \2$w'^j+Fwᶅ^A6V  CG*#&(& 8%t)8Qoqk!_cR,hf^]uZAs:+|(jH*†V,Tzŕᐦ t6M#p9/)GϿh,?&FM0-ws A&6@ȹp]ΜFt')醤'J߳r>p?pTWW =-v%kϨG 9 Z;)VMjvR̗s*D?XZ~~v˶;XYm7U& @nE" zҳ&YfHFwOfGi6Xc##4iG|'1)nmE]m`!Vldoqn ]Lʼ#`(9oz(OR&UnP9 Q_ƦmE^Ây+PWq qV:hVdP՜Q%xK?!LMc_/?q8myrG6UvJ,|[" -?"`0 Oe+iSO22 Z@vN'}@3O]5ZN"˜wsu?T )&1תKDКKu>{E'B" :b?j^!(1A=60Y} BZ=f RTܨdł]N=4 \ɲkpmS4>-A2 2?Ͳ@/EpAT=Q܋ 9clǼ8Ri%qn'ͬXMwIśk$(`+׏H4nl`Vx8QI/efځ1FLwKi >> z$crw[ :v{pb]/*+ς$ҴV.bl$c0{ a/_hxB}5a l /!=53~zJԬu5Jq8$ϑH@]iAB(ђm?e~ %w 0z-QY3> C R sQo6&z(db/.IǕhZ6L%;G9߃Ez/"0|!='w )'rr%W M1Fbhxf=˦TBlR+ Jȍrr!`LUF_DoC+Og 8jYZq6(r8o}P73b}p{,foҞ/޶4t,+#UIyo0ϛ=(Y_yΗmg$P@.uv'&>K"!jHtWJZ_<$/ ^<@HbڱF:m(nDrb+$440HtweF'ostgO/eCn45Z4aDTY0(/2V|D tw>A#M9nfz/ mU&xLī&V0S]qE#-Yy,3Sx ί6t'KڨJ-#r^O:i ?Gҫj@íI'1g  D_^rSk)YU` ZCp#C*5lՖka'—Z+*cL.Y֚)_Rgy)1 C|rbb)Q}bi vݤ0v50Ґa;L-VT XP95L_spl|t%&Ub5!&ȦۑꁊWKɜZX]^Y($xh'_^s}pvs 8pzq:iJiehB,֭Ko񍬮x,Ѥ̟E &5DLXj֛~90w{P+m=ɲS݁ e+uA5B ACsދp-h0ŗDo-3}1)֔0|W$};QC FLL׶s p` 2=|s0]+Sfs0VԝhMd50*ꔮ0O";i\Yu+Jc4l!' I?ҏ%0_ɥ6/nޕ5/\0S$hq)7_OsoV"-9ԱQJ݁뷸}bms+K$PY`~#\nIN(k50TRj^N6wW!Db:{ꯓp>ե ~)2))cK"~c p8v<iVOaP߼m= ;|eι#A6]^~&^f:J7tV8^9п|jKTofIf)xlؾN3+ #F?b[5<*l]|皛 [޲9[|뤩cQSҹkt@Oq1\&]0Rzl+DPEQ88@`(ˀDJ+c\Z-6(9sR`‚`R!o a>FlKZ _kTCYʖeN[?˔)]URjRM H@&c-/yɜ߮E4orƌ]c>ZHZrOd0E`hb2A5h7ת2 7,pĉg9X)5$onНWjx0@ۣB>bCtW\!b<0mf/'Mz8;t7@RO_tXHIT챢GUX4rqAf5Q9fu }ťm72B֏5,FK])` Pճ_lgDdDn ]ր4(. ?,.kgj0< FYl%e K1%5mο7+j)WD_E@TD6/fJ/N9onJ뉸{5G~-Zoi)a^xٽcPëe&EPJ5aSI8%;nq@v01 a&*Y0;rtT!Pˆ̖қ=H<{5COL~%8@z*эxK?]?8ҺŪxi\\lb":-(<یbo滻`; RJzBn(>b2UԽGoL4vb9TGkZ`r2x6CKŌ8%j h {RqJ ,37L1AG9gOyqб '(<-YvCn]t9Tˀ5N% O^4gjXAE+?ʏIn\ӿ ̿!Rď~ƪ̐vV͈H8 qjwCsۋiǮ+]5!VLlx`yf"ȭM-V-!_ΧF]_z d<5lRC=]\BbU S:hl(GaQsiZfKjXtރ/^S_%j؞j_$, 'r0ܙp7 "Fv6|'ƃW5S,OB*n zBaN[wZ b5TXoi ebɽ)os+4L|=O>fTOZo%H5u:̫b ,(ԚW=gvOKJZ˵GRi~Z*PHZ n0 UK_PY5D"LWHxΝ]0(8啿j!ŀ1w NɭǑi%q8_V]ADH̑ >o>r<=z,s#Fg$u@#ۼ+͚@kd6GQsO^`Pcu1 ؜ uViSʗ: g$EF_cuq?ޝv%uGF|5|Gl#&*~:UFEoWU;=8'3[^ZG\Hl8ӿcb|Du`x }X99akhjesYϒI+YλrF'UWgbx@\zq3"Z(O8+|c(SjPۑ!CV |2}y|CuPMsG+ϗPDFl8LrOM73+I(omC(~n e@'0@To񽯱?m[--Үkw+ϲ(?)?㤖i!8Dؾp5ߩD99tH>&8aՆ 2߬/-vԞ۔(C'33aB,Rs'>NÔ}K)[=tR ](g7gf6L^Lu^m JGE-yϺCz:\WVJt\q얢DE[ /󮓼zD˺a/lZ=F53#"Nm U7&̮lki=XkW, i*!aΟb߁M\ .BYJbT zx&3Qrחy @TpW5=(^ۺ;#>⽛JlD.E¡a:5?·7*\=v5(rBlRX =iͨ!{y z7j uuDw' .դ P܀}fKD2h[CX(zwFi91 nndGv(OɅH;w%f"@!oK,5?Fi#r?@G.@g ac*X 8&*β-Z))u$FmZ.gEVg8X_SG@z(!lpjzm V३rzh{ER <䱪%;X>=85h8)VeH ϝj61P9E)/ Kl釗0>+Ƒ@GK||w6Er;kl*OsȩSRzv1pNA&iT x "^c G  eÔ4trǭgՖ'1L'28,֝)y‚HnЅGY=i5 vqةRpF IHώKs`+E X;']׊ڊ_Ǻ*7Whm{$ v/:9/ݨ7iLXs4, xwBEȅ'ggVË\}=hOvPm<,NsA*53z 9]B^y:~UT\IЧ.$h)(u*3h ٣jT"T~B@p!/dLihXG.x/;ZAgIu-J V"gMDֽ|RuJ%|g`W,-ʋKݳC6 CB{}ɤh}r2h\K=U(nnj )id}qb9;OfE_4T YA/}sfBh6m[ƣH)t`v75|O%'pwc{.8̀VQҺț~wKX&_YDAn 8gnz=;Ŝɐ_MRѴ_Ҥ P# IJ0Iy-x lj9pXﱠ q.u[ ptRss]r.BT*rGc/J2N} _O 4-aƁ}1ZR03XW'YƩtR#=TS|MdRq^~[jd1K[R] &NcHcrvl?pYE٥u.7+lC: zH%Ez>bT"tdg4}d)ı=)D!^mgY/ߺ"%٥xgTˬt=5?ϵ]*:)f.S ,LlHGWQ\LQΡ|]hZ)MfCC7'F7Ҋ3U>p2 9p,P-pe/'P̣^-D,gUj9EFEʢpqΈRsNgmeȕޚLZ jC%&`>4e4ucO\Z|rGcr@J [sF3JA$~-$<yD H>J#6@[F4t dMւ9ߴS oT,T!˛[jd~P2@iPiJ_E,uc|UcWHH a!:"0o2.8?rj U㲿^ł,!\4Qj37)-Xt||%LVczaƁMY:'IXnpsB[{^ m;ulq[_ܕs >#xԸ[~ݢ:ضRij ~m'n:+iWF 䂷ZyO<-%s݂Bm.6ʯ "$)AXZ.î<:#OIǞ/1ۙdb6<˵o(t:ax:^M[nLDG';  =!{5w*L}h9Gx2I䄜a:`)xЄk?{tI%j(X*98g1apriź]+s1A 5b aL Q_B5!aƯhbL2)cYdkц"4UOSwa32hibk3ϴ98~K\DH~TQZݵG}X{{UAc2Q]7qVR&ğ4 AY$d80no$͖6aY So~@^* sz%y8c kV] &~Y0s?WO00odRH\s %7cGmZrJ-ý#_2D7?4c^ʨ\? 6y7] 9"NB"[GoX5}S*ݘ*zg'"nWX1p,.3Nba?xײA և)$)O]Vu@ppy[T6FAbwWV鬒rVs CAВ0WG1*x:i?hLhEЧ4ZIyn1Q_+&x1وLATP|Є)Ev p(JY.bT:㿳]=Ys" VuvUBJ `hPZ0dnYձ#(XaĞ8tf(;kI]mƲ)PzTx0DFFzb NQBdfKa}L0炘s{*X[rO/ ~;&VyC7hnnqWÓ9@'܌ {rLC7)7[p.VH{cgӱҴO.Zf= l+X6֍Q8{=¼-+l01ӡuc\|R7'ȩ>{ TFBJ{+44Ģ5(WN :vQ=mZ]w8 g/~`)ΠҪG!LfgrzzCư;@'Y/o>̻Lbc[:~ݱoQueGp]v`pe3-n VS(RfŃ\8>T$nzՀU NI;\Ѯ; XMq;)i]c z8t,SP\K&{4iW$ "cC=G8\>dGLjEmbD/bK)b?F:yn*\KRD7!_i@,+Ll03]g/*B\=ؙG`).n?y3cJvoN,.)"_󗅌7n--]z4NRCBzK807s7}?TLΒ8i)NNգu⮯x5ϙh=β/o:ڸ3y@B4FӭDpM)jq0ݫ)R #:ƈ2:q3m\e'țcr[?!ՠ:$p7oon >Sn=9#"Q`m= 5?C_;(˱.˟or&*'% = vV EinVTf.MC9<92MjWcƷH0I<.lebܿ ELf Q;%us ~}S~(yl "i^v?wy?y 5m@Q'YI0F=z2m52C@Ik-$g1L< Rgoͤw=fG:6J_NVh QK)*">" E)4Ώ5wJt?zvA*<3Cx8d5X@E|,(Glg c'_bK2GFO. q#)f rOӐ> Wxws]QTq TS)BJ'RVedvgˠ=ޭ!_ 3 E"BEZLl(%܏XJb9mviAuLDJd{luTSVE +޻!q~(v.?S3hoNSeGLRfHBnږFf|zZld?x!j7:0.{^,9=&KjPZ5R5< 8]o |og Feɖ) j~+Il \*VC`[Z=+4ΫoT׵6~Ԭ+Z<^T[o_H} UY`΄9:R5}$ogC->8F)-? NV\\x?uUFDt0kg,;(sf:n,%S0Iy/VM9c,q"2m:rZ=[~0S^[* nu31VD2ß9L(͹!tku~C*%^f" WI9i:F]ތzo 4 eyжF # ˉ%0KrT`踺)sD+eCʖ{(FlIS->)JԵ6yij;;^dC*4j 5^av^{@ vbDv%qĖ0bK#7QkTОʊiPpa1PSr߲O(qA֛#fSj#0Cv(C):_Mrj˿ 1g/ċ6`bSFJTUY+=G[[KuUK'UQ9H@Ƣd:W4ǵcħ@\4F6Yrw39&i7r.qE9z;qʈ[wٸ+ -NyiܤEQuvKu9$Vbj}wZWV:;V/ /sZ%gM=a8w{h7$Q\aWIaFYW:l7 ̀e[#qmhiEVE^꿷IGxTEPS{ iäCh'&<,avPlu*QZo@ICo$ߪ LZ$EE<ߒ_ ; N`N/a hn\,!_ ꃜ5}XӾG"Xva< ^2[ޮʍEwTLb$x4SYk[u "Wӣiᗩ,ژ\z X/jZ%;X 5h1P "l-=@$ B*eu8a%ㇱy#a8X"I؉odJFzS^hd| g l N +I|9?1(~w=S]Y"i X9'M%xOt piA p5:E[1 !5DUljOAi*`q}Ѷ]:=c^L|42؟iR> bjpsܐ>u d~ろG]͊Pydȝ> E'2c |xήל.3}BNvfWm1< K Zp37x`gQ9@aj>K *o6d2?.tCbB玣/]陡{&eX,>Kʄ5$`!'S0 .TT-K]s xT~^`;bٙ!^aMV$PЁy TQ*o3яWc1sgj}GzI /ەlEE:V_8ۻO_ zkum=lDbkys?S0jzch4fXȄnix{5JUW6q2:X폣̧%[UeF/xe$Wy($8H3gj2{svLr@MZ )T,}Ck^LQlc[]_1}GD ϡy__i1|%L_b7slo5PSw?7hE6,N~TKmVms1WCk kvpuVUbX7d$y"-k0E܅JpƇ5O}@U fl.vh !j T-hS":8yC9wĴN Tc ґOKAk!ZMq<;;yɹM)U%t5㞟U\OYQ^# [Qa]=p9&[LMQ\dRD;~=g2`oX{W{zgZ[a.uo#wdEe6R#k?Q6P]c/j(a2h*/퍏~#m\ݬ`ԸG%n6r-OTrzK?*OL;muSјwΊl=~;)##_aBNːi:G@ֽN/! WxH=%saݓvX%&Vmhdă&^[m;@/.'N -uE|vi4cqCwg_8nVUŧ>r%P>aO] jQzbM` kh͉[DCK:Q I-Mݗ OBeR?[C;8 `i maEa%ֳ\tnAc` 9qhO5v5QȿN+L= 24qn)_N-*34ՠLm+wC>M谙P? 2͙ԊˠY|f.3"zE Sc1޴9F[Cjۅ m8nfZ<-R % \Kº( QXXzpȁ[;2K`oC2~7-hМ=<_@ Y{7.NsЂ qZWAWO 7kk9x’b ]#bfm[vx_ND~Tn&L뙕TU;FmsQR3ʿּUۄҚ-mOp@{MZ)ӂr'0洸Ou @o U`톤/[3Ed nvF7Ѓ{#%9}9, *>+z¤1Le, GYF /-@2{\LrAwXan oNKԇe)*h3FDpX|5U5Mm4V5j͗tcn+"t<peiiֺH0[EqI@ݩ)ryo`b=9/崬I[T^J֬ܒ`!a>R"Js`Ҟ;|z˃OP&f2 }[0I\sn?K?H BjmGW X!4V7ڦ5HzED) ӭ=#Og85;<#,u?4[zE^/y3 phdž %|\09 L0ín) d2uXffF1J *w$P){"A%+ t J[pi4 *pR.ʱz)v̍=P5N&b )=PB@KN<:ZPThT*a^n$bp{3:yLWkrkxJY2]aYMO/7[/N57vSS}h,@' H.;^6-aBScHA&VNUYSV1[ɯcu /.{C"u _l@=\fd]5<-R!5ib1zca#/Kԯu!q0Poo44z WPBo;4=B0rYd-"{3@ Nֹ~1 sJ ttU`K8[ɚh˙)U1 V6oX%>k"RQIc眍7b߫ leB\ |dkj#&*e5LT\y10Z4Bڦ#qδvpC#{ee0$}va'gKUhciuc\?xvшmlnӮP@8ro&UD$k  Hax5mlTȤބtAMm[՘#,`0o8* C*pnWCͳg(rPvn.6Mh_=Qzi2la6y4Rbt7%Zy~ v -AK÷Y+I[\]~V<,%8C Öܔ\QCά;?M#%AIXU֧ e@'ȖYW m#=3@ɒdR:3>$&yݪovv0;U_%֊߷rRDi2\yC~+CȂ"h U\j@<_,)Σ= $o݃ ՗~MNcX̰Di8tSw(0={cTo}{YOJ=0 .!+=*9yα=|kw:@-x}':*sidzqcQQ%RVawwPA $#6NTQ)&d)Wfi]i݉Nɀ⋿WgXv&0h%'3w:eKdU9T"?ƥtg0K58\PuÁުdjƛ䔊%eJ53b _:[xV܀S/728IX*;XƏQ1k$zO)ZoP6;0 WK2* fO)Uz~\7YKUUTQסk`;j%< qsVD /Sǟ$vFcw8~1/G|ni,l /⛳܇9kw6Fn<iGOX9Ѡ.pY;Tgѿ#Vnb%WM;DSB|kf׹pA$|fM@l&dk[a>\bT߄+ɄnEc qzko|2uy|pJw{j'AH^c lڗ8@@խPfCb=I쀚gcgiy@S+D :`5Xo0` 2mi`' n}j0\jaqa=yz0uUqgmjL  GH|kDZK- ֓Dd;wba1{o2dz[ ^7kvqmѩ֡WZA/ʢkqeS oeȢy ~] tTj[~v5t.BD޶&NFm8T[Oĉ620Iͦ/!Bc~n'|R@ntݧ˥ږo>m{4Ubo&}fYb-DZ7DRMOxdes# /8xZˇE-gM&/Ba }G2mfri;jelm ~h[&6`0r ґ&ܼ-OAh1S5d% e/H.4|hӄdK JrX9*Nmh/ =sBٞ""M$hchqаD82R"*)\iF1K;@;PS)!Ftgq}#?fhWs%uģT#[®zEF鱀Hz0an^6dsGOH{Ɲޞ%pZ܈0 CW"ݲE,ֆ&2EwGN~<:ۯgɀ>[?LU(mdN)^$XVX[Krbay&3}4 #8iADWw9ҼcǙ3a #9Zogw73j pә2#,Yihi;y*0".ǣF|=7tq"3A0 LIGi1Td 7eB БiɂU{ r){ucO {)j1~DڧK 8V܅:~zN*h @k]G[`1}R]v-p^^E;Ok4ބZo?U@5x)wopWͧƽ nn_tniŃt\x KmUKEϣLv4HX -<'+2-#\`bR!hEWD|:8p_Zc$}b@Ja}L^#hSX C5y(dFQ _E#ѴW- 釃e} i14]nO",dCd{}Ŏn&%a}8j/4H%} ExkLH`WH2='Cw޸{WNWoT4ƕKEt|qp[[hH:na#a'bD-GwM|8r?]E?̍( qbӬ|@ uN -N׉~٣O䕺; W#^]h_tٽ`N4;!52^t1kK[ KaXld.4ręv2^!cלZKi\*!NSY94'{3G!) |rعWHydhX(xx]R'RG iR`o\H_K@>mNI$x%k%Sp`ը!L^}d6֣)S}f@,95k6#( p֐(x01ٜȞ.K׿_'ɂWO_wMk4 $~{ܠ_hsmQ^, s-9I摾 ]ce 1s"<کD!آ-~4ou<1ö'qzN #ʣbl'& ZBa ʈm / 6%Stn].7U|Ca]%"Ƞ 1QLb%u=F`- S{뤿Ñ >>M;h ЅݙhXP6\d.QP.ԠEuu &idf>kEf;S1+3禞'SPҫ1k)AJ& sF9NrFe|MF2NϷΚ Ĭ]ljg܂<]/ L2="|յ&$6a)5Hj^:e"p|:)Ie..{*%Eޠʀ.N&&T`7WEHUI@^kekdK^Nvٷﺗ!1N$0F;jugsW`]Qo*R0sdr xhdZ' j )A|yv*hS_Zͼp3ƫ=W[dvCcYWOT,w4nr$8vgaK6 |L8?r~T#-R$roMO;\HFypʚُ|ځ}a:M[q)|"`_\ۏS#>E5gJFspbXTRDwmC5 9eBw?-wy<N׋z{Dʪ;Lm%c`1]NA7nMݝ?FdfTS|@G1wKR[ly-aK&%7Z< %Ő|͌X8 "<14 X(E4 9ŽVeky?Fc, I ]:']).,ux7~3īvwRe4NF[M(6t7?5Ϩ7MC֬_ "x4ߦoxOxM08B) f O c%%h7x0PHV<琂b\dѨUCvI<]_]|}ꐫA~٫KZ\rɋA&M;wV=H kLKаbd7!P/L`:T@0^!s<7s;U{}LJT061"o( 7VJĸk^I_G!MGWsi])2~@ӟR=Dk[IQۓg8mAqQpĦ&V#s7xN9 D:2 uuy6L#b \x}CPrYEդqX gu/Gb8E'wQL&A3/i Ss+kp4L 4f8R$ϣoQ%G'/ԧ 'TNlrԲZKQ*iMPD\\jVW1?Ӏl Sg.Oƒ"+pmBKDG0^ݷ4/#8,/c(uAvt^Lf23O[p}ғgZѾ<>\[^+bqKD׹|=TCcK=Lj{&?( rw\#,e+dY懻Y3dzv*_ ѓ(N4o9f^d-g 62S/ǖtK[::lIB.bW0>Z⥼y` 戳) uk!ENQJteaj'̚}8Nb9 i6 %Ӻx Q墨ը {:&BDjԯH[qgA4)MZHT>M:~@Z':һFwMTŅB8#ӣ½v"#%>+7zD2 09*phTLy\nfy))g̱~HOV# 3('Ut*ciF2 ѣ$ ڂHERG@Rvʯt#py1tjw.8zA|yD$% ewd6h֖&A7x:ɘ$;6  |kS**#b!%0˪ŏRᅪ0h?<՛,*vϤ{Gg3\ǥo!D]({0ރ8ԫ@%ުƍJP^jE':.ĻZOTJZU&'PBۧ p8Ba-i }#,sHTx| u ;_x&U~Ꙏ#2`vʇVGc$-p>K.RO]MZ9i .qTku'@lHs]1OF,cT@ ^ %i[D\_<\(r},2L Lݵ>ʦ$lPŽ?*&!"I^ %9rT- Wn!ʛJ8%V=} e/ Bb4wJBsI'沾,8c,ګd?ihB'-ձ&uW“qf(,瞫(WRw.m*x]ћ#pq79ОrI\sBC~|pubeej$RKɆFJ ,|I¢vCo|.)^څE1k?&8!Ka<:2% R!}ۥe-:J b:Z^ F@Sm(cl>dk_BHJu'`,Lh;.{jpgQs)J@+ȋ1V dTQr/HWLͭ'$>He9j$Gg՞vf#HLi0.6~ K\y&WNJLA ;F[ :n;5 wi= a4aѓczGv N()r(t"pKJH&tvg7ifTxaR†s`-]1Eyܭz!*s9HnS`)Phkզ4DRv78D?&c [+ۨ#<.m&!0.o L#\>Mk[ƈW.SϑSuCS]ey]?%]7ZBy{I]e|@BЙ+nehڣu<R'&C@n\J ~2+YAP"l7laJ %q\8 f@k^ !Y9^M{QvZDUT%V8ܑly"$SMɂxNb"/$%#=*Xf'Ԙr rXjϩ- E `s^.F AչxLjҋq`֯TŋdjGz~GMbGAΪ&&twݨ%6v^Z*)&<\B]2Qs@h0]}O~4˶D#)=_pQqD~,P'>jȒy{B2Dr(^d*&H_ Taw#z~6:Ń$k@Yz  vdCfiZsp@ Oy#)%)w`zno?s8/P@1SDS'nxs|ʂ.T OE vrL%^!(:%эQ9Eh7&_S%BaR h%?fy[n-/m.~bN9cxݒB ,&r~CztsvW.䏱pгv"FֵZ?;@  q`K{0?tU u=sw^ WRM{ʫNO>@OoبUnԭV {  ݉4Ppzu $I/uΡꡲuEpg2\V.?f{jtP|+d7a~Ӏev‰l(i&Sas``k^hU(0cÓi PT/aAa4U+O͢WW DƈSK&[e"Z>@(}-$wTVt#ZqR" D0Wo:wU=6p{~  O^/HuW[ewl~\ =R*V]rQ "R\e]UWBqMG ]£t.  n,(Ͼ2a^t:0prgqAowe<5#W/nopSsFͻJG}cD߱5+9q(K#T :Cqw:LVFw<\qJǺpWk#ޠd*\ 0r%JД$uznH)k2ʲzyRZ>]qiriu+W[(bd_q{)`vۼ{S=$a#U@ع:F'Kp&^\[Wt'31fQϿb{taD R*`Q"?||.PnhXEBSuGT. 켃@wY˱yl倁FCC);U)~T3{ tR\eYBMQ#P@pV.F snY-0eDsB,Tg'GdزbǰjS1_RYw&ĦUE_XZ/c^ō5ĥOp41DsiuwltiImbw] P3V?py*{Ww˰(vZ)DVua%nW!'+ڼ18P0 ci] $mSngzcT䆨f?":V (2l!-"=1$TVT؛^I3s{0zdX:'e-wQ iPjlX̊ŀ[a돾{O\f9- R`{_:A\kNSͮ;j7l &ik3aJ"!Sn"-AoޏBY8aY1ר?z ̕ԊBx!S懼A pQ $0I.RlgF4WgsoirVS)cFpf3gâmXˌ:6F*=?w` [H%/v&/l@uGh9x.15`jdXxI{Q_R~uYLu~ [!L`\Hi_8Bq J]^=AM|k7W$,ejnν~*Qoj)<Z=qJ^?pa=]iDnv&$$&Bh;L6 f{ǢI:TTLr`V$KMҌe2Ϧ Ccbւ5ŤVB`}r 6OցMWyEI'%ϹJ 䈚v۩c&=6"J+y^5]yǮR%;eH|p ZOƨ%#Ғ`ӷ0`7 CAKN`>rUnjϥא+h2xZIۮ=yhh>Y ݲ~ma4 cHGI%cwޞd4LD6ldpֺŦZ((瑲d_bC7h OpT|墌)@r U3vZ\OaZ{z~5ráT"뛠q@" x>:ss \SC!zJ#|es.u0i`'L_vd1 [1iٔf\>g |GHb)4|YQc$O|Mʿq@l&A6!o n$ ;:€{v:GUV>Ҹk5}I?B1@{qSbá2yuj~gdZI|Ҭ;cDN؊ر}If&@wOMp]eo4ǫ!cr[Ǝ c,B!ne9! 33k '-d>H]^#, \]G-BŒG=Oe/aozk]jb|2ӡf6t ??r\9QRp|ٙ!O{I,T8!aA YdSlE͈>V1%(`*WѝE|uL6=q*a;+ަ`efB쳽ij2H\$NfxѨUh3ؾxNոmc#HU=JJ͚7f# ,.8=Jl\NXrel1ram'{D\G ~>rJ.yړ<'r6EIt{>?WPw|7PTlB@ %?6De6AKpeud⏯_$:ے)f3?w@C>#bٚeRZ4p;V"BSmSM@%3HY2n1Y髨YʸBނ*zjx@L Ano"h"n=da%v2ᱎoD@!".Z.Dcd>mm $^Hݽ6Sl *crՀe96rs_li L=X>(B'Id'S)[tIƏ;*~mB Y iy/&1~D\PvѦ5 n9ȲyF*;:CEm@F`st!IG5jV7Ox۽LjiL0*{tܮo&4뵫mSx@KWl\Uw L8WX`/p9u&H+500H6Yಠ;!Y_w,3Mo  0`b,/ }@0׮>LuAW>daIp}o$2ƨ6u%%!̈XP6cţaQPжeu=&R͝ b|@~$#G{j)h%8lxF^a-0t'c#nKDv*BbW|I8 .9Q9M;&11a}oVƺI6E>|QgUwDFy)7lj+Ou6莙(d @{Í=JOC+ j*!$DZZO2;BKQan, M1h<ࢍIf<e"™4yxMiD_KΉ6]o^P&VUYxf6Px!eo4Z{Y p\4fgp(@(V(uU*2I[$ZOL rqP3LZ65И s`W0}ej$[ nvi鿴R|[MnãM՛ϰ@W09#bqI r (ˉ}StDWa<^cIah4'vZ C ,f@. d'iۻkȋʇO/'q٭SIZ/!_`A_M20/D{b<GdpM[o`sP~KPC=GQO_T`F1Vʻ 6ԡ=Wfpycʴj j]}!hXQ0o+RSkD] ,6SxGr[|hBr"?Z ə⛙rJy~kZ I޽t;5;Nlܜ$ɾ4sCc>vUُV‹DdZ8_*ޕ@sG 㱂kF# w8m:b'} K<̊%xUtCd='fU[7vS:f" f IA.1qAgHX]PWk«'`,#JF!Q{*nL1˯<>裘Bg펝F~'dzN-#W7\i*Ѿ_3ġІ9msb0;9إ]mǜ ŕ_ͥXDisB,@X '<"]sZM]SEe PbٗmNferk|mB-0by~js:~Y)_nPh"3u~TU&7U\σ=;"ʰը. ,)'9B,B]mp{$g;@ ߃rS "H6IԆsQ|g?-N=G\~qlS VsNIF+'@sCR"ym&ߕxwj7Uh3P7=Pj; fׂ%2+ywRn;Ŕq#vacc?/a5Lu/M>P§(9$ 7&T|0jhD8Hl.!4k`doa-0i*-uTf6ޜE;sq1Z˯fDj3% +]B@U|Zx1BvԼmO'D'%אٺBd~/S`G ̷(z ?SvSuܒn%ߊ.G:H:|qNChICU 뒉ilASbȑ# XCu'j"H%LښV ZXhRh8/^۰5Ĭ1Թ][P^WS-tDGbO$|ϫ0M#Az:Xf1EPaO'B0`9փBc65uZްW0F&9FU<,'mkx oRxv<=ta]2Uj}d @9ElA'M$"{ ]<T7&X(v*+rnI#X9.T@"iϖTe Y3Yv"wlnb5O "Z$XUx=P}veOW] 8Յ&$!Bzɼyp_I#o2$1¾6Ƭe{)|F)YyLSzHvV2Qult 0Njθmre=`2M03!_3F鯗TLc-FGsCij{ voLWE,(LjvA%lI)VqCmx[ۆ8G i[.K3I ?PJ!)gjcmN-J\E<28 㭊Z[$~Nln֘- {S&ɱ"j҃[ Ci z =uRJ LN;}k :۸>~"LȒ̿?9 a m1VO`*tZLKL;JݻV< IH#7+F. dkzMds1(= sUHjyel'Hm%+҂?+Y27R\UnSspW;+t; 2Jw ~@'{Z\|hq3O8`Bsvb>‘i PkYOs#2JFzԥ#Ń4 8bS_mQGJ<L&Զ^|3IyK81hFkE£Մ|~ŊLYbǏf=jRì5)IY'O4`{xĈ e9{6%bm~ϬI=B_$7v &lr|'C MIwq5(@:XUpM/v']JH03V-~o[ĥY/UBDcAM/)qUAsN; b_kQ?>Ⱥ;3U.*z=BRT9*d\٩07E>=㳗 \h8,_~;'ΐ yD&X_vBwg-Mc8D5qSڢ^;ay[ &llNatx2{{i d0Ѯ(hw^Lq{GaHũef] 6-JDp}{MFM(OP~2E(12vؤO}osԳh֌<.FYĔFX_2.@uwoFם|zK=C#?'Q3-Wzv/@!vуGJ- (3͝a~R<&.L5R"^g]Q?QD|S11YsnX _vUVX6(9Si%3q&Iq/l'qL$庭ng^w5Kv?NtKG'AKWVXQ凁l f}fBc1m-ܨ endstream endobj 10 0 obj 46432 endobj 11 0 obj << /Name /Im3 /Type /XObject /Length 12 0 R /Filter /FlateDecode /Subtype /Image /Width 2085 /Height 1259 /BitsPerComponent 4 /ColorSpace [/Indexed /DeviceRGB 15 <991142414729540F627D521F6CBC36C9F567F764502C1644CF252DFB198BF6001DE05084989330A28C54DB5FB0887623E854653A1155738E50761B9161C7D96C9C7F7F8CA491BF798F92E7B102E7EF57>] /SMask 9 0 R /Mask [0 0] >> stream L] DD T/ӊ3twdAՀ*3F{Dі\`b.*U-l@r%eirRc}]7:hĒ(۴>$\JW >alI2H_YʬX!~~4R1|(NIdʍ`^-PRb-`A'ѧxJu[H4vm06 E̚*abëYUTPL^I.$@<4cA5 @6ה[NcqClt&٤tl2;NH^KxC w%}YBpyK"[@GEyM zAo_jG2 #!kѾ3Y,_q i vO> Ib|IU#%NbpDok* :$~B9Y#Php ܍b$Va̒[1‚UEG?@GM\e $"|!LN(qV`7}al;1P vXEJDq~Yòj45Bî@j!Op4rU'!wL~=Zg@ȂMNqH1st;oقYB|'ވ[%(˓pz>ԛ^1³1g(*f6Pu%[=L~]Ҙ[%w\]xoR4T:n poy,Uի?H\Xɷn bUPY C30c٥6zEf90·|o)JA+!CkR/X@q!u~y{G$H` |:dN&#$1(mMơXp]z˞Ŷy^ԩ_L=J!<[s z_3 BΓQCǐEn-L`i|8FT~~_a R)_1KR컏,`3ҙLEOxq)|JF̤{S8xL*yj[6k{|YUfV<6̛ GKrZ]/ of7-jGzP.4 2QԩzpEu'?M!fo{:YaΕ]T!A>2ǎxoyрO誇 n2ܸ{zK0c*m=5{)^qm:vQ6:` /Բ3|eޅg5fSf zD=.Dqf L x&$A{57Ql u[~XU{"#?HPp^Z)Al|J :FBܺםE? ͜Џmd u$U< KҤUÛKeƒTSgwj猝\&hjSp*zX.SCڹ-$'#PIj6b׍܌>0&S+ p;U[ Sp%ՐNQJk@\zd,SD_A*ST 꺪A*ˮCdȽF Upc/.hF ~G6,RzG [#QoP{H$I6*Ve`hju w"@΋ 2"})Q5}VGp 4MyCNΜL$X ״z&ȕ}ln_OBW;ilj;I6n/4/̿.FboIr`fn<kw&-%o(;u oWq Zhy>9NoHD"+]S" ;}JE=D8O1 {{0~Gzda/7l$]AJdDނtnq~ R6ӪrxcB9xyB BiQ`OhZ};M"ovu NK\?2u))c0Ouw mWםܨ?-$:S`v?|iV/-wJIJhLNhQ%NsygÅ)m '{MoW4 צoۚzh8|"N:>0]䰙1 ,>?'h#^l|o_a7LrUE.1!q@hvR I]m'dഎSn}d+7pwI8UΆ8(VX0Ua9؂(BvN]g8X(j3' fx7K(c´N!J43Ffs {RcqV"d{ů823GB!^_ѨdDLC}d(|GE5\6=9.Syfaf%fyȎAi!άv,귏*gMCEJ7 d3Ztds!sNKy¶%G[uSOWN0cýۨDԈ,j1wp*`vx2 j*QWAc[:LKUid; DcP"DĠt}Ԃ=3)69SgJ3OaoǬ 0Q^EG8׀j,01{;X-27ZNG`t}3M1_dܠ 7-+{RLAy"t*!6TRnAq%9gr| NX+FDj}FzLӂWiT% B(9{@TN1(>dJeMwsio‡9B'UM %vHe9k6{a($?s8tqL-]LS}!##cq/%d [ XW$@CC9 ,qz[&K~> 9i.wQ8Jl2yA'/7Q.9h{in;xq-KDxb!%_ +#ptx e'(LT`XF|9ptX-)  6f$+IQY˜Ws /E;ԲLq!{ e))]rW'%PmQ҈*ZF=DVn vD![dPwiQpmΌ\)TA,ZpC|4ښ $;2FhD<El)[cmҔˉ[ua8 B}W6M(OEQC uܙuy+fRpU~O*Wd0&du  L Ӕ2B<붍9('X s'TTt^G3Q,4c 2Av<m>OVG99(#gI*Ùu?/ ̝Pt[*wëtvޚǴQgCc ̐.(7kxWuwq̙Cꉏ gHר@%ŕe䲊RP$9QĴ<:p;B(rI}! { +3P.0=ϧL$%lXD{Y١})=ET&$o0 P;Lj|~ډ4K)+PdK5!?}ZWG^cq*n%oMZk¶/\զlQQJOJ`/,І?J~̢ sfT(.)ŖUbMQ8i.۽u?n*V~msġ֍jXυÚhDzy^Mciayk9iC zrD@!DWӟ"G'G yIpou$:)6QЛ8k6+{PL.}fCy8xn]}+K؏%\GqMYyhUصZRCzb aݏ# It7=}1J}K~WjN3\ 6s4$Ԭ&A⫟Ҋl{UO"t:z 'ݱ桡嗋M,..qv֍y:92"VvW rk@54 < ^=a;tUnjFmfL";fn`q@F&ms QlJwdS_DzjP4\tgKi*.e+{s$op~4zjey-B+td~t' %"9t+7p 65G\֛ uIax͟p) xT{~" "V*u$1jZ[cGS{4#}fA?i#ScIyD` !)JܲO)S><K aYs? 垎р58>iqH9_JE==~BJppS nwm#rݝp]Puv 酝$IoBT%xXd79vUgQi m`f59]u"F>fs'ZDZRx/&UEX3NHϺݯd`<r^Q}٫>I\fh9d,Ocp)U {/T Ɨ7hvC 'sc3k:$q˫e2ơy'Xg]j5?P4ItzPVưBiM[fg6CݼmH^^34N3aL=2E8dp4 ;|lxPgqa.JMkK1ݺ:sgBQTW 6|QKkl(xF=&-ƗRi\LQKF Bǽ-C4z}| &)+cZa{F G7P}F۲XWxJ&#YDԃ09 K!1Xø H$df/nn}A;T%l9r-%-ҿ:1Za4ڬsEPQtfnZa /O %Ր9myHJ9f fsa[1at6AגW$ RA/O-H`H(QWV rݺLT·e<^N:Ad$ᓃ_"A8JbUZC@:KjCQ| V5m^rx: 1HKUމfS"^mG'kYUB \qތg 31;lemlon"NP&j"p}G^bTDSIWpAYB:+yl-k]qS} D^~ŋOs|tLH# @LYcu&VQCszZM7E"Q#?v|,0cXS4׍W*F ]EC!YKw5jRӷɴ6A+ _?O6&pJgA"Z,,G_*^b;G:)_k\(op RI|TAe'*6qZ~Pʒۘ nIoD e_?D<'^0//vFj8Šm#i\ǯ— =b/#W:t&! eoY)\E ilٌucYz)cXs"TpL d + w+%8 ,DJ֝L;hz2G7QHe{%˒q /oSΦR>s5hCY(0ujєׇu!߫W(2JfR3jl o u`^;ROJ 2EQXcƙ|kO|:5$)eN!_AH,Eg ӵ' QHF p:IdU6p%}G{`M+= _}'ș>P\ eie4^1%Jy5t @>#@}Us|172ey6WYQ{ ɘbFd\mG_9@[&'JZ{U `=d.x<[̏1w>TLVJè  t&L&1}:4.Z^[@50{V SΡ϶^;il%KߍrxS* þo*dtDAY\|Nk _ST ˣ-]Q\D*5j0x}5dռu?.Q7DWXE)9}P%p>_؃$M$ۊ" l/nR(/>=HG;qlי8D:=GœZri/lILIM-4p8ޏpf톫dԺu zϽId5]l1ywԔBL_+X$}3QZ[n5{3D"95Zer(.o3})qdC)qnjZ2=+a]QR s3عp&;NTd'! <$ў;6x0־~:ntZ`h,OOPmM)3⚇py5+q UbՃ~8-]D&}=&q5 }w}Z Њgd>=wAF"WŃ|sڠv~fgS3藹xn,2"?[%OޕnM" ʯ$B4M:N;S +o뙽huL^5U.kC#틏? &%~~ dV RLO3vIyuWxJ-hk>`iG*3yeNAQ2d2@ HMbv˷_Cj$ #4ڂ9cW|نF7vj5;@-v6 ˁA!8>YZ;bwl<bPLwa61^ %O fTҶ_Z޴Xәۏ;<(_/݋ ՇW'<~螽:Մ/G!0_+ *NDE_T@knd; !p1㭓i#W D^o2(,E6[١*VmSVr?S Xg `˨hC^vs %s*R8 I pb3=9OM X;VL<[z \^LjܑoMptNOXz$ɞ+Vu\\0( `Lϯ(JL_(/,X n~V.GW(rVMl.cz SʊNZG_0sJ_$EˎD6Od>1Og]xؙ8G=5;kLm}-;Yϔz61ɽiQ$G lq͇> 뗮QM& BO)ԭB@yYmrxƬqrՆ{嗸CZ'۔s <[wiÒV\Pu-3cMjep aCO_i1Z;5B LZug/4‰ n- +t&\-gq hWj۱ZR+N*X[ٛkQk rhٚM3@X^9+sYtU Xf؍bj%n_Z1hڈMEhיeM%hq uMe\XY(Enb _h Iįs_~7-+ṭ=Էt hF*0;A*< *ɟGٵ~Jyj"TXNt/eg7a΀#[0:+GS|bH]y4Od&n"x x\ ; 1{SBrt%wqlm6Yh{o8*#HAE]w, *:BZ'INbΪtx-#՗R;3"FMgb( FDc.pD xh:~LSnW)m/?K7sD$/E*-^VRǸf rE| cLF:Gm8HMQ+YGUfGK_ ?lld"D! }|(kSqx?7GvϜ#G!>)į\D-x"eos8p6}"a;͛#m0N1%16Y/9q?m!*)Y/{gvݦCkT]&Ih]lve_u Acv?бW3$n 6C&\otx=BO7)`?tzʃBzlv^9hI l*?%!6B_MV {Xʅ=39s-$]Q{ >z&`^U9C#` CjQ]ξL| ,KR)9\mF+t?Kvg!*s*H K_Th몈w E9Sٻ2fΙ QϾ6VKr̲,؟Dq8̣_+L{QQ%z%vKMۭ~{"%ӝAcQ^qXVY,W*k_NI9עCaYƞ;I߯ВJԞ4v2vޣ,wdKKB ^ORNN H۪;X#<RcL._ܕ|CZnA,%fp tEkYC!Z?1&k jŪ9c%Bb[,ďwgmm"Tij6Q M/4.(BK1뭖TN΁XX!p-,9!)Jz`?[,3[ qpc,yb>dZ=%ĕ}!LjO׆& =Z S7kW'2.7=̺C3 sD1hd$kX(Mx! "ʻnQJt ْ6SFڐ/[OB5oSiptUy<⩭X1>-пҫpoYqiMM=J xmshCU9%DC2 ĵm;s,Nb k.A}0F{M+X|gJ],?RyJ9gXn> J~E67 =[ 1s\m2Wy'W c-Q@fZs/Kyo 7&`[ n[xgzBwg]9XFN1no~5wY:eQş.^CUjG^n?SnwwO"feS>]>3Fà68V))X.~Z@2zK @\kD$L`BW$&_kJ7c}}'Kәrd!=3Vr&oĮ!E &k4a#)*.K#4rʚ[NIޞb#kEl'Ta^ˎD!9}!UQQʺJ%Z ;9up `^xZ<qtj11VF%řN |#=k>T'Mgх( %rhL`ݒdIDĭAHB#U+n"B2ͪU2ݤ;|*&z`ԣJ3]Q_IŴ5zt%gK޽9Վd7MUXQxBO;4 $N8!A΁3mit[ydLOrUmi*IlHN zٲ[ *D)El5U{֟U#C@t䷿]D".}]{qx-Kiд<t C)q9ڰu28~PT]ZG1U*-}.ҏloq0/  ?̽k[``VOvK U;TYhf#pC&8Kxt؉QwEUIos:% 6͋y8\dDB CXTg sg.َT ‘-(a)T*`I1,LJ5d#R-"X@q! F\X}|7VWU$Sl7~dBqK'P(O4}aPqý_z̉WxC´=*Ԓ?]aN&ݴvUB&)b;Qh(VO 6FosH&,xMe9I|SԧSهIV|tD6o!gk#[]ѷ:]Z9K5S1(jV*+UagδWrј]~ݳԃ@ܛM! &ͥr1x$aQ6ȫDqc翎pR O 8o/;Ԗ`(s%ᘒƉ7 Le8Dn{~#3湋'[YD=ׂ4Yg]ƁM <HfׂА0Kb%.?낡57~}jQWѬ[Yʌ_mJnw_f : 4t!BMmL_~7ddS/mE8ҋvZY%?\]/%VgRז2C)UV53֬ 6juIkRUE$`6pT{uD{e&v8LxSFTH]˃k,ν<|X|;tS+ޓ!j0)k{{PröK$Yq@cט~"#rtR-~U05>n_sN$͠ eA]v54I縨ċP^ɹ㫛 ž&/ɬr~[wƊ͞;;?eh% dw;g7cy_1".&]dGw2윋C%EQ5v*Ov0}P0h~lWeStz"ӫ)|_ːk#r@X_E8^t݃zBAY0_9WAA9ɣ֩Y{xjOfʎךoi+Uxج,GUUL-MdY]C%-ְ[y @ V X8` HZ +Zv`1hG .AGl6UOZSr'd!i+<W7M wۢm C'7&}޻~]0Ɗi:kW?͍;ߕvH-@fLw9~dj2sl|QhyA{*w~xt7 a+k(7lF  bR۔0OƖҨd 7<Ԑ&%(Z(+s_ vcN%յ'Y#h%-8 n[pFϲ;p^KJ_3˿DsW%X+|WѤz`'5=Xw`ՑϖT_p:UP5ЋT` xzėB@;np]V͒8Nr| q'x*rBGJ&jY85z1zp}7A2_B;#m|Ens-98U(T&(Gy~~ȵc,Z[!-ˏ OFoƟ;q& a |b^z/3ANJu]KP%|]fBS5o |ׇϦoŢD +I3%13{${`1f7V]@37,~7G%(LmES4Mv[K^T"'w'|P܇EQCgZ1,͌ө܄jFcZ-~wuo| ø'v>ΖHM05H7D=$XUѻ*G^זO;B.~ g iXn0R#4Y$1t,qrȘ4W8`N*>N+f>8{mCXzĈq>oPw/#?J .G!9ݝt-{oA{pN Y6lgk,%Mw*U@b|}u"`ڢ@r+po<IW)Y?zZw5O1rvkUj BօWD |l2àGzF$|Mi:.?e&~!7ĴBL:׿i&/[?ryoLgU:l<ԞxDHq .Zjx&]~rI>(l<5ÛA4@gv_iG&tзzԋ9v#k" ;u$Σ;_,AB?O ȨĘo#'7 ѩѵrbC<$ZIۡEqPzw=^ɦ<`{ r"3O%7 [ rwJ,8N˸5i"03ddc6 ǧ19t7&ot;ƣ`wkzʆY)'*'/4qSצ2NڃԤy(Nyo6,zhr,(oR%p{Z/sV#-2جepLYVC*CQXNd]Ķ6X8?3l)KZAB"79jPi=N')uo (ơeD=??G$>jF$ U"r`m9kseSOTl'*~d'Y׈y+gқan%0AYj/f4gyzP`{*$;Bs<ږpp^"?a{{H"D= Փ\PFثIr;ߛzX]׷s,3o$RHvB #hG{ = "E¶\^RO_)Z{WNduG#A^@Py⻚x*$ C.{#[yyhT3~o˯Fy64yM 8S/U59vsݗdy]q,XG{R%xTxW{M_, /{s5t\~9H , z,_g emmK7ݮn 3FoN9{7.{ ;AV"0]Ӫv;#|=Uw'8BSi55҃pGj{mix5!XYThË}zEVS\qaJ_x/ ) P!Okmq_nCWƐKdn+=*/-Ě~7n/᝾؋GphLy(ySWhY6"x@ =ʒ<)xļSċ**옉 3a3{OfǙr?y"gɲVqPkIoC#rȻo6,uS1jWyv0j:?z*atXH$D5ASM#(Er>QY5ȣ1Seql@~ݫY8'3vrSU"= Dkr 2w5Mg(hì hlNJ W:Ncn'O֞hWؼR(D;pҤ4=/‰/jV% հgZ(> 7zR<Vh[vT^aBf#zI&TYnnu'MVcВ0dv N2uD#PB(Gsan`p$wa%@/D<,D` :˰H.[YxZ$wa&9dtF=o#$mRAJ_.\%Z] T8swR Q{Uow|[/.Th`/+u7ۓ "x k^/Nj G$؃9a8 @ǯy-u]=ΙBDs/?ݖ(\wbPVrC E;8Tw̻_cy]H%-4ˬ֢6:,!W[ \,Zɨxk=5?)>wtE >Z+NVڪەnIq#6|#)*2r@[dû_-(jbp;`tˈ>EdY| Z4tA$ۘBDo>ayN]T۲>y,ŕر&1́??JEGgFP˶ y:9#'FpK?<:C^ve o TV6oוL\??P!NѪUHfQa>r3Y($p-ٔqZG0>҉<ޗ`+VcD2Ѕ89ٕBG+a]|1FZջc#^\_ gyJ Tv^cРM |9wZA?R9 5נeռWEIA(VDfwW2@= Y*QTv;cyE.#3V *f6-ܬoLHR~\9Opx@;x8:Ok\b|lI{pM(bHԪof&ہB]wfJLJ_--(u7T0[-^3Te'4$br'uY(n^ f ]9{doߝc9J"N]b1D8[([V2f,Vg-n[sk|=;p D:4t:@j #"ndji4~S0Bmscf`a  Y:sB;lВA*r{k9=w5u84LpQQwNH5{l5n{gtQ 1YXynpMs]?C[ A?_dӥTY(23^QLUa(o 1t|LCR%=*fe,oޛcfLOE` hC71Qo FUQ᧑. $Z9 1߸?ʨXM (JIr#kax.z;%B!e}*ٕŸ;y'2͗řTި`0 <yhS&ޡ!;zW40bMN"'爼Cj=bͷ Ro>NS0BNG؅q4՘C–a_ngVCZ3ѩ8YUeٌ!Q2;f֘ B+\C8JՠC|Xʻzwf:jԺE@A$s2MN2"zwzPA{O*oXjtjI=}T4L/ mg#}VipR$K\34ku>C]5izu׀PKw":Z'\5̳aиߌs#6YYKGcnan7y#^eAeIʐGCXWZo6ܷ40Aځ\6w\֭{J=ڮ7 *uΌܞ:.ёyԌҬT٫U.M*,ėO΢?[s)AYBt/(H]`T3݉amFc'5~(<Φ]<o;hN#}:lˎ ڭz=UUEO )Ȟi#@s*Y_#RyaKk$nh7unݣuE&rLZ%IF~/%d?D!Ka'" L]SÈǩ`S9g6Z. ."rHU$?-k} s5x}e#F%> +g6+_x;OY 3yz'R9gٮw!k1waN8ju]4rvG$)B JmtɊ0&ɇcQEzUshMBgQ&wVZZ֢7TP  Nr:4 Prdԟ/'Fz!۠bCUC&RB[n$e@Ǡ_\8dbaUP|w[Є}8G_Ag;095kEkr:XMՎWؗD-[1:63]t v6V/gF6sMm8YLZC9e` bք-aaq+) 7Q˚!-*0ŝ#Bu@a;{lQm翴_(w dYP$|EFȏLXo<ld]ھ-'kޗ/v s{ȫHOV+}.EЙon 6',̥Zc;YҼbTud`'tJ6Me1q=ұ+CHJ%3 yQ ߐ|Rj|k!7ĵDFFFzM搤DL$VTOwM©穬)ɐ· s{ckĥwfóyk,t+Cpa ] 5_fw$E t킘@&GզCP/Ȝ ehڤ؟a?ytiE#\BoYRJݍcfJ6Y,.8;@8q86`gO`'p,_ V͔TVtnA_|QGvKlcda\rXnZ &K+cD-ڣIх}f8BjL+b` 9l Fr~Bº]gsOC._ |O1-!s@nu ku@wMMYu7k Y)[ PSjj\.J§$Jx}{RNe'WO06Pcl=@G")70gfo2e=dژy{>"5 $J]s ؝\*/19ðS=Wj{]}EBt*gfhGqA-x)Em\|}!jBx9EMްH6d_ct$]M!ࣩ`oJ8GJO9A!p*}SG\ AH臄'!W4kٺTfq?Ж`ق$9dOtjZZ^_xpbUh1|?&Z0<?ViP%{"ǬR\j0euwά8;L CŏnPaiYOqφ^{QL}9-p>%ż #YHZ]D<_Zڅ[Ct|A"xH{?6>ҌPF Hٛi{tw[R&=x {0%%S1#+~P|Yie왴[ΰ? yo!:4gfܔO2߶e;$$|u9]lJ^͕Qka=F;%`юFJ0)J*8*#Nx},aA1{37遑`X߇2k;XkD?6Q'萻&[Z FRSYΗf f-נcc\_V,KIb3Mmҡ<#R`Ta'tQCl=Et__5W`9qXWD -[e^hX*`z.i"$~gCO:눛W W齫%")-Ao䙂wAaUOw$ `M`mю~b\+i>C5I~fW9~XSdt8^&݉$9@ )'Վ2r(z7б \ WND8;SʢKWU]:So4F^0w75!J`nn^0To#to;q^CtJK TMIZ٠-x mpɈ2_\MgX[_o^ ZaQj?*fcO6L[40RϠ t;Ԥl9ڷ,&W0QHOEůF+6 |^]U?jqs\,}鵗hܡm`}oR#7u.11-(4URk\˔)D:dEI Ob \ZoǨnP1GT)&>Mټ$ zP=BvSȿ 0h9݊ f_Ղz7#A)2n(blRiTqũgo\& 9lxKE]f -WS# OiѿޏlOQddj2Ez(` 7Ck\)";;24sZY2;Бy~ 8zDk,ʝ>COVE*ʙ3kRi.+do7k/񴓊##]^5LjM͑);ղ`zK enGrJ+qtI&dKL|#rI&XHfk"l5S}g1ŵWGw*?VL~)ci6*"4'YBlv%{">Ď/ɆU4yilt0UX㣉-G~j.~y-2V!~-#Gl Wڬ1C{OkKAZUͦnd榗\z k$hrʻxB 4ͷKwY`&L#\퐌c1#}V]t7DJK1$Y)rG__ xVMCްa?"V+6NIZbIa2 )E)Skb1?8*-vy`bkNi$8ԋAxDЃyx Ye"BERHQaVtD׆K*IRf8;_Ŏf r": .B~zCnq2}NJ*#)S%nqYTƧչx ZB3|Aй3HtU8Ss6.$=0'n/ E`L/aXm/װ&-Ӳ7YnDBlH3|QzChMu@oxRqW?ޕI=gbRLץo_fgn"h[c_~K|KA>&dFd.,DY)m~DX~`QU!ȷ$d;y[tͥ/1C5R Ǘhiyqxp7j҃!,霚KL0Yex&XHI~{(+5.r>k>M6 1Xp Fnzi[-gb{_jlht&e&$6K5\|2Țۼ>p}0KM(WA-fbщ1_p+uKHGH&C6< GkJ"; ;w7_ [4^IxQv$at1<[uԺY^}GY{#rϨ%Ǣ-z-(㒝ֵ q~NmْG@.Agpt kg,!IL|kLJAjOMU=ǐi/p#9Y5.#gTi&TWX}֒=#HH]̵?,ZkGH?7a2`E4E-Rf0ލ\#6]՘%ȬMgֶ cRZKYE@Ƹz]NLu6)C!5_eL^|{Te'npu/ OI.;QfCi$o݊[R7/+ 4%5䓰KbQdǘ/$p߲uwTrЎ4"V'(4aos5 GQ9,}cdC?U2V+nqBl1%[須g+.7ëRÓ޴j|.a֣j d\FWw" j= A%=ȋ:U8sDc$ NsQ1QʱY> -'DSly!?HMm4b!u%KMʭ@thYy9>&Ti%JDfE 7,4T0b_|3ur2K3E1hC Ib(shYZ Fyö&^S(vT R?KM<󾸽HE+ DMQ}>q~5n9g&]!;CD{(]N>v32wQ%ֿ第]ܜ"ˬ-:,dk|1<LZ&1zP`=S~1nš۟~Ր4w2K<<6j\O21SٯQ%ARI C)*y\>V!]ź%ࠓX?f-]gXOV)%:,|9H(c (Y ! )u@9[<DŽ ׭I)7=~)FhDιBj{2NLl@=RlF^ zJ?=*=VO09va_?aB FIH3Fj*HN = ޖ,f#"] u2:8~.>Iװ"7 }af\M|V0StE;XLg~=m@(?i?>ez|79A|jM}N(&9 u&2ZC\<'T#W'bՍ+n/dy.m.iȎlNZo^>÷lRz{jȐٚ:gCΥ@YA D Ɔ$ *?{Sx59 Q]j Zy?(Ӝ_+Jj_MN^[n7X Y;"B$ϐҸ҆~(fH)Pg8B< sӶX&JPm]9 B`hbh!@Io|> i$`ZRWkr(_IC @gǽ+EK= =az2BzepzHt(],40 H՟ WqA2&hYS&9.pVFtVB-:{j99A iG^;VI+(U&e7w`ou9'Nn֋x]?&`^QXGyd4t%GR lݍCs9<$+\G(:,^dk)TS!#s+[%Cqlt -zҡ'jy/4FKgDhx ;Ghbh_O6l1a{[M.pj+2V_Tqu$S%5W߄USeWt,Rb ܲ^~U 0z12ʟsJ`<;Mv#Q >]O UBT:lt o,6 3 [Zқ yl1"0%tdPBqf̲'"uq&!(3SZo k95>Zv<*wE16`Y{Gny ˹Iw+Wxw ga)ESW3NXSp~6C?na}6@Jù hxe4R-A"By(93lM&hEg"3>,[JD0~G* zqq %ЃݘN2eѩ9a%cgQ.2wj&眩x؝V `lu7\^L~TƕMOݫРh<9&*i> $8KF6+<ɫu?W\A9QC }jp1.*C@`0A݂?F\qlϷ0ҵNMKҫEd6г{IV 5wWa{ Uxzz9 ;`kܜҡm ^CA]ݲb %b+O8Ў[;m?K+l^;; c :f/[ "GlCUK |2eQ3 T+f8Ax䟆oc c~Ҟy|`PqF˨c}+SZcHM/'&JxrޓMjC軞-`ºbFeԁX]$"{[i;5޷e67fʣ@O<̑jFhZA#RY#҂ F|>o:Ai8 R[Z;W9DE|9>Y "0lgc6kYj:./Tؠ G6]Y$p,!rn0(=mi7Ή# zȰ*aʏeA!mn$7/B(wӨ!r&Faʁ:IgҶht< OP߬3'_WR7yg&T ,d%#'smy2 2Sv.Ʒڈ9m<`6^8Zpv3to`'vc1 }Hq.2X}xRpjk \Uq67#-edɾYTy\rBzArqW!p*&nZvaiY5ݡKCx+ƨ Y%4ݷ3LJ zOF<9hwvHu -K ]tg]KjٿYӋd+!hJtq%a ceĢsbGAGIVE6zsԚ"JO?yE{sw^iPiW1jOpNE?K}pͻj ;8z#3"|ǻUc4ߍG]FXjLVX˫mgt2~ r4+BF{G 0DHQ#9J|o|<>,jen(!>VBʎ_笴*e׿ L}.S!m_^nt.&\cdY vK除*Uv"ڒVڮ~Cqd\28xgFQ7c%VGrM5OhgMqJ&6C(Qݡw2޶ީ/]x/Rތ_}-3M¨@˟O!MUSLHp@]Ǐ:a1_y@gn cOZBru}~Cb޾%Kv:Q|,%n5JʄԽû޴l>v5S{b1VI])0koi'fTG;.^q&ҞakWwSt6Lk߾YcN/7$_?V$ 92@?:5Y;-z_uuBAk"gwm|nݸk,'Mų.Dz O14Q,Hd .jqhՐ,5 zmW%sP[3@\"b[jKɐ|gZ&?~AUH iS3q1 +L\b쒇yFIϕ6F[0cOef w!\)lx(Adc圕_oTS>JQb /NLpz%;tqfj4 RߍEN~5cU7#3^Wubϴ}T$_7 bCm:DHO#[kV1eH֞恱O.fY+8chAK1/Jr -F(,aޤdQ5tkIJ_x ZfvFL-G}e8OUXZ-} |ߦA`MZe{(Mw4P!n"l B$*Zexv=6.ϿU=^KFk75ѝq9SHw@~~`iX( ٤Jr:]ѰF(scJK~Gl>8SWw|I*况Z /LT?A5I7kWШm0+Eu5BxE=w 7p^wj$Isj>s)YUe<4WØ83r)btS;'G.ĸM2[pKPInQS%"-HUo(]&"z4թrhߨRf!S"Ⱥr&JOQJ~slh7AfSnlP8QqUYRド= -Aս*G(pQlz-E"m?!瓌q4WTG\R4>¦EmL57z,dV:⯬95 K>خ~ XaR |RTª~i {ղ_C$פmY·4/'> qyB˦Sh 5ha`A ds$h=mV+oY@t߻Q<}$@kz:0Pt"wёm .R{Rm?p؅ǭD 3**Q!Vk.fe2/m)A]Hjy I!->No$^K2vVvCW؍%fFJ3Uˁ8=?vҹ!;jvsߣqZ){l&h"*~/;߯%1띲ʼn -a. \]N˜*!ֱi93c. ζZ8S%#DH8b}|VQ= LPD^ddOsTWp} _ej`R*;D7%{^(>md޵@zrϢ\C|ٙ}|$ 5(B1-(shSa>üV oD1X{NR C d,-Զ+Y;۲۸!C٣ b> Z^[~QsH{Wۼ)aJ%BW JRd8/7(R^͔~KRBl,r_Ң!'`pǙԒ|POIG2DƗ+(E$FWY#b%zTu N}w-A^0L]}8ԡ˳ll},87BM-ᗁ [}~0kۢWd1\V;/{ 143]FV8f6^ XڜQ13p#ccc8@./]4왔~w ʏohF?3/IvnZL'L*>/Fw7slj.QKWmP&]G% J=}WM///&]|qo tDI؅*d>[| >hk>GɴkNv ON3m [BnJ#3MS!U˃Į j&\=#t.<8qfo$-Hz π_u!][]&wpR=}P5 @dMӅպ PVċW"gVnEk84!|piC{b} ,)bO;./ xˮ5^ehO?ĻaIӾjhPFM|H/Qk׫7X2  SѭbCRzD6 0#@!?שpձ#})4Q.}؄K̿W-`.9wٍz7r@fʹ+eVk7\0䞤1D %$(@P;iEr~C9(%aZ"9*Cftj} ~V+-Ywnibk'Ŝ a%+T#l~6_vvkT zxnK-sЯb 9pfh!N7TAH> stream 71пBȊA:띗MA7<ʮdϴ8*kҤWlh#xZ#&*P9_*g,V Pqcו^W!|<9vNGIFbٖ.H@?)@ z'(| UlM AH̻Cvb yJ':jlln~EՕ Ϥ~Aцzn >F2zu յ+,'FoҪlٲ(ވ2l|_Iѯli-% PGAER} *~wҩ'PA8-9d:.2Ŗ|TcY}6fC8C,uf!f(ϷW8T7G8yoѢ=LӪ2>+KiPUL z$yTTukjV} 4kuVF0(!Y!h{ >bʰ<V4Nouq߁~J 1˅]~LcKC0'bGKO7UtAl !~.9fQ?˫`U99ڤDJS9 OK> endobj 18 0 obj << /Length 19 0 R /Filter /FlateDecode >> stream So2V sEnP5ئumA(i7@_w1kt~Hi%c?3 -'܀5B-ϫqu}54?72~ Z:~䄚2lC[E38P;=0b {OXDLIj ^ց腩BlW0%7=[K̘y}4>t>a~?HR A2iۂB \4{K R?[b&F.0][R.JBaFGjŴ`,A(8ߝ`ʾE]&^߇cLKq^j-`.N%nz+\|9IpjԳe.럟@oj[35D̅pqdq^J!yR h{ X<_dٽ=~ ob\BD`R4*n>4N+F;P塏S61uYK;"?)6Nɿ@9w$*ļ<#,۝nuh'BOWj MTHW_Wy)1 7 IrA c(}qB) Ԉu`8s̝A1Y;[_׺/ux|QFoh-O-8`ף "|ngw]4xvMR7Ϣ2UW"h>x]r^!ToՆK2/50/AŊ[8Q^ĨidxwP"gI}GeR;O]ݾ9X㋧!wub \z~!j G:eh(Ѿs$)}w {d2[Hj)_ \ O3k"ŵ:|%p?TYy GKsCVq1y?HP>A iߟi 4-i6W.oEX[ihRuQ΅0ٕ ±eisC~jF0eaIp~)hN :iZj2%6Rs૒m:G@=l#ʥ^_D偮Ztß%KAy66BS`m@_ x^N]ؑ+dg͝ ܭq-OӱV5+#jm];4 9EEH,U$:E"|`wU!MJ Fr(b5ӎcT**m9y,|g֕#aʼn3'iWIYj^A?8qY_ψe QDHBpGrۑ_;z4#ɳ5~uccB囆OۥT .C4J@~ 0F2Wg.p$I OQE|TNIe?&oYC;yveNt\eML>-Ea ]DL|gOXȆHjkY_B Xۥ3F~Y,M964֛rq MWPj.j02c?}]|/-Hȸ|c-4d}Ų%!o4}\B?IܟMu[Au[?/tAe_g"`!m1(7-Yc8OcZtS{8g||pOu%h^8<μ5e"sKxBeT.=ݑ"ܯ;>ɺ{71.2+}Q޾w+2mᭂdԞi@ :2rYX/ endstream endobj 19 0 obj 2688 endobj 20 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 18 0 R >> endobj 21 0 obj << /Length 22 0 R /Filter /FlateDecode >> stream F. kSU na4Of3PȮxGOVR 0#֠ yzcdY!7zDzZ#T/usr1UnȮKTtpZ٭zeD!Ec8r mYTa*|"i-Ls1I1{m<ΰSHP0ovո,Q*Ѯ>|E)6e23[J::idgjXbVeS, ?50FHY׏n p,JnZLF8'`e:& b!BE›C)Rj+`~>?cE >lEWzGe ">B2hkwv>l9 >>j:[rzk%84zXWf̷Y`f]9@^L"[f!)[c9|yx3 [R~dkaYHRr.֕HF^z ?LA5¾D|F%MMRp'덾fVEl|=vQfPFJKGh;ˍt7ϲf⋼80&>Zxt{ Bb`ի 3K ]Li{)^Ϥ![V%sG$;cή2 (4~*{ ]׾/PWϑn%$&U5KRT&$xOs,׃V@`WE%m0} oh!64/<΄kyE8r/?%KTx ~ H^2GdɘÀmL{6Zz᨝p s74hxX;wKs_GmIEINT1AH!>]/0 HXouUX{%]cyPuF.u0rTno5d $87.((<ʭUm|P))pY lk+(@ewrD9yش!i+L/澾gz Gr ר@]2P^tҫ}\z*NwRY 7"94`=sT`c .PĔ Y;˼U7oFn,IRP^JHc[>cMw޼0-Kbp B(:2S8%ݕ-a" 9e#>NODUW}ς͵R-S^8x.u#3ĉO?recHY[\=>BZW~mYOYdOΗ:=Q !/Z7/W_Ӯpe&~yίCs0w18 }ƕ#aqdVfJiTL_=_ @?aK S\%Lcaryj9}w?ՠ~ͱ]$aFju,K)"!qga/f݇ͮޮjzܬD,\iQaن2&x3&}(q@ܴsK0&c0$PMV4<# ܽT՜\ 8sN!,q@j%ck/[ei@Ψz@~$TXJ|pRGg SX+jmp+untcpBC "<(aXslX#o\F3SɆY 6VG1U'JL'EѺ lQ DD{ }S>ȻpKvnV);n=5*ADP3 #t)⢉#Ǵ ]R؉F )ua=)ǦzI:̟wjϱPXDiU;bV/>Amx%Ԫzsd҄C8pn Zom2;IP͞e!zr 1hhM/)HiEώEqfF{Wi"#:~} ,> endobj 24 0 obj << /Length 25 0 R /Filter /FlateDecode >> stream 9#a}!:%N ,sTCI)$)OjgCoUPu ,юW @FK47u~boeצz<@ޢoA) bv*|ǔ.Bw#Ѣ4!eli^JK?n.;00`oa`Pp#GSDOz׋BElE`S,F}kXMd܈r (@tH^juX/oJsM06ah3"'B%` 1 6yY2g$^`F`(=g>WVIzZ|R&F;ڸŠL)$>@kb1p[H7P~U3WDAa.ڠ^H&^_7*"2q,i+0w+g0 ^LdS OJ)J^&{3iM|6 f38D`xk=  Kx4ڗ_W|+mRWv[\ =QL'lJ ou )daC?^ҫQ>%ÍƘAK_Lɞ)$^v?\I%уITr\5~ays&4k zg#>`N-i7EZ;\ܧvamԣzMKxzI)(+@kYI\]CCJ|YeAÔ4rmg%~[yH}QWC!դQJ{5Gߖ"y0Ut|zxxgDL .ێn?bߘ+Pz1@bfɯeCwyAéWR( Y]v1xr7'mXD?ĉi7p|TRϨ( tzZ0?ҍzҢҠR CK ͈hdȇd_mvzײַ |PڷD3+0;OT Fs芋/L߫T|,^E vh&/߽pݲ_U«81h9):9kDoj%fį"1lc)5?y?ZgY86ڞxՍCVuYeIJ/o[غ0v;XvxdH`,봏F rl4S ƴPBzIn. &k{#9:!aQ-qY10}\R]n!eY:=ӷL ӍY7aDJG |2M lbB7iOK])0XGnnu5/{ǰgy06J޾SfB)q&,aM>:4d0Nat:ॷѐ=`=<2f $:IwR5FfeN endstream endobj 25 0 obj 3232 endobj 26 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 24 0 R >> endobj 27 0 obj << /Name /Im4 /Type /XObject /Length 28 0 R /Filter /FlateDecode /Subtype /Image /Width 51 /Height 51 /BitsPerComponent 8 /ColorSpace /DeviceGray >> stream =tFFu%ldO!MN(]'=1>F>Yx1 ?1î}wI#P:鷝-Jp!ͅՃyn6P %s 7n1캠WkV pzvPh2٪8)So?,%HBxiV/66WmWFȥlEJFYx64e} sm?AX$)]~K9 e(c"R@]pϣ`vUck:\Q%5-u$Vhc,j/c *3JÃ8 Pg"x'H*Č.3دnwi"̢ 5.j#K%5(k !RK ./ JU,|vw72;D8&Lu&; 󪝬^P']Sr7$%>Tl1:cK,oV@9ek*UY5dyk{:6 ^ȜUY%ރB|',E@s_m?"qy> stream ekv18FvLӔs)rZFƢt-2nMyrN<}&/eoЏ ]&͛cU{,'Zv2uwJ':ѻDSV5SNVXk1\l&q? $ʄCLck}sC=aeU rǤD$%tMQBZ(8-kAOtåM^0'Ӻ!,:{@:bto̠La~rBqn!8;;*P$F-ٞB&dbI[IC0ܒy!ۨ+!(K(i?+SG/4<~S!Gm.̫*8WP>"&,+hի5˜x6 %@S69q@I0e}-l/Q3ho;"hھ@X!Nr\Р8 t1O؋enfrd ekjV>p&Zo ]# K( ^0 {u4VnB;]o Ϣ] J2 [7H+2A5LcMәd 9]e#ņDXi9YfHUmwPF(g8Q< &_N i;.!( g`XJ<`^!57tlIVk2NA?Aqq㮊ts Q=coqIAZ$8A[SPĦehzoXFް2fW6UɆ#;d?p ~Mq [c]%y#Ֆ)=׃fZH&OEL{lR.ڠfLl|1?GN4?ký&[|2'ԌIfot'\I3lhkfus*aaKN9eNZȱHuo/,r{'6qJz+aY endstream endobj 30 0 obj 1504 endobj 31 0 obj << /Length 32 0 R /Filter /FlateDecode >> stream F;etbxf  1 Fj[or<N y[pz [QĈsJ۷nS{83bC0FV~^8hi,{?~:f/el~쾢|4pT勰"4D?J{6vRFxҩ_7s vFz/^kg~Z5h\D;ۡA[q]L\1x˸QH_,x젳Hi.?rT)I%0~;kD9UO2e'͝}oM$&m>B -ԓb(pl2  ғ l[nDCmɞ&#w^Q`|vMQ6\Iʂs޷ڮbRň,k醰+6:QdH}xaH`m=?+14l,0R2o,aL-' i{|*'w|I˹L"s#Z-Ē&X4X[Hm1XB :Awv!+# %O2z Sq"fjno%{Vwu/Se`QЙBpg~65o-~?;##muN[IVK0$i_R|uy=vY}:=U;YC1/tHQ_ ُ.{%<[8,Ʒ C /25P`cpg,vbDhͼ"uP# ^̾ofv=gLHwLfDZ؟IbI U~"aCfU}f}NOkn+>(gA~}8?uYbfwoRh2uӰ6<"ԛB;D@gr\f0uε-'Ђ^Yp,œ]٣6Rj__Za#ܠv2ScLH4ngTL!ۅ&kpG0vZjQ.3&KØaCGYV 01aB&bE5ͥqDn'$~<$*e}Ik¦'h6օ9xQ4jSjkA>kG?ql~8ІD.̫#͒q~;&܌ZO٪0_VGgC~\hk,I C۴ې06%?t\px:$l2%gϾdl56w .m#)`'.kj7~i gxE2N6=B[_L!.= Ċ&wBx6~R?,o#\e]?* ';C7ۑ_*#:  R15ⲳXjD]Eʀ܋R8" (E>w q}YI&Y66IlQ'bvS)Cɲ%v~.nԃFZ 56ɍrkrۖbQ[f(5*ivK'ۏiפmB*fy=-a~A0DDz}khCwa5B 3joY<ϚV(-Vt>`SI'7 ~6K,CO(=RYδ1.h!)!o9Geb[sr?)kgiu~]] ?nA/8J -wؕ~f%.,j'/Muj],A~qk<6mOjc&B5tCaoI!_urGa'$G_MlWbͦH1cD;7L,M7iPum~ f`_:0>甯."@ \׎Ĝ.hˡS-<8JƶWl 䛛 wI~ {}܇ܵV`Dev)x2"Ŧ@M#s6(4Mc2DرSp`C@_2jz{Aʵew71BZAƾ")1uV2>ME%m$,1!qFӚlL@ҳ}JQl:_g;B]"m)uZ{ͻ{9j ,C$,_R&Vx84s8Wg1DӴFbhGztוub2{?Ȱ ]b`*m+yᱷB )]L0S,r4ZGAB5囩%F 5/2+髨8s5ufŋp ;֚ROf}f4)FZzP埪 fPaJvr-x&p>;)NLPf([lZ endstream endobj 32 0 obj 2800 endobj 33 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 31 0 R >> endobj 34 0 obj << /URI <5C6AB7AE6E2BE7F1296808454BBD5FF375AEF2F5FF99EF7C0CD744C145ACCFAD76FC6E7D68F8BCA970EBAA14AD4B3C07AC34B485D9114BFF407C1662876C726ED68C397C945A6DE2B62459B0C74DAA27> /S /URI >> endobj 35 0 obj << /Type /Annot /Subtype /Link /Rect [ 236.036 703.506 446.554 713.824 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 34 0 R /H /I >> endobj 36 0 obj << /Length 37 0 R /Filter /FlateDecode >> stream mB\{HЄ"q 7ϙ8Y"ӽߺAa[i {G6,z0 M JN.יdd@,D"QO; L_$h:#[jӁm[F~h f5Z6"6[6&o~]( J#]LmŒQWܴXa)+jIpJv#E5 x"@j54Rp[˶Æ^RӱE*xH'bat hkO?ft]r>ЗO<IfIK'iR=nV&Jxq\g5]Pb~ڹBzH3 'u:eǧ g:hsu~\ԧTc n^1G|-{ƹgߠǺ_1UɥjEgu= :zPU4]Ǡ<"xcңSF|eCɶhYCt_ 'gez&T`MGHy#G f#1s%{8~HٙzR+*7d~),ͲhO,>{zH=Q_DG[>N:Td W/B-j\#be W|;/>9 [@1z*ԉw88i*|l> qb?\HH_,(ғCI^ҧk=sdZRq_&@'V"' a--"ȱn~YjtShY{+^Ò-VEҋ1@SʥZjaiL` JVF~_HVg$a x{XoOpy7jV7_11*Mڏ 8+tѯ,[]F>VTH}؃wDlj[u8`[ei|oe;2)>vy 6hB'oJ3`dtoKy ɳAwac1 Uh#tƈ44͊W٤&9Eo {lyq(jָHv,wBZ$҃.$cG> dA9|)9k=%N++e60[X+S`HK<2LmK(dy f:Us Жl 9]K> endstream endobj 37 0 obj 1408 endobj 38 0 obj [ 35 0 R ] endobj 39 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 38 0 R /Contents 36 0 R >> endobj 40 0 obj << /URI /S /URI >> endobj 41 0 obj << /Type /Annot /Subtype /Link /Rect [ 138.82 674.451 291.687 684.769 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 40 0 R /H /I >> endobj 42 0 obj << /URI <1C1E1B752846C8FAEA350D028DDC793FAC9C0E8E055F3889C38E5EB6143F2D335D906C2D0D4606CB66788C9C0EFFCE6D> /S /URI >> endobj 43 0 obj << /Type /Annot /Subtype /Link /Rect [ 107.767 656.026 191.499 666.344 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 42 0 R /H /I >> endobj 44 0 obj << /URI <3022D517D3FA835949C09F6C45BBB1EE6A84DABFDD065E1E364A6F25D406550ED33E25A3EE1F2E21595A71D88F114F13> /S /URI >> endobj 45 0 obj << /Type /Annot /Subtype /Link /Rect [ 122.815 637.601 227.117 647.919 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 44 0 R /H /I >> endobj 46 0 obj << /Length 47 0 R /Filter /FlateDecode >> stream ez -A[tGjJ\f*G6r8a]a&1ь}Y2 9ؙjAJIk؎&Y!%m1,rwp9}_PwWOAjZ+2*)v>"ֲ5mxPKTuOqKgu{wrRV /q&b1HWJDklX?C+iN{81+'//2dm?C縧ظHͪQ[F FjXsRG /^aNʧVwF D[L(ClD`W7Dp!'ĎC)UźH|4)1@tJP|%Үm+RhtъLָ=r{BT,ٵ /zJ֗(&L`a2 ]]֔(ɠ6RMxҚ0!`ң ^N.tq9zDGkI'I]djIG-˕6vJ ݼC`t>rDAHy>&r9qr4d endstream endobj 47 0 obj 976 endobj 48 0 obj [ 41 0 R 43 0 R 45 0 R ] endobj 49 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 48 0 R /Contents 46 0 R >> endobj 50 0 obj << /Length 51 0 R /Filter /FlateDecode >> stream 'Twr&el E=zi 2bxI#`+7KLp+ ÏD0.%3j@hX<P7Ǽ8 iiߣlY18k3_(qʡf)0 72gU;}gZD"Fv[[T!҉\az Ќ SBԡؼ,04J3Eeإ35&+' LƊ?3@,;[!wnSyetbVh=no AA kuXd `д߲ j-^dbMq:صbC,s3Aܰu¦ũܑ+/yd??t;ج[jW_:z*mY@dqj)aSh*o-/]01͜BG42ܪo"BrV"̟l([_*n`T(\rWo:V^}2R\^O߮;Y$OC!CgI}fB4 ,+ S8ֈ_mt+c}Hf^E漢Vڀb|h!=T!_qprW{-܏E:s#~\XZ6 KY*x> ԇΛ2jP~MN<3]z@kxf@R=*uC3-AV.}FjHieB?)7{9~^hqhqoRb`ӊi;"#V!SyT=]:.)J ;~ٿfc.|u'B'sd{c ,N-1wPr[.6ldʠj"^ bj2+jO4z7K)%Wa[@OVh:a5}AL)W9AJd[tֱk.ݣJU[& @筍e2P[G7#iWů"j4SLJ8NҫjϜ!{Y˴ϜHo K&hwP1idTmȊ+WFa0֜& ՝wbK? vC]g2Kb—Q Ưi4Q0 SHP\a[R Ș'] ԕ-pI[|pl#})I.oc@uXIs% ic=cvųt0dds6#{Q[<4|C`; ۝G_ic ,Gm}Ӓ<ˠv9hyvcy;U#rR"dwB?T:Q\>H^odp> endobj 53 0 obj << /Type /Annot /Subtype /Link /Rect [ 411.285 722.519 416.301 732.837 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 54 0 R /H /I >> endobj 55 0 obj << /Type /Action /S /GoTo /D [52 0 R /XYZ 56.692 680.317 null] >> endobj 56 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 84.837 97.25 95.155 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 55 0 R /H /I >> endobj 57 0 obj << /Length 58 0 R /Filter /FlateDecode >> stream C4襋^ϾC%t?g\À[Ru׀Z\.?{!Pܩ;@$>:bA~ WLPg rsS! 835>X7V$`5MηW/!M$~V*)(lװK''x,41a4Eʋ| }T95WannJx<;^; fM so|َ"Vб]oe\WkHijs{i bF9pYENzfӴ8z]TS6Qk0:7l07^.Jlb} LgW9kyL)-{Hl<g^='/Ea$,I>dl26SDddr&# Y_RӉ}7Gv6c^ra|GV_7 ņQ[%" !q2P$NXjXIJaT֔dF@fX mRXǣ! @kNu7O& p]0lУMVg^k#`M<c2Pz`W2X8.Ri/nG+ ζ$;PIsh ~.-2Nkx/O7wFMw zh Mo!f'݊3D`u[}}S="Np=!}#=};W9O1RGY^01Hõ Z`$nVh_|gZ ^VGPصy ~[z k9DY5뙈i!&Eˣmq:-\52m*B`7bߝ|,bʀ]g[*/§,/wr56)EqM&#e|{8ٮ2Ɂ2y&w=υ؉T/`^:ȌHL//>kpq1*֒I\Pl,etĈ1}WS!s@Szz@;?Jt(Rl%=ľZzr<0*Ň4轈?m( lC"c@) /w4(31s7&]oh2+^^mKD:=bIOY^qި@^,*޳ W3PZY endstream endobj 58 0 obj 2112 endobj 59 0 obj [ 53 0 R 56 0 R ] endobj 60 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 59 0 R /Contents 57 0 R >> endobj 61 0 obj << /Name /Im6 /Type /XObject /Length 62 0 R /Filter /FlateDecode /Subtype /Image /Width 2400 /Height 1200 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream 9ָ ?H"Ld 6`Dl6.N`C8H}=+ǭl]LUٜgqۓAHڵ yEΣB{$xo'jT}В,C2wߥ@2P[+ѥs4Bvl^;e6r/R wY>%PT14(ptWPMBfD*b!cb yR] zrLK;FcPXGWI@~̮%E7G&Rk^'4:A#A kȦت73˫a)[z¾g5̦Dғ۱CK@i'*]z̠BвRnoc(AJf`ΗO%4+gKN]8HXE52 q{s~MpH&_+fR.m!M+!V 5y29D9|yXjPb'&}jT=&Sձq 7m`eg4"ˇiGNtkȆ:x>y 71'H:툌ƶ9i zߡB `/߁6-KQt|E+-hZ ڇ*g"_vU&(\rSKlLmo.)'ƭ^kr .am&$("uKᮼ9AߐX2O hnو#&/Ć9l*=\ EؙlG .ZM+zJz LERyViZ& SKQ猨Nw6Phapy?YKw>\);J(i!JK$^2r6]^w_x Ɔpz4h/.VSzNpNX`xBW;j,lmzvk =x9Dsu0+e[([w[8:9|.6{06';"[9=/|M6ӵ^T߈MdD \[V\˝2p>C\,z"MJSIfn4"2K7{xcRwc,![&%E]S mgGU++.FOiH2C:^w@yEy'ăw+%.ao{vxJO Ҳn}iIvxOLB?+<TRŻ.%CR pk,i"_e18hMa''_1%",q uЅȞK@Gֲ8Sam?uPg"0aiI sK!t_9zW3Ȯ*qXGv@Tb98 L-хC6@U4ӲAh׊u+=WykmTz.* ߂/Ci11+S&ټ^@\|͞ qR䘢T0c 2W>x) ;#7F}.u9tLE@93gK95$oO?q@8$v #ɢj4Ym~\l-8齹F#Ə@]8wL"9 %7u'+LsfzH@-ĠM^n]~FfL?7zVs;/@REy On@,`M}a-D'LʦFZKɰIΙl{хScAw]qTrMMAshc)mZǂz8_Iy@̡~u_+B*k\IܗQ;(>e H^ B7X3,FX~C h[ձ j-m=x 𢞐Uƞt6C1Ѩ &#nI&sׄ=C.?M8~ 'g;uK}j5ӣX+nc %`_ `+ ،0%O'L f#K;bM (VkvzgMP Tz]LFV&,l=QbPdq~v:loHȣGyIz(j qMހJ}Ea@' %p`no=$l8ޙסa)[AL{teY-B^ƒw}PߎzqFo͛}an1͂d8.2_G5C=m:7Lό~\Ldn1~qj3UezLӂRC3]H/md-;5W7нNai I=)5Lg Nxw|ɭ ZՋ$zk\!zYK5U5j]C,v"k&jAe'S!@0V`<߀MJ 9}w~$δ{e^*TnpE./ 8W%)@9GQʼne"3MrEZtYHQ*cJk{HPg4<yDhVǓJ?[?9Ŵ>[E=Xp[دC_VHzSԵ0-+Qd-}1it5l=ñ7LCig@Opm4ޱV.퓈,\jYz_enEQlJBՖV^""NXIWr‰ͦ2-ב>Ȏr{l2VS)H]~l7~~7"/"L*6J2J9u6:7E0)AU8ҭ㮋J1A uoHWk"qYp[ Q[xw;TflEN}JF98J6g9P2?1@i-`ʜ)!}@F- X[4ѠRb"7dV/ t]|]zrQSGZ#n⸟헯D5,ϡX$^`BHƦ@k*i]12=?}9H$ܵߖg  neRG'VW*񋋩bֹ)OU *,꿅Zes7_`KU9 뾵EP*„.ytc&]E%_[*Ǫ1OnE>yϰhH~eCǾzܲ sT_$ T!7}fxKCRz8gKÜi.HD$T}<&Ivh=9@pvέ:N7XeF<緥Wh>J_+*5ۚax:i샂vf)CdUC ]Q4P*k:Nj%bdbPPi"1L7~i:ϲ^LfP\H<[ȬgQ}dyE  iY9i)ڤ.bDRJ>pDϯYЃ s3e{KyojP0(B_Q̠d"fʆ,t瞽S^ƺFރ{n=Bzj.;v׊ 5k {4ujǤ[Ҿrb!9)E|0-3)GT536@QoB{LG4`{:S0̶5]Np q8Jwh88O|2QHKJ\!XJuǬDEbg7 nRT)8lmA,dҧEF,%+b>ۣ~N2ZBf8Y6 E7Ʊ=W`m_P=/ux1z%7V<L^9TͶv;WcбErЍx͢Zɇ ||2oX]+P=һfƦhOŦ\P|VM;ڮ`mM5xsi>-_mI[#RNC6& ޥ`KD8&j_Gq!% !EBj|S7d6y(?iEim]ᱷT:{%iJ)O,S"bpݚG&2:Rf{GJDKbdLռ{~gZ;bz8?觽-|d&'Tq1(B٘8tەD(*s1SF:`,в?%i[ȄWpQؑ`4Lb 庬<_CoSDnipeȟ7^du+ISo$.caPn9eUŽQ G낸5OdUW&$5[4JNK)q)1F[>5қ8+Jr)nD000n|-մE],z6BS xF6Ĉ֐ yBOLOY  %?quIǃX\N5.>-=91`&>@.]Z^Iy5$\wvwX2Ӡ~[*9]ҠٌDr<1.Ny QΖ%><&v'p5~h#yc.|Md/AYyhv-@=R6r(~d&xx^b3)HA ő{VJ~*; +dy?izĶ[M>^^_`|Zg´»y jOEoLANif쾯Ƭ_btc+GqlR^,&M!G! _ŭ}7g jqD?I(*t:1Nba'翋we@wNItj c1Ο?{zȫ'[,f/Fb)cE=ނĨbV_ȭ5PWq(q߬+gjwe+& 65!Dp M.\mhι1ҟB Z})ez4b@JO Z(mcwN OűaexyCfzREB`W t㜕{_.EG#d2A6~kPގM&r.^x^/7ieCh#D^aF"@t#h>F3x;'1#8؏Sy2 y!?[/W[yj6xj$A! )x_⯛_V=ډ73_sԞ'8]1 ؅t!d_s֕Pؿ7 12b[YIpMb9m!rJF}ʏLI QFO'\'9_I[ }:k d^^8XP%Ϳg 3 AزCT n-BJNS(aV-6fD[nϱڷIC\"G.o#){Ch@7:&Lntqs sUwi{l0Tɰ)ɰ!^ C s'(r_yPTw34wH14M(SIU?J?4_ -:T:i&yǹ 8(y#-HO19qռCs;b@$e$fU(QfFqQ]Ҧ{#b6x]-k]J.$[DUtϋmɟ$3""fgu>=FuS'&rE~>7Nf48bzr nHt#򁖔Sl-HbaISyY٪zƱs8(ޒj0D>4iZKb"Ծ#:`k)p\i,TN*V;GܖG.5Ħ2Biٵac#pN$H:GAIP.zuň<^Gx,f^&C,4q\Ne\{e"hЈXԿm`;'H֣*G^矏 La~]!+k D[;UaF"fuBv{9U肴e*vb%+m7!5 Cc+j\.sRiТG5`} Sn5`:zF!䕅|9 %c~!"J2E(z CwKpr>bJ΋LMj<+)xrS 0bro"t8W\қ+51Ϯ^2ۛr4*wS b`|VC͌dw9K"-+a*(;x/F}OoVp!Jd@|jejy]/R. SNX^%=(zg06#cI}@pB},^k9g7)2~oxz/Nui)_l:< iue+;L]^$qR+1#Ti5.iSnS]c *cZjR߼B&%EO.M$ոL)Iߤ!eR6U+W=jmF,O|5x~iQ|*T)VpՀDՔhMPMq8L]/oh\C+RyN9*& _*# :>4Hx@wo'[y<NmOi4qQd 2JV*'VA GBiAm@-qL"Kb W^bT32`YU͟^{1b#^t->۝ "}+u~ 1_Kg#S>E}N{kH&L\NGF"(SwAAl;U|V9YBzzHE 1҂ypLRj޶NM+!"$I\űt<"-rmκܨ i/ "5.HɫQf$oGlAӱ0:LC TI+ w/Ƣ>DkA)M$t7gF Ш_4S k GfC* \rM@(x>oT bpO jB3_G䨊r56=Q~E"vÉ8mWϷo ӴPʰ5s+eB* liLdāXJR9&F=M~~H|V*m mY7KND,e y87$77rF9[.ltakq=dJ+uPt0Lk(otc56m>FvN3T`E.hƼ3)̓jDe/զw4 ~Wև=5 y0>>y8RNY|~QƥǛMFcxn:(1mu Z T%kYЅ%),V/P"2˹–X, RΩ?˒@[lzx=:YXl 6q1ƔKQ3`IGoPv=bA>wf>FC^7T5ey(UuSkLw)dd|pZmܧ>Q݁1td""֎sHZom _S͑}A7E?ϛ_UBBҖdJ}&)?7aDkk Ib!_߂}Gɞ>EoҎ<= طS2u㔷!^!. <?qf44$B@>r@ڀV,\FD7c]^Tw4P!\Z҃Hmqr>D@M+ڹ Rԧ@1U*ƛ]dI: e'PI#)@ɐǀtJ#s2bIjڃAqtե=2[}o`&jb)NqWU|\ #*,K4bXa>90ᨷ6kPoOii\0%J} \7+_o-h\'䗣^ j'K;$"'bGmMzC!N.j\(hΝ,wBi0U6YQi(͜.xFa-d_"/(L5v#8%m|uO`̗iEMٳkQorl!=C列jGף0 `+@{,d!Y)޸$o-:NhEj>Lq@[1}QB[Ѽv_rdaӕ/3Mʅ蔉(X\I OSC2%{F9OvmOtQn,j&L=,>n !"{~D߈`s3rP8`&Hq+TLQ9T6u'Y-P"*͚{7ٔF Kh,@6qL. `-{U'f*&ƻѣw`TfpQ?@lP̓4yO^;3yF)=)i<#G2qo 7MڥZߐ:0KC70u.>]탠{\} uh(/y(RqvPL!ZNŷNI^#RNS%ޖ&ZBLue1-NM-mzjfDB=1x/$$>yFfyԗhU}x S`f菉{*>&F:'9hR)(֑ WrId0@ uq~%"JM3 [o}=v~D(Df4[4gBp)N}oWOT5x-\%pB_ҩ 0o RֹmMVY8 l20àNpPnHDV\E Evl(DwXP&A/[f8;p!,m8(eT.EZp8a+غz@^gYwwIQ|l7[(ܹ帗5EQhUc*QvUlӱf!VoC%Ж*?xmm2ފJ۔0zrk5nW.7j+c_*[$_NeD`XSBt77#+8Wgtt!;3XLSpިe?u}jAT0akg٬v4NݦFӼ]j$9m۞~XIodX>Ȣ'Lt۫bm.2A/R) 3o/ߥ`h")#1m `i:ޗ.\>Oټz՜w)1QzƢ8?^vt5d̳RgGN8&{;#+PeEIĦv Mrn@FUrfI[&;0 <.0Вm3Svǧ&nDսd!>hx uA`Ѭ̥gGj2HZrs_ө{ͥeZ@%QTnYx_"BH;xNJc_ )2#vaQ`kDV=FϏBTO 2WZ iB PMR;ɕè'z߭U*@,;Hc?<]eNI]|:*XXg2$i7NE'@23[!MxW֍bsps뤓=h@5ہ C9VTVJr/M>`m_7 aϗeN5'vRl ɫRc&pu/~NZ/q]=B$%Zp4b^B_pea99v/0@֚>/?fmvH|hrFMkSgu?f6>KF :s¤q[%x-bP)s`Y" }ALjsZ[ی&$\Ob\uR3>6\0nkF|ڑHu{&Bh`5-%A"ҩ)V B9FՎ'[Pz8ƸҐo'u\pc_L2~s\{S~:,%p B'J, okNiMQ4$#`3 ǺVjx<7 zNo>Yt74?2; ^o41~x Qiyb3DĄkb2+chut&~ jW`鮩R7[-Stׇ؊c&Xʖ}(u+ 4 '. +TP<\x/;8JnNv1 L#_o d)LP $w9=D&A|JA9>*l{ẙ`=VmD+a<?Zgjz6.)K,2C" rNB #YcZfA\oScl/>]BOh*No*)BIiRZ+U7åS^.7x{ɍ+WǢdoeO(C%S]+NkXбxfÄA^,?3uN":'i~!SMWrS9Cݽ+f:wkis6Ceq~U$w}袱25!GZKěO)*kmX3Fwygf Վg5SP4}kЂ%V[-,P|]\Qy.k] Au&Fi4M@hUd`g%tqKk4l- Xn>Fmm|=#iԫ2iˣBoO F#mیGQ׿: %l]91fQbݏ>u#Ks}iQ簰j\T?>DliY`/ڮ9xRK}Ax\E_t-* j `H+. ήMS2f* 2/@w_HŨad0 [!- 4??yԹ_҈]8s|`Yχܲʯ *)'xCŀׄ [OA+(EDZU׺ކ2[S(Sq+5?Yy֓CN;v+-dҤuDpQSxv> -=w(2^[PI6QH+?(9.[I']wJ(ij}1doI4?^'Qt|g$-* =a1᪆r݋޴CBYIȻQan3fx\7_i*8i aYXfzwWYh.k#4 !^Њ{ q^Hs>4z( P.eΊd3cՉۀ^&$/ D[6 /LS>e)\5}Iu;[([wLck`!4v'!uMrHK 䜌Voy4;Km`|]B/Rt- ϗD/K6$̀ox8I_ >pC$ЦQ^[C^d{43PRSĮ*I}qP(YMX;h'1H b9S)}0 '67TEh2)1*yOEP|\.hW͒ 6\.<`6. v 4-}W#lIEWV g ~;νG? aOsj縆 4]euJد;=F@q2#g8we褒UPJl";KCŞZ _u7zŦOm+Qx_uJ3T` 'BHmUr4U <{΃БnrHȟdZu;<`2e'v_n*m\x g2ńQCTOY'Ngݣ)m9ٿ)AZWHZ[@-kwS좄Y!.X`̿LKPS-Wz6j [w %d(wRt8 fګ*aE2jehӲ<=1v2qyt)JNR7Zu~a~@+2>i}ً MʈJ<-ʡDU \)S1:S6.;z1!귄0ЬKH!0ֳ )2=G=tWI+"^nlS>3N[@&_C=4a>J¯$6L*qK]0?g(C1WVQvw xxESH3o} f_r9ߨw:U229XAqDab^q̠5ysJ\fyO 9.a  C! +0ґ7C+1}x1 P:xMҲ:Ð'2c+߼RHRUhlf1fi}F'&BRO"!>j9GrNP*vkĔSmwL,2% __baM6q?l%cGo_c%`70l&ЄmIZY~T#e`͌gpHE(EVU1#^!i>%f Xi_zxڠ`AW\C_hM;fԋ2ݗ_d*X0za*@ZA3ݭ:Q`~^_`|Z1L7w%Kz̮*2n|leDva~ܕLNgZLLS+m5j<0'B޻r3E Yp"X:.>O}w{]!o s:%态E)vڄ,f$Ȳ 43)4ҋokyZon{] IVsWȝj;9(b2bOn,YCr}~;[yei^Rzr۲BM86=Rq b%wCĆQ_gI),&(b܋,R9r#C!MOܜNѦg?HA`-"E )p=c7-R*re]&'v'o1!j7e?`&l0Սfב\YKS"o߸ep#-$t31T B[\73F g_e[)/"Wy,ϸ:>y}ϚK`-{zNouw?tFyw(s/œ)QwS.1s)]~X'έ] ?X)dnF1tO߾Fc`~ |9NRV+ska7ygpK _I Dݡsr}qrVMTo/kA88|)8uF%D'ÿm7/Ĵ3v|ƌk}έ~#>y,B*xX5$(uxʩ-zpgH4`Ke\D:.&Sf%bB{iA"*DW<Bb'>yƁTc%n+v6\- @5Y߽@=D~a*- R{U{b _qH"Eb94b8 bJBg)H {CYŸ߰B58tp$|< -\Z}Bȗ4 n2SX7,%ɕR| "䃟duT2P1_ib""pSf0]{5y2ZNh3琈6IqeJez<:0g $^_CQlekq3.Ԭs\,;ZUfAoQ ě(90cl@\JȪb]Bi;*2/eدұl'AiIޣ9$E("۩L)OdF瑟 YTp }rѦ֏M|51%ҸmW|+H YGbɴ\Bj8FGlPC-LNP$F$ކ(&Sv F]kl`W^ r(k̝B m`:xbj?y48`U&|b$ Vsnj` 94im$RskO~E *,jpR !3}glo:UT5,CY17R D6Na.w|Uj2G}5<ݱh@h S=Ys7Z;D03PH,Sy*3D8Q!rnRH/4|ogZ=ӯ\(`YxdlwjEE 4Ah}n2wݨs) h$ӟd &XCpt+)^tS4^ @4ԣ0ʫ ̖O'pn1$ @LyVӟ'Wnhws_"JSZ4cJUJ2V\4`,uړe BE篺3Εd*ݡŇ%B8v1 jA5O4*񛧶VE.3Zz1֪OuKuLHWK2t \}xtGv+<zN GXFNb{{Ƙҭ?}|;Q+[03v_IPwpdIN,IUU! wGf}+^)oyCC4cb3E4N-˓v>@ H)־AWbP v//DAHT{F{#xYg՟5.4Ȭn S&#ZkvaAC? M' m{bGӉ9Qՙ]9RWX2PN&>=+ŵr[4 a-~Qء>.nfa~NcAt3v4kjo>aS".v/Vuד~dArI](e얾v$+X7x#OȺ 7aQ1D ʇRp;\c1-aRAIY;'Qs$ 5,ay 5w EpQ6v"i:Z50Bs7[rk$_q (y$7`YOgx1!9FN'ثށ3Y $^W@PԿ3n3vCTyNW٭ $ooitUUmjyrJ~Z%P}@kvPJ!ǵqzd+@?_Oan̚3`a%PTWӢ.ksYUcԸh2 ۚߗ(y-*Rl;,%(ݟĈ,@t#ιA>J\Ar准Q+6 MPxhjGM "?]\EA<.f|ѹ*t 9mܣW-Y(.edDVzY,9 heΓ`/f| 'oӵr/jH}Nqm+3`KmĀ0[a ++'Ox>\;SFч5[Ҷv|d$%G!˿ !fAEa8jݙsy jqZ)9ՐzۣM΄Wap~\~ ba\7@՗S0>5EiC䣟N2t0Ukk1DfGp9tq'8=k|c40 "='xVRr qp[%[ ¬x{C$F-.=FsK|TQPOS߮Tĉg4I Q#[L13Kq9<1VP[!F#j l|' tIJ!aaAUx2d2V.$3QP|ޑ.=q 6a&vmG9[ng$=bݲ='Y!1;@xՇB'դq'qB&BMXP?N~8ZsnDȒLe TnLB7'᪭8lᢓ_?*gx}C}șmC޹*z&PM3^~oSyo~gO7 ~aڳ% G%螿|&jw>wizK*[#'ST׋1MPH9Lz ^,fqBK~9Isl`2VUhŞC*Ͽ3>0[v/iA_nSXWSsuAX~/#ȷQeD·7O 6B-`W)H_4%T; 4f핀vR3A}fX(o~X Y v&>7*|}iE7R?9Ś}&";U̟ 6h/T@יd ^S:jKIrP&ғCƈyk sp1lվPgڈjk ۗϜ4։Ŕ9 #V )Zlv ieCz2C٘.rJ,i}XHڋj pٸ3`pL@I@s( ȩ*QBG0+:"Gp'#fDѳcέprMVE[Ťמ@Qb.}ssG#UU?v-k~?& CZ&BWv?*3?ĪMao=.aJ3/ }@YBqoC+&&'|8l?FSoF^v{~Mi3hv<onIZ Er7}; mz KsOWǤ%EԍOF7(ƅҤQJP^.qDCĬ݋P2Pbq̜^=u~7}41aZ{) oeD"?FO.E֔LSɖn@ 9JF_ϣߣX,HL:{Um/,_,FJh $ ('lln_Kq+?^i,1x{3$^ tY?>Xwd\1eB <4 1; ɶ~1s'X"Z6:7I4pPd hւ U]g%˗^e*BߖWo:Ͷ Ac>c tx._^X0*@* 5:l#t 4ypz/ Z?9qF&̈́\,]uQƺՅ,HՉZ1 !'Lkk_}KӘ:g>{>3j5S2ƍԛ5҆~eR2@kBѝ\jU `σY A5Wer+?2{14iz/(_LIV &f0uPo4>[[~ج6ur]_|25/} Ge( }1Q Ϟ&5 3bxy];z"!_ˣ}n(h 6Y*WsB6{\kXBgٞypzןŬ^^'nMHB7g>a|?h䌓t\7dMrRdb9@{Gf K`~8=gE.J#Ń0)pdɮ(k/7fpVSsܔp9.E>9bJ|ٿBFj sD\W ,|ឹIhr$S$c)鈄TvY+xP"/l\VVU-#hu'rIP)kr}>FIE\jo<%R\.+{0+FmnNjҺ£§4?wP*mg]9KG99}iVkW =lcXkG@3v80s5 kͫ^YSd5v-W4Y4ј/c}h0(Rd9I#XgO3(av}M?m:UOFBaRЬ1jk{"0o5q}T7Wz:;OdUuwƲ'탕_aW +Tԙ zj5~3;S9YS3$A|By1&ePPMIjC"6te([W ~}E1Kx4"+zSqGTؿ)&*j /l9q\+O PvH b(}%_i X^U{:QȎ-#]A!xkD@jVEj-370aeHLTƭH%ZJ9ӎ2~e!5m{b|gG?nWʏIuZ|ޑF-D0 L;:&Q Af(^v,Tk.]A@bd xy\s#[ăOT><8(&)Ȕ,60_j)S2`;u^7{]bvżOxy"-1 _2m8\|IHi>Q>K.K쭥^Gf]o]D*,THUajkP71ƎHr,gYR`[mCt[q+xTK $2Vr9T^jfmÌu문XVMsRY7tC+=0-2h Cv扒Uҷ*ŗ%q^xd0t Iyb5$<~kޥfxh}\Z5[F_V_q' M1clMfd=Ef7/ ΖLDIq(&41  b&z}B OEyL;z~ ۄĊ޷&xmc8N'Նޞ;K'6(w0{upË|b,dp3+4})̌~Iu.$^Z->[h1hRfzc!@%I-H,68r l usx?h-bl5^ώ#Te~-~N&pނOwqةj G$U B%0f2T)1S ^-6bfg?!7OBD%,vR/s.Au'+þ[hL7 l9QShR;uqpACܝ>6U {hbh=ЫP(͢UQA(%mI۴r.vISQj-T7@ƽH\2Ӂm~n%3S%bs;tX2/tU) Ad+j|^n37ms nFg[b^p gHFbywg} Ys<ȍ5TT%F#5"2G =1S2:Ό󳥍tR),c^*N FX" H wY Ӫ_qs%ʷ @JsGUmVN|kbQ%1c|WFٱ0bV CVY"@ک$iS CGeDV"K[]0g4k !drgx* uv9 ָm)08, FTpyJ950[xbgYڱ^TH}"@e*}I`j^v-Bet/ŔL®Zv fEW¥T'um)Y@D 0c \H iT`DhS J 0z'=irÔ  Ѧ֓_mϣ$)B+DJ[i Vbl cew+1=0B.OR| "yr@{*c#vioOe=gP)'Y'!~WMeU/h(s8 C! w&cN5mTsm=8;N %_)\ ٮ;Z:Wy(|:fg ӆW+ 'PL%@n27ӧL$Cv `zz}2GDpXy޻Q;+I ^s*b A7"{0 MQ# f"-[;D5k ΣsSOi ‡G6أAsbw+G]gB*@MaTQUm.>IHtlTXz LlOzxD]Zb!?bm<zZlbOb&K0rߤM3I?(Z޶ Q }(Ytd⫒L=NMyI' 7~5XP`ʄ7PF$:l[[ F;V_OYDWGH=Zxb|W6>zΗ?wW&W,zh"9t /)gͰɤ45_X-ʥW՝NmR̐sE&HZ5{^r=,6ET4:j11FYdA1آjyNpwoѤg;GڅzqvO ;sp3Qj(:>NַZRVnؐoK"t3ŗlG lnq;9M_5 o)CqB=֮?T'JpڞjR ?ՊV]Rjd͹Sˣڊ-їzK ;5ItaryIۮ6~t^ ۗkF/o>*Y瑜B$}?g^B@P;+}[1>[|wDC7qgk L.VɔSwschPe݂{Ko*0muPR<t6qs5'eq7x) z V;1קS Ĝ"i.M6ZCrMXڨ3JHv@xCKzp"A0 :%d۹b+ ,԰r0oDr$uL ۾@Yo+qa&rT ͆ZO9|Q/.D[VDx:_h&F0ƒį >ubC.+Lk*r)+.\[.Pw}ߧ-]$6 aAӐ5KzbI]H}?1Ji_{ƮygND%IT^ iCLnPK%Z$x bE8[& ;U:w<=b\P z}RXMR2e0 9''qb.yM򀥠j1myr+e`>u_]}Q:uua>Jϋ#)J>{Z 7K1!{^T30 `^vyGA5O e] {V?.|8@lq+yl(DhˎrxMBOdj=1o 2EAo&N{^U@mPc j$0=c7T)q+6܃G`L嗦d]JgpŧݺU)!,^w|S!ߊ'WI<.fBg؞])/;.+cP&?P^ ƄᒜuVMc6E?C!0T)g($Y6UnjPPVҥ;>21婋/)(͋{276h\\~nd\5q,c/Ńҭ}EN!󜦓P8DoeUO!Ⱦ&reJE2e`)(c0ba+x̔jg!zi1-HRp] QE\0>v̐R,* 2F? 2* n\0y??pKq"7` L B; f9tQybo? Z<.)V(ٍSăU$NWJ HFu yk5SWvU2l}.g?psr9=/@-L'%bCOi"`h#ھ}^EeKo!fdj+L؛ L`x+; (ɓ,U*ΔdiJFb|Xt%кk׀2|h>ʅ$=|s"ELj80H  >+ S GJ~JD5EQ'7buh\m|Ip[\>cKϣKpTu60ŻLs;0un(gPmߐ{piU!=k+ ;A0| 嚱' xdMze .QAwjk֓XC&;vMڇiTE#U=Ev_Y _k ê8¡- }V8{-;1==ʢ!>S9ʌDp*9%Po>H 6HygGx!}JCsuv 2u[NI.%ί]!lKD P萎4e\5kjت7J~KūHf@Kba,c#9Ej!-.b ŵs.ZFK(`$=0<6Ղ]݅ri׹ H' G<J(sfӨ aPb `zĈ=jkX-*w!ݜ|`cYkٔ(G%;2=IJ xVݖ9~)>MS2{ *I(<}6r۞&fr`†8K [~%^ Bv@n8W-ZÞi/Jw{Yitg].QbS`4nFIֹk ~ab"WꭂW҅$mԞu*(~{RoQ -NbgLgaޝLn%9} j x^X-3:FW`/$Aw:*5{( mth5Ft Ծ#QA$?3X=H 4JҀ^ߎDXge;[8}ϙOnP@"]ֶG\(ua˕0 X^>Fˎ+̓+O]Y\sh@"P][y|ݞ{HMUk=J/ì3ԯ%=o+[p2*Qk뛂LIa!|g3im4~kf}Z6 IIʭj/Ӹۤ@Mrvm\h]Ac[ݲ@h;)J=;>/hR׻>oA/ɥ ޗP0'ÕS6֑ޗ#]O+9N{'*3&䝲Bd9cIKBV=\0O5Gؗcfб)| ^ ;(/wIBssRk&8e%mp3=oVޠUvU0dwb?g{IزjS,E imKaLr0󪱨T>/v׾.ǫOpy0n- ,ٚkPZN칭-;Q^;͔ݲ!2!aiD>g]|ipUA\~ (b% ˜Ha_*&OLIdD.MdOF‘{cjMAiS6#PyZu/o}TٱY 肋An^D4_ yvI?ZAaa Bs=;rSVykjGkOmTSnTv,0x椪3 7x ޻x.rZ6VeIخ7׭&!Xǻۇ(Dp lΪKLey;((|sݣZf0xzڊ@j?8qT_qcM/rm?yqb#+|}~1P;}̧"ۊ!\;;nޘ rLwZ5q 9Bb"oousaҰ'Xrr 7c\~T- xNY٤= ud l7[JguY:I3XK$&Nq (F 7[#߯hX(&s1St)=PyZgivk`)ձ#Cg'Z E|OG$qr֚O}jn]7E#빁..^6ylv(ep3T5Z(Ю յ!B?LP[ "; Ai^DgB[W_yJQ Oh$h |Y"W}Elҩ^ 4ՌRmA_'|3O^ ?Jq}!(`1V,yw,(<Svev6!C3RUVYzyYFvW>wgҗ 'Z)T88osӚv nΘmcF߂&v5|Ԫ2bi ݨMF"vQ#|N62KJt7j0Tzy{\)ZBhN~akV 4fN=}k([:T368Im 7P4+7/iȲŪdJ㦫`V4Ä;+ Lߞˇ6Z9J Ng%͕nXv2UAH_k%Sjs eod/%w ȳ+/Xؚ۠AE*:Xxu8Rz$z_`,@^w88 twco>C33H@ Fs&a{$F*iޕ\f鼉D l!w3.^=0ne4$>JAŢͭM8o2Q 'FLE,V+_$ }٢KɊg޲}*'2m` W0Da&}vJb:4.^6[d`c#SrG/TS]^4Iѹ!<ݖJZc0aVE׳~;/g_I"XUϪgC(1u_rA(ir.kU̜#̄vbC xAB_V)$N‹ ]yBF^9nى2fy\kr/DADJ]ubHs)?1UaA"Pi. { "V=f_[÷sBc])9|('ب[ $=pTI[T[Z1^# hϹ˴|$L7#^o_Z;!FoߧNuTQl!5qH_8DLQ5hsEl?¡MɺX6nv=7kjWeA 4J4b5XTbQ(_5z,A< ]~ATWl*1/g ͫ>Y0ˆkAm։+T{y7+ԛba"ׄscb؉gp1k38fj|w)c"$'` JPd^^Ad~ mʰi&$D:j-?#e)1>< kOJC*PmcpՉÚ+T-NZVhHnU.zՌO]@~Т.fVDEf|Nr2pPwAį:@^,e{ Y*( Թ~ #d1&ZdZ0<$I6Jv\ Yn)[>YxV`=>j}JF\d#WC]P[!Z!D=!f?v|AŃ` K.P~rtFz@9DOE'7׾Dq/,0N#&/غu?Oe4 |ǬP@Byr.iUJX9@ϯnxjv>c2t,qǃkTA:Fi34(`A<4?nI!29x{  c TN0kz)7K*g -.4tK I'.i}"I3EU(h ktqEH/o-ej6Ϯ9]7v1FU'.Q?j߉ԉߚz>IM٫yjg+Дf+18!Ec;tw"%2B};7f^ؿ  8녆}"j]Eե*X.Ec&㣟/ 2uВ6cԺ! :28'!?K0y>!d#ؤ, ܒ,jmZ\ \ 6t,e>^O.,fx\U`bbhR=zQdbu0_FM@@P3'tT ?qxֹ5۱O-.3mA!,!MwI PHX`r~{fk VP W~d\_ƎrŔ.H+pݰz0釮XEi2;އk$X\َ-!<@$M4 ` jT{̨[eJB2l'fȟ _Y W M(D/G {GF5W3C[T^v^ )j\xT :? tŖNRW@\Yem; ~;b,fPTљP*O9Gfr㮂;XbD)("t1_S3XG=]`ĘB GQaZ73Mڬ5[YW,u;H 4,DusXomPYޞC)W )E^*Aн&~=-w?nwEK&MM>Q`4cynspU" Rw#:zu6dsIw 9^K^KyՄ`%<߿j 5 ]3]_N]h{zz\Np{{vaRkb!:xA0J=!l|Rh\Y,z~CxKűFHԲ_E^C^UqH"ݶ(Y!eJZ/]o*5[zH|OƱ zc"*,ٳ{g.Kb=BH|G(y%-~Vcf|Gb]fVrwQT+'Pv'IQ2)\,͹E0-`%'IFzJ ƪ6#c3V=@ٕYzŲ}띎|!W$bx7v2#fbM9s-:8U5yod}޾%ݯJǃgR~Ng—O:xUŋs_1 1rov 2PJ[@ҧ Jh-{VfGg+gP,—<9. 3њ ÇHbb MaOE-,ՉB5 F&/ckHe/i>Bָ(Jz~:vuQd"6?G ԳXĿUZ"UBObfoQ hś# 58(l(ܗx f ) yYBWD;c.\rpV&AHV+IЈ3es[&rB*jX|29GCm'7\H,Q+c6hN8YQΊJZϻAKJ<5-W)B&c?s3᭙Să$e߀?_.-pg%xTm $-I&t0 w/ը$_ E&x)I[,wT s su o6ܞ3%\g(Klޅځ"#h[|1XTaJ?]et1 ua 7Jf<#{J.spÅ 9Ga>ɞ1?vEh m4ʫp7T-FD1JOLIXZGoգ'eDtOHyˤ<v9.z2рMi1[᥇_i/t;lݪ3)H.;%eaˇ ( |)P ͙b_J?21)ԏ`dw҈scN:r?_O]u ?ZyF>jC'gT]QS86KAl Д4 K#,<"ɼJ=\ nMK"uzicy'W1e,Ui:Z9>r(7qբGȏzy*dTet۰Rfܪ\Z(=([['N:ՐݢiyعczS{F3ݵYƗAWo+9\3i|r3h9%1n➥hB]N N׸=^R`5:eljg P,q> eXȪ$VdHs)Y LÒx]g+0\F^xǏW}} Ї8lSޝnyX>~p#%1i>'ɮ}j)>7mzy07{<דh+%ht"{!D Śh@0췝*ix[+h7@T1,k7\;:xd XQ wƳlvqfbF Uh*bqm@oÀ}U.mbM[UEu\v$GzKe=HTG&=.oc&Γc4?,_1-yv[ J.1襲 Q:PqM8Hо3=8"j81ʠIkr"o4`XtYU7pxdЄy}@2ʋIYW ֔U52M11 ESQ6Xq^gVZ$է +^p0^r;+DٸS -{C?^I~v:vW GHp@)ٌ2δy%SLڐ䶱%Xw%Pa`,?aՆЍEiI;H.sǺ: Kǂe " |DNE5 WG l4ۀ/s4ΎLyB񖮁Jha^qUChMNK=oƎ?`י0ȷ^5G#aeg57@jCIX"LdBzB֚dMHWH&z><%/Nާ+ OtY0`)ERA7n)(aO`e&Bb7ru2@`ˬ)$1ؚY%-ٳ]Ȋap_./]av/%nKX m)uc1a~#@먳D^QRyXV9(F ؏zRAb^xa-cW:~tav#9wF1=ÂʢF]G6#y4c"`ƧMZYN3OgZ!;t^j)udg 3]u_`ԖM%7Ru=ͬ=Bj-쏣qXCHwy=a0@ tĪg?'s6xMM{T|krSf|{fL/<m@t;w{|<"f K!֤ GQXD_8\7}0\nv("1e#71#9$ϊrƚd4-Y¢zc?|޵)ZGkyl2R߯yYW|KʎSM z0!QAʐdfUP)&mUj :"t#;cYJInK!FBySaUӡYCapDo= U7u{jjSsl$ՎQp:ݔOYhVyliϳ1O0zБ۾r9|LɎ/2cJ^cv> lyBm'2U1θgrb{ա֋_HS"i! r/6>&\2vli wƪ-eIJrbDtgX%,Ҿa.d.FI67w^ $G@5^nUorl-C( b}h"=JdTrUukS|f*\IF %8)+ X!%/=HTDPbVj9ƹ6MPk3ËZT busX enq^MYVXl܆fYhj{$K zbH<Ia-!g2h"Q+ٟuKQ*ׇQ<'*GI4H֗mUQF6[-P0J*uLRX'.y{owOjfQ$l^ǭRiTnDΑ,:)<5\*qNiAa{hƘ^7Yi3~kd`o& MT dbM `"4Ym! 1ۣuY¿?W)oh^~.FwUܢUVWWr_U ،hJwC YD:<)P೺boSQQ,nH5t8 A$ q<ؽz;2{Wj{Ox`8D xqmR!MR1zWqaP5@IG"-̹8ʖt]^T?cN`amhXYdVptKՌmjFCJEөer 8 =9z[<—Ib?IԎ8\N Ws㉘: ׳1\r+QfB4_;4V䖉y nuO5Kޟ "h@P:\2jϢy8- j<uW\|N2q]n0eF{o YqPފw\=~,_{N|,Μvp8Z+'?n/a u2]KqTvٮӏ1xP),*gWzZw}4}am o߃trv<2S4k!XRpR`y< ThPŪm0v qɿj cZ-6\=H#ߛi>|$ L%ZORhě.+Fwl5] @[4Й([e %lF[ZϿaI{4V.cKG-XwogS\hmxJ?T2$憹Zz}]duIy>x׋ST]@u&6WrxW2= f Ϫ@3*upD311iiGUݑ.Xo󕷅<*h 0pQMwoPQXu<0t3i6+)xOM;2\ pU`#ap?L[3bd{ =b+tq,r jq\P>5AptpKk{pls)]9X\-Y|3Zö>$\'*~ޒ0RaǷC"[؀hRrTTDQ'1CXcz >tsWp~Μ3~qXgc #⮋=s`lYz0"LPyd([.R × o!Ń +(=D7=||^ xXLգmVH0M/U[SPS1![]m2F+w d:F* -4-~],X뮒$lٙ܇D =+&Uy~^Y_, RMvTM:Klt", !8D5` h(jLr/H+O!dfp $fp߱BzSH{ڨ|sj?rvrNnFJUwf*,As`&cArLY Ssq]>omޜ®zWZCry(b w%@z 7nЮ XWT1Y)i & 曻[1{~lʩ@ѮF<GpS`PUvI PjQd?A{ϑR^m X-Bl ,)W-u7k861< !VDfGՂiF,-Kg(5sЪvE4;Q\#IL f,9;/p@ry! z|c'_wG̞e WG#"Om-BuBf|-AvVzKQ7oņ 0K28YŢ9ѩ–|嶗/Cs]@Πm?֯ =IU(i,o@T5qV?qiA^C͟hIG*weA~w3co'OE&{[R@^![ջwnullĚi;ܸ1ҽ0 U ϫ Yl~TQgͥ{6QtnX@5¾ qxki A19kgwdžoh0gi<C7v p8O} aD7Զ:i +35"ӷ$F:ɕƷ{XBC0"S;:8'*6=&ut[ jd)uW)>6fnaj~^A(VWFҺ Bu6R 4x: ddrI#)/ω3"(]vp>[[Tǔ\xQq #7eZ5'[@`GC`I3g71J9Q||J>+kεMbW4gB*WM {])p^l{-W<6+cʟć|eYrt;;]!9E(lM'AiqHck@[ NgXF~ B43qY`@oW:9z3z:8"Ly,b!`h~],<aϸ!GB!Bc(lͫz<(}^lK;q/РvÖ!@x䐹g39fڅo:;`5uco^83!3'$;%/""O븩p/]h6.gM\}9L?t@y bhq5(M?Q-Wyb;RQ~Pe,wzzaQbe!VVWs)‰/͊B8 _DIæ0dwtXtћx6󅊘%$R 0{|UB쌗1 -iIР0Tk5~}ЗMm}  BH5uj8A !LO'k yIz=jvwsBPԯӥ֑gs\P3d4L1+6 {^DpҍV Pi+߈"֋M@ܗF 2zظ꟣^ e]jkk?.n91QT.fģf7^3Kg980K8#@?γ?>8_Ϻ֌- xqzFHޢ#Ol 0i5Me {u's@[15F a6L6٭Y*cRY8Ŝ]CHXE7ѵ|l_ u&oxƭrs6038@ϲ?n $mOK!g{7 k,7;Pvr^i"S1{(jtL4o5 *gê?< Vd.Nީݥ83 siLT !4ݙ87RツPj]qX".eIeB--U3PG:&`TⱲ\>k7y/EC}^85G^ fT]8‰2Q 轑H8er_zhmh;k>*!+l\*OɳDswu-kft ~zdWgͬ.YN=O~ ;X\?51)`N6U$ AB#r;={yNC_Ҝ ]]"7Y'0#쵲I}9u:uMtQY,/cπ\X%a+Gg_N"KGlTuUM!PKQZwshb b &]E@%&zF[AL,<xM%z) Waq)!9/=^v]$C"XL&1ILiZS[R@r 2=-=bM,p K҂#4uo/.7j{a)7prMޜGq;2(n3(AF6lK#t`? U09Gݏ#zOy.`lPŭ~G!t}>KpC1C526h Ĺ]RăV5PS)=dR 0'öqE/ӭVOm{m`T+v[{zSN'*$BH߶MGk]Uֺ`5Hx/VR~CŊ۵Ak`3$CYw/Jm{*6FOئ~cswV ?j$-!Rƥ]@{|Eֶgw?wPq=nw 8^ChR ^6bďpZJc)$+X. Z]m=uʑ_VS_ -,'u\=pis+Η[ҳlދA/pvEɇ1;˅/y9RuGl~Ym#"Q!F^x7cOfr}6>H%S}5BL()ōhY-V*(:\deQ0ENڑKk,NyO>2dbK?'k])G,ϸ)[R, 6Ke>a!Pl<; 3RbZUٓRRGD35 .Q_wU)$(>zh˧S{ X]/es;9זtnO-ꃅ$2 Ok;(XKrUb=%wJTt,!%#o([K?@oL{EfaT[⚧nn. JQqfG뜓X*ʧ9tҬ!מmJV)o{yuqp^wSmr}*1`u>Y4(ލ@2PRQL1g;еߵ%d &k$&FDis=)hE͸c" rVIl"c̨P"< =Odei~FrO/-?bB(>V1ĽN̍asdfEM2`W8!:z"ЦL,wL}v$%mj;} K\L q m7Fa %͑%tc)'y~,q(yn"Bq B܇)M:0VrX9, řeLj2ܫK6nJVbꝁR -/b ˗0o8پUiBڢ y X2`q - ab2=VM/Mxo]1f=+.g |yZRq3XꒊCeNI|nB釥 n{dIE'&DùT1oL)ж6+[5\lNtKМy1]S굒+1Y+|"5T,"y4%a,ܪ0U2qayq8NĿQӿ'\_V%ZZ2m2 ,p&yd#…_m¦mfr K<6(zFVCs\BJ681?91 kF6)*ڼ+a]y(vZ皳SߎuF) /i6UFuůktxܸ+϶cl) i>/t)/!+n#Ŏף$}s(dtT2$L8P;>ދWM\cpF+մa?z)e3Ӿ()hOʀ9'U"uth!gBcG% ؏[F<>ȶ߻Ͻn>A,tյ[*`R*ĸQffޡ +R:KI?~k8E$!)4X@-}r }gcE.lG .ON9UiFdwh܉"UhQ_VDjW(?pm;U]9k tV\pJG)yW֕ZYB~_v 燠O%\%VQ%Fܲ BKp?+F;lX^Y Lgȶ2nI j^U)y {AԴQg[,gY挒/:mwU]>ȴߣ:0G9](JE:QB2THj8.f=5tH6nߏ $ m6јD>u7m%c v= A[~٫jߖ4Rfer䱚 CT10AwLJoGuek)eTH0TULLH0:ٗ~%7ꔲ7|˾4z@8 gqwОw`33Гְፂin-Wfߞ2 @hkJ_)YR=djY"IA,H^ +H,ycbQ8/Jj=ϬvSTPCL>˰(^.4i <=FNp292M I;g U 2y{W qВzrNa8.}Gu a۶y:w+>fq*YcVW]!0/$]s;](Q8 a1ƥ0jߘ:rOpT}„(>DPLSa ׀IFo<[n8_gSgvnc׻|Mvja) ɔHZk[:`&-!k`FzB?ݿj^akz j~ܪ:kN:fzn>qCC^Q6Ի {0[tN U`?wW[{VB𰿾a/=I.cZ!lzK ŞÔڹW;q& ZʋeoHF!B}~c΂m*G '[1:]2\}\Igp}+3ùCo%D<3G`gN%Z@sBuN5xPspρYuJpiJگVv<=y'Eo mL&Q ;Wej+&ɞƐ+>^@fJ^ҹHUl$vC)vйMZȾlX5vU@@%;o~:xq]GGy5r:'Ǥ@i_ZBz ma{}JG!X˵F(3uU\[t.+p,KٌB"T+Frckn|TH ],&+JSbՏ-/vfS)"Xh mUGJpDahIr3ߜ=7?D`ˌ7󲪸Ph-^Pfq~aATCJ(YRXZK]; $ 'ɜvXq~R0`AikL2>{/(M#li_bv }X/wmY}`7ٰJ-N3:{Ɩ\~ kM2z nT>ׅLK`6S׈̄+FJ#VI` &Q;i3"!ZdVaGg;8ZJ;Hs )ޠ'MikVw+t3i ggf(v̊lw^9Bh9KZjon{~`EQ_  C=Ap]AK7ˁ%"9A OSY懼Urwn@U}Vxϻ> l(/<;RpdjX ( 147,6"t }Tca#STN"B-YkE ~33=ET\d[;BZ7CD/}"C%yawlӪ2,?+TR7-쪛[z`~,X!-oC5 fJAWDZaT7&TIH97bOƑ1sT9ъcTLwv۬nzI_$b7NKJKe"j7hKhDEh+S 2`]arYru=1r N2r¬jx֪0~#-n,[F#Mwt! )t1K9>؜x!Gt #g.nBNju^xq_ "5R,DJ2;8Cpmp֐C 'GӿܥbLR*D'Umʂ̓> Ґ@^Qef4I'O"W`sU5Zzcёv{^FD&== pFBlyN@[[SS;Ƌ*bdԓEuWn `= a60l2XM_>Zlő V"m?Y|3(MVRII$.'K08x9#X(qXid%:hj~S7gҼ.u ᱗͋'a.?luD=(1&2?KcxRvLdt;ZG;gd֧dA2=5 _r!ʽ !ۜt5 }.H}wS< S` Q27HpLRחTd r}IɎ\&WHLEu174 yƬ̚M ,iGd+ Ӻ>cLKA UWҫ)Wl{@tf ̈Z֌tW0~†fς:BTccg9*?]=[H?ټnf= J"t܁kJҦ=t r7?  0)ǔqyABFprVkx1UJ9gAJOe≹]Xc.͠U .HM jh(nY{qA4SX(眙1*復Ir;  q|zJ?p4A d=`Vl#Zd$ 9Гe-=yV8dvo{e Y8%;&&Q=PwE4pS gRt׵"@1.$YL9ʛIx)GKVZ\2k Du{KW7g!@1s ԈcR ;΢jigzs r^Ҋ zr9n/cZ}˜[u=y4=@[ݥ 8J=O: Q(kRGH4G.ZRl,+qZDϐsNc$UeI[r8kE6m9ms&ί晾xwƊʚOq[*;^3ZC;Xȟ_‰x/R$Ja~ Cz}6 ~ +ߚ>Zbrf_i!%A3u%o=UͳcbT_,vQԺ=DžL!Xe`n5?nk p~c\!;o8W!"4"hCn4D36(2PL E蔣SW`@G-!sz2 Z2eŮk"v?;ˌ[Zkjka 6b) ` JEBׯzdzY>pp fix&)PS7g ~ ybjÆFT?^Q!XSj-D,Vnbؼ"\~sc.:D곩& s^$+@M7, Dj ~"C`"aTh&0BvR㊅溧`acvB(ȨdܯBÞLa8P9UH㛛BA7 `e'92m iY?KLUT-jOSoqwЌ~(֟D^V1::px=8W"$E y yPıU+spd#J̃te.iZpa,p C /?d1RαM]wɜHPy޲5g~. r:iHz홠N[ZWfN0L@:bZQ $ 1b*Qz̴ī5f|#4؟V^֏W_|IXȉ"W5#-6F N@,1GnQ TI\[@/h6R Tˀ(,}&ɲD1z;=鐌_.l_AG˱ηX&c?Xy$Iuj;IDKՏasqRK%U۴$¾ANNPt{+.0i31ULWb 0t=h)9X7 ڪjўW ĺp0*PPOfx 61XGporgY=A>"C>V4!^%G^-}jyZjbR5&'~P:9WwY7j9i;3pu+OJID>{_?̫y󺾪񞤼n"wMtgQ9fT ѣB[%lkڒY)3o߁xJi(:'Ӱ etG? T47Zj8Yi޵]z(btsYM^r41<<&:ݳ&||>w%29ݠejxZD|#C9Y"95q0}k:U2z̏GyS<5bW߸۵=>kg>!a4GyHO z|Zu:zEdc/|y].hYbTUﻦS>'N#(ָ$t9vMv}fFc@Nry~Kz{g+r2 g˾]y `-+{D2t,ύ #"4O9wɌa < iϗ??n^1x'SzXrHq!VP8|_.$sOkˆmQm|21ԊLd,\194-&,~̓Tۈ 1Zt6r@$)Z3Vm%~WS]64_ZjJЗh2gO*5AESξlSҿ{LeT}Ļe*T:'c+/,D:j>t;o :H[ujhqptEӏ?j1TMhkf!u=U+q8# j]H+\2Qu(0!SH) KfJhHםkz+֚%$y" nZTt϶Gu ĿD-ω|'TK ww[v9n~'G[!2r 4|j"r#OiW>ШP9Hnb$4Xc,?ho%AK'eRZVaW,K@P/9r=(Q؄ȩ̓ *Wg\}ޑvg߇ƨ"ź4gPYk?KGd8 ime<ԧ]i*["V<.@q~gxcF!ʐI\Իem~ 0X4 JsZit<,Tf)>}c̡ؼL4ͭXVz1Mv= Y}olC JOsEF:8LvRfec<Ĥ7/A*JR]Cޢ/f1è2d#zOh _揝j@:ٹVcO j !Mc \ȪH+2m5יʪvQ>e0cnOⴉ5SRPSӾ%;{Z5Su'q-'RM^ /7Eo]w{']gHTMIQz5`ItTs8Z"IPg\wWŁ!p8vgj}:.24o ?Q FNeвË^ YVv2i&'އQ;tGzquRҺI.-\x{xTk>Ӭ {8޺2B-RGSd.6PO9eD2C,NpeBܴյGa]%seh>O #;eqb s$MO,\S-q jr87#-as@`nyXNA3l{SZܸ(r: $W1}jI2A)gṾ>U}Mߠ 14'o;Xoh ;xD|T7ƑH(P)~p{KxS`3f5ݭJ-B,† Lh/j1!PXa7| w~AP}apn[jܳ޼PGKe5=tm{3i,P%:TMkјGa|!9~x..% 9@<(OLeH$sQBHqB7óq2~[E=7amJ+̞P@.g3>o󣫤2Q]$E,+$x*.`JbYUgu04sM KB!ES{u^(6/n:4D&ڹ^մ <>w( ;ex 8"[g7Ë'lK5?RCB NQ723@p=3fPidˬ&!ADM1ù~pUOUjVJ$^/z$o@5٩&Q`UD_0[XAi_UĿĕBdOR),HEtFЎjّVV)# ~ {1>ҸePbdo.EK݈6 mvƝ`9h~^~oO#TPX`fY Nt.AH'gwaSUC{nH:XבIULB;q6,< H, EP.4 uujF&_HMVoNKM9q# idr3 -ks8>oNtCw 1& F`hy ϊ .ƚ*sbvoiWdnW` v8ưHup/}mẀM'AbBȨ_!sIX}D[NP?-RHktopdzЏ:9Nb l úuupUnR4-b+Й*n^D9n ܨ4};&ut J\P_h7Fϫ6)zQ; `JU'GSmRO~7]ޚ:f4ZArIIi_A_fE1έKVO'"Ll]eo4lU8Ns2Lβդu%;3+{iX+"wQv pE+z*=nHڱ5ҮwHFܝ1 Ho?ppu Ϙ^,ΜwyGI`Ȯl=Y+M UKl "øB,VopϗW%ٗ{8vQb(tqڍ7rCBXlLVYNVǪNB?7:^׊&RⵆaDðlֻe#@"[u8viРO@l171tŶg$&r^(;3KBK`ieH-ސL1W ';H\M7 ARE#*~ 5nyM/fy݅hXaaTWn]iïDP5(n1&B20"^JAb ZI1n>~D( ;㝪~{GBr|P4ߵQ?MӮF&Z!/?4JL\]?DGkn]cz ({)8z\8d 46w~;O ٝX3cXUBH.BʪQvm4;dI"( LƗpfU^sM C^bO'Œӹ%Ӹ:h?B[k kg=8st*&鶥u5l)uSs @u-*FXuװCQFJh20(6 qu52ڈAˤ]cn@$H}|{\*}ܖD,<3z0ҭ7?r7i4.rd8aѝmu{($UAi9Dfu'B;f7g3|i׉kC>Yb on񛏦rg:֍4}Z$@ܛgb(J qҨܿA(, JfeɔͲYӆHwXL_Ʌ|͚asSedP(^szˮj|!OQA6l sdp X J͢ K*nR-YOΞ9a'VϺn5!̰x{݃l\8 z0B ]ۣ?E-h6S w:f/ju ޒsYb>+ Gu^H Wip۟Dخـ?zx~bƱ+q'K'OFGUF-tL$T:'+wnE}Bz:pg<)'*L֧3 7}`d+!K_RxsBe/5=Ip$|g)uL+vm#EAu*~Y*rrHt!Yr,8$T/fɿ(Qҟ0O~5Lm ;i8hZ~R$5Bgz-v0W>I9 ʲ0t.΂lC@& uOp6:Tq-a7 lg?i]xeih^qVr^+8 V-1$RJf9n~Za"WNF)>i&ɮS \bpdnD7M ݰ.ꞀίF [㮽 oAgkvqFEX44,Ag6N!j1%)`P}2i ^*b֊<ծERlx4n_~t;P=.tm 2P"!VҠ~6{iX3l2#!D[#qcx*lh)57FFIn~YmEtTI`7P{v/8(Y&FU6 ez@Iߦ `ܣUR?@)C<{'P Q9; i,/*BG䁎'N^;i8Iw?&t98 JBsfDzV¾j.& ,|vQ(*;]{:? + z qܖ[T YՂ n9SWm5͍!VJ+9 a3MOIs4 yl@5BLuXjxJќ):':* ؤQy}~웫&n~h_S  @Ώx˼EʏNgW0)wrMs[=K-'i̬}Б ұk*{vR GY- 4(o850 k%Ayġzh IDTd0f_V = IjvKP ?*{(~Hnn tN)QV7 A{DTLANH#e?b^džC ! 7f64cMGPs!#R } @ΫQ)^),RaLBCF[g=KBbhPO]f >7cAmI2 љܘ$mwKf %?Av?VoZH0]`O4 [q9dl=,'3ȵ(6O43_V U}l GvO6#T73U5D }Ŏ{WUV t\bzFmq{K<{mU`9ǘ帆` b?hi/i #dP%伺N&]>WꣃVM ;0m%zb>ˮ_$!nsqsM㢗Wt4D{l`REǀace^;zď Bu;R`?flLOaZhvL4l2)ZVu\#UB!eʰ/}EڬӨHV}ekaHn6As8khf5<1ZB[)tt!wggz[L*(/zB88VWǦSj0HR/4lh\8N^O~!PJ$ 1"6XĐwwj'\nGC=ĸRqbRZ*'N>"X/I kǰػk>MEdBԣl!Cs%})Gޣ0Qwt>߂)9{ poGdL*U <0Ai(9I܋!]u.2w`YO_-TjHvz|l2̩S0t%'.E:6clctv++ wQi ,:"jp2'q0XtGEGY{ᯠ WP?GTߴpg`\+2d+C[Rsy?ѥݭH,6^sWU B$HUt-LQt~p>l0!,iƺ ʀYh'+އU#+Zoɋ` ]j,7 ):OZpl?tGR=$RAFΎJ|2R,L8:b5so[, $qE9Kz[lONug_is| ʘi[тW~ap!hb(ZFkp?f|IU M7O>;O!^Є" ůC܆9NLSYi <7udLMͬ>&}[-&Cͱuc?R_+bJZT7c;g?%!::#,0I#O' J|p 4-Sݩq@m;^s>4\Akv:kH{F``}3_`OBcŠpNpkXt^4;*QYwps}:FE׷<,a >}0fX+:BVٮGXS&UfS-{[ݥ <8,K'(j/lɘCVjգߡ7~B# x B$LrzYT44ړ,6U ZXa=T=j\(#a/[f^'2YDړQ%  i22 i7Mb3QXLJz{N8r"l8cHx/MBNTiԘ]/ְ1=gf,a<.|0Lh~#hXK+-}PJŽ Of^Y``EO!L%*F?- O f42 )??mg+V/\^U'iW=rF$حpDRS(Cp8a.#\6Tѻ(]E}f'-t*oۧ;g6 i6HW$F,;'7U1 ;>g0G-ct@ %GsVnu0, i Ք^>C +i%])ϾϾ`C_2%7Z}Gh6-B'NpXД1"EehP!xk|}c.(L1^ExG1qwԴұ A|PǪk": ,4$eK@eLt+ol#քI7ϓc$+}e6p[yD &JZ%~p3$ҫɍV< H1 9H%w\U #eWF ^?3ilK֍&cREp|b ̶hè9~Obe d@C)Xk`"“!&s98l&<_G.P˙_rE rn)bJwĮaL\ )OCҼHZ|EtV@2ivIV2 ¨x`Ϋ4.sg΄#3Si=V}٘I0&r'罾YmY}acoc9k럹g)\Vjp ۆdڱ$ՠZᗃ@:fpQ25S'$w-Ak244LinHZ^/nW f3ÛٗY={mzh|cϳ,gf#6hpZd8Dq OT?>[ + \bj,%P)o`]j}d"Hxh0h3^gȣxx%9q 5&ZlVx>QpUQ0BzOBW!)Z-EJ]EG&}9b&a+k I(@zP616Ԅc?^4⽝=9BL$Z1/sq;:TZ!s]-4OKA\;3g\+օ]iDvnH)y"y} VgYk"m ̕w!:GǸe`✚_ggEMdYU03D"TYr0[q@B"KԄ:Z8$%9DQ` FSS bCJ+rIU Fj.2ERs y[+K)nSIJmmK<%kIL?@ZB4)B_GيRҟb)Hdz b͹;ip+ lc[1hȿ+c:$'տwѡqT8nP!+JN~kOTq5Pr)K1 +C)^?94:n%İeDhŤG Pඟ](ϖF `2f$hHdy"b$%DzDINt O)8R -Gv{1{Oٵ[K-٘|{/,\`fk5VQ0*O&,d \zؘk}p)S(vrE&ii BC53Ɍ7R5BUtMXYd]x^ˑٜ;;(@LI{VɫODfcLTCqn=R_[4ddpiCၺ,زNv ݏ<y=+)Q!dk$qi{gؒ(jyX h6}'ʓG>(zߎyzĭÁ!)(BtCOjފpᬁB%m_VkΠo13/5|I2 C x|>|d P.Plp֗.[;|ijA )g23~4l&,A\.z0| Smς ,]%q=)W?nprv^Àڶ?6JI[RxqW5ij㋦xVK|RH7WI9ue~avj<ZT`sDsX2S(8ZV:%ފ&k:bN""GA9X' VhD1] H/`wDٜMyE>:`]%ݿ P{s`'C*8ht`cDi,{v Ŏ &_dN38X'Kz^T Qa3@{r5I?Gy6o97cE#BjNft-tߨ+c-]ւl3~5Um~4@ŃMFJjw VV)&0쵽WUА}@\4{s+?EI9/-mWy%S[!X|$7cKڶ%_j`a1~sXNVWg9ks܄z=uGǏ@ep F5+sg`&UK ;P+Fp.:Ζvjƭ/􌕇UQ?3>чrZ- hMBٵ/xwK|tڀnI^M"1{:d<$ޜ$^o>gqNVH割;F ?ΔWT=A'ywXu) &,r|`5FIH=.D&HG!dd Y͛bLA-RRb+X螿\' W" IJL^vԢ p\U'yn1qw}Uuךa `n?y6Ȱ#[F#6NDΠyٚam9~/% skƥN ]ƴu.ݨH Y j`*Xxȏ9rGKq/ui'fms(R);\kg"Y)ůˠhjq}5.>%V,QμRg.#St9= o |b#S{ [5lRx7B=_p$G2P쒦(ؿO.dްU2Q BjsMBI:K6=PE1=ocG%^RkJpPwDl~_`zF֖g>۝CXn'uZWg:tϛ1)lUy3Z#?cJ?*{HK-%o.iraY6W`q 3J}FG&?i' bM6wm!?S} jWߒRpSz.k! ''K۾2PEo mIYb4I]y|n3Z^ڕ=OԊr:T0EiNphZ͖ O؟<\9)TQny[Ђ6LyK'LJxĿdY!@^;X9r?m;SYD%"#MEix$D50BƜOy@ks!bV9WGՃ7t08qh/>)Et6Kr+j7J>ҹV͗> qȃH58P,$e¼qdDOD>F~sLsnp'J;e u|9+~byZΪB: (/pevzɧOxԮSe]T л#SY޵FpD(Y#4QEDMwFqxsU˛NP3O;TH~MkӀ.OMlqIUq]rmNpwr "'U X\M0 Vk~=NMM*YxN-~_kJ $=_L`IoׅN|p 7Oq'~fmjhxǏ y+]wytqy&tJ8PڵH  tbC@&1E}B+Jߔ-t3HcDB2WChsQ˃k&:7Ff(B7k@^'*-u#UYxǠ5 D7뫬Uɘz#,(w|*k !KG<;1RVfs5Pqtb$k~:= =p0?ݱ7`HTs{4ov.Lf?A'jv Yʄ+hRj^ rr;~G7;]FpipҜa)%k'(:/wJi"Aoû돱pRٛRrP}}8_a*,پ<] 0e&3rl|KrOaI4g46r=^(1R"!s|wLKLU ? Ax*vl a较 e#2 X_I^uFry('N;a+p fh\h-ww\$~YQ&XiePKw-wѩnʝz xТV@ `*r@QRX0j,Tb&M럠m )u`P]z2~I gd% |a (ђ1;X˅&a#:eD%,% $X^njFDl&` ie޾y-:&#|feR}1R*X 6Q4D4֗`ffon>䖥Y=g:3Er'tمM3 LjpJ+G `湗[IZd^N; [)j.[^F& &@r,'=v'$A?3 dW!&GCp0"X. ?g*sEsoճ?F&N*d+S4w.N#M yơH,t=^!tAG&WQr9ǔ"L1$=~jpIu:Aw}]&dl;V'858/5*˖͡AL eQA_+}s^|XdOvXO\`A}ZM(۷(TUp@$9\IM/ZGdx9ثs՞ɞ6ط>-83<Ӛ o2_86-h(Nd%c2 "NʡP57!cuwMwgpi#TQn'_ X=߲`ό .m{ѣôUN<-?8͙z:j9Tگ$!r6$ 2̬xu> EH{zTN'؈'53 \LԕUv/nyú8r,GAϢ7Quj&~gg(e9l-<~ m"X峍- 7%F:Mo-;gAҪT}Luؚ'u΃Oyao-yqov0"Fö`E>k$*7V|_ n>H?*g23FiY%w=#RFy YFJom gvko\vʫ8wNyzI7H(aذ6B3D{%?× ^wPVuZ6-#X"ђǝ"o\V輫NʉύHK2c!&v{NqH;J!P5 jcZT RM3cu2iH5%yu)ֿSbꐕz!xTIBE;Afh똮~=2C/@mJyXuK-uMt9d,d%"7jWRhM"f{vw̉`*wS2kCX\[F{)?l93+A 3W]j10 XVUGyȟ,ܚDckSAbxN(ES_Uf|!Uߖ"Qh =/5P/JU+D+odA Ϊ,τcHv.CҔIJ j̰xrA|e)P:vgq<yܸͦ q?i r7B" ¡^.޳*E-N6wZ8H2db4n r[NY~EuҕX;|?ZӵZҋB ^S/c |vU2W_;&ZQ.{v7 1}b"cz>O4ٜkogOp1J n=cf/rtŰ;ٲ!Io]뺠"a3a- 1l(O@)b%Gމ5&' -;2*|FHQ9.=+ 얇۲(.=Gun֞apH:sTnLri ݄ug^1x5鞰9)<YZ˒/F 9ۼ^h]H.% 2H|: )v'\˄r4g4n:[®y2#&&c@u) 2&EPcۜ]=B-xCgoB8>r~IZ~])™ETBLª>Nl{J-CmL1[7zP TU5q N59ţ̣ U%WskƸmʈQ3i<=0'laWqI;buN3w _Xۦ l7 9P'? V p6IDOzQ*X=%aW)2+{[З !_Wd&r7;}.}OmkWKL><&Y#7f_MOՌوK; B+U\s_:&>8nPtn`}B^j)zRemw 5`@/?4qcaF r17tu)%xTpw҆sRH8.(h*Eq r[?g^ǚfu@mH @g:cGf0ZZ Li.f'loN@Ɨ6M}*Bi&p 8avC]t ]VAEq'aiåNx7XM= c5=%ób5MҌ} BCj6sƞȰB9J+oAr )? +*—ͬ6[r {\1B섔ekj' BG\mxDmw)٤5A%/{s(H *t 军bggݓN@&[R|Pcy|El5F-eaV3'>s@{ Je.E856UiH,d11?_"$+kHKW@kTUφ[a Џ/]tvp;ыo} C\ og0*jfBQf+O }jD޳N*i[z%북) '0z.ZqKawRuL{M=\vyG(IKxwA+J 8IqT22FP @A-l>P`"4A$)n+丰d`u{EF]dƢyjxǰ_DM Rc;lS{K=UisNwz&X76^Db.*{PE; 2P?.Bom T t`h fԍ! Jd%3F8_7nFN(.]P5 E '7bhbjT7j=!6鑼,Jn+H/y5 y=oA_'%cOv/1ƀ 0ɀLt1:=;*PLO|]Mab̔M.8_7VuHnAm㦘ӑOoWW9c2aGÊ Jr'BE„*Q:ݩ"Pú!J;{b%HYG U!,*P<'EufLL_O~BP6UAw?/+EiE_D V`;coNHk6S+[b4p4ſJ$:I2w4JlO|ΌJ8Ӭ8ֺܧDGY8I &ad4*Z#} 4V +NJI6o5uhD>zc>̢B{zT_uD'#p@ (gFI-߽C7M Xk'i'z\pJ|{Iv BGkc$&Gv~݊L9_Ӫ`k4*d1FzKi:,U&)s$pt>juG 8ޑ~?UB}cIx m4<͇hT+se(́h[<>k oITi ^>(WuN+F'۠YЕj7F3@12,?lu NMF_sspy<r?.!BGRX<GJRz\f]7U\0eS?Oh6 ¯[Dh-)+'!R5z'Q%di)~⥵YhDXq3/VZ~l:bIAI 6"Ał ph싼FҪ/+ϣ6}kK*DYli075Vl; T9UNGUK3tF5FMQ Z@Rn 3zRCP71HxJ6G(!dn_?A'f{ 59sd )&SgA2G1J4$%ly> > F<_F|hڔB+f~'P#u uNzۺ9Gﯜ*]k"pWG&II@0=ʧ#6>f{)uv(oGǧ-r>?YoB?JeI˼bѲG?`qk/ fŪZ K`AڑnǍSٻݚw,3VZmf&^((XGx"FkAjqUjOefS 4G?A?yxmj\J ]i&fuxǝb  \ƌJgK޴+c@Ӻdv=Y;TI lyJRH}]LQdvug.Ě;v=KA}4ͼB Դ8yČJgjaàO U>рHQ=J̜!@ .Js]p hQS/;$a4:E+mE*JDF#}x|W?d0:X($k0K"ж 53H5E^9؀$lxQMzbSIjC9-ϥ 1i8ʛIzsm[3%@N ݣ9ߊrBa2"Pjʵs"UO$"ڭbw5З^G6"W[JlYC9(CS)0 ТNRYep9B=ǽfUj%lb" lT$vAAW:EI*X.5i<+͕]2j,೴1F[[/2ے´Z8RC 6E^N9W6wEd#.(IT[2LWPJ;Am$`CT2vnLN2;PszeWuJTm STr%plTyxpVhwS8\AEDÂ)Vӹ0k6]Gry^"@՚}^QK#A|hP\{_ԅq>bSrN!9OfU #vKzN;W! e4Β(VaǬMO9#R[g83),Cb[v^v C`td}O濕% p5 t! :pģ+?T0>LAWAqh ˹`lJ0n7KCԁP-VKe{M0b*d&)agdԛ[AqΘ7s~NMv;ZoM-A%sS3nL=q%dIBs&ժdy`GkOJSEdk3۹Ϊg{dIY&*ԡc£pAhO'*v3v`sJW0^vMxR9O5҄ hO”m0^OfV!_}XM. Z& 30;]ߖ'R1}XBl>:rVwSuvZF$mFᅨ>j "_ ӉVn;ojӗL [% uWh\{e_T=w>_ojZp?n-Aʗ_W'=@=$HL͘c'>90Kϲ ]V&/1"̊M89wtb!Gm q^I;G]S]ՠqV~Wfv, ښrS 'b%kXn9]Y ( (탑,ϋDNEE] 6"G t}3\l)b? ꊮ͘t4Tenʪ\6Yz!z5JĄɦ%&NHqDv tGw$r]e+¾mNwFY^ 웰5F?X+"í57|.A#{A b#%<ՕPA%.Ex2qԩX'Y#,a b=3B8Ȋ y*/E7L ݶ|p2e.4rMӡQ3tΞl4fẕuh=-2JhDɐ&b`ރךjC* $Vl6OP!~@ռd g 3z1HZ=шPI۔zO1Fb1zB;Hg}i3M◓ƜzNmZZ;(ɸ0]Nɗk&e~o tq{sf$AHr7X/O?xU)wAXi[i~i#sk '+^3,(\C89SFZcb;8u=I? @Z$U*r DZ񹮳[6Uh:W!"TGcmFy{)$v68QIY /abXW'S,|!%Q]r=N&˘b[>-hFc=g˔ ءnJ7)i1,-Lӗ甖, 6eyd&x2('~^D% (}mW.x;Ztt/U=ˊ\gyشpq B@;v~;bܻ+馌Tm%Vlfڵlݳxbh¬UY`':|GMFM/)nAǕ{R%4rvSnb RQ̹ *PhGukݥ˯+ǚJ♱1+sc%>՜chST*{R)ާwټe{;,[5)΄sD>H|jY\ AB\2%/A:35Xuk^.7-͗  8gQfb-l"=` ǂM*l Hk_ڼy'{k\c.Iޛ˨F)dPx#Υ-%U>55¯!d#z||AN-8!]y ²*~Oq}Qy\֢3䫝O&:?*";_7qala+ 0.jIX<Ͳ}Ih$WokJ7'w©YctC` xЦ".,$UX^n),f]r| @+ƴ9Ʌ G `=-19WSN!^^7 φ ln5J!Mm ~XQVȁ'@ޠ!-JZnQlX vļ`xY{DI)( cYj'j*T.)`xS5 ;qnICnOQOJEQ~p k\Abl OQC5>OGgI\Y3tfx#=eHd;7xxR߮ιa'&,F:L̃v]p^WWVU]Mf1"p#>YkyU-lƈiעJE៱~yYT5x{TÌ_?ϭ=gs)hJIv}ns&'6z5̩8R{etf ElٯH} =uI/hGUWvd HzPi ʁS)>~D94f"CnR {:=R_p𶍙Mth6욂)XA&QEwNI^q?;P*AZ .':UDCsб I$h/'+. O~ԡe .@ xV]`l³m-C0&n8jSh<͟0¦43bsrWU0-ټUaj*>kd3P,E\ɦCAEègKV~S|Mv)^S/iTbMjxkq}{.khQ4EnowZAlrI{pO 1)ͺ) gg`'Q)#:ٔbr$ LN{}#˻3q 2t5+ >4_`D "L\-/orN5jښ*mH^#Xx6]:+蒢/gP"ro{m|>xgŌ)P)s.| p˜)EjA6c/Sec_K/BăރE*{=B4]@f#sQ oNIQ ՜,+DUwlA(ZvE÷ĈA!;%ZY~Mu: zD턁>z㐱\jLK~ƟVzw}h33˶PI7G|pf~QR3` 5#G18o =2IӶ,n;rܘGL +>*1B#X:N v; .=NӋdwD\[]2C돍kl9kj&<* E#Iwşx~~&6$ zso;@Vib. \;kFSq"o_rz5vhֆ `34TVQ7ycu O{~lH2gEB E4/H|HMY$ilLs^-WQ\ѷEg^y3p>i}рue#9Ɇȓۙ daR14bm WM7y؊Wzɑ'N{f w\U:Z݇B7o#@/LY ڿVY{F.  '4kmJUTWmuu R+3)M;Kwn=A cٯڟ 3I ņm|t;q`^wO?)h-o1^4ЃX$Gw;/W\3ͮX/:xUQƄm1&+J#CwePݼPЦy =3tetm܊ X$4jWSӕ ksuƖCcHWׅc~NCn"*3i{.Ҳ6~=z@!W^I'B_6ʯw|)LF, ?cH6@OٔDlĵfey- 7.X-I*H^4g* ,7uGtϗ@bd 囅7έ8Z kb$֌dvro1̰ ͖*&;82F)ǜ}dc8¡I X @_CPjEFQJ)M?Q?=<eK$n?o*926O$}wX zWm{|+vl<6fCO>+Xb~YApZo]H`WW6m7*A`tHFXw8h#_4ɲ,p:qHjdw6o&6/CfL*nT^<7nuSE iQy=ZRuu&ܰm" u'2*Ws" 3ŠqqglX5oZ:4RF ajO8U:jȺ]Iq:ȸgqlq  5_ǚt[VsmjGB=-Х'\t-(}b[;Ȗ2X!^gE**Ǽf-8 bkpo(f9?.̗ -}[S&{t;Xׇ\kp $s7;R$t'dx9*$'z:-Hz=/xV)\/LpOBc5 "$3_*[iYe1 dÚO0%M-ǏLM`!pw,]yd2hб#NS,utat$\B'狅?¬ 9 %;#BTU{wUw_6qS.6R33A+)΀*)=<ػbAg *ӝ T].~JrkI68?iܱM$IJ\2i֝J N ǢAqXȩF:~ʒs  pֱ^~l}Ka˒C^)$S+'C+ʬx}P*S罦dxϤ 9J:{&6dG\`8=]UˎAI%z+ϔ0g:V-l:+M&8uYlwhLkOX&I\ӗҋJSצFHĒin0d6kQዂ~ڴ3!qft9 XB4_SObb]{w{׾mI)cjdFHY%y8ITzR,~;vIʛPJ!zXn*pGdSW ǔsSg>f 2L7vn8n%c2:d5vw1^Wz$3@NjL4’}Zo) 0QdIԪLHl>He}x OĤ0:сb?Jwo(\;ĽCxp2,2MTY}>ZGdl`woڙ4A<^Ыj(Ά_sj1jjhb!@X$ޜzP?&50?3v@}M(Ew ]UEOIO֞ΔGH̵Hۚ%o b} * 9O{v(*etvv:kUlbuʩ ޝ34}gq'[L`9ݛӥ? ].ģ׼3-`jZw76H<>bٙw@Et۷["Cw gl$ X4D9jV ZMVR".kuˍ=oL˚h eZ[})6xAel pm|U ό&gόR)eإt4ghvʉĠ]b/JAeQ#SFLGXcBQ~lܕqZJ&HxϪVJf:N5$ \;KTJeR8 q[,DW9C{ 6A\#;<)_ZxL0=n(ikfcРU2|( GM\Sj4dZC _/EtAg=E#6eKKR@8SWɻ(bzaz}ܿB! [YQDp',8[4ⴋ2tje#e{"Dl9l6) (W# Q.~{)|LjQh,؝FwYf t[cCUXreeb?lmFF1Y3pPF}Hύ}`ͪv yl|&;B8t$&5 `N7-kz_FcU:Hdމ;=ǟۙrcJ$-&O0'ȓbߤ8!߭ad]V9Ҟ tm z\(W$Lvvte 71BfwQbn<[?g0~"rvyR8gp9re?14ۙ٘,42A$ gGhg7$&)yp1Xʽ ȷ P$*E3Jc7nǻ.LVǢim/(aV>qT5NjWҩz tFfnj>ڤ[XkA]E-Ц!\j[& =T1)Qh.':EjOXMe5.-j!&"wփp_޳6Eqʼn}pr eEwN_{X Īc]iNbkX_HNQax%]ʐm+'z?r[.oak Q[Bb 5>zQ2RuT`P}E0?i1Qj+|Ί0%'[K\<6h[&NP&\ O00bIH7-щ* ECuJp*a0;S5=௽zepe af`y-BIɩdqп@$/mҕzM!y]8" kDRd`5eZ$|ރд9f9UK;j57w'`6X&N*V{8#CUY8&&BjQ6!PS Sq+dm„SgBtXU&QYgrCEern=;g6TP{XG.aA:8CƑ W12xT1Yy'i1ȭN]ʻ392%NvHcos5GL  I )}Y tH~7kzo^EDC`g4r;xA :4;|ZO'-dLQiHxT'gT2DyxhsHưt9ͧ*ށ'Y [O>uق~?˰Qr۸%prhB\QEQC흊>gp*"\eʈ `9/N[ԷkR"fxlCpÚԖ"\ݛmyN ]Xu.\T+gWIXnH& %7/z$ENtsA/4IRFɞR2G84oI,ˮyίO8兴o+MHq]Wy [ȣ\fo`[7ՃaJ}͋o[BJR4\+9L]zrPB٘x=,0t%2eC`R&|irhBEt#W#`;s啮0/?b>{Y8Wx~;5Zv ~3)#0rcx >l٦:э0ghil}ʸmY6qkmٰFLj BS}V0k 0Q2#ӁSRWa9eUvKg"l׮FiNMN^nrpF;py)#ŒFnm!NJK|ai±Y,uP3ӕyILprۄ( Q{@B֠U`R. ȫg 72/Zwϩ.s$]Ѻkqzz;^pc[/=sl?Է̟hKPhIb{*@*6/+WE@*F(&%|Խ8Rm,:B?[ʨm zRJ7 Avmz=G-H]`Ku _fD/^,8tUOYZ"\I ef9;~6],|Fi}BO9.{}4,ǵF][C9(Ůmf H : %A KYlD5dIF ;92aݱ]:E5[@#z"C Ml''_WHrgcUBOɽx# YnLHeCF1V*e(E߭:vmBW~d+O^*ؗ|0 9 -gݺz2uh|=E"$?/%d&ՌTh:VrQnxr$ m]1ohÝ˥/.oz}R<,W!E&H֓!@)! ucNFBQI]0N0I/Īrxu<4ZDoXc3#/_ٛxtکm;p=n۷iCE]]5S;Pm{R7|džlJ}Hlsu? lJZ|Q6 ȑg>6$ 06.֝ &2_v~BX% g}c.8Wؾ:7Qmad?<&7".M@xN!YJu5aW#s~O,.d .4sGoO-~qDMqPD2X9~!q^)S Ù@KI}q("()x§4?Ȓ*P|@O7piJ-řg`xvYx_advŕqQg}SuZREy|Xw ےrfi<8,r5`"G8]'؂MG5{\7U-8\$W!UesYٴ' Ǝe"grgn;RE9LqH=OQA+ڤ Vdp K7)K_3y2>Y&x%=I4GFv~{goUwExbM}+i}s,FhTMM톅2`~  ؒ J`2uVM\*!lcvͪ<SgGwEo%JAgʰL{'` zמT^[&l-죚Bs'pS{^gS(vթѝ`(J 7g -qFq #[ۀ8*ݬHmvyMH\or-)D?or2}|P+UĚ:&8u'>D29vh0F>4חd. .[rld1h/ GTKml փ} H^Ʒ=Έ;2']eaIڴVmTeN!䆃-S[,{I"NM ?xpFhGcF*=m%xFv?w=aTm$ݠ#b*rD8RTūC@\J֟iU=N\ ,mcC094O9sU1HmAp? %0wr:50#,ox"RiP}xHonI`Րŋ<'-5?7DsCgm B;Qܢ#G8 1cK}{Emlάp-v-r59C"*t1~(:9J/JSw tFm:Y})ѽoS~oDb(3|WMuPQƵqWӁi%43EwxN7vRI[ л=kGG8 >c{F/Ʌ9BeIA\ 0i 8 a%ri{-3Mg01usGI5Fai'OOOJ*rm=V&e~|Z\6HgĶ\WBY&F/7CEd" CK5 Ll{ ?I~h.ELnwN 4!6{A]\0I&x4%CPZZ$@YRTũ.6Q~S2S- '^/gCX[0O6` @ 8[&k?A-;~L^5bΗچk_8Ι9RǸ U1䞥+'wrP5a(J$h" Y?C_57?*`rbƭ{ e+E5çLJ]zwk+Y呡'߷IOݙ7^ITa%k03uX&n}s¶"\ѾKW҃]F|sQՅK"r)-x {zUG؛ W|:[j鳎kRW` KtYGm`=ea[u FmX?(bSev5ңׁ=0¤qrSiATqc uF(Z4ĥ=C=$v9酖{kNJ9MhҁEq|Z!r>gQӘ㞢oRmU=fCVvտ#5~DܷԈ<}Dv)qл|rGt_DO)F)$ p;ϧ?(Qt~d6 ǁ d}8\kWr@Pdoehf%ٰsHnʭA[Ćh1M'KtgHX&?ʌMT(2\!1ğPX -MdSᵔJIPjl0yro#H?&x@U_q+vcx=O0ƬA,d\EVyM4?ds,~ߡNQ~B: [NG[@EL$/JCD3ļu ɯgtb/jMH뎲@})#SL| ڈIQ*DhʽP'3MFа\0271>WRQU^D')B"zHeLp%_y spXa ;.gj%1,`ykc>fNڹ%ٸc[]!~.`}GW"+x'mK=[a z.YdȪFb_Or#G5ײXn1L|!dI}AR԰ߏSx$ju=QhQ"F OSM&j3 9ꠌ=sȫQhxtY/D'ט,ЀCxg,sA: gK5Q#!ZoiUF&Podk,_ *g'1zEtJ~ꡤoԜmh?,]bb1GDh[igKP0)d5ql:Li^#ײT)\s7,?g6:uwc!<*r|+a nk{ s{OTPv_H=l@:[nmEM4) lϯ:,1 TC*A.[<=?|.=no"NBeV}?I21,"qdq4[Zd "NEѮߐ=e/QRlyvn&fq D\^ @΀CҊ_'vaWMj8Q*tMYKB4U4<IA{4I-pf:ElϊF QƧ?)/gƇՖ=Llx`!y x }~˖ݿ;-zE7dQ40t"#|'p' WFm@ P|`L*0wj>kU* P qu; 4DhhOoT5h$l kM`P<4uӏ˝yj])Z',e # -=Bm?6fp&h}TKk$[*|-WQ@!t:phsWnag31'n%: c 1Е!Yc(]o8ig^We n؀h$Ix(ЩB|T{l !FUiaS! 1FZrE<ڣhpV#:!5q7jt7kE+,񣱔wҜVJ߯rUij]>vXle j x^.XEK7P 06ɜ;4c χ>-ly5N(xYL1\Y!æ/)u9$ILiU7yթGhN L1x&}kKܵ){xڧexO M "kwENh,1n`(@ ߖF _}#eqKfZYdX!e-J8l:jy~MB6aȦ:HρJI.c\mP28* 2aUW[l] #4cx.+E=}=ߙD5ko*l0"/S:>)r.S/ܿ Eʥlk եDCa!*ֆ/7{6h :> aumObPҬ_ A¤LҀ}?<\t~,l+Q ۄK"ÕYj!_M7U2}|,joANg[sV95LE|Z?#Ү2UEc^aa|3Z9%GIOEK̚ yɁrhُV3L]]W%8YyU$`ak[F NUD`. VUm5‚ew co zQ Mc`a QL(QRC>Kb:LD3'K2>"CMu2@H?x|N5 Jy^U+J 6 #e]@\*Ю֣̯ EZZfM7@FMjC!&#;'ZCtU̳Zd!5G hMZdQ\ri c#>V~,FG.[V>6€-]}|*q7הZeNUn0v<HRu39_*漱J'd'Xt0y$B0vo3㗻+A\ 5TadPߞ<͜ht^:~E^I 6MaWH &JHB1{} l=h D鼑$] x`ͩ|)Kk#+: D|IE8p%һd˹ *B݌KvDЄⰡB[r:&XNa (k;jZzRL#v+ZQ7EK@U zL}jMEftS5@l2R7*_i*c<-3ɣHߏ?=UzE[y.D ާHʊs5g5)ɡ-[dC.N|Ss6\OIC΁i\9-G ``fOwiAc/(c!$B2N7t@8J!vc_UyH.Sڠ:z''!1Q7ESi~,҄ߞ좊oLK4rKOGDelFϖ"w{LTA0cG{R6k"M[Y1{f,ŘF|a>W*RN~pm2ń^W'J?4Jh4aj*w,cg/dnak$f=Q/%{LE2Wz0ȷ"SfLZ\#{&RQB#y7^zPN@Z,&9[h@s${'W spFf8+HԢBpcC2-}[Tͻ/Mz@hZ#r:D?=9eK#خ)Hbj2?9܏3W{3*Tgz6D,%L:UF79|8[ ɢ[w3ݣ3FA5MoQ5b}ךOmSfuB@6rV;ϳf1^Ts,$h?8 uӁ Fpo蔀3}"FH5_M8Fۉ?xyCYM K<\PI =-KbGr# :\|;r`9n@w4g4$.P-Y*/yET?} Cz(ҧDIdN*Τ"]% ";Gұ|2^;K{SɃ^ %Zn 8Kx/1{^M`/&rM64>Zs `^@Y]G. Ci]a[y2gvIc|4cY7Ce/ᝍb=YtvĿ}龷@3yQcMFh=ʳgN7+=eqY)Mky Q$?5a&L? |_n"LF]eZ.uӈUc,=O4d͍!绛A&D7Y ЀOz;gI#vTCS'.nN u;#TLZpH^CزmmW,Budr7$#Xz)9h)D䱛,C}Uu9c…NЍl,Bq?C6dQI\oc3WGN%%^1<U`*ȴZZk3:qNTOA.4nEG枓0nYNW꠴8(\ m`q\Ads~9/L8xab,ME7NKkŁF׭;MUg2匈T!5)pt]Wq|s^<|JEB2G)9'46B4 %,iCUb]rtoePP[eVBʏSzBt7,.cCy֪Vж$i0Lӈڿd.3ΰ=@Wee+-4o\걩6F*y+ߕ 1.d&bsY dOqHD9`8z]1`{aVg ۋĸqR7Nj h!EE~~z\ԓuNm^- 9Kxof%)fF3dGI%{-h=Fn%cr2-dzf.{=F^legÔƷ_}1? |_\9w)GsOc L@oL,#q[ẳ<\ЛaЅJ:f#0#9{Sm~ПoaTV!4~ެ)jM^6+ X<lg#“8dʲb,jCd~yG̋ʹB3ov?[_[I"՝&S.ё!z&6`k]5"&I"B: x!,q:^@vR 9f|;U ,dx?slTl U># wdBX/0:/㰦BOYNi.<cì;4Uh҇#R)Ld;]x>=lZb0{€5^;E,{xcj 1/5,?ɛVG6HZ7SXf(f77گQ?w_L a.{ IpLXa# /U|ΌhA|^8U|r (`J IOD~] Dc;JbNăPPn|S1ueY-;aόE=rY>`(DJ32%r3vÚ(N_ɋu M+.` 5c\h D,Vmx%+X2Z| Di!ьے{\y7e 1^cznYk+3pVz( aff>?7e.P߂ܚ9*WzY<ۗ!:|y N'E'^-hR 7A7H -ml ]|phTa!Ԋ9;A5bBǏ#e$;Sw_,PT:ܲ^ު.4,Kե^{͋ tFG"EEB ]Iyw,+fji/t=7AԠp6o\L*hgQz~'!c;y?aݙYTS`$QF+fԊAr<i^?区Gr=r j'XAWe8H(tcj3c_ Q8\QӞ#ÛZ?똿\~yl^T)F2 C*!11>omV._G#jň'H>uj{DqL@\yQM HfQKQ P= l[)[ M8n_eOÕm .j4<)A"5yN~GhC0l:D>}w&~RS8$JK-*wE4Bl,CY:/C1k?&&)w^0:'(* 05򌃆EKؑcZ l! G>ݳ*8?IrSoy5GUwohTe^ƪɸ5x43.B }R%yNZQq%}UJ^41ѱtU&*EFcppҕ3>6{V^D9u8l윍pIBTKX3,t2cqKS(z|3liHljvٽjF"Z!KײW)As~E}gTEDVG qPv" |xhȯ]NI.Hb?[1i\ihaݕ`N!0I6 $;TcItƍ z5Lgh4HM2*71IAH,G  %wܨ\ƥ)F̣u}WFUrs;ftL!@M\q3}&ڝ%AGf)0b0T!JL^cLߩe;W~ x7~%["AnU]a? 9^syJĚ,Ņ]G[Am޲lM]aŘߤ3fgoكR[?!msg9%P<7"va5EF Q!ui+M~m{1Kg Q)'ic@mAM,H/ m_.|o"k,$Tɮe: ȍΏ4쬴sT"cÞI) Boj2Ɩ#BF9E$fS ,I.[ EtY&8;R"WW[^q&3IK7=PX.+vRuӃ(_QEc"@2>ՏAH]SkKGM_~AG;;0eSG&vС`IxҿiwlXxW5:Xa pseʚnwh#:m$/6-9T)"LLE^^|!Ul ql^H4߰Wjk5%PɄI)=Rq"H1jL|DHp5뒚[/IF,APhгuudsQFW⍝ zpe@{ގ;T0eby;?v~ps=bFMA]{$KuUC>O}wosP,prO96KP'E16B{jCEw]["v9 mV/\@v4;њm K^!-7SIO–fM"lmLmŖC12RμdҐ"Ov_fHʎ*@tXB MZo_h h*(߲߳Cd Sg9m1g* (%PqhjiչPV.kuo"BBw2Fn~mARϷƒ.6R=sn[lZVl5h8Q ӯ޵9"I0 [edh'՟D7negO%[Nߟ8jCǺ5EYrA']8'DzIbf ]$nn+|_T23 vMUx(:LY^NrpʲTw\OÑ7w*"G#Pe7zꈾJB'sWQڄP[ 3I[Q.x'm<wK*[8ʕ6j endstream endobj 62 0 obj 110688 endobj 63 0 obj << /Length 64 0 R /Filter /FlateDecode >> stream L਩1e%Y 3Ts2HQvJY=PG3/,J7Kv1<䫱ǹW6kQ+|Ww؋?;91쨢L4 љ1o' kH!8|d3jĝA/EB%m/- wE=[ژХ־,Y'јSmdm[@oΫ.(tTx!jasɮmb!JKRiLJ]JW*cSļ~vW#BNHq BĻ=:wr-SH>f6-PO^٣7Ak$€Q=>KcnE&{+̳ &k:/4R4qQ dʍH)}sWyՍfW >:Ndzch`íG3W3ۀ\ay"{PŮtEx[~PBk^A(Z 1@lVJQCӂ[$gabhsWR z.=zq&EM0w`-&VImuEۧ[atKߞ>=\Q4=&Էw%$#=Uree? fx՟qBR==8uŨD1InAOq ZV Mc6\_PH~h$! &>Ga p(/! pzune`2}itZ9(,n"hKGe[rm}p`'(s΅R]@REE^Sfs;P+ s:,yMlggn6e+RF5l_j&T뇡0.6>CқGw8i衦-u_ x+" nD_.)P4/Uj{- A# ɐ5EWò endstream endobj 64 0 obj 2160 endobj 65 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 63 0 R >> endobj 66 0 obj << /Name /Im7 /Type /XObject /Length 67 0 R /Filter /FlateDecode /Subtype /Image /Width 3000 /Height 3000 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream hꙛt[ 3Sg{Yej롕q ׭]ek1_M*Y2ilel;L72\rh"Ƅr@fJCP4%!98ߌ/"@U>FV~*g(LaŠDC\DGt2X PFU?1gطxbCyQVP3>YcfxaLI&5uͳ^||#"rtYC#ʮvM}${H<9#I ٗ m+P?fN肌b+xsr璤ʧxf\5uAQ-Zjua~0GE$h$\6si Nٮե! faXFE\&*E؎I;VLvrLΤ3pS.BPDggK1-XC?#4ɬ?fks]\}>`{σQ3Q' x pQl˪,5k0FCSz) dh?lGOFT:]ѢHd7L& Ryo?K?G}ꮟFj\~d[(]ŁC-]KN,Q6! dHoـܵX>}h2ȶmI$jeOv &euHH5'Ee!x!RγHtP"^dC\1k0hA"ڎ;Q4eS.{HPFTg|Y&6bE#7kgTO,ɡfS]ˎyN|p#NfVtd;iA9( mnE#0pŤ!fJ7?ŽNs@l6e(xClJ&{Is]HwS1?$t)2d 6 8ȶܗVRbIs~Dd=6RN}jd}coѮ~³ơ̮ɠvL|}dMZOvĐ<؉ܩAFE!g +)=}T ⹚EISņ"k'q3Eoq+*WgBhDq !Dt %9{~g`^B+)@GGWȯs"1ckngE|wLE{R)\=*Iv(PI)SWxQdtH`UAXb5Ml´IǹѾ)z%q4]SoF8̈r vt0dQ8Z;UI?s"0H"m% Gi'BÊxfV ji6:PPo_Sj*sw3mGɇ0ªT1M`TmD:?=#03VokS$ޟ>^1K@hZMۖ6ZZOoq:aӢAU'v8>iSU|{/Z1sSP_^Xx-QߐqX5. }$gڸPFڰ_A*+PcltDJ% OJz3a?5)K 1/Ul n @P]g y<c"k"'GʈNHnٓ[鳮W$`ܮ Uخ/EBg8NMd#t&O;s% ֊uDr96(DK~G 7"^?A|w EE.va:6jjݽMl89ĄnP&NdNT5>..{k OЭUa2@ӟRR\2g;);FZ*$1HhtZڬ ާpġZ~G(A61+xvXR{j>U4*{4,8׭g=iGhhKK+5j7Ć#&-|ӗ2&`y>XY;:HO^%)ht"G=P n?*[L1$pIaHCVD7{5mq.0tdekY7bV{PrP&9ܴpLބ옒A1UO5"c⅊`ion4{:n;0SvXM],GL03Ry!*@cM~gL(#44>]3[hX˛;@ȹM ggjZWˀ @2eZ?,+,*dfOH2B;awvsx#jW`9>TJ\Y5_/aM jglc9+ 渚1;5L>۞nǑk e,&/mD8;2L,8HBLB(v=%3L'lA2dyjhh=ur^ >ddKH1 jDP|=z2Os]ˎ GO?tUsj<^ZI"<*&tgD4cަnP*rלN])7@moPN{H`9y@(~"o/yxynh\uIH `)mT%tD2g»6^XK(P:'/#.MmBJ'‘pt# 剥pt~pf=h(Eeb7n]N6CK4'Ykς6ޏjӤի^pTOkKzN0/@~p0ih؂kC,IX:*eVGl*TOQ"GFuƄ w,XI99oo5[L/Y.~-ObƋ7H!1zxA;H/6IFYbG, %υƉ16*Y^]˺ \ ]^L Gb`[)'> `ڧ_ <2fg`~203Ntiǧ"2.U0*U7.|jaf);,HbBemy;T@yB ȶdNߐaٔr`4^к= X.9\C`Uđ".‘`\ kV*4T&F//6$ %|"V9GOjʹm|~wU^T܈1]YNkk5ͽdz},/bUe lh@xUԟ+ bIͬ-o0F{`nPLBDQ7P$ED]z M6믉ѥ"VU٣p @ԝO2|r^|c0\t  LAbELReq0-plZV#L*u|H1OИ=RRL[@CUw1v53\mq8&-% 4N&fAd|dkT6CJ#:e^(<׿8 3nt W0}_Ca$bӛjϜN4_ڴ22c^te+ gݵvmo4; r`D+ %rcjiN3VnCSء 0@ k׺Is >cJysSB5ݺGFʨ݄CTsqrYN_p}،hZ PNuP5]VOcmU"l!QT2S:L؟]VW ocٖnD*pV/|4fPBCz̖`+'kXl4!q))$!m1x:)R2M*{KȬn{ЋH|hT=8@]7-0ϧHv/aŗK3iGC`8r!?} 5stSC?RROCad91 ]o|pY4KǤj[A}o̸:'PD.NC " S+Mn'%jV/֏}3YȺ|d(jr GUJywU>SyWxen4bsIK<1W& P LJ9Htv-vd>I=W0I}N eu*~QCej| AۭliTˢ}DOP4=Pߠ+DUk]U%m6גޛ,"py\vrHG1F|OQL:8LкK _]4mpPKS FOZTm.İ>biϣ@W~hhZhD{{6)$th9 f,R280gNtpL`/BqlRȱIYeD/T6kf6dq$SU:$~XTzQIZYTCnKZI'\̏#Du%̡kG` n 7&OqAAP VM /m"8OIU)wo`Vàΐ? Ml[*}#tgo|1=VJ[g I@ex-{'27D%n9Su<+KCt]%+25k'tFL(?ElRC7cdT&.N=XJAQmPfSy*CaV޼V$|h(8ܐ1ŴA̶263y,ЗB;52l1+3ަn̗ߍms"O~&.+N]"Gf] ' QfOs̊p@ };<OA ^IC}׉qPx )Fy0E4DR=8kYF@ԧ4@. E;-~X߅~$^Q,lBrTaWbP~8;"8#8Z4B5;@2 [tѠ1#S~F)ÄB]o\Rο zҚk)qDɽ&9kw%]2؃Nz[bt.|bdGM ̄<'/rLaڟ0|nB,{w(uܧ&Qo~ȟKKW\BBuwvj _<PRl2?w}hF傅CV;6$յ2myK77DB!=3K#ٲHk)+۠Zl_^ >6-1rUEv͖KCr!;4l+4akD!B~S+R /GM>Rɻ 16#"h`Ksu͡{>zl;gCR=˻4Jɫ~li]yW%R00| T>"qNщ9*>r {Ct\fMom ι|L;Rٺ;ҭh&A;=|R n Dϲ@R _N*igջ̶>fnZ[.)V)W1Μ ͇^t`(@m0*.INbXW5Wm\amUN6?6u$6q?k`f0;Ѿwhr8x3YAH-<2FS/4GΏ=j׎o͵ WU:^AG_01bgZY_R[bZqBb|m] ~PT &:#iFz5AXsZ؝Cu Uϑ1wE-ORm7.ebjO&lv78<0i?8)t,^a##`5 <[vD ?! %'ȼRkdU,.eаS|!-n)lK7K*pZ{oDW Ύ*<4L ˆoC9R;7 r^R?#ॡ+eWDm4~4}@榲mq% WZɶ˟V=9(@/NAjE~h_NrW'4.|ĺ)N;2< KDtV uflrvho,f%5sw\ m*QrU-Wd(;q5<[u9<*uI/e٨YR3x~5|E"pSPm>VF!_b"·@;NNl8.ZHw~Y5Tr9Ogyf 0͡Mƻk ;~Z*;r(/Ixd)>XE%3fCRFV!#lz${zS ~[<ð`HT@%1ѲX@9 T>TT], t{xs8ְCk*j6`j @K4/(ͽmsC` n eCEYeTaZʊQ갵dBQPIr]] ;i˶#ICRd9/^pB.{[ Tj&eXʶwIQZ^ҏBݎ\_o.˭%[@)$ 1kumrW@I>\_\/P{qd!JԚ1dփ_j+['E ܌Fx2bsZv}g>fE2M/c'cGl?C)\*`^5,N%AVgWiƲi} ?ĕ j 5&a-''Տi؍t\5A۪qo-@'ʽ<*Et}b!^l>~C-k7>G^w\+H6:4g-;dA(Sl01kb 긠A fu]$9꼙A"_5ָKȕkAtOf6f~nV Fr3j {S⟱Rڐ>_9͇{wPM!s6<:.7}j|#'C/F0(0멆DIkטhgrWxX pdDcO)ܴKppo Km*@ E/gplGA&]t IL "Nn 1ffW?n[ 8_jxSFVev-`pjLN\c0 l8YGfw%\f[+ůkԿSOv_c7Q[~ qdUp[B pQj*+A pIMf M֠ba1Th;NLNqWLγiuy/} B?&> U:}̿p*xV DtA(jjK!ׄ|]_,[ZI ݵti?3 rs$"`(f~KP co$UThlGVt\~=#39&eR Oz>4(0fiEm^2nuʮQk*C .|!&;}4أ(7+ʊ)6壬xCm۳WIstWWAH}oDa3ם'})|-'"(Acf)RA6LNJ2R;]x|8Ec64ghT3i7'2*Yh3 txM#Rb\Hϭ\`[`^h]!jOY?pk@axG,g7aրA=5Ӹ)q[skhԷ 39;왏i 񋂗~-5oX%؝~Pz1YAy?4C$Fqq Yjf(#:8) BP.UXsȓf]C-shgǬ:/]Y;X]J: e>LJ_%]Yˑ` Fb goTDü/=cR.je{m>&QfjD/"C2?p h ᅤ34hhh{jɺ~1J+K˶IG5kAokӢ)Ós`2Y#bTYJGSyCCi"V\fuiڡw.g} P>ϝ5z a.h,9r@񍙺x|`m!yW bq/ZR\]-gloN3'S}ж9*pРUc=8CU5y/g |uԽ2}k:1k ZW i/"#JH'P1|3vLSa$;ؒSϳwq0jĀǏ5|!N}t׺)al_j.w&~R?3,Lv=[g! 2(>oئ:dYobLP+;BK4JإH(4-}䩔ǴI`7+!plf#)>oYr1ry"ZvM8e`"6ۙ2{&ER"Cj̒noHZ%e Ǧc"+ .,nBM<:&&^p a#xyKƱdqv C &OK68J0T#muo*B$H tEch{hςCo o2m]\ C$pBQ,s)NOK6!<[lp!`N8Ss_R7PGaQ&t-da2ufAƆwZdZ$\|E33=R@>IE b BlwAB" \ۊ"P>;ү)-cK4$K*O4dU ,[b5f`D)gx7GZ `p6H3֭$hh1&nVBUJ`Ⱦ~ bfE΄ۚFְk={u>mS4TSވ'R[.Yvbgzm+ 4'&L#c4A%g%C?R*ۙ_ː[&O%F?Gخokjɑ;ʯToo H- IB/!? waشm;-E#H!HEmxAHpߢ~]SfM~`okgLCF!LR3i K~/wBsS7=$fffVO+)7Yՙۉ ,X xZLSEZa6}t4@{ڽ*.b)c'+%N+4D_Yi+V**1{;zB."E.6[ mkx&`io9GcfǸGV\$w{,IopZgT:.[B 9 1\']jp0JD dqٯ *c q&PTw}|]RbW'yG.{ܖ+EGH#Ֆky$;9MA~э5A-29hڳcλ,t#@cF;yJq<ڲcV$Us"s~tIܦ vxbhZ;@aE[64pY0$l$3h%WƗ1YbYQDO 1BFο1NݘyQ.XwǴH"Q55mqOu yh3wPl<7 ZBrHGw{ *SYe-V.8(ݹQ b߬GI>-uRx/8jŖjuł@jS[En ܍"5 FsFp|Z~T8%T"i3:[|m0Só.#yp 1$_:ơ"a wJJ-#aƭJI,rl,P L d}pǺ}BK[Ӄ]oH;mWoHWi.v- o/yoK>\ljG uzp^˾c]>e ĪɛôxcP/)Q1ߓ޽ۈU*C{ `OjƾۛiL[RD$pVeRmǘ^mŌ ;[6p;0g)_<Ƃ۪ǩǤK8?bf eClDR`jH:ӷ&RHթrtFiE{J4Z =$6krct TI%0s]ɡV PD:FC!o;H!lL`Έ.MNx)m}Xdo9H)xlhCRS;̘_Q7E շG}rgl 65s ;B Bb$6XϚÿDW(FĈq(ڦ?']g*13+ Zö`B-R$?N#'N_~$jdXntK F}Sbi#ѽ3s޲OMjz !K>X-X1|5>`'F5QΏ?ozѻ R 8KUqwufeۥDBIoK\Tk~Tyl*&3K?cnfXLu׏hg&Stu(QVDe2!k>:U<:#C%k_\6Gw[39@GEb}P ) 3= uFT{Gk'z4jk˓+5]\|XHc_C&ݰwߪ' jg nnro:ջ!JkX۽նrhIRZ~ebXGd\Z@FF6loB@m ?E.T5䄕3=?KF7oۚM( y=tô } YG-Z򒃪U ag;j6fXlU18! +pD!IDYikn KǛɘbɸTy߁0F3ebz#!^ys2`KՔQ8+64>x\[1wTN<HJECƊp.D I? V4ck_Frw$JL:0OYȝi&o򍇵|̒"0LhKNC*Y`lLp-.q;rNysF+-" xe5dpD%$<3"?m[cz.ղ`3ǩi/ndt-nI}, 5tC9*?㉣(Mg*~ B`%m}p wBWsQ֍2E:}Gog2H7).IɥU#6- ^Rvr1 >d,0Jkf1Țck |Mڽ ܇NSyVhiY4mO!V C:*.h`!TK"uVD7NrJ|䉒@9][4Ld %Ua ^l夎eƦ\-Ҁ#mQ<ol rg)l 0mc0a՘s(3it|>q6e Buҏ? *sSf(/KZ'L( 3 ƙ&Fӡ)ԣ&^vHg*?TZ'"2H9[kn!}WU3X *YoC3OG>(xEʑPɹ˱ I-u]%}vq {A"-L^\ a3@;7SswE3, dmj<\b?Z2筬焼Yebc/۫7ĤlkEԄT'bYJ(cHP0s)Rf2Sv flק'}d xGcr  $0\& &dM,:6'=k3 K5%śH7T-Q`'aYzEt 4{[C1 f$,)9z[~bu=!W6Zz{QD6ŸU)Tb^M.3w=VbD*z`S SnQ C#Fu@2ݶ!~Y?t/$hpT|T0b}Urr~a+V<擜ۭԙ5%jq`=BLc w%.m]ꃆ)c(^ ;%(mnjPnMRRT}R WpVNŀM5R {y|VCπ#=3ڸ??hN?b~tTx izD~X\3!ʜ!]ZB7?U:6) [ak8&tUIGIBrf$;MԂOn:$ 1-z`rMRt֔?( N8g.3Y:̣D,q7>hqcHʪW<sQmwK/Jv߇7F !o fu@,<M3>7=s-1[Y}d ỸKAc٩y5s d8Т;8}wKxz^q'!߳nXeg֑"C_~VɁ^[R2j, 6dQD'RB엦oۭ3%DӨL3z x2{ȇfb3qhyPPvCnW$}٣ ZvrN9!$ w#dΩE u* r$@^jmIEEswd2"B b2Q]z]?&#q=BӴKH] Ѥ5/_ZrC?q^]NBj`F/Fe\Un5`ᑏ&Z&"wflSZ?uz>R)c<̱Kī t> Ƈ;.IPbgk<n2[Ž(}<ԧeN| QӼ+y x&۰3$;L Fj5.@cji)1Cï<(&5y{歪V;)/uB }h-.( @*:/dp`- oCj +#(#Yb_ɒoG6:8Wpw8sucd$[m􍜨g38BތR"W9VKVplHv4Cr  ^%UUCc wHIEhߠ"]K/EKW=uTTxғ4D 6@%sV.;, }xnd]F$u~놨a@{*O_#^BRE;6?>?;FKmo 2/k@@TkqRJb=O^zw\*h6A=h+@\\IXtT#ۄ(MJ$mTG~8fbyuqQ]|҆쀅هKEG숊O&tvE}'ii텬6?j ttQmbL)*) 6Xs*q=K$jኪ!xA7A.ͅTMa chaerȁ$=R>Tu>}:̴Zwy&R,zdR]QࣼqqL[_1+USU?:X3\B"NʆMZܿy2;X*&k8MT/b?BZW^ G]#z[sS–( |Wg%գP#sxq;Ag?m. GDu)BwY{<7x5$4&bB͟܅Akxukp='FD: y](%,Q8`T.#-@u,L#QԿ4 sI2bO=Nyq!zf uiNن:'2CP ٖ]-PCY/GnC *{o_GA-w:%mԡv=t?+b8].D(C"Q}8ԋQ6^h8%?1h.RNٵypmBۏZWK20z;Do sL,l!|:#n.#VHEu禣̔,=%_ וh|]V3CJ;[\V`RjU*R]/ .sVo!ðSZZ?}SD#w3%!ۥ2svL gI jiN@1>#4af+UhAPkjJ5{ 77n`%$4B?Ýn+mOjآ ޅw&utL9:vQ0PyeTЕ R{Bk,هsT2 ־ W!mmOc[Ha !R]؍#1(wGDràcܯ0t{bDp>Ik{$Qb9}MTuBݑokojO6\5I~Fs7nS4%o E P 8;&b`K{\6yt@\?UP,e=.h1~JϹ¬s3e'jt {7YPQ-;rM gC.Sm$B'f4fؘ? sF{&K㎞tϒ P8ژ]qC۷=m/O,+3W1Ҙ!}۵?(|[s8 0o5gF0 U#}i189YS0ӇBx-POZ*7O I%ZJ@L;G s0ϫjBõ}PdsBI qA2{aW) _*W_?p.q VaW*9q$٠?RtV 35h9hAVI5;& +:5m&5JsɗyiAѸs+?'X&|JT7aO)q1Oy6eRVq+mG10v?ںFG+;E¸*+aYO.,j(w*-v\o>z,AW)y }LQuĿAo0z9 \@8Em2C}&b:xp3[f.Cx,Ç'!Sc; 1HUBTW"$^rv4mHeh1RK3?EZFZ{@]Jk..آIikwZS. NEicw&jkUKn Hj/dT`ͼ2PKϲ)fސ{* : G5<*쌓 KG7ց D#%OaJ=7kWipj%TJt3Jy&|Irwi q rotљ:-a$~=Hr;st aZJ> >$m%G ӎ u d= Ty,@%KЮCZ;)r֤flݙtH$~Ts`~%b*F<28L2>7}p&Sf~=@Fﮃ¹Ѳ;}c# )db)⡮!x-"4&j\ڃ_%X-|G#W}09N 2WeH\|)B/:vtLZgx.,)Gz0BEx/dZoK\kaRw~FGOc@GDm ߨpyoI-CSM Vi>js$̵q;O%]݀r&!(x6cI*[/t3M% f ̲uj,.r+F5 ~83vmk3AY:,q=bSNQ$7!Ʃ@ЊleE5Luh}JlTJLٍ ؚ r !@Օ)O)dg)&mM۷ܪ+ʩ^f gzGH i:QŪ;;Reu-:bi wn` #^r|8l֜b99 BL J[ZbnBY<̢YkQaik+=L]㜹Tǹ D,) :d9P0# Nv'X <A(?8U ϶#QSp$k(\ib+vXÉ=] ̥U:ܢYzx-_THiPUw**׷Is&jUpLut_ PcS=AekJ:˭2v3557:óL'֬:4.y3E=^ ,cǎazlnZa-U~Э|3WJ8@{!h4c㦁;&0qx^4O9 0h p ʱ*ossU4(O O' +S̙/9z1q:\|',2L,^ ƖtocbzDS/r2g ?Rf6-Se V$*g; P"+ ! S qyVGYwdCY5q|$kӜqiJ'H'TCKD)W:Px>Pp3<\ ΋#ՙ12@VjIHּNIY 7LAXXA0K{1[׳ÈV SO"l<p#`MF2vИ k@Hwmn^1b{lU@s6A 2^.W##g.6u>\(eGBǣ)]{Pd0JҞSW=9 j?|FHR(GD§&`x/{`'YK-csdωAO(hl5KЛ)u>x:R֗rt>=unsa;a*w&Y7V8WXZZj>JI4/}Ga$޳ VGIwIfk~ip\Y^F彁E qrqu,`bg4s# 0EL x/Qm;`\ &R_Dqw@+Y UKdýS)#ez<7Ewbϒ@{ TwF~}O&mMRDzLyQlj&x05ۑ?0?r*e(L#O MXMݻ|yTפqV`:-?>?U 7zݖl/Q\n*{gK&./#Pzgð_pQ .bl"!d0rǁɺ(gp;0aВBh 0_ch" Nm ҟ"o1ݨR1!Y~7yF=syt$3T/L$&?,7CDF?x`\UFC,;`p5OLYҀcd{Vv9oQުy  kڴϵ.z[)3|^4@l 4;7p|\MVH? PZ]Z$ eEZ YWs$}PwqO e~V%kSȵՈ8o˕+'jܻ"clqi̖ "%Q h_>`xYKDZ\[mSuC j5u@Դ<^m~˒ >=@1E@(ߜ?Q!+܆>47`A{+X9ȰS R9bqKHhIAT29G5serʩ׷ %`= 0?@ |1I]7$ ;IIRw'Ϗ쀆MA"d*PiimF 3}A!l~x^pV؆,x$QٷQ2tdnF'ز5-摦UI qT?BIjaQvW/Ȓ(^drBIua9KS~^\^1 ?sv2?GH\뉤r +aXǃG!F|aG,ձB?(U`dRS4N 0FtSvqཕ}>r(HX, Cb*p'POr3:F-%zqd{?B%3̲:qx_YpY&̦+9c S_|(L~5l&2K`]JcoG>tr[cn;Pg`JOzP "@! Mֲl8ިsWd^҇S`J.3ebw!̿o9WYoƐr|D.'K뷀fw؆ҺhBxj楛'ߔv/o"DwRsNlȆ-)svw5H('Hե8&&CM^dCET륢\yˆn5#l:lP=h=.͞F<,6+ew5=+xq~4Ǣ kB{7=7Fɼ+5r+x0lr+oϚ#l^\šx_`Gqcn* q4\4AE;c@, 5jm -?fQO9Ԁw0؅b1epwY}KjWWf$B}o=u~>r.;J/ xI&ʴ2@qr,VVy)=+k i=SܫgME$S o_}N#I7p!h\chtb6,9mD,Ni2I_ cWSu0r˴NLD -ECABOqqJFc~nC1*Ƌ_nEaHr[)NMumrloCuA4vB8|8'n=)U>tDLRzgExS-E`x)e3Jap7*0:w.4\S4a׶y渙Elwmx-'~ ɚB h0wXs>g`B2,%޿p3px`}IB:(ٮ,EeuðqgM"UϚyM*&gX]lXb,[im9eY4TSaUԧ(MӑT[?P sj&jEħH[% R(ucuxW1jPhbE P~A ݵxエi]0o.sہn*x3Zaɝ{L|o|ߟmpRSo_1X(X-J B|>ay};s>B'5SЙCxEޢwTѸ-^ YQ]w!z5G6S9U< 5%\ Dm~= /)qrBW9uJ 6bK(!O29<ܸmMM]flLRȑWMA [;lCtA\fOם2opuȽGQI 헰Yg̫YAHq@Sd(KB%$B_v%XO\ݼ4~>l5_R+7.H4xT"W8?\ i$9u,ЯPv3@?c3d OiL 6YuQ@%+*BnݠAn/=4x,o<L =HiMP Z&}an¼~}~(hrm<㙵+1{F M݊ |ڱ|{`0N˅=X|Bvfj ~ޭg!$#g!JlWbMv:e+^ "$k"+m<14脤)<(پɄ.1v{eG~0 lPY\@dɿTk =:htOyT.4z,[{J3,8uIaQzJcү1!2 }֤XNf6Ս="'M䦯 tR髂/O ڭ\=l*WTzBGťV'Y6)OT"wWYKL\6-hGFՕ]eȻy,;} jm~G?R ǧ>\qoщoVa('|ưy5<WszZ}{MIqٍpc6U>xطٞ"U۾B[_9&{;ĩV0@_!h@=-c E 13'Qx36-B ix >u-r,tmP }hYf@tG>b)(n=O;*,IsG5p\0'ȓ3SrI=+Q"% *T%؏)ve A-dLxnS7Tx| ŗ۹SmUܨEg>J~bluf%]`Mrd(!?l3=c a .Ḅ'%03\_@ 8isF5Z Ŕ!~&cD @*2(+gщz蘞tgNOoC%TI'=`3MzTS@ ~ʒjk~CI_EM7*`.o;-GFP:GXSﴕqhY*(5TPU6Xsm1_ £>rd]Ɣ# +AS +ĩS}& AWL9_R?ܖ9sA,~bmkpJ^ے@Zg'I3%Go)t-Pm H=JM+R}(#29u#~X0f@M<`Hw~zN+WW(!j0?muaWb7y@Gy{ef^ 8D魖Ѕo0i-u05A:VxD r -(iDQ ;71HV1u_r"|1:`(a:cP㕐= rNYy5!(!<ze)񨿺czj{^\GS&;u׏?٥A :ѷe&4_d:H}2IFs*L63&kslIK'褟7Ոɷһ;飐+ $ 29!3XrYX"w9EF#]\J9,R"`m vjbGلw۰?Yq_Gyl1iWGzH5ږj P#vXf:2.tûE>򣞣]qE> vih ďmʼE~N ^5/p[͐T O6Vێk}]x*7Y"~HrCqx/=}"#*}VhZ}3,O)-2,ّvi/sr ԟjkN1^uL ZTNyBmqr8^6QTU#SN Fzr'CjHngSV@00(h ͻwZF 8d'?~ˡg\`&>Yl/ҭ@Mn8`ZmP+n!JVE(#X(`S $6وD $a\b`20Mg毂X墭ͽ\2uZ쾦= =QL ]p 9}֥5p Cm`~"̅@VFS6äJIՆH K­GT:Ԫ_ NN1_s~=gWY;TɋozPqld#Ak<{q1 F˙eF |ZV0(8շ)ǷUEģ =U^1lT>\q:*)Kol;Q͹`Vg4$Q[{)ٟ5tEy}i.[J0U? $W/%R@T! 9k,U ]?]L#Y7#>_(Z6X˴ѫd^v{/F$ţ)UC8C=4-Me[^VkvͬgUa2/ۍ  jT`)R#FIi%h+p9@s.O$pc_nLq5 )(L[>E*+ZoV{EovaF޴e[N(Z3#{#zQp+7[PiBB;3 O."^}}%ZdžʠY8*D-1~ M[\aӘN$1{WHPP[R|wMZ^9j4-zvD|p^ <ʢja|K_=Gʿp w6~_u'j_imڐqウUͣFkf[;>D+D.>:Z$n rG[ aE~=X  lόg bS> >tQ̅,84\$-o,u胩oY[fd擟` E(]egU QK%0\7oⴅxRGԏyP`ZI;bR0 SZo4 Py`ԁ¼N `!8˳ U>! QnD~!c=0@,l!FJo~\YHs'g2ɍWW#B0% ]+W}ȹ_{}I*?YpzcVfY4č#mO6A6wK !M/wSdb\'Rg즫Q5ᱫem%?!RV;2 1SD/djiXLljxwP|:,?Gd!X]Ì ./Ѩ9QU|BjTӺ,DG& LdefM>aO,oGѲtPՆg᥺55Z:TL_c;] " _4{S. o#YUs^=QJl`B;Z-M6%D&]`bGJ1mGG~Oo5FaFWYҏuYLkXk ,C\%w*&["O&ˍO vڌAƟAL+ptTtcöE Ԟ@u)h!ֱx9䦭Qd#s}EfٍLN`0ğQXXWQk9^Q+lI0nU0T[Wp7]DC 4VGS֖̄ϵ{S7HN|KUl $UGҕɕ]bTH`⻤!/arީbPbkǃ#6A[5I䖐uG ܋N{'PIlv`bqZ0=D'Zs|L=YzŻa rP3llJ\]:)yLʯ5q]ٮ"G~?rY1X@%X&4Pѫ(AfP`qf5 E8;եIKmsT8G{Y^i٘w k| ׏Ǥrt%-R:Vri|6K4~Wbr.Sw.}c a]ło/M&SU>K_P 2/6;;R"tJ`8y,k!bGHK}4wƻ?{ )}6R},(Yr \a3qyhH'9V% -r~d:z23 FAY 1~] iN>G@L& +e7Xn7H%@' "`?$[}-sD!5WePFǭdו_, n"" C YBYD8ğA^$+_c0Cay nޔʡ_|\ŽdN-?h 29JitHyj;W=va{wߴ>gKI‚S v:>tysd&[]Ft(E2dx_?XϴwUӜ6nc[dWRM*p ̸ l*%b>䲯1lj ⾩ji Lj*,& JV^Y]0@CJ_2I(| vSyTټ- ټ?H kP0IK;/CmyǏqu -*MIbkT񈹙{wͮЌ'b8K! :=Nc>ԘBVeCXIŜeQرоJY:Κ&od8~ygo6A¼jVstJQ!iۍҟ.** O(V/PNY> >dEzLߋY!WS\Ƃ#xwT^_%<@7..Cs(F BvjNLfr:p tLݴ9>z” Qg2.Q~!t1:&c$>g]S,F8$}r.F'TdCAEc׸\S(_'PZ^8T.(l9;fP)wgZn9 /KLb{S l~DGACbb$/$#oZhL>xh'/] "/{'YqÝ0>#IFi$ xS<@,U+RMqEkU Å yVPľẍ́$(erX.[RE;zI7g<&^[> XP5FoBsrVU-Υ^*F].љ:ŤKB-(sNn5+(UɄ?V-F?m <)iO.->sW6M౉_aZ½]⊢2IBl]ׁ]Z_~s4JM݊3Ƃzb(Z g)7M~ŝB.9IH-gK7Dc.I]G Lsw0ϻRFJev ; 1TӗgZ oә}.?)jqtcD2B1TXV>?5]0~VJhd i:DZB^\- ;MLz7wD e_lFb؍X Wm9ϕE@!1GB}|fQꚋ|@[ va6dX'ˏ0AtDd\9V_bư< U,d1|:!"#/de/s Aάm\j*M P)A $.Vي'P9Nɒ#Zԩe fT}$áey5YaqɁW !w\f_aU󻯷yFA;2 LTԈ͋تCV+"*>YZ{ diSa9"J> 'u߰0;Nߤ7E r.,FvCD!460]tu]XR ^\Z"җSUhe- q:0"m[ ~{(R7JJ=A_ ;(uab[;06 ^ ?"m ]5|Dk` ܛEpuFg᧶ܼԱu_ [3k\?<Ge̓K"[}=0oeIkDr " ӓԃa1R"p>,Cc~:SHr~P~HL\oP bm ãkf M|ܧXIC%u TkL#MWa}gz4~JHEr\Dd pʂBlKBN uA$2OŖg3WoԚm ?C69#K@Ĕ6f;a[7pg*f;ƶ8C@:씘T rV`O;%S㲏5&xhz];D7^KXZ{-uS`{fp˅@ W VҏEՌ2BX`upqTпg͗X.wor~)ӣ1^5;Ff/g]+Qp'&Y 8/-ʎmoK ]JAPDM@~cǫ-(o `DŽIn{} wž[>em n.3,M1z%Nm)iһjj.Љ1yTVK@7h.D,$4pޭ^UFhja oevA SR4/8) HpE$=de,ɤdCwJh9/ېZ}ygIx<؛xZT5|Ԇg>28頺HKzOS4*Or >1J=,YEOe~sf!VTˈrp,;r}yʑSg k޵Ǥ lua ޶(zf 4oAƇH=EЭOA(K`#Q_ )D.teF9w%:f?6g[ ܔ Sl)F/Œ[&@ p5S5ī&94# ҄B 43( l/KA[RP S".R%XVmS= `[T @. !rPݢIE8Ayj<ĉ+ϔRAJ7 VJ4Y~6 ˵ r>i%YO~s>Ʉ <^$%5ꦂ"x&e- F j5-ĵa]ϭSN^l(:D"T~fU:41 4?!;R\[pMJzSz@١$\'Z^0 Kr6݋ѰE@^/(2yy6qFNr.3] CF l]4`J4~]q=uhQ҉'DqHmkjBBW8fPTi Wlb~y%7-IԱAnqAffUTa*ɝ^՛he66?zGF+kݫSs pH-/5,J%}A v(B}lU7PHw/@,7BVSU8H{гw>vzŞiJ%/ZX՛'6xd,G ]@]ۅECѱlW>;6b95=XiF."o 6*}L4ŝsotɼPƜh]VྙBg̸wcǘa@B; 3gVϬib7dnˮMz `$:!-+W&Qu9+Nir]ĒT{Qnn5mi7G*FSZOD]ο,VB5CGAY'hߥ3DW{ɺw*0fJm}RYSC%?c\/I "&OHԮhC2< yu'Kl_@EwȼXJյE=udLa=>eaReڍu7|R\AIzye>vmy]("cV46SO  O+bUdW*-$ZN6s"%ϼhsFLFIiF_МI~?{w3湴O!N +0Q_Eqnص ;4Ѫ_Jh'#thj=B0PT yE117cQ/jB+l;] D#PS[V{l2Qp73@Mhs(~Wٮj$ؚKO1ݖ.4эiT2P)qw,v{f)n> vOZۿ;22̃yC?!F r Ub7wNMm ^:-eaj)-8ɟ0OO\2M2WH W'Ps;$PSbV%v M'_' 8)| (Y bHe$4-N-l"6Chmz3+ ]yM7V ɵy7#ŕhVq/#]{ D:'0gat~+Io5K:`]AM$]Wv;l2il B}xx',&D 11hÚ("Y|ҨG@a<ծJJ?wvz@,}A|(+,8npm!&qe {wtD taJvT_yPb`BH9`( ^QxY%~QS?^tJ[nkvb(x6$wcE;%(VbXr 5ICMòBvֻF ԭK6w1.xŸ sDZ';gTXV+/'0rCyb5[E+0!-pz:7~I-hXϞRt Nrї6RvNxs1-7|K|cx6ZrgO߭^qw1H6/: z9"ABF΋&X5U{QuC"H6}ŖOMgJ{溰5 .2Msn3kӒ;%cK}q?r{'ZݢEZJ'Vȏ͝]⡒QHvi!-.r]"8L66c_~7캍OD_:0dS~tC( @G;rwTz9n2CU;1&k@e/qLe46ƛϕ+̞ǰ`+$Bm */p/Sfߋan:pӊ&.4LE1 fRsnGJڳq ,[N`D'M& i*5檿 `86aѯ8^HzIDi]mƴ~ds }ݦuPl->dc7N@%|DtâejSqzAL"}Dx^aHIUZ*WgJmk'O@fn̮80; 5@F&k|FG fU 2&{,k=/5K }T;.?u5Q*^*U侱v6G_~M8I(y$Аܱm@gl[u=37 oV%rPg *ݾQϯ_G!$^nڼK:(+rLلЍ~3* c6w]y*pm.ax NuigGvSE;7eDtIlZ}emHrޝD-r::pKTmQ%x_y ҋw*ϋOĕqFN+V~ nOǃ[YetFcDwVdP`(K%IouG1嚮D Xi{y ̃YNFU?UHiSYc-IS]5<-(vǿIcD3բ&)2?{G'YEK|qjmU$!Åچ*΀[+ 8M`M\uqvO9`e\tB0Tْ Bcέ5_Uj?5(pf{x4c{@?onoEl$ Y&>'.eAZ﫡(ni.҇~ashWԶRoJ۬bVj]xRu|YbdU_2n]0 9>p{B|`?@2B(*'ݫzȏBgMpf1'Q [xBTi'Bp79|.TUP^y W^Ƕ#B&]h=&hzD|-V)R7CVS(PT#w@u"za SDQڗon{$i 6ꌝ{<2F|J>\35|Q7}5.Z/gJ;(e,oʟdA /+-,Q_vWƒ.r= !f̍!nVu4&6pmAX@Z%LD)8{J{ 9op?!#IYaױ6 sůfM=B{2}ݙq!fdr䨋e* vA;aefgX˩WV]!cE:ua8b y}^2%~pҸJ+hq[ P ,w)v:Fβ]h8Gk:9&&InJR#ƿa`#2&fX.w7hZΦRsY[4\. }HKh _t 1= pڛXz"/eEaH_K#ց!,D*[B $@=:)=@#Bt{NȻ9O ` y5S$66ړ<:TOV}%ڛ "t}yU# m%@kԕD}ڇCP3UG}z) ;O_.hm)i"؇|=8ٙ$g%,\OH>Uq="^&G9Z 3kqQ!횈ט>6.Ʀ5^ ٯGrHotw79,;YĽ`+mo N%Tyo^ #IiٺXJε|m1ɣQbuYSiP:ž %!W*$JCrԣ Y!y0F!n΃;0ҚVZvHS%n ".V[ i*>a[XE!kRYհu"q@Z-j `f\ ]eWX0Ua E-OkZ˿oҲ0Q{(a5EjT#Ū?V**`ec$Vmu3_,Te<#IGE/}>Ƒ;_QlܾIWpGǜDAN)><Иo.iR"ЮsaPύcm}xxg`a[(6@" ⪕XWrmljU 3)REϼCw,SohX v8(>[B\z$:+zkO?3!CNOn ^o]"Z5@{INu?4!1 8~,8yw("CǴt>sCfYlsuM'?\ѣҾ\Gn cLj}/ R^܃srƸ|dY&.T>4+Ҡi{ (,8)՟KML;sR .1d5@UtD.~g&CyGx+p$ѝ/l_ t4R#\1iޙpqLCzT9;vu8Ո <ԃ;3`wT4n(x1͊KXV%@;XLaH%anVAyFeswʂ{}7 7zmw/s,V&G^7t8JggdR wLnABQDGO<x:Ȭ؞RhTZ5g3l ]p(׮):+Sk^mM06_4H뒫w{$qdX2PJMkdU߄VQ*=U'RuE%i fqI:I[ K,:BpR^V46 Ng ZuA+II}6G`8-x^s,xYHqRϟ9<Ύš\$[}qHrܖ+wAk1k}_/%:Dwk21WMGn0p<SHY=} BY d;A1/}9Z"0[N܏yy8K4JPEI. Γg@ p @%uN2.ün<-JmLа\`c|zU=4FrHU G0>cɕmZ0)"kRU|5.I1 a߳G sM #ə:(=lhU!zU*iօ3k0*NȥV\ĝcĿ\kӆKT7 ^Ċ:6ody& Ƿfko^!THXES-e+4^)<1)IV[]Ob- ኗe2B[[j% n 0&Otr 1@eߪќ/A# Ui\$P(z^L4 +}0g}Jƚ?7s9@51.̌z(HnXBs/rߢg53(Ϊh*5gݓ[k4#5Ф?tBh(:_n9_3V` *28{MZgƹY93qQs"Fx ?9!~@: tU d M.rc7toB^(Q&ՕZS+M * YK;dD9zZfEWQV\Frಉl#U!J9j&~;$db4%R([FzpLwGpg`@hbuucC~ W%$xw.0G^ fŸNJ3+^+~O?~ݰ%ѣyR#jH#_ϬHw_]7QYёgs"ƟV@ݥY;)r}iUs>rZ̾&s naGTIL{6o<.ȷ{ˮHD̲drڛ!u6> CvE.'H*wpb69U}m6?rJm8ޛ<̄`UnDN(^<^e<*nU\Fԇ6Uj5N=ϟ*q)+vs \ExR:owR֢nH(==>Y0^99NM5@q1۲J Vݱh!KfG<ų}T2›vO0˙X_{Fy24 ?ly jD?Gtv #ʒ8?5[yBp )uTbF̛x3%YCB.VKR&Eq*s["vat"YI'00I10Zv޿vRd;N-} jbkM)oGK+=`U^zh7vWTN4 (dC|$id.% lUNv^?`<"9MIӲc1-_V|(|k6zy9vơJB y$m2tx-OkLHt$, (uuQji>XM $0h A_8%`R^ҁsw<~q#=3DL(6@aG,Wy{¨a}N XWD{ۛx`mLNh)SD&zVCJ}՞:>0I*0z@+q`A`gۢo-/Om.2$%Z?rݩ̜ K1j vXTvu2J0ԅ_Y w B~Pnnm`cf*udajAJ&34|c0 Ց: O^uцrg;߅aCv>;j}im@Ok,o5O`MR[++{Y[gAL!(řӣg'"5#.rG*28~.Sn㵋m=Ri+'Q|{]IK Jo43|<2;з p_ճ'u,;q䌪Πb›n_Yנ ZXoj7 b* -bwqKGc*f͍REጓWB|-$/'d,u~ɞfBgm'(ض ;~$GSxDNw xiUxm5ɵ/M~Cư)3?2]|\aPA~wIv$=40$]b1na3|/&7,ӿQ-h޺ځ%&TJ[2]{FR\ûyxuRs:h6~vFb2au08\Z>tߒhȪe?A+ڂ\ʞaQxzYF/T:\$VxTz<_A%$}bɸH&s~z nf%8l\rtݟrbotEAsMv ˪N&(FSn]v8 ow&cl񼤗f:NRtٙVsNj g_εa"K}TEXb)[ Lq5`20jEgDp mP?RGˆQS6}xp} or›kd/1P$Sd kE?q  /x+s|¯O>%fyL0(&w82$V@ 2 0+ e%bKerPQSə0aSIf*j[?Z( Oс_^v_QXӆk\d)k5٣֓k-@J+kխ 3'.l.ٸu|Դca!~V[b`g\0L_e) !S{)-1F c ׀ûRlW\tg J՗)!HmيB!`(ab/ҳ9~V ?kXL`I`X].'&&\wlؐZ `-6BE@A y #$CQsnDT x\yͬd[]f0lkR3t]?׆^n fx^0p-/:…Y"47_ɓ Jˎ/&8_w|ǟZ|MU+d&л.ݞZ]o*=]LTQsXZcN -f*ċc; woWDgF-gZu`aU`ӧΗ#-W#7чCعxֺ6Wi0N}=qNn$m )Lu4+z4eؿ.iuأip6~LvGkw u6$'|xwYyM2omNꤼ7nI+r S# wHNXhk5)캞cT۫׶+$Z}qtӡ`F;~E9; wȹO8c<kwiUR#`ɿX`gbxp@,8 Ċ_0P*]Ȇ뼦g[m 3tk;_7|3b) I Fi4־66w65LZX>%u hzc@鷠33)#.7"' H JO:{fV$8a}=CeBG\5d~KiO W=GDvtEKDۯ =ɲAc@G-1H6N#KUخ6]C8as[4|݌.w/$q0us!:|vpu.دe24 Wmzl21JX\̆~4#0E*En06_(t% Jc7Q@|炽akɒ#C~Yiқ\ӗt?#,bϲ2Af$8^ϬTK{(ZJdwks^x#(3 %|X/6q.LQ8hoա Czr&̮oKHtݖYW<|wOyjsGQ>tJ J&0E{#ϑtU]O UUYF!:PuD@@2@#/Ca58h3 G p U"~9֡(ۑ' j`!>a&D "#,%P#HyS"J-m8/G6c7C:*Hރ Uۻ%ƙql1˸qOWfA("| \*j:d4upx^VIhgS]m v8Lmz61*0 /it7vIl_p1xK[9۶}}QͶk,T,<-bhk;HF+G`H8bFB'~4]?D*OM p靃O jaRU/;? ot}.mɒ*w?OqZ`&,tu:\xiH#7 /rۅIuLVʐr9!}S,] >)hB.E ΏQ3UO(!/;z~ӵ߮'.\lT6k2-EjyLhWw2\׶I-mLE(E׭"ꦴAv!s2%A0!i`q;09~Cu]ݒHl+ ءq8ݑ)iyE1@|? 't|PF8nWrh @oC_thTb qpRivo%4UIgAY_ 0;;Joojj; ѓ $ o Z[f.~w9q0-LesX^A2@KO oj?Bvh6#}FHR<'5{LYg9&6m&rc1*4jLQR7E۔׍`(!Aeۮ&)ps#k.Dk"Ӥ rBð`ﲵќlnǼ|oݛ^?M|V6=ck]n>8gr3}KV %ң$^4Mun܌*8PT [᰿ky|AyQ>ij0C/.wh% U y(H@/V͔*Y6zb/ՑNPgl:&KQ\;\X ce+IJkrᏤ:JtQ5P {zCf9T#ٶ==DŴڍ~Ӥ5)d gY,f21]&FdP=^]?V ̘сw'0d|#mK OwCerY; [AyЕ5VOh, Rhd A:9~fjVqCdZXp)|83Z3-|X \2O9zq߲ɠzr1mTɢE64Pckq$JZ:xkvmd6 %<K*lrT1TP R^$c9ΰֶB,  1&+P:e)ɚ/߸/V;kFU-KJw:+Op?UHZ:=NcQsQxrPѤpyW*3l|͛|٢,;bty1\{¶*v3`Eәlt3W17[c@ԯ|?̈4d&2BipM|ۙBw/Utpdc`T; $t2Qr3g[ħ/Ǟ۠^#*^0s0lCeM\| o%h̰UG؄fM.t/|ܒ38+ӫe=&kna(^S\=G?~Tvp1疇$bݎnFWp{1rn:"2ĒKv7~O$<(-jM<1"mI\NQW"Ŝ;x;#,t0B2)oPa͒Mӑ,(E/qgf߰>ٕ5skEUU@dtUE2%p E(ZH-|4$aj4|**_j %Bمo6)lhCwOwavFbmUA_Z.釢 E~).*ԝ@D_(Iێ)CC避:Esx6K%Y+CAgvUbsN3Dc.6MuQvxt *#%:pPu3̉&9&xW\ $ _3qvJ9Sz_@`$#[\`m}KrLvqkO1\gŪ0o.ϺWflljC\Tg'q`AK_O,fsV˚X8 j[:r++.Y7 1,w0X#W/~.6<.e^q(+ѧ3rP*Vuk '\=4l >EP /u*meJlw["RS%Gc0>a,R&{1գC^1Iyj"6B%bt]H㶊bq\\Z @ Rkq3$K ˸!4D6j`P)N>E 6E@6'ޒߴѻ1M?W!(櫭N1^T]w0xzK!1A>ǿ(Sv?t4,=zF~zSWÀl)Bp 4~¦_(@¬. xs\5t847ddk:- iW**+@Oĵx !t+ʅy(O.\w_0-Az5GLJ#r#&u]C洖J`}238@VD@NCD9YbDHAa"pרK`z0tb${ܳ!'֌)@g I (5Z=nM "H [TP;Y>r$CX%7*O|>?Q rf:- k"y6RVnLz,]gr`xaLD=[TH@5n~)Zᢢ9?y:ڂ ^#0ǐ " .S2-hBqK =ȶRe;$W%cր~~ TL:[*`//Xlȅ˃Q+$}kE (9 ^[4s ϫ4n-p|9s?У\qz@_]>n.B:Ԍ,4OVhHLm>h?`.ԨD'm+;uIF[jžlf֥`F 5ږ߹2jL`.+jT%_lzt&l;"kZCr<ȃ|v %aP>fxe]1Tk3/V>Rk>Msg'xTԟ+1g&x ZG5H, A4Xid)rAXV%%S+ 㮝M#IKT<-:Ƃh!v ¶E]a#j9m^2@B9W 0|cKC2ac5%R_i0k "z~nvnaT-GxZeYolI#]%H[ds6,.Yd6 (mhBr~)ڍk[{1$̗ +ac.Y&_Ҡ6w_8[2򾧮 %C,!P(gXwk@gz:r&coR>lcʁ}. yLh0C|8!F_K\62@rL 5&3<.^¸sdYJ- WZX(4/jDtd999.!%X B/UzǪ= 4 Bircy-s"=Ör% Ɂu 5#`ezndeT [շZ";_JxB̑VD-A6DWjjx_pؑDoߡ7&gK8AMF^I$*:^)+У&"TD`ԍ4,RLwR%cw Yb_Wk?֏~him=5[ْn5ƫ$?B20G# 7A!N4Fh"{1 PXhS߹iZ;.u̹ S2(YۃF w޵)4e(6)Dymי'? K$]O["5;g̱/yJmod7 _!:1/flZnru`KGbS" pz>CYW_&mT|bi: :]8ƒ|p>p#(l(]̝)<=GOЛ,z PkY: EюmGӽT Ǽ\b_K`0 `' v?X [5-C-It8WWL!<29KC;aH*͠58,(r`X&ox- Cl̈́{^|`"46h1x8Qגðb;bB n$x񘜼C]Z9?)/2-+9r>lAJ&jnL4XK,h+xׂB(}RY[(?G5AΌ7YcD\R+/xUH{ӄ=tUnP/'G!Yf oR[Wk ;ެFs=JP ~4qk&kIIB]P\PghS1}ޘa2v$!jjP648TM3a49P)r|ymg8Q$^gPĂ$N-w Wm!K\>m -HfɳboG# 3#.2$1qv]fş~bO`TU8ݎ ~?8^Ps{9C:SZ?Ǿuj|A,"N2wӣa"|mYq\s 8C㩒)SѧE*ͅ*#aK۝L;E\(^T5Fh:`T"߰?1oN1~cxʽ= ӫ 3|P o܅Bq)uB+&ƞto< 6`FX]wSTPS+F\_gSR& ^lXz; .ƽj[@JuwnXOɖWQyW}Gz9姡$C"`~Mc.ܵbkbnkrL&Jc9k*p/, ,Rq箆w᳌wawI+[]9@, 4;%Xs *yC#?~4ˈ"n~lO|$ ~;vG*LՉn4!>mNo~;+}5tRY/x} 2YLceV y9^[ws\겟R :Q  JǐmT( xA3VIsK4MK{~Ty٣lGg=9Xd yFtݦP~NFX/ fCPCroJnZn҄Q43^Q,W^r*V3$+F+)kyE9C MI * \fo#+͘N`4z6 [.fl)wjI{L}d^B5V3LFll(9>aXYrKDه-wRT:xE_}ytܖH 7b )8nRQU_{Rk[ 崒ԝv?N.H9ݛ1WQU)j=()rF~lD,7pd5;F H˩k_ kQMm[2HjPpw|FǫM ~QZD bDN2'wb )⑓Ou&oݲg/LqiLsSO[+ǔ.\JvGx?>whHU##|x 7v\Ɛ춙= kdI-`;466kE."l%>B^bχZg$?t q,l}6~X|18Ju9DbՄ>-)v-pWax4=9?e,=*We1 ?|nb.4Cȷo sY4>`6 8N 5jYeELY|w>} +%Lq@*X8Э'2MaX^"Lv/,Q#(}E+ěɍ]0{H.?_e6Dczh[-֔: -q밇!Ͱ:o.PcWh^ yBPt(tMflj%"GuȂ%fRA<<Ϭ2!FSs#fM8+C9}ue:͔\hKج!)M[QH4@2DaDX(Hc&D@ A*Sitأ?g^whB@rt-[S}Ts)xw{[r[*>n$<];|tv1-?YTyj}d*3M̳ ak&pM !n[-@C&BuPh$7ѕhMe˹aPLbYe>ݠ5i!5avA@So>UFM-EvؿV*"niBܳE@2qwExc\{1*9VՕq#K'=[&) $R)iWkͣ@JIo,R99{y,)fjyM9 "XQxuQeݘ}9S~leo`xg.ԷZ.lsͥt*atAJ4p{= wrm?a[>*Yl LⅾDږ E0iwp" EWn_N3w C&43U&;T$K4z= I5_x(IqzO-^8}C#ub-(TcpYIMOπ3stR!DfhqpARX΍876pB: J+Ƃ$Fis<\nzMZV_<9*b0rx MN/-85ayyFӹ$MW'*VdUTl4+U/h"n<Ш(VWf5AN&٦2 KY^k+[of ;ED%s)1ȁ~QQ%Kwt|~.Gz }{{6."E&22S".iX-]0\YNbH<%܍^%Z6L_"n/>PR[L^QTn)];۠V RJa-~X_f_^cajoIʧSQ:W7z9o~ll~ѡW saje ٍSOsVQ`cÌaV37C|\fDČG:tnSa7\#oî7N)ܦͺsXXsj%㜒uN \ʥX=o˒gXz~mK&cbټT]*;s]s'!c| - QYou1+!4lX@hI@;C;NwG6+NqzFY7%Jr#Ps:P/F} 9[0a$BςSzsk9G׵8P.t5$b OJVƇYc 3/$i-'\% T60* F\x;&J1S\agwbK#JX/KvOdHΐWOI%BP[&zWDDif= ,o wIkTgl5dOp!~24E9T\U1ʾ)-uɘk*c(X$6.$llkކKz "z=jjSwyq^`ߠ6mty00@Z*%t:h$d+(6d?Wn~=lq0z\s $l]m`KOHk7 . qʮw14p$ySƁV>4R?,' uKl Η̏qAS$_v, 4,Т,SG*N1lU%3v?h"` ?i8tlL$jK-ƙ':'- 旽%q{}[L5AySw،z͏[$G4u(G2t$]_TEa[/{ GAs\SgKM2:wI8窅"lzʅaköwͦPR+_Z;2~n+`{΂.FU[֢2D z{jYݪC)"V&5߲R~ @Kg&fPȹLݢo)9>!rF͡lL{4J;DiLDĢ3 P^BFr.sEbp|_fPuxC'KiЧFf"(6A%-ע "?|>=#wαd UiUg.F Bw2d.b~iCeGBww7a1Yg죆+IC]ےT DRMB5u9Tţ] eCZݐ 0(WE ͳ[1r}kZ|ͼ<@OD1cWuhȨ> rd$gyw1=ϵz~~Gf?pa>v+fqGK;dyQ[dgLs ]'O _)%݊u1A3fw8ֹhգ39OrBC2]>Rw }6-xsRW<LP\N38V*/x=Θ10F\qe4, \7I@8Ϋsc/t)Z.kWR$;gV=M~EnH`]V>;"&.Bz;1꛴PWimG m]W(Ap8A'__6;B.>^z7LY]"8N;C㐍b1\V@r(ᩗ^ൡcB(鎋 ߀*ZPԪ6zr:DV'?-!˜VaeҴ^Vԋ^1VvfKp+ dHEgS5G騉N7OceB @@[Yay/C„/ *Bm&Na(*YS~!lHMR _(76Eb4 eq[CSH:m\AQ17\A>l2V i'm1M&7$>i_I]עv2;&pإUh>͖x3c#E:˦/ғ/ʶ -k'@ a:A n2@B ,Hʝ~D]ߴºUjx_$x~`(\b:~-S,X 02|٤:OLc![zY!lEOLYϻT=ÐReT(R,GfٟX ϘyipYz#ʑs4.1z (`[T +O<MuN}q3Nζ {zM\M/kܞ`IilҰt |MZ3zyv#'oƈ,7xQcb5% ^ښ`&hu5*u(ۄf1 br72 ,_&i!bb@|ryR4Im-*n&KS@i7X~G3|sXqjDJ]M*QY.]jC0J!Jkbi>3n@r%){\lHD?L8:LdױHkzRnl5ZbWQvK̿2Q2@{1X'ҪgCФE.igr"͖Q1 #i?1!?z1Gd 0BC6 7ZV`IAuQ1M9y˦DQbep; V%'ẃ{]FbD yY˶̬sK7,1b2i{x+BXBǐ՟ L!EHn*hMOvhv6:*& JswPRG PB!2QQ~e^(O0>h~al*L57ymy(qiSKo)o?>IPe.Vks2{Z5GP 7<__8q!Qe!P8R&)7ZyΑ9k !M'Q1њC M,/S]CᎰצsjk$滋Ӣ?9˾2/H;QƔ CʣVeMCz;\y <ޑT09=kdF?<7Ud%[hK_*qIdhq8q!!׳2SxY(ׁe@zt30zR%6,t,?Us.y%+*Y{7k4g?.8-.G4BQT@Ǵ测* iJ an:rgȦz|,DM۳]1txZ|׀~Xn JtaX[#@$}LJVVs)M*e>`k3.^bݾY7êנflqxI/sL1 Dg?W1_殀GDZ+n`e'D;ʨ-H_՜c;*H@ܐ xx/fWZ[_#=-16 { MO#Tvrki7=;Ys7Tgm 띑;(Nʎ/wgQ:4x0" _M0gΡ .~Io{q)5pê i'c i~ e[߁;=sX &Vͻ/ |a(5A7wg|tލ6G MNuFBPP UlЌ@.j9޲y8 ~X`9s{y@uqZ*p{LLf ۵'YσnR=&-Ck@([߅Q`w‡Q5%mtfևR& ƻN}4426RQuV37 E*Ihmacq6DF1w﫢A9ȏ-# i@ /"l&G*k`>xxgӏy'GA'}>h$ ,9,,K"a}IXOדqϨ0)o=xB*$Lע Z)ї6mT ja@؛~7)ûP bNI͗W; 5T?* Z3mpd/-cOrE1Ћia x#}mDz0Dr>apE2$f\%{jXXw QI}?E K+iR^/ :5q ּ NtL϶v1UϩSfOJқCMBMLqS֧y(.Q3TLS-#Ve5-m[t8,%C 3AH\?kS`Zhl[6漘Lh+$QnKaLѳlirOo/02GUZɭJ-dK¹[+48"tV$fH4ڋp摥VcFQp׆?S[>M{ʨ>h_ah?H@P<0by/r AsCK4u Ǯx9p7ҧf13:P@يۧu#]ERV_yLSt8%f@ɩ@@s W.'b!Gaf1r"*uv!Ȉ>OA2\)#ZY5_BP\ 6QƥS1-ْ0F|*gMtsP.?|Kmj댓y'?+yC E QzW& `_1 +^3vY4jx J~/ 8xyPBe{_3iP> n_TͽT/(J~ʶƘUԍGx\kF[eCȳ˱mYVHXcݗD\ղ9!V}d+(qC<}~jZ=E=Mɱ,a:0GV p% ZߍY-{E7pH_\SEVB?LNZyTf(زJ+fZF{LNxY@n5x+ZgSt|+H+sKRfdNt$\w<f[[!Hp׊i7L׬SnH#d~{|2 =}yF]y8f$YJ3\a|@Ia\kHf7O@]( B-CH4Dnk*_B"Tz Ti82Z \?DzS!0zHDY!%9pfBꑍ.~D q-hz8 5hH3巻ӞSmE&Q^v]B@SBlh@4it-돱#'@2?H\sX:5,YiRJ6&2mBIPogSįA͐ݸs[ xSE+%z5JeWz5vxPFi؁/h:ȦV5s1}Zțc,Tw<5nw_Tʼg<.^5$\ TWz4R.&dwV0œibr/9qjXƕ:Q| Y|=T s"KeCyhktPŋ"b&.BS]GXL-jb]Ur* h[ulָ_>MH7~q(I6G϶*BBpy 7p9h wL{Xk7]5x|gW4}xoޖLLGfla=2YŏdD E]aJ â>FEa޷'&:& >(@)C6U݁;T;K BdDc6,z~SYo#xK%e?i:N 3Ўxby;=@zp[&9P|LO٧3d#3_{(#)7fBV̌Go28/e}|a޹oͥ* fMns E#==@>cgSz4l&kLUHZQP]sޓj%Pqm:|u-@ ;1f`؅f|,%h0Je8b흑n%jnsLl ʙu~jٴ1B]ʞgxԬ $|qgSgGiQ ) F#3T vkϫޥWQrP Dp,'wr@%gX"L+?v#e%O9ǜ#`xd唀 !ųj!5>}{:MgDݣ0IK, L<\B ש vtTf֞WVw^yPz.K)X+܋`">@K,E 2Tمm&:Qnu|[g0AerށȓH@C8vc42a݉P8d1"2l1L"^E#*:`.kh+nkYeQCjaQolr'NDx؞3y-4:R&To'v.߄o3[JhV8M頠|KaDS8؝}1>{uS!aN I~n||~祤UT`HOettnU7:%nEyrBIrB)sy4ͷJ*9kVCYnUȥ料*(04n9F!)+:Z1|nL|N[[+!s"̄2ZW1aM@ FG?;uuH#٨z @u ~Ӆq`R{TGͼA=Ptx{ovF"B?*@🥉 3Eù?Ӛ#h+fJfŋ$q!Tqz#Tr%hY=ΔuYBcHhΓ‘&Ǒ.|D_ỹE }?܇NSxF]i_DJpYdofH1;pt}4Ϡȩa--gtFDdPdCPbX"!W=.| mL XaV,[E⏩@}.}y*+K(*n~Z'4N7 LyGf7KXeg$/Jznx$~ԡ3y"-lq'D[( }5rYatDEA`<L؞joϗkOT/~jd(l[JjPT F^N:f"كD*WUz̀0Cc0gS1WGMV}\f̭+Qa+N23Y,&P2<Fd4GδH{4r`a;"FH9q?Z¾^|n(mm\o_;寮;Z]M +2p*Ec #M!h9{Aٲ9>:t(vƙbѿ;@yiqȖwZ*.Mo5K!̝j^Mdos)p- }@9^k]w * ЖM;؃E& 0GGͭc x?c&3X搊*1ѫ)sloq?$)E~} gRmYu}zƂE[s)}vU6Ol 4rĨ-B6-W?k6^ihRxpm)ӄjɕ(Y8yWdp\o?sʃxݻj< K-Elޚz-GERij'DfӛKѴ):FKf{>uej=U`Ln7d熟tE v_`{J@u{ J~{< iMY jl Fs$UȚK_? D>,y".r2\4Ԍ%^:v6k6*2~xrO{Rc݊WsKUwX\VxS&3, ـJO|*!"LǙFd֖qQdF++:X5Gסk2)3Y PRg#iښI)y1#)͞ 3M'oO:ʻP S!m,ZߟˆF8{db`wpa9!՗ܟV5 `&w!UT4K)4˫-9^)MЃ{\t FJ玥O~5azxģR&?-1N?hlgҳ݃=caakN o9uIFu*Kf, ݏ<[TB%9-Wd׏r>ʙ܇@BDﻘ׈Qof/׎ӱsBپ#+GTPC] V%O%}pO^HLf<>;tk*G!TlTţp{&H9f E,haeN/ ":6ҕ^#Vi&e*ph䢩1Wn%[<AǴh$}PRoXz001Y <C˭}?Q[@nq6ҡy$=o-_jAbd1z|y* v-RM~v˟xݘIm9vPKHMn{CImuANOD1.? ]SVB;Ig/o:^C^Ff_{ =Jمv^?ukT9KM4 `s2=ۖJiN6sǗ 8:>;!vČeueE R횰uSB r!q_G \Q\ăC4Z\L[U\n>^%l},5\8M{exJM9{f .Ҏ.$hZjqoy#jOPփ[@h9?㤳Kns$Ɛ!p>!A`c Ef+*沐ʋϭn C FFKK= gu ~aoPB*!JUCCR *zAr3PpĹVD:BfmٻJ'@$9B֍90;O2At"9Wc B'`k6XN(풷.):KʹgE:ěwH+K"srx%4W<^+7Ou t=}5DՔg`n&Cϋ#y >+.i?jjXe^zfՍ:qi;W:=xL64^b<OC, DtOD> .эo2xf1! E-(HkȨzc)ũ4o{$ȸѓ[GgܜvRK,cx%ttdN cӲ\Mlyy>#?>%PW>-UNSnQm7:H+Rx,hcd2Հ˲NsV!$Ñ<T}L?SiTu|U2c~fb{GJtgXG^b(j5Q.87&╍͜ WFW,o._'S-u4)'&c,yB&F'<;*+M/8r-=_tT*e&\w|(j*dzJiIK@" :>t Ӻqd2&\L~Xrc `06<:]'kLisBi{v7(?N]^FhK+8Au-l4f_2Wp[fRFSM9ÚcK2c ״ux O7'NF4bVGn-JnV.JccS'JM)+a19>mF:.-&Yw̪gqkdR㋶o:n~̄/6B6͒G6Fރc= Ӏv 3lrNTh@~U8ܻ1;TcicuO솧(3 _ދ&`}Wg;O3%=)Ps(`6 3|wݽ~;ǸPM-/SJ+MlnoSbMXE!F[4!q[Nͭ|a.S 0d>!2I߻e&ec'uMߺM>hɔm BNHtRKbO2HgU PI",ff=w=|4- F iHȲACӻ.~n<~󊐷bu>r2- }6%w$;wϞ4 r8|puAU$5|l6 XޟX+F% Ze]# g:no\LpM$Ϫ<#KW)\REu~`7ID̂ҷX Z>SMSNWM,d;x.u~nŃ5 "-.;ɵP.D Յ- n>EtaOcPW2 pX ?h`w|?fbly'@սU9fN>Fe#>6[ k3~ )gE Zl&Lp:ع߻(OYAδ:pyp#-zd'!bcY8ZxPӜ[!-Nj]/jQҙKR ـ,,x-41.- ֥!Uq8FV=5]]yˊ7WܠX< !'TX$W#͑=%\Y@O0\pPp=bk,"_ \m3(FM+n͆@CjHߏ4K]2Z;kz,fOp  9{K6LZSnsfܡnA")02!ZcNʟj u5t{Z)7E1YN9ڢdmE>V^_h1rTsÒk۷b~`!xi2gKdfu 1yB^Jj /v^B]ٽ.tP-'Ys( LTg fq~wB|!Z oxCGV)8il0#@}ZVx_eOF؆T Zv/7%I  ՇV0-{Yvl6ueZ@8lgمM".pc0\'rI $<շNrh ~Zw={8[0 J"4?:aiD&CI"H S#!ZI^"L c{> >!UŚ灦msqÓ0aг//[FJdW]hK|XY" ɢfp@s{ 2%Ӯ3a-DG3kSFvBG LX2#<-75袧jOj1ŪMb&DDW & 0Be(ȌL{!NQƹ&(l)uZЌ *I`+AE]~2g&]8'uj>"R-WwlMzap+2fS52j/Αf<Ȗ|LT߲Zgh;>YzډFއlmtK.`ɋN2q&PԘIA ^><ތN&xV<b$HD_$ɩDH$BM>-@;: i\&'zl:E:cݸ9/p%قg2^6epтA%x,tϊ$6EOOЊ 7 WwxJMd t)o^ciW6u姃EOHwț1vbFu@dP]gXw@kt 3OmrȜ'H,}jnUdZX)> ut&`ʗ|B/:?jg+Nqwr'e}/4Ic&3mKPQ2.Ih[8:j W{Q"/Y$ڣwk(~Yc ՝_f,eYi#Uӟh']Ǎ*53W*ĻFv6.2b-y T.)@ *:O#w{c0Chb/b]à5(-}'.~fLŒWdF yj8sFYo!ԉݬ*'G>)y=XCJo۔\ppBՃe@IOX܆[z|馽n ,mxdFѩ!u$1+5.M>'cMaonUZ1˰ S A 36qGJ]HӃc4v6(%ZN9n}{;loov[|+% oQB>j$~{/ &^{z>UKW׀ۘrL-0EX00]e| !Z-1"9{bN&, pox!c4>:uPx49h -iobM#nkQ pMK%KMݞmqW7WqNOku%Pʼ5ô!q6JVf[BsEL E@K@K~n,"@҃u*rhw”(g3AQ?>İw|_5nC@N߸3,D@%HvQaQKvuO5Q"%6(nUyrhYej\DL ̳b0ΉWX-$mq6Ą2*dH %"v)w2AQ rݢc,7WW)7^5nT~$g0BA]OH!@XehArm%9&ʿvr1#[7ox0Fȧ W$%*}w]S@3@=/Rbbs ovĝzYvO0لN \%ìbb1u| wyll#D%gM-Pៈ@-e:Bp`1@ N 4*%!ޥqSJ74D!z (zpS=\Yx 6EcE ߎ.,P\*1n6|ܛ E]o ߦ 6-AsŠ*T| 0fynGZG3.6O KF>@W,_qJVӹ'^~8? ϒJ.oCcډH=Wr@4c'< v.F jB`pdRެMd/]cIwu@EǓOG80B˷z(DϽN=&[G* ;juߤd]5 :-Y_0fiXt Gk~%A,'Jr 1R0\ veXNa"_Á6-ђɩ0"iTuulR{&q0T: AMKkK|U@XbLӯz-E]>R5x6UY&LތGcXk9ಟX҅i&UVI0ۧXQAT='UcTl |!yEC2 QUDC!Z)DbZЀ:1W(],Y ͂Yݡ'2'4$̃"i:l!U!k#m ̦tܡ 24-]L"^*n[pNWUUҲd|$wM ܛCII^Ag{ɅVOڔC;C[|C'{CL9=rK> ͌~Pnsi<2k-CR}.QSš .Rv= US.<| .Xɶq/%L9ԍJt a=져a:uP%!ͬnA/ ͼWCǪ  `'07-AC]`gM.dxS ؉#`Y?+/wY6|6OTlPy]%pp֓G =+@DM+VD{+vl7 s՗BKY,'/ԙy:M')ef4XB?qޗs=韰_U)Uֲ?`ZtjLP oV,q/ݢIFɉ4C8^N$C=|-E'C Ǭ `D"?]\=y];~OiqTݱ{M޺B iBf<^GsYjvzN%Bdo-@trsC|/\6[Ibn !t?Ca:s𒜆f6B~ߊXM|thZ^YûT jcz4x!5{!#g?0]CЯd.Xeu:cơ_dzV/5}:d38nE!$7 QrW[Q51'9F.`3`Bc>ghٶ;m*kgC{cka& ܘˋ :aY {^Fϊ|;eb)xߠKk ,'^Jfyjl%Y*y%?lO/Z*U5Y0F͑t*|Iy_V"IԸYB#ĒXYL vQlV;ã}'>ݶ5'Pg5Rnʆ=~a1NSlyX(=Z!g"iMvXrx:ގ (Ų&/T":GJTDѡJY7pdj<ي.2 $ցd,3ʊJ_Rii?ó*6U:5A؋'bBz-Qa+ua`3"ʪw04NtIէ{FZ&O+#4:zKV7CP1%]+K*bHCBga+͐ @Ŝ@dL%NZy9aͩo&)'dPr%ۮr:6aLfH`L0n0kcde><0dO3)wul("J-;qPHZIqxh\[~I-w\?2Wwu"*sNHSwɹN[Rd]ze'!@} 7|uv $_hoo Z_\'/5`{*LGL:|z&Ȯg9[wK ~+YDz@lL6fu[~7EQ.^fw[8S>/)|}20TQ3+('LzO.;I@,]Oe!Sxd©R?)/i/WFu,2}( 7seWCdbrz*xgƇfHC%&%Ltq`K2Q"t[5'EMB$<[NcWl|pr^wXAiָtYewNdO +(Ft,,Dj1Fo=A_s0TSGMÉ޼X –?u嚇x&MH[iYژc(k涽K STwQ]ENҌKgiP}܃g∠]!O*eҿYm 86G{^V4M^:W&Q&PO4cA7OH, LN D&aY0f F pgp2Tx Y-t4L1&k7@~o')6l6p!d+uSe8f*R=+u],'VÙFDgI#hnIsDI cMvI|2]D<XXh/j>66X^xATU2P2{+=-]g7v "b7Aq,dF[ڲLH"_bZ.;)yy'ƉM40i%FT[1N:߉(@""r%8X5ƺBoC=Ix@I=Up+/l?s]d>)d?uLykBOږX>iF{5RBȇ_n)rFǑB0rJ럔ۇL\)Vs`jhąrOWٷ୫8 vյދeV4VSrU\2TnؼC&j޷q(K;BMB>t^'hkd G(Kq2V(qQu'&HژS٨Soq1ODap Ok'>W燄h3K< |}@g?nm >5f˕R'wj,ҟՍv r5u_iUU5R $k44P _HLf_'\TD"ISE9S:dsQCߛm9uW129ODtN;$Ӝ"oM.x?7]vz ڼ6}v3ɿŝ)GXdB^ʶ۔];A'}PKpr-%1 sXMY2o$H hIl7+oxPmyܘۙQxU5Jr8'ExyJ/ZϲAHbow$*ſ>-b5{wQ:} ӕ@i;,z5 CP ǥu6s3ɓӹv(41Eqnt5 ~3Pj_MڝnE_(X''Ս+ѼDI1U1]y+1.! ר"Zg)h*'K8NA;<(&E8h6B'Xl6ٗ#slm`nvJ'XdΝ˯:ihދG,V#l`c\>]xd`XY3$;:'Lj>ڮ{x9$ wY[ӑqD! Q%wx'v,-OVE}CDAsx( zo s249'WV䏗MfQWkwo^LYd`#_o1kB$UXC͆2A-'ɦn֪<d )8Tڮ{(3+cLl' ɩ֢"k&)`e""J=\hnrfr|z"ɢp" $'MJAYr(IjE'J41j}>ǀf?,'*}OipLI擻ik 4bc}LU ]. ^ !ɟU)8vFQy3ma0AT[~H$'G5Xݝ{G![b5;Mɋ2}|o4޴qWK{0\%y-|%t9À\M hJM(w9c!_FPc4n#(\cwDDu8+BK&z=RnU+ ɇm?_~nawݫ~P<=Ĩ%:W;dC@?v #|~aYD (?$ Ğw.JD5t s>ͅ@C G *(-'}uώvխ5pbj*7SLu?.F9CxanNr }(NKe)LZLztW:*v8tFA2fkwp`0/Q N|䥣:c×F؟*#y)l$uTWGr:Eañw=[WR-vj@IY;g1>xtܔy) u)U4_8p31DGv~frD@Τ/A2j =y`#Uo;F5Z;Rfk@7KSAgGV`yhKWOx!Xԕ c+&GYF,5ے[xhӊǫ6D߄t&mǃY<#Bye 5 *v1ux" Nl}9px6F{+ 9\C BvhRx)샒VB$g8wO;Dă8CP/&r-OKB/-iP6Mg0 OnR\G$J +ss^TB)Y`xt-KxU2̮n1!ߖ4qjѳ/[_eMR~阦7s|r*V6 {a{ԲO\dKFL N. .^_GoUG Xl9&4Ug49\gM֘TX6{`ׄ:#Zbѫ%:F Ѵdޣ; /L/^*wL9A'3aS#sx^Ñ8*]C JGe%%.# f;O:.ăvgmO>ޖ2uLh`r@iyw_3@:*s4vf=+kXr)µs ' aܘjz(f>$> 0" *;IW`=fS}-!It{`U0WPx%[C }3Q1A:t0%n\%ynAE=e aV'ŠWL4W .6on j=Nxi7356xd)^_@Ik1#@{#La1ۂz!}T* Ga[Ќ<ONRL|\*M 䚖 IȃMɭcP?W|I_/ c,0N.> #! o X3S;7^8Q4Tw(&L6u3hH<]dWuVHЫR}no:7Mȼ#0nm| 3~N=9{+;SVߎ'=Dr-x+S*sZV)5]?cٷhfkc1QN. A"ޘhDXD5A_ו['YhNɪtqp^] }n_t;wq懎AjĶSHQ(N9NJ.SD:R |-qզn7}f*+chpN1zur ǖJ54 ;ilnx5eg|km<9)N_OwHFّpā#V}ajXԇȩ*p|;MymZd)&ja0DCV(k?YqZCm~_l5klW)Él*Pg)'#`vf݂C􎱄ags|FX #^.!kVռ6r/1Z7W;PF0Kcքֈjh/Q<@{4 П #yA$ z-8H?>; rUY'bS"+CbXӱ|OI2[`+eN:?FXJ&W[gM( :?[3Au# Mc3.[_ب|&]G4r:F[Є-D{F.iU|WNQ2j%@V'w|4Q.;Rkif 9 J_vI$5cF"Z[doxw.bdK`S1ư}8Tfe^/w>bl {ۥ.h1Xl5Ռ^g57M 73Zr[M|]pNɭJ'4Y Y#{؇ݻz!R''Hݐl=%3Jw,:l[?Hecq q$3HJN7+-7fpՑE4pHVjѨKrZz] c)UDjНDb_Raot$CJ@Dt~,tuj,IW32I3m6 sgXP[jEߢIR"@:mG6 }:+/@BcF9E3!nb*t^.B_Xr:,4xy۟Bj;_M#fpOC>"# Վ lJ\nQjёeH%Ȭ/nאAYbn?Z5O(qw 엪h@$_oGlP|rGӤgP_B & 5J]HM_-H0nwv >9q2'O("ihMvϥ1jMY qbՙ0ZH 7m+4ibvr RQ>-ǫ$Ԃta('`Vsh-2Bs NA)Pl,͊ uY-AKk 'G'8k$L{ I$j~ݑ0aӏ/$O>v޹w|Ggu'KԬthH"1|ZJ%tE{ʸpn :l1']y1LoswZ GӾ24̷$jrw:{sF~(28''os0?f8o₊MN/&eVDj$i~ '1p`),9)na " yMG*q իaYH.Kqb*"a>OΑ0t+AHllžoHΐʞ3s/Mi+(F4MV~'}@B|͐3ElhuZ<,ݦ `M>7A [)IGC^ɑzRPMI 9(~8C::O?@J>+eQtf s?1ڟפo6R_"w\MnE -KGfJF'LU;ED IƧTYe2ۦGE#BhnGRV;1Տz5G.~l.!PXSN‡µo : `Z|U'8;I0Uno'nl^ )Ɋc%͂`8azhTU(st~fѨkFsMHx!UCPJ %2ƕ sSGO,{8{ llmZ"Ug N& +z* ٖTˇz/^)$ažS>2 tuLЏ XAźL.@gqd&9r #JS_Ne("]5} XkdnR<9}1j 2ǜv>{R5!Oy{/4W+o?@,3 $".*G%W$`m2/bݲ A?Ҭv2o<˒ˀ+_ð-c(J =}:h׹Ys}]_6x_[F^pqU"IƢ9p3<|Ȁ fH]/{ۗC0[v<_} Wgw7Vk׎ ;MM/X`i6ߖT&b%Hߪq␳e',1UfPw2ᰇ+k6D`6_6 E;'CR[HD87jG"0{5SB;sf[Пm9 i$~߶ c5wLڻ(pRz4|FF,ng!,'r]kCq2Kmnzv& gER|q`lѲWKٝ{Tpšپ>0$#<6ڠ+M:HtkD!\ nX.05?oKDT-&RϞ+hO h=d06[JYrDʄl܋ %t=m\۝*Q%餺9jCGmz"Ԯ}&Jp\s~?aG8 € IGByܨ0 I  O{ehF1RoS>%]1J!"qx5%*H2&mK J5$+q; /k!34n_ŢZa$Je[?}ۼ9 ~K}yi£VN@G6;Xmq=X=m:6-(/,ZAwn"D:vbй) C3`W#%7 nLߘ \f󺢥e‚G TXPI!˧ 6&GHbϟIϥD|ᥤEF\oZ.fQ (28i&';. jQZ)aH8.D; {!9zQSV~qfM B=?{Vpm'6>vDEEyJ'IHEp 2 ۑ<Ȝ&yq&>u¥v7v~ai +Qag5Ǔcp0xW-%4 4-8gGqSDiyB|aɣU_%ύ oA OYfZq 8){zbǷM#U Ň}.d" F(ܝ ݽQP)-'ȼbjT.x>C%~"9"8HҦј v٦p7᧪7~o,YSI ob",Zo4U9"hu+*iwhR?}c),ԇ[W4vcQ vpUxbdOFR((CmU^4HmW"Udz+SU6\8>Zc ^X58ܯ:S, yZO)љ`N`a%ԛ(̌aXRpFЄ { P+rĕ$?,lqcoQ&j co")i u$ 9E$*P,Üjףlb)h㈅<"|'w"ٴyұ_WV9r&xe; s̱kJq*B޾lin5_!KwQ} VB2bL:9Z4g^~).odj[6=]li^{1ȸf:JP8{ rfgs|_xh\Օ""_yD;~>K(@X`!7[YnOGB̿ݽA7l\_*ykC oBzxlj$.C6>W4Ap S^)-Xz6,j ߰d8oE M:7W?\1[o6szJ1wiKKYKS꽌|ǟBthUy4:/=KuCY5&;Bשk۶&UH 3s0XTէ U i?o J(ݍ{lYE_(+A(gEZPr˷5u,e.?,21u*'Cy@{8t+7 Msg?@qn6+0ߋ2dg_R4 ]BFƶzڌRmE IkQy ШK"m)$Z&@pD;q"p͕{2JLɔ SdxPW.6 =MLQqd|E_\1w2c%rR~b~;LӱUtUo1p kdOҴ G|o?**+;#֚N58`c"GW9 jE Inɐ# 1`2qga]=}6z>' dn\•@=u+g{n 왰U SIS+7$!B㘤{RvQSMhBb 5߆WdJt݊و No`:tL4tS4<OO\I<m2$oK\ĞO5Kg4d`c0d4 M׽~X{/xS]; 70?hȠnCG6]e31Q L61XyrkX<2>H!ZA"fy2`S݆ZkSFHJ^(_se5@W4Hj&c"ZbPZCȺV pсw@+*s^޸^#IG'"&O^,A ƴ1w6Id|)7ږ}6|@[9D*/^`+bqNkwe.O <2s3cA.V %hB!Mח\7 4w:Q ,':ۺ LŎ;[ ޛřMw~{2LZ|M+Ie<6E);CsmwwI9M]f Ɛn3{iZP¨=l# 7֞^rJa3Gb*$0|r]ܙFo%|"Nsᮾ{ytW#V ˊlRaA k%IߞVRJ8QNo_* }·NgX,S%E8DY|wKppPoQ.aK)f3IֱAv 7j>p`چ/# 8⠫i?f=H9:C}ےX8T/O N֠#ܩ&];cڐ'8?8f6W*/}X~H;5êR|œZ/ĪY+š2p@3ho"Q >lCQ׽~e#d*S|O"6Ci3_3  uhFJYW?hFЊpOquYc{ Ӛѩcojf/;8J Cm:m0[p7!%I4)n̶f孰PYQ5ZJ`}= }{\RէT=_2j/Snk%ڈk`5=Z`{xV|c̉G֧˱F+ˠYp~j1lZwIlkix=D$3B7T *[FV: Ei r^o"xU[%NLbPtCO&~+) U(/.ޕӺ~`εciw F؍IH24?q =!8H/"UR8¯ [PM@k )J+SQ} >~p9, qIJSߍHqMӿ'z8X -wlSI X]q.xFH&T(2KңI6un>Zo#aX*[5*2I. niN(I6G?ˢI^]fp?OA%?:<\["Y.%DF vVv6 < 27kQ=w ^Wgpjآ.!ZL73"׺\dh֏U#0]gc r {=G fHCNN-8J3yk}*R2fkt|KQՅJ@%)_\$9+$)A0֓##W#Hfsx )yJvC ?_s%<"%ca/FjiT76bFN=JSs7<{1ˆ#Rր o;6d~!+LR+Ecɽ'n/U0Ś,L L?*U{a[ͦ l74i/wTo?3Xkwċ>bZW$>ubv]YTRQFD"NjsTy{ hҽ,&+Ί$_-y3I H#l Iz\ږڗ^@L$eRp%MioZ⡝gAޜ(­̠\:]`qP̠xKtu2^lyȱ^9G. 7B1Whk4N&NNHy̢5n99/&c6,s`:Ty"22[~\i)_y#D>d}1FQ]ߺ:vRJE\*)ݻVey+.~+ZꟉ 4^1Jhn2 vroVIot)Jۛmh<,Xj[npU$ } 5" L`Ï_%: gڢ+y$X-ǀVnI@0~98Z뷺%4h08DE\0*&&bdR >q;oB{\;0=-v|[\`!ƍqxMl/k/ 'a׼AnZXpj vzF Ͻԧ>" vVήnk'St82.~"ܩv)D2)^c;eiYoE6}m7!DVV\ KD2qRHi8L_~-h !}Q:yS-O/!m(V_}B`&"YR?F4TQAߠ#q,hwrvuFy!FBC to223}xN9 t9b{ǂ [.=dnng4n2K\4b'LJsV?0-,(\aI+x< ZD) o;yf8T ]3u 6m. {'¯]}hXYc x\g}oh"q5< Ksh\a?4LQIb4i`[7H3JdSsuBl޸`‰ƍ(oݾ Xlu].L4|3)E4 0A̽%0抠>pݥ\e |ei|=z5R[[JB0U_aî]>_`P/ϒ+э@"p|XG$\,5qo;u) 5{o$y;Dh Lr%9gNɌ.yQ({ Ω#Xzi~Z]QeD~B Rp7s4R*^ӌpٽ$ɡqsrY I?GKIRN!!UftT#ؾSѨY?{~ &ҫs-x]{n~.jw| eFe^z?ЋJ/Wkb)_/ IwV<=ݺlJxڪN1_VNK*#3 wۯ,~YUG]^W˂2Bm# LXН<AG AMDC`N'6ۚ}sGtQ!Yιg/H HJxcF-)1i"*xCgV"( A[ل Sejx2OB|1̧%?»lCJ'R#Δ(k: s '.#V&u>6D*i4u,CyI)EgFBDNY O5>Y.n䤦둧| &sUOgA$XwgbXwAM5TՏuxQyNJA%02`FDWׄ\Pbbx/љ^_P_U[M-0N<&b9XmvmavD?"Z}R!#rg:KT-ZGj]Vn|峇H\n!G ͊XҞ|Ts͢{t [3"HuiU@i6~^wٚh|5~wg"IdD J8O lz`"w{wg+Qx"ady}K+D`kRҋXa_VJn,naQXR@JL&~)8g>bfzy=[(̑45Ew:t& OXq#b{zQB9c#-rF{0MI2,meBu(RvVeUkhKoU$!=pV'o>Gnތ"ڼ*?h BKɚYN$3:8Zc >(p&bRHNx^оNm.hiR5yhw= T/B3UlXRms|#kLF=GKrP_ׯ=篘ڥ+*ny?Y9jV-a/LG<0rE. SK}lH׏p=yi±Mqei-NFRH#W?WjӂyYna>e3_< lƓ|QK0"4Wg%et<)+s7N\-٥Ե ۈ@$:BA%yweev۟FV* TdLFxO:_piuaJuOė̄2jLeed۱IHis- `? NT]w'kPLJ^-R?K2! Go94_2.H Y<:/Hnfyci="%(P iw-EY`6&B1Nr#H| g[>,E {a%d$2Iey@q[uq*|yy#΃ \tn\ 9 [3ɬ/-d+cTe_8qGJT2C!22)~nVks5Nh7o""[8.$Cv<nҏHaAz; V@c:EH2X,ʰ[ckEmlP.2wr9  Ļ?O-Kw9veQ[u-. eV>)ӦDol mVtR*cKUG0݌nFpɅI27e$)xP4sz9:.VGZkO ZA&W,?W{TQkZR{~-)QjV3 Sc<"Nbw币)' ڷ5h44sq|J\K7\o}p݃Wro(,VF(Y~%M"5uOeFK@Ddx;=#TSνzV%hI_gqL1AmVInXD?^EVUV06vՠ{kZOC(ZOfN{7 h71s#d|A\hT .n!z#[#zz2ubΩd1FK\ A ÅSEsEG_&K*F{1$@~d S*nn+ 꼎q K 3 Rt5$7"Mkm.o9/$^=ڊyju)l DL(Ewf%4%Z+PSav@O$ `|-d WA=pm{J{WH7Z{@o֣&GklCS1um~St'Fnn{8f@Y溥1|p@݄.Q ,|"Rpua羲tzIBjW{P.~HPyj;1Y[o%{Z+F.M o)o"8cmY`·t*и5sڙsa-Iښ)XdeuO*R*6/T +97mon0A7T7j!-Z3jsu6WNǝ_ Z^l; ]߯%ho@5dK;8NTkma_WFO-&Em0DS)ibk"jl~ăy]vwkB6!F]hG%p.ݨAe ..=Y淫#s 1&>xvÆQѐZxyȽiDGgE r(޿F[RKN{#r @dqSS! t\$8U=]B 3Gł;8/˥#v``d}ɧD+$Zh-bLP_U,w8BSW{GMw$B\᲌k}8U}y_k ᴟ90hI5F!* AL&;NcLnhQjhB9mq1r,X-2&K+Sxso;AjfxAC'O6ce0j`$:-c2/^%n @p䋽o"Eo@~71 {]^ܞ$<4 ~I략b<KwF''ŰMȻM^H={n1pa.8JrXv!bU@(@>I8D'K/z!aR,=5G + x`{dYK }$lۣc'/_DeP6Vv4wl҇-l| a/gj)ՅϞJui4/NePT4}1; ҷ_XOMų Q&iy^)&aހt ^u֥p,Ն1xJ<144+j["䲹B7ʾ co|!ϐb` _0 ?;a޽bM({r7屃H7 ObF|{=iNJ!l>9 2,/\jFJQ7Gchf˽)rŗ|P;JS-fj&9];v''GǠ+=4ˋGJs8r\f%tnE:* i1 1`jVzS uG丞P:yQE;9 F?^<`Yl}CBj5N"sx)ll}٥w}\M[--fb~e@C)JKCK\+MA ` P^gjȀS3#`)1>L=uCDyhIRI<9usyቘ:.q{2'ղOݝGr#LjYo҈5@z8/ NbUma\TX_@hrjQ[ˁҐ9] 6_煠S|O 2odּXbpV̧IH#*IһBӅp7ZU9 mxeᡛEfZwЃ!EjgF+Okij;@1 RGZVUxS*'.S|\ӮM.QZO T,Ҵ>jY^iN<Ap7Į- -?- ~IUPj|NV*)k7464VNW5yoͪiZw_vA}9 oYgQ>Kȁ=>X/JYXRǂت&492BͿg]z&kT(.$NN(SNZ&5*w 5uH(\hEg\bG |AzMpJ vz]qp ̃׾LZS؞21LBЎvsn6u%'؀(T7)("ЕV4%qf-/iuZarRMyk!/x9)xos f| .{{੪)I30 {&ghSYbKl4d :Ug*dʌWCa-ee$*8AJinWb n&Ӓ XԾ VN+̄9FpЀoXF$?U%#my.QfrM/⵶W+HX;isй.jcP$ c$o=} UIu8{Y@&aWj2!OD۰wO4'U4#-Qwќ^ol&Sq<ѹ-NХ;8/X(nQ8bw //;%{*}*՝}1ZEѾNv:^C*Qn q8mCjݵd5s+_,vEy`1P[A/=!F%3tt3'fe` xP&bڣLH:˱v;6M ./Yv@fot>sD}AL9j;Q x:p+{g4jS̝ 66REj@MߟB4%dEkC3:-K#^د"wtBX gjobҞ'ː󸳵Аs|PVwmA,Ħ+K\U'"{9Q-H#:rލ`vĤ% iWpr1@ϡ%XPx囪Ətdko^; : M\C*f~2TIŻ?r:/8`kx$A09 zxKc]l[!Zg )bSS/텸QJ^^ڼee7TA84) I35 /z;&9ox Nc`\;>uh4ylA+/qi&euK!?ȓ9t_:,O_Y^(b @.h.w$$I ŁmEؚKWs6z;1tKns2QH[$z^ƅS“<׭m_Ě Mg8Auê_BOuُDcM_{@$t|q}!{2G@KuLl1aU]WM%"?pV1xl[< DVLAi(Xu6<2C&4d1Wl9dU\~Ur4.1 Շ ֻ'..pH;o]v-_^8jpw/;rAMUB' ]al^?{FS~T<ћo)OMWEFܫM\K(Ʃm/S4vapd?W ad>n_ʐ25[:@ԳGOy[eDuf)e10UQ'bW('գ̅Ԉ%zْ´=0 yhov5y6=-؋:J7~Zmwe`-$wK0mPqĶ*(w܇alF17t FK@;%D_~F9 *&gףXtK1P e9 0+>pAc?$/d\C?Ѥi`/U҇?tm|Xۈb3$~-I\:T~6&'t+/6fR1Q&t⎫$STg^x(G~:xbI=OV~~#n"$VlHrUwT2'IM09*Qb4"͜ޑԔ*̿u}Q{ʈR92S/1I't 7j\ϩ} N 1,^Z2 vSs5vH^vV _Ly1M!gG)[]$; Tz"Ĕ{?LaNt%KށBq q}?ñ!W h$ 8`Kw:$o z^Aj6sLi~h顈Qx Z+G:}qN[Vs+e9/1ҀIY'Ӌr bL(U5$$8b7e5Pi3CF8D)<iR ϖ'Iy\Pzj*͵̡2o~؆j@@7_3V =i#FΊR01'>MB} pt*( 0. lp}4ŋ:נ$;ü8&mEѿ sE 1*Eo$kD)Bb"W # t]#&|~B)aDLJJbCTAlW#% W`rf*+M~${#.l~<c[+1BkTƇߵMЂ|g a+n,I\^SxUciJ\(y_E/ȊTegᆞS >ajh:F(wX9Kv~tjuI_۬vo.qN%OWOo GW>83hh \ŮA p 1wD2U+'ch9~pCV}<շ+R5[Գ$+Y/j\Mi"xM&کH8!^ MSc7sEF1TZ]䡿k>\ydvj&}dL??R L IBZpdܺ}{ f)N Dɿ 41e]e|%K>F7"V/@u"kwF8,sֆ-+u9M)H ˦U`,9o_ Q3{bu@I`P]pEв+] <n˯بue[CW9R&z2.؊'ܜ3@TP+ ;H$6T#/3f]46ޙJ=1VknOW@~ {=oHKB!MK9"-0୯Ze*__ R05x3X>Ydȉn/sThRVG6pKFeE2= xu ޲)߄4J?ptFd["|@MOo҂[=OJ6Ҁti8*Z7W4-нqKoBWJcQ}>.~gӨbM-3vtUmv:w|&I~V .= s;SЇ[5 !zWUΕ,4>E$aO+=uZ);>xͅG~pq<膪oj&~),yx[98O|ōȩ(+xGKf0 P+^4}58Yp$ Gu ɤXX=l|PՊw+;jPqE݁yB-xz8K)`W w<,GXMRnBfdUuOst)y+,<\VH<**/yT|2փ`&X'pq^hc J(0.O!BdH-[:$4,6* K3&|.ׅxݷUUaYW!$|TYuc? 4K}/u)7b@)S= _&]9ө;k6H&TҼM3R}NK16w>/ [`|rڳIB:OݗQ_>^ӫbl%i7\U48Vf/fk´@0RɎۙfE}9 ExQK]HӈQi}+qgLn/0^y+&{d0EJjA$L'+?AO.f nyD\p3PnSN〸*$V(5'- LD]P:"寎Em3oYfM Р9f.27|SǞ(afL/9Su?ܚPKs*jV!~knK[n_^H2Lk##%*;' C~<0eYThlF}**XażRmZ C)nkZ FCU2xc skw~-h  Ҷ٪q(pI '8X(\@"n{Wxem) S ޠg]Θ>B;ɛLYo$]yf.Fw"nwL˃j}7.6 7I?r(Of`#O>r?0I4t?O{˦2O2Z)leii+5p24(Q,$GᐉY_IRA:'Xt NЊ0{q xN?Q U @^f/uYLQ:*!=ÌE)OٗÉd&i't'!,0'SBxUIU$hEJo#C FAm7>i;HI6rݽ1`xM8{% ` D*!Ab0N?u~ſN~_#q؄\nrKROļM3-kORWrNmUyfs NNKU u= . F&x%~7Ex`/E *%O@٬F+fR;.X*=Ger#t! Y Xt8W/U|;nIKؗrp%4WPfHrjLmAf5=2[e69YwhP_X6^ 5`[ͻoQ(f|8YnG:\~Ux-u'57³aX#lgnUΚ\cz8{; Z(Inㆧܦi=ș(gL -D{>(goi4/1\2߃*DbjB)WßWN-`hN-;"F>Vohy*ݦ7Ov Ø  /ySḐn")`Qf`"ŋI]QzT b>;X~5-nd`~m7d~zE3i:aN#a .ba_ 3+E9g` f$_$TPuu2  N9y[VRa ϻJ"Xjh?&P l9&g2[ rlJ~ =N9{J(;f{ +sO6Ժ9)#qDfe {oy|h"Q&2siySi I(N( re2!B]`dlѸb 7vڶ[N)pyYbB҄f3:_^kٽ!rJԵף/Rc`EwHέ HuF.a(Ď񖵳!ۭ`qT:S%Jyu9^ތH9fbOl< v} VY~Nj~mF|Vʑ)U,} oVBr 7sJWgDX*߹L` L3˕WJRnòj E.aeT ydc}Z|ѡX ]t 0[tQܐV `E+i!$&߲ab):|K[=qX!JT@*#Շ1 ā֭TPۤvkq;d;8iV\1Mb3qȨ >3 Q}ZYFRJ{#,\ufPTWM0qMV:kx)>wVZFr1'6sqПcOn䈠P *1ky;rZ2R_G*bNJk0qn3$fer{%ŭF[Tp~ ȐK59"U3\NrۅTkTUEĥ?_fpgd؁srQizVޕ՘l>:9-+{Z2Yx:Jž>'WA Q¨K7Ÿb ҲbTh왗 E6^~/E zi\.n[6p?u>Μg`-yKJ,R`۹qy܃]1 lA ,\y@4,/-1*> XGglB<7$bOu)%/\)cw0\pU xL4LjC$#%/b켑j/&ͅ_W,0cAjbjh!DX1*j zc14/1#t(E-К̬ !gXCoIs)xvaJC-%D: # At71bLކt;!g3Eg ],:9O!]tO$謨 [qIb6aؾ MX4tJj!4bXȞ͘–6e2~!_mDC5&!3\ jdBS4j }B_ڍHpt9шo#K;F9uV$]?A$%{u{(Bl89К32k s(2T^p[)0 yf ِ:M@,v ]:X'Vgv |PL(4x㺤ZU>8y)9\@:O q*x eIҶK,NSc=b|H#tɊ0`w78]Ь5}'+@mp;.Zc_CoJG֤ `29\:I-˒Ř\UU`Z3AVw=[qBc ]3.CMJ:$fd>i?͓OXX6Up2xŹwu,T>wq ΁8k@tZEZt(*V-nK:3`qpk;NxH+!Dn U>- 6h~2 FU3G}rRM/vNW_"+|WRPBM*[w*UP쭩m3_R5X۹D-|-x|30^Ƴzc]&ÄTrгYWW@f 4*(+T[sԍk!Z gEX AomAhRb~Pf2K)3}:O%|f[;<x\SP Mcƺ^"/ QQa^*ս ۷̞tYM08#<6%0x ~AV>)>hgӔ 7w<2>yRrňpw\!@.B#~<}Wf=^26dI$Ȝ=$$ͅ7MjU)ͻ]d9'_J;{rL3%cAW$\#T47|1%ED9yk{/ݟGK Shvj_% =[LRmKmDtE0Ţ@VfKUnq缟F)X]1Mz;ѫjtupnŶ·pHrx!;(KwYQ$eK e\enphė#fC4l$mrKl@W7Pz;|V!'=IF7%-@`X%RѹGnRq Qͺc>!dw)b[p= EJo!J-ш uA+b:,o0N9^ŵ_RNVzq uUZ36e[2ޏgNv.pʼz;K/4t3m$Hoe0+$6*GWv3*!h1ā؆m~o"+h1I=I3bcbwHc3ȹrR'4TR hBqg&I6p얹 R)7 㪸,cclwD֘IR 'dNtgm^>Sbe;^:¬9x g\Ec  CSSR5XcHj3?~jV| .ʂznoa3YʵNĠ:ߨWϟqchbj>o-w${g(nEfr H|F\Z8mT\,/c~P4Zeqe_6Z[DOsH_L~m=ک74#xSA$9̤ԲҚFO> !KE/*F2wNAXӔ*O'W!.f((Y)%'5E(i`&x sQ雿iQ601eWRy[eRQ[lیBEm 2<5C't3y}9#.tGm3Gb1ǧJ)_0J7;լP;Zęeq@Y@P.|,KlQ{4 7"4s/ev\R4# g#/(":El"lтqW{t`CKՙ\ xحM&6`'b4Kgƛ>c) =6ߴ$H`8LۖƢODݳ &l >n1(YQp 9WQ| 1{K2۞]R0,{$Ѕ eCiŻJʴs^ή6K=bx˗(5R  D2]&;q@&f>--+6_d\Ej8Q8GMoPZEE(a*z}qҙoI fWWN|.Ny݊IVmQtՃ:A g M;D4C$8^xJX? a(X{Ht[ <=/f2 5t\iʸj冫 Tr$ c9zEҰK3؎Ev!77hr\ԡ[Y*صIbU,`Rfǔk͹.-cdpK1cs {9PvlbP'$PpWu0 ,lX#d?ˀcR-Z,"i#s̘ߝ8F`'mę:k}r0s2(GY!f)E :`eZ8ոc͆$–q)o- z4_9JaV#@2",ƐIG@ yE^@wf2oQI7b9p*{XyBWd@juqO}d#ON~ʉ@[sZiz`!+)L=jiGd7zv4qlУ}h9"h4L ύZBT^DXl0&%Hct G"s<$E/ ǡ1\˷,-H,8vx/2;ɣ[ UCқUpTUpYWlν,`[Ari؜*L> 2=O9}|-em5c+kT~QŦ u&!Bc6̑@蘭#W20w⪦-+"{3m )T"`).ây>$j l\^P ;e/mnyCdHsoWBe?;ٶaEaC,df 2yxj"H;6ۂV$pS#k~ h_I-` *MN(5qV"i뇟eߺ(|G(&ʝ*!"R[] 1Twfs7aU^|Õ(b\ s̈́7H|QR3o6%Q4װ dǾM F0.71AO K: mGp֠",Ɓ}c p@mdT|6 ynvӀ۾n?G;qwa*Ur$2opr*g P? ,9[+mm EADKM8ϩ0WGlv#`wO2kُ_S#s(_g>rA}%&fθ |2`z\sF?6 v\ƪ[u]SD͎ #uil,gts# 4XōWH(!SˠgX?P`}|~7hc>\_&W'>]Р`'p=CnG!CvGTOgӮIoo!N DLr5+]<37dj [#~ʹHƉ:V /#i|T j!N=&BZYpm5s 7~ȷcpqk^hLá2SXyշzDZVƲBFbdFY/iɩ <\z+ ö)DiW{QU'F\g6ywүm-zfM9]=MZ.w<GVqdj^m [6-[CylkeuV;뮍LbiWugoH|ﶃ){Z/sx,C{lӧpFo BLeϒ7%w"kZbU1 m AdmƉ뎇@()ҾPrbE}K.0~yh)9*Y@ߟ^܇P"o~]1%2#j=Dj]K,3% Mg7~~e'Ǽ0ЌP4Mdw@Wb5 H: I>#-߲sFE֜_ 5l3-n5}#^JjB_.oZ;&Z}w*&/I 8Hl(ܣ2ߞ /n5ԭީ;fT"]>"NSHTFJFŒ+"3D)А0o:K1MUqhߐ-9zrFIc=ED7 zPk3 KZs6ͳW" N- uV঵'w*y0WT^4>V^!:;XqrEJr[̷1j K{7!eCN\ ) 1P<<&&Lƀ# RLƪ??T߿Nԑoj/6WQ3Pqg7lnVB+?2 qK[=-z7$@!;43}Ã+M ;r>S@no#=SpY3 sEhJZT]JKF9xr.†`ɇ=E^(N̈́Px ֺL'Qkkkk8o*>$Q~G'ߨ.;$8L:@".'`kOdө8 +(2wsj>zlw AMEQ#1;)2Y^S 3c;>2bD H6./+~n`3#˞$c ugoaMlqy8G3Hl H =bl0!Y,}#RxI#fy:L_'Ѭ(7:^%N6M˵䠡Lwէ*Z`<+ʦuBn>|'ғb!yʰ ܙbJfN ,Vd5M_,(Ȅ3n6N.a`Hك8u(pңZ޼Ș<ʴkGAĉ9tmv3!P h > W(Ys~. iS>(cd]3O`51Ri }IWHEML7$^`tB]h%/LMOy᪔{Lx"/ED} tJ-1ޮboGZb6ckgaHK&no||JlL$`_4m/p!h7"g`ulՏ"B)*]@lKUE!糗YZvY369f 0Nϼ84@ n;&%zOWHT^JfKx+#:A=^n A#]Xz$ѿ2l=$C=S5" rcYK!!Z[LAH;Ni^)$HKjAA<Ў;py ;=u[co,/sxR;o2qXduyo <äqI3:f( @4[A^^`S. yՏWs5wlgiߊi:,;rhyGHB,: [/YD r_98=ʕwƋP1^I?@sjp˴dKbCS_ ,菮Vvo}^f} zf-e"IQ[UfQ:_ok2/ڡ-i0B< Y2y-$4+=ka*N X˜Ea"[g.r,Ai%5y5g`yá ư4OIh4@|]D($FZ\\2F e/q&!#4谣?^%OQ~ 2}Ȫ^#ei*!k͈̀ lQtNS,ݻtCV|/oؑ,NDj2ے'AWAy7Vbz3b:|_%ìM da(QA, l$ BZ\Q (`d5WH΋p5tHXҚH!"ܴ~W`/T37E2طfbtqلx7wyh|V,zW$ŽS :U#`K6;æ5D.?Zv HQ[=t%xRbgz<>v+F5 ADޣ8ِ>@gZ 53 E0oƸHvA䤓Oz&ƾfSJjB61Dq0񚭰NmG=A~2j,M`Y bl 5KCGjC:G,R߭"e }y 7A;QH0Kr;P?!#Afoc ѣzT+KgHvߖ7e}R{eb$c+wY/?zIL7vfUėUW d+v-J[Gs^24")fYqnk䀳qrC Oeٽ f͎1R 4){} 030}~vC`{HڈDtR&-yB9 bn"L$O]P M_*j e6 &EÀGGo \Ay^H ~:Y3mć`P>Xgf4֪ߦS=)+}WC6#')Iou&]t9mmo%UJ! $>1^U!`PLKqQI]; 3OYJSr D65"P* ;)__=ԡ2O+8ASW["6?(x5ʾtf0E4GsPt/f cmXH9^K˰9r@>\߿{[׽=YpOi!9rpdVJ x❧&vɲVxG.W]uO4 ~ӪS RS~Djzm0SϑuGVg7yKs'C~)4u4pڶYu]I>8 (ΧǼN()I4 B&J`^]MN)!u%' +CU]dT0cy<s=1v.({7 N/֫T\Q˅3}v9Қ l$AE5.,KIm*_~Eogzj/5xz'q㪶kHhqHK Ea~~ d>t,pHFj\ۚ[Zi=" vǜR#Y Ude6E2:)H_tqv+5Y֧"0w(Ɠ>@p22ؤa\fX5dHBoH*H#` 1/n'b9&foXCfQl.nsj(R3xV-)Mi{4 h[YDQܾӻ 2~1([pSB@DS?+%ӡe6w,erJ4]Hƚ9S `c(Md}!.XD ,XDqviTd1;ꖺPvbV(jG1ZLnjW=wth>[Z]Y rN袌`JO{ ʓ%}-q3ݖ +o)_MX}Cc4UF*J:W:T택 nN2.{Ҵ<7 &L>}Hq(el^z(m\}e [2!PIqVo,5lc4E*QgcRR2㈕!ߒ,0Nf8ISvxr,dX_AmsQrqrp+<BcZSxZ>9|ONŨe_r,ֻEZN-w[uPgӧ4ɈIrf/gW@e̺DuK/JNZ5gͫw-ʄEVDM(}j^-΢vTHPaGB{ma s:A:kL 2!s64`˷}eaeUPIZT җXO1`st~֭  (=|VMMݹGޥ"D7۞Vz,@O-$T~)ٓAJ%[X,* CA_L=$ɧT6$c"-L'֓љLҫ-Q,x͐hҁ\Bxbgu.gGpoR2gKNAA#.9h}4ş"6XoM,j [Df}n>d[.C_~*R;^&=Ѿy^!@^MX2`_29աn`EoR^ìY P ը7',M ?xY ju:f Xg'9࠯2.mq.1$tS?wnjGmTaHCG MI@W'Jl}(ULjQ?nJiwC,(0"j{\`wK&4'/]3? t36CfĉgpSR7`Y1.fzn5P3许Ԇ9~1 WwsiϞª]07oF4gV@ek>>RNI9')5J=lmEHVy}'a6JB`z4oc;gbA@V[\{ׯݱJJ-O5k,.'=<$1LWBpF).I1Y] &Y ~%bx灻6)~GZ/| }6 oL㝃)@ØtpĊˊA2&M%L_w!2"b5F0 e~4cl@S~cvrGȚ,޿4~wEuZI-hfvMd6'CLpF%U HwOBiˠ\A }YK$OIS ƊyoZ6WM=yӪڍTf|>RECJ7h9ՃZBZmg!<9 2U!~#*-.JGkp1`^_v_&xhh土nvzE0hKT "†lf8 a*){~:!V u<ݥ D5+`Ò4X1ϻS5"OR^WBVI%F'k3Z&Ul 0TleR C.!6_Y'=\MoLr,5BZ%̜̽ [#.KK>P źܕ@'W䢏[΀zϳǯBKswCGl_vsЗUDE=FפUk)QnAfn^ĔW+u|[Mymg҅Nx2{8u&nyQNUߡ? l#6vتQS[V& hw!K UKG}UW)M: ܤDy*B qz(Bni 9 .̊<ʱH7qG$~CסʫMxˀox =KSct/QͬS׻pG LlIT]E|.~)7][  dE&y)aqSg/Jyy d4I:Z{ynQ! }yTy1V9Y.q}n{fρ0dyN0s7^䟻 &eHH7.c/$:YP1ɞQڂ.T,fުH;ߢwJ\67҉yU%s7:9k:͸*t=EߟB"` ϸWO@Ej} B+- LǟAW]>i;E=X־oyMm6#%IxMd:IX5N=8Yk׸9Xj01MnK ]d;}iKC3wq妏 04Pxi$@B n>̃Pv)C|&֧pYŎ*`@_@FNPs%NoB@(Ek*dh>H<_“g1 %e^{$\(*;XHT|Kbpb aԲRܰ?@'UX:R/eiWRe{A*&a~9'O=Gf KGᨫ/D{V{% ?|"w΀PQWS`f(R%6!A2nk gIC/<ǡUCЁ% :ݜ9P'pF>BFO~?wkQ24ɚOXN3rV5(i EayF޼1ۯjH뷚3(VjO aO9[g-vg51n!nDܚj&y#i}FQ'QuycbmNk1PՐ:rXmD+f]VZoYZ_ ^}}$wKTU餛wψbFt?p9-:g5dʺ[0l4>|}"" '0'$sHxaF ( iEkIMP\A| -["fEg0I鍀wP1f$utѝ0o~@Al;^gR)$;vF\Oۧ?tN\~0z7\XE,vSy9%*s]k XD8?AAv *E-Dt  Y1U>"XM_̙1hWWnquɝᒿ^R9E7f#>*wkvQqƈ]K ;9 I1+T GУ=&r%4#Sz6Sp>6P+Fw`C`Q `eZfCϯE9cf-Y_*z'N`5guQsɣŘy5;bZ gPAB aT!33Q ИC۲GL2%j^cg˶|(n8WUUXk3E G-ۻi~kԿR{nzH>,EBow$9VZaҤ|[_kG[J*_JէTq:KCEiWף|pqH9?DVpYZcY,>}> xj9MqO',5h uC r$_Wt] {eop[2 QM-ze#8_U0gؔa\ni_H]9˗c&S$}3X\7l>ƽZj:>ta:dAN[(D)b|wΠDp;(LԍЄ/Ɗ"Gd{o-'d k_X'Ԉb)!qGp6J'd;;){P")8h PF^*q;d|M-(r>ܴeLwBm썲 FE߿[ ˅HkCReڢ8Ős+99uC]ԅi?bx0H'Ƚªye |~ 7Et[WMuq$+-vYd 7ݜOLg z^2):ey"۪&VY bÒ7Ÿf֤At XR:e7"GBbi ]6Ch7Ik#OO5'ۑ+բiVo¼pC5f)!I2J۝8U17C`B}M5ܹYS(8W`* uwՙǻ|y^o&wɩǾy ?|D~aO!ϰJDߐe/">a" "yx<[sLȏSPš;O@PstGm"*lXG({iQ M3YT2/_(J1;A35Gr$?qee"u[:`g4 GoK _&=iRCmǿN9*+'ե#NueX x< Z`@9Zw 'Wvrۆ4uq3$N .yF`*(cTkĻ`Eȣ\vPӹ]PrՏ0x7R -s\[9p0|%.kyQ2HNSnEuUF ɋG]ćy "+ܗ K|Uwm(f w%b{/@qJv/ yhW3C ݖŌ3 h$H70&uj ndzJI24uL(Lr邁:;\)R+)Gf]U ;Mql#u cc+zYʩp>ңEew!3 HoJ`+{#=CS=gWDUiz^YfPtG>W;FZ z<ŹйLbOh\?GñlZ0-E ?Y:DE3}50ϛ2}i3bP͑C6#w[13~I>5h]aOY-"W-"yYC5\L(u.ްf,@~Q=eњAS](a'J+6XT}$i:X"BЛ͋Q9axkⱔ mH7*&fZw~dE#NQ$J,4gԗ*Ü r0}+uCY#lX}YWgR6? \ziԋ6>0m_w7oh't:=vBRԆl&蕾.H"Yi)9(#-|&K9fAJDhK8> |V.!qHmRMXQD8rfջ “}1oĮN8\)u5:)ob|74ƽ}z8jv ##'$W;wJ67[]]IX&;/i~Ң!8k+e>%>Zk n,Mpbj |ҹ4;v8l`(dvLE"zyI5ί|*Kȶ)@kGV;@.5 Xu(i eZ[ 3œ$4QOpN?)稃6o]M];(!')k {>G\K,Vklb8HUl-.[HWqk&[EƇ ZUC~߮+6Saܴ{U[.NS,_aϢ'?.~o-Kc< kf|LA/8*edWr#"߬o$8%w(JhĥQ kUb9Λm`qc6)F^._|@łZWQ`o;A*M-hAlPB@bѠ!;n e ޾^"(? Z2'W%S2W45Bj.90=Og5xڝf,=89,4 $M,0 QLVuM?iZ{"Y粘±#.u R1ޤ}o wPQB8PCE$s-E[zzsTEEXٞ o a,r'.hL NH?5fWݼNO 4W]$߂\q{Af9a%//* /JU- Ht6~Ot)Ù)򏎔-lv3yg>O2;G"OfEʛ&Ă&ђAAUnN@t6sW&2~h2PmQPsz{FbPWiq&2_md}ϫ2:i +dFiYvž6M+w?fNRlc&4VNv{(.80ng.zBGUj> @]._ueJGl}?mFOvGH^>m._^Lt5_ȔP1׾&7DM&knd@kQfkd.8Tu+5w vs kx Q4i ,xR G?SssazPKզv{)VO ΃>)!\K6 H~\~?) @nP xVf= )hbJ/fC0TvP]| mu@_o3aRyn lK$F7w D6sAE>ƒxgvP@)PT]XT<,$b-U=i4\+FKȤͣU0NSWq)Q`=gqM<1JjğK]eꄔ3Ne'(=u"u;;Y9UbA/RO݊-=UJ+Y~aXUkGS()p8ciEzIب[`ʮEQ %T'2c,#XI[$*Ér4$dX.) g%0wӶdW SYSZoXS ;_ fS& ^$XI'CR8(D\Q`L 13eD0&>IYt^բx-f8LnIiŪ|SAL߼i!V0vY'||ѭ~Yn:C{5>aj"^0_ܟW]p'IA/6@ %LuiB@+[{fM%/=@PN|d-Sʦ,g& ZsF\\M HS͏l$WPB 9 b E~ 6 k$)ﮕPm׎ kv k\J%-qC]"ԓ qC&SøeӴ}wJnr7nt w[J[M{vs T{ RG) ag¿K~lu)/_(֖c:v\' ='`'>BÈJ-녍]3GI}|1K@@DSos83ܥE d\HI@.']" H%{- KF3hS~aEA鰮^: > ͳ Ԁ=gz:I鴈.G *ine`WXFԬدjzG%'I56Wh3q$j˘'t ;]_;bEqک30㙠"$4+u_'<pElYGÅ&cWD ҴRo?ϞE"|6KkN:;ZTڹ|S Wn-9~}T; t@6+rZMcGC2M2(5VQ1N S@R*}`n{"Z-;M,O%Bl/TOm9UH;_dbVƱ u 6lχ"+X8t`3ųg"!{ dYWTy@rW2NLEeKi1uT8c ;kYL΂OexX3_K4ZYZ;oۿpdO(y|-ϫ_[=)wWVMdDJo>UyD8i6ݪxdc;J8XyDuKo.\oa'2mrka̺>|uMdl;CAhCM8/0˞HsnH<3vAŽlP>aL&"Λ bM)>ׄ'_v4%~7dn%`+%{?R y*s yh~\v Y.zN «r{z=[FpNj3)~Rꩣ6g4bQ (P܁uFzRG>E૰ ΤcFxmM,BOc,t\f?)*_RX1Pe;7+6ct; np=ܛ)naƺQEãQ̼ڬp}?`ꦂua"DeVZ@;2g@4E%Hvnu%Q3r._AjtK/>jd(S=wc'Q AUs#ߚU)~p]~3XŭP 輗*CQ5VјFXuS&&TX^RvOO^) 8̳rʶt]$n@@ۨ6Ӓ D =v+6nt%)>`5!KP3+/%R(Fʸ-*HJL@#0WqŸ_:y6oKN2K3yK~i-Gi'rxhn3fmպG evmDi{r. mG8dp2!Eݸ; `BD&-cੀi zIV#zl)K+Q ;{7G$YP6MHIߐht<3{i!Rlߤ@O_}*.0*J}pxIt4i6]O%뿲<?)w忷TxJ ]")?D&{F܊-,W@CugF8;h*J~<-c9"E.bב暐{A.gEqYNvZK+J'հʲz#N)+8?Q־o[; ' 2n'u'z]~JL3}͓[*N$K_VRk26SOyQtaz{t4u躐m|W;#/` @kCc0`j(VD, <&eGSbo WEw6ziAAb =TW{8I{L`>W} ݧm c>=n8`Z qYFgeuoWL[`)qf幾?8U ۲R?#ANl9l٤52$/ @*Z}KZHi$ ?{ `!$M^f!i0’#?1)Z֢0` *–/{3 3X#eL=VRGԂ(o`Xz{g gKX~#HqG,?run߉$U#GL:%` (Oc ܱoWQN 8Lq,Y<52![i2_հ&LcxWD"q-StztGfIFFtX h.I=9׉$2=4>aU*1^CaX&Ht#S%IGx@$Hz\i_ kx6)٩"He7 0!|@Gm r`%j|Wڽ/AK[Y/N31?=;<h>F sPǬLXe=#dg?< ܇G·2a}/ b)F8_uc|Ge2̅π͋F[>#fP6>~%O;:4m|)6266Vs9aMW~bv埰l]7ƝHd?b sSLc*Mw5ڏ롹t+Fk& RmY v\ EJv60o,fCNi6o-xwr!(jc%Ha RS"|?ݺ ef'")~{@&u m.Mh_ۣL aI&6S¦9 _m{·0{~2 =E]nUw y|h|i3Ix7lkdfZ)o0Q4y *ft UUqcr+g$W磀aclV‚BڋFZ? ^L}+| ojV;J|we sXwv B\å֌ӃP9zY`6i6LT5M;6 64پv0b()~s۞}bg0 E "I,Cso nEiU5N叁bl -IZ.G>{ dS10 Zi3@9[9H;W*8&s 5f*1EKb]@kg"+}K`eg%IUQehCt2НJ. `̪2;a,(Tu{H&pQNKV$iKzdď>,@gز?TgI;\?8#"{nxLzS_ .ppdNbNnұ|(`*Efr69vUjGԬ&5=ã\ӿ/pAQB&^zꇹM hbh=3Ky8N D,)N JWOZ1d.Ci8w|})yˡN#<SF\Y0d2[0 QUof У:Mz3Ю"^dU1!+^*Ձa<)mzh}xڮR}g4 xB[ IZ;'Z2KѪ94,usE23c 􊒊k"*>tShHCg޲n]+nFr29.E d8zSNMxcF!>:~/@ B򳁟_T5C% N턗w$<OY roM s}vE~ +ՎאKm\L'EzW JB<"qmg؊H8\g J6^ղ+뫝]/9827:$7eDpDL]yKBϧv(W6&|C%h_p'厸:A/^UՎ}Ðyު3n'CvJrT=Ӆ'y;}ۊ9\v<#[\ҫ<>39_CIqv6ߪ?*-Er_w{>w%? zȼPя9 ^ E5&eһߤb(ll IgXxdr%ŷFBq*.v`cR'˾M{>CuaSQ "hZ'Շ9CS|"U8 1gvߪBߵc4M9[4Z|AbW Q(۾˫p0a]C>ҵԹy^9Λ;&T6ݭ4,/% Jt/ ,:Y S5EP %wr#DrIJ|}hZ O_A~X 3(~6ç-pO>(AZSlD)h}S7< ֭Yhum SEJ=󈚧q}UىXT8 k{ h—1ݮLbKf裢%,oR+^m/yKJW8łɊj9<)-8% |Kbvy$TJ3Z ?jL:fcRy)yq%RHmBg/ m>D'u-Jjit}j 4+-,=O{/,a _D-L7qkDUiZ9EůR^6k&Jm֝3 mV'=qJ07׿S[gЉdvq|St &nz2PӨn /1ֲ yWx|&AQBh*:a d"YKEY+ͬHSSsp1v#aU6m~*j'{;5$b7{"O蜬Ld"MH;yxgh1JNz~&l̊+p϶i\ EgLX{r+2yU>{R# 29۟uF*o}s{OٷnRQwrDҔHQw?aB yY-\3* zGBݮSkmPkXM8Lӆ6Gr 6q Zxj+a,a=W>JqRgAALEf]]6d| e߽*|ג9k7N^B !>fohEԫ0ORyyh˕Hf+ u`AfGf73{JgHq]x+ZǺ*"V*֟oe}\>- 5Pi}hgO;!Zpʈ xOw2ΣPa'h*3/#Uy |ihZ'xQyh@G5roRX]>BoĚٝ0肚{/ybZ Sо6,x2_6EbCH7ʳHj Hnf)8ԩxC t$A@=P'GDp^'n#g/ d_16vyB:r3o쎬^Hx6hl1Amݨ&X\f Rª RL oWc+ W̗Pdى<@Ym8NS2{V=Y :M(ra Izҽ%b&E?ڎ%&avY|&̦ˉjeC~댘my{Ԇ#+;h Fv4!lS$ȻL'z/MvvatnxvM\̣toMUa >G WMGf6qQ%|ő~T9ޝ='52$:C}[o #jȲC0m C..\"rHTqf*&Ƌ$΋ Guz)KKJ3~s]|]1k65bܩ,o_(?hl%T%< {5r IZ[yzڞx!.=XQ9UKF`=,@1Q&srC ZNj[;Daz1֜'@џ'DT?VA n zc @݋:y?__@U~j AZnI h1cC *Hp,K}L,7M:K3u!cوs^&ˆn&fZ2h_w/."MFEIMܰ&i)BkJb$GMۊz~8yM ?=>[[H!& e.rVb&xe9=muYr3lJ%󼿞j<"O$ʭF:ZԺq0sKpٻt/-Y:;WQ(@@Նf,)P xu ץ3{[)-MTϖ!v2iY=YOaz2{Xci /:@m?m0͔=C.pʫN `6z0xxU^Ga@,tt(gakw.@P1xυ>=!YoYziuQUVQ6vx['wHXSBmzCUT$MceNi%KdDt̡h\ROb`?hxY:|j^KVÊ7zBRPzs(w$Gʗ G^ia8TũTpmeא˃KM 2U~/%^ozYU)8]UWOyv.0vO'/jPU䃇'-y-{!/Glձ(c4XfÑ<ӳ"b^FSX*@)Mk'F(%^jYET3x=8vemGѿZ7q+GaE|tx ,Al1ծ2AfOBL%ees*B'cDSmW<miaOx+Sj-Lگ=-{Ws$Smm|t\Plj;=EP=)`贈Eu VpOHt44uZc,0&XI;5ל[ KlR]+NObk3~vbpv-*Snvtuگ6$&`nZtv96YJ`4=zgL+zP~snj [MHRNZja"ŵ @ aF!x@x C84_TSb*j?fvQӝxE0;Yҁji:ج*|rLPLKg?bܻNn@(~3|{0j^rul. zUueiu=M?6=)=.}ݱp6B2~'ED zCtn)SKM;@ O t T `lLoSUsR8KIP޲Iz9%Q"Gq%n d?XfgYC7]g<1t#|d_Dݢ Wg)[ޛS-ڢ\2J6xj[ҕDt)dg-۲д] ^|1ӹIquV;*vO*'Tq9fżx~ܳc; ,U 9hjXoZ^}`}eNEvy#8gGP_A (MZütQj=͗A{Ӣ^p';/(:`$̀GÔp #L(d TZ%U0x-~]^;G􀇟Nex(V/ Af-ZY4HinO;@.6NxݿWnepCeD]:k0~_?!_&D\7HqoYfʱՊ(\k<=5tRGܟ(͹TX~Ւl<]hw^)o[p&\:(u|;bK\'֍l(+_2.BYqCB6ǾIc}rfEyNs(V ق51@Pp*! [KG ܒW"Iw ]YV* MF2tpҾԼm@XJo$"‰†*+;L E.dȌ"p( p;1pqc@ ˤ $bFqh<{?^?5~2:wRuI\+ƙ[dKHPp?Wy.f>o7# \'Ъl" `{|FrO!npɄ@:{rw@u jJF mt\RqpneIՃt:haYz ]pV|ẽ18{vj>-gU'L_,> U\ FY=l;"ya|cpX*-D[41^y, ž[Sۨ_~LxNe@:ܹrh%@VP_zyI_[HJs{4w Q.0#Zy@M%DU1K?d&f4J ;1Lg2f-G<6AѵYW}:r' HO,S1 Tщ]?V*/DvrO|$Edĉّ TPm%|MWV.:${#cQ^eP /6+M78_Tfx V֥7Qj;eB\>Ad[eijTcypWLD@|z0WV=GRYeʬ: X \U zg9]䍎^QfuHpD,E׿Du͕EcaKZ׿oeϺGk? v^ F"X[[~ȝSQ6+]'iĞry@QKBP1VaAʝ)KGܽ7"[;S<@M#ˋOVD>`Qp3D~ߋѶɽ58[ x/ܷqzE8(#ge{)y*ı?]GR{l,(~H=W L`;d L/GXȊq\]hB`3S!l AAu.(Ц,i(%LlJgF=4 ƥF9:E;Yz* t!Ey:CE׋W"\U87wT/}سLö }Kb-#+,-?Z> tx Lb5y([ %;?HOڗ˘PjmAl.~>DvDZ llKd!ɜ|e Ae2wz?Е M?< Xx#2z^S:\$F;֞ &Iɘ# -R\ ՏEvtM"ȣeቇ@5WN/JvԠuJhxoa "&FL^*<Z3͗F{k6)h K:oRHzpwZwQ-Dv#tnq+8m8L贔b71w3v=CDhWͪB2BNYCѤ> ǡqp,xAlHIq&g,؅wlL FzkE`Ճ{"b>T4N *lOq9mN Ed}ogRnr%+ Qؓ=mH9S?@CT4<#d!\[coV2JeFI߉IK;6՚#ET'~Ѥ>ΦZW>f€ Xi+dU+\Z#9r(mؿ1o'ךeƿz3!s.L|uigvOH{Ep2웛WYԛV= ʳei^xIM8#3*`4+OaNд+@@űlge0>mmxXWϷVbfrk5x\˖+N \(l999)kӠ]L.[xBa*11h$!SՄIZ5Bvt}yoV裰KmUFAҕ5ү3..tRo|^ zw;I?G:6)>+z DZ{VJ+)sFNy󥿘Ӱ nG,j}/g,^0ǟMe@_6Q6ktnB: e<5"8FVql\P@Jlvc:}gJZ|n aSB\{ى(,Titü;zpxM8M*tݐߊE믟xuNFpn!Pyt;4yQO~@6QPibPjFW];V+/3DzϐZV1һz?@.s9 d_#ˢ^EU4Rz]!a+W~u 0kȤ0F %Eu=OtJsg=[`\]# &]%xyU2Q"d`(VNjzzTK^)&kYއ2奐2ᒣEiE YL*-TK_FWI.p؛T/XB3`唤^r1b&eZpb1#IiVQ|x.ku+p}L "~6%Q}Ca +vq%`硼PpҌ#-& #f{KO&k/I4@P__DlE`-:>tb_ 'qPq Iѧk U/ D &&( ʛ1LZLNB̖-s`QCYs$^3(b6{(ȵy.;گs! Tw|W< c~Jc£=oRL$/q44/C ]_N C4HCaīPgaD)!>7<ë,hyU*)2ImtzUEzVBx5YdR;ķA<#= E(LT~¬]]5hBEqJd 蚣"ʞgAH척&qI'W~ч, e:x"[ėS/ڠ(OBC`xR a(%1sbX 02*҇%{Z8' <_U#Rf\jNHԎ:y݉(dIi/7DFl #ȃD0\[S7Uۈ59uDTjǵINX~ A6y&m@!yN1MI4 ( RiPh$. $X.0ug_*ct Nb`U2i&.\mFf8h?mnG#(b՛oY-a>~ !dFFrmZ@I6BqM#FH%D fX}_ ]ioCbҋZ7F蝃'j_4 7,WA.7O"(@֕p~\AլV?<6&l +1|aq2Zpc#vr>I|{ h*5ͦo+!K b9+[.&1^ÚWƲZtfYU~Q;j?fx`v\hˀ?3ϊxbqc l:9)B$skɮxW#mʨ|NyN(D~d#2as\>E۔t_:}Ey x!&Q۟YݿqS%e ~}׫@藮E0= 0\FswbTxIKel/"y4Q%S/NNtok1JvSβuk؊.s@\ ]J F[{(5aL# aC,"TZu,BV́GoܹcvZ qaK3kKG beKlzMG}]2<yGVƦ%W[xELJR o^U3gQ-pV☊'IFPpXk}KՍB3!p<<܍|M?ipSbׅj#Jy"|g ,ҏ Nzdj]  z% }x7 Yz( "O՞h :婠Z2Y5Гf7LK0w5' mLOZ@X$%7(M tb'>p([+̫#(Zd:F3_8R6;^M&==* @O-q@*w"贺"ȣxAQ4]nvsMŒh/xͯXc[i `d Fu%-UfתpíCpI{r[6)3 uLQtuu7ba<߮N_<: Zuir+cqPgk%(g!N?j ]SeqL!G]ZI_j¨b;臐HЋ(n_ {`?3A'p%֩良8k~ !s=)6a@8w0-`L2ifN(߅=+ (2L4:hJ=t7k#av\O9?=F2+=E'l&o'?_oN "=|poB+gc,-Nj_0{wPuQStw#^!t7BY[E$֫Pصw'l8iĒ2Fs**Ϥ;#0tЍ /Jc{ kCdIHFg4&t1tA߹4AGU3'Z5GeI1^w 9>HVt %C9X ?큜Ҟc DŽs L$2^^D Yb#x(.88lԏ *DNGό₱4uç2R)MK̎6T!Dυ aa\V5M(y}%v5x\WWdyqR,ڷbl%.~Ir IyD/M't/d+IU 4* #} "򙓖XOUQKLkruaiEPjo[ǞǼ_^Z7*ٔtE,ヰ,VPa8o `؄Cp|JVi42b3[IR:>+`Y2>W%(w5)יU~+:G3Jsn-0WiN:" tmF}ǘ!_~ Grol bɉ^xͭ9Gp 7ĝ| <Eձ{n7#w&>+Xѿ_>I_ *QB13?wTU C *~;t:̈́B@E06{ݪ+Gq\+t.F*B>5HLCRk5{ nـXZЂV~L+0+Uq3_;xqn㚨v}cNx;$a=x AC`^<]!(CKM$Xy`2Hc(A}ϴ_~4Zݼn~/UH 0):!n\`d LJƙmG:ym3mJKNiRHȜ勼KqR=Gn lO #2[Fy.C@o8 z*үrl+| VNfLߢF;+chTqKpi. f1_;`UoL0#,tr|=M4[]GAi]i?=/N'LD~.6|"Q"ʿ3Et] ˍCȴxPGc62gMZ! QoE p>@|v,>0ze|Z0Y`SrtniDWj,kO.d@8הj"z_IDž،K2zN GZҵaeDse(Ŏ(H&F9|ĕ^(oc2)[qseto5u̐;6D`<{53{f51ߞen{`z50y*e3 $&m̈́MJCmbg*XⴤYAZbJSJO1VB%Ӑؘn9nVq}rW .ȥLr- 﨏Atݬ{*59OKSw^2܉ bPA_)^ BQ#?\Ar^AI{-~*X(L+aaƂI3 v'}nҳ9ά*9e3mJ^jqzǿ.5l-Gܕ{p}4A9R c$M jHз⒴qב@˜ ng&Ld9=׻ q*+Oɖzn5.lEՎfcOhEiA2IGjq¡~Q 9T$Ђ^a& 0H#Xz~g5-Fȱ: oR r޵0ڡ3csdCcE}hh^:UէhG㮫;wn \֚oVW W /CFHJ(т1O2ODAa'Dh!P_U߮s*cC$?&!'rqkSA5]X[k*-1zĥ`MOyʗ"|Dw}{6J \K +Sv H.̚Bjt)MSpm"h0yR2ק;bF|՞V07L"^Ke"/zcGȶ?{ 9Bؑ5ษyԏ/ 'gsI:'ڌFb~҉ZWlj$c-Y hB }>T5L.@ C;qo}TA:K_c嬂h&OYt o j%XjY\&RZ@"TErhf`}|Mv pvF`Kx08$Y=}0\UԆMP 'M:&.zPgBGР+և3M=Mߏl`\3MUNBް7:dE^Nv  Xps!V&-T;^XE͠= | ?]u#+hL 6_ Pm7L8B̥aSKN9` j@ 0J+Gy (@0/fA(z+ +XA-,g` I8M#d*U@fzǵhcƲT\²DFQQh#W;$3^?-)i[g8=JJ.ke Q꼘6ːs^pHtaՍ[ \Ɂ4?@BG[ f+\l9 2bG#NcI;f{^I(G8'0'TmܧcSWOq} Q3ˑ1%k\  G9ܵxnkVx০]7T312[y\ l3*RMm TZ9|aJ0vGv0'KXѳWVAK?$] _/ņ=#J".)`l_{ #);i+Yd*\*4aT6ʷb]U20*rJ<Ubuƌ2R“`~6xҷ{6)1!W? "bvحdI"4D =Ukn 5^\qY!o9􁼹euf2yܪr`RM,$Hwx ͫߗd~ӝЉs0a'ku!%jOGt^V~*z(ږU4nP>0QW{,ֵ7U ,b"q{I$.M B؞KO8.M0x4OWEpvhfyLy;2-\Kw٥QgE]g1BPO![t9"j9le_Ū R(/y2ZfbUϒ_ݭ625#Qm\]N}\?̳DsYֽ_:GD/DϘ>#V{/d<K"Eþ1 3L!:nAG3OլnCÇ#^9n'Jny뙠Os: m$GHTkod ?!r҄9̭TEsx,ζtt0T g6瑀a_ JھRJ|Q>Xy Njd/-Ij y ZN ;kby_u4r_\^cv)g*!koGbh/H.jٙQ+NݘJuJ7k ggo`xswP 'mvEJ8yz*+rK₵lo^|XNޒnYWou_AIybʜ9xJ^\Y^Hh.&j3i⯅HԲͶ׻0r ۫@r"BZ[~[]=|.c0o9iORrr{9ʰ|Ƿ&/gZ[q&Pk&6d !MGDE0z~w2\ I0L鷾曅~F솜 Qe-F6)o;qDE} `0l+%}:V50T')ZfR֐+g0>4ޢD)X (~=l)I+ֶl`YusuBb<9nrN#W@v+H;r*|rsQ__\kqúމlxhC(`3YjI v~qj BB3ĨK6J*mr]ggΒ&2NdLf] Ɓl7D+[%G{ta͕Zgu-t/U>8 -]x2K;[UmڴiwYDŽ+h +->@7-tBœXn,eຯTO£j mjLü5geDJnPbh5 5.?(?l̈V7,*D3~pŞ;s{t&`b.dǸc d7_Y;hv7bq"ZE!n_OE -SHC4u(CN {GW' GJaFt܄U,5<̉PixomBB~#]LHT|l I~t' ؈"5P&+]^ 9,2c[[P|Z3<-Bnf8zbOpDF 1pEx5r>v:MY9dWaƜ 0_~NX}ׄBw &Z`SۼO*^Dܧe2bb TdA?vPP 3%DjK \Z95ɑ8d~cӜY&?F7^&J໖rTpR$cGDvE8LlI~ e <6 U9G9c 5A/gzHTb eӥ\Ri#eou.V-G~U0LTN emA)0HD-3<}P/)V@[8&|p^/R Ҹ (c Oe3k治[ ;k-ݹSnZ "eh8}4 ^m ,dœ#ǡA?lPF DҸT{o℆RFU;BcAs1|PZ?5V`9RdO˒+]dË^wS4k @X[դlZ 'vCay|EyTL~v7y =)$7P`@P#c4S׋wSԂF8xd@tk;l.Tq]=٠;-NBhޖkM{kXO7{mJpX]ʐN''=lH>~=J&w3\eXb4i.]GGQ=г3u.Rؒ AH\uy8ZU IB(d?1+al[o|*a{}ߝ) 2D -<}]O8pu?&־u|A9b&ɦf JΗ7/8;"eEJc0:\g76l+›pzbehEŬZCJC !@0 D-" 6$ zZ}ۜaMg>*kb6oymUqퟔWIs c( SX.өՔH"i>ļ F\ L'bp' -n^Z^gwbfɡ"Mj,彣/*Y5J zPjW:֊EQJ7z5`8~{Q|8'-|J^@!D:ah-I+tOBR#kЙ&UqaZ[_-s#i}҃I, {TtA+5QYcsEz͋S֓' cM8KfoS?Z'avAH c{q,L{|K`02T@(vq52q|(#@{樛P L8@]sc: e~) `@NSge32`c-ކ;.ҽ拷djgB70_ ^CwQF8TƂĤJ'uER$Ŏ `521.ᦈl̹LS֓A/̠CҺ-nʛn~ڟ#I TPE tݐI,dX@f^ LNd8*z*A`/|\O}/$6;d'AlMazeTG/ x+Ń"ٌõ"K<[.hBM@ NW2mQHAb5D)'PίBY'Lg饶1Y9ar7L\p)7bVPq$&>ތ7 ݌LůgSpQgi%->AԮ+_@/q:nze|pD-}țO)CXH-03jIX~FeI`tkjVrNNy{Ofh M+F; !_ ~LzMI""ܙ˾vS/WFq |&n$ yB,TDNٲ}l,&W1q"!N⨿ 鲑󸎆YmMdOvaۗHg۪18o|,X-.Bȿhw^ǽ%YS(/Yڗ6 ث6,ysƆZ±9+gG] 9{mA=%kP &jUu;ͫazz|8$K?nuv1$_eq$5ZkZj'kmLq+;_ۚ+Ե?b=hMU}f0 nEbYrz ,]`/""r{p^Шt7eS3k )낒1ldV}W"JtľL_tf*/dV=J8T 6p<~H"rLQ]NDŽa VĄ @-^3=̙ ^K P]m6Xtt9|"S've~EAsAC||W`?/|}dn;]đO /TYOufLahgvbm{W,F7B9:DY*#]_Bvg;/*`2rOrC' ǘ1A⻨A#щ-  Ja/XU^X ș~(02 A8r OJѼRdܽ"^>Ɠ*Mv>KJj At\% r6.h[N[ԛ|6$%ITF?q@Az͜-n0RH8LOcPr7HiaVM$UC@qPze늬&ΓbG1ԩI 5WVs6,ť8U?6Ċ5Na cԓm? {2pR8L.ڥKwͶ~/?#MbEJTi*eR֛\hqO b"۟XxD!y5ڔJ"0t _{/CB=Of:-!SaR0$ oL5kO~plSn !0]hOvzy64ޫ]s7ޞěDlR=>)#yb% bb!^+歳,#jU1@N]:i~$mk3JĈ/ {{) pN* S{bBnc+0Ac1P3fP% eavp͛ޗI/ 1(k/V+n&&k|oa *J{i7Kh2 `Ad( ]OiZEƇM[1LTωE?K"~(!ԘڨKIkvfcƀ( ԕ⇱su K -:JuPzKI߄9D|^DzE0gC w9˶MRZ48+Fsb jI_0r~0);'7bE,plKs }go咎w hmZQ`irсwk-1^(; pU a 8wn;`?s^ &]'mkY^[EFu ⬷ C8N*'n]o'Ske ta8RavZEӐ4#j~qJD$Di9?+KEݷDTYA ⧧>sd`օ cJR߷0;Ӛx]@L< SnPuűyRVL'Fq14&1+DM)"Mnvatԉ{ˎ4 02|7rj/{ wCYg*nE0O}רխJg# 0lş|kl_^ŐNYƐ,V|lL BFc7`۔|럆)`[ M0ёmw*:>OUC.A%΋b`M9BQUuuhaMKC wlr\t,m{VyXjA)?u"A-]Uꄑ$`;& QEZm+\A= (eWyڄ3UO_8;TI$Rrv<&2]x@j^[ذWE#{8mū7"0D5E;lSv95}ĿOE)`E_;^9M] q;$־e L\\b5=nӇOPΘɣ5nɈHCM:㪼5QsCr3ZhG[AZv|8훅-tw֔ z oaR*ь l|znt+-]M6=yz5C0VlS?CJ{2:Cn:rD{[`.)MG,'4L}O 7~"aiH9 y% 8cj>f;AOg>V+ g˳N'WZS^ċ2f2%eyhywAHeWrP.Ar%%UTJ\k*4 ھ}JOQY}4.oD{6*RCXuM-| qQ σ@31NZXōP[M2A^nZm3&}jD~yIk$az`)BBX~HR07}a45+t7H+M|7aFj.y;5UQRW.'$Gi)3M{J<oH$h:[6"Q;T!_T~CəIͧhh"kJ7r3coy?-::mbnLteY;V׉yRDQ?9GqThk;\D2H1մ{Ghu| -?$G5Oo[QOY~ARʕ$s7yV`ͪ 0mɔ;=Z9. x&=AkU^\V.ꩮ?p>[ڠƫ&OirG" %dUn+F#eGTM$YqܕyWjׂLrX*T rz{o L&zf6 *1b %T[$Ksl/tP"7iZLy-fb)a'RdS55=9쇼Bɛm#̾u-j ׫1YYQ?/gT2Z_Y ߽וf]8I(}1yglcQ*qq.)^Vu=}0;YH s$NnW[&<ͫ'||_}I}uKhZB_9to(2̽, pjz+o6SCd}'eXxުH7c@HFނ?Jӌ%bCB@֏ܣJcQh9p;qseL^}1 ˫u oC|Y#;0ijq0$KA1MF[/ߞ{kl[:7&q_Ǧ 3J,xqJmVco1REiK=tnQV=Sxa`L~vɻy+Q`jaeU C!D`"Q^:9pT QVy&6b V _R;a= G<,bvxXY?q1b,@@n r*ޖDׂ6GaQ;@;_oCL5ojlt[kQzS9lmenuf<lݟxCy iUwX1m׽8_ .C!غ6L0!kVWaZ8hhfŧd?⒒֘epO W ,(K2ÎAkBE@EXВEoLF'i(k6O?J/.w]ݹ`*rKzw%q3؟dž/.<^'Q¹IQ7 $+6Ŕk1kzwP@{>qmoq;pp0p.H)6=y=ET!н|5%o^dMGh-o$ Uɠ[J.!XJн>q]TNC~};%^Q_zHyRtR}jƸ4|^ C@AМCr,.\/m*Л7GTցmNLt= 66Rq/}rPJFc22|2i+ >P)%t>ogr 0-Yy0alWJ&LNv}̊uTm=7{=- UBӥD1j6DF=0uQ]uBTԈْ1LkA|ԶfC^uYG f_,|QOE vڲcx01E%;wvRo 1ZE.clYbk| :b7v8zzZ C:_a-'ߘ ψ=X͋fY PAu 3o_ۅW&|Y|hɐ SZͽ҈/TNᕽ_G'{%|9S/13M [ e2<1gЫ>&Ν >KQ,;K*uG&~̗R;$ogVz^ D*'{Kc/o<UN 7Yɶm vU\gzRoT toXAw9p)Mc|v޶},T1a,Z1X@h.Vu`έvGrԣ.If͹Pcbh.dL"GUO /|U }>n^#x@K:CKw~]th_44.duE"@յ i [ -O8r\kREREݤj"]3}V'h IԱ@6Et[ʯկfv`}.-*Z7J 9S" 9ǩu>n&WpZ:#b@ >&fZxa + Hcu["n$\GZJO5?-\Cg 3/|b(9/m- 5W\m;(/c} X=m8<2k{߫ǙP,elv9+`dKϗjt`m}SU;W)xX B"(7+d 1c7:q'KOW 5-sWIG|AKO_OgXer"Om!`R5O^s%C"VhVacGLsЋ5&ՔTb g UyڤaۛO1 oBD0M\vC;\[LٷbJ滙;)֖=kU7◿mjPq`\ t-j@-*%)EsQ30N+LS~_^KXˊAɗjg' :G]$rXFeGWs=U( &Ø79AtN c/oVS<\0GA yt>) 2 Zuʻ Ƨ#9%vOu#d/Pm7w1SNKy)hPD/V,oHCшb"powi!-j4[1L]?Fp@"n W]*.ؗ% ׻X "1,$Mٔ B$u(1Nͯ.ĥv16 |R\p-uc`6`d_na_R xٔkl t1-L0Ua2ZECGxgjI߹\0U&חgk_ZlS6JjD5:̈́6;!jbeaR->]ΦYEvUcw-N5U^$an-radN@wp|UuŠ]ZZ{5q<"sc1M#MC+$2G;@}N (y[E󌧓tnaǐW<"]i,s/84W7 ]8ax>R/+>Rl'PNPfeǐ.ChOqR>{n~+H˾!L.NzG͚ }6LS" tlge^M%'[_TNV-" > gX|FaDU&{ SݕETpR o\Amx>Y=6 (,(s8X @MEaNQߡ֌l\_wՁqp7lV|ތ{/+.1f. [8a.w*P_p Y9S)S AKf][uݧ0{)@c9& O"iijGZ @Z4YA!'(d9}}qĽiD5:̀6]tf+t].3$;T9po) S9- @RW |;R>D^g{:+@h!9 i^`ì`| /HZ\((-"CX%x_2@l;T@Ɖ8:(N[l^{h@ _yL>!(7CUh-y. $Ts3&pgh%dݑԝ@+$]C 7jMol#QG1 E٣Lo=ijShxBڧ+fAgXcHf&GTX$);}j*ٵD;S\WKP|;4]Y<ßr)6o0Dr'*5Y')ޏ|Ŵy^+rE0NKi4&xxjX$R9G`Sag-@o<7%{ZF>{I2Z*f¼ (8=G ,wcmv:GUlݛ{U$/wѦnΒ<@&u.Ȃ\U5Sn9wnh:i&[;ɺD Wl t$-IC G2#rI"?Ce=·)9&fPMVb' F7hR8 6lLɪdG- lٳ}6M;[!QA X½Gg՞]|&piyKQ#U3'" Ccq7pY@ٌVU(74C/_ؼ]Z,#hoQ\$wŢD[HtY}0 |Mơ}cqeyqSxx%o!Q꓾uJi1u7O"Zs(S"3cr.\ 0. h|d$7;qe*d;>K㽈0(Swo43RQJe|za|k wkr#>F}/~Rp` 1JdH> 0rS_Ou0D~E2fw!%RH7 DNw-{Ї[TcB:&1^27W|j_/dR4 p=5gw^$>-737J r(RBI9 ;W֏jXԭ:8li;d0?Ul\1Aa;Y'za# 3ٵLi,fGQz #W+ l-e:.W) μOXR4VC,=ߋ6wp&'Ўp$tO}j"SP\NciA'T樫ujq އ/G(6 1 gB`l-90`t{lEhs-4!!` ~KO) ).-&bn.*yr٭kZ7VX*H1D6c A2#pqi&;=RH')鈋OkIIf{*-Cs3:gC_1Q;ޔ( {owc 2ɗ{/v}9[e<`\ yyx*& .durz.C[C&gqﶴ@;8Ir^wɻĊ@Ēd.["dJ}"a>Rٖ_ٱPvg};oWpMA M2<#`#7qUmeBտ>)ppFq[qT)VSW8r4s@fV7w|Ƞ6X_L`])q8ldڝNc{KEqɞ!VE}R<3B$7U>4t7 H]4)tmV'BT}5T啢DbE-`Zʟ[ǣH!(ؿF|n6T|⏸*Yk@y&(ʉHrh@(u]x_M$[M+(X|B~1fS-lñVۄ 'fREf.z'CLB<Μ/Qʠ ƗYܓk"1K.Li3bE($R Q)|Nfj\5P9H҉Nr.L u?2<(Z6"3e2SzD 6}xm53MR-$Pa+`dJQD?IvGڕB[lzE2!c!&)Q w,Srw@zqJLҟ^f6Dn]H5M3-Y@$VG2/hyG}QL@h)<#"0<+y8W;nLpt;3MLkTOl+ aB8]cnR T|Ejyi+ë yJif"i&.g$P$l\<*#w9όX·!v4] H pV)-a#A9i'V?f+oN|zwbDZT2S2fQVt~з ki`=Y+`b) sH~C]xJˊ+Pʮkj3,xw gTUDe8m)r "K.IFh"PGՂŒAyru@ =bH`x6A(N[e<>ΠuXk2HN%N9Ncd_=V$]\O2"^:&_ğM7+y,-e]n#pd߲.BnP a$H0%>uNb,/NѲ0p)!]ڕo$"h[$ʚw8n҈Lqu} BReV.n\:Ye zP*A(5m؞+, RŔb)m+wI Hk~u~nbY6@:+_ę!&/ `JCy7LWռwߒ  "P(fpw49=9Љ>f^Աg[>(\ :ԁGăea#r3r^F E ĉZ, l.͐U^hڀBǓ : ߁Js{uWd;RBeK5+bD?$L\rIy|0WYC~y6̗cIRCJOCM:B #p ^fbr2Nػ\aPvr Qm-.T4Sc?%|S+e-L0]\XKe1йt_MBp+>M)7A)%|fr8!ؽXP_ea1fy0"WG&?!M\zYj9hR#Y%R\u}81Ϟ㙖{j}hZ9~8bi\lx" Kp QKSP(BsnhSLR[wS ř~tV w>׮܏I"-F)p\oK Z82 jZ^n.7iFvX9q}:Ι+NjcY\5=+EC%E#R5wc;8"VYK?S-5PURŕ2![v`Һ،F zPJ v2hRs SV7y[+9M2 4n͓5RG[ sK"r866!g7[v;bؙ}3䮨bwlXWu_-d&P45N*ƅ(*$.!| q- $NBe>@X";\odMlcé T ~qQީ/kZSxb_Sh'*vLGU =KHlbK# BRcS!iri@L][KbFA䱤a;{͍՞唙u3D;!f&}xoS5Z Xu'Zt }%rI1݁#;rtl]_ngΦO#Y%ou9Ⓗ3V-z&bQ{ b_V|(j"*Ϫg8%=^Bp`[D*Jg?I܎-8گUqP.Ԓye޷ C\ڽy&"JϘɨ.{ʴl(tw}[[wtDԷuƹcV;0) uyJ%DEb\Khx%7.8S3פ,< ע4k%[b%^`F0;}:{E |=q+m6L m%Jڅ(J"\]wuS-}?_Ѣ f* $AِĄGC2ӹR .hSH``g]=;t/KuH?S Nmo ,\d&fSʨq7 N>UOC.}+y](_Θ,d# vîxl.|a@5G#yj<8'6g_g(Ch;#3q>vQS g-l d-u;f`iw[Ҫ]^L?Ç$OWʜ"k=q =/oRZ}[U- 1?L]W6hO0ɤ/勈$?* +H#lfJBL>PG`yʆ6uSgcln #KzF& 7ͅP(KP*,W|=.]B=mǡIvc y\m}vn'&}g/U&x=W4.)|'sOρICB> TxSuw)ɾnD 5dQH'QkZ; 2IUS dL]O9杩[Zv/Uf"=rRĖu4(gu9 |܇m-,[YGF|. V_JȱS}-.i <}0=ԨVtɣ# .z%Q_vge!:ˮkU.MdP8S{+i8^d>j=4tizI80>i.қ {MZ~u[Xxy,eCd|)|Y]>uVT;@Zݦ|ƲzI|@-LNAj{2c&!@H%"JF'9zC$[Z ? "qkӅ N̄ȼ2?e=1gnsȓZfv.~qŅP& Z*6<ȶmѦw=FlXk8 $hԾu[Wy D%G`43e6rh5]FŌ ]_%>υ,uٟDsGh2D6Y.H%-x?/̂Xy=\2!rYRaybw:'}^CQ{j7ZMNcC _Cb/6ׯoAzyuaX=pn"a#`4XKRyv,F;੯< КN4Ov[#K0"僝ė&Q<υFLbݛ1`(bش/ a >* (xKq 4րMEUx1YW+2):b;Q[8MVvK[(  Lп ڑ틪OFvv6 Lt>f3u`-ʌ ԬB- -/Z7OV ԇK7ᦴ}9#-Q;|s9ut~.-t;lAׄuVaz8F43Lsr;\1hCU6\\)GE]nYkН 5(ՒlEgOS3HUʋDΥwm깣!4~6@?,8177<86tgKl73t ]1_ED3־^˒2#@A4R ̢3Ln ~ (^2>;o(if,[cH_=q; p4pc&+Z@8=2Ql"&Ubk`f%QHDPEsI¹l.4rcae;Orja:$IWNCф50<Y4GC.] •uaY/DSƺ>KzxacIϕ9llUM,*HchડJoNo!k>:A R1{Mb-U HVʨ,T"aX "Yp/Yz(ީ4Iy!0CLM19 >^jX2Jwt g452pgaAAf׍=GDPʌ\rI.AP/@հ@kfsjl!;ճ l梙5_>b9DHI^MP w3o@洉PC*/`P-XY9ޟx]X:o">TշQ.EERC[4_mHziXݸIAu!~;͐Y8 A$EBF%d[4 cy1ILJHLP6CE-I'Y  ysZȱBc5SN"{b$ٙ=ٹ/_~V@>I0|vXN6i^8W0\W }SsrZb0)Iqn%Rjjo)ş,D /W)K#(2[𴶅kSw.MewXlt {MMOpQ-ԷD _]X+ GvVG<slL(C(ţ%A,xɰr1؞=@rZ bKWnBX9dMp˞|9_!n> Ĩ<ecZr}lAQ )8" r+TcKaɁW_<*>3 tvZWyČ[.xVy Y<2ca):ɖ_gYQ<:S\v%Ckbc% T}Te Rcӽ8@y,2*cyw~BZDk:afv_Q)vpd,Ξ_C8Aڽ: RHٌyZU8B;Zs{^p.t9dO*DfV?)qoo|e/E&"[,1H>R5x(]VΌk7o޷?L4zv>=AdLϠO1u!'n5CRVe]+HFer '|$ɘ(8YoL/K۝RRƷF_D1'΅.=XG^';MB!ڻv1( "43ZVl#ڥ˴<]˄I+k]4yKc\6`jZ˔80wpKKMd~ilwY'd _~h(HKƥ;"$B=e"U,Ȯ@AT:ُ 햔" _Jԩ ѯ4_x<ܓ r(>(:U~6Z=!T'y6}‹ ߶i\jM˓s+ؚd?Ƈ3-r}WT?t;'qv5IJ\ac.h+3]- 2VdUеy_b5A~><67'++!G5(%zzKVRpΚk\qB* $;իTyjŠnK{> Yܜ|ch$p&QpXD[Uȱ2XkXܛ˹xLe& h`x9PLm١jߝRRY,-Fccy4V> NSwf&qN]*x RN3cTy@݆̭O*ˏ=w[2ʐzЈkD y{ȑ߶MA&Lƨj>EYaw^@1o2 žװpvY)]X7g9:#RΉܞ 2dZϢe1FȮB uE~(Lufu*%{ ד ԱFZ->{πt Y#UIȭp6_^ s'% ~esH=ygWE'X"ac{DXbvb)30Jʟib7z5X'd?At9YaQ&,RKZecA5R l чGjI>qvbbVWbJQ62:h g7OtWs/B7!arh0Kt>Deg90G-8˺m2H,x?~-58©I/@ܦ 0/'OEӚC~ fQr<dk«t ^KY;BI PRgG*]ՑgHdV=H~PL> `yIy#ΛUBr9hv1@_GnGd|fbT|iqsy"]Fogø#C=(IZ}rk[Ny wYbDsU~hz4[½Lm4;:E&@(.퀻6!_^ДhBQGV +v!D5 澳&ߴ4:y5-RxԨND3hFqI܀ah#W wJmԼ$C d &:cSOZ6L|X:<~8J#7SrzGZYx;|W.),'߉R*WÝo؎9Gb sp9yL@Oy1?Md.OJ8 BT~1wqDDy4a9& {{KHRovT9繖P !&k{+-):P՚/ƭ{bT\(x[d:{W\D@pyԣEuOTdR?Ni5ըֿn CةɌ0nڅ]agRjzjuٵGت}`x;8a35W]h:V&_g}KzH/߾ye V`e_ gؿ 'R8)w{d1H!"87Q{P:y[:T0A:PdUf7ɆW+J(X%җ4mjE bē98UOs^w8oXAd hjxIϲ[{[kYhE ǵmE"xFE(|JlmKI!Ԟ]36Uw1ΆU)p 婯o筶*q\"$9Y_80Aoun";^&ֻrlTsl&@gn3~72&FU?'VbCxiߦ jl 3WWqbAPpEmv[Ѻ'MԞ4eyIѶ pv+0f88??qZuoC'L_Zd~۷]x S]*O2۲lU$&Y8_,?PL8wvɕE !;xs7@,zUn'dN8e W8^4C90~> Eӧ%Af|Z/%ogU_K Vw\  \H俟> Q-sL°4!Y0QoO( !=n@a8,&= ~3~n6SBr0KG? ;MAA X/t‡~&u%2lo w%l7/[&+;tWIuDh9J(΋@pt z|+35yV\?ySnA4]Hz="ɇηn[\8 B-f5m$;s{R8͝!ZNgdЋ=<;xѴ_58h^w{/(!)v( [&qN - uժYoڍZ1_gyCCbMd |i'0Dƛu7V{yDʪhhseNX?+4a-eXZr)@GJuk7r@4,"_2IIlD%D0ɱHdPJt<}Btk i N,/2ҝ^_.lf.t̾,у,}rT3]M E$E4螙P֘Sh0{ 'sK?p vbI^~.zΔKˑ_3OD-RLV\E3ČwX):Sa&BY&ǘ6k6-&;~s` Z03_3^tN%kc{ %#xϓ`֡18Z׶1.DE%jhk叉[$%҇Q5%J.oaX? ~t1N<7ExɨLe-MxJd0o-ZCʏzK(i::{*0c Y^p>z~(U&fްy-Se쭺?~ˌUJ4KuҰWa"L~=޶qO8´ "y,Kbg@.l!Yߠńz uS4Tŧxw/`K8'oTqpgK$ܞ#ӅK(/-;L^"L(۽V6C#M=7b I`TJػIe}esHh="Mcθr+^D&3J7e&.,%YqXXxݮy)We({h9g#my& {㺒3s0?uAz?LI&Qp,ᴘ8Z2T| РӘ#W+e\/mƗ&!ʟD 7XF ҘmUnwQi'XX;b-m| fmn_MG e+b.LXr KLơE`W~Lbar%Y({A/ V,,jeYf--eҤ~hbP_⹍D BqgtμhP{zt$ݛ(Im w^́|I8Ɏj|3d"&2ؾ}D-A+=!׭ԫ3&&s\[LTZa}n$v82 d>N/G*W@$ 5V猉/91իGGƢW6\#[J>JpfsLFSsUDVV QWL19VvFwȨAmtr 1]bSdַ T s*ajw\u^Sػ2&]olǚSfheXfcvsU~\ZHѕW '5 &?ظJa^ Y0uQ51P;}iUڷ%c r{H񬣫 N/;;{$V+.vV3F[$"b]aq͞Bu2፭4HM Q8;.M3t %C37%p 4yGK8Ͷx9oU_v~)Kj:Xމ6q6]rᙞWn۽:lzVø_#<ÔSCVX-8>UY`/gCJ՟$Wk%=kbSK"+ Yɔ.V~^ U1.)3jusvPd j&CpK51ȗBF|?l%:t YC?hq'#3nZZvLF `X OQc0n$@%);+}of}PϟjBa#=|lG8ӀzNk2b]LtZPFRcQ^Rɮ~C{=Q⑈J5P2$AgS|5.r2{ ?E1jƤ> uv+`OpuEi7}!?E~%fz&_l#7)}cFw!Mv:ؙ"`3n>n{8f<'N7 f{w/2sMk攃[b*M}X\ororEE YfN1!afsl?i[Y4CbgVϢwbvؚ;Ef_&Za }CacsEIfnage(7,F'z :Zo$'褣H[VPpX P2y?^ òĚ؃R/ Vd&Jg_td`3kfm.PH-oݱ!pU}ϻY}Z xE80ow2o'{i5mI٪FUKޒu}+ q4x =VD+9EC@0ëȓM!VɂK`58G W5I*F)mIuc i Z6ELoMu+܀Ng Sxi^FƊ J0Md{ B#r]Ff JiedsBEZfj G'PJM;-w[!̻2IT&y+j>d)a@t_5a>#%B$̅π]KQrXA!&[vlYV2v 12%WݺExu4,px$-!$~4im*g?<\uN.™uA_KƑBtJ0=y2T}oi;e`O nX @z 假ECHƏ_sg)lLŬjN;MσL0JeQa W|ո00-X+~PM׳7nzha?,YShwCiU 鏨LDE`췻]$,_VH$rc"O0o=(iBS')f@~g0v+_  :6/{ mPi|c>wd:.ugqI (N`5 ]ALC RlB2b[#{<#$C6ݷ.XĤЈ rQ%g/>y7zbM,3Z9I ?|>D|ˡ:`y8e׀@Of d) Ƹ`)TPp0oGD~R J7bukbU| y܄fDuQMԹC`Rq}UlI."}$?рTŮRюPs|'tW^5?:Ƚ3"@1fr<\r#S }B*-lqFL2jP,}[J04] S*7+p3P90R/5<ʫ&(iOߔK &IVV/u {J l$=k#6:D`8'[`=nr@$v8}f% }+->4um%&MQ>h\P$Zb@;X4 \5"jT5[L-&GFc!Z:5{&to%I勰RX1v'jc TCD_7S "Wq74 {iJ54ziD:aIcpC3\.dFg%e`8e}5i ܖ~ln8utvւ/Ӭ썔o_ntxHytb/>.f M\h<*cvr,y?E9`1^ HrIX3qXYTpq?({L\7LCxXa ;ڇ |bZ_ɪlKOfݡъ&dkB&eo9kac/WT$O}=&0.K>Pa}CڸѦjv/omzնc tĤ~/h:avn TR: x/%(N5238I}j^yYz.IQIG<[9rTe$+ "?< [O=m@=r4ꓰa+urS=:&kA,96J ϶lTpØc]w(Q8DY4oMPϗ× Ie5]ZM"(Xpjj V!%jG*qc-,-9X{S6k?4ڇR1q5ts$ Ip"DFi/zwOP77L=3+mI }CܥcIG[Qި =NPܖ9!GA 3wZ1N9 /h[4WTPB0YmVTYekI|0jOQkמRW74:(S[ "3v1ퟸ|m>h],hQ[X4~`+ `茙15#*i}Ge;Ͳ4oo( >yȫ% {u)T]\ l4rŝ݃y%~q<'rH*霎U>գua$#cӏICK ě05Omrz=3,D*yѾfh} Oi۔_4(/;L&,.(' vSg̐4GY&Z(Wi@;v +ŷlGr.I;wT%b#c?0.qȲLQozxAk:^9 ĎF/e93ܴwgxxS(Ʒ$ہ@^ xtW8:UyTdNPhΧnv3MG UgrJ<Bu,_1Q`~KK-ۑோr#8 DjsagV lvHO`3a7p*+M#KVb4'ݼYzyޟtOA"ӀaTFBQ+n.ċ[; r1e^2Y?&"jb5Q+'שHY82CDݬ~.J>J\V9ӟC"Nu}كI.7{߇;sbt_2d=s\Ld@}'#mT0;,聭L %ƊN2sQ<ѧWE}Q}|չ@l CKW*cABfX=Ha]D)JM.e3ٱm{o3C)F79fX[Eu)跟a)50kHE?E>(LЁ\\6Qbc<`68Y7X97ȢE.R8 K?RMsӘ}(|B'[@7J+e21:4=A&y䞮H5>ܖ6EؒqFi؊0C#{z]<Te58!Kaqt炰MoϏpVW+ UkQýd+ A ?WN#3EZ+M"oTW<)g-i8%kE-3nai쉌3L3!~t|`&{Nw=YZEɄ@L&n*PxU_aao&=:gz_j}Imz!1MJߒ 䪜aW\Ҿ n('ܤJ!BV)Q< @i9↶l[Ȼ́ QD!7ip@?} m˹x`[NOZ4?*؛"T ۫x̜wKQtGpSBS`ņI225 XX~ً!cOwLh$(Ca$޲onV:*~iܜVp ޾@eGiFy¯b}׹K kE)\7Q\9}=Z0]6ltQ3`=qX12( Im3{MGX C1-ҍ2UɵIvԚtW.-?vl,4HЀ[#ŝiֹ͎ؼ&`yn։h׀aa|%5HY3x|rɷ^. >iEk_yŋ^0ު$lD|J]u~O}!(V ꬂ3ꞈBtG/?R}QW/L_-} %z寧12(`'PدU+͌Y(HN =NQh^6֚mrNkAZ_dKVv. &WQ@0z纒hg 0ʮh 3|>`=31i1_?Or"U-hQ&U;Gjnnla}]!;=8͑_P ҿo!-Ӡ_L;xIRWs?*<d*'*yۙ:gM"~M9D)X;kP*.֊u %dɜVp4ϛuK ]geBIRAM~./8 *C{qhw׈GFxxhfaX\x7P]k)/0~ʝ|oX -2>~>&IszS#+)dӶR&ϊ52]0HP!N%*4\=i=ަ8MTkH DD(Rv<^ʫUkU9I MQy3#1kY:|g _v%8bꀓ(tu e $XJ߫EiI  1@1?US0GX]i5;ԧb V̭a)Q2hK#\ NwɋzG\cWV/[eyjt>X#pq'Kn8c4 *, .5NtKg10ʅv>:fJN8 ^lꔏxQ뎉jNr⇊/eBZAX|/,AU[Sʢߡbա懥\%gWs9WEExxbF|Kȧ,AT% 1= O]UwhqQFR1>DR_5됇nշ-WW}2Ո %|OۍX?FZyů=sC{9̵8ZJFkEvn\h [&؂{Yl_DcJPt&ͦWLz, sߺ?Bab\~,땎G"tUaThd+CSoeǎ)n$rJ֦.'w[n5[raY}/b#ٝ.I *饜=nwfՙSw1j׎YT Rwэn=-z9|No! Pa(Axzզ x ]nT&hY"Bƫza:.j"T=W xzeK i =v}dS_Ӟ>7)ѓy-ViepF\Fd{s?6XM N?s|>[N}hh.B^,l\ )3΀AJ9v!A,/ބ˱#+dk[T=cΥc 1'wزPdE߸d:IfT8kAJt4G-yFj*̳K2=K)x]}O9ƿn׌UiQE@۾#uɲ3VfoÄs>&IN.LY7X0V.R[#DY#%?oiFgP<&l&`>B!~6dk]Kl^CZ&=,Sx$ VCB|RU}f $I'Ȍ&+v#=j6qҾ#Syt>_^hXyt\_R7qފnLt--Kqyҧ[^JZ7}^y:5?֣* =ʡBƚM ݧ͕YMq3S%Ruo?W|JdBE<}Y:V׀ILJ~m%Yu5s+ͭXqCMZBZT+LVHE2D*U#7Z"61!KϥJcgEHbSNХmqDd<<id\uj5woAz {x_\Ԭ7hTo=ָ@KQ-Lgc]Ƀ PAlن ɤJSf܂61t. _ "SQ VL ֝HZe2vJ:k+aAҔ}6P X]bԠR*Mj>fq( Xl:5ح#:Ptk~!`%J`\54z52f:OGV#frxC$ ֕mfv C1+.:[S)u\t=Bwx+\UExƒ =[2][`aYpMa,rQmp "\KmVe>PlKMepQ$9F]Qp*,á|Meq "3|VНQx1mJs{1bIA;8z*z!U<_Ou)l(KvѻfʒY4,tBl[7 O n=MeJA؝JtCm:(HըNO>ƙ@̡3giP(6H3sZLu¡|8.g";P(R{eyѰ2+nX3.q29օIz8=5o@sL͢?chS/[ ޺jIZ A)T5"4 -4w5v)P-y@8jo=QO:YK)WA LSy^ ;i!4 Mw4d9lq=K&p+5q%ⰾ6 j\A!Q*\GqJ;mbxo=ڶ.#/3!pvuxH*PUobyc?#% c SmU)2;Dn;'z["ۨ֟9]ћhOH9@Ň wh:769.{Y9~Bz{r=B8!m@_skjXgA0b۹-!Fc,! |yfa#Zh@TLp*W*2˧qƔ>e4kUZ!_ύj ]*YĖ]l{O IA2hl`>$iUK !6%_;(v6vnE&&3ݡU\dHS,W[$aq k2q7j}*16t \%xמ,H f ‘&e$؋:?'*Y7C#c[!pn^\|9f]3Ґ1]Oǰxm( vuyd+Glud[2zy5 ...Yirfp 3|*h+ $dYЕIwa`d}MAx^^gGåǨr#D^Ms /P`6ЅLDž!z]eǕ1 ,MaK/pWs}Za9AAn#d>ʳm`:F}>I77Rn Y f70=;9O_֌\B| D"{JtMi%N#bDP5 _`dfYIkf&\u(+78N|&lLfG6^0 D]טkڜe}Mډf7@"ik~ԃʏK(7("+_w,{ɿT, Ne'PN"RU>lQ}}օ.?@zG[>*&ܧ$G,r9#Od2-{`qĘI53-#eW\bZ;(, oEdGӣq3X,PWeg02 ՍR5 b|2O3^၅nLb (3NIoe%?B.kL.#!hk%%cG9:RE_$gؐ*n5.*hu2d).&VqFʤ\cAf*B86 G3;D >Kb۔q=JIaqOKUcUmʚi_ BIM@*!O5)G]tNA.fx:M$Ҷ䫻VoOeDh͓ƴH1"/Sah><'ٻ֪2'a3Zgxx"5J5錿t,<``"%( {ڌsvz^USDϊ.גyQ%q63Eoyчmdd8$Vx?]ERuۍfٜ/ƣSMhxIaXDD([D4Gy@Z-a98aFf%l"łFGuV˜MH۔?5S3mAkNʩzOe8zd`' ^@khJK߷mU50Oi342vcJZkؓ$YdKՋsPFƅ-23ECuL^m0xDwI&lv,(hd56W̗nTES -%B5Z?fAzW}cz8Gy1uZ4[Ӭ$/xNt.#o is0YN۹C`R[.CL$4|ۨ+p3#fnc7;9% }X@ysܱDԠKB[@ 8u^`&:}vHrY<ŔRU;1#әgܖgw-Qe` cB=H[rtzD2ɧq&cK:6uWS^=݌@Ao}} u=*!WaqkAGDžuM/%`-r]T"$HqR>ʄя]ҞPU qdZՎL}y]o GVgWњpPе$ (clKC$(r}Þ?H?)x|'>y~!k>)|)Lzt9*Vx;;8&a-\$褏R %S>)\Vf+)dS=ۦi )N%]l4ךגvv!zDt~2؎iƯ vڀݚ@;1RiYtCc0g A6PsyդbPEf;* nH᣺Qy켣ψW]ˇLQDzbh5 gZqNc=wxyK숬ݦrĀ}]<K#8[m[S_R}l%jCr,je\CFEϾ69l !#ǰey#DcuݜkԖP1 BueHub`|pjUh`'yߎA:5*\MRa%m&)wfM18t+, ߣQC#*& Pd>2L/#Pgo$ð3m ԕbCWX _ZTX||1'LRWtF(Ϟ"} ['2JzX, Ǝ$AoiɰFֻ‘!1#> Vn }H}r ` |ltLB4W&)8jL5R wDX"r% !P@GTab a8]$t)2|߲< ]o]{ 'H} Ŭp' lM3# GF\ PH(s:_28G35ߙ7955Q G:ul9q[9 =s[rZI>0>3 mbo1|xn Sau;66|4·WrDg60]nߔJ4{ B*rXRJϩZa-–psLzhIf!DDX 0y3㱬WMh ᑌ1µ#}A Ytxx&s:<Z0ޤm تEr6S6NǟPlcXJLmj,zⓘP?pZfIc${\6- sąP+RV|Ibi|Rki+S:.#K+TΟdc ;UTBc+W!ǤǪ2i%fuXAȧ'.n1"ky @xɮN-U ]f` Qxdt*S`%Aֽ ۮbxg°Ri2,$;0~[j7|0[}$)a=-ʷ Ż+b]NbI6$gu{ 3kx[ICX7^[9{7<ER4V玸i Hmy NB׋=Bb;BdL؏Nb]x͊ϗ Pp?}pJgss2HY?պUZ? C2a:f^& av*m@Hzx]Q$Oa ѳmTn 5zj놿#0nj]_~m`DjOy>MAtB[nnHK*1 W6(!&3,7AA@<%~`~]6gޑ U灤&l! T4LACML ڨmo.2%uű5%V?T>q}ZۅW5͔'Rz';Gu"Z1ɓ;e; 8^C-5}~Fss̽m "e_[69瀏cbB=='-' kf.+=c8FDyG%S/隔x;Mxwr5ʚ҈ 6Eo0$( "g֨en{?Q P*tլi:Q/f4Aq#3Nɔg#$!MƝn :#=e>Ur*;kWp3sA6~/=MA.Wc&coPu '9֫_0$ffF.tKC dBhm5nc,P96hDt M/G) `OQ6.kү''*Yt57"{lDq&)1$ӁIo H oomL1,vMlT$w=I0+*`fyt_?h ͦzNfe4[XkkvY8s׫{VAS {̦"Ӎz:>h;-4-B ^ƀɽeIqϨԑ+yh`jecL,љ8osvjSAw-=xa(2/r!jGs^ט.7* 1.b|qT_#0L%6Zy{g2}Tnݵ*eEQm=YfVo?J t\p "ke1T5 6`BBV_U]MZUO,tTӇ魙j @}  Xn#"-+D^voc6 x]jLsU@}&HSz U2ˋg74zDº)uPp:8:bSG*q8LhۮpZ506g.1}m:0CF#fvy.?f?rh &pW/%|_a5> PQI8BX4lζ5{sx?vׂʻ\[`|Ĵ2K\GEoK:!~ɨjcovuooXB. dEo5#Od(w| u.3 OLXݢX 5ӐVb3=ŋY׿1((-la s!էGS"?婭$gs/OL7(EoPTE!GbgdM@^!Ok*}&ن1.|zMYJcgcA Bߢ ON|:[qgӀ9>rZ/Y.@ag ڗ2dS~eDv\2U=m%jʙpIc:W%3| \$YV:ʮd-N\ ^g3RN_(2BElãc%Cg!4*wJW^ DbK<̥tmت´NŖ2+г F1t:m]B(W:W;&!G83K l)Aa ~? 4ܰaj@(\\j.gN]f䳩$ƿsڨ3O&od{Ѩ_cAVNd M4WrUqB=s~"0sX3Gǽr@"9fP/%qOk0}c1h_kU+1dI x,k.PV<I%s2c /q#> {8M]&.aQ }?>-I𢋫ZTK9/l9!S|466I׮MGS^-Lgٹݛ ܣF'fz7tiMF,*@<n__& ,!)+:NRD#coO(wL~Zċ` $eh ab㹽p9KdU=tJ"5vEdFC+]f^8zL> /+FTƃP"PL*&^Ty.XO՜ܪ֊έjN,n̅;j[&ο-Bs5 oMlePsŸG{*s갲UXlyܑX: vɵna_o6.<Q 9gϒ+^y[CƐ|9q5{U:8ٹyxӀ-D#p|7S|HkI\ 8U)4)~oopfv7j&aAirJx7FX߿XmnEGϡC:vTyG-HQ~Ѳ..`bt&"Njcm37UN:OhAbĻ_W|A=h'CѲޝX9p<~X͡G,G4Q}Wʪ y<ަs6sJM-϶-5)ʩ`ӗ8.\=79eE}imU{٣jwS ?NZ&\-mJ;N#Ǖ ` O\Wn®]"<io9o̪鶉 vVPH /`du+UW># ^~n);j Cm%6>*hnn\( jʞMu ‰4I -,UM{iEv!XY8*a1N_+# ZsBg| A7o.*}"F*wuH8>fߗ-.V:ϰڅe51O6$yðk@|!v{Meފ#`vU0yq xIw 5_S&k>j+hCg·,Nl Zj ̲an(Gyj"K>^8,wT T1d`?O;9-/Crmj# Ld𛩸s]?KLt'gۋ|+㋗Ke:JTǏB4Z`ɪ~XaS$M]5`A*DRR_3Y6r&p1e2qIܯd 2bFB@s~z/{0 OGnFD[eȺA wv!}ki=K`A(_h`H\aw)|Ji`꺫KbUu1P =~ʀ35ںdI%iN|Mk} (7I#Ca֣Զ, X`x>E~`?DJc 1؄HΗB[0#D,|LaD{eŹmy)jDClH)s|*C SWA,c7xkgMz rbsރ>D^AZ!_(n&6 Euh[L9dPX[% FJ[#nizWsJ|TEJ #jܸز$l&o63})-uqwkAw/ľ`lJ[^kFUr39'hf\>_Q^\݈kvJuT_r:wL GA2f:ۍV+(u8ZD!:TJٟ&% ɔ_#kbEr B0yfKZݛFbᛟͲ7uM`u0ەI gRie'#6IqFN%6ZA0!m $htݬ /Dަ+L8&xƐ&E U5ŇLU@#D2,ќf׷pzH贡-;Yx/x-;V?cQе˛%]RND/] tĢOոz6kGA16A]pZIԢsIB`w~ؐYK~~8vyf'jFqmɎr+*#I)xXi6)iCHPzz_Q(?G"\]tn!hRD(c)fx_!`qRſoUWz?Q27 7} ۜk⤀ٓC^4 00hٞ *5}:F0ā'<ֱ*q9 Fth"G^z}cl<þo=#7޽׉B}5*0qEW5Kׄ7 U@J4wM*'596uhi5gC'}\.|WrмbRB|>G W[2ߏQ^EJ(a l*e? ;CL$kkK 俑;:Ph=,3 ڔxokc/)]D˴3C,{ }đY! QA|sƤ?s /qz쭤)I7":,[m|3qcAS}86Dm+mV2~%3~\m]yLhA(x(^BLlޡ+ANEWV饒0/̆2"v s/(ǂz[( 6p-RN z? wjLwTX1.;}$~ 1^-Q/q쏲96pP󠐒LPz}]07$߱Wo3GVGf"[s΃C$ZoA|;*=EF6WR.Cv?Wb_jȞHluuwmʱq"?ډ1PV xʈ@_バq'a~HGٽ Ć82Td^ր[P'+HXjElܑtG1A; 6mQkV{L{+2onN}Йy zkZ8!4Ls5ռ/eڪm5U%SJI1~`\'IaF47惮oD}#H3'D6ц_C&lSaYrm۩`Gʊٮ [I+M3M9y ׽&YV} c[`A$f: m dsqGI>A7)>*8p vWOavCT]/r3kITbCuw @]:yLX:H߀dPG ˂C&< }u 1y|8["v4%2ۣYKўM }^(Q#$ojݯ7:h-n`ޝ2ug4E(m`` P |}1ȫ]^]lTjAUh–{i_(g|?/ |CkTGv\-0"X/bYo~& -^*+Xwj#ן0K_ڽӰHWj"=M=ƶqՆdUHfi1i?P܃oSpB(<Þ [J?j%0= WKtWJpL~ww|-vgs[tڈ+eya+u0g (lư-"ӽ 3NJs[Nne(QeC`c];{Ή{f+xil5"=v g\ 0ro^eЮ@ _mdߕ9HPB^4j^?=Wa$ˊ1$=n󂿯pWdZB35m P/[.eK{3tq-/}/- wИ!P4rVTjM6m/;Dk0on|湩qnj_)G^MCTd[gAgH*Cϟp*7m<ҘR-'[sjL(?o(zǓfxpKXp۩0h! O,&.m}3 4-dsR =|%Au z'UM=)\. n7LKfv]"=efʍ;)gUrc!wA.>ц !e(OkIx*dnue.~5pA$ pCH Ǿ#Kcy¾gK(5cƄ]2R*Q+O@m.K~kTD۩-t-oVA?5hMabA?}ɘAӥ_#QZA*JmnI} lJ(BRO&k#q:%g?g: 2!<ݎ7U}Gv8}8^gNCb6-HrqZf}\0(#KD9;0P[NH8QȮ$({!ly󦵞}C`vg +K*Yn$_XtUe8`g*+s脥*C53pF`a`k,sZwyIrj'; ԥo +_øi@FW B?O8{Ekg;udb%u%h%;ݓ}ҖFZ߲ =yYS& ?!aW!2ل6 OASN͸-bB"'G@YCIP+L\T!j]BT~d\F:3ĔCߩCRI+O3 (C-orgUR2 `^b-{S**L7|~HxoVP/tO:e1eY[%X\ ,\6'HᥓIt%.SZ8kX&ru_0nXuo; 3)u}Qs,$%Ł>%Uš&V81doEl:?kNAQ\x &vnb)pqhJ^RXdUAQ徏ojJu{$źz0"y][!\Sč9xZZ]b񪄬 *0 qQf$inXloK|N\XbM;1'1PΉ優:ׁx.XE-(-[jO":0gX\&702RgJ^킀,E,5;Hc}(^"2l3OgML0r^(2@68E7DpeNGBGpS#/7ڞO/X %,Sىy5.jDv9q4BG* )Ix$X88ƟƕaC2zܰ(q'6YFʧާBTO7wګ߯cͪ&vfoIT_'u$^=?vexY9 v=4"uVd-eq:1A sv/Tg5EP\֏BP%;P>s!mZ=[!i֤"L} 7l!6*%B1ĻyFcdPY[W(Qnk%`ig*-Bu5r@jRv%9n ߻MCh2} +~.l+:FqoneMb':+! oфuYxƟfW8CP7!-VNY:ʹ+40zڟ3pzԕzRV?>l,=֮U;֢] ;#Z08kG~mi2 m4\P!+qEЫk s%Āg8'92%e_ֺ!/=FH~!`eO=58RAJhz[𬊊dy/j*V1aB%{p0)-(D(o-cV-920BBdowuD;g[ELSIOk:$.O!߯N V2td~Ne%Y.!~ O:_0\*v'}8Ҿɣ^ci!LROOCi"OJ0'™ԟV>xPfw:A)2p K;U^]dM$tSs^vÌ1$,ݭG̘r. RN R%Lm%&,s5' @ g>I?%NYޙ EJ۬Ͱ֋=-;nÄOU κ;n}Jߗ=Y `y1/zN릱&)t^^-JF2]+9DPnw> CK-$'y0 ϵpAM8XL}Sǎ}ojBgI[㟄ưXuH`M+)wX/d8y~Ng-Fu1?T2pv/3gqM՚ϴ9쭻Td܂ ~''ra{膾§C5E$BFNF$>s:Y"'2yeZF>'FbƱu?]#u<mHL&·@n? -4ަ8,Ik1-:HZ¢1RsA[aQx-4P$n*/#'] YL H:Q";v~PgzM 0 *>vO3Խ#mLAfZQAŚڶ/ Sc*լqCbKxUoSi~x}@&1,8ZV <7sGnCUB 2LQžς~vB.Z/ӄ-0 â6#boIkFJdp] a'4[ cq_ k}G'8`U/ua *HGMQ6T3H{pXVp CnM7?m}]oGXIDkUo\k^뺇P~4?. P%3kn^;ra?&"b' X:UFQגAV[!yiBmH% V-!rnbW6&TJ:l4"ml;V%n÷`ɲ: ȧ lYmXnWڠʕffZ9a7eh^3>O\IV/W꿬Ȏ 瀺Dיk\>-Fʤ#-*2@3R;= KĐyE̢gB["Ow>JLH !έ0J:P[ H:; Fϡ !bソnzôފcz!ӟhz9f^2 =hӕ@fj䣒֟jNǸXn) lKVymγBu8ô6z~X5/ Y[,k%&nFj$ԙ ~fҘG0?#'=YҁJv)V5wCפ$Arp#tKu$52glku}{Nl2aϜio|t}mIłv&-i6~ N6&rw}VC2Ò YbI UrYm3cbڧ uMⵘUba!RYÕ.P茱$n^@zbR< UaҦך mmU$,4kt N̆M3eGɦSW*i]\[X?\^3T]\ەbW i4*ѳBV:aH 7e F9Ph8glLpa{C qu'Q?A+ѕ3 ^D`1%SK%z`e`((qcΥ(6A3?PlW0ߋ3 ~ӘTA#7 | xɈLbm%B6v E USP6M?v! hiV{ pz㴗 -y(UN,ӥ{C*Q_׻a^FǸO+:2 BU\Q2&o@{~9'yq6\IBXCAb7q0~š)j%0m)'-VB㺊u5[b5nY^t!yAUqҵ*x6M N'ρ7rQ=t@2gV0SZ/@)۾CgֳXK5nUH3g&eƝ~ n+aQW`)j|{D g9 qIH*4 $6 ՓFZ OH;*b`dty ﱣz_'@r"C;~+ӕ07TqԫQ:(JB=`w~&;q$-f՜#2H`%Jv[h^31aPi,3V`B@ŶeHr# NJԺ$sօ+y/v 1%rʒϻ2L<|}'A4 YrK!-@QHѬ}Le;ZƭG@V*ȔXc#sPo|?uakt^-?fݔTXe!2_NlfS0 Eu؈dQKo6:E?n:iѩkHko~a]9=3UvIUYSmkzH3LG265߽e ~\pxd4A;s(D''g= iE*C=gl]FRTE >VY$?t!v'v-tst5KSF`#Gj}@]/g(Yb[ КyXM_tɻSͪݻy*SC'gWmc\]k'X t9"w?*MR@dbǢ8JBBs҇1ʕ3_h.Htu%%\VKHB`thfm DG7d]/9=(؟nfxONO[~CL{Nȏ )pI';^jTI%K l՝vYkl;( GFZ$}<61)XQRIDnDI&oTuΨ)V0tP1'Kl|soTHy[1?.nYh9gw#@zC]`QS+XGBYFz wydy:^tr (tYp^PG>z2TQ2RN%'zD/N(lk?K/3bwbTRLI3l$vuU>Q '!3]tf &~?g_@7mf?O֍S 74s|KC"a"y@թq4f!5l _蔅 =M{.&M.UhU]݀=s]hʢ9IDj)' |!mHÏ8㱨:s"p2"p SPy f9=3]HV3= ?}Ҭ"ZIԘ>7ΤWsSj-.r YR\ 32ifq#?2T1Cwl/r8 "eMY]j Y8=h}VLғ ‘!D-޼++`k)36w ƻ j} l nQz U-ݭD cyd61`vj19fk#Cc%A3k2 QרzSM-(x`u \K2K$ FFo>'LX^:cSHreKTZL6 P1hX j7T#yK>$cC`8x#S3_ɛ>YHGSԟťN,fqrSy+e~'> @k!cPGiScǗ(8:K1@YQjVQIÄf6f3(ue6:g݀joۉLsjBLͲ(?8fxC]h5)Db꾔J[\PI|3wIbnVQ=xunI3Op9y^AhGxJr5_>ռW6Bqk,w5?0ZO=E)C70< _]^ɩTŪbDm=4+˗sFR t endstream endobj 67 0 obj 241024 endobj 68 0 obj << /Length 69 0 R /Filter /FlateDecode >> stream i=^hDPX`\O`ѯ8l 0S-W4G?uM;T%/H&gِ"}(3.woz.t j@YZT|tj"Lx^y}߄@`~ 5/C1%Vw 'J.?`"@SE930Xv*H*x+5J6> tl1PiR랇GCU 3!wJ/4fnVVjT[ƹA?M} *9%p 43 Ywm6(@֑$2c@#OSoq*1H!D a$BoT&fh 9$!sd|T듟Ϡ}J"%z|xp/(`ǕggNpޙf MXl*fǡY}4M5}]H^ e4]6顿@zlmET'?v*m|LDwP- m02 1)YGD ۆ_ū3ޏ[)x5%n+,y`1A~o Y%ϿQl)_yjJ:-+H_Xa>]ф%ß$4W'$ASyyS[2ణkVv'ɕGխƖ(& AJfiAHDfzL^B#NYhE:EF6YڞejĨ˔^U0X n.J<ӟ.iI$IG)}kVngd+ȇDPTYKE|1[zD9ioSuAW|,p^6=Olc&/X:Évj@g֒ذ\DWlNwSѧ!zbS/ӿ9g@Iy0]"~o m[<_ .N3y+w s\@GJ78؜>A;eWo9dCDo3'Fiz؏g3MW_b(l 3dW0n{╠ZэGd;(T<r1PĖV"9F =&`7ɭn09F/R{/7Ϻ>Kz} endstream endobj 69 0 obj 1216 endobj 70 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 68 0 R >> endobj 71 0 obj << /Name /Im8 /Type /XObject /Length 72 0 R /Filter /FlateDecode /Subtype /Image /Width 3000 /Height 1600 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream -h *CJXqO)Mҡ×Èm(&! gJȪd,LեJā!ZڶR@I0B@}2A䘓PϜ Ywd;l_]aV-1{mEŜ1IdBX@)&uNjLBC7J).STK%.JOfNHZ*nL%{s&->E_+\_Aye~'ӄ,2]ji~rd?fEIӺ OP/"J{O1 7Y\>t^Cs˹0!au7F6 LTdR 6~Tw$˝iXP&{9nhѱJ]G#ɻ #sr`FIR;6OG{eZS\a+?zL.!?ܤ#9@ eġTT܂I5ӽik 34)13<wjbud=g%$٠Oh7!duW32Ȟ]u7׃ # G*q dmX&?۳Zr Ee0C~N+SR县d(_{ Vlqg$єU“5Qsb1ՈX[UcRug#9 `& C1:"~z- I'͵m+qZAfj$J^%Ni%+^"l0&i`܇Y̜C|t* d.]0r(&Uc@c;Dngy[F13$pb5n{&5Vg#qIOWWtFƂ+٥{7~#sF;WĜFӠ!&vH/O?q3 f,3䡧WZ=vR׽M`6Nd/hY`Ɵtv孡ݙӿ{>&GEk2f&%o\k6|u̡űo/ 4 8hC7TE:ևvvXCl9zp|MOlA UejHO`NVN(F_O q]YMO׵زP-6zh> 4S:kQcMeSembӻxfXNB'>igLgi/Zf'4+jLc[#q;Ƭ0vifE ]Ax0j[Vf+ve_W}o{$JNĭ'D:=宲,.K\+*swV3}ߥ~m{1kW lvC\!Jx_⾧e$}x^yԫN4 Ha?VloE]M  H!{1*ȳyo8-:ܸWhh⻇X3ǘcFߣXj.oga}%'w8n4' Վ,Ցƅ8+!aP={}Of6.kbW-P*ӡJ47PܹfݙNH GTj&Il%s ϕ.]nz/OO%˛8Xnr&k$|½kY-b5a|tVæ_*rVExV+סˍF"&eͥ܏`#N!&>neO( 8:!F9w[^[KHK J^C>tI{k60Erbt>~=h%Q2Dk amD&+\⺓GU/=e8l-u+s%viK\QLhB05ez*muui(X$f"E8p1RGǫ% 2WN/V t $ƪ|JO+?9JlC% { (,[ jhn?r6^ē (Q`A@d<씛Y=j2ѯ2zt"ܹm5EHOl08~`RC GD.%>U;ϗcg;y_QMȱ]8êM:_"enAs(ݽr(iItƂF| ?U)q+s0YFk. w5^gIqZ+_xKp2!wk:+Hw^wcO84cp\Ey<:a鏷FE NuVC{6RBƗ8f x{gNz\ GYN{KJY3C#y]vZ.u5#\aQ/4+6$7:!Oآ3Oa|*}A;_)x"Kh8NOQZP;'LtWo8HZh? ԣ2Ժ.S܉JUR\iưMK# 8Ƿ09^,{CgiHK ט\98s%2AqYLW:(VE7 |)$F9f-GPy2^ nz0>:CCƼuLɈaGU=eۉt-xc$bCMQ (do8e;QƵ]ېr-m)^sAgU;.)kϩB8v3rЫsvt7yH@Qs,fΗE9qzcFH D[& HoXJ)B3פ Wak{Q G]~T; 3e}=hLz8g)x}R9Qc׸Yke ]k4i}=wR%X*:HjIտ^`V-Xѻc4EVaуq 7'a?0ψ` [q N>d!y^OwJP9-lud(RK5cg~,b"L QoQn-$qL_]D[~3-qF9?9Q:='qҋHWr!uOK+ g2IK?dd1F#s*s $-lRC}@:}=!&u ib2'4$GoICȦ,{‘U{݄v'9+`kf/pFH\X<]X'Zf| !ri4|ႝnm@f>%|1yЭ)4{#m j&Чr9c̣T~wnt%Ѱe Ȣ|)R6z% ƥq._~!ijK W brM/>EbV8,ζGޠ,AfO!gcQӫϲdUȿ8L9& BٸM<^aQmU:}lZU *JQ } GYSepkL蝻VL(U`,.S,L>&F츦k$cOJG0 Q+UBsE#V[#mK'+6Ur XVRUkY).^HA6!/ؖM?b2&swM5& N(%;iu ,}xudr zquj*G/y@7 U@ޝV@FgLn;c^5 Fo#AWcy潔/WQk |e Ny-oȡ)Q-!E[Qba(PryAz*ق)-1ٛ{&kW&^~]0Y)jl;vWZOަ)`.MK2^qA0I/'D@B@PfNЂ2h'Le,ȠgW꠲wpbwPV9GHW} ШhGiLyb`M%wFaxj13$?W#ǿ2#'ʱ0na@Qpߌm`U$bkQ#[CKʌ9|8ڻ }z;TCyN<0fdC)n&mu+|H`; )ɢwكJ%i> pC2s_?zv8m9T%Usk?)أ-!-),[84#,磌fUs瞙|j",>q!bX*7b.6{glOeɶ O):UEU0UbNGxתVT@JZ ؚؤDvxڙ!.@_j†*q#Pe`uy}nMWn8-,رUNhT >Kah6( y] hNIZ`'A~Yk[%K)s9H#_rD]sc|mr'@dS%3y喇ȫӖY]9i6 q/6S^p[G j|)9.j77(̽E`,I ^|^E_󹀘n=ѐl{>ŁH'ZeԴGvaN![ /~5UMMB\BT49*w(|h^pLezINZr**8_&FF*;y**}7x0'!޲m?Y4Pڂgo!PQ2ǰ'<h.ցD-v_L5+dNn?_7N3KDJd㢈>m@(g EP^T&)ޫGj [sfI&]P8)L&X8Ҹʡ='dc Z!sxQg';R[gQcMw=]z4 Il *ț"}vluZGrsR1L⨌)\+D{XiDvJ0e%D_u6ϊ ؞D4;1DجDy5TYK=ң]5XhyM @c1W*%` SNQ[_v6|;=f-5Bw<\E0lv(^]#rH@8O=J'<8&' 6y%͗Ʈ[. {|_ٜT3^ؔLEt *8RiMmw0I=f^#iX\{ׁs7#1G4  >*w3$/De_< bsi=|I^[OŖ5v@qP ڞ5YT-n@w!H5+ݯj,*%{7\7+ jG><_[gyXŀ,<}p1NL8F5g!!x u:01%o:T]og1 [ -w\J(Or1 BbEa)#Rhqgl6>ѱCI^OӐ)Or3'},*`?R 'Ϛ' ~Ov Vq/6C_hnA>nwxcVy)D_.Γ-U!]K v0Wi 2H;sg"SՓ'%tB?$⻯9fkԺyMBH+<}`N7 FWaBh/$Ð+UJ"lmy H{kd9p0jw+ R+Fyoڑ7sX؝>B+C=MGCg"b,OPz.{HwZ,'J P&Ǯ8#k}w䰕SfJ,n>$"hu+-V%_n蓎"|ք )& ^Τ?U0{dVT=D f* 1Vu }SQba "sEQ5i)`39xxq7 {_fߒӏ+ 7}-\Pi<} , (f]ܸ{t_<R̹Z~jkئibO8W1hv=iӚGn{v^2eE˅^ :ٿH\j~]DX7my#hD"~`CՊ &܋>t+v6b`|2mhV%{sk;h48櫛11 8VHz_E"KjzYC&I<>1X'g~%?dJfe< ER.6Dm?`c ^gR0/_zѰDx5D.v1Yjzf!rQəϢ'* d u RďX$sN?MU#E\ ͋ZJFV:IXjڣ0VAקB]3M b|Sw-xv0rZ,S/Wo v=83 kd_G8?@@ɃS%v [1ۺ0;?^wg:2Gppp$UAĚrc& ijec?#, Z:xO) *Cq )r.XcZGK;bLNģX5U'ځv?k}> *s-y4\zW[\+w'm,hx|U1e~ZKCŃ#6Њ厱;ߑȊ~W_ ;c@׌߿U7oGQt>)U^`]fggn_E<ƳrBkqoPnjP{>u(חvP ׎A;qWCa5=Y6#oCl7s-9hsXM H^lWT7syR;:W, ʐ7ʓْ s*lǦvR , F/>gQ.=Dte~111RYyT@w}D\J4P h˶5NOO8cg<[IJ#KsPW+@wm ꒺4^p)2$j~ #pJf4SCu\*חdGC3CJn (wP$#C \m݆ ]`4+C0ʷ/4# O:|>#[Hv"9$'/]HL*\9-^^<#Oɠ0d߀lNNBya62ޖg5 [яO# 36Br~QF D2 {4VHauNocQ"i'}0k? Q7Jq/!=LN|!z*͐T)'dڙC6"^Y1ɝ.P+Z}R٣4ɠ~GnǂDG6@ זI`+p b8#jT$Og&% oqI)mNŽ>; M!S[ɧ}KU`xd_"(:~Q)n taɑk紊c}?O{W63N2߉Va!ZTnx֧6Gw( $s n 662!v` 7Zt!]!c#0<+މ?0i}qM0Ӣ'V5Ubi00X'2(d*C7JGr{ݑp/^506n/,y+1![U΢Jk3=0a"M˺>ֶNm2e[E :n[pmL" Ry$q# '۬+:C!bbjzm%1v騿GVh$T#\1 ݰ]{n'&X^gXCJժP UW?2n{Elڃ 64̖s@iؒL?=/V"(5(&=IdGx44֕k i(ks `7R"[t|2b9iR$'U -ڴL,J/]I=):_~ j:LB؇?}~hiYb[Y.F`4?dZ9_e frWιG0z"/{Xl&*⡜ σ=TM$VQ(Vc (t$Zc@*YE1?A 䈍Fna'dj8$ +X|-%S<^>)R| &'TgjX_]&b·+Qf{hq&Ƥ8Q*&VgGp:m(GQ'T>؊ÊQS^#by2$H-gٞ~E=it}@Țgh>]K73-c^fLCLXX] pXdT6kօ%3Ã%@j\x8r2K>Hg`Ot!u@>ˎ6uHɰrp-qtU#Tbl [L~]DJh$d*]םv,vjp% FTsOOR=Ymu(\KhV}9t63c(O:E:]}p-eAF#BwHBTHxg B `Z6P1‡Dhȇjv MRVT[e|{z˲'[k })Vj/ehqǬLNrǤb݁]{y*!nk o\o51U譪D]<$/O2>8Jy)k,PUsܢ1')lۥ!b TFA5R#@@e_RlqS;1ǕEo6P]gPoݾ c5C]~eoMwi!E~˼DIzHئNUk1N㣖Jy!["9̪M!mM"Qȓi /<)dme! #:GfۣI3nFٹOOT7yyQv|+[pl)1]:75V/YTlsC(_Yzp[CNt{姓Ώp)AX_-3L$jZ(H&>9g=ryعxe]pکqHU,KXX[~m4Jk ,&}0L S>@~?+miR⇐o)ЉUEB?bۦe=Yطʲ~z.mi깱/CTFqa6,9Aǃ}iv)ЫPބwqqs &VdaǏv\=('> ګc^jԍADg8;omXO" ZvQ5~IJm ~Fj?1 $S|«2m 4yuw>-YR&Gc$N'9 w F |gI?"G* 挨IZ}Il4i&mE*e‰T~ 4oҒ8N G($%qD"Pabv 7idg.rĽ[ip5]t WnmW27!"u"wU[+i:!ӄux,ޣ!bB02y|lxk8ÿp) @KNf*aҍ6d۶xGr Qv F&]Rm>O|l yV(A_FЛAn)#>IU&U`X֖c|7wSRQ54DrX+tHNа+DsV){`r y&LD#obMTn98N/iP#DyVg[S1Q,)xFԻMOu8-@ǜF)p[ FCjm C#M]Oioٵ(iB X'~~ Y,Gvxpc Bzqi+svÃ_}wNM0k)…`hkF"-fB1bZ5`Ϯ1Lw#gYt*|aPxɷ#>[7\uY {]&Osg MXTuhE B ''ﻉ𱵄ιz+`ҙَuTq3A#ES p'}/ba<2!_p_VlA4I=CP-Wsi!z)}>@AQf SCrJ鬒j |fUBi.ϱHx$o?0 ZX>Ȩ*D4آGg;;pqELЋG9WDQLmDC 0Pp&juVZO7;< K%*ˆèѵ=fэnc2_G"ԓ:^B-;$ \Uxn(f 0`ܵy\O%vLRb!PD SJMh>ZoYǎooM/nGrG,v %Ñ(>PbYD|$c)uX^G2Õ#^ &l|]hfoU[͜/, 녲QeFLksG*T3 $.R$3'ai!OB5`5F ;pW.~ZLwymօ3rɸ)̵Mm䑲yʿ3|!9S9ba|*KC|9v$aMR} c›K_9{s-j8G J[p5H孺bb {م"v Lܪ%I +ܴ=mhiQ"4}ZJ: ս)NBh\7 O%^ZR7u'pyè&KI4S)m|XwSYh)~2̢s5Htse8W[ֻgjL@dph6w{fWs,9xgqmp3nMY" {_IM>m*JZ%J+>{JiA R4 ;*S >*Ñ(xoL2F eMsxm嘠!ljˎ[%&;9bը:{]tŜ22$`Ǎ!]F6\"-0iWfd=tWF[R}P>{NK!H *z䟌EWc uyun +*̲1 vfX 畽$O&\ub[^[#,ߚ@#kwl=-J$U1uZy(О 6?OԷd\WIT:r5u}c͵+-j!vڌ}:`s+ca&ȍ[ ?5@??{/A ?R/OFUo)uV$05{Y&-in^|$exG虲 *KzBȶ= $5ZFqŦE$CfQ|AǶ " ~Ԋrd+畖VrJ(uxYceCk5ʅ:aG.s]Ÿq]).)l{g@`e`܀㹘eh/ߣ1+OMK K3i"yzsxҽi(py#*n!dkH`'¼5m]TC; z.m˹Ci0t NX]{O. ݚQܡ#9^mp۲2;{w:SsMkl)tW֛)i2@[Ei'}Ξ^0K̪&BY@nB@нv.CRw{S:%7X="F(Ktsl UD3`XV)x>o#>S):I…weK4iw!fESe5\PmqDHqb'|IKiڝO&0R TŸ;eV{?І-: .ShT9&Z3U[rizWx?ԕ 9G^sce5v oj5[TwKaV޿ |jQ)ZIO&o4𳚀RfeB&-4;{GC'?MnN3yq:En;y`Py?-Konrr%VI::* xntbD)l Ҽ};볣.[|~ 쮊 1,kiš1/!HHaFMKѐ hv7k ܥ XQmh|{ =f8I1BGDjl%%DSg`z"تR$'8+.ĩ[tO\(p{+w &uiwwzd"}uJJqtڵяKRk)6Tڑ? AZ1{-VH. CE, tE:tWv#{ŤUJ.DUuEJ(l><]\bmkƱ0p[āwG%N{zR/PÔl2d.m+6' 0)~dŽLI Ȟߨ/K{5z6],PcERT;=-Q!JZyt4UCGw$IF@}$: T&}'Zb^7#TrbxEv@]3^6w)?l}Mv7YSzCSG|htuRYVw>3!L&hyPت ԟ ,=} x-V șp4&3}|\N6OgDʻlqǔ!l)ߡw'yf%?ezb=b%Z0bAű)d"9cr07#K^Z{-X }χ`p $xmoPǪْ"HamfD=ˊ@,;GT DחǺ 8Iʪ$9)S}~:w{5GUٵ(b~:g,G%g^Y7_0{g.DW(LOso$Xx⃑˳~=v?c=P[|%fg{(bS&.%'=&|9vDT7_ ^Ec#Fb}%Cσi}YoFeߐI PSBsK"&w4{,؃`LuzF%3ElCTe*TZ2.6=YԗKōV  sL Iv!Q)M ؿa,8`,Q s+X/pJ6v2N4CA1e60c+; G!-T-FFE |(~#[xf n #dn{[PU;]+DfFÝ,HꊲlBgkDЩΝGNQNQkE/ UߤS A~ij@G.@j`5 ƸZ,4@kÅxN~D4Ÿ;9b/}bTRFz#Έk'$XZG9iv!-Cdg7fQô6\ {-iٶ5j$|&{% s|_=e!2g{׹廎#CT1\^ڣIb<|H([ HGkqha/8I5܃;7ݕj퐂ȏO=.f(N@Eo6vw\+܆Ht۬YgdMnᏛDărsWw?GGpz%FQg$gUɼ%vlWPu+ ^} ?)YeAm c GF$B*nXDkcTȮsvI'ҰYE1Y:_\|-VE̮7U}.6!rV{*\-Z^|c;"_8 EmYqsJͤ(w:INt ̹L|_'i ͺaĊ "R{3TN"!-ҵ$6h&{+>ȋT:i< gf1䏡 Cu6cy{Cvo_G]@kX{1+V)߳1Bc4Y50 #2Rܻ6&i Hg(2@{_kԕUZojKȧ9n=^.;LC"փ-*ȀmUUtRp>_-JV=܅ J\ &z+'*t  -3Ĉ/.W(d(JhEZ$?/v 4WUulSV+t]igT8{ +Itygu]gԼ3f\2cҡ,Ňť0{&J)#j5_>%Zs ;ЖM{WR`+Q*^n)PCY9̣{(:npQ)s"zA 'sXvΦaH1̎D.l-\M\qF&@Hd65XpED+rrօ-@_ՌRBȆ aǭ]gl Ok|tK؜_C'<1#{yMQOބJA;ib!2U UX;3{ :v0bGPo,~\ҕpd5,.;o7]RCF"PD0`bZ clkQåm&٘~Tp:{8t$`p'*r"gR4}Q)5CNcΰ%(HON)6m} a{}U{ۏFBo]*l_Lj47j_)Ռ3o*Y* OݨlӦH)ld1 tzvy6D/3X 9uxD.!K!]&]CrҷK]-%']yxٲeHK裸DVȸ\U}jP;!=$ri1O&ׇ(YXrVA<:k# mRi`yimTo-zE?rK #~s9)\9Ie ]0ٽM*D] wޠWӚh" 1fOb%E p8794aLz8:HA,_$~ewdU--ctJ"0)g5efP^m'z i;l—O1 wJ!W 1ac#5!`7qq=FW)kn+_v&S s\}%~ATx8)ވ" ;_*6G0TǏ6siCak[-kȥ똤bj{Nc߳|p*~Bx=%A*P;«..d$?CaRW;4J 9l,iwgfuBm"%mPwBX@e"08)QH< SpʏLW_urױG ibM=FMX]Ş/P!$yH&h-XOwFtڀHT`Ճ錆 |P7-8Lus:*fiFdK%! 5? ҙg*]gJ;̩Z8gfG+7 ]'fxΆ% n-U=lK5`^9W;ֱǧؐ_wXhoq,b ?@✯=pe(.Ұi?cw6*?oT9i1Dn fjM B}.f k_z#m}fAn4r{}".L R^<%+GߵX Elߘ^tSh|TE /[{>EfJe!ל-x) DZkWxVVG(1lzJ9f]X.;ЪU)-YَBWcgOr[' nͱ sPucE]j',R*jN2,=k7x YʄR2S e;)ڂ5 y-Z6ϋhm9[RBMw0rr(LqVT,Qgd%E k4rT=IEz4,gXK/rL-PuGs7L;q9_M ry0RȔ'u"AKSȳEfa1ngdo3S^`UZY}hBtI!묢(z] kήL/}񛥊fcz.5evq*p2W$ xe J٣1^%!@VEІ*2MfaHϋ᳧V W0І r Z&-@4 %.Njw!` !0Y'?;P䀲:`%Ѿ|fKCpsyt)4}Øv-ipn5*|陋ahzA Hp1imV. gQhlƬ8=[u Df@g@:Uz+ڎ18z];k\= *rnVu;SBK2K~U۬AK%RekJh+3`#rpYI liG^H@87ZM,IZ+GD09.cdNUe$W)ae8j[ϕk7;j:~I]#x̡[iWYm04K+z[&$7"x4 MȈEbҩh5ɀbMuo $ў9K@D#З}'wӱ'$ qNV?A;Z90nhDވ@xn@T7`oŚljVZ_bwr Pp8^ 3 M1K,([Ͻ^H:-sPC ǝ%,TR{gSpU|5$Q+ӫ}*赔mCf[XʩG\~oօf!EzAT`4X#.>ǣxxԂArGO 'E73XBUzT** dW}8[U+`%kzbЈL,`?w{Uiț|7>>*=w '֎]hSdgœx@klE9Hǔ,*)JW Q9)@HG?\k?A _0탌 dX=uNMށ),'$pe|$Cv2p7M̜7 ;*LZly#N(r hhI4+'SAx%*cF}nNs h|ohelgx5uGwhNyYFP`m\VviٙjGuh1w<|hOa. gNc} EIN`LГ+ʅU`9'6wӼ#;/|XMIej;֐ϻ:oJyj˪ڨ%*TNMXFUkU ;ʵ z.QłTײ@ n]'By"Dn)0q;b_)Ջ6ҜXyQ %[/o$ h@L]xic&ĶƠ=BXz- Jޛxjmo!Jl|j57ie?'/ eB61:; -G[}.7^Om<s6<]h Q/@)TPFllYdپvs얇ߊV ) U0MNU{Aއ4@L[\0ʖd[^*v3 w`wFI8#d0;QSo2f u dǤpj0$*UAJzM`!},) Rdi:P4_2PڍGw[2H{aO{*8#ǯvA#!kwok5F}),Q ŶwQ~;lV ݀xht+JHkwHd,M&aay0V`H >9f[$x(j$[vV^wa][?}r|QkF-Ia͡R텴]6=lErM_ 4a|fB#Ypr9:EN0i rF7-L MŖPz੓P>FU+d퓡[IU=!H r*`̮VA_Ğ* %rU}Vvg]y(8} SE& c*, EeV漍7³_k Cm&ӻoƌ?.4,'s .Te߷@3Ac|q/˟~mGT j,v{>]]Sؙ3O4"q-7UIrz`BJ/$ s O4l]y | |ixDNJy~(H!B:-c [%,*>1<-?! N]LЅKYU?۾Ŕ wz@x;/r>x`8V `X^Vcwq޾Z`D~gZgeqPB6kd|Hl Avu[cAor$SSы 0 f͈8yV;s uʄLWcL@Y{6|H<՘bh;<1~2$C3..mS_92%r+ o I6Sa񝼕eE榥\D'M-B()kvb,4\cgR4kbζy!}VhpB("j^}LMy*pNtr%)4cuyT^%NܞfK>3CO 7 !qAD ꨴ݀yI( :\1@ 5ҭPjݻm-m8.Jnm/v!R9&|(j,mN^ؤ4܄5I_edL.3%ot(I >"څo:GB,;[RaEF`g{1'>!\Wq8x̟p%6Kɶr%dbsXXgĸZBLyseg63o y_`&F@Tk2Qzׁ6v昤.i)D*CXyiyEꠈ+8(Fr3SŠr; ƯpxulI"mIuDgѺ&CXCN U^F`iY c֌uMcCP糳P!c/ȩl\,O6Nb1sj`zy! 2}"eVȪ/%#p qCy;B+/{q<,LHLk>'|utϿp9RԱqAe2yDikU85mk.TӼXCsx^>]-2xk|}vEa"Px!r{ 4b+M0Uv,'+d(hUa$ܢPm@le(lK{W(q y>PB B[fmcp{:$~^+"6`3מלiNҫjq:?* Ά(ES{Q7օiL0;QT3U?RCBY}Hbp8lM֞pUEݖQ.H2IfRQŧ%@%{fH$b pC-W6XA4KWoenD_؄:6=L[C> Sɧޓ}V偁J\<*gx\e Cz&uz'-Oյ_D{xnѝL;R?{%.hLSѥ `ЖHQc* OLyqAuEmtprۏΫ\?Tn a{da9 -L;'.rMRQ$xTP EٽQj6U1:<6@׫&7(`xÈ&TE`,7u'ZV<%NLZ= l'hnq v?brGX{hϹr ^i= 1T[|*]:|{vNu*twg/NM$~̛ p‡uջL Q^ wc?*T6L0<,t FyL a27{3To-M> N h947G^ Ksr0?@sxn?4_`ʆL!A%Q)}yIY; =I%nTI},Xư.Ժ g3H)"RL~գrG /RWM[{')ƞpk]y)mҾ9wE ?nO2Me@(?\EQY|^猞@&4=4u[WA䥡rty1Wq:3DͲ!.MЙ3e2!uȝ;77l99hdH98y%rxc$ꠄнdLGNcO}? D1 ߹!ԬL?P}HdM33jcoz`/d`sCZ3"61`1/°xb.סr MɦAgx,v̀YB2#%2[XJ{:̽ģ:ʸPprL%!27MoP}A3-^B?{(0'A6u#Y;/]PN%ѮDžߗfn7b8d:AD`Ѕa'aq,A;9P&{rudJ -zԠh1Č$|LP#VLd@jo:l)<؃s2"~$x{c,ihoMɏM@llt37N[K_)kNtGT7Ϻk mEeippɡm"T+@2]9;9#^TM^*&j>ƞfͦYnQΐ[Iwfa)=Ls4ZJƒAʃX *"qF2F~EwۄXw0k9(MJOTeニ|DpBYuU7BK q S`Y闼9)(ocs&޿+@ xVвӳއlRykƊ 1Oe%ҚBqr~ju5P{nN$D[6 )HN7VNAF͟r73,vY? R|Ȥfe5&GfrC11o,J:0@_pJER^!& =7v#W}ugk)9+_ekGk1]կ )%6(:<xVG$R6Mz$1Y_'5U 6YP@H%a@wA f[a*҄~{iFi-ԚAA4.c={+.6~C&en=#{hs -&^ps Mӈߟ`/`L f+,PU(g[%t~"1"ա#F8ݫ$z xnqn;FUH~(0nWL(Rˁ|DA4s)Jt &Gez~]Q#AfI@>1"9h%7w2+cR s=IsʵO&]Voy3T<{Hgnr~hhLPТnK(Rt2t1P o\t~6DbXI2q *:$ rjH7bW*N)VܤmM{_V-MSan% j?j7!֪bFIfZ6MzMSqPmy{r!RSjpx#jsL@M3;$c lwdav/ţ05W?$XFlǶz bqYd:S5ɺ/!2h"8죩]iZ#р8/t,wՃ!8IfE~h""-X!tc b._AQ== b'%i&vFYr7Po@98SI?QZ%%s.X$H~hZI]׻7G)\+#ZdI٩Ѓ3j*j:S`H݀U;zP宕#$^;/W+:|m1qϫ"WZ+[L(ڥ}!R@6ڈ=|JwߢG񗇌 /p.r&|:!ݑSW=08^D(_c [^{oHu(#?qy 3Dp {dl(y72ίL)L,Պ-pٚz7+ ^t`o+u$23-û>J,gYhm$ip!ԋt6Wh-P/p)])b[q!pg,~r?ą 1hIji.3OjR>M0|aDm5r9ZM#@Ui&8yfJt7_}c(u3.A>-[Zu(7sJ[!Ҽ( 5-+֓*pu]cvTjnT0!$a\őZ1pHCNjuC;BY>w1d{8O.2V8y0 "z[S,yB[^1HpR5b_Pߢ4ȇL:&I"8LΎ5_*׊@g7{Q2?VK@ ߮{{[9 o5 !(sleL`z|@( ^Gޒsx㿟~m"-BG[APeװ8:JҭjԌ/iTw:9wLfbC$ÕsA,=:p-F*T 﫭EU¾TvX %]/1(6RKpxӛ~? e ;pufU& qhlX ޲yQUgc㡝סV4D=4!&0+v7a=h^@׋8TUm0vr <='t[Ug;q [JVz?էRLߔЊZhj)3Wٞ6X~>rq4cQX [oVkM|/$CI~uUp&%M Meͻ'OJh[F aV#L4RKR!JRJkcӦ]-E &,jӸ!m6Cy+c>O2xqN &&m@3Ki,(rtE@C"4xLN!Tn9hUF@dufaMC `>8GBn~%X@*8 eF197>xp-p4N7v,MrԟLXC2k/??KI;cjwoy wZ 87ueFBEU qحmx.enk'RB&txr,H$yaGr0zZ"M! <jGd-r)A>ߤAi\(-B+ pvfK7?ǫxY{{#IuJl B:R ,χ%pT /IRj@ag{D%[rI[oc]\X-?m:{za΄7)˙+4KMn=IYXY+X@c#6]Fct_Ν~(|垄Zõaxi3hcu+ͻ-)Jho eKfN5Bfކ `8 h%cb I _BJ,U݉||ʄ%}.-cS uomp Lt莌l񱛥CnPv?eS٭BmW/Ҵ5j/6kFUiɏ4j!m2eׯQ9DּD9f}~nEb\EM4n֖M?ŚҮ/JߜjJ+Qڰ0);Ceg-wpjp=aغwS"tkoj)mSdN!]Y&?I@5W.\*J٥Bu]iOf|VQd:?!}C2mr.JͿbUG]&wSLNC~{v} '^.u/ڕX Hj~Ҍh.8`U0Zjgpd70M;rTD@MAx)ʄ K)n VOLų*U7֡/ҵ:hا%7iKU;^>44K~#vlKz(a 8m\V႙[qb9ZA؞;,b+:v!HV5)pݩ<ީA-`"na/_+VloCb&FXʣȴGy@hZ 0 i-6Jk u?܌Gp56 o,;YqbM Ҁ/IyG?M1kԺ$o4S*SGωUKTl]mua 8[}w(Řy\R%9:zsҶ Ȱe8sȫݒÉoM`+`V@ DUPTHe$V#/OC- kEqOS>ur̭89IJ!Kf.[wloYgگgrd7cnj>e^n xaثK?;PSbUQ7= +Th%b< Uqeu"+GB Hrc`"?Lֺ~oÒP٨inCW脎J14iD%.5+#s q7GNA|ei4]5WKy< nczHքZj.g=\&m_:EbYF'.um>:PaJ#D^0f,DD0n 5ֈH `.S o 1%1ml.[yE'!F_*Tnzܓ]EJTv:ڕt~xQon6R(-- 1 W5HHJ:dF2e仅&h2&5YI&#i8sy$:Zg/d_ɕ;u ߮X\RW#.ULk)#M¡iS("?3LSQ=)UZsw'a(ϥ28qZ2D ⡺Z$ N> [Ս8E}V>WKq7KaLQ]C1v6:W!J)HQ N$y)Wƞs] wM T5?<6G\q5.40ܦd՟b/G3j/Q_{72;O:9T7D=p-"@2U'7za]* xև#dclqXaI~_Is%NZf0*&;Gϸ׮ }86ǂ.r SmXDrc?FIx#qC;Pou*mab'Az 'he6-}Iz7+`sK 3r[N9o$ޕ.d<zqGJ,k-:!c~Ҿ:TK[ Ma3qsQPmM,NF7 [,y/X zb(9Fb7*rk1P,i6Z k\-b0rF2IIJ낤߾A^*$RMpC݂IhcWձ}vB6ոڧ(M[I$'<>p񾯽ˬK?f}nف˜j\δCUGa-v#`ca !fEX^?ƒ*]y~|fGv+l[eYJjEy-失Ү}&a?Is(QZ;DDw?_K;;3u[v(Ohj6}k (UX+]g}a~}*6NQVk`B|[/t(1 0`AsKu9bw/;!:'&Drg +rLncxnp|Og| 4$gP]o? :*m >o*3OFKR{C34ΌAz+Y?Dҗ'A˚Z&@S(HF8&m.G(WF(&wBsDWXr΋9w γ!sJƭrk]f\ެT/{:4]{2C7pqqZ }/m]-xM|drh ʻ+Xɑ]Ãi5mK׹q/-1~ҺNFlo ɤ=Xj\ &%,\7+&Y>iIH&a:u|0STw3"FtッI~VX~&mfB#()Ǘd]HD}<42(яP B>6*,TyJ@8O/>Z`$,̟PƻW X?v˔gomhD#dڇb&&x(qǹ'[#s/, 701xJ'HJ-۽'9.fm!ó]<H:S_%j=‹+]~A {(2{jy_c> tA%6+ZFk97&ǩU4j3o !v L[.Zf) S4/8=Nc_Es ,:وxA"QKKs~r5\DGFΰ5ޢ%c1V9-0-9Yp8Y>O_3TnW0Aqvݻ\UR"'v옍zAK oSkOXd_Swj~tF6u0%%EQ&KDH}x!:M2dmh2~ʨO^?Y}u"i>o`eSdŎ%ˤѦe<-o3bE5dӸy4P! vj Jr ;Hض3_1Dn fbvyb*ݮVi.P+Ud>)W8}gdwL˒@THuEA(H, df]׻PVbQ6rQN8U)|DSj(4-,H-(Dk 02QH"e!PMzZjJ o,b&og\S.OgOupc!unvg~,:NL0pа?pk%lƸd7 8qfKDZwՑLG DEZHNgD@jia;*6Bj auuh ?d3~vp*}pQGd: \K%fG-mJ"_|fz3{Rkc YY R .*?3jzܔ硓2wfKyv't%OE{ց}>nT#آ"2?%g1X,:L%8r (nkA%]*?7xog<MM_ B2&Z 5;aG @r>d AVR; q?2`l(}]FjOۻw)HlĿvINRhz2Uz"uASʚZwx}UME-r.O`RyFg@ Y'Е/w\cuƺ=BMLYOf{ݖLwYC@]$"&k5+3Tp 7sNa*1H6>-:2³ r'3gHO X ?hB?NaVq5KfčC񁜺ڴ byS WW2&[06阷">9{mnjm]tx͙SI6[E sm3 h$(i)lz~u$ 8J+HP?H#) Nl-#8)\n7]z:0&X:xU@rwsLvJ/wQ>H|U_|4Fyo\URё"s=w$%mv7!uU ("6bd-XM2pF!H`҉UWaX5rHwRm y%}WC@AX0_7o̱7ۛ^\FЌ40Ͱ 0`6{PN3P.`d̋V=24Iltl:U$ihDI^ʚW[s&^_̓`d-&B 긌*l,b}b#.oJpʋ5O$`/">F3=_wzxb8O*CWkgW>OƓ 2SoUךD>c7}(Pg\Ƶ=jDY07*O[8]``8T&Vͩ!{J\>g^\Qr# *ƖE]ǐinsJ`OpkQ|iNpIfj=_[j(7۩ T ;:_1(:pqtWhȡUvFdgX=Rǿw,2:/6^{x>#AU2ݵ+SYORzғl'E&V.viY"ށ?xse|*բ1&3 3cq4>ύVH1.*[|ŪjxL].dBOK]VN> 9s͚- sn/)T0keRGZ!rm4k ;$,9N4^:l~`dі(ƍ"8KTBege346#XHh|EC3R(Z_A۹#(=ȫX\KYU n%l(93LL|F0]p .ɖߠtObjw@v]Xޙ|$5G 6uaMZ$\?]QNfGl+ZhU=ItO3tV!y. 4 +sP[Ex!97>2MbUCXǤ1 7|][V܂k'"4@ZbDqyI` LрKwUZq$881P?m8B-/'޶l01&S/.CT0udžZt,kC y's;lPȪJw?DݢL6tB%\ASGtޣ#YPX5,-uLȯ61|iW&)57T?D0`rKd6EnJ\dٞLEܢ/2DStJ A<;Z4%IXufrhj!Gx99nZ'$bF0Uykf%W4?0Lj[x+1К:ꬌOIbZ*bdğףGβ ~Ȗj̵K6%0PWKΆ)P3B=/ >+lA"ˠR5l:#4'-@%0qayg|L|] xYRvl(8KLc[L$NZAрyz FfCA1}b, .h˅06C_N3@Fs k UX zNqJʝaūTBRAQɴoOp~l3 ]? M7,h Oڗ.TaQs ]HFug)<ٌx|h#re@v pN%߈`AQqC1Zg~SH!}BM|9/ [ʃ2@| *1ˢJD%+G $)> qup6.IRWQkVP*ަ$1KKTO^sts$"BO .C fu 51$si##k;m4Wۄ"`zDLJ|Z pLdcDD(AFIydL`d"{HYf |Mvd#NfߓSTIu M$+=ͨ0yof!C\f7HV gtZ}QqBqo/k'V>$x7rn彺B.FI&; ە'y?QbC}%2B% $5=Jv{IXʉ:U0;xZ֯i]6eݑR'3 <^) Q.D^Hs*]T(J8ޯE_ɗ mC2Bʠsy3p'# ;PӜbጜ>G!۵Nwr("]pTJH2q7uуbYk7җn#Hm??cI 2ivw=[ 6q稏D_kG3yKO1f p ; E֢ޚr@A (hܶ]h/8,,i&~su{e2b>ae~;ZX0[\ -Lɔ7ÿs^eA e8?^1ba֛~$?5ʽ/UP0?s"wsewΗ?BD Q|1= _HߨRu88_mۖ hMv)+Ϙ{jꐅ6{G?.vc_-P6(aFsq9~Sz̰ ̉L/p}7 x-d;SˬnHg==8fM1M_UUG qȒ޲$({*@U. XW m>YMINj\n$#W妇TTun%ovG6%(oS^B%cnx pW@$>{svwƧQuZK[j Op -\6sjiE4\Zλ9=~knT4Q|Cq6uϨG62s ?RBMdQYtT^TR7֫ΎFچ'u.K* H砸Df TeN%}KF6f:MX%„b.{Φ2"l1:@$D([K-kE]dOaQ=o3w÷rEoMGos c,rW|3~0'iCbm_!VFgws.n n#QOɬL-㬢vAAUj1kAF !ӦN/zt`_sW))`,+ Eb34Zg톛% $_#RO=eQRP((NJ/th_tcsϊNgIG05yu#k4 d"u7}e_T8KlW _σVP'p\$ljͩ4⎜rN媐.Eʨ`b{L2۠jLsMK7_&ߐ<^c) =`Fvne> E# rn RSe~l(RLX5'A30TtP{BU+ɇ.O?d[*b^(KWT:Y+c.V֫[BhhcxJے=~%[Db[+P/fVIc-8r.B勎C 9CYj_{_-&:j( zofܜfɣR,ap^܌ڃd6Y9َɘ5隢e@;DЊL5Yr>og&IO58N&9ZPj|>$Nh;(NҊ?JfFVϾp{˖zt;t5W)Zݶ6/ḷ$ J72_B"W:lU5(CT 3O2֑}`B|VN?ӎ9+>|gI!85pf Q%xMJ-)nq3/|Ӄ1 VDKuIp<Qvp&"BA` 󯜼R5gj.օ2򸪘|c1fQ#l/l ={i1r^,b)\[ˬ{vѳnLq8C ϨSfN,=7&g!c.<ۆ6(rn]IgmuYV(_kM?#x$4_/Ӱrw^F Jp!DdK|xnKT~[ř˓~C||m(xk?RJbxgQ1}mt?kHeY\6*]E񓻈"xx+ּfGGƬz(Q=nuRL("c/-4 gI=(]֠FaG( Cw_3nEDtH+/ vA鏖s#ڑBKdiX{"G{àpm+Tp7Ԍ3a>҉I6!VTHn˷}54-~* yy2[ertOJ ;csC v4A7bv3[l|?]UU(oʒ 2MZ$&SrV0|3:,PιhyG+h%u "+-pKTGќvng+}&<1H*$f;S4fR5FѾ%3[K<,6wH,K~-17eV9pzʬvp!l&ԝƠu/.'91CxRk}տ(?rmR$"$`}#rgۮ_o~HeiV};3XP$XOw@ +XvC:n-MLV%vtIMםtи4f]aƉG9$V8]4EЄ*[Kw61Ɔ쭴ycd)_Cȏl_ 0.KL鮻ŗTiOy}yMFu 1jg;*I$l6"B̋M |r@suY`[ Ry\KGT@χ7m6,1ǥV3$pzgW0S6rz{CQ8?F}zVNbszt_3/+o_R‘~mQA^k⸖СU-'_fJ817!ܽ1s:\\i[*oSAдPp+ɝV Ƥ]a$uH=jTdq "(Y @8$K\,@PS9WJ([?nGLPhx6"ghGb10zyH<e0*8@֛;ڎєN<+7]L@),oY7,Iu9b!t] "wO3|^[}T(nAIn&9}NH}[Uk_@4]sg5ګj90]-XeҔfJ^" Aut#B/XuRE<[T5ʲN'F접ɔқMaGaSGku^֘QzJ%]^]/rAYܺNR }:"rD1E]S^bsi "1"m0U?C)_%ENB?֦1z5@4Ycͪ Ul<CiDZ"~V{5J*S`й_*}4a1br|Y. F˽fr <=Axz FHhџL M%SP+ :D\֙ gQ㛵u<ņߡmEՔ'F!`JոarrQX3~(H@h(2>BrláA=gnO5o,2xLaӞwySJpnՄD1Dۂ',4P&mI@╢1IOiG&Dg8ADLuˈ/}G>. j; 0Laė4Έœ/HFVnQyD̢HYLHm {*L5wG>=J|nj;nx%6TT.R ?Z.:c(bדK',$vcEg}Oa=Z'y1A~S~06K>ጔ4RI/mH\CHQLOUpKiv }%Wd1,,GY fZI86,rwsqǡ|VTJS|fW:9I5ãy׶(wv%QMc =Fk5t5CRӈ,D;W!"Be xmpϻ-U73uU`H|px@eSB⃪K"B3pX?[7Ń)Mӟ-nlX&{1F@U;"cxZ{%J9 Wz3x !t5XgмxSDk;*,-d)\Uɘ$$ҹNoޙ ]ȜBYaBa"IGH`q3z?]*7볌6 N[T6֊!q4m^8TBdCd2Ը(s`Vg5=DLq/` Q*=].y`|>XXB\KVݞ-(cg$~W4^ )-TD8(w2d-D#%W;ry2T0(->@> 5NouFVd<4{FҿaVeLvfv1?ی݆ q!# SNCv`.dNqNC6)3b6NcI_kQt=":vP[>o~PiIRX2e ? iA+0+ҧRr~xUG$NvEMi'pId}~l6$7Qv%ߍ*|G<ܤĐnž) -~i(%nM0&D䉤7sY?LoKo D,IY0l3mB'ڲ[hPg9/[hyIyX(~"ErJj煈c,\ΔO=Kp Ы_~,d*A!4,dFI8/YbWJ(| F|$+ s͸4!5@r.`C[8؞6]i"c%40bzTS)׊:j#6_^-@R\l. P'uz.QnTYv%܆+ggfj#%$+0p pgdV~@M?н#Yޮ 3&9=dUp$ ~[޿dچ(o5(*0t}1;U McR%{߯m ){βt)Lw o@LyqvPsUy?JHHqp? h _<"JM;_))]K%H>nB(Z.y!޺b,՚'I:fk^08 Km ^׋ W8y7cozΗKH[a*+t$ou]w粒D Quџsp8X;aX[s3mk)^Y ɊG{'Bo̔PvK_A!c ş<%}~ߩq1{k|^6z~v<=J(% !_%nwLt&^AH 78,i]2jQl,N]QO8fGy'HGcSWFTыVo1DNA3:na/*M |DmEf<20СPz y1XVΆsʩ'-KE:*l~C9Iz ү;ɪofjl9Y ˅,wX*@FlMUEUcvkRPT2`3aST|rlpqqt\cJ Qx)C z&^}xn`4$sT^' d.mX۵ ?G,\* `6'WT0@,ov|9$6%W4\$Kʁb+qSVaa*1Zȇ9u`:rfb,s:Y WDƢ 'oL`L^.Ual5p՜hFvp4VkR q'vPHҶސfJQhb[߹N3@!Z (m]*@yK>W,Y9CrIF3jR]&y)5=w6rhg iBllt5@׋46? opqLr돿X;1 2"E,p$Х9;IZxT0Ҝ'n|@?n͔(ms"# ]<>THY-Ԓ! JqC?}T/(j#݉&F=C* 8[þ^|2P-Kd)@1Mc0_:Ez5P0$ LP;aߪbF(L .MTa؋&8% L2ML>"1'1; ̆hS9WIhqa%b-^/j:7ȓޗ[dEzܥ_8tKD'0 >ŠprHc51v8+{ nW;E0q scG@M6yGG̃92f7sW΋oMl/q.0#eΨf7uת]䪐8K9ڰVޑ@یgZ tfza~hGނd%/@l>iG<}^ ՐCjE~\ sE,!7Lai");! }^8ˆͿ^w$f5FuU=TpaCdMݽ:2\vmQ+>M\  ù51mOfTRg&KDQ3ۧ7~yڎv6gRv#Ymad8[ O1 L&᧏:XizSS )и8g퐺5[ATj#P{g!iC=q, $3ԫ_ ;z)5Cb#S - Jx-vC}W GH\$F<z@O Ԩ1s_hrQd֧@2}QLAs̴r&YٚA:s`c;78S,%ǀzR\0 L> N,N<†}cG|+mfs> X2eVqCRUf>-6k}39usث3Z(i7*u 2bxt&p֟wz(-fs$*;5;U 0-SKWÈCGDj:$:bzA"wagX^,F] 9&59 a.:|ceGY7oe``L,VᾸ4Hkٶ sۋኗlcWmu-?Pd,t{Bd |M-Ix 鎸ٸre*-aۣt?PUZSUdXA2b,gM_"*ς53j_~A/O}dZ @m!8t+f)fa|nqg\}:Y\nRc3K#ӲVI!; 2u`<{twBHe %K7ot焎BV#&XuC太= ܈([L, HatTړYm}EPfGңܡ>D$:T6CU)<܅,B:@,křj bPtڽU2pNgQ]v/A ?M`5{GJ< HWx9`J d4/}uM}/xuwEk$)vD Z`CRA;Z_ヴb}oJ ^=4PqԅPX}X]i]]C6gbH tQ=iZ[?zNO)\? t=nvQ$Krg}Lq54Lb|8P/X*-zr[Ea%jO( 2[^1WeTvCÏJҠ<}ʣR"hrO'IsԪ[F% 9+, (ȍI1(]~ҧ6gh8V{ $nGh-3YZ+p!miE⹈Vlk'HXxGx\Cēʇ A# K>#H庌ԟs஥˾/[e2svI(t㲞PF6- Ds6SB_{ßq'R,Ca-4ݔ ~ɥw- |'(K薡Ha6Xp@DN0&ͲD6xÒ4,xhLnaSc9I%k+gTm`̉0!To`VnkL!r4 357hr؁f<_Zyؚ+z/t rj9l5:F.藱r{f0|@$v/V0j GgOȬo)7Ri85 ׏a  y]ncǜo4]n~M9Ԃ׀>D(GvKy.JBQ]c%*,4Jxr'w}SkF|GV\~V1[RЛ]Yx]c)6={ o1Wb~\{||.h3s0~6> bDÛ5Fll6oٷ'mQ2bPM$z6`[Mio \-ljl(v&Ǡf^*öLmرT[a=7ܩy A"y7eq|b,).Gn^t_;WJ}Cj8_*BױWqJ-u$)FiiZԗ,)4rO}@OL-Djpg΁a/!q{U+cXIFJL GW>kKbmhESyδOd"]0 V(_r-`ԞauF;ۊ߬1w>>K@`ބ a?/$b흒 앁?t|o|HӦ Yy _`-(y!F3 ?&݉y5sP䘦ZB|]㚭|ɲC7K<̀Ʈ870A)5 6bI==X cih)6piMM)\ p6R࢛m-tBԿ/rOyWnTw9v)|$[+ f^*s_!#t"\fF\1 3B;9^K(@7Bj{v^Ekۤ9 D@\aCuimN%JZt|Ukv-.>\nX31)lHz)łn%I ㅹ^kQSLR/Tf_J?ۏ[17'}\7f=KdS O{nET@qk@o)eB hL|hARfv/2™ßYǸX##6B\rSfZ" +N/D\QPJ]}>\kIm:;pB3x''kX<yX @kI^Tѭߖ?,26l_ t>gѺӝc&?[j>}S6Px'_ z:"㛖FZb~oWlDop1c{'-Q+%=nL7)>iX βsۨ2Zx~f&0"l6jT4guօٯ1&h/!a?Bg]Lqa?Q}ZS De=){ " 1gOe/ ܪ6)QNN4 W@-.kvw_e&ݜ^p?kx/X\ݼ6a/Eފ|4KoB*t-b1cBM4 ?HwD}$՚㪩~ovr_O$\`S/RzRgaRGL]YnjG,"1OtRN@LEzgNmoa_D=5MP> /i̗vMbɪМ9o%[X?GXH eih#QUuXBɃDAcnCP@o's'a!lj֮͡d_-䫞.h7>F٘\\9: #`Ic@#c\Sw9*8we!%@{kvy/jt(* cY|a7d]oGcL+KUzJ"0)ء:8J|fKՒiVLޱ;H$1^ oIRÆ= @C8;)#Ch#=6Ҧ-ӿ$R9]uZc_ނ@.'QN.֒${5,EljPA xd{ k4h5iAR3DVҰ|>]/;V@Z~z{됗\MErسJZd ɻe4Dj5pQxHc,]3S,pI_4GGiT8D.7t&] RLD$$0eh<0typ:Z5NI (6aݲHf,U, [ 5:(qV /}euq;hj |Qzw F%;{bvˇGI XsRJ~#B)CO6&ZQ+kT) e|!LvNU8"Ibc mx p'YE±Ó^d ȔL0 @9l ^]+BH U6KU XZY*k.;yE 2%bhptc#4MIabE<ˊsǡaDAHp% pʦ?io)qy޳ڏBAV!= `;݈ XK8[MB=݌@xHBz%(+E鵰`.d|/-̠)N/ى(;GO#ytY\Il ̔K:؀ftv& O·I<\~KeWC72IaKoz߯nC]WUoS+(Bg%c4i7U?{[Di>)z]S^U\u ɸn%etdkR35>&J5= @UNqdw$fW*vӿGR?9J,vt|]QǸ_Bݺ}(Vi'x>δfP%KhPd(O˗+Ӗy Ǚw]'Rp$Ox`ECuPvo9Jі\&x'esOpX/_~j;IjDoƣ3վ8pQWbv(^ЧջI2KI ՄB&YŎ}4{xSvZIi7~/b+nkp{E=GFSCH`R~gE{`B1,fGǁSE!gazD0sR G6(kLE|zGjJ@v{:PeLIƞ?* a _S@Sn8D,7c<o*pJ4;tgP{ ' _H;װ;n> kJ#1Qռ8ӿ=|@Tec;zуlFiV:a-I:0ֆvxe~".UaQ&d!ˆ}S.@Gtb5bݍR'{B`ɥi|J|!cZM]9Iv1T :$L4=}h wՃ$\zQ4 nO[wXH,rh|Т-Ϯ% 8[ivyȄC SKG#mF>'MyƙaeQCƞn±H o8D.\fK_J_{ohICv[,GbޣwpYA~E+U3JA,r(9A5GhA rfP M+z˨e x P qg~M}U?pz[b6KbxZP"mk7Aeb]!+˃"mXB/A3S &@>u,¾ "9V-A-z]5^Syy&/_K+2Aȶ@,fm9X嚗U"4f+ic #ׅc+=*c*PPE*KE)JJRY2$Ynp FØ)ΩPH^ܿ ($uN/G;ZG@ V4~! uk:$:P[VM+lF}0MXAވkʿγIe,댏 0Cğ3jŽBݮ}SMM4g. HiiUb@.#* %xNL} MM l_]֤N+9M6Ť KDdTK]x-`o-.@"+wI"2Iq [;JEljsg87= L]\ a<ؑs}:J~ϕ$>B 뱰aJVx턴̥("lhk1+=_L~'}aa@Q& (z!VY:2C+r_#%$`Hkw)Y%_30._߀%/2Rl7i0PmL%7H;D(D!(5!͵Yo.?#Uט|=EK^C '_~7*E4>pƔIP =f`#/QC(Kqp~$m UgE-u7#?$ɊR.-9"y Sq7jOn>\14OuڬL W-W%Ntj{Y[?ыHIV^g3ԉpd)%,.?6G(?qs!d)KqO#9tkiaqıjLY(7QRW߂J*oꀲ@UsЙf!x|`i PR홀ϠZjH1x&$G ^q]<<͏r0ڜ1R* ,3"EvC Ѧy}ےT) x |ovlqΣH&}*'kwdavY9@$ܟeM#$KD[^%BhiID,.QƂS_YIVyK\N]&hQf+/,>uۣ9?:A<^yg$D Lg)ȕ%,Z~`9 9EB0$iQEaqaJ1םM !ѶViny)+OO-MBmquCt$['رj,wv!1MWYt8yGũI֙Ud1?_ZxԐ֥+L0)pnYm"r7Pi̒ uxJcR:Jkyt'9ލA![Nysmu#' 2?`JV͠Z-BBHx pHOs ~gHYÙ z6K#_ǬUlԉ]CP۰^i,S|?o-֬d1kM/>d]RhS;,-9=fPغ$ٷ7NuG#}VjQT5PL5|%rDDqb R.z||y@\b˧c8F& .sUD2Wڹ7ΪN{Ӿ5};ut*uCVr Ex/Jс<J 9 W Le30 DɲF$6vu&.䍒Sq׫ G^^qK$<=Jt?pc ߐ^fxb4n24Zu5*s3\. =+ ɿ4h~ 7/¬]C8 Y^) d[mBakꩲ}ZI$DHUv?2N9LPYnd^xByJ*k/=uuDRjPDMǶDÙ6X-QPtU2yhDCҺ 6uXJѢioV"6hib[I )NĴ%AnKQk-<蛯QPj:yg6A-Дzz!M'M)dBVl7Ը] J7 gAf!_fگ432!xqgIi+D2ǀi$ ^fj#QO,S%sg61b˪]\02L6;cD SAX?`Nmpkv&Ld|=}pb-NMzlf!Uoܛ/2O3! q!Esڦa>}toh"<О#<,4ͧ`_QE3O1JUbמ`P`Ad[.>hi羗r7NLo΍:sq)jNw0HLQQ[h[a]Kx%$ :.OO+c6Uf [W )#'烋;˔A|!(d!=B30Ò;AM:آs̖5hL)Y?~t81=!-@j bt\8<9s{#‹/q oR1''V?? If$mчPZ͕PRά>K36`+6=I6_ŇX E,l/#3ٷ%]blX<1h+5cn=&G@S 4dl;K ?)^u K9)9MI(vGS&b3߭ĈVVͺyh1n`\O @qAhڰa ;ѳ^yIleutLw{h{9m75/3Eg߬vQ=38 $VIYh[>>a)JE y|T;(N&uvҴ;PQt 6˦a W9oJ>> PpNETVIcKIMh;ݛ#cѺrNJ$X/2OmٙmENXd_6 29ܔ a.'_ aԬ2VZ*K0FFJ8HϦ‹綟.e Pۯlp1z5qiZR^Lּ@.t |e&.YX/1|'&ٟP&DUЛr[*1LuB#.Bx< Q-%#7׿22+`7|&;?x a~p{y:u}(^`^qg3fɀ_OI\BH7z$ `С1M5-*L[à{mYRi,` 23ljz;LFWb8̿齎1S^%uV&U\% S",Lo+zHlrg\(}`_>־L,ܨ 1Կbd~ 3y*%IW'Jcq^Of_/RrCDރfaQ9<*>{di^Zi/ڱ665ÂʉX1k]w4%1]E ,!R @]lM7)1Gn3dfcFld|s 10LLOi4N2JXKਨ߀H~\_hӃ.5٫0^Ǐh5;k8meg5nn|kCwOJ%q}f[Yɰcߴ= ;ԉeɼ(59SwQkp)tSD@,VCxA(lQEJ7>8!@ٵ+1BύX50paע{'`>}f"9+wO luúmh=Ĕzۈގ2웂aDVGO'PEXOV@cZ oɭZo,:G\iݨ{Js>"AAy#,ګPػ$,gpKcvѼjZilpTq`6|CPbkQjGcixיe9g9Kv 9zgq s-~w] BܼnG|rf" uT1.k$ruš%eiXȓ9gnK.',Iv tgE:D|*sgM+w4aP7Cm@PCݗ)+&V24HP3H&:hs01_q}Eby `AY7) rp<;Qu[z ͧ}4I^̖ٓ'ucM2</"/o-F@׽Wh1i,ym~U& %uF%<Ѡ%S7kC?jvnMc9O.U{.(Swnv*c%7EY$Advz S#_9C}T 㑴U0u[W1E:]\ԍ%X4uDt9q)$֣ fB 7(j;#p!iSk9t$4 %c: |t#]:'1{Ag\ P~9e<8sS ̾&yyoT \^ I~lz;+X_q} EfmV!( >؞zNC<b>=?>Pkc>RQB_&|o+0wٖ9n, ۜu,9'kkͮ%Dk7lRɸ墅* s@|wch]M$1eSIw/?HF&NiBܑ-9 .3 .<]Gx:ƭ2& ФA0;FQ3FVgf@u3?6 2`˸MAYc f<>HeQ3:l,Z;(o!`[ւ Pq >!Ԫ>dkJ$F?T] qskEP0AtEA0+D_qY:tNLГU[sξbk흰mRydwks6=ƭ7 *zܓɧJ}^Pz_S**j 1萍?GZD_x2SȮ)$$LvJ +҂u<1M}[پ =pH*nw;x e =c~>fSSeYqy4_ؤ}tmЩ.̡6̧u1b!GngˆOO v݉N*jQ4>A |i}eI6=/X;+ͅCUќjqzcp[U19 >nV YϺX΃`R$Eb:8@uԜ dm:@"/SDXTp-ԬOzݴsmУ7"Y0*Ӂ{0餲;]uH[{?/xVmHى"j ۜcvI9jK3gŕ>/>%29흷>jeh׿&'goAEh>6_uw*=C.  ]Ef\&[BvR.DPу'aEsIBe trMȞsl)^Pw[L[Ocs[s̍p{'&ß_Zo9-vוVO?~ᴚI:-PFP,n& 1|ËJ@aJ(hw0QsDc(EufZ~RV4p^NZGK ir-d;_S;$}wh2vsСcHi![/֤0qD|mXlMe#Jb,Yҩν^Ρ-@,Ja9Ik|:~tn~v"V)'=t(k BrOي.kw8˧&SuqtH,2pv `m~Ƨ%&/ QLTՐry 7RA ^RK;Pr܏ߎfv?聦j>gCزZCSx)F ]oӂ ytAZA%~XrR/r|H_-,5fюMbi@H8i?xmc993WJQHzj#d f $ZGdk͛VqNB~kf _)k['K޽1"5{:hL\t(-tOt(xWv$]}r7}Œf$f'ʄē;r8~uǪBQ Cov҉Ս'+-mMތd-׀87L`u.Ex=S5ǯ9x$SO3m1ZbrDj=SC' x̤HY?\̶*u EY>(3&1ݦ;$IQ*9=fG /2u5zrp+͔8#޼w Â'^+սGVJBT&+kr,.hd'k\y(4X YRlW ΍Ϻ Рl0%yݮZcKKr-KK):#jyxw%1ݹ`a X -Ad'i +.b}B"Ү /4cyT:Kwqkz+Y8B6:(gxL{pť"u';6(|}I+D({&4K˛o`FAAW/K-\l\ inSښ a$MsT&[6*C]޵7GE&_%X̧HU:7#ڝbB]bJuZLk8윶jSoj-9Y`84S-u SŒ. [ZC/Z![2lV@{㝡}j*XHbBpN`D!eCoso(:8hT![O&A:j8̨sNQI@[: eg%ϥi#L'穀# \kO4n_b^,,!e)ȡ 9'^0!IӒhDP4 +oW_t[}GMquNSX(s:^t& S"I?bPp>Ru#!lL49Ol0"w/N)Z.UXz[V;d Y< #G >B|ЄOm9~Ƞr[*ηAڻc/[De3׵ ]ޖSʹі47ug|~UȘY%8$rQE۳( )lmP#A 8@Y7mL%u2-pVޔ_3j7r hOZ;}>&;qDd ]M*5|tBV6!&j`;dS-bbAk8/Ke<_٘XW$]n=(w4S0۔sެkH q^9a%[DǶ׆}s%b*d@@] !|8awٴ)>O6 ci mÈ66<0mꝙ4btFfą܏ rz_`+UNtǂ|1hY $9%V>~ĿK>+q42 9؟Hέ"#{X n֮XϷ !B6~'a ˍT\2[_ϺrG^v;40t6`|C1 H(ތj=4^ba:4@4"34k,JǛ~m)4kdCئy|43jP\߰fV|T,Ա$T!ra oSM H|=V؇$b@+y+^P ֢%ȦORDm-(%TXYU;qv̗aWCԈEsxP.DR8C2ܙYW^vPԣ3k  w eB _/Jd긔`CUCd#7}Z;?o/r)%mhs{!y;[ wZ@" έo z$3 ,z@uځ~ yת=+`CLѣLVsO\,fٗ|3̿6Q桎ŧC9B º{!CO-˝J$uuTaId ѡ8EЇ [f&*kGariry80|W}iaswmN_2 VsTz?WvЅaX4Ue oHvV+:d]J_׶.R]`T h/5 =}vbtm)05l> YLX/ ɵ#AI e`cpa_#a ٌADUK8PVp&X h069[H\Y;#oDYV \=K,2cnI$XWjbbVd9#ZA.R+tw'5֋RMdLhˌc Ł0$ ȋ}E3sJ_"fKg&P`M%z)e )|G0?,eE| 4]N/@Îm:"kan wC%h>} ;4]ET;~PW^T"p$;PJra1n4M6:#98-J]sD:%ͮ^4Rʹ JfvNTG־ Qb- 0)lT0ʺpDV`oK{[?[B$Ừ׷||>;ѕxi8ERi1/DiY N]܋X5`2+T=2 VmHde#Ug `tFb.YFmusYϹV7sP#|pb||KGJg/ W $}mIwsdL3\c{=XQMX`Y8<o2y~=ELyi2U4RB4$) >OTj#?K nU1AZ<Ы\P-}+Z]lW7-Үr=֌}4zS>ͼ-e5 # ZXa0'ECxkʇ'k7ˣDdct /Ғ3rw-C `ʍr4Qd - UBoEE/@9Gi(N&`|He, %ЁtA(ru핽¹z̿~JsW\u 0JA_H١U AŤV |@1xCrϘ2ʕf7,NɥQ_~bۇ5e\@q-*z .4תπ ;OP(Nd=>U9(iswl⾳há C>}$]LOs2I0Suw'+IFG:ލkT = 8?`CD]$jy DHz쾯Д :g1]BiED #g"ke* y޼۽{"qY O6/߀M #u&H:&I^ݾ;?OnP )m1KTf 3u~N6MtٖGVG n껸 WYeC mdnF/z9~D:񺮫K㊋wr]9 :+B pUiH=nH”@ wc%s%Ec$J}VT_/)P8 n7s dD+-_wC0Y2bJ# tǫCE `1}-3AnbOYd3 ZS8IU#EV%)k qb5Dj=gq24פ5)̢ U3 Jz=^fV x\. )?Z!5Fh&<3 MU(h[/uskE7$*8kvT(+()%u)a w-lQU'N0/]w䴱ǁ?żus~ٖe5xq@ez/D"C>cv'ZAIn2nvOّ- +5Sz҄c,ŰVwJvJrSέw׭mvB) 'Q ;@X@ꕸk]e"5&'Y& PIS= Mҽ4UWAg~īJB;I}g\r6=uJZWŸBF3芬FݿNQƚi!vFJf͓l&sUD6\Y&wjrOD]CS12M3:d Vl>Y=W0F0˴)$#4m(5Fl5|F %iW`쐣a9Ptczvpɿﱵ L5\ Cœsi!@;I/wurD3#Tڮ-zw6~S|Y/m('8,A)gN)$.Ia+W$VlIn_`"ٷ)b`@-Fϓdp-,4 sQ2˓)Z3hP0 9+k4|'r':Xn0?5c}ꓑZܐ*͙|ŹH-na"V#|!z 1'I8]^2fR$Cy":YG~hQ拊džZ)F~GT]2ޢ*o-~jx; <8R,<h_ FF9\O ,Cte(ڎm]^*7DAضpt0iڍq=єf{*łw*fE2i[bGF_z~[09ż,B$| `=Im$\]r8-P_nT2|~+{/>trdh6 +y3['TK=[Kvs:s2"ا2Ii޻">$F⩵_XybY$3I_]D.+=b1@7.8p{.Q{YIGn.oO\#'&6TLx&?-Lb cs8{#%NW)UZ?EX/Rss a}S7Y({C Kʀȩ^1=ĝJr/\ie9P Xg9VFx_)`@.-= XN\G2xx!8~鈳*+"7Aj̮şÌaprkf 5~;PŹZ`1f…XW 2@rin]krEӡ,[7rfD*AdVR WfkE⼀_EOTԽHIoKdK+-, J8!7 kc<ȕzgIܶP` Zy4_6=&;y+C@ \JWTbñp*}SxPF2:+֒[+JHX\ýE2C+M[K8qK9YO f1RbQukjN369GktRdZ 刴aJ(DxO c׽$rؔZ]_Ŵxrד0`NZc>- BTBBQO愊q;}6Ǫ,;Nqy2j6œgS*Ԥ?eJ|Y55.+J""cK=n,p#Րm?X讥niw&4p4e<)u}2ӏ"M "kuWIyPwa|qۆrQ:u,LB7k )h4NNuY!$ۧ@@% .*"䋥)5.Xȳj<"\RI< Fz߾ ΏF:1v 9#ʎE@hdNi)LձR\O@[G1qqH+cxǖsdt/m.Ϲ;)w|Ҹ7wT#I_91}zƵwD`h0}I=umf&n"%p~ŭUpb{kby+ޙ0buۙ u<^E]Ve4˷ӣz[ bgu{|b-57E@^nD\}aRI  Xqe+ >?6y A(! )t?9uoV.1aa?W譤n ߶xY-1j=EU>[G%p@#VkG_z*'|=׶CWd'&bH51M\w ;>j\iOOI.\\Bs% = 1BC s{5B VkCw|ȆID$ J/7]n=0>݋#<*&kq ˸1"Ut 򝨂(R/ȇGKsjuZ 7_Gـ5]Kl`Ye4M-6poȉ'T*=+eFK9sgV6[m+uJD$LQeK4{]F{ )+@yR7/eM'~F ?s𾕬AHl`2! 2xM,:#(<?GF,ZZ ߺh,ֳ݉ p.>N0'1kK<wW7G5 ZP@-Wz$C!9 K4Iܗ(@A"8̓ǩNMީ\ɜanjz~S<Ê#QB&#H]涺s `ձG6 :c2?kl;W t1(5N٘'ɽf`ppqaYU (ZV 8 K>p(Xׄx|RA7뤆1rVas E31 eOGk?<ٻOe T~];],HwrH&@acnGeJxAVn8! DApgɐdd>%UlԵ])B`NCe,dFl7 Xj~", x)j[;+K@JzQp0Po.q=x~rp:1)L Q & I3hSԍbY!yGAU) uMU Q#1G%q j $(2#5*ڼGKJ_'=fʞ .ފHG7O%@E9qm!UiyTߥ wFɈum=֚5IB[/NXT![z|5+ iu҃)/4':r/жY*^J6:N.aUq^kj?ov}s+E34d?~căQ5 wQPr >ŚEN#9a<(mZl9?>%hW3w*!SBe6 Wm2MQf3Y=7Nހras2-Ek^g~@beWϏSJsTZ~pW6&y!)Qۃ4|=t5 Ydɹ>r<)ɡ-&cuiAPYEW{l`R UX4IjYrMDF:ٽaƍ{Z6x/VX#2usT Yx{H@?[hSdIWSԏgT^"%b@*<;֔_ +'~:3 \6'_ qkeYUqX|X]\46?|6ns%xӖk^㧤/4o!֖&ح0]q4s2^|w3w(Rތ ]za>NLOG/Ǎ{TNU߾23KGLdXU˧ǿb ik $A/gZ!v,ioV?x0*O"Ht*辅Dx[$sxfWZތ*essv`(B,(tOez{K)yɮè/7S5ӥU*pAcUsK & "L$H4sŦW-LHiw۔u&+D)pY)GadqVރ={ܝN@Q/wb:`os'߽߄&^)K;k F:Uu'IFfY6,˛t$E8:IRʓ̄_>32=tO i;4M¨ ^Y;rZՁDO{o9։Q5a#Q¾L&QJ2ߓ& 7!QC/2Ҍ\2O7M7u*-{ܬVz)tL R§XdyD_^/R 4U3s g -hlBѾ&.d+`/С3L׮fr6|PMF#,ҥ:Aߕ⪁yװ R{IyB_j=a-7LEqHAb{*V6" w]T2"}KƄ0~UqQ0% Pw0Vt(y`4kP4]uy/[KbjdCׅ֓:lL[o"I+]ɒ"fqm-_mtԌal}4S;"֊(ʐšJ r9{Z6uyDyCnRxl'jbjQHQT~q1Z &-|ُ/Ky> xjp>zU*]~)Mq?k5ZKeea2KҶY1yfx7լ/9%/ '8\ Cme },],`'C˦e^}/Dz;K=(jp4,T`/:x%TAQqɉoYY]{?Ȇ@vwnj7!gY2[B 'IdBM,AwC_6`Hsx V5n%:9mQmph8=ȨCT69B!H3[߷쳓vfiL[LPmI{T.;W=/üW `Vg4F{o -;_[Usl1' T.l}3J 52̒l`b*iǠf;Z\QB rJϞPra;QP'iz,ZWI!Ф2Ta ɀwyͮcG'@TGm5i@2_L e!cIuIMr`ʑ_?ᦻK0m@D13V+ xJlCG(+ZI|/;6vmDD4ni`*gA>͗V{kYVltE:=JiV:t<O9GYK7b o Z,d$uJDњh_AZ픒fFaՐ =YEs_~EJQ'+|+7n8㰕:H:3>ZL5PGro門gw+DjW'(d(@ qxhx"h+RdƷ-2*Ge&Vx㺔PMLU>pvY4. =1iEW> C.^ 4҆AC)g@Z< Ai(Kg/Jhy曕"w윅 "W!dp ږKڙ0O[ 2H/{׷\Ȏ : /! Auhs:wו]SҀTQ5.4YAN]=Á(:KGhͺZ@v9nU.I&sȫ1/1DŽ \D E1+RjFIfvs&9dC +}~p-k^{y yHtO]jwp0{ky>z<׶jdx$n645YtA kUceL 6zh!u&w##X,X4I7̶>u£ -j%^[lBT+YX;ZPsinɲؙepT#؊_H7np4,a7Ya4܌.В9W'|Ckd@d{؎3#^&$@vyBk?ƪkT_\8k#VF9J j!ܭޚx+nbLXy x 5@+=u)-'k4n5%"z}=k1 >;fa9iHę?][oBXF=YU%6nʭ媫Ut2][s*l☝u%1̋ϱt15jhyF vZ)hag4? m>hdX[3S 2£Q&C#C6ߡܲ"hVp ERiMLwFJx\"*jZ<46yb(1_ո7RõH"kG #2Cja/D3[v'w$dA%7%k- mR*@[ 6o۴774"Vm~4,.L7&H\6܃-Zl2H}6#1(Qnc _yp4-%gO7>^gN3p6E֋'>pb&Ѯsg딓1]G`6{3!땓1 1`cפQe(O&6ﶷ)5TgT" L7+W9mZm6#̌I^ZN_5řd8je4)_`2Dv ;ߖ{#Sn]ٯgY@Ir~ 0r A$i>r:,:kOn3bdU9(j g{E`%l ] ¦_m;t2kؕ4R _h2硬YA-87Y,Z@>2D$n[&D K`l瓤AH EB!(9iN*_Rf f5_d5!$ ]`m. wXOꀿvߍ5j$*㪠5K](s* /WG_tQO\`LeѴ}tޑ-< u2ݕBF<5$Mbg[ѧQRvAEEX47_ut JPx+Ǎk h~)3b488k4PRjLgyzL jzƳ'ʨ}'bH[$$!fV x[^2}3]vAkj$,r|cZsxIIӢAGB0qqmW.*KtN( 老yzι۾v\5Dp`JL%{Tmvɓ(oNm'VQuW!hQsgF{A+w>&0*cuP~Y.6=V!7;fb ,:Vt?om0[)YF" W#J$U谜 EP{ p! ZRWj"pP?q›d2FCZUYkx@GVܐ j40>IjDk'G uX E|;/Rv{K_f֛V,-7tLpUGS$R4<ՎF".d8=K]$k}UXX^2i !@p呢apK.Cz8RK8.ukҠ6Ȳ}nw[7/[f'TՋ~})ŷg_v]Jn-!rNGj!薇 ˻?z1U19-JE >l#j9yi<; qc]fO'KB+)-H9)9όey/H3$ۄuݣmxE5D5zp#`6,,·\)DT›Mx2i9Rhi GS3 ^xYl\Q[LOh+'e!XtN Z QآRȣ !)HUz~7a,8|)T6m@@&9GvɆ x[@ NT0q1?ONSX:@JS>/-mt;+d 1 tb#xo K4 | %V8YERz/n^aZ+@T?6DWOm̓"HᎵU!t2|k`pd;hF$YѶR s0<~53ԑKbfM|Z9р`Jm@EH2|Pʰ#Tԥͫ99- XX41&UNaKg;U֒ YO*Bʼ5LvmmhTIF)Or8*\<z&6gWJD W@ +Ww, CD"DVBv훭$?I!|Pm-@ݮ/q_-EKF׋Sc:wDL˹TeT;^ZPC/B"m}RB\0WfRRh| )}1,/ҼJG+9u7{\Ɣh:hxTlG #H LH6-=.I` ZI"TzySQx۩8[Efu)EvgC^j%^uh.GF[%ɔJanMDsPT*: r >kԥF\$Ҿ1P-S2./^dWA.Qp-B+@v7b܀icBܮGٲvJR:dY "]u? "l4Z `RBT*YZocA`Mӌ5=ƔX?msV;kd:1z;SeO > D X?؍˱2̿(`T8O}BC _5H9҇ɨ8|,t=[_Pjp\ubF duˋ>Բv0* LT? LnCH"$:XB-bT J:!t ,7nuzӤ?e[&h(S2G\ 1"Eő9L[80uB&;?r߮@# Qe/i.)!I6(%.-ͧ82>3@sf3 oص9 'Q.M<"=M>/5C3RuVfÚ:ї2쾒z ;RC_0Xf䯻 Z<&L*c.?ڡ@W< gBSKCk2+)]*1_iRԚvx?73 .Dss)vWgiU&4*6!β3n}7QEӲ..?oTL9C}5@ɚ,8k8&_ ,?z˅#3 ϫk^`hg?0ezA ~*s W~%Va,<%W\MS?_+#(p)S 1^$С]J.,>}4N'=baWIغ/6upU}Ÿ@O59|0P)u3z\2[7hWiIQ|ksYa9Y U2~ Vt~G) 4ZjefmG`ҾZR7c}SadPt\>pfRb[KHB?X:] aZbG1bLB!5Ou#ϦdN {tqfc&=1dn=Ƞ0\4-N,)Ԯw6A:L)^|28/N2n0-$XVK58/chu5QB&<- ]Gqՙ% u8DP42Կߌ5SjgNޠ{Z1f <R5^q46YHMk~@D䋁h'I"{k)*Xm=ʶdKFU)r6:!F̣ɖ6A쵇ÝZ|<#U:+Oz~X`x|9iʨi"zrSIfG#s͸qi_gkc|& jk٢~hy^$q!rV$AsӇؗ>X'<6 {n|*4ir~{ ihSepL|Udɜ<;WF#V(B,29Wqʣڍg;3Y@,QO:i%H"QqO`VƀGDXaU=׍k'R=c %>ՔGD|F:ڡo)ĭohUEu4J5“X; .<Jg' `sX>A%f4(ϡ=;~؈GpYztNm3w`6,E^`|om2ZZzU˭c'E^ujo n##zldgH]WXrLU<ƍ!2dkvFu+W)t"'>!wJf%R٬܏\1MD!y͙#ݜ/z[xvmICf*s(Wy>Z) "7 u62||~Ljn0t$$sg~#2X]ea-ML7"#H Y] p^mD3BڋL:=QBZ3#aG];i*]qCM/dzBw:V4KD) mm 6f+Gɟl.Kq"jJۜ"S,a괽FXFf(P> yrWĈkLKB3+ tkc 2qY*X@} c6 nZԱlte{ F#k~HbG*< Ba=&zd^L"Wk38f;D&jz=&>̈́i1j$^j+\!rƘpIJ6da\8r߹ ^@EsyS\`g=QvHUAH2 vuUy2aC`Qԃ'D'=$WMP!!/\(#xw,jʱwʒldM ѰtW8-QL"c6F6Mq[txj  ]D92\XsD[n; 6Zt~G[0z͌|C%yV 6uh\BϞD@g9lQUK|;# |]a" hޔZ:;-?EВ(Yx}^Uu5N֘h=ù(V'HKl(qԠdԃp1uDB58I3RE7Iy\k6TY;njmcP%۱1R11Э<% a窚YLTf_&+V4!jK B幾%wsճ>[8^{b5C:Zmw1z<hԽedFâ\ ,E#nw/$d$E\MAa(uPƥQ ZD%-s5|YѦ4oE)RJ|vljZ\E犵^k0ivlLPn t`m_6/c:"GȄӡKA YswJ+F?VԴK)q֊ Ts H91(NY9~=F4ǁ 2]sBL Y[ <تqh#K.mXX_>Y6޲2~(ԍ|eZvYi͝E4CB‹m"'#Wq<uE.6'J'Y!4IuFLjg)X^":+N`c!G嘧zue C:09F{Xި+OR䘞9В:re > D>tx>Gb_:3T,#QhDFu(JzegI:0,x`]"SZbĉ+8?{P[ P0"k\OWBjX|2ԐljUF-)ZrrݒPhe3%r5Oyf;–P1(Pշ^7z .ڦ?Ȼ>Ka6I,ʡ߸*6! ]KW&M<_yУU4rw o(΀Hgllȋt[h F0TTQ7PiFv>^g̏8 EVk_ rz"r0мtnT.6Y$~QЊQe#A V||&|gcqd)iU l3EMgPV{'?/'v*u*ylpF S =R͢-_hV,3+v%u3"qs l>ȒJ׀+`N| s҄T".hWwK @K :AwgK:إ,R˜ %x,  ÜE]V{oV/`!ꨀ?#ݩxg=J =Bl\)_a֜/G}t$ *HS90154戹.0𭊡9觍TX;71 #e7eLAEj ^9ONߊb'IyZgk@?4%R}cn!ywVa&x(Y :,0RFgsnl*JM8lM0ʬҢ׸C1Z#fJ0F1mк|.)Ą03lٟ j[a0!!BgJ|ӒJ930-9pm,fzbX/Ƒ<]DrlFExV)Nn$,iȈS} [=wI|ÎN,~ҭCIo,luFhKָK%kDPŇ ū[.n< ;a6/#DNa3W(C./sFdI~[s$mKmlY-_{!V7u-`խe?l(!/c;p13%F+^\de)pxLr{g0.))\QJRO  KyR/3zyj789\]F3]N{S^ޕK4TB KMSp̶"6i2@y>"! 7Tx=Y/0-v~[ѫ3ɲx W1Y=O漒K4RBU*Xddst!b Ms |qV) 0p*T^)v0;h⣴FyVAw!oX N}m.Km`>[lY2؄[nplrѤhAJUHbN) <n)+3C`Z?63$&36x~—vTc+>c>̓ÀZ`ټ]Ն68(yD/apABU+pa uyʂ31g ]D]}.EW¯Ј_svqRK[Td1b 3K™MN~ed^`4"37Y4Y(1jok oi8Xu0GΓ' |RO۲Wah 6DfAG0_/L>:J׿mH+jRCs"ӪOETFKs.KU:ٿ&aNS?lH8f4 sIſNRIuuID*ͮVQ< Nr6}x:oizWp䑺c"pM#\x9aVaV2H gTmdOGWeA9WW$LY{N`t4}~NAtT`7/8mxTM.mUfU[0lG?զ Q/ĩOsڎyNBX|ǩR*ARdzm % "Үy@?41B(݅RV M6 vQϰl@ޚ /I Abpg)}WɞpT1m,DnedFdXug$ltq#B޾A^{|:1:N+I~vG_Lŧs 0v`CPE=aXG20֏,)RsN94S)Wp[iL^( #嵊O ?] }Oa)mu*J Cǥ1Hڇj-'~t'.)R4vO3I \XVAegV#mMl&ZUwZG2*ܢM$"T R?xV60|[5*9q$z G9qgǰg*WL| ~v!u=Q73vɩU a$?=N[3%5mɹFP `PT8` R_xgc >o;_1-*jsc @d<<\XBԫў:2SJB~v' _( gO}B˱ӹߗEQqx1og#I@qՕkg*n3({!nyxs)x**K?Jt=ed93A cu_Ȑxq̞]_GN1]qtpb BeΊŲrc#viw0RR*gqxWi ~)Duy ^O0qRd1"pX A=K c#UJA Y*Rsh\z Nj-JM"d|Wi> 8ܮWRW}c"&zf L-(w݋::SbtVYNEL *bVڴNF`+%b$j ,L&7)k'& ǁ/Sŋ\Z( 'S$ILUf#d=谚H#* -p8sC<+ڮ_Z9 = b*j?U&@61><]eK`-/Xs9yփ "^i=KA_ m-)ۗe+AbQ]~w)/)#O7Isf90;"HT51ECQ iәZg3kd2mhSd;<`|G zX%Hh/No?%\#/ "{q-. ;k)=_ȷƇ$ŀ,dx 'D'xkys薃/R#eҖZ5X"5W";>쯮aߔi|7VP%6T=,p[9RQmEty)XO(WHδ젴fYEWr6+ʇ_UDEMbϊl9{cLg)}/(K5ҍEClZc)rf)<Ќh[ @lwN.k'ls:s=hh! dLUJ` cGI7oP#$Q\^i0:y ^z slOIHc5 i'zQn*7!Špy˻%!BTƜR'rx!y/xU?lmQQ5ci|`{9MF[ێ&{}te;zGl$rl/` EO(UC [J=`iw%WL/XV=Ȟ.)ǭشrS桤2~Vt?b{I?Z8 Fq'XSyt]*Q6x aS9'qWt|r!"B݃.`fzW*b=="L~Z] 3V4byJ"쓚Z MH#h[[\:ƌ4wHhfz_s},k]ÿ؉ٺN5jS;#ԽFYCήɸ/iYZq18kBU-9VJYy[F7֨3mAȒ/WgG{j)7h8dHaifvȧ<5,u luWo IgG 鱓3=rSa¦鲓YQ={Y|[lS_9DI ӈjއAkO^xN F|b lz75[20eg_z==@Kȝ^ѿFg5-lX'Ql:5mْsiǮv CN>觏:zS͚զzzbܼgt~ 65N0Uʰ 8 ]<9 %hҮ锡[)ݏB*_d#ﯳ.LbL4t>k}]m.MY--CuɥI"6`|XX,t©LPVQn-,3 q߲ʅ#ܮF=7tQjX7 JOYlHvg,и(ЍH:Ϳn%iW&TK(_A>b& ݾ8 !oөmû RjZUK"ZLZߴhۛOe_@KTRF"_^ce\ԖIif5PMae)Da'}1DWSo)ޙߓZ됬 Em&LNWKM*,&o*|,58BL 99Ŧ'.S*9ނ?Hn}dQy&gP+kPܠP ΔRC,}ib, GRŜ>L?PDptѲĴjiBB`(8jFVI,[ymgYc0[RnD]SV8×{P=f{)86RXy QM1 [{&5lcXV ioY3 -gBRi/at\+?6|x`y ѷ=ZWr,=k.EtVo:W[,}Dè%";[,U=Ȼ~ LuHcN'=sƊ Lۼ;ඝYf/K h8P53$+ERjXebfqɪ9˱RZE͌zʨ;Ĺq8Tv/!phkGc+ޤܪzĩ63r)j W]:s׀tզcz(.!+( V 1IL(jt"Å[I P}({_qbf$$jQ10gRz^lʴG=~Hǟt9*Qh(zBC8SRMHTm}œ{ T su kRbf;C:G#j6w:y<.ۤHl}MR 2R% 熘ޠOZP:Qxk2Sݏ<I\EKPԉNH;&gb"#xCYI,>^ 1Tɲ,?_$*Ā N5\c.)蓈GF^_-AE97NkeOISK^Y:p ujEcIa n!gtL*KU:>*x3Ӳk޳e_$:}/{Cz"5q<)4Fm>'dEaWֹq܌j#aXVf| gଟx~(f[&Gkl^aAhZv#" kn AsPr*lrsO&N6PO(&^nwxDI('@P- (AQK {{DC^N) sW_o KVu.2T󠎇{-  l?Mh14l"z χ2SuK_`6K߈Q_UDlNUJR \:z#ZkrZ^$f?PYȉn~y C@z]G'~cL#2 [ _| Ex<+Q9E3`k{/NY3%31 qG'#%E)]zORr">6pqkQc6{[o=*{JkgpQ= ~ƙ~%eVTvI<5"I.0DCB9Ro'@ :"sJ{F{E'+c$Ka#F8| ]̰ұP\3\$@qw^f4b`Y09>XrFqf[R^]ܮ]γKQC`xC]KKs­P-/(4 ]lhX~AE>=Opw)r-K9W SdqM 0]D$0!yEHHiޢmmR~kYu;! Ѧ5ww)_֋8k9eכּLK"#UcF 3c):3Pm ٺoQQ]pD.PPiE3hcĢ"0%\I1̾-Yw tZ'GQ H,265~I$73+$}C>7 ˡ']qa CM]D|ߠHjoLxF>2Һa*LK@v0XapT$ X#Fx}dȂ?bS9B7R4 &g`c~ @c5CLʧwryw%AgZF(cbIY9aau&0oP۟@?P螢&VKNRm\pqh4MZ= }^]L/z$ElktcWu";Hjܷlnmc.&X 'gIH.>9B_>ot'<-`e{B" mw|=FC ^Υog=mȒ=Ƭ?&JFk]Z2^V&cxgs̾4ݡaZҥe9+4a${L<5á~s6g_VvBB֡LFVӝ/+;g?[LB)m9lhA)i_C@ %cBBq fWqi`8mƙ7UV]`RH Z/8Ub-)YtNէCK⌹-`^ݡd8|wpX%сW< xkRgqiFO+52Y0H0պ 7{W6)?" -JK갽-, wB] [씡b*ڍOϦAƻ]sk 33j˯;.c-RֆR=Śfӑ|S"Cˋa*@eix+"AQGdMFZdr7O=Gv0-ވS^(W5ԌGOՎJٜu"rӁ2ޒᇝ n4P֥@E][oop/’&%B0&~Eq*tA3j{3,olxh3m|bJ ~bbzs.UVp30#V>D+Xݢ/?!,a U 0ym Fu>y70\Y^Z  JڶlKrbmvUX2$ZE;ݻV*|vwM>!Bij}|$~utQ2HEҁTডEx|]hW xK髖4X]?0O#_w(n=AʡRjC$x#+GFcG9QO1ڼc9s~ܠCոQ?'Ú| gs?] ٲ9U҉r1#?sˎ>Lu2˕-LaLW/+V/T4M ~xLNՔr.~oRobo$!)#T*P阥m) (rhMq_>E)L|V ɫPN]⥜̠a\@PgQ O_B4Neׅ蕬+s\5Gh2ē+nʨ>+X;CyG1❉ɐ&([* v=nTe;Jņ.DpUdt<]7mS^)SoE!;YjDm0BM}뫙pA }F3Mr詨_^wՐR\Kb 6b+sdžH^ënNs'tFLb Gi<ك5 7ח_C[厎- an!]`?d;I;Hl0ǩ'1 үhCOϷ0_5RW'~n}!Uy|޻tLIZ׵ue2/|14+2ML] `dҺd7,w=CK3(Hy|:c:~T44x",|leHPrzjQ-y|S[O2i ` JY`ѰY/Y}oj!B=/~wg2!\g$rg0 ầB~P{}-Vy\( W9'R&%b8뮃¬^:,bfAۋݲ5ƉtQ2 cp"Dde%3G#Q3ڐM4%9Gaہ u.`çmeX+@ rhX3az @Wc*q"C*9qF$% ,Umrk@Isa@RyXR7ZBA#5pַ: ?YaܯFt 2>ާ]\х}T:sq2ow g):<)A8ᲯUt[ua(D.%nܛYx˙T\P:{`L͠cla8z!K6ļKkd32Pv)Cΰo])޺krl k81;Ec&uc?e6 C:#F'#N9Y @)'k6 @egaZ4,^Ûʇ_bV8W9JnE _tĂ2 v6 SY-d-r v#-Y5q|.!ouA\= aw3llNە3U+*._[2 VzyChq"RkvJ ӗkݡBd)Eks0os@9bfTUVP]eelK@B/t[rz{Y;|h \P6 /F-E,8B(|{\=14*aUS}jdnprO=uqiޱbq#GK.r&#E`' OЀa8͞XB m6%F25xnţBՁ72"CQR4xµi"-3ehB.ۉYo-1Tu+*3Q4) w˃7q9uP4-? jRdpTgҼ_M^75hwCu2L!}[9 1 n jm4 LFщ_ZLICM aoؾfLgGIahks4~SmW+ "{ 5|}R+ڥZ=$rnhJcw2吝3!_[,! ́Oe15fc,v^< Rt71cb4hO7|j7cT} ޼w'MT;Rtmm*KoJb[;%F+Q?3#ҧIb40jt}S%% .;M@%n芗v~t!VgS~IZs$' LJ',i+9=nQ^.Hdv&X*ZG{x{t'U*G^r9.JǛO&RG~~ t+Aw8q Ε:2dRf>G ުx/~ oM MTT4dd~jnצE5fC=bH8:Ϙ+^فq. v("=78 `*:t%Tԑu j"ǪNhu]b' .95_]=@x3^h3ѷa.`TiLGdcGݔv<<(n,z]!6 lhΊ5=pᬿ%ZdP"!;[v){o$ bAx<)>`HsFCS^!S˖uæae*h ,Cn>0-IM2P0ʙTy<6n~;OTЦd bƅm&EC?B.a;Ӣ GWKdo{:qG:|RZ/6x)' Mqw, ɲ}#f\KwS")mGx=.Ryb R+H2**'T` ]dF kH Ohv:@9bbq<}_NBk[|k9#gkYco@ d8z!p3n z&C$zg kBGͳwtݻc? Uq{(TyXaݲMJuқ {67 f`@Lr_{+`tnR3/╠.657xI6C.QƜFc4 KHл[bY ǹodBAB#D%XzHrLI(.x#y&O796 ȧrn->Wt'zt ٬.h>bv7*Oҹ_ k5@2=:Gv Ú,vs!Kǵ{Zj"?xU ]PN,BB6%+?i &݊fq|N{ ѐ\;uҸ}@qWgj K3G4gvcnwb2 0|4GlI#qH@`s{ @~@p ;ME#Mk{j%; RˈK*Ċ|@#R"w"f”g&-ⴣ_^"A)dǻ/o9(ʘ(<)@3xխ\83\Ita-aXgb&~.(TY@ Ĉ_i{C[:| D T8+س==Nv 9КsUyzj-D << Ы }J(zWߠ3aˮc]r]a9ɿΰ"߼MYH) (ܝ3~-/X01nb2/]@v[h׋x()$:`f/FOOET_qQoN/'lԀZc F])KX/-m4k);A W.=E8V͵Vu1-xY>e/{S(>[ߒw٪gs7hLRJ&(Oǭ=4/?g[HK2y4cud`ibս3gS;VvQPVm~ï\ײ(Ǖ-ώvӐUeŞ V[}N9f4jIo ;*M$ULwekM$]@<`Va2l0f:(Q0Ú[< kLSJ~>5&?LtН`烑 }b''}&Ħ UgE> j9xu> ،af biѶ1FW}B fT}p9nO/׃l=URB pb$hV6nisMXs!/@2M zF[0x 2G2iIPZV\~W!m*^Ĝ?;Q'M^묃UkZoFf]q 4E&0tQ2֤9L0jdgLa*vlP5ܖ3ʄ}IyIzW,Z>_Vי6򈯟$hQ‰K7N${ :%[̭C=:w^o g|@>'&p|O8, Qg7G< r\^G6i2\8k'/Xrå?Lfb W7k ^ *qUco9ujJ!u.uq-<6R8.3ש&D@my> 9 =P'qZ0ؚ P.fm*G<%]Arg$Dv2$eNBlFc}`z}!0|c}a)p^`s>fh:R'HVJ(;݀'AGNۉ_VVsI8~q@oro LߒF[{@F~ڢ*5}՝ABBϳH1}2zNjLΪMK G"C$KIAQ^;qR8aǹ0~Q:e@' [롩qn75}ԮIsT5D }+w~Lj"r ;=Pp 훢E.'Ң#m}Ȉ'\Y7omڲO,^wɠQ/Gz%fSAԠ9R c!X(_vuN+i9;ׇ.QLgA|\Q=khV}2T{ei<dAk}xv[h-t tP2.,#s6<⨕Ϲ5-g~Km vKto@Wrc"W2 ;~to)v-~o8(;OEv Mˆ؆1b+#|7-Z傻@i0Fpj SL4Y'nڟaн-QP%v?J0sdgV >+OGH;[EΙʅ0; -qrA56!ΉtLoBHqR #Fe&B+5qQspx v1w:'vǢxPž,yw1~ssal+AK_Oqc< ]A84 !nOJA5tS2>ev5MNZ]qYa)2'C, OJ֖O1X/rM46oފTke73%:T#m̈́@f']=Yajse|Xu«xzڕ rH4@N7P؝]lQݹj= v:ҍ^n?7!f0EEp{R 6ªۍO#Wk+uPL6;B5E7_Хg8XH*4W6U:b@DoVC3Bj\)4hjv+ "@swC@:i#@Wb6-Γ1ޔX:2l ʏ̀7RCwj1S:C{2`Ufe UPF"rm6%rHgdErU&giLn2q ЧYGDfRunab}~20CaQ-utN6RtO 5]SGTaŁrrU>L0p/Gf"@ד/ݻ^,s~ǟE9+V&.!'f٧2V%نUgX<7EooX䄗Gh8'uzL7*܍ oz̻eC#i_Qh0۷\%A/٩K L}qG6X3n0{2n,$)4嫭uxM_'ؿ óehkgOaAA7`6Г$QQȦ D/:9)KޏȒ'n조Ĉ25űjѦb .Rۘyά..:HgPsfUȖ@31)a). 7:ޠعeFׂI tb'x!Q%Bu%L|&0`k[p$W$.u;4R 3k541E`xL\@_mո+&;'~wfŠ5W0)A@%ɰ[LPYO>1I,k"7$N>pTo/V)U}bvui 0\Յ{f\>T{nq/V\hq(s2l v'EoN'6V}Gzxc|P7!?+8,UzqCfj)M=dzj%Z-|{hZuJ$fydaQ%}p/ ZvgJ`op . iYk&8%O[R**_13$(Z_vd]R$UMӕP?VVcY!v_+pX p,M [fG0cNQ8x瀈p|e&×ZIcjAS7 kX'@0o _%YX˴nstsTgf!nuuЏ_JA Zsϓ=*SEL3zm ^0Pj9Ɣ#7a,r4t5NTe\蜘IRW#\8[@pP5ByY ZSX{;9 . 39{+]k YG`0['[Fݑ&FO]̹19M޷GntJ8 ̉34α1MJNΦ G Pur#W[J@:+(AH $ki'hgQ ދ.Vi64Kzy9o̻PsCQu2i[ s,n{=wiUؿq cB3(Lf=Gx a)+F+vqH$wb( A\ssl@K7D3"呰O"< rվ*iu!k{dT8xCDcB{~ mtb ͔%lгoyp;JN~/uxNjfSu Z*l0:Kb @?+b IzSȤ6# G$~`C 9܌t'/;=+wW$' 5V19SpyMy[zG3r\Fgvz*ZT FJ86B4Gأ.- p{Va8> XZ 5Lr$/]c",N /pSΊDrL OG"ҹt~RBOzEaeYgmUgqBFcKu&\ϲi+t|2bCYݭWԊ ,;?Ácv}dŁ3E+7M[WE> Ф!& 2Zҩ,-҆?+ьg D`Rw侤xѥ4B;yvpfwG`c\Txz bl(v_?;"KѤiD5hעy S Y(@` 1fϣq$[ BLO SJ74+`oO%gyh$_#ZLXqvLY]|ui`耵X0 _A%-֓[R9 (=c\s6Nlh&O:a#J(-%V›߁z_]pYdO0I^iOa4^P9IQ'ϗ@/ʞ<*=y܉`BEqYf0)T|vPކ{=Kg=ÐLJ2aA ˿i~slY|Շ$%*6QPlHb׵3?{gpcE7B :ZR`MZ4@]MەInZj+N6 !LJ{?blZٓ"Ή[sZF%8<-kjo +v?$CcZe[sbDN?;C 5fbI= e!@h,)G|< C]8*͆._rC,<}Y x eQң,m^ƒ y*f̙~->%h9p=Lg@ú1 6(L^r%И 1CR'^ŒzQ-gmdp7b @i¥.?;6>nYFM]}/ض]CH"2m.Nt7*~` ‡E":ϰهǓ㹴Wsލ l\|fX S%~#np9iG~@Wont?jInd[3ui) IX (Q긡k58 f(E ԥN^4_vpM[L!:nW ~'ggG$;rӓgɳ<[!Tc>w#?%4=ym'DU ?(?`ЮoN/7Ch+,u "IelϞä4P9 jPBg@%]W+.lGdA͠ʳ\+ډT|]}#l8l0\ڟ~<"AIZ\>f'T:[O@W!Ӽm:nD._n[(0:As;{ d yT}~a~] Y gT33Z'#|6ADyr'%@ߩfQ5@*wH\G8ukӅe{*jֹL O_s" rQ%ŨB~۩ْKtS7 A\$gm žcp̱73vX< ͇+3jL sc%etڞO8PdE9k; [O/քQw*̈l26nj;o*JoGlb,L%hhlpb v"і*_ݗ#ϕ;UQs^I@_\X Q8˜1f?ݴЊ ?oq$lQL8R5x&xiMVz|*v𗗹4 R1_xE2˽a΀a_f2 )BP{B9Da%iٻ8M#2 Qgo) ^)i sYlLӽ !g&x7nɪF#ZZOǖe&~%ݯmfsX (2eӄFZ{,6=s^,w=r;CrcL]Q %rRz*;bW ɟ~ y18U%,ڥV^n'9.<(ޫUzy@2xkJYmD𷝀. }u0{\6{lҐij@(o U].m4$xi,|rgON> ?+a7dots]JV%qHGyw~䄝`lm&/A5[Vj{+З<=<[./n;yʈx{V&N>rzIވvrQRoT! 9g;%%!߳& 0jj6/ӿ}N@ԛg!H4yţ[`OajJ2IƤ>Mǖ v-94]b+.{ϴ 3ػQ}WSM)XN;4`LUVp#_.q{Vdz_1pUbdi]֥I_0ar4y$'*[fPo_#3-!́ |/C\L=]E{nF8aI]B7mV;Dyfm^qآOZfE'kƘmC߹dyATРI,Yp8**0NkRzFB"(iw fC>8:TDXuCl0om!`umceW qBj3^XgzfS82iwV*T%1WM5Ez`p&eYZ{=їZ֛ PD ~hb`v Q*(<֟^ڽ&c\Z*-jG;)vcRnDSƈƕH#xW BMقS&ot W avM[8cq#}rlO)qSqڻKE8ͼ*E/BSj>K $_! q]RթJ R!>rk̽H0FOqQz) gkxěF8.29v4PN\8#c#m R23yF*?JuT_@k9o̦]2}J6 F1p&&I"ӿ.8rsʵ|q꒸5ĻR\{ &JJF`$%ao2$7L`sENy+U4G=@P&k\B֪B˚2hW|K-:&x|c)ϚeNzΰڏ@Z^J $ujUUȵDy6&GHugUț`k]Ky R43  c±js(rzQ!S2ԜYeF-76[w8헏Dg_WeO}xe)oӽU BvzbM SI_^u"%C&h})2O0GHڬ3Ƕ^ O[^3on۟%U!H1ĈJC o~,g}wATrjB[*"#F{tQ2Bp\}ɂ-]I Mt{G&3YBrx']-dC->C~.<sjF7g'ÞU\aeAJ"0O4S(W31"JM^6*):ކ9%I>|D[y*Z4=o3Xkm{Zvg˥FD )mީ0v{(UҢHG p6ޔg~]=o\bf_Qrl5[+95!Rһ[ ը&@. bAO^շCU'gJ!wcџCdZ 䣧f6G7e\`xz! s [p^JZmW)})%cWс]Qz/WnNU+BJVYcG>v-#D'zymKlv|G$@w'6d̳BB{b9خ24b o0cIs|A%Ԣ[bA6%&DҐ8x` lٜ01HWg◨u[)Zu]}@sJ՗A(gi)<%K<߈J:ȳ&ƅ>[qMQ4dMc-kR}mYVveVsU<ÿ,O2!7\;`}axtNU՘GA@C=Oe;1~^ +ɉMAsMF՗ to!i+E)%5Wh`I!:w,cNч;z ''7|Ԥ.z˗݋⃯:b " j.!I ^G6 !Y3 ?/#X`2axvnȊ?BFWwCB"\; xNR`8ͮ3b6#_􄶫`2ZѼDujk $BnCXlĔicnԛn^YqfD^^@zF>Osj?ZaטD+E6,צs\'C|#vjkVKW>Oo2mjymkȂ)laz/OIB2oy6̤d$cCrrNJ"8=H&x4 O~I h4M]2#'g-Tw#ҵ-%oi2 Bbޤ#D)X<UnmvqMޱ;OV_ғ['pc,[I;v?r%uolZ8g;a?TsAMJ,!@sۻň{M6,b+FؐT)n&+Cs&s>zt5`@SF@6"K4]?ND+Ea};Jij wz6b*897R G{W/dè@-C +Cxs?[S9(.rw9V } Y|;f.58 L/ 'GYeqk25lUC#0. fw,IЁp ƾ0M{FW5TMaG򑒓a z_!f n[@.Tmn$Ih(s{dR=Nr~ %nȝRml7^a})jk it0;rxs&Q?x iMK`maڸA vXZB G9,tRPvY} VVE8rߞl M%6U#N+z4(Q5Uɲ'v5`7mSm:GHD|;ⱸ) a;<+d( q6!  Fō0#'CJ47KJ>%̟0"+2yl 6Bdo$ px-:o $N< D$LmnbQ@̢zoOTuyrvozBIȪ.XY'H{VW5Z2Ol\!6:G&n˸gc,E;㿗4 #Gܳp9MJ wM U\"oa%[~:LnUEހ&DʅRP~H5vd:BcêqyvtDƚ ;RPץuG猅};I@-`~{ygA_h̝;Ӯ*?R'C :*_\ Vas`0N5z3Ab"31hSu5]QOGL%vdOճʣ $`a<T:dթ q _U3W^Mm ,7\Z'|3ɇZ^g08As_% ^"֛m占rfFAwP-=.s᥸/RFQgSFBNhm:dg >Fzʪ7j[dkΡPbuA_s OO iCAҲEo%*RSj^'#M#o> [-橡0)9k?\*%'0|-rLlŢd4?w!43|X_DtNXMp l5n5 eHkv]ؕ"]|Oݙ+ѣDzX&Ζ@\69AG|ȴq5U2c&W|]mBOQ+L(Za֎a_\segx$6w{fi09;*}#..=]Rw$i;GwR^ l8ѻ6o-/ZB$GC˵;=BiFpEuXBmS{!3w^*fdd HOk<PĶBZ>̰뒆_uxź\wWLiӱ? WԸl<"浴5yxa$CipđU(݈ hWzV%Kv:blZyҳj_AЛOF^Ԡ)3U 51baW9|#P-p:۱*O܄!Z<*ۻʎ'cSXooMyÌgɩ ̞z\qW#WEַS]~rj*m\4ۈotVT/"W`Y:ߕ6ѝ /q5B{J|yJ5څ*{(, md r}6u-Y*Sꛖ'0A}e G,R!ЌIH0|ڐq}'dʎ |dS%X >'OtqU[*!P\ٮVJ ڙWr\c+j$TߡF2e*&';lK=Dh:b D^08w`=Y- z=[n|UW0W5)Ά_k5YChS0\=rnr~z?];Oms p[^y.¢3/E*YfV\)rҋj.P@UsWP =+dOs}C1ɾڞKnA.Gg:}lRQ!t$ X_itv#m<7}zP.IgDze#LbȯGȅv@~Vs ]Z5$tTqks=]b/t&0EA-ÉD{c>I VM 2){ct;}m*!T? :)9ψQirlNsَtQ0UCPh/>G}gƈO낣P;W (۝0vG$u,]pˌWq2{HOvQ)^ fiI Q=uR'=Ns{r0xЉP ^&zY[v&i/.r2gbUpj]FT-gkXֵ9RnJgԄ1yCc` WpxQ۔aZFq湀U*ltC}DN֙gfiF>fGrn}ff'ɼQjN/N4LStCa:ғ;!:MFirDP{R2Zw~ yu\'s'•D0VW<36+9ej :Ɍ`MYn9eл;JI N>;oXxoF^G#kg>gyXZŌTz\6` #"*HPڝ&;k< 9Pm 5qZ޹-o3u`De_ Ƿ6K +fY"9PiQ-f`*FdM2TUyfH,xAj݈99f̵_py[\Pu0. Nђ%&޲+#>CHj-4W$:My^]2p `髠a+D!lۇpw1t ajpAn| t=RH}M*8T9a-r782f`ʚ~dI=xBNplFĕB܆ -6CNKSB4R5 Q6"8S\Sn<Us)5%Zho&e:c9ENi6X /qlXj\1ivFMGcQ{͚ ãzzxv:ö쫲?WXQ)mwgAi!4`ga5,'6`P>YeDiibE{i"=Zc;HaFw㹹]i X4n R}xI+8gwKCT% =7+>'#f.-0H$ N4Mt\p4,Bً_ eV'_HȧUx_7QTBDΏ/vtc ;U=L,.? jBKnm؈Q:؋yhzUXխ#L-1uyBԙEIma&+r3}.xZjpd򼺐roP6 8ѵA#ks>cv&1)ð|}XD4 jWĦ.f>x>|Lh+\Bҫk]ߖ-Dx`H'/:%21KM, 7A!D >-(߷0AKf7`@+_ c#i>|Me1 _v<"\$0!KSg?J'>87cf>gȋXUɞͺ}]ycB^>1 -ȴI&e{$n$982`.23IH6։h.޳nIA|>+XEH:궍ȱeѱej rzňGI|K;2A`8Z[֩kaa,vnu/8. !?C#n|eNUnBe I?:'pɟn`+uY~~䮋8 r 2S^g Y"F] B퀃!N4&D,ۨQ5÷>C2iÖaLN}.{vvo)>8Vd. 5б(L;~:x-(3X7H@簘rǑ!o {ǐ|겆/Qr03R`)O<L%UOF#hQ궸qP%bURH,1GߑR'J I$B4iPk* 7%_M% vv ~%4S:Ӛ= yQmkn+2Z.^ /G3`8ؗ@0k>(˃Oj4u"h&\mz+FIL6w `@4v`a@_ۦ7mb]_H7nzTs~^x(I=cO QYo84)7սRZq.yF:8 ;A=]7)edupGV5Ԝvc_)`N٥&ڳ)5_ommp63y{D0= ra6ϦIIj`~TDŽ@x|L߸vcQ45iG9wvϚz# !g19^ C=|ߏVZ )2,𹟒 e.2;c MgA98̢oӮYw۽Y׊û{8?qY!XJ*|@o Ѯ5Z RЙ~iW+o 5g)S_L{[˞#WBb9?"BwƐQo៮!9̱cuwOH# ,Zch!ٮ=5 L#;A- .彧dm&{#>Է k='/8RlXk,@C*u4^zBxs7*wߘF$p1Kfڲj&X*5NƈbAKTzwWb3flNxq ?oDX֒@ƭyH`Q^nl.d"(֍7w;2ʅu#˅ki.H,y~a泞SG0(SLM\!=B٥8 D2ux >ҩ$&FAap@jCc95QW7囖WF'jFixLh;"J ySR844o06>N3CZ`q# bV)mCz5K^U&bQIч8OmIHVШ)<BKoH$-iQ ?B>rkԸza(=(o}oqG-ՠZo*v(ry [̵zi1]B#8A;E@ Uk![☙LS{z*sJj,z2H@qޘ y??َ^ uU?C0k|Dg.Vc5r̉衦ϼd-Tاld8WАtzzEċ瑋Art' sԂ)T"W槖'"Q1\gw'VNiBP`|gڝ5]98{0ctXG3ê6b ROOpb"iUdNCI|P )15iu@TRZ*=u5gdUEnF#ק[d'CҌHz]] 萢wAoȞ;C8;ĤRq 3c"\&Ah'O ^GǓ#2(Rv' +R oo qgݤSg}|6R"[$D5DU>ah]'9t'uB ̉Ŭhoas%}Z֮i;N Ag[-rFqXʗ`r껵u5TḵEL@VπrB[e L4 m BiUĔ zld.#D-u^s;? {hNL@{tP,ħ޿e0T\,sbKZSEtxŋLp50c@uk6 rۓ孴t9 -efsS5!!]y+E| IjEhH3pݬ{19"ta~7*lc\sѯeƮ<|q+ΪibuAP#U E<gw6HӲЊG움 @S>6" :KLaxowfWۖԻ²1vc x4Yo_BQOj8Ig  OBh3}}!#@.Ŀ4#"0e= M]:Ҳp.K7~6z&+k12^uÒf2s %jgTrFrNS;9KqQ"el>9E;| $NC6cVӞwG妴Q6}_4{l䞒 ̩`o}xޑ`w-{/fwqPHs6߽ j71J23`/v ݑ^B$Π( |u~%>7S.]);gcϿmY*)HdC3¹9UA]L0({{s_\muB^lE"oMPQ '4#7ٶ{ODCim2 ߟ!88/9߽$Id,8o#=Y]] h=`ԟz$ċ)8ٞ1T2qt 7Vq6l0bG1@#XPX#[-P^U{Aq8pxe.'<h[Uȡ#mVlOMn|#ϜtU,R 3Rˌ|f %k\ VF3r3:uFaYlY?Y9R8CFw_˭ͨ~MEmڤ>EO|rT1Qpckr&bDPa*6ʇ|{NtdRj$6}8]KnM"t|WuxC#e5-"(P!:z`Srߞۿ8s~=&CXT*=;.b`g9~yDS8PVpX$0S%Q!o3zt5[D6xr\c8L+ >,Lj Վ/sQ?qbl\跖}2(ºQ-,Xenk_ؾ] ~F#L84 VWp]C#|N ) 揦I2 Zx)10淶jORFvōN( 'tA\M*Ę{ijZW(4CwۓÝ{t8FI3=߰ҞK²A J /碩%mBKybay8Xə"x\X X$}" B|$'8a NKGGxnm'`AQ1AW1uJHE펓19!TO3#hda7DwV& m˝3(!ܨ7(S(B, eεb?gÏy!pelzRJ'(X54,9X&K?V'pHg[r-oqD7I6VM5[ۯfB1vcT *HZ@V]jeпJU;V?k%*fB0Gݾcz]~]gGGY2]wL-[q&척D祭1H%$#q>;]8j^K@r=[\= #)dm΄4ʵ(n(eOizkR0>$8@B'k@:ɱu5%ScPCe#ϡF1Ί!p8^8@Fsfh< 39 # 6qW0 FD#^*11=WaϢ4cq\j;^'".,SZPT<$ Fq÷ɮ=KPTbk=l:;8:>HpqsD dq/mxĜs*d˦obR?f >ZciK>\" !m}ZYC^f@\TJ3XzQ(PH/bu0ڄ՚@g:#SߧijFQΠA. sO#ݛ_ oWǛVO^q)R[s)-g[VMMl2{GFwL}go2F9Kw}t{5&: 2N;zKaV(eS|r.lFr2`vy1>sxCLr?m-SIIB#zpV 3 х@8IN*|< ˁ]1ʈ(j*E'"OdS&D"tQ׍q+.L w=Gi+s66LLAVFvuOq,(؞|F۴&:!Y\ץ/2*M+B`܂9&eδMA/Y.J(_xHisY?VT-FpkDa.Ԙ?rN"8c 'E􉂰:6I(q ^x []B+ZnDi[;=p$z&GI:,1zth:b:XtݕAVFQ{;Nwf(k~;p0߅ Rĩ9 e0U>ށeO xH@yxXAd؉'Ns-׺>Be>.)t @Mm 06$tZVK^4 Ov0 HT&\}="3iQbɘ6:]-(ZXTgP\m5X/v~$E mF9':laz{:zEQ5s>?X f4h]!VAp11f vmQ4(3=Se7S'M{_~{{Bb}a0տLbɧISl" zo7G?WeN0r{ynIMda X5#}BnUg -c`R̞ {v L/oai <oP}l Ujt^XX ?r@Vbga5] PFTHwHG+qwʣۗ~C,";xae.L#VvD"YE6ea;a{96A(9sڇ}8GbrJCLf}z1d[}-tӗY6ÊvfiNKC<, 7cNQ!cfeuh1iax5%@Q]]s .D.M̻]RX Q*0)h̍`HoS2=%f@ zy-y\\| _吜XhM)XޛSvl2RP7v?Ajs҆™LԷd* aLCa]d>|")]3os:9D=*D\޳:8G7ao.D7NHbnp|.exEɥӞe5ܻV?=}IdѦARL69MeGg&sʩm $|N"׳}p9#0vcTع1[ݰh^xSl1'm ~!`.ߧX?b?j[K7 NhsY"b@daWJƲre[\NaJ֗E::T+nV%- Z;= .Fe/w@i=]|ވD yJ%:{o7|ca:mj_91}AJwLGc$m͢O01>*\/< c<^Om>~1 أrۙYj=x-졶ɿ /<)|Ԟjc'=ܼώ8 :ґ<'q9'^9BQ.rz_2hH#mȍ7)o&/VsɶIe3AA_E$Nq|/ƹ6`"%( O[6MHW \VQ+ 9EB E 7hu4A!*:܃,pNK̫dq%7N&nL5iz=N&n榈+k=/;}͐d`A;pHQy,-ƬQ?6GM`N|,8"1 \lSNGV365uy`Ή׆ccηP8daj)׾R@F$2ߗJ҉ ~6WIV^hlZMoa]SRA:V+ekH-;9e0?%.K1Cf HS.`؟PSd|%@`LQ}EܵL|\waKս-U1ݥ7X-ZYp׿_AP*@ɞϧCͣR3>oD|*"^:/s8 ˘z)x B&Ov]XuY`u*(kGx򵛕NXn?,1/v3vԹy6j+/?/UǪfDRl?TA//Zӷ?jsbчAB|NiV:4C{7*f>䌕OCkǂއV+cPzo`i~([^R3LLRZu§l0DKթPE3WQ#zH..g*)Rՠyb|Xa&^VW̌+=ʂ4ɲXKhfnźjRTdOelEayKhtzPewgfbAiq3/ @9ioD܉ 3rp W; Z[I!#qO+t3;=y4EFFT $ J=M'9}H}g@Hu>O % AuM_\Etq0hx5n W2EP =b lN }S&^r&,iseOV@zpچt`']"}#4ajh31{٭~$~M#sw:blp\VG2Y(׫Zi$Gu8顒fQ@ց“n'eDRneiD)l/Pze3zZ5Q2A#r/~,Gˢtx=KNvȁ)n>B% eWㆭwbOPaL'TEs$ MjX˭jAYC05O$ʟ4Wu1s-iZl ZZ ,F^a>wceDZk ރ|c4_&A?k<`zAk9{)іxtyNȩͣC8h,`QV=&') wEDHI24}͡kSp'KpIK2rps:\ؠK-ZP ]&(V HAI$RBP(E0q P3b'={㵦^tb^\ aFYUji+~Qw/υe LEjs<#Z9 ~Y"NA>n&y6qʹל_m*tT=I圍+E$ P4DS/OQ9^%~˝%BjvLœDPwjb+ŰAmF=vg~ @P %Īoh {L )! J&K㹬~3p7}4Q`9-`,]lw4SZȨ#?-PH(F8a}_wxBZ|.apkr|mwϷ8:7cc2`(wr q6/v"Yw>QEڳXH.up% h@>IАY˾&-Rd|ǻAnz>5OhCaɵo?cnuH+aO$P[b( rL)!m N4ئAO~+φ v48WEU" Je~6 Ċ.OgwY_쨧/o,>ۓGN KUq`/c_s nɿUxKDAc$}aMI 8'Y< hB@ \$ѱoO.)ta;@hA{yҥKOC"]W%QR}k+oډrQ]Xd S5ʔDPn\-ئHtB#iY yh3SĮ1TSFKũɮ1RU x@_~Y:\ꉖ5冹%(v,ʄ~ Ze]t y{" &+3r${ ?VjKVO,(*&>?vw/+V1ېPbA_E7Zf^iF^7DJQG`^3;%F8&o&ޡyu {p󋗮^ݲ\AU$ ע[SӷKB*, ojJ9 ;IC92 C* #Lv 1F9,%DU6&N=& :tfpv/^aAW,eNep<Ĥ7E?  >t:\L3ta[A+P4=veӟEn7?T^E>*ǿM-g]eTvT#.@K״L󰃾Y*wS>= mQ>ZFܑs9ȩ;Mwwkō{_CYt:@M*cXoCa/j^MA_L'*F=Ą@i\T6%x bThdVh|U^z7_6qn`_p1JM|ofo׹čf?Q\$HF1F]]@Mxc70B|:BL/[՛6uU!SRtcDO0"h鿇TÿÀWXB(Aԟomci.gdpU3תu]02d湢)'P KfY_KHŠיJxvɲjY`jѩ.L-v ).䡌xrD&[b9WAPf5gr\Ba?փ_g xHt퓝>LU3ܑ`>1LzjQи[Gz'>qԢ?S `y\v;:IS$!U@0ֵەMp-3&3pPhzd&{L,[*riHX}!?F-\9W݁b9ur?A.XyS~ߴ y {ixh ׽Q.eHߌ]0)j n;V{ւ]_8fm)ơw>3+]~IM6c!x9VHBÒK\DnG9ci~1j/;IHȉRh)dkzq;P83bJ}eͱ iI/ɒE C9s4etL&ۙ @I1u' L1} 7QgpIUp5ŭ4j3e.,Mæw4xӫ8@pZH+\sG<ҍO+5,i Uʊp5Kݴb^SSgQӍ`M osjVfsv[w5YI{us3\?r&@e9ؔAb>?謨n.~V1ׅB 5t˿s!0G&zp ^XE$am4S2cb'>6GhӾ˖4nΠzAOwӷ/e(ہzO_dgG~m)gVMHS)\OH4^gSj'MCr1)Cx1.Q7mg:v&H_Xԓ},믣 qXx$D`#O#T&XBK .CMnyJOF_`+ڟx~?>P͜CzyuVVG 1kP42Ş|hVpHLPJJ&{- ׷Ci*Hb'5RX9+mC,XtxkU\kgz3hlsnWvdn!C %1 + &r P<<}.yzis"4uef#8')CSQrf s#C xAޜ䕾 Ig>K;֍N(yųxn@?d:i2e\/SBjLTvjGEִ>RwEhNf jj FnG$[NgDph%-Xkvlw3(|Heꢘk]ɎjWוAiE K]"&A;O!2_r{nV2a4U!zp3ilI8V6pC(@eQb,-bOvl:u2s7DkZ(x7_ۅ b7+ɍJr7nTΊpE:1at_|MG(⡑µNbC-<%XJR%;kXR ܅ HR?= * 7҅qjHisȋVcA<. _m{y9c (%ψOv!0,KO>K,LnB겹Z v%Bla;Z! G݄š){4wk\R!H*%v!+ 4{^Q)KRˌJg"QmګfMza_H~qMg_kP(@sJ"2CWtG|&S x*VUƟr.mQ#jcv $dhXian(GmX7@jn;` G&G-̋$Z)rb 2}$b!my)w8PޓTWvma/ czT64]?^XSgT+Y32p?, ;rp(`q}t yf?+(x6)b =!6l`)~eBmP YKk$4=9:nk/-@&D*eLs~TU8TS;St*0E?PHTe>_Yv!PS;񰃑l r@dH%3Ou XݠĊ7\>`̋\zPm|.ӪwooȺ>b!qf͹fl׸W0 IZ㳅Nߓi/`lPEf3c|\6 8㮝=B{؛rڠ!.DBHM2o֭ «,*2Џ:a) gm#F9]Օ-܉Ny7뙌$I ֱDaetQΡmU."sqS\2wu5~:, U{b稿{y)ol7Nwn?5E(|ia7+o, fD冂k+%.@E7 G; (qCb')C\XQOLяu4zh CVR (ڏ_ڶ.h|CGF Kz!cke 9PnqΜT`/lwwƫД [q?G\4=…TnݮW^n%jpq^ޝ~Tw/. ='ʁG+.i=6;)C̹ ?r Cu|ו6DYq.w ] Bu04))ÇDT ,N+Y u9 b,㝗2酏b5cv2+BX]ﳃ[@ff˜jkK?71dDZ ZّL# /IX.3`%]\d-MRvP+)_P}DOҌ͐Ȱ[(WVlљdĚ ~ ȵt զͮSeokRNguV2U{uw~\ mC_WxLOc~#djTe)ݾU@%R3=x/N"ùI!͸y)iOp"lDFҨ܅UҤ*hnuܓIa0O577b}"9QOeh&u5](|ve2 X%#DzɊսLnynC9v\ `T5$#fx!¼BЀ%ߙ/]nI*u:#WńnL^-y2SQ 9N8* r[j_oMRޙM1ܗYV,O&ް\mDT!Uu KwP v?Bʙ;D-A]x`Ws}l,_L.f`JJ Uц 7#QE0aU/aSsIn(H@>]ș߀TFZxL+_9J!'u:=Ùis)_=i':wINq7цysG g_ dY$/2m`xv 9Ă#c5̈́z \GVmclǛ4ȣu;Em͇ Gky nqyӚ-ACT2qe2i  (D5 6SzYIOf?H){ e׎GΞ@ nE`cn:]~&&dD*ʡjNRlΓ%ۑ[J8RZ;?wfFenm˝)60ifg %{ -} ow2i @*%M!L+$!wŸB`fРOH-Ңv6ٚ˒xcf3 |!4MKn<6?$w%|WDm(3 $̅dEU I@р#e:!!^roA:PϚƷ>X!>/c j H|ݝQ@ f]vZS\9p)R ۥj[ӭT.B_PcjGƷL\գm`D&E hmH̷i]{>M#Y ͍0nGQ;iR}tWX q~+R~aA uu$-znn1LɗO5^1~}T߬,м%?mD ~1@$q M@h'-ew#D^ݟjݦ!ߕҚtQ0)kT&*Z=}Xn=6 s_M8se,eݗOgn \tGb3aWXV@Ԭ쳔L>1w—Fs*-W0Y9߫EU5oɑNUF.ӭspܸ1Q W<:~5@- ;I3NN_e(=CSB9M80 0<:,^;ˑ Mx\ϮKS ]yӡ`COG`$_Ԭi VC A15D!폯j0CN.4/~*[7XhO*b e2vGفCkzs\),ڃd4n/8F>bױ<$dj's,`cXı̼L[#>Ks*))b.H*Q3$x5! m%F6ulB`CC,m[)w}"?=_i2m,qt݄Hي x$CC`} 6RR V7aji>;6I0L:Մ•"%i쎺$m @S/{8B!U)?N}HUŦIRUO \ 'G6'%0R%EwD)}t DȽ E!}P \ۡ)E]e(NQ2zaoQK$IBώiz] x=o3|G\7-1&,Lbȡ!)GIOEJcM7.l8;(R+߽Uni! <ě cED&7Ze\,~Ѳ9~E^4kTCPq.Υcc$U1M~ mUuQS^/nI&G EƓ{ʨU,ZT ( ,0ֲO =Y*rhql;荒.(0; Ri;[1"_A0%2 gՕm`:b^;n59BćDP{ 2{œŊȄO;t>NI'OXROo@_,mto! AnVP&l!Y)W m'v8Vl jP[K<.JWp7u̯Mƀfن!|okHW?>qyU M*WK nRfzԙX+w,O,E6JB)d";].dj#y 9n=Z~eʈ *IȖh3 C l C׸F % 1D=.ʹT]`k/dm 6 . 0'iOHjHxW:di'E$ ~L41^ (DK3ҁtW-0ڞW\Kߚ×lIj'7#GŶ؋Jbt2 ^AA6\Mb6B wJvTx<LSw {xwOJ_iݖmz@^` DӫqNi_֠ E|Cr}zP 9P`s-*۲RiLAex#uskTٟLgWUXbli.-0mj7}-?0eqkzÙR|َԊZjnl%q c ΍cc:( :*QFwfO_kw$I*a%+H !: 牦8d~HL )-b4V",M6?<דMmdfu\ ׷U*|LvRl lZ;bwjGM FebDPB6r(V)<0tQͅ Q|0=8 >'F @W̵/5&nĮf\W ;#o-\9Jit1}&P\&+ftB[R;vKrI}4!UV#2}%+[vnpz:& זmzz n{yBhv$!t- i%l'|!NfU.xKcTJ6',uGwXiͅ/c>(GI&{n1 0 l)VX'*w^Q;dv2Ыej.g,@*T\LޠE۸PwBFJɄBEmfmxo ϩ[7T Vjh8=jڻtO.ijOnXWٽՂ4WUJ6;:e,?K5n4wa.>acTe#j}$tuϸQ-Ƅ9h"\l!iezl]C Z9v> L$^k@Y A_J=6[ F2Ԕbi0hheHne *Ei4GXy)ce!8 $_ b"r ~Lb-ӄ ~ sk!5c+qC6*rw~l2Xt@6]&2NJP }&>gP/x=6X5lk%2/~Io-ڲޛpW]C^+2\{P2qpzCb 8ϼ g\ȯ4xnV. gd$g8 3E/Bfl'^3Bvxzզ&c:ڷE]_erp,5'-lZnSGnBF {;T(7O8zihE; ^)k*ORQ)f15R5:~MKn*Ⱥp?S,# Zإ1a?8^n/ܺƻN?7 E[4 -_R@qum${ZرSXhu%$C@3 OGDe k>m& g9)@(%yO@QtfoJ㘑@KFMn jh҆lUsGT:R09+Ky*Ew=. Y@*!+e|$t]KTc0suO."!궕M[U]͜%[#|"BAn[x{'rhRa|RqGJAF9 4pv#@XS^cŃ̊yۘ̚ѱyٍBngܱnJ63$%?]3*-l5EV܈I?>/6AHMB s.N F/3قc KZG-`K҇"RVvEKb4<~;W\Svt9g&Ւ#P#0Rnm )s_6.wrmb;яB-?%ݧ.k'sV7raOC@ {l &`pb2wCΘkx%,͈@"z9b_vQ# &/g7+֦ Ym% YzpT4|1NTzX i&p"Bx^nۡwby?@?u2RN^νG?ڃO'Bp2 PW%$H9lS҇ܟ0AR;3j0nZ@7z:VP䮪=UidzrK]Ш F0M68uqlscԁ(yn*ȀV3<ڰ(X}U2ã@S7K[byjS L\޷ dKcŻ-h'e4;bQDۇq~vψ^}9WP 1oaIB R5:C٤LvtTAQZ.@ngykKm\# Oj36> P~H0q+^ @dЏ~<JL1&VF|drΈBKg_ƛIQ+9R&E`4=¾T?]T}e=TN-uȖ~t 7v ",rQLfaC [.Aǣ9b pLB?-Ny TT럜>ܑݬ` A.  @Phb2}2ЊAkZ*qp& -VAkES&"s'`d4 j` cV#*~WӁ2e(4'{eMJAyaf<흲p54ﮩ{ew+䠼/}_  aGRoGaq:t~۰[pp9qQ.MLuccK$_`!=@')d&mDPCXwI 9̐$w})tp`!tx`;|]7S!IC4!oM]'ɵ/yXLUX=nWemubh%RrO j&3}hɎ”# X,ָknDl~B9]oli]d*;u0½^BŠq!gX 7&_Hm!]J,&{9wITQ1l-%4W֩l~l̓.,;6#[\gN84,L MTЎCXuezOM#"/ R!˨Pv簤S ʭIqdXFWQŒ#aa}IBE~V4EY{3g{YbX߁/O7VW*? Y{O'} Ouƭ2vڡ[׷f<:[L2筪 $Ϲ,} ߶sϝ 4v"R%lE\:V-_bC7M=41 eJUyHڎe >Qh@ԃ_TzH\y强Fo'"TVzLWR6GPYgω7 Z/ 9_YVn 1z@Xux^xn,͠F#0f}{5gm,<^J ?|kD D6IS T>}FeBvL5bSKvWOcM~k.zPĉɈU>-ց]UgSOK9T!Pʶv l_g+Cl_4X ,r C0OGp7'#r*XFV|*p]~ctϦ\2E7ڍ 0@3[kֶ!t\7]S*R+י: Y ZOf:<%7ew5$Oi+w8$KRg59vZȢXT^h?V^Uxh{|l HHZZR g/g=IC%gVJ(j]EbJ/4PwF8Aph j7vU@=q/̒әNu7}wftҪ}x/xF9a(WJϘg$?EEIY 43Tpӛ_:Iߞ e'XP3x}axP d7uR%AOj  %861n!`ܚSjB0#bCg% 72 ?v_dD\=ҔX Lh~>k/p);r zE߆4'|w ͠Wvgru9 h_x5Fuf׏ڔLa .:_kRs-Eq|뀩K 7wbXg#rl{bфgE'ݽri(ij(~_8@O2PHV#AR}\}u>1jBڸOg",2OaG|<LeGǫqDi30^3azx:/3ϳ;֘ϸ1eIQpe]h9tu[M!tD%vi5][N 3I?#TDݙ7ãʐ'^CB:"kL-෤qV](>uǿVEwy]__n+YФA1-1%7hN5ZSH2%6mᶤ",n[3s n1Ḅh>>:Oe@CV>, ^츼Q_>!rJ\)߳d,9߸?8uTDym k:‰2yF)c^/If ي6~m8k 6Ӫu =zvZU/>Ѩ L-n9[ fW>< f~xYZ۷e9/d}$SB@Ү zuٿKPŚ̏YV[Ljgőʺ9kȫOc7YzCQm1B)v%jq,L;o}|'&a NIM't$rx_+!FrG;P4Cyx - 4 A #Zr`H, 606h¥TW#nQWg)wIz-dBUU+<;Gu { $,DFd`K +Ly' v G^]*.=0If|*`Smz勤4k|$h+9ǖtUBdw΋lT1ŮLߒQ!Y1" yh k+@ FH U^$1ojw&)NTA'fB΋Sg|םx>% m- ۼ;=6uwoʮf21{sټ'HF䎕UIOh7n$TZf <>F7JY@*JDo#a$ xZ?4<0<[YTeHhuAZ|pFV~h/#lql&2#"9Wf,BxrYc=nq Ý STt3W14D";nOzw{ٹ*b~RAMxM*X:ZQtSe' O Sp)~qvw|Om1{.sswtgwӠ!>,d8`^$sƬr\֝:i BLM<#`c%7>ҟ9WfS1Rl2FQ=_Kfiɚ\X-;ZaX0}Co$ӨW3-T؟П!R ꣏ 90wYEx Pbe5+w+itq}) 8 ^Q˝ؐ" XgbkC]pŸ"-Gjl܊skln]wn;bq s.O۰](k0{ETOkms_R8)ԛnokiK0Z>f*-X;89ۑ7m67UʗU.It|mk9gD4cB4p.H 8O[ұFdxKs2w-yʨ8=+wݲDb= * V;UС9QpSt!>|t+*/z<?1_ *lCdJ;_lO]k]Qv]̫#h>8(a2䦪3PPB&v>立}-Vy #19eƆ#7$9qd,oCd;!xg)8@ERаl"   @S3ԖJ]CW~,j= voTEZGׯjܞ͙KQtx (Y,X@Q4G]) fhJ/&?A;PtKўY1Z[3nJ4&_l|=>fBЁv@};^eR@ПAz=.327D+p7 osxd3CC) Y8 1$ָ]G*F}qCW}aaDx?SAR%¾c<E+yku Zq/!ɴe QDY@N߹߶|0G\`UH!;%v{a rҾNj@MGBPf|'ew2,DAr"-R79xzS}cs ?$s;b0ڪNx90<װ/y({҂%&HZ7쨋ix:PA$EDfI.'  LӀMj4X*.p r";J.qz*t+S[nأ}rHN',yv_Ia?bEE57sSs2?l[ۻK#EoZ}ՕC2x +.qs\xnw4ٰU?  36͒+"Σ'/I{'$^h 3ېNl v *W< }e*n/:VO9,Zl2u Thǧ"66-,sMzW?AuY 614~kvi!vaqI.يg,b2GP8[2F*@9jB)p7 4#%-XJJodV̤ ־~~C}◸e#*)q HV~aҒ݇V3ǿAUa1|oJ/e<6/)?hd>C_ȍaS!mNkW d~b]쓧orf`e,rTFuXV!P uV۱0Q&ub8SDA AuWK.Bw~-/l'DqG"z|KnX <@̪qK|ixO-u,DQ+{U!գ\%1'^>jt#tII6]-c7pá#O]iUʯΤw ~vՄ_/kP= wTrwy1y-mטdf5m -~[, *R;:i9ѠrfS7J ᰱJ٭@Xn}#EaEЄpg!Ce, BB%[u54[É 㢎sEv7%vU,7daڿBV'0/]fj0@tf.Eï$\ME\YL`Mf,Xv9fN;xGwܢUt AX!Z¼"ū*Ox[e9;^:]~]6Om>vyPsv.. کk[$)ty%:y2C{ ΏrF XeGE_wT1ak=m`[DW0@j6Unaa }?o@{Q<~{XOLLN(>A~Dפr"s] F_Xt*ߒDUe>R1x)/"Q,:x2ѓ?Ɵq_9y;:.@1"{1F?oM3g|α H$`*ԉ'ӭ't(@~& w1MȎxI?A- ܛ){(0yIn,JҥRRYВ\&@J0wP#q}wjěYC*٭kK1J W8#U/KvDȏwk BOy wljz18k$^?+*O+nYq [:SŞlpץ{oaWv0)_X iWԺ05k$tdA <ޒTYO]eW4 5^HPbxYQ4$SԷ3Y' uh-NY8l;¿=`VOmjھ5KQ`!uX~ c(*tMU~4ܽ0Ձ1D'$L+B&:{h!hUVTiJ \?HvsdU\*lw!s~$ńL>3'_ U.Zz=vki5j'n 0B;ywf/|5$ NLr['lq֣ O`_,6I]H>/=APȃ-]9V%i\i= 6M'Xhr* /Vo }4Vxl![If:\O?)>,q57z߅>/<}\e|gڏf@ ZZ|tQe&|?h&I7=\~_s$SkٓsӜO˵#l3 ҴE ]jLߵh2& FF̘֕WU ?KpG|9z#uZX5f1UB=ZnCۈwIOKk!aCȦw9hołvlQ 4;9]$GHVbٿ!juσl˙JUk"mu\P`< Bd7+q(q>a ͯF/xdw.݋X!Llm_}+>\]Tt{G(XKۈ/zg-XIJ!H-!9B6hTZ71{1D<ّ5"k>)?`? lb6# }a7J_B^:yƅw(̻ CF"lI&aD&Dr*^ؼ=-YG lI.5omjET`)cc:ǸAK%zC'Gsi4 >z%ɻrGռ¾&XRS1NA |/*R. t6q"zZ(-܈uCB7VV["%3b$7>?H{v flmD KB3-jɆrK/Cp.O;qSWGd-"DPNѰ?Gwj$CJ83z" z0<%Haʛ(B`,b?0 6}ِV!OźSp.Z}HqTiH5,s˶:,JDkfyc.?VlK`c4}Ns*V$ܗM4 *A~Xuyu-dl;&s#QAė [guJk6GզR5v:tx c}'F-Wt 'Mrb/6l4q)!ܸ&d^H~7 " +m{{&~:S 8Hٰ)3ŪF\9WեPC/p9f!2oSH"g(gq'n"wjlFY9R~~ZĊdJoZɦ؁4s ` Vywљ Dbk__g\>T$pP攪˕t%+@5C-{"dq-% us:Qdͽ1JYZOű}tV.r@fۤĘvgAͤP(qݷu0l)qTf~t3:53*p}Z *RǠARx"Cij X.&ҽ #\2Q2k|uUs _Nн9r wQh;^26a)sJꌂ)NA!y\ZW=Ow_9$43;, dl0FzK$O]S1M%Rp@ylG. W)r:H\L:>䝶xza_`yIߜHegvbH,5z+lAJ1gJ檛ZwSp(Mcs<| hMTOuA\_N_P_Q4/񇩚%[_&2'[IlFL9/LõjUV:`EofIK/[=><.JZ B1ЀwkkHSRlZYCvԄ6fV$WߝUdUА/H@͎IJRG?<7kmލˆEeg}2E}"?~(l(xoΝ9B5S"r;Pnv2Jşq4) u"c~1DؒDTw|TÂtܴ ˮ1jZ]MG-VҩJVukr>J;BbyUZRqyO6v|F~RJ\٨r?}L~Xy?7\Z*oLDw0tfNioG|t!k[!gk([?+QpM4lb5Y@>=Z]zkګVr!cіl#uVi \1SZ}@=K y\zA1,Lo#qKfL<'b@̗8)t(Bj+7%-x(4SNȽw 6~xj6Vu<&8y͆\"VØҚC&lgQේ{DyP$_ \56kmtFƶsZT kH+{T6\xaow6HS&S ª5zEû#&g){."}@Iq(=l*ϳ]L|A. ybru P8u(Y:\_uwitJA(hE&fPHY"pUkVv40W`Q:& K0J8QdžSW[$0MY+ |L=Aa/<) 3Ȯ"W"0 2@>#Xp B i滅׸q$!ZyHv~@P16 &Kc<|#X*cJF=$H/}<12!\קg e8j)W6.PYWg'Վ![w'1,t#e)߈SXWAieh+z+T=ݔSB✈ ٫6Ŋh6+]XFC)+(⠒A,:ۧp]5Yl'\5 ĜӼ<+- @tQmbۢ emeRŠ B6"AhUae礶6$jpe]'fs ]bݫ4B97gc5NPV{e(6#,|w[D_ZTRӾ瑆80F;*Ksrbz030ϢU=uQ5xbAM'I$nG#Ee^7a tEAp35WeT.֑PhKz=PNiQl 𯬴%D|ioQ\޾2i R2!#+➦׍.BL$&*9K6A3!Ġ¬Ԣua>_01fgvaR9"OOvh;hK;hKM) F/|7̄z.M#sʅuY޹T6["F"MAdJ`Zz0rglǤ<,2[oNm%8ZOnФIgtPfͲ,/oXĚy7'9r/G\ 1~~u |#D]1:׻Z^6A εQ4V+ϘݖidKwiHk}ߕ:[nC8~EZV%}:0$;E!lQQ$c 4XapD:r>εQĖz ߫mhݗv~#X;4|@{`5B[yϔSZX0I:ZzAZM pd1ئțΥaΎR ~!rLСx}ת*ƹ))&  8ObrP𪈲C gyhJbSaNzj Jc?!4Y53ߧ%DZƹtek!$) Ur<ʮ?[|yϦ"ܨ`{×fK?^c۶Ѓ>K:qFbTi\stjݜ#wq?kReƦHp8/kMqd"6xa}CxW%AmIL90vt/,*2q]O찫\L ΟS.DS:Lx[.%JgnN[ էyKD+k2n ˕Q-sKF0 s6W<1 7iwVJyfJEɭSV'}nv-gwp|@OiIK>*P=M$ʔVX 6~e!`2 )WDM$JC@gK?ׄ?w&9t6o!?wzip %4I&2RȤZ#{D[ pAYIQhq:{#,D0=_Tːdݗg%77d9Ȗzv8eNU[#6nG ؙ!=G@^x65b0"qE~&+z4RsF"!.Q:>zx2X(P*)g ?t:.˻릔(`6݆P>o^הNzJeTbVt,Lv:qiHNEa$IMV-F2p ϧJ鯋z "v(~*mkR 9č77; 7ujZ>3v%ҧGZ x[SI#0/s}2IʂAQߋ w컹rkf; ԃ{n糦=`Xo}0d Ж2-lJOx\d^Y>MD+iן^\9;KNjT4.?L/-F`~2Y85!R =$6xQGJOI-{kvfV)%R3Wc90TߙCRGt\Mmyyr{ XkgZ B{L˼"KF&sN,Nna1 y˃Ե #6Bri+ vMu|{bއR 4v+0GIvag7x@qVwе QV'N-sfZz Z^qzЋJ¬#-S^sd5>k[]hm'NwAT];s1>YDUy= U\wC7$"A`^a=ѵ]j{A`Y1˗;2QXtJ?n6v`PcFMqajOhi Gbt񡎷μ$pXiP$]Ds6b`b7\Ϯǯs͗I#9<`)WH\OずNũ6+裇EdCF#)z]3 ;Tۆn/Ţ&΃ҋE˞ fG~ㄅѐ =~o9Hԃ褐Qj\TWy™4OPr!X z?!obR"[N돽2[}B6.L > >/N#-G3-&3sewx7V_q"$t58=ǓmB֫V]'H+OcjC)`A?5u؞_KoW/ERGй&@"om@r&{M\L6(܊9Ff! ^dV"{+-d^_땒 * UkEj/Plcsҭ0?-n&Bc/@~<0j槚nܩKk8DqLqU^vRhE/&hW'{ .2%KX*O0PHJk A837NOV06AFmu*66_KėO?m@Uñ o6t8R1l˼ijA7DЯ:~}Z2PY 7`~r&+ӷ&,Lͮ:;%GGiIThz=/A8.鷇X ƅt4q~- aG%1;jV!pn,rd/R4#q[LO] ÜZW)m` =WJWrjUHh=pg$_Cj)xjHk՚bG$uP)jnp0>5Y]?º^ W'0䢈p rP4w?OkO8_`40uy%&#"蝅6oZLO!TluPYԓڞ`i[SI4x)W킁\7GzWl&nIx+Tfh^Ȍ|-#ͪg!9"f\)B~+L)LLüc[HƐ20YVXΟ3\NIVa."O7p62;R<g}a+ƲX:{.W8Jolz}5]H.m%ll6ӊ :5 qD8O{TLJOzatp.eT]IY~d CFإQ`D>kM~>ZLQ%ϘApUDmͯM)˩ @t#=:Tk 24sU(@0C?;5'UILгThigJ,d/{d7?! 9\,=_ Թ9|[\VTJ"ҷ7c/Xm6}m}k^X(1, 5 9ڭ.RLhZrBr--~,T %bDO2&#Ȱ3 / =dt]hJnu jF'wAC }Ιٸ[z77 w4BHxpVixXrШ`h= [YVp&=2|#gA`*N.$h>*}:=ɀg̾ 戡.>-"j? :+;7U;fHv:5"hA\Z|5޵ MY{y&a$ܞD֕ N ;ċMYf=18y8ZAda ύg=SmO@F5V({q\AR&E_جDΑLyv ( }MlՅנsGCQ" B%. 5? S=rV7$-\\[ct,WI@{_W& 0m|aQMU Mڷ,6TRRJf, Brݖu$5N'l_of=7TB-H-_;M""eSxl2n a(jr x Eu߅as_#3z|JgsD(P=fѴ;(B܈ySWrAT9A6^u\"#rULi ǫp=V#kP˓bqFWcphj_<`4R`/V f/4Qۓqހ~lv``pӯޱ 7{3i NP(Jz$e9S|zQE&TSalAZP9 }QՉDC":ZHcl*h+s`0y0\H:;>B+͘ޥ"N`bF =O]ĺn?-=nds6ғ}gTI9CM.kq,y0b"0?mD+5օ-SXsJL2ֵ0S^T/m6(`^+{Zreƪ_K!XcDuC*]ֆAզ֚#^ r}DE(==$9e4o 2Cuf.ښ՟뎺K>:l%C ;Nlr&69uEL"k5`˘JFhݗ9zdTo4v+P;cBw)ڥiD.89ce;{+֋"T@q5#(#8ED)D , Qv>Lu@v.ԔF ~ [sX\Ւ%*.J7f a۝%wM,ez]'U2ɺ(n<]~u]([΃B¬8=LOCܣI\cR \ڼoyq].p|o"5DQ%^AݶPyX /Ȝ&Ȅa("À>uyl5'6ڊE.!.iRRb 1zaDBPN^٨_ʻ9u GH4B"_p׭ ;P*>.uf]X6ӫvCBmxߡyHɢhV֭7ݤ4>[goIDûp|o|CM„C TVLd&'ğӦS a<>~ F^k5l]H!r C":S/GPcQ3=枷㺢]l7-ٺ[O6+*FUi͊_1ɞ8.`%]|QR ?9Me.M(*< ]sOOHRɰa;06Zn |$>ؔF^L5/̑SXʤoY1LVl"Z&MDA_,ZebM`=N [6 k)w.[aHnuE n8հi]ŝҖݜbwK;0dOՀP)3=7/bE6GCز{M]dnmKsdsA~8"d"qךi\:<'z%/*9a ~ZPIyC͙^r(z#KI- ogPۘk(} fg .*,?*.>:{ c wlh~έI`\MhG1isWs6-v/tzm>S.am2ZEt ;]h$LOMPs6F_j\΃|s"~Ѥ*cZv][AW-8؏i ,[{(͸8%f|^bc;8Y j/d!DS>k' ХX(Be P]7q\*,YK$|6 LZ+t%G<#|- ·I jG!-X LM.?l|\fy2Rgm2К(ڞ ]MLj=/,bSLYH,fExoc@RrF jT} k]PC<9s$c0Ѷ8݊e!Ndm֟NȶLZѹEG6y[ƌu&[g2ѣ~@p JL3P#m"> stream %y춯?oo3 D!e0 j[L9 f<ڄ3- NvjŬd$TJ\p@FKTN#BBq[9'W$ԍ]MqBT>Rtqs6i[}랒  ;m)V(|C:z6ЖL~rj̢&aY0>sx)nH Ld<6!Jn}ȱ[AƉE  EBƕwL-F3~Ve#Xt@PMŝCz]<گze/QrFgP*o ,23N ]E}8 _pw{ !00\0v6ωi Ǖ5F<\ڙj d?I`#]aKkd_oᩆѾ#Md$[ɂpY \\7w2|v3,1ZnTK2gu]L5U. ء n8g TOw+CN+ㅢevMhnY6kXӰk f$-LM2X đ[VHA^׬kBr%K?5܀R:c +3d̦1Yo֩s=.d"WBl& ;ֲ# wQ=ck6aKT<-].EJzBȩ+yBH ,.ݡs2>wBriQgY+cꆙD8־PhN ;ATX9jEf@֎(4.QZPPGrvhJi.<#DG|k-8(3fc *%ifa-T\Z~<4l,ͨIm:mLW4qmJm.U6 f݇x.u%?b&i+IGFkm[^;umt~7XJ>k ec???.2:`cΖ2r#Tc '*,3pkʋd %%ү"iXXpz%O\f3# x/雽A GsU)e6`N)dXA8[xDWU]Z)4a:A$7߈j!q-R:3i)1S3l`u؊7wF>. :!S61pedNx3P_'4[h'2R/+V?nksMN_abgF.2!.ui{n U3ˌPREfuY9*\VlsUb*P4EM\QB@JO^Bi8gI TـRA\yc+9^{杘l?Td`E ~8 J_$Gj˔()IT+N(yZ{ N XQ]FOt,̉hLNΞnmW` :B7ؗ3)WaVxpE"ps>PQ҅p膜\1fڹϦ׍koe @&`@`(a+D> =v%0g:6 -4LPe5r?{C.K@P-+Աj/ Vq[ڛ endstream endobj 74 0 obj 2640 endobj 75 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 73 0 R >> endobj 76 0 obj << /Name /Im9 /Type /XObject /Length 77 0 R /Filter /FlateDecode /Subtype /Image /Width 2400 /Height 1200 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream ^)i@=gŊkX@vOp(`ȝ5MwrZ YgHr罒wő:2o<\pD]#r(fwd{ߌj?iǞ%%x9qrizObPqp܏)wUE#) W2RصJTT˵Hå]NhJCļ!W5m}iJžE F=f7~hV m V-nϚk@j n {+2YU{7'v<>}8|!P):M Z8ψ;3/DR%Z_h0j}xo-F/)6s.wlHwСX J&Frɉ=8`|z>rFy(VR$en7:(  RYIGeC)^ҡ.!˄t*/'klJ)lzEXϟ/) Ed0=- `R <ݾldJ}^D+D$=h!X !,֧#Ui-Ej){ᣝu6l!6<<[bjB=Lqs{Hn1?B@(N?W2μt=BJK\Q(G `{>ͻ[ "ǚTogD嚌M.sDGy#ZQ'ø;'y҂3(b9m'Pn2.c}g=R>MFNXnDC-f ޮ%z $ p2?nT;^lY%m*ZU|M&#;e{ .W?ɇMQ]Ʃv[=+|&ĦP_Q =kMP<5)-oYT/\KBDȨet5CY,>^80w!w'ȚaB*qp2[59ғc|&U5#A)뽼湃DC7&z:isVo.LMdd'tlJc00읤gÔBYnHNd?$NnVzH6[(`mXo=նZ(ܥQ"qxmFTއI~rCyjK-{FfP0H]z} [ ļmӧ)(+"!˥@ޅef&/F7PwzBY7'd RK41Q "ໄJm|(;7E1NTNf<. vG{_-jhV~a5y L GCQIlp#"&Ŏ|=b[>"͛K1!es|yBF榝ss;@a??κ ؼmS,|(xmD^U?;ߴ,ysC?]+XK 0ͱt'Sl\ vU^Ǚ"`g;'F#_*/ # Y_"]^ѿ70dq1ך]P Z:-´X?%iBI5(gKh϶F¨AY/Meuۚ,pT:wO)|鍽C#i_cWN)O֗x$!x7AFTcž6_:W4h3ަ;Q eϤ23pY/+͛4݀en-0Qz"@)S-thz(&RtimUN=7?PeSޅ\}uet|@=0tkġn[rc4(?Y,t9TvY+=c+}Ƽ](-<>xL+7y.9.6κI_ٖQ?y"T u*`0M B/O84,^lLjQkW#m~-B슮꜠=ܵƧׯ>n4@}#ѓ8Iiҡ)9DT4F)䙼g%c*#C\@dHg9%IڴěP>Bֆ+3fi1v%H>3n/Զ$%tIԈJ(';c !1[C\m+YlCKf *SdT,a_Gmt1K,n=DK8֖5q`y}I[&dl~ExtEǛzq3$NYNP޽I[^#sOel/2VkΫ%|Ze@0Zm</GAgb)pJ= !G m Y{+PzpQ\kvAQ]ڃC1і=4(GIFKHsDKh`6 ͗A'GɯEܽSK ۨjW$ɴyԭr$w-ߴp*_]]/e"i̊`U }hvb<"{*T7w}X QL sxHt0*j1bQAaSǣFMtufBe=&6GSc@ R-feNtqD@J",̍h.E,&d)XRBRK<ǘ"zJ5~8-5=hsyL7-O*tQ[J ug]0<)1ݱ>L/i0=#M7eF#FtM*yUԛ3gAC[O˄)ͷtCY~Q6/$A!EtCHb h@;*~}1,F 2 e)0&&zާe`9֙ᣦܓv (Vě\0Nߌ[L/|@lg :10O v`4͢KUa@M Q^a|iߔBGؕ„㙏I}mճ0ezy3RvDF.2I-k!zheЇ놧!=0 M8hm+tEՐ-zNq偳dvk4|'j(.7Sm'/T+k32$b.f?S gb{天7{< 7\i:q.-X`{GfC:`C4"Te`#ɩR1ʸlرz׿ЕFP(6J:f8^">?йV.Ahf;r;a\#9{ɞ uY]q=cv8*^urzF[Yyި 0G-EP}x9tP\;QuQ2d20nFi6e'R Dpxtxd:qnmZ8|gww1?G Pbq )JjjTdhs #%Se`(9֫ :KP y-x(nGM-{mI>(@G/75=%W{ S_c ZX{6f>u#s_vgJaSl!zdQ}ITtҾHx+'ؠ\$QJ0(?Wۗۑ"n^K6-CF]*ypfg ^l}F ?HtkKtGhfxNsCوFS<(c" ѴgR5br9bDڅL;ٙIΙ]Q xސ0t7q I;d8Xs-EЇa%O;;wi@(<41ѷB9,m , CY5;xnܡTejrB}H,ڒq1ux[LMHy.#uma,ʾ< *c}#Y&㪵S$7vC]WN2hD /hg#d]qΡ?}!-=HjYEpXbMM% p˩\71>c>?(4wZ>q*!"PnM6+?һV8ɡdKs]kq,5\4` "ԫf$]T -%<EmD8fyNWyxԣ{ٓ޶YhދIdದ'SI-N xEot18ٸ16L*񷥭2@ޙru KOsgd†mT wf%`w?`*|(!H oa 6HLj7?G] _G5_L籸`ղ% [ ߰_NH\M%t_~-Dk@]EE%9fA4bF&8DŽ׶kn#Y71Mz_CHC5='<($ɮˬGVy]%ijJm_K1n7kk2orqQ5$jAgȄxwܖġ>&e-69eLz,󶷟08!OZ̧@Tɍ8a?'5hNK*weTlD!X54]l%q*cPIa|gQJT|'`_T 2+q>4 ~ Sd F,se(ؐqq%PPEs%!E[i1:.0yܢ,8#._/*qˤAI 6dz}4ɜgf2ԨSجcf`>o&W1f -xEU#q-)?Z ֋{F29 ^ew{ [RBKhg%:&Ddw߂7l6`dC 7}|„ i`}ERfml" q5yoيP3K/XukY-8/XC!&>/56v젮/,"ui23'"7[*Z?1LYė xHGcZ:;B,OB^& <˨%) Fa*U2v^8(Z"&RB/ 5+xrߌ]z ͵$MK""f[yu?nL8ľ>jo?U7?6 v'ה jcg&lMen@o֫ejN$hm]5h;;nRhoO.E^rYs~wNeNN$Y`dk„z ̾Fap,kZbsDȯA|\TgwW輪Н;x|<(& 6GyCH`Dh,s41 ;-^_d+teB}TC$z(Q|Xd:a_0; ɹ'>i+w( 55ksSϨ@5[<'JPȟ]Cdk ;gH&W#5=eAU%SRp #q ބ#dui'ƈUѧЧiXU\+=`RzzV^-w² 2 H, Tr VW0Y5`{8X'KkFއ;th1I=6$ k  u #=Q'r0bbQZ!O!eO$sXW)0zN掰tDC7]ӃNnɈ^AP{3^S9qVj8GAhE$V'FPTL؀=2Vy3 Uv+#ϗ!`)0^X2|ɢ6fsgFU:"Sb%ٴ[X uDD(KWYm3썢SyMk-tǗ>}Qg,5h' ^[p4BZQ.Z9&O S(Dw".DeQ:b,x;&>R}yq؅Vf ߷)yZd {CY;tw\BSsd{s4Bn€ Y}s#NIS+UH,Ms gWg*p~9FRxf&Vm/ Dz]5X.^8B ]S, VHvB&,\*Y?"K1 mNa_.7!1"5M\,`o΢BH cٔf%X1,NL&}`G%[!9O|у\Ktn@DyWࣛ\ݬ-8yC>lq"HX!0)n Ig3aROA:H~z+J;4}T&W>jR> d4r 2Ƶ {1oڈN1.oBU3o|d82L^Oeg" of.!I{796>oql"ŤgVVZV,*oCryKh]irn 18CY(P<,S8^V]OOpV&w_`hzg(rZML ѩY2C)ۘЛ|mrmV8Md#S@D+6a[y~0JvnףNٌI=Gf2\t<.rmv)x'@m5H_ǣԂY>sQ[ŝ( `"p2:J{4}H|Aܠ-b5} f#s1ʥqXb5;{2iX뜙d{d\k;[!e@ xz+7*1e,e_1 τ)*lht$|<قiJ>,.#SEuA3*.Qwz8\rčt>x6h0%R< b<#cRmM|{Y/qJYbfbmh١SCni_bElWKN>rV9p3e70 |8?&9doP _q>"L^ P1FoCcOkցy;K벗YW8j1;W%\k]!_U-CA/˖_9k.?Hn!(8'X4>kDLoJ'sYe?ϔ/hLf9!i֐y_k dW*V -'9;Z(%`k:Ҭ29h4\"E>x?M2f\KW}R7-lw fvAYZ0,ic\C4AI;г9c'2;4}|Vsi,P!<|;$2r].eI=U_b<{@u%Z PFYܧfi.+D1/#vv&+w〱0b"<؁?*olA0YU);fA^%pȩ (~z:Ɔ!+aO;y꧕.T -'ƒ޿!oD ѻT:l~8;bTq܈Y.T?l?ω)pneN$ō f~`m z^ #dmƳ5ѻJ6?LGS&.i74u0AX:!)~ &>ȃLgs?1fo*Z-y=t# |oЕ/8>Ip@ם9!0 ư*4'Jp HJ.ڑ7KIʮӓODe^kBO?Njշ嵨_d+WeOy}4q .½|\_2EgNQɠyFKSLL;#+spw.zwZY,M;3WtK?_BGŷ_oxa(u%t-!% >SbVNjn6ZB]{&`d;RP 5([E/8(u__Pzo6A .1dt Z 5ҽ"-$v!G~Lw|#aNULJR}=B'$P V֤Y1r+b?tRT`T>5ͯSD{i4zY6η?z4KßVj ^z.H'kC8:1n w4TWpLM{2N9 +x:9hԏU#!72[圤>W_1M;goPoIrt#/NR (7wvGKGL˚$KĖϘx Xs ңWN? Rygq9Y!UsRQ8~?1] ADF}/ 9D+ab@S}bdc0LȞAP۷qח/OWD/ ƦG)ʜ!9b4$}I&(hwZ}K"3e*e[z"~σaS%q;0X0mݩ"Ómyx4~4nI9MXotRmqS~P&dqւHzwHϜ}:fj![jTVTj? Z;\C8Qڌ 1"ܵtLLc{z(5f[[N;J^$0~,N.RIOImI/Y`yFH ?[+ŬKhdP!N̚oU2ra|c}9c=CJ'OX}.C_c.]mcg*3+j[X9ߗ'a\\ѭʆ[Hybqs9˞ gEd ӳFD8&Ơuy`P? [.V`أגolxMԔ™NG `À"έy iscVo)y5-bQ(2_ DYiؿr<:uR-Yhhtt:Ŝ_G t1΁x- RßuB~!RuS4ڟ֎Qh~f,-Hǜ*|FMs 5&C}qWH:,xl}ӽ\"5֫UW.XoL]@/<3i{Zbkj "N6Aˈ{ebWiҁ6i/ώ')XѸ:|qJY`mRztӞ82%IZ7=8 CÖ`G@W_fJ8L#K" P,N@fqhz&V h5i=-"@(y eg0HT=1zbi(x"$PW7w?wӗV 3 S5 1brTL]?3cekJ__`x4Mb=)Y I#JFk˭!`tz]cYh~*- Le A6Qoqr|1)EESSڀ 6* qtrKe+ߣ+<~COf.-#6dj:n<QFkeY'~5uֱ>ME}NsMYE~cBY$vJM=r1HZCF po"aÖIK|O!:aT'+5JrX-f+ Ix)`ĐW.Lwx ԎH}WW#kÞ3/pSNObk4+TY꾉c=vpD1a9$|^[$)5cqʢLO'JXp2`2@b˕VkDE{>HY͊Sڰ0Cs|h~HB'&b/ASNa噵#{S+<ð,Hx>Ǎx,|߇\ڮkr*dz+XsZs.tZ@ ,'BJ2Vlƅgcs{I@i:Pn\{;;6V*SaVAnZ1Xۡ<ٮнE"3P?\u9  {MN, a.dE oX:z\`:wV)pRO$#q>s+V"Ξ0*0mnk!_Ď]TzΚrTrN9SNem@bғtvhpb5c̚f+ ;FK%?u}g`6 |:[3,Da{{K7I*^ۨomi; @tjvo(ϷC2s4Ú1MYL>u`&djf 1ހMQ-&Ǝ?)vc¿['P !JBF. (뉞 )ɟ eD-_BQN>+, s:# 6(. 7?y6Ԇ K㖈8Oc0@>_ {:C9dGIB*gR<bʢ5x_S+,[wo.p:K$\:6vӗ5Rc HٝOBT66p8/׮e <ɻ CN"ܴDžx^2N]k32s-B,;\‹.nc5+ggvҞvc1)9\G?^4;V^sӔZs79x_W?ʧlvGOj$ \V@Gm}j Pq" . RݧHԔ<]PMaCKh۽= 1UWGVkSEYe$DLIy\$p-[3k,0=BB'Qu ZF3rDQ)nhC@*o;)snN'ŽGy\}#1ؔ|}Q\?=4]?QԣLJa?Z~=R+uʺm`w0˻2x:A\7?6I"H h|:ǥ=Azk",]%B[#' Ш^7Ao..? B*羶˪̷u3't2.~g'(C"kHD-%PΤJ:Vc}zqgB̚؂vPMR->ft>mfcMz+H lpJh:S S8r|S-,P騧nu/ϩ 2hZ|P"T6t$9Js`_.Q%]r譈⪾%r _e1?7?֯y[ZR1n˒=yv 2vjF QeqT'yFD[@ ;^xĕD )t,@L&M%W?ScC/¥-js)I7LԉIbJV\֩7E7*I 9}NJ$xySy]CѢ3Xoq ̇F,P-+Lc3Wj7,TS#",L: i;RiC|.:Ң!oD R%H}8+e;t- ;oTCKKo}V3f ! F]1&~X{> m/y_([WGʾ*GEđG0Fɿ"NHn-46ߡ4^ Xx_~UnL'qmLecKQwZp``=lHh7=KdXJn9|~cd`1?.c[ <Կ7=$ܽ?\F }VȘA!bb ]4` PAQ5B:d̬dN; bhq`c_a\{mroX&:„jذ3YyO8 V酥Sn:ѷq˕ߥ}~0 9%p2hAg3,޴t%z2~6y%*"Rhx.;? P5G_3J D|t߲܀{!Lq% /E:0ht+Oz '1AG2oɩapQ#2^jH@*scg !LʫtE%>^Ok\LiTFb7թAdIGsF8aO=";n&sǪSPjˌ18K;6{LS;W$^.SiXPSmY|#(_DOdy;;%;!VUbH)0j#J:cpi E{_7"+8l2QP̶[fݰ] 1,y,-iǢ%>3:"pz]C!O nݻ}Ш Ä/hl/Ш!$- -+#^?jphx \]veõD(6g({GWVmApP?,T=XƅzK38dMܑQ@0e ˷v4SQYy1-؋A+ӭY^iҰAZy;߸jx?5?@CfBYz)0Óxcq{yUWưf]a_CgyqFd]H#/RqbP2ײD#zWA:XdJQ(AqBý U^v`䘎cU.%HO0尞YkEc\jA1ʔike3AIV Z_dftkPvbUYϋaNqsaP{>{OXb5W5Xtԗ)UK& GC_F"Fq;,Z[r)a|d*;,Nӌà*9Wtr>r'Oʭ%fq}1D ]*$<4$nDK%JDqNb_w܂;tp+'R%o'g yh)qԳԃ'䟸i]3aݗv3c??dQ /[h((l PrL+l^rY}i#{HZ!p{'}[nGPoZ7>fMἷj({qDvmc?ڔRr.Y+#+ѥ v LoaUUș~5X^er2Dh`|5#zyVxV\H$b}/ "EZ޷zKfq2]A<[ʆwҢԶ`)k1i,oi4EB8[qUЋ.}JA^ zǨR{`!D9tS+=N7D'(M_zP`jTj%Mf-~ր Au@ANњ]$IC[a,iIx~Ѕ͗*=Fh/b&TZߵ(5f:͉]HͮaV|w'>+%".talmnHO[& p{n%N|@qE$G)[df(qh Flw SK}O^2/ JŅ[)ꚇ+UŢ#r}\Q'[&Gp1`Bm1Y'`~5q c6TbD 'hoZP|M_ţURl0hB) `_6h̒I ŸߙJȬ8pe[|(17GO- b/yX6Ba*HKS"U~:\te~$ZL+N HOfY%j!*ĘHKӠ(g0NҪE,]&])U5NfW [nҕuɼ-4 ɉb@غv/_k 23piUgv (*$C@ah& ijoIVcjKYxg{4ssQwCTH,eevjz$Y!3HϷ9:VtVa@ut1&9m@*3]WX—/,8,&;rۼXvg|q~-%w7I01pQˊ6w]݉s"A]]; Yj^xZ>sq4`ҩZ,%(aoNX"HE^}!}";,PI!tGY l{U,-)VѼoCgltP:ϱd\~cVmMr#NǏD/ e*e$K1S;Iᢀ -%;)(N~7>\㮯ED@ƣKuZpxvo116_y$ul_)'͆9fONY#9P|xhT)ew87쉙D ܀UBR\/|'~ql#/Ulې^^@2`Ӵ/Z򷷻c3zR:FwC S4zrk̐vGm%20}h +`]n=GUVK)`*Xi-NH07-gVi5Hp)Q$H1Aû9}vvd\^>!Bo~JJ9ءt#3 |kce0w3=f,M,m7)sVwdEj$=(\ uo*dfj7vOjt(ƹkΓH1u,SmQ}[ Ň* w`ա*6հ]u[s'ikSZhYG(}m@;l}oɭ;c gf=yRi `c=1.tC3IU>нb8VVq)_ߣ騅c^A*[^(4I̍$s,*JZ~Њh2wh(1)5|Ep&R^?:fݝc\r6~FȥeE~ߗEVxWPDOv*ZܨnlN) X e<ᙰ>`fMY@-C =:猕ĺo7mJ6T/YRXЕ.R@CN 0PpjKW0x1UA0-g.Ca2dϿ(>Λ]PjS~7)rwZ RbJEq .Eg萮AuQ1?4LE)=iE+^t_GҖߐnQ~v?md#es~R?z 'ps^, / $' FSjjl_,ߪte#Ck{5qޭ 6԰7=#umS btw:J:WƹiE <%~sMlJ}Ʈzm o@@Nknw*M/?6LZ:ơg-m*v8$$DV&@./D9Y}xbb^A U4>ZtǾ^@V,$x %ʾ.N2Zc頾&zwbW7ep=raZfeۨx`ƂH v gC6u*]žT1¸pnnE/QL9X$VĒ bi'\=txZk5Yٻ 63YgW80 [)80!UYӖUvD!'_KPBjiqdn 1A&&`SF!Y \gPwsƊC/G46ecTmaF"ֺ?s$;/LXc/ҼE1]y@.&=.v _9"Ծ9dOzŇŽ{lv_/d^-.I|֖Em ` TP=9r\ f)E8 `Y@ߎB/廛mA@MY -')4u][лq*CZʸ`M[6YivD3X -C.2+Gp.@R)Tm~%opo>D3'!5M)^J"J*?|I$$k{g=x" )A$'FeF}3ɣCou>28DT? >YҲ&Mud&쿟AT8Gi4 ѪBw ֤6|?!沓;*3q(@K:9:(gQKt*)=6j=Ci{Xt<;+S? :) Ftّ^Hɥp 98Dž =,D~%VVTU]m!J­$VR߻O A2 E8.԰Ϊ^m>X㰒dˣd]<2s,m7dT!5Hfjz..05b@iK-D.*+v {Ė1-'Ҽ. /p#&h1A 4䑰sq%c=R. g«ZtXk^OQ*@_\+1\s@|tz"x٭sLQ4n9n B }o5%GV32TP.P at ^ j sahj+쪮*+6,P2$~BB{w|xvm+4f~ 漖#/:e7y9:҉PL ջ)`VI}&b /jN4L8tsrHx`Ȁ7rp у0t S\Jf U!^QW&{:jP8{;j^_G6F:,n"&Z֤NUxNc>xG=I`c̚pc(Yu*o-@2*My@o0OPx7y:`Iq鏳pwd6A֐5h|Q5}7w˷'υߞ!z0Ko塊_ sL#^k-It@iB9ꎯ8\tm:|_K)M.|6AޟHRyT0d'Uެg l .#O#i^sQc2t2Yޫ0ʦ#/BKWB9aڷ;wCȺ*FZyﺕ%*,"y{e&ݱK;RpO HXe$73\z.L|Q粷ٙUiw~cX(b [#̸a):;Hj҈FiS?ogDn۩bDٌ1>}p_J;TVoJ^dQ%Ia.jR(ePfEtX&_9] TQZse/rA2p~zPE:"vx@?EVC f:=s{An%_ Rxȟ[mPcӤqI(j?Vq DN8<ЃV@Mmc^b99\'u)>TrYpԭ:O(oGY-9!)AQT9ԱP+v-t{W&Uei[(%09m[XX/hfv(Ą;ijӜltIogmt0q$Z]#F.MܝOfSN @ KLQߟ>5$ZZ/ 4|b5mxžu0QU7J^ʵ ?eMM d(ZQ_KeF,pVU$`=a hA:\„m.R5t FOj@IVfrSBkTIyn"M=yo5K}PYThׯ"h v;4K$>nZNl &;%Gp5A\G 9 DN2rMDyZeDa V,bxb.C|+K!2CiXIV&$W@|i}Jj)UmA'ׅ!.z|_#XӦ,Nxx>ڼѕ!vMO#rˁX7#Ć\?R4R ofv197= #CSaw37g;fd/Yoc\ ;_4}ʛzJE/oGۨG -k9sHޞ5XZ"x]"5k?צIzqsYHLxvP-Χ.iۋ~VZ{N器]oxuai8(jFF&?eq DdY%O_lP5&\E/S "${V\'fHO'zP37{ggw<R$a;s]2Q>WyD~g@S0h Srxi 5GnJQ4 >N F%f yKR-M-^]>lC08V XK+(_0QTB>M #9[|o F  &C'8d;y`yK} $ OŢf!#O*øHQێO 33t,dpktRV\5"8z5E~O3A셥 cNgv%NP'm?[0q9myh aqjÃntw{[.YMskkٳŻ=!24̆.bO>ɕ+׉1΂cg?KnͲ.`-3h>cJsI+2DgB\MX¢ۇ]`gRb 'h6-bZJ*V~ 2g7fhga×%?{|H h0]tlzsK`T>j۠9w Yl$))ɽm.e:<7:>GlW1ZpbҮQHh:!6=[ b($SyN.* ݽ5IuUKپ01fM`.}b 1>ߋFphT+G6q`Q1n21,YHK- ()UHS Sq#ٍ[ƒuJ^vt<|Yju%ڣ'I|fdUgI0kDOX/vƄmDግnkp̿X|ۥ{W|)!QR,.$l$6BHw0:Yu-tETz s(FwePcsOJl[hN׼rs=\C S1P8(tե۵GGi3#>MBPa/] :#x  i2 7wu+$+,.B>@9$";J2o+4\u)p ,> qoQʢ|?Xg ~P h)9?in]K1k_yJcWiC<iģ]trkG;{S^?:?G'P<]jfPRPe{vlۧ}9ɤrV~_tGO6<9O̿>~&3LqN:[+ $GX*lWC:GY ɲ9uVc OѿBx_*O m,UB>lj1W7qs;;x_Y` H LfYuU-s 4aTLFlҵ1cMݙXnW`y,,iMЬ2. SPs/`{_3#N΂)1qIvh9*BQC:tr99yfiMiPŸk] G=DQk%WTPy4Um`(G#5s1.2:YrUzTW߽e.F!]`A܋+IR?Vϩa&E~KT`@WNhW:#IL.b0a"q!91$N ^I<@^Y"w|)$t7XhTnRJ3k:Ex"Noa'ip۪Na +!s~W4OqBb6 *M1&yH؜E-eNn $8v,X˔3A:j(9z4=w&Xf&<]tJS^I6R919AF`$RԵ[K4@Dqna_3E ϱ}}_pvX܆ӌX2Ϥ ~Z!~_/; UGEٳ?9dZc9J,Ba~"\UޑLڋ/ӜWIK L tga{\J&3U2_㒫֖bkd F]wXOwǔSt(=):'rBb.6̒oeJoub/xI9c\ES*]&T&~3N@U/cد/_['F)č(ϲfIFV:"/w+khla9sxVџMDU6ԉ:囻Kg1&#%V.uL&< qn=YP|?9.DCvӔkY>QhvhFz?z' <+D%i\KWm!p&'ʅrلP@TlUYXEZX_j;>mۜ`$@0}]317æݜɚk=@DiIAhFyL9|R+z |ņ.|ڃ4_g`0rs*0?fPC 6.t3Dix:,g\ <* @BGٵ,eT fiD4Q]Napu1a\}لՎ)G`7 _3+ 8;sk.^FTt%1uyfdXXSX&Rk`yR;!gNv, u=0[n"pv h 6Q.@TMw ֻ[p 3̦>^akߎH,ScȆᘕǺH p"\FE$/ ڟuqTc/+Pj~s:޾|1x|Rn/Wokh4w h)jt_Vq,cz~оP;bguPe m,DJ0nH[d Oi-#uU,'D\<:A^/"zri |b; -~6:b+|x4YPy.,#6km&;ݣ5{0/I,)K[_d}\z<)~.j&&DrCw9&`i@tD0.: % ϿXr(ggwo) VPtY\H dL ##߆Чp}ƣVؘR#BK|RJv3KoeV ^CؠI*[c >#̓~$ ,%4ÆPo6yUa(˄UD-؁ZG}GD}R*w^oQZGyɧ4#@ ";ѫ5Ki2(؉-BЖ=& gaSwe" Flys9.'C&!ub][,IϞdSJGF\@OOM|g?YZf~w.b^b?N(d} z"vR]IΝ0O]( v Faj q4,.z)f RtXqg1+ %?`~`Dr`=r"KZX\#{^m,fO9 m7P""l0e0C`Ob p64:='u1mE+& NN[s ߙzJRz\m&D3؏}"c>d S6|6Dbӣx? @ȧlE -Pgf?<&; =YMq M!=H/vљiY`ܥgf5ˡ_dQ E%Z)rQ'BEx">BlS։{36z }-l殁#(r&I5K5J՟u/kboo0z }BKnwCU^;Zj_L i} ̡UQ,ldvymJ6 Q+<*Pߓy.|n[`+LQ;+ʭ[%ȠR('v%c=܌RB ]H+~v&BX2[z m{Un\Fk]RfL 9[ ~mçJ9ֹ#~6ebN%vN*^{sx<It;l&q]"9$;ƍKn(wv |.5(&X5a՘ W~a~!=A0 4`)E|^#e> Mf8)4"DXh' [CYc՞`_볠Tz=%6"6JsPYw#r޼x5s᱔iܟOU,x/,AQ-]]ӭ0;UroD)D3vƥND5OuB IrHC'8z|f*|jD|UTw h*sm(s"TLpMɶȣI|.d%bA@G چ6_ IBTLo8nf{~{4Öy$֝j_5D8ik`byg&ZlIytԃ5D/{z4v`q.è'}vGxLxFB^X_%&vWz``4f0h0QKwЂ~bFAT/I @Jt,`BʟJw C bG#F%h ;?>|NQ.FGP4!w=B6|[7Eۦ["#2lKϧ3K/L.{L4M|B_sA<ڵ/v&WnGپ ;h:Rp-Yf7(iY &̖3",k&"a)+ tfN1w ͡!_(Y.{j#=Z [%zQ-힆.?\59q?X\ &H8?| `-*DFn޽,#"5?7{Vkj4>s/G &RXl%mΣv3F&9Oұb"|Pb? ЗLDf8;ofW)x^#絰l|9Kd&WGYy?GL'SpdMBheaCa%!u(Wv1l2SFsZ'2a+JN&*߭Qkvi!ʆ$A6?;RtҮZZ[Y=h|Y x2O/[ K]ol#Y9|QW ,k( Ra}胇lAtL>%.c>ⷼq<`g??BLܳEnx>4.٠Xr'ʑKE$l]Ქ057OsS 2U١2r mkXf$:[ ܔy(=ai嶹~lݗ0Z!$3ioi(L5Omciօ[A].̃N~V(EG\aԞTbcWQ#ޡCoA _<<P7 ZW4LE#)4Qsf )h㚝~b}ʐ:JXu- bS~V%`*HeXi!n&I؟ټsL8l#.0 Uwbaz\ԶNFAr>mŪIBlpロ)l@DJJ]GKH#!`ʐ뫪 jQwܽG+7Po~o.meN,*MRL,&BTKrC>p^EMclN*L QLU36f?f:w:@K[ב [BdJR8%'W˝m15Q׀CSLG&aR(ӏHt/>])ܰ.yGT'AI[o=;www)x9х+@K'1<,z8.^iB-&pFqo%9?zbEo#s*,>d.^퐠j prPcC?{F, =D97F7rnj|IJxX3&VLA{/k, o5KНf=mĥgixn 驨ĵ[|&og3>nISre\ 9^L!f봅J4oL(h:'N`=7ZJmMH2jĪ,q@V 8vi[[50!DhK >ts?08%W/uhw`V%=H4A NB0MZ˴/q^T:U#sbZRߜZ-kDaȏƍhUgsa'FM2zS5Qm>pķ8a6Oy Q kQpM=`ʕ y!ۛ JV%'$aUԑѮߎ=X:_&Zg?rM\HDlmN`&f p{n7r6kTzy*պ80r1½Y+8w44 ]@dCs-IAj9߂{BnA5<)^u6˛HlpԌK!͖[r貒&+PAe'6Aa--YșR;ȤfHh)ʒ尊x[m.Öç~@>H5sYiUHGY+Ɓ&8LJlSHJ\o f+um;{Ų .,9 x:C:q%e(elkr^HI۽1c >^\ r6{~yηf' ;ً,zyBPP>zBmR!]#O ax0/q4(=)DMIP0O`4E_K+jG6zIx\S T"( codd%Ǜ^% G ;-ikL%X a=%ԹhT'ۙ=cï+t6%7Ë]bUmiJR*0[4cN<,M?e&&azc7c0ZS G*-d|1D]ٿԏxbx4NͩG+ғDS EyǍ?_6\D>$pWɢi'`?v6Z%\yA ez1[^ 1v[8e"0Cw. 08eJe$2>]O32:QW9ʒiR{3gŕY q^p(2. jѕ^55XZX+ [j{n aQmKi蝳S'i}[M>=˃5uбf@EěΑ_g&~Hc ,=hʞ0ZI Rϊoǻ)|><*ɛѓԄب;!͋j``vN cjF?cb9۵N@cOJT!_ngŻ[17܌k%&ɿG' e,<ꦷI0{^uN6[h%\KH&u%"t{:.",4;@ UO&|*-pxٔ'm6`Sf|B'MŻfy]+tIf5C) jx~>a%qQ6cuá^L/&z0\Kzy;<0ޟvOߓZ:[ۈS^[۬<7+ $Q/˺#QܫLz肻͢]~50vE3=|2Tt@FZd-2c~v#B6KY7VNkI KWp)ډ8:‰b&uƚc*] __|uSF]9i?@,菀rhyjeC3 ŬIl{w~t[XȭΆr/M>86é-:bȊbD}T=VD%wnΎ^հ}+#CZMTWYkLsY؁±D܌ ':Z8e%{`)Bs}VWD_3l^): 9QVsQ2-7OE..XfXI lQEEd;"@zȌM}$Q=4GP-ݪoAͻZ3EK/!Y]Ibx `s+}u{5̴qO !"HTzy:GU~:;Ve㦊'JmY}>`\ ۟%vG)<'nFNQi [%z$*GU mLX}`z 4Rds$̥UR~!P2$*-pp:u`)kxa^*RT"J6S1F_TEk'ӥqaHւ  Wf:bᏠ`@W>%¬D -He$s04 ƭ%ɴ氇\pk|n'GN8'!Z_"xLO-#nT|޴CmL4;Y:->~5#[oN ? XuީIQl+ⶦ.}(YLD_ǿ y FJu˕% .NA37ƧuVS',Yckc{@C9XmXX6؏Y oBĈ\cɀlrzڦ%KZkK}8ҾRq X17Eũ{(f8!ߧ<@kA,߄@H[/Zu1t2u˫by˓%%|`LzOG;G:zZ[aQ*pgz])KOi-HL^^"WوWI]p֋؊FkuZ \&9j<͢OC3xk? 88+=5 tV 7ySvRG|<BR0!fSܒf4V)H?ū/0CқM歳:Ȫ 2C\R,uf?|DHO^ ͈BiJs?扩w<60] NpdĬ` Pwg|eem7zlf7rnV0 +'[SnG`#ԎA4P+2Eahgr&FtBzY M[}8I([|^tEO[.SIe`%v̒ r:%lb]j raa?8K@ɻLL2_ 8VtBG&V`E qzN>R ÀD_] 1'"Deİ<$@2} }SOl+7ⰯNL-)Q)Т yPRS6 ڢH卮XNĔ7o/Sf,}H"y@QPg&ލe0)X8:Kɽі|7hur7isؕ#_BK:YGat߬lC$3sQ[ ×܀gWEĢ ,E9UemGmˏMnv/1IDEõ0 m_9x +7NX{oM@eVp|oV;tJ.}[N=J=l/_U9n*Etnd5=, ${<$FC), >QsRXT=P1Po,pDTZ_䩤3(]T Q= ~2I68V[DKEtTHf@w-_z*ݔqR-9 c@d0&/Qz4EA~v Yf8j&\Vo`O}J<O& N6OrUB=ob+X`P\4@h0=7JeAH@D]}<]x_yQ "XzYe,t {?hGs_t_:98?cOTl*Š+썑rAYN"l-",'k0K+yC-;u/E3B*G: VI;P X0΢!3 !W!}W` B1{B 2^?9T+l?+(hqE#31IYƥ6 $ࣧFPyxu󐽿@|iܸ7>{6\/;z -XNG_{\/rUBmB=~[FNM| %1!kidwʥQ:(kY*^IbE|wuX(RX<0 5Ԥ5L(.ǝlJ]?1hRƘȞvemԡy5L"mfфq5ZVN76Y &J6UQzG,ex~6-N^SnbEJtm -<f&@B|lgdHaH. ;1{[r]Y׀rrh#z@T7VFu. ݢS8AT-ovDž+1(@E O.SYwg~MIaSdc[?kXpb20ɕ 2i0οLQ=6aX9V1T}ȨA|ʶ@}H9z->%6~^740)ͷގϤ1/7w=؈L[6P"! pqpX6E_n-vJv,ͽ/S)kQKSmoЍ䑜u@ 4d&|cӅ̦l[6-1w$ॱmY JYu` @➟?f&^G<ޝpdM1 j9 y5e"6 ֲ H'0+ߤj's%}u1Á _֤6;"#>9hӮcɑJ EGvvfg? ۧ :,n&v2yDWx_v638Ԭm?N\QjF<,HNK2r:2AbO&z!'f1O=X_%#66kgbٖv)я4">}r/܍#=~|d/BZ&ȃ'>CH0\tZF[qz Zڱ),"{t5I&2m)xyW- EX1F\G$bbN:^K gp5iK>buߩͥ<*qR^z]*q|;qVdBȉ;4c y͐mИC-A>[,L3,"յA}SYu7ՑS D JeyYqY3r^ߘn ?{^z3d]6GL5{ [ H*:,O CI W ;S˛տe7,9`$*{ƒpR۳{H"CC*֗ y|$w{zJ}>HajF8ϊK*!7WKɬ8* +-$ON#l -e(0^Yg83oƹ~ȠёA&|jz{UNIy"'ktw ~t֪ʮ\mњF SB N9W_J:J^nO31)e' բSęKR>>WnsT/EVȽ‘ToDv .-@gbT=sTB_FJfڀt:aƑߓiQ3Y"Fk |yOw'GUEc?AȜw9fN^)Y!9:^qgD M 24-o(-a5X;wZ)UɗܑZL@ D"D!C:VT/T)F6IYG{F~BgjD$bwJ ( 7'DsΐOㄭvR=q>If\#t$Gf}(k:vZyd>RhZ]nG.]9 kL_D=[s MvRtr&ӮLHs+fkc]}|;N, \(*0ia9EGv[y_7 =H+۝-N~fLAV^ toC D7p8~WB?DpVIxD"_zlȦrL|rsccqW+Ĩ]e-pmV`io4(祅Fo}#U&5( ƮKzqjLkӫTa(Me Zl"%~fx2nyl1ߍJN"FA5|Y{K/Y#l ȤoWHD!L{Zˤ )lK^zjHޑ,ʧekL^Q|#}dx&eѸm$oe6oY׃if`Ț>MH1RPHar*7fW'"u{:(KnAˠx'pQ"?h*ϪWdTp~8++7[m]18zׅRb(L'}4MElQ0wP|aߢ;OR"Y}h)lO(];^ |dgΉ)ɪ"{h-d7Os%I >ZyMmvEaeRHPWt n}RK3,kb C0&su !e$Y Q i(-6R)Z ?R "E"Y[~i\<ƒ=1N@lVpN#<%-[IQ5wʪ`rQkY?SJ#bFH V\iOԧCxdTT4dzԪCpABSn Gp%qzh}6&&M(2g` xltPy ݂bղIE6cH< 'ݘeH}[fF̝ 4`:U"+FԖ ߇RD8'M=ABk]"eeC7hW: ZTrbtOi@5E3ms9x]u^^o_6gnH}_V}C);?A `eO<7'/J4VIҧK֟NO_CEvy .?q+]fGhV_*k};mMJH-Z\ 3Huۀ26hqk\Ⱥ*gH2 9SڰEJ;VɡQ55^'7/,A"S$&eFoMqn(+$ >Ƙl Hncȝ@=#3 &xNK<\s?9@&w)bg]<^N?*::eqVPCs5D/%ɊG|Y>Q,rL%TJRI>G8| ;mljSw9r?xE}Åׅre< wq@] asO4{~%*c̗*"YHVfPP_'dgAF)]( qA45J-|純RB{Ϗ)sr։NSFD%]OX;VV%sِr7$h'iDE/A崖-vk1d9eocnaֱ*߳]+eF}^PJJEb] ֐gf^a'D4~dRpJo9Lw)$za;)L.7> +VmOР /Y%M5h 5NE,ơIF/]9'7H]5m j5vvx-D;׏R*¥c$ސS5& ~̓a'7' .B89Ƈ7X3{'UՆZPEܥMcjtFM#E;ʵ̜VtEgK謀˱a^<`qBh7bcC'(o"F@8b2Qxc샬&\5Q&5El6{+@^/d\A;V~h1uu8V" u C} (UZl)\~G΀^Z=0S|xljBueݼJ [T= ކ.*7.+_0xl-4$jDȸ.*3?\hyrco EΤC GhWXSŵGNKs@ xep)$ӯn#"2nvKYw)n>k`S#B:HWBGyn%*E[(G'fm6'y#VŲE/3+yD~fu!q,MN cݠ뫽痽r?gF $ qwdGӫuQFq> T/+.O]%.9#.fL쑌M.Z+s5Z?C Mjqv7<0*3&@Ә]JfF l Saͮ[tߦ(lgÐ*_:Pse8y&'}X:T?)!Vl-i@3+ pXjyAz˥Ji<@;;<4%g>=?B{*ӒLyen 50\BlT?(Ɍ7H;nM?Ek.׸`ʖ\!IϔFږ̌9) ;,yhdVʩ!iQ;̡Lߗ.,HޘwcҗX=0`Mak3 #L6xf6QuʆXh0eϼ .cpð)c8=j'Tly3EypؚdhD^mpw)2r|'wŠ6W廕D~5}_ЍxKM%/)z]'|nXH[2b ȗEM,Ԑqa(O4ђV 'Xch`~ [:mk3vtyWoL}& %5p<N7{"o-F7žĎRz{ӭ 2gl^&'B' A^oTx-ՏE /j{R\ek=ov];1fBQvR _5k{0QϷP`",(ǮŠ5dn^A7*&{>aHRbڤ ;:wqpoWo}LQK`\e}5JW[lu?sG3'| aVݝ;Q 2*gߣƂu)p&[7'uzO4MMZIktjHMpv@Q!ҷO쏋{(O$C :׬.nY*,}wcoV fK3.mA浪QeYYŋ5Mɼ̨u rdq^jZЍ:G P P@8:_8XWmC݈=V!~J>9'3M?HZ!<.C@>}l[?f;")p2ʹMf6^aq >@=vJp[ !NH{{j ~Yhh^p_ȟGl:.s'Əg_l' d!:ű)C1cs@۠JQS{u}QȌFp<0QdM}FE홪'ska! Rgtoo,ȥ̒^Bh[0#{<3%g8JFd,̶c9*2x7;*Q,6VI5gNze(u-V qL y"?g2_fytdAPN=vcMjb88r(E=](\uޓfsbI!~I 1O?Z%*6;;mYK>A (BH_=qK/g@EM [l2,、uܶ `bJTJf:IQ_YȈm5}}Qm'?_hDI\0 N[v9^ǃϲ+#isWE rq$ZO[ r2^る!Ir`j{o /y.xˬkU}#{l&Ul+Hc@oK|&L~)g)QkL-?A[/41E-^.NV}W,/6Oy~Nukr>B=Nlsl>gzϯ^>*PRg# "!=w.#lњ! UwENO(NN|gB& \Z7a%}1A+I\HIpx*^C,m|){ 6h ^wy2wiK>]ArbC[{~Dg*-ɟ-CThM*C'wƔ-B,5fF`u49Y?ZOM9Exbp)<.kWf3x rEF+q7(6]&)x fx^5՜Mpv4EL|w1? ^%?d  !\oIDng{(EDЧڱV+(Dag#0 5: "#6X"B~Q. uXOC7+Jmu&"97*Ɉ4oOBZXi a=!Pٝ@P88w5z 7e3>|}_ lt?QҨZ(_ vJ]܉ǣ5Z4*I;Ӛ.Բˬڬ$$LJx0c'a1cዒj 0Qؓ7xyJ7tR6 &ˊ~] RVV,(p0i+oA f\ãG3Id/|/'˥XK,/yӸ 8>6_adMU塈DH4~ix)kwՖ8̜"bOWlo"*| 9s/t X5!ʴP;?Z ]cЗʙIiACRCUG#E![C1%C0Si~ނmr`٘SDg 19͒3CwE'w15`<+y狑OOok/&Md^*irՃh>dǪֆ*xٻS y@4 *j́ǮNmV+庰\ ~wj;("\T3_X4-j54DS1rƢ_5T)NN5FysVQz)^HOQ;<ϼ4̺Zю^Hy\}Z/#} RԉLt]mM0!ʒPvOQY z=~$tl}efFGKN>"6@k!5 q00GҊto|Nީ7q+_)^'i2Tγ27эOcM6#O:{' r*1kOay.4lۗ,FNw_-E3m\R̦-*T] &A c>[?# KH.G2S" 4MHuqY]b{bWy>0N gsr4^s'wkW0nh/'5xMUKу2!X̰GWyKn $~B}"*VAVTvG[oAкYpɻi>  UVY.=RV?֦*ͭ=ǬܮYQYdXI.W#Zs^%_Jo<*;'!ҏ8.W"h+(v5d%Sbp(F<&6#I҅NG+ٰK>O23;Jt[w5nN*HZŤi H+ͩ9A mdK Mu<|޾Jj9dj*-pL5h2pR(CWݙ΂2tm_|kdGA4Q[COBtXtŢvSKK'Ij[T?QycmYZpVl%֯750 rp72E7{PLuR Hc) QE-/ :@ BL#:MrUD[@HN {?R" 'o5+t#{ .u}Q#QmzpxN˺p c%]W?z2s[4uF }n}2"S-51Kf9qh_gԽi_%3@٭wdޒB3v_W$s t{c!Q_ [/PYUAp Wc1=$"H[x3K 0 R<> E 3) UPyiV/ǖ \;뀗fc >iUlX~7B!a\ 0[ۖRws+?,.=:N/ d .x&ؾ5>媈h>gnXt6x-F#&X ynAU7B<is2현OL]\b!$A`/q45]Ѻ2k0I%I<0Bf_TwT$waQ=Cwf;qgRͼ+HJ{; -@Y+u1 Rxyz] @5.<ֆ`顇7օUU)(I3I s(tmغ)gcjs-EB2T `0ȈV\(+>@jd$wUJdD_%z f,U;FFاфes<~@\e&:3:`ėaЅ y']]h2R oږηcP~wE~i]$W^МF$*TϷ|LxGRNãωs8|K6bZ&tnS}l=5My]fLZ?ϋѰJм1_-8^}=D=G_tiր2NU*+B@S=͖%vsf`"7 mZ p 1gU}]0Y3³Scmg,]m#ϊGMc0[E<3|TŽ <8WO9H9F ]?R>@TBhOĨ #Ld(+;FgF-V1"tS5 ٨+=%Xdj>7UCt[7謞XR) JJ2{-z-L!]<9a IrOR/^_rbCSt;[F5i)rBk 7ٷePnEmb7g~#b RT՟vƹr D$v>)?wDYS%LBNoӭ|":GYiCkn[:M)z˅wӒOO5kǷgz#>b2eR=l7ً٦Lk}otUbŹ_3#Ye\Āw *IjUKDMC;KհŜ Ga.(ky2C'ɒ-}Ǟn v4H%Ba"02x}29:;J[ ;/3(AxoE_1/*Տ?ZIb_(cx6"#o]/#rm*'PUk6+ω`a MiDŽ H{T #i+ɞ_Mpm+北sSuQ𥟑Vt[OOuV*4u1[g7wh '",nCo o$C;KH8 CXl g]M\n8 Bԋx)h,(L|ιmsx>5RÒbbFf ^טj+o7 py oJB9c}ͅYZ`s| H)ůxy6/Y_1>2@wKmxu^Α~Zj}.uHyOg4ȏF`["]g3LsH-QI9U+~/-I$:g~b5Q+Vrs{ ˪mF='?p|}x]9Svt)*_[d{8FT;GcRɩ|/fcױƂn)*)Rj|q{;]i4N2 ]}d 0fNpz\%Iup.ôZys>H5I7:j 7DVO)x2¿`f[MtzKzDm8bɿSQ0ȪkzsAbsYe[#OaS~27bfE.uxV,@-4$ #r|KO-784$.b] Oaz ?#%l2-MwF1./iktOo`Ɇ+89fL qtJ?۝*"\#ZH ш^~*_D;s*qlenxI/\G1j㋮||Ѵ2lΪuZgiEo%Ef炒s;}7X\3os7X6Ic}S],B0TBXe3%xflt>> D>Kç8"Jc5ђ'XD߽dpͶ%foo,&M&˛a.jţ{(w,s\ Ct? 6a+.^ڪRv ! !<۶,r |QZl8 G2uVmYx1g/UNuX4@̗n%r32E0I+ bFpL_%zB'Jr, Vo uyB]F*{Zo3Ф&ʗDc-HtG@Ӌ㏅8ea!P0B%綘2-0+ )i@v+6~`#`LqXnDL)OX+%[t XC|{!qm>bAO<#^C|وGB"ybqXyuî(~6nEqd㴲a~@șI$;f2SL,ォ,㈻,%Cd/I䙮`ù悞̙- E9{?S6MI:g3"-ɀQMrvɐ+i; &Wiha7!9)|57P4D8N?"iUm(A-~?dՆ*DӶVZZ.૤ĄG{u.mFv|&Cct B)*r394Rcr„Dn~ӶSm-jwCr yyP"(k)S_SB*oxund 9/ !ւXV`+R׆iKezԯ0`QmEAՂo}}i6b} Eujw .'V͡*7QFn*/ 4&[e.f4XTׂ+D(aEOf8YoRT݃"|1Kjo~b'W$ʳlo'*-DO@ EQU??oqH-gϧ2s>6TʼnY"Ŕe0Ws#KqHܐB8b]OK=#v,M@a|'JXlB`t!JCV GpO?]BB}b|TnfN2Ps{[70Ӆ3|*!ODnR EQ 1A+kO}@DVQGŠ?{ԝBּąK{}Z(~RB[b 0k;eXmS6t"}_5^n҆m,!c\`["/l*)IWKk;^:"JZ@ ;fcR] cVN%clRF3z֪(8 |CY̳yY ûVĪC7}Qeܵb, SQ`52z:ڔLAx~?+G3͢ d ]c_Lga$Ԛ̓ޜ8XEY}M%jfږy՜,@ȐLQePT%hW:ſMIVh +F5:RdQ:(D2+c97˽Zԙhm6f{\g׬N Cd3D].:PQ'Qq)yGВj5; ۪PwV,:W߅0WӦKX_Tb=ҝ}v@p"8$7I?Q=KHpfRv" @qZ1 Kx+}yOt!)%Yw=O9'%߾͌knퟻn*%qfn*/F}qnj eE-B,'9!+>\D{r 9,S-MVcퟹ\5 c|66u;Yz^ gJx6oִ쳮 @E=U<avT2ך٠t*h?QHz<ژ}er>cKBVJ!˗]W $ZG25fny)7H3K6)ƱCԔjuWYFmAY&{ZLH*2up0Tuj9,Scm8%lkQ;ļLZQs?Q1pu~Y2QKaG`Igl֑I "e)'!ݭxOrju%t㔀yfmgIo!Gniy]{o]IZ8-sAxܹpk'2AEA0AgUU x>6pYmcxJC M(%sT aҁ޸,.N4ΥQT^S օ(ݗLO`&d7q+wƟ \ ~|Dܔ3A)Sx Mb46?Kk{edugLe[.eü.ՌS5W td`w jn~*l| ڶZhL0'ZI<.skw%MJk3m;7ܧd]R}2H9ka#o . <i"EI(ǏK=SvKuxЉzv}MK#F,jY3ew#ю|\2C`~f>سF9Cj `c.ˇӪ5JܥM9P<+ON-9biRA'`_:~Kq72=|ƝY|BB #N9Ui#AItA=8F"fXb}{hbj)NW, e S]zoJb9_K[݆R?sF6r.\,B/:x@|Xߣw+Araj䰤xbkKn@fe|:$iܘ2lyJ>L 2:N-3^ƫ@hr$;s\"ijG^`[YwFFf/Td_ϙ(xtꉓEoe ,U9X P<fm&FHDE } u,q|.摸-;Ją_i"*n|PLs`Լ4|ԙn=ۊVg Zw*-;몞2w; HGD˪1W+ccpC36=*B'1 h.ʚy. W,?wq&+kƯ9owY1^̻VahC\v{wYlpeE=>9e=Kjڵ t]#?ܒI{bdMG3c4D^'Ty?5ͅ ꪪsnJSJ Gf'>,FJl"b寬›k0.,ňp3pȾdߢhh[⫗}Vu^94O]'=q>)䆱!+';d<sNp kǫBOuMN; ѣU5|EXr.7]Z=#dDM1Uב"/. e̟'< $˴>8QkHXL>WC;ZL^(LJE#ʞ@2Di{Mn]BK<^aǠڶNTkɚ W,]/xeBR%(+W ^>#d}eeT{eSxA1ԁ%[3D5BO܆,D'kTe[߮Sxz5(Q&0F t|_ \Mד*Q'_T% [m_IפB cOXLFbD+)}CGn[` "/4>_us$x2σdNX~zo;8{.q$gY\dA P8淃ucs;hElZs}:NXi儷D ,WWF߬=/G@=ՆX˥ZY_hwH|-BC r]f9?oIN@+5dQE.a=8 }9qc*^ZYF;-< PnI Tי$s28ۍAo6絍}ۖIocJa- %,&?(GK~X*cC4S.H_?ze_JTVtsuX~kc/ 0<}tͨjS4PO:jƿ!SiO\¨2^>ޔvõr'Ş)xU #|l4yRlc r?; 0dKʬ5&"ޕju 0y so8(VίrՖ>,7r6఩/K-D Zc)0 h?H_2%k$e8MޭGohmC0$ReSdD%\Z)'[)w_eXy>ve`RE2?`I̼?J;e D]> %tX%d'qiJ@L*ǫӦ֙haV1=AV׋\ 4a]NwP;?[mzա&-uϏ]ߋU*_+J|S!FlR 2r}9c;Vِ8rRڏTsUM/؞ߔSՅ.W'Dx_ d{ރ ?WԝL)|gwq<exq-Ge?V %B>澕Z-Z+<H CJcENwb^˜t9!TOQ='~Dw\NX;49CPbV7.$7?yf381*ؙeۉ~g5GbFC?Wߝ^VkpC~8<@iIx.#5STk]zx+1>So} Iw!gtib>E6vS %i{ qJG(e1a[Zo~7`W+w=O*1vC,߀u9]E76"ݟͱL@d\=*|ȯ&gn7iJy2S^o"jje65p"l:/TBq^Ҕ%Uha׻ zTxwg P4j0Bѷg"uW%GoYzhKjokht̄ K+=d.~U6(5IrڼVܤ9Gf&+b7'O0,<}5'(ѓ7? x<pO`E^?^/_QozS,f\"K [I>%$ρ]#W1|}PjaoH%ƟQw9"<9Q-@D5oXaC\m&aZb4~P,2@>7,G,Z}b^{s->3UjLX15:˒Wr!jE;4(F;I=Ry8wu[oɜ$ă^u³Q̀f2(6e٠ 5oM )~4K'V ,LfǴ VRܿCcgD_~u& [ r~gH1Ct&(YQrtU.uVBv`tKNbs u3zIKIZX4zͷ I0-,-Q|LL-r0uBxg]@~L 2MfhMZfQ vę+HR:C.dXB/stu9\[t+`j:iHIv L~ H;\3.,Y?.{_yywl>i7,EoƤ H.7 =M7cn/ utq{O}ֈy s622ל8݊~hƤOY1OsrlT頸ѣB7TI%/jW?¸BuPɡ,}o蘰ZicP:2H9r2P !f9sWӨr Ec=zBB7KpHdn[oC̩vç^ Scs:/r\bRg%`WNj' 2d`WmB{`pBݭ;,!\7q[ץzGwqk74>x95hS \x"Syx R8k*I̎Q`ϳY?~W1Vn)P|[F#xy2΂g+@5K=Z>%b\@.sNVVӠ&oZc9 Y0sфP:?*D;gc@YdFצ rA15dp^ YzuzCӏz[e8Wc['G%%.qu=I Uߞk<w&=p>1jYO4UnhwϦPiq5`;i82K Zx yHuw FҒ$έW /'ԥ/dh!J mn6Z|,S5#Т\הZV;hix\Y)[Fa`LK>,ь geT phb0 *ȑ2jx%];I7M_LwІG@9 @ *+d6pzA_0vErtݧؽ/ekl:.+2Ы$vMbX17/՝s>i=L,`\~`ߋs [ˤ) ?^lO=zEkwN 1+~B[4he#*vLUNުwaJs{/ӎSP`^%f&o*c\oه^Aso"G! ޸'l:WQ-5V%R26]ɐj6Sy Ŝsk#UrdnMbug91a_*\1$gYLr?MDķmlɡ>˅l'+u}ݏ&=(fua3>^t|W]=N)6 "I-YBʖEÏݛIClID_`O2vU0hu)"M7GsޠLBupo3 X{wp {w\N,/IZ^`kG 2`SoYSND,:B6?#&9O[%/0ri/j@uޮ3xP6S5p~d:69y${_KvaKsäIn۶FpR;A%4awŃ(kJiv?R_U|$/`,oЃ F-n,'NFP}S\7%3Nc1js"Rhy63f^ H/F9nwa Co:hN, A4bY}g7mfN ;q.H+z*ZSSoGJ'K 1BKo+L7X}֦=ȧ dD̰c~c0l32kgt1֬˄^:{ʺ1d;Nڭ|eLR*{ղ(3teaԩ 8~t>h s+.:S?,TZ_sDT~+0EXʫ~OO?B?0)O rdN %!eZ7aF nQ RX{JTĤ;(pȓ\ B$gdwTcH@8 VRHr:KOiU  SyA"-+c9'TR[.|Zq+ͺkہƔ{^]q+?eM6g"϶^wW(Y3q zKSxaP]šshsWSZ#isC)hԢM(6T);<"la߶@eHmZǯ02l%΋SY Ej3[1/%脮&(V]*Gf#_vUx^%6s٤XXD;^%_0D)Lm;@ǘ,˭F3^ Deo[ܸy#$UgDp=$9,|~1֍Sd}<%L`2Euθ6d8chok~]F\_rHDsrTRSGp64şph"w U"ڃ8]$@I K&Xbj}è7F*?gbtpRmQ6ݸ68+#U 9!L^8)Ro*De<%=$(]z8`UۺcJI~RL+%x1y1єTt@[ehSonb*b# Jɹt WXX%MmMhrX^ }ReGBIG)oj6Nqi Kl1%)!p#z eM_<P'a?m(OHn/#,঻f//wԖp`P{/AGmʣ4)TFWhfvUG'h7Z#p Ab gGG;O/E0zhVH0F1^D\11¾uC3ܞ) 1IDNF(tR$uR=J[b_.Fx=ve֏U /]ʚ4^|唳҅s^j9oې1gQqAq:K d{0' , v_i[D0¶) 22*nÝQU~_-Vt;"t&?FIn-4jPrd T?ڀ}# |@t;<˯;U0;ng5'8+! #HݽJ,4WG5@B7E /:.!n!Si6N6 eF 6 @aP`ٯ$EoGo%\~Glxyhߎ^k"n!cK.aޙTTn,A].ޭe\v5]: x%+oճᴗ*TQ/L28x:dDPEڃ]M޺Hr633~fV9Ma צXhXǝ9uF!CBrgpNT9s{:bWp3t;T7`L쑤3kQ$D O܊j5^:P}*t56_4mr:+a( ~Z_ֺ~kё9M@?~?ўuI8_A *i$UQl{0 :R@SqnjPdmu/ډQ* Y°| bsܵ j#nc0.":oIJ X :Sl 'A(j˺XAgՍ@m6njjUFo5/ 7 dgF:.4P9;dPJQn s$#Ft8VgxRYlCw|ڜ^zMk W 6 EY}xOvNљ"; tL-y#QS,DwGTL:g`ƈ,)PCqP}|ځ*FUW?]zK;Е{I kvS*+J;m m?Z"OO-1!{AP0o~71fзyپVY~MqDMfpIXd=EEf Oץ ֺ]1T2X-bX3"utoC <vĥW;+:*}ܔ?劙Hl|N4 OQm7ԯOz]R73̴``p[S&_V]ʔ izqL#ŋÓKzm ݝKS. u$`#;9!R/>ys6Ы/K(LA}O(SU&Ү+?OSC,*,~K/6k Wmȇ1&UyuGP+Kq4vӑ=(@Lݠx{mkdV )E5hJGnl4u}Yk>IN\k$'͸…hro>y|ӧCW>G/缕vQg؇b/щPVxY!mز9M,(MD5|w ۢC3p3B۸x=Nmc4ʫz = |.Hk_ H="%BSAs 08pޜٜtx#oͨl5vRh#u9圩6]:"Yf‰;Z{XUuf3 6 @$%Q7.G U,KH.K6LJs?`9, kkO %q2N1BC$%TD+" &ՔTqⷊ=@ ;# owB$NNUZ |@Bj`4yx_{4 ^TFf,WIeaPrD۝}|X;] Z^%Ӥ'eq? YMtVnSEDwb؀3Fl2rimH'DI>6@ddC1(ylK{)K?]xh|34a GV7Bbux핻q92#BY8e_L|OO&8έس#< j7&wIdg=-j!% %G*ݕ6lXU %G7$4|$-/'Imk0vr5t eu&ֺK,puJVҊ؎{9gv&}+\whwcS)uogݮkU[wuZO.]7[ CFҥ825RcCHNrv~٥c\(fJi D `>N"N/ OώQf#qw(6ji#zIuX@#KJK.z gdvY^4TTpZX] 6t @Sᑝ{co1qwa{N: A\]%뮈8Y.!LCG{(~.IJ]QGPm5m] n7[Dj+ZE\O2 8EpX\ ŜE'1n)"׭bڞ Dwl~N9F̟:n&yPn 6cӠL#?d7QGd7p6'kr|Z,`e\"tPcJ%09[TtGy̜hg1pt=ƣ2y Vc4V1 09nDCӨ3BV)_>8H11=ntI_e5v]m\X4 yt*SZI߸:Y'L>c:FOr׮.0OqS5}_\[}ez:kR5dD^,6  box$һ`'~A_Utsϵ,(۹(1QϬ{UV{K E80.gD,"}|H{y?3^N@t?0QzӉD]DNkgY;A=\N0_%ǜ<| אD҂iL`P !`ء.ꇄFn3V!d tX/S1H3SӼ?B'A$}6mai]/P8IMn%P^A0W7NJ'~0q=4#|3(3ATVz K+KvPĝkcB_5x@ubcV*z `#.e4_[}/XɤdJ3|zt8+kqnŷRŤ@N1ъ޿b)_eQ78>W*e[ #91]21{N  o`tgUb|A!6\1"o1/swx t0QTT y un~|1x[xK>$|Gp̿)2G>aZZ(1_Sz$\4GE\jԤ'v}ToKX*ٛ{' m4xG!f" >VccBWM> } s}RCdh2\cIiJ7bPgP2KBVr~/c j:?i:  ۡt [mAعTRΧw!'֩)~p GL]n.| 7svC\u(2Fv6"c5>UhC@ IEL;#HmQH,Xoë%P{4w545ڔ"/؎لa2ma `UF!E=rXdճkqmAG})}ET9q#3)߾Ms~ʙ:" Di;x"U[`Y(ZڏbTZdm-[.ff\,$Ae:G?̬~>ݞm3cmڢ;lj=UC#H^y˺zBw k=ж\sp!XnԊ,BlT+O`#Szr#![RVLf\=iZ|g|> ̳gj`LSΑ9h)ԺL`7HzUhRWDQ; eqWonZ7K6ƿPhR{N_u@'ә tׂBy =qXcS?8KXϭm)JS_ ?T?zLSD)-ĉECj7KH5%HkL.__h]jP@8=^n.-Jřz^)Gz[{8U&*u56aw,ЊTQ[6| i% =J Odž䙗 T['lPq7 RC4'5ZOUm]}b0y^ Rǀz}2 bUnrǒb+x~1xY)]kXQ,[WC_Et9$xq o9dxRǭv|j/?sIl1dLp",=z &;gur+4W5DӄGwMt,7$km{sXFOGtRT$ cp1;﹏NsQ$Spkk!P/@!Y*w; 4D.)*+{6ne?f @ t'웤9xf?Ǖǰ5fY@7p0JSlN:%@pZ AC۴M2i! e"'ڞjHha030nKU`q!{5?oC叓v8TJqx?ƏĆS1>[fv/f'g֐K# qܐ tgtѣJ#, S2SdCSH[ǐ[!@&F≊(6B`.C|gN+ns&>PCGwڛt߮{"lO?2}&j;F>nܱmᠰ@𗻤a5Լ^H(9fB1NHyߥ@_ӨŚ|-felGe2 :mud7K6 6FJX &Z" t 0;GEx;]+_{:*w2bnLꝸЗzWx3㦆BxKM*ULq_M#YK%ԼdZM/f#H%_PMUCˉ5݅P;科Rs,g70(ފnRWG@Yʗ- Ldie It#P xT7vй97yOX\dlꐥn u|*X}b۬S6Fc .J`==7^D7KHAfVWzDz1C:~X+ޞs F=l}btD*e~7-c+ξ 7qQ[~Ml:-BgД[gu@r4#U/ r< 1[)/g,[}zZ*Y(0UFMU#\[8d(rBMUW %?iW6kaQ_̕HL=UbZTe .G|@d Td@[&ScCċl#7L1|SeTStКZTjD 2SbG$ LMA}8cm EQ ~Uи45ΉxG?eq{cv;|x5]frkIk}o*حUOZ,O\L|,y[$Hq0dAaNn͈K\E4_;H2N#hǠ#Ctf1BPwFna}I] e@Ql6M0|'d:v0E?s@pǩZ<}uZ*_'_\-U'*>g$Q)\͍]=m玙;@x{gU̳{$1}͙yG/uAMNKNXc`[2SW]ڽ-32Κ@*YW /?ߑ0W"%-͗)m_uًE+̆+Gq?(l3{'cta -5õB$<^j}"rOEeE*k¢_֊= :N-3=*4l2!-;H\ <̈jG?},fP5+(l_%5zWpI29`NnQy<5LdydV{g BpJuTC_CٛV(ܔ 9[Ӳ6rt1,Sb^/7ЏпnG-[sS.WF [9ʪ]G+IMac W$6[g)k993N Ԓ0oV xoh ͌#X*pW|E;XٮJ]dFJc$Bq}KwC DfPjRViׇj% 7,kES=T΍BD4h)Ma4WjMD"?j:}$ 34"'CMmZɨ 䑉f]DZU |ǙQ*+(9[xr '}?B*/+ۜ c _к ^wv"JÚ_;)XE{MR)OfS(gc%dYsPC]PaIqZa'}"zelq&Jkc+R6ozw` 8*}sDUh#Rw؅XYQ33TMm)$o?xoPޙa(;bҬBQbNLiu!yZCtkNMAĪLfg:#Dc\nّ8:X5G*AAj L{q]2ҐGw%'K0194^1 BCYE%7?ʶ8OW.}_:ћU9d`"8~Ic- P 'I,1&ywgI2ww Sxٱ6Kvp>{pl6^ވ [~ ?%iH8^ eNJQğ*´0l+g G⊭f&0Xa{qfdsء{_0 F-8.K @=b'z81oG\H2t:D-{&fsCA"=rYcu 4Yo<p|@EL ٰIFP478a b#FK&`݂9A[S-TWW )%o%4Lt@|H[G3R!H[L;%wm]k IKx2@99/Fujn0d<2:WafYz&Y 4_tN*[9(ăK3Y \o]:L֙ u m.G텞Y>an ^d*į",DB=%F=̉I%itG~s)7 j?]'ܭjCSgȢHzѝo˜>u #0>gTE>WX_T TaYڦi:Bv 7&W l #H9Ϛ7kEvL̤0-;w%+_Ket⢔DL'Cv掫; / iS9YʇgyкV'*s~:EMnFr#IuDDU=`u`σ&ǃJMbBr+t(F;9@o3<_`>dmҫGRa#olQuB} ٛo>*ðvI6,?J?  p f;|-֥ͮU<oYIm^8\muk1qxj@цn Zdk}Gy.y%ay{KK[ Y+drdr߂l#(5_ KJo_gZkV0`}/zZ89#|LU-IH8(T#S ;W3]hnV<>-k;+v)۬µ667NIߧħFU9l7[Ljٞ};2cs'󵤘(UC}<@)օ0xx,vLI."< g3h]ED^&Uc|ET#QnT "5;/@S0*iYY)zr|(OԀz.b6aeeXukm{PC|~^4q}%Sz{^i^g~pC܂U˛۠Ð|%_A,Le0Ri gkuNnn[UX^el(bJ`i%LpⷴK*D+Jo+BZa>2{%ʇ۶Cs{hTX5 |"L_ʈ dp%8*w‚)>"B&7ܓh;]2ݜ'kE62~aܪ*RIУq! ?T(tԂ]An-k~➗ )@~l ԳBI`&sĀ4AQg܀ڡZeht xry V9q%_9魞m~s"COs^ GABZI?tfrNFjVldAym  ȵ&߿ȑovgD!R;4.;=gCҲtŒ8!6XLƟۼB4`%i \)\Cw½['l$7R:8#AKI(_ ۫|. _E9چ >M^&qsDqE2z 2N (]stvT%NNYUnR7OH|J(y_3d%;n򈹡S ~xX!gyXOŋ"Q?FcWTɍކ+?0dsf'@huJ' &a,s:I$.k}:ޫvE=l]/z9ANCbydŵ6Y萜 )HQ3^͢Jr/^/SrJT4rY%E81%N.Y*>6nGRk@ITs>yC4]2l_m {/ztӯR@[I/aծnKhYRF{Z,%_>sq6PqUJe1 n}ص|,jj(#*:R9e=V %JH`ݸ@ɛth1yeVm<}^W-#(B%u7b=^`'e +)Mr&%oRa/XT :@ ~"ˬmËW~܍O]"4UQ-]Z,9w!g'rCi [Fb |_{5޻:hF gd F`V~֨C-&iKRJySQBO .U2U}|  Y>7AT! 3~xӽΉ"QJ8^g SkrDth]Lޮp849^K^w(@UNH+x</鰄Թf=y3WKV"7 Wygl[B,|kQ::\|YG#:tیk92~rx(<6h]m X$:|sR-}}3[ `+SHbXKkNL|]Dk/d\Џ-Z]kd(6X32lwXbrȆaّ .;IX"t{>`e-;!FW9C[ԁ,g6!aV$:]O4wm3-Vz|RNbжU,vi_D'4?`cqjhΒ_"ʧ9**{ O+ϸs:vK&C/r+xdW난C$g箅r]DNߠ2r-ߊ#L5HtRLK,bZ 9EAUe5]"5Y.>nvX%,}ew:\URoۤǰ\RX,RZjTԣh:. [Je(@zӇ*mNl 6S8ohXm4$Al}yn}6hӯJ}moմ?Nq7#9`rJu}RXuŸ&WarNʛl͢3"$1[ֲc{|T;0OK6 d 8n& JPy =Q:Qo5rDl,09ÝB-Q/b@)>zq9f f[& rS$ElхV-xG@2,C#))1Y:_`-XED ⅼ[D߈o{C 5:/p 0U_s22O&bh1Ui-&]$[P%?[OOD!l7sdoj͈2PC!bTɨP5ң$o Thz9>M.w3H -} 3iiF~G b}D㛷R1L8$}x4 4[ ;QpXT5)*EH:ބ):"FRstbSZZrjC 3:v>26$7؋|L#7hf G{=8w56+yy9x9cvO&' C"}, yflqOY|BE;ôҪi觟M.z`˓~Nג(Q]w`~hTboSg'Ri2XdѧOˎz<='24A-Q2s<YJ`v%w%q/6ZBc'.GqފM6ى(?!2÷H1*2M aG 0Y]a>ʑ8qnB{~&(Õ!骊|=GGw }mr~?+[,Q lnM~~sTt%< zWx.lVTb\sI8ֽ"w=~W陾1_g_[*x ;+ 5Qn-%ωZsjտW %g#½jCov8eKcIy T?4?%]$V-:lYKiT,?*dXꭶ ͛~p6ܤ)Ao& ʠx f}V8^8Z]N-VhAF-P5g-2Hr?B'g0_Uՠ_GK: '  DŽO_zG9QZhFԭ5@v)PH~FWf=åF [{:V7L%|:@}9O}r )!<.\}Gi ފsb3^Ϫ}cd&;$qI9Cwe I }Nw/t^PŨSdxHғ/yIiQdx8q̿WUr [Tj7?p'кi,K_uzDs8|JZ4mDT2Jt qI&/+o tvIJ G8Ž@Gm/P^c]zmSBfRB WUH2Ƨ37gӂCRىٔO̢zNmQ!uI2܏)UɸNHQYZ/a2oŒ~(p9aD/d+}S#q"3밿eVxgXE#/W- 괊7TOA?^ZYiI EWġt3ꎬq-i۞|[b1~͑)>'qcrA8ZA"I^ͬWGЫj5s Rm MHZg?%oMt,0h Iv>YX5+$m*@Z^gMrB %v*P+v} a꒺ iV}K#CɤaxBM{o5u觾 c(}lzY7&5{a %ぞ1FY2 1\7xfgR,Pbs YT6ЦJ ds!<۪T ޖz[f\Q+(CO9QX FȭzxNd vKXf^cD Y _K2脣̨{먾팂r>@\z2X.\ Cۣ鉰]( ]bfjeYglm С$E? :;V !?yA61R6,?%$.K l;pKx0nQ W(?4!DIKd^X gǴ^lV6xVYJumY4Ą2`3eDz3Aұ; gP=;eH=!^:x Bꦨ0Ǩʝi*_eY7 P<]kebzI9. Бg5ڣSQ7dB>rE͆ZDWqZ2ukfoJ,&c`<ä†PYimrL^2Vq|NO bٯГ:3VkhuY8ĴT/穡M m'(T D-_?R ;!n>$F?]5l 7Pe;#6~>mqb:GY*ѷ(e ؜a>ْۮʣ2NRBEYSA|JBwy*έ7K{sY|3y z0z  Ȇmdx=WU)ak B܌Ddv,A%CTdۡη$`*gd~Lq@?Wy/NҳLuJd\}1zv%P7{ LDy~9u hz2p7r_ ͳoԵ'R?XTYB%pHq!}{Lr3x< }RI~1ϖ8\Fn0~iDM .uy֔l⎶ oenpUT_`ЄL ]U}K_#uH7;dMpPupű'\Өnvg%mp m6H^o ׹E?bX$(w*{Vk P=ʨλSd: ʮ#9zXiXBЕ㭝CMY#W? )Cbǰא/IT+pOdYY>f5oG%M# گ=WXBcU1ȀIb]%1Q+@!w~Bzh׵^# n i E|/ʑ9_qn6]R77F4D3Җ͖H}8v'{}aVrܨ%Υঢ়:$:/oU)hps@r&#g2Z a]*J_Jy۹'j#IJ S"^E˰ޮ֋]΄UCxWI #$Ibg< ˷f.xpuἾ@]5ae>-vʻ_{41>h,|̨r*(R aC%z@2GAF{a h퐤sQ/'&Je򴲉X1%yKpςd9Z{,6K!2 ^üI)Z̔w&!A3 5u GO%?|l !y0$?H@^|E5qмK?9i!qĭ=z3» H$_[o}f3 I="Pdj"zd˩ vђl}NXE36$s.5|)aGhpy7,t @<r>>Q֍-)~yj޷m,aLѕG#+|gPkf)}V;/dy` +J Ŀv}c=~a-!6_(4<~U\hE?G;H# NEsΚ]&̊a2ΰ FQm&䷱d:#:^I0Jm],R>/gJ^9~b9MH[9>)qC藬:73F/!ivjU;r\ _ >:oϤOȍ5Ĉ %њ_$oY co{hY/N2HR;G½fṅd?muleamΜ7)@,oյ2LxtѼ0L c trcB+7 0T2Ou5'k38"oq;@߳D#"ӫm , ٖE}(;̄Gū)j;ZP8\.q6VvU#] ~ԥ S3kt+bMvg( r[$7χc/wV<~D]j,-_.nxL>=M'\Q6kU3G˨ Ffs5ƵP8|}{##CpTyl 7oYŮiȗ$:dti_M3a4=2GHfgHҋ [ 36;nW7޸]>TguWs)kzy2`Oq,zqvz9Xq],zqooýM.N"z|c'#'zݿݱVawR*ЀȯBzDP,|z2VX>j {N**ݯ3|t,xܥ\45S`ynɐH%r@ ( b zIYpb1;!]!asG x)Ot.N n+FؘlXb\T\`;9'Ş(,JD/ 6D+yape ȲIqC/杆[Eջ +E8x0Hj0+۬F2<+RHl }?Ìv阍XHD14ނ8^:`r+{+4o]b'͜ Yn`{=ڮ{X¢6Ovzêj VqL+˙DQ1[Y.,XCe'J5F$}8U'G5ڒ1hӕe\Q׳ΫO>3Nujnz \$a"HzB?Q:JbJ3!QB$#@Bu=sAKp 9f~8TG7ؑ(Bh i İ:;TV;M7}4%m # u96 ”6Ki&owCF DEC4WJ%V֊4{s#G1jI29}`l8uU .!{ҐQLG؛,z 9+lp( "p&Z[f\`äX=!w -ˍq|d%%^\bXA4wNgϩir2$n7\ >J4et~=(!ރh@ɚ盳  |^8@S}s]/#ߓo"-{n&y9Mm4}#05O'܍×n4w4Rv-+n1:#ыژUV`b%s̈́Lf&!Ŭ2kmIqҙ:=찯 x@eoBk6[̴=(}M-@rG>3끓R0Qb$hހJCCԬotԱkGIkhH Ey. r;h"b3⛒(ZDx Qrټ̱EhP`͵fYEkՂoEa4FßGFmR-eNA/sMI`sssP_o&5Y ܬNM?GP\N˞͹ЉM$jAMa-"|/l:y f2.>J6Aka͠ȥ!8Q08Y rĔ^ExSyjoE>I\NKģU| :*-=^CRJސr/!j|ފHr1MkQGl~6X Urceاdݘ|oj ڜgoh%\GJ] وĝ4O.Qnra v; Vk)E'RdX5'b)nI5!>Mi#8l>l"}wA;ɫO)ƒJ}feN:7oս .b:CK~'^/5{_Ō2%x8 %VlEצЈފ*_o;O:I%vC |(5/I6єdb%%6T<[jXNDZ'x%)k{ +0x{!*i<6p`s!'{}K?%a-xw9&}I"}Osٻ*z.lLX苛cO\5{F)CY !.sO7?-Ϙi喕 Y-VPfW=:„ ?s!&jYA ;g8 d mѶnQdm?MD!Ew:.A#CJBm`[z:O$C<~͖}h!M!v+I AN2=Hfn?[1B'2Ea'*FϚe WQ_V`_NĐ]%5j;T)pI#ow0N^ܕLQa)4 ;?%(t2ix֩^73sQDBrn5|NXDkf#ٷ Zg#>$KEH(4ꅯ2tY]GNh㇡IlM8*=9%a JMbMczx_Eä7:Qt,p; %\{^Xk8Zs6Fs4ԏ4\6¥`qNT\)@Z% ̝v <~pݗF1+tظTa{)eСd}:Z֋;(A%$5Ѫӟ KӧKquO-\3Hbp|9`7uG;(oD#Ҡ66,t[E߂ea&'d\[)`f+sg]~u:0 T'ԩ[FF38?=zIu, :m䩅OB.Xa^c/?-0s 8'%vm~OŒR9F%*,{MmO4?h\Gh|5`ݱR䴕]tgS6j׀Һ* !Qv4sd*>Lr7J=->5wT.-uP (I ]2'jP9:ǴUyEھҴ @$na'.;t]zPF 3'Bz `2;ہ귱eO9Ґ$on{Q̪"|BCzme!ɡqX5ղ0,?ô xs3k df8H3!Y9X&ʴRJX,wr'4$Ɵ#41l)p r"4Hx5'}:] gJ(!Srg4E!Ρ۶ v-j&Wh1?'Q҃7 J 2w&[푏iv== LmJu]ي'+߼Dbaզ.kmaiioP5fmW8VOێEG)"lZg3Ve::G'^VCsr0y8Ť\."v[X=VL 'hs-0FrJCWff #T6!62{eTv*# 8z8YDM,WYepM7x.ޣɇ[!BA(ICK @ϐ; w]k`0,<s;\ w3^7-!x5Ed?4?g|uV5Ċ ().ۘk] :h6eh1G #MfhKaB N<CUեIO`7uc\RiX4fr+5"xMϚ6P KzMZr[<Ԙfg(Kj=~>iLCƌ%?$fJtV0uyuJ~  [ltq FzDN%h{Pu|pISHV37քM&g< 6Vq ֶ8xÍW"G|P,At$ @hʋ_J ͈~}p!SxNiMZ: ?`;C T(hʩT2SF*3,=>תzB7kŏ#*I7h[O#zZWw<˱1}wu"wz1Eʉan]bv&i|7p נȶ)(Qw/\sB|K/%{+ڻ2IWǰ 8p tQlٟjOײ_dw˥vdaUבunt#1fTS:#+%2P(V|"QoP5$\j9GNytwB9Y C?fzi՛6 Hvdi{Cez\-V=PHE&Xmt|"M7:h Nbv_s+MR\Q]n񀌶R)BN)˵߅knz ֏0[%-b[Hpi>];fOիweON3?soV@#UCQV /o;\O&E¦3#h$f(XT|< ^v=q Z`$u+\a;8磶l1t3TO4X3Y۰Z&VIPQwMČŽ03k~Pw{'qhW4՗"HPrQwUg֠fKrt YBw2扐Te4p^wd, S'n  ;`X@/[cR9ə3Ax{+a K`m 2X-g--F6Z+B <825xIމ5IŝWtbFUo2YQIew9\Wy;HrXX@ާ ^S\X k}bKoPy;A‚7j;pe@b0)0R꼜ՕNn0&oPՆ8nB>@,#[{rB!~]6۶:TUJ Q”MĖ-0KO 1yrHml>nIٲ It2 @aS50KCW3TBpQ(ߓɛYZJ2q<-rhe~N1 'xafu2/;6ş5l}` /8`o~r>i-PƇW:'~z.m˽DB0P]vŽ&م/p.0t ?J9Lx/DHh ƠLb\3y5?G-L&hfV13x+"lV$ Fζ1ȕs~,lJ:hA&4#D E3Y1xi%z:PAR vm~1l'GuDž뜤3&K{zxUq޳["+"RњrT:ݬӴ.KS,}#iv-4 PZX*s4AlfOm% af+/_狆v A}1Mn&]nFﱊ蜌ne$2w&(`ZUrv|'+ %"B#SO#2[VABPZڤbhpyoun`V1p.hйV.5:N/i'M. DM۳`} r-1l6Q7I@r]hB"F Ʃ^_d#H! JȳƁ.CG<G!bKDs J5<{JlT8 OG,lؿd5^Q.[XCP7{> o͆:bqARDI Q 豤siF3#J#%:o'Y&8Y+?~5%/-Q7S&S(|:v?AC7=j5ܑk7Y>o;|i@1Q"q-#"<"̍egNfIf@cͯ,@vjs9H 1C|;bvˈlUd|}#4|G+g4!bbd >板eYr/N:Y7sUhă9"OLbSz}p\#A {~x]1ߜNKؔãrSZhgգzNk` [>9۲7m{G;mzS(N"j*%`W|wp\@D>l=(V+fd wxza{dܱ?=q0 jW1]OhfYGJXnw[8 _eAډ˃=jvf19PjBabhfx)00 Br24BğU!%"mY攓0Qmͮdp+oڨ̓J1T0Y-a}ir+wkf i|R.`5obGx+.'VK݅p6MIό5 kŽ#ѝYg,ȕB#D<9URh"c^OuC cQ,GT# g3֞Dž`"zʯi\)'9[ +iÁ@b8: CTsz1osrvg>Tܣ*tYx s }C:| bp%[׉&PC=D~13 EISq߮> ѺXdo9eN!FIfQgnb1riAж,o# eZ'fGewfy66#Ti1dr76(d+m1"v:,M8pꠚ8R|J١^OEY-ؕ@ lxRwH߼r {rs Ki)vs؅6XEZ !yK8.ss:۳:Y2cgy4nzC7 gI_F7a┣UJE;z0Ŵ,#+}kgKİd/K!:d|S[D0kʳ7=ÖgWἫsm6@:}OpTz/jksvq܋{o}cҷs\hsO|=iqy<|?p2#(g|ϟ OJ> OC:J L̝w@su%\/2 WF9v[!⢱齡D$ȭCY<ɨqVbij΢CK O9Bf3ztc7}eQp]j]RIch ;3!F/Hk7mq0@0%->'QzP7kRFnZfF\?S nwl?H/l6}DKhzyMJd1XőG﯀N>h? H \P•"TdtA'NCXBZKRqi."jS`W{z0'΀bX5[|䫭u~. F8.gL<\1Ub;&: ,fLn\{ps&O>lAJAgQE^rÀ͇.6Ge{!(3T0r۝fzؕUyUP9i VEZ G_Y8T:~xd K8`a\gS\WjoeG\͐[k( wL-JWߞѺm#CwmBp:쥌 Rr0G987‡K-ON僄YtÛՒ锝D>(-ORf3!6\\+㾛6Ÿ y["_S k-mbnHf|WS5A8E@ =FoUF3$H,n:TD-< D2 qTKS%#(5/ub sn T'coN&NַLg Ղ:㉔[qny[h#49_Yٕi,I1G][@+W*VTQ;SKy̲2L 5MK7WurkD{T|0 dsh=xSBtZZ$h,)SDݭ x5SʼntJ͈NH2@=c\<\wiL^ ,^gMvNQ{٣Ic:4= ͈p4SYDc۷=_ӹEy-=y(e"n!z;h[{;kCplĠj{T#ڕrRԆE>1yrK+ Y"pe޸+ ]*,uީs8. !3qh ,T:j_:m nG|Buib TY> \cJK5Rr.wV{S,gNbA_a.tُ1ڎ:ŞѿIԻ/ԏ폩FMEٌ|HaUʟ]D9ZjF@?UܶͰK܀ *q.FBt c(p=vE* |BȴxX2ڪNscɷx)#i =><9(MnB\e}tA %SpAP>܌Rbm%Ant$ǔjw}ܑ-װȕja醝&^z-+o0>I+5"qGԣd`*{fE -/Beo+@G~$Wp:*%oƙooD Dz\437@6Yx;f!'xzA8YC5ե4V58!헟!0"m~$ۅ\%QT>u g"~@%pL 2Q"Az+h]ay|kDrfR<]M~2LfU#gs2B9C]~?Q6j(}j$,hweRbԀRhun hYܰ+9n%i']N1$G2-'jԤz{I=ĄԕͽP@©8+掊"gKJ|xѳZGk3EYi˔+M"Bs}f@^|Ѻ_c=;8nuD{wʲIBIrloe o 09v[a؃ LS "tp$`'m.@74)v{46!}U̍}_q֤c\oV0@،t#n(bf:xR3N&W|]1R k6ne$$YsACYH )GW"jsu$s^"BB {b(,- ߗ /"^RA@QD^vlra: kk8Cy\hI.LXXK Dزxh6bj'RAkQ }R3cr>q ˝1 |? 'ғlelA]]ZU@MRH3"mnun{'UR~4rWo?yvIʜغ(/c 2c , XkKu,c*y3kXhdc7R"SuCKh-ɼt!K Wtr:$祂 iQFGYd$|זR1)ko (w=Aژqpc@K(+#'π"zl]5D6NJ38'0#L?z\QOH #@)klєHًT ԳuQYb#V] ]rCK c9S: uW~c a އ}NRseڄsyu.₁R@YAj:~3%_/ ⤕N]qЅKӚ S WCowd}r.䞳%}`,\C1ٍ-"v Zrb޵)a }\MLWoK wv84} BW.$H^vC ".nÙ#!6UF%gÔׁ'&9g[CP;Xߝjb&XSUi%x#Gc5yPAkUl/ SEm_'Y/x+W?ԗs)r~Hh!EߢJ3SXO>,fXKi\%eP'gpu[4c2 Ȱs0M_Z)ۀ fb16U~@o&kxSMP*6KA0x \vb'kE\)!7Qkpjbgyz=P,!8D*(?B)uxqPGyDl X*ܲWV;7F,$WBM- 9lAxǠZb8mǬx0K0DhmBwzJ# + Pp5w`|Ԭ0<;HôlI'G+홁9@b7Z-265LOs^ȈH l2Z{V>pEb{œ$P)SSy(?Nz!TP;`+). j $GпaKna^k6l4Tb[Y2 :`~<߃>^ʔNEd{=Nf [9) [*WhKVQ7)?S8 뚃kF|چTy(c~WP_eN^"-7h'Ȟ\L3`ΆX3Iwk6᪁~H΁ \9K}*J yه\"u!_}sA%aΖ) qs9QЊgH `e~ iHf~wʤuݼ(%_3:IPWtbN]LH.C3HE2Q8r`$C3A)ӶV(co;31TC` 98qtY2NyNc_ػ64Q.{8 .ъCAXR`eH&ֳNgikj@v2qFM>қ>5j .KP^)zM,KsRJ}i[H%=/rhk)?s d5.ʜs:$;E 7Ʀ^)Ԝ3p:M``MU9ġ{@A%ZIM\-P9 9>t~01iQ/ՖrĿU`ˡkQf?d7JձVŔ(G֍@,4qc 2qWZO[lᄑY)Qt(4{5kaYaoV,h;Jo~s9 E $j> @Ji /F EƤD%=Y>K1K K)8|Va?l {}GijamspfmT F)Ī* PK֮.޷KoV͈+r/zm`&q0x{ˤqQIN<7SNψcxM(oD}ʚ1|ȍ)?, l:٘u;G6XjM#}Đ=sLH&0Dg?fNp_/8mWc 7 7BVr7}pMсQ&vϨGVJiOpcl_Z*qc!gh EWIIx \߁\᠘if/ř1ecI C>,!Uҧ?ڳx@5[wahd(IhR=χT"kA+Y0⸩7gXg# 鶀J%?yr`\~Ӭoo>XM"V(` {gX$jDg<03P2yfh!ojհWZrTrՏ8mMD-<҉Y#͵Hv(6GxCĵcic*~RZmsOEܛGtDOGV]>@~+AuZ^4UbQg;AC$FP+ni*R׹vܿg5AB C-zZ%@ fFNgC]5Xw3WÄm&Hg5c>\v Xm?(Ә>$Fdscif>D+5%H0Dio{fDK!y WJnC-Ȱ $1fe{ic o9@?ݳ`G}GlMţ{uz{(q7'ՕXXiYm_^>S^Q?"O2y.oX./Y:ש Q?'h!nq䴏WkAAwFu*jRA4YM$?@'Cd΃ݕ %cI +>}9j0$M()~_&~G+9q#1jwm{!i+;{BZDžo1q:)~zÄE]vT۽_;Ka?cɤ"IW(i{l8T <'+Nm_\v^Y4tUOl̙dw ): j wؔxKq/j5"y4[V z Q4.KJ{1Th.< YӲ=)98dct :Y2@eM/ɰ*"'U˖i,Kh>!yaa-,G7,::n lȽ5{7r1u] w|*OaWa# &=P)5Ow3%*ZFpѩ>KwҟDg0HlgH'gA?gXGOh0hVҮIS\V0qp.dx.SD!h 4KgKr8g).ZA側ΙzD0lW:k1b"PPıFӱ.c.@)%e, `'vN \+t1u> u8Տ3`QB;6Ah[4@uylBpbH`_ m 1jAn*+ybQCfm2M#/VQ$>E!lO RV?Rh6p:Aj 8?ۮ8T@S<.J'}; p3w;Hq6nO-{1>sFc=þ<%։6%1yem+ S@9mdV\@eß2 Č*dv Zɶ,`~V:63c?^p< y(eozVv{}²'8$+dLsHsrj+LwͼRlm]AS>Ver5xw50P].ѮՕ8X)i CL8! `X\JdxhR2i S>}Dy F r\E0v¾/&^\|]7|0Akyp]%љi@ߟ~0d!^: o.\nY/d>|79:V7oP[hD aȱ"3ov֮A3Ve27b~F2 "i~W\!c0=d\ԓ7U/ 8?Qc\)+[bժZsՋ4̷5+E e&:X&)q9 `; uk*7a#_:ɨ7TH>6n{YSP9*4%Nf1uYA[ꆄ`lMiv]}.Br<"nZZddgɦe:ą"-5OE KDWi3BET656Ѐ;,dhྱـ.Ls0 k&x 8t8 H4NVCvu\zQ&QM^}pa⸍y RXhTbX|3^^V1JSÀ6Bg{JeƊNߨˍLǞ<-Gk^!P] ngņ @R,yA P?3_wf!q׺tNw2/< *LnDQ+d1 .1i L#0jC䝫nU"4> B1 +[) ߿IV PDJNY5+b?GD15.{xDhFC U#b:,ͺ(h:,4zL~!MI[ч߯76@;If8q[fa_T8lLQ\j\ Чvmn^^wTH!-Q6h9x$mޤ?ayS ;{vex\dr8X=b-pdTVh/艍~ HK^5\<ѵ;.-94t^ ?vRɂxFgbkq±\ \WZ?/ؘeLồy.lm9&QG->(v@ S*"S 4."u2 F q_Yj===u/z"$\4ȼ-ZI1[qEֲvCarQ*sc'RNՌ񡎼5vCkRO1@f)DMwAt%G~>4 /t}0cw/RH&|Lb}R %vC7\/>BkCZ6G̖K:?)$ 9q ] *B-1yސƣ{Ipbm)=~M435 $1*n''\]o"ܬk*MmoAwB䒮Sajhfu1>UtI_.8I/9;'f:>"a@oY0&޿ߍ4z˿xQVm'' JBQ3p/Ueܚp\5ά?P+S3)['75Y)b~_+X\$E K2Ht+e"F ABNBYa9 s6!s\ ;0DbV0etl/gHiΘBļR_-Hs#8 F" F>s$^/;kzhnNt51g qLZzN TU)Xtg,oDo:){~ cgn61!2q1=fb;UB]_'&qZ0D w46 KT٨#u'ճ#˜ F 5Q8[ ?-ȹoNªqv`Z#.H7%?;m HwǓ .ŪZxkv;z*%s.}GQcڭ& Q߰'z@nmBLJtUnt!Y!GS <248d^.;dH Ʒ=__l :̷&žf?\xgŸ:`k /e?4OlW'M 1M*ܥk?Z񧄨9mE|\cKR_>X"'k +a1!ʂ!> stream Fug<<ٴ g-nx&mgdj꧁q 7UV-@%t J |l4Bk{mm$GP۸f rc9 lYf֒ӆuay>Ƶ5/d5P()7Ԛ\J=8B1qg?fPqHf5!"5+9hkK[MA/cv"V2 [ɒ87BܷZK$.auV@e~1w\3͒$HuNJxʝ xxO0voky8HնrCx$H&r9q 32/Kh! 7RCd a)T @ZOvuH:MZk aBVPn~m;$Ǻu\LWⷥ5Ej)W2jnZ@GG 7 EA>Z,aŌDJu_^74^|LC2e^r vdfV|+P.c_dlz] sOQB'JbA@vV xT 8BFS !!*ǷvKl;]1L| ?YO`4~B5oF5|& m}6T6ϖ 5J۫TiIRgC"g%Iop -aۙ#M^eNsp :Y8ZHUd4+kG1hmX2T(,4!ݽ](^ ewTf_}YV݋w:zxJԎ.B?$o3T00kK0kD!h53lS2$YnkC!<>Gzpq,\ X f1@2C2"a[Aulʔnai߽4°i{Q%̉`qSE5XTDv_@8'L!\u?u*pCp}[ .~v?|&~礏(_}xVsD"y_LըChUC>'Z׌~kq#߭ԺOyNO$\ffͨw> endobj 81 0 obj << /Name /Im10 /Type /XObject /Length 82 0 R /Filter /FlateDecode /Subtype /Image /Width 3000 /Height 3000 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream NgAaUz iݽ a3 ^Q#R[H)zHub3?ao+k(5F~Ӆ"ӕƺE5oٮ4<:C~B/?;_ [,WlN8IFu\ZHc!Q3W*?/KpHdDk$e45Yn$ X+0*]Xi<1[]qMC'hr]%FăC$rUPnC@C,F34-Qz:~snb-ո8M2 Cx] +1񬇥FQ/:Vݤr=!e{ƩK TCGc9Oܗ2t!^qW.8~X+\ˮ]IX#L@wWk4R^ePC0rU!Y0BNȞMe@dL譙">7[y4ڜR c8&!w1Yobc~Fފ+:6ƸG-WT~ $,Ų]I , =xsG¢0L$J/,NvgRi0E=xbRSs#}ub "ɮ+C?dE[. Oܫ{2c"qpl/|5]T"cƦXICx y|ef ZYmY[w+ַ71lj%R'S=<wNEmN7y|3%3# @V<[/pF m3(%oz {tʯbzqxqҽ 6iTrom E2H ژ.5iY^4g؆[\uCY10Y\짿yR0%"UH2mDSEЇu|2-8VըBa3W W4{ <#Kc&.nbҞy? DfC>s.3yWڳB)nd05d_9S,:b#..&uVmm%2ysȳƍNʸ YSRv6JCq 5U ~@A+q4yX$ "f@ ~A`[pKQFXo+K AAeeI;FYςsԤY@GD)qVt _B'"P/FiH瀟AuH7j_U*L&oyddL:|wf`Hg{US\>-L5~$z Y2/2v"~͞]˻2PAI[ N3`~BBttpO=0ӝX78tГy3Am;dYNLNJ#![f{ uͩ<46qܳJ53z`c gd("_Y7dnJ!.7MX&~CKXπ5x&}ذ$854 lg'tDD-ZJa l~QcwuIe:s@̱;j!e* .FXL i;@ dRiFo;Gjvqg#4r6ڹ7 nvS*AbM soww1#Qx?31^rjJP< yEUzoL;j)4EULFr'܏5@҈fp@Q/U1{l#*㨓%J#o1$ܔM O H^"tO< ً`V +x!(2Nm{fߟ}vB/bxweX6I\$65I:hP{ڪd!!&ET'WȖ7wLscs +\]ѳy1NH$`LrunEǔcr }3\*l|*ЊyĊP%ߓ#|@\r<.)+Nl'l݃rW:Yz?YnJB\S"4x5tY_c͙UID!9 / S#v reCX2alF]u~:uVzgJ0ۋҕ5pp{u/NLN/1W\5DΈ/DD-0SJdprr7E|<. ,hoF P s21UL|NYYeJ{oG` e\8G4BZwV3y?z?[o)Aӿ1cLe1\TjA!%%eeȺoZ] `v\lX6h*2AxgGT}>̥b"dBƔ? JҀ_l_cڂLMuYn>o^Qe3 7aM(z§իN6 W"%0kW=S~{9P.s tJtdI(~г[: $5[sߓB$Q{e"iC EUα1㻭E{aխ #{oQTUTV E6?`.*Z,=*u5i+17^ngjnk{1BE0:F,@G6uS`"þĿ`pW]3誫b*bE:tr_%ϝ\1FNǭfw@z?-h3n6 )=cAvf,^g?v<Ƥ'n-S'>fj_U\?.p"RL1\daߵZޠ~ƭ 1~TVB,Z֨ac rc|06f _/dՒ>c= qd0Z°`]'=;+B/˟&А<\M ?1nh._zB %N Lk\2LۚXzzI3Rum*,UF&|N\TpVf'&ٌnn X, \9-vFL8dLkqjw2w`7h6sY1GHhr-&햀q}%^Nr~y(߶-`4⻳RZ{W1ta,k|iNukY鮹r>JJݶL*E )/mS\1jSK*lW>t>id"]`0!e`M. ow@ףG,Z^d3ҧތFXzS6p@V ϥ^2`p\қ` 4os7ۜȶ$ .E.ޛ:h> ^bud\ci4]Fc?0bzieoporw3+H"ԫ#Ubɢ(k8s0iG -5}K;LJA3{Za:M;)hLM3bUߡ~%ygOm OSYJM[B nYH},&pB!'PۊqP3.!n[4\^< ʱ\ S`F5| xTC:+|]_dGmlf{׃CDTouvJeS͠k |7%\,G3sy,s UQ-Km{S+')F^*Gξ>`W̫M[ey!w]~(R͓!.jQFQ)~{^ܕ Kq=sBF[lȱ"R9;( u =ҶѢ/z Ua_ AȷvWR`x,G 1c#+TaFL)T_4S \%;OU+%f/H u<@5н /ꖰlS +ܳN*k\/G=2Qss E|o2 t"h]p.|0, ;YK3)Z'1&Р@`%mB/D)7*zd' ކ&$fd&~{:PD˨x-% VߪcM*?Xa)~e"&ˏ#5cv>RSBX;'7 S:_VOaIdjokԈQd[*(c FHb7WahG+{̦bȁ[<0-.|[8n2&ڋ'&Դ! vwo/ЎĔ [ef\uʹ.JJ^JBs6 ӕڻո2WZZOV= h~,cdQB8['Y\^;Tb $*s`X?lg8DE>qj{T+7e>g6f $ G؟čGЅFaF\܊["C50\x`hx% Z]2޿mLog\n5 In[JTs$;[ LŁ/Ùfag$=>!2 ?\'ZcԀgm -v+u;S@'=yy\QeÓǾЬ{mSL8Ԓ Z3NL!0HxEO$]6M R"RCK(i`u-Y7=xWύNJ)eʝ31qe 0i);ھ)gA3J%~!%8L㰰ySy4rFVl`mH/3gi4϶¨OOCgnJI~.Ɗe~f԰R3F0jsSC%RV+S K[Ny21ef`m:W"`FȅChDּ(lءI A%&] 'QޚiPp"&7l=1fiH  ҙ@[V}+T73e^=^Fn%k\vw<&@A1q$ˁj4uE34s{6bQ@$kU?t|ӊrj\iLb9«.wH>LzP_&A GpM5=MĴֲVA9E\_L9B䥿ٓ%{:db" Y̋,&65!Xwvԟj UZ7kƕ0KWLt<_]c|k7!R GCs#1s0$g< h3?k"TMz1EN,U }Zg %RqWk">z8@6}N!D_kb{ejB%y[J7ԉ10+Zsm+b i0elaݚэ͵K 9CAK}juTNVf{› lwslZ7u!bbV.ص-Iٹ9a8+2&u4xG=~M:z*]kYk:|j͝HokKl Gd(A\Uo^-QX7aN9 2DZ]).S'>QX";Ȋ=j_K0>dP QeBg}=G(` /D(WlxTu&&UNKnNCp jJꐘC;LkםӰfuФ;:~Gi5i] le*R %JDr€?<61|˥_t;VdDxf;a>ndξso210U^1@QJ^aQ1-kϝ0G ~gH\c/MӖfՖEv:|3q1оf5c>@ػ\xA:9{qw:y^5K ~cb)\ЂنGZ[8[TJlm&Ju=)PІi$֑Skڤ*_9C-{36VN YTBfg1BI ^<Ӭ]CUVnxl-DFkNz^B 68)DB9{""6Z &D&L,f88Ʀuho}Mϛ>FcKGb/8中)e-?4",/y_zn;J&ѱW6X5I$Mpd{ ɪ*b2=y˫#Mh%~R$/|ӣ&Dc+wԃ,K'DV-7=? 0絏#W3P>F -qD3[6lO=Gp̰O)kup!z {N=J|+%SH*v Mbګ&.A*mPǢܵSV >`Ͷ)a_G]|@4ˋsSkFXzu @ 3t|`}o ɍ\F q4"2!֮5\B,5djێ.t =]TD=ό`C0"KLL)ץؽuR !XT@E U67pXݴceI$UT֯mjBup?օcQEK!WO׊ݔ>ڼUb!+!fVP5Njjn '-7 #kHWDs&P؉Yd;E!ޗK5B530|(t㘦3vN9OUmZ"Y`̨`Ε5b3T=eC̶=\ZĠc?W3k w-fTԷúI/*jW^(IK/4L r2ھm龍+((;.t/[]=_؟ܬIB5mYj,o85jPzL) f5:iYWֹ|(ei摰rǽƵK6y+Jj?4lT}iP:="ZIڌV :%RR`v4s &n\ePvN㫹J4A4r~￟]_ƠcGJx?o@TI? (Fnasgs< 'i*T 62"[h+i~ ͚Cq`( љ鴉kF`K2Y"E3˗1B,j}><2tH׿C4P͖IO,7i}{\J FDV܃G,o#a#10 8/[޴e=5:Y(T.̾DވH8:Dap YwPq']? _o) .lրLWl+at1, hOtRNzzdt/(/?̌Ge&~AΩ#v; ]{RoMZS$D&aKR0 ;vK)SWcPI1fۦ~Ě"ykec8cp*EJ5m䁶֞d}}P x ŤOB:s2$z-2QE8To^E9 cVB˹VCT;g,Iʲ9,Rf>1[%X]$ȗW-xUS"!'sGҏQ?zcIs$p6**B~ xdރo˙pf7Yޖ6;]xg05/HtՈWy_o2!.V5U2+'`TA*AHGN$PYZPSo" >ͷ˫WI重2W$OOS΄텨2p%; 'ƒҺ%1`6*cs6x+yp$>DVu@Rxki[u^T3HES.L 5t⩇dDS6r mܶ@HRFumv:jD}Yb{c҆Rv֊n(U7' SnNE o ur aNt<(͸i&E>J\le*`$30j vz8~bI[yo/<[/$5:Mȷ|g]w^ @;%,eA 7;C'g4hA 7dal߰P1 $2 ~Û؇cG0Ixz !ʓ(ཨKPP-k"] $LPq8R<Ė]wG697 =X%8=Ea 0Xyb%pKֈ8nf|oY֋,oceZ[@̓DXR;:r_@!Oxn]ݼ#}{u[XI#+ 磹uN'~aS]V){N| v :rEQ|@mRn>Je0ΩljX}\ZW0AuM.5^«zp:Qy`G2)Q՞Mbbc+4v6 3ygE 'Zۋ8z$1D ԛ1*|ٚ,IQ҄# Rl\r~ou$%-:`Uiq(gU (OqJƣSKqn$퍘ZtTEN,ntel݄V`#MiaCh+n}˜ q|y'Qߋ%vgv3 zH]DDp4w,$բ-wSX[ΫB~ǘ͘-';)9gkS>#B܎譺FEgGN>rǻV(8NGѩ9o &Y6E6ѽKZ&3!8šmDqlCÆd9Hz>L`6E7 `Ccac0=SZ%3_.dbb3Fqf@b>/E[lfWAb;n'F=F i_z=Zrȱ½F:/wʋ#hPnjC 9/&3o)A`!/ B}]14!^=$s*~%~?%v.,2@ҡ'H1gT<FiPB{4\o+9X},[WYZ-f)aRʧ]vnQS52+qqbY8@4U?=5 yvŬѩ2lF6+yugRYaDǝåT}u~H2+,6`*E ?xsh6~03 O"7z(#/6B:VL c|熦]GhCQѝAJ%8MsfNE-& TjRKߪ?P/X?fd܁x\>W^nՐr9'"Q%6^CTt!mWӯ.V#p"xoVIfUUQC;Y`*`2Rj <*Y(1ʳ8UE @ X $ 'd e$oK{pX :7r װUTM\N NW`:w~ \dIc(1)XuW WjH!& ;PnLqZo@\02 ) :c.c{m6h~x%t&`qė~aׯ2D-# \W/9;jcRIÉLe4X3X%;ra3 5<[ŒQ;@'}*Px̕.1E~Ǐ5{0%W$f%F}qkPM>+4F"bT}~-vw =T/(edB0x *w5D1g"IQ\ b&nQdۣ/ Zj8u|ZEnP`$[#IUMKţ"= /2 >qL X|͔N=n#EeD246ך/.z3<2 s̠AY9c?L(m R- B "- uamms|#EV΋'J?Q3>G6ʯ;dM'zerk(y}K߰ ~>##%()k#F/4] HbϦG7|ǠlC q5yB-k&7Bey> Haᴹ ]I]L,xCb3PB@r9éwyi 6۞N~jD4eJZA`fcqTZ]1XjJ}+Elv$ x"(։{/'7D> įPZCs+\%IQ]V?cL]^3I5c&tHtlb&Ua֕_494c? Cgo-m28_QǢT#}͵B涋jH czw1f"a&zjž$JZ#&f HT,r4C# EƇHcL9cIᴕYwƙsS) x!_:ΗͩfwG,_Ex&b0D7(N婐8SUCPvqބvD$%{#+ùdz>FPL5 28SNxBW:&H{\0(4Wck$Ehu C߈' t7UV#*!٘կGdi+6kMZx.{]aNQ<:99#*ԥ7R>lIU-m)9" D*fün:C-oO0p$C"=o4@>ڕ:)ȴ? P_.ӵw+͋dNNvNfd?]kMǂ~/?ht]*@6Z'Xr K-Sm^Sf@)MIzEq'Bmc&Dwvh7 %7#zmniݑ |G|uF4쨄:ۀ'VѶFy l\1 w* Yfj;9 i44pPˡS;+UЯ6> < ޤOun/(6K{@k8즲\К?6LpWNΡɴv6[]18t1o:ix$PwjL6;oB ]<@vs Q W$v*# ϝk&Cs|{_SYLr1aW$22&Ce-=ѤsVU}ԴRBwW21kxw|5PW:PYxFZ7辝DZRۺqeZ-.JM遬,9uт,Y:iۆѥKU˳ʹMz Cx!Qԧdm1#Py&Ov'moȝ&KvI\}@mIaDv;蔰 Wd6 WWyiȌXiS pa2sZ]R>9 \Rԧq\(U,[D(6U6=/*yDWRIQoDY~k*ZPL{s;DBmM[[V)QJ<+i!"+@VF皓2zd\x ox夰~tRKWE2}JSXE>7l "WCZ ZW^CewNb\"j~}]wy*h!DL͖_"z:bd%>aMaoֵBT^ۘ]~$,e<'@x4!- wWo#@XQ_7aynzjj2ȗ1v%D=["GݔwLY0TOhS8tb(~U-hůx&*bÜo Fv0?'φUgwR VB~g4C ,G-`a]ϰW x;6# BR -Xe/׽>Qt[i@D|o%O\>ÿ| !ځ$EvEBNHxn:SV/=' (uH>ېn;<0k|oCf`k)R(͹@=h&U|r;k5LDJ ݮTJؽ)T*`yC"mtǎ_X u<&PZm-t;RqJ(\6_u;Aj.8@2ti%:w_&ז-0+4@-1 zW;d=; j\8 Et\V 9LfUn{SrN2:` G)ngAP r:E ҃#;oiBr { 鎍T}Kk=3mRSp>B/t`^ k~Ջ؉l6 Ua,2Xgr~JgDG][XA0dE8 )⦏Ë dX꡽PWдWbQjeELK3s`| DSlb5($kA i]x%M;ua T񠚭Y+`hkY~0< Kg_t_Mu$O=7'0at8 :1Ιty)>[IVW 3'/BܙUV 2)(yengQe?+w ]kՒRez۾-2}3SSj g M5(Vmo4 JHǣSew 5+o3t?5E!M,.bolk+lcqKCv̀C$aՋ'm֎T~"foG},,!y.,`1U^pVW"Ie!*:h)K.d~,D"%Թ[1ETɘ -}dIy9㍢ !V#Ch)z&@-sLp?u۬EBH1҃X FX0gK7 \R &;Qmwnf&z}IGSZE?@uIc ]a5'`J@7VaCJ*jOaE) {!BL\LVjvẀO$O0{"1jr{v۠KkpծxN-G2cG@}iV9z̞>\A2ܭ%G_g~3Xxy.u1=CtIUsP\ɮs2Kb5cՃ7eS34MDp-넞:lQ60ߴXuյtAϯgW ]L!sY)ɜQ0U`wI9d=4kQMRއK{> e b]knDy rg1yߤFϪf` LkZO%Dhph/ h!о^Lnj&1pF%{ J8:o@8jm >#ʐ]8x Gc;dD5>v֧?{)a#Y!=_p m 6=u]ևvtҼP1=/:Gܳg1}#&NkxP]uQ&+6rV)80S7\BMYV8nqu%svtQQ ˑwݯYZZ⯶oV?8-1)fzeGfyk%hC]4볯:8·E-VWfݵ(UGbMOSKXՊISXK:h& l!( r t9%q7#. lUHcm 'N9TVͰp"Mzu?{:L-kw+-tQ \GS$Ԋ .V[] kB 6_)AJ{e`NNo+ZhAEXfb(kE RخM}Z;ru4#κ[m;n+ k򹵋%FC'[fa1 빳IH8zV p(zRUtOکfg۞yG@uk }<Jcf_"i{a/C*)`|CES+UߋhCHߡAc>4mUbh/@w dTCʏ2C7bRVv#˙Ц;BDζt*y)^d[i"z[s%p}:Ul9_ 6ɳz%қ/ w٭ڀ+"MjB Gԥwb'%S\AV |7یAIׯySZdg!>&rnfo>fO* cNɎF9ôF>Q5~s7hة(;rڷm)'{`WOfsٓ& <$ݫfL#kq$mYCh7M7ou72c%ז.Vx<-0մg|voFY@ܶ&tPn}K&i*J:m\i^c5鍎 RLiq[p2NkdX9n=/h=e IL_TBg0$wXݡ9-_ p3;?ro; %?h"aؚτîh9/ƌK2,`nGXo*[{ ҁ!^Ƴ\uX2-63l%bwDͪU9e.Al`jIj.s{=ܟf si$R彟6jàWkM'YʶB?Xp[Jn}c [6$KQǼ>a쿦XvSF`w-P@ Q&P 2U/ ѾuSb`=Arpd@qJV2ći]eӡDб5Uݮ\VkU /\8Q,3Ti8dzܣW0س)̜8EdgP1AHo} XK*$[P䣲ߎ!_Z6KP]~ ݧF~$fޖi#Ƣ8}^ŕzTh6*-1.l—>̐Eur,ֿ0Lgxv@tBE`oI^H"Ig f=Nj;?̛Xw-Ow`_%kQz`axʎn?XF?SG;( cΏ3-&{Q3SIlD(w2E!:1{ۓ.ZR6qa 鼚| ;V?k|dRa=jPE{߭<E6w#(6AWc"KZ4D+}0~Lf*TTۃ6 }NB]P^# Wos]@OpS9޳ \jTU(R-‡XMX5XA 6k=&_=2႖2Ss|/ω}%A`kOkXɴ\yˀfm  &!mrp3o$M"֌q[Iu"OGV!V٭>){c9tL5;j1 S8[`glfMZ~{+PZÖD tY0A.R´Xz G&1>ήL:)؟ m7q{3eiNJ/lJ:=CfA4V= .ӟMF✁hVS[QA Lt:7 a4tȄn a::-ŃVma!NRVoINazԗuza.I%!(KzĻ *6@x/O$uٵfT|,!yOc޶hJ6T{4]|}cdm]":RS{BPZmu0 jmN.\u9"3Bqբ#eOr.H(5]c2S|"<'*8w 6b[YH(HJ80x;eڝe" '"Enՠe[DAA0G.8YE\2u9/kx{ Rfc̤ ۅ?pGAWf]{$QԅJ7G̛gDǵ}ITdx^ۡ,nŎIG' !܄*ZÖzʶ)*a ZZ{*0e:!ϮB )<Ƥ Gn◙/Bl`B jK??|zuoƭnteTdF lOlbD&1&"9ܕ+v˶726eb;imx!5i@$ S /6uQ&AG K0(VoX@%Iw>%5^ -#Y!dF;PT9R=VM 5#Zۻt߿60ymZqYr4!qǙfsv4ʜ>Rpp)3ZZYrD^V,A=v@&HP\H(TbeF(Zcv_N??<c'yLP]S3m 2~g<*`R;K0_%aGpBU7Ǽw'g0WGGO2S!AGo.үBgPS}r+&9TǛ9]t*flʍz %f] 4mos&TIm3 $WLP%Ixύ9Оr JqC¬J{w_;a,Gʹ`5m_>dhdfn~NAJ-^9jaƂscŊ^5=mr9A査v5-u<-Z)56Uv&CW0.۔kN'%≦f 35k.Ы+\HZ~sKC-;וIbD-.& c5!xK="8?ؼ)_K~w!bR\vk$40Ɩ9FRYu1;GB՝]]= H,T&$ȡwrU-=BqGrJw,հV0Cߜ? d_9zL/@ԧx؛lЂѳYL!/`A+zwHߜlYcs#Fëwy(^ =,C~f\XU\WlՆS+3YLBDEn!#ljb{mԅ3/r߇Tzj.z @%tmFPC ƁLfM-h)^E8%AaGېb|B/Q`49#|H ]z.&"ۏ?=붱6וKNU55;hvZ7;g| $٤j0MNL(>F˓O[~IJ7 XQ fi{?/v;z_F£-'N]ڳXrӚR>U:|'  4ӊԋɇz JiVS$I/](*oU^ lP$*]Dˉ}~@ c-䢚.h x$јv5zJL=JB~#nbnU (XT! }3[ק Em@Z_BT(chs2w\??*1$(>1o D5lڋOӒ~-e^ܕ5+m'?g~ 6C1Y\\>2^:OF˝\UW@0CP iTĻj(@HVKU4~Mqj!BjeԊZ-ɳgܖ/44Qِ047 2ZjloG'hKdbȸ"ct= x.ߢWa4S5& ,(i@94Zֺ,oM}qQ5B$Lm%DA 'Qup +qLC$\Q,_j_J=Y$#^_ؾ|ƈza RL'sXaoiw);V2;0NX },LCFrtL).r f;5N3GڨqLǽV1w"%W|:h7"KqxwȻ48/tZ +gz^$ sQPX99%jFyYt.1I0\B%D%oTگ{I &Zs(U+k.`t!TJ$QmY%sssݜS̃Ai]KK3+NL԰.^ .&OJ?gT^+=sM` 7{ڟ`9 F5m*q>OEvL|Dž{&z-Oef Ì]\ )KC/.s?7]۬ X}yv]=&)rt0Č">~.*uݒ^ԻA0z,D, ǕPPhF ڱ3sq`}R͞?"^.PƉs:{?UadV\o!6q4TӼ=!X1%$Gg8 98s1 X=$4_kVԯG1N;ѯLJy:I L8OK(˦IYM 'vc兙ڗhz!XDPө^K{ K`ڒfoPUiN1@dgd9s ET<CU:^Ip&{?&;C/#3GTI\{ys/Y:F(z1J\4j͛JoBxUz?;AntoeB kF 1T:SZcբp6t"gNQLzH~]Y~ ]+X{ ƲoCrk"cS]x7q:*,Pu2rqzzBNeTOʘ 8b&FEkZov3^ݲ'T@0+B:cCӛ2O xs.gmOJ0ou`8 o4 )Ay(RՊ_6e1przs.9:my۞d~]!3 /Ւޝ_ʸDޢim)HuWu>yoSgEtTHp\ WmBAizLŰCH3v%T |m!y9#̻߮ZMD4^.>هU:D$N{LvHAi%'-1;'ruWN h8Xz?4hm=i[?Eِ8рz!*&+6ϑ*I3 PS! 3+A__kv@R#E&)r:Vc^4S,dqݼw\6V V%l%=gxoX3&D#7;7&a.@*W[,0\khd A[kwqF㻻CPWʀ}œE|%`lH'tqBo&jKZS!sBwinrzHEO#&B%suk6~=]-T;rYyMfog\}zKc8>g׋; Ƿ[.9k ET(>L]=8 R1.#1zK2K\Q ;hoF|, PeF_,pBu^iؗ #JxKąI67eWUbVZ*I:B BV  TאY'|Z N"\2q@x$ܐlK-4rJSYWB=!+2Ə=8`'pgէYjU'Yæ!xP)?C.*6KYj<)pdI Y%H^02RBt epͣK߃"ZlaA-f+t4GV?)\2PYΞU~VtR. 04 AS^ jҞS&/3E'3_D@ö gĺ?77J7B֥BF}T^:nO?^3$ OA&ve@O‡p&{72csZk+(ΆP}-7Me4NAԈJK nD׆9FFyg8;ҽ+^1~3.OQ*t|d" а9jZYuZƙLȴEcZkڸl(B4eҶQ ZE#쑄`6{*YGヿMfXb`н_L" #%a{i m@Q!R5/}N&6w$$|lz,dɗ@SGXdaN)g[쟜yley˧z=U@[ჭȷ<ԓ2NTWn!n6e#7+&wQIf:igFQ;Ts/s7=2 r6#B~j ܍R /KYLO]:mGGWl9p]]G4_GUnVP9162쵸QYؽ"THd(=sX]GpIeH`ʮgOȖؘ DOfbZ+g1:wkalO$ޭvټw!JJ'h-euG#&FvdVLnH+~ 1I5E+b&?n%pRj)Svh+$0Ӳ1 R!RaXl-Yu0ړ, ;^8 y5k^N[fo꾇Fa]^df2^H챒J&T}-X3!!ՇwW1J}x̹"f&GiZy C:1ݦT-5aїY6ăcgw#%hB;ypdARUFZ8H;)ܰI5Xw%=F0UGL/c TӅlp H7D#%A@^p~S _< XSbl Zw%xk~GUZ"P0蟻lAqѡY.h{q [e]@lj8\1c%FXYa5OzhUѫ)h|ۨ$LƵVsøv~rBkXC#I7t+*#E§͎ܔ}OO/WVi&d$XCy{tJsSB7=|zY4VER T 0=Pq(Ny޴f+Jb>9P.YLl1'xU! 01: 1p g`!Y΋ RG uވa68vr,LiQQ➿:Fy{>) s,Jſfi,#I1aĔt<5lXO%v^ FQ2|:^}4 d2z7{軋j>h%O( <E OnUAؿe2̀@8BqNNo^M1ew<ݵ#1FM8TS 匌\҄o3`;`on0cTr5ed959>;0#i]$ցMd5dֺQ뜛>mJ'U-VH\FYeپ-t n]7 %ClRhPLXo&'[rZ~Ө 둒Ni^VpZ{ > 35c΢? e{p=/ `ȥ]6렆zKzRUG=8J,H^>DR؇k ʸ8d,H@Y:N"?JT L9!ߓW8.3R\r;'!oh5⽼zBo[c+}o0gyM[ikdVܦ%0QEZNlu]wNEyLU^Y$IS(Z9ygKψ44 '+dU*Ll_cϮ7Bt&1JMcINdr:R~3 g1mdt  ɽٖFaB7nF5ɑ6A?p W2/-vM[U,׳'Jmdw@_9Gy1BqI!d|ɵdՒ1Q;To< ҂S(F_eTW?^M62> /$0gR쬎XhX %&b# v a e_-D~jaw5$o1 yb5>uPzbz ˯~BH6sF=͡ H8%2nqn>(K;|~C6J/ IJXPgϝ5 B1,wuk(_V7;9# #Xz bPS $]`n {r%z't3^R`@`dŧWb28n=fCyZ2[KYn:mE!+8:z!pzzBQVP|mXͲN7@ō'z7&a+nn,(X:M0s?};M.!'iġ50`HreЁ;gUS/{-8 M+m>l/R22GQ]IȠU\m~fPB欬w➥>[C\5;U~4vPV䗧.掺ozpIe)^xp9 ^}zEghp,HÐ:LOuǜ@J&qg4k~ScZǫ*|vEZެdVDϾ"[/(Gmu⭢:$8!vc-KޙSD{(eUM΅5m|k7Lk]ӵӓgjVnyҴWD928ձr}`Xlu!YG__֓WHOP\rj@pwn9xo;OF*aA`F2Ţ_`˻etWmSt2#-uwZP}cehByMyLZ&/aQ:2g@x=:ͧHwvx},xj9^خA5\ryr.qbG$;oϡ1a~UpbZފn {b@'\q$c?ߡ&NډLK0Q[5a<{jhIW^\tn[ CdYFTZnuYS~%)gF S1cʏC~'X1AbHh<-UC:y4}z݀(,0ae\hB݉GWy GX* jN|,IB\̸jVZ0]ڵ9/4d$ǧ!;B xF1 NХ6I[fL2&) #t"d6,:[6怑M-zu_[U|I8F7D]nnD E=_#4DB>CQf@ۢ}-ެ>aP$@,0nbiC?EjuмXa'_ 7-l*?.ktgcVgWt^gP*`2  V)"\ vKA<ҭ `A6#\|/=V`N:#_[<.n>tXA$p;O`至Apʢl$ *}F\tiY`1 os'1kn [*Si>g{<1dggM1ky!w{H="Wגuwx|ۄlӾXY0֍qfcg$Ӵ\5b5\TIĸ\zR$S_aӧGQ WQWAYCu? 7Qviqqz1St!^;\:aA*?]G&2k$Zx{ޱ9t0Hvi K7I~/bq@^EKZnM5Ma˽øb%޹v]<\DconHL9j NÑ1)K1dKvws TX?@כz2HFta<0~gAEDK7D?GmQ7W!(@otzIٙW ې7b5cM,<}y`*$CQVD/}݌%Q<¶d 2f**h456iy=*K/74mU` yjs}DGMP/Gy &>Ԃ2vrivʷK ዽ8nrBNy/,f8g`R3Ē;#{7vu@ 2s\0Ӄv熾|€*~Ea0 v{$HzjH[Bֆ牊/SmNzzPL(#⪄4ク^Imk`07]=rg̥k( 1&K Apl+MHԫwGꇒuB rȠȹΌ,W{k0 /L9vuVrO5GG"otf $T_ȪcfIn"AE{\bW}!{5LՋUSn ӄn{/H=tyJJ1_{)[d^cHz}%Nњ(u&#ɜu:hD# /&*Lt&KK\tJjWx>A7ؖҖשܐlB%,n6N;$8 uZ#Bj3>@x,:ΖA=roIfȈ/]:ZA_qg8*!,uzN왰l"RYZkuQ>YݤK1ɹاC8WNAFhFLgN^H bN3ubemI;śj{mD[ fW yE&r/&UhL{ǻzULnyu#t ;6v9~HK6B$  ltWxxV;yt"2hhI$;D$0J.g;اT"Q+2`Al"|'[2gku iSf -#X>;0{c0b %FWOKzaji\c<}wvyW| 6F6}Z?ĬКX󇫅[,V:uȀ]x[GC !(߆|Қis^Xd);$Mޞ_ٝѬarڝ;Xe&|GG!Df<ԤO> l$˿c*/Md0g6UpeJF>UI^ =7~bEBgB ư4U oᩙv;KA?F0h`lA˻q-0ht1+ 7xK[.O>[Ơ@Y $qҀd "}(;:uGh~##lt h.멱ӾD+/(ާ!.g6htr% w[Tmjܻ7FE?|Z2 ̷, :blzv )Σ:WHmDa).yPc`2 :qiNbr1O\u@p"v<;}yM1FƎU1Օ5{G~NĀS-A̍_'[X\Ouy[U::(^k@*$\$\WGw\L\6I?)FArm Ϲnra RRh $Ҋk6 ~_5Icq6J]MG'>s?|\`h8PN\ӗ+aeTzVJ.'}2HkTcK0IrEAIA9'U,mLruc̉M )r]\r\LX+d]e)G3oasꯩ|Z$B=*'yǶ؃׍d;7I"!S 'S8ҍ;rR]R䘖`KOqSCfl!Pxݰg Z{]+JHaьeCqנ|.avC2*rYwqB~2 _>|{-eX*OlᴁğCI{YJIC<^|TNK=B<r,}C]1}7s<SdSׅXM53FuÔEBGAgRQ71U&1AI0(h6cz ʼn/xr&xVA39@n׌!W Za^Oԉُq QkŽ6/~{Rcpw{:zT_V*)!+0/q<P+#vӮl \Wx9 0eDf~X uDW)pOiS]iFe_(dBD?s>.5)?ЧZӚ ӣL} v'=}2FxAj1o‰`m.g#/e=xr#6dE8ZUB ]EO)R1%lmx0i{7/vUL,)w&#/ UCD%':S+na%ݴӡt²糳!Pq:ѿ~)P ے>̮R- BI.:/}…9K3s|j#R4I.lQdR.Z1g8[K&SjLw$YV !,;TOR&g.'X4㻧ʠPK? Cş}48u3}ܾ75"H_VŴNZj'Vc6f)8ӯ] ҽ  t]bW1ĤoWXo}(CLjkԇe2JpɥA!U 0:~&w.Oǻ',/lFŐ|vPC_!Vy6Ύ`^&S`݁^%2<:PxTWh9!Z\m;A3C&ެdEMf2v%Ċuj"H\sQ6 '5tfGT(Ry2;6DX>O:ٚ1bZ`0P:QPYa]PGƈ"AMlTq` us,qG ">&~MoJ c.qvr1}N D>ō[wtP[i#DRE!yael. 2lФshzbU@稐!3Qy}erQ&D6@N]|G{^]9Œ1rl4c睪*dPm?b rIܓgS^!mSX&_ ?ϝ)CtA*6(2G>cסMdF%TlM;͠&{J%^.[oFOKBw!=讫'? &wZsEbUFIGd yP7*MNL>kSCZ]ܚ3jILlV%ʾߪH:WV X&Lit{t',=6p.ca5SIO77'LW|lt5I+ ],t!0%ާ.a[l;̻}V:s [2:A#F,ؒÄjKɜ2hà1>(KGXfZ(o2G0YS@Udž PnlfJEyY'h$sx+b 7H 4JV bUaܯ/3jjȼAzt/I5L#I4)΁i|Ihd~#@˧.fV̀0%E\7 |qCB4_T"*9@j&R^}>|~ztJ{.[|1N xw5䒧f(!P1s p`'|st]^$ 8Y .f*DǠ{֔GUдI͑,q*"kup_)xnB8rmm|?Bܐރ.DW,5'j7p4FǞ%ki,Un^/z?mhP2H˃nKpQspҝJ$fJbCȿ̓/-f^^gMNГͱ99Zj=Gy:e|VmX;@zxfc|k#j$rbp#[憿ra) (KvXWBgN9u\r8Q o34A*F'2η&'Z߀_>N1{9Ki%wW-R,tK-Pb7O0qWkW/[j1}~o%^I&5W6N j+P;I Q-/vA&k67\PQ(t"^7.%;QȮ|HOrΌJ"N'P ]#d6,ᐾy1[\k7KqQIqH;EkT2'l쁾~~*fŷ%זh=MP}-]tHupЛZ(a[#wAHGɞvN.1C)<AY.T9 L5>owOKzo-qq SIS1(Ec}]ZP +id-EZ='rh%BGPrZOmZj2SF.L9=%Jɔd`XqbT4hZP;)OӇW"=7Xz1q Z}X&T.a6 iߙ`\[{FYKCT d4)Vuw-Lޙk3LSJWۑ7R#;:f7K骬˜vsmפF 5 ᲋0XF3!WuSz$Z]Q')8N#e9jiϞ+cVs^}R<{*B;& %Fs FIڏ{3UD{do7p( ~rAa8eZ}?į`Ce[5[çpwBWPR3[˻K, ٩C6 Òđ9(&"A1' mHSe.zdeoY8 cAA>Bo㭾HPZ"կK+ͺaXs"ZfUtIt8D僆A0v"c7 ]cTMUXAyNtKsє9(c5g_ ڕ#O[nl5=5^U6mi$SDUւA4]k>U.uw&Z U؄ZэbtJ\1Lv&g<~Ƨ;%? ewV1b4B2#43A-[G, O%om^^:ߞ%0^ KIaOjẕpMro?N8U@ O]J@H= "Ӱ:gFB)FO;/w7r4hHɳ'{~WTsZxB;1ϰ%E݊xJNZvs0 ߚ;4y)Ĩ'DA3NEG[Nx+Tj>(YMIIr!ONHxPH  gQ3"qpDv >mAN$FȎ@Q1 3~5MTPD}&U_;$->NtMI@Aן <^A^5@$l(+Eرqlh+xU=#oNAJ?l4 w Mm.6$2/nMDg, ħLշQOzzMN_/ı*#y MYxʼ7f~c$ӕR_QtSM0 ;'[=iPs7@ʾbD@PWǂX"-Dy hP9F $FlhW o3| -óqtS(&\*̡D55EYytB0VX}aeZF'Ɂ Ey1 S[0p3YzL梮D<#أNxqaۿbFpp.iZ( l7a`xq߈p9 HMhS6g NIĝoNq%~[NvZ0uݲu/VFVIrhX ﶪad$C)뺟ŷU~He,Y3Lq@ܢZFv~bSHǸƋTC =[gi֙4Zfl;UJ%@x%^1:НNM->N"<DiYKT"B7_Tsa~V ^N.(΍e>l;p'IOWʶ+R2:e3j!rO5|G@yzbmwHfZ[ hM׋rġacYp'R#`d+DAz peuZ}@3Czi ?ʜ=qm8&.@O#-1x k@W-Õ_ÑZLQ5>O8W [:K(JXe1ׁ#FVpzp.f'':/2Eu]V\p*։4A`psbDQJW}tW"uݓ gN7A!-_wbbçOG{}Y WH&Ua qճroF}.d`VSڌڻir=noq^Ы.nCRL+#U>y_WPžL2`2Xk ylJ q=y-al጗-*`ĥv 4ɤR? >gR?6MuXM?鐢n6eN?=!f, 좫Rs>[S8g>_ o&G7;ҪgA+F/>2`0Kh(|@P2Y$zPЖ(dsst"/H>{nQ~h0+{"^XXfbм'VVLu3K[)A.\*wtMFȾXc]MF&vA=e߬}so~/Am?n۶AewTyrփv!# Aa7͠0\VUoPR# \3?k<{$<*Ii%&ԛ .6䐎 ,t'a>a1G=QwpXyDNK>όo[>냆%g,HE\vzp@n_I1]$x_'7#3o=En|97>1 YLK7Hj,j?kr%6,nE"|\:FM{Wl$ `Xۙ6b$?.wjXl2j|W!ipEF|",A3[f% |w3V@Tr(H:yfz$=xb{ɢQ;ڛw7 X:Sxj*|\lGqQ3-%m+S5LfRZOb>r5:fӟ1P㶡<@ t[C1DT^$ŦC%J$0iU!(hP ғUN@=޿t 2Y"Jrc}7@E k!hk2vMcڱ,rK}[Lk^xKc*W#osqeZBSjѓ< SK'.Ga˛+!l8-%O뉈K Iy~?1#7%/9$sPIjqT&ԿC0'{{}[0\G\9/=>زjafS'$r&Fr_#IEN=2y XOjKq+{71 m6 )ߞv$ۍҼy˜Rjȃ+#uHTSqH*Jp?/& #}4 4aUQXY# SV e*K]h5s!+_s&l$R45D>(pj@&&(jޑcƒ\O(~0LH q*yןnH?tK?dPsF\8qDȎicb j 10p ײ6VD1j;zB~ȴJT8ε_咭 TM덌L(Ȑ8#u/{ʱRTP/#θ$A/_91?p/,d.V4-az?3ŠZ4@_ɋ5UcǞ~_}oey10`nA@|66L!`7PMŃBք6$%ftۥzYD}`AfPd%ԔVW{'Iٻ\SDT9$V%ΐ[X*&0C*'vZ 2lVxpg.,rR>d+q_+5W UuͲ)Nv92g<yט駠S )gLFI~ !1-}p&tC zFo)4 =Iy~>R/o7?G `zÀͱr:A*λaT&aEtVJ(gme BGP->  0ˍ_ԟ4 ] wFfTtwj M:1iW}~ &cBؙ*b"wxB.Gf[AEƩ3pHM\,S;Lw1H^rbtY{dW Mj>:ZxרVG4KD޽mw YLsbۓ#jZ/L7+܀:qT$ 6 -˼|y#n 1̮N˂i XI^zMeFF0`FOn0˛ KDt*5PGc2h+mZlKh%fe-VkSK_ r  LP,ϡWm?tۚ;(,Mxh]4!0P EvFztϊjdjYN1_o en 64WG0 >g;o;jt5mOv"D0M6c1՗Eʈ#S쓄~%O8? '!X3 鵄9# Cޑ{E2*&"g2xis q6z[^Σ*Qw]ҼW>E^3-=Q'Vtaa HN5|eꘓ5 OjVckk/݂a#2 [WSϋp}B;+,=D' Ua`S*PVcӞ //(eL僽2h:y9g o6uo䒉ފ#=sF(e=e:1ؑQmAA8$6TT$߳w?VQ)PML9؃yjG2BS7֠6 %FkP kolی|^?9rtRR8V6{>2>>%act9ģa,`SCx`vS,y&xXٛuC<F[ m*Mb&䯪{\'+Ԗ2>7]x٘^a7;Ø#T+!{rW)aA? %z|x lx)nxV[?ʾ)<'f)y^>kH)ଡ଼JV)cl"y*3.Ϫn.<RY&ja1M:RZТf)R& -7H>E5_U /Xj D _o].J`$kuj Ѹ9-@7`e#LEHWW)ܛ޷xՐzz@oܼX0:<& kHv܋KYYJ 4ToYVEKn(xthFN KgطYñ+nrj??J5,V@G% }]3}Pu&ͱ|?;j]HSύBtnCϜ' N*y;*e]-H>rɫF8l( ܽӌadm8fPծ^m8V+˔Uc uo3'Y1 LLiVso`C AGW01/eS ȋU176B3y!|k[*+NYVL /4SF詂 hWNc OILܿ6 73VaLDjMv>L&|y d "6*3ϬS7pWC|bbhEȶyOπ)XL7mJp 6dznծBNDpv+퐅SN(r+Of>ݳ ּ$5ұNz >>L{]1L̾ TlXwjVbaoB:>Tۓ6yz’<wз^uE%ՊGN Jӣ{qxYK&Y`fS̊m|ɤ!Dl瘦D ޵lu>[ک0A[{p*R]U?+8SS4,D׃pjON`3^<28O_0hw6uT7ҧ-KNDe~ d¹ĎO*h6)0!^P9S=K8: F !L&r qfmJM6Hfn YؽNQl۬%\i}pl#,:- IrS(NcW:!eIϿiJed?vq#l'wяX&vC{7?k&#XSsLu7d^"}],e|JpڒKޙW뻀VopHթ xf @s]_ZVqwt*< !Cci)rW#&=mdwEۛdžn*#),c\o,\d4[L [1<7"@@2S뀌O/ɢ_Ɣ1Ck*n.6(`Nq?_ԯ>﷤G`ɟHx\߇/hl2 `T;՝Y&<=cGA*]|/\̕tP8<.G#-EC,m޺Яطl:ֳzLq  &6F;"*Y\e $e*[:!IfJGFoI a`ɝ4PxްW.JIHH өUayߗ ~']٩)X*,e)lno|X͹B6XZ.s:EmEĥa׶Ƥ4 }'Hkd] MвG5*G#znkyMz|k&z IК ~j _3!2 pH.)'>%iyF ?w¬EH|X3_(2 8qi`{PEAnP SaEؘߙ&z!]5S|sl;Yo\xqSb0&>і!f);n9pġ &W0\C'>qsm~Œo,6DP$hB;_B3k WcU #fFp'dbC@!xTmoU!aUem(<-!/m)}Or$TM*FdO7T$D|m&'PqZʥ8t1^}@=F\IYWIǾ>:?s]$ ' [n~#~u 7,VlFsDV JO*ߡ`øi$NhA ]@Xζ@R2U}t8rѹp6IC'0Ծ%9e!s+8K6ǐ/B6u x{#w]bq_|F"bL+s n|y$xӽMO?xo健( Y]H{%k0L"]۲>I57WHóʾԄ&"*e*# .Hס46Cض%.0%ˌ5 neGOA0&j%YԄX"z]'=HDxLsdWb[&a>)<7xCC"-P::U^V4j|I&]~Ns1\\C$BOkS.eec\x'j=кPVϻamqhRRvGo[DzoQ4+dP{y³wU?aa`z9L- JauЌVfU)nWjPeA,bNm(ئdJ9CypCؾt?8Q^g8% ଯޮEGٚ |r^v{ G<=b( N@S}dy,kfe?w9,V ܱ*_Pߔv'?(T$%.w˧}#[XgpDP񏦧 dicc. cDH.MT۫ }' -2:H3S8 ^,zbN+ю#-uH8 ]J GP13HHc|4M ~QWDYװΩVQc/eJc\\D3QxHx TG4œ5Q ĚF=x|gO ی-)h9VMSvi\H#O̞d?%fַٸ.5\bZsG/^HyvOtyVVCցA T?6>0Hݓ3k/w#\g 'L9=]ykzIUszӋK2]>gS)%j U(Ey-Mqxdh %\C4>9oU=Q5XytZIg+NЌ!`͹n}Q˱i52*ç49<#4Ep2) 9Ac$o0\œǃk$c𵀟BzqGMKV:?i9z@t'dӃv!W2E9Ry;8_< PA2Y/e|7 wbk._p:W4 Xe4L.2?фR 3t=$Yqg(T6^rǁq/G E&TrI݊pJ^'di wZf@#G3+(=ʡMxHFup1-A{ ѾQ-f'?smg(c\?JŢD:^u.0ok)ٵNfoBМҮqIeK/T5"=ʉίp65YEC+3 O춗4Ixl09`S 光s0xtAVhK7[]tՖR2̠a|azwj: FbnRɂ.g 8 &qZw0P{V!:m_n >3(i6Q_VU;3kKtRIC/sߕG\6UBѺtB4)Oq==ʃXab`=2P 4nI] .[DbAaːVT#oǷߞyBSdȵfჄ0jQyrL9BȣQ42;In .󯶀&! U]l#UK<~P},'u頦u g?KwIn*Pǃ8UCx@B"Ā_yBмnd>*FQj[2>.˅HΖGXړLtjqܛpZE\!,ky(=1&.qFj̫=;  2.B]l*O2OWyy]݄n]7lAg[;fQws V-?9J풴sk*iw-q HޒW@.%̗y?vV,}FDn5Ğ[+ WB*qDOa{˿azX8'$  j_hn8>ڑZ!$ FyP=>ߥ9z}~}g ϭsbQWoUN/;$f=[g3T&ubE2A!_A+d?S8ѶVǞrXGȞR~[2\E&D@HM/90ryE 4h|M_4Z\2MGRGd];*K3jaR+쯆ۨ'Y|zV^9Y 0gyyM)F-u5'hH"`iIgbH i0:>6f 46qdEըoDr)?&aG\I ^_}:]gy.e}AO֌ &Ccs8hINԤdxH˵.˥s$#e1Hd"(L̝ !)Iu.ǕMƱ+y`$;;gqqj.!ADWLZT/<#:ΘbauwYjRLYK2_𶡫:9U9ũE0")@ HwiAGWf(ezcvh5@`ۋ}пf7%" !mFY,6 ͏maP&c3{ ܴwBjZUn}J).t̷ΕLȍBput~Ŗt?[ώ4Y.FpR^ I@)꺑X; =ɳQwJK1w akB_/l/@5QAؠ(H P3kzA WZz61GUƒǬt'y42Dp$7J%0=m[`!a|~5ɸ~o!i!)AО4*]sv ŕB9(X@MF8~fX"!M7 }\Xb#1Z@~Ys&Tb##ܠlm յOC( ,&/qGcxC:+f:ӥkg+\ u6#W=FX4;< e8n5g6@Âorb>{Jg[@q*T+yT",,1K\4;lg]ur_o"Β{y}YBVŊ[ jA~;4bIjvnio^-iW]PT~FQ:C/]P^/HcͯJ&M*oqzr-tz+Kl?DC͓}n&M !ۗ;Q :z)"Q7V?L n$˰^XjIZ] y}v'9;ZFܲ XK֕Q/+OFYdc> w?#ܯ"xGW$bg~(=Xn(M".2(?r_|](7SGuAYoK ><]t\gn.be |1IeH( "4P{2릎&"'}3Co`0YJLy^]SV]Zf!`𔮔Udsf-ΰn8䷆jgvgh/'frsLWlfeuPB͡2.,b]wI%Oշ[eȝb [~h= n; AsC8UsȽ~[@8PWC7!\l?zμ[I<|8)^묛C1=)/wT_yNDŽ\Y`nn;Pn?ԕJ{nE|3kz4ڙfj'&&T =p )bLx_3FX_:6uۿ0uYO)%m:g̙*5*GGECQLF2mbqcWkK3F-X% [D8nt39T$ݎ d!6DJ!i F(+ ^4<ȫE!wʤFryT>'J/^yנ!sJ>.%\:;dՀȶx ۮIe@/8Oi[+/x6p?Hj@/(+ELMWsA‰Om;4v>+qY#'\Թ0"a<:xݳ&iB lxwqW, #'^sRD\b/[`oC).CQ62t0ci7E %Ryp/9HB{|CIcd%Uy/@3T}q Ohk 1чحG^Q>?}u՟05n@Fv4aD"Z+SƐa!P* TS Q;@GSeEcm<㾺t0v:3ݏYmw+džp;0Nƒmɐ/$?HA'GMl݁uSzV :%]R4F`P ? +_q[6y63b{6gHQH؈0N @vy{$LBd%2l}Ҿ-XQLTRtEܾ,DƝgeO|\?I(XFO&M//NIjt?[n>&4}lpDn6O!du0qNSPn,)^e.8F%a8,ۄ,b^l̀0h]dZZ)HC2kPA5O/e7@DHh B>z7 1ﭣǥ Y):2O!jx8)B)w=mu92>egq1ѦsܻϣUH^hyBA I`W/iaDFjy 0}ǖs|^ 151X@,]UV-&r6>lĴ3@T1Or^hc2Oj"?6Ob5/Oi?SbnSlR\94〒A܋U;T}S \`Qz߲rs) 60e}>_oLD瘂,+OӋXY>MA I2+섕){Hq lzϭP۲lzz W{ Q_5Vza x'qy2SXc(С6GKn|?-enr8 "ٿhqh}XDOZf`|q)]jƔs1 e+Z0M8H٢J,fu&dLLbp/o_@a$V0ˑQY)e#MFnLxʞrgn2|#:Ot w7H{H*GV<2Pm|2ᙝtSKkb;+QυRsvVhἄL\n K{.\|xD舿ѡS?-9sdgǀu.cUd S||$FE>n\R8 v{ m|LVs6}Au$Oc'T{xPA#^rI}i%]Ιv0Ue -*c>9Y̳'L-㸘*uMwʞlf1-¨wN~@ڢZ99J gw].TS  Dls pXG r&Sx#}ʱ:>tp 7K%Г }T2vxcr |2|ŷslF;(=P dC5npzYzU,Ǫj?mpдɝfgLӞаZ+-[˦`%Ύ*u2:z m;u5Xգ0}ex^U laEĊ|sáiJT1ePN>K^ r*ν\ftjKq%]1R}$g귐;9-n% Ջ<)vj** R L= X/ >%,|IS] ^G٨SJ% ѿގ{K+=_qSoHSC|FGsҊurRhBIG>P!WgFf@T6.a\V0qVjk?2QWǬWCw8@b>@6&EDWS238{.)B~UNc%x+0n>K/\1\~ E&I)7"9w]G`, t'V</^H&oRd]+ #b[𢐲ΜiyVzב| /:by4D{3|6H=ā0&B-`hC*c?|˓}eb|fgOVN֖M+X 8YUQe~1@4Du9NydkuI] NTv꺉3`6\Ǣ)A TUBw`8M҉P3R$؂ZrQKi*A@MuNG] r^)F~J4K~ RwUw^Gm){xKROLI% w3eh!,< \%7H$loo%L?|jN?]-[gbޖx9ɻ΄G=tm&D Ձ޽ %'wBMQ2${/<a)Hz{<\.G$g-\0pעF!kS~D` :kLZ㮧wv~qEo 1}MhXXT g_ xp'([/<Ж7tD`m9K3A=¶}GX|kTH|m|%8 7(svwǥDņƂtVd#XDz#DS:C٫|Iw]U*`spyD|zv$ $㫢m.O|2|s^tpHPSPQ,Tx{͕IlIN{Y qjk\/,Bu#RѦ5GQ6qAaswl~Zhs|D S:@y圠p{/pI5 b35T oDȊ38)<9 `١2_,V%] )=Y(3#/Ψ$G/2#lHyNFV ޓ/|EXT2ǰZOM>h8`'k@N)-TPyʖ9 *+6Bs#$rԶN2Gk2IXngܨdD4wtSOyގC[u9&޼ L(Nqbvgѣ¥Z̷>&ղDH tFZ^iv͏VJ%_O%~*78Oʫ>U 9^-VǤo;dDzcB>D.{K"rO{r IkJQ% .;@^3ḥ=՝Fo׉E8 R-CWѳD`V@T- -0z0 G6j-Wacx:502E)tJΑdb EpPʧ7ۈjgքMj8 Wۨ5Bu<99oM\@Kg*Pn"o/huIU2ϔM𕴑EA,7S&IXC7 䮩SU%AvDk $a+)`@ۯ# ):웞i&b\_+Ey4v[a(znFfu\MtGԩ~)ɿk=-&d5 6P䋮Ȃ1@ǺLS De$"o4K}K~c<t( Dх"7r|vǚ RuϿܽ0o9֯pD]]wj}jZDz׫Q,8۾kg)!HN,$mJu7e} +8EǶ xhn^#9CEa/Sh/?TJ^?/C2S?wc|bE]@z죍ccqG4O3dL. /I~ JPUխȟ=Xn K K~,K$_m͵`7KBSEH] DEʎ `BcHQ-UenXf3ΫoM`>XπfPd4DKr¹Y셌@颿z$Gm^KOk@W"gQG?֗zf@̍'?X$<[m[$J3w#aoaߧi\};>f_6SJ& I{PT7+IuwԚR]YiTz/-,qCLJ]XU$C#P&@ɣwlJ*Œ ˱;..~.0)z`"e)?_%{AgF"zBOR@cuޮu@ae)[6ӎ EGp/7JM[ן[^GJIaUr¨v҂MS?i-WYGt˰?o|qy ҙ5SY\` gD{7_tZ_, Yc -pC˄glrvBhǦuEZsB\y)T^h,BXx(,}1K7Yf76VCeR S^kNO1#x{Bp\ׅz<D[+05m=t.LP`+i6 H!};NxL5zm PS-6܂ bY1yGB83I*f1fJB$5;NElL{@>LL^芷!djMk=ӑ8 Nۖs`b\sal'&P4s#&j Ѥ+C^M)!y˻ήٝcJP5-<-"{`ɏG&܅?m@ %Ľ936^-' 1Y\'ⷻ%'DzѨK{dYg#ID g~R uMB mrƊ4//ypNH|茳efqXN|- ]S]͹8x;׸G'JKd%nT(4`$.&~r .9 j˧7_]xwg9cŢS75ȅߟe'g)oGMG9rʮby#sC{#U Eb̠9w7qIP1VNܼpޭ5'P}8./_׭3g2.m:O<5KOM[W'yE-pQ3f:I$AyƸRw~K8EJf7s"Lp /dPpfwB() ݎͨHF&%;P!twIme~3@\&qT9z2h<&253hejtl ,g;lbo(N8c 弓&@;FY3Zs͔/|bC qщ"֩!vd3 ܖ ]RlϪ#j-f 4 8rX vYlb;nm 0ٷ ;dN*8·\C \(V k3;YJ1`P_;ܧ{  kI(jjcm"Iqlb"( \Ž! ? Uk"MzR-YhL|]+U[-Ⱥ G H>̗uҮifcKN7U6\EBhS 0ky]Q/#]:hKwg̣57rUC,%d4dSlD7&ˮ HȽ$B8$ lq]gcl^fJ絹&Yߢ| H.deb$*6~ޓlrQb7cH ϦL#IeS٬P]qJ*NѩS&J}IiL&)Zco gt"˜3mO4;a4gg{_[8{ctV%m#^w`}+C œb,Snk kFVr90(DGF'jG=J\l*bB8kˎY3wG(;SGj3,8Gs)5-5PÃSَvU?<vBST"x³WEW ^l䚈[Hq%y&U/"qb& xA4S4?!8qE)- [ܿy(֗B f+`'HQ k$5YF)`AWaZ }\ia)fy@t"@pf"k^)!Ҋa^BȡS`>JRP<5%C}G-O4w]H"Ȝ l[GM`r~̚@"S`0c| R3j4X;ĕZr/fM;q{x;$i'(x\Ŭު#vDp'"V8Mm?pb>Z(3r~,f(=OcPLv8#W(G+긓(zka18G 8D_3O,7Q{[CTaB'qٕF,) ѻ3*,^PaVd|0S٪∊cr;=d#1ulY{V<Ĥ!(Dpu}3OL`]rh8e ^G0L *qJ!er яٝM[3lhUJ=p_?Tt:е;5jDfŒb󰏃/o\TS|:4 b+n)/BVSDd4B.ؽ!G |+з Rgnm/`sPTDOh}#lcw bD |\t֎Q Ass7nPmE@xc$UK'J_~L#V %BQd,3B@ƙjIr׽ PܥJDtC( $+Ҳfm1X3]WSh!"QpRBkQ}/C7'$%LXu:#( 3~KPXsyGAv} {A1T:94iEYu|ݛD&(?I[؅[-(#誛 pi­vh./*.0 -Ihc""!S,elMN j}M‡.3SFdY`h3#o&358L$+y([%p<,} ʢD&u;Õzj\L0,Yc*ғHsi!B-˿1'?(; e31MnYРm8ٛ|bZ2Num1Wet0MPk8/ӤV%1VtgFZ$׍*=sb.I7pͩCݺ{ ձ>tu=~p\;OFʲLz0rRRiaí떅謩 ~K2jm11FgFp:[&T@ \;8E_E= $YB_y^Y-ΝPa2w_i" !HM/60땧2 cO (i';%p}7.[wh9^Iyr(~3fpe'bui{ CAԧ֢bRpJHiJw>5(Dfaf!)%6,cm/ʼn7lz 4%t1={҇;%%-v\\t-3`'󸑷3zy1,݇<>V>q+鸚`3 A؛?n$k70dBVSѕKB-3x[yjDXZ=:oQUÿ^eoE@`_d|b!%_+UytR=ޘHr( N/e;>qo%Ԯ{b{ff[4 ]y!4Q.73S0ڣ @$<#?ݶ 'g%.$DmuAȡM1ڞ@?IQP6A u #}o0; ٔ{T#=:@`dmӁ4 cragѡ=7?eul2 ŦaE u0lx.\ְ -tt kףnX+me̡[\ΕHjN{AKg1\73hPu8Y2m iF߅܎V\([:So}\jQ5*:Edm>C,;pKH8n>{{C3C+,dW ,*u\&BS=Z &h@Y ?A 9T+S)^/Ԕuyo6^MIZ|WU8yKpIB1 Bݪls^[CevKWN_礜 ^16"Xd.%r߷}k9nguXdİN.?yXV+Ră߲u- [AmOFFԟ9stYr9-*Dy7\J)Co|i>P pY*d|aZqٸytً#3d\,w^d(ACФmB*@N,m '0Qdpp "PSV)c|<2UץE-/˭&$[bʐjM# B~j5+wNKa oaרy|p$C^>A/mSg_C((U3HPXV;Rk'qimMn,W(Te@FOƹnB2Yy#SKߴErFt@1τ2f*=:.NM Dfubl'\uu򆰁[Vr([xZp·ײg!jL;'UZd&ZC|Dg3'SAI|n?=Нx'J~!8V}42)e8 !.,:i}Z.ǣ=#,@<FMzC=>Aey{2%NW,*JN)O goqT*Or^˖-q$md*,"N2Ug%W-CQµSj˭4*A>~;)N;^;+By wV(4,K6s w} (Xۇ-j6#e ȞgURSV!,b9SU͔dH{ձ /E7׮sҤ枃"YFdnkAx\9 8*A'yOEjR-Ϛj'e`;9;@-a`[Ib,-#7Vhix6mԾECJuvJlk=Qk#jQ1@}1oc+Fwg7,rr%n[ba %b\T\t?W-0jG(Gq I+L(6<!xs뗌77䶐 ;5m<{q v7YN1GX5gLrZ0a(,CEѵ?"pks{iEG G#`&$N- 1J]&C)wq=,pF{09aˎ{d}'ʼզnvxԬW$,7?$6La{8 Y#0/-F+ߑf!0$__n&"4B~Wi+2otJݢnz;*ukS"h_v>A,zkk+7$M޽Ӡ! Ϭ/Uh"#ߜ2*&$?wх1aNM{?n!0?4XljW22VGv +A [9<SY8$Z^Dio4T >WM@ZMW;/`Ϟ~ H X> 4êP##I@gz˰Rhϖ)cTsB.Js6*RWcܥ=-e!;ԧ=?j3xW)~#c.ֽnzu]i(ÑXߧ=]Ibi}+fZ~sWuU\D4M +#m$ʠ6`k+*G}(?PhڂR 9+Q9 ݾ*:̺Glfir 9ކ(23uYe h7`mp}s>v T:%D#b!6#Mmf{5 G#{] , SWC xKdžhVpf`{*,;(^5> CnK.U'μ gE-ͦ'̑1Z֯ F8xH(Z-иudktzp}P;Hv.'قs#]S8 bg /ހGMU}gAзSXѵf:"z/O. r aMsKfJ"P Ky_ĺS׷UEI9Aݿ\͑^{:ޯ̮;)gC(u QO܃ a 3t=Kd u^Ldž%gc}F[IX%/om-',_ PŌ_S> FJzrv'{>wW: f^NeW. tU*Z'KȈV]5>=z/XdO%ʰߐ!I'|tL:r͖0T֨[Lv~?e0su&d83hMnU'=-lj:D` jD7b:6|Q- Z6L\Lu ,rfMd΋7B}-*?i#԰Qǚ P"8($e\)[Z/rmuǬ m=ST^?b\2;k ~3MŞbV+oT*F(T.<`rNi5P#[s'l|AhUJt1+^"ru Ѷ-E?,8KSfSklOܢ2Ogqטٻ4$tH^Z<3^J8 =TEY3sn_kk y3JtҫtUGjb0_ : n2[.4`AFcq^yq ?ȣh5U!#c4БLeҷ)[oXY\z۱ŀ=MY5ʅɨ(aHH-d;~<0ZH/ʥPv# x2yaXs?߹d +n Ւ]SGL68֨Fԉ[sVK 3']4^ѮA''e-Nf 58n`Z7Vy !7cUT\Γ):R#vt('f}M@PTN k-I,aY$<;pN؁P-@KoŞaxU Ol]~׏a Oj2TR֤5@'qf ~u%f<*s<op '?t5F'mkȐl(9w^7.~p*05*J|@{/ٝ{bF?]ZuMCmp]ypunJC;D05q&|b5g&Z2'ŏĮnXמ ˅@Ը&^-"֫ o 94D/m$?ש6MFV+ER-Wwµxla aG)~׀݄޷ɂp}nZuƒ`Ӿ:jzö5Sv:_GraxBN³ +15Zp=$g3D0LCainlK5׬Iҫ`CueBBZInrFiIX^EsmtbHDZz FOdzxT=s tcѴMS,],d\ՈSx8bṢI3ew?"+\lZ$ϓ֝_ l^4>r`UwŢ|K8]tMê;@D ]dveO{p|!8BFF'# CND :UL'"[P.ifD"9y_/dSQ1\%&Pnc] L&S(CL QwglSț(±|SiO c=l; .bb\PhAX4:Q`m\.jt-[pwUhsx]&( gq-NH;.gu*:_k]jF0jc<4)[C[= 5.J<=(LAŒyi#cuӷ6= ld,,zֻe$ dlN.Nr :j93=}ŊK~c*&h_a6it†P@/VreK̝BH7p{y3ɉbhkY!XKBYݫwV(HtӤ$GDm\Hg#Q7FwbBV٬c;, wRpCET+N􆱸D` ʰ{+?SΫ!8!R~vXJ9h?,> /L¼ AD0x4MɃ(*YEl+P߬2>`#1kCLFU;{ˑU=X{#Y\D5vao._B˹U VK= 9a'2j?) $~wtөʐl۝kA/ '͂}%wVm UiPa!&<&Vjx5LR(B@ϊR~BݲL(prTg"ڻQf2 gaWlC8fuY%#}12MhOf&] WҹwŃx &N=ݨ>f~RԇnnPtf4,t*Ϩuf&Gki0Ml)Au;`q؍yNuMuCT 4yizៜɴC;9$EcR%Yw;<^*]ƞCjFJ`/MA\@ln@X69ck,qn>D0=_:GXqsK;}?y3W/[]\A:H<&eBDek.B# {HEUoץ(i5}tEE~V` rN=RScDAy!5"cW}LY`I ı؂S#ع8qpǀaȧ7FTb,zFQ+RH.H.ɭ?5'6g P,[r)OZƒCֹ& mTS_K?[Z.z+ϟ@xTFF)<ປ:<;0ϫ68P^=mt3Hw,tX%@~Ui{p^If%VFmRȜ׈ƢRUn[\jh87&;W9tѣuAnfWilo0. :BژC~$ӽ[ :})RԷ|*v~:+%XK bx=(OPY~=x`,k"aBrFVr].,;Ng\lCG&Rְ]槾C3n$Uzv )/Ɋ<{"hsDjx nY|WyCD*)93oh~Dܳf+h'֊Y_{$('Ϗcq(Z͜nEB:6g]D>bLWo"gAjr ihÐء~:4!6}:PTuE|꜃/M]^>caƌ}嘆\KJH0a$$eS|$AQ͆ wO>Ĭ]//eSؚKn*{ͨ x[go ۚLYBvdpdE\撫WPPu\^>!*zTQ:s]pܚ6^6:+< #Lהĉ[+{ʯ 7!7cܒj 8Q!Y.+ ʽ:wI $2^?+@3BMaSخ{1U&Empl툩=Jc3&xEA \vqtYZpj@̧(!Xip`?”>HQD ,s#MS6 йc>&WyTP$8P6tRфf<{@"Pqj4zFT9+Z~ٳ*̡ mHwٻMcOߝW D[ɠ2z +d[snwF5g(2qoO#-QJlSm>dIT X%0Vws!?8B/=\Dgt|~`!*R蕉Pg'`tq!v$jiVC  E[QzĪ ]RZf9zsߣ2Hˇi 2%o~>]9=HR 6ZgwUW8Lr3!1;k?Β4&3öՏZ_An,5\b a t^UαK"[.]fՋ\^:DsKÞ9/G R{mw]`t!Dk,0ٜ _\:$J\( 77\  $j؂qҼ<t -Xo:#yun>&󉷡5_L7HWb~jS_b8^E4ئ(7S։mڟ`jЛX )oȈ0;wP? }/EpgHdrYLm1Ns8'IXH&lރN5\>ƻ {OY5Jtf\5V/s,x+mQ$j aV *  LVoۧ.ď}}֕3Prl-˳׼+1/s;Ao÷Rt*D#b|$DA\*-Wn^WpZH5aiy̨ewM@1lű*P`BpT*u\E1lTuy%$ 琅sVi5- Giv.:>^Hgung:*g VчG1sE;vE̹^]8b^}!*L +2P*ʸbxeSLИ}㡯Inɸm'p~CRYy>` 3,>sӓ0 UN+ EH$ЏX IӇ+|Lsz6;f YғY>Y X%\HDK@!in'Lڦ%]qĔY,*ՖXb Sڴn7R#QG  W#Bʼn˞Jg< +Jb7X?yDwԉ%ˇA8Pv4sFY1Dhi}bcJQ1u`ZO$ 4U~d%,f -"bYQᆺ{Z]4Z \%թ/{ʸ~B穆LL+|9 ؜[X.-&R7x}(ͳf 'oFnǴU&,1=e<_\!&a E]4D|CpS\z\8*;WhKC":GZB^]"RYy8'.3JAm,/Ni}bN860y /6YayXx \%Mq$Dwe_-p>3r(s^Cs(ܥce34w,'c\Oclq޶3z6 *XxI{©^C;"é9.JKȠT4lKSxZKb T};kSE0[sWok:A:$OY&Ŕc\ t*R׳Iݿ]|SeI|Wid}W +yHǸYø*1PP9C`' P=ldijّl@nyL. xo.yiBuXs.?`\`KC')߱d LlgܞlV $)[|:;Sl$?D" $ h‘r]1P#"EIa4Z"ndn^x4ӈVӝF6*azp}9>i > عԎ.N5np†gA+qB8XI /& ֳUNmND&^|qzfT8hP?tSﳶgSʌK#Kzc=}:mߙ}ɒhg7urE, /DWH7m_&9q˓Afp.q+jVAcd_I+Y @wQD3Q$5<䔝Vz*-6B!k |lUj)"|ylHV\]Ѣd&~$ f~v E p./7ٿpˣ}6Pn6LJqU.qÂiͥ8kpa7oυ2wF~GXJK=$UfYܠΈd2ִP 4s7GDB-ZRc.ʸ ˆy%/Ikoj&?f.&<-h␯J+`ب+x1hwxK8 4g2qPFŭF}C2Ou$LVܹE'SHMź HTE|aa!,ӵ"X* 6`TyC}eV0SBə#uU.knp SiwD5'x[JsNuXۅ0%q4 °0Ҿb>!F>,W_(Q⽩iG 4Io3J*n] "kA~on S ])\h8kWuOTq^rfdpV[DfDkYxeYgYPْ %9qCh"w%gaŀ//b8w(RT%yx7@iy9Ne'أ|2˿@`o&I(=!81 ?NYHҚ8 `z) ɽ"?j8tQA:1)]@.9&^3%Jc3Yz.e@`K-=!F/a?GưCv^?&-FATH_5ϵFpt.>`;e Ԋ)t;(/bs/l2#PT`I1A1{<8dH 0)ui}%( 7%3m=t 5і(mm?,?xVXwA≏'_ 6d^ա\q޻ʅVVhf4;|k2K]ۜN Z"礝xVf6ʾ0OIuKo !e{W8CԔ˖އLYGc2#_Aۏ:7MN۷6$V|(A+T:( ϟn"ֶn0fGfw Hl!Wd\`H)I}c!FSB$vAոo#VN'P +w[/pȜS[ۀH0HncxAX9M@9+ߥB]fw2[&qY~.%U<5۹5n<Z$S2eWn؜ IE(3 ֻU\ɭ!<}%$6٬t8^T!D0C(I@<=\ERU{uL+HRAۼNko82HxPѲ#ûPn9u$YGڅC[8n &?V\ti3l$%YpE*ڃ2T΃P&o3g$U<1_"u<>Tx?b0$5WHZr钥2ʚ:gs~%D,$ >-Ęڴ3Q#hlDɦ rEVχ">vNS"BePe9i#qf{B/Yq2b'nszՅ˰>\t(b;3SfvE[P% ?8keЫj>CvUCg^#ffL6"u%+Fpg51^#X.}o/tϭH&bOV|:vjr@ю2(tӁ-@1njdR5r!-muIZV@\طiz~[O@<=̻JAS& ]0dOi&ɟ~+B02 C70,pO jD;[/ x\C<vg ʐ*󋫏#ZV0.ɋU{r6|u EYHi pGe;םWO[^* RقNNǵh7,\j:k=8& 62jo^kXVBw/ihzL8`~l Fb6F/V aU*PL_/X/ Ih^ePΠ9݀!J /Fe)BS̔bnՇ]h>P͝V~L~r dhqԖkZ6>/w*^t5!blEmةrʇj}C>orn!YdYC~ғkUR2@L2j{ nxּ^h _e<62!v7^%zMGdqTyܪIe]⢉me1XuW8X-FV2H!í>%b 3 )%b=/Zya?>`$۲6!7i"/vCDGXD#_L;&sSدCp˜[ Oƙp?"IJlt ߆|c.O.t$u<\q Ans馿Ц!1lykT3Ovm`f% @2k5+7ƛzzA`QRTog3+#X4ƒ7:cg A}kì X'Vzak?(y%Bn]:X͘"ʹ*EVQC_XqUX9C2R =W2>+C)UJ@K/UʌǓG.B10ږsa^% .z,#(Ub@͐@_pP IJa9Aon˙NߺˁqDNPjyGsI[}ޭ9 S|Yb[rfVW{& F;E p 'Ve)'bY:t/z}+wꇱ5j`NjM.(xbFw,AFtx19i}+1KpӜ]2`hB{N1 f`IMQ1E0:4n-g^q*M"McK%ӈ"ƗyF34/14vs-j0vNv:IcFW}VR +ejE_nj2gbTR. 30ѦREæk LTZzkJ tO7rCDzF[9"iM>T^xK=~-DWK?'x6]"C^~H]B#ȕvI Id҆W(^;xj`ʲ{jH,ȴ ]O62Fj8-cPgN 3o>|Uqe_~/` M uD{U.gg$`cuN,BA=8um 8 2`kn9K+J,ܐ69䒖 THo`*.8U$\X17 IRLO+ ?7Kn kZ+7&Kiv"ݛU-k'Xh.#dL$d.3_0y)m`ь~/)@6#/>PכUrKNSSbF¢>A¨T2<>=W !h%ڙ4~:;YKVguOAu:I|({pKAj ANN9|/i<[wѬygÞ/e;XɊι워i HYaخH/RqƎ"Rc{ihI!E mZ Fz-vgVX (E&i,( F@:ʆc㯚;'Mdh$[tgMM?"`KOƼ "RhLp6uE}dsZv"&)e%Dtx#ξ$G,kinSG~f"o$A ~rM j^Ŕ0r ^+,X`'몘uo/homG&>N 4W ً7͊Cfrm9c;⎀p/;MgBɔg125JKxRe8\mr&%\ rYv8oRR8a39k$]c2?>Cg@-J فڒ>j٭leV]8,-Pņ< -47/%`3adt*<Ä7pre']e䙖\BԀːFߍaH8WXG'`)vuܱ7ҀﲏČ|M"tQT OMv:,UqJaXu[mȥS_{Zִ#6[ !'ɭ}UT={s9 VVBL: ixU3@$k#]rS-Gbs&e4Ӛfps H-f4E(9?ma;bONa3ݙtm2oSw]T7r5#\iiG+c$S`䕘$O?]VZAtȀཧA1>h^RߘbQrrL3ca&o}%FaRʴ"{ID;'ҪpAPGe`7!a//:dN(ٙ兤Gj nxۥMht8)' :LX٭=NlSǸ:3&h[ GȗF])!\?p:AtB=e|v}yB3("D@i`" >w`8OࠚZ '|6 #;9x:ƿs o>5]ȊubcF:ub1^NeKQ/rFث?@C0҃'z8liW T9ǹNnM(qyC'z4$~$$IYZ]܇ci Կ0FA:,Cv:iK$5@^61>J U%Jq{W; a~RԶHT}1 ]TiJ 7yC>a*ҷle&nyKŹu}]81v-IgbFʝDn2%0aZuVgeϨ {zj[nP_onz5 ǫeu xK 5B%3PvE͖0y&͋'PoC5[kee:<cq8l~_N|“|,!6$Ua+WM~˟d8 _,|4]-50{eK_&׊FӚ!ʔ'~ D.!-F sE~[aQЭ-OVasdB}9c;ߧ#xINDArEaDk#_:C^}-‚=yG3 hP⹹&`EqAP?u` Ӵd|=BxS6/O۠WzQt[u~dQ0tf3|X`cvnƝC_חV' 9.ld^j3"~74λ7{ Apؾ5^̗[Ĥ;Ͱ?yC鸵7FU5q\wV)|<[**x&@\.* Bh~שZ*6o2T,!ǃ2 N0ܰS;,JsFZ6wn^ G% 92)s-( Xg ڳȜBOKRX %`_ R@;1EYb#h.Yx魦] _i =:iB1;?h5 W%DSh&(!@1;*x8^}X>CʯeޝfsUU3vRA{fގ`0gv}(Lqwh)=C"tkkh\gCglˑ tyf6jrZSuHqci%} dwLO0e8`&XkQ"]a= #tSZ2>ZXO t Bs}W{(1f.JFDG3P<VͰIg\sO$$)lyoxB,v؂'N8,Cw|T)~~g.=r/2i!TmS,) GD~2Ix1rV+9 |u0\~JG>)9Jym\Ɯ-0·&[;=_}p<ªP-Z/"Yp_V8 ;\.R1JU 8xPXLF.7`f|+O߱Vi*:Ga4m^`[8X&7P LRS`~ފ(bJ6]<+ qYvcEh^F(hK)B"&&s*0CoFewrtR+)Wju :\;nBMOebՈB25օ2L5! o"}ĪA~ 3MGk[%R4OdEȧ&ZmvF\ Ǜ&hLv4L_%F eȜNJ%Xǜ|%F, dGhK zlk鰚dW!Y85jn uGo2LJcjd in,%=)<4ėW)(SmzA @us4gO{m<\vHQ B~{fV͐~LH!@lߎ|IX Zkwtb%\y|S&,;Av7T5ENbMC0"hYC{5}as $RH+o7mv ܆ݐ)%-FM&͒OVPx/7cQ\R{so!k`l%&qi"V,(M\zL% |ZN+I.jPyw7>,"|яj m՟ ;O&)rXOn }59 *=YrI/^y82DZ'tsa?RTJ%(I^DDIrXnof%TQ?.4|ZO*Cg!pb Xr1h2hv[c>@x߭HoxJUr#JYB |F>&P ߓ!6)D34- je42Lqo_DDcn 肔zaoɂgɾ)qh<4ӲLƣ{Z.)V l83 C<M'cU_՟`&-~#gVfJg>h֑wxgǹ :K 9QEt6rG V&®NSNb %^<m ͽ7iG#zL |X?ZjjꃭO?&^c`M@vӴwS !EGG bTZBU_[pGѽg;a'Zg- Z< % ]/B\q=ȸGmtTJϪ`V98킢muP񳭞AgڂTUˀ ѱI!Mu",*_gx;ڰqM3=:?y溘fV2\DiВ,(j6x+Cg =XA=p7qEj+_ds>xsf}+dAV :NhidlqU`7JA%)UzR AM;$5Lj^BM.6@URu{%Kt9`|E֜NylRbq伸$dwlkҳ R0.>}s蕕Py[Yձ2K[9| 6JEny~1žqhMgg0!&-^D"JwΣ=s7 V-'`*ޱ90h'lXQD阎[>ѬATb/5\"\cz*X!l ۿ7/ ~tgNQc{}آ AHH#2^&:tlI[ jp2@sU5͐اʚ9?gl2CfmAB]7PM4u7%sWޝM \g+R~Os'Mt ot]"b PffSɷXY#LaA]L~BUI‰(~ ,9HD쯉}7kRɿO»; 㓘rZO\ {S$i=}v"M K>R'3G旅"ThCmؽj)m.aN5-D̈́;楪>,ëIA#N,ps_Q hK9< In; 먣G.J雘|Mmv,WrKF( eAt<5s7nAtHN&-BՕQ VaQ WCP+J/65 B˓"siܱ#꫸T=7gJLv' D /0v"l #t|[Ё5X <`/߱!~?W. ߖ*H@,JRTfLi5otP*LD 5):YgjN/׆.1GI0 '! 7/v8YCι|. }^ %WAz(H2IF-f *8%mcjcq:eU<`VVp;wD͊ق8 ޢrW'8bR+~lsY險ݪ*^V74>?eI~pv,NPf. îҴI Hu^g\= EЛg5<:2CD%>!J쟷3I.?>8.9XheC3A.˶@pDF@`LUNO#Mf}qK1R돈úRB( n0GP˩P>2\< XQ_iUz% Bx'08>*Z5XiL= O:V=<uEDZ]“2;"{/}N>(CJȵ6ɱSfUbK=xdkk4 Uy.*x,{pwj)em l]ff_yߝoCC.ޠEO0?Z ]B-jNh, f4{yM8aL&Q#{͖%L,4Qq=onLbE_h˄_Y;vici~p++sR[lku:3mj^`,dlE9:p跥mB@)k/RO~$J xd5=2lLN|%) h8JҮsҟ O`u7^8/ MdP"AmlApc^.ׄ:;ڑ^^BV\iNOnWC-Lp`Mo--ho⃿7g0q(`# ̦/֙;xpj25\cߧʍ, .>3PՊYXw,>Ro! $f /`ylm9G)FS+-{*8&Z(hA о0x4t籄u*ƒ|,xtb{qNS&%,S6 !ۚb<2fg+x/^/UR ṽSΘڻ:8YSߑgiͰ-8oaliŒ9k׹2Ced`E~PRyVUL*1f34q c73q`Naɢruעs~˾-a]ʘP^3DUK^|3^bP~0R7jsO*8|_[ddɇӬ#Ea! y=me=Ս}Ȓ~6#q,-U2rXvVؐMz"lU*R6z"עEܫby!vBmUl/j]nij" ԿqZ_. k%ZRA=T"4{。Q*Za!sjKB-ɺo1ŘV)Y'##m@*lȦSJ}(l3b[ƊSndkOծ3!K(wh Va(lӚ̈́HWs0x!fJv^ή;;hfE/ߛ0"2{N<7(Qp,&B)NQ^ݓ۴صM-em@Fomb6Å6Ki=&٪[/1]ز`^`OZ{k"0T gFdmI Ӏ2ҷfOK|}U/- jAdv" Vs9fmgXl@V YаF;gm$ n_0-<g(Ki o;n1-erP.¨M$%$zroC'Eb^8RE, fXDL!:->HV N, N +XCst ԘlM%R¥\g1'=ұ\s{_S̑oHitݻוOߪ**~75d槳[i-Ylt/MSd6x^>˃cp>P2ʓ3q9y1Џe A|Xg}[oen=Vmlr֗E$QO0_gJ#肹K@;o7wiK+cT4 *l~./#m 4OLj8'cC SGt˞xdKؔzp>Pmp~VMHnSA3}`D{㡪6iTS:\;XC 憽D ٻ>` g9nۓ͌B0[O)$A9/`׃K=tᦚ8z -&F`Uҡz>Ц5's1AywtE$.8CYpkm(_ωk;+$4+\&Çch[6geEYfv&q^u5f2 K6$`ɠe,"ʹOc83n@V)L/֖7m;kr)$-]P|W32ݑWvthPC{a BigԒ!D ~ւڻ, Zs)hFqYxߦ&6|<gf8j.Ö=>YVe*;21MI݆h6`%FRY{F/x@ e0ߊEE*!! ϻ8Wc9ӼmQ"/ }w+IrN7'=rF!'o 9_ڦ7J hoKK/n0 A* ;sGG lή>$\> <Iz -fjL_ >MJ6@Kh;TiR1Ш6ez">DUZM4 d7o(最95\\>AybT AIƥ#E;"axk/VW\H$A)i*mwkY9!#lW.Z7/7O քt0ce}i + :CM[?IvaP=9ѲuPX)c=v?܆XCul]<:qn^r7\jI8 +֟`bSClp Uhk :lr%Z?/RҒ*^a(q!eǨ8U)]730qA U]h!^{nЦiwnR N5zNi)w}xhW(IR`F0inH>ؽ+3XX/8ݞ)`dQlwOio@6Ch#Q:J'P0xz"?Z(ߐ ;enxّӥPpxߌEoR kFyLXK!FeJ 5RrE/D8EUKt_[m}(o^n 0kÍɀM E ΊPq0֓?mӧ\S9}6^&C&k98W-<\p:E]OF#ѣ/ZsO'2dΏz zUTTTf^%@?"ΨjJ|aYgC5lxaϟb{* ҳhO [=kX'Ѝ|q8$J2Q=Ǜi>M<UNaiM:SPGg$^P~AR:eHxqsy:+A;>0}73rkX4X1i Vi5{|՘ѱ`fĊYz)$7Ӆ~;^ qQ?~Pݬ>ލDre 6 ՍuZyۖlk[+W%B5ƍ{{0ۨ^j0-xȌ~LVppHN"\tJ!!N #ƂԾO;fK)}fZ%r%ga* v BNk?my@lړ{m m }=dK &IpȤM2R\%:{9x9d>ml?v6zZ\—Ro1 cv=pa$sOnŌ ,[ΛyU>)?`m;IԦ#kDǤ{5^>;zApL=MmI4N2`nCgZP'4)lVb(vcԷ&wA9 s~ T7+a;X@xbHe}!l\G vU I|OD/A]wȢOFAXO{Q>1a\eEqxdrfh-%C(#PJn!?'mJ|L!Еd''?8.1\7葥JDR%yAwWTL1Ĺ{vΥ u fKSV@ 뎫| mĪ곪*y`]2)<geN^vBwġDl> ;35H!.~.XGiS֨.ݱ?TL!aPjwEcT)da^OhEz&7sq4("";4,Z ݦOޝQGW׼vC{Xf+~EF7nK -'-No:0' lۻLQMEkzS2M` :ٱ9I}O8mn V-j~r8M(ԑD68yؖ`]|3/`~|اEezAN=hu+Tu40XuZVo.MH]ybDVvCQjz\c/8}Ja?lG!<!G+ W;k7tጷy]JسV|b0%$8>o4s[JmW몽)--B iŜPZo^~QuCC _IJWd{EvZˬ(t:(E=㍗08Mdg*=a3 cn4 s UҿI`ov t3dDB9i>g Na(DDybt"/.^8jy:F] =F<4#әE(B44<*o:D3o+;YxY"'9%A.u"cG;>KX󻐄:g/kjqB+;V8 ?H>}ڣmu, %g9S&aBߝYPLc]wX:k?10vĀ\$jPs~7>ר c=[6w]j/x 1AtsA`5$^|O78l8Kt]%%5OR4O6'=0ߑ4#FeI6SUs Żr]~\vAIV~jr 7Lr5m3uN!cw ӀW{;33ޔ̊qSEhy׷<Ӑݺݏd+y{)( ]=[A5%v2⣼5Ӡ 4kZ`Xy=f|F 53l046S}F+*] jE`\9*#r/UHKXy.=DFWX!Lh=ڬGL36V_iu{WR|ֹ[5;H;A0kE=MZ} }KP~p,.SHt3dR=l8N%MWPFo/m.s!XV>N\߻ Zd $ ,XvAi@xwBTM25߹_8v$n7cym'3fjEPRc@%'}pTTuqϟ: anO]"6TX ]fY0J`[24 Oxmީ)Prjyf1BEV',L$?g> uGa\1=f~ HcS6Ǜp?:wQdR%Oryp2d` 5fx/\NL o@'(9Ww}ɰ3px:0V m)^W0=Wdbж 2cj8ǡ#b!UWl0%'u#t$1;0nW+ofX"_b֬a1:+QU9*TeJ]nN S->Dr^$y+y@PN#h K%W)3}|r%&F۹'n_҃oJwUo%'53x䰄s*?c.M^O}\-C5NRZT]5b_7׈t/%06>BbJn@(+]AjBF{#q2\.D KtP ۸kU]A/#!ulzn tz.ɨo*,fګ}7(&i~,im(-8|+{$PFL6d*.jH2A:W8Y;էS%1JyS+,gMyTlIsau]T}F[zL_xUw!!k,xdXw3 l`\W.r PγIiנcS\i8w n 5}f>OR H2Fpu$- V9+, ?"i:60E !%rn/FrTL8=]R6NF^H/BW.X=Ea NY<؀q"|?cE2>VצV٣p3|Ҁ,U8Cd>NL*2eߩ= ) Vþ9Zf8!ƧHħ*i`/Vv69ᨱyH}'7IhܺH3bLuj$-&BњP5]ZļZ!>re%}òﲎmb*rP!4{yQ$y5eS$I/doǮ`zyK͕Zk9Xj"sm֦߹q9j@HF7,=e(]M#GĈAɺ6#?#5u 2ki7KHXltK5 #a'B\]|K.;7Ien|K=$zG4t'kKCvaM=X)N7faf/'i2 PZ LK}b[&Ŋ]$ԧ  ҇Xdvךv&kwS2%ķs#v >74's†['C7}lt*׫F{w0 A:Y ץbysƥmTAdb1dvӟl8:U=!Ц3GtQ]Ư6D+,$c8/%Dȱyl*0=W]`D.QH m\B@'Jt+TLMG4w(t)缎rcx[~iN 脦p=fx;|%MtRUAiZg`SW)gYK I=ĶO _LN9<̩|e}k/ LFˢDlB4tt4M!&"OHTYOI܊~\s kv'M!-M)fAQ)@(,=띥+b|_k )#xUҠ9oZ.?xvgoGݝՔwI&Nb/U&Ϭ^?I@< .}|Tc鲭Ůin 8R񬡛D+t&s^8qސ{s4ޭQPG$̣D3~94/5i-s1"qə9gJpm"5Hܰq(MFΞ!d\dn0HM8Ρw|_8:^̊k!{drlC(w c`+`r9$X6d⽹^~2A"݆$}cTlaB 8bb5NBx5i3! m0ڪ=t8u$fyqT% ė9A*DL3d]>J m/ܰy^77]a|wmU-t.X@LpO2t\pY^ϟ-أ7+lcr]۳?f #қ8[C"`R sI~ע[)췵ihQcOC/Akxnw״M2WhXrjKo<V4H8 }vYrlaNw!90G(qM}=WZ|c> ڰiII4jضʨWB&F g|z$d[(WI Sr뫨KxT)4JM!(D0 N&TR]05jU3΄V[btb]Zs&.#uuaZC̫Om"Rp}5ERmJ$^{@!!H,sRta&VzElcs赎&PpKtFfբ Xr_unAif;($-+Ș5i=!8?h]J3!"thq^Q!BAVbBm-9 c|"*JM\%)AM ֛b+h|+,-w?4^Ms8\7bcBC,H7ȅ$9érm ы7aBg&)aml&M\йI >NWJd@'4Q W frbo)ݗ|_XPhv V*1u2Qx[fMyA+pIfвtB76+a񕖬V]*Fu|'0 U!vn 7l2vl).1$m䥿u5 _u&4f\ ֎Cp`Ln76Ԥvj)~|(9%y/匮(:etȏh/L4@VL7\@!D/B-U%&X{BqL>WMs sRUzM ݰ{Jք^ayLɵ'NgZ&x` R0HaHi^4O *4SKUܤ$qH>&2y~ k”d٭XܠVUfROꁓa$(, I˺7UU+?O^AowKLmʁKM]jN\r arX'``Og#% [DeuarACoN9}STT(V o I7`GM%}T*-'h=/ 'xb#|Y~^$iJ+=0t-C Ģ5f&;g|q#.-f)q?p~=XQ2& &ײ6Ls "^zIӟj5O`՘h(=۸ ND^v1E)w)kVnѺگ5Uz2Tgc#AW?25n66EN>h5!X&YAyEKGs7Ak {kl}8ʧk&?VW` el^/AUlmttC$ Zws; ni LϢv)NLy^VMKRffվ"m]V PX}dqyd".{ɑI's'^5N;>#Q6.|@=CdB0#ᦩ:sek28$i;sT$(8k^#Xcଝ/nښ`xp8d>fKckrTl14xӚ,eR̐F5V?)a tfnVJS†1⳰,KzcsJ*A rA,ʴU 3I"T 'Y*PmDUiҨ+~C܆2 ;U/nBφ=z_L>gcNX%<+kdu~K!FGVvnjldʳxr|M&x[5ɁQ8r| /kEkmBmM`,2ٞx@D oOLPj;T%Vo}07Mט'HiF2v\ u?FҊ˝\s>'N!X{ktN_ hE;~K]ڪ37hҽ<ڟl0i1vi]Ɗ]7O_Lb ?_r /dB;Nȍh̅"EqL(Bw` {O$(ioȯ֗lJo A*}epƼK"}@a$=Oq9kl3% úV T)%- #_99ދI~|JlAg(9N'VFù17MOY(G:p޽tB(T]㕿Ԉ6 x}g;[mRܐ63n Ӱjm5:C8 b] [lkV Ě輹Z{4e^:d ǂ<4nJ fiKY!`aDs OL` f2cr׊iLNgˆ$SJU:sR9YQ@k0Gz/FFPJ BED Oa]'܎˽UvOVFt PM$Ov Y7Vvn, /w}oq}-"/ݒw;'c 2k Da{o ;ZPehZx(`NЃ> ? @o Z܅,YZq[qGl)5_HFOH`eCpEd2,sl_K^NZ㳺ҁG+ RhטC%) iXGT5sA,Fw4֓|Rs2гAx54׊GcaHL,xzܛ$?-I,%qV%wPWG>nŮޯ*cm_Ii 3hWi> ~!պo܈*m~RKU3ReQm[VgV]xi-_QiAbiOğVdU-)8;ZY*6W`.8.$eVt|PW{cSua: bxf?%q'B"9۝@v~nOT OGr+5sY Huz s'jQi5zD gY/lfi L]A;Sxpc~fU*٩R06q'EP~%r@r Дb$Lt\8)k$V.,_^:pzǬɹM{,޼V*1C^!s$y" gO{ɓ.1בq$g6Jkꏭƕ"i# k vEYhi^XѝW3E)o2y^b>lgM$zI0ᖉ.9|w?W'7]x$L.`9op-c:"M;&*'xCn r5p`*tO} xCM>ie@@c۫>yWհ` sgrw!3o;zv&{׹EYP؄u5D:h13NOFZcsv4T!(Ls^:POPBAl]<3IHMD|/fŁUmM+?l 4'OFgl3 [ު^^:de;tH5ڑ1Br=Qg&# Z#.F* e3T؎Yp. 5duC$$&177@ _hwcSyЛ juzf斎I^ovxmehxxg&`ɟv1wmʢgGe;<ǐX ܻl8xܯۮɶR*]¥^X]QzӅ]RE/1& iO%7K!jR, ߑCG=wUFC$?Ƭp!Kzx}摧4O1xQU\{P4ݮͭ }scSTG l&85i̐~J$, pt먞!|”j$: k5VJ[{/ыro.~b}9_{K KhRv#9mvOTG.Jz, |ke?Ek0!j]{B1ـŞ9Tաzt=VX|bmj67t`SezC˚](e$S2&@{hb3 7R2ۜ 1c?_cXsv=2a՜5yPMDH  ЀuA VXBߗΊ[y%2ZÂD%'qW/FJޓ$Ϡ,AS17 ~]E6{Of].#,;uh* 8 2@R}z{YF&m ;I_ Srn6MrǑg`e+ҿ^ymĬ+-nuI@*eCG2.cQ$#Z³0tvT}̸O)Hs i͠QdXe"q^=?vX"6uP 'B&;`9# ].d[4>~N%Z Ɂ]l_/K`T?,h} &('1MUdYb]"!,+sfZqb/N ;c-h9 P D&sGs !uaG6ty,咁8g 'Obiz#JsN8nyUOjcZ(=30)6)iKu6Č~ǬcGh˟串2O ~ \AX芤9m5,꫿TkP*Pcb5sG_X`_ǮZjb͐(2LM8unvNN9 gmЄ@^L7 N22V?#cŊ+t@"#Q0޼\0ohu)3_#+y)e:-iG3 gZQbm$+i~i%xVa.:7QߑׂY`p2pm)i->+Z\a#]s[+VX9QJa-SςaWh4,&tm`2r30֦wWOfʁ[zrg @IDsyAnR5ɹ3o\㫞W$qxt#4tiG"p/WZ# QLCkYKy]?D Prlľ%0K.TGJJ){abi5ws.wyۃ#+pnuBДnzz܃oP.[.5 \Ī\^"P͘8c+ E'ḋ&LbMDJp f݀v!_$Y;S;H]11։ߥKSQqJ'-2~9G + ;46E6EО)+'p(JOh͡4ܟRZǔ6\ %ҪTfO5_E_JZ[MD}1X1, .,XnU %m|3:S](W:8_?yIyY.Q |GPv| B #bXVoQKK =˸lMuH;?Me7av~wEo-ERPI>= FBjsTig=›Tds o֓,iRA:6hcƌf3s.L;BCtmd)taH21;U`|K6[+F)1xM:6:em.SR Z7oq i_3;;ی 첨] ,݃8Tn{Eߡ2'eP%[e{(Xg@0,iov)_Ds?Ӡq֩٣S5ATR$"T>[fa..Qe2 *:ӆ>s]>@_ƃa @uġ3嫩 ه`qwhP;d1zc=tvb4J5)~x^e5d,ҎicН.Rś4WZXGQ0(v>HNcĉzTzU/?N1/^f}&Vam6HK܇ra"r5;PWb%HRXm*|] /i}{DŽɼFb2􆙴a[PUqs̙x}*ߎγ!=Vz5kz{PGE\Iwj?'bСG~ {^By}r=ߟ$ nwY"Jl6B㋋MUFY/Y4~h$HJC軐;%TW7u%$Å_9= Uj楸WhI)JO=]|myhF)EʕvnDI~L:f@5b׃jiy<"O<*A (QtsPT }FУug҄ɸ!9{Ӡ Jv-|P{^Fu߬Fe5U: fRi;_j?(& A "ã ҉f_)|S9;Za{sݡbWOY/R zW9 Iĝz kbj[B ׌ EX]7=/" BǵV~N1.Y8hY(G MZo?s)D ̀Ozʽ衭hWyl{^-z᰾aS e&e`rUt&˳Kqjr:)<_ b39пĶPxc0zțT3$d_rjzk.c3Ir2Xi2Q9" .0ﱓ% hV-mKc z u{=ذ3}O 9iZ/@1E9v ayT[IYF{jV#7Bĺ=-4bja- i2t K_pVF=?9U>|Z*9N 8_/ i 7̘Fhi7N$|ߗO~J-t08[ ZMе$zJN氥@";?.tT`4#uK85W,bLfgeǬ:ur228ų X }G<A8LU/ sXSn [x(GcŴLss `ZV0BrWʔ~m⌾]-`BnCo _Agz`( !ijOC2 .(Pfe8fAA1xfJ pHl`|zؐ-h3f~YzӧH]K de@ г-5YǤ1jy@Q]cpk}

h۬mV2Y`.X3<# Kh5G؍vg )ƺpt?).>^)46sItAJgC{a41RWEz^\_ ]b4@uSd$ASҪ۬0 ҭ]ZĶ,qR<7cu"?-5y ]Y1﯏Mץ܏}* SZyCR[8 <+. v߀ݔspR<&n̏NʸܠP:o-pjؠj"-W1ÍM",t!Pq_"l6f].2) iC;'#}OosݥI NXf'~8N/'RL0>i X^"61ˋ@~+92$1%Rr<]+#9;Sdžf_JS 4O:9ݝ _#QS6x1ƒ=X *5.w͐ch&WX/~oGyT&;+;bدCC mԉgE[Z\ k'OxJ$2b7 FkH^#H^fX/ic@d?8M3'NlxpgoyveQ+}$w*s  c h٭I`TH< v_[f%.Gu6FN9.GsrHCyIGNPqy-'=ʖ*~jV;Y|T3"~uUU3Ո!  $S/)/ H˿=#B*AϷvCMg&O9U" "ٶ#ʖhbQŗ%8_] uY0Tmt})L*O) E]_SG\}C56(ab˴0}$|(B Hy6DIqu׈KΪY]p g75?gp7TDz~`0 5ij1X6{dSXN!Co,$@bI8!>#RTdiC.ӹ3FSiRh:7?(ع_9d [iPrbIHy'דF`:";sޘ ܮemtuutw:(oxt8>a=h⍅&B`E ?Q~Xzl0,(|~oTZuSlOS&0exZ8eH&^I$Dz*&8beG2 ÊHl3W*JUiTXO!)xI({Þ1kX-C@Ka ]8^Jjp ; xUu#`Gܡ' [lFC82|}fUE*ytI5lYRN₉f(/F7|Lqs}K&O$5¢=24K7pB R8XA- |0+ qOޝuX}(>MUIm[Ǡ/`Rf4:,dH˯JB}i<8D yvQ9/oQݚO#MU a$( h@A %dĸ{vG XN0u( +Z7 8j8'dJ_iOQ2Zӻ=hB{U`fBbOI{M%4T_  q´GNeEEFB̲'+p`aE yE]hdzk=Ko}~lQw0Ko#<ҝaGam$@':0MXK b1@`'kH_v`&՟&[eP*P a MI'#W/(1ڋ>K,^Fh`ؕgƀ0Hkqi2&Ae36kP$GύCழy "U>5)|~|_gEZa?+is>^5 4do0>C}y/ADO B9&Ͼ9]pb8"|,!G0`Q<n7m,b/%%X=[0N:'>0rWCd x`C ā56*Pk+i40&q_ 47 r;LjъtA9kW:ﴯZP3̫\#|%:,.{Vn{t~gDј_Dqwx.uI0 FQoς ŮFN璎z|(Be^ݕ+cJBbO&_àY+Cѹ W2bu߅fk`+ns=q8YZ2 <#I\[V3C -܅_*g/;)tm|Ӯz(j4X;vFr֒,rUTڮ ׺ <[ k;q#PA?\3ҲXK.]oZdmϮy YFU\g{aк9ݏ-oFj) b#ɉ55\ W Q w{;/~/j!,UL*bT7?娙!SB5.רV0괎^PĞrݗf|)nU+6;V@2?xgIǂ婸g37^mk:'pY` ˉn` Q gmIYO&/閌oQ߱,ڒgK,"n|@5XM%xz2!4?'`_+_ OY`p>k&_qiub4a\KDކZ)|%7,fK#:GxԌv!֫8$"Ғ On-4kXMUsgG!P" [ |.`T>*aE=^ܔ"XNu[s)cۦixK>мrٴ.?f+9OJUdz}lL .y2;aklzfm؟R&B\+_A UROXXZƋtW^nWZuo`x_9t;hIAӳ?~#ⅻAoxr\N v )8EnK$N7 FD-̝渌emw|{d$0iOP $4n z"@$*14euXFB<eq)zdعDq*6ч-;,ur,E;K<tfj~\}RHkl!1jgcL˟cL_zbf/Ib7τZ_$Ak0`L;/o4dWGC()4FA#8Q2a%sX#ɤᥕNEAT zddK K;J{~_yHGd8l͠F.)B=tpb2QMf[7eP찯]DIwb-)) +[⨛Uӊ3Zϖ %t7>~Y>咥 PPp k |Bho}{`5 u%_b[LP\eG6$,+N,yֱi>gĄnޚOВq߅)M*Cܞ!y0auL Ѯ!*׆K*2g;PhHoYZ{r3\RP6 Uy j}xm&雠BףLn;%S$\T%v  G Mf:L|zӞU RTVx9³%,ZŲ~7!%HE| z)IQQny۫al;ryA7i0!: qBȢ`-P,ҴrKk} s ~s%-`HI+Z$5< \6QD R=qL`ėmVslIi۬lJ̀yz'ջlzJPnC;1%ʧf(Qmp`XҞS)> HFB_`,lG-`e'7B·֝4[MI'%w{fo TT+~S&wz&9УEP fcnN Cobꆖ=rQU(07p;Bvb0p?S TV)p n6ՎCX43.ff&.a Ԅ\+63WFsgh%~yQ0'ӯkHB=^-Uon~_ Wg }t}ҢF*fg8TP9d,qV:b2[EZqV0$ a r'$;OyC!5F^L;S0Vgc7ЇhQB8(НC[@[.v_]/C=]P'QHw孍o?#w#ۣ{1sd\6=ۘfwM 0/1£Bg{y&N Ï*t.L=D;]egY%̴Y'`fnݖYKeYNa\wq=b6XO+.@ R s/]B']s[Tm&+U~ޗ^!.W CJ:Uj!|/VVIyr51㴧VB%pd*wUQ 1 C!D=ATJ`EL2Z.k]}S_Gѯ7(`.ȅ>5`wy(C_dG7Xr~cE@v$d'(ɑX6DF4&<'_&-BsP!pbRNR!۹P$t’Nxia:ZN Y4wZIOؠA YiDz?vbJ6e2ڋCÝ58ٵNdʁ}i#ɢ "sj['-ܪPZ!)}W_1t?H; \ jGl3' mRM~o!-.C^^/'s;)m5 9Nnwd.1._#|̰'P+3SZ _IV.ękDQ_Ip屢'J*fXC-mycIn1U'˲TLB:`CXraLL D |ގǠUM̾gk_kƜBQGxrӫNi90Lw˿*s&Y d@z0uqgqVy:fenA̡/zm]~fMG=Z ]I"U~5fwУz*T%U ?+QÒOŶ;1!ТhjCXrHLJ1;wSU}3sTD@}` y!&>Z˙r Z U|Ʊ{5ز HT^]׭pY$;l*0=ގ،q.KOtE U^-lB3t4Wq3h( )pI9&t6lr|;)bH8잃Qz2O-3`o_aK{mlkނ" pj4vQ&Ha2YFGY>0u&n:~G82?~1^/Ra;oo  TkSETKͣo`><_eO /pܷdC,mp>N\G FW&^>2? #WnSwV&mn3J81'mcUJ Z͕VHEXI8R )6@J P\"^]pɻ >E`*F#RL81Q!ƒm!FB 𻦃B۝eL8?D>y>O^Kk*h> .Y T_ށ!:PzD-]9i+,h7Z:ܸn̺pr3En"1g ֦iG`ԃLԾ֭q6,B3:r[Ɍ5 m|o[Y^"Bft ^7.~CP4kW?UHUCmJ49pt;;o,A鉫] 8r,:ZY:ڽ+JOC#%JZMU\E#O1ABi~DL*;v2mvxdAr,+Mkd6;bd8|?$|`u ״/0h#rW&i궮:>'!.E)+3fb:i/G[Z';| CYO%{}{B峸HfU{5U4&ˀ@_-?ΔS,&ʒEyԠU<\ZL=c_rQٰgu(Baloyts1TڽJ1& /挔Kɽ"m2+`/"cQ34fc+RD w(lԾ}Чn?|źyeX$-ʞ}Q ZZ`e>--onrN|)ǀ8yky:*7 a^.T˱"V͆7`.?'Q~ryEZ02o%o$"%c/Xeh2&rlM#g[WK` 3B$tX0U2S6оD|Q&V]a›Wm1^~Y4m*o,ʟZ$]:3?gIifdA+ m݌V:6̿=b2\a`AM~8^| l?Lzk5iN1F4i2^.YR>)fBr@[ JA*y@ r hO_OzZ׺jv |bu#@xut/f_ּ;/$3Y/6ۋE?J~K4A fdZK/ 9D=_lԴ׮ls":ԁ ԑ%}>Dn,bh$umwX Dئt2ۨrRSHq jj\%L# 5U}"#>í/HlR9$D@z8V*zmS_dh8rIi2_kGTeuݎM4Zt=:Ĉq[;w(wcߔ̢©C ? jw9".;w^ujȀ(0e{}*"S8Ņ8{C Wz<0+N[ PhAkdlA{-xyps-8U0Vlf܅Uq`z`o>ߘ&2aiLOը%,Tg6&7ȮvAM$g9̳B bx})_ V]Fy]MeÖI/r,8C{W:fO]@B#pzHkoUu>V_bM3K:‹4P̥\qAOXk*>% f8c8blhOs/YVۉfnj'Ǹ,9~xK)kIk)pCͪ1%v-'5sm5w(@Ua%8'5rsg '/Gb$7BsAߊ<ꢖ^|sE]٭n 3ßU{X0 zž61YaWE$iSB/OztP;>*VPG$ꍲ= W {^1)kbq" X)w *lS suy`蹲l@jO;ӹΆdpYUS9"u'ݸ_-KdAWa+DZ7rnjd "u ݢSxl8v2/7&Pa(泿it1@f۷UWgs<+V"kI>mB-'˵ tB6MfI<T"9jrq-˯oVd hZ_7<+0"J H JgtN[ λ$b,ǁOE^3pr%-!>IxRR#Nƍ$q>Q0JئcFM۵-ĿhͥԳ{3I'9m dzI61%~M@P{>6o~lG(Hn5|v"#GǫHeƭINfM5AvJ6`1w2.5+V$TH{v8k?c^-ddPr=+0;;2-Pg9y⃛HyDmbp7ɝX`bG{ dkRMDfPY>|0/TYIm&{E h6XeTI x(Ƣ@MV t9[e$Avns$xꣻT=N;ѫ'~>PظVM%,{D'7STjS]\f'/HpL#&ΏTc$N/EAm^ZZ( "UM򎅩 vkd0[q:؞u:ζ,N> 2Czzb$'ͼrsN:ԭ㔑Pcٞ6mn{J"ӯƙ3{՞1\ źDs]q[ _̍=MV,b֬o؆+&`|dhYZ.Nv "z_ם75 ^bxIh;CS`nDPPKXm76zR}Woqt"먷$7%)>2 $|zi{j4u">]t?I;asbF7_;nzo@c0јCZK߄TA1a.D%rM.¬ We $ F9_\ChxA#wJrr(8$zV9IUGFۓTRDYS'͌!žצxd DGctM9>KkTX>ޭָL,Bθ`h})1ɢY#uȰ??|x3i~dg㊑n\M{pWS~!K碨 {lkCkSwbSDu{ Eř9fY''lc y%ڊNb"0b.V[5Q(1X%adֻL̏xG~)p$E7,D޲n$&Oª&e.Y!Ί/EƁ#x*B]e</A\Ԉ3@Q *᫞JOv鄫cb]¿qsh1#^utx )}yϱk{ݨNAOʦgWz\\ia2murmNg!쩑kc 4 Z^i{svl6Gr)2mw#ڻAj Ls\\c7Xrӗ> T (Ym0)0i(SdwK#cĻԐܣEд76%bp0V>WCd5gte=A_~W25¯jDGZR'AtΡA%mTxΜf8q;B_A 0wgGeN}֊CtQф.7t5cbKk=bz6(5rU @ ox*|ܱG{>C8,ȇSTVXk{pAVaӳ2ԡ#?+; pE^OœI:8IP>QW2tn"`YǘQws'[|`'JpNJvAeAcUO#((Ӛ(r3Gl E)2E0*ٟ}S@^|ɞ8=\#j RfAa=6x,C8"dѱ}^a)U@XpEc1AލyYuh5~Jp1io\ (69 Cxɘ.4y,)-8VϦ]4pPe~(dtekkcYtBYQZg" *GweTY,ffk7+OÒR%6-r󁫇0EH( d$Wv#/ǝ7Ct"9s;D3:r!HwU'S]2n@.[Lelw[!-ET7 Rd㳈:dNC(ѵ:<7}R!.zRrbƫJk\pԣZWDmuſFԷ-?jJx[p~81ӝ#9sJsfs1Pn!t8QgoSZO?D}\dDȾVM.~$[x(ܡ_b -uuϤ}ľ9M4sJgB2HYX:5p [u#рK69H+UD_+B=M TڤU <6L, iSV[F$p4NN];_9 o"03"msc8K,"? {ve]T䴤p?2F*}l5+m ^z;rs%lOh9JƹwRgR;A~q31؃2(~e.:ig/ 5 wW#rޗuY"Vf𤭌#yekOD\FnHz*0+~^3$]ȷ,?] İ̐?~:vG3~/_ޔр'LAn T#ovla3ͤIIz! g|:| 37'סᑷ3~R $-X}xRS&SJ9reQO>Be6=cuySUt;*o%XqiN$,m_wXBM< 1dΕX.Cjݏ b+z szDӡ).s-wZ!W \Va5Z,urȼF; ^tnFs|ba' 82 r"yGcTDU /ᯯ q^>4{ʯSBpK Dݦ\SN }ǤYwnZq KM@j%ϔJάxL#(4´R|l:a@:;i'ȃe\`JӓY~>>bgL@\\5 r 2|d 7ᤨŇjOoTMOW(4Ϊ z#Z"HsN:RWV;- Gg*A,2 QԖ6Uo#a~.sXhKu䎍ql49{̫&}<4i-iRXf2/Ɠ+ht4 zuXMK0O0llEҽKWjn?d[-Vɹ-(L:G0ǐ?HVV!)ts5cv76vxsĬ@cc]Y@Lv 8koS֗.xO%E +@?($}=QfOB|? 6rј ]2χϊztט[;y2wP tiaUY=XKvek88)iaV̘^xjusFܓՕ#(&C71nA d/=MK27JNQa=|F3䣞oݗ@U 댺}q@)À:bE])MUHhpwA(gb5K؎1O8L5;6nQSz`̛ o@V#N0zH ;$rX.k#LƢzU|K7nI *_%*Wz@q (@ޟ¢/ttc {=TOݹ)71#7¡yĆ KF(˶2 ~Pf}mF14%S{(\1ۋz #$s 2 C̠X/465x-KY;йQHϸeѼ!.*;Sޯ2pRIP.HI}SHۇ3/"Q0j~%^ nYCE 8^R+; yM5gK߉!1.myLWaHqu-i*9B+OQ<&= V~5 :iХ{`Nl޳͞6LN#fHX&^0Q'?DIiz8 _!Nmv;ېpmӺ܏ӦC`2A1T JEa1Ӡ 3JVf>3|t80 _8A%yATow7ҟT$ 8;g˝3"mnUXq$!M`/9>f88:n;V/ZrZEwNai|"OMwE-ES.GI&>xЀ<0Պ_ >!Swt>#9E [Me8+$;9< ZN'lZ΃ - E'?PfAmt2x#_< PygDu}?KV,/焷&d?$<_[ɆqpWLH}d6f/ti."{T(XQ<%t]PAnWO81r6D2l7q2oɦhZq|\pu]aF^t_{ ׎ +z[ Yp&\!~ls̅g.UflxEKk k6zo)S.1o OiVQ(rfٚQn2pC O[N^LP\d)07Q\&ZOd~gqnýAKIj!3)?kfQJDN ]b8]VD3FyK&읟)vKVZJ[֭ޤ3b27&sj .W^t}iWځ茆Bl_q =\5vQ, #pa C)]X&f2 /hwrÆs,&#HL + QیdJ_Nܦȭ*].G% #ve7spA`rO,t6$ߋ-WYx)F7KJ4JW\uδ6\5JW=V#<.YVFNT&ED_‚|pOFIOؙUl\tI~vH UCQ[%T_kƘسm+C wF ҥrw-iG CDuX.zev~J9]8X4$*D0alZeQ$$_ WWFurx_zöհ ĎiF}Md~f"6*M ʷK 7FԲGLBa6; ]VsGWS& NnȰ3#<vw.Pz}X%~|JV IJ `XTy]J݈ZCY涖ȸ9wUi?sRMM5s}64ӯ׵ZX|W^/7XX,Z  ej1w4k$pmFimzι[ˏk]2@; Pu*9>ޝEFl|r8I&[p=`#gQx?^y \KSI}L7L_oDa6 d =Txz}Fo͜YLCvL]CV9i굆IWW)o}]_K RCK.)zAx]6ݺX/)KPm$K-8{ .R$X[͉ _6ẻ7-ݛLago9YU卯ɸUwyƫ|Cj$vY8&7_rU'e}3dl`6<ƁZk#U8Ǟu!)|j=!w`aCQ0;pŜz;=S~I=QU(1 4z3Gk ܈,EʰD]x1idST}SVӼOei D}3@ϴfUڀ2 =>+$t)a葜d?K`O[mՒx'*b]dߋM史iFPJSNO["6#ZlBV _JPhܭ+'!r4@YAZAhOP~Fe{*e7`إ\XLpIpptA'K씁01%ܩ^CK~3T2 l6'k%դ}^g y;pN=1SIh l 0^=?эR1370ĕCЍwKbs99 ژ 0*|V0 ecT&Wmq Q'CuǓ>.l xS[ _l邼CbGofPngHۣ]l\eG<;j2#.Ȓ=t]v4΍NHcH %ǙSrsQ8O&ѽpԸkRU_'@ 6CWPAQy,2.l_F+z8);4kةI#mO0L8uF89!~A(~cj7w[Q? ,uЁ{xvEW+&ԇfXb{3kO:2-[xV#9 4iwC o[K(u7 oVg)D1OI0'[2҉ 4&/F 󢄔1N d ob.Ωn$t0if8 N ,GRx/*!'=Cn󱖭ec.=g{ozK5Β#DN.!+T͋jsAÂSft޷T$h{r]M30zV2!8L`wWчMvE(q$&{9 I o9NI¡~yRh)+B< M7mo7շKha>ŏu c'Qo|ץkxK?ׯ C`'qܶT(NPP~6}eTĮBB[btyI7qbAu&'1z5xZ,AR}_S]y7qR0 GS'U"b_,BD<_'ͤdMKOj&p嬂3VA_9Q=bm΅V gBn^cW3a]mo2َ1E}hG{#][LI@h[&h\ͺ $ڵU!e!/ߩh- eOw7 +n`H)&^s7) NO80^w}=yG=LԓD%6vo T@}|&P®dҰ.|_ ",kZ } ӲNkI`l%}"HOrzO;ʍ"k!wPJq#Ry ixg*y^-}b"r1TY _=q7X([ɱ2.)}E`Se4opg- 3:7bȫ7.b`# lj u_W׿MZ` BF YZcZ(a/i Po5:(:U83 C+3[6`Uw!EyYKA ZT`9,lP:G%U٪pōTh{\,(z}ҷ76e\t0֟fi;th狓[Ȑ#EMI㽿koJא?ʚШK/,\1eXz '2(^+jE1QEosEA]Li(uCtx"ةX}J7WO_^pM.1eND~܊-ktmA'4/jKY3r.ǗXG8ipʭtvCF5X3ǗRo x ;6LxP1.e2fE&n5lEm٢{ `vʋr##YhFHhR7l Ur.uQaȎwI8ғ^ Kdqg\3[zņa%Uz>$09vKdQFgYUC[ ]w +3>)Qb }bB uK~;M,+̠FqPT%%ާrOunIK)~݉D:IJ{IC"9Y 91-͟Q]*i96PvڗbDF+gF_A &<(TNP*Y~po9^kRYq^kz>P1VUDnz ֢A 怔P;7bV~Gq2~>ABim' J-Ēy.K p#{v?=mҵ4 l"OnËJӅX.3:KR+= X)Z͎jn+juZ/qDCw83IvqjN'҄H)޳Ĉ MNIq!LdF/ZNPQ XA.a0hޡ01Ttnh@^RxdMcO7QxᙊKA%.d6UwDGkoSvES?\0m,ٻEm)QFk8V 4UտzwiJW@ݷLg0vF&bOol~|>new&(z񠂲C/.StLlW d;4L.9_#1zLGQ7_疩lFکakְp0!Pʅ$)ZBmj.,&&d/;GIq85IpC< C8G&O԰Sg17QjfK5{=-bɼ 6mWgלq[C>&-%i@.o;`<( O*g?3Fp.V΁ }qxu㹠+ ۛ}Q^[Stğ`溔C;u1r/`iDA2[ʳIjI20K$m7_}9rMAUMfF&`J6tukq8u4'ZVzuBOi*s >DVuM*>W{X<ܸ Np>CHzܹ{7O_A?x]]ۯ.!ϟ_Bم)ԃ ^Q 4:1w&}0TڝcZPۃs8b/ 4i2yxG*Wpa-0wyZ'y I"ʺ  +X;n՘8薺vlhpXuWD[R9y^Xs/&OTT qCQnɁu͛.q)mA/:rme솧!47w{3?*<"zQ{E(5B&o@omFԇ/znR5yUu&Γg'[0&*6Lⷜ~v,mqzfzl #[й,"Tl:4 4߅44SqT{!HMjMD^4POi7* E[#z_p]jnaԠcTr>u .lh=xa_}Q$/4:xG޵A{gPpo$s_XF/KUCzXљppP:[sS@Sr|~Sx pK~.\]b˥xu<~?or)L+Tؾ7*yjr,LGAkLCTUxb`ԻFwIǃG<8/k4oSz'!w :Dn0THtsC |masSvBe^⏅кQHx/羴& O\OSP4HRxokXGEGK1֋݁FNf_RI'8<ĐZnBqǼtQwϚ{Jsׯr˴UƤ|Km~{hU4;KdaVRdϰq Sfcp&3l6.KQ|b_T>iӑ6"5e!2tR4rE)|iSIYL dgez\9hcTp @Ā ?c '8&}4+/nv&d〢MKC e(Kiw wґN' 6|wA-MM"dmO1%vE%o~`S%X3VQQ7F)b:̅67Zcڑ:8Ҥ!e`ho 6 5_E{3hd)=hOYH<ݼM@(N-Jz:'M"f͂Uk/H(nb4w0u "̐a0G3\}#(¬/A`tb&i kbi2}-Vn$TkGWooF4yY 6/ D. 3ݺ1lɒ-x'LPX 9~-xMC¢'P;oQ; #x~7q6;u hK^P#h:3G4l[3"nsgyuX| c#2;~7c^turt9ՒL Aזּ|Z@J-M}VZo@t6Ϳo,&kb:sbEfPc/v?׆nɅIJu 3ޅ@⪠W\npf]tzøvOd~OWv^W}>NҎV&/bJG 3*wX Ȱd}0Kr#?" će&;8=CG۞I548j|-ۀ ^>wj9A gx,7ZouRl%Q!` &kFץ^BE}h>$iV?/ǧp{xZܐ7¸XQ$k[ÕCmB `=0)˪ŬX #Fg*26p,4>s]Ps;9@1:C,i6mb饠pDa^9WWFk^ØX͝p %~4hzOس$gZWy3ĘK<4׃) Jn* Cm ʂ#w8bi_P5l߄µXW> }4i$:>홽`@khЄ+̋nvv\VL,ш.%Y[K GRWOߵ8)n_U=oD뢠/n"wZzȆ3༶P)>^j8fW*]:,Q9-D? (aVW" K alFyJ@sCV1Ѻma*þF枧ap93 D<R< )zZX-bh>&]"ȵ|irTqj^O+$H3$*uۻ ׄ'i#|D9'SԄp}`Rr$cxdf E^ r f]:"yvŚ.L15O`X3fT0*[I9o3 GD̮5lEPA>«gK_~#S%toO 1Fc$j[#ذvjė_i~y g"^q5A\Ptf&Oۜ,4IiD(.E̹ѪZՓ[ PwT̯Ӡ UvIy@?m`%/)+gnȯ=/gz @ɠZ@>B뷰sxƽ&\6rK$t8(*|ӕL>Eme}=I@k}P,v ڐb]F rGbZ?7c I*a rbs܁EBNgy&?0C  Hץ;H)u݀&\gi{r;kĝC9_XJrzk  iks.d @Lf2n6(20EJFzB04i3rհ3C!S6 OU#/0\dzFr㷿QdMǁq,Ptt3t7lg"=gHݺ͗5dhg_x-I(Pc6)sY`geKx<g9xa!-=7 J2^uȔh  W kߏtܵ&f8|M cˏq' bW9s5 Μ401QK&Xz#c>1jNR6A׷ںAՈig|ZD<:Uq WX`"7tqͿDFFf$9{.^{hZF3^ݏdXZA*Yw)eÅw ."UVϑgϟEkrqs?+>7|$sFw͈#ME)=x$]M$=SY1VcIJEUQ ijSXv,8eZ7RŵBRCEȧhs΁{}FGI~1O1wxȊLgM88W] F(e:8~/|k?gmϴ%:IOp;s>=:1cGch 4 KuqYztâk"bv ڻ%wh@qt$|18ML:4/D 㻊>mj5-|v8NN @9$.:g}"֏`T(̮~{-ΥiC%\lm^ҎI639ɑ6k74uҎH|PM$uS=]er0c# v-C$@Prk^:6!e5챂EVB,lRhcXy/UqO0[Pz{AߊA1WUU|`HHZ8v}l?#8)ӄz=ũ9x Q;D7qmFVDB xO~OK* 4AϾl0C 4qI]sI޶v3r/2VA0#&=%AoLJbHR}*$XI P`&N&Յ>K-&s8D 6 r=B WGWq3Qu~"j(՛ZjԊA0RxaHˀΗUI`vʊ}Z*,Z4匪= z' Gȩט{o]IQ)6lXh5P˒N^k izjA,*Z@/B+I"9:7CZӝRhԧm$Wk4F4z+ <lIBCcJ V1m=? i{7:ǟN}z9D-Wi,#,92-wy <(4?ϯ0½|§,ߚRTI$|gKuIt(W۾9 ?+|BO0LŦ04YYC(燮.w޷(蚋_>i)g胠SpS2пHȂmdF>8,NZϪf݇´HmiCz̻oȸDV#q|)muſv@T;C|,i~ gYado;ċ0¥duφĒ]1Mb'Xc|[+bӤ}Q0.+dN∹s%wf!JTlwD&"uJ򝂦vc5ﱽQ*airk-VZ0g[p\)@]n?Q`yӛ*1ЋgI^42yJC| >|A;CЧa@KSI:u2 v٦ y(}j(ٺD~E@7N|j0n2֢U |m?N em;Gٜ*¬=6*!*f'x1Xe?X93-<1G| }':~4w;ag ĸl[ Q-WEڎOa:\}&*Y_Ht9:BUX])e,Sai }φ ,|= Ms%)zW? yӁ☤8O|LA2&(XǺ1*F-~)/ Np#~̫D`E-٭{p&* $Lw4U:Qbo+B #y~ ɶLA;Ϳf;˔I GcLpE:~8@!N`*Aqjѕ"oMA=*m{k"$njm"p~%FL,>.,$TioI@ Tz-y<5px7ȫ淧JYbK) h؀R'JcuH֌jcL=[1tL<'2i?Ԩ6j9ÿ6nr^" ψ j(a%a+nRqHS'K*B^:]DZ5*b_qq(fΥnH6# ﮷%s>)Igl@N*60_kl2ίLJ(G_}2$R  n:Zev? C"sQgXx6wm/θ V?[ Li4i%{ )s33 ;iu`ub\&LLP˙{v4nTvk/iqϲK'Y6"]u1ӆxoV@"JWTGz MThZWbp [gZ -[4™\cd$N\mIX.Z^%o CF9֛({Wo"?gK9^eM'tg 5),؋3j]]*HّPQ{a#nx5#@Fv+oKy*a"T.'KQCѓ*q H )gu.@295!LͼZZ ]w&*I˚_ǣ:ޑ<cdAY-$RpۯZgwܒe5G)eY֊>I]t5Nz>PYǷ12u;7z\ e yjR/"-է4 w0g"ܹ%`)k Y]rffnȇ5)P痨\JWc{ n!_'+3 x.?Q؝?-[Clx -fb 2m߅2d7!AH):[Yr\l ;NOyQuMY}S!DP(ǪkɃu9y `JZx".c5#q^[Rce B7fe 9ȅ/jH J6SaC_#z1Y n945È+@8c- ]c# R,gnqb5@j,w25|HqJJ3@L&6Z 䲐n"kpA=U$k[=a_ Da&X@/`{|w цd/^TwOר [wB,7_+՝_ l`j6(kĺ`۶ܛ>U yPg( T5j3LRijǺ]d+ ZGF"]y!4ϛ]x|r~M4M :ag-4jcQG?Bb Fv*ت7bu%t d9oɞ]_!QIJ4COkvW$Uwi6p'XGFjj| =׼ h#~xa~)G+iYj"vx|Ѳ4_'_[{8:dYBvlwGbk\+Zc{ Jj@Or17 ax5VmW/j_ :aDρ4un~,K7oX N8f̈́[@R:qײ4&Ԙ_y&#;Zx8%R67_9OP)TkЈ=Nh-IewrY9dK咷99O!HMy 5I=s5;Jӆt G†©s:ku3aY}\CC.X["-2mLrꨎ+% թ@50p%oųԷqvБΫhBJC]h'|⬐~VhG;?tԳv,dIK) ٘b4DcU:2>ET@e"P䳿5 Eov]KaE WPOy(@"c2 W.FVHW 3H"@cDP}#F?Hq^g.'ሊ{:&M @.2_(aJmJ{x#xp 4 ָ~L7N)ϠΞ`a]~H3*3,9CF,\ Gyp$ƄlpO>RkeMa{jy%?;G[5"2ƌ$9h2KmXֶ$ԏx=woNT#O@̢϶?ġlb%nx-7T(}|iN1#HR [pW>d?ŁTqglͿ.!B݃icq.kZ0DBSQlIz@Tv8+޼< yX մ'1Ob80m{tԫ4x• Yu+GFy ZgmW2 fz<+  y¦VڒN^>E Yy"4<C1kg2)ΎgԵǼ[LTOȝ"* )Zis3eP&0 +As/mR c?dɆE\dХ,QxN@y>D[#9l` CٞGuXg R=g(fZE8d\&jmr9d.ϿNPzH{Ss<8ma݇8U^E{$쌏zlQƦ_.*j^ j"zqآ/-yț'kگ͸߃rM:z;o&[I 8I>tkΘ'AJ=NFX_?ysЙ2X0;s?Maal\_B©_:dɞ#AYQ=ۣPz5Fo|]EϨ-cj+/IYh>yaWpti &$Ы;ݿ qT?6P`֏]TSԅU3u%(\u7rG hirN.My]YZՃLK|i9 -8̏y3ŪӔL*owkXEe σhfϪUɯW =>P,SQzÔXbVFRZ)Ru֑E5|U CԝڧNSc-3χZ)ؓikl/y?3y;jgH>' JqC̜@S!USb1_> +l5Nnk7O 卫f Z!FֲƯ6GQEtm<Ժ#aN7${Bγ"`J1Լ*E=".S)+LffS*>8[2{"woyKHF}(B\5_tLmIY3mԊTI2|->mTVp}OWa {{pĆ*c)׌w(0v edIhrXVqpj]Q%}V@'-;aGCiu?vL5V> cM!:/J}TYQ|*)$Vp!Gwr"m>\ 1NHsŃs'm-6U 8Y1lFj޸D, zC[) f Ы< ͽH_Z _2fRFeOhIqS{`w2[WMWpEpB\Đ1g!sG%Yd:{k<۸U]#1ƐCcQя׳@ o4()7O7>Xd#koZG=.vR_ek;y1>;V&Mj<i}B0L]bReM翏X jWF)(|yYi,tBp@' F=dǮWr.9z|-ӂ$4EwwH\뱰-eU|utlfhLC"a^wn 6kbշx9#y3 lR%[3w3 쥒xwMjXNdތڏj:Y}9Uŭ\{X h_,Ss;FhBy{hڸpel[:Џ.o})c pY8N迟0wq)1R*r]-^- m|Ԩ,#42M^k&4:¹"Sp݉sD_*ZfÅX1"cA71ًbQ2 {'OӪ[#LX#r!L1pۄ!@#<6ũG\B)F64`C5[ P G_|d&>"VSX p2'}Lotv\<8*\Dևz)Uў+XݮdspR3UkV. ÁZHYc[ʻ( L}L?P ,9jj0T87( C޸88Y ǡ坷~a+-/!v/=Q1K {f4a[y=a%!Q[(Y?O+:4 C4!rA)G ,L?I\7Nc<ҫ_} %UbI *6CC=MXm5FѲΣILr/axS)l# DMNt,Q!&o<l Anķ0D/> '~fo4@I4܁nc562a܇C[I.i,})RL"eSr҆rҧ! k>!ɭo.>V`:Q0ω^sWa?-B'ў. Ɏm*=Z|5Cck3;n5FO`?9\"0{؅NV2K66P,B~qvG\wE9_l =+H4!A#6:+>ܡ*­ZV vyWa_H4gf,AL`1}pG+re}|w{b@ڲB2ׄa>P*ZgyihQ!&yxLw>](pZ~U݌HP7 ST#fނm;aWm,`Ƴ}]C4JSNm E61 UkL07*ij58k>/.^g|Ů쏡߄U7}z엶`VpaWE@FEe:8HݖC >!mHgyWql F&'Nw)x*J-&m>}P%}lӤuB`"Gy60"}pRdS5:NB[S*%\oPRfz%5`fxJ̭?&W%H7S[1 ZPz - y=RiЫ1}Ќn]Z<{y{‚M$G˱Fjcy׍5PHzn : -t ; y!uل5Qmv|q #f[C9|$ :} 6w-K{1$wCGIՆhՙF3ZS.:BLo5>%"=q}hsXa ;Hڿ[g⶯yh>z&?(e\SRl܏Ur2 HaM[_س4{5HjM$ @wAd@r Ĭ/ ;o'IFKsi0t2qm<>'%65P*?cp辯 '9f)SKX- y+]8mYqi`u)_Eb6kF\RT5C琸DLZi[v [rè`OdTPc> 8!E%x Y{.[Bb>*0N0ůُp mA,j >=jEFGI4$mBq-h)XElS [g<ΫkS'HpqXIsPS=UiuG Hǽh;a0lWQ황ha,FrY`TȔ*GwZ hyilcut?312QtGПL,m#.Q Z&ڭ )A6sC|[1m[4)qr7%Et239G/VASgRfX,-wF2oL#0 wLf*xkD|ũ;z|2FJ[.!5ªTȡ>ꀧk%GQ ]*WͦI^ %bsX ^ 1Wx"[Fp ^ }˝;`eeU] s6mPL)yUL1%7 `Jrx.ӓh=Y6{?0O]FPrnG"8O9! $6fy7󂯛=S6b:/VTaDm[(;GMՂf<l]`mxT`6Ǜ(,t zJj'jӺ4H>(M,ĉzǠчK_Y3 .ye ?{T`x)bZIy#FT77 jw޾r*=T9Zqin-/ R6w[#FR -0WBam》ۺ\ByRcWEցZ}UxMxo'{Fk 0:GR# vh:6]r ԡٸ F{ 抆m]4F7)>.:8b-1JVcGU'kͰ\ dgU-k!!NW6Z$\z5I<+co ?2x mdC~"ic1^hܿR~U=}'md{DH2!O,gY 54HC9KYmp%GluM0њh_\DIYG%:7pѓ#VU%hk^(2|rH>Gr2AWJeTS_0vPpois Hy[^QR:_HX5B!B: $1wrǫBȉikA9Y辚O5_#~,FEZhkjT:0n f MZҒ0UyF=⌘BK0^qy&֔]ǂ$FJ/D5S^~6@m%Y ݩ9Wp+cZە*A^??i尿Cgn0*LÀ1̝ĊRWnp %Lw9W^~ )`Y\բ XnbMpY'S8zqZi#gf7$;$~W7YP$`\JR)ع y. / =H$o2 : 9*^E;t٤Cy1cGVӌSm8ؤPu+KkSWڂmjEa wE5KKd:p~H`l1-2#lN*NJ(2o>$ # S)*2x=}tT@˫7v:F&nOLc؄k霷`}Zhx6LN|O̲Ra}`\-xiedfsH4|BMv7B7jMAo1JsC<QדIHX4E]ZA-p2ЂV3"nJ^TtN, Ir lh\N9XB>#<̎NqcVb6R ;]*a*?Ҋb__tj9r >9H7\%\;h78rWo\+CGYХ;?HACՏ/!#RǏ^pg=CT03}tHRa䃠mMdy_@lJS6LsOI%6 W3MoS)낛P.;߿lC o1>$C_=i9x~g= zZ#_ Eؠ}5YQ WuשC$[tMVksM7'# }+c;++QWi"s]1) " ? !OXR^lXpp7CoO(M?ŽjֿUX-iPll"\1UgMW{;$<ڶRԴ#;w2qadHw\޷o3Je@q@e!ãV(t\Id\ne2'ԋI`-{32* ^Pڼ'e~|ZQ0nMOn/``iٕnήf $bVݐԪnOyf@!&6R#%0@L ,n=o#[ʹ7nQk啶38R>֤9F/0/<0nۊ[i̋n4߁D<k*/WpdWS' _A5C$Ab *21 [h{C_T5o'H5M/ܑ`ؤg BBwHB3Xh`tfŜ c2:,?xi5J8퉓?:}?t7Lo(Xϫ4A%L .{?"!:$x@UL5`X/ !p{ NHIeX1V} ,E=Kǰwݓ~;{i\c9nVXP7i8s@+^'vɻ8D^z x>K\CI0]lԜٷ{E8k~X.U CMy29%w/Nq_}Fx;[ګ;YE^s_q3-X=cŘ5ۀFlmWW} FؖQ4ȩY .P/Z}}Gd2u| ً˞:7J^:{XMKy3vZ`zUw`3Lc4H?[: ^DX#kl/gvKflTFsz5{:Xӡf"|%R90f'd5HgPtѺ~5U5[Ԝi{oxV JBaq ]Xqt-OuƇ-*҃Nsa>Y}je?|r6oR3Mo@VF7TQJ]͜ D(,Rvߍҙ^9Y)4kxEXHIr2' Bh|X1ASldItI< C n; c+卦oع]H5gFP=U-XbxeS Xo.գTy>_kF\HBx'.Jh wāpbV߁;P+xOVnnn_ JKAf%ηޑ^Ph#c֛c? [BA%sݳ ,.?Gr 𬚌 ;^/8%cc<IѥC^ى˫>BB[l˜.XXHEȩwb4/ T4w\g@ dK4+G ӫ Y`yqɟeeBWMcHږfj>}iI_Jk;:C!yHe f:3bV-\/JSNVͣ OXyOAˋ%R`,@P3I1+ [ QG#] <0 !v>eCעl3&_̊ˣƥ@rX^H듸N{R?oչ"B6Pq^[ }į.-oMTi00Cܲɚ" fV$N| fH]8"IJ84[%KG%=ڊ" .iGhK93ܵ`#P3/=CpWG|, * $qw|[ p[z)PvkyN3ޛщ+da|6ŝ&+ܸ.cƓ촴mbl `b q/`8Iy7d~JW@$&~ΐLħou7שfK A,XrE*֬ϲ6}SNR6c6'p yJQ-nDjs̥mtmX##jazm;Z[^Wv)Ok"1:W/ P _F?,Zf c9%kvh]}VaJ/mʺ*tIxlG G_7$4p0pX"l^[r97CÍEek+%:"#8 XS΂@m%Rg$N3mԫŎK_! JF .|Iȍ'О4FcTϥh,XUXŲ!'mx\2Ÿ9X.Eğ٬[ IJ@W? -0eU?a0ʳ9[wxgluw{eAV\ RVp.C`q./6hKwXРK+q{Z")V2HVA>DoV5[ܹ3~鈼H,sWvx|1lC.%p9-[ beIe[q߹k&*w_?]6hdb&yM&a nrmk`v<،:.G`h)P$ޞ~#nmH*eϬP\]d-,m$x v:5UToo0K[gv/6w9 x1 1YG4jFEzk@+Xl~kSx9 \v>_ZeAx_$k7S˗=sd͘8UQ1H.o "ֲ#<'2n:tY4_xeI33g4R0is޵(@uu0SLWNfzĞ$b 64yE>J?OŊL"$|.5ޟoւ ÿCҽݍučp@Ր kZp<ο=l_ xEXǷ= oFz=B&qwb8W)eMd4QI6k7nKmɪm Ld(=qX^PYqr릘rjNQ2GGq0'Xҭ,z)0I< :պKE/kM/ܱG2Ӱ":"3Sba>;;93w†Vj"gHR&"b0n4#߇†j,O7Q0 ӯdCܺ @~iǓ%PTf I=e&S;)@³_v*Y :c 2뭽혈i=ӧRJG1ƨNO KJ`5xJQ]6,1h7~?ĕeu375s [:] F~,*s)=|y}7[6H7 0Bo7K}0`o7rn^{}!} ާ׻ð7%@xya-9rkWx/5|ZMs>難 S:ջpx$ l&k/ĪLa_։'*H"M4?: n5+WQZgwbcG E a}4ζ0Eˇg'\E_=uVi%# ;kǨ#ɵ'qfRyOqдE\Cn6/85t1 į J䰳rdz,|gu0,/0,C6<=qc(eǺ/Wk!?`9!T) 077Z{qU~SYDKNbrxЮurf9lP!o;XƤ$W֯W3;H0єh/ºWzR9r}lc4s BP '+0>87W06զ|KSV0*u F6ȸ"iiM/\E6[SX e WsL <Ə_!7RbaoNj;%l &V?u oޭdV/g ة c#ijJɞV6DdsStVFJX.ZchhMmrki-Ă#bkf4V }d=CM)ib)x&{vK%\pj̋Hn|hp0v;m^\4:rQ`|aVixv_E(ηh7xD*Cy$IH y?d mg>}%5{jJv"d= m J? 🝀Նȏ2(דex=@R~~kjM$utܿ1޼ʃgU!J4e${ \,D H{*6;M6S.^3vhyyZ0L-| B _5O UCz\L}%FUWT\ xښJ%̊/Uv8vWbHzf}펃,[$)5/Ox;/yxEe#2 /Cu%Pc;.@[ ~w5"6KG0y%~ޡ<?¨bDMkF4;кu\"}%=T}wBqo(<=I} h1g NXG6kԻ H" tldsOqw%rR;ԴZzhۨdfH|LS~2 ܵ?f 5$BqjOA\|EGƱApX5bYJ: $8J Dˎhg<$NrD{(^\Gs5 0;\VIӗ:t\=d?>ohdЛE@@h3O nŗ'֥D=b&j(~`!~7.FJ`?($1 \-74.&Z۳2(U% 35/@H]Z13k]ɖg:\CB̊q!̄H~WĠ7c)|EF y(ȿ| }9$A"Aހ|55XnTdb2:Wʻke:/ܜO\4p>[sE = jBs4hqj#EW5+c w{ 07" [],1y2+Z-;Y&kaS?'L"fH\R#_?k3?9F1JX^*~jC޾-f+ 5egzj ߯3<ȣwvK%mA[a(?GU=w^*m`Wx6B!>4ڮO tDV\L&۴wL<#$F! X&zly(5{4j?HĀU硹8D7/( ƜvlPn"QmW]fafdXZA+j4wrfc*zKCZF*v0?EZ^IwmGvyYjP4ƕ-*mZoԻ^KBKXzGۢG5S?D^iǛNKm-n+D1"NxmOV:>iɅbkdԜ ei!CtRwsY.sM9?Аj@/NoW}>x?[REi*],!Pͱ\ww[ni}fUz`RX&ꞹǔF TI5\gm)b"1Mtb-md\|e`(!E.}NnW%V,k+~\]3=g>^@CmecyZ )3Xsya]?๻cۭ SO,T^NrV8i 7Dcb5U?377_*1A\+jdgJF.OLU ^5b2 gc"`e'PS։O7a2M6 J*cXR09TgűW}ZDC#ZdbnF?$>&s܂\0F&5_օ+<"pXQd~jtAnȶ<戤2Xř/oMTjϬq kXw.O3$j?8[l6O"A&PQ=p-? ?ʘE*+^*@W ' ]wqO33l՚dq.4p b/[*yBEJun*ck,ӡ&/ٸPANU{dz{ڞ.UCY巒6FQtӈ'}qQ*V%cFݍ8v(dzB쌲<[ݖW:^L_t8X.&r!Gq%[³ +R73-tx5Y}fߠXL@O"xyE'A6At{ԿzTQԡ! x u >CҰg Ɯ@c+WvFkHKcMeg18 vF,/(Bad=w5h/] KՔߵu3LcB!0<;dJr1u7 3㫯ː)E~#[:#4/ Q;'dA5Ac3@aIҧq Lҭ7ȼ .N=g"%hҝwt:sk(LaDmfJ6,i(x#Q w>@7I6 pnǣ ڂ,pD걎6{T)JC=%YߪC8v[t-nWKw7`!eiYuG~qZg](TKh?Iya2cP׋GЌi+IM`:9 ΥΆugyL9?"Plڥ`!fCjf "u*6ĥέçhq ԣt=6 UG;h6t^=ed뒤Iu[aha3La=*!#ZMfz?.KxI1n^{ ]uZP YƷQt +"L#C]U3@*UAi0x) "?Ye3;dCd.z+d^͸vB,Fo .vPZQVG=! ʰz҄9ΐq:Ƞ3Gͮ.W̓-TD5uiN=Z 5n#{6'0V5sX*zݸrK9['gm@DKg0F"xVY,_ 3#ύI;G@fVqQ MkG&CmZ ٹpaP2"%[ȭ1\L&b؆6L rBfq͝6I{#Ӵi`?d*n 4cMe ۿ`K# 6?% .$H 4haÍUc`Ny#pE"f2>zʪEsʑk*mWLMCh]KnF5 :x |gUųwwb(P\b+US&7O*E)*ύ~4Jڏ"ɘx@*Dͣ{3Q~kKT* 'b`f;d"bS]u´2\naC&|%2X@+B|U(bU-έFU\?2v<6L܋?!DguUOۨߠ4 0(YBTQ̯||'O7/fGa ~uit=>#=O!~ G&UV'+~&*ߝE%MkRp9ѬďzA~?^B޺ܽ=M 36o e衜XۊI(kb&[:1yך2-;. " ~YOL{T 7M  `tm+U${ 6,'[9hi#Qe+naFjZEMw Po[fGc:!<ȡ=~Lg~ ^_ﭒ3dΠ6%k#Qz⚠!̀5O]N&|:0w4O2ݿRb[7̈́%bZ4{>0n|VHEL)6˘)g+$/-hr0?pnm"ռ"/LmIl9h!0[j/%CVߤCk$1ʍG$b܃DEiGqTGs FFMXe Oz 5i2?Q( .΢t _VA0Ļ<̅E -=딨niLRQdsImSqg|1=(e78(%\OvC[AרC+$3d:E4%-dۤRk)Xy[ql=獵k(o?SL($4Rmr7/8:TmWݽ``p"*Q#;pg|ϴVIك#1im~7BJZ!j%!h8ohZĻc`< xFnt͠Ĭ!h*1I7r_ڇ]EgbEj #櫇%Xі*dLF BҢ/u*nH < 6zd.-،h=JY)uƹOMkEomzO`)`^ldvJxx,[=AK[)0\K'd|L:O-b8Г1i+dFq27YRsԏ ٵgYb;!Vбgˈ)&*OW \=2 $1q`P%pN_edqp}zAgc ~zB8L'͹ȆR$+1\w;H_̳ geԬa )w"C) <T8פr2 *?N]Ssju A:&)UTv 6A"MXϕ0͌$N)[W ml ~a.yC?2pq9XՎiZK8f}'0فԱ/Vb#;A3NbMZL k7 $CdQ%_-Ů/ p::/ oDs *[ah0'T@$F^'' g~wֹCPvNBz;eCgظduD?zGլM#$g剐%N x\}ּ@ceJͣ(hhѧ/N>v5Dt'^܋Q OnQ'!5o?)Av͹O d9^aD`Aox绺NgDRCJMÒ'N"L^OWD+sXv_b]̉ixv_ӛ> $:C,fl I]#fNKYvpݼ{"`GVW ly>MxBD|ueH/%42;8eH-astgR*Ъm3BW\`>$x>WJ@Ƹ#Ъ/J M)D H;\I]+>;ءgdeJP<Ka4 Ief}]KwnW9>dbOS$rY%\f5g7h@T6HYb5e ȵ0ii|څ3ORm7oLeYlйU-!Bx%Gg0!v@f=s`gPߺΠH^R9Vb~*s?t0ON~$?%#h'z#r|xx0A" 7h&Lx@:=l l_,:Adl4ξ媠 aCo`0~~}w/Ae\y8A֔.oMi3H?@YٰyPV1fL}EL-}e/1QNPSSsD!jELsʷ2q *Jү}XtZmQ8MLl!|maӋQka!XvYQ W,=r [7zQA2Np7KXEz jHDIh-A8ǟ~#ue_M ˯~` U? Ҩh&]{ _6؏CT1=GʾbN {\9Gn섂J:+ yDCDNxtD|,a>6Dٜp ؊ZxNVd9dF0:Q$b4}H|c65%Wx=w0ܗ/KNd%<ɵn[Zs zɀx_ѭ?Abf^f\pQ\ wC t?ܖ\ !A0g-9έ8EWQ~RzemSUB>=l*2veǣ kItSK6wtY3O䄦qtY'9q?Ol?;FHW]!K;i Hi !e&_&K(; ɥ=AL3._gt*s ̀6'7N93rO3LGv?~s2/3cġg)S0~#/ *Ѭ/.Xi}@@Br_Ů6/Rr5Dz ϩ fFg8r_%O)^-⻹kY`3x*+䏻Mݲ;M 1n7Tn0n3M6a%4&<}o}lk v#i6c3>C.0m~}ԗ <ɣnqPNl0qе}: pl)X ZFO8^qᣫ#$U0]3_]%zj5.A6oe#:_S)b 6"UMo]Ltϡ7 g~$f(? N !QK .LhiWq߈btCx%?+JaGY&ALts9&Ϙ &6ef e|i}5zD`tn&u6w+()V>-SW9Ń9H}&;3NVo-L44mt&ŷ*( VsER"׎ߞjyt s^v#Tfpʠn\;D1롿9*$č*Q|rffX0iK VݑPBlo߆D3ƌS|qwP~;a]!SE:MܠPqB>RhUFzYM铬HLuԨ[y24MTx>ZIЊVddYzS 9DX1%(t""pɫD.=Je6t/Z,@o6-"Z>u{f4o?V'EWjr!]R3<蠥"$6 R/J'Wbj^iv#?eU6CR͖Ͱ,9nvR LY6Dg+_f%ktc+÷TbEd{ߎ͏^ɧku5r[c "ѨҐ:/=㑶 c:bA E_8S{ AȻYes%~so,ܔ͊ /!(9JB"O!:G /`ggщR@]=lkiUm"I[<%S1D6 G}]JQd$Xa1D=pL =ԼOrQ#6SEYEatuo yܵ쿷CY;[s{b+S RQ#k(+wn>bIc~tMȟ$zin Lɚ_zX5/Ý3\H>#n݉8DY(*H6 Jfup^YyJ>ͭfKQ8D+iڽgCR_"W[& P4lHx||˴*#RC̻M\R?RҔ̈CKnS(Ɩ<Fn-=JlO]?f Mj."twlI$ N(.zr?oGn6hfI^-;RRHDQCQa|>Os~ua:|tPBL=@{Ci(?m 5A߬G%_`&x?s*|UE3+83bFXIVLog$Kًt1o]^S=3YMxBZZdc`ZEeE@2%>B^{i$c+uњmLVwXkgy!4|لy`D6<^Vo@)r򞥫҈h «L` =JSwѰ5]_auF?"XFi)kX6`OBȗ1k!tFp"RÝЊDZޙ!OYlW> 3!_[ԫ0y;v?V8GAo_gN~n )M2wpX_N[OA^Wgީ~&іj&J~FL"n`*$+`]=ah=J_gN;+PZ9HugXp >S Qm@te @̣LHYJb `$RMo"r^ԦP'\%t NxST_ڊfΌ6 v?{5F (uUs41-c}[,^3лe_)zk6z:|7Ⱥ,;Gb6U2z;}v & gu*4F*4rr=ޜ*;[ַ4KUSK턾vw[I|lZp[ RSD@h̶ƫ$3{:1n35d8XnCd]6=Cn>-|\D?(.KRKZv% ʽۥ E$+%A#y.{kM֝5=H9 i,Gۯ|=nySmI]bٷ:Kد3X:T|5E#WFG[QݚgFeyG.'9 ^4=QK@,pɲhpVh]<1r9)}8{=\hJ(Fbo=7tBwx__A3=()W0^U 3j'<Ks(4s+} O*eӠ0gBrc2RnIiח=u`n 4ٺplv`~*yN Se)Ea8LA{7lggN~mϖvsYyx$Pu\N۱؛';QT% t/ aeދ,Dީ[h`U{{e{Je* N2[}ONˮ5e; bs63"ɠ4Q+TU FQtZǨZDg6fl =[Eۅm&ٗ;%%vPv+L@}fMD h.Q\^{&^Q G\O-1I<80:W5lĂyy97LcQ%6ElE9MSG2> [ADn{Lej!N{F1A& $M}N;2x'sHj-Ԫ26E<<vߴ[ˆۍ)9/{+&p!.mkS3Ph0a+v6$/_|r5*##[;'3֊RP֤QvA@J'fD_3bYTq"@Iq6=ujv⛜(at]T]{2A`3Ѓm`01/pE60|p% m!75ğa.hXqLEW.Ww tۉqٚh8ۣ썰}#H K7'9&tNftWc/ȧmqǟA}:Pt0T2%w]࣪a]w!T+`^_o{3YTnבeKi2+?s&Ӌ*IsLĞuU^&M)Ylk #ڕhNNK lbSN;6?8&N*C3D<أ;yWzN3Gnaa9X7U*"~/Cj3/% !4F:ꞵ:2VM=.zp!dPY-T Av;kpydA[ͼAbx [1ߣp,x?ycK3ݤLu} γ[Wv˧ HtȟfvjQ9 ҌiH9l'w5&n.oˑgTJmsb8Y CҞߓqMO8VhU=Dp900N0 ' eH[&Ͱ{s*B[9Dw\(w @]h͡%Tڌ8Ar0Y/o|Έ@DfxOqP l~+3zطTv/:^=5JvYsh+b0wQ™p"Tu<\Y΢]'} AF@/;Wዉ!:OIwc6Fʯcjx ,=h 򲲉m9)%<1\xD,FK-}hݦ(IHw7=y yZNt\e9m5N pn55gÚ[N{WJ=u.@S萾~ Y K[0 4a{jZ ->Ӈ1;UQufθ]2_")ĉ:eC[[5'8ջ!/ #jjZD{IuZç>ɑ] gf.ѧHLσHyo{gw|IK9ՇhEUs q(;)"%jTbA+P W[h nOUۖnH^:7Bq| "Nnܠckw?ဓ{yV77}kE5 m*#bM>pEĖUS)n]'~~O_@_κBnK ]嘶",&>\w$yP)j`$ѿ;u?s/-'ƍ@ XٍSVhT!ߙоj1,w#nX~>U /ܶPK=wb KuX ~5֠ >Uv۰ )5d,˨ FΙT? ٥М2z+1D:}%Vo ։OĎ]6dLx$  48ƣ`4n0_G:nT; :6T  {FkAhB.n9Q5 lGd d@e|A^5쟒iVevt㮻4Ç;}+EArCY>'HeU:vQWV 6xQrbwzjc);GtQ7$$rHE{^R8z${bBs[4Ȓ54B͸a/ d|͐,ToI;Gm D6~ Qgoq#^enmݮO#YBR<42̱Tv'Yњw).' Z8 MSF#5yOlC°;(]!d#pV$U`aHI0fj%'{9m? 8=`뒯;5Y($V %{sDŽmMTAJ>P@% pŲ'+J{ǒ`^3tqE >P033dkRsܟH)Ì$qIrhT;-4oUɒQC+,ZF!tJB#Ù5LkrHЗbrԏ漠FbT8UŊ]q.%ݾx'Qi& 7JG8\ wQ"q{|vvm<ϡ@h\m!ىܷ"մ5"]Զ^i/S/~!JukQ6'J"Y!n>8S:]LM)e{uѯ)ռoVoû.J1%G Cn`rTd&Agy(5lQTI;h_N {duoIw_c&`0RVZXcZG&T547VdwUE5axeMYGox_51<>" ?ͩTPeA esK,KzA8g=6^RgEVV2EB]՗>PvZD|iGpCP>iՂNcY|TҨ.HKLr6cegb!40Tv xpmc"д-Xl͚r"5~k?{>2ν{Qر\xН6Gy,]K=t$ds0?p*z\ͺ3\9>Ms V.Ho/I|c'4Ea zZ jN,xd0Q ,286?{mfjK "sz"?4cPE>e,E,wl @OLb$Jꤗ( 1::]_H괵2ff{π )H9uZʹbg.SڋaTABrb(g5 Ҋ7k5~._*} IƖDU'qfR_ <#&0>5׈rQ5uX3;>+)}J4M0H va?-UٰvPk̺L5͐6Bkzd/gd6hSLwucS<]ygw?w'C\IꐾҡW713U2n)Q39%L?SS'={WEAu=2v2 IIN%uy/g4:ƫJ奼j=[$*Btp teSrn[5E}x)IOz!^d4廸8Dמ DPt_ Q<ʕ 7;JIlh dZ tzA_lrgJx}Ķ$Sn~=pƜq ҊV7 R,kFVqŃ Kjk{ On4!8Y zE3%9?Eis/9˂TrK ߘ4Wi^\Z3Df#"0#ya =x7,M$M33njVGz5!nY"\{K6D}i֋#vNp,Z V'THp% "8Gxq :;KsJ'LGYSу/o}iB!JyU0x0Ak_(6T "&6r3G&,f\ӫ:A$. 2! eq1 Cنf̂/VhNZ*M:M.sf.qbcb=6q)gS:-Y/èчyI_wZӡvnF3s߮d2A%cVd3@t*YgPL9 f%nA_;vOK8mq߻YYzg,QL+@a'S1w.T=̌OC.jf 2\huHO(L*mu)rE^|uU黊{Y:whU/nD&zMX2xIü2YwpU.ŕ 3N5=RK8xcFputƆՀ݊5~H:&l^"-94j`0CdF. @ӌ޼:ԿRg:17T, @(ou#Nz.r,ƔJo-_]`P`>lK*oiINemq{UlPZ/:*BP9,-B.WC8w-8! xy>q+V VJ51O CEbJfg0߫Icv1ecj ^dC'H*ϗv)C BD& %ڍCyj@9+UyWsn zh1<Hf$IxuB²Z}+njъɾ~Kw> nqA/lg"I<0H76MP8WY"'Xz|>OJ3oUŐ?Y^~'w9m.-0w ȻZlIP6,UQR .}4\G"QX.J-9C74$Uc>agͯ[[/:jxW5vY:V;$ң1:ˊ/~3-_*ҙ Kv(eFuVzĹljƴ!u ‚Yyk6`۶Q"}<GY`.'ڍ/r.,LI=L(T#c p"Ŀ[]q3cm0PǑ BG3Ǭ64s_ߟo0*1;~u@>p|~;h;' D˴>j`ƿJhtǦO48]|tI kvH0~Ҹ{Y詩GrE6-%oOݡ7fL5Ϸ@'ș9&gRð >Ic .ƙ`[|ݦق>*ÁψÂ>Es>ʂ5:pZ mS]i8!),L'N4B25D_ Γ{.vƏ<lhgv}$ϕ_A~O\JykvRÔ:|Ĥ }!o Ux[lB3SA2#[Z؀xFq9 =afs !`: SDa9C 0i=B tAѾKayjoG-fz>_ q*Tn+\dS67(ќWzJ~7/˜jcU3eV4%j~/_k6JҚ5W餺cK-T%^qpVWOY[)MgN<OU<@Hꕣo:U2bJT0|=n27^gƼG.|ke*Vyv\rK"-D]L.Jy 7I+pWgw,M 4 E=2:Ћwcj2F=˒^낑ӣuLL {C$ {Q }sݽ@n"vL~b/tNrUqoz@uBQh{[["Aʠ++QDY:PΩ;j&q}O]GǗE>75F)?.#6j1x '/  H{YV1qk#AO:?< Zlf%/*q #zioM>|Mc~@ס)rkҩ.R_M#7^TKv =Zx'\xh/\Oy]>,IWiDlܩUg,cR,ĝ;{H/Z&|2>Ɖk{J7ɚi0x˭Hro>+גcQ%!Y9SO; v:LBv|V5-sHp0!uj蠖Ci+E$R1z N!] px'M3fP'‹K;vwrFoT_&)j (aEi7dO2{e&İ#dJIsI梔= hAJ9tM%$G BCܳWvUIBC)rJ9΢Z?QHJ HhZ^~uMINTbl(ǯ_v^7em)~z#\(-h50J:i*"JrK"uREWҊ{jˏ))rTFҺK6^.v%mOiI'fEI)0ʳOz=ew5l|iAWEO1S8K0 }XT~@ٻ6b F$j&,u3{Ie@`'$JƾzW ߗ];? JNl- ԽCU3 /3$kTwLmҸO;m*]53O3C rMl*⦉Xc]<8a$!"L=T|j ea:WپU2͌u[! ,yU&Szw[X|=G, AhB$faT%;]2p"b՜) !o<@ ԣ#RV\hdujv|uW(Q;3a]Ko@^YN]ڪ&`y XDmMT)KdyD{>|ҕ%5RߠG"#ZnJYyT"[xih, _J.?hWª, YTNtcMP ' -^]NP/nR,̢UV5(l M̤y%UevV5wK͠yŷ=A{ lf0NE N*La(':aX`gVJ 5^-(`)=$YtL4UMp }3OݚGiWv2 o4кVmLla< {l64`|0vOI!R1>GPCJD0VCSb5<5Ci8_|8=;8d³g2.b>'\Zڇ:l}^|2.r7EY[_fِ\dlszQT{(q;l_3(%$fVS;3>$dŢK`%oۆ;|xz9s WAPFG͓Zm7LBˬ({i:(͚K/4 YoX+Ap{4'R Osi KYܽ b~>1CBW?8AA>q8BmEEb2;,YQCuyyLs,]u#2 cMF-ziiDvh5v> QIxh c/] gs^l]2-GRGj|8+NZz4d\m,e/3*iv/?չ[ģ]9"{0;xe Ph8g좉z2TM8&a }'7 3`(;NZKc(W8-?3({!v!hpfYGZ 寂1rnv,mY-l?J,=>9XRL.0*YxMǝw!IPZvǫA)-:vbe]$L&Dw}I_>W nٵˊRMd,|b+쐕[vB{ v=^ɴ*B #̼ ld/HCT}O$QяPh7洳j)xa\s/[P8tנ-vS<2p/=,@sh7&Z`] ;| TR~ +`@ xh:YH *@GJ^G$yKi/WTvHh-]-d~- T5]Y܌v$th#JMLTǚ;Ȣhtևom/3/άda&TI-!X1MRωS>ݯ ĸ0zWvYEʙN?Dz'3Xt10 ;ykb;)\kKWN_zt/j %,Ti6sjF# F_F-;'>w&V P6rᴔVb4_XV.AtoY&;7a_S 'BՋC$LUW ΗjVu]Tԙիԇ,\/;b܌@Iq℩Z#&sB%yW ]tYB֤7O7 ^!i@9!Zr#zpM#H&>ú¹TnnP/F-Ǝ^%tFQlC՟9 p8Di_X_eӝ@MI=FgH?PXv3ZG?Qҿ7*6*(ɣځrxUWO)Ä7 E= fOSU \`(r'=ɆKn>TNgƄ3(SvDkk(2UZ<*a3\퍈J'F,CR=PLN:\~5Y@6̻N6߬ʒN,l0> Zqcא+s;gu*TfΡm7,k ໻bm k- Wq8Y8QєL\S_G%C6#z(rua675쮽kݕ,nS*r 3{!O@?^',OdGySNJV^ hq tp̔ {3 /Y e_JHX9b'>Nmi1?!#%Tr9I::f7 ٟ|>ke%Z!tŷuW+( m#*?zmE7ǨL͎w;@D8twsM/ʬbjR4X6vLL&¾qm {@)̳\rd| {Se!itN 8 =^[e#A|ϩ)<*:ܗ`iZOpހi 75B*F߀>9앳Gk NNr}'5QW"b FY;o4Έ`*pw/Of&@XD| cX@O^l)bU-Gм4kW "H@˘h'f? \MftOgU@% V,6h[ |W27=я$Y$teLo! jVy6L~ˑRT1*_mG/a(tNqC[U=y}٢s3 3I8Jշޚ'=@'Ƚ4h>!>e-M_X o;PM0"ةp{hosǂEPOKf'}ex{P}ۑTWU|Y?W"cjy6s0NYoʙwv`~ozl6>7CZzY;w6I9Gɖs{‘Aqwt9'3Z; =##*t0@^f+rig'L#}&fq 2<ѦGaܠփ+6]׾φR3{& yQ%'M*CZ7)Q\Y4.Z-yK=bL H`:Ԇ2Oտh >+uJza\=1' X/O<$} (hTǁXCh4;*/B4er3GJPGG1R*'üjjfe5 T\\+)<8JG^m=HJuУw?n[ zRmD.Hy־F@p-O+4Uףȝ7I)nž15Ԩ,&NI9M?|ײ_R'h_SyR^#|P!<׍h۟F{Gf`%1Ipm$`MԻ=R xhh© ` wcɫ LZ[+ڝ^bnITw6\_T0ވ #6f]c0WCHs/GR4gF %F\&e[j3TjX0gzntD+YHRdDT{,0Td:c޹Q'os3$سa@taYdr!<=zfu\(ms?61ehyB1yt(oR;0;NNSbyș?q&' s5hE[ƞ e)1ٝ@U*V˒- \Z90Ᏻ3B6FP)ۗh/OUp#D5s.D ^/a{:If'!RN.8s}M{\$W֓,T6 1'MR;1a13dMr"LWr=ɚt8I%J?Վe'6U("mQٵvs.>`lwCkH+w7Ԡ$+p pӯf2ߨ;Uz,>KݡT Ra`#~[٦҉=4CVro_qfDeߎFV,!l9$ ;Z [vct&d~xi%c|&#$V(v`0~ٯQh[µK*wV0 ƍѮ\pNϑF$H}(UCU yO ORQ\E.{2'6T2F ~ƛ.q,Gr hWNN1vrDZ=%JqygXu׬`+Ȃ.MմyһtlI ؎50( xq='Ga7M1S0550[dc'$X 5՝"[g|cY)$a Y/ E @44CqhCGN5hO~!=U[Ð;ʸzЪ.g8Vs׸^" =A#PCGK0i/nmYjIuPTZ^?6`ɼ'Ϥa]!hs#{_%9NDAn .LR\ 29Qn r;~HX?Y&hpZϖ:ZTbU_lzf7,)8%|'H2l]h=vUFT"5,et?T#QV[(]AMQ.V<83z߉Jg -M2ݧ1i;.L?76j1$7UGuv0CG%.36DnJ7LeFBj;[?ʛ$9~41nnNY)O"?fY )[T#i Ehi/$Td~ ^Qg}ԏtmS {4FB=f±G#otj)ڸ ȂH,yN} 3`rN6X Gy(aG_]j1PX ڂ>tEqQEoHfʍZ [&y]Uh =p=uRb[4؄G|]_*lnRy3'|<e$c{eLey P7Ua7'T0FoZƾa͛#C z_ Zށ>䪢s ַ.J hY#mɖgN0Hç?V/F{1:]trQot &鯐i1"CV1Σϗ2>rET6^mʣZ+lLN5 6;_9%E% D?8]LA{F8E Ph"bjF2m.;8ۚl~~ڗ(υ/EF]Xu|ޠ.ҷ4U ˌA__sLӶfo]sbA"/n ib9.l器sw}w^XaFܧ'7uԈ'De_ K]᱑{\? +4qvaq3t3;V'g5ukףb"ҼĽG:&wgIo/FmX\%Џ/4v'pHƬ@Q<.Élt1e5%sG5 '?@yx(i;UiEH(̏zg19Hf[]`F3)#`9\x_žؑIf ռJW+\a`TLzR+haB !_;v1,yټOΉMaaZ t5u Z4[VH-BQ_ח:"_i$֐ mjt*D+xKε,@"Y@']zx .ɄzlBNMh Mنtk=p߱}68Iϡbc9-!(/5g?7ԗ ta%,pD;ooϋAB\¾"&im_,,}CAc@W~2PO0BѥCvx6 M,|`Uxy8:G[]vɱSmu@<]6-Af y)ݥ#C#!W5H D@/]whgo.#]Kv|6-jA avbXiĮYwXcU!.Sn{f;4)wx=I 4b'T F)9XuRM Yփ}z<C5.ez ;)^̉wAVV㐏6TjCٯG`o8] r^6ca [ۡ_j zJצ%l"IUm`VtT¶>5~XQ@s. yM:ӸZ?R1b46\Q'+MF{|e0{f~؛o95[N'К9cy:5y A03dEHz9^Kڥ cu-HLҗZ"%Ơq6 ڷIyB? /X01 ݞ؃ݵ`v4*r@T)mNce\,YM3>9KT(0zU$,Ha P{p)Sl@/J ,Ѭ1[qoxSTY|}-(3;VV᫗z*e{e|/ LCs>Ot[R_^,\lG{ItJiLGJKAnݚ{G菆)!g&pTb a*xIIݵa0"뱚ŀP84O"O ]95#S;6Ԁ6 &iGa&y| "^{ *kS$Qw.ck oOqka U_=և KBJR_ek=)XF&ኮY'b22fXKfjx!F! 4FT͏^`_t;lqw o /ieAI&P4Ps]fT!)ͭlq+rٌP{%O6RlN丛2+p3% r8_f^L]!7ya)g~c2؄*[`숏HKA1m*h֤ɽ$ޞƹpg8 +D˾6]#$~OxWP6*ъ 9y(A>ayM"-p8u/v{>f/ftYIltwKq_jٵѤ+I[1!5vlL e#j-[" #ζ#W ̾9닶T]MWIMGORkAxkuyja|(ܓp40k2K$iNI2t !~0z\n5֍oG쾼 )hFfx%3Yk 5r<d"Q $֐}vY-x|!?U~;i!L ŵ 222{9O6gWs_O*6/=vcdkx`nJ~^=}y\Ů!.miQnD*9|I;H*eZrt&b>?e#l&P4,ǴxGk,Md~mi93 TuhLTC|됯pU@t'3plRI(IkjVx'pu- Qmq++f?Dz|b[O<œn'f8!k{)}"1>%?Yyun)y;RkH 7rWCm&O$NzUvۡ%f`Fp̒Tyc5}3dw.$vsh0H4Ql7>l?vWǛ-w{ wCAa.Hǰz#gtDqE>EOyGϐg cFQ%( G[8G">Z@ɠ!廓OApĐ(t1yy_o_[]v:N:V4_euNNp#5.c8G9=7 azkbəaՠȚ(+7b__QiH&WĀ r#R2 endstream endobj 82 0 obj 220400 endobj 83 0 obj << /Length 84 0 R /Filter /FlateDecode >> stream .;'jC<BnuHt:WSxGuMO@X;N27ƤUoDvJո]zC_ciYo+26ͨogC, b;Vf+GJN{=¶*g+w0Rw;}K?.ɝckS]_ak$W">Eێsy l9cwjJ-&~SZu0 " GE!~ urrцƼrYbY}5* %@1]|dƻٯTWUqmr _@~2KT\tt×~Hީ:,$[\ZH<ґȕ"V!Pr}6# "ww귯bLXov[BLO踲4[$] endstream endobj 84 0 obj 496 endobj 85 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 83 0 R >> endobj 86 0 obj << /Name /Im11 /Type /XObject /Length 87 0 R /Filter /FlateDecode /Subtype /Image /Width 3000 /Height 1600 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream Iy!64ޛDX>KcA;J[gLNI\͙ˊDQci2^_S7~@rɳ`SqZz;kAtq 8!<*`jj:u'8/co?;No~liT27'yYrT}? j9mΓWKirXAe"_a\E "DnzXp5oR & IصJ9/6L5{~hB:!}:Su.YGã4PHxQ=6! |An?L̨cחX#)sYe=3VUݱ`iׂf +uC^ӓr(7R[>^3S}V߸1}#JF.KF[zT,} G2>! 2w( F,GXN&&Kn(f+g;5Np񗧭f:K/Rj " 6PK(\`WRf@qHb$֋H)ϻQsRS͌Mh" v1< Qi.A fܵu/{Fl鶓|]҂5zFxRh*o TGWFbUk'ODv!-.-0qp9AbOQg/)](4O|[uBj l°4v&aBx!$u6Xw纞SdHO 'HVik#}e[OIja.\-j{#'(5(*M5/P|J8rl%g,xhC˺`jOsMmOT]dK̦t4|]B]R}^76D AK? J5NjOY^a7 t(/UIx:why /v,>Wl o ),Uu~}RIV/*N̸3ʆ`韢#{ K]6zFR|!h`N]6ŤB(FFT6­ p,D*pc"0$e @a-%-[IxJ`y2I Q ?0u׭N]0È ?uE̕G x֒Z^_S'@r3 B;8.R  KS0WY2g"6ߧļrƺa`tITߣ9!YZ O"}c)z F%KqPQMo {r|ϙot Eu5XL|lB9@R!:i{3_)<m- D4eL+֯ Pf/[1 y1اDƗX : iWb[/^{b"c|^دFd^*XZ\DͭJ'T_a?Ax}z !ƏZrKbw2=ωYN^;w4=% *~!O($'J#axrs@reiJ,EřzOjSՓ1b\!v9; ʶFӌInٝ$cey$C`Χv(΂ 4con Kuf1ZԐ 3&f5'AӚ_\!k)cs[H:/v'[ D8 uDNh#qpg|iϐ~S }˿+6uzXax} wS-Vϛ|o˫4 Cz/A\]#}6"c]#4IBOՖzPKܕ7ݨ &,xCٕ᢭pv+ w`6J ,cH֩`@4IA8,"M>'N30<05N_V(sed,qu43XW}P}۩!|T.iNJ;m~]NzJdEU0_YY Ș*V$A'81ڸ)>\?lChw5me|k&JjJEH?[l%DK:$zQPsfqc&W7c|!)we9$ S1C{)m V !I{àqHX8-N 2t0&(#à $M=-hT^G}n`,A'޹EqҙVad> uOou_P\PtW%俄q1CzL*S>G-lr*%hV}+Qb ޥ3!)ubL-3"-!'7&;UŠ-%P&uzD.5ݙ݂KdyH;g©UM>[e.?i A5_DsDЬWb~sMm8ZT{`kqY/fJm}_|꠽,x2-3 ^~BB!ryD {ZJT1`gy*\tv98kgnl5*Yubb杁'& <*rѝ\T+:`>ǐks;DKL;`ݍ˱`!a0AF4cB!VjmG0q &%76!}Zl][hPs6aS\ŒM2պ'Tegx,o@ѥ`?{ $/E)f~_6 Ǡ32TW~Eh^;)G^8 h# N ^«!ώeOT}~ҡG @pw #Zp^M^l3tY$y=-qͥma`oPe 4ڒniE ~VR6U_3 k3\͹QU~ V`9V%vۘ71*xq`=c|)9Ҳloc 9*|KRwAfIzkbۺRڹCP. Ce'X_mj‡eQ-(,>6SD{\h,Uzi)*rCG[Q l[Xf9L!AY9maedٿG[6HTRCLLNa֜ݺ%yHUhqq=h."(U)=11hhswh@ O3&=}Nio]dg2=e/ZY >Kiogcoq|IEU/LC٤)dByc&N>QOnQͶe7UQgqW}q^;yK_bH Stc=u^ꃸ 5wXվu“E .FB(*r9dXZh\k7wC)* t>wJ'D?]Lo֡*pGE[_bI'Q^(ܭe*VˏIJ,ɲp7;:BTWT#etZe,bEYE(Y~+f~9}Buje/o@tfLfjDL^ sh=҇1lz 2 e>na':#:"1_{2x9zj$ޯWR~,ӘKO{?ZXS,fΚ&77ʤ_HMGz&Qlh1V$ԩm PV-]-IJՍmyn7|e!hlLʴvTײ-(^aNj\=]2f2g_u`  J1Ò tgHOĈ"KZb09 o-m h3̦]κj0}^\IcR7UYx'";aټn4`dh?mJyQsFٞ(Ew.X4uѸZ#umfd4ͣ .7 T4HH3.vV9_ddZ5/'hء vL9 [X֍O)ɗW&%m᱄,mSNF_ t6bsx;%W/L\Uԭ )`Qٝu"$'>ahc Võ\6#A? FNJnrcZpx7q^;cfYX>z?U9g4YĂQBgUhg#o޸G}W6j##  Xҡ9o%:IkI9K6tӺiÈLype/u%hRq fÓ&e>`z)pt'{~U?^ݱc@Z8^4֝V"jqa+d7`^BH:ٝN]/t(3B$CZE_FQm˙E د]y~HNnu_jF'w#O fƪViV<31cMOz$?4hm!nt, EtQ=I$% dfE0]Yjk-zdK"9 /-X,f3 x:[h /V*Xۊڿ]fh'kFWWn)Қ&^CQb@aZԸ`us8+~ nd1FD|ZK`} ȃ+iW[!>C+ѩHYɐ:!7 /]*Щ^΍ !-;K%,N U >Lhj)l1fOpo/D(*l.UqǗI穔bzy NmجlCwB|pA'=6mZuP-oX@JNބW %c)>iw9KiA[]݂٥V}i5UIK:&G8,oZwGYnˢ󓒵|+g.(9\/MC16=F[e yvA+i.#g-)F D %ѿQ QyZ?]G;ȭxޡgb+j6/啋9_3~X_/vgvĂ^ץtAƯDpwkv+vY,8̦aCX75' B>\SlUUv{%_>9S6_~)r"Ihc~w[VëeܯLĝ# \:\zU!1TRǓY4pa ϾlkԆ=%VNDV_,KТB!Q$>+ΘkׇSHc 4|ϣn6=ht ˅QɰiHG줦d!@Ach(G64X]#BʝOY9.ji;-V(jf5?~B{^!1"ZX785رd'(%zrPȖ•9錸bw=BN?sm@rW'2E4c<Օ1%`nm{*WCTVEԩg!uMeɾPPu#tCF25F_K:됚ro0Z9g8@{L)VEy2h=g 6 ˳$3ÁzFSt\o7]LjI}Oǎe@eJJ68DyȴIUh)Sl:0GsFQD| k= d5ǣMd S03T|<扃KLc= 4GmUA'9N`pܳc{#7}q# tgrWKG.3R^ 6h(1n FCy2N1_8$u)؈;xe 8-"lry!E1!;Ii%k/Ԅ({}7?ߒ0x|.g)KlD %gN_'Be1QEekcyA\^W ВDZo| "wk])Z y>S6 &TQȈhiL+N+u\)a, e?6 wwQJ !<|r}` #X^}r"|AlxGB[0{qh>]^Z95۶a(cNCʴ/ڃϞ錇 ey(oT*G釮/kt@2|,Y6# -s66 8'+93?@hXILg\ l~,^&?a*ǚNX|I;]{C{w8QoTFVn^稢x:[_Up3 jx/F|?=u5Ylz")2E#u{qIpjhPJ9͙z]3x vEڌROpm-#Ph7Nx7 iW|6.JYX sd+X)NW+nw{HA81qv}6h| +-Fԯ{iM} "T g g=Wt 9EkǨ[5Kbuyy M}Pٗ Y7\J_#k)*!H,>29UfCF׶T<o@ ?}9: 6xA#7!}\ ?:jw B_tL:_"G|-djeiuTGq٠a!}Z]ƻ׭jJ!U9b]N}d^ҊE//BnSeoBȁ:ִi i| `f.LJIF.` QVg'TH8["౶vwIbxᩯ8!j;ö%p%kj1!˨/ &Τٸk#IGM<|us%)Oj-H dahc$b2>)DYd‡rs >Ԧ9m1ArtK:oa,~$W-j9Cn~:n;f*#ޞd>cL6d-^+šd0ޡ O{j $ 7@g,io[OB~,j'EJ߳,ZOZIS[.gF4taCYB0T]N[ OѼ .3Ơ,1izL"áNrrO@ H!&׭Syehޥ4UJ"q5Dn%.- A+A}2{N K݇6٦i){Vp('j7Ꚍ蝄 W,|73aGVcܔ]yv TR~ap!lg@I 2XEO}Fab$=*'!G( ĀMc+07|L&Xi(B='[*aA!/԰H-s3UxGy Q sK2r|3ų%>,D 4؀jX0/e|JE3OϺ㆒R(I@q܂ 3}㑎k/ʆ 1 _\ĩ3>.;&5i*I.ߍveD.o",R@X|=Wo!(L.*K=!֏N}h/95ܫA{Kb yaAcR/o瞤K&[l7w|87"5 e  G[21ɂҰ% :ah3(\Cۛv(/xuk[""sJly{dw+rI/ld|fNG,z^neQ k;3BUj+1<7JKŤqH*TCOB?F?YE TQq7+b(xCMkѲpל|q@.K` w&l!w},Ss7ZQO:+-"3\bʞ:Lm=wV 10j.bf&mS1>$%p@u u:42SuprxT)jKY.7L1\Qd?X9D4:7c[4ӱUQ{܌glN5 Z'2U}>[F){RC?Btxj =0wh]6^\h娽QY}~𼞀^} -cLѹŜu/m[=$G'W%i b :?&j?ɿ.g|+)&ϑuccu+&Y}V==+l e鿝ԳjvEO>zhŕڽEDb<~`dtu 9Dݾhf41V6%1k+ˆWeol.ےF^µyԅ 8H>qC&,@K+uF).fN)1206d ($UX Rǃ$ >OXaKok/u>%|dEF=նFJx,Rqݕf)AYhI?6XYYFh_Ï'奅#f1(@z޿2vRNG1 /SVZE;_z+@G_wg–t/jh wF%KܣABG@ڒ,~s3fi+'R ]օ#Fhw}>#N2؎{t@c %pWIỴ':1jF. 2yӿc^YH4Z|=(1}q1khH Qˉ]^c mԙK+i [s}a[x?f@)1X}LjPފ`9'/u;-{ ĉ_OWu]yk?*.{pmaTSiZ|)<9$̌WUɣHC[ҲGYEJ(=w \~'?u%bbd~ 4J^~0@/߶l4A)sfY` c<+"$dB=f>S6NáOxU8e˨j=(sUhфcM'{jLVe3zǕ^6 Ky\ 8ys#tjB{( |[z+mG8 u R/rDA"U"`bP/ AqjY߷Ng@FlbY8E5Puݡ{R' ? &ہmBj{ҪYPk6uU-sa\:ËBUj?yTPfhʜUE1yJ}_0X0"_lQ|R01hAË#"׏R;d8i'"['6k[)X]^ 9#ߴvXUxH. wnPQ}y߮(b$`s\b|#7< Qъh}*NR?x4jffI%7Dn9چJ8.[ޗbp.M+e4/P)9uf]&guܕWFJ RR4N?RJ88]kxh,2%g!%f7qSpA [GƭM|>aRCHؗս~rr`_T7I@QD%&RֶRԢF8,8&q}(CWKy =UV;!Mk\u_&WR wZ UJ6:^9D WҋuB܇BX酢J.> :yZ?,V3|3{tj)iܡ)[n ў%1@s' D/g  ^A9bӝ/SL  wy&T$J] ©N:̙"bpdWTL.X'_ ^Z2'=~!*.UzD?5WS";77-(:N7~ydacT!@m$`lH/s#Sj4*zNH?NYCa {})f0)Pv!@o.c$yH[/se Ur}p(JBՓm d|ή0Vt'>)Gei =vDi}-ձ2x 1v0w7\toT%^.!lՔ6FXVqgtZ+'fA;V$|+Yz%J; /e;Y!BbgDʑ-ZȴN3QuKUe {6ΞOݪ+b5^JMKɫbrW?31Ĵ;5q%>)..p#usFihSGLRWfÐ0 b- J|X'5pZZȆ?]ͤWk xZȭ1e`7~~7lGX@E`(j&W@hqu[#zPv4<2v%P>,2_ /'=go<h^+7Āt*2$ ] Dhʜ%ĮeidJ9͔ ED _^sG4ÄO&|ޜ&+uPv}nBXB]p ']!e\,!?ofj8Ɓ1AFl!IBRYv,DQbq5H`48>_t먜h(!+]RSޏkD3AywG4"\o,uty<(m1'B,1y.J?g-_f yL#36CM?|w27s/K|CY>nM͐J~^,_[0*) KD)(}9FB1*ߵ;kLC@P1hgp4}QZ]!5 ɎjZeغ+|x@ 71xXP*Yc=(\OVǬ&*V{GgLPF.b U  :Fh!?Y1jZ^Ml`RI>_rL+0V𩣢)x`Ÿd>D=?pE-JYVO˯ fĊhʜzm*J>'\ޡڹHi,Tp@|Q$ eVr_},8QJ!IYό3jRA?mW;Ḟd &Ci7qή+Xօаj>O#'W^ߋsMɿhu!I$-ń4Yji/+.zu 262T }59K!Sw՟S\N 9.-Hb /|Y@Xz.urP2zK瓾7[CL.(O96O,PX48~\z;YmD[kƱ#kE~񄴱 . Y lE[UWB%익ڏ"24_f~(y6᠒@F˧T\c2lJSB'M|t_&&v;ϒc 4xV9ڤS}u͔hX\B=(b\"cjiE&ݏ r|j VtxEC;H6럈W _װ;<#˱\"F'oL`-.{*G aQ}+u\ANC+&(,Y=^*1˰cXq'eK}}!ElɊ䘀!X#" SXUPx}]6p>4]ST`@s͇Ъ0 =gnw&2W^뷒{GSY:D"hK{S3Rw(qp.Su o2??S7k'msZcpDZ0>|KOvˤTCK" #hj-8L早:%Mn@6$<ޥzsܔ>zRB@9ş~,Hg,8;A= 8:x$w\#knXK]nWΫNѠ{F>aRxv\5cšd^8JG<"`?=zI :kiQ\pX,vy)wpd(B|= pe ܨeDr6%8CU[Giy/nP`|G 2G4[+JBۉ>VpEkG[o5_R /cE _}X[[xgPmTȒ­]4Hיܶ/HH7QH4+y ' 4D@nj%.p 2Som ̤(=U+;LO8AV6UEtPq>eCߴ2oĈ5O!lm󦳢!9*^> &KYhFT`6yx?3Cq%?hNIpa P =?65(d1 oKPfnD97:5x4}/'\<;,a-Y~yv0w.J}Ўw}77@>{7<>LL{L" P9z*%־F"(6xg"E?]I>B\g^22 [m?o∋D.L C/iKsF(Vhp% e&Q%i'iR-f,!uϪ`7Nmnjhk'fw.nTLtl^8xq1clQ'7>H"' rM`B8 BUPHTʆLlpw b %F! G-m,ѰK [! `UnW{j'х+aݙK>:k!`[h'T0i ԴIXȓͨҺ;$&m)%+!Teuew76O}{Jb FmLJO]~NKI;'M!g`E˿g@rW:钿GL6)ntƠ }(f'g]˜˜zo>r;n@8ܑEx%J]W]/L %f=`@yWk:ɱܣ.|wXcR]va, 0va%h")Qؓeչ&9-aHÜ9[韀4!1ވ(YI G?棥|Jŗy׽*UJ=̟TOfƅB۱G ?n2|Ǻʤ^Vq pc,ʜ r'i+*udUqsݔVp3t$1"C17~뷊wC;D#TJ DQA  =(wϬTz~E sa]\Od o 1Tu%w yòR|_ :9QTS`@PuEy:hd5vlۡiqŲ+I9_\Ǟ!1CRg>शAtX%̋;kf剌YV8VW=%l,v[L%gf\E OގAm^ cFZT+jң m}@޴8/'6>J ?SoJ|1gRVYi; Z X\[V>2^Usrh}Fudj&b:|%dlSZua\rV .†4B L`wt1 { Swh&w(or}(֒f3Ehv=>7bBےӅ;O[g qL4'o HLjOa8'w)ݜ vleeThvuQ_s@_7iko\3i$$ۃ[aeJo,^D=|ajkvҋfcƨdE[:*-_.h#AՇ~>o,ٸ[ :ӷVNJΐk։@KHvݥTJ44 ʂS8 7OMxN8mY+f/ t?XyJ,kʦ2<I śՔ5tC,9tkR_kKNq6la>+7GzM% -=lEU?ɍV8FEמS~/F&6QwjAV"}:>TN'N휣,zWح, w \!lb%x 7mbjup]h_s> Lj>ͧnqbJ~_D^/.^ fg*D֬~7~ Ma9"7[}B] I \z`nH\}$;m+Q?a#eAolV􎴵9&أ,‘BFc[%كރd䊹G#UiiNzݦ`DΔu!z)}lمɜs NiAZWW1^ HZftgaܤ.߭a{YdoaֈV 67t,>PJ2{؝e337!iv@huDxZz&8}띾t?yu нnnㅔB))$Oܮ䂳*h&%T *;3d!洀AN:ץF A[ȱ,nś\-E["> w-z"4@XیIr+8V>z}Y2NLs\ JX8 E70fXN.Jv|UuAT4=_VzPD'z_WzϖP*_!!5Lc`o~eulóW&͈ K"k#1}Xζ\$j97ޙ$S,Pe~+`G"צGDw3,=`$CWa̻ z7kͪH2AVc({>?a>p97%EcPVL|̫ گE"?@y2Eփ!'O EEbp=Ng& ہ %"M;ݩn49e i8427Mga}\ _#7\O?O岭G{|SSN~z]*BURwRD7ɓ+}{S2^\y`"F ,W3]?ΨZhXJn:uljwt[`3\j (y|hۈc /#x5mבS#`> Y^/>SNŏ}}Hqc4[E&qeouxYLaJb&_Ƃk_|>g.p rm5/55"|Tkz2{pzhR:ĂWgA />יnwߖƪ`mfƏ{w~"~"AD4}~H # !.g ndBSϯzq&ڸRp*?av>>6vQtòVy?r?` Q9!&9)OqZ=#_GFƋՂY(|V[.Duk}q%\޼A (XrMdAjj :goBK޸9D+?k͓ULMF4CB["!Ǣ ;5Cx1۹U{X "@];\ڳ߆/{ 'G_9jlXl  X!+{1 Z Va_yq6^2q~j@bKk!G6K{N)XW?\rpBgq1G*y|4(w| 0vFM¬3gJ̓-(w~*9m]{fOck=ThȬ~zӀwb atK$ͤKJz֑)%f @/(h<GQVi޷昢L9dV|I9r ~!ҋ.z =z,lt0Ѫ7 fՃJar 4yF"ϩ.{4IX& ]!584a>â[['1~՝:Lڋ^o\iy^™&" ^sswwk?>=$?&kDSAqduGDY xQD=IK ?hAƺx#}Qd2:k5䳏ʌi @La'Ɏce.yZj 9,!4TDE1_:dd^xڬ^f;On, ']Q^cieBSc6F]ԔKOD8AB9=@./zx{F|2aW1`A޻b_uoMޞjP$ Jziʨ7+7t4OVmƇ p8(y24xNG\F9uk]z9wԡQ<q%k[ռq'rzѱ(tXe'QQmʔ05%>YcѢFI­"lGxqO -÷C-}hQ?a,VcH}?Z3m܁!(drHJә"ɕ]eat'Ri`#&.پ ҃ޘ!(}8Cr|br`ƓVS-]7ǑxB`̓Mj#A2m;)p>b"KcKZu Z S` 8(cнe]SrV!)dr:h;\X یr)Mku}(͞(YPS Om ѐ=w^M󔇢+Jg?^C!ٝ՗Lu0+ַۅα<<3lѐ@|7%01"GlѸ}R؞vn Ty!NK`#F7ƃ% KG2`Ɯ" &tw fcޟܜySTn< 8hy:-"F[cM}>z'W[xLu"ś5=hӹ}0,( Is)cyAn " 9cQďe=$tA89a;5FM2̃ :Ug”UNwG[s6(GP!d"]VUG!NN+=$usT\^N@3)mʹ*3w*] /`o=+1r.R/%R{ћۉҟ` )hv97 ΖYSJ0uׅ.-\~_(rkÙY_;%n^3ꩆݑPMFFS.Ł,ڑR6Ïߘ9n+.A'!)$u}BHVe縥ȟ+zX!@OĴ=XE{:Cpl!UnpuK=}}\7R%cF$o3*O8veیev20]!eI6d7^6@䚤SU%0u\n'xN/Jj,:SR{jQ.lfM~%pjP0} X^)B9qkV0Bi* 'RA2 mאT7HLIH|~a_ xK xHLhǼ:-$h*0|̉%_mr y1;72|sс)hPPk9"f逗+ A9W٪<JwҜA :Tp;=F ND&;/ē6p>6 ،cIuQcG~L$re mcZϟlҚ_BM$#8jIF7x` 4I# G 5-ϐݦax A_`;˔S C/45/犂t `FҒZV`mWn tpF!qpT8CiI"+oakpQɹzzM~Yã*&" 6mg!}Zi΂wp20UV-]An=}ؑ"\$F0:>6c6(6 Dۄw@1ֳ{WclۆjjRc aa]G7npgD l &o<}jb C\e*;^z6Bk`w] CX"Nl%SkSpTTGQ)gGrNP-Z>$k OpTwD #L&P$ǂ!%9E GR4PMn =YUS 34 )19uĘ Al0/.+ !%֜2k"&$ rN[}sòb%D5P١i̪9,r&J2@$d <Èh9tN񘆎^|<ޞ8Ecwr%B55L v~:TO nu)UֈIR \p5 OH`o8=@UH"HyvIjv/g)Wgd  gl\.YmA 7ðو0\񖸐9@;pmߵ73ҒELxBn'p/2'ڧIO1k•NLv0;J>N5 7x oq_R)?`,g|`ktiJ5za7ã07KP <֡ɦ9E~SejPgȲS>sOhOΫEC2%z[zF_BA=՛Y"ʐDAX8zX(ǗS1l{YRF (. աp/m^1%v`\\*}-(.LD&63A5 ؊bHT`c㍘[6~ `5EDr].yК/ bpఔNȋǵRe6 P'<L@0NDex2Z+ZG-~+ɛȞ#,P\_*s֞ՅԈK:lֵ+YDŽ_m?ϥ }yL5eM2Z\LHE9ƕ|XL|=|o$+<&Է"4Af![Lw戯?4n%b:[@w| :ۓ&ٙ" EreAk( #?0P÷ ‘]SW{ԛ h-R JmvQs]FuNyzStS{݂yR֕; 6@ JIւU&Bհu DߪQ]U[.3Ѝu_@ٔ"2+Eey;SZw|.v;6ˎW͍ GH*\M?=\N?eKYjY"NЪΩ|#a۞Bcq/L`k2]DPЅWr0hGQ*QVAz .Ԅ#b?:8-:C0n7/#aYӍƏ1(z-#g7MJOȁShqK_7*=5~5s1v5H%ևϥr ,}Chs,ExwY"$dX拆!B֮in$2OkOZ5[xˣ +4; <ѕ?)fdRY$Qڥ!1a0T?bB#V@u I`~M$QC72X6m1}Rs5Cx#ki{(cL.?Lɤp۰/S"%[zz=!|뾫` D6Mc&H)ynb#N]9w0;|S' "A=ps@{Z0X3 K01_7}#x]T dDSo$ ڍd:@#Q#ݘd =#s Lh-5X Ů.yv߹h!W$Ķޣio&hH|> ڟy-d w V8眱>g~HŽcsCg?C?;ڱ{# ⫶>Bl :DԠJ7îcL8 9} oﴘ3=ܙt'ghW!|Fu{7{zkǁNqZ{'H(Ӭzjfu$~VqV0ӥ,U*%=]m˒NU(|U37bv[w@K e;*w,,ij Z!^}UDŽ*VTb|1?r"/*Kdodiwg߾ܔ[Cp A$￸.˕:{KsUqՇ#=zqYl&r}0 ϯ֌\#ֺx0aޕbѥ-jd5U*.S|) <ф BE 0w _N;IR0cKiS^Ð 5tOhdC=$6?pņJ YvcZKp Zwiv1z fF7E#JejPzƙ!ֲj7+z )nOWPf7O-N= >y_#**#"/<Ϡ'zƈgByBSAN+uN? bqs+2mzOXm >F>ۺ!Å2VuSK?CECVЍh L~yis.jɵ^)P85/hhYv趂D\9,1 ԫM R_̶Io&ίςQ2iMhnb\q%Z/,޸ѝ ~bکmiLDԘ>G,qs/*9+Y\*S>I4D >Plo?O53< t҈_gIaT6ۙ [dcT|=>K9PABnJp6.2.f/(;O yap1JI23 uzwy(B}ybF* Gp^"Nf=BX\()eMB2ؾNxQk}5GC1oԏ{AixC分1CfK1RJÅemgbea0^t DuSd[$9 Gomqp>|v 37 @ܒ`Ky0 =sLz=0q[v&c|M]~ﵰjIkbC]0`L 1Hߘ/~g ~а rg,Aor;OWmٕ5\@N)"k^喑r\M _$rlDneNl{t&`SB I T"y'VDt1/0iVcJ^&jŋe2!8^}R_&E*?h: cPJyWXwbo6^RnSWCUK)*Zy \<ЋpvNNJridP䊌rgO8YSay}DR92B1sx3yTq+_"ךdÉ vaM&O2I$ Ӕ鼯t׆>K>X"oQ={HŨ3BtJWZ_BJR\V4@Eezs'2V%m ۼ,x2m'/Iq61'_dDM=V҆d)޳cj,ND%=|lĐ^c/7zՐe:k߼QXsT95ceҌi)9fzW6lkQT-¹OwtmPɚb#&lcgGhM@ծXj All{aODɼM)W(ÐgHqDa2 l1.;Luzfj"𠱅chǍ,^P)D (unˎ/O?xۘ1A7Jsl "vr $5q-OVudPԁv/F9"(qjLY|^cMRJ@LjDn3^!C T=͵vm[V|h؀(4-soI@6aB& A%q:gطչC>ph@GH610Ck hqd*a̶.& @h%IBY= $-`bjSNm8_=9slJ \(ϳ怱0ZRwVcjN"!|) wy\um<ܜGV~/;>&޸B8ˈgv$s)}M2"2Zq2)"Bh:;|#VBBY;Z^ŝ;!$*iVZ g'bg6O 8cBqW$M&#ȇk>ZGkPK#j8C:bi^{5jd?2;LuG@ocoZ嵱 rU@bk$'zB:Yp!k x͆m̴Ћƪ pgUKvJ1S+y1ytVY`4*t3L׉):'6gfkkʞæq۲SZ0PY-Ndd|dt$y7  !ӣt_LsHE'9~WlVuB9Hwo759`&a_#%#G8{ok8[̶eEzk, 4vc/:-#l~v34trIPB':nL辊RFeg34I+ /SXHHjMIK؁WXMVwP,,Ha[ߖ:7!OofLkpȭ4VjX \]I]MxJ4Py͂k9ZDc=b*ZS9K]U`CUK,r]hC2LXӞElT[@a fQaxjaA=x:l|@v!+G> tP]t9u[c\8sZ|\§<=gYvtr(t}l5ٽ=i+F哾=_(܄ڭ[Wf4e8<nxX%r`wF\y>2mN*[-[0%Ë >CHnaKܠy1_Y6(8ӌX 5܎b+Eᶳ USѕli*^*iPvܙ k<`Ļ j`W[͢SON'dF#YQxCp%;ஓdd "F@=zLLn+H!~z\mj|DSr~8$h LAs|0*NK;``C.Lpҏ(k+ Ʊk4Vua7βm5@KW9]'i,2!G84)\enDHHS|}9ApIů\\AoAIF/?Ħzu"jjd/)!_/wID., Jc@vDVvʪQo a dYTjۋA#kfT폺1E\Ilc\gsweU"UѠSdj*$p9p\բb@n#оelg9 4w;-6]ʾVjd\~fdJ?rfJ Mnhx e9, `:+mۛPX5"2* űZxl~no1V*@MFmXkh. (eUY!GTU5TմC ꁅ)dB28*E:xd0[c6Qs P*ouf\.Dpҿ{f$gJq9:Sh?VDž|18f+Z>-¨ڬJ{ m& ykвʙ&A{f|l[c\=6jRM"Δv"ZrqRH;~`]qɟBQsHc"<^#[{ s%VGOZrRW~5a~K((`HlE+YZR5cC#C(f\ ԅNeD@G^_ ,n1re;be ~2$ A~h¬v<ސ)~ǛS˗F:QhboqAv@7E q9%`C ̰>F! 6fH{ ңWQYoA3|oIB^xM蜵W% |G摡][X8rueuKo 3oko#t1I#ыJo}LY3I&9ϳϼ`r#'T;|@DгC@b˰LŸL/./KpY}0rG^y][Dž5y)lz>AJKib/K4ϊX6z1nN c*;=2NS˅ChŒ1yBM>Q1U4fWKDu2.Zq yr$!yh?+˩|T$xlFٳl+ "?C/,VFH2O M6IސrA+uIlB1L( `y-sMȲK'BIiYM?PKN|Y!mP摁s s%s(I,lP7/Q7D]sGM#m?3(;$(7'b GF6#>~.yV?+Ob;'pgvgEmɦN#՛$eHf* B`VJQ_ٹڝb$[i痣ӷK9Кs8 "Ǚo aDQ $nZ=!( 75fWmc,ޅ)7tl϶/9^ 6IY<6>|kz‍JK[l[1‚gs?QrDRiM.Iǯ_@*+{A1vA}*刢f`+`@?i3ӧCypȮNe6N5ȉ<Ih w~sj88#5ZM®QU' -GWҝ0nѠ j6 Q rܣ0PmLt?ywRBB1$.-j'adrR&e6q*tـR 婔R 4O_b#q5Mxoy-BqDdnێHқFI+{Gⴆ>[B#,ށ@hAh%=Ō,oӛ HjxI( ?@"IY7gU0 KO=T-RQю$1X턾AoNߵro>Ыk](+Yp$cXK~trUN9cgJl'./jBo֐ }F}!.I?cJnUsЭ>*@ZVR ѷBQwȗH͍-)h;I4Ur&"TlOY:ʼI 쬞LqYXG0W#^ȬP ,ou\ ܨԘJ_ܴ>p'G>8R81Oml*E|$e);$UFQTuHD 69{020˥%w3y}T,{{F A! 4ZY4 /I,d֡Lqqhs,37u\ت*{0EoOvS+G&`TIDO@anJ΍OFs:< R1 p{6ɘ5Ep&*)fھP`.@|&؃d,k7zHΘ%M€y~4c3(!媶_Fdti/r՟A $#ct4ܷ-7޾G;1xp>?Ds Q2-e@:[yRK*7{s}2oc]8+%4E Z\(kn'm [oVYlU*'Gb/vUщiftL*q%DVms&CWR7bLR)3lۡh=#4Fݸ*~LlkN|ZkZ3OyO{ߚ&pHYT:H_Ty/NիA=ǂD1|kJFp^zzf< P @D9r+&J>x7,yȂ@M!}Jx5I\B*]]$'I3B/J?hpuCcH`{U(S)".كN ջ<Ș# KlSU_.? (~i#2Gʳ B/ G[;0I,̹ɟ;O/W w4LhMƌW |EQ$m1!nǁ#K'&$KIeD-l H>u҈(9n?쐈qh:urWSާ@͙OP1lE96*g8 O6\a駕1Wpr /HN3gPڲn+q}.Y\6A!1ȣǤl8L:Y[ۜwfC+B[ˡ?Xrk1Kz399R6RsKK jg&RyuUQ:E>`fY1QA ,>_Z ܉*_Xi^ Wr@a=`U[S5V3÷3)9wҰANn9lf]Q]CsH,X{N0(^N й'fYG0RCJX01?2=0܆~Socb J;r' %€`O]yXO~; Oxt"ze^;_k2U)y^"~ z='ۧNۈ@I,|9zO(޿mU C!xۗT7NDD=):|7{EZ;&do "i蹵&zOHCoɚ%lx<87xu*.m%an+~DK$O/> fR#*= .X2I&%0{|=8Q7Oż# gEzA3-x$ØPJY/EE)WjZ-_88qZy#v8PƷ8/%fkK9%uz͸r 3xY]eZ"s@aƗKhPE ;&(DYHӤ1t\/vpKхyZX+X*eS`w(O}!3bw2l!Tڀ}ƒ~bfPk<͵h*S&dɳ_t5S*0pAڬ?1FenZb-~DbNEI * bQGnzmS ]Ux!0,Sݷ ޭ.@2pQ1uI.xyEsU R!8+ˍz{9=V>ℓJ2)znfs߂b|t : bi.uL \|xx'JuhrA ʩ$BV[!ԗ~M/CBwwM'1iW);qPQE:.n0<{!Yan7/BO5)oS⣙`#K+{sD,"09X4iѼ?"9C1{kj9p/UV@}~"wuT^ %>_}zv]T Ty78#=s=֌\O7* ,\6p5u?ҩIW낯l|ӹ/ȲAV|+3XX$p"6\ub"o<`I{EȨvUV;t8U"v> =A{7<9e\,CBrIײ4(L#2/XBDx94:hl8akPtde@m {[2%-*L'k+fK#s4/C2(R+Bӵ}yȕB8yc2m֚V֘8PSɪ^ CSV7R*RYE .E ntӂ)ο}e<ѧ(s%Rbg|7! M69"!, !-ԨÜ@m"R~iOV$'Ӷg IO[,ƒܵ#N~BW6W85:{DHk$%豌l%`ݟN#a;s3?yȊ XMJ.' lv sgT z,4yX )mB.VM+}5 `h|9~*c&zhlliD6m<-m\_4Dv8'w3p iRŠr0 9KJl ͼkGϟ|a5ڐEŌ*s!gZ['i4ތƥU6K  M$8|;N 8н]87jFQK@W (9jSҴǕX%o#OꎈPCF(;9¦ G5 |#y'<ΡP3z4كeZ ?f|Ɨ ,s܇6b+5UA(~l"HC0_h;;"oN+Xhm&,ֆy*,\%1k= |451{r50)?д?ţ'uRJ,Qct+?I%Z RbA{pEJ#}JAnmX(I&ím]rN(I^d,  j w9%M_T*KIe"~Ȋ@eN, ({W/h‰vUpu\0 yH|6%y;|{i!FnZ@꩝^ƽQaiᐔ銆ۗeС`-O=zmclL!Ab}9 Q,MFhYd0VoLٗɋP:l0q*nlMQNPJX<8nt"ο]3bh\.O L80qn3og9/zǁ-}@1bY. n#zɮQBVz 9 U*% w-6:f/M|@Ԓz= ` } "&ZG+z o:fnI.fŌLX#V27kHT;q}JP G okzڂo9k9-aI(_^PbDo\s./<^0-3d^ z20?ڬiC\gCȐȊڠ^YødMG%ZLI%}2L(61 b3YAo6o,,A+acO 4c-VYG!mP90kZ*NlI)[%O^yT9O_a#\ϐu[w:Gk;fH [|*5Sq$!,0UªL/NWk+VaؐLsC)5>Cs @rCll[+ W]yKC50 Py:4e~: 2PV B,2O)z=>v=CQx%.ByNA0B,OfЋ=&\D{ SZ9A7̅1orkb=Fn%׹kmQ+i] o'E71w?K6$BZJ{ijh-/y˸Rw֓KOQ`Hu6^tQ7kNl.;MWLɓ<`.}}B|J~dTqq{8@wHzi,8"<wV ,^Ē&\IHn~5)/qKpbYؓ . \ s~\K "=+e9U?:@\Nzj l~I":mQ3i#O1gK-qq+?Ysq[+Q34U"ξQՆWX#z=qL\o[UdEہè[|)qK1֠B1d]esw1Ȥ<&c`j׉\g)Gm#CjĊf>n W2;KtX_F;SYFS ,=Zzil:ƍB@1RdM;&dyI] F=:y@);h?=B @/{1*=تR0tm^4mB}KQ0Mj͹ J!J3"TevE^k c䊸mnUVf{ҋO .~nTNn}ĸqX EEwbg! ``$|n"w V{ 9 mƦ#B##~蘴fP`!dTcAJ :HB1yJ7"9e  %TT:J`c+%C/bd֔Lq3ֲޣqipBu]S?E-vCdwL֪$ie'֪ݯ%r A?BNJFv.Kr|2]=  ū_L;%d?BneRكwnC2Y{D(N\Idg_Q5!=_qu]LqA*ݦ-~;vG+_V^V 쵂9Law9 ~MUے }I`/f8G7"%ާ ==17v]!I8^1>m>LČ澶Yy'̐TI(\]KYo>xgmx!vU3N ;6x zS3w{];>e*^JA.//dhji@F - R^R7C0NwQuƙ)~吇3[UZV)%;k"R0 ߲N(3"Fza &Ka^<<缂G(E~? @4&?atlRs9ISLa!vmEj  ϬuNj*ϖ.n[S15[ O7Fk-x?{o>a〻js,oc^E@^5)Gb:_#HLGaK%/Y~Y-Zw~IMB zO|k <֥+<"䢪/9}=qI.u߸|" P'f6L&ŔSi(zHPT\w+ʗA]"*9x[mwبik$&sS)Sֿ&$$kە>(#W6*N(Tk/dÕo;r ^&`eѥNn[[mi@ #æXZÕVev+*7Șu>)U~\cqA`,)>Rɵ8w$}4UatTfZxlHcb"4@0^63o3MYT50w a96&@[X_}(-ˉCZ"g< :(yV8^ s/vPTt-ebKav3,uu]nWhǟuڮO$riSe:?AOpyT3"ʯ9}ȿΧUX!a\z<}GMrۺe~o0xv ~/\JtjM~;QƜz`Dnwd#|lZ . ~:U#R"d=D`Y'UYkm , z̘5vƞi3Ho*N @fqM֥5Y/ԲY09tGsF j}r+}f]WVF;'#y{P8JO\wqNM]zqjAY=fm چ$eAp AbA[enPrȥ"=Zu~1{T)+P?ƠYV=橮o(RX>9CoҦv7rDqCagB&[MC_U! 5)%Wϒj0g`+v0wo*ujwԁ^9ʌC&:K%:Z!Tz:[x$؏!Hvm0̨0a#&'Tc,.}M9_Z5Gn$'ҷa]8hrGfzXoy $H&y]jiqpg6:o׺]Ltn8r5.g/j}b@葚;VkXRDRm-BR q|bw[ƅh"'ϙ7BݭK0pEOz8ig6mL ,C֧V:ٛRq|փ4~`$`NW&7;q5f QF9䁡l02tN(xY^ɉ/aDk=FmJj&^qv˲rC:Ȳ:E-=LPԹ2Jrf9"E( hHQ&8ϩ]r,_egsrXr^ &Ú#XK eMT|^+kXbe2שUw"xA6pPU̗N _\ ε0|`͗ y~`Z~"'$+:ѣh[BP/ bäoP:L"?XX^Q2p7I-![Gy 7mzCg=y~6/Q?dr߳BakJ6sRR{6.ё v]ŭj!}<;̨UMb駦D&R>BIJez A_`+d Rp6lPXrZac\ygQke>gEbv]#`#4sarPR ?GI}bvjVK:k, {UHaα4ª׳y̥+.ޓĂ.%гt%(>r hS 5C#G;(`c _ ejH3sǍp/O[fe2"ˣ>İŷ411G G70,ye=m+Qs]3s֙qP|QmK5((گ:B_Ez}{ l6QC@2%Yhm-|-5"b|&NWs>l%)AUM*QAWw]r,AW7'; .cY+݇p\'`"6Wnp} 3=!H}jħkj4!ڣM_4dz XA~|`o˫h[rgSwئ>^'a>xߘg6ܛ1IA][$8hEqat󗧗k)?O-GimL6q$2=Ir-*^ٔ,RCK^q}7HjK$ SA'EChM ㋡7?#KAu%=ϡCB<Տ$cZ^ޏ]s~^NOPu+$ŏTa fcz^)uĞוKɥ_N@E+KA]KY=Tk{[1YPѯ3Zk7sҸh7PNRGq9jM c;xɱr8.>ӌbήO,`C5yM=Y/^5;PHa!n[Xw@s3-=N+٥ %(f@p\ Kٱb7Y dmEݡҩ l7[N N텼XF螔"8֍=E7C,Jpo %if}xSfSu'!;cL%^s⁃PwOP%dWq] p9xܳPil ۫H]x*כ R")C| +,F,䅝?`-9ق `9Ӹn4 vW\&y4%F.s__MSv*3 TvFqyς1D?D0&lDA&븀͖x _"hFP+c7} f 6wxPr@#]K. SYI^ɺ%Or}C#cI_ 6<tJpْ}{qgeĦ8ёMwNrK{W4&pۓ-y$@`x,gkE+k]3>㒧 a]z4"cjMv2P~DJv~P)e`E?&A߲(} hPW$#H dxAMԾMB{Sjƽ3 \[6dd|Zsq|f]K.olHmtz@ZZ㷽'hçI󝇴J^{u\! d9eyj}UO_ԼϾ9OF_[_ьσʈvPkW^܆N;T`eݬ jΔ7k>*4(}(udʺc F>.}PoUmpM gN/h6#!3ѿ'V4HzN=pЌOsHd܏*j$`$G nh/tv_*I?eNgpqmKa-m@TVhX5]~0U`KD+|*r4b ThȔE + {ީD{*GbU{g $lZ kwߡf )X,N7-<] di/AvKljal(!Zdhjlq"bn `q} F72  .G@h_U7e%%9o}"oɸ4o]W".6\EgъZwwӣ< `H#GiM?Sð2rPs(ӟu0J.btkd'A1RBp1o.\X 3&X:3,0ZLCa"r|Xud2`"12i9֕`E~1*w+{[uLbhyK?8E'ki7ֿglИ1`{ #C\![*4eWT߹?4<گ)|Aop_r#(}f[[hڔ;Ms]qNR+bO"E^ څ7}=5HJ;mwF/4_Q8Bh)B"@d"$~OP`j:Ot,@:7 Z|ku=fF5(=a\].PB4ӿ؞% )qQ&;g Q*L ,2lsҟ6t3S~AVCY /~KӻqԊ6w{ASNnbs|G 4o I@Moݸ$b hscoK;<"Q_ JF"C![3]k3vvR.j&]Ca<6'pVOF=D0)q)F=/3Q60ІP5^j"6qoSNMF[T4gl i k}uc-̐ԏЉ3W)/lٔ9ېq {E/)BEd5Ӄz*[t"625Ϻ0VWUFlxccEV~" ܟޜZl0L$*PQ[npu׼}?Ū5y[T32z.Y̰;ǭA%s`8_[i'5sC%);7 AmJC9hC ^52ӄ@v ? vN/r]6RCϱ5ӌJ p^ps41;83m?]Xb)}V|V ެwYeI.e;cŇD9%+UG ǜj.gJ3)ܡ#DV"?4Xuk}RWR>3yC{xB nB ~ m}-okbjG:Mu9ˡĂj%-$2]R1qf'f4dπyZLoRN4=BbO1%7'h}Qn[ J)@;cDYխ-#\<Ѻ_P+%!w->'XœvV-Ys F*eiR'yԉ؎3v18FM9wf[>J]$LOXQ iw:,\PmLåLԩY%waEL: P.( u*ֺl/ qԕjU& oeSQz OonFf=nUA3gPV \*'yy !sVGSbg-t=< `!ܤ[3P҇8G*oӨĮe9F@\_HT]N{u?&nUFX8UHmkYSea`vGѨ$s:j\~]JtZW"djˏ7 wDYtٕݗ[%G }q|7+aoN+Vr&K)+\Ɗ\=#ɵ7+]zo7WWsLjeMRQr@%ۉ[&N )TtP4)eg4Q j4,\~Dfaۂ|ي:*w27o_,Hp=N0Й<v^uxW~}ÈYf #̈́H֕Q(\[>n](fHozIKF α7 ߻ޅ[N8|Pbi*Wo-wtt/*H(}O;Y;F$i䱚`XؓgeJsQC2.duy͑jK,쑒:gNhD7 ^.jQȴt7u$OJx91*[jR5X2GGXgW1Ǿ?af:dz{-3791` &pK Ho9rsI"XSCL2B2+R =Wnwt2ytXX/GС'21(#r*Қ }%5h-f "Li8tfOj?VѳWh|2Ҟ0Ꞡe'Nt=NiHH4gX<faru0^ܖ%I< r(tpôiؾLCnslqBb4ĽB81> BXɲ)${4ƍ|@Ծ BuEQ.bJSQ#Pfdr'ƅDO:]]!R|?]5,u;E=Wy/X96w]Pa:rh`y/Mx%qxR&СH#n ۺJ-*mďJU˾ { yԉ;TdgvaxPz=|k$0 t6Dgbz1?JuH=|'$ +%?ۦ-~\Tܩ~1b߂*@u2zkw ľ$jHao'R`ٵr\^.忾ގ ;u:BR&zIyP5Fah8S%b V]vEq1QVҺ^Yxؗ+J圄*\؋KJ7C+AJU0ֻu}Kx aK}4ŤMD.1VN)MF4#B:9{w Eg[ `Tۤ;yDYMR ow]a~27dnE)?NoU m nE[]}c8TޮhUs0F_ Mx @UyX.Dg'Iס+7L+\cѸ<'~HucEs^H%7Զx;8o1r\N!'.P~ڛayx!έ#oc&pwl,[s!lƺ0O{L!QU[uw"=x@̞VS?+zO/#T8Z_ 7qY`<*d|xX&:҆6w{ Kώ e 2,(U[ѤuR|b0"ps3QSi *\y"͝%5 kn?p}x⥍yRzò ?@&7.*4K hU}ğE0%8Ջq.L2St SX $l N9ttn ;JvA&³0(|sPZjw_ (H:Iv^eeX%YLhkJڠCC+R@Կ .K=BtF$=oǑY+z4p-Od]&Xqcb'QƺiDȉZfuts:5,C}` 9@Xn9v_+m\\ʿZ}sy7Y9$^ .]BXx =|RN2 W 9 gԤYLc<ND4 '2I2WriT}$`~  ni5$Am)`@;u(Q5Er{ކ4z$k;ѦQxHXr)(ձl51JJw'L8ɍFoz.k[ 30"ԧiёT3.O㦮i `/nܡFV\!S5[ND$fuO62laF2}x\-G>13_ |+*]5OajFbsX]P'7:JP(Ź`ޱ[w0 ]I8~n`׽SOoQo,IiL .؈ |_2f+ɹ3|<>i=g"1u.,"{ZW)jT 8QS&b\WG.-HcLxoD`b8*J%sw/c*! Ѡ )3} 7BVpowD簨ץKҰCp{赇/,!k,?j1[xUAT]$<@*C+KktW&l T'OȽk57zîe摅:KI'ɘ }\a3eaI}&-ϵ,P0{(<"5'T|نd ^HSΓ[+ RÄzSS r(K_: cW $!?6P.^h}ff Jâ~ YVk a;AKP'uA.gZZa#KJE7xzyNvv#C5Rh`y !!PX8ٽMgy|<\%&)ܹ .T$mTfZ34 3E#]GXzbs灹/7}'}@XL(i@$d%o "M)5z$-mōH C`pUTKc~j4QH3+OOlѓHמo5h3=&=Qg īXiJ4 1no B=Aﴝs j\hK&kRIea9+A 2o-÷]Xgϰ)NjKH'fؙvWb<a(Jmr7 )58Of)C|Rhxb K۽o4'3M> ȊjfBN.,u8$^s򮪏HWT'U٦lrf ?dh~Ȫes 5x] f=y ]/ ߏS+ q6]tXYOw*W{qEzY\g c%k8Baz<vM+ `I^oB*#Pg7 {;Z5j\m -_ɢ?|@OMǗ\@Tidx:JI-0P(v,?Q(LOQEZx}_Sgw<`FKAEW.JQV;~W?Je03^.3"YSx12Y_;4!]WzlC2F4+g$QHux0M *G`$aBTBAqF'VY09dQ`ξo*c{Xf4`$;:Ɗ_GT6|1Sh`C\u?ōU6)q:΂EjqcAHP|K >]z-kEmCJ-<{e$Y=ex |X A'䌞Ě?>/] 0^v;!$k#KqsUSgy2L}*j;{S:c>ʝwUZ!bƎRB׵\$\Pmv(2GD9] rB;N>~+Ǖ#f]qQ'adv&S ؈~T +L)f1~Bj2KČ?Ix..H6\IZ@uEdi8;f"b<<_`jS^eB0An{x&{nn86uZD?aDsx Qky&I4cpRlĔ:JPmFprWZƙVz+EfE筒Gzn+{V#@@ڇ"Nョ6uwzPY/7miՎ5s`S HlZH^ș6X|b0+M,DSWJkU9K *$Nj$}+k2NEgs~Y e4!P^%S+J1x`[6 =Da˞aR1N1lܙaי ҍF8IcnԁSV@)(<[ |;*0a1OqQC>Ħ4,ͳU: jM5.%*]`(Ll ޷:LuNLRE].), HH{ٴD$F&cG2梎.2^ԥ H״F)x8"uzc9L(_eܛ:<VwMPQ;7};N-4I;\a-$ B@,ñXg]|xRea?W=(f P a V GJ}d)Kuj$!+UA^5{f48CD”X}bm!HCn5Bȏ`_0 .C!д+ߘCےR|GqP8MYR9u! <'i~LhOdD$F$7- 7>m͵d0nK$~ {~)*g~uEq,bBB=yZ/3Ͱ"/3ޠo\pO:a"إ\o*+-3w{5K㞃|*\lBC['e)|( HJwo_pS3Zyˀ-(;*JP:< : ^ OhWJ1AW2C n9:HHi@.GxDΫ 5ixyD<8I8h&8 rfa]|eNY"mG.T^*!k ^;G xQᩂ!q}* *+s_|ĪfhN!fJzI \l _L7(~*Dk(FHus?ލh @p~PRܦ$; ܫ J_?pXr :. Y e.E$M"?G FI{=Q(;*p#:%}@Z֫ĠC DN6w94-&(hXX*Hi8l'(kېCI}{~%.8_BI>ֽv< d}1gP/km o*T'\~--ӘP lGpŶ\<_X OwnZ*uΙSRaBoJbY~f㸭iQYsa0x8|\G\ Lrb)֧<;%|GVHڊSJ >14>wGsel5jp /hPC -RoL'(A\GVGtܭE%. BAZj}6 5Pzւx 5pԿсE"鍏)vJonü6MUĖb kE^b:55 ,ű~>|M֞9CiX5zT1Nd*n]ɽϋbe`ၪ 6IO0)lMDeF't>hg3̠*/aҖbӥ7]d˺B}Kƅ9|PUdRjx!N2<Ʈ {HdU,WC E$p0LY n)mP¡'(5oge3Bsv@."G}ӧK)ܩT4X?C{1+fKM|3 67*>}&%YE~c"iAãj7YX)/ntHa sXwp&0o͌K*6WBPMG6wO+MaE/@?'+Mg| ÕyҖoOy+F.^^5bKI_ؿ7*pG] KJ)LM6c̹HyNGSp=e]/[{e{*bYMt8/b Ow}ۄbG qDeLʄ1cSa==ď|%b^Ӛ{qphDq9M{V;D֟$-joqHѐ~t+lG/d"ca^ZbDŽ̝4;&:2z&HfDžrXHܬyk-#@ C2i `ɤ{1)0KB&t=DPG"xYUጌM5vX//G hPy/ wvE[;!:䄱f)8qV"xv,^6c߉ZJKdL`V7)dG<&k„"LO%L+#w؎!ܲ,!y}# l99(KT9Y6}bՀEJ?O#kK.=R{!slWUKW53 lPj )h_\9X1YQdd&/ДI +BL@>m5(isD%*P![{ꤸ? ^iek9֊nviI+#ੱ=wCv7_3Ke Ԝ_GDKHޫrӯ׌±(1aQO49<Ճǩ2<6u)q_HCcٴ a'=)kiREVJogaz-:ɲ.6E9I'¾Һ̯噓Ev<1:WÊmr۔:ˮ#,vbZ]"ux}jw^ƽfԽڴqx%`WKY7:xrYqIժU<Xt]'WYW}|EJ Lڜ7}w̔"t&8UװfGxyP^| r8(Mb [\ >@Ow:AE+Y8ptJuމӀiJ!imߘ\oli?tߪL19h N⹯R[B7 Mڳ$~: $I'x,8GbbFHGZ1Eԉ;T"ϭ㛖|`9|ڐRS9 4[*X sc,rQwOOe | Sμ;&g#磺Q'hf</*cj;w`%O73m)Y羚M%wEcb#rVqYcAT`Pl$ P7&7 Gҿu7j: dsRYÚ$dVkgmwG{+$*#(ŕ|amH:KTVL^^-z#C\I7z;2se@D[hԋdp]-2^֤ыlSkYr_oT2kz]b^mͼ}Q %\G5 ,)AWjAe‘Q N Q"\ӱ#1.s`iZ -j ? G hdk*EACw=/<~uWK"tDs4tc[IDpSm6 5*3 XPd?mNS 8~E+qϽN:́Uqb}ew9G7'uǁ1#qie~]S #kS~mxK.æ Ȝ3d[;>"b$IkbnYLS@MX`;J<VS;$+*@K!UYjN5U:J)- ^PZsYN ڝqQ|J~)m4`mǼ4 Iv@0Iy& oGJg6W0io> /SWy?k͎K^n`\Eyt0 θ>%Q/KπEнi(oL}c8@JpSDfSa(eSjf)gkѫγ-[ Q %]`ej;YޓWDo /ItWs3Ǻ#^gl (!#p;(D . ~_ Se Q Û/Zo8<ӳp(b{m<Ւ I~S Ѥ5'Y؜!- 2B²!>>Qa74xg)%ln;K23.F*:1lPG>YH`%=,FRleJc |PChoXxDB]-q=ԕ ռAʝ]`J# goҔAKfZKy:.%&b/V\bA@pqEiUzki/S' ?E; lF(Ȋ%Byfʦ;^Ze_tExfM?-y_1[Yp ̋90G#(E fJzg<qZAq/B6CF$lTc, pPNQ>Ҋ{!7Aa6 smOeuYS;Gx*:}:CR<6vKwDDn'TJ9d:wdX&fLQ!Hx<>`)~~f~fN>6<UYuRUN=5N $tYc7M_ v[=a + neXWI_$Z(lc(r k??hbڇ0r?.9=?+Js)RAv[C V)liK7d#E cēNScE7mIDBXȻsϳ->4qE0l1`M 1ڰ%kf紎xYpYh{|sRnrvg&)H_6#< :Ni8kմ[zja4+nBvs1UMzQrx,091< TW<5V?/z["uQmM͋>oAI_\%-^Rsm9Djz B);^y6 &gϟ|MӹݡDY|}AٌIbE Y2z| HcϤUM31R0r>\ٹHDst$LrG~2^-3RlKP‡lң)X3:9t'Ca P>$/T6cá'rEklLpy!aa~t=_m8I!6~od_`R `r3Ͽ3dX~$qDM<64 jRLRXDN,-ɗ,b8zlZ쒸aX8Dd# {o85¶/ 9U#gۦ -G1,Cs2c!c$ %ý$AmhutJ!2Viz3RvCZ+Z:fuB&k! '3zi%L;L ]1ے`Aڀ'AvPbn6Sƙ~7 s_2ܹmXܑ b$A<<ۍa1iFzrx5+yW VgICJSixW}zSLYo,kZ2N-'<ŋG7O\`E\OȖ+'hVt8CfrBy'p=f[;@@?g38Son`PD5HyC䯫s{zCPyqS/ig 77Y'nm^A hh\)^RQHJĘO82'qߺZc_LlnOE7I'$"ΆϮeM=Lge8/1csoh}n~Um(4֑?M46v'+$*!Gh,B#F;/96ŋL$oQ_LEr.υBiBx˚N+y.b]``#}Ǭ3 RV4+ezç8[Z(I|]'(Yx4&{]`{e]cQְL`b*f9#2pzd3ePۼUX%Y[viONAV+;c^Ǻhی85.a[nog{>jxޘd:;P4ҟ1" =zJ4IPw/5`0eU@_ݷNvyx4 P-dyqlU{s"(Y?BqKqUAA_Ͼ3V]r|in+VmRz\e1|0FWe)1`1O&{4TGE1 8+Y\hD6 lſ5q^BsYxT *%J \R eE|aGU]9mW1{-AdUO hzvX'ˇ(lM_ـSJpHg<M|%FCF7i\v_[kbH\exsTAgh_q bl,g^ }:LI!kcHr>,ni~gK/\y-D4Yr4ʽC*B6[SʜA,59=J;.m4] D$g(ֻt|P8+v/ }2fV9bUj09/,j/T,N3Dx>oAuqMTG%qcYly0[,'iߺ˶ܞRmeEo 8XW_ԁxJUJn>–QЧ‘3x[i|,>])\,&1 ir9ꠔTUWAA &S`s'XclPutSNJ ZA[w1Np 3w+n׽!}Tђ!-MCL 𧴪S}+ qAovNW]΍h' QDY@)?fP= d@LMJ(#\:&H Ъ bIRTZ2ͤ K}!:h%˽R82޽I뉒 S zV\9G~k2R uPb9VKkHo']͚r5 ;AT`W_x7'rfAV=*۠ (y^T/Mt⒃F5)>lD]6vB\T7 wafql+?} V OjsseR2Zd*rvtÞ *{!rh62x!o/=0'ע,Z/`(.=5"DuREu"Vqۜ 5Uh}L(t-焌o>=mb1M&X'ݬchx,M>#b@V7"ooQ;Ĥc&U.+O =KŤr悞~L#MqD5\2L4"u03'Ӯ:0^ 4`6OC= 3F̒o{iNn ZjVImf鲁KgjN@P&M9@.E;6ov36kTR e35H̋{s\Ham `kOWn&UCWk6i*0 ϒ"z@eԖE ~&^߭9fBKhS67*6n [gFVT$rrJӟGtz OϴƯ%)rFz QVvݧ-_*_+lIF0ɸ=a,KCݗwQ' 4}ob 4OL3ԟ}2d̈ 5v읁j97*Ҡe]q4ω#LeN΁a{7s*AyfPq%Q?R7ozL{,X`=gXS U- 2Clp߿8=\B~k%us:e- ք l.'@BA%ȗg%f| eΚn1>3(!QH[ y!Q%J-!~xW>ueR 6ھ{}ʅ`wSG R<2ΌsknUТXkqguӣ%J9fX*@OGdMNh܄RNz :ѰK'=BE11TTȷRlfڧHNQOx@ErgPJML7WO.#N7I`aѓ9ڇҫL㸼=F!'7?*eO, 1TCAʩHw}@Vv93BoFVSZh5'H_ _> d]h4 1J"a:8#jTKȥ#O& [:z߅&~Xq2@TN>hNoO, gCŤ"͛st%7e_laZ%cz>|MDxwAyrozq|c3~?/Û(ɼ56 lSqn=VALp.69Hm LYSRG['j&T'On8`'bѲFA&4HMҺHjߞH/F>I@Ep4׬b,Mvr3o & zEVU=!Gv0qmU4zzF嘘'% hYhl%3K۬SuX~ 5QHMw#Q[З)4%4DYj%:7 `T2Qu⺭ VU|X kg/{]U]Pw/vpzeBD{7O6`Gυ@ m7KxIfcថn]N훽nR%~.|ׯp2l>!l~d"ekn3?t- <Ϲv0ЌZ>8-I^|P%sl> LW܊&=hwQ%Xkț.+"Rk0ܵ^Tqdm 7sB9 c'lp'D>4 /s4-x ۰@Q.%R>.^,gwh(T@7FqȝmBm7g 6: r})K:`ΔϹP%KqI < 3s[)եqxȟ, b_:'+dnlWaLM3BIDcrPߕN$Qڎ*lJr6E^vϊXb5d[[0~B+ly)f0{Sх2c ф0˫:`F 3Rj:PRR)ʞVAj4Nt~NPFU=Cdw^=iΐ\573lG V.jw; "d|F}-'`AYg\51$^# FKF $L[ nvwj.iԃCvM9IcҾl.OG _))?>lf+9d[:Y=xy;t!'4 Dbzv*kB.2n_l(sl(6(` KgqXyq*(A8Kz'Ѡdwb.巹uFaNnSuy!P݄4TZuyOqYkryW=Ix`2'(h:2*<.1k6hH/Brm]}`y$ķFFӤKl)mK7n'4z0;BVPلZ9՜?+"c$ar^ڑPM# m!VD^‘O#Nr1VWH c:_Q0?JZsgҝW5KCۣ0`Ē!URkB@"p5 ß%l1*xĕ^+O·ZĤחblCr2j,Z:$dY}N~E{;R,qfaqדp7,Y[ 4߼^6Kb,<_UKwqw֜/`o9|@i_A zLDhy<*ڋVZ'9HEBlV y>t!$U 9rFYw ~ؑζ5MiLw-K"LlGڢ;N+- m!.fzJYID֎9meQGmGBݨF<4"T5GY .4gy`5'gy J;d&11Gs$E-bܑ*Cܻv#I$}5VWo]٤% ۲ Ǜk\j{ھs(sɾKpCe7JqDB|-!{8glg}'ju5Y[W. |i x yþt9sNމglCF,Y4\7B _{kI'4RGڭgV֚mˑDվԏZGt /b6pJEpԡU̍bv2O"S Rз=rVUCT-B Lt4}éLҼ尞OB A o"'S~~lq=JWް=KLiZҕW,}uVpbFxM1#wL S2cJi3#Z*QUm?<^^&䨂#B!Z=K(Hp :!&2~M&'Mӧ !t [W, ?H+>d,{},7?~.$vS_=Gn58;x/ H# V_fO9lQ8lަ[%"U1iw3; Dl4Cu `,fw}җ j,ꉢ~D2:evmPu82q3[U qw9 ^P?( uV@NN&*H/l0WQ,aF4*#:x2CE ľ/^7Sx]ZU9B v<3t||iAo!;&MleLʜ+4{FJCO¿w:)X YZUR@E3mXv(v ŧKl$nZtVS8\%WR0?h`V=Y?XWr8{(^ )\6g^=C+ <A e R \vvCv_{m>-44}LKLp " $+Ϻj CȈs؅J~YxaC$3,։39x)3<#hdO<=Ea]v:(gd,''dNTʠ0AsQ `ԓWi#p-O= =c${oQqW⁼U_fˁ:fq@.YyyKHNFgvcG׉YOAq P|*h1oʺvG!R 5ϡc+H%^7bVSw6ˊ/1HWL!_[3øB!%)عb!)'aPHwȕ~16x=Vӆx+1&{~c* 21 ŭliyAXtMTnOj7gϋ7U^ɅQy&g",Ϳ3=*P8 q:>XZmwB~ɨ͢(!x~IY_=  ~PpSM Swo@xi'G\(+@> ? %sK ~}֗Ho b"=r/Yb.QޚDx52([n}zZ*:^ nr;ΛT-VA SOlm!BJ^Β%K # "S'BxAWvLڢ-5h<ޤ엝%drLdis'-w8hH CJ?cWMS WմGyg$YGiHQe⼵8]uCD,_8fc%ŸKc=eyaT*DPY:Wo9/`;M!^ւNM= *Gd+n#D꫍(eM=_Y\O@-QxxUqx}74u3Ѣ{*j f#tf5snt6ݖ1`3wi.6TAi hDI|n>м9xo22A=lJB͝h:~/Pb@8IO`E HΞ5J( =%^?Ck,+E[xKt[W8 ̽aw2~$KdX̼Ǖ 9Ft*1ԣA+"c:l12h5]qCOsi+iF͉ ㇇\e^*e> +^!}B!߁_ɇd9`շXw3zGUC[ : {6"blAЗՁaAd6\ 'w퍡2IcN.JG1إ$tFtz[J׌TK(b I QI50t/v^8 g@udsORC5Hy**~h )QjCv̻褽e_?'r&SF^'Q-NC6S>f#nݿbp(+iϴM= YU?OJ~2FjtՓt*|t8!U-_dH1'K&Qb GlKٖ.βр+ѧ Ӝ '/ɶxiBˢj}oK!oq4a{ɿ{ L<^bbVk~-͈8&U3i_̥u \CрWEbT좙A- Fy9m-¥ V.D|!jӼ 5TJ˄0cXDlA~0O $TsLa|?6ا/ t3+|x)g(O6DgՆe6iP'񥣛x:S]s1i÷:\f{tN/.e(oBN *P}A6$vD6ߟ& fI%*& TK: o#A3nm{ ;^Ѕִ:^Kq%M6l3Be/EoLc d eq4D8.5t)u4Ѱ0omQ=h#θHv1g}yWjJi˖ݎ'';򪊸 B0wq8!.~#Zky)?]V[S}26&;ȇV g8KA굸_|ڲ[R=|fFAV`nf )>/x9[s@W?숐Ԗ]^D|7/3Dh\ T.  O:e5"&2H6xm @`SjJ$C4Ԗ`rX&@D<<AAaVLnŘ\cWd_ ECLyJbUYFjgl~/s IdK7s;6r6Bfk#&f5֟-ͳT4U)ߒ8[q kIiRт+^'3(}q"s%5zNmx m>GnĴ@&j Gol2G_0Ohޕ)y— FUVQhlE8p,|Enpla?W'Jө[6{Sx/Kp0S =Ww/Dc4z}x'\bxfVܔ3_e٣*8dx:&*-Q X ?r>;,Ÿ"=IzɝB[PLb%ٯ.0XU S'eJCyGU0ZsѦ` TߞSn:m1K<Ӈo6  37)UmFB>VE%JWu:FLOV$W̐`XbiCx&h."x'9ۅ O;!߁Pq\W^_@9$01-pB=M4vza݇v0Ưz,@yq(C] ~^ ;ǟ F,O3)S);`oT  u‘Wk'/Grqrأ U] IN@4ܱ R2Q\{eۄHY%cNӌV-LU<9CJc#ZG%nwPwXoʰ@WlDp Lq(3xyEN2!?Hugڟc>(=֪4HV&3JO9 w|, {,>ޤx"% u6.[ny`tX9yI/A Vn֐%* ʀocȔDb6K460&"d$kf캵fhTg:Kb Í\oN菉P xjJ.GWA!A?&8W |'?] ]Wܩy,yWX $ņRZbg-VXUk拽?atE 鲯ˀɕjL1+wƐ3ce)kŎާCB g_&z\x} @M qւEDii7ʌMӍOr!E:In&+4|N༸gc,l+CAoҘg)oy.z,#O;mN,:H]C#(fLzUߝhHU<ʥ+VͤOjS+u ┺ȂI >O A(1`e5O@/naIC ?qCNƑ |O3vv[M $,6H,>zlLēr;wFU[26ayHP`Ӳ`95xt󱐓#yKѝcI?JًMQ^rXP)p¶X&s#]WJ4EV:2w#.$1:~VGJآ)CĉI`tRM 8ȗkUr8\X >}5hw׋bNI_Mez+=][h(elo i눩+6I<-jݿu˭ŰXj/T_v̄TQWrp@9݄`lhw$3YK~c' A &9uK`,D^LPX_[ipQZUJz&ݙ;;HsrB(DL++IN\Nyky7'̈́f>2ՔOTy<_yWެ[u35_x,~@t\ s)7Nn[>sv ˇMIߊ)@mzxY}愹ȩuB5mldAT ms0pFѼp56mk1^7:_dL v?!x!VR{M`3p"RVO+usdf)i4,w9m,a&1w'R^5խ/ ̺01qzFLYG$0D#}2,;lknts&p!f\rh|S}WKpX{EVb,Du`0ii%iyKay10~MO,c'^]t ,sox)$+5}#u^yK0LoaplHhKH4 .z1q8tܧ6Џ!ˀ$؃0LSV:XESoN%n-7fZ{*5{q>`6qbyþ&Mϒ`sYA] g}}{|0m:׏u šK|\f=PsCn-fq;FH;B&g&ҙ<Dv6kQH첇n^DHt-(#hJ } Ѫ _ PzD^66G ZUיI\GARBƭ.FggiǶXDH*M _+U3ћ%g\PԩXI_"8z4jJ}TSa,|?Gh3.Ǿ)4sY|]'-1Am ]y.T Vl8-[1ENJ9`Wn,fz4g'"E~^8C$ :z'g!x[CїGV|˭^Frv!m!ۤ&bv Y(F=>vm gP1X<5\gPqXȵh/Z.*<7wjt_g2u7u\p_zı^ c z*XY9n3rÿ+M5PRcha. s}'IYe5 ,'t%|3WW^2Kc2>Ô):[X ]a%R04Tr>1g6B=>Rn+jY]q\GHZUHT,<;xy=Uz%\Z~foOΨk#!fݹX7<"J0cOx,&B-@vXGM"8tBMFdX}pf.Xx=!;8iZxyX-s&#쇚"QrMMchI[pnlZԏYV:P8.gֿ5TZ{A,9ٿsƳe^of7žzʧt gZJOwINyefbB t)7b~oSYC'wPȅڞt|Ifpzx L;c;* j<( c'e?; 6Dɝpt( )F_'xXUqM10ib;ڣ*zcq5:h6k= sMƪd;uCp6> E˿˫0^naing"jZ"Ll@],YQuvN\Rr)My@G fHe?KV~6-Z›THvt7{%]`3>2ޫU'YdxQ"$ -D@ٵk4$ۖU$#av4Oj/ZYF .~/2FIiMz]dR// ōU^J YQ㳈[-g*f#0xB$Pq޻zzϝG lkSKki''QRf^p*XtE1M'?yV%{7WDTzpJrm6nRS {륻Rld~:g柀]jz^D)>4tPGSU"!Z\: OBe{\^z{ד#ngٻ?zWת. _WU-dѤva5^ܬ+kŬ/\a<9c Ƈ2Nˍ1X9v34`^YMKM,q zk塴̙eKC%M\5dziUjOQ1_ sXFW&f+y{,##D,܃rArsBNJ<++B:oI?i<+v[yÀI(7g*vB&vr9E)# %NY'^m˹!vE2A*51X;g_1cHV}<}tgHUqjG9Uuvb'oA%wduzY'D6+z`G|{xं™@{,Aq⽿^N ӍȺVm4+®uͭ OOo޽um_ru8YbO"ȡ,))ػ76+ZbʫHWu.gSvR PTMk<+AZȢޯ.vf\>ῐ٥#fz*&J*r]Jk^O1dPxUɰ@xocGOrB@N?>:Cf>;|j9QݨJdhNÄ'` t(GU.6jZ:T"iY>'DKPD b/B^j<29`c=UɭgJ+j5nRUX7H0V*0U]ȭ qt,+8gfQw鐆ɵg_$siƹ'Pa\M[ᕘݏ4Å"-^F¬ښ>f.ߡXȐROG`GuDud241;jIw1ebXxsxyoC 2Uմ1ZE:*\nT-J"]-ʣkGS!aبGzWFd ?:.!٣қu"B.B9º{&;3\\L7| RʞE(s~ ~{ b lҐ]33WCU\'GY-WEt(ɀ'wiF-ʑzwP}a}:1icJ+ +5Lh.vI4Gmr/SJF9S5(~Zu DF5'zM z$J|$)> q 7CQJlDi-GTC mv;CHTaJ7 RZp!2H}cF b{{EF&F(&+zWӪ#r*:rNs'u\܏ "fOѼd<ϩrօ:E̼,q fyIIex\#j.MeaNր^HD&r~r6ǜGtz9)0ǃy;rP.D}Y W ֶ?c|\j<߶ȭۭKW %Kļ)h@vEY'^ƊNɺvؤr͔?y>,a(vW,y$T֜g'RFnԠѠƘf%!ܢ,­wM&'1x:t=Ȋ|Rd(na8@AcS-KKBj5{v&3r&%FCp0x@2(Z}N-[*瘍U2:ud4HC]#Auɛ销 !Lĩ,T\ѭ@iTpk=APQ70MO?x˭ Tt-_D Ń-֬W<l+~ ]s}jY!AI*jm_lV^_&1T>e  ;E@9":/=_yLm4=պ!FIal Q1[T)xK@zD]}DM{}im\l/yD%gD:UF@EƶK^yL r5x:W~86ӹؼ4:K5׺rLR#DyK{؈8C*gItY!REL GPŸ,Z$UzA.ny ϫMP;>myB 㻨˧SD2:pSkӯUb4^Z};8&?0ep@%isɎA dϗ1D+H"R6޶0WΉj`GSL(&hBuRFo6/X6~vv'N_7pr L+8^O a\ԖbSM4l o]rΒn >`%?7qp̽_ {?#5T4)'Zh\n!B5yT5CvZ!дy*,;2;wG'7Ղ%43.1^5/$+Hoc8<ד3W~y)\Ď զGAmmGH*,fR4R eI69(F"-q0>Ù<#~]h4i]Gm-YgIîQ69x<j[doT+yM&H 0恛\v$6-:^JC{ޜ1!-}ttv7^XߘDhy y .ICXѭFDI:U:,|fu?HWq|'N?Uc w7acp]u螸fH&#;&nuɍ ]ĉ}s0ٳG],k_Bl -kO(4cZIa xU$&sϠ B^i"PCϞƻ:n%w Fpzf_W&A$u%,K7^]t%ekU/WmrUrmqS;#DPP'VIN?59 ֞1C!Na%5j/^l>NHlftƈTUm{RV<􈡽Zqd¼JftlTMLAۯ^"F2D`k ]*J?va'44ҙ@#paiUÞawBm+49ۗ.|&eNWZ=ldC\bD]Tv4f 0y)4/h^7}Lmyˤjc/X- ;x BӲC:YAjGLȏ۩2JW)<eUg1H[ն7x!h V+xJM@N~B $@e|8Kof B9/} >:6DɧvMEK_Z^y'qMсxm1abt֍KhU!C8@ A˱*"{&Ad&YxzUIEE:9Z,C@q =i~{[: kD&tϯtl>v4:jl-}e(ۊ7rc(51v+W#ɠJBJNn"L,7n¥tNvdݠ> lADUOLBh_8F0xBkgp{qٯb:Վe ◝2Җr *]!GIb:wl&&_\\jU_dZ6,v2)MauquzۀZsb d \ea1]1J!tE-yY9NKoA=E?/ %NDhvWڜCg<)md.Rl-/fo'4x!a h;FyĊ/.NiϓmK'ףnz4ҏϦ`{g \u: /Djϩhfka?>Ljnڻ9 PK)ttRQ4VhpE7qp{TIV穉TQeQPf]i~&]竪{5FUŏF`/[Xtr'ۀmtgt&(xJkHoz#8bLۑ& \9i`\7D~Wo$TI.JE,B%+hJ,5$R)gILNznO!Y>rؾZ4S1hQ9RWnnuu)}ܽT&*^"s`(c.ZJz4TXI!g+ekuXQ71qNb.!Q='^p|@l%s崓-oOS]|a3:rgpsSl\BH;  7VLT^H ff N*3:5J ns^$aiOWPg i崕4 lU,HuM,j8j-uXZD;>$>3|eּ8uqpR~7Dp*T^,eyybAOLv,5)<`2wy7=J蒫%щ'v^9}-iD$ϮG>"?Hn,a6 [`۬ Mq c7NLSQݧkLfmn߇XX;WZR#=N7Uv'/7!Xܷ9B=Ï8v`2hpTm[_pbMǠ>9{VW egMjVeJK9N"Q&&e/|>C>6~w<:E@fq{4MʕAS 3N ;H8xA~ כ^LTMnK25)=/п87/ jOP}ΏA&TeStYrx%@s/}s`|;ZUg,GISܙE,_"4j&Oۜ-gGBݛxns붕hЏ`Zz^4ޟZk.=;mcH>[ Z>HPIl&2}<=Цdl"3XnOf7>2aBW͑#eQՇRf!ә#%KC[B@d]4$m5&HoVL=I?!^z #n3" x1=5m'In.[sH}\\=J]R@XUaHK:i.%j]6hkz\j+yV)6ې"cW^iYʂhy7l@b!u`@>gFsX; sѠKݥS":VށarÍS4wN֞~9J[χ:!DNcpcw|`zSGdɸ'rzb~~91*+2kR>yy~ZM`8Y=+HshQ-<>ű.*xBA- 5Rg϶|sd%kSyƵ6 1S0ܗ}x3Xz,7O ÷ @Y,J "'I:Ul\Y&U8@"Z^om>cަ- Sjۈ#P}3GS@Ves37 KE?SG;*%.oְzi0QQr R<' ~uKzY悇ȷnNg"ߛu52~T(ήiv0ɏ8uT\ze+4Ttʭz`iCKU$  :*vz ͖Լ.su?,[8GN]J m^e/5~[^Ntܰ?7L-ojHYoNwOhnh8ΰ{M$ 8^m C2$.g7&EeJ)]9L 03$vOlŅBڋ3N`T#Fɠ)&22l堹do8ǔe5Epfܜ-@NKՕhFOt) =CJ;|#,(N%wb$S2l"˔w9ThO4X'ӊ+Xe?JCQ7nJPw3p!N&4-oݐfl1A\ٷy p~0Q})47+GL{ЙcJUq DL~ :@&/ ?n&biRquDJqSJP# q,a sn W=#+4 +/=#j&[XL6#"1ɄZᓾGoN-1)wڲ[ωf! &Q/p./57â)+5xY,\{$8>p6O.FS8ނ}= )c]V=3wשQnl&.QD!䄕?LjXP,~,*x|ߪEg24?€k%keXM'?]sLȹN}b$' |똊5uBHr,At1#ΖDVQDgLǷkM)D] gKZ?T[`g\v5 5WKzFDi?\*ğ)V{*;v"v5XxLq0w".Bc ~`sZq}.l(Q5R>y=̑vrYKzzyȑ(EK#[DeMU 0lgAr&Ȃ?󃬶#C Zg_ f0uE_ '$(t)4{kX|ܖ.Nb}ά"Պr[0Zz:iqPFN8@4F򖝊K" [K='?ý߸+z,iṊјݭ6 Z5܂T*e%C4eEpwgHSNXc, n \y<q[w??fb)%r f̡z?%7`D4cU7bCOyչzKjʣMj{~N$u[K{#{Mm7RKPZu>Ӝk{&;vgHb+ p9W[zXsf(Tt!Q"b!ߥ ǎЛ($7+ ^REݻ_+˦'ʽ3cCV(yv͏CryVFQ Va;D8DirL]c _C+DSx'(nV3*Gq f.g(~OL%(U*^x/y(NOK̸4?J54> a߽穤 Ε&+̍VB|]>XM88:.@\3Cs]a* ׭_TvWWXLb\4KwѯtΟ e~joBϣdn bawrcSḗ.X2 q!q>^2.ؙ5 tqJN?Aޚ* ?H"#-éT: *qB)ɺx=0c=OYxEg V+?UI,fT_/b 99^e`2zDriY3~:= ٢5 S07pK$2TN=$I%fʁIOul0WntB`:?iS_>73o !sUҿ{:,l)V¤ *a|;=f D$2w*[8X xxEc?(ތZ\~ND _L|^k?PXL]zJh鮇Ēh !{JMÙF雸|OiV^rW1p!a0)Yq=%Rd7%K aάv{U82c\MGq "8b4ēEKf[Ѳ;}S{%> qGG6 5Q8ݣW\z}2_y ~„%/*O^m?G_ Qs(nvÒ2h[n# [ 1; 85,_0TX7]E"{ ȅ}u-y͖(Z[ًIfeg'?}K,nۇ`zWW2d7~OiI^В쳦=ͭ.l"]O<|ǩ)>px}9D pLGV(\Yb<<|k _$VYf>$/E vD3yu]=Ε츰8hT_6d8"E1<]UǽSC!nYUvi,?_XygA4'EA*?d )WR1ْ|&RVZbxt~9R$̃)"ᮃEi S374!q]}[],n['C5!6meO/CWHMx6ფX714X 4/.cAb"ɇ`OiaM5߂=4ĕc+b5tüA0/TXf~Un\㶤~zC$UT3FxcM(R퓟Iъ= Wf id'1j-VQԓ̄.&~ЉEC|-(ڜA!ۧ0SonbHM/cHSm"Sb6h"Gcq|wP< Z "Kz֙[CbH&.D:==w>Ͽ/N]XD[\!|^[ray* IDIT}-\jRp:fqMdWXgV+={x"2CvBn m&Q"͸ܪ7f Xr#i}5HX(zg.Hk& F[ @#r 䥖 {u\C7:ivM!!ce1x&L$4(?b[K S3̂ڗ3u1)#u+:?!LZe:t%:*->ӭ*C̑i gO@d It3f+^χaae-xRp_=ZY_9aț(g6tZaYe0w2:zx =zP55(ȅ98{C%^"5ݛ者I炶n" <j`-Wj@d!{-bIXb}FydDoԸLaoV7dJi^b9@06V E!9>{l\{Jx=87 zu~ќnD{r 6=UWuNF2z!˃20CfnP]l:;mLU bnlqA9{ ᴙ>]u~օVx*eumժ!H`0MyxMZ[o^|/Z+}K&ذ.C^Uhd[BNR3ٽ i +<7Ka>%(5I874n?k_]0?r'~Uuy-y $TVHX֎yr^G,,7Pa +n\;<4hgƋ ϪQ,U\ķro;V_טmʬSL<ƓU`$կ<:I./a E9xl|&f e ^K['wO`am֠ꡘ^$]).bvp*A &Ѷ 3x&&(lH*̒/TCIn()󒃄ĕFv3 waZ3L\ %&$W>%.i=C4ƌB^a19!mCu򣚮 O0:_Ϩ/ ;N jglA'4^.=DoKYnW|p10rSIcg8 ~·~ b)@[aUhjjjti2X@5`Wk\ r`0zx $ޮ0GRZK ;~J:y-nDJi/k;iQu@ ۂcH'FnM7d}qW6W%vRF(ASbC _ JspZR8wH! B]ue_3wQo"kz+0҄x:F gqGr- -CU<5E:M&&x֢M%DgKI~U+3hfiftes 0y/ԍ"u;Kir;CMzli(^v=hy<@`D4w-aXl ĵZY7A2ƗU9ڪ׉\sUNĹhM 9m| yGUeDfJDv0X[^6oBܦVКKH o'TLǿ/&2IIf5?;3!;4bl%Bֺw".\;|c]fhnŝ5wǑ 1-E(-Fb5"@6!64vs'):} rC-4=I6r[iOhف)(U#JK*0c\xċ!P24BZ@(8#d`?LdjT(={{|}}֏JNSY {;CO=Oi:6 x"_ BW[56<-9=)H}?_mAl94p37QAے [KQPXj(BKNSژQ`ů/hSqJ.>]_%h\s]mRxϻk/J( t7EX}T"  $O|F+a(t%t0nS5Ѣ LDm0 j\bd:7C"lN6q RA-+W 8g\96wNA0LL>fdPY{g46ר:φ:Ty(yIwI?AK"Lc\+6,c +gc#.>Vr1do.B 7C* fB0GBѩY<ёT^wI>!e$B[c d.p8'B9\*ZMs!@,A<3y|[:t Ne'"`6EݤRc/ dP~IfYonvMڀ䂮.g| jg[н<zY  I(;>ߒd݃delpe@m $ E]vzQxhI;;q#hoH9ʚB[Ϙ *ږ3o -ϊUP_e"Z>eb e;Ѧ^KMu3zcGw)nI!c-ʔQ؜4@hnxڹraF^c3 Y󒛒K.B   |fomI:/*JGE EZ_pj>NH[XBK4OwUkZo>d|rH_ оr^nBv" kb4z:6.Y,?~`9u6.4q9Wcui{SZ7'mX*2a yvQ/Pb4U{OWL!s:NDn)&:/m f[W:ΠtM!rƞQ4 kCVU zɦLERǜ5&QXz|JD RIq8u} AZEq]tdynD9;ЂbӶXğIalvero/[+j3Ѵzk-wA6_O'X:vɑRn$&_ʩw29| M&í;/[fQv6;A̸;?'!׸wA:"co%?nIsh2\`Vz*MzQiʟHPgX#߇o Vvϕd(hx9/Jh6;91{Z@ KRtIJ1AzqAMr,UMD/PnO8W7z7H1WRb 7mo0ƾB+)> Q?9d%nPz doXu3) xS}>,<L cDHƆY9:#(ߖc߀s2M/4lF o28'aNȪe 'mD s |UАk47k0f{M6h.6HTP#찕W0OAH"}1׈ڬ(xjV`)d"^|%Lw>:Ju5YjЌ _sI榐=K $ByxcN8@d} \1SU{B{]E;aD:u) AxF#MD}]:Ak[VY+Թ{sWT&oȹ3ۯ`XRUTo0 jHH 9?wReL  Z N-Y> aZ/gn} Ijf7_'і~*]u$& v۳POϟ;$n~#"6FxC1ݱBBw;8&l0^i',5a|krhBI̷J3=2nZ'M.69"ʧ\ #qBqVC Ύ,9˙9oE'-;A7bvn"ːӏS6#٥Tx"79z[k4Y^\EC):g)A/kVS8a[l#ؒK$|za$aMSq*E<0'Ӗ3O&( =1OUB9Nv?Z1I)~\c1@v5p#Q\鮰^Yx~1Mwވ| 3p (*)8&ھjh,_o#м?#cѷ,Lbp}PYnK`Vx!FGUn_G7{W [O{ HP,z@/8'mg:=FcO5m͗Iq5!bUcqA.Q~AtUÄn5͙3$W`~2tHٴUNJ. "묨ZUH82JG*;0_ri1M$Z\Z߬W?>}iև]2Cjm*paww,Yk !nPx(>Aq\bt"M~Q'%_]axљLeWm_ j֋b恠J 9$qQ$`%cw INbv0\~iǜÉpw|cATK 0ڏk 6?Pqf>A:*6Y+9XY:KX͜Y`u{rfcWT/.΀Y ȯ!jjyhrj<÷weSn0R9BxK>т^"!gXӂWMЬ}΍]#FM\yok!2%GxW̫.M&m/x򛫌SGYj0֯J瘣zU݄j_"#SkY(-B D#&R9.s65ĿBZκGt`FU6s~`(I}M..Pvu\i<2~Kv08A%_E3_K4r;"߭Y> vְ$Eέ R W)r8K&˫VԷPTkw]_ Ʉ Ƥml8ǘjo_aָafr?ndq> }LJ)kA}x˅ 7u"\8Xa- >0AfqXO*XĞ:wg[ƘK":ۘ) E"+lyi?iY+Jw땑g4Wb{Alt #}b'Q^%-8A3 [;dM3ŴT,uMLK/M3ڃN렍'O+Ty/8_CJrǏɳ{*E b"e"cO$tg%Ct<\%&n9݁ͻ|8/jFB*Ȳ9܇tлSZvH JW&!xT-Y@>F/u^vLr{,Xcsmpo>D)V_:qھ?7 <H/mb-.Gw\EI}<$n>fi(7ex+e@[!眖ۃ1a{eh[1g2x{*54| {{qMP› ( *d@ss⾭x_ht姆vO/.17 빗)aBzq7|"ZxSGc- |l~ ;n Q5ġ$Hi@^Y\j*`%+] N}6kaq }ErŒ㨏Ftp3v@ ;֐-gȵyWV/fa[M!;| ^uA [N3A3[GKl=7|EʔnܺԼ5aլ6ƻEy-ӴGp 4- SQ:2F",oD3"[.+ОtgN}Ei{]<}_7ImtOޤ,iGX>63v]HxsyPtUKVhe O7]ڛ=NeI ~K"'QI!MgL%˪kpZ#=Հ9#/7py>WyeY+-v 4;D Ei8I ]ǓءXų1cA(ڙ#3P]WaE,|&҃0޵gڦ-e䲌(tVnw^~$nd>,G8<*p2c}:ɦ jPulD3_xPl쐅ZeH7C,1K)7!tS  $fAUd:SjNQĚhV]6r6b'e>bpMSՎC 0nc_=pXr!fO${(] 1z| ǟeaBsFNxe\(_ Ba_CJGJ.UQSWGRzs^èe!oG)hzA  @D&$,:Z tt-#7-;.BC ]] 7/Q,=삾vE˸XmἚ?=%#hxe[P>e<-*hn]J~NŔ@K*Z1c:OȜjP{D 6TX{kUwD-d!Od؇0ϵ| ]iOY ,.!DņI y">@"Ls(Ws{MѳfT"OxW*㡦L%D/lOBh=1yfD "P)q.50F-¼jNF>!ϢدI!Q)<-_sIҕXr_"@{W]hVpkFјBK$Pi`@FR_X}Oګ9cdU_׋=R3Wtf,*"yKW`T}HJu8WɍQ2կԑ WvdPS}qY_!?xa[0c*$ Z<#Q֯C~~cF;QtV'& UDS7쎽QkbCt;"gWm&M>irEn":UW[]k/3)|^`؉h`["Yd6!xmEU}SJ;|xqB?-CD·c( c]ӬCg3+=Zٶ^c٧-n{,a*D8bLϪ[B2Kez9>l+.Ӹj[[+A6S):0s6<[L+JLH:ft\ĖUt OFO9wvؖbQ=wlС;Y" }l V0pUmg&YxnKumY[DŽNYrQxωYp$$:2WukfKjE[U,,l cd՞Ɯ MhdȄ')K?kw+AsRI!!zg!l-+Gk|)sO\l:9c eb 8KQu^u:ԍ9PU5Oq!MR!@Z&N#]'&1U GbkL&-,0گ (wR4pU/3wfycLګdMn# 3b^"i8̅}obXԐb|( EeW?4ϜҭG£DA1izk5ݡCÔhތ0Vt@aRb\D̳@cX/J6eM;jRI;NLί^lJjXE0а06Jd9*|`yz)UE:^Ze+Rs] Hu· Q'mQo$ U"%$+ S̸WӺ ;GWQϪ]Vo|Ep.4z^  Sp_d,P:S w.!P/+ā+=.@ #EJNDqTi^br+BE'= q!`n]F3uvX/ROƫ0 dQSaU6 s1$:NGbwDMQ# J}+v"8̰lm@Hi<ݧ3fM|+Mհ^ݱ"94gΎbs|sFpXÝBkϐ1:S:=%!x1vM9p :(.ȾU^>MKY[Mj!l %n0X}A^D3㵍@."OqרjQ?j% |]Ūu4]1-2H_?z]_-_~]W%"4h6e4R̺ "Wt$\9fyg@Wn±IrhC+%qΆUy&[!xo^:?+悌`zKl6#*RcR. }䢂 kLCߚ 5J4|;q\UDQw2}n>/W4J!D.-lB:Vtތi4(z5cpR|*^£H߄Ȉɿ3ьlwFrKI<^ <@9FҴE-ÚbަJ*>9:YUZ󺵕U -0ϩ*v6]EHrھkY0Xll*0cgna~zxb^=Hv!a7] EzTc;a5fh"!0yQfywb#<0m-&G̮vK׀]vB˗m ~0Pq+iW7`Cl8֦QL@8 ߃Xr> ]RP|ء y=zB.ܻW~k V) SkP+y{%4'%D[r9e (Ydx|U~A*lPwDe;baMJ*. TV:M=/a\@ ǣʟ`,,]o.@k>毝3N}(Ay;ޣ-'H@Qg>tLwDm(mz>nׯ8BxEƤv20i=PԵR_nic^]BTșT[Pcl?vp4|F#BpI >.03sI$s[g?,5fBZ|K|oZ83V6(\Wd( EdInQn}b!iCVd( >ӾҲGa$p| u LNzSWaHl>E#@9`t~{V|۴E&`gk` wPPYddlh斫!S"Jcɧ>rDk=~L-gg."=,ƾci}mFuɜ7Ɩ_-D1z⾲&uHwb6hEZ2 %7`2D4ѿT?W8 HoM+|54x̰TLҲ8BpU*u`$rؑfݾOϙb+CrN>޷ib=Uf$gM2gsZ|q bTvh˩+1 Rx8{7v=MkV[$~Z*-T\Y?a;P14B~oO?>J0d)oYqA۪6y9#~,a>9!G_'PzSc)R6?.;@rl&^E;*ςt 2iw_ϥ< ~Ƙ~7BD~ F~*j\~"O{|2]^PwT4}}w&7K]]#v{c|E 1fhR% ͳՀ˟Pv^zCC'2rmV!r# .;ָ9s\FQaaҏM%E fj Dv:5՚AJop s zNL2 ӬŽPU(G'&Y%iw6}?hotKϭH"JxԢEwz&uڛ8xs! q#-~vf!DcG^C@A3.ul O'G#"h%=V&*T-}ՑOK+QޥL6R*+'hi>e,}V끄r'k 0쿴]a5N+<#Q ɮS%WOï"O֍6W.~_T*1PDC 32Un{Y4gKB PBby3%ZQd" #(`2U˺I2v.[{7~k-n%njDD_D\6탌~*R-=sPc]I:i]Z]-SfuPB_DEnNS!˥m 1TAl}lLO sMadTڠ.ar-'$TC5ޢ:de1(S+ٵxYõ{&4hP?Z=pWfC-E*ry N8T@$ˡ|ui?D9p[ 4̎ wwdŚ"#ǨМjj9IM跏Hޡ!Pe7 XK+| !m03mްy~*~ 2HEDx˽qi΄Y˽cԟm-w$.9rs*,"''ٹZUtVh &^{ؽTݫ6]iJH锇QjUtaԂ={7A&5Ń °zN Rx3oX%,bZa+0xQAg W;Pnn {=e&WI2k׆Rv3dJzxcw"RggMړ;ڡrnSώ֊:,z5\ӨSQzxAÂ6E؜/bc͹j3[a39b3&ضXiO7R5XulwDGC. nCчT9&pKd@d\jM@;4q%\&+$mY3E$\z5Z.l&_UO݆H%cP5xIBW" NӽP$+fE.q917a\Ȫ5bTm Y#T0P}B:%ۭͦ uVV۪bW,>z](H0|ϊ1Ӿzα"ß:/4ZTА(IlA}XXt! &5%w!߫d}P2OqV;r-xEi^~U,8;H)۷wI t.6.c7.FIczJjs;cҾw+~]<߸GAtG/<1ϲRhC 1\S20Ŗ SkŞxS=UWǂ8g5M=\U$3Bhxg[Mxx+#/^J^C.z+9UA2~y#@H\Nջ(kgﯞ&}| TBnm<-{5& Y5K|Qۥ5$@SܮF/Gk%Bë%fs42TS2ٍ[.m.r$w08뀿vCse4z 9YDjVֈ1]7t!\ ObQ(3Ulp=%T #OXK R&M8-cpfnQG?IdV\R> |y]k(ќ,qXOJDIwufmZ]DlMnPl+h: U_;`8%LtOr9wsg4Gǔ }ٜ|] Sa@[]CHc6xZ`mU} 8,@0ʂbnN"j$!ҠOjgVGUc_sQm_tb|ݬaxXzE81W VCZ\"b6r%t ë$=c_ {`r`sRSvZMf\Aǭy1^ZD֭CIڻ I681,?<V qLevU C(9k{7/7 2_])=̤KKDӗf\&N%1YuRj?_ R% `ַzsZIϨ%P).8I/pڑdE\kP %%Va w >吢JVJ}@"u9&YTLS|gFj̇L)TC]$ǚd̴[ІL?p7W4K63Ar1L`0p':9k;ʉ?3dy;G ́st "PkQwM{OGi>Ukb])KQ?.*cuG}:0SR#<19߮`jYY У! \(FYf|MRf} ?q9C~9p5@)HRɟAT|;_!3x<:l9f8^ΪPy[>rY\4Y1πeӔ^и@_%&}Y+xqxEVA(Nq㧖X~m nv .|"b&7roSrj^Od|LRD-dE+5tba.]9"B+@Aͣ..c};.`cQ}w޳v\"д= TDO:Z Wo;ig#8f?zB?/"ZEO`!?{f2(8`1ZVcQdbl1Sxʴ)׶`gIN<:$/)oP^h-kfT#kr~}j*E5_(}sK4ЛU2 GBS$(>sbë]0hbfվ W1c(0{QW킹zxS$mS| >sEJ4{>%9H,m8`=xyj4">7c1kPc\7˳L/0HhEV` 6rB1!S?,+v#,T9? A *9 6x"OR9IQGȆF T%]:F۸^3V_IdZ߄iw㩋u|hQ< S4>)gk&UwP2xtC}q+dRKK_ _7=kȋlg4y EN@"ErIAӔ`M qRc-J߮2nƦG$0/%C^h)ǹcӵ:#p3entn&[ w U~vuBP'uDMgT|̹ST$;OV^oG?&©kOԏ"ǁьVnqsO-=eQ4񍝑"2hlq]A)y#irևv>eEy/_L/ouRB MHUScg@"`}Mo DZ[ au|; ڶCI?}ɗ{86U#C3R(y]*u;A)בǩl $AW=uf-+ufrx,2㭀 .r6MGAWc*5В%\+/8=V >"d%ndR-|JyE_vyMQABk@Y'7.[$wBSVu:Ư5#lG"+Nx6^kCy"9F!jIfC0ݰ&l?50O)𱰍>p$ WH?13uh)q CѡQG*-2™y5^njYL[Z$ězaBs~2bF48lJ)?o#%HpKu&k~;[UU _F SMD|"\MXZ8Ii-8y@ר©ƙ>XZ/iI*CvMcEjeG8rƮ.cRM(! |[CdF+RtLAsZ ̂_BW }qTPV{ R\cOr-W}v6]K9#aS$0ӕ9j7p;siրqq ,@IUhq nMqO_ZbvdئXbi/Pj,WD-UOׅZZylsRVԽ }I`!6Ά 1d|Vk]ƙDXQSUb$"rtIIҬNvUC{h_L,׏>oCWW)F43B/ )᝹.ݰo]F~Ӗ$5X oނQ94ۇ7=Inj kKbVԊC7TF9W_2-; LPPDr#$>MF5֚4X*yw翢;f-q7˷órC`i$-x:I%[ퟵ\>\Or-ˡ,enpi8C #˙П^9' 8ijWV1?P\Hlmi:~CeR1u\՝lؕv NKF%X =h7%U'Rͷ$!\ߜC&BŹ)qD2ڊH|ĸ<cGĪ̓c&4 %0ӽŶ?TVq<si|ւK$@erbPa dP^Eic\wLcpg۽dž 7f?zQZ_~@#`37 &'X8Lk[*Ε({–eϷoPkbH>MĐ*'5S"IfT4gNw4kAu6?yiyk`l*"a iA|7޺ne.J\(:V`OHyҧ8Qz?DӜՔՊ<-\kRaf!vyG n/Pj,xƇPh9rY~PKZRm[#?B|:ZX{g6a>f| =CD:Yhj9 /w[cwH40 ?7Sku;[LC"aHBؒ]#o Ɣu ~p7AHgv\7uz|I@4JYn5띻@}t?^9XZeMf^h1,h,@ܲ•ipuQ%4T'|^4K1{J\uD#w`עia8'3 Z}Sب=Ujz^ym򒻐u_N.ctMJ4#ZZ`<LgOC|,g~(fpv&r+f/C wqΧsZ< j[%7<;&Ip:KOB\w_;⇪ K6}}I_PW%+Sz%Q(;),F4NT+\>(79=?9!A*X1hwiĉ'fw\C'sj]҈.yn3htHzk[Pq.BF2!cZvN |o}Ё)0 f]:j}p`X~V˜d,ȓF2AΊXu+h2} 0eI95k"ץdz \ng޺/-"|MO&5/ %͊3^/OcP-LD)wLZn~QjdVbP>]&.PR!TszZNOGX:ЙJ,QDFJ )ܳ@l Oh@Pkv{0U*e#+ 5IeTpPa9-FʨPdf6(r2j Vg-_ *̖TŒ#$^v&%B5sY{&^?셽o"lQ*ꮶF\6?4Ȇ%țzEwf$@"|ͥ7v wpanVW-Њ1"0hfƨDRf+A|D+F:M+nuQC8.ѼXX Bb5phC ЛpdoDq5iXdbXxo]xW}N Fxs O#\wEQz+5b,>]}g=cRat$fۿS,HۑwHiAtсji_gXLkjz"|x~kMMybfH(8{q@w?xɅ tsR*U8=ynڕZ>F.Dӊ{LM o1Z'AB7:~(~-j׋ ѵLW%?;G5=AQQ䎚Gdk9<]O]&RH& ˆi L2E%?ITVO;_NxG]b0k.P;[n-I O%r Y"/W9K+jg+h$]5 WJ]@2؊G:A$#*M/ak{m8[ti\sdG|y'$GY@'Ogcw$GJ kW:18N`oy|Hdq Z kX.sm|_V ';dʍ|<@_ڬk, ddC=\H!\!K1 tR˓-еɵ`w#7.Rq//Fmcl k)>k  RVEy9qHԐ[oc K~tAG!~ÔB'quDW,rO|[.bĤ! Qv7c9d8$hбI T^a/xQPK#<.$DC (s~vɥJ!*vmSz]vHw7Y(2 xHg#)#pB0Evܞy({ovZقKZr$6mx$>Q{k,3 l$ n"L_sti0Έ E-/_+ǺhD5)!ދYUuFmɶ0RE볗fw}lS̆r+{'/]]f"6v"bfR9y4F(E_сN= mVwB;I86/S n߬RM`~] FaE?N HW$f'v*q"QeϢ%{ch2"lv@,L *[/:V܅-F8Cim\Z[-q+SmIA;y{k奮}i;*]lRR)rLvYT^ inoоnތ +rMw{S憐 >>6?`Ge -G J4'uzIa3cirRcbL4ݵ`!8(ȅ6|CLJ\#u@o67<}޶(yy4''X6 K@rkK(WXnR;֍™=:﹬Nt~|-=d{P^!dp@maHlo>zg ͩGo|7@` LAu6ͅoml42'rܬN^^lYa;hH;T ޛ43:tz.uEİ57N3ݹx0]Lk(xQK|&l/GT)]*wH߉ hXIKLKUU4IF$~)3A+fmo%"R ?Eط,ǖ9+HMO/Uݖ1]Gд>lw 2?i.JϽI.E=}ur"X&,+&>^HR1R1]uwRjSa{Ueg,},8'J";_qd?&Vإ|9yeWϾ$2uߙSbU&b9ܚufsbd+LeݣZ;MN~ vHj",1)κQY$hL8"9iOzGhN|^?WFd:Ň߆i0>B[U3[͓֔#U?kȦ0 kBS 6lP6=fm,?ة L Lo_.H6WRQtQHn *Y !ȉ閯J|@]Cz1"pT)('b9@]O=| c-@Vxҭ4dSfm2R+́Zv77[9&ᅁ6ĖBFH@b%\a﹵: |E,Nz7`<)5`ԔAKC7&"t"h^db_?I{ QaQbH!i1΍l Z^@;ޅ\k pNIٲdy/ ,tX56?0H|h_D_|#-Tfעx5zGdfRĜ/R+9YLKcCx!T,{IT*:]RrPa /?@Xhiv4BHeAߪbDbSQ jSﳭEYU'XAes,{Á E˚(3ll=/m +UʼngLPZ۠T~U ˤ|Xez sܵAV~XHՌct mqBz`dǽY5ko6"xd[솬_13)XoRF9@:(5<8UHt_M5%]h`8;'@'a56]5~5TwPԚ1riT 4&g tryFԷhNs\N8P@it/^;3l06Y:Wԝd~q9튊)4cyK/619l!V dݗ5Z录h^aS3(gsf(9PVn .78dթMO?ςME3H* ~fC,^16osOeKx lYp?1{ eZE>EL &H ",I剛 j$j{g<EMs |#߻c^wq nQO\Kљ/z;Q`id]1zBdyzu<[ks'vDYt'оH7XFOtsm95~4`&46v<>-Z|mvf#QKK SDSѣ}lTs-j.9*[:pɪڐ1p/"}i6uƩ .ڤevj!:IXۃQ,ұ<4_ lI@lhDנ`˳qK3r aa5h2BP$PE8-<`:ES6gc]j(䪲 LJNPsAy'HhJPJ]4hD>^`DmrDR!=PѨb9Uߌ('Osi/a7aSJKm5I !tLJcJәfntZ+rRw:uDܘQzI/n~ hYɅ2D6|#k;tZ< 6tpp/~Y MVS/ Hv ţp-j8F-Χ01N~1G%sçe傢#&ewܜ'âu|YQq\/]J q7}1#'}ѴbCƊ;fB0e6 aZUFO5l_h5a6T '~aNK6tևۢy9^x3?̣:9L4KnVIװZJTLa ĶYj+#9 pz"Zw] M6ޑGM@h$(_(CԚHs)  $9>^ln >&(i?۬!_.;OP&rɽsM]g&)1)Nݶcit_`i_ Nn0١`F!H} s5R>ί9 DCm_Za>!Vo(g ֟RcpOj/9*  >dr?lxJDt?cSָH㹠- vYǾҢI3rW: i rKLmC@ D1c"Pt_}ØgAp; ag{6W8ۊ*\vm{bdi'(Gzr3 rP H0sAظIӶ"FSTaȊϯDDf#KC%}V4`?',i !W.b3(E8+0xC A6ߑhuʸ?ijL_8fB.6k60gP])j ^L 8ϳ&wH$7Y8_7ϊP5zxAp|fNB6>&y]Yqp7Vw _RIW5q6K`o,.3ȰϓX\7GV)sbq+/BV3HT/ q+\^0Ks3L?{CǼ:Nl j0C?hyp'Qpvn4EUڣ=ʤrYqSVН#/*wt>?kZ+밫P`BXL! $Esicl9;uK)dMV> Q:Ilrօg"HOxǦ-I9ӪˋKu6EݥAQn`sW?U”$Q:؛soicπZho,kFR"0)"%,WkV)yqi5o18CNkG2\)IlQ 4.޶'ʞ=bDX0=0kTa 0v7 ,7/#BYm7Gl_} -Z4?KhK;- ֘=hp#|1w~{%EYdǡYHxsbܩX[*jB/t!tx$}?!Ǎ P^&d Ed#ES cAVXbUY_VyVЎdNdXSA 7ج5VaF<{ч.`qbÇ=e=g2 %Ef_B UN#xߣ)A W3aH"|.>p&6^sQ:un*ĽgHi<dM b2n鄊C$}0"w0 42\>X2/γt?U'ώE-ΰ}KuqwAE-wdUǣpe:Z u X ][nW8[r `[nFaP:~6X|3x"M6TGnb5#DϤ%@C:S:i{(Y+[+f/UT*{]L;]97iT=+7P'ɪz_xQ@މh0I: e^Y {؎%A k}=X栽)ThJv.N/w¤k^AmcRбwu8a>P$3zMdGn@prm);Jdz3푪LE@ RWT FK{i|26PNX d@;UPV4qqs`p&ᐃ6׸Ȱ}bT}tu]4"TD^Ոw>Q(cK #ǖ7諛n VAÆzR{6w^mUS-%W-R*C]&n$ƴ)tR*(!!&Kb@BH"oj>Igoy]&7,EHHXcՅ^f`sR%e2!:BUܨ.S-'MMvfOund 1^?s -"3^؜oXJ ysͺLT]MoSLk.>>Zb|,B>OF2{ Nځ]b17v aTЇe[ޛ*UJT vM'q}4I3[qnjq"AQ،'T$IkQ a/FuWgZhN w-Hb\*WL\Qx<ܤD%pW@_8_{׸Pڟ.,Kvc̰\Lnv|' Hw%OV&u)<8DNƫBMAj׾E " jLnC1PUn1l*z݊I,t*WpU{{:˹}ݜ;%:We_ [wZ(E` :#NO89pP?cwcJk̠hVŜ~"<ۊd97:CS(GQ檩}}j$:7˙?ޒNhO a[zh{'6Kb䗿K. TN7rT~?G}im "琧4ۄ:>K,477*Vbg+c? xliTnn3LRP@w 4GxdbV+n7!>jslaIi]߲EkT&*5װ  OЃv)O_Pv0b+rG2VG}23F1CQ3PbɺvI~vOk,ϼٽuЏ 5J$GsOEfb|G4٣mS\U51TZӜnxӽI{ vtxGUFG )iag%LAUEh():gk_%vq-& $^ch=* qia)Nd؄SG'U ohD'xkدqw\ҩNvz?Ab aefXM` Qi RH-h]ϛl{d>^a~ͯ:*, P(TNS~hqoe1G7!}&Av.,BorGATa1uUMXuo,Oף0߫u%: IJ,\PYϧKTb=uZ! \#"T U{|lvZ@O>ֳ^JNBF:W+̀I0%eYs46-kx]Ilu\9 h bh8p)Mp(jˍ[>jC\z?CbVvp/nQ.ړhj܀!3KP 2[w am:_" K~?C#@V)?6-(1䲲=AL3 %ОՌ  ^3`}૖gzvнQ}xOZ|gN1}Q@@=q-&jsg^eɀbD h86doC-i[d(8hڠDUP'*CIɠTkqrߑ{hhl})vx"|b¨FpLP۶PZ Vh9|T JtKБ Tl%0U\XTl VP%\?2 m)/`aWYbWX.#9vȀk*zITZC3릲J߃a 42rS[>č&`5u%C_k7n͍wGc#l1a}'%ν@ S dj|5'>2z%RÅ.>TY}/, \AyD<6fCFA@KZl(?N9>b=E_&h-2nV5piUz#[&tR2҂/mZ=S^2lDٕLi =Jc]O}͒6 ޵aؓQ9G͋f1Հ>) ^ue'.LM)ē'=A;+{T*>O[Bl&VֈB|s0L[|ImQI:ic^x(fIƙ [2 N0솀޶: n_WSk^F1fCRHbH c|"}Z gefз]lj0E `d{RzQn!?(c蜶k2:=g!oHv8RR;İQ3ʹ: sO,!{mEk$,sl~ kEkú7{:~H_w gqS#0XYBa LŸ#ŋyG"Pzoa!~'60XM9z`R'bq)W%3ٹca3=igůmXX ЛavzS`䎛X(`D RV_q!Rm7TZQBG75yGˣK5EF]Y+nr]i;>"?w"|U ׻|~ۡ- ^kBiDrT#F*[>dh~oXH連$-"pDhG/sK@P3X&2@FaƯD9R5 cD6Ѡ<_G<6(L:a[rw =ld=J\aKroԇO*5%I2jI!Ԝ ;$VlHb`Em>/cך+xv㌥&?6dRփ< yL"  kls5{g'KL۝N׳.kyA Yq" \}/8+T|NKDyFP; !֩5cX5A) l'eZ7~2lTRK=8b ڧy e0פN y)@wS? 5s5FdsNKxgjgB*ěXwqySUf#l rHf VFƭt U 0؊NKMEȗ_ʮ,OɒXcԌ*爊J\/D!%Q3i-AeS̓3v+Ѵ('nk'-阬 IɇIOl_h c؉}X&U3f-shE0i:CU.y9͓1Tmc.D+z%">C ٩`l8 Gۮ_6r]}L{lZa'F]3(R囡q.ij@34Rԝڴ&2M?LmWƜĮʺ%D`p3f l% cpk):9 :gՈB- ~=WS2V< ]Zҽ?+uu"TbGV֘Hn{钩=veNCYN)bKZvBN$E^Ft еDaq5`X_=927"w<* i57=zt :g(J.ޗYf>3'8QoWf)w) gYh^@8+RdZ / ʗtV+)>Zu1;( v0tlb2&J3Mw/ҏ #9@\wp|tff]dG.D1sLL l3u&~}蒏P*DȨo-VM1LI2nؗ/(АA$WCgqz;ʹLBo\ N&G_͂.{T ==I᪪ci!Rr^\##KLl!EA ~95؉UAzh#2$|KmOV)pDvɈ$`׍;n 1&(&k&4%oa^1n c?)a3t&&JccPFbO49#6̜G;] tT׋T.OZ C@(.M]8W1N"5ihyq\V7( 8>jڝr#^#g>IBB"Pk6!kuNIl|tzC ԣWdT^fM C\ K4dr/X3A43yA&coX_-{9u39EA/ȫW i%KpsT!1AV7?[7)73s'΂HJ;fb mbfviNߐny$ od0< ^+,HTms\cUSQͺ-j_cpaP9.Ƨ > o9Lzr?8ECkV.E # %/eӤãOf ՙ1<[ϳL6y?3`sby)(S<BrtCzvYYþV>:;༾iY7|qL9|Fq"TO4ts̏xڨd,bR@#3-o]YU)-ud{Zh=^UP+[9a>UOɦBvQGHMu`-!2}6F3(UG,c"LbJTQwM*:wu20cP1rSH잩 BDd659M^/YYܽ >{0[hy/y"ryGƹFL&ǜ.qϲ0!SÇ>nwGkU+Մ؝CMddYl5=}[YͽNV+'Y g2;59ڋݼlASN:o=2P'\s +=LЀutrJR55Dpچc].??4S Rp@,_⟖6*9(S #X>hy4"NcVIA l<'pX8[Inؑ=>g 䉡w!#wNi5!#=}+ q0dSpEDZ&}bTb ~}0_!-#yPB! 4=D0+I3ݹkNqt Deh@CzIRƑlvZl7~N" ķXn/ZF^?PǟѤ|3clDƃX%N֔/0O! )\}>[Π=2waKHnѶ+TW,ȦPVU_$/R^CbRχH^|@g`ibD[AZ לqJkE[7EeLP$Mw?i;FfD>֕7ܪkძƃV}ŗ2nlI*1DE'A>{קҟ#k:Xps +5D9J]BUNS@ q|)Gƻ#]l_Ph_\10l1.? mtUaNlW&. '_@U$yiS.T#qlT;/N*,s:I3EKg Yug:V{xg',< :WPp2F/߆Zy-fX3y ;xۭ?w a^I G 치- 7϶>-&\4Llc V}djT}SEOs %\c^/<s-Dʔci3$ ?lke(W^sY4 #LfW#Bx_ 3 2qZc|^_N8k)mNzdBtZSnMZŞ+S[5GeQ:k)ܻ p0LXk5-UebRD3A;WpL\8%J^6͒5J{OEiz/[7lg4Ւs9y햤Og^gN5'h$>w8FM8yW.K/Œ ȮЙ&OPkԩIP2`1YפaX|N ;#g;1򆤘|%qAQ1ʙjeܗKab}O&&uh(&@;+k^C,jf_t' *g>K~6`3,Eu/ĭaf.j#B7q}O!B!SAFݸ_JtCj#/q##X|T(>N18S֦yl "buѿdj'HyBnm܈B|o}jpr0D{HJe#͆b0|X/ӟ f`F<ZX$_[6< `A/$MXdВHUJ^ml@魷VbվDQ, #|<p!(\X.7;E1GMF4(ߥMt`ӆOE W0U!oX[bj;.^RÓ 0HE醪 U]Aϑ FfG˓E 6T#r_HԘRn;!EV9iT@90:5?OŒ07~̈́W'hSH湠m,N2K@s2hOKn5*c 2  #ÖquL1љouc~l,DG D0}xd*ϕTяeہL2{?)wu =lS]IJ\d+wd1Usn?8Oa g9Ɔh|yi x%IA_kRHK}qD> `* i7UH '^tf3'+`]r㳫MyI~0pA_N“mpM1VY)$3T 36)OKKur17*ͺh^ `DΛXB$U!Q-Wt! J?١G$BhWXRKuz mUk"k>y?քuV_T=MP%fM-׀Ql:' t%Pz9])g]؆$>?"I! t?(+{TBZO^*~P~h>  rlmc/Û"/0Cz&rBl@]LVĩ.o/^}jq|ٲ/F[Q]Ɵ(+q(cJpUdTjW0B!K:k}:u(٫6n}/TXhV >e<9cUFƀSikCPkPէ@-iOn{ωB$gf*:/p!ğiǤ ̲$/UWAO3=$N|!{?C&ȉ+aEGӓ^I7w(R1m-ИO.hr[߶nnD@(,`(Ѥ<S̭&u_ o&>_q/\hvM\g|!R1ӛ|#G6H3A)e5ڛŬ@Sn7f 1!<q a |aT`;:з@ 얺 ]v9ʟ w|FqPRuF7s<7sN8=x|-G K -ga&rde#uJq ɲͲm'g u`E-Fpr7JvW 0~'<~;_ *[|~nA>'8*d*d xAgPſD!ihO.7B^դu"x5i*EBY\i8ԤĦiYgp P1Dg>G ln:JeHE'~h.֑n f͂xT . JwċT&Dt]O}&<+)m&ڪ/͢Ĕ!_cε`@hw1T3읭Tcey!3#]`fFX`8v\Ȉ-Gh/wH3ptGj/yv3sPaڿElr#7(OLB>x;y`-2;lAl5f'7f{t7%h^dWhvl)0u&_7V 'Ym2쾁p_.$ǡ  dvhX-j@ row)hpá-[ >v=u _iY}+r"b -lxRB^ŸP1($ig߶R@,$ο?F]ef%3C|XK3Hwa.NElZVBe'"j="ے~ J8T*aovxYԯMc@0oP(Ї1 \tΆCp vew}c à1C7{:yM^;ff;e܀C_J?Oa1"G8?I*it4l"< dG#BTRc7.c ˁ4?m]yo9e=%40/#V;_HQXB`Fz \e L>]sݓs6wGgooacޱ)tR\BJU|ܵ?SaّJ[Lj}O;p]zq\ÊK$* g\b>:O$]J@c5]9ƗM<&<(,`9h\Mjqtj]@ϯ|Rj~1YĊ'-7.z{A3GҮQ (]J´?n䫎 )˥VPw4U3<Fr0\T;SXMݕkQWfUPgFuS=D$_u]04‚$'k[lh<-osvB{>-m:Nn.ppވ.Ȃ*Txm 2ܐ:@0bԐ6j i|pwRbvrP.PA퓙&Mh/Ǧ}{rȌ[땓K]Ps4X50(\Bj0#),& 1Dv^ S`[\< 9PF{Vhϵ俴z=|ﰙ J%#SOCjڟ 6 ];߸/DXk‰!.Еwq }\E/w_ #)NX!t埩 gI 7V~CU"aiK{e&݄Yy 9 xА )IO0{Wv 2x k78tfx9㹎ƦZO|֗+G\D_x!,WrYd/::p,}3t ڴn+s5TwR=OD} D*XuYQ+:tY~i4Kw c^ =כ!>.DP4e B!y$AIY52cpR #gLP#~GQd]+AdJRiRҌFa?׋> AVDt7^t dk5ɶ0:OΊMQwza[ZlayT)kFLҘujGJAD E)1]C^ ]ִXy шCJ{{^/=ȸX(E8.O(\ٕo .P *CAgr3`!Y1|\@7:L:ƛyY_gBm5uΚ}[xjV]5l'+KE>{h ȎĖ@,Mp_LLsM:+vMfԃh24Gd4R }ZxXQGk;M\RX*T\+>ߐX!8<ϡrGIsP rZ%[zw4]L‹(B)-9'bR(מQ3ffM0~% 5B?ˊ`y毙8I^V0P@=YN&?>]o)|;ܦe,ᬗh-@g? {+v;j E"[]?yݩg/}L1 YW''D6t׆FJwO1v=l ֊Vp22ˀ^ c#QW=б)P#"IL܋2;ZqQ: zы,^QQ !J"O4\ZDg_Edk0J))r1 '2C^ۘt,[&껅Hf)"B/!n`T0NyB :bIN^-u[ÖrXӲ6|ô0_n{l|7ZSL_A)PhDsHYFw^fJZ{z]s&K~8Vک -o?ȶ,(psD Q{Cy E=KjGWs1>_E -\P<[4ޔa~j!! g+ T(^{O>I16;SpUͭt9?2(^;_٤;P d "|-zl>,t$<*@aQz=a?P&klPfE~E5\=H2yisKA{ %W&D;ȻmFI_C^ᐺ08ӓ@Rg;Y0Ipb Ii46{~gdi~$iO c]pqrs H",ʠɠDVg6r<~&5mu?* Cg;hGa~s;q.r/tfMUMKE_$ +a;Sڳ$I@ |󶇨S,rG2c=iڦ[ Ӊ/jQr6J{If˦|Mؤ?UsP>cyq[w_=OT?6 Ix8E7-5>x֟reGNCץ`0!mC&o7$!‘n2Ei?:4R9GJcԞ<:!("3/ܐbSUAU(WUZS.œ⺴ `!=^U[ז덚G$L$n (o0^ yrplH(,e[P$!sQ2`9w.]=ː͇p< tM?ͽDԊI_!w'&j*O~5HP^{[ᔴ%[BsjKR⪠څ]\Ui}\:DOp9!/h -|n=X@(z} o1. T,ݭ`k'CF:k %eT Q?&NGmጎDx3 X]o7>v;?y1*Da }Fe0N* b@}dcfA<el1`* -_}x5jko(;ܼ0gҭ:t?\%sQ5h]ŪXO>!* =|,9E&MP n/kmJ.9M;A:0ܜkD&#]>O͑3)If@T-@ N=aW5g~TdOfAͮȝ$&:&]m*l^9l%c QNǒ2&&fڡaWCvp4#1JJU6_BHTaF(IZK%_eX605ͤ!6_G{+ 0><2vq:) #FܳLنa|Taɲ[;]V+ۤ\Ɉ#v0:]1rVlBMW}d020+R$Rc 0sKHrQr$޺d1-х\r +/y3)Ygg٤R\gzR*iЗuP0r Td])` \YaoO)Q+F˨Mɷz֨s~]Q3lReFSv-dwx|> U}6MJ!s{||K_p_Jiv]:!rIZEKjۆ)?az1d mxN1w)O\Xq8`% ~jdVP^IuÁ&J*=RHĈjyɯz+$ +Zet֪. }O!@U͠~>'>8U0֕`@!: 4L'@5̔M`G;vQ~ÙlpjPZ+$)w M[8+&yFӊq'> ƨ3nN_Ry0W1P$*֜R?@:&d.ň`,41_S},^z7lymi<%(+5QG xYσw+ YNKp#;ςb?{F[H[|2-A- j`Usen=n8s~O`NgJ9='~6DMRE!kJ^-kujA2%֘iKxcs7Izm|Xd5Dfco[{ˑ%}{\iMb SM%ɫJ}|r]•/v}5, t=0I]FZ8ܵDFڵX+\/&aaAR^V7瞁 ^ !΍: M=ŒJ-7? V$ώ b/><~:`? }d0{nw`!e2-߅]!lZyEl3 KBH@LLdUD h˄a #חHxFrdy;@Gr߀e$ࣙ%S=Fo"ԃ6|ʛ ncvL(hwk3WL_C}*T;!x7l&7֍Rּks~Tr֘p#ZY;RԆ$/FFRBo[rZi]4yBNi{@Ysh"H'|[ o=k阼;kTa)~ 6bYs<{h^Q"ppX19)n{P  MB>D@UKX- RB\LΌ͟mb h<֞MrMWvJ摌7Q^(fv54$ã!}s^Kclxű%4SC-<?dX,@"8ҢXq{lad8>'(kTc`ɝCOCWd0x@oG]7C, 0ll8W{Ļv:֌$1*J?CWw/@չٛgt/sF;z,ȍ(:cN{mʒ+[vC ܚҚМz!I:5J{AM?;8.0-^u$3doe{F}C`d~_\ߡyמ<;&jq<`2'wkyሟ= ۅ_ z:Byֻˇ%f9 ԄjZ%3n`=4npSƧ"qL)ܼҼ@l%^s +ރ<D[`wyc$8?TIF %u uKikAJNMR$$_)jOqo_^NڋB1pavh > x |sAbuEfupT9v:\CZg5K|gs(B :E&0GYGI@BAg*WaqV AaP8=,Qu*|d9$ٱs xsQZ_E00fǹ(b㷽u^+kIuϽFTozZy2ϣ󆸃 %u' }abu"c:@M$όx i̱$ 1WA`vCҦ;~غ$x35MןKQ8 jAr| M4 cSӕ5e$G.s%F'etBAt zݶh<6)kj,: j`Рeaow5vezCY{<)H[rtzf0u*K5ﴨ&1q ON'iǘۉpd;E$O SM `aKS= r%rCo)J'MZjb{:gkV;S zS!B01')ǃFrV#Ab 9_ ap-8_M=cv(IwՁ8._pa?+ QNu2NURת!ӛscnݫ&A., B. sAaZ:ZHuWqF E8%zm %gmx[>G̅$BܾD-aZr{&5|CwW Ͷ/JuAONxL7dkqUy-Rgd1M6  a4%#H= ]s%$_ 7Z`u ǚJ8V_D {8▄iԫz'/[q$~RvyDFx7/qgd޸EiapxM-oM^09˙ڝNRĻu ['Νҳe&g0J>gWu^,zR%~מc֟}{Lzb~ BZF7Afҍ gԎ4X1UmzJsQRm\mA18Ni NqNt7c PA_65w )c ŭ7g o8[2?%$ W(]A_DQ'_`6$鶕5(=t`!SҮ+_R`S~98[Kg2Z#E6 VMYFRF"P4ֹnBB(yu&ŋ^*Po!?]}tEUXg}YNE)'LDHcIPNk﹜ɩF<Η\90&GJsr{ڣ ω2˚ +|i?Rx~0>b|ٺsZB=Wɮ~5y}cSYs唂"q\07 gժ*|LisPk᫖Ԛ%]T޹=f6`Zz * Ҙpⵗ*^>S,9z .Gk!I[fq䓸%ۺ1rDKČڣJԍqy?FX 빷qc!7ϑ5,ܒQ%<[!Fku@Yo4O-݊(ѷs; YXs9 @nrlic>vDiF|6T)M#U%]#WRAUfBO$7u?/ӳLyLnMΆNWPh#T՟#v~B:G+!ʶ\V1+k3 LE NyH3ª%ESr̭sݴ`ybQӞ$`yypβOh{PH1xB-X>x4׬w)`@~XIE5y ݌h'"D"(8e Bf8F7Nx&ǿI<}R\7QHhI=;V.ƀ7v}dDr>KWÄ`bV o=y)i5u1e@1A3)H3`Գ\z.N:1M)-݉4CINKؗfr`{Mv*e?<)фH *p1n@0רsZJRzasbŸ-{`~9Z9 >jV"*̭j{0\7˜\)4Qč+N~vu^(Yg^/b K>8)eƝ'^(ǐ 9\;F؂]ĴtGG>W?#] b-e:8lL,V1SI{ENg{#7]C 7BU v0?)^|S[Lu,@(VG@dN,"Hjsvjhq Ǎr_$۶/v܆ P>5&D.' >E!j`p 1_GrF(u13\q㯓UJ7@i=!tSvy/}i-+-"\榀8wi6طzf6Ќp3 DZO7GL$UӒ^k EQY *,Q"V{rC<$8ÑDzn=Mcr-[m`6TK)gӝt \&0՛򼩅)D◾'qQ1'slg@i&VqJ-(%^}n[>m &|i'i&Y?\l9QSX6U{ABV%X&^}e3^p!ߴ;r ad|A:fnl zrAz. <{$8Y}G_kWah- ScJ3.9C=?9GtLDG}1n|*q{]W?;  EuFA xTi b[6>tЊ"˿ eOvUڅ)Oy(-{~ɵVUaT ՝ٔ!/kWmA#qnmAJ飵Q:glOxo ē:zooB#Ǩjя 6f ˞ٜsfD #fNAbˢ+%S%L۞']=l]:yhQ7v0<+uGt0u"! 2:G! B=$++楃.?7/olzChjхzL3nb U?Š0"C::sZ5CTmF_#[QP ]E["]/s Gt2paž LsctLj;PWq9V͚ښ$z8% Jy[:RAlߚuWK 7 Eի3' uk'>=zKuG]m/7AѧA9<cu`+j}<_?zsűVdba/#A6p:{j!0Oq) E8?J=ӌ_ bW7}ۛO&?ܸUONQQ Lf)r _@k%SƘ%Õo9892+FAj3zB61PIK;sݭ#`TM d8t9> V`H?Z J%ZaۇhUҢҕDvxNliHlZ )0>y,*l _U_!{ގy2eEg(82SSN)2kġ;W,j:7T+f86FDXc 9ЗtFSG{b>{n~n_k6O `+훿U Ge>!=+XLHcg#|?EG6r){ }i@eq2ASnĐ_灈;Y_Q"rLZc  u FU8.MokVXIMT?O_;ߤ(N>dNU}b2], Cy@ 1/⪜*5)Ǘ?,ҢbYRŐPz% T<0]W#k8.5Ngafw ⥼|upf;P _PYl>[H09|]92O xABTiXe?0rQBf^49s6QIՎBL-P)&0'G~t ~˒ƙ 0[ƓG{lv n]{[> ڹP^x\TDMVC"fvOp4ZhAemvSȅv _+m$Q|ׄ/MJ ʐ_P*Ek&LO= 2?e& ~=#O*LHV%Rϫv"Ɖ 7bS *Gn#Q]`WvV](n֗0b"yOqWj]kdd[jݛ {rcͺ|5/R@m_)/ԣxh9RۀVĊo{GH?k۲8:Sd> O)8rNj*c;xg$Տ#2nFb "+K(E\b.ѧ#Y8_@We#ƑZ^~Ǩ5x2{0Ӱp'RauG'fe'QzNTX7!YmHGƷ`:10] 4툷]s1[z k5/(JV?(:,7!JwiK zpt*KM JP[`n(I@'kc|; &|6 Ms@=` aI1#7{OdiZP6Z?TKeLr_x]پyMkq (WRPKfVrJ=IOXO[z}ٲۮn'ػ$A3Bn:Cl|GRG7&.Nmz)ֲ\fE8Y;_^ՅvwPMg2Ci.JP;΀9;/6;ɳo2փk6gz"8d^IuF$]Hk1񙃦>+yU m9*>gLgc@Lv`RqP2䰡סO mI+/,DHTjWQs)9֗~ėu&t6P,;zf -K8IV7|e*{'FB0Q9"Xfn2,KɡupulzIRNkOmAjZA=CY 'џ]MnZ&5\bwig[=5z B<|ҫ?Ā8R5Sb$~ jqn.W%zOWR붇l?%T*Rwx=:. ղB=rM-7pyIf[[Pd`.ښy 8(.2k iNh#N{$SW3!+1е#A0TDY'x&c+|عkZBzp&!C^P%PA$ "KEt '#ˑ6m%]/>OIeh}~ ٦UMYٿcT)xiPL?Ӂ.G}tIl*[ԔYUzs< nfU)`P>4?yYZ>ӿfp .N={‡e&֭ (s*5]{DǐbWt'0se%G m\\čwyџHiu(=SHz)DZ-n-|59eL0r|@RY (=~syZ#:+u:KO%tP܇ _$(łKօ6ɳ̎57VRMNK{Hpvh>|[0g )97y *=qF6np(8陇U." :zΩtR`"L1Bn!Sz}?6Fgx5 ƭϼKTHձs16䗝Y- <_@¹ bW=\~/ʠ1F;y" yEᵾCs3A6u# ޻E8BٛaÐvtKUX5\-CقWR>RۀCС]d2R뼒J)zI$&Nb]n&/'ȝ飅}L2腕Vlj䌑T>6x^TuONi-Ł+DAK5->8o##d ڈL"a1:䫄ޥKL)!>aɴ" M|Y@^q֕; vTF0hQ[/>@!QFErgQ5Paňz%VQ-,.9*-I<>=%W\ÓfŧW->g!3M&~Jeg9+=V<}Q#J`W &2lLNpU/u%rxU Zsx"eHꨱPٟXre7CwҢКo3t=R)-_ZW:녠嫒VFdAk?丬`О4Qak`ʄ+_nCtüoeyWC/RP(wJoyl\@@ 7jvhw` 3Mh|i-c_hZC9!^X3.fqT~{oGtn hԨ8b IwdzZ&LYXEW^*v&fv(O|]GAn=\60eh֦/scu8~n9>w\;KRM5K |9WyW)F͗<}y<^\Ӈ08UrQ"͔!q#ҟXǩ*T قy#_"H+ç-+8n BXn{  տȈ*3^+%\$б~o< 5R~ J {}[{]4|q"1Bi=f%)4f u>}aDyT]AUhn б>SL"VJqR(kA1 rc#N,:=$#*mȈZ׏MR~YOR` r5;|!nɷI CmmyJvUs/I)sٌM <o20}pR TCQ(ӌǍ~tG"R`bHjEr~LCiλ 86{AV!GjRGw2rq|d7 Ih(6Yok,CDrqVOL:Qx+EtyJwyC0 Ͻ&b R6B&JtY- O!jVkKV:`n"Y3! GwQUZ~!JY!ïD,,KAyO ]δI)t0xe6ػBmbH-}9rPO*010Y"* <.#n~7rL7ϟǙ643< #g}ISu}1~"R>x#ǐ~ŠK>%D>_e0U>2OE\PD%@r:cxE!t+}k:dSΑiZңH01jDOw' ɟ.Nw萉vyo)u_Y5{ kT=s!7*,ߏ~g>Ԑ9d =V[{dٲT@ l(\-\{ l9 glyF6s_. GBb iJqhH': ]hJ0PՉT~Ph 1Ғx@ p3M8 @toQԒI"0Oa$1ViBC Jl젯 nߗ-hʓ- [dlrve(wva:g:/%t޵1,wV͏֢f%u 1O2c2ZG,Lq=%#C!4qQe&Vbiy^cК[l+"L@UW Kw1,j6) Nބ@"~gHb Bc:ȉKiuCNWh(jەF["? 9+EH&sqXvWFjUy`k1!bUE}=;v#럫`N@gKWBi'<M5 {UR+}ϾVC+3ʡ֒9HW`zh#<ȅmQd3~X]d`!>1C@/ M6v5oz OzV~ %`EiO/sUR.WLbl%KvJO $9D IOp8O_6ŢġpvLn8<588^oCV~y. UHs+wttpj"yl1H6fRX\|aVNG?>Ld{HrS*+tKyb*[\΀ڮi,d+9e5@o8S9o_tU'+8%Nn&^ e$lJQDTiP) "?]Zsݝ!UUfp>~i@x #C je'lPn<'IQqed{|le8-DK،ntf}Ąw\DyZ#OpBMc4NtbIAn͹0`n.[l]=Ǘyh Vfh| 1 SAr#?e$ RLbLr]E}Ki8|Fևgy?xoO(PlLcGsW&!ALn*.eMIU'YY)ej'Q4 NCnxbf@n኷ݧ_8lme#CПfca`]g=[>63^̇rAqg4x:Jbk='DrYuPkjfGS?1PWF_P$p쎞RWCKhÒܶJ"X?(Xꗍ<}ʧ~my>:ls c@ 9rZ:4:B' r 56YCY_.dSOA2ö2d7ӆ J[ڠ 8P E<4 oenm&LN='.ފiP `U*\!k#(C_xC.Ͻ-㉷ UtGN!ӈ&ɸtqQPj2@4/P#u:Z(^$~N P B3 3:,[BQ~B(A/&#$D%Xu#=GNDb+ʙB(9 !.-[>u$z\kuպbꖝ,eM+(Wt*.Y5mo'ۄvpÜ~N7]do\2@< 6"s4zUF[\p\$Pt84z~?NNhXQ\j GQj^!I#?U]=e3#R$p{% : ]=O$g呖!~,[F  K|' /0B:﫭z5EqaCg[,!JJק;5΢T^"ZZj'bx@GĿ:JtVQdUANgh[]_v9=zޓHHQwn=r/BĀgouQRF\؎]sPƲ Xu_xT WOrPY4h|pzmm^r'I|CKblY%6s죸b׾՘  5EREz3SB7&i:f261a&׭^VuȔn*3H4t1I]ʼnwjہ~v=ސZ9aXΙ8I2Ro`7 qU|N1 4o1f&sWC4ڸnV&A4`kK2U,@Q6鵻=ϥZ-ioD[myҦ@w*>B1.*J}Nk-Y?q4̓UAU03NLU,zdnXWxz4yJ=<1[FFߐ?7ݕݿ&g^aJXzۗ52Ax0+ɐrh cJ{E(]4)&7uL.1uj\Ӹ+NصۏVNEIǪ5t :r [-_*Qver!k}*ȶ!dE6 kMɒ/ݧ3s/Y1Me״-SOc jFYRelȇ< VriL)HPa1Oev@K;>RT5=G(qogM~v;khQ}6Du1:,HZܑR!~:~ !]'UOʞ!A1 mX"jq/D, *";Xɳrͦ:~يZ%!/Yo ENhFkqD\A X!}t4*;5Q}htm^Az[~b0œF+9a8!t ʹm=mٚWY# 5 M%cC_OIn?.XuN3Q`%EҊ|}t7W8P{П}{g^slKAG8l0y> ĩ7Jt=bt/g)8QqDʅSʧ @αߗݱII`Ik>"w{dMTz:\869KXJ1w.zbJͳ+uPq0:†s ɍc(K NFwEP5[U$Dpt3n0CoEuAd+Lj!|bz۹hyfݢx7ȡ WFu刑7nͥwqmInpL1)*y"sڿvK䘷۵RTUy#"S~yBi+6Uk0{aƁ#1iU;L^Hh',t>ofCΝM4>f$9P ev|6#En|@9i}k2T=z`B۸J:X_\ y 1^8eY3~Hn1h,LϾQdR0bq2B`;7lzcB%aPߨHūOn^[2S;ct Q{v@fhi HczhZC kK…e=Gǜ [ ^kAZȁ/u93ڢX~皹ԘE$7ZGIir: >gxzd=hϗ=;7l ӏ `'5^slywS~ ێ5])SOSl S|!I 4a/K?SىJWp۬?zܳ4tҢ.}8'.o/W33,r5տ1(>I+$$rZoXn5bծ{g<rG0qynm齄|q#5& ,r@. V2l]dxjW]E Ӧcq>lqEf9O PdhKM("I'G| íU@b ؤҧ33-QX:+3OF{ύVEc`q=sE4ؤir%CNX{xWqZ7 hh O^^/c*+ObjMZiPCe"L+iuKg'hSs8snk9\  -iNw> A&Wx(ItC9˖DuVS!B|EM6 .P P-WifTv`b'H&kTE Ϩ0 "d) ҇LY[TGL5R B\@o^=dl:e"ZpG;X)~;2'v|T(,R\L;`n thfB]RAtw/VQўsM. Ci%?#ėw)7F,Bt/"3+)|1T'6yw5EN0+ѫJ0.'i>cc/98l",7$:O]y:) t]d_du">!M"B}Z$ɫ i_5?[*^*tn^;FĿ%ӽfV[)$kb6=r&W֔ϱXv'wZگ^:؜C5OTs_J&rMU /j':'RA*kw$~ Xp"r|k1NerUP0@܃.~ǍOhxBkS>9BgG]#$IbWӗ-J5n 6b֥WiXU&- QGRiN$'~Yf(b3qyv1)f.E!sF (i' PEGC&EGG5 .&#&~/+.H zC%eՉek;"澎S&?0ֵ Ŏ%hU e#:`%?+DSc/|$sXؾu^UhS." GjPM]_g:]ðNv*Ha]X-Zr z%Ŷe~?;N__h6OJ" X-z#n$=[TzyJ쮌_ L֞S  ?۔Ncƙ~$"n[{vF>R̟3#9-YAt"m~d ǂZ csYե/` & .QZy5^@I%)W'1m,iFH$;Q:e;;~u1}9n]-0pFgrZ3̪+.ot aqJBhKj}&81*=Ah- (c>LyɟbMm#DRx: z*@e:t,қ<}FƓ  v`n `v :zUϐn%yd<ݕ|E8SQ_ OJwa=hURrp}n0 |RՎ\w_δ,Kխf;!cCQq ]~pڢw}:Nw9f|)gGkA(BD;ڽ{(긣UoO֭c>ݳg{J՜^pt(UWwEeGٔ 1ғ"YN{ٿapa%;Osd̎/GhYpdqj-Q:N2U:vF!fh瀴J` #eFIS[|Mwer@N¿[d㪗3wk=8ꜜZ|ۏsvmzj;ϫ7 Cab4rՅ@;W/j U\CT&yt (V?R6ڄ[zg}`7O0iAiBDh.NְBOɲ9Ū4Q+lLϠ;`δQ២3b@϶+c !l*/2[+|$&F5韕^D+$up"H,Ǝ, '8s~dss<[+Rlar4 eTd&|az9mI7;'MX=75IC硝آpb-D"za>S]ߘC]@x_8eӥTBn:HOVEzҊ f}i\\r5uX1:4 U%և{nC/Q^qʡ'|S[K$=`>Ⱀ.l1ׅf/_>?'ljUfoq."N_ISթ+p/lf!.c8[I2GQ˗^8  ט7Ks @ c&9d۬[Eo#IkI?=$! A0 oi,m%Wj Y$8C k;e՘B3#vMBTf80<u[Z43DZ~N,:_8)e6gC0}_DR Lm OR mSet-|ӑ!uo,m6Alp0)\&$H/z!rZ7Uye0? ;nTڬҙ;J߿}@$sJ;+;"w EN\k0޵MrH3 *#a)7W,MIי˗bBS_u;?CHJ;j|yJ~~ut#ya},A_r׃ x\jE&8oKISԶ*6?II7IM2xſ]ufGby&}R  x2\(֧ _qLa<8;V7]`H-K_@SQ=&sÃG^ ey{s"ם^s: ^0 N0NOm62ԬUJHw*£ 㰿ԐFŸKys.:IoqgM[Oĭd*:>!, NwZenNu ,ߙr"2_fe3]^,N|Opvv3+s>!3YTbGߐRW)O-a o_]|pt*۴8m遞NVT (oX]Z,sv(c(Rq&DlKR԰8l#opҦ3l H-L&~&4Xo8'%=rv6^jӍs.Kp#KlV rHIo99)х;8d6LiO"kcBʮ#@/-=! -O-!wqGuvȍ]B-DTezszPR5o$y .di#Sx?t(c^"J>fK%aMtȒ-z&FʋJǺZr2CZpYy%LU@P~OhqLTDKnRGKbx.GL?@A$e ;`~=,ZDd)Y)pJܻ SK'H8T, 2uJ&wD ę%a-)ޢ3ĀcSK,dS1RR3{ N6>@*Ue/,P.N% {Ma5\E${0h c\3Dt>,Hs+t牅}TÝѦ9hQ4MO6S>{IH?Jov3˽ )2^Ɓt|;up/ ȞF'mGPv5e_ K@DHx~l;%_b^>IyӆźPu$AuG@*X]Cu+IcԭHTE=}u@q뿺/zrVgBe-v5*@G `\,Oހn9wjKiBi8הiRμVKˬpne jO((V =`Ef݇ZVNX'f8Lпgk_IkJiO&4]J ' YEaR'*45\w2y96Qg久}\axCتy #Lwq #߿|X2rJN|R2X}I AqrV aלyB/I:3b$a>ռy| x"WE[HB~)տVH ZiKMeVY0r 1$gG'Tbe'@@(*;UՎ ~@_ !5(Fbmqc+ݝ"9K)kyE5Aik`Z΀/K}ޟ2Ӷ u?d#*w X$/WB+ yjy|=FBT=@6Lp6 3^zFeV}LhsәIl È _fvfċmռ7#ljgzKj)Yb 1<6wYM)nx*GZ|$K*&n5 晶{Y0$~~[lnx uvf'|ү/:VCopz-|A+qXA6d;|wiQ^|zê.[u#^**`B v0/OrK5R͔).NcR+!l"-#Vl]npY|@ZU'f"Z5>2تwayz q)^XӦ k5t֢YJ5$%-{O%r 9OgCy\mlCcf=1Eschz+$ɅEr{qhflkotmnHՕ}@> "xe=9"t-պLmów뽀YڏelDd2Dġf)E?x178ꣲ7K&&AU(X}sMglltK2/?zֱvcZ4 LsX]ZgG򠢝-1m[)>9T)CgZHDg/pK՝>#V}Dd\ٕڧ?Àر$pk'& *VݮjZKBQ+z,+x[Ž˷JR1RS[X7);lʃo:>狄&#-@^k-|ar#BV'eUq'Hx~]g] jW$F2_C`JxVhQn8S#v xB&R/{62JIyTZeDT P;-w/;ه[2Sf c(=Wj5p ܣpUN5#DsH;iiUvy|n}T'g#? *5P{j{ ^w b2NAӳ-4%y2;3*~ Q*Kc&hCY|mLaӆZ"aDImX ] ܌&u;D ,j3mUFY6SJ(j1CdJ޸Y7Sm tT^2(V6.z5%? )Zjp͌UێYnX}-=ѝZtdSkE#fKK2tVok@6h=a|a6SsW8ƈ LDlވTE[[wzCf;y{`~Zoe}cv]Z8qpl) `op 夙 JjY$.'1rt$g+fnJj5vڶtgȰLfrDf%y#Bf>Z\Z2ޚu'eY#Rn34) Fm_Yw@jƈPL]47X0-sxT~} LJѕv,j 1O'C4n׾3v6HdɒJ9b9[8$> bG)ߜ#+y.,mv?2Fޥԍa 8ljd%jU$דD׵GG>OK\3ЦV$ݪbŭTˢ)MZOکl".^`"[r)_,H-f\J@M5 q\[i87̯ |ǩŴz|n!-]}&Y;ujq{LGf'͡;Ov] *4+>O3Y̪مkbVk."N楜!KKv*jDʻ)ʬ Ewn11 AXZi|P$ QbX `u Yͳ/3|eE-% :)Jt|棿br^~-SBʰLxl!W{]bS˫22&-yS{YhWbE~־mNs)3L]Hdʫ^NΎǂB!=Wi~˪gYU,K'1l.^#G9ף=P0Ǻˍ@r)~A#sW:5x:0;FLA' ~|%d:rɛMypi6g{Io%:k]Z AHj"opr=Y*(R(`"@F^hbRյ5Kp0B[N)RIioY2K@B7OzM{2r@madDM$x@+pȷ*&\g ژ#Ӱ}eBfILx"Oѱd_1hgEm~vQ76uΡ`b_~cU0({t v/K-% sA Q {21;)x_ti -,0d)6m/ōy~m WYύ>o=Viǝ3b>Y}yI˥H)̉/œ{|R ־(ڢyI [y`*1,_%2$T=,n=<kIIhKF,+je:#}\=S"]McXo,Y'/glJ 6v}۴;Xn!%*TND ^7u-mb}f5AD "D3_%kk5 Xn:0=o‡غG4{gM((Es-+Zgc+U513}-TK%APf #sdzЊ* JE,ZuAv"*gRH%MBJ|h&-m/~>7]%(>}p5Rtkj,l"ʡ 7T@jD`c FNȡ̋Qqec^Qe4 f?Xz_j3?ƝD\Zߢ S3 TXT*GR?4 5aR':f_̢Jx Fզ+Az#܄Qє~"!Q73E+h8 {k5֧|'uڱSi_ ~~& LNtvP՜}x.J͕;oZk =]?dWk WRmyRHʱRa]M^n ~#{PuJhkU+wީ D)[׸s<=?7ŝ5=>GhR2ηWоRL>H Fdb*)VS2j*!"^ D:7+enޮN"DƘ%Xz?E3 2S+w`_%nDD"k=eɱ7AC<[DA%w݁'y0/vZ64FV#zƄRl̙;cP_G;>^64Wd2s]UuK&CN,|"Ev6K%<|<_r|X%\CyrvB]"s(TP~O>*QiF:nM06 b* Ywrw?rM6ikj wA_\<5 ŪYDL҈W0IvuBjO,R\`IRHUǎ?,i`l2SµqX5yt,Q\t¶`u P=mjoV |-?+5 @°R,ApYFׅ-&LWS HSc~ 5}(8Ĩ> BoZ ~oݞ"Jձ}zLVL Y˸S" ^ =H )'ᛂL}(tqpD:_W- px;0ieq񲟾S#Bͨ.L6A#z@L` 0^UIs^^VZ\~q.lNfԫAt cbO& qx-:@GF3mF?4[U\-?Hb\5%J6n~fx?!f; b1FI![ x\ζW`C'*&a#YS!yF6d` 0aㆾ,Ƈ1OsC0Z_-sس Y{W̄KiO^1#H/2JZX6yHwtlgnv [zo匲Npǣn@td/)r>kQY׋3k :(m4]P%A ͜Q[p0C.<ۆ)"4Xz?erAoqL3o\zs%eQ,@4!5FZ8xh=rN"(5WB0@62\|Aд'Ta J/uQ)cNus"?~Ø1'j@ gODw";>F<jk7R!tab^qvA`5/uF~[amR5Q4v xxΖ!T$bM$jBe %P$X_[$Ǭ vT.ZJ訐|ipS 9`=C^|~IG:Jc7u!q#1;weBUa.ϥ}7DY}akn=OAubs{XbN jfG3RfPje]#kx]^G!2an࡭T}V能64 .ڵ5I%|tc84c`\Õ^v g4n ^L T{O^QŮ8gz-Nqg*b^[ ]r+0]̢lBg Â[tAoY 5jP%,t:;i@ȷTl>1Vuݢ0E,bp*v% cO.|օ'`1đOn(IPBoc)f-!!`!?N| )ilӆcZ8٬VGeӞg[#8*3%2wB]bGd.A'q %{ ?.)͢o: 4xmu#Ɂ0`*;TWt ļd^.]`&#Zy0iMr͋mНh@ t X $w=BKE=m~mbm\$$Y,34S ZJ˴9!'7u{@e( ~xbr6pyLU$y*!6-915\!~kqUq2>Mcl'bgJ%=%Q*  þxKw g؜IOY_K֛v~ vZB g*^yd[{zn v4sxl] y/_|M*֓u+u$4X?X7mT?^,1]7 iEd'4D| |hjTw%n1eP,RPSh.n/A =2AOo9jHAr -P<.D90׽)=x^{{2h[3h|T&o79ToWgJbڽ8{S:unJ^EKG A7humWHI$I.g:R&FG8;`rUN8-Y$1ZԧX}#r)+Pې8%"=Q>l{*1=k/?ܭ쀒Ds KH} 9yڙPT4d=lrg4K85-Ot'-Um%.Vybw&M> }?fU0لNF!0!{0?g*kԩ)GX?MZ$ ̟ĜqY8.KD JYP.i\qږ!?wK-U$Zo#*,!d>u.~ e)QŮ?wm>v>}p=0la0͵^0 fra\YdgUqzÙ 28 w;*j^kEs%u=Rgu岸ڸFfm -);ȄBvG)*n9\oQx,2ԁ0`=3\EטV{'!Q<1\+ǔ +VqJ o߽iz^g>I[+W0"M*#rjsQ ZJU"1wo226Dz$J`bM&Kc_|e.chfY5޷H`pZ }خ-lvBq<31a~!bZ': P_gK@iS,WPbB yj>Jq@A#Wv"Co^ ĩVf5Q`5V#[*R:60_TDtlMbkQ٤&|FNX$<ډǪdlYaMhO, ?Oؔؤv8h&*ǫré7@J@gޞSCV^˥kK%#{.F%Ez5[1+TD5- .8%ݕ{xvt崟.{bޘ%qx폌I W jOV\}|A,a$kzn:wmzVu޳A 2cjpq/!b+lB5-n}k']`DN}C2*Ɯ%FwvQ'sj%{f] eiNXiN9Ty;*iQj6MsX39gg].j?wd+Ye,E()ve 4}٭?+D}) D< Om`񽭮>`(zӕju\vgg9>u,k==X}󱀳&?{L7~oy 64T6z4{#nvSz@Ԯ\4eF4S/,_E| r*4XX;*GtF$,?;| Jϖ@y^NBAu}|֌ΒwzWqWdS2⛤D2LZ ,/54mo8!- vM7ꄊtkB!rir0n.L6ffb='bŎTB`,SKaG<(N(F 0= 6%R3\ b3,@z7"1'M>Gvp֩ sŖ jSR7팻1Yn^Ji,'\]0jN}ZM/ >`[/_+Ց02M2+l{|d,Gc|(` /:eQ}ݠruk.P+Yj9W)+P ՖeTcf}ntr3=Z@<y9F>pv,̩|n#~u_Q%<[qS,x?R@GP ܉HWf6shSz 5}yi.-V͈4 tƆv)H|K8Uz1 /iJ‚:*BgSգ|ѵ]aV"bJ` p$ \Ƅe *N$lqeGt$nm`KN,}YڔUAe4h,r͎^Fz&n'Xeayg7FXK ~SUATY-y$!q6Ϊy[nrf8G%wArdV'?J;6$&q[Tx-ٖ7@^4L¡VECߩȴU$cN ~2ZUq)Ho=VeˉFkz,2g)sL.u~:hSPqSiQtMJ͍Faȟ"c`{hHMO3D/B1o2Kq3>+6ȼ;+?vXB6g#xy{,⏆GG_]ؽC 4ߢ~3bcMzz,ͤX.ltx$&Jbii=\]8@JN9)lp}@(7"=LLo!7 Q31=3хiEڡ2)Nizdžvq5~P3,OEb2NU^P٢q7EUM$ oS ,EJ%?r%08R_f$Qr{|i߹ݭ#[#%fߩH;yFd\i#Pv{|+1M A.RZTzi^" [ XHeN nW&: e7sV̷(m{ǩ8qe(}y9'Y<DǍw3PApU9MJI7J'=)dsfY UЪ/PmCQ?#2cnؗU9l#4`}eR.-Ş WqW~TBfz(@Q Y^MJOǼ]IC#"ӋG~O@DP4H}!ۧ! _;l kX=*{jTv<;nKb=9+rc K(Xux؅{F0Z"~[sY8SLTbjӑ.bH,"W! rL\H~!SqmqT8z3ͣ!rc?=_M95LJ*]L˓s  8ǥkIֶʳkθFkUxr»NV1`oj.RIG䐈,]Z|^ R>ԉ 4 cGD&vݓ7k|.:9ThCQ7XWAf+Nq @eDXzx$s\جܮC (6ۤRQn;^b/»U㎔ ´$v) 3Ғ6h0HRd6?\OmJeck7pBeq$ c^8up*8[M9ؕjY#hH?XߙNu@;&p~te='rOޥ]&Re6ʙ7-;0 4%F5!LMad]U`tb{eڞPr%3vIr09Yd!t",D8#z#P?HոkhS%D|^Z08`#"Gj8yK?7`̤[\z[sݼaoDaB' "cAh@+*m ڇ3~0mjXg߹soweTMw;3\8WfȾ5wyl K`dd7aE}Hz5e~珯MD ƹc}|wh>0OF]d?Z8=Te`e:=[d?OL3;׭B2NYIl-?ع. 9Vsx8Tzt%#޸u sUP;9Z*ާ6Ă W˪PoS{& .}>=i4䚔cg֣H)q˖? 2>N`IT2^n$JhߺŷbH5ǭ)ʩծT*[U(}N{_q0hdm6ćlvɴF62q q o(їʐZs)S5pV˔*Ổ6S_xԘNP564F=z?}'[(R> =(TXSS"; gDaǏSh/C1BElOߵlBa|)B~e./$%;8#:uQI8%ak/ 7\˜3H/eG? kR&C1N*S;Gi T_4G={ێULhcv2öB8pl%mE'ay4m%_w?pcGIbzEetd81R.#EL3LP/W_Ċp ebǃf+xG7؋mHS"SL& ͲR7H` F;G>Jy@GJ[pHT:` qfa* %ūϘoԶI~bVST-Fcܕ4lu5,OKARU f N|y?7n@kx\Ru`v۵D";׸$ ͍-9gا6\po}bm=_bͫ!!}f-js?Mt>X Pg6۲|tPKG<,\bь,X gy񚏿$:Td' 'k4(d=n/ߐY4 mip EQ!lq|7_6QyX1{]yF lϗdfj.0FԨWY@Yb4h8\"~AˈP+?KΩ#[$'xY57e?1@hz0\DK2'gN8 dbԔ3mYH$ Ӄ곷nI͑fx%h(*}`"K3/B6Lb{1Y(galbbE,wV1@Due> ?g89 ?=/Dc m5v'MlW2dKwSZ~K1lAz%06>s) ׏1h!#-:5cl)\lRGnOP'=cȯx^nvtWޜhf8,vn9u3new#ʪ>hP6i!)\Ntﶁ rQ(] /#j1^`H9$~?硩b=CNMt|V@ԇY<6 4OpAKk6hl->tҕBF3'V{~yQ˹/`:N06}᷎.̡ܛ]ο:̀w|Fb#I QMay0TOKu8(` z/nȰ-zGnjK-f;.#J==T/ /Fz_!?7I%h]ɒx~FkAB$fhNʙ=-Bs;xANx ]PˈuF(]32&q@O"?՛e.ӇR6YjF9(bɡ_cW8Pr~8~/'_q{mM/ЦK8r oFy9)@͵: _`@/&/͜  sz`j }` o]Cў O_KcnK6$!6 z5a.dPxV{Q͟` &aDGX ~cBP=I;&-mDrHsHL0r=Dtjd'3vD^Ub-*[R܍9[?rm˜l[I @zgrk|yl\smeHr8S);:[ rs7RPCc}-F6-E;v^AH$H'J|܎=eN@4ੵRGlqbقNtِp"Жq}yi}|i]lvA= g}{ i"bU͵ՉNإI:|ZnR L"Ȗ46T7(6k,Ey5iJJ&νTo=@6ʦdQO+(DM`O\t{+wD_[2H%qωfS݇TW"veHyKʡ*Ȓʧ-@ ^e|5/'[$=.u_T.YΏ|Ls6C(Տd-]ᘭIVcw|R3y=\xUaz(I!bd˵.TѦBƤ_ ?t_ jHͷ3xzL7j1o1GM0T!) 3&`;߬-w7 70XR\ FA܎- .\7VMG]V y P/2 )S? lj`.'Z+MB2"/@w`A@(.Vۡh%m-A D"i~3ՄS8{&*yl囗:ԫ/}He3~eJWØ8X=?Cܖt cWS&VAmVI^pǣW-߸A[w M4xԝ\S&@OO%i5PVl?aNWJK1kM'@X+G`mJ")bFboث IչZs*ÍA.i80m[`ϙy.A;o wSgs^*HBد܇1;m]㟦gxUh=Q#x a%nR9aĚ`\ZH0 )\Rsmg4i?=MMFP kibvD:leG-O"@ORs'݌c85C ّ5^IQV%SFJ)~tlvӁ=@EOG00JiݒwhDP#r/m8U%z\ejB B+Z5 ޹\|>J| <^sF ue/aB$c(gcƾ Nc:Qrq_œ*%In 5:̷*yj˯ambm ԉTpp|+M3D˚ٗ`K8'+jCqr CP!2=;X.%tȑD3rp,F@t@G§c$Kn?3:AK^> TxȁF͹!cTᢑR&T;h^1`-:<;xZ ;~MsXxIYn[ *&ɨ0"y (ѬƈvG~˄\[?o4geehD U_kx8#=铺Q[帧 ō{ƙBF4.RqfD쇘ޣ;EoP2/BT;^h O}"->ٿ3Yp,1]B ~p^y2}'TCӚ ߬z*IzzhrF"!Z; lz3Krex/)+q.›6?T$t8GH0{8A/9 UH~d0cRSAlqީh^6kumCMg>V+/JCְAnes6AʰR$<+uLVw1.>x)P`n,٬r X|^FVL'& ",)2VvmOo]q. o./~$#l6|u1S*\DЧ뷈cT5,\ jg:SHiyS U m3=T$FZ~>ٺ!>l6빨̕U|\YWP gřa?"HX 4SG9G߲ݘ,ʸ2^~.n35`al"[*txCڌ<$R }5ЧxE,,Ƀ9D!iCﷂNc0M5Y/c?PȊx{-1 F bA"d_ .{}xmG0|W:e>^$v3 KmwYgJbG&`ocPdҠ, yui. :i ٷ2,P]nALViEd;UI;^SЮ#X }Ec0޵!NXmgZ<$-%Ѯ+4ŞinKgqŰkrN# @ vu  ?¼m4Ǜo[beӣZU8I(uN>J]1bK'l3\ë3r|fٍ)b>d[u0!m5sα<(a *xh?]qV/GZO.)1u$nXo+~&=~r}+eT$RcIp_t ))=R/M. 3o8r '5A&N"]3 ю}cfo/\U$}X34\>|1Ѓr/`RC zr|A`}tḂꁯ%/'c V!rZXQCd⫞ =N۩qFB{p"o)slQ1f6xa=6&C:L+L$r<6> mBI*,w{tՏ?V\dH%6gY\֚O[&"(BG7쎂>PeW_ o6juJFc4^{j5bbrW&;/:>ᚊk@ R?7'{%w.F:DDY5>!b {_1(%B@Va_{g{XĽC4F"vVcdeBTtw,훵aK" 2لVp h᳝v<і3%A2f+wܞ"d0e1szK >Gү q[3]plxRgВq%hW:rS:~8Smc Lno_-~sbq&cȯ3 ߄PiJCi2b1}x5\/lum3^呺vӃOU5H> >0\GnmxK>>Tï4I GKӠI2BK`ݳX !{6uBr0ҳt~O `%$źH`9iJiڷ-45?Fd$1,XzѢ+RM~Ld SmCƤ&1({B$ʶ-bUFh35Վ8(K_P Qc#5QӬSa$euL(,hnܑ+RW$!lBQb]ڦ]b]UF)juLjdLZrn/&ŋ<4l\:2fJ|ٽG:>w@lf1bg K (wQ.>1wٵ歶~um>;&M8`yԒ[F?uodjs蒹X, eװ7!Tr_byǑ,N`^rx;RXZg|ӿÆS|Z_(qqW~l_J(1ɳ喚v^}m@):lp˺SgIi]ܧuYtGhec,ga6Z `fR|6R(.'h.gh@QK1t|(9)CC,ЀQ?@i@,w0LsɀAi}3 "`Q,uj[g?e/֫.Xl;22d*ޝ4+4.[֐W)-bYF$RoT'  bUbL~gCYAͬėqBqdب,cl׎ɞ+0/v/ԃ'5{LVfRXkNJWcF-4IZb;ʎ`V ٹ]B0QA2n3 FAއI͔SrbUz"@N9 i8/Wޮժvn:czzcSک G=}>ET}a PT٪ňgx'SnRC2"g 9z)X=x/MrO" R|♣nwU +Z6Oo"RťiNuݫH33bU#;8\ 0_Q50lZn@Lxn W6E A7*!%@F1!,=%_NO=yC?Zl@#IS"5V)74U)s m<F+1;(W[܇~9F eϸCwz{1GK@QC"=KNYf 'BYkiXk:okU`Niي VA92SQ?@zɂSb4iLUt3Ei"L3B$[TJSw ,4X7iA0\0hZxUƺdHa%Y&%<)B0P 䪚3:F) :2.sI]w!;T6z&_/;Fs꾸a28nu BuVKi\4r6<')m wPzzC6-7f_*!`7r%3geOT6 y w=n]9NH4EMo$am9JL1a#GHMslŤ?W|lHp;r}I&ɔbX^L1|:qQ}aw?01$Vw2cí-Ot_ԾiO\CM)1ŠwH7ʽYvdSMo,,]E):Ex|6x:^ @)׆- 371pn66\]B^_d]IsHnwL/w+1O *$(KJ=>C=4hL*ODUN;fdZ6|ázamq15^WRp.SSj J`_!Of(%in\ηfh;@wkn*?|ϫO2`k~Ahr^Ej"#sX65DϢ]FjmRNGejJ1c=u}VWEEGk-%C?.}̆EO M\>6d2R u$犛`k\=K) nuVz3XbmKwtK1NE >r#xX`|KMr{'9ろm:@L=xlIp< ISb8azX@=nX&a R]iv2G̗"#eTa=jDG,؉U?i5z-Xרݑf˪5y=(W^ 9Wtt`,g=F'UN B{Cރ'eoOƭJ Z٠ĊW4 dW$u| t)8e9 Dnvqg}VO;}r|Ir1vee8\rZ"5s>e^Qs1FuC9U!K MEٙ!> c!a]w:&)M*$B1͟ENѯr@Hb׍S i2=OA<7َɫvks8 34ۡG5U#H&*hM sxX]Xn@[A%vQX-.@e M |l6DZЎkL p/ke-p]ȕG~5VNO<ٱ|Yc=BhMLHP7@UW1٠0\9ʻWw_srtY%Iu[A&&#ڮ}MjpiGdl58~V>Tp!zMT춅HYWO.©(eR7y9aI8U٦z0!5'iP PډyKx|zEd(dB \z9G$:qӓoQ';~%Ow)&Q,fwm>l*s])4^[w%~k*5WS~.-%SwEm#`u']~Vl,nJ .,C-kώŻ:ڍ }- S "mxm\_iԷ`h3'Kbz:]3>s+5UcMo_H'gB5C D?ِ&6Xv9ezw-DXRHmtĎuFoA({1s̺GS9'|jPˁLZpI_smjY&M DWk ˸r ,'Z-9Ww /*^Pbc&2c)^ g)Š`$󤨑Yj{'zH'IR`0WfR!ؔOmkO+hU ۤX{ CN8:1-)CE1p/n,*izEvF&QsU4_ǃb,<713z3l ՝#+G7S7؅!Qgْk%]*ĭ%3v,^`BA,J?^NA/ qRRm3(݊cY ž$T >қOsQ\aÌ%Oe҄y//;&sut.TNL5]}ty-9Í o"Mfmwb`TvPVo,s(#5(=A3HN#?nF$0&}-lOx4Ls0!"dR):-w.hCŻ|xlc?E~~GQnu Tnd_4:D&f=kX˛n3S;0 clvPdkH3 辡hj+1?Ry$ѣN00smf܅fQ0LH rVTֵ^|<^ol)29B=kb pXP)B}/}Qr3,O) #ågG-фG=ica!7D (|<ތ%=GTPFvY8jJuX cX!֌e!V"/4e﮿ HR .p Xz6ac|C;غ]I zޓ tR \ 8 !3^`_hCƟ ([B$7-u\u O)[:7*"F(ƸiR:>SC`laDaeƩfO{i HHr'Bi2ZJbxS+F0xd?p JmMDJ:뤭(*A-PN t \bl kTtN?1;Yj;QH˾[}hܰpR9= ׾PE!u591=TvYF Bbj v{B߾ ObHԾƩoG[])\='0AZai+K _&uUfl]ѩ1hJkɴW|!vs}hv*x pcU!:kX$-57 <>Y_ԲŎjB܄^V!E$uW~&̤{ M#{83j2,]] oxXɠ\qX.Vv+m@C:J[_/Aִ8BнXi0@wi4kK;%>q)'3Ũng!SΙ(/tB8݋L*-=qF(F́Ҏ˓sTLpZf+G2Pq/@Pd9.눉TSZ 9~>.D%D*\uhaᄉKQ_po՟%wpРCvp|L tѭ7 foymSR^ڬ֜^Lt37-Xd''$HjK[Ph>裴kh q:@AAYk੪ӡ=u.bBXI,Mi('7R;yDB CI_׌{q.Tgq?6#gXJlnGҟD2/1:0?G< hDb* g^mD;t#|4_=e#_ $oRȢﵤkihЪTۮċ6d`;\b.AU E45ǂ~TЛ?{k&o`)g.෰t('/+.BlF7ž ܑ2ԘwU8B|яc߁ @̯(i.42L:,ZwV,^Kd\ј:Ur#eXXp͐]X-6KhHlssMi:X/MƩC@dX6pqM.d(F(DK?'=J7H*(צ*1//r6M d~/I1pO; 360_׽oUu9z%6u:NQ~3x [r|b. s G=G LmΤ5Gye/>Pڤ c s?0\[!hU|t`N|d _Ptɥ.YKe\#4Wʊ*f r-:S#9IhdaV@^˹3/;Hn_*KC<Ͻy-#Uۚ"[nΐPv i|kF蟣SֽX6E;t YlpD1֑$h6JzPօo1DmodZ>$ cA_[F#qE^##UbDcLj)+SڡY*8cE3פb3p kx2;JMlR9}Z$dYhef{XD3 x8n'`Dr[Ls i󉻗pktͰ9q6˽\6Oe\/WX|V\f2;;C3[F[fr#RuP]sؑ 8!a3۰X@`r1k22]#R(:dK?*rD'l:&^cQ<QJ_+TcE} ).hQ6J:1'ߴ ҈3wBҲx.M/a;ntNaDpi2lƆO(?g t4PliK r6<9cD9%c>:#&`mUU~Q=5ܗWHO#ה `ȶ`oe|$'Le v]:к*^bnJl0I!_9z5U V_ ͏, nJZn#Cnrb:2 y֖UFPJHu/V=w]_@ucQc+Ѥxb]e]I,XCacE&Β޿9FHݫW:ҕbQ#zb4ԞrgDR{efQe3FAj6@B<4AK;Emٌ#d>E>Irbr2@͓)%=t=2ܰIcƬXhQl6rf<ܵ!øTэ V&OZg2a:&'c9pn I7%AIJ7jYQ`99tK BU endstream endobj 87 0 obj 211552 endobj 88 0 obj << /Length 89 0 R /Filter /FlateDecode >> stream )~07HXe`j{ .Sڤ<٣\K7";2*`LAxz7luQɀb!T(H~]@#UT[m7J4w CvR~(2f߭ npLI쪾u.EztB~DEiono"%n_P1宕.qOq_Tkl& 1ޜsVrŽ?\-i%L꘺h 'Y0>df8$BVuC-+kP%?)uoӾZI=œ Jl~e+]$*N?6L Ҵ!)ᓺx#[%Zt4"4yϧd>Q ?켏-LO{@ߎok zio2ƅRylZjlq^@}9ݖ]И ?Lv-\KE MEgoX*X6$ " {oz .4C C l+5g£o6랗.-"ce _ 2þO DF|![q-NtḤ)xkA8}:%70Vr2!P@6 X UWEsf v_ڳ7@&a즜B> endobj 91 0 obj << /Length 92 0 R /Filter /FlateDecode >> stream DtTR tAR qP5&%r)G&{l7sHf )YIfB2h횺#);gV%`@G4UE]3vҼ_ؓNF.uS龶 )'R.BV6P>>( *q)n!ap XRY/+Ȁe>BX!35+$ӰAkf)43R!P/KMFQ/9;OxENRrQ/H˕VSJРlebNQ),ٱ?QZ5s y^N+V% )"C~u4n1L=$+:a22 jpyTS IoT#tUp=r8%ۍp+=Ȇyͳ5Էq(ja+y2nktd/gU۱]y9s\( *pEXXHuǔE2 P=)P %VWP9$FDj93x&Ā(jiܰ6=eA\9G ,Gɸ#Ck\~H05YvpKA[`73hf8γv<XSqA޴[1rO´g^A.cp0g^/v_a[o;SlECmd,Di]LN+2)$f6q%FBoCVw ( g}U]ԇ#{ڟI t1ɬY{ #v$368͂=|Ai-ߓ͔]n"@ߚi2mλETk&:xBH=FwIeJJwHT[ե Wun(Qw+ul9 ?I`B\/lfcg(d~_ ܃1vMgЖ 9gr;ejxFGBDDȵn qaEb{Y_0 kО|&x-'#^:.ycmKO|1}nu9 ,x2xN}|W?6C^ƨ{6=zf<{x&`4޹RĺD3_$/z#GsrAIhH_ʉھc-X/sކ#Uj57S:V~Gh ;Ky0 /A&ڂ*oÜ.mJA.i 5aFhMu4l},ꔀzyjݿOH d stu;3kKDu mG:}>d:6˹Qh U>2R\`]'.(_^2&!Y%(gNLn#}CHjKɢ˄ mH|.v8&ɮ]-Zws#b4ϣNЫ%zԁwl'O v*RRû8E iZn fwgK;%OyV)1RX6By#8*p6fys_ZeftU:-̗:3Я;o;M0m5:5ᅂ< ~2Ȗh§j@;_z),maoʽcK`xhepodjfUtc=7׶8#ލ=6Ptd3s[Ub$Jb]mp,Q B":X ͪ?wNW; % kQbfZ{kç uKz_~%*ea rN.$GG({S.;(!W1'*=y`ls=͒&= Gksx/ >iPXfΗx:R091+nPssb1+S">e5VE@ZjI?Uwҗs[S ##j-M^6RF3i|,zz"|MB\+rJg7˻s5gTsl,^Hŕ=59qQ kL6x)PNNZÜ:\1бwҝVRw/8rPj p B8z*{} IjH-G择Twdx¹5lrV9:(jK)c݅iA#I a2p][B3QadZ\gn-IA3h"=ejr  @ډpx{´e9ZKMdOꅆO`#>v$D_csgG_K DSC~"Ӊt~'cfh)$}ļ}rEs590/jnK@Ϭŝ B.꿐9Y Pg e@fv`Zvlҁ4 / u8q2k<*e8`",PD62cޅ6I-99|3RyntL荁]،ẍ́pj:,ؑ9[o slV_|nF;$Ld?;}vwtklt .='OH^W+A?屒vL$th+tN о)yp-,9Si1 Dx_Q$8E^RœC.]l endstream endobj 92 0 obj 3408 endobj 93 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 91 0 R >> endobj 94 0 obj << /Name /Im12 /Type /XObject /Length 95 0 R /Filter /FlateDecode /Subtype /Image /Width 3000 /Height 1600 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream ishF)04aZ?QP P 'LnXNJwLR[^(ܾ[ec(a_fm)C6,.d% <rީ}w?@kc^ "ヽ1.QdCUh1>ıi,"cWY3ᰁy4e~0L; ^6Y ^|盄F8g'Qҥ"Džd(8Ul?!v@jdl Jl[MxZ"q ySnJVfL^}/,Cd7QФU ):0/,u=w?hYYJn-g uXlP] \/wTMJ1%}] p['Uwn% ײw#$NjVYp@o>NYm#nLޠw_b^,<›kv1pS$GըLThHSOui5tT?l Lu[jhv%AƣdWGyhz`+3-`h DMP9M/{ +Y#,d'tBXc 3t5o5Ozlmv`?ޯX*w9bg(|#(tu/|TêOx8tFc:r$bE hQj%j%Oo[;y[Sq^Pq& 5!g`eҦKB:ini Q{!+p4;"&%Wt]T$=2P$xY!Fխq$<}7[N{5IcmS=Vr]ϞvhAפA2G< `~4{>H+HdudT`!&k7iVwi){PE^KU롮7QqFJ8Q.qnX BXL ͈1r>bݦoS2K'd C)X(~Sk ۿ e?wLYN6>t { #o_s‡g`X^i}}c- F 6(lOH;sRqhB/ r"Fl#:f+ww6[j=,ig25!X~n`A3@S]qy&F lUB.oGbf%N p|H@\zYo)Sr(>bl(b#tʬ9 ݍ}n2M*#Р .= u®z O lTm's*di~,61ʈ)% NX$&8nZKGQ=<ш~. zyC0 :.CL|'ۊjĚ挹FRꤧu 6\$tom)%7bSŪnaW8gR«#3ݍ! 5F璎|} hjI$tP2mTQ)6דu^ ;;A㚇0Y)d[~6H4 FN!_FDگh>43JK3۟ ) 5$N† X+d=:~ˎ"u7E hCgV+R@9˥,0s6XR[N`k\c+Zڻ7ڽO6,{5@XH;xB2W[>g_Bu;"aڄK)@Nrwˡ%o{DD@1f3m畀R+] \5Wh`[s@Y6@bNQGwİ8(#4LYB4M7zŌsϴ b},FmۯW>4p4'j>: ]?Wv)$ h[0S;ɋCEA`4M4waP试G&wjA%~3)Igy!񋷌yvdDּ?q&lxW 9&-'Xʔ H d=e4KjIu@3N77d2'x.<*H*_Ͷ\ehiaZCtz xۂ`L!~ 0<_ ٠.rSTa=` p>vp_h>F57[OݤuBI&REP=KxہoK D3&Ňy369E\Z` L0)gtH7qѾPv>,KZ ̻yVByU4r^‰7*r ;|l%"29ƨ|xW]tMބ} l=s%EJ^e%>A02д> w􋧶,/Fj]3|?0_yx(yzFjm\*hYj YA+ϼ,B;=X;+ ؕ5"pZCG43CB!{#s,FF<81wd Tz`D|#$_+[t!)t!la\Q7ʱvxd U0`o\#y*:ibqqth 5 N>JZD/* G%J6uvKNH{;n D!vΖ(m2䫅3R  1,`o;O\|Dmlnrr*_p$uH&7ĸb%5nFrd"Re+ N]a.~${tBp9dkaZC& ӬF c|zdp2w'K?Wyʆ޸o$ Zׯ MZziWf,&*  2p-2;qL'DEӰ̹-i+d2^٢L1?2AR yC#Ċn2a"QZ m xM>|JPuu n:U&Fe``p92"i.x5B z.O !¤{kɊb[j)e^=֎?9lJP]+9. 6rh!]w >>^|:lShүu!ROeb3ӪS]H֜ҥ"+nvd̻3p9XRֺ`!-G ʂE ieGCyy1BցBtxe^z%S l) &rM{yY~V+(-e%z%1gAipFb嶞 pa4Ǒ% ԫw!t %*`C RrJx =3X|(\ei/š>>1x%"y p\IN2F) -ʹrkٴaVnB`JAa)8P733~a |mhSq@F'2c7X:GM s[lTfzXV CgrVx=hr[qo.(l'=]J \G"֟6d~jsq*GBs+E0+âK͊:U~i2P@zTA(0n>kRd-XmG' P=U=ְ ,>إ(@{A;vWp0U%X[U.!y0G\Jcvtg~Yeoָ"C%vI;2β?eLc6/UΧ`/9Έ/\,-pP3=6o|r$7H-0@gipʊ峙-gm?f$鉖V IyٌZ E;~}TlJosqg iKr&Z?z+W!j6$W18a`] P8eM )LrCR?i1`T *|J+sQ[k"m҆粚!Soa(x2VI94U#O!1s{o/&ün Ϯt*6)Țc%W@7Z+ZO CCl}ߊ*_kj39}NV}qfMl %?z;34>Ңʻ+d1@|py)C0?gfqKiNcaKWxvyŦ0=єV"w nExb| u@Tz 4O^(/:pvΧ/jRM5P1Z`Jj@ĸVHoi˱LDzHU ܜ+ኖlᏽK*C 福[whK_bwB(kO~&ʔ.._o!gJH#Wi,X_JM@a&-}m>99AUc(>Wʼg{\wuQH~ξ|]6tJ? {6AyPñfZ(IJv9 4 'マ}:=~r3Ӣ2LCI҄%d@Iޤ$̳"[E MH`|1[lO};<`"#)wdONXh8pW@#5nD>I)Aׄ's[/Q WߝNSe ു@$+ 4˙2/ʊuKԹJ $lpgPyӀ2[@l|I^qӖq% lRwz:0/}ZSts9AhR,pܖ䓂5Ω*ƌ(h\/:GE(ن<.j>"%W߇#F'}ClA&m glaM\Vg5s*pɄ˭ ,,KsDhWd$: pzj%+锴 3cV۔㋌*/?&_km kh<%$q`dH"(@,',XJB-wamzeHB5V?bOts>?p>q$19_?e*" "+;⒭GqltC n.+5w!'|=#n|f'ݨġ[*~a=?O RZ"ERE21#Q{gv|ٚ%sBP_\޹jՃ f$f+ѫu]?^F/b7"4)Ύzs5z5Թ*n*_B2kH-7BbLuNrL %Ca!Ԫyg>&H: :W; 2m0s`['Jy̻(A-Z8p%aPqHxk7]'XTlDL ^H dCI1f^IYHAN=.0r~z1BIliBu[tP8E7 O$!6}ةmB؂.G)w#mVr'wΛ eX?S Pcd'm6W*hGJg@L6k4%H@}ݶUJ_ vS{kl *2j`[Et&h/85V#ck=DtWmzm3djGkm@"W ~ gLO-7=Qw7)& &^WdTJ8 6BXwvAu{LY3(gua3K |+lHӥNŽ+gRǶo+tS 6d~)b)ŀfyR)yc(j\idG*=$Z51Opet?@5 P w8M]8֛Qjk%5;a>r]9;s@h{,P̯*k.x,>UD'9AT=t4/ vQ:zx&çEoIfB+T @vSQUB dJJh䚖Kt؄tﱙ:;+*mX/WeLP Ml) v]酭HY?Q F X RoUҗ;f_M[N60g/& eEL`@@='jS^v}av~ pcG|UH{KWa_ VW}#ܼחW21;2ꃇ53V}A8ɃKAr]C ;*2M&:s*r;?'гsac!@3]X[ H~JQs/NIѨxOtoڻ\BJm0]%೾X ln`TCh2of҄'q2Gn)7\뽔vv;dXH5C :̈\ڢXg遝na7`}c)kF (#C6$Eԉ+ ֨φkߟτTrt7 q!kT֪He0G:ZI߉ $|)/vmn,u|`?rR^%tU?<Kkq fP*k<Ǝ၎ZφEtyuda}#"}Y)g_YWquߘ}25W,߉)eX{Ӭ"̋>΃_=;pu :# qZ- g:LMi(VѰ;Y?5J["mcr #AðYo"6'/%/`}0%lyR s2Ws#Qc8<`\v|Bj/^yIX ù!i/xn< z N4Rw9n6Kw)ݭ\f2)з|,%>`(&F-fQUi lxOG8wqB.p:R"& WD:;TSZjKiF@3Q0nc k}rz̗KN4 ^U8y.~dy%䁱 4IVA݃6L ogӓy0ƣ!`u7n"%׬?aͲf74oT|tY&£o?$p0Qˀ:SsRiH&_ 䄆ǼO8_wi>_dI'X\W=k(kNo(R!@bE*A^\AW-[Q22ޢpv6 8 4wi'`$IaD#O?Z|ߞq3É򲩱{zApgB]2 4 0l6l7MЫV+(LCv-$~8gHNvjL49Q0vZ-HۧRyA;"J:e;ÅW 8S!r'MK?Iޖy<[.?G~S(EBL3ؔ9peۅƒ%֛-$N xxO S6b~lNZtR0|Ls%Nb+]J5THD.\I8H~VhCB-|!`KPk :Qn+U'e>!E&MHexBǃ/pdmf)ܱlpJJ^ek+(DmCkzr5c`uTr>QP;8~ jq}zT}pZټ $VyjE;=LDd=o/CWpn ő<tKw6x~fX fsQQ<CmC}%chC.؝Uei+Ǭw[<02iA;9W(!gߙ'F_*SLC/=KliPͦUE+Ϝ9@ &NXzzR'&6JNσ#žx%(ti7gN9v9*t5z?˸ 79Ѐg|v&|'!0,'q*E!:V=w7L$%t!+F/}!@ G\#j!C3?rpXң?.v'W]`yp7mr]]*T 2gP aA$>.DAEc& ~Vcz; ٥~09| giPh .[I#vmIvD.ݯ(w$T2emoO-i|EaD)ڨٮgAH^. WaW3ߓ1@t}EhɄ;bZ5tݺ^h+W, DY1Xâ*eg" `g47 (83\$a)ufd𔾇4HƕԫR ,.#pӤu5,}ID6ss%F8qp*HkJnJqJmsh/ڼߖp*|FtxK'7-iyYMOM@/Tiu$~~>ear}JSCK8PC^O).1vo3n}u]+u'h/V`rKP126- x#ȅ! sߞE~B:{Q]/1y46;TkOeR7HbU2zz%"ՀWuj<|@pbcӹ򻖁YJ762KW4 kڽډd^\L8֡p/"z@:iy0HXАx:Bke`LOv9H +hˮh=.Ntqn>iiއU6cՀIby6/tX Ha8ęΰqghv4%+H%ŞByT[z p(afؒ9zv; MD3''![0?єeŗZ3 !($<.9`̝jQ*Pyp%Mŭ6tݺtA+A:,2bj>uW;G 8͐cn6wӴZ5Wk"4[0 nQoe#_¯YAM^:c>D[+ MN,t<R.0Qt-Ojj؟5 ~0ޱs &,64O~MW=v2H^,CQ ,7=@O8 _Ђ]ko)%h?Z([Ie=|曈^oo&#y% t>Po L5#+ŏ]Cx:Ib3-@2[n]C#萢 :,yʴ%c?q-Ζ07RmC1xfnʧ]Ee+G1bىLK5H]킠i af-Xe'l_J\4k@i y4\a={m*;gcoQ4Y*iVksM~a&Z8VYzkb-$_Jska\aVdoݣѓT}-h@Cfۗ[ɉ{?5 /}HeE FKdukȨ٨kq m}"H֒9\)(CLjv)6&_gK/F :nl+:KIN\V~WxMu01P”g>#n4_qbQGY5OYd3%p3_4;A2Fp^,Uk㕓OPYcIЕ"~ݜsu:0E (( qf6n98׸3PTiWS }hKّX]X/D/aK~(,FCd,C=/,XɇtМ7*p45uM4ǧZmu%,bp۵$q53Nfwv7myLۗ•GMet}fa:~7g]ӛ0 QN3IQ5p)%Lp 5>=r%K%[#*۰gC\0 }tk> -ߖ{-DLOJ{SP0[hTt, UC2:^%dV`DMF÷]7 % Y|s&Rл6JSR&8SiSM5|Qw|zX/NYs1v% 6!miUb>*y2WT03%ҩ}ήTĠua! |)-dn9@ FJls+<xK4XӼ):3X!g,]R"}RQVp+.ؚ9J>nA;?DF=…~TV ~`Rƿ'p:lPFan59N=\XI˫Wsq Jz'Aa7HO&r:iֲ_e}; ށA_\^_x7pn@a-L97̷[QL0iNRʔXc^U]"ک^69 ̀O` ItɄ b:kX^0H}jfy$/ݑ/C)}HYlcm|?zGX7DAW+ȁ Бڦ"Hux$ʫ'_XE/5^,/E@2@n8~=~S2^cjKh]}6G5_Qž2@6Nu0E@~|rWFKIs\W @:pV.! ZT%YW:v$+4mیPEHew[K6Hn2F,]h|p=^{KxwC m'fV:Ja&*xYi| ؗ\"aL3;fGcʶT籏SE/4 \ZO7͗!8_h&'%ԓsڈN;Ɋ6JsD闐gK  Nm*xIՑ~pX2bNPe+be>K]:cdz3Fy1\,[L/b O4{/,gZ^ąbUR( p,xt dY2螚`BZ ʻ bo-=|l|C4틴?d'nr&B|<x.ݥ]\>3 :n" D<Ol9H[?!4L$7dwi;YП"Ɋ:oܻVpB7'bH{z<[q3]N#j 8 6C3#{`pޕaNv4o#eV%h<&״eӦ 55W~^nkޛ fKUEsW ?cj 0ѲɢDr_F[[f~NX!V}{G b9ُua@F7c>_t{_:ˠ)yS;^>)ѸX4@CZS2c&)ANZԵLv68m*?\|bz*oWZƗP[>}нþ$~5L#{D]1R>)a#1_ {QN [uUV.\ ILTTȎGvPEKljb& l!kz>x|0t0 %>yUoGE}5[g.Qۙ\)|è}6G~tJ?obm'љ<_MHy/LڸLp*> GAL1nlvWC_fP+Pw Doy@ȡrs9J2ç(ȵ|mQH v)1z-=Ŀ |{a^Em'tӽu4v mŻ5y^e<)rDu?yI$@\,Z{}!qLEIzXr>y!dɫ捏+]/)^r/Y 6xu1F'!N/߿&FK&.~DӈHפ@#QX1@>4IkQGFBfҢnB/Z)3GrY҃`Iym׋lD cڙIƢ&1Q=qrM@L+=X#^9wL\|fA }oB\cqw #E$07ϰiBK!8I$s ?c,rȸ]A&uv"d6S i)QY)v!ߌŝTċozOeR&@5[)/8,!Βpȑ\IH &+jQ pC܇dtcZǬ^1ъpP&}nkzlQUV/L$}J&у-፮oJ!ј*[NfkZUAv=Gkg <A䒄#仚Pz7w۝풢qr6Zg7^f3;l\Trie pŌH;o!Уx,ே5TW׋aAE>G?3-e ,xA4b#B\l7 i4?^g%kB4P}{*H^_4n,-Z7:o>/S%8ⱌ"Q$}Ѫ|[~U1Ċ.Z*CZmڸk4tB:@_@`rm6vl+! :x?z*)Q:f0LD ݒ+G5|rn@?_sP y*4aYЕ1W4|8浜<(yd{ξk fe:;~Qɩ-.I{o$0Iyle`u{.J&5oR hxdfkazj߭uϮz Gh]g Sk{"%^^(. wi+l "E;n@TQOcl_>N_[!"J f%\ac:D}J;N8~~;-ផyywN3!%?G%u74..)8k@O ps$)~2o>衏I:3oNKw> Lqz>3vlu@d֙3c肟 {Ibi>Ud_*Ja1i@V„ s9 ~k^w7ii"ZdMk"'$6n4]uTЋ׏ I#tC*ca'!?`C;o:Zia.gO,XQZ>r85e,)!k6vd\[$ }@ǯ1ټq{UƗ_@I|b@Q6a5yBρsBZ|mN>]?P=GIsFL&vwr9DJtyG4GF5n/ zeM w:Rd3we) 'FfA.}[Z҃pR` rh7 /4{•앤|)RK_u@emd "Li;,Xb+H5 w[1uA_8IU:$ BfFDs,`R)svGj(v»j =}?e[Jaj PG`g=[:HyݯOAFlU*@.ǵ9=35_)<:oLH f"W`^T~LR,NOfd+p& eD:ku{:[Y7έ?(iNXOIR& VR"ʳ-I fQ~\ hQvoS&MPLREP5)7bCv 7G>O^c[Ѫz@i5x?َLPC4Il =V'a@r*5r3gFEXzhTATwk^*Y2&YHk ΃ }}ls* /?&h$b6ufE4z ((5?l磋#-/隊ȑ!/% 3@"si4MK0n$T]#U1,qr鵙(R1,(q!pG'M7ݢlnZ -HSصWkT?akʾ b,nrhQ؟ R3g # /eՈy:a~ :{>C|)7u~6b!x7b'pQ;$5+GΜ Hӭcgk%( Z2?;9H#SM2k|{e@a#{B#aelv:N?=$hcmZuqșe&(%OwDsEV*(r0(U A[h3W;z=.i"qLރjwԚv$ϘD?\} 񭌈̚j(Ёq/g̷}ZK~d߁ٯ*S㙍g7$x{̉pbq0Vq;JMIy{S1'%8U+f)]aJԝ_yigWWfN~ wY+]*69,CqFʧwSM~.߀,,?+#i 6m)? /8T \t"&Cğ:)!>o~&^sMʂ>a7$=yҲ{P Ơ)bhZPBѓCōbB~OGzwcxBnaPJb?61o9̊m#:*hߧu}K0wA@ltKAYVH f炱ȲX?l}HNE9УB.eѝHv`PSOje&)unޓN\@jOMBՃJu3jI vvf97ٯ⽸ 9y췠.&U>Tiz'$ l0%n}t;w^ɕ(?p| Yzޝ_4(B)^ŋmy_)_Ƃ;ê;DWӮr?@R\'pݧG8*U46Qc|D!Z.^8:+3d\Us(RɈoІ**T6ʚºGE[VC)MxԎuL5J/1t?8v@$i_rm-KXtt{|o pk"eX<,DjyO, mh>BBz ۄڛHGEpfYW2af4/f}|}h|& RJd1;f[x),J,&;QhLMdZ 6! tU&󬠃wP!-=;[5 xW}7B7=6bӕ%3#&^V#'P Ä|\:Qj28Q!LR81l"K]؋k"E)Vj|  jɶe#+ї‡ٰdMjjtT8px+SUBԛI2IzU9UyIY'\nBa]Ħ6Xn|5'>0( 6*ӸJ2P"ScXo6~X:\K8Wʹm^cHQ3CMx#o5woP՘NoAHHԿHy:Qoɀ>ӉQ P=+uhwV? @HVf,NĵC..\նDWXC_Panq7YȲǚ/l^p:=#=faW]A3U}M3Svdie1LșF>r!dNBB_17 d7a} mkv]M%."ߢ}| ^6,^ U'y PD3n BLk .XbXB2Da0flJmsSә7e 8>$zFJ=nQZ}LHjo_s/r9## wHfT|U"b CŌ^N57|&TQwml|kЋ| +!uhJ=%BMFS!zMY|2 \~$QOkzFD(*j蜉B*p R^l=3> O'$[|6fSs _yˠ$1`PB|V5DHkC!j;/6>߾)F?1yQ50( Rt\$ɽ6iF8\jy`+Ϳ5?n?W\Ӵj8^dBM]kର'(_< #B,ܧ\7::6#xջǮK6W8/j č#3slh:NKj[>qR:cݿkM! %|?n/6D.4gcFc$x klk$me~L$PX1dN͞e_H氹.K2' b( HpHe U_,B~M7fo),JL2Oy{\l>+-N$[rVU'T TϼQ3#dD?$aG$yvrQ-Ʀ=>iK RO骆):iiݽE0n.f|cXTӝI&})s En]GJ~.i5(r}ֈOc0Rf &lG`䅁,EJjw{hE<pi pWJ ǯNM{AYJ1݆ǘ5]7Wj L8'i1870DenF;|L ^0* /E#Y)ep@ʘ>}u1/d266Axp:`)#V]|?\Ţ*:϶tF 3݉|HtDncZ EJLA`k02ywsIUWu?mqIimYG![Gc%40 LMVoOqLKUv‚L=֔B[mǶ)X5){BԒ|Re|# p.=H+g_rQWK4GB+ BrP2 ;lCCk|W$w0hUأq#6l(ŠOLv4oNAyV53/E ꊔGz<ȎI4=QWgޮwN#,gNPZ ' Z|J>ǣBr>D dP=@hr_, ?/Ձ;ˀs-&*%7O-We `]7s}Q1hރ@~ہ5wr籕b<[A>1YFvܽ= !->g/+3P^%oMcjۃ&p5߅b!m@ ~-o)Zk~cXAe$@ǷNWVQ644#  RpYVұf5`6 4cŤqxSh | `DR,Ol:eOM׽+Hë¹ra7O1 h57']؈x{%-_(uo%fαEip*Nu{< rZaBbX)n K~#A$N'ĒhȺ""icZ>?-by$g.\W:K~"}Rkt_ DC$] A3 6`P R\r6<Y˝ȴCiv#W!@1jo&R$X]j jPrao)7CC9 e U0r^~t{(Kv k}Mw6l+u}?E1s]\͵58 8.mĽ,7<CꫂX=>T*~]7;Ev BbzZ5~_tm9ri+C{jVx[PόÆThAN ੫r}nVV`Dv=t72qjZ9k&h/T:Y%df moo=ղ|B:Z-?p61=);.9hp~ Q \@PaǺ:¾س`;`-L3fuKԶcAHPIf'ήv!5#"E"g_u=ZW.׸ H}LFRvqzq ut[s,RsUg|j.pb?Fx+n)rC 7 f' rYز/HeiaX# OHh8b>A0C u)+!G=*cAbռ8ϖU|ډWlgNlqLrT;R$oJUU-/c "(*ҩ 9Ti+E>FS`&֣JkE]7vn_dO&D_.G4'6PwV9ng[J z1U62+x!3F/y$QKazi۰FQE'<ؤS!ȉ_'{a7/Qitw;C`(ӊʑX IlfuHيŔFt5Ѫ2QېvSA]-&)Ov!7q5 z ]zC#ƈM-]e,19-ad[t԰ğvt^蛎x/TƘQD깤(2sv;<0U]0BC׫^5\.T:<^tzr)}CS4Y F)TrnlRȬu `|}E8[c18dGn",2I͓p `/1j7H{ ߢu߉1aTYGh塶fe8,=y S / m lb<+oyN'w&ht()^EPAGC5ʚcגNfy6%~z" _0 A @#`yϲ(+(:(~>+tGGN\ Yƭdz. oQ-YιЇ}'ʡ&tDžj~D4#T"MEeut/LSX{rx4O e[REPrkaí46Ux~2oדAK2ZvͱJu).PnXHSd%  kVɆV3IgMY^f(p˚pΛSM.a6} :Z@~q>sC g ܾ}Dm piUv˩Kr[\$1U˅`SFS>uJ6H*+^{OG9%$ c8mL#7k_GT| GhF'*FZad;%U&,qa/Ē:x;1|zӹ#Ybu3 f^|D>!ksh K,/ ĩKh'vzSG;h0SoGV#UU 1JW=Y M'DR+O^'l$|DH7 ^a>c><"tXxσV)рt_3ec/ sikhk0Kp^3/g2Y EB(}zOtտˆSG oӖl`9K?1eƉQ cK1$H n S:sїsgg3?\$鎙Ŝͨ{6 QnƗ&0t \0 "Ia1SIF =JN ,8Ҟ-|jcGHFmP#LwY$o`{#. *SZ!,{r|w¢0 \_wm.%(z[@F̊8<$s-02#VhۤZgT{v|Hg1O҇\_sܵkLhJVϰP 'J.(V#ʮIep#vUlψ˽ֽLj~b ö{ 68g׿ 5c] |wUuR$Jmrzڦ圈4w<;;Vʳ4hc,L) VN4[.9c{M6|_?QW>!!nxe+BeR*?v!)DUԑmPGYC-U "җACQqSl$>I:lê&hd+OR VVn/RSt!||{p 2 )̝й\DE6T11=hH~}M8af8ݰ#zi;-Ć /vFojaK4û@9\P?C Vr]Z<([Qla[CU"aS;g;Tu!pcKc@@Pp)͵AWA[gf7q +As~s43.K]C4,'{$̏&_k(8X4FC -s4uVzz |D˿-~ Fo]e[s7&C5#$i7DEtaOGWFj 8FUuo`uLY^0z X065=%&r.o[tol3Y:3qb>>ErpMpG ^ ۾ƒn'=Hū&zb!H~HsvLɱ^]p8-I 1*Gڻ.UIvcUuч> QɢoUOd%Q/9T0婯L/<8AABn6~Z1f<[ʾ|G"XS!N'Ld.u{0::\?!B3=>Z#UXIo"@%xlieXi‹RCȶŠR-dKg[suC{IRKh=`Tꍨcš*e?&SZ9 -oqcI1n{5bPR n8\T }C<-]$( n'w)_P. ̫|)_=rvc~ēzDK*+Bd@ݷ4yИ@uUgx]^ce@ qjN Llζw$t tJl %S7ٕVhI/4qcPuNk/fC϶map.tQ/X<,`WD1 E-p>+u:x#T_|oŷg2$PFr Y'I|ڗǼniv >j(=6$T-0DTo [tT~a \C\XA/VUHQuM8(/Z];HpÚ@ݟŢCۊƶE)48RffL]< o0 оEP ^ <7UW$t7!fS~4O,:TUѯZ\/Etp¨e9֚qk)hbkrTNuqß/n#QJR}8Duɳ?Ш> cBfyfib;:;"T,ݯpuj7ڲqRT L\AJL H˟L1 ',e4kU Adyy !a"w!=;x +ۀ7*fuTgJZ R%_v:DAU:13fKg{՟qs: ë5ǵ|. CFM{% lң8z_&iXcB7~$o8"~Ge'fUHړ]lS*)PYB6}ܶɆ&C9)0nP־i;Z72uC-պH-oZ>W\6^&e/nɇX[Qbnf h0JLIG*oL5%T]]9@Jb״ǛHX”U/Q]eN*D/WC`]~cBN1>#]Mަ&UHP,3Iw,/sR Wp`=ꟓQv92V H+(C37tAʜ焄O5ʏ?";PtS2<|^3S)Uvg㳃-c~꼼m|du^  Ymy-s jDWl"W HBj\4U55 Pz{-tѸDؾ@^lR#PiF(NՎ~NjkA w >A="?AnYeґ+#jGA.E\Ư_9\򯉄-zX@{"N{l &$wY23+)N :'S:cK#ۯ/($r}G X(Aamt5bA`edf<zAɮ|bfTRf:;<}97M)慓NFRoOA[v36<*@y@#v @5%C¯\PCæ5 ' ձ(|gN>뤈 r 1a-2%ORz )`&9R%ш'a4 XYұʹ՝7\1kjF\ tyV>{j\}pv[6J& d 5{h̄B~׫VҭpqΦCʹ):R֭0)`MQ/h۳>g|"/aKwq]CtOڶW0-K<gM*67r} }$@HO=9)S0B,Fҽ^Dr1M5ͻB7 beiYӧ7[A}YS/~.9#u6 6B7ȔoT ۀR; DbB>T t%<e7Gb{w37ĩ3}_%.P/Qfܿ \^lS$Hٷ?zt$8> 0xgKD0O Ӧh<sy+lX;vQ!ɣ"c1J8~6MgUF˻I X28V(snEJ H{vQHW0K\^" %\IS!CB!(^k5v-e(fy7En `蓮pş-u~@=XWQ҅hJ%JLv R:Ld@G"1^PXufl& 0UX=JzBD4nfA-ћ߹K613a YbDD|1Ҫ4E}O.UG$Kq<>6J 84 ]雾u1#8ae)I Ufq<|8[Oʾ܄h6W\{1ۗeojxZjǀP'yM"͌ީ idWvFYqd^p*9J<RG6T3j3e92V!gOUJE9I;ʨt);o/&;(9t=dӻKtܡ'exĿE[0A :{1yaIlh*h aӝimw fAs8~հeO+})ې'3\zdy5'JNX!,`Ɯ,Fa/Vze f \AVz1 %6ť : -Yq)pXf#KY+'k9.:fcryoff;w`k*؜?$\]b)2D;AdK>lŮ^HzJGa9w(l9,7#S&ç<]ɉ}@ '쬇дk<6H. EyLd&4!`xoBk?xȬ1CF”ux { O.XSS/٢y` 4:ao Hf e)lD5~@а2E P§8TNyСl]8&!tYt1!d괺Qic ѫ|5Yq^s{l NSMxSy=^nӄ_Aԡ*EB&e'DΠJ+8T'O|_<:NUH=Ps⹃2[7Ei] |vrg07(_aW_bAuh/˙ݵ)~E B_pz-oJ |~S ђL M5#{"|Wi@KП}?Ѧ!:o۱D/a49.=q[&s|(T5.N*sn CV-H8`-f&/?rPntݍ:`ǛXڑpѭ^]4 ktb\v}٫ {,\@y%A\VQFЮG4]bV!,W׶|urz7_H`=t7R^T :vC*dQm517g]<0vphIT3Wt` B} ![b~Y;/]ƾw3%m:f}]mqdV┧VOzG(5~ґ /l X_r%ANeS6 J[CSgpP$gE0"c\g[W~]i&CNR רNElzll $9ؾI8"$ _h}(:A -deM寿lJ "l nk9n q\1_wā>G}^#'`Oh:BYR2J mq uT;4v*4J}-7Eϲ0hm>Lz/z1`ٮ< ;A߃{, >R[~.{#e7AbAij{K|)=D/pDl\Y9㺸++6+WO 蟒KU`sYJ1_M&u DQI Ãħ14IqSiǍ{N%uȠE#g蹦daN!mŹEXLeQI9dyvE! stmdg*è/臾KgՈ*:وv( ڶ&Q]#(إ8 /' +s0̶:#PJpKY)z4H\C~L]'_'4 gf"X*}4CCTc/s'33"ru8ww'l*>&,N\ѿx R~v;uKOd >p0̣G4pSy>:FyJϤԂ߿6+T}3LwV I -KG+ex+CyL52NPIApO8c 3 @01vHw3Tb 䦓JSigZׄ{=`|חUH\.ٕ@Y-j :@ ui=/_lg¡Bvn/%y@eG]81}>[I+a2cjːlh+ǠԑVh`=x\.voY;ǫ5Q1z#,U mO:[[?D[()%ʕ腾dFƘum3@G~H[ǵzmJJTqW_25䚰 6>$yn=`29[1qJrDd!v0h0 &­# !~ǥQ@CAQ0;2;~ᨅ &sGQriOse|h0i4]Ǥ@ .$;Cbj'snSЖ@fؗ(DĻ.@BSA}|;_ˉybKOGĶJ Wy0n.VESLtԨ1d(0L[4Jx?rlr|Zޞ 4c&lE]ϸ|ȸ R[f@IW]UE#cn2CGN>F}IBi!Fn.۔ڂ-b%09n#D$_ZԚzbQ$SjEHX>ll,y{'z6%rKRZ'&XOF Ct`cQzޡ_<IP&Cjl * UWy 6 A '>" :*$amPr9(+BYm_r좣ݳkii(ƭQ-R3wVI\H@sY^AY+^~W|^ gP)k~ t/@' qGV{dQHHjYs7V/%7U'ɵn̤~ "u+AUmC{A[; Ҩ\¸ ʜAYx6";W(%&cf 0aQ(g2Jv[H|Ȏ-QNAYTSbT6[šSz:⍒@b:Py3UMFXÚضm_EW>TN^ -a Eb;qEjX)jle't oYJ*MVjh?)A^i%ѹRM]P(^84RӠs1¢"26^M[ʷ,Tb#.nŔ>݅HF"Uj,Yv)VFW~Q 3S,LuoVc%%s\&cYzX2ޡ!BՁ\FDi9,+TۣM^JRbtv v Aŝ/B$Bóo$NW/ 9-}0_W/Y@t{X;hŹn&b XO^\..R 8#eBٮ +P,K.f[6X }#k;Xp#u}; !+SlAC˂z 16 Y ;!/`)oDX]dYc(*mfMi2m^HIĪr SYlYf>w1nr^g΁?Se97+&`F̍p1HZeۀ$D %fGdXPee]TcDim/a`z?;"GVQ:`h܂Ŧ&}m.2Ur=o2j"m5N6-i-bƥlWBmz=nx5l^F\OOFi6d '(tf (;s`AOڭ%>,X=xg!yAߝ8l9qD}%LȄ~HHG&;j)~ [<"9"(SCO&BKq>{kM4;}-{A}W$Hc-+1ew2Zc^D L;~`j%~1V8M~۰/Nɥr(dN_B[t ,) ziy3|i1",ZEt5ԻX8CeW-0 6eaC\ʨDb b~?4ę۫` ظ,+tiv-(kJn&Vc~IKc39gibjE 賢W$-z6^m2 QKn)I~8Œ+Ϧz02_fxXo`rZ^ 0ڱ:M?}6rgWK-98pAb_9 iI<6$|t#0sGt^r5wV5e\(of$y)Yy$M lu-c>^^YCcƚe"gb (3]=|y5"ӅeB=gܔ(l 9zv 0i5Zp'a\&G.$I>xp8ugJNx+ak$n8WX NW;}%H |>L|ދH}!nyvzd\'Rr=G-wx|n~H>\d<5O@gOfz ι \<DNi/:c0?uJ5寭A AUм Vm?WMvSy2 "NX о7İϗkpvY(D^<\OAD'3vL^0P7aٚbdEJW<#:1@"VDɽ_#:hncHCQl!ެ`N=ǵkI})AjZVg]w0MA1eNdo PE]֏zQE1 "jk.{~i9Dհx|ԠO"fAޠ#c7l-ʤg_jY~ԏ}.hAh쬸[,zjkSi|m s^rhAuHh֟lʔ4"p)5[AcDq,²b1*Ƿ x-)2U֪ȫܵ:"1>_L:Yz9b喈: v]0#;bbr*P7g'I @ƅB+k 9Wqؠ= X JF1"wCݛ38drjSn&+QJx3 W6[+uaa!56HDt/o={p6_/)忼ܤlHK'>t:HQͷN!F\ݸFK'Z-zbXA%#S ˬ+psˊ T &1T0ŠY_ba&4|~ݦ=]!?c3Q9yb:sH>id ԋ?["hTEspPO} :ڣRB{aMO>QkuϒBF! ͨbn"OAGM2 _ Iե"AX[۩JA)T=t0P`Rrro8ȫ=-}c`<> j SUI j>!)ֻZ 3t(N3>~d“[v. /yN7ʅ;O>)5cq[m]>C3=oMTp sSIN\mZhm9h'3766ڞs}r~ք{>񹟈D>41Ó'4 `SzMujgBd& kl/A_@=6u z*>PU'vb0o'_XV VՐ)|6i~]IC2OU"Ǘ#8/zxw9ܮWM8-svU,:&ltRkԊImRhփ!z`MWߴAYEd{Igo:Pi}«|F-;%CaŞjG;PPL,zxHP&OQI"fu$Qv}9.y_wU1KR`ag>?EY4h !Yꠓb;ʡ&&:*]BzSImJs7 ̦0/f1Fb WjMo~ ѝpL.t>]#=<f`|}>wG<&0+/[=z%4QV'_OTE5֎oOT0eOՌz28 >m^ zuHSFuFs56mPDz>tNas,?UN\W8Z8{*c1Ɓ&M{(;܇ Qz~nY YU$P~O*fӍ` aZJygkcwdO<FJrAi`C,*Rv&WB4!<(LqZIk5HoIi!!ŕP(ik3ɞpN5o [\\+/@ܟd`)hϒydi؟YuAE彋 Kv^=bë/Uwmq/'l$_蔥Hnoe_dWA (z!93ňw;ޭ1&sAJ?9BgC==9P)-y$PJNzuV&y5AjьȰcuR)&l RrvUW% :$j:q+]}0X_M=,?3RQ@4[;ϒlq)(if:7 4mE6i rPo4#ݹ65=e(!A#!&7uVy3zN]7`%#-а:iU0Ga1XR%-qRf5iq=Dyn"-JC#uyi$tHf}x=F4iQcvǫb4PE* 3a\|\1;g/G%n5YMZ#@ҍ0YM}!7@_8X#5/6'ȉ$[cjm0TÃr݇\Ҕ$F;KyKVHoGO;EMח?eI˯R*#m n_!K4Fd^蕊:4 vn+0T>$rĹ:lr:~/^=JC(ԞJ|K Wƛ>}\mS3)͵>4 oG1ޜ#ZNaY5(=_n ^h/#xbK;I7x%<Ӥ>n5Bxtܣ{.7SIah_rHsA23$XUwx`($ЮHh5|4eyL IC 6$ʁd3葳.%W{ A/o(kc(V,Q^Ƅx9q`kӟq9N)GӼQ"s2N]lf;f3/Y1X80PPٜO]_>Ku/;+p2:⌫$ˠU_Z;X?a|iVD8ng#:z,GqcO-/8p R{26~qj8 ~ }.)1? L@YS+~>!Mc,߱5Q%? 4{%vљ_0)]W9/V$ɯHYB4{) 8\h^R$(&ׅqw,M5VǦH}H^D:%X&']Tl|c'0 `Q91f?NPÍf:)Xy %ͼ%CF>A*eg²y#F׊5:F)*oY/&oU6gPS\:R;tGi\ykvȜE]!5hih Ԟ3&t^}s'YNR;YrWBݷ 5rվjGU+ceoUPbϋHR4:^ _. 6aVkw |OAILS 却Yy#헚+_yOZs/jZ`-Im7-.d%FQ7G-O#,syxˮ0~9'ˆO'VSH7!}rMhbˤӺZ'"b@[B/fO n1n\1񒫤b`!kkj^PڌNv::?ف0kdY3k@YԺ{~iӹrRNdYxJ??l*ѽ4LC& / @XWO D>: }-8^\u$cP {˓\WZ+t}_6?z&st*CPfky=_DZD CED]K׿Fy wo]!O&Ќ 9Hwv_ A%2\`{ NgDr_qYQ=.d$uۇoA%EPʆ>E!}P5X7cGRtL0.LJ.0$Bot&>bwگ H}؝C;ìg]L誏)U&Dn<^V`K&@!S#olsu":Ѥ~Nw2ךY~3J)-jA\W{QASpbH zh}i~`ɩf~Pp脯 N}_/0 x^\BJ}Ӹ6߮5JvՔfՂ-NXH3x0H&$/$Zk J(csaKep || {G? Gn^}yLܻlޛ|_,(Vw;E+ 0j V_Xky @S7t|a]ek?FNub&hp%>c߉s61[xD#e?+ [N-(Oe&,:j^Lq]:{e)}n\ħeÃ'1u>O'M[to% ;׸1-4zu?RC @?4jp_gVM}bU {,UD=R3ꩊd3jAFFdia9`xjA`VIՃl:\Ąa(c^O/fnviEԉԘZgI@?ܬޫŞ| n)/omv/W/}\( k%_& 88 C5ٿ|Y$[*$ Eۥ[lcp(GILmiTJMt0h]Ď]/ ^@l$žGq$Gzl'׸J:8Z,d̂a~;~'tRW<,I)nϨR[֛!'O>4t3IR"N֫=_L &eKvXzɻܝum"wKĭ9DJ=Cp)qO?gUX׏K텯OHٕȲ:u¸IĮ7\sBaX^=IFÝorcX,xQwK>oOwNp(vWYVQs.e;;nA/gt:h) '??>3,$qI⚙.26QAZxi$xLoۙį*K,6Fj"Ch6ʖL%'<cC;> VŖgBWj]J0뻄5|?՟x{<~rR.b2"`d9  cri\FL-B,o)_O|̬IFm\fD`8Fm5Վ͜ CTyYq,MPs421 E])($ E6&v4D^޹Fa]Ϝ10Lj8FlsM*WbR_a?E4~:N5`ӡ/*d%H3*Kns`|4WpUox;PA*\3?A"JA^&~6/*c} Ve@ԅ޹Yیt|%,jW "|ٷR&w5˖Dm&[;}\5}@TcUl7f.Ȯ._l9VkǴ:{qx(|[UѦwU|rM ,1sXHcK(j K ތٟ eБ7"өՒ裮?$}U$GͭF]^ ֊fP;jqf `)P<9SӴQMD -_& pPWVa@a2LKFk"!{~V>"f-@7(]5!WǡK2NY^D08Lpp=fiH \$ pC~;ҥ;H@t%WI9ixx$uޜ\O(֑BSYՄ}Bjg*p2tNO'֋1M.E_`ˉ\nr?T<#bowM5-n`CF]) C_Ct>n3O*N8i誘+l߯lm#ڜ Ű׮:uF;mVr?΀oIԗ`Dg8"g;w5dΒuuN" _Ѣ@8w`bnW*b-_y'@$'k:b? [[>3tw$ڐY~ֶ>Hnc,cĒ([̗huZrY:{3b+ޠ*m(X׆*⓾A$4+E2ko0[~IVnl-cy"?,Naɢ E _-ҴY&+_HPdLA *Ұ,ތ{4y];oOW#%rI};bgSCrۻI^݂iۜw0  ,iwEl=9FƸSm3t܃,lͯN;I4+! IiS _+&,)<_¯t s05B3'3@}mC)mTncnصjg; c!o<1؟RJLU2f)\^Smw$ھ6a?눢^,> lߛ4Gͣ=#-2(+.eyd#yLGUŢm/@-"њWƃZo>tʼn ˦T`vRjFVb6Y`#B]O.ʥ ~ӂOJQ,D/?dZT=2&?ճ.qk}Xnܮ֏xt}vϳwI y :,I07 DBT@#YHUfcCswX>l$vA%7 :&97 uzYM,9?|3\6NSdU>cM(p|?3gF)$祥ڠmEB2;HѮ֯j[㻔!$U'v:Ag"Ct8ק&(WA^7e|jC\}^( 9 g3G~Xpup6S06 YpoAB~ORW/8c_`7'nlb f*%K b,aAB4bSq*R v;ޝ(ܗ77躠rXd\/ܲCA.j"J?[UFJ뺊6~T0Hu „ہeDVvҩ;JY?Q>(մhּpPМ+PڗmEWUN $~3HfU4`W]L\bT6u.ĥ) /[ 5=iF*Lˇe"lUAk?SlWfE2 O!T\}%[;sFehh;jl*@f)/Q)WlEQwO$!M('<2F=d 82Zj+p̏z0Ic&H{;N]'ʓM_E*׼ .m0;8z:QJp]U9)?Onǀ `|H^f#lC)/u/\oBcjjّ-xAx|{)-WZ__멦C<ܤw.]c6E C_ :.ݤ66&.ނmloX2 _Oq(H W0ծcILc߹(Vю@vZ$&΍ zb~A{#[+涟(\/J>ޔ: BDMoFӽ$h1\BE>Jj7 nJBr)6iT&<#ɣ1UCBI_h2ـb䚦.0.J2o{ zCtzY/k}\C7Hiu! &LOwܴ{K8tJ!Qp}HoQǪܴr"PPmSG{q%`tc!qOϼ!I EIRz1'3C3hlHU<- -#9a. #vqTBT!({5il-!!7b֮!^2р' 8qHMzvC)Ww(*SpuA~Nkg d' 7lBR5q;Il#HLлuL[;I{}-R]S 3\0sE$Rzɱ;(`8"]OG5@ƵFw?(, k oY=! 7#oi L$r=붦c4?)PO2u!E'UЎ+s,ciaBfEg\^i\d_$Q󝸑?21XqS X/`/硼 ɅVdT}B,/p& D9[ʤ@}! m)!CN@cуLU&u!}. ndxkĺ+B ٲy3 # MK"s<CT&6UȌwj?rvՔďQE5nN.,ëm&މ@CX7'T G~N!@k\^5@Lz92C_ ØgɜL pj\fޥ\[hOәԵֽ9):X(ӥQUXplK9å6 ~ióv90VB%+C>}glVN6_#hB6 _ڜzĝ߱jZptD 5_ oă*Dur\߽H3Xeլ-hQz5(qAO} JV'F? xvj -]V=T=pnATQv-׌/[̖Md[cT*g+&|19\ P:4*(^ Z,yp[C+ ?~ )k6V^mf7m=/eKJGf8mPD˯ƅӅhUxS}b׵.fD$LFƦPO\p(m56Dq1G S6'=u|a[v=mƃfé 1faU6"do?J$;,H0@lb׫4z)L#w81U+9EAW %bb g4w55t[l/.³ZIlWOxR ;^@:gFr_)4mhr60dhM7!fgP,^șjQM30bv*f2t+WHpU䧻 Mf{OӉth]Hۉ11V^S܅ǑgP cS>=JfG,}QTnxIO!XO!*],ova:*!x2I0<X S j4c; s(&=!Qc{Gk|۱Y9(Aj;DOscRR/Uׄ$lw)aA{@`SJ 5 ͕?V'F%B&T.W4vuϓ_S, 944~ڞÎ:LsjT40@6l:O/&e%"qV6AOqtpaߕ*!%CV<]][w_ π2H Jc㹍{v BU}I.]q]2Mxj''^:>|.a| D_\3ůp^OUV 9cIQgynvkMEz{ٍ"?%B 0ՆWs,""*ALȠ> 'A  wSK%y0ѥn^nN5:( Om^[M 񤔺 `7$$ǟui96D:0ku'c9X} <WWLD‡X[俢n?rƛ%6aҰ `$[Rhᢀ2Pҏkb!a;:Gx쩊<`ìFŠgxgCNL@?v7@2Ԍa#PeN# )GTe38ЃvlRPy Uﵹ P6g%&䫕CW*-_ؔhzZ`N, 0R%5_2jaK2S1xbHB<|wK2nv*-Ax`XC'ɯ'J70hƑ03&>^=C<˱ L %lV;` ;}GT4wkUYIEMΑXޒ[.oXT1e_h$hag*.PSxK֗?9 Tdž'SKS!rOiO[.9|wf䈞9·׿(5;m3r̜28c*tt߈@DGiI"A=ޫT^Sf[w=SAjӿȄ 3)sEj>f;K9MHLG5 _)ϧqf o@0${nDu)98Qټ_Ȓ& s[M*`]I^Q.R:)0gIYiJjužH*s/}i'jŻh?wPBx ^"/+ES6#[`-eH aYE;m!Ӛ~xќ?HSM c̅qy<c?CN@zjNy IjyR91&y9Ѳ\CVvq wv9JO]^Ȇd9-mF9FqeH] ̏͑+Ȉ"0ȡ9Zl^ ,<| `2|4;A6F{I{V5DmZߕ(+@{4Xժ Мq<.$'[˥kCߧr;e**"'4,\X> mOɓV͋ ׀ oE@!0f8L6}y_D*x~R@NxN&j) ^^?,K{_ZžWf8v; usӔӝEJ:]i4W,DGxRۂ@7+$n$~Qդ2{Lڊ =s̈́$ 1OY6q/5]yc&_%/UCQP0DY.It5FwiĜE-2&[H'IS+mut=:<Eȿɬ>}=b'[%e㿀&bqBIV(e3kkX5%_4"2 ^\q~?5N:-|Pʔ8X w"+l\$aD`/_H%$ YM:LERhUˍ!Vi<1ϑw4f퓉wZpc/[%vofQsaotXo/ ,+G̶@I9'_;S) WpKiMit٩X^X`z_D=jsJ1APGjzj9胺ܦa~]cHdW%V(UmeҾ=^"DS6~:˗g:`BpB8`!#z`aۜD=1AKqIWwKTwE$#gjK# J1R?nc7ε7pO\6Oжܢ1%G}fm~F-5ckcN^iWy &;=9quVnomD މ4!^EcgƝG-Am `x%hPSJd9]DnMpދgV~C3u$1dClCõR/i8B-YTASU}hIT=,%?A=*e?íYnE2|loj@.=yXS s;lasme.&^ӈdf?ZeL*iP5%jҦ!_& ipEC#W0f4֮Lt+?tXJX:[Nd+ӗcV'(3%ez^>󘙾sV71ACfսXQ7Ӟcum(2P[Ql^h!^?Uj8(Q~-HE4H]{`?~xm'~CFT.}X Z*qr6&ϯ3ΚF?%=hRސra2 ilr4*kޥ/=zhczR[00EpSxI 5Nc&\tp{XHV_:`=95( ېogdgK9\[e;lDsMȻr76,R#y%&n.zb|E1RXSfb;&?WdЎh wX6HGĽ@p2I01u=C bS7qjِ" ~Vjn:ctcZo2+;A+0d$ItAM=?PB*td)piCop鰮rʵR[)Cckꟷpl7űﵑ%gE=TlR[1D+֞ _~ËYv5ڪ:~kJGO%~ѫ:.YhrKuXd&xӛK:j֕W bxp+a"j88LeA~t1=qSα9Fu lc6$,W1;}( B'Fkh9HL0MO`N4ή)K匞?F"W`|Ƣ]*We"k0']췙t W}{06r.hܺ6իz~<Qڻa M bn:?6#e,J^អ߬5IW)e3G B퇘ziΉT/}?\.hTϰ/Ĩ#)3ejiׂܗh՝O3P3/ELƖH2F9F GO i_EI{>LOp d+9v\ 2ܔ# HO C{ m : Y԰/~dIa6{ Rj|$I1T9Z6WT5TTZf^TfM++xxbm_Ϋ*% I'xKlZh̻>(mG eQAq#X^\ĭ /$0coz; 8r7}/+P_*|(ȆTmFA?LqoʃƤrF47TJQ_n F=y4=t)xA#MNe%Jfk@ =L),3\8*>eY[ႣGӬp=COտ\G[5 C,W9$Gr Mez¿{jo I62s J{'],""X%۟ {?07miJmS=[ctl`XKn4l#Vib;CQXkٶ:0+J12mcq 痥t|KG4hgkÀCk\" 9o\pۗlb"Ps^B :h+nZW6 \MU՞6EOkƹٖ-*$W8 67dW'Sw%0Q4 U3cnrOOuupOی~IbÉ`c-^җa&vJsWU&_,C4B6wu%2p ?14-B /} ;!L8 [n]Fa6p¹ϙ]uqo%T^Pc4 G'i{˜{UJj5r .RUkB,/Bc0{ > j\DoBi2,#*QmE%Z_^F2>}YR:o7ތ+g@Þ`g]]0hVI a lKlChO OHgJ}q S/ H% 3qݥIw %75_ԯD"^zVJG/]/).YҒ*;[5O.~<#iSXKG,yfVmwSxJ {c%aDZF̢Uc?aI{b9gJ60nX$Ig`Oj*w)hB" 1~x`A}mߤ:4E:ƻ(3qKN$2(Ֆǿjta3hV.*g%NP o"wiMMQ ) O2-PgU6bNO0aZ}NiU#i2d'W/xѪ#_U3 kU2g39utED:8]!}>b0rUbтDNj:L[ǯ?v˹CIFn*V W ;nԏqΣ"Hpk?t8g)؜K7JD3An5.c ,硒C?8*u\&N,P'84YCrvF`Lx~^]^&%&DbP%~OHndYS11VMֱږJ$ MGfWoO?߸|cE2=y*FeFRQ[W^ٌՂ;*8.Q P uLDZ͵HBXVAJ%@\.L4 Y4Dz֎ j^5'X 3CeQ 'v4͝)[ܺ~iY0p6f1h-ϡmo,\5XR%Y"n] sZŷ־>#47hgKez |vLRLF{ q L]Լ%L7~OjyWM$>٭, 794R|B!wƀɜ_ji(ڦg/m*(;! & r;Uyfp6֙U},!'D`m@c:Kd/l&a*$_}+ zC?bEA\4WOcW.ܭ  C)G["qv-@10I"`0RkbU or9b-1iD?W;wmpz+U]xhz\U @۩A:75Z!ݸ6j׶)](RGyd#E"f9M]4@K[k5x Z%ÓI?mUT͢0oY7hVD.\ҝC?4FhVxwt!bf,,sp}SM_8|X *tw'20d$sF T;6vfe.`"6ۿ34H.CLj@՗hDVm|X17}V;,@+AS,9?kal,|t+nQp#Ej7OMmTr>(UzXTf7\|~/hbᩳL%ӧ6ٙ?{" ̀/!kj@g=;l= L*e5D@OdWq:L^z|-ْTE$8r}gEVDG^w-\TدX%t.]TL5JΤȲHM*R*n0ŞcT`C_ uBJ6t} s;%n#iY|Y^ sC ~ `̌|J.H/K"#K ߗs"sƧ˰4;8ʚQQQ ڋtV٭2Y}LF4rʑK}Ζxx]%$C>; X\ 3~xbc77W#Prg2 R<0Y`rn8Z)-Z{>Rl4^?΂m2 UGa\:"Ee΁=szUFϥHݜOtM[evg+2%V+;iF5UG;yM{v]0C&5mzk{ ~޽䡚G39h5g}a\ ;ҭF}6zju6BNM~"d,W[a`ft? GC|y` &,d6qRqg3Os,st AD3K$Ƅq;c iIL*OSUM”kA^"zHe#Ϯe}|C*@kykw=u$Nfvw,uy6}g30C/:tD;d3G40@?flXo ۹DY+eϑ~6eUW;x4Z}'|u2\G`%?-(?b"Y12nvHYn`#,b@11oLav5P7ԧHk%}AdWmČUɐLH%. H]:UTZӹze3{W E:8Eg:VYYl`&+v=CUF[S<6@h0N)S#08FRQCab 4GZ<$-F )DI՜lm3` ~ YEd &0J2Ƿd#$n#Nφ!@ 𔶗9{FAWG&Dx5;5F(]a#(]]3'1pץyzEhNs÷ N6hjG؊-U=+);5Wlvj~ Cq؅`VLs]j )mޞf٨ U>>) #x"P;aB4os)~/  2,Z_@f?qsEn^½IlVÉaEt @qb)CQc !z ,t'mx`ӂj(NL3%mix8K qf47.wc_MDXu3[U(9@\‘(FHB_~Oa6ċb:&Dh)E {-4F?(:wZ!9VpJ ~ĵz .Vj'Dj}!6]#-?&9֑yq[_ep(x3ugFk+і w޺x5 !ApFMschsLlʰTtnVDf,> *%tA@#$h;G oĝJGm^N2<{JSL=N4D]QHo}q1hC΄dJzi)@NNq^X>";P&8&-,wwg ˪/o^$j4`SCcALv).IMU&`:zpiF.\MW2:NWoҗj;{l0{ah3XLAT';]kb'q‘_.SʑXI}Ǩ vXvA@.IdUpB N]uʇYUܥ"M rJ-/5$9rq0DΤɬOusu(ʺ,~;I c!TX`٥sqzEDhrBâ^xd8?Cy.5d]o˻֠` _of!Hd~҂58?3Ij[º㜵2H̝Hgemz<%ML2˹Рmַp.)9 cFSj*-e.a<>ŸĿi$^ wٵiՑ;a":.w<\*i6؊&.?2w)Q3>9_czމ]tѥKaqQ4V]CEI U-A.4-׸Cg Fg"s0_IdQx,3+(c?Xu=?}iGR5;JhgCP捷7QK.Eՙgn{=r-y=KꏅMz3Ԅآ:ŹnBHĽ`Xf,v$w3 .lT^8rh'(G)@V*yt |Pe䴳 :d8I5l6YQDU sX%ӃLe9[~?D;zalJ幾#X ‰/@jv=S\ęQFICˆRȃ%$YT "B9Դ8 ƁCO!/ |izhN%R|R5γÏi7Ћx?u :aKNٖVߥ8i^IuyGd} DHQHc /e•G_B6}S-{K<̽zH1J^jMpր#6VP eoFJ˗׶v(ohL L' h ą $oyld1 G u,"w! :GJe@˟@v#VhU?Lb6`B;dLe{E}UYm- {%B@BYx&y6|m3ƏXֳt@`4JfVꉫyv ЏoYP3_{* RxOO2ef76Kta{vĨ=h ٛ1Zi~| `J]ywجsś>IlSHPKL3^rTpwMw]Y04Rʳ/Fz $[Pf3 )Gx":B3.ę%~]RVwU[ԒNᲢE-JD"Y s 9IX*GCZ7lđhጮmD qI/ 9v V[S_lq6^7Z\uv_/-cڂBՉY,_TNfL]T2QԅG#a_68Uf3ۊ̯M1"QFirX-?.^.9N5LVM# ҝ<%E sc+tѿȕ>gثײmHƏ mz"`/Yt~9 bB{qx,ǖadhY0n.1NVt]ʛ_t= iu9-nKƄ'񾋥;T-70r~ [X=_@1ߝckצvxH6&8|P Z$Hc?.13$[V <xN#'ШJ(>`'>OAfUmZp'r97n?q4xD~?oq@ݐuۧ[00]q[ǿ-Lu]ZKC5FxjƐOYVMNc *O > ~)d_R2zܘE" ɳX| eh!~8QGDDNa8 W9WvdU Nj?K Ij QcOn~bE@dkMT=XknAXfj!$lJu سWCFm:d #εj}E{5'Y q&q?re" $ꋤ(v4I@vp"\1(<;5 h<1R2(&nAX$t,Q@zL Q\Z\#M?Mڲ}B>TqPt}d)Nl!?3HI{݅2I;ۻ[_J-I45ygpkZn 8ٴ[i:IS4&}!6)DTmW!? 9?ks05 *9NR=a(,Ja0'9Pn}yR:fmk;>X +ptN?B0>s,NRy[히2r ,j^e]W-eFJTZܹA+KLW˟b"$lƞvv/`{qu SZ>k>R _2}CJڌtϡ42/^4Ug57aIBn6U:(GBb], GK'^;g~HAu-(][ȊFn}HQ5hZCR軌:꼎[V I[[efB=ܻ(3}j$WT: ,M RznW@ҕgiK@93fEx$?`c,G/O#0!.!X,q'N-Lw"`(cKTfu tjdJsqVHLNp>'0:TgoPs#/ŦTsJXI8Q3 ײ]R]5ţG̉aI_+"l`h U0sf5%GFťk+C vG*o"޻Q4|b x]Kzy%Hq4Lf%eDQv^ϳߛCu*X5̸_YʼnYؠ]3BVɛ٣tVx\۟VuB8_ח(WLJ E ޫ|ֹ~cgOK]ΝvL369 &+)xQجT̝ߔnI̠zԊN,I~gVemXi7qdH섑\ G[Nni%C|*}(bE {3cl BIJBL">~Kn+vL;`22|TԊ, N{v2 bؗ-dwCGe /q#lIM{cp~C&5= YUbe1%ܡ\`6JKr=Е^;;:-Y5Y{{ljPwn֙aq~g&'$PY(Od P b޶nV}~NqE-t om=1q#Vm%7o|ZEBٸ3[qw ԼjDnKʂu|(aaߪc CI-54K8F_MFcd]l=$â8[I u*P-F`؃#_M`F3n o#ƻ4I(ëy BSN.=xfP&>((U OˌjoQV<6ٝun#J049+P,w/ݔQg꡽`VCy|3Vң|ՀO/H X=t߶5lmH?^w:ToVQ@TA?U܊nDEԢ4!!*t\l%s&l<ڻʗQμHUĠzxD3^zu wmOZ6^@dT7h 2F,x Ǎ8ND|.b%45=; ny/Űի 'մ24LA&(j ]!1?>t:8\h&w$ÊA{WthɁy$ϱuB cCi͑\D)Iee:w@o$윐J9K8w#;1 I/OASPtXhFK 3OT0&|kNۗvNJ3eh}i@/#c,]zIأ 41%z =U ^V@>ːP":"Pē6Tҥ|G(շ𹺓9Y eɘycڢqaIJS(;=,͕84y$r.]ՔӧvUN q_ lnbkCњחȌYw< 4;IX:.Ü*& phMvuS+K6vŷEedP/U0KIᧉ+^^P[K ܳ |΁3EZcQvTrd{sg0Wapi b{TqCΔhRHzeW#R0pߡ_Gs,+rC@;(=耢kH֯/l$!(*[f{ =y X+?i[,rUz4``DN.SZS_ʓz {GPW?a[Stx__xUsx|бO:CruΐP";q#Q!.0g (*cǝ=Nrlb D鰽͂0֭0([upJWL =S/C،].11]֏_3st0=,а#_M1_D rcmË8}T`T-j(Ͼ%1dC[ip#~⎕_gU;y P z0-u7sE W PTO8%!-J~6z3s*V% 6#;ʾu;utvV*6JPf2oOQ Wg0LJ~nW@xOa_*rnGEC/r| `٩!L/vr7a8q-2hQ]wg- tsAocdP{*lq [0UֻS)bƿF[cA 7iLxg4[A%"艹Ը}ucI=e ƣhb*k:͗* Yy^fsY}HɂˡU|6.dŁ4+H$CNu:J~ RP IɏáT5SDЃ43|M SNAdHl~{O]T0vP,5iЈTʆ̨;t87\,Whl(G3~oj\ B&C* B2)9JavۻRia-{ߎnLxS(3ZtY ؟t ʅ'PO+NcߦW<0|v,oQhtCmeWXZi^fwd)^VHE+xAT.[־zj(q&\^ҫ.i+w(`T32y>G<_yvyߩzct;gӭ!RO1>sNkE;x#ΦjM+M}DծSՉdIk 4QMZR\gWC6N訔{QJpU-ٮ6X艴(a\wc??wxD QB Z%w8he۞JyUD|-@ ,:gy?s qzAo-%c%GW U cB&5CoLwQxbef[G|]Q{ kYfߟLDt8julhg0_'h$[cUz_*{!}s6K8o!tdSdY7zGcb.)C-^6DY7\M1 t6x%7Z2 "Մrp|<̬TS(JI9V1v8*|} r[@Ti6S7ҴokTsд=OpQl1"8 ^p^[MPO6Ou:`i2#QUN 4\fw<", S܃ʞuat҇9zBOFe׃.>iwqOfa(dO ec OJDsҬ; QߋM7\׬:"/-UbSԉGK;P#Ð&n/ K{Ek|/MPrY Ʋj+h 5 [p\^ NGJc3`XqڅbLחdNZ8Wi=P t:uW> !4*Pw\]q4IWdn л3MM/h_|% j_6+Dhh_<9s~_%nKc gYAr -ySߟI/c(S:?&$ANZq$HfI҄T?^_R,,_淜)LcW!<` uY} F/[z=)g5K;P* wʫ@8\ {Aw ׃dO ҎPݕ/ X)GB|P?!x7F$f$ Q,Í`ƍV~'Ք QJ:;yմC :b,X}E߃B9zYp9gлEk+%/e ZqsxsqsJv9<Ї>L?cErb~SV=iy#מbf94J 1"?90jw 6/P-y6{W\C9~5Ơ8㢡Rj0T8ahT^p$Lvu)ܭgkcKzQ2e:gUYcf,9ےD"b_SґuP~t@]>47:,KȠdmݨNmDlc|x}}KaeQ*"U@c:虨G{t7uFmzp-ZD |[S^8Z2$jjf=ջ,[>Wٵp_^3Cdc3 c\nfSV=>zW{)jPܰ~=_~ K$vޏ?dBbs[ĿR#npO(X&Hm(\2s ,B5_O'rHb I3,$BfL̯W&[3}v䘆b^/(b >/'Pd]{򟐶 S@A% i!Ҳl|gχXUċF79@V{Ub4ܢ&6sV6hK,TUPy.Vi 1` &n 9uĬ]lvXcø -7r$NSnZ&D2}hVkdAG}d5G#ٟF et!B,9$;+on ݫy>|:tږ(?RFGzhE;|i!2lRD$tHru&:aAesdNS'Z Nn M&; vcxȚ$E\J<4QQąWDS"J瑢cmM|f~.En툛-2PTIۖDH y~nԆNZ& ׭@Sg-H#vpv7vd5Fb)ɒg"L(OILz}75y] p@`lޤލЗcYmsw8S?ͮc$}!g|3v&*bDZ;03)3žG-lL@tEPX*U?ON]U+h9A;4jxGAjBadQI[Nt1j7ԗ].]9xj@k; PaPZ29K4d-U5a⩃]Z**]7SnύJl" by|^l!}2귇zSTl(F-mE;(^?RG.V|;`ɇӫbm|nT&4ׄ"݅Sפo =gm ߯Z5;ηhv+@x!BܼL܌40%qXx4W~:6هb4Oh1-R /b}Okq2t- GP %ϫ 2Y *ӛ%T) s@P\KxWUבoLݱNu=Xg#KQ#QY#% ZRJ4=gUAGu-15E:2~\bjex؅ZIuu["$_U̞LYU>}+ M|$ k>~ ?KzŹp$JtG2Z //}Oj'5^+^~ ~]$N1sǸDlС\4b9!0([>~/!x6 o4ƨZGnuJrsU38R@5u_mח^đ]ZخD[vPlwT>־3V1N2Bڀ똞L(M4LKqOjc +IF `#9TJ}aI\R0 $߶^;308K Fa~NP0g 0aN ЈojBdxQWC>h3ɣQ(APe-uWΰ4~yMrLlS?d SQA*󷣡o5}fj%ry.PWijӒ =0%)89ҷ(ZH꽯h#<ʴ巰(SCq;?#WEu f׶i#ix]~8ਕbIxN9R 0pJ2*ĦSG1QHV>Wv>/gT  SXжVc(4B~t*?m?ܵ-Ѩ/4pH>9h ˴S筑 wQ;xHڄX>0JDU1ޅm?o2Ե?,uUWwWUt8g~3O Ԥ^Ɂ# ֓A[ ȷw@L:RChDC3xAVDªW$~b!3*!,QTyPxm)(&</ɚs,{:s.>^ړCgafPA9pqabIq*B kj2j3_L4q& >PiN֩06zdT kFU>GR\Ӓ[>k2Ψgm!.,]pL+QXS/2h2v i0G8u2W@m_ 7Kp=ڀM*|Xǧз0vOCߏ^HO TPnYG2ʾQkȶu*H{Z1`ѓ~8;tno=hDzb3z.,Y{@HÄ=&1]Δx=?ؐGm$wA,, |rg)[S"ذ(rPqh|lkf;!PϔR a j躲R:;U:=_qrݬ׬Z zEGDosM۵3/}0=2(8OOcuvaB;=݉zA "';anq!o}0ݺn[8Y1)SL6ypmq ?a 9KDOqpwo=Hjk3= ܥ:qݞ`Qqy Ө}Y188y*\X=о &6Vtf(\lJ5^8̨HU:AK3Xe%hQ Q+4 W@Ű/{R[LNUX6CGkTQ ^A;7%!t2>FĬI|'51\<T%+gr=?~=ZYHzMyi;)NB#6e:h47|ޑQ(d]"Ǘ+ 5ɣ [Ylt& b1ΟWf9P};C=Α}7nge&eV4wճ[Y>(n55P CnR{>)SE"eՑ>b}9y?v5XЊ|Ih AG`}\@+B L<{\-`y:bc|83gM 2%xS2xf3YQfrbJsYY`BK*mz +pOq'=s"|R ІY]G ,C2?I;+@$0.=.&-x Cl|D.L 5m|t(4+L[uUDd~rvy]bx[&Adr2prHgHY:ϫ{{oV8Vʯ=s8LqERi~bOﭙ,o] n'̀fI:\3(2 b,dҵAs|lL.L=<}9ajs 9!~?4'r!r9ߙ݋GvđW~o^܂&뛶%z}?oT: ;VfDȔWyk*ZSu !2xM.\[G*K<ʼ tZ'Ea8 ]8`$Z =Y%jI |/' Ou*#"ȪKk'./Ty s^iM B6A2vw6<{#N*bgN v`"8)$6Q',*0qM+,bUk?f%6h2; P81X V'x5g #֐|sڑk/+?"1̅_gi6i"%fuLb:, FD9LJ*{yFJo=v9U *n]:|nlU ?6ˆEF8o,O4Ch؞d6-`?%[{r*G8/gleeK@|0}3IE/5ki3GВKUR_C$5UcCTpBkj~}njZP- L X\Naw"rwI^/gbS*8+%`;&S n`^24 =-0ٯSO K#,弹X4^-9p/Mkd&z[u&!ujvV vƝqW{O#62*cdnSJl{KzreďV;_OE4`OuO;drCӮ '$'BY dgcZpjwMX=i *s?WZ1W,6MC6|X6ezX$J^ᤷPZB3$a%-k_I`s53d4*1+AgkH<2`QZI-Ğ<%\B/5܇H㷞 FK&п5쫨gDxc-;lMfxE@1QS7S.= ѧ>T"{:工=:9`\ CNg:v׭]$`nfpذք S)C1T:jW:fI2j݇ǒ,(QrPxaCX)`5(wJ]`=A4 otJCI#䲈wfv 17쒜97KKᣃ_s50^vdJɉϺ5^Z3 2H˻^ƎDciՆgxs݅%[/%9SXP{hiIUaЇ#b\r C$g b(xC|`o7VCb ۅiJؠyDPZ^8@eLAйmcɆs+F| TPxo=,}$deE61_3sn. %EcӸ}A dYM!]nq%fİxؠ7vBՍ:Kja?IJ K|r"ff=Ȟׂ˄4j &eEH>nXur2*]鐜^bimW]dR'H>WV| Ij/h1d8Ǟqv8JG`C)H& /H x?چb;÷_H(0].,xʩ$~FE b)&K܎`@(Ђ 8?;Еz51_lRLI4w IC`K+ )ٮ FCjS#ZI\11?GD0RvT/#?|: 0~|(?R~2KX+ IlGnmG3Ӄ)qWޣLs.T1,"\lvE5ظ%nf(vJ:٪= >x#%0c.l~w%U ;' uqy L;EKPn;ƿw]d}EBc7™BlJD} ߈Hf`fO?;  v?B23}]XmМ5 +&={;Y:|DL@s_Bx^R@NsNyoCmO"aݼ2x\tw_'2->}F9e.}x}t>Q;w] GU"n;{9a2Ne߅y$͆ڻP3bCz^tz-pV).?$;=lNm[keCwoOkuWxS_5t gfQ,-XLYH}%0[ -ocAy<Ӽs{^38@)S! , ' z˸5엒%-#T>,uR=T?f Yf!&Fv)%yOZ{lF̧m=fh׼ߠL$Bx›CKuG k򁦀gOD,TzFivwZ|"E82QOB43M vCx|P q _xpnj$ mnVj=ˉ$ )mj2/?+ lp9wA`{mYͯ/q ōh.WWl :~SnB^dQl"痔 & /&9$9NBfAC80ha@RĆ (X6(64YzvhFzTYA'"vBB1[Q@ԋx(^''W9^`&o"Uf>v(mE`[>"OcrO3'Cl9=aBν1Ɵb YΨQW xBnO$Jmfjvmtቮ{r ճե<kyxS% W;#E!w/tf ]4{?C X]_'؇@yVpSǺ}F3}xmd쌯`Lv5v85Ol1ŊeF/Jk*}hc7c‡VwqnrH$':{No\6t+b 4B JUyد3 5"K TB!;AX nӌ4nK&\y( Zu4`ڱ[+~ie@%ȨFC:_Q|U0ljHt9ި>5--tz;^2F#01}z3ͺh;!4 Bi5*9D2NEtҁ5C~z_ k'ʘ6"]'r̖81ӎdqug)v&.ڞIk2V@|ٌJE<:P~m-f%enn=td;I+o^DΕU>7L!  |2셋re0) x,I2r0@m\ cmˁ'~(]̄A1hO9❈WS%mhp&PQ4FG5W@q5tWCNcT}x_1ϵ,o&5ؕvΥFf=yz*2k aANY1r~#MPɯt^"fKs|$4[{I߬8"p5߰8yј\9cg|m(ù@8K Ƽ-0 i'r8%w%/F{~\0%y, n8ԞIEgߌ,<=lBX\=ȝ{ns2QX='XLm}$z9nF29DeY2dt7{*k,gU,gN TZ&(^ˎ'8-:5IS92()vv%i gbmn\WN?cm=\9Ɉ\.u0 A^1CɩQ* ߧ7;+6֐+W߃@CsSf["Bt5?\W Fty?Ԕ2Wl\B?Pe'OJ Q/r)U$leԋE&w1aC$}卑) q uh1?tuIvCs{o IPpm0vK=ْi8-$*@uy_'_Jd<ﲹ0O]=r⳵C}aAd?Fѵ֚[rc.reTvcPLd9ƪ&f#Vz_膌m>5e{g}iNqUCPQ.ԲɎqӧ;5S&H_3N*Rns1QL3% x~xoK>PiF̴ֻ i5HJ^bgcjNY|UFZϫg(Yo/=VqfJ!bXe MA򚤽2=RH-F.Sr86vě0'j֬.MWjv:NIL@RQ𜆳zXa=r"%G;r%YKOT\{ETG_<3gaAHݓd[=*痱eI!4_991^~LR/\ur)<e4GBsx4&D%zixA1md)l"1|S>TJHκ|:\8k_;t+T3jUQ~mvkr {s>C e(yŝaMpV|=\8E!jotKPje~Z%yYψq7; _ufa -xԚT@M Pz?Ղ/-ߨL]D\`s.~) >Lt8+nϳo' $(Is9=?Y ]0!GΨQM X4&[` J Ь"泐O { d(0;>oo[9bfhF_(׳aDnDq=L8RKJѻNL;Fʻ{he' PYD8(h\O:qec]Bj)䲼JZ55欛`YAkԈ \^y):H|C_ٮ'1S ڝ<~=!Cn.s\`Bo=$b/'{aAl6gqsS |P"î5s:tHFqއ&(jIӦέkC! Fq*/|VP'bXm:w/ONe*Rr)v "P^ 1˲6ĵC{lR۹2B`4WE,nu79b茂!.(hHQ#jr@ڏg?E_Sw7zLDc/7ڨ%9Cd6?fmuM|#T؟ׄ)M/(O|%GIve|>|p2V9C|JrH9!!Q(Oڂ40`!Y$J;D8+:5"O]hh:ի|dĮz',(n6U_FCsH3}Nެt0boʸJ".Cb )eɠ|\yK}4ֲ@$D8nB7WA]Be ۀo+KĻX#u[,qrsdd(vџɀ^/V 5z赙ӏ̄dEcC{Ww&v:I4A#^g\ S{VWPImvm~~!ᆈ Zc]Z%A8:v"$^TxpkbZ:][[uuhm=2tm*B`̬&]b5MlJmRhBG+CJ #]Q'/_uc" W?H2#1Gډ¼̳YU de _>WgUVI-ߥ` \14x~1űm1.(5,MfHm>zSI5,\W\efB Sv"Q$`;!ɞbBVƬp65zh,V,GHWN?7߀> i z=g&54 xR#¤T_{H ]#R:hw<$K6Rw`xѠDs6"iOU[t#M+ 0_KrZ+6!6}7=Q}Po?ݎݽli?Ul&~7VĿLxiOE_GowG} Ǎ.<\qt!lUn{9JW$s=KWqU-vG7qϬrЙtOv+1\'P~h fw}p>P_sX) uݤ\0(]<䊛$ +&A.:fHNfT=K v> /pJg|_P2 > 1 &<2nK:0Z.&w-?$Z~baʝ:਼K!yL]?5x-FvYJYSVl2VK;DSe!!&iw/3-s~v9#08 .ҟ: !FPfVf<+J' $53 Wc5<>ҜO!7w/'}h׌ : iݽ+p]&}Nm8$ cBufvLE" ƋG91/Օc .6L$M)8iP ;:6ZWCYTLȵN /jZx*F~yt]U7u J۱4 G;gl @-We4 &uk?x>2CjLci&3Jc>81d w(R)K@O~LtAio51J2>ĆMOdn ڝat͹f*7 d^h*Hiv>{m׉ hlS(u|f)}xPu`߹Hy9sʈ 02R定ш3]7,5bwJ~gX5-J4eCFWobXocbϑ˅sjv#R7Y<XE>r5ː6ózQ.#pVAm0^ |˵~HFQ1E@[tMYuF:BW@ ~X;h\bgMOqvy|+ʭyά='w`쐕֜Kȶ/ j?QT`2n pLB֤ 6zwGEe=祰S_Ml},鲐]tR[]CcxbĘѪLeXQ|aC"Q} F-Pr1CZ˸'?_"a'({e*-W$1Ӟ}(Y|]$ Z52~̛'O(w8-(|ЗHm ޢIh&a $[2]7K\x ")Ewq¶ 3 m:zEv˂L[Ifq.~%=2v#{,PwQCH:ͷ1ߥ2woM=Fbuᢪ~BX"x;yli?b  D.{\˻CDdYC--{w 9@Bů/ wwٱk[NfFMԍMMbɀO&mS 2eLDj*9[)|]_J)y/ ~=]8&w^t 0w>C}?)4zGt[ mFu-H[ nx a ͩ~TU|gg]I|Ie”7J@^}y`~+ܕkiuHSa ]ع36.I)g2\|גlD9UÄasL H/7Bh+rdqjTJaL|* $cv9[AZiqqJd?9xʵ#,`KH I= $Rڏdϝ7!Cx}x|*0T6ࠌ74ROw; SؿR%J?aeXpdRWQ*1Dxi SS}!`†_(oq-gU% |pr'Rw9$r\R=%npeRq_&eE7 B;hL Y֣OY9NwS)t/$ sX?aT[ os좎v6hQ|+48Nѷ|d{1`f]m d BP]YhWqoțD8M\}(6z?%&iM튦Kp북=hq 8,!ǯV3h#-M$tKNB0"~6D!^˒Þn ɺ U 3+򭁟3Ue ZRL=}"o৊XuG]ha4P| )5Ŕw7ҕkv ^Wr)$"&$E1kܳnyX$)EQÓ p*1K\R̀dWeB\7ÒIVRH#: Uo # FoP˞U6?EppQu/Q,att$E6rS2Tw22$Hm|{QИNK34{~}0cQ ϮdO##3+i>QU<2Y@E_-'Kf/lDfy Nj#e4; 7:>!rU {&0;e3u<FȘB:Qˡ_{;u~] 0e7oY|$4Bz`7k5Z0jWoJ,'F)?w7\"&{%^u60([kY3"8N5_>7u,wCJ'3]҂uB~=j,?1ᅫQJ!q`5gt/Fa=xLJQ@`\-Qؓ4]KBA' #vR3KI۾sál*QBh1S<=fO6 .L{q (Y<XyKqa Z=@HT%D D4^V-hNik|#K$#Lo!3#/h%1v 5(&kIFTՀoʴ91{]Jo['I%~6d%XK qIYrr5wSWK~ּs4"dKl`ikϞpH/G?JV@>CZiitE&؆tʺ\@p&s2L3: (IYna~).8s߂z$9|޻2LHY/rv௘fu,(7):A?LZ'5j*85G`b4w:]U܁VѾvB}è4h2J٫I&#<XFREGa.\~2fYpA[sj!bZ FDA:,)[2gܢ+BtO_J˝|_CE\> 2dTfdhݱ ]P?yiSLAZ*qVxqyJ$VaNG-Y,Hk>Pb|ESV DݶAصi7VEErs:"pRz2GVu&j %Mz-:O>~h[2Fq ng(xt ?p,_a٤h-X7-5?߁盛5+AXiaEsWWk.UyLYomK?sKQtuZ ްv'30r7&2kcj% U.4 P>^}df*v y 2ABkY=ٙXkm9(4J}(z~b.hQb!D'cBAoiWC-瓙D7{#FTɧ;wuKf_",XtE`7)sc:֕gff/D[o1F*' DV+ܻ P=<(U,\aRQW= Px"K Rh[Ulf#'xͰq(xnxu>E ֕dp F]%(Vf$ `KUWpD!Oޤ~u~Yo1/Z`UAURo4Pql'*ƌ\KJ"_%~sK q'"sǑ;#T-Ϡ&O+f8 00sYo/IՃw@ Y2hn5;iftW.(|J~nEv2ol 円%}bRU!*$$SBṭΪh݀LC3'q X~(~1IoN`tMrZ ss;BƒwSا)LޝnP_ST &͸3p.Tus҆baF#4 " 8EԹv#?kԋ<>efe<%KSz rLB+ ۷֊)M-0(Ag{,=wn|S፠@9I!D7y`Ph@>],YJC1E  Ꭲ❻\e1b~2'^ <gV!~wj)qMo^hzES%eNvRAԪF|55D O5O~)P<\CiQJ x>Х FE{{Op.u}}}Q+S}U^i0ȶ"D8 6"'lO 'c&KO?z %:dm-NwT$UFSn/oT^65`Uh\DnukEFܓ'd/ig&rT8Xrgɪuc娽aBИiC=sx*sBIBҷqȺ в@H7ꯊ VQۆ<2pS Fs?:6e,nቝwʼnuX;_Տ%aQJdy: [n.kɳ"85H<)vPx1BwP4z:*VS1 vk=p[Y"pU` BbhOfÙ!u@*aCB7:=/}~ly g~f_#=md!|LwpzŘg8β:W(2XhP ,i9l B0΃x@}鞜ęf`AZ"ai .< {ݵfWw2 Z>jU:nj. ~<>˶dAOTQ'?XYYˀwl6{Ͷru\"kۅanpaC5/e6W-ܑ*uP_w3ҕ?@vE2#՜^_V,,6Rp3U 3>p"XЂD3K!g[7v@;gfv2-8Y>@%f];{cNHh|vJ#$k̅,UH,d#D}) K`Rkh2V%@vʋhi Wѝ9ѡ{u|(!lI/Ej5%NsBUN€T2b(*ճ(ʢnN6M~ٳvOSE_,1?/W|)?Q %EpcJP>$Q }h$74ưDG`UHRo-bp7sĵh[EZ}Q%WŬ,f :N5L5`꼸߹FNh;QP5r;!H dn#o.)3qH-05yy9dO$/p!Fck޺(FoVI/2) C>ןT?`سe@BWN:d fO˅ oR~'eGp*mN5_p~$_[MqcX^>Ni>Ůcx#OqePi'ki6i&h\ܕ3G`+VNm;t~d̳G*lL8"]t]"6DQe?=0 K!Ĺ@)U[b"ߐ#V݃O'V6{$hIw}mVK^0Jf#~ m0ڴ!"˕Ev*n՚10 hW?,U)WhadޭNju xʋXwmW }E_@}o5r{:NUlwuU AQm]Pc"3淋C|ǂ9c*oȴYB84vz#2XE9sqDdA$= ,sW))p$~s@vڞt?@;cwB6>03E~]SnmЦbm:(Շ |cC^=&4=Fg3GHXDYX)RV!Xa Қ"Gn3\^,w1ѣ-Og(qf㳐͑$/˺u5xIKF+ori;y_G1^Pr51 6\;xSp[rfs:^``* pR+M.JB/S4OYQC$Ћ؟y42ZZZ83wP"k⼕I |k7n+i+ږ^PHԺ$3EvZQW̓-e~q8?/9 =SxUa#+N꧗,:psqK oLQm{`I"i%=k̑-^}Q}ʚcy|i/=@-;ֆq*T8ׇq"F<&0%;0i"wzMqރἝ;d:-!/5$j%KI@C ˿&H-K0)Z&;|B.eA̧BofCGs~"tt2uK ||}8k.O.@^&Q%,VЈ`w\L:3趤1ԒYuѻ&v0~j׼nsvN7k x0wDMbc} v7'л]1J0w]n7$e2 F]to3\UpW Y}0,O($6:F"@P#;;8RшR;j0/TF껞K6s)g_>L_y>*V;c?yuhWРOؤdn`MHb/ +`kQT*1wR57 ¡M [DMŽSz0RzE-1RJLK4Gsx8ޯ3޹SjᾴLIuR &(x{18d&Y_}YZcT j8(s1iܧXkסT)nɲ#>e,{tI'PIFo Lx#`f AKl`M~2U , @qr:DJzdx^>12?F~2 'IRMTIJɠ3J[)D ӻ`+i}ИJݫl'c;@ڊCM0Xu${0(Bǎ4ޟge}uo+ث{P3w g ~Yɹʭwe0W-[ A=[d즊!n!#de#.+` L O:-B#9lJخuP<z9žۉGp#" }42_ LPnsfp:g57Z̅%R~ Yy..O a4:اpLtZSN7}D| 6 "rϪq:U4ģ-ju5<KRVr_ثSPnH./ԮHVp,SL,pK< ]I$?'xy,VJA95`v#A$6`զ@@9hw0 "y{brLFRO|fݾVZtkr;MpVGa(VzvOh%m( &it+N3t㊗5T4~>ZlǢ% mfr}ooguUbF,L$ XEudz]vXx)K=HHY(vi)TB#\Yl N܈y_#;Xr˳ԗ]*4el*Q~¯n^fTbRJjѦUiYrLxNtw`lc#`#5Z X1LYED>Of@:essâxfGs{}lg|7VgJ ٤ j<_? %U Gr9Q>6j5QЩ}Z@YXM'J!7pU) RmȬk\jwHek+]XI7n!wXmjp檪"H#^se.|ř_epP|:7n6 ;g0{\&FmĠM Wf;DP+QC *h+0hă05Chسki-)-acvu?ZTLJ.k$kT~w#m"TןAzh쇰oX5L h 4{w*.uzsXJUX`o9mGB%zOLh_|:?c <'E+wrl> `]sh9 nxetpr\"XCUG )"ԟ/bHZOm5>?g dt ?ӉHs%1Ї횔O- Xh1d Eҍ 1f:]/$)JI׻ƫoNe2xغVE.X״6vR|LEPdL `sLNLu767dB(J~9eWcJwKzI&1Q|zFz۲JLmzQg:Zy֓ Ǩaa.T4?5%07ř8B 1>B1陁 'ξ]Af%j4AሰMc"Zxtk/ aNDWD =pդ n`S8q ģ7;?{>3DwB.Lef9J{B-;T3'c|'*%l\=A36HY 1:b?!t>4cS1e=>1Bphknw@{~KMxxQ% "5V|!ЦֺV˚[E1ZOܘAi ^_rW6Ym" Lgbֶ|S ֋y<ږ4":Uh3z%J>}r[5$ZLfl=}JfMTiÕ؈pveZ{q\S)Q^+02Iy}ӻ6ԫdaQ![ˉdqCv{s6Oz >|9KN>o>k,,Ӓw804./{lDI*%2x3nrkd$O)f^8ȕN0DUE#{| z߳[Vԕ5<~Ɋj9v!9W/ Mp;֐'+LWK~\l| E=|Z>H *D1\ -Ec8AC:E]6䄣=Ėݾ:9oa Pqz l'imS/d LA3 ,KMޔ{lo'Fȉ~Kֳ9QSJحXÓP\A1v:XU]YZ# kZ*,%ͷO.wo2͍C\YK *d x(8hSl70P7}XG6b85Kגscs׵w*'Ћɲ?"+Ex7 ?: W։9 ]-{fA,UL&L&ǃzn]$*vVUU>|Wda6M]\.'򇔴.\#7)YPޏQQ'3.gFJ|uw)p+7x$zLlo'իD.S;eצpdPQ ~BMXc<.\F 4R>~G'x}Q:ݾ{,)TW`H.D2ɋD10yrj gң 0$A~t1!@,h~kMB(mOh@/1E+ŤTitCj(Uj :n~ u VX$QO8H)  h's U,HDiuVB? x=N^mSçKO0NV ?>ԺmqwqSJvef)-) ?ʓrAȺA'ƞsk.JAVDq6|rXE)Kn`#ɶ1a7^8j IyAxm: @3j"C O! FFVceY6cƄz rE&JiI}ӮVƪS|7XdEZP:s=adٺS9{*.A v H8E0JUJhY^Z?W:a@Jxo't,?&\[ASV嬋UU]gcooaGm~AR}4j3oiƝwi'1MPVKiҗ_ڹ7PtJ}y)-0#\6 y>"vqHG$!$~Նv,fHJ=;ke897 ůPk=gQ('9:ؓAnbsJ5D?A"3g]eA f g K~|?A?}w'4ޕͽH&G<) 6P|yJ9-`#ESAX" xБ\Ɗq4c^ӳn55) ̵߭/i0[q_XT iq `vsO;Wge5+.nBqn?x$S\ 퀳G #"/yK8y|$ҼVh^q4.(B \m cN,O3s0-7p@[`E|\d%W3E7%32ۺKP7HOn]KmLZC*sӏ -]%Iҹ*6)DtM@,k*,P`/h;'dgbͲh܁ME+Y) /iFM9ʪ{ )uyt|R}ګSvW[vó g:x|Gq?v #I彗ݘ?ЃUl:*I2}ᶦC]8619Gi#8âCkT7Ul[VO1 = |qM^M꜀VW$tĐ:AȹSF oʪɎ$?O,E`|4j*zw@FB}:ɞYNqvCCDRL9_ 兄+^Y9RN>i=&zYrK5F z1պ(Ukpg\ " ƭ鐶gpyS-p ;fKd 9b ny^5n9 +@٬]mW&Įd$Rj#Lq8qzfB^sx9xƒejݺ$ݻe8A,*bfU.4C&ϊf:-X6s+B.3"C8%2F8y#4N)"uaT,lr@8V2`P&LBa^BlXZM‰}_]h*J[; `8H5p9{ 9] AJi<ȍOS+bi%RY@^rA&ÙR\! !~$nop%5XxIo:B;èA)e -Õ~"ZDr*VvzSIXȓƨ; 6ɨT8\,ق2R{)VgEe'z/hDnt̆bᓵFK*̦M;Xbaɻ':@\~ā5Vlj@M"T_wp)9Jq˚Igl?x5;wsԊq˫,i)^Ʃ<߲04BPMoARl~_ӞJƫgu[7ird(.I6bn>@R#nai}J3@Nٳ$@zxin!8(!DzVwb4eba{}$GΖD"XE-䍣) A$JaV{jܚMgɳ1'qpm$0s t&0fޱ%w^V,+cxQF}te1j&irznjbMc=)  |D2\Yg{\m j&".5bp+zV"(x=\֢K (7GN6J*iT?AP^SRܳ×ث;w1])RLZbo/~e)\QJ`uNmVdi!˯{] P?l)2l,8 ` 8߿UD@0 x3_uJ0 p%Fn[7i#0m\9-)BO*f3~ˣH0ɐ0ޕ3i+IC輌 $9;xZ%KK$][mL"#X~#=Vy"1`Jk9- eT(j&s ݮ!ḅɊ4@~Jt<2d H71J4{O7=,USR5eg%;|dM$.,4ƅa:,ɷ \Qx(ecw &J|},[7%iΟT5)ڇauYS)ɝ-F}]h .(G"kkrtkúogQb| MYi'|x3çgnYqJa'aKye~ aYx.{B?_QpbYmi%XtrXJjp*:P@qugz4B {|Cdp ZOHϷ|T& t3BC{z9 ֘U0WcDLI-Ś ɻZ!M`2|m#ar[)k[.@vvvjKOy@9,.nREoө׍&"FC Kr>I=7"^k<5-<7N6~u!{:6&w/ FڬPtYk[i8 {'ش67nB8^#xZ&b-(5:OEJ7FAv3I"<\-׊u.g}Wlea>Dv'|h3„gV!:VFaF "r;F 3ijBUe$]Χ烃?hR`l;c* oӊ|ٞSѐ0yGwֿ4\E1G㫆 $URPsEXU50q7UO eH8ٺj3b$}s;~K1'þ`9S}-(?}0&#[)gd'c!x9Ers \7_SNoȏi栬(F&ow }8\&9`~݀y{3 Qx2&,);xWyWea #h~v4G;hՍfˮ`#iˠAdbX//L+!U^35gm`~6L%XrS2?%d": bRIkp@J_RA:"ھ>C˕6$4I A7Ǜ{M~yL$Y`ǵ`wiUC tWuYxNމ$ aИ|dRL,0J3J%m¨;8p'xx-c m?iU2WMpdjHmµ @|'_t:CC*tfpvԉceH3d7vh`kiLCB$Һ4^([訂֖J0>=*`[] U]XǕcU.rLe5TS2C;|qo~11E< ND䉂$p1t3I_ANX'_QmLkC-H.i^JTX,Vv}`* #)&& 〼o^t6 7MCT_y/g䫨 sfpIS *LtƊ !`3~ 7ZUb쌲Ö'*DV4Y~`Y 8lp5/*gzo^['47S$5*xNcFSM +/XhId!{JVjwiXl tRݸk~rb`VЖO["d .}9D`U/t29ǁ6(vQK ic|r∈ .O.g߁66P 4dam{aNŽ ,:X#ej HM%+:1wFa3Lz9@;(:sc\v!SRܗ ,\M¾IҳH8 Ld91*2𔄋_ Wd;:9>!݃{+Q Ɗ1^(67: ؐH׍=aYYaLXy2(AlW lm).cYO1#Xgz4SK?P̑x7a{+n4JUvJI\NT|3n9=[$vN#^ @#uZ3X4&A X4d[-LBH>Qⴂ4N;:p0.O78i9̗4=q͞ZF~"<=]ښ st_z&\Y,ˏ0nK酁J$^$ e>@w` ˑQG&\I6hCr]bҍN`~:aD oHgHCq*mt$,wln$n#SP;73ڃ2 xix4z%Sn+"bn:d#XJJPH+853SUS`|7 ⌄f#IV-ώxO5PRlR^ +z{cA{fR0o0V{I iE)8|3 Z-f %]b_9x[e!@L鱲wM艓 Qkϡ״FM-MBҘJD#TH2 ağP܈?Aچ88T)m>RS]Y,d R6U+x[q;~%b:jbBec:HEҵ. VePz3AVg,uW&‹!A^!^U~s5*V liI2l3u x_,U>QļԵwI/L;r$sng(Q~{?ݪI d:1"u+~ȝ94~6d?5$3`OENT04S%a=[o;='swB}x34JsC7ݴC7mQoozhHg,^dt41irC4YJLCs;.M"?f{;Vs&yQ0otDٍCmQ FKufZ\wuoPh*d4\%zGL0"K#ֳLJyJAR_0q>)'G]aDU-tؤݛha*$rآ+E(˺bn3{payկ<,=G52*;<3B[}; ab°Kӂ}Lo"f.pU hd8wGNP _)9S0pӬD[cyw"ܮs+Wuʻ;j qAËGvNb}"Mq37bT$Q&X#ξVcqΓX'd.,iB MagnVzXu?s5bDB:8V^XLZۿ6Znqdr.rfqx|Iu԰/$H .bCB>A5quva'VW,e Rkn,ȶYLwF!~[.'XR2 FsgL{^Bj<hddIR1ZZIgT0(/ JgΊf>KwO#b@ЋEč-YB]ХwAZp4>n%j^8|Jw: 58){iVg? i% aW(- kӬ*Ӎyd(TՉ"4dׇӀ@9UE\A.v6l. ̤"X*]q,{q6D8e f]DP# Ffe(nN^S*Όt䤇+>TN@lNy^='㓤brjUefJ5Ý Aj>tU 8-,- 2kڙ¶DJs\iZKnvgVtbwSZBK15_?5K}|¯>|-σ,~ܽ{QzÐb t5 "ke"IvJe̢}C Hk8C-J3 cr@Jki_ YxB30{ϓ|S3 Ƅ/O ə9ZŹeBu@]%䇹 up:8ZfeK˟:O;=nCK#/d;<(HGU^B'z'oZk:[XDEAt|;%#PI0l/Y"2(AQov]d@:;\x# ӎT[ ץzj`%]t1Py)U2lD1mW;1<wo{ 2ԐݞL4V ޫ ?nG{Nb. :)L+\,q"twjh"ΔFno,9,|"4oWQHTPaMs&^P9UzW H䫒bн:R IGT$NC=tj} `jEtX˫럓~ha#ƯdҮ@}+o?+GOq3cyJ;lQxWs$Gw;%|:@{屡+1nZA$0vdAbMG釷sG$u;#,:A>Q^?wkO}`z5Ӳ<(-$Q UxpvWΣ~O]إGT\@ד@tEqi%$ <f.B|̯*? /Uo5x:?@793~Vuـp-lId !yHXD9hP( * 6-^EN:bYƕܢ ]3h׹yne1Xa@X[ T -Y%j9H 2 @E(żע1ɲ8H 0vTaִׇX x.^0Uw8S&g9*|;6 wڞ(v}F=k䢓\ ;k[\ȍ$~5Q5 _GUt0nVPA _W=N,~ ۾|Wc`uAkg=lg`:[C#0kCi[M;:1I )1N+=m 2ە>&MO)Eʄ3*{ Q_ÂJ(HG*׬fqR_mrʈ}:876햧YPu'Zx[)sqcLdb(d%OSc~~A_yY\qm_YSoM^4 = PCU^ŀemV70B) A$pW W؇Է:̜n&E7?73Es DiX?vEYwU Kzȥ@@H&~9>jqlq>aVƍHw <`l;'ywDu3WZMk/ EIRn$]pߓ%ANU (vM_ޝ;,I2=5WX~u]D×`v$(`CwORn{LioH=T|': y#6W+]O"ꯈ2,?刴YD@ ''LPC0KZFXU6YEy*x Aey# pSZHzw{o$ ':ZF槱U"6Lnğt~k"*mq91{16|x:spnFiw@EOQ1@܌y߿iyt$/:&@`]Ub3Cۯ%)!$ۺҗJ!b}Ku9b݃́UGL!'\dĸx $J8kL 8ê6< +~u#hbn,v5{jK C6?MYŇaOϤtm9cAKx] Ni99ɃL_BznP YQ%SeQ; ycRi˛V 8a ZIr:8=bnJWnM;{0pB,c>wmGFpQ7.7i/?"8JQR/X$Dκ_g͡( W48$Ǔp g;xRl`D: c|yK"dG7|ТCv]l;lF3ҭU RN?,3e=MA76R"s128&$0CUJc b:8SA#`L /.E'Dl:@h:RcZ~&!MEѹD.򩔋 Ғ[v #idh_SD .2P,L{~{w =zG^(̥-Q8ENI6~JpkMm r,`!4ELO jcVǬ%k{i2-1g'u,o~5<䠖mӣH 0D,3Ax yn 9`^"KH֋ȟlF,S2|)9Ǽ8{T]95%KC^:AAnkz;pۇ so@n W{&%`x ෗ǑdP2xmNdfٍR2z2zbr8Dt=w{qϮNKh{A=\i+&KN-R[F8'6Ag=Y\hojPGrd12a+촓'ZN29&|!L\XCw]eVR?{%3EP%sSAW&_@W<:N.i }YSY!OiW\ plHVΜmxQWβ,} |#]l2Hkĺ{21SZQ^#_m$f0A20{t- IPY>K:l=) *@e 5BG=tC.;[\@Ϡ}mW+I[f̖^_Kw_!#^ /U] q<嚂<*ZW,-|I>',Q$~oy"Ț7 5!X-j]%vG:_ua #"$z6N>l iUigs-׵!v1 py#Q"yNlc7fnTg4^/{$mZ*҆"d'uN /EA&9Q@:bCz-47Ny}ë|H$޹][@VP5~1"v"ٻ>a[%E0޸M;dogmGrѢpxjf+%9Swʞz1E S!?># ,bQ FS{d[J\l)mIGOd n6q+M/?X#@Z~ 7pXYT?T4S Xu^#vVmC{l%a4H߁(7DžXtH~?`Rwqm5>Wc T;_#YV2Ѯ&kћx'ܠHz >ȰUr6\=j={^gp;S#oǠԍ''/7qpp^1Y|<$oSߓG |wQy^WWnhg@ԒɭF)Fa*SE MeMHٰx2Ie7| rVyoM?}Zgbwk8KyD)6!`qc,=v.B?PJMXCֵ枉8MK"gyкxG>BYL;rZDsid=Wa+mc=rlq!)R|3),M7O*aWMm_?U!kE '"(Žb)q41eT::[%:3 J<6Yf!5 \#2K!Bp7,Rfvz| ^hRL>.$'~\I;39f'KR;٠Kj@BREf| Zֹ?0'Hzئ<ϡE2L$2A_?RdpB+1b׺$/8*{- 6ZD28i >n :u'v.|(֟&\Ti|he l5N@z>.xYI7o^츖pːBkG Wgyvz6_MV6' !3;"BX1X+Kr|0s gbm!~_k̸,5Zw )P=1=^6'Pmr7 2˿\@z@מ|n/+7#M'`p$й+,v +JTq9`73n`$?~Jo&I`;[B  Ojg*elsahh@.Vj@8TĩNc Ü렛)5~= ftNDA6s;W,,"v׃wUr7[sRLmh B.|!x!ܙV]S~VmNA_lMFXׂF}"ϙ#/$bJymdTobfVXИ [K}Gr:OTB?.~Bӧ342&B'~!\3%n evy\ފi6G[0Hc91zTCy:z߫fR$]䓸n3z)|J2}M^]ڦrڳaQP y<>R0LCW!pZ9؊37 %o ajPVA/udEԮ8.h{㹄ޏlEļQ>E-vc1:K #1 _qw)'NU@e@r7XZEŌq7[vf65wWJ܈j`5'UGe[ [˛vcr.x}~I 0ƒ5hUOE>YoA&i}["zZm!._֥]cr\X/h!:" Q18I @ѻWĀXSzqnaic&5dDFCv/bQmXG74e%ܡ#?1rzZr8%I9;K hO4~3TFqer3>O"hl'{i!Tl &p02 :$ڬGڬmB޵Fl S-s&#fp}႟mOp912uWlʿf)ref|e6:._&WO궆sW Na5LK`Ї#\%6D[ ϊ:-U0 קoʵ7, WaAX 3&tabI<T@sӔѠΞƙSdg AܣT\"`&3N EI\hO26Ja#Z9` <^Gvzk,0J1>7/j))j9p494e'nQG7#6g˫9gjvY:;/+ 'm 2UcmS·lm[W`4Kv$+9uQ!_]Hpk(sjFЉ$;UO xqS"TVJT{t4?79q$`uo'6ڐ_9v7)`i(Us3M8Σh9*/X+Ӆ*ŹatK V gcS90B@@3ɽvjqv+r[":dW' d(d'std.a`z,QN)o n@o=D6]$:+ S(6e I(B.hlԌ:qvF-ϡxE(=*/W2~=(mP$&@Q\lPZ"杜5&?h;[n?C|?\޽eBs} _@D"x UQbiWV-΋-0p%V\&GzIZ$?`dܺ@v ʖ>\o4u5%v"#YjzH\u:8)уR:jEh:t'!ܙZ0`> \(l&V,ղYRǛ%\ݏ4lmp4=?S[&s+HP ~*?lXe3& ~Aۥ)8ZjscAUgFIЊ>\WR:UC qē)yjZcM8H!J@>f4% o8Zq[Ƈ7Ux1#6RWK=-O z@E5 7b;p>&i|T(9fٺl$xFvfs_ NnaA??%NWe1i~?#}dOC47"&Xe~=TYĢYҜWwm׊WojOtcx@1c˭愔[#9w1\>_=Y d&= g\y xAۣYs*S 9*6 q93ܝ#fƻ{*MlH)~ȷPNdL6 L8\xJE=:Hⱽ1'S _'q< Qi_%],]+ Q z3B6UEdBǻCqFZa h@=1j&jPNfDTrWQIqYx{0TJl"50i&EKV&v8IhTx k.-sC#"\~浲H%_Wxb·kBHAN2SNh*UXlqķ3C0#:6` jeK (1z~-ܑRJ\$` 5v-ioloG"z+j`Z)0']h|qmBWᘝMrn&Мح}%aU7YTNVSiyitطn"}ӻ2.F1~Np"3ȣ]`3w񷓖qJURy&I4yJ1ĜZW5-XcS_W4f'Q^%(>oxi*,(y^6n[2Y>,lN쾒5jWEp;T^] @s 57dp7|i=IX@S3TJ `m'4q~(^ +װ= =n0U'JwG>HVgxNM+nF+|u5đ;q gd?BLE+PЀw2;148ϟ`J<`D7.Ÿu ml䤖Y2e ["IذhUtY!a 'M c)V891UB5$lI:Ji}> U}/{-+6ń{,gLqk+ej..sK槚LK~xk|kL8Oć>:Eo`hŞ\I(e#6KhRW "z=2 wXAz",*#Ҹ534'TJFw;pjP:1@~-Ɠ&qqHG3V[yBF7EIhni*nl0f\ @mmQ3&:wo{#9EzHZݒ1~'-vଌ'-'Q"DYT~V-h=ˉG6^DPY;Wn!plxHUk!g r4izmQ2ё6ʵ#^ ~r'*F[4KK1oW|vBG7`iAK( j?kf'6p Q?=QD+^ifM̭;Κ{}c}b sVicS90nUܱE <uySVUBp^O"]]YUv z ]p6eJdZ|%`ўLb l?:HgXf $N%\)inZlxlXdaqs0fn?mF[5 s%lN=._Ali'XTRuV<{a 2HY\m|*rW_/j5-X߻)!+D;r쀚^׉u "zH~rH{SȻb1~4\@^}g 0' pNWgKW<#i4\3j(70p?2+|e1$a΃p#j^R b}T+:@ u5?<7د DD -2L;`'ϐINan]0OXj`\O-6q=~jBP#NIAQG 'VݬL-̸jIs]2z71F ~e9MQe 1[(+0}X4{ORB쉘l89v6"aw6d(6 e \rgpaꧭhxKeB V肴#K\a b$$4'?i zN33;K]Dn] <1 9K?Sџ3{Xgr2w<R:Q@42 (>ޔwH67 j}E01{>v9!]vlt-53EѪV%\e zYi$iO xhDh/@rʔݏA|{oYزP`30K5&Q6&{ , AKe0?Қ˅Ě$aFg{\ӜJ>`-"Ε҃)v_$He=.\:b*`~'l9)@XI.g179kqׇZ^DN I=bmy>܁zT)K'N+vPx$g5`T6c+z08KnEɠw W-AV.>BMț]a\,>* b,Sfr/z'^itQ+.Avj aܻsb}'e0fީy= ڪ7l";`mx61Y 8`kRnp4 h[?˘kK,q`zjv3~on-H⥩G2sBW+mT@02mݹ[ԃoǷI '#S [E*QWTy P@W)L(YWGr_7Hؿۓ(ݪSAK-^xVQD M𭌡P\ƒr |+wfB'r-Uk4'86{L;8?vG2 i7棃ڑ.SƍnsT$@JuE $nfLz>7BrcUdW(tCJ"*|ZO w0sK_]p߾>sHaI@CRZ~k5<'lse! +3`"*~4>l+z'Kh+.@\RAhO^n>q ͂9\K\j5f&41%N:V6s̰Kv源~g{o6LskʢU&=<|"iHC$ Fti>TbPdaR  kϳbޟ8U 7kR#PXOT ԏo/I @ΛJmpa;B'8T5jOhIt %TYF}_9@kS@<=%&OK녩9<~4w)P:B1_S^*a\sxn4 Δ|}KEqdt,\EҫބNrys&_Rgx0!U',ԯsJq1' x+ LHƼCBKW3{c .jާI~:md шx`C.diV8Ug TMGs[@\#]f \ا>< alJSJզi㪕<_0_M;"a8rnz]X{(ͭT-^ۻYBkU)&:.xEQ]B0$lEϰF:%zX_ jS?J(2AwmS Bt-h8ќ7 k)m l85bZáh'U&+ '/{Kn}CY$}Q\攴^(ȶ)VnG4Ql8Ȗ͔Cf}H п.N񼽻 1p?ִ%l $os8cCE+z8Vٯ2*Hҋ&I_7ZIeHo:g@$iFDwTpA4ٗyL Ak#X/ٵnB E t30),{{P37Ť@YVk&&4? _NZb8^ؤ pP`yX.LuXUt,#>gi|UJGp+>(wKXO1? $ y1EdaL<%5bbhmTNF6waN6Ni^eg/Z]JHsbnS.,Q53 H` ]sam 'm]t1l@s#0E%cG4FiNxsH =(zQ ^^ ҔF`P,?e,/ 7}~눸o]DyUK7sb p_ LJ}&֧؃)9N;tyzwHFDn9Ю$3 xA14!HGn@Cu KȒ>x)'5B߂jgkOhxSIr\ @x_{C5/Y{ع3W_ʑFiUکD6/ш !t#!Q&_ITi|vh[ӎ5xQZM #0 UͷO \ԇ*huᐓyZ-L[R/*5촣SlG8J%0J%VQ[R)r)_PGJt$HbDۧlyk{FL|n T5,(&Diw'A!{I -LJ / ķo:ztZ1iLh3b3LT1L$byyjW#Ru"KFo~݇;TGw>D 'sߢ0Yn#ױlJP sڃwF3?07 8e7#ϯ``Ŵ2x7e-wW:^"  y^?lsTjTѹ0lȜO@m4)Zk)^ʗ]Dz5rbO$=j  qwR867Fmh/Br] PbwO*`*Bj>A uR4;uffxi'对iBr=%=AbZN`04dtE}n0ӃIY(9yg*K(!"M3yl"UxY&Ř}lFTpC̾q;0 f"d?n5Ig(IXsm\I#.*Ru*d@Ĩ}{r9mdu0F5Qx `DACQ"nQr>z ^0_1Y:O}(Ty dY>seU^.O5YkZx4Eg~)иa5z'=TZ&IKŹj5{gn7Cn..R栋]qSCpĪGtuyVRX1̭̱GI?bOAݮ( <,ӭ7\D>fSu|9qRJ4dtVKbAQ]UYX".W̝"}^B^̵w f3dK7-:w떼a˨uƦDP"2²sDDHx`kQk{OrrA-}oHRk59{ ~*cꣀf^.e^ s-ׯTm h7].Z UY56L!FHLbpjC]zW#䭧 T,V0B;qf~66~[P-$h#M`M<1&~'}~'=w̥R7ky 2;?ZgB0 -h?n>x3i'M&bH9Y]FحqFHծ]0չбV ,iџ 읳`쫸˜mUgf{NAyP҂ {?o?1a8LnbX1\d9 szL(/;-xW^ުj}B$"odJsջAH`5q!ao~z;kQ'3f%mHB2tuj?ϋŤneg[U ]( LZX 5,o.E{ȷ_{|jӉMSG0ψEZ~eNޟ;v sefjVפ)B/ _Y7HzHUȪ8(/p-i\É1d⨥Ie0bQҚoղ AK-姊ou܎YRšh`A}ҞRT6ʚ d<\/G0^7TY=rF@UU:KyI]ȩzolH_a~xB'+ޟX6)*Vm%IU0 Wn\ZR%`$84숟y|+SĈ*ґ-& iAdvj+/rla/e/z-]0W06(ʤ+Mj:8I~ 0U@}}˘ /[0to֓T0Z -xW?esxHz{Qyd[$  zM.KF?99E|T˓+>B}@(Rj\ǂ;`83aPdY1]yX283yqܻW[F,D8i \S ;W?.9=@SrWn(G:ȟ!ܾ ;V5=nEόg$Mxh;fc;T^kz$ﻉkeXPoʇ[6,6Bt=F:8͚8|qԉQiގ"Ď}T܇hHfb ;HVkKaS5:h^65@lS0tz)#x},:sǭXC_3n j/|z1ni"۫j~z9nTX;ЎgHSFWG}leeGAb+t Ԣ9fJVP ~cS5n;Ye5\u[W@ʢD8߾k[*ST*;9Td/<ΕXŧQ801;X+I+3n1 9.MٲJ}QSDa H!鳢L&̡X MfɖjV\fi=1m|b6PjAo^whtnF ]6yn9G &ņ}d2]rJODجN^D- F NPP6"D Ke(A.a~#evmS{K'(MܶNɥOer: [iL-0j|63 BUU"_+;:vCH|čwAK|ɥ>lclzVپ4_>5tׯ⊢)EV\\]$(6p}/&\Q|%KvU,=D&bg׭GJi:U [:|KWD5vR"&.M+&TU;\-:^ ь>j. ^+3X%D~YQ3xT:˦]\5 UN$[Zya7;z{69b~ N$4n9jxGvbbhi_QnR)&)6ioJl6b8d){LSN/D΄;JhnZc lwcC*߽h4&BH&'ڟWS ~& >S< 7&[}ě)5}f8b'1;S`n;h(kJNfFK[|< rHӊް ݦhXu2NY( p,Gl0nI,}з% 7_g J8ŶB\-YSC>uuBbu\s} 싚ioW(&-pmn=?A:woA:2?kF͉s)-RHl|GP_ù<`*%DFhdx>"zC>% 9Vq{c*Ъ]./]n0+0N_]{̪|D"q]E!uGe޸LR6` |B2OS7fVy r|ϟXض;r7*)ȓ(fMJ'q@޵j:s6}W6o`y՛{^'ˢ*@( 箋_]U+8`nv#\WVC'2˒}t}` dOM|l}*z3chNA^BǭV!LBK 8ڟCI=9)"l5&% Lr<gvGPWGEAC=e,,%sn3GIR)7%߾fSi}s8vxKQ)qN؊87GDAwWglmؐs( 2:m@Nv%&tk Ҡ^KNwI4p)ZIx>RڀE\KNuO'd! Qs異܉{AKҮgj /Y!P e}r&&7w\O󵆵`?v}kb3^RFǴ礄ͮ38~'I h'nvحaG%׷"#fxͤ,C).eJC'hq7hO\ge֖QCXvoѮUHo߼e@LF@dȝ]oٌ c_p6 (\ɁWlv>|z*=n%@qt9=%eh:_#e@&oBEc{hCvXH# K/.4eLō2++͊L\W FFUeA0;~8!-?IuT6,5PNW*WNt }4KaYEm!t|(]xD'N:)S_wn.{W,s@~^}"E7(Wxi[m^!^8<rq"3!n8/=RDQ{ D+͐.1@fb׳PMOu9`'1',;RGm+-AxGjXq5MR]#k6qd&%rd T. 3-1:dP+FETQe>ES{:WŌO* E콼%6$;qǫ̣2ٚC>R2qh(O jU{Y$ \5芹 E0.[rm()^jR^hy'㷷jrյG{V@\7S!%)E3G!"Ւq =/29לA7S0#Ldj/\F>@if߇V5lFk;.w閟}c1l;|iV[AQ Uu akVWTP ! >㛣\ف/EBdĩlP7(#vݒFy@s#ʼnwܑf\ rf쳫flD) ~a210lĝb/nzqI MVQpFwQj?]L@gY) \h%_ɣW g@}MY柋,y77͒yP+'j>wjO9〣)wV:[ҋl}@D]Y6 cU4a@{lV_]1t 1 p+c&¾āU^E^T[?pzzC*eTqdۃ^8[xc>BY+r x>o-\]v_:3R9(6W,nڛ2|S/AZnpI匇Г`m@0Ji>nYޖzSLLW󺼯A>ZC6OD?ɲM~+ x\0*\X] DER0^(N! 3' Ht$'ʳ@L2 6B(Nf̚,n'kI%'p{PɃJ#^rHzF.)[1lhu䅼{s6ڕeeW&f/|_>xs/ |eN3K9Ľ֏W}E؀;owɵa\mPuAfdt ?0dx[p6وVZukuo.@RQr/Vd!@?2(ʃ~4X8<8juS uY)`K/b k }M GӾ'!7-Ż-M¿ #oFxe6x7\wx[;|†o_಻^\fr^LDGH}aW8pSΫy㧺 p˷>_E=1Z̛*6M& 6Q)[#)0 Kh|M(*6QWj(My8Ya.bR/JG^r,sΆgIR;X3O!s@9aAUU-Ţ Nb$㆖@3TCFԷ~B[9d߭}I7B2o}[VԳ?ZC t> ɒ:R_xozX$W~fVhf)v)U(屪1Yaa6ؽr1OtPl׃MرUcࢽǟFW5RD..vLͦ_G3RiӋ=d4S,$kRyd*p)>I(BqЌwTj'/pAY*hCҙʎ4Q6MbC ax03Ur W*yp,*v"5ߒNTd("KGK3=ؘ)T"R=գ.Uh7Ȟ/iV#"r959Hio,>u΄K}U{%D]KUd2aq]\3YhOb>$:3fqJ5[MLarg Fe=XrD1sl{_>@=g0",N]fU}KN gO:8f\.Lɾ߿g@gyqTBBDŐs|M7H/y{?hUHm_jUáT6>9D'Y{wbe06v_*KTИ`ėObے ``,rQ $LDqX1xNC@ q^eroא<{ML/4ILNo{~i͟RfFnce aѳ/RmM ;C'uELO}c,(Gk7A:v3K̴讝<n)WW _|p LqqB=VeTI헯Pt1ngV@>Oߝɯx/CRqO`7d9X|dN&GXLAniƛG b"}32¦4ڰ[L\K8!K&;T󹊄1js)utru>vs),Kpc{>t/=2d4#Dlp!_Āz*R/+kvRmBz 8! ioWgZB%ރ4_^Sgt-Cq*,*ǷeEqِBo)UC =e9ćI׺v=*vxڷJ:5W}xSjɌxfPjK[2P$0|b?t mS{%dοH沠x{g~̾ `BywZ ȁ\ ;vNQv/`bUo=mx(,.BIeL]"҆4I!ՎdhKw/R4<%p$M3]l[Zvog Eiw _9ZۙM8[F7Tda.1^zd-rOx2AAv6t7˪GK [K%HWzTi;"t>[Sf&:al7t0P^OD{=#kj*ܮB@gǒ"NQJݦSj ڰ~MPQLJN':uZ׸@x<,{Wr^l*X4m"Uib_sz8'U0IN?@7Bk-5LSCLF "]2,\Ve`76/Gr/遾g!hq{!O 9otMI)T G&۪MZ!g3si<>,"&}(I"٥ڃ'! KMbj/=⃚ *;De{ jW\_MPE!?HA;b[5eQUFPظ,|XKÑAhE+*e>2&̌0[Ӓp*Y_tl lP66gVdExr($-3JK<ˎV|i?&^CƶvUs~G̯ R2jZ F<5Vt6Gn~^OC_Hn0l}'κ^f85 #V "V5 %)d0R^3s͋HǣŘnOi$hD֟4-yw)dq Q>8IRhA߆s[%:mN>h[q={ )8Jr]Rexn+Ǡɛr&4$s1hN_${fP YJK#b+,Sgs_Q"b$>QlFIZ>'f #AFa9oL7|F+_0"[NrHb$m% !#臬{36:Çxgot.Uq.6U(}),O+_a#eb67BNG[Vaz;fX0 D"'{<M1Rk+!wxWΤ 1BM"W4Gga֒ uҪ&ة(%nM V&ҜyYݍŊ5XROBH &@C%$9 a]EƮ!Wg)-)vmï=<M$Oq QW «ʲLDu A xbaji!u豀?6#lWJpW!9AjNTԪan|j /ߞK|ʌED"[/1>#_磩 t[eqf=*X%QYw@$"$K6, qvpL7V$aT3 btlYz0/DfZ˿bl`j= @kᒭ"G:$DP[?QE!XYS|aS|ڊ5o8xѬ֓Sbљ:7OʴVy#ks{5BC2Oa9:v=J`!RCFp"R! u0ЛC5ZwboSt)Iy|^ 1Oi 4&鳑r*;#ݕ8 l-#k\k gzc~`R3AІ},M}vgM"P&z Z|OY+3+ߟyxԒ.N$n@ .Z-S@‘NkgywZ96E ;[:Q6dzj㠗LU}y[ >cD4qK<6i@Ф?ra=Mqc=fTi-- dEI ~c+`" Ҋ^ӥl|6 |gр:f":v/i46~,H[⢆tcY!_h 2|-^Si73y5! 7'dLYXQOPǪnmeB5~&qvo£8ˣ3ԝ ! t?$e(Dh˔VMj4E|ot8qض"Aw\DS?LM*|8 !*b#KXi{d4xƁa'OQOͪy08˖O]tNJ='ⳌOm\ /{$^YWjq>pث22d=MM֘z7:4Z~gja]G* foD*dOg= `#/Ib~ B:oR,;v\NNW7c'B9&n37]U0e%tj=D=~:?h`%| k-{F3vy?.i(s SL _g9s9:.rG†i1|WYak '21+J&a#܀iN#F * HN'/sr5YGS..!$sl!֍w%Dk5@wI6p3$͡>>_YX)!"> M6+q16 {>եl&Z@f ׸f̣kx&Z 0+qI!u.s1/:'MH]+OMﰳX[DY!9yR#*2Gގ _ u`0RGdhآK0;mjECbaop$r Eټ3EڱÈ*@5O{hH4hbZB|S֧I/싳@tFIE0VxXV5>I+0f|v4* 3,aMt90#|I&+<=:Uzipha Km:σ=D?S3FrSw@XOd>H@+NAqM >\fSaNsH0H6<.0/R;IDp3o(qަalW>*Za!Ts,ĺY]8O V=ՉvUY:mvGlcJ #ҭ$|m{.9n::6$4C[M ]["+.hY^l⚇hRb{z?^5AhjU8_5cn4ec0ӽOե=$٬늿2;][Jϒh8Am%/G-J^Y\=ݓlM0F(oxmT[LMg̊Х%,+>BDEʥY8Js-:NGꅤxK_Ajg/I9"6tR.[B*tԘ ٿjiÙϪLVHы1ezLw}ߋ4H`4 b-ƈ@w@z$2wɦr?e3b* wS,[z59=? /ŇV4ePO'H>2Z` ;v3lٔjb,p!!`M1ځQ2,mC[ᖂCg֩A&Tjs089CCC XqIe!zΆwfiW-~/VK"kR\8O4 އxv#PY3 (&AVL4OG=)]Vr,nH_t_.  VG`\njj;X=lѣ 6>Clm/6yj c%iT'/XCQ/D$2 XLrbTۃI]]|@I9"՚PFpbJ(*%8(-չֳUSI.LR(3#xfNF!XPRT%ygnGBpDQsm~KbM"0{gh_:8w* ކs]xNߍk0C^ \Vι 2cn~9Ŭع($z-Vn|ɖ hd GrX8Ԓ!n,U/bo T2 U#)VڔYR0A0+UdEچ%,]QǏ8o ӹ|F]mg=3ǀ`{2s=< W,KF,}fJ|Wz o{*=s򡜎6_g@V˫ !#vfAiB2)tsv[g 6 aB~4vUȖyUBY<9#@k4MLe>00jtŒ{tTӠ}e*!>1\+w&%oe(c[iD$vqwSg.uͨ t ! b8}P]`kaC4#+}!34We酻4@L|!@&$ֺYhZ=, :cW\F=dP˼TT#9'өԪf庥:yYY^LUX62;"8] BT#  Qd>M91};@>=n މ8i'#i$B OFpl–>[+!>? W7TkثSUPZz_!Ovo̹>qqHmO|B` mAYvñΓxu).j$F#_r]Z NK_ v-(mƘ{0G$җnI@$䱁÷LJьuǪ]-x;xГH10~XR7U eNUa*'DQk*cq2MKQ_n&?Ɉ9Sc$=6˯̩&i %^.:j t[օ4MWJ`߉xPFlRۮg>0و~iD7wW@؆rW+:춯Dv;5sI.>>|;[txv12 ؾ Gv: j Ld ʂ`Zv-H'9?V::I_4ȃjs˯}~ZJ"zFFIJ"v 3ݒFtI)$l;Ksʢ Mdra3H$:r1zV*mYDLy  /dIR|6ZIUH|r@yY=rQb'f(E]~TF.?3k9G}72WNㅵc8yytlv'oZ]W⓵i\lR9ƎKbc0TL7 J~b sZ+}b,d` (eWBmfۙS˻Գ" 0ܶR"9o7\T܄2AK.ʑ38UqFFBdҏzbxϊ`k ,ksoA6?V9~ "&dlˢtͅSGU݂DdTi9Z*$L2!vf-e%--Ϛz%gk&Gcnq[(jn4 vvyocL%.g\é͖-,T |cNK{C2o_3Se^xkpTvSaJL+J.mI] ׫䏛3Ud'{ '7A%1hogI)E4';q{=H(`s ߫$(`IQAa*FEHH9>GVkxY{l*@ mP(iwK&ԊkO˳r|R!·+Ҽ-:ܥ5OxoMp=io8jvurOIqt:MKOse&N5aikHsfxV(E!E86"&נ;ʑj0o#a 1h\N7=CGY!f ;mZ JLϣBE.'Ux%Xz:p̸NAPC{ A%N5̮VB_[l\2l+b,!FV\9/h>x&ErD m$ej t<(.Z `8O^Xhxrl+0o=J(37/8!d;R/`m8,; Uk[忉]LZ;BclI~W;jL*IOȧzQ>t.K7Ų2WgzA̞[j/Mg)>NN`J'tSbzdz@B Y,l= U+e/d`7Qv.` TCHZ;S<_V%H]WC=^F2y$^/&K6A6gWǯ3_(]e;}?XH(.QYWg@+#`  V-0-=? sD+c{YGtJ+3=UvgXDf_ #zf 8Ǡ>!)" ;7LYRG,+uǃuSM* uL 8bb*AYH ^w@u^TU޿e$Lnthawk69)Vk#`0 Jf3?+BFp:9gn~,*jZ:VR +᪱bPV [x>ZeVB'W<)uyZ}zӻ5 AVbY6䋥lDv \?!$x-)-^?ɳ^ɫ4=Мg}?yD8 m@wS8e~𲏖]Ek[x~U-`e6+_̏,ωHOHbq Y:ݕI%5UOp" j/(8C隲6H(R\舄-X<΢5~K۸ =5pR3 6o2SYI|/^R[N'W#NZ/x8H?>hQDɁ]NC^A&"'Qsyyԉ0܄U^-iKO JI]O2t^i}yc0Gh9cz]tǐ~& :KvTtK P/.~jip?Spk?!$V #T?"wGv 엟Yg~0[ߪw=3X&` ΤG] B^3hZ~r)VfPW񬚠W}@鈥*Lg 4mj:`~hbEE<6q=KdzjW#~ yIiv1?t~5LXuSp${W<VF`Cp3^΂>E X!B25!sz); .xULJC|+C);;Cp`&E9L)èRJ f$č]'/Tߴ89#,^BÕ)?YGQhMΖ-.n"קrPqQAm0Ȑl-ebNCox|aP':ψ}FhpDoP=,/4K*Yۗ֔ ώP\*?7 owT̓68K@"?CFMoװ&HOIX*V{&f;Ic]`lްjiZvK 㡱@s1I1yQ  F=YPU YdФpr<8l 4ƦNIEn:Խ}8YcӣY:) ym|&qEyZrhWkh(?S@{ppmF23d"ʅm.(UJ 'enHEU4c[,.*mU{4. ߑz  Y*RiGp9L'jqT|xX?JLQo-/G@D< %,Ir7Xm&A@?^k"n޽! JtGq*}>J$w#WʶC]6-ΐE5 Nodt)4dԅVhJaHϳ8D]"xLw0t4 RuGjq4Bq+$HH--cڮRVxq»`gRzsCIgmIj*Yɣ"r(&N/ Ppe{ܭ]`-(rO*`;xUfO kf>|9.vNmb7qc=}}6ilʶil' lKuHѨ@DK!M/P]Vxa闸u_~SiWp-.lxNL2c]qx\ķ5>yH_9d]eW9z}zt+AR#絘dB=z>`x8^U'C~}*S'=L! }8ogIۑ3,s݄uB<*zdq9x;SsWI=4^\UXm_2 `nx+vs>1XS4i[-ӿa E~6I3A)tI(^FWnfupC{8J8b`w[Zo\Vr8vÏL aܴ-iK"l2movlȎV0`6U4YLFáa!Nxb,b)H_ UZ7oVsSd҇CZa{[*cu>+*p22ӇƨW_m8Kr" Ø%ÙwRȔeO3#u:(j$UЩG 7Xj1Ce06޳rfWvH͠u\9LMn4eM'@uZd$eg\&ZɸgL@% N'˝&3NP68/!3 ?S.ɳyOs@߯\ns#,U#(I0,_o!嵷0E`;/K+ZvNo% 5A1FN8IdM3 ͪ%9v4l7O]0}'4@Bbj hd\b`HE,~1p [;a k:3RtK19`&; z&9o9̝GɱZtV=Us HEÚ~3~N}oˋFaˁؒ1dtvrrC٩h.73jN0zCK+|ᇮr?Ba` Z:q޵#zf"%FYj]CAhh0Q& @&>(m u=M?/# %uLyz:7ć(Knn:a?*l,a_;-kD3^Xo+Z`21s`֋zҒ~Go"&N$)mw0MU8}n9!0cҶ= @_CK?IId(/J, @JUk(\( K_v&"mCsJHQSpkDvDVM bRV ,׶u)WDlֳ`ݗy}~?t}pԽ^9Jf~)KiYއHO+^R?ي#,`Knay}vu7?`\{4_I3c\168Ó3jjHj3ߣs'? 2Qnԫ+VK(:o6\O&u'F#/vi lΔgb% Mb)D|!#Mt[pKXl,t FIZBPqn%&sR[T'0˟Q״AbU$?SخIy߶0bl0?> DWiIȦ5p4;I[2 u;;?NGOL01WUmD4M1ᜌ,Aq b^5 ׺B.G~[ %-E>EyrZ-bNħ/B떡_%sM-)0E qg=T>E`D1 b#JQA2$& Kov`pT`᣺U&|wߓvzab T'9{ɲӫRl E35OLp{ .av~.<$p/"LU] >/Cp,p]\0VE]:ACij6$,P< (J%>\vʆ\x, 8/$XXVrۀ)q@fS$V7!~$!2Si]s0mYA<4$Ңg՗*0$0~iAOCXhɧm%˽8?(8_f*8ִRn& R4@ùXPhx~{/QJFO۫mWC89M`M!`VT*G1}wJ0X:,5{.%5B|u8ze\T%C%Ӥ곏qǤWKp WWyh$>@xO< /"p mPvT=ںu$QP!zY+CGlvRQ!4.x.ԍgy&ۛIED.=`V@EOVCzOw;Y-C=2\h!5邹ڎe(,;@AQ Bo=+:t'++7. U ½Av6Njzlm=@@^gaɛz}gJk49Я2GEY8+Q"%,9&ttj9Ev2(dJ|Eea>f/ݦ ]]j6EG1ygFVI3ho._ t1GE\wz\P{wG*@@D;MhN7ȧr׋U_#dq  KJ*PgӰw,xI vt\$,36@(Q~NH[]&*>$ca<N,̜yc$ʢݭ%FۥEg#;kOċХc u6uD+i0%[H_l`4X1]RL gpߐO"2طz(w Sm=AEd^ECV!DPĞBN"Īr<`Y~]'qq O5! = zzGC4'9}̃f (kE$1;4AiS+=' E&k݀􊚻jh;?48[Ff*RLs4%`$&H  Ogq S;[\sxI]Gt] {w5?_ly}'Nh]\{A(L-cLլܸ)4Zk)f'4ڷLఎ˃ endstream endobj 95 0 obj 161424 endobj 96 0 obj << /Length 97 0 R /Filter /FlateDecode >> stream 2n*)]T>e.mYBes"?x18dPz qݑ7_MhF[ہ|4'o5*HD|dYګpBS"gFeX*Dz>!1E3|SV,P\Â'Is :6QeTOB;IDNJ :8Z~to.OGʰfKiG2n,7x:4[u`-# EMZl KTey~YV+$b)9ҩujlܻJHyf ؄R72sh+]ݸL]] ˿̱}In%)x![ \qtIr閆iu+peptdCwaǯ"\ݛ615 SxLl(X5|c7-6[ߞue#|\i:RbLjClB=H|s-%zZVg2vx=Amf]O}7̔cIQ(<+cIVso5rhx'^%kømAli~Q@=(7!Ϳ{gT|>JY']dk$Pʙ KZ*Qը6Wx]QFUMwq SHk)fnUev7ڜW,c>(E>Rk.z)K83oC[t6" 0ۺ 9~|CѤv%f endstream endobj 97 0 obj 1136 endobj 98 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 96 0 R >> endobj 99 0 obj << /Name /Im13 /Type /XObject /Length 100 0 R /Filter /FlateDecode /Subtype /Image /Width 3000 /Height 1600 /BitsPerComponent 8 /ColorSpace [/ICCBased 2 0 R] >> stream ƈ?]V#y?BibgnD3AvpA6bZCt*x0j˂SP&F(B#>0-O"~vBGX$I+n$>$dp"EOz:ySaCt:SzAUqC;\Vd/BSϽU±/_ܱ Ys_C7nlCZG[￯}2k^"c"aߪ2sb(آ>7Bi)=4lz)Մ,sae ~tҏtL?RT$Hs!rMiê I__HrbwUеG(oq=(b@r*@d-EB $tIҙU菜/,ėh; lԩ Y I2&S;>BP3ONYo:"]NB ږ=)TUKǶf-Gra7! c,dx٤S&\8 lc.m.TNIme.VPT< УE˸O4n"/{_2#1jmoFaz]*>X|OZfs|g_ꩨ^/c_qt??JbE A!?xJGkyw4˔IPʙo݉ff JQ HyWCAl,ԈM̝Ƅ %Ahk nd5BT(2~.u?q)-LǟŴO\rvXӎ G^:UI&ݻEL%I)KQ:ڪ0`ɾSQϺ vH$v3^uG6b`l ;b@[;2E2Ahsw35Oͮ(g׾u 43F`})mwOYUc* e)I9('5j X O4: UIK3Fo/pMm0^{_u͉u/L6&9Ҫ#ǾդgEwC6%A&+pޖe!{M>=^̲=0;i̭Husv' %Be{0pIo3NIh̀uC}obwbFc6VRTs;WζEY+u|%ڃ$gamu_Epa_<ǑE>qYP 6CL Q i7XckF*iL"ؑ~MA)$Ғ| tzII#Xz®7ܟ=i{h2W JЉ]IUVI/ͧ\zzVf5IO$oRC#0c43ttkywcޓrMs$EmN>VWuG2ԍJ:EDا8`]wέt|ɃX\.;^OҦG '&܇O0U5;ʩ} R0r/G냒l.}8;L:]z~DTΖ>x A>>i"v?d*_d=]eXC aa%+S&nB Pp.2fAFJm& OGØߪli|׉oKOÊtD*Ļ̖5)n,r7~zT&"tG _S?{I" B7Q+5\ QS{{7oȄb뇣qiR7EӒ [vv*[LILB]~EamS<<\q\:h$1U 6鴵mZv,L=|V)6AYO5`0>]}KL|F2ľEr.8a(EʂNu\,1ɧ|Hշ̉ii(K@Lyy"G>ܜÚgccZd<k <80|y\S ɰrEU.7 [X1#*- wft-F躱yS8/P']\tvE7'yitƓ<}5ɶ="Ub|Km !Kf.]q쒙o gi꡶9 D(g_ȖtRms'j⾪_Swh>8"U <kEZ[( 8,"ޚs9J9O>N06ۀOS4 :19MmifQڂfJ7؞Rזpw^7 frC(guqEO6wR4]?}[ܯa(Kj6M?J_4=Yb u4m5tJ*0CNXMQѷSFX~"gMNAS{.5b_OZJ qj,3R@Dؾ-= ȩOUnfZUؓ8b(73ăqe 8az"Mn-vRzb)w̻@2EcCD:7[WWԱ%]*J_ZYĠVvI)iҳ3 !a^<}4g-XdfW0=ć짊 ƹ*>; 6aV8|Ŵ"<ҙ2"vv۰&DG_jeVS/`u›/0BbJדÅ)rV//J ĥA*>l~/h!|j 4sӸzt$88p*݅G99 oUGlph3F[_YX؏X.o|Vy*;#DY&oh!=iO<iElyZXp_h|,MUHהO 32^zVMaBwܖ5El^vߢ!~TKɛ׋}b*МPq NY2 te|Z:U-3mUnҺn+)~& B[Do*Z=f1@N.N{u:%xtE+v`n̯ 9h7rZs Wg bUߡ 6}w֍9@V$33T@-0>ֿnl=K&,va(ϚUc7 omg(.ZMukn_B>Zd_3MlL1ipJ%}ş[2 {Sr)n*?w*Cgͦd4܅yuS+:["Q܈ěN\(&jLM¤6v/˲XZƆkRP |_D4*lYߨbΊHTn_q93_w'b=vo$C1)dsR5[cq',ސDFk/VL_ouT VjF81j9h&u\Z`*Lv#kj_#3ٟ^Un4Q||@+v*Ԃ'x W ܿxwV:g`Ta1 $&:o3 ѭk2<7LW,2Fe[oVv;~=Ճ2WefVDDa] PymnL_`[AϾkl:﴾͵2P2UX!oךt(EyZJ<kҾ H~c$6_=l$_.Z[i]GV-ILq#WPGR`}Ԋ,֧nQ&%[d-qƔG~3 Wwc4-'Ni׹ <.y;3.Y+AnN͔d|y/=$ SW-!CX-X}?F4D闣u%3;=]c#T 0¯nw)}.{RÃ4t(s`dl Rq.d>i _ݷ{y.[.?^0d ɮsh13]3fwERhwxvkvyh5Q^!$iw+8BV4Ķ ,M&зt!ԐɬIMM#aUXiTPۑ`|A'-Cv".(5-#u"It̠`ā¯m`= Fbdq|1u2 G*^>\\;aH>9q`k*fU _7U\-~4 pl3{aceۤ?E Nn)XcBGdKȪ64 .[4bA#۵Z%ʴZ&B1 =7ny@f!KR[WV{X*}bVСʮ Jgq{D@Hƾg*lLfJXGɏh2xM , D4*X\K|ߟsJlf f^Y@׈kCfz+'6'h(ILtC"]D—qy'w$bHՔTа"%%Q{G)b_:;9ԊF2=Lw=~BgXန8<#  }٨μUϋeUwb]4H+|V/Y*<~TJE?wi C,K*%7A-{cVתYbMUj>őY`!{f>f--5?}B\<80KppbSr22_5DM'lStQlhCg\ҭPK-NmHϑKG?{žB/U޿q^\WX^H-, ^>Xc\aPECo%~A} mC L-p;UM !@Z+Yc .|sy˵hURfsO0~  U@run(oodY_i`%WD/Q0]M8hG[n2)7WOE%T+{2V> 7|}mJί7Ui|kJ̓"OA-XB i_ z3fv6 ֧ǫ!K%RV~ies QWU}[+,y4˭T?4)w@t[.o^K".xu ?r9 Z|<7uYd 'oid|M'#6Mԥ9';wbގpߪ=$[NhE")h `v/A:SgJ šCBZY8ŏ.4"rT?mCWt8 D_;V l7j#V9qt%`$ݾP3g8Gt;I%@|FxlPw=_Ԗ Dfyp,+2r_,8ϙv5]?UVR(W#h(a`6#*$XN D^m(lj82j@~V[%:_=k)$ F)i c~Y)45*Jw?U{jؿHJ< ,ьH֏^.ؗ4(_'U K  VFl<!O{M';'"!>f՟g%?݇!ݔ |yud8f]_ {&H{5-[ez)xhEYhU5xҚq榾S?Jb{[gú7? 1衊(]qQZlFӉ"Zp_]X/'OaD\| lꌢD|'/cM;*f]M{?6-_~⽢u'LrV*MO1{J+KPƺ0t)|m_MlYY*,=N3P`-[3ӳ4oX{y0T")HLFD3A@5sp}G_2ݴ<&m НKX?AMz;{M!"+FF`^\&em$lЕ6|J%;($ՃGCDAA/U"Ͷ {'ڏv;Q֭ൿ@e{BjƝ))eJ)3~{= d yEmꑫ$u^[jPd0dWc Tq-nl)L"`3{qv!~DQx:) ; M"vC@18_RŒhP),4G' ЊojY{L )(sӰH^lCڋ#NŜ J ),ĦlMjyc-gdr184+I~r>#gE -ںZGm_Av4ez=m>l==JR)H>;[! MTqXy>FT\myr4]kBWMux&``H [Z͵A 湖wՑ_Xb McĺT1 f;gK _`5 >B$xKNG3J|ۛt5!O!y]$̬DB~hl@pʍsBS7("El99`U G Vm+ 4{юSIJ@l{L"voJ[N"~@{ަB$:8;:"՞TLPgLKL4f$D k }s(z 4WnLT1Ą2'&௤$#Q‡ f?$Cx> Q 5ܮ'7nmC=w@\ُF s֌*5Zfab%gXMlUz[Rꥍ3 2\5.S ñ&lJ܍|Qw.exD@ɈZ8y뼓>݀%ZI@pp#5Dv}s6]F $E?| : (6 Ҫ( N==nI!?O@ Tg/jl)7," +mф3)U$~ݷLѝ)pr ~7ш{[ fykZ\57Tբv +<j}GZҨAs6-L(~kj/aՙ`a0lb2#BnVfؔTM&N&sŦsAτӜj-v48:ܳbS* zsL Llb㸿8m РS[t$T02瀃G1%7*043t k'oB,4 nӏZ;n.>Ⱥ9Z=mG"%&^8 NGXEux.%1A1qg{ ojV]U<(7RDe_Y1\JehrbL?ʼ͛?mvhk# `fsfF\h(CiwTr]D'Ew)ʮD#tQq)Јk e)9}pEIfeX?f.@Ģ"EGxvT!}HKV㍞N'(R6ɯDz[XE^v=nȨ7#>,Gnt9iІ#ƞuѪԣN ioU{0mXkV#4sv٬5%M>Tqy1^4&H~/7Cka3 WA8`'ky'LT~Jv6QRqըP/Z3WUͱ &g3$؂RSV^lYhWFS5 )P)vKc O lh *1$ӐhJB[ܘ)[_˛g{ olbfkLC?eK0xwp9X`,jE'An./mv0 =*4ѧF2[)1k}й {HU7 00%Z>slL061:{3fr6%qZz7:=9C y9txP|֧l# GXUH[ F֘vXH["ڡ>+3V "kqt*ihIT2kqV!M-t'K^zh',KNGV6,:Q+3o-` $u U'w؇2vhys|{ðԇmߊw,98vq;#9x.ASsz`_~ؿW94%24.;HGzx`c &xwmm/TTQ=Ieu+ ΄_ T䔺#hmXD>7Ah/Byc e1<)8$8Ccs y/ǰ G15Actq|M3v[†uׄي^o'=_O27# dF!RFTf2/G/+1E"S$HBL Q8A%KcP.^xO3^ 7?3rʷ6Nl$}NsXO&WR~3ŕØ SM}NaQnRm91"|1:GQоp>¸*\g$NYq!>[=%/7'DTy1dAJm?,{p#^(C8QuSs^X6 :N2Th̴3'.Q2ɵ)2vorq܂)1u,fr P[_%o0V*v5b7vC7*r}A=`XB'Hunٷ<AYW6Оv)0 < A|g-0p,,X;'s<>~:bt*z9c$]+@^Fm\',zNp*ɃHRLhrZo L[R\zcD<7˭ .=Hd5vt06tFMRd LG1\c-3*5|)vJ,bH(q w #pkAQ;eqfk<|o8:ɲ0qON{wI\ ʏ79Q-0 &1c҂q .|j7SBmˬyn=~R5x' Z('NijVg@O1zjjCDp<I"|lj#s+)m$Obڦi'rz'3$|WUt>pz`Uz.6ix|-aCvC!KW:CSK@3lQ`"1YSl[.J!Y?|:.q__yE7d8 2fg"NxR?!.~׼NeR*bpAb0*V[y/ӞPj>" iQi4x^m`< MNX櫹d<DzR!vt8}o!bso$Mzxp_e ~;ђt g>D\bg&&)OaڣKus8(r.p|aCe٫3gNQ,RSC5,u:"|{LGjfѤtƝ0v+MNR|O I!ra`<~4 ɉ֤h0{ŗo,80 2[:ퟅG kry'yCk۴Œ@ o2 Urn<7f˷ z{ĥQ9nm j^dA#ә0mS?abmZz>AcM >wuƘk.cM4Sv3PS9_୳9]5 ۔ —cwo@lmM0eǎ*tN&`G 14(Xƌ2dy&OSE~ImifW,aLfqr9 ; iҽjTF X#pj?+I6O^. FᐫلI6KV3 Pʻ8|_B[B;Uݳ1XaZAnMh~tP>wl@pynzd2cO{nn|ß=x<2O@<ٛ 墢冒௏vrP dN@_t0G=k똥#0ɶ"'6H<2ď;!3Xh67CY}|38Ly/:S-)p*28mT2^` ?Ph,HqQ6zI Z5qrznb (9VۥCqK"ᙍҰ эOnA03fs; W=m%oԾtMy#E7b MṮi(4jR_^uϐk} JҀݫ( vnmso]F`Š{a5;t|Rߪ[xwM bx̟Q-.)E)(n~lCG9̜< r"iʸLNQ<;DjX>#Cܘ.yl(sAge6D_iTe$O.PW\,X4b{=;ǂb쏏vKSR `ʎ$H:ƥ<.YQ;6ǃZ]p%&sIǕꬹ/l,J9wz`|!ɡ6%#ME弫]{g)'TٌhF.Э>pV3P|)+i5deg@0 (ot{Sy}8e{`6r5z3ʡ/"8O/9mPL/{QϳX 7 OQY Ļui$dwת#;cf\ lPf'΅:)]o3NPddj,ckc3bjNF̥2^mU~8pȷwJT}BU5(?fd~Tj R(N1TQۻmPqY`VY7^PƏV0V 7ȅ} a]ޖ-lF*!yV7QKU4=Htļ?7I$*iD89T; ]oCLȇE:zʜX < \d8 z 45S:ֽƉaѪFofGYn 4\d@i!Z| -vX&7.4CC۲no *%&U( aPJ|#9 RIֈ|CB'oa\ Lc1OrJY|'CcRb5meU17i߿8IB+C-h8{b-/qO@+ʚ/.m@}WDו孫LA>xQ yS Q|SvXE3,O|6%`$r):|!cn ڂOb5N-5ŲD1[%X|81% \KgQ~?݈NJf[&È=˵!m' Ӂ |d:QkKƹ uv g!6({_i4b Z_L"WA)Y* (IÙ!/p*wKv!tlü7Dj'ylwt`_hg¦񵑯QNt(3=5:eqZ*+uhh=@>BC]H4Tc؈8-3x{9@ˈYCUY[P= ;@~O"kBס,C XL[pQ sҌ4u Ec \nhxwF,RϰdAup0Ç#xo+wuͤl`ц#gJ93|}"X, Q>hJpǼD{Smm'N-<>oRlt31D%)B^R?ʀOȵ~Fu#gAv s:[q^GiluN8pز{Quْ9}VS(~KwOKUNt$*Edž{G{e s\G_QLƖЊֿ ;u?LjҧMkp| lvF^^)PFOtx^IM,nh:һ c`: !/hʑP(g҄Usn~lZL& ٚl( %W8LM?ɇ{R)s<(`H& !<c4e1ۨntNfmOUh i/J69\#$e:1gUE"avP4:kYEUUNU SLךG hh[+K6h}e1j܀B^Eue7]ʼn)O,x^8 2yìg.Jcmz 2w1.7Lj0{<sx`J]0{K3WmZ۽bl5{b腑Z#,C ~!9*3rtrq /g^H4B"D^w>Jy؇|OZd.y:f(v:)G[G# ~5#LrVEҢEj-ic^cjn/|Z49Y+5#4jd. J܃YA3-ns0ïVր#-8#! c^Tȸ3ZKTq^T¡֢|-f]p59u4*nd76Yi4KpQF+~ IMKxP/vT 6Wְ|zױWm;55MJqp=vܝrfwg 3]WPG۷R˖j!V)3<$eF'aİsU\mI`T:JO9f m".qeG_[ *F@Pyˆf5xwig g[qmr EfFNUtb\qtu z6y'ӊsy g))b㲞7^ xO喔:_.mJf&:ShWھJ*0A1 y |:|3~== c܍&ߩ=dxq nAcK2^/ę w{G[P3=2B9N6}BP0$ /z8wʔy㬍E5"nkH@Й:m9 bՕ;:L`ɁSǢ~kRaĒɄx,#UɌB BJX$%;G :gȡLQhKrMټHAdZPj &9M<nL- 02w&l;U+U(մ JSd30uWn7FbBik0Ϝ ʕrmм"4I>\ Yc]E"_x [SjY.FPVG%?Ur7pScP8qѹD ].UF%xmyKS2po) :Eup971u.|OQ1Dg);s>p;KgAM3!x=[C*rIUW{nQ7'qٜ¤nPWr>kC30)9H.G%> u}xLaʑ[@&f.h:YejԎ@r]є0`C1 Al(i8\UeUPƄHl𝡌͂ij'<UVU ;Md3);ϗ]ǓH MU W{J\'kޡBRBZ- 0`Dv { nHt9p'~; \SXJME`Xړ! +?_w^ eMw ô'_*>t5on`#IuaS\~Sݴ6VUnHe+n4Dc?CC!&4GIwK#p>r7˄qP v1@TEؓhZҭR+"}`TraZ=3gXa((JwwY40oXp|qPKYfGhn1EdtmP_Q٤v;ϡV&,$eJ%eUQ<7+x$|tgϧkWKP&kˆxKjMF97&v("3fXcdHl BzX)k⬓vR6Ivd['湠|5;c:}[LB7cX!IpOjtٓJZ8+O (N2ː8~F?vD)scue)bM5`bmq9I:Bw_ b fgi&`;f/GfݏU[Ev>DŽTZqN&2U32`3e~5+Ndu6^nu[JDI5'RJ߈T)wǴucFJ^ ȅJ̾u^.F΀.YZw\R/.w%=SĮu2RJ y Abs3]HH&E.~s0Y6"3\aH(G*6]Xgs<ewX~f;sp59lʛLO$S:.C ]X!utkC;(UgM/Oͣ9%q>'Yz}{B3c!m(hOG@QN@"FY;W7G [VAybsV)h\;& V 7v4;z% 1pf'T7tHb:1 rTn!.%}AW§X ~ -lT7[sǴcÆ'&v)(2Lj-BT/^ni.{Ѷ>=}tWcO\r`JlMsk, 7$nĕaGbcn l@eo%DD'%3/‹k%"Zq&,V׃LZwȖV^5m>c.vԊiso9ͯRvQ%deȸH6ZA:E@i ?d_EzuIuz[19}>Ed3Ӻ9DSjk}i^RH~|50p{-"j­eRB_%zsbe2B7qb!*zuL1S0ŠlX뵍9)m덨a i?bP0G;"BO~ xfMDwܠH0~W%j W7 !+x!9;|=:~o|-Ѧ~ ‡A꤄:ϲh7T;7l+d|z~!:8 7. 2KA0e50nC9@pp9`D!0;m BB+[R\4\C5Xw+ۉDLP;%,32fd&Obאs;e]'* 0oяwcƅ0!C=c&%f\r(Яԉa DDha wl'[̵Ja2g A/;>Pծ $@kCNBkCɎuԊJ @}yJa07,OF#!7R5֋i{050M@!z1Y7ݷ f^jNa1T+0<=KD畎Ex3ό'x9+kQ~Rys޿4-Bn?Ƣ/rpع HӝҨ1^?0ځ+*+kGB*Ϡ?ˌxRٌT~ϑZ,--=j6 JKLWݢ.bK= `+ ԕ4@{ؖȽJ e30:Y`n.' ;U`wߋVE<>HT.Ȗ%-{K.j%~`uђ3oQנf ނm4gNMAg5 $thE3(߼ͭ`za3&EM>OS0U"q$ԺS BijuS$x|ݤgH!8c6o{/~R@zsF%-^DJgfڵM]2W)'47mg0`p6~[^oaVJlZ4>h lbI6<4M) ' |?r:)1X|h'/HH |݄Uq=!hTLy%GvOÐ; \Beiʃ*-$+y2t]qogωRڹ7^w,{Nn@ *O)[̧E.A `Q.ŇbX)91M*0^:A } |aQ.J 5G!?2Hٱ Sf)Uen g/T.Ԟh pDq&oR|MN˖a?-E!e`y&% ?9bk|i.lO0dN3o%:Bz#E az3L-Ut%ժ0*FsY3ٺޣ[6jwZ0)[ q}4^غ͸Xjݮu`OA&ԛc}Tf9V],J6|+!z0G9;dg m`ăK(~=c&H8'i=z䦱>rT2G` ͤ&K;$8N.՘՞qz'2 _2 uc/YxY̧Tdl9#OUMu? XABY.cZwBڦH '+5;X*Lg)e%Cu6qb~)kh>Bd *IL(GETzbft@$7yĻ<$!œ7fsCV9л(AO nDz :j `b㸶ok(YWw:fWX y u.LH@@xrZu_>V@5Y[Vϟ}?opaFfx`۸+1Osʩ%sOrAUqTh)E`oJ6u^(qx R[W>ܸIЉ.]MEmߕX*)_m/YZܪF!h}h2\^\ b_2u $ӑ!߰y!yK=U"lnr5f/o p(k.0<,`~>~0 HH/[i@1`ᄚÖ Aaf-n@L_R(^@XaѸd(ݶ m=[C'fo2w.\8[C }hzxNlO?*\k`.Y =CJNGaP4ɞ2T9kO$(4p W@>! Ek}#s!}U+gbw\>a0.0iaj2vxIȺ6veL NJ$Z0M=nvOꎢCp~-}! ߳!lHnV2#J\PIֽzC|+X5 Js6Dp qb oM KCysi.sgh9JLIVٔgrF>|y^uaaش, [ݸm__^2)Yaub~3,27=ʠY mZf0 ' +DQ S'9X_>q- FR޴^&0ͤFC} vR8!˰T'CW9`LBF39b\~ou{pV[VCy)ُNV|)() lf/dO`)b96.:E㎱2m/^u#l#jma-0P5(mLgӃ#h<<dDc=/5f)r=2`KK+aJYE@u|=/xb̫GA`w2BURW=52zkAuoUk#~RAd/3Zr`&T]ֈJnϜ Qjf,U1i+X@ %3DR0tYp j,v}h{EAԽ-[iKH&蔽 ÷S6DtLFQySЕAJ&Tn[c/[$)]y=R@MSt&7>YTQY*SNH!u:#=9# epjdk#F:}XSYE3Ww1ֽӿԱu|G, MdP*"6/d3ܹqUkpLRʞ}n9!{H%By;]?$ Nu4؛=NXʉ5KF?$z/ Kjhh.˧QA웴a&}8; Zx?bueR_Ret˔hA4jf|#00~Ό?,o2WXFSoݛvg*gz=]+kj鎊Wit#8A:C'kڨm%ם968W0F#WWHѭ Ad_27 .wR 1ԥojFKvxtO-pLvWTFٽMg[*ez |՛杔xbJ:XP@)_\2fVpaJЗƤ]Q# LχO- fM|k=*Z檙2œ^A0Jߍ=#蘧?V:U6M5Ö[K9x5/-*f? j_Do7Ymʜ7Mh~p(hv v`mB]B͇[9*D_9󙀯bziq[dχ'h_%?egfm5ˏEX`*Q[C8T3{Ea#W7>Ø#Q:4>^#{6 `;" D !VQIߝޫ5u(P²H^h_H)Tk|s8l-=\xs k6VкuL_VE,6m0w9JCj=,Zؤ$h&5WalezXRChۜk aҁ*Ȧ!U׽*5<WHLXNP{g]ah-jvIlg b;zpAs3W_T߭P5V6;s#5樻>WQ $D{M{:8 ϔ$ȏ1vZ4[xHc1ޛ%3XPV[j:|'</szQe +S#>e t6U%j7?0 Xc]+t_Ǵ'46AM5o9w=')'RԞظ[wʟK' Hn<&)v2@ ^E}xN A ?5ۅkDC+'=xh=&:; \k^W2ݣVCEF :Nܔ]u1-o)x@ڸޚv)m% w* V_ je9 >[ 㔕lo}4 Glw(b4164d"s {&vѥ%8)|ސAU kOfN.e疆*'Os#D K6i^6Pb(T/7N+eQnSֲġ֑URs&^>;U_ D(YTE󊨬M%YcEFF"dC2~ښ+ߺeGgɤ|.w.ZG]*<} 5RzܪMmYŒG*1~ku[_To"ݮ ({4n]0#ΊOu{+glu-?!VzDwLBc+XO`0(P'Nژ^w0dDCC;MTt\FZ[p݄}6볒vtDt<4?-r<,Cޟ*s<>u3ۀR e؂L#ybz_bM.443&b$·- FbIc0"֥0.I٬g7yБzʜ A+7Fm!CI]d(.Rl}'0}+zJD)r:HgGNE(##@̾b5ʬk 2UOq.Gl.yJ SD&Vd3;ZXJ~@́ { L卋] 6y>: rrŏ3i}HT%-YRR ,KnbQߤZ,H&tn~O$W27" pt5 )rpClhs8d:X' RA6'!A Z,gPCmX{FWmu6 ՛WehW)͆_*nEת4(2R 񪴝+ <ݬqǙ-0hlI 4IvւrD5˽ 909k,"j<$qr%湻J9.Af(veOݶu996U߆RU"WEv=5F˴zb@MO*48oGQ|\~dD=:67اV:7DQ:\{LP6Qu]f %H6[76a'q^3w;oQߥ8e(tyoWȒ^q7F:%8 4t?O=:qoTpI"MxgKh~L"p"򨫪H8w':mcikcS9L ƈy2Guo;s}Bip3X5~[%54KmbdMliD>*&ڍg|Tg6Xqd*"*$P9nX˨diM#8Ҧ3"'A'ibWVc^0/|O+!tђ`4\Lϣ[ȝ9*CF}&*Ɔč}RYT)䷋aڅ^i}opb Tϕ̾X[I{qQ}{ЦUş7 o]2iG'WbOhxյ;+NoPqRKNX:<8HxP/s}кTؑ&`GeGO`ta*Ar=wk. f5@o!Q{,Ҳ@6`-\fq2!i 3s,x0F%RXP5PXKΓx.3xDD/?ՎQ?n0Df4qc?:}ŅgѲvU]oByh !r+,.nǼp.X:T#@?hGBDVyOGJ`lӇ #^V׾X߉_]+5 6pyޛ} Bq㊾RnKxg'(pz"wIYIP뿈#Ӣv8JL>¤ s/\j]`>. Y]&X3rkz^|ot"Q*5T-.kp1W٣=_CH CڑԧU Y%f Yo9&8{ף}wdJ5 P5U\jeMaD8i[;׫PO-2Bhf^f<ZJd"mɘnNZ}Ɩ06ǜNykcZp ?ۥ^ -C˯RyjYf*BR:$kk)kzRM.֊(6y'*U3Q]!hL{dW!2lyuH"zk'{a~H{U˗g%/g0B4bY3߉|@:IbtHB(԰')I;ݜZ UYФ%HLNW#pV nl?RpKrQL~X^ \^;.4!KҪb%>UOԥ]zn^aXI&CF:OWoYshؖO8H'.TҐ-w!,m̹8g򉐵QgISj"3zHm`kQYvy0eeqþU ˾sY4=S=B &,`v|R',cVna'왾c_!$И;- 1.M;+y-L=r?J3`IJoaF- H&ĢWMl]Gc f;ҋ0.is HY~_zsJ""++yzX"%m-C W\|,p(x(o-@ng3H#k{A2~ӱ;y:8ƣ\JmY !ExŰ}rJ%,sCy@bhqȋ'$" 39@"j.[^8ܺ:tS DYn*s+ַ;VvvRnsr!#霂ն~{\H[''Y١,k^=#8OU)>83R_rsԨ򥱕?Z= Ulg$R$ 8=NmY|AtQ*~WFpˎn61oV-db?qwkNA t=_#|UƘ/`kJ=%ځ_Wv\HBM!E0~䷯ch|6b%cž1`@+׷p++]FpkU"LbjV%a OIŶM d. e)|nK."A<$P]U'Ap.h-= |Xu_8v+^iTI ?3L& Uxҩc'dqbPr$lZQJHFM~tJd58ͳ\ U2+/GїmF~].^'X9m m?eam6b1;x DJ1b *v&ʪ(\ D&D0,GDcS!0ɱ)ԋ 4VS2Nj߂»{]X6E_GۦR݄iF;9xKHNpr4]9mDF#S2 fF-KD-i&TUoOlV/Pt9t4L'^Q%d" bF MCmkʀw'3tQj{I/O|M.S:cRc w_madÊzOs;a@}.\7قܩtO2jĪZ?rŏoI"wjb:\%j&X3e4G)W?$FUvw-yv<.ܘssufgK7 QPsˌ40P! Bz[9(݇zjR,_ȳg  ,B%|_#+w9Y/rldh-ސ> ړ QxgRؼ+@=z{$NeuS}׽s{4f\"Ms(Dc~vQHF^`mok\]=XǨlV=tY!4Nz.W8quGMG6͖3>|{}cՙTV~xřGG:nZze}`vCg}a=6,zrngr1`E j6tl|}tgv>޴qqPC߃PNJK=m/59uAz(QWeB lH2& lSh³A4gX^Hh A|ӇLlż)Su]G(*#pTSLc\:VtŁ6>K3F^F3rJL9ev7Z7Aek#<4Ӑ&bV 3:K;ev5\KQ=. *,uS{s61 o4<8x} Ro9J(wcJhl|)";9+\k%\Lrڴ.kR8//fu PۦhE+|7p%Ěѩx膷++*,Y0U k֬ i\'XtK_ QpcYD^=(YP` OBICrI Ƹ}fQۊ떦?!6Qe+yo W=ƍGO!nA85f \58XAz&$##% .~`b80 GDKզ]$@5=:|a& 3ٙX_7'>fqZ)M/ Ü}>iR_,no76H3p g^YVsvG3BM5bwO{ynB/'8_N^TTˍI%FY-c\QiڷpN*F`Hp>BCIMoc:",'CyzxEM7՘pq}FCiB]N\Ww6웸Tsg(B]-+q2ϐ]EʱEԗ?5"g٦1xmJh縵ܧK).{r &` 2wGބTE+y ʑy{aK%p| 3{ 7_.̧#RfO3 {G\PoԺʂ ZJ?QmG+ՆBRJԳ[i$ y&:P-+4A.=n}fTLc֩d6cK.[hp# cɖ_7>ٖ_ `$Nt%m%-SH kl1fid%SWt(*s++/*a[y5TOXE^w#iE-.bC$Է@t4Ӷr MPK1!$3`6W"20cٓ{cXUЀp<|"< `֝²A 1N JrUM=1ZI.g|Rlih B"ۚUI "7;Cl.f̸(d??hIER]2AiP9bB tPgԀcց"ݗ XP,HS(=…mda !D&D0Y鿝aÄὮơ, %甛O!Bk#EepiD]zwpq/&)3=TranAо"qb -,Y@6/оK% 1:2a0eLy9GMGGty׍V<8}n J[4([ɬ!㣲ƪꚄ:,L}:w4 ~"X%ꘐtފʄK:d[.&lUw{){+#iK_c{MGD-]_^2~nr.ڸ A 6DZi,C yD,JOU9q}҂j̈e{^7+f Q E, r"ΧU*T{v0%VnoOc<>#xּ[-&/ŧjj fKyq*ƽx&#m*vahUT9أjK 5u?+B痀罍 4-@iWڻ'nMBٕ . b3Qr|a)섢 f˦bnp Ti!tlR 4Z6ځR s oN_G7d>{Goc{(SXԂI>d^߭V*NjyCz+{ofz74l~2a H@oE2; *.E(!6fb)?W{$]NuMircKBb[Evm|)*#xBՌkǎV0WڷK;z;gVs߃'hfozum~ΫؙG˙ /?GL(.kmJm+XX}qAu,ameIb֡{.c3m1co\Q0]ֈ~ nu;8Dr} iFru wT}kk%uIhM?+IT+RH-DF,5e*83sIoN|%/[e٣k˼u:cߴ<Lo;3^lc:Gy#D;!o=r5~'>$ܢVyDT=Avjcs7R 0Щn`UV?rܦ1RTX!RWx"=6X][1Jmm.E\f̾OE\c9~ې05}* 9#YBW9f}m"uWDDWK|ȒgdNaL&K]@ohf í,sl'i ]C]~Q"~\9@Pb F>y2NEg4eO:/Dh:T#G X>N"|m #pE,/X4pI}9Mz&}t * PlHTD՚#?H5y_0pRAVTh-l8Ě?h!.ČPۺsO{ tT-J2'#RҗaB*,oD£X2s8{~6T6($Y+4~r$!nWIDjAE-g݂ޅ ]5v&n{\ !Vfk% M~cvaIj̟jNVK)X1su 8HJ {'3Xeꯐo: znxL0r'gV fN8r6&"&ɋۡ](!ι+ Ku4 ߠ % rf˲4l_@/׮}9-IfYڧB[I( 2Ih`!"~AXcǛ6VșRQ2Sz 焎業 rD.+cvDsǻ ұRr@Tg BA]~]p%}kLa^̤oym m7zr%̈1if#b vΩI ^5/CXp)fMh8H yn,ZAj"1T?X k-~JA)toIhd*ށ^uYNFu< +p>ɟМ92 tn wM+n2 9V@Hl5)N6;7cdQ4UNtIg`H9԰bNꨃ,I{]5߱!Fv`bK.7eTIj!N[URr] w~xhMu=(̂Ltۡ?NVmI@pT###@m]ɑYقI?.("ИkoO턏cdj^& f_ ϿrUY"R.tqҟzx- ~PmJ{{=NLgi)>D^hD%!LM޲gpd}JD1 vfE]MmpnP{|݆՛*h{d2]wa9JxgޛIH̑I? 2]5 riv͘Q f$92*Dg_pcQԓH7IurxO&* "X-ZhdXSAŽX!8Vz0=gd2Y#0TvpQ "# '4T#;SDJRᯯ edϸ#ap/'d~'@h0%Ү 1)j=! Qmdlw(P96 hNcgѡR^R]{` !9XErg1W~ц;!z΅1%=}՛)ٕ;iy|{"D04,\C8rg"Vk9Y(81Y ]QT>8>#{ *:x79 ՛Pf|`|rp qtH6K\x$A`bgD;vcx~w59v[h]'0`M>f~a#Y e$Aܧr1Iy۲6ht$X-RQ*?_-X#D(}]#}xc2DPPaN.:_dGv.v삐Ňg@n}O:]fU= IaȹMs"6G1YF΅ yKL-E.}GfThXGO;eۨLY ,zpWJD8E쨬$^j<{/gz̈X4) r^ P=DLHac?ӹ0}}:w7TLkf>[Sby3pVY1M X?Ӥ t!70yL_1s d NX>/ҜL$B*r|W=EKP|4sMc!.zHi_*^YAxܙuO&R8UbZLˈ50p=0qѻ7~1A5# hvtPMo\YvԎՄ~Ta;R ΣbÞOr#Rw2? rk@ltGEcq/td{&hhj~bwy[+-8ɟ^ѧn,s|uEzQt c3C'WTRB߃l8m._NiJ}M/r0R%oːAV<楯78+iP?0Ս,2OQ9 e w_W <x5$65dD'iֿ|ftQ|P xQDNmfB+jnCW ;V& IMX{D)% eއhxQ_*O'֘T ըĸ%sn)xqoT,rQgqūrpq^#k" wb\jxN(F@Tt)i'%r  윓_\awqfM(FAP'͖`sGX2 L̢I EuM! FQluJFS9j-O!U쬁' cNk17г\ SHJc)VwQN_QWxbHF.;B6ɽ<H#`GYbl*> /W5~k[ض;τYGl*-趥 )+ydLr㕳?P7]/P=M˱y5uxCˈh!Lq$m[#;+UJg!4n&Tà.!`}bo1sb͗ȡaG5|s(rl*Lj ;7+pP8*z$7vI c~&˫?8!9>Mlx ŀXS(7q O ^ V!FʌxW9A?@ |{ fXX(upQq*c1zF`H+3H$`Ӌ?q3+#D:AKZs%9&<yɶJmavj]O\Tbs}Hdh`H%>W`r䚶pOˌ)Ocd·!~`@ %c BfzsIϯt˺ ׍6rH] ʠ#골њQbAٴCYʓS1YmWg&ß6[lvRӨ%Ǿ8kո7:{Ex:Sdt!o`WbF#}Y\ s~Q+u%@|YեZ]MWLcpo$@ē[.j1R#K]vUȶiƓdת+ݓR51#a/B0l\szPQ@[-' `]1&!`yDhX:^pVg;[Y(nbLg =IEQ/B(O~[\u aS[4X?ATB}rW(7:SLd@̜saQjmk|d#6W}Uds,2m=:i]+N/$EPpNWA# q7P[XU]${'>fR`R u8&aRx騐LH ^^WTL/hA)=]; lDAN&1Zb*y%7* lĐKS:MBA쎏J cKܥxQw'R&d ۴m:vRu{.=]J^e8zc0x %z%qq0y5q%yB' USJ\'Foi ּٞͷ/.0{yLQYZ_D Mhg_4!PqLY܅׿}Jz}m0l/ q %wARz`SIN_*W*Fo[y#mb U A Ѭ.LKyJ<};NRs&ngH뽵tZToLl'}e;u4,jq7v2Q!OHvIwѱ %s}l=:x9h|[g 쓀7u{AQSN:Aꗀ$iu&FD EO(+9me\1uGܡ'z`JKlrp&p b(X$ ^eWU%J٦ p| *~1NmRPUPB?V~0iS?ʬJ(?K#:z'auMw|GwJ ^`>٠yш n. Vez&gF6j.xxur!eJ3[l^: * 5}rsh'nÕy\h |bOxzWx;|Esy"#"RaHycg0i sba3xrfslk )i#2c5'?Lu[</Q u=4pga|O2F텁&mF*ҽrvpDzdX*>bՔ,r (U3n(k>㑎Y[@z܏f۝^i!t[ BQ|j Qb]}LGK'J$톸F/Gli80ten4;=߄lщ_,1vm8&K+GS1"i2j';&=ycvWOV>6t*%,,=/~fy&bIB-zQf |d91u(yo+$x7%oJjl&$3y&s͢o,~(:}Ԥ"-Yo3I,+aQpZ/< {d/A)nxѠr 3;S0YMDudWy\r;R%k)Oa4%%3nssRuWnQU#fǡ*1 Z'dUac%C->e1΀eQ3VF͑x :ndP:NH.z4@mo4Z$"B ʨ+l"S>+"4ךc>2zfNU(_蘣_ mH@w$^%:sCfGՑL8p}c'+MHM -ÞVLԗލ~h&~dh"E4w z'9jֱDx'nUK-<2__"e@lVajUqaU`Re9Nm{.'eAyއd2X9@y "3ۭfBqg7=zΟ"zT؟8 lQB?ieB+.{k˼To藔wBS'B;T IL= |E$F@^Ԛ4wO2X/oLZchDw8` 8 8>oˣFH:.xY1gՁ8_~tO<\QD=z| *}㩓g ]U>嬖^A83P1 =c\SI#i,ƽpu}Q-e(TƵ.BGnx8w-Hh4tzz:mW~s0?V~F˷#z#f:;}|Hӈ@qMaDwC5MB@ه::?q8K{ OI*ڨ~Q Xd ) C3W2.x`(QXEP]W~ lY{Qs&Md..s .ge$dkg>55/ E HͷrMq6U_!I 8ca$ۆs$F+=AVk ":X(.n\=9 :w{.5*KP=r݁Ѳ?r1/[RY4Ø_~ڐ'%[)bduj bo&lWBSWqyn•v[p 2jsKHE)zϐU=!c?< jLFꂐ(ٌ@YgfwȃZbTHH},`ۜz$51l(L21RD4lH,H)(laai}dl)4x\:܆5 $)e.X"BِQnJ󾖀g $iJѳ^RXbz_nAu?*.:ݏZ椝Gq}}HZ@ NZno>&ͰI#B>|#!JM]l;*^!r*Z@\ow#}AS"6h6֜m3GeT KN _U mM!rOQ|u7="t_DYowuMEw8qΧ0aV7!tovpÕZ{Zۻ*Ln&xϰL I6J*yHٷɎZ`تv̰y8. ˭DbrڃSۨ%1#&Oyw+rXT&wgcm'pؚʃ*gFFvرzbҒ}*!ueV$0$MǻDΨX|tw%T?CX% *ù$yT BhJ˄/+XN C>V9G[,2.7Q56 d26 ޖ׀ kAv珩i`#Cj JtL6&jXf(cQTG&V >ȑ_'DZRuϼɐk 3K[ӆq3ȟ0Uxƨt0Һʜ+~'*pQ ;+x+.kgP5hhD@UD-|./TeE|YXjc1_\^a'`/w 0>vp>KڎM(5լ@'4{k؉qv0f>,R27/p|ǯqWSG Tr5ڱf擇ed-aާv&ĪD*({8%Ȁ@JQ=ZgC~9!WO4j4~0B2Z)P5:(x$.@P|VByPGFH6hA #U<eM nà|A dr^;``BdZRm-!w-9{둮%ʉ@?A2FIg n(KL3Q>өj)#dE궿&+TP9`,+s%$Y+lu"m#Qc\Y{ՔB:Uw] YBձa*[!Fdi0<+~:jՈM!jp$?[(NS:dxn)'U -6N jtC}wq j8Sw\\!oP,>&k//OH@ WPG-OK4Kij|)Y_~UX5Αvkk-'&'Bhh:l%CN%< r]'ugrnD/Y!UnO19VC]RnEȇj}c{+9KB SIw.Kumn26(RE?0?ΎC* Fa,LJG[h:G}h؇G^Q$йD+h%f*_iP ӻ٤Vǀn xlT?)^7؞ :p%1gV&*2Vp疫̸ǐ gy<գwdz=2滫O}(R-ĸi孱^lGpJ))Q|>rzqͨPQiF+މxuPq ?SCٽ )a-?cO:MZ]6S44"{#3mNhOy^ I!m*?~=.>SBZ$X,K=PrA_~SPNpdRuIe9?v4+/Kan=0oQ9`TUn| ?AT;CMb ±J5M,h/kPX'PγgB. ,0yD);evNe4¬_y[~֕?B)@[Bw0^U歲&@Z.=+܌ධL4T<^Š}8sjom@.Lj7HKg{*[ "{Ph_W~yB_y*WԈTV.$ksdS;kΦ2L܄q*<scj'}ak b[#26src P-o-Yb=>^V1삮0Zvj},Ű"6Lt~]+ێ儔ӎb-A Q@Q;kGPZԙOֹ%W"lMWY5ɝ-Q}l9og1UBL!UHc?XEf܀ܚ(N\as+@ [Ԛ6drO}6lDjW |+–&I{U7( @֯!<ݫgeuau?Z0tq2HB_ͦST{FH=GzP?/2!V ʫL_YSq"Kf>W@px'1&#:`4P`S ˱&z9)J\Τ]˩wXqIBMN֢u֌q޵[aC:# 1쐔"f8! Am UHe вRHZ@=.6Zt=_!dd}n+ QdK۪ҺcID["N,W1JR TOr^Y[};(>Y>qǯPeƖJeZ ];LOlrڎ+ڰ_#{7lΫY+j8 =S4( ,hg qQMPfoO$L썝J;r >(# A*ۂ^}0 `˜j^'6$mU{}5YAu꜂& @`0 ?3>w]x웂-M;,4 y`3Țu嶖\X̶teg ۟L4B[쿶F72ܾa,>ƧA_N D+C/}z ;Vy1&U(h=[ah7z$<̐GIFaZa3꾻\ԝ6MݣQ,Y+ #!D+Ll.|S@M#@8'/Y8T%&so N%l ?Xl\D$(¦kYSw+Ç FC #6M2QHߺ^GyI;8瀑N0IEX׌-)RhN .,TxRkw76oYXe~dj "XYW-ig;~IG6}FB j1rWX|U^>$)w)P ^٫w傸.4nC+ǚT|44rXk\f!jnbz_*r2#>鱳dT `P͎3l`qS4 E| ^5}a4$h"Ƨg`Rw$k Xf P(‘ckCybJ>.}A }A=l?X:^Cfcoҧg]6qm~UI oHY!ܟ{kC85zPp#)(qT$f{eUk\y[q7+yISݡ4m/(f!d'OB |??ݿ ѱf|>&9XlhHE1ῂ#݋_vX=@d/i>Z+MogKpr 8p.1Ra ȅ $j=ChSq9UV:kR+pɽLaq3_-.Es9pk*ߝ DK*rPӸ &ʞݳXY8 83yi]% k\XC"8C=1j5|ÚQ |WTjP,_Mqn[7=.;#|k)ma]mV1#e[u"&qTfm=0:i,U+,0 Bl{+=`)ya@l7MۊTΈi2pc3upLn:m}5T?KN)oFv~zpPoaZn9J m&e9 ?8+@J%{1!܉H\¦ZGJ9 8? - !H!q_ q%HV&qc i"]`R7}W}.n 2Sr `߶i&DvN d+!ӿ )ȉ_\?ro*s\~=F_@Bc=5>=]AJ  Q̋snMzL+Zc1ک7## vI]ij a3)p| DJGJTr`*M_I(F4F!h6q'كVd& S:I3qUF}{&x;U@HF^h}Lx;#xe} s?FE:X&?yEOU8jbuHɣD뾰zBR'¼1Eyv w? ?akd4 \{90~`d/ ];0۽T,㞼]|]MqW%R8 ךX̅lO Xv AZCyaS66-;A/]7-^s`8gD8!D[B q=ZBbu$IåT@hp@pUL0ow}ٸ`?y_U l\G&"wcgRٙvKq0b&l{w(fmPrT !;1@` JoLN<لnych!V`wumKv|8:0gJ ;m)XN7dO2-L `$`ujw>aQ*V/Wܔ8H =]=~Dj8IjmzP*|w)h@z>t.녱oFjMșP#LXO.8L& /a h ۻ+G1_̏G26+] M-ބLFo"ɆP2tLU ѷU*=f#K+sC3MI݃{HqØ[ cn;GY} @~_NL\+Xb"l%+QkI\3T Q?"_7Yw~{m伌 Zw'+&jGJ,#={n^Y)$<<},VZmAm$?YBػJ$Ժk]g.ElkL|d4Ccf]_=&ar"mS>l4ߎBŏJe#q$|ܽ`j]jJt[,iT$րo}[Vњ<|8@ؗe HiL^<q8&WMUFim@*PxT9S;I63~G3oU,FU ƈ5㞹x)z3Q|>0l:z;d0`5?9!—;* ?[!_ ꫟$0C:2LzxEmR*yL>Z7wwӱ3JފN#SJX$mtx]!&1+ :sq]`_M(u.c 2xO߳0B!e";)ڵtda:a h#:*;6;y\(WmAZz@BjNl9-s;?BŸD| \' ^圻Oߵ3ht.5|^{j|fC fd^+߹9ёō,;[bxe Ln,щJSb?[,Wg5~#/d0LCH#bb~$4jO;ax6S =.awƲ'.AfP=`&c|&LP PDn_Uz)SڜDuryUegɔ0H~ynA•e1m䣢3 JbC~J 6XU'=J s P!-kɶ_1ٯNjc3fƤ"xh)DXFĩaبBԶM(*'27##k=RAL3l)3 &mRN ;s^BɓaCmͥGK5cv{.,Xfh*@2HPT|`yUFW@V'(Wh~ jܣM9D/C"EqȭƬ䫩hp믣5%r1/_V3ܔTzTߓf cSvvF5@gü"ЕiMbۢQwUM (?`3^^ uO2M`_("24d mUuzg,=ٰ9j"a>ϥ Rx~& ~ɖA$X^- P-$ԇnߢL ;|t巁nwA5*~LM[)m+jt/L&E nshɸ]Rc(קIQMA-k@XA"6d7R19f]҉K8+ҞXvv)-aeTWp8;l+WF0'L4UJ's?OyS鱻R֞ob Y+8JcV=F Uh w+^d|%+VqB }~;Yj `us>.ߛ4^;z9( L/1VdH`_LTe׿8f@paot8ypYb Hnq_i˯@=B0>;SY`!!(_?a@nt]T (69/$E#+ &REPQC9ĸ a3(W-;Q$`laߍ& \y{>EV['j8 v&=H;HudrJױV^@ҰiJDg_SK=WLCuܤBaYukr϶ *Uo!&6LMܚV]*9H93d儧1$;ě(w#Wށ) Vw[S ˰3?4(%c=m5>h`O|tvЩ "hISmk:JҤmCb,0ì>aUjU^ϩu!eU5@/+|\K$3 ~JG )iqsVh(OHnT;enH7d בS%=zQ+ю;" p.(Q2.WrcN"WT=?"cz ʍfH*m(/rk(TwaDpV4þ8yd˃Or5߁}RIHUY\X䔂B}zC']/BAJJ7HMu"Y$}Ek LLyB/Ra~6N(hE'ź"Chؐ' |:|2:2>N,i6~ۂ>ʀo?LcPN!4yb v#%)" N2; Z9)%b+[lYX闸jI4=Ēм,M-/0\5w%QԨqpyn?Հ#; pF쏓)#&Bn^r'']HGXDWBi O5QZt3w`#r^ 8VD$p)΁ܝ\/{jTimzx $23I&ɠhxYl W[!sG^bȄQvJ- iVX3S׹xt8ڠh.󇵢iMTCa! Gw_U>66:[ih֯](ƐןkR7$*i:=-u/q4T@ʇķc ޲!4c-I!R%SLd/%ZV}nuݵ^:8jJ zs!RW6lp`NE^s5YS:Z{,?aGG>k,(آ5p"/wߑrCScDZvPT%Y3 ѪbuTj(OK}mDu -ϭ_Mv1&jQyE9]A hʽv4$#t^秝-V>lĹL=HngțXaXDmG9R_8a+yv ujj%k%ˇT*CFxpaG,b|: k0͚$j.M52KU"};4lW""yQ<; 8/,Б mrInVph$fI4؜v1cuǟY V3?Bo❍,}P zD[TŤ`Z}mP>"Ě`v [I$uwz٧fESvl!Tu]{Z r~E,PVފ:3񼒷QLXJ8wK?,8tz1G8cXӼQܤp3`fF}|ZU]2)a1xA#[^p>O~t"!7NJt{DqNǪlvLB*ϸ]fxw |7UT4#=*će }ιr Vsu9ӹNJN$ ?7aIHTKF^ƚ}Rr` &uLێh>4XVHEaXy@͸]m Y y`_}ۤ1Α gm' _myNwɵ\v ]ldY>W<`& DP'VB RԴyJ (M_s uxRk488TR,&SEmo‚|sϭ D!K47S^|@6{"D@p^W+"XhEeDߜHOEhr[ոb"<1ZAF'6/(ԛP g`5-+![a8/2or?N5p2_~[򟺃 C4)7N͆F; ϊˏu2w:_f[sP+gn }D@B/ ډmOƺ0bhe/{ham`P $|_׎4$JrB'K3!b^~0>BiF\ waGBP"AR#5^/ #n^Z=&!N N$BB ;Wyl! R+rý\Oi*ޱu6K.% ^0F^ ﭬsѹ&uq[|W@iC1`|.N[ٌQ -I7Cmà~3h,Ot+ L!M7vPP}8~\ڂzFB',uSi<.//Q25wD>#1\P\=?2Vz٩_v4 {hJIsF˛ۘO/T/w( %l:P2Jy3hKq#K5@N .3d|W(4{5N~y ;@8ӾqiDb`:]1e2,A#9R9vi]vPo8MojÿM$׈.% @yꐕ;27[RK^B}gb7mϝ %),lp#zc_/OTxpB-Ҫ~Y# (Ԏ}c:U㪧dcSz &_H֥o&~NF˼:Y)z_XMu½$ A83ؚ|kZn W5zI-{_@5!oAYVa,zgVOT>&efmw0`U\NTM j 9K=KLKm+q0"" : X! F!`jy0F̰/M%h)O3~ྭ [;Q«5=1D7hdY 3OX\U:g v@V%"[ГaGAcoO.OBx#67Ub*4̹xWCNDƼTS\713IF6<:v.{ F8.S0x*n `>jK48 =BwR29_>m(#aY⫴S wgk窸T/hUNòO7$>(~Ɵ*>RW9%FE;ؽDl}4 [2:'AOI{׵qOH{pٔ 'sŷs~IOT`[GQ@##ҦU d6+"d:jn㴲]X>>C[=7LxabJC0hM)폭Ur8beh1&E}--\zNnz-=R8sfTJzJ(KS0Ip1##(Rs%do&Xش(W ȇH-x.PZ|lE97K7j QjuՂ$nd8@G47^C(tc& FU5WC=;՞ݴ1Q%8aPoP: 'U3FGI{r>/yu|w[7X "x~X])]M:kT|%+N)ܸGRwpy4,l'ۮI{*$([֩6YycT 5VwJ=)6ѰfA\ޭほa)aXfG lȽ>J5GxP O_YQÈjS{Dpv*%g)2#f _)R8" G.IgoSܣ0CpZ sUxZ-phdwg8j GXfHuU:E6hP_>E|eQi)3:g;o \]7 Ӟ3'$64e, adEy(*Ӎ('lMuxr[581TDs300ڹ3.V4 :S Z.- c Q赕k9c_ힹEt)Hh8r̡j’{} Bs,]ϦTU=S 䗠P~\1oIW/,AtI@%gLa

Qhbc͋}něj;h`l~ƈ`,@ \1,m~`8x#_7e# YL 2y~;-:B ݑb5X ЎCZ%ü;\6Xb.```TF"7ɲ>ඤ[G |2ȍhcQ'mgWHL,!s (D6b.ב`u⥌ ޯrD47!PSddB [\ߦSaK@XVwyߧ0wTx"+;A̼#l$}WQK= 쉜%lzI{[-"3tܝp?3Z_ HAQiЛ;Q)Pb%_\9#T/ 00%TE(sP%-jg+;sE]>BqYqnY ~c|AMdQxG2_iDAr~v49,!ˌp[|dFeimLuK_XU_H}]Fw"iYQP;E T(19һ~d脐in# Qg 4֖xU^˗r~^IN};lrVz"%{I!'_έ GWwK-cNPj|dU4hX UkХ"bzJ^>f'y֍²tGyB7H&H›qНC\Oq.ce\96]䋪ɛr7J {f' ٞ ?<[@J,}koū6sk@66OL>娝U8 MauqH{m2C5@R8"|RŗOBq GD!ψF@I;V@o׀j ʖu# Zѓ.j Y` KsYTNG"%b*2]Uʘ8 <$uܳ+ &Gid5q>%莖W?ͯht^# r E(Gi~\݆ -HR骞}_c JT6E?4qdC3[ 8:&^?\W!ݵ%F_F07iHZ$ dMn5sP|Tp[{a&({r% i6@܇>}]ǽ9 Uϲχ(rޔBdv`xj]p$Bi"\1z8MP!/GijG!Kg.DO','7ОL\a,Ѥ>Sc~/7bL/,Rkt*+u:]k̻$,Iވ[l;sd7* L`DAM1e>Ag/dYrČ6mKD-x0G8L*PgLFB/F'z莢گ"#@+2@D+b.5ECO –qɔjŒˠ?iKbk}h66}[j&LJ#hu"+ V(͈ubA gy?29 9_l5Ki7F2$MG%;Hg$xjOzB+|*;+s%Iw\>2m3G8=c,-LzmbDhHø2<U}μsI/۰ì^*= ]St (MP8Ц0$j}etFѓ/>U \o̻H$z^|iŠUȏ=~JM"wUhLkEfa;P۹nQj_fAng4||ʫ׉:9K{\c O~ M7kTa^c Ə%Fܶ{l~CǮ X^WuyjNaNxX]Waz 6vu qcTm`f\֊H{(t 3!횉Ȟ݊K̍C|"ߔ̧w@th]e `x0"朮j;_ݢnS7櫯_jިŸS{V5QM>>BӼ@Tcѓz-F`8?2m򚽄81kA%I6~&N)Z"5+aI2ɷ6yhsLTDt\TXJ-~G8w:^ylI'FB`qmu[7>^X.i6'JG3[[F|m& r5sZqn8g/׌Y00o%߼~2zʢIqWyı~U0O|e뗴@pcc>=>ZKPl~Q|ИuF]lڟ PiV^ᰬ~x{Ȫ y KT!ć>7^F*,o.*`K0UOo<ÿKN y-[R!2dNR$`1/eαMq|rXGP02/HB|,YUH!N}҅H+ynވ(w,FR?W̃#oM`~!y 1cS۰f!w.,ɚ=(HBC֞2إxJVډg8"R&(34na p|HyQyF@`)Psih2r (>ts)aVMeho.t[VapA8Ɇ_λ =_Ogr}2>|phSeeƼŔ8?څ$n侀 i2)mQyG?] _-6u пB|yq{aӕ jAB/  }_/M2 И> k;>__@\|_B=6xxM#sSa5>(I;>@͙: 0oe 0$R n| @amS3j4?Uesg*B?5~v:, SՁC!)>d:rW)Z5q鍂rZ̫Gw#!$4*'lPw7p?i⏥hw$z!ɬ;XW$N`mNf f{Qݮu'~}Ǻ{.E_]ÆO6hI]Hv.~(3 b͠!(郴juTѣr#]|Xivʛ=.+S=)sOqd u]ul~GS Ga=n9=%MowydJَAq4NjC!6-vWq 2!92X nC 0'_A:F#WC}ke)bR:$(vxxb<^fT뤾1Y7zP׃W-JOa='߭[(|Y{a'@LlG=6ma3kI)_7Rv_n0@&dow ]3w.nyE`W)LEe\E/ʳ `@m( i!tC{Ȏ&YED~N'ֻ,XA)A|g5Tֵ'ii#MZu"I3DLnͺɰx)t<&v^Rb9Kgn .FkQ:|N1CjJYm<_۰v}rPC[4opX;KשNv&3]36B]Pp|Z` QB  Edk,)xBxBvQ[ 蝠3 {ULvB͘dNg{X=PEd*LUAH[_J+h8'7@VB}\K^@j(H0g34n\&08 ,Fs>-G̎(H?^q8&}\ tFL couMqv?//by&ʇٮh}KDbB>rwzPt5 P5/M@k]Bɱ^@IQ:ez.Q{\e5^4!cyol4'|Hb't.Oȍp9g_՝mGocŻ v8VYeg\BdyOgA^TMg (| Ujf zџpB1g_Jhp(he]oR{ѣ>N)?*qa,5F@ HۡX嚇-_ P - r jns/Q04.0+YoseAvv-! a"3 =:GQR*GG Ya:֖ ހƛ/6*geIí/0l%ZO %9 XO'Qe_ΥT,]ǏI]{,f'$tRf(9r"PXkmETch)@{:dtM =E K0)\3'/+0~ov`:-22*FFrmCa%"-䘙wE-Ѣ^ۑA3V`WKG9xQME-Jc,[2݊/!gsh+o槕}*>sdyJo2v >rŷk2M6z\/|*-W3]'HSއX[}_?|Ӯұ',蚁!lWଲW=ղ">"yQ$<[jaIvhm|CInj0G} 45A5[*??jB,)cOl݊wt$] $hǛ(=} 1$j1tc{T6WJ|#0(#3  @^/rUza0Y9 NRPXB5r[ϼCF׮Ȥ(CJ=Pra1|TnU%7#@ 7مcwGQ⒈1_ 1su 8 Jq1,6]bL+ڽv̀QgLFQ&\WGMg`;frZ5?U{kQ8QfMa!)RYۺbZc{r7 Tp ;%p9A.ًn}? [WECND7Uw(b;| -j ӷ ͳLd +#7o.w, gsiD\I)c_E@)xYW 3hД귰Pü }"hxM0bLн4 Z||SH>]e  LA(>bSp}'FA͜9|t658ŪO<;(P9.ڳw$`4)x_TN1Ff?<~O^?A$*ͦT9Vn<։M.7t7/ 0})u?Iӫelm׵~h,Zu:`3'V3瀼qNѸٿ̯ڏ){o±R5@7Gre9$ wDK )=Zr.]G-9Dw<0)wwd9B1M <Ξ ,ǖI϶:@IUƨ]zTN)g Te֪^snwy:0`A·'^Z VsJ^zȓZNz̆Qy7]uj߳-Sۃ+W0>ת)SA/ӉNU$TbO!ܢD6*)# e'l8'f|5U` + ځB}΍G@Tb-$m7unkZ%Ե[gEwvYv"+~w$a?*]BO#v/US kwߦڨP&egߜrYAHl;܀n. 8W2}ZwbL8;SW6r̰CP7*qմқ=pš6tgҷBX, E7:1Gr}U"S_Y}\-FB YLwzARa(AǛ# \:ܨzZ]t7UA*0eR;*1CּEȢ?1S A@MF=E#G/ws?ff4Kpyi 9-Y⏴-!cm/xC1$":Z? .ac,BBOHC4W/G2i~= Jy%wN+Сj\4 :.|)&&Bsu,E'+)rl6- Z!,ܧ Fpii;ē18e1h[8#q*Cmf<^z;HRpgI j?BNm_w9ѝST]@Lkb?w@ԯoZĭ/}l/=s|H<t'I8۪|):'ЮH=.kU$ ]p TZ*;L\t ire.|޵dV%ҹ>|z0e^ȩYG+y7ID[c2}T{1Yzk8r..˦oaZ:Lc"n^ڠ%:j":3x;AJk^d ZfiQzclG;H][^J'Oi 5H}třFg-9p '½,+p76NgWͨc~7e<&('!$ &^h]`Dfpd=`1ZQ4 {$Ɩ pdS?_ȈvSzⰬM1T2~keFcu%dv髓׺L *ɹECg.B~!JOIq H߀NQMK龜MZ_  .QY u:285/dn%o_&;)M(='7y%sgZ~m5D#[tK^F%FDH,))l6=֒{P<]F0,y n S,mѿzPwBΥuqZ]~@*ͭk $:- A8X:p%K~Ks]NKs!G<J]\?ӽ(fLv?ZG틿Ȑ13^ ߿ϞJzݶM7# UF7U^Vt&o⮗د./vhӅ0!(Pe+&2l38#ե~I8*ⱜGC|iYKȴ&m-23r[b"ϙ*:=]?p5P[2E(zQ1mG#଀ǃ|5fSq5NmQ@H7]G/֥>GMϘI̊nm>e@y= gjo/DS%i$h$]ܢvI4oU,)\v7-ճ`Ab^"N~55fzd^GB\FyxB,V &܎amdފl=sX^=Yx"(.Qvi$wletr<21,<@⣜]*Mz}8G$0G dNC/놹6E1"שput ڭ⏳5wtb RhqT_^eh gn7QjݦQbJ|9l6]D7Rc]2s`]5fK/NndUd{xlHdS!;*+H!x__aV2?J2无>*n0=?s^!xȘai98Ԁ--?ǮW,So+@n&Ysh H;e0B+$qO-N0:]K2bV=񮡒 ۏ 4`eCDr]d@:|_fހڇu YIHnB tx4Y:ݛh}mVPnv"˱#W'wn4ֿ4T5B/G,1Qmz(#RX*($(Rk&|X/fx~ZfTK]( zJ4Ѫ4"uBn]•ּ^L'\4l'A]4@K?0wBCyQꮤ7C&[ !G(hd4uؕou1p8mRܰΦˆþŲ{~GOV10Si w0ɧ9H9Ec9-rcO 2IQ]a >&4l@nÙn>H#:-觀yvuG%I'_ H,,+;V;xSO+Ig=~ raU!T/?WmU!hܒ}a_ 'qʍolDjV|p<%-0j',?C8FTsDYTU(x04f'!9U"h\{PG}Hv6oOC[?3{CZmB@{!.ܰqՋ!a Dv"K:,&?z[NUkF%=!=Ռ=W$͎M`Rdby*sx b6f/`*#3}5VD;7bBYhGH>Q#`:iϢ|/ú@(jȽެ%ZV@I6`R=Gגpqe\\DZ=l"5gfM ':9Mpo2}$(n=27ؑQǴ+cL!լAb?鬹wK`cz]=HZ-I1GvN[#!AkɍIx%,QzI _!ۡP,L<ƆNttU3a9i/f|>o f>1l15_RV.] nVty F<@"+D k>8[!: 8é/̎ v>A}&9 K=(8n oď7:s ز:̆kDw; 0w^&fD*ߟy>+5[1 ̀ C^FX>()oIshRIAZk|nϺZ?V!ʋaK&NtԣKo35zJJ5V ~jbjߩ&LK2W&#f5 OPZدPca5Q)CK$ >3o|<b:)EOqXy`40X3Nb=ܷxt$Mȅ0{po/N^٘f.ֽW !4kŻqCzI*tdP3e<5mdצ+!GwiH>Ɣ e\rCà澺Fiw3Yars H')Yq:úC2eSni.L'jYb|1_$k}˔pu-ol\nqޫeXZ}wIr|s~ؑ҂ք$?4} *XmjxE4V㛥s[Z 6fq zK w;vF;6(8yl>y0cndEхj*f;l!)2x\BEH4Cf^+Ȓ{WA=ؾ*~e6w7z00=FoFArHiآ*,r=dgtܹs0;`N hIMȾzpJe'q U a[&SaOmgp\u:86b h=u8>G¬;&(:Jܴs wA?kcw!ICG"[H еL6nz&6KQTNZq=w]kX&'KWRgE=CUYN1EO`EB(_tv$wRn`B#^闵>Mx:"֚="ʕޙ[uaWj>n =~n}εkc#OMYy}@W.JmNIQ4ˢv)~&jVn'9s~n8% _6|:.VMr t[I/d{cܡA¿uyP|gb״|w`o fWFg2D;*[{"k: c$K4?!kz v?I:,,Ob"A}"@:Bm)%ۥgr-RngXEe #fpB[RFhN#rps34v9=`fr oҕPܢ$CI {J4w F1=B?"з1F^OЇ\cS#:k~Ȏ^ 8-(TFsyg6 oLG"4P֪?%l4$\ZXSǬR-),Ȝ:v3br`8A:JlY @KވȖƨ׸_}|RL1\?)3v_sK[ or%T>[aebY_-ñ0L^% $;Ij曄4*5AWɇX`\4%>Q!p])X?Uq,1#ȬC\-k}09gW 8Kb\Tᮚc@˘ٌ?lA "12(wmˍ>xˍT9JjOKar8R@2Jڪ_=y0ņS/+.P츄|bS[)] ak9τ>62Q=_O)N$ UO=}"B=z'dD4(n-UcrjZ3,UwP<)jq]LL3~j^T\~m*vhm48I-rСW$0;D㏢Wgk;]Bĵ;Ȟ2qdpٟ9U薘g`D0O0SpXQV>{tUB.%Dh݊V`5cSd:2m5Mi>]._A4v7Vdr=튲;GQb[W\D#Che 8Ak]@|8;k3*C@ETr$e:|_<'|cI!X*i0+㈜9f]eG%e'$,CW}a# %hXda <4ӤM.8ɗVbA14諵,rI|C05/Q jHSx`˄`ޘ g>Jl W\\哠w t=R /QO.,z֘Mķ K0)p% -4:5,_Xs8X'ob-NXzbe;GI4 F?)2u <OZfWv6AƈPgXA2U?@=0%JS%h;jL9ѿ rk`808jkNȇxW ̷@89; j[a}j|mOp0j_W|-n;$u.Ooa vOoT@t31WS[Y ˥\NaiI{ݵH7݇fQ E-y֤̦}G`C>:gi˝¡r!gp>Vlڴx2+@Rn'LVx(SwOyn'l|sʧ%'#?ǜ9O#@2UBupT;!~ﴮgyNf`&isq bf3qOd/\Dip,ɔ*hNiEY/3B}OZxXezYO+D"kN=Nw]]9Ń&W nIθ.Ξps&֋u]ؘ# ֮`<px Ieejֳh99 j%m?0|`ssZ/`3H7.'yA16$pB֙a=N)\p1g$NҤ9\Uc!Y rpz6$nu:Nwy0q^L[z/c˱(44 '"4 dg7ZdiP`o*B\kgzW~81*v̀M#|=pX ;AJ[O"m!5m'7ҶEÙ1I.K[D`eNFxQ9Yʄ' J-@S&C&W/z53z$eRB~Jæ947"rrڼB< >8&%/0YJzHAv7$ϋ='DBB̝n>monTԬ>RM6*e74J Jա e˨7d7N*;17UND_mIu0- ((gmp:ԭ|Ƣ] Ža#S,<]4gOϕ޿= Y׋/2*Y>x,lك= (Y;oN|~@z%cHY 7[7-&~ŝkR /;l: 4+~%8 ~u$56#P'bN9Qƈ"]1)8=AXZ#2Z5Ľxald[Η&{YY[n0mLթ͕7q"M23_[(8>#;WZ!1 ߺ tv -$3ǧ,{Wcu]w@K^7imʂNe{kߖke0֙k%Ķ5ڎyoo鼈{އ 8CBI;;rZ`G 5kV1Bɉ(YubfJq[u߻(膿׸z, /{A5dͬCWÇ:nHy>ɛhW"DRh}ɡ^'4wHi}6_ٟb:zyA ӵD,tD͌u_ԇOH z*pFIU1T&KǰrqzW W|ŧDT;d0K_=PyL_=F2u)Ѧ<2M ShRv"-ш,JleH>r u8ȧ"b4JAqWPc6pCG!GsbL)K!"^KKɼE-cHX@WMƪ(oEG9OR\讅B<3LcLZ>N!HiH.Yy]FHgT^ V qҫuEp} ?U Z\(M*;塘)֣][J Iڋ'TcvM_sfPgƠ%[c7X_*PzNvgjykrg]e^ b{dml9& I/$?s'ܿ&uap1uF/EfŒERSJ*!SUY  c=Qo \YJ&$yQ*~(d{{p G:4N"dC>Ǻ9Cq'k{W3'z ~j6cQHQ;ͰLy].LΓ$t~`|QtAw%)mQ?h%m&BV4_)K;r+C^1wŝ60#ahF:mpLDt^S|-<$ܷ jw='Fu+˥o]j%wڀv'KKg,KuI5i+nVg7)ڌJk7"p ؽA"P.8?%h}URUXm@fL9c0sqI@z>r=,e"R.33Kf7uqQg.y:*uE=ؒB܀d:N]dU W=\p~߰y@9J8ZkS'Rٻ,(N5vDOr{x \h-?ʈ&7Q!w&\aៃ.aP;x}yF'%f쥛rmIA lg#2{f +F[{OT CGQ1ErTJXe"'Wk2%JЍp`]ԛQiJRʅ(7]bR_]ZfD$P~?IZkPI8 . y6_֭ůPv1FCW7a-T8ZAY_* BZІ$8`'9 >HL6[ jS[ChBU:/虃+.yba}+Gk A]74~IC|mҾ'[+T 8K,/y]IJq8Ҵ|`95P.Xi(\!b Ir$7u c {dn7&M6Xr?iQ1->?/FB}yoהfl4*sN_ ETأu`1Q9֬%Ftz`hYqZnWZ~FoRNٹE\wug(.R"ږԽ{(r׆=i>3"6jUmL31GqZQ {<ݦހl˝X߯5;7Dd\jƺB9;T^Nq K E=|t\5A6s ܱ?wY෽p_"r*z]LV&PoFC<|JEQK['(B7 ¹䭆 J#/H dhҧ<oW[)1Jh@U<:_wY6'iZNXT64oSNZYD8GglJl^>\1, &ǻ&gN5g[6ffXnFDMXT`7Х)T\[,e[*۞9jm_<'6lA?V5U4q)eC^g8<)cA`+ku|,VM<x jYX4cMmV9l/23i ܨf$_ zEd ځYjVV D=x[|Bdx$bΊ@ҨI߱.FxXs~ЦR9BtVE(u[)GR ykѓg3s! UCG~+9m8$nA8 7T#s  WdKFos!?O3Џ5T3Jj5gw#o55 e9Z$"j۸U)֧HS1 -%)0t+0`Bd#v)S5܆Cp ['DQuG YG8T+evylA(+5i]i.Jɻ n3fNlu\ .ki1@=7:(2~dμ1;})x+PHB57ծshqx׶R (ߚ蜗@7IR,WlPJՋ XEeEn]$ +^59e)Rn{~贗%o[!g]r$SYk{j <"S}H#:ٔž^cJ#n-tVozfq)>Iw`/9=+o j$gdTx*@hF.0劻@)N8xZW]:2u Rt}9i=w Zu(C#mRġ>'H$0qJ]?+LyB ۹ MTK=׾<Vj׽ $0C2Lu>ruLچ7 iT _j*A? 'v2_iU%~Fġsy! MxvUbRU@ }f(N^u5 ;;kozKz`r<eW_Ɇ, T5`+lUͅ_q`)9)|$51G1~UU bp8$eDқ8h^%"]K3+!P=G9"9)]MSnp]Ylbhʎ=# Ɲ3\*U*C m!? 1'P WG*QNX ,y{N3C |†Vy5XU$]uǪx ǶYdex"N#J Z٩c. D`:]@-挝$e3;i xviO=#hCtAݠQhBCln4bn\@G|՛Ы)l[LC^jAt'F}ߦ<)f\ ꄄQ=yIs0reT0XoߧFۈ!Bw7MBv1%+|V!UO6wN t`@5]GGXP[p̈́ XzN^vlŁn٩"](H?hue;z'X;RɁ*%A4`AcZ*eLgiְwE_Uv"561'JP|] *,ĕSk_YaF|_K gC4spZDfBaSzJ-@,<@Sg.'b$r.찧AQp*BY iCV);W( 'u $ȉ7y4z|gY=Rr.%#"U4#7!n3 aĀ,__*g:܅3g>櫇xd1ӹ Kj 'l֞fm+%ɮ ҔRP3okCI^fZ-'qOj;ob SxՑj` XNsi"uR=Lv=#t*V<.3` HC dy>,;cԟERA'ǀ{h d+@%)m\dx^.SM( ]*F43 '6nS+ ԡg4^/4ӛ NiusJgV8k.N)ƇXЂuKm߼̬i>cZՙZe㬴[W:^ِ0ǮHܶ2ChَD!'^6 }Q}6SMUI BWK?I/36L׳P'*#\2r4>GSrM"zSd#}l'B}+x{(9E: ;Ykw #}bUB%H`rq 6CLFo=x܂L'O.*,a* ɪ*Lggdz&c{:F+5o0<{5+3`I.4'o8*8BQE2wO49'&#f"(@*h]R)d2Onƒw?㤌ŇY ^x4L-iTƌ؍'EF0P̝/LL!*:>:&7yO̬͆U#UqNGRQm)er=ڎq%gEfD-LX᭭>G)N>mɝ9_s6aq7,XäDK%+j,ݙ@AN+PGmgo"〬1#wUqC,MyT/gy2&Ә®X`=ώmS̘` 'İ'KFG2eY89$ӏ+MMoIy(zӖw^xt-D6#LR 8Cz/;Czrݾ=Mxʤf7y4oI?I"$YMk~DtiT{W~[9^=e;+(`d5;CoCQU`t <` M[ZGBL2L)xqqX-:zIJ: P6< $g܎]bX5~ s9Cd?oTYus!"KiJ%˭ES_錛&=JԔ.j9wW?'ɨ@;|ۚ8KA13@g=6r,< Ğ^=]i3Rw-/L.SDZ{(HGBp!Ghy|Ko!R,7V1l<:̲uG$. z,޿ wL_OgɃTjÉ|Yt YE7ozz}gOr..?V˥:׏'2'G=xu]l!o[&E*wѽE+uخA"j@x fHhCe"faG !ZJ\(hfh4!2ݐ+>[,셖4hGjPӕ0:li%'6Uƚw@ut+UY(ݮF_~^cm=΂Ouu `9w 1{`gn6~Q/g<"Y !? ĽRst+t] ϝ!]!8Ad<$E<C@| !03fIJfq26hOe궪?ӞV=K<4 5K2(U~Wn0՚ʊ C )]"ȐA>Uv ccďPԯ5\mG리{rl<-Kہ,m/bkx<~$ <> #4vhJݔqEE2 ˅VBi#`+:p;Uq V44G13ͻENn3R\љ4C&vQXםR{)r/A|egx!BɃ3J Ԣrӗ\ cxN `RGPK.m{ E+ȳJ2Zf4i_Q2Q|EhT$"a%h J" 2\:&-&mb!x/DzJg [id9REf15:ڟLm&ypR>uOC#-fZ{9A-g,ǒ]C\wה>7ސVCiy>6ׇ"R#3V7 K_E̷DMiZ<)ƯY4ij>YrH|͈Cj\"==ҥ }\(VHR|&l;NǩGX*9X  ٨ړ=(S/#mUX`{&pٵyD*;w|L[unL&fNeS9t`bUvmKDW&?ܭۜ,x_J 9 ٪ ת@Љ1 ڽJ;wp z e -x\8bMwFJfCGVݷ(\\z8,Ϗ^- *fQb:]ww`cb)0% ÿ;+yL Z1+NA@Rܣ[jw"= ( s-xGT }6!|fd!s)ʔHz6M ]ďv/r䵯A}ICj !cYόVZ ȯn`1FkfT?gیsLIaɴnbo^LbWfڳ^\ m,Y%gyYukR+*pDY=WNqExs?mW:&H3uT"ɍ]=jjP/ )yc1]6}@0\z^8ahaL;ڻy a>!Kk>,ͧ/tmcc❬FR Zc-SuG+GQýmŗ_ LdIVWSa a#bj 9 K"zoۭHS6B:&@w#Xe$n6\!q*ө&1'Q!uO  1p+Xp#w+nC+Mkw\9>^m8J3X|aj@P11~vQ>1y]r@w)Ӿ#XVݫA | /iBlԉ)Ғ@ڢAnV¶&c\prw*\B0D1n8_ڑC0=O6r\lnS_עHKI>6Z8j3{һs˨ [ղ^T8j](CR+1VE0'jYLc_>]H:EUrga nb9ön-PTIgzFƋ]Ղr£)z~lyUrQ5d B#7?sf G[(T^k-fu4,ms̙JЍ`y˩@`_{\.q[S%wT8h&˦|j@p ayUPfh@< h8 xmZأs ~ӃLK n _ާ? )Wu,&SF?%g>m 8–WqM'9Jd.DHﭛyКf_Rg>SJv5Caez8*-SFBpBl <_Z4N~SjV~m=PbPyK}tIfSH{ X\LVWJ63j Ht_>P.؉ctWdj@ڭ#axQR;).RV~b*.+nA8yD4 юM:7 cX5I |5t|e(s'>kkg%.|2@*+3oD(3e];A 0/E7y,׺`UKaMmx)e ENcq6Θh)<sүEzy~-FÂlCWK͸7ݢ {*ǷK(c4Q۳+5tP(Q6DE}5]].Lf3ytC|-]֒sR&G֚2?vaAᕀXؠ2 pWHiYwQPļ256m(9LV/W9l7-[&Nd7l+uY`bBI>oѶ=7TC(`˓GeCT>97O1o; &]ɮ˶ ]30QZO$?*Abv粯tIzg8ttpuTi8Cѯ$`{=]ށxZe`5 hyP&3ßLp^>;tz-%*׾U=&Cg LU߰둮VKljTx>֩+p'lmۿ<2?+j-k{<敇quK/M:GzaY ڮ#_PAߍ:" D  }$A.փybD^%1H@NB`^h(m]aQfq ~k>N0/VGfEڣ|M7W9Gn+&^gAH~|Vx~}XhA />{2:? oD\Pڞ~E:`wé r9?89k-t ŖO%p(PR'מ֧OwDVyfՎ!6w«LBż' 0Ӽ^=ĮMVV,TTC>DwhɊ[N7֪7|4,0]e5@kM*CJ)."0ơ͢5*[| '潮CT\o BD 5ksнjc.j]i`ڔ_Edbp%#لޛbqpMD\ ?F]遛ӕqCw@";,Rn˔jOvnTA}; g[n` lW&/\>-E,EY˅$V3'HEf<3AfrmWjܰ^I,u{H>J\̎<%+şbo #"Yas #f1K4lgYxSlɸsQ ABgUz|d_iJ $WRZ:ɞ+LcCm%Ik˗IB0dYBI?RX PTBCU2TYTr.bO-۠[` ;밧z V_\QF:ԁA3`FU4]O8oB`qN]ZzJYǭny uWE2IN_5B{g wR̍R+5m~ĦZbYXkWU0Þc M0gJzv}rgfl}ݞ _ lކorIӕD]gZMrV)qiԁ~Ǧ[ZNՙp-T*qp|K}_u5F fխ~$&Vo> KNY#fƑP9b F=ww>FZ{(?~Q~ܛ[*uOx:qqEF ^R5ZrvQr~r[1܊#څňխӭjvH^>sl@fhnC .^_]Bz[a&.KL z86! 0t *wu27FH7wMx rzob:N~ 0󉬺H5sp$æZ2U蔗}uEdS};Y8י1fnvbjk5F<m~uq-LNoL8[gԤ"#+{)>Cٯ.}Wܳ`HBp 27lID5*/[=RBG.*; ӊoO&t{n!V{-|xR1NXNyDgL%`Vlѩq d16jPsZ)t-JP)>MA"]1CPJvt_x9Ou 79ZR=̭OAcٯSuJ앯sjj6st,LÛBpܽ5-ր>RW_1*3ҡ{vvj5~[Փr7*ߙ8f3*ti!vpÏLϾSBZ!9q/ aK13 up Qgtolb^]DS󇢬 Mu{k!27m3~r圖Rv0_2H^M*m*>5SMQTe OO+t t˰u#X32 N .{y<>۵pNqHTy]=b`+ OYWS>aX#~6W=wv@R| F\[Z]FpîrExR(JݨM۰ת;.1U*cyȩ(!)T JoP+Hm@xh`5<)U4*)ċ+pۙ]OdD ]>tY͞جyYWƮ@⡒䮽4Y>CP'@oԩd KǕ}R7Po0_ itjVwU5Jls'ceI=U~@v .[ĢDUCjk ,Gz4g* G`l&iNqi$Tb"p7s.2LXZ#u9(Ir\be?vMht/ o-O*OuaVz(U fdk雜DBf3p78PP)$ GSKe.y"57Y'b^!Z\i [ۼY??@1}mt8Թ*˿ XRCEX+8XwxK1sv0@p]O`8ZO29S܁ۦY)E^'Mz d GJ yj2FBfQ3]F5K X 퇳]̞@yv0g5KFOBY goUR 1_п RNYG*3H%YLGv.-B_:Eù]5Q_^9kHZlye5#,w/(r&,5`:c I .a3.l.xB (Əf fqz[+=pGHڟB@H%"%lW1IpY w# 'G%e%ruBV7À@¤hb%0G2~2f !ݥLݞXSԬ˨%Fȇ0WE'ws7m6Л+L^w*5S62}UUޞRFV@G˖`O ԄlF&XB(' $<."4WMI|u(Ƹvi9Kigv(teW<ԫ7Gg` LbԁhWnA׶w\ ,g;c92ԟAri܄6]ڒU0 wΧ|7O`<~=ܞv1Eoe ] pN zdw ~nu~vqWޘOL'@ w< 7&M'3%yL[L~ѭHFje$IY(t\TXNR:G)1-rⓀlQb i&}[ ɒC >霵_;SI.EP*V^Pn (K H;`(mMS~ V$c"!V7 0R9!ˊl?$t,v붃/IcnQ-hzfWE K‘[_<ܱNc)th_<%ӧ ᛷ]pI V,{G" d‰tS +hhbEBNNwyn8[z?`ˉBr]41;<*ְKV(vmBOeO0 v*uǺҙ/)3;S yKm=VʜYp2s4#cbw(xƩŔ+q઴p`[!CBi/Tvݡh&k2"I€owV7O&ZOp;j 3c Yz>C^ $.d)&E?BA<DBD=ge'`xD.`koL;SR7<`+tf@S|4U(i3¿*x%SJƾC 2A_q"tktDGcULo סq2Z9ڀsݸRGR8\=]3ݚ C)66gl{HO1Mu~+Vu|Е#8Os? gO7-FXoDVG+S} X} ZC%''KaGg3b´Ƶ)Ƶee#Ĉ FH_!%\[n7G4Ss4虶Bn=:Rgn(s3pkwF= N:qЙĕRT@dAG [^_/!;f M_?Sfdh.oDzxRlΎ,")'rAZO87 cdAB֑#$䥳^\j)msjõ3%grM7?8:?I3M6Lt8]+O{uyd:s 묘P)3͢8*Juf>j?Td Зx'rIk4D918ݬcl&dӈ0^RFXy78zfaB<㳜.6FG&;@:37^Z}שofRtQ&4&h${Z<37YĀڑ&uK|' Dv*eUxZDS+Yߔ6:;cf}w4Y'`|e>蓺FWư4BGy&@4pHN B=Hv=rshԥ3;[ +=_&~X]pLw=V òؒ8Z̫Z+ MMD ~[ rSKUM$tq0&9eLe-bF__YyN "s̾m6T\ +gwa$_V+SmX/8`η 5.Yq/)rvS-N94&V¾{!Pω 7d<1q|4 ng:OX=,uxRL+A'{V'NbIo 6_#drs \u*;r@z#*Xxi!kF5֓|Zݪ\%~Я]@$g A P EVou0x3}d\Bl)SvE'ҥ2g&%r^Q#hj.ۅNf c\V}`h )ͪ<[EE(Hj2g_*@l)_ Y# ZwFVܬ$yPr/, ~>\5 E%uU;~ o)q.=}y;'/)%iap=?~!UV˃=i9W#dJ)ʉ|iEaN|GVc)YԐi5ߡ-zWP K ?_Use8ߵZdʅJ!6Ϯ܅RhIRw 0W@\Ok@Lz_g}@Q@r? ҆Nw$; r3C]&`~̭V3%'-DY|mo7XKQ%BF-%i 2cBf-`rDĴnH٣yQG 'm!VJjZ'̄c8ό۟2<тYW>ktyƸj#[otnk:pyydqDRH }gh+j2x|sPu[gXn:;pY\#~owȴ9݄Ww'~OC8[!S|w}&̾ *Mr?h] Yi/ =i%3i6(nv+ "eRe)ukǿи$a 0?5IH'ʡF^NpNHSy]ز]9y܌nfBʮi%t/Gs-u\k,jנKEjK='IơƿOIT?EjM, rl;$cIc1]SV"oi˭ qtCє.CHLĴш.x%F=,M5*|.:HRGǶyk@g-l4[x3Q5ɵPh@nNu:p w[?pq%(EȀ=> S,l'<1v̓6G"MR&d8*}jځB"3 b/.LǃAej FҜFٛx Ґ@D@ʙ?%7k6ma90[snj➬nSV7n% i55 cQWxh:?%;?ܪK G XdU  (S5Zxꕬy7{wp5&+ǵXC0%TFS@sDdm}O R 3FQvb≝{(GSM{;IG>дWjf7KPӅ/5K͢\x1 }@S<3Z=z".bF]|t`\g[bɴBU+_&ג+S`S؆Xp͔NH'&!(J]a;s]WJ):C|#ճX+u-xrK\&8|Zㆊ*S6 dGj^eIlevC&o\h{D:Խ |vܲ_gm rc%&\$Fbx)gl+>ǾzC&2)0!B+]@! q? k!"vc*-9W/@0FHLSr ]1#z/&@2MF ?<ຊcFNXT~[ز>d> =t)C޺!g hێ2L&h̅n[j< (\ jij‰8ȘwQmWmX&8 cb*MAJRdMmm{ʀbP +爷/3W[< #8G[o?&%OHQ^-"i2:%XR`-Y|z(O#>Eb6Ꙕqȭt,P -c=cvL,L||O_J>Kp [pe FԲs#C}6w+r,y]VXtzK%@<,{е$J\C*:{zb֝3wd苻w .>D^s8zhp<9ZL+/!kLٺW7ME2>uH5m⌜7?; ;JEzA(m+ߨTAk6`M-\PP**{}xN`նUIݖI=!*f'+=LJ@[VUHlXk{6gxűMn@W:IO'[=U$Ő2qj8d P^)vT6S|8bƔVͯffJOᩢ6."m)E6s{?>\ Y1J8vs)20J/($`l9 ^2@ACXd/z``3-vlV~Mʒ&!}~g|w|f?aIe&!M8ާ=st,\M< Y!2Y}ٿpv]/s" АQ9VrN#b͇V+VK&uVF #1;!ynᛠ0kUP_Kchۇ?7栌F?#4.;yܵ3>_RraؓPۋ=TރǼׇ$eVPKMڈj3׋.R?Zpçjⴌ9\T%]xv=OmzD%]Ue fl,JRa R7]!la~ v{vJŔwwZ[//*D,M <_'E׸(TE,oVL.w2ATբT¦l yW»>@֝)6nj+ 6q܂^7nw_il &BM?ReJXJ;hK%n]5%9Ǩ`n;\ŠhtMZ6o i3]U/S!L^]+5-/7/i寖<],xTp$~<՜p,t3Q{Wz hnNA.JS@E~ʙU AK%>Y0jӜT`Nld^0$Y޾D}sV* [Ob@>bY=LCe>jt@^Du+i셙unA@uFI5x<)Z~.rOLu:CީlY> 1=w@ qu'fZ6X ezFHJ*2d=.)>Stq3DQ4( ktًw]šnȨϔP)e4Fbɺ/2a9 r@ʎD5ߙ}fB^=epK2͔#6 tiА* :ˬ}!r!q-o]߿KN&,N‰7PTnuUhOGѭ4[|kK Ix9$}a1ht$,bt)󔈅(6YOt%FU+ 9Ej=9j4DO:-c3xMxERڻ(ӤH:Ge_2 DԎ3"R'E ugR\]b.n„d"AGWTq,A cbqDvZ[EFxW3>(֓ #d >rD9~`V(H omD^7CKa]REf lusBh$^4 Lbxzn-puh#:v68=sx;@-7e> Y5NnOXW*s΅?Q(ХbPugA Y/n=#ӻmI};20СNZ7uU^r-XZ_7+YiL Ѱ$Dl €Q%wTo9lA~n>amwet7@ a 1m%˃=Thc&[ Mhm+0#uK$#4m=zKZҕ?3}"L|Ϝ#Mq;6>Z+ͭ=c)n宫 |<մ$U Y:$ıloLʱwY{#|BVǷ^W̙avpM}=IAt>^k{Y1㦐"LIΖ)aVK> &' )9Vբq1쥿m[ur6ZG"Sb%VT>zʇ+'8IZg\(/Bm(Q>?ئc.F1PyoUMա:aM C~~霄MRclµ Fd %HY:5Ӧ|Q;A}}YcG!y_u]:ejϦϮmw>a|ѻ'sBx6[{R6iI^&=uep^(v͹ jrjƍ ^Hh+)YLzE{0Wa͕-=MvelN Rk7syV[k T &fLGJAkLϿz.m䧝 0SZZa^P\pHP'> 6z moMO:_knrGsX HXW֛={WS^GgbD&}9..~++f<@گk cO4;j8<$v,]Q+-; 4vgEdsh%8LLmGXJ("Z|ZqK 44Ǜ=kC5Ev29?`/w=❕8 E 'YG=?R+i fj pF=S}vj&T#В78>[lʏis;gB[Ri%qQ;D|rKd3+bU}hEjw&Cl T(PCĕGғ0R)DZeyir-$IG?pŢe-"jztogyLAd>eQcXUTt^+h_' ^{#ѣWm=nJYlD_ ,q 8)ln"3X 䪳vܚ7yޯ^. f  4rZENÁ{94Pʬ6rcKkhB~]EB9RL*?F;mh0Z#Ϋc: (yv>歫[G)Be peJ *Rs"7H3r8/ORVsAAFN\Nz31VH@IOyrO>c]GRU[ 3D̫Ԉ3ڮw}P3`SΘo(G!Ѓ? swHs}d;IG/0o!=œ,Dc&4UoCŃr%bymQ͵Y}IF#=r6kD),~@'>>$y unΦ9YbxS'Q^(ݖ1 e=`!4k{xBReJ`NQ#ٙU^BV)LV*H~ |qH.!u01޲d<20nshbT{2:g4ߤ[!~9 иi)W]Җ)ʛ=4(%#th<۩;]*Yv^=!B*kH"ۛN=ݡzH蛒% X^$d| =>fpS)djKOr ꈮ;P%jiGaxEcB l!1 alc)+]UߣFݼohDQ1HaANxBc<Qh5c@&AucrW oWhDy%2vzw%mޮQwAl*y㹨xS9-O41Xjee1mrBi}ÞS ;7^E{dkj^L ¥ސ7j)?tjY nkqYۢOMtH7mx0(38D܉^$ fB͙? Oe%þ8J;1qˁQ \j+ 6$N&jbTG#v1ȹL#R *D晅7'!2Sala L4KnAґ?:ا,V^RZt'ѭ)?9(ޝ pܷ3c zqo R{<gͽwor|ßjN6.PQ4* lz#bx~}AqYaFcI?e#)wx >Uby}46 .us:;ٺ.ft_-+}fY +O59jVGF:=1Ͻ8aaqG6LvNrg%4-s55RO+cv6Lt^IHu_Ni+>}ے];$[Zta~`YlIa~zn Llv*#~⭹7Ls::֭7},/iyj2Q%U+TӃw֡/ ڷ+q-(P Ƹk'"tyDg@n2Qj|̣ ]0/Xr`6ܔ-Yz3vpe\ \:C7bߡFŹ.cn-4 /֝q Z%mɱ8|r/DPBp-@ݜ[('2Km[_Z3QKq3k ہ j6eՈ:lxF-2~.MfFVCB%WB'/qy*" #fzydKE!i܌,Lrx$#q, Z7I(Mq$jN$tVKng wvmTZEx$Tp9lύ-%M[Yuߣ#-^~*Px^Bij!c{V FP \cdr~S)#xG\7[շ%X՚J?_&['';aiC{S]̗Dc SwĠ)-t3ŧ̲W& C4;/Wڞ|S.&* \Kgx%*9Zy'γ=u,P8[Ж.k8Ů8^9?+l˪]n\ԫ #<2+LVYI Poq\^æۚH^@؞ s.N5nV>*|z$42*#Sp/PXTHWFs81AV[䪲hfy7mW@ڟ%p0wmZ״a7qw3B@2qoXaTG'DI}?}YI5eu@8]}Ni :Dcb>nFL;iifi::/\wmt6tG+eJe)P=od(~~¶u&A6pF!"n8ߋ?ct01"@-!1#yjz~Œ(`ʷ3l!ɻY,ȁ=yvGQfa&5~3M Ud*6~U2Mtv vFٞ(pSqܘ*=K3shxAވ%iSL Ծ\v1$hg\U /aÂXk4 nib ~n.Pbn2CNa%lHϷΐEG_ySUºMF,|u.,5It’7?-G B"G [g2Caۃd{9( "\[BH+%J/dD7\xsXmj=WS̓kۛ9L7@K6-Fl`k7=OK<<}w[aYQ WP PX:yq㹉 N]Ѧ3ZCkfo`T'.Mqv9 {C& mOp?UVEMY'R<,/=I;Z(_OIנF_f;Jb8.E-4!1oEGk\[o^'W$ӓ, ;zLI@>&pAksm1Kk3S0H٘!BBcYo}z"R;u@XTԏӏsgK/كQ6F,<8ZQR?8=ܚU<]L]`HugcۜUϊŖX=B1z8ӽN]`ދ/1a/8BK7LQ$ޔ+\]}"y4Dh5"! ~` zson{ Ib ] tuV%=}U vNWGQGDUꬶ|Z8> W$oo28{1RFU6ҭ0c 9 R|ĥXRcg/l;l%.(DSh5ׯoH9FeE{`43ĩFaǓP>=KJ@bm%}& x2˨"Vw5|^Ϧmb!2ۢ㼬O5c& O=vу2u[?d4 lI`$su{:'DBA܈A]w@t)]B%0oF@_ZS/ %Uoa̙h=[>M;V)҃Ӷ(x]:ЮTShGiLɕ?*Lmr#a8+\ m)jnQn̖nV痁p,?`,GlB3v#q|:!TYc};3"L;9zZjY maAtU@ SL\·Y2FAQ$,bIlu=fعIx\,fb-PW<2x]{b\NJma~J̜IJ)ˀ f1#!m,`ջ{ E"d (a(,mpU[ ev/*^sH.<| ҫxr%Ӗ ^?&P| >c!!\,tpFN0P-0W> k"ul4ӌB]q!G 4#hpS#=W54LgZ/Xc:Bw?=V3L$땅<I2Vq!6Yxv$'-xڤyŻ" ̜Dq^մ pjoF^1" ϥϋY %/IulҹNi$ӃT7We<ℬZ6dP1"%f{_0jIr}uPK)Y 2WFLLu:n 4 oX(ʾ)jғ#Ogҳ|X&StP+.Nٔ ߁id.! R#E16!jkӪ&rj;?bmY[2 `;A.iDL\:Cԛ$i#ls*`.VIrM?Z@ )fsw #p5s۞w̌HFyyח#KZ#Ęg1:"F(_kϚI hrNjؗIGKBM跺|ѣ-TixQ+k_m8d^aecZI34O tBG+9IZ\?ɕx%O] @*5Ō,›ZÕ8T v;dʕ1BXT7w`MKxcp cs{Y't6?|p0x쪻m-;zr iŤ(Z?j}J\e5g?p#^,JQJ˔3K (7!5V.<` dpEQbX|։K19bhԂUuaC+q uDx=CR;-75ɌS6˼|:O;pUd~#2PDn *xsH 7ڼ)R_ a+ut^zd"__$`bU3Tv>BNQ(%}Fy@(>ug6Q7uФ a´9!y{U*|=u7&u?!eXyЦ|QO$.xC:Nq=zLCL}c^؈y^X}=8dLs,Rj22dfW\cԥ" y~¡ce ys >|Z|Kae xܨB7<6/Tύ##F`x}BlhxpRdMx+OVpEO۔U6ߺOt)r撯*Qv([`"c7zžKڜˆtT9QZm7cOrfO}MfY` Ā\"FVj;ZywxqޅyB[ߜĥ"{0L(LS.~jfjGZ ڦĈ(bF>~P@%舉qvPmȹV!˪UMW+u%wpc9-}yrƛ`-yw"׉`m&abU?/3ȍ~Փ.Q";.vC ۾483GG9p'j'A]ձXz}i4Njgjq6Ӛ{ZyԍgDgEJt;s pᎊ^Nj?H G6ۤ'k R34O4x;QI›?aV$db&Vڻ@q<1@ +ؾT8A qBFPD> \$V̮-Qgs rإ@T<Ѝ??^wdZG$ fK";J2FbXߨj? d0!!=B} \C:xJ27q ;tm/},@jG A׹r辁Io7l} +"ͯ㻫H*>x AsdZD0}j\t7/ 0D!*,ZԘ$7[7Ti6|j5Dwr62E~xY򕰚$B,Ri ф 'vcԿ]x0bk5Uyd l7!|9ݛO4pM\[pfg#!󟼶x| Ei:04fDa1m6{1dh U!5g~gjKH`S:`"4dE,r8><,aWH2!;eYH q7H0?GG֤gu} iZ86˿ǧzRV9rv?\IuGXl:-7PGtFy3F$gN7=Ic> `XÃmU#A lfO99DjW)'1S2 Тg\m&vv F%؋jӨw Oj!0] 8Dv{/kgt$vit6KJ_4/>EIp5yϫ?#*tVwX_ FYtO[8hPIwCέ(׸0ρ[sTE95ȷ$ چܢ>5k3{UIί$HV65*t<3agQpu̵xY!I=> ֙wPHl;;D>aiWl]ƑP.YqEHJUJ;U[cTuY2j=RJ^{J Q6j J\*8}La<'C6DjBdKaOQj{k{'8 Qxq.PuVi,܊g/Vy.:F5zw՛(KȆcj?׾@a{sWg3`;BДkF9Twk{)i+ crPNRzt;h2${HS ͦ,A#$=$.}59*sdhTLYPt"y/N;4J"مMR _bHrGg ljQc hGr8Lθv⵷TSL>Y V.5н@18YGBA@U呠tL*?=t lOǥ <}/PN4o7R2wbYBv{F*( `Z**ʟ|MF&w{EKRclD -dLi kFd;Rx)2ML]D"Mi+JF?벇 jew~F_*DDwPޟ> ~ͭҪyVW; do籦{P³hQx8Rh1D}͘EL䋉荺=6IZD)nubI~P:S2R{LEɼC8d0˲ ]9TYW_gF N`tvYoFCU~'#1 5%C"GP;"~.n1nϠLB_#07byN0[?G[\ CpV$@~־G۪)b_xn3޶ p|Ӽv%2I#1AWnj(s餱]HZ#;ѭλPnMFT}t;:ZL<_ TofoƗ~VdR%R}=+K XCHddx]ClZo1XY Y@C/ՌC.хW^HOŪ\i.%SY 2q$=D:3gά6(өpr4UWxy }Bn Z5r?ꫦjY1ē3ӬA~~" {4rp unuy%Z_RK Pf@J $`,eWwJIB-0=ʡBf; 6E? LYTZɠ d@p]E& "ȥٖ#4ޘ1oFp;VPء+嵺6&ayeJ`0:]ÿl YlAx ΥO\G䪷Rw:foϘ#xu:!//wxN0w"I7g/*8Rfq#+fNI#-ezmތBe _y 7  V2n5KxƱ0 =]eCO"?V.fNn モ"2]Grób'|ۨȷGmZܫҼa0ц $ѥIBQjULI%3Jso0bhDAJ7C∿ 6g<LOhu޴[ZMq, eygd270 mr &]ʃ =WiYߢ6VNT\|%A誂[v%mϡ7M.ʧpڂ?Q9H sabAr`lJC H1s\;E%,f8# H\ykEE4|)گꨭV,nAlqm2M-03huGa[خ1qˆmm%0ŮZ@W2^(x2;>k iݏ10\z#,irT锇U,WhR)Jѓ;OI7}NpNi,Fw l>Fmh2Hs{?peЊԄ^$$$)>>olˮ,$xvBYq+Ly@`~#~LMP ޔ⣧7XnX#p:pL lxQE S 9ȽcSR ϊɓ>I${qEs͞LGu)z [ n5Oz(!}IK^XkoUy& Gvڲ[Q@[C'c}l_Cx*0V*Ͼ.9 E-{[ě[gBR5dҐѧyIy+(| U2'\SMkÍzWBIgl2jJ> ޟY7hNt#| զVVWQUTd69DTiYnkYyо++{#^ll18lTOwt~4ܥƲ57AĥŒA͑,F8SF "b4ԆV@ᒡq6A>5aF{?yMO Ko2 3) uz*KR:0hM27"^hڪ:E$Gt\+xٍ"*qtV|q`~d- @hF4#q5 4]DEיִM*5~ЏcH9zV}وaxm&'.~+S1&b?IBbt3*Ô~kֺgLI]K,|b.ۆv/LjL K2X7Ga$Qĥ_Rqf$}\Wmx/?v.~ѥd^=Iw-}V`;Fe˯pf*/hre5[`z0RڪT@25 x,9ZJ@9vO 04'g 6Mcϝs1m,9CyO9X6+6즔/#aXNT㱢6~eA@f۹%ɚVn"amF䥾Ka]3HP|ww2Anי#U?_໢dkQwAB fr`DEɎ CJNB{l!)qGdPg#7K1vGϐpyk$t?UKO!At @Z6@BzFhW MgĚ 9 1Ϫ%S2]QV?,_q&RcƏLoǒ uzX4t|?o5D(*w՘-[3!f熏W%r aޮ YJZ*Y+֥;d;U?JXמrx/i I+% 'bCC,+̚0ezѭv&N}v)_ArP;26!C)5=3TJ24)'T2$~Q(q%ܚVgї6^@{~v\jk}CfhN M&ƓKIݣH%-~q7be2.S&N;g&Oӌ0"Бe`(0rV\1aM),IKE33O&+vޣ-D I]`݉{qb "m!ySpv jB=ԢɩӢ.(h}F99CÙn_eP [7LuEHGRQG<4nYGUmHMs7@,;@?[BoE臍\6v/d˸FΏ|j_-氤_"ɬf{3k1;M|[|>9ɱ3WƺAz>tϪQuP曶3^a+Q YΎJÆ[L O,4=0p@ 8 >bq)|u:섀 sKN+Z} {Ib%%DQ>`HxnB, u>e.|KSz]!vw0T6V'\}Pcybsr.~iUG1J;L jx/]i3)ybV[m`M7K) [jI] 8 Lm +mޞl~{4JO\佥ɐxCTbLQBc6ӎ&K8mz2M#$؊FQUu7ghl=4[*h5rx$7*%q}d*h9-9W@"aQd. 䣕gԊ [DU /6xf49!j66ˑƬXk1J=?VrӣqZW&Y`2}3٠{EƖ#IcI*%Wqdy+pY(Qq۽%AAuxOW%>86 Vp&^h4C&8N[0FlnP<48kXɎAY X zgeJscP@wu<`˖wbzw~&a6YƁO!o K<\0x]$qQ5w0RxQ5B^\D972C{t0=|Eq k>T˓妌wI#'쬉=Aicc_3^UaKH ׸ _PhC9zJЇcWS[ nC8a}|\kЙ>wN8=]ظerWQ]drɇ/u1'vf!zY[JhGJ|:ߓq 9CPʽ8-Qts9B&% %.Yԥ؋LHxJ *`{L[^:Ykan.:oI0gڐFuC-fb8-Id}xZ7JHDx~ysS~j/w0Vr84FeXtO )Gsƪ6!\j]:W``*6{g&W[n5YCa9Yd*Jm4KzV]LM=Ѯ 4*8E[DGL/#,/W1GQEi#<(J#pÐb}T?RCM@Pdgj;R[g8ԱpՕ9yjKP{Yʺ) JvU9j, RG"mt.~#ĘFB UÄh;`/MU)y@gOz宮.Tu|EXۃ2hyI;>>;P$.hM}LÅBه h dGwKyM&L  (B*b&͍4O!Ul{̅";"cV D%< ,1B+PB.~+^ = L.b_4@:xs&F v1.gAlvq*gu x R7{A81YH]8q*>\PVmgkmӧ YjK=>>Ht>2 "zUK`x5Xs cE܈>!cRHvt:g-F'\nʹ S2PʪT*9ɶUtd9Q:bJ N,0^D|k詸9&{xMh"ѰXӰx_JJFTyc!Œv"7GL&tGNI[`2%[=t UĈL-K;@f EkBxէj8El6z}a:odϮ]7SDRn,ruM͕KF4Ԣ,Z&= "n+6KZ+5¹Br/[ <Ѕ;e!5krPaɠhMWvx j6CKcHˆ#ird BKԫ=%LrЋ`C,],^}>:<> 0fʹe3/a*oqQ]݀ki?;p|Ӄ ǂ`}x`51WW%6UCýu`~ }#GL\QK3f%XpjGHpV$[tMc 19lq2ZbKFoXtYf8"0њmKDD soӰ"ktItš&erud(c a0qW`qdz!RU v9|XF|4kOS_}L ,ywb)¹EXS $ f@n<ϮGLj 21Q^(I`F/M~ث'H C)=S~$:C[|kibc|)hc?jskc|%EEA'85UTos*ee\_E, i-r7ŭWwKdi9 ~`Fpagvm6l /19e**">ƬԃcSf4Ã\ASW(Ji2%Ոo$D%Nzsi1+J&"Kg/>S(5_ mr'|Ns]ʼ/<x`1DhF6#/zjXՈײ! ˊW͐>-qa9Z2A=-~w69s$cny] ~ڏˍDe6H6DK.ZMB,c̼PrT9;$Of ^i4HgP>ld$ɮ8y5=Ih6Qf+% w w& 2WUlFޙe: r%&Jf~PsGkR6z]ݷیJQ8ߝQ욱WQwWM_~0U( "%]"E տ2N^s+S!Y:+U0Xwx~E7D{#Ϝ} ͩM<OY'؊P4#v[$I/~s+te\xBᖛ 6 ;{f"7 5ިZ K#fa7Oo8 ~;iciGC'YaLswnjJ/ye[{ĴW{Շm%%VS%k _fs)|/g2HB n.it5ŕ/(D6XR>A@~lX0u(-nFGrR-%ZeF' TLb:lTڶ'a}xpg)zu)Kґ Tede>=&Rh*iW P3@Y >wV hqU^ȡ2g% k>WqU⽡:9}lꮼ_Y.&]V{sAK(:kI$VPZ׃nQlߪIIz4CC aXt!-MA-"3P k2u[n}zFL,ч4yYo9PJo@{a HԋႪ2,+p)*#-\ĿH6L_>:44f81b V{nՐ>6~v4Ƹs~M[ r)l.v1).GZaYSgNDw>uҽ Gyn窻H_ b.3]BH)ps-Z17ٓi7{{c\۟3WƳnh(~Ӎɡ ^1P OX/UFabEoEx_z;95?0S&Hc5qo9gڸ/ ~GfȰ/ڢWe-#W@s!6}:jFD_ 81W82K^AF*,vUW3{:V_mo>}^9F;փv'X/ڡ0Ki/dA`x2WڵdϪCj adcGOρ ߣKJFpDy2z0S":e 9=TuT38O}}|)*P96/T|'!=1xShɛ.hOvщ WQ!X,;+#`mWXRCTx5\|U/sr<Ƅ΋;cLx Od@ 8~U՚%Uh5@"7EbO_WCq3^!m-X7*'! D8 >!_p=,)fN~:QtFԛLӲ?(^ÒѫN߬{$\&t/؎1ȈtzFA`:'5 9v)=~|hg `SOft ;m(z,@;S'S֓x66^2 ]` TL/C+"BŇl,+`r|Hw Z$$jT}Y1R6dnO*iI#ޜViR Ur&-LG} (\!N_13P[;/8 xpG1ˋ# "72)9,;B+t{!,os%U:ƀ"KsK3e<[&zE=Y6zzB[lU1UoL8 ]0D>4!nИ¬F#*|mΩ)^h–huٸgt\zY\9Jn H[6k,m{A)L7P>XFg)4 y!4=N6^2ɤVByPy7)4ݨ@2u ]h3ߡÂB-i>pi_uXކGUm)rһOO.J@k^'ԼO] dp{=a/PzHr)(uvj3np g[GR2:^Hj`_i39GEżp]{ɋvU?׈Ob YTwy^hD38L%fhnXk4W·f eC[aN}*2GUxV(,6gXjUM&!}aɯ;,yeܻ5e""6"CRG{t' [BJh_5潬O{;ΔzJ2Br N}E]OhʲPn X\Z(<^̓#̹qe `n4V|/KഅK1qFEjh-,P /c-N-ط}wQK y_v{bfL@OGu̹& 4ICn; /9mE⩭M>nK/?ToPf՟QVBRJdG`&$Fj͉e'St&8P֋mRYTw{TyG )8\k*mc?+M`|W{1ds$`?6A} Cfe9'+b\.5}L`gs><4 {9!;lfeC@HI' pAI!Dؿ -R 7RU.؇?p^mŅ9x(z@+L xEM~'p&[`3Լ:.cޞܰXT[+ 1{7Di$@o BW8Q2oOJ2z  .cv'y#f??㢸3?+LW A7?ǀ0Mv6(RlG4טU`$OrkjM5֓!'Jς!Bm8חk4A?\('U N8,PQm?>bs, +Ju.%>NP֤#ci'U,baURHĊ&.U 4"O{C7z x{PG;#fhGO1:D%AL5&"'"AoM~ 6=[)7SLb cۧ>~< 7IQ*wpq̏A%qE`'tp^cʭzRWjAJ,wC:`Cz+e2;9&Y+ĖwDO.-|~c:JK-7|fE9 /n"ze$n } mWo408|&۷ hg{ŶH6|=g*(ݖnz_o.5/9KѼu>l.eet鶆ȱ[vݍ-t8@e^syyӽ K6պ;2}5 &tEwT1pUjH|:%>-C mu@JBJ\4nm(X Ni+Lש Q. f_8n\ Pxeiޏ{5%>&24Tl)_LNCj#6$YWu3QlFs3IwmmTHbevȏ}$fbL>7\ґhE`xfeyI2 _t3D=9wa :+˥) Ȱk_N|\Tc~яٰX*hw ZBhcA 42fG4"m鄰{H2IaI.6BN0U7T9 s}й_jf|7+Jk7>,PV+`vOi*bjW^3b2}YcxQ2x'AR7+晁f]pgʲF'bX;~4ȋY1R ^ӊqW0r>m})+yLnR:[Fd;^fNO$|oqXTY$+iw  m%mοި0K8kOcEQk [ϗb a7K}~-Upjr z"A~}7Ӣ9U|4z,x$Ztu~e;\v /v]5Iθ1~ـECh\R%aSB?DYg(' H45TM9peP%n!lKr3p4C oO)\~='Q7y叮hxx;\1Mst 8NTYzBN2a S %Cr}'4Vl_4^Q4>rON0xm ߧs 45fJ{+(?P4ɉv0*ps6&m[ĞYG5&*KP|Α5dxh#/q}kmƍ޳MQT'N9.8PbZtu gP#O}ehD-"tB@.}pv}#¾"{vCv/ @;.jES܅_HYH2jf5'fϿiM"-]"t`r_/iCq l ;PzQutw@^K2blԛ;5D ױy=7{Tu}u7 LZ8aѥRӠ–Úd6Ew#wek3. U/`nji}qx:ږh.PX!ڄr19?HQ@$\Z|rrdJF5m.3 @EUsuwxvݼ#H$,,>L :h1_8K"MLXӥ6zt L/bIBYŽٗKE:sClɑݨ6N]_7K.:YPe>N)8ɾ)띵;j˸Q`v XB s饃/aH]!%hiR+? zC bw\FjlU=lԶӫaMCUbbR7yI­>_ĉʊ 8j2@%.`ZJM8a-j9SE|I【nUJDW/:{B( k<7]g!^!%mF &щĠkHHg[ -C\#C֦'& 5o6 G~ uGmHiVGT5R_W<|2sMֆu V߭Lp㧫5J]_i@2c.N(D޼."DY{vEcnqzZF[6@@n; Wc^]b{)W8r4) 4 B Og&0FkॉG&Y/z#WL筮%GR'ESfiimՒ=g%wN CHs)nB̈sg^.d;Tik+9,qcBbǨftih)sVU]r1 :Ruo#Lee[Xsư( ck ļ#cۑ 2"OZG3DUyɨN&pfJ(ǰc2a+E5WaVk]9Yr|`2S#բ~1HC'y_D< CjK stʑ c`(T#_#Xy\ƇEʼ}H&@N!x>*@֝CL)kkWc[7r@8=M[' #t%^COPM'!& «LMqfX'  +]TPt?·٘bJh S{0Dқj(6(jK^Ĕ^30K%*x> f~Ϋ#Rfi'WQ Gֲ=Ul kޗ% a؃tNOSr (Wq=KFZ3^8߸ۣT`-KR)-N*,/Oq&2|8 ֝)ĺN-Ǻ&,#s p3A)_6W'>-LT u3"7kBhveӓ.Uɷ2PRR x |Uk¾.t}YN vuZd:1Όe3d!z8@h&҉L꺇.jɛ1]ψ'YjqLAר 2nj[[C"q͎;$UK>սD dӨO_pvPqu k.'Vg{̪].R-e"C"su*q͛k( tEra/,߬QV4ҝ^d^F4I7bYK.`0e)v>;Wcɥ_3=&lINrKUϫHVg+=\|(P3gcb4"YMD?{/nbUޠlmQ 2nO6Rޣ9r}45[6%Oڔݠ ya$(3zV@0%Ƥ,.]kprk%U0mh*)-5ΫDZ:(=TSK>z|:l|[yl"@'|}R 3M'%[R+6owEl)efx d0%e,aUQ2Yḿl{MER KY8~3I E$4괮#91_ YWDxe|@> 8X1xFO7TfLg$첬q:o8I9\5,Ѝxgsj ڗ=N΂+k>,wNNn4R0g!,))'nucn xW07"A5MCOHIH#Y0.5лٔp`l^," n?K [@&`n 2d2#9MhE+drG9㑷ł6{_)mT7kV7NY`-*ql"9:#Bv=\=wdTeztZI\H~&R?Ղ)r_}|4`90FӎВBVP|6 ݏwWffڜY 2v3R2-M* ɧjul_mhʒ}.0wgp|Mf:uC`D;'㤵TWɢou#(ck4;ܐV۽ ôyMDH WJvwcX ~W;23}MOWp_!GM@ɭ-rdX_'QAT '.ay8ht/*~Gމe5ZYK n2C;K?>Nk7+1o ҄ \,boU.qU]Sr8zRєgH.K+w,V-C?\/?TKs‹=5!B27*PWx}0Sl:;6l>~HVUv+MM.!A ϿV&7z!.]! }zOUC Ƅs *c[_:Eb7lPaM`e46T*:zb3@vJ=qD{#(!0sZ@M5G '0έGGo8% 7\Jka+|2<G1Zբ=y yͷM.FtƮyܟ?Ms]=^Qt]RӴ\zV6&ROD􏫥#imÍ,էփ<#F,k: ]D :sr8#WHFXt&aK{G$ ZO_x6RRBXقc}) RмvKFIDU^GC$={B aAS9j`g )q ** '-;?3X K&W$QwvL,gDϏMyMđfMN XrW"8!ôԫmo+ /CɂEQ_]ٔcx  '2DUEQ}qi-_K3~b&ש U:_3g.5 =Zw.-o \N1;d85\<tVx% e|^毠 V7~!Khdq׮]2hf, *%Lu["Kin=h|dKb BNL&3HlpM3av.-3Hor1@lJM+?wݑ 7y4~2PدGg܏TRKGۤTs5Τ<[6g @*!g'>]ؖb`Y(N,iX+ހ+T4[ MlFsO&~% rPy?E62fUNk;0w8v.G 80:Xh1%oG-fܗ?d2Cv=пNmk(wre3kityU(yʇ޸.b\hsLdE K:v : de=)K[u-ꢼ&ָE|g x:'jT?@C*=0x=E6ړؘCkmQjsԽ%@ muڝW6wwxYY);́b7xE/y ?0S|O\W; %jly{Hv?;Vq@spfp4žXŸ׋ؾFnC<"vBasAˌ_bӿ:Vu,0oF jzَ0}Ȼn4 Z;{A ky_re\r?l_P֦9Yd%vybsLa)cyw+]qH * 箋p afds,x9mֲG7`4^_>($]0Up {\8 .N; 45PCwؘ2C i!@~Ȁʏ(lcP*X{{y]1Xu%7NDxM2y"4auAJ i8rwf s8.8jG/֫}oZ"} W`U蝍/9˷^^lw}!49؎MNu 2$T3F Gpv,.ʹ9;!Hꝺ)yjւ;@vL"nX6n;bశ%Lwf/D;[YE gg _e!T '[iD?:wz&Gsę1Ld#U#_\鸏nċE#X%8pvs H!wghV/.ƗjwBYVeqUv:I3mc-Ln:VND4!R CTZ sG#iN[@I ##-';x9;'W@@.klh.N|p;*7#OeU2_hkT2u^@{ 0fUR(pt]$xkC\ʞN-zzgbϵ'#OzIyeClD~' ]F;״eFj_a޵&6?%lkVG!" rk~Կ2'- ت3xL$?dv8d=bs#vFAJ6WeP2{5)A9 (.ש"°T9Dn3̧"dNqpl2S MP1`-ΦkAFd-.=x4Tf$圞tyRb8%(8@\]{W-U3lr4T;JfZSIy00*n@|}Jb-K"McYh槯It[~}r"t8adͥ `8I$˪T J |vh{1/Yf-~PuvQȘf ƹga,+ɶd (i y{ M *55RvW0n2Q1_2޹:T$?JtEẫ'* r^!ot\sv=|9OqR"}-Pp@ gYD5OT .x']e`a|o(Ae'"~n " jƒ3כbhꢖԊݹhxu֋iʗ^QwٓӴS+~ 'Km&@K15A V?gwjkTp7 Y ȌܓA'l\Ls⋀hT. .F_#B"lJ ";}qbK76>x0iҎ|Q2 SY3K' sUJ wuS,]{(6z! ^`Wc]V%>ny|/=3V*ӼoԄv%r9nGYws35'L4 ݈ ^i׵q,Cu ڮu-vײAEDiLyWEz QvEE%q6}x~+G3yrxm҆B=M J!EV/VrVNMP#FeVCX:C` ℶC-2MZM Wqzz7D~4Dž63T_8t-[man,U%4;0η.ZVo-8Ӓ/ٸ$uAD%%8k|,ܤRI*XźsR:4㟪M׽قB餻QsLE+Ǻ=TB͌vvH 511F`B1J9TLM p描 1N+PL .04O)CE(R0񆃛$|9qF^NJ90VeuHBקT:~O.r.lI?] Cͣ(7q"JPLF,\0:=^M(HDV]bf@r/8IMuհ].;|=`\G#5gEjMħGPirN2Agie1d iO+Gv;>"ޗ;a+~G[K= ! . uuF]bX=$w k*歲-mt1 sy؀oRG}B0C9B_B!F[w"EI$d(3ѕ֔wV Ep=lsbTv:wJDdq !uOq@o8ѥN%5r+Zù97Z?ȕY^ ļe~֔~x䟹4.GlϊֲݧL҆,!Yk`†E^d'ʐ~Ǵx$Vםu}* 1]^`$LUM67^-; l uZ>f2q?H,e z*x| Te]: z-3+n:7 q_H'Kh.55e7P69Q?"L k>8/ǿyJaut#{) uz' B2t>υ#exG:Fo%G=s4<0pTqF v{0N-G(U免+GY-&;i8A ڮ<yM!5NN}P`FTD5Ύ Csm{< kA_lhPVD:sF[#5<`sK_ Ѝe80*% 3ak\ JFi #>MFK48ycPb7.QČͨHFjDi.axBQ!Y$Z+рzd ;]GD,S"uo/ 'b]髥"k9(0RAɃ+VLQx-Li4h)q nrfRbW#w#fgN6}(mX:K)ފH"tM{46STp)rl#=tL\-4|Hg7%I LXh2n3?u>;M5|ڇ8-a <6ǐIg.VCHX4EVE3@*Nd uBi5Fi57NOhxO"Eddûc{{RRĽ+>8/VcQ&Ϡy,q&U2+3䵋ap7vKzͮgG%6twB^mk3}E6>ӵ:g.}Ob+*N& ooeݴW鬇ۃ/A~ fz>PlXF2\Jq,n{򟺺F~p]@;ل#+`l:t~s!rL\:$4mUd2~ ^+P7K෨Y ,e'n `Q&D7`1k|d]0ԣX9 5\n Gh^+g PDm"\ 0nk͛Ԓ^fRB>Ԡq*OE!3{=lc$.1k|'%rH0_ɕdL Ƨp4\?J}H2>yu(|s #V+h4Ǎ b"'p S@LԆa1Y]5oR:nWqH8&α(&8RB#-34)Up}`Šٵ' [O$K!JV)WDAnoŦ);FFrX?y;Y,'jpXͱ۾R *QP G#* vgiDLejc/~Ý_ Aiާ}RG.TZ;Ildp&V*(]j0 LݭҴkDZOMx߱ 1]~9m#ݽS[A::3 ě~Ђ*y<8g{SbB6?X\Ξq~]NRqچ4> ]{9iYqH &b}jpK-Vu$X'ohɏ2>Dl{uX[=gevyJ;^9"OmÍb@ /UPB|֋ Z"ɩFuSqHj\Ds ,,r@uJ@IH8,)]!zg:rn(NFd[*啴%9ac{` BFujAyDtֳ$ߵmiu!#֜@y, ; 1Ҵ p5%@%?[㒝EUa5hw($.rB!šC-k{^Y.?߈j>E^(tBPNgjFD5u=fRZP\ʰa1!.gn;Hs6s=VcW΢v0)Ǘ+| h faJ6m&jO] X" /) [H]MhC9Yq|j7#V" w|hX}tq]|R0ǣJ'aBȳEWwщZDCu8&$,A;"#\Qn!fWGE2X=탘Vk^߬V~MW\oc åv!;^iE7ڎM"|">-T= p;K 3%¢` bVr7IK/hYqE= Z^=ZHt`Aq4z}"-$fAN9aі~-7 Q>T{? J̻| <5|=F1o `#!jM:n?z=Vx>u-J%aV#),Q!0>}k%Ɲ 58h*N g<8lAN|c:w/k94n/0\$!/ 41~ѼS 3#W#3bEYD>m?JNp1-Av;{e`NZ%T15qA]֖<\XÀEK z˳@P*TB(_43B"@4O`A11Pq@h$Z,=߫4J-r 29lӼMwƸoХ8(*|q13čBjwycY8K?Mrmt(Pb9>TđMJD G&%Bĥǀ- %U%Uؖٹ 5ʗyUT:2>0>$dcB,^"gg*10L2_3/{ΜP@ނ<_P&tawwĦNuMr.5 ̢V>\fי'cȞ%kX?8ߐ Q$GǝXmc  n3ܚcDb_MZÐ''I2bkx Q S`4`1aI?+F}o;~mr\C!F׆/c(XJQh{s 'jOp:#Oin2Fԟ(1 ?u"FA^>5S*b)fNp/$U/$iv8 >F#VJg +6R^f308DG}#R 7x6|B3R i?BNE?/:b"X2C4U>/nRT p{lF7pr(?6So@erN%2k+g23!n,򾒹C_EV) ЛlZȌKP¾C[!%U1Q'6aĐzlu T(u ^e%6 6k.Y3iMB*W>`$_dU<^'jl]M}Zql6, QsL5V^ Wt.r:xS7UHެs=ᡏ%5w h W>G$/Slwm `8os$QsmD;Ѵ.= St2xY33Hnk+1m,k4^ߡ3 YBmwuypH9v9fbb=Y="U^D}2|jDܣa|#z$erb6L]w yD?οDFwb9(6*7A8gcfy;МOI钦ܶ9o -y6|FT_[XGc1?2B}F}AxPPơ3`tSE<F%yB¿a?Fy^Ш%θIэx5 ې " DirsAyIrс#rR T!˥hՏKf3-Ȗc,owx/p/lf@Vu[PWV_;um͡iaD㷤u Croؙvn_ΰ<^JDY۹ ȃ<`}u}R­TLҗ^h͇P;Uk 0?]I ./l,PpQZ.IH"_L_w)Vc d+h+&~rm`|~m}"4sni4n ZH+ +D,[X /޻SpDة91e %/_6* \*-2M >ud /6K\B\m\[;rݝۍoWmxq>),$)ksE]OׯpHuiNMo10Lܭx7*uxto*&WkEDѹՕ_ ^Ș9b,lO+85׳-XfD7:E+J։^VnSh x"zLYY;>ckfV{r܎[h38Bڰs:Xʮ Ɣ6nհۯxѧowiHRLPl)xA|"ئH5> 0lh{SHIlr_j \hiK}?ɸ$5OilTdtXʟR4cjT1W,ΪԕEstf&3='פ"Vv\rꌹev (6ȩbbF,TUx M2l ̈jddkzژPS [GZv9kNݓ+-R'"tOCNX= DUdZ<ڹDRk*з~ojY+o_qxO_$ǺAvdB^jMWb90b@,z^m])9`~BYCtE%hgqP _O_|wa[<7gSJ!l0.wY3&aB|qm&ՒjDKZ\)6oG}٢| r& )xJ$ѣzƶ m8P F9xdczs T*!2hn$_&8?12zWkl1\\U_9p̜㜇>t55L9vee!WvgxptS1 pq:4!h:_Q14z1.,9-~ϚuŋFD]XX/HץmeLSlC "^=cJ m|qV$>r DKƆTOP(0eZ߶I!Cu,=n-[4*ʋűkLd<ú@()8ScjtRs=)z,Ɇ"O.pz, ?—Imr*8 %0ݓGq0&~ϑ)`^[ F6GD.X 'c60-yAb%sНb@83'vN=m׋G+_e 6-:'u8M(RhBxN)t o:f Jk}X"+7 akRḍ ;X5![T_T &|v^7VZ:@GO7.D?dƗiIvob #Dqt'puT z0\BWʻ%hf ŊF1`l2^Dr}CX eӬ* go T߾fԣy&/ jM. "Fo"d.IyÍBR9bd$f'6i9QgsOtJ/8#6T;ٜo3pP/hm* \8KY0 ֗SG? f C1@ f`)[)f6Y7f!&N$؃Nhl<=dd 2H$N&Sg2nup|U>KBkrYh5wKu hi=e^ඃymބ} ;8F6ĎxTV$2Y2 _ex3q 2zU(xshUoC%7d8 3(ϬՠJYaBb/fΣόf\ʋYi~0ǘ;u]EOc) AgTܶ~%8OHOixvlj  .N $eK{C2u& hA-s8ɻs%|lqz(wCy [չ ֧hK{ZО7yf'>*@A!3<*IOU?wm?BwDJfNb8y*ъi(x2>\HQq2>J zEei" ai`TQ9hx7\3IS_ G*V[brP-XnnT˭7ԊBzOM҃EBډA瓒( ['GӐ:(sspB6>X<)!F؍j*wsbjX]Vd~2:f2j:~y9gX$^h*pOrG0ѥ_` %%H9^_E!V7ߞTHT Wv)PsM:jK;սZN/{bq!^ᶊܢ!:ǸDEWEbKvCF\l%^*H@'?H*]bʳlł<㟀 IjK2ӰrVX5ECC2ڒ 1٫D[vx#k --yU@BP96co/uTNm:OGYv̅=BӃ|AY}N?386. 5+"]zbf9h}b̥o fw[GRhMqTo[ixAZzډ eY~e0'"H`ra)f jў^5(<ncMFHV#A 9 ـl CmԎV"2gInO֒K~!ܘ!yps%0 =/kԦPӷ3eLioB]Fk&L09E$)G{)|esIT+U*bQUM?y̸vUlOϽ$>3?0mDr^wFoPrtP~vA(T\(4|6Їm7(!֚QPh 뇰.Ȝ!4='^rthfmiЭ (k(\P$omo)7?%d iF2a;n_@J[YՍJdB&U.֙Z) 5vpU)N>h ХAv:.]G8!"QB)6ᒡeڄЁn4ڑќ V6 :#fq' .|hƧM@ow~^"h LA i4cIS[fHN?wrPtq8,}?Wiɫx/mD{ڡv-bq;d5]Jur>*PsE=dڠ2gԑThrqRi{J?Rig]6v>Ȕ?v=~%:G,a0åvD"8nX-x{D%؋>z>,/fra,Nw3NC*bnJCu´LQzt3T%e7$!`T.6痚0Kh~Å$<\OP:c?Y/[8eXf5^'`+rMT#7Y_ ~Hre0`+pZ>M0&))vaaVsqVca_hP71~QfB]D6wCţb`^}Xى(B@F;ߧADu xBjPR}Jb&6 r$ʽnGor)-+Poڸ0)!6S0O(gx$9-?;`qWfv-r6PhhX6w_~N8 ƗwnPڣxrw|DeXq_p@ bQI`+5} o+ZFV[3&`(oWRx3 's C+u!TLpՏ'o.H= Sɒ\<%&.K65NߕwqDq7$5@yc[[jX&,!#:÷1OG',v!a,@ ;ƭi!f 8J,MLg2GcʹLu~G(]A}AX,$`LS]ƨ"L:IWYv5Х&Iݷq m%)u2)t%( bJ/FZeZn.*]{kXƏi7e\4`%E;sӆ(Ո dR+:늲'w"4$C я#?!Ec>.i:=2HD?\\`̕lC芐^v;oDZ1Â%۽Ƒ$dLQ&/(ֈlo@pGZni{m#mGղݻFb$Нl QIx U0ZZIh]ﵯ¬V-t,WFy5WXU*j.!,}E οyxBejΰj{Jn@9"7K@.1#h-[bG# \RZo9]pq_$Kq7uw+9<\4V].Qr 83~1 $uQyҳ$[r806J1ucTY1 Qv8 -ztjߛIm253a5Sn@XlJ<^OxF{[~;Q_ؖ-TAɍݭC}kX;yiT̰tCSId' ѡ;w]X{ETan%aLq<V}P]k@\L q+w ] ˁcɪYwDV6ƒe>;3'^ugw ufTGƞ Q9w_1ڿ!S_QG{h ؆@c 4[҃C@nr4hj0)%?d/,Mcݒ*2)UOɀ/BKT W@Xl(Epe^tE "Yư16e 6I,B~Ȧ~le `g.-E侅$ :.-a|4347xb>}H8t*ܖ[Q. 1yvMf:v 53NZ>MdJѮ~JF,cuhξ_a%NsncHҹA>,7fΪ܄;-4* E!eˮi=v+1vk4yù LiB6CHF6uS4|O<34 $[iԜzfpYH :rſ_74MJЭw=kT.7xgCaDzܱ ʤt]WdmW<Xll>HjWnծ G5 m)ѝAŧEbp:@wCN硤籡1_֡;y< F8p4.+X(]R&\NxF}Y >09LmĐs"3ru_`A@7 8 肊ji 9uƗˬRI=);"k`WӼrމ,ɏDjk҈}f2KHl?TZB>R^LI5ٴ 4srK"%4m.c Xsa%2C "֤8 w5{Jz+ngI!~a%NI , ^Y}]Nr<Cc]4*&=n|u@%0GGYۣU!MEhaS[ v085y+{JH@s,ffxm,W 4 CzR8(c f1u/O `| 05Dm=!e/5D( =RkNj3ϗ B%O*8$ q 9ouƌ'QU}3ZPG}fK vǣlm>qדcvt?u .93pg^v\)+D~{rh%}+&H/נVv]2 OY&xIt  b" 08$ s3B]ƕ++՞ JdЍkRAq:~B maVN}^Fp;Ϛyb8@\I6$!ú;ǀ;r!(R3$ɚx9zI A}⮘y@P˯u.K>4liSDH[2i~ S H*øZ7Mû sirՌ8,iժHW-H3Tʸ(6s~75TsaZ\2sYzLI70\# 5h2ީw6E--L9A)ռXK'pe FHYrCQFpf0Nu"J%8&Uw Ca(S1Y)@l+\R…R Wy27#\>zw~xk/e.EdFy*޽ٹ͒8Q/bWt2eM*J+8 he7Şi#4x>8FW)B6܅\ڒjhMe@O_AlTT2}'\w8E}4IaX b 8aHƻļm6.JqDvoV {N%M>}KeiGжmcܳVׯk\;BBЀ H+-2$ǭ;]=U36d80X#j??I4t|W!fWb񤂂U$Bg`|/Z~(+y vr3>.ȟn,H|)NsrM63o/28wSr1f޿f#=usUM_OfolODS(_U;$xs > _?ܨ#te@m$\3;C.jU)7Vc?"\Rh>/kX - !H0ʞ'4D_j %24O߉R8BG#]MC GD5I j+9 =) Xr^]sM (xc)7*(Oq-f%r,PA @Y8uJ9)-(vLk]#FcW{]E C "7aל*{t,Sԏv" C 9EZd:k _4OFo2F4i< r+I-jɣ{VR4ܢs';ŏg tSC獃,4njƣ73N?=1g [ L1o0O,#-3\ &eHϒ>\ͫyΩ _;3,_f&X)ׅ,I9xLę \(1ΙY k̖,*uH 5|9oo0 Me 5#xۘZS! ꫀ4  ܭOB-*Uc4C8_8,<%b~k z};)TOsR꒦f⃋2PVvKuoIQb>A4O]460zǝ/c~a܆mVw.NO?)T1euz]YRn0X< "b$$3 7o \;PRP h*+u9K%ƨZa6N<@U:%vĽyL¦YhrUmU6 n-X,<=8ukH~>MڗOU!ZfJH$(n'@Fy쒵]C L6m%G%S fZQ'$x򭗋v &_*etc&' ̸)a(FpZ[om (TDE<5;̯y 'dKdPrS_ 퀩N c$t"\ BE)qY˝@*7Q_|zT&!76}LȆR}S0،D/\㑐1m0$X +JPM&^<&ʾH EDELxuÏV^/]wFȘkkĺ)pt[EUJڰy`Ự]F3 X֫5P xBZ"V͆bZ/ i O{+E:-UzcV_w!n.5% Hdr@ҷ*1iW .!8b1>>-7#|@esedYS(xduFr{.JD3?Ev͚s!I73|Bfꏠjw$xvX"6<h ;tK:+WCӨϦvXWiafщ|PnDc(@ ՗ TnA:[hꮍZ ;[s“b^x0p&[D6:i_f!_;beĝ?Fa;$o+I2wqCr6qH?|i9ڟ> {@S-q:CsJBמ!#(2sU[I2v{yWzw7XfpPw`?"ڻ"r"yq˩Ҧ务գ(SY>4+$S$<6%۹!-ɛD+6{st4`u+n(&<*c(Arx ݕڬhuc`x-Z!NOrƕ\^l&nW]o%6Daa-tӠԵ9r=JDK%cwӥc`\‹FD Lg^ȾM#E_͚ie)z[>m Ww%oZ3V)*VrS"FARYٚSde_o(,$5wޥz7ױ8-r1f^3ՋG?<Ѥ \Y݈9ŷíПfAwAVBq**_ahk'nI ֐# c7>7]݆UMC$pF/41FA/Mmr0  jI47T*щ*^ f=^ "sF=gˏ3'-@i4SsA' nV#$?1 Z TC j+9l^ާpfmj hg;ņN DMx Tףϵ2qyʯ;p~3=;Fףbɬd=}luyC xU8Ql1rK0T;)5=4k cOH7oOB vbMWhX m=n w 9xT@]n:b7ʸs?Zf Xh>3x(_A=-q asqa &R'B]9*?6xY%*̧36dCΦ]3q," DM?'}צfjkԖ c[;-GA29vg-Ga,^ߍ{@j?XLrRmx]ipfފu򄸑;IM@B=yKZ,yUpv4`EU c)9,I{[өl[s#IH Ph_1QDQ@e e]ѶV$`Vk4gCq7P zRh,j7q썁>G-xZ>bi;iI.# ]!Z]Ϲ̹(["#w{ཚ uu,5UMD6Hr=nċ!$=FT* ;Zpޛ/GYt$F:l>N'Bp[>qːiteFo '|sFaF"Ҵ)XO]' z  +iOH@kIa]?;cewrԏnglɒU wӇ?I-|PRHm8؈,Oޖ ȷh%7m͔pi)L)#\'9wۉe:k!O LN@E"W$Μo!ftxmB˫&5-YCz-I%u]ˏJ%P~+0Cư;\60\ ؕm g{lI7c_YTs~JnڛuC1yW < Q,}4q Өظ靘?Hn-ܕ΢r_"1j@[[h| XDzf hJ[zb쪭9J܇1 >Z͏lx6dp5$5Nsf:x|2IG<6FDZQ<G<L4ݦv@_A{}4], MU6g`8Ս>޿')V}gh<|cM(b(9uQڇ ؾ!%R487XpvG$ꎰsS(3#y[ ,`P !a!gʷd̵ѧ;3sϪ'z4:j;² gun.Ǧkblx'E{=QO+UdRt҈hcek!HeR@],?wԿJ II1(g<9=Ļ>KneRk; L/䉽lg~\$dn2p5|['SpG`)3K޹9_TO>x'qԠKTeK#Jz" RD' _/c[ش 35N #uɤNքi [mk;Sfrj4D;G\,_޷ʋ^S{>+уtRPRzN6OCPp Mɍ w׊3) ųʲh{e 䟔;x"A%!h{qX;g#p+ֿ ^ZC. $}{Bs43 W8iC(#x6b 3~LXګP3 %[ #SJ )'t ?:"/[-H:N= <}A2uI >kzMSSqq;C9QoNB9 hCkq *y diOM0gQHdm,_2Ĕ2kXڎݤ?G/V+@tȣjF~AiPg<( <`6)[<6vRHx7H@"ЈklVOElx&_}xǟI(iQF\xsȒIGKbEGۉG)ᐱ1xoqcWץ|e:`XrYb\EH yKk?Yef˪x vnKv;bg %wK)YHpdy}"DxR,%lh=,C U0/6۬ߕ~t;1RIh&{T((?Ѷ[|P._ C[sRF9 !8l [V-kxlC(re_@j|?=h v#o $Ur] }4 х˩Q{NS']^ pua8HaɂA"^:dgsh+ܚ@$H YMXtyӄL|ёGJ U36pV78ze"z(Bd{>ו2_Э7M 0IvnCtbp|8t*@&ҙ8ө} Y`~0as}"a]<<;() |_L d]KuICT /T}ƵQ_=]D n?YD86hpC{-}Ɖ=f-0li5S^$cz8 X[nɺe CqWjlF'~=aTkAU+f,}ʉ"OAO@Qx'ܱ%Tu8j2D~^m|q(b?|q;c u7z,^8D⑵R›[X> +"zw p_w1d'F!ļkwnaG*zٺQI|<HEҐ)t |@@?CvӻTjldJvP3dzXn VXOtk j[CnΣSʖN24Jk0Ŋr9m#PP`R~Dp*Cچ9{V2 ?ڵr{@F9I''Jd*͏(I:RrT٘@8O䄚6-V d'FƼ-^@cpͺd҆%F^rgpGc}ЮFLx=8`]3B!ϓvE C1iH\zmSCԟR2P(s8{:(}YuIĨ5a%~И[(Bc񰕍VtΟ8BOCҹȇk#2xU2 K {u/ɬ"6s2) byS[1'GX1w3Sݺ;1ַӼh>>0YFa>  }*W[^4x7m%;NAl2'CA H9-7NN~ ɽ(3Al?pft5?-~>ۧTڙ8w]. ~M{zh ?/.mڎ[Tpu5=k,szr8>GYZLC3lZSʬXK> gj9:jł&\r M>()8ILML##6t Zep7ƥq& :i|{nCؓ-=] T>I3hby;^U=X݇Kf,rxf6F$Ac#הI"e4@.ax# 6jBqTRe[iytO!qn@cie|ʌqPIt1E)ayWD X%5RW%H! 0B7ɽ ")۔Ľ7\U Vw'PnQAy'`Bş{q0LL%~?Kv!}rHNZ6; X|d}aWeͦ0d>)\zXƯ'?~v#M9܏59dFTh.F_Vg%.ecRt l_r&Ė,Y er]UV<;U+Ph_# hm籀Z9?ibm/V74J9iO^h32z~tlmJٳirj؛e3mK^i=nK%Qq JOgu`0v.02v*1 Yj*>dOvoMZ--'ə_x1RC@{qϬ}[7K^RjCL![ךb/Զ .C nQ{bk?DuĜY=Pv^{@ {y)U2kQVRԍ^gە,2C{~i:i9j 9s4<+' WyFDk_&$;t4=92^dGTd)=(".k] e }((gpW.TuBkHw=$Dsq.U$F;^ Q&bj0B{`mTF/Id:6DnjU&k_Q| 9A;ǘ#G8vb׋Ρzgc\tsc2iQ'D ۚc=~ Hbtay֚Ĥ>;%!ki27d7P>8gBSwAT9r܆T[5<վ>*gC2iUvf7&oa$u1vO4m-`r֙eH*ZC=O')5 V~9 <U3r,>%XRqb kNnJ)*s&4Od@xb =.4sO0EMt4&E!i %OadWdR<^`U 1vzpsꗣ;*(z~cd4 6Ef9>EU΀!4c*@{C& }Leuw vj;g>EfvED ʣEeYefFgރ5Ӷ9^>23Rߵ'H{Ͱ6w sgkYiꗭks(/& Zh@y`s$r6~\ aS/u64/ ; L;2KZ1<s:Ús̀4|U ndb+9ތղQF~F6T3' K 7ֻ063Nl-LOcŷGt[zFx5_rU闃6wʽ v Dzю)S_jI.5@x 蛴 u T"w(aأr+ө:.9lҰ$k|֭KW\vKmiLcxe5ZEMK"F$(\J׊.flJ!0Y-ǔFa͘ `ZP2?.$ν InK*.dXη~(P4Xp&؉o4|Mv:ks(r M*~/c0UH Eax] E֫Q{pՄ5H O}@vKoR7Fv%I&@5>_ nל1^5*k9f dx+E^N¯ATIzvU?R}= tl/yNszMrיub ؔ4}q#|8!'NaAF܉r^{6-|:Iޠa f{8;$j KRo/US<-pSw-[q8zKPblө`̳җKq~q3KAX&*ع&lw6-qy~_~8fblBԿùw-nXݬBp^/0bHA82|)[!j[0isot{o{aAF3Ho2C&ur\Xp2+-!-J1iYii,%y!u͜~=;EO*$߷}H1lĘ$'nD: 􆼹c|e]-u! ^r\6ܪmyPdz܏yX ֶ*PgOkm\tC+1VY;מ6( 3u7UsbmVGS=',.V!DV+T^ȕr\WzOAXW8fY.~|@Kp*jR4;65b{߹oD},ʽbk_EL )3P0+t,ByGÿch"K襜6}I`.5yZp![bCn9nPJIQ)p}#y9ir62ܩ#48IEt݋9;n2zFUᨔA 8s+6kȂb ^!͡c>g: etcȤ 6dwb/t`YK}ZsKjmA"Db9Q 7({vX3cGGH"lAf;"*a}y!]3'*0vCo5?hr]ZYBUG'䴭 !!NcWY>t/y- F"(z)p^ת_Tl#Z :kTfLڑӪ[DٶL#|UFylBpj*R'+%BJ^6Dp~"0"rqbxBȿg\cwQ0, @ AD#܁=2~7|-=+_9{Ni͆Q$'{8fayȲjj-‹@;{|m^džxC$'*/pcxvhٿRTxW[hN -m ]o]{-RˮPşJ}}KgF=0l X~mNm#oբtAq\=п2 L:WlJCM7war's92МIP≮dK.5x&,!AI[ Y]n tL \(,E"rZ6 #BU3&&7lp</`Hz#hw%d^l=>1j#j!#}b0Q9x'iј0(=fX<7v.UD bȩ\ޚAp' N(ej@?X݂兡ե}4u:1Uc(E@1$6W͗W b#7}ś@AW8T\|}%jnL\ @)U>Υ=zncQ\Y&[]'\EEMM fDX'qKU-(׋_nHy PLYBh%6Io,5+{9i0e +\U (L3˺-Wztlz>]b#ף0-ݟXЪg_Y{ BpaEyi<>Z /TƦ /0iǝuHjp |%oM1_D%p~1 A8H#oq-j ܙuC4)ix&f@vWA;yYh;JTuyJ(Nz'=dtݤ؞^'t+;koyt5c+ױ~ 1AMy.R[^mEJF>L!ǜGVq*"O @J%R)#OiC\]˗˪9ň3 \gXj^)PX} k켔.KZNs$2iV[K."qgI\"vl}> Dæq~hrA: 0ga<ocs:ٓ!&5nrEK^V׬ShMxXH,\=%G{ʞdU"BSc }{Nwn\ww'li8;@<㖔}O睞Ezk-5kIσBVYI'GnPi}ंD!yΚ@;:@j\J݊9c1ݷFT:-D[i%G]C&t!ob=ǥugUS\?gz%6K&]^&oz xԚ(˧SZ.mOe.ǹPb8a!Wi+ 6O@swɣŊ&21_I=KNBݙ=1AlFq=EZDy&}[*W:=qg"<Ov#Oкm0!Ӓly<(yՎLF,_?p*[*ИsVf̞- &b9$(Ե}o"Q͢k& ZT CY#u{W*~0ݚk*g+ݣͱcj& &khteK{ ~A`|,_MܭQr~R<4*^2#}ݢܣ3+ZX=ZTcO.(M,5yҀp?['1r8Qa{K@&w2Yi9@ nYg jdG*.ń(Ӷ_Fp \7IdP`"J>:a\'nw[ ܃? NBkˤ`%{M?P?S6pzf7)ps Tk3TB.{ W>G;ڒA]< [Θ~ SMKAXX"1xG\K$.e$L*1żsJpE_N"[lmw#oVOK,ɵDOoTlK3/, . 958$Sf0l]nS&枱U%O WmA^YhF4ɉHY ~0,[a(^Qմ# ݌1旃Zg |o3%_, 42MQs R[h۲lA~bZq&L^p0f$EGד߫q^!M>Ք1/Y/һgCF6 B|Ϟ*N!ݮ@$!&B{ p^W-+ L;;CE՚3P=m`ʮZσ@3&A4mwv lU*d.YaѕX|IJx$ po knryPyf8HTy>$7iB3&$4_sRP%$ge彩x$jVDt|{3b\Iž 4LvXe/N Y(yz24wsIDiة$GH6`(;|P}}|p(su 㟐Lm,Rbly# _U@ozְ gpl 0n5C$ $1a(IGh`i P>enF~KlAg颚Oq@ +}mnFcql y_4HdYX~Зn2aaIa 1Jo|]yPeHFф # P$w>%QvCkĔ̳Eq\jBn*Qߛ~"DUrRܱrk ć8%$t`93Z W (~q5SrM'ߟ[kɵFҽ$A :ʲM=ObcYOճ4R۲☚DŽLƯPJ\-J:]_̭pۼ9UF.ZBVCʣՅuoRW ebO}^Du'Ec 0ټmiD2@'Zl Jt@vTOduJƞGP !rbcF^&9JA>/UĢ|6<'xZ)qC@^QՕ(bܪ?B0/S׾ %{e.P-wAhRE;Ər`kuT*/ {}1B*aԭZ K(x|VGr'{7Ň{.< :|J,5+_Q FŞ_.Z(ՆS= }cDMKWDv= \8\-3{:JbS2\*ʴْ!`&;0B|8ʭs5dgs)=N?nOOmPbs-i۩ƀ5-,f BU9*b{S}0@P]!8=]$ endstream endobj 100 0 obj 188224 endobj 101 0 obj << /Length 102 0 R /Filter /FlateDecode >> stream vPpBBn &#+̷{u͜qp/NY}| kZ {ILҤ4DOVb~2wMZsOW;oP7dhĂ)|R D}?*3:È.c0b/wY" l$ebtӺGnt"U==yQ&M{(+.rOʍ\~^bbvbWY{x gI6PiA !9i@nƄ5C|3~o; L-=l BL~,Y[Lϼ)O "Lt@LC8vUOf11[9?h%Kb_WH%"EC"E氢SfZ݋X B,y|ŀ>TdeuG]9bK9P2eeCD˙dK"fQk !%Ҍ&28,%ߠI*l3 aEB!2 rȐY?-aB^G6(5Ow%KOfW7uF65Bډȕ=xDcM)v/vJ|Ј(چC;./2UHHl 4A,fiޥ&ں!㾼QQdS endstream endobj 102 0 obj 1216 endobj 103 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 101 0 R >> endobj 104 0 obj << /Length 105 0 R /Filter /FlateDecode >> stream V/-Xt=>۠7Ap0j SQуh;G§tb[mO /IYà,sX}:[3Ez:cN J%w>V)~r5k4:Yxd%xaw._Pv2BId-`֑\-ZN>]nʲTGә%Yi_"K`~eADߕD(k\(RHwex^fa5U<-P _jI~Qgo8uVw$MA:\$ >"-pR bY "̇?'\pդ2vݏ MV S 8dz=! #q/7$օ RZ^9#.2SƝJ ] sT-}P|յÂZ~8AP #bnXzP:-9l?}/y0-7oqX?Ub,]$49guۮ賊l_K$/L]pp$ڡ"> f{πHUp)`@E =Hzc' ޑx26IK Y7SwȌqdDX+iC<[B^vϵO*#~C٩O)ar(+m^:LϩSF0izxCv4k!Ք#6 Kd7 % 1rjcp!glTATƛx󵻘>0 ܂nm ,Ⴝz*P VV~O`(B-Cf^]底Wk2ph&#IO-e~0;>;6PzwP0Z̼2M~믍^`^g 2'5ϽaM!0m*#߈?KGȹtg`ոTFƥ>)PƓ[tma:B9Hq!H M.FYdb'IQNu+N.`B'g[[O@sˣWK@`]XeOmZ+:e]x̍Qe$q7ܞ`?>`Mu+-썐| ꍭk5}^T?nϺ6,TWƐ@/ AQL^4y=1̾I(Q@)эGAPs~Y݀"{ofI'_*G ʬWu+eWЌL,O~q\|>`|kYxFG"TZ(\#F*DnBŸYoP03=v2"s򸑈xYwMyR7TU.+[[/QvAf\aL 1ObO P+ *\a 3dK1%=_QeIQP;4~&7zK(]Nad)')t2]>ٝphP/j K/t@h4 lYywpo.+w8du0 4:$Uה XʞI0*X?v7u7>Ňut^|UoZsI' KP*۷J?OB*#3 b뚯d>_ÀYo[FTc\8":#DB ڽ !݂+k:l~28ӓ#6#$k`߅9Q+>C Q-Hױ,[+rl b#@;/< 6J%vBok+BeӪ1kFMW/L3*()!=IADbsIT|NPF5}X`\U%;Cdn>Ԣys,Yឋvt)8\Vn̻-, qn#5$: mಠmu [V*L~}K!XwwW7Q]㭣cGczyCZjZ|+![lV Y4<@vijZGI7sȯf$Sex Y0OֺũlRhj dYqD2i;]ބ$>Ξ`剣ETpz+B&Le.,! `(',' C/OlvZWY+Sϛ Ue?ŊH^bmnW>8ь@Ks;˳;?iJv~ߺBw唤;#HĨ\+Mw 7n)bkcxmmnvb/AzαHQ2Ro-ʐb4,kPVj,_*^] W~^`O UfƳ(Hk"h*2S\&śrYqa>Kg*Q[E_>.nz.kfUG1qtPofJ3=-a54k=ͻaJN0{S=ׯӽ};u+o/a WS:Ks^֠? tҭ!Rj8,W?;vpi,m6y-O.r>=.Nvk endstream endobj 105 0 obj 2880 endobj 106 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 104 0 R >> endobj 107 0 obj << /Length 108 0 R /Filter /FlateDecode >> stream `rMo8GRC+Pi#EbJAcXjWeA/z#yf0h Bօ>Sxʹb ܒ׷Q[ms6(p"MOY_2k+VS>XCbऔ)ܹ=WVoz]"1YL;sy)4?v2gTxlj)1dQ5 %؟hN;Wul!0J֮ޜPej2U}T$iBoxr_bHPCG~Q>,? ]`|[:6 _c=t /"QJNWqI#496?5ƫG23sDm慆vA{Se- c1I"#*]KJ:c\Q|4kc7ZcMm kQG.=}yU]aM}x{^ʮb1À9q YDΡ`{]]"/輿&㯻Q YVf 2OXNpg͒yV+oz| +3Z]$Bf=V<>92!iJ'7j[~W<$([JXXb.bk6`!p ڱ PPAwv:|ZL:?sd#ް>W6*xc,T˞JWcOA7! Vu3Dgx0x.8g Pٮe"WNH~ZIJ s2E1S8A p|cq|ۡgaXu&fP a.ue#9YR-c{vG(%2d_G9QCHuἝ9+,1R!s!.GzvpdϳRA]###e,(z=: N2@=m鏒ZD7?pٚߢ@!cD짪 WZ鉋YJ'@f=v6qaNF6|_iE@:U*li(\*,D-L o'>XU?Ppy*вlU\E+ײi3'mehKeH*j|9{P[Ύ,m> endobj 110 0 obj << /Length 111 0 R /Filter /FlateDecode >> stream Ϩo$6ҰqY}YsAS'o=+w)%hi*Rgg^<.;\x}*!irSwW1S*Cë2/4̟)8#E-HpZh{tYߣP&$Ux N*ט}X ڧx_em\iSX𷢮0Ֆq o:sm܇T-{癫z]_2--CK]1+kf'-MrP&)5F(}U*Ct~Ɠ%{__rjRrUku!ʥ*qg\ =TMqvCAvXȄ%ub~+.23Z\- J淿2!JhhFfSㄐ)E9NG5 5sЁ̋*(9ӕ)\p)m ]k)~ R$|R{ 飸L?O߸chlx_@HӎGLꈔ <^<$ZChhǘy+sWCVUaw Q\n5aygKYaA4e&gힺG&>{w/,_>"r=$s/)pTkc.FNͤ*jmxnd9 ޖs?Tt}edќS5q߭">`Nd^`\U ?o L:R_ZW?d+m#񎧈VSda YDI;-179Q67Po[ZObrfJzA;ţy72VZo1mXaG<Ǟeʃv̘i& Pa5#jvkQ}v endstream endobj 111 0 obj 2416 endobj 112 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 110 0 R >> endobj 54 0 obj << /Type /Action /S /GoTo /D [113 0 R /XYZ 61.192 609.431 null] >> endobj 114 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 597.672 97.25 607.99 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 55 0 R /H /I >> endobj 115 0 obj << /Type /Action /S /GoTo /D [93 0 R /XYZ 56.692 771.023 null] >> endobj 116 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 552.072 97.25 562.39 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 115 0 R /H /I >> endobj 117 0 obj << /Type /Action /S /GoTo /D [106 0 R /XYZ 56.692 614.896 null] >> endobj 118 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 453.672 97.25 463.99 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 117 0 R /H /I >> endobj 119 0 obj << /Type /Action /S /GoTo /D [112 0 R /XYZ 56.692 683.15 null] >> endobj 120 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 408.072 97.25 418.39 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 119 0 R /H /I >> endobj 121 0 obj << /Length 122 0 R /Filter /FlateDecode >> stream e4J~-m ^qE~]j ԈCzS1`0a\ `YMgwq!=IȰ!F}3K4 frr͖3| *rm GA^vsrIۇ.V7.YCA`$BHvIe{mP@ 3/t悧@ꢳ4kŰ/B:q:Nv~˻ eY.R:bMØ/Bt%8pt]0 ?LݶZv~ϠM+K# E,ND J\i\dd ȷ Xr@(1هRɩ6 O^JP}'f;g|IW'{6Im4Ķ\'R{aO^\otTwkoTL ' B̊R<`5Ti{zLn pd骲PK0Zvǹ6Ik(+C%oWjf>mTpEנX9I14syMMd?nimD<3!7] |^"ǣC* s`K^K 5F`7GL3۩L%*es`Y #B0cX @ZSEL”Ֆ;JрgHeeǰҶŒ$pc0zZU6,}緻ϣ病 tX=mIDiX77G5}!!ϳ 30%4@AEp[sၸ(!֫gф=ǝ ~tF*f[q,3Ӽ;fF'9_KdI ۵qxrzP_KmG,Ӎ]-Rq"_$B^xTwwDvU+}"&1aDmL/ޙ3hgz.v.Wc:W&.a'O)=ǢZmT62E9ŷə,49MEl+9҇jN A 0> N0 _aݽ5|Ė_f dچ1NغScJԁ1J!]SV\ԧ XJD}О$R@̚ĺo/pG9OȘmPggKU(8=HI2]hDZI+~nhoL㩕Z$DʉYqHj͜g- ]7_M5ja"b6,7X`\j]b5If4j̢4߹ٳҳ^#kie+\ՖfwF)`./ K=OfiJx31꼜k@+ rf%꾂Gb2S*Ucb}#>﬽ LQgYV3USiXi:USlطĞ:qڻ9'닫 5)JQL3=`w.(.uq7g}|H‹ 砭ڔq+Y )Ǯ CfPGYHܭN$JOA,:Hk4jIWs-6ƫ5/][Y̠x?#z>ݥszbv-I^ftA2U m1} 5BL*2'ɤ-؜o wБkO+]pi:=3u46?0W^wcy5D2UEǍ5$`ti6ޛ"5˶ !=0ozxf1 '|QI@;~#&i_)ÙǔJHIeG캻?N Dce6gy-S! nFnn=amnVfrIp0U`ktlj%\(J_W҅3h;&hEW"g^~9{D[9a IKwh~LxlhQJ} Z8ߤ˼n 0w ^9E+z 'R;ܼ2ݘƂ!28#)v-IAwyL%pԻnqo!ΐ FIЮ(g²{-CXxISn{IHuhn@Bf?jڝU+xH3^ۼc]J}lu_. .&IPyϱ7@Zva-C灶28M* N=Su.~'S>k"$W^Utd?!&+fJ-vM{~;~+b-ғ31%r`~}365qh.܁Ca~,7'sxl x endstream endobj 122 0 obj 2704 endobj 123 0 obj [ 114 0 R 116 0 R 118 0 R 120 0 R ] endobj 113 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 123 0 R /Contents 121 0 R >> endobj 124 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 755.764 97.25 766.082 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 115 0 R /H /I >> endobj 125 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 710.164 97.25 720.482 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 117 0 R /H /I >> endobj 126 0 obj << /Type /Annot /Subtype /Link /Rect [ 61.192 690.964 97.25 701.282 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 119 0 R /H /I >> endobj 127 0 obj << /Length 128 0 R /Filter /FlateDecode >> stream {:ٽc2ګ8qnLSmoY;%z3&lf%0_47.>FO"KGJSK&{FP- '&2y,7^aΓFgV_ 6Hmg L;ZGP"tT֙ƺOmJ_gIy˜a.~` Cݲ݄V*X6ЫzxzmшxE~b@7'NlBLțLNC\zsR,Ayr%tey` )ICxġ!'ޙ="ց{{xSlxA*qo0ϰ7ыyf0x>ц77>d&JnrD^ݎMo*-.*=dB ФynO\^3)j_:6QVTTS "{KNzaXd4🵧.<5 q 4,FK;9 \%m  7mAB|^izlgc  ^ ۳Zyl2>t"(O{]]gW`;9zBEERVig8cz[}Yt;.W!:htUŃ[/ͯ77Z3pۏnuT4"vE8*җ4wˀ+ 7+C]K{ЖppjM4N2'g+s-߄")C;Lkҷ"pwk#n;ЍW g+!jP؊+D4:T dh8KHÛ*5ʜ1N!!Db #,M$ /B Q{>}f<k߷6`Ps.t#G`sc*(YC7ʞF׭C$Zl,kY[0B;š1>AuPZV}rúF7h1VtOUQpa%or/)p+R IU^ޕ@HC7{VƟ@3}jL뚾2֍^Mzs:c'#ocV endstream endobj 128 0 obj 1488 endobj 129 0 obj [ 124 0 R 125 0 R 126 0 R ] endobj 130 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Annots 129 0 R /Contents 127 0 R >> endobj 131 0 obj << /Length 132 0 R /Filter /FlateDecode >> stream 2th8e&ȯqf./.]>u$U%Q|C7lbN:~NA.;\&}a[VO#!$8-#${tp6fd Ֆ;$6%!\UOqÏct_lY1?:Q]rNukΤI`,#&W.ʒ-6 EONjab^[=oYR OEj#[ HIf77u™wD~QK*`Hq,-(L|R$2>Bͦ|1O=Ϊ<:TE ѠzQH0 ")\µڌ&uHW>%ζz%qGdR-ܴ.jwyb$Ymml"y>1!;mu1خ2qqoUZ4k-}gX_i8W=H.KRDW1NA0dp71ֵ^ܡ/|ڽF={ui|N\jdB@,?g P~LFM~C+AґN脛]Jh-;a=}¯)'Zsa.fw%|- Hʯ/0kɢB OU7y2'A3ڕ͢0اw fr/=߂@->G)w-%H7gN"2oI\ZC?\q&V_mâbޥ{C'}l?KW^}[H@9rBIS Z7xivr>r{l.>tPv:Pop h[)j.5$R EKX'G'<ϻkL"HtKSt6nڂ5> endstream endobj 132 0 obj 1760 endobj 133 0 obj << /Resources 16 0 R /Type /Page /MediaBox [0 0 595.275 841.889] /CropBox [0 0 595.275 841.889] /BleedBox [0 0 595.275 841.889] /TrimBox [0 0 595.275 841.889] /Parent 17 0 R /Contents 131 0 R >> endobj 134 0 obj << /Length 135 0 R /Filter /FlateDecode >> stream m8в˷:_kZSD˴>##Y(Fg.9N}NGI`\ .BG:y2POcvz濚yzOFb7 fB5S;CToI*lKsg*CLy6$1,2#?q;|cw•* ۧZb?U(vUt(R|'O)I:ѓSCےu7jc-txS╒URdJ#azIJeP@[,ۻD^'I7maȡn]I:BVŮtqW8KUs^^>L-M7R|0~ͤMb /^r4:? t½jnzIu-oO=4= -MJmKLkvDXj!Lgi%vlM;W։18$¯b=(D >C)^Hֻ3DgXbwWCU܎d9-eך7- ,L xe,u\5W/V_\O@)voKa] Ȳ,7:6񰳝&='ElC{9ȝb2|M?y=#!CZfX7R҆P`qdTc#(r[Erg=.8jmfxiR+\oRb!r)lCjΟݢlDz*ٽk?qmix pW.7 m th"hu$'#>e:8L+߹tx d3xVZu*[~pcd^wLޟ5VMvܩO^.>+[C }SMoYzNCvdwPk*2 MB`R#v4E6(^6)XٿѤH UyeF8! eXCߧ-.>X:xe5.ܟK;%F(|CěgBWudG> endobj 137 0 obj << /Length 138 0 R /Filter /FlateDecode >> stream `a0 c4N1SNSFKpP6."j^LՙԨKȡ=}Y Uvە oqLMnB^fiV d'cLty]v/&{Kr.S;@{L/`s|U6C޷. iчujg\zuer{IJh&t츛-r_FyY?ۆg؇D4[ 옖l9x?\'>2Jķ^К$gdkS|0Q'R>8RFC˽H 9jcv~̮A}BxsGo{isXggC~_~ *,bYIe^cv 9ۀ]7|ls)E'ezBkIQ%,~+WȆ>d" [y? HeZJ:;{ua^(C*:.- ыH-ؕW)SG}IM șʑjc%So/y 7I枓yzHb8jԧbՆ?JY%+g-i]6IYs\hFrùCBUa-)m(Z/#yH'9P#Cx~'0T;J-ND,EЫ:ZӡiQ?eZd72(=<G8jצ8QɜΤO(s06=!ۡ2>eC'sp ȕh7S =іXO 5H/T S#= ;fBꈴd_Zf}x'wO{-oZ*$ -P=$aSo~Nw0 ^`~'d[pv# m#,dvxpq;(M  c`@j4RD]c+זR4a>y#l:S/9E!@9U:wݛݘ^2RS\/:gS}JӴ>l}?D?V))'gVk<21')n^*J6 -KXvd!~r k`>yX.;""hȵ;i8QsLNs3`[(]^QH` 7Ŏ+ ҵtqjm=g=T2F׆nZ J[yI[>OC-RB7Za)m 6QC'ṿ2rpl9t#aGI';jR6dV1ec|*IIä ]nچ\u*W)}dVF8EUZO`qzaN&_QicYEL014ͩ2H6>)ZH\Ā0Ϧĥ.BL[,sJCy(2SGǪ-#w:en0U)Kg@([5aKD6];ۆZ^zYoE o@ҝvkoγu|{$QJE:}ieEe֙hm+tkܐpϷ~61S7ဋ!g,?uc&Vh ?IQT>𭁥e`3x'֔YΨ^x]I`2E^^xJefegC/Zg^ ]̄krn @]g{}F~ȺC?Y[舐ẘZPL"OIG4pt !wb3#"OHqz VM(ᑱ߆> endobj 140 0 obj << /Length 141 0 R /Filter /FlateDecode >> stream h}⠲tP@!HƖI~{0CFhzP[)E]2Рo.6<"WwbtAzH XvKҶ(ev!Z⤺YzÐ2tImT^kK*,HI5y'%' 9bG:`S_㎮DDV|$` C  55$ c`! "@@p)9y*g$JbSOFZj9?r-[]XE(V#p_dn(W -e{ oQu\TK1D-3uPoa0AIwkav0ԂLftl{gbȭosc9 F E)7+Q6ڒ%|ș|`bcW6XsvhcIk y:0=vxFj\>zikn |LL4u#i*_V6~3(^[m` 5zN !giXWLe7V^ _Us5ћM3#宠wt ÆP7vA拸}5Au4:̥Ԑ&vm-v$h\k&rbB!fJD yp0 Lclٜv Ê2Z)OA q*0%ɫBQÄZ;pqO݋p8UH ZFV>K J-lkG-tlݣB%G:La0@:'-T-7.;2Q{la%=;9he"'Jd̵&w0+t&.BΗEl/S X9<΃)')tUܿ}"vmog-t 0'bZÊƟ{*F=)ohl3G'. ]ibNQ?<6hYa .p}pRE` Cbq~F_zO=sut;!ePC8[W髱gB>UzjoaRoKm2dN\! yLv> endobj 143 0 obj << /Type /Action /S /GoTo /D [23 0 R /XYZ 56.692 771.023 null] >> endobj 144 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 706.484 61.708 716.802 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 143 0 R /H /I >> endobj 145 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 706.484 209.812 716.802 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 143 0 R /H /I >> endobj 146 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 706.484 535.492 716.802 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 143 0 R /H /I >> endobj 147 0 obj << /Type /Action /S /GoTo /D [23 0 R /XYZ 56.692 734.174 null] >> endobj 148 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 689.033 69.232 699.351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 147 0 R /H /I >> endobj 149 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 689.033 171.268 699.351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 147 0 R /H /I >> endobj 150 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 689.033 535.492 699.351 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 147 0 R /H /I >> endobj 151 0 obj << /Type /Action /S /GoTo /D [23 0 R /XYZ 56.692 612.289 null] >> endobj 152 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 671.582 69.232 681.9 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 151 0 R /H /I >> endobj 153 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 671.582 181.267 681.9 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 151 0 R /H /I >> endobj 154 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 671.582 535.492 681.9 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 151 0 R /H /I >> endobj 155 0 obj << /Type /Action /S /GoTo /D [23 0 R /XYZ 56.692 406.783 null] >> endobj 156 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 654.131 69.232 664.449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 155 0 R /H /I >> endobj 157 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 654.131 194.808 664.449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 155 0 R /H /I >> endobj 158 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 654.131 535.492 664.449 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 155 0 R /H /I >> endobj 159 0 obj << /Type /Action /S /GoTo /D [23 0 R /XYZ 56.692 290.567 null] >> endobj 160 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 636.68 69.232 646.998 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 159 0 R /H /I >> endobj 161 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 636.68 158.013 646.998 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 159 0 R /H /I >> endobj 162 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 636.68 535.492 646.998 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 159 0 R /H /I >> endobj 163 0 obj << /Type /Action /S /GoTo /D [23 0 R /XYZ 56.692 208.365 null] >> endobj 164 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 619.229 69.232 629.547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 163 0 R /H /I >> endobj 165 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 619.229 207.832 629.547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 163 0 R /H /I >> endobj 166 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 619.229 535.492 629.547 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 163 0 R /H /I >> endobj 167 0 obj << /Type /Action /S /GoTo /D [113 0 R /XYZ 56.692 660.476 null] >> endobj 168 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 601.778 69.232 612.096 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 167 0 R /H /I >> endobj 169 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 601.778 209.328 612.096 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 167 0 R /H /I >> endobj 170 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 601.778 535.492 612.096 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 167 0 R /H /I >> endobj 171 0 obj << /Type /Action /S /GoTo /D [60 0 R /XYZ 56.692 771.023 null] >> endobj 172 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 584.327 76.756 594.645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 171 0 R /H /I >> endobj 173 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 584.327 222.88 594.645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 171 0 R /H /I >> endobj 174 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 584.327 535.492 594.645 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 171 0 R /H /I >> endobj 175 0 obj << /Type /Action /S /GoTo /D [60 0 R /XYZ 56.692 459.332 null] >> endobj 176 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 566.876 76.756 577.194 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 175 0 R /H /I >> endobj 177 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 566.876 192.322 577.194 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 175 0 R /H /I >> endobj 178 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 566.876 535.492 577.194 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 175 0 R /H /I >> endobj 179 0 obj << /Type /Action /S /GoTo /D [60 0 R /XYZ 56.692 147.641 null] >> endobj 180 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 549.425 69.232 559.743 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 179 0 R /H /I >> endobj 181 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 549.425 250.435 559.743 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 179 0 R /H /I >> endobj 182 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 549.425 535.492 559.743 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 179 0 R /H /I >> endobj 183 0 obj << /Type /Action /S /GoTo /D [26 0 R /XYZ 56.692 771.023 null] >> endobj 184 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 520.636 61.708 530.954 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 183 0 R /H /I >> endobj 185 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 520.636 180.189 530.954 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 183 0 R /H /I >> endobj 186 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 520.636 535.492 530.954 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 183 0 R /H /I >> endobj 187 0 obj << /Type /Action /S /GoTo /D [26 0 R /XYZ 56.692 734.174 null] >> endobj 188 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 503.185 69.232 513.503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 187 0 R /H /I >> endobj 189 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 503.185 159.234 513.503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 187 0 R /H /I >> endobj 190 0 obj << /Type /Annot /Subtype /Link /Rect [ 530.476 503.185 535.492 513.503 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 187 0 R /H /I >> endobj 191 0 obj << /Type /Action /S /GoTo /D [39 0 R /XYZ 56.692 771.023 null] >> endobj 192 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 485.734 69.232 496.052 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 191 0 R /H /I >> endobj 193 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 485.734 197.294 496.052 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 191 0 R /H /I >> endobj 194 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 485.734 535.492 496.052 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 191 0 R /H /I >> endobj 195 0 obj << /Type /Action /S /GoTo /D [49 0 R /XYZ 56.692 771.023 null] >> endobj 196 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 456.945 61.708 467.263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 195 0 R /H /I >> endobj 197 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 456.945 215.785 467.263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 195 0 R /H /I >> endobj 198 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 456.945 535.492 467.263 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 195 0 R /H /I >> endobj 199 0 obj << /Type /Action /S /GoTo /D [52 0 R /XYZ 56.692 771.023 null] >> endobj 200 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 428.156 61.708 438.474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 199 0 R /H /I >> endobj 201 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 428.156 161.676 438.474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 199 0 R /H /I >> endobj 202 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 428.156 535.492 438.474 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 199 0 R /H /I >> endobj 203 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 410.705 69.232 421.023 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 55 0 R /H /I >> endobj 204 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 410.705 480.291 421.023 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 55 0 R /H /I >> endobj 205 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 410.705 535.492 421.023 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 55 0 R /H /I >> endobj 206 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 393.254 69.232 403.572 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 115 0 R /H /I >> endobj 207 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 393.254 370.654 403.572 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 115 0 R /H /I >> endobj 208 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 393.254 535.492 403.572 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 115 0 R /H /I >> endobj 209 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 375.803 69.232 386.121 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 117 0 R /H /I >> endobj 210 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 375.803 381.214 386.121 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 117 0 R /H /I >> endobj 211 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 375.803 535.492 386.121 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 117 0 R /H /I >> endobj 212 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 358.352 69.232 368.67 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 119 0 R /H /I >> endobj 213 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 358.352 382.204 368.67 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 119 0 R /H /I >> endobj 214 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 358.352 535.492 368.67 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 119 0 R /H /I >> endobj 215 0 obj << /Type /Action /S /GoTo /D [136 0 R /XYZ 56.692 771.023 null] >> endobj 216 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 329.563 61.708 339.881 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 215 0 R /H /I >> endobj 217 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 329.563 177.021 339.881 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 215 0 R /H /I >> endobj 218 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 329.563 535.492 339.881 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 215 0 R /H /I >> endobj 219 0 obj << /Type /Action /S /GoTo /D [139 0 R /XYZ 56.692 771.023 null] >> endobj 220 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 300.774 61.708 311.092 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 219 0 R /H /I >> endobj 221 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 300.774 173.193 311.092 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 219 0 R /H /I >> endobj 222 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 300.774 535.492 311.092 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 219 0 R /H /I >> endobj 223 0 obj << /Type /Action /S /GoTo /D [142 0 R /XYZ 56.692 771.023 null] >> endobj 224 0 obj << /Type /Annot /Subtype /Link /Rect [ 56.692 271.985 105.257 282.303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 223 0 R /H /I >> endobj 225 0 obj << /Type /Annot /Subtype /Link /Rect [ 124.144 271.985 179.067 282.303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 223 0 R /H /I >> endobj 226 0 obj << /Type /Annot /Subtype /Link /Rect [ 525.46 271.985 535.492 282.303 ] /C [ 0 0 0 ] /Border [ 0 0 0 ] /A 223 0 R /H /I >> endobj 227 0 obj << /Length 228 0 R /Filter /FlateDecode >> stream z!LAsgQd ks69i9+7 Y\mK4l)x?J|iD7')]V-x+K:ؖԏLFO E3DӎBh4/5Nݼ4W*:rٻRjF5ץ_U>JspƜ Wâ_LHHNX,e_!z$19P#QO1.=Hgol]*ֹj ԡgmo|B?0x|VEJH#pR LkZ.#zA}Hzg! (^"Jf̮Fn0AҬޖ\ht{!kOfj&;@mMÛ;G$7tCӤjLNy4> endobj 231 0 obj << /Type /FontDescriptor /FontName /EAAAAA+OpenSans /FontBBox [-549 -270 1204 1047] /Flags 33 /CapHeight 713 /Ascent 1047 /Descent -270 /ItalicAngle 0 /StemV 0 /MissingWidth 500 /FontFile2 232 0 R /CIDSet 233 0 R >> endobj 232 0 obj << /Length1 11684 /Length 234 0 R /Filter /FlateDecode >> stream brN`~/ce#?D6_3Mv_,~;fcް0n*Ǥ77W+SF0!ZR &H0 >-dM}hw:w@--? nqG[#ZE6τil` O[8l U݁5/ Kc{s [n Hy:eZ( ǐ$4`(jܣ1kZ7{'oT%gṽYE<O+싺'(CN9P2ٳ+G9~qs pk1^GKO!{B:Tp΀̱ࠀ1r7trphv VK22-{ +4 vGQ.<^9˜kDZ%Ȫ=s4ۀ #^=wvn/ 3d=FJp6bR祩_џpkbq/DV>*2&BK`W&a"?NQ[8A u8p \>d5e6ٱl^K6{}y P+YŰ=ճjz2r NJG7tt5 bR[]$=`i\)Ym euJ&sA+wI`40C'zg z&(>a{6]?Z̋5e}>40רgDI@vNm7>TQUϲ9l4]s=7tx~-.lGūQdߏsV} |ՐOI!i0n.}~8tboLo'|yE"^ OaG@ZNV=rq}ksȊRcG޻}ϥB!|넪l=RTWafS|>I5I.LCqc qJ8B1Cr)+y":=u].5'5(FZ]c=h[ EaR[$`?& 6xMۯxU): \lvx—&PX7Kp_2-j [:kW&뮪:-qs^"ľ(2>SAǖ'N(ԝ #$?9RfQX5~sտV d՝m\u/`l~Ǽ v0P!Qqk8(ܖ`|wr3dR;b-! ZtAf 3EI17sflPcBh6>"Tș,f[QSbW?k!TVޥZz}ݦھG'r#A׾Ms2)3}g{<]--k%?u5,w-.PCI܈_M(KhGc l%?#a-c``DY'r(&BX\*{x DIl]UҨv]M0ˑjlI4x(v_\,І< E1VF99lI|ɨ=m;4%v@N/B?< -{ 3dgnlToW< >x#g2ߛe}ɫ"-ih? ȖR5]SP|n5%ࢩ{v u`̈=Q袣i;GP(`4 {(\ (>^2_q s[=ś\‘3Dmm]Sc7tv8rUɹFoF_g;|e9F:s.?NyizT2u` Og^?J蝴e:-pm9Yezg\,d xove`z8g5 >Ua*C'%%P|6)5w84~^39 /%1C ) XHW溌=wv^ [/7BmkT`,d_MY@8&\u5O؍Pͻ?x{ 0i~U:c-T⬛'Gp zHP82RrOvme[ 4C&O$)d(uqvnbIQ:Zyḑo*Z/gXf]%Cw2c TqclHK%T0IJ(Fu}jmvƥ#Zrp=-@O-h}sWNuqЦ{I͓1E_θRM@Nb9bӆoz1 9'6T>XPDvL2€伯AFqyؚ*S*ŸgyԅUeԑD×Q+/<٨6'C{-o?."fb+:Y3"BDC]n"-Jbl ,9YO0Jг6j%ԨM8q,IVĶ3ln27NwXߊGDK-CR #~$MU g<ʹD7|eROF9>4ȫe?!z8k`*P(u,Gʠeьֵ h5^&ST] V~VF:X+FF`"ȷ݀yD{|bk=ĝe7~k,+4JhcQWNL'gH7E u;K6Q gɸ<o.R[iȼP.va>/N x/hqpCs4J0X 2gOWh/8@0qt)CR0t=!h.еXE#3\b'5qtFKzl-!TP"yK᫧9ma3WY'-$hܯ[rO*5*/G۪KhRVcN?r9fJ^ۮn!][%$ tyW]|8yNEIicE3޴pG_  { (T+>:pf:0g;S6+|Z/}Wvܶ$ZdP,wPs}h#5\$e| 7 ȜWXJyhL (P\b!3.,Zo[ưF1fŴFP00犿,R bq]R {Dm((z 0 $C*Ӛgk,#2J43ޥp#"3۠t+mZ=vȘx@K ՠli FJ^+0N[( E먏4#ynK3j%yUg9/4&raqDq"y 嫰wb@j/2Mr|Nsqdyv;m_jVtô d69(vmQN9_SX.VX8g1ɵ^QvU^&yg@<xuNm;yCqu!6ڵڣz ~RDN:Ǩjdo8>kcnd&{u%TzYi=|Sltψ_Ή_DE$A؈hAKeVze>%kNb9L,pc87y@Ҝڗ2T.=t'q2COP:~VmJܼкԶG~ם>μI)s0"T"_AbcZ["#Ϙ=CM1OlXk˸",Q;i7EO bP'\+4>Zi-uC=iQ$H)Yek3U:kA4S*,ć(mx&(m%K6n !GtRhZa#uI9jndI N'EO=. lM Ry:qpӨDO{iāty5mlCTc\(ciO{Y$9QZ<D h=6 Y}#k{ '7%g-6{̦;|ȋABFڡ}5OC $ߎ_*:`Λrc.4!Ww TR`dž,P +-QvuE(HIbKET=WPja+W0U2O黡&!tRV}z@N1zj2 qt `+UsQhe1E![޺iɢb"4Uzr~+ffLqX ˽+=yʟZ1q1/lz+ӥ G`Ȑߩjdybc="WA>n/onRUbxL'VX|`&Oiގ]_pi rܯ\Uyu_Et/]Wyheڛ?6(lGqO3Gb0$?Ga0vDQw k$yQA`ςW{rDCoS>T&:Ptr?|!AˇElk> stream i<x%E&sb͗-h__?aJ?H%H1"3E∓vf endstream endobj 235 0 obj 64 endobj 236 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAA+OpenSans /Encoding /Identity-H /ToUnicode 237 0 R /DescendantFonts [238 0 R] >> endobj 238 0 obj << /Type /Font /BaseFont /EAAAAA+OpenSans /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (ҧ\)q4l%A%+\(B) /Ordering (*:dH\nq18n%.@1D) /Supplement 0 >> /FontDescriptor 231 0 R /DW 0 /W [ 0 [600 259 321 602 561 613 353 408 556 252 604 553 477 618 612 753 519 278 930 630 612 476 729 613 595 252 612 338 571 556 523 500 548 503 266 571 777 524 571 252 571 571 632 613 571 516 547 571 902 591 467 571 571 1000 932 500 647 266 925 259 ] ] >> endobj 237 0 obj << /Length 239 0 R /Filter /FlateDecode >> stream 6O4!awA`cQI,#RIb#w=ƸFŦ2]K(&>: @ gla, [y6b\OwJIښTG)aZ0j-N5:`wlO#[֛m玍8:l9eyp 4my"K64&_hb֕'&K W5 o=Ad zCuoF:Qxqjsџ%֘Z=Mڛ.8J5yq) z|0EEg jXd'F9fn1u_Y|qkR b xk W:^=x[;*WUcRVgYUy+Um$񵖨 X-\JWC8b2M;3pSl榜]-)u9Dw;8AAzj` ^kx> endobj 241 0 obj << /Length1 20704 /Length 243 0 R /Filter /FlateDecode >> stream |Gb]j ~lwDAJ)mB-3kAyp'!nQho2Hu`B;g~{W\b_a}mM7$rq^}>{J޲%gT zEY\I (:g?ɐHx/OBWbxBsp{.}n hc diM} J&)fo];(**dHu.m_(.5?pɘ|`p(Gm"~!xY; ZZ NYP4`-v!1ؾ8gk["Iihk҆!8j!|Q]i8n#À2-Q{q |'anqBBK Q,c0R Z>k7oNvA $.7AΝ!Y2U-M|!+rNuy9fHDq kԇ Ըɷ N'DCwoK\*6Qo|9^G__Gr|s~sYVH}l?u\~P!o8ѰJ˶`G[pkܰea kMwxfq_GN m:7dIw[N;_Nk-,jJncH>d9dFdEmMv<*Ck9ǀ!kkYSI3}՝ڣ6T 5x*:q+X aI3lC`C}\P$' ophUv\߅Iaf82::$~܆{͵|J y Rge&*֑fįR$RTLD>f[%en5KWb94e9# %$m1@vXC9SKǾ qeVV|Ϋs[=]/> j]u"j _fsyd.EGxYi\DL5.=~{ )l6NŠ?Z<;B6 (HLH0MDe'ÎWվ JS/=ȍ/=kvU̪ڠ }[|jc*>j7; /-6Wԕ! 8$%L FfE?{6aH G20gmpoAb%:jf\iyZOuD)VflZNQh$5/v;hu'[dI Z>ڤq;PHnᩧZDỏdvӚvMt1JU?[z73qqzIk]SB=6%DP VLH@TS.՟ $H+"Ls,dbzC?T&ߊSgL&˜j^Jf!!vy;8Q$kfnx=ITeRW$/_x6^Th&M&ڕKNяQsVTΏC>_܈JMykwk=uX>1g,@'0եXaIa ~WSxxC,X7޴x\p/f](42P Pir w~P}89 6ydDvn Iµ^BF[~csᬩr+Jj=eo:b*]y6wPd x+u6|,{E΄$w"3LX:űG9 4)Wa. C %j`_"Ȧ}̈Mzv)|nkpҐSmuk Mn}PPr2H2!EeidiJLp YM1Ȅj<˶1;H$/ޞ!ep=B}|$*9H:֣-[A*Z;2V,X75^eVӋRa^и"ӆz_Ghxg ~g57__gZv\o#ud1TVs4J^h־Y ci-D) S6f!w|/ʿ\ Ww);XzAΜbhIRZ0I.[U&jMr:9vpۏ s!Yzu }Lzj{5dcY㜺 7W`ǤD,5<`喰M"_jX&|62Q!)&^Il♈͜O'r/]By + J-JV"Ơވac2mh1x^!dO fRNɩ ! s+il W UఌD>imr$^D` }MW$M@w%wbaV<8cB?y مF"CQjY (]ki!$p-SVJw.@9ѹ8yĻC8Mw3ל<|ț]5qKpsX7x虤16l5z쇚 Fj~COxOMF\.՚v㫻B360r"TtwAW~T"d{/ЊKդ@>־/h:RƨIOΞu (bWALFB<4 cLv܄OU|@17 {}[A1NtU1#Ca)!>H Pd| /; xWvEXi"U7'UxGdI94c\fܪ] 0j$X7ϋǠu LUln@8hp;@4Z4'7l3=h?3ܱg ^KHRT~VkR*s\8E kgG) m4,w%EM7 JQ=eJ/ބGO͈yiQ]^躨0LZ>qSn-1|gfqZ TfN^a9 GTz,9.GdHs;AL<)7M'Mٷg': iv 1 .‹;V-VfԨ`$]-2%&ƗmJթ+9}ϰAOSf\f rrR]ZrjQX6I7= Xh_NvPKU4:%0a޸E'8κ89>5":^^T o`mS|`ItAm-R{&uxq]Ik/OC<%'a `&?SXe Ƈrf'W(%fMJIkfxbӽ#ũ)k |>s\jv!7<. rN6lRUVR^V]R%CFҵ+p027'(I+Y_€/ 2b.~5& Qtj|g+[ tHʇM\,7&jҾցE^mПJ_kAC5䎳`OK6ۊM ?JbgUX(O?)^puiFuwWusGB@ji)aGjs6A\HkSZW$;BnYEyfoߝ19SK:S{e1v_`Yz MWKx=5+ȽC]2i`UF<9 ӰiYN--sa,W}yA:uG~$8>IK1p/:;3F``8mF2ސ. ަTD:Dtf狘֓PY2Ȗ1#'kPMTdEe)HVEl[7H@7vS4ӥ٘XmkJ-(\6RǠc=z%Yr:q[nU"i(tˠmnW5虈\v7j6ietST( (NS+ԥ[dgL"ڄHZ. +7ljE_ Z+ ^qMI#t` E|q _7Cf!~\w[&I~4 ('0 >RK0tWa3sJnbSJrdO OlUubWƈZܣ'jƾBP!Xgѧ,y/9Kt˻Db=YW(\s.r@ ǜ^Q_sjVk-.@TN6clף?_l@wk{ JwA8lC(X/_'薍L~=_Qկq24>o ;2B2z/F"a+C`ߗמ<; *h">EKsP/$jKGz~-o@9JXBZfSij&o4u+)HTGV[B$;.1Bq0Uyv#,]C%X͂rd`#l/fMesD)d؄IHEEh7l⽘-SmޯuЦYztB:ԋ Mhku}v?sY>3 &)Jc,o> 8u_%ъt?4Eє>Dwo$d;ZoAY=#:q0pve .+OdsYv2+j4 >(\E u˓\M#In$ ''S!I XLKvP6.]S&p,h*)<7v <( Txz08AH>޶UG`_oo}B6BNLP[Z%6?xpܗPhrh01]X.^~#zw~/-v7|B?+B$t-MGg\n-{Ǔ0/UhwEGɁRy1%+A79 y-zXB弐0g'㔁9p.o>kyTlwmNISrfQ eХi {K i͞-Ln[l)0%G܌My˿9.:ICBڂnT8F+ܪOjO&B2;ބfB3֨XB>bu_`PX'nv#iYH^ZjCի!zA@~&kw- jWfl"krDd]+dt 1!B(x?Wvo9){x&m:G;MG :~!gt&ai'=;/N#Vr`,/IzԶy]JjjIWLD; bs\,`')A#/| v˯ܝYdIaù4HɭtmMp^:Sa& fmņ4Dݥr`X_kPhބviEm>򦼍S K9opB7xa?xT5ۂU[$nqü&sRx {KQwœy^hrM ]@$uRV:ڀWZ8LLMuW%Ìg׉ pT@5XmG[nA3?ՖQU/ /AmYS0@qn;LcjD/\dl4܍׷V&{)HHqzA}(lYTɄZJuA4-F4nnmW@zbNyQ ;.#ytp=ϫ=IQl3{\ّ)`UΪ>~f[ .&@v4#pkl]CKT65%ai;AvD>osiձ0=|P ?Ulm9l6AT3&cOY猻fo襕E&<2Vٝ|@WkyfMd=P>=}12d4j$xKO"Fղ0S+^Qá 0|&(cQ t+ZN# }%\FF%羛xR 1y[ԜTQ#ʦѣ*(n҇oftc9-a%R"~cv>.GcKE]T(/}|x*Eѷ/%"\:A uoq wHK3s^ա-q{az[pK},|5_# ϽF8]cN!7gOipB&%_Y[ I@F:"I6 Q/{dЦ_ӸsseUR˴A$z!b"Ukͼp=s=;< ƪ 6vfOяWb\)醗 2~Phs+#p..vwihʒL@͉(t.>ƤR2k@}wGߪY穰*AAOU]qb!vw5w?i3-2oS 2I*4o' 1,4IV訝Y?Axz| xcCFsV7~(bC|um;A7{SIN@2n{X^6-0jMiwvI=bK r*c|gaNSſߒs|Ca&* +%Q[so;.A[/;xDCa7kG8/͠=qɕZƂlTXRߐ NVlkes$wCP2W# [fhMo”{UmwTɗI_ ,(ax2 ktGٶD>{<M++{IX%MhY-[TZhFZOKNRi}H4bLxY^T^_s(>KfH"N0u^GmW5 7l \# Dcr: ;ًRZ ) SʘH"UvLg;c]|(e_N%m}l(}kj6h?Em&BE%ǔY h'3Kd3jpG^)Ӥj/%[_ ǧ_ójg]B-Vfiz,?v FbV2*f4nQLeӂxAlw2 ܩ%r@dLoᮄUZ!JXy2[@p.%t*l1iV2akJ0Bx**-\9Щ"6B@vW?5P9\^\mBl3i(s-O ĿbPCyXub6v]BLcjv A,$2ga8Zh!M?~ƫ+Qt\Qc;G;֟O`6bk4 I7lA{^Xɥ"M_6/DAC [0?:+WUȯѧG-z)?,b$ǐDR3 &R|Q2S3o4";~[ؑf.7Rx\!7cOgFUބ>N-5)JZG"3 uy|,v#^TerŻ*\DS!#^QV6"4E/%HEg>!Iۗ %O季&e!%)j@ËjGKVe[O4QHN [pE #]xMS~1}qSCJZrH1ӖIꖾORq+:2Hn[ cWzVE,#GDfRLjgRצH&Pv"JRv%MD6%S*/{[)s&irXl0ʋBhӦsJpYnbd F8Uӊ v;|J jsI[|<4CQtJXWCM[uLEK|_TXY2: {RYL}~Yv# td̸_=3bOyIAx)ZI啞S$O^k4B2k{ ɟ~1Q5Y!¼De#$F^p!rόk*\=%k\fa݈V [4rW:O$\}v.QA[WgZPZ>ǃ'w P"hRPV9{)`DbdaKm޿|v,_p.6C`Wn'c}^/:P>,X-b—NF>旅TCGsED淙?)o!69$^6VcU!";R/2w L&LhdE 977{0﯁xF-A>u^=Kͱ^]<RWfXYʚIz熡B7/ S2Dtle8 6{&U9[ Md94hŠNMƵ-0Cn,^qO䢌Gˊ-|&NI{oAJmHA\'GouJKEfs-jk،w@7䯬*Q"@lkU }{({C q"閽zRS9K0e-П3_eW>|rM޲*;9o˟cfzуsyeX*a*ҩdE~ -D\k>:aʅGl|_0uԺ pQ8I+=jOR>m+'Euρ) -:t*{Q =ec`NsiMpOܤוU+hwݨūq=TpI-͝ohDk7Ou`b9?vY#}4 n鎆t>%{xB`!QC!K/,X̸_C G{S_3BJ#Ǟ*dSX3T1pۋUJ2 *Q#[T0['qJ3U)-y㒠/UIúj_ ;VM1@XuBN0nNIE.Ns©+Z!JUj{ŽiTJ؈ .MZk"oWD0)v Ijt`5!{v^}'lQ0}gp:V Ҕ434:K]`_ge ՝CnMp<zhnC%ekcibئh}[i}S(ot>CKf>f9A5?|.Ez".O>~$Fl脞񝨌aK}.JCM%0p ~T@ by݈ս- "o##)5(p`)FX%6}ceS:"[m'\h(ʦۻ:~'m^K hm/RK>jڿ('QXdh8jC [gg T]W;DPET@#<7[r)MxOfy̧- Oo&*ʮ^_$_O%W2 >?|ZTQ~#LQ?mF6zQ<7m:ߘ+L!RA7CPHYJn1nx!|,.Eo}FJƜJ\&ۋ=4G8Fu_xV}FF0ڍ{bʼn.owbNƼˉp Ozd }/+W":c˗xlHC0nC8R! ) 6C_G=҂?CΏ 31#H1o|+OC'rS5M>h@GSMˏn2b78ejAqqB^y (LDTHi/Փ:@*kObC7*Ѿ^VwZa Po7cߋ=ƭ;lB%N/IV)*V٧<* 7g2R/J^U2kL_"Czr{sBd1.VBT]!6vP; 1ġ滈7f*|].4=0K ݥW@U01z}+Hԏib{JW'ߕFkw0 REn։34?)W!ry:+2ꗩ*9Y#Y^pԘa^DtR(]!Ą5a[1Q̮UF€h"Cd"wur ]^ PnشzvE4E@pd040`ˬ{ Z 6h?yӰ(E1[ 'UQ)&U{ْLe6+&z];[aƘktWKɡc)m&h86dJQ=ƦsM*K+!"G`Vy/Tl`QzE||އJfgnGJȭn6JPBJmEYLZ=Ng"y~k+)Xj^D8},{yY}=`C_vsU`V; "s"Up}oǢs1D(^3[b3\.C'@}'tc.`r==xBnyPV=La*] r6&e,s}LlhbQCKc{VH'LC\ZyNu c,g%g~'-0$c$ǔ¤I׫%;qanJR7qDFcG" endstream endobj 243 0 obj 13536 endobj 242 0 obj << /Length 244 0 R /Filter /FlateDecode >> stream 3G_PEZ[׳ 68ERǥۇ,wl2 endstream endobj 244 0 obj 48 endobj 245 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAB+LiberationSansNarrow /Encoding /Identity-H /ToUnicode 246 0 R /DescendantFonts [247 0 R] >> endobj 247 0 obj << /Type /Font /BaseFont /EAAAAB+LiberationSansNarrow /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (٩X\rn-7ro\\m Ү) /Ordering (BDHN\fk\f¤㓄W[_\)) /Supplement 0 >> /FontDescriptor 240 0 R /DW 0 /W [ 0 [228 228 272 546 456 228 456 546 683 410 228 456 272 456 456 228 683 410 456 456 456 456 591 456 456 228 182 182 591 456 228 456 410 500 546 591 456 456 546 410 591 456 410 591 638 456 500 546 456 456 456 546 591 479 272 272 546 832 182 456 819 410 456 410 228 287 157 773 228 638 456 479 729 591 456 456 546 479 291 410 228 ] ] >> endobj 246 0 obj << /Length 248 0 R /Filter /FlateDecode >> stream - uV/L8u9%,@ɔfH^#Mw7uω[Pry~ CpW%Ҽa&ص@rwimLs'Q\$ \(ɛ}d,ip̰6:tFG8,JTW†* ̚bnFYۅJՠ:Hhf?{(Oi^*;Z;0Y |XEpef,yϞ8d&^SyʣzI `;R^@ ED@ fHl1nҫ]\c:JG3<4"&q)6VRdcxX" 9OC{6Ńs}zʥ=~* 86h,+טq?rN Y;WvD`\A_Uh-E)ջ#pSgMoťۄƀOS &ܯ C> endobj 250 0 obj << /Length1 7784 /Length 252 0 R /Filter /FlateDecode >> stream 'd}D!ʤ@_XLL8?\ fK : U^2nS'T%~,ΰ6@uՏ\S%yh8(&99O8OxlSFGPF4U7lkś][D9z7,_Y)f$pN^v73!k{ ݞ,'oÆ$r?0\)%LP5+g%?ŋo45HoJîTe( ~!3 ߰AGs 5s6U?-WصNCZ`^ 1b2] }٢v~lDҎ-8 Wd <6/1ns`!iDzvHfo`Tp+}V؞.=`b3faQE*+{_GeՍ`rX* q4nwI,$=El(׾EtBŏ.naܝX xA1h@6S%pޣ}b7^k¬{2Vi-2RR*Y4W@+F|>SDOHp8cmDcUb+/LoK<9}E  bVd| q!ܧIm^pj~KC.@P2Q()HոgqHd27oY,]}֭\#B1;@xR jщYNVB˲@aboDJ /#wUkeC 'o @3ܫb,k$U,o)n"\ή@6 Ȣ>)t0^9Iڕupnl3zû ~ Wǎ32h,lZ]q=v6* #oEm$W1^t)TNBv^ÝA_^4<0١O1yv8`ն`h.WI!SrLU7KKf- c8㳷[ p|R/<4bmcwśDk94'Sڝ&Z/e*SGZg};+Z%R\xZ~&G#,K,z 5cF$:DzkihlUѴ=5*"n+ R}}`ȤI΃tM'ެ'mJůQoӰnpHSu/OzX94=-†nNBmM)h?F+DQ[8kPh)7$uX4pg%I4WRKbCg=/6V#z)G7_ i}y^ὑC"=ЍEq˂a Oh%1^v}weǏ[ CAd1yJE4J\{E&"p{mROSEܨf2&t.*T!{4ڜ.b2$6ad iJ8F/zLďa DyY+y"YnZ$@5׺q5d+Z5+ {9hpHFˆ_֞> ʂm+iM\w^NR`ï'W!pϻ7Y@'ʒ*"8Ћ#L̇s[K~(}m맕'W\WhX0J9bR`pf]@<4w~ʺO6);LqY#+.ix6'K/ByHљհߧ,NxUZ$UyC-=׻+Inc=i#3L,L#GuA1!;BN;ĵ'㆓} xNZ3R8y#X xfj2S_b6ovB1W0tMYAƀٻNB2W0Wnaų"VT0{ߍ0$R,tfYAyzi gF?F\N 7OV8~3I;o/"C9SۄM̙ic,ŭ_9fx3kr+É |{-!"mOSaC$ `?|QK Y FCtҜ4 /u`c !fai5*&{CZm'Գ\P-u"ͨoyne=t31-NT$z\4[PDU&7)J=)&7h*㓯D;EY|$>s Ӛ|n IHQ.;A%tfV}>=o ٨䝽6'r-t1˶P))ڼGeALe=xYBM'^G NQv=S& ܿ[akUWZoEa#fvZ,MҽWGO]26n9A`i躎|p*ci% QUj..U1D=R79:&/uhB=wvRK඗~@v }Zp#6ƭ?< qSVB?-ּ滂G~pEp̕%ŗ3'!<U([v#fjɵ?Tpg;bo6K=:Vn ~zeiTCPy!ꜰ˭qf8pا\&pJ㠰 nP8{깦Kz:z%Iv{+B34z=~~Z=.D–~pRߴO째lNEG;TjhK @E z wl0)K9myK)aarB(6t eXǥ:}өVzw;~TMpӅ~N3Xe۠+<RZHTlhO{K0t';߾ nx8鹿:!=3rǑÉ;,bC! 9S:g*SD,d;)̧=?ҏV=ILcwf դ(F0e[`L g ƱS2,Cat1qk%]7WNoo Y6tZ8S*L&pq Lx& ؈z74:JB02mJՊ,=0uxJsnZ?;+?- qm[3ͺȧluC OԆf't|] J9-±;v8w6!KY)`2ѻp{8gM~Hגbu"/J2c| Nr= endstream endobj 252 0 obj 5040 endobj 251 0 obj << /Length 253 0 R /Filter /FlateDecode >> stream ظ3tQ0Ͻoü~d F5t endstream endobj 253 0 obj 48 endobj 254 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAC+LiberationSansNarrow-Italic /Encoding /Identity-H /ToUnicode 255 0 R /DescendantFonts [256 0 R] >> endobj 256 0 obj << /Type /Font /BaseFont /EAAAAC+LiberationSansNarrow-Italic /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (T>lDwlH0,z!-) /Ordering (S~chP@8iD\r@) /Supplement 0 >> /FontDescriptor 249 0 R /DW 0 /W [ 0 [228 228 272 410 456 272 456 456 228 456 410 546 456 182 228 410 ] ] >> endobj 255 0 obj << /Length 257 0 R /Filter /FlateDecode >> stream f (9m+W>?2裂TiO.Yfw'w9d@MHbZ-"V*JEi^ )f"d~g_vn7 EצNMr(zAB'z3bB˒z9UsA#Oxp4^ V7-o\~B6"s3up λ# ={ ;'z xw@c7GRhU_`GbΰcUnOXqvܯ|NֱL`o endstream endobj 257 0 obj 320 endobj 258 0 obj << /Type /FontDescriptor /FontName /EAAAAD+LiberationSansNarrow-Bold /FontBBox [-151 -303 1000 1033] /Flags 33 /CapHeight 687 /Ascent 1033 /Descent -303 /ItalicAngle 0 /StemV 0 /MissingWidth 500 /FontFile2 259 0 R /CIDSet 260 0 R >> endobj 259 0 obj << /Length1 15980 /Length 261 0 R /Filter /FlateDecode >> stream bai:J fMJeX߬HēCʏ8)m~JN-.'wy & /dLlN+ &ӑrU6SqCq%MGC>.Tpa>M[;iT'2]1P21r{q9c;Ey{3يP+ ;`y0, s]0>qZ؛q>%6&!栶ꡎ"=UOp7 \᷀Q\j!I\j3FKRgJO!̐ R8o!>U>A3ѻ{rbݒ0ן\מuQ&Q>XvL磧/ :E̻j\ex?>;GJk`oc&EO<蓼3EO1>I8YoƠЊOo#^@:gnW2pTM;qa:\:PF&^.K6'▒6D55g(V~]E/1>rAbMs==u<ש&ff"x~}!%Pqhkc(:8dKr%!xˉ X/ʟti͹JR,4Tɧlw@Jxq[—q*$n;\ЏVcuF8%YϞ>taS'CZ\zRK=aU P%azwBU @@$=Wo`d5nK$nYD(^AMD->-%_L2n1Ehlh 2:aY۠&ɗ4Ap2|7S)F~(*f!پ~vHp ʡL\8dmȕ_!ASDlUěq ld^\Vw63e\V OxHE:&CDŏWNX{!&0SLxn3W(_¶4dAvH͖sK>. 0Cm͒E)45 3ڢȭN)SiaX)-#(b|H=&!ROtD$6Hi19؉ס$]`;f/)ִ;rcΝ"0G,`1hz-a!hlp\x/EщoȪ8eW Sw)''AH$f;(W˂n6w ǚ4V=-Gs˯ufajµ#`7p鴴w00_2 2:qԆޡT lLvo`Z)OOO^Q~LcE"mk˝K.~|p[yy6;:AYt2W8Dg- lOmql]T!B~k3g~5O@_WU+=7N)r&zw%0H/͈`o /'$tMDyT^n"q((D뗜#)X2"T,1W=$-^R%*) ¶.N z |ZL0 |xJʗ9dM C,_|{; Wn(NvN$Rꏟ|vm. vdife.'[)Xt+LݝuA#aC}`odB=B2]":vނܘBZlśNh H65My>mi K:hKrt>* 8KN!4 [fQ2W_^[tt4Yse3 h'hX OiOKUwWNg;O/&2#00)8\lj2c6}FAb 2ONMcFb.f0Ip.bD7N(d5owqj X3gvWeqK,Զ>z.@پ7xkMYrc؛0҅v؄a^X5Ob5 ) \ש;(/[pewg҃Z^\EfoZ*vs@кM!+Cw-qH3/?|%GZ#z[*Ylz1%@gwטBDkuH~6"YN:%g޽Dcz]fRgNfw@ÛA)XSf阤7-RLq%> yG»I͇jO7 GgcvfdYpp ɺJRY*z[َӎwHQ53_&^ #<Z>{i1>t?۳{tɬa^c%@"oFCkchdGM]@/vB)2vReEI:bϞ  Oʒ0/ Zp?:pvhlmPhwrkJ58HMǤ2V*G0b_&h0:O;h.]BDv xA&U~/9YfI64'uZxZSє0F;AB=| ˩ 1|2wI/׼ƙ|\5,<,8Vi@ҎF tǖ_ qnSQ*s wgme۽x+CQknlta%Gsy3tޮdN,Y< ɇ} 2v0X,9 &Tsj.%ӔMk}+ If/')l#* xB̖,aoяZrζZl;ǎ!OPbq.1V|GpG-% L@BOq+mJI|Jė3ErӒ23l 6Y8ap,?YF\{oַw l$Þ;\\XLxuATAϴScVhS p.|&sJZ?|l\7o&>,YQ Ht7T`3ioRY3opA)"@( ;ؙzvV ecx0%n"|5 K;nÒG4ͣKʉ`!i9u=ITU 8@Vj1زJ!GBTCJ? ̀x9SfJJW Ck BKhdTcKrt ~[m&A1T˃{܌[셵g}~BHHDiȄIdތ`ɮ#j}W-I!&x")}?6vo LQmP)0<+E+/+M01g*9 7.:FY}ʷ !j(p<ÓN++įdfoAR \͑q E,9}I[*4ZQ¯bmtWM"i5c!JDz3QYIԳĉɃzguj3Q6L 6o9?>a?WLp3dVBˡ E^}0|8s4G@>Op7/< ܷZ]+6 kati #o ꋵJdaJ &s#PO޼J.]nxnʾ cy>uDC#iNf%FQ ħoNoCQQ#`v "]6kݎH>7牙kK-~OH ~2M|ģdlm`ߠ)-XW+ F!H{cTRm@)FpojLj. CC+qrORe´0U[ hE$/A3GXs޴xHY !^ޔ) Sœ}ĸ%DD'~ﱋX1^քsO:;&%c\;t*+KُݼN^[#HØ33{P)@ GZ)=mȕw""fʫb8H1(6SRnqר[S&j]LE'$ d\^k[+iru'm㔟cGKQ2邷~jrH}\f/%# iW/g&%;os'΁4H&|љ6L#$e#7ҵk!PIƚ>q fͺ 'EiC04U ZNUѓ7ٖiBPm:W8m )`k,ٜkrWivp bݡՙƦG="`;s;MHP|Ez͙PA"H 棡/'6e8+tOyWCJdc-O.zle6d'KJܿS$q9]B4*)y)[BXK&qk-?o+ oc"K^Hl4o8 <9^LD*cv @= B~(+#1gkIH6f䌨pJ5'!fx@s$u¶yJOE8aOҊV?m+>K@P)[ì( ڴiH=;9 è5oQG y __ ks \E) OCJF EWg&! \gG ;&6:+d(+3u v,R3\%G̻4JRui)/oSHhR*rX4&6S'pRrril@K&/P꺕鈩_ xd1cFkj_E%` (w |ŒoI'9Bbr2YVC("r=!r?cV'hمxghHwh~ fs_Ė-IUG/Xd/l۷m<v{ ԘԠIUyMPtϦA{zoJcYx!K]C֚֡eb%_!vO%Sr꼢᷎㓟.TZ+E5\Οt>9zFt9l-zW+ %TYvu?  f n5? t.bFўExp&_:>=GL!ŃAu#XH~c3GE4BY$ --ąp8O'k|˙SqkP F:e}H_HaF;!uSv> {φ>5Al*AĹDRsWGmPC)d,K3tf}5]O6-A[y!A8D{oAOѸ"?w-8a޸J'T7Ax 7L`3  +5%`䐋i;R'2Qzt&Rǀ~QN♢oӊJJ :TE}~N٘kb '& oÌcynW{b⍂QMW]Ӭ$60pNݼ+8A>'F*`tOr;qV>+g$[ϖdxqLX*W42' V u^ *lq"JPgk8iXhf{Δh'O뽻G7=7o#6 $s/~ܽ78]ЊXA(<헵h[˼,!+Kd^1$0 p0 }8{ϩ9JNtωc7pu@JEce/ Ob3,(Ѕ!'G6xW;E{68p&N183wsEQh/iKzvJ=at7]d3$'|_Tط> 6-8P2bV3ORu~% #B#;LP 8>v6ǍUJ@]v>nK5QsʖbmZ5Gɚ׸~>R7ҥWS-:Cy ʽ`40Z(]Oۧ`m)+B)S$9 4ܬ-^ s L[m G a a) =*]n,]LO%a ͬȹInݭSjo)?O S]jSZ/2R(^3m:L/S՗.ژs)3nI>+orwnb.d}Hm80UIoˈ{Ý\WۻyYoA_\Go}9{?%DoBTs|^q=Ş2KJ'p M VAwɰSa*ӎ5Q&h]Rm(-,.U7#-YBr%$F@f~Fm9D.Wzb~dDZ"Q쭃Zp_4%|;%H=^C莼vj[uy4oև(땳WKEEXjN$ ˦QN(5JI I p-Y؛ !Wm4 p^nV#}qyd QEAsZ[X b4Γƚ| ׇCjZLyj2=8_4ݷ$ 63f)3 T@r'aIqv_i'aː }Lw` t0EY r4n ~8 ϝ!zָmA"%dWiDF׆eSބ]0B}/RjâV~A;L>~/8 6|S}>VF(%~~g:~S?׍~V=|N⬖FXtG-<SBS;84P1|M^).MޕGAUߖ0 `KDRӕ[wwQ$<Qr؇sgB٭a%#RT2ᣰ\ endstream endobj 261 0 obj 10560 endobj 260 0 obj << /Length 262 0 R /Filter /FlateDecode >> stream fCd9VXD]JHw!NswuJ ev?e endstream endobj 262 0 obj 48 endobj 263 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAD+LiberationSansNarrow-Bold /Encoding /Identity-H /ToUnicode 264 0 R /DescendantFonts [265 0 R] >> endobj 265 0 obj << /Type /Font /BaseFont /EAAAAD+LiberationSansNarrow-Bold /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (FyAј=?*\)sz-) /Ordering (1TNȐ}CW0,\)b!olh) /Supplement 0 >> /FontDescriptor 258 0 R /DW 0 /W [ 0 [228 228 272 456 546 456 456 456 500 272 228 456 546 729 456 318 456 683 456 500 500 500 228 500 456 500 410 500 591 456 456 456 773 456 456 591 500 500 272 272 591 638 546 591 638 591 591 456 228 638 591 228 500 591 456 546 500 456 456 ] ] >> endobj 264 0 obj << /Length 266 0 R /Filter /FlateDecode >> stream nm6h6)şp|2^gQwIQxِm8tlkGt^`'Q#.ԋ[0p_C~CQ# UЀzq%MH~2[<n)Sr܎h?58wM ,?Iǰ {Jsګ-b&ᾯ(Rxo<,Kj.2%TLrbb*x8UO"^M=#f> endobj 268 0 obj << /Length1 17716 /Length 270 0 R /Filter /FlateDecode >> stream vj;+P˨1քnB6 ϶h kJ5UE9QҮI:{J_/p;tQa1swQ /Z?Xf^!lۅZ_.!'_1얦?l<%B3CHڨ} )kxCzJHKcz,#:Nf(1]O4УǝhvWqMbϾhZKkZeG4n)Z(\@@(NC!AΘWgHq`]mPC7k-螗g2j?>Q t Y#i[fW/aàRe퍖Nn[vtUFCr7g94:xtݱP}wwog@\9%[:[XE6g3!̦2V'*=ei{鋑F{~6"ߒpj&Vfr6:c맻h0Gq} 2: ukCJA_.n}lYeѸ\R0Foo#jhJuWdo ^`& . Im?S{)?6V@6d :omUq5=wcw hwi[UWGXBw3JTɳ.j14x9we7tG?6oK:W Qq 6z Q/W&_~N}O"CoBLns(A|}v|{$,K KѹW>GDRTbmB35 Wn%`2Vt,87,r!%z5나JW] q⺉Nd1-Nhc3p`Qsq8ݗ&q C^Sy '톬s(z{vzbZ' L:Ah?BMɲU#m}OZA9?w-A8Fmwcl`HYS? * _7^)%lDJLO_k4Nc쩳.Q$kypnͰkGhvo$;bhָ%w7T**NV9H9~w ~9]N"\1pߖ}=*k$&3Z!j V|}*>HKsڐw?{8~i~AeP(c/45Jѩ< O5|uǐZ tVdHP4/+V|uѬ$ӟ 1צ8P~骦_dsõP\xvvڱEqoo} |8Y6ACʫ{,2yᙸ6>OSZOKMM!OLL5ޒ^GzMT3z3}@ w,IM[gJQ冂8/ܶ|ҳOٮkbpW'7 [C ]${-6X $Z,4@[:=͎7]l rbC /flVnس-9j{Y \ٍyg&X5跬COdVpMXdz#BN1gW&Nd>xS/9yXEp5 (ӛN&`$xB$C+>DXIZBL+=wĪ;Oe 'KJQ:ok3MZ$r,*˟]?ף? K4|.b7gAxg{._=3jn(Uq@}82t ?ĜL:er*78P w؀U[R[}Aeڧ.狽gT1sH9u/|ІS71I؊E0'^fV)X jh>ީnXklk}AtۧHVp("> \y#"} FS9AO4A)4| {9 U>(*&ٛ5eN|o@F &g:{0- Rݧg#q@g@d 9ER;eQ V"ϊBrZ^9B/ m=q{ ;ѽܸE󀔤%mv³b] Bu@7,Dm:E}T_0W&-T @Ygd \0ffL{8PݫHaPkQ;b b.aƗ)䮆<' ?W~f"[d2!IJk˕2:de't >§$=_3v΁0"A!^ T{{2f'th*L4  , 4]G&yP8_Fa @bs#`aWȊ/ړa+}FYG`cSyh==B5/UB+6̽y)[xZE4`6w~;EL/a26iw)>tav6"Z؛z%/]l֔5R[&-wiROۉr~y̕HX]4zAoC<8NxU},-G,Zrςot€K:kwokvXZ3}-Z ve7hC7VxLfӑNlMjگެ'`;V%I [c$ q"zVSOss5Q^k308ENd4j}T][-*L> my~J~2B]3uŐj$4]/Ew:}| aAkeO ;dm.ׯ $qc"Pk% f̘x)q)NHl^#VT' xs`/An3SʡhUZ N_ 1TVc} o3̟Z^gN}6G4ko-AK&)BsC|+޷&tMδ(;8XY wA: r*BN +Z38* /j@>Euu])i㪋p:v9bkTΊG E8b/MQir*[6W~DcyTt/x{ f ֤9Fs{03؍N"Xҹ ۝W *pf+@V#DLH`šfK%9Mz?|=-`9m1p~A;̰duUaM Ǐd_Qs̚}|9ȭ$cJ84eV+OT ;&JRS8jgA#&;̒I+ _kaK:g}5:~WbK ЂL\wؐvWiĜ^PH92ŕ[mժ&+"e s=~.\ӯ=qiZㆩz-^įN#/ YjTx@{isRrM"" '~fo섌iJ4[+!G}'Ύ/' td͏XUG;iN®kmU\!"6BdFók4>,jqNZֵY9ȧ% w?W?H?}GsW}F&q [ @W^759=Յ/ɬl>ż׿)ET|/Î58|}@zK7k+{5s# ؋14K7,ŭ)=9?(O^녣B^8hP߅\lWM9)Y|B!4Mj;^{KYnky,r[VC7y*^oD?,-$^1ej|?Ƹ\~!^JT emS~5v >Qz6VfB y`#sknZq@!~ub-a!!NmZ#gp.ޙ_JcCE n׬WUrl ʦCV||\"P$2*a 6iڰ`@^Y lfb/Ȭo8.[ʵī0)^%¦e*'3-$8 z;O-U Zl;'q6'}J3g㻆QS plmMk .sJ;J+3LꅭGAԽt`a(Bx٣u$[rHqp;GVc^ͣ*Lot [A2]a'#2n S::Nx 5UGD؛;z]ӳSRXM㽝י"dQG nRe?aZ:xjTO+1Zb8j݌;ytѲ/&|AA'8)[ @ WרiwAs2{/:B+}=w"p3 PQY`5Tl|}GZ-ף[wg{h!A5Ղ}wtoK_]D_lKWgf&~x(`A;nߡ!9K E͇[WqB*+Ebr*'_󝆳*`9uba2]*)Da#Ow^ FH)pyBb¯t;<8+PۨhLxٍV Gi9~?WÄکM}{^JoLKNGu(pjB?瘎(_B1&5r4%QA "_7بkþmAQxb}gsՠ5|ڐcM+Rd(p"U>QViVm7wܗQ clˆ0XbG5AVk""nAwت=J>krf-^jr"~E1)GƠ},;y`:\ fvۘ8s/'ܸ؜@q~CAt D9</ʱZc.p֍`b$輩vU sSOiLn-P"!?1HI|T_ %A櫭q7h;Jӹs(:_kƾ yR&T3e[>0va=SW c0gB*-/[ p35w?"?dI@t'.{=dDwA6"+OB'/Y>yl%5m0MyizG\cijh_ӏ KGDveq?m ڍ=ofg8BA;-ZdhcK_E!HO&WG-E*Y_uC&,, pX4%U"!k Nҩa/2Hv]+_$n 6!>CEkPj|R9h0b4m{ȕiUͿ'ؾTP+vxzZ2)pňa~u]^#h֌-DzۘτjB6SQh k`r٥8\_s5u!wBVX4R? )'&E-/D_ +< 8Ge %ed;sg[_Lbzc׷75ß@8QhE8ބS}0.E 4zne*RsyiF!O#,; D,vGl)yi/RKUnUFjƇR|ӴkxG@ G3Qz{ nWPd wߥ[U-L_ÍS5tj^=dM(W7|Q@;iB}A7Vp aw-G1[hs-4Z;@pr?q]PtE[`t,_%{ UP3<6A fkuQ1~+>ը*SQmϔ./9AOa9-ʟ;uod+κZ~tK,ɮns()iβ G ,AEn7hn@g ~ X;LOZ XtBK主.g>E F^:c|gե-B|o;>(0jPV/8qs_Y‚F~9Ϊx axE6>"3(x3tyf4wA4c2ٟo,atm2լ2_+Am~vaG9m ɨ͔ޔ\}F6(`hm!Ȥ7vam)rrk!>,-Fcl5?)b3_8Th&ZYɸpe(<.QgQ7wAN(Ӯ6(,$?VD-lc 2Lf\1Me, pڱm2fZ&@KyVHћC\? ȏqܩK53K{$ga =.xfk%8NPܯOctdPrxrg?ٙW-ʥD 3|n?ݒixT\&QY D7~K"sP̔LRhaaR"x}J I'MN,^ vMj:Bܒ b)?"#P)`16f 41Weh{-i䶕рʒ݄|ayxU[4a=ؾ cDmLtKcqcGAxz݈+X?Ns})/V=GХY"Y@,qzjU_Y6W/W܂p5[z\Qeɇ2*pA>nB*w;אbO0AZ. _.?N2Y[I؋I6׆踫_|{I|?:|UII'=E6:4j}.lmF I?f*$@<jq=xJl}\`aaV1!3z}tzY6BN JR&<725&J .òi67? hSW a8p}+%j T~_{ !{gV/>ք$'v^ G-,Hmed|C Ԙ98)FcE=Fl3C4j_YI36:76c;Xl/SGp'uO3~ =iPVn+nŠf~}nwZ 2<`As zS߭~hzx>-&qh9p'o5ftEgљvoҋwySy /uzkZPĐޤkF⯩ܦ3ٝI`af5#' DX5ݸQ>1#z/}uUi N\mK3Jd;*vuG{~_}ЭD*xêLwdyn}0 GN)oM#56x߇_`kϨu.鲻<#0(;[[nyJO{= >$\v<$MKoq:o)"MvUv1M@CBrkD{Z!!{@ z\q5{]( Hӵ~CО.v6ǬXvWr쑃J68_N)3ncV@@+&ԣ?3/ڡ5P6lI^1E8q u+]&~z+%0 Nد?mg]ab9 xQɳJb0< \ʻ&`h\{v:ǩ"“ҺTg$MhA쀌gfRQQvg? >rq ?"G4 Fn*~K[/\_؈$G/ylҟYB ~Bf$z`䇍[YCR -1BC(F #u2huJlj"P4j]9 oy:Or{@ mq,~ 1|M[g] )XP+$=tx}ݲgUkZ:QжrB'vvu Ocnr&y~<ߐZ7bT|Ŕ/F=P:ir6-ze{+Gf] S؝yuSM(bq@PJ՜sg1 )XCT7!~hM/;.:;))w: bZ3"B/U%MN HF,wkЂBMn>Lx0츐HrF)?*>/"j|?Ҟ^娃/Moe#v,ޠ7h"Lc5; 0 +E#/x[iLbW7-4׶Ctk kR;&:<}S Y8 9zH7@@Zy~e Oҡ 0,JeQܰ0jw ak [5oLqңuH2F1omtCz_ oRLTߤm:-Swʧėu 3n)4-@ǒ9% endstream endobj 270 0 obj 11952 endobj 269 0 obj << /Length 271 0 R /Filter /FlateDecode >> stream NN-XaQ?A(zBLjs|Ҙǯ߬6 endstream endobj 271 0 obj 48 endobj 272 0 obj << /Type /Font /Subtype /Type0 /BaseFont /EAAAAE+LiberationMono /Encoding /Identity-H /ToUnicode 273 0 R /DescendantFonts [274 0 R] >> endobj 274 0 obj << /Type /Font /BaseFont /EAAAAE+LiberationMono /CIDToGIDMap /Identity /Subtype /CIDFontType2 /CIDSystemInfo << /Registry (kKOp4\r|q.È34@M&) /Ordering (\)/H.@T{PnU\(ik/΃\fR\() /Supplement 0 >> /FontDescriptor 267 0 R /DW 0 /W [ 0 [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 ] ] >> endobj 273 0 obj << /Length 275 0 R /Filter /FlateDecode >> stream ӽXED!T=ueT\0.C݊T`N XZ9hPs $L=Xo#bQQ=$1< $:eׄVPm1W6%eHâ:R! n ٪7DZIxvA.ꑯ> endobj 276 0 obj << /Type /Catalog /Pages 17 0 R /Lang (mir&NwQ_6Oh۳Pv]Q|tgW) /Version /1.7 /Metadata 5 0 R /PageLabels 277 0 R >> endobj 16 0 obj << /Font << /F52 236 0 R /F43 245 0 R /F45 254 0 R /F44 263 0 R /F47 272 0 R >> /ProcSet [/PDF /ImageB /ImageC /Text] /XObject << /Im1 7 0 R /Im2 9 0 R /Im3 11 0 R /Im4 27 0 R /Im5 29 0 R /Im6 61 0 R /Im7 66 0 R /Im8 71 0 R /Im9 76 0 R /Im10 81 0 R /Im11 86 0 R /Im12 94 0 R /Im13 99 0 R >> /ColorSpace << /DefaultRGB 4 0 R >> >> endobj 278 0 obj << /Filter /Standard /V 5 /R 5 /Length 256 /P -1036 /O <582431740A22E3ABBBF1475F5B2FAD59F58E553981B81C9E40A4E5EAEF5CD173F9D70E4D24436A615AFBE0391A6BEF61> /U <8E61034DD859AABFD0FAE591A327616694AC95BE42C378E371ACB186FA38F9C936F139ECC6C26C1F606AA82BCEFE3858> /OE <408BFA0C4C02970C0C6516CA83888C62319345C933CD5EB805100A7C23E7118D> /UE <6BE370E3241579EB2BADEDB99EB6A55769BAC52AAE1754A826B629AF991E0322> /Perms <1B93B2B4629C0B896A9A7ECB7796FA89> /EncryptMetadata true /CF <>>> /StmF /StdCF /StrF /StdCF >> endobj 277 0 obj << /Nums [0 << /S /D >> 2 << /S /D /St 3 >> 3 << /S /D /St 4 >> 4 << /S /D /St 5 >> 5 << /S /D /St 6 >> 6 << /S /D /St 7 >> 7 << /S /D /St 8 >> 12 << /S /D /St 13 >> 24 << /S /D /St 25 >>] >> endobj xref 0 279 0000000000 65535 f 0000000015 00000 n 0000000276 00000 n 0000002857 00000 n 0000002877 00000 n 0000002910 00000 n 0000003879 00000 n 0000003898 00000 n 0000195954 00000 n 0000195976 00000 n 0000242612 00000 n 0000242634 00000 n 0000286692 00000 n 0000286714 00000 n 0000287462 00000 n 0000287482 00000 n 0001864814 00000 n 0001864382 00000 n 0000287708 00000 n 0000290472 00000 n 0000290493 00000 n 0000290719 00000 n 0000293243 00000 n 0000293264 00000 n 0000293490 00000 n 0000296798 00000 n 0000296819 00000 n 0000297045 00000 n 0000298286 00000 n 0000298307 00000 n 0000300034 00000 n 0000300055 00000 n 0000302931 00000 n 0000302952 00000 n 0000303178 00000 n 0000303376 00000 n 0000303516 00000 n 0000305000 00000 n 0000305021 00000 n 0000305048 00000 n 0000305291 00000 n 0000305457 00000 n 0000305596 00000 n 0000305730 00000 n 0000305870 00000 n 0000306004 00000 n 0000306144 00000 n 0000307196 00000 n 0000307216 00000 n 0000307257 00000 n 0000307500 00000 n 0000310520 00000 n 0000310541 00000 n 0000310767 00000 n 0001777847 00000 n 0000310907 00000 n 0000310989 00000 n 0000311124 00000 n 0000313312 00000 n 0000313333 00000 n 0000313367 00000 n 0000313610 00000 n 0000424509 00000 n 0000424532 00000 n 0000426768 00000 n 0000426789 00000 n 0000427015 00000 n 0000668250 00000 n 0000668273 00000 n 0000669565 00000 n 0000669586 00000 n 0000669812 00000 n 0000854647 00000 n 0000854670 00000 n 0000857386 00000 n 0000857407 00000 n 0000857633 00000 n 0000974964 00000 n 0000974987 00000 n 0000976919 00000 n 0000976940 00000 n 0000977166 00000 n 0001197778 00000 n 0001197801 00000 n 0001198373 00000 n 0001198393 00000 n 0001198619 00000 n 0001410383 00000 n 0001410406 00000 n 0001412130 00000 n 0001412151 00000 n 0001412377 00000 n 0001415861 00000 n 0001415882 00000 n 0001416108 00000 n 0001577744 00000 n 0001577767 00000 n 0001578979 00000 n 0001579000 00000 n 0001579226 00000 n 0001767663 00000 n 0001767687 00000 n 0001768981 00000 n 0001769003 00000 n 0001769231 00000 n 0001772189 00000 n 0001772211 00000 n 0001772439 00000 n 0001774853 00000 n 0001774875 00000 n 0001775103 00000 n 0001777597 00000 n 0001777619 00000 n 0001781588 00000 n 0001777930 00000 n 0001778067 00000 n 0001778150 00000 n 0001778288 00000 n 0001778372 00000 n 0001778510 00000 n 0001778593 00000 n 0001778731 00000 n 0001781513 00000 n 0001781535 00000 n 0001781834 00000 n 0001781973 00000 n 0001782112 00000 n 0001782251 00000 n 0001783817 00000 n 0001783839 00000 n 0001783884 00000 n 0001784130 00000 n 0001785968 00000 n 0001785990 00000 n 0001786218 00000 n 0001787800 00000 n 0001787822 00000 n 0001788050 00000 n 0001790480 00000 n 0001790502 00000 n 0001790730 00000 n 0001792712 00000 n 0001792734 00000 n 0001792962 00000 n 0001793045 00000 n 0001793185 00000 n 0001793327 00000 n 0001793469 00000 n 0001793552 00000 n 0001793692 00000 n 0001793834 00000 n 0001793976 00000 n 0001794059 00000 n 0001794197 00000 n 0001794337 00000 n 0001794477 00000 n 0001794560 00000 n 0001794700 00000 n 0001794842 00000 n 0001794984 00000 n 0001795067 00000 n 0001795206 00000 n 0001795347 00000 n 0001795488 00000 n 0001795571 00000 n 0001795711 00000 n 0001795853 00000 n 0001795995 00000 n 0001796079 00000 n 0001796219 00000 n 0001796361 00000 n 0001796503 00000 n 0001796586 00000 n 0001796726 00000 n 0001796867 00000 n 0001797009 00000 n 0001797092 00000 n 0001797232 00000 n 0001797374 00000 n 0001797516 00000 n 0001797599 00000 n 0001797739 00000 n 0001797881 00000 n 0001798023 00000 n 0001798106 00000 n 0001798246 00000 n 0001798388 00000 n 0001798530 00000 n 0001798613 00000 n 0001798753 00000 n 0001798895 00000 n 0001799037 00000 n 0001799120 00000 n 0001799260 00000 n 0001799402 00000 n 0001799543 00000 n 0001799626 00000 n 0001799766 00000 n 0001799908 00000 n 0001800049 00000 n 0001800132 00000 n 0001800272 00000 n 0001800414 00000 n 0001800555 00000 n 0001800694 00000 n 0001800835 00000 n 0001800975 00000 n 0001801115 00000 n 0001801257 00000 n 0001801398 00000 n 0001801538 00000 n 0001801680 00000 n 0001801821 00000 n 0001801960 00000 n 0001802101 00000 n 0001802241 00000 n 0001802325 00000 n 0001802465 00000 n 0001802607 00000 n 0001802748 00000 n 0001802832 00000 n 0001802972 00000 n 0001803114 00000 n 0001803255 00000 n 0001803339 00000 n 0001803480 00000 n 0001803622 00000 n 0001803763 00000 n 0001805457 00000 n 0001805479 00000 n 0001806028 00000 n 0001806274 00000 n 0001806531 00000 n 0001814572 00000 n 0001814550 00000 n 0001814714 00000 n 0001814734 00000 n 0001815413 00000 n 0001814892 00000 n 0001816003 00000 n 0001816024 00000 n 0001816291 00000 n 0001829949 00000 n 0001829926 00000 n 0001830075 00000 n 0001830095 00000 n 0001830883 00000 n 0001830265 00000 n 0001831553 00000 n 0001831574 00000 n 0001831852 00000 n 0001837012 00000 n 0001836990 00000 n 0001837138 00000 n 0001837158 00000 n 0001837696 00000 n 0001837335 00000 n 0001838094 00000 n 0001838115 00000 n 0001838389 00000 n 0001849071 00000 n 0001849048 00000 n 0001849197 00000 n 0001849217 00000 n 0001849924 00000 n 0001849392 00000 n 0001850514 00000 n 0001850535 00000 n 0001850795 00000 n 0001862869 00000 n 0001862846 00000 n 0001862995 00000 n 0001863015 00000 n 0001863739 00000 n 0001863179 00000 n 0001864361 00000 n 0001864643 00000 n 0001865774 00000 n 0001865204 00000 n trailer << /Root 276 0 R /Info 1 0 R /ID [<6F6AA5E40F29B8132C0EB0108776CE32> <6F6AA5E40F29B8132C0EB0108776CE32>] /Encrypt 278 0 R /Size 279 >> startxref 1865983 %%EOF duesee-imap-codec-0d00966/assets/docker/000077500000000000000000000000001507724125200200135ustar00rootroot00000000000000duesee-imap-codec-0d00966/assets/docker/ImapTest/000077500000000000000000000000001507724125200215415ustar00rootroot00000000000000duesee-imap-codec-0d00966/assets/docker/ImapTest/Dockerfile000066400000000000000000000010621507724125200235320ustar00rootroot00000000000000FROM ubuntu RUN apt-get update &&\ apt-get -y install\ autoconf\ automake\ bison\ curl\ flex\ gettext\ git\ libssl-dev\ libtool\ make\ pkg-config\ wget\ zlib1g\ zlib1g-dev RUN git clone https://github.com/dovecot/core dovecot WORKDIR dovecot RUN ./autogen.sh RUN PANDOC=false ./configure --enable-maintainer-mode RUN make WORKDIR .. RUN git clone https://github.com/dovecot/imaptest WORKDIR imaptest RUN ./autogen.sh RUN ./configure --with-dovecot=../dovecot RUN make RUN curl --location -O http://www.dovecot.org/tmp/dovecot-crlf WORKDIR .. duesee-imap-codec-0d00966/assets/docker/README.md000066400000000000000000000001641507724125200212730ustar00rootroot00000000000000# Docker > [!NOTE] > > These files are not well-documented and rather not useful for anyone but the maintainer :-) duesee-imap-codec-0d00966/assets/docker/grcov/000077500000000000000000000000001507724125200211335ustar00rootroot00000000000000duesee-imap-codec-0d00966/assets/docker/grcov/Dockerfile000066400000000000000000000007331507724125200231300ustar00rootroot00000000000000# docker run -v :/opt/imap-codec -it from rust:latest RUN cargo install grcov RUN rustup component add llvm-tools-preview WORKDIR /opt/imap-codec ENV RUSTFLAGS="-Cinstrument-coverage" ENV LLVM_PROFILE_FILE="coverage-%m-%p.profraw" ENTRYPOINT ["/bin/bash"] #cargo clean #cargo test --workspace --all-features #grcov . --source-dir . --binary-path target/debug -t html --branch -o target/debug/coverage --keep-only '{src/**,imap-types/src/**}' duesee-imap-codec-0d00966/deny.toml000066400000000000000000000007721507724125200171040ustar00rootroot00000000000000[graph] exclude = ["imap-codec-bench"] [sources] unknown-registry = "deny" unknown-git = "deny" # Uncomment if imap-codec-bench is not excluded # allow-git = [ # # Used in benchmarking # "https://github.com/stalwartlabs/mail-server?rev=53f0222f308b3e844c158fc0e603d10361da3c63", # ] [licenses] allow = [ "Apache-2.0", "MIT", "NCSA", "Unicode-3.0" ] # Uncomment if imap-codec-bench is not excluded # [advisories] # ignore = ["RUSTSEC-2024-0379"] [licenses.private] ignore = true duesee-imap-codec-0d00966/flake.lock000066400000000000000000000010721507724125200171760ustar00rootroot00000000000000{ "nodes": { "nixpkgs": { "locked": { "lastModified": 1757023789, "narHash": "sha256-roMtzAgp0M4ExsIsFScWvWY0t1vjWtJwsqaxFQ2hwk8=", "owner": "nixos", "repo": "nixpkgs", "rev": "d689442f4e5a79df371c65b977ffff66d8fed809", "type": "github" }, "original": { "owner": "nixos", "ref": "nixos-25.05-small", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "nixpkgs": "nixpkgs" } } }, "root": "root", "version": 7 } duesee-imap-codec-0d00966/flake.nix000066400000000000000000000013611507724125200170450ustar00rootroot00000000000000{ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05-small"; }; outputs = { self, nixpkgs, ... }: let eachSupportedSystem = nixpkgs.lib.genAttrs supportedSystems; supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; mkDevShells = system: let pkgs = import nixpkgs { inherit system; }; in { default = pkgs.mkShell { strictDeps = true; nativeBuildInputs = with pkgs; [ just rustPlatform.bindgenHook rustup ]; }; }; in { devShells = eachSupportedSystem mkDevShells; }; } duesee-imap-codec-0d00966/imap-codec/000077500000000000000000000000001507724125200172435ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/Cargo.lock000066400000000000000000002560661507724125200211670ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "abnf-core" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec182d1f071b906a9f59269c89af101515a5cbe58f723eb6717e7fe7445c0dea" dependencies = [ "nom", ] [[package]] name = "addr2line" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] [[package]] name = "ahash" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", "once_cell", "serde", "version_check", "zerocopy", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anyhow" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "arrayref" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "asn1-rs" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", "num-traits", "rusticata-macros", "thiserror", "time", ] [[package]] name = "asn1-rs-derive" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", "synstructure", ] [[package]] name = "asn1-rs-impl" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "async-trait" version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", "bitflags 1.3.2", "bytes", "futures-util", "http", "http-body", "hyper", "itoa", "matchit", "memchr", "mime", "percent-encoding", "pin-project-lite", "rustversion", "serde", "sync_wrapper", "tower", "tower-layer", "tower-service", ] [[package]] name = "axum-core" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", "futures-util", "http", "http-body", "mime", "rustversion", "tower-layer", "tower-service", ] [[package]] name = "backtrace" version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "benchmark" version = "0.1.0" dependencies = [ "criterion", "imap-proto", "imap_proto", "regex", "tokio", ] [[package]] name = "bincode" version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ "serde", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "blake3" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq 0.3.0", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bounded-static" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2325bd33fa7e3018e7e37f5b0591ba009124963b5a3f8b7cae6d0a8c1028ed4" dependencies = [ "bounded-static-derive", ] [[package]] name = "bounded-static-derive" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f10dd247355bf631d98d2753d87ae62c84c8dcb996ad9b24a4168e0aec29bd6b" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", ] [[package]] name = "bzip2-sys" version = "0.1.11+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" dependencies = [ "cc", "libc", "pkg-config", ] [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ "jobserver", "libc", "once_cell", ] [[package]] name = "cedarwood" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d910bedd62c24733263d0bed247460853c9d22e8956bd4cd964302095e04e90" dependencies = [ "smallvec", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets 0.52.5", ] [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", ] [[package]] name = "clap" version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-channel" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "dashmap" version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "data-encoding" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der-parser" version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ "asn1-rs", "displaydoc", "nom", "num-bigint", "num-traits", "rusticata-macros", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", "subtle", ] [[package]] name = "displaydoc" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "either" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "encoding_rs" version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] [[package]] name = "enum-as-inner" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck", "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fallible-iterator" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fallible-streaming-iterator" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "farmhash" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f35ce9c8fb9891c75ceadbc330752951a4e369b50af10775955aeb9af3eee34b" [[package]] name = "fast-float" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" [[package]] name = "flate2" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ "byteorder", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "gethostname" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" dependencies = [ "libc", "windows-targets 0.48.5", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", "http", "indexmap 2.2.6", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", ] [[package]] name = "hashlink" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ "hashbrown 0.14.5", ] [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hickory-proto" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" dependencies = [ "async-trait", "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", "futures-io", "futures-util", "idna 0.4.0", "ipnet", "once_cell", "rand", "ring 0.16.20", "rustls 0.21.12", "rustls-pemfile 1.0.4", "thiserror", "tinyvec", "tokio", "tokio-rustls 0.24.1", "tracing", "url", ] [[package]] name = "hickory-resolver" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", "lru-cache", "once_cell", "parking_lot", "rand", "resolv-conf", "rustls 0.21.12", "smallvec", "thiserror", "tokio", "tokio-rustls 0.24.1", "tracing", ] [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest", ] [[package]] name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ "libc", "match_cfg", "winapi", ] [[package]] name = "http" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", "pin-project-lite", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", "socket2", "tokio", "tower-service", "tracing", "want", ] [[package]] name = "hyper-rustls" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", "hyper", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] [[package]] name = "hyper-timeout" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", "pin-project-lite", "tokio", "tokio-io-timeout", ] [[package]] name = "iana-time-zone" version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "idna" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "idna" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "imap-codec" version = "2.0.0" dependencies = [ "abnf-core", "base64 0.21.7", "bounded-static", "chrono", "imap-types", "log", "nom", "serde", "thiserror", ] [[package]] name = "imap-proto" version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de555d9526462b6f9ece826a26fb7c67eca9a0245bd9ff84fa91972a5d5d8856" dependencies = [ "nom", ] [[package]] name = "imap-types" version = "2.0.0" dependencies = [ "base64 0.21.7", "bounded-static", "chrono", "thiserror", ] [[package]] name = "imap_proto" version = "0.1.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=53f0222f308b3e844c158fc0e603d10361da3c63#53f0222f308b3e844c158fc0e603d10361da3c63" dependencies = [ "ahash", "chrono", "jmap_proto", "mail-parser", "store", ] [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", ] [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", ] [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "generic-array", ] [[package]] name = "ipconfig" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2", "widestring", "windows-sys 0.48.0", "winreg", ] [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", "windows-sys 0.52.0", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jieba-rs" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f0c1347cd3ac8d7c6e3a2dc33ac496d365cf09fc0831aa61111e1a6738983e" dependencies = [ "cedarwood", "fxhash", "hashbrown 0.14.5", "lazy_static", "phf", "phf_codegen", "regex", ] [[package]] name = "jmap_proto" version = "0.1.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=53f0222f308b3e844c158fc0e603d10361da3c63#53f0222f308b3e844c158fc0e603d10361da3c63" dependencies = [ "ahash", "fast-float", "mail-parser", "serde", "serde_json", "store", "tracing", "utils", ] [[package]] name = "jobserver" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libsqlite3-sys" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" dependencies = [ "cc", "pkg-config", "vcpkg", ] [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru-cache" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ "linked-hash-map", ] [[package]] name = "lz4_flex" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" dependencies = [ "twox-hash", ] [[package]] name = "mail-auth" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e9759ecef5c0d048464fee80947ca5ef25faff98add10ea8787a6e195b8dc5f" dependencies = [ "ahash", "flate2", "hickory-resolver", "lru-cache", "mail-builder", "mail-parser", "parking_lot", "quick-xml", "ring 0.17.8", "rustls-pemfile 2.1.2", "serde", "serde_json", "zip", ] [[package]] name = "mail-builder" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef70f53409852d2612f2249810cbbe0c9931ca25b739b734bafc7f61d88051d4" dependencies = [ "gethostname", ] [[package]] name = "mail-parser" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5a1335c3a964788c90cb42ae04a34b5f2628e89566949ce3bd4ada695c0bcd" dependencies = [ "encoding_rs", "serde", ] [[package]] name = "mail-send" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d9f578319ab6d0ef6bf39ca7051ad4ae960e1d647a55cc71b86e04236b25167" dependencies = [ "base64 0.22.1", "gethostname", "md5", "rustls 0.22.4", "rustls-pki-types", "smtp-proto", "tokio", "tokio-rustls 0.25.0", "webpki-roots 0.26.1", ] [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "matchit" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "md5" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "nix" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", "memoffset", "pin-utils", ] [[package]] name = "nlp" version = "0.6.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=53f0222f308b3e844c158fc0e603d10361da3c63#53f0222f308b3e844c158fc0e603d10361da3c63" dependencies = [ "ahash", "bincode", "farmhash", "jieba-rs", "lazy_static", "lru-cache", "nohash", "parking_lot", "phf", "rust-stemmers", "serde", "siphasher 1.0.1", "tinysegmenter", "utils", "whatlang", "xxhash-rust", ] [[package]] name = "nohash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi", ] [[package]] name = "num-bigint" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "object" version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] [[package]] name = "oid-registry" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ "asn1-rs", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opentelemetry" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ "futures-core", "futures-sink", "indexmap 2.2.6", "js-sys", "once_cell", "pin-project-lite", "thiserror", "urlencoding", ] [[package]] name = "opentelemetry-http" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f51189ce8be654f9b5f7e70e49967ed894e84a06fc35c6c042e64ac1fc5399e" dependencies = [ "async-trait", "bytes", "http", "opentelemetry", "reqwest", ] [[package]] name = "opentelemetry-otlp" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930" dependencies = [ "async-trait", "futures-core", "http", "opentelemetry", "opentelemetry-http", "opentelemetry-proto", "opentelemetry-semantic-conventions", "opentelemetry_sdk", "prost", "reqwest", "thiserror", "tokio", "tonic", ] [[package]] name = "opentelemetry-proto" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2e155ce5cc812ea3d1dffbd1539aed653de4bf4882d60e6e04dcf0901d674e1" dependencies = [ "opentelemetry", "opentelemetry_sdk", "prost", "tonic", ] [[package]] name = "opentelemetry-semantic-conventions" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" dependencies = [ "opentelemetry", ] [[package]] name = "opentelemetry_sdk" version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f16aec8a98a457a52664d69e0091bac3a0abd18ead9b641cb00202ba4e0efe4" dependencies = [ "async-trait", "crossbeam-channel", "futures-channel", "futures-executor", "futures-util", "glob", "once_cell", "opentelemetry", "ordered-float", "percent-encoding", "rand", "thiserror", "tokio", "tokio-stream", ] [[package]] name = "ordered-float" version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" dependencies = [ "num-traits", ] [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.52.5", ] [[package]] name = "password-hash" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", "rand_core", "subtle", ] [[package]] name = "pbkdf2" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ "digest", "hmac", "password-hash", "sha2", ] [[package]] name = "pem" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ "base64 0.22.1", "serde", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", "phf_shared", ] [[package]] name = "phf_codegen" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" dependencies = [ "phf_generator", "phf_shared", ] [[package]] name = "phf_generator" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", "rand", ] [[package]] name = "phf_macros" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "phf_shared" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ "siphasher 0.3.11", ] [[package]] name = "pin-project" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "privdrop" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bc12de3935536ed9b69488faea4450a298dac44179b54f71806e63f55034bf9" dependencies = [ "libc", "nix", ] [[package]] name = "proc-macro2" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] [[package]] name = "prost" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", "prost-derive", ] [[package]] name = "prost-derive" version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "proxy-header" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e220ac9305411757d06712209b7c2d1d35c3a1a577301e87855f6219585ecb" dependencies = [ "pin-project-lite", "tokio", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-xml" version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" dependencies = [ "memchr", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "r2d2" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" dependencies = [ "log", "parking_lot", "scheduled-thread-pool", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "rcgen" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" dependencies = [ "pem", "ring 0.17.8", "time", "yasna", ] [[package]] name = "redox_syscall" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ "bitflags 2.5.0", ] [[package]] name = "regex" version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", "regex-syntax 0.8.3", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.3", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", "http", "http-body", "hyper", "hyper-rustls", "ipnet", "js-sys", "log", "mime", "once_cell", "percent-encoding", "pin-project-lite", "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "webpki-roots 0.25.4", "winreg", ] [[package]] name = "resolv-conf" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", "quick-error", ] [[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.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", "getrandom", "libc", "spin 0.9.8", "untrusted 0.9.0", "windows-sys 0.52.0", ] [[package]] name = "roaring" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b26f4c25a604fcb3a1bcd96dd6ba37c93840de95de8198d94c0d571a74a804d1" dependencies = [ "bytemuck", "byteorder", ] [[package]] name = "rusqlite" version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" dependencies = [ "bitflags 2.5.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", "libsqlite3-sys", "smallvec", ] [[package]] name = "rust-stemmers" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" dependencies = [ "serde", "serde_derive", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rusticata-macros" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ "nom", ] [[package]] name = "rustls" version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls" version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.4", "subtle", "zeroize", ] [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64 0.21.7", ] [[package]] name = "rustls-pemfile" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "rustls-webpki" version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring 0.17.8", "rustls-pki-types", "untrusted 0.9.0", ] [[package]] name = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scheduled-thread-pool" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ "parking_lot", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "serde_json" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smtp-proto" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b8ad3dd187f0d4debab02ad65405a9919d6a4f7bce25bd64a258781063a53a" [[package]] name = "socket2" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", ] [[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "store" version = "0.1.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=53f0222f308b3e844c158fc0e603d10361da3c63#53f0222f308b3e844c158fc0e603d10361da3c63" dependencies = [ "ahash", "arc-swap", "async-trait", "bincode", "blake3", "farmhash", "flate2", "lazy_static", "lru-cache", "lz4_flex", "nlp", "num_cpus", "parking_lot", "r2d2", "rand", "rayon", "regex", "reqwest", "roaring", "rusqlite", "serde", "tokio", "tracing", "utils", "xxhash-rust", ] [[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "synstructure" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", "unicode-xid", ] [[package]] name = "system-configuration" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "thiserror" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "thread_local" version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "time" version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", ] [[package]] name = "tinysegmenter" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1755695d17d470baf2d937a59ab4e86de3034b056fc8700e21411b0efca36497" dependencies = [ "lazy_static", "maplit", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-io-timeout" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ "pin-project-lite", "tokio", ] [[package]] name = "tokio-macros" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "tokio-rustls" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls 0.21.12", "tokio", ] [[package]] name = "tokio-rustls" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ "rustls 0.22.4", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] [[package]] name = "tokio-util" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tonic" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum", "base64 0.21.7", "bytes", "futures-core", "futures-util", "h2", "http", "http-body", "hyper", "hyper-timeout", "percent-encoding", "pin-project", "prost", "tokio", "tokio-stream", "tower", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand", "slab", "tokio", "tokio-util", "tower-layer", "tower-service", "tracing", ] [[package]] name = "tower-layer" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-appender" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", "thiserror", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-journald" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd" dependencies = [ "libc", "tracing-core", "tracing-subscriber", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-opentelemetry" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" dependencies = [ "js-sys", "once_cell", "opentelemetry", "opentelemetry_sdk", "smallvec", "tracing", "tracing-core", "tracing-log", "tracing-subscriber", "web-time", ] [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "try-lock" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "twox-hash" version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "static_assertions", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-xid" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna 0.5.0", "percent-encoding", ] [[package]] name = "urlencoding" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utils" version = "0.6.0" source = "git+https://github.com/stalwartlabs/mail-server?rev=53f0222f308b3e844c158fc0e603d10361da3c63#53f0222f308b3e844c158fc0e603d10361da3c63" dependencies = [ "ahash", "arc-swap", "base64 0.21.7", "blake3", "chrono", "dashmap", "futures", "mail-auth", "mail-send", "opentelemetry", "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", "parking_lot", "pem", "privdrop", "proxy-header", "rand", "rcgen", "regex", "reqwest", "ring 0.17.8", "rustls 0.22.4", "rustls-pemfile 2.1.2", "rustls-pki-types", "serde", "serde_json", "smtp-proto", "tokio", "tokio-rustls 0.25.0", "tracing", "tracing-appender", "tracing-journald", "tracing-opentelemetry", "tracing-subscriber", "webpki-roots 0.26.1", "x509-parser", ] [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.66", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "web-time" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "webpki-roots" version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" dependencies = [ "rustls-pki-types", ] [[package]] name = "whatlang" version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "471d1c1645d361eb782a1650b1786a8fb58dd625e681a04c09f5ff7c8764a7b0" dependencies = [ "hashbrown 0.14.5", "once_cell", ] [[package]] name = "widestring" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winreg" version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] [[package]] name = "x509-parser" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" dependencies = [ "asn1-rs", "data-encoding", "der-parser", "lazy_static", "nom", "oid-registry", "rusticata-macros", "thiserror", "time", ] [[package]] name = "xxhash-rust" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" [[package]] name = "yasna" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ "time", ] [[package]] name = "zerocopy" version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "aes", "byteorder", "bzip2", "constant_time_eq 0.1.5", "crc32fast", "crossbeam-utils", "flate2", "hmac", "pbkdf2", "sha1", "time", "zstd", ] [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" version = "5.0.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" dependencies = [ "libc", "zstd-sys", ] [[package]] name = "zstd-sys" version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", ] duesee-imap-codec-0d00966/imap-codec/Cargo.toml000066400000000000000000000060521507724125200211760ustar00rootroot00000000000000[package] name = "imap-codec" description = "Rock-solid and complete codec for IMAP" keywords = ["email", "imap", "codec", "parser"] categories = ["email", "parser-implementations", "encoding", "network-programming"] version = "2.0.0-alpha.6" authors = ["Damian Poddebniak "] repository = "https://github.com/duesee/imap-codec" license = "MIT OR Apache-2.0" rust-version.workspace = true edition = "2024" exclude = [ ".github", ] [features] default = ["quirk"] # Expose internal parsers for fuzzing fuzz = [] # IMAP quirks # # These features bypass interoperability issues to allow safe processing of *almost* correct message. quirk = [ #"quirk_crlf_relaxed", #"quirk_id_empty_to_nil", "quirk_missing_text", "quirk_rectify_numbers", "quirk_excessive_space_quota_resource", "quirk_trailing_space_status", "quirk_trailing_space_capability", "quirk_trailing_space_id", "quirk_trailing_space_search", "quirk_spaces_between_addresses", "quirk_empty_continue_req", "quirk_body_fld_enc_nil_to_empty", ] # Make `\r` in `\r\n` optional. quirk_crlf_relaxed = [] # Encode `A ID ()` as `A ID NIL` quirk_id_empty_to_nil = [] # Add missing `text` by adding [" "] "". quirk_missing_text = [] # Rectify (invalid) numbers. quirk_rectify_numbers = [] # Accept excessive space in quota-resource quirk_excessive_space_quota_resource = [] # Accept spaces between envelope addresses in `FETCH` data response. quirk_spaces_between_addresses = [] # Accept a trailing space in `STATUS` data response. quirk_trailing_space_status = [] # Accept a trailing space in `CAPABILITY` data response. quirk_trailing_space_capability = [] # Accept a trailing space in `ID` data response. quirk_trailing_space_id = [] # Accept a trailing space in `SEARCH` data response. quirk_trailing_space_search = [] # Accept continuation request commands without required space `+\r\n` quirk_empty_continue_req = [] # Encode NIL `body-fld-enc` as empty string. quirk_body_fld_enc_nil_to_empty = [] # arbitrary = ["imap-types/arbitrary"] arbitrary_simplified = ["imap-types/arbitrary_simplified"] serde = ["imap-types/serde"] tag_generator = ["imap-types/tag_generator"] # IMAP starttls = ["imap-types/starttls"] ext_condstore_qresync = ["imap-types/ext_condstore_qresync"] ext_id = ["imap-types/ext_id"] ext_login_referrals = ["imap-types/ext_login_referrals"] ext_mailbox_referrals = ["imap-types/ext_mailbox_referrals"] ext_metadata = ["imap-types/ext_metadata"] ext_namespace = ["imap-types/ext_namespace"] ext_utf8 = ["imap-types/ext_utf8"] # [dependencies] abnf-core = "0.6.0" base64 = { version = "0.22", default-features = false, features = ["alloc"] } chrono = { version = "0.4", default-features = false, features = ["alloc"] } imap-types = { version = "2.0.0-alpha.5", path = "../imap-types", default-features = false } nom = { version = "7", default-features = false } log = { version = "0.4.28", default-features = false } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] duesee-imap-codec-0d00966/imap-codec/README.md000066400000000000000000000046661507724125200205360ustar00rootroot00000000000000# imap-codec ```mermaid %%{init: {'theme': 'neutral' } }%% flowchart LR imap-types --> imap-codec imap-codec -.-> imap-next imap-next -.-> imap-proxy imap-next -.-> imap-client style imap-codec stroke-width:4px click imap-types href "https://github.com/duesee/imap-codec/tree/main/imap-types" click imap-codec href "https://github.com/duesee/imap-codec" click imap-next href "https://github.com/duesee/imap-next" click imap-proxy href "https://github.com/duesee/imap-proxy" click imap-client href "https://github.com/soywod/imap-client" ``` This library provides parsing and serialization for [IMAP4rev1]. It is based on [`imap-types`] and a [rock-solid] and [well-documented] building block for IMAP client and server implementations in Rust. The complete [formal syntax] of IMAP4rev1 and several IMAP [extensions] are implemented. If you are looking for thin protocol handling, i.e., LITERAL handling, AUTHENTICATE, and IDLE, head over to [`imap-next`]. ## Features * Parsing works in streaming mode. `Incomplete` is returned when there is insufficient data to make a final decision. No message will be truncated. * Parsing is zero-copy by default. Allocation is avoided during parsing, but all messages can explicitly be converted into more flexible owned variants. * Fuzzing and property-based tests exercise the library. The library is fuzz-tested never to produce a message it can't parse itself. ## Usage ```rust use imap_codec::{decode::Decoder, encode::Encoder, CommandCodec}; fn main() { let input = b"ABCD UID FETCH 1,2:* (BODY.PEEK[1.2.3.4.MIME]<42.1337>)\r\n"; let codec = CommandCodec::new(); let (remainder, command) = codec.decode(input).unwrap(); println!("# Parsed\n\n{:#?}\n\n", command); let buffer = codec.encode(&command).dump(); // Note: IMAP4rev1 may produce messages that are not valid UTF-8. println!("# Serialized\n\n{:?}", std::str::from_utf8(&buffer)); } ``` # License This crate is dual-licensed under Apache 2.0 and MIT terms. [IMAP4rev1]: https://tools.ietf.org/html/rfc3501 [`imap-types`]: https://docs.rs/imap-types/latest/imap_types/ [`imap-next`]: https://github.com/duesee/imap-next [rock-solid]: https://github.com/duesee/imap-codec/tree/main/imap-codec/fuzz [well-documented]: https://docs.rs/imap-codec/latest/imap_codec/ [formal syntax]: https://tools.ietf.org/html/rfc3501#section-9 [extensions]: https://docs.rs/imap-codec/latest/imap_codec/#features duesee-imap-codec-0d00966/imap-codec/benchmark/000077500000000000000000000000001507724125200211755ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/benchmark/Cargo.toml000066400000000000000000000012021507724125200231200ustar00rootroot00000000000000[package] name = "imap-codec-bench" version = "0.0.0" publish = false edition = "2024" license = "MIT OR Apache-2.0" [dev-dependencies] imap-types = { path = "../../imap-types" } imap-codec = { path = "../../imap-codec" } criterion = "0.7.0" imap-proto = "0.16.6" imap-proto-stalwart = { package = "imap_proto", git = "https://github.com/stalwartlabs/mail-server", rev = "3b950cecec01b4b1083cc900d6742c11a665afab" } tokio = { version = "*", features = ["signal"] } [[bench]] name = "greeting" harness = false [[bench]] name = "command" harness = false [[bench]] name = "response" harness = false [[bench]] name = "trace" harness = false duesee-imap-codec-0d00966/imap-codec/benchmark/benches/000077500000000000000000000000001507724125200226045ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/benchmark/benches/command.rs000066400000000000000000000056471507724125200246040ustar00rootroot00000000000000use criterion::{Criterion, black_box, criterion_group, criterion_main}; use imap_codec::{CommandCodec, decode::Decoder, encode::Encoder, imap_types::command::Command}; use imap_proto_stalwart::{ Command as ImapProtoStalwartCommand, receiver::Receiver as ImapProtoStalwartReceiver, }; use imap_types::{ command::CommandBody, core::{Charset, Tag, Vec1}, search::SearchKey, sequence::{Sequence, SequenceSet}, }; fn criterion_benchmark(c: &mut Criterion) { // # Setup let codec = CommandCodec::new(); let instances = [("simple", create_simple()), ("complex", create_complex())]; for (instance, object) in instances { c.bench_function( format!("bench_command_serialize_{instance}").as_str(), |b| b.iter(|| serialize(&codec, &object)), ); let input = serialize(&codec, &object); c.bench_function(format!("bench_command_parse_{instance}").as_str(), |b| { b.iter(|| parse(&codec, black_box(&input[..]))) }); // Note: We don't get a fully serialized command with stalwart here. This hinders comparison ... // // ```rust // Request { // tag: "A", // command: Search(true), // tokens: [ // Argument([67, 72, 65, 82, 83, 69, 84]), // CHARSET // Argument([85, 84, 70, 45, 56]), // UTF-8 // Argument([49, 58, 52, 50, 44, 52, 50, 58, 49, 51, 51, 55, 44, 49, 51, 51, 55, 58, 42]), // 1:42,42:1337,1337:* // ] // } // ``` let input = serialize(&codec, &object); let mut receiver: ImapProtoStalwartReceiver = ImapProtoStalwartReceiver::new(); c.bench_function( format!("bench_command_parse_{instance}_imap_proto_stalwart").as_str(), |b| b.iter(|| receiver.parse(black_box(&mut input.iter())).unwrap()), ); } } fn create_simple() -> Command<'static> { Command::new(Tag::unvalidated("A"), CommandBody::Noop).unwrap() } fn create_complex() -> Command<'static> { Command::new( Tag::unvalidated("A"), CommandBody::search( Some(Charset::try_from("UTF-8").unwrap()), Vec1::try_from(vec![SearchKey::SequenceSet(SequenceSet( Vec1::try_from(vec![ Sequence::try_from("1:42").unwrap(), Sequence::try_from("42:1337").unwrap(), Sequence::try_from("1337:*").unwrap(), ]) .unwrap(), ))]) .unwrap(), true, ), ) .unwrap() } #[inline] fn serialize(codec: &CommandCodec, object: &Command) -> Vec { codec.encode(object).dump() } #[inline] fn parse<'a>(codec: &CommandCodec, input: &'a [u8]) -> Command<'a> { let (_, cmd) = codec.decode(input).unwrap(); cmd } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); duesee-imap-codec-0d00966/imap-codec/benchmark/benches/greeting.rs000066400000000000000000000033721507724125200247630ustar00rootroot00000000000000use criterion::{Criterion, black_box, criterion_group, criterion_main}; use imap_codec::{GreetingCodec, decode::Decoder, encode::Encoder, imap_types::response::Greeting}; use imap_types::{ auth::AuthMechanism, core::{Text, Vec1}, response::{Capability, Code, GreetingKind}, }; fn criterion_benchmark(c: &mut Criterion) { // # Setup let codec = GreetingCodec::new(); let instances = [("simple", create_simple()), ("complex", create_complex())]; for (instance, object) in instances { c.bench_function( format!("bench_greeting_serialize_{instance}").as_str(), |b| b.iter(|| serialize(&codec, &object)), ); let input = serialize(&codec, &object); c.bench_function(format!("bench_greeting_parse_{instance}").as_str(), |b| { b.iter(|| parse(&codec, black_box(&input[..]))) }); } } fn create_simple() -> Greeting<'static> { Greeting { kind: GreetingKind::Ok, code: None, text: Text::unvalidated("."), } } fn create_complex() -> Greeting<'static> { Greeting { kind: GreetingKind::PreAuth, code: Some(Code::Capability( Vec1::try_from(vec![ Capability::Imap4Rev1, Capability::Auth(AuthMechanism::Login), Capability::Auth(AuthMechanism::Plain), ]) .unwrap(), )), text: Text::unvalidated("."), } } #[inline] fn serialize(codec: &GreetingCodec, object: &Greeting) -> Vec { codec.encode(object).dump() } #[inline] fn parse<'a>(codec: &GreetingCodec, input: &'a [u8]) -> Greeting<'a> { let (_, cmd) = codec.decode(input).unwrap(); cmd } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); duesee-imap-codec-0d00966/imap-codec/benchmark/benches/response.rs000066400000000000000000000033711507724125200250140ustar00rootroot00000000000000use std::num::NonZeroU32; use criterion::{Criterion, black_box, criterion_group, criterion_main}; use imap_codec::{ResponseCodec, decode::Decoder, encode::Encoder, imap_types::response::Response}; use imap_proto::Response as ImapProtoResponse; use imap_types::{core::Vec1, fetch::MessageDataItem, response::Data}; fn criterion_benchmark(c: &mut Criterion) { // # Setup let codec = ResponseCodec::new(); let instances = [("simple", create_simple()), ("complex", create_complex())]; for (instance, object) in instances { c.bench_function( format!("bench_response_serialize_{instance}").as_str(), |b| b.iter(|| serialize(&codec, &object)), ); let input = serialize(&codec, &object); c.bench_function(format!("bench_response_parse_{instance}").as_str(), |b| { b.iter(|| parse(&codec, black_box(&input[..]))) }); let input = serialize(&codec, &object); c.bench_function( format!("bench_response_parse_{instance}_imap_proto").as_str(), |b| b.iter(|| ImapProtoResponse::from_bytes(black_box(&input[..]))), ); } } fn create_simple() -> Response<'static> { Response::Data(Data::Exists(0)) } fn create_complex() -> Response<'static> { Response::Data(Data::Fetch { seq: NonZeroU32::try_from(u32::MAX).unwrap(), items: Vec1::try_from(vec![MessageDataItem::Rfc822Size(0)]).unwrap(), }) } #[inline] fn serialize(codec: &ResponseCodec, object: &Response) -> Vec { codec.encode(object).dump() } #[inline] fn parse<'a>(codec: &ResponseCodec, input: &'a [u8]) -> Response<'a> { let (_, cmd) = codec.decode(input).unwrap(); cmd } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); duesee-imap-codec-0d00966/imap-codec/benchmark/benches/trace.rs000066400000000000000000000106331507724125200242530ustar00rootroot00000000000000use criterion::{Criterion, criterion_group, criterion_main}; use imap_codec::{CommandCodec, GreetingCodec, ResponseCodec, decode::Decoder}; use imap_types::{ command::Command, response::{Greeting, Response}, }; fn criterion_benchmark(c: &mut Criterion) { // # Setup let grt_codec = GreetingCodec::new(); let cmd_codec = CommandCodec::new(); let rsp_codec = ResponseCodec::new(); let instances = create(); c.bench_function("bench_trace", |b| { b.iter(|| { let mut tmp = Vec::new(); for (input, message) in &instances { tmp.push(match message { Message::Greeting => MessageOut::Greeting(grt_codec.decode(input).unwrap().1), Message::Command => MessageOut::Command(cmd_codec.decode(input).unwrap().1), Message::Response => MessageOut::Response(rsp_codec.decode(input).unwrap().1), }); } tmp }) }); } enum Message { Greeting, Command, Response, } enum MessageOut<'a> { #[allow(unused)] Greeting(Greeting<'a>), #[allow(unused)] Command(Command<'a>), #[allow(unused)] Response(Response<'a>), } fn create() -> Vec<(&'static [u8], Message)> { vec![ ( b"* OK IMAP4rev1 Service Ready\r\n".as_ref(), Message::Greeting ), ( b"a001 login mrc secret\r\n", Message::Command ), ( b"a001 OK LOGIN completed\r\n", Message::Response ), ( b"a002 select inbox\r\n", Message::Command ), ( b"* 18 EXISTS\r\n", Message::Response ), ( b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n", Message::Response ), ( b"* 2 RECENT\r\n", Message::Response ), ( b"* OK [UNSEEN 17] Message 17 is the first unseen message\r\n", Message::Response ), ( b"* OK [UIDVALIDITY 3857529045] UIDs valid\r\n", Message::Response ), ( b"a002 OK [READ-WRITE] SELECT completed\r\n", Message::Response ), ( b"a003 fetch 12 full\r\n", Message::Command ), ( b"* 12 FETCH (FLAGS (\\Seen) INTERNALDATE \"17-Jul-1996 02:44:25 -0700\" RFC822.SIZE 4286 ENVELOPE (\"Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\" \"IMAP4rev1 WG mtg summary and minutes\" ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((NIL NIL \"imap\" \"cac.washington.edu\")) ((NIL NIL \"minutes\" \"CNRI.Reston.VA.US\")(\"John Klensin\" NIL \"KLENSIN\" \"MIT.EDU\")) NIL NIL \"\") BODY (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 3028 92))\r\n", Message::Response ), ( b"a003 OK FETCH completed\r\n", Message::Response ), ( b"a004 fetch 12 body[header]\r\n", Message::Command ), ( b"* 12 FETCH (BODY[HEADER] {342}\r Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\r From: Terry Gray \r Subject: IMAP4rev1 WG mtg summary and minutes\r To: imap@cac.washington.edu\r cc: minutes@CNRI.Reston.VA.US, John Klensin \r Message-Id: \r MIME-Version: 1.0\r Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r \r )\r\n", Message::Response ), ( b"a004 OK FETCH completed\r\n", Message::Response ), ( b"a005 store 12 +flags \\deleted\r\n", Message::Command, ), ( b"* 12 FETCH (FLAGS (\\Seen \\Deleted))\r\n", Message::Response, ), ( b"a005 OK +FLAGS completed\r\n", Message::Response, ), ( b"a006 logout\r\n", Message::Command, ), ( b"* BYE IMAP4rev1 server terminating connection\r\n", Message::Response, ), ( b"a006 OK LOGOUT completed\r\n", Message::Response, ), ] } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); duesee-imap-codec-0d00966/imap-codec/examples/000077500000000000000000000000001507724125200210615ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/examples/client.rs000066400000000000000000000046351507724125200227150ustar00rootroot00000000000000use imap_codec::{GreetingCodec, ResponseCodec, fragmentizer::Fragmentizer}; #[path = "common/common.rs"] mod common; use common::read_more; use crate::common::Role; enum State { Greeting, Response, } const WELCOME: &str = r#"# Parsing of IMAP greeting and responses "S:" denotes the server. ".." denotes the continuation of an (incomplete) response, e.g., due to the use of an IMAP literal. Note: "\n" will be automatically replaced by "\r\n". -------------------------------------------------------------------------------------------------- Enter intial IMAP greeting followed by IMAP responses (or "exit"). "#; fn main() { println!("{WELCOME}"); let mut fragmentizer = Fragmentizer::new(10 * 1024); let mut state = State::Greeting; loop { // Progress next fragment. let Some(_fragment_info) = fragmentizer.progress() else { // Read more bytes ... let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty()); // ... and pass the bytes to the Fragmentizer ... fragmentizer.enqueue_bytes(&bytes); // ... and try again. continue; }; // Check whether the Fragmentizer detected a complete message. if !fragmentizer.is_message_complete() { // Read next fragment. continue; } // The Fragmentizer detected a complete message. match state { State::Greeting => { match fragmentizer.decode_message(&GreetingCodec::default()) { Ok(greeting) => { // Do something with the greeting ... println!("{greeting:#?}"); // ... and proceed with reponses. state = State::Response; } Err(err) => { println!("Error parsing greeting: {err:?}"); } }; } State::Response => { match fragmentizer.decode_message(&ResponseCodec::default()) { Ok(response) => { // Do something with the response. println!("{response:#?}"); } Err(err) => { println!("Error parsing response: {err:?}"); } }; } }; } } duesee-imap-codec-0d00966/imap-codec/examples/common/000077500000000000000000000000001507724125200223515ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/examples/common/common.rs000066400000000000000000000030271507724125200242110ustar00rootroot00000000000000#![allow(dead_code)] use std::io::Write; pub const COLOR_SERVER: &str = "\x1b[34m"; pub const COLOR_CLIENT: &str = "\x1b[31m"; pub const RESET: &str = "\x1b[0m"; #[derive(Clone, Copy, Debug)] pub enum Role { Client, Server, } pub fn read_more(role: Role, message_begin: bool) -> Vec { let prompt = if message_begin { match role { Role::Client => "C: ", Role::Server => "S: ", } } else { ".. " }; let line = read_line(prompt, role); // If `read_line` produces an empty string, standard input has been closed. if line.is_empty() || line.trim() == "exit" { println!("Exiting."); std::process::exit(0); } line.into_bytes() } fn read_line(prompt: &str, role: Role) -> String { match role { Role::Client => print!("{prompt}{COLOR_CLIENT}"), Role::Server => print!("{prompt}{COLOR_SERVER}"), } std::io::stdout().flush().unwrap(); let mut line = String::new(); std::io::stdin().read_line(&mut line).unwrap(); print!("{RESET}"); // If `Stdin::read_line` produces an empty string, standard input has been closed. if line.is_empty() { return line; } // Ensure `CRLF` line ending of resulting string. // Line ending of `line` can be one of: // - `CRLF` on Windows // - `LF` on Unix-like // - none when EOF of standard input is reached if line.ends_with("\r\n") { return line; } if line.ends_with('\n') { line.pop(); } line + "\r\n" } duesee-imap-codec-0d00966/imap-codec/examples/fragmentizer.rs000066400000000000000000000025251507724125200241300ustar00rootroot00000000000000use std::io::{Read, stdin}; use imap_codec::{fragmentizer::Fragmentizer, imap_types::utils::escape_byte_string}; fn main() { let mut fragmentizer = Fragmentizer::new(1024); loop { match fragmentizer.progress() { Some(fragment_info) => { println!( "[!] Fragment: {fragment_info:#?} // b\"{}\"", escape_byte_string(fragmentizer.fragment_bytes(fragment_info)) ); if fragmentizer.is_message_complete() { println!( "[!] Complete message: {}", escape_byte_string(fragmentizer.message_bytes()) ); } } None => { println!("[!] Reading stdin (ctrl+d to flush)..."); let mut buffer = [0; 64]; let count = stdin().read(&mut buffer).unwrap(); if count == 0 { println!("[!] Connection closed"); break; } let chunk = &buffer[..count]; println!( "[!] Enqueueing {} byte(s) (b\"{}\")", count, escape_byte_string(chunk) ); fragmentizer.enqueue_bytes(chunk); } } } } duesee-imap-codec-0d00966/imap-codec/examples/server.rs000066400000000000000000000124261507724125200227420ustar00rootroot00000000000000use imap_codec::{ AuthenticateDataCodec, CommandCodec, IdleDoneCodec, fragmentizer::{FragmentInfo, Fragmentizer, LiteralAnnouncement}, }; #[path = "common/common.rs"] mod common; use common::{COLOR_SERVER, RESET, read_more}; use imap_types::{ IntoStatic, command::{Command, CommandBody}, core::{LiteralMode, Tag}, }; use crate::common::Role; enum State { Command, Authenticate(Tag<'static>), Idle, } const WELCOME: &str = r#"# Parsing of IMAP commands "C:" denotes the client, "S:" denotes the server, and ".." denotes the continuation of an (incomplete) command, e.g., due to the use of an IMAP literal. Note: "\n" will be automatically replaced by "\r\n". -------------------------------------------------------------------------------------------------- Enter IMAP commands (or "exit"). "#; fn main() { println!("{WELCOME}"); let mut fragmentizer = Fragmentizer::new(10 * 1024); let mut state = State::Command; // Send a greeting. println!("S: {COLOR_SERVER}* OK ...{RESET}"); loop { // Progress next fragment. let Some(fragment_info) = fragmentizer.progress() else { // Read more bytes ... let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty()); // ... and pass the bytes to the Fragmentizer ... fragmentizer.enqueue_bytes(&bytes); // ... and try again. continue; }; // The Fragmentizer detected a line that announces a sync literal. if let FragmentInfo::Line { announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length, }), .. } = fragment_info { // Check the length of the literal. if length <= 1024 { // Accept the literal ... println!("S: {COLOR_SERVER}+ {RESET}"); // ... and continue with the remaining message. continue; } else if let Some(tag) = fragmentizer.decode_tag() { // Reject the literal ... println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref()); // ... and skip the current message ... fragmentizer.skip_message(); // ... and continue with the next message. continue; } else { // The partially received message is malformed. It's unclear what will follow. // To be on the safe side, prevent the message from being decoded ... fragmentizer.poison_message(); // ... but continue parsing the message. continue; } } // Check whether the Fragmentizer detected a complete message. if !fragmentizer.is_message_complete() { // Read next fragment. continue; } // The Fragmentizer detected a complete message. match state { State::Command => { match fragmentizer.decode_message(&CommandCodec::default()) { Ok(Command { tag, body: CommandBody::Authenticate { .. }, }) => { // Request another SASL round ... println!("S: {COLOR_SERVER}+ {RESET}"); // ... and proceed with authenticate data. state = State::Authenticate(tag.into_static()); } Ok(Command { body: CommandBody::Idle, .. }) => { // Accept the idle ... println!("S: {COLOR_SERVER}+ ...{RESET}"); // ... and proceed with idle done. state = State::Idle; } Ok(command) => { // Do something with the command. println!("{command:#?}"); } Err(err) => { println!("Error parsing command: {err:?}"); } }; } State::Authenticate(ref tag) => { match fragmentizer.decode_message(&AuthenticateDataCodec::default()) { Ok(_authenticate_data) => { // Accept the authentication after one SASL round. println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref()); // ... and proceed with commands. state = State::Command; } Err(err) => { println!("Error parsing authenticate data: {err:?}"); } }; } State::Idle => { match fragmentizer.decode_message(&IdleDoneCodec::default()) { Ok(_idle_done) => { // End idle and proceed with commands. state = State::Command; } Err(err) => { println!("Error parsing idle done: {err:?}"); } }; } } } } duesee-imap-codec-0d00966/imap-codec/fuzz/000077500000000000000000000000001507724125200202415ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/fuzz/.gitignore000066400000000000000000000000311507724125200222230ustar00rootroot00000000000000 target corpus artifacts duesee-imap-codec-0d00966/imap-codec/fuzz/Cargo.toml000066400000000000000000000050471507724125200221770ustar00rootroot00000000000000[package] name = "imap-codec-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false edition = "2024" license = "MIT OR Apache-2.0" [package.metadata] cargo-fuzz = true [features] # arbitrary_simplified = ["imap-types/arbitrary_simplified"] # # # IMAP starttls = ["imap-codec/starttls"] # IMAP Extensions ext_condstore_qresync = ["imap-codec/ext_condstore_qresync"] ext_id = ["imap-codec/ext_id"] ext_login_referrals = ["imap-codec/ext_login_referrals"] ext_mailbox_referrals = ["imap-codec/ext_mailbox_referrals"] ext_metadata = ["imap-codec/ext_metadata"] ext_namespace = ["imap-codec/ext_namespace"] ext_utf8 = ["imap-codec/ext_utf8"] # IMAP quirks quirk_crlf_relaxed = ["imap-codec/quirk_crlf_relaxed"] # # Use (most) IMAP extensions. ext = [ "starttls", # TODO: Disabled due to #633 #"ext_condstore_qresync", "ext_id", #"ext_login_referrals", #"ext_mailbox_referrals", "ext_metadata", "ext_namespace", "ext_utf8", ] # Enable `Debug`-printing during parsing. This is useful to analyze crashes. debug = [] # Enable testing of incomplete fragments. split = [] [dependencies] arbitrary = "1.4.2" imap-codec = { path = "..", features = ["fuzz"] } imap-types = { path = "../../imap-types", features = ["arbitrary"] } libfuzzer-sys = "0.4" [[bin]] name = "fragmentizer" path = "fuzz_targets/fragmentizer.rs" test = false doc = false bench = false [[bin]] name = "greeting" path = "fuzz_targets/greeting.rs" test = false doc = false bench = false [[bin]] name = "command" path = "fuzz_targets/command.rs" test = false doc = false bench = false [[bin]] name = "response" path = "fuzz_targets/response.rs" test = false doc = false bench = false [[bin]] name = "authenticate_data" path = "fuzz_targets/authenticate_data.rs" test = false doc = false bench = false [[bin]] name = "greeting_to_bytes_and_back" path = "fuzz_targets/greeting_to_bytes_and_back.rs" test = false doc = false bench = false [[bin]] name = "command_to_bytes_and_back" path = "fuzz_targets/command_to_bytes_and_back.rs" test = false doc = false bench = false [[bin]] name = "response_to_bytes_and_back" path = "fuzz_targets/response_to_bytes_and_back.rs" test = false doc = false bench = false [[bin]] name = "authenticate_data_to_bytes_and_back" path = "fuzz_targets/authenticate_data_to_bytes_and_back.rs" test = false doc = false bench = false [[bin]] name = "tags_structured" path = "fuzz_targets/tags_structured.rs" test = false doc = false bench = false duesee-imap-codec-0d00966/imap-codec/fuzz/README.md000066400000000000000000000261251507724125200215260ustar00rootroot00000000000000# Fuzzing ## Setup Cargo fuzz requires nightly and `cargo-fuzz`. Install via ... ```sh rustup install nightly cargo install cargo-fuzz ``` ## Provided fuzz targets You can start the fuzzing process by running ... ```sh cargo +nightly fuzz run ``` ... with `` being ... | Name of the fuzz target `` | Purpose | Expectation | |---------------------------------------|------------------------|----------------| | `greeting` | Test parsing | Must not fail. | | `command` | Test parsing | Must not fail. | | `response` | Test parsing | Must not fail. | | `authenticate_data` | Test parsing | Must not fail. | | `idle_done` | Test parsing | Must not fail. | | `greeting_to_bytes_and_back` | Test misuse-resistance | Must not fail. | | `command_to_bytes_and_back` | Test misuse-resistance | Must not fail. | | `response_to_bytes_and_back` | Test misuse-resistance | Must not fail. | | `authenticate_data_to_bytes_and_back` | Test misuse-resistance | Must not fail. | | `idle_done_to_bytes_and_back` | Test misuse-resistance | Must not fail. | Three first five fuzz targets are used to test the parsing routines. The fuzzers all do the same: try to parse the input from libFuzzer (and hope that the parsers don't crash), then, if parsing was successful, serialize the obtained object (and hope that the serialization routines don't crash), and then, parse the serialized output again and compare it to the first one (and hoping that they match). This is motivated by the fact, that the library must certainly be able to parse the data it has produced on its own. The last five fuzz targets are used to test for misuse-resistance. The `Greeting`/`Command`/`Response`/... structs implements the `Arbitrary` trait that will produce a random instance of the type. Any instance generated in this way must be parsable and valid. It should not be possible to create a message object via the API, which is invalid according to the IMAP specification. If a crash was found, it is helpful to use the `debug` feature and rerun the crashing input. ## Try to be more effective * Use `terminals.dict` as fuzzing dictionary. It contains all terminals (>1 character) from the IMAP4rev1 formal syntax and ABNFs core rules. * The `imap.dict` dictionary contains a full IMAP trace. `blns.dict` is the "big list of naughty strings". * Decrease the the input size to e.g. 64 bytes. Short inputs might still trigger complex parsing routines. * Use multiple processes. * Try to use `-ascii_only` to exclude inputs, which are less likely to be valid (useful to test serializing.) ```sh cargo +nightly fuzz run -j 32 -- -dict=terminals.dict -max_len=64 -only_ascii=1 ``` ## Structured fuzzing with `Arbitrary` These beautiful commands ... ```rust Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, And([Answered, Deleted, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft]+)]+, uid: true } } // z UID SEARCH UNDRAFT UNDRAFT (ANSWERED DELETED UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT)\r\n Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, And([Answered, Deleted, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft]+)]+, uid: true } } ------------------------------------------------------------------------------------------------------------------------ Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Cc(String(Quoted(Quoted("")))), All]+, uid: false } } // z SEARCH UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT CC \"\" ALL\r\n Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Cc(String(Quoted(Quoted("")))), All]+, uid: false } } ------------------------------------------------------------------------------------------------------------------------ Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Cc(String(Literal(Literal { data: b"\x97\x97", mode: Sync })))]+, uid: false } } // z SEARCH UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT CC {2}\r\n\x97\x97\r\n Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Undraft, Cc(String(Literal(Literal { data: b"\x97\x97", mode: Sync })))]+, uid: false } } ------------------------------------------------------------------------------------------------------------------------ Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, Undraft, Undraft, Undraft, All, Undraft, Undraft, Undraft]+, uid: true } } // z UID SEARCH UNDRAFT UNDRAFT UNDRAFT UNDRAFT UNDRAFT ALL UNDRAFT UNDRAFT UNDRAFT\r\n Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Undraft, Undraft, Undraft, Undraft, All, Undraft, Undraft, Undraft]+, uid: true } } ------------------------------------------------------------------------------------------------------------------------ Command { tag: Tag("-"), body: GetMetadata { options: [Depth(Infinity), Depth(Infinity)], mailbox: Other(MailboxOther(String(Quoted(Quoted(""))))), entries: [Entry(Atom(AtomExt("["))), Entry(String(Literal(Literal { data: b"[[", mode: NonSync }))), Entry(String(Quoted(Quoted("")))), Entry(String(Quoted(Quoted(""))))]+ } } // - GETMETADATA (DEPTH INFINITY DEPTH INFINITY) \"\" ([ {2+}\r\n[[ \"\" \"\")\r\n Command { tag: Tag("-"), body: GetMetadata { options: [Depth(Infinity), Depth(Infinity)], mailbox: Other(MailboxOther(String(Quoted(Quoted(""))))), entries: [Entry(Atom(AtomExt("["))), Entry(String(Literal(Literal { data: b"[[", mode: NonSync }))), Entry(String(Quoted(Quoted("")))), Entry(String(Quoted(Quoted(""))))]+ } } ------------------------------------------------------------------------------------------------------------------------ Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Old, Old, Old, Old, Undraft, Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Literal(Literal { data: b"T\xf0&!\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd$-MxM\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd", mode: NonSync }))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("&")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Literal(Literal { data: b"", mode: Sync })))]+, uid: false } } // z SEARCH UNDRAFT OLD OLD OLD OLD UNDRAFT BCC \"\" BCC \"\" BCC \"\" BCC {34+}\r\nT\xf0&!\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd$-MxM\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd BCC \"\" BCC \"&\" BCC \"\" BCC \"\" BCC {0}\r\n\r\n Command { tag: Tag("z"), body: Search { charset: None, criteria: [Undraft, Old, Old, Old, Old, Undraft, Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Literal(Literal { data: b"T\xf0&!\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd$-MxM\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd\xbd", mode: NonSync }))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("&")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Quoted(Quoted("")))), Bcc(String(Literal(Literal { data: b"", mode: Sync })))]+, uid: false } } ------------------------------------------------------------------------------------------------------------------------ Command { tag: Tag("m="), body: Fetch { sequence_set: SequenceSet([Single(Asterisk)]+), macro_or_item_names: MessageDataItemNames([BodyExt { section: Some(HeaderFields(Some(Part([1547436031, 4294967117, 1308622847]+)), [Atom(AtomExt("iiiiiiiiiMM?"))]+)), partial: None, peek: false }, InternalDate, InternalDate, BodyExt { section: Some(Header(None)), partial: Some((1296845901, 4143333197)), peek: false }, BinarySize { section: [761019391, 1296895278] }, BodyExt { section: Some(HeaderFields(Some(Part([1533634921, 1768515945, 1768515945]+)), [Atom(AtomExt("!!?-MMM"))]+)), partial: Some((1296845901, 4143333197)), peek: false }, Flags, InternalDate, InternalDate, BinarySize { section: [1308622847, 4294967117, 1308622847] }, InternalDate, BinarySize { section: [] }, InternalDate, Envelope, BodyExt { section: Some(Header(None)), partial: Some((1296845901, 4143333197)), peek: false }, BinarySize { section: [761019391, 1296895278, 4143333197] }, BinarySize { section: [761019391, 1296895278] }, BodyExt { section: Some(HeaderFields(Some(Part([1768515945, 1298753897, 1280126382]+)), [String(Literal(Literal { data: b"U\xff\xff\xff;\\-M", mode: NonSync }))]+)), partial: None, peek: false }]), uid: false } } // m= FETCH * (BODY[1547436031.4294967117.1308622847.HEADER.FIELDS (iiiiiiiiiMM?)] INTERNALDATE INTERNALDATE BODY[HEADER]<1296845901.4143333197> BINARY.SIZE[761019391.1296895278] BODY[1533634921.1768515945.1768515945.HEADER.FIELDS (!!?-MMM)]<1296845901.4143333197> FLAGS INTERNALDATE INTERNALDATE BINARY.SIZE[1308622847.4294967117.1308622847] INTERNALDATE BINARY.SIZE[] INTERNALDATE ENVELOPE BODY[HEADER]<1296845901.4143333197> BINARY.SIZE[761019391.1296895278.4143333197] BINARY.SIZE[761019391.1296895278] BODY[1768515945.1298753897.1280126382.HEADER.FIELDS ({8+}\r\nU\xff\xff\xff;\\-M)])\r\n Command { tag: Tag("m="), body: Fetch { sequence_set: SequenceSet([Single(Asterisk)]+), macro_or_item_names: MessageDataItemNames([BodyExt { section: Some(HeaderFields(Some(Part([1547436031, 4294967117, 1308622847]+)), [Atom(AtomExt("iiiiiiiiiMM?"))]+)), partial: None, peek: false }, InternalDate, InternalDate, BodyExt { section: Some(Header(None)), partial: Some((1296845901, 4143333197)), peek: false }, BinarySize { section: [761019391, 1296895278] }, BodyExt { section: Some(HeaderFields(Some(Part([1533634921, 1768515945, 1768515945]+)), [Atom(AtomExt("!!?-MMM"))]+)), partial: Some((1296845901, 4143333197)), peek: false }, Flags, InternalDate, InternalDate, BinarySize { section: [1308622847, 4294967117, 1308622847] }, InternalDate, BinarySize { section: [] }, InternalDate, Envelope, BodyExt { section: Some(Header(None)), partial: Some((1296845901, 4143333197)), peek: false }, BinarySize { section: [761019391, 1296895278, 4143333197] }, BinarySize { section: [761019391, 1296895278] }, BodyExt { section: Some(HeaderFields(Some(Part([1768515945, 1298753897, 1280126382]+)), [String(Literal(Literal { data: b"U\xff\xff\xff;\\-M", mode: NonSync }))]+)), partial: None, peek: false }]), uid: false } } ``` ... were generated by ... ```sh cargo +nightly fuzz run --features=ext,debug command_to_bytes_and_back --release ``` Happy fuzzing! # Known crashes None of the targets should crash anymore. However, they already uncovered interesting serialization issues. Please try for yourself and file a bug report if you can do it! # Fuzzing IMAP extensions You can use the `ext` feature to activate most IMAP extensions. Note, however, that some extensions are still experimental and may crash. If so, please file a bug with the crashing input (and enabled `debug` feature). duesee-imap-codec-0d00966/imap-codec/fuzz/blns.dict000066400000000000000000002632761507724125200220640ustar00rootroot00000000000000"\x75\x6e\x64\x65\x66\x69\x6e\x65\x64" "\x75\x6e\x64\x65\x66" "\x6e\x75\x6c\x6c" "\x4e\x55\x4c\x4c" "\x28\x6e\x75\x6c\x6c\x29" "\x6e\x69\x6c" "\x4e\x49\x4c" "\x74\x72\x75\x65" "\x66\x61\x6c\x73\x65" "\x54\x72\x75\x65" "\x46\x61\x6c\x73\x65" "\x54\x52\x55\x45" "\x46\x41\x4c\x53\x45" "\x4e\x6f\x6e\x65" "\x68\x61\x73\x4f\x77\x6e\x50\x72\x6f\x70\x65\x72\x74\x79" "\x74\x68\x65\x6e" "\x5c" "\x5c\x5c" "\x30" "\x31" "\x31\x2e\x30\x30" "\x24\x31\x2e\x30\x30" "\x31\x2f\x32" "\x31\x45\x32" "\x31\x45\x30\x32" "\x31\x45\x2b\x30\x32" "\x2d\x31" "\x2d\x31\x2e\x30\x30" "\x2d\x24\x31\x2e\x30\x30" "\x2d\x31\x2f\x32" "\x2d\x31\x45\x32" "\x2d\x31\x45\x30\x32" "\x2d\x31\x45\x2b\x30\x32" "\x31\x2f\x30" "\x30\x2f\x30" "\x2d\x32\x31\x34\x37\x34\x38\x33\x36\x34\x38\x2f\x2d\x31" "\x2d\x39\x32\x32\x33\x33\x37\x32\x30\x33\x36\x38\x35\x34\x37\x37\x35\x38\x30\x38\x2f\x2d\x31" "\x2d\x30" "\x2d\x30\x2e\x30" "\x2b\x30" "\x2b\x30\x2e\x30" "\x30\x2e\x30\x30" "\x30\x2e\x2e\x30" "\x2e" "\x30\x2e\x30\x2e\x30" "\x30\x2c\x30\x30" "\x30\x2c\x2c\x30" "\x2c" "\x30\x2c\x30\x2c\x30" "\x30\x2e\x30\x2f\x30" "\x31\x2e\x30\x2f\x30\x2e\x30" "\x30\x2e\x30\x2f\x30\x2e\x30" "\x31\x2c\x30\x2f\x30\x2c\x30" "\x30\x2c\x30\x2f\x30\x2c\x30" "\x2d\x2d\x31" "\x2d" "\x2d\x2e" "\x2d\x2c" "\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39\x39" "\x4e\x61\x4e" "\x49\x6e\x66\x69\x6e\x69\x74\x79" "\x2d\x49\x6e\x66\x69\x6e\x69\x74\x79" "\x49\x4e\x46" "\x31\x23\x49\x4e\x46" "\x2d\x31\x23\x49\x4e\x44" "\x31\x23\x51\x4e\x41\x4e" "\x31\x23\x53\x4e\x41\x4e" "\x31\x23\x49\x4e\x44" "\x30\x78\x30" "\x30\x78\x66\x66\x66\x66\x66\x66\x66\x66" "\x30\x78\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" "\x30\x78\x61\x62\x61\x64\x31\x64\x65\x61" "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39" "\x31\x2c\x30\x30\x30\x2e\x30\x30" "\x31\x20\x30\x30\x30\x2e\x30\x30" "\x31\x27\x30\x30\x30\x2e\x30\x30" "\x31\x2c\x30\x30\x30\x2c\x30\x30\x30\x2e\x30\x30" "\x31\x20\x30\x30\x30\x20\x30\x30\x30\x2e\x30\x30" "\x31\x27\x30\x30\x30\x27\x30\x30\x30\x2e\x30\x30" "\x31\x2e\x30\x30\x30\x2c\x30\x30" "\x31\x20\x30\x30\x30\x2c\x30\x30" "\x31\x27\x30\x30\x30\x2c\x30\x30" "\x31\x2e\x30\x30\x30\x2e\x30\x30\x30\x2c\x30\x30" "\x31\x20\x30\x30\x30\x20\x30\x30\x30\x2c\x30\x30" "\x31\x27\x30\x30\x30\x27\x30\x30\x30\x2c\x30\x30" "\x30\x31\x30\x30\x30" "\x30\x38" "\x30\x39" "\x32\x2e\x32\x32\x35\x30\x37\x33\x38\x35\x38\x35\x30\x37\x32\x30\x31\x31\x65\x2d\x33\x30\x38" "\x2c\x2e\x2f\x3b\x27\x5b\x5d\x5c\x2d\x3d" "\x3c\x3e\x3f\x3a\x22\x7b\x7d\x7c\x5f\x2b" "\x21\x40\x23\x24\x25\x5e\x26\x2a\x28\x29\x60\x7e" "\x01\x02\x03\x04\x05\x06\x07\x08\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" "\xc2\x80\xc2\x81\xc2\x82\xc2\x83\xc2\x84\xc2\x86\xc2\x87\xc2\x88\xc2\x89\xc2\x8a\xc2\x8b\xc2\x8c\xc2\x8d\xc2\x8e\xc2\x8f\xc2\x90\xc2\x91\xc2\x92\xc2\x93\xc2\x94\xc2\x95\xc2\x96\xc2\x97\xc2\x98\xc2\x99\xc2\x9a\xc2\x9b\xc2\x9c\xc2\x9d\xc2\x9e\xc2\x9f" "\x09\x0b\x0c\x20\xc2\x85\xc2\xa0\xe1\x9a\x80\xe2\x80\x82\xe2\x80\x83\xe2\x80\x82\xe2\x80\x83\xe2\x80\x84\xe2\x80\x85\xe2\x80\x86\xe2\x80\x87\xe2\x80\x88\xe2\x80\x89\xe2\x80\x8a\xe2\x80\x8b\xe2\x80\xa8\xe2\x80\xa9\xe2\x80\xaf\xe2\x81\x9f\xe3\x80\x80" "\xc2\xad\xd8\x80\xd8\x81\xd8\x82\xd8\x83\xd8\x84\xd8\x85\xd8\x9c\xdb\x9d\xdc\x8f\xe1\xa0\x8e\xe2\x80\x8b\xe2\x80\x8c\xe2\x80\x8d\xe2\x80\x8e\xe2\x80\x8f\xe2\x80\xaa\xe2\x80\xab\xe2\x80\xac\xe2\x80\xad\xe2\x80\xae\xe2\x81\xa0\xe2\x81\xa1\xe2\x81\xa2\xe2\x81\xa3\xe2\x81\xa4\xe2\x81\xa6\xe2\x81\xa7\xe2\x81\xa8\xe2\x81\xa9\xe2\x81\xaa\xe2\x81\xab\xe2\x81\xac\xe2\x81\xad\xe2\x81\xae\xe2\x81\xaf\xef\xbb\xbf\xef\xbf\xb9\xef\xbf\xba\xef\xbf\xbb\xf0\x91\x82\xbd\xf0\x9b\xb2\xa0\xf0\x9b\xb2\xa1\xf0\x9b\xb2\xa2\xf0\x9b\xb2\xa3\xf0\x9d\x85\xb3\xf0\x9d\x85\xb4\xf0\x9d\x85\xb5\xf0\x9d\x85\xb6\xf0\x9d\x85\xb7\xf0\x9d\x85\xb8\xf0\x9d\x85\xb9\xf0\x9d\x85\xba\xf3\xa0\x80\x81\xf3\xa0\x80\xa0\xf3\xa0\x80\xa1\xf3\xa0\x80\xa2\xf3\xa0\x80\xa3\xf3\xa0\x80\xa4\xf3\xa0\x80\xa5\xf3\xa0\x80\xa6\xf3\xa0\x80\xa7\xf3\xa0\x80\xa8\xf3\xa0\x80\xa9\xf3\xa0\x80\xaa\xf3\xa0\x80\xab\xf3\xa0\x80\xac\xf3\xa0\x80\xad\xf3\xa0\x80\xae\xf3\xa0\x80\xaf\xf3\xa0\x80\xb0\xf3\xa0\x80\xb1\xf3\xa0\x80\xb2\xf3\xa0\x80\xb3\xf3\xa0\x80\xb4\xf3\xa0\x80\xb5\xf3\xa0\x80\xb6\xf3\xa0\x80\xb7\xf3\xa0\x80\xb8\xf3\xa0\x80\xb9\xf3\xa0\x80\xba\xf3\xa0\x80\xbb\xf3\xa0\x80\xbc\xf3\xa0\x80\xbd\xf3\xa0\x80\xbe\xf3\xa0\x80\xbf\xf3\xa0\x81\x80\xf3\xa0\x81\x81\xf3\xa0\x81\x82\xf3\xa0\x81\x83\xf3\xa0\x81\x84\xf3\xa0\x81\x85\xf3\xa0\x81\x86\xf3\xa0\x81\x87\xf3\xa0\x81\x88\xf3\xa0\x81\x89\xf3\xa0\x81\x8a\xf3\xa0\x81\x8b\xf3\xa0\x81\x8c\xf3\xa0\x81\x8d\xf3\xa0\x81\x8e\xf3\xa0\x81\x8f\xf3\xa0\x81\x90\xf3\xa0\x81\x91\xf3\xa0\x81\x92\xf3\xa0\x81\x93\xf3\xa0\x81\x94\xf3\xa0\x81\x95\xf3\xa0\x81\x96\xf3\xa0\x81\x97\xf3\xa0\x81\x98\xf3\xa0\x81\x99\xf3\xa0\x81\x9a\xf3\xa0\x81\x9b\xf3\xa0\x81\x9c\xf3\xa0\x81\x9d\xf3\xa0\x81\x9e\xf3\xa0\x81\x9f\xf3\xa0\x81\xa0\xf3\xa0\x81\xa1\xf3\xa0\x81\xa2\xf3\xa0\x81\xa3\xf3\xa0\x81\xa4\xf3\xa0\x81\xa5\xf3\xa0\x81\xa6\xf3\xa0\x81\xa7\xf3\xa0\x81\xa8\xf3\xa0\x81\xa9\xf3\xa0\x81\xaa\xf3\xa0\x81\xab\xf3\xa0\x81\xac\xf3\xa0\x81\xad\xf3\xa0\x81\xae\xf3\xa0\x81\xaf\xf3\xa0\x81\xb0\xf3\xa0\x81\xb1\xf3\xa0\x81\xb2\xf3\xa0\x81\xb3\xf3\xa0\x81\xb4\xf3\xa0\x81\xb5\xf3\xa0\x81\xb6\xf3\xa0\x81\xb7\xf3\xa0\x81\xb8\xf3\xa0\x81\xb9\xf3\xa0\x81\xba\xf3\xa0\x81\xbb\xf3\xa0\x81\xbc\xf3\xa0\x81\xbd\xf3\xa0\x81\xbe\xf3\xa0\x81\xbf" "\xef\xbb\xbf" "\xef\xbf\xbe" "\xce\xa9\xe2\x89\x88\xc3\xa7\xe2\x88\x9a\xe2\x88\xab\xcb\x9c\xc2\xb5\xe2\x89\xa4\xe2\x89\xa5\xc3\xb7" "\xc3\xa5\xc3\x9f\xe2\x88\x82\xc6\x92\xc2\xa9\xcb\x99\xe2\x88\x86\xcb\x9a\xc2\xac\xe2\x80\xa6\xc3\xa6" "\xc5\x93\xe2\x88\x91\xc2\xb4\xc2\xae\xe2\x80\xa0\xc2\xa5\xc2\xa8\xcb\x86\xc3\xb8\xcf\x80\xe2\x80\x9c\xe2\x80\x98" "\xc2\xa1\xe2\x84\xa2\xc2\xa3\xc2\xa2\xe2\x88\x9e\xc2\xa7\xc2\xb6\xe2\x80\xa2\xc2\xaa\xc2\xba\xe2\x80\x93\xe2\x89\xa0" "\xc2\xb8\xcb\x9b\xc3\x87\xe2\x97\x8a\xc4\xb1\xcb\x9c\xc3\x82\xc2\xaf\xcb\x98\xc2\xbf" "\xc3\x85\xc3\x8d\xc3\x8e\xc3\x8f\xcb\x9d\xc3\x93\xc3\x94\xef\xa3\xbf\xc3\x92\xc3\x9a\xc3\x86\xe2\x98\x83" "\xc5\x92\xe2\x80\x9e\xc2\xb4\xe2\x80\xb0\xcb\x87\xc3\x81\xc2\xa8\xcb\x86\xc3\x98\xe2\x88\x8f\xe2\x80\x9d\xe2\x80\x99" "\x60\xe2\x81\x84\xe2\x82\xac\xe2\x80\xb9\xe2\x80\xba\xef\xac\x81\xef\xac\x82\xe2\x80\xa1\xc2\xb0\xc2\xb7\xe2\x80\x9a\xe2\x80\x94\xc2\xb1" "\xe2\x85\x9b\xe2\x85\x9c\xe2\x85\x9d\xe2\x85\x9e" "\xd0\x81\xd0\x82\xd0\x83\xd0\x84\xd0\x85\xd0\x86\xd0\x87\xd0\x88\xd0\x89\xd0\x8a\xd0\x8b\xd0\x8c\xd0\x8d\xd0\x8e\xd0\x8f\xd0\x90\xd0\x91\xd0\x92\xd0\x93\xd0\x94\xd0\x95\xd0\x96\xd0\x97\xd0\x98\xd0\x99\xd0\x9a\xd0\x9b\xd0\x9c\xd0\x9d\xd0\x9e\xd0\x9f\xd0\xa0\xd0\xa1\xd0\xa2\xd0\xa3\xd0\xa4\xd0\xa5\xd0\xa6\xd0\xa7\xd0\xa8\xd0\xa9\xd0\xaa\xd0\xab\xd0\xac\xd0\xad\xd0\xae\xd0\xaf\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3\xd0\xb4\xd0\xb5\xd0\xb6\xd0\xb7\xd0\xb8\xd0\xb9\xd0\xba\xd0\xbb\xd0\xbc\xd0\xbd\xd0\xbe\xd0\xbf\xd1\x80\xd1\x81\xd1\x82\xd1\x83\xd1\x84\xd1\x85\xd1\x86\xd1\x87\xd1\x88\xd1\x89\xd1\x8a\xd1\x8b\xd1\x8c\xd1\x8d\xd1\x8e\xd1\x8f" "\xd9\xa0\xd9\xa1\xd9\xa2\xd9\xa3\xd9\xa4\xd9\xa5\xd9\xa6\xd9\xa7\xd9\xa8\xd9\xa9" "\xe2\x81\xb0\xe2\x81\xb4\xe2\x81\xb5" "\xe2\x82\x80\xe2\x82\x81\xe2\x82\x82" "\xe2\x81\xb0\xe2\x81\xb4\xe2\x81\xb5\xe2\x82\x80\xe2\x82\x81\xe2\x82\x82" "\xe0\xb8\x94\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\x20\xe0\xb8\x94\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\x20\xe0\xb8\x94\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x89\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87\xe0\xb9\x87" "\x27" "\x22" "\x27\x27" "\x22\x22" "\x27\x22\x27" "\x22\x27\x27\x27\x27\x22\x27\x22" "\x22\x27\x22\x27\x22\x27\x27\x27\x27\x22" "\x3c\x66\x6f\x6f\x20\x76\x61\x6c\x3d\xe2\x80\x9c\x62\x61\x72\xe2\x80\x9d\x20\x2f\x3e" "\x3c\x66\x6f\x6f\x20\x76\x61\x6c\x3d\xe2\x80\x9c\x62\x61\x72\xe2\x80\x9d\x20\x2f\x3e" "\x3c\x66\x6f\x6f\x20\x76\x61\x6c\x3d\xe2\x80\x9d\x62\x61\x72\xe2\x80\x9c\x20\x2f\x3e" "\x3c\x66\x6f\x6f\x20\x76\x61\x6c\x3d\x60\x62\x61\x72\x27\x20\x2f\x3e" "\xe7\x94\xb0\xe4\xb8\xad\xe3\x81\x95\xe3\x82\x93\xe3\x81\xab\xe3\x81\x82\xe3\x81\x92\xe3\x81\xa6\xe4\xb8\x8b\xe3\x81\x95\xe3\x81\x84" "\xe3\x83\x91\xe3\x83\xbc\xe3\x83\x86\xe3\x82\xa3\xe3\x83\xbc\xe3\x81\xb8\xe8\xa1\x8c\xe3\x81\x8b\xe3\x81\xaa\xe3\x81\x84\xe3\x81\x8b" "\xe5\x92\x8c\xe8\xa3\xbd\xe6\xbc\xa2\xe8\xaa\x9e" "\xe9\x83\xa8\xe8\x90\xbd\xe6\xa0\xbc" "\xec\x82\xac\xed\x9a\x8c\xea\xb3\xbc\xed\x95\x99\xec\x9b\x90\x20\xec\x96\xb4\xed\x95\x99\xec\x97\xb0\xea\xb5\xac\xec\x86\x8c" "\xec\xb0\xa6\xec\xb0\xa8\xeb\xa5\xbc\x20\xed\x83\x80\xea\xb3\xa0\x20\xec\x98\xa8\x20\xed\x8e\xb2\xec\x8b\x9c\xeb\xa7\xa8\xea\xb3\xbc\x20\xec\x91\x9b\xeb\x8b\xa4\xeb\xa6\xac\x20\xeb\x98\xa0\xeb\xb0\xa9\xea\xb0\x81\xed\x95\x98" "\xe7\xa4\xbe\xe6\x9c\x83\xe7\xa7\x91\xe5\xad\xb8\xe9\x99\xa2\xe8\xaa\x9e\xe5\xad\xb8\xe7\xa0\x94\xe7\xa9\xb6\xe6\x89\x80" "\xec\x9a\xb8\xeb\x9e\x80\xeb\xb0\x94\xed\x86\xa0\xeb\xa5\xb4" "\xf0\xa0\x9c\x8e\xf0\xa0\x9c\xb1\xf0\xa0\x9d\xb9\xf0\xa0\xb1\x93\xf0\xa0\xb1\xb8\xf0\xa0\xb2\x96\xf0\xa0\xb3\x8f" "\xf0\x90\x90\x9c\x20\xf0\x90\x90\x94\xf0\x90\x90\x87\xf0\x90\x90\x9d\xf0\x90\x90\x80\xf0\x90\x90\xa1\xf0\x90\x90\x87\xf0\x90\x90\x93\x20\xf0\x90\x90\x99\xf0\x90\x90\x8a\xf0\x90\x90\xa1\xf0\x90\x90\x9d\xf0\x90\x90\x93\x2f\xf0\x90\x90\x9d\xf0\x90\x90\x87\xf0\x90\x90\x97\xf0\x90\x90\x8a\xf0\x90\x90\xa4\xf0\x90\x90\x94\x20\xf0\x90\x90\x92\xf0\x90\x90\x8b\xf0\x90\x90\x97\x20\xf0\x90\x90\x92\xf0\x90\x90\x8c\x20\xf0\x90\x90\x9c\x20\xf0\x90\x90\xa1\xf0\x90\x90\x80\xf0\x90\x90\x96\xf0\x90\x90\x87\xf0\x90\x90\xa4\xf0\x90\x90\x93\xf0\x90\x90\x9d\x20\xf0\x90\x90\xb1\xf0\x90\x91\x82\x20\xf0\x90\x91\x84\x20\xf0\x90\x90\x94\xf0\x90\x90\x87\xf0\x90\x90\x9d\xf0\x90\x90\x80\xf0\x90\x90\xa1\xf0\x90\x90\x87\xf0\x90\x90\x93\x20\xf0\x90\x90\x8f\xf0\x90\x90\x86\xf0\x90\x90\x85\xf0\x90\x90\xa4\xf0\x90\x90\x86\xf0\x90\x90\x9a\xf0\x90\x90\x8a\xf0\x90\x90\xa1\xf0\x90\x90\x9d\xf0\x90\x90\x86\xf0\x90\x90\x93\xf0\x90\x90\x86" "\xe8\xa1\xa8\xe3\x83\x9d\xe3\x81\x82\x41\xe9\xb7\x97\xc5\x92\xc3\xa9\xef\xbc\xa2\xe9\x80\x8d\xc3\x9c\xc3\x9f\xc2\xaa\xc4\x85\xc3\xb1\xe4\xb8\x82\xe3\x90\x80\xf0\xa0\x80\x80" "\xc8\xba" "\xc8\xbe" "\xe3\x83\xbd\xe0\xbc\xbc\xe0\xba\x88\xd9\x84\xcd\x9c\xe0\xba\x88\xe0\xbc\xbd\xef\xbe\x89\x20\xe3\x83\xbd\xe0\xbc\xbc\xe0\xba\x88\xd9\x84\xcd\x9c\xe0\xba\x88\xe0\xbc\xbd\xef\xbe\x89" "\x28\xef\xbd\xa1\xe2\x97\x95\x20\xe2\x88\x80\x20\xe2\x97\x95\xef\xbd\xa1\x29" "\xef\xbd\x80\xef\xbd\xa8\x28\xc2\xb4\xe2\x88\x80\xef\xbd\x80\xe2\x88\xa9" "\x5f\x5f\xef\xbe\x9b\x28\x2c\x5f\x2c\x2a\x29" "\xe3\x83\xbb\x28\xef\xbf\xa3\xe2\x88\x80\xef\xbf\xa3\x29\xe3\x83\xbb\x3a\x2a\x3a" "\xef\xbe\x9f\xef\xbd\xa5\xe2\x9c\xbf\xe3\x83\xbe\xe2\x95\xb2\x28\xef\xbd\xa1\xe2\x97\x95\xe2\x80\xbf\xe2\x97\x95\xef\xbd\xa1\x29\xe2\x95\xb1\xe2\x9c\xbf\xef\xbd\xa5\xef\xbe\x9f" "\x2c\xe3\x80\x82\xe3\x83\xbb\x3a\x2a\x3a\xe3\x83\xbb\xe3\x82\x9c\xe2\x80\x99\x28\x20\xe2\x98\xbb\x20\xcf\x89\x20\xe2\x98\xbb\x20\x29\xe3\x80\x82\xe3\x83\xbb\x3a\x2a\x3a\xe3\x83\xbb\xe3\x82\x9c\xe2\x80\x99" "\x28\xe2\x95\xaf\xc2\xb0\xe2\x96\xa1\xc2\xb0\xef\xbc\x89\xe2\x95\xaf\xef\xb8\xb5\x20\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x29" "\x28\xef\xbe\x89\xe0\xb2\xa5\xe7\x9b\x8a\xe0\xb2\xa5\xef\xbc\x89\xef\xbe\x89\xef\xbb\xbf\x20\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb" "\xe2\x94\xac\xe2\x94\x80\xe2\x94\xac\xe3\x83\x8e\x28\x20\xc2\xba\x20\x5f\x20\xc2\xba\xe3\x83\x8e\x29" "\x28\x20\xcd\xa1\xc2\xb0\x20\xcd\x9c\xca\x96\x20\xcd\xa1\xc2\xb0\x29" "\xc2\xaf\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf" "\xf0\x9f\x98\x8d" "\xf0\x9f\x91\xa9\xf0\x9f\x8f\xbd" "\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\xa6\xb0\x20\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbf\xe2\x80\x8d\xf0\x9f\xa6\xb0\x20\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\xa6\xb1\x20\xf0\x9f\x91\xa8\xf0\x9f\x8f\xbf\xe2\x80\x8d\xf0\x9f\xa6\xb1\x20\xf0\x9f\xa6\xb9\xf0\x9f\x8f\xbf\xe2\x80\x8d\xe2\x99\x82\xef\xb8\x8f" "\xf0\x9f\x91\xbe\x20\xf0\x9f\x99\x87\x20\xf0\x9f\x92\x81\x20\xf0\x9f\x99\x85\x20\xf0\x9f\x99\x86\x20\xf0\x9f\x99\x8b\x20\xf0\x9f\x99\x8e\x20\xf0\x9f\x99\x8d" "\xf0\x9f\x90\xb5\x20\xf0\x9f\x99\x88\x20\xf0\x9f\x99\x89\x20\xf0\x9f\x99\x8a" "\xe2\x9d\xa4\xef\xb8\x8f\x20\xf0\x9f\x92\x94\x20\xf0\x9f\x92\x8c\x20\xf0\x9f\x92\x95\x20\xf0\x9f\x92\x9e\x20\xf0\x9f\x92\x93\x20\xf0\x9f\x92\x97\x20\xf0\x9f\x92\x96\x20\xf0\x9f\x92\x98\x20\xf0\x9f\x92\x9d\x20\xf0\x9f\x92\x9f\x20\xf0\x9f\x92\x9c\x20\xf0\x9f\x92\x9b\x20\xf0\x9f\x92\x9a\x20\xf0\x9f\x92\x99" "\xe2\x9c\x8b\xf0\x9f\x8f\xbf\x20\xf0\x9f\x92\xaa\xf0\x9f\x8f\xbf\x20\xf0\x9f\x91\x90\xf0\x9f\x8f\xbf\x20\xf0\x9f\x99\x8c\xf0\x9f\x8f\xbf\x20\xf0\x9f\x91\x8f\xf0\x9f\x8f\xbf\x20\xf0\x9f\x99\x8f\xf0\x9f\x8f\xbf" "\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa6\x20\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa6\x20\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa6\x20\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\x20\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa6\x20\xf0\x9f\x91\xa8\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa6\x20\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa6\x20\xf0\x9f\x91\xa9\xe2\x80\x8d\xf0\x9f\x91\xa7\xe2\x80\x8d\xf0\x9f\x91\xa6" "\xf0\x9f\x9a\xbe\x20\xf0\x9f\x86\x92\x20\xf0\x9f\x86\x93\x20\xf0\x9f\x86\x95\x20\xf0\x9f\x86\x96\x20\xf0\x9f\x86\x97\x20\xf0\x9f\x86\x99\x20\xf0\x9f\x8f\xa7" "\x30\xef\xb8\x8f\xe2\x83\xa3\x20\x31\xef\xb8\x8f\xe2\x83\xa3\x20\x32\xef\xb8\x8f\xe2\x83\xa3\x20\x33\xef\xb8\x8f\xe2\x83\xa3\x20\x34\xef\xb8\x8f\xe2\x83\xa3\x20\x35\xef\xb8\x8f\xe2\x83\xa3\x20\x36\xef\xb8\x8f\xe2\x83\xa3\x20\x37\xef\xb8\x8f\xe2\x83\xa3\x20\x38\xef\xb8\x8f\xe2\x83\xa3\x20\x39\xef\xb8\x8f\xe2\x83\xa3\x20\xf0\x9f\x94\x9f" "\xf0\x9f\x87\xba\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7\xf0\x9f\x87\xba\xf0\x9f\x87\xb8\x20\xf0\x9f\x87\xa6\xf0\x9f\x87\xab\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8" "\xf0\x9f\x87\xba\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7\xf0\x9f\x87\xba\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6\xf0\x9f\x87\xab\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2" "\xf0\x9f\x87\xba\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7\xf0\x9f\x87\xba\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6" "\xef\xbc\x91\xef\xbc\x92\xef\xbc\x93" "\xd9\xa1\xd9\xa2\xd9\xa3" "\xd8\xab\xd9\x85\x20\xd9\x86\xd9\x81\xd8\xb3\x20\xd8\xb3\xd9\x82\xd8\xb7\xd8\xaa\x20\xd9\x88\xd8\xa8\xd8\xa7\xd9\x84\xd8\xaa\xd8\xad\xd8\xaf\xd9\x8a\xd8\xaf\xd8\x8c\x2c\x20\xd8\xac\xd8\xb2\xd9\x8a\xd8\xb1\xd8\xaa\xd9\x8a\x20\xd8\xa8\xd8\xa7\xd8\xb3\xd8\xaa\xd8\xae\xd8\xaf\xd8\xa7\xd9\x85\x20\xd8\xa3\xd9\x86\x20\xd8\xaf\xd9\x86\xd9\x88\x2e\x20\xd8\xa5\xd8\xb0\x20\xd9\x87\xd9\x86\xd8\xa7\xd8\x9f\x20\xd8\xa7\xd9\x84\xd8\xb3\xd8\xaa\xd8\xa7\xd8\xb1\x20\xd9\x88\xd8\xaa\xd9\x86\xd8\xb5\xd9\x8a\xd8\xa8\x20\xd9\x83\xd8\xa7\xd9\x86\x2e\x20\xd8\xa3\xd9\x87\xd9\x91\xd9\x84\x20\xd8\xa7\xd9\x8a\xd8\xb7\xd8\xa7\xd9\x84\xd9\x8a\xd8\xa7\xd8\x8c\x20\xd8\xa8\xd8\xb1\xd9\x8a\xd8\xb7\xd8\xa7\xd9\x86\xd9\x8a\xd8\xa7\x2d\xd9\x81\xd8\xb1\xd9\x86\xd8\xb3\xd8\xa7\x20\xd9\x82\xd8\xaf\x20\xd8\xa3\xd8\xae\xd8\xb0\x2e\x20\xd8\xb3\xd9\x84\xd9\x8a\xd9\x85\xd8\xa7\xd9\x86\xd8\x8c\x20\xd8\xa5\xd8\xaa\xd9\x81\xd8\xa7\xd9\x82\xd9\x8a\xd8\xa9\x20\xd8\xa8\xd9\x8a\xd9\x86\x20\xd9\x85\xd8\xa7\x2c\x20\xd9\x8a\xd8\xb0\xd9\x83\xd8\xb1\x20\xd8\xa7\xd9\x84\xd8\xad\xd8\xaf\xd9\x88\xd8\xaf\x20\xd8\xa3\xd9\x8a\x20\xd8\xa8\xd8\xb9\xd8\xaf\x2c\x20\xd9\x85\xd8\xb9\xd8\xa7\xd9\x85\xd9\x84\xd8\xa9\x20\xd8\xa8\xd9\x88\xd9\x84\xd9\x86\xd8\xaf\xd8\xa7\xd8\x8c\x20\xd8\xa7\xd9\x84\xd8\xa5\xd8\xb7\xd9\x84\xd8\xa7\xd9\x82\x20\xd8\xb9\xd9\x84\x20\xd8\xa5\xd9\x8a\xd9\x88\x2e" "\xd7\x91\xd6\xb0\xd6\xbc\xd7\xa8\xd6\xb5\xd7\x90\xd7\xa9\xd6\xb4\xd7\x81\xd7\x99\xd7\xaa\x2c\x20\xd7\x91\xd6\xb8\xd6\xbc\xd7\xa8\xd6\xb8\xd7\x90\x20\xd7\x90\xd6\xb1\xd7\x9c\xd6\xb9\xd7\x94\xd6\xb4\xd7\x99\xd7\x9d\x2c\x20\xd7\x90\xd6\xb5\xd7\xaa\x20\xd7\x94\xd6\xb7\xd7\xa9\xd6\xb8\xd6\xbc\xd7\x81\xd7\x9e\xd6\xb7\xd7\x99\xd6\xb4\xd7\x9d\x2c\x20\xd7\x95\xd6\xb0\xd7\x90\xd6\xb5\xd7\xaa\x20\xd7\x94\xd6\xb8\xd7\x90\xd6\xb8\xd7\xa8\xd6\xb6\xd7\xa5" "\xd7\x94\xd6\xb8\xd7\x99\xd6\xb0\xd7\xaa\xd6\xb8\xd7\x94\x74\x65\x73\x74\xd8\xa7\xd9\x84\xd8\xb5\xd9\x81\xd8\xad\xd8\xa7\xd8\xaa\x20\xd8\xa7\xd9\x84\xd8\xaa\xd9\x91\xd8\xad\xd9\x88\xd9\x84" "\xef\xb7\xbd" "\xef\xb7\xba" "\xd9\x85\xd9\x8f\xd9\x86\xd9\x8e\xd8\xa7\xd9\x82\xd9\x8e\xd8\xb4\xd9\x8e\xd8\xa9\xd9\x8f\x20\xd8\xb3\xd9\x8f\xd8\xa8\xd9\x8f\xd9\x84\xd9\x90\x20\xd8\xa7\xd9\x90\xd8\xb3\xd9\x92\xd8\xaa\xd9\x90\xd8\xae\xd9\x92\xd8\xaf\xd9\x8e\xd8\xa7\xd9\x85\xd9\x90\x20\xd8\xa7\xd9\x84\xd9\x84\xd9\x8f\xd9\x91\xd8\xba\xd9\x8e\xd8\xa9\xd9\x90\x20\xd9\x81\xd9\x90\xd9\x8a\x20\xd8\xa7\xd9\x84\xd9\x86\xd9\x8f\xd9\x91\xd8\xb8\xd9\x8f\xd9\x85\xd9\x90\x20\xd8\xa7\xd9\x84\xd9\x92\xd9\x82\xd9\x8e\xd8\xa7\xd8\xa6\xd9\x90\xd9\x85\xd9\x8e\xd8\xa9\xd9\x90\x20\xd9\x88\xd9\x8e\xd9\x81\xd9\x90\xd9\x8a\xd9\x85\x20\xd9\x8a\xd9\x8e\xd8\xae\xd9\x8f\xd8\xb5\xd9\x8e\xd9\x91\x20\xd8\xa7\xd9\x84\xd8\xaa\xd9\x8e\xd9\x91\xd8\xb7\xd9\x92\xd8\xa8\xd9\x90\xd9\x8a\xd9\x82\xd9\x8e\xd8\xa7\xd8\xaa\xd9\x8f\x20\xd8\xa7\xd9\x84\xd9\x92\xd8\xad\xd8\xa7\xd8\xb3\xd9\x8f\xd9\x88\xd8\xa8\xd9\x90\xd9\x8a\xd9\x8e\xd9\x91\xd8\xa9\xd9\x8f\xd8\x8c\x20" "\xe1\x9a\x9b\xe1\x9a\x84\xe1\x9a\x93\xe1\x9a\x90\xe1\x9a\x8b\xe1\x9a\x92\xe1\x9a\x84\xe1\x9a\x80\xe1\x9a\x91\xe1\x9a\x84\xe1\x9a\x82\xe1\x9a\x91\xe1\x9a\x8f\xe1\x9a\x85\xe1\x9a\x9c\xe2\x80\xaa\xe2\x80\xaa\xe2\x80\xaa" "\xe2\x80\xaa\xe2\x80\xaa\xe1\x9a\x9b\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x80\xe1\x9a\x9c\xe2\x80\xaa" "\xe2\x80\xaa\xe2\x80\xaa\x74\x65\x73\x74\xe2\x80\xaa" "\xe2\x80\xab\x74\x65\x73\x74\xe2\x80\xab" "\xe2\x80\xa9\x74\x65\x73\x74\xe2\x80\xa9" "\x74\x65\x73\x74\xe2\x81\xa0\x74\x65\x73\x74\xe2\x80\xab" "\xe2\x81\xa6\x74\x65\x73\x74\xe2\x81\xa7" "\xe1\xb9\xb0\xcc\xba\xcc\xba\xcc\x95\x6f\xcd\x9e\x20\xcc\xb7\x69\xcc\xb2\xcc\xac\xcd\x87\xcc\xaa\xcd\x99\x6e\xcc\x9d\xcc\x97\xcd\x95\x76\xcc\x9f\xcc\x9c\xcc\x98\xcc\xa6\xcd\x9f\x6f\xcc\xb6\xcc\x99\xcc\xb0\xcc\xa0\x6b\xc3\xa8\xcd\x9a\xcc\xae\xcc\xba\xcc\xaa\xcc\xb9\xcc\xb1\xcc\xa4\x20\xcc\x96\x74\xcc\x9d\xcd\x95\xcc\xb3\xcc\xa3\xcc\xbb\xcc\xaa\xcd\x9e\x68\xcc\xbc\xcd\x93\xcc\xb2\xcc\xa6\xcc\xb3\xcc\x98\xcc\xb2\x65\xcd\x87\xcc\xa3\xcc\xb0\xcc\xa6\xcc\xac\xcd\x8e\x20\xcc\xa2\xcc\xbc\xcc\xbb\xcc\xb1\xcc\x98\x68\xcd\x9a\xcd\x8e\xcd\x99\xcc\x9c\xcc\xa3\xcc\xb2\xcd\x85\x69\xcc\xa6\xcc\xb2\xcc\xa3\xcc\xb0\xcc\xa4\x76\xcc\xbb\xcd\x8d\x65\xcc\xba\xcc\xad\xcc\xb3\xcc\xaa\xcc\xb0\x2d\x6d\xcc\xa2\x69\xcd\x85\x6e\xcc\x96\xcc\xba\xcc\x9e\xcc\xb2\xcc\xaf\xcc\xb0\x64\xcc\xb5\xcc\xbc\xcc\x9f\xcd\x99\xcc\xa9\xcc\xbc\xcc\x98\xcc\xb3\x20\xcc\x9e\xcc\xa5\xcc\xb1\xcc\xb3\xcc\xad\x72\xcc\x9b\xcc\x97\xcc\x98\x65\xcd\x99\x70\xcd\xa0\x72\xcc\xbc\xcc\x9e\xcc\xbb\xcc\xad\xcc\x97\x65\xcc\xba\xcc\xa0\xcc\xa3\xcd\x9f\x73\xcc\x98\xcd\x87\xcc\xb3\xcd\x8d\xcc\x9d\xcd\x89\x65\xcd\x89\xcc\xa5\xcc\xaf\xcc\x9e\xcc\xb2\xcd\x9a\xcc\xac\xcd\x9c\xc7\xb9\xcc\xac\xcd\x8e\xcd\x8e\xcc\x9f\xcc\x96\xcd\x87\xcc\xa4\x74\xcd\x8d\xcc\xac\xcc\xa4\xcd\x93\xcc\xbc\xcc\xad\xcd\x98\xcd\x85\x69\xcc\xaa\xcc\xb1\x6e\xcd\xa0\x67\xcc\xb4\xcd\x89\x20\xcd\x8f\xcd\x89\xcd\x85\x63\xcc\xac\xcc\x9f\x68\xcd\xa1\x61\xcc\xab\xcc\xbb\xcc\xaf\xcd\x98\x6f\xcc\xab\xcc\x9f\xcc\x96\xcd\x8d\xcc\x99\xcc\x9d\xcd\x89\x73\xcc\x97\xcc\xa6\xcc\xb2\x2e\xcc\xa8\xcc\xb9\xcd\x88\xcc\xa3" "\xcc\xa1\xcd\x93\xcc\x9e\xcd\x85\x49\xcc\x97\xcc\x98\xcc\xa6\xcd\x9d\x6e\xcd\x87\xcd\x87\xcd\x99\x76\xcc\xae\xcc\xab\x6f\x6b\xcc\xb2\xcc\xab\xcc\x99\xcd\x88\x69\xcc\x96\xcd\x99\xcc\xad\xcc\xb9\xcc\xa0\xcc\x9e\x6e\xcc\xa1\xcc\xbb\xcc\xae\xcc\xa3\xcc\xba\x67\xcc\xb2\xcd\x88\xcd\x99\xcc\xad\xcd\x99\xcc\xac\xcd\x8e\x20\xcc\xb0\x74\xcd\x94\xcc\xa6\x68\xcc\x9e\xcc\xb2\x65\xcc\xa2\xcc\xa4\x20\xcd\x8d\xcc\xac\xcc\xb2\xcd\x96\x66\xcc\xb4\xcc\x98\xcd\x95\xcc\xa3\xc3\xa8\xcd\x96\xe1\xba\xb9\xcc\xa5\xcc\xa9\x6c\xcd\x96\xcd\x94\xcd\x9a\x69\xcd\x93\xcd\x9a\xcc\xa6\xcd\xa0\x6e\xcd\x96\xcd\x8d\xcc\x97\xcd\x93\xcc\xb3\xcc\xae\x67\xcd\x8d\x20\xcc\xa8\x6f\xcd\x9a\xcc\xaa\xcd\xa1\x66\xcc\x98\xcc\xa3\xcc\xac\x20\xcc\x96\xcc\x98\xcd\x96\xcc\x9f\xcd\x99\xcc\xae\x63\xd2\x89\xcd\x94\xcc\xab\xcd\x96\xcd\x93\xcd\x87\xcd\x96\xcd\x85\x68\xcc\xb5\xcc\xa4\xcc\xa3\xcd\x9a\xcd\x94\xc3\xa1\xcc\x97\xcc\xbc\xcd\x95\xcd\x85\x6f\xcc\xbc\xcc\xa3\xcc\xa5\x73\xcc\xb1\xcd\x88\xcc\xba\xcc\x96\xcc\xa6\xcc\xbb\xcd\xa2\x2e\xcc\x9b\xcc\x96\xcc\x9e\xcc\xa0\xcc\xab\xcc\xb0" "\xcc\x97\xcc\xba\xcd\x96\xcc\xb9\xcc\xaf\xcd\x93\xe1\xb9\xae\xcc\xa4\xcd\x8d\xcc\xa5\xcd\x87\xcd\x88\x68\xcc\xb2\xcc\x81\x65\xcd\x8f\xcd\x93\xcc\xbc\xcc\x97\xcc\x99\xcc\xbc\xcc\xa3\xcd\x94\x20\xcd\x87\xcc\x9c\xcc\xb1\xcc\xa0\xcd\x93\xcd\x8d\xcd\x85\x4e\xcd\x95\xcd\xa0\x65\xcc\x97\xcc\xb1\x7a\xcc\x98\xcc\x9d\xcc\x9c\xcc\xba\xcd\x99\x70\xcc\xa4\xcc\xba\xcc\xb9\xcd\x8d\xcc\xaf\xcd\x9a\x65\xcc\xa0\xcc\xbb\xcc\xa0\xcd\x9c\x72\xcc\xa8\xcc\xa4\xcd\x8d\xcc\xba\xcc\x96\xcd\x94\xcc\x96\xcc\x96\x64\xcc\xa0\xcc\x9f\xcc\xad\xcc\xac\xcc\x9d\xcd\x9f\x69\xcc\xa6\xcd\x96\xcc\xa9\xcd\x93\xcd\x94\xcc\xa4\x61\xcc\xa0\xcc\x97\xcc\xac\xcd\x89\xcc\x99\x6e\xcd\x9a\xcd\x9c\x20\xcc\xbb\xcc\x9e\xcc\xb0\xcd\x9a\xcd\x85\x68\xcc\xb5\xcd\x89\x69\xcc\xb3\xcc\x9e\x76\xcc\xa2\xcd\x87\xe1\xb8\x99\xcd\x8e\xcd\x9f\x2d\xd2\x89\xcc\xad\xcc\xa9\xcc\xbc\xcd\x94\x6d\xcc\xa4\xcc\xad\xcc\xab\x69\xcd\x95\xcd\x87\xcc\x9d\xcc\xa6\x6e\xcc\x97\xcd\x99\xe1\xb8\x8d\xcc\x9f\x20\xcc\xaf\xcc\xb2\xcd\x95\xcd\x9e\xc7\xab\xcc\x9f\xcc\xaf\xcc\xb0\xcc\xb2\xcd\x99\xcc\xbb\xcc\x9d\x66\x20\xcc\xaa\xcc\xb0\xcc\xb0\xcc\x97\xcc\x96\xcc\xad\xcc\x98\xcd\x98\x63\xcc\xa6\xcd\x8d\xcc\xb2\xcc\x9e\xcd\x8d\xcc\xa9\xcc\x99\xe1\xb8\xa5\xcd\x9a\x61\xcc\xae\xcd\x8e\xcc\x9f\xcc\x99\xcd\x9c\xc6\xa1\xcc\xa9\xcc\xb9\xcd\x8e\x73\xcc\xa4\x2e\xcc\x9d\xcc\x9d\x20\xd2\x89\x5a\xcc\xa1\xcc\x96\xcc\x9c\xcd\x96\xcc\xb0\xcc\xa3\xcd\x89\xcc\x9c\x61\xcd\x96\xcc\xb0\xcd\x99\xcc\xac\xcd\xa1\x6c\xcc\xb2\xcc\xab\xcc\xb3\xcd\x8d\xcc\xa9\x67\xcc\xa1\xcc\x9f\xcc\xbc\xcc\xb1\xcd\x9a\xcc\x9e\xcc\xac\xcd\x85\x6f\xcc\x97\xcd\x9c\x2e\xcc\x9f" "\xcc\xa6\x48\xcc\xac\xcc\xa4\xcc\x97\xcc\xa4\xcd\x9d\x65\xcd\x9c\x20\xcc\x9c\xcc\xa5\xcc\x9d\xcc\xbb\xcd\x8d\xcc\x9f\xcc\x81\x77\xcc\x95\x68\xcc\x96\xcc\xaf\xcd\x93\x6f\xcc\x9d\xcd\x99\xcc\x96\xcd\x8e\xcc\xb1\xcc\xae\x20\xd2\x89\xcc\xba\xcc\x99\xcc\x9e\xcc\x9f\xcd\x88\x57\xcc\xb7\xcc\xbc\xcc\xad\x61\xcc\xba\xcc\xaa\xcd\x8d\xc4\xaf\xcd\x88\xcd\x95\xcc\xad\xcd\x99\xcc\xaf\xcc\x9c\x74\xcc\xb6\xcc\xbc\xcc\xae\x73\xcc\x98\xcd\x99\xcd\x96\xcc\x95\x20\xcc\xa0\xcc\xab\xcc\xa0\x42\xcc\xbb\xcd\x8d\xcd\x99\xcd\x89\xcc\xb3\xcd\x85\x65\xcc\xb5\x68\xcc\xb5\xcc\xac\xcd\x87\xcc\xab\xcd\x99\x69\xcc\xb9\xcd\x93\xcc\xb3\xcc\xb3\xcc\xae\xcd\x8e\xcc\xab\xcc\x95\x6e\xcd\x9f\x64\xcc\xb4\xcc\xaa\xcc\x9c\xcc\x96\x20\xcc\xb0\xcd\x89\xcc\xa9\xcd\x87\xcd\x99\xcc\xb2\xcd\x9e\xcd\x85\x54\xcd\x96\xcc\xbc\xcd\x93\xcc\xaa\xcd\xa2\x68\xcd\x8f\xcd\x93\xcc\xae\xcc\xbb\x65\xcc\xac\xcc\x9d\xcc\x9f\xcd\x85\x20\xcc\xa4\xcc\xb9\xcc\x9d\x57\xcd\x99\xcc\x9e\xcc\x9d\xcd\x94\xcd\x87\xcd\x9d\xcd\x85\x61\xcd\x8f\xcd\x93\xcd\x94\xcc\xb9\xcc\xbc\xcc\xa3\x6c\xcc\xb4\xcd\x94\xcc\xb0\xcc\xa4\xcc\x9f\xcd\x94\xe1\xb8\xbd\xcc\xab\x2e\xcd\x95" "\x5a\xcc\xae\xcc\x9e\xcc\xa0\xcd\x99\xcd\x94\xcd\x85\xe1\xb8\x80\xcc\x97\xcc\x9e\xcd\x88\xcc\xbb\xcc\x97\xe1\xb8\xb6\xcd\x99\xcd\x8e\xcc\xaf\xcc\xb9\xcc\x9e\xcd\x93\x47\xcc\xbb\x4f\xcc\xad\xcc\x97\xcc\xae" "\xcb\x99\xc9\x90\x6e\x62\xe1\xb4\x89\x6c\xc9\x90\x20\xc9\x90\x75\xc6\x83\xc9\x90\xc9\xaf\x20\xc7\x9d\xc9\xb9\x6f\x6c\x6f\x70\x20\xca\x87\xc7\x9d\x20\xc7\x9d\xc9\xb9\x6f\x71\xc9\x90\x6c\x20\xca\x87\x6e\x20\xca\x87\x75\x6e\x70\xe1\xb4\x89\x70\xe1\xb4\x89\xc9\x94\x75\xe1\xb4\x89\x20\xc9\xb9\x6f\x64\xc9\xaf\xc7\x9d\xca\x87\x20\x70\x6f\xc9\xaf\x73\x6e\xe1\xb4\x89\xc7\x9d\x20\x6f\x70\x20\x70\xc7\x9d\x73\x20\x27\xca\x87\xe1\xb4\x89\x6c\xc7\x9d\x20\xc6\x83\x75\xe1\xb4\x89\xc9\x94\x73\xe1\xb4\x89\x64\xe1\xb4\x89\x70\xc9\x90\x20\xc9\xb9\x6e\xca\x87\xc7\x9d\xca\x87\xc9\x94\xc7\x9d\x73\x75\x6f\xc9\x94\x20\x27\xca\x87\xc7\x9d\xc9\xaf\xc9\x90\x20\xca\x87\xe1\xb4\x89\x73\x20\xc9\xb9\x6f\x6c\x6f\x70\x20\xc9\xaf\x6e\x73\x64\xe1\xb4\x89\x20\xc9\xaf\xc7\x9d\xc9\xb9\x6f\xcb\xa5" "\x30\x30\xcb\x99\xc6\x96\x24\x2d" "\xef\xbc\xb4\xef\xbd\x88\xef\xbd\x85\x20\xef\xbd\x91\xef\xbd\x95\xef\xbd\x89\xef\xbd\x83\xef\xbd\x8b\x20\xef\xbd\x82\xef\xbd\x92\xef\xbd\x8f\xef\xbd\x97\xef\xbd\x8e\x20\xef\xbd\x86\xef\xbd\x8f\xef\xbd\x98\x20\xef\xbd\x8a\xef\xbd\x95\xef\xbd\x8d\xef\xbd\x90\xef\xbd\x93\x20\xef\xbd\x8f\xef\xbd\x96\xef\xbd\x85\xef\xbd\x92\x20\xef\xbd\x94\xef\xbd\x88\xef\xbd\x85\x20\xef\xbd\x8c\xef\xbd\x81\xef\xbd\x9a\xef\xbd\x99\x20\xef\xbd\x84\xef\xbd\x8f\xef\xbd\x87" "\xf0\x9d\x90\x93\xf0\x9d\x90\xa1\xf0\x9d\x90\x9e\x20\xf0\x9d\x90\xaa\xf0\x9d\x90\xae\xf0\x9d\x90\xa2\xf0\x9d\x90\x9c\xf0\x9d\x90\xa4\x20\xf0\x9d\x90\x9b\xf0\x9d\x90\xab\xf0\x9d\x90\xa8\xf0\x9d\x90\xb0\xf0\x9d\x90\xa7\x20\xf0\x9d\x90\x9f\xf0\x9d\x90\xa8\xf0\x9d\x90\xb1\x20\xf0\x9d\x90\xa3\xf0\x9d\x90\xae\xf0\x9d\x90\xa6\xf0\x9d\x90\xa9\xf0\x9d\x90\xac\x20\xf0\x9d\x90\xa8\xf0\x9d\x90\xaf\xf0\x9d\x90\x9e\xf0\x9d\x90\xab\x20\xf0\x9d\x90\xad\xf0\x9d\x90\xa1\xf0\x9d\x90\x9e\x20\xf0\x9d\x90\xa5\xf0\x9d\x90\x9a\xf0\x9d\x90\xb3\xf0\x9d\x90\xb2\x20\xf0\x9d\x90\x9d\xf0\x9d\x90\xa8\xf0\x9d\x90\xa0" "\xf0\x9d\x95\xbf\xf0\x9d\x96\x8d\xf0\x9d\x96\x8a\x20\xf0\x9d\x96\x96\xf0\x9d\x96\x9a\xf0\x9d\x96\x8e\xf0\x9d\x96\x88\xf0\x9d\x96\x90\x20\xf0\x9d\x96\x87\xf0\x9d\x96\x97\xf0\x9d\x96\x94\xf0\x9d\x96\x9c\xf0\x9d\x96\x93\x20\xf0\x9d\x96\x8b\xf0\x9d\x96\x94\xf0\x9d\x96\x9d\x20\xf0\x9d\x96\x8f\xf0\x9d\x96\x9a\xf0\x9d\x96\x92\xf0\x9d\x96\x95\xf0\x9d\x96\x98\x20\xf0\x9d\x96\x94\xf0\x9d\x96\x9b\xf0\x9d\x96\x8a\xf0\x9d\x96\x97\x20\xf0\x9d\x96\x99\xf0\x9d\x96\x8d\xf0\x9d\x96\x8a\x20\xf0\x9d\x96\x91\xf0\x9d\x96\x86\xf0\x9d\x96\x9f\xf0\x9d\x96\x9e\x20\xf0\x9d\x96\x89\xf0\x9d\x96\x94\xf0\x9d\x96\x8c" "\xf0\x9d\x91\xbb\xf0\x9d\x92\x89\xf0\x9d\x92\x86\x20\xf0\x9d\x92\x92\xf0\x9d\x92\x96\xf0\x9d\x92\x8a\xf0\x9d\x92\x84\xf0\x9d\x92\x8c\x20\xf0\x9d\x92\x83\xf0\x9d\x92\x93\xf0\x9d\x92\x90\xf0\x9d\x92\x98\xf0\x9d\x92\x8f\x20\xf0\x9d\x92\x87\xf0\x9d\x92\x90\xf0\x9d\x92\x99\x20\xf0\x9d\x92\x8b\xf0\x9d\x92\x96\xf0\x9d\x92\x8e\xf0\x9d\x92\x91\xf0\x9d\x92\x94\x20\xf0\x9d\x92\x90\xf0\x9d\x92\x97\xf0\x9d\x92\x86\xf0\x9d\x92\x93\x20\xf0\x9d\x92\x95\xf0\x9d\x92\x89\xf0\x9d\x92\x86\x20\xf0\x9d\x92\x8d\xf0\x9d\x92\x82\xf0\x9d\x92\x9b\xf0\x9d\x92\x9a\x20\xf0\x9d\x92\x85\xf0\x9d\x92\x90\xf0\x9d\x92\x88" "\xf0\x9d\x93\xa3\xf0\x9d\x93\xb1\xf0\x9d\x93\xae\x20\xf0\x9d\x93\xba\xf0\x9d\x93\xbe\xf0\x9d\x93\xb2\xf0\x9d\x93\xac\xf0\x9d\x93\xb4\x20\xf0\x9d\x93\xab\xf0\x9d\x93\xbb\xf0\x9d\x93\xb8\xf0\x9d\x94\x80\xf0\x9d\x93\xb7\x20\xf0\x9d\x93\xaf\xf0\x9d\x93\xb8\xf0\x9d\x94\x81\x20\xf0\x9d\x93\xb3\xf0\x9d\x93\xbe\xf0\x9d\x93\xb6\xf0\x9d\x93\xb9\xf0\x9d\x93\xbc\x20\xf0\x9d\x93\xb8\xf0\x9d\x93\xbf\xf0\x9d\x93\xae\xf0\x9d\x93\xbb\x20\xf0\x9d\x93\xbd\xf0\x9d\x93\xb1\xf0\x9d\x93\xae\x20\xf0\x9d\x93\xb5\xf0\x9d\x93\xaa\xf0\x9d\x94\x83\xf0\x9d\x94\x82\x20\xf0\x9d\x93\xad\xf0\x9d\x93\xb8\xf0\x9d\x93\xb0" "\xf0\x9d\x95\x8b\xf0\x9d\x95\x99\xf0\x9d\x95\x96\x20\xf0\x9d\x95\xa2\xf0\x9d\x95\xa6\xf0\x9d\x95\x9a\xf0\x9d\x95\x94\xf0\x9d\x95\x9c\x20\xf0\x9d\x95\x93\xf0\x9d\x95\xa3\xf0\x9d\x95\xa0\xf0\x9d\x95\xa8\xf0\x9d\x95\x9f\x20\xf0\x9d\x95\x97\xf0\x9d\x95\xa0\xf0\x9d\x95\xa9\x20\xf0\x9d\x95\x9b\xf0\x9d\x95\xa6\xf0\x9d\x95\x9e\xf0\x9d\x95\xa1\xf0\x9d\x95\xa4\x20\xf0\x9d\x95\xa0\xf0\x9d\x95\xa7\xf0\x9d\x95\x96\xf0\x9d\x95\xa3\x20\xf0\x9d\x95\xa5\xf0\x9d\x95\x99\xf0\x9d\x95\x96\x20\xf0\x9d\x95\x9d\xf0\x9d\x95\x92\xf0\x9d\x95\xab\xf0\x9d\x95\xaa\x20\xf0\x9d\x95\x95\xf0\x9d\x95\xa0\xf0\x9d\x95\x98" "\xf0\x9d\x9a\x83\xf0\x9d\x9a\x91\xf0\x9d\x9a\x8e\x20\xf0\x9d\x9a\x9a\xf0\x9d\x9a\x9e\xf0\x9d\x9a\x92\xf0\x9d\x9a\x8c\xf0\x9d\x9a\x94\x20\xf0\x9d\x9a\x8b\xf0\x9d\x9a\x9b\xf0\x9d\x9a\x98\xf0\x9d\x9a\xa0\xf0\x9d\x9a\x97\x20\xf0\x9d\x9a\x8f\xf0\x9d\x9a\x98\xf0\x9d\x9a\xa1\x20\xf0\x9d\x9a\x93\xf0\x9d\x9a\x9e\xf0\x9d\x9a\x96\xf0\x9d\x9a\x99\xf0\x9d\x9a\x9c\x20\xf0\x9d\x9a\x98\xf0\x9d\x9a\x9f\xf0\x9d\x9a\x8e\xf0\x9d\x9a\x9b\x20\xf0\x9d\x9a\x9d\xf0\x9d\x9a\x91\xf0\x9d\x9a\x8e\x20\xf0\x9d\x9a\x95\xf0\x9d\x9a\x8a\xf0\x9d\x9a\xa3\xf0\x9d\x9a\xa2\x20\xf0\x9d\x9a\x8d\xf0\x9d\x9a\x98\xf0\x9d\x9a\x90" "\xe2\x92\xaf\xe2\x92\xa3\xe2\x92\xa0\x20\xe2\x92\xac\xe2\x92\xb0\xe2\x92\xa4\xe2\x92\x9e\xe2\x92\xa6\x20\xe2\x92\x9d\xe2\x92\xad\xe2\x92\xaa\xe2\x92\xb2\xe2\x92\xa9\x20\xe2\x92\xa1\xe2\x92\xaa\xe2\x92\xb3\x20\xe2\x92\xa5\xe2\x92\xb0\xe2\x92\xa8\xe2\x92\xab\xe2\x92\xae\x20\xe2\x92\xaa\xe2\x92\xb1\xe2\x92\xa0\xe2\x92\xad\x20\xe2\x92\xaf\xe2\x92\xa3\xe2\x92\xa0\x20\xe2\x92\xa7\xe2\x92\x9c\xe2\x92\xb5\xe2\x92\xb4\x20\xe2\x92\x9f\xe2\x92\xaa\xe2\x92\xa2" "\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x26\x6c\x74\x3b\x73\x63\x72\x69\x70\x74\x26\x67\x74\x3b\x61\x6c\x65\x72\x74\x28\x26\x23\x33\x39\x3b\x31\x32\x33\x26\x23\x33\x39\x3b\x29\x3b\x26\x6c\x74\x3b\x2f\x73\x63\x72\x69\x70\x74\x26\x67\x74\x3b" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x20\x2f\x3e" "\x3c\x73\x76\x67\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x31\x32\x33\x3c\x31\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x20\x2f\x20\x73\x63\x72\x69\x70\x74\x20\x3e\x3c\x20\x73\x63\x72\x69\x70\x74\x20\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x20\x2f\x20\x73\x63\x72\x69\x70\x74\x20\x3e" "\x20\x6f\x6e\x66\x6f\x63\x75\x73\x3d\x4a\x61\x56\x61\x53\x43\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x20\x61\x75\x74\x6f\x66\x6f\x63\x75\x73" "\x22\x20\x6f\x6e\x66\x6f\x63\x75\x73\x3d\x4a\x61\x56\x61\x53\x43\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x20\x61\x75\x74\x6f\x66\x6f\x63\x75\x73" "\x27\x20\x6f\x6e\x66\x6f\x63\x75\x73\x3d\x4a\x61\x56\x61\x53\x43\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x20\x61\x75\x74\x6f\x66\x6f\x63\x75\x73" "\xef\xbc\x9c\x73\x63\x72\x69\x70\x74\xef\xbc\x9e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\xef\xbc\x9c\x2f\x73\x63\x72\x69\x70\x74\xef\xbc\x9e" "\x3c\x73\x63\x3c\x73\x63\x72\x69\x70\x74\x3e\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x72\x69\x70\x74\x3e" "\x2d\x2d\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x3b\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3b\x74\x3d\x22" "\x27\x3b\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3b\x74\x3d\x27" "\x4a\x61\x76\x61\x53\x43\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29" "\x3b\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3b" "\x73\x72\x63\x3d\x4a\x61\x56\x61\x53\x43\x72\x69\x70\x74\x3a\x70\x72\x6f\x6d\x70\x74\x28\x31\x33\x32\x29" "\x22\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x20\x78\x3d\x22" "\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x20\x78\x3d\x27" "\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x20\x78\x3d" "\x22\x20\x61\x75\x74\x6f\x66\x6f\x63\x75\x73\x20\x6f\x6e\x6b\x65\x79\x75\x70\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29" "\x27\x20\x61\x75\x74\x6f\x66\x6f\x63\x75\x73\x20\x6f\x6e\x6b\x65\x79\x75\x70\x3d\x27\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x32\x30\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x33\x45\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x44\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x39\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x43\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x32\x46\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x41\x74\x79\x70\x65\x3d\x22\x74\x65\x78\x74\x2f\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x22\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3b\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x27\x60\x22\x3e\x3c\x5c\x78\x33\x43\x73\x63\x72\x69\x70\x74\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x27\x60\x22\x3e\x3c\x5c\x78\x30\x30\x73\x63\x72\x69\x70\x74\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x5c\x78\x33\x41\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x5c\x78\x35\x43\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x5c\x78\x30\x30\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x65\x78\x70\x5c\x78\x30\x30\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x65\x78\x70\x5c\x78\x35\x43\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x30\x41\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x30\x39\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x33\x5c\x78\x38\x30\x5c\x78\x38\x30\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x34\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x43\x32\x5c\x78\x41\x30\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x30\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x41\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x30\x44\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x30\x43\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x37\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x46\x5c\x78\x42\x42\x5c\x78\x42\x46\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x32\x30\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x38\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x30\x30\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x42\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x36\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x35\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x32\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x30\x42\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x31\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x33\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x41\x42\x43\x3c\x64\x69\x76\x20\x73\x74\x79\x6c\x65\x3d\x22\x78\x3a\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x39\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e\x28\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e\x44\x45\x46" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x42\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x43\x32\x5c\x78\x41\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x35\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x31\x5c\x78\x41\x30\x5c\x78\x38\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x38\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x31\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x38\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x37\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x33\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x41\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x32\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x32\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x33\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x41\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x34\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x41\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x31\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x44\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x37\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x37\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x31\x5c\x78\x39\x41\x5c\x78\x38\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x33\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x34\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x31\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x38\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x34\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x36\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x33\x5c\x78\x38\x30\x5c\x78\x38\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x32\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x44\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x41\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x43\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x35\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x41\x38\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x36\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x32\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x42\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x30\x36\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x41\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x35\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x45\x32\x5c\x78\x38\x31\x5c\x78\x39\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x5c\x78\x31\x43\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x30\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x5c\x78\x33\x41\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x39\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x44\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x5c\x78\x30\x41\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x20\x69\x64\x3d\x22\x66\x75\x7a\x7a\x65\x6c\x65\x6d\x65\x6e\x74\x31\x22\x3e\x74\x65\x73\x74\x3c\x2f\x61\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x30\x41\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x32\x32\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x30\x42\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x30\x44\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x32\x46\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x30\x39\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x30\x43\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x30\x30\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x32\x37\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x60\x22\x27\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x78\x78\x3a\x78\x20\x5c\x78\x32\x30\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x33\x42\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x30\x44\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x46\x5c\x78\x42\x42\x5c\x78\x42\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x31\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x34\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x33\x5c\x78\x38\x30\x5c\x78\x38\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x30\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x35\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x38\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x30\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x41\x38\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x41\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x31\x5c\x78\x39\x41\x5c\x78\x38\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x30\x43\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x32\x42\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x46\x30\x5c\x78\x39\x30\x5c\x78\x39\x36\x5c\x78\x39\x41\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x2d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x30\x41\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x41\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x37\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x37\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x31\x5c\x78\x39\x46\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x41\x39\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x43\x32\x5c\x78\x38\x35\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x46\x5c\x78\x42\x46\x5c\x78\x41\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x33\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x42\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x46\x5c\x78\x42\x46\x5c\x78\x42\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x32\x31\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x32\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x32\x5c\x78\x38\x30\x5c\x78\x38\x36\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x45\x31\x5c\x78\x41\x30\x5c\x78\x38\x45\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x30\x42\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x32\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x22\x60\x27\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x5c\x78\x43\x32\x5c\x78\x41\x30\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x30\x30\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x34\x37\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x31\x31\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x31\x32\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5c\x78\x34\x37\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5c\x78\x31\x30\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5c\x78\x31\x33\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5c\x78\x33\x32\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5c\x78\x34\x37\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5c\x78\x31\x31\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x34\x37\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x33\x34\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x33\x39\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x5c\x78\x30\x30\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x30\x39\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x31\x30\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x31\x33\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x33\x32\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x31\x32\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x31\x31\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x30\x30\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x5c\x78\x34\x37\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x5c\x78\x30\x39\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x5c\x78\x31\x30\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x5c\x78\x31\x31\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x5c\x78\x31\x32\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x5c\x78\x31\x33\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x5b\x61\x5d\x5b\x62\x5d\x5b\x63\x5d\x73\x72\x63\x5b\x64\x5d\x3d\x78\x5b\x65\x5d\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5b\x66\x5d\x22\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5c\x78\x30\x39\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5c\x78\x31\x30\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5c\x78\x31\x31\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5c\x78\x31\x32\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5c\x78\x33\x32\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x5c\x78\x30\x30\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x22\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x6a\x61\x76\x61\x26\x23\x31\x26\x23\x32\x26\x23\x33\x26\x23\x34\x26\x23\x35\x26\x23\x36\x26\x23\x37\x26\x23\x38\x26\x23\x31\x31\x26\x23\x31\x32\x73\x63\x72\x69\x70\x74\x3a\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e\x58\x58\x58\x3c\x2f\x61\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x22\x78\x60\x20\x60\x3c\x73\x63\x72\x69\x70\x74\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e\x22\x60\x20\x60\x3e" "\x3c\x69\x6d\x67\x20\x73\x72\x63\x20\x6f\x6e\x65\x72\x72\x6f\x72\x20\x2f\x22\x20\x27\x22\x3d\x20\x61\x6c\x74\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x2f\x2f\x22\x3e" "\x3c\x74\x69\x74\x6c\x65\x20\x6f\x6e\x70\x72\x6f\x70\x65\x72\x74\x79\x63\x68\x61\x6e\x67\x65\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e\x3c\x2f\x74\x69\x74\x6c\x65\x3e\x3c\x74\x69\x74\x6c\x65\x20\x74\x69\x74\x6c\x65\x3d\x3e" "\x3c\x61\x20\x68\x72\x65\x66\x3d\x68\x74\x74\x70\x3a\x2f\x2f\x66\x6f\x6f\x2e\x62\x61\x72\x2f\x23\x78\x3d\x60\x79\x3e\x3c\x2f\x61\x3e\x3c\x69\x6d\x67\x20\x61\x6c\x74\x3d\x22\x60\x3e\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x3a\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3e\x3c\x2f\x61\x3e\x22\x3e" "\x3c\x21\x2d\x2d\x5b\x69\x66\x5d\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x20\x2d\x2d\x3e" "\x3c\x21\x2d\x2d\x5b\x69\x66\x3c\x69\x6d\x67\x20\x73\x72\x63\x3d\x78\x20\x6f\x6e\x65\x72\x72\x6f\x72\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x31\x29\x2f\x2f\x5d\x3e\x20\x2d\x2d\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3d\x22\x2f\x5c\x25\x28\x6a\x73\x63\x72\x69\x70\x74\x29\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3d\x22\x5c\x5c\x25\x28\x6a\x73\x63\x72\x69\x70\x74\x29\x73\x22\x3e\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x3c\x49\x4d\x47\x20\x22\x22\x22\x3e\x3c\x53\x43\x52\x49\x50\x54\x3e\x61\x6c\x65\x72\x74\x28\x22\x58\x53\x53\x22\x29\x3c\x2f\x53\x43\x52\x49\x50\x54\x3e\x22\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x53\x74\x72\x69\x6e\x67\x2e\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65\x28\x38\x38\x2c\x38\x33\x2c\x38\x33\x29\x29\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x23\x20\x6f\x6e\x6d\x6f\x75\x73\x65\x6f\x76\x65\x72\x3d\x22\x61\x6c\x65\x72\x74\x28\x27\x78\x78\x73\x27\x29\x22\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x20\x6f\x6e\x6d\x6f\x75\x73\x65\x6f\x76\x65\x72\x3d\x22\x61\x6c\x65\x72\x74\x28\x27\x78\x78\x73\x27\x29\x22\x3e" "\x3c\x49\x4d\x47\x20\x6f\x6e\x6d\x6f\x75\x73\x65\x6f\x76\x65\x72\x3d\x22\x61\x6c\x65\x72\x74\x28\x27\x78\x78\x73\x27\x29\x22\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x26\x23\x31\x30\x36\x3b\x26\x23\x39\x37\x3b\x26\x23\x31\x31\x38\x3b\x26\x23\x39\x37\x3b\x26\x23\x31\x31\x35\x3b\x26\x23\x39\x39\x3b\x26\x23\x31\x31\x34\x3b\x26\x23\x31\x30\x35\x3b\x26\x23\x31\x31\x32\x3b\x26\x23\x31\x31\x36\x3b\x26\x23\x35\x38\x3b\x26\x23\x39\x37\x3b\x26\x23\x31\x30\x38\x3b\x26\x23\x31\x30\x31\x3b\x26\x23\x31\x31\x34\x3b\x26\x23\x31\x31\x36\x3b\x26\x23\x34\x30\x3b\x26\x23\x33\x39\x3b\x26\x23\x38\x38\x3b\x26\x23\x38\x33\x3b\x26\x23\x38\x33\x3b\x26\x23\x33\x39\x3b\x26\x23\x34\x31\x3b\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x26\x23\x30\x30\x30\x30\x31\x30\x36\x26\x23\x30\x30\x30\x30\x30\x39\x37\x26\x23\x30\x30\x30\x30\x31\x31\x38\x26\x23\x30\x30\x30\x30\x30\x39\x37\x26\x23\x30\x30\x30\x30\x31\x31\x35\x26\x23\x30\x30\x30\x30\x30\x39\x39\x26\x23\x30\x30\x30\x30\x31\x31\x34\x26\x23\x30\x30\x30\x30\x31\x30\x35\x26\x23\x30\x30\x30\x30\x31\x31\x32\x26\x23\x30\x30\x30\x30\x31\x31\x36\x26\x23\x30\x30\x30\x30\x30\x35\x38\x26\x23\x30\x30\x30\x30\x30\x39\x37\x26\x23\x30\x30\x30\x30\x31\x30\x38\x26\x23\x30\x30\x30\x30\x31\x30\x31\x26\x23\x30\x30\x30\x30\x31\x31\x34\x26\x23\x30\x30\x30\x30\x31\x31\x36\x26\x23\x30\x30\x30\x30\x30\x34\x30\x26\x23\x30\x30\x30\x30\x30\x33\x39\x26\x23\x30\x30\x30\x30\x30\x38\x38\x26\x23\x30\x30\x30\x30\x30\x38\x33\x26\x23\x30\x30\x30\x30\x30\x38\x33\x26\x23\x30\x30\x30\x30\x30\x33\x39\x26\x23\x30\x30\x30\x30\x30\x34\x31\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x26\x23\x78\x36\x41\x26\x23\x78\x36\x31\x26\x23\x78\x37\x36\x26\x23\x78\x36\x31\x26\x23\x78\x37\x33\x26\x23\x78\x36\x33\x26\x23\x78\x37\x32\x26\x23\x78\x36\x39\x26\x23\x78\x37\x30\x26\x23\x78\x37\x34\x26\x23\x78\x33\x41\x26\x23\x78\x36\x31\x26\x23\x78\x36\x43\x26\x23\x78\x36\x35\x26\x23\x78\x37\x32\x26\x23\x78\x37\x34\x26\x23\x78\x32\x38\x26\x23\x78\x32\x37\x26\x23\x78\x35\x38\x26\x23\x78\x35\x33\x26\x23\x78\x35\x33\x26\x23\x78\x32\x37\x26\x23\x78\x32\x39\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x22\x6a\x61\x76\x20\x20\x20\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x3b\x22\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x22\x6a\x61\x76\x26\x23\x78\x30\x39\x3b\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x3b\x22\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x22\x6a\x61\x76\x26\x23\x78\x30\x41\x3b\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x3b\x22\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x22\x6a\x61\x76\x26\x23\x78\x30\x44\x3b\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x3b\x22\x3e" "\x70\x65\x72\x6c\x20\x2d\x65\x20\x27\x70\x72\x69\x6e\x74\x20\x22\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x6a\x61\x76\x61\x5c\x30\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x5c\x22\x58\x53\x53\x5c\x22\x29\x3e\x22\x3b\x27\x20\x3e\x20\x6f\x75\x74" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x22\x20\x26\x23\x31\x34\x3b\x20\x20\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x3b\x22\x3e" "\x3c\x53\x43\x52\x49\x50\x54\x2f\x58\x53\x53\x20\x53\x52\x43\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x68\x61\x2e\x63\x6b\x65\x72\x73\x2e\x6f\x72\x67\x2f\x78\x73\x73\x2e\x6a\x73\x22\x3e\x3c\x2f\x53\x43\x52\x49\x50\x54\x3e" "\x3c\x42\x4f\x44\x59\x20\x6f\x6e\x6c\x6f\x61\x64\x21\x23\x24\x25\x26\x28\x29\x2a\x7e\x2b\x2d\x5f\x2e\x2c\x3a\x3b\x3f\x40\x5b\x2f\x7c\x5c\x5d\x5e\x60\x3d\x61\x6c\x65\x72\x74\x28\x22\x58\x53\x53\x22\x29\x3e" "\x3c\x53\x43\x52\x49\x50\x54\x2f\x53\x52\x43\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x68\x61\x2e\x63\x6b\x65\x72\x73\x2e\x6f\x72\x67\x2f\x78\x73\x73\x2e\x6a\x73\x22\x3e\x3c\x2f\x53\x43\x52\x49\x50\x54\x3e" "\x3c\x3c\x53\x43\x52\x49\x50\x54\x3e\x61\x6c\x65\x72\x74\x28\x22\x58\x53\x53\x22\x29\x3b\x2f\x2f\x3c\x3c\x2f\x53\x43\x52\x49\x50\x54\x3e" "\x3c\x53\x43\x52\x49\x50\x54\x20\x53\x52\x43\x3d\x68\x74\x74\x70\x3a\x2f\x2f\x68\x61\x2e\x63\x6b\x65\x72\x73\x2e\x6f\x72\x67\x2f\x78\x73\x73\x2e\x6a\x73\x3f\x3c\x20\x42\x20\x3e" "\x3c\x53\x43\x52\x49\x50\x54\x20\x53\x52\x43\x3d\x2f\x2f\x68\x61\x2e\x63\x6b\x65\x72\x73\x2e\x6f\x72\x67\x2f\x2e\x6a\x3e" "\x3c\x49\x4d\x47\x20\x53\x52\x43\x3d\x22\x6a\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3a\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x22" "\x3c\x69\x66\x72\x61\x6d\x65\x20\x73\x72\x63\x3d\x68\x74\x74\x70\x3a\x2f\x2f\x68\x61\x2e\x63\x6b\x65\x72\x73\x2e\x6f\x72\x67\x2f\x73\x63\x72\x69\x70\x74\x6c\x65\x74\x2e\x68\x74\x6d\x6c\x20\x3c" "\x5c\x22\x3b\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29\x3b\x2f\x2f" "\x3c\x75\x20\x6f\x6e\x63\x6f\x70\x79\x3d\x61\x6c\x65\x72\x74\x28\x29\x3e\x20\x43\x6f\x70\x79\x20\x6d\x65\x3c\x2f\x75\x3e" "\x3c\x69\x20\x6f\x6e\x77\x68\x65\x65\x6c\x3d\x61\x6c\x65\x72\x74\x28\x31\x29\x3e\x20\x53\x63\x72\x6f\x6c\x6c\x20\x6f\x76\x65\x72\x20\x6d\x65\x20\x3c\x2f\x69\x3e" "\x3c\x70\x6c\x61\x69\x6e\x74\x65\x78\x74\x3e" "\x68\x74\x74\x70\x3a\x2f\x2f\x61\x2f\x25\x25\x33\x30\x25\x33\x30" "\x3c\x2f\x74\x65\x78\x74\x61\x72\x65\x61\x3e\x3c\x73\x63\x72\x69\x70\x74\x3e\x61\x6c\x65\x72\x74\x28\x31\x32\x33\x29\x3c\x2f\x73\x63\x72\x69\x70\x74\x3e" "\x31\x3b\x44\x52\x4f\x50\x20\x54\x41\x42\x4c\x45\x20\x75\x73\x65\x72\x73" "\x31\x27\x3b\x20\x44\x52\x4f\x50\x20\x54\x41\x42\x4c\x45\x20\x75\x73\x65\x72\x73\x2d\x2d\x20\x31" "\x27\x20\x4f\x52\x20\x31\x3d\x31\x20\x2d\x2d\x20\x31" "\x27\x20\x4f\x52\x20\x27\x31\x27\x3d\x27\x31" "\x27\x3b\x20\x45\x58\x45\x43\x20\x73\x70\x5f\x4d\x53\x46\x6f\x72\x45\x61\x63\x68\x54\x61\x62\x6c\x65\x20\x27\x44\x52\x4f\x50\x20\x54\x41\x42\x4c\x45\x20\x3f\x27\x3b\x20\x2d\x2d" "\x20" "\x25" "\x5f" "\x2d" "\x2d\x2d" "\x2d\x2d\x76\x65\x72\x73\x69\x6f\x6e" "\x2d\x2d\x68\x65\x6c\x70" "\x24\x55\x53\x45\x52" "\x2f\x64\x65\x76\x2f\x6e\x75\x6c\x6c\x3b\x20\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x62\x6c\x6e\x73\x2e\x66\x61\x69\x6c\x20\x3b\x20\x65\x63\x68\x6f" "\x60\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x62\x6c\x6e\x73\x2e\x66\x61\x69\x6c\x60" "\x24\x28\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x62\x6c\x6e\x73\x2e\x66\x61\x69\x6c\x29" "\x40\x7b\x5b\x73\x79\x73\x74\x65\x6d\x20\x22\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x62\x6c\x6e\x73\x2e\x66\x61\x69\x6c\x22\x5d\x7d" "\x65\x76\x61\x6c\x28\x22\x70\x75\x74\x73\x20\x27\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x27\x22\x29" "\x53\x79\x73\x74\x65\x6d\x28\x22\x6c\x73\x20\x2d\x61\x6c\x20\x2f\x22\x29" "\x60\x6c\x73\x20\x2d\x61\x6c\x20\x2f\x60" "\x4b\x65\x72\x6e\x65\x6c\x2e\x65\x78\x65\x63\x28\x22\x6c\x73\x20\x2d\x61\x6c\x20\x2f\x22\x29" "\x4b\x65\x72\x6e\x65\x6c\x2e\x65\x78\x69\x74\x28\x31\x29" "\x25\x78\x28\x27\x6c\x73\x20\x2d\x61\x6c\x20\x2f\x27\x29" "\x3c\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x49\x53\x4f\x2d\x38\x38\x35\x39\x2d\x31\x22\x3f\x3e\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x66\x6f\x6f\x20\x5b\x20\x3c\x21\x45\x4c\x45\x4d\x45\x4e\x54\x20\x66\x6f\x6f\x20\x41\x4e\x59\x20\x3e\x3c\x21\x45\x4e\x54\x49\x54\x59\x20\x78\x78\x65\x20\x53\x59\x53\x54\x45\x4d\x20\x22\x66\x69\x6c\x65\x3a\x2f\x2f\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x22\x20\x3e\x5d\x3e\x3c\x66\x6f\x6f\x3e\x26\x78\x78\x65\x3b\x3c\x2f\x66\x6f\x6f\x3e" "\x24\x48\x4f\x4d\x45" "\x24\x45\x4e\x56\x7b\x27\x48\x4f\x4d\x45\x27\x7d" "\x25\x64" "\x25\x73\x25\x73\x25\x73\x25\x73\x25\x73" "\x7b\x30\x7d" "\x25\x2a\x2e\x2a\x73" "\x25\x40" "\x25\x6e" "\x46\x69\x6c\x65\x3a\x2f\x2f\x2f" "\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x25\x30\x30" "\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x2e\x2e\x2f\x65\x74\x63\x2f\x68\x6f\x73\x74\x73" "\x28\x29\x20\x7b\x20\x30\x3b\x20\x7d\x3b\x20\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x62\x6c\x6e\x73\x2e\x73\x68\x65\x6c\x6c\x73\x68\x6f\x63\x6b\x31\x2e\x66\x61\x69\x6c\x3b" "\x28\x29\x20\x7b\x20\x5f\x3b\x20\x7d\x20\x3e\x5f\x5b\x24\x28\x24\x28\x29\x29\x5d\x20\x7b\x20\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x62\x6c\x6e\x73\x2e\x73\x68\x65\x6c\x6c\x73\x68\x6f\x63\x6b\x32\x2e\x66\x61\x69\x6c\x3b\x20\x7d" "\x3c\x3c\x3c\x20\x25\x73\x28\x75\x6e\x3d\x27\x25\x73\x27\x29\x20\x3d\x20\x25\x75" "\x2b\x2b\x2b\x41\x54\x48\x30" "\x43\x4f\x4e" "\x50\x52\x4e" "\x41\x55\x58" "\x43\x4c\x4f\x43\x4b\x24" "\x4e\x55\x4c" "\x41\x3a" "\x5a\x5a\x3a" "\x43\x4f\x4d\x31" "\x4c\x50\x54\x31" "\x4c\x50\x54\x32" "\x4c\x50\x54\x33" "\x43\x4f\x4d\x32" "\x43\x4f\x4d\x33" "\x43\x4f\x4d\x34" "\x44\x43\x43\x20\x53\x45\x4e\x44\x20\x53\x54\x41\x52\x54\x4b\x45\x59\x4c\x4f\x47\x47\x45\x52\x20\x30\x20\x30\x20\x30" "\x53\x63\x75\x6e\x74\x68\x6f\x72\x70\x65\x20\x47\x65\x6e\x65\x72\x61\x6c\x20\x48\x6f\x73\x70\x69\x74\x61\x6c" "\x50\x65\x6e\x69\x73\x74\x6f\x6e\x65\x20\x43\x6f\x6d\x6d\x75\x6e\x69\x74\x79\x20\x43\x68\x75\x72\x63\x68" "\x4c\x69\x67\x68\x74\x77\x61\x74\x65\x72\x20\x43\x6f\x75\x6e\x74\x72\x79\x20\x50\x61\x72\x6b" "\x4a\x69\x6d\x6d\x79\x20\x43\x6c\x69\x74\x68\x65\x72\x6f\x65" "\x48\x6f\x72\x6e\x69\x6d\x61\x6e\x20\x4d\x75\x73\x65\x75\x6d" "\x73\x68\x69\x74\x61\x6b\x65\x20\x6d\x75\x73\x68\x72\x6f\x6f\x6d\x73" "\x52\x6f\x6d\x61\x6e\x73\x49\x6e\x53\x75\x73\x73\x65\x78\x2e\x63\x6f\x2e\x75\x6b" "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x63\x75\x6d\x2e\x71\x63\x2e\x63\x61\x2f" "\x43\x72\x61\x69\x67\x20\x43\x6f\x63\x6b\x62\x75\x72\x6e\x2c\x20\x53\x6f\x66\x74\x77\x61\x72\x65\x20\x53\x70\x65\x63\x69\x61\x6c\x69\x73\x74" "\x4c\x69\x6e\x64\x61\x20\x43\x61\x6c\x6c\x61\x68\x61\x6e" "\x44\x72\x2e\x20\x48\x65\x72\x6d\x61\x6e\x20\x49\x2e\x20\x4c\x69\x62\x73\x68\x69\x74\x7a" "\x6d\x61\x67\x6e\x61\x20\x63\x75\x6d\x20\x6c\x61\x75\x64\x65" "\x53\x75\x70\x65\x72\x20\x42\x6f\x77\x6c\x20\x58\x58\x58" "\x6d\x65\x64\x69\x65\x76\x61\x6c\x20\x65\x72\x65\x63\x74\x69\x6f\x6e\x20\x6f\x66\x20\x70\x61\x72\x61\x70\x65\x74\x73" "\x65\x76\x61\x6c\x75\x61\x74\x65" "\x6d\x6f\x63\x68\x61" "\x65\x78\x70\x72\x65\x73\x73\x69\x6f\x6e" "\x41\x72\x73\x65\x6e\x61\x6c\x20\x63\x61\x6e\x61\x6c" "\x63\x6c\x61\x73\x73\x69\x63" "\x54\x79\x73\x6f\x6e\x20\x47\x61\x79" "\x44\x69\x63\x6b\x20\x56\x61\x6e\x20\x44\x79\x6b\x65" "\x62\x61\x73\x65\x6d\x65\x6e\x74" "\x49\x66\x20\x79\x6f\x75\x27\x72\x65\x20\x72\x65\x61\x64\x69\x6e\x67\x20\x74\x68\x69\x73\x2c\x20\x79\x6f\x75\x27\x76\x65\x20\x62\x65\x65\x6e\x20\x69\x6e\x20\x61\x20\x63\x6f\x6d\x61\x20\x66\x6f\x72\x20\x61\x6c\x6d\x6f\x73\x74\x20\x32\x30\x20\x79\x65\x61\x72\x73\x20\x6e\x6f\x77\x2e\x20\x57\x65\x27\x72\x65\x20\x74\x72\x79\x69\x6e\x67\x20\x61\x20\x6e\x65\x77\x20\x74\x65\x63\x68\x6e\x69\x71\x75\x65\x2e\x20\x57\x65\x20\x64\x6f\x6e\x27\x74\x20\x6b\x6e\x6f\x77\x20\x77\x68\x65\x72\x65\x20\x74\x68\x69\x73\x20\x6d\x65\x73\x73\x61\x67\x65\x20\x77\x69\x6c\x6c\x20\x65\x6e\x64\x20\x75\x70\x20\x69\x6e\x20\x79\x6f\x75\x72\x20\x64\x72\x65\x61\x6d\x2c\x20\x62\x75\x74\x20\x77\x65\x20\x68\x6f\x70\x65\x20\x69\x74\x20\x77\x6f\x72\x6b\x73\x2e\x20\x50\x6c\x65\x61\x73\x65\x20\x77\x61\x6b\x65\x20\x75\x70\x2c\x20\x77\x65\x20\x6d\x69\x73\x73\x20\x79\x6f\x75\x2e" "\x52\x6f\x73\x65\x73\x20\x61\x72\x65\x20\x1b\x5b\x30\x3b\x33\x31\x6d\x72\x65\x64\x1b\x5b\x30\x6d\x2c\x20\x76\x69\x6f\x6c\x65\x74\x73\x20\x61\x72\x65\x20\x1b\x5b\x30\x3b\x33\x34\x6d\x62\x6c\x75\x65\x2e\x20\x48\x6f\x70\x65\x20\x79\x6f\x75\x20\x65\x6e\x6a\x6f\x79\x20\x74\x65\x72\x6d\x69\x6e\x61\x6c\x20\x68\x75\x65" "\x42\x75\x74\x20\x6e\x6f\x77\x2e\x2e\x2e\x1b\x5b\x32\x30\x43\x66\x6f\x72\x20\x6d\x79\x20\x67\x72\x65\x61\x74\x65\x73\x74\x20\x74\x72\x69\x63\x6b\x2e\x2e\x2e\x1b\x5b\x38\x6d" "\x54\x68\x65\x20\x71\x75\x69\x63\x08\x08\x08\x08\x08\x08\x6b\x20\x62\x72\x6f\x77\x6e\x20\x66\x6f\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x78\x2e\x2e\x2e\x20\x5b\x42\x65\x65\x65\x65\x70\x5d" "\x50\x6f\x77\x65\x72\xd9\x84\xd9\x8f\xd9\x84\xd9\x8f\xd8\xb5\xd9\x91\xd8\xa8\xd9\x8f\xd9\x84\xd9\x8f\xd9\x84\xd8\xb5\xd9\x91\xd8\xa8\xd9\x8f\xd8\xb1\xd8\xb1\xd9\x8b\x20\xe0\xa5\xa3\x20\xe0\xa5\xa3\x68\x20\xe0\xa5\xa3\x20\xe0\xa5\xa3\xe5\x86\x97" "\xf0\x9f\x8f\xb3\x30\xf0\x9f\x8c\x88\xef\xb8\x8f" "\xe0\xb0\x9c\xe0\xb1\x8d\xe0\xb0\x9e\xe2\x80\x8c\xe0\xb0\xbe" "\xda\xaf\xda\x86\xd9\xbe\xda\x98" "\x7b\x25\x20\x70\x72\x69\x6e\x74\x20\x27\x78\x27\x20\x2a\x20\x36\x34\x20\x2a\x20\x31\x30\x32\x34\x2a\x2a\x33\x20\x25\x7d" "\x7b\x7b\x20\x22\x22\x2e\x5f\x5f\x63\x6c\x61\x73\x73\x5f\x5f\x2e\x5f\x5f\x6d\x72\x6f\x5f\x5f\x5b\x32\x5d\x2e\x5f\x5f\x73\x75\x62\x63\x6c\x61\x73\x73\x65\x73\x5f\x5f\x28\x29\x5b\x34\x30\x5d\x28\x22\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x22\x29\x2e\x72\x65\x61\x64\x28\x29\x20\x7d\x7d" duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/000077500000000000000000000000001507724125200227705ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/authenticate_data.rs000066400000000000000000000002121507724125200270000ustar00rootroot00000000000000#![no_main] use imap_codec::AuthenticateDataCodec; use imap_codec_fuzz::impl_decode_target; impl_decode_target!(AuthenticateDataCodec); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/authenticate_data_to_bytes_and_back.rs000066400000000000000000000003121507724125200325130ustar00rootroot00000000000000#![no_main] use imap_codec::{AuthenticateDataCodec, imap_types::auth::AuthenticateData}; use imap_codec_fuzz::impl_to_bytes_and_back; impl_to_bytes_and_back!(AuthenticateDataCodec, AuthenticateData); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/command.rs000066400000000000000000000001701507724125200247520ustar00rootroot00000000000000#![no_main] use imap_codec::CommandCodec; use imap_codec_fuzz::impl_decode_target; impl_decode_target!(CommandCodec); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/command_to_bytes_and_back.rs000066400000000000000000000002511507724125200304640ustar00rootroot00000000000000#![no_main] use imap_codec::{CommandCodec, imap_types::command::Command}; use imap_codec_fuzz::impl_to_bytes_and_back; impl_to_bytes_and_back!(CommandCodec, Command); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/fragmentizer.rs000066400000000000000000000027351507724125200260420ustar00rootroot00000000000000#![no_main] use imap_codec::fragmentizer::Fragmentizer; use libfuzzer_sys::fuzz_target; fuzz_target!(|input: (Option, Vec>)| { let (mms, data) = input; #[cfg(feature = "debug")] println!("input: mms={mms:?}, data={data:?}"); // Ensure `Fragmentizer` recreates input data (when smaller than `mms`). let mut emitted_bytes = Vec::new(); let data_flat = data.concat(); let mut fragmentizer = mms.map_or_else(Fragmentizer::without_max_message_size, Fragmentizer::new); for chunk in &data { loop { match fragmentizer.progress() { Some(fragment_info) => { #[cfg(feature = "debug")] println!("{fragment_info:?}"); // Collect emitted bytes. emitted_bytes.extend_from_slice(fragmentizer.fragment_bytes(fragment_info)); } None => { fragmentizer.enqueue_bytes(chunk); break; } } } } assert!(emitted_bytes.len() <= data_flat.len()); let check_prefix = match mms { Some(max_message_size) => data_flat.len() <= max_message_size as usize, None => true, }; if check_prefix { #[cfg(feature = "debug")] println!("Checking prefix..."); // Ensure emitted bytes are prefix of fuzzing input data. assert_eq!(emitted_bytes, data_flat[..emitted_bytes.len()]); } }); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/greeting.rs000066400000000000000000000001721507724125200251420ustar00rootroot00000000000000#![no_main] use imap_codec::GreetingCodec; use imap_codec_fuzz::impl_decode_target; impl_decode_target!(GreetingCodec); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/greeting_to_bytes_and_back.rs000066400000000000000000000002561507724125200306570ustar00rootroot00000000000000#![no_main] use imap_codec::{GreetingCodec, imap_types::response::Greeting}; use imap_codec_fuzz::impl_to_bytes_and_back; impl_to_bytes_and_back!(GreetingCodec, Greeting); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/response.rs000066400000000000000000000001721507724125200251740ustar00rootroot00000000000000#![no_main] use imap_codec::ResponseCodec; use imap_codec_fuzz::impl_decode_target; impl_decode_target!(ResponseCodec); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/response_to_bytes_and_back.rs000066400000000000000000000002561507724125200307110ustar00rootroot00000000000000#![no_main] use imap_codec::{ResponseCodec, imap_types::response::Response}; use imap_codec_fuzz::impl_to_bytes_and_back; impl_to_bytes_and_back!(ResponseCodec, Response); duesee-imap-codec-0d00966/imap-codec/fuzz/fuzz_targets/tags_structured.rs000066400000000000000000000033561507724125200265670ustar00rootroot00000000000000#![no_main] use arbitrary::{Arbitrary, Unstructured}; use imap_codec::fuzz::fuzz_tag_imap; use libfuzzer_sys::fuzz_target; /// Define a struct that represents fuzz target inputs. /// Includes both potentially valid and invalid tag patterns. #[derive(Debug)] struct TagInput { /// String representing a valid or invalid IMAP tag tag: String, /// Additional field to simulate complex scenarios or invalid patterns noise: Option, } impl<'a> Arbitrary<'a> for TagInput { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let tag = u.arbitrary::()?; let noise = u.arbitrary::>()?; Ok(Self { tag, noise }) } } fuzz_target!(|data: TagInput| { // Combine tag and noise to generate the input let mut input = data.tag; if let Some(noise) = data.noise { input.push_str(&noise); } // Convert the input string to bytes let input_bytes = input.as_bytes(); // Attempt to parse the input as an IMAP tag match fuzz_tag_imap(input_bytes) { Ok((_, parsed_tag)) => { // If parsing succeeds, validate the output // Ensure the parsed tag is a subset of the original input assert!(input.starts_with(parsed_tag.as_ref())); // Optionally, check if the remaining bytes match the expected pattern for noise // add more specific validations based on fuzzing requirements } Err(_) => { // If parsing fails, check specific conditions // Ensure that failure is due to the expected reasons // Check for specific patterns that are known to be invalid } } // Additional checks for performance metrics or memory safety checks }); duesee-imap-codec-0d00966/imap-codec/fuzz/imap.dict000066400000000000000000000022601507724125200220340ustar00rootroot00000000000000"a001 login mrc secret" "a001 OK LOGIN completed" "a002 select inbox" "* 18 EXISTS" "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)" "* 2 RECENT" "* OK [UNSEEN 17] Message 17 is the first unseen message" "* OK [UIDVALIDITY 3857529045] UIDs valid" "a002 OK [READ-WRITE] SELECT completed" "a003 fetch 12 full" "* 12 FETCH (FLAGS (\\Seen) INTERNALDATE \"17-Jul-1996 02:44:25 -0700\" RFC822.SIZE 4286 ENVELOPE (\"Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\" \"IMAP4rev1 WG mtg summary and minutes\" ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((NIL NIL \"imap\" \"cac.washington.edu\")) ((NIL NIL \"minutes\" \"CNRI.Reston.VA.US\")(\"John Klensin\" NIL \"KLENSIN\" \"MIT.EDU\")) NIL NIL \"\") BODY (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 3028 92))" "a003 OK FETCH completed" "a004 fetch 12 body[header]" "a004 OK FETCH completed" "a005 store 12 +flags \\deleted" "* 12 FETCH (FLAGS (\\Seen \\Deleted))" "a005 OK +FLAGS completed" "a006 logout" "* BYE IMAP4rev1 server terminating connection" "a006 OK LOGOUT completed" duesee-imap-codec-0d00966/imap-codec/fuzz/src/000077500000000000000000000000001507724125200210305ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/fuzz/src/lib.rs000066400000000000000000000077051507724125200221550ustar00rootroot00000000000000#[macro_export] macro_rules! impl_decode_target { ($codec:ident) => { use libfuzzer_sys::fuzz_target; fuzz_target!(|input: &[u8]| { #[cfg(feature = "debug")] use imap_codec::imap_types::utils::escape_byte_string; use imap_codec::{decode::Decoder, encode::Encoder}; #[cfg(feature = "debug")] println!("[!] Input: {}", escape_byte_string(input)); if let Ok((_rem, parsed1)) = $codec::default().decode(input) { #[cfg(feature = "debug")] { let input = &input[..input.len() - _rem.len()]; println!("[!] Consumed: {}", escape_byte_string(input)); println!("[!] Parsed1: {parsed1:?}"); } let output = $codec::default().encode(&parsed1).dump(); #[cfg(feature = "debug")] println!("[!] Serialized: {}", escape_byte_string(&output)); let (rem, parsed2) = $codec::default().decode(&output).unwrap(); #[cfg(feature = "debug")] println!("[!] Parsed2: {parsed2:?}"); assert!(rem.is_empty()); assert_eq!(parsed1, parsed2); /* #[cfg(feature = "split")] { // Check splits ... #[cfg(feature = "debug")] println!("[!] Full: {}", escape_byte_string(&output)); #[cfg(feature = "debug")] println!("[!] Full: {parsed2:?}"); for index in 0..=output.len() { let partial = &output[..index]; #[cfg(feature = "debug")] println!("[!] Split (..{index:>3}): {}", escape_byte_string(partial)); match <$decoder>::decode(partial) { Ok((rem, out)) => { assert!(rem.is_empty()); assert_eq!(index, output.len()); print!("\r{index}"); } Err(error) => match error { DecodeError::Incomplete => { assert!(index < output.len()); } DecodeError::LiteralFound { .. } => { assert!(index < output.len()); } DecodeError::Failed => { panic!("Expected `Ok` or `Incomplete`, got `Failed`"); } }, } } } */ } else { #[cfg(feature = "debug")] println!("[!] "); } #[cfg(feature = "debug")] println!("{}", str::repeat("-", 120)); }); }; } #[macro_export] macro_rules! impl_to_bytes_and_back { ($codec:tt, $object:tt) => { use libfuzzer_sys::fuzz_target; fuzz_target!(|input: $object| { #[cfg(feature = "debug")] use imap_codec::imap_types::utils::escape_byte_string; use imap_codec::{decode::Decoder, encode::Encoder}; #[cfg(feature = "debug")] println!("[!] Input: {:?}", input); let buffer = <$codec>::default().encode(&input).dump(); #[cfg(feature = "debug")] println!("[!] Serialized: {}", escape_byte_string(&buffer)); let (rem, parsed) = <$codec>::default().decode(&buffer).unwrap(); assert!(rem.is_empty()); #[cfg(feature = "debug")] println!("[!] Parsed: {parsed:?}"); assert_eq!(input, parsed); #[cfg(feature = "debug")] println!("{}", str::repeat("-", 120)); }); }; } duesee-imap-codec-0d00966/imap-codec/fuzz/terminals.dict000066400000000000000000000022451507724125200231070ustar00rootroot00000000000000"==" "\\*" "7BIT" "8BIT" "ALERT" "ALL" "\\Answered" "ANSWERED" "APPEND" "APPLICATION" "Apr" "AUDIO" "Aug" "AUTH=" "AUTHENTICATE" "BAD" "BADCHARSET" "BASE64" "BCC" "BEFORE" "BINARY" "BODY" "BODY.PEEK" "BYE" "CAPABILITY" "CC" "CHARSET" "CHECK" "CLOSE" "COPY" "CREATE" "Dec" "DELETE" "\\Deleted" "DELETED" "\\Draft" "DRAFT" "ENVELOPE" "EXAMINE" "EXISTS" "EXPUNGE" "FAST" "Feb" "FETCH" "\\Flagged" "FLAGGED" "FLAGS" "FROM" "FULL" ".HEADER" "HEADER" "HEADER.FIELDS" "IMAGE" "IMAP4rev1" "INBOX" "INTERNALDATE" "Jan" "Jul" "Jun" "KEYWORD" "LARGER" "LIST" "LOGIN" "LOGOUT" "LSUB" "Mar" "\\Marked" "May" "MESSAGE" "MESSAGES" "MIME" "NEW" "NIL" "NO" "\\Noinferiors" "NOOP" "\\Noselect" ".NOT" "NOT" "Nov" "Oct" "OK" "OLD" "ON" "OR" "PARSE" "PERMANENTFLAGS" "PREAUTH" "QUOTED-PRINTABLE" "READ-ONLY" "READ-WRITE" "\\Recent" "RECENT" "RENAME" "RFC822" "RFC822.SIZE" "SEARCH" "\\Seen" "SEEN" "SELECT" "SENTBEFORE" "SENTON" "SENTSINCE" "Sep" ".SILENT" "SINCE" ".SIZE" "SMALLER" "STARTTLS" "STATUS" "STORE" "STRUCTURE" "SUBJECT" "SUBSCRIBE" ".TEXT" "TEXT" "TO" "TRYCREATE" "UID" "UIDNEXT" "UIDVALIDITY" "UNANSWERED" "UNDELETED" "UNDRAFT" "UNFLAGGED" "UNKEYWORD" "\\Unmarked" "UNSEEN" "UNSUBSCRIBE" "VIDEO"duesee-imap-codec-0d00966/imap-codec/src/000077500000000000000000000000001507724125200200325ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/src/auth.rs000066400000000000000000000072051507724125200213450ustar00rootroot00000000000000#[cfg(not(feature = "quirk_crlf_relaxed"))] use abnf_core::streaming::crlf; #[cfg(feature = "quirk_crlf_relaxed")] use abnf_core::streaming::crlf_relaxed as crlf; use imap_types::auth::{AuthMechanism, AuthenticateData}; use nom::{ branch::alt, bytes::streaming::tag, combinator::{map, value}, sequence::{terminated, tuple}, }; use crate::{ core::{atom, base64}, decode::IMAPResult, }; // ----- Unsorted IMAP parsers ----- /// `auth-type = atom` /// /// Note: Defined by \[SASL\] pub(crate) fn auth_type(input: &[u8]) -> IMAPResult<&[u8], AuthMechanism> { let (rem, atom) = atom(input)?; Ok((rem, AuthMechanism::from(atom))) } /// `authenticate = "AUTHENTICATE" SP auth-type *(CRLF base64)` (edited) /// /// ```text /// authenticate = base64 CRLF /// vvvvvvvvvvvv /// | /// This is parsed here. /// CRLF is additionally parsed in this parser. /// FIXME: Multiline base64 currently does not work. /// ``` pub(crate) fn authenticate_data(input: &[u8]) -> IMAPResult<&[u8], AuthenticateData> { alt(( map(terminated(base64, crlf), AuthenticateData::r#continue), value(AuthenticateData::Cancel, tuple((tag("*"), crlf))), ))(input) } #[cfg(test)] mod tests { use super::*; use crate::testing::{known_answer_test_encode, known_answer_test_parse}; #[test] fn test_encode_auth_mechanism() { let tests = [ (AuthMechanism::Plain, b"PLAIN".as_ref()), (AuthMechanism::Login, b"LOGIN"), (AuthMechanism::OAuthBearer, b"OAUTHBEARER"), (AuthMechanism::try_from("PLAINX").unwrap(), b"PLAINX"), (AuthMechanism::try_from("LOGINX").unwrap(), b"LOGINX"), (AuthMechanism::try_from("XOAUTH2X").unwrap(), b"XOAUTH2X"), ]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_parse_auth_type() { let tests = [ (b"plain ".as_ref(), b" ".as_ref(), AuthMechanism::Plain), (b"pLaiN ", b" ", AuthMechanism::Plain), (b"lOgiN ", b" ", AuthMechanism::Login), (b"login ", b" ", AuthMechanism::Login), (b"loginX ", b" ", AuthMechanism::try_from("loginX").unwrap()), ( b"loginX ", b" ", AuthMechanism::try_from(b"loginX".as_ref()).unwrap(), ), (b"Xplain ", b" ", AuthMechanism::try_from("Xplain").unwrap()), ( b"Xplain ", b" ", AuthMechanism::try_from(b"Xplain".as_ref()).unwrap(), ), ( b"oauthbearer ".as_ref(), b" ".as_ref(), AuthMechanism::OAuthBearer, ), (b"oAuThbearEr ", b" ", AuthMechanism::OAuthBearer), (b"xoauth2 ".as_ref(), b" ".as_ref(), AuthMechanism::XOAuth2), (b"xOauTh2 ", b" ", AuthMechanism::XOAuth2), ]; for test in tests { known_answer_test_parse(test, auth_type); } } #[test] fn test_authenticate_data() { let tests = [ (b"*\r\n ".as_ref(), b" ".as_ref(), AuthenticateData::Cancel), ( b"AA==\r\n ".as_ref(), b" ".as_ref(), AuthenticateData::r#continue(b"\x00".as_ref()), ), ( b"aQ==\r\n ".as_ref(), b" ".as_ref(), AuthenticateData::r#continue(b"\x69".to_vec()), ), ]; for test in tests { known_answer_test_parse(test, authenticate_data); } } } duesee-imap-codec-0d00966/imap-codec/src/body.rs000066400000000000000000000637731507724125200213550ustar00rootroot00000000000000use abnf_core::streaming::sp; use imap_types::{ body::{ BasicFields, Body, BodyExtension, BodyStructure, Disposition, Language, Location, MultiPartExtensionData, SinglePartExtensionData, SpecificFields, }, core::{IString, NString, Vec1}, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, opt}, multi::{many0, many1, separated_list0, separated_list1}, sequence::{delimited, preceded, tuple}, }; use crate::{ core::{nil, nstring, number, string}, decode::{IMAPErrorKind, IMAPParseError, IMAPResult}, envelope::envelope, }; /// `body = "(" (body-type-1part / body-type-mpart) ")"` /// /// Note: This parser is recursively defined. Thus, in order to not overflow the stack, /// it is needed to limit how may recursions are allowed. (8 should suffice). pub(crate) fn body( remaining_recursions: usize, ) -> impl Fn(&[u8]) -> IMAPResult<&[u8], BodyStructure> { move |input: &[u8]| body_limited(input, remaining_recursions) } fn body_limited(input: &[u8], remaining_recursions: usize) -> IMAPResult<&[u8], BodyStructure> { if remaining_recursions == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let body_type_1part = |input| body_type_1part_limited(input, remaining_recursions.saturating_sub(1)); let body_type_mpart = |input| body_type_mpart_limited(input, remaining_recursions.saturating_sub(1)); delimited( tag(b"("), alt((body_type_1part, body_type_mpart)), tag(b")"), )(input) } /// `body-type-1part = ( /// body-type-basic / /// body-type-msg / /// body-type-text /// ) /// [SP body-ext-1part]` /// /// Note: This parser is recursively defined. Thus, in order to not overflow the stack, /// it is needed to limit how may recursions are allowed. fn body_type_1part_limited( input: &[u8], remaining_recursions: usize, ) -> IMAPResult<&[u8], BodyStructure> { if remaining_recursions == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let body_type_msg = |input| body_type_msg_limited(input, 8); let mut parser = tuple(( alt((body_type_msg, body_type_text, body_type_basic)), opt(preceded(sp, body_ext_1part)), )); let (remaining, ((basic, specific), extension_data)) = parser(input)?; Ok(( remaining, BodyStructure::Single { body: Body { basic, specific }, extension_data, }, )) } /// `body-type-basic = media-basic SP body-fields` /// /// MESSAGE subtype MUST NOT be "RFC822" pub(crate) fn body_type_basic(input: &[u8]) -> IMAPResult<&[u8], (BasicFields, SpecificFields)> { let mut parser = tuple((media_basic, sp, body_fields)); let (remaining, ((type_, subtype), _, basic)) = parser(input)?; Ok(( remaining, ( basic, SpecificFields::Basic { r#type: type_, subtype, }, ), )) } /// `body-type-msg = media-message SP /// body-fields SP /// envelope SP /// body SP /// body-fld-lines` /// /// Note: This parser is recursively defined. Thus, in order to not overflow the stack, /// it is needed to limit how may recursions are allowed. (8 should suffice). fn body_type_msg_limited( input: &[u8], remaining_recursions: usize, ) -> IMAPResult<&[u8], (BasicFields, SpecificFields)> { if remaining_recursions == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let body = |input| body_limited(input, remaining_recursions.saturating_sub(1)); let mut parser = tuple(( media_message, sp, body_fields, sp, envelope, sp, body, sp, body_fld_lines, )); let (remaining, (_, _, basic, _, envelope, _, body_structure, _, number_of_lines)) = parser(input)?; Ok(( remaining, ( basic, SpecificFields::Message { envelope: Box::new(envelope), body_structure: Box::new(body_structure), number_of_lines, }, ), )) } /// `body-type-text = media-text SP /// body-fields SP /// body-fld-lines` pub(crate) fn body_type_text(input: &[u8]) -> IMAPResult<&[u8], (BasicFields, SpecificFields)> { let mut parser = tuple((media_text, sp, body_fields, sp, body_fld_lines)); let (remaining, (subtype, _, basic, _, number_of_lines)) = parser(input)?; Ok(( remaining, ( basic, SpecificFields::Text { subtype, number_of_lines, }, ), )) } /// `body-fields = body-fld-param SP /// body-fld-id SP /// body-fld-desc SP /// body-fld-enc SP /// body-fld-octets` pub(crate) fn body_fields(input: &[u8]) -> IMAPResult<&[u8], BasicFields> { let mut parser = tuple(( body_fld_param, sp, body_fld_id, sp, body_fld_desc, sp, body_fld_enc, sp, body_fld_octets, )); let (remaining, (parameter_list, _, id, _, description, _, content_transfer_encoding, _, size)) = parser(input)?; Ok(( remaining, BasicFields { parameter_list, id, description, content_transfer_encoding, size, }, )) } /// ```abnf /// body-fld-param = "(" /// string SP string /// *(SP string SP string) /// ")" / nil /// ``` pub(crate) fn body_fld_param(input: &[u8]) -> IMAPResult<&[u8], Vec<(IString, IString)>> { let mut parser = alt(( delimited( tag(b"("), // Quirk: See https://github.com/emersion/go-imap/issues/557 separated_list0( sp, map(tuple((string, sp, string)), |(key, _, value)| (key, value)), ), tag(b")"), ), map(nil, |_| vec![]), )); let (remaining, parsed_body_fld_param) = parser(input)?; Ok((remaining, parsed_body_fld_param)) } #[inline] /// `body-fld-id = nstring` pub(crate) fn body_fld_id(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `body-fld-desc = nstring` pub(crate) fn body_fld_desc(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `body-fld-enc = ( /// DQUOTE ( /// "7BIT" / /// "8BIT" / /// "BINARY" / /// "BASE64"/ /// "QUOTED-PRINTABLE" /// ) DQUOTE /// ) / string` /// /// Simplified... /// /// `body-fld-enc = string` /// /// TODO: why the special case? /// /// # Quirks /// /// The following erroneous content observed: /// /// * A NIL value in maddy. pub(crate) fn body_fld_enc(input: &[u8]) -> IMAPResult<&[u8], IString> { #[cfg(not(feature = "quirk_body_fld_enc_nil_to_empty"))] return string(input); #[cfg(feature = "quirk_body_fld_enc_nil_to_empty")] map(nstring, |enc| match enc.0 { Some(enc) => enc, None => IString::try_from("").unwrap(), })(input) } #[inline] /// `body-fld-octets = number` /// /// # Quirks /// /// The following erroneous messages were observed: /// /// * A negative number, specifically `-1`, in Dovecot. #[allow(clippy::needless_return)] pub(crate) fn body_fld_octets(input: &[u8]) -> IMAPResult<&[u8], u32> { #[cfg(not(feature = "quirk_rectify_numbers"))] return number(input); #[cfg(feature = "quirk_rectify_numbers")] { return alt(( number, map(tuple((tag("-"), number)), |(_, _)| { log::warn!("Rectified negative number to 0"); 0 }), ))(input); } } #[inline] /// `body-fld-lines = number` pub(crate) fn body_fld_lines(input: &[u8]) -> IMAPResult<&[u8], u32> { number(input) } /// ```abnf /// body-ext-1part = body-fld-md5 /// [SP body-fld-dsp /// [SP body-fld-lang /// [SP body-fld-loc *(SP body-extension)] /// ] /// ] /// ``` /// /// Note: MUST NOT be returned on non-extensible "BODY" fetch. pub(crate) fn body_ext_1part(input: &[u8]) -> IMAPResult<&[u8], SinglePartExtensionData> { map( tuple(( body_fld_md5, opt(map( tuple(( preceded(sp, body_fld_dsp), opt(map( tuple(( preceded(sp, body_fld_lang), opt(map( tuple(( preceded(sp, body_fld_loc), many0(preceded(sp, body_extension(8))), )), |(location, extensions)| Location { location, extensions, }, )), )), |(language, tail)| Language { language, tail }, )), )), |(disposition, tail)| Disposition { disposition, tail }, )), )), |(md5, tail)| SinglePartExtensionData { md5, tail }, )(input) } #[inline] /// `body-fld-md5 = nstring` pub(crate) fn body_fld_md5(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } /// `body-fld-dsp = "(" string SP body-fld-param ")" / nil` #[allow(clippy::type_complexity)] pub(crate) fn body_fld_dsp( input: &[u8], ) -> IMAPResult<&[u8], Option<(IString, Vec<(IString, IString)>)>> { alt(( delimited( tag(b"("), map( tuple((string, sp, body_fld_param)), |(string, _, body_fld_param)| Some((string, body_fld_param)), ), tag(b")"), ), map(nil, |_| None), ))(input) } /// `body-fld-lang = nstring / "(" string *(SP string) ")"` pub(crate) fn body_fld_lang(input: &[u8]) -> IMAPResult<&[u8], Vec> { alt(( map(nstring, |nstring| match nstring.0 { Some(item) => vec![item], None => vec![], }), delimited(tag(b"("), separated_list1(sp, string), tag(b")")), ))(input) } #[inline] /// `body-fld-loc = nstring` pub(crate) fn body_fld_loc(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } /// Future expansion. /// /// Client implementations MUST accept body-extension fields. /// Server implementations MUST NOT generate body-extension fields except as defined by /// future standard or standards-track revisions of this specification. /// /// ```abnf /// body-extension = nstring / /// number / /// "(" body-extension *(SP body-extension) ")" /// ``` /// /// Note: This parser is recursively defined. Thus, in order to not overflow the stack, /// it is needed to limit how may recursions are allowed. (8 should suffice). pub(crate) fn body_extension( remaining_recursions: usize, ) -> impl Fn(&[u8]) -> IMAPResult<&[u8], BodyExtension> { move |input: &[u8]| body_extension_limited(input, remaining_recursions) } fn body_extension_limited( input: &[u8], remaining_recursion: usize, ) -> IMAPResult<&[u8], BodyExtension> { if remaining_recursion == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let body_extension = |input| body_extension_limited(input, remaining_recursion.saturating_sub(1)); alt(( map(nstring, BodyExtension::NString), map(number, BodyExtension::Number), map( delimited(tag(b"("), separated_list1(sp, body_extension), tag(b")")), |body_extensions| BodyExtension::List(Vec1::unvalidated(body_extensions)), ), ))(input) } // --- /// `body-type-mpart = 1*body SP media-subtype [SP body-ext-mpart]` /// /// Note: This parser is recursively defined. Thus, in order to not overflow the stack, /// it is needed to limit how may recursions are allowed. fn body_type_mpart_limited( input: &[u8], remaining_recursion: usize, ) -> IMAPResult<&[u8], BodyStructure> { if remaining_recursion == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let mut parser = tuple(( many1(body(remaining_recursion)), sp, media_subtype, opt(preceded(sp, body_ext_mpart)), )); let (remaining, (bodies, _, subtype, extension_data)) = parser(input)?; Ok(( remaining, BodyStructure::Multi { // Safety: `unwrap` can't panic due to the use of `many1`. bodies: Vec1::try_from(bodies).unwrap(), subtype, extension_data, }, )) } /// ```abnf /// body-ext-mpart = body-fld-param /// [SP body-fld-dsp /// [SP body-fld-lang /// [SP body-fld-loc *(SP body-extension)] /// ] /// ] /// ``` /// /// Note: MUST NOT be returned on non-extensible "BODY" fetch. pub(crate) fn body_ext_mpart(input: &[u8]) -> IMAPResult<&[u8], MultiPartExtensionData> { map( tuple(( body_fld_param, opt(map( tuple(( preceded(sp, body_fld_dsp), opt(map( tuple(( preceded(sp, body_fld_lang), opt(map( tuple(( preceded(sp, body_fld_loc), many0(preceded(sp, body_extension(8))), )), |(location, extensions)| Location { location, extensions, }, )), )), |(language, tail)| Language { language, tail }, )), )), |(disposition, tail)| Disposition { disposition, tail }, )), )), |(parameter_list, tail)| MultiPartExtensionData { parameter_list, tail, }, )(input) } // --- /// `media-basic = ( /// ( DQUOTE /// ( /// "APPLICATION" / /// "AUDIO" / /// "IMAGE" / /// "MESSAGE" / /// "VIDEO" /// ) DQUOTE /// ) / string /// ) SP media-subtype` /// /// Simplified... /// /// `media-basic = string SP media-subtype` /// /// TODO: Why the special case? /// /// Defined in [MIME-IMT] pub(crate) fn media_basic(input: &[u8]) -> IMAPResult<&[u8], (IString, IString)> { let mut parser = tuple((string, sp, media_subtype)); let (remaining, (type_, _, subtype)) = parser(input)?; Ok((remaining, (type_, subtype))) } #[inline] /// `media-subtype = string` /// /// Defined in [MIME-IMT] pub(crate) fn media_subtype(input: &[u8]) -> IMAPResult<&[u8], IString> { string(input) } #[inline] /// `media-message = DQUOTE "MESSAGE" DQUOTE SP /// DQUOTE "RFC822" DQUOTE` /// /// Simplified: /// /// `media-message = "\"MESSAGE\" \"RFC822\""` /// /// Defined in [MIME-IMT] /// /// "message" "rfc822" basic specific-for-message-rfc822 extension pub(crate) fn media_message(input: &[u8]) -> IMAPResult<&[u8], &[u8]> { tag_no_case(b"\"MESSAGE\" \"RFC822\"")(input) } /// `media-text = DQUOTE "TEXT" DQUOTE SP media-subtype` /// /// Defined in [MIME-IMT] /// /// "text" "?????" basic specific-for-text extension pub(crate) fn media_text(input: &[u8]) -> IMAPResult<&[u8], IString> { let mut parser = preceded(tag_no_case(b"\"TEXT\" "), media_subtype); let (remaining, media_subtype) = parser(input)?; Ok((remaining, media_subtype)) } #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::{ core::{Literal, Quoted}, fetch::MessageDataItem, response::{Data, Response}, }; use super::*; use crate::testing::{kat_inverse_response, known_answer_test_encode}; #[test] fn test_parse_media_basic() { media_basic(b"\"application\" \"xxx\"").unwrap(); media_basic(b"\"unknown\" \"test\"").unwrap(); media_basic(b"\"x\" \"xxx\"").unwrap(); } #[test] fn test_parse_media_message() { media_message(b"\"message\" \"rfc822\"").unwrap(); } #[test] fn test_parse_media_text() { media_text(b"\"text\" \"html\"").unwrap(); } #[test] fn test_parse_body_ext_1part() { for test in [ b"nil|xxx".as_ref(), b"\"md5\"|xxx".as_ref(), b"\"md5\" nil|xxx".as_ref(), b"\"md5\" (\"dsp\" nil)|xxx".as_ref(), b"\"md5\" (\"dsp\" (\"key\" \"value\")) nil|xxx".as_ref(), b"\"md5\" (\"dsp\" (\"key\" \"value\")) \"swedish\"|xxx".as_ref(), b"\"md5\" (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\")|xxx".as_ref(), b"\"md5\" (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") nil|xxx".as_ref(), b"\"md5\" (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") \"loc\"|xxx".as_ref(), b"\"md5\" (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") \"loc\" (1 \"2\" (nil 4))|xxx".as_ref(), b"\"AABB\" NIL NIL NIL 1337|xxx", b"\"AABB\" NIL NIL NIL (1337)|xxx", b"\"AABB\" NIL NIL NIL (1337 1337)|xxx", b"\"AABB\" NIL NIL NIL (1337 (1337 (1337 \"FOO\" {0}\r\n)))|xxx", ] .iter() { let (rem, out) = body_ext_1part(test).unwrap(); println!("{out:?}"); assert_eq!(rem, b"|xxx"); } } #[test] fn test_body_rec() { let _ = body(8)(str::repeat("(", 1_000_000).as_bytes()); } #[test] fn test_parse_body_ext_mpart() { for test in [ b"nil|xxx".as_ref(), b"(\"key\" \"value\")|xxx".as_ref(), b"(\"key\" \"value\") nil|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" nil)|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) nil|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) \"swedish\"|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\")|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") nil|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") \"loc\"|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") \"loc\" (1 \"2\" (nil 4))|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") \"loc\" (1 \"2\" (nil 4) {0}\r\n)|xxx".as_ref(), b"(\"key\" \"value\") (\"dsp\" (\"key\" \"value\")) (\"german\" \"russian\") \"loc\" {0}\r\n {0}\r\n|xxx".as_ref(), ] .iter() { let (rem, out) = body_ext_mpart(test).unwrap(); println!("{out:?}"); assert_eq!(rem, b"|xxx"); } } #[test] fn test_parse_body() { dbg!(body(9)(b"((((((({0}\r\n {0}\r\n NIL NIL NIL {0}\r\n 0 \"FOO\" NIL NIL \"LOCATION\" 1337) \"mixed\") \"mixed\") \"mixed\") \"mixed\") \"mixed\") \"mixed\")|xxx").unwrap()); } #[test] fn test_kat_inverse_response_data() { kat_inverse_response(&[( b"* 3372220415 FETCH (BODYSTRUCTURE ((((((({0}\r\n {0}\r\n NIL NIL NIL {0}\r\n 0 \"FOO\" NIL NIL \"LOCATION\" 1337) \"mixed\") \"mixed\") \"mixed\") \"mixed\") \"mixed\") \"mixed\"))\r\n".as_ref(), b"".as_ref(), Response::Data(Data::Fetch { seq: NonZeroU32::try_from(3372220415).unwrap(), items: Vec1::from(MessageDataItem::BodyStructure( BodyStructure::Multi { bodies: Vec1::from(BodyStructure::Multi { bodies: Vec1::from(BodyStructure::Multi { bodies: Vec1::from(BodyStructure::Multi { bodies: Vec1::from(BodyStructure::Multi { bodies: Vec1::from(BodyStructure::Multi { bodies: Vec1::from(BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![], id: NString(None), description: NString(None), content_transfer_encoding: IString::from( Literal::try_from(b"".as_ref()) .unwrap(), ), size: 0, }, specific: SpecificFields::Basic { r#type: IString::from( Literal::try_from(b"".as_ref()) .unwrap(), ), subtype: IString::from( Literal::try_from(b"".as_ref()) .unwrap(), ), }, }, extension_data: Some(SinglePartExtensionData { md5: NString::try_from("FOO").unwrap(), tail: Some(Disposition{ disposition: None, tail: Some(Language { language: vec![], tail: Some(Location{ location: NString::try_from("LOCATION").unwrap(), extensions: vec![BodyExtension::Number(1337)], }) }) }) }), }), subtype: IString::try_from("mixed").unwrap(), extension_data: None, }), subtype: IString::try_from("mixed").unwrap(), extension_data: None, }), subtype: IString::try_from("mixed").unwrap(), extension_data: None, }), subtype: IString::try_from("mixed").unwrap(), extension_data: None, }), subtype: IString::try_from("mixed").unwrap(), extension_data: None, }), subtype: IString::try_from("mixed").unwrap(), extension_data: None, }, )), }), )]); } #[test] fn test_encode_single_part_extension_data() { let tests = [( SinglePartExtensionData { md5: NString(None), tail: Some(Disposition { disposition: None, tail: Some(Language { language: vec![], tail: Some(Location { location: NString::from(Quoted::try_from("").unwrap()), extensions: vec![], }), }), }), }, b"NIL NIL NIL \"\"".as_ref(), )]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_number_quirk() { assert_eq!(body_fld_octets(b"0)").unwrap().1, 0); assert_eq!(body_fld_octets(b"1)").unwrap().1, 1); #[cfg(not(feature = "quirk_rectify_numbers"))] { assert!(dbg!(body_fld_octets(b"-0)")).is_err()); assert!(body_fld_octets(b"-1)").is_err()); assert!(body_fld_octets(b"-999999)").is_err()); } #[cfg(feature = "quirk_rectify_numbers")] { assert_eq!(body_fld_octets(b"-0)").unwrap().1, 0); assert_eq!(body_fld_octets(b"-1)").unwrap().1, 0); assert_eq!(body_fld_octets(b"-999999)").unwrap().1, 0); } } } duesee-imap-codec-0d00966/imap-codec/src/codec.rs000066400000000000000000000246631507724125200214700ustar00rootroot00000000000000pub mod decode; pub mod encode; /// Codec for greetings. #[derive(Clone, Debug, Default, PartialEq)] // We use `#[non_exhaustive]` to prevent users from using struct literal syntax. // // This allows to add configuration options later. For example, the // codec could transparently replace all literals with non-sync literals. #[non_exhaustive] pub struct GreetingCodec; /// Codec for commands. #[derive(Clone, Debug, Default, PartialEq)] #[non_exhaustive] pub struct CommandCodec; /// Codec for authenticate data lines. #[derive(Clone, Debug, Default, PartialEq)] #[non_exhaustive] pub struct AuthenticateDataCodec; /// Codec for responses. #[derive(Clone, Debug, Default, PartialEq)] #[non_exhaustive] pub struct ResponseCodec; /// Codec for idle dones. #[derive(Clone, Debug, Default, PartialEq)] #[non_exhaustive] pub struct IdleDoneCodec; macro_rules! impl_codec_new { ($codec:ty) => { impl $codec { /// Create codec with default configuration. pub fn new() -> Self { Self::default() } } }; } impl_codec_new!(GreetingCodec); impl_codec_new!(CommandCodec); impl_codec_new!(AuthenticateDataCodec); impl_codec_new!(ResponseCodec); impl_codec_new!(IdleDoneCodec); #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::{ auth::AuthenticateData, command::{Command, CommandBody}, core::{IString, Literal, LiteralMode, NString, Tag, Vec1}, extensions::idle::IdleDone, fetch::MessageDataItem, mailbox::Mailbox, response::{Data, Greeting, GreetingKind, Response}, }; use super::*; use crate::{ decode::{CommandDecodeError, Decoder, GreetingDecodeError, ResponseDecodeError}, testing::{ kat_inverse_authenticate_data, kat_inverse_command, kat_inverse_done, kat_inverse_greeting, kat_inverse_response, }, }; #[test] fn test_kat_inverse_greeting() { kat_inverse_greeting(&[ ( b"* OK ...\r\n".as_ref(), b"".as_ref(), Greeting::new(GreetingKind::Ok, None, "...").unwrap(), ), ( b"* ByE .\r\n???", b"???", Greeting::new(GreetingKind::Bye, None, ".").unwrap(), ), ( b"* preaUth x\r\n?", b"?", Greeting::new(GreetingKind::PreAuth, None, "x").unwrap(), ), ]); } #[test] fn test_kat_inverse_command() { kat_inverse_command(&[ ( b"a nOOP\r\n".as_ref(), b"".as_ref(), Command::new("a", CommandBody::Noop).unwrap(), ), ( b"a NooP\r\n???", b"???", Command::new("a", CommandBody::Noop).unwrap(), ), ( b"a SeLECT {5}\r\ninbox\r\n", b"", Command::new( "a", CommandBody::Select { mailbox: Mailbox::Inbox, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }, ) .unwrap(), ), ( b"a SElECT {5}\r\ninbox\r\nxxx", b"xxx", Command::new( "a", CommandBody::Select { mailbox: Mailbox::Inbox, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }, ) .unwrap(), ), ]); } #[test] fn test_kat_inverse_response() { kat_inverse_response(&[ ( b"* SEARCH 1\r\n".as_ref(), b"".as_ref(), Response::Data(Data::Search( vec![NonZeroU32::new(1).unwrap()], #[cfg(feature = "ext_condstore_qresync")] None, )), ), ( b"* SEARCH 1\r\n???", b"???", Response::Data(Data::Search( vec![NonZeroU32::new(1).unwrap()], #[cfg(feature = "ext_condstore_qresync")] None, )), ), ( b"* 1 FETCH (RFC822 {5}\r\nhello)\r\n", b"", Response::Data(Data::Fetch { seq: NonZeroU32::new(1).unwrap(), items: Vec1::from(MessageDataItem::Rfc822(NString(Some(IString::Literal( Literal::try_from(b"hello".as_ref()).unwrap(), ))))), }), ), ]); } #[test] fn test_kat_inverse_authenticate_data() { kat_inverse_authenticate_data(&[ ( b"VGVzdA==\r\n".as_ref(), b"".as_ref(), AuthenticateData::r#continue(b"Test".to_vec()), ), ( b"AA==\r\n".as_ref(), b"".as_ref(), AuthenticateData::r#continue(b"\x00".to_vec()), ), ( b"aQ==\r\n".as_ref(), b"".as_ref(), AuthenticateData::r#continue(b"\x69".to_vec()), ), (b"*\r\n".as_ref(), b"".as_ref(), AuthenticateData::Cancel), ]); } #[test] fn test_kat_inverse_done() { kat_inverse_done(&[ (b"done\r\n".as_ref(), b"".as_ref(), IdleDone), (b"DONE\r\n".as_ref(), b"".as_ref(), IdleDone), ]); } #[test] fn test_greeting_incomplete_failed() { let tests = [ // Incomplete (b"*".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* ".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* O".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK ".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK .".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK .\r".as_ref(), Err(GreetingDecodeError::Incomplete)), // Failed (b"**".as_ref(), Err(GreetingDecodeError::Failed)), (b"* NO x\r\n".as_ref(), Err(GreetingDecodeError::Failed)), ]; for (test, expected) in tests { let got = GreetingCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = GreetingCodec::default().decode_static(test); assert_eq!(expected, got); } } } #[test] fn test_command_incomplete_failed() { let tests = [ // Incomplete (b"a".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a ".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a n".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a no".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a noo".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a noop".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a noop\r".as_ref(), Err(CommandDecodeError::Incomplete)), // LiteralAckRequired ( b"a select {5}\r\n".as_ref(), Err(CommandDecodeError::LiteralFound { tag: Tag::try_from("a").unwrap(), length: 5, mode: LiteralMode::Sync, }), ), ( b"a select {5+}\r\n".as_ref(), Err(CommandDecodeError::LiteralFound { tag: Tag::try_from("a").unwrap(), length: 5, mode: LiteralMode::NonSync, }), ), // Incomplete (after literal) ( b"a select {5}\r\nxxx".as_ref(), Err(CommandDecodeError::Incomplete), ), // Failed (b"* noop\r\n".as_ref(), Err(CommandDecodeError::Failed)), (b"A noop\r\n".as_ref(), Err(CommandDecodeError::Failed)), ]; for (test, expected) in tests { let got = CommandCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = CommandCodec::default().decode_static(test); assert_eq!(expected, got); } } } #[test] fn test_response_incomplete_failed() { let tests = [ // Incomplete (b"".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"*".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* ".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* S".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SE".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEA".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEAR".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARC".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARCH".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARCH ".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARCH 1".as_ref(), Err(ResponseDecodeError::Incomplete)), ( b"* SEARCH 1\r".as_ref(), Err(ResponseDecodeError::Incomplete), ), // LiteralAck treated as Incomplete ( b"* 1 FETCH (RFC822 {5}\r\n".as_ref(), Err(ResponseDecodeError::LiteralFound { length: 5 }), ), // Failed ( b"* search 1 2 3\r\n".as_ref(), Err(ResponseDecodeError::Failed), ), (b"A search\r\n".as_ref(), Err(ResponseDecodeError::Failed)), ]; for (test, expected) in tests { let got = ResponseCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = ResponseCodec::default().decode_static(test); assert_eq!(expected, got); } } } } duesee-imap-codec-0d00966/imap-codec/src/codec/000077500000000000000000000000001507724125200211075ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/src/codec/decode.rs000066400000000000000000000613601507724125200227060ustar00rootroot00000000000000//! # Decoding of messages. //! //! You can use [`Decoder`]s to parse messages. //! //! IMAP literals make separating the parsing logic from the application logic difficult. //! When a server recognizes a literal (e.g. `{42}\r\n`) in a command, it first needs to agree to receive more data by sending a so-called "command continuation request" (`+ ...`). //! Without a command continuation request, a client won't send more data, and the command parser on the server would always return `LiteralFound { length: 42, .. }`. //! This makes real-world decoding of IMAP more elaborate. //! //! Have a look at the [parse_command](https://github.com/duesee/imap-codec/blob/main/imap-codec/examples/parse_command.rs) example to see how a real-world application could decode IMAP. use std::{ num::{ParseIntError, TryFromIntError}, str::Utf8Error, }; use imap_types::{ IntoStatic, auth::AuthenticateData, command::Command, core::{LiteralMode, Tag}, extensions::idle::IdleDone, response::{Greeting, Response}, }; use nom::error::{ErrorKind, FromExternalError, ParseError}; use crate::{ AuthenticateDataCodec, CommandCodec, GreetingCodec, IdleDoneCodec, ResponseCodec, auth::authenticate_data, command::command, extensions::idle::idle_done, response::{greeting, response}, }; /// An extended version of [`nom::IResult`]. pub(crate) type IMAPResult<'a, I, O> = Result<(I, O), nom::Err>>; /// An extended version of [`nom::error::Error`]. #[derive(Debug)] pub(crate) struct IMAPParseError<'a, I> { #[allow(unused)] pub input: I, pub kind: IMAPErrorKind<'a>, } /// An extended version of [`nom::error::ErrorKind`]. #[derive(Debug)] pub(crate) enum IMAPErrorKind<'a> { Literal { tag: Option>, length: u32, mode: LiteralMode, }, BadNumber, BadBase64, BadUtf8, BadDateTime, LiteralContainsNull, RecursionLimitExceeded, Nom(#[allow(dead_code)] ErrorKind), } impl ParseError for IMAPParseError<'_, I> { fn from_error_kind(input: I, kind: ErrorKind) -> Self { Self { input, kind: IMAPErrorKind::Nom(kind), } } fn append(input: I, kind: ErrorKind, _: Self) -> Self { Self { input, kind: IMAPErrorKind::Nom(kind), } } } impl FromExternalError for IMAPParseError<'_, I> { fn from_external_error(input: I, _: ErrorKind, _: ParseIntError) -> Self { Self { input, kind: IMAPErrorKind::BadNumber, } } } impl FromExternalError for IMAPParseError<'_, I> { fn from_external_error(input: I, _: ErrorKind, _: TryFromIntError) -> Self { Self { input, kind: IMAPErrorKind::BadNumber, } } } impl FromExternalError for IMAPParseError<'_, I> { fn from_external_error(input: I, _: ErrorKind, _: base64::DecodeError) -> Self { Self { input, kind: IMAPErrorKind::BadBase64, } } } impl FromExternalError for IMAPParseError<'_, I> { fn from_external_error(input: I, _: ErrorKind, _: Utf8Error) -> Self { Self { input, kind: IMAPErrorKind::BadUtf8, } } } /// Decoder. /// /// Implemented for types that know how to decode a specific IMAP message. See [implementors](trait.Decoder.html#implementors). pub trait Decoder { type Message<'a>: Sized; type Error<'a>; fn decode<'a>(&self, input: &'a [u8]) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'a>>; fn decode_static<'a>( &self, input: &'a [u8], ) -> Result<(&'a [u8], Self::Message<'static>), Self::Error<'static>> where Self::Message<'a>: IntoStatic>, Self::Error<'a>: IntoStatic>, { let (remaining, value) = self.decode(input).map_err(IntoStatic::into_static)?; Ok((remaining, value.into_static())) } } /// Error during greeting decoding. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum GreetingDecodeError { /// More data is needed. Incomplete, /// Decoding failed. Failed, } impl IntoStatic for GreetingDecodeError { type Static = Self; fn into_static(self) -> Self::Static { self } } /// Error during command decoding. #[derive(Clone, Debug, Eq, PartialEq)] pub enum CommandDecodeError<'a> { /// More data is needed. Incomplete, /// More data is needed (and further action may be necessary). /// /// The decoder stopped at the beginning of literal data. Typically, a server MUST send a /// command continuation request to agree to the receival of the remaining data. This behaviour /// is different when `LITERAL+/LITERAL-` is used. /// /// # With `LITERAL+/LITERAL-` /// /// When the `mode` is sync, everything is the same as above. /// /// When the `mode` is non-sync, *and* the server advertised the LITERAL+ capability, /// it MUST NOT send a command continuation request and accept the data right away. /// /// When the `mode` is non-sync, *and* the server advertised the LITERAL- capability, /// *and* the literal length is smaller or equal than 4096, /// it MUST NOT send a command continuation request and accept the data right away. /// /// When the `mode` is non-sync, *and* the server advertised the LITERAL- capability, /// *and* the literal length is greater than 4096, /// it MUST be handled as sync. /// /// ```rust,ignore /// match mode { /// LiteralMode::Sync => /* Same as sync. */ /// LiteralMode::Sync => match advertised { /// Capability::LiteralPlus => /* Accept data right away. */ /// Capability::LiteralMinus => { /// if literal_length <= 4096 { /// /* Accept data right away. */ /// } else { /// /* Same as sync. */ /// } /// } /// } /// } /// ``` LiteralFound { /// The corresponding command (tag) to which this literal is bound. /// /// This is required to reject literals, e.g., when their size exceeds a limit. tag: Tag<'a>, /// Literal length. length: u32, /// Literal mode, i.e., sync or non-sync. mode: LiteralMode, }, /// Decoding failed. Failed, } impl IntoStatic for CommandDecodeError<'_> { type Static = CommandDecodeError<'static>; fn into_static(self) -> Self::Static { match self { CommandDecodeError::Incomplete => CommandDecodeError::Incomplete, CommandDecodeError::LiteralFound { tag, length, mode } => { CommandDecodeError::LiteralFound { tag: tag.into_static(), length, mode, } } CommandDecodeError::Failed => CommandDecodeError::Failed, } } } /// Error during authenticate data line decoding. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum AuthenticateDataDecodeError { /// More data is needed. Incomplete, /// Decoding failed. Failed, } impl IntoStatic for AuthenticateDataDecodeError { type Static = Self; fn into_static(self) -> Self::Static { self } } /// Error during response decoding. #[derive(Clone, Debug, Eq, PartialEq)] pub enum ResponseDecodeError { /// More data is needed. Incomplete, /// The decoder stopped at the beginning of literal data. /// /// The client *MUST* accept the literal and has no option to reject it. /// However, when the client ultimately does not want to handle the literal, it can do something /// similar to . /// /// It can implement a discarding mechanism, basically, consuming the whole literal but not /// saving the bytes in memory. Or, it can close the connection. LiteralFound { /// Literal length. length: u32, }, /// Decoding failed. Failed, } impl IntoStatic for ResponseDecodeError { type Static = Self; fn into_static(self) -> Self::Static { self } } /// Error during idle done decoding. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum IdleDoneDecodeError { /// More data is needed. Incomplete, /// Decoding failed. Failed, } impl IntoStatic for IdleDoneDecodeError { type Static = Self; fn into_static(self) -> Self::Static { self } } // ------------------------------------------------------------------------------------------------- impl Decoder for GreetingCodec { type Message<'a> = Greeting<'a>; type Error<'a> = GreetingDecodeError; fn decode<'a>( &self, input: &'a [u8], ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> { match greeting(input) { Ok((rem, grt)) => Ok((rem, grt)), Err(nom::Err::Incomplete(_)) => Err(GreetingDecodeError::Incomplete), Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err(GreetingDecodeError::Failed), } } } impl Decoder for CommandCodec { type Message<'a> = Command<'a>; type Error<'a> = CommandDecodeError<'a>; fn decode<'a>( &self, input: &'a [u8], ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'a>> { match command(input) { Ok((rem, cmd)) => Ok((rem, cmd)), Err(nom::Err::Incomplete(_)) => Err(CommandDecodeError::Incomplete), Err(nom::Err::Failure(error)) => match error { IMAPParseError { input: _, kind: IMAPErrorKind::Literal { tag, length, mode }, } => Err(CommandDecodeError::LiteralFound { // Unwrap: We *must* receive a `tag` during command parsing. tag: tag.expect("Expected `Some(tag)` in `IMAPErrorKind::Literal`, got `None`"), length, mode, }), _ => Err(CommandDecodeError::Failed), }, Err(nom::Err::Error(_)) => Err(CommandDecodeError::Failed), } } } impl Decoder for ResponseCodec { type Message<'a> = Response<'a>; type Error<'a> = ResponseDecodeError; fn decode<'a>( &self, input: &'a [u8], ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> { match response(input) { Ok((rem, rsp)) => Ok((rem, rsp)), Err(nom::Err::Incomplete(_)) => Err(ResponseDecodeError::Incomplete), Err(nom::Err::Error(error) | nom::Err::Failure(error)) => match error { IMAPParseError { kind: IMAPErrorKind::Literal { length, .. }, .. } => Err(ResponseDecodeError::LiteralFound { length }), _ => Err(ResponseDecodeError::Failed), }, } } } impl Decoder for AuthenticateDataCodec { type Message<'a> = AuthenticateData<'a>; type Error<'a> = AuthenticateDataDecodeError; fn decode<'a>( &self, input: &'a [u8], ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> { match authenticate_data(input) { Ok((rem, rsp)) => Ok((rem, rsp)), Err(nom::Err::Incomplete(_)) => Err(AuthenticateDataDecodeError::Incomplete), Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => { Err(AuthenticateDataDecodeError::Failed) } } } } impl Decoder for IdleDoneCodec { type Message<'a> = IdleDone; type Error<'a> = IdleDoneDecodeError; fn decode<'a>( &self, input: &'a [u8], ) -> Result<(&'a [u8], Self::Message<'a>), Self::Error<'static>> { match idle_done(input) { Ok((rem, rsp)) => Ok((rem, rsp)), Err(nom::Err::Incomplete(_)) => Err(IdleDoneDecodeError::Incomplete), Err(nom::Err::Failure(_)) | Err(nom::Err::Error(_)) => Err(IdleDoneDecodeError::Failed), } } } #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::{ command::{Command, CommandBody}, core::{IString, Literal, NString, Vec1}, extensions::idle::IdleDone, fetch::MessageDataItem, mailbox::Mailbox, response::{Data, Greeting, GreetingKind, Response}, }; use super::*; #[test] fn test_decode_greeting() { let tests = [ // Ok ( b"* OK ...\r\n".as_ref(), Ok(( b"".as_ref(), Greeting::new(GreetingKind::Ok, None, "...").unwrap(), )), ), ( b"* ByE .\r\n???".as_ref(), Ok(( b"???".as_ref(), Greeting::new(GreetingKind::Bye, None, ".").unwrap(), )), ), ( b"* preaUth x\r\n?".as_ref(), Ok(( b"?".as_ref(), Greeting::new(GreetingKind::PreAuth, None, "x").unwrap(), )), ), // Incomplete (b"*".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* ".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* O".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK ".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK .".as_ref(), Err(GreetingDecodeError::Incomplete)), (b"* OK .\r".as_ref(), Err(GreetingDecodeError::Incomplete)), // Failed (b"**".as_ref(), Err(GreetingDecodeError::Failed)), (b"* NO x\r\n".as_ref(), Err(GreetingDecodeError::Failed)), ]; for (test, expected) in tests { let got = GreetingCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = GreetingCodec::default().decode_static(test); assert_eq!(expected, got); } } } #[test] fn test_decode_command() { let tests = [ // Ok ( b"a noop\r\n".as_ref(), Ok((b"".as_ref(), Command::new("a", CommandBody::Noop).unwrap())), ), ( b"a noop\r\n???".as_ref(), Ok(( b"???".as_ref(), Command::new("a", CommandBody::Noop).unwrap(), )), ), ( b"a select {5}\r\ninbox\r\n".as_ref(), Ok(( b"".as_ref(), Command::new( "a", CommandBody::Select { mailbox: Mailbox::Inbox, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }, ) .unwrap(), )), ), ( b"a select {5}\r\ninbox\r\nxxx".as_ref(), Ok(( b"xxx".as_ref(), Command::new( "a", CommandBody::Select { mailbox: Mailbox::Inbox, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }, ) .unwrap(), )), ), // Incomplete (b"a".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a ".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a n".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a no".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a noo".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a noop".as_ref(), Err(CommandDecodeError::Incomplete)), (b"a noop\r".as_ref(), Err(CommandDecodeError::Incomplete)), // LiteralAckRequired ( b"a select {5}\r\n".as_ref(), Err(CommandDecodeError::LiteralFound { tag: Tag::try_from("a").unwrap(), length: 5, mode: LiteralMode::Sync, }), ), // Incomplete (after literal) ( b"a select {5}\r\nxxx".as_ref(), Err(CommandDecodeError::Incomplete), ), // Failed (b"* noop\r\n".as_ref(), Err(CommandDecodeError::Failed)), (b"A noop\r\n".as_ref(), Err(CommandDecodeError::Failed)), ]; for (test, expected) in tests { let got = CommandCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = CommandCodec::default().decode_static(test); assert_eq!(expected, got); } } } #[test] fn test_decode_authenticate_data() { let tests = [ // Ok ( b"VGVzdA==\r\n".as_ref(), Ok((b"".as_ref(), AuthenticateData::r#continue(b"Test".to_vec()))), ), ( b"VGVzdA==\r\nx".as_ref(), Ok(( b"x".as_ref(), AuthenticateData::r#continue(b"Test".to_vec()), )), ), ( b"*\r\n".as_ref(), Ok((b"".as_ref(), AuthenticateData::Cancel)), ), ( b"*\r\nx".as_ref(), Ok((b"x".as_ref(), AuthenticateData::Cancel)), ), // Incomplete (b"V".as_ref(), Err(AuthenticateDataDecodeError::Incomplete)), (b"VG".as_ref(), Err(AuthenticateDataDecodeError::Incomplete)), ( b"VGV".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVz".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVzd".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVzdA".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVzdA=".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVzdA==".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVzdA==\r".as_ref(), Err(AuthenticateDataDecodeError::Incomplete), ), ( b"VGVzdA==\r\n".as_ref(), Ok((b"".as_ref(), AuthenticateData::r#continue(b"Test".to_vec()))), ), // Failed ( b"VGVzdA== \r\n".as_ref(), Err(AuthenticateDataDecodeError::Failed), ), ( b" VGVzdA== \r\n".as_ref(), Err(AuthenticateDataDecodeError::Failed), ), ( b" V GVzdA== \r\n".as_ref(), Err(AuthenticateDataDecodeError::Failed), ), ( b" V GVzdA= \r\n".as_ref(), Err(AuthenticateDataDecodeError::Failed), ), ]; for (test, expected) in tests { let got = AuthenticateDataCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = AuthenticateDataCodec::default().decode_static(test); assert_eq!(expected, got); } } } #[test] fn test_decode_idle_done() { let tests = [ // Ok (b"done\r\n".as_ref(), Ok((b"".as_ref(), IdleDone))), (b"done\r\n?".as_ref(), Ok((b"?".as_ref(), IdleDone))), // Incomplete (b"d".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"do".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"don".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"done".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"done\r".as_ref(), Err(IdleDoneDecodeError::Incomplete)), // Failed (b"donee\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), (b" done\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), (b"done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), (b" done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), ]; for (test, expected) in tests { let got = IdleDoneCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = IdleDoneCodec::default().decode_static(test); assert_eq!(expected, got); } } } #[test] fn test_decode_response() { let tests = [ // Incomplete (b"".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"*".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* ".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* S".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SE".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEA".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEAR".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARC".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARCH".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARCH ".as_ref(), Err(ResponseDecodeError::Incomplete)), (b"* SEARCH 1".as_ref(), Err(ResponseDecodeError::Incomplete)), ( b"* SEARCH 1\r".as_ref(), Err(ResponseDecodeError::Incomplete), ), // Ok ( b"* SEARCH 1\r\n".as_ref(), Ok(( b"".as_ref(), Response::Data(Data::Search( vec![NonZeroU32::new(1).unwrap()], #[cfg(feature = "ext_condstore_qresync")] None, )), )), ), #[cfg(feature = "quirk_trailing_space_search")] ( b"* SEARCH \r\n".as_ref(), Ok(( b"".as_ref(), Response::Data(Data::Search( vec![], #[cfg(feature = "ext_condstore_qresync")] None, )), )), ), ( b"* SEARCH 1\r\n???".as_ref(), Ok(( b"???".as_ref(), Response::Data(Data::Search( vec![NonZeroU32::new(1).unwrap()], #[cfg(feature = "ext_condstore_qresync")] None, )), )), ), ( b"* 1 FETCH (RFC822 {5}\r\nhello)\r\n".as_ref(), Ok(( b"".as_ref(), Response::Data(Data::Fetch { seq: NonZeroU32::new(1).unwrap(), items: Vec1::from(MessageDataItem::Rfc822(NString(Some( IString::Literal(Literal::try_from(b"hello".as_ref()).unwrap()), )))), }), )), ), ( b"* 1 FETCH (RFC822 {5}\r\n".as_ref(), Err(ResponseDecodeError::LiteralFound { length: 5 }), ), // Failed ( b"* search 1 2 3\r\n".as_ref(), Err(ResponseDecodeError::Failed), ), #[cfg(not(feature = "quirk_trailing_space_search"))] (b"* search \r\n".as_ref(), Err(ResponseDecodeError::Failed)), (b"A search\r\n".as_ref(), Err(ResponseDecodeError::Failed)), ]; for (test, expected) in tests { let got = ResponseCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); { let got = ResponseCodec::default().decode_static(test); assert_eq!(expected, got); } } } } duesee-imap-codec-0d00966/imap-codec/src/codec/encode.rs000066400000000000000000002277321507724125200227270ustar00rootroot00000000000000//! # Encoding of messages. //! //! To facilitates handling of literals, [Encoder::encode] returns an instance of [`Encoded`]. //! The idea is that the encoder not only "dumps" the final serialization of a message but can be iterated over. //! //! # Example //! //! ```rust //! use imap_codec::{ //! CommandCodec, //! encode::{Encoder, Fragment}, //! imap_types::{ //! command::{Command, CommandBody}, //! core::LiteralMode, //! }, //! }; //! //! let command = Command::new("A1", CommandBody::login("Alice", "Pa²²W0rD").unwrap()).unwrap(); //! //! for fragment in CommandCodec::default().encode(&command) { //! match fragment { //! Fragment::Line { data } => { //! // A line that is ready to be send. //! println!("C: {}", String::from_utf8(data).unwrap()); //! } //! Fragment::Literal { data, mode } => match mode { //! LiteralMode::Sync => { //! // Wait for a continuation request. //! println!("S: + ...") //! } //! LiteralMode::NonSync => { //! // We don't need to wait for a continuation request //! // as the server will also not send it. //! } //! }, //! } //! } //! ``` //! //! Output of example: //! //! ```imap //! C: A1 LOGIN alice {10} //! S: + ... //! C: Pa²²W0rD //! ``` #[cfg(feature = "ext_condstore_qresync")] use std::num::NonZeroU64; use std::{borrow::Borrow, collections::VecDeque, io::Write, num::NonZeroU32}; use base64::{Engine, engine::general_purpose::STANDARD as base64}; use chrono::{DateTime as ChronoDateTime, FixedOffset}; #[cfg(feature = "ext_condstore_qresync")] use imap_types::command::{FetchModifier, SelectParameter, StoreModifier}; use imap_types::{ auth::{AuthMechanism, AuthenticateData}, body::{ BasicFields, Body, BodyExtension, BodyStructure, Disposition, Language, Location, MultiPartExtensionData, SinglePartExtensionData, SpecificFields, }, command::{Command, CommandBody}, core::{ AString, Atom, AtomExt, Charset, IString, Literal, LiteralMode, NString, NString8, Quoted, QuotedChar, Tag, Text, }, datetime::{DateTime, NaiveDate}, envelope::{Address, Envelope}, extensions::idle::IdleDone, fetch::{ Macro, MacroOrMessageDataItemNames, MessageDataItem, MessageDataItemName, Part, Section, }, flag::{Flag, FlagFetch, FlagNameAttribute, FlagPerm, StoreResponse, StoreType}, mailbox::{ListCharString, ListMailbox, Mailbox, MailboxOther}, response::{ Bye, Capability, Code, CodeOther, CommandContinuationRequest, Data, Greeting, GreetingKind, Response, Status, StatusBody, StatusKind, Tagged, }, search::SearchKey, sequence::{SeqOrUid, Sequence, SequenceSet}, status::{StatusDataItem, StatusDataItemName}, utils::escape_quoted, }; use utils::{List1AttributeValueOrNil, List1OrNil, join_serializable}; #[cfg(feature = "ext_namespace")] use crate::extensions::namespace::encode_namespaces; use crate::{AuthenticateDataCodec, CommandCodec, GreetingCodec, IdleDoneCodec, ResponseCodec}; /// Encoder. /// /// Implemented for types that know how to encode a specific IMAP message. See [implementors](trait.Encoder.html#implementors). pub trait Encoder { type Message<'a>; /// Encode this message. /// /// This will return an [`Encoded`] message. fn encode(&self, message: &Self::Message<'_>) -> Encoded; } /// An encoded message. /// /// This struct facilitates the implementation of IMAP client- and server implementations by /// yielding the encoding of a message through [`Fragment`]s. This is required, because the usage of /// literals (and some other types) may change the IMAP message flow. Thus, in many cases, it is an /// error to just "dump" a message and send it over the network. /// /// # Example /// /// ```rust /// use imap_codec::{ /// CommandCodec, /// encode::{Encoder, Fragment}, /// imap_types::command::{Command, CommandBody}, /// }; /// /// let cmd = Command::new("A", CommandBody::login("alice", "pass").unwrap()).unwrap(); /// /// for fragment in CommandCodec::default().encode(&cmd) { /// match fragment { /// Fragment::Line { data } => {} /// Fragment::Literal { data, mode } => {} /// } /// } /// ``` #[derive(Clone, Debug)] pub struct Encoded { items: VecDeque, } impl Encoded { /// Dump the (remaining) encoded data without being guided by [`Fragment`]s. pub fn dump(self) -> Vec { let mut out = Vec::new(); for fragment in self.items { match fragment { Fragment::Line { mut data } => out.append(&mut data), Fragment::Literal { mut data, .. } => out.append(&mut data), } } out } } impl Iterator for Encoded { type Item = Fragment; fn next(&mut self) -> Option { self.items.pop_front() } } /// The intended action of a client or server. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Fragment { /// A line that is ready to be send. Line { data: Vec }, /// A literal that may require an action before it should be send. Literal { data: Vec, mode: LiteralMode }, } //-------------------------------------------------------------------------------------------------- #[derive(Clone, Debug, Default, Eq, PartialEq)] pub(crate) struct EncodeContext { accumulator: Vec, items: VecDeque, } impl EncodeContext { pub fn new() -> Self { Self::default() } pub fn push_line(&mut self) { self.items.push_back(Fragment::Line { data: std::mem::take(&mut self.accumulator), }) } pub fn push_literal(&mut self, mode: LiteralMode) { self.items.push_back(Fragment::Literal { data: std::mem::take(&mut self.accumulator), mode, }) } pub fn into_items(self) -> VecDeque { let Self { accumulator, mut items, } = self; if !accumulator.is_empty() { items.push_back(Fragment::Line { data: accumulator }); } items } #[cfg(test)] pub(crate) fn dump(self) -> Vec { let mut out = Vec::new(); for item in self.into_items() { match item { Fragment::Line { data } | Fragment::Literal { data, .. } => { out.extend_from_slice(&data) } } } out } } impl Write for EncodeContext { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.accumulator.extend_from_slice(buf); Ok(buf.len()) } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } macro_rules! impl_encoder_for_codec { ($codec:ty, $message:ty) => { impl Encoder for $codec { type Message<'a> = $message; fn encode(&self, message: &Self::Message<'_>) -> Encoded { let mut encode_context = EncodeContext::new(); EncodeIntoContext::encode_ctx(message.borrow(), &mut encode_context).unwrap(); Encoded { items: encode_context.into_items(), } } } }; } impl_encoder_for_codec!(GreetingCodec, Greeting<'a>); impl_encoder_for_codec!(CommandCodec, Command<'a>); impl_encoder_for_codec!(AuthenticateDataCodec, AuthenticateData<'a>); impl_encoder_for_codec!(ResponseCodec, Response<'a>); impl_encoder_for_codec!(IdleDoneCodec, IdleDone); // ------------------------------------------------------------------------------------------------- pub(crate) trait EncodeIntoContext { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()>; } // ----- Primitive --------------------------------------------------------------------------------- impl EncodeIntoContext for u32 { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.to_string().as_bytes()) } } impl EncodeIntoContext for u64 { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.to_string().as_bytes()) } } // ----- Command ----------------------------------------------------------------------------------- impl EncodeIntoContext for Command<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.tag.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.body.encode_ctx(ctx)?; ctx.write_all(b"\r\n") } } impl EncodeIntoContext for Tag<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.inner().as_bytes()) } } impl EncodeIntoContext for CommandBody<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { CommandBody::Capability => ctx.write_all(b"CAPABILITY"), CommandBody::Noop => ctx.write_all(b"NOOP"), CommandBody::Logout => ctx.write_all(b"LOGOUT"), #[cfg(feature = "starttls")] CommandBody::StartTLS => ctx.write_all(b"STARTTLS"), CommandBody::Authenticate { mechanism, initial_response, } => { ctx.write_all(b"AUTHENTICATE ")?; mechanism.encode_ctx(ctx)?; if let Some(ir) = initial_response { ctx.write_all(b" ")?; // RFC 4959 (https://datatracker.ietf.org/doc/html/rfc4959#section-3) // "To send a zero-length initial response, the client MUST send a single pad character ("="). // This indicates that the response is present, but is a zero-length string." if ir.declassify().is_empty() { ctx.write_all(b"=")?; } else { ctx.write_all(base64.encode(ir.declassify()).as_bytes())?; }; }; Ok(()) } CommandBody::Login { username, password } => { ctx.write_all(b"LOGIN ")?; username.encode_ctx(ctx)?; ctx.write_all(b" ")?; password.declassify().encode_ctx(ctx) } CommandBody::Select { mailbox, #[cfg(feature = "ext_condstore_qresync")] parameters, } => { ctx.write_all(b"SELECT ")?; mailbox.encode_ctx(ctx)?; #[cfg(feature = "ext_condstore_qresync")] if !parameters.is_empty() { ctx.write_all(b" (")?; join_serializable(parameters, b" ", ctx)?; ctx.write_all(b")")?; } Ok(()) } CommandBody::Unselect => ctx.write_all(b"UNSELECT"), CommandBody::Examine { mailbox, #[cfg(feature = "ext_condstore_qresync")] parameters, } => { ctx.write_all(b"EXAMINE ")?; mailbox.encode_ctx(ctx)?; #[cfg(feature = "ext_condstore_qresync")] if !parameters.is_empty() { ctx.write_all(b" (")?; join_serializable(parameters, b" ", ctx)?; ctx.write_all(b")")?; } Ok(()) } CommandBody::Create { mailbox } => { ctx.write_all(b"CREATE ")?; mailbox.encode_ctx(ctx) } CommandBody::Delete { mailbox } => { ctx.write_all(b"DELETE ")?; mailbox.encode_ctx(ctx) } CommandBody::Rename { from: mailbox, to: new_mailbox, } => { ctx.write_all(b"RENAME ")?; mailbox.encode_ctx(ctx)?; ctx.write_all(b" ")?; new_mailbox.encode_ctx(ctx) } CommandBody::Subscribe { mailbox } => { ctx.write_all(b"SUBSCRIBE ")?; mailbox.encode_ctx(ctx) } CommandBody::Unsubscribe { mailbox } => { ctx.write_all(b"UNSUBSCRIBE ")?; mailbox.encode_ctx(ctx) } CommandBody::List { reference, mailbox_wildcard, } => { ctx.write_all(b"LIST ")?; reference.encode_ctx(ctx)?; ctx.write_all(b" ")?; mailbox_wildcard.encode_ctx(ctx) } CommandBody::Lsub { reference, mailbox_wildcard, } => { ctx.write_all(b"LSUB ")?; reference.encode_ctx(ctx)?; ctx.write_all(b" ")?; mailbox_wildcard.encode_ctx(ctx) } CommandBody::Status { mailbox, item_names, } => { ctx.write_all(b"STATUS ")?; mailbox.encode_ctx(ctx)?; ctx.write_all(b" (")?; join_serializable(item_names, b" ", ctx)?; ctx.write_all(b")") } CommandBody::Append { mailbox, flags, date, message, } => { ctx.write_all(b"APPEND ")?; mailbox.encode_ctx(ctx)?; if !flags.is_empty() { ctx.write_all(b" (")?; join_serializable(flags, b" ", ctx)?; ctx.write_all(b")")?; } if let Some(date) = date { ctx.write_all(b" ")?; date.encode_ctx(ctx)?; } ctx.write_all(b" ")?; message.encode_ctx(ctx) } CommandBody::Check => ctx.write_all(b"CHECK"), CommandBody::Close => ctx.write_all(b"CLOSE"), CommandBody::Expunge => ctx.write_all(b"EXPUNGE"), CommandBody::ExpungeUid { sequence_set } => { ctx.write_all(b"UID EXPUNGE ")?; sequence_set.encode_ctx(ctx) } CommandBody::Search { charset, criteria, uid, } => { if *uid { ctx.write_all(b"UID SEARCH")?; } else { ctx.write_all(b"SEARCH")?; } if let Some(charset) = charset { ctx.write_all(b" CHARSET ")?; charset.encode_ctx(ctx)?; } ctx.write_all(b" ")?; join_serializable(criteria.as_ref(), b" ", ctx) } CommandBody::Sort { sort_criteria, charset, search_criteria, uid, } => { if *uid { ctx.write_all(b"UID SORT (")?; } else { ctx.write_all(b"SORT (")?; } join_serializable(sort_criteria.as_ref(), b" ", ctx)?; ctx.write_all(b") ")?; charset.encode_ctx(ctx)?; ctx.write_all(b" ")?; join_serializable(search_criteria.as_ref(), b" ", ctx) } CommandBody::Thread { algorithm, charset, search_criteria, uid, } => { if *uid { ctx.write_all(b"UID THREAD ")?; } else { ctx.write_all(b"THREAD ")?; } algorithm.encode_ctx(ctx)?; ctx.write_all(b" ")?; charset.encode_ctx(ctx)?; ctx.write_all(b" ")?; join_serializable(search_criteria.as_ref(), b" ", ctx) } CommandBody::Fetch { sequence_set, macro_or_item_names, uid, #[cfg(feature = "ext_condstore_qresync")] modifiers, } => { if *uid { ctx.write_all(b"UID FETCH ")?; } else { ctx.write_all(b"FETCH ")?; } sequence_set.encode_ctx(ctx)?; ctx.write_all(b" ")?; macro_or_item_names.encode_ctx(ctx)?; #[cfg(feature = "ext_condstore_qresync")] if !modifiers.is_empty() { ctx.write_all(b" (")?; join_serializable(modifiers, b" ", ctx)?; ctx.write_all(b")")?; } Ok(()) } CommandBody::Store { sequence_set, kind, response, flags, uid, #[cfg(feature = "ext_condstore_qresync")] modifiers, } => { if *uid { ctx.write_all(b"UID STORE ")?; } else { ctx.write_all(b"STORE ")?; } sequence_set.encode_ctx(ctx)?; ctx.write_all(b" ")?; #[cfg(feature = "ext_condstore_qresync")] if !modifiers.is_empty() { ctx.write_all(b"(")?; join_serializable(modifiers, b" ", ctx)?; ctx.write_all(b") ")?; } match kind { StoreType::Add => ctx.write_all(b"+")?, StoreType::Remove => ctx.write_all(b"-")?, StoreType::Replace => {} } ctx.write_all(b"FLAGS")?; match response { StoreResponse::Answer => {} StoreResponse::Silent => ctx.write_all(b".SILENT")?, } ctx.write_all(b" (")?; join_serializable(flags, b" ", ctx)?; ctx.write_all(b")") } CommandBody::Copy { sequence_set, mailbox, uid, } => { if *uid { ctx.write_all(b"UID COPY ")?; } else { ctx.write_all(b"COPY ")?; } sequence_set.encode_ctx(ctx)?; ctx.write_all(b" ")?; mailbox.encode_ctx(ctx) } CommandBody::Idle => ctx.write_all(b"IDLE"), CommandBody::Enable { capabilities } => { ctx.write_all(b"ENABLE ")?; join_serializable(capabilities.as_ref(), b" ", ctx) } CommandBody::Compress { algorithm } => { ctx.write_all(b"COMPRESS ")?; algorithm.encode_ctx(ctx) } CommandBody::GetQuota { root } => { ctx.write_all(b"GETQUOTA ")?; root.encode_ctx(ctx) } CommandBody::GetQuotaRoot { mailbox } => { ctx.write_all(b"GETQUOTAROOT ")?; mailbox.encode_ctx(ctx) } CommandBody::SetQuota { root, quotas } => { ctx.write_all(b"SETQUOTA ")?; root.encode_ctx(ctx)?; ctx.write_all(b" (")?; join_serializable(quotas.as_ref(), b" ", ctx)?; ctx.write_all(b")") } CommandBody::Move { sequence_set, mailbox, uid, } => { if *uid { ctx.write_all(b"UID MOVE ")?; } else { ctx.write_all(b"MOVE ")?; } sequence_set.encode_ctx(ctx)?; ctx.write_all(b" ")?; mailbox.encode_ctx(ctx) } #[cfg(feature = "ext_id")] CommandBody::Id { parameters } => { ctx.write_all(b"ID ")?; match parameters { Some(parameters) => { if let Some((first, tail)) = parameters.split_first() { ctx.write_all(b"(")?; first.0.encode_ctx(ctx)?; ctx.write_all(b" ")?; first.1.encode_ctx(ctx)?; for parameter in tail { ctx.write_all(b" ")?; parameter.0.encode_ctx(ctx)?; ctx.write_all(b" ")?; parameter.1.encode_ctx(ctx)?; } ctx.write_all(b")") } else { #[cfg(not(feature = "quirk_id_empty_to_nil"))] { ctx.write_all(b"()") } #[cfg(feature = "quirk_id_empty_to_nil")] { ctx.write_all(b"NIL") } } } None => ctx.write_all(b"NIL"), } } #[cfg(feature = "ext_metadata")] CommandBody::SetMetadata { mailbox, entry_values, } => { ctx.write_all(b"SETMETADATA ")?; mailbox.encode_ctx(ctx)?; ctx.write_all(b" (")?; join_serializable(entry_values.as_ref(), b" ", ctx)?; ctx.write_all(b")") } #[cfg(feature = "ext_metadata")] CommandBody::GetMetadata { options, mailbox, entries, } => { ctx.write_all(b"GETMETADATA")?; if !options.is_empty() { ctx.write_all(b" (")?; join_serializable(options, b" ", ctx)?; ctx.write_all(b")")?; } ctx.write_all(b" ")?; mailbox.encode_ctx(ctx)?; ctx.write_all(b" ")?; if entries.as_ref().len() == 1 { entries.as_ref()[0].encode_ctx(ctx) } else { ctx.write_all(b"(")?; join_serializable(entries.as_ref(), b" ", ctx)?; ctx.write_all(b")") } } #[cfg(feature = "ext_namespace")] &CommandBody::Namespace => ctx.write_all(b"NAMESPACE"), } } } #[cfg(feature = "ext_condstore_qresync")] impl EncodeIntoContext for FetchModifier { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { FetchModifier::ChangedSince(since) => write!(ctx, "CHANGEDSINCE {since}"), FetchModifier::Vanished => write!(ctx, "VANISHED"), } } } #[cfg(feature = "ext_condstore_qresync")] impl EncodeIntoContext for StoreModifier { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { StoreModifier::UnchangedSince(since) => write!(ctx, "UNCHANGEDSINCE {since}"), } } } #[cfg(feature = "ext_condstore_qresync")] impl EncodeIntoContext for SelectParameter { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { SelectParameter::CondStore => write!(ctx, "CONDSTORE"), SelectParameter::QResync { uid_validity, mod_sequence_value, known_uids, seq_match_data, } => { write!(ctx, "QRESYNC (")?; uid_validity.encode_ctx(ctx)?; write!(ctx, " ")?; mod_sequence_value.encode_ctx(ctx)?; if let Some(known_uids) = known_uids { write!(ctx, " ")?; known_uids.encode_ctx(ctx)?; } if let Some((known_sequence_set, known_uid_set)) = seq_match_data { write!(ctx, " (")?; known_sequence_set.encode_ctx(ctx)?; write!(ctx, " ")?; known_uid_set.encode_ctx(ctx)?; write!(ctx, ")")?; } write!(ctx, ")") } } } } impl EncodeIntoContext for AuthMechanism<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } impl EncodeIntoContext for AuthenticateData<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Continue(data) => { let encoded = base64.encode(data.declassify()); ctx.write_all(encoded.as_bytes())?; ctx.write_all(b"\r\n") } Self::Cancel => ctx.write_all(b"*\r\n"), } } } impl EncodeIntoContext for AString<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { AString::Atom(atom) => atom.encode_ctx(ctx), AString::String(imap_str) => imap_str.encode_ctx(ctx), } } } impl EncodeIntoContext for Atom<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.inner().as_bytes()) } } impl EncodeIntoContext for AtomExt<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.inner().as_bytes()) } } impl EncodeIntoContext for IString<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Literal(val) => val.encode_ctx(ctx), Self::Quoted(val) => val.encode_ctx(ctx), #[cfg(feature = "ext_utf8")] Self::QuotedUtf8(val) => val.encode_ctx(ctx), } } } impl EncodeIntoContext for Literal<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self.mode() { LiteralMode::Sync => write!(ctx, "{{{}}}\r\n", self.as_ref().len())?, LiteralMode::NonSync => write!(ctx, "{{{}+}}\r\n", self.as_ref().len())?, } ctx.push_line(); ctx.write_all(self.as_ref())?; ctx.push_literal(self.mode()); Ok(()) } } impl EncodeIntoContext for Quoted<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "\"{}\"", escape_quoted(self.inner())) } } impl EncodeIntoContext for Mailbox<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Mailbox::Inbox => ctx.write_all(b"INBOX"), Mailbox::Other(other) => other.encode_ctx(ctx), } } } impl EncodeIntoContext for MailboxOther<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.inner().encode_ctx(ctx) } } impl EncodeIntoContext for ListMailbox<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { ListMailbox::Token(lcs) => lcs.encode_ctx(ctx), ListMailbox::String(istr) => istr.encode_ctx(ctx), } } } impl EncodeIntoContext for ListCharString<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.as_ref()) } } impl EncodeIntoContext for StatusDataItemName { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Messages => ctx.write_all(b"MESSAGES"), Self::Recent => ctx.write_all(b"RECENT"), Self::UidNext => ctx.write_all(b"UIDNEXT"), Self::UidValidity => ctx.write_all(b"UIDVALIDITY"), Self::Unseen => ctx.write_all(b"UNSEEN"), Self::Deleted => ctx.write_all(b"DELETED"), Self::DeletedStorage => ctx.write_all(b"DELETED-STORAGE"), #[cfg(feature = "ext_condstore_qresync")] Self::HighestModSeq => ctx.write_all(b"HIGHESTMODSEQ"), } } } impl EncodeIntoContext for Flag<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } impl EncodeIntoContext for FlagFetch<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Flag(flag) => flag.encode_ctx(ctx), Self::Recent => ctx.write_all(b"\\Recent"), } } } impl EncodeIntoContext for FlagPerm<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Flag(flag) => flag.encode_ctx(ctx), Self::Asterisk => ctx.write_all(b"\\*"), } } } impl EncodeIntoContext for DateTime { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.as_ref().encode_ctx(ctx) } } impl EncodeIntoContext for Charset<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Charset::Atom(atom) => atom.encode_ctx(ctx), Charset::Quoted(quoted) => quoted.encode_ctx(ctx), } } } impl EncodeIntoContext for SearchKey<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { SearchKey::All => ctx.write_all(b"ALL"), SearchKey::Answered => ctx.write_all(b"ANSWERED"), SearchKey::Bcc(astring) => { ctx.write_all(b"BCC ")?; astring.encode_ctx(ctx) } SearchKey::Before(date) => { ctx.write_all(b"BEFORE ")?; date.encode_ctx(ctx) } SearchKey::Body(astring) => { ctx.write_all(b"BODY ")?; astring.encode_ctx(ctx) } SearchKey::Cc(astring) => { ctx.write_all(b"CC ")?; astring.encode_ctx(ctx) } SearchKey::Deleted => ctx.write_all(b"DELETED"), SearchKey::Flagged => ctx.write_all(b"FLAGGED"), SearchKey::From(astring) => { ctx.write_all(b"FROM ")?; astring.encode_ctx(ctx) } SearchKey::Keyword(flag_keyword) => { ctx.write_all(b"KEYWORD ")?; flag_keyword.encode_ctx(ctx) } SearchKey::New => ctx.write_all(b"NEW"), SearchKey::Old => ctx.write_all(b"OLD"), SearchKey::On(date) => { ctx.write_all(b"ON ")?; date.encode_ctx(ctx) } SearchKey::Recent => ctx.write_all(b"RECENT"), SearchKey::Seen => ctx.write_all(b"SEEN"), SearchKey::Since(date) => { ctx.write_all(b"SINCE ")?; date.encode_ctx(ctx) } SearchKey::Subject(astring) => { ctx.write_all(b"SUBJECT ")?; astring.encode_ctx(ctx) } SearchKey::Text(astring) => { ctx.write_all(b"TEXT ")?; astring.encode_ctx(ctx) } SearchKey::To(astring) => { ctx.write_all(b"TO ")?; astring.encode_ctx(ctx) } SearchKey::Unanswered => ctx.write_all(b"UNANSWERED"), SearchKey::Undeleted => ctx.write_all(b"UNDELETED"), SearchKey::Unflagged => ctx.write_all(b"UNFLAGGED"), SearchKey::Unkeyword(flag_keyword) => { ctx.write_all(b"UNKEYWORD ")?; flag_keyword.encode_ctx(ctx) } SearchKey::Unseen => ctx.write_all(b"UNSEEN"), SearchKey::Draft => ctx.write_all(b"DRAFT"), SearchKey::Header(header_fld_name, astring) => { ctx.write_all(b"HEADER ")?; header_fld_name.encode_ctx(ctx)?; ctx.write_all(b" ")?; astring.encode_ctx(ctx) } SearchKey::Larger(number) => write!(ctx, "LARGER {number}"), SearchKey::Not(search_key) => { ctx.write_all(b"NOT ")?; search_key.encode_ctx(ctx) } SearchKey::Or(search_key_a, search_key_b) => { ctx.write_all(b"OR ")?; search_key_a.encode_ctx(ctx)?; ctx.write_all(b" ")?; search_key_b.encode_ctx(ctx) } SearchKey::SentBefore(date) => { ctx.write_all(b"SENTBEFORE ")?; date.encode_ctx(ctx) } SearchKey::SentOn(date) => { ctx.write_all(b"SENTON ")?; date.encode_ctx(ctx) } SearchKey::SentSince(date) => { ctx.write_all(b"SENTSINCE ")?; date.encode_ctx(ctx) } SearchKey::Smaller(number) => write!(ctx, "SMALLER {number}"), SearchKey::Uid(sequence_set) => { ctx.write_all(b"UID ")?; sequence_set.encode_ctx(ctx) } SearchKey::Undraft => ctx.write_all(b"UNDRAFT"), #[cfg(feature = "ext_condstore_qresync")] SearchKey::ModSequence { entry, modseq } => { ctx.write_all(b"MODSEQ")?; if let Some((attribute_flag, entry_type_req)) = entry { write!(ctx, " \"/flags/{attribute_flag}\"")?; write!(ctx, " {entry_type_req}")?; } ctx.write_all(b" ")?; modseq.encode_ctx(ctx) } SearchKey::SequenceSet(sequence_set) => sequence_set.encode_ctx(ctx), SearchKey::And(search_keys) => { ctx.write_all(b"(")?; join_serializable(search_keys.as_ref(), b" ", ctx)?; ctx.write_all(b")") } } } } impl EncodeIntoContext for SequenceSet { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { join_serializable(self.0.as_ref(), b",", ctx) } } impl EncodeIntoContext for Sequence { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Sequence::Single(seq_no) => seq_no.encode_ctx(ctx), Sequence::Range(from, to) => { from.encode_ctx(ctx)?; ctx.write_all(b":")?; to.encode_ctx(ctx) } } } } impl EncodeIntoContext for SeqOrUid { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { SeqOrUid::Value(number) => write!(ctx, "{number}"), SeqOrUid::Asterisk => ctx.write_all(b"*"), } } } impl EncodeIntoContext for NaiveDate { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "\"{}\"", self.as_ref().format("%d-%b-%Y")) } } impl EncodeIntoContext for MacroOrMessageDataItemNames<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Macro(m) => m.encode_ctx(ctx), Self::MessageDataItemNames(item_names) => { if item_names.len() == 1 { item_names[0].encode_ctx(ctx) } else { ctx.write_all(b"(")?; join_serializable(item_names.as_slice(), b" ", ctx)?; ctx.write_all(b")") } } } } } impl EncodeIntoContext for Macro { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } impl EncodeIntoContext for MessageDataItemName<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Body => ctx.write_all(b"BODY"), Self::BodyExt { section, partial, peek, } => { if *peek { ctx.write_all(b"BODY.PEEK[")?; } else { ctx.write_all(b"BODY[")?; } if let Some(section) = section { section.encode_ctx(ctx)?; } ctx.write_all(b"]")?; if let Some((a, b)) = partial { write!(ctx, "<{a}.{b}>")?; } Ok(()) } Self::BodyStructure => ctx.write_all(b"BODYSTRUCTURE"), Self::Envelope => ctx.write_all(b"ENVELOPE"), Self::Flags => ctx.write_all(b"FLAGS"), Self::InternalDate => ctx.write_all(b"INTERNALDATE"), Self::Rfc822 => ctx.write_all(b"RFC822"), Self::Rfc822Header => ctx.write_all(b"RFC822.HEADER"), Self::Rfc822Size => ctx.write_all(b"RFC822.SIZE"), Self::Rfc822Text => ctx.write_all(b"RFC822.TEXT"), Self::Uid => ctx.write_all(b"UID"), MessageDataItemName::Binary { section, partial, peek, } => { ctx.write_all(b"BINARY")?; if *peek { ctx.write_all(b".PEEK")?; } ctx.write_all(b"[")?; join_serializable(section, b".", ctx)?; ctx.write_all(b"]")?; if let Some((a, b)) = partial { ctx.write_all(b"<")?; a.encode_ctx(ctx)?; ctx.write_all(b".")?; b.encode_ctx(ctx)?; ctx.write_all(b">")?; } Ok(()) } MessageDataItemName::BinarySize { section } => { ctx.write_all(b"BINARY.SIZE")?; ctx.write_all(b"[")?; join_serializable(section, b".", ctx)?; ctx.write_all(b"]") } #[cfg(feature = "ext_condstore_qresync")] MessageDataItemName::ModSeq => ctx.write_all(b"MODSEQ"), } } } impl EncodeIntoContext for Section<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Section::Part(part) => part.encode_ctx(ctx), Section::Header(maybe_part) => match maybe_part { Some(part) => { part.encode_ctx(ctx)?; ctx.write_all(b".HEADER") } None => ctx.write_all(b"HEADER"), }, Section::HeaderFields(maybe_part, header_list) => { match maybe_part { Some(part) => { part.encode_ctx(ctx)?; ctx.write_all(b".HEADER.FIELDS (")?; } None => ctx.write_all(b"HEADER.FIELDS (")?, }; join_serializable(header_list.as_ref(), b" ", ctx)?; ctx.write_all(b")") } Section::HeaderFieldsNot(maybe_part, header_list) => { match maybe_part { Some(part) => { part.encode_ctx(ctx)?; ctx.write_all(b".HEADER.FIELDS.NOT (")?; } None => ctx.write_all(b"HEADER.FIELDS.NOT (")?, }; join_serializable(header_list.as_ref(), b" ", ctx)?; ctx.write_all(b")") } Section::Text(maybe_part) => match maybe_part { Some(part) => { part.encode_ctx(ctx)?; ctx.write_all(b".TEXT") } None => ctx.write_all(b"TEXT"), }, Section::Mime(part) => { part.encode_ctx(ctx)?; ctx.write_all(b".MIME") } } } } impl EncodeIntoContext for Part { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { join_serializable(self.0.as_ref(), b".", ctx) } } impl EncodeIntoContext for NonZeroU32 { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } #[cfg(feature = "ext_condstore_qresync")] impl EncodeIntoContext for NonZeroU64 { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } impl EncodeIntoContext for Capability<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } // ----- Responses --------------------------------------------------------------------------------- impl EncodeIntoContext for Response<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Response::Status(status) => status.encode_ctx(ctx), Response::Data(data) => data.encode_ctx(ctx), Response::CommandContinuationRequest(continue_request) => { continue_request.encode_ctx(ctx) } } } } impl EncodeIntoContext for Greeting<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(b"* ")?; self.kind.encode_ctx(ctx)?; ctx.write_all(b" ")?; if let Some(ref code) = self.code { ctx.write_all(b"[")?; code.encode_ctx(ctx)?; ctx.write_all(b"] ")?; } self.text.encode_ctx(ctx)?; ctx.write_all(b"\r\n") } } impl EncodeIntoContext for GreetingKind { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { GreetingKind::Ok => ctx.write_all(b"OK"), GreetingKind::PreAuth => ctx.write_all(b"PREAUTH"), GreetingKind::Bye => ctx.write_all(b"BYE"), } } } impl EncodeIntoContext for Status<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { fn format_status( tag: Option<&Tag>, status: &str, code: &Option, comment: &Text, ctx: &mut EncodeContext, ) -> std::io::Result<()> { match tag { Some(tag) => tag.encode_ctx(ctx)?, None => ctx.write_all(b"*")?, } ctx.write_all(b" ")?; ctx.write_all(status.as_bytes())?; ctx.write_all(b" ")?; if let Some(code) = code { ctx.write_all(b"[")?; code.encode_ctx(ctx)?; ctx.write_all(b"] ")?; } comment.encode_ctx(ctx)?; ctx.write_all(b"\r\n") } match self { Self::Untagged(StatusBody { kind, code, text }) => match kind { StatusKind::Ok => format_status(None, "OK", code, text, ctx), StatusKind::No => format_status(None, "NO", code, text, ctx), StatusKind::Bad => format_status(None, "BAD", code, text, ctx), }, Self::Tagged(Tagged { tag, body: StatusBody { kind, code, text }, }) => match kind { StatusKind::Ok => format_status(Some(tag), "OK", code, text, ctx), StatusKind::No => format_status(Some(tag), "NO", code, text, ctx), StatusKind::Bad => format_status(Some(tag), "BAD", code, text, ctx), }, Self::Bye(Bye { code, text }) => format_status(None, "BYE", code, text, ctx), } } } impl EncodeIntoContext for Code<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Code::Alert => ctx.write_all(b"ALERT"), Code::BadCharset { allowed } => { if allowed.is_empty() { ctx.write_all(b"BADCHARSET") } else { ctx.write_all(b"BADCHARSET (")?; join_serializable(allowed, b" ", ctx)?; ctx.write_all(b")") } } Code::Capability(caps) => { ctx.write_all(b"CAPABILITY ")?; join_serializable(caps.as_ref(), b" ", ctx) } Code::Parse => ctx.write_all(b"PARSE"), Code::PermanentFlags(flags) => { ctx.write_all(b"PERMANENTFLAGS (")?; join_serializable(flags, b" ", ctx)?; ctx.write_all(b")") } Code::ReadOnly => ctx.write_all(b"READ-ONLY"), Code::ReadWrite => ctx.write_all(b"READ-WRITE"), Code::TryCreate => ctx.write_all(b"TRYCREATE"), Code::UidNext(next) => { ctx.write_all(b"UIDNEXT ")?; next.encode_ctx(ctx) } Code::UidValidity(validity) => { ctx.write_all(b"UIDVALIDITY ")?; validity.encode_ctx(ctx) } Code::Unseen(seq) => { ctx.write_all(b"UNSEEN ")?; seq.encode_ctx(ctx) } // RFC 2221 #[cfg(any(feature = "ext_login_referrals", feature = "ext_mailbox_referrals"))] Code::Referral(url) => { ctx.write_all(b"REFERRAL ")?; ctx.write_all(url.as_bytes()) } // RFC 4551 #[cfg(feature = "ext_condstore_qresync")] Code::HighestModSeq(modseq) => { ctx.write_all(b"HIGHESTMODSEQ ")?; modseq.encode_ctx(ctx) } #[cfg(feature = "ext_condstore_qresync")] Code::NoModSeq => ctx.write_all(b"NOMODSEQ"), #[cfg(feature = "ext_condstore_qresync")] Code::Modified(sequence_set) => { ctx.write_all(b"MODIFIED ")?; sequence_set.encode_ctx(ctx) } #[cfg(feature = "ext_condstore_qresync")] Code::Closed => ctx.write_all(b"CLOSED"), Code::CompressionActive => ctx.write_all(b"COMPRESSIONACTIVE"), Code::OverQuota => ctx.write_all(b"OVERQUOTA"), Code::TooBig => ctx.write_all(b"TOOBIG"), #[cfg(feature = "ext_metadata")] Code::Metadata(code) => { ctx.write_all(b"METADATA ")?; code.encode_ctx(ctx) } Code::UnknownCte => ctx.write_all(b"UNKNOWN-CTE"), Code::AppendUid { uid_validity, uid } => { ctx.write_all(b"APPENDUID ")?; uid_validity.encode_ctx(ctx)?; ctx.write_all(b" ")?; uid.encode_ctx(ctx) } Code::CopyUid { uid_validity, source, destination, } => { ctx.write_all(b"COPYUID ")?; uid_validity.encode_ctx(ctx)?; ctx.write_all(b" ")?; source.encode_ctx(ctx)?; ctx.write_all(b" ")?; destination.encode_ctx(ctx) } Code::UidNotSticky => ctx.write_all(b"UIDNOTSTICKY"), Code::Other(unknown) => unknown.encode_ctx(ctx), } } } impl EncodeIntoContext for CodeOther<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.inner()) } } impl EncodeIntoContext for Text<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.inner().as_bytes()) } } impl EncodeIntoContext for Data<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Data::Capability(caps) => { ctx.write_all(b"* CAPABILITY ")?; join_serializable(caps.as_ref(), b" ", ctx)?; } Data::List { items, delimiter, mailbox, } => { ctx.write_all(b"* LIST (")?; join_serializable(items, b" ", ctx)?; ctx.write_all(b") ")?; if let Some(delimiter) = delimiter { ctx.write_all(b"\"")?; delimiter.encode_ctx(ctx)?; ctx.write_all(b"\"")?; } else { ctx.write_all(b"NIL")?; } ctx.write_all(b" ")?; mailbox.encode_ctx(ctx)?; } Data::Lsub { items, delimiter, mailbox, } => { ctx.write_all(b"* LSUB (")?; join_serializable(items, b" ", ctx)?; ctx.write_all(b") ")?; if let Some(delimiter) = delimiter { ctx.write_all(b"\"")?; delimiter.encode_ctx(ctx)?; ctx.write_all(b"\"")?; } else { ctx.write_all(b"NIL")?; } ctx.write_all(b" ")?; mailbox.encode_ctx(ctx)?; } Data::Status { mailbox, items } => { ctx.write_all(b"* STATUS ")?; mailbox.encode_ctx(ctx)?; ctx.write_all(b" (")?; join_serializable(items, b" ", ctx)?; ctx.write_all(b")")?; } // TODO: Exclude pattern via cfg? #[cfg(not(feature = "ext_condstore_qresync"))] Data::Search(seqs) => { if seqs.is_empty() { ctx.write_all(b"* SEARCH")?; } else { ctx.write_all(b"* SEARCH ")?; join_serializable(seqs, b" ", ctx)?; } } // TODO: Exclude pattern via cfg? #[cfg(feature = "ext_condstore_qresync")] Data::Search(seqs, modseq) => { if seqs.is_empty() { ctx.write_all(b"* SEARCH")?; } else { ctx.write_all(b"* SEARCH ")?; join_serializable(seqs, b" ", ctx)?; } if let Some(modseq) = modseq { ctx.write_all(b" (MODSEQ ")?; modseq.encode_ctx(ctx)?; ctx.write_all(b")")?; } } // TODO: Exclude pattern via cfg? #[cfg(not(feature = "ext_condstore_qresync"))] Data::Sort(seqs) => { if seqs.is_empty() { ctx.write_all(b"* SORT")?; } else { ctx.write_all(b"* SORT ")?; join_serializable(seqs, b" ", ctx)?; } } // TODO: Exclude pattern via cfg? #[cfg(feature = "ext_condstore_qresync")] Data::Sort(seqs, modseq) => { if seqs.is_empty() { ctx.write_all(b"* SORT")?; } else { ctx.write_all(b"* SORT ")?; join_serializable(seqs, b" ", ctx)?; } if let Some(modseq) = modseq { ctx.write_all(b" (MODSEQ ")?; modseq.encode_ctx(ctx)?; ctx.write_all(b")")?; } } Data::Thread(threads) => { if threads.is_empty() { ctx.write_all(b"* THREAD")?; } else { ctx.write_all(b"* THREAD ")?; for thread in threads { thread.encode_ctx(ctx)?; } } } Data::Flags(flags) => { ctx.write_all(b"* FLAGS (")?; join_serializable(flags, b" ", ctx)?; ctx.write_all(b")")?; } Data::Exists(count) => write!(ctx, "* {count} EXISTS")?, Data::Recent(count) => write!(ctx, "* {count} RECENT")?, Data::Expunge(msg) => write!(ctx, "* {msg} EXPUNGE")?, Data::Fetch { seq, items } => { write!(ctx, "* {seq} FETCH (")?; join_serializable(items.as_ref(), b" ", ctx)?; ctx.write_all(b")")?; } Data::Enabled { capabilities } => { write!(ctx, "* ENABLED")?; for cap in capabilities { ctx.write_all(b" ")?; cap.encode_ctx(ctx)?; } } Data::Quota { root, quotas } => { ctx.write_all(b"* QUOTA ")?; root.encode_ctx(ctx)?; ctx.write_all(b" (")?; join_serializable(quotas.as_ref(), b" ", ctx)?; ctx.write_all(b")")?; } Data::QuotaRoot { mailbox, roots } => { ctx.write_all(b"* QUOTAROOT ")?; mailbox.encode_ctx(ctx)?; for root in roots { ctx.write_all(b" ")?; root.encode_ctx(ctx)?; } } #[cfg(feature = "ext_id")] Data::Id { parameters } => { ctx.write_all(b"* ID ")?; match parameters { Some(parameters) => { if let Some((first, tail)) = parameters.split_first() { ctx.write_all(b"(")?; first.0.encode_ctx(ctx)?; ctx.write_all(b" ")?; first.1.encode_ctx(ctx)?; for parameter in tail { ctx.write_all(b" ")?; parameter.0.encode_ctx(ctx)?; ctx.write_all(b" ")?; parameter.1.encode_ctx(ctx)?; } ctx.write_all(b")")?; } else { #[cfg(not(feature = "quirk_id_empty_to_nil"))] { ctx.write_all(b"()")?; } #[cfg(feature = "quirk_id_empty_to_nil")] { ctx.write_all(b"NIL")?; } } } None => { ctx.write_all(b"NIL")?; } } } #[cfg(feature = "ext_metadata")] Data::Metadata { mailbox, items } => { ctx.write_all(b"* METADATA ")?; mailbox.encode_ctx(ctx)?; ctx.write_all(b" ")?; items.encode_ctx(ctx)?; } #[cfg(feature = "ext_condstore_qresync")] Data::Vanished { earlier, known_uids, } => { ctx.write_all(b"* VANISHED")?; if *earlier { ctx.write_all(b" (EARLIER)")?; } ctx.write_all(b" ")?; known_uids.encode_ctx(ctx)?; } #[cfg(feature = "ext_namespace")] Data::Namespace { personal, other, shared, } => { ctx.write_all(b"* NAMESPACE ")?; encode_namespaces(ctx, personal)?; ctx.write_all(b" ")?; encode_namespaces(ctx, other)?; ctx.write_all(b" ")?; encode_namespaces(ctx, shared)?; } } ctx.write_all(b"\r\n") } } impl EncodeIntoContext for FlagNameAttribute<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } impl EncodeIntoContext for QuotedChar { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self.inner() { '\\' => ctx.write_all(b"\\\\"), '"' => ctx.write_all(b"\\\""), other => ctx.write_all(&[other as u8]), } } } impl EncodeIntoContext for StatusDataItem { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Messages(count) => { ctx.write_all(b"MESSAGES ")?; count.encode_ctx(ctx) } Self::Recent(count) => { ctx.write_all(b"RECENT ")?; count.encode_ctx(ctx) } Self::UidNext(next) => { ctx.write_all(b"UIDNEXT ")?; next.encode_ctx(ctx) } Self::UidValidity(identifier) => { ctx.write_all(b"UIDVALIDITY ")?; identifier.encode_ctx(ctx) } Self::Unseen(count) => { ctx.write_all(b"UNSEEN ")?; count.encode_ctx(ctx) } Self::Deleted(count) => { ctx.write_all(b"DELETED ")?; count.encode_ctx(ctx) } Self::DeletedStorage(count) => { ctx.write_all(b"DELETED-STORAGE ")?; count.encode_ctx(ctx) } #[cfg(feature = "ext_condstore_qresync")] Self::HighestModSeq(value) => { ctx.write_all(b"HIGHESTMODSEQ ")?; value.encode_ctx(ctx) } } } } impl EncodeIntoContext for MessageDataItem<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::BodyExt { section, origin, data, } => { ctx.write_all(b"BODY[")?; if let Some(section) = section { section.encode_ctx(ctx)?; } ctx.write_all(b"]")?; if let Some(origin) = origin { write!(ctx, "<{origin}>")?; } ctx.write_all(b" ")?; data.encode_ctx(ctx) } // FIXME: do not return body-ext-1part and body-ext-mpart here Self::Body(body) => { ctx.write_all(b"BODY ")?; body.encode_ctx(ctx) } Self::BodyStructure(body) => { ctx.write_all(b"BODYSTRUCTURE ")?; body.encode_ctx(ctx) } Self::Envelope(envelope) => { ctx.write_all(b"ENVELOPE ")?; envelope.encode_ctx(ctx) } Self::Flags(flags) => { ctx.write_all(b"FLAGS (")?; join_serializable(flags, b" ", ctx)?; ctx.write_all(b")") } Self::InternalDate(datetime) => { ctx.write_all(b"INTERNALDATE ")?; datetime.encode_ctx(ctx) } Self::Rfc822(nstring) => { ctx.write_all(b"RFC822 ")?; nstring.encode_ctx(ctx) } Self::Rfc822Header(nstring) => { ctx.write_all(b"RFC822.HEADER ")?; nstring.encode_ctx(ctx) } Self::Rfc822Size(size) => write!(ctx, "RFC822.SIZE {size}"), Self::Rfc822Text(nstring) => { ctx.write_all(b"RFC822.TEXT ")?; nstring.encode_ctx(ctx) } Self::Uid(uid) => write!(ctx, "UID {uid}"), Self::Binary { section, value } => { ctx.write_all(b"BINARY[")?; join_serializable(section, b".", ctx)?; ctx.write_all(b"] ")?; value.encode_ctx(ctx) } Self::BinarySize { section, size } => { ctx.write_all(b"BINARY.SIZE[")?; join_serializable(section, b".", ctx)?; ctx.write_all(b"] ")?; size.encode_ctx(ctx) } #[cfg(feature = "ext_condstore_qresync")] Self::ModSeq(value) => write!(ctx, "MODSEQ {value}"), } } } impl EncodeIntoContext for NString<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match &self.0 { Some(imap_str) => imap_str.encode_ctx(ctx), None => ctx.write_all(b"NIL"), } } } impl EncodeIntoContext for NString8<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { NString8::NString(nstring) => nstring.encode_ctx(ctx), NString8::Literal8(literal8) => literal8.encode_ctx(ctx), } } } impl EncodeIntoContext for BodyStructure<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(b"(")?; match self { BodyStructure::Single { body, extension_data: extension, } => { body.encode_ctx(ctx)?; if let Some(extension) = extension { ctx.write_all(b" ")?; extension.encode_ctx(ctx)?; } } BodyStructure::Multi { bodies, subtype, extension_data, } => { for body in bodies.as_ref() { body.encode_ctx(ctx)?; } ctx.write_all(b" ")?; subtype.encode_ctx(ctx)?; if let Some(extension) = extension_data { ctx.write_all(b" ")?; extension.encode_ctx(ctx)?; } } } ctx.write_all(b")") } } impl EncodeIntoContext for Body<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self.specific { SpecificFields::Basic { r#type: ref type_, ref subtype, } => { type_.encode_ctx(ctx)?; ctx.write_all(b" ")?; subtype.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.basic.encode_ctx(ctx) } SpecificFields::Message { ref envelope, ref body_structure, number_of_lines, } => { ctx.write_all(b"\"MESSAGE\" \"RFC822\" ")?; self.basic.encode_ctx(ctx)?; ctx.write_all(b" ")?; envelope.encode_ctx(ctx)?; ctx.write_all(b" ")?; body_structure.encode_ctx(ctx)?; ctx.write_all(b" ")?; write!(ctx, "{number_of_lines}") } SpecificFields::Text { ref subtype, number_of_lines, } => { ctx.write_all(b"\"TEXT\" ")?; subtype.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.basic.encode_ctx(ctx)?; ctx.write_all(b" ")?; write!(ctx, "{number_of_lines}") } } } } impl EncodeIntoContext for BasicFields<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { List1AttributeValueOrNil(&self.parameter_list).encode_ctx(ctx)?; ctx.write_all(b" ")?; self.id.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.description.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.content_transfer_encoding.encode_ctx(ctx)?; ctx.write_all(b" ")?; write!(ctx, "{}", self.size) } } impl EncodeIntoContext for Envelope<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(b"(")?; self.date.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.subject.encode_ctx(ctx)?; ctx.write_all(b" ")?; List1OrNil(&self.from, b"").encode_ctx(ctx)?; ctx.write_all(b" ")?; List1OrNil(&self.sender, b"").encode_ctx(ctx)?; ctx.write_all(b" ")?; List1OrNil(&self.reply_to, b"").encode_ctx(ctx)?; ctx.write_all(b" ")?; List1OrNil(&self.to, b"").encode_ctx(ctx)?; ctx.write_all(b" ")?; List1OrNil(&self.cc, b"").encode_ctx(ctx)?; ctx.write_all(b" ")?; List1OrNil(&self.bcc, b"").encode_ctx(ctx)?; ctx.write_all(b" ")?; self.in_reply_to.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.message_id.encode_ctx(ctx)?; ctx.write_all(b")") } } impl EncodeIntoContext for Address<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(b"(")?; self.name.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.adl.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.mailbox.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.host.encode_ctx(ctx)?; ctx.write_all(b")")?; Ok(()) } } impl EncodeIntoContext for SinglePartExtensionData<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.md5.encode_ctx(ctx)?; if let Some(disposition) = &self.tail { ctx.write_all(b" ")?; disposition.encode_ctx(ctx)?; } Ok(()) } } impl EncodeIntoContext for MultiPartExtensionData<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { List1AttributeValueOrNil(&self.parameter_list).encode_ctx(ctx)?; if let Some(disposition) = &self.tail { ctx.write_all(b" ")?; disposition.encode_ctx(ctx)?; } Ok(()) } } impl EncodeIntoContext for Disposition<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match &self.disposition { Some((s, param)) => { ctx.write_all(b"(")?; s.encode_ctx(ctx)?; ctx.write_all(b" ")?; List1AttributeValueOrNil(param).encode_ctx(ctx)?; ctx.write_all(b")")?; } None => ctx.write_all(b"NIL")?, } if let Some(language) = &self.tail { ctx.write_all(b" ")?; language.encode_ctx(ctx)?; } Ok(()) } } impl EncodeIntoContext for Language<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { List1OrNil(&self.language, b" ").encode_ctx(ctx)?; if let Some(location) = &self.tail { ctx.write_all(b" ")?; location.encode_ctx(ctx)?; } Ok(()) } } impl EncodeIntoContext for Location<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.location.encode_ctx(ctx)?; for body_extension in &self.extensions { ctx.write_all(b" ")?; body_extension.encode_ctx(ctx)?; } Ok(()) } } impl EncodeIntoContext for BodyExtension<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { BodyExtension::NString(nstring) => nstring.encode_ctx(ctx), BodyExtension::Number(number) => number.encode_ctx(ctx), BodyExtension::List(list) => { ctx.write_all(b"(")?; join_serializable(list.as_ref(), b" ", ctx)?; ctx.write_all(b")") } } } } impl EncodeIntoContext for ChronoDateTime { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "\"{}\"", self.format("%d-%b-%Y %H:%M:%S %z")) } } impl EncodeIntoContext for CommandContinuationRequest<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { Self::Basic(continue_basic) => match continue_basic.code() { Some(code) => { ctx.write_all(b"+ [")?; code.encode_ctx(ctx)?; ctx.write_all(b"] ")?; continue_basic.text().encode_ctx(ctx)?; ctx.write_all(b"\r\n") } None => { ctx.write_all(b"+ ")?; continue_basic.text().encode_ctx(ctx)?; ctx.write_all(b"\r\n") } }, Self::Base64(data) => { ctx.write_all(b"+ ")?; ctx.write_all(base64.encode(data).as_bytes())?; ctx.write_all(b"\r\n") } } } } pub(crate) mod utils { use std::io::Write; use super::{EncodeContext, EncodeIntoContext}; pub struct List1OrNil<'a, T>(pub &'a Vec, pub &'a [u8]); pub struct List1AttributeValueOrNil<'a, T>(pub &'a Vec<(T, T)>); pub(crate) fn join_serializable( elements: &[I], sep: &[u8], ctx: &mut EncodeContext, ) -> std::io::Result<()> { if let Some((last, head)) = elements.split_last() { for item in head { item.encode_ctx(ctx)?; ctx.write_all(sep)?; } last.encode_ctx(ctx) } else { Ok(()) } } impl EncodeIntoContext for List1OrNil<'_, T> where T: EncodeIntoContext, { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { if let Some((last, head)) = self.0.split_last() { ctx.write_all(b"(")?; for item in head { item.encode_ctx(ctx)?; ctx.write_all(self.1)?; } last.encode_ctx(ctx)?; ctx.write_all(b")") } else { ctx.write_all(b"NIL") } } } impl EncodeIntoContext for List1AttributeValueOrNil<'_, T> where T: EncodeIntoContext, { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { if let Some((last, head)) = self.0.split_last() { ctx.write_all(b"(")?; for (attribute, value) in head { attribute.encode_ctx(ctx)?; ctx.write_all(b" ")?; value.encode_ctx(ctx)?; ctx.write_all(b" ")?; } let (attribute, value) = last; attribute.encode_ctx(ctx)?; ctx.write_all(b" ")?; value.encode_ctx(ctx)?; ctx.write_all(b")") } else { ctx.write_all(b"NIL") } } } } #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::{ auth::AuthMechanism, command::{Command, CommandBody}, core::{AString, Literal, NString, Vec1}, fetch::MessageDataItem, response::{Data, Response}, utils::escape_byte_string, }; use super::*; #[test] fn test_api_encoder_usage() { let cmd = Command::new( "A", CommandBody::login( AString::from(Literal::unvalidated_non_sync(b"alice".as_ref())), "password", ) .unwrap(), ) .unwrap(); // Dump. let got_encoded = CommandCodec::default().encode(&cmd).dump(); // Encoded. let encoded = CommandCodec::default().encode(&cmd); let mut out = Vec::new(); for x in encoded { match x { Fragment::Line { data } => { println!("C: {}", escape_byte_string(&data)); out.extend_from_slice(&data); } Fragment::Literal { data, mode } => { match mode { LiteralMode::Sync => println!("C: "), LiteralMode::NonSync => println!("C: "), } println!("C: {}", escape_byte_string(&data)); out.extend_from_slice(&data); } } } assert_eq!(got_encoded, out); } #[test] fn test_encode_command() { kat_encoder::, &[Fragment]>(&[ ( Command::new("A", CommandBody::login("alice", "pass").unwrap()).unwrap(), [Fragment::Line { data: b"A LOGIN alice pass\r\n".to_vec(), }] .as_ref(), ), ( Command::new( "A", CommandBody::login("alice", b"\xCA\xFE".as_ref()).unwrap(), ) .unwrap(), [ Fragment::Line { data: b"A LOGIN alice {2}\r\n".to_vec(), }, Fragment::Literal { data: b"\xCA\xFE".to_vec(), mode: LiteralMode::Sync, }, Fragment::Line { data: b"\r\n".to_vec(), }, ] .as_ref(), ), ( Command::new("A", CommandBody::authenticate(AuthMechanism::Login)).unwrap(), [Fragment::Line { data: b"A AUTHENTICATE LOGIN\r\n".to_vec(), }] .as_ref(), ), ( Command::new( "A", CommandBody::authenticate_with_ir(AuthMechanism::Login, b"alice".as_ref()), ) .unwrap(), [Fragment::Line { data: b"A AUTHENTICATE LOGIN YWxpY2U=\r\n".to_vec(), }] .as_ref(), ), ( Command::new("A", CommandBody::authenticate(AuthMechanism::Plain)).unwrap(), [Fragment::Line { data: b"A AUTHENTICATE PLAIN\r\n".to_vec(), }] .as_ref(), ), ( Command::new( "A", CommandBody::authenticate_with_ir( AuthMechanism::Plain, b"\x00alice\x00pass".as_ref(), ), ) .unwrap(), [Fragment::Line { data: b"A AUTHENTICATE PLAIN AGFsaWNlAHBhc3M=\r\n".to_vec(), }] .as_ref(), ), ]); } #[test] fn test_encode_response() { kat_encoder::, &[Fragment]>(&[ ( Response::Data(Data::Fetch { seq: NonZeroU32::new(12345).unwrap(), items: Vec1::from(MessageDataItem::BodyExt { section: None, origin: None, data: NString::from(Literal::unvalidated(b"ABCDE".as_ref())), }), }), [ Fragment::Line { data: b"* 12345 FETCH (BODY[] {5}\r\n".to_vec(), }, Fragment::Literal { data: b"ABCDE".to_vec(), mode: LiteralMode::Sync, }, Fragment::Line { data: b")\r\n".to_vec(), }, ] .as_ref(), ), ( Response::Data(Data::Fetch { seq: NonZeroU32::new(12345).unwrap(), items: Vec1::from(MessageDataItem::BodyExt { section: None, origin: None, data: NString::from(Literal::unvalidated_non_sync(b"ABCDE".as_ref())), }), }), [ Fragment::Line { data: b"* 12345 FETCH (BODY[] {5+}\r\n".to_vec(), }, Fragment::Literal { data: b"ABCDE".to_vec(), mode: LiteralMode::NonSync, }, Fragment::Line { data: b")\r\n".to_vec(), }, ] .as_ref(), ), ]) } fn kat_encoder<'a, E, M, F>(tests: &'a [(M, F)]) where E: Encoder = M> + Default, F: AsRef<[Fragment]>, { for (i, (obj, actions)) in tests.iter().enumerate() { println!("# Testing {i}"); let encoder = E::default().encode(obj); let actions = actions.as_ref(); assert_eq!(encoder.collect::>(), actions); } } } duesee-imap-codec-0d00966/imap-codec/src/command.rs000066400000000000000000000656601507724125200220330ustar00rootroot00000000000000use std::borrow::Cow; #[cfg(not(feature = "quirk_crlf_relaxed"))] use abnf_core::streaming::crlf; #[cfg(feature = "quirk_crlf_relaxed")] use abnf_core::streaming::crlf_relaxed as crlf; use abnf_core::streaming::sp; #[cfg(feature = "ext_condstore_qresync")] use imap_types::command::{FetchModifier, SelectParameter, StoreModifier}; use imap_types::{ auth::AuthMechanism, command::{Command, CommandBody}, core::AString, extensions::binary::LiteralOrLiteral8, fetch::{Macro, MacroOrMessageDataItemNames}, flag::{Flag, StoreResponse, StoreType}, secret::Secret, }; #[cfg(feature = "ext_condstore_qresync")] use nom::character::streaming::char; #[cfg(feature = "ext_condstore_qresync")] use nom::sequence::separated_pair; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, opt, value}, multi::{separated_list0, separated_list1}, sequence::{delimited, preceded, terminated, tuple}, }; #[cfg(feature = "ext_condstore_qresync")] use crate::core::nz_number; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::mod_sequence_value; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::mod_sequence_valzer; #[cfg(feature = "ext_id")] use crate::extensions::id::id; #[cfg(feature = "ext_metadata")] use crate::extensions::metadata::{getmetadata, setmetadata}; #[cfg(feature = "ext_namespace")] use crate::extensions::namespace::namespace_command; use crate::{ auth::auth_type, core::{astring, base64, literal, tag_imap}, datetime::date_time, decode::{IMAPErrorKind, IMAPResult}, extensions::{ binary::literal8, compress::compress, enable::enable, idle::idle, r#move::r#move, quota::{getquota, getquotaroot, setquota}, sort::sort, thread::thread, uidplus::uid_expunge, }, fetch::fetch_att, flag::{flag, flag_list}, mailbox::{list_mailbox, mailbox}, search::search, sequence::sequence_set, status::status_att, }; /// `command = tag SP ( /// command-any / /// command-auth / /// command-nonauth / /// command-select /// ) CRLF` pub(crate) fn command(input: &[u8]) -> IMAPResult<&[u8], Command> { let mut parser_tag = terminated(tag_imap, sp); let mut parser_body = terminated( alt((command_any, command_auth, command_nonauth, command_select)), crlf, ); let (remaining, obtained_tag) = parser_tag(input)?; match parser_body(remaining) { Ok((remaining, body)) => Ok(( remaining, Command { tag: obtained_tag, body, }, )), Err(mut error) => { // If we got an `IMAPErrorKind::Literal`, we fill in the missing `tag`. if let nom::Err::Error(ref mut err) | nom::Err::Failure(ref mut err) = error { if let IMAPErrorKind::Literal { ref mut tag, .. } = err.kind { *tag = Some(obtained_tag); } } Err(error) } } } // # Command Any /// ```abnf /// command-any = "CAPABILITY" / /// "LOGOUT" / /// "NOOP" / /// x-command / /// id ; adds id command to command_any (See RFC 2971) /// ``` /// /// Note: Valid in all states pub(crate) fn command_any(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { alt(( value(CommandBody::Capability, tag_no_case(b"CAPABILITY")), value(CommandBody::Logout, tag_no_case(b"LOGOUT")), value(CommandBody::Noop, tag_no_case(b"NOOP")), // x-command = "X" atom #[cfg(feature = "ext_id")] map(id, |parameters| CommandBody::Id { parameters }), ))(input) } // # Command Auth /// ```abnf /// command-auth = append / /// create / /// delete / /// examine / /// list / /// lsub / /// rename / /// select / /// status / /// subscribe / /// unsubscribe / /// idle / ; RFC 2177 /// enable / ; RFC 5161 /// compress / ; RFC 4978 /// getquota / ; RFC 9208 /// getquotaroot / ; RFC 9208 /// setquota / ; RFC 9208 /// setmetadata / ; RFC 5464 /// getmetadata ; RFC 5464 /// ``` /// /// Note: Valid only in Authenticated or Selected state pub(crate) fn command_auth(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { alt(( append, create, delete, examine, list, lsub, rename, select, status, subscribe, unsubscribe, idle, enable, compress, getquota, getquotaroot, setquota, #[cfg(feature = "ext_metadata")] setmetadata, #[cfg(feature = "ext_metadata")] getmetadata, #[cfg(feature = "ext_namespace")] namespace_command, ))(input) } /// `append = "APPEND" SP mailbox [SP flag-list] [SP date-time] SP literal` pub(crate) fn append(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case(b"APPEND "), mailbox, opt(preceded(sp, flag_list)), opt(preceded(sp, date_time)), sp, alt(( map(literal, LiteralOrLiteral8::Literal), map(literal8, LiteralOrLiteral8::Literal8), )), )); let (remaining, (_, mailbox, flags, date, _, message)) = parser(input)?; Ok(( remaining, CommandBody::Append { mailbox, flags: flags.unwrap_or_default(), date, message, }, )) } /// `create = "CREATE" SP mailbox` /// /// Note: Use of INBOX gives a NO error pub(crate) fn create(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = preceded(tag_no_case(b"CREATE "), mailbox); let (remaining, mailbox) = parser(input)?; Ok((remaining, CommandBody::Create { mailbox })) } /// `delete = "DELETE" SP mailbox` /// /// Note: Use of INBOX gives a NO error pub(crate) fn delete(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = preceded(tag_no_case(b"DELETE "), mailbox); let (remaining, mailbox) = parser(input)?; Ok((remaining, CommandBody::Delete { mailbox })) } /// ```abnf /// examine = "EXAMINE" SP mailbox [select-params] /// ^^^^^^^^^^^^^^^ /// | /// RFC 4466: modifies the original IMAP EXAMINE command to accept optional parameters /// ``` pub(crate) fn examine(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = preceded(tag_no_case(b"EXAMINE "), mailbox); let (remaining, mailbox) = parser(input)?; #[cfg(feature = "ext_condstore_qresync")] let (remaining, parameters) = map(opt(select_params), |params| params.unwrap_or_default())(remaining)?; Ok(( remaining, CommandBody::Examine { mailbox, #[cfg(feature = "ext_condstore_qresync")] parameters, }, )) } /// `list = "LIST" SP mailbox SP list-mailbox` pub(crate) fn list(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case(b"LIST "), mailbox, sp, list_mailbox)); let (remaining, (_, reference, _, mailbox_wildcard)) = parser(input)?; Ok(( remaining, CommandBody::List { reference, mailbox_wildcard, }, )) } /// `lsub = "LSUB" SP mailbox SP list-mailbox` pub(crate) fn lsub(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case(b"LSUB "), mailbox, sp, list_mailbox)); let (remaining, (_, reference, _, mailbox_wildcard)) = parser(input)?; Ok(( remaining, CommandBody::Lsub { reference, mailbox_wildcard, }, )) } /// `rename = "RENAME" SP mailbox SP mailbox` /// /// Note: Use of INBOX as a destination gives a NO error pub(crate) fn rename(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case(b"RENAME "), mailbox, sp, mailbox)); let (remaining, (_, mailbox, _, new_mailbox)) = parser(input)?; Ok(( remaining, CommandBody::Rename { from: mailbox, to: new_mailbox, }, )) } /// ```abnf /// select = "SELECT" SP mailbox [select-params] /// ^^^^^^^^^^^^^^^ /// | /// RFC 4466: modifies the original IMAP SELECT command to accept optional parameters /// ``` pub(crate) fn select(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = preceded(tag_no_case(b"SELECT "), mailbox); let (remaining, mailbox) = parser(input)?; #[cfg(feature = "ext_condstore_qresync")] let (remaining, parameters) = map(opt(select_params), |params| params.unwrap_or_default())(remaining)?; Ok(( remaining, CommandBody::Select { mailbox, #[cfg(feature = "ext_condstore_qresync")] parameters, }, )) } /// FROM RFC4466: /// /// ```abnf /// select-params = SP "(" select-param *(SP select-param) ")" /// ``` #[cfg(feature = "ext_condstore_qresync")] pub(crate) fn select_params(input: &[u8]) -> IMAPResult<&[u8], Vec> { delimited(tag(" ("), separated_list1(sp, select_param), tag(")"))(input) } /// FROM RFC4466: /// /// ```abnf /// select-param = select-param-name [SP select-param-value] /// ;; a parameter to SELECT may contain one or more atoms and/or strings and/or lists. /// /// select-param-name = tagged-ext-label /// /// select-param-value = tagged-ext-val /// ;; This non-terminal shows recommended syntax for future extensions. /// ``` /// /// FROM RFC 7162 (CONDSTORE/QRESYNC): /// /// ```abnf /// select-param =/ condstore-param /// ;; Conforms to the generic "select-param" non-terminal syntax defined in [RFC4466]. /// /// condstore-param = "CONDSTORE" /// /// select-param =/ "QRESYNC" SP "(" /// uidvalidity SP /// mod-sequence-value [SP known-uids] /// [SP seq-match-data] /// ")" /// ;; Conforms to the generic select-param syntax defined in [RFC4466]. /// /// uidvalidity = nz-number /// /// known-uids = sequence-set /// ;; Sequence of UIDs; "*" is not allowed. /// /// seq-match-data = "(" known-sequence-set SP known-uid-set ")" /// /// known-sequence-set = sequence-set /// ;; Set of message numbers corresponding to /// ;; the UIDs in known-uid-set, in ascending order. /// ;; * is not allowed. /// /// known-uid-set = sequence-set /// ;; Set of UIDs corresponding to the messages in /// ;; known-sequence-set, in ascending order. /// ;; * is not allowed. /// ``` #[cfg(feature = "ext_condstore_qresync")] pub(crate) fn select_param(input: &[u8]) -> IMAPResult<&[u8], SelectParameter> { alt(( value(SelectParameter::CondStore, tag_no_case("CONDSTORE")), map( delimited( tag_no_case("QRESYNC ("), tuple(( terminated(nz_number, sp), mod_sequence_value, opt(preceded(sp, sequence_set)), opt(preceded( sp, delimited( char('('), separated_pair(sequence_set, sp, sequence_set), char(')'), ), )), )), char(')'), ), |(uid_validity, mod_sequence_value, known_uids, seq_match_data)| { SelectParameter::QResync { uid_validity, mod_sequence_value, known_uids, seq_match_data, } }, ), ))(input) } /// `status = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")"` pub(crate) fn status(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case(b"STATUS "), mailbox, delimited(tag(b" ("), separated_list0(sp, status_att), tag(b")")), )); let (remaining, (_, mailbox, item_names)) = parser(input)?; Ok(( remaining, CommandBody::Status { mailbox, item_names: item_names.into(), }, )) } /// `subscribe = "SUBSCRIBE" SP mailbox` pub(crate) fn subscribe(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = preceded(tag_no_case(b"SUBSCRIBE "), mailbox); let (remaining, mailbox) = parser(input)?; Ok((remaining, CommandBody::Subscribe { mailbox })) } /// `unsubscribe = "UNSUBSCRIBE" SP mailbox` pub(crate) fn unsubscribe(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = preceded(tag_no_case(b"UNSUBSCRIBE "), mailbox); let (remaining, mailbox) = parser(input)?; Ok((remaining, CommandBody::Unsubscribe { mailbox })) } // # Command NonAuth /// `command-nonauth = login / authenticate / "STARTTLS"` /// /// Note: Valid only when in Not Authenticated state pub(crate) fn command_nonauth(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = alt(( login, map(authenticate, |(mechanism, initial_response)| { CommandBody::Authenticate { mechanism, initial_response, } }), #[cfg(feature = "starttls")] value(CommandBody::StartTLS, tag_no_case(b"STARTTLS")), )); let (remaining, parsed_command_nonauth) = parser(input)?; Ok((remaining, parsed_command_nonauth)) } /// `login = "LOGIN" SP userid SP password` pub(crate) fn login(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case(b"LOGIN"), sp, userid, sp, password)); let (remaining, (_, _, username, _, password)) = parser(input)?; Ok(( remaining, CommandBody::Login { username, password: Secret::new(password), }, )) } #[inline] /// `userid = astring` pub(crate) fn userid(input: &[u8]) -> IMAPResult<&[u8], AString> { astring(input) } #[inline] /// `password = astring` pub(crate) fn password(input: &[u8]) -> IMAPResult<&[u8], AString> { astring(input) } /// `authenticate = "AUTHENTICATE" SP auth-type *(CRLF base64)` (edited) /// /// ```text /// Added by SASL-IR /// | /// vvvvvvvvvvvvvvvvvvv /// authenticate = "AUTHENTICATE" SP auth-type [SP (base64 / "=")] *(CRLF base64) /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// | /// This is parsed here. /// CRLF is parsed by upper command parser. /// ``` #[allow(clippy::type_complexity)] pub(crate) fn authenticate( input: &[u8], ) -> IMAPResult<&[u8], (AuthMechanism, Option>>)> { let mut parser = tuple(( tag_no_case(b"AUTHENTICATE "), auth_type, opt(preceded( sp, alt(( map(base64, |data| Secret::new(Cow::Owned(data))), value(Secret::new(Cow::Borrowed(&b""[..])), tag("=")), )), )), )); let (remaining, (_, auth_type, raw_data)) = parser(input)?; // Server must send continuation ("+ ") at this point... Ok((remaining, (auth_type, raw_data))) } // # Command Select /// `command-select = "CHECK" / /// "CLOSE" / /// "EXPUNGE" / /// copy / /// fetch / /// store / /// uid / /// search` /// /// Note: Valid only when in Selected state pub(crate) fn command_select(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { alt(( value(CommandBody::Check, tag_no_case(b"CHECK")), value(CommandBody::Close, tag_no_case(b"CLOSE")), value(CommandBody::Expunge, tag_no_case(b"EXPUNGE")), uid_expunge, copy, fetch, store, uid, search, sort, thread, value(CommandBody::Unselect, tag_no_case(b"UNSELECT")), r#move, ))(input) } /// `copy = "COPY" SP sequence-set SP mailbox` pub(crate) fn copy(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case(b"COPY"), sp, sequence_set, sp, mailbox)); let (remaining, (_, _, sequence_set, _, mailbox)) = parser(input)?; Ok(( remaining, CommandBody::Copy { sequence_set, mailbox, uid: false, }, )) } /// ```abnf /// fetch = "FETCH" SP sequence-set SP ("ALL" / /// "FULL" / /// "FAST" / /// fetch-att / "(" fetch-att *(SP fetch-att) ")") /// [fetch-modifiers] ; FROM RFC 4466 /// ``` pub(crate) fn fetch(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case(b"FETCH"), preceded(sp, sequence_set), preceded( sp, alt(( value( MacroOrMessageDataItemNames::Macro(Macro::All), tag_no_case(b"ALL"), ), value( MacroOrMessageDataItemNames::Macro(Macro::Fast), tag_no_case(b"FAST"), ), value( MacroOrMessageDataItemNames::Macro(Macro::Full), tag_no_case(b"FULL"), ), map(fetch_att, |fetch_att| { MacroOrMessageDataItemNames::MessageDataItemNames(vec![fetch_att]) }), map( delimited(tag(b"("), separated_list0(sp, fetch_att), tag(b")")), MacroOrMessageDataItemNames::MessageDataItemNames, ), )), ), #[cfg(feature = "ext_condstore_qresync")] map(opt(fetch_modifiers), Option::unwrap_or_default), )); #[cfg(not(feature = "ext_condstore_qresync"))] let (remaining, (_, sequence_set, macro_or_item_names)) = parser(input)?; #[cfg(feature = "ext_condstore_qresync")] let (remaining, (_, sequence_set, macro_or_item_names, modifiers)) = parser(input)?; Ok(( remaining, CommandBody::Fetch { sequence_set, macro_or_item_names, uid: false, #[cfg(feature = "ext_condstore_qresync")] modifiers, }, )) } #[cfg(feature = "ext_condstore_qresync")] /// From RFC 4466: /// /// ```abnf /// fetch-modifiers = SP "(" fetch-modifier *(SP fetch-modifier) ")" /// ``` pub(crate) fn fetch_modifiers(input: &[u8]) -> IMAPResult<&[u8], Vec> { delimited(tag(" ("), separated_list1(sp, fetch_modifier), char(')'))(input) } #[cfg(feature = "ext_condstore_qresync")] /// From RFC 4466: /// /// ```abnf /// fetch-modifier = fetch-modifier-name [ SP fetch-modif-params ] /// /// fetch-modif-params = tagged-ext-val /// ;; This non-terminal shows recommended syntax /// ;; for future extensions. /// /// fetch-modifier-name = tagged-ext-label /// ``` /// /// From RFC 7162 (CONDSTORE/QRESYNC): /// /// ```abnf /// fetch-modifier =/ chgsince-fetch-mod /// ;; Conforms to the generic "fetch-modifier" syntax defined in [RFC4466]. /// /// chgsince-fetch-mod = "CHANGEDSINCE" SP mod-sequence-value /// ;; CHANGEDSINCE FETCH modifier conforms to the fetch-modifier syntax. /// /// rexpunges-fetch-mod = "VANISHED" /// ;; VANISHED UID FETCH modifier conforms to the fetch-modifier syntax defined in [RFC4466]. /// ;; It is only allowed in the UID FETCH command. /// ``` pub(crate) fn fetch_modifier(input: &[u8]) -> IMAPResult<&[u8], FetchModifier> { alt(( map( preceded(tag_no_case("CHANGEDSINCE "), mod_sequence_value), FetchModifier::ChangedSince, ), value(FetchModifier::Vanished, tag_no_case("VANISHED")), ))(input) } /// ```abnf /// store = "STORE" SP sequence-set [store-modifiers] SP store-att-flags /// ^^^^^^^^^^^^^^^^^ /// | /// RFC 4466 /// ``` pub(crate) fn store(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case(b"STORE"), preceded(sp, sequence_set), #[cfg(feature = "ext_condstore_qresync")] map(opt(store_modifiers), Option::unwrap_or_default), preceded(sp, store_att_flags), )); #[cfg(not(feature = "ext_condstore_qresync"))] let (remaining, (_, sequence_set, (kind, response, flags))) = parser(input)?; #[cfg(feature = "ext_condstore_qresync")] let (remaining, (_, sequence_set, modifiers, (kind, response, flags))) = parser(input)?; Ok(( remaining, CommandBody::Store { sequence_set, kind, response, flags, uid: false, #[cfg(feature = "ext_condstore_qresync")] modifiers, }, )) } #[cfg(feature = "ext_condstore_qresync")] /// From RFC 4466: /// /// ```abnf /// store-modifiers = SP "(" store-modifier *(SP store-modifier) ")" /// ``` pub(crate) fn store_modifiers(input: &[u8]) -> IMAPResult<&[u8], Vec> { delimited(tag(" ("), separated_list1(sp, store_modifier), char(')'))(input) } #[cfg(feature = "ext_condstore_qresync")] /// From RFC 4466: /// /// ```abnf /// store-modifier = store-modifier-name [SP store-modif-params] /// /// store-modif-params = tagged-ext-val /// /// store-modifier-name = tagged-ext-label /// ``` /// /// From RFC 7162 (CONDSTORE/QRESYNC): /// /// ```abnf /// store-modifier =/ "UNCHANGEDSINCE" SP mod-sequence-valzer /// ;; Only a single "UNCHANGEDSINCE" may be specified in a STORE operation. /// ``` pub(crate) fn store_modifier(input: &[u8]) -> IMAPResult<&[u8], StoreModifier> { map( preceded(tag_no_case(b"UNCHANGEDSINCE "), mod_sequence_valzer), StoreModifier::UnchangedSince, )(input) } /// `store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP (flag-list / (flag *(SP flag)))` pub(crate) fn store_att_flags( input: &[u8], ) -> IMAPResult<&[u8], (StoreType, StoreResponse, Vec)> { let mut parser = tuple(( tuple(( map( opt(alt(( value(StoreType::Add, tag(b"+")), value(StoreType::Remove, tag(b"-")), ))), |type_| match type_ { Some(type_) => type_, None => StoreType::Replace, }, ), tag_no_case(b"FLAGS"), map(opt(tag_no_case(b".SILENT")), |x| match x { Some(_) => StoreResponse::Silent, None => StoreResponse::Answer, }), )), sp, alt((flag_list, separated_list1(sp, flag))), )); let (remaining, ((store_type, _, store_response), _, flag_list)) = parser(input)?; Ok((remaining, (store_type, store_response, flag_list))) } /// `uid = "UID" SP (copy / fetch / search / store)` /// /// Note: Unique identifiers used instead of message sequence numbers pub(crate) fn uid(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case(b"UID"), sp, alt((copy, fetch, search, store, r#move)), )); let (remaining, (_, _, mut cmd)) = parser(input)?; match cmd { CommandBody::Copy { ref mut uid, .. } | CommandBody::Fetch { ref mut uid, .. } | CommandBody::Search { ref mut uid, .. } | CommandBody::Store { ref mut uid, .. } | CommandBody::Move { ref mut uid, .. } => *uid = true, _ => unreachable!(), } Ok((remaining, cmd)) } #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::{ core::Tag, fetch::{MessageDataItemName, Section}, }; use super::*; use crate::{CommandCodec, encode::Encoder}; #[test] fn test_parse_fetch() { println!("{:#?}", fetch(b"fetch 1:1 (flags)???")); } #[test] fn test_parse_fetch_att() { let tests = [ (MessageDataItemName::Envelope, "ENVELOPE???"), (MessageDataItemName::Flags, "FLAGS???"), (MessageDataItemName::InternalDate, "INTERNALDATE???"), (MessageDataItemName::Rfc822, "RFC822???"), (MessageDataItemName::Rfc822Header, "RFC822.HEADER???"), (MessageDataItemName::Rfc822Size, "RFC822.SIZE???"), (MessageDataItemName::Rfc822Text, "RFC822.TEXT???"), (MessageDataItemName::Body, "BODY???"), (MessageDataItemName::BodyStructure, "BODYSTRUCTURE???"), (MessageDataItemName::Uid, "UID???"), ( MessageDataItemName::BodyExt { partial: None, peek: false, section: None, }, "BODY[]???", ), ( MessageDataItemName::BodyExt { partial: None, peek: true, section: None, }, "BODY.PEEK[]???", ), ( MessageDataItemName::BodyExt { partial: None, peek: true, section: Some(Section::Text(None)), }, "BODY.PEEK[TEXT]???", ), ( MessageDataItemName::BodyExt { partial: Some((42, NonZeroU32::try_from(1337).unwrap())), peek: true, section: Some(Section::Text(None)), }, "BODY.PEEK[TEXT]<42.1337>???", ), ]; let expected_remainder = "???".as_bytes(); for (expected, test) in tests { let (got_remainder, got) = fetch_att(test.as_bytes()).unwrap(); assert_eq!(expected, got); assert_eq!(expected_remainder, got_remainder); } } #[test] fn test_that_empty_ir_is_encoded_correctly() { let command = Command::new( Tag::try_from("A").unwrap(), CommandBody::Authenticate { mechanism: AuthMechanism::Plain, initial_response: Some(Secret::new(Cow::Borrowed(&b""[..]))), }, ) .unwrap(); let buffer = CommandCodec::default().encode(&command).dump(); assert_eq!(buffer, b"A AUTHENTICATE PLAIN =\r\n") } } duesee-imap-codec-0d00966/imap-codec/src/core.rs000066400000000000000000000332461507724125200213400ustar00rootroot00000000000000use std::{borrow::Cow, num::NonZeroU32, str::from_utf8}; #[cfg(not(feature = "quirk_crlf_relaxed"))] use abnf_core::streaming::crlf; #[cfg(feature = "quirk_crlf_relaxed")] use abnf_core::streaming::crlf_relaxed as crlf; use abnf_core::{is_alpha, is_digit, streaming::dquote}; use base64::{Engine, engine::general_purpose::STANDARD as _base64}; use imap_types::{ core::{ AString, Atom, AtomExt, Charset, IString, Literal, LiteralMode, NString, Quoted, QuotedChar, Tag, Text, }, utils::{ indicators::{is_astring_char, is_atom_char, is_quoted_specials, is_text_char}, unescape_quoted, }, }; #[cfg(feature = "fuzz")] use nom::IResult; use nom::{ branch::alt, bytes::streaming::{escaped, tag, tag_no_case, take, take_while, take_while_m_n, take_while1}, character::streaming::{char, digit1, one_of}, combinator::{map, map_res, opt, recognize}, sequence::{delimited, terminated, tuple}, }; use crate::decode::{IMAPErrorKind, IMAPParseError, IMAPResult}; #[cfg(feature = "ext_utf8")] use crate::extensions::utf8::quoted_utf8; // ----- number ----- /// `number = 1*DIGIT` /// /// Unsigned 32-bit integer (0 <= n < 4,294,967,296) pub(crate) fn number(input: &[u8]) -> IMAPResult<&[u8], u32> { map_res( // # Safety // // `unwrap` is safe because `1*DIGIT` contains ASCII-only characters. map(digit1, |val| from_utf8(val).unwrap()), str::parse::, )(input) } /// ```abnf /// number64 = 1*DIGIT /// ``` /// /// Unsigned 63-bit integer (0 <= n <= 9,223,372,036,854,775,807) /// /// Defined in RFC 9051 pub(crate) fn number64(input: &[u8]) -> IMAPResult<&[u8], u64> { map_res( // # Safety // // `unwrap` is safe because `1*DIGIT` contains ASCII-only characters. map(digit1, |val| from_utf8(val).unwrap()), str::parse::, )(input) } /// `nz-number = digit-nz *DIGIT` /// /// Non-zero unsigned 32-bit integer (0 < n < 4,294,967,296) pub(crate) fn nz_number(input: &[u8]) -> IMAPResult<&[u8], NonZeroU32> { map_res(number, NonZeroU32::try_from)(input) } // ----- string ----- /// `string = quoted / literal` pub(crate) fn string(input: &[u8]) -> IMAPResult<&[u8], IString> { alt(( map(quoted, IString::Quoted), // quoted_utf8 must come after quoted but before literal. #[cfg(feature = "ext_utf8")] map(quoted_utf8, IString::QuotedUtf8), map(literal, IString::Literal), ))(input) } /// `quoted = DQUOTE *QUOTED-CHAR DQUOTE` /// /// This function only allocates a new String, when needed, i.e. when /// quoted chars need to be replaced. pub(crate) fn quoted(input: &[u8]) -> IMAPResult<&[u8], Quoted> { let mut parser = tuple(( dquote, map( escaped( take_while1(is_any_text_char_except_quoted_specials), '\\', one_of("\\\""), ), // # Safety // // `unwrap` is safe because val contains ASCII-only characters. |val| from_utf8(val).unwrap(), ), dquote, )); let (remaining, (_, quoted, _)) = parser(input)?; Ok((remaining, Quoted::unvalidated(unescape_quoted(quoted)))) } /// `QUOTED-CHAR = / "\" quoted-specials` pub(crate) fn quoted_char(input: &[u8]) -> IMAPResult<&[u8], QuotedChar> { map( alt(( map( take_while_m_n(1, 1, is_any_text_char_except_quoted_specials), |bytes: &[u8]| { assert_eq!(bytes.len(), 1); bytes[0] as char }, ), map( tuple((tag("\\"), take_while_m_n(1, 1, is_quoted_specials))), |(_, bytes): (_, &[u8])| { assert_eq!(bytes.len(), 1); bytes[0] as char }, ), )), QuotedChar::unvalidated, )(input) } pub(crate) fn is_any_text_char_except_quoted_specials(byte: u8) -> bool { is_text_char(byte) && !is_quoted_specials(byte) } /// `literal = "{" number "}" CRLF *CHAR8` /// /// Number represents the number of CHAR8s /// /// # IMAP4 Non-synchronizing Literals /// /// ```abnf /// literal = "{" number ["+"] "}" CRLF *CHAR8 /// ; Number represents the number of CHAR8 octets /// /// CHAR8 = /// /// literal8 = /// ``` /// -- pub(crate) fn literal(input: &[u8]) -> IMAPResult<&[u8], Literal> { let (remaining, (length, mode)) = terminated( delimited( tag(b"{"), tuple(( number, map(opt(char('+')), |i| { i.map(|_| LiteralMode::NonSync).unwrap_or(LiteralMode::Sync) }), )), tag(b"}"), ), crlf, )(input)?; // Signal that an continuation request could be required. // Note: This doesn't trigger when there is data following the literal prefix. if remaining.is_empty() { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::Literal { // We don't know the tag here and rely on an upper parser, e.g., `command` to fill this in. tag: None, length, mode, }, })); } let (remaining, data) = take(length)(remaining)?; match Literal::try_from(data) { Ok(mut literal) => { literal.set_mode(mode); Ok((remaining, literal)) } Err(_) => Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::LiteralContainsNull, })), } } // ----- astring ----- atom (roughly) or string /// `astring = 1*ASTRING-CHAR / string` pub(crate) fn astring(input: &[u8]) -> IMAPResult<&[u8], AString> { alt(( map(take_while1(is_astring_char), |bytes: &[u8]| { // # Safety // // `unwrap` is safe, because `is_astring_char` enforces that the bytes ... // * contain ASCII-only characters, i.e., `from_utf8` will return `Ok`. // * are valid according to `AtomExt::verify(), i.e., `unvalidated` is safe. AString::Atom(AtomExt::unvalidated(Cow::Borrowed( std::str::from_utf8(bytes).unwrap(), ))) }), map(string, AString::String), ))(input) } /// `atom = 1*ATOM-CHAR` pub(crate) fn atom(input: &[u8]) -> IMAPResult<&[u8], Atom> { let parser = take_while1(is_atom_char); let (remaining, parsed_atom) = parser(input)?; // # Safety // // `unwrap` is safe, because `is_atom_char` enforces ... // * that the string is always UTF8, and ... // * contains only the allowed characters. Ok(( remaining, Atom::unvalidated(from_utf8(parsed_atom).unwrap()), )) } // ----- nstring ----- nil or string /// `nstring = string / nil` pub(crate) fn nstring(input: &[u8]) -> IMAPResult<&[u8], NString> { alt(( map(string, |item| NString(Some(item))), map(nil, |_| NString(None)), ))(input) } #[inline] /// `nil = "NIL"` pub(crate) fn nil(input: &[u8]) -> IMAPResult<&[u8], &[u8]> { tag_no_case(b"NIL")(input) } // ----- text ----- /// `text = 1*TEXT-CHAR` pub(crate) fn text(input: &[u8]) -> IMAPResult<&[u8], Text> { map(take_while1(is_text_char), |bytes| // # Safety // // `is_text_char` makes sure that the sequence of bytes // is always valid ASCII. Thus, it is also valid UTF-8. Text::unvalidated(from_utf8(bytes).unwrap()))(input) } // ----- base64 ----- /// `base64 = *(4base64-char) [base64-terminal]` pub(crate) fn base64(input: &[u8]) -> IMAPResult<&[u8], Vec> { map_res( recognize(tuple(( take_while(is_base64_char), opt(alt((tag("=="), tag("=")))), ))), |input| _base64.decode(input), )(input) } /// `base64-char = ALPHA / DIGIT / "+" / "/" ; Case-sensitive` pub(crate) fn is_base64_char(i: u8) -> bool { is_alpha(i) || is_digit(i) || i == b'+' || i == b'/' } // base64-terminal = (2base64-char "==") / (3base64-char "=") // ----- charset ----- /// `charset = atom / quoted` /// /// Note: see errata id: 261 pub(crate) fn charset(input: &[u8]) -> IMAPResult<&[u8], Charset> { alt((map(atom, Charset::Atom), map(quoted, Charset::Quoted)))(input) } // ----- tag ----- /// `tag = 1*` pub(crate) fn tag_imap(input: &[u8]) -> IMAPResult<&[u8], Tag> { map(take_while1(|b| is_astring_char(b) && b != b'+'), |val| { // # Safety // // `is_astring_char` ensures that `val` is UTF-8. Tag::unvalidated(from_utf8(val).unwrap()) })(input) } // TODO: This could be exposed in a more elegant way... #[cfg(feature = "fuzz")] /// `tag = 1*` pub fn fuzz_tag_imap(input: &[u8]) -> IResult<&[u8], Tag> { match tag_imap(input) { Ok((rem, out)) => Ok((rem, out)), Err(e) => match e { nom::Err::Incomplete(needed) => Err(nom::Err::Incomplete(needed)), nom::Err::Error(e) => Err(nom::Err::Error(nom::error::Error::new( e.input, nom::error::ErrorKind::Verify, ))), nom::Err::Failure(e) => Err(nom::Err::Failure(nom::error::Error::new( e.input, nom::error::ErrorKind::Verify, ))), }, } } #[cfg(test)] mod tests { use super::*; use crate::encode::{EncodeContext, EncodeIntoContext}; #[test] fn test_atom() { assert!(atom(b" ").is_err()); assert!(atom(b"").is_err()); let (rem, val) = atom(b"a(").unwrap(); assert_eq!(val, "a".try_into().unwrap()); assert_eq!(rem, b"("); let (rem, val) = atom(b"xxx yyy").unwrap(); assert_eq!(val, "xxx".try_into().unwrap()); assert_eq!(rem, b" yyy"); } #[test] fn test_quoted() { let (rem, val) = quoted(br#""Hello"???"#).unwrap(); assert_eq!(rem, b"???"); assert_eq!(val, Quoted::try_from("Hello").unwrap()); // Allowed escapes... assert!(quoted(br#""Hello \" "???"#).is_ok()); assert!(quoted(br#""Hello \\ "???"#).is_ok()); // Not allowed escapes... assert!(quoted(br#""Hello \a "???"#).is_err()); assert!(quoted(br#""Hello \z "???"#).is_err()); assert!(quoted(br#""Hello \? "???"#).is_err()); let (rem, val) = quoted(br#""Hello \"World\""???"#).unwrap(); assert_eq!(rem, br#"???"#); // Should it be this (Hello \"World\") ... //assert_eq!(val, r#"Hello \"World\""#); // ... or this (Hello "World")? assert_eq!(val, Quoted::try_from("Hello \"World\"").unwrap()); // Test Incomplete assert!(matches!(quoted(br#""#), Err(nom::Err::Incomplete(_)))); assert!(matches!(quoted(br#""\"#), Err(nom::Err::Incomplete(_)))); assert!(matches!( quoted(br#""Hello "#), Err(nom::Err::Incomplete(_)) )); // Test Error assert!(matches!(quoted(br#"\"#), Err(nom::Err::Error(_)))); } #[test] fn test_quoted_char() { let (rem, val) = quoted_char(b"\\\"xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, QuotedChar::try_from('"').unwrap()); } #[test] fn test_number() { assert!(number(b"").is_err()); assert!(number(b"?").is_err()); assert!(number(b"0?").is_ok()); assert!(number(b"55?").is_ok()); assert!(number(b"999?").is_ok()); } #[test] fn test_nz_number() { assert!(number(b"").is_err()); assert!(number(b"?").is_err()); assert!(nz_number(b"0?").is_err()); assert!(nz_number(b"55?").is_ok()); assert!(nz_number(b"999?").is_ok()); } #[test] fn test_literal() { assert!(literal(b"{3}\r\n123").is_ok()); assert!(literal(b"{3}\r\n1\x003").is_err()); let (rem, val) = literal(b"{3}\r\n123xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, Literal::try_from(b"123".as_slice()).unwrap()); } #[test] fn test_nil() { assert!(nil(b"nil").is_ok()); assert!(nil(b"nil ").is_ok()); assert!(nil(b" nil").is_err()); assert!(nil(b"null").is_err()); let (rem, _) = nil(b"nilxxx").unwrap(); assert_eq!(rem, b"xxx"); } #[test] fn test_encode_charset() { let tests = [ ("bengali", "bengali"), ("\"simple\" english", r#""\"simple\" english""#), ("", "\"\""), ("\"", "\"\\\"\""), ("\\", "\"\\\\\""), ]; for (from, expected) in tests.iter() { let cs = Charset::try_from(*from).unwrap(); println!("{cs:?}"); let mut ctx = EncodeContext::new(); cs.encode_ctx(&mut ctx).unwrap(); let out = ctx.dump(); assert_eq!(from_utf8(&out).unwrap(), *expected); } assert!(Charset::try_from("\r").is_err()); assert!(Charset::try_from("\n").is_err()); assert!(Charset::try_from("¹").is_err()); assert!(Charset::try_from("²").is_err()); assert!(Charset::try_from("\x00").is_err()); } #[test] fn test_is_base64_char() { assert!(is_base64_char(b'a')); assert!(is_base64_char(b'z')); assert!(is_base64_char(b'A')); assert!(is_base64_char(b'Z')); } #[test] fn test_base64() { _base64.decode(b"AA==").unwrap(); // Note: "pad bits MUST be set to zero by conforming encoders" [RFC 4648, sec. 3.5]. //_base64.decode(b"aa==").unwrap(); _base64.decode(b"aQ==").unwrap(); } } duesee-imap-codec-0d00966/imap-codec/src/datetime.rs000066400000000000000000000305751507724125200222060ustar00rootroot00000000000000use abnf_core::{ is_digit, streaming::{dquote, sp}, }; use chrono::{ FixedOffset, LocalResult, NaiveDate as ChronoNaiveDate, NaiveDateTime, NaiveTime, TimeZone, }; use imap_types::datetime::{DateTime, NaiveDate}; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case, take_while_m_n}, character::streaming::char, combinator::{map, map_res, value}, sequence::{delimited, preceded, tuple}, }; use crate::decode::{IMAPErrorKind, IMAPParseError, IMAPResult}; /// ```abnf /// date = date-text / DQUOTE date-text DQUOTE /// ``` pub(crate) fn date(input: &[u8]) -> IMAPResult<&[u8], Option> { alt((date_text, delimited(dquote, date_text, dquote)))(input) } /// ```abnf /// date-text = date-day "-" date-month "-" date-year /// ``` pub(crate) fn date_text(input: &[u8]) -> IMAPResult<&[u8], Option> { let mut parser = tuple((date_day, tag(b"-"), date_month, tag(b"-"), date_year)); let (remaining, (d, _, m, _, y)) = parser(input)?; Ok(( remaining, ChronoNaiveDate::from_ymd_opt(y.into(), m.into(), d.into()).map(NaiveDate::unvalidated), )) } /// Day of month. /// /// ```abnf /// date-day = 1*2DIGIT /// ``` pub(crate) fn date_day(input: &[u8]) -> IMAPResult<&[u8], u8> { digit_1_2(input) } /// ```abnf /// date-month = "Jan" / "Feb" / "Mar" / "Apr" / /// "May" / "Jun" / "Jul" / "Aug" / /// "Sep" / "Oct" / "Nov" / "Dec" /// ``` pub(crate) fn date_month(input: &[u8]) -> IMAPResult<&[u8], u8> { alt(( value(1, tag_no_case(b"Jan")), value(2, tag_no_case(b"Feb")), value(3, tag_no_case(b"Mar")), value(4, tag_no_case(b"Apr")), value(5, tag_no_case(b"May")), value(6, tag_no_case(b"Jun")), value(7, tag_no_case(b"Jul")), value(8, tag_no_case(b"Aug")), value(9, tag_no_case(b"Sep")), value(10, tag_no_case(b"Oct")), value(11, tag_no_case(b"Nov")), value(12, tag_no_case(b"Dec")), ))(input) } /// ```abnf /// date-year = 4DIGIT /// ``` pub(crate) fn date_year(input: &[u8]) -> IMAPResult<&[u8], u16> { digit_4(input) } /// Hours minutes seconds. /// /// ```abnf /// time = 2DIGIT ":" 2DIGIT ":" 2DIGIT /// ``` pub(crate) fn time(input: &[u8]) -> IMAPResult<&[u8], Option> { let mut parser = tuple((digit_2, tag(b":"), digit_2, tag(b":"), digit_2)); let (remaining, (h, _, m, _, s)) = parser(input)?; Ok(( remaining, NaiveTime::from_hms_opt(h.into(), m.into(), s.into()), )) } /// ```abnf /// date-time = DQUOTE /// date-day-fixed "-" date-month "-" date-year SP /// time SP /// zone /// DQUOTE /// ``` pub(crate) fn date_time(input: &[u8]) -> IMAPResult<&[u8], DateTime> { let mut parser = delimited( dquote, tuple(( date_day_fixed, tag(b"-"), date_month, tag(b"-"), date_year, sp, time, sp, zone, )), dquote, ); let (remaining, (d, _, m, _, y, _, time, _, zone)) = parser(input)?; let date = ChronoNaiveDate::from_ymd_opt(y.into(), m.into(), d.into()); match (date, time, zone) { (Some(date), Some(time), Some(zone)) => { let local_datetime = NaiveDateTime::new(date, time); if let LocalResult::Single(datetime) = zone.from_local_datetime(&local_datetime) { Ok((remaining, DateTime::unvalidated(datetime))) } else { Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::BadDateTime, })) } } _ => Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::BadDateTime, })), } } /// Fixed-format version of date-day. /// /// ```abnf /// date-day-fixed = (SP DIGIT) / 2DIGIT /// ``` pub(crate) fn date_day_fixed(input: &[u8]) -> IMAPResult<&[u8], u8> { alt(( map( preceded(sp, take_while_m_n(1, 1, is_digit)), |bytes: &[u8]| bytes[0] - b'0', ), digit_2, ))(input) } /// Signed four-digit value of hhmm representing hours and minutes east of Greenwich (that is, the /// amount that the given time differs from Universal Time). /// /// Subtracting the timezone from the given time will give the UT form. The Universal Time zone is /// "+0000". /// /// ```abnf /// zone = ("+" / "-") 4DIGIT /// ``` pub(crate) fn zone(input: &[u8]) -> IMAPResult<&[u8], Option> { let mut parser = tuple((alt((char('+'), char('-'))), digit_2, digit_2)); let (remaining, (sign, hh, mm)) = parser(input)?; let offset = 3600 * (hh as i32) + 60 * (mm as i32); let zone = match sign { '+' => FixedOffset::east_opt(offset), '-' => FixedOffset::west_opt(offset), _ => unreachable!(), }; Ok((remaining, zone)) } fn digit_1_2(input: &[u8]) -> IMAPResult<&[u8], u8> { map_res( map(take_while_m_n(1, 2, is_digit), |bytes| { // # Safety // // `bytes` is always UTF-8. std::str::from_utf8(bytes).unwrap() }), str::parse::, )(input) } fn digit_2(input: &[u8]) -> IMAPResult<&[u8], u8> { map_res( map(take_while_m_n(2, 2, is_digit), |bytes| { // # Safety // // `bytes` is always UTF-8. std::str::from_utf8(bytes).unwrap() }), str::parse::, )(input) } fn digit_4(input: &[u8]) -> IMAPResult<&[u8], u16> { map_res( map(take_while_m_n(4, 4, is_digit), |bytes| { // # Safety // // `bytes` is always UTF-8. std::str::from_utf8(bytes).unwrap() }), str::parse::, )(input) } #[cfg(test)] mod tests { use std::str::from_utf8; use super::*; use crate::testing::known_answer_test_encode; #[test] fn test_encode_date_time() { let tests = [ ( DateTime::try_from( chrono::DateTime::parse_from_rfc2822("Mon, 7 Feb 1994 21:52:25 -0800 (PST)") .unwrap(), ) .unwrap(), b"\"07-Feb-1994 21:52:25 -0800\"".as_ref(), ), ( DateTime::try_from( chrono::DateTime::parse_from_rfc2822("Mon, 7 Feb 0000 21:52:25 -0800 (PST)") .unwrap(), ) .unwrap(), b"\"07-Feb-0000 21:52:25 -0800\"".as_ref(), ), ]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_date() { let (rem, val) = date(b"1-Feb-2020xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!( val, ChronoNaiveDate::from_ymd_opt(2020, 2, 1).map(NaiveDate::unvalidated) ); let (rem, val) = date(b"\"1-Feb-2020\"xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!( val, ChronoNaiveDate::from_ymd_opt(2020, 2, 1).map(NaiveDate::unvalidated) ); let (rem, val) = date(b"\"01-Feb-2020\"xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!( val, ChronoNaiveDate::from_ymd_opt(2020, 2, 1).map(NaiveDate::unvalidated) ); } #[test] fn test_date_text() { let (rem, val) = date_text(b"1-Feb-2020").unwrap(); assert_eq!(rem, b""); assert_eq!( val, ChronoNaiveDate::from_ymd_opt(2020, 2, 1).map(NaiveDate::unvalidated) ); } #[test] fn test_date_day() { let (rem, val) = date_day(b"1xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, 1); let (rem, val) = date_day(b"01xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, 1); let (rem, val) = date_day(b"999xxx").unwrap(); assert_eq!(rem, b"9xxx"); assert_eq!(val, 99); } #[test] fn test_date_month() { let (rem, val) = date_month(b"jAn").unwrap(); assert_eq!(rem, b""); assert_eq!(val, 1); let (rem, val) = date_month(b"DeCxxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, 12); } #[test] fn test_date_year() { let (rem, val) = date_year(b"1985xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, 1985); let (rem, val) = date_year(b"1991xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, 1991); } #[test] fn test_date_day_fixed() { let (rem, val) = date_day_fixed(b"00").unwrap(); assert_eq!(rem, b""); assert_eq!(val, 0); let (rem, val) = date_day_fixed(b" 0").unwrap(); assert_eq!(rem, b""); assert_eq!(val, 0); let (rem, val) = date_day_fixed(b"99").unwrap(); assert_eq!(rem, b""); assert_eq!(val, 99); let (rem, val) = date_day_fixed(b" 9").unwrap(); assert_eq!(rem, b""); assert_eq!(val, 9); } #[test] fn test_time() { assert!(time(b"1:34:56xxx").is_err()); assert!(time(b"12:3:56xxx").is_err()); assert!(time(b"12:34:5xxx").is_err()); let (rem, val) = time(b"12:34:56xxx").unwrap(); assert_eq!(rem, b"xxx"); assert_eq!(val, NaiveTime::from_hms_opt(12, 34, 56)); let (rem, val) = time(b"99:99:99 ").unwrap(); assert_eq!(rem, b" "); assert_eq!(val, NaiveTime::from_hms_opt(99, 99, 99)); let (rem, val) = time(b"12:34:56").unwrap(); assert_eq!(rem, b""); assert_eq!(val, NaiveTime::from_hms_opt(12, 34, 56)); let (rem, val) = time(b"99:99:99").unwrap(); assert_eq!(rem, b""); assert_eq!(val, NaiveTime::from_hms_opt(99, 99, 99)); } #[test] fn test_date_time() { let (rem, val) = date_time(b"\" 1-Feb-1985 12:34:56 +0100\"xxx").unwrap(); assert_eq!(rem, b"xxx"); let local_datetime = NaiveDateTime::new( ChronoNaiveDate::from_ymd_opt(1985, 2, 1).unwrap(), NaiveTime::from_hms_opt(12, 34, 56).unwrap(), ); let datetime = DateTime::try_from( FixedOffset::east_opt(3600) .unwrap() .from_local_datetime(&local_datetime) .unwrap(), ) .unwrap(); println!("{val:?} == \n{datetime:?}"); assert_eq!(val, datetime); } #[test] fn test_date_time_invalid() { let tests = [ b"\" 1-Feb-0000 12:34:56 +0000\"xxx".as_ref(), // ok b"\" 1-Feb-9999 12:34:56 +0000\"xxx", // ok b"\" 1-Feb-0000 12:34:56 -0000\"xxx", // ok b"\" 1-Feb-9999 12:34:56 -0000\"xxx", // ok b"\" 1-Feb-2020 00:00:00 +0100\"xxx", // ok b"\" 1-Feb-0000 12:34:56 +9999\"xxx", b"\" 1-Feb-9999 12:34:56 +9999\"xxx", b"\" 1-Feb-0000 12:34:56 -9999\"xxx", b"\" 1-Feb-9999 12:34:56 -9999\"xxx", b"\" 1-Feb-2020 99:99:99 +0100\"xxx", b"\"31-Feb-2020 00:00:00 +0100\"xxx", b"\"99-Feb-2020 99:99:99 +0100\"xxx", ]; for test in &tests[..5] { let (rem, datetime) = date_time(test).unwrap(); assert_eq!(rem, b"xxx"); println!("{} -> {:?}", from_utf8(test).unwrap(), datetime); } for test in &tests[5..] { assert!(date_time(test).is_err()); } } #[test] fn test_zone() { let (rem, val) = zone(b"+0000xxx").unwrap(); eprintln!("{val:?}"); assert_eq!(rem, b"xxx"); assert_eq!(val, FixedOffset::east_opt(0)); let (rem, val) = zone(b"+0000").unwrap(); eprintln!("{val:?}"); assert_eq!(rem, b""); assert_eq!(val, FixedOffset::east_opt(0)); let (rem, val) = zone(b"-0205xxx").unwrap(); eprintln!("{val:?}"); assert_eq!(rem, b"xxx"); assert_eq!(val, FixedOffset::west_opt(2 * 3600 + 5 * 60)); let (rem, val) = zone(b"-1159").unwrap(); eprintln!("{val:?}"); assert_eq!(rem, b""); assert_eq!(val, FixedOffset::west_opt(11 * 3600 + 59 * 60)); let (rem, val) = zone(b"-1159").unwrap(); eprintln!("{val:?}"); assert_eq!(rem, b""); assert_eq!(val, FixedOffset::west_opt(11 * 3600 + 59 * 60)); } } duesee-imap-codec-0d00966/imap-codec/src/envelope.rs000066400000000000000000000143231507724125200222200ustar00rootroot00000000000000use abnf_core::streaming::sp; use imap_types::{ core::NString, envelope::{Address, Envelope}, }; use nom::{ branch::alt, bytes::streaming::tag, combinator::map, multi::many1, sequence::{delimited, tuple}, }; use crate::{ core::{nil, nstring}, decode::IMAPResult, }; /// ```abnf /// envelope = "(" /// env-date SP /// env-subject SP /// env-from SP /// env-sender SP /// env-reply-to SP /// env-to SP /// env-cc SP /// env-bcc SP /// env-in-reply-to SP /// env-message-id /// ")" /// ``` pub(crate) fn envelope(input: &[u8]) -> IMAPResult<&[u8], Envelope> { let mut parser = delimited( tag(b"("), tuple(( env_date, sp, env_subject, sp, env_from, sp, env_sender, sp, env_reply_to, sp, env_to, sp, env_cc, sp, env_bcc, sp, env_in_reply_to, sp, env_message_id, )), tag(b")"), ); let ( remaining, ( date, _, subject, _, from, _, sender, _, reply_to, _, to, _, cc, _, bcc, _, in_reply_to, _, message_id, ), ) = parser(input)?; Ok(( remaining, Envelope { date, subject, from, sender, reply_to, to, cc, bcc, in_reply_to, message_id, }, )) } #[inline] /// `env-date = nstring` pub(crate) fn env_date(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `env-subject = nstring` pub(crate) fn env_subject(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } /// `env-from = "(" 1*address ")" / nil` pub(crate) fn env_from(input: &[u8]) -> IMAPResult<&[u8], Vec

> { alt(( delimited(tag(b"("), many1(address), tag(b")")), map(nil, |_| Vec::new()), ))(input) } /// `env-sender = "(" 1*address ")" / nil` pub(crate) fn env_sender(input: &[u8]) -> IMAPResult<&[u8], Vec
> { alt(( delimited(tag(b"("), many1(address), tag(b")")), map(nil, |_| Vec::new()), ))(input) } /// `env-reply-to = "(" 1*address ")" / nil` pub(crate) fn env_reply_to(input: &[u8]) -> IMAPResult<&[u8], Vec
> { alt(( delimited(tag(b"("), many1(address), tag(b")")), map(nil, |_| Vec::new()), ))(input) } /// `env-to = "(" 1*address ")" / nil` pub(crate) fn env_to(input: &[u8]) -> IMAPResult<&[u8], Vec
> { alt(( delimited(tag(b"("), many1(address), tag(b")")), map(nil, |_| Vec::new()), ))(input) } /// `env-cc = "(" 1*address ")" / nil` pub(crate) fn env_cc(input: &[u8]) -> IMAPResult<&[u8], Vec
> { alt(( delimited(tag(b"("), many1(address), tag(b")")), map(nil, |_| Vec::new()), ))(input) } /// `env-bcc = "(" 1*address ")" / nil` pub(crate) fn env_bcc(input: &[u8]) -> IMAPResult<&[u8], Vec
> { alt(( delimited(tag(b"("), many1(address), tag(b")")), map(nil, |_| Vec::new()), ))(input) } #[inline] /// `env-in-reply-to = nstring` pub(crate) fn env_in_reply_to(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `env-message-id = nstring` pub(crate) fn env_message_id(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } /// `address = "(" /// addr-name SP /// addr-adl SP /// addr-mailbox SP /// addr-host /// ")"` pub(crate) fn address(input: &[u8]) -> IMAPResult<&[u8], Address> { #[cfg_attr(feature = "quirk_spaces_between_addresses", allow(unused_mut))] let mut parser = delimited( tag(b"("), tuple((addr_name, sp, addr_adl, sp, addr_mailbox, sp, addr_host)), tag(b")"), ); #[cfg(feature = "quirk_spaces_between_addresses")] let mut parser = nom::sequence::preceded(nom::multi::many0(sp), parser); let (remaining, (name, _, adl, _, mailbox, _, host)) = parser(input)?; Ok(( remaining, Address { name, adl, mailbox, host, }, )) } #[inline] /// `addr-name = nstring` /// /// If non-NIL, holds phrase from [RFC-2822] /// mailbox after removing [RFC-2822] quoting /// TODO(misuse): use `Phrase`? pub(crate) fn addr_name(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `addr-adl = nstring` /// /// Holds route from [RFC-2822] route-addr if non-NIL /// TODO(misuse): use `Route`? pub(crate) fn addr_adl(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `addr-mailbox = nstring` /// /// NIL indicates end of [RFC-2822] group; /// if non-NIL and addr-host is NIL, holds [RFC-2822] group name. /// Otherwise, holds [RFC-2822] local-part after removing [RFC-2822] quoting /// TODO(misuse): use `GroupName` or `LocalPart`? pub(crate) fn addr_mailbox(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[inline] /// `addr-host = nstring` /// /// NIL indicates [RFC-2822] group syntax. /// Otherwise, holds [RFC-2822] domain name /// TODO(misuse): use `DomainName`? pub(crate) fn addr_host(input: &[u8]) -> IMAPResult<&[u8], NString> { nstring(input) } #[cfg(test)] mod tests { use imap_types::core::{IString, NString}; use super::*; #[test] fn test_parse_address() { let (rem, val) = address(b"(nil {3}\r\nxxx \"xxx\" nil)").unwrap(); assert_eq!( val, Address { name: NString(None), adl: NString(Some(IString::Literal( b"xxx".as_slice().try_into().unwrap() ))), mailbox: NString(Some(IString::Quoted("xxx".try_into().unwrap()))), host: NString(None), } ); assert_eq!(rem, b""); } } duesee-imap-codec-0d00966/imap-codec/src/extensions.rs000066400000000000000000000006601507724125200226010ustar00rootroot00000000000000pub mod binary; pub mod compress; #[cfg(feature = "ext_condstore_qresync")] pub mod condstore_qresync; pub mod enable; #[cfg(feature = "ext_id")] pub mod id; pub mod idle; pub mod literal; #[cfg(feature = "ext_metadata")] pub mod metadata; pub mod r#move; #[cfg(feature = "ext_namespace")] pub mod namespace; pub mod quota; pub mod sort; pub mod thread; pub mod uidplus; pub mod unselect; #[cfg(feature = "ext_utf8")] pub mod utf8; duesee-imap-codec-0d00966/imap-codec/src/extensions/000077500000000000000000000000001507724125200222315ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/src/extensions/binary.rs000066400000000000000000000067271507724125200240770ustar00rootroot00000000000000use std::{borrow::Cow, io::Write, num::NonZeroU32}; #[cfg(not(feature = "quirk_crlf_relaxed"))] use abnf_core::streaming::crlf; #[cfg(feature = "quirk_crlf_relaxed")] use abnf_core::streaming::crlf_relaxed as crlf; use imap_types::{ core::LiteralMode, extensions::binary::{Literal8, LiteralOrLiteral8}, }; use nom::{ bytes::streaming::{tag, take}, character::streaming::char, combinator::{map, opt}, sequence::{delimited, separated_pair, terminated, tuple}, }; use crate::{ core::{number, nz_number}, decode::{IMAPErrorKind, IMAPParseError, IMAPResult}, encode::{EncodeContext, EncodeIntoContext}, fetch::section_part, }; /// See and /// /// ```abnf /// literal8 = "~{" number ["+"] "}" CRLF *OCTET /// ;; represents the number of OCTETs in the response string. /// ;; The "+" is only allowed when both LITERAL+ and BINARY extensions are supported by the server. /// ``` pub(crate) fn literal8(input: &[u8]) -> IMAPResult<&[u8], Literal8> { let (remaining, (length, mode)) = terminated( delimited( tag(b"~{"), tuple(( number, map(opt(char('+')), |i| { i.map(|_| LiteralMode::NonSync).unwrap_or(LiteralMode::Sync) }), )), tag(b"}"), ), crlf, )(input)?; // Signal that an continuation request could be required. // Note: This doesn't trigger when there is data following the literal prefix. if remaining.is_empty() { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::Literal { // We don't know the tag here and rely on an upper parser, e.g., `command` to fill this in. tag: None, length, mode, }, })); } let (remaining, data) = take(length)(remaining)?; Ok(( remaining, Literal8 { data: Cow::Borrowed(data), mode, }, )) } impl EncodeIntoContext for LiteralOrLiteral8<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { LiteralOrLiteral8::Literal(lit) => lit.encode_ctx(ctx), LiteralOrLiteral8::Literal8(lit8) => lit8.encode_ctx(ctx), } } } impl EncodeIntoContext for Literal8<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self.mode { LiteralMode::Sync => write!(ctx, "~{{{}}}\r\n", self.data.len())?, LiteralMode::NonSync => write!(ctx, "~{{{}+}}\r\n", self.data.len())?, } ctx.push_line(); ctx.write_all(&self.data)?; ctx.push_literal(self.mode); Ok(()) } } /// ```abnf /// section-binary = "[" [section-part] "]" /// ``` pub(crate) fn section_binary(input: &[u8]) -> IMAPResult<&[u8], Vec> { delimited( tag("["), // We use `Vec` instead of `Option>`. map(opt(section_part), |section_part| { section_part.map(|i| i.into_inner()).unwrap_or_default() }), tag("]"), )(input) } /// ```abnf /// partial = "<" number "." nz-number ">" /// ``` pub(crate) fn partial(input: &[u8]) -> IMAPResult<&[u8], (u32, NonZeroU32)> { delimited( tag(b"<"), separated_pair(number, tag(b"."), nz_number), tag(b">"), )(input) } duesee-imap-codec-0d00966/imap-codec/src/extensions/compress.rs000066400000000000000000000051451507724125200244370ustar00rootroot00000000000000//! The IMAP COMPRESS Extension // Additional changes: // // command-auth =/ compress // capability =/ "COMPRESS=" algorithm // resp-text-code =/ "COMPRESSIONACTIVE" use std::io::Write; use imap_types::{command::CommandBody, extensions::compress::CompressionAlgorithm}; use nom::{ bytes::streaming::tag_no_case, combinator::{map, value}, sequence::preceded, }; use crate::{ decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, }; /// `algorithm = "DEFLATE"` pub(crate) fn algorithm(input: &[u8]) -> IMAPResult<&[u8], CompressionAlgorithm> { value(CompressionAlgorithm::Deflate, tag_no_case("DEFLATE"))(input) } /// `compress = "COMPRESS" SP algorithm` pub(crate) fn compress(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { map(preceded(tag_no_case("COMPRESS "), algorithm), |algorithm| { CommandBody::Compress { algorithm } })(input) } impl EncodeIntoContext for CompressionAlgorithm { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } #[cfg(test)] mod tests { use imap_types::command::{Command, CommandBody}; use super::*; use crate::testing::kat_inverse_command; #[test] fn test_parse_compress() { let tests = [ ( b"compress deflate ".as_ref(), Ok(( b" ".as_ref(), CommandBody::compress(CompressionAlgorithm::Deflate), )), ), (b"compress deflat ".as_ref(), Err(())), (b"compres deflate ".as_ref(), Err(())), (b"compress deflate ".as_ref(), Err(())), ]; for (test, expected) in tests { match expected { Ok((expected_rem, expected_object)) => { let (got_rem, got_object) = compress(test).unwrap(); assert_eq!(expected_object, got_object); assert_eq!(expected_rem, got_rem); } Err(_) => { assert!(compress(test).is_err()) } } } } #[test] fn test_kat_inverse_body_compress() { kat_inverse_command(&[ ( b"A COMPRESS DEFLATE\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::compress(CompressionAlgorithm::Deflate)).unwrap(), ), ( b"A COMPRESS DEFLATE\r\n?".as_ref(), b"?".as_ref(), Command::new("A", CommandBody::compress(CompressionAlgorithm::Deflate)).unwrap(), ), ]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/condstore_qresync.rs000066400000000000000000000111131507724125200263400ustar00rootroot00000000000000use std::num::NonZeroU64; use abnf_core::streaming::sp; #[cfg(feature = "ext_condstore_qresync")] use imap_types::extensions::condstore_qresync::{AttributeFlag, EntryTypeReq}; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, character::streaming::char, combinator::{map, map_res, opt, value}, sequence::{delimited, preceded, tuple}, }; use crate::{ core::{atom, number64}, decode::IMAPResult, }; /// ```abnf /// mod-sequence-valzer = "0" / mod-sequence-value /// ``` pub(crate) fn mod_sequence_valzer(input: &[u8]) -> IMAPResult<&[u8], u64> { number64(input) } /// Positive unsigned 64-bit integer (mod-sequence) (1 <= n < 18,446,744,073,709,551,615) /// /// ```abnf /// mod-sequence-value = 1*DIGIT /// ``` pub(crate) fn mod_sequence_value(input: &[u8]) -> IMAPResult<&[u8], NonZeroU64> { map_res(number64, NonZeroU64::try_from)(input) } /// ```abnf /// search-sort-mod-seq = "(" "MODSEQ" SP mod-sequence-value ")" /// ``` pub(crate) fn search_sort_mod_seq(input: &[u8]) -> IMAPResult<&[u8], NonZeroU64> { delimited( char('('), preceded(tag_no_case("MODSEQ "), mod_sequence_value), char(')'), )(input) } /// ```abnf /// search-modsequence = "MODSEQ" [search-modseq-ext] SP mod-sequence-valzer /// ``` #[allow(clippy::type_complexity)] pub(crate) fn search_modsequence( input: &[u8], ) -> IMAPResult<&[u8], (Option<(AttributeFlag, EntryTypeReq)>, u64)> { preceded( tag_no_case("MODSEQ"), tuple((opt(search_modseq_ext), preceded(sp, mod_sequence_valzer))), )(input) } /// ```abnf /// search-modseq-ext = SP entry-name SP entry-type-req /// ``` pub(crate) fn search_modseq_ext(input: &[u8]) -> IMAPResult<&[u8], (AttributeFlag, EntryTypeReq)> { tuple((preceded(sp, entry_name), preceded(sp, entry_type_req)))(input) } /// ```abnf /// entry-name = entry-flag-name /// ``` #[inline] pub(crate) fn entry_name(input: &[u8]) -> IMAPResult<&[u8], AttributeFlag> { entry_flag_name(input) } /// Each system or user-defined flag \ is mapped to "/flags/\". /// /// \ follows the escape rules used by "quoted" string as described in /// Section 4.3 of \[RFC3501\]; e.g., for the flag \Seen, the corresponding \ /// is "/flags/\\seen", and for the flag $MDNSent, the corresponding \ /// is "/flags/$mdnsent". /// /// ```abnf /// entry-flag-name = DQUOTE "/flags/" attr-flag DQUOTE /// ``` pub(crate) fn entry_flag_name(input: &[u8]) -> IMAPResult<&[u8], AttributeFlag> { delimited(tag_no_case("\"/flags/"), attr_flag, char('"'))(input) } /// ```abnf /// attr-flag = "\\Answered" / /// "\\Flagged" / /// "\\Deleted" / /// "\\Seen" / /// "\\Draft" / /// attr-flag-keyword / /// attr-flag-extension /// ;; Does not include "\\Recent". /// ``` pub(crate) fn attr_flag(input: &[u8]) -> IMAPResult<&[u8], AttributeFlag> { alt(( map(preceded(tag("\\\\"), atom), AttributeFlag::system), map(atom, AttributeFlag::Keyword), ))(input) } // /// ```abnf // /// attr-flag-keyword = atom // /// ``` // #[inline] // pub(crate) fn attr_flag_keyword(input: &[u8]) -> IMAPResult<&[u8], Atom> { // atom(input) // } // /// Future expansion. // /// Client implementations MUST accept flag-extension flags. // /// Server implementations MUST NOT generate flag-extension flags, except as defined by future // /// standards or Standards Track revisions of [RFC3501]. // /// // /// ```abnf // /// attr-flag-extension = "\\" atom // /// ``` // pub(crate) fn attr_flag_extension(input: &[u8]) -> IMAPResult<&[u8], Atom> { // preceded(tag("\\\\"), atom)(input) // } /// ```abnf /// ;; Perform SEARCH operation on a private metadata item, /// ;; shared metadata item, or both. /// entry-type-req = entry-type-resp / "all" /// /// ;; Metadata item type. /// entry-type-resp = "priv" / "shared" /// ``` pub(crate) fn entry_type_req(input: &[u8]) -> IMAPResult<&[u8], EntryTypeReq> { alt(( value(EntryTypeReq::Private, tag_no_case("priv")), value(EntryTypeReq::Shared, tag_no_case("shared")), value(EntryTypeReq::All, tag_no_case("all")), ))(input) } #[cfg(test)] mod tests { use crate::response::resp_text; #[test] fn test_condstore_qresync_codes() { assert!(resp_text(b"[MODIFIED 7,9] Conditional STORE failed\r\n").is_ok()); assert!( resp_text(b"[NOMODSEQ] Sorry, this mailbox format doesn't support modsequences\r\n") .is_ok() ); assert!(resp_text(b"[HIGHESTMODSEQ 715194045007] Highest\r\n").is_ok()); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/enable.rs000066400000000000000000000066561507724125200240420ustar00rootroot00000000000000//! The IMAP ENABLE Extension // Additional changes: // // capability =/ "ENABLE" // command-any =/ "ENABLE" 1*(SP capability) // response-data =/ "*" SP enable-data CRLF use std::io::Write; use abnf_core::streaming::sp; use imap_types::{command::CommandBody, extensions::enable::CapabilityEnable, response::Data}; use nom::{ bytes::streaming::tag_no_case, combinator::map, multi::{many0, many1}, sequence::{preceded, tuple}, }; use crate::{ core::atom, decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, }; /// `command-any =/ "ENABLE" 1*(SP capability)` /// /// Note: /// /// Introduced into imap-codec as ... /// /// ```text /// enable = "ENABLE" 1*(SP capability) /// /// command-any =/ enable /// ``` pub(crate) fn enable(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case("ENABLE"), many1(preceded(sp, capability_enable)), )); let (remaining, (_, capabilities)) = parser(input)?; Ok(( remaining, CommandBody::Enable { capabilities: capabilities.try_into().unwrap(), }, )) } pub(crate) fn capability_enable(input: &[u8]) -> IMAPResult<&[u8], CapabilityEnable> { map(atom, CapabilityEnable::from)(input) } /// `enable-data = "ENABLED" *(SP capability)` pub(crate) fn enable_data(input: &[u8]) -> IMAPResult<&[u8], Data> { let mut parser = preceded( tag_no_case(b"ENABLED"), many0(preceded(sp, capability_enable)), ); let (remaining, capabilities) = parser(input)?; Ok((remaining, { Data::Enabled { capabilities } })) } impl EncodeIntoContext for CapabilityEnable<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "{self}") } } #[cfg(test)] mod tests { #[cfg(feature = "ext_utf8")] use imap_types::extensions::utf8::Utf8Kind; use imap_types::{command::Command, core::Atom, extensions::enable::CapabilityEnable}; use super::*; use crate::testing::kat_inverse_command; #[cfg(feature = "ext_utf8")] #[test] fn test_parse_enable() { let got = enable(b"enable UTF8=ACCEPT\r\n").unwrap().1; assert_eq!( CommandBody::enable(vec![CapabilityEnable::Utf8(Utf8Kind::Accept)]).unwrap(), got ); } #[test] fn test_kat_inverse_command_enable() { kat_inverse_command(&[ #[cfg(feature = "ext_utf8")] ( b"A ENABLE UTF8=ONLY\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::enable(vec![CapabilityEnable::Utf8(Utf8Kind::Only)]).unwrap(), ) .unwrap(), ), #[cfg(feature = "ext_utf8")] ( b"A ENABLE UTF8=ACCEPT\r\n?", b"?".as_ref(), Command::new( "A", CommandBody::enable(vec![CapabilityEnable::Utf8(Utf8Kind::Accept)]).unwrap(), ) .unwrap(), ), ( b"A ENABLE FOO\r\n??", b"??", Command::new( "A", CommandBody::enable(vec![CapabilityEnable::from( Atom::try_from("FOO").unwrap(), )]) .unwrap(), ) .unwrap(), ), ]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/id.rs000066400000000000000000000121051507724125200231720ustar00rootroot00000000000000//! IMAP4 ID extension // Additional changes: // // command_any ::= "CAPABILITY" / "LOGOUT" / "NOOP" / x_command / id // response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / mailbox_data / message_data / capability_data / id_response) use abnf_core::streaming::sp; use imap_types::core::{IString, NString}; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, value}, multi::separated_list0, sequence::{delimited, preceded, separated_pair}, }; #[cfg(feature = "quirk_trailing_space_id")] use nom::{combinator::opt, sequence::terminated}; use crate::{ core::{nil, nstring, string}, decode::IMAPResult, }; /// ```abnf /// id = "ID" SPACE id_params_list /// ``` /// /// Note: Updated ABNF. #[allow(clippy::type_complexity)] pub(crate) fn id(input: &[u8]) -> IMAPResult<&[u8], Option>> { preceded(tag_no_case("ID "), id_params_list)(input) } /// ```abnf /// id_response = "ID" SPACE id_params_list /// ``` /// /// Note: Updated ABNF. #[inline] #[allow(clippy::type_complexity)] pub(crate) fn id_response(input: &[u8]) -> IMAPResult<&[u8], Option>> { id(input) } /// ```abnf /// id-params-list = "(" [string SP nstring *(SP string SP nstring)] ")" / nil /// ``` /// /// Note: Updated ABNF. (See ) #[allow(clippy::type_complexity)] pub(crate) fn id_params_list(input: &[u8]) -> IMAPResult<&[u8], Option>> { alt(( map( #[cfg(not(feature = "quirk_trailing_space_id"))] delimited( tag("("), separated_list0(sp, separated_pair(string, sp, nstring)), tag(")"), ), #[cfg(feature = "quirk_trailing_space_id")] delimited( tag("("), terminated( separated_list0(sp, separated_pair(string, sp, nstring)), opt(sp), ), tag(")"), ), Some, ), value(None, nil), ))(input) } #[cfg(test)] mod tests { use imap_types::{ command::{Command, CommandBody}, core::{IString, NString}, response::{Data, Response}, }; use super::*; use crate::testing::{kat_inverse_command, kat_inverse_response}; #[test] fn test_parse_id() { let tests = [ ( b"id (\"name\" \"imap-codec\")\r\n".as_ref(), Some(vec![( IString::try_from("name").unwrap(), NString::try_from("imap-codec").unwrap(), )]), ), #[cfg(feature = "quirk_trailing_space_id")] ( b"id (\"name\" \"imap-codec\" )\r\n".as_ref(), Some(vec![( IString::try_from("name").unwrap(), NString::try_from("imap-codec").unwrap(), )]), ), ]; for (test, expected) in tests { let got = id(test).unwrap().1; assert_eq!(expected, got,); } } #[test] fn test_kat_inverse_command_id() { kat_inverse_command(&[ ( b"A ID nil\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::Id { parameters: None }).unwrap(), ), ( b"A ID NIL\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::Id { parameters: None }).unwrap(), ), #[cfg(not(feature = "quirk_id_empty_to_nil"))] ( b"A ID ()\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::Id { parameters: Some(vec![]), }, ) .unwrap(), ), ( b"A ID (\"\" \"\")\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::Id { parameters: Some(vec![( IString::try_from("").unwrap(), NString::try_from("").unwrap(), )]), }, ) .unwrap(), ), ( b"A ID (\"name\" \"imap-codec\")\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::Id { parameters: Some(vec![( IString::try_from("name").unwrap(), NString::try_from("imap-codec").unwrap(), )]), }, ) .unwrap(), ), ]); } #[test] fn test_kat_inverse_response_id() { kat_inverse_response(&[( b"* ID nil\r\n".as_ref(), b"".as_ref(), Response::Data(Data::Id { parameters: None }), )]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/idle.rs000066400000000000000000000067541507724125200235300ustar00rootroot00000000000000//! IMAP4 IDLE command //! //! This extension enables the [`CommandBody::Idle`](imap_types::command::CommandBody#variant.Idle) variant. //! No additional types are used. // Additional changes: // // command_auth =/ idle use std::io::Write; #[cfg(not(feature = "quirk_crlf_relaxed"))] use abnf_core::streaming::crlf; #[cfg(feature = "quirk_crlf_relaxed")] use abnf_core::streaming::crlf_relaxed as crlf; use imap_types::{command::CommandBody, extensions::idle::IdleDone}; use nom::{bytes::streaming::tag_no_case, combinator::value, sequence::tuple}; use crate::{ decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, }; /// `idle = "IDLE" CRLF "DONE"` (edited) /// /// ```text /// idle = "IDLE" CRLF "DONE" /// ^^^^^^^^^^^ /// | /// This is parsed here. /// CRLF is consumed in upper command parser. /// ``` /// /// Valid only in Authenticated or Selected state pub(crate) fn idle(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { value(CommandBody::Idle, tag_no_case("IDLE"))(input) } /// `idle = "IDLE" CRLF "DONE"` (edited) /// /// ```text /// idle = "IDLE" CRLF "DONE" CRLF /// ^^^^^^^^^^^ /// | /// This is parsed here. /// CRLF is additionally consumed in this parser. /// ``` /// /// Valid only in Authenticated or Selected state /// /// Note: This parser must be executed *instead* of the command parser /// when the server is in the IDLE state. pub(crate) fn idle_done(input: &[u8]) -> IMAPResult<&[u8], IdleDone> { value(IdleDone, tuple((tag_no_case("DONE"), crlf)))(input) } impl EncodeIntoContext for IdleDone { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(b"DONE\r\n") } } #[cfg(test)] mod tests { use imap_types::command::{Command, CommandBody}; use super::*; use crate::{ IdleDoneCodec, decode::{Decoder, IdleDoneDecodeError}, testing::kat_inverse_command, }; #[test] fn test_kat_inverse_command_idle() { kat_inverse_command(&[ ( b"A IDLE\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::Idle).unwrap(), ), ( b"A IDLE\r\n?", b"?", Command::new("A", CommandBody::Idle).unwrap(), ), ]); } #[test] fn test_decode_idle_done() { let tests = [ // Ok (b"done\r\n".as_ref(), Ok((b"".as_ref(), IdleDone))), (b"done\r\n?".as_ref(), Ok((b"?".as_ref(), IdleDone))), // Incomplete (b"d".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"do".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"don".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"done".as_ref(), Err(IdleDoneDecodeError::Incomplete)), (b"done\r".as_ref(), Err(IdleDoneDecodeError::Incomplete)), // Failed (b"donee\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), (b" done\r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), (b"done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), (b" done \r\n".as_ref(), Err(IdleDoneDecodeError::Failed)), ]; for (test, expected) in tests { let got = IdleDoneCodec::default().decode(test); dbg!((std::str::from_utf8(test).unwrap(), &expected, &got)); assert_eq!(expected, got); } } } duesee-imap-codec-0d00966/imap-codec/src/extensions/literal.rs000066400000000000000000000054601507724125200242400ustar00rootroot00000000000000#[cfg(test)] mod tests { use imap_types::{ command::{Command, CommandBody}, core::{Literal, Vec1}, response::{Capability, Code, Greeting}, }; use crate::testing::{kat_inverse_command, kat_inverse_greeting}; #[test] fn test_kat_inverse_command_login_literal_plus() { kat_inverse_command(&[ ( b"A LOGIN {0}\r\n {1}\r\nA\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::login( Literal::try_from("").unwrap(), Literal::try_from("A").unwrap(), ) .unwrap(), ) .unwrap(), ), ( b"A LOGIN {1}\r\nA {2}\r\nAB\r\n?".as_ref(), b"?".as_ref(), Command::new( "A", CommandBody::login( Literal::try_from("A").unwrap(), Literal::try_from("AB").unwrap(), ) .unwrap(), ) .unwrap(), ), ( b"A LOGIN {0+}\r\n {1+}\r\nA\r\n??".as_ref(), b"??".as_ref(), Command::new( "A", CommandBody::login( Literal::try_from("").unwrap().into_non_sync(), Literal::try_from("A").unwrap().into_non_sync(), ) .unwrap(), ) .unwrap(), ), ( b"A LOGIN {1+}\r\nA {2+}\r\nAB\r\n???".as_ref(), b"???".as_ref(), Command::new( "A", CommandBody::login( Literal::try_from("A").unwrap().into_non_sync(), Literal::try_from("AB").unwrap().into_non_sync(), ) .unwrap(), ) .unwrap(), ), ]); } #[test] fn test_kat_inverse_greeting_capability_literal_plus() { kat_inverse_greeting(&[ ( b"* OK [CAPABILITY LITERAL+] ...\r\n".as_ref(), b"".as_ref(), Greeting::ok( Some(Code::Capability(Vec1::from(Capability::LiteralPlus))), "...", ) .unwrap(), ), ( b"* OK [CAPABILITY LITERAL-] ...\r\n?", b"?", Greeting::ok( Some(Code::Capability(Vec1::from(Capability::LiteralMinus))), "...", ) .unwrap(), ), ]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/metadata.rs000066400000000000000000000461611507724125200243670ustar00rootroot00000000000000//! The IMAP METADATA Extension use std::io::Write; use abnf_core::streaming::sp; use imap_types::{ command::CommandBody, core::{NString8, Vec1}, extensions::metadata::{ Depth, Entry, EntryValue, GetMetadataOption, MetadataCode, MetadataResponse, }, response::Data, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, opt, value}, error::ErrorKind, multi::separated_list1, sequence::{delimited, preceded, separated_pair, tuple}, }; use crate::{ core::{astring, nstring, number}, decode::{IMAPErrorKind, IMAPParseError, IMAPResult}, encode::{EncodeContext, EncodeIntoContext, utils::join_serializable}, extensions::binary::literal8, mailbox::mailbox, }; // ----- Command ----- /// ```abnf /// ; empty string for mailbox implies server annotation. /// setmetadata = "SETMETADATA" SP mailbox SP entry-values /// ``` pub(crate) fn setmetadata(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case("SETMETADATA"), preceded(sp, mailbox), preceded(sp, entry_values), )); let (rem, (_, mailbox, entry_values)) = parser(input)?; Ok(( rem, CommandBody::SetMetadata { mailbox, entry_values, }, )) } /// ```abnf /// entry-values = "(" entry-value *(SP entry-value) ")" /// ``` pub(crate) fn entry_values(input: &[u8]) -> IMAPResult<&[u8], Vec1> { map( delimited(tag("("), separated_list1(sp, entry_value), tag(")")), Vec1::unvalidated, )(input) } /// ```abnf /// entry-value = entry SP value /// ``` #[inline] pub(crate) fn entry_value(input: &[u8]) -> IMAPResult<&[u8], EntryValue> { map(separated_pair(entry, sp, imap_value), |(entry, value)| { EntryValue { entry, value } })(input) } /// Slash-separated path to entry. /// /// Note: MUST NOT contain "*" or "%". /// /// ```abnf /// entry = astring /// ``` pub(crate) fn entry(input: &[u8]) -> IMAPResult<&[u8], Entry> { let (rem, astring) = astring(input)?; if let Ok(entry) = Entry::try_from(astring) { Ok((rem, entry)) } else { Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::Nom(ErrorKind::Verify), })) } } /// ```abnf /// value = nstring / literal8 /// ``` #[inline] pub(crate) fn imap_value(input: &[u8]) -> IMAPResult<&[u8], NString8> { alt(( map(nstring, NString8::NString), map(literal8, NString8::Literal8), ))(input) } /// ```abnf /// getmetadata = "GETMETADATA" [SP getmetadata-options] SP mailbox SP entries /// ``` /// /// Note: Empty string for mailbox implies server annotation. pub(crate) fn getmetadata(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case("GETMETADATA"), opt(preceded(sp, getmetadata_options)), preceded(sp, mailbox), preceded(sp, entries), )); let (rem, (_, options, mailbox, entries)) = parser(input)?; Ok(( rem, CommandBody::GetMetadata { options: options.map(|x| x.into_inner()).unwrap_or_default(), mailbox, entries, }, )) } /// ```abnf /// getmetadata-options = "(" getmetadata-option *(SP getmetadata-option) ")" /// ``` pub(crate) fn getmetadata_options(input: &[u8]) -> IMAPResult<&[u8], Vec1> { map( delimited(tag("("), separated_list1(sp, getmetadata_option), tag(")")), Vec1::unvalidated, )(input) } /// ```abnf /// ; tagged-ext-label and tagged-ext-val are defined in [RFC4466]. /// getmetadata-option = tagged-ext-label [SP tagged-ext-val] /// /// ; Used as a getmetadata-option /// maxsize-opt = "MAXSIZE" SP number /// /// ; Used as a getmetadata-option /// scope-opt = "DEPTH" SP ("0" / "1" / "infinity") /// ``` pub(crate) fn getmetadata_option(input: &[u8]) -> IMAPResult<&[u8], GetMetadataOption> { // TODO: Generic parser required? alt(( map( preceded(tag_no_case("MAXSIZE "), number), GetMetadataOption::MaxSize, ), map( preceded( tag_no_case("DEPTH "), alt(( value(Depth::Null, tag("0")), value(Depth::One, tag("1")), value(Depth::Infinity, tag_no_case("infinity")), )), ), GetMetadataOption::Depth, ), ))(input) } /// ```abnf /// entries = entry / "(" entry *(SP entry) ")" /// ``` pub(crate) fn entries(input: &[u8]) -> IMAPResult<&[u8], Vec1> { alt(( map(entry, Vec1::from), map( delimited(tag("("), separated_list1(sp, entry), tag(")")), Vec1::unvalidated, ), ))(input) } // ----- Response ----- /// ```abnf /// response-payload =/ metadata-resp /// /// ; empty string for mailbox implies server annotation. /// metadata-resp = "METADATA" SP mailbox SP (entry-values / entry-list) /// /// entry-list = entry *(SP entry) /// ``` pub(crate) fn metadata_resp(input: &[u8]) -> IMAPResult<&[u8], Data> { let mut parser = tuple(( tag_no_case("METADATA"), preceded(sp, mailbox), preceded( sp, alt(( map(entry_values, MetadataResponse::WithValues), map(entry_list, MetadataResponse::WithoutValues), )), ), )); let (rem, (_, mailbox, items)) = parser(input)?; Ok((rem, Data::Metadata { mailbox, items })) } /// ```abnf /// entry-list = entry *(SP entry) /// ``` pub(crate) fn entry_list(input: &[u8]) -> IMAPResult<&[u8], Vec1> { map(separated_list1(sp, entry), Vec1::unvalidated)(input) } impl EncodeIntoContext for MetadataResponse<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { MetadataResponse::WithValues(list) => { ctx.write_all(b"(")?; join_serializable(list.as_ref(), b" ", ctx)?; ctx.write_all(b")") } MetadataResponse::WithoutValues(list) => join_serializable(list.as_ref(), b" ", ctx), } } } /// ```abnf /// "LONGENTRIES" SP number / /// "MAXSIZE" SP number / /// "TOOMANY" / /// "NOPRIVATE" /// ``` pub(crate) fn metadata_code(input: &[u8]) -> IMAPResult<&[u8], MetadataCode> { alt(( map( preceded(tag_no_case("LONGENTRIES "), number), MetadataCode::LongEntries, ), map( preceded(tag_no_case("MAXSIZE "), number), MetadataCode::MaxSize, ), value(MetadataCode::TooMany, tag_no_case("TOOMANY")), value(MetadataCode::NoPrivate, tag_no_case("NOPRIVATE")), ))(input) } impl EncodeIntoContext for MetadataCode { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { MetadataCode::LongEntries(number) => { ctx.write_all(b"LONGENTRIES ")?; number.encode_ctx(ctx) } MetadataCode::MaxSize(number) => { ctx.write_all(b"MAXSIZE ")?; number.encode_ctx(ctx) } MetadataCode::TooMany => ctx.write_all(b"TOOMANY"), MetadataCode::NoPrivate => ctx.write_all(b"NOPRIVATE"), } } } impl EncodeIntoContext for Entry<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.inner().encode_ctx(ctx) } } impl EncodeIntoContext for GetMetadataOption { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { GetMetadataOption::MaxSize(number) => { ctx.write_all(b"MAXSIZE ")?; number.encode_ctx(ctx) } GetMetadataOption::Depth(depth) => { ctx.write_all(b"DEPTH ")?; depth.encode_ctx(ctx) } } } } impl EncodeIntoContext for Depth { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(match self { Depth::Null => b"0", Depth::One => b"1", Depth::Infinity => b"INFINITY", }) } } impl EncodeIntoContext for EntryValue<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.entry.encode_ctx(ctx)?; ctx.write_all(b" ")?; self.value.encode_ctx(ctx) } } #[cfg(test)] mod tests { use imap_types::{ command::{Command, CommandBody}, core::{AString, IString, Literal, LiteralMode, NString, NString8, Text, Vec1}, extensions::{ binary::Literal8, metadata::{ Depth, Entry, EntryValue, GetMetadataOption, MetadataCode, MetadataResponse, }, }, mailbox::{Mailbox, MailboxOther}, response::{Code, Data, Response, Status, StatusBody, StatusKind}, }; use crate::testing::{kat_inverse_command, kat_inverse_response}; #[test] fn test_kat_inverse_command_setmetadata() { kat_inverse_command(&[ ( b"A SETMETADATA \"\" (/test nil)\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::SetMetadata { mailbox: Mailbox::Other(MailboxOther::try_from("").unwrap()), entry_values: Vec1::try_from(vec![EntryValue { entry: Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), value: NString8::NString(NString(None)), }]) .unwrap(), }, ) .unwrap(), ), ( b"A SETMETADATA \"\" (/test \"test\")\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::SetMetadata { mailbox: Mailbox::Other(MailboxOther::try_from("").unwrap()), entry_values: Vec1::try_from(vec![EntryValue { entry: Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), value: NString8::NString(NString(Some( IString::try_from("test").unwrap(), ))), }]) .unwrap(), }, ) .unwrap(), ), ( b"A SETMETADATA \"\" (/test ~{4+}\r\nt\x00st)\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::SetMetadata { mailbox: Mailbox::Other(MailboxOther::try_from("").unwrap()), entry_values: Vec1::try_from(vec![EntryValue { entry: Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), value: NString8::Literal8(Literal8 { data: b"t\x00st".as_ref().into(), mode: LiteralMode::NonSync, }), }]) .unwrap(), }, ) .unwrap(), ), ]); } #[test] fn test_kat_inverse_command_getmetadata() { kat_inverse_command(&[ ( b"A GETMETADATA \"\" /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![], mailbox: Mailbox::Other(MailboxOther::try_from("").unwrap()), entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ( b"A GETMETADATA INBOX /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![], mailbox: Mailbox::Inbox, entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ( b"A GETMETADATA (MAXSIZE 0) INBOX /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![GetMetadataOption::MaxSize(0)], mailbox: Mailbox::Inbox, entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ( b"A GETMETADATA (MAXSIZE 1337) INBOX /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![GetMetadataOption::MaxSize(1337)], mailbox: Mailbox::Inbox, entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ( b"A GETMETADATA (DEPTH 0) INBOX /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![GetMetadataOption::Depth(Depth::Null)], mailbox: Mailbox::Inbox, entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ( b"A GETMETADATA (DEPTH 1) INBOX /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![GetMetadataOption::Depth(Depth::One)], mailbox: Mailbox::Inbox, entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ( b"A GETMETADATA (DEPTH infinity) INBOX /test\r\n".as_ref(), b"".as_ref(), Command::new( "A", CommandBody::GetMetadata { options: vec![GetMetadataOption::Depth(Depth::Infinity)], mailbox: Mailbox::Inbox, entries: Vec1::from( Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), ), }, ) .unwrap(), ), ]); } #[test] fn test_kat_inverse_response_metadata() { kat_inverse_response(&[ ( b"* metadata INBOX /test /xxx\r\n".as_ref(), b"".as_ref(), Response::Data(Data::Metadata { mailbox: Mailbox::Inbox, items: MetadataResponse::WithoutValues( Vec1::try_from(vec![ Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), Entry::try_from(AString::try_from("/xxx").unwrap()).unwrap(), ]) .unwrap(), ), }), ), ( b"* metadata INBOX (/test {4}\r\nABCD)\r\n".as_ref(), b"".as_ref(), Response::Data(Data::Metadata { mailbox: Mailbox::Inbox, items: MetadataResponse::WithValues( Vec1::try_from(vec![EntryValue { entry: Entry::try_from(AString::try_from("/test").unwrap()).unwrap(), value: NString8::NString(NString(Some(IString::Literal( Literal::try_from("ABCD").unwrap(), )))), }]) .unwrap(), ), }), ), ]); } #[test] fn test_kat_inverse_response_metadata_code() { kat_inverse_response(&[ ( b"* OK [metadata longentries 0] ...\r\n".as_ref(), b"".as_ref(), Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::Metadata(MetadataCode::LongEntries(0))), text: Text::try_from("...").unwrap(), })), ), ( b"* OK [metadata longentries 4294967295] ...\r\n".as_ref(), b"".as_ref(), Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::Metadata(MetadataCode::LongEntries(u32::MAX))), text: Text::try_from("...").unwrap(), })), ), ( b"* OK [metadatA maxSIZE 0] ...\r\n".as_ref(), b"".as_ref(), Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::Metadata(MetadataCode::MaxSize(0))), text: Text::try_from("...").unwrap(), })), ), ( b"* OK [metadata maxSizE 4294967295] ...\r\n".as_ref(), b"".as_ref(), Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::Metadata(MetadataCode::MaxSize(u32::MAX))), text: Text::try_from("...").unwrap(), })), ), ( b"* OK [metadata toOmaNy] ...\r\n".as_ref(), b"".as_ref(), Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::Metadata(MetadataCode::TooMany)), text: Text::try_from("...").unwrap(), })), ), ( b"* OK [mEtadata noPRIvaTE] ...\r\n".as_ref(), b"".as_ref(), Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::Metadata(MetadataCode::NoPrivate)), text: Text::try_from("...").unwrap(), })), ), ]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/move.rs000066400000000000000000000027121507724125200235470ustar00rootroot00000000000000//! IMAP - MOVE Extension use abnf_core::streaming::sp; use imap_types::command::CommandBody; use nom::{bytes::streaming::tag_no_case, sequence::tuple}; use crate::{decode::IMAPResult, mailbox::mailbox, sequence::sequence_set}; /// ```abnf /// move = "MOVE" SP sequence-set SP mailbox /// ``` pub(crate) fn r#move(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case(b"MOVE"), sp, sequence_set, sp, mailbox)); let (remaining, (_, _, sequence_set, _, mailbox)) = parser(input)?; Ok(( remaining, CommandBody::Move { sequence_set, mailbox, uid: false, }, )) } #[cfg(test)] mod tests { use imap_types::command::{Command, CommandBody}; use crate::testing::kat_inverse_command; #[test] fn test_kat_inverse_command_move() { kat_inverse_command(&[ ( b"A MOVE 1 INBOX\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::r#move("1", "inBox", false).unwrap()).unwrap(), ), ( b"A UID MOVE 1 INBOX\r\n?", b"?", Command::new("A", CommandBody::r#move("1", "inBox", true).unwrap()).unwrap(), ), ( b"A MOVE 1:* test\r\n??", b"??", Command::new("A", CommandBody::r#move("1:*", "test", false).unwrap()).unwrap(), ), ]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/namespace.rs000066400000000000000000000117701507724125200245410ustar00rootroot00000000000000//! The IMAP NAMESPACE Extension use std::io::Write; use abnf_core::streaming::{dquote, sp}; use imap_types::{ command::CommandBody, core::Vec1, extensions::namespace::{Namespace, NamespaceResponseExtension, Namespaces}, response::Data, }; use nom::{ branch::alt, bytes::streaming::tag_no_case, character::streaming::char, combinator::{map, value}, multi::{many0, many1, separated_list1}, sequence::{delimited, preceded, tuple}, }; use crate::{ core::{nil, quoted_char, string}, decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, }; /// ```abnf /// namespace = "NAMESPACE" /// ``` pub(crate) fn namespace_command(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { value(CommandBody::Namespace, tag_no_case(b"NAMESPACE"))(input) } /// ```abnf /// ;; The first Namespace is the Personal Namespace(s) /// ;; The second Namespace is the Other Users' Namespace(s) /// ;; The third Namespace is the Shared Namespace(s) /// Namespace-Response = "NAMESPACE" SP Namespace SP Namespace SP Namespace /// ``` pub(crate) fn namespace_response(input: &[u8]) -> IMAPResult<&[u8], Data> { let mut parser = tuple(( tag_no_case("NAMESPACE "), namespace, preceded(sp, namespace), preceded(sp, namespace), )); let (remaining, (_, personal, other, shared)) = parser(input)?; Ok(( remaining, Data::Namespace { personal, other, shared, }, )) } /// ```abnf /// Namespace = nil / "(" 1*Namespace-Descr ")" /// ``` fn namespace(input: &[u8]) -> IMAPResult<&[u8], Namespaces> { alt(( map(nil, |_| Vec::new()), delimited(char('('), many1(namespace_descr), char(')')), ))(input) } /// ```abnf /// Namespace-Descr = "(" /// string /// SP /// (DQUOTE QUOTED-CHAR DQUOTE / nil) /// *(Namespace-Response-Extension) /// ")" /// ``` fn namespace_descr(input: &[u8]) -> IMAPResult<&[u8], Namespace> { map( delimited( char('('), tuple(( string, sp, alt(( map(delimited(dquote, quoted_char, dquote), Some), value(None, nil), )), many0(namespace_response_extension), )), char(')'), ), |(prefix, _, delimiter, extensions)| Namespace { prefix, delimiter, extensions, }, )(input) } /// ```abnf /// Namespace-Response-Extension = SP string SP "(" string *(SP string) ")" /// ``` fn namespace_response_extension(input: &[u8]) -> IMAPResult<&[u8], NamespaceResponseExtension> { map( tuple(( preceded(sp, string), preceded( sp, delimited(char('('), separated_list1(sp, string), char(')')), ), )), |(key, values)| NamespaceResponseExtension { key, // We can use `unvalidated` because we know the vector has at least one element due to the `separated_list1` call above. values: Vec1::unvalidated(values), }, )(input) } impl EncodeIntoContext for Namespace<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "(")?; self.prefix.encode_ctx(ctx)?; write!(ctx, " ")?; match &self.delimiter { Some(delimiter_char) => { write!(ctx, "\"")?; delimiter_char.encode_ctx(ctx)?; write!(ctx, "\"")?; } None => { ctx.write_all(b"NIL")?; } } for ext in &self.extensions { ext.encode_ctx(ctx)?; } write!(ctx, ")") } } impl EncodeIntoContext for NamespaceResponseExtension<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, " ")?; self.key.encode_ctx(ctx)?; write!(ctx, " (")?; if let Some((last, head)) = self.values.as_ref().split_last() { for value in head { value.encode_ctx(ctx)?; write!(ctx, " ")?; } last.encode_ctx(ctx)?; } write!(ctx, ")") } } pub fn encode_namespaces(ctx: &mut EncodeContext, list: &Namespaces<'_>) -> std::io::Result<()> { if list.is_empty() { ctx.write_all(b"NIL") } else { ctx.write_all(b"(")?; for desc in list { desc.encode_ctx(ctx)?; } ctx.write_all(b")") } } #[cfg(test)] mod tests { use super::namespace_response; #[test] fn parse_namespace_response() { let tests = [ b"NAMESPACE ((\"0\" \"\\\"\")) NIL NIL\r\n".as_ref(), #[cfg(feature = "ext_utf8")] b"NAMESPACE ((\"^^\x00\" \"\x07\")) NIL NIL\r\n", ]; for test in tests.into_iter() { namespace_response(test).unwrap(); } } } duesee-imap-codec-0d00966/imap-codec/src/extensions/quota.rs000066400000000000000000000502151507724125200237330ustar00rootroot00000000000000//! IMAP QUOTA Extension use std::io::Write; use abnf_core::streaming::sp; use imap_types::{ command::CommandBody, core::{AString, Vec1}, extensions::quota::{QuotaGet, QuotaSet, Resource}, response::Data, }; #[cfg(feature = "quirk_excessive_space_quota_resource")] use nom::multi::many1; use nom::{ bytes::streaming::{tag, tag_no_case}, combinator::map, multi::{many0, separated_list0, separated_list1}, sequence::{delimited, preceded, tuple}, }; use crate::{ core::{astring, atom, number64}, decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, mailbox::mailbox, }; /// ```abnf /// quota-root-name = astring /// ``` #[inline] pub(crate) fn quota_root_name(input: &[u8]) -> IMAPResult<&[u8], AString> { astring(input) } /// ```abnf /// getquota = "GETQUOTA" SP quota-root-name /// ``` #[inline] pub(crate) fn getquota(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case("GETQUOTA "), quota_root_name)); let (remaining, (_, root)) = parser(input)?; Ok((remaining, CommandBody::GetQuota { root })) } /// ```abnf /// getquotaroot = "GETQUOTAROOT" SP mailbox /// ``` pub(crate) fn getquotaroot(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple((tag_no_case("GETQUOTAROOT "), mailbox)); let (remaining, (_, mailbox)) = parser(input)?; Ok((remaining, CommandBody::GetQuotaRoot { mailbox })) } /// ```abnf /// quota-resource = resource-name SP /// resource-usage SP /// resource-limit /// /// resource-usage = number64 /// /// resource-limit = number64 /// ``` pub(crate) fn quota_resource(input: &[u8]) -> IMAPResult<&[u8], QuotaGet> { #[cfg(not(feature = "quirk_excessive_space_quota_resource"))] let mut parser = tuple((resource_name, sp, number64, sp, number64)); #[cfg(feature = "quirk_excessive_space_quota_resource")] let mut parser = tuple((resource_name, many1(sp), number64, many1(sp), number64)); let (remaining, (resource, _, usage, _, limit)) = parser(input)?; Ok(( remaining, QuotaGet { resource, usage, limit, }, )) } /// ```abnf /// resource-name = "STORAGE" / /// "MESSAGE" / /// "MAILBOX" / /// "ANNOTATION-STORAGE" / /// resource-name-ext /// /// resource-name-ext = atom /// ``` pub(crate) fn resource_name(input: &[u8]) -> IMAPResult<&[u8], Resource> { map(atom, Resource::from)(input) } /// ```abnf /// quota-response = "QUOTA" SP quota-root-name SP quota-list /// /// quota-list = "(" quota-resource *(SP quota-resource) ")" /// ``` pub(crate) fn quota_response(input: &[u8]) -> IMAPResult<&[u8], Data> { let mut parser = tuple(( tag_no_case("QUOTA "), quota_root_name, delimited(tag(" ("), separated_list1(sp, quota_resource), tag(")")), )); let (remaining, (_, root, quotas)) = parser(input)?; Ok(( remaining, Data::Quota { root, // Safety: Safe because we use `separated_list1` above. quotas: Vec1::try_from(quotas).unwrap(), }, )) } /// ```abnf /// quotaroot-response = "QUOTAROOT" SP mailbox *(SP quota-root-name) /// ``` pub(crate) fn quotaroot_response(input: &[u8]) -> IMAPResult<&[u8], Data> { let mut parser = tuple(( tag_no_case("QUOTAROOT "), mailbox, many0(preceded(sp, quota_root_name)), )); let (remaining, (_, mailbox, roots)) = parser(input)?; Ok((remaining, Data::QuotaRoot { mailbox, roots })) } /// ```abnf /// setquota = "SETQUOTA" SP quota-root-name SP setquota-list /// /// setquota-list = "(" [setquota-resource *(SP setquota-resource)] ")" /// ``` pub(crate) fn setquota(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case("SETQUOTA "), quota_root_name, delimited(tag(" ("), separated_list0(sp, setquota_resource), tag(")")), )); let (remaining, (_, root, quotas)) = parser(input)?; Ok((remaining, CommandBody::SetQuota { root, quotas })) } /// ```abnf /// setquota-resource = resource-name SP resource-limit /// ``` pub(crate) fn setquota_resource(input: &[u8]) -> IMAPResult<&[u8], QuotaSet> { let mut parser = tuple((resource_name, sp, number64)); let (remaining, (resource, _, limit)) = parser(input)?; Ok((remaining, QuotaSet { resource, limit })) } // This had to be inlined into the `capability` parser because `CapabilityOther("QUOTAFOO")` would // be parsed as `Capability::Quota` plus an erroneous remainder. The `capability` parser eagerly consumes // an `atom` and tries to detect the variants later. // /// ```abnf // /// capability-quota = "QUOTASET" / capa-quota-res // /// ``` // /// // /// Note: Extended to ... // /// // /// ```abnf // /// capability-quota = "QUOTASET" / capa-quota-res / "QUOTA" // /// ``` // pub(crate) fn capability_quota(input: &[u8]) -> IMAPResult<&[u8], Capability> { // alt(( // value(Capability::QuotaSet, tag_no_case("QUOTASET")), // capa_quota_res, // value(Capability::Quota, tag_no_case("QUOTA")), // ))(input) // } // /// ```abnf // /// capa-quota-res = "QUOTA=RES-" resource-name // /// ``` // pub(crate) fn capa_quota_res(input: &[u8]) -> IMAPResult<&[u8], Capability> { // let mut parser = preceded(tag_no_case("QUOTA=RES-"), resource_name); // // let (remaining, resource) = parser(input)?; // // Ok((remaining, Capability::QuotaRes(resource))) // } impl EncodeIntoContext for Resource<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.to_string().as_bytes()) } } impl EncodeIntoContext for QuotaGet<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.resource.encode_ctx(ctx)?; write!(ctx, " {} {}", self.usage, self.limit) } } impl EncodeIntoContext for QuotaSet<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { self.resource.encode_ctx(ctx)?; write!(ctx, " {}", self.limit) } } #[cfg(test)] mod tests { use imap_types::{ command::{Command, CommandBody}, core::{IString, Tag}, extensions::quota::{QuotaGet, QuotaSet, Resource}, mailbox::Mailbox, response::{Capability, Code, Response, Status}, status::{StatusDataItem, StatusDataItemName}, }; use super::*; use crate::testing::{kat_inverse_command, kat_inverse_response}; #[test] fn test_parse_resource_name() { let tests = [ (b"stOragE ".as_ref(), Resource::Storage), (b"mesSaGe ".as_ref(), Resource::Message), (b"maIlbOx ".as_ref(), Resource::Mailbox), (b"anNotatIon-stoRage ".as_ref(), Resource::AnnotationStorage), ( b"anNotatIon-stoRageX ".as_ref(), Resource::try_from(b"anNotatIon-stoRageX".as_ref()).unwrap(), ), ( b"anNotatIon-stoRagee ".as_ref(), Resource::try_from(b"anNotatIon-stoRagee".as_ref()).unwrap(), ), ]; for (test, expected) in tests.iter() { let (rem, got) = resource_name(test).unwrap(); assert_eq!(*expected, got); assert_eq!(rem, b" "); } } #[test] fn test_kat_inverse_command_get_quota() { kat_inverse_command(&[ ( b"A GETQUOTA INBOX\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::get_quota("INBOX").unwrap()).unwrap(), ), ( b"A GETQUOTA \"\"\r\n?", b"?", Command::new("A", CommandBody::get_quota("").unwrap()).unwrap(), ), ( b"A003 GETQUOTA \"\"\r\n", b"", CommandBody::get_quota("").unwrap().tag("A003").unwrap(), ), ( b"G0001 GETQUOTA \"!partition/sda4\"\r\n", b"", CommandBody::get_quota(AString::String( IString::try_from("!partition/sda4").unwrap(), )) .unwrap() .tag("G0001") .unwrap(), ), ( b"S0000 GETQUOTA \"#user/alice\"\r\n", b"", CommandBody::get_quota(AString::String(IString::try_from("#user/alice").unwrap())) .unwrap() .tag("S0000") .unwrap(), ), ]); } #[test] fn test_kat_inverse_command_get_quota_root() { kat_inverse_command(&[ ( b"A003 GETQUOTAROOT INBOX\r\n".as_ref(), b"".as_ref(), CommandBody::get_quota_root(Mailbox::Inbox) .unwrap() .tag("A003") .unwrap(), ), ( b"A GETQUOTAROOT MAILBOX\r\n??", b"??", Command::new("A", CommandBody::get_quota_root("MAILBOX").unwrap()).unwrap(), ), ( b"G0002 GETQUOTAROOT INBOX\r\n", b"", CommandBody::get_quota_root("inbox") .unwrap() .tag("G0002") .unwrap(), ), ]); } #[test] fn test_kat_inverse_command_set_quota() { kat_inverse_command(&[ ( b"A SETQUOTA INBOX ()\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::set_quota("INBOX", vec![]).unwrap()).unwrap(), ), ( b"A SETQUOTA INBOX (STORAGE 256)\r\n", b"", Command::new("A", CommandBody::set_quota( "INBOX", vec![QuotaSet { resource: Resource::Storage, limit: 256, }], ) .unwrap()).unwrap(), ), ( b"A SETQUOTA INBOX (STORAGE 0 MESSAGE 512 MAILBOX 512 ANNOTATION-STORAGE 123 Foo 18446744073709551615)\r\n", b"", Command::new("A", CommandBody::set_quota( "INBOX", vec![ QuotaSet { resource: Resource::Storage, limit: 0, }, QuotaSet { resource: Resource::Message, limit: 512, }, QuotaSet { resource: Resource::Mailbox, limit: 512, }, QuotaSet { resource: Resource::AnnotationStorage, limit: 123, }, QuotaSet { resource: Resource::try_from("Foo").unwrap(), limit: u64::MAX, }, ], ) .unwrap()).unwrap(), ), ( b"S0001 SETQUOTA \"#user/alice\" (STORAGE 510)\r\n", b"", CommandBody::set_quota( AString::String(IString::try_from("#user/alice").unwrap()), vec![QuotaSet::new(Resource::Storage, 510)], ) .unwrap() .tag("S0001") .unwrap(), ), ( b"S0002 SETQUOTA \"!partition/sda4\" (STORAGE 99999999)\r\n", b"", CommandBody::set_quota( AString::String(IString::try_from("!partition/sda4").unwrap()), vec![QuotaSet::new(Resource::Storage, 99999999)], ) .unwrap() .tag("S0002") .unwrap(), ), ( b"A001 SETQUOTA \"\" (STORAGE 512)\r\n", b"", CommandBody::set_quota("", vec![QuotaSet::new(Resource::Storage, 512)]) .unwrap() .tag("A001") .unwrap(), ), ]); } #[test] fn test_kat_inverse_command_status_quota() { kat_inverse_command(&[( b"S0003 STATUS INBOX (MESSAGES DELETED DELETED-STORAGE)\r\n", b"", CommandBody::status( "inbox", vec![ StatusDataItemName::Messages, StatusDataItemName::Deleted, StatusDataItemName::DeletedStorage, ], ) .unwrap() .tag("S0003") .unwrap(), )]); } #[test] fn test_kat_inverse_response_data_quota() { kat_inverse_response(&[ ( b"* QUOTA INBOX (MESSAGE 1024 2048)\r\n".as_ref(), b"".as_ref(), Response::Data( Data::quota( "INBOX", vec![QuotaGet { resource: Resource::Message, usage: 1024, limit: 2048, }], ) .unwrap(), ), ), ( b"* QUOTAROOT INBOX\r\n", b"", Response::Data(Data::quota_root("INBOX", vec![]).unwrap()), ), ( b"* QUOTAROOT INBOX ROOT1 ROOT2\r\n", b"", Response::Data( Data::quota_root( "INBOX", vec!["ROOT1".try_into().unwrap(), "ROOT2".try_into().unwrap()], ) .unwrap(), ), ), ( b"* CAPABILITY QUOTA QUOTA=RES-STORAGE\r\n", b"", Response::Data( Data::capability(vec![ Capability::Quota, Capability::QuotaRes(Resource::Storage), ]) .unwrap(), ), ), ( b"* CAPABILITY QUOTA QUOTA=RES-STORAGE QUOTA=RES-MESSAGE\r\n", b"", Response::Data( Data::capability(vec![ Capability::Quota, Capability::QuotaRes(Resource::Storage), Capability::QuotaRes(Resource::Message), ]) .unwrap(), ), ), ( b"* CAPABILITY QUOTA QUOTASET QUOTA=RES-STORAGE QUOTA=RES-MESSAGE\r\n", b"", Response::Data( Data::capability(vec![ Capability::Quota, Capability::QuotaSet, Capability::QuotaRes(Resource::Storage), Capability::QuotaRes(Resource::Message), ]) .unwrap(), ), ), ( b"* QUOTA \"!partition/sda4\" (STORAGE 104 10923847)\r\n", b"", Response::Data( Data::quota( AString::String(IString::try_from("!partition/sda4").unwrap()), vec![QuotaGet::new(Resource::Storage, 104, 10923847)], ) .unwrap(), ), ), ( b"* QUOTA \"\" (STORAGE 10 512)\r\n", b"", Response::Data(Data::Quota { root: "".try_into().unwrap(), quotas: vec![QuotaGet::new(Resource::Storage, 10, 512)] .try_into() .unwrap(), }), ), ( b"* QUOTA \"#user/alice\" (MESSAGE 42 1000)\r\n", b"", Response::Data(Data::Quota { root: AString::String(IString::try_from("#user/alice").unwrap()), quotas: vec![QuotaGet::new(Resource::Message, 42, 1000)] .try_into() .unwrap(), }), ), ( b"* QUOTA \"#user/alice\" (STORAGE 54 111 MESSAGE 42 1000)\r\n", b"", Response::Data( Data::quota( AString::String(IString::try_from("#user/alice").unwrap()), vec![ QuotaGet::new(Resource::Storage, 54, 111), QuotaGet::new(Resource::Message, 42, 1000), ], ) .unwrap(), ), ), #[cfg(feature = "quirk_excessive_space_quota_resource")] ( b"* QUOTA \"#user/alice\" (STORAGE 54 111 MESSAGE 42 1000)\r\n", b"", Response::Data( Data::quota( AString::String(IString::try_from("#user/alice").unwrap()), vec![ QuotaGet::new(Resource::Storage, 54, 111), QuotaGet::new(Resource::Message, 42, 1000), ], ) .unwrap(), ), ), ( b"* QUOTA \"#user/alice\" (STORAGE 58 512)\r\n", b"", Response::Data( Data::quota( AString::String(IString::try_from("#user/alice").unwrap()), vec![QuotaGet::new(Resource::Storage, 58, 512)], ) .unwrap(), ), ), ( b"* QUOTAROOT INBOX \"\"\r\n", b"", Response::Data(Data::QuotaRoot { mailbox: Mailbox::Inbox, roots: vec!["".try_into().unwrap()], }), ), ( b"* QUOTAROOT comp.mail.mime\r\n", b"", Response::Data(Data::QuotaRoot { mailbox: Mailbox::try_from("comp.mail.mime").unwrap(), roots: vec![], }), ), ( b"* QUOTAROOT INBOX \"#user/alice\" \"!partition/sda4\"\r\n", b"", Response::Data(Data::QuotaRoot { mailbox: Mailbox::try_from("inbox").unwrap(), roots: vec![ AString::String(IString::try_from("#user/alice").unwrap()), AString::String(IString::try_from("!partition/sda4").unwrap()), ], }), ), ( b"* STATUS INBOX (MESSAGES 12 DELETED 4 DELETED-STORAGE 8)\r\n", b"", Response::Data(Data::Status { mailbox: Mailbox::Inbox, items: vec![ StatusDataItem::Messages(12), StatusDataItem::Deleted(4), StatusDataItem::DeletedStorage(8), ] .into(), }), ), ( b"* NO [OVERQUOTA] Soft quota has been exceeded\r\n", b"", Response::Status( Status::no(None, Some(Code::OverQuota), "Soft quota has been exceeded") .unwrap(), ), ), ( b"A003 NO [OVERQUOTA] APPEND Failed\r\n", b"".as_ref(), Response::Status( Status::no( Some(Tag::try_from("A003").unwrap()), Some(Code::OverQuota), "APPEND Failed", ) .unwrap(), ), ), ]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/sort.rs000066400000000000000000000052061507724125200235710ustar00rootroot00000000000000use std::io::Write; use abnf_core::streaming::sp; use imap_types::{ command::CommandBody, core::Vec1, extensions::sort::{SortCriterion, SortKey}, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, opt, value}, multi::separated_list1, sequence::{delimited, tuple}, }; use crate::{ decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, search::search_criteria, }; /// ```abnf /// sort = ["UID" SP] "SORT" SP sort-criteria SP search-criteria /// ``` pub(crate) fn sort(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( map(opt(tag_no_case("UID ")), |thing| thing.is_some()), tag_no_case("SORT "), sort_criteria, sp, search_criteria, )); let (remaining, (uid, _, sort_criteria, _, (charset, search_key))) = parser(input)?; Ok(( remaining, CommandBody::Sort { sort_criteria, charset, search_criteria: search_key, uid, }, )) } /// ```abnf /// sort-criteria = "(" sort-criterion *(SP sort-criterion) ")" /// ``` pub(crate) fn sort_criteria(input: &[u8]) -> IMAPResult<&[u8], Vec1> { delimited( tag("("), map(separated_list1(sp, sort_criterion), Vec1::unvalidated), tag(")"), )(input) } /// ```abnf /// sort-criterion = ["REVERSE" SP] sort-key /// ``` pub(crate) fn sort_criterion(input: &[u8]) -> IMAPResult<&[u8], SortCriterion> { let mut parser = tuple(( map(opt(tag_no_case(b"REVERSE ")), |thing| thing.is_some()), sort_key, )); let (remaining, (reverse, key)) = parser(input)?; Ok((remaining, SortCriterion { reverse, key })) } /// ```abnf /// sort-key = "ARRIVAL" / "CC" / "DATE" / "FROM" / "SIZE" / "SUBJECT" / "TO" /// ``` pub(crate) fn sort_key(input: &[u8]) -> IMAPResult<&[u8], SortKey> { alt(( value(SortKey::Arrival, tag_no_case("ARRIVAL")), value(SortKey::Cc, tag_no_case("CC")), value(SortKey::Date, tag_no_case("DATE")), value(SortKey::From, tag_no_case("FROM")), value(SortKey::Size, tag_no_case("SIZE")), value(SortKey::Subject, tag_no_case("SUBJECT")), value(SortKey::To, tag_no_case("TO")), value(SortKey::DisplayFrom, tag_no_case("DISPLAYFROM")), value(SortKey::DisplayTo, tag_no_case("DISPLAYTO")), ))(input) } impl EncodeIntoContext for SortCriterion { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { if self.reverse { ctx.write_all(b"REVERSE ")?; } ctx.write_all(self.key.as_ref().as_bytes()) } } duesee-imap-codec-0d00966/imap-codec/src/extensions/thread.rs000066400000000000000000000262611507724125200240550ustar00rootroot00000000000000use std::io::Write; use abnf_core::streaming::sp; use imap_types::{ command::CommandBody, core::{Vec1, Vec2}, extensions::thread::{Thread, ThreadingAlgorithm, ThreadingAlgorithmOther}, response::Data, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, opt}, multi::{many_m_n, many1, separated_list1}, sequence::{delimited, preceded, tuple}, }; use crate::{ core::{atom, nz_number}, decode::{IMAPErrorKind, IMAPParseError, IMAPResult}, encode::{EncodeContext, EncodeIntoContext}, search::search_criteria, }; impl EncodeIntoContext for Thread { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.to_string().as_bytes()) } } impl EncodeIntoContext for ThreadingAlgorithm<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { ThreadingAlgorithm::OrderedSubject => ctx.write_all(b"ORDEREDSUBJECT"), ThreadingAlgorithm::References => ctx.write_all(b"REFERENCES"), ThreadingAlgorithm::Other(other) => other.encode_ctx(ctx), } } } impl EncodeIntoContext for ThreadingAlgorithmOther<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { ctx.write_all(self.as_ref().as_bytes()) } } /// ```abnf /// thread = ["UID" SP] "THREAD" SP thread-alg SP search-criteria /// ``` pub(crate) fn thread(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( map(opt(tag_no_case("UID ")), |thing| thing.is_some()), tag_no_case("THREAD "), thread_alg, sp, search_criteria, )); let (remaining, (uid, _, algorithm, _, (charset, search_key))) = parser(input)?; Ok(( remaining, CommandBody::Thread { algorithm, charset, search_criteria: search_key, uid, }, )) } /// ```abnf /// thread-alg = "ORDEREDSUBJECT" / "REFERENCES" / thread-alg-ext /// /// thread-alg-ext = atom /// ``` pub(crate) fn thread_alg(input: &[u8]) -> IMAPResult<&[u8], ThreadingAlgorithm> { map(atom, ThreadingAlgorithm::from)(input) } /// ```abnf /// thread-data = "THREAD" [SP 1*thread-list] /// ``` pub(crate) fn thread_data(input: &[u8]) -> IMAPResult<&[u8], Data> { let mut parser = preceded( tag_no_case("THREAD"), opt(preceded(sp, many1(thread_list(8)))), ); let (remaining, thread_list) = parser(input)?; Ok((remaining, Data::Thread(thread_list.unwrap_or_default()))) } pub(crate) fn thread_list( remaining_recursions: usize, ) -> impl Fn(&[u8]) -> IMAPResult<&[u8], Thread> { move |input: &[u8]| thread_list_limited(input, remaining_recursions) } /// ```abnf /// thread-list = "(" (thread-members / thread-nested) ")" /// /// thread-members = nz-number *(SP nz-number) [SP thread-nested] /// /// thread-nested = 2*thread-list /// ``` /// /// ```abnf /// // Simplified /// thread-list = "(" /// ( /// nz-number *(SP nz-number) [SP 2*thread-list] / /// 2*thread-list /// ) /// ")" /// ``` pub(crate) fn thread_list_limited( input: &[u8], remaining_recursion: usize, ) -> IMAPResult<&[u8], Thread> { if remaining_recursion == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let thread_list = |input| thread_list_limited(input, remaining_recursion.saturating_sub(1)); let mut parser = delimited( tag("("), alt(( map( tuple(( separated_list1(sp, nz_number), opt(preceded(sp, many_m_n(2, usize::MAX, thread_list))), )), |(prefix, answers)| Thread::Members { prefix: Vec1::unvalidated(prefix), answers: answers.map(Vec2::unvalidated), }, ), map(many_m_n(2, usize::MAX, thread_list), |vec| Thread::Nested { answers: Vec2::unvalidated(vec), }), )), tag(")"), ); let (rem, out) = parser(input)?; Ok((rem, out)) } #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::core::{Vec1, Vec2}; use super::{Thread, thread_list}; #[test] fn test_thread_list() { let tests: &[(&str, Thread)] = &[ ( "(1)", Thread::Members { prefix: Vec1::from(NonZeroU32::new(1).unwrap()), answers: None, }, ), ( "(1 2)", Thread::Members { prefix: Vec1::try_from(vec![ NonZeroU32::new(1).unwrap(), NonZeroU32::new(2).unwrap(), ]) .unwrap(), answers: None, }, ), ( "((1)(2))", Thread::Nested { answers: Vec2::try_from(vec![ Thread::Members { prefix: Vec1::from(NonZeroU32::new(1).unwrap()), answers: None, }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(2).unwrap()), answers: None, }, ]) .unwrap(), }, ), ( "(1 (2)(3))", Thread::Members { prefix: Vec1::try_from(vec![NonZeroU32::new(1).unwrap()]).unwrap(), answers: Some( Vec2::try_from(vec![ Thread::Members { prefix: Vec1::from(NonZeroU32::new(2).unwrap()), answers: None, }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(3).unwrap()), answers: None, }, ]) .unwrap(), ), }, ), ( "(1 (2 4)(3))", Thread::Members { prefix: Vec1::try_from(vec![NonZeroU32::new(1).unwrap()]).unwrap(), answers: Some( Vec2::try_from(vec![ Thread::Members { prefix: Vec1::try_from(vec![ NonZeroU32::new(2).unwrap(), NonZeroU32::new(4).unwrap(), ]) .unwrap(), answers: None, }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(3).unwrap()), answers: None, }, ]) .unwrap(), ), }, ), ( "(1 (2 4 (5)(6))(3))", Thread::Members { prefix: Vec1::try_from(vec![NonZeroU32::new(1).unwrap()]).unwrap(), answers: Some( Vec2::try_from(vec![ Thread::Members { prefix: Vec1::try_from(vec![ NonZeroU32::new(2).unwrap(), NonZeroU32::new(4).unwrap(), ]) .unwrap(), answers: Some( Vec2::try_from(vec![ Thread::Members { prefix: Vec1::from(NonZeroU32::new(5).unwrap()), answers: None, }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(6).unwrap()), answers: None, }, ]) .unwrap(), ), }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(3).unwrap()), answers: None, }, ]) .unwrap(), ), }, ), ( "(1 (2)(3)(((4)(5))(6)))", Thread::Members { prefix: Vec1::from(NonZeroU32::new(1).unwrap()), answers: Some( Vec2::try_from(vec![ Thread::Members { prefix: Vec1::from(NonZeroU32::new(2).unwrap()), answers: None, }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(3).unwrap()), answers: None, }, Thread::Nested { answers: Vec2::try_from(vec![ Thread::Nested { answers: Vec2::try_from(vec![ Thread::Members { prefix: Vec1::from(NonZeroU32::new(4).unwrap()), answers: None, }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(5).unwrap()), answers: None, }, ]) .unwrap(), }, Thread::Members { prefix: Vec1::from(NonZeroU32::new(6).unwrap()), answers: None, }, ]) .unwrap(), }, ]) .unwrap(), ), }, ), ]; for (test, expected) in tests { println!("test: {test}"); println!("expected: {expected}\n"); assert_eq!(*test, expected.to_string().as_str()); let (rem, _data) = thread_list(8)(test.as_bytes()).unwrap(); assert!(rem.is_empty()); } } } duesee-imap-codec-0d00966/imap-codec/src/extensions/uidplus.rs000066400000000000000000000156311507724125200242720ustar00rootroot00000000000000use std::{io::Write, num::NonZeroU32}; use abnf_core::streaming::sp; use imap_types::{ command::CommandBody, core::Vec1, extensions::uidplus::{UidElement, UidElement::Range, UidSet}, response::Code, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::map, multi::separated_list1, sequence::{preceded, separated_pair, tuple}, }; use crate::{ core::nz_number, decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext, utils::join_serializable}, sequence::sequence_set, }; /// ```abnf /// uid-expunge = "UID" SP "EXPUNGE" SP sequence-set /// ``` pub(crate) fn uid_expunge(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { map( preceded(tag_no_case("UID EXPUNGE "), sequence_set), |sequence_set| CommandBody::ExpungeUid { sequence_set }, )(input) } /// ```abnf /// resp-code-apnd = "APPENDUID" SP nz-number SP append-uid /// /// append-uid = uniqueid /// /// uniqueid = nz-number /// ``` pub(crate) fn resp_code_apnd(input: &[u8]) -> IMAPResult<&[u8], Code> { let (rem, (_, uid_validity, _, uid)) = tuple((tag_no_case("APPENDUID "), nz_number, sp, nz_number))(input)?; Ok((rem, Code::AppendUid { uid_validity, uid })) } /// ```abnf /// resp-code-copy = "COPYUID" SP nz-number SP uid-set SP uid-set /// ``` pub(crate) fn resp_code_copy(input: &[u8]) -> IMAPResult<&[u8], Code> { let (rem, (_, uid_validity, _, source, _, destination)) = tuple((tag_no_case("COPYUID "), nz_number, sp, uid_set, sp, uid_set))(input)?; Ok(( rem, Code::CopyUid { uid_validity, source, destination, }, )) } /// ```abnf /// uid-set = (uniqueid / uid-range) *("," uid-set) /// ``` /// /// Modified ... /// /// ```abnf /// uid-set = (uniqueid / uid-range) *("," uniqueid / uid-range) /// ``` pub(crate) fn uid_set(input: &[u8]) -> IMAPResult<&[u8], UidSet> { map( separated_list1( tag(b","), alt(( map(uid_range, |(a, b)| UidElement::Range(a, b)), map(nz_number, UidElement::Single), )), ), // `unvalidated` is fine due to `separated_list1` |set| UidSet(Vec1::unvalidated(set)), )(input) } /// ```abnf /// ; two uniqueid values and all values between these two regards of order. /// ; Example: 2:4 and 4:2 are equivalent. /// uid-range = (uniqueid ":" uniqueid) /// ``` pub(crate) fn uid_range(input: &[u8]) -> IMAPResult<&[u8], (NonZeroU32, NonZeroU32)> { separated_pair(nz_number, tag(b":"), nz_number)(input) } impl EncodeIntoContext for UidSet { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { join_serializable(self.0.as_ref(), b",", ctx) } } impl EncodeIntoContext for UidElement { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { match self { UidElement::Single(uid) => uid.encode_ctx(ctx), Range(start, end) => { start.encode_ctx(ctx)?; ctx.write_all(b":")?; end.encode_ctx(ctx) } } } } #[cfg(test)] mod tests { use imap_types::{ command::{Command, CommandBody}, core::{Text, Vec1}, extensions::uidplus::{UidElement, UidSet}, response::{Code, Response, Status, StatusBody, StatusKind}, }; use crate::{ extensions::uidplus::uid_set, testing::{kat_inverse_command, kat_inverse_response, known_answer_test_parse}, }; #[test] fn test_kat_inverse_command_uid_expunge() { kat_inverse_command(&[ ( b"A UID EXPUNGE 1\r\n?".as_ref(), b"?".as_ref(), Command::new( "A", CommandBody::ExpungeUid { sequence_set: 1.try_into().unwrap(), }, ) .unwrap(), ), ( b"A UID EXPUNGE *\r\n?".as_ref(), b"?".as_ref(), Command::new( "A", CommandBody::ExpungeUid { sequence_set: "*".try_into().unwrap(), }, ) .unwrap(), ), ( b"A UID EXPUNGE 1:1337\r\n?".as_ref(), b"?".as_ref(), Command::new( "A", CommandBody::ExpungeUid { sequence_set: "1:1337".try_into().unwrap(), }, ) .unwrap(), ), ]); } #[test] fn test_kat_inverse_response() { kat_inverse_response(&[ ( b"* OK [UIDNOTSTICKY] ...\r\n???", b"???", Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::UidNotSticky), text: Text::unvalidated("..."), })), ), ( b"* OK [APPENDUID 12345 1337] ...\r\n???", b"???", Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::AppendUid { uid_validity: 12345.try_into().unwrap(), uid: 1337.try_into().unwrap(), }), text: Text::unvalidated("..."), })), ), ( b"* OK [COPYUID 12345 1001:1005 1:5] ...\r\n???", b"???", Response::Status(Status::Untagged(StatusBody { kind: StatusKind::Ok, code: Some(Code::CopyUid { uid_validity: 12345.try_into().unwrap(), source: UidSet(Vec1::from(UidElement::Range( 1001.try_into().unwrap(), 1005.try_into().unwrap(), ))), destination: UidSet(Vec1::from(UidElement::Range( 1.try_into().unwrap(), 5.try_into().unwrap(), ))), }), text: Text::unvalidated("..."), })), ), ]); } #[test] fn test_uid_set() { let tests = [ // ( // b"1 ".as_ref(), // b" ".as_ref(), // UidSet(Vec1::from(UidElement::Single(1.try_into().unwrap()))), // ), ( b"1:5 ".as_ref(), b" ".as_ref(), UidSet(Vec1::from(UidElement::Range( 1.try_into().unwrap(), 5.try_into().unwrap(), ))), ), ]; for test in tests { known_answer_test_parse(test, uid_set); } } } duesee-imap-codec-0d00966/imap-codec/src/extensions/unselect.rs000066400000000000000000000005561507724125200244270ustar00rootroot00000000000000#[cfg(test)] mod tests { use imap_types::command::{Command, CommandBody}; use crate::testing::kat_inverse_command; #[test] fn test_kat_inverse_command_unselect() { kat_inverse_command(&[( b"A UNSELECT\r\n".as_ref(), b"".as_ref(), Command::new("A", CommandBody::unselect()).unwrap(), )]); } } duesee-imap-codec-0d00966/imap-codec/src/extensions/utf8.rs000066400000000000000000000035371507724125200234750ustar00rootroot00000000000000use std::{io::Write, str::from_utf8}; use abnf_core::streaming::dquote; use imap_types::{ extensions::utf8::QuotedUtf8, utils::{escape_quoted, indicators::is_quoted_specials, unescape_quoted}, }; use nom::{ bytes::streaming::{escaped, take_while1}, character::streaming::one_of, combinator::map_res, sequence::tuple, }; use crate::{ decode::IMAPResult, encode::{EncodeContext, EncodeIntoContext}, }; impl EncodeIntoContext for QuotedUtf8<'_> { fn encode_ctx(&self, ctx: &mut EncodeContext) -> std::io::Result<()> { write!(ctx, "\"{}\"", escape_quoted(self.0.as_ref())) } } /// ```abnf /// ; QUOTED-CHAR is not modified, as it will affect other RFC 3501 ABNF non-terminals. /// quoted =/ DQUOTE *uQUOTED-CHAR DQUOTE /// /// uQUOTED-CHAR = QUOTED-CHAR / UTF8-2 / UTF8-3 / UTF8-4 /// UTF8-2 = /// UTF8-3 = /// UTF8-4 = /// ``` /// /// This function only allocates a new String, when needed, i.e. when /// quoted chars need to be replaced. pub(crate) fn quoted_utf8(input: &[u8]) -> IMAPResult<&[u8], QuotedUtf8> { let mut parser = tuple(( dquote, map_res( escaped( take_while1(|c| !is_quoted_specials(c)), '\\', one_of("\\\""), ), from_utf8, ), dquote, )); let (remaining, (_, quoted_utf8, _)) = parser(input)?; Ok((remaining, QuotedUtf8(unescape_quoted(quoted_utf8)))) } #[cfg(test)] mod test { use imap_types::extensions::utf8::QuotedUtf8; use super::quoted_utf8; #[test] fn test_quoted_utf8() { assert_eq!( (b"".as_ref(), QuotedUtf8("äö¹".into())), quoted_utf8("\"äö¹\"".as_bytes()).unwrap() ); } } duesee-imap-codec-0d00966/imap-codec/src/fetch.rs000066400000000000000000000447501507724125200215030ustar00rootroot00000000000000use std::num::NonZeroU32; use abnf_core::streaming::sp; use imap_types::{ core::{AString, NString8, Vec1}, fetch::{MessageDataItem, MessageDataItemName, Part, PartSpecifier, Section}, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, character::streaming::char, combinator::{map, opt, value}, multi::separated_list1, sequence::{delimited, preceded, tuple}, }; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::mod_sequence_value; use crate::{ body::body, core::{astring, nstring, number, nz_number}, datetime::date_time, decode::IMAPResult, envelope::envelope, extensions::binary::{literal8, partial, section_binary}, flag::flag_fetch, }; /// ```abnf /// fetch-att = "ENVELOPE" / /// "FLAGS" / /// "INTERNALDATE" / /// "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] / /// "BODY" ["STRUCTURE"] / /// "UID" / /// "BODY" section ["<" number "." nz-number ">"] / /// "BODY.PEEK" section ["<" number "." nz-number ">"] / /// "BINARY" section-binary [partial] / ; RFC 3516 /// "BINARY.PEEK" section-binary [partial] / ; RFC 3516 /// "BINARY.SIZE" section-binary / ; RFC 3516 /// "MODSEQ" ; RFC 7162 /// ``` pub(crate) fn fetch_att(input: &[u8]) -> IMAPResult<&[u8], MessageDataItemName> { alt(( value(MessageDataItemName::Envelope, tag_no_case(b"ENVELOPE")), value(MessageDataItemName::Flags, tag_no_case(b"FLAGS")), value( MessageDataItemName::InternalDate, tag_no_case(b"INTERNALDATE"), ), value( MessageDataItemName::BodyStructure, tag_no_case(b"BODYSTRUCTURE"), ), map( tuple(( tag_no_case(b"BODY.PEEK"), section, opt(delimited( tag(b"<"), tuple((number, tag(b"."), nz_number)), tag(b">"), )), )), |(_, section, byterange)| MessageDataItemName::BodyExt { section, partial: byterange.map(|(start, _, end)| (start, end)), peek: true, }, ), map( tuple(( tag_no_case(b"BODY"), section, opt(delimited( tag(b"<"), tuple((number, tag(b"."), nz_number)), tag(b">"), )), )), |(_, section, byterange)| MessageDataItemName::BodyExt { section, partial: byterange.map(|(start, _, end)| (start, end)), peek: false, }, ), map( tuple((tag_no_case("BINARY.PEEK"), section_binary, opt(partial))), |(_, section, partial)| MessageDataItemName::Binary { section, partial, peek: true, }, ), map( tuple((tag_no_case("BINARY"), section_binary, opt(partial))), |(_, section, partial)| MessageDataItemName::Binary { section, partial, peek: false, }, ), map( preceded(tag_no_case("BINARY.SIZE"), section_binary), |section| MessageDataItemName::BinarySize { section }, ), value(MessageDataItemName::Body, tag_no_case(b"BODY")), value(MessageDataItemName::Uid, tag_no_case(b"UID")), value( MessageDataItemName::Rfc822Header, tag_no_case(b"RFC822.HEADER"), ), value(MessageDataItemName::Rfc822Size, tag_no_case(b"RFC822.SIZE")), value(MessageDataItemName::Rfc822Text, tag_no_case(b"RFC822.TEXT")), value(MessageDataItemName::Rfc822, tag_no_case(b"RFC822")), #[cfg(feature = "ext_condstore_qresync")] value(MessageDataItemName::ModSeq, tag_no_case(b"MODSEQ")), ))(input) } /// ```abnf /// msg-att = "(" /// (msg-att-dynamic / msg-att-static) *(SP (msg-att-dynamic / msg-att-static)) /// ")" /// ``` pub(crate) fn msg_att(input: &[u8]) -> IMAPResult<&[u8], Vec1> { delimited( tag(b"("), map( separated_list1(sp, alt((msg_att_dynamic, msg_att_static))), Vec1::unvalidated, ), tag(b")"), )(input) } /// ```abnf /// msg-att-dynamic = "FLAGS" SP "(" [flag-fetch *(SP flag-fetch)] ")" /// ``` /// /// Note: MAY change for a message pub(crate) fn msg_att_dynamic(input: &[u8]) -> IMAPResult<&[u8], MessageDataItem> { let flags = map( preceded( tag_no_case(b"FLAGS "), delimited(char('('), opt(separated_list1(sp, flag_fetch)), char(')')), ), |flags| MessageDataItem::Flags(flags.unwrap_or_default()), ); #[cfg(feature = "ext_condstore_qresync")] let modseq = map( preceded( tag_no_case("MODSEQ "), delimited(char('('), mod_sequence_value, char(')')), ), MessageDataItem::ModSeq, ); #[cfg(feature = "ext_condstore_qresync")] let mut parser = alt((flags, modseq)); #[cfg(not(feature = "ext_condstore_qresync"))] let mut parser = flags; let (remaining, item) = parser(input)?; Ok((remaining, item)) } /// ```abnf /// msg-att-static = "ENVELOPE" SP envelope / /// "INTERNALDATE" SP date-time / /// "RFC822" [".HEADER" / ".TEXT"] SP nstring / /// "RFC822.SIZE" SP number / /// "BODY" ["STRUCTURE"] SP body / /// "BODY" section ["<" number ">"] SP nstring / /// "UID" SP uniqueid / /// "BINARY" section-binary SP (nstring / literal8) / ; RFC 3516 /// "BINARY.SIZE" section-binary SP number ; RFC 3516 /// ``` /// /// Note: MUST NOT change for a message pub(crate) fn msg_att_static(input: &[u8]) -> IMAPResult<&[u8], MessageDataItem> { alt(( map( preceded(tag_no_case(b"ENVELOPE "), envelope), MessageDataItem::Envelope, ), map( preceded(tag_no_case(b"INTERNALDATE "), date_time), MessageDataItem::InternalDate, ), map( preceded(tag_no_case(b"RFC822.HEADER "), nstring), MessageDataItem::Rfc822Header, ), map( preceded(tag_no_case(b"RFC822.TEXT "), nstring), MessageDataItem::Rfc822Text, ), map( preceded(tag_no_case(b"RFC822.SIZE "), number), MessageDataItem::Rfc822Size, ), map( preceded(tag_no_case(b"RFC822 "), nstring), MessageDataItem::Rfc822, ), map( preceded(tag_no_case(b"BODYSTRUCTURE "), body(8)), MessageDataItem::BodyStructure, ), map( preceded(tag_no_case(b"BODY "), body(8)), MessageDataItem::Body, ), map( tuple(( tag_no_case(b"BODY"), section, opt(delimited(tag(b"<"), number, tag(b">"))), sp, nstring, )), |(_, section, origin, _, data)| MessageDataItem::BodyExt { section, origin, data, }, ), map( preceded(tag_no_case(b"UID "), uniqueid), MessageDataItem::Uid, ), map( tuple(( tag_no_case(b"BINARY"), section_binary, sp, alt(( map(nstring, NString8::NString), map(literal8, NString8::Literal8), )), )), |(_, section, _, value)| MessageDataItem::Binary { section, value }, ), map( tuple((tag_no_case(b"BINARY.SIZE"), section_binary, sp, number)), |(_, section, _, size)| MessageDataItem::BinarySize { section, size }, ), ))(input) } #[inline] /// `uniqueid = nz-number` /// /// Note: Strictly ascending pub(crate) fn uniqueid(input: &[u8]) -> IMAPResult<&[u8], NonZeroU32> { nz_number(input) } /// `section = "[" [section-spec] "]"` pub(crate) fn section(input: &[u8]) -> IMAPResult<&[u8], Option
> { delimited(tag(b"["), opt(section_spec), tag(b"]"))(input) } /// `section-spec = section-msgtext / (section-part ["." section-text])` pub(crate) fn section_spec(input: &[u8]) -> IMAPResult<&[u8], Section> { alt(( map(section_msgtext, |part_specifier| match part_specifier { PartSpecifier::PartNumber(_) => unreachable!(), PartSpecifier::Header => Section::Header(None), PartSpecifier::HeaderFields(fields) => Section::HeaderFields(None, fields), PartSpecifier::HeaderFieldsNot(fields) => Section::HeaderFieldsNot(None, fields), PartSpecifier::Text => Section::Text(None), PartSpecifier::Mime => unreachable!(), }), map( tuple((section_part, opt(tuple((tag(b"."), section_text))))), |(part_number, maybe_part_specifier)| { if let Some((_, part_specifier)) = maybe_part_specifier { match part_specifier { PartSpecifier::PartNumber(_) => unreachable!(), PartSpecifier::Header => Section::Header(Some(Part(part_number))), PartSpecifier::HeaderFields(fields) => { Section::HeaderFields(Some(Part(part_number)), fields) } PartSpecifier::HeaderFieldsNot(fields) => { Section::HeaderFieldsNot(Some(Part(part_number)), fields) } PartSpecifier::Text => Section::Text(Some(Part(part_number))), PartSpecifier::Mime => Section::Mime(Part(part_number)), } } else { Section::Part(Part(part_number)) } }, ), ))(input) } /// `section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list / "TEXT"` /// /// Top-level or MESSAGE/RFC822 part pub(crate) fn section_msgtext(input: &[u8]) -> IMAPResult<&[u8], PartSpecifier> { alt(( map( tuple((tag_no_case(b"HEADER.FIELDS.NOT"), sp, header_list)), |(_, _, header_list)| PartSpecifier::HeaderFieldsNot(header_list), ), map( tuple((tag_no_case(b"HEADER.FIELDS"), sp, header_list)), |(_, _, header_list)| PartSpecifier::HeaderFields(header_list), ), value(PartSpecifier::Header, tag_no_case(b"HEADER")), value(PartSpecifier::Text, tag_no_case(b"TEXT")), ))(input) } #[inline] /// `section-part = nz-number *("." nz-number)` /// /// Body part nesting pub(crate) fn section_part(input: &[u8]) -> IMAPResult<&[u8], Vec1> { map(separated_list1(tag(b"."), nz_number), Vec1::unvalidated)(input) } /// `section-text = section-msgtext / "MIME"` /// /// Text other than actual body part (headers, etc.) pub(crate) fn section_text(input: &[u8]) -> IMAPResult<&[u8], PartSpecifier> { alt(( section_msgtext, value(PartSpecifier::Mime, tag_no_case(b"MIME")), ))(input) } /// `header-list = "(" header-fld-name *(SP header-fld-name) ")"` pub(crate) fn header_list(input: &[u8]) -> IMAPResult<&[u8], Vec1> { map( delimited(tag(b"("), separated_list1(sp, header_fld_name), tag(b")")), Vec1::unvalidated, )(input) } #[inline] /// `header-fld-name = astring` pub(crate) fn header_fld_name(input: &[u8]) -> IMAPResult<&[u8], AString> { astring(input) } #[cfg(test)] mod tests { use imap_types::{ body::{BasicFields, Body, BodyStructure, SpecificFields}, core::{IString, NString}, datetime::DateTime, envelope::Envelope, }; use super::*; use crate::testing::known_answer_test_encode; #[test] fn test_encode_message_data_item_name() { let tests = [ (MessageDataItemName::Body, b"BODY".as_ref()), ( MessageDataItemName::BodyExt { section: None, partial: None, peek: false, }, b"BODY[]", ), (MessageDataItemName::BodyStructure, b"BODYSTRUCTURE"), (MessageDataItemName::Envelope, b"ENVELOPE"), (MessageDataItemName::Flags, b"FLAGS"), (MessageDataItemName::InternalDate, b"INTERNALDATE"), (MessageDataItemName::Rfc822, b"RFC822"), (MessageDataItemName::Rfc822Header, b"RFC822.HEADER"), (MessageDataItemName::Rfc822Size, b"RFC822.SIZE"), (MessageDataItemName::Rfc822Text, b"RFC822.TEXT"), (MessageDataItemName::Uid, b"UID"), ]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_encode_message_data_item() { let tests = [ ( MessageDataItem::Body(BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![], id: NString(None), description: NString(None), content_transfer_encoding: IString::try_from("base64").unwrap(), size: 42, }, specific: SpecificFields::Text { subtype: IString::try_from("foo").unwrap(), number_of_lines: 1337, }, }, extension_data: None, }), b"BODY (\"TEXT\" \"foo\" NIL NIL NIL \"base64\" 42 1337)".as_ref(), ), ( MessageDataItem::BodyExt { section: None, origin: None, data: NString(None), }, b"BODY[] NIL", ), ( MessageDataItem::BodyExt { section: None, origin: Some(123), data: NString(None), }, b"BODY[]<123> NIL", ), ( MessageDataItem::BodyStructure(BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![], id: NString(None), description: NString(None), content_transfer_encoding: IString::try_from("base64").unwrap(), size: 213, }, specific: SpecificFields::Text { subtype: IString::try_from("").unwrap(), number_of_lines: 224, }, }, extension_data: None, }), b"BODYSTRUCTURE (\"TEXT\" \"\" NIL NIL NIL \"base64\" 213 224)", ), ( MessageDataItem::Envelope(Envelope { date: NString(None), subject: NString(None), from: vec![], sender: vec![], reply_to: vec![], to: vec![], cc: vec![], bcc: vec![], in_reply_to: NString(None), message_id: NString(None), }), b"ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)", ), (MessageDataItem::Flags(vec![]), b"FLAGS ()"), ( MessageDataItem::InternalDate( DateTime::try_from( chrono::DateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200") .unwrap(), ) .unwrap(), ), b"INTERNALDATE \"01-Jul-2003 10:52:37 +0200\"", ), (MessageDataItem::Rfc822(NString(None)), b"RFC822 NIL"), ( MessageDataItem::Rfc822Header(NString(None)), b"RFC822.HEADER NIL", ), (MessageDataItem::Rfc822Size(3456), b"RFC822.SIZE 3456"), ( MessageDataItem::Rfc822Text(NString(None)), b"RFC822.TEXT NIL", ), ( MessageDataItem::Uid(NonZeroU32::try_from(u32::MAX).unwrap()), b"UID 4294967295", ), ]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_encode_section() { let tests = [ ( Section::Part(Part(Vec1::from(NonZeroU32::try_from(1).unwrap()))), b"1".as_ref(), ), (Section::Header(None), b"HEADER"), ( Section::Header(Some(Part(Vec1::from(NonZeroU32::try_from(1).unwrap())))), b"1.HEADER", ), ( Section::HeaderFields(None, Vec1::from(AString::try_from("").unwrap())), b"HEADER.FIELDS (\"\")", ), ( Section::HeaderFields( Some(Part(Vec1::from(NonZeroU32::try_from(1).unwrap()))), Vec1::from(AString::try_from("").unwrap()), ), b"1.HEADER.FIELDS (\"\")", ), ( Section::HeaderFieldsNot(None, Vec1::from(AString::try_from("").unwrap())), b"HEADER.FIELDS.NOT (\"\")", ), ( Section::HeaderFieldsNot( Some(Part(Vec1::from(NonZeroU32::try_from(1).unwrap()))), Vec1::from(AString::try_from("").unwrap()), ), b"1.HEADER.FIELDS.NOT (\"\")", ), (Section::Text(None), b"TEXT"), ( Section::Text(Some(Part(Vec1::from(NonZeroU32::try_from(1).unwrap())))), b"1.TEXT", ), ( Section::Mime(Part(Vec1::from(NonZeroU32::try_from(1).unwrap()))), b"1.MIME", ), ]; for test in tests { known_answer_test_encode(test) } } } duesee-imap-codec-0d00966/imap-codec/src/flag.rs000066400000000000000000000133301507724125200213110ustar00rootroot00000000000000use abnf_core::streaming::sp; use imap_types::flag::{Flag, FlagFetch, FlagNameAttribute, FlagPerm}; use nom::{ branch::alt, bytes::streaming::tag, character::streaming::char, combinator::{map, recognize, value}, multi::{separated_list0, separated_list1}, sequence::{delimited, preceded, tuple}, }; use crate::{core::atom, decode::IMAPResult}; /// ```abnf /// flag = "\Answered" / /// "\Flagged" / /// "\Deleted" / /// "\Seen" / /// "\Draft" / /// flag-keyword / /// flag-extension /// ``` /// /// Note: Does not include "\Recent" pub(crate) fn flag(input: &[u8]) -> IMAPResult<&[u8], Flag> { alt(( map(preceded(char('\\'), atom), Flag::system), map(atom, Flag::Keyword), ))(input) } // Note(duesee): This was inlined into [`flag`]. // #[inline] // /// `flag-keyword = atom` // pub(crate) fn flag_keyword(input: &[u8]) -> IMAPResult<&[u8], Flag> { // map(atom, Flag::Keyword)(input) // } // Note: This was inlined into `mbx_list_flags`. // /// ```abnf // /// flag-extension = "\" atom // /// ``` // /// // /// Future expansion. // /// // /// Client implementations MUST accept flag-extension flags. // /// Server implementations MUST NOT generate flag-extension flags // /// except as defined by future standard or standards-track revisions of this specification. // pub(crate) fn flag_extension(input: &[u8]) -> IMAPResult<&[u8], Atom> { // preceded(tag(b"\\"), atom)(input) // } /// `flag-list = "(" [flag *(SP flag)] ")"` pub(crate) fn flag_list(input: &[u8]) -> IMAPResult<&[u8], Vec> { delimited(tag(b"("), separated_list0(sp, flag), tag(b")"))(input) } /// `flag-fetch = flag / "\Recent"` pub(crate) fn flag_fetch(input: &[u8]) -> IMAPResult<&[u8], FlagFetch> { if let Ok((rem, peek)) = recognize(tuple((char('\\'), atom)))(input) { if peek.eq_ignore_ascii_case(b"\\recent") { return Ok((rem, FlagFetch::Recent)); } } map(flag, FlagFetch::Flag)(input) } /// `flag-perm = flag / "\*"` pub(crate) fn flag_perm(input: &[u8]) -> IMAPResult<&[u8], FlagPerm> { alt(( value(FlagPerm::Asterisk, tag("\\*")), map(flag, FlagPerm::Flag), ))(input) } /// ```abnf /// mbx-list-flags = *(mbx-list-oflag SP) mbx-list-sflag *(SP mbx-list-oflag) / /// mbx-list-oflag *(SP mbx-list-oflag) /// ``` /// /// TODO(#155): ABNF enforces that sflag is not used more than once. /// We could parse any flag and check for multiple occurrences of sflag later. pub(crate) fn mbx_list_flags(input: &[u8]) -> IMAPResult<&[u8], Vec> { let (remaining, flags) = separated_list1(sp, map(preceded(char('\\'), atom), FlagNameAttribute::from))(input)?; // TODO(#155): Do we really want to enforce this? // let sflag_count = flags // .iter() // .filter(|&flag| FlagNameAttribute::is_selectability(flag)) // .count(); // // if sflag_count > 1 { // return Err(nom::Err::Failure(nom::error::make_error( // input, // nom::error::ErrorKind::Verify, // ))); // } Ok((remaining, flags)) } // Note: This was inlined into `mbx_list_flags`. // /// ```abnf // /// mbx-list-oflag = "\Noinferiors" / flag-extension // /// ``` // /// // /// Other flags; multiple possible per LIST response // pub(crate) fn mbx_list_oflag(input: &[u8]) -> IMAPResult<&[u8], FlagNameAttribute> { // alt(( // value( // FlagNameAttribute::Noinferiors, // tag_no_case(b"\\Noinferiors"), // ), // map(flag_extension, FlagNameAttribute::Extension), // ))(input) // } // Note: This was inlined into `mbx_list_flags`. // /// ```abnf // /// mbx-list-sflag = "\Noselect" / "\Marked" / "\Unmarked" // /// ``` // /// // /// Selectability flags; only one per LIST response // pub(crate) fn mbx_list_sflag(input: &[u8]) -> IMAPResult<&[u8], FlagNameAttribute> { // alt(( // value(FlagNameAttribute::Noselect, tag_no_case(b"\\Noselect")), // value(FlagNameAttribute::Marked, tag_no_case(b"\\Marked")), // value(FlagNameAttribute::Unmarked, tag_no_case(b"\\Unmarked")), // ))(input) // } #[cfg(test)] mod tests { use imap_types::{ core::Atom, flag::{Flag, FlagFetch, FlagNameAttribute, FlagPerm}, }; use super::*; #[test] fn test_parse_flag_fetch() { let tests = [( "iS)", FlagFetch::Flag(Flag::Keyword(Atom::try_from("iS").unwrap())), )]; for (test, expected) in tests { let (rem, got) = flag_fetch(test.as_bytes()).unwrap(); assert_eq!(rem.len(), 1); assert_eq!(expected, got); } } #[test] fn test_parse_flag_perm() { let tests = [ ("\\Deleted)", FlagPerm::Flag(Flag::Deleted)), ( "\\Deletedx)", FlagPerm::Flag(Flag::system(Atom::try_from("Deletedx").unwrap())), ), ("\\Seen ", FlagPerm::Flag(Flag::Seen)), ("\\*)", FlagPerm::Asterisk), ]; for (test, expected) in tests { let (rem, got) = flag_perm(test.as_bytes()).unwrap(); assert_eq!(rem.len(), 1); assert_eq!(expected, got); } } #[test] fn test_parse_mbx_list_flags() { let tests = [ ( "\\Markedm)", vec![FlagNameAttribute::from(Atom::try_from("Markedm").unwrap())], ), ("\\Marked)", vec![FlagNameAttribute::Marked]), ]; for (test, expected) in tests { let (rem, got) = mbx_list_flags(test.as_bytes()).unwrap(); assert_eq!(expected, got); assert_eq!(rem.len(), 1); } } } duesee-imap-codec-0d00966/imap-codec/src/fragmentizer.rs000066400000000000000000001562161507724125200231100ustar00rootroot00000000000000//! Utilities to split IMAP bytes into line and literal fragments. //! //! These utilities can be used to fragment a stream of IMAP bytes into lines (with metadata) and //! literals (before actually doing detailed IMAP parsing). //! //! This approach has multiple advantages: It separates literal handling from IMAP parsing and sets //! clear message boundaries even in the presence of malformed messages. Consequently, malformed //! messages can be reliably discarded. (A naive implementation of byte discardment may lead to //! adventurous (security) issues, such as, literal data being interpreted as command or response.) //! Further, this two-layered approach allows to more easily guard against excessive memory //! allocation by malevolant actors. //! //! # Example //! //! ```rust,ignore //! # use std::io::{stdin, Read}; //! # //! # use imap_codec::{ //! # fragmentizer::Fragmentizer, //! # imap_types::utils::escape_byte_string, //! # }; //! # //! # fn read_bytes() -> &'static [u8] { b"" } //! # //! # fn main() { //! let mut fragmentizer = Fragmentizer::new(1024); //! //! loop { //! match fragmentizer.progress() { //! Some(fragment_info) => { //! let fragment_bytes = fragmentizer.fragment_bytes(fragment_info); //! //! if fragmentizer.is_message_complete() { //! let message_bytes = fragmentizer.message_bytes(); //! } //! } //! None => { //! let received = read_bytes(); //! fragmentizer.enqueue_bytes(received); //! } //! } //! } //! # } //! ``` use std::{collections::VecDeque, ops::Range}; use imap_types::{ core::{LiteralMode, Tag}, secret::Secret, }; use crate::decode::Decoder; /// Splits IMAP bytes into line and literal fragments. /// /// The `Fragmentizer` prevents excessive memory allocation through a configurable maximum message size. /// Correct fragmentation is ensured even for messages exceeding the allowed message size. /// /// If the message size is exceeded, /// [`Fragmentizer::decode_message`] will fail and /// [`Fragmentizer::message_bytes`] will emit truncated message bytes. /// However, fragmentation will seamlessly continue with the following message. #[derive(Clone, Debug)] pub struct Fragmentizer { /// Enqueued bytes that are not parsed by [`Fragmentizer::progress`] yet. unparsed_buffer: VecDeque, /// Upper limit for the size of parsed messages. max_message_size: Option, /// Whether the size limit is exceeded for the current message. max_message_size_exceeded: bool, /// The current message was poisoned. The message will still be parsed, but the decoding /// will fail. message_poisoned: bool, /// Parsed bytes of the current messages. The length is limited by /// [`Fragmentizer::max_message_size`]. message_buffer: Vec, /// Parser for the next fragment of the current message. Is `None` if no fragment is expected /// because the message is complete. parser: Option, } impl Fragmentizer { /// Creates a `Fragmentizer` with a maximum message size. /// /// The maximum message size is bounded by `max_message_size` to prevent excessive memory allocation. pub fn new(max_message_size: u32) -> Self { Self { unparsed_buffer: VecDeque::new(), max_message_size: Some(max_message_size), max_message_size_exceeded: false, message_poisoned: false, message_buffer: Vec::new(), parser: Some(Parser::Line(LineParser::new(0))), } } /// Creates a `Fragmentizer` without a maximum message size. /// ///
/// This is dangerous because it allows an attacker to allocate an excessive amount of memory /// by sending a huge message. ///
pub fn without_max_message_size() -> Self { Self { unparsed_buffer: VecDeque::new(), max_message_size: None, max_message_size_exceeded: false, message_poisoned: false, message_buffer: Vec::new(), parser: Some(Parser::Line(LineParser::new(0))), } } /// Continue parsing the current message until the next fragment is detected. /// /// Returns `None` if more bytes need to be enqueued via [`Fragmentizer::enqueue_bytes`]. /// If [`Fragmentizer::is_message_complete`] returns true after this function was called, /// then the message was fully parsed. The following call of this function will then start /// the next message. pub fn progress(&mut self) -> Option { let parser = match &mut self.parser { Some(parser) => { // Continue current message parser } None => { // Start next message self.max_message_size_exceeded = false; self.message_poisoned = false; self.message_buffer.clear(); self.parser.insert(Parser::Line(LineParser::new(0))) } }; // Progress fragment let (parsed_byte_count, fragment) = match parser { Parser::Line(parser) => parser.parse(&self.unparsed_buffer), Parser::Literal(parser) => parser.parse(&self.unparsed_buffer), }; self.dequeue_parsed_bytes(parsed_byte_count); if let Some(fragment) = fragment { self.parser = match fragment { // Finish current message FragmentInfo::Line { announcement: None, .. } => None, // Next fragment will be a literal FragmentInfo::Line { end, announcement: Some(LiteralAnnouncement { length, .. }), .. } => Some(Parser::Literal(LiteralParser::new(end, length))), // Next fragment will be a line FragmentInfo::Literal { end, .. } => Some(Parser::Line(LineParser::new(end))), } } fragment } /// Enqueues more byte that can be parsed by [`Fragmentizer::progress`]. /// /// Note that the message size limit is not enforced on the enqueued bytes. You can control /// the size of the enqueued bytes by only calling this function if more bytes are necessary. /// More bytes are necessary if [`Fragmentizer::progress`] returns `None`. pub fn enqueue_bytes(&mut self, bytes: &[u8]) { self.unparsed_buffer.extend(bytes); } /// Returns the bytes for a fragment of the current message. pub fn fragment_bytes(&self, fragment_info: FragmentInfo) -> &[u8] { let (start, end) = match fragment_info { FragmentInfo::Line { start, end, .. } => (start, end), FragmentInfo::Literal { start, end } => (start, end), }; let start = start.min(self.message_buffer.len()); let end = end.min(self.message_buffer.len()); &self.message_buffer[start..end] } /// Returns whether the current message was fully parsed. /// /// If it returns true then it makes sense to call [`Fragmentizer::decode_message`] /// to decode the message. Alternatively, you can access all bytes of the message via /// [`Fragmentizer::message_bytes`]. pub fn is_message_complete(&self) -> bool { self.parser.is_none() } /// Returns the bytes of the current message. /// /// Note that the bytes might be incomplete: /// - The message might not be fully parsed yet and [`Fragmentizer::progress`] need to be /// called. You can check whether the message is complete via /// [`Fragmentizer::is_message_complete`]. /// - The size limit might be exceeded and bytes might be dropped. You can check this /// via [`Fragmentizer::is_max_message_size_exceeded`] pub fn message_bytes(&self) -> &[u8] { &self.message_buffer } /// Returns whether the size limit is exceeded for the current message. pub fn is_max_message_size_exceeded(&self) -> bool { self.max_message_size_exceeded } /// Returns whether the current message was explicitly poisoned to prevent decoding. pub fn is_message_poisoned(&self) -> bool { self.message_poisoned } /// Skips the current message and starts the next message immediately. /// /// Warning: Using this method might be dangerous. If client and server don't /// agree at which point a message is skipped, then the client or server might /// treat untrusted bytes (e.g. literal bytes) as IMAP messages. Currently the /// only valid use-case is a server that rejects synchronizing literals from the /// client. Otherwise consider using [`Fragmentizer::poison_message`]. pub fn skip_message(&mut self) { self.max_message_size_exceeded = false; self.message_poisoned = false; self.message_buffer.clear(); self.parser = Some(Parser::Line(LineParser::new(0))); } /// Poisons the current message to prevent its decoding. /// /// When this function is called the fragments of the current message are parsed normally, but /// [`Fragmentizer::decode_message`] is guaranteed to fail and return /// [`DecodeMessageError::MessagePoisoned`]. This allows to skip malformed messages (e.g. /// a message with an unexpected line ending) safely without the risk of treating untrusted /// bytes (e.g. literal bytes) as IMAP messages pub fn poison_message(&mut self) { self.message_poisoned = true; } /// Tries to decode the [`Tag`] for the current message. /// /// Note that decoding the [`Tag`] is on best effort basis. Some message types don't have /// a [`Tag`] and without context you can't know whether this function will succeed. However, /// this function is useful if the message is incomplete or malformed and you want to decode /// the [`Tag`] in order to send a response. pub fn decode_tag(&self) -> Option { parse_tag(&self.message_buffer) } /// Tries to decode the current message with the given decoder. /// /// You usually want to call this method once [`Fragmentizer::is_message_complete`] returns /// true. Which decoder should be used depends on the state of the IMAP conversation. The /// caller is responsible for tracking this state and choosing the decoder. pub fn decode_message<'a, C: Decoder>( &'a self, codec: &C, ) -> Result, DecodeMessageError<'a, C>> { if self.max_message_size_exceeded { return Err(DecodeMessageError::MessageTooLong { initial: Secret::new(&self.message_buffer), }); } if self.message_poisoned { return Err(DecodeMessageError::MessagePoisoned { discarded: Secret::new(&self.message_buffer), }); } let (remainder, message) = match codec.decode(&self.message_buffer) { Ok(res) => res, Err(err) => return Err(DecodeMessageError::DecodingFailure(err)), }; if !remainder.is_empty() { return Err(DecodeMessageError::DecodingRemainder { message, remainder: Secret::new(remainder), }); } Ok(message) } fn dequeue_parsed_bytes(&mut self, parsed_byte_count: usize) { // This will remove the parsed bytes even if we don't add them to the message buffer let parsed_bytes = self.unparsed_buffer.drain(..parsed_byte_count); // How many bytes can we add to the message buffer? let remaining_size = self .max_message_size .map(|size| size as usize - self.message_buffer.len()); // Add bytes to the message buffer match remaining_size { Some(remaining_size) if remaining_size < parsed_byte_count => { let remaining_bytes = parsed_bytes.take(remaining_size); self.message_buffer.extend(remaining_bytes); self.max_message_size_exceeded = true; } _ => { self.message_buffer.extend(parsed_bytes); } } } } /// Stateful parser for the next fragment. #[derive(Clone, Debug)] enum Parser { Line(LineParser), Literal(LiteralParser), } /// Stateful parser for the next line fragment. #[derive(Clone, Debug)] struct LineParser { /// Where we started parsing the line. start: usize, /// Until where we parsed the line. end: usize, /// Accumulated state based on the parsed bytes. latest_byte: LatestByte, } impl LineParser { fn new(start: usize) -> Self { Self { start, end: start, latest_byte: LatestByte::Other, } } fn parse(&mut self, unprocessed_bytes: &VecDeque) -> (usize, Option) { let mut parsed_byte_count = 0; let mut parsed_line = None; // Parse next byte for &next_byte in unprocessed_bytes { parsed_byte_count += 1; self.end += 1; self.latest_byte = match self.latest_byte { LatestByte::Other => match next_byte { b'\r' => LatestByte::Cr { announcement: None }, b'\n' => { parsed_line = Some(FragmentInfo::Line { start: self.start, end: self.end, announcement: None, ending: LineEnding::Lf, }); LatestByte::Other } b'{' => LatestByte::OpeningBracket, _ => LatestByte::Other, }, LatestByte::OpeningBracket => match next_byte { b'\r' => LatestByte::Cr { announcement: None }, b'\n' => { parsed_line = Some(FragmentInfo::Line { start: self.start, end: self.end, announcement: None, ending: LineEnding::Lf, }); LatestByte::Other } b'{' => LatestByte::OpeningBracket, b'0'..=b'9' => { let digit = (next_byte - b'0') as u32; LatestByte::Digit { length: digit } } _ => LatestByte::Other, }, LatestByte::Plus { length } => match next_byte { b'\r' => LatestByte::Cr { announcement: None }, b'\n' => { parsed_line = Some(FragmentInfo::Line { start: self.start, end: self.end, announcement: None, ending: LineEnding::Lf, }); LatestByte::Other } b'{' => LatestByte::OpeningBracket, b'}' => LatestByte::ClosingBracket { announcement: LiteralAnnouncement { mode: LiteralMode::NonSync, length, }, }, _ => LatestByte::Other, }, LatestByte::Digit { length } => match next_byte { b'\r' => LatestByte::Cr { announcement: None }, b'\n' => { parsed_line = Some(FragmentInfo::Line { start: self.start, end: self.end, announcement: None, ending: LineEnding::Lf, }); LatestByte::Other } b'{' => LatestByte::OpeningBracket, b'0'..=b'9' => { let digit = (next_byte - b'0') as u32; let new_length = length.checked_mul(10).and_then(|x| x.checked_add(digit)); match new_length { None => LatestByte::Other, Some(length) => LatestByte::Digit { length }, } } b'+' => LatestByte::Plus { length }, b'}' => LatestByte::ClosingBracket { announcement: LiteralAnnouncement { mode: LiteralMode::Sync, length, }, }, _ => LatestByte::Other, }, LatestByte::ClosingBracket { announcement } => match next_byte { b'\r' => LatestByte::Cr { announcement: Some(announcement), }, b'\n' => { parsed_line = Some(FragmentInfo::Line { start: self.start, end: self.end, announcement: Some(announcement), ending: LineEnding::Lf, }); LatestByte::Other } b'{' => LatestByte::OpeningBracket, _ => LatestByte::Other, }, LatestByte::Cr { announcement } => match next_byte { b'\r' => LatestByte::Cr { announcement: None }, b'\n' => { parsed_line = Some(FragmentInfo::Line { start: self.start, end: self.end, announcement, ending: LineEnding::CrLf, }); LatestByte::Other } b'{' => LatestByte::OpeningBracket, _ => LatestByte::Other, }, }; if parsed_line.is_some() { // We parsed a complete line break; } } (parsed_byte_count, parsed_line) } } /// The latest byte seen by the [`LineParser`] with additional accumulated state. #[derive(Clone, Debug)] enum LatestByte { Other, OpeningBracket, Digit { length: u32, }, Plus { length: u32, }, ClosingBracket { announcement: LiteralAnnouncement, }, Cr { announcement: Option, }, } /// Stateful parser for the next literal fragment. #[derive(Clone, Debug)] struct LiteralParser { /// Where we started parsing the literal. start: usize, /// Until where we parsed the literal. end: usize, /// Remaining bytes we need to parse. remaining: u32, } impl LiteralParser { fn new(start: usize, length: u32) -> Self { Self { start, end: start, remaining: length, } } fn parse(&mut self, unprocessed_bytes: &VecDeque) -> (usize, Option) { if unprocessed_bytes.len() < self.remaining as usize { // Not enough bytes yet let parsed_byte_count = unprocessed_bytes.len(); self.end += parsed_byte_count; self.remaining -= parsed_byte_count as u32; (parsed_byte_count, None) } else { // There are enough bytes let parsed_byte_count = self.remaining as usize; self.end += parsed_byte_count; self.remaining = 0; let parsed_literal = FragmentInfo::Literal { start: self.start, end: self.end, }; (parsed_byte_count, Some(parsed_literal)) } } } /// Describes a fragment of the current message found by [`Fragmentizer::progress`]. /// /// The corresponding bytes can be retrieved via [`Fragmentizer::fragment_bytes`] /// until [`Fragmentizer::is_message_complete`] returns true. After that the /// next call of [`Fragmentizer::progress`] will start the next message. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum FragmentInfo { /// The fragment is a line. Line { /// Inclusive start index relative to the current message. start: usize, /// Exclusive end index relative to the current message. end: usize, /// Whether the next fragment will be a literal. announcement: Option, /// The detected ending sequence for this line. ending: LineEnding, }, /// The fragment is a literal. Literal { /// Inclusive start index relative to the current message. start: usize, /// Exclusive end index relative to the current message. end: usize, }, } impl FragmentInfo { /// The index range relative to the current message. pub fn range(self) -> Range { match self { FragmentInfo::Line { start, end, .. } => start..end, FragmentInfo::Literal { start, end } => start..end, } } } /// Used by a line to announce a literal following the line. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct LiteralAnnouncement { /// The mode of the announced literal. pub mode: LiteralMode, /// The length of the announced literal in bytes. pub length: u32, } /// The character sequence used for ending a line. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum LineEnding { /// The line ends with the character `\n`. Lf, /// The line ends with the character sequence `\r\n`. CrLf, } /// An error returned by [`Fragmentizer::decode_message`]. #[derive(Clone, Debug, Eq, PartialEq)] pub enum DecodeMessageError<'a, C: Decoder> { /// The decoder failed decoding the message. DecodingFailure(C::Error<'a>), /// Not all bytes of the message were used when decoding the message. DecodingRemainder { /// The decoded message. message: C::Message<'a>, /// The unused bytes. remainder: Secret<&'a [u8]>, }, /// Max message size was exceeded and bytes were dropped. MessageTooLong { initial: Secret<&'a [u8]> }, /// The message was explicitly poisoned to prevent decoding. MessagePoisoned { discarded: Secret<&'a [u8]> }, } fn parse_tag(message_bytes: &[u8]) -> Option { let mut bytes = message_bytes.iter().enumerate(); let sp = loop { let (i, byte) = bytes.next()?; match byte { // A tag is always delimited by SP b' ' => break i, // End of line reached b'\n' => return None, // Parse more bytes _ => continue, } }; Tag::try_from(&message_bytes[..sp]).ok() } #[cfg(test)] mod tests { use core::panic; use std::collections::VecDeque; use imap_types::{ command::{Command, CommandBody}, core::{LiteralMode, Tag}, secret::Secret, }; use super::{ FragmentInfo, Fragmentizer, LineEnding, LineParser, LiteralAnnouncement, parse_tag, }; use crate::{ CommandCodec, ResponseCodec, decode::ResponseDecodeError, fragmentizer::DecodeMessageError, }; #[test] fn fragmentizer_progress_nothing() { let mut fragmentizer = Fragmentizer::without_max_message_size(); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); fragmentizer.enqueue_bytes(&[]); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[test] fn fragmentizer_progress_single_message() { let mut fragmentizer = Fragmentizer::without_max_message_size(); fragmentizer.enqueue_bytes(b"* OK ...\r\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 10, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"* OK ...\r\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[test] fn fragmentizer_progress_multiple_messages() { let mut fragmentizer = Fragmentizer::without_max_message_size(); fragmentizer.enqueue_bytes(b"A1 OK ...\r\n"); fragmentizer.enqueue_bytes(b"A2 BAD ...\r\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 11, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 OK ...\r\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 12, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A2 BAD ...\r\n" ); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[test] fn fragmentizer_progress_multiple_messages_with_lf() { let mut fragmentizer = Fragmentizer::without_max_message_size(); fragmentizer.enqueue_bytes(b"A1 NOOP\n"); fragmentizer.enqueue_bytes(b"A2 LOGIN {5}\n"); fragmentizer.enqueue_bytes(b"ABCDE"); fragmentizer.enqueue_bytes(b" EFGIJ\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 8, announcement: None, ending: LineEnding::Lf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NOOP\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 13, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5 }), ending: LineEnding::Lf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A2 LOGIN {5}\n" ); assert!(!fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 13, end: 18 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE"); assert!(!fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 18, end: 25, announcement: None, ending: LineEnding::Lf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" EFGIJ\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[test] fn fragmentizer_progress_message_with_multiple_literals() { let mut fragmentizer = Fragmentizer::without_max_message_size(); fragmentizer.enqueue_bytes(b"A1 LOGIN {5}\r\n"); fragmentizer.enqueue_bytes(b"ABCDE"); fragmentizer.enqueue_bytes(b" {5}\r\n"); fragmentizer.enqueue_bytes(b"FGHIJ"); fragmentizer.enqueue_bytes(b"\r\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5, }), ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A1 LOGIN {5}\r\n" ); assert!(!fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE"); assert!(!fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 19, end: 25, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5, }), ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" {5}\r\n"); assert!(!fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 25, end: 30 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"FGHIJ"); assert!(!fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 30, end: 32, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"\r\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[test] fn fragmentizer_progress_message_and_skip_after_literal_announcement() { let mut fragmentizer = Fragmentizer::without_max_message_size(); fragmentizer.enqueue_bytes(b"A1 LOGIN {5}\r\n"); fragmentizer.enqueue_bytes(b"A2 NOOP\r\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5, }), ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A1 LOGIN {5}\r\n" ); assert!(!fragmentizer.is_message_complete()); fragmentizer.skip_message(); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 9, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A2 NOOP\r\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[test] fn fragmentizer_progress_message_byte_by_byte() { let mut fragmentizer = Fragmentizer::without_max_message_size(); let mut bytes = VecDeque::new(); bytes.extend(b"A1 LOGIN {5}\r\n"); bytes.extend(b"ABCDE"); bytes.extend(b" FGHIJ\r\n"); for _ in 0..14 { let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert!(!fragmentizer.is_message_complete()); fragmentizer.enqueue_bytes(&[bytes.pop_front().unwrap()]); } let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5, }), ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A1 LOGIN {5}\r\n" ); assert!(!fragmentizer.is_message_complete()); for _ in 0..5 { let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert!(!fragmentizer.is_message_complete()); fragmentizer.enqueue_bytes(&[bytes.pop_front().unwrap()]); } let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE"); assert!(!fragmentizer.is_message_complete()); for _ in 0..8 { let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert!(!fragmentizer.is_message_complete()); fragmentizer.enqueue_bytes(&[bytes.pop_front().unwrap()]); } let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 19, end: 27, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" FGHIJ\r\n"); assert!(fragmentizer.is_message_complete()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); } #[track_caller] fn assert_is_line( unprocessed_bytes: &[u8], line_byte_count: usize, expected_announcement: Option, expected_ending: LineEnding, ) { let mut line_parser = LineParser::new(0); let unprocessed_bytes = unprocessed_bytes.iter().copied().collect(); let (parsed_byte_count, fragment_info) = line_parser.parse(&unprocessed_bytes); assert_eq!(parsed_byte_count, line_byte_count); let Some(FragmentInfo::Line { start, end, announcement, ending, }) = fragment_info else { panic!("Unexpected fragment: {fragment_info:?}"); }; assert_eq!(start, 0); assert_eq!(end, line_byte_count); assert_eq!(announcement, expected_announcement); assert_eq!(ending, expected_ending); } #[test] fn fragmentizer_progress_multiple_messages_longer_than_max_size() { let mut fragmentizer = Fragmentizer::new(17); fragmentizer.enqueue_bytes(b"A1 NOOP\r\n"); fragmentizer.enqueue_bytes(b"A2 LOGIN ABCDE EFGIJ\r\n"); fragmentizer.enqueue_bytes(b"A3 LOGIN {5}\r\n"); fragmentizer.enqueue_bytes(b"ABCDE"); fragmentizer.enqueue_bytes(b" EFGIJ\r\n"); fragmentizer.enqueue_bytes(b"A4 LOGIN A B\r\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 9, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NOOP\r\n"); assert_eq!(fragmentizer.message_bytes(), b"A1 NOOP\r\n"); assert!(fragmentizer.is_message_complete()); assert!(!fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 22, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A2 LOGIN ABCDE EF" ); assert_eq!(fragmentizer.message_bytes(), b"A2 LOGIN ABCDE EF"); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5 }), ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A3 LOGIN {5}\r\n" ); assert_eq!(fragmentizer.message_bytes(), b"A3 LOGIN {5}\r\n"); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABC"); assert_eq!(fragmentizer.message_bytes(), b"A3 LOGIN {5}\r\nABC"); assert!(!fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 19, end: 27, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b""); assert_eq!(fragmentizer.message_bytes(), b"A3 LOGIN {5}\r\nABC"); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A4 LOGIN A B\r\n" ); assert_eq!(fragmentizer.message_bytes(), b"A4 LOGIN A B\r\n"); assert!(fragmentizer.is_message_complete()); assert!(!fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_max_message_size_exceeded()); } #[test] fn fragmentizer_progress_messages_with_zero_max_size() { let mut fragmentizer = Fragmentizer::new(0); fragmentizer.enqueue_bytes(b"A1 NOOP\r\n"); fragmentizer.enqueue_bytes(b"A2 LOGIN ABCDE EFGIJ\r\n"); fragmentizer.enqueue_bytes(b"A3 LOGIN {5}\r\n"); fragmentizer.enqueue_bytes(b"ABCDE"); fragmentizer.enqueue_bytes(b" EFGIJ\r\n"); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 9, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 22, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5 }), ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 19, end: 27, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_max_message_size_exceeded()); } #[test] fn fragmentizer_decode_message() { let command_codec = CommandCodec::new(); let response_codec = ResponseCodec::new(); let mut fragmentizer = Fragmentizer::new(10); fragmentizer.enqueue_bytes(b"A1 NOOP\r\n"); fragmentizer.enqueue_bytes(b"A2 LOGIN ABCDE EFGIJ\r\n"); fragmentizer.progress(); assert_eq!( fragmentizer.decode_message(&command_codec), Ok(Command::new("A1", CommandBody::Noop).unwrap()), ); assert_eq!( fragmentizer.decode_message(&response_codec), Err(DecodeMessageError::DecodingFailure( ResponseDecodeError::Failed )), ); fragmentizer.progress(); assert_eq!( fragmentizer.decode_message(&response_codec), Err(DecodeMessageError::MessageTooLong { initial: Secret::new(b"A2 LOGIN A"), }), ); } #[test] fn fragmentizer_poison_message() { let command_codec = CommandCodec::new(); let mut fragmentizer = Fragmentizer::without_max_message_size(); fragmentizer.enqueue_bytes(b"A1 NOOP\r\n"); fragmentizer.enqueue_bytes(b"A2 LOGIN {5}\r\n"); fragmentizer.enqueue_bytes(b"ABCDE"); fragmentizer.enqueue_bytes(b" EFGIJ\r\n"); assert!(!fragmentizer.is_message_poisoned()); fragmentizer.poison_message(); assert!(fragmentizer.is_message_poisoned()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 9, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NOOP\r\n"); assert_eq!(fragmentizer.message_bytes(), b"A1 NOOP\r\n"); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_message_poisoned()); let decode_err = fragmentizer.decode_message(&command_codec).unwrap_err(); assert_eq!( decode_err, DecodeMessageError::MessagePoisoned { discarded: Secret::new(fragmentizer.message_bytes()) } ); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 14, announcement: Some(LiteralAnnouncement { mode: LiteralMode::Sync, length: 5 }), ending: LineEnding::CrLf, } ); assert_eq!( fragmentizer.fragment_bytes(fragment_info), b"A2 LOGIN {5}\r\n" ); assert_eq!(fragmentizer.message_bytes(), b"A2 LOGIN {5}\r\n"); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_message_poisoned()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!(fragment_info, FragmentInfo::Literal { start: 14, end: 19 }); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"ABCDE"); assert_eq!(fragmentizer.message_bytes(), b"A2 LOGIN {5}\r\nABCDE"); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_message_poisoned()); fragmentizer.poison_message(); assert!(fragmentizer.is_message_poisoned()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 19, end: 27, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b" EFGIJ\r\n"); assert_eq!( fragmentizer.message_bytes(), b"A2 LOGIN {5}\r\nABCDE EFGIJ\r\n" ); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_message_poisoned()); let decode_err = fragmentizer.decode_message(&command_codec).unwrap_err(); assert_eq!( decode_err, DecodeMessageError::MessagePoisoned { discarded: Secret::new(fragmentizer.message_bytes()) } ); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_message_poisoned()); } #[test] fn fragmentizer_poison_too_long_message() { let command_codec = CommandCodec::new(); let mut fragmentizer = Fragmentizer::new(5); fragmentizer.enqueue_bytes(b"A1 NOOP\r\n"); assert!(!fragmentizer.is_message_poisoned()); fragmentizer.poison_message(); assert!(fragmentizer.is_message_poisoned()); let fragment_info = fragmentizer.progress().unwrap(); assert_eq!( fragment_info, FragmentInfo::Line { start: 0, end: 9, announcement: None, ending: LineEnding::CrLf, } ); assert_eq!(fragmentizer.fragment_bytes(fragment_info), b"A1 NO"); assert_eq!(fragmentizer.message_bytes(), b"A1 NO"); assert!(fragmentizer.is_message_complete()); assert!(fragmentizer.is_max_message_size_exceeded()); assert!(fragmentizer.is_message_poisoned()); let decode_err = fragmentizer.decode_message(&command_codec).unwrap_err(); assert_eq!( decode_err, DecodeMessageError::MessageTooLong { initial: Secret::new(b"A1 NO") } ); let fragment_info = fragmentizer.progress(); assert_eq!(fragment_info, None); assert_eq!(fragmentizer.message_bytes(), b""); assert_eq!(fragmentizer.message_bytes(), b""); assert!(!fragmentizer.is_message_complete()); assert!(!fragmentizer.is_max_message_size_exceeded()); assert!(!fragmentizer.is_message_poisoned()); } #[track_caller] fn assert_not_line(not_a_line_bytes: &[u8]) { let mut line_parser = LineParser::new(0); let not_a_line_bytes = not_a_line_bytes.iter().copied().collect(); let (parsed_byte_count, fragment_info) = line_parser.parse(¬_a_line_bytes); assert_eq!(parsed_byte_count, not_a_line_bytes.len()); assert_eq!(fragment_info, None); } #[test] fn parse_line_examples() { assert_not_line(b""); assert_not_line(b"foo"); assert_is_line(b"\n", 1, None, LineEnding::Lf); assert_is_line(b"\r\n", 2, None, LineEnding::CrLf); assert_is_line(b"\n\r", 1, None, LineEnding::Lf); assert_is_line(b"foo\n", 4, None, LineEnding::Lf); assert_is_line(b"foo\r\n", 5, None, LineEnding::CrLf); assert_is_line(b"foo\n\r", 4, None, LineEnding::Lf); assert_is_line(b"foo\nbar\n", 4, None, LineEnding::Lf); assert_is_line(b"foo\r\nbar\r\n", 5, None, LineEnding::CrLf); assert_is_line(b"\r\nfoo\r\n", 2, None, LineEnding::CrLf); assert_is_line( b"{1}\r\n", 5, Some(LiteralAnnouncement { length: 1, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line( b"{1}\n", 4, Some(LiteralAnnouncement { length: 1, mode: LiteralMode::Sync, }), LineEnding::Lf, ); assert_is_line( b"foo {1}\r\n", 9, Some(LiteralAnnouncement { length: 1, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line( b"foo {2} {1}\r\n", 13, Some(LiteralAnnouncement { length: 1, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line(b"foo {1} \r\n", 10, None, LineEnding::CrLf); assert_is_line(b"foo \n {1}\r\n", 5, None, LineEnding::Lf); assert_is_line(b"foo {1} foo\r\n", 13, None, LineEnding::CrLf); assert_is_line(b"foo {1\r\n", 8, None, LineEnding::CrLf); assert_is_line(b"foo 1}\r\n", 8, None, LineEnding::CrLf); assert_is_line(b"foo { 1}\r\n", 10, None, LineEnding::CrLf); assert_is_line( b"foo {{1}\r\n", 10, Some(LiteralAnnouncement { length: 1, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line( b"foo {42}\r\n", 10, Some(LiteralAnnouncement { length: 42, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line( b"foo {42+}\r\n", 11, Some(LiteralAnnouncement { length: 42, mode: LiteralMode::NonSync, }), LineEnding::CrLf, ); assert_is_line( b"foo +{42}\r\n", 11, Some(LiteralAnnouncement { length: 42, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line(b"foo {+}\r\n", 9, None, LineEnding::CrLf); assert_is_line(b"foo {42++}\r\n", 12, None, LineEnding::CrLf); assert_is_line(b"foo {+42+}\r\n", 12, None, LineEnding::CrLf); assert_is_line(b"foo {+42}\r\n", 11, None, LineEnding::CrLf); assert_is_line(b"foo {42}+\r\n", 11, None, LineEnding::CrLf); assert_is_line(b"foo {-42}\r\n", 11, None, LineEnding::CrLf); assert_is_line(b"foo {42-}\r\n", 11, None, LineEnding::CrLf); assert_is_line( b"foo {4294967295}\r\n", 18, Some(LiteralAnnouncement { length: 4294967295, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); assert_is_line(b"foo {4294967296}\r\n", 18, None, LineEnding::CrLf); } #[test] fn parse_line_corner_case() { // According to the IMAP RFC, this line does not announce a literal. // We thought intensively about this corner case and asked different people. // Our conclusion: This corner case is an oversight of the RFC authors and // doesn't appear in the wild. We ignore it for now. If this becomes an issue // in practice then we should implement a detection for "* OK", "* NO" and // "* BAD". // See https://github.com/duesee/imap-codec/issues/432#issuecomment-1962427538 assert_is_line( b"* OK {1}\r\n", 10, Some(LiteralAnnouncement { length: 1, mode: LiteralMode::Sync, }), LineEnding::CrLf, ); } #[test] fn parse_tag_examples() { assert_eq!(parse_tag(b"1 NOOP\r\n"), Tag::try_from("1").ok()); assert_eq!(parse_tag(b"12 NOOP\r\n"), Tag::try_from("12").ok()); assert_eq!(parse_tag(b"123 NOOP\r\n"), Tag::try_from("123").ok()); assert_eq!(parse_tag(b"1234 NOOP\r\n"), Tag::try_from("1234").ok()); assert_eq!(parse_tag(b"12345 NOOP\r\n"), Tag::try_from("12345").ok()); assert_eq!(parse_tag(b"A1 NOOP\r\n"), Tag::try_from("A1").ok()); assert_eq!(parse_tag(b"A1 NOOP"), Tag::try_from("A1").ok()); assert_eq!(parse_tag(b"A1 "), Tag::try_from("A1").ok()); assert_eq!(parse_tag(b"A1 "), Tag::try_from("A1").ok()); assert_eq!(parse_tag(b"A1 \r\n"), Tag::try_from("A1").ok()); assert_eq!(parse_tag(b"A1 \n"), Tag::try_from("A1").ok()); assert_eq!(parse_tag(b"A1"), None); assert_eq!(parse_tag(b"A1\r\n"), None); assert_eq!(parse_tag(b"A1\n"), None); assert_eq!(parse_tag(b" \r\n"), None); assert_eq!(parse_tag(b"\r\n"), None); assert_eq!(parse_tag(b""), None); assert_eq!(parse_tag(b" A1 NOOP\r\n"), None); } } duesee-imap-codec-0d00966/imap-codec/src/lib.rs000066400000000000000000000114011507724125200211430ustar00rootroot00000000000000//! # IMAP protocol library //! //! imap-codec provides complete and detailed parsing and construction of [IMAP4rev1] commands and responses. //! It is based on [imap-types] and extends it with parsing support using [nom]. //! //! The main codecs are //! [`GreetingCodec`] (to parse the first message from a server), //! [`CommandCodec`] (to parse commands from a client), and //! [`ResponseCodec`] (to parse responses or results from a server). //! //! Note that IMAP traces are not guaranteed to be UTF-8. //! Thus, be careful when using code like `from_utf8(...)`. //! //! ## Decoding //! //! Decoding is provided through the [`Decoder`](`crate::decode::Decoder`) trait. //! Every parser takes an input (`&[u8]`) and produces a remainder and a parsed value. //! //! **Note:** Decoding IMAP traces is more elaborate than it seems on a first glance. //! Please consult the [`decode`](`crate::decode`) module documentation to learn how to handle real-world decoding. //! //! ### Example //! //! ```rust //! use imap_codec::{ //! GreetingCodec, //! decode::Decoder, //! imap_types::{ //! core::Text, //! response::{Code, Greeting, GreetingKind}, //! }, //! }; //! //! let (remaining, greeting) = GreetingCodec::default() //! .decode(b"* OK [ALERT] Hello, World!\r\n") //! .unwrap(); //! //! assert_eq!( //! greeting, //! Greeting { //! kind: GreetingKind::Ok, //! code: Some(Code::Alert), //! text: Text::try_from("Hello, World!").unwrap(), //! } //! ); //! assert_eq!(remaining, &b""[..]) //! ``` //! //! ## Encoding //! //! Encoding is provided through the [`Encoder`](`crate::encode::Encoder`) trait. //! //! **Note:** Encoding IMAP traces is more elaborate than it seems on a first glance. //! Please consult the [`encode`](`crate::encode`) module documentation to learn how to handle real-world encoding. //! //! ### Example //! //! ```rust //! use imap_codec::{ //! GreetingCodec, //! encode::Encoder, //! imap_types::{ //! core::Text, //! response::{Code, Greeting, GreetingKind}, //! }, //! }; //! //! let greeting = Greeting { //! kind: GreetingKind::Ok, //! code: Some(Code::Alert), //! text: Text::try_from("Hello, World!").unwrap(), //! }; //! //! let bytes = GreetingCodec::default().encode(&greeting).dump(); //! //! assert_eq!(bytes, &b"* OK [ALERT] Hello, World!\r\n"[..]); //! ``` //! //! ## Features //! //! imap-codec forwards many features to imap-types. See [imap-types features] for a comprehensive list. //! //! In addition, imap-codec defines the following features: //! //! | Feature | Description | Enabled by default | //! |-----------------------|--------------------------------|--------------------| //! | quirk_crlf_relaxed | Make `\r` in `\r\n` optional. | No | //! | quirk_rectify_numbers | Rectify (invalid) numbers. | No | //! | quirk_missing_text | Rectify missing `text` element.| No | //! //! ## Quirks //! //! Features starting with `quirk_` are used to cope with existing interoperability issues. //! Unfortunately, we already observed some standard violations, such as, negative numbers, and missing syntax elements. //! Our policy is as follows: If we see an interoperability issue, we file an issue in the corresponding implementation. //! If, for any reason, the issue cannot be fixed, *and* the implementation is "important enough", e.g., because a user of //! imap-codec can't otherwise access their emails, we may add a `quirk_` feature to quickly resolve the problem. //! Of course, imap-codec should never violate the IMAP standard itself. So, we need to do this carefully. //! //! [imap-types]: https://docs.rs/imap-types/latest/imap_types //! [imap-types features]: https://docs.rs/imap-types/latest/imap_types/#features //! [IMAP4rev1]: https://tools.ietf.org/html/rfc3501 //! [parse_command]: https://github.com/duesee/imap-codec/blob/main/examples/parse_command.rs // TODO(#660) #![allow(unknown_lints)] #![allow(mismatched_lifetime_syntaxes)] #![forbid(unsafe_code)] #![deny(missing_debug_implementations)] #![cfg_attr(docsrs, feature(doc_cfg))] // Test examples from repository root README. #[doc = include_str!("../../README.md")] #[cfg(doctest)] pub struct ReadmeDoctestsRoot; // Test examples from imap-codec's README. #[doc = include_str!("../README.md")] #[cfg(doctest)] pub struct ReadmeDoctests; mod auth; mod body; mod codec; mod command; mod core; mod datetime; mod envelope; mod extensions; mod fetch; mod flag; mod mailbox; mod response; mod search; mod sequence; mod status; #[cfg(test)] mod testing; pub mod fragmentizer; #[cfg(feature = "fuzz")] pub mod fuzz { pub use crate::core::fuzz_tag_imap; } pub use codec::*; // Re-export. pub use imap_types; duesee-imap-codec-0d00966/imap-codec/src/mailbox.rs000066400000000000000000000170201507724125200220330ustar00rootroot00000000000000use abnf_core::streaming::{dquote, sp}; use imap_types::{ core::QuotedChar, flag::FlagNameAttribute, mailbox::{ListCharString, ListMailbox, Mailbox}, response::Data, utils::indicators::is_list_char, }; #[cfg(feature = "ext_condstore_qresync")] use nom::character::streaming::char; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case, take_while1}, combinator::{map, opt, value}, multi::many0, sequence::{delimited, preceded, terminated, tuple}, }; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::search_sort_mod_seq; #[cfg(feature = "ext_metadata")] use crate::extensions::metadata::metadata_resp; #[cfg(feature = "ext_namespace")] use crate::extensions::namespace::namespace_response; use crate::{ core::{astring, nil, number, nz_number, quoted_char, string}, decode::IMAPResult, extensions::{ quota::{quota_response, quotaroot_response}, thread::thread_data, }, flag::{flag_list, mbx_list_flags}, status::status_att_list, }; /// `list-mailbox = 1*list-char / string` pub(crate) fn list_mailbox(input: &[u8]) -> IMAPResult<&[u8], ListMailbox> { alt(( map(take_while1(is_list_char), |bytes: &[u8]| { // # Safety // // `unwrap` is safe here, because `is_list_char` enforces that the bytes ... // * contain ASCII-only characters, i.e., `from_utf8` will return `Ok`. // * are valid according to `ListCharString::verify()`, i.e., `unvalidated` is safe. ListMailbox::Token(ListCharString::unvalidated( std::str::from_utf8(bytes).unwrap(), )) }), map(string, ListMailbox::String), ))(input) } /// `mailbox = "INBOX" / astring` /// /// INBOX is case-insensitive. All case variants of INBOX (e.g., "iNbOx") /// MUST be interpreted as INBOX not as an astring. /// /// An astring which consists of the case-insensitive sequence /// "I" "N" "B" "O" "X" is considered to be INBOX and not an astring. /// /// Refer to section 5.1 for further semantic details of mailbox names. pub(crate) fn mailbox(input: &[u8]) -> IMAPResult<&[u8], Mailbox> { map(astring, Mailbox::from)(input) } /// ```abnf /// mailbox-data = "FLAGS" SP flag-list / /// "LIST" SP mailbox-list / /// "LSUB" SP mailbox-list / /// "SEARCH" *(SP nz-number) [SP search-sort-mod-seq] / /// ^^^^^^^^^^^^^^^^^^^^^^^^ /// | /// RFC 7162 (edited) /// "STATUS" SP mailbox SP "(" [status-att-list] ")" / /// "METADATA" SP mailbox SP (entry-values / entry-list) / ; RFC 5464 /// number SP "EXISTS" / /// number SP "RECENT" /// ``` /// /// FROM RFC 7162 (CONDSTORE/QRESYNC): /// /// ```abnf /// mailbox-data =/ "SEARCH" [1*(SP nz-number) SP search-sort-mod-seq] /// /// search-sort-mod-seq = "(" "MODSEQ" SP mod-sequence-value ")" /// ``` pub(crate) fn mailbox_data(input: &[u8]) -> IMAPResult<&[u8], Data> { alt(( map(preceded(tag_no_case(b"FLAGS "), flag_list), Data::Flags), map( preceded(tag_no_case(b"LIST "), mailbox_list), |(items, delimiter, mailbox)| Data::List { items: items.unwrap_or_default(), mailbox, delimiter, }, ), map( preceded(tag_no_case(b"LSUB "), mailbox_list), |(items, delimiter, mailbox)| Data::Lsub { items: items.unwrap_or_default(), mailbox, delimiter, }, ), #[cfg(not(feature = "ext_condstore_qresync"))] map( #[cfg(not(feature = "quirk_trailing_space_search"))] tuple((tag_no_case(b"SEARCH"), many0(preceded(sp, nz_number)))), #[cfg(feature = "quirk_trailing_space_search")] tuple(( tag_no_case(b"SEARCH"), many0(preceded(sp, nz_number)), opt(sp), )), #[cfg(not(feature = "quirk_trailing_space_search"))] |(_, nums)| Data::Search(nums), #[cfg(feature = "quirk_trailing_space_search")] |(_, nums, _)| Data::Search(nums), ), #[cfg(feature = "ext_condstore_qresync")] map( #[cfg(not(feature = "quirk_trailing_space_search"))] tuple(( tag_no_case(b"SEARCH"), many0(preceded(sp, nz_number)), opt(preceded(char(' '), search_sort_mod_seq)), )), #[cfg(feature = "quirk_trailing_space_search")] tuple(( tag_no_case(b"SEARCH"), many0(preceded(sp, nz_number)), opt(preceded(char(' '), search_sort_mod_seq)), opt(sp), )), #[cfg(not(feature = "quirk_trailing_space_search"))] |(_, nums, modseq)| Data::Search(nums, modseq), #[cfg(feature = "quirk_trailing_space_search")] |(_, nums, modseq, _)| Data::Search(nums, modseq), ), #[cfg(not(feature = "ext_condstore_qresync"))] map( preceded(tag_no_case(b"SORT"), many0(preceded(sp, nz_number))), Data::Sort, ), #[cfg(feature = "ext_condstore_qresync")] map( tuple(( tag_no_case(b"SORT"), many0(preceded(sp, nz_number)), opt(preceded(char(' '), search_sort_mod_seq)), )), |(_, nums, modseq)| Data::Sort(nums, modseq), ), thread_data, map( tuple(( tag_no_case(b"STATUS "), mailbox, delimited(tag(b" ("), opt(status_att_list), tag(b")")), #[cfg(feature = "quirk_trailing_space_status")] opt(sp), #[cfg(not(feature = "quirk_trailing_space_status"))] nom::combinator::success(()), )), |(_, mailbox, items, _)| Data::Status { mailbox, items: items.unwrap_or_default().into(), }, ), #[cfg(feature = "ext_metadata")] metadata_resp, #[cfg(feature = "ext_namespace")] namespace_response, map(terminated(number, tag_no_case(b" EXISTS")), Data::Exists), map(terminated(number, tag_no_case(b" RECENT")), Data::Recent), quotaroot_response, quota_response, ))(input) } /// `mailbox-list = "(" [mbx-list-flags] ")" SP /// (DQUOTE QUOTED-CHAR DQUOTE / nil) SP /// mailbox` #[allow(clippy::type_complexity)] pub(crate) fn mailbox_list( input: &[u8], ) -> IMAPResult<&[u8], (Option>, Option, Mailbox)> { let mut parser = tuple(( delimited(tag(b"("), opt(mbx_list_flags), tag(b")")), sp, alt(( map(delimited(dquote, quoted_char, dquote), Option::Some), value(None, nil), )), sp, mailbox, )); let (remaining, (mbx_list_flags, _, maybe_delimiter, _, mailbox)) = parser(input)?; Ok((remaining, (mbx_list_flags, maybe_delimiter, mailbox))) } #[cfg(test)] mod tests { use super::*; #[test] fn test_mailbox() { assert!(mailbox(b"\"iNbOx\"").is_ok()); assert!(mailbox(b"{3}\r\naaa\r\n").is_ok()); assert!(mailbox(b"inbox ").is_ok()); assert!(mailbox(b"inbox.sent ").is_ok()); assert!(mailbox(b"aaa").is_err()); } } duesee-imap-codec-0d00966/imap-codec/src/response.rs000066400000000000000000000671711507724125200222520ustar00rootroot00000000000000#[cfg(not(feature = "quirk_crlf_relaxed"))] use abnf_core::streaming::crlf; #[cfg(feature = "quirk_crlf_relaxed")] use abnf_core::streaming::crlf_relaxed as crlf; use abnf_core::streaming::sp; use base64::{Engine, engine::general_purpose::STANDARD as _base64}; #[cfg(feature = "ext_condstore_qresync")] use imap_types::sequence::SequenceSet; use imap_types::{ core::{Text, Vec1}, fetch::MessageDataItem, response::{ Bye, Capability, Code, CodeOther, CommandContinuationRequest, Data, Greeting, GreetingKind, Response, Status, StatusBody, StatusKind, Tagged, }, }; #[cfg(feature = "quirk_missing_text")] use nom::combinator::peek; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case, take_until, take_while}, combinator::{map, map_res, opt, value}, multi::separated_list1, sequence::{delimited, preceded, terminated, tuple}, }; #[cfg(feature = "ext_id")] use crate::extensions::id::id_response; #[cfg(feature = "ext_metadata")] use crate::extensions::metadata::metadata_code; use crate::{ core::{atom, charset, nz_number, tag_imap, text}, decode::IMAPResult, extensions::{ enable::enable_data, uidplus::{resp_code_apnd, resp_code_copy}, }, fetch::msg_att, flag::flag_perm, mailbox::mailbox_data, }; #[cfg(feature = "ext_condstore_qresync")] use crate::{extensions::condstore_qresync::mod_sequence_value, sequence::sequence_set}; // ----- greeting ----- /// `greeting = "*" SP (resp-cond-auth / resp-cond-bye) CRLF` pub(crate) fn greeting(input: &[u8]) -> IMAPResult<&[u8], Greeting> { let mut parser = delimited( tag(b"* "), alt(( resp_cond_auth, map(resp_cond_bye, |resp_text| (GreetingKind::Bye, resp_text)), )), crlf, ); let (remaining, (kind, (code, text))) = parser(input)?; Ok((remaining, Greeting { kind, code, text })) } /// `resp-cond-auth = ("OK" / "PREAUTH") SP resp-text` /// /// Authentication condition #[allow(clippy::type_complexity)] pub(crate) fn resp_cond_auth( input: &[u8], ) -> IMAPResult<&[u8], (GreetingKind, (Option, Text))> { let mut parser = tuple(( alt(( value(GreetingKind::Ok, tag_no_case(b"OK ")), value(GreetingKind::PreAuth, tag_no_case(b"PREAUTH ")), )), resp_text, )); let (remaining, (kind, resp_text)) = parser(input)?; Ok((remaining, (kind, resp_text))) } /// `resp-text = ["[" resp-text-code "]" SP] text` pub(crate) fn resp_text(input: &[u8]) -> IMAPResult<&[u8], (Option, Text)> { // When the text starts with "[", we insist on parsing a code. // Otherwise, a broken code could be interpreted as text. let (_, start) = opt(tag(b"["))(input)?; if start.is_some() { tuple(( preceded( tag(b"["), map( alt(( terminated(resp_text_code, tag(b"]")), map( terminated( take_while(|b: u8| b != b']' && b != b'\r' && b != b'\n'), tag(b"]"), ), |bytes: &[u8]| Code::Other(CodeOther::unvalidated(bytes)), ), )), Some, ), ), #[cfg(not(feature = "quirk_missing_text"))] preceded(sp, text), #[cfg(feature = "quirk_missing_text")] alt(( preceded(sp, text), map(peek(crlf), |_| { log::warn!("Rectified missing `text` to \"...\""); Text::unvalidated("...") }), )), ))(input) } else { map(text, |text| (None, text))(input) } } /// ```abnf /// resp-text-code = "ALERT" / /// "BADCHARSET" [SP "(" charset *(SP charset) ")" ] / /// capability-data / /// "PARSE" / /// "PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" / /// "READ-ONLY" / /// "READ-WRITE" / /// "TRYCREATE" / /// "UIDNEXT" SP nz-number / /// "UIDVALIDITY" SP nz-number / /// "UNSEEN" SP nz-number / /// "COMPRESSIONACTIVE" / ; RFC 4978 /// "OVERQUOTA" / ; RFC 9208 /// "TOOBIG" / ; RFC 4469 /// "METADATA" SP ( ; RFC 5464 /// "LONGENTRIES" SP number / /// "MAXSIZE" SP number / /// "TOOMANY" / /// "NOPRIVATE" /// ) / /// "UNKNOWN-CTE" / ; RFC 3516 /// "HIGHESTMODSEQ" SP mod-sequence-value / ; RFC7162 /// "NOMODSEQ" / ; RFC7162 /// "MODIFIED" SP sequence-set / ; RFC7162 /// "CLOSED" / ; RFC7162 /// atom [SP 1*] /// ``` /// /// Note: See errata id: 261 pub(crate) fn resp_text_code(input: &[u8]) -> IMAPResult<&[u8], Code> { alt(( value(Code::Alert, tag_no_case(b"ALERT")), map( preceded( tag_no_case(b"BADCHARSET"), opt(delimited( tag(b" ("), separated_list1(sp, charset), tag(b")"), )), ), |maybe_charsets| Code::BadCharset { allowed: maybe_charsets.unwrap_or_default(), }, ), map(capability_data, Code::Capability), value(Code::Parse, tag_no_case(b"PARSE")), map( preceded( tag_no_case(b"PERMANENTFLAGS "), delimited( tag(b"("), map(opt(separated_list1(sp, flag_perm)), |maybe_flags| { maybe_flags.unwrap_or_default() }), tag(b")"), ), ), Code::PermanentFlags, ), value(Code::ReadOnly, tag_no_case(b"READ-ONLY")), value(Code::ReadWrite, tag_no_case(b"READ-WRITE")), value(Code::TryCreate, tag_no_case(b"TRYCREATE")), map(preceded(tag_no_case(b"UIDNEXT "), nz_number), Code::UidNext), map( preceded(tag_no_case(b"UIDVALIDITY "), nz_number), Code::UidValidity, ), map(preceded(tag_no_case(b"UNSEEN "), nz_number), Code::Unseen), value(Code::CompressionActive, tag_no_case(b"COMPRESSIONACTIVE")), value(Code::OverQuota, tag_no_case(b"OVERQUOTA")), value(Code::TooBig, tag_no_case(b"TOOBIG")), #[cfg(feature = "ext_metadata")] map( preceded(tag_no_case("METADATA "), metadata_code), Code::Metadata, ), value(Code::UnknownCte, tag_no_case(b"UNKNOWN-CTE")), resp_code_apnd, resp_code_copy, value(Code::UidNotSticky, tag_no_case(b"UIDNOTSTICKY")), #[cfg(feature = "ext_condstore_qresync")] alt(( map( preceded(tag_no_case(b"HIGHESTMODSEQ "), mod_sequence_value), Code::HighestModSeq, ), value(Code::NoModSeq, tag_no_case(b"NOMODSEQ")), map( preceded(tag_no_case(b"MODIFIED "), sequence_set), Code::Modified, ), value(Code::Closed, tag_no_case(b"CLOSED")), )), ))(input) } /// `capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1" *(SP capability)` /// /// Servers MUST implement the STARTTLS, AUTH=PLAIN, and LOGINDISABLED capabilities /// Servers which offer RFC 1730 compatibility MUST list "IMAP4" as the first capability. pub(crate) fn capability_data(input: &[u8]) -> IMAPResult<&[u8], Vec1> { map( #[cfg(not(feature = "quirk_trailing_space_capability"))] preceded(tag_no_case("CAPABILITY "), separated_list1(sp, capability)), #[cfg(feature = "quirk_trailing_space_capability")] terminated( preceded(tag_no_case("CAPABILITY "), separated_list1(sp, capability)), opt(sp), ), Vec1::unvalidated, )(input) } /// `capability = ("AUTH=" auth-type) / /// "COMPRESS=" algorithm / ; RFC 4978 /// atom` pub(crate) fn capability(input: &[u8]) -> IMAPResult<&[u8], Capability> { map(atom, Capability::from)(input) } /// `resp-cond-bye = "BYE" SP resp-text` pub(crate) fn resp_cond_bye(input: &[u8]) -> IMAPResult<&[u8], (Option, Text)> { preceded(tag_no_case(b"BYE "), resp_text)(input) } // ----- response ----- /// `response = *(continue-req / response-data) response-done` pub(crate) fn response(input: &[u8]) -> IMAPResult<&[u8], Response> { // Divert from standard here for better usability. // response_data already contains the bye response, thus // response_done could also be response_tagged. // // However, I will keep it as it is for now. alt(( #[cfg(feature = "quirk_empty_continue_req")] map(empty_continue_req, Response::CommandContinuationRequest), map(continue_req, Response::CommandContinuationRequest), response_data, map(response_done, Response::Status), ))(input) } /// Parser that allows a spaceless, empty continuation request `+\r\n`. #[cfg(feature = "quirk_empty_continue_req")] pub(crate) fn empty_continue_req(input: &[u8]) -> IMAPResult<&[u8], CommandContinuationRequest> { let mut parser = tuple((tag(b"+"), crlf)); let (remaining, _) = parser(input)?; log::warn!("Rectified faulty continuation request `+\r\n` to `+ ...\r\n`"); let req = CommandContinuationRequest::basic(None, "...").unwrap(); Ok((remaining, req)) } /// `continue-req = "+" SP (resp-text / base64) CRLF` pub(crate) fn continue_req(input: &[u8]) -> IMAPResult<&[u8], CommandContinuationRequest> { // We can't map the output of `resp_text` directly to `Continue::basic()` because we might end // up with a subset of `Text` that is valid base64 and will panic on `unwrap()`. Thus, we first // let the parsing finish and only later map to `Continue`. // A helper struct to postpone the unification to `Continue` in the `alt` combinator below. enum Either { Base64(A), Basic(B), } let mut parser = tuple(( tag(b"+ "), alt(( #[cfg(not(feature = "quirk_crlf_relaxed"))] map( map_res(take_until("\r\n"), |input| _base64.decode(input)), Either::Base64, ), #[cfg(feature = "quirk_crlf_relaxed")] map( map_res(take_until("\n"), |input: &[u8]| { if !input.is_empty() && input[input.len().saturating_sub(1)] == b'\r' { _base64.decode(&input[..input.len().saturating_sub(1)]) } else { _base64.decode(input) } }), Either::Base64, ), map(resp_text, Either::Basic), )), crlf, )); let (remaining, (_, either, _)) = parser(input)?; let continue_request = match either { Either::Base64(data) => CommandContinuationRequest::base64(data), Either::Basic((code, text)) => CommandContinuationRequest::basic(code, text).unwrap(), }; Ok((remaining, continue_request)) } /// ```abnf /// response-data = "*" SP ( /// resp-cond-state / /// resp-cond-bye / /// mailbox-data / /// message-data / /// capability-data / /// id_response ; (See RFC 2971) /// ) CRLF /// ``` pub(crate) fn response_data(input: &[u8]) -> IMAPResult<&[u8], Response> { delimited( tag(b"* "), alt(( map(resp_cond_state, |(kind, code, text)| { Response::Status(Status::Untagged(StatusBody { kind, code, text })) }), map(resp_cond_bye, |(code, text)| { Response::Status(Status::Bye(Bye { code, text })) }), map(mailbox_data, Response::Data), map(message_data, Response::Data), map(capability_data, |caps| { Response::Data(Data::Capability(caps)) }), map(enable_data, Response::Data), #[cfg(feature = "ext_id")] map(id_response, |parameters| { Response::Data(Data::Id { parameters }) }), )), crlf, )(input) } /// `resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text` /// /// Status condition pub(crate) fn resp_cond_state(input: &[u8]) -> IMAPResult<&[u8], (StatusKind, Option, Text)> { let mut parser = tuple(( alt(( value(StatusKind::Ok, tag_no_case("OK ")), value(StatusKind::No, tag_no_case("NO ")), value(StatusKind::Bad, tag_no_case("BAD ")), )), resp_text, )); let (remaining, (kind, (maybe_code, text))) = parser(input)?; Ok((remaining, (kind, maybe_code, text))) } /// `response-done = response-tagged / response-fatal` pub(crate) fn response_done(input: &[u8]) -> IMAPResult<&[u8], Status> { alt((response_tagged, response_fatal))(input) } /// `response-tagged = tag SP resp-cond-state CRLF` pub(crate) fn response_tagged(input: &[u8]) -> IMAPResult<&[u8], Status> { let mut parser = tuple((tag_imap, sp, resp_cond_state, crlf)); let (remaining, (tag, _, (kind, code, text), _)) = parser(input)?; Ok(( remaining, Status::Tagged(Tagged { tag, body: StatusBody { kind, code, text }, }), )) } /// `response-fatal = "*" SP resp-cond-bye CRLF` /// /// Server closes connection immediately pub(crate) fn response_fatal(input: &[u8]) -> IMAPResult<&[u8], Status> { let mut parser = delimited(tag(b"* "), resp_cond_bye, crlf); let (remaining, (code, text)) = parser(input)?; Ok((remaining, Status::Bye(Bye { code, text }))) } /// ```abnf /// message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att)) /// ``` /// /// From RFC 7162: /// /// ```abnf /// message-data =/ expunged-resp /// /// expunged-resp = "VANISHED" [SP "(EARLIER)"] SP known-uids /// ``` pub(crate) fn message_data(input: &[u8]) -> IMAPResult<&[u8], Data> { #[derive(Clone)] enum TmpData<'a> { Expunge, Fetch(Vec1>), #[cfg(feature = "ext_condstore_qresync")] Vanished(bool, SequenceSet), } let (remaining, (seq, tmp)) = tuple(( terminated(nz_number, sp), alt(( value(TmpData::Expunge, tag_no_case(b"EXPUNGE")), map(preceded(tag_no_case(b"FETCH "), msg_att), TmpData::Fetch), #[cfg(feature = "ext_condstore_qresync")] map( tuple(( tag_no_case("VANISHED"), opt(tag_no_case(" (EARLIER)")), preceded(sp, sequence_set), )), |(_, earlier, known_uids)| TmpData::Vanished(earlier.is_some(), known_uids), ), )), ))(input)?; Ok(( remaining, match tmp { TmpData::Expunge => Data::Expunge(seq), TmpData::Fetch(items) => Data::Fetch { seq, items }, #[cfg(feature = "ext_condstore_qresync")] TmpData::Vanished(earlier, known_uids) => Data::Vanished { earlier, known_uids, }, }, )) } #[cfg(test)] mod tests { use std::num::NonZeroU32; use imap_types::{ body::{ BasicFields, Body, BodyExtension, BodyStructure, Disposition, Language, Location, SinglePartExtensionData, SpecificFields, }, core::{IString, NString, QuotedChar, Tag}, flag::FlagNameAttribute, }; use super::*; use crate::testing::{kat_inverse_greeting, kat_inverse_response, known_answer_test_encode}; #[test] fn test_kat_inverse_greeting() { kat_inverse_greeting(&[ ( b"* OK [badcharset] ...\r\n".as_slice(), b"".as_slice(), Greeting::ok(Some(Code::BadCharset { allowed: vec![] }), "...").unwrap(), ), ( b"* OK [UnSEEN 12345] ...\r\naaa".as_slice(), b"aaa".as_slice(), Greeting::ok( Some(Code::Unseen(NonZeroU32::try_from(12345).unwrap())), "...", ) .unwrap(), ), ( b"* OK [unseen 12345] \r\n ".as_slice(), b" ".as_slice(), Greeting::ok( Some(Code::Unseen(NonZeroU32::try_from(12345).unwrap())), " ", ) .unwrap(), ), ( b"* PREAUTH [ALERT] hello\r\n".as_ref(), b"".as_ref(), Greeting::new(GreetingKind::PreAuth, Some(Code::Alert), "hello").unwrap(), ), ]); } #[test] fn test_kat_inverse_response_data() { kat_inverse_response(&[ ( b"* CAPABILITY IMAP4REV1\r\n".as_ref(), b"".as_ref(), Response::Data(Data::Capability(Vec1::from(Capability::Imap4Rev1))), ), ( b"* LIST (\\Noselect) \"/\" bbb\r\n", b"", Response::Data(Data::List { items: vec![FlagNameAttribute::Noselect], delimiter: Some(QuotedChar::try_from('/').unwrap()), mailbox: "bbb".try_into().unwrap(), }), ), ( b"* SEARCH 1 2 3 42\r\n", b"", Response::Data(Data::Search( vec![ 1.try_into().unwrap(), 2.try_into().unwrap(), 3.try_into().unwrap(), 42.try_into().unwrap(), ], #[cfg(feature = "ext_condstore_qresync")] None, )), ), (b"* 42 EXISTS\r\n", b"", Response::Data(Data::Exists(42))), ( b"* 12345 RECENT\r\n", b"", Response::Data(Data::Recent(12345)), ), ( b"* 123 EXPUNGE\r\n", b"", Response::Data(Data::Expunge(123.try_into().unwrap())), ), ]); } #[test] fn test_kat_inverse_response_status() { kat_inverse_response(&[ // tagged; Ok, No, Bad ( b"A1 OK [ALERT] hello\r\n".as_ref(), b"".as_ref(), Response::Status( Status::ok( Some(Tag::try_from("A1").unwrap()), Some(Code::Alert), "hello", ) .unwrap(), ), ), ( b"A1 NO [ALERT] hello\r\n", b"".as_ref(), Response::Status( Status::no( Some(Tag::try_from("A1").unwrap()), Some(Code::Alert), "hello", ) .unwrap(), ), ), ( b"A1 BAD [ALERT] hello\r\n", b"".as_ref(), Response::Status( Status::bad( Some(Tag::try_from("A1").unwrap()), Some(Code::Alert), "hello", ) .unwrap(), ), ), ( b"A1 OK hello\r\n", b"".as_ref(), Response::Status( Status::ok(Some(Tag::try_from("A1").unwrap()), None, "hello").unwrap(), ), ), ( b"A1 NO hello\r\n", b"".as_ref(), Response::Status( Status::no(Some(Tag::try_from("A1").unwrap()), None, "hello").unwrap(), ), ), ( b"A1 BAD hello\r\n", b"".as_ref(), Response::Status( Status::bad(Some(Tag::try_from("A1").unwrap()), None, "hello").unwrap(), ), ), // untagged; Ok, No, Bad ( b"* OK [ALERT] hello\r\n", b"".as_ref(), Response::Status(Status::ok(None, Some(Code::Alert), "hello").unwrap()), ), ( b"* NO [ALERT] hello\r\n", b"".as_ref(), Response::Status(Status::no(None, Some(Code::Alert), "hello").unwrap()), ), ( b"* BAD [ALERT] hello\r\n", b"".as_ref(), Response::Status(Status::bad(None, Some(Code::Alert), "hello").unwrap()), ), ( b"* OK hello\r\n", b"".as_ref(), Response::Status(Status::ok(None, None, "hello").unwrap()), ), ( b"* NO hello\r\n", b"".as_ref(), Response::Status(Status::no(None, None, "hello").unwrap()), ), ( b"* BAD hello\r\n", b"".as_ref(), Response::Status(Status::bad(None, None, "hello").unwrap()), ), // bye ( b"* BYE [ALERT] hello\r\n", b"".as_ref(), Response::Status(Status::bye(Some(Code::Alert), "hello").unwrap()), ), ]); } /* // TODO(#184) #[test] fn test_kat_inverse_continue() { kat_inverse_continue(&[ ( b"+ \x01\r\n".as_ref(), b"".as_ref(), Continue::basic(None, "\x01").unwrap(), ), ( b"+ hello\r\n".as_ref(), b"".as_ref(), Continue::basic(None, "hello").unwrap(), ), ( b"+ [READ-WRITE] hello\r\n", b"", Continue::basic(Some(Code::ReadWrite), "hello").unwrap(), ), ]); } */ #[test] fn test_encode_body_structure() { let tests = [ ( BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![], id: NString(None), description: NString::try_from("description").unwrap(), content_transfer_encoding: IString::try_from("cte").unwrap(), size: 123, }, specific: SpecificFields::Basic { r#type: IString::try_from("application").unwrap(), subtype: IString::try_from("voodoo").unwrap(), }, }, extension_data: None, }, b"(\"application\" \"voodoo\" NIL NIL \"description\" \"cte\" 123)".as_ref(), ), ( BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![], id: NString(None), description: NString::try_from("description").unwrap(), content_transfer_encoding: IString::try_from("cte").unwrap(), size: 123, }, specific: SpecificFields::Text { subtype: IString::try_from("plain").unwrap(), number_of_lines: 14, }, }, extension_data: None, }, b"(\"TEXT\" \"plain\" NIL NIL \"description\" \"cte\" 123 14)", ), ( BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![], id: NString(None), description: NString::try_from("description").unwrap(), content_transfer_encoding: IString::try_from("cte").unwrap(), size: 123, }, specific: SpecificFields::Text { subtype: IString::try_from("plain").unwrap(), number_of_lines: 14, }, }, extension_data: Some(SinglePartExtensionData { md5: NString::try_from("AABB").unwrap(), tail: Some(Disposition { disposition: None, tail: Some(Language { language: vec![], tail: Some(Location { location: NString(None), extensions: vec![BodyExtension::List(Vec1::from(BodyExtension::Number(1337)))], }), }), }), }), }, b"(\"TEXT\" \"plain\" NIL NIL \"description\" \"cte\" 123 14 \"AABB\" NIL NIL NIL (1337))", ), ]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_parse_response_negative() { let tests = [ // TODO(#301,#184) // b"+ Nose[CAY a\r\n".as_ref() ]; for test in tests { assert!(response(test).is_err()); } } #[test] fn test_parse_resp_text_quirk() { #[cfg(not(feature = "quirk_missing_text"))] { assert!(resp_text(b"[IMAP4rev1]\r\n").is_err()); assert!(resp_text(b"[IMAP4rev1]\r\n").is_err()); assert!(resp_text(b"[IMAP4rev1] \r\n").is_err()); assert!(resp_text(b"[IMAP4rev1] \r\n").is_ok()); } #[cfg(feature = "quirk_missing_text")] { assert!(resp_text(b"[IMAP4rev1]\r\n").is_ok()); assert!(resp_text(b"[IMAP4rev1] \r\n").is_err()); assert!(resp_text(b"[IMAP4rev1] \r\n").is_ok()); } } #[test] fn test_parse_resp_space_quirk() { assert!(response_data(b"* STATUS INBOX (MESSAGES 100 UNSEEN 0)\r\n").is_ok()); assert!(response_data(b"* STATUS INBOX (MESSAGES 100 UNSEEN 0) \r\n").is_err()); #[cfg(not(feature = "quirk_trailing_space_status"))] assert!(response_data(b"* STATUS INBOX (MESSAGES 100 UNSEEN 0) \r\n").is_err()); #[cfg(feature = "quirk_trailing_space_status")] assert!(response_data(b"* STATUS INBOX (MESSAGES 100 UNSEEN 0) \r\n").is_ok()); } #[test] fn test_quirk_trailing_space_capability() { assert!(response_data(b"* CAPABILITY IMAP4REV1\r\n").is_ok()); assert!(response_data(b"* CAPABILITY IMAP4REV1 \r\n").is_err()); #[cfg(not(feature = "quirk_trailing_space_capability"))] assert!(response_data(b"* CAPABILITY IMAP4REV1 \r\n").is_err()); #[cfg(feature = "quirk_trailing_space_capability")] assert!(response_data(b"* CAPABILITY IMAP4REV1 \r\n").is_ok()); } } duesee-imap-codec-0d00966/imap-codec/src/search.rs000066400000000000000000000375501507724125200216570ustar00rootroot00000000000000use abnf_core::streaming::sp; use imap_types::{ command::CommandBody, core::{Charset, Vec1}, search::SearchKey, }; use nom::{ branch::alt, bytes::streaming::{tag, tag_no_case}, combinator::{map, map_opt, opt, value}, multi::separated_list1, sequence::{delimited, separated_pair, tuple}, }; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::search_modsequence; use crate::{ core::{astring, atom, charset, number}, datetime::date, decode::{IMAPErrorKind, IMAPParseError, IMAPResult}, fetch::header_fld_name, sequence::sequence_set, }; /// `search = "SEARCH" [SP "CHARSET" SP charset] 1*(SP search-key)` /// /// Note: CHARSET argument MUST be registered with IANA /// /// errata id: 261 pub(crate) fn search(input: &[u8]) -> IMAPResult<&[u8], CommandBody> { let mut parser = tuple(( tag_no_case(b"SEARCH"), opt(map( tuple((sp, tag_no_case(b"CHARSET"), sp, charset)), |(_, _, _, charset)| charset, )), sp, map(separated_list1(sp, search_key(9)), Vec1::unvalidated), )); let (remaining, (_, charset, _, criteria)) = parser(input)?; Ok(( remaining, CommandBody::Search { charset, criteria, uid: false, }, )) } /// ```abnf /// search-key = "ALL" / /// "ANSWERED" / /// "BCC" SP astring / /// "BEFORE" SP date / /// "BODY" SP astring / /// "CC" SP astring / /// "DELETED" / /// "FLAGGED" / /// "FROM" SP astring / /// "KEYWORD" SP flag-keyword / /// "NEW" / /// "OLD" / /// "ON" SP date / /// "RECENT" / /// "SEEN" / /// "SINCE" SP date / /// "SUBJECT" SP astring / /// "TEXT" SP astring / /// "TO" SP astring / /// "UNANSWERED" / /// "UNDELETED" / /// "UNFLAGGED" / /// "UNKEYWORD" SP flag-keyword / /// "UNSEEN" / /// ; Above this line were in [IMAP2] /// "DRAFT" / /// "HEADER" SP header-fld-name SP astring / /// "LARGER" SP number / /// "NOT" SP search-key / /// "OR" SP search-key SP search-key / /// "SENTBEFORE" SP date / /// "SENTON" SP date / /// "SENTSINCE" SP date / /// "SMALLER" SP number / /// "UID" SP sequence-set / /// "UNDRAFT" / /// search-modsequence / ; RFC 7162 /// sequence-set / /// "(" search-key *(SP search-key) ")" /// ``` /// /// This parser is recursively defined. Thus, in order to not overflow the stack, /// it is needed to limit how may recursions are allowed. (8 should suffice). pub(crate) fn search_key( remaining_recursions: usize, ) -> impl Fn(&[u8]) -> IMAPResult<&[u8], SearchKey> { move |input: &[u8]| search_key_limited(input, remaining_recursions) } fn search_key_limited(input: &[u8], remaining_recursion: usize) -> IMAPResult<&[u8], SearchKey> { if remaining_recursion == 0 { return Err(nom::Err::Failure(IMAPParseError { input, kind: IMAPErrorKind::RecursionLimitExceeded, })); } let search_key = |input| search_key_limited(input, remaining_recursion.saturating_sub(1)); alt(( alt(( value(SearchKey::All, tag_no_case(b"ALL")), value(SearchKey::Answered, tag_no_case(b"ANSWERED")), map(tuple((tag_no_case(b"BCC"), sp, astring)), |(_, _, val)| { SearchKey::Bcc(val) }), map( tuple((tag_no_case(b"BEFORE"), sp, map_opt(date, |date| date))), |(_, _, date)| SearchKey::Before(date), ), map(tuple((tag_no_case(b"BODY"), sp, astring)), |(_, _, val)| { SearchKey::Body(val) }), map(tuple((tag_no_case(b"CC"), sp, astring)), |(_, _, val)| { SearchKey::Cc(val) }), value(SearchKey::Deleted, tag_no_case(b"DELETED")), value(SearchKey::Flagged, tag_no_case(b"FLAGGED")), map(tuple((tag_no_case(b"FROM"), sp, astring)), |(_, _, val)| { SearchKey::From(val) }), map( // Note: `flag_keyword` parser returns `Flag`. Because Rust does not have first-class enum variants // it is not possible to fix SearchKey(Flag::Keyword), but only SearchKey(Flag). // Thus `SearchKey::Keyword(Atom)` is used instead. This is, why we use also `atom` parser here and not `flag_keyword` parser. tuple((tag_no_case(b"KEYWORD"), sp, atom)), |(_, _, val)| SearchKey::Keyword(val), ), value(SearchKey::New, tag_no_case(b"NEW")), value(SearchKey::Old, tag_no_case(b"OLD")), map( tuple((tag_no_case(b"ON"), sp, map_opt(date, |date| date))), |(_, _, date)| SearchKey::On(date), ), value(SearchKey::Recent, tag_no_case(b"RECENT")), value(SearchKey::Seen, tag_no_case(b"SEEN")), map( tuple((tag_no_case(b"SINCE"), sp, map_opt(date, |date| date))), |(_, _, date)| SearchKey::Since(date), ), map( tuple((tag_no_case(b"SUBJECT"), sp, astring)), |(_, _, val)| SearchKey::Subject(val), ), map(tuple((tag_no_case(b"TEXT"), sp, astring)), |(_, _, val)| { SearchKey::Text(val) }), map(tuple((tag_no_case(b"TO"), sp, astring)), |(_, _, val)| { SearchKey::To(val) }), )), alt(( value(SearchKey::Unanswered, tag_no_case(b"UNANSWERED")), value(SearchKey::Undeleted, tag_no_case(b"UNDELETED")), value(SearchKey::Unflagged, tag_no_case(b"UNFLAGGED")), map( // Note: `flag_keyword` parser returns `Flag`. Because Rust does not have first-class enum variants // it is not possible to fix SearchKey(Flag::Keyword), but only SearchKey(Flag). // Thus `SearchKey::Keyword(Atom)` is used instead. This is, why we use also `atom` parser here and not `flag_keyword` parser. tuple((tag_no_case(b"UNKEYWORD"), sp, atom)), |(_, _, val)| SearchKey::Unkeyword(val), ), value(SearchKey::Unseen, tag_no_case(b"UNSEEN")), value(SearchKey::Draft, tag_no_case(b"DRAFT")), map( tuple((tag_no_case(b"HEADER"), sp, header_fld_name, sp, astring)), |(_, _, key, _, val)| SearchKey::Header(key, val), ), map( tuple((tag_no_case(b"LARGER"), sp, number)), |(_, _, val)| SearchKey::Larger(val), ), map( tuple((tag_no_case(b"NOT"), sp, search_key)), |(_, _, val)| SearchKey::Not(Box::new(val)), ), map( tuple((tag_no_case(b"OR"), sp, search_key, sp, search_key)), |(_, _, alt1, _, alt2)| SearchKey::Or(Box::new(alt1), Box::new(alt2)), ), map( tuple((tag_no_case(b"SENTBEFORE"), sp, map_opt(date, |date| date))), |(_, _, date)| SearchKey::SentBefore(date), ), map( tuple((tag_no_case(b"SENTON"), sp, map_opt(date, |date| date))), |(_, _, date)| SearchKey::SentOn(date), ), map( tuple((tag_no_case(b"SENTSINCE"), sp, map_opt(date, |date| date))), |(_, _, date)| SearchKey::SentSince(date), ), map( tuple((tag_no_case(b"SMALLER"), sp, number)), |(_, _, val)| SearchKey::Smaller(val), ), map( tuple((tag_no_case(b"UID"), sp, sequence_set)), |(_, _, val)| SearchKey::Uid(val), ), value(SearchKey::Undraft, tag_no_case(b"UNDRAFT")), #[cfg(feature = "ext_condstore_qresync")] map(search_modsequence, |(entry, modseq)| { SearchKey::ModSequence { entry, modseq } }), map(sequence_set, SearchKey::SequenceSet), map( delimited(tag(b"("), separated_list1(sp, search_key), tag(b")")), |val| SearchKey::And(Vec1::unvalidated(val)), ), )), ))(input) } /// ```abnf /// search-criteria = charset 1*(SP search-key) /// ``` pub(crate) fn search_criteria(input: &[u8]) -> IMAPResult<&[u8], (Charset, Vec1)> { let mut parser = separated_pair( charset, sp, map(separated_list1(sp, search_key(9)), Vec1::unvalidated), ); let (remaining, (charset, search_keys)) = parser(input)?; Ok((remaining, (charset, search_keys))) } #[cfg(test)] mod tests { use imap_types::{ core::{AString, Atom}, datetime::NaiveDate, sequence::{Sequence, SequenceSet}, }; use super::*; use crate::testing::known_answer_test_encode; #[test] fn test_parse_search() { use imap_types::{ search::SearchKey::*, sequence::{SeqOrUid::Value, Sequence::*, SequenceSet as SequenceSetData}, }; let (_rem, val) = search(b"search (uid 5)???").unwrap(); assert_eq!( val, CommandBody::Search { charset: None, criteria: Vec1::from(And(Vec1::from(Uid(SequenceSetData( vec![Single(Value(5.try_into().unwrap()))] .try_into() .unwrap() ))))), uid: false, } ); let (_rem, val) = search(b"search (uid 5 or uid 5 (uid 1 uid 2) not uid 5)???").unwrap(); let expected = CommandBody::Search { charset: None, criteria: Vec1::from(And(vec![ Uid(SequenceSetData( vec![Single(Value(5.try_into().unwrap()))] .try_into() .unwrap(), )), Or( Box::new(Uid(SequenceSetData( vec![Single(Value(5.try_into().unwrap()))] .try_into() .unwrap(), ))), Box::new(And(vec![ Uid(SequenceSetData( vec![Single(Value(1.try_into().unwrap()))] .try_into() .unwrap(), )), Uid(SequenceSetData( vec![Single(Value(2.try_into().unwrap()))] .try_into() .unwrap(), )), ] .try_into() .unwrap())), ), Not(Box::new(Uid(SequenceSetData( vec![Single(Value(5.try_into().unwrap()))] .try_into() .unwrap(), )))), ] .try_into() .unwrap())), uid: false, }; assert_eq!(val, expected); } #[test] fn test_parse_search_key() { assert!(search_key(1)(b"1:5|").is_ok()); assert!(search_key(1)(b"(1:5)|").is_err()); assert!(search_key(2)(b"(1:5)|").is_ok()); assert!(search_key(2)(b"((1:5))|").is_err()); } #[test] fn test_encode_search_key() { let tests = [ ( SearchKey::And(Vec1::try_from(vec![SearchKey::Answered]).unwrap()), b"(ANSWERED)".as_ref(), ), ( SearchKey::And(Vec1::try_from(vec![SearchKey::Answered, SearchKey::Seen]).unwrap()), b"(ANSWERED SEEN)".as_ref(), ), ( SearchKey::SequenceSet(SequenceSet::try_from(1).unwrap()), b"1", ), (SearchKey::All, b"ALL"), (SearchKey::Answered, b"ANSWERED"), (SearchKey::Bcc(AString::try_from("A").unwrap()), b"BCC A"), ( SearchKey::Before( NaiveDate::try_from(chrono::NaiveDate::from_ymd_opt(2023, 4, 12).unwrap()) .unwrap(), ), b"BEFORE \"12-Apr-2023\"", ), (SearchKey::Body(AString::try_from("A").unwrap()), b"BODY A"), (SearchKey::Cc(AString::try_from("A").unwrap()), b"CC A"), (SearchKey::Deleted, b"DELETED"), (SearchKey::Draft, b"DRAFT"), (SearchKey::Flagged, b"FLAGGED"), (SearchKey::From(AString::try_from("A").unwrap()), b"FROM A"), ( SearchKey::Header( AString::try_from("A").unwrap(), AString::try_from("B").unwrap(), ), b"HEADER A B", ), ( SearchKey::Keyword(Atom::try_from("A").unwrap()), b"KEYWORD A", ), (SearchKey::Larger(42), b"LARGER 42"), (SearchKey::New, b"NEW"), (SearchKey::Not(Box::new(SearchKey::New)), b"NOT NEW"), (SearchKey::Old, b"OLD"), ( SearchKey::On( NaiveDate::try_from(chrono::NaiveDate::from_ymd_opt(2023, 4, 12).unwrap()) .unwrap(), ), b"ON \"12-Apr-2023\"", ), ( SearchKey::Or(Box::new(SearchKey::New), Box::new(SearchKey::Recent)), b"OR NEW RECENT", ), (SearchKey::Recent, b"RECENT"), (SearchKey::Seen, b"SEEN"), ( SearchKey::SentBefore( NaiveDate::try_from(chrono::NaiveDate::from_ymd_opt(2023, 4, 12).unwrap()) .unwrap(), ), b"SENTBEFORE \"12-Apr-2023\"", ), ( SearchKey::SentOn( NaiveDate::try_from(chrono::NaiveDate::from_ymd_opt(2023, 4, 12).unwrap()) .unwrap(), ), b"SENTON \"12-Apr-2023\"", ), ( SearchKey::SentSince( NaiveDate::try_from(chrono::NaiveDate::from_ymd_opt(2023, 4, 12).unwrap()) .unwrap(), ), b"SENTSINCE \"12-Apr-2023\"", ), ( SearchKey::Since( NaiveDate::try_from(chrono::NaiveDate::from_ymd_opt(2023, 4, 12).unwrap()) .unwrap(), ), b"SINCE \"12-Apr-2023\"", ), (SearchKey::Smaller(1337), b"SMALLER 1337"), ( SearchKey::Subject(AString::try_from("A").unwrap()), b"SUBJECT A", ), (SearchKey::Text(AString::try_from("A").unwrap()), b"TEXT A"), (SearchKey::To(AString::try_from("A").unwrap()), b"TO A"), ( SearchKey::Uid(SequenceSet::from(Sequence::try_from(1..).unwrap())), b"UID 1:*", ), (SearchKey::Unanswered, b"UNANSWERED"), (SearchKey::Undeleted, b"UNDELETED"), (SearchKey::Undraft, b"UNDRAFT"), (SearchKey::Unflagged, b"UNFLAGGED"), ( SearchKey::Unkeyword(Atom::try_from("A").unwrap()), b"UNKEYWORD A", ), (SearchKey::Unseen, b"UNSEEN"), ]; for test in tests { known_answer_test_encode(test); } } } duesee-imap-codec-0d00966/imap-codec/src/sequence.rs000066400000000000000000000113771507724125200222210ustar00rootroot00000000000000use imap_types::{ core::Vec1, sequence::{SeqOrUid, Sequence, SequenceSet}, }; use nom::{ branch::alt, bytes::streaming::tag, combinator::{map, value}, multi::separated_list1, sequence::tuple, }; use crate::{core::nz_number, decode::IMAPResult}; /// `sequence-set = (seq-number / seq-range) ["," sequence-set]` /// /// Note: See errata id: 261 TODO: Why the errata? /// /// Set of seq-number values, regardless of order. /// Servers MAY coalesce overlaps and/or execute the sequence in any order. /// /// Example: a message sequence number set of /// 2,4:7,9,12:* for a mailbox with 15 messages is /// equivalent to 2,4,5,6,7,9,12,13,14,15 /// /// Example: a message sequence number set of *:4,5:7 /// for a mailbox with 10 messages is equivalent to /// 10,9,8,7,6,5,4,5,6,7 and MAY be reordered and /// overlap coalesced to be 4,5,6,7,8,9,10. /// /// Simplified: /// /// `sequence-set = (seq-number / seq-range) *("," (seq-number / seq-range))` pub(crate) fn sequence_set(input: &[u8]) -> IMAPResult<&[u8], SequenceSet> { map( separated_list1( tag(b","), alt(( // Ordering is important! map(seq_range, |(from, to)| Sequence::Range(from, to)), map(seq_number, Sequence::Single), )), ), |set| SequenceSet(Vec1::unvalidated(set)), )(input) } /// `seq-range = seq-number ":" seq-number` /// /// Two seq-number values and all values between these two regardless of order. /// /// Example: 2:4 and 4:2 are equivalent and indicate values 2, 3, and 4. /// /// Example: a unique identifier sequence range of 3291:* includes the UID /// of the last message in the mailbox, even if that value is less than 3291. pub(crate) fn seq_range(input: &[u8]) -> IMAPResult<&[u8], (SeqOrUid, SeqOrUid)> { let mut parser = tuple((seq_number, tag(b":"), seq_number)); let (remaining, (from, _, to)) = parser(input)?; Ok((remaining, (from, to))) } /// `seq-number = nz-number / "*"` /// /// Message sequence number (COPY, FETCH, STORE commands) or unique /// identifier (UID COPY, UID FETCH, UID STORE commands). /// /// "*" represents the largest number in use. /// In the case of message sequence numbers, it is the number of messages in a non-empty mailbox. /// In the case of unique identifiers, it is the unique identifier of the last message in the mailbox or, /// if the mailbox is empty, the mailbox's current UIDNEXT value. /// /// The server should respond with a tagged BAD response to a command that uses a message /// sequence number greater than the number of messages in the selected mailbox. /// This includes "*" if the selected mailbox is empty. pub(crate) fn seq_number(input: &[u8]) -> IMAPResult<&[u8], SeqOrUid> { alt(( map(nz_number, SeqOrUid::Value), value(SeqOrUid::Asterisk, tag(b"*")), ))(input) } #[cfg(test)] mod tests { use super::*; use crate::encode::{EncodeContext, EncodeIntoContext}; #[test] fn test_encode_of_some_sequence_sets() { let tests = [ ( Sequence::Single(SeqOrUid::Value(1.try_into().unwrap())), b"1".as_ref(), ), (Sequence::Single(SeqOrUid::Asterisk), b"*".as_ref()), ( Sequence::Range(SeqOrUid::Value(1.try_into().unwrap()), SeqOrUid::Asterisk), b"1:*".as_ref(), ), ]; for (test, expected) in tests { let mut ctx = EncodeContext::new(); test.encode_ctx(&mut ctx).unwrap(); let out = ctx.dump(); assert_eq!(*expected, out); } } #[test] fn test_parse_sequence_set() { let (rem, val) = sequence_set(b"1:*?").unwrap(); println!("{rem:?}, {val:?}"); let (rem, val) = sequence_set(b"1:*,5?").unwrap(); println!("{rem:?}, {val:?}"); } #[test] fn test_parse_seq_number() { // Must not be 0. assert!(seq_number(b"0?").is_err()); let (rem, val) = seq_number(b"1?").unwrap(); println!("{rem:?}, {val:?}"); let (rem, val) = seq_number(b"*?").unwrap(); println!("{rem:?}, {val:?}"); } #[test] fn test_parse_seq_range() { // Must not be 0. assert!(seq_range(b"0:1?").is_err()); assert_eq!( ( SeqOrUid::Value(1.try_into().unwrap()), SeqOrUid::Value(2.try_into().unwrap()) ), seq_range(b"1:2?").unwrap().1 ); assert_eq!( (SeqOrUid::Value(1.try_into().unwrap()), SeqOrUid::Asterisk), seq_range(b"1:*?").unwrap().1 ); assert_eq!( (SeqOrUid::Asterisk, SeqOrUid::Value(10.try_into().unwrap())), seq_range(b"*:10?").unwrap().1 ); } } duesee-imap-codec-0d00966/imap-codec/src/status.rs000066400000000000000000000111031507724125200217170ustar00rootroot00000000000000use abnf_core::streaming::sp; use imap_types::status::{StatusDataItem, StatusDataItemName}; use nom::{ branch::alt, bytes::streaming::tag_no_case, combinator::{map, value}, multi::separated_list1, sequence::preceded, }; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::mod_sequence_valzer; use crate::{ core::{number, number64, nz_number}, decode::IMAPResult, }; /// `status-att = "MESSAGES" / /// "RECENT" / /// "UIDNEXT" / /// "UIDVALIDITY" / /// "UNSEEN"` pub(crate) fn status_att(input: &[u8]) -> IMAPResult<&[u8], StatusDataItemName> { alt(( value(StatusDataItemName::Messages, tag_no_case(b"MESSAGES")), value(StatusDataItemName::Recent, tag_no_case(b"RECENT")), value(StatusDataItemName::UidNext, tag_no_case(b"UIDNEXT")), value(StatusDataItemName::UidValidity, tag_no_case(b"UIDVALIDITY")), value(StatusDataItemName::Unseen, tag_no_case(b"UNSEEN")), value( StatusDataItemName::DeletedStorage, tag_no_case(b"DELETED-STORAGE"), ), value(StatusDataItemName::Deleted, tag_no_case(b"DELETED")), #[cfg(feature = "ext_condstore_qresync")] value( StatusDataItemName::HighestModSeq, tag_no_case(b"HIGHESTMODSEQ"), ), ))(input) } /// `status-att-list = status-att-val *(SP status-att-val)` /// /// Note: See errata id: 261 pub(crate) fn status_att_list(input: &[u8]) -> IMAPResult<&[u8], Vec> { separated_list1(sp, status_att_val)(input) } /// ```abnf /// status-att-val = "MESSAGES" SP number / /// "RECENT" SP number / /// "UIDNEXT" SP nz-number / /// "UIDVALIDITY" SP nz-number / /// "UNSEEN" SP number / /// "HIGHESTMODSEQ" SP mod-sequence-valzer /// ``` /// /// Note: See errata id: 261 fn status_att_val(input: &[u8]) -> IMAPResult<&[u8], StatusDataItem> { alt(( map( preceded(tag_no_case(b"MESSAGES "), number), StatusDataItem::Messages, ), map( preceded(tag_no_case(b"RECENT "), number), StatusDataItem::Recent, ), map( preceded(tag_no_case(b"UIDNEXT "), nz_number), StatusDataItem::UidNext, ), map( preceded(tag_no_case(b"UIDVALIDITY "), nz_number), StatusDataItem::UidValidity, ), map( preceded(tag_no_case(b"UNSEEN "), number), StatusDataItem::Unseen, ), map( preceded(tag_no_case(b"DELETED-STORAGE "), number64), StatusDataItem::DeletedStorage, ), map( preceded(tag_no_case(b"DELETED "), number), StatusDataItem::Deleted, ), #[cfg(feature = "ext_condstore_qresync")] map( preceded(tag_no_case(b"HIGHESTMODSEQ "), mod_sequence_valzer), StatusDataItem::HighestModSeq, ), ))(input) } #[cfg(test)] mod tests { use std::num::NonZeroU32; use super::*; use crate::testing::known_answer_test_encode; #[test] fn test_encode_status_data_item_name() { let tests = [ (StatusDataItemName::Messages, b"MESSAGES".as_ref()), (StatusDataItemName::Recent, b"RECENT"), (StatusDataItemName::UidNext, b"UIDNEXT"), (StatusDataItemName::UidValidity, b"UIDVALIDITY"), (StatusDataItemName::Unseen, b"UNSEEN"), (StatusDataItemName::Deleted, b"DELETED"), (StatusDataItemName::DeletedStorage, b"DELETED-STORAGE"), ]; for test in tests { known_answer_test_encode(test); } } #[test] fn test_encode_status_data_item() { let tests = [ (StatusDataItem::Messages(0), b"MESSAGES 0".as_ref()), (StatusDataItem::Recent(u32::MAX), b"RECENT 4294967295"), ( StatusDataItem::UidNext(NonZeroU32::new(1).unwrap()), b"UIDNEXT 1", ), ( StatusDataItem::UidValidity(NonZeroU32::new(u32::MAX).unwrap()), b"UIDVALIDITY 4294967295", ), (StatusDataItem::Unseen(0), b"UNSEEN 0"), (StatusDataItem::Deleted(1), b"DELETED 1"), ( StatusDataItem::DeletedStorage(u64::MAX), b"DELETED-STORAGE 18446744073709551615", ), ]; for test in tests { known_answer_test_encode(test); } } } duesee-imap-codec-0d00966/imap-codec/src/testing.rs000066400000000000000000000067251507724125200220670ustar00rootroot00000000000000use std::fmt::Debug; use imap_types::{ auth::AuthenticateData, command::Command, extensions::idle::IdleDone, response::{Greeting, Response}, utils::escape_byte_string, }; use crate::{ AuthenticateDataCodec, CommandCodec, GreetingCodec, IdleDoneCodec, ResponseCodec, decode::{Decoder, IMAPResult}, encode::{EncodeContext, EncodeIntoContext}, }; pub(crate) fn known_answer_test_encode( (test_object, expected_bytes): (impl EncodeIntoContext, impl AsRef<[u8]>), ) { let expected_bytes = expected_bytes.as_ref(); let mut ctx = EncodeContext::new(); test_object.encode_ctx(&mut ctx).unwrap(); let got_bytes = ctx.dump(); let got_bytes = got_bytes.as_slice(); if expected_bytes != got_bytes { println!("# Debug (`escape_byte_string`, encapsulated by `<<<` and `>>>`)"); println!( "Left: <<<{}>>>\nRight: <<<{}>>>", escape_byte_string(expected_bytes), escape_byte_string(got_bytes), ); println!("# Debug"); panic!("Left: {expected_bytes:02x?}\nRight: {got_bytes:02x?}"); } } pub(crate) fn known_answer_test_parse<'a, O, P>( (test, expected_remainder, expected_object): (&'a [u8], &[u8], O), parser: P, ) where O: Debug + Eq + 'a, P: Fn(&'a [u8]) -> IMAPResult<'a, &'a [u8], O>, { let (got_remainder, got_object) = parser(test).unwrap(); assert_eq!(expected_remainder, got_remainder); assert_eq!(expected_object, got_object); } // Note: Maybe there is a cleaner way to write this using generic bounds. However, // we tried it and failed to provide a cleaner solution. Thus, it's a macro for now. macro_rules! impl_kat_inverse { ($fn_name:ident, $decoder:ident, $item:ty) => { pub(crate) fn $fn_name(tests: &[(&[u8], &[u8], $item)]) { for (no, (test_input, expected_remainder, expected_object)) in tests.iter().enumerate() { println!("# {no}"); let (got_remainder, got_object) = $decoder::default() .decode(test_input) .expect("first parsing failed"); assert_eq!(*expected_object, got_object); assert_eq!(*expected_remainder, got_remainder); let mut ctx = EncodeContext::new(); got_object.encode_ctx(&mut ctx).unwrap(); let got_output = ctx.dump(); // This second `decode` makes using generic bounds more complicated due to the // different lifetime. let (got_remainder, got_object_again) = $decoder::default() .decode(&got_output) .expect("second parsing failed"); assert_eq!(got_object, got_object_again); assert!(got_remainder.is_empty()); } } }; } impl_kat_inverse! {kat_inverse_greeting, GreetingCodec, Greeting} impl_kat_inverse! {kat_inverse_command, CommandCodec, Command} impl_kat_inverse! {kat_inverse_response, ResponseCodec, Response} //impl_kat_inverse! {kat_inverse_continue, ContinueCodec, Continue} impl_kat_inverse! {kat_inverse_authenticate_data, AuthenticateDataCodec, AuthenticateData} impl_kat_inverse! {kat_inverse_done, IdleDoneCodec, IdleDone} #[cfg(test)] mod tests { use imap_types::command::{Command, CommandBody}; use super::*; #[test] #[should_panic] fn test_known_answer_test_encode() { known_answer_test_encode((Command::new("A", CommandBody::Noop).unwrap(), b"")); } } duesee-imap-codec-0d00966/imap-codec/tests/000077500000000000000000000000001507724125200204055ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-codec/tests/trace.rs000066400000000000000000001340721507724125200220600ustar00rootroot00000000000000use imap_codec::{ CommandCodec, GreetingCodec, ResponseCodec, decode::Decoder, encode::Encoder, imap_types::{ auth::AuthMechanism, body::{BasicFields, Body, BodyStructure, SpecificFields}, command::{Command, CommandBody}, core::{AString, IString, Literal, NString, Quoted, Tag}, datetime::DateTime, envelope::{Address, Envelope}, fetch::{Macro, MessageDataItem, MessageDataItemName, Section}, flag::{Flag, FlagFetch, FlagPerm, StoreResponse, StoreType}, response::{Capability, Code, Data, Response, Status}, secret::Secret, }, }; enum Who { Client, Server, } enum Message<'a> { Command(Command<'a>), Response(Response<'a>), } struct TraceLines<'a> { trace: &'a [u8], offset: usize, } impl<'a> Iterator for TraceLines<'a> { type Item = (Who, &'a [u8]); fn next(&mut self) -> Option { let input = &self.trace[self.offset..]; if let Some(pos) = input.iter().position(|b| *b == b'\n') { let who = match &input[..3] { b"C: " => Who::Client, b"S: " => Who::Server, _ => panic!("Line must begin with \"C: \" or \"S: \"."), }; self.offset += pos + 1; Some((who, &input[3..pos + 1])) } else { None } } } fn split_trace(trace: &[u8]) -> impl Iterator { TraceLines { trace, offset: 0 } } fn test_lines_of_trace(trace: &[u8]) { for (who, line) in split_trace(trace) { // Replace last "\n" with "\r\n". let line = { let mut line = line[..line.len().saturating_sub(1)].to_vec(); line.extend_from_slice(b"\r\n"); line }; match who { Who::Client => { println!("C: {}", String::from_utf8_lossy(&line).trim()); let (rem, parsed) = CommandCodec::default().decode(&line).unwrap(); assert!(rem.is_empty()); println!("Parsed {parsed:?}"); let serialized = CommandCodec::default().encode(&parsed).dump(); println!( "Serialized: {}", String::from_utf8_lossy(&serialized).trim() ); let (rem, parsed2) = CommandCodec::default().decode(&serialized).unwrap(); assert!(rem.is_empty()); assert_eq!(parsed, parsed2); println!() } Who::Server => { println!("S: {}", String::from_utf8_lossy(&line).trim()); let (rem, parsed) = ResponseCodec::default().decode(&line).unwrap(); println!("Parsed: {parsed:?}"); assert!(rem.is_empty()); let serialized = ResponseCodec::default().encode(&parsed).dump(); println!( "Serialized: {}", String::from_utf8_lossy(&serialized).trim() ); let (rem, parsed2) = ResponseCodec::default().decode(&serialized).unwrap(); assert!(rem.is_empty()); assert_eq!(parsed, parsed2); println!() } } } } fn test_trace_known_positive(tests: Vec<(&[u8], Message)>) { for (test, expected) in tests.into_iter() { println!("// {}", std::str::from_utf8(test).unwrap().trim()); match expected { Message::Command(expected) => { let (rem, got) = CommandCodec::default().decode(test).unwrap(); assert!(rem.is_empty()); assert_eq!(expected, got); println!("{got:?}"); let encoded = CommandCodec::default().encode(&got).dump(); println!("// {}", String::from_utf8(encoded.clone()).unwrap().trim()); let (rem2, got2) = CommandCodec::default().decode(&encoded).unwrap(); assert!(rem2.is_empty()); assert_eq!(expected, got2); } Message::Response(expected) => { let (rem, got) = ResponseCodec::default().decode(test).unwrap(); assert!(rem.is_empty()); assert_eq!(expected, got); println!("{got:?}"); let encoded = ResponseCodec::default().encode(&got).dump(); println!("// {}", String::from_utf8(encoded.clone()).unwrap().trim()); let (rem2, got2) = ResponseCodec::default().decode(&encoded).unwrap(); assert!(rem2.is_empty()); assert_eq!(expected, got2); } }; println!(); } } #[test] fn test_from_capability() { let tests = { vec![ ( b"abcd CAPABILITY\r\n".as_ref(), Message::Command(Command::new("abcd", CommandBody::Capability).unwrap()), ), ( b"* CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI LOGINDISABLED\r\n", Message::Response(Response::Data( // FIXME(API): accept &[...] Data::capability(vec![ Capability::Imap4Rev1, #[cfg(feature = "starttls")] Capability::StartTls, #[cfg(not(feature = "starttls"))] Capability::try_from("STARTTLS").unwrap(), Capability::Auth(AuthMechanism::try_from("GSSAPI").unwrap()), Capability::LoginDisabled, ]) .unwrap(), )), ), ( b"abcd OK CAPABILITY completed\r\n", // FIXME(API): Option no TryInto ... Message::Response(Response::Status( Status::ok( Some(Tag::try_from("abcd").unwrap()), None, "CAPABILITY completed", ) .unwrap(), )), ), #[cfg(feature = "starttls")] ( b"efgh STARTTLS\r\n", Message::Command(Command::new("efgh", CommandBody::StartTLS).unwrap()), ), ( b"efgh OK STARTLS completed\r\n", // FIXME(API): Option no TryInto ... Message::Response(Response::Status( Status::ok( Some(Tag::try_from("efgh").unwrap()), None, "STARTLS completed", ) .unwrap(), )), ), ( b"ijkl CAPABILITY\r\n", Message::Command(Command::new("ijkl", CommandBody::Capability).unwrap()), ), ( b"* CAPABILITY IMAP4rev1 AUTH=GSSAPI AUTH=PLAIN\r\n", Message::Response(Response::Data( Data::capability(vec![ Capability::Imap4Rev1, Capability::Auth(AuthMechanism::try_from("GSSAPI").unwrap()), Capability::Auth(AuthMechanism::Plain), ]) .unwrap(), )), ), ( b"ijkl OK CAPABILITY completed\r\n", // FIXME(API): Option no TryInto ... Message::Response(Response::Status( Status::ok( Some(Tag::try_from("ijkl").unwrap()), None, "CAPABILITY completed", ) .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[test] fn test_from_noop() { let tests = { vec![ ( b"a002 NOOP\r\n".as_ref(), Message::Command(Command::new("a002", CommandBody::Noop).unwrap()), ), ( b"a002 OK NOOP completed\r\n", // FIXME(API) Message::Response(Response::Status( Status::ok(Some(Tag::try_from("a002").unwrap()), None, "NOOP completed") .unwrap(), )), ), ( b"a047 NOOP\r\n", Message::Command(Command::new("a047", CommandBody::Noop).unwrap()), ), ( b"* 22 EXPUNGE\r\n", Message::Response(Response::Data(Data::expunge(22).unwrap())), ), ( b"* 23 EXISTS\r\n", Message::Response(Response::Data(Data::Exists(23))), ), ( b"* 3 RECENT\r\n", Message::Response(Response::Data(Data::Recent(3))), ), ( b"* 14 FETCH (FLAGS (\\Seen \\Deleted))\r\n", // FIXME(API) Message::Response(Response::Data( Data::fetch( 14, vec![MessageDataItem::Flags(vec![ FlagFetch::Flag(Flag::Seen), FlagFetch::Flag(Flag::Deleted), ])], ) .unwrap(), )), ), ( b"a047 OK NOOP completed\r\n", // FIXME(API) Message::Response(Response::Status( Status::ok(Some(Tag::try_from("a047").unwrap()), None, "NOOP completed") .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[test] fn test_from_logout() { let tests = { vec![ ( b"A023 LOGOUT\r\n".as_ref(), Message::Command(Command::new("A023", CommandBody::Logout).unwrap()), ), ( b"* BYE IMAP4rev1 Server logging out\r\n", Message::Response(Response::Status( Status::bye(None, "IMAP4rev1 Server logging out").unwrap(), )), ), ( b"A023 OK LOGOUT completed\r\n", // FIXME(API) Message::Response(Response::Status( Status::ok( Some(Tag::try_from("A023").unwrap()), None, "LOGOUT completed", ) .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[cfg(feature = "starttls")] #[test] fn test_from_starttls() { let trace = br#"C: a001 CAPABILITY S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED S: a001 OK CAPABILITY completed C: a002 STARTTLS S: a002 OK Begin TLS negotiation now C: a003 CAPABILITY S: * CAPABILITY IMAP4rev1 AUTH=PLAIN S: a003 OK CAPABILITY completed C: a004 LOGIN joe password S: a004 OK LOGIN completed "#; test_lines_of_trace(trace); } #[test] fn test_from_authenticate() { // S: * OK IMAP4rev1 Server // C: A001 AUTHENTICATE GSSAPI // S: + // C: YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBw // MFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0 // b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYW // Mud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHA // cS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJX // AleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0y // C/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknb // I0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhi // vd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpAL // pHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9n // FdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdE // NKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhx // O6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTB // vCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg== // S: + YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMC // AQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0 // uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg== // C: // S: + YDMGCSqGSIb3EgECAgIBAAD/////6jcyG4GE3KkTzBeBiVHe // ceP2CWY0SR0fAQAgAAQEBAQ= // C: YDMGCSqGSIb3EgECAgIBAAD/////3LQBHXTpFfZgrejpLlLImP // wkhbfa2QteAQAgAG1yYwE= // S: A001 OK GSSAPI authentication successful } #[test] fn test_from_login() { let tests = { vec![ ( b"a001 LOGIN SMITH SESAME\r\n".as_ref(), // We know that `CommandBody::login()` will create two atoms. Message::Command( Command::new("a001", CommandBody::login("SMITH", "SESAME").unwrap()).unwrap(), ), ), ( // Addition: We change the previous command here to test a quoted string ... b"a001 LOGIN \"SMITH\" SESAME\r\n".as_ref(), Message::Command( Command::new( "a001", // ... and construct the command manually ... CommandBody::Login { // ... using a quoted string ... username: AString::String(IString::Quoted( Quoted::try_from("SMITH").unwrap(), )), // ... and an atom (knowing that `AString::try_from(...)` will create it. password: Secret::new(AString::try_from("SESAME").unwrap()), }, ) .unwrap(), ), ), ( b"a001 OK LOGIN completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a001").unwrap()), None, "LOGIN completed", ) .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[test] fn test_from_select() { let tests = { vec![ ( b"A142 SELECT INBOX\r\n".as_ref(), Message::Command( Command::new("A142", CommandBody::select("inbox").unwrap()).unwrap(), ), ), ( b"* 172 EXISTS\r\n", Message::Response(Response::Data(Data::Exists(172))), ), ( b"* 1 RECENT\r\n", Message::Response(Response::Data(Data::Recent(1))), ), ( b"* OK [UNSEEN 12] Message 12 is first unseen\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::unseen(12).unwrap()), "Message 12 is first unseen", ) .unwrap(), )), ), ( b"* OK [UIDVALIDITY 3857529045] UIDs valid\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::uidvalidity(3857529045).unwrap()), "UIDs valid", ) .unwrap(), )), ), ( b"* OK [UIDNEXT 4392] Predicted next UID\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::uidnext(4392).unwrap()), "Predicted next UID", ) .unwrap(), )), ), ( b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n", Message::Response(Response::Data(Data::Flags(vec![ Flag::Answered, Flag::Flagged, Flag::Deleted, Flag::Seen, Flag::Draft, ]))), ), ( b"* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::PermanentFlags(vec![ FlagPerm::Flag(Flag::Deleted), FlagPerm::Flag(Flag::Seen), FlagPerm::Asterisk, ])), "Limited", ) .unwrap(), )), ), ( b"A142 OK [READ-WRITE] SELECT completed\r\n", // FIXME(API) Message::Response(Response::Status( Status::ok( Some(Tag::try_from("A142").unwrap()), Some(Code::ReadWrite), "SELECT completed", ) .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[test] fn test_from_examine() { let tests = { vec![ ( b"A932 EXAMINE blurdybloop\r\n".as_ref(), Message::Command( Command::new("A932", CommandBody::examine("blurdybloop").unwrap()).unwrap(), ), ), ( b"* 17 EXISTS\r\n", Message::Response(Response::Data(Data::Exists(17))), ), ( b"* 2 RECENT\r\n", Message::Response(Response::Data(Data::Recent(2))), ), ( b"* OK [UNSEEN 8] Message 8 is first unseen\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::unseen(8).unwrap()), "Message 8 is first unseen", ) .unwrap(), )), ), ( b"* OK [UIDVALIDITY 3857529045] UIDs valid\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::uidvalidity(3857529045).unwrap()), "UIDs valid", ) .unwrap(), )), ), ( b"* OK [UIDNEXT 4392] Predicted next UID\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::uidnext(4392).unwrap()), "Predicted next UID", ) .unwrap(), )), ), ( b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n", Message::Response(Response::Data(Data::Flags(vec![ Flag::Answered, Flag::Flagged, Flag::Deleted, Flag::Seen, Flag::Draft, ]))), ), ( b"* OK [PERMANENTFLAGS ()] No permanent flags permitted\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::PermanentFlags(vec![])), "No permanent flags permitted", ) .unwrap(), )), ), ( b"A932 OK [READ-ONLY] EXAMINE completed\r\n", // FIXME(API) Message::Response(Response::Status( Status::ok( Some(Tag::try_from("A932").unwrap()), Some(Code::ReadOnly), "EXAMINE completed", ) .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[test] fn test_from_create() { let trace = br#"C: A003 CREATE owatagusiam/ S: A003 OK CREATE completed C: A004 CREATE owatagusiam/blurdybloop S: A004 OK CREATE completed "#; test_lines_of_trace(trace); } #[test] fn test_from_delete() { let trace = br#"C: A682 LIST "" * S: * LIST () "/" blurdybloop S: * LIST (\Noselect) "/" foo S: * LIST () "/" foo/bar S: A682 OK LIST completed C: A683 DELETE blurdybloop S: A683 OK DELETE completed C: A684 DELETE foo S: A684 NO Name "foo" has inferior hierarchical names C: A685 DELETE foo/bar S: A685 OK DELETE Completed C: A686 LIST "" * S: * LIST (\Noselect) "/" foo S: A686 OK LIST completed C: A687 DELETE foo S: A687 OK DELETE Completed C: A82 LIST "" * S: * LIST () "." blurdybloop S: * LIST () "." foo S: * LIST () "." foo.bar S: A82 OK LIST completed C: A83 DELETE blurdybloop S: A83 OK DELETE completed C: A84 DELETE foo S: A84 OK DELETE Completed C: A85 LIST "" * S: * LIST () "." foo.bar S: A85 OK LIST completed C: A86 LIST "" % S: * LIST (\Noselect) "." foo S: A86 OK LIST completed "#; test_lines_of_trace(trace); } #[test] fn test_from_rename() { let trace = br#"C: A682 LIST "" * S: * LIST () "/" blurdybloop S: * LIST (\Noselect) "/" foo S: * LIST () "/" foo/bar S: A682 OK LIST completed C: A683 RENAME blurdybloop sarasoop S: A683 OK RENAME completed C: A684 RENAME foo zowie S: A684 OK RENAME Completed C: A685 LIST "" * S: * LIST () "/" sarasoop S: * LIST (\Noselect) "/" zowie S: * LIST () "/" zowie/bar S: A685 OK LIST completed C: Z432 LIST "" * S: * LIST () "." INBOX S: * LIST () "." INBOX.bar S: Z432 OK LIST completed C: Z433 RENAME INBOX old-mail S: Z433 OK RENAME completed C: Z434 LIST "" * S: * LIST () "." INBOX S: * LIST () "." INBOX.bar S: * LIST () "." old-mail S: Z434 OK LIST completed "#; test_lines_of_trace(trace); } #[test] fn test_from_subscribe() { let trace = br#"C: A002 SUBSCRIBE #news.comp.mail.mime S: A002 OK SUBSCRIBE completed "#; test_lines_of_trace(trace); } #[test] fn test_from_unsubscribe() { let trace = br#"C: A002 UNSUBSCRIBE #news.comp.mail.mime S: A002 OK UNSUBSCRIBE completed "#; test_lines_of_trace(trace); } #[test] fn test_from_list() { let trace = br#"C: A101 LIST "" "" S: * LIST (\Noselect) "/" "" S: A101 OK LIST Completed C: A102 LIST #news.comp.mail.misc "" S: * LIST (\Noselect) "." #news. S: A102 OK LIST Completed C: A103 LIST /usr/staff/jones "" S: * LIST (\Noselect) "/" / S: A103 OK LIST Completed C: A202 LIST ~/Mail/ % S: * LIST (\Noselect) "/" ~/Mail/foo S: * LIST () "/" ~/Mail/meetings S: A202 OK LIST completed "#; test_lines_of_trace(trace); } #[test] fn test_from_lsub() { let trace = br#"C: A002 LSUB "news." "comp.mail.*" S: * LSUB () "." #news.comp.mail.mime S: * LSUB () "." #news.comp.mail.misc S: A002 OK LSUB completed C: A003 LSUB "news." "comp.%" S: * LSUB (\NoSelect) "." #news.comp.mail S: A003 OK LSUB completed "#; test_lines_of_trace(trace); } #[test] fn test_from_status() { let trace = br#"C: A042 STATUS blurdybloop (UIDNEXT MESSAGES) S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292) S: A042 OK STATUS completed "#; test_lines_of_trace(trace); } #[test] fn test_from_append() { // C: A003 APPEND saved-messages (\Seen) {310} // S: + Ready for literal data // C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) // C: From: Fred Foobar // C: Subject: afternoon meeting // C: To: mooch@owatagu.siam.edu // C: Message-Id: // C: MIME-Version: 1.0 // C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII // C: // C: Hello Joe, do you think we can meet at 3:30 tomorrow? // C: // S: A003 OK APPEND completed } #[test] fn test_from_check() { let trace = br#"C: FXXZ CHECK S: FXXZ OK CHECK Completed "#; test_lines_of_trace(trace); } #[test] fn test_from_close() { let trace = br#"C: A341 CLOSE S: A341 OK CLOSE completed "#; test_lines_of_trace(trace); } #[test] fn test_from_expunge() { let trace = br#"C: A202 EXPUNGE S: * 3 EXPUNGE S: * 3 EXPUNGE S: * 5 EXPUNGE S: * 8 EXPUNGE S: A202 OK EXPUNGE completed "#; test_lines_of_trace(trace); } #[test] fn test_from_search() { // C: A284 SEARCH CHARSET UTF-8 TEXT {6} // C: XXXXXX let trace = br#"C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith" S: * SEARCH 2 84 882 S: A282 OK SEARCH completed C: A283 SEARCH TEXT "string not in mailbox" S: * SEARCH S: A283 OK SEARCH completed S: * SEARCH 43 S: A284 OK SEARCH completed "#; test_lines_of_trace(trace); } #[test] fn test_from_fetch() { // S: * 2 FETCH .... // S: * 3 FETCH .... // S: * 4 FETCH .... let trace = br#"C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) S: A654 OK FETCH completed "#; test_lines_of_trace(trace); } #[test] fn test_from_store() { let trace = br#"C: A003 STORE 2:4 +FLAGS (\Deleted) S: * 2 FETCH (FLAGS (\Deleted \Seen)) S: * 3 FETCH (FLAGS (\Deleted)) S: * 4 FETCH (FLAGS (\Deleted \Flagged \Seen)) S: A003 OK STORE completed "#; test_lines_of_trace(trace); } #[test] fn test_from_copy() { let trace = br#"C: A003 COPY 2:4 MEETING S: A003 OK COPY completed "#; test_lines_of_trace(trace); } #[test] fn test_from_uid() { let trace = br#"C: A999 UID FETCH 4827313:4828442 FLAGS S: * 23 FETCH (FLAGS (\Seen) UID 4827313) S: * 24 FETCH (FLAGS (\Seen) UID 4827943) S: * 25 FETCH (FLAGS (\Seen) UID 4828442) S: A999 OK UID FETCH completed "#; test_lines_of_trace(trace); } //#[test] //fn test_from_X() { // let trace = br#"C: a441 CAPABILITY //S: * CAPABILITY IMAP4rev1 XPIG-LATIN //S: a441 OK CAPABILITY completed //C: A442 XPIG-LATIN //S: * XPIG-LATIN ow-nay eaking-spay ig-pay atin-lay //S: A442 OK XPIG-LATIN ompleted-cay"#; // // test_lines_of_trace(trace); //} #[test] fn test_transcript_from_rfc() { let tests = { vec![ ( b"* OK IMAP4rev1 Service Ready\r\n".as_ref(), Message::Response(Response::Status( Status::ok(None, None, "IMAP4rev1 Service Ready").unwrap(), )), ), ( b"a001 login mrc secret\r\n", Message::Command( Command::new("a001", CommandBody::login("mrc", "secret").unwrap()).unwrap(), ), ), ( b"a001 OK LOGIN completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a001").unwrap()), None, "LOGIN completed", ) .unwrap(), )), ), ( b"a002 select inbox\r\n", Message::Command( Command::new("a002", CommandBody::select("inbox").unwrap()).unwrap(), ), ), ( b"* 18 EXISTS\r\n", Message::Response(Response::Data(Data::Exists(18))), ), ( b"* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n", Message::Response(Response::Data(Data::Flags(vec![ Flag::Answered, Flag::Flagged, Flag::Deleted, Flag::Seen, Flag::Draft, ]))), ), ( b"* 2 RECENT\r\n", Message::Response(Response::Data(Data::Recent(2))), ), ( b"* OK [UNSEEN 17] Message 17 is the first unseen message\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::unseen(17).unwrap()), "Message 17 is the first unseen message", ) .unwrap(), )), ), ( b"* OK [UIDVALIDITY 3857529045] UIDs valid\r\n", Message::Response(Response::Status( Status::ok( None, Some(Code::uidvalidity(3857529045).unwrap()), "UIDs valid", ) .unwrap(), )), ), ( b"a002 OK [READ-WRITE] SELECT completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a002").unwrap()), Some(Code::ReadWrite), "SELECT completed", ) .unwrap(), )), ), ( b"a003 fetch 12 full\r\n", Message::Command( Command::new( "a003", CommandBody::fetch("12", Macro::Full, false).unwrap(), ) .unwrap(), ), ), ( b"* 12 FETCH (FLAGS (\\Seen) INTERNALDATE \"17-Jul-1996 02:44:25 -0700\" RFC822.SIZE 4286 ENVELOPE (\"Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\" \"IMAP4rev1 WG mtg summary and minutes\" ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) ((NIL NIL \"imap\" \"cac.washington.edu\")) ((NIL NIL \"minutes\" \"CNRI.Reston.VA.US\")(\"John Klensin\" NIL \"KLENSIN\" \"MIT.EDU\")) NIL NIL \"\") BODY (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 3028 92))\r\n", Message::Response(Response::Data( Data::fetch( 12, vec![ MessageDataItem::Flags(vec![FlagFetch::Flag(Flag::Seen)]), MessageDataItem::InternalDate(DateTime::try_from( chrono::DateTime::parse_from_rfc3339("1996-07-17T02:44:25-07:00") .unwrap(), ).unwrap()), MessageDataItem::Rfc822Size(4286), MessageDataItem::Envelope(Envelope { date: NString::from( Quoted::try_from("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)") .unwrap(), ), subject: NString::from( Quoted::try_from("IMAP4rev1 WG mtg summary and minutes") .unwrap(), ), from: vec![Address { name: NString::from(Quoted::try_from("Terry Gray").unwrap()), adl: NString(None), mailbox: NString::from(Quoted::try_from("gray").unwrap()), host: NString::from( Quoted::try_from("cac.washington.edu").unwrap(), ), }], sender: vec![Address { name: NString::from(Quoted::try_from("Terry Gray").unwrap()), adl: NString(None), mailbox: NString::from(Quoted::try_from("gray").unwrap()), host: NString::from( Quoted::try_from("cac.washington.edu").unwrap(), ), }], reply_to: vec![Address { name: NString::from(Quoted::try_from("Terry Gray").unwrap()), adl: NString(None), mailbox: NString::from(Quoted::try_from("gray").unwrap()), host: NString::from( Quoted::try_from("cac.washington.edu").unwrap(), ), }], to: vec![Address { name: NString(None), adl: NString(None), mailbox: NString::from(Quoted::try_from("imap").unwrap()), host: NString::from( Quoted::try_from("cac.washington.edu").unwrap(), ), }], cc: vec![ Address { name: NString(None), adl: NString(None), mailbox: NString::from( Quoted::try_from("minutes").unwrap(), ), host: NString::from( Quoted::try_from("CNRI.Reston.VA.US").unwrap(), ), }, Address { name: NString::from( Quoted::try_from("John Klensin").unwrap(), ), adl: NString(None), mailbox: NString::from( Quoted::try_from("KLENSIN").unwrap(), ), host: NString::from(Quoted::try_from("MIT.EDU").unwrap()), }, ], bcc: vec![], in_reply_to: NString(None), message_id: NString::from( Quoted::try_from("") .unwrap(), ), }), MessageDataItem::Body(BodyStructure::Single { body: Body { basic: BasicFields { parameter_list: vec![( IString::from(Quoted::try_from("CHARSET").unwrap()), IString::from(Quoted::try_from("US-ASCII").unwrap()), )], id: NString(None), description: NString(None), content_transfer_encoding: IString::from( Quoted::try_from("7BIT").unwrap(), ), size: 3028, }, specific: SpecificFields::Text { subtype: IString::from(Quoted::try_from("PLAIN").unwrap()), number_of_lines: 92, }, }, extension_data: None, }), ], ) .unwrap(), )), ), ( b"a003 OK FETCH completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a003").unwrap()), None, "FETCH completed", ) .unwrap(), )), ), ( b"a004 fetch 12 body[header]\r\n", Message::Command( Command::new( "a004", CommandBody::fetch( "12", vec![MessageDataItemName::BodyExt { section: Some(Section::Header(None)), peek: false, partial: None, }], false, ) .unwrap(), ) .unwrap(), ), ), ( b"* 12 FETCH (BODY[HEADER] {342}\r Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\r From: Terry Gray \r Subject: IMAP4rev1 WG mtg summary and minutes\r To: imap@cac.washington.edu\r cc: minutes@CNRI.Reston.VA.US, John Klensin \r Message-Id: \r MIME-Version: 1.0\r Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r \r )\r\n", Message::Response(Response::Data( Data::fetch( 12, vec![MessageDataItem::BodyExt { section: Some(Section::Header(None)), origin: None, data: NString::from( Literal::try_from( b"Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\r From: Terry Gray \r Subject: IMAP4rev1 WG mtg summary and minutes\r To: imap@cac.washington.edu\r cc: minutes@CNRI.Reston.VA.US, John Klensin \r Message-Id: \r MIME-Version: 1.0\r Content-Type: TEXT/PLAIN; CHARSET=US-ASCII\r \r " .as_ref(), ) .unwrap(), ), }], ) .unwrap(), )), ), ( b"a004 OK FETCH completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a004").unwrap()), None, "FETCH completed", ) .unwrap(), )), ), ( b"a005 store 12 +flags \\deleted\r\n", Message::Command( Command::new( "a005", CommandBody::store( "12", StoreType::Add, StoreResponse::Answer, vec![Flag::Deleted], false, ) .unwrap(), ) .unwrap(), ), ), ( b"* 12 FETCH (FLAGS (\\Seen \\Deleted))\r\n", Message::Response(Response::Data( Data::fetch( 12, vec![MessageDataItem::Flags(vec![ FlagFetch::Flag(Flag::Seen), FlagFetch::Flag(Flag::Deleted), ])], ) .unwrap(), )), ), ( b"a005 OK +FLAGS completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a005").unwrap()), None, "+FLAGS completed", ) .unwrap(), )), ), ( b"a006 logout\r\n", Message::Command(Command::new("a006", CommandBody::Logout).unwrap()), ), ( b"* BYE IMAP4rev1 server terminating connection\r\n", Message::Response(Response::Status( Status::bye(None, "IMAP4rev1 server terminating connection").unwrap(), )), ), ( b"a006 OK LOGOUT completed\r\n", Message::Response(Response::Status( Status::ok( Some(Tag::try_from("a006").unwrap()), None, "LOGOUT completed", ) .unwrap(), )), ), ] }; test_trace_known_positive(tests); } #[test] fn test_transcript_from_rfc5161() { let trace = br#"C: t1 CAPABILITY S: * CAPABILITY IMAP4rev1 ID LITERAL+ ENABLE X-GOOD-IDEA S: t1 OK foo C: t2 ENABLE CONDSTORE X-GOOD-IDEA S: * ENABLED X-GOOD-IDEA S: t2 OK foo C: t3 CAPABILITY S: * CAPABILITY IMAP4rev1 ID LITERAL+ ENABLE X-GOOD-IDEA S: t3 OK foo again C: a1 ENABLE CONDSTORE S: * ENABLED CONDSTORE S: a1 OK Conditional Store enabled "#; test_lines_of_trace(trace); } #[test] fn test_response_status_ok() { let trace = br#"S: * OK IMAP4rev1 server ready C: A001 LOGIN fred blurdybloop S: * OK [ALERT] System shutdown in 10 minutes S: A001 OK LOGIN Completed "#; test_lines_of_trace(trace); } #[test] fn test_response_status_no() { let trace = br#"C: A222 COPY 1:2 owatagusiam S: * NO Disk is 98% full, please delete unnecessary data S: A222 OK COPY completed C: A223 COPY 3:200 blurdybloop S: * NO Disk is 98% full, please delete unnecessary data S: * NO Disk is 99% full, please delete unnecessary data S: A223 NO COPY failed: disk is full "#; test_lines_of_trace(trace); } #[test] fn test_response_status_bad() { let trace = br#"S: * BAD Command line too long S: * BAD Empty command line C: A443 EXPUNGE S: * BAD Disk crash, attempting salvage to a new disk! S: * OK Salvage successful, no data lost S: A443 OK Expunge completed "#; test_lines_of_trace(trace); } #[test] fn test_response_status_preauth() { let line = b"* PREAUTH IMAP4rev1 server logged in as Smith\r\n"; println!("S: {}", String::from_utf8_lossy(line).trim()); let (rem, parsed) = GreetingCodec::default().decode(line).unwrap(); println!("Parsed: {parsed:?}"); assert!(rem.is_empty()); let serialized = GreetingCodec::default().encode(&parsed).dump(); println!( "Serialized: {}", String::from_utf8_lossy(&serialized).trim() ); let (rem, parsed2) = GreetingCodec::default().decode(&serialized).unwrap(); assert!(rem.is_empty()); assert_eq!(parsed, parsed2); println!() } #[test] fn test_response_status_bye() { let trace = br#"S: * BYE Autologout; idle for too long "#; test_lines_of_trace(trace); } #[test] fn test_response_data_capability() { let trace = br#"S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI XPIG-LATIN "#; test_lines_of_trace(trace); } #[test] fn test_response_data_list() { let trace = br#"S: * LIST (\Noselect) "/" ~/Mail/foo "#; test_lines_of_trace(trace); } #[test] fn test_response_data_lsub() { let trace = br#"S: * LSUB () "." #news.comp.mail.misc "#; test_lines_of_trace(trace); } #[test] fn test_response_data_status() { let trace = br#"S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292) "#; test_lines_of_trace(trace); } #[test] fn test_response_data_search() { let trace = br#"S: * SEARCH 2 3 6 "#; test_lines_of_trace(trace); } #[test] fn test_response_data_flags() { let trace = br#"S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) "#; test_lines_of_trace(trace); } #[test] fn test_response_data_exists() { let trace = br#"S: * 23 EXISTS "#; test_lines_of_trace(trace); } #[test] fn test_response_data_recent() { let trace = br#"S: * 5 RECENT "#; test_lines_of_trace(trace); } #[test] fn test_response_data_expunge() { let trace = br#"S: * 44 EXPUNGE "#; test_lines_of_trace(trace); } #[test] fn test_response_data_fetch() { let trace = br#"S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827) "#; test_lines_of_trace(trace); } #[test] fn test_response_data_continue() { // C: A001 LOGIN {11} // C: FRED FOOBAR {7} // C: fat man // C: A044 BLURDYBLOOP {102856} let trace = br#"S: + Ready for additional command text S: A001 OK LOGIN completed S: A044 BAD No such command as "BLURDYBLOOP" "#; test_lines_of_trace(trace); } #[test] fn test_trace_rfc2088() { let test = b"A001 LOGIN {11+}\r\nFRED FOOBAR {7+}\r\nfat man\r\n".as_ref(); let (rem, got) = CommandCodec::default().decode(test).unwrap(); assert!(rem.is_empty()); assert_eq!(got, { let username = Literal::try_from("FRED FOOBAR").unwrap().into_non_sync(); let password = Literal::try_from("fat man").unwrap().into_non_sync(); Command::new( Tag::try_from("A001").unwrap(), CommandBody::login(username, password).unwrap(), ) .unwrap() }) } #[test] fn test_trace_sort() { let trace = br#"C: A282 SORT (SUBJECT) UTF-8 SINCE 1-Feb-1994 S: * SORT 2 84 882 S: A282 OK SORT completed C: A283 SORT (SUBJECT REVERSE DATE) UTF-8 ALL S: * SORT 5 3 4 1 2 S: A283 OK SORT completed C: A284 SORT (SUBJECT) US-ASCII TEXT "not in mailbox" S: * SORT S: A284 OK SORT completed "#; test_lines_of_trace(trace); } #[test] fn test_trace_thread() { let trace = br#"C: A283 THREAD ORDEREDSUBJECT UTF-8 SINCE 5-MAR-2000 S: * THREAD (166)(167)(168)(169)(172)(170)(171)(173)(174 (175)(176)(178)(181)(180))(179)(177 (183)(182)(188)(184)(185)(186)(187)(189))(190)(191)(192)(193)(194 195)(196 (197)(198))(199)(200 202)(201)(203)(204)(205)(206 207)(208) S: A283 OK THREAD completed C: A284 THREAD ORDEREDSUBJECT US-ASCII TEXT "gewp" S: * THREAD S: A284 OK THREAD completed C: A285 THREAD REFERENCES UTF-8 SINCE 5-MAR-2000 S: * THREAD (166)(167)(168)(169)(172)((170)(179))(171)(173)((174)(175)(176)(178)(181)(180))((177)(183)(182)(188 (184)(189))(185 186)(187))(190)(191)(192)(193)((194)(195 196))(197 198)(199)(200 202)(201)(203)(204)(205 206 207)(208) S: A285 OK THREAD completed "#; test_lines_of_trace(trace); } duesee-imap-codec-0d00966/imap-types/000077500000000000000000000000001507724125200173325ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/Cargo.toml000066400000000000000000000033621507724125200212660ustar00rootroot00000000000000[package] name = "imap-types" description = "Misuse-resistant data structures for IMAP" keywords = ["email", "imap", "types"] categories = ["email", "data-structures", "network-programming"] version = "2.0.0-alpha.5" authors = ["Damian Poddebniak "] repository = "https://github.com/duesee/imap-codec" license = "MIT OR Apache-2.0" rust-version.workspace = true edition = "2024" exclude = [ ".github", ] [features] arbitrary = ["dep:arbitrary", "chrono/arbitrary", "chrono/std"] arbitrary_simplified = ["arbitrary"] serde = ["dep:serde", "chrono/serde"] tag_generator = ["dep:rand"] # IMAP starttls = [] ext_condstore_qresync = [] ext_id = [] ext_login_referrals = [] ext_mailbox_referrals = [] ext_metadata = [] ext_namespace = [] ext_utf8 = [] [dependencies] arbitrary = { version = "1.4.2", optional = true, default-features = false, features = ["derive"] } base64 = { version = "0.22", default-features = false, features = ["alloc"] } bounded-static-derive = { version = "0.8.0", default-features = false } bounded-static = { version = "0.8.0", default-features = false, features = ["alloc"] } chrono = { version = "0.4", default-features = false } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"], optional = true } serde = { version = "1.0.228", features = ["derive"], optional = true } thiserror = "2.0.17" [dev-dependencies] criterion = { version = "0.7.0", default-features = false } rand = { version = "0.8", default-features = false, features = ["small_rng"] } serde_json = { version = "1.0.145", default-features = false } [[example]] name = "serde_json" path = "examples/serde_json.rs" required-features = ["serde"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] duesee-imap-codec-0d00966/imap-types/README.md000066400000000000000000000101441507724125200206110ustar00rootroot00000000000000# imap-types ```mermaid %%{init: {'theme': 'neutral' } }%% flowchart LR imap-types -.-> imap-codec imap-codec -.-> imap-next imap-next -.-> imap-proxy imap-next -.-> imap-client style imap-types stroke-width:4px click imap-types href "https://github.com/duesee/imap-codec/tree/main/imap-types" click imap-codec href "https://github.com/duesee/imap-codec" click imap-next href "https://github.com/duesee/imap-next" click imap-proxy href "https://github.com/duesee/imap-proxy" click imap-client href "https://github.com/soywod/imap-client" ``` This crate provides a complete set of well-designed, misuse-resistant types for the [IMAP4rev1] protocol and various [extensions]. Notably, it does *not* provide parsers, nor serializers, but tries to become the "standard library" for IMAP in Rust that is useful for a broad range of crates. If you are looking for parsers, and serializers, head over to [`imap-codec`]. ## Features * Rust's type system is used to enforce correctness and to make the library misuse-resistant. It's not possible to construct a message that violates the IMAP specification. * Fuzzing (via [cargo fuzz]) and property-based tests are used to uncover bugs. The library is fuzz-tested never to produce an invalid message. ## Working with imap-types To ensure correctness, imap-types makes use of types such as [`AString`](core::AString), [`Atom`](core::Atom), [`IString`](core::IString), [`Quoted`](core::Quoted), and [`Literal`](core::Literal). When constructing messages, imap-types can automatically choose the best representation. However, it's always possible to manually choose a specific representation. ### Examples
Automatic Construction This ... ```rust use imap_types::command::{Command, CommandBody}; let cmd = Command::new( "A1", CommandBody::login("alice", "password").unwrap() ).unwrap(); ``` ... will produce ... ```imap A1 LOGIN alice password ``` However, ... ```rust use imap_types::command::{Command, CommandBody}; let cmd = Command::new( "A1", CommandBody::login("alice\"", b"\xCA\xFE".as_ref()).unwrap(), ).unwrap(); ``` ... will produce ... ```imap A1 LOGIN "alice\"" {2} \xCA\xFE ``` Also, the construction ... ```rust,should_panic use imap_types::command::{Command, CommandBody}; let cmd = Command::new( "A1", CommandBody::login("alice\x00", "password").unwrap(), ).unwrap(); ``` ... will fail because IMAP doesn't allow NULL bytes in the username (nor password).
Manual Construction You can also use ... ```rust use imap_types::{ command::{Command, CommandBody}, core::Literal, }; let cmd = Command::new( "A1", CommandBody::login(Literal::try_from("alice").unwrap(), "password").unwrap(), ).unwrap(); ``` ... to produce ... ```imap A1 LOGIN {5} alice password ``` ... even though "alice" could be encoded more simply with an atom or quoted string. Also, you can use Rust literals and resort to `unvalidated` constructors when you are certain that your input is correct: ```rust use imap_types::{ command::{Command, CommandBody}, core::{AString, Atom, Tag}, secret::Secret, }; // This could be provided by the email application. struct TagGenerator; impl TagGenerator { fn random() -> Tag<'static> { // Make this random :-) Tag::unvalidated("A1") } } let tag = TagGenerator::random(); let cmd = Command { tag, body: CommandBody::Login { username: AString::from(Atom::unvalidated("alice")), password: Secret::new(AString::from(Atom::unvalidated("password"))), }, }; ``` In this case, imap-codec won't stand in your way. However, it won't guarantee that you produce correct messages, either.
# License This crate is dual-licensed under Apache 2.0 and MIT terms. [IMAP4rev1]: https://datatracker.ietf.org/doc/html/rfc3501 [extensions]: https://docs.rs/imap-codec/latest/imap_codec/#features [`imap-codec`]: https://docs.rs/imap-types/latest/imap_codec/ [cargo fuzz]: https://github.com/rust-fuzz/cargo-fuzz [core]: https://docs.rs/imap-types/latest/imap_types/core/index.html duesee-imap-codec-0d00966/imap-types/examples/000077500000000000000000000000001507724125200211505ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/examples/serde_json.rs000066400000000000000000000012301507724125200236450ustar00rootroot00000000000000use imap_types::{ command::{Command, CommandBody}, core::{Tag, Text}, response::{Response, Status, StatusBody, StatusKind, Tagged}, }; fn main() { let cmd = Command::new("A1", CommandBody::login("Alice", "Pa²²word").unwrap()).unwrap(); println!("{:?}\n{}", cmd, serde_json::to_string_pretty(&cmd).unwrap()); let rsp = Response::Status(Status::Tagged(Tagged { tag: Tag::try_from("A1").unwrap(), body: StatusBody { kind: StatusKind::Ok, code: None, text: Text::try_from("...").unwrap(), }, })); println!("{:?}\n{}", rsp, serde_json::to_string_pretty(&rsp).unwrap()); } duesee-imap-codec-0d00966/imap-types/fuzz/000077500000000000000000000000001507724125200203305ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/fuzz/.gitignore000066400000000000000000000000311507724125200223120ustar00rootroot00000000000000 target corpus artifacts duesee-imap-codec-0d00966/imap-types/fuzz/Cargo.toml000066400000000000000000000023451507724125200222640ustar00rootroot00000000000000[package] name = "imap-types-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false edition = "2024" license = "MIT OR Apache-2.0" [package.metadata] cargo-fuzz = true [features] # # IMAP starttls = ["imap-types/starttls"] # IMAP Extensions ext_condstore_qresync = ["imap-types/ext_condstore_qresync"] ext_id = ["imap-types/ext_id"] ext_login_referrals = ["imap-types/ext_login_referrals"] ext_mailbox_referrals = ["imap-types/ext_mailbox_referrals"] ext_metadata = ["imap-types/ext_metadata"] ext_namespace = ["imap-types/ext_namespace"] ext_utf8 = ["imap-types/ext_utf8"] # # Use (most) IMAP extensions. ext = [ "starttls", "ext_condstore_qresync", "ext_id", #"ext_login_referrals", #"ext_mailbox_referrals", "ext_metadata", "ext_namespace", "ext_utf8", ] # Enable `Debug`-printing during parsing. This is useful to analyze crashes. debug = [] [dependencies] libfuzzer-sys = "0.4" imap-types = { path = "..", default-features = false, features = ["arbitrary"] } [[bin]] name = "into_static" path = "fuzz_targets/into_static.rs" test = false doc = false [[bin]] name = "to_static" path = "fuzz_targets/to_static.rs" test = false doc = false duesee-imap-codec-0d00966/imap-types/fuzz/fuzz_targets/000077500000000000000000000000001507724125200230575ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/fuzz/fuzz_targets/into_static.rs000066400000000000000000000006641507724125200257530ustar00rootroot00000000000000#![no_main] use imap_types::{ IntoStatic, command::Command, response::{Greeting, Response}, }; use libfuzzer_sys::fuzz_target; fuzz_target!(|tuple: (Greeting, Command, Response)| { let (grt, cmd, rsp) = tuple; let got = grt.clone().into_static(); assert_eq!(grt, got); let got = cmd.clone().into_static(); assert_eq!(cmd, got); let got = rsp.clone().into_static(); assert_eq!(rsp, got); }); duesee-imap-codec-0d00966/imap-types/fuzz/fuzz_targets/to_static.rs000066400000000000000000000006241507724125200254200ustar00rootroot00000000000000#![no_main] use imap_types::{ ToStatic, command::Command, response::{Greeting, Response}, }; use libfuzzer_sys::fuzz_target; fuzz_target!(|tuple: (Greeting, Command, Response)| { let (grt, cmd, rsp) = tuple; let got = grt.to_static(); assert_eq!(grt, got); let got = cmd.to_static(); assert_eq!(cmd, got); let got = rsp.to_static(); assert_eq!(rsp, got); }); duesee-imap-codec-0d00966/imap-types/src/000077500000000000000000000000001507724125200201215ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/src/arbitrary.rs000066400000000000000000000506211507724125200224720ustar00rootroot00000000000000use arbitrary::{Arbitrary, Unstructured}; use chrono::{FixedOffset, TimeZone}; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::AttributeFlag; #[cfg(feature = "ext_utf8")] use crate::extensions::utf8::QuotedUtf8; use crate::{ auth::AuthMechanism, body::{ BasicFields, Body, BodyExtension, BodyStructure, SinglePartExtensionData, SpecificFields, }, core::{ AString, Atom, AtomExt, IString, Literal, LiteralMode, NString, Quoted, QuotedChar, Tag, Text, Vec1, Vec2, }, datetime::{DateTime, NaiveDate}, extensions::{enable::CapabilityEnable, quota::Resource}, flag::{Flag, FlagNameAttribute}, mailbox::{ListCharString, Mailbox, MailboxOther}, response::{ Bye, Capability, Code, CodeOther, CommandContinuationRequestBasic, Greeting, GreetingKind, Status, StatusBody, StatusKind, Tagged, }, search::SearchKey, sequence::SequenceSet, }; #[cfg(not(feature = "arbitrary_simplified"))] use crate::{body::MultiPartExtensionData, envelope::Envelope}; macro_rules! impl_arbitrary_try_from { ($target:ty, $from:ty) => { impl<'a> Arbitrary<'a> for $target { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { match <$target>::try_from(<$from>::arbitrary(u)?) { Ok(passed) => Ok(passed), #[allow(unreachable_patterns)] Err(_) => Err(arbitrary::Error::IncorrectFormat), } } } }; } pub(crate) use impl_arbitrary_try_from; macro_rules! impl_arbitrary_try_from_t { ($target:ty, $from:ty) => { impl<'a, T> Arbitrary<'a> for $target where T: Arbitrary<'a>, { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { match <$target>::try_from(<$from>::arbitrary(u)?) { Ok(passed) => Ok(passed), Err(_) => Err(arbitrary::Error::IncorrectFormat), } } } }; } impl_arbitrary_try_from! { Atom<'a>, &str } impl_arbitrary_try_from! { AtomExt<'a>, &str } impl_arbitrary_try_from! { Quoted<'a>, &str } impl_arbitrary_try_from! { Tag<'a>, &str } impl_arbitrary_try_from! { Text<'a>, &str } impl_arbitrary_try_from! { ListCharString<'a>, &str } impl_arbitrary_try_from! { QuotedChar, char } impl_arbitrary_try_from! { Mailbox<'a>, &str } impl_arbitrary_try_from! { Capability<'a>, Atom<'a> } impl_arbitrary_try_from! { Flag<'a>, &str } #[cfg(feature = "ext_condstore_qresync")] impl_arbitrary_try_from! { AttributeFlag<'a>, &str } impl_arbitrary_try_from! { FlagNameAttribute<'a>, Atom<'a> } impl_arbitrary_try_from! { MailboxOther<'a>, AString<'a> } impl_arbitrary_try_from! { CapabilityEnable<'a>, &str } impl_arbitrary_try_from! { Resource<'a>, &str } impl_arbitrary_try_from! { AuthMechanism<'a>, &str } impl_arbitrary_try_from_t! { Vec1, Vec } impl_arbitrary_try_from_t! { Vec2, Vec } impl<'a> Arbitrary<'a> for CommandContinuationRequestBasic<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { Self::new(Option::::arbitrary(u)?, Text::arbitrary(u)?) .map_err(|_| arbitrary::Error::IncorrectFormat) } } // TODO(#301): This is due to the `Code`/`Text` ambiguity. impl<'a> Arbitrary<'a> for Greeting<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { Ok(Greeting { kind: GreetingKind::arbitrary(u)?, code: Option::::arbitrary(u)?, text: { let text = Text::arbitrary(u)?; if text.as_ref().starts_with('[') { Text::unvalidated("...") } else { text } }, }) } } // TODO(#301): This is due to the `Code`/`Text` ambiguity. impl<'a> Arbitrary<'a> for Status<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { let code = Option::::arbitrary(u)?; let text = if code.is_some() { Arbitrary::arbitrary(u)? } else { let text = Text::arbitrary(u)?; if text.as_ref().starts_with('[') { Text::unvalidated("...") } else { text } }; Ok(match u.int_in_range(0u8..=3)? { 0 => { let body = StatusBody { kind: StatusKind::Ok, code, text, }; match Arbitrary::arbitrary(u)? { Some(tag) => Status::Tagged(Tagged { tag, body }), None => Status::Untagged(body), } } 1 => { let body = StatusBody { kind: StatusKind::No, code, text, }; match Arbitrary::arbitrary(u)? { Some(tag) => Status::Tagged(Tagged { tag, body }), None => Status::Untagged(body), } } 2 => { let body = StatusBody { kind: StatusKind::Bad, code, text, }; match Arbitrary::arbitrary(u)? { Some(tag) => Status::Tagged(Tagged { tag, body }), None => Status::Untagged(body), } } 3 => Status::Bye(Bye { code, text }), _ => unreachable!(), }) } } impl<'a> Arbitrary<'a> for IString<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { #[cfg(not(feature = "ext_utf8"))] return Ok(match u.int_in_range(0..=1)? { 0 => Self::Quoted(Quoted::arbitrary(u)?), 1 => Self::Literal(Literal::arbitrary(u)?), _ => unreachable!(), }); #[cfg(feature = "ext_utf8")] return Ok(match u.int_in_range(0..=2)? { 0 => Self::Quoted(Quoted::arbitrary(u)?), 1 => { // When the generated `QuotedUtf8` is also a valid // `Quoted`, we use `Quoted` to avoid the case that // a generated `QuotedUtf8` is reconstructed as `Quoted` // and breaks our structured fuzz tests. let arb = QuotedUtf8::arbitrary(u)?; if Quoted::validate(arb.0.as_ref()).is_ok() { Self::Quoted(Quoted(arb.0)) } else { Self::QuotedUtf8(arb) } } 2 => Self::Literal(Literal::arbitrary(u)?), _ => unreachable!(), }); } } impl<'a> Arbitrary<'a> for Literal<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { match Literal::try_from(<&[u8]>::arbitrary(u)?) { Ok(mut passed) => { passed.mode = LiteralMode::arbitrary(u)?; Ok(passed) } Err(_) => Err(arbitrary::Error::IncorrectFormat), } } } impl<'a> Arbitrary<'a> for CodeOther<'a> { fn arbitrary(_: &mut Unstructured<'a>) -> arbitrary::Result { // `CodeOther` is a fallback and should usually not be created. Ok(CodeOther::unvalidated(b"IMAP-CODEC-CODE-OTHER>".as_ref())) } } impl<'a> Arbitrary<'a> for SearchKey<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { #[cfg(not(feature = "arbitrary_simplified"))] return arbitrary_search_key_limited(u, 7); #[cfg(feature = "arbitrary_simplified")] return arbitrary_search_key_leaf(u); } } #[cfg(not(feature = "arbitrary_simplified"))] fn arbitrary_search_key_limited<'a>( u: &mut Unstructured<'a>, depth: u8, ) -> arbitrary::Result> { if depth == 0 { return arbitrary_search_key_leaf(u); } let till = if cfg!(feature = "ext_condstore_qresync") { 37 } else { 36 }; Ok(match u.int_in_range(0u8..=till)? { 0 => SearchKey::And({ let keys = { let len = u.arbitrary_len::()?; let mut tmp = Vec::with_capacity(len); for _ in 0..len { tmp.push(arbitrary_search_key_limited(u, depth - 1)?); } tmp }; if !keys.is_empty() { Vec1::try_from(keys).unwrap() } else { Vec1::from(arbitrary_search_key_leaf(u)?) } }), 1 => SearchKey::SequenceSet(SequenceSet::arbitrary(u)?), 2 => SearchKey::All, 3 => SearchKey::Answered, 4 => SearchKey::Bcc(AString::arbitrary(u)?), 5 => SearchKey::Before(NaiveDate::arbitrary(u)?), 6 => SearchKey::Body(AString::arbitrary(u)?), 7 => SearchKey::Cc(AString::arbitrary(u)?), 8 => SearchKey::Deleted, 9 => SearchKey::Draft, 10 => SearchKey::Flagged, 11 => SearchKey::From(AString::arbitrary(u)?), 12 => SearchKey::Header(AString::arbitrary(u)?, AString::arbitrary(u)?), 13 => SearchKey::Keyword(Atom::arbitrary(u)?), 14 => SearchKey::Larger(u32::arbitrary(u)?), 15 => SearchKey::New, 16 => SearchKey::Not(Box::new(arbitrary_search_key_limited(u, depth - 1)?)), 17 => SearchKey::Old, 18 => SearchKey::On(NaiveDate::arbitrary(u)?), 19 => SearchKey::Or( Box::new(arbitrary_search_key_limited(u, depth - 1)?), Box::new(arbitrary_search_key_limited(u, depth - 1)?), ), 20 => SearchKey::Recent, 21 => SearchKey::Seen, 22 => SearchKey::SentBefore(NaiveDate::arbitrary(u)?), 23 => SearchKey::SentOn(NaiveDate::arbitrary(u)?), 24 => SearchKey::SentSince(NaiveDate::arbitrary(u)?), 25 => SearchKey::Since(NaiveDate::arbitrary(u)?), 26 => SearchKey::Smaller(u32::arbitrary(u)?), 27 => SearchKey::Subject(AString::arbitrary(u)?), 28 => SearchKey::Text(AString::arbitrary(u)?), 29 => SearchKey::To(AString::arbitrary(u)?), 30 => SearchKey::Uid(SequenceSet::arbitrary(u)?), 31 => SearchKey::Unanswered, 32 => SearchKey::Undeleted, 33 => SearchKey::Undraft, 34 => SearchKey::Unflagged, 35 => SearchKey::Unkeyword(Atom::arbitrary(u)?), 36 => SearchKey::Unseen, #[cfg(feature = "ext_condstore_qresync")] 37 => SearchKey::ModSequence { entry: Arbitrary::arbitrary(u)?, modseq: Arbitrary::arbitrary(u)?, }, _ => unreachable!(), }) } fn arbitrary_search_key_leaf<'a>(u: &mut Unstructured<'a>) -> arbitrary::Result> { Ok(match u.int_in_range(0u8..=33)? { 0 => SearchKey::SequenceSet(SequenceSet::arbitrary(u)?), 1 => SearchKey::All, 2 => SearchKey::Answered, 3 => SearchKey::Bcc(AString::arbitrary(u)?), 4 => SearchKey::Before(NaiveDate::arbitrary(u)?), 5 => SearchKey::Body(AString::arbitrary(u)?), 6 => SearchKey::Cc(AString::arbitrary(u)?), 7 => SearchKey::Deleted, 8 => SearchKey::Draft, 9 => SearchKey::Flagged, 10 => SearchKey::From(AString::arbitrary(u)?), 11 => SearchKey::Header(AString::arbitrary(u)?, AString::arbitrary(u)?), 12 => SearchKey::Keyword(Atom::arbitrary(u)?), 13 => SearchKey::Larger(u32::arbitrary(u)?), 14 => SearchKey::New, 15 => SearchKey::Old, 16 => SearchKey::On(NaiveDate::arbitrary(u)?), 17 => SearchKey::Recent, 18 => SearchKey::Seen, 19 => SearchKey::SentBefore(NaiveDate::arbitrary(u)?), 20 => SearchKey::SentOn(NaiveDate::arbitrary(u)?), 21 => SearchKey::SentSince(NaiveDate::arbitrary(u)?), 22 => SearchKey::Since(NaiveDate::arbitrary(u)?), 23 => SearchKey::Smaller(u32::arbitrary(u)?), 24 => SearchKey::Subject(AString::arbitrary(u)?), 25 => SearchKey::Text(AString::arbitrary(u)?), 26 => SearchKey::To(AString::arbitrary(u)?), 27 => SearchKey::Uid(SequenceSet::arbitrary(u)?), 28 => SearchKey::Unanswered, 29 => SearchKey::Undeleted, 30 => SearchKey::Undraft, 31 => SearchKey::Unflagged, 32 => SearchKey::Unkeyword(Atom::arbitrary(u)?), 33 => SearchKey::Unseen, _ => unreachable!(), }) } impl<'a> Arbitrary<'a> for BodyStructure<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { #[cfg(not(feature = "arbitrary_simplified"))] return arbitrary_body_structure_limited(u, 3); #[cfg(feature = "arbitrary_simplified")] return arbitrary_body_structure_leaf(u); } } #[cfg(not(feature = "arbitrary_simplified"))] fn arbitrary_body_structure_limited<'a>( u: &mut Unstructured<'a>, depth: u8, ) -> arbitrary::Result> { if depth == 0 { return arbitrary_body_structure_leaf(u); } Ok(match u.int_in_range(1..=2)? { 1 => BodyStructure::Single { body: Body { basic: BasicFields::arbitrary(u)?, specific: match u.int_in_range(1..=3)? { 1 => SpecificFields::Basic { r#type: IString::arbitrary(u)?, subtype: IString::arbitrary(u)?, }, 2 => SpecificFields::Message { envelope: Box::::arbitrary(u)?, body_structure: Box::new(arbitrary_body_structure_limited(u, depth - 1)?), number_of_lines: u32::arbitrary(u)?, }, 3 => SpecificFields::Text { subtype: IString::arbitrary(u)?, number_of_lines: u32::arbitrary(u)?, }, _ => unreachable!(), }, }, extension_data: Option::::arbitrary(u)?, }, 2 => BodyStructure::Multi { bodies: { let bodies = { let len = u.arbitrary_len::()?; let mut tmp = Vec::with_capacity(len); for _ in 0..len { tmp.push(arbitrary_body_structure_limited(u, depth - 1)?); } tmp }; if !bodies.is_empty() { Vec1::try_from(bodies).unwrap() } else { Vec1::from(arbitrary_body_structure_leaf(u)?) } }, subtype: IString::arbitrary(u)?, extension_data: Option::::arbitrary(u)?, }, _ => unreachable!(), }) } fn arbitrary_body_structure_leaf<'a>( u: &mut Unstructured<'a>, ) -> arbitrary::Result> { Ok(BodyStructure::Single { body: Body { basic: BasicFields::arbitrary(u)?, specific: match u.int_in_range(1..=2)? { 1 => SpecificFields::Basic { r#type: IString::arbitrary(u)?, subtype: IString::arbitrary(u)?, }, // No SpecificFields::Message because it would recurse. 2 => SpecificFields::Text { subtype: IString::arbitrary(u)?, number_of_lines: u32::arbitrary(u)?, }, _ => unreachable!(), }, }, extension_data: Option::::arbitrary(u)?, }) } impl<'a> Arbitrary<'a> for BodyExtension<'a> { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { #[cfg(not(feature = "arbitrary_simplified"))] return arbitrary_body_extension_limited(u, 3); #[cfg(feature = "arbitrary_simplified")] return arbitrary_body_extension_leaf(u); } } #[cfg(not(feature = "arbitrary_simplified"))] fn arbitrary_body_extension_limited<'a>( u: &mut Unstructured<'a>, depth: u8, ) -> arbitrary::Result> { if depth == 0 { return arbitrary_body_extension_leaf(u); } Ok(match u.int_in_range(1..=2)? { 1 => BodyExtension::NString(NString::arbitrary(u)?), 2 => BodyExtension::Number(u32::arbitrary(u)?), 3 => BodyExtension::List({ let body_extensions = { let len = u.arbitrary_len::()?; let mut tmp = Vec::with_capacity(len); for _ in 0..len { tmp.push(arbitrary_body_extension_limited(u, depth - 1)?); } tmp }; if !body_extensions.is_empty() { Vec1::try_from(body_extensions).unwrap() } else { Vec1::from(arbitrary_body_extension_leaf(u)?) } }), _ => unreachable!(), }) } fn arbitrary_body_extension_leaf<'a>( u: &mut Unstructured<'a>, ) -> arbitrary::Result> { Ok(match u.int_in_range(1..=2)? { 1 => BodyExtension::NString(NString::arbitrary(u)?), 2 => BodyExtension::Number(u32::arbitrary(u)?), // No `BodyExtension::List` because it could recurse. _ => unreachable!(), }) } impl<'a> Arbitrary<'a> for DateTime { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { // Note: `chrono`s `NaiveDate::arbitrary` may `panic!`. // Thus, we implement this manually here. let local_datetime = chrono::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt( u.int_in_range(0..=9999)?, u.int_in_range(1..=12)?, u.int_in_range(1..=31)?, ) .ok_or(arbitrary::Error::IncorrectFormat)?, chrono::NaiveTime::arbitrary(u)?, ); let hours = u.int_in_range(0..=23 * 3600)?; let minutes = u.int_in_range(0..=59)? * 60; // Seconds must be zero due to IMAPs encoding. DateTime::try_from( FixedOffset::east_opt(hours + minutes) .unwrap() .from_local_datetime(&local_datetime) .unwrap(), ) .map_err(|_| arbitrary::Error::IncorrectFormat) } } impl<'a> Arbitrary<'a> for NaiveDate { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { NaiveDate::try_from(chrono::NaiveDate::arbitrary(u)?) .map_err(|_| arbitrary::Error::IncorrectFormat) } } #[cfg(test)] mod tests { use arbitrary::{Arbitrary, Error, Unstructured}; use rand::{Rng, SeedableRng, rngs::SmallRng}; use crate::{ IntoStatic, ToStatic, command::Command, response::{Greeting, Response}, }; /// Note: We could encode/decode/etc. here but only want to exercise the arbitrary logic itself. macro_rules! impl_test_arbitrary { ($object:ty) => { let mut rng = SmallRng::seed_from_u64(1337); let mut data = [0u8; 256]; // Randomize. rng.try_fill(&mut data).unwrap(); let mut unstructured = Unstructured::new(&data); let mut count = 0; loop { match <$object>::arbitrary(&mut unstructured) { Ok(_out) => { count += 1; { let out_to_static = _out.to_static(); assert_eq!(_out, out_to_static); let out_into_static = _out.into_static(); assert_eq!(out_to_static, out_into_static); } if count >= 1_000 { break; } } Err(Error::NotEnoughData | Error::IncorrectFormat) => { // Randomize. rng.try_fill(&mut data).unwrap(); unstructured = Unstructured::new(&data); } Err(Error::EmptyChoose) => { unreachable!(); } Err(_) => { unimplemented!() } } } }; } #[test] fn test_arbitrary_greeting() { impl_test_arbitrary! {Greeting} } #[test] fn test_arbitrary_command() { impl_test_arbitrary! {Command} } #[test] fn test_arbitrary_response() { impl_test_arbitrary! {Response} } } duesee-imap-codec-0d00966/imap-types/src/auth.rs000066400000000000000000000147121507724125200214350ustar00rootroot00000000000000//! Authentication-related types. use std::{ borrow::Cow, fmt::{Display, Formatter}, str::FromStr, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ core::{Atom, impl_try_from}, error::ValidationError, secret::Secret, }; /// Authentication mechanism. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[non_exhaustive] pub enum AuthMechanism<'a> { /// The PLAIN SASL mechanism. /// /// ```imap /// AUTH=PLAIN /// ``` /// /// ```text /// base64(b"\x00\x00") /// ``` /// /// # Reference(s): /// /// * RFC4616: The PLAIN Simple Authentication and Security Layer (SASL) Mechanism Plain, /// The (non-standardized and slow) LOGIN SASL mechanism. /// /// ```imap /// AUTH=LOGIN /// ``` /// /// ```text /// base64(b"") /// base64(b"") /// ``` /// /// # Reference(s): /// /// + draft-murchison-sasl-login-00: The LOGIN SASL Mechanism Login, /// OAuth 2.0 bearer token mechanism. /// /// ```imap /// AUTH=OAUTHBEARER /// ``` /// /// ```text /// base64(b"n,a=,\x01host=\x01port=\x01auth=Bearer \x01\x01") /// ``` /// /// # Reference(s): /// /// * OAuthBearer, /// Google's OAuth 2.0 mechanism. /// /// ```imap /// AUTH=XOAUTH2 /// ``` /// /// ```text /// base64(b"user=\x01auth=Bearer \x01\x01") /// ``` /// /// # Reference(s): /// /// * XOAuth2, // // --- SHA-1 --- // /// SCRAM-SHA-1 /// /// # Reference(s): /// /// * ScramSha1, /// SCRAM-SHA-1-PLUS /// /// # Reference(s): /// /// * ScramSha1Plus, // // --- SHA-2 --- // /// SCRAM-SHA-256 /// /// # Reference(s): /// /// * ScramSha256, /// SCRAM-SHA-256-PLUS /// /// # Reference(s): /// /// * ScramSha256Plus, // // --- SHA-3 --- // /// SCRAM-SHA3-512 /// /// # Reference(s): /// /// * ScramSha3_512, /// SCRAM-SHA3-512-PLUS /// /// # Reference(s): /// /// * ScramSha3_512Plus, /// Some other (unknown) mechanism. Other(AuthMechanismOther<'a>), } impl_try_from!(Atom<'a>, 'a, &'a [u8], AuthMechanism<'a>); impl_try_from!(Atom<'a>, 'a, Vec, AuthMechanism<'a>); impl_try_from!(Atom<'a>, 'a, &'a str, AuthMechanism<'a>); impl_try_from!(Atom<'a>, 'a, String, AuthMechanism<'a>); impl_try_from!(Atom<'a>, 'a, Cow<'a, str>, AuthMechanism<'a>); impl<'a> From> for AuthMechanism<'a> { fn from(atom: Atom<'a>) -> Self { match atom.as_ref().to_ascii_uppercase().as_str() { "PLAIN" => Self::Plain, "LOGIN" => Self::Login, "OAUTHBEARER" => Self::OAuthBearer, "XOAUTH2" => Self::XOAuth2, "SCRAM-SHA-1" => Self::ScramSha1, "SCRAM-SHA-1-PLUS" => Self::ScramSha1Plus, "SCRAM-SHA-256" => Self::ScramSha256, "SCRAM-SHA-256-PLUS" => Self::ScramSha256Plus, _ => Self::Other(AuthMechanismOther(atom)), } } } impl Display for AuthMechanism<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(self.as_ref()) } } impl AsRef for AuthMechanism<'_> { fn as_ref(&self) -> &str { match self { Self::Plain => "PLAIN", Self::Login => "LOGIN", Self::OAuthBearer => "OAUTHBEARER", Self::XOAuth2 => "XOAUTH2", Self::ScramSha1 => "SCRAM-SHA-1", Self::ScramSha1Plus => "SCRAM-SHA-1-PLUS", Self::ScramSha256 => "SCRAM-SHA-256", Self::ScramSha256Plus => "SCRAM-SHA-256-PLUS", Self::ScramSha3_512 => "SCRAM-SHA3-512", Self::ScramSha3_512Plus => "SCRAM-SHA3-512-PLUS", Self::Other(other) => other.0.as_ref(), } } } impl FromStr for AuthMechanism<'static> { type Err = ValidationError; fn from_str(s: &str) -> Result { AuthMechanism::try_from(s.to_string()) } } /// An (unknown) authentication mechanism. /// /// It's guaranteed that this type can't represent any mechanism from [`AuthMechanism`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct AuthMechanismOther<'a>(Atom<'a>); /// Data line used, e.g., during AUTHENTICATE. /// /// Holds the raw binary data, i.e., a `Vec`, *not* the BASE64 string. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum AuthenticateData<'a> { /// Continue SASL authentication. Continue(Secret>), /// Cancel SASL authentication. /// /// "If the client wishes to cancel an authentication exchange, /// it issues a line consisting of a single "*"." (RFC 3501) Cancel, } impl<'a> AuthenticateData<'a> { pub fn r#continue(data: D) -> Self where D: Into>, { Self::Continue(Secret::new(data.into())) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_conversion() { assert!(AuthMechanism::try_from("plain").is_ok()); assert!(AuthMechanism::try_from("login").is_ok()); assert!(AuthMechanism::try_from("oauthbearer").is_ok()); assert!(AuthMechanism::try_from("xoauth2").is_ok()); assert!(AuthMechanism::try_from("xxxplain").is_ok()); assert!(AuthMechanism::try_from("xxxlogin").is_ok()); assert!(AuthMechanism::try_from("xxxxoauth2").is_ok()); } } duesee-imap-codec-0d00966/imap-types/src/body.rs000066400000000000000000000276231507724125200214360ustar00rootroot00000000000000//! Body(structure)-related types. #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ core::{IString, NString, Vec1}, envelope::Envelope, }; /// Inner part of [`BodyStructure`]. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Body<'a> { /// Basic fields pub basic: BasicFields<'a>, /// Type-specific fields pub specific: SpecificFields<'a>, } /// Basic fields of a non-multipart body part. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct BasicFields<'a> { /// List of attribute/value pairs ([MIME-IMB].) pub parameter_list: Vec<(IString<'a>, IString<'a>)>, /// Content id ([MIME-IMB].) pub id: NString<'a>, /// Content description ([MIME-IMB].) pub description: NString<'a>, /// Content transfer encoding ([MIME-IMB].) pub content_transfer_encoding: IString<'a>, /// Size of the body in octets. /// /// Note that this size is the size in its transfer encoding /// and not the resulting size after any decoding. pub size: u32, } /// Specific fields of a non-multipart body part. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum SpecificFields<'a> { /// # Example (not in RFC) /// /// Single application/{voodoo, unknown, whatever, meh} is represented as "basic" /// /// ```text /// ( /// "application" "voodoo" NIL NIL NIL "7bit" 20 /// ^^^ ^^^ ^^^ ^^^^^^ ^^ /// | | | | | size /// | | | | content transfer encoding /// | | | description /// | | id /// | parameter list /// /// NIL NIL NIL NIL /// ^^^ ^^^ ^^^ ^^^ /// | | | | location /// | | | language /// | | disposition /// | md5 /// ) /// ``` Basic { /// A string giving the content media type name as defined in [MIME-IMB]. r#type: IString<'a>, /// A string giving the content subtype name as defined in [MIME-IMB]. subtype: IString<'a>, }, /// # Example (not in RFC) /// /// Single message/rfc822 is represented as "message" /// /// ```text /// ( /// "message" "rfc822" NIL NIL NIL "7bit" 123 /// ^^^ ^^^ ^^^ ^^^^^^ ^^^ /// | | | | | size /// | | | | content transfer encoding /// | | | description /// | | id /// | parameter list /// /// # envelope /// ( /// NIL "message.inner.subject.ljcwooqy" ((NIL NIL "extern" "company.com")) ((NIL NIL "extern" "company.com")) ((NIL NIL "extern" "company.com")) ((NIL NIL "admin" "seurity.com")) NIL NIL NIL NIL /// ) /// /// # body structure /// ( /// "text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 31 /// 2 /// NIL NIL NIL NIL /// ) /// /// 6 /// ^ /// | number of lines /// /// NIL NIL NIL NIL /// ^^^ ^^^ ^^^ ^^^ /// | | | | location /// | | | language /// | | disposition /// | md5 /// ) /// ``` /// /// A body type of type MESSAGE and subtype RFC822 contains, immediately after the basic fields, Message { /// the envelope structure, envelope: Box>, /// body structure, body_structure: Box>, /// and size in text lines of the encapsulated message. number_of_lines: u32, }, /// # Example (not in RFC) /// /// Single text/plain is represented as "text" /// /// ```text /// ( /// "text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 25 /// ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^ ^^^^^^ ^^ /// | | | | | size /// | | | | content transfer encoding /// | | | description /// | | id /// | parameter list /// /// 1 /// ^ /// | number of lines /// /// NIL NIL NIL NIL /// ^^^ ^^^ ^^^ ^^^ /// | | | | location /// | | | language /// | | disposition /// | md5 /// ) /// ``` Text { /// Subtype. subtype: IString<'a>, /// Size of the body in text lines. number_of_lines: u32, }, } /// The BODY(STRUCTURE). #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum BodyStructure<'a> { /// For example, a simple text message of 48 lines and 2279 octets /// can have a body structure of: /// /// ```text /// ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 2279 48) /// ``` Single { /// Inner body. body: Body<'a>, /// Extension data /// /// Extension data is never returned with the BODY fetch, /// but can be returned with a BODYSTRUCTURE fetch. /// Extension data, if present, MUST be in the defined order. /// /// Any following extension data are not yet defined in this /// version of the protocol, and would be as described above under /// multipart extension data. extension_data: Option>, }, /// Multiple parts are indicated by parenthesis nesting. Instead /// of a body type as the first element of the parenthesized list, /// there is a sequence of one or more nested body structures. The /// second (last?!) element of the parenthesized list is the multipart /// subtype (mixed, digest, parallel, alternative, etc.). /// /// For example, a two part message consisting of a text and a /// BASE64-encoded text attachment can have a body structure of: /// /// ```text /// ( /// ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23) /// ("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") "<960723163407.20117h@cac.washington.edu>" "Compiler diff" "BASE64" 4554 73) /// "MIXED" /// ) /// ``` /// /// Extension data follows the multipart subtype. Extension data /// is never returned with the BODY fetch, but can be returned with /// a BODYSTRUCTURE fetch. Extension data, if present, MUST be in /// the defined order. /// /// See [ExtensionMultiPartData](struct.ExtensionMultiPartData.html). /// /// Any following extension data are not yet defined in this /// version of the protocol. Such extension data can consist of /// zero or more NILs, strings, numbers, or potentially nested /// parenthesized lists of such data. Client implementations that /// do a BODYSTRUCTURE fetch MUST be prepared to accept such /// extension data. Server implementations MUST NOT send such /// extension data until it has been defined by a revision of this /// protocol. /// /// # Example (not in RFC) /// /// Multipart/mixed is represented as follows... /// /// ```text /// ( /// ("text" "html" ("charset" "us-ascii") NIL NIL "7bit" 28 0 NIL NIL NIL NIL) /// ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 11 0 NIL NIL NIL NIL) /// "mixed" ("boundary" "xxx") NIL NIL NIL /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// | /// | extension data /// ) /// ``` Multi { /// Inner bodies. bodies: Vec1>, /// Subtype. subtype: IString<'a>, /// Extension data. extension_data: Option>, }, } /// The extension data of a non-multipart body part. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct SinglePartExtensionData<'a> { /// A string giving the body MD5 value as defined in \[MD5\]. pub md5: NString<'a>, /// (Optional) additional data. pub tail: Option>, } /// The extension data of a multipart body part. /// /// # Trace (not in RFC) /// /// ```text /// ( /// ("text" "html" ("charset" "us-ascii") NIL NIL "7bit" 28 0 NIL NIL NIL NIL) /// ("text" "plain" ("charset" "us-ascii") NIL NIL "7bit" 11 0 NIL NIL NIL NIL) /// "mixed" ("boundary" "xxx") NIL NIL NIL /// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// | /// | extension multipart data /// ) /// ``` #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct MultiPartExtensionData<'a> { /// A parenthesized list of attribute/value pairs [e.g., ("foo" /// "bar" "baz" "rag") where "bar" is the value of "foo", and /// "rag" is the value of "baz"] as defined in [MIME-IMB]. pub parameter_list: Vec<(IString<'a>, IString<'a>)>, /// (Optional) additional data. pub tail: Option>, } /// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`]. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Disposition<'a> { /// A parenthesized list, consisting of a disposition type /// string, followed by a parenthesized list of disposition /// attribute/value pairs as defined in \[DISPOSITION\]. pub disposition: Option<(IString<'a>, Vec<(IString<'a>, IString<'a>)>)>, /// (Optional) additional data. pub tail: Option>, } /// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`]. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Language<'a> { /// A string or parenthesized list giving the body language /// value as defined in [LANGUAGE-TAGS]. pub language: Vec>, /// (Optional) additional data. pub tail: Option>, } /// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`]. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Location<'a> { /// A string list giving the body content URI as defined in \[LOCATION\]. pub location: NString<'a>, /// Extension data. pub extensions: Vec>, } /// Helper to enforce correct usage of [`SinglePartExtensionData`] and [`MultiPartExtensionData`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum BodyExtension<'a> { /// NString. NString(NString<'a>), /// Number. Number(u32), /// List. List(Vec1>), } duesee-imap-codec-0d00966/imap-types/src/command.rs000066400000000000000000002702321507724125200221130ustar00rootroot00000000000000//! Client Commands. //! //! See . use std::borrow::Cow; #[cfg(feature = "ext_condstore_qresync")] use std::num::NonZeroU32; #[cfg(feature = "ext_condstore_qresync")] use std::num::NonZeroU64; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "ext_id")] use crate::core::{IString, NString}; #[cfg(feature = "ext_metadata")] use crate::extensions::metadata::{Entry, EntryValue, GetMetadataOption}; use crate::{ auth::AuthMechanism, command::error::{AppendError, CopyError, ListError, LoginError, RenameError}, core::{AString, Charset, Literal, Tag, Vec1}, datetime::DateTime, extensions::{ binary::LiteralOrLiteral8, compress::CompressionAlgorithm, enable::CapabilityEnable, quota::QuotaSet, sort::SortCriterion, thread::ThreadingAlgorithm, }, fetch::MacroOrMessageDataItemNames, flag::{Flag, StoreResponse, StoreType}, mailbox::{ListMailbox, Mailbox}, search::SearchKey, secret::Secret, sequence::SequenceSet, status::StatusDataItemName, }; /// Command. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Command<'a> { /// Tag. pub tag: Tag<'a>, /// Body, e.g., CAPABILITY, LOGIN, SELECT, etc. pub body: CommandBody<'a>, } impl<'a> Command<'a> { /// Create a new command. pub fn new(tag: T, body: CommandBody<'a>) -> Result where T: TryInto>, { Ok(Self { tag: tag.try_into()?, body, }) } /// Get the command name. pub fn name(&self) -> &'static str { self.body.name() } } /// Command body. /// /// This enum is used to encode all the different commands. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum CommandBody<'a> { // ----- Any State (see https://tools.ietf.org/html/rfc3501#section-6.1) ----- /// ### 6.1.1. CAPABILITY Command /// /// * Arguments: none /// * Responses: REQUIRED untagged response: CAPABILITY /// * Result: /// * OK - capability completed /// * BAD - command unknown or arguments invalid /// /// The CAPABILITY command requests a listing of capabilities that the /// server supports. The server MUST send a single untagged /// CAPABILITY response with "IMAP4rev1" as one of the listed /// capabilities before the (tagged) OK response. /// /// A capability name which begins with "AUTH=" indicates that the /// server supports that particular authentication mechanism. All /// such names are, by definition, part of this specification. For /// example, the authorization capability for an experimental /// "blurdybloop" authenticator would be "AUTH=XBLURDYBLOOP" and not /// "XAUTH=BLURDYBLOOP" or "XAUTH=XBLURDYBLOOP". /// /// Other capability names refer to extensions, revisions, or /// amendments to this specification. See the documentation of the /// CAPABILITY response for additional information. No capabilities, /// beyond the base IMAP4rev1 set defined in this specification, are /// enabled without explicit client action to invoke the capability. /// /// Client and server implementations MUST implement the STARTTLS, /// LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS]) /// capabilities. See the Security Considerations section for /// important information. /// /// See the section entitled "Client Commands - /// Experimental/Expansion" for information about the form of site or /// implementation-specific capabilities. Capability, /// ### 6.1.2. NOOP Command /// /// * Arguments: none /// * Responses: no specific responses for this command (but see below) /// * Result: /// * OK - noop completed /// * BAD - command unknown or arguments invalid /// /// The NOOP command always succeeds. It does nothing. /// /// Since any command can return a status update as untagged data, the /// NOOP command can be used as a periodic poll for new messages or /// message status updates during a period of inactivity (this is the /// preferred method to do this). The NOOP command can also be used /// to reset any inactivity autologout timer on the server. Noop, /// ### 6.1.3. LOGOUT Command /// /// * Arguments: none /// * Responses: REQUIRED untagged response: BYE /// * Result: /// * OK - logout completed /// * BAD - command unknown or arguments invalid /// /// The LOGOUT command informs the server that the client is done with /// the connection. The server MUST send a BYE untagged response /// before the (tagged) OK response, and then close the network /// connection. Logout, // ----- Not Authenticated State (https://tools.ietf.org/html/rfc3501#section-6.2) ----- /// ### 6.2.1. STARTTLS Command /// /// * Arguments: none /// * Responses: no specific response for this command /// * Result: /// * OK - starttls completed, begin TLS negotiation /// * BAD - command unknown or arguments invalid /// /// A \[TLS\] negotiation begins immediately after the CRLF at the end /// of the tagged OK response from the server. Once a client issues a /// STARTTLS command, it MUST NOT issue further commands until a /// server response is seen and the \[TLS\] negotiation is complete. /// /// The server remains in the non-authenticated state, even if client /// credentials are supplied during the \[TLS\] negotiation. This does /// not preclude an authentication mechanism such as EXTERNAL (defined /// in \[SASL\]) from using client identity determined by the \[TLS\] /// negotiation. /// /// Once \[TLS\] has been started, the client MUST discard cached /// information about server capabilities and SHOULD re-issue the /// CAPABILITY command. This is necessary to protect against man-in- /// the-middle attacks which alter the capabilities list prior to /// STARTTLS. The server MAY advertise different capabilities after /// STARTTLS. /// ///
/// This must only be used when the server advertised support for it sending the STARTTLS capability. /// /// Try to avoid STARTTLS using implicit TLS on port 993. ///
#[cfg(feature = "starttls")] #[cfg_attr(docsrs, doc(cfg(feature = "starttls")))] StartTLS, /// ### 6.2.2. AUTHENTICATE Command /// /// * Arguments: authentication mechanism name /// * Responses: continuation data can be requested /// * Result: /// * OK - authenticate completed, now in authenticated state /// * NO - authenticate failure: unsupported authentication mechanism, credentials rejected /// * BAD - command unknown or arguments invalid, authentication exchange cancelled /// /// The AUTHENTICATE command indicates a \[SASL\] authentication /// mechanism to the server. If the server supports the requested /// authentication mechanism, it performs an authentication protocol /// exchange to authenticate and identify the client. It MAY also /// negotiate an OPTIONAL security layer for subsequent protocol /// interactions. If the requested authentication mechanism is not /// supported, the server SHOULD reject the AUTHENTICATE command by /// sending a tagged NO response. /// /// The AUTHENTICATE command does not support the optional "initial /// response" feature of \[SASL\]. Section 5.1 of \[SASL\] specifies how /// to handle an authentication mechanism which uses an initial /// response. /// /// The service name specified by this protocol's profile of \[SASL\] is /// "imap". /// /// The authentication protocol exchange consists of a series of /// server challenges and client responses that are specific to the /// authentication mechanism. A server challenge consists of a /// command continuation request response with the "+" token followed /// by a BASE64 encoded string. The client response consists of a /// single line consisting of a BASE64 encoded string. If the client /// wishes to cancel an authentication exchange, it issues a line /// consisting of a single "*". If the server receives such a /// response, it MUST reject the AUTHENTICATE command by sending a /// tagged BAD response. /// /// If a security layer is negotiated through the \[SASL\] /// authentication exchange, it takes effect immediately following the /// CRLF that concludes the authentication exchange for the client, /// and the CRLF of the tagged OK response for the server. /// /// While client and server implementations MUST implement the /// AUTHENTICATE command itself, it is not required to implement any /// authentication mechanisms other than the PLAIN mechanism described /// in [IMAP-TLS]. Also, an authentication mechanism is not required /// to support any security layers. /// /// Note: a server implementation MUST implement a /// configuration in which it does NOT permit any plaintext /// password mechanisms, unless either the STARTTLS command /// has been negotiated or some other mechanism that /// protects the session from password snooping has been /// provided. Server sites SHOULD NOT use any configuration /// which permits a plaintext password mechanism without /// such a protection mechanism against password snooping. /// Client and server implementations SHOULD implement /// additional \[SASL\] mechanisms that do not use plaintext /// passwords, such the GSSAPI mechanism described in \[SASL\] /// and/or the [DIGEST-MD5] mechanism. /// /// Servers and clients can support multiple authentication /// mechanisms. The server SHOULD list its supported authentication /// mechanisms in the response to the CAPABILITY command so that the /// client knows which authentication mechanisms to use. /// /// A server MAY include a CAPABILITY response code in the tagged OK /// response of a successful AUTHENTICATE command in order to send /// capabilities automatically. It is unnecessary for a client to /// send a separate CAPABILITY command if it recognizes these /// automatic capabilities. This should only be done if a security /// layer was not negotiated by the AUTHENTICATE command, because the /// tagged OK response as part of an AUTHENTICATE command is not /// protected by encryption/integrity checking. \[SASL\] requires the /// client to re-issue a CAPABILITY command in this case. /// /// If an AUTHENTICATE command fails with a NO response, the client /// MAY try another authentication mechanism by issuing another /// AUTHENTICATE command. It MAY also attempt to authenticate by /// using the LOGIN command (see section 6.2.3 for more detail). In /// other words, the client MAY request authentication types in /// decreasing order of preference, with the LOGIN command as a last /// resort. /// /// The authorization identity passed from the client to the server /// during the authentication exchange is interpreted by the server as /// the user name whose privileges the client is requesting. Authenticate { /// Authentication mechanism. mechanism: AuthMechanism<'a>, /// Initial response (if any). /// /// This type holds the raw binary data, i.e., a `Vec`, *not* the BASE64 string. /// ///
/// This extension must only be used when the server advertised support for it sending the SASL-IR capability. ///
initial_response: Option>>, }, /// ### 6.2.3. LOGIN Command /// /// * Arguments: /// * user name /// * password /// * Responses: no specific responses for this command /// * Result: /// * OK - login completed, now in authenticated state /// * NO - login failure: user name or password rejected /// * BAD - command unknown or arguments invalid /// /// The LOGIN command identifies the client to the server and carries /// the plaintext password authenticating this user. /// /// A server MAY include a CAPABILITY response code in the tagged OK /// response to a successful LOGIN command in order to send /// capabilities automatically. It is unnecessary for a client to /// send a separate CAPABILITY command if it recognizes these /// automatic capabilities. /// /// Note: Use of the LOGIN command over an insecure network /// (such as the Internet) is a security risk, because anyone /// monitoring network traffic can obtain plaintext passwords. /// The LOGIN command SHOULD NOT be used except as a last /// resort, and it is recommended that client implementations /// have a means to disable any automatic use of the LOGIN /// command. /// /// Unless either the STARTTLS command has been negotiated or /// some other mechanism that protects the session from /// password snooping has been provided, a server /// implementation MUST implement a configuration in which it /// advertises the LOGINDISABLED capability and does NOT permit /// the LOGIN command. Server sites SHOULD NOT use any /// configuration which permits the LOGIN command without such /// a protection mechanism against password snooping. A client /// implementation MUST NOT send a LOGIN command if the /// LOGINDISABLED capability is advertised. Login { /// Username. username: AString<'a>, /// Password. password: Secret>, }, // ----- Authenticated State (https://tools.ietf.org/html/rfc3501#section-6.3) ----- /// ### 6.3.1. SELECT Command /// /// * Arguments: mailbox name /// * Responses: /// * REQUIRED untagged responses: FLAGS, EXISTS, RECENT /// * REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, UIDNEXT, UIDVALIDITY /// * Result: /// * OK - select completed, now in selected state /// * NO - select failure, now in authenticated state: no such mailbox, can't access mailbox /// * BAD - command unknown or arguments invalid /// /// The SELECT command selects a mailbox so that messages in the /// mailbox can be accessed. Before returning an OK to the client, /// the server MUST send the following untagged data to the client. /// Note that earlier versions of this protocol only required the /// FLAGS, EXISTS, and RECENT untagged data; consequently, client /// implementations SHOULD implement default behavior for missing data /// as discussed with the individual item. /// /// FLAGS Defined flags in the mailbox. See the description /// of the FLAGS response for more detail. /// /// \ EXISTS The number of messages in the mailbox. See the /// description of the EXISTS response for more detail. /// /// \ RECENT The number of messages with the \Recent flag set. /// See the description of the RECENT response for more /// detail. /// /// OK [UNSEEN \] /// The message sequence number of the first unseen /// message in the mailbox. If this is missing, the /// client can not make any assumptions about the first /// unseen message in the mailbox, and needs to issue a /// SEARCH command if it wants to find it. /// /// OK [PERMANENTFLAGS (\)] /// A list of message flags that the client can change /// permanently. If this is missing, the client should /// assume that all flags can be changed permanently. /// /// OK [UIDNEXT \] /// The next unique identifier value. Refer to section /// 2.3.1.1 for more information. If this is missing, /// the client can not make any assumptions about the /// next unique identifier value. /// /// OK [UIDVALIDITY \] /// The unique identifier validity value. Refer to /// section 2.3.1.1 for more information. If this is /// missing, the server does not support unique /// identifiers. /// /// Only one mailbox can be selected at a time in a connection; /// simultaneous access to multiple mailboxes requires multiple /// connections. The SELECT command automatically deselects any /// currently selected mailbox before attempting the new selection. /// Consequently, if a mailbox is selected and a SELECT command that /// fails is attempted, no mailbox is selected. /// /// If the client is permitted to modify the mailbox, the server /// SHOULD prefix the text of the tagged OK response with the /// "[READ-WRITE]" response code. /// /// If the client is not permitted to modify the mailbox but is /// permitted read access, the mailbox is selected as read-only, and /// the server MUST prefix the text of the tagged OK response to /// SELECT with the "[READ-ONLY]" response code. Read-only access /// through SELECT differs from the EXAMINE command in that certain /// read-only mailboxes MAY permit the change of permanent state on a /// per-user (as opposed to global) basis. Netnews messages marked in /// a server-based .newsrc file are an example of such per-user /// permanent state that can be modified with read-only mailboxes. Select { /// Mailbox. mailbox: Mailbox<'a>, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec, }, /// Unselect a mailbox. /// /// This should bring the client back to the AUTHENTICATED state. /// ///
/// This extension must only be used when the server advertised support for it sending the UNSELECT capability. ///
Unselect, /// 6.3.2. EXAMINE Command /// /// Arguments: mailbox name /// Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT /// REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, /// UIDNEXT, UIDVALIDITY /// Result: OK - examine completed, now in selected state /// NO - examine failure, now in authenticated state: no /// such mailbox, can't access mailbox /// BAD - command unknown or arguments invalid /// /// The EXAMINE command is identical to SELECT and returns the same /// output; however, the selected mailbox is identified as read-only. /// No changes to the permanent state of the mailbox, including /// per-user state, are permitted; in particular, EXAMINE MUST NOT /// cause messages to lose the \Recent flag. /// /// The text of the tagged OK response to the EXAMINE command MUST /// begin with the "[READ-ONLY]" response code. Examine { /// Mailbox. mailbox: Mailbox<'a>, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec, }, /// ### 6.3.3. CREATE Command /// /// * Arguments: mailbox name /// * Responses: no specific responses for this command /// * Result: /// * OK - create completed /// * NO - create failure: can't create mailbox with that name /// * BAD - command unknown or arguments invalid /// /// The CREATE command creates a mailbox with the given name. An OK /// response is returned only if a new mailbox with that name has been /// created. It is an error to attempt to create INBOX or a mailbox /// with a name that refers to an extant mailbox. Any error in /// creation will return a tagged NO response. /// /// If the mailbox name is suffixed with the server's hierarchy /// separator character (as returned from the server by a LIST /// command), this is a declaration that the client intends to create /// mailbox names under this name in the hierarchy. Server /// implementations that do not require this declaration MUST ignore /// the declaration. In any case, the name created is without the /// trailing hierarchy delimiter. /// /// If the server's hierarchy separator character appears elsewhere in /// the name, the server SHOULD create any superior hierarchical names /// that are needed for the CREATE command to be successfully /// completed. In other words, an attempt to create "foo/bar/zap" on /// a server in which "/" is the hierarchy separator character SHOULD /// create foo/ and foo/bar/ if they do not already exist. /// /// If a new mailbox is created with the same name as a mailbox which /// was deleted, its unique identifiers MUST be greater than any /// unique identifiers used in the previous incarnation of the mailbox /// UNLESS the new incarnation has a different unique identifier /// validity value. See the description of the UID command for more /// detail. /// /// Note: The interpretation of this example depends on whether /// "/" was returned as the hierarchy separator from LIST. If /// "/" is the hierarchy separator, a new level of hierarchy /// named "owatagusiam" with a member called "blurdybloop" is /// created. Otherwise, two mailboxes at the same hierarchy /// level are created. Create { /// Mailbox. mailbox: Mailbox<'a>, }, /// 6.3.4. DELETE Command /// /// Arguments: mailbox name /// Responses: no specific responses for this command /// Result: OK - delete completed /// NO - delete failure: can't delete mailbox with that name /// BAD - command unknown or arguments invalid /// /// The DELETE command permanently removes the mailbox with the given /// name. A tagged OK response is returned only if the mailbox has /// been deleted. It is an error to attempt to delete INBOX or a /// mailbox name that does not exist. /// /// The DELETE command MUST NOT remove inferior hierarchical names. /// For example, if a mailbox "foo" has an inferior "foo.bar" /// (assuming "." is the hierarchy delimiter character), removing /// "foo" MUST NOT remove "foo.bar". It is an error to attempt to /// delete a name that has inferior hierarchical names and also has /// the \Noselect mailbox name attribute (see the description of the /// LIST response for more details). /// /// It is permitted to delete a name that has inferior hierarchical /// names and does not have the \Noselect mailbox name attribute. In /// this case, all messages in that mailbox are removed, and the name /// will acquire the \Noselect mailbox name attribute. /// /// The value of the highest-used unique identifier of the deleted /// mailbox MUST be preserved so that a new mailbox created with the /// same name will not reuse the identifiers of the former /// incarnation, UNLESS the new incarnation has a different unique /// identifier validity value. See the description of the UID command /// for more detail. Delete { /// Mailbox. mailbox: Mailbox<'a>, }, /// 6.3.5. RENAME Command /// /// Arguments: existing mailbox name /// new mailbox name /// Responses: no specific responses for this command /// Result: OK - rename completed /// NO - rename failure: can't rename mailbox with that name, /// can't rename to mailbox with that name /// BAD - command unknown or arguments invalid /// /// The RENAME command changes the name of a mailbox. A tagged OK /// response is returned only if the mailbox has been renamed. It is /// an error to attempt to rename from a mailbox name that does not /// exist or to a mailbox name that already exists. Any error in /// renaming will return a tagged NO response. /// /// If the name has inferior hierarchical names, then the inferior /// hierarchical names MUST also be renamed. For example, a rename of /// "foo" to "zap" will rename "foo/bar" (assuming "/" is the /// hierarchy delimiter character) to "zap/bar". /// /// If the server's hierarchy separator character appears in the name, /// the server SHOULD create any superior hierarchical names that are /// needed for the RENAME command to complete successfully. In other /// words, an attempt to rename "foo/bar/zap" to baz/rag/zowie on a /// server in which "/" is the hierarchy separator character SHOULD /// create baz/ and baz/rag/ if they do not already exist. /// /// The value of the highest-used unique identifier of the old mailbox /// name MUST be preserved so that a new mailbox created with the same /// name will not reuse the identifiers of the former incarnation, /// UNLESS the new incarnation has a different unique identifier /// validity value. See the description of the UID command for more /// detail. /// /// Renaming INBOX is permitted, and has special behavior. It moves /// all messages in INBOX to a new mailbox with the given name, /// leaving INBOX empty. If the server implementation supports /// inferior hierarchical names of INBOX, these are unaffected by a /// rename of INBOX. Rename { /// Current name. from: Mailbox<'a>, /// New name. to: Mailbox<'a>, }, /// ### 6.3.6. SUBSCRIBE Command /// /// * Arguments: mailbox /// * Responses: no specific responses for this command /// * Result: /// * OK - subscribe completed /// * NO - subscribe failure: can't subscribe to that name /// * BAD - command unknown or arguments invalid /// /// The SUBSCRIBE command adds the specified mailbox name to the /// server's set of "active" or "subscribed" mailboxes as returned by /// the LSUB command. This command returns a tagged OK response only /// if the subscription is successful. /// /// A server MAY validate the mailbox argument to SUBSCRIBE to verify /// that it exists. However, it MUST NOT unilaterally remove an /// existing mailbox name from the subscription list even if a mailbox /// by that name no longer exists. /// /// Note: This requirement is because a server site can /// choose to routinely remove a mailbox with a well-known /// name (e.g., "system-alerts") after its contents expire, /// with the intention of recreating it when new contents /// are appropriate. Subscribe { /// Mailbox. mailbox: Mailbox<'a>, }, /// 6.3.7. UNSUBSCRIBE Command /// /// Arguments: mailbox name /// Responses: no specific responses for this command /// Result: OK - unsubscribe completed /// NO - unsubscribe failure: can't unsubscribe that name /// BAD - command unknown or arguments invalid /// /// The UNSUBSCRIBE command removes the specified mailbox name from /// the server's set of "active" or "subscribed" mailboxes as returned /// by the LSUB command. This command returns a tagged OK response /// only if the unsubscription is successful. Unsubscribe { /// Mailbox. mailbox: Mailbox<'a>, }, /// ### 6.3.8. LIST Command /// /// * Arguments: /// * reference name /// * mailbox name with possible wildcards /// * Responses: untagged responses: LIST /// * Result: /// * OK - list completed /// * NO - list failure: can't list that reference or name /// * BAD - command unknown or arguments invalid /// /// The LIST command returns a subset of names from the complete set /// of all names available to the client. Zero or more untagged LIST /// replies are returned, containing the name attributes, hierarchy /// delimiter, and name; see the description of the LIST reply for /// more detail. /// /// The LIST command SHOULD return its data quickly, without undue /// delay. For example, it SHOULD NOT go to excess trouble to /// calculate the \Marked or \Unmarked status or perform other /// processing; if each name requires 1 second of processing, then a /// list of 1200 names would take 20 minutes! /// /// An empty ("" string) reference name argument indicates that the /// mailbox name is interpreted as by SELECT. The returned mailbox /// names MUST match the supplied mailbox name pattern. A non-empty /// reference name argument is the name of a mailbox or a level of /// mailbox hierarchy, and indicates the context in which the mailbox /// name is interpreted. /// /// An empty ("" string) mailbox name argument is a special request to /// return the hierarchy delimiter and the root name of the name given /// in the reference. The value returned as the root MAY be the empty /// string if the reference is non-rooted or is an empty string. In /// all cases, a hierarchy delimiter (or NIL if there is no hierarchy) /// is returned. This permits a client to get the hierarchy delimiter /// (or find out that the mailbox names are flat) even when no /// mailboxes by that name currently exist. /// /// The reference and mailbox name arguments are interpreted into a /// canonical form that represents an unambiguous left-to-right /// hierarchy. The returned mailbox names will be in the interpreted /// form. /// /// Note: The interpretation of the reference argument is /// implementation-defined. It depends upon whether the /// server implementation has a concept of the "current /// working directory" and leading "break out characters", /// which override the current working directory. /// /// For example, on a server which exports a UNIX or NT /// filesystem, the reference argument contains the current /// working directory, and the mailbox name argument would /// contain the name as interpreted in the current working /// directory. /// /// If a server implementation has no concept of break out /// characters, the canonical form is normally the reference /// name appended with the mailbox name. Note that if the /// server implements the namespace convention (section /// 5.1.2), "#" is a break out character and must be treated /// as such. /// /// If the reference argument is not a level of mailbox /// hierarchy (that is, it is a \NoInferiors name), and/or /// the reference argument does not end with the hierarchy /// delimiter, it is implementation-dependent how this is /// interpreted. For example, a reference of "foo/bar" and /// mailbox name of "rag/baz" could be interpreted as /// "foo/bar/rag/baz", "foo/barrag/baz", or "foo/rag/baz". /// A client SHOULD NOT use such a reference argument except /// at the explicit request of the user. A hierarchical /// browser MUST NOT make any assumptions about server /// interpretation of the reference unless the reference is /// a level of mailbox hierarchy AND ends with the hierarchy /// delimiter. /// /// Any part of the reference argument that is included in the /// interpreted form SHOULD prefix the interpreted form. It SHOULD /// also be in the same form as the reference name argument. This /// rule permits the client to determine if the returned mailbox name /// is in the context of the reference argument, or if something about /// the mailbox argument overrode the reference argument. Without /// this rule, the client would have to have knowledge of the server's /// naming semantics including what characters are "breakouts" that /// override a naming context. /// /// For example, here are some examples of how references /// and mailbox names might be interpreted on a UNIX-based /// server: /// /// ```text /// Reference Mailbox Name Interpretation /// ------------ ------------ -------------- /// ~smith/Mail/ foo.* ~smith/Mail/foo.* /// archive/ % archive/% /// #news. comp.mail.* #news.comp.mail.* /// ~smith/Mail/ /usr/doc/foo /usr/doc/foo /// archive/ ~fred/Mail/* ~fred/Mail/* /// ``` /// /// The first three examples demonstrate interpretations in /// the context of the reference argument. Note that /// "~smith/Mail" SHOULD NOT be transformed into something /// like "/u2/users/smith/Mail", or it would be impossible /// for the client to determine that the interpretation was /// in the context of the reference. /// /// The character "*" is a wildcard, and matches zero or more /// characters at this position. The character "%" is similar to "*", /// but it does not match a hierarchy delimiter. If the "%" wildcard /// is the last character of a mailbox name argument, matching levels /// of hierarchy are also returned. If these levels of hierarchy are /// not also selectable mailboxes, they are returned with the /// \Noselect mailbox name attribute (see the description of the LIST /// response for more details). /// /// Server implementations are permitted to "hide" otherwise /// accessible mailboxes from the wildcard characters, by preventing /// certain characters or names from matching a wildcard in certain /// situations. For example, a UNIX-based server might restrict the /// interpretation of "*" so that an initial "/" character does not /// match. /// /// The special name INBOX is included in the output from LIST, if /// INBOX is supported by this server for this user and if the /// uppercase string "INBOX" matches the interpreted reference and /// mailbox name arguments with wildcards as described above. The /// criteria for omitting INBOX is whether SELECT INBOX will return /// failure; it is not relevant whether the user's real INBOX resides /// on this or some other server. List { /// Reference. reference: Mailbox<'a>, /// Mailbox (wildcard). mailbox_wildcard: ListMailbox<'a>, }, /// ### 6.3.9. LSUB Command /// /// * Arguments: /// * reference name /// * mailbox name with possible wildcards /// * Responses: untagged responses: LSUB /// * Result: /// * OK - lsub completed /// * NO - lsub failure: can't list that reference or name /// * BAD - command unknown or arguments invalid /// /// The LSUB command returns a subset of names from the set of names /// that the user has declared as being "active" or "subscribed". /// Zero or more untagged LSUB replies are returned. The arguments to /// LSUB are in the same form as those for LIST. /// /// The returned untagged LSUB response MAY contain different mailbox /// flags from a LIST untagged response. If this should happen, the /// flags in the untagged LIST are considered more authoritative. /// /// A special situation occurs when using LSUB with the % wildcard. /// Consider what happens if "foo/bar" (with a hierarchy delimiter of /// "/") is subscribed but "foo" is not. A "%" wildcard to LSUB must /// return foo, not foo/bar, in the LSUB response, and it MUST be /// flagged with the \Noselect attribute. /// /// The server MUST NOT unilaterally remove an existing mailbox name /// from the subscription list even if a mailbox by that name no /// longer exists. Lsub { /// Reference. reference: Mailbox<'a>, /// Mailbox (wildcard). mailbox_wildcard: ListMailbox<'a>, }, /// ### 6.3.10. STATUS Command /// /// * Arguments: /// * mailbox name /// * status data item names /// * Responses: untagged responses: STATUS /// * Result: /// * OK - status completed /// * NO - status failure: no status for that name /// * BAD - command unknown or arguments invalid /// /// The STATUS command requests the status of the indicated mailbox. /// It does not change the currently selected mailbox, nor does it /// affect the state of any messages in the queried mailbox (in /// particular, STATUS MUST NOT cause messages to lose the \Recent /// flag). /// /// The STATUS command provides an alternative to opening a second /// IMAP4rev1 connection and doing an EXAMINE command on a mailbox to /// query that mailbox's status without deselecting the current /// mailbox in the first IMAP4rev1 connection. /// /// Unlike the LIST command, the STATUS command is not guaranteed to /// be fast in its response. Under certain circumstances, it can be /// quite slow. In some implementations, the server is obliged to /// open the mailbox read-only internally to obtain certain status /// information. Also unlike the LIST command, the STATUS command /// does not accept wildcards. /// /// Note: The STATUS command is intended to access the /// status of mailboxes other than the currently selected /// mailbox. Because the STATUS command can cause the /// mailbox to be opened internally, and because this /// information is available by other means on the selected /// mailbox, the STATUS command SHOULD NOT be used on the /// currently selected mailbox. /// /// The STATUS command MUST NOT be used as a "check for new /// messages in the selected mailbox" operation (refer to /// sections 7, 7.3.1, and 7.3.2 for more information about /// the proper method for new message checking). /// /// Because the STATUS command is not guaranteed to be fast /// in its results, clients SHOULD NOT expect to be able to /// issue many consecutive STATUS commands and obtain /// reasonable performance. Status { /// Mailbox. mailbox: Mailbox<'a>, /// Status data items. item_names: Cow<'a, [StatusDataItemName]>, }, /// 6.3.11. APPEND Command /// /// Arguments: mailbox name /// OPTIONAL flag parenthesized list /// OPTIONAL date/time string /// message literal /// Responses: no specific responses for this command /// Result: OK - append completed /// NO - append error: can't append to that mailbox, error /// in flags or date/time or message text /// BAD - command unknown or arguments invalid /// /// The APPEND command appends the literal argument as a new message /// to the end of the specified destination mailbox. This argument /// SHOULD be in the format of an [RFC-2822] message. 8-bit /// characters are permitted in the message. A server implementation /// that is unable to preserve 8-bit data properly MUST be able to /// reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB] /// content transfer encoding. /// /// Note: There MAY be exceptions, e.g., draft messages, in /// which required [RFC-2822] header lines are omitted in /// the message literal argument to APPEND. The full /// implications of doing so MUST be understood and /// carefully weighed. /// /// If a flag parenthesized list is specified, the flags SHOULD be set /// in the resulting message; otherwise, the flag list of the /// resulting message is set to empty by default. In either case, the /// Recent flag is also set. /// /// If a date-time is specified, the internal date SHOULD be set in /// the resulting message; otherwise, the internal date of the /// resulting message is set to the current date and time by default. /// /// If the append is unsuccessful for any reason, the mailbox MUST be /// restored to its state before the APPEND attempt; no partial /// appending is permitted. /// /// If the destination mailbox does not exist, a server MUST return an /// error, and MUST NOT automatically create the mailbox. Unless it /// is certain that the destination mailbox can not be created, the /// server MUST send the response code "\[TRYCREATE\]" as the prefix of /// the text of the tagged NO response. This gives a hint to the /// client that it can attempt a CREATE command and retry the APPEND /// if the CREATE is successful. /// /// If the mailbox is currently selected, the normal new message /// actions SHOULD occur. Specifically, the server SHOULD notify the /// client immediately via an untagged EXISTS response. If the server /// does not do so, the client MAY issue a NOOP command (or failing /// that, a CHECK command) after one or more APPEND commands. /// /// Note: The APPEND command is not used for message delivery, /// because it does not provide a mechanism to transfer \[SMTP\] /// envelope information. Append { /// Mailbox. mailbox: Mailbox<'a>, /// Flags. flags: Vec>, /// Datetime. date: Option, /// Message to append. /// ///
/// Use [`LiteralOrLiteral8::Literal8`] only when the server advertised [`Capability::Binary`](crate::response::Capability::Binary). ///
message: LiteralOrLiteral8<'a>, }, // ----- Selected State (https://tools.ietf.org/html/rfc3501#section-6.4) ----- /// ### 6.4.1. CHECK Command /// /// * Arguments: none /// * Responses: no specific responses for this command /// * Result: /// * OK - check completed /// * BAD - command unknown or arguments invalid /// /// The CHECK command requests a checkpoint of the currently selected /// mailbox. A checkpoint refers to any implementation-dependent /// housekeeping associated with the mailbox (e.g., resolving the /// server's in-memory state of the mailbox with the state on its /// disk) that is not normally executed as part of each command. A /// checkpoint MAY take a non-instantaneous amount of real time to /// complete. If a server implementation has no such housekeeping /// considerations, CHECK is equivalent to NOOP. /// /// There is no guarantee that an EXISTS untagged response will happen /// as a result of CHECK. NOOP, not CHECK, SHOULD be used for new /// message polling. Check, /// ### 6.4.2. CLOSE Command /// /// * Arguments: none /// * Responses: no specific responses for this command /// * Result: /// * OK - close completed, now in authenticated state /// * BAD - command unknown or arguments invalid /// /// The CLOSE command permanently removes all messages that have the /// \Deleted flag set from the currently selected mailbox, and returns /// to the authenticated state from the selected state. No untagged /// EXPUNGE responses are sent. /// /// No messages are removed, and no error is given, if the mailbox is /// selected by an EXAMINE command or is otherwise selected read-only. /// /// Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT /// command MAY be issued without previously issuing a CLOSE command. /// The SELECT, EXAMINE, and LOGOUT commands implicitly close the /// currently selected mailbox without doing an expunge. However, /// when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT /// sequence is considerably faster than an EXPUNGE-LOGOUT or /// EXPUNGE-SELECT because no untagged EXPUNGE responses (which the /// client would probably ignore) are sent. Close, /// 6.4.3. EXPUNGE Command /// /// Arguments: none /// Responses: untagged responses: EXPUNGE /// Result: OK - expunge completed /// NO - expunge failure: can't expunge (e.g., permission denied) /// BAD - command unknown or arguments invalid /// /// The EXPUNGE command permanently removes all messages that have the /// \Deleted flag set from the currently selected mailbox. Before /// returning an OK to the client, an untagged EXPUNGE response is /// sent for each message that is removed. Expunge, /// 2.1. UID EXPUNGE Command (RFC 4315) /// /// Arguments: sequence set /// Data: untagged responses: EXPUNGE /// Result: OK - expunge completed /// NO - expunge failure (e.g., permission denied) /// BAD - command unknown or arguments invalid /// /// The UID EXPUNGE command permanently removes all messages that both /// have the \Deleted flag set and have a UID that is included in the /// specified sequence set from the currently selected mailbox. If a /// message either does not have the \Deleted flag set or has a UID /// that is not included in the specified sequence set, it is not /// affected. /// /// This command is particularly useful for disconnected use clients. /// By using UID EXPUNGE instead of EXPUNGE when resynchronizing with /// the server, the client can ensure that it does not inadvertantly /// remove any messages that have been marked as \Deleted by other /// clients between the time that the client was last connected and /// the time the client resynchronizes. /// /// If the server does not support the UIDPLUS capability, the client /// should fall back to using the STORE command to temporarily remove /// the \Deleted flag from messages it does not want to remove, then /// issuing the EXPUNGE command. Finally, the client should use the /// STORE command to restore the \Deleted flag on the messages in /// which it was temporarily removed. /// /// Alternatively, the client may fall back to using just the EXPUNGE /// command, risking the unintended removal of some messages. ExpungeUid { sequence_set: SequenceSet }, /// ### 6.4.4. SEARCH Command /// /// * Arguments: /// * OPTIONAL \[CHARSET\] specification /// * searching criteria (one or more) /// * Responses: REQUIRED untagged response: SEARCH /// * Result: /// * OK - search completed /// * NO - search error: can't search that \[CHARSET\] or criteria /// * BAD - command unknown or arguments invalid /// /// The SEARCH command searches the mailbox for messages that match /// the given searching criteria. Searching criteria consist of one /// or more search keys. The untagged SEARCH response from the server /// contains a listing of message sequence numbers corresponding to /// those messages that match the searching criteria. /// /// When multiple keys are specified, the result is the intersection /// (AND function) of all the messages that match those keys. For /// example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers /// to all deleted messages from Smith that were placed in the mailbox /// since February 1, 1994. A search key can also be a parenthesized /// list of one or more search keys (e.g., for use with the OR and NOT /// keys). /// /// Server implementations MAY exclude [MIME-IMB] body parts with /// terminal content media types other than TEXT and MESSAGE from /// consideration in SEARCH matching. /// /// The OPTIONAL \[CHARSET\] specification consists of the word /// "CHARSET" followed by a registered \[CHARSET\]. It indicates the /// \[CHARSET\] of the strings that appear in the search criteria. /// [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in /// [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing /// text in a \[CHARSET\] other than US-ASCII. US-ASCII MUST be /// supported; other \[CHARSET\]s MAY be supported. /// /// If the server does not support the specified \[CHARSET\], it MUST /// return a tagged NO response (not a BAD). This response SHOULD /// contain the BADCHARSET response code, which MAY list the /// \[CHARSET\]s supported by the server. /// /// In all search keys that use strings, a message matches the key if /// the string is a substring of the field. The matching is /// case-insensitive. /// /// See [SearchKey] enum. /// /// Note: Since this document is restricted to 7-bit ASCII /// text, it is not possible to show actual UTF-8 data. The /// "XXXXXX" is a placeholder for what would be 6 octets of /// 8-bit data in an actual transaction. Search { /// Charset. charset: Option>, /// Criteria. criteria: Vec1>, /// Use UID variant. uid: bool, }, /// SORT command. /// /// The SORT command is a variant of SEARCH with sorting semantics for the results. /// /// Data: /// * untagged responses: SORT /// /// Result: /// * OK - sort completed /// * NO - sort error: can't sort that charset or criteria /// * BAD - command unknown or arguments invalid /// ///
/// This extension must only be used when the server advertised support for it sending the SORT capability. ///
Sort { /// Sort criteria. sort_criteria: Vec1, /// Charset. charset: Charset<'a>, /// Search criteria. search_criteria: Vec1>, /// Use UID variant. uid: bool, }, /// THREAD command. /// /// The THREAD command is a variant of SEARCH with threading semantics for the results. /// /// Data: /// * untagged responses: THREAD /// /// Result: /// * OK - thread completed /// * NO - thread error: can't thread that charset or criteria /// * BAD - command unknown or arguments invalid /// ///
/// This extension must only be used when the server advertised support for it sending the THREAD capability. ///
Thread { /// Threading algorithm. algorithm: ThreadingAlgorithm<'a>, /// Charset. charset: Charset<'a>, /// Search criteria. search_criteria: Vec1>, /// Use UID variant. uid: bool, }, /// ### 6.4.5. FETCH Command /// /// * Arguments: /// * sequence set /// * message data item names or macro /// * Responses: untagged responses: FETCH /// * Result: /// * OK - fetch completed /// * NO - fetch error: can't fetch that data /// * BAD - command unknown or arguments invalid /// /// The FETCH command retrieves data associated with a message in the /// mailbox. The data items to be fetched can be either a single atom /// or a parenthesized list. /// /// Most data items, identified in the formal syntax under the /// msg-att-static rule, are static and MUST NOT change for any /// particular message. Other data items, identified in the formal /// syntax under the msg-att-dynamic rule, MAY change, either as a /// result of a STORE command or due to external events. /// /// For example, if a client receives an ENVELOPE for a /// message when it already knows the envelope, it can /// safely ignore the newly transmitted envelope. Fetch { /// Set of messages. sequence_set: SequenceSet, /// Message data items (or a macro). macro_or_item_names: MacroOrMessageDataItemNames<'a>, /// Use UID variant. uid: bool, #[cfg(feature = "ext_condstore_qresync")] modifiers: Vec, }, /// ### 6.4.6. STORE Command /// /// * Arguments: /// * sequence set /// * message data item name /// * value for message data item /// * Responses: untagged responses: FETCH /// * Result: /// * OK - store completed /// * NO - store error: can't store that data /// * BAD - command unknown or arguments invalid /// /// The STORE command alters data associated with a message in the /// mailbox. Normally, STORE will return the updated value of the /// data with an untagged FETCH response. A suffix of ".SILENT" in /// the data item name prevents the untagged FETCH, and the server /// SHOULD assume that the client has determined the updated value /// itself or does not care about the updated value. /// /// Note: Regardless of whether or not the ".SILENT" suffix /// was used, the server SHOULD send an untagged FETCH /// response if a change to a message's flags from an /// external source is observed. The intent is that the /// status of the flags is determinate without a race /// condition. /// /// The currently defined data items that can be stored are: /// /// FLAGS \ /// Replace the flags for the message (other than \Recent) with the /// argument. The new value of the flags is returned as if a FETCH /// of those flags was done. /// /// FLAGS.SILENT \ /// Equivalent to FLAGS, but without returning a new value. /// /// +FLAGS \ /// Add the argument to the flags for the message. The new value /// of the flags is returned as if a FETCH of those flags was done. /// /// +FLAGS.SILENT \ /// Equivalent to +FLAGS, but without returning a new value. /// /// -FLAGS \ /// Remove the argument from the flags for the message. The new /// value of the flags is returned as if a FETCH of those flags was /// done. /// /// -FLAGS.SILENT \ /// Equivalent to -FLAGS, but without returning a new value. Store { /// Set of messages. sequence_set: SequenceSet, /// Kind of storage, i.e., replace, add, or remove. kind: StoreType, /// Kind of response, i.e., answer or silent. response: StoreResponse, /// Flags. flags: Vec>, // FIXME(misuse): must not accept "\*" or "\Recent" /// Use UID variant. uid: bool, /// --- Modifiers --- #[cfg(feature = "ext_condstore_qresync")] modifiers: Vec, }, /// 6.4.7. COPY Command /// /// Arguments: sequence set /// mailbox name /// Responses: no specific responses for this command /// Result: OK - copy completed /// NO - copy error: can't copy those messages or to that /// name /// BAD - command unknown or arguments invalid /// /// The COPY command copies the specified message(s) to the end of the /// specified destination mailbox. The flags and internal date of the /// message(s) SHOULD be preserved, and the Recent flag SHOULD be set, /// in the copy. /// /// If the destination mailbox does not exist, a server SHOULD return /// an error. It SHOULD NOT automatically create the mailbox. Unless /// it is certain that the destination mailbox can not be created, the /// server MUST send the response code "\[TRYCREATE\]" as the prefix of /// the text of the tagged NO response. This gives a hint to the /// client that it can attempt a CREATE command and retry the COPY if /// the CREATE is successful. /// /// If the COPY command is unsuccessful for any reason, server /// implementations MUST restore the destination mailbox to its state /// before the COPY attempt. Copy { /// Set of messages. sequence_set: SequenceSet, /// Destination mailbox. mailbox: Mailbox<'a>, /// Use UID variant. uid: bool, }, // The UID mechanism was inlined into copy, fetch, store, and search. // as an additional parameter. // // ### 6.4.8. UID Command // // * Arguments: // * command name // * command arguments // * Responses: untagged responses: FETCH, SEARCH // * Result: // * OK - UID command completed // * NO - UID command error // * BAD - command unknown or arguments invalid // // The UID command has two forms. In the first form, it takes as its // arguments a COPY, FETCH, or STORE command with arguments // appropriate for the associated command. However, the numbers in // the sequence set argument are unique identifiers instead of // message sequence numbers. Sequence set ranges are permitted, but // there is no guarantee that unique identifiers will be contiguous. // // A non-existent unique identifier is ignored without any error // message generated. Thus, it is possible for a UID FETCH command // to return an OK without any data or a UID COPY or UID STORE to // return an OK without performing any operations. // // In the second form, the UID command takes a SEARCH command with // SEARCH command arguments. The interpretation of the arguments is // the same as with SEARCH; however, the numbers returned in a SEARCH // response for a UID SEARCH command are unique identifiers instead // of message sequence numbers. For example, the command UID SEARCH // 1:100 UID 443:557 returns the unique identifiers corresponding to // the intersection of two sequence sets, the message sequence number // range 1:100 and the UID range 443:557. // // Note: in the above example, the UID range 443:557 // appears. The same comment about a non-existent unique // identifier being ignored without any error message also // applies here. Hence, even if neither UID 443 or 557 // exist, this range is valid and would include an existing // UID 495. // // Also note that a UID range of 559:* always includes the // UID of the last message in the mailbox, even if 559 is // higher than any assigned UID value. This is because the // contents of a range are independent of the order of the // range endpoints. Thus, any UID range with * as one of // the endpoints indicates at least one message (the // message with the highest numbered UID), unless the // mailbox is empty. // // The number after the "*" in an untagged FETCH response is always a // message sequence number, not a unique identifier, even for a UID // command response. However, server implementations MUST implicitly // include the UID message data item as part of any FETCH response // caused by a UID command, regardless of whether a UID was specified // as a message data item to the FETCH. // // Note: The rule about including the UID message data item as part // of a FETCH response primarily applies to the UID FETCH and UID // STORE commands, including a UID FETCH command that does not // include UID as a message data item. Although it is unlikely that // the other UID commands will cause an untagged FETCH, this rule // applies to these commands as well. // ----- Experimental/Expansion (https://tools.ietf.org/html/rfc3501#section-6.5) ----- // ### 6.5.1. X Command // // * Arguments: implementation defined // * Responses: implementation defined // * Result: // * OK - command completed // * NO - failure // * BAD - command unknown or arguments invalid // // Any command prefixed with an X is an experimental command. // Commands which are not part of this specification, a standard or // standards-track revision of this specification, or an // IESG-approved experimental protocol, MUST use the X prefix. // // Any added untagged responses issued by an experimental command // MUST also be prefixed with an X. Server implementations MUST NOT // send any such untagged responses, unless the client requested it // by issuing the associated experimental command. //X, /// IDLE command. /// ///
/// This extension must only be used when the server advertised support for it sending the IDLE capability. ///
Idle, /// ENABLE command. /// ///
/// This extension must only be used when the server advertised support for it sending the ENABLE capability. ///
Enable { /// Capabilities to enable. capabilities: Vec1>, }, /// COMPRESS command. /// ///
/// This extension must only be used when the server advertised support for it sending the COMPRESS capability. ///
Compress { /// Compression algorithm. algorithm: CompressionAlgorithm, }, /// Takes the name of a quota root and returns the quota root's resource usage and limits in an untagged QUOTA response. /// /// Arguments: /// * quota root /// /// Responses: /// * REQUIRED untagged responses: QUOTA /// /// Result: /// * OK - getquota completed /// * NO - getquota error: no such quota root, permission denied /// * BAD - command unknown or arguments invalid /// /// # Example (IMAP) /// /// ```imap /// S: * CAPABILITY [...] QUOTA QUOTA=RES-STORAGE [...] /// [...] /// C: G0001 GETQUOTA "!partition/sda4" /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847) /// S: G0001 OK Getquota complete /// ``` /// ///
/// This extension must only be used when the server advertised support for it sending the QUOTA* capability. ///
GetQuota { /// Name of quota root. root: AString<'a>, }, /// Takes a mailbox name and returns the list of quota roots for the mailbox in an untagged QUOTAROOT response. /// For each listed quota root, it also returns the quota root's resource usage and limits in an untagged QUOTA response. /// /// Arguments: /// * mailbox name /// /// Responses: /// * REQUIRED untagged responses: QUOTAROOT, QUOTA /// /// Result: /// * OK - getquotaroot completed /// * NO - getquotaroot error: permission denied /// * BAD - command unknown or arguments invalid /// /// Note that the mailbox name parameter doesn't have to reference an existing mailbox. /// This can be handy in order to determine which quota root would apply to a mailbox when it gets created /// /// # Example (IMAP) /// /// ```imap /// S: * CAPABILITY [...] QUOTA QUOTA=RES-STORAGE QUOTA=RES-MESSAGE /// [...] /// C: G0002 GETQUOTAROOT INBOX /// S: * QUOTAROOT INBOX "#user/alice" "!partition/sda4" /// S: * QUOTA "#user/alice" (MESSAGE 42 1000) /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847) /// S: G0002 OK Getquotaroot complete /// ``` /// ///
/// This extension must only be used when the server advertised support for it sending the QUOTA* capability. ///
GetQuotaRoot { /// Name of mailbox. mailbox: Mailbox<'a>, }, /// Changes the mailbox quota root resource limits to the specified limits. /// /// Arguments: /// * quota root list of resource limits /// /// Responses: /// * untagged responses: QUOTA /// /// Result: /// /// * OK - setquota completed /// * NO - setquota error: can't set that data /// * BAD - command unknown or arguments invalid /// /// Note: requires the server to advertise the "QUOTASET" capability. /// /// # Example (IMAP) /// /// ```imap /// S: * CAPABILITY [...] QUOTA QUOTASET QUOTA=RES-STORAGE QUOTA=RES- /// MESSAGE [...] /// [...] /// C: S0000 GETQUOTA "#user/alice" /// S: * QUOTA "#user/alice" (STORAGE 54 111 MESSAGE 42 1000) /// S: S0000 OK Getquota completed /// C: S0001 SETQUOTA "#user/alice" (STORAGE 510) /// S: * QUOTA "#user/alice" (STORAGE 58 512) /// // The server has rounded the STORAGE quota limit requested to /// the nearest 512 blocks of 1024 octets; otherwise, another client /// has performed a near-simultaneous SETQUOTA using a limit of 512. /// S: S0001 OK Rounded quota /// C: S0002 SETQUOTA "!partition/sda4" (STORAGE 99999999) /// S: * QUOTA "!partition/sda4" (STORAGE 104 10923847) /// // The server has not changed the quota, since this is a /// filesystem limit, and it cannot be changed. The QUOTA /// response here is entirely optional. /// S: S0002 NO Cannot change system limit /// ``` /// ///
/// This extension must only be used when the server advertised support for it sending the QUOTA* capability. ///
SetQuota { /// Name of quota root. root: AString<'a>, /// List of resource limits. quotas: Vec>, }, /// MOVE command. /// ///
/// This extension must only be used when the server advertised support for it sending the MOVE capability. ///
Move { /// Set of messages. sequence_set: SequenceSet, /// Destination mailbox. mailbox: Mailbox<'a>, /// Use UID variant. uid: bool, }, #[cfg(feature = "ext_id")] /// ID command. /// ///
/// This extension must only be used when the server advertised support for it sending the ID capability. ///
Id { /// Parameters. parameters: Option, NString<'a>)>>, }, #[cfg(feature = "ext_metadata")] /// Set annotation(s). /// ///
/// This extension must only be used when the server advertised support for it sending the METADATA* capability. ///
SetMetadata { mailbox: Mailbox<'a>, entry_values: Vec1>, }, #[cfg(feature = "ext_metadata")] /// Retrieve server or mailbox annotation(s). /// ///
/// This extension must only be used when the server advertised support for it sending the METADATA* capability. ///
GetMetadata { options: Vec, mailbox: Mailbox<'a>, entries: Vec1>, }, #[cfg(feature = "ext_namespace")] /// Retrieve the namespaces available to the client. /// ///
/// This extension must only be used when the server advertised support for it sending the NAMESPACE capability. ///
Namespace, } impl<'a> CommandBody<'a> { /// Prepend a tag to finalize the command body to a command. pub fn tag(self, tag: T) -> Result, T::Error> where T: TryInto>, { Ok(Command { tag: tag.try_into()?, body: self, }) } // ----- Constructors ----- /// Construct an AUTHENTICATE command. pub fn authenticate(mechanism: AuthMechanism<'a>) -> Self { CommandBody::Authenticate { mechanism, initial_response: None, } } /// Construct an AUTHENTICATE command (with an initial response, SASL-IR). /// /// Note: Use this only when the server advertised the `SASL-IR` capability. /// ///
/// This extension must only be used when the server advertised support for it sending the SASL-IR capability. ///
pub fn authenticate_with_ir(mechanism: AuthMechanism<'a>, initial_response: I) -> Self where I: Into>, { CommandBody::Authenticate { mechanism, initial_response: Some(Secret::new(initial_response.into())), } } /// Construct a LOGIN command. pub fn login(username: U, password: P) -> Result> where U: TryInto>, P: TryInto>, { Ok(CommandBody::Login { username: username.try_into().map_err(LoginError::Username)?, password: Secret::new(password.try_into().map_err(LoginError::Password)?), }) } /// Construct a SELECT command. pub fn select(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::Select { mailbox: mailbox.try_into()?, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }) } /// Construct an EXAMINE command. pub fn examine(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::Examine { mailbox: mailbox.try_into()?, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }) } /// Construct a CREATE command. pub fn create(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::Create { mailbox: mailbox.try_into()?, }) } /// Construct a DELETE command. pub fn delete(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::Delete { mailbox: mailbox.try_into()?, }) } /// Construct a RENAME command. pub fn rename(mailbox: F, new_mailbox: T) -> Result> where F: TryInto>, T: TryInto>, { Ok(CommandBody::Rename { from: mailbox.try_into().map_err(RenameError::From)?, to: new_mailbox.try_into().map_err(RenameError::To)?, }) } /// Construct a SUBSCRIBE command. pub fn subscribe(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::Subscribe { mailbox: mailbox.try_into()?, }) } /// Construct an UNSUBSCRIBE command. pub fn unsubscribe(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::Unsubscribe { mailbox: mailbox.try_into()?, }) } /// Construct a LIST command. pub fn list( reference: A, mailbox_wildcard: B, ) -> Result> where A: TryInto>, B: TryInto>, { Ok(CommandBody::List { reference: reference.try_into().map_err(ListError::Reference)?, mailbox_wildcard: mailbox_wildcard.try_into().map_err(ListError::Mailbox)?, }) } /// Construct a LSUB command. pub fn lsub( reference: A, mailbox_wildcard: B, ) -> Result> where A: TryInto>, B: TryInto>, { Ok(CommandBody::Lsub { reference: reference.try_into().map_err(ListError::Reference)?, mailbox_wildcard: mailbox_wildcard.try_into().map_err(ListError::Mailbox)?, }) } /// Construct a STATUS command. pub fn status(mailbox: M, item_names: I) -> Result where M: TryInto>, I: Into>, { let mailbox = mailbox.try_into()?; Ok(CommandBody::Status { mailbox, item_names: item_names.into(), }) } /// Construct an APPEND command. pub fn append( mailbox: M, flags: Vec>, date: Option, message: D, ) -> Result> where M: TryInto>, D: TryInto>, { Ok(CommandBody::Append { mailbox: mailbox.try_into().map_err(AppendError::Mailbox)?, flags, date, message: LiteralOrLiteral8::Literal(message.try_into().map_err(AppendError::Data)?), }) } /// Construct a SEARCH command. pub fn search(charset: Option>, criteria: Vec1>, uid: bool) -> Self { CommandBody::Search { charset, criteria, uid, } } /// Construct a FETCH command. pub fn fetch(sequence_set: S, macro_or_item_names: I, uid: bool) -> Result where S: TryInto, I: Into>, { let sequence_set = sequence_set.try_into()?; Ok(CommandBody::Fetch { sequence_set, macro_or_item_names: macro_or_item_names.into(), uid, #[cfg(feature = "ext_condstore_qresync")] modifiers: Vec::default(), }) } /// Construct a STORE command. pub fn store( sequence_set: S, kind: StoreType, response: StoreResponse, flags: Vec>, uid: bool, ) -> Result where S: TryInto, { let sequence_set = sequence_set.try_into()?; Ok(CommandBody::Store { sequence_set, kind, response, flags, uid, #[cfg(feature = "ext_condstore_qresync")] modifiers: Vec::default(), }) } /// Construct a COPY command. pub fn copy( sequence_set: S, mailbox: M, uid: bool, ) -> Result> where S: TryInto, M: TryInto>, { Ok(CommandBody::Copy { sequence_set: sequence_set.try_into().map_err(CopyError::Sequence)?, mailbox: mailbox.try_into().map_err(CopyError::Mailbox)?, uid, }) } /// Get the name of the command. pub fn name(&self) -> &'static str { match self { Self::Capability => "CAPABILITY", Self::Noop => "NOOP", Self::Logout => "LOGOUT", #[cfg(feature = "starttls")] Self::StartTLS => "STARTTLS", Self::Authenticate { .. } => "AUTHENTICATE", Self::Login { .. } => "LOGIN", Self::Select { .. } => "SELECT", Self::Sort { .. } => "SORT", Self::Thread { .. } => "THREAD", Self::Unselect => "UNSELECT", Self::Examine { .. } => "EXAMINE", Self::Create { .. } => "CREATE", Self::Delete { .. } => "DELETE", Self::Rename { .. } => "RENAME", Self::Subscribe { .. } => "SUBSCRIBE", Self::Unsubscribe { .. } => "UNSUBSCRIBE", Self::List { .. } => "LIST", Self::Lsub { .. } => "LSUB", Self::Status { .. } => "STATUS", Self::Append { .. } => "APPEND", Self::Check => "CHECK", Self::Close => "CLOSE", Self::Expunge => "EXPUNGE", Self::ExpungeUid { .. } => "EXPUNGE", Self::Search { .. } => "SEARCH", Self::Fetch { .. } => "FETCH", Self::Store { .. } => "STORE", Self::Copy { .. } => "COPY", Self::Idle => "IDLE", Self::Enable { .. } => "ENABLE", Self::Compress { .. } => "COMPRESS", Self::GetQuota { .. } => "GETQUOTA", Self::GetQuotaRoot { .. } => "GETQUOTAROOT", Self::SetQuota { .. } => "SETQUOTA", Self::Move { .. } => "MOVE", #[cfg(feature = "ext_id")] Self::Id { .. } => "ID", #[cfg(feature = "ext_metadata")] Self::SetMetadata { .. } => "SETMETADATA", #[cfg(feature = "ext_metadata")] Self::GetMetadata { .. } => "GETMETADATA", #[cfg(feature = "ext_namespace")] Self::Namespace => "NAMESPACE", } } } #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum SelectParameter { CondStore, QResync { uid_validity: NonZeroU32, mod_sequence_value: NonZeroU64, known_uids: Option, // TODO(misuse): "*" is not allowed. seq_match_data: Option<(SequenceSet, SequenceSet)>, // TODO(misuse): ensure both have the same length? }, } #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum FetchModifier { ChangedSince(NonZeroU64), Vanished, } #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum StoreModifier { UnchangedSince(u64), } /// Error-related types. pub mod error { use thiserror::Error; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum LoginError { #[error("Invalid username: {0}")] Username(U), #[error("Invalid password: {0}")] Password(P), } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum RenameError { #[error("Invalid (from) mailbox: {0}")] From(F), #[error("Invalid (to) mailbox: {0}")] To(T), } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum ListError { #[error("Invalid reference: {0}")] Reference(R), #[error("Invalid mailbox: {0}")] Mailbox(M), } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum AppendError { #[error("Invalid mailbox: {0}")] Mailbox(M), #[error("Invalid data: {0}")] Data(D), } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum CopyError { #[error("Invalid sequence: {0}")] Sequence(S), #[error("Invalid mailbox: {0}")] Mailbox(M), } } #[cfg(test)] mod tests { use chrono::DateTime as ChronoDateTime; use super::*; #[cfg(feature = "ext_utf8")] use crate::extensions::{enable::CapabilityEnable, utf8::Utf8Kind}; use crate::{ auth::AuthMechanism, core::{AString, Charset, IString, Literal, LiteralMode, Vec1}, datetime::DateTime, extensions::{binary::Literal8, compress::CompressionAlgorithm}, fetch::{Macro, MacroOrMessageDataItemNames, MessageDataItemName, Part, Section}, flag::{Flag, StoreType}, mailbox::{ListMailbox, Mailbox}, search::SearchKey, secret::Secret, sequence::{SeqOrUid, Sequence, SequenceSet}, status::StatusDataItemName, }; #[test] fn test_conversion_command_body() { let cmds = vec![ CommandBody::Capability, CommandBody::Noop, CommandBody::Logout, #[cfg(feature = "starttls")] CommandBody::StartTLS, CommandBody::authenticate(AuthMechanism::Plain), CommandBody::authenticate(AuthMechanism::Login), CommandBody::authenticate_with_ir(AuthMechanism::Plain, b"XXXXXXXX".as_ref()), CommandBody::authenticate_with_ir(AuthMechanism::Login, b"YYYYYYYY".as_ref()), CommandBody::login("alice", "I_am_an_atom").unwrap(), CommandBody::login("alice", "I am \\ \"quoted\"").unwrap(), CommandBody::login("alice", "I am a literal²").unwrap(), CommandBody::login( AString::Atom("alice".try_into().unwrap()), AString::String(crate::core::IString::Literal( vec![0xff, 0xff, 0xff].try_into().unwrap(), )), ) .unwrap(), CommandBody::select("inbox").unwrap(), CommandBody::select("atom").unwrap(), CommandBody::select("C:\\").unwrap(), CommandBody::select("²").unwrap(), CommandBody::select("Trash").unwrap(), CommandBody::examine("inbox").unwrap(), CommandBody::examine("atom").unwrap(), CommandBody::examine("C:\\").unwrap(), CommandBody::examine("²").unwrap(), CommandBody::examine("Trash").unwrap(), CommandBody::create("inBoX").unwrap(), CommandBody::delete("inBOX").unwrap(), CommandBody::rename("iNBoS", "INboX").unwrap(), CommandBody::subscribe("inbox").unwrap(), CommandBody::unsubscribe("INBOX").unwrap(), CommandBody::list("iNbOx", "test").unwrap(), CommandBody::list("inbox", ListMailbox::Token("test".try_into().unwrap())).unwrap(), CommandBody::lsub( "inbox", ListMailbox::String(IString::Quoted("\x7f".try_into().unwrap())), ) .unwrap(), CommandBody::list("inBoX", ListMailbox::Token("test".try_into().unwrap())).unwrap(), CommandBody::lsub( "INBOX", ListMailbox::String(IString::Quoted("\x7f".try_into().unwrap())), ) .unwrap(), CommandBody::status("inbox", vec![StatusDataItemName::Messages]).unwrap(), CommandBody::append( "inbox", vec![], Some( DateTime::try_from( ChronoDateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200") .unwrap(), ) .unwrap(), ), vec![0xff, 0xff, 0xff], ) .unwrap(), CommandBody::append( "inbox", vec![Flag::Keyword("test".try_into().unwrap())], Some( DateTime::try_from( ChronoDateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200") .unwrap(), ) .unwrap(), ), vec![0xff, 0xff, 0xff], ) .unwrap(), CommandBody::Check, CommandBody::Close, CommandBody::Expunge, CommandBody::search( None, Vec1::from(SearchKey::And( vec![SearchKey::All, SearchKey::New, SearchKey::Unseen] .try_into() .unwrap(), )), false, ), CommandBody::search( None, Vec1::from(SearchKey::And( vec![SearchKey::All, SearchKey::New, SearchKey::Unseen] .try_into() .unwrap(), )), true, ), CommandBody::search( None, Vec1::from(SearchKey::And( vec![SearchKey::SequenceSet(SequenceSet( vec![Sequence::Single(SeqOrUid::Value(42.try_into().unwrap()))] .try_into() .unwrap(), ))] .try_into() .unwrap(), )), true, ), CommandBody::search( None, Vec1::from(SearchKey::SequenceSet("42".try_into().unwrap())), true, ), CommandBody::search( None, Vec1::from(SearchKey::SequenceSet("*".try_into().unwrap())), true, ), CommandBody::search( None, Vec1::from(SearchKey::Or( Box::new(SearchKey::Draft), Box::new(SearchKey::All), )), true, ), CommandBody::search( Some(Charset::try_from("UTF-8").unwrap()), Vec1::from(SearchKey::Or( Box::new(SearchKey::Draft), Box::new(SearchKey::All), )), true, ), CommandBody::fetch( "1", vec![MessageDataItemName::BodyExt { partial: None, section: Some(Section::Part(Part( vec![1.try_into().unwrap(), 1.try_into().unwrap()] .try_into() .unwrap(), ))), peek: true, }], false, ) .unwrap(), CommandBody::fetch("1:*,2,3", Macro::Full, true).unwrap(), CommandBody::store( "1,2:*", StoreType::Remove, StoreResponse::Answer, vec![Flag::Seen, Flag::Draft], false, ) .unwrap(), CommandBody::store( "1:5", StoreType::Add, StoreResponse::Answer, vec![Flag::Keyword("TEST".try_into().unwrap())], true, ) .unwrap(), CommandBody::copy("1", "inbox", false).unwrap(), CommandBody::copy("1337", "archive", true).unwrap(), ]; for (no, cmd_body) in cmds.into_iter().enumerate() { println!("Test: {no}, {cmd_body:?}"); let _ = cmd_body.tag(format!("A{no}")).unwrap(); } } #[test] fn test_command_body_name() { let tests = [ (CommandBody::Capability, "CAPABILITY"), (CommandBody::Noop, "NOOP"), (CommandBody::Logout, "LOGOUT"), #[cfg(feature = "starttls")] (CommandBody::StartTLS, "STARTTLS"), ( CommandBody::Authenticate { mechanism: AuthMechanism::Plain, initial_response: None, }, "AUTHENTICATE", ), ( CommandBody::Login { username: AString::try_from("user").unwrap(), password: Secret::new(AString::try_from("pass").unwrap()), }, "LOGIN", ), ( CommandBody::Select { mailbox: Mailbox::Inbox, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }, "SELECT", ), (CommandBody::Unselect, "UNSELECT"), ( CommandBody::Examine { mailbox: Mailbox::Inbox, #[cfg(feature = "ext_condstore_qresync")] parameters: Vec::default(), }, "EXAMINE", ), ( CommandBody::Create { mailbox: Mailbox::Inbox, }, "CREATE", ), ( CommandBody::Delete { mailbox: Mailbox::Inbox, }, "DELETE", ), ( CommandBody::Rename { from: Mailbox::Inbox, to: Mailbox::Inbox, }, "RENAME", ), ( CommandBody::Subscribe { mailbox: Mailbox::Inbox, }, "SUBSCRIBE", ), ( CommandBody::Unsubscribe { mailbox: Mailbox::Inbox, }, "UNSUBSCRIBE", ), ( CommandBody::List { reference: Mailbox::Inbox, mailbox_wildcard: ListMailbox::try_from("").unwrap(), }, "LIST", ), ( CommandBody::Lsub { reference: Mailbox::Inbox, mailbox_wildcard: ListMailbox::try_from("").unwrap(), }, "LSUB", ), ( CommandBody::Status { mailbox: Mailbox::Inbox, item_names: vec![].into(), }, "STATUS", ), ( CommandBody::Append { mailbox: Mailbox::Inbox, flags: vec![], date: None, message: LiteralOrLiteral8::Literal(Literal::try_from("").unwrap()), }, "APPEND", ), ( CommandBody::Append { mailbox: Mailbox::Inbox, flags: vec![], date: None, message: LiteralOrLiteral8::Literal8(Literal8 { data: b"Hello\x00World\x00".as_ref().into(), mode: LiteralMode::NonSync, }), }, "APPEND", ), (CommandBody::Check, "CHECK"), (CommandBody::Close, "CLOSE"), (CommandBody::Expunge, "EXPUNGE"), ( CommandBody::Search { charset: None, criteria: Vec1::from(SearchKey::Recent), uid: true, }, "SEARCH", ), ( CommandBody::Fetch { sequence_set: SequenceSet::try_from(1u32).unwrap(), macro_or_item_names: MacroOrMessageDataItemNames::Macro(Macro::Full), uid: true, #[cfg(feature = "ext_condstore_qresync")] modifiers: Vec::default(), }, "FETCH", ), ( CommandBody::Store { sequence_set: SequenceSet::try_from(1).unwrap(), flags: vec![], response: StoreResponse::Silent, kind: StoreType::Add, uid: true, #[cfg(feature = "ext_condstore_qresync")] modifiers: Vec::default(), }, "STORE", ), ( CommandBody::Copy { sequence_set: SequenceSet::try_from(1).unwrap(), mailbox: Mailbox::Inbox, uid: true, }, "COPY", ), (CommandBody::Idle, "IDLE"), #[cfg(feature = "ext_utf8")] ( CommandBody::Enable { capabilities: Vec1::from(CapabilityEnable::Utf8(Utf8Kind::Only)), }, "ENABLE", ), ( CommandBody::Compress { algorithm: CompressionAlgorithm::Deflate, }, "COMPRESS", ), ( CommandBody::GetQuota { root: AString::try_from("root").unwrap(), }, "GETQUOTA", ), ( CommandBody::GetQuotaRoot { mailbox: Mailbox::Inbox, }, "GETQUOTAROOT", ), ( CommandBody::SetQuota { root: AString::try_from("root").unwrap(), quotas: vec![], }, "SETQUOTA", ), ( CommandBody::Move { sequence_set: SequenceSet::try_from(1).unwrap(), mailbox: Mailbox::Inbox, uid: true, }, "MOVE", ), ]; for (test, expected) in tests { assert_eq!(test.name(), expected); } } } duesee-imap-codec-0d00966/imap-types/src/core.rs000066400000000000000000002041571507724125200214300ustar00rootroot00000000000000//! Core data types. //! //! To ensure correctness and to support all forms of data transmission, imap-types uses types such //! as [`AString`], [`Atom`], [`IString`], [`Quoted`], and [`Literal`]. When constructing messages, //! imap-types can automatically choose the best representation. However, it's always possible to //! manually select a specific representation. //! //! The core types exist for two reasons. First, they guarantee that invalid messages cannot be //! produced. For example, a [`Tag`] will never contain whitespace as this would break parsing. //! Furthermore, the representation of a value may change the IMAP protocol flow. A username, for //! example, can be represented as an atom, a quoted string, or a literal. While atoms and quoted //! strings are similar, a literal requires a different protocol flow and implementations must take //! this into account. //! //! While this seems complicated at first, there are good news: You don't need to think about IMAP //! too much. imap-types *ensures* that everything you do is correct. If you are able to construct //! an invalid message, this is considered a bug in imap-types. //! //! # Overview //! //! ```text //! ┌───────┐ ┌─────────────────┐ //! │AString│ │ NString │ //! └──┬─┬──┘ │(Option)│ //! │ │ └─────┬───────────┘ //! │ └──────┐ │ //! │ │ │ //! ┌────┐ ┌──▼────┐ ┌─▼───▼─┐ //! │Atom│ │AtomExt│ │IString│ //! └────┘ └───────┘ └┬─────┬┘ //! │ │ //! ┌─────▼─┐ ┌─▼────┐ //! │Literal│ │Quoted│ //! └───────┘ └──────┘ //! ``` #[cfg(feature = "tag_generator")] use std::sync::atomic::{AtomicUsize, Ordering}; use std::{ borrow::Cow, fmt::{Debug, Display, Formatter}, str::from_utf8, vec::IntoIter, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "tag_generator")] #[cfg(not(debug_assertions))] use rand::distributions::{Alphanumeric, DistString}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "ext_utf8")] use crate::extensions::utf8::QuotedUtf8; use crate::utils::indicators::{ is_any_text_char_except_quoted_specials, is_astring_char, is_atom_char, is_char8, is_text_char, }; #[cfg(feature = "tag_generator")] static GLOBAL_TAG_GENERATOR_COUNT: AtomicUsize = AtomicUsize::new(0); macro_rules! impl_try_from { ($via:ty, $lifetime:lifetime, $from:ty, $target:ty) => { impl<$lifetime> TryFrom<$from> for $target { type Error = <$via as TryFrom<$from>>::Error; fn try_from(value: $from) -> Result { let value = <$via>::try_from(value)?; Ok(Self::from(value)) } } }; } pub(crate) use impl_try_from; use crate::{ error::{ValidationError, ValidationErrorKind}, extensions::binary::Literal8, }; /// A string subset to model IMAP's `atom`s. /// /// Rules: /// /// * Length must be >= 1 /// * Only some characters are allowed, e.g., no whitespace /// /// # ABNF definition /// /// ```abnf /// atom = 1*ATOM-CHAR /// ATOM-CHAR = /// CHAR = %x01-7F /// ; any 7-bit US-ASCII character, excluding NUL /// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials /// SP = %x20 /// CTL = %x00-1F / %x7F /// ; controls /// list-wildcards = "%" / "*" /// quoted-specials = DQUOTE / "\" /// DQUOTE = %x22 /// ; " (Double Quote) /// resp-specials = "]" /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ToStatic)] pub struct Atom<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for Atom<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "Atom({:?})", self.0) } } impl<'a> Atom<'a> { /// Validates if value conforms to atom's ABNF definition. pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if value.is_empty() { return Err(ValidationError::new(ValidationErrorKind::Empty)); } if let Some(at) = value.iter().position(|b| !is_atom_char(*b)) { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } /// Returns a reference to the inner value. pub fn inner(&self) -> &str { self.0.as_ref() } /// Consumes the atom, returning the inner value. pub fn into_inner(self) -> Cow<'a, str> { self.0 } /// Constructs an atom without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: C) -> Self where C: Into>, { let inner = inner.into(); #[cfg(debug_assertions)] Self::validate(inner.as_bytes()).unwrap(); Self(inner) } } impl<'a> TryFrom<&'a [u8]> for Atom<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { Self::validate(value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Self(Cow::Borrowed(from_utf8(value).unwrap()))) } } impl TryFrom> for Atom<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { Self::validate(&value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Self(Cow::Owned(String::from_utf8(value).unwrap()))) } } impl<'a> TryFrom<&'a str> for Atom<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Self::validate(value)?; Ok(Self(Cow::Borrowed(value))) } } impl TryFrom for Atom<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Self::validate(&value)?; Ok(Atom(Cow::Owned(value))) } } impl<'a> TryFrom> for Atom<'a> { type Error = ValidationError; fn try_from(value: Cow<'a, str>) -> Result { Self::validate(value.as_bytes())?; Ok(Atom(value)) } } impl AsRef for Atom<'_> { fn as_ref(&self) -> &str { self.0.as_ref() } } impl Display for Atom<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "{}", self.0) } } /// A string subset to model IMAP's `1*ASTRING-CHAR` ("extended `atom`"). /// /// This type is required due to the use of `1*ASTRING-CHAR` in `astring`, see ABNF definition below. /// /// Rules: /// /// * Length must be >= 1 /// * Only some characters are allowed, e.g., no whitespace /// /// # ABNF definition /// /// ```abnf /// astring = 1*ASTRING-CHAR / string /// ; ^^^^^^^^^^^^^^ /// ; | /// ; `AtomExt` /// /// ASTRING-CHAR = ATOM-CHAR / resp-specials /// ; ^^^^^^^^^ ^^^^^^^^^^^^^ /// ; | | /// ; | Additionally allowed in `AtomExt` /// ; See `Atom` /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct AtomExt<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for AtomExt<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "AtomExt({:?})", self.0) } } impl<'a> AtomExt<'a> { /// Validates if value conforms to extended atom's ABNF definition. pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if value.is_empty() { return Err(ValidationError::new(ValidationErrorKind::Empty)); } if let Some(at) = value.iter().position(|b| !is_astring_char(*b)) { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } /// Returns a reference to the inner value. pub fn inner(&self) -> &str { self.0.as_ref() } /// Consumes the atom, returning the inner value. pub fn into_inner(self) -> Cow<'a, str> { self.0 } /// Constructs an extended atom without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: C) -> Self where C: Into>, { let inner = inner.into(); #[cfg(debug_assertions)] Self::validate(inner.as_bytes()).unwrap(); Self(inner) } } impl<'a> TryFrom<&'a [u8]> for AtomExt<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { Self::validate(value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Self(Cow::Borrowed(from_utf8(value).unwrap()))) } } impl TryFrom> for AtomExt<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { Self::validate(&value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Self(Cow::Owned(String::from_utf8(value).unwrap()))) } } impl<'a> TryFrom<&'a str> for AtomExt<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Self::validate(value)?; Ok(Self(Cow::Borrowed(value))) } } impl TryFrom for AtomExt<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Self::validate(&value)?; Ok(Self(Cow::Owned(value))) } } impl<'a> From> for AtomExt<'a> { fn from(value: Atom<'a>) -> Self { Self(value.0) } } impl AsRef for AtomExt<'_> { fn as_ref(&self) -> &str { &self.0 } } /// Either a quoted string or a literal. /// /// Note: The empty string is represented as either "" (a quoted string with zero characters between /// double quotes) or as {0} followed by CRLF (a literal with an octet count of 0). /// /// # ABNF definition /// /// ```abnf /// string = quoted / literal /// ; ^^^^^^ ^^^^^^^ /// ; | | /// ; | See `Literal` /// ; See `Quoted` /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum IString<'a> { /// Literal, see [`Literal`]. Literal(Literal<'a>), /// Quoted string, see[`Quoted`]. Quoted(Quoted<'a>), #[cfg(feature = "ext_utf8")] /// Quoted string containing UTF-8, see[`QuotedUtf8`]. QuotedUtf8(QuotedUtf8<'a>), } impl<'a> IString<'a> { pub fn into_inner(self) -> Cow<'a, [u8]> { match self { Self::Literal(literal) => literal.into_inner(), Self::Quoted(quoted) => match quoted.into_inner() { Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()), Cow::Owned(s) => Cow::Owned(s.into_bytes()), }, #[cfg(feature = "ext_utf8")] Self::QuotedUtf8(quoted_utf8) => match quoted_utf8.0 { Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()), Cow::Owned(s) => Cow::Owned(s.into_bytes()), }, } } } impl<'a> TryFrom<&'a [u8]> for IString<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { if let Ok(quoted) = Quoted::try_from(value) { return Ok(IString::Quoted(quoted)); } Ok(IString::Literal(Literal::try_from(value)?)) } } impl TryFrom> for IString<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { // TODO(efficiency) if let Ok(quoted) = Quoted::try_from(value.clone()) { return Ok(IString::Quoted(quoted)); } Ok(IString::Literal(Literal::try_from(value)?)) } } impl<'a> TryFrom<&'a str> for IString<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { if let Ok(quoted) = Quoted::try_from(value) { return Ok(IString::Quoted(quoted)); } Ok(IString::Literal(Literal::try_from(value)?)) } } impl TryFrom for IString<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { // TODO(efficiency) if let Ok(quoted) = Quoted::try_from(value.clone()) { return Ok(IString::Quoted(quoted)); } Ok(IString::Literal(Literal::try_from(value)?)) } } impl<'a> From> for IString<'a> { fn from(value: Literal<'a>) -> Self { Self::Literal(value) } } impl<'a> From> for IString<'a> { fn from(value: Quoted<'a>) -> Self { Self::Quoted(value) } } impl AsRef<[u8]> for IString<'_> { fn as_ref(&self) -> &[u8] { match self { Self::Quoted(quoted) => quoted.as_ref().as_bytes(), Self::Literal(literal) => literal.as_ref(), #[cfg(feature = "ext_utf8")] Self::QuotedUtf8(quoted_utf8) => quoted_utf8.0.as_bytes(), } } } /// A sequence of zero or more (non-null) bytes prefixed with a length. /// /// "A literal is a sequence of zero or more octets (including CR and LF), prefix-quoted with an octet count in the form of an open brace ("{"), the number of octets, close brace ("}"), and CRLF. /// In the case of literals transmitted from server to client, the CRLF is immediately followed by the octet data. /// In the case of literals transmitted from client to server, the client MUST wait to receive a command continuation request (...) before sending the octet data (and the remainder of the command). /// /// Note: Even if the octet count is 0, a client transmitting a literal MUST wait to receive a command continuation request." ([RFC 3501](https://www.rfc-editor.org/rfc/rfc3501.html)) /// /// # ABNF definition /// /// ```abnf /// literal = "{" number "}" CRLF *CHAR8 /// ; Number represents the number of CHAR8s /// number = 1*DIGIT /// ; Unsigned 32-bit integer /// ; (0 <= n < 4,294,967,296) /// DIGIT = %x30-39 /// ; 0-9 /// CRLF = CR LF /// ; Internet standard newline /// CHAR8 = %x01-ff /// ; any OCTET except NUL, %x00 /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Literal<'a> { #[cfg_attr( feature = "serde", serde(deserialize_with = "deserialize_literal_data") )] pub(crate) data: Cow<'a, [u8]>, /// Specifies whether this is a synchronizing or non-synchronizing literal. /// /// `true` (default) denotes a synchronizing literal, e.g., `{3}\r\nfoo`. /// `false` denotes a non-synchronizing literal, e.g., `{3+}\r\nfoo`. /// /// Note: In the special case that a server advertised a `LITERAL-` capability, AND the literal /// has more than 4096 bytes a non-synchronizing literal must still be treated as synchronizing. pub(crate) mode: LiteralMode, } #[cfg(feature = "serde")] fn deserialize_literal_data<'de, 'a, D>(deserializer: D) -> Result, D::Error> where D: serde::de::Deserializer<'de>, { let data = Vec::deserialize(deserializer)?; Literal::validate(&data).map_err(serde::de::Error::custom)?; Ok(Cow::Owned(data)) } // We want a more readable `Debug` implementation. impl Debug for Literal<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { struct BStr<'a>(&'a Cow<'a, [u8]>); impl Debug for BStr<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "b\"{}\"", crate::utils::escape_byte_string(self.0.as_ref()) ) } } f.debug_struct("Literal") .field("data", &BStr(&self.data)) .field("mode", &self.mode) .finish() } } impl<'a> Literal<'a> { pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if let Some(at) = value.iter().position(|b| !is_char8(*b)) { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } pub fn data(&self) -> &[u8] { self.data.as_ref() } pub fn mode(&self) -> LiteralMode { self.mode } pub fn set_mode(&mut self, mode: LiteralMode) { self.mode = mode; } /// Turn literal into sync literal. pub fn into_sync(mut self) -> Self { self.mode = LiteralMode::Sync; self } /// Turn literal into non-sync literal. /// ///
/// This extension must only be used when the server advertised support for it sending the LITERAL+ or LITERAL- capability. ///
pub fn into_non_sync(mut self) -> Self { self.mode = LiteralMode::NonSync; self } pub fn into_inner(self) -> Cow<'a, [u8]> { self.data } /// Constructs a literal without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `data` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(data: D) -> Self where D: Into>, { let data = data.into(); #[cfg(debug_assertions)] Self::validate(&data).unwrap(); Self { data, mode: LiteralMode::Sync, } } /// Constructs a literal without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `data` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. /// ///
/// This extension must only be used when the server advertised support for it sending the LITERAL+ or LITERAL- capability. ///
pub fn unvalidated_non_sync(data: D) -> Self where D: Into>, { let data = data.into(); #[cfg(debug_assertions)] Self::validate(&data).unwrap(); Self { data, mode: LiteralMode::NonSync, } } } impl<'a> TryFrom<&'a [u8]> for Literal<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { Self::validate(value)?; Ok(Literal { data: Cow::Borrowed(value), mode: LiteralMode::Sync, }) } } impl TryFrom> for Literal<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { Self::validate(&value)?; Ok(Literal { data: Cow::Owned(value), mode: LiteralMode::Sync, }) } } impl<'a> TryFrom<&'a str> for Literal<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Self::validate(value)?; Ok(Literal { data: Cow::Borrowed(value.as_bytes()), mode: LiteralMode::Sync, }) } } impl TryFrom for Literal<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Self::validate(&value)?; Ok(Literal { data: Cow::Owned(value.into_bytes()), mode: LiteralMode::Sync, }) } } impl AsRef<[u8]> for Literal<'_> { fn as_ref(&self) -> &[u8] { &self.data } } /// Literal mode, i.e., sync or non-sync. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToStatic)] pub enum LiteralMode { /// A synchronizing literal, i.e., `{}\r\n`. Sync, /// A non-synchronizing literal according to RFC 7888, i.e., `{+}\r\n`. /// ///
/// This extension must only be used when the server advertised support for it sending the LITERAL+ or LITERAL- capability. ///
NonSync, } /// A quoted string. /// /// "The quoted string form is an alternative that avoids the overhead of processing a literal at the cost of limitations of characters which may be used. /// /// A quoted string is a sequence of zero or more 7-bit characters, excluding CR and LF, with double quote (<">) characters at each end." ([RFC 3501](https://www.rfc-editor.org/rfc/rfc3501.html)) /// /// # ABNF definition /// /// ```abnf /// quoted = DQUOTE *QUOTED-CHAR DQUOTE /// DQUOTE = %x22 /// ; " (Double Quote) /// QUOTED-CHAR = / "\" quoted-specials /// TEXT-CHAR = /// CHAR = %x01-7F /// ; any 7-bit US-ASCII character, excluding NUL /// CR = %x0D /// ; carriage return /// LF = %x0A /// ; linefeed /// quoted-specials = DQUOTE / "\" /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Quoted<'a>(pub(crate) Cow<'a, str>); impl Debug for Quoted<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "Quoted({:?})", self.0) } } impl<'a> Quoted<'a> { pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if let Some(at) = value.iter().position(|b| !is_text_char(*b)) { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } pub fn inner(&self) -> &str { self.0.as_ref() } pub fn into_inner(self) -> Cow<'a, str> { self.0 } /// Constructs a quoted string without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: C) -> Self where C: Into>, { let inner = inner.into(); #[cfg(debug_assertions)] Self::validate(inner.as_bytes()).unwrap(); Self(inner) } } impl<'a> TryFrom<&'a [u8]> for Quoted<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { Quoted::validate(value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Quoted(Cow::Borrowed(from_utf8(value).unwrap()))) } } impl TryFrom> for Quoted<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { Quoted::validate(&value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Quoted(Cow::Owned(String::from_utf8(value).unwrap()))) } } impl<'a> TryFrom<&'a str> for Quoted<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Quoted::validate(value)?; Ok(Quoted(Cow::Borrowed(value))) } } impl TryFrom for Quoted<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Quoted::validate(&value)?; Ok(Quoted(Cow::Owned(value))) } } impl AsRef for Quoted<'_> { fn as_ref(&self) -> &str { &self.0 } } /// Either `NIL` or a string. /// /// This is modeled using Rust's [`Option`] type. /// /// # ABNF definition /// /// ```abnf /// nstring = string / nil /// ; ^^^^^^ /// ; | /// ; See `IString` /// /// nil = "NIL" /// ``` #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct NString<'a>( // This wrapper is merely used for formatting. // The inner value can be public. pub Option>, ); impl<'a> NString<'a> { /// Convenience alias to `NString(None)` for any lifetime. pub const NIL: NString<'a> = NString(None); pub fn into_option(self) -> Option> { self.0.map(|inner| inner.into_inner()) } } impl Default for NString<'_> { /// Returns [`Self::NIL`]. fn default() -> Self { Self::NIL } } macro_rules! impl_try_from_nstring { ($from:ty) => { impl<'a> TryFrom<$from> for NString<'a> { type Error = ValidationError; fn try_from(value: $from) -> Result { Ok(Self(Some(IString::try_from(value)?))) } } }; } impl_try_from_nstring!(&'a [u8]); impl_try_from_nstring!(Vec); impl_try_from_nstring!(&'a str); impl_try_from_nstring!(String); impl<'a> From> for NString<'a> { fn from(value: Literal<'a>) -> Self { Self(Some(IString::from(value))) } } impl<'a> From> for NString<'a> { fn from(value: Quoted<'a>) -> Self { Self(Some(IString::from(value))) } } /// Either an (extended) atom or a string. /// /// # ABNF definition /// /// ```abnf /// astring = 1*ASTRING-CHAR / string /// ; ^^^^^^^^^^^^^^ /// ; | /// ; See `AtomExt` /// ``` #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum AString<'a> { // `1*ATOM-CHAR` does not allow resp-specials, but `1*ASTRING-CHAR` does ... :-/ Atom(AtomExt<'a>), // 1*ASTRING-CHAR / String(IString<'a>), // string } impl<'a> TryFrom<&'a [u8]> for AString<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { if let Ok(atom) = AtomExt::try_from(value) { return Ok(AString::Atom(atom)); } Ok(AString::String(IString::try_from(value)?)) } } impl TryFrom> for AString<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { // TODO(efficiency) if let Ok(atom) = AtomExt::try_from(value.clone()) { return Ok(AString::Atom(atom)); } Ok(AString::String(IString::try_from(value)?)) } } impl<'a> TryFrom<&'a str> for AString<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { if let Ok(atom) = AtomExt::try_from(value) { return Ok(AString::Atom(atom)); } Ok(AString::String(IString::try_from(value)?)) } } impl TryFrom for AString<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { // TODO(efficiency) if let Ok(atom) = AtomExt::try_from(value.clone()) { return Ok(AString::Atom(atom)); } Ok(AString::String(IString::try_from(value)?)) } } impl<'a> From> for AString<'a> { fn from(atom: Atom<'a>) -> Self { AString::Atom(AtomExt::from(atom)) } } impl<'a> From> for AString<'a> { fn from(atom: AtomExt<'a>) -> Self { AString::Atom(atom) } } impl<'a> From> for AString<'a> { fn from(value: Quoted<'a>) -> Self { AString::String(IString::Quoted(value)) } } impl<'a> From> for AString<'a> { fn from(value: Literal<'a>) -> Self { AString::String(IString::Literal(value)) } } impl AsRef<[u8]> for AString<'_> { fn as_ref(&self) -> &[u8] { match self { Self::Atom(atom_ext) => atom_ext.as_ref().as_bytes(), Self::String(istr) => istr.as_ref(), } } } /// A short alphanumeric identifier. /// /// Each client command is prefixed with an identifier (typically, e.g., A0001, A0002, etc.) called /// a "tag". /// /// # ABNF definition /// /// ```abnf /// tag = 1* /// ASTRING-CHAR = ATOM-CHAR / resp-specials /// ATOM-CHAR = /// CHAR = %x01-7F /// ; any 7-bit US-ASCII character, excluding NUL /// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials /// SP = %x20 /// CTL = %x00-1F / %x7F /// ; controls /// list-wildcards = "%" / "*" /// quoted-specials = DQUOTE / "\" /// DQUOTE = %x22 /// ; " (Double Quote) /// resp-specials = "]" /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(PartialEq, Eq, Hash, Clone, ToStatic)] pub struct Tag<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for Tag<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "Tag({:?})", self.0) } } impl<'a> Tag<'a> { pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if value.is_empty() { return Err(ValidationError::new(ValidationErrorKind::Empty)); } if let Some(at) = value .iter() .position(|b| !is_astring_char(*b) || *b == b'+') { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } pub fn inner(&self) -> &str { self.0.as_ref() } /// Constructs a tag without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: C) -> Self where C: Into>, { let inner = inner.into(); #[cfg(debug_assertions)] Self::validate(inner.as_bytes()).unwrap(); Self(inner) } } impl<'a> TryFrom<&'a [u8]> for Tag<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { Self::validate(value)?; // Safety: `unwrap` can't fail due to `validate`. Ok(Self(Cow::Borrowed(from_utf8(value).unwrap()))) } } impl TryFrom> for Tag<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { Self::validate(&value)?; // Safety: `unwrap` can't fail due to `validate`. Ok(Self(Cow::Owned(String::from_utf8(value).unwrap()))) } } impl<'a> TryFrom<&'a str> for Tag<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Self::validate(value)?; Ok(Self(Cow::Borrowed(value))) } } impl TryFrom for Tag<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Self::validate(&value)?; Ok(Self(Cow::Owned(value))) } } impl AsRef for Tag<'_> { fn as_ref(&self) -> &str { self.0.as_ref() } } #[cfg(feature = "tag_generator")] #[cfg_attr(docsrs, doc(cfg(feature = "tag_generator")))] #[derive(Debug)] pub struct TagGenerator { global: usize, counter: u64, } #[cfg(feature = "tag_generator")] impl TagGenerator { /// Generate an instance of a `TagGenerator` /// /// Returns a `TagGenerator` generating tags with a unique prefix. #[allow(clippy::new_without_default)] pub fn new() -> TagGenerator { // There is no synchronization required and we only care about each thread seeing a unique value. let global = GLOBAL_TAG_GENERATOR_COUNT.fetch_add(1, Ordering::Relaxed); let counter = 0; TagGenerator { global, counter } } /// Generate a unique `Tag` /// /// The tag has the form `..`, and is guaranteed to be unique and not /// guessable ("forward-secure"). /// /// Rational: `Instance` and `Counter` improve IMAP trace readability. /// The non-guessable `Random` hampers protocol-confusion attacks (to a limiting extend). pub fn generate(&mut self) -> Tag<'static> { #[cfg(not(debug_assertions))] let inner = { let token = Alphanumeric.sample_string(&mut rand::thread_rng(), 8); format!("{}.{}.{token}", self.global, self.counter) }; // Minimize randomness lending the library for security analysis. #[cfg(debug_assertions)] let inner = format!("{}.{}", self.global, self.counter); let tag = Tag::unvalidated(inner); self.counter = self.counter.wrapping_add(1); tag } } /// A human-readable text string used in some server responses. /// /// # Example /// /// ```imap /// S: * OK IMAP4rev1 server ready /// // ^^^^^^^^^^^^^^^^^^^^^^ /// // | /// // `Text` /// ``` /// /// # ABNF definition /// /// ```abnf /// text = 1*TEXT-CHAR /// TEXT-CHAR = /// CHAR = %x01-7F ; any 7-bit US-ASCII character, excluding NUL /// CR = %x0D ; carriage return /// LF = %x0A ; linefeed /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(PartialEq, Eq, Hash, Clone, ToStatic)] pub struct Text<'a>(pub(crate) Cow<'a, str>); // We want a slightly more dense `Debug` implementation. impl Debug for Text<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "Text({:?})", self.0) } } impl Display for Text<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "{}", self.0.as_ref()) } } impl<'a> Text<'a> { pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if value.is_empty() { return Err(ValidationError::new(ValidationErrorKind::Empty)); } if let Some(at) = value.iter().position(|b| !is_text_char(*b)) { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } pub fn inner(&self) -> &str { self.0.as_ref() } pub fn into_inner(self) -> Cow<'a, str> { self.0 } /// Constructs a text without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: C) -> Self where C: Into>, { let inner = inner.into(); #[cfg(debug_assertions)] Self::validate(inner.as_bytes()).unwrap(); Self(inner) } } impl<'a> TryFrom<&'a [u8]> for Text<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { Self::validate(value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Self(Cow::Borrowed(from_utf8(value).unwrap()))) } } impl TryFrom> for Text<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { Self::validate(&value)?; // Safety: `unwrap` can't panic due to `validate`. Ok(Self(Cow::Owned(String::from_utf8(value).unwrap()))) } } impl<'a> TryFrom<&'a str> for Text<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Self::validate(value)?; Ok(Self(Cow::Borrowed(value))) } } impl TryFrom for Text<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Self::validate(&value)?; Ok(Self(Cow::Owned(value))) } } impl AsRef for Text<'_> { fn as_ref(&self) -> &str { self.0.as_ref() } } /// A quoted char. /// /// # ABNF definition /// /// ```abnf /// QUOTED-CHAR = / "\" quoted-specials /// TEXT-CHAR = /// CHAR = %x01-7F ; any 7-bit US-ASCII character, excluding NUL /// CR = %x0D ; carriage return /// LF = %x0A ; linefeed /// quoted-specials = DQUOTE / "\" /// DQUOTE = %x22 ; " (Double Quote) /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "char"))] #[derive(Copy, Debug, PartialEq, Eq, Hash, Clone, ToStatic)] pub struct QuotedChar(char); impl QuotedChar { pub fn validate(input: char) -> Result<(), ValidationError> { if input.is_ascii() && (is_any_text_char_except_quoted_specials(input as u8) || input == '\\' || input == '"') { Ok(()) } else { Err(ValidationError::new(ValidationErrorKind::Invalid)) } } pub fn inner(&self) -> char { self.0 } /// Constructs a quoted char without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: char) -> Self { #[cfg(debug_assertions)] Self::validate(inner).unwrap(); Self(inner) } } impl TryFrom for QuotedChar { type Error = ValidationError; fn try_from(value: char) -> Result { Self::validate(value)?; Ok(QuotedChar(value)) } } /// A charset. /// /// # ABNF definition /// /// Note: IMAP is not very clear on what constitutes a charset string. We try to figure it out by /// looking at the `search` rule. (See [#266](https://github.com/duesee/imap-codec/issues/266).) /// /// ```abnf /// search = "SEARCH" [SP "CHARSET" SP astring] 1*(SP search-key) /// ; ^^^^^^^ /// ; | /// ; `Charset` /// ; CHARSET argument to MUST be registered with IANA /// ``` /// /// So, it seems that it should be an `AString`. However the IMAP standard also points to ... /// ```abnf /// mime-charset = 1*mime-charset-chars /// mime-charset-chars = ALPHA / DIGIT / /// "!" / "#" / "$" / "%" / "&" / /// "'" / "+" / "-" / "^" / "_" / /// "`" / "{" / "}" / "~" /// ALPHA = "A".."Z" ; Case insensitive ASCII Letter /// DIGIT = "0".."9" ; Numeric digit /// ``` #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Charset<'a> { Atom(Atom<'a>), Quoted(Quoted<'a>), } impl<'a> From> for Charset<'a> { fn from(value: Atom<'a>) -> Self { Self::Atom(value) } } impl<'a> From> for Charset<'a> { fn from(value: Quoted<'a>) -> Self { Self::Quoted(value) } } impl<'a> TryFrom<&'a [u8]> for Charset<'a> { type Error = ValidationError; fn try_from(value: &'a [u8]) -> Result { if let Ok(atom) = Atom::try_from(value) { return Ok(Self::Atom(atom)); } Ok(Self::Quoted(Quoted::try_from(value)?)) } } impl TryFrom> for Charset<'_> { type Error = ValidationError; fn try_from(value: Vec) -> Result { // TODO(efficiency) if let Ok(atom) = Atom::try_from(value.clone()) { return Ok(Self::Atom(atom)); } Ok(Self::Quoted(Quoted::try_from(value)?)) } } impl<'a> TryFrom<&'a str> for Charset<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { if let Ok(atom) = Atom::try_from(value) { return Ok(Self::Atom(atom)); } Ok(Self::Quoted(Quoted::try_from(value)?)) } } impl TryFrom for Charset<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { // TODO(efficiency) if let Ok(atom) = Atom::try_from(value.clone()) { return Ok(Self::Atom(atom)); } Ok(Self::Quoted(Quoted::try_from(value)?)) } } impl AsRef for Charset<'_> { fn as_ref(&self) -> &str { match self { Self::Atom(atom) => atom.as_ref(), Self::Quoted(quoted) => quoted.as_ref(), } } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub enum NString8<'a> { NString(NString<'a>), Literal8(Literal8<'a>), } /// A [`Vec`] containing >= N elements. /// /// Some messages in IMAP require a list of *at least N* elements. /// We encode these situations with a specific vector type to not produce invalid messages. /// /// Notes: /// /// * `Vec` must not be used. Please use the standard [`Vec`] instead. /// * `Vec` must not be used. Please use the alias [`Vec1`] instead. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "Vec"))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct VecN(pub(crate) Vec); impl Debug for VecN where T: Debug, { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { self.0.fmt(f)?; match N { 0 => write!(f, "*"), 1 => write!(f, "+"), _ => write!(f, "{{{N},}}"), } } } impl VecN { pub fn validate(value: &[T]) -> Result<(), ValidationError> { if value.len() < N { return Err(ValidationError::new(ValidationErrorKind::NotEnough { min: N, })); } Ok(()) } /// Constructs a non-empty vector without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: Vec) -> Self { #[cfg(debug_assertions)] Self::validate(&inner).unwrap(); Self(inner) } pub fn into_inner(self) -> Vec { self.0 } } impl From<[T; N]> for VecN { fn from(value: [T; N]) -> Self { Self(Vec::from(value)) } } impl TryFrom> for VecN { type Error = ValidationError; fn try_from(inner: Vec) -> Result { Self::validate(&inner)?; Ok(Self(inner)) } } impl IntoIterator for VecN { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl AsRef<[T]> for VecN { fn as_ref(&self) -> &[T] { &self.0 } } /// A [`Vec`] containing >= 1 elements, i.e., a non-empty vector. /// /// The `Debug` implementation equals the standard [`Vec`] with an attached `+` at the end. pub type Vec1 = VecN; impl From for Vec1 { fn from(value: T) -> Self { VecN(vec![value]) } } /// A [`Vec`] containing >= 2 elements. /// /// The `Debug` implementation equals the standard [`Vec`] with an attached `{2,}` at the end. pub type Vec2 = VecN; impl From<(T, T)> for Vec2 { fn from((v1, v2): (T, T)) -> Self { VecN(vec![v1, v2]) } } #[cfg(test)] mod tests { use std::str::from_utf8; #[cfg(feature = "tag_generator")] use std::{collections::BTreeSet, thread, time::Duration}; #[cfg(feature = "tag_generator")] use rand::random; use super::*; #[test] fn test_conversion_atom() { #[allow(clippy::type_complexity)] let tests: Vec<( &[u8], (Result, Result), )> = vec![ ( b"A", ( Ok(Atom(Cow::Borrowed("A"))), Ok(Atom(Cow::Owned("A".into()))), ), ), ( b"ABC", ( Ok(Atom(Cow::Borrowed("ABC"))), Ok(Atom(Cow::Owned("ABC".into()))), ), ), ( b" A", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 0, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 0, })), ), ), ( b"A ", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 1, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 1, })), ), ), ( b"", ( Err(ValidationError::new(ValidationErrorKind::Empty)), Err(ValidationError::new(ValidationErrorKind::Empty)), ), ), ( b"A\x00", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 1, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 1, })), ), ), ( b"A\x00", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 1, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 1, })), ), ), ]; for (test, (expected, expected_owned)) in tests.into_iter() { let got = Atom::try_from(test); assert_eq!(expected, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } let got = Atom::try_from(test.to_owned()); assert_eq!(expected_owned, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } if let Ok(test_str) = from_utf8(test) { let got = Atom::try_from(test_str); assert_eq!(expected, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } let got = Atom::try_from(test_str.to_owned()); assert_eq!(expected_owned, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } } } } #[test] fn test_conversion_atom_ext() { #[allow(clippy::type_complexity)] let tests: Vec<( &[u8], ( Result, Result, ), )> = vec![ ( b"A", ( Ok(AtomExt(Cow::Borrowed("A"))), Ok(AtomExt(Cow::Owned("A".into()))), ), ), ( b"ABC", ( Ok(AtomExt(Cow::Borrowed("ABC"))), Ok(AtomExt(Cow::Owned("ABC".into()))), ), ), ( b"!partition/sda4", ( Ok(AtomExt(Cow::Borrowed("!partition/sda4"))), Ok(AtomExt(Cow::Owned("!partition/sda4".into()))), ), ), ( b" A", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 0, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 0, })), ), ), ( b"A ", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 1, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: b' ', at: 1, })), ), ), ( b"", ( Err(ValidationError::new(ValidationErrorKind::Empty)), Err(ValidationError::new(ValidationErrorKind::Empty)), ), ), ( b"A\x00", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 1, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 1, })), ), ), ( b"\x00", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 0, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0x00, at: 0, })), ), ), ]; for (test, (expected, expected_owned)) in tests.into_iter() { let got = AtomExt::try_from(test); assert_eq!(expected, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } let got = AtomExt::try_from(test.to_owned()); assert_eq!(expected_owned, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } if let Ok(test_str) = from_utf8(test) { let got = AtomExt::try_from(test_str); assert_eq!(expected, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } let got = AtomExt::try_from(test_str.to_owned()); assert_eq!(expected_owned, got); if let Ok(got) = got { assert_eq!(got.as_ref().as_bytes(), test); } } } } #[test] fn test_conversion_astring() { #[allow(clippy::type_complexity)] let tests: Vec<( &[u8], ( Result, Result, ), )> = vec![ ( b"A", ( Ok(AString::Atom(AtomExt(Cow::Borrowed("A")))), Ok(AString::Atom(AtomExt(Cow::Owned("A".into())))), ), ), ( b"ABC", ( Ok(AString::Atom(AtomExt(Cow::Borrowed("ABC")))), Ok(AString::Atom(AtomExt(Cow::Owned("ABC".into())))), ), ), ( b"", ( Ok(AString::String(IString::Quoted(Quoted(Cow::Borrowed(""))))), Ok(AString::String(IString::Quoted(Quoted(Cow::Owned( "".to_owned(), ))))), ), ), ( b" A", ( Ok(AString::String(IString::Quoted(Quoted(Cow::Borrowed( " A", ))))), Ok(AString::String(IString::Quoted(Quoted(Cow::Owned( " A".to_owned(), ))))), ), ), ( b"A ", ( Ok(AString::String(IString::Quoted(Quoted(Cow::Borrowed( "A ", ))))), Ok(AString::String(IString::Quoted(Quoted(Cow::Owned( "A ".to_owned(), ))))), ), ), ( b"\"", ( Ok(AString::String(IString::Quoted(Quoted(Cow::Borrowed( "\"", ))))), Ok(AString::String(IString::Quoted(Quoted(Cow::Owned( "\"".to_owned(), ))))), ), ), ( b"\\\"", ( Ok(AString::String(IString::Quoted(Quoted(Cow::Borrowed( "\\\"", ))))), Ok(AString::String(IString::Quoted(Quoted(Cow::Owned( "\\\"".to_owned(), ))))), ), ), ( b"A\x00", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0, at: 1, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0, at: 1, })), ), ), ( b"\x00", ( Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0, at: 0, })), Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: 0, at: 0, })), ), ), ]; for (test, (expected, expected_owned)) in tests.into_iter() { let got = AString::try_from(test); assert_eq!(expected, got); if let Ok(got) = got { assert_eq!(got.as_ref(), test); } let got = AString::try_from(test.to_owned()); assert_eq!(expected_owned, got); if let Ok(got) = got { assert_eq!(got.as_ref(), test); } if let Ok(test_str) = from_utf8(test) { let got = AString::try_from(test_str); assert_eq!(expected, got); if let Ok(got) = got { assert_eq!(got.as_ref(), test); } let got = AString::try_from(test_str.to_owned()); assert_eq!(expected_owned, got); if let Ok(got) = got { assert_eq!(got.as_ref(), test); } } } } #[test] fn test_conversion_istring() { assert_eq!( IString::try_from("AAA").unwrap(), IString::Quoted("AAA".try_into().unwrap()) ); assert_eq!( IString::try_from("\"AAA").unwrap(), IString::Quoted("\"AAA".try_into().unwrap()) ); assert_ne!( IString::try_from("\"AAA").unwrap(), IString::Quoted("\\\"AAA".try_into().unwrap()) ); } #[test] fn test_nstring() { assert_eq!(NString::<'static>::default(), NString(None)); assert_eq!(NString::<'static>::NIL, NString(None)); assert_eq!(NString::<'static>::NIL.into_option(), None); } #[test] fn test_vec_n() { // Note: Don't use `VecN`, it's only a sanity test here. assert!(VecN::::try_from(vec![]).is_ok()); assert!(VecN::::try_from(vec![1]).is_ok()); assert!(VecN::::try_from(vec![1, 2]).is_ok()); assert!(VecN::::try_from(vec![]).is_err()); assert!(VecN::::try_from(vec![1]).is_ok()); assert!(VecN::::try_from(vec![1, 2]).is_ok()); assert!(Vec1::::try_from(vec![]).is_err()); assert!(Vec1::::try_from(vec![1]).is_ok()); assert!(Vec1::::try_from(vec![1, 2]).is_ok()); assert!(VecN::::try_from(vec![]).is_err()); assert!(VecN::::try_from(vec![1]).is_err()); assert!(VecN::::try_from(vec![1, 2]).is_ok()); } #[cfg(feature = "serde")] #[test] fn test_deserialization_text() { let valid_input = r#""Hello, world!""#; let invalid_input = r#""Hello,\rworld!""#; let text = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(text, Text(Cow::Borrowed("Hello, world!"))); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x0d' at index 6" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_atom() { let valid_input = r#""OneWord""#; let invalid_input = r#""Two Words""#; let atom = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(atom, Atom(Cow::Borrowed("OneWord"))); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x20' at index 3" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_extended_atom() { let valid_input = r#""OneWord""#; let invalid_input = r#""Two Words""#; let atom_ext = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(atom_ext, AtomExt(Cow::Borrowed("OneWord"))); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x20' at index 3" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_literal() { let valid_input = r#"{ "data": [ 1, 2, 3 ], "mode": "Sync" }"#; let invalid_input = r#"{ "data": [ 0, 1, 2, 3 ], "mode": "Sync" }"#; let literal = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!( literal, Literal { data: Cow::Borrowed(b"\x01\x02\x03"), mode: LiteralMode::Sync } ); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x00' at index 0 at line 1 column 24" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_quoted() { let valid_input = r#""Hello, world!""#; let invalid_input = r#""Hello,\rworld!""#; let quoted = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(quoted, Quoted(Cow::Borrowed("Hello, world!"))); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x0d' at index 6" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_tag() { let valid_input = r#""A0001""#; let invalid_input = r#""A+0001""#; let tag = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(tag, Tag(Cow::Borrowed("A0001"))); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x2b' at index 1" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_quoted_char() { let valid_input = r#""A""#; let invalid_input = r#""\r""#; let quoted_char = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(quoted_char, QuotedChar('A')); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!(err.to_string(), r"Validation failed: Invalid value"); } #[cfg(feature = "serde")] #[test] fn test_deserialization_vec_n() { let valid_input = r#"[1, 2, 3]"#; let invalid_input = r#"[1, 2]"#; let vec_n = serde_json::from_str::>(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(vec_n, VecN(vec![1, 2, 3])); let err = serde_json::from_str::>(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Must have at least 3 elements" ); } #[cfg(feature = "tag_generator")] #[test] fn test_generator_generator() { const THREADS: usize = 1000; const INVOCATIONS: usize = 5; thread::scope(|s| { let mut handles = Vec::with_capacity(THREADS); for _ in 1..=THREADS { let handle = s.spawn(move || { let mut tags = Vec::with_capacity(INVOCATIONS); let mut generator = TagGenerator::new(); thread::sleep(Duration::from_millis(random::() as u64)); for _ in 1..=INVOCATIONS { tags.push(generator.generate()); } tags }); handles.push(handle); } let mut set = BTreeSet::new(); for handle in handles { let tags = handle.join().unwrap(); for tag in tags { // Make sure insertion worked, i.e., no duplicate was found. // Note: `Tag` doesn't implement `Ord` so we insert a `String`. assert!(set.insert(tag.as_ref().to_owned()), "duplicate tag found"); } } }); } } duesee-imap-codec-0d00966/imap-types/src/datetime.rs000066400000000000000000000232011507724125200222610ustar00rootroot00000000000000//! Date and time-related types. use std::fmt::{Debug, Formatter}; use bounded_static::{IntoBoundedStatic, ToBoundedStatic}; use chrono::{Datelike, FixedOffset}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::datetime::error::{DateTimeError, NaiveDateError}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "chrono::DateTime"))] #[derive(Clone, Eq, PartialEq, Hash)] pub struct DateTime(chrono::DateTime); impl DateTime { pub fn validate(value: &chrono::DateTime) -> Result<(), DateTimeError> { // Only a subset of `chrono`s `DateTime` is valid in IMAP. if !(0..=9999).contains(&value.year()) { return Err(DateTimeError::YearOutOfRange { got: value.year() }); } if value.timestamp_subsec_nanos() != 0 { return Err(DateTimeError::UnalignedNanoSeconds { got: value.timestamp_subsec_nanos(), }); } if value.offset().local_minus_utc() % 60 != 0 { return Err(DateTimeError::UnalignedOffset { got: value.offset().local_minus_utc() % 60, }); } Ok(()) } /// Constructs a date time without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `value` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(value: chrono::DateTime) -> Self { #[cfg(debug_assertions)] Self::validate(&value).unwrap(); Self(value) } } impl TryFrom> for DateTime { type Error = DateTimeError; fn try_from(value: chrono::DateTime) -> Result { Self::validate(&value)?; Ok(Self(value)) } } impl Debug for DateTime { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Debug::fmt(&self.0, f) } } impl AsRef> for DateTime { fn as_ref(&self) -> &chrono::DateTime { &self.0 } } impl IntoBoundedStatic for DateTime { type Static = Self; fn into_static(self) -> Self::Static { self } } impl ToBoundedStatic for DateTime { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "chrono::NaiveDate"))] #[derive(Clone, Eq, PartialEq, Hash)] pub struct NaiveDate(chrono::NaiveDate); impl NaiveDate { pub fn validate(value: &chrono::NaiveDate) -> Result<(), NaiveDateError> { // Only a subset of `chrono`s `NaiveDate` is valid in IMAP. if !(0..=9999).contains(&value.year()) { return Err(NaiveDateError::YearOutOfRange { got: value.year() }); } Ok(()) } /// Constructs a naive date without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `value` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(value: chrono::NaiveDate) -> Self { #[cfg(debug_assertions)] Self::validate(&value).unwrap(); Self(value) } } impl TryFrom for NaiveDate { type Error = NaiveDateError; fn try_from(value: chrono::NaiveDate) -> Result { Self::validate(&value)?; Ok(Self(value)) } } impl Debug for NaiveDate { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { Debug::fmt(&self.0, f) } } impl AsRef for NaiveDate { fn as_ref(&self) -> &chrono::NaiveDate { &self.0 } } impl IntoBoundedStatic for NaiveDate { type Static = Self; fn into_static(self) -> Self::Static { self } } impl ToBoundedStatic for NaiveDate { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } /// Error-related types. pub mod error { use thiserror::Error; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum DateTimeError { #[error("expected `0 <= year <= 9999`, got {got}")] YearOutOfRange { got: i32 }, #[error("expected `nanos == 0`, got {got}")] UnalignedNanoSeconds { got: u32 }, #[error("expected `offset % 60 == 0`, got {got}")] UnalignedOffset { got: i32 }, } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum NaiveDateError { #[error("expected `0 <= year <= 9999`, got {got}")] YearOutOfRange { got: i32 }, } } #[cfg(test)] mod tests { use chrono::{TimeZone, Timelike}; use super::*; #[test] fn test_conversion_date_time_failing() { let tests = [ ( DateTime::try_from( chrono::FixedOffset::east_opt(3600) .unwrap() .from_local_datetime(&chrono::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt(-1, 2, 1).unwrap(), chrono::NaiveTime::from_hms_opt(12, 34, 56).unwrap(), )) .unwrap(), ), DateTimeError::YearOutOfRange { got: -1 }, ), ( DateTime::try_from( chrono::FixedOffset::east_opt(3600) .unwrap() .from_local_datetime(&chrono::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt(10000, 2, 1).unwrap(), chrono::NaiveTime::from_hms_opt(12, 34, 56).unwrap(), )) .unwrap(), ), DateTimeError::YearOutOfRange { got: 10000 }, ), ( DateTime::try_from( chrono::FixedOffset::east_opt(1) .unwrap() .from_local_datetime(&chrono::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt(0, 2, 1).unwrap(), chrono::NaiveTime::from_hms_opt(12, 34, 56).unwrap(), )) .unwrap(), ), DateTimeError::UnalignedOffset { got: 1 }, ), ( DateTime::try_from( chrono::FixedOffset::east_opt(59) .unwrap() .from_local_datetime(&chrono::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt(9999, 2, 1).unwrap(), chrono::NaiveTime::from_hms_opt(12, 34, 56).unwrap(), )) .unwrap(), ), DateTimeError::UnalignedOffset { got: 59 }, ), ( DateTime::try_from( chrono::FixedOffset::east_opt(60) .unwrap() .from_local_datetime(&chrono::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt(0, 2, 1).unwrap(), chrono::NaiveTime::from_hms_opt(12, 34, 56).unwrap(), )) .unwrap() .with_nanosecond(1) .unwrap(), ), DateTimeError::UnalignedNanoSeconds { got: 1 }, ), ]; for (got, expected) in tests { println!("{}", got.clone().unwrap_err()); println!("{:?}", got.clone().unwrap_err()); assert_eq!(expected, got.unwrap_err()); } } #[cfg(feature = "serde")] #[test] fn test_deserialization_date_time() { let valid_input = r#""2015-05-15T11:22:33+01:00""#; let invalid_input = r#""+12015-05-15T11:22:33+01:00""#; let date_time = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!( date_time, DateTime( chrono::FixedOffset::east_opt(3600) .unwrap() .with_ymd_and_hms(2015, 5, 15, 11, 22, 33) .unwrap(), ), ); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!(err.to_string(), r"expected `0 <= year <= 9999`, got 12015"); } #[cfg(feature = "serde")] #[test] fn test_deserialization_naive_date() { let valid_input = r#""2015-05-15""#; let invalid_input = r#""+12015-05-15""#; let naive_date = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!( naive_date, NaiveDate(chrono::NaiveDate::from_ymd_opt(2015, 5, 15).unwrap()), ); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!(err.to_string(), r"expected `0 <= year <= 9999`, got 12015"); } } duesee-imap-codec-0d00966/imap-types/src/envelope.rs000066400000000000000000000030561507724125200223100ustar00rootroot00000000000000//! Envelope-related types. #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::core::NString; #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Envelope<'a> { pub date: NString<'a>, pub subject: NString<'a>, pub from: Vec>, pub sender: Vec>, pub reply_to: Vec>, pub to: Vec>, pub cc: Vec>, pub bcc: Vec>, pub in_reply_to: NString<'a>, pub message_id: NString<'a>, } /// An address structure describes an electronic mail address. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] /// TODO(misuse): /// /// Here are many invariants ... /// /// mailbox: /// NIL indicates end of [RFC-2822] group; /// if non-NIL and host is NIL, holds [RFC-2822] group name. /// Otherwise, holds [RFC-2822] local-part after removing [RFC-2822] quoting /// /// host: /// NIL indicates [RFC-2822] group syntax. /// Otherwise, holds [RFC-2822] domain name pub struct Address<'a> { /// Personal name pub name: NString<'a>, /// At-domain-list (source route) pub adl: NString<'a>, /// Mailbox name pub mailbox: NString<'a>, /// Host name pub host: NString<'a>, } duesee-imap-codec-0d00966/imap-types/src/error.rs000066400000000000000000000017251507724125200216250ustar00rootroot00000000000000//! Error-related types. use std::fmt::{Display, Formatter}; use thiserror::Error; /// A validation error. /// /// This error can be returned during validation of a value, e.g., a tag, atom, etc. #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub struct ValidationError { kind: ValidationErrorKind, } impl Display for ValidationError { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "Validation failed: {}", self.kind) } } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub(crate) enum ValidationErrorKind { #[error("Must not be empty")] Empty, #[error("Must have at least {min} elements")] NotEnough { min: usize }, #[error("Invalid value")] Invalid, #[error("Invalid byte b'\\x{byte:02x}' at index {at}")] InvalidByteAt { byte: u8, at: usize }, } impl ValidationError { pub(crate) fn new(kind: ValidationErrorKind) -> Self { Self { kind } } } duesee-imap-codec-0d00966/imap-types/src/extensions.rs000066400000000000000000000006161507724125200226710ustar00rootroot00000000000000//! IMAP extensions. pub mod binary; pub mod compress; #[cfg(feature = "ext_condstore_qresync")] pub mod condstore_qresync; pub mod enable; pub mod idle; #[cfg(feature = "ext_metadata")] pub mod metadata; pub mod r#move; #[cfg(feature = "ext_namespace")] pub mod namespace; pub mod quota; pub mod sort; pub mod thread; pub mod uidplus; pub mod unselect; #[cfg(feature = "ext_utf8")] pub mod utf8; duesee-imap-codec-0d00966/imap-types/src/extensions/000077500000000000000000000000001507724125200223205ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/src/extensions/binary.rs000066400000000000000000000037711507724125200241620ustar00rootroot00000000000000//! IMAP4 Binary Content Extension use std::{ borrow::Cow, fmt::{Debug, Formatter}, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::core::{Literal, LiteralMode}; /// Either a [`Literal`] or [`Literal8`]. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Clone, Debug, PartialEq, Eq, Hash, ToStatic)] pub enum LiteralOrLiteral8<'a> { Literal(Literal<'a>), Literal8(Literal8<'a>), } /// String that might contain NULs. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Literal8<'a> { pub data: Cow<'a, [u8]>, /// Specifies whether this is a synchronizing or non-synchronizing literal. /// /// `true` (default) denotes a synchronizing literal, e.g., `~{3}\r\nfoo`. /// `false` denotes a non-synchronizing literal, e.g., `~{3+}\r\nfoo`. /// /// Note: In the special case that a server advertised a `LITERAL-` capability, AND the literal /// has more than 4096 bytes a non-synchronizing literal must still be treated as synchronizing. pub mode: LiteralMode, } // We want a more readable `Debug` implementation. impl Debug for Literal8<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { struct BStr<'a>(&'a Cow<'a, [u8]>); impl Debug for BStr<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "b\"{}\"", crate::utils::escape_byte_string(self.0.as_ref()) ) } } f.debug_struct("Literal8") .field("data", &BStr(&self.data)) .field("mode", &self.mode) .finish() } } duesee-imap-codec-0d00966/imap-types/src/extensions/compress.rs000066400000000000000000000103061507724125200245210ustar00rootroot00000000000000//! The IMAP COMPRESS Extension //! //! This extension defines a new type ... //! //! * [`CompressionAlgorithm`] //! //! ... and extends ... //! //! * the [`Capability`](crate::response::Capability) enum with a new variant [`Capability::Compress`](crate::response::Capability#variant.Compress), //! * the [`Command`](crate::command::Command) enum with a new variant [`Command::Compress`](crate::command::Command#variant.Compress), and //! * the [`Code`](crate::response::Code) enum with a new variant [`Code::CompressionActive`](crate::response::Code#variant.CompressionActive). use std::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ command::CommandBody, core::Atom, error::{ValidationError, ValidationErrorKind}, }; impl CommandBody<'_> { ///
/// This extension must only be used when the server advertised support for it sending the COMPRESS* capability. ///
pub fn compress(algorithm: CompressionAlgorithm) -> Self { CommandBody::Compress { algorithm } } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[non_exhaustive] pub enum CompressionAlgorithm { Deflate, } impl Display for CompressionAlgorithm { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(match self { Self::Deflate => "DEFLATE", }) } } impl TryFrom<&str> for CompressionAlgorithm { type Error = ValidationError; fn try_from(value: &str) -> Result { match value.to_ascii_lowercase().as_ref() { "deflate" => Ok(Self::Deflate), _ => Err(ValidationError::new(ValidationErrorKind::Invalid)), } } } impl TryFrom<&[u8]> for CompressionAlgorithm { type Error = ValidationError; fn try_from(value: &[u8]) -> Result { match value.to_ascii_lowercase().as_slice() { b"deflate" => Ok(Self::Deflate), _ => Err(ValidationError::new(ValidationErrorKind::Invalid)), } } } impl<'a> TryFrom> for CompressionAlgorithm { type Error = ValidationError; fn try_from(atom: Atom<'a>) -> Result { match atom.as_ref().to_ascii_lowercase().as_ref() { "deflate" => Ok(Self::Deflate), _ => Err(ValidationError::new(ValidationErrorKind::Invalid)), } } } impl AsRef for CompressionAlgorithm { fn as_ref(&self) -> &str { match self { CompressionAlgorithm::Deflate => "DEFLATE", } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_conversion() { let tests = [(CompressionAlgorithm::Deflate, "DEFLATE")]; for (object, string) in tests { // Create from `&[u8]`. let got = CompressionAlgorithm::try_from(string.as_bytes()).unwrap(); assert_eq!(object, got); // Create from `&str`. let got = CompressionAlgorithm::try_from(string).unwrap(); assert_eq!(object, got); // Create from `Atom`. let got = CompressionAlgorithm::try_from(Atom::try_from(string).unwrap()).unwrap(); assert_eq!(object, got); // AsRef let encoded = object.as_ref(); assert_eq!(encoded, string); } } #[test] fn test_conversion_failing() { let tests = [ "", "D", "DE", "DEF", "DEFL", "DEFLA", "DEFLAT", "DEFLATX", "DEFLATEX", "XDEFLATE", ]; for string in tests { // Create from `&[u8]`. assert!(CompressionAlgorithm::try_from(string.as_bytes()).is_err()); // Create from `&str`. assert!(CompressionAlgorithm::try_from(string).is_err()); if !string.is_empty() { // Create from `Atom`. assert!(CompressionAlgorithm::try_from(Atom::try_from(string).unwrap()).is_err()); } } } } duesee-imap-codec-0d00966/imap-types/src/extensions/condstore_qresync.rs000066400000000000000000000053711507724125200264400ustar00rootroot00000000000000use std::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{core::Atom, error::ValidationError}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum AttributeFlag<'a> { Answered, Deleted, Draft, Flagged, Seen, Extension(AttributeFlagExtension<'a>), Keyword(Atom<'a>), } impl<'a> AttributeFlag<'a> { pub fn system(atom: Atom<'a>) -> Self { match atom.as_ref().to_ascii_lowercase().as_ref() { "answered" => Self::Answered, "flagged" => Self::Flagged, "deleted" => Self::Deleted, "seen" => Self::Seen, "draft" => Self::Draft, _ => Self::Extension(AttributeFlagExtension(atom)), } } pub fn keyword(atom: Atom<'a>) -> Self { Self::Keyword(atom) } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct AttributeFlagExtension<'a>(Atom<'a>); impl<'a> TryFrom<&'a str> for AttributeFlag<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Ok(if let Some(value) = value.strip_prefix("\\\\") { Self::system(Atom::try_from(value)?) } else { Self::keyword(Atom::try_from(value)?) }) } } impl Display for AttributeFlag<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { AttributeFlag::Answered => f.write_str("\\\\Answered"), AttributeFlag::Flagged => f.write_str("\\\\Flagged"), AttributeFlag::Deleted => f.write_str("\\\\Deleted"), AttributeFlag::Seen => f.write_str("\\\\Seen"), AttributeFlag::Draft => f.write_str("\\\\Draft"), AttributeFlag::Keyword(atom) => write!(f, "{atom}"), AttributeFlag::Extension(other) => write!(f, "\\\\{}", other.0), } } } #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum EntryTypeReq { Private, Shared, All, } #[cfg(feature = "ext_condstore_qresync")] impl Display for EntryTypeReq { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { EntryTypeReq::Private => write!(f, "priv"), EntryTypeReq::Shared => write!(f, "shared"), EntryTypeReq::All => write!(f, "all"), } } } duesee-imap-codec-0d00966/imap-types/src/extensions/enable.rs000066400000000000000000000106371507724125200241230ustar00rootroot00000000000000//! The IMAP ENABLE Extension //! //! This extension extends ... //! //! * the [Capability](crate::response::Capability) enum with a new variant [Capability::Enable](crate::response::Capability#variant.Enable), //! * the [CommandBody] enum with a new variant [CommandBody::Enable], and //! * the [Data](crate::response::Data) enum with a new variant [Data::Enabled](crate::response::Data#variant.Enabled). use std::fmt::{Display, Formatter}; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "ext_utf8")] use crate::extensions::utf8::Utf8Kind; use crate::{ command::CommandBody, core::{Atom, Vec1}, error::ValidationError, }; impl<'a> CommandBody<'a> { ///
/// This extension must only be used when the server advertised support for it sending the ENABLE capability. ///
pub fn enable(capabilities: C) -> Result where C: TryInto>>, { Ok(CommandBody::Enable { capabilities: capabilities.try_into()?, }) } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[non_exhaustive] pub enum CapabilityEnable<'a> { #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg(feature = "ext_condstore_qresync")))] CondStore, #[cfg(feature = "ext_metadata")] /// Client can handle unsolicited server annotations and mailbox annotations. Metadata, #[cfg(feature = "ext_metadata")] /// Client can handle server annotations. MetadataServer, #[cfg(feature = "ext_utf8")] Utf8(Utf8Kind), Other(CapabilityEnableOther<'a>), } impl<'a> TryFrom<&'a str> for CapabilityEnable<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Ok(Self::from(Atom::try_from(value)?)) } } impl<'a> From> for CapabilityEnable<'a> { fn from(atom: Atom<'a>) -> Self { match atom.as_ref().to_ascii_lowercase().as_str() { #[cfg(feature = "ext_condstore_qresync")] "condstore" => Self::CondStore, #[cfg(feature = "ext_metadata")] "metadata" => Self::Metadata, #[cfg(feature = "ext_metadata")] "metadata-server" => Self::MetadataServer, #[cfg(feature = "ext_utf8")] "utf8=accept" => Self::Utf8(Utf8Kind::Accept), #[cfg(feature = "ext_utf8")] "utf8=only" => Self::Utf8(Utf8Kind::Only), _ => Self::Other(CapabilityEnableOther(atom)), } } } impl Display for CapabilityEnable<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { #[cfg(feature = "ext_condstore_qresync")] Self::CondStore => write!(f, "CONDSTORE"), #[cfg(feature = "ext_metadata")] Self::Metadata => write!(f, "METADATA"), #[cfg(feature = "ext_metadata")] Self::MetadataServer => write!(f, "METADATA-SERVER"), #[cfg(feature = "ext_utf8")] Self::Utf8(kind) => write!(f, "UTF8={kind}"), Self::Other(other) => write!(f, "{}", other.0), } } } /// An (unknown) capability. /// /// It's guaranteed that this type can't represent any capability from [`CapabilityEnable`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct CapabilityEnableOther<'a>(Atom<'a>); #[cfg(test)] mod tests { use super::*; #[test] fn test_conversion_capability_enable() { #[cfg(feature = "ext_utf8")] assert_eq!( CapabilityEnable::from(Atom::try_from("utf8=only").unwrap()), CapabilityEnable::Utf8(Utf8Kind::Only) ); #[cfg(feature = "ext_utf8")] assert_eq!( CapabilityEnable::from(Atom::try_from("utf8=accept").unwrap()), CapabilityEnable::Utf8(Utf8Kind::Accept) ); assert_eq!( CapabilityEnable::try_from("utf").unwrap(), CapabilityEnable::Other(CapabilityEnableOther(Atom::try_from("utf").unwrap())) ); assert_eq!( CapabilityEnable::try_from("xxxxx").unwrap(), CapabilityEnable::Other(CapabilityEnableOther(Atom::try_from("xxxxx").unwrap())) ); } } duesee-imap-codec-0d00966/imap-types/src/extensions/idle.rs000066400000000000000000000016561507724125200236130ustar00rootroot00000000000000//! IMAP4 IDLE command //! //! This extension adds a new method ... //! //! * [`CommandBody::idle()`](crate::command::CommandBody#method.idle) //! //! ... adds a new type ... //! //! * [`IdleDone`] //! //! ... and extends ... //! //! * [`CommandBody`](crate::command::CommandBody) enum with a new variant [`CommandBody::Idle`](crate::command::CommandBody#variant.Idle), and //! * [`Capability`](crate::response::Capability) enum with a new variant [`Capability::Idle`](crate::response::Capability#variant.Idle). #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Denotes the continuation data message "DONE\r\n" to end the IDLE command. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct IdleDone; duesee-imap-codec-0d00966/imap-types/src/extensions/metadata.rs000066400000000000000000000063061507724125200244530ustar00rootroot00000000000000#[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ core::{AString, NString8, Vec1}, error::ValidationError, }; #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub struct EntryValue<'a> { pub entry: Entry<'a>, pub value: NString8<'a>, } /// Slash-separated path to entry. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub struct Entry<'a>(AString<'a>); impl<'a> Entry<'a> { pub fn inner(&self) -> &AString<'a> { &self.0 } } impl<'a> TryFrom> for Entry<'a> { type Error = ValidationError; fn try_from(value: AString<'a>) -> Result { // TODO(#449): Currently, we do no validation. Let's gather more // practical experience before settling on a representation. Ok(Self(value)) } } impl AsRef<[u8]> for Entry<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub enum GetMetadataOption { /// Only return values that are less than or equal in octet size to the specified limit. /// /// If there are any entries with values larger than the MAXSIZE limit, the server MUST include /// the METADATA LONGENTRIES response code in the tagged OK response for the GETMETADATA command. MaxSize(u32), /// Extends the list of entry values returned by the server. /// /// For each entry name specified in the GETMETADATA command, the server returns the value of the /// specified entry name (if it exists), plus all entries below the entry name up to the specified DEPTH. Depth(Depth), } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub enum Depth { /// No entries below the specified entry are returned Null, /// Only entries immediately below the specified entry are returned One, /// All entries below the specified entry are returned Infinity, } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub enum MetadataCode { LongEntries(u32), MaxSize(u32), TooMany, NoPrivate, } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Clone, Debug, Eq, Hash, PartialEq, ToStatic)] pub enum MetadataResponse<'a> { WithValues(Vec1>), WithoutValues(Vec1>), } duesee-imap-codec-0d00966/imap-types/src/extensions/move.rs000066400000000000000000000020741507724125200236370ustar00rootroot00000000000000//! IMAP - MOVE Extension use crate::{ command::CommandBody, extensions::r#move::error::MoveError, mailbox::Mailbox, sequence::SequenceSet, }; impl<'a> CommandBody<'a> { ///
/// This extension must only be used when the server advertised support for it sending the MOVE capability. ///
pub fn r#move( sequence_set: S, mailbox: M, uid: bool, ) -> Result> where S: TryInto, M: TryInto>, { Ok(CommandBody::Move { sequence_set: sequence_set.try_into().map_err(MoveError::Sequence)?, mailbox: mailbox.try_into().map_err(MoveError::Mailbox)?, uid, }) } } /// Error-related types. pub mod error { use thiserror::Error; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum MoveError { #[error("Invalid sequence: {0}")] Sequence(S), #[error("Invalid mailbox: {0}")] Mailbox(M), } } duesee-imap-codec-0d00966/imap-types/src/extensions/namespace.rs000066400000000000000000000064221507724125200246260ustar00rootroot00000000000000//! The IMAP NAMESPACE Extension //! //! This extends ... //! //! * [`Capability`](crate::response::Capability) with a new variant: //! //! - [`Capability::Namespace`](crate::response::Capability::Namespace) //! //! * [`CommandBody`] with a new variant: //! //! - [`CommandBody::Namespace`] //! //! * [`Data`] with a new variant: //! //! - [`Data::Namespace`] #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ command::CommandBody, core::{IString, QuotedChar, Vec1}, extensions::namespace::error::NamespaceError, response::Data, }; impl<'a> CommandBody<'a> { ///
/// This extension must only be used when the server advertised support for it by sending the NAMESPACE capability. ///
pub fn namespace() -> Self { CommandBody::Namespace } } impl<'a> Data<'a> { #[allow(clippy::type_complexity)] pub fn namespace( personal: P, other: O, shared: S, ) -> Result> where P: TryInto>, O: TryInto>, S: TryInto>, { Ok(Self::Namespace { personal: personal.try_into().map_err(NamespaceError::Personal)?, other: other.try_into().map_err(NamespaceError::Other)?, shared: shared.try_into().map_err(NamespaceError::Shared)?, }) } } /// A list of `Namespace` definitions. /// /// Corresponds to the `Namespace` rule in the ABNF, which is either `NIL` /// or a parenthesized list of namespace descriptions. An empty `Vec` is treated as `NIL`. pub type Namespaces<'a> = Vec>; /// A single namespace's description, containing a prefix, delimiter, and optional extensions. /// /// Corresponds to the `( string SP (<"> QUOTED_CHAR <"> / nil) *(Namespace_Response_Extension) )` /// part of the ABNF. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Namespace<'a> { pub prefix: IString<'a>, pub delimiter: Option, /// Optional extension data for this namespace. pub extensions: Vec>, } impl<'a> Namespace<'a> { pub fn new(prefix: IString<'a>, delimiter: Option) -> Self { Self { prefix, delimiter, extensions: Vec::new(), } } } /// Extension data for a namespace response. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct NamespaceResponseExtension<'a> { pub key: IString<'a>, pub values: Vec1>, } /// Error-related types. pub mod error { use thiserror::Error; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum NamespaceError { #[error("Invalid personal namespace: {0}")] Personal(P), #[error("Invalid other namespace: {0}")] Other(O), #[error("Invalid shared namespace: {0}")] Shared(S), } } duesee-imap-codec-0d00966/imap-types/src/extensions/quota.rs000066400000000000000000000237051507724125200240260ustar00rootroot00000000000000//! The IMAP QUOTA Extension //! //! This extends ... //! //! * [`Capability`](crate::response::Capability) with new variants: //! //! - [`Capability::Quota`](crate::response::Capability::Quota) //! - [`Capability::QuotaRes`](crate::response::Capability::QuotaRes) //! - [`Capability::QuotaSet`](crate::response::Capability::QuotaSet) //! //! * [`CommandBod`y](crate::command::CommandBody) with new variants: //! //! - [`Command::GetQuota`](crate::command::CommandBody::GetQuota) //! - [`Command::GetQuotaRoot`](crate::command::CommandBody::GetQuotaRoot) //! - [`Command::SetQuota`](crate::command::CommandBody::SetQuota) //! //! * [`Data`] with new variants: //! //! - [`Data::Quota`] //! - [`Data::QuotaRoot`] //! //! * [`Code`](crate::response::Code) with a new variant: //! //! - [`Code::OverQuota`](crate::response::Code::OverQuota) //! //! * [`StatusDataItemName`](crate::status::StatusDataItemName) with new variants: //! //! - [`StatusDataItemName::Deleted`](crate::status::StatusDataItemName::Deleted) //! - [`StatusDataItemName::DeletedStorage`](crate::status::StatusDataItemName::DeletedStorage) //! //! * [`StatusDataItem`](crate::status::StatusDataItem) with new variants: //! //! - [`StatusDataItem::Deleted`](crate::status::StatusDataItem::Deleted) //! - [`StatusDataItem::DeletedStorage`](crate::status::StatusDataItem::DeletedStorage) use std::{ borrow::Cow, fmt::{Display, Formatter}, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ command::CommandBody, core::{AString, Atom, Vec1, impl_try_from}, extensions::quota::error::{QuotaError, QuotaRootError, SetQuotaError}, mailbox::Mailbox, response::Data, }; impl<'a> CommandBody<'a> { ///
/// This extension must only be used when the server advertised support for it sending the QUOTA capability. ///
pub fn get_quota
(root: A) -> Result where A: TryInto>, { Ok(CommandBody::GetQuota { root: root.try_into()?, }) } ///
/// This extension must only be used when the server advertised support for it sending the QUOTA capability. ///
pub fn get_quota_root(mailbox: M) -> Result where M: TryInto>, { Ok(CommandBody::GetQuotaRoot { mailbox: mailbox.try_into()?, }) } ///
/// This extension must only be used when the server advertised support for it sending the QUOTA capability. ///
pub fn set_quota(root: R, quotas: S) -> Result> where R: TryInto>, S: TryInto>>, { Ok(CommandBody::SetQuota { root: root.try_into().map_err(SetQuotaError::Root)?, quotas: quotas.try_into().map_err(SetQuotaError::QuotaSet)?, }) } } impl<'a> Data<'a> { pub fn quota(root: R, quotas: Q) -> Result> where R: TryInto>, Q: TryInto>>, { Ok(Self::Quota { root: root.try_into().map_err(QuotaError::Root)?, quotas: quotas.try_into().map_err(QuotaError::Quotas)?, }) } pub fn quota_root( mailbox: M, roots: R, ) -> Result> where M: TryInto>, R: TryInto>>, { Ok(Self::QuotaRoot { mailbox: mailbox.try_into().map_err(QuotaRootError::Mailbox)?, roots: roots.try_into().map_err(QuotaRootError::Roots)?, }) } } /// A resource type for use in IMAP's QUOTA extension. /// /// Supported resource names MUST be advertised as a capability by prepending the resource name with "QUOTA=RES-". #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Resource<'a> { /// The physical space estimate, in units of 1024 octets, of the mailboxes governed by the quota /// root. /// /// This MAY not be the same as the sum of the RFC822.SIZE of the messages. Some implementations /// MAY include metadata sizes for the messages and mailboxes, and other implementations MAY /// store messages in such a way that the physical space used is smaller, for example, due to /// use of compression. Additional messages might not increase the usage. Clients MUST NOT use /// the usage figure for anything other than informational purposes; for example, they MUST NOT /// refuse to APPEND a message if the limit less the usage is smaller than the RFC822.SIZE /// divided by 1024 octets of the message, but it MAY warn about such condition. The usage /// figure may change as a result of performing actions not associated with adding new messages /// to the mailbox, such as SEARCH, since this may increase the amount of metadata included in /// the calculations. /// /// When the server supports this resource type, it MUST also support the DELETED-STORAGE status /// data item. /// /// Support for this resource MUST be indicated by the server by advertising the /// "QUOTA=RES-STORAGE" capability. Storage, /// The number of messages stored within the mailboxes governed by the quota root. /// /// This MUST be an exact number; however, clients MUST NOT assume that a change in the usage /// indicates a change in the number of messages available, since the quota root may include /// mailboxes the client has no access to. /// /// When the server supports this resource type, it MUST also support the DELETED status data /// item. /// /// Support for this resource MUST be indicated by the server by advertising the /// "QUOTA=RES-MESSAGE" capability. Message, /// The number of mailboxes governed by the quota root. /// /// This MUST be an exact number; however, clients MUST NOT assume that a change in the usage /// indicates a change in the number of mailboxes, since the quota root may include mailboxes /// the client has no access to. /// /// Support for this resource MUST be indicated by the server by advertising the /// "QUOTA=RES-MAILBOX" capability. Mailbox, /// The maximum size of all annotations \[RFC5257\], in units of 1024 octets, associated with all /// messages in the mailboxes governed by the quota root. /// /// Support for this resource MUST be indicated by the server by advertising the /// "QUOTA=RES-ANNOTATION-STORAGE" capability. AnnotationStorage, /// An (unknown) resource. Other(ResourceOther<'a>), } /// An (unknown) resource. /// /// It's guaranteed that this type can't represent any resource from [`Resource`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct ResourceOther<'a>(Atom<'a>); impl_try_from!(Atom<'a>, 'a, &'a [u8], Resource<'a>); impl_try_from!(Atom<'a>, 'a, Vec, Resource<'a>); impl_try_from!(Atom<'a>, 'a, &'a str, Resource<'a>); impl_try_from!(Atom<'a>, 'a, String, Resource<'a>); impl_try_from!(Atom<'a>, 'a, Cow<'a, str>, Resource<'a>); impl<'a> From> for Resource<'a> { fn from(atom: Atom<'a>) -> Self { match atom.inner().to_ascii_uppercase().as_ref() { "STORAGE" => Resource::Storage, "MESSAGE" => Resource::Message, "MAILBOX" => Resource::Mailbox, "ANNOTATION-STORAGE" => Resource::AnnotationStorage, _ => Resource::Other(ResourceOther(atom)), } } } impl Display for Resource<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(match self { Self::Storage => "STORAGE", Self::Message => "MESSAGE", Self::Mailbox => "MAILBOX", Self::AnnotationStorage => "ANNOTATION-STORAGE", Self::Other(other) => other.0.as_ref(), }) } } /// A type that holds a resource name, usage, and limit. /// Used in the response of the GETQUOTA command. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct QuotaGet<'a> { pub resource: Resource<'a>, pub usage: u64, pub limit: u64, } impl<'a> QuotaGet<'a> { pub fn new(resource: Resource<'a>, usage: u64, limit: u64) -> Self { Self { resource, usage, limit, } } } /// A type that holds a resource name and limit. /// Used in the SETQUOTA command. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(ToStatic, Debug, Clone, PartialEq, Eq, Hash)] pub struct QuotaSet<'a> { pub resource: Resource<'a>, pub limit: u64, } impl<'a> QuotaSet<'a> { pub fn new(resource: Resource<'a>, limit: u64) -> Self { Self { resource, limit } } } /// Error-related types. pub mod error { use thiserror::Error; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum QuotaError { #[error("Invalid root: {0}")] Root(R), #[error("Invalid quotas: {0}")] Quotas(Q), } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum QuotaRootError { #[error("Invalid mailbox: {0}")] Mailbox(M), #[error("Invalid roots: {0}")] Roots(R), } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum SetQuotaError { #[error("Invalid root: {0}")] Root(R), #[error("Invalid quota set: {0}")] QuotaSet(S), } } duesee-imap-codec-0d00966/imap-types/src/extensions/sort.rs000066400000000000000000000051071507724125200236600ustar00rootroot00000000000000use std::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "arbitrary")] use crate::arbitrary::impl_arbitrary_try_from; use crate::core::Atom; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum SortAlgorithm<'a> { Display, Other(SortAlgorithmOther<'a>), } impl<'a> From> for SortAlgorithm<'a> { fn from(value: Atom<'a>) -> Self { match value.as_ref().to_lowercase().as_ref() { "display" => Self::Display, _ => Self::Other(SortAlgorithmOther(value)), } } } impl Display for SortAlgorithm<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { SortAlgorithm::Display => f.write_str("DISPLAY"), SortAlgorithm::Other(other) => f.write_str(other.as_ref()), } } } #[cfg(feature = "arbitrary")] impl_arbitrary_try_from! { SortAlgorithm<'a>, Atom<'a> } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct SortAlgorithmOther<'a>(Atom<'a>); impl AsRef for SortAlgorithmOther<'_> { fn as_ref(&self) -> &str { self.0.as_ref() } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct SortCriterion { pub reverse: bool, pub key: SortKey, } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum SortKey { Arrival, Cc, Date, From, Size, Subject, To, // RFC5957 /// Note: Only use when server advertised `SORT=DISPLAY`. DisplayFrom, // RFC5957 /// Note: Only use when server advertised `SORT=DISPLAY`. DisplayTo, } impl AsRef for SortKey { fn as_ref(&self) -> &str { match self { SortKey::Arrival => "ARRIVAL", SortKey::Cc => "CC", SortKey::Date => "DATE", SortKey::From => "FROM", SortKey::Size => "SIZE", SortKey::Subject => "SUBJECT", SortKey::To => "TO", SortKey::DisplayFrom => "DISPLAYFROM", SortKey::DisplayTo => "DISPLAYTO", } } } duesee-imap-codec-0d00966/imap-types/src/extensions/thread.rs000066400000000000000000000125271507724125200241440ustar00rootroot00000000000000use std::{ fmt::{Display, Formatter}, num::NonZeroU32, }; #[cfg(feature = "arbitrary")] use arbitrary::{Arbitrary, Unstructured}; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "arbitrary")] use crate::arbitrary::impl_arbitrary_try_from; use crate::core::{Atom, Vec1, Vec2}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Thread { Members { prefix: Vec1, answers: Option>, }, Nested { answers: Vec2, }, } impl Display for Thread { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { let empty_answers: Vec = vec![]; write!(f, "(")?; let mut stack = match self { Self::Members { prefix, answers } => { write_prefix(f, prefix)?; match answers { Some(answers) => { write!(f, " ")?; vec![answers.as_ref().iter()] } None => vec![empty_answers.iter()], } } Self::Nested { answers } => { vec![answers.as_ref().iter()] } }; while let Some(answers) = stack.last_mut() { if let Some(thread) = answers.next() { let thing = match thread { Self::Members { prefix, answers } => { write!(f, "(")?; write_prefix(f, prefix)?; match answers { Some(answers) => { write!(f, " ")?; answers.as_ref().iter() } None => empty_answers.iter(), } } Self::Nested { answers } => { write!(f, "(")?; answers.as_ref().iter() } }; stack.push(thing); } else { stack.pop(); write!(f, ")")?; } } Ok(()) } } fn write_prefix(f: &mut Formatter, prefix: &Vec1) -> std::fmt::Result { let (head, tail) = prefix.as_ref().split_first().unwrap(); write!(f, "{head}")?; for element in tail { write!(f, " {element}")?; } Ok(()) } #[cfg(feature = "arbitrary")] impl<'a> Arbitrary<'a> for Thread { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { #[cfg(not(feature = "arbitrary_simplified"))] return arbitrary_thread_limited(u, 7); #[cfg(feature = "arbitrary_simplified")] return arbitrary_thread_leaf(u); } } #[cfg(all(feature = "arbitrary", not(feature = "arbitrary_simplified")))] fn arbitrary_thread_limited(u: &mut Unstructured, depth: usize) -> arbitrary::Result { // We cheat a bit: Start from a leaf ... let mut current = arbitrary_thread_leaf(u)?; // ... and build up the thread to the top (with max depth == 8) for _ in 0..depth { match u.int_in_range(0..=2)? { 0 => { current = Thread::Members { prefix: Arbitrary::arbitrary(u)?, answers: Some(Vec2::unvalidated(vec![current.clone(), current])), }; } 1 => { current = Thread::Nested { answers: Vec2::unvalidated(vec![current.clone(), current]), }; } 2 => { return Ok(current); } _ => unreachable!(), } } Ok(current) } #[cfg(feature = "arbitrary")] fn arbitrary_thread_leaf(u: &mut Unstructured) -> arbitrary::Result { Ok(Thread::Members { prefix: Arbitrary::arbitrary(u)?, answers: None, }) } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum ThreadingAlgorithm<'a> { OrderedSubject, References, Other(ThreadingAlgorithmOther<'a>), } impl<'a> From> for ThreadingAlgorithm<'a> { fn from(value: Atom<'a>) -> Self { match value.as_ref().to_lowercase().as_ref() { "orderedsubject" => Self::OrderedSubject, "references" => Self::References, _ => Self::Other(ThreadingAlgorithmOther(value)), } } } impl Display for ThreadingAlgorithm<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { f.write_str(match self { ThreadingAlgorithm::OrderedSubject => "ORDEREDSUBJECT", ThreadingAlgorithm::References => "REFERENCES", ThreadingAlgorithm::Other(other) => other.as_ref(), }) } } #[cfg(feature = "arbitrary")] impl_arbitrary_try_from! { ThreadingAlgorithm<'a>, Atom<'a> } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct ThreadingAlgorithmOther<'a>(Atom<'a>); impl AsRef for ThreadingAlgorithmOther<'_> { fn as_ref(&self) -> &str { self.0.as_ref() } } duesee-imap-codec-0d00966/imap-types/src/extensions/uidplus.rs000066400000000000000000000013561507724125200243600ustar00rootroot00000000000000use std::num::NonZeroU32; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::core::Vec1; #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct UidSet(pub Vec1); #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum UidElement { Single(NonZeroU32), Range(NonZeroU32, NonZeroU32), } duesee-imap-codec-0d00966/imap-types/src/extensions/unselect.rs000066400000000000000000000004771507724125200245200ustar00rootroot00000000000000//! The IMAP UNSELECT command use crate::command::CommandBody; impl CommandBody<'_> { ///
/// This extension must only be used when the server advertised support for it sending the UNSELECT capability. ///
pub fn unselect() -> Self { CommandBody::Unselect } } duesee-imap-codec-0d00966/imap-types/src/extensions/utf8.rs000066400000000000000000000024251507724125200235570ustar00rootroot00000000000000use std::{ borrow::Cow, fmt::{Debug, Display, Formatter}, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[non_exhaustive] pub enum Utf8Kind { Accept, Only, } impl Display for Utf8Kind { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { f.write_str(match self { Self::Accept => "ACCEPT", Self::Only => "ONLY", }) } } /// A quoted UTF-8 string. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct QuotedUtf8<'a>(pub Cow<'a, str>); impl From for QuotedUtf8<'_> { fn from(value: String) -> Self { Self(value.into()) } } impl Debug for QuotedUtf8<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "QuotedUtf8({:?})", self.0) } } duesee-imap-codec-0d00966/imap-types/src/fetch.rs000066400000000000000000000377541507724125200216000ustar00rootroot00000000000000//! Fetch-related types. #[cfg(feature = "ext_condstore_qresync")] use std::num::NonZeroU64; use std::{ fmt::{Display, Formatter}, num::NonZeroU32, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ body::BodyStructure, core::{AString, NString, NString8, Vec1}, datetime::DateTime, envelope::Envelope, flag::FlagFetch, }; /// Shorthands for commonly-used message data items. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[non_exhaustive] pub enum Macro { /// Shorthand for `(FLAGS INTERNALDATE RFC822.SIZE)`. Fast, /// Shorthand for `(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)`. All, /// Shorthand for `(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)`. Full, } impl Macro { pub fn expand(&self) -> Vec> { use MessageDataItemName::*; match self { Self::All => vec![Flags, InternalDate, Rfc822Size, Envelope], Self::Fast => vec![Flags, InternalDate, Rfc822Size], Self::Full => vec![Flags, InternalDate, Rfc822Size, Envelope, Body], } } } impl Display for Macro { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(match self { Macro::All => "ALL", Macro::Fast => "FAST", Macro::Full => "FULL", }) } } /// Either a macro or a list of message data items. /// /// A macro must be used by itself, and not in conjunction with other macros or data items. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum MacroOrMessageDataItemNames<'a> { Macro(Macro), MessageDataItemNames(Vec>), } impl From for MacroOrMessageDataItemNames<'_> { fn from(m: Macro) -> Self { MacroOrMessageDataItemNames::Macro(m) } } impl<'a> From>> for MacroOrMessageDataItemNames<'a> { fn from(item_names: Vec>) -> Self { MacroOrMessageDataItemNames::MessageDataItemNames(item_names) } } /// Message data item name used to request a message data item. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[doc(alias = "FetchAttribute")] pub enum MessageDataItemName<'a> { /// Non-extensible form of `BODYSTRUCTURE`. /// /// ```imap /// BODY /// ``` Body, /// The text of a particular body section. /// /// ```imap /// BODY[
]<> /// ``` BodyExt { /// The section specification is a set of zero or more part specifiers delimited by periods. /// /// An empty section specification refers to the entire message, including the header. /// /// See [`crate::fetch::Section`] and [`crate::fetch::PartSpecifier`]. /// /// Every message has at least one part number. Non-[MIME-IMB] /// messages, and non-multipart [MIME-IMB] messages with no /// encapsulated message, only have a part 1. /// /// Multipart messages are assigned consecutive part numbers, as /// they occur in the message. If a particular part is of type /// message or multipart, its parts MUST be indicated by a period /// followed by the part number within that nested multipart part. /// /// A part of type MESSAGE/RFC822 also has nested part numbers, /// referring to parts of the MESSAGE part's body. section: Option>, /// It is possible to fetch a substring of the designated text. /// This is done by appending an open angle bracket ("<"), the /// octet position of the first desired octet, a period, the /// maximum number of octets desired, and a close angle bracket /// (">") to the part specifier. If the starting octet is beyond /// the end of the text, an empty string is returned. /// /// Any partial fetch that attempts to read beyond the end of the /// text is truncated as appropriate. A partial fetch that starts /// at octet 0 is returned as a partial fetch, even if this /// truncation happened. /// /// Note: This means that BODY[]<0.2048> of a 1500-octet message /// will return BODY[]<0> with a literal of size 1500, not /// BODY[]. /// /// Note: A substring fetch of a HEADER.FIELDS or /// HEADER.FIELDS.NOT part specifier is calculated after /// subsetting the header. partial: Option<(u32, NonZeroU32)>, /// Defines, wheather BODY or BODY.PEEK should be used. /// /// `BODY[...]` implicitly sets the `\Seen` flag where `BODY.PEEK[...]` does not. peek: bool, }, /// The [MIME-IMB] body structure of a message. /// /// This is computed by the server by parsing the [MIME-IMB] header fields in the [RFC-2822] /// header and [MIME-IMB] headers. /// /// ```imap /// BODYSTRUCTURE /// ``` BodyStructure, /// The envelope structure of a message. /// /// This is computed by the server by parsing the [RFC-2822] header into the component parts, /// defaulting various fields as necessary. /// /// ```imap /// ENVELOPE /// ``` Envelope, /// The flags that are set for a message. /// /// ```imap /// FLAGS /// ``` Flags, /// The internal date of a message. /// /// ```imap /// INTERNALDATE /// ``` InternalDate, /// Functionally equivalent to `BODY[]`. /// /// Differs in the syntax of the resulting untagged FETCH data (`RFC822` is returned). /// /// ```imap /// RFC822 /// ``` /// /// Note: `BODY[]` is constructed as ... /// /// ```rust /// # use imap_types::fetch::MessageDataItemName; /// MessageDataItemName::BodyExt { /// section: None, /// partial: None, /// peek: false, /// }; /// ``` Rfc822, /// Functionally equivalent to `BODY.PEEK[HEADER]`. /// /// Differs in the syntax of the resulting untagged FETCH data (`RFC822.HEADER` is returned). /// /// ```imap /// RFC822.HEADER /// ``` Rfc822Header, /// The [RFC-2822] size of a message. /// /// ```imap /// RFC822.SIZE /// ``` Rfc822Size, /// Functionally equivalent to `BODY[TEXT]`. /// /// Differs in the syntax of the resulting untagged FETCH data (`RFC822.TEXT` is returned). /// ```imap /// RFC822.TEXT /// ``` Rfc822Text, /// The unique identifier for a message. /// /// ```imap /// UID /// ``` Uid, Binary { section: Vec, partial: Option<(u32, NonZeroU32)>, peek: bool, }, BinarySize { section: Vec, }, #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] ModSeq, } /// Message data item. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[doc(alias = "FetchAttributeValue")] pub enum MessageDataItem<'a> { /// A form of `BODYSTRUCTURE` without extension data. /// /// ```imap /// BODY /// ``` Body(BodyStructure<'a>), /// The body contents of the specified section. /// /// 8-bit textual data is permitted if a \[CHARSET\] identifier is /// part of the body parameter parenthesized list for this section. /// Note that headers (part specifiers HEADER or MIME, or the /// header portion of a MESSAGE/RFC822 part), MUST be 7-bit; 8-bit /// characters are not permitted in headers. Note also that the /// [RFC-2822] delimiting blank line between the header and the /// body is not affected by header line subsetting; the blank line /// is always included as part of header data, except in the case /// of a message which has no body and no blank line. /// /// Non-textual data such as binary data MUST be transfer encoded /// into a textual form, such as BASE64, prior to being sent to the /// client. To derive the original binary data, the client MUST /// decode the transfer encoded string. /// /// ```imap /// BODY[
]<> /// ``` BodyExt { /// The specified section. section: Option>, /// If the origin octet is specified, this string is a substring of /// the entire body contents, starting at that origin octet. This /// means that `BODY[]<0>` MAY be truncated, but `BODY[]` is NEVER /// truncated. /// /// Note: The origin octet facility MUST NOT be used by a server /// in a FETCH response unless the client specifically requested /// it by means of a FETCH of a `BODY[
]<>` data /// item. origin: Option, /// The string SHOULD be interpreted by the client according to the /// content transfer encoding, body type, and subtype. data: NString<'a>, }, /// The [MIME-IMB] body structure of a message. /// /// This is computed by the server by parsing the [MIME-IMB] header fields, defaulting various /// fields as necessary. /// /// ```imap /// BODYSTRUCTURE /// ``` BodyStructure(BodyStructure<'a>), /// The envelope structure of a message. /// /// This is computed by the server by parsing the [RFC-2822] header into the component parts, /// defaulting various fields as necessary. /// /// ```imap /// ENVELOPE /// ``` Envelope(Envelope<'a>), /// A list of flags that are set for a message. /// /// ```imap /// FLAGS /// ``` Flags(Vec>), /// A string representing the internal date of a message. /// /// ```imap /// INTERNALDATE /// ``` InternalDate(DateTime), /// Equivalent to `BODY[]`. /// /// ```imap /// RFC822 /// ``` Rfc822(NString<'a>), /// Equivalent to `BODY[HEADER]`. /// /// Note that this did not result in `\Seen` being set, because `RFC822.HEADER` response data /// occurs as a result of a `FETCH` of `RFC822.HEADER`. `BODY[HEADER]` response data occurs as a /// result of a `FETCH` of `BODY[HEADER]` (which sets `\Seen`) or `BODY.PEEK[HEADER]` (which /// does not set `\Seen`). /// /// ```imap /// RFC822.HEADER /// ``` Rfc822Header(NString<'a>), /// A number expressing the [RFC-2822] size of a message. /// /// ```imap /// RFC822.SIZE /// ``` Rfc822Size(u32), /// Equivalent to `BODY[TEXT]`. /// /// ```imap /// RFC822.TEXT /// ``` Rfc822Text(NString<'a>), /// A number expressing the unique identifier of a message. /// /// ```imap /// UID /// ``` Uid(NonZeroU32), Binary { section: Vec, value: NString8<'a>, }, BinarySize { section: Vec, size: u32, }, #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] ModSeq(NonZeroU64), } /// A part specifier is either a part number or one of the following: /// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`. /// /// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part /// specifiers refer to the [RFC-2822] header of the message or of /// an encapsulated [MIME-IMT] MESSAGE/RFC822 message. /// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of /// field-name (as defined in [RFC-2822]) names, and return a /// subset of the header. /// /// The field-matching is case-insensitive but otherwise exact. /// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header /// and the body; the blank line is included in all header fetches, /// except in the case of a message which has no body and no blank /// line. /// /// The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part /// specifiers can be the sole part specifier or can be prefixed by /// one or more numeric part specifiers, provided that the numeric /// part specifier refers to a part of type MESSAGE/RFC822. /// /// Here is an example of a complex message with some of its part specifiers: /// /// ```text /// HEADER ([RFC-2822] header of the message) /// TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED /// 1 TEXT/PLAIN /// 2 APPLICATION/OCTET-STREAM /// 3 MESSAGE/RFC822 /// 3.HEADER ([RFC-2822] header of the message) /// 3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED /// 3.1 TEXT/PLAIN /// 3.2 APPLICATION/OCTET-STREAM /// 4 MULTIPART/MIXED /// 4.1 IMAGE/GIF /// 4.1.MIME ([MIME-IMB] header for the IMAGE/GIF) /// 4.2 MESSAGE/RFC822 /// 4.2.HEADER ([RFC-2822] header of the message) /// 4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED /// 4.2.1 TEXT/PLAIN /// 4.2.2 MULTIPART/ALTERNATIVE /// 4.2.2.1 TEXT/PLAIN /// 4.2.2.2 TEXT/RICHTEXT /// ``` #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Section<'a> { Part(Part), Header(Option), /// The subset returned by HEADER.FIELDS contains only those header fields with a field-name that /// matches one of the names in the list. HeaderFields(Option, Vec1>), // TODO: what if none matches? /// Similarly, the subset returned by HEADER.FIELDS.NOT contains only the header fields /// with a non-matching field-name. HeaderFieldsNot(Option, Vec1>), // TODO: what if none matches? /// The TEXT part specifier refers to the text body of the message, omitting the [RFC-2822] header. Text(Option), /// The MIME part specifier MUST be prefixed by one or more numeric part specifiers /// and refers to the [MIME-IMB] header for this part. Mime(Part), } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Part(pub Vec1); /// A part specifier is either a part number or one of the following: /// `HEADER`, `HEADER.FIELDS`, `HEADER.FIELDS.NOT`, `MIME`, and `TEXT`. /// /// The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part /// specifiers refer to the [RFC-2822] header of the message or of /// an encapsulated [MIME-IMT] MESSAGE/RFC822 message. /// HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of /// field-name (as defined in [RFC-2822]) names, and return a /// subset of the header. /// /// The field-matching is case-insensitive but otherwise exact. /// Subsetting does not exclude the [RFC-2822] delimiting blank line between the header /// and the body; the blank line is included in all header fetches, /// except in the case of a message which has no body and no blank /// line. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum PartSpecifier<'a> { PartNumber(u32), Header, HeaderFields(Vec1>), HeaderFieldsNot(Vec1>), Mime, Text, } duesee-imap-codec-0d00966/imap-types/src/flag.rs000066400000000000000000000201751507724125200214050ustar00rootroot00000000000000//! Flag-related types. use std::fmt::{Display, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{core::Atom, error::ValidationError}; /// There are two types of flags in IMAP4rev1: System and keyword flags. /// /// A system flag is a flag name that is pre-defined in RFC3501. /// All system flags begin with "\\" and certain system flags (`\Deleted` and `\Seen`) have special semantics. /// Flags that begin with "\\" but are not pre-defined system flags, are extension flags. /// Clients MUST accept them and servers MUST NOT send them except when defined by future standard or standards-track revisions. /// /// A keyword is defined by the server implementation. /// Keywords do not begin with "\\" and servers may permit the client to define new ones /// in the mailbox by sending the `\*` flag ([`FlagPerm::Asterisk`]) in the PERMANENTFLAGS response.. /// /// Note that a flag of either type can be permanent or session-only. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Flag<'a> { /// Message has been answered (`\Answered`). Answered, /// Message is "deleted" for removal by later EXPUNGE (`\Deleted`). Deleted, /// Message has not completed composition (marked as a draft) (`\Draft`). Draft, /// Message is "flagged" for urgent/special attention (`\Flagged`). Flagged, /// Message has been read (`\Seen`). Seen, /// A future expansion of a system flag. Extension(FlagExtension<'a>), /// A keyword. Keyword(Atom<'a>), } /// An (extension) flag. /// /// It's guaranteed that this type can't represent any flag from [`Flag`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct FlagExtension<'a>(Atom<'a>); impl<'a> Flag<'a> { pub fn system(atom: Atom<'a>) -> Self { match atom.as_ref().to_ascii_lowercase().as_ref() { "answered" => Self::Answered, "deleted" => Self::Deleted, "draft" => Self::Draft, "flagged" => Self::Flagged, "seen" => Self::Seen, _ => Self::Extension(FlagExtension(atom)), } } pub fn keyword(atom: Atom<'a>) -> Self { Self::Keyword(atom) } } impl<'a> TryFrom<&'a str> for Flag<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Ok(if let Some(value) = value.strip_prefix('\\') { Self::system(Atom::try_from(value)?) } else { Self::keyword(Atom::try_from(value)?) }) } } impl Display for Flag<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { Flag::Answered => f.write_str("\\Answered"), Flag::Deleted => f.write_str("\\Deleted"), Flag::Draft => f.write_str("\\Draft"), Flag::Flagged => f.write_str("\\Flagged"), Flag::Seen => f.write_str("\\Seen"), Flag::Extension(other) => write!(f, "\\{}", other.0), Flag::Keyword(atom) => write!(f, "{atom}"), } } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum FlagFetch<'a> { Flag(Flag<'a>), /// Message is "recently" arrived in this mailbox. (`\Recent`) /// /// This session is the first session to have been notified about this message; if the session /// is read-write, subsequent sessions will not see \Recent set for this message. /// /// Note: This flag can not be altered by the client. Recent, } impl<'a> From> for FlagFetch<'a> { fn from(flag: Flag<'a>) -> Self { Self::Flag(flag) } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum FlagPerm<'a> { Flag(Flag<'a>), /// Indicates that it is possible to create new keywords by /// attempting to store those flags in the mailbox (`\*`). Asterisk, } impl<'a> From> for FlagPerm<'a> { fn from(flag: Flag<'a>) -> Self { Self::Flag(flag) } } /// Four name attributes are defined. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum FlagNameAttribute<'a> { /// It is not possible for any child levels of hierarchy to exist /// under this name; no child levels exist now and none can be /// created in the future. (`\Noinferiors`) Noinferiors, /// It is not possible to use this name as a selectable mailbox. (`\Noselect`) Noselect, /// The mailbox has been marked "interesting" by the server; the /// mailbox probably contains messages that have been added since /// the last time the mailbox was selected. (`\Marked`) Marked, /// The mailbox does not contain any additional messages since the /// last time the mailbox was selected. (`\Unmarked`) Unmarked, /// An extension flags. Extension(FlagNameAttributeExtension<'a>), } impl<'a> From> for FlagNameAttribute<'a> { fn from(extension: FlagNameAttributeExtension<'a>) -> Self { Self::Extension(extension) } } /// An extension flag. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct FlagNameAttributeExtension<'a>(Atom<'a>); impl FlagNameAttribute<'_> { pub fn is_selectability(&self) -> bool { matches!( self, FlagNameAttribute::Noselect | FlagNameAttribute::Marked | FlagNameAttribute::Unmarked ) } } impl<'a> From> for FlagNameAttribute<'a> { fn from(atom: Atom<'a>) -> Self { match atom.as_ref().to_ascii_lowercase().as_ref() { "noinferiors" => Self::Noinferiors, "noselect" => Self::Noselect, "marked" => Self::Marked, "unmarked" => Self::Unmarked, _ => Self::Extension(FlagNameAttributeExtension(atom)), } } } impl Display for FlagNameAttribute<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { Self::Noinferiors => f.write_str("\\Noinferiors"), Self::Noselect => f.write_str("\\Noselect"), Self::Marked => f.write_str("\\Marked"), Self::Unmarked => f.write_str("\\Unmarked"), Self::Extension(extension) => write!(f, "\\{}", extension.0), } } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToStatic)] pub enum StoreType { Replace, Add, Remove, } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToStatic)] pub enum StoreResponse { Answer, Silent, } #[cfg(test)] mod tests { use super::*; #[test] fn test_flagfetch() { let flag: Flag<'static> = Flag::Seen; let flag_fetch: FlagFetch<'static> = flag.into(); assert_eq!(flag_fetch, FlagFetch::Flag(Flag::Seen)); } #[test] fn test_flagperm() { let flag: Flag<'static> = Flag::Seen; let flag_perm: FlagPerm<'static> = flag.into(); assert_eq!(flag_perm, FlagPerm::Flag(Flag::Seen)); } #[test] fn test_flagnameattribute() { let atom = FlagNameAttributeExtension(Atom::try_from("Custom").unwrap()); let flag_name_attribute = FlagNameAttribute::from(atom.clone()); assert_eq!(flag_name_attribute, FlagNameAttribute::Extension(atom)); } } duesee-imap-codec-0d00966/imap-types/src/lib.rs000066400000000000000000000270031507724125200212370ustar00rootroot00000000000000//! # Misuse-resistant IMAP types //! //! The most prominent types in imap-types are [`Greeting`](response::Greeting), [`Command`](command::Command), and [`Response`](response::Response), and we use the term "message" to refer to either of them. //! Messages can be created in different ways. //! However, what all ways have in common is, that the API does not allow the creation of invalid ones. //! //! For example, all commands in IMAP are prefixed with a "tag". //! Although IMAP's tags are just strings, they have additional rules, such as that no whitespace is allowed. //! Thus, imap-types encapsulate them in [`Tag`](core::Tag) struct to ensure that invalid ones can't be created. //! //! ## Understanding and using the core types //! //! Similar to [`Tag`](core::Tag)s, there are more "core types" (or "string types"), such as, [`Atom`](core::Atom), [`Quoted`](core::Quoted), or [`Literal`](core::Literal). //! Besides being used for correctness, these types play a crucial role in IMAP because they determine the IMAP protocol flow. //! Sending a password as a literal requires a different protocol flow than sending the password as an atom or a quoted string. //! So, even though imap-types can choose the most efficient representation for a datum automatically, it's good to become familiar with the [`core`] module at some point to master the IMAP protocol. //! //! ## Construction of messages //! //! imap-types relies a lot on the standard conversion traits, i.e., [`From`], [`TryFrom`], [`Into`], and [`TryInto`]. //! Make good use of them. //! More convenient constructors are available for types that are more cumbersome to create. //! //! Note: When you are *sure* that the thing you want to create is valid, you can unlock various `unvalidated(...)` functions through the `unvalidated` feature. //! This allows us to bypass certain checks in release builds. //! //! ### Example //! //! ``` //! use imap_types::{ //! command::{Command, CommandBody}, //! core::Tag, //! }; //! //! // # Variant 1 //! // Create a `Command` with `tag` "A123" and `body` "NOOP". //! // (Note: `Command::new()` returns `Err(...)` when the tag is invalid.) //! let cmd = Command::new("A123", CommandBody::Noop).unwrap(); //! //! // # Variant 2 //! // Create a `CommandBody` first and finalize it into //! // a `Command` by attaching a tag later. //! let cmd = CommandBody::Noop.tag("A123").unwrap(); //! //! // # Variant 3 //! // Create a `Command` directly. //! let cmd = Command { //! tag: Tag::try_from("A123").unwrap(), //! body: CommandBody::Noop, //! }; //! ``` //! //! ## More complex messages //! //! ### Example //! //! The following example is a server fetch response containing the size and MIME structure of a message with the sequence number (or UID) 42. //! //! ``` //! use std::{borrow::Cow, num::NonZeroU32}; //! //! use imap_types::{ //! body::{BasicFields, Body, BodyStructure, SinglePartExtensionData, SpecificFields}, //! core::{IString, NString, Vec1}, //! fetch::MessageDataItem, //! response::{Data, Response}, //! }; //! //! let fetch = { //! let data = Data::Fetch { //! seq: NonZeroU32::new(42).unwrap(), //! items: Vec1::try_from(vec![ //! MessageDataItem::Rfc822Size(1337), //! MessageDataItem::Body(BodyStructure::Single { //! body: Body { //! basic: BasicFields { //! parameter_list: vec![], //! id: NString(None), //! description: NString(Some( //! IString::try_from("Important message.").unwrap(), //! )), //! content_transfer_encoding: IString::try_from("base64").unwrap(), //! size: 512, //! }, //! specific: SpecificFields::Basic { //! r#type: IString::try_from("text").unwrap(), //! subtype: IString::try_from("html").unwrap(), //! }, //! }, //! extension_data: None, //! }), //! ]) //! .unwrap(), //! }; //! //! Response::Data(data) //! }; //! ``` //! //! # Supported IMAP extensions //! //! | Description | //! |---------------------------------------------------------------------------------------------------------| //! | IMAP4 non-synchronizing literals ([RFC 2088], [RFC 7888]) | //! | Internet Message Access Protocol (IMAP) - MOVE Extension ([RFC 6851]) | //! | Internet Message Access Protocol (IMAP) UNSELECT command ([RFC 3691]) | //! | IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response ([RFC 4959]) | //! | The IMAP COMPRESS Extension ([RFC 4978]) | //! | The IMAP ENABLE Extension ([RFC 5161]) | //! | IMAP4 IDLE command ([RFC 2177]) | //! | IMAP QUOTA Extension ([RFC 9208]) | //! | IMAP4 UIDPLUS extension ([RFC 2359], [RFC 4315]) | //! | IMAP4 Binary Content Extension ([RFC 3516]) | //! | Internet Message Access Protocol - SORT and THREAD Extensions ([RFC 5256], [RFC 5957]) | //! //! # Features //! //! This crate uses the following features to enable experimental IMAP extensions: //! //! | Feature | Description | Status | //! |-----------------------|------------------------------------------------------------------------------------------------------------------------------|------------| //! | starttls | IMAP4rev1 ([RFC 3501]; section 6.2.1) | | //! | ext_condstore_qresync | IMAP Extensions: Quick Flag Changes Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC) ([RFC 7162]) | Unfinished | //! | ext_id | IMAP4 ID extension ([RFC 2971]) | Unfinished | //! | ext_login_referrals | IMAP4 Login Referrals ([RFC 2221]) | Unfinished | //! | ext_mailbox_referrals | IMAP4 Mailbox Referrals ([RFC 2193]) | Unfinished | //! | ext_metadata | The IMAP METADATA Extension ([RFC 5464]) | Unfinished | //! //! STARTTLS is not an IMAP extension but feature-gated because it [should be avoided](https://nostarttls.secvuln.info/). //! For better performance and security, use "implicit TLS", i.e., IMAP-over-TLS on port 993, and don't use STARTTLS at all. //! //! Furthermore, imap-types uses the following features: //! //! | Feature | Description | Enabled by default | //! |------------------|---------------------------------------------------------------|--------------------| //! | arbitrary | Derive `Arbitrary` implementations | No | //! | serde | Derive `serde`s `Serialize` and `Deserialize` implementations | No | //! | tag_generator | Provide a generator for randomized `Tag`s | No | //! //! When using `arbitrary`, all types defined in imap-types implement the [Arbitrary] trait to ease testing. //! This is used, for example, to generate instances during fuzz-testing. //! (See, e.g., `imap-types/fuzz/fuzz_targets/to_static.rs`) //! When the `serde` feature is used, all types implement [Serde](https://serde.rs/)'s [Serialize](https://docs.serde.rs/serde/trait.Serialize.html) and //! [Deserialize](https://docs.serde.rs/serde/trait.Deserialize.html) traits. (Try running `cargo run --example serde_json`.) //! Using `tag_generator` unlocks a `TagGenerator` to generate random tags. //! This may help to prevent attacks that depend on the knowledge of the next tag. //! //! [Arbitrary]: https://docs.rs/arbitrary/1.0.1/arbitrary/trait.Arbitrary.html //! [parse_command]: https://github.com/duesee/imap-codec/blob/main/imap-codec/examples/parse_command.rs //! [RFC 2088]: https://datatracker.ietf.org/doc/html/rfc2088 //! [RFC 2177]: https://datatracker.ietf.org/doc/html/rfc2177 //! [RFC 2193]: https://datatracker.ietf.org/doc/html/rfc2193 //! [RFC 2221]: https://datatracker.ietf.org/doc/html/rfc2221 //! [RFC 2359]: https://datatracker.ietf.org/doc/html/rfc2359 //! [RFC 2971]: https://datatracker.ietf.org/doc/html/rfc2971 //! [RFC 3501]: https://datatracker.ietf.org/doc/html/rfc3501 //! [RFC 3516]: https://datatracker.ietf.org/doc/html/rfc3516 //! [RFC 3691]: https://datatracker.ietf.org/doc/html/rfc3691 //! [RFC 4315]: https://datatracker.ietf.org/doc/html/rfc4315 //! [RFC 4959]: https://datatracker.ietf.org/doc/html/rfc4959 //! [RFC 4978]: https://datatracker.ietf.org/doc/html/rfc4978 //! [RFC 5161]: https://datatracker.ietf.org/doc/html/rfc5161 //! [RFC 5256]: https://datatracker.ietf.org/doc/html/rfc5256 //! [RFC 5464]: https://datatracker.ietf.org/doc/html/rfc5464 //! [RFC 5957]: https://datatracker.ietf.org/doc/html/rfc5957 //! [RFC 6851]: https://datatracker.ietf.org/doc/html/rfc6851 //! [RFC 7162]: https://datatracker.ietf.org/doc/html/rfc7162 //! [RFC 7888]: https://datatracker.ietf.org/doc/html/rfc7888 //! [RFC 9208]: https://datatracker.ietf.org/doc/html/rfc9208 // TODO(#660) #![allow(unknown_lints)] #![allow(mismatched_lifetime_syntaxes)] #![forbid(unsafe_code)] #![deny(missing_debug_implementations)] // TODO(#313) // #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] use bounded_static::{IntoBoundedStatic, ToBoundedStatic}; // Test examples from imap-types' README. #[doc = include_str!("../README.md")] #[cfg(doctest)] pub struct ReadmeDoctests; #[cfg(feature = "arbitrary")] mod arbitrary; pub mod auth; pub mod body; pub mod command; pub mod core; pub mod datetime; pub mod envelope; pub mod error; pub mod extensions; pub mod fetch; pub mod flag; pub mod mailbox; pub mod response; pub mod search; pub mod secret; pub mod sequence; pub mod state; pub mod status; pub mod utils; /// Create owned variant of object. /// /// Useful, e.g., if you want to pass the object to another thread or executor. pub trait ToStatic { type Static: 'static; fn to_static(&self) -> Self::Static; } impl ToStatic for T where T: ToBoundedStatic, { type Static = ::Static; fn to_static(&self) -> Self::Static { ToBoundedStatic::to_static(self) } } /// Create owned variant of object (consuming it). /// /// Useful, e.g., if you want to pass the object to another thread or executor. pub trait IntoStatic { type Static: 'static; fn into_static(self) -> Self::Static; } impl IntoStatic for T where T: IntoBoundedStatic, { type Static = ::Static; fn into_static(self) -> Self::Static { IntoBoundedStatic::into_static(self) } } duesee-imap-codec-0d00966/imap-types/src/mailbox.rs000066400000000000000000000277441507724125200221400ustar00rootroot00000000000000//! Mailbox-related types. use std::{borrow::Cow, str::from_utf8}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ core::{AString, IString, impl_try_from}, error::{ValidationError, ValidationErrorKind}, mailbox::error::MailboxOtherError, utils::indicators::is_list_char, }; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "String"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct ListCharString<'a>(pub(crate) Cow<'a, str>); impl<'a> ListCharString<'a> { pub fn validate(value: impl AsRef<[u8]>) -> Result<(), ValidationError> { let value = value.as_ref(); if value.is_empty() { return Err(ValidationError::new(ValidationErrorKind::Empty)); } if let Some(at) = value.iter().position(|b| !is_list_char(*b)) { return Err(ValidationError::new(ValidationErrorKind::InvalidByteAt { byte: value[at], at, })); }; Ok(()) } /// Constructs a list char string without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `inner` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(inner: C) -> Self where C: Into>, { let inner = inner.into(); #[cfg(debug_assertions)] Self::validate(inner.as_bytes()).unwrap(); Self(inner) } } impl<'a> TryFrom<&'a str> for ListCharString<'a> { type Error = ValidationError; fn try_from(value: &'a str) -> Result { Self::validate(value)?; Ok(Self(Cow::Borrowed(value))) } } impl TryFrom for ListCharString<'_> { type Error = ValidationError; fn try_from(value: String) -> Result { Self::validate(&value)?; Ok(Self(Cow::Owned(value))) } } impl AsRef<[u8]> for ListCharString<'_> { fn as_ref(&self) -> &[u8] { self.0.as_bytes() } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum ListMailbox<'a> { Token(ListCharString<'a>), String(IString<'a>), } impl<'a> TryFrom<&'a str> for ListMailbox<'a> { type Error = ValidationError; fn try_from(s: &'a str) -> Result { if s.is_empty() { // Safety: We know that an empty string can always be converted into a quoted string. return Ok(ListMailbox::String(IString::Quoted(s.try_into().unwrap()))); } if let Ok(lcs) = ListCharString::try_from(s) { return Ok(ListMailbox::Token(lcs)); } Ok(ListMailbox::String(s.try_into()?)) } } impl TryFrom for ListMailbox<'_> { type Error = ValidationError; fn try_from(s: String) -> Result { if s.is_empty() { // Safety: We know that an empty string can always be converted into a quoted string. return Ok(ListMailbox::String(IString::Quoted(s.try_into().unwrap()))); } // TODO(efficiency) if let Ok(lcs) = ListCharString::try_from(s.clone()) { return Ok(ListMailbox::Token(lcs)); } Ok(ListMailbox::String(s.try_into()?)) } } /// 5.1. Mailbox Naming /// /// Mailbox names are 7-bit. Client implementations MUST NOT attempt to /// create 8-bit mailbox names, and SHOULD interpret any 8-bit mailbox /// names returned by LIST or LSUB as UTF-8. Server implementations /// SHOULD prohibit the creation of 8-bit mailbox names, and SHOULD NOT /// return 8-bit mailbox names in LIST or LSUB. See section 5.1.3 for /// more information on how to represent non-ASCII mailbox names. /// /// Note: 8-bit mailbox names were undefined in earlier /// versions of this protocol. Some sites used a local 8-bit /// character set to represent non-ASCII mailbox names. Such /// usage is not interoperable, and is now formally deprecated. /// /// The case-insensitive mailbox name INBOX is a special name reserved to /// mean "the primary mailbox for this user on this server". The /// interpretation of all other names is implementation-dependent. /// /// In particular, this specification takes no position on case /// sensitivity in non-INBOX mailbox names. Some server implementations /// are fully case-sensitive; others preserve case of a newly-created /// name but otherwise are case-insensitive; and yet others coerce names /// to a particular case. Client implementations MUST interact with any /// of these. If a server implementation interprets non-INBOX mailbox /// names as case-insensitive, it MUST treat names using the /// international naming convention specially as described in section 5.1.3. /// /// There are certain client considerations when creating a new mailbox name: /// /// 1) Any character which is one of the atom-specials (see the Formal Syntax) will require /// that the mailbox name be represented as a quoted string or literal. /// 2) CTL and other non-graphic characters are difficult to represent in a user interface /// and are best avoided. /// 3) Although the list-wildcard characters ("%" and "*") are valid in a mailbox name, it is /// difficult to use such mailbox names with the LIST and LSUB commands due to the conflict /// with wildcard interpretation. /// 4) Usually, a character (determined by the server implementation) is reserved to delimit /// levels of hierarchy. /// 5) Two characters, "#" and "&", have meanings by convention, and should be avoided except /// when used in that convention. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Mailbox<'a> { Inbox, Other(MailboxOther<'a>), } impl_try_from!(AString<'a>, 'a, &'a [u8], Mailbox<'a>); impl_try_from!(AString<'a>, 'a, Vec, Mailbox<'a>); impl_try_from!(AString<'a>, 'a, &'a str, Mailbox<'a>); impl_try_from!(AString<'a>, 'a, String, Mailbox<'a>); impl<'a> From> for Mailbox<'a> { fn from(value: AString<'a>) -> Self { match from_utf8(value.as_ref()) { Ok(value) if value.eq_ignore_ascii_case("inbox") => Self::Inbox, _ => Self::Other(MailboxOther::try_from(value).unwrap()), } } } // We do not implement `AsRef<...>` for `Mailbox` because we want to enforce that a consumer // `match`es on `Mailbox::Inbox`/`Mailbox::Other`. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = "AString<'a>"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct MailboxOther<'a>(pub(crate) AString<'a>); impl<'a> MailboxOther<'a> { pub fn validate(value: impl AsRef<[u8]>) -> Result<(), MailboxOtherError> { if value.as_ref().eq_ignore_ascii_case(b"inbox") { return Err(MailboxOtherError::Reserved); } Ok(()) } pub fn inner(&self) -> &AString<'_> { &self.0 } /// Constructs a mailbox without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `value` is valid according to [`Self::validate`]. Failing to do /// so may create invalid/unparsable IMAP messages, or even produce unintended protocol flows. /// Do not call this constructor with untrusted data. /// /// Note: This method will `panic!` on wrong input in debug builds. pub fn unvalidated(value: AString<'a>) -> Self { #[cfg(debug_assertions)] Self::validate(&value).unwrap(); Self(value) } } macro_rules! impl_try_from_mailbox_other { ($from:ty) => { impl<'a> TryFrom<$from> for MailboxOther<'a> { type Error = MailboxOtherError; fn try_from(value: $from) -> Result { let astring = AString::try_from(value)?; Self::validate(&astring)?; Ok(Self(astring)) } } }; } impl_try_from_mailbox_other!(&'a [u8]); impl_try_from_mailbox_other!(Vec); impl_try_from_mailbox_other!(&'a str); impl_try_from_mailbox_other!(String); impl<'a> TryFrom> for MailboxOther<'a> { type Error = MailboxOtherError; fn try_from(value: AString<'a>) -> Result { Self::validate(&value)?; Ok(Self(value)) } } impl AsRef<[u8]> for MailboxOther<'_> { fn as_ref(&self) -> &[u8] { self.0.as_ref() } } /// Error-related types. pub mod error { use thiserror::Error; use crate::error::ValidationError; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum MailboxOtherError { #[error(transparent)] Literal(#[from] ValidationError), #[error("Reserved: Please use one of the typed variants")] Reserved, } } #[cfg(test)] mod tests { use std::borrow::Cow; use super::*; #[cfg(feature = "serde")] use crate::core::AtomExt; use crate::core::{AString, IString, Literal, LiteralMode}; #[test] fn test_conversion_mailbox() { let tests = [ ("inbox", Mailbox::Inbox), ("inboX", Mailbox::Inbox), ("Inbox", Mailbox::Inbox), ("InboX", Mailbox::Inbox), ("INBOX", Mailbox::Inbox), ( "INBO²", Mailbox::Other(MailboxOther(AString::String(IString::Literal(Literal { data: Cow::Borrowed("INBO²".as_bytes()), mode: LiteralMode::Sync, })))), ), ]; for (test, expected) in tests { let got = Mailbox::try_from(test).unwrap(); assert_eq!(expected, got); let got = Mailbox::try_from(String::from(test)).unwrap(); assert_eq!(expected, got); } } #[test] fn test_conversion_mailbox_failing() { let tests = ["\x00", "A\x00", "\x00A"]; for test in tests { assert!(Mailbox::try_from(test).is_err()); assert!(Mailbox::try_from(String::from(test)).is_err()); } } #[cfg(feature = "serde")] #[test] fn test_deserialization_list_char_string() { let valid_input = r#""OneWord""#; let invalid_input = r#""Two Words""#; let list_char_string = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!(list_char_string, ListCharString(Cow::Borrowed("OneWord"))); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Validation failed: Invalid byte b'\x20' at index 3" ); } #[cfg(feature = "serde")] #[test] fn test_deserialization_mailbox_other() { let valid_input = r#"{ "type": "Atom", "content": "other" }"#; let invalid_input = r#"{ "type": "Atom", "content": "inbox" }"#; let mailbox_other = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!( mailbox_other, MailboxOther(AString::Atom(AtomExt(Cow::Borrowed("other")))) ); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!( err.to_string(), r"Reserved: Please use one of the typed variants" ); } } duesee-imap-codec-0d00966/imap-types/src/response.rs000066400000000000000000001403131507724125200223270ustar00rootroot00000000000000//! # 7. Server Responses #[cfg(feature = "ext_condstore_qresync")] use std::num::NonZeroU64; use std::{ borrow::Cow, fmt::{Debug, Display, Formatter}, num::{NonZeroU32, TryFromIntError}, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use base64::{Engine, engine::general_purpose::STANDARD as _base64}; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "ext_id")] use crate::core::{IString, NString}; #[cfg(feature = "ext_metadata")] use crate::extensions::metadata::{MetadataCode, MetadataResponse}; #[cfg(feature = "ext_namespace")] use crate::extensions::namespace::Namespaces; #[cfg(feature = "ext_utf8")] use crate::extensions::utf8::Utf8Kind; #[cfg(feature = "ext_condstore_qresync")] use crate::sequence::SequenceSet; use crate::{ auth::AuthMechanism, core::{AString, Atom, Charset, QuotedChar, Tag, Text, Vec1, impl_try_from}, error::ValidationError, extensions::{ compress::CompressionAlgorithm, enable::CapabilityEnable, quota::{QuotaGet, Resource}, sort::SortAlgorithm, thread::{Thread, ThreadingAlgorithm}, uidplus::UidSet, }, fetch::MessageDataItem, flag::{Flag, FlagNameAttribute, FlagPerm}, mailbox::Mailbox, response::error::{ContinueError, FetchError}, status::StatusDataItem, }; /// Greeting. /// /// Note: Don't use `code: None` *and* a `text` that starts with "[" as this would be ambiguous in IMAP. /// We could fix this but the fix would make this type unconformable to use. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Greeting<'a> { pub kind: GreetingKind, pub code: Option>, pub text: Text<'a>, } impl<'a> Greeting<'a> { pub fn new( kind: GreetingKind, code: Option>, text: &'a str, ) -> Result { Ok(Greeting { kind, code, text: text.try_into()?, }) } pub fn ok(code: Option>, text: &'a str) -> Result { Ok(Greeting { kind: GreetingKind::Ok, code, text: text.try_into()?, }) } pub fn preauth(code: Option>, text: &'a str) -> Result { Ok(Greeting { kind: GreetingKind::PreAuth, code, text: text.try_into()?, }) } pub fn bye(code: Option>, text: &'a str) -> Result { Ok(Greeting { kind: GreetingKind::Bye, code, text: text.try_into()?, }) } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToStatic)] /// IMAP4rev1 defines three possible greetings at connection startup. pub enum GreetingKind { /// The connection is not yet authenticated. /// /// (Advice: A LOGIN command is needed.) Ok, /// The connection has already been authenticated by external means. /// /// (Advice: No LOGIN command is needed.) PreAuth, /// The server is not willing to accept a connection from this client. /// /// (Advice: The server closes the connection immediately.) Bye, } /// Response. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Response<'a> { /// Command continuation request responses use the token "+" instead of a /// tag. These responses are sent by the server to indicate acceptance /// of an incomplete client command and readiness for the remainder of /// the command. CommandContinuationRequest(CommandContinuationRequest<'a>), /// All server data is untagged. An untagged response is indicated by the /// token "*" instead of a tag. Untagged status responses indicate server /// greeting, or server status that does not indicate the completion of a /// command (for example, an impending system shutdown alert). Data(Data<'a>), /// Status responses can be tagged or untagged. Tagged status responses /// indicate the completion result (OK, NO, or BAD status) of a client /// command, and have a tag matching the command. Status(Status<'a>), } /// Status response. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Status<'a> { Untagged(StatusBody<'a>), Tagged(Tagged<'a>), Bye(Bye<'a>), } /// Status body. /// /// Note: Don't use `code: None` *and* a `text` that starts with "[" as this would be ambiguous in IMAP. /// We could fix this but the fix would make this type unconformable to use. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct StatusBody<'a> { /// Status kind. pub kind: StatusKind, /// Response code (optional). pub code: Option>, /// Human-readable text that MAY be displayed to the user. /// /// Note: Must be at least 1 character. pub text: Text<'a>, } /// Status kind. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToStatic)] pub enum StatusKind { /// Indicates an information from the server. /// /// * In [`Status::Tagged`], it indicates successful completion of the associated command. /// * In [`Status::Untagged`], it indicates an information-only message. Ok, /// Indicates an operational error from the server. /// /// * In [`Status::Tagged`], it indicates unsuccessful completion of the associated command. /// * In [`Status::Untagged`], it indicates a warning. No, /// Indicates a protocol-level error from the server. /// /// * In [`Status::Tagged`], it reports a protocol-level error in the client's command. /// * In [`Status::Untagged`], it indicates a protocol-level error for which the associated command can not be determined. Bad, } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Tagged<'a> { pub tag: Tag<'a>, pub body: StatusBody<'a>, } /// Indicates that the server is about to close the connection. /// /// The BYE response is sent under one of four conditions: /// /// 1) as part of a normal logout sequence. The server will close /// the connection after sending the tagged OK response to the /// LOGOUT command. /// /// 2) as a panic shutdown announcement. The server closes the /// connection immediately. /// /// 3) as an announcement of an inactivity autologout. The server /// closes the connection immediately. /// /// 4) as one of three possible greetings at connection startup, /// indicating that the server is not willing to accept a /// connection from this client. The server closes the /// connection immediately. /// /// The difference between a BYE that occurs as part of a normal /// LOGOUT sequence (the first case) and a BYE that occurs because of /// a failure (the other three cases) is that the connection closes /// immediately in the failure case. In all cases the client SHOULD /// continue to read response data from the server until the /// connection is closed; this will ensure that any pending untagged /// or completion responses are read and processed. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct Bye<'a> { pub code: Option>, pub text: Text<'a>, } impl<'a> Status<'a> { pub fn new( tag: Option>, kind: StatusKind, code: Option>, text: T, ) -> Result where T: TryInto>, { let body = StatusBody { kind, code, text: text.try_into()?, }; match tag { Some(tag) => Ok(Self::Tagged(Tagged { tag, body })), None => Ok(Self::Untagged(body)), } } // FIXME(API) pub fn ok(tag: Option>, code: Option>, text: T) -> Result where T: TryInto>, { Self::new(tag, StatusKind::Ok, code, text) } // FIXME(API) pub fn no(tag: Option>, code: Option>, text: T) -> Result where T: TryInto>, { Self::new(tag, StatusKind::No, code, text) } // FIXME(API) pub fn bad(tag: Option>, code: Option>, text: T) -> Result where T: TryInto>, { Self::new(tag, StatusKind::Bad, code, text) } pub fn bye(code: Option>, text: T) -> Result where T: TryInto>, { Ok(Self::Bye(Bye { code, text: text.try_into()?, })) } // --------------------------------------------------------------------------------------------- pub fn tag(&self) -> Option<&Tag<'_>> { match self { Self::Tagged(Tagged { tag, .. }) => Some(tag), _ => None, } } pub fn code(&self) -> Option<&Code<'_>> { match self { Self::Untagged(StatusBody { code, .. }) | Self::Tagged(Tagged { body: StatusBody { code, .. }, .. }) | Self::Bye(Bye { code, .. }) => code.as_ref(), } } pub fn text(&self) -> &Text<'_> { match self { Self::Untagged(StatusBody { text, .. }) | Self::Tagged(Tagged { body: StatusBody { text, .. }, .. }) | Self::Bye(Bye { text, .. }) => text, } } } /// ## 7.2 - 7.4 Server and Mailbox Status; Mailbox Size; Message Status #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Data<'a> { // ## 7.2. Server Responses - Server and Mailbox Status // // These responses are always untagged. This is how server and mailbox // status data are transmitted from the server to the client. Many of // these responses typically result from a command with the same name. /// ### 7.2.1. CAPABILITY Response /// /// * Contents: capability listing /// /// The CAPABILITY response occurs as a result of a CAPABILITY /// command. The capability listing contains a space-separated /// listing of capability names that the server supports. The /// capability listing MUST include the atom "IMAP4rev1". /// /// In addition, client and server implementations MUST implement the /// STARTTLS, LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS]) /// capabilities. See the Security Considerations section for /// important information. /// /// A capability name which begins with "AUTH=" indicates that the /// server supports that particular authentication mechanism. /// /// The LOGINDISABLED capability indicates that the LOGIN command is /// disabled, and that the server will respond with a tagged NO /// response to any attempt to use the LOGIN command even if the user /// name and password are valid. An IMAP client MUST NOT issue the /// LOGIN command if the server advertises the LOGINDISABLED /// capability. /// /// Other capability names indicate that the server supports an /// extension, revision, or amendment to the IMAP4rev1 protocol. /// Server responses MUST conform to this document until the client /// issues a command that uses the associated capability. /// /// Capability names MUST either begin with "X" or be standard or /// standards-track IMAP4rev1 extensions, revisions, or amendments /// registered with IANA. A server MUST NOT offer unregistered or /// non-standard capability names, unless such names are prefixed with /// an "X". /// /// Client implementations SHOULD NOT require any capability name /// other than "IMAP4rev1", and MUST ignore any unknown capability /// names. /// /// A server MAY send capabilities automatically, by using the /// CAPABILITY response code in the initial PREAUTH or OK responses, /// and by sending an updated CAPABILITY response code in the tagged /// OK response as part of a successful authentication. It is /// unnecessary for a client to send a separate CAPABILITY command if /// it recognizes these automatic capabilities. Capability(Vec1>), /// ### 7.2.2. LIST Response /// /// The LIST response occurs as a result of a LIST command. It /// returns a single name that matches the LIST specification. There /// can be multiple LIST responses for a single LIST command. /// /// The hierarchy delimiter is a character used to delimit levels of /// hierarchy in a mailbox name. A client can use it to create child /// mailboxes, and to search higher or lower levels of naming /// hierarchy. All children of a top-level hierarchy node MUST use /// the same separator character. A NIL hierarchy delimiter means /// that no hierarchy exists; the name is a "flat" name. /// /// The name represents an unambiguous left-to-right hierarchy, and /// MUST be valid for use as a reference in LIST and LSUB commands. /// Unless \Noselect is indicated, the name MUST also be valid as an /// argument for commands, such as SELECT, that accept mailbox names. List { /// Name attributes items: Vec>, /// Hierarchy delimiter delimiter: Option, /// Name mailbox: Mailbox<'a>, }, /// ### 7.2.3. LSUB Response /// /// The LSUB response occurs as a result of an LSUB command. It /// returns a single name that matches the LSUB specification. There /// can be multiple LSUB responses for a single LSUB command. The /// data is identical in format to the LIST response. Lsub { /// Name attributes items: Vec>, /// Hierarchy delimiter delimiter: Option, /// Name mailbox: Mailbox<'a>, }, /// ### 7.2.4 STATUS Response /// /// The STATUS response occurs as a result of an STATUS command. It /// returns the mailbox name that matches the STATUS specification and /// the requested mailbox status information. Status { /// Name mailbox: Mailbox<'a>, /// Status parenthesized list items: Cow<'a, [StatusDataItem]>, }, /// ### 7.2.5. SEARCH Response /// /// * Contents: zero or more numbers /// /// The SEARCH response occurs as a result of a SEARCH or UID SEARCH /// command. The number(s) refer to those messages that match the /// search criteria. For SEARCH, these are message sequence numbers; /// for UID SEARCH, these are unique identifiers. Each number is /// delimited by a space. Search( Vec, /// MODSEQ #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] Option, ), Sort( Vec, #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] Option, ), Thread(Vec), /// ### 7.2.6. FLAGS Response /// /// * Contents: flag parenthesized list /// /// The FLAGS response occurs as a result of a SELECT or EXAMINE /// command. The flag parenthesized list identifies the flags (at a /// minimum, the system-defined flags) that are applicable for this /// mailbox. Flags other than the system flags can also exist, /// depending on server implementation. /// /// The update from the FLAGS response MUST be recorded by the client. Flags(Vec>), // ## 7.3. Server Responses - Mailbox Size // // These responses are always untagged. This is how changes in the size // of the mailbox are transmitted from the server to the client. // Immediately following the "*" token is a number that represents a // message count. /// ### 7.3.1. EXISTS Response /// /// The EXISTS response reports the number of messages in the mailbox. /// This response occurs as a result of a SELECT or EXAMINE command, /// and if the size of the mailbox changes (e.g., new messages). /// /// The update from the EXISTS response MUST be recorded by the client. Exists(u32), /// ### 7.3.2. RECENT Response /// /// The RECENT response reports the number of messages with the /// \Recent flag set. This response occurs as a result of a SELECT or /// EXAMINE command, and if the size of the mailbox changes (e.g., new /// messages). /// /// Note: It is not guaranteed that the message sequence /// numbers of recent messages will be a contiguous range of /// the highest n messages in the mailbox (where n is the /// value reported by the RECENT response). Examples of /// situations in which this is not the case are: multiple /// clients having the same mailbox open (the first session /// to be notified will see it as recent, others will /// probably see it as non-recent), and when the mailbox is /// re-ordered by a non-IMAP agent. /// /// The only reliable way to identify recent messages is to /// look at message flags to see which have the \Recent flag /// set, or to do a SEARCH RECENT. /// /// The update from the RECENT response MUST be recorded by the client. Recent(u32), // ## 7.4. Server Responses - Message Status // // These responses are always untagged. This is how message data are // transmitted from the server to the client, often as a result of a // command with the same name. Immediately following the "*" token is a // number that represents a message sequence number. /// ### 7.4.1. EXPUNGE Response /// /// The EXPUNGE response reports that the specified message sequence /// number has been permanently removed from the mailbox. The message /// sequence number for each successive message in the mailbox is /// immediately decremented by 1, and this decrement is reflected in /// message sequence numbers in subsequent responses (including other /// untagged EXPUNGE responses). /// /// The EXPUNGE response also decrements the number of messages in the /// mailbox; it is not necessary to send an EXISTS response with the /// new value. /// /// As a result of the immediate decrement rule, message sequence /// numbers that appear in a set of successive EXPUNGE responses /// depend upon whether the messages are removed starting from lower /// numbers to higher numbers, or from higher numbers to lower /// numbers. For example, if the last 5 messages in a 9-message /// mailbox are expunged, a "lower to higher" server will send five /// untagged EXPUNGE responses for message sequence number 5, whereas /// a "higher to lower server" will send successive untagged EXPUNGE /// responses for message sequence numbers 9, 8, 7, 6, and 5. /// /// An EXPUNGE response MUST NOT be sent when no command is in /// progress, nor while responding to a FETCH, STORE, or SEARCH /// command. This rule is necessary to prevent a loss of /// synchronization of message sequence numbers between client and /// server. A command is not "in progress" until the complete command /// has been received; in particular, a command is not "in progress" /// during the negotiation of command continuation. /// /// Note: UID FETCH, UID STORE, and UID SEARCH are different /// commands from FETCH, STORE, and SEARCH. An EXPUNGE /// response MAY be sent during a UID command. /// /// The update from the EXPUNGE response MUST be recorded by the client. Expunge(NonZeroU32), /// ### 7.4.2. FETCH Response /// /// The FETCH response returns data about a message to the client. /// The data are pairs of data item names and their values in /// parentheses. This response occurs as the result of a FETCH or /// STORE command, as well as by unilateral server decision (e.g., /// flag updates). Fetch { /// Sequence number. seq: NonZeroU32, /// Message data items. items: Vec1>, }, Enabled { capabilities: Vec>, }, Quota { /// Quota root. root: AString<'a>, /// List of quotas. quotas: Vec1>, }, QuotaRoot { /// Mailbox name. mailbox: Mailbox<'a>, /// List of quota roots. roots: Vec>, }, #[cfg(feature = "ext_id")] /// ID Response Id { /// Parameters parameters: Option, NString<'a>)>>, }, #[cfg(feature = "ext_metadata")] /// Metadata response Metadata { mailbox: Mailbox<'a>, items: MetadataResponse<'a>, }, #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] Vanished { earlier: bool, known_uids: SequenceSet, }, #[cfg(feature = "ext_namespace")] Namespace { personal: Namespaces<'a>, other: Namespaces<'a>, shared: Namespaces<'a>, }, } impl<'a> Data<'a> { pub fn capability(caps: C) -> Result where C: TryInto>>, { Ok(Self::Capability(caps.try_into()?)) } // TODO // pub fn list() -> Self { // unimplemented!() // } // TODO // pub fn lsub() -> Self { // unimplemented!() // } // TODO // pub fn status() -> Self { // unimplemented!() // } // TODO // pub fn search() -> Self { // unimplemented!() // } // TODO // pub fn flags() -> Self { // unimplemented!() // } pub fn expunge(seq: u32) -> Result { Ok(Self::Expunge(NonZeroU32::try_from(seq)?)) } pub fn fetch(seq: S, items: I) -> Result> where S: TryInto, I: TryInto>>, { let seq = seq.try_into().map_err(FetchError::SeqOrUid)?; let items = items.try_into().map_err(FetchError::InvalidItems)?; Ok(Self::Fetch { seq, items }) } } /// ## 7.5. Server Responses - Command Continuation Request /// /// The command continuation request response is indicated by a "+" token /// instead of a tag. This form of response indicates that the server is /// ready to accept the continuation of a command from the client. The /// remainder of this response is a line of text. /// /// This response is used in the AUTHENTICATE command to transmit server /// data to the client, and request additional client data. This /// response is also used if an argument to any command is a literal. /// /// The client is not permitted to send the octets of the literal unless /// the server indicates that it is expected. This permits the server to /// process commands and reject errors on a line-by-line basis. The /// remainder of the command, including the CRLF that terminates a /// command, follows the octets of the literal. If there are any /// additional command arguments, the literal octets are followed by a /// space and those arguments. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[doc(alias = "Continue")] #[doc(alias = "Continuation")] #[doc(alias = "ContinuationRequest")] pub enum CommandContinuationRequest<'a> { Basic(CommandContinuationRequestBasic<'a>), Base64(Cow<'a, [u8]>), } impl<'a> CommandContinuationRequest<'a> { pub fn basic(code: Option>, text: T) -> Result> where T: TryInto>, { Ok(Self::Basic(CommandContinuationRequestBasic::new( code, text, )?)) } pub fn base64<'data: 'a, D>(data: D) -> Self where D: Into>, { Self::Base64(data.into()) } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde", serde(try_from = "CommandContinuationRequestBasicShadow") )] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct CommandContinuationRequestBasic<'a> { code: Option>, text: Text<'a>, } /// Use shadow type to support validated deserialization /// until `serde` provides built-in support for this case. #[cfg(feature = "serde")] #[derive(Deserialize, Debug)] struct CommandContinuationRequestBasicShadow<'a> { code: Option>, text: Text<'a>, } #[cfg(feature = "serde")] impl<'a> TryFrom> for CommandContinuationRequestBasic<'a> { type Error = ContinueError; fn try_from(value: CommandContinuationRequestBasicShadow<'a>) -> Result { Self::new(value.code, value.text) } } impl<'a> CommandContinuationRequestBasic<'a> { /// Create a basic continuation request. /// /// Note: To avoid ambiguities in the IMAP standard, this constructor ensures that: /// * if `code` is `None`, `text` must not start with `[`. /// * if `code` is `None`, `text` must *not* be valid according to base64. /// /// Otherwise, we could send a `Continue::Basic` that is interpreted as `Continue::Base64`. pub fn new(code: Option>, text: T) -> Result> where T: TryInto>, { let text = text.try_into().map_err(ContinueError::Text)?; // Ambiguity #1 if code.is_none() && text.as_ref().starts_with('[') { return Err(ContinueError::Ambiguity); } // Ambiguity #2 if code.is_none() && _base64.decode(text.inner()).is_ok() { return Err(ContinueError::Ambiguity); } Ok(Self { code, text }) } pub fn code(&self) -> Option<&Code<'a>> { self.code.as_ref() } pub fn text(&self) -> &Text<'a> { &self.text } } /// A response code consists of data inside square brackets in the form of an atom, /// possibly followed by a space and arguments. The response code /// contains additional information or status codes for client software /// beyond the OK/NO/BAD condition, and are defined when there is a /// specific action that a client can take based upon the additional /// information. /// /// The currently defined response codes are: #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Code<'a> { /// `ALERT` /// /// The human-readable text contains a special alert that MUST be /// presented to the user in a fashion that calls the user's /// attention to the message. Alert, /// `BADCHARSET` /// /// Optionally followed by a parenthesized list of charsets. A /// SEARCH failed because the given charset is not supported by /// this implementation. If the optional list of charsets is /// given, this lists the charsets that are supported by this /// implementation. BadCharset { allowed: Vec>, }, /// `CAPABILITY` /// /// Followed by a list of capabilities. This can appear in the /// initial OK or PREAUTH response to transmit an initial /// capabilities list. This makes it unnecessary for a client to /// send a separate CAPABILITY command if it recognizes this /// response. Capability(Vec1>), // FIXME(misuse): List must contain IMAP4REV1 /// `PARSE` /// /// The human-readable text represents an error in parsing the /// [RFC-2822] header or [MIME-IMB] headers of a message in the /// mailbox. Parse, /// `PERMANENTFLAGS` /// /// Followed by a parenthesized list of flags, indicates which of /// the known flags the client can change permanently. Any flags /// that are in the FLAGS untagged response, but not the /// PERMANENTFLAGS list, can not be set permanently. If the client /// attempts to STORE a flag that is not in the PERMANENTFLAGS /// list, the server will either ignore the change or store the /// state change for the remainder of the current session only. /// The PERMANENTFLAGS list can also include the special flag \*, /// which indicates that it is possible to create new keywords by /// attempting to store those flags in the mailbox. PermanentFlags(Vec>), /// `READ-ONLY` /// /// The mailbox is selected read-only, or its access while selected /// has changed from read-write to read-only. ReadOnly, /// `READ-WRITE` /// /// The mailbox is selected read-write, or its access while /// selected has changed from read-only to read-write. ReadWrite, /// `TRYCREATE` /// /// An APPEND or COPY attempt is failing because the target mailbox /// does not exist (as opposed to some other reason). This is a /// hint to the client that the operation can succeed if the /// mailbox is first created by the CREATE command. TryCreate, /// `UIDNEXT` /// /// Followed by a decimal number, indicates the next unique /// identifier value. Refer to section 2.3.1.1 for more /// information. UidNext(NonZeroU32), /// `UIDVALIDITY` /// /// Followed by a decimal number, indicates the unique identifier /// validity value. Refer to section 2.3.1.1 for more information. UidValidity(NonZeroU32), /// `UNSEEN` /// /// Followed by a decimal number, indicates the number of the first /// message without the \Seen flag set. Unseen(NonZeroU32), /// IMAP4 Login Referrals (RFC 2221) // TODO(misuse): the imap url is more complicated than that... #[cfg(any(feature = "ext_mailbox_referrals", feature = "ext_login_referrals"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "ext_mailbox_referrals", feature = "ext_login_referrals"))) )] Referral(Cow<'a, str>), CompressionActive, /// SHOULD be returned in the tagged NO response to an APPEND/COPY/MOVE when the addition of the /// message(s) puts the target mailbox over any one of its quota limits. OverQuota, /// Server got a non-synchronizing literal larger than 4096 bytes. TooBig, #[cfg(feature = "ext_metadata")] /// Metadata Metadata(MetadataCode), /// Server does not know how to decode the section's CTE. UnknownCte, /// Message has been appended to destination mailbox with that UID AppendUid { /// UIDVALIDITY of destination mailbox uid_validity: NonZeroU32, /// UID assigned to appended message in destination mailbox uid: NonZeroU32, }, /// Message(s) have been copied to destination mailbox with stated UID(s) CopyUid { /// UIDVALIDITY of destination mailbox uid_validity: NonZeroU32, /// UIDs copied to destination mailbox source: UidSet, /// UIDs assigned in destination mailbox destination: UidSet, }, UidNotSticky, /// IMAP4 Extension for Conditional STORE Operation (RFC 4551) /// A server supporting the persistent storage of mod-sequences for the mailbox /// MUST send the OK untagged response including HIGHESTMODSEQ response /// code with every successful SELECT or EXAMINE command #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] HighestModSeq(NonZeroU64), /// IMAP4 Extension for Conditional STORE Operation (RFC 4551) /// When the server finished performing the operation on all the /// messages in the message set, it checks for a non-empty list of /// messages that failed the UNCHANGESINCE test. If this list is /// non-empty, the server MUST return in the tagged response a /// MODIFIED response code. The MODIFIED response code includes the /// message set (for STORE) or set of UIDs (for UID STORE) of all /// messages that failed the UNCHANGESINCE test. #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] Modified(SequenceSet), /// IMAP4 Extension for Conditional STORE Operation (RFC 4551) /// A server that doesn't support the persistent storage of mod-sequences /// for the mailbox MUST send the OK untagged response including NOMODSEQ /// response code with every successful SELECT or EXAMINE command. #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] NoModSeq, /// A server implementing the extension defined in this document MUST return the CLOSED /// response code when the currently selected mailbox is closed implicitly using the /// SELECT/EXAMINE command on another mailbox. The CLOSED response code serves as a boundary /// between responses for the previously opened mailbox (which was closed) and the newly /// selected mailbox; all responses before the CLOSED response code relate to the mailbox that /// was closed, and all subsequent responses relate to the newly opened mailbox. /// /// A server that advertises "QRESYNC" or "CONDSTORE" in the capability string must return the /// CLOSED response code in this case, whether or not a CONDSTORE enabling command was issued. /// /// There is no need to return the CLOSED response code on completion of the CLOSE or the /// UNSELECT \[UNSELECT\] command (or similar), whose purpose is to close the currently selected /// mailbox without opening a new one. #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] Closed, /// Additional response codes defined by particular client or server /// implementations SHOULD be prefixed with an "X" until they are /// added to a revision of this protocol. Client implementations /// SHOULD ignore response codes that they do not recognize. /// /// --- /// /// ```abnf /// atom [SP 1*]` /// ``` /// /// Note: We use this as a fallback for everything that was not recognized as /// `Code`. This includes, e.g., variants with missing parameters, etc. Other(CodeOther<'a>), } impl<'a> Code<'a> { pub fn badcharset(allowed: Vec>) -> Self { Self::BadCharset { allowed } } pub fn capability(caps: C) -> Result where C: TryInto>>, { Ok(Self::Capability(caps.try_into()?)) } pub fn permanentflags(flags: Vec>) -> Self { Self::PermanentFlags(flags) } pub fn uidnext(uidnext: u32) -> Result { Ok(Self::UidNext(NonZeroU32::try_from(uidnext)?)) } pub fn uidvalidity(uidnext: u32) -> Result { Ok(Self::UidValidity(NonZeroU32::try_from(uidnext)?)) } pub fn unseen(uidnext: u32) -> Result { Ok(Self::Unseen(NonZeroU32::try_from(uidnext)?)) } } /// An (unknown) code. /// /// It's guaranteed that this type can't represent any code from [`Code`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Eq, Hash, ToStatic)] pub struct CodeOther<'a>(Cow<'a, [u8]>); // We want a more readable `Debug` implementation. impl Debug for CodeOther<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { struct BStr<'a>(&'a Cow<'a, [u8]>); impl Debug for BStr<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, "b\"{}\"", crate::utils::escape_byte_string(self.0.as_ref()) ) } } f.debug_tuple("CodeOther").field(&BStr(&self.0)).finish() } } impl<'a> CodeOther<'a> { /// Constructs an unsupported code without validation. /// /// # Warning: IMAP conformance /// /// The caller must ensure that `data` is valid. Failing to do so may create invalid/unparsable /// IMAP messages, or even produce unintended protocol flows. Do not call this constructor with /// untrusted data. pub fn unvalidated(data: D) -> Self where D: Into>, { Self(data.into()) } pub fn inner(&self) -> &[u8] { self.0.as_ref() } } #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[non_exhaustive] pub enum Capability<'a> { Imap4Rev1, Auth(AuthMechanism<'a>), LoginDisabled, #[cfg(feature = "starttls")] #[cfg_attr(docsrs, doc(cfg(feature = "starttls")))] StartTls, /// See RFC 2177. Idle, /// See RFC 2193. #[cfg(feature = "ext_mailbox_referrals")] #[cfg_attr(docsrs, doc(cfg(feature = "ext_mailbox_referrals")))] MailboxReferrals, /// See RFC 2221. #[cfg(feature = "ext_login_referrals")] #[cfg_attr(docsrs, doc(cfg(feature = "ext_login_referrals")))] LoginReferrals, SaslIr, /// See RFC 5161. Enable, Compress { algorithm: CompressionAlgorithm, }, /// See RFC 2087 and RFC 9208 Quota, /// See RFC 9208. QuotaRes(Resource<'a>), /// See RFC 9208. QuotaSet, /// See RFC 7888. LiteralPlus, LiteralMinus, /// See RFC 6851. Move, #[cfg(feature = "ext_id")] /// See RFC 2971. Id, /// See RFC 3691. Unselect, Sort(Option>), Thread(ThreadingAlgorithm<'a>), #[cfg(feature = "ext_metadata")] /// Server supports (both) server annotations and mailbox annotations. Metadata, #[cfg(feature = "ext_metadata")] /// Server supports (only) server annotations. MetadataServer, /// IMAP4 Binary Content Extension Binary, /// UIDPLUS extension (RFC 4351) UidPlus, /// CONDSTORE extension (RFC 7162) #[cfg(feature = "ext_condstore_qresync")] CondStore, /// QRESYNC extension (RFC 7162) #[cfg(feature = "ext_condstore_qresync")] QResync, /// NAMESPACE extension (RFC 2342) #[cfg(feature = "ext_namespace")] Namespace, #[cfg(feature = "ext_utf8")] Utf8(Utf8Kind), /// Other/Unknown Other(CapabilityOther<'a>), } impl Display for Capability<'_> { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { Self::Imap4Rev1 => write!(f, "IMAP4REV1"), Self::Auth(mechanism) => write!(f, "AUTH={mechanism}"), Self::LoginDisabled => write!(f, "LOGINDISABLED"), #[cfg(feature = "starttls")] Self::StartTls => write!(f, "STARTTLS"), #[cfg(feature = "ext_mailbox_referrals")] Self::MailboxReferrals => write!(f, "MAILBOX-REFERRALS"), #[cfg(feature = "ext_login_referrals")] Self::LoginReferrals => write!(f, "LOGIN-REFERRALS"), Self::SaslIr => write!(f, "SASL-IR"), Self::Idle => write!(f, "IDLE"), Self::Enable => write!(f, "ENABLE"), Self::Compress { algorithm } => write!(f, "COMPRESS={algorithm}"), Self::Quota => write!(f, "QUOTA"), Self::QuotaRes(resource) => write!(f, "QUOTA=RES-{resource}"), Self::QuotaSet => write!(f, "QUOTASET"), Self::LiteralPlus => write!(f, "LITERAL+"), Self::LiteralMinus => write!(f, "LITERAL-"), Self::Move => write!(f, "MOVE"), #[cfg(feature = "ext_id")] Self::Id => write!(f, "ID"), Self::Unselect => write!(f, "UNSELECT"), Self::Sort(None) => write!(f, "SORT"), Self::Sort(Some(algorithm)) => write!(f, "SORT={algorithm}"), Self::Thread(algorithm) => write!(f, "THREAD={algorithm}"), #[cfg(feature = "ext_metadata")] Self::Metadata => write!(f, "METADATA"), #[cfg(feature = "ext_metadata")] Self::MetadataServer => write!(f, "METADATA-SERVER"), Self::Binary => write!(f, "BINARY"), Self::UidPlus => write!(f, "UIDPLUS"), #[cfg(feature = "ext_condstore_qresync")] Self::CondStore => write!(f, "CONDSTORE"), #[cfg(feature = "ext_condstore_qresync")] Self::QResync => write!(f, "QRESYNC"), #[cfg(feature = "ext_namespace")] Self::Namespace => write!(f, "NAMESPACE"), #[cfg(feature = "ext_utf8")] Self::Utf8(kind) => write!(f, "UTF8={kind}"), Self::Other(other) => write!(f, "{}", other.0), } } } impl_try_from!(Atom<'a>, 'a, &'a [u8], Capability<'a>); impl_try_from!(Atom<'a>, 'a, Vec, Capability<'a>); impl_try_from!(Atom<'a>, 'a, &'a str, Capability<'a>); impl_try_from!(Atom<'a>, 'a, String, Capability<'a>); impl<'a> From> for Capability<'a> { fn from(atom: Atom<'a>) -> Self { fn split_once_cow<'a>( cow: Cow<'a, str>, pattern: &str, ) -> Option<(Cow<'a, str>, Cow<'a, str>)> { match cow { Cow::Borrowed(str) => { if let Some((left, right)) = str.split_once(pattern) { return Some((Cow::Borrowed(left), Cow::Borrowed(right))); } None } Cow::Owned(string) => { // TODO(efficiency) if let Some((left, right)) = string.split_once(pattern) { return Some((Cow::Owned(left.to_owned()), Cow::Owned(right.to_owned()))); } None } } } let cow = atom.into_inner(); match cow.to_ascii_lowercase().as_ref() { "imap4rev1" => Self::Imap4Rev1, "logindisabled" => Self::LoginDisabled, #[cfg(feature = "starttls")] "starttls" => Self::StartTls, "idle" => Self::Idle, #[cfg(feature = "ext_mailbox_referrals")] "mailbox-referrals" => Self::MailboxReferrals, #[cfg(feature = "ext_login_referrals")] "login-referrals" => Self::LoginReferrals, "sasl-ir" => Self::SaslIr, "enable" => Self::Enable, "quota" => Self::Quota, "quotaset" => Self::QuotaSet, "literal+" => Self::LiteralPlus, "literal-" => Self::LiteralMinus, "move" => Self::Move, #[cfg(feature = "ext_id")] "id" => Self::Id, "sort" => Self::Sort(None), #[cfg(feature = "ext_metadata")] "metadata" => Self::Metadata, #[cfg(feature = "ext_metadata")] "metadata-server" => Self::MetadataServer, "binary" => Self::Binary, "unselect" => Self::Unselect, #[cfg(feature = "ext_condstore_qresync")] "condstore" => Self::Unselect, #[cfg(feature = "ext_condstore_qresync")] "qresync" => Self::Unselect, "uidplus" => Self::UidPlus, #[cfg(feature = "ext_utf8")] "utf8=accept" => Self::Utf8(Utf8Kind::Accept), #[cfg(feature = "ext_utf8")] "utf8=only" => Self::Utf8(Utf8Kind::Only), _ => { // TODO(efficiency) if let Some((left, right)) = split_once_cow(cow.clone(), "=") { match left.as_ref().to_ascii_lowercase().as_ref() { "auth" => { if let Ok(mechanism) = AuthMechanism::try_from(right) { return Self::Auth(mechanism); } } "compress" => { if let Ok(atom) = Atom::try_from(right) { if let Ok(algorithm) = CompressionAlgorithm::try_from(atom) { return Self::Compress { algorithm }; } } } "quota" => { if let Some((_, right)) = right.as_ref().to_ascii_lowercase().split_once("res-") { // TODO(efficiency) if let Ok(resource) = Resource::try_from(right.to_owned()) { return Self::QuotaRes(resource); } } } "sort" => { if let Ok(atom) = Atom::try_from(right) { return Self::Sort(Some(SortAlgorithm::from(atom))); } } "thread" => { if let Ok(atom) = Atom::try_from(right) { return Self::Thread(ThreadingAlgorithm::from(atom)); } } _ => {} } } Self::Other(CapabilityOther(Atom(cow))) } } } } /// An (unknown) capability. /// /// It's guaranteed that this type can't represent any capability from [`Capability`]. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct CapabilityOther<'a>(Atom<'a>); /// Error-related types. pub mod error { use thiserror::Error; #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum ContinueError { #[error("invalid text")] Text(T), #[error("ambiguity detected")] Ambiguity, } #[derive(Clone, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] pub enum FetchError { #[error("Invalid sequence or UID: {0:?}")] SeqOrUid(S), #[error("Invalid items: {0:?}")] InvalidItems(I), } } #[cfg(test)] mod tests { use super::*; #[test] fn test_conversion_data() { let _ = Data::capability(vec![Capability::Imap4Rev1]).unwrap(); let _ = Data::fetch(1, vec![MessageDataItem::Rfc822Size(123)]).unwrap(); } #[test] fn test_conversion_continue_failing() { let tests = [ CommandContinuationRequest::basic(None, ""), CommandContinuationRequest::basic(Some(Code::ReadWrite), ""), ]; for test in tests { println!("{test:?}"); assert!(test.is_err()); } } #[cfg(feature = "serde")] #[test] fn test_deserialization_command_continuation_request_basic() { let valid_input = r#"{ "text": "Ready for additional command text" }"#; let invalid_input = r#"{ "text": "[Ready for additional command text" }"#; let request = serde_json::from_str::(valid_input) .expect("valid input should deserialize successfully"); assert_eq!( request, CommandContinuationRequestBasic { code: None, text: Text(Cow::Borrowed("Ready for additional command text")) } ); let err = serde_json::from_str::(invalid_input) .expect_err("invalid input should not deserialize successfully"); assert_eq!(err.to_string(), r"ambiguity detected"); } } duesee-imap-codec-0d00966/imap-types/src/search.rs000066400000000000000000000126571507724125200217470ustar00rootroot00000000000000//! Search-related types. use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "ext_condstore_qresync")] use crate::extensions::condstore_qresync::{AttributeFlag, EntryTypeReq}; use crate::{ core::{AString, Atom, Vec1}, datetime::NaiveDate, sequence::SequenceSet, }; /// The defined search keys. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum SearchKey<'a> { // // // IMAP doesn't have a dedicated AND operator in its search syntax. // ANDing multiple search keys works by concatenating them with an ascii space. // Introducing this variant makes sense, because // * it may help in understanding the RFC // * and it can be used to distinguish between a single search key // and multiple search keys. // // See also the corresponding `search` parser. And(Vec1>), /// Messages with message sequence numbers corresponding to the /// specified message sequence number set. SequenceSet(SequenceSet), /// All messages in the mailbox; the default initial key for ANDing. All, /// Messages with the \Answered flag set. Answered, /// Messages that contain the specified string in the envelope /// structure's BCC field. Bcc(AString<'a>), /// Messages whose internal date (disregarding time and timezone) /// is earlier than the specified date. Before(NaiveDate), /// Messages that contain the specified string in the body of the /// message. Body(AString<'a>), /// Messages that contain the specified string in the envelope /// structure's CC field. Cc(AString<'a>), /// Messages with the \Deleted flag set. Deleted, /// Messages with the \Draft flag set. Draft, /// Messages with the \Flagged flag set. Flagged, /// Messages that contain the specified string in the envelope /// structure's FROM field. From(AString<'a>), /// Messages that have a header with the specified field-name (as /// defined in [RFC-2822]) and that contains the specified string /// in the text of the header (what comes after the colon). If the /// string to search is zero-length, this matches all messages that /// have a header line with the specified field-name regardless of /// the contents. Header(AString<'a>, AString<'a>), /// Messages with the specified keyword flag set. Keyword(Atom<'a>), /// Messages with an [RFC-2822] size larger than the specified /// number of octets. Larger(u32), /// Messages that have the \Recent flag set but not the \Seen flag. /// This is functionally equivalent to "(RECENT UNSEEN)". New, /// Messages that do not match the specified search key. Not(Box>), /// Messages that do not have the \Recent flag set. This is /// functionally equivalent to "NOT RECENT" (as opposed to "NOT /// NEW"). Old, /// Messages whose internal date (disregarding time and timezone) /// is within the specified date. On(NaiveDate), /// Messages that match either search key. Or(Box>, Box>), /// Messages that have the \Recent flag set. Recent, /// Messages that have the \Seen flag set. Seen, /// Messages whose [RFC-2822] Date: header (disregarding time and /// timezone) is earlier than the specified date. SentBefore(NaiveDate), /// Messages whose [RFC-2822] Date: header (disregarding time and /// timezone) is within the specified date. SentOn(NaiveDate), /// Messages whose [RFC-2822] Date: header (disregarding time and /// timezone) is within or later than the specified date. SentSince(NaiveDate), /// Messages whose internal date (disregarding time and timezone) /// is within or later than the specified date. Since(NaiveDate), /// Messages with an [RFC-2822] size smaller than the specified /// number of octets. Smaller(u32), /// Messages that contain the specified string in the envelope /// structure's SUBJECT field. Subject(AString<'a>), /// Messages that contain the specified string in the header or /// body of the message. Text(AString<'a>), /// Messages that contain the specified string in the envelope /// structure's TO field. To(AString<'a>), /// Messages with unique identifiers corresponding to the specified /// unique identifier set. Sequence set ranges are permitted. Uid(SequenceSet), /// Messages that do not have the \Answered flag set. Unanswered, /// Messages that do not have the \Deleted flag set. Undeleted, /// Messages that do not have the \Draft flag set. Undraft, /// Messages that do not have the \Flagged flag set. Unflagged, /// Messages that do not have the specified keyword flag set. Unkeyword(Atom<'a>), /// Messages that do not have the \Seen flag set. Unseen, #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg("ext_condstore_qresync")))] ModSequence { entry: Option<(AttributeFlag<'a>, EntryTypeReq)>, modseq: u64, }, } impl SearchKey<'_> { pub fn uid(sequence_set: S) -> Self where S: Into, { Self::Uid(sequence_set.into()) } } duesee-imap-codec-0d00966/imap-types/src/secret.rs000066400000000000000000000076321507724125200217640ustar00rootroot00000000000000//! Handling of secret values. //! //! This module provides a `Secret` ensuring that sensitive values are not //! `Debug`-printed by accident. use std::fmt::{Debug, Formatter}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// A wrapper to ensure that secrets are redacted during `Debug`-printing. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Clone, Eq, Hash, PartialEq, ToStatic)] pub struct Secret(T); impl Secret { /// Crate a new secret. pub fn new(inner: T) -> Self { Self(inner) } /// Expose the inner secret. pub fn declassify(&self) -> &T { &self.0 } } impl From for Secret { fn from(value: T) -> Self { Self::new(value) } } impl Debug for Secret where T: Debug, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { #[cfg(not(debug_assertions))] return write!(f, "/* REDACTED */"); #[cfg(debug_assertions)] return self.0.fmt(f); } } #[cfg(test)] mod tests { use crate::{ command::{Command, CommandBody}, core::{AString, Atom, Literal, Quoted}, }; #[test] #[cfg(not(debug_assertions))] #[allow(clippy::redundant_clone)] fn test_that_secret_is_redacted() { use super::Secret; use crate::auth::{AuthMechanism, AuthenticateData}; let secret = Secret("xyz123"); let got = format!("{:?}", secret); println!("{}", got); assert!(!got.contains("xyz123")); println!("-----"); let tests = vec![ CommandBody::login("alice", "xyz123") .unwrap() .tag("A") .unwrap(), CommandBody::authenticate_with_ir(AuthMechanism::Plain, b"xyz123".as_ref()) .tag("A") .unwrap(), ]; for test in tests.into_iter() { let got = format!("{:?}", test); println!("Debug: {}", got); assert!(got.contains("/* REDACTED */")); assert!(!got.contains("xyz123")); assert!(!got.contains("eHl6MTIz")); println!(); } println!("-----"); let tests = [ AuthenticateData::r#continue(b"xyz123".to_vec()), AuthenticateData::r#continue(b"xyz123".to_vec()), ]; for test in tests { let got = format!("{:?}", test); println!("Debug: {}", got); assert!(got.contains("/* REDACTED */")); assert!(!got.contains("xyz123")); assert!(!got.contains("eHl6MTIz")); } } #[test] fn test_that_secret_has_no_side_effects_on_eq() { assert_ne!( Command::new( "A", CommandBody::login( AString::from(Atom::try_from("user").unwrap()), AString::from(Atom::try_from("pass").unwrap()), ) .unwrap(), ), Command::new( "A", CommandBody::login( AString::from(Atom::try_from("user").unwrap()), AString::from(Quoted::try_from("pass").unwrap()), ) .unwrap(), ) ); assert_ne!( Command::new( "A", CommandBody::login( Literal::try_from("").unwrap(), Literal::try_from("A").unwrap(), ) .unwrap(), ), Command::new( "A", CommandBody::login( Literal::try_from("").unwrap(), Literal::try_from("A").unwrap().into_non_sync(), ) .unwrap(), ) ); } } duesee-imap-codec-0d00966/imap-types/src/sequence.rs000066400000000000000000000625021507724125200223040ustar00rootroot00000000000000use std::{ cmp::max, collections::VecDeque, fmt::Debug, iter::Rev, num::NonZeroU32, ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, str::FromStr, }; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{ core::Vec1, error::{ValidationError, ValidationErrorKind}, }; pub const ONE: NonZeroU32 = match NonZeroU32::new(1) { Some(one) => one, None => panic!(), }; pub const MIN: NonZeroU32 = ONE; pub const MAX: NonZeroU32 = match NonZeroU32::new(u32::MAX) { Some(max) => max, None => panic!(), }; #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub struct SequenceSet(pub Vec1); impl From for SequenceSet { fn from(sequence: Sequence) -> Self { Self(Vec1::from(sequence)) } } macro_rules! impl_from_t_for_sequence_set { ($thing:ty) => { impl From<$thing> for SequenceSet { fn from(value: $thing) -> Self { Self::from(Sequence::from(value)) } } }; } macro_rules! impl_try_from_t_for_sequence_set { ($thing:ty) => { impl TryFrom<$thing> for SequenceSet { type Error = ValidationError; fn try_from(value: $thing) -> Result { Ok(Self::from(Sequence::try_from(value)?)) } } }; } impl_from_t_for_sequence_set!(SeqOrUid); impl_from_t_for_sequence_set!(NonZeroU32); impl_from_t_for_sequence_set!(RangeFull); impl_from_t_for_sequence_set!(RangeFrom); impl_try_from_t_for_sequence_set!(RangeTo); impl_from_t_for_sequence_set!(RangeToInclusive); impl_try_from_t_for_sequence_set!(Range); impl_from_t_for_sequence_set!(RangeInclusive); // `SequenceSet::try_from` implementations. impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(sequences: Vec) -> Result { Ok(Self(Vec1::try_from(sequences).map_err(|_| { ValidationError::new(ValidationErrorKind::Empty) })?)) } } impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(sequences: Vec) -> Result { Ok(Self( Vec1::try_from( sequences .into_iter() .map(Sequence::from) .collect::>(), ) .map_err(|_| ValidationError::new(ValidationErrorKind::Empty))?, )) } } impl TryFrom<&str> for SequenceSet { type Error = ValidationError; fn try_from(value: &str) -> Result { value.parse() } } impl FromStr for SequenceSet { type Err = ValidationError; fn from_str(value: &str) -> Result { let mut results = vec![]; for seq in value.split(',') { results.push(Sequence::try_from(seq)?); } Ok(SequenceSet(Vec1::try_from(results).map_err(|_| { ValidationError::new(ValidationErrorKind::Empty) })?)) } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] pub enum Sequence { Single(SeqOrUid), Range(SeqOrUid, SeqOrUid), } impl From for Sequence { fn from(value: SeqOrUid) -> Self { Self::Single(value) } } impl From for Sequence { fn from(value: NonZeroU32) -> Self { Self::Single(SeqOrUid::from(value)) } } impl TryFrom<&str> for Sequence { type Error = ValidationError; fn try_from(value: &str) -> Result { value.parse() } } impl FromStr for Sequence { type Err = ValidationError; fn from_str(value: &str) -> Result { match value.split(':').count() { 0 => Err(ValidationError::new(ValidationErrorKind::Empty)), 1 => Ok(Sequence::Single(SeqOrUid::try_from(value)?)), 2 => { let mut split = value.split(':'); let start = split.next().unwrap(); let end = split.next().unwrap(); Ok(Sequence::Range( SeqOrUid::try_from(start)?, SeqOrUid::try_from(end)?, )) } _ => Err(ValidationError::new(ValidationErrorKind::Invalid)), } } } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, ToStatic)] pub enum SeqOrUid { Value(NonZeroU32), Asterisk, } impl From for SeqOrUid { fn from(value: NonZeroU32) -> Self { Self::Value(value) } } macro_rules! impl_try_from_num { ($num:ty) => { impl TryFrom<&[$num]> for SequenceSet { type Error = ValidationError; fn try_from(values: &[$num]) -> Result { let mut checked = Vec::new(); for value in values { checked.push(Sequence::try_from(*value)?); } Self::try_from(checked) } } impl TryFrom<$num> for SequenceSet { type Error = ValidationError; fn try_from(value: $num) -> Result { Ok(Self::from(Sequence::try_from(value)?)) } } impl TryFrom<$num> for Sequence { type Error = ValidationError; fn try_from(value: $num) -> Result { Ok(Self::from(SeqOrUid::try_from(value)?)) } } impl TryFrom<$num> for SeqOrUid { type Error = ValidationError; fn try_from(value: $num) -> Result { #[allow(irrefutable_let_patterns)] if let Ok(value) = u32::try_from(value) { if let Ok(value) = NonZeroU32::try_from(value) { return Ok(Self::Value(value)); } } Err(ValidationError::new(ValidationErrorKind::Invalid)) } } }; } impl_try_from_num!(i8); impl_try_from_num!(i16); impl_try_from_num!(i32); impl_try_from_num!(i64); impl_try_from_num!(isize); impl_try_from_num!(u8); impl_try_from_num!(u16); impl_try_from_num!(u32); impl_try_from_num!(u64); impl_try_from_num!(usize); impl TryFrom<&str> for SeqOrUid { type Error = ValidationError; fn try_from(value: &str) -> Result { value.parse() } } impl FromStr for SeqOrUid { type Err = ValidationError; fn from_str(value: &str) -> Result { if value == "*" { Ok(SeqOrUid::Asterisk) } else { // This is to align parsing here with the IMAP grammar: // Rust's `parse::` function accepts numbers that start with 0. // For example, 00001, is interpreted as 1. But this is not allowed in IMAP. if value.starts_with('0') { Err(ValidationError::new(ValidationErrorKind::Invalid)) } else { Ok(SeqOrUid::Value(NonZeroU32::from_str(value).map_err( |_| ValidationError::new(ValidationErrorKind::Invalid), )?)) } } } } // ------------------------------------------------------------------------------------------------- macro_rules! impl_try_from_num_range { ($num:ty) => { impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(range: RangeFrom<$num>) -> Result { Ok(Self::from(Sequence::try_from(range)?)) } } impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(range: RangeTo<$num>) -> Result { Ok(Self::from(Sequence::try_from(range)?)) } } impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(range: RangeToInclusive<$num>) -> Result { Ok(Self::from(Sequence::try_from(range)?)) } } impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(range: Range<$num>) -> Result { Ok(Self::from(Sequence::try_from(range)?)) } } impl TryFrom> for SequenceSet { type Error = ValidationError; fn try_from(range: RangeInclusive<$num>) -> Result { Ok(Self::from(Sequence::try_from(range)?)) } } // ----------------------------------------------------------------------------------------- impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: RangeFrom<$num>) -> Result { Ok(Self::Range( SeqOrUid::try_from(range.start)?, SeqOrUid::Asterisk, )) } } impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: RangeTo<$num>) -> Result { Ok(Self::Range( SeqOrUid::from(ONE), SeqOrUid::try_from(range.end.saturating_sub(1))?, )) } } impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: RangeToInclusive<$num>) -> Result { Ok(Self::Range( SeqOrUid::from(ONE), SeqOrUid::try_from(range.end)?, )) } } impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: Range<$num>) -> Result { Ok(Self::Range( SeqOrUid::try_from(range.start)?, SeqOrUid::try_from(range.end.saturating_sub(1))?, )) } } impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: RangeInclusive<$num>) -> Result { Ok(Self::Range( SeqOrUid::try_from(*range.start())?, SeqOrUid::try_from(*range.end())?, )) } } }; } impl_try_from_num_range!(i8); impl_try_from_num_range!(i16); impl_try_from_num_range!(i32); impl_try_from_num_range!(i64); impl_try_from_num_range!(isize); impl_try_from_num_range!(u8); impl_try_from_num_range!(u16); impl_try_from_num_range!(u32); impl_try_from_num_range!(u64); impl_try_from_num_range!(usize); impl From for Sequence { fn from(_: RangeFull) -> Self { Self::from(MIN..) } } impl From> for Sequence { fn from(range: RangeFrom) -> Self { Self::Range(SeqOrUid::from(range.start), SeqOrUid::Asterisk) } } impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: RangeTo) -> Result { Self::try_from(MIN..range.end) } } impl From> for Sequence { fn from(range: RangeToInclusive) -> Self { Self::from(MIN..=range.end) } } impl TryFrom> for Sequence { type Error = ValidationError; fn try_from(range: Range) -> Result { Ok(Self::Range( SeqOrUid::from(MIN), SeqOrUid::try_from(range.end.get().saturating_sub(1))?, )) } } impl From> for Sequence { fn from(range: RangeInclusive) -> Self { Self::Range(SeqOrUid::from(*range.start()), SeqOrUid::from(*range.end())) } } // ------------------------------------------------------------------------------------------------- impl<'a> SequenceSet { /// Iterate over a sorted, deduplicated set of sequence numbers or UIDs. /// /// # Example /// /// ``` /// use std::num::NonZeroU32; /// /// use imap_types::sequence::SequenceSet; /// /// let seq = SequenceSet::try_from("1:5,10:3,10,10,10,1").unwrap(); /// let largest = NonZeroU32::new(10).unwrap(); /// /// assert_eq!( /// seq.iter(largest).collect::>(), /// [1u32, 2, 3, 4, 5, 6, 7, 8, 9, 10] /// .into_iter() /// .map(|e| NonZeroU32::new(e).unwrap()) /// .collect::>() /// ); /// ``` pub fn iter(&'a self, largest: NonZeroU32) -> impl Iterator + 'a { let ranges = simplify(self.clone(), largest, true); let ranges = cleanup(ranges); SequenceSetIter { ranges, active_range: None, } } /// Iterate over the given set of sequence numbers or UIDs. /// /// Note: This method expands the sequence set keeping duplicates and ordering. /// /// # Example /// /// ``` /// use std::num::NonZeroU32; /// /// use imap_types::sequence::SequenceSet; /// /// let seq = SequenceSet::try_from("1:3,1:3,3:1,1").unwrap(); /// let largest = NonZeroU32::new(10).unwrap(); /// /// assert_eq!( /// seq.iter_naive(largest).collect::>(), /// [1u32, 2, 3, 1, 2, 3, 3, 2, 1, 1] /// .into_iter() /// .map(|e| NonZeroU32::new(e).unwrap()) /// .collect::>() /// ); /// ``` pub fn iter_naive(&'a self, largest: NonZeroU32) -> impl Iterator + 'a { let ranges = simplify(self.clone(), largest, false); SequenceSetIter { ranges, active_range: None, } } } impl SeqOrUid { pub fn expand(&self, largest: NonZeroU32) -> NonZeroU32 { match self { SeqOrUid::Value(value) => *value, SeqOrUid::Asterisk => largest, } } } // ------------------------------------------------------------------------------------------------- struct SequenceSetIter { ranges: VecDeque<(u32, u32)>, active_range: Option, } impl Iterator for SequenceSetIter { type Item = NonZeroU32; fn next(&mut self) -> Option { loop { match self.active_range { Some(ref mut range) => match range.next() { // We know here that `next >= 1`. Some(next) => break Some(NonZeroU32::new(next).unwrap()), None => self.active_range = None, }, None => match self.ranges.pop_front() { Some((a, b)) => { if a <= b { self.active_range = Some(Sorting::Ascending(a..=b)); } else { self.active_range = Some(Sorting::Descending((b..=a).rev())); } } None => break None, }, } } } } enum Sorting { Ascending(RangeInclusive), Descending(Rev>), } impl Iterator for Sorting { type Item = u32; fn next(&mut self) -> Option { match self { Sorting::Ascending(iter) => iter.next(), Sorting::Descending(iter) => iter.next(), } } } // Simplify sequence set into VecDeque<(u32, u32)>: // * Use u32 instead of NonZeroU32 (for internal purposes) // * Expand Single(a) to (a, a) // * Sort Range(a, b) so that a <= b fn simplify(sequence_set: SequenceSet, largest: NonZeroU32, sort: bool) -> VecDeque<(u32, u32)> { sequence_set .0 .0 .into_iter() .map(|seq| match seq { Sequence::Single(a) => (u32::from(a.expand(largest)), u32::from(a.expand(largest))), Sequence::Range(a, b) => { let a = u32::from(a.expand(largest)); let b = u32::from(b.expand(largest)); if sort { if a <= b { (a, b) } else { (b, a) } } else { (a, b) } } }) .collect() } fn cleanup(remaining: VecDeque<(u32, u32)>) -> VecDeque<(u32, u32)> { let mut remaining = { let mut tmp = Vec::from(remaining); tmp.sort(); VecDeque::from(tmp) }; let mut stack = VecDeque::new(); stack.push_back(remaining.pop_front().unwrap()); for (x, y) in remaining.into_iter() { let last = stack.back_mut().unwrap(); if last.0 <= x && x <= last.1.saturating_add(1) { last.1 = max(last.1, y); } else { stack.push_back((x, y)); } } stack } #[cfg(test)] mod tests { use std::num::NonZeroU32; use super::*; use crate::core::Vec1; #[test] fn test_creation_of_sequence_from_u32() { assert_eq!( SequenceSet::try_from(1), Ok(SequenceSet(Vec1::from(Sequence::Single(SeqOrUid::Value( NonZeroU32::new(1).unwrap() ))))) ); assert_eq!( SequenceSet::try_from(0), Err(ValidationError::new(ValidationErrorKind::Invalid)) ); } #[test] fn test_creation_of_sequence_from_range() { // 1:* let range = ..; let seq = Sequence::from(range); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1).unwrap()), SeqOrUid::Asterisk ) ); // 1:* let range = 1..; let seq = Sequence::try_from(range).unwrap(); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1).unwrap()), SeqOrUid::Asterisk ) ); // 1337:* let range = 1337..; let seq = Sequence::try_from(range).unwrap(); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1337).unwrap()), SeqOrUid::Asterisk ) ); // 1:1336 let range = 1..1337; let seq = Sequence::try_from(range).unwrap(); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1).unwrap()), SeqOrUid::Value(NonZeroU32::new(1336).unwrap()) ) ); // 1:1337 let range = 1..=1337; let seq = Sequence::try_from(range).unwrap(); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1).unwrap()), SeqOrUid::Value(NonZeroU32::new(1337).unwrap()) ) ); // 1:1336 let range = ..1337; let seq = Sequence::try_from(range).unwrap(); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1).unwrap()), SeqOrUid::Value(NonZeroU32::new(1336).unwrap()) ) ); // 1:1337 let range = ..=1337; let seq = Sequence::try_from(range).unwrap(); assert_eq!( seq, Sequence::Range( SeqOrUid::Value(NonZeroU32::new(1).unwrap()), SeqOrUid::Value(NonZeroU32::new(1337).unwrap()) ) ); } #[test] fn test_creation_of_sequence_set_from_str_positive() { let tests = &[ ( "1", SequenceSet( vec![Sequence::Single(SeqOrUid::Value(1.try_into().unwrap()))] .try_into() .unwrap(), ), ), ( "1,2,3", SequenceSet( vec![ Sequence::Single(SeqOrUid::Value(1.try_into().unwrap())), Sequence::Single(SeqOrUid::Value(2.try_into().unwrap())), Sequence::Single(SeqOrUid::Value(3.try_into().unwrap())), ] .try_into() .unwrap(), ), ), ( "*", SequenceSet( vec![Sequence::Single(SeqOrUid::Asterisk)] .try_into() .unwrap(), ), ), ( "1:2", SequenceSet( vec![Sequence::Range( SeqOrUid::Value(1.try_into().unwrap()), SeqOrUid::Value(2.try_into().unwrap()), )] .try_into() .unwrap(), ), ), ( "1:2,3", SequenceSet( vec![ Sequence::Range( SeqOrUid::Value(1.try_into().unwrap()), SeqOrUid::Value(2.try_into().unwrap()), ), Sequence::Single(SeqOrUid::Value(3.try_into().unwrap())), ] .try_into() .unwrap(), ), ), ( "1:2,3,*", SequenceSet( vec![ Sequence::Range( SeqOrUid::Value(1.try_into().unwrap()), SeqOrUid::Value(2.try_into().unwrap()), ), Sequence::Single(SeqOrUid::Value(3.try_into().unwrap())), Sequence::Single(SeqOrUid::Asterisk), ] .try_into() .unwrap(), ), ), ]; for (test, expected) in tests.iter() { let got = SequenceSet::try_from(*test).unwrap(); assert_eq!(*expected, got); } } #[test] fn test_creation_of_sequence_set_from_str_negative() { let tests = &[ "", "* ", " *", " * ", "1 ", " 1", " 1 ", "01", " 01", "01 ", " 01 ", "*1", ":", ":*", "*:", "*: ", "1:2:3", ]; for test in tests { let got = SequenceSet::try_from(*test); print!("\"{}\" | {:?} | ", test, got.clone().unwrap_err()); println!("{}", got.unwrap_err()); } } #[test] fn test_iteration_over_some_sequence_sets() { let tests = vec![ ("*", vec![3]), ("1:*", vec![1, 2, 3]), ("5,1:*,2:*", vec![5, 1, 2, 3, 2, 3]), ("*:2", vec![3, 2]), ("*:*", vec![3]), ("4:6,*", vec![4, 5, 6, 3]), ] .into_iter() .map(|(raw, vec)| { ( raw, vec.into_iter() .map(|num| num.try_into().unwrap()) .collect::>(), ) }) .collect::)>>(); for (test, expected) in tests { let seq_set = SequenceSet::try_from(test).unwrap(); let got: Vec = seq_set.iter_naive(3.try_into().unwrap()).collect(); assert_eq!(*expected, got); } } /// See https://github.com/duesee/imap-codec/issues/411 #[test] fn test_issue_411() { let seq = SequenceSet::try_from("22,21,22,*:20").unwrap(); let largest = NonZeroU32::new(23).unwrap(); // Naive { let expected = [22, 21, 22, 23, 22, 21, 20] .map(|n| NonZeroU32::new(n).unwrap()) .to_vec(); let got: Vec<_> = seq.iter_naive(largest).collect(); assert_eq!(expected, got); } // Clean { let expected = [20, 21, 22, 23] .map(|n| NonZeroU32::new(n).unwrap()) .to_vec(); let got: Vec<_> = seq.iter(largest).collect(); assert_eq!(expected, got); } } #[test] fn test_clean() { let tests = vec![ "1", "2", "*", "1:*", "2:*", "*:*", "3,2,1", "3,2,2,2,1,1,1", "3:1,5:1,1:2,1:1", "4:5,5:1,1:2,1:1,*:*,*:10,1:100", ]; for test in tests { let seq = SequenceSet::try_from(test).unwrap(); let largest = NonZeroU32::new(13).unwrap(); let naive = { let mut naive: Vec<_> = seq.iter_naive(largest).collect(); naive.sort(); naive.dedup(); naive }; let clean: Vec<_> = seq.iter(largest).collect(); assert_eq!(naive, clean); } } } duesee-imap-codec-0d00966/imap-types/src/state.rs000066400000000000000000000124151507724125200216120ustar00rootroot00000000000000//! IMAP protocol state. //! //! "Once the connection between client and server is established, an IMAP4rev1 connection is in one of four states. //! The initial state is identified in the server greeting. //! Most commands are only valid in certain states. //! It is a protocol error for the client to attempt a command while the connection is in an inappropriate state, //! and the server will respond with a BAD or NO (depending upon server implementation) command completion result." ([RFC 3501](https://www.rfc-editor.org/rfc/rfc3501.html)) //! //! ```text //! +----------------------+ //! |connection established| //! +----------------------+ //! || //! \/ //! +--------------------------------------+ //! | server greeting | //! +--------------------------------------+ //! || (1) || (2) || (3) //! \/ || || //! +-----------------+ || || //! |Not Authenticated| || || //! +-----------------+ || || //! || (7) || (4) || || //! || \/ \/ || //! || +----------------+ || //! || | Authenticated |<=++ || //! || +----------------+ || || //! || || (7) || (5) || (6) || //! || || \/ || || //! || || +--------+ || || //! || || |Selected|==++ || //! || || +--------+ || //! || || || (7) || //! \/ \/ \/ \/ //! +--------------------------------------+ //! | Logout | //! +--------------------------------------+ //! || //! \/ //! +-------------------------------+ //! |both sides close the connection| //! +-------------------------------+ //! //! (1) connection without pre-authentication (OK greeting) //! (2) pre-authenticated connection (PREAUTH greeting) //! (3) rejected connection (BYE greeting) //! (4) successful LOGIN or AUTHENTICATE command //! (5) successful SELECT or EXAMINE command //! (6) CLOSE command, or failed SELECT or EXAMINE command //! (7) LOGOUT command, server shutdown, or connection closed //! ``` use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::{core::Tag, mailbox::Mailbox}; /// State of the IMAP4rev1 connection. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Clone, Debug, Eq, PartialEq, ToStatic)] pub enum State<'a> { Greeting, /// The client MUST supply authentication credentials before most commands will be permitted. /// This state is entered when a connection starts unless the connection has been pre-authenticated. NotAuthenticated, /// The client is authenticated and MUST select a mailbox to access before commands that affect messages will be permitted. /// This state is entered when a pre-authenticated connection starts, when acceptable authentication credentials have been provided, /// after an error in selecting a mailbox, or after a successful CLOSE command. Authenticated, /// A mailbox has been selected to access. /// This state is entered when a mailbox has been successfully selected. Selected(Mailbox<'a>), /// The connection is being terminated. /// This state can be entered as a result of a client request (via the LOGOUT command) or by unilateral action on the part of either the client or server. /// /// If the client requests the logout state, the server MUST send an untagged BYE response and a tagged OK response to the LOGOUT command before the server closes the connection; /// and the client MUST read the tagged OK response to the LOGOUT command before the client closes the connection. /// /// A server MUST NOT unilaterally close the connection without sending an untagged BYE response that contains the reason for having done so. /// A client SHOULD NOT unilaterally close the connection, and instead SHOULD issue a LOGOUT command. /// If the server detects that the client has unilaterally closed the connection, the server MAY omit the untagged BYE response and simply close its connection. Logout, IdleAuthenticated(Tag<'a>), IdleSelected(Tag<'a>, Mailbox<'a>), } #[cfg(test)] mod tests { use super::*; use crate::{IntoStatic, ToStatic, core::Tag, mailbox::Mailbox}; #[test] fn test_conversion() { let tests = [ State::Greeting, State::NotAuthenticated, State::Authenticated, State::Selected(Mailbox::Inbox), State::Logout, State::IdleAuthenticated(Tag::try_from("A").unwrap()), State::IdleSelected(Tag::try_from("A").unwrap(), Mailbox::Inbox), ]; for _test in tests { { let test_to_static = _test.to_static(); assert_eq!(_test, test_to_static); let test_into_static = _test.into_static(); assert_eq!(test_to_static, test_into_static); } } } } duesee-imap-codec-0d00966/imap-types/src/status.rs000066400000000000000000000053571507724125200220240ustar00rootroot00000000000000use std::num::NonZeroU32; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use bounded_static_derive::ToStatic; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Status data item name used to request a status data item. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[doc(alias = "StatusAttribute")] pub enum StatusDataItemName { /// The number of messages in the mailbox. Messages, /// The number of messages with the \Recent flag set. Recent, /// The next unique identifier value of the mailbox. UidNext, /// The unique identifier validity value of the mailbox. UidValidity, /// The number of messages which do not have the \Seen flag set. Unseen, /// The number of messages with the \Deleted flag set. Deleted, /// The amount of storage space that can be reclaimed by performing EXPUNGE on the mailbox. DeletedStorage, #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg(feature = "ext_condstore_qresync")))] HighestModSeq, } /// Status data item. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type", content = "content"))] #[derive(Debug, Clone, PartialEq, Eq, Hash, ToStatic)] #[doc(alias = "StatusAttributeValue")] pub enum StatusDataItem { /// The number of messages in the mailbox. Messages(u32), /// The number of messages with the \Recent flag set. Recent(u32), /// The next unique identifier value of the mailbox. Refer to /// section 2.3.1.1 for more information. UidNext(NonZeroU32), /// The unique identifier validity value of the mailbox. Refer to /// section 2.3.1.1 for more information. UidValidity(NonZeroU32), /// The number of messages which do not have the \Seen flag set. Unseen(u32), /// The number of messages with the \Deleted flag set. Deleted(u32), /// The amount of storage space that can be reclaimed by performing EXPUNGE on the mailbox. DeletedStorage(u64), #[cfg(feature = "ext_condstore_qresync")] #[cfg_attr(docsrs, doc(cfg(feature = "ext_condstore_qresync")))] /// The highest mod-sequence value of all messages in the mailbox. /// This is the same value that is returned by the server in the HIGHESTMODSEQ response code in /// an OK untagged response (see Section 3.1.2.1). /// /// If the server doesn't support the persistent storage of mod-sequences for the mailbox (see /// Section 3.1.2.2), the server MUST return 0 as the value of the HIGHESTMODSEQ status data item. HighestModSeq(u64), } duesee-imap-codec-0d00966/imap-types/src/utils.rs000066400000000000000000000140151507724125200216300ustar00rootroot00000000000000//! Functions that may come in handy. use std::borrow::Cow; /// Converts bytes into a ready-to-be-printed form. pub fn escape_byte_string(bytes: B) -> String where B: AsRef<[u8]>, { let bytes = bytes.as_ref(); bytes .iter() .map(|byte| match byte { 0x00..=0x08 => format!("\\x{byte:02x}"), 0x09 => String::from("\\t"), 0x0A => String::from("\\n"), 0x0B => format!("\\x{byte:02x}"), 0x0C => format!("\\x{byte:02x}"), 0x0D => String::from("\\r"), 0x0e..=0x1f => format!("\\x{byte:02x}"), 0x20..=0x21 => format!("{}", *byte as char), 0x22 => String::from("\\\""), 0x23..=0x5B => format!("{}", *byte as char), 0x5C => String::from("\\\\"), 0x5D..=0x7E => format!("{}", *byte as char), 0x7f => format!("\\x{byte:02x}"), 0x80..=0xff => format!("\\x{byte:02x}"), }) .collect::>() .join("") } pub mod indicators { /// Any 7-bit US-ASCII character, excluding NUL /// /// CHAR = %x01-7F pub fn is_char(byte: u8) -> bool { matches!(byte, 0x01..=0x7f) } /// Controls /// /// CTL = %x00-1F / %x7F pub fn is_ctl(byte: u8) -> bool { matches!(byte, 0x00..=0x1f | 0x7f) } pub(crate) fn is_any_text_char_except_quoted_specials(byte: u8) -> bool { is_text_char(byte) && !is_quoted_specials(byte) } /// `quoted-specials = DQUOTE / "\"` pub fn is_quoted_specials(byte: u8) -> bool { byte == b'"' || byte == b'\\' } /// `ASTRING-CHAR = ATOM-CHAR / resp-specials` pub fn is_astring_char(i: u8) -> bool { is_atom_char(i) || is_resp_specials(i) } /// `ATOM-CHAR = ` pub fn is_atom_char(b: u8) -> bool { is_char(b) && !is_atom_specials(b) } /// `atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials` pub fn is_atom_specials(i: u8) -> bool { match i { b'(' | b')' | b'{' | b' ' => true, c if is_ctl(c) => true, c if is_list_wildcards(c) => true, c if is_quoted_specials(c) => true, c if is_resp_specials(c) => true, _ => false, } } /// `list-wildcards = "%" / "*"` pub fn is_list_wildcards(i: u8) -> bool { i == b'%' || i == b'*' } #[inline] /// `resp-specials = "]"` pub fn is_resp_specials(i: u8) -> bool { i == b']' } #[inline] /// `CHAR8 = %x01-ff` /// /// Any OCTET except NUL, %x00 pub fn is_char8(i: u8) -> bool { i != 0 } /// `TEXT-CHAR = %x01-09 / %x0B-0C / %x0E-7F` /// /// Note: This was `` before. pub fn is_text_char(c: u8) -> bool { matches!(c, 0x01..=0x09 | 0x0b..=0x0c | 0x0e..=0x7f) } /// `list-char = ATOM-CHAR / list-wildcards / resp-specials` pub fn is_list_char(i: u8) -> bool { is_atom_char(i) || is_list_wildcards(i) || is_resp_specials(i) } } pub fn escape_quoted(unescaped: &str) -> Cow<'_, str> { let mut escaped = Cow::Borrowed(unescaped); if escaped.contains('\\') { escaped = Cow::Owned(escaped.replace('\\', "\\\\")); } if escaped.contains('\"') { escaped = Cow::Owned(escaped.replace('"', "\\\"")); } escaped } pub fn unescape_quoted(escaped: &str) -> Cow<'_, str> { let mut unescaped = Cow::Borrowed(escaped); if unescaped.contains("\\\\") { unescaped = Cow::Owned(unescaped.replace("\\\\", "\\")); } if unescaped.contains("\\\"") { unescaped = Cow::Owned(unescaped.replace("\\\"", "\"")); } unescaped } #[cfg(test)] mod tests { use super::*; #[test] fn test_escape_quoted() { let tests = [ ("", ""), ("\\", "\\\\"), ("\"", "\\\""), ("alice", "alice"), ("\\alice\\", "\\\\alice\\\\"), ("alice\"", "alice\\\""), (r#"\alice\ ""#, r#"\\alice\\ \""#), ]; for (test, expected) in tests { let got = escape_quoted(test); assert_eq!(expected, got); } } #[test] fn test_unescape_quoted() { let tests = [ ("", ""), ("\\\\", "\\"), ("\\\"", "\""), ("alice", "alice"), ("\\\\alice\\\\", "\\alice\\"), ("alice\\\"", "alice\""), (r#"\\alice\\ \""#, r#"\alice\ ""#), ]; for (test, expected) in tests { let got = unescape_quoted(test); assert_eq!(expected, got); } } #[test] fn test_that_unescape_is_inverse_of_escape() { let input = "\\\"\\¹²³abc_*:;059^$%§!\""; assert_eq!(input, unescape_quoted(escape_quoted(input).as_ref())); } #[test] fn test_escape_byte_string() { for byte in 0u8..=255 { let got = escape_byte_string([byte]); if byte.is_ascii_alphanumeric() { assert_eq!((byte as char).to_string(), got.to_string()); } else if byte.is_ascii_whitespace() { if byte == b'\t' { assert_eq!(String::from("\\t"), got); } else if byte == b'\n' { assert_eq!(String::from("\\n"), got); } } else if byte.is_ascii_punctuation() { if byte == b'\\' { assert_eq!(String::from("\\\\"), got); } else if byte == b'"' { assert_eq!(String::from("\\\""), got); } else { assert_eq!((byte as char).to_string(), got); } } else { assert_eq!(format!("\\x{byte:02x}"), got); } } let tests = [(b"Hallo \"\\\x00", String::from(r#"Hallo \"\\\x00"#))]; for (test, expected) in tests { let got = escape_byte_string(test); assert_eq!(expected, got); } } } duesee-imap-codec-0d00966/imap-types/tests/000077500000000000000000000000001507724125200204745ustar00rootroot00000000000000duesee-imap-codec-0d00966/imap-types/tests/api.rs000066400000000000000000000207601507724125200216200ustar00rootroot00000000000000use std::fmt::{Debug, Display}; use imap_types::{ command::{Command, CommandBody, error::LoginError}, core::{AString, Atom, AtomExt, Charset, IString, Literal, NString, Quoted, Tag, Text}, mailbox::{Mailbox, MailboxOther}, response::Data, sequence::{MAX, MIN, SeqOrUid, Sequence, SequenceSet}, }; macro_rules! test_conversions { // Unvalidated (y, $try_from:tt, $from:tt, $as_ref:tt, $object:ty, $sample:expr) => {{ let object = <$object>::unvalidated($sample); let _ = object.as_ref(); test_conversions!($try_from, $from, $as_ref, $object, $sample); }}; (n, $try_from:tt, $from:tt, $as_ref:tt, $object:ty, $sample:expr) => {{ test_conversions!($try_from, $from, $as_ref, $object, $sample); }}; // TryFrom (y, $from:tt, $as_ref:tt, $object:ty, $sample:expr) => {{ let _ = <$object>::try_from($sample).unwrap(); let _ = <$object>::try_from($sample.to_owned()).unwrap(); let _ = <$object>::try_from($sample.as_bytes()).unwrap(); let _ = <$object>::try_from($sample.as_bytes().to_vec()).unwrap(); test_conversions!($from, $as_ref, $object, $sample); }}; (n, $from:tt, $as_ref:tt, $object:ty, $sample:expr) => {{ test_conversions!($from, $as_ref, $object, $sample); }}; // From (y, $as_ref:tt, $object:ty, $sample:expr) => {{ let _ = <$object>::from($sample); test_conversions!($as_ref, $object, $sample); }}; (n, $as_ref:tt, $object:ty, $sample:expr) => {{ test_conversions!($as_ref, $object, $sample); }}; // AsRef (y, $object:ty, $sample:expr) => {{ let object = <$object>::try_from($sample).unwrap(); let _ = object.as_ref(); // ... }}; (n, $object:ty, $sample:expr) => {{ // ... }}; } #[test] fn test_constructions() { // Unvalidated | TryFrom | From | AsRef | Type | Sample test_conversions!(y, y, n, y, Tag, "tag"); test_conversions!(y, y, n, y, Text, "text"); // -------------------------------------------- test_conversions!(n, y, n, y, AString, "astring"); test_conversions!(y, y, n, y, Atom, "atom"); test_conversions!(y, y, n, y, AtomExt, "atomext"); test_conversions!(n, y, n, y, IString, "istring"); test_conversions!(y, y, n, y, Quoted, "quoted"); test_conversions!(n, y, n, y, Literal, "literal"); test_conversions!(n, y, n, n, NString, "nstring"); // -------------------------------------------- test_conversions!(n, y, n, n, Mailbox, "mailbox"); test_conversions!(n, y, n, y, MailboxOther, "mailbox"); // -------------------------------------------- test_conversions!(n, y, n, y, Charset, "charset"); } #[test] fn test_construction_of_command() { trait DisplayDebug: Display + Debug {} impl DisplayDebug for T where T: Display + Debug {} match CommandBody::login("\x00", "") { Err(LoginError::Username(e)) => println!("Oops, bad username: {e}"), Err(LoginError::Password(e)) => println!("Oops, bad password: {e:?}"), _ => {} } let tests: Vec> = vec![ Box::new(Command::new(b"".as_ref(), CommandBody::Noop).unwrap_err()), Box::new(Command::new(b"A ".as_ref(), CommandBody::Noop).unwrap_err()), Box::new(Command::new(b"\xff".as_ref(), CommandBody::Noop).unwrap_err()), Box::new("---"), Box::new(Command::new("", CommandBody::Noop).unwrap_err()), Box::new(Command::new("A ", CommandBody::Noop).unwrap_err()), Box::new("---"), Box::new(Command::new(String::from(""), CommandBody::Noop).unwrap_err()), Box::new(Command::new(String::from("A "), CommandBody::Noop).unwrap_err()), Box::new("---"), Box::new(Command::new(Vec::from(b"".as_ref()), CommandBody::Noop).unwrap_err()), Box::new(Command::new(Vec::from(b"\xff".as_ref()), CommandBody::Noop).unwrap_err()), Box::new("---"), Box::new(Atom::try_from("").unwrap_err()), Box::new(Atom::try_from("²").unwrap_err()), Box::new("---"), Box::new(AtomExt::try_from("").unwrap_err()), Box::new(AtomExt::try_from("²").unwrap_err()), Box::new("---"), Box::new(CommandBody::login("\x00", "").unwrap_err()), Box::new(CommandBody::login("", b"\x00".as_ref()).unwrap_err()), Box::new("---"), Box::new(Data::capability(vec![]).unwrap_err()), ]; for test in tests.into_iter() { println!("{test:?} // {test}"); } } #[test] fn test_construction_of_sequence_etc() { // # From // ## SequenceSet let _ = SequenceSet::from(MIN); let _ = SequenceSet::from(MAX); let _ = SequenceSet::from(..); let _ = SequenceSet::from(MIN..); let _ = SequenceSet::try_from(MIN..MAX).unwrap(); let _ = SequenceSet::from(MIN..=MAX); let _ = SequenceSet::try_from(..MAX).unwrap(); let _ = SequenceSet::from(MIN..=MAX); // ## Sequence let _ = Sequence::from(MIN); let _ = Sequence::from(MAX); let _ = Sequence::from(..); let _ = Sequence::from(MIN..); let _ = Sequence::try_from(MIN..MAX).unwrap(); let _ = Sequence::from(MIN..=MAX); let _ = Sequence::try_from(..MAX).unwrap(); let _ = Sequence::from(MIN..=MAX); // ## SeqOrUid let _ = SeqOrUid::from(MIN); let _ = SeqOrUid::from(MAX); macro_rules! try_from { ($min:literal, $max:literal) => { let _ = SequenceSet::try_from($min).unwrap(); let _ = SequenceSet::try_from($max).unwrap(); let _ = SequenceSet::try_from(..).unwrap(); let _ = SequenceSet::try_from($min..).unwrap(); let _ = SequenceSet::try_from($min..$max).unwrap(); let _ = SequenceSet::try_from(..$max).unwrap(); let _ = SequenceSet::try_from($min..$max).unwrap(); let _ = Sequence::try_from($min).unwrap(); let _ = Sequence::try_from($max).unwrap(); let _ = Sequence::try_from(..).unwrap(); let _ = Sequence::try_from($min..).unwrap(); let _ = Sequence::try_from($min..$max).unwrap(); let _ = Sequence::try_from(..$max).unwrap(); let _ = Sequence::try_from($min..$max).unwrap(); let _ = SeqOrUid::try_from($min).unwrap(); let _ = SeqOrUid::try_from($max).unwrap(); }; } try_from!(1i8, 127i8); try_from!(1i16, 32_767i16); try_from!(1i32, 2_147_483_647i32); try_from!(1i64, 2_147_483_647i64); try_from!(1isize, 2_147_483_647isize); try_from!(1u8, 255u8); try_from!(1u16, 65_535u16); try_from!(1u32, 4_294_967_295u32); try_from!(1u64, 4_294_967_295u64); try_from!(1usize, 4_294_967_295usize); macro_rules! try_from_fail_zero { ($min:literal, $max:literal) => { let _ = SequenceSet::try_from($min).unwrap_err(); let _ = SequenceSet::try_from($min..).unwrap_err(); let _ = SequenceSet::try_from($min..$max).unwrap_err(); let _ = SequenceSet::try_from($min..$max).unwrap_err(); let _ = Sequence::try_from($min).unwrap_err(); let _ = Sequence::try_from($min..).unwrap_err(); let _ = Sequence::try_from($min..$max).unwrap_err(); let _ = Sequence::try_from($min..$max).unwrap_err(); let _ = SeqOrUid::try_from($min).unwrap_err(); }; } try_from_fail_zero!(0i8, 127i8); try_from_fail_zero!(0i16, 32_767i16); try_from_fail_zero!(0i32, 2_147_483_647i32); try_from_fail_zero!(0i64, 2_147_483_647i64); try_from_fail_zero!(0isize, 2_147_483_647isize); try_from_fail_zero!(0u8, 255u8); try_from_fail_zero!(0u16, 65_535u16); try_from_fail_zero!(0u32, 4_294_967_295u32); try_from_fail_zero!(0u64, 4_294_967_295u64); try_from_fail_zero!(0usize, 4_294_967_295usize); macro_rules! try_from_fail_max { ($min:literal, $max:literal) => { let _ = SequenceSet::try_from($max).unwrap_err(); let _ = SequenceSet::try_from($min..$max).unwrap_err(); let _ = SequenceSet::try_from(..$max).unwrap_err(); let _ = SequenceSet::try_from($min..$max).unwrap_err(); let _ = Sequence::try_from($max).unwrap_err(); let _ = Sequence::try_from($min..$max).unwrap_err(); let _ = Sequence::try_from(..$max).unwrap_err(); let _ = Sequence::try_from($min..$max).unwrap_err(); let _ = SeqOrUid::try_from($max).unwrap_err(); }; } try_from_fail_max!(1i64, 9_223_372_036_854_775_807i64); try_from_fail_max!(1u64, 18_446_744_073_709_551_615u64); } duesee-imap-codec-0d00966/imap-types/tests/readme.rs000066400000000000000000000016231507724125200223010ustar00rootroot00000000000000use imap_types::{ command::{Command, CommandBody}, core::{AString, Atom, Literal, Tag}, secret::Secret, }; #[test] fn test_readme() { Command::new("A1", CommandBody::login("alice", "password").unwrap()).unwrap(); Command::new( "A1", CommandBody::login("alice\"", b"\xCA\xFE".as_ref()).unwrap(), ) .unwrap(); Command::new( "A1", CommandBody::login(Literal::try_from("alice").unwrap(), "password").unwrap(), ) .unwrap(); let tag = Tag::try_from("A1").unwrap(); let _ = Command { tag, body: CommandBody::Login { username: AString::from(Atom::unvalidated("alice")), password: Secret::new(AString::from(Atom::unvalidated("password"))), }, }; } #[test] #[should_panic] fn test_readme_failing() { Command::new("A1", CommandBody::login("alice\x00", "password").unwrap()).unwrap(); } duesee-imap-codec-0d00966/justfile000066400000000000000000000174061507724125200170220ustar00rootroot00000000000000export RUSTFLAGS := "-D warnings" export RUSTDOCFLAGS := "-D warnings" msrv := `sed -rn 's|^rust-version = \"(.*)\"$|\1|p' Cargo.toml` [private] default: just -l --unsorted ########### ### RUN ### ########### # Run (local) CI ci: (ci_impl "" "" ) \ (ci_impl "" " --all-features") \ (ci_impl " --release" "" ) \ (ci_impl " --release" " --all-features") [private] ci_impl mode features: (check_impl mode features) (test_impl mode features) # Check syntax, formatting, clippy, deny, semver, ... check: (check_impl "" "" ) \ (check_impl "" " --all-features") \ (check_impl " --release" "" ) \ (check_impl " --release" " --all-features") [private] check_impl mode features: (cargo_check mode features) \ (cargo_hack mode) \ cargo_fmt \ (cargo_clippy mode features) \ cargo_deny \ cargo_semver [private] cargo_check mode features: cargo check --workspace --all-targets --exclude imap-codec-bench{{ mode }}{{ features }} cargo doc --no-deps --document-private-items --keep-going{{ mode }}{{ features }} [private] cargo_hack mode: install_cargo_hack cargo hack check --workspace --all-targets --exclude imap-codec-bench{{ mode }} cargo hack check -p imap-codec \ --no-dev-deps \ --exclude-features default \ --feature-powerset \ --group-features \ arbitrary,\ arbitrary_simplified,\ serde,\ tag_generator \ --group-features \ starttls,\ ext_condstore_qresync,\ ext_id,\ ext_login_referrals,\ ext_mailbox_referrals,\ ext_metadata,\ ext_namespace,\ ext_utf8 \ --group-features \ quirk_crlf_relaxed,\ quirk_id_empty_to_nil,\ quirk_missing_text,\ quirk_rectify_numbers,\ quirk_excessive_space_quota_resource,\ quirk_trailing_space_capability,\ quirk_trailing_space_id,\ quirk_trailing_space_search,\ quirk_trailing_space_status,\ quirk_spaces_between_addresses,\ quirk_empty_continue_req,\ quirk_body_fld_enc_nil_to_empty\ {{ mode }} cargo hack check -p imap-types \ --no-dev-deps \ --feature-powerset \ --group-features \ arbitrary,\ arbitrary_simplified,\ serde,\ tag_generator \ --group-features \ starttls,\ ext_condstore_qresync,\ ext_id,\ ext_login_referrals,\ ext_mailbox_referrals,\ ext_metadata,\ ext_namespace,\ ext_utf8\ {{ mode }} [private] cargo_fmt: install_rust_nightly install_rust_nightly_fmt cargo +nightly fmt --check [private] cargo_clippy features mode: install_cargo_clippy cargo clippy --workspace --all-targets --exclude imap-codec-bench{{ features }}{{ mode }} [private] cargo_deny: install_cargo_deny cargo deny check [private] cargo_semver: install_cargo_semver_checks cargo semver-checks check-release --only-explicit-features -p imap-codec cargo semver-checks check-release --only-explicit-features -p imap-types # Test multiple configurations test: (test_impl "" "" ) \ (test_impl "" " --all-features") \ (test_impl " --release" "" ) \ (test_impl " --release" " --all-features") [private] test_impl mode features: (cargo_test mode features) [private] cargo_test features mode: cargo test \ --workspace \ --exclude imap-types-fuzz \ --exclude imap-codec-fuzz \ --all-targets \ --exclude imap-codec-bench\ {{ features }}\ {{ mode }} # Audit advisories, bans, licenses, and sources audit: cargo_deny bench_check: cargo check -p imap-codec-bench --all-features --all-targets # Benchmark bench: cargo bench -p imap-codec-bench --all-features # Benchmark against main bench_against_main: rm -rf target/bench_tmp mkdir -p target/bench_tmp git clone --depth 1 https://github.com/duesee/imap-codec target/bench_tmp cd target/bench_tmp; cargo bench -p imap-codec-bench rm -rf target/criterion cp -r target/bench_tmp/target/criterion target/criterion cargo bench -p imap-codec-bench rm -rf target/bench_tmp # Measure test coverage coverage: install_rust_llvm_tools_preview install_cargo_grcov # Old build artifacts seem to be able to mess up coverage data (see #508), # removing everything in `target/coverage` seems to be the easiest fix for this. rm -rf target/coverage/* # Run instrumented tests to generate coverage information RUSTFLAGS="-Cinstrument-coverage" LLVM_PROFILE_FILE="$PWD/target/coverage/coverage-%m-%p.profraw" CARGO_TARGET_DIR="$PWD/target/coverage" cargo test -p imap-codec -p imap-types --all-features # Generate coverage reports # - LCOV info report for coveralls.io # - HTML report for local use grcov target/coverage \ --source-dir . \ --binary-path target/coverage/debug \ --branch \ --keep-only '{imap-codec/src/**,imap-types/src/**}' \ --llvm \ --output-types "html,lcov" \ --output-path target/coverage/ mv target/coverage/lcov target/coverage/coverage.lcov # Remove profiling information and build artifacts to prevent wasting disk space rm target/coverage/*.profraw rm -rf target/coverage/debug # Fuzz all targets [linux] fuzz runs="25000": install_cargo_fuzz #!/usr/bin/env bash set -euo pipefail cd imap-codec for fuzz_target in $(cargo +nightly fuzz list) do echo "# Fuzzing ${fuzz_target}"; cargo +nightly fuzz run --features=ext,arbitrary_simplified ${fuzz_target} -- -dict=fuzz/terminals.dict -max_len=256 -only_ascii=1 -runs={{ runs }}; done # Check MSRV check_msrv: install_rust_msrv cargo '+{{ msrv }}' check --locked \ --workspace --exclude imap-codec-bench \ --all-targets --all-features cargo '+{{ msrv }}' test --locked \ --workspace --exclude imap-codec-bench --exclude imap-codec-fuzz --exclude imap-types-fuzz \ --all-targets --all-features # Check minimal dependency versions check_minimal_dependency_versions: install_rust_nightly cargo +nightly update -Z minimal-versions cargo check \ --workspace --exclude imap-codec-bench \ --all-targets --all-features cargo test \ --workspace --exclude imap-codec-bench --exclude imap-codec-fuzz --exclude imap-types-fuzz \ --all-targets --all-features cargo update ############### ### INSTALL ### ############### # Install required tooling (ahead of time) install: install_rust_msrv \ install_rust_nightly \ install_rust_nightly_fmt \ install_rust_llvm_tools_preview \ install_cargo_clippy \ install_cargo_deny \ install_cargo_fuzz \ install_cargo_grcov \ install_cargo_hack \ install_cargo_semver_checks [private] install_rust_msrv: rustup toolchain install '{{ msrv }}' --profile minimal [private] install_rust_nightly: rustup toolchain install nightly --profile minimal [private] install_rust_nightly_fmt: rustup component add --toolchain nightly rustfmt [private] install_rust_llvm_tools_preview: rustup component add llvm-tools-preview [private] install_cargo_clippy: rustup component add clippy [private] install_cargo_deny: cargo install --locked cargo-deny [private] install_cargo_fuzz: install_rust_nightly cargo install cargo-fuzz [private] install_cargo_grcov: cargo install grcov [private] install_cargo_hack: cargo install --locked cargo-hack [private] install_cargo_semver_checks: cargo install --locked cargo-semver-checks duesee-imap-codec-0d00966/rust-toolchain.toml000066400000000000000000000001401507724125200211050ustar00rootroot00000000000000[toolchain] channel = "stable" profile = "default" components = [ "rust-src", "rust-analyzer" ] duesee-imap-codec-0d00966/rustfmt.toml000066400000000000000000000001361507724125200176430ustar00rootroot00000000000000format_code_in_doc_comments=true group_imports="StdExternalCrate" imports_granularity="Crate" duesee-imap-codec-0d00966/shell.nix000066400000000000000000000006611507724125200170740ustar00rootroot00000000000000# Compatiblity file for non-flake Nix users. # # (import ( let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in fetchTarball { url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; sha256 = lock.nodes.flake-compat.locked.narHash; } ) { src = ./.; } ).shellNix