http-types-2.12.0/.cargo_vcs_info.json0000644000000001120000000000100132310ustar { "git": { "sha1": "6f38b3ea86602a8fa907c135373ff94400e11cd4" } } http-types-2.12.0/.github/CODE_OF_CONDUCT.md000064400000000000000000000061520072674642500162220ustar 00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at yoshuawuyts@gmail.com, or through IRC. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html http-types-2.12.0/.github/CONTRIBUTING.md000064400000000000000000000051770072674642500156620ustar 00000000000000# Contributing Contributions include code, documentation, answering user questions, running the project's infrastructure, and advocating for all types of users. The project welcomes all contributions from anyone willing to work in good faith with other contributors and the community. No contribution is too small and all contributions are valued. This guide explains the process for contributing to the project's GitHub Repository. - [Code of Conduct](#code-of-conduct) - [Bad Actors](#bad-actors) ## Code of Conduct The project has a [Code of Conduct](./CODE_OF_CONDUCT.md) that *all* contributors are expected to follow. This code describes the *minimum* behavior expectations for all contributors. As a contributor, how you choose to act and interact towards your fellow contributors, as well as to the community, will reflect back not only on yourself but on the project as a whole. The Code of Conduct is designed and intended, above all else, to help establish a culture within the project that allows anyone and everyone who wants to contribute to feel safe doing so. Should any individual act in any way that is considered in violation of the [Code of Conduct](./CODE_OF_CONDUCT.md), corrective actions will be taken. It is possible, however, for any individual to *act* in such a manner that is not in violation of the strict letter of the Code of Conduct guidelines while still going completely against the spirit of what that Code is intended to accomplish. Open, diverse, and inclusive communities live and die on the basis of trust. Contributors can disagree with one another so long as they trust that those disagreements are in good faith and everyone is working towards a common goal. ## Bad Actors All contributors to tacitly agree to abide by both the letter and spirit of the [Code of Conduct](./CODE_OF_CONDUCT.md). Failure, or unwillingness, to do so will result in contributions being respectfully declined. A *bad actor* is someone who repeatedly violates the *spirit* of the Code of Conduct through consistent failure to self-regulate the way in which they interact with other contributors in the project. In doing so, bad actors alienate other contributors, discourage collaboration, and generally reflect poorly on the project as a whole. Being a bad actor may be intentional or unintentional. Typically, unintentional bad behavior can be easily corrected by being quick to apologize and correct course *even if you are not entirely convinced you need to*. Giving other contributors the benefit of the doubt and having a sincere willingness to admit that you *might* be wrong is critical for any successful open collaboration. Don't be a bad actor. http-types-2.12.0/.github/workflows/ci.yaml000064400000000000000000000035330072674642500167370ustar 00000000000000name: CI on: pull_request: push: branches: - main - staging - trying env: RUSTFLAGS: -Dwarnings jobs: build_and_test: name: Build and test runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macOS-latest] rust: [stable, nightly] steps: - uses: actions/checkout@master - name: Install ${{ matrix.rust }} uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - name: check uses: actions-rs/cargo@v1 with: command: check args: --workspace --benches --bins --examples --tests --features hyperium_http - name: check unstable uses: actions-rs/cargo@v1 with: command: check args: --workspace --benches --bins --examples --tests --features "hyperium_http,unstable" - name: tests uses: actions-rs/cargo@v1 with: command: test args: --workspace --features "hyperium_http,unstable" check_fmt_clippy_docs: name: Checking fmt, clippy, and docs runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: clippy run: cargo clippy --workspace --benches --bins --examples --tests --features "hyperium_http,unstable" -- -D warnings - name: fmt run: cargo fmt --all -- --check - name: docs run: cargo doc --no-deps # check_wasm: # name: Check wasm targets # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@master # - name: Install nightly with wasm32-unknown-unknown # uses: actions-rs/toolchain@v1 # with: # toolchain: nightly # target: wasm32-unknown-unknown # override: true # - name: check # uses: actions-rs/cargo@v1 # with: # command: check # args: --target wasm32-unknown-unknown http-types-2.12.0/.gitignore000064400000000000000000000001010072674642500140370ustar 00000000000000coverage/ target/ tmp/ dist/ npm-debug.log* Cargo.lock .DS_Store http-types-2.12.0/Cargo.toml0000644000000042720000000000100112420ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "http-types" version = "2.12.0" authors = ["Yoshua Wuyts "] description = "Common types for HTTP operations." documentation = "https://docs.rs/http-types" readme = "README.md" keywords = ["http", "types", "request", "response", "h2"] categories = ["asynchronous", "web-programming", "web-programming::http-client", "web-programming::http-server", "web-programming::websocket"] license = "MIT OR Apache-2.0" repository = "https://github.com/http-rs/http-types" [package.metadata.docs.rs] features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [dependencies.anyhow] version = "1.0.26" [dependencies.async-channel] version = "1.5.1" [dependencies.async-std] version = "1.6.0" optional = true [dependencies.base64] version = "0.13.0" [dependencies.cookie] version = "0.14.0" features = ["percent-encode"] optional = true [dependencies.futures-lite] version = "1.11.1" [dependencies.http] version = "0.2.0" optional = true [dependencies.infer] version = "0.2.3" [dependencies.pin-project-lite] version = "0.2.0" [dependencies.rand] version = "0.7.3" [dependencies.serde] version = "1.0.106" features = ["derive"] [dependencies.serde_json] version = "1.0.51" [dependencies.serde_qs] version = "0.8.3" [dependencies.serde_urlencoded] version = "0.7.0" [dependencies.url] version = "2.1.1" features = ["serde"] [dev-dependencies.async-std] version = "1.6.0" features = ["attributes"] [dev-dependencies.http] version = "0.2.0" [features] async_std = ["fs"] cookie-secure = ["cookies", "cookie/secure"] cookies = ["cookie"] default = ["fs", "cookie-secure"] docs = ["unstable"] fs = ["async-std"] hyperium_http = ["http"] unstable = [] http-types-2.12.0/Cargo.toml.orig000064400000000000000000000027260072674642500147550ustar 00000000000000[package] name = "http-types" version = "2.12.0" license = "MIT OR Apache-2.0" repository = "https://github.com/http-rs/http-types" documentation = "https://docs.rs/http-types" description = "Common types for HTTP operations." keywords = ["http", "types", "request", "response", "h2"] categories = ["asynchronous", "web-programming", "web-programming::http-client", "web-programming::http-server", "web-programming::websocket"] authors = ["Yoshua Wuyts "] readme = "README.md" edition = "2018" [package.metadata.docs.rs] features = ["docs"] rustdoc-args = ["--cfg", "feature=\"docs\""] [features] default = ["fs", "cookie-secure"] docs = ["unstable"] unstable = [] hyperium_http = ["http"] async_std = ["fs"] cookies = ["cookie"] cookie-secure = ["cookies", "cookie/secure"] fs = ["async-std"] [dependencies] # features: async_std async-std = { version = "1.6.0", optional = true } futures-lite = "1.11.1" async-channel = "1.5.1" # features: hyperium/http http = { version = "0.2.0", optional = true } anyhow = "1.0.26" # features: cookies cookie = { version = "0.14.0", features = ["percent-encode"], optional = true } infer = "0.2.3" pin-project-lite = "0.2.0" url = { version = "2.1.1", features = ["serde"] } serde_json = "1.0.51" serde = { version = "1.0.106", features = ["derive"] } serde_urlencoded = "0.7.0" rand = "0.7.3" serde_qs = "0.8.3" base64 = "0.13.0" [dev-dependencies] http = "0.2.0" async-std = { version = "1.6.0", features = ["attributes"] } http-types-2.12.0/LICENSE-APACHE000064400000000000000000000251160072674642500140100ustar 00000000000000 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 Copyright 2019 Yoshua Wuyts Copyright 2016-2018 Michael Tilli (Pyfisch) & `httpdate` contributors 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. http-types-2.12.0/LICENSE-MIT000064400000000000000000000023030072674642500135110ustar 00000000000000The MIT License (MIT) Copyright (c) 2019 Yoshua Wuyts Copyright (c) 2017 http-rs authors Copyright (c) 2020 Jacob Brown Copyright (c) 2016-2018 Michael Tilli (Pyfisch) & `httpdate` contributors 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. http-types-2.12.0/README.md000064400000000000000000000041220072674642500133350ustar 00000000000000

http-types

Common types for HTTP operations.

Crates.io version Download docs.rs docs

API Docs | Releases | Contributing

## Installation ```sh $ cargo add http-types ``` ## Safety This crate uses `unsafe` in a few places to convert from validated ASCII byte buffers to utf-8 strings. ## Contributing Want to join us? Check out our ["Contributing" guide][contributing] and take a look at some of these issues: - [Issues labeled "good first issue"][good-first-issue] - [Issues labeled "help wanted"][help-wanted] [contributing]: https://github.com/http-rs/http-types/blob/main/.github/CONTRIBUTING.md [good-first-issue]: https://github.com/http-rs/http-types/labels/good%20first%20issue [help-wanted]: https://github.com/http-rs/http-types/labels/help%20wanted ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. http-types-2.12.0/build.rs000064400000000000000000000037750072674642500135400ustar 00000000000000use std::env; use std::fs; use std::path::Path; use std::process::{Command, ExitStatus, Stdio}; // This build script is copied from // [anyhow](https://github.com/dtolnay/anyhow/blob/master/build.rs), // and is a type of feature detection to determine if the current rust // toolchain has backtraces available. // // It exercises the surface area that we expect of the std Backtrace // type. If the current toolchain is able to compile it, we enable a // backtrace compiler configuration flag in http-types. We then // conditionally require the compiler feature in src/lib.rs with // `#![cfg_attr(backtrace, feature(backtrace))]` // and gate our backtrace code behind `#[cfg(backtrace)]` const PROBE: &str = r#" #![feature(backtrace)] #![allow(dead_code)] use std::backtrace::{Backtrace, BacktraceStatus}; use std::error::Error; use std::fmt::{self, Display}; #[derive(Debug)] struct E; impl Display for E { fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { unimplemented!() } } impl Error for E { fn backtrace(&self) -> Option<&Backtrace> { let backtrace = Backtrace::capture(); match backtrace.status() { BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} } unimplemented!() } } "#; fn main() { match compile_probe() { Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), _ => {} } } fn compile_probe() -> Option { let rustc = env::var_os("RUSTC")?; let out_dir = env::var_os("OUT_DIR")?; let probefile = Path::new(&out_dir).join("probe.rs"); fs::write(&probefile, PROBE).ok()?; Command::new(rustc) .stderr(Stdio::null()) .arg("--edition=2018") .arg("--crate-name=http_types_build") .arg("--crate-type=lib") .arg("--emit=metadata") .arg("--out-dir") .arg(out_dir) .arg(probefile) .status() .ok() } http-types-2.12.0/src/auth/authentication_scheme.rs000064400000000000000000000051170072674642500205240ustar 00000000000000use std::fmt::{self, Display}; use std::str::FromStr; use crate::bail_status as bail; /// HTTP Mutual Authentication Algorithms #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[non_exhaustive] pub enum AuthenticationScheme { /// [RFC7617](https://tools.ietf.org/html/rfc7617) Basic auth Basic, /// [RFC6750](https://tools.ietf.org/html/rfc6750) Bearer auth Bearer, /// [RFC7616](https://tools.ietf.org/html/rfc7616) Digest auth Digest, /// [RFC7486](https://tools.ietf.org/html/rfc7486) HTTP Origin-Bound Authentication (HOBA) Hoba, /// [RFC8120](https://tools.ietf.org/html/rfc8120) Mutual auth Mutual, /// [RFC4559](https://tools.ietf.org/html/rfc4559) Negotiate auth Negotiate, /// [RFC5849](https://tools.ietf.org/html/rfc5849) OAuth OAuth, /// [RFC7804](https://tools.ietf.org/html/rfc7804) SCRAM SHA1 auth ScramSha1, /// [RFC7804](https://tools.ietf.org/html/rfc7804) SCRAM SHA256 auth ScramSha256, /// [RFC8292](https://tools.ietf.org/html/rfc8292) Vapid auth Vapid, } impl Display for AuthenticationScheme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Basic => write!(f, "Basic"), Self::Bearer => write!(f, "Bearer"), Self::Digest => write!(f, "Digest"), Self::Hoba => write!(f, "HOBA"), Self::Mutual => write!(f, "Mutual"), Self::Negotiate => write!(f, "Negotiate"), Self::OAuth => write!(f, "OAuth"), Self::ScramSha1 => write!(f, "SCRAM-SHA-1"), Self::ScramSha256 => write!(f, "SCRAM-SHA-256"), Self::Vapid => write!(f, "vapid"), } } } impl FromStr for AuthenticationScheme { type Err = crate::Error; fn from_str(s: &str) -> Result { // NOTE(yosh): matching here is lowercase as specified by RFC2617#section-1.2 // > [...] case-insensitive token to identify the authentication scheme [...] // https://tools.ietf.org/html/rfc2617#section-1.2 match s.to_lowercase().as_str() { "basic" => Ok(Self::Basic), "bearer" => Ok(Self::Bearer), "digest" => Ok(Self::Digest), "hoba" => Ok(Self::Hoba), "mutual" => Ok(Self::Mutual), "negotiate" => Ok(Self::Negotiate), "oauth" => Ok(Self::OAuth), "scram-sha-1" => Ok(Self::ScramSha1), "scram-sha-256" => Ok(Self::ScramSha256), "vapid" => Ok(Self::Vapid), s => bail!(400, "`{}` is not a recognized authentication scheme", s), } } } http-types-2.12.0/src/auth/authorization.rs000064400000000000000000000101020072674642500170470ustar 00000000000000use crate::auth::AuthenticationScheme; use crate::bail_status as bail; use crate::headers::{HeaderName, HeaderValue, Headers, AUTHORIZATION}; /// Credentials to authenticate a user agent with a server. /// /// # Specifications /// /// - [RFC 7235, section 4.2: Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::auth::{AuthenticationScheme, Authorization}; /// /// let scheme = AuthenticationScheme::Basic; /// let credentials = "0xdeadbeef202020"; /// let authz = Authorization::new(scheme, credentials.into()); /// /// let mut res = Response::new(200); /// authz.apply(&mut res); /// /// let authz = Authorization::from_headers(res)?.unwrap(); /// /// assert_eq!(authz.scheme(), AuthenticationScheme::Basic); /// assert_eq!(authz.credentials(), credentials); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct Authorization { scheme: AuthenticationScheme, credentials: String, } impl Authorization { /// Create a new instance of `Authorization`. pub fn new(scheme: AuthenticationScheme, credentials: String) -> Self { Self { scheme, credentials, } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(AUTHORIZATION) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let value = headers.iter().last().unwrap(); let mut iter = value.as_str().splitn(2, ' '); let scheme = iter.next(); let credential = iter.next(); let (scheme, credentials) = match (scheme, credential) { (None, _) => bail!(400, "Could not find scheme"), (Some(_), None) => bail!(400, "Could not find credentials"), (Some(scheme), Some(credentials)) => (scheme.parse()?, credentials.to_owned()), }; Ok(Some(Self { scheme, credentials, })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { AUTHORIZATION } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = format!("{} {}", self.scheme, self.credentials); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Get the authorization scheme. pub fn scheme(&self) -> AuthenticationScheme { self.scheme } /// Set the authorization scheme. pub fn set_scheme(&mut self, scheme: AuthenticationScheme) { self.scheme = scheme; } /// Get the authorization credentials. pub fn credentials(&self) -> &str { self.credentials.as_str() } /// Set the authorization credentials. pub fn set_credentials(&mut self, credentials: String) { self.credentials = credentials; } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let scheme = AuthenticationScheme::Basic; let credentials = "0xdeadbeef202020"; let authz = Authorization::new(scheme, credentials.into()); let mut headers = Headers::new(); authz.apply(&mut headers); let authz = Authorization::from_headers(headers)?.unwrap(); assert_eq!(authz.scheme(), AuthenticationScheme::Basic); assert_eq!(authz.credentials(), credentials); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(AUTHORIZATION, ""); let err = Authorization::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/auth/basic_auth.rs000064400000000000000000000101020072674642500162510ustar 00000000000000use crate::auth::{AuthenticationScheme, Authorization}; use crate::headers::{HeaderName, HeaderValue, Headers, AUTHORIZATION}; use crate::Status; use crate::{bail_status as bail, ensure_status as ensure}; /// HTTP Basic authorization. /// /// # Specifications /// /// - [RFC7617](https://tools.ietf.org/html/rfc7617) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::auth::{AuthenticationScheme, BasicAuth}; /// /// let username = "nori"; /// let password = "secret_fish!!"; /// let authz = BasicAuth::new(username, password); /// /// let mut res = Response::new(200); /// authz.apply(&mut res); /// /// let authz = BasicAuth::from_headers(res)?.unwrap(); /// /// assert_eq!(authz.username(), username); /// assert_eq!(authz.password(), password); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct BasicAuth { username: String, password: String, } impl BasicAuth { /// Create a new instance of `BasicAuth`. pub fn new(username: U, password: P) -> Self where U: AsRef, P: AsRef, { let username = username.as_ref().to_owned(); let password = password.as_ref().to_owned(); Self { username, password } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let auth = match Authorization::from_headers(headers)? { Some(auth) => auth, None => return Ok(None), }; let scheme = auth.scheme(); ensure!( matches!(scheme, AuthenticationScheme::Basic), 400, "Expected basic auth scheme found `{}`", scheme ); Self::from_credentials(auth.credentials()).map(Some) } /// Create a new instance from the base64 encoded credentials. pub fn from_credentials(credentials: impl AsRef<[u8]>) -> crate::Result { let bytes = base64::decode(credentials).status(400)?; let credentials = String::from_utf8(bytes).status(400)?; let mut iter = credentials.splitn(2, ':'); let username = iter.next(); let password = iter.next(); let (username, password) = match (username, password) { (Some(username), Some(password)) => (username.to_string(), password.to_string()), (Some(_), None) => bail!(400, "Expected basic auth to contain a password"), (None, _) => bail!(400, "Expected basic auth to contain a username"), }; Ok(Self { username, password }) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { AUTHORIZATION } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let scheme = AuthenticationScheme::Basic; let credentials = base64::encode(format!("{}:{}", self.username, self.password)); let auth = Authorization::new(scheme, credentials); auth.value() } /// Get the username. pub fn username(&self) -> &str { self.username.as_str() } /// Get the password. pub fn password(&self) -> &str { self.password.as_str() } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let username = "nori"; let password = "secret_fish!!"; let authz = BasicAuth::new(username, password); let mut headers = Headers::new(); authz.apply(&mut headers); let authz = BasicAuth::from_headers(headers)?.unwrap(); assert_eq!(authz.username(), username); assert_eq!(authz.password(), password); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(AUTHORIZATION, ""); let err = BasicAuth::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/auth/mod.rs000064400000000000000000000015030072674642500147330ustar 00000000000000//! HTTP authentication and authorization. //! //! # Examples //! //! ``` //! # fn main() -> http_types::Result<()> { //! # //! use http_types::Response; //! use http_types::auth::{AuthenticationScheme, BasicAuth}; //! //! let username = "nori"; //! let password = "secret_fish!!"; //! let authz = BasicAuth::new(username, password); //! //! let mut res = Response::new(200); //! authz.apply(&mut res); //! //! let authz = BasicAuth::from_headers(res)?.unwrap(); //! //! assert_eq!(authz.username(), username); //! assert_eq!(authz.password(), password); //! # //! # Ok(()) } //! ``` mod authentication_scheme; mod authorization; mod basic_auth; mod www_authenticate; pub use authentication_scheme::AuthenticationScheme; pub use authorization::Authorization; pub use basic_auth::BasicAuth; pub use www_authenticate::WwwAuthenticate; http-types-2.12.0/src/auth/www_authenticate.rs000064400000000000000000000116740072674642500175500ustar 00000000000000use crate::auth::AuthenticationScheme; use crate::bail_status as bail; use crate::headers::{HeaderName, HeaderValue, Headers, WWW_AUTHENTICATE}; /// Define the authentication method that should be used to gain access to a /// resource. /// /// # Specifications /// /// - [RFC 7235, section 4.1: WWW-Authenticate](https://tools.ietf.org/html/rfc7235#section-4.1) /// /// # Implementation Notes /// /// This implementation only encodes and parses a single authentication method, /// further authorization methods are ignored. It also always passes the utf-8 encoding flag. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::auth::{AuthenticationScheme, WwwAuthenticate}; /// /// let scheme = AuthenticationScheme::Basic; /// let realm = "Access to the staging site"; /// let authz = WwwAuthenticate::new(scheme, realm.into()); /// /// let mut res = Response::new(200); /// authz.apply(&mut res); /// /// let authz = WwwAuthenticate::from_headers(res)?.unwrap(); /// /// assert_eq!(authz.scheme(), AuthenticationScheme::Basic); /// assert_eq!(authz.realm(), realm); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct WwwAuthenticate { scheme: AuthenticationScheme, realm: String, } impl WwwAuthenticate { /// Create a new instance of `WwwAuthenticate`. pub fn new(scheme: AuthenticationScheme, realm: String) -> Self { Self { scheme, realm } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(WWW_AUTHENTICATE) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let value = headers.iter().last().unwrap(); let mut iter = value.as_str().splitn(2, ' '); let scheme = iter.next(); let credential = iter.next(); let (scheme, realm) = match (scheme, credential) { (None, _) => bail!(400, "Could not find scheme"), (Some(_), None) => bail!(400, "Could not find realm"), (Some(scheme), Some(realm)) => (scheme.parse()?, realm.to_owned()), }; let realm = realm.trim_start(); let realm = match realm.strip_prefix(r#"realm=""#) { Some(realm) => realm, None => bail!(400, "realm not found"), }; let mut chars = realm.chars(); let mut closing_quote = false; let realm = (&mut chars) .take_while(|c| { if c == &'"' { closing_quote = true; false } else { true } }) .collect(); if !closing_quote { bail!(400, r"Expected a closing quote"); } Ok(Some(Self { scheme, realm })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { WWW_AUTHENTICATE } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = format!(r#"{} realm="{}", charset="UTF-8""#, self.scheme, self.realm); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Get the authorization scheme. pub fn scheme(&self) -> AuthenticationScheme { self.scheme } /// Set the authorization scheme. pub fn set_scheme(&mut self, scheme: AuthenticationScheme) { self.scheme = scheme; } /// Get the authorization realm. pub fn realm(&self) -> &str { self.realm.as_str() } /// Set the authorization realm. pub fn set_realm(&mut self, realm: String) { self.realm = realm; } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let scheme = AuthenticationScheme::Basic; let realm = "Access to the staging site"; let authz = WwwAuthenticate::new(scheme, realm.into()); let mut headers = Headers::new(); authz.apply(&mut headers); assert_eq!( headers["WWW-Authenticate"], r#"Basic realm="Access to the staging site", charset="UTF-8""# ); let authz = WwwAuthenticate::from_headers(headers)?.unwrap(); assert_eq!(authz.scheme(), AuthenticationScheme::Basic); assert_eq!(authz.realm(), realm); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(WWW_AUTHENTICATE, ""); let err = WwwAuthenticate::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/body.rs000064400000000000000000000613320072674642500141560ustar 00000000000000use futures_lite::{io, prelude::*, ready}; use serde::{de::DeserializeOwned, Serialize}; use std::fmt::{self, Debug}; use std::pin::Pin; use std::task::{Context, Poll}; use crate::{mime, Mime}; use crate::{Status, StatusCode}; pin_project_lite::pin_project! { /// A streaming HTTP body. /// /// `Body` represents the HTTP body of both `Request` and `Response`. It's completely /// streaming, and implements `AsyncBufRead` to make reading from it both convenient and /// performant. /// /// Both `Request` and `Response` take `Body` by `Into`, which means that passing string /// literals, byte vectors, but also concrete `Body` instances are all valid. This makes it /// easy to create both quick HTTP requests, but also have fine grained control over how bodies /// are streamed out. /// /// # Examples /// /// ``` /// use http_types::{Body, Response, StatusCode}; /// use async_std::io::Cursor; /// /// let mut req = Response::new(StatusCode::Ok); /// req.set_body("Hello Chashu"); /// /// let mut req = Response::new(StatusCode::Ok); /// let cursor = Cursor::new("Hello Nori"); /// let body = Body::from_reader(cursor, Some(10)); // set the body length /// req.set_body(body); /// ``` /// /// # Length /// /// One of the details of `Body` to be aware of is the `length` parameter. The value of /// `length` is used by HTTP implementations to determine how to treat the stream. If a length /// is known ahead of time, it's _strongly_ recommended to pass it. /// /// Casting from `Vec`, `String`, or similar to `Body` will automatically set the value of /// `length`. /// /// # Content Encoding /// /// By default `Body` will come with a fallback Mime type that is used by `Request` and /// `Response` if no other type has been set, and no other Mime type can be inferred. /// /// It's _strongly_ recommended to always set a mime type on both the `Request` and `Response`, /// and not rely on the fallback mechanisms. However, they're still there if you need them. pub struct Body { #[pin] reader: Box, mime: Mime, length: Option, bytes_read: usize } } impl Body { /// Create a new empty `Body`. /// /// The body will have a length of `0`, and the Mime type set to `application/octet-stream` if /// no other mime type has been set or can be sniffed. /// /// # Examples /// /// ``` /// use http_types::{Body, Response, StatusCode}; /// /// let mut req = Response::new(StatusCode::Ok); /// req.set_body(Body::empty()); /// ``` pub fn empty() -> Self { Self { reader: Box::new(io::empty()), mime: mime::BYTE_STREAM, length: Some(0), bytes_read: 0, } } /// Create a `Body` from a reader with an optional length. /// /// The Mime type is set to `application/octet-stream` if no other mime type has been set or can /// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to /// framed messages such as [Chunked /// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding). /// /// # Examples /// /// ``` /// use http_types::{Body, Response, StatusCode}; /// use async_std::io::Cursor; /// /// let mut req = Response::new(StatusCode::Ok); /// /// let cursor = Cursor::new("Hello Nori"); /// let len = 10; /// req.set_body(Body::from_reader(cursor, Some(len))); /// ``` pub fn from_reader( reader: impl AsyncBufRead + Unpin + Send + Sync + 'static, len: Option, ) -> Self { Self { reader: Box::new(reader), mime: mime::BYTE_STREAM, length: len, bytes_read: 0, } } /// Get the inner reader from the `Body` /// /// # Examples /// /// ``` /// # use std::io::prelude::*; /// use http_types::Body; /// use async_std::io::Cursor; /// /// let cursor = Cursor::new("Hello Nori"); /// let body = Body::from_reader(cursor, None); /// let _ = body.into_reader(); /// ``` pub fn into_reader(self) -> Box { self.reader } /// Create a `Body` from a Vec of bytes. /// /// The Mime type is set to `application/octet-stream` if no other mime type has been set or can /// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to /// framed messages such as [Chunked /// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding). /// /// # Examples /// /// ``` /// use http_types::{Body, Response, StatusCode}; /// use async_std::io::Cursor; /// /// let mut req = Response::new(StatusCode::Ok); /// /// let input = vec![1, 2, 3]; /// req.set_body(Body::from_bytes(input)); /// ``` pub fn from_bytes(bytes: Vec) -> Self { Self { mime: mime::BYTE_STREAM, length: Some(bytes.len()), reader: Box::new(io::Cursor::new(bytes)), bytes_read: 0, } } /// Parse the body into a `Vec`. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::Body; /// /// let bytes = vec![1, 2, 3]; /// let body = Body::from_bytes(bytes); /// /// let bytes: Vec = body.into_bytes().await?; /// assert_eq!(bytes, vec![1, 2, 3]); /// # Ok(()) }) } /// ``` pub async fn into_bytes(mut self) -> crate::Result> { let mut buf = Vec::with_capacity(1024); self.read_to_end(&mut buf) .await .status(StatusCode::UnprocessableEntity)?; Ok(buf) } /// Create a `Body` from a String /// /// The Mime type is set to `text/plain` if no other mime type has been set or can /// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to /// framed messages such as [Chunked /// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding). /// /// # Examples /// /// ``` /// use http_types::{Body, Response, StatusCode}; /// use async_std::io::Cursor; /// /// let mut req = Response::new(StatusCode::Ok); /// /// let input = String::from("hello Nori!"); /// req.set_body(Body::from_string(input)); /// ``` pub fn from_string(s: String) -> Self { Self { mime: mime::PLAIN, length: Some(s.len()), reader: Box::new(io::Cursor::new(s.into_bytes())), bytes_read: 0, } } /// Read the body as a string /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::Body; /// use async_std::io::Cursor; /// /// let cursor = Cursor::new("Hello Nori"); /// let body = Body::from_reader(cursor, None); /// assert_eq!(&body.into_string().await.unwrap(), "Hello Nori"); /// # Ok(()) }) } /// ``` pub async fn into_string(mut self) -> crate::Result { let mut result = String::with_capacity(self.len().unwrap_or(0)); self.read_to_string(&mut result) .await .status(StatusCode::UnprocessableEntity)?; Ok(result) } /// Creates a `Body` from a type, serializing it as JSON. /// /// # Mime /// /// The encoding is set to `application/json`. /// /// # Examples /// /// ``` /// use http_types::{Body, convert::json}; /// /// let body = Body::from_json(&json!({ "name": "Chashu" })); /// # drop(body); /// ``` pub fn from_json(json: &impl Serialize) -> crate::Result { let bytes = serde_json::to_vec(&json)?; let body = Self { length: Some(bytes.len()), reader: Box::new(io::Cursor::new(bytes)), mime: mime::JSON, bytes_read: 0, }; Ok(body) } /// Parse the body as JSON, serializing it to a struct. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::Body; /// use http_types::convert::{Serialize, Deserialize}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { name: String } /// /// let cat = Cat { name: String::from("chashu") }; /// let body = Body::from_json(&cat)?; /// /// let cat: Cat = body.into_json().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub async fn into_json(mut self) -> crate::Result { let mut buf = Vec::with_capacity(1024); self.read_to_end(&mut buf).await?; Ok(serde_json::from_slice(&buf).status(StatusCode::UnprocessableEntity)?) } /// Creates a `Body` from a type, serializing it using form encoding. /// /// # Mime /// /// The encoding is set to `application/x-www-form-urlencoded`. /// /// # Errors /// /// An error will be returned if the encoding failed. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::Body; /// use http_types::convert::{Serialize, Deserialize}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { name: String } /// /// let cat = Cat { name: String::from("chashu") }; /// let body = Body::from_form(&cat)?; /// /// let cat: Cat = body.into_form().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub fn from_form(form: &impl Serialize) -> crate::Result { let query = serde_urlencoded::to_string(form)?; let bytes = query.into_bytes(); let body = Self { length: Some(bytes.len()), reader: Box::new(io::Cursor::new(bytes)), mime: mime::FORM, bytes_read: 0, }; Ok(body) } /// Parse the body from form encoding into a type. /// /// # Errors /// /// An error is returned if the underlying IO stream errors, or if the body /// could not be deserialized into the type. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::Body; /// use http_types::convert::{Serialize, Deserialize}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { name: String } /// /// let cat = Cat { name: String::from("chashu") }; /// let body = Body::from_form(&cat)?; /// /// let cat: Cat = body.into_form().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub async fn into_form(self) -> crate::Result { let s = self.into_string().await?; Ok(serde_urlencoded::from_str(&s).status(StatusCode::UnprocessableEntity)?) } /// Create a `Body` from a file. /// /// The Mime type set to `application/octet-stream` if no other mime type has /// been set or can be sniffed. /// /// # Examples /// /// ```no_run /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::{Body, Response, StatusCode}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.set_body(Body::from_file("/path/to/file").await?); /// # Ok(()) }) } /// ``` #[cfg(all(feature = "fs", not(target_os = "unknown")))] pub async fn from_file

(path: P) -> io::Result where P: AsRef, { let path = path.as_ref(); let mut file = async_std::fs::File::open(path).await?; let len = file.metadata().await?.len(); // Look at magic bytes first, look at extension second, fall back to // octet stream. let mime = peek_mime(&mut file) .await? .or_else(|| guess_ext(path)) .unwrap_or(mime::BYTE_STREAM); Ok(Self { mime, length: Some(len as usize), reader: Box::new(io::BufReader::new(file)), bytes_read: 0, }) } /// Get the length of the body in bytes. /// /// # Examples /// /// ``` /// use http_types::Body; /// use async_std::io::Cursor; /// /// let cursor = Cursor::new("Hello Nori"); /// let len = 10; /// let body = Body::from_reader(cursor, Some(len)); /// assert_eq!(body.len(), Some(10)); /// ``` pub fn len(&self) -> Option { self.length } /// Returns `true` if the body has a length of zero, and `false` otherwise. pub fn is_empty(&self) -> Option { self.length.map(|length| length == 0) } /// Returns the mime type of this Body. pub fn mime(&self) -> &Mime { &self.mime } /// Sets the mime type of this Body. pub fn set_mime(&mut self, mime: impl Into) { self.mime = mime.into(); } /// Create a Body by chaining another Body after this one, consuming both. /// /// If both Body instances have a length, and their sum does not overflow, /// the resulting Body will have a length. /// /// If both Body instances have the same fallback MIME type, the resulting /// Body will have the same fallback MIME type; otherwise, the resulting /// Body will have the fallback MIME type `application/octet-stream`. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::Body; /// use async_std::io::Cursor; /// /// let cursor = Cursor::new("Hello "); /// let body = Body::from_reader(cursor, None).chain(Body::from("Nori")); /// assert_eq!(&body.into_string().await.unwrap(), "Hello Nori"); /// # Ok(()) }) } /// ``` pub fn chain(self, other: Body) -> Self { let mime = if self.mime == other.mime { self.mime.clone() } else { mime::BYTE_STREAM }; let length = match (self.length, other.length) { (Some(l1), Some(l2)) => (l1 - self.bytes_read).checked_add(l2 - other.bytes_read), _ => None, }; Self { mime, length, reader: Box::new(futures_lite::io::AsyncReadExt::chain(self, other)), bytes_read: 0, } } } impl Debug for Body { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Body") .field("reader", &"") .field("length", &self.length) .field("bytes_read", &self.bytes_read) .finish() } } impl From for Body { fn from(json_value: serde_json::Value) -> Self { Self::from_json(&json_value).unwrap() } } impl From for Body { fn from(s: String) -> Self { Self::from_string(s) } } impl<'a> From<&'a str> for Body { fn from(s: &'a str) -> Self { Self::from_string(s.to_owned()) } } impl From> for Body { fn from(b: Vec) -> Self { Self::from_bytes(b) } } impl<'a> From<&'a [u8]> for Body { fn from(b: &'a [u8]) -> Self { Self::from_bytes(b.to_owned()) } } impl AsyncRead for Body { #[allow(missing_doc_code_examples)] fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let mut buf = match self.length { None => buf, Some(length) if length == self.bytes_read => return Poll::Ready(Ok(0)), Some(length) => { let max_len = (length - self.bytes_read).min(buf.len()); &mut buf[0..max_len] } }; let bytes = ready!(Pin::new(&mut self.reader).poll_read(cx, &mut buf))?; self.bytes_read += bytes; Poll::Ready(Ok(bytes)) } } impl AsyncBufRead for Body { #[allow(missing_doc_code_examples)] fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.project().reader.poll_fill_buf(cx) } fn consume(mut self: Pin<&mut Self>, amt: usize) { Pin::new(&mut self.reader).consume(amt) } } /// Look at first few bytes of a file to determine the mime type. /// This is used for various binary formats such as images and videos. #[cfg(all(feature = "fs", not(target_os = "unknown")))] async fn peek_mime(file: &mut async_std::fs::File) -> io::Result> { // We need to read the first 300 bytes to correctly infer formats such as tar. let mut buf = [0_u8; 300]; file.read(&mut buf).await?; let mime = Mime::sniff(&buf).ok(); // Reset the file cursor back to the start. file.seek(io::SeekFrom::Start(0)).await?; Ok(mime) } /// Look at the extension of a file to determine the mime type. /// This is useful for plain-text formats such as HTML and CSS. #[cfg(all(feature = "fs", not(target_os = "unknown")))] fn guess_ext(path: &std::path::Path) -> Option { let ext = path.extension().map(|p| p.to_str()).flatten(); ext.and_then(Mime::from_extension) } #[cfg(test)] mod test { use super::*; use async_std::io::Cursor; use serde::Deserialize; #[async_std::test] async fn json_status() { #[derive(Debug, Deserialize)] struct Foo { inner: String, } let body = Body::empty(); let res = body.into_json::().await; assert_eq!(res.unwrap_err().status(), 422); } #[async_std::test] async fn form_status() { #[derive(Debug, Deserialize)] struct Foo { inner: String, } let body = Body::empty(); let res = body.into_form::().await; assert_eq!(res.unwrap_err().status(), 422); } async fn read_with_buffers_of_size(reader: &mut R, size: usize) -> crate::Result where R: AsyncRead + Unpin, { let mut return_buffer = vec![]; loop { let mut buf = vec![0; size]; match reader.read(&mut buf).await? { 0 => break Ok(String::from_utf8(return_buffer)?), bytes_read => return_buffer.extend_from_slice(&buf[..bytes_read]), } } } #[async_std::test] async fn attempting_to_read_past_length() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from_reader(Cursor::new("hello world"), Some(5)); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello" ); assert_eq!(body.bytes_read, 5); } Ok(()) } #[async_std::test] async fn attempting_to_read_when_length_is_greater_than_content() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from_reader(Cursor::new("hello world"), Some(15)); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn attempting_to_read_when_length_is_exactly_right() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from_reader(Cursor::new("hello world"), Some(11)); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn reading_in_various_buffer_lengths_when_there_is_no_length() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from_reader(Cursor::new("hello world"), None); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_strings() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from("hello ").chain(Body::from("world")); assert_eq!(body.len(), Some(11)); assert_eq!(body.mime(), &mime::PLAIN); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_mixed_bytes_string() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from(&b"hello "[..]).chain(Body::from("world")); assert_eq!(body.len(), Some(11)); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_mixed_reader_string() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from_reader(Cursor::new("hello "), Some(6)).chain(Body::from("world")); assert_eq!(body.len(), Some(11)); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_mixed_nolen_len() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from_reader(Cursor::new("hello "), None).chain(Body::from("world")); assert_eq!(body.len(), None); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_mixed_len_nolen() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from("hello ").chain(Body::from_reader(Cursor::new("world"), None)); assert_eq!(body.len(), None); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_short() -> crate::Result<()> { for buf_len in 1..26 { let mut body = Body::from_reader(Cursor::new("hello xyz"), Some(6)) .chain(Body::from_reader(Cursor::new("world abc"), Some(5))); assert_eq!(body.len(), Some(11)); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_many() -> crate::Result<()> { for buf_len in 1..13 { let mut body = Body::from("hello") .chain(Body::from(&b" "[..])) .chain(Body::from("world")); assert_eq!(body.len(), Some(11)); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } #[async_std::test] async fn chain_skip_start() -> crate::Result<()> { for buf_len in 1..26 { let mut body1 = Body::from_reader(Cursor::new("1234 hello xyz"), Some(11)); let mut buf = vec![0; 5]; body1.read(&mut buf).await?; assert_eq!(buf, b"1234 "); let mut body2 = Body::from_reader(Cursor::new("321 world abc"), Some(9)); let mut buf = vec![0; 4]; body2.read(&mut buf).await?; assert_eq!(buf, b"321 "); let mut body = body1.chain(body2); assert_eq!(body.len(), Some(11)); assert_eq!(body.mime(), &mime::BYTE_STREAM); assert_eq!( read_with_buffers_of_size(&mut body, buf_len).await?, "hello world" ); assert_eq!(body.bytes_read, 11); } Ok(()) } } http-types-2.12.0/src/cache/age.rs000064400000000000000000000062370072674642500150230ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, AGE}; use crate::Status; use std::fmt::Debug; use std::option; use std::time::Duration; /// HTTP `Age` header /// /// # Specifications /// /// - [RFC 7234, section 5.1: Age](https://tools.ietf.org/html/rfc7234#section-5.1) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::cache::Age; /// /// let age = Age::from_secs(12); /// /// let mut res = Response::new(200); /// age.apply(&mut res); /// /// let age = Age::from_headers(res)?.unwrap(); /// assert_eq!(age, Age::from_secs(12)); /// # /// # Ok(()) } /// ``` #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct Age { dur: Duration, } impl Age { /// Create a new instance of `Age`. pub fn new(dur: Duration) -> Self { Self { dur } } /// Create a new instance of `Age` from secs. pub fn from_secs(secs: u64) -> Self { let dur = Duration::from_secs(secs); Self { dur } } /// Get the duration from the header. pub fn duration(&self) -> Duration { self.dur } /// Create an instance of `Age` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(AGE) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header = headers.iter().last().unwrap(); let num: u64 = header.as_str().parse().status(400)?; let dur = Duration::from_secs_f64(num as f64); Ok(Some(Self { dur })) } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(AGE, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { AGE } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = self.dur.as_secs().to_string(); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl ToHeaderValues for Age { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let age = Age::new(Duration::from_secs(12)); let mut headers = Headers::new(); age.apply(&mut headers); let age = Age::from_headers(headers)?.unwrap(); assert_eq!(age, Age::new(Duration::from_secs(12))); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(AGE, ""); let err = Age::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/cache/cache_control/cache_control.rs000064400000000000000000000125560072674642500216760ustar 00000000000000use crate::cache::CacheDirective; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CACHE_CONTROL}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; use std::option; use std::slice; /// A Cache-Control header. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::cache::{CacheControl, CacheDirective}; /// let mut entries = CacheControl::new(); /// entries.push(CacheDirective::Immutable); /// entries.push(CacheDirective::NoStore); /// /// let mut res = Response::new(200); /// entries.apply(&mut res); /// /// let entries = CacheControl::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); /// assert_eq!(entries.next().unwrap(), &CacheDirective::Immutable); /// assert_eq!(entries.next().unwrap(), &CacheDirective::NoStore); /// # /// # Ok(()) } /// ``` pub struct CacheControl { entries: Vec, } impl CacheControl { /// Create a new instance of `CacheControl`. pub fn new() -> Self { Self { entries: vec![] } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(CACHE_CONTROL) { Some(headers) => headers, None => return Ok(None), }; for value in headers { for part in value.as_str().trim().split(',') { // Try and parse a directive from a str. If the directive is // unkown we skip it. if let Some(entry) = CacheDirective::from_str(part)? { entries.push(entry); } } } Ok(Some(Self { entries })) } /// Sets the `Server-Timing` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(CACHE_CONTROL, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CACHE_CONTROL } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = directive.clone().into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push a directive into the list of entries. pub fn push(&mut self, directive: CacheDirective) { self.entries.push(directive); } /// An iterator visiting all server entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all server entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for CacheControl { type Item = CacheDirective; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a CacheControl { type Item = &'a CacheDirective; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut CacheControl { type Item = &'a mut CacheDirective; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `CacheControl`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = CacheDirective; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `CacheControl`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, CacheDirective>, } impl<'a> Iterator for Iter<'a> { type Item = &'a CacheDirective; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `CacheControl`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, CacheDirective>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut CacheDirective; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for CacheControl { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for CacheControl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } http-types-2.12.0/src/cache/cache_control/cache_directive.rs000064400000000000000000000134250072674642500221700ustar 00000000000000use crate::headers::HeaderValue; use crate::Status; use std::time::Duration; /// An HTTP `Cache-Control` directive. #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq)] pub enum CacheDirective { /// The response body will not change over time. Immutable, /// The maximum amount of time a resource is considered fresh. MaxAge(Duration), /// Indicates the client will accept a stale response. MaxStale(Option), /// A response that will still be fresh for at least the specified duration. MinFresh(Duration), /// Once a response is stale, a fresh response must be retrieved. MustRevalidate, /// The response may be cached, but must always be revalidated before being used. NoCache, /// The response may not be cached. NoStore, /// An intermediate cache or proxy should not edit the response body, /// Content-Encoding, Content-Range, or Content-Type. NoTransform, /// Do not use the network for a response. OnlyIfCached, /// The response may be stored only by a browser's cache, even if the /// response is normally non-cacheable. Private, /// Like must-revalidate, but only for shared caches (e.g., proxies). ProxyRevalidate, /// The response may be stored by any cache, even if the response is normally /// non-cacheable. Public, /// Overrides max-age or the Expires header, but only for shared caches. SMaxAge(Duration), /// The client will accept a stale response if retrieving a fresh one fails. StaleIfError(Duration), /// Indicates the client will accept a stale response, while asynchronously /// checking in the background for a fresh one. StaleWhileRevalidate(Duration), } impl CacheDirective { /// Check whether this directive is valid in an HTTP request. pub fn valid_in_req(&self) -> bool { use CacheDirective::*; matches!( self, MaxAge(_) | MaxStale(_) | MinFresh(_) | NoCache | NoStore | NoTransform | OnlyIfCached ) } /// Check whether this directive is valid in an HTTP response. pub fn valid_in_res(&self) -> bool { use CacheDirective::*; matches!( self, MustRevalidate | NoCache | NoStore | NoTransform | Public | Private | ProxyRevalidate | MaxAge(_) | SMaxAge(_) | StaleIfError(_) | StaleWhileRevalidate(_) ) } /// Create an instance from a string slice. // // This is a private method rather than a trait because we assume the // input string is a single-value only. This is upheld by the calling // function, but we cannot guarantee this to be true in the general // sense. pub(crate) fn from_str(s: &str) -> crate::Result> { use CacheDirective::*; let s = s.trim(); // We're dealing with an empty string. if s.is_empty() { return Ok(None); } s.to_lowercase(); let mut parts = s.split('='); let next = parts.next().unwrap(); let mut get_dur = || -> crate::Result { let dur = parts.next().status(400)?; let dur: u64 = dur.parse().status(400)?; Ok(Duration::new(dur, 0)) }; // This won't panic because each input string has at least one part. let res = match next { "immutable" => Some(Immutable), "no-cache" => Some(NoCache), "no-store" => Some(NoStore), "no-transform" => Some(NoTransform), "only-if-cached" => Some(OnlyIfCached), "must-revalidate" => Some(MustRevalidate), "public" => Some(Public), "private" => Some(Private), "proxy-revalidate" => Some(ProxyRevalidate), "max-age" => Some(MaxAge(get_dur()?)), "max-stale" => match parts.next() { Some(secs) => { let dur: u64 = secs.parse().status(400)?; Some(MaxStale(Some(Duration::new(dur, 0)))) } None => Some(MaxStale(None)), }, "min-fresh" => Some(MinFresh(get_dur()?)), "s-maxage" => Some(SMaxAge(get_dur()?)), "stale-if-error" => Some(StaleIfError(get_dur()?)), "stale-while-revalidate" => Some(StaleWhileRevalidate(get_dur()?)), _ => None, }; Ok(res) } } impl From for HeaderValue { fn from(directive: CacheDirective) -> Self { use CacheDirective::*; let h = |s: String| unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) }; match directive { Immutable => h("immutable".to_string()), MaxAge(dur) => h(format!("max-age={}", dur.as_secs())), MaxStale(dur) => match dur { Some(dur) => h(format!("max-stale={}", dur.as_secs())), None => h("max-stale".to_string()), }, MinFresh(dur) => h(format!("min-fresh={}", dur.as_secs())), MustRevalidate => h("must-revalidate".to_string()), NoCache => h("no-cache".to_string()), NoStore => h("no-store".to_string()), NoTransform => h("no-transform".to_string()), OnlyIfCached => h("only-if-cached".to_string()), Private => h("private".to_string()), ProxyRevalidate => h("proxy-revalidate".to_string()), Public => h("public".to_string()), SMaxAge(dur) => h(format!("s-max-age={}", dur.as_secs())), StaleIfError(dur) => h(format!("stale-if-error={}", dur.as_secs())), StaleWhileRevalidate(dur) => h(format!("stale-while-revalidate={}", dur.as_secs())), } } } http-types-2.12.0/src/cache/cache_control/mod.rs000064400000000000000000000033660072674642500176510ustar 00000000000000//! HTTP `Cache-Control` headers. //! //! # Specifications //! //! - [RFC 8246: HTTP Immutable Responses](https://tools.ietf.org/html/rfc8246) //! - [RFC 7234: Hypertext Transfer Protocol (HTTP/1.1): Caching](https://tools.ietf.org/html/rfc7234) //! - [RFC 5861: HTTP Cache-Control Extensions for Stale Content](https://tools.ietf.org/html/rfc5861) #[allow(clippy::module_inception)] mod cache_control; mod cache_directive; pub use cache_control::CacheControl; pub use cache_directive::CacheDirective; #[cfg(test)] mod test { use super::*; use crate::headers::{Headers, CACHE_CONTROL}; #[test] fn smoke() -> crate::Result<()> { let mut entries = CacheControl::new(); entries.push(CacheDirective::Immutable); entries.push(CacheDirective::NoStore); let mut headers = Headers::new(); entries.apply(&mut headers); let entries = CacheControl::from_headers(headers)?.unwrap(); let mut entries = entries.iter(); assert_eq!(entries.next().unwrap(), &CacheDirective::Immutable); assert_eq!(entries.next().unwrap(), &CacheDirective::NoStore); Ok(()) } #[test] fn ignore_unkonwn_directives() -> crate::Result<()> { let mut headers = Headers::new(); headers.insert(CACHE_CONTROL, "barrel_roll"); let entries = CacheControl::from_headers(headers)?.unwrap(); let mut entries = entries.iter(); assert!(entries.next().is_none()); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(CACHE_CONTROL, "min-fresh=0.9"); // floats are not supported let err = CacheControl::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/cache/clear_site_data/directive.rs000064400000000000000000000042110072674642500213360ustar 00000000000000use std::fmt::Display; use std::str::FromStr; /// An HTTP `Clear-Site-Data` directive. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#Directives) #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] pub enum ClearDirective { /// Indicates that the server wishes to remove locally cached data (i.e. the /// browser cache, see HTTP caching) for the origin of the response URL. /// Depending on the browser, this might also clear out things like /// pre-rendered pages, script caches, WebGL shader caches, or address bar /// suggestions. Cache, /// Indicates that the server wishes to remove all cookies for the origin of /// the response URL. HTTP authentication credentials are also cleared out. /// This affects the entire registered domain, including subdomains. So /// https://example.com as well as https://stage.example.com, will have /// cookies cleared. Cookies, /// Indicates that the server wishes to remove all DOM storage for the origin /// of the response URL. Storage, /// Indicates that the server wishes to reload all browsing contexts for the /// origin of the response (Location.reload). ExecutionContexts, } impl ClearDirective { /// Get the formatted string. pub fn as_str(&self) -> &'static str { match self { ClearDirective::Cache => r#""cache""#, ClearDirective::Cookies => r#""cookies""#, ClearDirective::Storage => r#""storage""#, ClearDirective::ExecutionContexts => r#""executionContexts""#, } } } impl Display for ClearDirective { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.as_str()) } } impl FromStr for ClearDirective { type Err = std::string::ParseError; fn from_str(s: &str) -> Result { match s { r#""cache""# => Ok(Self::Cache), r#""cookies""# => Ok(Self::Cookies), r#""storage""# => Ok(Self::Storage), r#""executionContexts""# => Ok(Self::ExecutionContexts), _ => todo!(), } } } http-types-2.12.0/src/cache/clear_site_data/mod.rs000064400000000000000000000201060072674642500201400ustar 00000000000000//! Clear browsing data (cookies, storage, cache) associated with the //! requesting website use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CLEAR_SITE_DATA}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; use std::option; use std::slice; use std::str::FromStr; mod directive; pub use directive::ClearDirective; /// Clear browsing data (cookies, storage, cache) associated with the /// requesting website. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data) /// /// # Specifications /// /// - [Clear Site Data](https://w3c.github.io/webappsec-clear-site-data/) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::cache::{ClearSiteData, ClearDirective}; /// /// let mut entries = ClearSiteData::new(); /// entries.push(ClearDirective::Cache); /// entries.push(ClearDirective::Cookies); /// /// let mut res = Response::new(200); /// entries.apply(&mut res); /// /// let entries = ClearSiteData::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); /// assert_eq!(entries.next().unwrap(), &ClearDirective::Cache); /// assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies); /// # /// # Ok(()) } /// ``` pub struct ClearSiteData { entries: Vec, wildcard: bool, } impl ClearSiteData { /// Create a new instance of `ClearSiteData`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let header_values = match headers.as_ref().get(CLEAR_SITE_DATA) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in header_values { for part in value.as_str().trim().split(',') { let part = part.trim(); if part == r#""*""# { wildcard = true; continue; } entries.push(ClearDirective::from_str(part)?); } } Ok(Some(Self { entries, wildcard })) } /// Sets the `If-Match` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(CLEAR_SITE_DATA, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CLEAR_SITE_DATA } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, etag) in self.entries.iter().enumerate() { match n { 0 => write!(output, "{}", etag.to_string()).unwrap(), _ => write!(output, ", {}", etag.to_string()).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, r#""*""#).unwrap(), _ => write!(output, r#", "*""#).unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) { self.entries.push(directive.into()); } /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// An iterator visiting all server entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all server entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for ClearSiteData { type Item = ClearDirective; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a ClearSiteData { type Item = &'a ClearDirective; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut ClearSiteData { type Item = &'a mut ClearDirective; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `ClearSiteData`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = ClearDirective; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `ClearSiteData`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, ClearDirective>, } impl<'a> Iterator for Iter<'a> { type Item = &'a ClearDirective; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `ClearSiteData`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, ClearDirective>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut ClearDirective; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for ClearSiteData { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for ClearSiteData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use crate::cache::{ClearDirective, ClearSiteData}; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut entries = ClearSiteData::new(); entries.push(ClearDirective::Cache); entries.push(ClearDirective::Cookies); let mut res = Response::new(200); entries.apply(&mut res); let entries = ClearSiteData::from_headers(res)?.unwrap(); let mut entries = entries.iter(); assert_eq!(entries.next().unwrap(), &ClearDirective::Cache); assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut entries = ClearSiteData::new(); entries.push(ClearDirective::Cache); entries.set_wildcard(true); let mut res = Response::new(200); entries.apply(&mut res); let entries = ClearSiteData::from_headers(res)?.unwrap(); assert!(entries.wildcard()); let mut entries = entries.iter(); assert_eq!(entries.next().unwrap(), &ClearDirective::Cache); Ok(()) } #[test] fn parse_quotes_correctly() -> crate::Result<()> { let mut res = Response::new(200); res.insert_header("clear-site-data", r#""cookies""#); let entries = ClearSiteData::from_headers(res)?.unwrap(); assert!(!entries.wildcard()); let mut entries = entries.iter(); assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies); let mut res = Response::new(200); res.insert_header("clear-site-data", r#""*""#); let entries = ClearSiteData::from_headers(res)?.unwrap(); assert!(entries.wildcard()); let mut entries = entries.iter(); assert_eq!(entries.next(), None); Ok(()) } } http-types-2.12.0/src/cache/expires.rs000064400000000000000000000071720072674642500157450ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, EXPIRES}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; use std::option; use std::time::{Duration, SystemTime}; /// HTTP `Expires` header /// /// # Specifications /// /// - [RFC 7234, section 5.3: Expires](https://tools.ietf.org/html/rfc7234#section-5.3) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::cache::Expires; /// use std::time::{SystemTime, Duration}; /// /// let time = SystemTime::now() + Duration::from_secs(5 * 60); /// let expires = Expires::new_at(time); /// /// let mut res = Response::new(200); /// expires.apply(&mut res); /// /// let expires = Expires::from_headers(res)?.unwrap(); /// /// // HTTP dates only have second-precision. /// let elapsed = time.duration_since(expires.expiration())?; /// assert_eq!(elapsed.as_secs(), 0); /// # /// # Ok(()) } /// ``` #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct Expires { instant: SystemTime, } impl Expires { /// Create a new instance of `Expires`. pub fn new(dur: Duration) -> Self { let instant = SystemTime::now() + dur; Self { instant } } /// Create a new instance of `Expires` from secs. pub fn new_at(instant: SystemTime) -> Self { Self { instant } } /// Get the expiration time. pub fn expiration(&self) -> SystemTime { self.instant } /// Create an instance of `Expires` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(EXPIRES) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header = headers.iter().last().unwrap(); let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(EXPIRES, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { EXPIRES } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl ToHeaderValues for Expires { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let time = SystemTime::now() + Duration::from_secs(5 * 60); let expires = Expires::new_at(time); let mut headers = Headers::new(); expires.apply(&mut headers); let expires = Expires::from_headers(headers)?.unwrap(); // HTTP dates only have second-precision let elapsed = time.duration_since(expires.expiration())?; assert_eq!(elapsed.as_secs(), 0); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(EXPIRES, ""); let err = Expires::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/cache/mod.rs000064400000000000000000000010520072674642500150340ustar 00000000000000//! HTTP caching. //! //! Web page performance can be significantly improved by caching resources. //! This submodule includes headers and types to communicate how and when to //! cache resources. //! //! # Further Reading //! //! - [MDN: HTTP Caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching) mod age; mod cache_control; mod clear_site_data; mod expires; pub use age::Age; pub use cache_control::CacheControl; pub use cache_control::CacheDirective; pub use clear_site_data::{ClearDirective, ClearSiteData}; pub use expires::Expires; http-types-2.12.0/src/conditional/etag.rs000064400000000000000000000135630072674642500164470ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ETAG}; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Display}; use std::option; /// HTTP Entity Tags. /// /// ETags provide an ID for a particular resource, enabling clients and servers /// to reason about caches and make conditional requests. /// /// # Specifications /// /// - [RFC 7232, section 2.3: ETag](https://tools.ietf.org/html/rfc7232#section-2.3) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::ETag; /// /// let etag = ETag::new("0xcafebeef".to_string()); /// /// let mut res = Response::new(200); /// etag.apply(&mut res); /// /// let etag = ETag::from_headers(res)?.unwrap(); /// assert_eq!(etag, ETag::Strong(String::from("0xcafebeef"))); /// # /// # Ok(()) } /// ``` #[derive(Debug, Clone, Eq, PartialEq)] pub enum ETag { /// An ETag using strong validation. Strong(String), /// An ETag using weak validation. Weak(String), } impl ETag { /// Create a new ETag that uses strong validation. pub fn new(s: String) -> Self { debug_assert!(!s.contains('\\'), "ETags ought to avoid backslash chars"); Self::Strong(s) } /// Create a new ETag that uses weak validation. pub fn new_weak(s: String) -> Self { debug_assert!(!s.contains('\\'), "ETags ought to avoid backslash chars"); Self::Weak(s) } /// Create a new instance from headers. /// /// Only a single ETag per resource is assumed to exist. If multiple ETag /// headers are found the last one is used. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(ETAG) { Some(headers) => headers, None => return Ok(None), }; // If a header is returned we can assume at least one exists. let s = headers.iter().last().unwrap().as_str(); Self::from_str(s).map(Some) } /// Sets the `ETag` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(ETAG, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { ETAG } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let s = self.to_string(); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(s.into()) } } /// Returns `true` if the ETag is a `Strong` value. pub fn is_strong(&self) -> bool { matches!(self, Self::Strong(_)) } /// Returns `true` if the ETag is a `Weak` value. pub fn is_weak(&self) -> bool { matches!(self, Self::Weak(_)) } /// Create an Etag from a string. pub(crate) fn from_str(s: &str) -> crate::Result { let mut weak = false; let s = match s.strip_prefix("W/") { Some(s) => { weak = true; s } None => s, }; let s = match s.strip_prefix('"').and_then(|s| s.strip_suffix('"')) { Some(s) => s.to_owned(), None => { return Err(Error::from_str( StatusCode::BadRequest, "Invalid ETag header", )) } }; if !s .bytes() .all(|c| c == 0x21 || (0x23..=0x7E).contains(&c) || c >= 0x80) { return Err(Error::from_str( StatusCode::BadRequest, "Invalid ETag header", )); } let etag = if weak { Self::Weak(s) } else { Self::Strong(s) }; Ok(etag) } } impl Display for ETag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Strong(s) => write!(f, r#""{}""#, s), Self::Weak(s) => write!(f, r#"W/"{}""#, s), } } } impl ToHeaderValues for ETag { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let etag = ETag::new("0xcafebeef".to_string()); let mut headers = Headers::new(); etag.apply(&mut headers); let etag = ETag::from_headers(headers)?.unwrap(); assert_eq!(etag, ETag::Strong(String::from("0xcafebeef"))); Ok(()) } #[test] fn smoke_weak() -> crate::Result<()> { let etag = ETag::new_weak("0xcafebeef".to_string()); let mut headers = Headers::new(); etag.apply(&mut headers); let etag = ETag::from_headers(headers)?.unwrap(); assert_eq!(etag, ETag::Weak(String::from("0xcafebeef"))); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(ETAG, ""); let err = ETag::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } #[test] fn validate_quotes() { assert_entry_err(r#""hello"#, "Invalid ETag header"); assert_entry_err(r#"hello""#, "Invalid ETag header"); assert_entry_err(r#"/O"valid content""#, "Invalid ETag header"); assert_entry_err(r#"/Wvalid content""#, "Invalid ETag header"); } fn assert_entry_err(s: &str, msg: &str) { let mut headers = Headers::new(); headers.insert(ETAG, s); let err = ETag::from_headers(headers).unwrap_err(); assert_eq!(format!("{}", err), msg); } #[test] fn validate_characters() { assert_entry_err(r#"""hello""#, "Invalid ETag header"); assert_entry_err("\"hello\x7F\"", "Invalid ETag header"); } } http-types-2.12.0/src/conditional/if_match.rs000064400000000000000000000161530072674642500172770ustar 00000000000000//! Apply the HTTP method if the ETag matches. use crate::conditional::ETag; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_MATCH}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; use std::option; use std::slice; /// Apply the HTTP method if the ETag matches. /// /// # Specifications /// /// - [RFC 7232, section 3.1: If-Match](https://tools.ietf.org/html/rfc7232#section-3.1) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::{IfMatch, ETag}; /// /// let mut entries = IfMatch::new(); /// entries.push(ETag::new("0xcafebeef".to_string())); /// entries.push(ETag::new("0xbeefcafe".to_string())); /// /// let mut res = Response::new(200); /// entries.apply(&mut res); /// /// let entries = IfMatch::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); /// assert_eq!(entries.next().unwrap(), &ETag::new("0xcafebeef".to_string())); /// assert_eq!(entries.next().unwrap(), &ETag::new("0xbeefcafe".to_string())); /// # /// # Ok(()) } /// ``` pub struct IfMatch { entries: Vec, wildcard: bool, } impl IfMatch { /// Create a new instance of `IfMatch`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(IF_MATCH) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in headers { for part in value.as_str().trim().split(',') { let part = part.trim(); if part == "*" { wildcard = true; continue; } entries.push(ETag::from_str(part)?); } } Ok(Some(Self { entries, wildcard })) } /// Sets the `If-Match` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(IF_MATCH, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { IF_MATCH } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, etag) in self.entries.iter().enumerate() { match n { 0 => write!(output, "{}", etag.to_string()).unwrap(), _ => write!(output, ", {}", etag.to_string()).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) { self.entries.push(directive.into()); } /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// An iterator visiting all server entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all server entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for IfMatch { type Item = ETag; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a IfMatch { type Item = &'a ETag; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut IfMatch { type Item = &'a mut ETag; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `IfMatch`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = ETag; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `IfMatch`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, ETag>, } impl<'a> Iterator for Iter<'a> { type Item = &'a ETag; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `IfMatch`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, ETag>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut ETag; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for IfMatch { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for IfMatch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use crate::conditional::{ETag, IfMatch}; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut entries = IfMatch::new(); entries.push(ETag::new("0xcafebeef".to_string())); entries.push(ETag::new("0xbeefcafe".to_string())); let mut res = Response::new(200); entries.apply(&mut res); let entries = IfMatch::from_headers(res)?.unwrap(); let mut entries = entries.iter(); assert_eq!( entries.next().unwrap(), &ETag::new("0xcafebeef".to_string()) ); assert_eq!( entries.next().unwrap(), &ETag::new("0xbeefcafe".to_string()) ); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut entries = IfMatch::new(); entries.push(ETag::new("0xcafebeef".to_string())); entries.set_wildcard(true); let mut res = Response::new(200); entries.apply(&mut res); let entries = IfMatch::from_headers(res)?.unwrap(); assert!(entries.wildcard()); let mut entries = entries.iter(); assert_eq!( entries.next().unwrap(), &ETag::new("0xcafebeef".to_string()) ); Ok(()) } } http-types-2.12.0/src/conditional/if_modified_since.rs000064400000000000000000000072650072674642500211500ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_MODIFIED_SINCE}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; use std::option; use std::time::SystemTime; /// Apply the HTTP method if the entity has been modified after the given /// date. /// /// # Specifications /// /// - [RFC 7232, section 3.3: If-Modified-Since](https://tools.ietf.org/html/rfc7232#section-3.3) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::IfModifiedSince; /// use std::time::{SystemTime, Duration}; /// /// let time = SystemTime::now() + Duration::from_secs(5 * 60); /// let expires = IfModifiedSince::new(time); /// /// let mut res = Response::new(200); /// expires.apply(&mut res); /// /// let expires = IfModifiedSince::from_headers(res)?.unwrap(); /// /// // HTTP dates only have second-precision. /// let elapsed = time.duration_since(expires.modified())?; /// assert_eq!(elapsed.as_secs(), 0); /// # /// # Ok(()) } /// ``` #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct IfModifiedSince { instant: SystemTime, } impl IfModifiedSince { /// Create a new instance of `IfModifiedSince`. pub fn new(instant: SystemTime) -> Self { Self { instant } } /// Returns the last modification time listed. pub fn modified(&self) -> SystemTime { self.instant } /// Create an instance of `IfModifiedSince` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(IF_MODIFIED_SINCE) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header = headers.iter().last().unwrap(); let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(IF_MODIFIED_SINCE, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { IF_MODIFIED_SINCE } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl ToHeaderValues for IfModifiedSince { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; use std::time::Duration; #[test] fn smoke() -> crate::Result<()> { let time = SystemTime::now() + Duration::from_secs(5 * 60); let expires = IfModifiedSince::new(time); let mut headers = Headers::new(); expires.apply(&mut headers); let expires = IfModifiedSince::from_headers(headers)?.unwrap(); // HTTP dates only have second-precision let elapsed = time.duration_since(expires.modified())?; assert_eq!(elapsed.as_secs(), 0); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(IF_MODIFIED_SINCE, ""); let err = IfModifiedSince::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/conditional/if_none_match.rs000064400000000000000000000166650072674642500203260ustar 00000000000000//! Apply the HTTP method if the ETags do not match. //! //! This is used to update caches or to prevent uploading a new resource when //! one already exists. use crate::conditional::ETag; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_NONE_MATCH}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; use std::option; use std::slice; /// Apply the HTTP method if the ETags do not match. /// /// This is used to update caches or to prevent uploading a new resource when /// one already exists. /// /// # Specifications /// /// - [RFC 7232, section 3.2: If-None-Match](https://tools.ietf.org/html/rfc7232#section-3.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::{IfNoneMatch, ETag}; /// /// let mut entries = IfNoneMatch::new(); /// entries.push(ETag::new("0xcafebeef".to_string())); /// entries.push(ETag::new("0xbeefcafe".to_string())); /// /// let mut res = Response::new(200); /// entries.apply(&mut res); /// /// let entries = IfNoneMatch::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); /// assert_eq!(entries.next().unwrap(), &ETag::new("0xcafebeef".to_string())); /// assert_eq!(entries.next().unwrap(), &ETag::new("0xbeefcafe".to_string())); /// # /// # Ok(()) } /// ``` pub struct IfNoneMatch { entries: Vec, wildcard: bool, } impl IfNoneMatch { /// Create a new instance of `IfNoneMatch`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(IF_NONE_MATCH) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in headers { for part in value.as_str().trim().split(',') { let part = part.trim(); if part == "*" { wildcard = true; continue; } entries.push(ETag::from_str(part)?); } } Ok(Some(Self { entries, wildcard })) } /// Sets the `If-None-Match` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(IF_NONE_MATCH, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { IF_NONE_MATCH } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, etag) in self.entries.iter().enumerate() { match n { 0 => write!(output, "{}", etag.to_string()).unwrap(), _ => write!(output, ", {}", etag.to_string()).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) { self.entries.push(directive.into()); } /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// An iterator visiting all server entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all server entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for IfNoneMatch { type Item = ETag; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a IfNoneMatch { type Item = &'a ETag; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut IfNoneMatch { type Item = &'a mut ETag; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `IfNoneMatch`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = ETag; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `IfNoneMatch`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, ETag>, } impl<'a> Iterator for Iter<'a> { type Item = &'a ETag; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `IfNoneMatch`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, ETag>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut ETag; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for IfNoneMatch { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for IfNoneMatch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use crate::conditional::{ETag, IfNoneMatch}; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut entries = IfNoneMatch::new(); entries.push(ETag::new("0xcafebeef".to_string())); entries.push(ETag::new("0xbeefcafe".to_string())); let mut res = Response::new(200); entries.apply(&mut res); let entries = IfNoneMatch::from_headers(res)?.unwrap(); let mut entries = entries.iter(); assert_eq!( entries.next().unwrap(), &ETag::new("0xcafebeef".to_string()) ); assert_eq!( entries.next().unwrap(), &ETag::new("0xbeefcafe".to_string()) ); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut entries = IfNoneMatch::new(); entries.push(ETag::new("0xcafebeef".to_string())); entries.set_wildcard(true); let mut res = Response::new(200); entries.apply(&mut res); let entries = IfNoneMatch::from_headers(res)?.unwrap(); assert!(entries.wildcard()); let mut entries = entries.iter(); assert_eq!( entries.next().unwrap(), &ETag::new("0xcafebeef".to_string()) ); Ok(()) } } http-types-2.12.0/src/conditional/if_unmodified_since.rs000064400000000000000000000073330072674642500215070ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, IF_UNMODIFIED_SINCE}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; use std::option; use std::time::SystemTime; /// Apply the HTTP method if the entity has not been modified after the /// given date. /// /// # Specifications /// /// - [RFC 7232, section 3.4: If-Unmodified-Since](https://tools.ietf.org/html/rfc7232#section-3.4) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::IfUnmodifiedSince; /// use std::time::{SystemTime, Duration}; /// /// let time = SystemTime::now() + Duration::from_secs(5 * 60); /// let expires = IfUnmodifiedSince::new(time); /// /// let mut res = Response::new(200); /// expires.apply(&mut res); /// /// let expires = IfUnmodifiedSince::from_headers(res)?.unwrap(); /// /// // HTTP dates only have second-precision. /// let elapsed = time.duration_since(expires.modified())?; /// assert_eq!(elapsed.as_secs(), 0); /// # /// # Ok(()) } /// ``` #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct IfUnmodifiedSince { instant: SystemTime, } impl IfUnmodifiedSince { /// Create a new instance of `IfUnmodifiedSince`. pub fn new(instant: SystemTime) -> Self { Self { instant } } /// Returns the last modification time listed. pub fn modified(&self) -> SystemTime { self.instant } /// Create an instance of `IfUnmodifiedSince` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(IF_UNMODIFIED_SINCE) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header = headers.iter().last().unwrap(); let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(IF_UNMODIFIED_SINCE, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { IF_UNMODIFIED_SINCE } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl ToHeaderValues for IfUnmodifiedSince { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; use std::time::Duration; #[test] fn smoke() -> crate::Result<()> { let time = SystemTime::now() + Duration::from_secs(5 * 60); let expires = IfUnmodifiedSince::new(time); let mut headers = Headers::new(); expires.apply(&mut headers); let expires = IfUnmodifiedSince::from_headers(headers)?.unwrap(); // HTTP dates only have second-precision let elapsed = time.duration_since(expires.modified())?; assert_eq!(elapsed.as_secs(), 0); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(IF_UNMODIFIED_SINCE, ""); let err = IfUnmodifiedSince::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/conditional/last_modified.rs000064400000000000000000000072060072674642500203270ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, LAST_MODIFIED}; use crate::utils::{fmt_http_date, parse_http_date}; use std::fmt::Debug; use std::option; use std::time::SystemTime; /// The last modification date of a resource. /// /// # Specifications /// /// - [RFC 7232, section 2.2: Last-Modified](https://tools.ietf.org/html/rfc7232#section-2.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::LastModified; /// use std::time::{SystemTime, Duration}; /// /// let time = SystemTime::now() + Duration::from_secs(5 * 60); /// let last_modified = LastModified::new(time); /// /// let mut res = Response::new(200); /// last_modified.apply(&mut res); /// /// let last_modified = LastModified::from_headers(res)?.unwrap(); /// /// // HTTP dates only have second-precision. /// let elapsed = time.duration_since(last_modified.modified())?; /// assert_eq!(elapsed.as_secs(), 0); /// # /// # Ok(()) } /// ``` #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct LastModified { instant: SystemTime, } impl LastModified { /// Create a new instance of `LastModified`. pub fn new(instant: SystemTime) -> Self { Self { instant } } /// Returns the last modification time listed. pub fn modified(&self) -> SystemTime { self.instant } /// Create an instance of `LastModified` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(LAST_MODIFIED) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header = headers.iter().last().unwrap(); let instant = parse_http_date(header.as_str())?; Ok(Some(Self { instant })) } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(LAST_MODIFIED, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { LAST_MODIFIED } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = fmt_http_date(self.instant); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl ToHeaderValues for LastModified { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; use std::time::Duration; #[test] fn smoke() -> crate::Result<()> { let time = SystemTime::now() + Duration::from_secs(5 * 60); let last_modified = LastModified::new(time); let mut headers = Headers::new(); last_modified.apply(&mut headers); let last_modified = LastModified::from_headers(headers)?.unwrap(); // HTTP dates only have second-precision let elapsed = time.duration_since(last_modified.modified())?; assert_eq!(elapsed.as_secs(), 0); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(LAST_MODIFIED, ""); let err = LastModified::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/conditional/mod.rs000064400000000000000000000014250072674642500163000ustar 00000000000000//! HTTP conditional headers. //! //! Web page performance can be significantly improved by caching resources. //! This submodule includes headers and types to communicate how and when to //! cache resources. //! //! # Further Reading //! //! - [MDN: HTTP Conditional Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Conditional_requests) mod etag; mod if_modified_since; mod if_unmodified_since; mod last_modified; mod vary; pub mod if_match; pub mod if_none_match; pub use etag::ETag; pub use vary::Vary; #[doc(inline)] pub use if_match::IfMatch; #[doc(inline)] pub use if_modified_since::IfModifiedSince; #[doc(inline)] pub use if_none_match::IfNoneMatch; #[doc(inline)] pub use if_unmodified_since::IfUnmodifiedSince; #[doc(inline)] pub use last_modified::LastModified; http-types-2.12.0/src/conditional/vary.rs000064400000000000000000000160120072674642500165000ustar 00000000000000//! Apply the HTTP method if the ETag matches. use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, VARY}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; use std::option; use std::slice; use std::str::FromStr; /// Apply the HTTP method if the ETag matches. /// /// # Specifications /// /// - [RFC 7231, section 7.1.4: Vary](https://tools.ietf.org/html/rfc7231#section-7.1.4) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::conditional::Vary; /// /// let mut entries = Vary::new(); /// entries.push("User-Agent")?; /// entries.push("Accept-Encoding")?; /// /// let mut res = Response::new(200); /// entries.apply(&mut res); /// /// let entries = Vary::from_headers(res)?.unwrap(); /// let mut entries = entries.iter(); /// assert_eq!(entries.next().unwrap(), "User-Agent"); /// assert_eq!(entries.next().unwrap(), "Accept-Encoding"); /// # /// # Ok(()) } /// ``` pub struct Vary { entries: Vec, wildcard: bool, } impl Vary { /// Create a new instance of `Vary`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(VARY) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in headers { for part in value.as_str().trim().split(',') { let part = part.trim(); if part == "*" { wildcard = true; continue; } let entry = HeaderName::from_str(part.trim())?; entries.push(entry); } } Ok(Some(Self { entries, wildcard })) } /// Sets the `If-Match` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(VARY, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { VARY } /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, name) in self.entries.iter().enumerate() { let directive: HeaderValue = name .as_str() .parse() .expect("Could not convert a HeaderName into a HeaderValue"); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push a directive into the list of entries. pub fn push(&mut self, directive: impl Into) -> crate::Result<()> { self.entries.push(directive.into()); Ok(()) } /// An iterator visiting all server entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all server entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for Vary { type Item = HeaderName; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a Vary { type Item = &'a HeaderName; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut Vary { type Item = &'a mut HeaderName; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `Vary`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = HeaderName; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `Vary`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, HeaderName>, } impl<'a> Iterator for Iter<'a> { type Item = &'a HeaderName; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `Vary`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, HeaderName>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut HeaderName; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for Vary { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for Vary { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use crate::conditional::Vary; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut entries = Vary::new(); entries.push("User-Agent")?; entries.push("Accept-Encoding")?; let mut res = Response::new(200); entries.apply(&mut res); let entries = Vary::from_headers(res)?.unwrap(); let mut entries = entries.iter(); assert_eq!(entries.next().unwrap(), "User-Agent"); assert_eq!(entries.next().unwrap(), "Accept-Encoding"); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut entries = Vary::new(); entries.push("User-Agent")?; entries.set_wildcard(true); let mut res = Response::new(200); entries.apply(&mut res); let entries = Vary::from_headers(res)?.unwrap(); assert!(entries.wildcard()); let mut entries = entries.iter(); assert_eq!(entries.next().unwrap(), "User-Agent"); Ok(()) } } http-types-2.12.0/src/content/accept.rs000064400000000000000000000304520072674642500161310ustar 00000000000000//! Client header advertising which media types the client is able to understand. use crate::content::{ContentType, MediaTypeProposal}; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT}; use crate::utils::sort_by_weight; use crate::{Error, Mime, StatusCode}; use std::fmt::{self, Debug, Write}; use std::option; use std::slice; /// Client header advertising which media types the client is able to understand. /// /// Using content negotiation, the server then selects one of the proposals, uses /// it and informs the client of its choice with the `Content-Type` response /// header. Browsers set adequate values for this header depending on the context /// where the request is done: when fetching a CSS stylesheet a different value /// is set for the request than when fetching an image, video or a script. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) /// /// # Specifications /// /// - [RFC 7231, section 5.3.2: Accept](https://tools.ietf.org/html/rfc7231#section-5.3.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::content::{Accept, MediaTypeProposal}; /// use http_types::{mime, Response}; /// /// let mut accept = Accept::new(); /// accept.push(MediaTypeProposal::new(mime::HTML, Some(0.8))?); /// accept.push(MediaTypeProposal::new(mime::XML, Some(0.4))?); /// accept.push(mime::PLAIN); /// /// let mut res = Response::new(200); /// let content_type = accept.negotiate(&[mime::XML])?; /// content_type.apply(&mut res); /// /// assert_eq!(res["Content-Type"], "application/xml;charset=utf-8"); /// # /// # Ok(()) } /// ``` pub struct Accept { wildcard: bool, entries: Vec, } impl Accept { /// Create a new instance of `Accept`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create an instance of `Accept` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(ACCEPT) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in headers { for part in value.as_str().trim().split(',') { let part = part.trim(); // Handle empty strings, and wildcard directives. if part.is_empty() { continue; } else if part == "*" { wildcard = true; continue; } // Try and parse a directive from a str. If the directive is // unkown we skip it. let entry = MediaTypeProposal::from_str(part)?; entries.push(entry); } } Ok(Some(Self { entries, wildcard })) } /// Push a directive into the list of entries. pub fn push(&mut self, prop: impl Into) { self.entries.push(prop.into()); } /// Returns `true` if a wildcard directive was passed. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// Sort the header directives by weight. /// /// Headers with a higher `q=` value will be returned first. If two /// directives have the same weight, the directive that was declared later /// will be returned first. pub fn sort(&mut self) { sort_by_weight(&mut self.entries); } /// Determine the most suitable `Content-Type` encoding. /// /// # Errors /// /// If no suitable encoding is found, an error with the status of `406` will be returned. pub fn negotiate(&mut self, available: &[Mime]) -> crate::Result { // Start by ordering the encodings. self.sort(); // Try and find the first encoding that matches. for accept in &self.entries { if available.contains(accept) { return Ok(accept.media_type.clone().into()); } } // If no encoding matches and wildcard is set, send whichever encoding we got. if self.wildcard { if let Some(accept) = available.iter().next() { return Ok(accept.clone().into()); } } let mut err = Error::new_adhoc("No suitable Content-Type found"); err.set_status(StatusCode::NotAcceptable); Err(err) } /// Sets the `Accept-Encoding` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(ACCEPT, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { ACCEPT } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = directive.clone().into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), } } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// An iterator visiting all entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for Accept { type Item = MediaTypeProposal; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a Accept { type Item = &'a MediaTypeProposal; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut Accept { type Item = &'a mut MediaTypeProposal; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `Accept`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = MediaTypeProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `Accept`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, MediaTypeProposal>, } impl<'a> Iterator for Iter<'a> { type Item = &'a MediaTypeProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `Accept`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, MediaTypeProposal>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut MediaTypeProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for Accept { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for Accept { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use super::*; use crate::mime; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(mime::HTML); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), mime::HTML); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut accept = Accept::new(); accept.set_wildcard(true); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); Ok(()) } #[test] fn wildcard_and_header() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(mime::HTML); accept.set_wildcard(true); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); assert_eq!(accept.iter().next().unwrap(), mime::HTML); Ok(()) } #[test] fn iter() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(mime::HTML); accept.push(mime::XML); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = Accept::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), mime::HTML); assert_eq!(accept.next().unwrap(), mime::XML); Ok(()) } #[test] fn reorder_based_on_weight() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(MediaTypeProposal::new(mime::HTML, Some(0.4))?); accept.push(MediaTypeProposal::new(mime::XML, None)?); accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); let mut headers = Response::new(200); accept.apply(&mut headers); let mut accept = Accept::from_headers(headers)?.unwrap(); accept.sort(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), mime::PLAIN); assert_eq!(accept.next().unwrap(), mime::HTML); assert_eq!(accept.next().unwrap(), mime::XML); Ok(()) } #[test] fn reorder_based_on_weight_and_location() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(MediaTypeProposal::new(mime::HTML, None)?); accept.push(MediaTypeProposal::new(mime::XML, None)?); accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); let mut res = Response::new(200); accept.apply(&mut res); let mut accept = Accept::from_headers(res)?.unwrap(); accept.sort(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), mime::PLAIN); assert_eq!(accept.next().unwrap(), mime::XML); assert_eq!(accept.next().unwrap(), mime::HTML); Ok(()) } #[test] fn negotiate() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(MediaTypeProposal::new(mime::HTML, Some(0.4))?); accept.push(MediaTypeProposal::new(mime::PLAIN, Some(0.8))?); accept.push(MediaTypeProposal::new(mime::XML, None)?); assert_eq!(accept.negotiate(&[mime::HTML, mime::XML])?, mime::HTML); Ok(()) } #[test] fn negotiate_not_acceptable() -> crate::Result<()> { let mut accept = Accept::new(); let err = accept.negotiate(&[mime::JSON]).unwrap_err(); assert_eq!(err.status(), 406); let mut accept = Accept::new(); accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?); let err = accept.negotiate(&[mime::XML]).unwrap_err(); assert_eq!(err.status(), 406); Ok(()) } #[test] fn negotiate_wildcard() -> crate::Result<()> { let mut accept = Accept::new(); accept.push(MediaTypeProposal::new(mime::JSON, Some(0.8))?); accept.set_wildcard(true); assert_eq!(accept.negotiate(&[mime::XML])?, mime::XML); Ok(()) } } http-types-2.12.0/src/content/accept_encoding.rs000064400000000000000000000305270072674642500200020ustar 00000000000000//! Client header advertising available compression algorithms. use crate::content::{ContentEncoding, Encoding, EncodingProposal}; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING}; use crate::utils::sort_by_weight; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Write}; use std::option; use std::slice; /// Client header advertising available compression algorithms. /// /// # Specifications /// /// - [RFC 7231, section 5.3.4: Accept-Encoding](https://tools.ietf.org/html/rfc7231#section-5.3.4) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::content::{AcceptEncoding, ContentEncoding, Encoding, EncodingProposal}; /// use http_types::Response; /// /// let mut accept = AcceptEncoding::new(); /// accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); /// accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?); /// accept.push(EncodingProposal::new(Encoding::Identity, None)?); /// /// let mut res = Response::new(200); /// let encoding = accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?; /// encoding.apply(&mut res); /// /// assert_eq!(res["Content-Encoding"], "br"); /// # /// # Ok(()) } /// ``` pub struct AcceptEncoding { wildcard: bool, entries: Vec, } impl AcceptEncoding { /// Create a new instance of `AcceptEncoding`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create an instance of `AcceptEncoding` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(ACCEPT_ENCODING) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in headers { for part in value.as_str().trim().split(',') { let part = part.trim(); // Handle empty strings, and wildcard directives. if part.is_empty() { continue; } else if part == "*" { wildcard = true; continue; } // Try and parse a directive from a str. If the directive is // unkown we skip it. if let Some(entry) = EncodingProposal::from_str(part)? { entries.push(entry); } } } Ok(Some(Self { entries, wildcard })) } /// Push a directive into the list of entries. pub fn push(&mut self, prop: impl Into) { self.entries.push(prop.into()); } /// Returns `true` if a wildcard directive was passed. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// Sort the header directives by weight. /// /// Headers with a higher `q=` value will be returned first. If two /// directives have the same weight, the directive that was declared later /// will be returned first. pub fn sort(&mut self) { sort_by_weight(&mut self.entries); } /// Determine the most suitable `Content-Encoding` encoding. /// /// # Errors /// /// If no suitable encoding is found, an error with the status of `406` will be returned. pub fn negotiate(&mut self, available: &[Encoding]) -> crate::Result { // Start by ordering the encodings. self.sort(); // Try and find the first encoding that matches. for encoding in &self.entries { if available.contains(encoding) { return Ok(encoding.into()); } } // If no encoding matches and wildcard is set, send whichever encoding we got. if self.wildcard { if let Some(encoding) = available.iter().next() { return Ok(encoding.into()); } } let mut err = Error::new_adhoc("No suitable Content-Encoding found"); err.set_status(StatusCode::NotAcceptable); Err(err) } /// Sets the `Accept-Encoding` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(ACCEPT_ENCODING, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { ACCEPT_ENCODING } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = (*directive).into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), } } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// An iterator visiting all entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for AcceptEncoding { type Item = EncodingProposal; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a AcceptEncoding { type Item = &'a EncodingProposal; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut AcceptEncoding { type Item = &'a mut EncodingProposal; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `AcceptEncoding`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = EncodingProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `AcceptEncoding`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, EncodingProposal>, } impl<'a> Iterator for Iter<'a> { type Item = &'a EncodingProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `AcceptEncoding`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, EncodingProposal>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut EncodingProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for AcceptEncoding { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for AcceptEncoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use super::*; use crate::content::Encoding; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(Encoding::Gzip); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.set_wildcard(true); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); Ok(()) } #[test] fn wildcard_and_header() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(Encoding::Gzip); accept.set_wildcard(true); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); Ok(()) } #[test] fn iter() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(Encoding::Gzip); accept.push(Encoding::Brotli); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = AcceptEncoding::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), Encoding::Gzip); assert_eq!(accept.next().unwrap(), Encoding::Brotli); Ok(()) } #[test] fn reorder_based_on_weight() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?); accept.push(EncodingProposal::new(Encoding::Identity, None)?); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut headers = Response::new(200); accept.apply(&mut headers); let mut accept = AcceptEncoding::from_headers(headers)?.unwrap(); accept.sort(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), Encoding::Brotli); assert_eq!(accept.next().unwrap(), Encoding::Gzip); assert_eq!(accept.next().unwrap(), Encoding::Identity); Ok(()) } #[test] fn reorder_based_on_weight_and_location() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(EncodingProposal::new(Encoding::Identity, None)?); accept.push(EncodingProposal::new(Encoding::Gzip, None)?); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut res = Response::new(200); accept.apply(&mut res); let mut accept = AcceptEncoding::from_headers(res)?.unwrap(); accept.sort(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), Encoding::Brotli); assert_eq!(accept.next().unwrap(), Encoding::Gzip); assert_eq!(accept.next().unwrap(), Encoding::Identity); Ok(()) } #[test] fn negotiate() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?); accept.push(EncodingProposal::new(Encoding::Identity, None)?); assert_eq!( accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?, Encoding::Brotli, ); Ok(()) } #[test] fn negotiate_not_acceptable() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err(); assert_eq!(err.status(), 406); let mut accept = AcceptEncoding::new(); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err(); assert_eq!(err.status(), 406); Ok(()) } #[test] fn negotiate_wildcard() -> crate::Result<()> { let mut accept = AcceptEncoding::new(); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); accept.set_wildcard(true); assert_eq!(accept.negotiate(&[Encoding::Gzip])?, Encoding::Gzip); Ok(()) } } http-types-2.12.0/src/content/content_encoding.rs000064400000000000000000000071430072674642500202130ustar 00000000000000//! Specify the compression algorithm. use crate::content::{Encoding, EncodingProposal}; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CONTENT_ENCODING}; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; use std::option; /// Specify the compression algorithm. /// /// # Specifications /// /// - [RFC 7231, section 3.1.2.2: Content-Encoding](https://tools.ietf.org/html/rfc7231#section-3.1.2.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::content::{ContentEncoding, Encoding}; /// let mut encoding = ContentEncoding::new(Encoding::Gzip); /// /// let mut res = Response::new(200); /// encoding.apply(&mut res); /// /// let encoding = ContentEncoding::from_headers(res)?.unwrap(); /// assert_eq!(encoding, &Encoding::Gzip); /// # /// # Ok(()) } /// ``` pub struct ContentEncoding { inner: Encoding, } impl ContentEncoding { /// Create a new instance of `CacheControl`. pub fn new(encoding: Encoding) -> Self { Self { inner: encoding } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_ENCODING) { Some(headers) => headers, None => return Ok(None), }; let mut inner = None; for value in headers { if let Some(entry) = Encoding::from_str(value.as_str()) { inner = Some(entry); } } let inner = inner.expect("Headers instance with no entries found"); Ok(Some(Self { inner })) } /// Sets the `Content-Encoding` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(CONTENT_ENCODING, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CONTENT_ENCODING } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { self.inner.into() } /// Access the encoding kind. pub fn encoding(&self) -> Encoding { self.inner } } impl ToHeaderValues for ContentEncoding { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Deref for ContentEncoding { type Target = Encoding; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for ContentEncoding { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl PartialEq for ContentEncoding { fn eq(&self, other: &Encoding) -> bool { &self.inner == other } } impl PartialEq<&Encoding> for ContentEncoding { fn eq(&self, other: &&Encoding) -> bool { &&self.inner == other } } impl From for ContentEncoding { fn from(encoding: Encoding) -> Self { Self { inner: encoding } } } impl From<&Encoding> for ContentEncoding { fn from(encoding: &Encoding) -> Self { Self { inner: *encoding } } } impl From for ContentEncoding { fn from(encoding: EncodingProposal) -> Self { Self { inner: encoding.encoding, } } } impl From<&EncodingProposal> for ContentEncoding { fn from(encoding: &EncodingProposal) -> Self { Self { inner: encoding.encoding, } } } impl Debug for ContentEncoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } http-types-2.12.0/src/content/content_length.rs000064400000000000000000000055050072674642500177060ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_LENGTH}; use crate::Status; /// The size of the entity-body, in bytes, sent to the recipient. /// /// # Specifications /// /// - [RFC 7230, section 3.3.2: Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::content::{ContentLength}; /// /// let content_len = ContentLength::new(12); /// /// let mut res = Response::new(200); /// content_len.apply(&mut res); /// /// let content_len = ContentLength::from_headers(res)?.unwrap(); /// assert_eq!(content_len.len(), 12); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct ContentLength { length: u64, } #[allow(clippy::len_without_is_empty)] impl ContentLength { /// Create a new instance. pub fn new(length: u64) -> Self { Self { length } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_LENGTH) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let value = headers.iter().last().unwrap(); let length = value.as_str().trim().parse().status(400)?; Ok(Some(Self { length })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CONTENT_LENGTH } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = format!("{}", self.length); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Get the content length. pub fn len(&self) -> u64 { self.length } /// Set the content length. pub fn set_len(&mut self, len: u64) { self.length = len; } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let content_len = ContentLength::new(12); let mut headers = Headers::new(); content_len.apply(&mut headers); let content_len = ContentLength::from_headers(headers)?.unwrap(); assert_eq!(content_len.len(), 12); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(CONTENT_LENGTH, ""); let err = ContentLength::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/content/content_location.rs000064400000000000000000000074420072674642500202370ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_LOCATION}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; /// Indicates an alternate location for the returned data. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Location) /// /// # Specifications /// /// - [RFC 7231, section 3.1.4.2: Content-Location](https://tools.ietf.org/html/rfc7231#section-3.1.4.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::{Response, Url}; /// use http_types::content::ContentLocation; /// /// let content_location = ContentLocation::new(Url::parse("https://example.net/")?); /// /// let mut res = Response::new(200); /// content_location.apply(&mut res); /// /// let url = Url::parse("https://example.net/")?; /// let content_location = ContentLocation::from_headers(url, res)?.unwrap(); /// assert_eq!(content_location.location(), &Url::parse("https://example.net/")?); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct ContentLocation { url: Url, } impl ContentLocation { /// Create a new instance of `Content-Location` header. pub fn new(url: Url) -> Self { Self { url } } /// Create a new instance from headers. pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> where U: TryInto, U::Error: std::fmt::Debug, { let headers = match headers.as_ref().get(CONTENT_LOCATION) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let value = headers.iter().last().unwrap(); let base = match base_url.try_into() { Ok(b) => b, Err(_) => bail!(400, "Invalid base url provided"), }; let url = base.join(value.as_str().trim()).status(400)?; Ok(Some(Self { url })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CONTENT_LOCATION } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = self.url.to_string(); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Get the url. pub fn location(&self) -> &Url { &self.url } /// Set the url. pub fn set_location(&mut self, location: U) where U: TryInto, U::Error: std::fmt::Debug, { self.url = location .try_into() .expect("Could not convert into valid URL") } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let content_location = ContentLocation::new(Url::parse("https://example.net/test.json")?); let mut headers = Headers::new(); content_location.apply(&mut headers); let content_location = ContentLocation::from_headers(Url::parse("https://example.net/").unwrap(), headers)? .unwrap(); assert_eq!( content_location.location(), &Url::parse("https://example.net/test.json")? ); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(CONTENT_LOCATION, "htt://"); let err = ContentLocation::from_headers(Url::parse("https://example.net").unwrap(), headers) .unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/content/content_type.rs000064400000000000000000000076030072674642500174070ustar 00000000000000use std::{convert::TryInto, str::FromStr}; use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_TYPE}; use crate::Mime; /// Indicate the media type of a resource's content. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) /// /// # Specifications /// /// - [RFC 7231, section 3.1.1.5: Content-Type](https://tools.ietf.org/html/rfc7231#section-3.1.1.5) /// - [RFC 7233, section 4.1: Content-Type in multipart](https://tools.ietf.org/html/rfc7233#section-4.1) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::content::ContentType; /// use http_types::{Response, Mime}; /// use std::str::FromStr; /// /// let content_type = ContentType::new("text/*"); /// /// let mut res = Response::new(200); /// content_type.apply(&mut res); /// /// let content_type = ContentType::from_headers(res)?.unwrap(); /// assert_eq!(content_type.value(), format!("{}", Mime::from_str("text/*")?).as_str()); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct ContentType { media_type: Mime, } impl ContentType { /// Create a new instance. pub fn new(media_type: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Self { media_type: media_type .try_into() .expect("could not convert into a valid Mime type"), } } /// Create a new instance from headers. /// /// `Content-Type` headers can provide both full and partial URLs. In /// order to always return fully qualified URLs, a base URL must be passed to /// reference the current environment. In HTTP/1.1 and above this value can /// always be determined from the request. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_TYPE) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let ctation = headers.iter().last().unwrap(); let media_type = Mime::from_str(ctation.as_str()).map_err(|mut e| { e.set_status(400); e })?; Ok(Some(Self { media_type })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CONTENT_TYPE } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = format!("{}", self.media_type); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl PartialEq for ContentType { fn eq(&self, other: &Mime) -> bool { &self.media_type == other } } impl PartialEq<&Mime> for ContentType { fn eq(&self, other: &&Mime) -> bool { &&self.media_type == other } } impl From for ContentType { fn from(media_type: Mime) -> Self { Self { media_type } } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let ct = ContentType::new(Mime::from_str("text/*")?); let mut headers = Headers::new(); ct.apply(&mut headers); let ct = ContentType::from_headers(headers)?.unwrap(); assert_eq!( ct.value(), format!("{}", Mime::from_str("text/*")?).as_str() ); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(CONTENT_TYPE, ""); let err = ContentType::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/content/encoding.rs000064400000000000000000000030000072674642500164450ustar 00000000000000use crate::headers::HeaderValue; use std::fmt::{self, Display}; /// Available compression algorithms. #[non_exhaustive] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Encoding { /// The Gzip encoding. Gzip, /// The Deflate encoding. Deflate, /// The Brotli encoding. Brotli, /// The Zstd encoding. Zstd, /// No encoding. Identity, } impl Encoding { /// Parses a given string into its corresponding encoding. pub(crate) fn from_str(s: &str) -> Option { let s = s.trim(); // We're dealing with an empty string. if s.is_empty() { return None; } match s { "gzip" => Some(Encoding::Gzip), "deflate" => Some(Encoding::Deflate), "br" => Some(Encoding::Brotli), "zstd" => Some(Encoding::Zstd), "identity" => Some(Encoding::Identity), _ => None, } } } impl Display for Encoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Encoding::Gzip => write!(f, "gzip"), Encoding::Deflate => write!(f, "deflate"), Encoding::Brotli => write!(f, "br"), Encoding::Zstd => write!(f, "zstd"), Encoding::Identity => write!(f, "identity"), } } } impl From for HeaderValue { fn from(directive: Encoding) -> Self { let s = directive.to_string(); unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } } } http-types-2.12.0/src/content/encoding_proposal.rs000064400000000000000000000100660072674642500203760ustar 00000000000000use crate::content::Encoding; use crate::ensure; use crate::headers::HeaderValue; use crate::utils::parse_weight; use std::cmp::{Ordering, PartialEq}; use std::ops::{Deref, DerefMut}; /// A proposed `Encoding` in `AcceptEncoding`. #[derive(Debug, Clone, Copy, PartialEq)] pub struct EncodingProposal { /// The proposed encoding. pub(crate) encoding: Encoding, /// The weight of the proposal. /// /// This is a number between 0.0 and 1.0, and is max 3 decimal points. weight: Option, } impl EncodingProposal { /// Create a new instance of `EncodingProposal`. pub fn new(encoding: impl Into, weight: Option) -> crate::Result { if let Some(weight) = weight { ensure!( weight.is_sign_positive() && weight <= 1.0, "EncodingProposal should have a weight between 0.0 and 1.0" ) } Ok(Self { encoding: encoding.into(), weight, }) } /// Get the proposed encoding. pub fn encoding(&self) -> &Encoding { &self.encoding } /// Get the weight of the proposal. pub fn weight(&self) -> Option { self.weight } pub(crate) fn from_str(s: &str) -> crate::Result> { let mut parts = s.split(';'); let encoding = match Encoding::from_str(parts.next().unwrap()) { Some(encoding) => encoding, None => return Ok(None), }; let weight = parts.next().map(parse_weight).transpose()?; Ok(Some(Self::new(encoding, weight)?)) } } impl From for EncodingProposal { fn from(encoding: Encoding) -> Self { Self { encoding, weight: None, } } } impl PartialEq for EncodingProposal { fn eq(&self, other: &Encoding) -> bool { self.encoding == *other } } impl PartialEq for &EncodingProposal { fn eq(&self, other: &Encoding) -> bool { self.encoding == *other } } impl Deref for EncodingProposal { type Target = Encoding; fn deref(&self) -> &Self::Target { &self.encoding } } impl DerefMut for EncodingProposal { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.encoding } } // NOTE: Firefox populates Accept-Encoding as `gzip, deflate, br`. This means // when parsing encodings we should choose the last value in the list under // equal weights. This impl doesn't know which value was passed later, so that // behavior needs to be handled separately. // // NOTE: This comparison does not include a notion of `*` (any value is valid). // that needs to be handled separately. impl PartialOrd for EncodingProposal { fn partial_cmp(&self, other: &Self) -> Option { match (self.weight, other.weight) { (Some(left), Some(right)) => left.partial_cmp(&right), (Some(_), None) => Some(Ordering::Greater), (None, Some(_)) => Some(Ordering::Less), (None, None) => None, } } } impl From for HeaderValue { fn from(entry: EncodingProposal) -> HeaderValue { let s = match entry.weight { Some(weight) => format!("{};q={:.3}", entry.encoding, weight), None => entry.encoding.to_string(), }; unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } } } #[cfg(test)] mod test { use super::*; #[test] fn smoke() { let _ = EncodingProposal::new(Encoding::Gzip, Some(0.0)).unwrap(); let _ = EncodingProposal::new(Encoding::Gzip, Some(0.5)).unwrap(); let _ = EncodingProposal::new(Encoding::Gzip, Some(1.0)).unwrap(); } #[test] fn error_code_500() { let err = EncodingProposal::new(Encoding::Gzip, Some(1.1)).unwrap_err(); assert_eq!(err.status(), 500); let err = EncodingProposal::new(Encoding::Gzip, Some(-0.1)).unwrap_err(); assert_eq!(err.status(), 500); let err = EncodingProposal::new(Encoding::Gzip, Some(-0.0)).unwrap_err(); assert_eq!(err.status(), 500); } } http-types-2.12.0/src/content/media_type_proposal.rs000064400000000000000000000105170072674642500207310ustar 00000000000000use crate::ensure; use crate::headers::HeaderValue; use crate::Mime; use std::ops::{Deref, DerefMut}; use std::{ cmp::{Ordering, PartialEq}, str::FromStr, }; /// A proposed Media Type for the `Accept` header. #[derive(Debug, Clone, PartialEq)] pub struct MediaTypeProposal { /// The proposed media_type. pub(crate) media_type: Mime, /// The weight of the proposal. /// /// This is a number between 0.0 and 1.0, and is max 3 decimal points. weight: Option, } impl MediaTypeProposal { /// Create a new instance of `MediaTypeProposal`. pub fn new(media_type: impl Into, weight: Option) -> crate::Result { if let Some(weight) = weight { ensure!( weight.is_sign_positive() && weight <= 1.0, "MediaTypeProposal should have a weight between 0.0 and 1.0" ) } Ok(Self { media_type: media_type.into(), weight, }) } /// Get the proposed media_type. pub fn media_type(&self) -> &Mime { &self.media_type } /// Get the weight of the proposal. pub fn weight(&self) -> Option { self.weight } /// Parse a string into a media type proposal. /// /// Because `;` and `q=0.0` are all valid values for in use in a media type, /// we have to parse the full string to the media type first, and then see if /// a `q` value has been set. pub(crate) fn from_str(s: &str) -> crate::Result { let mut media_type = Mime::from_str(s)?; let weight = media_type .remove_param("q") .map(|param| param.as_str().parse()) .transpose()?; Self::new(media_type, weight) } } impl From for MediaTypeProposal { fn from(media_type: Mime) -> Self { Self { media_type, weight: None, } } } impl From for Mime { fn from(accept: MediaTypeProposal) -> Self { accept.media_type } } impl PartialEq for MediaTypeProposal { fn eq(&self, other: &Mime) -> bool { self.media_type == *other } } impl PartialEq for &MediaTypeProposal { fn eq(&self, other: &Mime) -> bool { self.media_type == *other } } impl Deref for MediaTypeProposal { type Target = Mime; fn deref(&self) -> &Self::Target { &self.media_type } } impl DerefMut for MediaTypeProposal { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.media_type } } // NOTE: For Accept-Encoding Firefox sends the values: `gzip, deflate, br`. This means // when parsing media_types we should choose the last value in the list under // equal weights. This impl doesn't know which value was passed later, so that // behavior needs to be handled separately. // // NOTE: This comparison does not include a notion of `*` (any value is valid). // that needs to be handled separately. impl PartialOrd for MediaTypeProposal { fn partial_cmp(&self, other: &Self) -> Option { match (self.weight, other.weight) { (Some(left), Some(right)) => left.partial_cmp(&right), (Some(_), None) => Some(Ordering::Greater), (None, Some(_)) => Some(Ordering::Less), (None, None) => None, } } } impl From for HeaderValue { fn from(entry: MediaTypeProposal) -> HeaderValue { let s = match entry.weight { Some(weight) => format!("{};q={:.3}", entry.media_type, weight), None => entry.media_type.to_string(), }; unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } } } #[cfg(test)] mod test { use super::*; use crate::mime; #[test] fn smoke() { let _ = MediaTypeProposal::new(mime::JSON, Some(0.0)).unwrap(); let _ = MediaTypeProposal::new(mime::XML, Some(0.5)).unwrap(); let _ = MediaTypeProposal::new(mime::HTML, Some(1.0)).unwrap(); } #[test] fn error_code_500() { let err = MediaTypeProposal::new(mime::JSON, Some(1.1)).unwrap_err(); assert_eq!(err.status(), 500); let err = MediaTypeProposal::new(mime::XML, Some(-0.1)).unwrap_err(); assert_eq!(err.status(), 500); let err = MediaTypeProposal::new(mime::HTML, Some(-0.0)).unwrap_err(); assert_eq!(err.status(), 500); } } http-types-2.12.0/src/content/mod.rs000064400000000000000000000032110072674642500154420ustar 00000000000000//! HTTP Content headers. //! //! These headers are used for "content negotiation": the client shares information //! about which content it prefers, and the server responds by sharing which //! content it's chosen to share. This enables clients to receive resources with the //! best available compression, in the preferred language, and more. //! //! # Further Reading //! //! - [MDN: Content Negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation) //! //! # Examples //! //! ``` //! # fn main() -> http_types::Result<()> { //! # //! use http_types::content::{Accept, MediaTypeProposal}; //! use http_types::{mime, Response}; //! //! let mut accept = Accept::new(); //! accept.push(MediaTypeProposal::new(mime::HTML, Some(0.8))?); //! accept.push(MediaTypeProposal::new(mime::XML, Some(0.4))?); //! accept.push(mime::PLAIN); //! //! let mut res = Response::new(200); //! let content_type = accept.negotiate(&[mime::XML])?; //! content_type.apply(&mut res); //! //! assert_eq!(res["Content-Type"], "application/xml;charset=utf-8"); //! # //! # Ok(()) } //! ``` pub mod accept; pub mod accept_encoding; pub mod content_encoding; mod content_length; mod content_location; mod content_type; mod encoding; mod encoding_proposal; mod media_type_proposal; #[doc(inline)] pub use accept::Accept; #[doc(inline)] pub use accept_encoding::AcceptEncoding; #[doc(inline)] pub use content_encoding::ContentEncoding; pub use content_length::ContentLength; pub use content_location::ContentLocation; pub use content_type::ContentType; pub use encoding::Encoding; pub use encoding_proposal::EncodingProposal; pub use media_type_proposal::MediaTypeProposal; http-types-2.12.0/src/error.rs000064400000000000000000000165050072674642500143540ustar 00000000000000//! HTTP error types use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; use crate::StatusCode; use std::convert::TryInto; /// A specialized `Result` type for HTTP operations. /// /// This type is broadly used across `http_types` for any operation which may /// produce an error. pub type Result = std::result::Result; /// The error type for HTTP operations. pub struct Error { error: anyhow::Error, status: crate::StatusCode, type_name: Option<&'static str>, } #[allow(unreachable_pub)] #[derive(Debug)] #[doc(hidden)] pub struct BacktracePlaceholder; impl Display for BacktracePlaceholder { fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { unreachable!() } } impl Error { /// Create a new error object from any error type. /// /// The error type must be threadsafe and 'static, so that the Error will be /// as well. If the error type does not provide a backtrace, a backtrace will /// be created here to ensure that a backtrace exists. pub fn new(status: S, error: E) -> Self where S: TryInto, S::Error: Debug, E: Into, { Self { status: status .try_into() .expect("Could not convert into a valid `StatusCode`"), error: error.into(), type_name: Some(std::any::type_name::()), } } /// Create a new error object from static string. pub fn from_str(status: S, msg: M) -> Self where S: TryInto, S::Error: Debug, M: Display + Debug + Send + Sync + 'static, { Self { status: status .try_into() .expect("Could not convert into a valid `StatusCode`"), error: anyhow::Error::msg(msg), type_name: None, } } /// Create a new error from a message. pub(crate) fn new_adhoc(message: M) -> Error where M: Display + Debug + Send + Sync + 'static, { Self::from_str(StatusCode::InternalServerError, message) } /// Get the status code associated with this error. pub fn status(&self) -> StatusCode { self.status } /// Set the status code associated with this error. pub fn set_status(&mut self, status: S) where S: TryInto, S::Error: Debug, { self.status = status .try_into() .expect("Could not convert into a valid `StatusCode`"); } /// Get the backtrace for this Error. /// /// Backtraces are only available on the nightly channel. Tracking issue: /// [rust-lang/rust#53487][tracking]. /// /// In order for the backtrace to be meaningful, the environment variable /// `RUST_LIB_BACKTRACE=1` must be defined. Backtraces are somewhat /// expensive to capture in Rust, so we don't necessarily want to be /// capturing them all over the place all the time. /// /// [tracking]: https://github.com/rust-lang/rust/issues/53487 /// /// Note: This function can be called whether or not backtraces /// are enabled and available. It will return a `None` variant if /// compiled on a toolchain that does not support backtraces, or /// if executed without backtraces enabled with /// `RUST_LIB_BACKTRACE=1`. #[cfg(backtrace)] pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> { let backtrace = self.error.backtrace(); if let std::backtrace::BacktraceStatus::Captured = backtrace.status() { Some(backtrace) } else { None } } #[cfg(not(backtrace))] #[allow(missing_docs)] pub const fn backtrace(&self) -> Option { None } /// Returns the inner [`anyhow::Error`] /// Note: This will lose status code information pub fn into_inner(self) -> anyhow::Error { self.error } /// Attempt to downcast the error object to a concrete type. pub fn downcast(self) -> std::result::Result where E: Display + Debug + Send + Sync + 'static, { if self.error.downcast_ref::().is_some() { Ok(self.error.downcast().unwrap()) } else { Err(self) } } /// Downcast this error object by reference. pub fn downcast_ref(&self) -> Option<&E> where E: Display + Debug + Send + Sync + 'static, { self.error.downcast_ref::() } /// Downcast this error object by mutable reference. pub fn downcast_mut(&mut self) -> Option<&mut E> where E: Display + Debug + Send + Sync + 'static, { self.error.downcast_mut::() } /// Retrieves a reference to the type name of the error, if available. pub fn type_name(&self) -> Option<&str> { self.type_name.as_deref() } /// Converts anything which implements `Display` into an `http_types::Error`. /// /// This is handy for errors which are not `Send + Sync + 'static` because `std::error::Error` requires `Display`. /// Note that any assiciated context not included in the `Display` output will be lost, /// and so this may be lossy for some types which implement `std::error::Error`. /// /// **Note: Prefer `error.into()` via `From>` when possible!** pub fn from_display(error: D) -> Self { anyhow::Error::msg(error.to_string()).into() } /// Converts anything which implements `Debug` into an `http_types::Error`. /// /// This is handy for errors which are not `Send + Sync + 'static` because `std::error::Error` requires `Debug`. /// Note that any assiciated context not included in the `Debug` output will be lost, /// and so this may be lossy for some types which implement `std::error::Error`. /// /// **Note: Prefer `error.into()` via `From>` when possible!** pub fn from_debug(error: D) -> Self { anyhow::Error::msg(format!("{:?}", error)).into() } } impl Display for Error { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&self.error, formatter) } } impl Debug for Error { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.error, formatter) } } impl> From for Error { fn from(error: E) -> Self { Self::new(StatusCode::InternalServerError, error) } } impl AsRef for Error { fn as_ref(&self) -> &(dyn StdError + Send + Sync + 'static) { self.error.as_ref() } } impl AsRef for Error { fn as_ref(&self) -> &StatusCode { &self.status } } impl AsMut for Error { fn as_mut(&mut self) -> &mut StatusCode { &mut self.status } } impl AsRef for Error { fn as_ref(&self) -> &(dyn StdError + 'static) { self.error.as_ref() } } impl From for Box { fn from(error: Error) -> Self { error.error.into() } } impl From for Box { fn from(error: Error) -> Self { Box::::from(error.error) } } impl AsRef for Error { fn as_ref(&self) -> &anyhow::Error { &self.error } } http-types-2.12.0/src/extensions.rs000064400000000000000000000072610072674642500154210ustar 00000000000000// Implementation is based on // - https://github.com/hyperium/http/blob/master/src/extensions.rs // - https://github.com/kardeiz/type-map/blob/master/src/lib.rs use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; use std::hash::{BuildHasherDefault, Hasher}; /// A type to store extra data inside `Request` and `Response`. /// /// Store and retrieve values by /// [`TypeId`](https://doc.rust-lang.org/std/any/struct.TypeId.html). This allows /// storing arbitrary data that implements `Sync + Send + 'static`. This is /// useful when for example implementing middleware that needs to send values. #[derive(Default)] pub struct Extensions { map: Option, BuildHasherDefault>>, } impl Extensions { /// Create an empty `Extensions`. #[inline] pub(crate) fn new() -> Self { Self { map: None } } /// Insert a value into this `Extensions`. /// /// If a value of this type already exists, it will be returned. pub fn insert(&mut self, val: T) -> Option { self.map .get_or_insert_with(Default::default) .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) } /// Check if container contains value for type pub fn contains(&self) -> bool { self.map .as_ref() .and_then(|m| m.get(&TypeId::of::())) .is_some() } /// Get a reference to a value previously inserted on this `Extensions`. pub fn get(&self) -> Option<&T> { self.map .as_ref() .and_then(|m| m.get(&TypeId::of::())) .and_then(|boxed| (&**boxed as &(dyn Any)).downcast_ref()) } /// Get a mutable reference to a value previously inserted on this `Extensions`. pub fn get_mut(&mut self) -> Option<&mut T> { self.map .as_mut() .and_then(|m| m.get_mut(&TypeId::of::())) .and_then(|boxed| (&mut **boxed as &mut (dyn Any)).downcast_mut()) } /// Remove a value from this `Extensions`. /// /// If a value of this type exists, it will be returned. pub fn remove(&mut self) -> Option { self.map .as_mut() .and_then(|m| m.remove(&TypeId::of::())) .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) } /// Clear the `Extensions` of all inserted values. #[inline] pub fn clear(&mut self) { self.map = None; } } impl fmt::Debug for Extensions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Extensions").finish() } } // With TypeIds as keys, there's no need to hash them. So we simply use an identy hasher. #[derive(Default)] struct IdHasher(u64); impl Hasher for IdHasher { fn write(&mut self, _: &[u8]) { unreachable!("TypeId calls write_u64"); } #[inline] fn write_u64(&mut self, id: u64) { self.0 = id; } #[inline] fn finish(&self) -> u64 { self.0 } } #[cfg(test)] mod tests { use super::*; #[test] fn test_extensions() { #[derive(Debug, PartialEq)] struct MyType(i32); let mut map = Extensions::new(); map.insert(5i32); map.insert(MyType(10)); assert_eq!(map.get(), Some(&5i32)); assert_eq!(map.get_mut(), Some(&mut 5i32)); assert_eq!(map.remove::(), Some(5i32)); assert!(map.get::().is_none()); assert_eq!(map.get::(), None); assert_eq!(map.get(), Some(&MyType(10))); } } http-types-2.12.0/src/headers/constants.rs000064400000000000000000000167120072674642500166520ustar 00000000000000use super::HeaderName; /// The `Content-Encoding` Header pub const CONTENT_ENCODING: HeaderName = HeaderName::from_lowercase_str("content-encoding"); /// The `Content-Language` Header pub const CONTENT_LANGUAGE: HeaderName = HeaderName::from_lowercase_str("content-language"); /// The `Content-Length` Header pub const CONTENT_LENGTH: HeaderName = HeaderName::from_lowercase_str("content-length"); /// The `Content-Location` Header pub const CONTENT_LOCATION: HeaderName = HeaderName::from_lowercase_str("content-location"); /// The `Content-MD5` Header pub const CONTENT_MD5: HeaderName = HeaderName::from_lowercase_str("content-md5"); /// The `Content-Range` Header pub const CONTENT_RANGE: HeaderName = HeaderName::from_lowercase_str("content-range"); /// The `Content-Type` Header pub const CONTENT_TYPE: HeaderName = HeaderName::from_lowercase_str("content-type"); /// The `Cookie` Header pub const COOKIE: HeaderName = HeaderName::from_lowercase_str("cookie"); /// The `Set-Cookie` Header pub const SET_COOKIE: HeaderName = HeaderName::from_lowercase_str("set-cookie"); /// The `Transfer-Encoding` Header pub const TRANSFER_ENCODING: HeaderName = HeaderName::from_lowercase_str("transfer-encoding"); /// The `Date` Header pub const DATE: HeaderName = HeaderName::from_lowercase_str("date"); /// The `Host` Header pub const HOST: HeaderName = HeaderName::from_lowercase_str("host"); /// The `Origin` Header pub const ORIGIN: HeaderName = HeaderName::from_lowercase_str("origin"); /// The `access-control-max-age` Header pub const ACCESS_CONTROL_MAX_AGE: HeaderName = HeaderName::from_lowercase_str("access-control-max-age"); /// The `access-control-allow-origin` Header pub const ACCESS_CONTROL_ALLOW_ORIGIN: HeaderName = HeaderName::from_lowercase_str("access-control-allow-origin"); /// The `access-control-allow-headers` Header pub const ACCESS_CONTROL_ALLOW_HEADERS: HeaderName = HeaderName::from_lowercase_str("access-control-allow-headers"); /// The `access-control-allow-methods` Header pub const ACCESS_CONTROL_ALLOW_METHODS: HeaderName = HeaderName::from_lowercase_str("access-control-allow-methods"); /// The `access-control-expose-headers` Header pub const ACCESS_CONTROL_EXPOSE_HEADERS: HeaderName = HeaderName::from_lowercase_str("access-control-expose-headers"); /// The `access-control-request-method` Header pub const ACCESS_CONTROL_REQUEST_METHOD: HeaderName = HeaderName::from_lowercase_str("access-control-request-method"); /// The `access-control-request-headers` Header pub const ACCESS_CONTROL_REQUEST_HEADERS: HeaderName = HeaderName::from_lowercase_str("access-control-request-headers"); /// The `access-control-allow-credentials` Header pub const ACCESS_CONTROL_ALLOW_CREDENTIALS: HeaderName = HeaderName::from_lowercase_str("access-control-allow-credentials"); /// The `Accept` Header pub const ACCEPT: HeaderName = HeaderName::from_lowercase_str("accept"); /// The `Accept-Charset` Header pub const ACCEPT_CHARSET: HeaderName = HeaderName::from_lowercase_str("accept-charset"); /// The `Accept-Encoding` Header pub const ACCEPT_ENCODING: HeaderName = HeaderName::from_lowercase_str("accept-encoding"); /// The `Accept-Language` Header pub const ACCEPT_LANGUAGE: HeaderName = HeaderName::from_lowercase_str("accept-language"); /// The `Accept-Ranges` Header pub const ACCEPT_RANGES: HeaderName = HeaderName::from_lowercase_str("accept-ranges"); /// The `Age` Header pub const AGE: HeaderName = HeaderName::from_lowercase_str("age"); /// The `Allow` Header pub const ALLOW: HeaderName = HeaderName::from_lowercase_str("allow"); /// The `Authorization` Header pub const AUTHORIZATION: HeaderName = HeaderName::from_lowercase_str("authorization"); /// The `Cache-Control` Header pub const CACHE_CONTROL: HeaderName = HeaderName::from_lowercase_str("cache-control"); /// The `Clear-Site-Data` Header pub const CLEAR_SITE_DATA: HeaderName = HeaderName::from_lowercase_str("clear-site-data"); /// The `Connection` Header pub const CONNECTION: HeaderName = HeaderName::from_lowercase_str("connection"); /// The `ETag` Header pub const ETAG: HeaderName = HeaderName::from_lowercase_str("etag"); /// The `Expect` Header pub const EXPECT: HeaderName = HeaderName::from_lowercase_str("expect"); /// The `Expires` Header pub const EXPIRES: HeaderName = HeaderName::from_lowercase_str("expires"); /// The `Forwarded` Header pub const FORWARDED: HeaderName = HeaderName::from_lowercase_str("forwarded"); /// The `From` Header pub const FROM: HeaderName = HeaderName::from_lowercase_str("from"); /// The `If-Match` Header pub const IF_MATCH: HeaderName = HeaderName::from_lowercase_str("if-match"); /// The `If-Modified-Since` Header pub const IF_MODIFIED_SINCE: HeaderName = HeaderName::from_lowercase_str("if-modified-since"); /// The `If-None-Match` Header pub const IF_NONE_MATCH: HeaderName = HeaderName::from_lowercase_str("if-none-match"); /// The `If-Range` Header pub const IF_RANGE: HeaderName = HeaderName::from_lowercase_str("if-range"); /// The `If-Unmodified-Since` Header pub const IF_UNMODIFIED_SINCE: HeaderName = HeaderName::from_lowercase_str("if-unmodified-since"); /// The `Last-Modified` Header pub const LAST_MODIFIED: HeaderName = HeaderName::from_lowercase_str("last-modified"); /// The `Location` Header pub const LOCATION: HeaderName = HeaderName::from_lowercase_str("location"); /// The `Max-Forwards` Header pub const MAX_FORWARDS: HeaderName = HeaderName::from_lowercase_str("max-forwards"); /// The `Pragma` Header pub const PRAGMA: HeaderName = HeaderName::from_lowercase_str("pragma"); /// The `Proxy-Authenticate` Header pub const PROXY_AUTHENTICATE: HeaderName = HeaderName::from_lowercase_str("proxy-authenticate"); /// The `Proxy-Authorization` Header pub const PROXY_AUTHORIZATION: HeaderName = HeaderName::from_lowercase_str("proxy-authorization"); /// The `Proxy-Connection` Header pub const PROXY_CONNECTION: HeaderName = HeaderName::from_lowercase_str("proxy-connection"); /// The `Referer` Header pub const REFERER: HeaderName = HeaderName::from_lowercase_str("referer"); /// The `Retry-After` Header pub const RETRY_AFTER: HeaderName = HeaderName::from_lowercase_str("retry-after"); /// The `Server` Header pub const SERVER: HeaderName = HeaderName::from_lowercase_str("server"); /// The `Server` Header pub const SERVER_TIMING: HeaderName = HeaderName::from_lowercase_str("server-timing"); /// The `SourceMap` Header pub const SOURCE_MAP: HeaderName = HeaderName::from_lowercase_str("sourcemap"); /// The `Te` Header pub const TE: HeaderName = HeaderName::from_lowercase_str("te"); /// The `Timing-Allow-Origin` Header pub const TIMING_ALLOW_ORIGIN: HeaderName = HeaderName::from_lowercase_str("timing-allow-origin"); /// The `Traceparent` Header pub const TRACEPARENT: HeaderName = HeaderName::from_lowercase_str("traceparent"); /// The `Trailer` Header pub const TRAILER: HeaderName = HeaderName::from_lowercase_str("trailer"); /// The `Upgrade` Header pub const UPGRADE: HeaderName = HeaderName::from_lowercase_str("upgrade"); /// The `User-Agent` Header pub const USER_AGENT: HeaderName = HeaderName::from_lowercase_str("user-agent"); /// The `Vary` Header pub const VARY: HeaderName = HeaderName::from_lowercase_str("vary"); /// The `Via` Header pub const VIA: HeaderName = HeaderName::from_lowercase_str("via"); /// The `Warning` Header pub const WARNING: HeaderName = HeaderName::from_lowercase_str("warning"); /// The `WWW-Authenticate` Header pub const WWW_AUTHENTICATE: HeaderName = HeaderName::from_lowercase_str("www-authenticate"); http-types-2.12.0/src/headers/header_name.rs000064400000000000000000000117710072674642500170660ustar 00000000000000use std::borrow::Cow; use std::fmt::{self, Debug, Display}; use std::str::FromStr; use crate::Error; /// A header name. #[derive(Clone, PartialEq, Eq, Hash)] pub struct HeaderName(Cow<'static, str>); impl HeaderName { /// Create a new `HeaderName` from a Vec of ASCII bytes. /// /// # Error /// /// This function will error if the bytes is not valid ASCII. pub fn from_bytes(mut bytes: Vec) -> Result { crate::ensure!(bytes.is_ascii(), "Bytes should be valid ASCII"); bytes.make_ascii_lowercase(); // This is permitted because ASCII is valid UTF-8, and we just checked that. let string = unsafe { String::from_utf8_unchecked(bytes.to_vec()) }; Ok(HeaderName(Cow::Owned(string))) } /// Create a new `HeaderName` from an ASCII string. /// /// # Error /// /// This function will error if the string is not valid ASCII. pub fn from_string(s: String) -> Result { Self::from_bytes(s.into_bytes()) } /// Returns the header name as a `&str`. pub fn as_str(&self) -> &'_ str { &self.0 } /// Converts a vector of bytes to a `HeaderName` without checking that the string contains /// valid ASCII. /// /// # Safety /// /// This function is unsafe because it does not check that the bytes passed to it are valid /// ASCII. If this constraint is violated, it may cause memory /// unsafety issues with future users of the HeaderName, as the rest of the library assumes /// that Strings are valid ASCII. pub unsafe fn from_bytes_unchecked(mut bytes: Vec) -> Self { bytes.make_ascii_lowercase(); let string = String::from_utf8_unchecked(bytes); HeaderName(Cow::Owned(string)) } /// Converts a string assumed to lowercase into a `HeaderName` pub(crate) const fn from_lowercase_str(str: &'static str) -> Self { HeaderName(Cow::Borrowed(str)) } } impl Debug for HeaderName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) } } impl Display for HeaderName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl FromStr for HeaderName { type Err = Error; /// Create a new `HeaderName`. /// /// This checks it's valid ASCII, and lowercases it. fn from_str(s: &str) -> Result { crate::ensure!(s.is_ascii(), "String slice should be valid ASCII"); Ok(HeaderName(Cow::Owned(s.to_ascii_lowercase()))) } } impl From<&HeaderName> for HeaderName { fn from(value: &HeaderName) -> HeaderName { value.clone() } } impl<'a> From<&'a str> for HeaderName { fn from(value: &'a str) -> Self { Self::from_str(value).expect("String slice should be valid ASCII") } } impl PartialEq for HeaderName { fn eq(&self, other: &str) -> bool { match HeaderName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } impl<'a> PartialEq<&'a str> for HeaderName { fn eq(&self, other: &&'a str) -> bool { match HeaderName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } impl PartialEq for HeaderName { fn eq(&self, other: &String) -> bool { match HeaderName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } impl<'a> PartialEq<&String> for HeaderName { fn eq(&self, other: &&String) -> bool { match HeaderName::from_str(other) { Err(_) => false, Ok(other) => self == &other, } } } #[cfg(test)] mod tests { use super::*; #[test] #[allow(clippy::eq_op)] fn test_header_name_static_non_static() { let static_header = HeaderName::from_lowercase_str("hello"); let non_static_header = HeaderName::from_str("hello").unwrap(); assert_eq!(&static_header, &non_static_header); assert_eq!(&static_header, &static_header); assert_eq!(&non_static_header, &non_static_header); assert_eq!(static_header, non_static_header); assert_eq!(static_header, static_header); assert_eq!(non_static_header, non_static_header); } #[test] fn equality() { let static_header = HeaderName::from_lowercase_str("hello"); assert_eq!(static_header, "hello"); assert_eq!(&static_header, "hello"); assert_eq!(static_header, String::from("hello")); assert_eq!(static_header, &String::from("hello")); // Must validate regardless of casing. assert_eq!(static_header, &String::from("Hello")); } #[test] fn pass_name_by_ref() { let mut res = crate::Response::new(200); res.insert_header(&crate::headers::HOST, "127.0.0.1"); } #[test] fn test_debug() { let header_name = HeaderName::from_str("hello").unwrap(); assert_eq!(format!("{:?}", header_name), "\"hello\""); } } http-types-2.12.0/src/headers/header_value.rs000064400000000000000000000072770072674642500172700ustar 00000000000000use std::convert::TryFrom; use std::fmt::{self, Debug, Display}; use std::str::FromStr; use crate::headers::HeaderValues; #[cfg(feature = "cookies")] use crate::Cookie; use crate::Error; use crate::Mime; /// A header value. #[derive(Clone, Eq, PartialEq, Hash)] pub struct HeaderValue { inner: String, } impl HeaderValue { /// Create a new `HeaderValue` from a Vec of ASCII bytes. /// /// # Error /// /// This function will error if the bytes is not valid ASCII. pub fn from_bytes(bytes: Vec) -> Result { crate::ensure!(bytes.is_ascii(), "Bytes should be valid ASCII"); // This is permitted because ASCII is valid UTF-8, and we just checked that. let string = unsafe { String::from_utf8_unchecked(bytes) }; Ok(Self { inner: string }) } /// Converts a vector of bytes to a `HeaderValue` without checking that the string contains /// valid ASCII. /// /// # Safety /// /// This function is unsafe because it does not check that the bytes passed to it are valid /// ASCII. If this constraint is violated, it may cause memory /// unsafety issues with future users of the HeaderValue, as the rest of the library assumes /// that Strings are valid ASCII. pub unsafe fn from_bytes_unchecked(bytes: Vec) -> Self { let string = String::from_utf8_unchecked(bytes); Self { inner: string } } /// Get the header value as a `&str` pub fn as_str(&self) -> &str { &self.inner } } impl From for HeaderValue { fn from(mime: Mime) -> Self { HeaderValue { inner: format!("{}", mime), } } } #[cfg(feature = "cookies")] impl From> for HeaderValue { fn from(cookie: Cookie<'_>) -> Self { HeaderValue { inner: cookie.to_string(), } } } impl From<&Mime> for HeaderValue { fn from(mime: &Mime) -> Self { HeaderValue { inner: format!("{}", mime), } } } impl FromStr for HeaderValue { type Err = Error; /// Create a new `HeaderValue`. /// /// This checks it's valid ASCII. fn from_str(s: &str) -> Result { crate::ensure!(s.is_ascii(), "String slice should be valid ASCII"); Ok(Self { inner: String::from(s), }) } } impl<'a> TryFrom<&'a str> for HeaderValue { type Error = Error; fn try_from(value: &'a str) -> Result { Self::from_str(value) } } impl Debug for HeaderValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.inner) } } impl Display for HeaderValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.inner) } } impl PartialEq for HeaderValue { fn eq(&self, other: &str) -> bool { self.inner == other } } impl<'a> PartialEq<&'a str> for HeaderValue { fn eq(&self, other: &&'a str) -> bool { &self.inner == other } } impl PartialEq for HeaderValue { fn eq(&self, other: &String) -> bool { &self.inner == other } } impl<'a> PartialEq<&String> for HeaderValue { fn eq(&self, other: &&String) -> bool { &&self.inner == other } } impl From for HeaderValue { fn from(mut other: HeaderValues) -> Self { other.inner.reverse(); other .inner .pop() .expect("HeaderValues should contain at least one value") } } #[cfg(test)] mod tests { use super::*; #[test] fn test_debug() { let header_value = HeaderValue::from_str("foo0").unwrap(); assert_eq!(format!("{:?}", header_value), "\"foo0\""); } } http-types-2.12.0/src/headers/header_values.rs000064400000000000000000000122330072674642500174370ustar 00000000000000use crate::headers::{HeaderValue, Values}; use std::fmt::{self, Debug, Display}; use std::iter::FromIterator; use std::ops::{Deref, DerefMut, Index}; use std::slice::SliceIndex; /// A list of `HeaderValue`s. /// /// This always contains at least one header value. #[derive(Clone)] pub struct HeaderValues { pub(crate) inner: Vec, } impl HeaderValues { /// Move all values from `other` into `self`, leaving `other` empty. pub fn append(&mut self, other: &mut Self) { self.inner.append(&mut other.inner) } /// Returns a reference or a value depending on the type of index. pub fn get(&self, index: usize) -> Option<&HeaderValue> { self.inner.get(index) } /// Returns a mutable reference or a value depending on the type of index. pub fn get_mut(&mut self, index: usize) -> Option<&mut HeaderValue> { self.inner.get_mut(index) } /// Returns `true` if there is a value corresponding to the specified `HeaderValue` in the list, /// `false` otherwise. pub fn contains(&self, value: &HeaderValue) -> bool { self.inner.contains(value) } /// Returns the last `HeaderValue`. pub fn last(&self) -> &HeaderValue { self.inner .last() .expect("HeaderValues must always contain at least one value") } /// An iterator visiting all header values in arbitrary order. pub fn iter(&self) -> Values<'_> { Values::new_values(self) } // /// An iterator visiting all header values in arbitrary order, with mutable // /// references to the values. // pub fn iter_mut(&mut self) -> ValuesMut<'_> { // ValuesMut { // inner: self.headers.iter_mut(), // } // } } impl> Index for HeaderValues { type Output = I::Output; #[inline] fn index(&self, index: I) -> &Self::Output { Index::index(&self.inner, index) } } impl FromIterator for HeaderValues { fn from_iter(iter: I) -> HeaderValues where I: IntoIterator, { let iter = iter.into_iter(); let mut output = Vec::with_capacity(iter.size_hint().0); for v in iter { output.push(v); } HeaderValues { inner: output } } } impl Debug for HeaderValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.inner.len() == 1 { write!(f, "{:?}", self.inner[0]) } else { f.debug_list().entries(self.inner.iter()).finish() } } } impl Display for HeaderValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for v in &self.inner { list.entry(&v); } list.finish() } } impl PartialEq for HeaderValues { fn eq(&self, other: &str) -> bool { self.inner.len() == 1 && self.inner[0] == other } } impl<'a> PartialEq<&'a str> for HeaderValues { fn eq(&self, other: &&'a str) -> bool { self.inner.len() == 1 && &self.inner[0] == other } } impl<'a> PartialEq<[&'a str]> for HeaderValues { fn eq(&self, other: &[&'a str]) -> bool { self.inner.iter().eq(other.iter()) } } impl PartialEq for HeaderValues { fn eq(&self, other: &String) -> bool { self.inner.len() == 1 && self.inner[0] == *other } } impl<'a> PartialEq<&String> for HeaderValues { fn eq(&self, other: &&String) -> bool { self.inner.len() == 1 && self.inner[0] == **other } } impl From for HeaderValues { fn from(other: HeaderValue) -> Self { Self { inner: vec![other] } } } impl AsRef for HeaderValues { fn as_ref(&self) -> &HeaderValue { &self.inner[0] } } impl AsMut for HeaderValues { fn as_mut(&mut self) -> &mut HeaderValue { &mut self.inner[0] } } impl Deref for HeaderValues { type Target = HeaderValue; fn deref(&self) -> &HeaderValue { &self.inner[0] } } impl DerefMut for HeaderValues { fn deref_mut(&mut self) -> &mut HeaderValue { &mut self.inner[0] } } impl<'a> IntoIterator for &'a HeaderValues { type Item = &'a HeaderValue; type IntoIter = Values<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl From> for HeaderValues { fn from(headers: Vec) -> Self { Self { inner: headers } } } impl IntoIterator for HeaderValues { type Item = HeaderValue; type IntoIter = std::vec::IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_debug_single() { let header_values = HeaderValues { inner: vec!["foo0".parse().unwrap()], }; assert_eq!(format!("{:?}", header_values), "\"foo0\""); } #[test] fn test_debug_multiple() { let header_values = HeaderValues { inner: vec!["foo0".parse().unwrap(), "foo1".parse().unwrap()], }; assert_eq!(format!("{:?}", header_values), r#"["foo0", "foo1"]"#); } } http-types-2.12.0/src/headers/headers.rs000064400000000000000000000154530072674642500162520ustar 00000000000000//! HTTP headers. use std::collections::HashMap; use std::convert::Into; use std::fmt::{self, Debug}; use std::iter::IntoIterator; use std::ops::Index; use std::str::FromStr; use crate::headers::{ HeaderName, HeaderValues, IntoIter, Iter, IterMut, Names, ToHeaderValues, Values, }; /// A collection of HTTP Headers. /// /// Headers are never manually constructed, but are part of `Request`, /// `Response`, and `Trailers`. Each of these types implements `AsRef` /// and `AsMut` so functions that want to modify headers can be generic /// over either of these traits. /// /// # Examples /// /// ``` /// use http_types::{Response, StatusCode}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.insert_header("hello", "foo0"); /// assert_eq!(res["hello"], "foo0"); /// ``` #[derive(Clone)] pub struct Headers { pub(crate) headers: HashMap, } impl Headers { /// Create a new instance. pub(crate) fn new() -> Self { Self { headers: HashMap::new(), } } /// Insert a header into the headers. /// /// Not that this will replace all header values for a given header name. /// If you wish to add header values for a header name that already exists /// use `Headers::append` pub fn insert( &mut self, name: impl Into, values: impl ToHeaderValues, ) -> Option { let name = name.into(); let values: HeaderValues = values.to_header_values().unwrap().collect(); self.headers.insert(name, values) } /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a header, but insert a /// header if there aren't any. Or else append to the existing list of headers. pub fn append(&mut self, name: impl Into, values: impl ToHeaderValues) { let name = name.into(); match self.get_mut(&name) { Some(headers) => { let mut values: HeaderValues = values.to_header_values().unwrap().collect(); headers.append(&mut values); } None => { self.insert(name, values); } } } /// Get a reference to a header. pub fn get(&self, name: impl Into) -> Option<&HeaderValues> { self.headers.get(&name.into()) } /// Get a mutable reference to a header. pub fn get_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { self.headers.get_mut(&name.into()) } /// Remove a header. pub fn remove(&mut self, name: impl Into) -> Option { self.headers.remove(&name.into()) } /// An iterator visiting all header pairs in arbitrary order. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.headers.iter(), } } /// An iterator visiting all header pairs in arbitrary order, with mutable references to the /// values. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.headers.iter_mut(), } } /// An iterator visiting all header names in arbitrary order. pub fn names(&self) -> Names<'_> { Names { inner: self.headers.keys(), } } /// An iterator visiting all header values in arbitrary order. pub fn values(&self) -> Values<'_> { Values::new(self.headers.values()) } } impl Index for Headers { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Headers`. #[inline] fn index(&self, name: HeaderName) -> &HeaderValues { self.get(name).expect("no entry found for name") } } impl Index<&str> for Headers { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Headers`. #[inline] fn index(&self, name: &str) -> &HeaderValues { let name = HeaderName::from_str(name).expect("string slice needs to be valid ASCII"); self.get(name).expect("no entry found for name") } } impl IntoIterator for Headers { type Item = (HeaderName, HeaderValues); type IntoIter = IntoIter; /// Returns a iterator of references over the remaining items. #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.headers.into_iter(), } } } impl<'a> IntoIterator for &'a Headers { type Item = (&'a HeaderName, &'a HeaderValues); type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut Headers { type Item = (&'a HeaderName, &'a mut HeaderValues); type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl Debug for Headers { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.headers.iter()).finish() } } impl AsRef for Headers { fn as_ref(&self) -> &Headers { self } } impl AsMut for Headers { fn as_mut(&mut self) -> &mut Headers { self } } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; const STATIC_HEADER: HeaderName = HeaderName::from_lowercase_str("hello"); #[test] fn test_header_name_static_non_static() -> crate::Result<()> { let static_header = HeaderName::from_lowercase_str("hello"); let non_static_header = HeaderName::from_str("hello")?; let mut headers = Headers::new(); headers.append(STATIC_HEADER, "foo0"); headers.append(static_header.clone(), "foo1"); headers.append(non_static_header.clone(), "foo2"); assert_eq!(headers[STATIC_HEADER], ["foo0", "foo1", "foo2",][..]); assert_eq!(headers[static_header], ["foo0", "foo1", "foo2",][..]); assert_eq!(headers[non_static_header], ["foo0", "foo1", "foo2",][..]); Ok(()) } #[test] fn index_into_headers() { let mut headers = Headers::new(); headers.insert("hello", "foo0"); assert_eq!(headers["hello"], "foo0"); assert_eq!(headers.get("hello").unwrap(), "foo0"); } #[test] fn test_debug_single() { let mut headers = Headers::new(); headers.insert("single", "foo0"); assert_eq!(format!("{:?}", headers), r#"{"single": "foo0"}"#); } #[test] fn test_debug_multiple() { let mut headers = Headers::new(); headers.append("multi", "foo0"); headers.append("multi", "foo1"); assert_eq!(format!("{:?}", headers), r#"{"multi": ["foo0", "foo1"]}"#); } } http-types-2.12.0/src/headers/into_iter.rs000064400000000000000000000010220072674642500166160ustar 00000000000000use std::collections::hash_map; use std::iter::Iterator; use crate::headers::{HeaderName, HeaderValues}; /// An owning iterator over the entries of `Headers`. #[derive(Debug)] pub struct IntoIter { pub(super) inner: hash_map::IntoIter, } impl Iterator for IntoIter { type Item = (HeaderName, HeaderValues); fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } http-types-2.12.0/src/headers/iter.rs000064400000000000000000000010070072674642500155700ustar 00000000000000use std::collections::hash_map; use std::iter::Iterator; use crate::headers::{HeaderName, HeaderValues}; /// Iterator over the headers. #[derive(Debug)] pub struct Iter<'a> { pub(super) inner: hash_map::Iter<'a, HeaderName, HeaderValues>, } impl<'a> Iterator for Iter<'a> { type Item = (&'a HeaderName, &'a HeaderValues); fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } http-types-2.12.0/src/headers/iter_mut.rs000064400000000000000000000010240072674642500164540ustar 00000000000000use std::collections::hash_map; use std::iter::Iterator; use crate::headers::{HeaderName, HeaderValues}; /// Iterator over the headers. #[derive(Debug)] pub struct IterMut<'a> { pub(super) inner: hash_map::IterMut<'a, HeaderName, HeaderValues>, } impl<'a> Iterator for IterMut<'a> { type Item = (&'a HeaderName, &'a mut HeaderValues); fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } http-types-2.12.0/src/headers/mod.rs000064400000000000000000000010320072674642500154020ustar 00000000000000//! HTTP headers. mod constants; mod header_name; mod header_value; mod header_values; #[allow(clippy::module_inception)] mod headers; mod into_iter; mod iter; mod iter_mut; mod names; mod to_header_values; mod values; pub use constants::*; pub use header_name::HeaderName; pub use header_value::HeaderValue; pub use header_values::HeaderValues; pub use headers::Headers; pub use into_iter::IntoIter; pub use iter::Iter; pub use iter_mut::IterMut; pub use names::Names; pub use to_header_values::ToHeaderValues; pub use values::Values; http-types-2.12.0/src/headers/names.rs000064400000000000000000000006150072674642500157340ustar 00000000000000use std::collections::hash_map; use std::iter::Iterator; use crate::headers::{HeaderName, HeaderValues}; /// Iterator over the headers. #[derive(Debug)] pub struct Names<'a> { pub(super) inner: hash_map::Keys<'a, HeaderName, HeaderValues>, } impl<'a> Iterator for Names<'a> { type Item = &'a HeaderName; fn next(&mut self) -> Option { self.inner.next() } } http-types-2.12.0/src/headers/to_header_values.rs000064400000000000000000000037500072674642500201450ustar 00000000000000use std::borrow::Cow; use std::io; use std::iter; use std::option; use std::slice; use crate::headers::{HeaderValue, HeaderValues, Values}; /// A trait for objects which can be converted or resolved to one or more `HeaderValue`s. pub trait ToHeaderValues { /// Returned iterator over header values which this type may correspond to. type Iter: Iterator; /// Converts this object to an iterator of resolved `HeaderValues`. fn to_header_values(&self) -> crate::Result; } impl ToHeaderValues for HeaderValue { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { Ok(Some(self.clone()).into_iter()) } } impl<'a> ToHeaderValues for &'a HeaderValues { type Iter = iter::Cloned>; fn to_header_values(&self) -> crate::Result { Ok(self.iter().cloned()) } } impl<'a> ToHeaderValues for &'a [HeaderValue] { type Iter = iter::Cloned>; fn to_header_values(&self) -> crate::Result { Ok(self.iter().cloned()) } } impl<'a> ToHeaderValues for &'a str { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { let value = self .parse() .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; Ok(Some(value).into_iter()) } } impl ToHeaderValues for String { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { self.as_str().to_header_values() } } impl ToHeaderValues for &String { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { self.as_str().to_header_values() } } impl ToHeaderValues for Cow<'_, str> { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { self.as_ref().to_header_values() } } http-types-2.12.0/src/headers/values.rs000064400000000000000000000037410072674642500161330ustar 00000000000000use std::collections::hash_map; use std::iter::Iterator; use crate::headers::{HeaderName, HeaderValue, HeaderValues}; /// Iterator over the header values. #[derive(Debug)] pub struct Values<'a> { pub(super) inner: Option>, slot: Option<&'a HeaderValues>, cursor: usize, } impl<'a> Values<'a> { /// Constructor for `Headers`. pub(crate) fn new(inner: hash_map::Values<'a, HeaderName, HeaderValues>) -> Self { Self { inner: Some(inner), slot: None, cursor: 0, } } /// Constructor for `HeaderValues`. pub(crate) fn new_values(values: &'a HeaderValues) -> Self { Self { inner: None, slot: Some(values), cursor: 0, } } } impl<'a> Iterator for Values<'a> { type Item = &'a HeaderValue; fn next(&mut self) -> Option { loop { // Check if we have a vec in the current slot, and if not set one. if self.slot.is_none() { let next = match self.inner.as_mut() { Some(inner) => inner.next()?, None => return None, }; self.cursor = 0; self.slot = Some(next); } // Get the next item match self.slot.unwrap().get(self.cursor) { // If an item is found, increment the cursor and return the item. Some(item) => { self.cursor += 1; return Some(item); } // If no item is found, unset the slot and loop again. None => { self.slot = None; continue; } } } } #[inline] fn size_hint(&self) -> (usize, Option) { match self.inner.as_ref() { Some(inner) => inner.size_hint(), None => (0, None), } } } http-types-2.12.0/src/hyperium_http.rs000064400000000000000000000164670072674642500161330ustar 00000000000000// This is the compat file for the "hyperium/http" crate. use crate::headers::{HeaderName, HeaderValue, Headers}; use crate::{Body, Error, Method, Request, Response, StatusCode, Url, Version}; use std::convert::{TryFrom, TryInto}; use std::str::FromStr; impl From for Method { fn from(method: http::Method) -> Self { Method::from_str(method.as_str()).unwrap() } } impl From for http::Method { fn from(method: Method) -> Self { http::Method::from_str(method.as_ref()).unwrap() } } impl From for StatusCode { fn from(status: http::StatusCode) -> Self { StatusCode::try_from(status.as_u16()).unwrap() } } impl From for http::StatusCode { fn from(status: StatusCode) -> Self { http::StatusCode::from_u16(status.into()).unwrap() } } impl From for Version { fn from(version: http::Version) -> Self { match version { http::Version::HTTP_09 => Version::Http0_9, http::Version::HTTP_10 => Version::Http1_0, http::Version::HTTP_11 => Version::Http1_1, http::Version::HTTP_2 => Version::Http2_0, http::Version::HTTP_3 => Version::Http3_0, _ => panic!("unknown HTTP version conversion"), } } } impl From for http::Version { fn from(version: Version) -> Self { match version { Version::Http0_9 => http::Version::HTTP_09, Version::Http1_0 => http::Version::HTTP_10, Version::Http1_1 => http::Version::HTTP_11, Version::Http2_0 => http::Version::HTTP_2, Version::Http3_0 => http::Version::HTTP_3, } } } impl TryFrom for HeaderName { type Error = Error; fn try_from(name: http::header::HeaderName) -> Result { let name = name.as_str().as_bytes().to_owned(); HeaderName::from_bytes(name) } } impl TryFrom for http::header::HeaderName { type Error = Error; fn try_from(name: HeaderName) -> Result { let name = name.as_str().as_bytes(); http::header::HeaderName::from_bytes(name).map_err(Error::new_adhoc) } } impl TryFrom for HeaderValue { type Error = Error; fn try_from(value: http::header::HeaderValue) -> Result { let value = value.as_bytes().to_owned(); HeaderValue::from_bytes(value) } } impl TryFrom for http::header::HeaderValue { type Error = Error; fn try_from(value: HeaderValue) -> Result { let value = value.as_str().as_bytes(); http::header::HeaderValue::from_bytes(value).map_err(Error::new_adhoc) } } impl TryFrom for Headers { type Error = Error; fn try_from(hyperium_headers: http::HeaderMap) -> Result { let mut headers = Headers::new(); hyperium_headers .into_iter() .map(|(name, value)| { if let Some(name) = name { let value: HeaderValue = value.try_into()?; let name: HeaderName = name.try_into()?; headers.append(name, value); } Ok(()) }) .collect::, Error>>()?; Ok(headers) } } impl TryFrom for http::HeaderMap { type Error = Error; fn try_from(headers: Headers) -> Result { let mut hyperium_headers = http::HeaderMap::new(); headers .into_iter() .map(|(name, values)| { let name: http::header::HeaderName = name.try_into()?; values .into_iter() .map(|value| { let value: http::header::HeaderValue = value.try_into()?; hyperium_headers.append(&name, value); Ok(()) }) .collect::, Error>>()?; Ok(()) }) .collect::, Error>>()?; Ok(hyperium_headers) } } fn hyperium_headers_to_headers(hyperium_headers: http::HeaderMap, headers: &mut Headers) { for (name, value) in hyperium_headers { let value = value.as_bytes().to_owned(); let value = unsafe { HeaderValue::from_bytes_unchecked(value) }; if let Some(name) = name { let name = name.as_str().as_bytes().to_owned(); let name = unsafe { HeaderName::from_bytes_unchecked(name) }; headers.append(name, value); } } } fn headers_to_hyperium_headers(headers: &mut Headers, hyperium_headers: &mut http::HeaderMap) { for (name, values) in headers { let name = format!("{}", name).into_bytes(); let name = http::header::HeaderName::from_bytes(&name).unwrap(); for value in values.iter() { let value = format!("{}", value).into_bytes(); let value = http::header::HeaderValue::from_bytes(&value).unwrap(); hyperium_headers.append(&name, value); } } } // Neither type is defined in this lib, so we can't do From/Into impls fn from_uri_to_url(uri: http::Uri) -> Result { format!("{}", uri).parse() } // Neither type is defined in this lib, so we can't do From/Into impls fn from_url_to_uri(url: &Url) -> http::Uri { http::Uri::try_from(&format!("{}", url)).unwrap() } impl TryFrom> for Request { type Error = crate::url::ParseError; fn try_from(req: http::Request) -> Result { let (parts, body) = req.into_parts(); let method = parts.method.into(); let url = from_uri_to_url(parts.uri)?; let mut req = Request::new(method, url); req.set_body(body); req.set_version(Some(parts.version.into())); hyperium_headers_to_headers(parts.headers, req.as_mut()); Ok(req) } } impl From for http::Request { fn from(mut req: Request) -> Self { let method: http::Method = req.method().into(); let version = req.version().map(|v| v.into()).unwrap_or_default(); let mut builder = http::request::Builder::new() .method(method) .uri(from_url_to_uri(req.url())) .version(version); headers_to_hyperium_headers(req.as_mut(), builder.headers_mut().unwrap()); builder.body(req.into()).unwrap() } } impl From> for Response { fn from(res: http::Response) -> Self { let (parts, body) = res.into_parts(); let mut res = Response::new(parts.status); res.set_body(body); res.set_version(Some(parts.version.into())); hyperium_headers_to_headers(parts.headers, res.as_mut()); res } } impl From for http::Response { fn from(mut res: Response) -> Self { let status: u16 = res.status().into(); let version = res.version().map(|v| v.into()).unwrap_or_default(); let mut builder = http::response::Builder::new() .status(status) .version(version); headers_to_hyperium_headers(res.as_mut(), builder.headers_mut().unwrap()); let body = res.take_body(); builder.body(body).unwrap() } } http-types-2.12.0/src/lib.rs000064400000000000000000000144320072674642500137660ustar 00000000000000//! # Common types for HTTP operations. //! //! `http-types` provides shared types for HTTP operations. It combines a performant, streaming //! interface with convenient methods for creating headers, urls, and other standard HTTP types. //! //! # Example //! //! ``` //! # fn main() -> Result<(), http_types::url::ParseError> { //! # //! use http_types::{Method, Request, Response, StatusCode}; //! //! let mut req = Request::new(Method::Get, "https://example.com"); //! req.set_body("Hello, Nori!"); //! //! let mut res = Response::new(StatusCode::Ok); //! res.set_body("Hello, Chashu!"); //! # //! # Ok(()) } //! ``` //! //! # How does HTTP work? //! //! We couldn't possibly explain _all_ of HTTP here: there are [5 versions](enum.Version.html) of //! the protocol now, and lots of extensions. But, at its core, there are only a few concepts you //! need to know about in order to understand what this crate does. //! //! ```txt //! request //! client ----------> server //! <---------- //! response //! ``` //! //! HTTP is an [RPC protocol](https://en.wikipedia.org/wiki/Remote_procedure_call). A client //! creates a [`Request`](struct.Request.html) containing a [`Url`](struct.Url.html), //! [`Method`](struct.Method.html), [`Headers`](struct.Headers.html), and optional //! [`Body`](struct.Body.html) and sends this to a server. The server then decodes this `Request`, //! does some work, and sends back a [`Response`](struct.Response.html). //! //! The `Url` works as a way to subdivide an IP address/domain into further addressable resources. //! The `Method` indicates what kind of operation we're trying to perform (get something, submit //! something, update something, etc.) //! //! ```txt //! Request //! |-----------------| //! | Url | //! | Method | //! | Headers | //! |-----------------| //! | Body (optional) | //! |-----------------| //! ``` //! //! A `Response` consists of a [`StatusCode`](enum.StatusCode.html), //! [`Headers`](struct.Headers.html), and optionally a [`Body`](struct.Body.html). The client then //! decodes the `Response`, and can then operate on it. Usually the first thing it does is check //! the status code to see if its `Request` was successful or not, and then moves on to the information contained within the headers. //! //! ```txt //! Response //! |-----------------| //! | StatusCode | //! | Headers | //! |-----------------| //! | Body (optional) | //! |-----------------| //! ``` //! //! Both `Request` and `Response` include [`Headers`](struct.Headers.html). This is like key-value metadata for HTTP //! requests. It needs to be encoded in a specific way (all lowercase ASCII, only some special //! characters) so we use the [`HeaderName`](headers/struct.HeaderName.html) and //! [`HeaderValue`](headers/struct.HeaderValue.html) structs rather than strings to ensure that. //! Another interesting thing about this is that it's valid to have multiple instances of the //! same header name. This is why `Headers` allows inserting multiple values, and always returns a //! [`Vec`](https://doc.rust-lang.org/std/vec/struct.Vec.html) of headers for each key. //! //! When reading up on HTTP you might frequently hear a lot of jargon related to ther underlying //! protocols. But even newer HTTP versions (`HTTP/2`, `HTTP/3`) still fundamentally use the //! request/response model we've described so far. //! //! # The Body Type //! //! In HTTP, [`Body`](struct.Body.html) types are optional. The content of a `Body` is a stream of //! bytes with a specific encoding; this encoding is its [`Mime` type](struct.Mime.html). The `Mime` can //! be set using the [`set_content_type`](struct.Request.html#method.set_content_type) method, and //! there are many different possible `Mime` types. //! //! `http-types`' `Body` struct can take anything that implements //! [`AsyncBufRead`](https://docs.rs/futures/0.3.1/futures/io/trait.AsyncBufRead.html) and stream //! it out. Depending on the version of HTTP used, the underlying bytes will be transmitted //! differently. As a rule, if you know the size of the body it's usually more efficient to //! declare it up front. But if you don't, things will still work. #![deny(missing_debug_implementations, nonstandard_style)] #![warn(missing_docs)] #![allow(clippy::new_without_default)] #![cfg_attr(backtrace, feature(backtrace))] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![doc(html_favicon_url = "https://yoshuawuyts.com/assets/http-rs/favicon.ico")] #![doc(html_logo_url = "https://yoshuawuyts.com/assets/http-rs/logo-rounded.png")] /// HTTP cookies. #[cfg(feature = "cookies")] pub mod cookies { pub use cookie::*; } /// URL records. pub mod url { pub use url::{ EncodingOverride, Host, OpaqueOrigin, Origin, ParseError, ParseOptions, PathSegmentsMut, Position, SyntaxViolation, Url, UrlQuery, }; } #[macro_use] mod utils; pub mod auth; pub mod cache; pub mod conditional; pub mod content; pub mod headers; pub mod mime; pub mod other; pub mod proxies; pub mod server; pub mod trace; pub mod transfer; pub mod upgrade; mod body; mod error; mod extensions; mod macros; mod method; mod parse_utils; mod request; mod response; mod status; mod status_code; mod version; pub use body::Body; pub use error::{Error, Result}; pub use method::Method; pub use request::Request; pub use response::Response; pub use status::Status; pub use status_code::StatusCode; pub use version::Version; #[doc(inline)] pub use trailers::Trailers; #[doc(inline)] pub use mime::Mime; #[doc(inline)] pub use headers::Headers; #[doc(inline)] pub use crate::url::Url; #[cfg(feature = "cookies")] #[doc(inline)] pub use crate::cookies::Cookie; pub mod security; pub mod trailers; #[cfg(feature = "hyperium_http")] mod hyperium_http; #[doc(inline)] pub use crate::extensions::Extensions; /// Traits for conversions between types. pub mod convert { pub use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[doc(inline)] pub use serde_json::json; } // Not public API. Referenced by macro-generated code. #[doc(hidden)] pub mod private { use crate::Error; pub use crate::StatusCode; use core::fmt::{Debug, Display}; pub use core::result::Result::Err; pub fn new_adhoc(message: M) -> Error where M: Display + Debug + Send + Sync + 'static, { Error::new_adhoc(message) } } http-types-2.12.0/src/macros.rs000064400000000000000000000141620072674642500145040ustar 00000000000000/// Return early with an error. #[macro_export] macro_rules! bail { ($msg:literal $(,)?) => { return $crate::private::Err($crate::format_err!($msg)) }; ($msg:expr $(,)?) => { return $crate::private::Err($crate::format_err!($msg)) }; ($msg:expr, $($arg:tt)*) => { return $crate::private::Err($crate::format_err!($msg, $($arg)*)) }; } /// Return early with an error if a condition is not satisfied. /// /// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. /// /// Analogously to `assert!`, `ensure!` takes a condition and exits the function /// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` /// rather than panicking. #[macro_export] macro_rules! ensure { ($cond:expr, $msg:literal $(,)?) => { if !$cond { return $crate::private::Err($crate::format_err!($msg)) } }; ($cond:expr, $msg:expr $(,)?) => { if !$cond { return $crate::private::Err($crate::format_err!($msg)) } }; ($cond:expr, $msg:expr, $($arg:tt)*) => { if !$cond { return $crate::private::Err($crate::format_err!($msg, $($arg)*)) } }; } /// Return early with an error if two expressions are not equal to each other. /// /// This macro is equivalent to `if $left != $right { return Err(From::from($err)); }`. /// /// Analogously to `assert_eq!`, `ensure_eq!` takes two expressions and exits the function /// if the expressions are not equal. Unlike `assert_eq!`, `ensure_eq!` returns an `Error` /// rather than panicking. #[macro_export] macro_rules! ensure_eq { ($left:expr, $right:expr, $msg:literal $(,)?) => { if $left != $right { return $crate::private::Err($crate::format_err!($msg)) } }; ($left:expr, $right:expr, $msg:expr $(,)?) => { if $left != $right { return $crate::private::Err($crate::format_err!($msg)) } }; ($left:expr, $right:expr, $msg:expr, $($arg:tt)*) => { if $left != $right { return $crate::private::Err($crate::format_err!($msg, $($arg)*)) } }; } /// Construct an ad-hoc error from a string. /// /// This evaluates to an `Error`. It can take either just a string, or a format /// string with arguments. It also can take any custom type which implements /// `Debug` and `Display`. #[macro_export] macro_rules! format_err { ($msg:literal $(,)?) => { // Handle $:literal as a special case to make cargo-expanded code more // concise in the common case. $crate::private::new_adhoc($msg) }; ($err:expr $(,)?) => ({ let error = $err; Error::new_adhoc(error) }); ($fmt:expr, $($arg:tt)*) => { $crate::private::new_adhoc(format!($fmt, $($arg)*)) }; } /// Return early with an error and a status code. #[doc(hidden)] #[macro_export] macro_rules! bail_status { ($status:literal, $msg:literal $(,)?) => {{ return $crate::private::Err($crate::format_err_status!($status, $msg)) }}; ($status:literal, $msg:expr $(,)?) => { return $crate::private::Err($crate::format_err_status!($status, $msg)) }; ($status:literal, $msg:expr, $($arg:tt)*) => { return $crate::private::Err($crate::format_err_status!($status, $msg, $($arg)*)) }; } /// Return early with an error if a condition is not satisfied. /// /// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`. /// /// Analogously to `assert!`, `ensure!` takes a condition and exits the function /// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error` /// rather than panicking. #[doc(hidden)] #[macro_export] macro_rules! ensure_status { ($cond:expr, $status:literal, $msg:literal $(,)?) => { if !$cond { return $crate::private::Err($crate::format_err_status!($status, $msg)) } }; ($cond:expr, $status:literal, $msg:expr $(,)?) => { if !$cond { return $crate::private::Err($crate::format_err_status!($status, $msg)) } }; ($cond:expr, $status:literal, $msg:expr, $($arg:tt)*) => { if !$cond { return $crate::private::Err($crate::format_err_status!($status, $msg, $($arg)*)) } }; } /// Return early with an error if two expressions are not equal to each other. /// /// This macro is equivalent to `if $left != $right { return Err(From::from($err)); }`. /// /// Analogously to `assert_eq!`, `ensure_eq!` takes two expressions and exits the function /// if the expressions are not equal. Unlike `assert_eq!`, `ensure_eq!` returns an `Error` /// rather than panicking. #[doc(hidden)] #[macro_export] macro_rules! ensure_eq_status { ($left:expr, $right:expr, $status:literal, $msg:literal $(,)?) => { if $left != $right { return $crate::private::Err($crate::format_err_status!($status, $msg)) } }; ($left:expr, $right:expr, $status:literal, $msg:expr $(,)?) => { if $left != $right { return $crate::private::Err($crate::format_err_status!($status, $msg)) } }; ($left:expr, $right:expr, $status:literal, $msg:expr, $($arg:tt)*) => { if $left != $right { return $crate::private::Err($crate::format_err_status!($status, $msg, $($arg)*)) } }; } /// Construct an ad-hoc error from a string. /// /// This evaluates to an `Error`. It can take either just a string, or a format /// string with arguments. It also can take any custom type which implements /// `Debug` and `Display`. #[doc(hidden)] #[macro_export] macro_rules! format_err_status { ($status:literal, $msg:literal $(,)?) => {{ // Handle $:literal as a special case to make cargo-expanded code more // concise in the common case. let mut err = $crate::private::new_adhoc($msg); err.set_status($status); err }}; ($status:literal, $msg:expr $(,)?) => {{ let mut err = $crate::private::new_adhoc($msg); err.set_status($status); err }}; ($status:literal, $msg:expr, $($arg:tt)*) => {{ let mut err = $crate::private::new_adhoc(format!($msg, $($arg)*)); err.set_status($status); err }}; } http-types-2.12.0/src/method.rs000064400000000000000000000511410072674642500144760ustar 00000000000000use serde::de::{Error as DeError, Unexpected}; use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Display}; use std::str::FromStr; /// HTTP request methods. /// /// See also [Mozilla's documentation][Mozilla docs], the [RFC7231, Section 4][] and /// [IANA's Hypertext Transfer Protocol (HTTP) Method Registry][HTTP Method Registry]. /// /// [Mozilla docs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods /// [RFC7231, Section 4]: https://tools.ietf.org/html/rfc7231#section-4 /// [HTTP Method Registry]: https://www.iana.org/assignments/http-methods/http-methods.xhtml #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum Method { /// The ACL method modifies the access control list (which can be read via the DAV:acl /// property) of a resource. /// /// See [RFC3744, Section 8.1][]. /// /// [RFC3744, Section 8.1]: https://tools.ietf.org/html/rfc3744#section-8.1 Acl, /// A collection can be placed under baseline control with a BASELINE-CONTROL request. /// /// See [RFC3253, Section 12.6][]. /// /// [RFC3253, Section 12.6]: https://tools.ietf.org/html/rfc3253#section-12.6 BaselineControl, /// The BIND method modifies the collection identified by the Request- URI, by adding a new /// binding from the segment specified in the BIND body to the resource identified in the BIND /// body. /// /// See [RFC5842, Section 4][]. /// /// [RFC5842, Section 4]: https://tools.ietf.org/html/rfc5842#section-4 Bind, /// A CHECKIN request can be applied to a checked-out version-controlled resource to produce a /// new version whose content and dead properties are copied from the checked-out resource. /// /// See [RFC3253, Section 4.4][] and [RFC3253, Section 9.4][]. /// /// [RFC3253, Section 4.4]: https://tools.ietf.org/html/rfc3253#section-4.4 /// [RFC3253, Section 9.4]: https://tools.ietf.org/html/rfc3253#section-9.4 Checkin, /// A CHECKOUT request can be applied to a checked-in version-controlled resource to allow /// modifications to the content and dead properties of that version-controlled resource. /// /// See [RFC3253, Section 4.3][] and [RFC3253, Section 8.8][]. /// /// [RFC3253, Section 4.3]: https://tools.ietf.org/html/rfc3253#section-4.3 /// [RFC3253, Section 8.8]: https://tools.ietf.org/html/rfc3253#section-8.8 Checkout, /// The CONNECT method requests that the recipient establish a tunnel to the destination origin /// server identified by the request-target and, if successful, thereafter restrict its /// behavior to blind forwarding of packets, in both directions, until the tunnel is closed. /// /// See [RFC7231, Section 4.3.6][]. /// /// [RFC7231, Section 4.3.6]: https://tools.ietf.org/html/rfc7231#section-4.3.6 Connect, /// The COPY method creates a duplicate of the source resource identified by the Request-URI, /// in the destination resource identified by the URI in the Destination header. /// /// See [RFC4918, Section 9.8][]. /// /// [RFC4918, Section 9.8]: https://tools.ietf.org/html/rfc4918#section-9.8 Copy, /// The DELETE method requests that the origin server remove the association between the target /// resource and its current functionality. /// /// See [RFC7231, Section 4.3.5][]. /// /// [RFC7231, Section 4.3.5]: https://tools.ietf.org/html/rfc7231#section-4.3.5 Delete, /// The GET method requests transfer of a current selected representation for the target /// resource. /// /// See [RFC7231, Section 4.3.1][]. /// /// [RFC7231, Section 4.3.1]: https://tools.ietf.org/html/rfc7231#section-4.3.1 Get, /// The HEAD method is identical to GET except that the server MUST NOT send a message body in /// the response. /// /// See [RFC7231, Section 4.3.2][]. /// /// [RFC7231, Section 4.3.2]: https://tools.ietf.org/html/rfc7231#section-4.3.2 Head, /// A LABEL request can be applied to a version to modify the labels that select that version. /// /// See [RFC3253, Section 8.2][]. /// /// [RFC3253, Section 8.2]: https://tools.ietf.org/html/rfc3253#section-8.2 Label, /// The LINK method establishes one or more Link relationships between the existing resource /// identified by the Request-URI and other existing resources. /// /// See [RFC2068, Section 19.6.1.2][]. /// /// [RFC2068, Section 19.6.1.2]: https://tools.ietf.org/html/rfc2068#section-19.6.1.2 Link, /// The LOCK method is used to take out a lock of any access type and to refresh an existing /// lock. /// /// See [RFC4918, Section 9.10][]. /// /// [RFC4918, Section 9.10]: https://tools.ietf.org/html/rfc4918#section-9.10 Lock, /// The MERGE method performs the logical merge of a specified version (the "merge source") /// into a specified version-controlled resource (the "merge target"). /// /// See [RFC3253, Section 11.2][]. /// /// [RFC3253, Section 11.2]: https://tools.ietf.org/html/rfc3253#section-11.2 Merge, /// A MKACTIVITY request creates a new activity resource. /// /// See [RFC3253, Section 13.5]. /// /// [RFC3253, Section 13.5]: https://tools.ietf.org/html/rfc3253#section-13.5 MkActivity, /// An HTTP request using the MKCALENDAR method creates a new calendar collection resource. /// /// See [RFC4791, Section 5.3.1][] and [RFC8144, Section 2.3][]. /// /// [RFC4791, Section 5.3.1]: https://tools.ietf.org/html/rfc4791#section-5.3.1 /// [RFC8144, Section 2.3]: https://tools.ietf.org/html/rfc8144#section-2.3 MkCalendar, /// MKCOL creates a new collection resource at the location specified by the Request-URI. /// /// See [RFC4918, Section 9.3][], [RFC5689, Section 3][] and [RFC8144, Section 2.3][]. /// /// [RFC4918, Section 9.3]: https://tools.ietf.org/html/rfc4918#section-9.3 /// [RFC5689, Section 3]: https://tools.ietf.org/html/rfc5689#section-3 /// [RFC8144, Section 2.3]: https://tools.ietf.org/html/rfc5689#section-3 MkCol, /// The MKREDIRECTREF method requests the creation of a redirect reference resource. /// /// See [RFC4437, Section 6][]. /// /// [RFC4437, Section 6]: https://tools.ietf.org/html/rfc4437#section-6 MkRedirectRef, /// A MKWORKSPACE request creates a new workspace resource. /// /// See [RFC3253, Section 6.3][]. /// /// [RFC3253, Section 6.3]: https://tools.ietf.org/html/rfc3253#section-6.3 MkWorkspace, /// The MOVE operation on a non-collection resource is the logical equivalent of a copy (COPY), /// followed by consistency maintenance processing, followed by a delete of the source, where /// all three actions are performed in a single operation. /// /// See [RFC4918, Section 9.9][]. /// /// [RFC4918, Section 9.9]: https://tools.ietf.org/html/rfc4918#section-9.9 Move, /// The OPTIONS method requests information about the communication options available for the /// target resource, at either the origin server or an intervening intermediary. /// /// See [RFC7231, Section 4.3.7][]. /// /// [RFC7231, Section 4.3.7]: https://tools.ietf.org/html/rfc7231#section-4.3.7 Options, /// The ORDERPATCH method is used to change the ordering semantics of a collection, to change /// the order of the collection's members in the ordering, or both. /// /// See [RFC3648, Section 7][]. /// /// [RFC3648, Section 7]: https://tools.ietf.org/html/rfc3648#section-7 OrderPatch, /// The PATCH method requests that a set of changes described in the request entity be applied /// to the resource identified by the Request- URI. /// /// See [RFC5789, Section 2][]. /// /// [RFC5789, Section 2]: https://tools.ietf.org/html/rfc5789#section-2 Patch, /// The POST method requests that the target resource process the representation enclosed in /// the request according to the resource's own specific semantics. /// /// For example, POST is used for the following functions (among others): /// /// - Providing a block of data, such as the fields entered into an HTML form, to a /// data-handling process; /// - Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group /// of articles; /// - Creating a new resource that has yet to be identified by the origin server; and /// - Appending data to a resource's existing representation(s). /// /// See [RFC7231, Section 4.3.3][]. /// /// [RFC7231, Section 4.3.3]: https://tools.ietf.org/html/rfc7231#section-4.3.3 Post, /// This method is never used by an actual client. This method will appear to be used when an /// HTTP/1.1 server or intermediary attempts to parse an HTTP/2 connection preface. /// /// See [RFC7540, Section 3.5][] and [RFC7540, Section 11.6][] /// /// [RFC7540, Section 3.5]: https://tools.ietf.org/html/rfc7540#section-3.5 /// [RFC7540, Section 11.6]: https://tools.ietf.org/html/rfc7540#section-11.6 Pri, /// The PROPFIND method retrieves properties defined on the resource identified by the /// Request-URI. /// /// See [RFC4918, Section 9.1][] and [RFC8144, Section 2.1][]. /// /// [RFC4918, Section 9.1]: https://tools.ietf.org/html/rfc4918#section-9.1 /// [RFC8144, Section 2.1]: https://tools.ietf.org/html/rfc8144#section-2.1 PropFind, /// The PROPPATCH method processes instructions specified in the request body to set and/or /// remove properties defined on the resource identified by the Request-URI. /// /// See [RFC4918, Section 9.2][] and [RFC8144, Section 2.2][]. /// /// [RFC4918, Section 9.2]: https://tools.ietf.org/html/rfc4918#section-9.2 /// [RFC8144, Section 2.2]: https://tools.ietf.org/html/rfc8144#section-2.2 PropPatch, /// The PUT method requests that the state of the target resource be created or replaced with /// the state defined by the representation enclosed in the request message payload. /// /// See [RFC7231, Section 4.3.4][]. /// /// [RFC7231, Section 4.3.4]: https://tools.ietf.org/html/rfc7231#section-4.3.4 Put, /// The REBIND method removes a binding to a resource from a collection, and adds a binding to /// that resource into the collection identified by the Request-URI. /// /// See [RFC5842, Section 6][]. /// /// [RFC5842, Section 6]: https://tools.ietf.org/html/rfc5842#section-6 Rebind, /// A REPORT request is an extensible mechanism for obtaining information about a resource. /// /// See [RFC3253, Section 3.6][] and [RFC8144, Section 2.1][]. /// /// [RFC3253, Section 3.6]: https://tools.ietf.org/html/rfc3253#section-3.6 /// [RFC8144, Section 2.1]: https://tools.ietf.org/html/rfc8144#section-2.1 Report, /// The client invokes the SEARCH method to initiate a server-side search. The body of the /// request defines the query. /// /// See [RFC5323, Section 2][]. /// /// [RFC5323, Section 2]: https://tools.ietf.org/html/rfc5323#section-2 Search, /// The TRACE method requests a remote, application-level loop-back of the request message. /// /// See [RFC7231, Section 4.3.8][]. /// /// [RFC7231, Section 4.3.8]: https://tools.ietf.org/html/rfc7231#section-4.3.8 Trace, /// The UNBIND method modifies the collection identified by the Request- URI by removing the /// binding identified by the segment specified in the UNBIND body. /// /// See [RFC5842, Section 5][]. /// /// [RFC5842, Section 5]: https://tools.ietf.org/html/rfc5842#section-5 Unbind, /// An UNCHECKOUT request can be applied to a checked-out version-controlled resource to cancel /// the CHECKOUT and restore the pre-CHECKOUT state of the version-controlled resource. /// /// See [RFC3253, Section 4.5][]. /// /// [RFC3253, Section 4.5]: https://tools.ietf.org/html/rfc3253#section-4.5 Uncheckout, /// The UNLINK method removes one or more Link relationships from the existing resource /// identified by the Request-URI. /// /// See [RFC2068, Section 19.6.1.3][]. /// /// [RFC2068, Section 19.6.1.3]: https://tools.ietf.org/html/rfc2068#section-19.6.1.3 Unlink, /// The UNLOCK method removes the lock identified by the lock token in the Lock-Token request /// header. /// /// See [RFC4918, Section 9.11][]. /// /// [RFC4918, Section 9.11]: https://tools.ietf.org/html/rfc4918#section-9.11 Unlock, /// The UPDATE method modifies the content and dead properties of a checked-in /// version-controlled resource (the "update target") to be those of a specified version (the /// "update source") from the version history of that version-controlled resource. /// /// See [RFC3253, Section 7.1][]. /// /// [RFC3253, Section 7.1]: https://tools.ietf.org/html/rfc3253#section-7.1 Update, /// The UPDATEREDIRECTREF method requests the update of a redirect reference resource. /// /// See [RFC4437, Section 7][]. /// /// [RFC4437, Section 7]: https://tools.ietf.org/html/rfc4437#section-7 UpdateRedirectRef, /// A VERSION-CONTROL request can be used to create a version-controlled resource at the /// request-URL. /// /// See [RFC3253, Section 3.5]. /// /// [RFC3253, Section 3.5]: https://tools.ietf.org/html/rfc3253#section-3.5 VersionControl, } impl Method { /// Whether a method is considered "safe", meaning the request is essentially read-only. /// /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1) for more details. pub fn is_safe(&self) -> bool { matches!( self, Method::Get | Method::Head | Method::Options | Method::Pri | Method::PropFind | Method::Report | Method::Search | Method::Trace ) } } struct MethodVisitor; impl<'de> Visitor<'de> for MethodVisitor { type Value = Method; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a HTTP method &str") } fn visit_str(self, v: &str) -> Result where E: DeError, { match Method::from_str(v) { Ok(method) => Ok(method), Err(_) => Err(DeError::invalid_value(Unexpected::Str(v), &self)), } } } impl<'de> Deserialize<'de> for Method { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(MethodVisitor) } } impl Serialize for Method { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.to_string()) } } impl Display for Method { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(AsRef::::as_ref(self)) } } impl FromStr for Method { type Err = crate::Error; fn from_str(s: &str) -> Result { match &*s.to_ascii_uppercase() { "ACL" => Ok(Self::Acl), "BASELINE-CONTROL" => Ok(Self::BaselineControl), "BIND" => Ok(Self::Bind), "CHECKIN" => Ok(Self::Checkin), "CHECKOUT" => Ok(Self::Checkout), "CONNECT" => Ok(Self::Connect), "COPY" => Ok(Self::Copy), "DELETE" => Ok(Self::Delete), "GET" => Ok(Self::Get), "HEAD" => Ok(Self::Head), "LABEL" => Ok(Self::Label), "LINK" => Ok(Self::Link), "LOCK" => Ok(Self::Lock), "MERGE" => Ok(Self::Merge), "MKACTIVITY" => Ok(Self::MkActivity), "MKCALENDAR" => Ok(Self::MkCalendar), "MKCOL" => Ok(Self::MkCol), "MKREDIRECTREF" => Ok(Self::MkRedirectRef), "MKWORKSPACE" => Ok(Self::MkWorkspace), "MOVE" => Ok(Self::Move), "OPTIONS" => Ok(Self::Options), "ORDERPATCH" => Ok(Self::OrderPatch), "PATCH" => Ok(Self::Patch), "POST" => Ok(Self::Post), "PRI" => Ok(Self::Pri), "PROPFIND" => Ok(Self::PropFind), "PROPPATCH" => Ok(Self::PropPatch), "PUT" => Ok(Self::Put), "REBIND" => Ok(Self::Rebind), "REPORT" => Ok(Self::Report), "SEARCH" => Ok(Self::Search), "TRACE" => Ok(Self::Trace), "UNBIND" => Ok(Self::Unbind), "UNCHECKOUT" => Ok(Self::Uncheckout), "UNLINK" => Ok(Self::Unlink), "UNLOCK" => Ok(Self::Unlock), "UPDATE" => Ok(Self::Update), "UPDATEREDIRECTREF" => Ok(Self::UpdateRedirectRef), "VERSION-CONTROL" => Ok(Self::VersionControl), _ => crate::bail!("Invalid HTTP method"), } } } impl<'a> std::convert::TryFrom<&'a str> for Method { type Error = crate::Error; fn try_from(value: &'a str) -> Result { Self::from_str(value) } } impl AsRef for Method { fn as_ref(&self) -> &str { match self { Self::Acl => "ACL", Self::BaselineControl => "BASELINE-CONTROL", Self::Bind => "BIND", Self::Checkin => "CHECKIN", Self::Checkout => "CHECKOUT", Self::Connect => "CONNECT", Self::Copy => "COPY", Self::Delete => "DELETE", Self::Get => "GET", Self::Head => "HEAD", Self::Label => "LABEL", Self::Link => "LINK", Self::Lock => "LOCK", Self::Merge => "MERGE", Self::MkActivity => "MKACTIVITY", Self::MkCalendar => "MKCALENDAR", Self::MkCol => "MKCOL", Self::MkRedirectRef => "MKREDIRECTREF", Self::MkWorkspace => "MKWORKSPACE", Self::Move => "MOVE", Self::Options => "OPTIONS", Self::OrderPatch => "ORDERPATCH", Self::Patch => "PATCH", Self::Post => "POST", Self::Pri => "PRI", Self::PropFind => "PROPFIND", Self::PropPatch => "PROPPATCH", Self::Put => "PUT", Self::Rebind => "REBIND", Self::Report => "REPORT", Self::Search => "SEARCH", Self::Trace => "TRACE", Self::Unbind => "UNBIND", Self::Uncheckout => "UNCHECKOUT", Self::Unlink => "UNLINK", Self::Unlock => "UNLOCK", Self::Update => "UPDATE", Self::UpdateRedirectRef => "UPDATEREDIRECTREF", Self::VersionControl => "VERSION-CONTROL", } } } #[cfg(test)] mod test { use std::collections::HashSet; use super::Method; #[test] fn serde() -> Result<(), serde_json::Error> { assert_eq!(Method::Get, serde_json::from_str("\"GET\"")?); assert_eq!(Some("PATCH"), serde_json::to_value(Method::Patch)?.as_str()); Ok(()) } #[test] fn serde_fail() { serde_json::from_str::("\"ABC\"").expect_err("Did deserialize from invalid string"); } #[test] fn names() -> Result<(), crate::Error> { let method_names = [ "ACL", "BASELINE-CONTROL", "BIND", "CHECKIN", "CHECKOUT", "CONNECT", "COPY", "DELETE", "GET", "HEAD", "LABEL", "LINK", "LOCK", "MERGE", "MKACTIVITY", "MKCALENDAR", "MKCOL", "MKREDIRECTREF", "MKWORKSPACE", "MOVE", "OPTIONS", "ORDERPATCH", "PATCH", "POST", "PRI", "PROPFIND", "PROPPATCH", "PUT", "REBIND", "REPORT", "SEARCH", "TRACE", "UNBIND", "UNCHECKOUT", "UNLINK", "UNLOCK", "UPDATE", "UPDATEREDIRECTREF", "VERSION-CONTROL", ]; let methods = method_names .iter() .map(|s| s.parse::()) .collect::, _>>()?; // check that we didn't accidentally map two methods to the same variant assert_eq!(methods.len(), method_names.len()); // check that a method's name and the name it is parsed from match for method in methods { assert_eq!(method.as_ref().parse::()?, method); } Ok(()) } } http-types-2.12.0/src/mime/constants.rs000064400000000000000000000046610072674642500161660ustar 00000000000000use crate::Mime; use std::borrow::Cow; macro_rules! utf8_mime_const { ($name:ident, $desc:expr, $base:expr, $sub:expr) => { mime_const!( with_params, $name, $desc, $base, $sub, true, ";charset=utf-8" ); }; } macro_rules! mime_const { ($name:ident, $desc:expr, $base:expr, $sub:expr) => { mime_const!(with_params, $name, $desc, $base, $sub, false, ""); }; (with_params, $name:ident, $desc:expr, $base:expr, $sub:expr, $is_utf8:expr, $doccomment:expr) => { mime_const!( doc_expanded, $name, $desc, $base, $sub, $is_utf8, concat!( "Content-Type for ", $desc, ".\n\n# Mime Type\n\n```text\n", $base, "/", $sub, $doccomment, "\n```" ) ); }; (doc_expanded, $name:ident, $desc:expr, $base:expr, $sub:expr, $is_utf8:expr, $doccomment:expr) => { #[doc = $doccomment] pub const $name: Mime = Mime { essence: Cow::Borrowed(concat!($base, "/", $sub)), basetype: Cow::Borrowed($base), subtype: Cow::Borrowed($sub), is_utf8: $is_utf8, params: vec![], }; }; } utf8_mime_const!(JAVASCRIPT, "JavaScript", "application", "javascript"); utf8_mime_const!(CSS, "CSS", "text", "css"); utf8_mime_const!(HTML, "HTML", "text", "html"); utf8_mime_const!(PLAIN, "Plain text", "text", "plain"); utf8_mime_const!(XML, "XML", "application", "xml"); mime_const!(ANY, "matching anything", "*", "*"); mime_const!(JSON, "JSON", "application", "json"); mime_const!(SVG, "SVG", "image", "svg+xml"); mime_const!(PNG, "PNG images", "image", "png"); mime_const!(JPEG, "JPEG images", "image", "jpeg"); mime_const!(SSE, "Server Sent Events", "text", "event-stream"); mime_const!(BYTE_STREAM, "byte streams", "application", "octet-stream"); mime_const!(FORM, "forms", "application", "x-www-form-urlencoded"); mime_const!(MULTIPART_FORM, "multipart forms", "multipart", "form-data"); mime_const!(WASM, "webassembly", "application", "wasm"); // There are multiple `.ico` mime types known, but `image/x-icon` // is what most browser use. See: // https://en.wikipedia.org/wiki/ICO_%28file_format%29#MIME_type mime_const!(ICO, "ICO icons", "image", "x-icon"); http-types-2.12.0/src/mime/mod.rs000064400000000000000000000131370072674642500147270ustar 00000000000000//! IANA Media Types. //! //! [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types). mod constants; mod parse; pub use constants::*; use std::borrow::Cow; use std::fmt::{self, Debug, Display}; use std::option; use std::str::FromStr; use crate::headers::{HeaderValue, ToHeaderValues}; use infer::Infer; /// An IANA media type. /// /// ``` /// use http_types::Mime; /// use std::str::FromStr; /// /// let mime = Mime::from_str("text/html;charset=utf-8").unwrap(); /// assert_eq!(mime.essence(), "text/html"); /// assert_eq!(mime.param("charset").unwrap(), "utf-8"); /// ``` // NOTE: we cannot statically initialize Strings with values yet, so we keep dedicated static // fields for the static strings. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Mime { pub(crate) essence: Cow<'static, str>, pub(crate) basetype: Cow<'static, str>, pub(crate) subtype: Cow<'static, str>, // NOTE(yosh): this is a hack because we can't populate vecs in const yet. // This enables us to encode media types as utf-8 at compilation. pub(crate) is_utf8: bool, pub(crate) params: Vec<(ParamName, ParamValue)>, } impl Mime { /// Sniff the mime type from a byte slice. pub fn sniff(bytes: &[u8]) -> crate::Result { let info = Infer::new(); let mime = match info.get(bytes) { Some(info) => info.mime, None => crate::bail!("Could not sniff the mime type"), }; Mime::from_str(&mime) } /// Guess the mime type from a file extension pub fn from_extension(extension: impl AsRef) -> Option { match extension.as_ref() { "html" => Some(HTML), "js" | "mjs" | "jsonp" => Some(JAVASCRIPT), "json" => Some(JSON), "css" => Some(CSS), "svg" => Some(SVG), "xml" => Some(XML), _ => None, } } /// Access the Mime's `type` value. /// /// According to the spec this method should be named `type`, but that's a reserved keyword in /// Rust so hence prefix with `base` instead. pub fn basetype(&self) -> &str { &self.basetype } /// Access the Mime's `subtype` value. pub fn subtype(&self) -> &str { &self.subtype } /// Access the Mime's `essence` value. pub fn essence(&self) -> &str { &self.essence } /// Get a reference to a param. pub fn param(&self, name: impl Into) -> Option<&ParamValue> { let name: ParamName = name.into(); if name.as_str() == "charset" && self.is_utf8 { return Some(&ParamValue(Cow::Borrowed("utf-8"))); } self.params.iter().find(|(k, _)| k == &name).map(|(_, v)| v) } /// Remove a param from the set. Returns the `ParamValue` if it was contained within the set. pub fn remove_param(&mut self, name: impl Into) -> Option { let name: ParamName = name.into(); if name.as_str() == "charset" && self.is_utf8 { self.is_utf8 = false; return Some(ParamValue(Cow::Borrowed("utf-8"))); } self.params .iter() .position(|(k, _)| k == &name) .map(|pos| self.params.remove(pos).1) } } impl Display for Mime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { parse::format(self, f) } } // impl Debug for Mime { // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Debug::fmt(&self.essence, f) // } // } impl FromStr for Mime { type Err = crate::Error; /// Create a new `Mime`. /// /// Follows the [WHATWG MIME parsing algorithm](https://mimesniff.spec.whatwg.org/#parsing-a-mime-type). fn from_str(s: &str) -> Result { parse::parse(s) } } impl<'a> From<&'a str> for Mime { fn from(value: &'a str) -> Self { Self::from_str(value).unwrap() } } impl ToHeaderValues for Mime { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { let mime = self.clone(); let header: HeaderValue = mime.into(); // A HeaderValue will always convert into itself. Ok(header.to_header_values().unwrap()) } } /// A parameter name. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParamName(Cow<'static, str>); impl ParamName { /// Get the name as a `&str` pub fn as_str(&self) -> &str { &self.0 } } impl Display for ParamName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self.0, f) } } impl FromStr for ParamName { type Err = crate::Error; /// Create a new `HeaderName`. /// /// This checks it's valid ASCII, and lowercases it. fn from_str(s: &str) -> Result { crate::ensure!(s.is_ascii(), "String slice should be valid ASCII"); Ok(ParamName(Cow::Owned(s.to_ascii_lowercase()))) } } impl<'a> From<&'a str> for ParamName { fn from(value: &'a str) -> Self { Self::from_str(value).unwrap() } } /// A parameter value. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ParamValue(Cow<'static, str>); impl ParamValue { /// Get the value as a `&str` pub fn as_str(&self) -> &str { &self.0 } } impl Display for ParamValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&self.0, f) } } impl<'a> PartialEq<&'a str> for ParamValue { fn eq(&self, other: &&'a str) -> bool { &self.0 == other } } impl PartialEq for ParamValue { fn eq(&self, other: &str) -> bool { self.0 == other } } http-types-2.12.0/src/mime/parse.rs000064400000000000000000000457210072674642500152660ustar 00000000000000use std::borrow::Cow; use std::fmt; use super::{Mime, ParamName, ParamValue}; /// Parse a string into a mime type. /// Follows the [WHATWG MIME parsing algorithm](https://mimesniff.spec.whatwg.org/#parsing-a-mime-type) pub(crate) fn parse(input: &str) -> crate::Result { // 1 let input = input.trim_matches(is_http_whitespace_char); // 3. let (basetype, input) = collect_code_point_sequence_char(input, '/'); // 4. crate::ensure!(!basetype.is_empty(), "MIME type should not be empty"); crate::ensure!( basetype.chars().all(is_http_token_code_point), "MIME type should ony contain valid HTTP token code points" ); // 5. crate::ensure!(!input.is_empty(), "MIME must contain a sub type"); // 6. let input = &input[1..]; // 7. let (subtype, input) = collect_code_point_sequence_char(input, ';'); // 8. let subtype = subtype.trim_end_matches(is_http_whitespace_char); // 9. crate::ensure!(!subtype.is_empty(), "MIME sub type should not be empty"); crate::ensure!( subtype.chars().all(is_http_token_code_point), "MIME sub type should ony contain valid HTTP token code points" ); // 10. let basetype = basetype.to_ascii_lowercase(); let subtype = subtype.to_ascii_lowercase(); let mut params = vec![]; let mut is_utf8 = false; // 11. let mut input = input; while !input.is_empty() { // 1. input = &input[1..]; // 2. input = input.trim_start_matches(is_http_whitespace_char); // 3. let (parameter_name, new_input) = collect_code_point_sequence_slice(input, &[';', '='] as &[char]); input = new_input; // 4. let parameter_name = parameter_name.to_ascii_lowercase(); if input.is_empty() { // 6. break; } else { // 5. if input.starts_with(';') { continue; } else { // It's a '=' input = &input[1..]; } } let parameter_value = if input.starts_with('"') { // 8. // implementation of https://fetch.spec.whatwg.org/#collect-an-http-quoted-string let (parameter_value, new_input) = collect_http_quoted_string(input); let (_, new_input) = collect_code_point_sequence_char(new_input, ';'); input = new_input; parameter_value } else { // 9. let (parameter_value, new_input) = collect_code_point_sequence_char(input, ';'); input = new_input; let parameter_value = parameter_value.trim_end_matches(is_http_whitespace_char); if parameter_value.is_empty() { continue; } parameter_value.to_owned() }; // 10. if parameter_name == "charset" && parameter_value == "utf-8" { is_utf8 = true; } else if !parameter_name.is_empty() && parameter_name.chars().all(is_http_token_code_point) && parameter_value .chars() .all(is_http_quoted_string_token_code_point) { let name = ParamName(parameter_name.into()); let value = ParamValue(parameter_value.into()); if !params.iter().any(|(k, _)| k == &name) { params.push((name, value)); } } } Ok(Mime { essence: Cow::Owned(format!("{}/{}", &basetype, &subtype)), basetype: basetype.into(), subtype: subtype.into(), params, is_utf8, }) } /// Validates [HTTP token code points](https://mimesniff.spec.whatwg.org/#http-token-code-point) fn is_http_token_code_point(c: char) -> bool { matches!(c, '!' | '#' | '$' | '%' | '&' | '\'' | '*' | '+' | '-' | '.' | '^' | '_' | '`' | '|' | '~' | 'a'..='z' | 'A'..='Z' | '0'..='9') } /// Validates [HTTP quoted-string token code points](https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point) fn is_http_quoted_string_token_code_point(c: char) -> bool { matches!(c, '\t' | ' '..='~' | '\u{80}'..='\u{FF}') } /// Is a [HTTP whitespace](https://fetch.spec.whatwg.org/#http-whitespace) fn is_http_whitespace_char(c: char) -> bool { matches!(c, '\n' | '\r' | '\t' | ' ') } /// [code point sequence collection](https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points) fn collect_code_point_sequence_char(input: &str, delimiter: char) -> (&str, &str) { input.split_at(input.find(delimiter).unwrap_or_else(|| input.len())) } /// [code point sequence collection](https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points) fn collect_code_point_sequence_slice<'a>(input: &'a str, delimiter: &[char]) -> (&'a str, &'a str) { input.split_at(input.find(delimiter).unwrap_or_else(|| input.len())) } /// [HTTP quoted string collection](https://fetch.spec.whatwg.org/#collect-an-http-quoted-string) /// /// Assumes that the first char is '"' fn collect_http_quoted_string(mut input: &str) -> (String, &str) { // 2. let mut value = String::new(); // 4. input = &input[1..]; // 5. loop { // 1. let (add_value, new_input) = collect_code_point_sequence_slice(input, &['"', '\\'] as &[char]); value.push_str(add_value); let mut chars = new_input.chars(); // 3. if let Some(quote_or_backslash) = chars.next() { // 4. input = chars.as_str(); //5. if quote_or_backslash == '\\' { if let Some(c) = chars.next() { // 2. value.push(c); // 3. input = chars.as_str(); } else { // 1. value.push('\\'); break; } } else { // 6. break; } } else { // 2 break; } } (value, input) } /// Implementation of [WHATWG MIME serialization algorithm](https://mimesniff.spec.whatwg.org/#serializing-a-mime-type) pub(crate) fn format(mime_type: &Mime, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", &mime_type.essence)?; if mime_type.is_utf8 { write!(f, ";charset=utf-8")?; } for (name, value) in mime_type.params.iter() { if value.0.chars().all(is_http_token_code_point) && !value.0.is_empty() { write!(f, ";{}={}", name, value)?; } else { let value = value .0 .chars() .flat_map(|c| match c { '"' | '\\' => EscapeMimeValue::backslash(c), c => EscapeMimeValue::char(c), }) .collect::(); write!(f, ";{}=\"{}\"", name, value)?; } } Ok(()) } struct EscapeMimeValue { state: EscapeMimeValueState, } impl EscapeMimeValue { fn backslash(c: char) -> Self { EscapeMimeValue { state: EscapeMimeValueState::Backslash(c), } } fn char(c: char) -> Self { EscapeMimeValue { state: EscapeMimeValueState::Char(c), } } } #[derive(Clone, Debug)] enum EscapeMimeValueState { Done, Char(char), Backslash(char), } impl Iterator for EscapeMimeValue { type Item = char; fn next(&mut self) -> Option { match self.state { EscapeMimeValueState::Done => None, EscapeMimeValueState::Char(c) => { self.state = EscapeMimeValueState::Done; Some(c) } EscapeMimeValueState::Backslash(c) => { self.state = EscapeMimeValueState::Char(c); Some('\\') } } } fn size_hint(&self) -> (usize, Option) { match self.state { EscapeMimeValueState::Done => (0, Some(0)), EscapeMimeValueState::Char(_) => (1, Some(1)), EscapeMimeValueState::Backslash(_) => (2, Some(2)), } } } #[test] fn test() { let mime = parse("text/html").unwrap(); assert_eq!(mime.basetype(), "text"); assert_eq!(mime.subtype(), "html"); // technically invalid mime, but allow anyway let mime = parse("text/html;").unwrap(); assert_eq!(mime.basetype(), "text"); assert_eq!(mime.subtype(), "html"); let mime = parse("text/html; charset=utf-8").unwrap(); assert_eq!(mime.basetype(), "text"); assert_eq!(mime.subtype(), "html"); assert_eq!(mime.param("charset").unwrap(), "utf-8"); let mime = parse("text/html; charset=utf-8;").unwrap(); assert_eq!(mime.basetype(), "text"); assert_eq!(mime.subtype(), "html"); assert_eq!(mime.param("charset").unwrap(), "utf-8"); assert!(parse("text").is_err()); assert!(parse("text/").is_err()); assert!(parse("t/").is_err()); assert!(parse("t/h").is_ok()); } /// Web Platform tests for MIME type parsing /// From https://github.com/web-platform-tests/wpt/blob/master/mimesniff/mime-types/resources/mime-types.json #[test] fn whatwag_tests() { fn assert_parse(input: &str, expected: &str) { let actual = parse(input).unwrap(); assert_eq!(actual.to_string(), expected); } fn assert_fails(input: &str) { assert!(parse(input).is_err()); } fn assert_parse_and_encoding( input: &str, expected: &str, _encoding: impl Into>, ) { //TODO: check encoding assert_parse(input, expected); } // Basics assert_parse_and_encoding("text/html;charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding("TEXT/HTML;CHARSET=GBK", "text/html;charset=GBK", "GBK"); //" Legacy comment syntax" assert_parse_and_encoding("text/html;charset=gbk(", "text/html;charset=\"gbk(\"", None); assert_parse_and_encoding( "text/html;x=(;charset=gbk", "text/html;x=\"(\";charset=gbk", "GBK", ); // Duplicate parameter assert_parse_and_encoding( "text/html;charset=gbk;charset=windows-1255", "text/html;charset=gbk", "GBK", ); assert_parse_and_encoding( "text/html;charset=();charset=GBK", "text/html;charset=\"()\"", None, ); // Spaces assert_parse_and_encoding("text/html;charset =gbk", "text/html", None); assert_parse_and_encoding("text/html ;charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding("text/html; charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding( "text/html;charset= gbk", "text/html;charset=\" gbk\"", "GBK", ); assert_parse_and_encoding( "text/html;charset= \"gbk\"", "text/html;charset=\" \\\"gbk\\\"\"", None, ); // 0x0B and 0x0C assert_parse_and_encoding("text/html;charset=\u{000B}gbk", "text/html", None); assert_parse_and_encoding("text/html;charset=\u{000C}gbk", "text/html", None); assert_parse_and_encoding("text/html;\u{000B}charset=gbk", "text/html", None); assert_parse_and_encoding("text/html;\u{000C}charset=gbk", "text/html", None); // Single quotes are a token, not a delimiter assert_parse_and_encoding("text/html;charset='gbk'", "text/html;charset='gbk'", None); assert_parse_and_encoding("text/html;charset='gbk", "text/html;charset='gbk", None); assert_parse_and_encoding("text/html;charset=gbk'", "text/html;charset=gbk'", None); assert_parse_and_encoding( "text/html;charset=';charset=GBK", "text/html;charset='", None, ); // Invalid parameters assert_parse_and_encoding("text/html;test;charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding( "text/html;test=;charset=gbk", "text/html;charset=gbk", "GBK", ); assert_parse_and_encoding("text/html;';charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding("text/html;\";charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding("text/html ; ; charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding("text/html;;;;charset=gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding( "text/html;charset= \"\u{007F};charset=GBK", "text/html;charset=GBK", "GBK", ); assert_parse_and_encoding( "text/html;charset=\"\u{007F};charset=foo\";charset=GBK", "text/html;charset=GBK", "GBK", ); // Double quotes" assert_parse_and_encoding("text/html;charset=\"gbk\"", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding("text/html;charset=\"gbk", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding( "text/html;charset=gbk\"", "text/html;charset=\"gbk\\\"\"", None, ); assert_parse_and_encoding( "text/html;charset=\" gbk\"", "text/html;charset=\" gbk\"", "GBK", ); assert_parse_and_encoding( "text/html;charset=\"gbk \"", "text/html;charset=\"gbk \"", "GBK", ); assert_parse_and_encoding( "text/html;charset=\"\\ gbk\"", "text/html;charset=\" gbk\"", "GBK", ); assert_parse_and_encoding( "text/html;charset=\"\\g\\b\\k\"", "text/html;charset=gbk", "GBK", ); assert_parse_and_encoding("text/html;charset=\"gbk\"x", "text/html;charset=gbk", "GBK"); assert_parse_and_encoding( "text/html;charset=\"\";charset=GBK", "text/html;charset=\"\"", None, ); assert_parse_and_encoding( "text/html;charset=\";charset=GBK", "text/html;charset=\";charset=GBK\"", None, ); // Unexpected code points assert_parse_and_encoding( "text/html;charset={gbk}", "text/html;charset=\"{gbk}\"", None, ); // Parameter name longer than 127 assert_parse_and_encoding("text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk", "text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk", "GBK"); // type/subtype longer than 127 assert_parse("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"); // Valid assert_parse("!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); assert_parse("x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{0080}\u{0081}\u{0082}\u{0083}\u{0084}\u{0085}\u{0086}\u{0087}\u{0088}\u{0089}\u{008A}\u{008B}\u{008C}\u{008D}\u{008E}\u{008F}\u{0090}\u{0091}\u{0092}\u{0093}\u{0094}\u{0095}\u{0096}\u{0097}\u{0098}\u{0099}\u{009A}\u{009B}\u{009C}\u{009D}\u{009E}\u{009F}\u{00A0}\u{00A1}\u{00A2}\u{00A3}\u{00A4}\u{00A5}\u{00A6}\u{00A7}\u{00A8}\u{00A9}\u{00AA}\u{00AB}\u{00AC}\u{00AD}\u{00AE}\u{00AF}\u{00B0}\u{00B1}\u{00B2}\u{00B3}\u{00B4}\u{00B5}\u{00B6}\u{00B7}\u{00B8}\u{00B9}\u{00BA}\u{00BB}\u{00BC}\u{00BD}\u{00BE}\u{00BF}\u{00C0}\u{00C1}\u{00C2}\u{00C3}\u{00C4}\u{00C5}\u{00C6}\u{00C7}\u{00C8}\u{00C9}\u{00CA}\u{00CB}\u{00CC}\u{00CD}\u{00CE}\u{00CF}\u{00D0}\u{00D1}\u{00D2}\u{00D3}\u{00D4}\u{00D5}\u{00D6}\u{00D7}\u{00D8}\u{00D9}\u{00DA}\u{00DB}\u{00DC}\u{00DD}\u{00DE}\u{00DF}\u{00E0}\u{00E1}\u{00E2}\u{00E3}\u{00E4}\u{00E5}\u{00E6}\u{00E7}\u{00E8}\u{00E9}\u{00EA}\u{00EB}\u{00EC}\u{00ED}\u{00EE}\u{00EF}\u{00F0}\u{00F1}\u{00F2}\u{00F3}\u{00F4}\u{00F5}\u{00F6}\u{00F7}\u{00F8}\u{00F9}\u{00FA}\u{00FB}\u{00FC}\u{00FD}\u{00FE}\u{00FF}\"", "x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{0080}\u{0081}\u{0082}\u{0083}\u{0084}\u{0085}\u{0086}\u{0087}\u{0088}\u{0089}\u{008A}\u{008B}\u{008C}\u{008D}\u{008E}\u{008F}\u{0090}\u{0091}\u{0092}\u{0093}\u{0094}\u{0095}\u{0096}\u{0097}\u{0098}\u{0099}\u{009A}\u{009B}\u{009C}\u{009D}\u{009E}\u{009F}\u{00A0}\u{00A1}\u{00A2}\u{00A3}\u{00A4}\u{00A5}\u{00A6}\u{00A7}\u{00A8}\u{00A9}\u{00AA}\u{00AB}\u{00AC}\u{00AD}\u{00AE}\u{00AF}\u{00B0}\u{00B1}\u{00B2}\u{00B3}\u{00B4}\u{00B5}\u{00B6}\u{00B7}\u{00B8}\u{00B9}\u{00BA}\u{00BB}\u{00BC}\u{00BD}\u{00BE}\u{00BF}\u{00C0}\u{00C1}\u{00C2}\u{00C3}\u{00C4}\u{00C5}\u{00C6}\u{00C7}\u{00C8}\u{00C9}\u{00CA}\u{00CB}\u{00CC}\u{00CD}\u{00CE}\u{00CF}\u{00D0}\u{00D1}\u{00D2}\u{00D3}\u{00D4}\u{00D5}\u{00D6}\u{00D7}\u{00D8}\u{00D9}\u{00DA}\u{00DB}\u{00DC}\u{00DD}\u{00DE}\u{00DF}\u{00E0}\u{00E1}\u{00E2}\u{00E3}\u{00E4}\u{00E5}\u{00E6}\u{00E7}\u{00E8}\u{00E9}\u{00EA}\u{00EB}\u{00EC}\u{00ED}\u{00EE}\u{00EF}\u{00F0}\u{00F1}\u{00F2}\u{00F3}\u{00F4}\u{00F5}\u{00F6}\u{00F7}\u{00F8}\u{00F9}\u{00FA}\u{00FB}\u{00FC}\u{00FD}\u{00FE}\u{00FF}\""); // End-of-file handling assert_parse("x/x;test", "x/x"); assert_parse("x/x;test=\"\\", "x/x;test=\"\\\\\""); // Whitespace (not handled by generated-mime-types.json or above) assert_parse("x/x;x= ", "x/x"); assert_parse("x/x;x=\t", "x/x"); assert_parse("x/x\n\r\t ;x=x", "x/x;x=x"); assert_parse("\n\r\t x/x;x=x\n\r\t ", "x/x;x=x"); assert_parse("x/x;\n\r\t x=x\n\r\t ;x=y", "x/x;x=x"); // Latin1 assert_parse_and_encoding( "text/html;test=\u{00FF};charset=gbk", "text/html;test=\"\u{00FF}\";charset=gbk", "GBK", ); // >Latin1 assert_parse("x/x;test=\u{FFFD};x=x", "x/x;x=x"); // Failure assert_fails("\u{000B}x/x"); assert_fails("\u{000C}x/x"); assert_fails("x/x\u{000B}"); assert_fails("x/x\u{000C}"); assert_fails(""); assert_fails("\t"); assert_fails("/"); assert_fails("bogus"); assert_fails("bogus/"); assert_fails("bogus/ "); assert_fails("bogus/bogus/;"); assert_fails(""); assert_fails("(/)"); assert_fails("ÿ/ÿ"); assert_fails("text/html(;doesnot=matter"); assert_fails("{/}"); assert_fails("\u{0100}/\u{0100}"); assert_fails("text /html"); assert_fails("text/ html"); assert_fails("\"text/html\""); } http-types-2.12.0/src/other/date.rs000064400000000000000000000067410072674642500152620ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, DATE}; use crate::utils::HttpDate; use std::time::SystemTime; /// The date and time at which the message originated. /// /// # Specifications /// /// - [RFC 7231, section 7.1.1.2: Date](https://tools.ietf.org/html/rfc7231#section-7.1.1.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::other::Date; /// /// use std::time::{Duration, SystemTime}; /// /// let now = SystemTime::now(); /// let date = Date::new(now); /// /// let mut res = Response::new(200); /// date.apply(&mut res); /// /// let date = Date::from_headers(res)?.unwrap(); /// /// // Validate we're within 1 second accurate of the system time. /// assert!(now.duration_since(date.into())? <= Duration::from_secs(1)); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct Date { at: SystemTime, } impl Date { /// Create a new instance. pub fn new(at: SystemTime) -> Self { Self { at } } /// Create a new instance with the date set to now. pub fn now() -> Self { Self { at: SystemTime::now(), } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(DATE) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let value = headers.iter().last().unwrap(); let date: HttpDate = value .as_str() .trim() .parse() .map_err(|mut e: crate::Error| { e.set_status(400); e })?; let at = date.into(); Ok(Some(Self { at })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { DATE } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let date: HttpDate = self.at.into(); let output = format!("{}", date); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl From for SystemTime { fn from(date: Date) -> Self { date.at } } impl From for Date { fn from(time: SystemTime) -> Self { Self { at: time } } } impl PartialEq for Date { fn eq(&self, other: &SystemTime) -> bool { &self.at == other } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; use std::time::Duration; #[test] fn smoke() -> crate::Result<()> { let now = SystemTime::now(); let date = Date::new(now); let mut headers = Headers::new(); date.apply(&mut headers); let date = Date::from_headers(headers)?.unwrap(); // Validate we're within 1 second accurate of the system time. assert!(now.duration_since(date.into())? <= Duration::from_secs(1)); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(DATE, ""); let err = Date::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/other/expect.rs000064400000000000000000000057000072674642500156270ustar 00000000000000use crate::ensure_eq_status; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, EXPECT}; use std::fmt::Debug; use std::option; /// HTTP `Expect` header /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect) /// /// # Specifications /// /// - [RFC 7231, section 5.1.1: Expect](https://tools.ietf.org/html/rfc7231#section-5.1.1) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::other::Expect; /// /// let expect = Expect::new(); /// /// let mut res = Response::new(200); /// expect.apply(&mut res); /// /// let expect = Expect::from_headers(res)?.unwrap(); /// assert_eq!(expect, Expect::new()); /// # /// # Ok(()) } /// ``` #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub struct Expect { _priv: (), } impl Expect { /// Create a new instance of `Expect`. pub fn new() -> Self { Self { _priv: () } } /// Create an instance of `Expect` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(EXPECT) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header = headers.iter().last().unwrap(); ensure_eq_status!(header, "100-continue", 400, "malformed `Expect` header"); Ok(Some(Self { _priv: () })) } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(EXPECT, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { EXPECT } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let value = "100-continue"; // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(value.into()) } } } impl ToHeaderValues for Expect { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let expect = Expect::new(); let mut headers = Headers::new(); expect.apply(&mut headers); let expect = Expect::from_headers(headers)?.unwrap(); assert_eq!(expect, Expect::new()); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(EXPECT, ""); let err = Expect::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/other/mod.rs000064400000000000000000000003540072674642500151160ustar 00000000000000//! Miscellaneous HTTP headers. mod date; mod expect; mod referer; mod retry_after; mod source_map; pub use date::Date; pub use expect::Expect; pub use referer::Referer; pub use retry_after::RetryAfter; pub use source_map::SourceMap; http-types-2.12.0/src/other/referer.rs000064400000000000000000000111600072674642500157660ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, REFERER}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; /// Contains the address of the page making the request. /// /// __Important__: Although this header has many innocent uses it can have /// undesirable consequences for user security and privacy. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) /// /// # Specifications /// /// - [RFC 7231, section 5.5.2: Referer](https://tools.ietf.org/html/rfc7231#section-5.5.2) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::{Response, Url}; /// use http_types::other::Referer; /// /// let referer = Referer::new(Url::parse("https://example.net/")?); /// /// let mut res = Response::new(200); /// referer.apply(&mut res); /// /// let base_url = Url::parse("https://example.net/")?; /// let referer = Referer::from_headers(base_url, res)?.unwrap(); /// assert_eq!(referer.location(), &Url::parse("https://example.net/")?); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct Referer { location: Url, } impl Referer { /// Create a new instance of `Referer` header. pub fn new(location: Url) -> Self { Self { location } } /// Create a new instance from headers. pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> where U: TryInto, U::Error: std::fmt::Debug, { let headers = match headers.as_ref().get(REFERER) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header_value = headers.iter().last().unwrap(); let url = match Url::parse(header_value.as_str()) { Ok(url) => url, Err(_) => match base_url.try_into() { Ok(base_url) => base_url.join(header_value.as_str().trim()).status(500)?, Err(_) => bail!(500, "Invalid base url provided"), }, }; Ok(Some(Self { location: url })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { REFERER } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = self.location.to_string(); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Get the url. pub fn location(&self) -> &Url { &self.location } /// Set the url. pub fn set_location(&mut self, location: U) -> Result<(), U::Error> where U: TryInto, U::Error: std::fmt::Debug, { self.location = location.try_into()?; Ok(()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let referer = Referer::new(Url::parse("https://example.net/test.json")?); let mut headers = Headers::new(); referer.apply(&mut headers); let base_url = Url::parse("https://example.net/")?; let referer = Referer::from_headers(base_url, headers)?.unwrap(); assert_eq!( referer.location(), &Url::parse("https://example.net/test.json")? ); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(REFERER, "htt://"); let err = Referer::from_headers(Url::parse("https://example.net").unwrap(), headers).unwrap_err(); assert_eq!(err.status(), 500); } #[test] fn fallback_works() -> crate::Result<()> { let mut headers = Headers::new(); headers.insert(REFERER, "/test.json"); let base_url = Url::parse("https://fallback.net/")?; let referer = Referer::from_headers(base_url, headers)?.unwrap(); assert_eq!( referer.location(), &Url::parse("https://fallback.net/test.json")? ); let mut headers = Headers::new(); headers.insert(REFERER, "https://example.com/test.json"); let base_url = Url::parse("https://fallback.net/")?; let referer = Referer::from_headers(base_url, headers)?.unwrap(); assert_eq!( referer.location(), &Url::parse("https://example.com/test.json")? ); Ok(()) } } http-types-2.12.0/src/other/retry_after.rs000064400000000000000000000123320072674642500166640ustar 00000000000000use std::time::{Duration, SystemTime, SystemTimeError}; use crate::headers::{HeaderName, HeaderValue, Headers, RETRY_AFTER}; use crate::utils::{fmt_http_date, parse_http_date}; /// Indicate how long the user agent should wait before making a follow-up request. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) /// /// # Specifications /// /// - [RFC 7231, section 3.1.4.2: Retry-After](https://tools.ietf.org/html/rfc7231#section-3.1.4.2) /// /// # Examples /// /// ```no_run /// # fn main() -> http_types::Result<()> { /// # /// use http_types::other::RetryAfter; /// use http_types::Response; /// use std::time::{SystemTime, Duration}; /// use async_std::task; /// /// let retry = RetryAfter::new(Duration::from_secs(10)); /// /// let mut headers = Response::new(429); /// retry.apply(&mut headers); /// /// // Sleep for the duration, then try the task again. /// let retry = RetryAfter::from_headers(headers)?.unwrap(); /// task::sleep(retry.duration_since(SystemTime::now())?); /// # /// # Ok(()) } /// ``` #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct RetryAfter { inner: RetryDirective, } #[allow(clippy::len_without_is_empty)] impl RetryAfter { /// Create a new instance from a `Duration`. /// /// This value will be encoded over the wire as a relative offset in seconds. pub fn new(dur: Duration) -> Self { Self { inner: RetryDirective::Duration(dur), } } /// Create a new instance from a `SystemTime` instant. /// /// This value will be encoded a specific `Date` over the wire. pub fn new_at(at: SystemTime) -> Self { Self { inner: RetryDirective::SystemTime(at), } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let header = match headers.as_ref().get(RETRY_AFTER) { Some(headers) => headers.last(), None => return Ok(None), }; let inner = match header.as_str().parse::() { Ok(dur) => RetryDirective::Duration(Duration::from_secs(dur)), Err(_) => { let at = parse_http_date(header.as_str())?; RetryDirective::SystemTime(at) } }; Ok(Some(Self { inner })) } /// Returns the amount of time elapsed from an earlier point in time. /// /// # Errors /// /// This may return an error if the `earlier` time was after the current time. pub fn duration_since(&self, earlier: SystemTime) -> Result { let at = match self.inner { RetryDirective::Duration(dur) => SystemTime::now() + dur, RetryDirective::SystemTime(at) => at, }; at.duration_since(earlier) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { RETRY_AFTER } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = match self.inner { RetryDirective::Duration(dur) => format!("{}", dur.as_secs()), RetryDirective::SystemTime(at) => fmt_http_date(at), }; // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } } impl From for SystemTime { fn from(retry_after: RetryAfter) -> Self { match retry_after.inner { RetryDirective::Duration(dur) => SystemTime::now() + dur, RetryDirective::SystemTime(at) => at, } } } /// What value are we decoding into? /// /// This value is intionally never exposes; all end-users want is a `Duration` /// value that tells them how long to wait for before trying again. #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] enum RetryDirective { Duration(Duration), SystemTime(SystemTime), } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let retry = RetryAfter::new(Duration::from_secs(10)); let mut headers = Headers::new(); retry.apply(&mut headers); // `SystemTime::now` uses sub-second precision which means there's some // offset that's not encoded. let now = SystemTime::now(); let retry = RetryAfter::from_headers(headers)?.unwrap(); assert_eq!( retry.duration_since(now)?.as_secs(), Duration::from_secs(10).as_secs() ); Ok(()) } #[test] fn new_at() -> crate::Result<()> { let now = SystemTime::now(); let retry = RetryAfter::new_at(now + Duration::from_secs(10)); let mut headers = Headers::new(); retry.apply(&mut headers); // `SystemTime::now` uses sub-second precision which means there's some // offset that's not encoded. let retry = RetryAfter::from_headers(headers)?.unwrap(); let delta = retry.duration_since(now)?; assert!(delta >= Duration::from_secs(9)); assert!(delta <= Duration::from_secs(10)); Ok(()) } } http-types-2.12.0/src/other/source_map.rs000064400000000000000000000110510072674642500164700ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, SOURCE_MAP}; use crate::{bail_status as bail, Status, Url}; use std::convert::TryInto; /// Links to a file that maps transformed source to the original source. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/SourceMap) /// /// # Specifications /// /// - [Source Map Revision 3](https://sourcemaps.info/spec.html) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::{Response, Url}; /// use http_types::other::SourceMap; /// /// let source_map = SourceMap::new(Url::parse("https://example.net/")?); /// /// let mut res = Response::new(200); /// source_map.apply(&mut res); /// /// let base_url = Url::parse("https://example.net/")?; /// let source_map = SourceMap::from_headers(base_url, res)?.unwrap(); /// assert_eq!(source_map.location(), &Url::parse("https://example.net/")?); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct SourceMap { location: Url, } impl SourceMap { /// Create a new instance of `SourceMap` header. pub fn new(location: Url) -> Self { Self { location } } /// Create a new instance from headers. pub fn from_headers(base_url: U, headers: impl AsRef) -> crate::Result> where U: TryInto, U::Error: std::fmt::Debug, { let headers = match headers.as_ref().get(SOURCE_MAP) { Some(headers) => headers, None => return Ok(None), }; // If we successfully parsed the header then there's always at least one // entry. We want the last entry. let header_value = headers.iter().last().unwrap(); let url = match Url::parse(header_value.as_str()) { Ok(url) => url, Err(_) => match base_url.try_into() { Ok(base_url) => base_url.join(header_value.as_str().trim()).status(500)?, Err(_) => bail!(500, "Invalid base url provided"), }, }; Ok(Some(Self { location: url })) } /// Sets the header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(self.name(), self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { SOURCE_MAP } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = self.location.to_string(); // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Get the url. pub fn location(&self) -> &Url { &self.location } /// Set the url. pub fn set_location(&mut self, location: U) -> Result<(), U::Error> where U: TryInto, U::Error: std::fmt::Debug, { self.location = location.try_into()?; Ok(()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let source_map = SourceMap::new(Url::parse("https://example.net/test.json")?); let mut headers = Headers::new(); source_map.apply(&mut headers); let base_url = Url::parse("https://example.net/")?; let source_map = SourceMap::from_headers(base_url, headers)?.unwrap(); assert_eq!( source_map.location(), &Url::parse("https://example.net/test.json")? ); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(SOURCE_MAP, "htt://"); let err = SourceMap::from_headers(Url::parse("https://example.net").unwrap(), headers) .unwrap_err(); assert_eq!(err.status(), 500); } #[test] fn fallback_works() -> crate::Result<()> { let mut headers = Headers::new(); headers.insert(SOURCE_MAP, "/test.json"); let base_url = Url::parse("https://fallback.net/")?; let source_map = SourceMap::from_headers(base_url, headers)?.unwrap(); assert_eq!( source_map.location(), &Url::parse("https://fallback.net/test.json")? ); let mut headers = Headers::new(); headers.insert(SOURCE_MAP, "https://example.com/test.json"); let base_url = Url::parse("https://fallback.net/")?; let source_map = SourceMap::from_headers(base_url, headers)?.unwrap(); assert_eq!( source_map.location(), &Url::parse("https://example.com/test.json")? ); Ok(()) } } http-types-2.12.0/src/parse_utils.rs000064400000000000000000000120010072674642500155400ustar 00000000000000use std::borrow::Cow; /// https://tools.ietf.org/html/rfc7230#section-3.2.6 pub(crate) fn parse_token(input: &str) -> (Option<&str>, &str) { let mut end_of_token = 0; for (i, c) in input.char_indices() { if tchar(c) { end_of_token = i + 1; } else { break; } } if end_of_token == 0 { (None, input) } else { (Some(&input[..end_of_token]), &input[end_of_token..]) } } /// https://tools.ietf.org/html/rfc7230#section-3.2.6 fn tchar(c: char) -> bool { matches!( c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '!' | '#' | '$' | '%' | '&' | '\'' | '*' | '+' | '-' | '.' | '^' | '_' | '`' | '|' | '~' ) } /// https://tools.ietf.org/html/rfc7230#section-3.2.6 fn vchar(c: char) -> bool { matches!(c as u8, b'\t' | 32..=126 | 128..=255) } /// https://tools.ietf.org/html/rfc7230#section-3.2.6 pub(crate) fn parse_quoted_string(input: &str) -> (Option>, &str) { // quoted-string must start with a DQUOTE if !input.starts_with('"') { return (None, input); } let mut end_of_string = None; let mut backslashes: Vec = vec![]; for (i, c) in input.char_indices().skip(1) { if i > 1 && backslashes.last() == Some(&(i - 2)) { if !vchar(c) { // only VCHARs can be escaped return (None, input); } // otherwise, we skip over this character while parsing } else { match c as u8 { // we have reached a quoted-pair b'\\' => { backslashes.push(i - 1); } // end of the string, DQUOTE b'"' => { end_of_string = Some(i + 1); break; } // qdtext b'\t' | b' ' | 15 | 35..=91 | 93..=126 | 128..=255 => {} // unexpected character, bail _ => return (None, input), } } } if let Some(end_of_string) = end_of_string { let value = &input[1..end_of_string - 1]; // strip DQUOTEs from start and end let value = if backslashes.is_empty() { // no backslashes means we don't need to allocate value.into() } else { backslashes.reverse(); // so that we can use pop. goes from low-to-high to high-to-low sorting value .char_indices() .filter_map(|(i, c)| { if Some(&i) == backslashes.last() { // they're already sorted highest to lowest, so we only need to check the last one backslashes.pop(); None // remove the backslash from the output } else { Some(c) } }) .collect::() .into() }; (Some(value), &input[end_of_string..]) } else { // we never reached a closing DQUOTE, so we do not have a valid quoted-string (None, input) } } #[cfg(test)] mod test { use super::*; #[test] fn token_successful_parses() { assert_eq!(parse_token("key=value"), (Some("key"), "=value")); assert_eq!(parse_token("KEY=value"), (Some("KEY"), "=value")); assert_eq!(parse_token("0123)=value"), (Some("0123"), ")=value")); assert_eq!(parse_token("a=b"), (Some("a"), "=b")); assert_eq!( parse_token("!#$%&'*+-.^_`|~=value"), (Some("!#$%&'*+-.^_`|~"), "=value",) ); } #[test] fn token_unsuccessful_parses() { assert_eq!(parse_token(""), (None, "")); assert_eq!(parse_token("=value"), (None, "=value")); for c in r#"(),/:;<=>?@[\]{}"#.chars() { let s = c.to_string(); assert_eq!(parse_token(&s), (None, &*s)); let s = format!("match{}rest", s); assert_eq!(parse_token(&s), (Some("match"), &*format!("{}rest", c))); } } #[test] fn qstring_successful_parses() { assert_eq!( parse_quoted_string(r#""key"=value"#), (Some(Cow::Borrowed("key")), "=value") ); assert_eq!( parse_quoted_string(r#""escaped \" quote \""rest"#), ( Some(Cow::Owned(String::from(r#"escaped " quote ""#))), r#"rest"# ) ); } #[test] fn qstring_unsuccessful_parses() { assert_eq!(parse_quoted_string(r#""abc"#), (None, "\"abc")); assert_eq!(parse_quoted_string(r#"hello""#), (None, "hello\"",)); assert_eq!(parse_quoted_string(r#"=value\"#), (None, "=value\\")); assert_eq!(parse_quoted_string(r#"\""#), (None, r#"\""#)); assert_eq!(parse_quoted_string(r#""\""#), (None, r#""\""#)); } } http-types-2.12.0/src/proxies/forwarded.rs000064400000000000000000000557220072674642500166750ustar 00000000000000use crate::{ headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, FORWARDED}, parse_utils::{parse_quoted_string, parse_token}, }; use std::{borrow::Cow, convert::TryFrom, fmt::Write, net::IpAddr}; // these constants are private because they are nonstandard const X_FORWARDED_FOR: HeaderName = HeaderName::from_lowercase_str("x-forwarded-for"); const X_FORWARDED_PROTO: HeaderName = HeaderName::from_lowercase_str("x-forwarded-proto"); const X_FORWARDED_BY: HeaderName = HeaderName::from_lowercase_str("x-forwarded-by"); /// A rust representation of the [forwarded /// header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded). #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Forwarded<'a> { by: Option>, forwarded_for: Vec>, host: Option>, proto: Option>, } impl<'a> Forwarded<'a> { /// Attempts to parse a Forwarded from headers (or a request or /// response). Builds a borrowed Forwarded by default. To build an /// owned Forwarded, use /// `Forwarded::from_headers(...).into_owned()` /// /// # X-Forwarded-For, -By, and -Proto compatability /// /// This implementation includes fall-back support for the /// historical unstandardized headers x-forwarded-for, /// x-forwarded-by, and x-forwarded-proto. If you do not wish to /// support these headers, use /// [`Forwarded::from_forwarded_header`]. To _only_ support these /// historical headers and _not_ the standardized Forwarded /// header, use [`Forwarded::from_x_headers`]. /// /// Please note that either way, this implementation will /// normalize to the standardized Forwarded header, as recommended /// in /// [rfc7239§7.4](https://tools.ietf.org/html/rfc7239#section-7.4) /// /// # Examples /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let mut request = Request::new(Get, Url::parse("http://_/")?); /// request.insert_header( /// "Forwarded", /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// let forwarded = Forwarded::from_headers(&request)?.unwrap(); /// assert_eq!(forwarded.proto(), Some("https")); /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]", "unknown"]); /// # Ok(()) } /// ``` /// /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let mut request = Request::new(Get, Url::parse("http://_/")?); /// request.insert_header("X-Forwarded-For", "192.0.2.43, 2001:db8:cafe::17, unknown"); /// request.insert_header("X-Forwarded-Proto", "https"); /// let forwarded = Forwarded::from_headers(&request)?.unwrap(); /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]", "unknown"]); /// assert_eq!(forwarded.proto(), Some("https")); /// assert_eq!( /// forwarded.value()?, /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// # Ok(()) } /// ``` pub fn from_headers(headers: &'a impl AsRef) -> Result, ParseError> { if let Some(forwarded) = Self::from_forwarded_header(headers)? { Ok(Some(forwarded)) } else { Self::from_x_headers(headers) } } /// Parse a borrowed Forwarded from the Forwarded header, without x-forwarded-{for,by,proto} fallback /// /// # Examples /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let mut request = Request::new(Get, Url::parse("http://_/")?); /// request.insert_header( /// "Forwarded", /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// let forwarded = Forwarded::from_forwarded_header(&request)?.unwrap(); /// assert_eq!(forwarded.proto(), Some("https")); /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]", "unknown"]); /// # Ok(()) } /// ``` /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let mut request = Request::new(Get, Url::parse("http://_/")?); /// request.insert_header("X-Forwarded-For", "192.0.2.43, 2001:db8:cafe::17"); /// assert!(Forwarded::from_forwarded_header(&request)?.is_none()); /// # Ok(()) } /// ``` pub fn from_forwarded_header( headers: &'a impl AsRef, ) -> Result, ParseError> { if let Some(headers) = headers.as_ref().get(FORWARDED) { Ok(Some(Self::parse(headers.as_ref().as_str())?)) } else { Ok(None) } } /// Parse a borrowed Forwarded from the historical /// non-standardized x-forwarded-{for,by,proto} headers, without /// support for the Forwarded header. /// /// # Examples /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let mut request = Request::new(Get, Url::parse("http://_/")?); /// request.insert_header("X-Forwarded-For", "192.0.2.43, 2001:db8:cafe::17"); /// let forwarded = Forwarded::from_headers(&request)?.unwrap(); /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]"]); /// # Ok(()) } /// ``` /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let mut request = Request::new(Get, Url::parse("http://_/")?); /// request.insert_header( /// "Forwarded", /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// assert!(Forwarded::from_x_headers(&request)?.is_none()); /// # Ok(()) } /// ``` pub fn from_x_headers(headers: &'a impl AsRef) -> Result, ParseError> { let headers = headers.as_ref(); let forwarded_for: Vec> = headers .get(X_FORWARDED_FOR) .map(|hv| { hv.as_str() .split(',') .map(|v| { let v = v.trim(); match v.parse::().ok() { Some(IpAddr::V6(v6)) => Cow::Owned(format!(r#"[{}]"#, v6)), _ => Cow::Borrowed(v), } }) .collect() }) .unwrap_or_default(); let by = headers .get(X_FORWARDED_BY) .map(|hv| Cow::Borrowed(hv.as_str())); let proto = headers .get(X_FORWARDED_PROTO) .map(|p| Cow::Borrowed(p.as_str())); if !forwarded_for.is_empty() || by.is_some() || proto.is_some() { Ok(Some(Self { forwarded_for, by, proto, host: None, })) } else { Ok(None) } } /// parse a &str into a borrowed Forwarded /// /// # Examples /// ```rust /// # use http_types::{proxies::Forwarded, Method::Get, Request, Url, Result}; /// # fn main() -> Result<()> { /// let forwarded = Forwarded::parse( /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", FOR=unknown;proto=https"# /// )?; /// assert_eq!(forwarded.forwarded_for(), vec!["192.0.2.43", "[2001:db8:cafe::17]", "unknown"]); /// assert_eq!( /// forwarded.value()?, /// r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown;proto=https"# /// ); /// # Ok(()) } /// ``` pub fn parse(input: &'a str) -> Result { let mut input = input; let mut forwarded = Forwarded::new(); while !input.is_empty() { input = if starts_with_ignore_case("for=", input) { forwarded.parse_for(input)? } else { forwarded.parse_forwarded_pair(input)? } } Ok(forwarded) } fn parse_forwarded_pair(&mut self, input: &'a str) -> Result<&'a str, ParseError> { let (key, value, rest) = match parse_token(input) { (Some(key), rest) if rest.starts_with('=') => match parse_value(&rest[1..]) { (Some(value), rest) => Some((key, value, rest)), (None, _) => None, }, _ => None, } .ok_or_else(|| ParseError::new("parse error in forwarded-pair"))?; match key { "by" => { if self.by.is_some() { return Err(ParseError::new("parse error, duplicate `by` key")); } self.by = Some(value); } "host" => { if self.host.is_some() { return Err(ParseError::new("parse error, duplicate `host` key")); } self.host = Some(value); } "proto" => { if self.proto.is_some() { return Err(ParseError::new("parse error, duplicate `proto` key")); } self.proto = Some(value); } _ => { /* extensions are allowed in the spec */ } } match rest.strip_prefix(';') { Some(rest) => Ok(rest), None => Ok(rest), } } fn parse_for(&mut self, input: &'a str) -> Result<&'a str, ParseError> { let mut rest = input; loop { rest = match match_ignore_case("for=", rest) { (true, rest) => rest, (false, _) => return Err(ParseError::new("http list must start with for=")), }; let (value, rest_) = parse_value(rest); rest = rest_; if let Some(value) = value { // add a successful for= value self.forwarded_for.push(value); } else { return Err(ParseError::new("for= without valid value")); } match rest.chars().next() { // we have another for= Some(',') => { rest = rest[1..].trim_start(); } // we have reached the end of the for= section Some(';') => return Ok(&rest[1..]), // reached the end of the input None => return Ok(rest), // bail _ => return Err(ParseError::new("unexpected character after for= section")), } } } /// Transform a borrowed Forwarded into an owned /// Forwarded. This is a noop if the Forwarded is already owned. pub fn into_owned(self) -> Forwarded<'static> { Forwarded { by: self.by.map(|by| Cow::Owned(by.into_owned())), forwarded_for: self .forwarded_for .into_iter() .map(|ff| Cow::Owned(ff.into_owned())) .collect(), host: self.host.map(|h| Cow::Owned(h.into_owned())), proto: self.proto.map(|p| Cow::Owned(p.into_owned())), } } /// Insert a header that represents this Forwarded. /// /// # Example /// /// ```rust /// let mut response = http_types::Response::new(200); /// let mut forwarded = http_types::proxies::Forwarded::new(); /// forwarded.add_for("192.0.2.43"); /// forwarded.add_for("[2001:db8:cafe::17]"); /// forwarded.set_proto("https"); /// forwarded.apply(&mut response); /// assert_eq!(response["Forwarded"], r#"for=192.0.2.43, for="[2001:db8:cafe::17]";proto=https"#); /// ``` pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(FORWARDED, self); } /// Builds a Forwarded header as a String. /// /// # Example /// /// ```rust /// # fn main() -> http_types::Result<()> { /// let mut forwarded = http_types::proxies::Forwarded::new(); /// forwarded.add_for("_haproxy"); /// forwarded.add_for("[2001:db8:cafe::17]"); /// forwarded.set_proto("https"); /// assert_eq!(forwarded.value()?, r#"for=_haproxy, for="[2001:db8:cafe::17]";proto=https"#); /// # Ok(()) } /// ``` pub fn value(&self) -> Result { let mut buf = String::new(); if let Some(by) = self.by() { write!(&mut buf, "by={};", by)?; } buf.push_str( &self .forwarded_for .iter() .map(|f| format!("for={}", format_value(f))) .collect::>() .join(", "), ); buf.push(';'); if let Some(host) = self.host() { write!(&mut buf, "host={};", host)?; } if let Some(proto) = self.proto() { write!(&mut buf, "proto={};", proto)?; } // remove a trailing semicolon buf.pop(); Ok(buf) } /// Builds a new empty Forwarded pub fn new() -> Self { Self::default() } /// Adds a `for` section to this header pub fn add_for(&mut self, forwarded_for: impl Into>) { self.forwarded_for.push(forwarded_for.into()); } /// Returns the `for` field of this header pub fn forwarded_for(&self) -> Vec<&str> { self.forwarded_for.iter().map(|x| x.as_ref()).collect() } /// Sets the `host` field of this header pub fn set_host(&mut self, host: impl Into>) { self.host = Some(host.into()); } /// Returns the `host` field of this header pub fn host(&self) -> Option<&str> { self.host.as_deref() } /// Sets the `proto` field of this header pub fn set_proto(&mut self, proto: impl Into>) { self.proto = Some(proto.into()) } /// Returns the `proto` field of this header pub fn proto(&self) -> Option<&str> { self.proto.as_deref() } /// Sets the `by` field of this header pub fn set_by(&mut self, by: impl Into>) { self.by = Some(by.into()); } /// Returns the `by` field of this header pub fn by(&self) -> Option<&str> { self.by.as_deref() } } fn parse_value(input: &str) -> (Option>, &str) { match parse_token(input) { (Some(token), rest) => (Some(Cow::Borrowed(token)), rest), (None, rest) => parse_quoted_string(rest), } } fn format_value(input: &str) -> Cow<'_, str> { match parse_token(input) { (_, "") => input.into(), _ => { let mut string = String::from("\""); for ch in input.chars() { if let '\\' | '"' = ch { string.push('\\'); } string.push(ch); } string.push('"'); string.into() } } } fn match_ignore_case<'a>(start: &'static str, input: &'a str) -> (bool, &'a str) { let len = start.len(); if input[..len].eq_ignore_ascii_case(start) { (true, &input[len..]) } else { (false, input) } } fn starts_with_ignore_case(start: &'static str, input: &str) -> bool { if start.len() <= input.len() { let len = start.len(); input[..len].eq_ignore_ascii_case(start) } else { false } } impl std::fmt::Display for Forwarded<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.value()?) } } impl ToHeaderValues for Forwarded<'_> { type Iter = std::option::IntoIter; fn to_header_values(&self) -> crate::Result { self.value()?.to_header_values() } } impl ToHeaderValues for &Forwarded<'_> { type Iter = std::option::IntoIter; fn to_header_values(&self) -> crate::Result { self.value()?.to_header_values() } } #[derive(Debug, Clone)] pub struct ParseError(&'static str); impl ParseError { pub fn new(msg: &'static str) -> Self { Self(msg) } } impl std::error::Error for ParseError {} impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "unable to parse forwarded header: {}", self.0) } } impl<'a> TryFrom<&'a str> for Forwarded<'a> { type Error = ParseError; fn try_from(value: &'a str) -> Result { Self::parse(value) } } #[cfg(test)] mod tests { use super::*; use crate::{Method::Get, Request, Response, Result}; use url::Url; #[test] fn starts_with_ignore_case_can_handle_short_inputs() { assert!(!starts_with_ignore_case("helloooooo", "h")); } #[test] fn parsing_for() -> Result<()> { assert_eq!( Forwarded::parse(r#"for="_gazonk""#)?.forwarded_for(), vec!["_gazonk"] ); assert_eq!( Forwarded::parse(r#"For="[2001:db8:cafe::17]:4711""#)?.forwarded_for(), vec!["[2001:db8:cafe::17]:4711"] ); assert_eq!( Forwarded::parse("for=192.0.2.60;proto=http;by=203.0.113.43")?.forwarded_for(), vec!["192.0.2.60"] ); assert_eq!( Forwarded::parse("for=192.0.2.43, for=198.51.100.17")?.forwarded_for(), vec!["192.0.2.43", "198.51.100.17"] ); assert_eq!( Forwarded::parse(r#"for=192.0.2.43,for="[2001:db8:cafe::17]",for=unknown"#)? .forwarded_for(), Forwarded::parse(r#"for=192.0.2.43, for="[2001:db8:cafe::17]", for=unknown"#)? .forwarded_for() ); assert_eq!( Forwarded::parse( r#"for=192.0.2.43,for="this is a valid quoted-string, \" \\",for=unknown"# )? .forwarded_for(), vec![ "192.0.2.43", r#"this is a valid quoted-string, " \"#, "unknown" ] ); Ok(()) } #[test] fn basic_parse() -> Result<()> { let forwarded = Forwarded::parse("for=client.com;by=proxy.com;host=host.com;proto=https")?; assert_eq!(forwarded.by(), Some("proxy.com")); assert_eq!(forwarded.forwarded_for(), vec!["client.com"]); assert_eq!(forwarded.host(), Some("host.com")); assert_eq!(forwarded.proto(), Some("https")); assert!(matches!(forwarded, Forwarded { .. })); Ok(()) } #[test] fn bad_parse() { let err = Forwarded::parse("by=proxy.com;for=client;host=example.com;host").unwrap_err(); assert_eq!( err.to_string(), "unable to parse forwarded header: parse error in forwarded-pair" ); let err = Forwarded::parse("by;for;host;proto").unwrap_err(); assert_eq!( err.to_string(), "unable to parse forwarded header: parse error in forwarded-pair" ); let err = Forwarded::parse("for=for, key=value").unwrap_err(); assert_eq!( err.to_string(), "unable to parse forwarded header: http list must start with for=" ); let err = Forwarded::parse(r#"for="unterminated string"#).unwrap_err(); assert_eq!( err.to_string(), "unable to parse forwarded header: for= without valid value" ); let err = Forwarded::parse(r#"for=, for=;"#).unwrap_err(); assert_eq!( err.to_string(), "unable to parse forwarded header: for= without valid value" ); } #[test] fn bad_parse_from_headers() -> Result<()> { let mut response = Response::new(200); response.append_header("forwarded", "uh oh"); assert_eq!( Forwarded::from_headers(&response).unwrap_err().to_string(), "unable to parse forwarded header: parse error in forwarded-pair" ); let response = Response::new(200); assert!(Forwarded::from_headers(&response)?.is_none()); Ok(()) } #[test] fn from_x_headers() -> Result<()> { let mut request = Request::new(Get, Url::parse("http://_/")?); request.append_header(X_FORWARDED_FOR, "192.0.2.43, 2001:db8:cafe::17"); request.append_header(X_FORWARDED_PROTO, "gopher"); let forwarded = Forwarded::from_headers(&request)?.unwrap(); assert_eq!( forwarded.to_string(), r#"for=192.0.2.43, for="[2001:db8:cafe::17]";proto=gopher"# ); Ok(()) } #[test] fn formatting_edge_cases() { let mut forwarded = Forwarded::new(); forwarded.add_for(r#"quote: " backslash: \"#); forwarded.add_for(";proto=https"); assert_eq!( forwarded.to_string(), r#"for="quote: \" backslash: \\", for=";proto=https""# ); } #[test] fn parse_edge_cases() -> Result<()> { let forwarded = Forwarded::parse(r#"for=";", for=",", for="\"", for=unquoted;by=";proto=https""#)?; assert_eq!(forwarded.forwarded_for(), vec![";", ",", "\"", "unquoted"]); assert_eq!(forwarded.by(), Some(";proto=https")); assert!(forwarded.proto().is_none()); let forwarded = Forwarded::parse("proto=https")?; assert_eq!(forwarded.proto(), Some("https")); Ok(()) } #[test] fn owned_parse() -> Result<()> { let forwarded = Forwarded::parse("for=client;by=proxy.com;host=example.com;proto=https")?.into_owned(); assert_eq!(forwarded.by(), Some("proxy.com")); assert_eq!(forwarded.forwarded_for(), vec!["client"]); assert_eq!(forwarded.host(), Some("example.com")); assert_eq!(forwarded.proto(), Some("https")); assert!(matches!(forwarded, Forwarded { .. })); Ok(()) } #[test] fn from_request() -> Result<()> { let mut request = Request::new(Get, Url::parse("http://_/")?); request.append_header("Forwarded", "for=for"); let forwarded = Forwarded::from_headers(&request)?.unwrap(); assert_eq!(forwarded.forwarded_for(), vec!["for"]); Ok(()) } #[test] fn owned_can_outlive_request() -> Result<()> { let forwarded = { let mut request = Request::new(Get, Url::parse("http://_/")?); request.append_header("Forwarded", "for=for;by=by;host=host;proto=proto"); Forwarded::from_headers(&request)?.unwrap().into_owned() }; assert_eq!(forwarded.by(), Some("by")); Ok(()) } #[test] fn round_trip() -> Result<()> { let inputs = [ "for=client,for=b,for=c;by=proxy.com;host=example.com;proto=https", "by=proxy.com;proto=https;host=example.com;for=a,for=b", ]; for input in inputs { let forwarded = Forwarded::parse(input).map_err(|_| crate::Error::new_adhoc(input))?; let header = forwarded.to_header_values()?.next().unwrap(); let parsed = Forwarded::parse(header.as_str())?; assert_eq!(forwarded, parsed); } Ok(()) } } http-types-2.12.0/src/proxies/mod.rs000064400000000000000000000001210072674642500154560ustar 00000000000000//! Headers that are set by proxies mod forwarded; pub use forwarded::Forwarded; http-types-2.12.0/src/request.rs000064400000000000000000001073370072674642500147170ustar 00000000000000use futures_lite::{io, prelude::*}; use std::convert::{Into, TryInto}; use std::mem; use std::ops::Index; use std::pin::Pin; use std::task::{Context, Poll}; use crate::convert::{Deserialize, DeserializeOwned, Serialize}; use crate::headers::{ self, HeaderName, HeaderValue, HeaderValues, Headers, Names, ToHeaderValues, Values, CONTENT_TYPE, }; use crate::mime::Mime; use crate::trailers::{self, Trailers}; use crate::{Body, Extensions, Method, StatusCode, Url, Version}; pin_project_lite::pin_project! { /// An HTTP request. /// /// # Examples /// /// ``` /// use http_types::Request; /// /// let mut req = Request::get("https://example.com"); /// req.set_body("Hello, Nori!"); /// ``` #[derive(Debug)] pub struct Request { method: Method, url: Url, headers: Headers, version: Option, #[pin] body: Body, local_addr: Option, peer_addr: Option, ext: Extensions, trailers_sender: Option>, trailers_receiver: Option>, has_trailers: bool, } } impl Request { /// Create a new request. pub fn new(method: Method, url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { let url = url.try_into().expect("Could not convert into a valid url"); let (trailers_sender, trailers_receiver) = async_channel::bounded(1); Self { method, url, headers: Headers::new(), version: None, body: Body::empty(), ext: Extensions::new(), peer_addr: None, local_addr: None, trailers_receiver: Some(trailers_receiver), trailers_sender: Some(trailers_sender), has_trailers: false, } } /// Sets a string representation of the peer address of this /// request. This might take the form of an ip/fqdn and port or a /// local socket address. pub fn set_peer_addr(&mut self, peer_addr: Option) { self.peer_addr = peer_addr.map(|addr| addr.to_string()); } /// Sets a string representation of the local address that this /// request was received on. This might take the form of an ip/fqdn and /// port, or a local socket address. pub fn set_local_addr(&mut self, local_addr: Option) { self.local_addr = local_addr.map(|addr| addr.to_string()); } /// Get the peer socket address for the underlying transport, if /// that information is available for this request. pub fn peer_addr(&self) -> Option<&str> { self.peer_addr.as_deref() } /// Get the local socket address for the underlying transport, if /// that information is available for this request. pub fn local_addr(&self) -> Option<&str> { self.local_addr.as_deref() } /// Get the remote address for this request. /// /// This is determined in the following priority: /// 1. `Forwarded` header `for` key /// 2. The first `X-Forwarded-For` header /// 3. Peer address of the transport pub fn remote(&self) -> Option<&str> { self.forwarded_for().or_else(|| self.peer_addr()) } /// Get the destination host for this request. /// /// This is determined in the following priority: /// 1. `Forwarded` header `host` key /// 2. The first `X-Forwarded-Host` header /// 3. `Host` header /// 4. URL domain, if any pub fn host(&self) -> Option<&str> { self.forwarded_header_part("host") .or_else(|| { self.header("X-Forwarded-Host") .and_then(|h| h.as_str().split(',').next()) }) .or_else(|| self.header(&headers::HOST).map(|h| h.as_str())) .or_else(|| self.url().host_str()) } fn forwarded_header_part(&self, part: &str) -> Option<&str> { self.header("Forwarded").and_then(|header| { header.as_str().split(';').find_map(|key_equals_value| { let parts = key_equals_value.split('=').collect::>(); if parts.len() == 2 && parts[0].eq_ignore_ascii_case(part) { Some(parts[1]) } else { None } }) }) } fn forwarded_for(&self) -> Option<&str> { self.forwarded_header_part("for").or_else(|| { self.header("X-Forwarded-For") .and_then(|header| header.as_str().split(',').next()) }) } /// Get the HTTP method pub fn method(&self) -> Method { self.method } /// Set the HTTP method. pub fn set_method(&mut self, method: Method) { self.method = method; } /// Get a reference to the url. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Request, Response, StatusCode}; /// let mut req = Request::get("https://example.com"); /// assert_eq!(req.url().scheme(), "https"); /// # /// # Ok(()) } /// ``` pub fn url(&self) -> &Url { &self.url } /// Get a mutable reference to the url. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Method, Request, Response, StatusCode, Url}; /// let mut req = Request::get("https://example.com"); /// req.url_mut().set_scheme("http"); /// assert_eq!(req.url().scheme(), "http"); /// # /// # Ok(()) } /// ``` pub fn url_mut(&mut self) -> &mut Url { &mut self.url } /// Set the request body. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::get("https://example.com"); /// req.set_body("Hello, Nori!"); /// ``` pub fn set_body(&mut self, body: impl Into) { self.replace_body(body); } /// Swaps the value of the body with another body, without deinitializing /// either one. /// /// # Examples /// /// ``` /// # use async_std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// # /// use http_types::{Body, Method, Request}; /// /// let mut req = Request::get("https://example.com"); /// req.set_body("Hello, Nori!"); /// let mut body: Body = req.replace_body("Hello, Chashu!"); /// /// let mut string = String::new(); /// body.read_to_string(&mut string).await?; /// assert_eq!(&string, "Hello, Nori!"); /// # /// # Ok(()) }) } /// ``` pub fn replace_body(&mut self, body: impl Into) -> Body { let body = mem::replace(&mut self.body, body.into()); self.copy_content_type_from_body(); body } /// Replace the request body with a new body, and return the old body. /// /// # Examples /// /// ``` /// # use async_std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// # /// use http_types::{Body, Request}; /// /// let mut req = Request::get("https://example.com"); /// req.set_body("Hello, Nori!"); /// let mut body = "Hello, Chashu!".into(); /// req.swap_body(&mut body); /// /// let mut string = String::new(); /// body.read_to_string(&mut string).await?; /// assert_eq!(&string, "Hello, Nori!"); /// # /// # Ok(()) }) } /// ``` pub fn swap_body(&mut self, body: &mut Body) { mem::swap(&mut self.body, body); self.copy_content_type_from_body(); } /// Take the request body, replacing it with an empty body. /// /// # Examples /// /// ``` /// # use async_std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// # /// use http_types::{Body, Request}; /// /// let mut req = Request::get("https://example.com"); /// req.set_body("Hello, Nori!"); /// let mut body: Body = req.take_body(); /// /// let mut string = String::new(); /// body.read_to_string(&mut string).await?; /// assert_eq!(&string, "Hello, Nori!"); /// /// # let mut string = String::new(); /// # req.read_to_string(&mut string).await?; /// # assert_eq!(&string, ""); /// # /// # Ok(()) }) } /// ``` pub fn take_body(&mut self) -> Body { self.replace_body(Body::empty()) } /// Read the body as a string. /// /// This consumes the request. If you want to read the body without /// consuming the request, consider using the `take_body` method and /// then calling `Body::into_string` or using the Request's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # use std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use async_std::io::Cursor; /// use http_types::{Body, Request}; /// /// let mut req = Request::get("https://example.com"); /// /// let cursor = Cursor::new("Hello Nori"); /// let body = Body::from_reader(cursor, None); /// req.set_body(body); /// assert_eq!(&req.body_string().await.unwrap(), "Hello Nori"); /// # Ok(()) }) } /// ``` pub async fn body_string(&mut self) -> crate::Result { let body = self.take_body(); body.into_string().await } /// Read the body as bytes. /// /// This consumes the `Request`. If you want to read the body without /// consuming the request, consider using the `take_body` method and /// then calling `Body::into_bytes` or using the Request's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::{Body, Request}; /// /// let bytes = vec![1, 2, 3]; /// let mut req = Request::get("https://example.com"); /// req.set_body(Body::from_bytes(bytes)); /// /// let bytes = req.body_bytes().await?; /// assert_eq!(bytes, vec![1, 2, 3]); /// # Ok(()) }) } /// ``` pub async fn body_bytes(&mut self) -> crate::Result> { let body = self.take_body(); body.into_bytes().await } /// Read the body as JSON. /// /// This consumes the request. If you want to read the body without /// consuming the request, consider using the `take_body` method and /// then calling `Body::into_json` or using the Request's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::convert::{Deserialize, Serialize}; /// use http_types::{Body, Request}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { /// name: String, /// } /// /// let cat = Cat { /// name: String::from("chashu"), /// }; /// let mut req = Request::get("https://example.com"); /// req.set_body(Body::from_json(&cat)?); /// /// let cat: Cat = req.body_json().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub async fn body_json(&mut self) -> crate::Result { let body = self.take_body(); body.into_json().await } /// Read the body as `x-www-form-urlencoded`. /// /// This consumes the request. If you want to read the body without /// consuming the request, consider using the `take_body` method and /// then calling `Body::into_json` or using the Request's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::convert::{Deserialize, Serialize}; /// use http_types::{Body, Request}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { /// name: String, /// } /// /// let cat = Cat { /// name: String::from("chashu"), /// }; /// let mut req = Request::get("https://example.com"); /// req.set_body(Body::from_form(&cat)?); /// /// let cat: Cat = req.body_form().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub async fn body_form(&mut self) -> crate::Result { let body = self.take_body(); body.into_form().await } /// Get an HTTP header. pub fn header(&self, name: impl Into) -> Option<&HeaderValues> { self.headers.get(name) } /// Get a mutable reference to a header. pub fn header_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { self.headers.get_mut(name.into()) } /// Remove a header. pub fn remove_header(&mut self, name: impl Into) -> Option { self.headers.remove(name.into()) } /// Set an HTTP header. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), Box> { /// # /// use http_types::Request; /// /// let mut req = Request::get("https://example.com"); /// req.insert_header("Content-Type", "text/plain"); /// # /// # Ok(()) } /// ``` pub fn insert_header( &mut self, name: impl Into, values: impl ToHeaderValues, ) -> Option { self.headers.insert(name, values) } /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a /// header, but insert a header if there aren't any. Or else append to /// the existing list of headers. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), Box> { /// # /// use http_types::Request; /// /// let mut req = Request::get("https://example.com"); /// req.append_header("Content-Type", "text/plain"); /// # /// # Ok(()) } /// ``` pub fn append_header(&mut self, name: impl Into, values: impl ToHeaderValues) { self.headers.append(name, values) } /// Set the response MIME. // TODO: return a parsed MIME pub fn set_content_type(&mut self, mime: Mime) -> Option { let value: HeaderValue = mime.into(); // A Mime instance is guaranteed to be valid header name. self.insert_header(CONTENT_TYPE, value) } /// Copy MIME data from the body. fn copy_content_type_from_body(&mut self) { if self.header(CONTENT_TYPE).is_none() { self.set_content_type(self.body.mime().clone()); } } /// Get the current content type pub fn content_type(&self) -> Option { self.header(CONTENT_TYPE)?.last().as_str().parse().ok() } /// Get the length of the body stream, if it has been set. /// /// This value is set when passing a fixed-size object into as the body. /// E.g. a string, or a buffer. Consumers of this API should check this /// value to decide whether to use `Chunked` encoding, or set the /// response length. pub fn len(&self) -> Option { self.body.len() } /// Returns `true` if the request has a set body stream length of zero, /// `false` otherwise. pub fn is_empty(&self) -> Option { self.body.is_empty() } /// Get the HTTP version, if one has been set. /// /// # Examples /// /// ``` /// use http_types::{Request, Version}; /// /// # fn main() -> Result<(), http_types::Error> { /// # /// let mut req = Request::get("https://example.com"); /// assert_eq!(req.version(), None); /// /// req.set_version(Some(Version::Http2_0)); /// assert_eq!(req.version(), Some(Version::Http2_0)); /// # /// # Ok(()) } /// ``` pub fn version(&self) -> Option { self.version } /// Set the HTTP version. /// /// # Examples /// /// ``` /// use http_types::{Request, Version}; /// /// # fn main() -> Result<(), http_types::Error> { /// # /// let mut req = Request::get("https://example.com"); /// req.set_version(Some(Version::Http2_0)); /// # /// # Ok(()) } /// ``` pub fn set_version(&mut self, version: Option) { self.version = version; } /// Sends trailers to the a receiver. pub fn send_trailers(&mut self) -> trailers::Sender { self.has_trailers = true; let sender = self .trailers_sender .take() .expect("Trailers sender can only be constructed once"); trailers::Sender::new(sender) } /// Receive trailers from a sender. pub fn recv_trailers(&mut self) -> trailers::Receiver { let receiver = self .trailers_receiver .take() .expect("Trailers receiver can only be constructed once"); trailers::Receiver::new(receiver) } /// Returns `true` if sending trailers is in progress. pub fn has_trailers(&self) -> bool { self.has_trailers } /// An iterator visiting all header pairs in arbitrary order. pub fn iter(&self) -> headers::Iter<'_> { self.headers.iter() } /// An iterator visiting all header pairs in arbitrary order, with mutable /// references to the values. pub fn iter_mut(&mut self) -> headers::IterMut<'_> { self.headers.iter_mut() } /// An iterator visiting all header names in arbitrary order. pub fn header_names(&self) -> Names<'_> { self.headers.names() } /// An iterator visiting all header values in arbitrary order. pub fn header_values(&self) -> Values<'_> { self.headers.values() } /// Returns a reference to the existing local state. pub fn ext(&self) -> &Extensions { &self.ext } /// Returns a mutuable reference to the existing local state. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Request, Version}; /// /// let mut req = Request::get("https://example.com"); /// req.ext_mut().insert("hello from the extension"); /// assert_eq!(req.ext().get(), Some(&"hello from the extension")); /// # /// # Ok(()) } /// ``` pub fn ext_mut(&mut self) -> &mut Extensions { &mut self.ext } /// Get the URL querystring. /// /// # Examples /// /// ``` /// use http_types::convert::Deserialize; /// use http_types::Request; /// use std::collections::HashMap; /// /// // An owned structure: /// /// #[derive(Deserialize)] /// struct Index { /// page: u32, /// selections: HashMap, /// } /// /// let mut req = Request::get("https://httpbin.org/get?page=2&selections[width]=narrow&selections[height]=tall"); /// let Index { page, selections } = req.query().unwrap(); /// assert_eq!(page, 2); /// assert_eq!(selections["width"], "narrow"); /// assert_eq!(selections["height"], "tall"); /// /// // Using borrows: /// /// #[derive(Deserialize)] /// struct Query<'q> { /// format: &'q str, /// } /// /// let mut req = Request::get("https://httpbin.org/get?format=bananna"); /// let Query { format } = req.query().unwrap(); /// assert_eq!(format, "bananna"); /// ``` pub fn query<'de, T: Deserialize<'de>>(&'de self) -> crate::Result { // Default to an empty query string if no query parameter has been specified. // This allows successful deserialisation of structs where all fields are optional // when none of those fields has actually been passed by the caller. let query = self.url().query().unwrap_or(""); serde_qs::from_str(query).map_err(|e| { // Return the displayable version of the deserialisation error to the caller // for easier debugging. crate::Error::from_str(StatusCode::BadRequest, format!("{}", e)) }) } /// Set the URL querystring. /// /// # Examples /// /// ``` /// use http_types::convert::Serialize; /// use http_types::{Method, Request}; /// use std::collections::HashMap; /// /// #[derive(Serialize)] /// struct Index { /// page: u32, /// topics: Vec<&'static str>, /// } /// /// let query = Index { page: 2, topics: vec!["rust", "crabs", "crustaceans"] }; /// let mut req = Request::get("https://httpbin.org/get"); /// req.set_query(&query).unwrap(); /// assert_eq!(req.url().query(), Some("page=2&topics[0]=rust&topics[1]=crabs&topics[2]=crustaceans")); /// ``` pub fn set_query(&mut self, query: &impl Serialize) -> crate::Result<()> { let query = serde_qs::to_string(query) .map_err(|e| crate::Error::from_str(StatusCode::BadRequest, format!("{}", e)))?; self.url.set_query(Some(&query)); Ok(()) } /// Create a `GET` request. /// /// The `GET` method requests a representation of the specified resource. /// Requests using `GET` should only retrieve data. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::get("https://example.com"); /// req.set_body("Hello, Nori!"); /// assert_eq!(req.method(), Method::Get); /// ``` pub fn get(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Get, url) } /// Create a `HEAD` request. /// /// The `HEAD` method asks for a response identical to that of a `GET` /// request, but without the response body. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::head("https://example.com"); /// assert_eq!(req.method(), Method::Head); /// ``` pub fn head(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Head, url) } /// Create a `POST` request. /// /// The `POST` method is used to submit an entity to the specified resource, /// often causing a change in state or side effects on the server. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::post("https://example.com"); /// assert_eq!(req.method(), Method::Post); /// ``` pub fn post(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Post, url) } /// Create a `PUT` request. /// /// The `PUT` method replaces all current representations of the target /// resource with the request payload. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::put("https://example.com"); /// assert_eq!(req.method(), Method::Put); /// ``` pub fn put(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Put, url) } /// Create a `DELETE` request. /// /// The `DELETE` method deletes the specified resource. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::delete("https://example.com"); /// assert_eq!(req.method(), Method::Delete); /// ``` pub fn delete(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Delete, url) } /// Create a `CONNECT` request. /// /// The `CONNECT` method establishes a tunnel to the server identified by /// the target resource. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::connect("https://example.com"); /// assert_eq!(req.method(), Method::Connect); /// ``` pub fn connect(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Connect, url) } /// Create a `OPTIONS` request. /// /// The `OPTIONS` method is used to describe the communication options for /// the target resource. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::options("https://example.com"); /// assert_eq!(req.method(), Method::Options); /// ``` pub fn options(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Options, url) } /// Create a `TRACE` request. /// /// The `TRACE` method performs a message loop-back test along the path to /// the target resource. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::trace("https://example.com"); /// assert_eq!(req.method(), Method::Trace); /// ``` pub fn trace(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Trace, url) } /// Create a `PATCH` request. /// /// The `PATCH` method is used to apply partial modifications to a resource. /// /// # Examples /// /// ``` /// use http_types::{Method, Request}; /// /// let mut req = Request::patch("https://example.com"); /// assert_eq!(req.method(), Method::Patch); /// ``` pub fn patch(url: U) -> Self where U: TryInto, U::Error: std::fmt::Debug, { Request::new(Method::Patch, url) } } impl Clone for Request { /// Clone the request, resolving the body to `Body::empty()` and removing /// extensions. fn clone(&self) -> Self { Request { method: self.method, url: self.url.clone(), headers: self.headers.clone(), version: self.version, trailers_sender: None, trailers_receiver: None, body: Body::empty(), ext: Extensions::new(), peer_addr: self.peer_addr.clone(), local_addr: self.local_addr.clone(), has_trailers: false, } } } impl AsyncRead for Request { #[allow(missing_doc_code_examples)] fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { Pin::new(&mut self.body).poll_read(cx, buf) } } impl AsyncBufRead for Request { #[allow(missing_doc_code_examples)] fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); this.body.poll_fill_buf(cx) } fn consume(mut self: Pin<&mut Self>, amt: usize) { Pin::new(&mut self.body).consume(amt) } } impl AsRef for Request { fn as_ref(&self) -> &Headers { &self.headers } } impl AsMut for Request { fn as_mut(&mut self) -> &mut Headers { &mut self.headers } } impl From for Body { fn from(req: Request) -> Body { req.body } } impl Index for Request { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Request`. #[inline] fn index(&self, name: HeaderName) -> &HeaderValues { self.headers.index(name) } } impl Index<&str> for Request { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Request`. #[inline] fn index(&self, name: &str) -> &HeaderValues { self.headers.index(name) } } impl IntoIterator for Request { type Item = (HeaderName, HeaderValues); type IntoIter = headers::IntoIter; /// Returns a iterator of references over the remaining items. #[inline] fn into_iter(self) -> Self::IntoIter { self.headers.into_iter() } } impl<'a> IntoIterator for &'a Request { type Item = (&'a HeaderName, &'a HeaderValues); type IntoIter = headers::Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.headers.iter() } } impl<'a> IntoIterator for &'a mut Request { type Item = (&'a HeaderName, &'a mut HeaderValues); type IntoIter = headers::IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.headers.iter_mut() } } #[cfg(test)] mod tests { use super::*; mod host { use super::*; #[test] fn when_forwarded_header_is_set() { let mut request = build_test_request(); set_forwarded(&mut request, "-"); set_x_forwarded_host(&mut request, "this will not be used"); assert_eq!(request.forwarded_header_part("host"), Some("host.com")); assert_eq!(request.host(), Some("host.com")); } #[test] fn when_several_x_forwarded_hosts_exist() { let mut request = build_test_request(); set_x_forwarded_host(&mut request, "expected.host"); assert_eq!(request.forwarded_header_part("host"), None); assert_eq!(request.host(), Some("expected.host")); } #[test] fn when_only_one_x_forwarded_hosts_exist() { let mut request = build_test_request(); request.insert_header("x-forwarded-host", "expected.host"); assert_eq!(request.host(), Some("expected.host")); } #[test] fn when_host_header_is_set() { let mut request = build_test_request(); request.insert_header("host", "host.header"); assert_eq!(request.host(), Some("host.header")); } #[test] fn when_there_are_no_headers() { let request = build_test_request(); assert_eq!(request.host(), Some("async.rs")); } #[test] fn when_url_has_no_domain() { let mut request = build_test_request(); *request.url_mut() = Url::parse("x:").unwrap(); assert_eq!(request.host(), None); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_get() { let url = Url::parse("https://example.com").unwrap(); let req = Request::get(url); assert_eq!(req.method(), Method::Get); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_head() { let url = Url::parse("https://example.com").unwrap(); let req = Request::head(url); assert_eq!(req.method(), Method::Head); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_post() { let url = Url::parse("https://example.com").unwrap(); let req = Request::post(url); assert_eq!(req.method(), Method::Post); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_put() { let url = Url::parse("https://example.com").unwrap(); let req = Request::put(url); assert_eq!(req.method(), Method::Put); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_delete() { let url = Url::parse("https://example.com").unwrap(); let req = Request::delete(url); assert_eq!(req.method(), Method::Delete); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_connect() { let url = Url::parse("https://example.com").unwrap(); let req = Request::connect(url); assert_eq!(req.method(), Method::Connect); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_options() { let url = Url::parse("https://example.com").unwrap(); let req = Request::options(url); assert_eq!(req.method(), Method::Options); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_trace() { let url = Url::parse("https://example.com").unwrap(); let req = Request::trace(url); assert_eq!(req.method(), Method::Trace); } #[test] fn when_using_shorthand_with_valid_url_to_create_request_patch() { let url = Url::parse("https://example.com").unwrap(); let req = Request::patch(url); assert_eq!(req.method(), Method::Patch); } } mod remote { use super::*; #[test] fn when_forwarded_is_properly_formatted() { let mut request = build_test_request(); request.set_peer_addr(Some("127.0.0.1:8000")); set_forwarded(&mut request, "127.0.0.1:8001"); assert_eq!(request.forwarded_for(), Some("127.0.0.1:8001")); assert_eq!(request.remote(), Some("127.0.0.1:8001")); } #[test] fn when_forwarded_is_improperly_formatted() { let mut request = build_test_request(); request.set_peer_addr(Some( "127.0.0.1:8000".parse::().unwrap(), )); request.insert_header("Forwarded", "this is an improperly ;;; formatted header"); assert_eq!(request.forwarded_for(), None); assert_eq!(request.remote(), Some("127.0.0.1:8000")); } #[test] fn when_x_forwarded_for_is_set() { let mut request = build_test_request(); request.set_peer_addr(Some( std::path::PathBuf::from("/dev/random").to_str().unwrap(), )); set_x_forwarded_for(&mut request, "forwarded-host.com"); assert_eq!(request.forwarded_for(), Some("forwarded-host.com")); assert_eq!(request.remote(), Some("forwarded-host.com")); } #[test] fn when_both_forwarding_headers_are_set() { let mut request = build_test_request(); set_forwarded(&mut request, "forwarded.com"); set_x_forwarded_for(&mut request, "forwarded-for-client.com"); request.peer_addr = Some("127.0.0.1:8000".into()); assert_eq!(request.forwarded_for(), Some("forwarded.com")); assert_eq!(request.remote(), Some("forwarded.com")); } #[test] fn falling_back_to_peer_addr() { let mut request = build_test_request(); request.peer_addr = Some("127.0.0.1:8000".into()); assert_eq!(request.forwarded_for(), None); assert_eq!(request.remote(), Some("127.0.0.1:8000")); } #[test] fn when_no_remote_available() { let request = build_test_request(); assert_eq!(request.forwarded_for(), None); assert_eq!(request.remote(), None); } } fn build_test_request() -> Request { let url = Url::parse("http://async.rs/").unwrap(); Request::new(Method::Get, url) } fn set_x_forwarded_for(request: &mut Request, client: &'static str) { request.insert_header( "x-forwarded-for", format!("{},proxy.com,other-proxy.com", client), ); } fn set_x_forwarded_host(request: &mut Request, host: &'static str) { request.insert_header( "x-forwarded-host", format!("{},proxy.com,other-proxy.com", host), ); } fn set_forwarded(request: &mut Request, client: &'static str) { request.insert_header( "Forwarded", format!("by=something.com;for={};host=host.com;proto=http", client), ); } } http-types-2.12.0/src/response.rs000064400000000000000000000525460072674642500150660ustar 00000000000000use futures_lite::{io, prelude::*}; use std::convert::{Into, TryInto}; use std::fmt::Debug; use std::mem; use std::ops::Index; use std::pin::Pin; use std::task::{Context, Poll}; use crate::convert::DeserializeOwned; use crate::headers::{ self, HeaderName, HeaderValue, HeaderValues, Headers, Names, ToHeaderValues, Values, CONTENT_TYPE, }; use crate::mime::Mime; use crate::trailers::{self, Trailers}; use crate::upgrade; use crate::{Body, Extensions, StatusCode, Version}; pin_project_lite::pin_project! { /// An HTTP response. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Response, StatusCode}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.set_body("Hello, Nori!"); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct Response { status: StatusCode, headers: Headers, version: Option, has_trailers: bool, trailers_sender: Option>, trailers_receiver: Option>, upgrade_sender: Option>, upgrade_receiver: Option>, has_upgrade: bool, #[pin] body: Body, ext: Extensions, local_addr: Option, peer_addr: Option, } } impl Response { /// Create a new response. pub fn new(status: S) -> Self where S: TryInto, S::Error: Debug, { let status = status .try_into() .expect("Could not convert into a valid `StatusCode`"); let (trailers_sender, trailers_receiver) = async_channel::bounded(1); let (upgrade_sender, upgrade_receiver) = async_channel::bounded(1); Self { status, headers: Headers::new(), version: None, body: Body::empty(), trailers_sender: Some(trailers_sender), trailers_receiver: Some(trailers_receiver), has_trailers: false, upgrade_sender: Some(upgrade_sender), upgrade_receiver: Some(upgrade_receiver), has_upgrade: false, ext: Extensions::new(), peer_addr: None, local_addr: None, } } /// Get the status pub fn status(&self) -> StatusCode { self.status } /// Get a mutable reference to a header. pub fn header_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { self.headers.get_mut(name.into()) } /// Get an HTTP header. pub fn header(&self, name: impl Into) -> Option<&HeaderValues> { self.headers.get(name.into()) } /// Remove a header. pub fn remove_header(&mut self, name: impl Into) -> Option { self.headers.remove(name.into()) } /// Set an HTTP header. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), Box> { /// # /// use http_types::{Method, Response, StatusCode, Url}; /// /// let mut req = Response::new(StatusCode::Ok); /// req.insert_header("Content-Type", "text/plain"); /// # /// # Ok(()) } /// ``` pub fn insert_header( &mut self, name: impl Into, values: impl ToHeaderValues, ) -> Option { self.headers.insert(name, values) } /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a /// header, but insert a header if there aren't any. Or else append to /// the existing list of headers. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), Box> { /// # /// use http_types::{Response, StatusCode}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.append_header("Content-Type", "text/plain"); /// # /// # Ok(()) } /// ``` pub fn append_header(&mut self, name: impl Into, values: impl ToHeaderValues) { self.headers.append(name, values) } /// Set the body reader. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Response, StatusCode}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.set_body("Hello, Nori!"); /// # /// # Ok(()) } /// ``` pub fn set_body(&mut self, body: impl Into) { self.replace_body(body); } /// Replace the response body with a new body, returning the old body. /// /// # Examples /// /// ``` /// # use async_std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// # /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// let mut req = Response::new(StatusCode::Ok); /// req.set_body("Hello, Nori!"); /// /// let mut body: Body = req.replace_body("Hello, Chashu"); /// /// let mut string = String::new(); /// body.read_to_string(&mut string).await?; /// assert_eq!(&string, "Hello, Nori!"); /// # /// # Ok(()) }) } /// ``` pub fn replace_body(&mut self, body: impl Into) -> Body { let body = mem::replace(&mut self.body, body.into()); self.copy_content_type_from_body(); body } /// Swaps the value of the body with another body, without deinitializing /// either one. /// /// # Examples /// /// ``` /// # use async_std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// # /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// let mut req = Response::new(StatusCode::Ok); /// req.set_body("Hello, Nori!"); /// /// let mut body = "Hello, Chashu!".into(); /// req.swap_body(&mut body); /// /// let mut string = String::new(); /// body.read_to_string(&mut string).await?; /// assert_eq!(&string, "Hello, Nori!"); /// # /// # Ok(()) }) } /// ``` pub fn swap_body(&mut self, body: &mut Body) { mem::swap(&mut self.body, body); self.copy_content_type_from_body(); } /// Take the response body, replacing it with an empty body. /// /// # Examples /// /// ``` /// # use async_std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// # /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// let mut req = Response::new(StatusCode::Ok); /// req.set_body("Hello, Nori!"); /// let mut body: Body = req.take_body(); /// /// let mut string = String::new(); /// body.read_to_string(&mut string).await?; /// assert_eq!(&string, "Hello, Nori!"); /// /// # let mut string = String::new(); /// # req.read_to_string(&mut string).await?; /// # assert_eq!(&string, ""); /// # /// # Ok(()) }) } /// ``` pub fn take_body(&mut self) -> Body { self.replace_body(Body::empty()) } /// Read the body as a string. /// /// This consumes the response. If you want to read the body without /// consuming the response, consider using the `take_body` method and /// then calling `Body::into_string` or using the Response's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # use std::io::prelude::*; /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use async_std::io::Cursor; /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// let mut res = Response::new(StatusCode::Ok); /// let cursor = Cursor::new("Hello Nori"); /// let body = Body::from_reader(cursor, None); /// res.set_body(body); /// assert_eq!(&res.body_string().await.unwrap(), "Hello Nori"); /// # Ok(()) }) } /// ``` pub async fn body_string(&mut self) -> crate::Result { let body = self.take_body(); body.into_string().await } /// Read the body as bytes. /// /// This consumes the `Response`. If you want to read the body without /// consuming the response, consider using the `take_body` method and /// then calling `Body::into_bytes` or using the Response's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// let bytes = vec![1, 2, 3]; /// let mut res = Response::new(StatusCode::Ok); /// res.set_body(Body::from_bytes(bytes)); /// /// let bytes = res.body_bytes().await?; /// assert_eq!(bytes, vec![1, 2, 3]); /// # Ok(()) }) } /// ``` pub async fn body_bytes(&mut self) -> crate::Result> { let body = self.take_body(); body.into_bytes().await } /// Read the body as JSON. /// /// This consumes the response. If you want to read the body without /// consuming the response, consider using the `take_body` method and /// then calling `Body::into_json` or using the Response's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::convert::{Deserialize, Serialize}; /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { /// name: String, /// } /// /// let cat = Cat { /// name: String::from("chashu"), /// }; /// let mut res = Response::new(StatusCode::Ok); /// res.set_body(Body::from_json(&cat)?); /// /// let cat: Cat = res.body_json().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub async fn body_json(&mut self) -> crate::Result { let body = self.take_body(); body.into_json().await } /// Read the body as `x-www-form-urlencoded`. /// /// This consumes the request. If you want to read the body without /// consuming the request, consider using the `take_body` method and /// then calling `Body::into_json` or using the Response's AsyncRead /// implementation to read the body. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { async_std::task::block_on(async { /// use http_types::convert::{Deserialize, Serialize}; /// use http_types::{Body, Method, Response, StatusCode, Url}; /// /// #[derive(Debug, Serialize, Deserialize)] /// struct Cat { /// name: String, /// } /// /// let cat = Cat { /// name: String::from("chashu"), /// }; /// let mut res = Response::new(StatusCode::Ok); /// res.set_body(Body::from_form(&cat)?); /// /// let cat: Cat = res.body_form().await?; /// assert_eq!(&cat.name, "chashu"); /// # Ok(()) }) } /// ``` pub async fn body_form(&mut self) -> crate::Result { let body = self.take_body(); body.into_form().await } /// Set the response MIME. pub fn set_content_type(&mut self, mime: Mime) -> Option { let value: HeaderValue = mime.into(); // A Mime instance is guaranteed to be valid header name. self.insert_header(CONTENT_TYPE, value) } /// Copy MIME data from the body. fn copy_content_type_from_body(&mut self) { if self.header(CONTENT_TYPE).is_none() { self.set_content_type(self.body.mime().clone()); } } /// Get the current content type pub fn content_type(&self) -> Option { self.header(CONTENT_TYPE)?.last().as_str().parse().ok() } /// Get the length of the body stream, if it has been set. /// /// This value is set when passing a fixed-size object into as the body. /// E.g. a string, or a buffer. Consumers of this API should check this /// value to decide whether to use `Chunked` encoding, or set the /// response length. pub fn len(&self) -> Option { self.body.len() } /// Returns `true` if the set length of the body stream is zero, `false` /// otherwise. pub fn is_empty(&self) -> Option { self.body.is_empty() } /// Get the HTTP version, if one has been set. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Response, StatusCode, Version}; /// /// let mut res = Response::new(StatusCode::Ok); /// assert_eq!(res.version(), None); /// /// res.set_version(Some(Version::Http2_0)); /// assert_eq!(res.version(), Some(Version::Http2_0)); /// # /// # Ok(()) } /// ``` pub fn version(&self) -> Option { self.version } /// Sets a string representation of the peer address that this /// response was sent to. This might take the form of an ip/fqdn /// and port or a local socket address. pub fn set_peer_addr(&mut self, peer_addr: Option) { self.peer_addr = peer_addr.map(|addr| addr.to_string()); } /// Sets a string representation of the local address that this /// response was sent on. This might take the form of an ip/fqdn and /// port, or a local socket address. pub fn set_local_addr(&mut self, local_addr: Option) { self.local_addr = local_addr.map(|addr| addr.to_string()); } /// Get the peer socket address for the underlying transport, if appropriate pub fn peer_addr(&self) -> Option<&str> { self.peer_addr.as_deref() } /// Get the local socket address for the underlying transport, if /// appropriate pub fn local_addr(&self) -> Option<&str> { self.local_addr.as_deref() } /// Set the HTTP version. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Response, StatusCode, Version}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.set_version(Some(Version::Http2_0)); /// # /// # Ok(()) } /// ``` pub fn set_version(&mut self, version: Option) { self.version = version; } /// Set the status. pub fn set_status(&mut self, status: StatusCode) { self.status = status; } /// Sends trailers to the a receiver. pub fn send_trailers(&mut self) -> trailers::Sender { self.has_trailers = true; let sender = self .trailers_sender .take() .expect("Trailers sender can only be constructed once"); trailers::Sender::new(sender) } /// Receive trailers from a sender. pub fn recv_trailers(&mut self) -> trailers::Receiver { let receiver = self .trailers_receiver .take() .expect("Trailers receiver can only be constructed once"); trailers::Receiver::new(receiver) } /// Returns `true` if sending trailers is in progress. pub fn has_trailers(&self) -> bool { self.has_trailers } /// Sends an upgrade connection to the a receiver. #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn send_upgrade(&mut self) -> upgrade::Sender { self.has_upgrade = true; let sender = self .upgrade_sender .take() .expect("Upgrade sender can only be constructed once"); upgrade::Sender::new(sender) } /// Receive an upgraded connection from a sender. #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub async fn recv_upgrade(&mut self) -> upgrade::Receiver { self.has_upgrade = true; let receiver = self .upgrade_receiver .take() .expect("Upgrade receiver can only be constructed once"); upgrade::Receiver::new(receiver) } /// Returns `true` if a protocol upgrade is in progress. #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub fn has_upgrade(&self) -> bool { self.has_upgrade } /// An iterator visiting all header pairs in arbitrary order. pub fn iter(&self) -> headers::Iter<'_> { self.headers.iter() } /// An iterator visiting all header pairs in arbitrary order, with mutable /// references to the values. pub fn iter_mut(&mut self) -> headers::IterMut<'_> { self.headers.iter_mut() } /// An iterator visiting all header names in arbitrary order. pub fn header_names(&self) -> Names<'_> { self.headers.names() } /// An iterator visiting all header values in arbitrary order. pub fn header_values(&self) -> Values<'_> { self.headers.values() } /// Returns a reference to the existing local. pub fn ext(&self) -> &Extensions { &self.ext } /// Returns a mutuable reference to the existing local state. /// /// /// # Examples /// /// ``` /// # fn main() -> Result<(), http_types::Error> { /// # /// use http_types::{Response, StatusCode, Version}; /// /// let mut res = Response::new(StatusCode::Ok); /// res.ext_mut().insert("hello from the extension"); /// assert_eq!(res.ext().get(), Some(&"hello from the extension")); /// # /// # Ok(()) } /// ``` pub fn ext_mut(&mut self) -> &mut Extensions { &mut self.ext } } impl Clone for Response { /// Clone the response, resolving the body to `Body::empty()` and removing /// extensions. fn clone(&self) -> Self { Self { status: self.status, headers: self.headers.clone(), version: self.version, trailers_sender: self.trailers_sender.clone(), trailers_receiver: self.trailers_receiver.clone(), has_trailers: false, upgrade_sender: self.upgrade_sender.clone(), upgrade_receiver: self.upgrade_receiver.clone(), has_upgrade: false, body: Body::empty(), ext: Extensions::new(), peer_addr: self.peer_addr.clone(), local_addr: self.local_addr.clone(), } } } impl AsyncRead for Response { #[allow(missing_doc_code_examples)] fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { Pin::new(&mut self.body).poll_read(cx, buf) } } impl AsyncBufRead for Response { #[allow(missing_doc_code_examples)] fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.project(); this.body.poll_fill_buf(cx) } fn consume(mut self: Pin<&mut Self>, amt: usize) { Pin::new(&mut self.body).consume(amt) } } impl AsRef for Response { fn as_ref(&self) -> &Headers { &self.headers } } impl AsMut for Response { fn as_mut(&mut self) -> &mut Headers { &mut self.headers } } impl From<()> for Response { fn from(_: ()) -> Self { Response::new(StatusCode::NoContent) } } impl Index for Response { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Response`. #[inline] fn index(&self, name: HeaderName) -> &HeaderValues { self.headers.index(name) } } impl Index<&str> for Response { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Response`. #[inline] fn index(&self, name: &str) -> &HeaderValues { self.headers.index(name) } } impl From for Response { fn from(s: StatusCode) -> Self { Response::new(s) } } impl From for Response where T: Into, { fn from(value: T) -> Self { let body: Body = value.into(); let mut res = Response::new(StatusCode::Ok); res.set_body(body); res } } impl IntoIterator for Response { type Item = (HeaderName, HeaderValues); type IntoIter = headers::IntoIter; /// Returns a iterator of references over the remaining items. #[inline] fn into_iter(self) -> Self::IntoIter { self.headers.into_iter() } } impl<'a> IntoIterator for &'a Response { type Item = (&'a HeaderName, &'a HeaderValues); type IntoIter = headers::Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.headers.iter() } } impl<'a> IntoIterator for &'a mut Response { type Item = (&'a HeaderName, &'a mut HeaderValues); type IntoIter = headers::IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.headers.iter_mut() } } #[cfg(test)] mod test { use super::Response; #[test] fn construct_shorthand_with_valid_status_code() { let _res = Response::new(200); } #[test] #[should_panic(expected = "Could not convert into a valid `StatusCode`")] fn construct_shorthand_with_invalid_status_code() { let _res = Response::new(600); } } http-types-2.12.0/src/security/csp.rs000064400000000000000000000322320072674642500156520ustar 00000000000000use crate::Headers; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fmt; /// Define source value /// /// [read more](https://content-security-policy.com) #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub enum Source { /// Set source `'self'` SameOrigin, /// Set source `'src'` Src, /// Set source `'none'` None, /// Set source `'unsafe-inline'` UnsafeInline, /// Set source `data:` Data, /// Set source `mediastream:` Mediastream, /// Set source `https:` Https, /// Set source `blob:` Blob, /// Set source `filesystem:` Filesystem, /// Set source `'strict-dynamic'` StrictDynamic, /// Set source `'unsafe-eval'` UnsafeEval, /// Set source `*` Wildcard, } impl fmt::Display for Source { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Source::SameOrigin => write!(f, "'self'"), Source::Src => write!(f, "'src'"), Source::None => write!(f, "'none'"), Source::UnsafeInline => write!(f, "'unsafe-inline'"), Source::Data => write!(f, "data:"), Source::Mediastream => write!(f, "mediastream:"), Source::Https => write!(f, "https:"), Source::Blob => write!(f, "blob:"), Source::Filesystem => write!(f, "filesystem:"), Source::StrictDynamic => write!(f, "'strict-dynamic'"), Source::UnsafeEval => write!(f, "'unsafe-eval'"), Source::Wildcard => write!(f, "*"), } } } impl AsRef for Source { fn as_ref(&self) -> &str { match *self { Source::SameOrigin => "'self'", Source::Src => "'src'", Source::None => "'none'", Source::UnsafeInline => "'unsafe-inline'", Source::Data => "data:", Source::Mediastream => "mediastream:", Source::Https => "https:", Source::Blob => "blob:", Source::Filesystem => "filesystem:", Source::StrictDynamic => "'strict-dynamic'", Source::UnsafeEval => "'unsafe-eval'", Source::Wildcard => "*", } } } /// Define `report-to` directive value /// /// [MDN | report-to](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ReportTo { #[serde(skip_serializing_if = "Option::is_none")] group: Option, max_age: i32, endpoints: Vec, #[serde(skip_serializing_if = "Option::is_none")] include_subdomains: Option, } /// Define `endpoints` for `report-to` directive value /// /// [MDN | report-to](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ReportToEndpoint { url: String, } /// Build a `Content-Security-Policy` header. /// /// `Content-Security-Policy` (CSP) HTTP headers are used to prevent cross-site /// injections. [Read more](https://helmetjs.github.io/docs/csp/) /// /// [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) /// /// # Examples /// /// ``` /// use http_types::{headers, security, Response, StatusCode}; /// /// let mut policy = security::ContentSecurityPolicy::new(); /// policy /// .default_src(security::Source::SameOrigin) /// .default_src("areweasyncyet.rs") /// .script_src(security::Source::SameOrigin) /// .script_src(security::Source::UnsafeInline) /// .object_src(security::Source::None) /// .base_uri(security::Source::None) /// .upgrade_insecure_requests(); /// /// let mut res = Response::new(StatusCode::Ok); /// res.set_body("Hello, Chashu!"); /// /// security::default(&mut res); /// policy.apply(&mut res); /// /// assert_eq!(res["content-security-policy"], "base-uri 'none'; default-src 'self' areweasyncyet.rs; object-src 'none'; script-src 'self' 'unsafe-inline'; upgrade-insecure-requests"); /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct ContentSecurityPolicy { policy: Vec, report_only_flag: bool, directives: HashMap>, } impl Default for ContentSecurityPolicy { /// Sets the Content-Security-Policy default to "script-src 'self'; object-src 'self'" fn default() -> Self { let policy = String::from("script-src 'self'; object-src 'self'"); ContentSecurityPolicy { policy: vec![policy], report_only_flag: false, directives: HashMap::new(), } } } impl ContentSecurityPolicy { /// Create a new instance. pub fn new() -> Self { Self { policy: Vec::new(), report_only_flag: false, directives: HashMap::new(), } } fn insert_directive>(&mut self, directive: &str, source: T) { let directive = String::from(directive); let directives = self.directives.entry(directive).or_insert_with(Vec::new); let source: String = source.as_ref().to_string(); directives.push(source); } /// Defines the Content-Security-Policy `base-uri` directive /// /// [MDN | base-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/base-uri) pub fn base_uri>(&mut self, source: T) -> &mut Self { self.insert_directive("base-uri", source); self } /// Defines the Content-Security-Policy `block-all-mixed-content` directive /// /// [MDN | block-all-mixed-content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/block-all-mixed-content) pub fn block_all_mixed_content(&mut self) -> &mut Self { let policy = String::from("block-all-mixed-content"); self.policy.push(policy); self } /// Defines the Content-Security-Policy `connect-src` directive /// /// [MDN | connect-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src) pub fn connect_src>(&mut self, source: T) -> &mut Self { self.insert_directive("connect-src", source); self } /// Defines the Content-Security-Policy `default-src` directive /// /// [MDN | default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src) pub fn default_src>(&mut self, source: T) -> &mut Self { self.insert_directive("default-src", source); self } /// Defines the Content-Security-Policy `font-src` directive /// /// [MDN | font-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/font-src) pub fn font_src>(&mut self, source: T) -> &mut Self { self.insert_directive("font-src", source); self } /// Defines the Content-Security-Policy `form-action` directive /// /// [MDN | form-action](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action) pub fn form_action>(&mut self, source: T) -> &mut Self { self.insert_directive("form-action", source); self } /// Defines the Content-Security-Policy `frame-ancestors` directive /// /// [MDN | frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) pub fn frame_ancestors>(&mut self, source: T) -> &mut Self { self.insert_directive("frame-ancestors", source); self } /// Defines the Content-Security-Policy `frame-src` directive /// /// [MDN | frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src) pub fn frame_src>(&mut self, source: T) -> &mut Self { self.insert_directive("frame-src", source); self } /// Defines the Content-Security-Policy `img-src` directive /// /// [MDN | img-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/img-src) pub fn img_src>(&mut self, source: T) -> &mut Self { self.insert_directive("img-src", source); self } /// Defines the Content-Security-Policy `media-src` directive /// /// [MDN | media-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/media-src) pub fn media_src>(&mut self, source: T) -> &mut Self { self.insert_directive("media-src", source); self } /// Defines the Content-Security-Policy `object-src` directive /// /// [MDN | object-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/object-src) pub fn object_src>(&mut self, source: T) -> &mut Self { self.insert_directive("object-src", source); self } /// Defines the Content-Security-Policy `plugin-types` directive /// /// [MDN | plugin-types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/plugin-types) pub fn plugin_types>(&mut self, source: T) -> &mut Self { self.insert_directive("plugin-types", source); self } /// Defines the Content-Security-Policy `require-sri-for` directive /// /// [MDN | require-sri-for](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-sri-for) pub fn require_sri_for>(&mut self, source: T) -> &mut Self { self.insert_directive("require-sri-for", source); self } /// Defines the Content-Security-Policy `report-uri` directive /// /// [MDN | report-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri) pub fn report_uri>(&mut self, uri: T) -> &mut Self { self.insert_directive("report-uri", uri); self } /// Defines the Content-Security-Policy `report-to` directive /// /// [MDN | report-to](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) pub fn report_to(&mut self, endpoints: Vec) -> &mut Self { for endpoint in endpoints.iter() { match serde_json::to_string(&endpoint) { Ok(json) => { let policy = format!("report-to {}", json); self.policy.push(policy); } Err(error) => { println!("{:?}", error); } } } self } /// Defines the Content-Security-Policy `sandbox` directive /// /// [MDN | sandbox](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox) pub fn sandbox>(&mut self, source: T) -> &mut Self { self.insert_directive("sandbox", source); self } /// Defines the Content-Security-Policy `script-src` directive /// /// [MDN | script-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src) pub fn script_src>(&mut self, source: T) -> &mut Self { self.insert_directive("script-src", source); self } /// Defines the Content-Security-Policy `style-src` directive /// /// [MDN | style-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src) pub fn style_src>(&mut self, source: T) -> &mut Self { self.insert_directive("style-src", source); self } /// Defines the Content-Security-Policy `upgrade-insecure-requests` directive /// /// [MDN | upgrade-insecure-requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests) pub fn upgrade_insecure_requests(&mut self) -> &mut Self { let policy = String::from("upgrade-insecure-requests"); self.policy.push(policy); self } /// Defines the Content-Security-Policy `worker-src` directive /// /// [MDN | worker-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/worker-src) pub fn worker_src>(&mut self, source: T) -> &mut Self { self.insert_directive("worker-src", source); self } /// Change the header to `Content-Security-Policy-Report-Only` pub fn report_only(&mut self) -> &mut Self { self.report_only_flag = true; self } /// Create and retrieve the policy value fn value(&mut self) -> String { for (directive, sources) in &self.directives { let policy = format!("{} {}", directive, sources.join(" ")); self.policy.push(policy); self.policy.sort(); } self.policy.join("; ") } /// Sets the `Content-Security-Policy` (CSP) HTTP header to prevent cross-site injections pub fn apply(&mut self, mut headers: impl AsMut) { let name = if self.report_only_flag { "Content-Security-Policy-Report-Only" } else { "Content-Security-Policy" }; headers.as_mut().insert(name, self.value()); } } http-types-2.12.0/src/security/mod.rs000064400000000000000000000170170072674642500156500ustar 00000000000000//! HTTP Security Headers. //! //! # Specifications //! //! - [W3C Timing-Allow-Origin header](https://w3c.github.io/resource-timing/#sec-timing-allow-origin) //! //! # Example //! //! ``` //! use http_types::{StatusCode, Response}; //! //! let mut res = Response::new(StatusCode::Ok); //! http_types::security::default(&mut res); // //! assert_eq!(res["X-Content-Type-Options"], "nosniff"); // //! assert_eq!(res["X-XSS-Protection"], "1; mode=block"); //! ``` use crate::headers::{HeaderName, HeaderValue, Headers}; mod csp; mod timing_allow_origin; pub use csp::{ContentSecurityPolicy, ReportTo, ReportToEndpoint, Source}; #[doc(inline)] pub use timing_allow_origin::TimingAllowOrigin; /// Apply a set of default protections. /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::default(&mut headers); // /// assert_eq!(headers["X-Content-Type-Options"], "nosniff"); // /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block"); // /// ``` pub fn default(mut headers: impl AsMut) { dns_prefetch_control(&mut headers); nosniff(&mut headers); frameguard(&mut headers, None); powered_by(&mut headers, None); hsts(&mut headers); xss_filter(&mut headers); } /// Disable browsers’ DNS prefetching by setting the `X-DNS-Prefetch-Control` header. /// /// [read more](https://helmetjs.github.io/docs/dns-prefetch-control/) /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::dns_prefetch_control(&mut headers); // /// assert_eq!(headers["X-DNS-Prefetch-Control"], "on"); // /// ``` #[inline] pub fn dns_prefetch_control(mut headers: impl AsMut) { headers.as_mut().insert("X-DNS-Prefetch-Control", "on"); } /// Set the frameguard level. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum FrameOptions { /// Set to `sameorigin` SameOrigin, /// Set to `deny` Deny, } /// Mitigates clickjacking attacks by setting the `X-Frame-Options` header. /// /// [read more](https://helmetjs.github.io/docs/frameguard/) /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::frameguard(&mut headers, None); // /// assert_eq!(headers["X-Frame-Options"], "sameorigin"); // /// ``` #[inline] pub fn frameguard(mut headers: impl AsMut, guard: Option) { let kind = match guard { None | Some(FrameOptions::SameOrigin) => "sameorigin", Some(FrameOptions::Deny) => "deny", }; headers.as_mut().insert("X-Frame-Options", kind); } /// Removes the `X-Powered-By` header to make it slightly harder for attackers to see what /// potentially-vulnerable technology powers your site. /// /// [read more](https://helmetjs.github.io/docs/hide-powered-by/) /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// headers.as_mut().insert("X-Powered-By", "Tide/Rust".parse()); // /// http_types::security::hide_powered_by(&mut headers); // /// assert_eq!(headers.get("X-Powered-By"), None); // /// ``` #[inline] pub fn powered_by(mut headers: impl AsMut, value: Option) { let name = HeaderName::from_lowercase_str("X-Powered-By"); match value { Some(value) => { headers.as_mut().insert(name, value); } None => { headers.as_mut().remove(name); } }; } /// Sets the `Strict-Transport-Security` header to keep your users on `HTTPS`. /// /// Note that the header won’t tell users on HTTP to switch to HTTPS, it will tell HTTPS users to /// stick around. Defaults to 60 days. /// /// [read more](https://helmetjs.github.io/docs/hsts/) /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::hsts(&mut headers); // /// assert_eq!(headers["Strict-Transport-Security"], "max-age=5184000"); // /// ``` #[inline] pub fn hsts(mut headers: impl AsMut) { headers .as_mut() .insert("Strict-Transport-Security", "max-age=5184000"); } /// Prevent browsers from trying to guess (“sniff”) the MIME type, which can have security /// implications. /// /// [read more](https://helmetjs.github.io/docs/dont-sniff-mimetype/) /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::nosniff(&mut headers); // /// assert_eq!(headers["X-Content-Type-Options"], "nosniff"); // /// ``` #[inline] pub fn nosniff(mut headers: impl AsMut) { headers.as_mut().insert("X-Content-Type-Options", "nosniff"); } /// Sets the `X-XSS-Protection` header to prevent reflected XSS attacks. /// /// [read more](https://helmetjs.github.io/docs/xss-filter/) /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::xss_filter(&mut headers); // /// assert_eq!(headers["X-XSS-Protection"], "1; mode=block"); // /// ``` #[inline] pub fn xss_filter(mut headers: impl AsMut) { headers.as_mut().insert("X-XSS-Protection", "1; mode=block"); } /// Set the Referrer-Policy level #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ReferrerOptions { /// Set to "no-referrer" NoReferrer, /// Set to "no-referrer-when-downgrade" the default NoReferrerDowngrade, /// Set to "same-origin" SameOrigin, /// Set to "origin" Origin, /// Set to "strict-origin" StrictOrigin, /// Set to "origin-when-cross-origin" CrossOrigin, /// Set to "strict-origin-when-cross-origin" StrictCrossOrigin, /// Set to "unsafe-url" UnsafeUrl, } /// Mitigates referrer leakage by controlling the referer\[sic\] header in links away from pages /// /// [read more](https://scotthelme.co.uk/a-new-security-header-referrer-policy/) /// /// [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) /// /// // /// ## Examples // /// ``` // /// use http_types::Response; // /// // /// let mut res = Response::new(StatusCode::Ok); // /// http_types::security::referrer_policy(&mut headers, Some(http_types::security::ReferrerOptions::UnsafeUrl)); // /// http_types::security::referrer_policy(&mut headers, None); // /// let mut referrerValues: Vec<&str> = headers.get_all("Referrer-Policy").iter().map(|x| x.to_str().unwrap()).collect(); // /// assert_eq!(referrerValues.sort(), vec!("unsafe-url", "no-referrer").sort()); // /// ``` #[inline] pub fn referrer_policy(mut headers: impl AsMut, referrer: Option) { let policy = match referrer { None | Some(ReferrerOptions::NoReferrer) => "no-referrer", Some(ReferrerOptions::NoReferrerDowngrade) => "no-referrer-when-downgrade", Some(ReferrerOptions::SameOrigin) => "same-origin", Some(ReferrerOptions::Origin) => "origin", Some(ReferrerOptions::StrictOrigin) => "strict-origin", Some(ReferrerOptions::CrossOrigin) => "origin-when-cross-origin", Some(ReferrerOptions::StrictCrossOrigin) => "strict-origin-when-cross-origin", Some(ReferrerOptions::UnsafeUrl) => "unsafe-url", }; // We MUST allow for multiple Referrer-Policy headers to be set. // See: https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values example #13 headers.as_mut().append("Referrer-Policy", policy); } http-types-2.12.0/src/security/timing_allow_origin.rs000064400000000000000000000215110072674642500211170ustar 00000000000000//! Specify origins that are allowed to see values via the Resource Timing API. //! //! # Specifications //! //! - [W3C Timing-Allow-Origin header](https://w3c.github.io/resource-timing/#sec-timing-allow-origin) //! - [WhatWG Fetch Origin header](https://fetch.spec.whatwg.org/#origin-header) //! //! # Examples //! //! ``` //! # fn main() -> http_types::Result<()> { //! # //! use http_types::{Response, Url}; //! use http_types::security::TimingAllowOrigin; //! //! let mut origins = TimingAllowOrigin::new(); //! origins.push(Url::parse("https://example.com")?); //! //! let mut res = Response::new(200); //! origins.apply(&mut res); //! //! let origins = TimingAllowOrigin::from_headers(res)?.unwrap(); //! let origin = origins.iter().next().unwrap(); //! assert_eq!(origin, &Url::parse("https://example.com")?); //! # //! # Ok(()) } //! ``` use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, TIMING_ALLOW_ORIGIN}; use crate::{Status, Url}; use std::fmt::Write; use std::fmt::{self, Debug}; use std::iter::Iterator; use std::option; use std::slice; /// Specify origins that are allowed to see values via the Resource Timing API. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::{Response, Url}; /// use http_types::security::TimingAllowOrigin; /// /// let mut origins = TimingAllowOrigin::new(); /// origins.push(Url::parse("https://example.com")?); /// /// let mut res = Response::new(200); /// origins.apply(&mut res); /// /// let origins = TimingAllowOrigin::from_headers(res)?.unwrap(); /// let origin = origins.iter().next().unwrap(); /// assert_eq!(origin, &Url::parse("https://example.com")?); /// # /// # Ok(()) } /// ``` #[derive(Clone, Eq, PartialEq)] pub struct TimingAllowOrigin { origins: Vec, wildcard: bool, } impl TimingAllowOrigin { /// Create a new instance of `AllowOrigin`. pub fn new() -> Self { Self { origins: vec![], wildcard: false, } } /// Create an instance of `AllowOrigin` from a `Headers` instance. /// /// # Implementation note /// /// A header value of `"null"` is treated the same as if no header was sent. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(TIMING_ALLOW_ORIGIN) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; let mut origins = vec![]; for header in headers { for origin in header.as_str().split(',') { match origin.trim_start() { "*" => wildcard = true, r#""null""# => continue, origin => { let url = Url::parse(origin).status(400)?; origins.push(url); } } } } Ok(Some(Self { origins, wildcard })) } /// Append an origin to the list of origins. pub fn push(&mut self, origin: impl Into) { self.origins.push(origin.into()); } /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(TIMING_ALLOW_ORIGIN, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { TIMING_ALLOW_ORIGIN } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, origin) in self.origins.iter().enumerate() { match n { 0 => write!(output, "{}", origin).unwrap(), _ => write!(output, ", {}", origin).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Returns `true` if a wildcard directive was set. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// An iterator visiting all server timings. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.origins.iter(), } } /// An iterator visiting all server timings. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.origins.iter_mut(), } } } impl IntoIterator for TimingAllowOrigin { type Item = Url; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.origins.into_iter(), } } } impl<'a> IntoIterator for &'a TimingAllowOrigin { type Item = &'a Url; type IntoIter = Iter<'a>; // #[inline]serv fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut TimingAllowOrigin { type Item = &'a mut Url; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `AllowOrigin`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = Url; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `AllowOrigin`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, Url>, } impl<'a> Iterator for Iter<'a> { type Item = &'a Url; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `AllowOrigin`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, Url>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut Url; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } // Conversion from `AllowOrigin` -> `HeaderValue`. impl ToHeaderValues for TimingAllowOrigin { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { Ok(self.value().to_header_values().unwrap()) } } impl Debug for TimingAllowOrigin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for origin in &self.origins { list.entry(origin); } list.finish() } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let mut origins = TimingAllowOrigin::new(); origins.push(Url::parse("https://example.com")?); let mut headers = Headers::new(); origins.apply(&mut headers); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); let origin = origins.iter().next().unwrap(); assert_eq!(origin, &Url::parse("https://example.com")?); Ok(()) } #[test] fn multi() -> crate::Result<()> { let mut origins = TimingAllowOrigin::new(); origins.push(Url::parse("https://example.com")?); origins.push(Url::parse("https://mozilla.org/")?); let mut headers = Headers::new(); origins.apply(&mut headers); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); let mut origins = origins.iter(); let origin = origins.next().unwrap(); assert_eq!(origin, &Url::parse("https://example.com")?); let origin = origins.next().unwrap(); assert_eq!(origin, &Url::parse("https://mozilla.org/")?); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(TIMING_ALLOW_ORIGIN, "server; "); let err = TimingAllowOrigin::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } #[test] fn wildcard() -> crate::Result<()> { let mut origins = TimingAllowOrigin::new(); origins.push(Url::parse("https://example.com")?); origins.set_wildcard(true); let mut headers = Headers::new(); origins.apply(&mut headers); let origins = TimingAllowOrigin::from_headers(headers)?.unwrap(); assert!(origins.wildcard()); let origin = origins.iter().next().unwrap(); assert_eq!(origin, &Url::parse("https://example.com")?); Ok(()) } } http-types-2.12.0/src/server/allow.rs000064400000000000000000000117200072674642500156410ustar 00000000000000//! List the set of methods supported by a resource. use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ALLOW}; use crate::Method; use std::collections::{hash_set, HashSet}; use std::fmt::{self, Debug, Write}; use std::iter::Iterator; use std::option; use std::str::FromStr; /// List the set of methods supported by a resource. /// /// # Specifications /// /// - [RFC 7231, section 7.4.1: Allow](https://tools.ietf.org/html/rfc7231#section-7.4.1) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::{Method, Response}; /// use http_types::server::Allow; /// /// let mut allow = Allow::new(); /// allow.insert(Method::Put); /// allow.insert(Method::Post); /// /// let mut res = Response::new(200); /// allow.apply(&mut res); /// /// let allow = Allow::from_headers(res)?.unwrap(); /// assert!(allow.contains(Method::Put)); /// assert!(allow.contains(Method::Post)); /// # /// # Ok(()) } /// ``` pub struct Allow { entries: HashSet, } impl Allow { /// Create a new instance of `Allow`. pub fn new() -> Self { Self { entries: HashSet::new(), } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = HashSet::new(); let headers = match headers.as_ref().get(ALLOW) { Some(headers) => headers, None => return Ok(None), }; for value in headers { for part in value.as_str().trim().split(',') { let method = Method::from_str(part.trim())?; entries.insert(method); } } Ok(Some(Self { entries })) } /// Sets the `Allow` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(ALLOW, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { ALLOW } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, method) in self.entries.iter().enumerate() { match n { 0 => write!(output, "{}", method).unwrap(), _ => write!(output, ", {}", method).unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push a method into the set of methods. pub fn insert(&mut self, method: Method) { self.entries.insert(method); } /// An iterator visiting all server entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// Returns `true` if the header contains the `Method`. pub fn contains(&self, method: Method) -> bool { self.entries.contains(&method) } } impl IntoIterator for Allow { type Item = Method; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a Allow { type Item = &'a Method; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } /// A borrowing iterator over entries in `Allow`. #[derive(Debug)] pub struct IntoIter { inner: hash_set::IntoIter, } impl Iterator for IntoIter { type Item = Method; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `Allow`. #[derive(Debug)] pub struct Iter<'a> { inner: hash_set::Iter<'a, Method>, } impl<'a> Iterator for Iter<'a> { type Item = &'a Method; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for Allow { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for Allow { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for method in &self.entries { list.entry(method); } list.finish() } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let mut allow = Allow::new(); allow.insert(Method::Put); allow.insert(Method::Post); let mut headers = Headers::new(); allow.apply(&mut headers); let allow = Allow::from_headers(headers)?.unwrap(); assert!(allow.contains(Method::Put)); assert!(allow.contains(Method::Post)); Ok(()) } } http-types-2.12.0/src/server/mod.rs000064400000000000000000000001270072674642500153010ustar 00000000000000//! HTTP Server Context headers. pub mod allow; #[doc(inline)] pub use allow::Allow; http-types-2.12.0/src/status.rs000064400000000000000000000100430072674642500145350ustar 00000000000000use crate::{Error, StatusCode}; use core::convert::{Infallible, TryInto}; use std::error::Error as StdError; use std::fmt::Debug; /// Provides the `status` method for `Result` and `Option`. /// /// This trait is sealed and cannot be implemented outside of `http-types`. pub trait Status: private::Sealed { /// Wrap the error value with an additional status code. fn status(self, status: S) -> Result where S: TryInto, S::Error: Debug; /// Wrap the error value with an additional status code that is evaluated /// lazily only once an error does occur. fn with_status(self, f: F) -> Result where S: TryInto, S::Error: Debug, F: FnOnce() -> S; } impl Status for Result where E: StdError + Send + Sync + 'static, { /// Wrap the error value with an additional status code. /// /// # Panics /// /// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode]. /// /// [status]: crate::Status /// [statuscode]: crate::StatusCode fn status(self, status: S) -> Result where S: TryInto, S::Error: Debug, { self.map_err(|error| { let status = status .try_into() .expect("Could not convert into a valid `StatusCode`"); Error::new(status, error) }) } /// Wrap the error value with an additional status code that is evaluated /// lazily only once an error does occur. /// /// # Panics /// /// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode]. /// /// [status]: crate::Status /// [statuscode]: crate::StatusCode fn with_status(self, f: F) -> Result where S: TryInto, S::Error: Debug, F: FnOnce() -> S, { self.map_err(|error| { let status = f() .try_into() .expect("Could not convert into a valid `StatusCode`"); Error::new(status, error) }) } } impl Status for Option { /// Wrap the error value with an additional status code. /// /// # Panics /// /// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode]. /// /// [status]: crate::Status /// [statuscode]: crate::StatusCode fn status(self, status: S) -> Result where S: TryInto, S::Error: Debug, { self.ok_or_else(|| { let status = status .try_into() .expect("Could not convert into a valid `StatusCode`"); Error::from_str(status, "NoneError") }) } /// Wrap the error value with an additional status code that is evaluated /// lazily only once an error does occur. /// /// # Panics /// /// Panics if [`Status`][status] is not a valid [`StatusCode`][statuscode]. /// /// [status]: crate::Status /// [statuscode]: crate::StatusCode fn with_status(self, f: F) -> Result where S: TryInto, S::Error: Debug, F: FnOnce() -> S, { self.ok_or_else(|| { let status = f() .try_into() .expect("Could not convert into a valid `StatusCode`"); Error::from_str(status, "NoneError") }) } } pub(crate) mod private { pub trait Sealed {} impl Sealed for Result {} impl Sealed for Option {} } #[cfg(test)] mod test { use super::Status; #[test] fn construct_shorthand_with_valid_status_code() { let _res = Some(()).status(200).unwrap(); } #[test] #[should_panic(expected = "Could not convert into a valid `StatusCode`")] fn construct_shorthand_with_invalid_status_code() { let res: Result<(), std::io::Error> = Err(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")); let _res = res.status(600).unwrap(); } } http-types-2.12.0/src/status_code.rs000064400000000000000000000642110072674642500155350ustar 00000000000000use serde::de::{Error as DeError, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Display}; /// HTTP response status codes. /// /// As defined by [rfc7231 section 6](https://tools.ietf.org/html/rfc7231#section-6). /// [Read more](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) #[repr(u16)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum StatusCode { /// 100 Continue /// /// This interim response indicates that everything so far is OK and that /// the client should continue the request, or ignore the response if /// the request is already finished. Continue = 100, /// 101 Switching Protocols /// /// This code is sent in response to an Upgrade request header from the /// client, and indicates the protocol the server is switching to. SwitchingProtocols = 101, /// 103 Early Hints /// /// This status code is primarily intended to be used with the Link header, /// letting the user agent start preloading resources while the server /// prepares a response. EarlyHints = 103, /// 200 Ok /// /// The request has succeeded Ok = 200, /// 201 Created /// /// The request has succeeded and a new resource has been created as a /// result. This is typically the response sent after POST requests, or /// some PUT requests. Created = 201, /// 202 Accepted /// /// The request has been received but not yet acted upon. It is /// noncommittal, since there is no way in HTTP to later send an /// asynchronous response indicating the outcome of the request. It is /// intended for cases where another process or server handles the request, /// or for batch processing. Accepted = 202, /// 203 Non Authoritative Information /// /// This response code means the returned meta-information is not exactly /// the same as is available from the origin server, but is collected /// from a local or a third-party copy. This is mostly used for mirrors /// or backups of another resource. Except for that specific case, the /// "200 OK" response is preferred to this status. NonAuthoritativeInformation = 203, /// 204 No Content /// /// There is no content to send for this request, but the headers may be /// useful. The user-agent may update its cached headers for this /// resource with the new ones. NoContent = 204, /// 205 Reset Content /// /// Tells the user-agent to reset the document which sent this request. ResetContent = 205, /// 206 Partial Content /// /// This response code is used when the Range header is sent from the client /// to request only part of a resource. PartialContent = 206, /// 207 Multi-Status /// /// A Multi-Status response conveys information about /// multiple resources in situations where multiple /// status codes might be appropriate. MultiStatus = 207, /// 226 Im Used /// /// The server has fulfilled a GET request for the resource, and the /// response is a representation of the result of one or more /// instance-manipulations applied to the current instance. ImUsed = 226, /// 300 Multiple Choice /// /// The request has more than one possible response. The user-agent or user /// should choose one of them. (There is no standardized way of choosing /// one of the responses, but HTML links to the possibilities are /// recommended so the user can pick.) MultipleChoice = 300, /// 301 Moved Permanently /// /// The URL of the requested resource has been changed permanently. The new /// URL is given in the response. MovedPermanently = 301, /// 302 Found /// /// This response code means that the URI of requested resource has been /// changed temporarily. Further changes in the URI might be made in the /// future. Therefore, this same URI should be used by the client in /// future requests. Found = 302, /// 303 See Other /// /// The server sent this response to direct the client to get the requested /// resource at another URI with a GET request. SeeOther = 303, /// 304 Not Modified /// /// This is used for caching purposes. It tells the client that the response /// has not been modified, so the client can continue to use the same /// cached version of the response. NotModified = 304, /// 307 Temporary Redirect /// /// The server sends this response to direct the client to get the requested /// resource at another URI with same method that was used in the prior /// request. This has the same semantics as the 302 Found HTTP response /// code, with the exception that the user agent must not change the /// HTTP method used: If a POST was used in the first request, a POST must /// be used in the second request. TemporaryRedirect = 307, /// 308 Permanent Redirect /// /// This means that the resource is now permanently located at another URI, /// specified by the Location: HTTP Response header. This has the same /// semantics as the 301 Moved Permanently HTTP response code, with the /// exception that the user agent must not change the HTTP method /// used: If a POST was used in the first request, a POST must be used in /// the second request. PermanentRedirect = 308, /// 400 Bad Request /// /// The server could not understand the request due to invalid syntax. BadRequest = 400, /// 401 Unauthorized /// /// Although the HTTP standard specifies "unauthorized", semantically this /// response means "unauthenticated". That is, the client must /// authenticate itself to get the requested response. Unauthorized = 401, /// 402 Payment Required /// /// This response code is reserved for future use. The initial aim for /// creating this code was using it for digital payment systems, however /// this status code is used very rarely and no standard convention /// exists. PaymentRequired = 402, /// 403 Forbidden /// /// The client does not have access rights to the content; that is, it is /// unauthorized, so the server is refusing to give the requested /// resource. Unlike 401, the client's identity is known to the server. Forbidden = 403, /// 404 Not Found /// /// The server can not find requested resource. In the browser, this means /// the URL is not recognized. In an API, this can also mean that the /// endpoint is valid but the resource itself does not exist. Servers /// may also send this response instead of 403 to hide the existence of /// a resource from an unauthorized client. This response code is probably /// the most famous one due to its frequent occurrence on the web. NotFound = 404, /// 405 Method Not Allowed /// /// The request method is known by the server but has been disabled and /// cannot be used. For example, an API may forbid DELETE-ing a /// resource. The two mandatory methods, GET and HEAD, must never be /// disabled and should not return this error code. MethodNotAllowed = 405, /// 406 Not Acceptable /// /// This response is sent when the web server, after performing /// server-driven content negotiation, doesn't find any content that /// conforms to the criteria given by the user agent. NotAcceptable = 406, /// 407 Proxy Authentication Required /// /// This is similar to 401 but authentication is needed to be done by a /// proxy. ProxyAuthenticationRequired = 407, /// 408 Request Timeout /// /// This response is sent on an idle connection by some servers, even /// without any previous request by the client. It means that the server /// would like to shut down this unused connection. This response is /// used much more since some browsers, like Chrome, Firefox 27+, /// or IE9, use HTTP pre-connection mechanisms to speed up surfing. Also /// note that some servers merely shut down the connection without /// sending this message. RequestTimeout = 408, /// 409 Conflict /// /// This response is sent when a request conflicts with the current state of /// the server. Conflict = 409, /// 410 Gone /// /// This response is sent when the requested content has been permanently /// deleted from server, with no forwarding address. Clients are /// expected to remove their caches and links to the resource. The HTTP /// specification intends this status code to be used for "limited-time, /// promotional services". APIs should not feel compelled to indicate /// resources that have been deleted with this status code. Gone = 410, /// 411 Length Required /// /// Server rejected the request because the Content-Length header field is /// not defined and the server requires it. LengthRequired = 411, /// 412 Precondition Failed /// /// The client has indicated preconditions in its headers which the server /// does not meet. PreconditionFailed = 412, /// 413 Payload Too Large /// /// Request entity is larger than limits defined by server; the server might /// close the connection or return an Retry-After header field. PayloadTooLarge = 413, /// 414 URI Too Long /// /// The URI requested by the client is longer than the server is willing to /// interpret. UriTooLong = 414, /// 415 Unsupported Media Type /// /// The media format of the requested data is not supported by the server, /// so the server is rejecting the request. UnsupportedMediaType = 415, /// 416 Requested Range Not Satisfiable /// /// The range specified by the Range header field in the request can't be /// fulfilled; it's possible that the range is outside the size of the /// target URI's data. RequestedRangeNotSatisfiable = 416, /// 417 Expectation Failed /// /// This response code means the expectation indicated by the Expect request /// header field can't be met by the server. ExpectationFailed = 417, /// /// 418 I'm a teapot /// /// The server refuses the attempt to brew coffee with a teapot. ImATeapot = 418, /// 421 Misdirected Request /// /// The request was directed at a server that is not able to produce a /// response. This can be sent by a server that is not configured to /// produce responses for the combination of scheme and authority that /// are included in the request URI. MisdirectedRequest = 421, /// 422 Unprocessable Entity /// /// The request was well-formed but was unable to be followed due to /// semantic errors. UnprocessableEntity = 422, /// 423 Locked /// /// The resource that is being accessed is locked. Locked = 423, /// 424 Failed Dependency /// /// The request failed because it depended on another request and that /// request failed (e.g., a PROPPATCH). FailedDependency = 424, /// 425 Too Early /// /// Indicates that the server is unwilling to risk processing a request that /// might be replayed. TooEarly = 425, /// 426 Upgrade Required /// /// The server refuses to perform the request using the current protocol but /// might be willing to do so after the client upgrades to a different /// protocol. The server sends an Upgrade header in a 426 response to /// indicate the required protocol(s). UpgradeRequired = 426, /// 428 Precondition Required /// /// The origin server requires the request to be conditional. This response /// is intended to prevent the 'lost update' problem, where a client /// GETs a resource's state, modifies it, and PUTs it back to the /// server, when meanwhile a third party has modified the state on the /// server, leading to a conflict. PreconditionRequired = 428, /// 429 Too Many Requests /// /// The user has sent too many requests in a given amount of time ("rate /// limiting"). TooManyRequests = 429, /// 431 Request Header Fields Too Large /// /// The server is unwilling to process the request because its header fields /// are too large. The request may be resubmitted after reducing the /// size of the request header fields. RequestHeaderFieldsTooLarge = 431, /// 451 Unavailable For Legal Reasons /// /// The user-agent requested a resource that cannot legally be provided, /// such as a web page censored by a government. UnavailableForLegalReasons = 451, /// 500 Internal Server Error /// /// The server has encountered a situation it doesn't know how to handle. InternalServerError = 500, /// 501 Not Implemented /// /// The request method is not supported by the server and cannot be handled. /// The only methods that servers are required to support (and therefore /// that must not return this code) are GET and HEAD. NotImplemented = 501, /// 502 Bad Gateway /// /// This error response means that the server, while working as a gateway to /// get a response needed to handle the request, got an invalid /// response. BadGateway = 502, /// 503 Service Unavailable /// /// The server is not ready to handle the request. Common causes are a /// server that is down for maintenance or that is overloaded. Note that /// together with this response, a user-friendly page explaining the /// problem should be sent. This responses should be used for temporary /// conditions and the Retry-After: HTTP header should, if possible, contain /// the estimated time before the recovery of the service. The webmaster /// must also take care about the caching-related headers that are sent /// along with this response, as these temporary condition responses /// should usually not be cached. ServiceUnavailable = 503, /// 504 Gateway Timeout /// /// This error response is given when the server is acting as a gateway and /// cannot get a response in time. GatewayTimeout = 504, /// 505 HTTP Version Not Supported /// /// The HTTP version used in the request is not supported by the server. HttpVersionNotSupported = 505, /// 506 Variant Also Negotiates /// /// The server has an internal configuration error: the chosen variant /// resource is configured to engage in transparent content negotiation /// itself, and is therefore not a proper end point in the negotiation /// process. VariantAlsoNegotiates = 506, /// 507 Insufficient Storage /// /// The server is unable to store the representation needed to complete the /// request. InsufficientStorage = 507, /// 508 Loop Detected /// /// The server detected an infinite loop while processing the request. LoopDetected = 508, /// 510 Not Extended /// /// Further extensions to the request are required for the server to fulfil /// it. NotExtended = 510, /// 511 Network Authentication Required /// /// The 511 status code indicates that the client needs to authenticate to /// gain network access. NetworkAuthenticationRequired = 511, } impl StatusCode { /// Returns `true` if the status code is `1xx` range. /// /// If this returns `true` it indicates that the request was received, /// continuing process. pub fn is_informational(&self) -> bool { let num: u16 = (*self).into(); (100..200).contains(&num) } /// Returns `true` if the status code is the `2xx` range. /// /// If this returns `true` it indicates that the request was successfully /// received, understood, and accepted. pub fn is_success(&self) -> bool { let num: u16 = (*self).into(); (200..300).contains(&num) } /// Returns `true` if the status code is the `3xx` range. /// /// If this returns `true` it indicates that further action needs to be /// taken in order to complete the request. pub fn is_redirection(&self) -> bool { let num: u16 = (*self).into(); (300..400).contains(&num) } /// Returns `true` if the status code is the `4xx` range. /// /// If this returns `true` it indicates that the request contains bad syntax /// or cannot be fulfilled. pub fn is_client_error(&self) -> bool { let num: u16 = (*self).into(); (400..500).contains(&num) } /// Returns `true` if the status code is the `5xx` range. /// /// If this returns `true` it indicates that the server failed to fulfill an /// apparently valid request. pub fn is_server_error(&self) -> bool { let num: u16 = (*self).into(); (500..600).contains(&num) } /// The canonical reason for a given status code pub fn canonical_reason(&self) -> &'static str { match self { StatusCode::Continue => "Continue", StatusCode::SwitchingProtocols => "Switching Protocols", StatusCode::EarlyHints => "Early Hints", StatusCode::Ok => "OK", StatusCode::Created => "Created", StatusCode::Accepted => "Accepted", StatusCode::NonAuthoritativeInformation => "Non Authoritative Information", StatusCode::NoContent => "No Content", StatusCode::ResetContent => "Reset Content", StatusCode::PartialContent => "Partial Content", StatusCode::MultiStatus => "Multi-Status", StatusCode::ImUsed => "Im Used", StatusCode::MultipleChoice => "Multiple Choice", StatusCode::MovedPermanently => "Moved Permanently", StatusCode::Found => "Found", StatusCode::SeeOther => "See Other", StatusCode::NotModified => "Modified", StatusCode::TemporaryRedirect => "Temporary Redirect", StatusCode::PermanentRedirect => "Permanent Redirect", StatusCode::BadRequest => "Bad Request", StatusCode::Unauthorized => "Unauthorized", StatusCode::PaymentRequired => "Payment Required", StatusCode::Forbidden => "Forbidden", StatusCode::NotFound => "Not Found", StatusCode::MethodNotAllowed => "Method Not Allowed", StatusCode::NotAcceptable => "Not Acceptable", StatusCode::ProxyAuthenticationRequired => "Proxy Authentication Required", StatusCode::RequestTimeout => "Request Timeout", StatusCode::Conflict => "Conflict", StatusCode::Gone => "Gone", StatusCode::LengthRequired => "Length Required", StatusCode::PreconditionFailed => "Precondition Failed", StatusCode::PayloadTooLarge => "Payload Too Large", StatusCode::UriTooLong => "URI Too Long", StatusCode::UnsupportedMediaType => "Unsupported Media Type", StatusCode::RequestedRangeNotSatisfiable => "Requested Range Not Satisfiable", StatusCode::ExpectationFailed => "Expectation Failed", StatusCode::ImATeapot => "I'm a teapot", StatusCode::MisdirectedRequest => "Misdirected Request", StatusCode::UnprocessableEntity => "Unprocessable Entity", StatusCode::Locked => "Locked", StatusCode::FailedDependency => "Failed Dependency", StatusCode::TooEarly => "Too Early", StatusCode::UpgradeRequired => "Upgrade Required", StatusCode::PreconditionRequired => "Precondition Required", StatusCode::TooManyRequests => "Too Many Requests", StatusCode::RequestHeaderFieldsTooLarge => "Request Header Fields Too Large", StatusCode::UnavailableForLegalReasons => "Unavailable For Legal Reasons", StatusCode::InternalServerError => "Internal Server Error", StatusCode::NotImplemented => "Not Implemented", StatusCode::BadGateway => "Bad Gateway", StatusCode::ServiceUnavailable => "Service Unavailable", StatusCode::GatewayTimeout => "Gateway Timeout", StatusCode::HttpVersionNotSupported => "HTTP Version Not Supported", StatusCode::VariantAlsoNegotiates => "Variant Also Negotiates", StatusCode::InsufficientStorage => "Insufficient Storage", StatusCode::LoopDetected => "Loop Detected", StatusCode::NotExtended => "Not Extended", StatusCode::NetworkAuthenticationRequired => "Network Authentication Required", } } } impl Serialize for StatusCode { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let value: u16 = *self as u16; serializer.serialize_u16(value) } } struct StatusCodeU16Visitor; impl<'de> Visitor<'de> for StatusCodeU16Visitor { type Value = StatusCode; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a u16 representing the status code") } fn visit_i16(self, v: i16) -> Result where E: DeError, { self.visit_u16(v as u16) } fn visit_i32(self, v: i32) -> Result where E: DeError, { self.visit_u16(v as u16) } fn visit_i64(self, v: i64) -> Result where E: DeError, { self.visit_u16(v as u16) } fn visit_u16(self, v: u16) -> Result where E: DeError, { use std::convert::TryFrom; match StatusCode::try_from(v) { Ok(status_code) => Ok(status_code), Err(_) => Err(DeError::invalid_value( Unexpected::Unsigned(v as u64), &self, )), } } fn visit_u32(self, v: u32) -> Result where E: DeError, { self.visit_u16(v as u16) } fn visit_u64(self, v: u64) -> Result where E: DeError, { self.visit_u16(v as u16) } } impl<'de> Deserialize<'de> for StatusCode { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(StatusCodeU16Visitor) } } impl From for u16 { fn from(code: StatusCode) -> u16 { code as u16 } } impl std::convert::TryFrom for StatusCode { type Error = crate::Error; fn try_from(num: u16) -> Result { match num { 100 => Ok(StatusCode::Continue), 101 => Ok(StatusCode::SwitchingProtocols), 103 => Ok(StatusCode::EarlyHints), 200 => Ok(StatusCode::Ok), 201 => Ok(StatusCode::Created), 202 => Ok(StatusCode::Accepted), 203 => Ok(StatusCode::NonAuthoritativeInformation), 204 => Ok(StatusCode::NoContent), 205 => Ok(StatusCode::ResetContent), 206 => Ok(StatusCode::PartialContent), 207 => Ok(StatusCode::MultiStatus), 226 => Ok(StatusCode::ImUsed), 300 => Ok(StatusCode::MultipleChoice), 301 => Ok(StatusCode::MovedPermanently), 302 => Ok(StatusCode::Found), 303 => Ok(StatusCode::SeeOther), 304 => Ok(StatusCode::NotModified), 307 => Ok(StatusCode::TemporaryRedirect), 308 => Ok(StatusCode::PermanentRedirect), 400 => Ok(StatusCode::BadRequest), 401 => Ok(StatusCode::Unauthorized), 402 => Ok(StatusCode::PaymentRequired), 403 => Ok(StatusCode::Forbidden), 404 => Ok(StatusCode::NotFound), 405 => Ok(StatusCode::MethodNotAllowed), 406 => Ok(StatusCode::NotAcceptable), 407 => Ok(StatusCode::ProxyAuthenticationRequired), 408 => Ok(StatusCode::RequestTimeout), 409 => Ok(StatusCode::Conflict), 410 => Ok(StatusCode::Gone), 411 => Ok(StatusCode::LengthRequired), 412 => Ok(StatusCode::PreconditionFailed), 413 => Ok(StatusCode::PayloadTooLarge), 414 => Ok(StatusCode::UriTooLong), 415 => Ok(StatusCode::UnsupportedMediaType), 416 => Ok(StatusCode::RequestedRangeNotSatisfiable), 417 => Ok(StatusCode::ExpectationFailed), 418 => Ok(StatusCode::ImATeapot), 421 => Ok(StatusCode::MisdirectedRequest), 422 => Ok(StatusCode::UnprocessableEntity), 423 => Ok(StatusCode::Locked), 424 => Ok(StatusCode::FailedDependency), 425 => Ok(StatusCode::TooEarly), 426 => Ok(StatusCode::UpgradeRequired), 428 => Ok(StatusCode::PreconditionRequired), 429 => Ok(StatusCode::TooManyRequests), 431 => Ok(StatusCode::RequestHeaderFieldsTooLarge), 451 => Ok(StatusCode::UnavailableForLegalReasons), 500 => Ok(StatusCode::InternalServerError), 501 => Ok(StatusCode::NotImplemented), 502 => Ok(StatusCode::BadGateway), 503 => Ok(StatusCode::ServiceUnavailable), 504 => Ok(StatusCode::GatewayTimeout), 505 => Ok(StatusCode::HttpVersionNotSupported), 506 => Ok(StatusCode::VariantAlsoNegotiates), 507 => Ok(StatusCode::InsufficientStorage), 508 => Ok(StatusCode::LoopDetected), 510 => Ok(StatusCode::NotExtended), 511 => Ok(StatusCode::NetworkAuthenticationRequired), _ => crate::bail!("Invalid status code"), } } } impl PartialEq for u16 { fn eq(&self, other: &StatusCode) -> bool { *self == *other as u16 } } impl PartialEq for StatusCode { fn eq(&self, other: &u16) -> bool { *self as u16 == *other } } impl Display for StatusCode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self as u16) } } #[cfg(test)] mod test { use super::StatusCode; #[test] fn serde_as_u16() -> Result<(), serde_json::Error> { let status_code: StatusCode = serde_json::from_str("202")?; assert_eq!(StatusCode::Accepted, status_code); assert_eq!( Some(202), serde_json::to_value(&StatusCode::Accepted)?.as_u64() ); Ok(()) } } http-types-2.12.0/src/trace/mod.rs000064400000000000000000000011170072674642500150710ustar 00000000000000//! HTTP timings and traces. //! //! This module implements parsers and serializers for timing-related headers. //! These headers enable tracing and timing requests, and help answer the //! question of: _"Where is my program spending its time?"_ //! //! # Specifications //! //! - [W3C Trace-Context header](https://w3c.github.io/trace-context/) //! - [W3C Server-Timing header](https://w3c.github.io/server-timing/#the-server-timing-header-field) pub mod server_timing; mod trace_context; #[doc(inline)] pub use server_timing::{Metric, ServerTiming}; pub use trace_context::TraceContext; http-types-2.12.0/src/trace/server_timing/metric.rs000064400000000000000000000061150072674642500204550ustar 00000000000000use std::time::Duration; use crate::headers::HeaderValue; /// An individual entry into `ServerTiming`. // // # Implementation notes // // Four different cases are valid: // // 1. metric name only cache // 2. metric + value cache;dur=2.4 // 3. metric + desc cache;desc="Cache Read" // 4. metric + value + desc cache;desc="Cache Read";dur=23.2 // // Multiple different entries per line are supported; separated with a `,`. #[derive(Debug, Clone, Eq, PartialEq)] pub struct Metric { pub(crate) name: String, pub(crate) dur: Option, pub(crate) desc: Option, } impl Metric { /// Create a new instance of `Metric`. /// /// # Errors /// /// An error will be returned if the string values are invalid ASCII. pub fn new(name: String, dur: Option, desc: Option) -> crate::Result { crate::ensure!(name.is_ascii(), "Name should be valid ASCII"); if let Some(desc) = desc.as_ref() { crate::ensure!(desc.is_ascii(), "Description should be valid ASCII"); }; Ok(Self { name, dur, desc }) } /// The timing name. pub fn name(&self) -> &String { &self.name } /// The timing duration. pub fn duration(&self) -> Option { self.dur } /// The timing description. pub fn description(&self) -> Option<&str> { self.desc.as_deref() } } impl From for HeaderValue { fn from(entry: Metric) -> HeaderValue { let mut string = entry.name; // Format a `Duration` into the format that the spec expects. let f = |d: Duration| d.as_secs_f64() * 1000.0; match (entry.dur, entry.desc) { (Some(dur), Some(desc)) => { string.push_str(&format!("; dur={}; desc=\"{}\"", f(dur), desc)) } (Some(dur), None) => string.push_str(&format!("; dur={}", f(dur))), (None, Some(desc)) => string.push_str(&format!("; desc=\"{}\"", desc)), (None, None) => {} }; // SAFETY: we validate that the values are valid ASCII on creation. unsafe { HeaderValue::from_bytes_unchecked(string.into_bytes()) } } } #[cfg(test)] mod test { use super::*; use crate::headers::HeaderValue; use std::time::Duration; #[test] #[allow(clippy::redundant_clone)] fn encode() -> crate::Result<()> { let name = String::from("Server"); let dur = Duration::from_secs(1); let desc = String::from("A server timing"); let val: HeaderValue = Metric::new(name.clone(), None, None)?.into(); assert_eq!(val, "Server"); let val: HeaderValue = Metric::new(name.clone(), Some(dur), None)?.into(); assert_eq!(val, "Server; dur=1000"); let val: HeaderValue = Metric::new(name.clone(), None, Some(desc.clone()))?.into(); assert_eq!(val, r#"Server; desc="A server timing""#); let val: HeaderValue = Metric::new(name.clone(), Some(dur), Some(desc.clone()))?.into(); assert_eq!(val, r#"Server; dur=1000; desc="A server timing""#); Ok(()) } } http-types-2.12.0/src/trace/server_timing/mod.rs000064400000000000000000000152620072674642500177540ustar 00000000000000//! Metrics and descriptions for the given request-response cycle. //! //! # Examples //! //! ``` //! # fn main() -> http_types::Result<()> { //! # //! use http_types::Response; //! use http_types::trace::{ServerTiming, Metric}; //! //! let mut timings = ServerTiming::new(); //! timings.push(Metric::new("server".to_owned(), None, None)?); //! //! let mut res = Response::new(200); //! timings.apply(&mut res); //! //! let timings = ServerTiming::from_headers(res)?.unwrap(); //! let entry = timings.iter().next().unwrap(); //! assert_eq!(entry.name(), "server"); //! # //! # Ok(()) } //! ``` mod metric; mod parse; pub use metric::Metric; use parse::parse_header; use std::convert::AsMut; use std::fmt::Write; use std::iter::Iterator; use std::option; use std::slice; use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, SERVER_TIMING}; /// Metrics and descriptions for the given request-response cycle. /// /// # Specifications /// /// - [Server Timing (Working Draft)](https://w3c.github.io/server-timing/#the-server-timing-header-field) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::trace::{ServerTiming, Metric}; /// /// let mut timings = ServerTiming::new(); /// timings.push(Metric::new("server".to_owned(), None, None)?); /// /// let mut res = Response::new(200); /// timings.apply(&mut res); /// /// let timings = ServerTiming::from_headers(res)?.unwrap(); /// let entry = timings.iter().next().unwrap(); /// assert_eq!(entry.name(), "server"); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct ServerTiming { timings: Vec, } impl ServerTiming { /// Create a new instance of `ServerTiming`. pub fn new() -> Self { Self { timings: vec![] } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut timings = vec![]; let headers = match headers.as_ref().get(SERVER_TIMING) { Some(headers) => headers, None => return Ok(None), }; for value in headers { parse_header(value.as_str(), &mut timings)?; } Ok(Some(Self { timings })) } /// Sets the `Server-Timing` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(SERVER_TIMING, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { SERVER_TIMING } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, timing) in self.timings.iter().enumerate() { let timing: HeaderValue = timing.clone().into(); match n { 0 => write!(output, "{}", timing).unwrap(), _ => write!(output, ", {}", timing).unwrap(), }; } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Push an entry into the list of entries. pub fn push(&mut self, entry: Metric) { self.timings.push(entry); } /// An iterator visiting all server timings. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.timings.iter(), } } /// An iterator visiting all server timings. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.timings.iter_mut(), } } } impl IntoIterator for ServerTiming { type Item = Metric; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.timings.into_iter(), } } } impl<'a> IntoIterator for &'a ServerTiming { type Item = &'a Metric; type IntoIter = Iter<'a>; // #[inline]serv fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut ServerTiming { type Item = &'a mut Metric; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `ServerTiming`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = Metric; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `ServerTiming`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, Metric>, } impl<'a> Iterator for Iter<'a> { type Item = &'a Metric; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `ServerTiming`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, Metric>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut Metric; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for ServerTiming { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } #[cfg(test)] mod test { use super::*; use crate::headers::Headers; #[test] fn smoke() -> crate::Result<()> { let mut timings = ServerTiming::new(); timings.push(Metric::new("server".to_owned(), None, None)?); let mut headers = Headers::new(); timings.apply(&mut headers); let timings = ServerTiming::from_headers(headers)?.unwrap(); let entry = timings.iter().next().unwrap(); assert_eq!(entry.name(), "server"); Ok(()) } #[test] fn to_header_values() -> crate::Result<()> { let mut timings = ServerTiming::new(); timings.push(Metric::new("server".to_owned(), None, None)?); let mut headers = Headers::new(); timings.apply(&mut headers); let timings = ServerTiming::from_headers(headers)?.unwrap(); let entry = timings.iter().next().unwrap(); assert_eq!(entry.name(), "server"); Ok(()) } #[test] fn bad_request_on_parse_error() { let mut headers = Headers::new(); headers.insert(SERVER_TIMING, "server; "); let err = ServerTiming::from_headers(headers).unwrap_err(); assert_eq!(err.status(), 400); } } http-types-2.12.0/src/trace/server_timing/parse.rs000064400000000000000000000137340072674642500203110ustar 00000000000000use std::time::Duration; use super::Metric; use crate::{ensure, format_err, StatusCode}; /// Parse multiple entries from a single header. /// /// Each entry is comma-delimited. pub(super) fn parse_header(s: &str, entries: &mut Vec) -> crate::Result<()> { for part in s.trim().split(',') { let entry = parse_entry(part).map_err(|mut e| { e.set_status(StatusCode::BadRequest); e })?; entries.push(entry); } Ok(()) } /// Create an entry from a string. Parsing rules in ABNF are: // /// ```txt /// Server-Timing = #server-timing-metric /// server-timing-metric = metric-name *( OWS ";" OWS server-timing-param ) /// metric-name = token /// server-timing-param = server-timing-param-name OWS "=" OWS server-timing-param-value /// server-timing-param-name = token /// server-timing-param-value = token / quoted-string /// ``` // /// Source: https://w3c.github.io/server-timing/#the-server-timing-header-field fn parse_entry(s: &str) -> crate::Result { let mut parts = s.trim().split(';'); // Get the name. This is non-optional. let name = parts .next() .ok_or_else(|| format_err!("Server timing headers must include a name"))? .trim_end(); // We must extract these values from the k-v pairs that follow. let mut dur = None; let mut desc = None; for mut part in parts { ensure!( !part.is_empty(), "Server timing params cannot end with a trailing `;`" ); part = part.trim_start(); let mut params = part.split('='); let name = params .next() .ok_or_else(|| format_err!("Server timing params must have a name"))? .trim_end(); let mut value = params .next() .ok_or_else(|| format_err!("Server timing params must have a value"))? .trim_start(); match name { "dur" => { let millis: f64 = value.parse().map_err(|_| { format_err!("Server timing duration params must be a valid double-precision floating-point number.") })?; dur = Some(Duration::from_secs_f64(millis / 1000.0)); } "desc" => { // Ensure quotes line up, and strip them from the resulting output if value.starts_with('"') { value = &value[1..value.len()]; ensure!( value.ends_with('"'), "Server timing description params must use matching quotes" ); value = &value[0..value.len() - 1]; } else { ensure!( !value.ends_with('"'), "Server timing description params must use matching quotes" ); } desc = Some(value.to_string()); } _ => continue, } } Ok(Metric { name: name.to_string(), dur, desc, }) } #[cfg(test)] mod test { use super::*; #[test] fn decode_header() -> crate::Result<()> { // Metric name only. assert_entry("Server", "Server", None, None)?; assert_entry("Server ", "Server", None, None)?; assert_entry_err( "Server ;", "Server timing params cannot end with a trailing `;`", ); assert_entry_err( "Server; ", "Server timing params cannot end with a trailing `;`", ); // Metric name + param assert_entry("Server; dur=1000", "Server", Some(1000), None)?; assert_entry("Server; dur =1000", "Server", Some(1000), None)?; assert_entry("Server; dur= 1000", "Server", Some(1000), None)?; assert_entry("Server; dur = 1000", "Server", Some(1000), None)?; assert_entry_err( "Server; dur=1000;", "Server timing params cannot end with a trailing `;`", ); // Metric name + desc assert_entry(r#"DB; desc="a db""#, "DB", None, Some("a db"))?; assert_entry(r#"DB; desc ="a db""#, "DB", None, Some("a db"))?; assert_entry(r#"DB; desc= "a db""#, "DB", None, Some("a db"))?; assert_entry(r#"DB; desc = "a db""#, "DB", None, Some("a db"))?; assert_entry(r#"DB; desc=a_db"#, "DB", None, Some("a_db"))?; assert_entry_err( r#"DB; desc="db"#, "Server timing description params must use matching quotes", ); assert_entry_err( "Server; desc=a_db;", "Server timing params cannot end with a trailing `;`", ); // Metric name + dur + desc assert_entry( r#"Server; dur=1000; desc="a server""#, "Server", Some(1000), Some("a server"), )?; assert_entry_err( r#"Server; dur=1000; desc="a server";"#, "Server timing params cannot end with a trailing `;`", ); Ok(()) } #[test] fn decode_headers() -> crate::Result<()> { // Example from MDN. let mut entries = vec![]; parse_header("db;dur=53, app;dur=47.2", &mut entries)?; let e = &entries[0]; assert_eq!(e.name(), "db"); assert_eq!(e.duration(), Some(Duration::from_millis(53))); let e = &entries[1]; assert_eq!(e.name(), "app"); assert_eq!(e.duration(), Some(Duration::from_micros(47200))); Ok(()) } fn assert_entry_err(s: &str, msg: &str) { let err = parse_entry(s).unwrap_err(); assert_eq!(format!("{}", err), msg); } /// Assert an entry and all of its fields. fn assert_entry(s: &str, n: &str, du: Option, de: Option<&str>) -> crate::Result<()> { let e = parse_entry(s)?; assert_eq!(e.name(), n); assert_eq!(e.duration(), du.map(Duration::from_millis)); assert_eq!(e.description(), de); Ok(()) } } http-types-2.12.0/src/trace/trace_context.rs000064400000000000000000000210150072674642500171530ustar 00000000000000use rand::Rng; use std::fmt; use crate::headers::{HeaderName, HeaderValue, Headers, TRACEPARENT}; use crate::Status; /// Extract and apply [Trace-Context](https://w3c.github.io/trace-context/) headers. /// /// # Specifications /// /// - [Trace-Context (Working Draft)](https://w3c.github.io/trace-context/) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::trace::TraceContext; /// /// let mut res = http_types::Response::new(200); /// /// res.insert_header( /// "traceparent", /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" /// ); /// /// let context = TraceContext::from_headers(&res)?.unwrap(); /// /// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16); /// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16); /// /// assert_eq!(context.trace_id(), trace_id.unwrap()); /// assert_eq!(context.parent_id(), parent_id.ok()); /// assert_eq!(context.sampled(), true); /// # /// # Ok(()) } /// ``` #[derive(Debug)] pub struct TraceContext { id: u64, version: u8, trace_id: u128, parent_id: Option, flags: u8, } impl TraceContext { /// Generate a new TraceContext object without a parent. /// /// By default root TraceContext objects are sampled. /// To mark it unsampled, call `context.set_sampled(false)`. /// /// # Examples /// ``` /// use http_types::trace::TraceContext; /// /// let context = TraceContext::new(); /// /// assert_eq!(context.parent_id(), None); /// assert_eq!(context.sampled(), true); /// ``` pub fn new() -> Self { let mut rng = rand::thread_rng(); Self { id: rng.gen(), version: 0, trace_id: rng.gen(), parent_id: None, flags: 1, } } /// Create and return TraceContext object based on `traceparent` HTTP header. /// /// # Errors /// /// This function may error if the header is malformed. An error with a /// status code of `400: Bad Request` will be generated. /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::trace::TraceContext; /// /// let mut res = http_types::Response::new(200); /// res.insert_header( /// "traceparent", /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" /// ); /// /// let context = TraceContext::from_headers(&res)?.unwrap(); /// /// let trace_id = u128::from_str_radix("0af7651916cd43dd8448eb211c80319c", 16); /// let parent_id = u64::from_str_radix("00f067aa0ba902b7", 16); /// /// assert_eq!(context.trace_id(), trace_id.unwrap()); /// assert_eq!(context.parent_id(), parent_id.ok()); /// assert_eq!(context.sampled(), true); /// # /// # Ok(()) } /// ``` pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = headers.as_ref(); let mut rng = rand::thread_rng(); let traceparent = match headers.get(TRACEPARENT) { Some(header) => header, None => return Ok(None), }; let parts: Vec<&str> = traceparent.as_str().split('-').collect(); Ok(Some(Self { id: rng.gen(), version: u8::from_str_radix(parts[0], 16)?, trace_id: u128::from_str_radix(parts[1], 16).status(400)?, parent_id: Some(u64::from_str_radix(parts[2], 16).status(400)?), flags: u8::from_str_radix(parts[3], 16).status(400)?, })) } /// Add the traceparent header to the http headers /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::trace::TraceContext; /// use http_types::{Request, Response, Url, Method}; /// /// let mut req = Request::new(Method::Get, Url::parse("https://example.com").unwrap()); /// req.insert_header( /// "traceparent", /// "00-0af7651916cd43dd8448eb211c80319c-00f067aa0ba902b7-01" /// ); /// /// let parent = TraceContext::from_headers(&req)?.unwrap(); /// /// let mut res = Response::new(200); /// parent.apply(&mut res); /// /// let child = TraceContext::from_headers(&res)?.unwrap(); /// /// assert_eq!(child.version(), parent.version()); /// assert_eq!(child.trace_id(), parent.trace_id()); /// assert_eq!(child.parent_id(), Some(parent.id())); /// # /// # Ok(()) } /// ``` pub fn apply(&self, mut headers: impl AsMut) { let headers = headers.as_mut(); headers.insert(TRACEPARENT, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { TRACEPARENT } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let output = format!("{}", self); unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// Generate a child of the current TraceContext and return it. /// /// The child will have a new randomly genrated `id` and its `parent_id` will be set to the /// `id` of this TraceContext. pub fn child(&self) -> Self { let mut rng = rand::thread_rng(); Self { id: rng.gen(), version: self.version, trace_id: self.trace_id, parent_id: Some(self.id), flags: self.flags, } } /// Return the id of the TraceContext. pub fn id(&self) -> u64 { self.id } /// Return the version of the TraceContext spec used. /// /// You probably don't need this. pub fn version(&self) -> u8 { self.version } /// Return the trace id of the TraceContext. /// /// All children will have the same `trace_id`. pub fn trace_id(&self) -> u128 { self.trace_id } /// Return the id of the parent TraceContext. #[inline] pub fn parent_id(&self) -> Option { self.parent_id } /// Returns true if the trace is sampled /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::trace::TraceContext; /// use http_types::Response; /// /// let mut res = Response::new(200); /// res.insert_header("traceparent", "00-00000000000000000000000000000001-0000000000000002-01"); /// let context = TraceContext::from_headers(&res)?.unwrap(); /// assert_eq!(context.sampled(), true); /// # /// # Ok(()) } /// ``` pub fn sampled(&self) -> bool { (self.flags & 0b00000001) == 1 } /// Change sampled flag /// /// # Examples /// /// ``` /// use http_types::trace::TraceContext; /// /// let mut context = TraceContext::new(); /// assert_eq!(context.sampled(), true); /// context.set_sampled(false); /// assert_eq!(context.sampled(), false); /// ``` pub fn set_sampled(&mut self, sampled: bool) { let x = sampled as u8; self.flags ^= (x ^ self.flags) & (1 << 0); } } impl fmt::Display for TraceContext { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{:02x}-{:032x}-{:016x}-{:02x}", self.version, self.trace_id, self.id, self.flags ) } } #[cfg(test)] mod test { use super::*; #[test] fn default() -> crate::Result<()> { let mut headers = crate::Headers::new(); headers.insert(TRACEPARENT, "00-01-deadbeef-00"); let context = TraceContext::from_headers(&mut headers)?.unwrap(); assert_eq!(context.version(), 0); assert_eq!(context.trace_id(), 1); assert_eq!(context.parent_id().unwrap(), 3735928559); assert_eq!(context.flags, 0); assert!(!context.sampled()); Ok(()) } #[test] fn no_header() { let context = TraceContext::new(); assert_eq!(context.version(), 0); assert_eq!(context.parent_id(), None); assert_eq!(context.flags, 1); assert!(context.sampled()); } #[test] fn not_sampled() -> crate::Result<()> { let mut headers = crate::Headers::new(); headers.insert(TRACEPARENT, "00-01-02-00"); let context = TraceContext::from_headers(&mut headers)?.unwrap(); assert!(!context.sampled()); Ok(()) } #[test] fn sampled() -> crate::Result<()> { let mut headers = crate::Headers::new(); headers.insert(TRACEPARENT, "00-01-02-01"); let context = TraceContext::from_headers(&mut headers)?.unwrap(); assert!(context.sampled()); Ok(()) } } http-types-2.12.0/src/trailers.rs000064400000000000000000000162060072674642500150460ustar 00000000000000//! HTTP trailing headers. //! //! Trailing headers are headers that are send *after* the body payload has //! been sent. This is for example useful for sending integrity checks of //! streamed payloads that are computed on the fly. //! //! The way trailing headers are sent over the wire varies per protocol. But in //! `http-types` we provide a `Trailers` struct that's used to contain the headers. //! //! To send trailing headers, see `Request::{`[`send_trailers, `][req_send] //! [`recv_trailers`][req_recv]`}` and //! `Response::{`[`send_trailers, `][res_send][`recv_trailers`][res_recv]`}`. //! //! [req_send]: ../struct.Request.html#method.send_trailers //! [req_recv]: ../struct.Request.html#method.recv_trailers //! [res_send]: ../struct.Response.html#method.send_trailers //! [res_recv]: ../struct.Response.html#method.recv_trailers //! //! ## Example //! //! ``` //! # fn main() -> Result<(), Box> { //! # async_std::task::block_on(async { //! # //! use http_types::{Url, Method, Request, Trailers}; //! use http_types::headers::{HeaderName, HeaderValue}; //! use async_std::task; //! use std::str::FromStr; //! //! let mut req = Request::new(Method::Get, Url::parse("https://example.com").unwrap()); //! //! let sender = req.send_trailers(); //! let mut trailers = Trailers::new(); //! trailers.insert("Content-Type", "text/plain"); //! //! task::spawn(async move { //! let trailers = req.recv_trailers().await; //! # drop(trailers) //! }); //! //! sender.send(trailers).await; //! # //! # Ok(()) })} //! ``` //! //! ## See Also //! - [MDN HTTP Headers: Trailer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer) //! - [HTTP/2 spec: HTTP Sequence](https://http2.github.io/http2-spec/#HttpSequence) use crate::headers::{ HeaderName, HeaderValues, Headers, Iter, IterMut, Names, ToHeaderValues, Values, }; use futures_lite::Stream; use std::convert::Into; use std::future::Future; use std::ops::{Deref, DerefMut, Index}; use std::pin::Pin; use std::task::{Context, Poll}; /// A collection of trailing HTTP headers. #[derive(Debug)] pub struct Trailers { headers: Headers, } impl Trailers { /// Create a new instance of `Trailers`. pub fn new() -> Self { Self { headers: Headers::new(), } } /// Insert a header into the headers. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), Box> { /// # /// use http_types::Trailers; /// /// let mut trailers = Trailers::new(); /// trailers.insert("Content-Type", "text/plain"); /// # /// # Ok(()) } /// ``` pub fn insert( &mut self, name: impl Into, values: impl ToHeaderValues, ) -> Option { self.headers.insert(name, values) } /// Append a header to the headers. /// /// Unlike `insert` this function will not override the contents of a header, but insert a /// header if there aren't any. Or else append to the existing list of headers. /// /// # Examples /// /// ``` /// # fn main() -> Result<(), Box> { /// # /// use http_types::Trailers; /// /// let mut trailers = Trailers::new(); /// trailers.append("Content-Type", "text/plain"); /// # /// # Ok(()) } /// ``` pub fn append(&mut self, name: impl Into, values: impl ToHeaderValues) { self.headers.append(name, values) } /// Get a reference to a header. pub fn get(&self, name: impl Into) -> Option<&HeaderValues> { self.headers.get(name) } /// Get a mutable reference to a header. pub fn get_mut(&mut self, name: impl Into) -> Option<&mut HeaderValues> { self.headers.get_mut(name) } /// Remove a header. pub fn remove(&mut self, name: impl Into) -> Option { self.headers.remove(name) } /// An iterator visiting all header pairs in arbitrary order. pub fn iter(&self) -> Iter<'_> { self.headers.iter() } /// An iterator visiting all header pairs in arbitrary order, with mutable references to the /// values. pub fn iter_mut(&mut self) -> IterMut<'_> { self.headers.iter_mut() } /// An iterator visiting all header names in arbitrary order. pub fn names(&self) -> Names<'_> { self.headers.names() } /// An iterator visiting all header values in arbitrary order. pub fn values(&self) -> Values<'_> { self.headers.values() } } impl Clone for Trailers { fn clone(&self) -> Self { Self { headers: Headers { headers: self.headers.headers.clone(), }, } } } impl Deref for Trailers { type Target = Headers; fn deref(&self) -> &Self::Target { &self.headers } } impl DerefMut for Trailers { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.headers } } impl Index for Trailers { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Trailers`. #[inline] fn index(&self, name: HeaderName) -> &HeaderValues { self.headers.index(name) } } impl Index<&str> for Trailers { type Output = HeaderValues; /// Returns a reference to the value corresponding to the supplied name. /// /// # Panics /// /// Panics if the name is not present in `Trailers`. #[inline] fn index(&self, name: &str) -> &HeaderValues { self.headers.index(name) } } /// The sending half of a channel to send trailers. /// /// Unlike `async_channel::Sender` the `send` method on this type can only be /// called once, and cannot be cloned. That's because only a single instance of /// `Trailers` should be created. #[derive(Debug)] pub struct Sender { sender: async_channel::Sender, } impl Sender { /// Create a new instance of `Sender`. #[doc(hidden)] pub fn new(sender: async_channel::Sender) -> Self { Self { sender } } /// Send a `Trailer`. /// /// The channel will be consumed after having sent trailers. pub async fn send(self, trailers: Trailers) { let _ = self.sender.send(trailers).await; } } /// The receiving half of a channel to send trailers. /// /// Unlike `async_channel::Sender` the `send` method on this type can only be /// called once, and cannot be cloned. That's because only a single instance of /// `Trailers` should be created. #[must_use = "Futures do nothing unless polled or .awaited"] #[derive(Debug)] pub struct Receiver { receiver: async_channel::Receiver, } impl Receiver { /// Create a new instance of `Receiver`. pub(crate) fn new(receiver: async_channel::Receiver) -> Self { Self { receiver } } } impl Future for Receiver { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.receiver).poll_next(cx) } } http-types-2.12.0/src/transfer/encoding.rs000064400000000000000000000034130072674642500166270ustar 00000000000000use crate::headers::HeaderValue; use std::fmt::{self, Display}; /// Available compression algorithms. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding#Directives) #[non_exhaustive] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Encoding { /// Send a series of chunks. Chunked, /// The Gzip encoding. Gzip, /// The Deflate encoding. Deflate, /// The Brotli encoding. Brotli, /// The Zstd encoding. Zstd, /// No encoding. Identity, } impl Encoding { /// Parses a given string into its corresponding encoding. pub(crate) fn from_str(s: &str) -> Option { let s = s.trim(); // We're dealing with an empty string. if s.is_empty() { return None; } match s { "chunked" => Some(Encoding::Chunked), "gzip" => Some(Encoding::Gzip), "deflate" => Some(Encoding::Deflate), "br" => Some(Encoding::Brotli), "zstd" => Some(Encoding::Zstd), "identity" => Some(Encoding::Identity), _ => None, } } } impl Display for Encoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Encoding::Gzip => write!(f, "gzip"), Encoding::Deflate => write!(f, "deflate"), Encoding::Brotli => write!(f, "br"), Encoding::Zstd => write!(f, "zstd"), Encoding::Identity => write!(f, "identity"), Encoding::Chunked => write!(f, "chunked"), } } } impl From for HeaderValue { fn from(directive: Encoding) -> Self { let s = directive.to_string(); unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } } } http-types-2.12.0/src/transfer/encoding_proposal.rs000064400000000000000000000102340072674642500205450ustar 00000000000000use crate::ensure; use crate::headers::HeaderValue; use crate::transfer::Encoding; use crate::utils::parse_weight; use std::cmp::{Ordering, PartialEq}; use std::ops::{Deref, DerefMut}; /// A proposed `Encoding` in `AcceptEncoding`. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE#Directives) #[derive(Debug, Clone, Copy, PartialEq)] pub struct EncodingProposal { /// The proposed encoding. pub(crate) encoding: Encoding, /// The weight of the proposal. /// /// This is a number between 0.0 and 1.0, and is max 3 decimal points. weight: Option, } impl EncodingProposal { /// Create a new instance of `EncodingProposal`. pub fn new(encoding: impl Into, weight: Option) -> crate::Result { if let Some(weight) = weight { ensure!( weight.is_sign_positive() && weight <= 1.0, "EncodingProposal should have a weight between 0.0 and 1.0" ) } Ok(Self { encoding: encoding.into(), weight, }) } /// Get the proposed encoding. pub fn encoding(&self) -> &Encoding { &self.encoding } /// Get the weight of the proposal. pub fn weight(&self) -> Option { self.weight } pub(crate) fn from_str(s: &str) -> crate::Result> { let mut parts = s.split(';'); let encoding = match Encoding::from_str(parts.next().unwrap()) { Some(encoding) => encoding, None => return Ok(None), }; let weight = parts.next().map(parse_weight).transpose()?; Ok(Some(Self::new(encoding, weight)?)) } } impl From for EncodingProposal { fn from(encoding: Encoding) -> Self { Self { encoding, weight: None, } } } impl PartialEq for EncodingProposal { fn eq(&self, other: &Encoding) -> bool { self.encoding == *other } } impl PartialEq for &EncodingProposal { fn eq(&self, other: &Encoding) -> bool { self.encoding == *other } } impl Deref for EncodingProposal { type Target = Encoding; fn deref(&self) -> &Self::Target { &self.encoding } } impl DerefMut for EncodingProposal { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.encoding } } // NOTE: Firefox populates Accept-Encoding as `gzip, deflate, br`. This means // when parsing encodings we should choose the last value in the list under // equal weights. This impl doesn't know which value was passed later, so that // behavior needs to be handled separately. // // NOTE: This comparison does not include a notion of `*` (any value is valid). // that needs to be handled separately. impl PartialOrd for EncodingProposal { fn partial_cmp(&self, other: &Self) -> Option { match (self.weight, other.weight) { (Some(left), Some(right)) => left.partial_cmp(&right), (Some(_), None) => Some(Ordering::Greater), (None, Some(_)) => Some(Ordering::Less), (None, None) => None, } } } impl From for HeaderValue { fn from(entry: EncodingProposal) -> HeaderValue { let s = match entry.weight { Some(weight) => format!("{};q={:.3}", entry.encoding, weight), None => entry.encoding.to_string(), }; unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } } } #[cfg(test)] mod test { use super::*; #[test] fn smoke() { let _ = EncodingProposal::new(Encoding::Gzip, Some(0.0)).unwrap(); let _ = EncodingProposal::new(Encoding::Gzip, Some(0.5)).unwrap(); let _ = EncodingProposal::new(Encoding::Gzip, Some(1.0)).unwrap(); } #[test] fn error_code_500() { let err = EncodingProposal::new(Encoding::Gzip, Some(1.1)).unwrap_err(); assert_eq!(err.status(), 500); let err = EncodingProposal::new(Encoding::Gzip, Some(-0.1)).unwrap_err(); assert_eq!(err.status(), 500); let err = EncodingProposal::new(Encoding::Gzip, Some(-0.0)).unwrap_err(); assert_eq!(err.status(), 500); } } http-types-2.12.0/src/transfer/mod.rs000064400000000000000000000005160072674642500156210ustar 00000000000000//! HTTP transfer headers. //! //! [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#Transfer_coding) mod encoding; mod encoding_proposal; mod te; mod transfer_encoding; pub use encoding::Encoding; pub use encoding_proposal::EncodingProposal; pub use te::TE; pub use transfer_encoding::TransferEncoding; http-types-2.12.0/src/transfer/te.rs000064400000000000000000000300570072674642500154550ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING}; use crate::transfer::{Encoding, EncodingProposal, TransferEncoding}; use crate::utils::sort_by_weight; use crate::{Error, StatusCode}; use std::fmt::{self, Debug, Write}; use std::option; use std::slice; /// Client header advertising the transfer encodings the user agent is willing to /// accept. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/TE) /// /// # Specifications /// /// - [RFC 7230, section 4.3: TE](https://tools.ietf.org/html/rfc7230#section-4.3) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::transfer::{TE, TransferEncoding, Encoding, EncodingProposal}; /// use http_types::Response; /// /// let mut te = TE::new(); /// te.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); /// te.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?); /// te.push(EncodingProposal::new(Encoding::Identity, None)?); /// /// let mut res = Response::new(200); /// let encoding = te.negotiate(&[Encoding::Brotli, Encoding::Gzip])?; /// encoding.apply(&mut res); /// /// assert_eq!(res["Content-Encoding"], "br"); /// # /// # Ok(()) } /// ``` #[allow(clippy::upper_case_acronyms)] pub struct TE { wildcard: bool, entries: Vec, } impl TE { /// Create a new instance of `TE`. pub fn new() -> Self { Self { entries: vec![], wildcard: false, } } /// Create an instance of `TE` from a `Headers` instance. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let mut entries = vec![]; let headers = match headers.as_ref().get(ACCEPT_ENCODING) { Some(headers) => headers, None => return Ok(None), }; let mut wildcard = false; for value in headers { for part in value.as_str().trim().split(',') { let part = part.trim(); // Handle empty strings, and wildcard directives. if part.is_empty() { continue; } else if part == "*" { wildcard = true; continue; } // Try and parse a directive from a str. If the directive is // unkown we skip it. if let Some(entry) = EncodingProposal::from_str(part)? { entries.push(entry); } } } Ok(Some(Self { entries, wildcard })) } /// Push a directive into the list of entries. pub fn push(&mut self, prop: impl Into) { self.entries.push(prop.into()); } /// Returns `true` if a wildcard directive was passed. pub fn wildcard(&self) -> bool { self.wildcard } /// Set the wildcard directive. pub fn set_wildcard(&mut self, wildcard: bool) { self.wildcard = wildcard } /// Sort the header directives by weight. /// /// Headers with a higher `q=` value will be returned first. If two /// directives have the same weight, the directive that was declared later /// will be returned first. pub fn sort(&mut self) { sort_by_weight(&mut self.entries); } /// Determine the most suitable `Transfer-Encoding` encoding. /// /// # Errors /// /// If no suitable encoding is found, an error with the status of `406` will be returned. pub fn negotiate(&mut self, available: &[Encoding]) -> crate::Result { // Start by ordering the encodings. self.sort(); // Try and find the first encoding that matches. for encoding in &self.entries { if available.contains(encoding) { return Ok(encoding.into()); } } // If no encoding matches and wildcard is set, send whichever encoding we got. if self.wildcard { if let Some(encoding) = available.iter().next() { return Ok(encoding.into()); } } let mut err = Error::new_adhoc("No suitable Transfer-Encoding found"); err.set_status(StatusCode::NotAcceptable); Err(err) } /// Sets the `Accept-Encoding` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(ACCEPT_ENCODING, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { ACCEPT_ENCODING } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { let mut output = String::new(); for (n, directive) in self.entries.iter().enumerate() { let directive: HeaderValue = (*directive).into(); match n { 0 => write!(output, "{}", directive).unwrap(), _ => write!(output, ", {}", directive).unwrap(), }; } if self.wildcard { match output.len() { 0 => write!(output, "*").unwrap(), _ => write!(output, ", *").unwrap(), } } // SAFETY: the internal string is validated to be ASCII. unsafe { HeaderValue::from_bytes_unchecked(output.into()) } } /// An iterator visiting all entries. pub fn iter(&self) -> Iter<'_> { Iter { inner: self.entries.iter(), } } /// An iterator visiting all entries. pub fn iter_mut(&mut self) -> IterMut<'_> { IterMut { inner: self.entries.iter_mut(), } } } impl IntoIterator for TE { type Item = EncodingProposal; type IntoIter = IntoIter; #[inline] fn into_iter(self) -> Self::IntoIter { IntoIter { inner: self.entries.into_iter(), } } } impl<'a> IntoIterator for &'a TE { type Item = &'a EncodingProposal; type IntoIter = Iter<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut TE { type Item = &'a mut EncodingProposal; type IntoIter = IterMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } /// A borrowing iterator over entries in `TE`. #[derive(Debug)] pub struct IntoIter { inner: std::vec::IntoIter, } impl Iterator for IntoIter { type Item = EncodingProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A lending iterator over entries in `TE`. #[derive(Debug)] pub struct Iter<'a> { inner: slice::Iter<'a, EncodingProposal>, } impl<'a> Iterator for Iter<'a> { type Item = &'a EncodingProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } /// A mutable iterator over entries in `TE`. #[derive(Debug)] pub struct IterMut<'a> { inner: slice::IterMut<'a, EncodingProposal>, } impl<'a> Iterator for IterMut<'a> { type Item = &'a mut EncodingProposal; fn next(&mut self) -> Option { self.inner.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.inner.size_hint() } } impl ToHeaderValues for TE { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Debug for TE { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); for directive in &self.entries { list.entry(directive); } list.finish() } } #[cfg(test)] mod test { use super::*; use crate::transfer::Encoding; use crate::Response; #[test] fn smoke() -> crate::Result<()> { let mut accept = TE::new(); accept.push(Encoding::Gzip); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); Ok(()) } #[test] fn wildcard() -> crate::Result<()> { let mut accept = TE::new(); accept.set_wildcard(true); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); Ok(()) } #[test] fn wildcard_and_header() -> crate::Result<()> { let mut accept = TE::new(); accept.push(Encoding::Gzip); accept.set_wildcard(true); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); assert!(accept.wildcard()); assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip); Ok(()) } #[test] fn iter() -> crate::Result<()> { let mut accept = TE::new(); accept.push(Encoding::Gzip); accept.push(Encoding::Brotli); let mut headers = Response::new(200); accept.apply(&mut headers); let accept = TE::from_headers(headers)?.unwrap(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), Encoding::Gzip); assert_eq!(accept.next().unwrap(), Encoding::Brotli); Ok(()) } #[test] fn reorder_based_on_weight() -> crate::Result<()> { let mut accept = TE::new(); accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?); accept.push(EncodingProposal::new(Encoding::Identity, None)?); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut headers = Response::new(200); accept.apply(&mut headers); let mut accept = TE::from_headers(headers)?.unwrap(); accept.sort(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), Encoding::Brotli); assert_eq!(accept.next().unwrap(), Encoding::Gzip); assert_eq!(accept.next().unwrap(), Encoding::Identity); Ok(()) } #[test] fn reorder_based_on_weight_and_location() -> crate::Result<()> { let mut accept = TE::new(); accept.push(EncodingProposal::new(Encoding::Identity, None)?); accept.push(EncodingProposal::new(Encoding::Gzip, None)?); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let mut res = Response::new(200); accept.apply(&mut res); let mut accept = TE::from_headers(res)?.unwrap(); accept.sort(); let mut accept = accept.iter(); assert_eq!(accept.next().unwrap(), Encoding::Brotli); assert_eq!(accept.next().unwrap(), Encoding::Gzip); assert_eq!(accept.next().unwrap(), Encoding::Identity); Ok(()) } #[test] fn negotiate() -> crate::Result<()> { let mut accept = TE::new(); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?); accept.push(EncodingProposal::new(Encoding::Identity, None)?); assert_eq!( accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?, Encoding::Brotli, ); Ok(()) } #[test] fn negotiate_not_acceptable() -> crate::Result<()> { let mut accept = TE::new(); let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err(); assert_eq!(err.status(), 406); let mut accept = TE::new(); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err(); assert_eq!(err.status(), 406); Ok(()) } #[test] fn negotiate_wildcard() -> crate::Result<()> { let mut accept = TE::new(); accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?); accept.set_wildcard(true); assert_eq!(accept.negotiate(&[Encoding::Gzip])?, Encoding::Gzip); Ok(()) } } http-types-2.12.0/src/transfer/transfer_encoding.rs000064400000000000000000000073400072674642500205360ustar 00000000000000use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CONTENT_ENCODING}; use crate::transfer::{Encoding, EncodingProposal}; use std::fmt::{self, Debug}; use std::ops::{Deref, DerefMut}; use std::option; /// The form of encoding used to safely transfer the payload body to the user. /// /// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding) /// /// # Specifications /// /// - [RFC 7230, section 3.3.1: Transfer-Encoding](https://tools.ietf.org/html/rfc7230#section-3.3.1) /// /// # Examples /// /// ``` /// # fn main() -> http_types::Result<()> { /// # /// use http_types::Response; /// use http_types::transfer::{TransferEncoding, Encoding}; /// let mut encoding = TransferEncoding::new(Encoding::Chunked); /// /// let mut res = Response::new(200); /// encoding.apply(&mut res); /// /// let encoding = TransferEncoding::from_headers(res)?.unwrap(); /// assert_eq!(encoding, &Encoding::Chunked); /// # /// # Ok(()) } /// ``` pub struct TransferEncoding { inner: Encoding, } impl TransferEncoding { /// Create a new instance of `CacheControl`. pub fn new(encoding: Encoding) -> Self { Self { inner: encoding } } /// Create a new instance from headers. pub fn from_headers(headers: impl AsRef) -> crate::Result> { let headers = match headers.as_ref().get(CONTENT_ENCODING) { Some(headers) => headers, None => return Ok(None), }; let mut inner = None; for value in headers { if let Some(entry) = Encoding::from_str(value.as_str()) { inner = Some(entry); } } let inner = inner.expect("Headers instance with no entries found"); Ok(Some(Self { inner })) } /// Sets the `Content-Encoding` header. pub fn apply(&self, mut headers: impl AsMut) { headers.as_mut().insert(CONTENT_ENCODING, self.value()); } /// Get the `HeaderName`. pub fn name(&self) -> HeaderName { CONTENT_ENCODING } /// Get the `HeaderValue`. pub fn value(&self) -> HeaderValue { self.inner.into() } /// Access the encoding kind. pub fn encoding(&self) -> Encoding { self.inner } } impl ToHeaderValues for TransferEncoding { type Iter = option::IntoIter; fn to_header_values(&self) -> crate::Result { // A HeaderValue will always convert into itself. Ok(self.value().to_header_values().unwrap()) } } impl Deref for TransferEncoding { type Target = Encoding; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for TransferEncoding { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } impl PartialEq for TransferEncoding { fn eq(&self, other: &Encoding) -> bool { &self.inner == other } } impl PartialEq<&Encoding> for TransferEncoding { fn eq(&self, other: &&Encoding) -> bool { &&self.inner == other } } impl From for TransferEncoding { fn from(encoding: Encoding) -> Self { Self { inner: encoding } } } impl From<&Encoding> for TransferEncoding { fn from(encoding: &Encoding) -> Self { Self { inner: *encoding } } } impl From for TransferEncoding { fn from(encoding: EncodingProposal) -> Self { Self { inner: encoding.encoding, } } } impl From<&EncodingProposal> for TransferEncoding { fn from(encoding: &EncodingProposal) -> Self { Self { inner: encoding.encoding, } } } impl Debug for TransferEncoding { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } } http-types-2.12.0/src/upgrade/connection.rs000064400000000000000000000032150072674642500170030ustar 00000000000000use futures_lite::{io, prelude::*}; use std::fmt::{self, Debug}; use std::pin::Pin; use std::task::{Context, Poll}; /// An upgraded HTTP connection. pub struct Connection { inner: Box, } impl Debug for Connection { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let inner = "Box"; f.debug_struct("Connection").field("inner", &inner).finish() } } impl Connection { /// Create a new instance of `Connection`. pub fn new(t: T) -> Self where T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, { Self { inner: Box::new(t) } } } /// Trait to signal the requirements for an underlying connection type. pub trait InnerConnection: AsyncRead + AsyncWrite + Send + Sync + Unpin {} impl InnerConnection for T {} impl AsyncRead for Connection { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { Pin::new(&mut self.inner).poll_read(cx, buf) } } impl AsyncWrite for Connection { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut self.inner).poll_write(cx, buf) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.inner).poll_close(cx) } } http-types-2.12.0/src/upgrade/mod.rs000064400000000000000000000013650072674642500154270ustar 00000000000000//! HTTP protocol upgrades. //! //! In HTTP it's not uncommon to convert from one protocol to another. For //! example `HTTP/1.1` can upgrade a connection to websockets using the //! [upgrade header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism), //! while `HTTP/2` uses [a custom //! handshake](https://tools.ietf.org/html/rfc8441#section-5.1). Regardless of //! the HTTP version, changing protocols always involves some handshake, //! after which it is turned into a stream of bytes. This module provides //! primitives for upgrading from HTTP request-response pairs to alternate //! protocols. mod connection; mod receiver; mod sender; pub use connection::Connection; pub use receiver::Receiver; pub use sender::Sender; http-types-2.12.0/src/upgrade/receiver.rs000064400000000000000000000013740072674642500164540ustar 00000000000000use futures_lite::Stream; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use crate::upgrade::Connection; /// The receiving half of a channel to send an upgraded connection. #[must_use = "Futures do nothing unless polled or .awaited"] #[derive(Debug)] pub struct Receiver { receiver: async_channel::Receiver, } impl Receiver { /// Create a new instance of `Receiver`. #[allow(unused)] pub(crate) fn new(receiver: async_channel::Receiver) -> Self { Self { receiver } } } impl Future for Receiver { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.receiver).poll_next(cx) } } http-types-2.12.0/src/upgrade/sender.rs000064400000000000000000000014010072674642500161170ustar 00000000000000use crate::upgrade::Connection; /// The sending half of a channel to send an upgraded connection. /// /// Unlike `async_channel::Sender` the `send` method on this type can only be /// called once, and cannot be cloned. That's because only a single instance of /// `Connection` should be created. #[derive(Debug)] pub struct Sender { sender: async_channel::Sender, } impl Sender { /// Create a new instance of `Sender`. #[doc(hidden)] pub fn new(sender: async_channel::Sender) -> Self { Self { sender } } /// Send a `Connection`. /// /// The channel will be consumed after having sent the connection. pub async fn send(self, conn: Connection) { let _ = self.sender.send(conn).await; } } http-types-2.12.0/src/utils/date.rs000064400000000000000000000352220072674642500152750ustar 00000000000000use std::fmt::{self, Display, Formatter}; use std::str::{from_utf8, FromStr}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::StatusCode; use crate::{bail, ensure, format_err}; const IMF_FIXDATE_LENGTH: usize = 29; const RFC850_MAX_LENGTH: usize = 23; const ASCTIME_LENGTH: usize = 24; const YEAR_9999_SECONDS: u64 = 253402300800; const SECONDS_IN_DAY: u64 = 86400; const SECONDS_IN_HOUR: u64 = 3600; /// Format using the `Display` trait. /// Convert timestamp into/from `SytemTime` to use. /// Supports comparison and sorting. #[derive(Copy, Clone, Debug, Eq)] pub(crate) struct HttpDate { /// 0...59 second: u8, /// 0...59 minute: u8, /// 0...23 hour: u8, /// 1...31 day: u8, /// 1...12 month: u8, /// 1970...9999 year: u16, /// 1...7 week_day: u8, } /// Parse a date from an HTTP header field. /// /// Supports the preferred IMF-fixdate and the legacy RFC 805 and /// ascdate formats. Two digit years are mapped to dates between /// 1970 and 2069. pub(crate) fn parse_http_date(s: &str) -> crate::Result { s.parse::().map(|d| d.into()).map_err(|mut e| { e.set_status(StatusCode::BadRequest); e }) } /// Format a date to be used in a HTTP header field. /// /// Dates are formatted as IMF-fixdate: `Fri, 15 May 2015 15:34:21 GMT`. pub(crate) fn fmt_http_date(d: SystemTime) -> String { format!("{}", HttpDate::from(d)) } impl HttpDate { fn is_valid(self) -> bool { self.second < 60 && self.minute < 60 && self.hour < 24 && self.day > 0 && self.day < 32 && self.month > 0 && self.month <= 12 && self.year >= 1970 && self.year <= 9999 && self.week_day >= 1 && self.week_day < 8 } } fn parse_imf_fixdate(s: &[u8]) -> crate::Result { // Example: `Sun, 06 Nov 1994 08:49:37 GMT` if s.len() != IMF_FIXDATE_LENGTH || &s[25..] != b" GMT" || s[16] != b' ' || s[19] != b':' || s[22] != b':' { bail!("Date time not in imf fixdate format"); } Ok(HttpDate { second: from_utf8(&s[23..25])?.parse()?, minute: from_utf8(&s[20..22])?.parse()?, hour: from_utf8(&s[17..19])?.parse()?, day: from_utf8(&s[5..7])?.parse()?, month: match &s[7..12] { b" Jan " => 1, b" Feb " => 2, b" Mar " => 3, b" Apr " => 4, b" May " => 5, b" Jun " => 6, b" Jul " => 7, b" Aug " => 8, b" Sep " => 9, b" Oct " => 10, b" Nov " => 11, b" Dec " => 12, _ => bail!("Invalid Month"), }, year: from_utf8(&s[12..16])?.parse()?, week_day: match &s[..5] { b"Mon, " => 1, b"Tue, " => 2, b"Wed, " => 3, b"Thu, " => 4, b"Fri, " => 5, b"Sat, " => 6, b"Sun, " => 7, _ => bail!("Invalid Day"), }, }) } fn parse_rfc850_date(s: &[u8]) -> crate::Result { // Example: `Sunday, 06-Nov-94 08:49:37 GMT` ensure!( s.len() >= RFC850_MAX_LENGTH, "Date time not in rfc850 format" ); fn week_day<'a>(s: &'a [u8], week_day: u8, name: &'static [u8]) -> Option<(u8, &'a [u8])> { if &s[0..name.len()] == name { return Some((week_day, &s[name.len()..])); } None } let (week_day, s) = week_day(s, 1, b"Monday, ") .or_else(|| week_day(s, 2, b"Tuesday, ")) .or_else(|| week_day(s, 3, b"Wednesday, ")) .or_else(|| week_day(s, 4, b"Thursday, ")) .or_else(|| week_day(s, 5, b"Friday, ")) .or_else(|| week_day(s, 6, b"Saturday, ")) .or_else(|| week_day(s, 7, b"Sunday, ")) .ok_or_else(|| format_err!("Invalid day"))?; if s.len() != 22 || s[12] != b':' || s[15] != b':' || &s[18..22] != b" GMT" { bail!("Date time not in rfc950 fmt"); } let mut year = from_utf8(&s[7..9])?.parse::()?; if year < 70 { year += 2000; } else { year += 1900; } Ok(HttpDate { second: from_utf8(&s[16..18])?.parse()?, minute: from_utf8(&s[13..15])?.parse()?, hour: from_utf8(&s[10..12])?.parse()?, day: from_utf8(&s[0..2])?.parse()?, month: match &s[2..7] { b"-Jan-" => 1, b"-Feb-" => 2, b"-Mar-" => 3, b"-Apr-" => 4, b"-May-" => 5, b"-Jun-" => 6, b"-Jul-" => 7, b"-Aug-" => 8, b"-Sep-" => 9, b"-Oct-" => 10, b"-Nov-" => 11, b"-Dec-" => 12, _ => bail!("Invalid month"), }, year, week_day, }) } fn parse_asctime(s: &[u8]) -> crate::Result { // Example: `Sun Nov 6 08:49:37 1994` if s.len() != ASCTIME_LENGTH || s[10] != b' ' || s[13] != b':' || s[16] != b':' || s[19] != b' ' { bail!("Date time not in asctime format"); } Ok(HttpDate { second: from_utf8(&s[17..19])?.parse()?, minute: from_utf8(&s[14..16])?.parse()?, hour: from_utf8(&s[11..13])?.parse()?, day: { let x = &s[8..10]; from_utf8(if x[0] == b' ' { &x[1..2] } else { x })?.parse()? }, month: match &s[4..8] { b"Jan " => 1, b"Feb " => 2, b"Mar " => 3, b"Apr " => 4, b"May " => 5, b"Jun " => 6, b"Jul " => 7, b"Aug " => 8, b"Sep " => 9, b"Oct " => 10, b"Nov " => 11, b"Dec " => 12, _ => bail!("Invalid month"), }, year: from_utf8(&s[20..24])?.parse()?, week_day: match &s[0..4] { b"Mon " => 1, b"Tue " => 2, b"Wed " => 3, b"Thu " => 4, b"Fri " => 5, b"Sat " => 6, b"Sun " => 7, _ => bail!("Invalid day"), }, }) } impl From for HttpDate { fn from(system_time: SystemTime) -> Self { let dur = system_time .duration_since(UNIX_EPOCH) .expect("all times should be after the epoch"); let secs_since_epoch = dur.as_secs(); if secs_since_epoch >= YEAR_9999_SECONDS { // year 9999 panic!("date must be before year 9999"); } /* 2000-03-01 (mod 400 year, immediately after feb29 */ const LEAPOCH: i64 = 11017; const DAYS_PER_400Y: i64 = 365 * 400 + 97; const DAYS_PER_100Y: i64 = 365 * 100 + 24; const DAYS_PER_4Y: i64 = 365 * 4 + 1; let days = (secs_since_epoch / SECONDS_IN_DAY) as i64 - LEAPOCH; let secs_of_day = secs_since_epoch % SECONDS_IN_DAY; let mut qc_cycles = days / DAYS_PER_400Y; let mut remdays = days % DAYS_PER_400Y; if remdays < 0 { remdays += DAYS_PER_400Y; qc_cycles -= 1; } let mut c_cycles = remdays / DAYS_PER_100Y; if c_cycles == 4 { c_cycles -= 1; } remdays -= c_cycles * DAYS_PER_100Y; let mut q_cycles = remdays / DAYS_PER_4Y; if q_cycles == 25 { q_cycles -= 1; } remdays -= q_cycles * DAYS_PER_4Y; let mut remyears = remdays / 365; if remyears == 4 { remyears -= 1; } remdays -= remyears * 365; let mut year = 2000 + remyears + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; let months = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29]; let mut month = 0; for month_len in months.iter() { month += 1; if remdays < *month_len { break; } remdays -= *month_len; } let mday = remdays + 1; let month = if month + 2 > 12 { year += 1; month - 10 } else { month + 2 }; let mut week_day = (3 + days) % 7; if week_day <= 0 { week_day += 7 }; HttpDate { second: (secs_of_day % 60) as u8, minute: ((secs_of_day % SECONDS_IN_HOUR) / 60) as u8, hour: (secs_of_day / SECONDS_IN_HOUR) as u8, day: mday as u8, month: month as u8, year: year as u16, week_day: week_day as u8, } } } impl From for SystemTime { fn from(http_date: HttpDate) -> Self { let leap_years = ((http_date.year - 1) - 1968) / 4 - ((http_date.year - 1) - 1900) / 100 + ((http_date.year - 1) - 1600) / 400; let mut ydays = match http_date.month { 1 => 0, 2 => 31, 3 => 59, 4 => 90, 5 => 120, 6 => 151, 7 => 181, 8 => 212, 9 => 243, 10 => 273, 11 => 304, 12 => 334, _ => unreachable!(), } + http_date.day as u64 - 1; if is_leap_year(http_date.year) && http_date.month > 2 { ydays += 1; } let days = (http_date.year as u64 - 1970) * 365 + leap_years as u64 + ydays; UNIX_EPOCH + Duration::from_secs( http_date.second as u64 + http_date.minute as u64 * 60 + http_date.hour as u64 * SECONDS_IN_HOUR + days * SECONDS_IN_DAY, ) } } impl FromStr for HttpDate { type Err = crate::Error; fn from_str(s: &str) -> Result { ensure!(s.is_ascii(), "String slice is not valid ASCII"); let x = s.trim().as_bytes(); let date = parse_imf_fixdate(x) .or_else(|_| parse_rfc850_date(x)) .or_else(|_| parse_asctime(x))?; ensure!(date.is_valid(), "Invalid date time"); Ok(date) } } impl Display for HttpDate { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let week_day = match self.week_day { 1 => b"Mon", 2 => b"Tue", 3 => b"Wed", 4 => b"Thu", 5 => b"Fri", 6 => b"Sat", 7 => b"Sun", _ => unreachable!(), }; let month = match self.month { 1 => b"Jan", 2 => b"Feb", 3 => b"Mar", 4 => b"Apr", 5 => b"May", 6 => b"Jun", 7 => b"Jul", 8 => b"Aug", 9 => b"Sep", 10 => b"Oct", 11 => b"Nov", 12 => b"Dec", _ => unreachable!(), }; let mut buf: [u8; 29] = [ // Too long to write as: b"Thu, 01 Jan 1970 00:00:00 GMT" b' ', b' ', b' ', b',', b' ', b'0', b'0', b' ', b' ', b' ', b' ', b' ', b'0', b'0', b'0', b'0', b' ', b'0', b'0', b':', b'0', b'0', b':', b'0', b'0', b' ', b'G', b'M', b'T', ]; buf[0] = week_day[0]; buf[1] = week_day[1]; buf[2] = week_day[2]; buf[5] = b'0' + (self.day / 10) as u8; buf[6] = b'0' + (self.day % 10) as u8; buf[8] = month[0]; buf[9] = month[1]; buf[10] = month[2]; buf[12] = b'0' + (self.year / 1000) as u8; buf[13] = b'0' + (self.year / 100 % 10) as u8; buf[14] = b'0' + (self.year / 10 % 10) as u8; buf[15] = b'0' + (self.year % 10) as u8; buf[17] = b'0' + (self.hour / 10) as u8; buf[18] = b'0' + (self.hour % 10) as u8; buf[20] = b'0' + (self.minute / 10) as u8; buf[21] = b'0' + (self.minute % 10) as u8; buf[23] = b'0' + (self.second / 10) as u8; buf[24] = b'0' + (self.second % 10) as u8; f.write_str(from_utf8(&buf[..]).unwrap()) } } impl PartialEq for HttpDate { fn eq(&self, other: &HttpDate) -> bool { SystemTime::from(*self) == SystemTime::from(*other) } } impl PartialOrd for HttpDate { fn partial_cmp(&self, other: &HttpDate) -> Option { SystemTime::from(*self).partial_cmp(&SystemTime::from(*other)) } } fn is_leap_year(year: u16) -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) } #[cfg(test)] mod tests { use std::time::{Duration, UNIX_EPOCH}; use super::{fmt_http_date, parse_http_date, HttpDate, SECONDS_IN_DAY, SECONDS_IN_HOUR}; #[test] fn test_rfc_example() { let d = UNIX_EPOCH + Duration::from_secs(784111777); assert_eq!( d, parse_http_date("Sun, 06 Nov 1994 08:49:37 GMT").expect("#1") ); assert_eq!( d, parse_http_date("Sunday, 06-Nov-94 08:49:37 GMT").expect("#2") ); assert_eq!(d, parse_http_date("Sun Nov 6 08:49:37 1994").expect("#3")); } #[test] fn test2() { let d = UNIX_EPOCH + Duration::from_secs(1475419451); assert_eq!( d, parse_http_date("Sun, 02 Oct 2016 14:44:11 GMT").expect("#1") ); assert!(parse_http_date("Sun Nov 10 08:00:00 1000").is_err()); assert!(parse_http_date("Sun Nov 10 08*00:00 2000").is_err()); assert!(parse_http_date("Sunday, 06-Nov-94 08+49:37 GMT").is_err()); } #[test] fn test3() { let mut d = UNIX_EPOCH; assert_eq!(d, parse_http_date("Thu, 01 Jan 1970 00:00:00 GMT").unwrap()); d += Duration::from_secs(SECONDS_IN_HOUR); assert_eq!(d, parse_http_date("Thu, 01 Jan 1970 01:00:00 GMT").unwrap()); d += Duration::from_secs(SECONDS_IN_DAY); assert_eq!(d, parse_http_date("Fri, 02 Jan 1970 01:00:00 GMT").unwrap()); d += Duration::from_secs(2592000); assert_eq!(d, parse_http_date("Sun, 01 Feb 1970 01:00:00 GMT").unwrap()); d += Duration::from_secs(2592000); assert_eq!(d, parse_http_date("Tue, 03 Mar 1970 01:00:00 GMT").unwrap()); d += Duration::from_secs(31536005); assert_eq!(d, parse_http_date("Wed, 03 Mar 1971 01:00:05 GMT").unwrap()); d += Duration::from_secs(15552000); assert_eq!(d, parse_http_date("Mon, 30 Aug 1971 01:00:05 GMT").unwrap()); d += Duration::from_secs(6048000); assert_eq!(d, parse_http_date("Mon, 08 Nov 1971 01:00:05 GMT").unwrap()); d += Duration::from_secs(864000000); assert_eq!(d, parse_http_date("Fri, 26 Mar 1999 01:00:05 GMT").unwrap()); } #[test] fn test_fmt() { let d = UNIX_EPOCH; assert_eq!(fmt_http_date(d), "Thu, 01 Jan 1970 00:00:00 GMT"); let d = UNIX_EPOCH + Duration::from_secs(1475419451); assert_eq!(fmt_http_date(d), "Sun, 02 Oct 2016 14:44:11 GMT"); } #[test] fn size_of() { assert_eq!(::std::mem::size_of::(), 8); } } http-types-2.12.0/src/utils/mod.rs000064400000000000000000000025000072674642500151300ustar 00000000000000mod date; pub(crate) use date::fmt_http_date; pub(crate) use date::parse_http_date; pub(crate) use date::HttpDate; use crate::{Error, Status, StatusCode}; use std::cmp::Ordering; use std::str::FromStr; /// Parse a weight of the form `q=0.123`. pub(crate) fn parse_weight(s: &str) -> crate::Result { let mut parts = s.split('='); if !matches!(parts.next(), Some("q")) { let mut err = Error::new_adhoc("invalid weight"); err.set_status(StatusCode::BadRequest); return Err(err); } match parts.next() { Some(s) => { let weight = f32::from_str(s).status(400)?; Ok(weight) } None => { let mut err = Error::new_adhoc("invalid weight"); err.set_status(StatusCode::BadRequest); Err(err) } } } /// Order proposals by weight. Try ordering by q value first. If equal or undefined, /// order by index, favoring the latest provided value. pub(crate) fn sort_by_weight(props: &mut Vec) { let mut arr: Vec<(usize, T)> = props.iter().cloned().enumerate().collect(); arr.sort_unstable_by(|a, b| match b.1.partial_cmp(&a.1) { None | Some(Ordering::Equal) => b.0.cmp(&a.0), Some(ord) => ord, }); *props = arr.into_iter().map(|(_, t)| t).collect::>(); } http-types-2.12.0/src/version.rs000064400000000000000000000062540072674642500147100ustar 00000000000000use serde::{de::Error, de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; /// The version of the HTTP protocol in use. #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] #[non_exhaustive] pub enum Version { /// HTTP/0.9 Http0_9, /// HTTP/1.0 Http1_0, /// HTTP/1.1 Http1_1, /// HTTP/2.0 Http2_0, /// HTTP/3.0 Http3_0, } impl Serialize for Version { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.to_string()) } } struct VersionVisitor; impl<'de> Visitor<'de> for VersionVisitor { type Value = Version; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "a HTTP version as &str") } fn visit_str(self, v: &str) -> Result where E: Error, { match v { "HTTP/0.9" => Ok(Version::Http0_9), "HTTP/1.0" => Ok(Version::Http1_0), "HTTP/1.1" => Ok(Version::Http1_1), "HTTP/2" => Ok(Version::Http2_0), "HTTP/3" => Ok(Version::Http3_0), _ => Err(Error::invalid_value(serde::de::Unexpected::Str(v), &self)), } } fn visit_string(self, v: String) -> Result where E: Error, { self.visit_str(&v) } } impl<'de> Deserialize<'de> for Version { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(VersionVisitor) } } impl AsRef for Version { fn as_ref(&self) -> &'static str { match self { Version::Http0_9 => "HTTP/0.9", Version::Http1_0 => "HTTP/1.0", Version::Http1_1 => "HTTP/1.1", Version::Http2_0 => "HTTP/2", Version::Http3_0 => "HTTP/3", } } } impl std::fmt::Display for Version { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.as_ref()) } } #[cfg(test)] mod test { use super::*; #[test] fn as_ref() { assert_eq!(Version::Http0_9.as_ref(), "HTTP/0.9"); assert_eq!(Version::Http1_0.as_ref(), "HTTP/1.0"); assert_eq!(Version::Http1_1.as_ref(), "HTTP/1.1"); assert_eq!(Version::Http2_0.as_ref(), "HTTP/2"); assert_eq!(Version::Http3_0.as_ref(), "HTTP/3"); } #[test] fn to_string() { let output = format!( "{} {} {} {} {}", Version::Http0_9, Version::Http1_0, Version::Http1_1, Version::Http2_0, Version::Http3_0 ); assert_eq!("HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3", output); } #[test] fn ord() { use Version::*; assert!(Http3_0 > Http2_0); assert!(Http2_0 > Http1_1); assert!(Http1_1 > Http1_0); assert!(Http1_0 > Http0_9); } #[test] fn serde() -> Result<(), serde_json::Error> { assert_eq!("\"HTTP/3\"", serde_json::to_string(&Version::Http3_0)?); assert_eq!(Version::Http1_1, serde_json::from_str("\"HTTP/1.1\"")?); Ok(()) } } http-types-2.12.0/tests/error.rs000064400000000000000000000071560072674642500147310ustar 00000000000000use http_types::{bail, ensure, ensure_eq, Error, StatusCode}; use std::io; #[test] fn can_be_boxed() { fn can_be_boxed() -> Result<(), Box> { let err = io::Error::new(io::ErrorKind::Other, "Oh no"); Err(Error::new(StatusCode::NotFound, err).into()) } assert!(can_be_boxed().is_err()); } #[test] fn internal_server_error_by_default() { fn run() -> http_types::Result<()> { Err(io::Error::new(io::ErrorKind::Other, "Oh no").into()) } let err = run().unwrap_err(); assert_eq!(err.status(), 500); } #[test] fn ensure() { fn inner() -> http_types::Result<()> { ensure!(true, "Oh yes"); bail!("Oh no!"); } let res = inner(); assert!(res.is_err()); let err = res.unwrap_err(); assert_eq!(err.status(), StatusCode::InternalServerError); } #[test] fn ensure_eq() { fn inner() -> http_types::Result<()> { ensure_eq!(1, 1, "Oh yes"); bail!("Oh no!"); } let res = inner(); assert!(res.is_err()); let err = res.unwrap_err(); assert_eq!(err.status(), StatusCode::InternalServerError); } #[test] fn result_ext() { use http_types::Status; fn run() -> http_types::Result<()> { let err = io::Error::new(io::ErrorKind::Other, "Oh no"); Err(err).status(StatusCode::NotFound)?; Ok(()) } let res = run(); assert!(res.is_err()); let err = res.unwrap_err(); assert_eq!(err.status(), StatusCode::NotFound); } #[test] fn option_ext() { use http_types::Status; fn run() -> http_types::Result<()> { None.status(StatusCode::NotFound) } let res = run(); assert!(res.is_err()); let err = res.unwrap_err(); assert_eq!(err.status(), StatusCode::NotFound); } #[test] fn anyhow_error_into_http_types_error() { let anyhow_error = anyhow::Error::new(std::io::Error::new(std::io::ErrorKind::Other, "irrelevant")); let http_types_error: Error = anyhow_error.into(); assert_eq!(http_types_error.status(), StatusCode::InternalServerError); let anyhow_error = anyhow::Error::new(std::io::Error::new(std::io::ErrorKind::Other, "irrelevant")); let http_types_error: Error = Error::new(StatusCode::ImATeapot, anyhow_error); assert_eq!(http_types_error.status(), StatusCode::ImATeapot); } #[test] fn normal_error_into_http_types_error() { let http_types_error: Error = std::io::Error::new(std::io::ErrorKind::Other, "irrelevant").into(); assert_eq!(http_types_error.status(), StatusCode::InternalServerError); let http_types_error = Error::new( StatusCode::ImATeapot, std::io::Error::new(std::io::ErrorKind::Other, "irrelevant"), ); assert_eq!(http_types_error.status(), StatusCode::ImATeapot); } #[test] fn u16_into_status_code_in_http_types_error() { let http_types_error = Error::new(404, io::Error::new(io::ErrorKind::Other, "Not Found")); let http_types_error2 = Error::new( StatusCode::NotFound, io::Error::new(io::ErrorKind::Other, "Not Found"), ); assert_eq!(http_types_error.status(), http_types_error2.status()); let http_types_error = Error::from_str(404, "Not Found"); assert_eq!(http_types_error.status(), StatusCode::NotFound); } #[test] #[should_panic] fn fail_test_u16_into_status_code_in_http_types_error_new() { let _http_types_error = Error::new( 1000, io::Error::new(io::ErrorKind::Other, "Incorrect status code"), ); } #[test] #[should_panic] fn fail_test_u16_into_status_code_in_http_types_error_from_str() { let _http_types_error = Error::from_str(1000, "Incorrect status code"); } http-types-2.12.0/tests/fixtures/empty.custom000064400000000000000000000000000072674642500174520ustar 00000000000000http-types-2.12.0/tests/fixtures/index.html000064400000000000000000000001000072674642500170560ustar 00000000000000 This is a fixture! http-types-2.12.0/tests/fixtures/nori.png000064400000000000000000041011410072674642500165510ustar 00000000000000PNG  IHDRiO$YsRGBgAMA a pHYs%%IR$!tEXtCreation Time2020:05:16 12:50:03|lxIDATx^tדmYm9'Խ7ueV $ccQŒ 04l-Kg"nD5쌮!<#%}9'se4fY:MsSP$m۶4MO)ϋb90 45Mn1Kaʲ̲|Ƕ몪LfuT0MDH(л Mt:8c7 #"k6 l4ax<E)i+Y}/$ʒ.'4'i{@UTc΂.<CN|:DǢ'!AAD00 ?.jP\8%tLn "P WjHIɤ.Ӕ0g'AՑ3Gd!=j%H1Uaă-P.R\63 1&KI\pPЩhg"D=*iDPA90`qE),R0>@  (8kp]5PCq0Ejg 1%p ;I)J*"gTlP¶LŜ9a.@qm{a4Y3)y}%7?KSBi@'Y,)$r|=/nn6|M{:"iW6(y*1 3Ю2Je&2 &a`m&F6F>[]#q_I+,W$S>BĢzSB ybL]$0cΔg=ю3"Kҽ]ZinGIV>vt4S^^n^|R\/4|'2yEKz˿?og??Urz鸽/N?)c_Yq~(>O)o/SU[iѿ`/V_iZ/P]hffݟMO|}>\7Uter[dan]CZ>WGC~$!~|xno}*~۬W?կSw7ɱǻsoӽybۿ>}7s1?oo0/6|4eѮ>ܰe5w}ſg(ۿ{yؾݥom_}0ꪹZ5ǟ?CWt8~*~>ݕsջ /gWu>o/6ޗ/8]⻶Vݿ_s;o仡ܶU2oOsrso8wmq_OϬ?WlII6y_y2ҹX*n|/U1gU/^޽z.-? uYkukşGːu)-aEMW%Jn/+mYNԵ$l:"EmK)(" [TБB"\fn7d c3VUp%)m5<ͪj;M^lA,曛?|䍶#tWArK4.ePnf,kb+p6]>wi?g?ou;pJYsV2:֫u]7yZUsuuC8k:wx $t6b\8!Iynʬ}||F&&}&AY*K"Bh0N8@D38?z$ ,V#,BZh%E ݐՊECO_%5\DPk Tᡉ*t ρ}. R p p*h(QBlY'Pৄ ,zh*=8:JRB яQ/l&\a!>II'prJs$ڬP41p/h*rqM ̀պUg~9F8:-@<1BG$??]ZLRkU0кChCAk0h)JXgMl00+PRuɠ`QV.%1 ʪy.^- %t m*4-3͜E9J.O+DKlop LZ2舣VYYҺh*Rɖ9Nʟ;&0_(C2v+D%bj8t=ni2TQH0) 2?ƄvaXA J>#TM[#s~Bg.މ#4,΂ՂuФPQ嗲QՔ&-UA}\Ls9/NwBp g S+LŅuB1^!qY\'dWf`]0/*f;UF:85U϶,Ki2vQ*6%LkN )<[r lGE>p%@e ~ˊlskթ돧~Ȣiu%(erijhd+m,$ Y.(JdžK}߲&YDH&ʤ+FL!OB9F9Ǜ/I7&4 :fp3O6UQbS]_noʺ\U`d:z>AG5c:l(l.V "5A{F mLaR @,e %M3pW`Z:t@sS,LsR.Jh M\A!t5yؽMws&3wTnePY)wU2&J'x)# N A%@ԭj @q#:bNO~ 3 $EqZm%~)FSbDf)bk=!.M cp؟'d8gMX^oWJy6 xVBȜɺM3)@g_\26m&Cr8`&N(ЭӮCjs8`:%d=(I H)|'렕JӆMyhuP-=6>D` a.%q^Da y ]_2 %r1*M97;]d:* ]NigY3[QN 8l!B6s&WAhQBGt _kXI$rF?0FQA\uJDta0QX<)5`PwB␌~᳄Ì@ 0O XrOc(T]e Y7@'B18]3O%f" .MCGBe yNeXGx)apB)@H1_onN98GBV0C[t~5tcOGE6큓LᠯtA]yMRcGuFΤ:-+tZF9E},}55$/ǬJuUoH Sn_|NG=^97ilO][N&rVDYgOGrȍ!mA u?yR&.Lu֑jnXLQu+Xgv^,Ȅ'ZL07ݞ8WMbp("b`-B,;=s|%D+硙?̡AA1]DEf},X񯐠jD9LZM PyB/ Ѫl%P`?oDP,c X!H4QHZ/IlP41>%ǧ\lH|<#cwz=*!z\7L-%xm ;4D._$$BUYT4qi_DU19U sgʅV̉j ّ!@)|:߼;esJYtf#8iEED#"4?jQEU~>a+ _P?EZ G$_4Є3PE{S:381+TQ"`6茚`B jdMdJ_% / UBBc*E-fr`Tl6` )JPHOQN[e>B6V:PR53cw`mFLX6MGjydv'}$? =X hض"l[M6a5Y{O'6kJ@] *ӄUWhT~KPԛruC֫;ҿXפYA'IV dZ9k4=`ԫ"em`,u^6'"S7d nNkRKvHwTis2 | <0TiLjS;6)b6u%yb^ YNE$XUIUU]RM21<%i"ɐwi:NeViՔ>ݐͼ~ܾ$ԓykV2O8׫D.&I3WHD^`0S?WeV ^\py pJpJx3D4Jf?GAh1cp<|~F7D)VK.i 2d=, S )PX.C=C[{#(t\2l h#X À*3[ `MVlGc#X1a5~d~J<|L;=5VK/@63ba@PZUB+Q\q-@ }iʘ(M0K35VŀZx@(A(/AX!,K1~Jp2Z *7i} 6Q(f)y=LnaeZg00L`v4zDy!wmb){k ۲6t{8i"Hc E hȏylOaSO#.XOuYUAe en h.2M-=dS4D) ;@ۿ^zlSRh1~Nbcp)Q@@c]zT L"8̔A`Fp ˥z@54ĩPzd*MQ573gz56U]mch'/e `#G~C6wq|ѧ=$սD)e)EΪ-[?]벢g_pHCfjl2?ݡcTǶ>t'ڹ4QOW%u8 ntd *-YɅ?/3m]ƫeŃSIҤם$- >Y!sŦ0ainb]-O9ue]r0\|UsiQdd##1ۄ܉֫QM߷vSԦOm%0q|쟺/oz.6vV/\bG#EN߯7)u^Ut)x sO|VwOd$SƋ{GM{& #Eo`V^J  D(0G'2lLSJ)c8.bd(R?t:CU=s<==>'ؠǮ':2%8t##>X)Hիza4 ApHD+`VS :=NKU͗ Q-s Gn2D/!Vl7Za_?zڐȺӉ#'}ϋ4!;`%b zJ˥IیY9P?Uxpk-8` (Yʅ4 t?j@lu 48ӆ97]n;tE;UiF-LWmdg)`A < 5HgdȻΞC4KZ5%@?A8}&]j/qp8xDw_A)&PgT=~ߘ4FZ :I $9Q.G<k"%֌x.`y]./Gc]jlWqͺ;F<$Z2AV?haD#AZK u:%UV93.陰+ؽ͓NpRFD{xdLdNّ]EJ&mo!t6 Ω[:MJNP $45a$q =Dst{_V Βw90\Qfd}$̄)}5x٥\؝j jLps [P%.QH>4t=!fo\n:bݏYڤՊ(hj jԯh&7z)[?ѿq@0>}Qq%(UvǷ_/wnO[\?^ Baꑲc-Q[(D'E&7$kxҿhҶ>lAelr TaEݷonr.=gciP"XL4X)RV̊ij]@t%l ~T!D|/M/'Ѝ,(1OwZNm,0sggOoHfsuI>ccYeYshDɨwݳ^i1JVe5=^Ӕ{q9mp"JnaJ49O~Smr7?N0(@o F!DȪ)|H 6WAl)R"Dg5pR bxLȂ ; ət.䔞P(ˌH M_6xڧ E8h4;:e|Bt M }b~(`#V%l y'-;].SVZvaU A3n`Ŧ ̌Ud5y$^ZS(TcB(P(ibD &1y\]c}af&1Nf&}q>$m/)D7诽iFs$ c)p6q h bcQ3'NDݱB-9F'(a"G(J7TUfihO$)O.k2|AyVT ӥ\ę\yOF{I n걩/^+Ax삢3^fDu@ƂXDlZD@"@|}΃ u+,SjV>>~[[Z^sE4ܭ#Ÿ8 ;8{ /G5jQNF̩0%n2' >& z au"dtL-u>UT_a9mԑ(iBi'qy i&v5Qo- -L杦;6nLq2as' f(ˠcaՠM ,i2yHǴ]*Y8Wz1s|C&puql[WZpFV·.ںVZc#Ԟ]GUQ Sx`s|T{Ƽ1է: .4&mF}9A_#ސ;|0K6U6]oq5'}u:~VNMs؎L}8?M.߰_唜0bQI6'4YoVW#͸tܡfJ }F/V/>uVavezI!})x'?Gt7pzqHcd}}-և覬eJh|_x_|po~4ӋNSt$*S䧏owwC2r|/6Ǽo^d3y>ӛilKu/5[F?'2"mJnZ\tW_goj<#:wЧ܄"+ b eCPoGiUo͕m^H;AР%S|>3%Rk u\t4!FgUY;@ -Vu U P +JD)蔨hP!&18št:qrC[ AϦу. 'L8U< sd6PN d1Åӆ-J +дP R32sȂiOpMTM)Z;_LD ¡Qրu<h=M WiBsTAF<>ݭkn2?z&ʀmS(U& { NzP: JB1_Q&TΚH+E'P8J\E!\LX[T~\ZC;dA{ ўzEr頛9F[T tD[܈ܓe`I~!'L0ЊZp bq8h:8MG] {O 0-ma[eJ,}G9tPVq+8*%,2 6LdΔ(7WQl6޶\ڬЋ?qҀUuEv tZy=` HJW⎀OiaA4I[F${Dɞ ݃p{{-Z*T<<p"N8LptK6=9Қuܒ3!< U[\c iyU~D>NJ=7yg~HxS eG]RS6gh>qּ'g?>4䫫~xR$ Sڴ:x񃾼'0qz*I}!+b8oOS4.ac?>uC!Z&yYNԘiœ$ttkO/_UX/qeU7 KBRd\ĐLO^޿^I]mere1eYlk=eyZ5gՄEd*.#c1ɫ fr\,e¹M֏g yvX4a><>==H,njUG[D*F=euՠ**v;z]%#jelTF?(/n+28}IL#q R ) `Mh_]N.&=@? a[ uTXL<>>n;-}86K$GD*cz^*tAl'D"Tz5~?0R3gpa"+ihhЬEIBءY3,8.g*pO nE@%K.ɬ̹hӹr8k(P®'%(l 28Dwh[x .vӆV(-RJrϙVwV( vO\"femB"|0@tF.YZ,M_PfAZ)ftZ-x2훫QLY)4je)*sϡ@_Θ? *M\<(%&,_k6Pudgap.`VׅyqZp@(퀛8%# 6b opB*;`6!!E 3` W@l  YA8zٝƢ$əŗ@ \V\2tN<UW7Br1GHc<ѤXQo8_<[(=jĖC`mp 8zg,A<pO<tM{s3]mWINW7cVoզ_iAvuWuuayE.O8zƼe5W_zʯ^<$q=+lƴ ӜMJݱw }Z*`5I7eD/g;F-eEzUosf䪜/O⋣&pn.Dzs!SnVeJ}.=}}=#o6jU^_}%UFXx0Ͻܴ˜?KQ5 4\9ShE fޱ {]Ͳ^ gs '? M)f@^ԫx4w%gT;21ۘ wҀ: eg-%A3Xp#"b"`~ XMfEyLewKGM4͛=Q˒nJ}33lF*8~Vk]U5:Зޕy8nh2ȉ.@OƍRN 0E㾁@gWV8+ЩP(%鰙I߮x$D)xgRb ] h6 @t잉T)J7!Ni}xSj!j}=i; itp%V`:&v :*k0KqL.`@ iHU\c(iED",i (T' mPR[ ژ^Te(8<!Rx+ Dܳ6Z\%UD 854n" VN՞CrjK=Xy(hA1g$kJ`  醛 }aMvRb[ᡤU??/)7-)dEµ 't )E=pKhLU<p v%5?]JCAD[E IKRㅨNι؎8ԋL'C/_3$*;hMUC8Yp1sZc+bG (j!zXbJQO(Ab3rb&("fR] nĞ+b \*앞JK[fb Ph 8t`㳟.mvCn:-zaCNds7_3 ꫗cEڠ_&OCߑ_~A)ӛW?*)^~2䍘{z(֫W).ꛏ!_qScXHYIՌc33gө:̼k\SZd!wFrf>muS25YsL)CB-Jec0yܳSV-ñrV>^5I xG}~H.;.sG7EW .i-g/}%¬,͚;7x@`xNSU &R{ , ,bYG B7@B\Iᡜ#WlVnPJ __|7WMR_%0&TWn\mnop '3يCjCldoa&ij%8 /cq>iUѿװ7:.1{j߆t^4Ͱ *.hx"^c(+ YZ6t1E)5 '(Ս "t]6PKiUPu(Zi侔j HYzFvh؄cv q Vd'Y0'` ~&4a1J UpHK 1taX@`6?jM~5(օbfB\"bf{k^n0hZFnaՔŮX4N:75  DNcCTo)JT?`lCp_:H(52"fZ#CѤ@AAՈ񳬥BFw6 pyt }1D?X}xܴO4)QF .ע[Ccj|?CR_W?gǤY_\b}Ñ}s|Y~`!v&>\U/>K> IȒ?uC\}Z]}|gכrv|=h^0|֥Y~:d-~X}d5YE?$CO%-h^^ܼ"Yݑjq;ꮸ~YV!Ն:9 M$ܶNCy42}=kg+][TESdb44eC7>?+ߘUD[}HQ0F\k&ewXR SpL'}Ty;ZOAH*M$W'$y|ʈkc\J]t~QLtfFY -XyQ3.A~F+זGTѓ|z؟-q LMx_~CH>Ӷg<ٽ헿|ɤ_V%94w7wWTIu&g~Lg-aФ<|Z~D-X\bL DP3UZDX DĩXϤ<޽F&4{*vXdq'9#ż`\6/^daOYT\ kSAT5v[XdD.{٨J@+`@ 2 VJm,UxҮ"tbퟞ~?o~Y7񺩙rLf@U*" 7޲1K0b(E#ˆГA\I7I9J=PUit})Yl?rP {eꆷXwc/ '`?;p {(w_p\9l 䐵~@ 41l]2Rfy`@re{oD&B샭HaRS-6 ~+t5x; g0m2V7+xM6KP%I(ʤɮ_Z_*FqY~\|Ҧ6)Θ Gl$GSq50$J/7M1'g'ݧs}LPknV:+W8vbAz9k^I+%*sAS(S-0PiA7sz%IȬrVSL?WD+}-y p-+H)cg `i7DX_}7 .19/R1rZUQԥnTN,Jx2~&K)^byΓUɄ42>{x|hmfzhB+̉KEO9h\Q iϒ)Ɣ|Cd,$_Ք7cVz+Y]7Ir*z)T:Zdz!Z<0󒅔$UޯΚz^+$J<*24&6 }^\9vIR#$IQkdٔ:fwY(xqU6I͘܃s2%fZ" U5q.s[/rQ) 71+TP0N%fQ9YdZbvBM钾QsN)Z`:էẅkdj[Ueøpgu|z~!lf|u֚}kuFoK[%`P(bzdĭUPZ]V<͈)_< ژ(>G@tك3> 9'޹J?:8*\]GLQTãqRt#DV1x2夃Zw̶pRy&Zɂ7xܺh7\ "^Ya#\w_vIG^yއJ@ȠXJ+PɎ}k^MR8t7"iAn/GFWGAuЀ g82 A@y0(qnI\ZYp("1)]cP0Jrh{ Z>SH<࡛KjiR p& DEqU 8tUM`;"ng sDz[ O5 "e䣳!j$9\Lw:1/dKb4$ %I:oyHCYod nZow٢Ƥ^A`JЇmgde) VHVol`2(SJAToW"z}J_@29)'`T 29+ z"!EN/#uu-,*67Ww˒U\B8:|}{>zraꏇQW/W'{i4/_0LК 0pK1թF)~J/K L/t:r"h4D3% LJE((Tzow_CA)ESB:I]2'lz|usu}}ýlP(CB]5gn:%8HiPi).ڀ%p>W.Eq Hws|3J(*/~_1flHBT!tW(.M}?58ɾZR<s1sx ff8*An,Ic+E h!O+S5u€(X=0(QN>mAj ։o 19E35.A8.+B0Szh"DfpoSU)4AuUʰ.P"Hi=nP[1"ҶDB`1"MLwDvE@H3+`糴c⎀Sr@r,"P#p\('] 0""?_ ؐLJCpBm4@ΕhBfpf= `?:f@qN4/:X$C Аk`g= ׈g-.oUJw""%Uc]\T x08Em0@S#1=C:AX炦*RdYpi \ H Hói1VpZ5uZO> ,ƂOĤ_ۘ T8ߌ >tķi`9ɘtn٨N;pk}(tħ]6@t$/a΄>yjkʼSe7wܠJc@bL@ UІP l೅FQ4D 23sa! RkCtV'%n *%q|hl\_3vӎڼ9AM?eۢm>.|B#1; @ɒ¼I9kMD {(wm%er @ lz٨7eMm*M%FS3UDя2Mmtɠ7Q\M Zw9r/785}SU?B}74O ӨSLZ9VIp~pϢ{Q8Zdp]V[Ȩ2YҫS٤M_ aQ+MQSrǾ e겦I股 TF],&DSedqgqDbb eSv-iX;%Tp?z8nWMu{ ];p5VlxϏ^\Sw7W/no͊@sJъ=XT1 xXj ]ۧZ^,OA(Q+bAc[T - ΐt+4H CA m$}/&a(`)&l(EΚQ&0Xy$aEA ̨[XH%e37R1Do%p&:>|&kv`d.vH7jZZq͛oF .Wqπ5#5S`SCvfN-z*/eNt:"W|-0؞ХDafꆖ7z-C=VN,JQA׃K2^"PجVB*L`Ah/>b%"wONąU Ň$gtϥ]`G-V"&~2r-8(^p5\kw*3A=~Q:ERG=9饘=.@{3#P W@tT&*^}\+}<[~ ^- [YW,@q;Pf4ƻ􎻦[iWZ qTP6%d pFJN^ܟK[ }aLM_1Hs &}܂Էs&>><c;Ǥ?e}Myvxw/ɝۗ.-JYp~7Q>W7yuwwZ)'$lq:!ZGL /C&zDC,zь\ RgRrH;SbY;XڦşصpR |kE6}_OsD{6諦яC2#]wNnPfKCW׷W^r'w(bJ{X8}c*VqyD=fܛ$ťBDLե&7z(fu1 l?~GӁaEڬbc̵cz)䤧$OplՁg$.^dO'}e}nU1߰BR >C`MHTln2^%k05gZA vD0v18l6d~pVh57]+ؚ*` 0`JPh <x:lX-%|N܂M#AJ&"O+ POJGyqMo< =]| H/#j0D`g.#Ȃceٶ%eÈri9q-Ÿ)bMLX\.n6J#M 9f3'P<5&RcF᪥5<e`pF*pjȣМSV> ./uy1D+NI>,cw[-bVb=.eY`::Bs.B&JSkMO۩mQn.).5qhisl5@pl'= ?G]maƵ=$^C H9TQ^M+FlG(=sUFls_Q[ DZC٨F %,e8hL!- p<΁Eܞ@B71+ Ѝ00'tY,܊n [ Ջt{'RHZNӞeQi^~4'Ob&=nOG&{Sx8^D=>FV2}׿}}}BӇ4p͘# z%[#Y $Tx(N]kTzHW%NWZޮQJ4%zů7y4}n#jz pQ]nnA,Dg]rrwvXT|iO*þ͐CR[1pKQ՜k rkʏ7gdHN+)~z;564Ҥ88y:S;tMnCO[}=ǹWsG?Cx쎏CvMN2: ikݶpxzxd/M˞Yd) %M:\ &X .JPЈ_E^_ ,"EEvvE̚XGf•b&߾f:ؘ3UE=rγ狻>xZϲwD' :!(ƙ3-_:=&DR(ԼИ(%>qܽdPYp ǵ҃h&aX*" :FܲPIIXoKI+tB 'X-.=8T9m Zng%+kR."ezJUv/.y@{n0(4Nu/Ŕ>c=LpzYT*B-+OA-,b"$U81(@ZPURh~Dtph MI\}1wdD|T*2%fshgb:),8E'C#(b&cYH8KM"c %4 >dž}*[%eCS8isǽM`פJR[m6A\]]o61ͧS|EsKfEȫH"쐪&Gq;У <*elE2B,PQ/O }fYSn3Y\CVߞm{&}O7.0L3)%鏧]OqӉ֮%i9i7~M^O4s{8lO]{:ti~wvi{zaԝv%'߽?ӎM߾q;ߜwރw܏ԝ9mA2S? oYNOzꔶi۷O[8̢l*깩!.HaI+l!4b.ʤhtP&I޿MvlbiV ͫkEOrC&k:¨Blj턐Y fYKkz`Nf0W-+ϵfY"Yc“d7WOOlzT G3#$}wa,IDo3Z\[8% պ0sJ3/& 0Ti5h㔎Ph͙V(.Dɭ‰dw_ozDх8vԫ!tIS*`E^55f_! {I=\]OL+݇ N[?Ӷ}B*y6%=-kL =^~IYrnb/6t(3K,鮃ct=TwJ"H/-{FNqlA é3/㌭D7!ܟiIDb eSZtؾowsw;Oۤ=$\ߒn%T%tKz\u}w"پ'}{?Hi.,hDb4t(]O];vFqvfyTJtjFa8c= LL}޹>U0=NOOUŊ/?NKc0N%짴fE+tU$ï19 〄/iU9i2an`SDpn"((7wu; OMo7\ bT¨V/Ƥ7QuIj5Z?P7Pno^ܾN~)G|-?)(tkP~e6%ˏXʪ̹TA-NXٖq qW@խqb~+!N+1 2Lj6}-q,ë́Jڬ7dLxTE!qa"[DOUjV' JbއO_c(Yr zyx3TBM ĪAeS`  tؠqY P(ab:H .Ϫllh@Ni~&8+ jqY(*tChuSږYI+9 8S:|Xp~MC T+Iˎ?*DA7Y?: M)JDk7d#"MVKi"Kf6'DA݂vM#ut BϫK+myL3G; B9f`}}af`U Ip;PA7s&5`)eLT hp@V0#`ByNnĉ cRvX(p.&#aCe1?'.qBջqX<Ӎ[-8"N, H)D+ hS%urmHXa=ui[Lʶ0+h7 r!_J5`^iZlQ*D УP}!p%)ycc=ݣ뺶-31G`) C]5:/>\@$>ЊZhGG0L #?ʡlY].[ݖn?j&E?o77xw̽Yl"܌i>vTdf8%SI/xt\*Nz[ڡ?A⣲IxwܓKqp{%$;mVxH6V9Ƿ0L?>uzvޏ鴟a7]9vܑ-.w'q9 CtJ_ŽKH.sXb$_fۑM!1v%Y}qPtdE{w9ųyz2 LjLD $_t\]9+Q1da0o&.-\ :ۇԯor*͗C?UV6 ͷ_HnԬnRvwoyz6IӦx8 ۏ_}^6e굾H~ew`Iʪlb^[Z4ӂcj&綨1^Lt8XG]Ob HٜFBZ/(L,1U]_O6927ϹsK %M $*=z},&Zre֫6o?ڐ3=p?.;S!%t|f |E(MYJD#vƜ7lZamJ=-K1O/߾QC璦˒n;nبՊ_fX5y`;F8K&ɌŘẜ3 kքo6Jw"M[v tf(̞ ߿N)4YqX01?|9֑#~KdBڐ`OD8-b 0[į!Q,] 2jüH)NRb!lANd 0Z?U@ ǂpV/\ .fP 6;i!*+4DJ,`w+8]NN,Đwc~t UMj[P~7VA6H㥄Bi3%nfA<Ѣ&p6V8|VYӗV+Js (v3X%8} PK,08%lS ka~ܲnK܃nfJ3 mឨ4@X>S.:fJs+PsBJrB)g԰ш KsJ(q=4tM7B9Ϩg'b1܃ ++Z4C$=1t<ҺG5,Bg0`Nn@B(:6==bK9b ePWa?ag7=)j%V r)ᤤSf .e yЄxv}]w|=ݵC)wfjn_~t'ysGﲒuw=뫣BtNqO&C@n0vy:[oVu۵c۹ǧw)活Iov.rhI9G}o%fwO DnpxRBGXfC뷚5&vגT̜1!Ggΐ1B Po|CUw5ٔÖ+X%Bov?VcI˹o}- Joͷ tu?W}>Ww/˛+x37_]W͋7Y_~O1I_}^׻;+\$X*;m_:eY*noVd͊nj`H<`Bz&j %eUDy=C$WV/+4Ǫ(YF UR](h{qmsrrĒ'߽ʎG4)bA벫eS/s˪{~nU@]^@=tcUf{qPBFP"A,k= HHI< jF?NL͗&z5B3=XYy6pOzCYU8YYX5$i/|,gv,ᠤPpN> /nj%﹗d\Cq*n 9 L02` ~u:`> x0\vWybFLwq9l&:¬f%"H< MhhPŜ @^7W銌#o@'  ۶ bUxk <*C\!k@Qj+P}f-ӳZY m#$*mB_ ݞ ݭp_ʇ@<(8 xƁg]7rHqjǠ(GCNYUNt1DFh!Fr[{b[rq zMHQږdb z@YO;}#eelr8h!F&:` 9|MKq}(D?l@R]w#$e-\Խ? s}}͉b}=qjlz}Ʀovhw>3d<ސƐ<|% Srpu:SR>r-)P{6INS'~!y$ݾm9s}hKZ2w#I^iwQf)J^:%N=i#t8>V gOtus?؝2-PD%Iat>`vNز?>C5Sm1@OP gN!+o+&%0M8Uj@ahry9/cENTl"^i^H[,<_b ;.Iֵp$CH)kKU {<)ZiD7V֊9a7UP jCMa!ǃp8D՚@ bL݇ӓlH lzt ,tXYkF(sF^qSaj b.6U9ADVjAJa1dbjy5)]Zp?!\!$)8<d:]BA4cPHWN)ِPȕ6m7zj냕XऴJHa:LKflQ6(Ta*(8tږS>Eb (E (0S71"K0ͤb4!"\1F\|ENx" ULU9\bC=%ĺF㶧U lHȫH }ɞ~ —A7 H5K,pb:.\`"vqDg ^{A%E\df-yQoLB[V2rһFJbÌS/iؽ Glz鑜g:DŽݟS?u3mm}ퟆy:z a;v;K2}v坲,HSOu{K۶jyi7t L%O2fp,}ڿK*eߖTM'}ӛ= Vd3X:Rsx?7~z Kow_˔hmkcCs4a3s-ꃆ7}?wI'STm#)n d_M}*o˿|zKi͕TjJ:Ѕݑȑg5٬!)ٚ#Ep][$w鲎x˦q<2c\TB! 1G5qfOM5IH$2|Ιd:.{2h†W {ῲ#dD,RDJv'UچƘTt 4[E]y)e?TÌK# 8_5:nJo@QgӺԃ]C٠g唰9 P@ln2.&\:E)jJA  Ssr ri]Jm߾9N?4u]ڝ_onV릜(0r7\ moebSI& [~( K h~˕HL'E~Ϝj8fq?ÉsaEN4d ygz}| \mr8=luYܢ6Uwi̯ӇUJ/ҧݛ_IW̔^3]û!ͻ"W}X}9T:]i\w߯U]OfuJiG hڮ=6OdcR xd'O풡m 1uSݲvOu6|⓹~QW0y]rjǬkIT˫Us3V-IVպVc7l028abn&]֙ dvp^[]͋S~]͜mzUl>Ƞ8㱮=%9< J꫺([i}NV*{*$ Z^JpXPt~(gHxf}?<ܬ-@Έ%(=qQÇŴqʕ4ѪX*i4E1m 2̔"Sw`UsWZ/J}\E曲fV}m*ټip"EGq6'}VJt\x>i}# l](gSA<y<.c [E3m p펌mK3^A@9${ǧL'JbDu+B͊NybԗYYkq5)u8b74C" HWӈ62pk9RruuQ"'B ji(~`@W%,t#* AL!#뻑H:l0PER1tkF Lz C\7)M=fF]f{'m" \Aq+t\f(f8ϣUpM]m՞U_^2Ę!8  x2}~lY%XtWMB.P}U+nZ38=}۟}{Qo)GCJ&؝tUw9 ݽwbjjʩϻ=IWflԫ2fc'w}mF׬L}Q{{ly<}Y} tلR:VJ#Pl~@imlpvilOL7Z3EH):2eax.s` XNL@ "QMXDx .NM6=G!0 tt3xuG*s qq׀} bNG7QTU> BZ`Pj)\ōR< 2+Xy]5% htS ǀńLHUDd-eP2)igA}flƻ=ro2&#kga ZJ`3Mq]Gdc\]T7K[*a*KKq:h<#S[Ν~2y˩Y/R.+n0iZ;!js]qbg>:]^oP =Oklęa·zM3_I:nѐYVe|JS'GDj*}0y[_TzsSESFJVV8PpOoa&-j%$^~TW}Jn^n8.mH2KS_'kTI8cɆv4inmfB%aQ|#K`fݣҭl44('M|.@i3K-3׀[(NloL&V2A}ő}o|Ԉ2a:q8 66dWX_2M !Ruzv<O i`*XD\RP\I!r뷇cB?mNqY܆"wĖO_Za*88U+Hk3 N ݚi?| _lsC9 ;0 lVJ+/40 Hyġ}DL4XDCBwVR "0PBAM UC tA*+$0w}/5-Y '@uiB] n@hhFD:!nYHbYMn5?%D"GC qrPn`DZ"='P eAJ;n n⎀桄<?pùZ-%t4J\uM<Ÿ; PDpg)%K_[V "vAU8)N9&UxPʂjA`hA\WqdUSUJTqB%&OgX&`f?1gp ߻J bꦎM}G崚1z @A?7<ғ$>/%`AAtR`6 ""Fe -=2pkS4VMTZR5yNAVJt0vDLfLxv:5+t[K"^YMFR3']5_\mrU:cG㨏s^W}[&/aWBRsUs=m>6&/݉|ȆH!]kT:At^"M6JG}~&w:^Tg3r}m@MҨ9O>˱22iܥŊܧ=4 }7O}wdkx&DOTgF 1/jJKM,]F/ʺ!ӛ,ɀ [fݽ?~̦Hx8u>Mҷ-cK'Wˏ$7wWJKVW7W^}iγ%>Bt̹ÑifUtsuKV'qXtbgn|k28> Es_g9gX[D 1њ"1u<ф~N( 7ˉ|l销N՜@cxK3&dOf+ *` 2ܠ#A(iK,[ ˣpq`: AJpҡ3l p eĥ Kw}Fu+Nج~Ȁ8p oS"$BR}_>XgMU^}mN!AubsM0WJp3=t5nkaE2 pԁ@q(],1F0^;4r_Zk QE eVpa˞xbGs. 7nġǜ!OeUmCAVp(#a+땬T:)ۨ2n$M!TJc Pq_#eA8HM`m k@3 4l"P̌r`&ײp:ֶhTf 껞M`C` vxa0@%t7!妥Jf @(öK '>@t0Mi&UjJ,l D .`M"8 T!2 Jbt.=r|30c#UxiYBj~S l _F U6# AX7a<9`PP~sjgYkn[>ѧttY昐`Bf҆&.DSpS̚p[QOyBfF6෈zqq0bZG[ZP`g!Z ,(.J#Eq"<(gBw9 '.b&pN^1Ҭg3w .ƈƎ)Ԟt N]L\sXSI2[2jUuZ15]Y;lL•?SIJfY3X0y\)z3%T@W$NuN!޴V yzz;;/*֏D4yՐ#gU㼾~Q5Zy,;?b-zNH {nZis= ";XxUI!TMD zk餮2vEs.kXЪwnd$Y燹OKUY{Y`k`͸ ҈f4i iv8zZUW̬PoW7={{=[ZAgpL{rC1+oAE:{pLDϰ9_}㕈{3/Zm)5=Ŧn|;B 1#e>jIVV낖߻ZϗU9LV萞;4-ɪᲡxX-εNx?|{U.}D&QYV ⑌z]ryX?̊N>]\g}g?ٓ>-7*jtoo7C1 6나]:y9ڐG7))4LԻ砨i)NB GX ͠J4dX&X1{#y,ɀ:\T㢵`9Z&+׭aBGiW ,adAmo1e*+Rh%JY-W.awXzeI-.\=avN/xM1Ni@@_ou ܂3'8P$`@BTgg`0.'0gE2+qa?#ʚRf~:St[ *#quysuKUh@chqIʹS\5zu ;M&ޝ͚^*A"*tv[O|l-†CĢ'TJyn`4p79L4GŸ=́ ߠ(60kйhjENA$rveIIEB .L^[@@ P|nB9%D˺U6=7 ' T["M#&pv)d0a| kLZXh=i ُd(fLz8E?"Rr#hr`v̡cyR3NH+UJ0p\hf5Nuj"%rs{h+@P@Z[t(0Ji `[f)J r[l)J n$t)AhbAADdxd掷p'UWڰ)܈)XV{sC7+$>?Mb~Hi⪵a Kh)JN*i%Ĉ& =S\?z0'{_=z#Fk1AIVe@'8t%RwwPOs?Lq̀ϑ@ƅ)סrXK4QGFx>]gOm,ME*S1q*04'Xwܑy8@(UZ7J2t:j~/WG# `~6-e"dqWOMD[ l+Q}r]CV5C`N Ni9Mц %P(|WV<(Qsf)H(G-N?Ǯ9$>5}P /{3nZ~D5xN7/8N:z2E6SNO[hvO%DJbֆDVe{Z8i\Ê+O98u.nTDLVVm. rA9UT'[" &8P( :DAGyuf@ۅ"WlΆl#q =a : Ğ˶PQ*"[L?L -^> kҺR/^wzg3tA2@JOpD5 <[n '#ـ] 88v9SS ;ۛͦ!w)*F~td-AcnLʡiN,;e0voczGgbJb:eTo4a$O?FD!:Q[\\&Lu̓{Y N.9cI2dd%g4+ZRr-9\c]%}ZiEܦ\~9]t k^7W5)ڢ,0vy$i>)Z#~Э>ܝ=c}ɑN%#:$njC3T_qcURt_GW~<zP63’3O4~OCyxz}J6bqq~$UK"[us^\.a]dͫۗItfoNI]$Lxu87||::9PRA QyĶ(Q"J &;l7BZ`OZ1- NI`0?8p qW8zlʩڨMX**}w 3RmӲ*z,@clv8C| %(ƴ`B"ui j+ ':Y?&(B|%NL~tBݸu.Э8 2yBN:O*( & ݁b~#sa@?,* ,%pW8%"8&Pw6:D9y% hq=DX!MPJ7cp%pӂib_ZZl%OY(& JOi+ӞK0s[SE\ect+>̐,N|lz\ ՜tj']EAD1{#53h t`28z|'Γ(4]*ZGN!2TPPbӊמ 0(אA @ײPs< tl&x ~x`qC?WM-04bjEȯscezaK<ZM~^qf,Kd֯W8}0OIzSg.F.^1rlo9BTBGM!['P^nq'IPU ]nV75{ϊŒ3Sh$r&zyHbɩ77H8!O:NHJ>>PѾlLIr2圚:^?:A\r8%Zvr̗اÁe7%%ʔ-ILT۩^\IvQ#mr!W:s` I$ٮX24 U ח ˈʢn~?ڐ,Viꚿ>+ۤEZAQA0I Y˳񻦼7rW\lUy0j>Y^V%Se0rcT iAwȵ @~E2V|oG-ZJ +i]GV7AuG/M7nX7盾kepI픠JQ8vaϣqP!d ӿe]~]<"Yyad^;`x"Uw_߼~1Qv+61d҈Z Uۢ&L޿wb}y_SXNŷ_޾wmu84l0о1?ĉ4J?^7ƍQ ؟:c!64⊢clrF9mB2C5( e% 5ArԂfZ)i3TMy BB*gYaz"E9Z?럽;I) {4.irBD &6;o)S(]J:adm0@ 2c3%6J톫(\)KAY Ўɴ%Й qwRu J[v fO(W P\@is Wi9` \8 NJp 5UB'DpJp"8zaD~Vb9-[PZ.0c@O q&̥[q4) D޺ jND"i6-sSv :f0ž\ ZZɒ r6d9◰)=[)@3te'$=Olγ ѻ_<wрFP+9lGD]J:~YABq= ``f@nJL き ]"y)0@Ӆv NZb 4Q"Ni5% V2;rt"7zlE8% jƲĠloׁ>ZlH>9`hT'& @%~y@SZd)aՐ-}~KRrf>[݁S+~ Cw[NZp$"Ҁusۏ}eD| >c^e{i}X?&bʡ<~D21lQt:rgv2-V]ލ>Y&˫MܤIXV٣Ź7[#HvCOm`ORYo,Y=y<{wu1A8mdG/:xr'EJj,Ϻ/']\*.{q[<9ܐEmzyfˡCΗܞ}2^7 Nwû?ôȳ<~ޣ\cBFK&#KAz^JwNjQUCdmc-IeAoNlMm:v7Jی%힦dOw ƤXzm [W"vѶ4l[[z9aUpbMáo7e6S_$}%6!REWTqfoacҐ2;&?صdyڳ:d:loIj1L]WggJAc3r&(k,rXMw7o^ -ʆH|수b5SKYJ$<!a3~Ѕ04W_XtS߁?%{>XU:/Yh@ ӈ9iwSv~19^dѴ}LibE!TIٍ͖H=mz$b+FA?-':K_2g~1v1>]'֯BLT]̪抦.pjBǽr۾_x)/?0젔1 ]b*Fǭbe+sdP0117h贒A,qjK%=- RЃNZAlr%*>Œ*k~ĭ vV7Y- oPtqNXE!!@u 4A(p.t13AKZ1*wꯉ(4p+sWҾB?AD Vh%^Q"8 b6D_3Cb `P>@tZa:G0ɝ@U kGpA\?3 8lBIGH ns Hn~B ݭfAP1]&(#fUIz`3%)tW9b&}6-zizЯVzT ۛb蛋Пnn!_?ͯe~X?O}^_彟eO>_;7=9A珫ٰ}_}d>zsHQɊvYew O?jy~}M %;l|ٽuc\ey. {w-=M?>WˤoO}blϿAmdd܎-Kf?!W OI յEWIl T.#+)</6وC"%F1?o_=/{K2^?$_t_Դa T$ni$\8Z1ֵR??V|m '6H>o0J s8CJ j L( D) tJ!- [f[ T49A,e(>BG٬f\ 9)VĐA hWOhOS)p< ?2ONmB~]p4:dsPܜyPt$Dͯo^~nq5,ɝdj=Nd'̬ߧYvo)}/{_Ǵ5W{tϾk7myy?Ux|l{59/_6Žw'E8\?ݭ~M^7ţ˵rIu3e}TS|}ޓ/9+O_ܿzǟ^>8[>c;o>k6w>?ۯ~Qu>{*|5+=;8Ƥ3)ۡ:EZӣd&IYc9MsVd>nǪ^tݗA/ݢ mHm7YY5/cܻLvz~1[/+Ltwoh6?d{LvS͝o 5 XףҜ򤫳d:/_ۏICRi6EV>xnQ.i,ed{=ye3Vyř*F |;:u\zWf>sm`3Ohg߬,4!uZpiyji;&3TF)_y!ZJ)6z\yQ?NMȓK_z؀2=ض|͗,bM&{(У諞 CJ.j ӰJs HЌNfDNOjjZ#œ&!l]Fv,} => 1P_ݢ$',7p(Dq<br#p9>qf,2*1hDH=Ob)G1kpÔW903':5#JQZ XK2#:"0j'ײN@+%tk3n͟ac}ʞ̆z2w2<Dl *khJ 6Df6 ݡjB˚:Dpf'tZCuVkCijU."`밹 fZgY3PE-UJ>Yh3l֍FlNWs:& 0UbM)HS` U.-= J Z @,N DfC1*8gAf=.+kb#uWU 0Aw 8D (Vz .gwվ 8x' NJ)ᇢǸP\2j+}~mGZ@b*_C9z\q\8z1k?UqŅ1ݱ 8 BK4QLAzM \"Cxh*prB0^E8yrWD1ږIJrB9qxk;h)xB>h( ]Zd45;>8DAS 8< :)sA8'YpP?Ϯr=UkL-%9##C}rνqVk`G3Ws..}(n_{Y~s84CZW*.TGF5/nvVWWtY5rq%}RNw-r9,|G=~qAVW|rJ;BðJˋw!P_vdٓqdUuv'XE8OW>Dg,* CK@!6Ž,#zRaNEgK CvnqYJ1#e+A76ap~UR~ +zt0|Zfb8p&$Evoɘ;=ZUNl$Yxbۧo9?!Ͽ]EZ$wT97TöolV`ZW&ݾ}y}~~ys{znnvns{vw{SuC |{k77osvs=lo߾}jhH'wZ6- [nf7 a=im~6a\o7})Zv蚾ٍ~NF=o8 ݒ^"v+Hb#Ҋ R{,%zVH_֫0g7֌R_*̤]iE_\.~Yg ܃ UzRlӑ|2) кP"g)z\S.Yzd8X"p%_HtjiH3=ަ=- I5,zrh+.R/ mQ/pXb;=dAfC,BG0~lUǠVұP(1e/6B%06n `(G0V(>۬. 7K]S)%v/F@ vw:6C # psp(BNY*tms[1di9R&LVpWm:4j)у{hZAPk6"aSDf P&mR+,҈ش)+<3Vpwtxl0K[E8ŜP Mn? Y?J"(&€8]8%&% F̆Q *` zgcQpL]RuIŽɸ:XPu 8i5֍p#Q-nlc.z@dNq 6K+t.N I(r G5JldM7HIu2p-"DD~ų$~ %US\dcps\7tФP, q}ӉVLRur ޥHTi.PuoRx~~wjMZq}ӓ};g>HPԫ tq OTeʧƪX,勋^/u vzIIA6;NĬ[Wyng\sul\h%A?N*o~Ki%,5ǡl쪂TIG=1TMP'M67v?vMHVr$Mu!S7cH۴}[p֖.߾&)U/waUfv8pӭn96W*n^QgN-vZٴn>]㼬Iso~w7/#F{Ȍ0qR~\,֋lG8JNƌA/i{|!^q1WY(a3F,-e ~qFfA;F'H5IN (Dv#Ytz 6Y _e/z 2=HB7C2R.[: P?eI<6h>R!-Ƅ.`EMIb d eV˺bJ%~@eD }eE^V x !C>Zɾ/  wYj|*[qs,/\Y4q"IbcU]=&Oj{'U .i0|TJ,hU42!rWO) SCMi.NpfJC褊UZAp&m ?+1 H2I8 o \o\Zp&Oi&nFgpcC vɸu"h@.u l M@w@p(-hU ̔~&DI@AR7"!qg!"(al %U`fl@ X-UL˭p }ɸJ 1&hZy*L6J( hf5m,bnAs+cIJ0ST3 )0Pe[p[1'"x}t+?"wS\8.OZCqDZ QTLJmP{5*M@bsjECOriTT_#e|~kSsOV&)рD;ʤt@pK:I)0:'0zmÌJ: 8YOV-KI<9"<9Z-sZq7lݽh"k9 -uQ!r$4cu,./7؏VgW̑$$^ng?/w/Rr0gG9s2^fˠ,Zp\NJBU$0r'822KBd$l:¹6%NTOu12_"*2t-pOqdXl;9WQZ8Ҥ2)KQ@IA8tFαJ8/f61 1i@ĭyCwvFNrj0d\!e!xw!ȹ`G45vIV Bj@!@)݄Y4?nƐ=zO|jYkOd?uEvǔ'YPd+^~I^HA첫Ջr ?2i_Zg0[u9O޲4u|`pqDMCr5jFdTih$GllVND{އ$=_`Q.>Ͱ5`؛`qC㓏X$l=p$7M4*y,N+t|w{ M /$H"lp;ILMf 0{e U8U3شC+e舋q(wGG OV "Cc%RtY)0WAqwKj (& @B&: i23,eC46g)tĭqBHt(F&81݃*)֑}0qLw2X%8l fl'Mt/h2ќ{ ~0NyBZR'mO=g*;0`Ry& ы.a#ʼ\eE #TmBt(4k"&t = s1YU* qPKK|^<+2WJ\UTB *!==9%HU3vONybD2Fv8 W(UpwXU}ႢӁH(Ţ{%l)#2Q rPaN蠉0$7u}-.|2CH[!Or}Q\;S\ҼI\\}x~oxodUR+yX y//jcaZ3,Nz[ޜ_eqzo+V랓QJ෋r,vwspS_s꛷^~)CL9/wqǹMqrVӆa䬖OMq/,&پI80pVƢ%C0\v^u)'hUeyயl\"jXŵaFZܢ (46D,WЌ vK8E|weצyM3A#nvO'8kNɠmtAmOe%m ]⬯cx:Z8:E-W\c|B+I!.E~`P/^ṕ39 pP Ds;qx\&r4;a DT(z}Fv#E}8^~"HX\ˌK$2Ҏ67UM*%WB2N:\1VzWcf>d4VJ*rg!xR6L*SƴI9eza8F*Ͽfu~ayHg_α>_5WEz+ez}rz[O-Аkǿ|$Ű_m\P]]q [ tl}`E,k}j#]+UƼ߮fqj/IOoE1Ҳ>{N^] rXp~:I3XOIqXY=,틦~3akI>r}>{*Дk}yrcE\_\:oWftq͗%~ޓ̤o}+V;?pYսzJoWE_=k_~M ԝ~,Lw toD^q*&6GwN 2NݔQGbvu$/h+ /1m 29!f(XK'Px,e%LQ uhccEV;h-O괒t泞.uG={81 PRJFy>R"b.IoGz#Qru)6i I9UgY-KlD,*Hr`PW4Ag][w:E_[$[WqM'- l}딮WvlHbT_:=ۜ"rh AP[bu#܌tުMH.Џ3D6‚FvH{s\0LwM6.OH F!tJ8Mi*dB~`IK:b]BDTBJy7lWp۾Jj  @ZtҤw8lPI߂)gkjbO9JUFĬV+VX!M]8ARQ3C '>+a.`]h`3؄-@,ي[AP,h\|De3CD'tass 9k*%U3n}HY!BČ@94ks43LPsSx:sń Yf8i=0܃ 't7QڮV5$M A,b V?c PE@0 q~%Ѓ!ԆqMjHNUJa;@!MTuC5HlD2!ʥZ yTW6x(܊.Xsajp0sAqdA)> 8bP_6'Ø,p"uȞaQ2qd"ЄPD0:ل ǙI 0Qr[|i/i8LA P<AiY*uGQ3ʆE_SS#^4={>^^98^wTaH%I_"a֬ջ4 8]t׿w.$ItVtߴ3mH~^%͸_5]$j_~gsGME?__?-R]t/:vmSfV8嫋N=g)Qm7[/XtthGϲr)Q]T!Q⶷/fJsgF==ioE1V %]X8SpdM;qFll&,,Zi]UvX,SZQjI&wmdVrHgEK\~,2-:6 ohcQ%9 r9{wĐЍ2cOXdq7 S\Q(gKeM}.hf^1z($,T6\D}H?2h]}6`"|Ag`,n38ɶ3!!SY\y9Qg2&ꁒgSs+ф8F.I za27AzH"J#:{cc#ؑ/1Jd"DHa u܅ A$f'xzj>. J#rxhT ܄U}T O}wz`YCC$=&@ ญ6!Zm0 t0 8BG  Q$V  8`F9nB UJ(B qh3D樂 =#Ȗr7ILfhpJ,ev MpR$&7`j,)s: OL嘸1dp6Dz5b"xdspx(!%̔XAql - |XPREdFPc"o6IDAT0j0?)B25pJMlmfuaӱ:"ScH "zA bTg%3BX1j= 鳸wG!L[=D*4 J6;@WC184@`";ց\0a=C;G ~a_?Ҫ T 6RepTOFw)Ѝd<Ձu(]9?h㽴)WB:OWFPB), X9`SN8Me=egPb߰ˠ8fvi@pدS@*ACV[$t:C =}rܾv957_m{(}_~#+;+7_bon:v"ӳ~gYsΚÛoͳn^=Ӷq{t^Y%=Sd"'H78;iAMm-LV!tjEGv!G]YoLvUӒLa$@7XFd6Cϡ# )Si%OҜ+U{Yw;2.+q&%c轗|KeT^/^80lKX>~/mw5m_i_&etjPL2crqJ892 r]2fȖ n Ub])3v-#Ӌ1Ǹ0maٯ %':OۘJs=ɨ֙v~9q:O\LC9qu f)45/diasDž 0w=cYsqG*|PJhl kXe\ NY_k].ZDJ=g0(PBרB u΂ZEM `V">p*ia ݍ]Q8&&<7F s.c>ک(TN7;3AB1bp26(A0U pi]0/TjNb:QBŌ;hj:gnxBB^FP@(=@ 8N! D }3Rw q?p @՞[!t(.q"5ys*i<@U;)lG)%֩:\HdtFN DfBX- Zc*J p*u [ fd) K4ݪhǐb:Dpnr9Ϛn;앛@?U /;3͜3@49V*kA6WOq+A]VmL1sh"ڶϬ AĆA>R)@3hW6D f:֣( G](`.43Cbf`| n n0Z-e(ln%tRt)`fJ9gܜ7\m3|=N$9N45#uR?tM%n+ɨ%+9jmJ+NǴŹpG8 ̀Ph8CJzy҄ZZ5%Y\X1*[bg;t_4]:{59pl-H9?vEΠK~WA<U5b8[Cf R* 1g؆a!ߟ*9|h8QQL :@*8gvoW5/ݼnmzonweTEl~wa6}jq nW=Mڠ0?sPࡕ6 "08m|KUsbw ?Q(W)1qni#mH_HQV}G}~d'u'Br4y5@ _\+} ⴃ,YC~Y1q |?`@{4Se|ZJ*t-qI!grw@ krU% t c]%s (U \Z'XJ+ Qo/&b"J}X3BǙ3=D>PiO5Wz[5/NSIz1uoKFOuۤ2yZ=ӖI[*ehoR2o|"A?I4bR˦J/: eD"tW_OD0lmWy,u! }L͡^/([Uϗ+=G.Z6cՔWLKgX2NqtfSJWOݡn䵧ucQj*2v WDbgh'wx54O hgĘ4@,S6/LJ/عtGZZ3 P N[)fۖTtpYA 8vSf bDIs@k#gh*de8J gt~37*À a:2R9ɧ#@[daA3X\Dŀ Ni=)8(j{kA6p˘ G-{7X v.3֌,M bAZ't6+q =Zu]8V.hhhOUZ  f! %aN8*`RB1N05z(l,~({g H= \gHZ`pa&tMP(͡kP\`' 3@G->c*tI3 o&*CZf49>Z,*E[ ЩZ %蔳EJf#S#_n lM < 3%lh4 8X>rNjMkP*DPp(nE0evx[!Jx`wĬ48Vb6lV>sZ!vaAj.-n8b6ba'P =bD>"i8 TiQqgt~9aTlN{/p^NK0i(p#Qpp1o>'p+\cItbH: i#4$h!,`RVt1Rd͈"*(Af{@/˂4[.`/&h2Y! K5TɊwlp rnG^d67#a`Θ3ǁAAJ#WC ;4>Lt¶@H3R>Mndhd*uGHE D)o K i^MiꁰdڽrjQ${\2ۇâb`_FǁC4obdCW;V@;fq,_岺CH3Cl>lncg̾9tbGW"ZtX}%~W2O~>uzؘ @ rd`MӼ05Er 3dպI#KE}Mmu6e&Ӻ.96y50Me>͡u 1ДL,vL %:;T׵:y VQ='ڞ$[-AcG,640&];9=G0X\;zq9i3>@-aC YėQU@pS%,abCTx-cJ+8MQfeZ">I6p۔40UmќD9Pڨ]~A\tQl㫣`YtD`R㌩R*$TkgK$<9m:U U4C ~&%]V/u}j@F0icU Ah@o() Bqa ~4Di舘Vp DDA0oZ=onl00+< 3U " XĚ*CN_졭IZ'tpx(i2T YJlo A  0C@&87 PW)=/V͂ 85lNvV Z2tJ<3PD&WQh) X̯ '@uf*6s!.`ttk] MC b fb%f3g5꒦Y5ϭ3nRR%Rd\͎aCX n%."TYi0M ~(U7Aq_PB Nims hB'YΔTg`ND ZNS{El TAU!&Y%cgS(A"F9^t:'˺\rՆqVKlt"eyGՠY )Y.:2U/0 '`iU>rgW*`C3#8iH]5)2n&uJ3AB Eyܸz¬A:TJКg)iui Ao y|y :.Rķ/z<ՓUege1L$ݜ$"=!零oI!ٯbte<&~>+X/8(ZA;ΎYrJw]/=A|ܥ6<*wL<]u}߰b+{B]i6XfgzY8痏U3}:KNj=YabJD/!a#q 9;nwnw}oF(piwUӶ04qfwKHcqB2B82כ#*y}JiaDZZHحݸoƢZzӷNĺ=޽ƛ 2tm'E}=&na})ۑ=7&Ӹn8’%u'u ay4Lb=dcQa+i(Y9kpӌI Mg.{fRXhcobq5rJe">& %aQ┦hMLfc$%tefs  ?8)1H\jtUDbOb$̫:pPn]JMfjI~[:ouU-IF#eeO0 yWjҖ#b 3,a W%LKi3uM 4 Hwԩy<6 ,9E6z f+֜ 2=G,sLʜdol^7o777}dl@20E?1q\7-!Sꋥ|m[%{=ȳ2ۧ߾xҲ ϟןݔSnwkrś۶'۟oo6. D:vm ɐ/r\dx+Ӿ2 f:fJn}; _?.ꥲ < nǀ!M}7x}}i|4zsp# aj0 It=p?P̰:)xE Xnblz"Qxb| [sӴ$%)ד)A¨ϻDjc9I臁r+ֻk%A+0 ؐ570#:M 3XxD| }Zhr^T?^P0=Z ] pk`"v~ A=KPB=7Ŝ.=A[S9*DzjU X9>giױ>0w~(uu^*n%lX& DƒOP8c85@!0@3V`CGzC }옧DMD/mjxAn׈J]HXCn,d}( Ms݄ fh Tb)pO):uND()-@ j~YJ+8i5@yw͜ U̞u>ifpө c&,hġ`Vxh:ѳ)7PEE@ `6ܜ  @5G1qy{ʓ:Pt"(+* 沬7*q)瑲~hwa%BD.9M<f H)С+Pc0zW i,&[l(* +lٙnTH/FK1;4"ZjHѢ& xE oU䆾b9Cv[s@8 8wQ G<5L0phg`?4qۑO8hKǎ1S>o(*90~BzQ$L41R2=Ku|3AReMA ǡj)Mn53Dtyx@ϖ b.:x ]4 8%M 3H~r۸VMȪo8ȧw'fSnHJ?C԰;,%XeFu>8/%ri~uh(;$:M>rsĩ?'CW֨M"Tbq,Ӭjx$e7Շɋ߿yOPr"zuB Q x1vՂĎr}5ռX!OϞ>;G[~}˦"[pM'43~2g$͓.ۡ2`Mۼ|v~Obs_dXW|DR{02˺Z[=V0˵ q'c9 cY6pDdl/ {t9*&3Pb&h :KIRBB+qlw?Б-Jf6DB"@,tH'$xUx/ m|QG|k r݁nЬ*wI2ь>:wj!̆cUT~"לdqa٪Ae3Rts%F(Fv>vbO4g@@XIQ`.-31w<7Zp-6S$.q˽6~kCBwU`Rf4l #rATKLcoA8Ipo7oŠxifhph6D "nCfk:”D΀ 0ephg"%8"8 A(c#nmi f7: nPb0PfY"< U0~At=Ӕ >w}"H+ոU 7s?Q!CU I68gIkP}fjZ}MCwv8e6cW!ŅWЇq̓;ht@<իBFpaC 8YGp)J_ }p/5@ W})CRR咮'h,E9K%ԙqtsEò>`Web^@k>P{=BI_d]& \DD,b01.h -6=|?77͚uޤ qKp0={)yZ ;]ʵb').]uҾe^zr)ǜč6.S1`',X1ƆC/a1;Ha'θ~}SofumuAR5$1VVAs{7; hǢ3Hr$6]f%n3Dz-u8(}>)l馜UWN}YVi"˫L>- ~#,,{F}IKӬLYk_3XI$?MѬagȧbHk=MJ͞ 7kUJ6o&PsH^&PlU9\5/miJs&-rXY}x1^~k%zTqW/jehov|?ER6O;l)gW݃&G Ӭپjhx􇗟r"_O,g]lqi7DjĔknj7ӻ#&*s"R(kTLCV0Fe,B+\%KjWD{cwV_섫W XLծpKYl$vYU%aqs8Hˆܝ.E&^-&J ~@toE?K 2: ?3a}Apn?>K\>8Ƙǖ4,"9#3ל@z,:GDaFw8IqnCèð3D#.5UÙ⁆4à`ʹJn[Zᮼ!]+b -"D`@U;Ep8a DB!r7Ml[ 5V+td]uu+0PD,!+9*l)h8AkpO)aÈ~3nO@NjP :&PgIJ@+VVJA,K9 Z "v" {j%V'%P샙ui6 pZ3 1X'.K$?P, Ź h3"%i3Q n줉hDp?um_ ۳*ڭw  '`&k:nq.i=k]DAݹ X?y:N՜)as+?ocmt =a)}N&D8E m(.x%TsD HUЊEY:ʘ5L}NMUZHNs0WAŪ(M"]FUBP& @b@.(a&4QEgW#0J)ZR?c6.W(Ha&"'oҸ ){(栙k@\`chP9MA`Ec,XF~VHR$~[G!` X,1-Láٷk;fR|dS%˻E;e{f];MŢS,os='k>fS>$ys)OgCQJQJJQ(CمϤIWMLX7/a\>XK-.{(0dM~>?JW:8!7O^8\5Чl/o{ɴxm6}U~' mfl.]IfU~]}x,CqC~?9{{ۡ:9>?4Y^1e4O++8%qF%kȝ R+Cs?Iå -XL _;fZtLOkX}\ad0ɵvU^ț*8Iѧp%8`68e:~܃VhgZY U'Yu7):Yˁ6=ލns]X?{#u#QO$+hx_T5>h_(Ӷ,!>VD TZ߈)ak& /i )7RU "8>['Jp Zyee;pGh8A#B(@uĨsSZ$fO*,qMR%Ce|\Tܤ7]lalh}JTU93d1?iYs&Ez#IOBbJ @Z-V2ח fyꗝʱIvm:EoȐfOvu<݆\ v+$Onv-YS2.{h]d }gwŶlfuVo@Cu4禺wөdSdE[byMZgvGQ}dyxwX.[]~%Wʇ?$}ɡ|%.]<ŏ?Fzb q7Ë}鳩ZucOeWa;dV{'ow7C/ݼXإ>'+ε3-CO}Y),v\.>'?x#9^/XWm)Y~niHn||g՗qɻ2QFs9}L` pd yN )ZzrTAZ.3 ΩVQu*tw7B ܰ(BFz@crVƟ:6ᢺʡ@PHb7+BfRza :t(Ёザs$ q2DtIlYz)ۡ*`6G!rɹ"Bf#PQ"r0, ^} 1B|XPi*Gn4 Wǟ1Z!B#Cn0">ʁ (@u1*tY\k0NI iiwo]†K %DM@; :GApf(-) .. gf:J(> xBt3SRՙnYP0@nui6JN @ 3E@̉~ӡ7;@8%jicβ-n倥 iPPkz")Z/(N4C8L+3(<p  f^?8vird8gJd5Up:U(6(Vb)@ ޥP,X@(iN! NzprV2K "3"nuM^g13KipS2M-J.$": I<,J WJ#@wLH I&$8яR4qeVQJY(6) @ֈ'A GivQNM1g1jRU{W p#.'K" 8dϜ QJd1F&Pq$(Pk8W [>`͐i[Xdg йj3M^ox_@BĒe^ c!~;'CT@E-D85[ĢnM]>x.S˴ҏoϿɆCVx:7~޽?#_}wQxO|;NX?{8нٗ=Gxy~6>~N_}NI~ϲz1yQo~}ͲʺϪN4777/]9!V=IfLtazH?[zWvm`p2׏>x{CwOhmt(2.YQ/dVlnLjs;;KWk(uk/㰕Otu3M'_=, y-N,v޻'C>Jsh#SD&Qj)NJKwNdeg|˩ >ɰȇ|8.O{VX>yOw΍al7i-mrإD$in,O?coݫi߼쮟o/Ji$i0 *&i% )}\Ѫ`:E`zg ̾@P{BO?Sn݆bY4RDCyDA?WwƎFCif}=@. y647/4d9Vꎎ#:IG%)|&Vw\cp6"?b(%zlc4)#B%ZC 8p\P|R-K;RK'}lnjl뗺wU^C+i/fҫ֏?I\?jwf(W?a{yϖg?{^RM5c}?Nb琲:_~'0WjȦoR?N~wC 4^q|X=x̄(4Wˋ{Oۼ20SF&YUn[ч~쯛eVNU}N<'H<8[\r:Z:_{}?pIai*|ivLraW59yӘL<b}jY'R¼]fݎԥvx U>mlv %ݽ,ۼtooOot}zλd}ez,}:ܾ__?v~E{}@n|u_ m~=!bo6O?o;g1⨪9ZfgV-ך5JɈV0|I>R@lU` ReVJ ZLV8 OXƸWN®mXs e{@EtU6b^ 0E]F6btf 6JobR \{R5~TxN̸u%ael x&ꀤ@5lAD(&+3(4O|,O7D?wtm;L|CK.ud)R0MO&(v\2 x dU4F`Ȕ56 38D[V+E% ?l:M փBDE7 UPBծؠSXq)R`Ų恂;7T4!=D:PjUfǮt++EՁ50:gh@G<< sRc U@[D Ě PPb:U3clwd'a-Xs8&DʙH!}R4c𺣊8l642z FL48ln0jǐٌPHILZ^P"&ziz ,wZR/ jQF ww,+lbJ:io$߶_'x?-f̱WNwCWϧz$_ӹ٤ͮ9l2#0gWn+Iv3띾W4:GLmeLc /aX| =+HV5$c͸<,&ֱ}@64N9 X44$Bԗz<Ջ!6%]0vYǓp%S,]mtu0MEc 6!=]tŤwÔKr߳L+H, M$D^D|hENu cFGalu( }>i_!A%ep}rJtzU&{œ,"TW46P0XZ,3|Rƥ-InX@+K{ %K fwP.Mʅ*;JSD=ia"Z^QȬ*bIPZ ംX-̎=&w`Cբ (D0@wbV+Hn1? <}z4?a-jdC( q[BmiD D nx.‰ ~@+m>a K0.B1'8MC13 ;AhQIo A".8U@DVN+(8Zs]qY/ X@\#Y΀P5Y- =t&بRAӲO_<|(-Z- ̦S:b0 6RVkh;=KP9U{**D˂ =MVh7L 36n)Q ǰC.Sif@4?[(  t4vW KK̀ g8j6S-u7QBN+U:OP|W ~`;ՓjDspsZ?Lm4Cd@%'8tW ҵ^͑B7}0+Aِn(%yD6tԅN yWL%hY%*]T8 ˱=6C\Qh`tWS5DJ,z}]V!@X r]usdTh8C%)t猈1?B%FhCyb.2GLy8p zO%ˋAB3݄T0'%ѩOu1r@4d8b`88؄vTv$4ɐ~yXG7!ћpmdf̪ :c7~{-luKJհDvi}:&OC貂x1aWQ]e<{3lnaˉ~|e!Y/mrkey~InQZӴv)Ϧ>zcV0 2 -\PͲ#*MBcԇC6dyZ3RmCցK`<[aWjV5{L6mZ$.~fKw8]#HLݩe*U=td5Հ&NJp?3 qҜMz8umS:c41Bu=.2ye1ri8TF6*A&茨5/LpӜU1|ỏ..xQMUgXޝ99#^)L1iZs갶#X 3bCaq )t Qpe(;bUJ3dUmPtJ % ™0qBNuPc$ Jt%&?$d*<^rGY<EK+ B>D>9*B'ކ] 1=41nQ\3;vl1je7m9f?~PxD&uI Wz89aEUd6uE:I1 $J9CR]kN6=T?֠Or'pQN вKIխit-2=vQxr1>yiM說*WD}{idc -j4/E9횱k:+d$\XLzc c,%3}яuf(cW}7 -HHO5ӦotKG=Uj5(R:3=KY7IY)+2&ngXV+&+ ' b2eL˅^zP ~YIRz_52!K;5bQBg d`D^$#U||lX>kk#YBl/@޸ULb+3J<Eؘt-dlɮe 8jAb*9 J %F=C CQ y˶[8i&;Mһ (GJD]xT%x[M69L[|G7D Q%rlAq?a|V 2)u1ǝ1t+@%+ˈ椴rJѪ(;`m3?>tA{ Jhq6Y @*fn(Q"j-X@AJ.tA]Tጫ.UKr4 DWn9&<@38.QOnr|je}1jfkUl1asq՝2]g\n#κCoYqJ.a.Nk󧁦[]UHU^P 2;`=99AfCplqZ)bN+G7$7'%M0C Mb3&Rm*S %48%2lWéVqM N <ĜG!'"fLs8&B=#K:q8P--b<^}G4Q1YץX8T_.>l pL@d/Df`\ td]ѽ)[k圏Ʒ]]Ue{#xO 1<PjҟŸ[rE˗=C0vxqZDRe>wdL \'y̎,v]p$׭N$˫5i $4pb' jgj9> L8+i9OiM & /d˄,t+%>: H*Kr,=T?ԭ--V]}ZӣJ`Ɨj̉Q73\˥Q[%t׉]$O:%j@X Sq>.%bva-lJӴH=]է?zl3XnLNuVqD?98cǍxZp*V_,E倔LfE1Nz? 0U#VE-@琌*J mAdFSe4΢b4)h̤jn6̚Bf| SZZC*b Z@A0X}E%mX2Sg&xtĨB Twb2!- ]]C3 6kpgHc 1T$5 $6ؗ1˧Wa`1m(t,pKjo\l6Da1Q[dD  D| ҊE'M3t(E o|:Jk@]:'`tb^QA-w~j 8y@F#* 4Y2|T"@ap&L.)tCiSmU /s "J wpWDze*_UK6[d"l"Ն$ې!ؐaH jG7X;NyD͘sG̈+;wbN29O8)q䩥:I09ߛ.7 Q6 zD;hќ#nu1@{+$ ײ:[39aKxTappC+*GGU:E3M`VIZa<`ɝKH29AJvE#*>GK(tp^ә O2&(ׅ3ueлa,;{2OR\]u^yf\u?*T=_yti'$Bƹ͔O `M&epbw%?'lJ&eOܝEL!>;>O??P$헓%T5,.bL 7ڍG&hiCիnR?~$I2LSJz8$b,6흾2n)S!ǏhDkd3(:S.SycW=K4󥁟hH %-,5I['GB)IhfMQ7&d91:@mԉkTJfʮIK|n杦kow6Ge%gRR.;롗ʿ뛢oM Pv!Γŝ=M%cJz<.sWԫZ˞#ܣ "@(& 'L!¡ xA'xgh$3Ͽv2>B L5!f2oY9\b+`$%'؄>H2bPiiE !- x1[j<ծLȓφWyM6_) n W>E!WI֊ErBKc8ikhm6ڍe$ РYhuؽow—<::nOB>ѱ7Nr@[`g'D' ٯ?_!s2dDJ7~Αv!Y\g?lF`9$9?8\sFSNgrr]D5kavt&c_OoFѪ 9Y>cEsq'W]L}qqu;\CkbJCJD~s-myQݗu!0q'u^ySO>{ JʷT~ [IIfL>Qȝc껕ɇLjj`U8Xޠppek(p7V8t鶺IcHY8^2 Dn>tmo;{d|!1䗕Kpf(Iws<$uf bo~ߟ7O_rLTV>Lu٥i[_~9TO} DbY;*^V Ƅ{S !:WdS<?c3go?C:Ov Y1u޲х~gQjΌB.v6|>OP)6Md1 `;<G -BHh Mַoys ~_>᜴)FHb"fnZSfXTUVQ-`b&qWYN[cZ!]^u,>|G%'y73壡oy/ Ye%[?/sR]BAsټ4 a|7b~S>g1IVSe0|2p=pI~'||e6S-h|$syJ ADž'/_ v! dFh?^;!,y +`M'%6_`# 1 xN%ԎI2[WقqvD,v|$iI O Ѻ.Ķ$bxB4<'yE912oe`™Z]w0M iI (za uKn1 ,՜vע3~]a8~4+# fYvQܦﴴĊ֒kɗ1S;~`{VۺJ;̄h &V+BBSے'q."M'3ʼ`{kIWM wJל3("AμNa.1=8 Biql;WY ?Z@ӂ_W_/3!_e!=lO7'/?/?_~{o?y+'χosCOfo[Oo/oNo/~|_~=}|!:|?}|}~o~IC~{{?ںowo&cI{}ƽ=cs6wmm$E9u0x.3Pibd4:B,+m!Vkȳ3yӒsH{NɢKcmٍx͵$7;6 ?ms__WW?Gp zMϢi5Ǿd87Ϣ\^o8[ ,n5մwݦ ԂC{(Rh#bBˏA~G}mw@鼥uwnWHxk63;1=aGZmuCtN:)c2gǚM!9cq*()5{.K6w_>_{786jH-3GʗO }G/Nnb:d,Vf.{3z؝&gZ>~ja<tˋ}IeѷMo,[KT?^>D0?LG:ϫکK6 Z )7d& ~8]oSၵTg5-@d٢nD ؠ< ETfN%d #*  شvϏO \ 1 7t&|̫brY5OX!7_9E+ڈj #uu DJ9~!f'3o]30 9[2o><>PkwW5lIDK`]Pu|b&[[3<[t~3"A|ΛBhVvr39vk ђ,Ĝ0?]-#Ex|ĪP z0`Rv>vVE;f0r v%%'J#ZLNǗڻ.s!z掍B?f`1O|u ogl_zϛΗ܆c(Pnc0jݵ"(RrXSvAZjrdZx`@| l8SmޮaОsЧ~('<jWLb^^{9TfKLAq a*kZoHr7O&s~-%߁E5_|?xΗ@ G`3VᏟ~) `mn$Cߘ_B.o߹ƍ0^EˮoJׯ9__^:-TSC3qRUlGKWW+ ±OS6H,)aT \K+MY򻅄&.)QHyn̛,xNr{^R,ij0HzIxO*CDP-y'OnW_|vf854q F;LBȖZWȚ+>s}Dͅ!wOFe]p~,n 1x1 w#aHnݤfZ JN伏˰%Q `0V8(dzߓ“[2fKi*=dMvCO3h1WXKuNdR1͵"4h٢9_ 0~ tݭМt$`+=5¯('0$r<=Vkce^~U6n2kqc -P^]1D ȋw+"Z_92A6lۑ36<+2({[UeC9OE,D 1P!ITǬH|I)܂"V*c`H$e;ox*? piv`K !,Hv7N`0$`J:'K-w66YTEA{60]$C.(f#ŀBN^?y&G#ڱnXpu!B(*77DU]X8'kPM ]uF:| mވKzdϙ4/%t Cl o'vm}bZTو lq+N9v 4\{E2HL{LLw/o/Ym}4d֢?&UybsJSʭtf![![6\y&qV!+;>}oW__?8ݡVtܼOyFys;5hCƛ(W<-thU ؕH]~&w*Oʜl$`//f$2]SpYK,LMa%;' qe@mGt'K;A͓';yʞO梷jH%Fݓ >CA O|puڇ<Ͼ-8m(3BN tsmf=9*0e^\r?,/_7C8emuUs2}ȟ}7%gQN]Y HwZ*UI3r~o0y͝qؒz/ 3|Ap#۷o1rr!7N  |r6vtw9aQu0򑈛9?&/UXY-IV gnj&`sI;ς2: *⬒ݻ]Q iH5As8TeS*8jH5l+Wxˬ\9W=6u{p~zp,tfEyjE^oeQyƞ '[kRAcP2KPZp1]. %+tCq9n!fg϶ӯߌNWVOOMg,d>q ,|;Mq7)nwVfYq`?Kdυӂ14LiSLSl>;%\)W俾&e5 [)B>K0A&N>M'/>?Mڼ(`~_(ϫ%1R10Jn psIWh04`uH{!Js`sz{Χ4g6]:@+Q^iٵl ^L<߃'L<(u؅41O!ÎhZ!heBMx|NѬhp)iJ,+c>lu| araThSpv}5}UYԽ?W\BJLm/?}'v!H6<߫RJ 2˩k@3/i>CwғomxYX-xTl17C,H]Đ[&P`އgpVr_d ޵W?Z71fHrVh*KB|nOo,f&j;.Z1y HV^7!h Qъ|̐Gϳd `ɍkZ̐dq.:l%Rf'v>aRɇY™?yd*{p]&T37愋2 M&ӱ!$kْ)fU"lCJ?|~xãƃci^ȿԟܘٺpp1) 1Ķۜ++4q3kRk1n_$+6|LmK7_< vv_osP7jY[yx|pRn `u  ]A_yN\bB3tMUn rh{,#zO3(~u(r7,/J/sG 7Ɏo 8[wGR])ӥUOζ؋7\f-}|eɑUOCHcH}H_yjm3 SF1GJE6t0<}O؎2u4r!|~Ͷ:H6p#X-"#eɂ8eɪF?0lUKwWH-L_߿}<9;Y%o\.Udx))eMV(%C 4 (DWX|a:Qiwy@f(7E85b-KZ_/n #&;@˹Rd[ "sY2 ЊetѶϾ*Yn#5U!W yeV B/Y>ؔ6n J2}$ZNޚ4<_~[Wk0uϽXw?l$L9 ۠gN9r !&bH{wIN^Y ռөQq/ wHn}.;+!༰N;W[F90^j@۪x`'7c."B$!d!a "a]pJSb\}M朊.yy̐鶪y#km͖ rVZB*yV5 Iyyh9d_N;տ )9[ ECp0qZLЬ9+MҞL-L5愞d#8g=90Ws8$.+2%}%_6K-O۳YoÑ.vGr#a$L ƈH0~nQ]L:eNz NNxTc&[2y#  hgU+u^ĭ^K\0Js:83_mQSR\ߔimMtݝ6AMO_3r?Ї|7?WP^H Q,$hG ! /.שG_<5qw sy>^}c-Wu/ͼ󐩼^w3w S\wb,ӄ\ӔB) &¤ B L~=L\#?F{2p>GB[^ ̢ v5lj7Yc2I8KwZqlh^'npfKvyZSUN,2]O|Wÿ/__r`ՅZg9.u/|x+nie>|zjST杓^Fh*C7t$4v> ߮"ӗzשC:&opD&r 'L{7QӓX-68̈K5m-0/ߛsdH C(o~tP$wZy^^Kyvz GI1;kfjrwS$[V&#[uCƩ5Kxy lC61O+[iŪ|8`׬mE7.M2_+b;N_&2ۦ5ᢏϫY2O6nhkIؐBώnMV`.$a6z hCo9;++ 2+{V/dʫvWW} &/=h9!?Td X=em`O.񊾴3濳X1 )޺)dn~OM ;]޿o/AX{R~eDAc`LbGDK ?sD˃.0]*6TlA@F2j'HDXͳ 'DlDwg9DprP\0P-.\R-H8 aR-~NV]|E `  JΖ0褤սENJ:K Wu. .fݹҎar֢?jv)i|<-.{s4䘵5H-KD5Җe2n)nCa>uG01El00KCt *?''ÐD `CG1%F2pu|@D(d<`l0I³BOYIEY\wyIބk'nP*;^Ѭtdӵ,əc~<˼djsbPOZ>=oVIOKӷd^&ym55%ۧ rq{L$=!okl]rʐ=͈xMYv޷̩buF9phKKZ3\uip&[C" X-!P5 Y1u,셋NXߡ̧x#ڸ6CxGs.X6o}=K?KIy n77 u0GtA=q%Av~ʋD&c"C Y'RW Zx[?m>ozVR:ejʠMCNn$Y$~sm ܒ6<*L]U>'ȩή. c6:R.yw td7hV MBdM l[D.zDreZc⁛ErEa2Ô e5Yؙ!Ep#{U|A(/㾄SbffS9UOeƳG :7,ΓFF90}@`q֎hSƫ%A\ n!d2⽴$=[~[]L /yIX0B< ,P(Tqijs˅f*Z(lj]M$fv7V+bf>oɹָ̡(wWX_ﰫ\XU3T;设|o_Ef87ȫ8e̡:'r kt׏yL7!257'WiF[k afw#uO"יNHJx-?K)~mS~0_|e0|t9E2#i3~\ Ʉ-]xeIMXj%k Y\M|fr#`ifr̺@ 159 E<oU ֧;BkbG-I`D~Uowd_k -Y2f'7g"fV1˧%b\o+sk xO̚t!=utks0 6>3>*"Q\`BEag&~g[tk7#p? CtǾԨC}ys{+YKE`U l ?2c.'B0G\o&¤Z9+*HxC$TLhv^ϭ.`N <`j 0 1cѥP;9dh Mˉ.bM>N=9#&# s ֓I^Nصn&PIJYxX8<ҎoFW}NwqOHH`7i'G3,*=8؄\wE]\ 1d`k1? 鳋oyzs-ђϜP Û.fK75hx9:VZe矙oB| ESh$c&G31䳝jeZ z%\{7̑rO>$aW)&0Wq09alge*qI޹CE.=D)_yz_5Y/^zpQۆx鑜Fi TLP.WĴfHT+aNH0W/cϒ˳82K Bvn517g]vU]b>m0u36/b%'8(ݸ;!T#噎r#7nv˷o?\&x]f6qת[1s%r)x{tFrMq%~΅==iMփS'H%Xr{rL:+ Rr=̥5=ռ|6yF[t> |ε&6[uF~D%$PM-f9YԲ_t1o_ZFD2 %0xH*EGيK?z#B_ _MV&%AQ|G[ȓOr$7 ig)vXIPn*T]D{DR ]vB`-oLrLhGׯC.1AZD߾}u1(scךϟfId}: /[HTpɰŻsyfh  a _U RPsi ]Έsm]%!]&jt%ԯ "Dɇ/yvZ}tKiDy9{?ηON < !Iޕ)>o8}ȧ>}7*7pWwcσeSMQ}dSu~bۈ)V2g]#N!LXRgvN#O..ah/amh&ycCL.kyէb]03>  rf$)&vTItwhL:՝Z]8Wt*єt ~5S$҃ WaN"$ #]Z96Yݦ `ȣ uxm&3ic`. r ^:#"'VikS%+| a!G|u,r?v~\4J08|Vs] ?D{g`mTn2Wrm֛E*ϓ_Wʭ;t,Oݗ :g?G~ws/͟_9פhO$PR[\ 0!"kFu?DEH>LjмM.aM(fG)H㶭:N?~ȇHF M*tk[)vKl$Gxkwൈ<<@>Dzn vV~Tޫ}~ $#mDVj窦JTQ3pN>9YuW ji2r'ȩۣG|cDAt<9q`\!i-N".ʠ#lFA\큜pktH"b^S$dK_\x| g>:j<қvuG|?7 0|'M½7]n5 !,"亣aP 7̳!B) Dpϕ5^<5xcN[bIsz9ÛC&|'V3<=UZJ&(PyRR/cڧ$[+зގ:m&l d^ !F 4%mHŰ 4ku62r{&v8ʺe^a2[_ L7s][)_a'7 %K _Iij+&-_n'sW,*o)_ZK?o搡q(6 jbu'<۫y`۴U  rCm1xvN^&ͫ!{'sKn=둛-VT&t_mliH]}IXSUJ}{"Auua/xV:R$lDo|bH Fzi RD{3 F͉.fgƀO r`J s^B 1! yyZ>pT1ڸiY0aSqE8sw7QJ .'c 89jIk&vi]wCSt9fu"Ag6e$[7v![2x̬f> ŏ.l7}Hw&mmɏlɧuLHfn!j2(HF]Vbi-T.yYKdyЎcȇ7&?k4˛ Ԅn2KOt7". +ZO)͋4"\d9h\m:IΉzcyR$[4&~釟wyJǵs^Vf,[OY^eUJVWmG6AmY$j c+TO+aK{vʙO޹D\#BAc& vTwj*J8BCy%7?X=1E`G܆UaZ10n ?jj'rm4[!yٍ >mg0섅7[Z8?+1'{^VenlK\;2VJ!h\Ym\t8>7A VPH˗|?vvCf8@c3U erD!A:HK/5{G#𸇜۲'4rP1:yoV=OL$<=6f% 77];bOԞ6]|(b%5٪xqnם1$MjkZ;L|Rnv$/ymǯ=zidyU f820©f0l}/=כ6=̏+&cfnh7]*8 Bo %٭4f(~ig@`:23@ Os2lZ)ue[%* ~ lDhVrܲOrqjS)>Sn%~f,s8Y񩵦*"]5 yevuK/oQ^6kaj4 I=9_ ג-5dudLH+a?*7Ee_iK oQ{nAsK; bB>!2B>3ܖlE3W曾I CdKa%zd/!Qn.sKnopS93HF" m gkju[ZZnǣϨ )ɁmbGpc F C- I !ݙ $C j$06.nX`Hh!1aVIv3\b,f3,u2y2$]*$t&D 1МIt7_M8Znls;? pvڠOP9Y <;&u׎Y`:&zWճY`ƾ@h9I5Rm-~ݕ71?0[r- ;O?~KS\U[h}xyR;mM5uS|9N CX rK2_^o?cٚ%J 1k2HJPFp/" ʯQfv0{M^ž 6pȩ+Yyi~Z2CԂY[Z-u-`2R1μrjiCxe9Qb(Mٺ8/%|}'Ys4.y8k&L0('a;B#&0ݏs`(Ǣ;60 CuhwL JEjeorTUZLXQT[".@yC%n XAEȓm뺰w1g@H vcYagRA0א#lmW`-o9]pFcH>VS=s^fQ\IˉЄyv=ys2 ._}j] qCz/^;WI]~ESajDv9r1:I 9⩘|Tpx5Gz<'i{q:yX vijԎZ$l|.!f6p Z9엸mU7d rq= 2ўl<<\6j"g $g~.y kahGK,A6B^/y <~dHjLR.MG 49M3l!D cWéTtp6C4{"(0oT[$ag09?yOwKtHl+*LbYu[>zl}VkJn"L:|:oۖj,ֳ6FLl2[Ʈ{a+8+aj+|AMD|$]J(^;rx͟Q%|{5?0TLc5ͨ.ihn]ES!vۈ,C̆ch ݬ#G%J($'`Lo MQ&4s| /U 3/v?^-EWtnBxYde6%9| 2||,9ޞaH./ZCΑ(Z-fMnYY!lbyen3; OV/+*椱UK#k7+PgkC+WDwKQ-P Mdia6;~"O{jJL=GE[F3(O}}3ưjV).INJ,V},`KF,k1פy|I%fy53^8?~ȓ;X5=:e]1Ӛ]hh;"MaV)|^XCbՖ!2p^UENamȻ'idSy+tѮ3ocC]lCYoU j _V+[* sN 'I8t,_f\? t $3j`S<[g p+Ua_sRI$Ŋ6:kdg^eCv%3lZ99"Oes9YOdL;k vv ikU{J.3hz*3*9NkKVP=!`T9%D9|8!s gOܾxF|y5+ebFˁj8LR%A] 3ZE$Nœ'Zw=Eʑ-I-O_D<L`@|&& Wll\' jVI> BmhɏO$?C'A?aN5-%mtBc\,UZ?t ;4MmQhq-ovdk8Zk&,,{5[pb/].Lu ><+Ԯ7_Eېu }qˏ龗&mHwq♌r|K;)UCe-+rEd:$i!jtd p9`4j. lhtD8IaV}*mS `Kc5|nꊫK8a5KLKɺBaOY?KݔgZ>䗑|~ WZ{oc{3D?%[V2D^2LdEsYvM&H [d^ "C\} 4kXg΅-sqcJ񼡺&w-3f& x3ۈ t<M$9`!4tHp&Է񺱗t/d  Q;Wo:`^ݺ2{01iꂩ-mٮ],%*^av ~؅,R}4c3vL菴؃A$)Sk x 8\JS_ i:Tٲtnh9d]xkWʁ,k%TOڷ$ݸ҉P8Lܧ,Y CknrTNɯ;cd;t檑կ'ym rGKˊҲ,6: OK~[bg$Ƅ0nb-9-6 /2as][nϏir5Pۋu?E鱿}DRPһ=V6еr2~)TZ݌+;x9$Zt 36tHDdx\uϖt{D9Vmgky&D8-$m@ h >iI 0 6qkbbD|3۳E5Z+=/o7JͳHgo;FJ:K7Kqa~$ m[2ɭwZVs0’W␕^&I /ɭT#-dz`c廃c{__ٗ-gd׿~ˏ7yQș燻M^%FLiDZ)Uk<Ԛ&5JAxO~Ő/''!Z&뇳Q¸ILpJyM#=J9]onHڷ]kkG%3W ջ^}5!5(Xk3#&d|P `),|{5#})HEܲQw;Lb?iք PdU nbw:ohÃm%xSxuT.sT.z˃DxuKLy`ǻ+G/cR?JО.2y۸7ͣ)#m셼)˘9!ENT](HesyV.~1,~g0N}e;jw.Fa4 cܾ;fN"SN ,5 93c6/~W6=}EMx_}0sx'L~6n]}߿8ϔY.Ϳv]edcn[؍wǏah3A&1_GNǢw8ZQBǗGwj\on"s&+"q]kX߳?3eFD\avGٻ 6uuYwޗ%G+~NrC.ښd0FսW Dhb^R$3_qI#QB] 40!R eghBK|ߏa*nJXcn 9ᖒ0"B-FAEhN!xmL7 S0-< $vnlIb<0#¤}+ #bH0<,X#I۠$hRAd$亓%3 :`pfM&Yc5.ZO{Ӫx̜z'1y0s`c 3YEj!)0#,v]K~=Ai})A1Kg9*LVX[E_Zq|㵉ȁӤ٧ܢ6o/0s<=|z$.Nw`97&#:A 7×!;r㯣wܙl ]UE14Z|ק^f<9 i3Dߖ|(64^899mL ZT)=K`xCy^͓N4qߟK\Iq3ʤ-z?#̆phQF[Ցt7?[o̷眓h-QqBRS 8?jÍ@U1޼|H-ГAj#cd䇡%AxΥ%Ϲ`"!vqҢ>ve8A:CkuSLKSDM5hT50kr$Kx}]̚W,y IC} z_Hj)˖Ɨ3T5J+9!Ϙ5kyk:I:bS7-ii5f-AݻD"P O ȵVw4c v<:F 0/f_K2.R`f `$y0iCh$9qEfQ0$$1QK8[些U0Ih-݉-f1Bl^:,b~uaj G\O`A߄KdL5du۩U=ef{!0xB- = <9 ZBN0LXn9D.'qW rӝ+%"rs}pKh 3`#(ow?>>?==>kw{1yu0[B9XBa?mwy7$TF , 79o] Hi,J{H6)E$J-rahCah9`ӽn`:r.Mmb2 %T6%ͼcKdN"Q %F[WrjsIFQiW3?NR-dIjhW~fb!}oͿOti}RF$<'p.$LQ Yd9H  ]Ak_cܣSi[xx#]*`Dx 2B|AətD}N>y˳}6-ƪ[plh iyimh!Q!\oZ˱>Bu2@=ًl6 xgk$߿fД׏R׽Hdp,Xߕ}SLnsP>6\9H{F-7ldB%ْ- l)<3&ޑcϹXbQSQ>߿/7H$vfɽ.bHlƴ {y{^[z O S߻05nW=.a3d&Y&?N;d4RØm¼~3c]e-N*)Т#V3Y7t? dASyqhyĥi"k\-T|zU?ĆOן~isp T|0js"I“1ѢueurXwyLKm) 9 SnTy%Y4cdr "'9fjj&RV-`kg&Ǽ` |ɗ_\YZ Mʫ. L\&tt̜0FxQ`㮭 ? N%Xc jTOjBnDdF,BZ-0&NkN.]10$u 9 p$ 7Ү 6C "Yq"xH?Okμ$A#0Cg0\iu6[).4 go HC)EX%wfy9X`yyLUpyp!t[ښ.A>VC]h?d$a.?-Mzm) 0i`S՘kIݲv/+$iC.+Ą`|N|TKuZHB< W]3C 0I݄,%䦠Kkaa^~|?O/[CoytX|&YX2TH,|܊3u@;W- s* zG$3ľKpW3vG;-b8W$TeHf"tkb(l18]ofXΩ%ZkAZ5|(ZXw@] d#BnX=Jj^5 к'F'dUdI#{{rtKEuO@ ˉH3)9Y0 ;-%3B yyILt|~2.3g}zWl0L==n7{O_>? -#7ڎclat_㨗Sp< 4{4 [-9C5jN}&ȭXxv]0M9Ts& <9"*7X 9|Χ|jέ.Րhr ;Fw3=,b4y4e2ӝ-g('Thkv`RFpqIQiy򬻠 BuMІ3h/+]aEG87-0% JZsrC#ծgrlNB%B-xXHt|ԀWyRtW^<筨_.q<myόc-Mlb}|{{t?rE҅~}{u~̯8}xzz$vэ9-ܟ s(&*4l;XÔ>.LVn+;U١sM~6QnaNtᇙ 9FXt# cNk>ãa_;Ϻ[+i jUu`)ꊻU1 `'G/1`. tŸI-Hw|}\В?_"f933:'躇9-f{!`2d- VƘc;sD> ~y#}I03?HYbH><]CYKptG, O!3ym/֬tO3KҸ-cH9vaʹo>9ˣOFQ{p8U( #b>bŢG2U C7:ޤڷerrYݮi[k /}B_ksxd* l,-XvWauɬ~h*@u|a gݾ˸+Iz 'wզJ_&A 3kՍva`(W|ZTl1Y!JxD0ӝě dn0g~ܹ$_- J̔G)]Qm]x?5'əH ZH׳y?ڌtC-v!.:LNu; gb dzݼ10L&\޺D{Eg$W]dhҘpQKYQ{v`':I[`rv<}_Lp#H1UXp+DR9W>.wa 5#7 9[PS ?[LhT$BぼY*AN |<03vxZ'DC1Gy8&cTjrS,NM|䄺>[Zr>0WhL>!ZThy|H B fG: HlYU%$U-ui;C&纋GѺ0hIْ|#/F?|ݧϏ>xFl_?r/tFI+zF/xൿN/ȓ .bl֟-j>_ w9pŹZK6I>FJm'uE0u3/v*5ᘩfdV$]/uv9GLБhG˜+٢\$+wY^TP2\c3zE;pKsj_y l\ty9 f*BX}Ž{2?S+~q}N\6I{vvyݳHnwU\L6^Я|7 2~N-rD^(y9WU%ybͼg,sd>p[׸J\@(<}% d#W9zxϏ.茮b) mBplK؏42d,)a[@6,]Bd52_0 V4VJu^i?eM+NH&L?R8ϙLIwlU% gLQc6 $`B1r,ݻWORpk欰Ӵ O6AW~-ZdRF0Qfx%&ksJKK@H26ڙHd?lI`s+oe@8Rb'z%Nrv ^vَˆsﶩDO>'u}N/WL]g#|eI.2׻kQ:`#v20ɌGX^Xn`Cj4m h Au㳗 Y>h梐 g m?_!sp!!2Bi=h1ک&\kD2ugjg{?S^;[Z!BlDǠz0kK.UrB'FӀAh à含9&. yR-,Z SntÇ]+N\]K?bh`ڥ=QMpќӒKiВO58d>nZbF D5-<`дTh ѴS1CKc֮ H>ZCYzk>!ѬвE$x4=~۟o'wO=㾷~N2t'FYM{hV 0s8Il$< u,!~ ٶH9 .fQ+)@?$6_< ATi6 Zy-*-7#&=6LYM|:,b\T%[ma΢yt0$ҚkEgfzX dd1(.2eS~@A '$YK:<]/~ZyNKdR4Hm3Lڿ͐I[R*I+\xMO+K:vj<5{DͻLa< 7ZuButq#J߃y*OEyi;9 ݳY- MxWJO,郙?CbxLc"+ *YZ^VXi۷ɬfNzTHbw>F LH|ӅbXXj=~»(=OSȜpglDvF[9G|1-L0c*Zt<Fb$͜+Y }z᷿{z(-յǘn-@dٞ)2w>,<} kޏ|鳫8[bsT $-ǯ[+Nv]5h-|TtH Ȭcvڬ/<[hT鮥Luj0h'ܝT&?iI2YaV9h&rFMC-;u1`'p G˖0cQG.$֞-7DGF%>sNHfEba;-\enNhZAb|b@ayeIV F0L/'AȾҒ0'1'|s>x1^xh|2I'p[|-Ԭ;ɮ'ëb ckdh -v~N+%i]˗N!Թ$7xSi\4pKę9] ~TFzq~[1g<,p8!BLrTg44u$wsiNԪm ƕ?YԾqw\] #ZNdf3mWE!g൑):kpCVM4b+F*bcrm/ӜD\v䫁∲X@9mA)r0TZV6k!<1Qib?=><==>I*1-4;#K9#E`B$W*m#$Cs2-Dͷx3 m=C69綩1h˗/T˓p_ǐv~[y(rgG? QVciVY`ɾ' i>-)nSxZ9!d^Y$Rk환 sޖ/h3YecAk'G sq[,!1749jf]x-І%s\1G27.NH0 _x?{՜/a6vi'@-DEXWGזCdȿnj~$,|&'AHB9vfdƙ{myGVSOyDb->YrG0o!x+^ztmv{\KOR\] &DrЬmF!0bຄMS1o.?eXp>ZAL y`Gavd I[Lպ+9o(zsr  \AjVэTY{ȵ56? !+nRᲪůihsWGƬDigL#R LKr*yEu99Cۚ +Bݩf2(~hg>9f4,0mhr`r]G5>ӿ~r%kUR Yx@.xD|E}R݌G^+">M(z|e0W3O8y6=5LNIXY%(ERü|vk^+3}=";7 1bIIINu1d̾| OPw4MRrq Wٿ XNH;(gS{Yr|)pC*5˄O0k! wxFVNeǷ+'{U--m /l~#'}- ?Y \0ATK2#!u&O%{DHzHP%OM,~zUj dvxx)pصK[Kme\r/e4NOWbOwnWڂI+UJzYl$6 kM2lbsAi9m&AK!f3v{brT{={ܞP 9Y9Zt7jCċBM{ dt17Bahߍb.bB4r9Oʸt 'dB𘸾1M-ëd sV99;Mh8٥m hVX`c?5|~9kBoǜL.WxxG͒2Gg,9`39lZJ1!rvݢ$=];9U,=8e;5[0:&V9fӡ%1p!pku-6m?0*lOW\~d(6I30_YaC{,Jh؂i7R]nn%w?)6[K& daX0¬= NdNI7~DOH& 9-d5&1Tɢlхl 9h-!-"d>I"۷n\{ѳ2Kcud [aX?@4K%œU­b-4؄9$R-^[ߡa2a89Ycb`_!PVaT7 :%Y`$pUj=! c&3V@&7-"^c@3x0HG/adI:Rm6kcH.ƷИ9Bj. r&9]/&1ƿ}fl _qO)'qee\,;R`XJKnɗI͐Mc"uaP0[ץ0 9-@odoAE{BO;дY 7^#Xe]W|"U\F&׽MM21莆7D$KSmoU<ޘ ߫dC 3$ct6 OF;O88NE] \#dB3pnrsӑ+&#Z&j9!ܼ&3$妕jlv`)eo~n H)oًJ>zn.1IDM ƄH7ɔ0IG\dT&qr~@| 39bJ{*u,cgn(HA`aPHZk OU+ z?%(IIWW̫$ciL^{F 4oZDN,XHW;R׾Wb%0fH<фj'´9rp>ǠN9{n-'ɄxG 1$弮v> myC>خ 05\ryL~VqOvUD@6eVQÄ<|lf_3>Ln)wUpȥڪB*L."yXchI <>ʉռ sҨ%?-9]f#-X1wh'J๒)#iE=e;1)\GrʧZ6y}+%rxx[r0h +0\ 0ǐ2ђ|E{-Za֝%# 4FH5~d;030#cLXO,d,:!ɬf8fځ嶑j'`80F{ ߸,Jnp`lL402F yv.Eb] gr9SH,a"L*lCIl&#>YusUFq%I[6P ^e?'{~JM2o>K\9`EN=)0Ju H` 9-fhΏdՖyGd*B3W'_W?OqaT9~i*!GSi%Bt  ?Zm;:C3+4 F{ڹ|]huѢI;,ZU O\VU9M;A!Ԟ$1wm }𳍮Ғ랸s\7"%Oy,~qZQݰ.;8v8k]cZ$݂v68`D~R:! {Vwx#xYa\m)gYB#ײMkaԨA7NjDoy92s]%FwJMVV)) un.xyfs_7 2̳ca{2I&~sZVOxh gӢnÍBp6~Zq%Tg*LQ < C0ZĴ=OHjI}''\VYDtLgvD2Gir6Lmow`ZB`Tk!D~0K 65'!g~L0X6!$"g{ԕI&,+2!fq+n 84Dsg9L~v9 !ڼG+xQռMzN`JֆҿA 2rDۯ~z{cnޗ;'z"lFͣȵ"B`BV%ZĊ_ua0hB# lᵺ3|= Z ;O寜I^|HZ1ﴗ՘{%bn)uvfGĐ|<~K L;u4R}T8 9,:0$xplbztF5xk-+ L=:x |iO>x&Z1?JfمnS <רIdZsM•V.zH'Fv>I!]*̜hdx $aFS% sg ghU(jBa΂ٲdɺ'5'ٱ樶xJQ!!i9$NKZH ţN1LJLP /%6W}J*ǧ}C}чɑc&AuGN54!-غ+]}b+&߾&hm}p&".Z3-62Z@- &SN1YR ` s͡Ƕv `tMdgS?~k9jf#`1GQ t9h7eJ;C}N#O-.!]օg-F:.#Izf ҕ 0~G!OhڵDB@u2-+Sã'^JZQs9A*6!yfCrTv&ޫe$_ 3T{*n9cŲpR 99%&>":4“ot0̩/aAfY۩6dS 4{z{$UHr}zGLG{O rާVnko}4{o)Ƃ*&nOuܲTd N(5ϳG>?~z\7\8Gr文k8Rβ[h-*tR1-ZjJ;IdBi`:xD{Tk'A,2vO5I3n cg$x&1ۯ/?0{K"ݍqZZT>+J &~үKHHά1D;f1e^I ?~02R?CBw2-qrLh d4$|JF>hjNW,4\;%Pb4}V1 VM0YV.ئ1$rmV ӓ6C?w\&} !1x&ozkOZd;Q \yHIy}MbxGspQR*BI3f0F{x4qkx y&qt64 y|2K4lfv{:jM>Bf9 _W;-Z!2dU^6t 󏔝vNIR~1`Tcc _sce_Z 05 \F=18 P51滕o3=rh?,gfxydջ5mNYInR1-?He sNRRhׯ-Ғk\ĦLj|uTxm"~]0C!c%;y0jD˕2"YIW)8<&vZ; %09M;ۙkZ!qg%Bubvj.*9WGu"_qX@c*$fn{F&D` k4D 9ȧ:K59؁0!Y|ړF]iv1Z#{x1c 77"|/GRFW;L&YM8$y,D2hvVJSodK~g'c9Yŝ1 ]8m8В@V$r5Umrɮ_OI#'`>K |z5T:JN)*WM2{S±w+:w1 @R-]iC>ʤ{eK)1>qU(T6n3UɆH0S}_Rt(D/IH^Ճa9WnEiY k=GP8A܆\QM"݀0$.C 2m qM/_'k];_P<{>$Yt-Ձ 3%9 CnaK~_ْ!~["ʝ~PxU?QLrl1R1$f=D'H:HZ؛<07U%19] x &ʒ톿EGaN]˒$)>f?zCᄏzdI6,r 1κZ,! 􃙫d֥y傮11ltBO33$PfH1j[Vk:{ x]DY~up(ܟ2yZsHd̶ŏ7˛` V OBC;<d>7U+3REaU!1LBMح.`8.vKnu3ϘJw?ѕq'_P).bRHoTӀ # +Q$SwCͼח.Մ&ņ췥*&n6\ +roj*KPoΟȳQ?qX=zR/y̟Ep)a2W QV嶐E#j*2" ]S͜v CV(m~;3w0C2.d333RHuiARcDJ{Ib E֩Uy1 } bn\i{{φ<=_;eweeIRvZ~!Z#2ro/_m>}*:iTv%l |78xvw}*oֽD|J{wYFs#0s[Oz,|z=|Ga8ғ@޹ C xI>PBiq,mSO&J2n`Φ>Dp088OvCK5cڭ`(OOO9̛)9-:t:/͋ې#ikl@E/:q)Lf*fZ3%)BJ:tk{Ω dۡ&yϭ$ߙֺ_R/Aqw8aY $S'Ɇc0mһr&bTǖ?RKbx/$tQB=%%sd M^}~~gp].ˇ G6{ryI^Zf|Lw#Mr=yc-kI&yÜehߟplL䯯/2ΟSq 'ydS~J~F/^^vO>?ڥKy Hg/ؙmFnD]gHy@c`m"c9k7d-9ׁ ưXL̺0#|KvWfΊRlĬ$d&`D!`y"+D5؉Ym q mIj@Q(O珸5GE ~ p_ۍ7UFꖥElP/(FV8* ͵. >'gtN:µdlV0ԼvNpIڳ&'\=+ݔt;Lv7sKVdݶЪAäy(ϥt\q*>*YJMx]'AeYH]*TL&D2H%4{f`䏞D2IϘ\sp>5NCZ2oy_ =3gilC:_jg$~Xg]BN rFBK0޴:gX&8[Ȋ;t1Dĉ#վ7&k-J>9KH .0R B~003%矪P_w޴ïb@'[]-ô/KER!Iht7p Loj2PWn.Za! . ~bUr 6Ux irْ1$CbtE64#M~"v[kQ30}2??8dpp8D(mi8OhYrGqHr{o)>40gȕm8ô=Kj%SY.eBa r[ʹ3|B<.Ia-:N L8eE.7-ɴxusl)0 kD5rJ]MNcH"Q 7UGNЁI(w, yn,k V(Fk'~UO.~C"u z2|3 $ձ -*#Iߐ~~$d29uRC#<;VKE-mc:k2ђV-W\"|JeI%g9JyU"A2iKO à]\3ђmw<~:FY|¥K$1Q7d $/ 3۵9!.}{IqP]ݍL6EunsGK>O<>/6}1BpO3R~gJ'(=WQ.l48ҢRK|C(Dpe+-aI`ѝ xR-.[ 9f#^|a8202`f8L|*ׄ0:5lf1,q;Xc֫vQYvO!Ω9lфˍb $]u`d>mBЙ L˜;WkO B !P *~$ᇁm00T`'-՜dBo TIX,I<`k74{$ u `-HՎ̙L}̞~|o?{t劎`s$M1&̋\n%lwh ?<&abAMa($u?#ȁ6o$s.t`Si x$BjV%adҝ*^n~O> , "ϝ'b@}=1oyaθr2rOR\!g&,, [4ʲLٚQ/ `⏄rϭZF=]V\!K `dN;.$K쵟a!ckI>"LcXPKngp~fE>rݥpQ[0-&I\$D_}0]O>%FI^u)Gp]Qa 9fHo` t#"[<ڼVwUOLyV!`\2[5j BZw'D [-o- W"l7ɲ=>(&d My>OIH`ygGt9dõ" 0C_<ݟ?FKf;x*0x1M1,BO/_WhL“vn7fI1@@iID9>u9ѝ7" -~@6RjhψXPպH:7&axAJG\ۓ.à.@6 ϴEH ڪ]s{b!I 3K$[p 67d}`~[ˁ|jEօ-}s. TW{QQr{̉.Z}懖d!}?># 0gỈዓƺ|? n//Yؘ-QDUarх+KYf.P!$w Yg2w8KEcvl<dG͛pCO8 lxSoBI}yZ>1_TWBkWRyB:EZ޿6@ p&ﯫ%a IwQ0'{F &H$5ngR!HJ 29^qHXLjGue3~ $;Khvl gDK h ^lw'٧%$c`k `0$hgK1'\a2<02S;x y~&d.f fZo &m3PL(=D!!Unn Μdf +9@̗'Kre;G2d2:j-&$2 x%$sa=<`R!ATsK^˜xYW&}O˃S@;e 0KjeoCE6d!e/v )T?6ӿNo7חkt skYdtF~ۮίX$13CjsFۈ(-cĬeEe:CN(Y3?o]FHѲn#`@:\e걁EY6l՞\-AERY@C^B'H.-]8.,k(5o%q#F(g a 7Քbdž-VxY <(QEa)Z8S[JD^Xm]B9$U7^FbS65X)gƜEW lP»oΑ썎 'aNhf^~kMq<c:0*B@haϫ茁IS-QNH vT# O?y/{- UsX.%E*Q,˲}t e ˹x^:C!Q%WHBu ݱ:g{2+:UkR:<2} )栓X f_i'xa(S,Kh&$M=kxTg֍AT`1RRwtfX}KH53j0JEbrYt n` bN=Lf 6ul,p&62r|r3$z$%*qch+)WI$-v;b]9*Nn3g|UWMmFxƘ.]NTR.NZk8}dvEmrX"NĶ@8;'=/nJ"y@<rco\mX[U~FX.] ̒#|g-I"l6@R$/.хx ~\l0[U`HWԩ|0ti  WD,${:#qB.B7)BJXm0ZErgZHWgFzy!)0Œ\dCNV7W`sFąhY/jE^j(jy3gWfU@ۜ|I9*R0t-i9LE2^ew_H6?u)-uN>r<ڿU6+ 080 e%)85Q2 zq&:KǗ_ S9*QO u $yoBXvB1|:e{+Fa&CZؼP6z Sa7Z3#lWR[kp\qH+ 8$6'ȱiy{/ox`a$R8}Gz`0vV%e:5jAp#e/*|#KQ9%UB:Mi (1jb1ls^EHy,jɨ0C"VX-/֬f9y'뒰Pk*zXaaJ薚%4-d`̤n$%bJ΢x"9WK/k5JIq"l-3R:e"M~-pẕƲעTL&}-N+;}%eÕfaa"P] q1zxwǾggLO??gN~~Ӕ4(G ʷ?K65(4X) ACyu ]y"R uf JtxbY^DqipNMk %=BJrc''νsy#Y?)!Vz[ mRn*F_f+(Q2fyʢkc kRƦ. 6+1q5"pl^#]9p  z\2B iNg22ڄ 3ҦW%ޕ.>H(걫ef 0E1с b[$FcJ~2FgqR[S*puKb Ů\ȵn*BD$+نƥ >;H8)aHmH"|tj:Q6׷x5gNƥzMECi; V a)fCl7/}>0݁ڥFEBRɑ_W@/}|3?vPEjiudbkaw;#=*3U>73MD ^ΙOIܺ1ZF%6Eh,N`.fd-㲱ܸ\']yO/T K ^QJ],𺖛eWv$ # ;%,cӾd,=L@ 'A%&3oY`q<2HȖQ=3'0SK+\k`v^RHDR`f9: Wx $-Q #iwM@I[B26jb7N Sp!%X!u5$0 XWStʻ_NDc"8ѳf/\RۦT|OXv^=>U19v84~//|/*Wٓ 6!$:ӮW9SVp; @H̐ y}lj),G3uwɒ^WI,갴SZ06ݼFt|Ex!M* -pS{r93Vd5RU|يR\aWמx?77#2+l_eo,ŖQ˨%W'-ۏ;%׏Mn3b ƥ{iIu\/]30i9⪝]D7Xd,OP".P a(BW]`(J&t\s7T¢ڱap^R*%ڋʒxwόC v8y4yg[ى^g}"$wz^PX̞@nw:]x0[>`,-A,t~`1ˢ.,ZQFt  #aʒK7i'n}̔8߃NZARl=UFB{J8sC,]^o/B}7{afQ~33cqgX8d.ꙿ9Wn'w}p; S"LKYx3'$<'jRGf` 4UR;By7w5;g3#[ qPeT誊2K 6;K #>p !了nVXyytNq+s/;-*kgl=_?<=[%0-)UɟNuO.xćq6[,;(f<ͧo]={i ~H3G #6W7PVϹšV v} pnOm3?Lŭ d_ʂϴdov }lxvBX氏}hBcnI<6xw6xp22_S[uKÅnl&#wJ!eg %( BBR:;m'*{P5}0!m5C־g[tC}7SR`y$ɻoK,6gMig̤G`d;9eQY|xΛö3y"k'{S (lri] ʕ(%]`%ru DE9T`T\E@%x NM>_h^7dE!۩>J `d1,x@ٵ!v@ ]NJJGL#M8wC^βy#Ē)Ȗ*h-Hxx~z/+JΏ,qseӝn+S@>+K":?Pi9 .lQrx? aк\pKثUlEGo0W 5v9s_+(cݯOHN_^f>8IqEt;JjVo*(jsWHy[Y4NSX@ 2(]xй*2K̳Du҅z~y2 =!3,je&ud׍ <-an%ڳ mȞE ٯ\R'/aWEy,UKBt,i^I`2TH,TڙIϹgu;LqKn!h9{aU;=#By'{s@"\`Vc /`2'7V5{&@HʸSL#y`ꔽϦU/k)øwYa֕_Kѭ FJ](c)e \EOI EI]wd&zPnHo{e5C$'"ϩcHz1o6W ^+Q}~4ڐϻ@70ywF6F Z9f?iz\Bkn& \Uܺ`hY?~x71nBQ?v[Q0Urn0]-a$*`'7;˹Gw_ َ?`dѕصB @twlăEDk^/y:'Ѿ}؛녌yi%jK;yصC|Sv1{`>n p) @sղ[Bv 4؄Q+pgƗgV({Kqm) `qMکudOO<t{}(0p4hb P}$ rLa̻;\6>"Z.eY-jh*l0r-}siJb^0(qC(~W8[a<;^&H>v^_Xbt4 HmO@:6hҭZY⡳jU9E{*',gKx^-/7] FصaNW$YT[zNU|_̋o9?퓃+n-3&3sca(Ltb&}%ٲEtc# dfGe'p2; ;bc,G㋇yA<%,lދ[WR^sO etLLUO8UO#_J6+˒v1w,mn7 Ï.6zgTT8vos=aۼZlmtZŒPrեϖOA)' 6{I)x RMC`�}~"?WtB!f$+mM-27+ҤU[^ЉqK5g}WlD+o΂)WZ']uYyَU7.;3;`^tj%bُK3,.~j2.)Dl HKJZyqP>̻ЊgPk9GUK>&:)ӻ@P>Kx_"TXI؅B)<2ƹz70Ij]At9r,u Z^\A/$;]oNMn=2HqIӳ>z! 2݀e4 Egc 皓k*LK~;Q,Oӿ[ e'@:s6@+ʂhl-*3Wvy+c!a=pSk$ntC]UPBBꐺXZg3QOK֭ZIPB2Vጄ+v5jIlq=ܼMsy~Rjgſ7YF]Rҳyuݼc$;)Z~ oaY(ّEEv]UN$]K1H F?Q%ڙ&I7QZ# p^`ݵw&-))Ks$cfC 3{B0S2*@Vpqg9a!Y6ŌC}{/]k_p]yT2pYVo/>ᗗ_>z0Q3yNGNjg~/gR\f&!w%)c%/d-96vzĕ%#kJUhn;D%ȍ_#o_4ca&:x Y['5kڇԓ|O0#޴svޥ6\Xx3F{f@,xqu'`? e!ͅ[q9͟ZlSOSlNcyyÆ9')Zm][=U临G J6OQ?޾ϯ236R Np H)6)5Rsa"dGA0g4-tl9.ݫ~JBJ]qR )u nd,A_|iZB*x!+^mK ZK^eyyS^Y @Ry4H r*j릗1̔õ{sq@h3n9Y`"760je֪V.$   \+5#Zm3g!Q 7(]2;X9gz; `+1xc(i3p8#zlv<_7 h aǮqOY ~}W5vwD}IIɻ'9[ ^S n߯rk(['hރF2|_RvYIo0k%VIq<YxgLt {,t%`u 'cQNQGMR- a*ga2B86R{l"")r-Ձ屟aҳ p N9+%<7{W,Mf]VXh!@L-],b]GNl0j3IvxJ'\,{v}r@vx.%B!8C2c)cb?2}+R\e. C)gﳛ6jEx'rgK"'׊0iIc6S9V90_O9<}ָ3LuQȭG:0k׍HڄbwML#uIt'kO֭mar`$Ek{R6:=ͬ\缭!Ӓ'83?7Q ǎcgRϕn&j<e{ȬrD1Mt%oroIcJ=f =jfw8ȔG0GA8쐲#%ĠƊ!ȕK0 :/H1r"C`p<aff7|ZJZksYWƞ|ѡ$i@#7;K2C#йи 2/6)fԥL[fi6pFRpLeV:?5QZ.\:isoasAɮ(2){ Kr:)38$ %0BriG)\y-KFmm<V#bA~gO`Kg}u | `HLyc Iz6 #*e9-ĩ[i檨||}G]q'XW{ۤ-YpM眴5%=8,F8ps1o'c&B{]3a!yEu~O:2VV(EĿ|gDnҬ2N `rOü̕,x\!Mw 6q BђQ}s'Ox?|gԙO'߱\O]-2 Q2"r^&w Ws PmfD; %eItO$6U%,T%038uGk'ǁ #wʅi߾ۇcuOO/ 9d`vbLL˫mY))| %\do͹kn1k'[i?9.缾Ldΐiw{`s yxay5`ƶ&zFc@eewˌӓ?-h{y?4̕ Jv'35g\tJ[q˘y׿'^?: 'f깆7]%,:*b餐+`YHgU$jUma5Y3@!αntӦuUM;XR^Sl(VI1lgb!LKD_R-$:BclhXt 0V^mrhGU8% )QuKؑ"1ae%QX-oJ?a6dRKNٝ0Z@ŞE95#b+`a 1݅k8<*d3f0UI:rW(\@"gi fl= Adiug$k=Z5ӭ9mUhǸ㥟7K#AVlNa&AkycTn "b]ׅc"x3[ly0 \f>|yÏyDmÏmF0ףjvڎ 6@K)9r/w2s,+vΓ{}{5D`^Jl,˦{f#Ey+u=ݩEp W=fL̾Z caV^aU% p~7b{ ydi{YI $Gd)ȉ1N+j0ҌDM j,;$2:)i!UයSl_x/Ӈ_ޘ6m.yѩ@4ɧ;1'j^K+.B0PfdTuʗQ,J[̕=f٥$IJ|Sz~6SH njPJ m_&?X;eT&>_6kE%R^57%1@A1lĔuGyK"<dLpuK3!/vtAN"R": A+tf_ m i$ ?B "R&^b8eg OK{9*Fg+´uk&!9%]}B on*'M K$s޺6)EVmM?t0rCiό,)QgdiK`*ܗ3F{]9~v9Jc7\;#Woa:FL:sAܻ\&\WQD6K%yrvgLsbrb|/>F{R %ZW3pRN)N&Ícމݗq32ӟGeoɾ< =}{ ((aW33CoNr}+͒AMռe 9>bꣴ߾]wGԁ( C0u9+n-x.-v(F, sH6FR`8@xlr~?.^庅k !,,T5W- XC3Bb#).>bFUR d&TQ0dO'%EN4;]y/$mXh3|` dƹ ES NkcbGb!aC߷ ݂!Qj ;dʖR ex~z2)} H7k0΍s_p V8kI벋 l_Rlf:ekcD=9N>d_;caR@ZR#߿<2935S9OɷR= N9EBT BxU2&=}xM)1\q"S+.+Ocўt\,#`}իnm<lץUe![e|/,oX&ԣKa%'Ğƫ 3ýW%XBYYQ~,VjioR|eXwri'c"\Gb`,&K8ǕjE(1`ukrj?;@7QBX$˴f^.y:qbQ$6#`Y͢-QDw8uR ]xڃaғV{H7d1חyk{0Pƿ9 ;_~]YW[fyTH˗%s{ W$Ǻw#)!a[[=(Rl!a皉]a6I2PFf?)vfC`Ǚr!tq/0 )SAoiƗ9bf#l<Ţ_pyz~`vS|s_1vu^wW}e2-_}pe#A&T c.Cwj#zLhLcnmz.M rMs(<ӿ>y_6+CFu_Zn0xvݼV~9ZMQ;7BfL)0T|,yYW˩1^aehԧ`.:EKc <2:oxmk")`aV6vC熘DL`h!ʨ, \Ko3G0!NؼøUPK'UKƍծZJ"ٽp o فuSk`j8u[ R3iB؃38Hy:gJ?Эl:E{-P<}!y0;9 |`Y `ɦDY#P dYbN9bPgrx i&W&GQ-8Qˇ/y}_?^`5`ۈj]T$GQ%)uIJ`JI9Ɣ}. $\^0<36,*y6}-%?lzkw l¥L, ] Pތ*hcil<頧On-)jUURF]j\B(;"d'u g^ H`{K\s#gŸ> RcIS.V^#aDgL>;QpB&S^wI;{bΣd? vfF-)E?7| !Qi׬2ϥ6f}gQVZJUc'p_.=;U%2yp"p+5 ~3^;u.>~wXmJq3ExYlZwgˠ24ۃq> sw覤+%Ի|oQl`O/]ϟH .TU5:珛y69&?뿃b(;N==&ŭ @ACWGCXM,\H0SH):|ebiԆaaT6cʩP.+.O_ic`|P$= #NLmBS#eYT{ S"$yuyEpk[FugT)i2@cE"r:K"9Vl%9N(m6 i"l /V "jх:N7ǞP"0r^xI5/J$Kz*!$0QlReĒ-EwV߄#u75k!uylY DtN oJ' Ȣtgi! !r52\[ WۆOJDtSb9%6Pt ~SࢻL HzaNZ] (uS0{,*z:{+5hӸ[Ր4Z>)y3 +27'Cl`()te 2gS&xGq6Ƅ`bQ-Y,)g3w62_b@.Y'K* 6ɓn.bMAUIp͢`FC7k$tzbKt]׹;-#0=%H&`}&L(v ۅˮ%F7vb&3@I?Vׯش( OSU`m,$nGUHQ$$B ${xF C{Ӷ%L%x}sUfo3mȪChV9dk|؈"= kfƬf>f&D/.;駪5BIyL?5$L+j{$]m(]eXG؀uqQ\Sb8]©!hw,uqy̸s oYۇ얏u͉ڔpV%˧x*MRBޢ),8lbhkd&ޒ˜BNjk *ӻœ"#,4!v$WFv]\,jv!bݳ=b|W{d>^/vϤMuHwK˭&QDйځmmW=K ތgcΦ~y KUDѹ Tx&t bR._Pyc$uǥk\T'Jt*ߢ@z:cBوkI_EHh3K4!p.:y /p^:; dxʫwYcFKoo>22U!;,$y^0)R:NcB]Jme["pC~y:#USʱ1gA@ S`,ܷ!MZHlpA2O7}b+tckeP*hi;jrլ&Qݢ`-D;[{VHWxv\x7’Q厝amNV«XJlH;ZQUHFQ)Y aԥ(1 8bsr5Zc,%{ K-/fEegy[MJ5%rR؇nѤlE( I`OQHv\kPY+/@.JdbnP_`Ӳ4jPI!F1(ԬM FZ\>R Ngr(΁T-cQH(n9. . tz\bj8SgFvC,\ggyt1Fu*ђ$wiNyL3SOo矟_wl6ߓ2_Ƿ`?0Bʱ'}J;r?aeX2$vNʆummF߫ eY+Vߏ%My'附\!9zeCHV HYy'YL 6򄘖BJխVm#Rx;%SƵ2eI2c!$ ~7݇N' *?],M?kw KƍR';Ы$]+6$={/+dy恔aS:Vyu ?\.^쁵jR"t&LΨ[O[0n30g'=]D ;r]R"F di)\B6yעm,%??;n *EJr%Mi #%;EZ]_GaZRW[Bw,~t3Âw̓cڸ2'Z:K&V1M.Jd>[Pt,r7ɉţ٠keڥ14 J/V隨}Mj-I N)15 C3^B) \ֈ9JT⟋H hHMO~>?}`6Gy͢}3d43)qWIUI5EIG`*S@i:pL}3p Mi;zG`; H6Z`ڮS׸K9eD,\uH•L;{ct0.Uᪿ,Cu(#Ԛ4 \B[2Mg0 3%K~ucH򦈝 X b+vf>w"X^:}x&"B`ƶZ1N)0@]R`J\wuF=utmt `[r8GGgJa}::Eۅ |)).!Șrh#ᅡJAk}3\ž8 Y}aNU|9蔙mK˵g,5_;{yyM1a9UKfyHu,[sF8&cWLr]< S~{ʴv2L{ޕ\5C!ivqߙ l^tarl9hnsn2αǻNh#e b^Cfö,qG-0{i7sd0:effɲ89t&s2jG=^ c5G7a/}!83=\85ekkXm@Y"yM9;f38U6ߒ;s*ܰzQCeچ j 1nl;C W&529v4`pb!LaS YS]7BƜ@Kx?Fml1I (mfϧo*{J3 r?<ȅRY4Wݺl-K<;\YG:K﮾+Y~?N.rYeJ^W5Uɍ15(!3 n{ȒbL>{滬͑'l7夓ll 2[pP&l{X3(ai4ϟiۗ/ #lfO3~]XY覝M''jffR0Ou!b1@Ttݪ wiNwk118evadP$e${v nF;؍cb8XHyϾ~zπ;م6MsyxݐYx]04QRrtQVҷ|]4٤\ Zu5bQn*[u8#;*ҫ\LxOTU M40KcZFǓsǟ?^?xkD_nMfha1k6c }  EZ)ml 칊 Ik3,l\a#ds 3N#k&;*Hż)prII-K$W%țe޽`D;ٵ' > ~1(nʀ蹁ۃi|㺔umW*ZWƮMw+773r8Z,msMa\pp- 5Fָs-rey)؉J؇s'٩vLQqˬν+jeE?]R2:BkD]=/m͞U+j/U5;OxGXT`u)Fs ХkX(hkIٵ//_ pp.bQ)&t5n;ݬNV%g O*}B#9vYH- } %`a?<;K LIij^_V5FͲjNԌv.r12a,r>}1A<_pl;HeDf%λ!G!M 1N Kku( pg F,P`b ,)xFX†\uF@J$D¢%0.e# N$|l,r^8Ht%i.RP3E]KЦdɋmm#,u+iaN;\T].ty">Fgþ]d+Bbq#r:OcMU 9I¨MxPJ.&mF/%[ơvR Q@,ӎH"zD7C}NjoDJ$B[6h\c,^"5ױdo$.rB#XzcT~|/~W}5"  x cg '.Х+D=$6}oyB;\8oyf;pl g&τ e3'le1h#ɖ:sFԪ2i%F 'I}B'S>=ƹkl=SMa~4ƫ^wIj;wvr;̌5 3y{&(>GVݲɶb%|RX(2&96(w]L7$`'0aNwKCFC1΍~>"2~~W!v}4 {|r&~b_kfg`fHͶc-Y.ʽ6rq3$7E~ 0!Zˊ筌^nDՖXvK$ګ89M&o-J]VS'0VXynX f}qW%O@[qްKl3NlN&jllu5̟>3tn懥ٳgt$#&Wf=M͊9wߖ"D13oס:7q.Q^#`+5̥^ U#RXvXn߽ݟcDif$*"WsUIgt(,Im'Qh"Zɸ 'ťhPRa-%Ma)Â,!Kʾ]#K}Pu/&jE?{8>6z.۷7[BWڡ)6YC,kIfT1ԅ$trt aU1ʛ]ł-/d6-Ap3 fO i"izlH6雴9T#ZTU ОP;dH k-4y?ocOkcNix΄;Eù 씾k'W[n> 2FcIt )P xS\iS5 lyh/$e8X`i O5ĹO=׺ʘ A ̎3~T?$ #Wo >҉Mq9)tIQ$B.LFxBgmYR§sגs߃/_hm—RIv 9;ȯ~_eT8O,zڠC( b+dd恔!-2u]Y )pʅхvfzKSun<L05~Fo3WZ)oC؍\,yY(vvN5{2#EFC\N1 P`yN<?y0TC^[50Etkc܏!@Qq=&c %³lUPU<\f)ۧ?<|%偅^n q iEmeprCEk ^hTH`~EujA_7ZI_m98jfwT<%d!,()Y['cyU1mwg6 +pIF"q:LTP9*~/"XX~l40 I7dF@ra^*ԽiK.P6oggk*pI3NU={[ivp+,s~ש}J}]G;0Ά(Dퟀ?3 E6 k`ut^_5d㍤p}+D.OU F̢HUg!,F桛=.F)YDu]F y1}_X)ZdhmlmCl[H5!S!ii)Z+nB8/`=Čs<6$$%۲= (py-ƾg9nJ,~ˋޏOUy@᝸)3{>W.KYROm; F+P:>=KtiBm s"fL"?|rK7HMIS[K" "v S(Ck's%0JAб*ef"6Ft3K3E.Rs;3 2hlQ# 씊Yc+xQ+욌(Z^IIe]؄+ ؂ڢyz}gpPjCG8"q7;sa61Ŷ>(#?Cf P}F9 %:! (ƉI=B LSY% JڄCQw, p.vݧOIFgou3cp:exWU{%pg/zlw8)RvmYyar+u2`ZB0P싟6UbNJ؜o<=3?<%}mm r*ieX]]^^gt5Ju^]Cb6~u'"mAq6d3{T住w,$&S@ݼT}pH F]v\MfX§ɱ m"w{Gᥐk\;QZwY\{h R:+xZ;S+u,Z#ioX/;HaP`# yKS|f`.#DžJ=e4p U.{YD5]%a@YL}fgQUTK0QI2D"٫ʛ>o)$ƹ lwtcԦU!L^^:1OÜ7/9$B('./ G'W]0vD=Z*>9v \ZC|8F]yq e3l!. 9̑*6@`JCkrgN[IbݳBuN[ش9 ˸طFq1ދq##_I!܈oe̫B``hB2.³hS0mIDѦ3%3]S}d,(d&ޓE}N$3l(yVߒ%2d"WZ 03^Bّ`w9.ָ kΟr`YĐpmJ<%/e/(s9y6&v _,2c2ܐU@QCHfYu<2<+[Z{RVS]Ƶi?Yl$e$՟HT73 Lgk(u`kz*D;#}?}R$/ΘOKTut\4ȩ^_0L̀ߺh9_مt.\{"E;3Sq`"Ty{hfoR$=^Rt|Ͷ˗_ϫI;G[jݝtUN։ .C epх  {57V =ldIE+|iF=XHBih0\F(6p= / Lb.\[= k],(:႑>9]BX`bngwW0zXg[cD)DE~1/|b?:??c3fōߤ52I\f<(Od%5Q҄調+ ]& kk.:Y!wMIAX',]Hs5cfy@(3N ˒NŵUρjwbHAE_RPCWJJLmf0ǵs(*}] J" Q cqV01) h8c?. /aV9BIO["c`(Vlo4Ƚ1S!o,͕S),Zѽu[{*d/Uו+aw/=6IdpOfڴsf. F(=CmsiL6> 65EB$>HlON3FZSmĖL8'߿6}8DyX7]/;UzlhOvmA%#Qo,yȔB6h&:/n!a2}DvpL։e9t#,]0Za>H-o$ l,v"ESUJ+/PM%;8ZG꘯u9QZ !{䰘[ sEO#WӳUntZSFČdc§8m0; #DPH.՜m!!V%^r E1ߧb)BjQ=Á28@G^9w$E ¥8uw\BRX{^: Kg6%b/6a8#5cfgڞv˂{*h8A_m%\1'Rё tDKhdP9 $LL=g;sLu` !ý 4dV疡:a*b{uߴVCؑF*t6@taoo3<gohlj 뷇 B٨j2JbgJ0u+q2zJHw`ؖ>)oē\8S?z԰6.Z^?=_qk*FoIVejg$!U[xm#)&l]`Q`dijnX1O>RZ,fHWTŨ/7!tQ~c7rZ|Fkse^07o4)FRytU-r6ctBY:!Z+K5g+S`CnҐsYgd]/3:s%6ذlt a|W&0+(Q!T#L}A#jqj2g7!*96JmP7rPJW:̖]c[AUFdfE?ytiOHJ`L5=ez_ǔ^yA#uKnm>\UeZ~݉Ys^knvױ_ȽjvB+Jx$MܺͪmԩHlh wtni rB<*2LJ{$B5hVd3|)ùb~ڛdL);s1j ˰̨:e&=nΫHNɀVj)D.$ذJ -C~Q[=-5QVwstG |{f3Oxagh,O=qĈdɮO^s%![ "#wa[x]vhc/Gw3!ɛ{!汉a,0(\:3OD.Z)Y0W$?(Ȥlb% =߿|/8js }ˢ#pFUWOzai.o۹#V1,qpY\ɫ%ItDSI䍅2%Q? E,;6=,yOC BGHX@zl03JYb&JҖ+)~yŘB(1DKB+׬u $@'\*NB6bLN5 I'v [Ֆu1L[!fO4@JMa9.-KK~~ (*]ϾyqR $RtCp,ӿGkrB_u^F |:{ kgdiݛx5 `C˼}p#z>Ƀӧ'k^`!B%u),}hELC΢MaRbh 8pu^~rs5Oy9kzm %B-K]2b9;}W6d]{wV UknH[iud3@JQOƽFzeLFѵ),"jb?g` / d)gI8{+ `Á] ^ș73#`xV4E8}1NyRf ~ 7ꄝPbHž1v|X<𱯬b&#Qs$bDKn!ol(YJ2ڔRNV6KYj:69,%J\"Lϥ=QpJ^$/ EI4f)ґSO-;K,SMQd"wcȮK\.;!9Hc<0JI Lmf"l u %c$ҧ8wȡٞ&Di rH˖gvF.Oa6n\yyjJ1/8/ wϾ#riuY*׷hEꎴr%۠P26G6!2VrNUާ΄ ;̲kw\&HKP,ݵƥ$je%o"XTE[򺎂UFZv!旝 XH[~_q!Y2ֲ4^y`u!`g$(.U$K;J͏XfΑf%4I@!MBK -~FO!ta)t(v_{A Czj)f^PsXc+%{Z~$0v{BOExh>PՂ%(& ?pQy?KNqt1[.aK "ED倉x --1;}8#!)],ʮǴc_y}y۷'}t},#%ɲ;R܅Ϲ%<=맟/_Bx-оr5 !EQdi*)")`emq2^vGvG.`Z[ lf%ẑSYHZR1ҟ<|n%ZZUHO/4~!~vD4R{L#$ DĦ`co-޺tm]cH9H8…9<] P..>лJMzJ lS-0/JA+nF;f-Efgw~}RQrƄ239LT!lYNnm.ӧ#΍ v ?PM 6BHvI YӅs%=o-3LhXm'ޱ.j p]^!}!TKF6(䢇l0W~iкU: >w]'mF"L il3n !c B*h\'<QZFCSt%*4L YR=JzJ.qzE)3a.6)^Dm] `"ddv^*1+}9cn ltYrM]]HD.0JVQx"mi*FLq8;ah(Pr2E Nb2=ZT h",P 0LօJfé s3E[35E,K;> :we[xUzuT3-q/FWF򗂱Ǟ aҹ-V/EΨeE^, U16X!/^mMc'*x={L Ԣ=˫+y릐|uUK\p@Ip7``^BgBv&}%5q+j\Ӈ//Mz{w>OZN%+b&ݼ"0†nϠX&~Y' *(.]hE傔7py $<(cfwOmQT,ɥH:.TάμȎw5fK1ǻT'=dݕTK%ȣ(Iu$BZb'JWI3 DBq> sKƄw=}tF{ fbH!$##G૙ T9/Lf}0;tjY{fOz)CxgɘA$Ό;ɡnYFJ׆8F_|8i&g"!l.B%%ʸ;"+oʑ\0!nOvN۞kk]ڔ,FTGZ6BFq~aM ߾}UzgCΒmu ЪF! SF%0̄/mf1c^lr\h["xwEy]o?/{U"gZr! տ dlmEKP*B!F,e$Sv]Ŭش2~Z,KQuŲK).+N ɨ3{\Q;- "@qe K?n;4.$Dp0'* *EmfLTd&gF#a%O-ZP*ОEF_mJ.zY_ޣw(v+2 W[D,VLk36 T&& L`!^c@rݿ4W,8p[~G+e泪Z1lASX |ʢbJyl)m6ڢ`lO~>}<4oC͕Iy7{t1nګF)(6COk/ ɾ0Njx{h"\W9.>?G)@|ɾ7 YǛ1ZҢȫ |S  ׈UΙk,DK Ĝ7pݎCj Ǽ,?T魭r `DLFڡGg9M#AKIW{bɢcIg7u)h%\MH$\ZF!)`Vcڨsc&`)E(uu T~\YToi^hnNxD`D`\eY}@آJyLl -;BFؕd[ֳڦ; !Iu|@QמYZ|vH,Qʱh.1vcYhh Dh sEf YL Bx](Q)@;>|_̌XJ]1>6R.IC[ &&?*@4Wtl䴻p;6',cS unNVwaެU$L|f))c)XO%4\_|Eξ?B :CY뽤DHg,k.oY) o-nĈuQ|qK{wNi&qCV"oΚ޵PZ+bUʱ\;Xjp_Ae(5RMuZ./xW/[p$u1PWF0{[R&ߊ@`zrbU P(D7KQ+0g>O9'b铏aǫ;(P `uPޓbrN3~Q6#5%\(Dv\{e>29tx]ѵ#bɷ;Fȵ?Ygak̯/[!/~y~i|-:睦‡#XScq. Fgg?j?p}YOk;`bg?`Qݏ^:X*Ia,캐@eƮ+U+c#,cyF[8<).); hqgza.SCR2{izR8kh℗QWY 1͡V,;E;YD-oƼGt k4r,Zv\ۤ}Yk q稈aG}RE iY%tJ]%`~{'ˁ(^t1$7`IMd9 K9 ɢK y !X1/?iS żNWjp=d ɨ%oqim gvVy*֑3r[›® 6ށSTe<Ҡ`Z?qLw?3y*\F\8wg%-O.~M93)~նD7K3IʫKqTs%kDF%v1WJܴҟ i|f#9gPx7(1^v+`{SEvV$62߳W}FFVC:!3HÊ=(@X~}ϞU5ߴHTo5}-!9Ia;K`^.5/k7r<3\1+$֞T]W RbtkC C92} ڏKU[xmv]-HH(Q'\8Ř D87?=<xӯ_',EޥJ \!!VDrve B9ev~!u7drF *rS5M=+b"$l)W8O"Ɲ=~ kǞ1J5-IAq6;`YtK<%rUGԹ^:!yo{`6܉on Ot@٤ n Op /0('sPY9b0Frܨͳ:&omuO5wdY#P=M^7Ќzv~Ft!]S!{RyJе-'l3gt*ӧ_~ skעm i!wdH束^)-a{ܚ9;#y, LU"]u)#B;욭*"3o i _`dfn߾/Ӈ)T$nV7sdPD"N%>xԗ8)Xv.yMCɭ^1 r<;\7tݩ!r+go)SJq) IKt菀bi*i3\Gw2.< mƮՍsia/K !'6K)ģNsگIC$sz䀹R @LB0& 80M]>YWgsv7}9x:﷿?y $:5j O8 ?zhsvGai6),h猴"&7ϧnV8?\EM)s~!^eoD0yOK:'#D]՟aM1siyG \/rs&Ⱦ}^׽=l~&5{k|L6)ebEx_I0ʙm^Nޱʈ+$2xRϙᠭ.$ 1\C2/k1ùq=يt5qH6ƮC#MZ|9쌍UR,t2YQiY$}tF M2䊐ž;Q%Zk}rş7>f xM'2 <@3eS .]Lng! Iղ0j9$S(Fu;|`Ĭ{H ~!)4K^T30w,wSnhYfV)Ϋ$$d*ޚuwΨV9)9hVsmv;X:Iw~8\wr>'V+\W[vHB -B.qε]s5[}U\IG*/E'9{߮[4]: 8eysq}l# wXL3mЖrM!9Q^R2|jÏx*rJnFMtv< \M`]YV" VW%2xc͎RW;+3D¨]@.FW; @:F #Li ђ*x0,,\Z]h}jGU;V԰?0lץ;f>b Kvh'<%T:3yS蒢{; ,vPWGe?GXPxO)ZIa%9-)D(.fmp;A$D]?~-ԻO=]32HqGt\ufzYِ)2KJmE/;Bq%%=Feykch`?NF ԪEO't@&ޙ o-a(`Y(E{^-zOxx'~ l"l@=9ڟ,>N$F]nW66wRT*E&FYSwPguX ybI^7rd7f4snpomҮb8 ~!HWHvn:p.â!8t%qUd Z=1MmrR`c&bD\NѳưNyܟJY|2*7Ѝ>=s<#H v 3 \4)7/L}uy,D>CgHIJA 2YAxKS\a"y g{=)T >R!"pe(ė( \. ޹qC҆pH%LõYfU*Cv"C1 p9 [f`y+GfOCڣ`gJZ}~zi>ÞT8E[d/E-I‘&Η7[ܛbi ،G󐍛f0Bΐf`rmGgN"ǢbEU12-!LƱ2,!c2U2ܟ9ـh\ӈaJxW9 I ylh bl)=al.:]8_|kD(s[hD!4/I1{P&Iyf8 FBˉ(G4ˢm*ό:ҽ%ͫMu%7+zş".)n=–CU a} ͔fmce󚉫ix.* yyU}j#$?uFNfڬYS¥]Q$[HCq!W(l& +w\Ǩ3)cCG"XIP{ּkYF|4Ùn?c Ost}2%_{:Fv 0dCЀ-fC|H(Cv/hpʕ*2$m ox/ݰ$m[ CF4 +pJˣT+AZ R~Wyuh-b7:N:Оf %9 WTyI1$$Y/xr"N UndS?dKc𘄗(bXN0 20ّ ->=n-}˳|[e2?O_<+OY|irumm5cV;y]" 9ihRNaEMO`7l$_C9 .̤$ps_~-I"AhvLeEE&0 &G}^ h= }͏d[绲7Z3ғ<_g[ZW5U20&wF_W3N;ȟM0xCmlPE㡒|Պ0W Ja@.Co)i&BNr1h\Dl6f?`HLTFp*aFstA1SsBv e(D0<,ĩ+v+d&LW"6g!n ;EyϚo3<-s.Òe;{ w3Eh_=pZ|/!,3rsfc|hh}ؽhe_痠Oy3. -^}$x1y@}n${ ^[ٓalgqHXaE4,"'Ok$$JcbSV2[.{_5U1}8V,7 j!NUHd:\JBX`䶜Aid!hnUWXh4ڙa!ɓ˃S󀵗p'V#N|U'2WN~GI΀Mhp" y )g xCbgR#֟ǓJ8.Xtup!^cӶAII&GPue+0٠V k eX%y\`L3j] *Ee E[MM4wɰ/)qfQ@ɸ+"ArfG&'1|Diƨ"۔=wk<͏|k2矲{7}<"n)>q7wHI `!v'K0rKFv/\,|C ';oVxr4$@p@&=1Pq\܆JN4@+ԦF[y(d7+^C3  I':4ksiRTɳ)gֳZtt3:IC:f!x6 33ahT闟<}{}/=yEK静n6LOl CH ؐAހ^|m834ozߡ+-ܻ =XZ C*IPdsռVFmAsbFm|5ѽ'`99M3AF2Lչ.!՞E70 CFDZ2Vyɩ^}، [mcVldd1%MkJ1:#f,5~ºt I1^y JH q;#ޘg.H8B|3cc'O.=[ZENM#4{gq*ѡJ;҉!-'#4+\y)O`l30Ee8iy\؟`<#]g 3{RrȔKSڳ;S~PEcfoב!p}i-ݾ'j܏I8@L@ l8o{#/&^?}{޾z<'J C;L3U EQw1Q֔NȽ Ǖ].,B A1p3@')C]ßOv M[kB!lo1Uڝ-}HN{mCNn)(q'a9#,}5=Gxa2"8J3m9Layl2ٮO)SPfI1jw~x5mWjg-4\ME2=4A&DX#K $|i7?j,PԲFbmxiZ{Ipaec n ΚTg\3Jb~Qx" vC;қIfuɴ;w)vȆi" " =:4ҁ!$_ Iq؅O{ + a|I~ +splG_$ U2U']oje rfLG3o (h}a&| a ] B҆Ws#zkwHhg)|A^IyhQ[H7$ fPMc 4r!@-U 48I&Lj %d)Pg:@iːy۾s[73o|_>H#vyKϛs>_q"hu\@> ?~|Aŧ7?z>Gļ.g柋0)ʮ.yoK0pu͆A Sp̻Xk[^ꮴ{k~?'[H^~\q7156zLAm.c+L¢xAO8"[6qWr׺mgF]]foPyu"v1g1Mg= nyO{ԑS_d_p-2]ƶ/@(L]mvMys0[%$o̙V*vd%d d2&vOcj,՛'@*3u'lY%1;Y׽Ua%+@L%)PsaolhhtJE6dedoh6$0CSzlr 35\DMb6p8Oo_o|;/un>/ymPn:Yr j='sC Gk>;5D߷5t?mII}G붷yt/f]VHz=fHeFDfC˜N:LB&FfQ 5DӕS5됯1AffTMNs7s)$~S'r7ϻcÖe5RC^xalٟSpL C6bxה):󟿬˼QfWhgJˆ0n.}RYz\=ɀȻ·w9{9-/Zovv&c5:Yc^+lZyt+l[s>|IB—4{LeĐN A&m3$) K"qi Fx1\dK"'VMKv@F81<]xOcd}iy!5ub##pEs)aCpxDSBvOÃ8Ntϖ/Ç+pO$Jڏi{.• 8\{Y}=2mLCE])3 H?aZ\9_'(0M0[l D l6pI^ z56Onōrzʚ NZ 2z05˵ _?`Ǜ:ڼ?S.ޗUSoI=ekaR ;~L7ܙ䔼l}ե\8)e B л)s_|"f~wjN3uMBg_]FĥSN| &b rӐqH-z <"I_K޽ hh:g>"禝f^#Uqwo1ְ+yk"s±;S[fG_4AXKoG|J?d!nvzlBN>W{Yvj5zd|}I)bC1xgoeC9_< {K[g gf9Ƃ3H{?Nfֳgǩpۻ`tu{StYgIHIOCEO ˋo߾y?Oױn=MWָ-fw-JX+q :'v1YrzI:hkͩH YsX-Tettr&4F3YY)Qriiy,ݿAjs[cWuqWY\E93肝u?S0>Xι`^k=? C|` "̵3ڲinns 1rǘc8\:nk:;lZQ5ƸdulF̖0fULFW67} FyL޼yWa?M_Z)'-;In\2z;%o.~>!%zgs\Z?ڻ"c+UZ 7СM!ŞmbWJcNgKy7t.8 V-0/&=ϛ\|zhD`Wt"] !r"G;$ 8'$IoIIJ#UiA "[ ~f-Qfސw:VW/>޿q>$d/S}Iqmvvxgs 4#J"$|m'˘*ЩPBLoIvm3Y gϵy+!u~LjAgmދב>+s#L01|:3eE3$N^:pCNMCpGOf@; )CFgiv@eK" Ra]1ŚW ]M02w?< ch@Uh.^H͚ ^u ul>rùV#6?^wǯ]֍K gFB!agpND1 -)'8BO800 pRo8z͎@ ~ \'D-`ͨ.2;%!!v2ޫdhKKHVJ?GzOq\rQF?ٜ#m9crؠC2fyҐČBm1,ti^̓f oXᔜ " L !>>d.Djm$N =?>Z" dk0kr2x1ÃZ9MgijKθ; x#[Kf[1wR$ٛա4rdJ^4 L8Pbrtg~Faָ^?}^){~gm;+dK'Uo.8 cE`sZ \= g^8oǑ0Ķ8 wJiU1N94)P rh:'\Re$5,yf4Wns20_6MNVY_DÑ/] Bgs_$4Rt!֭P\?Xڋ<_ QѪlbPZ,Zİled'>\1$#.j^O\buBuD А Zd˿n>=c"=Ku5 2*Wą \uX&*VzfHT>|hHТ0 .(u\I;$9Z;#$muwM*>s7$^ $,&CyXbb[*x34iA802-)a$ΐe0O F6P A'/^6 H}O I_'et=:!D-8{YWKDC碋B 96UVa?H>^ ɒb\p"d ك6@ȩnXy;ts'!6kaW upvRK>z)=}?ݏ틯=¡&J&b>=jVq\wr?6߆ <$kv_f3FA"D?2eNIt@?~0;2W;8_mp.J A# ' [,Dt62*mHEjC0pcpmߜ3Hwukl쐗L@RA6\-A"`cvЎޟK?-ZYA{EL)03K'҈ZɎ8=utMi6!&Nimo^fJZܑ¿HPom6ZĐ>L/tBإ=@Kb(9\-rUUb h)'ڑhlH`81 Ϩ!luO3y@lv+wn hiið!#pܮl896ͅ DMrڠVװ$8%)$$1Iv!Ðq—uu;O/8cȟ4MQ$0;2$V-k1W/V7p͇o30v&BJX Á|F{f@4Xxu )u (D#k ]E E Iʹ-M&&Vܫ%f3 -JcMGrp"Mi+1o4[a;(i lҘ6k0ibX\51fh $m#RNJGaݖ1hHp:ly /!7l=#L]Y!84\[İT)!-!_' 0&TýoCcx8{й/o|%lZ(c*fӭa}(Bl–<IW9O>~w?g7eNjֆBevr䴠{%Y`2>}ؖ~MB7#~䘰 &ҳsN>DFLz=:Ahá-~,?(!ƏHK2"- v $/z>CF8E陵0wPQ @_N/IC)d3 !HkLYh'UEy qvmdN #VQ,{-4c&KEJeJ eYCTMs g-gT<4p3>Y]{OЖs=םB)3hֿ*'-&|rQضx̄m\ZHvQ\.dËr>+2)?96`%'sћ3o '{ |ߓ?Olu\RKΣLr6}as 6te`8m($ 16Iһo!uf8?}hIԼ޿ow;ĜdCkJ.SD[?pVI:y7z^x|]%#dgtdbEm203\?lkVFѧs˓>ጦlBC#<0qBn a.LNmT 9y2HpNfCix $$8 :r+)C^k5IN΁i53/ "{lHE~EO͆W=.605~4Z~=*'Bh4=,`ڲ" L'%Lw+vGIS -9e5aMsHں~DdeFNi!c% Ց߱&^MB $ CtWl<`-C LB7BBh`wZcp+O\j3@^Xܽ|G풬:C M3g4;=Cx!SrZ!ϐ[i^.^@6\mع$-nxaRDi-/PFYzk `g:2& قs v,%[cIB2m~JDD8F;!@̆v{2=ANHyM9a8~@;Yl߼q-n+,y`&WYzpM)\3 a\P٠ xO-WVȍٌV)-w78b3OWˑﶛW_x_觷C4Uф)-So4q?~e6dt"lc7WlY7 ӡr>'huoWą~Q#3]ý!OZ& cayGl3#\G#L='Mc:U!cr1$zs$h{O3zqX׮16sYWDhbPSK  ΦI0v?[t—sb˖X&ڨ P熛SJٺU(]Z__/z?36=G\m?6giMk=ҳ_!-z=k%qD;pnjU%IDATgWl±#Ȅ4BV4 :6T?ME L[ml_֦wDfxއ YU:0ġ$DG1hFaѪ.wHB5SԑnZ$cD aD8776q=2{l!e: G9h/EJ(d#³#KNcg[V}\ `G&rxB d/Zdg/Ue.m!2oa!/Fh+8ƸC}ߙҤj;CoQC- ~`S}<[.E>/^_!6̥%[tn2B mzgM1 E qHp ; vX r#~$͝4PWj8aXfV(2?a"BPL׬kmMę9渖NZvb<@ 5lhR.Ƈ2 R9b $}"-+ɦ٘hCECp ~ ͫ;o,eФ0=kZn3O rn{GakX}ȞU#fZ|O&n!=Z[e,̋{OwCn@=i(WߜmX=N`ofZXzmӮ͖\1Sa)pUzhwy#wO/ݏO?{<:M.YQ'2%e0:rNڣBMr(?/u 9>ϺSBQkRM|sxUƼwz_|}qcn4|kܙBZyM6a! p6 O\%`B2`9 ^?#Hq:Ĕ iC3 $WS U[@ ٶb2p1;)t=kN`973%s^ȅdڜS w9h\P}d\<o?oS7U2^/z eUͻym`m/ JVzN"X a3yBE{O򓌴\󐖶;p[~#BoZ/sB߼v\6w\AW8W_̟_<,[%$Ѽd;g<]C@lm'Z]˸QB 6&$;oɹ oBsskwf82I焳&@=mbNw:*0 UAxl_̍cpN@w?24*f >jj~ '[ClG~5CHAPD MgkA4"ċ^^]ıD\'-Qk_vG7 -}֋0.Wk9]ZO9y̭s!Kbir O*)!oosAd_PgUaC&$dm [8ye_4dMOlZ Ғe%Vuo~sՠ8#TD]{Jڎ=O5 ώ0dXC84 ٘4b=5b{k (ۧ%ScW0vKm䊜֤팦\J㉁Qc{5]BhazGYi Wav+''{-7>4%S:i nl^ \>҆tGL|Rc;JhsZ i܇?;qnɼ_޿yow?8lm 9EI>inGvaJvXjGBKte[  EhΖnm5 !]^q!57q(N#4!-' هCfm!T*1m^?ck_rZ  !.9b)y b~"3u6e5 &ˣSaAyrgx4T.m?Oyu5_|o7ȬՇeP3$$ `!0Nݰ0pA`y5H1[1Ag\1<  }ak~o߼ vp`Bq6f9(|5%:lR^c&' 4E٭d`(Z>.E.lZ'UTR RVG]+!YbЁt٫-nY dBBo C#%\GR+@R4&܍PTIzgWggzPZ8\N` -)eJތqS_x5_}{,pK> 1KWvZvWF6"DgABz~$0Y 3dh&@:d80)LOUe ӘP?Z8?x_gv<2>ܟVsa̝fs v~p)fpܽ՗*3^{Ȗ&;˹ASnֆͿ;77X/Svo<):L63&: ]:ėAGQR?}̷M_dr&$8ø94d<ıyK>48t3) gA@;|j`!WsMf|{!~ɣId\{3i( ""-q3_͔`ľ^8MWJ ҤfJVE8r'/6.>y2[{WJA{ӷ 9Rj"42BɠwV6)r6HŦY!c@Ѹ(!lZZ$nj/E ?sWhpLugh!#;f105C ~:p^&ˠ얢UII {Bq &!pFv $y0Y%gG%–6p!#<+-ObqjWoUJF[/fGgTgePM2ҥ򞡡M菵@^8i_2x☟ Aq ږp Y*r6 N0m)GJn BкnŇ/>[G*\SR-oT]%BH%ъJ!|F@zsr gC/\ebc^fҮ^ !$Lba2s]%6!uC2'Ν՘N/0 JH {i[B *تs0 Ͼ[N9~t!rY7`A$6B;2Q4@C{O0m-3ǖ;}kifO8bjr!t^jn ɫKr%Nȗ_t*o\SݙNy}=?b59 KCad9?Rչpnpd*kIʮp_:S<77o޾7!O#-slaަU'p~ZY|˞E/֟Hƴm3-2Z0Lgk59}׾n {h 2mrU2߳\1ct{f1$)g"J7t1LG&N8W6"[ݸY-՘n99rN4{6hK 3/ d )2n;@R0N)ai*4Vv2|ϭ`wćw>*&I*XM}L92Tv_71 4 h $oiȅYJ0۷oV? s!ܝ y2 nE2XPS*'9[*4c]zM†醴pI`QYl020NȌCW+KJSWFE-eyI gP‹rX6VLgA2&LmpdM8 wx}xtk5MiQĐ*]K_~o^cꤨ\^5m6'n_] Q% xP(qC$V;͖"a,$a!b^!y#LrO~hHV.fH̽>Ȓt P`1˰y s_N~׭e\|f ._ʡaH\ 4\e_{ eUe81iiY7dh=߁;jNJSwZ׼!/ͽ{ްhCƆ|EG /I%hɡL{vZ0dՕvQڋaf }<MGV|D.z"nz _>Լ~󻦿KY% 2Lp'bh1A2&'[CV1}V CKXCbγ#>` ήICʖ+рG %٥:{4\nӰ لD86G^TgRWi/

ٶbL0#Q`HOd0Yj .*Dfh $NÔ-oI$IwX=O,ư'$_ 2t-{eu`#dJd\U{LEWL+&j{Vr^0ē8J0 3 x8N =sü$^G4<ʜcW̅W6cV!'~i\ L5E~`ba҆C+FH`5T0Jo#T∢Lsl+"wo_P<.'lh}ql>YO[ !i! Z"fif_yOԕjf1G >7$VL0̚k;?u K'rÐlMnV,uI?hi_:qWpd\i ͧPbToF,',u@gsv2ȧቹtǘ|k87~x_W :?8c4\MӀa=xKa,a~~D&rWlZrW^W{$oB46H3NaK22h!Jw W`u '9O!K;﹮3֚"c^_03Ȥ}dOuQp)Mu^_c7m=}7yQgx׻!djj>.pcεVvN8ڪY %+w3oW+=xcK DH`=獿h,V{j<27n#!]1 s #h{ 7VH.!~ ܽIM]W_tŒ kI\7ad=4CbOڼS|j_KOzv -[yE94t6)e@=f@㳧ƀ&npy1iM=L1jQ甓GShȠ=_./# [ml>ۊwM~v>I;;3wc>8yioXE 3ygzs:{Mr&gngJ60t!w,5TcB8?ۗyt.V9μ]Mℷ5}'pcJ'^X|_}i&{<~Ҡege`?aﺍH@la9s3hӢZsٜ f8|_ddv \y?}roдءs1ol630&1aX0]_ E@Ki%N m1)')d.]_.}6yN))[Gvu"QNr!ȤsIx߄p>O_v`lABfmWP&DBF! )G$+.Y)m]$un,NSSgI q7lÌ9%fI'?{Q>Ū9A,F|61E 3BfVkbh\6pX8j,<5M7/Fi3o[>.]NQ?V̋=Gw| y*R wQF6T.] #ۙy_)Ԙsf m9B$m E=kn[dM _W`̪jN17L] |&)y:0avazk͂Gm.1N~}gH1@tH%aӼi8#׎ 43{N gv  2}lO }gj~/Ȁs|IyΔ+NCN۷I73&B aӧmcgyhs>I`2׾oަ蹧wM>[9VD[c ?ro5XEChe62½>wU~=y{C`=Ę7h&zT߿XOi?B,@3T_`|HhY#dGKq =mK#w%Ԍ41sEpf,!vS0l%Ap9gWo Qӂb2 !=u)Ly!Nd\!)Fͅ!{>-о@o( e9!)!yɾPg"ζS:Xr#qK4J-Dm3$W|=w=w;|/pNogC׉=2vxp.DC`7 M/`&sBl.U89EkH %gXØm u:=lᬪ?}C?=z|߾煄3jXINf"Ѽz&ذO;b}\%ȮL^buGm܅Moqϼʳ-ݝ|jXѭ;f_[B @!9.Ł3o:ےV@3 IIj~(ZBy?/<)l)= N 5;тsR91A}E|; vC9BNBCM#(83 wf݂4G9Zsև+ .(޳Y,hB5Fj;HqXZ˖@(̼ Y\FF 0 @ l* 솏sN-t e譗A8+mʝr`X:<\0^IL\7pGp?gn~N+mkg[.$NJ^m'fM.?dզ4qbIquNHh5=]]ic ^NkF%I߁CkQ8#0#c cwrFߩq P+RyG{4<\ӆbvipg>]pع9φ E1rW~64!2XS˝l!/c~fog8Y~J! )Zں;o̓$`I ! 銒y5@#x]A補$q 36wp\ ZXF7.@"yD6ҍcݳc +Lp*mmj{ )-&W $sVS"W%h bIZFLK C;:U I?m<kv|*yfXB\4{ҭڄs/P,o@&բ'>K'6&qu~]$Izެv܅ShjmM;ҾTRƔ\ɠ{H H{&UZ䪻I۽Ԓ!Fj> zvjNoT*~6$„W9gO>~ߗC[! ^eA 6AQCL r NʃH 4Pic91Oa93A)F~]!B$"! !)\jFhw$7xciP_^uUZCbV&bx24,a^!C`9 %gQl+)Id ~H#CDEJNɆWYÚdӆA#`Uq>z/[\!jba29{w-yy=SAs5l £pg+Nb3C?kհ6\%!JČKJ?CB!v7SEؤ@ % .{3M*LMJ!ȧ'8!Z"Wdn.f#-`L3N[6@ #dCdqØ:6/Y, ͆[2im: 0Cݟl>QV`dynnB.fѿ6rUTNp*1B]'TiTO% 7[JQW \54pK{*vN+b1d4ivEG ҳ[M3CUFx1ee3OqjEx/oyr {p'/;'SB>i~i~ҵ%nYꐞ[?0A;FC"D[!rVaH\Z2 @{Hg6$#9MZtGcȓuN#w %%f>$jI$a(d8 /Ťم$ >9ͅɐ.WTwQa`j8$eЉt."=mln2CZ+md˹V !nCUݐ]Ȑedp|\7A0?N6 'SxxrN;ݟSV=~)T[xߊT֚3h709^lh*̽̃%N|.%îғz*Xµ1& \uǐDM9 @)"{Gu v:W4ȩR~xuEF@ ћ^.}BXwva]Y8B&R\l(bM؁z۠Xs&y8O{9 U'CZ92э7<_4r C6#$1$~v\ُ٦6qD!Ǖ9;aM:DZ:0NM8l^6BTK@6iF2x,~ C^!Bm*:#Іx!EI}┓M!! *Ai^1΋à ֙-9 'd:{8" ' H(i`]7Q7p:&)9ϐq e+O -WIh+CaK0oF ҳVBh 2Q+gi7۷rs79oy GQ2:>Ax36p@!԰g6v^âHi#WHypO ғwAށÅI] rL隑?f$CH_LF%h"Y0Kz-x8Q.M+Ry& 阌ȥdJb;W-aN-Ӛy׋VUVXs6#~uɦd^WأIt6hNFHRհ6+p/ /M a@bl=Ib YWG#hR+?YGN!h4N$</!#N;OHsŜo\+ (s7:u6 Ck؆L3}$#rDηpD^lZSR ߞ;'BAu0xQ6hZ|r6IN>$CzKpOB4<-:MΩU9BmEyĝsgOR}.m+9mm_nc4WQ3]af~S]{זP<l+!?869Mv5ƶȆg֝򽀝{vd Ȥݖ%d씯n)1_t׵[mݡPi`uLYyݙ\e:]>|J{7)B\vZrZrb)qhR*hs=f9 Ex5\ה|^ @:qw"v:]hTNOG>"P=\9٘M]M ØGNe(6#^!+'iAl`"6e=ǶlF߄k_-D IL\oN ϥzMq̈04Pl@&@-mɐ&ѤІ'3A\XN';6X|حIe}7FG1Zf_&ۻptɴ9w6\~7[սy۹b >rxh4% 39Tr9-5VBgHη VS}.䕡J70A<ہB% em12|#6X\i -jSuH|?$? iߗ 55U9M۔F\{м[m+qNvK'*Ůq2wEtYK8vZ^H̥ -aji'|ÜKN!xz0ˀ/Lf8'hcqiyIe'򤕫O1~B D%LG2,;ss-@LBI3fp5Vnc4! rZ1C cn={n ټzBkQi2Q+l8ardz]dkgvczk56oǑf}Ջ7o *ɽtI$8I0Ɣ5/-M>^eOԒ\6L{4b8&̴@{M^:o@ E]:(s'iZ**7Uِjk!|Y!bN~}3YReYНU]@n ^>çyu$9E~gUclɽ6qN6|d3 &Ѓ͟,7o4ޥKʿU+̷W;qӳ~GveflqT M期/]O_=z{ӧI1"H|y<Ώy釟w:ebTNDk\UKLsI^s=X;,e6@,|&3O箶twekM.(@F+ultȞZz9L]'W=zzӹTX}3f15|0=//|-92z_/߼+'N&]ZWo~86Ϭ?BR.}B>xJ8j#ًRf6nH3/$]cO.{@s$ŰG87Sߔ2guw^ro֗s59m7iK3lnڭC;\Ytf+о @&|' $.82$8iSTioI ˿aQ!1Te)ƵK6[Htg|;AzqK 2l #ahD($a =ss`Fkؐ>u^a{@!v-a'l汓pϘFp YnqÕ^6ilogQ}0tIM1=Uߗj_ZC!۞'pE2DI7Y2ʄ Tܹ [jM8K1E7Yvu_{!*c_]Xr_N.[zBm&%b^ÜZ-*-f+9ό/|lO󥡦6kw?[)4L|@6-ˠI=ӑLީا9-j4UUuA4M-GP5h߿gH˅fȐf=R3er Z$ۜ{cJ[]--)pfw42c#'I!0@:qa B-{8Qiq%W7w}n(D8mS΁q#F_ C1;|^O=Ǐ?PIIkL cV!ޭX{Dv0Xz&k"| B tF߇ iH?|L&cZOt+YYa }vk?FA&6y_}%0 F!q}n OL*xO #l.:oC;óRE $2㄰F.۾ڋ|ŨZp4~!vUi:k ~qߛd[u0WE$JF0lI`Or\٤$y62pv5[F>}O*Ivm -!15akSS{5t 9ڎ)/ ] oeޟok`Eif˼ӹo=jiq"2h{|^4@S}-K|Ըh VBlKoֽޛKs! ΅, /*Yis\`*ot!HcWKϟ/xyQY䭛2:.Qҥ(.; >0BJ.VӘ4`?$B,&V.Zq ְ&IɑuO?묒./o㏿?~ !fɿt`uSS^;ʜdl|XL~KQ1OOܚI Fp=N9^Yl!|dK!5 /=aMb͎̿HkfiVkdBs&%%S`g!v tJv IZCR8d#& @*d"_r ;WW@ds\KW fߥ_=Oy-|Y;Xa؋n8#/.DH'o3]iEh=(rGSn{C/+oBHo ĥ=_v0 $Ŷ(c<W Qg 1< C-ܳ˃1t @ΫH ˙1.82TK@2Kؘ;yLMJ4&`#n_)I/'×+ZTEKt9a4aDf=[xsm!r=O޾N'nj=m71;tP 4oE6U̓HĬڞׯGOrbx?̛{L@ ^13%!T6 @̒8)&, *lx͔Tfb3<20ix5Xr"u` <]!mxޏ0BN1 pY8 !WmDt},o8$ 6,J[@K4.m4tyz [k"CQzb[$$WڈhΐM+$ . p1MhCHj7]H\^Ju˳ejBumN݆ጣ =|o?޼ן<(i*A -20bR5qbxM`~ %fMm !gR˼!ټ(|7Uҩ aӻ,H$%Lyc{G˧oeO0~?ϟyқO˫Np/y zbvy0 ֛W̡-,u1{KoO|׮X8]*m"2 LaD%c0B lB4:c֙&8#p௤@[6=̅y޸Z4̊쁁vޒs:\z0[ (~ 4yCDBD-0fHه,84R6x`C :棑7)b.v#{14F+ \BWr&n)-tBxp!琐ñD8DF'Bx1d65KR`CJSBk]J0(E7ߒQ'_I rMժL9,ڗ߾߾~g8=ުx(M~ :&a1 s`IOl=OҀUg_1'Cγ!mi 'Ţ BiҚgL%i>FǁQS礽&@ˆO!nۍSiE\!~MCN5j5&D4۬{o[6(80Z^X:[CQ26aȃ|pC4FIɳϢmyi뮬^Own-xxeʷ@.H=?$y ٛyrx?=JKP HM~4ryD#*!'NҖAHe޽>xcAؐ qFԤq - ]mΛMD6oiqo%E"pE%yO*ɨOϣiRo:!*9]f Ӟc(0R  `:^\ b1MqN9-BM;Fcź >%*5Vx!㦜6>f\ZOC`6&>9H _ڜpZwx ː>MONz5~ϟ^Oo^}7yۊ8ae#x[zߥ- a#aS~ z=<ρ[u-/&a W{83u:۬N.>gg\=l›k~s1TGm6L=EꊷT>K9K$J&;ZϥV.iy _]Ӓ' V1!CFo%^"JdJD,p9>^wA˰n`@ib拾;V%2e~nw6vA-Jʥ.9pZFzvlyAcSiŹ<eYl$@81ܔ#!qBo!#oqSo?Dizl"K x3ywJ$>D ~MԚ~ٌ8X ?ǀ㋒0p*\58I=(Ab 5QkD fdslF389ل7T5prRiչL@2߰} /DTGG*VQ,2pi ˠ!7i-2<\ב=+3I1Svϣ</{wm]UfٔwsdC3"ͅk2釗ޜ!@!:elK܊[Stxg4խDB/?bw洴,:v|˿U5a7~=a=fȂl(۬f#wt2x#R-!aX !h`IIW'k00d'!tN Dvrl!iEil`80r`fE ɡѼC6V24r\B֠/ZA9av#Y?Rj~Q&Mʛ,IY#M82 sAܛ8Dkqi\3ĻS҂2 Vը:؉p2 ɹdk@çK920 l9^%$ϯ·n6iۋ< Ol2 [ ܞtHut<ϕ#?|x~e~ =5>l52Bfg"Gn29p}4@@&'[A{WZUZL͓lf,󅵰L;RW*N]nT!#l2qjTJ;_ҏH1d~~SףҊ̼>T'ag 2FVnShP'=ٛ+C%6do-ՃBͮKgIy}.$̂>Fd]tc쓭In-ˉy20_5_=ۗ_~luGz]ػȂWHg$64/nCe"p0z7oGK;k 5:'7m.)=y:WQK_.Ӽ,? o^m:tisSPљBVz"{@wx[פpg^CfL352QK%wF2׸!lfAhv<o ˼zxڄk48:B1ᖱቝwWFb إ* $uR/|dۡ<+dv=e[ҴGܸi#!5.-=E^K1d/WjA['~9¨1ӏ,a^C}'E4=WÚ2_p/ Ld 2g/xkw7m!8{ .Xdܰbz \ 3ZUވ@0 |Sbpl{G!B$N%?E$/—!MP`]%@*MGRS%RDH؏΢+!-)/\lO^O)׵_ zBo!\ PCUj@29fOe@K̓ =Zz,}Ɩ14)hٌl0ev +pڸ[ea,j!OMN>4ur엗 hN(6oޢ|)gfBˋF:4 v+m?~8'`O$4F>"-a @8R:s)G+!MI!$NBAZfZ:IB:%/C l8aӡސ|CH`2Z[ ^#φյ 'ӭ_%$CDki^V][zEXlHT'qkrk$' 7!f\lƹ(]{+gM?aN ɉ" t ۸WcGҢt_@~|xyۯ?}x/OoIm $g) R\4e$'H!PE+ MD!^Xcl (NK6-U  Úi MQZ3$ OvFXp!CREڒ/m^2ռ ۄ'j)&1_8K3;%Ҫp}O !1$8sգS OԚiNZ2h6lI#&jUB? ,>]f } dV~s !*-TONIH+S7ffprI$4V,>9̐zaFثU)޾-pzpPg.y,iR4L^>dr%eY6$ c NvkI21V] 8[ޕ[QhHv]AYۧ/˗/c6cd^ke{KgؿAHCFxU%!9'Ӌk @-*܄rf X6$ e3*ĠC8&CU2edoMh:׍wW9Ғ]!sTۉ?r@\T[ %i?!"i3,K6>o`s[KW'tv%*z(*ΉpM6(q@":6r/ í\ My>{4?O8nTIAhDfr 4zM4n֤Dr8Dfn⤖̑ T!IˠM9I»:59r4맹uOy9Y9p2YWc Ǹ9Xw3!-N*8>G{h3< {wmy%-Z o:y&uh CFwqLϯKuB%\]!W# +ľGYl8}B)p˸}wN ƫ_yQ%!}nMצ^iẛ&NuLy3R94H8if+Czȷk<ncܵE i!'kyf0 3wXeanӌdY {O{=vhi4;@z@1Xfdzs:p y{k&.VF1D݉#Joco_| y~a?^;l$h8;caOʰd~|*=12DP[(!I{!ΎL^#\M$ 톴DZ#UD' 4t-37 Ñb;c|4`cRHbQkaZxpـ'C4>VQBR*̘4 $@dF!abh'!WsoifGoP:dup6T\l\匶S. "R~*ѵn>fm(޸ 'Մϫ;pD^Rɼ sFCxKr4)ۡ)VŘ~YޫHwFlҸ/Nsw,"Wq>3y!F9H?y맗7{^ýS-6Z$ZMH dpD7dc5//b~B?{pf1/w iABH&MhgQL!2NȆX!̏Gْp0Ssmsxiʐ=_bCUrńc'0Lhc{fd3\ӟT ^ Zo?0ޝ9uM'N!dciC~ ڻ\96uѼhdRl` lsUZf;yWYTy4Pp e@#>!|Wu6f8bqb"T@hZZ>/u.- iMhpScٴX<!g+UYCSeg~?lTϾsFKs Z8|nCشf|oB=$߫Oxu^ )v*ߋ>/-Cd^cݫ Ǽf"lH맹4[8}V 0r%x!~!&B $rUf4$t\H.Hت´݃!Ow*Ht iNYw?=>|ի L2iK 'U$zB^ m(X6<#}x=+pU!')d̫[A}!+Ǧe X, 8U-!4MF 'd^ncK\cC7bYf2üR>0{ =D QGSó_=úe0dLFA20v)pfXҞp-ybM08Ҿ[*G}J^\ƨψaؔ3dġCn;M*dN)6v02k!u͢s|DsC.SG~\5v߻,A"gb *$E$ %)F(h9}.15d CT>^bVNȥݦf:pFx.$#{00 .+20&Smfse; 1;dk iR\}w/篊ek>s2)!{#t 1Ȭ ;A F~sRBV (MHvAbf<#bb<6^&jmVD_->xO%@$i'8 e=Z1P8VdR =$wP} /$49s6CMBu" BLÖ!r/$<〉ttUl HIH@HjT Ď\ȵ 4p}{uS]$oݞA>CHc I2.oWx[? J\Np93/X\ox %$¸ CosM0p٘p2L̮ oeLB2a;C}O~xF~2/~ 1;5n]DٲiK d  '*"~WZh!*jBd36lk{(tn,c-#Nx;kU'~::8ݢ!0x27C-D鹸c D'%SUqL{9_+'V 96Rq/8!g}ڥF#d_[gOZ(FQ noaQqp7"!cAVڛrJ^@xT!N$)!;$2dT曑aw[δتp- >|ykNsIK]*Wr[܊+Z]aN8=YrSi \֥kduBmf@Rc+6\2&f0&xM-r1KM \irue5!øEBD8(0xnR/8]8OpA;dsd[ '-P>½s[؛OoyZ?``YO5^9ͫg.CaA$ ևF^`dwgI7~]sysl2:}!>$OivHCQKt3o'M6*!gz[Kt^zz <>ƜmW|9r~~_z&_ k!ܞEGLd&We\uwvk4ģQ4<[:`[2=6XCIw=5 co!q4Jn qwdcŰ崶Ë36)Hj/\ z#=;u.R@Uin'/q_ Gcͷ QJ;J*Ngbs2Z@vxsdOeNa rza d^Q60nڀZ%j:Ģ-UQo߆YSA6tHGy*!}8y AG1:!$QV{5t˱enI% 9=C2xct&[Bh՛Zx.- aQf&I{RHI2Ϳ!!4,_ӻI8sCY֯(%L̮ﴨRb Y6gFZ~I\~L8&>c;n~eJ2_)|v9Qoοdls/=Y-OW7> V5`:2Y|I9ߠS OڿZ1q/^>je,{|@|8V oCzcG v3-E/dRO>nuk{,.qN8@d`vy,f"NJ`voU4Df ;p )dYyZjC$/mW;ML]uCzG(Cte5*?~DoQ2x \ ONyHK,$!do!YFզs6̠Idc/a&;O ^CM&u~hg}KH#_ZڰCF%,\ UW]2%y 6؞&ݵ,ZF& -U$ ϐk۞2͋+9e(y&p%H!OI%tUzƫb+AEyd},4~\!VT4\5mtGbWA !@ft H^(e1߽{'z#i_002[ =KT i?$*5vJϼ g.^'p@ybBJEH\YɲΣ<DH7cڞhi !/9@Q m07W7aڐNM>su'0 H\nB@6mkH4lZC\ ;gD\ [<5okOoDDd믿;Z \IC6!n>׿$P~cdKӳHﯦ  40B7b!D?qhEk2odF •dWџ0v aB6)vɧ_On !IZ3AcQ>[Bp*I4RB'jC8Q)zbs5Av!278D͋5<!\@I*d>3%-@FڡrِmW?!PC$%3!+ɐX.OLAB ?v])Upp5Kw㞇7U _޿|OIo`7^8'}ׄ% >AЀ@z)'$4BS1iJOo'\iT/_f3Ȱ'2wγ<\ N]Rޝ*!I p1`ŨPLE+1D`(HaZ~vץQq<(-pR3@M{tZG 55\giԶ؆빤9:'h[mmu4DYR.#c Q1˙Βٌ{; q{}47;D5 {af">cH%YEdgOON@@ (In'ϙE껒qrbx\i@ {gm:C: ͔ 9Q@(E^!G!k)HfÏ-ab1FCbshI-6&M1JU›| /(T2Ƒ8 |fǘWKBd"~riϸp88i;l4`#DocA ~eު}V8ix_6hH%mp2 +r/!( {F.!rIOjՏ!#0Nh4UnhR6F1 iBh %`{~{ۺDsb%VN䔹]p6420ZSG#R铊ݰc7qkAO9+A?;qƦI1Ju <11*6B Q˺ K)VHdRx he`MBRNFy4u>syzw?|zO|yG̏lNd'=#ge،p-0'vpdGjO\h +Ě/ܽ,)Yz4II$hᥪ\ ';iwz0ħ#Љ<ÄFl6t[&DkL0&G B$A83RRa(#Fu#ʐn)UȦa–ƮaÝtn٭ f:0 .k= $l%iwK"+]#' 0= I.RY1\I r픘玲p1M)dW(|V,P 2 W*mU )0 2"*[ _VH uR5̛BNj!|7?$֞hǏ[&֭\ d#\nR *]\D) qBp'l8_oɠ\ŀ2CLx\^O @9eq8?* $VM< osAfW[~d-w7Wt Zـ%1}tnviq@4pÖ\#L=Z WU4TgӉN$cН0b jK{beh=TYz dXDjhg8lyd@.}zI&h4Z6 O`?2THAR jr'yMfPlf`fe˃ !7ߎi2%g%[*h!ggRtZ.2ߞ]MpW6|R!uH HQҎ{~H}$+ !,BVpvyq/N ?2%_ajq%o/ӌO7}BEovs9#Xp4s՝[F P FS.DᦹfP2NE/6sQp &&ΏFkp>ÐV(ͻi"7>>_>~y޵Ox3/Sriܶ cZO WʱvH'm*LVr ?LQ3 ggX[묜}fO;4򣜹#ɯ?3K{׻ٔ m)Ƴm&+%h^ta#u{ bӧp {sۋpvuq+՚@؅ C"xC<~.#2̉#1w8!EdoUN ~3B aI،. {n*&p ryU 0W(|eOLwQ!ܫ:lٌ֤#cT[Ůn`zGx L hӏ $ GhH*~\l yde z]cW+ Ym XWeֈ#zN)N4j)(i3<1]$ɀO߾4#^`1ֵbA$'lm7c&V$Cȹ#IEt+^Zu_u^:\0E Jv^n LSKbL3,D"C[.7?LF>| 2ʻ܉eCe8!e>F lP`ioxP翀X*YZOxu\XC6aА.F!l e ĤeO7mL"@),аlHF}bGI(]J$`6-!W@Q6zlrB&K9 bFty=25a%} Fi8rp6B2LTN̼$$v0 n VGvو4>Idu @FڴMUv#\2ϺTNK&ႧGשB1$B 4ʾ9W}IC̆Oo^c߽O?kç7~nd3O{JI>?ݻ-ۃG bic 8D#ґ8 l({h a}pZ ҏ='5iU}H紨ɟZ$L6zǚA 0v˵&n@Ca8DxחG}8a/g|jTkK;Cy-d}p%kZդ!a7eU|%\>Łp /$~䆏 o M] 6>1I7dLv2ȇ~@Ã$MvsJC"fVb[> e8!^;Dx{QLҐ>7v @9Tѐnd4Wv٦HFц^A$9^8{Y)!-&߼j4ġ3* ʻ)'a4}\8'/AfHe ,y9F۲M`F b7=ὁ )ꄸBp1>/<\^y BhHM334h!rsy6"t0DESAWh0(xK~I6/)ycJXp\X%0 \D3)cB .҃X$ (j x %7:O /X`"\6nl#2p8R;V"&f<__l_dG '׊Dp-L!䲞LN 9G^oȐ$aO=nOȓ.G''K 3|zO/7}ָ_zgkOF,Ol{׆r0IQV ,$;{N7^uc5ܳtv+lv|+aD ʇ)TJ[}4M77Id\ۉ&i؉ڜ }䰊NH:C}CŦsҢU4M oMA^.HTzͤ+BBL" )UPT4qA"ed."5!aRM .!P8v!'GˉKL*m#WACI tՒtLR y&yoD6Q=mC:$Jh@@^+22m6<:@3 nc"0cS-M9D&3/=U 1&3B'[ӇÌFs3%h~]2V8ѲgX)RN`CvuBǿ(0K)Tlibc2N]mj.t.>و2x.Db75Y{.iO y?R'iRcէ®Rdm;Rc2  !|l]BRZWKXx6}8[Č|lf)^-ĸQQ-nV̦ y$A~̓ŠCΰB "O`nHʈҐ90z_lz K]";h0\eBe֫7ʆO # (g+}Ԫ!A84F9Kņ3zuXyƔ6{`4;[J iVtm o)W]:2\pP?m4Кێ?NYٮy/[3טūsӒ :a3ڬa/T G4L"Mx[7C!nR-q>1hb*j>t/[0fy<n-)y{ÎKbHo|O54;-wr'grGհ9SkAUQ~ hRsg"A']^~͈cii'By\3܍e&<Ŀw ʶ ڮҌw,^4V[Njkwة *m9cRzT \dong:g[ɿϛTW, ĔvuA"K/~8~d:Bo7OdKrw9-C9k"t3_Eǀ,M=Kdcf !Sh럫wOdtjĜԵ| iȔYƤMm07N8YrCU0"#DW.1ôA -`y$-b޽Ypx%W6C$|4oiQ*z@_SEi(1I[c]ҟfl?D+)g|/i!Y -@6V%䚖6 92TW-3$R\nҦ)'tB&x==Tw|^ݠRxL!]6mu!4ܦ.DX,dg\B,JA'j^di쎥esvVXga+:˳83hgT][+YkTQc O+g.%14ăz7L2v .ZZW-TAˈ+ ٴ37z<=lS $BdFg@ $[=i/+9ǮQgʜ,ȼ;y 5d8&kBf EȻ7Txˠ .l0 [hrLT+@n~pu..[.SіCCFQ­d~2jH3ŜȼȮWx.U8ѽ3sJ"P'pnI66 [69G٩& .1D1+d<8ۏtynFWZ{kېgZIHvL|.7h>yߤqǫ^/KȤ6܂>:8ͬ5LmmUAtK蒓wwp\.KEc3˲6}Wn2沚_pTbO*x:i6stf/OOեi'fγ#ޫL'}SC*z#fh'Yi}V@ 7֯dd?azw_n} /W\sph"u#<[˗9J|{Iuy艜XYZ˗2| ^;d[[%4P>Ξn{x7s l߬% sH숓iX{{"WN&v1ǖΊyؼbƽ^¦ aN8|>lf^o1b\lv+uH8LΥO~:[ |l:k/RMZȆ3&$.yI K biF^\6cƼ4& ̝|mݫ\C 'i~W޻7á)q\8SdUR\"ft-;Za C`^),/eˠ>)^276߼DYv{"5̀hUEYF& q "ci<^DúP##BعmrW}֖Akvi>fϙCuNYo߸3c&;!Vrmo32w}o敢y0`NN,@NBmy?:CB[تY:'mЫӤvѰ>.҃lGls6}Hq`,jF'7q0:?Đ6ny5gڐ~:FQ 7ěZ'xagL{oNI8Z<8Cr˄\  @ Eg{ZM c^ Wh# "^Z"6oa\[yz#ȿD;H+4OjqxMo6p.;16 om*U}o$dL ~grF;B㩟!&WZav Iu^m\:*bHmDNƇ K/P6bI4t(,ZlY},peȘKuדFC*UCp/nA]\_د /-hEj:mZ!0ސdٴ$ $~7?{~v zfN&쒄i$*ΫvUӐOH|n^Afn;\lZ_gEotaTkN`:\2-ml^0a̿픅KK 0hRLC~1b8Ⱦ3˽ިcZNޤ8;{#D*zݽIHJ5hHvkVE6믿Q@Z ] 1y+AC}-03!Thr}'y _aF#OC(g.%aq ^Bޗׅ&*bH{~Ml0L^ ʟ@J N o8r!uŦe}Zy2Wa]Rs^ 2x lˈ 8dfM /9Hf*vϋQ:3B2®=$^'Ɇ^bX3f#6B8dgz*\pl֋BnpʖpA~Mٰp|w &|.0ǬN}-T+M>wA>H :$ljaX.5 _o߾8û?>HeFeO6F6/!1D&8 %0b#2'6}LZUn96>4T&-e]f?<ͬvOUZ5b{ L#0;yEK~ ׫ͨTfA R׼f t|\IS'>fxf$ ,8 ٜ< ;@7}Q@_=3L>knv( aZrc4+a ]I0$ 2dEG0>!0CuB<(C2a"Ux >HLWL}έ=,9f[ށ8[Z%/^bv|G~[N7! Hǀp{Bc#;rhyd9Sۗ6SDLD0eri䊖t x!mI2 q+!6ĩ '=ᕠdo 5hvғt_}:3JhxӮCj4apF9pa N b.l:Z6FFC2){@k7K kT d,80ph_u#4zQ63]+!q2BH9[ys5 k*4]L-@+ꯄG16p]y$Pb\Db֜$9n lL"Z=˒@&"ئ}2o|eه㇗㻟?zxyo~hH%NrȐ֘Eo;㵳ˀE[I̋lᶻn}GمS{~*]9#;A&mޕ]DR%G#W(QN=рtS@n?(8ǸbQu;r??fH|v=:?smmƓGxqah>oMb4A6ܨ,w u0F@1d@HGKG#p _:Zޘޤlf$B )|Wl A),4dI#%ai^;Mbx 1U6f"ٗ?kZ"RWK%dUmb{ Q@ 5}6ⷯS!JB$U1*{AU@2AxR*.dŽC41qyI_Ov;)E|5iXr1@hd"qMG4׉j2E 2HF4Lq/C1W !WMbæ.guC*u vw話#\d75, BC͂68努-~!褨3}I. .=K_%sa'r{EoLг 4rHsGįmC&,?KX$<Hp#mv.4JN#] Z;2O5 z[xrke^8qY%8\U1 緶UGOo~zyGz[?̻`F;ͅ='HD@'' ׫\ =S3 jحLLͬ{yM~xl%0|ݾiūpO^ח^i~k%mIifd[_qgm8s5Ödw7}H!E} `ᤡ&diăl}k G.{&oMm BECvtFfos2L*t8g`N5#pI"2n4DS"ie)h6 yX]VC dS1\(pI'][{O7dl 9Id^:{%dRor!F/u]*]M34<6|V6A}@:r)5A䚢O^A4}DAJΰ ;, ޒH;Xl!MB65 {!rBl4%#aRfG !EӤ\RMDA<0!5ʰҢB4>xT*bN7 5}ɜ\Ѵ y׾bt(cdHf)Cvirlb} C̫e7d30,6F+Z]ABc&U1LG_c8d^N\#I !jMh7꿜y6$ᇬzn0>N3$]M|]pŁ$\CCbؒ»1I#Ruy㚿m3YV}wi'yTnB:kw}?||/w{J᱓VmF>H*fdߞjPu3Z`Ғ8U0}ݐ.n|gژwJWՖ~!]f:HG&6p:uW7c_ٙx{-dRDY=u '~ec-x%?&kE[m+2M.MbC"wcמ^F 3FI{~HihW72,!a- 4ϰ(:2{5#ػ+v0ptHFxe&ǎo2jW6c(xN<~9c>9̤L5 e1L=9c$]YBћ`jyX]]U r^}5G]N0W27yVZd90&0`¢3 axH6e5F $p?,26kdoMuZhjg?N[FK0 Џ6,5-٘),r]7wXa@6/K >sҊ@ ܯJyyΟ6W 8hl}l/Y=F!SUl>odVgLm#>)t.R`\T^.Gh_y$Ì-2v~ϧ?ߡkwK|姟~o7:K0B v!g#BSp.VejNTsP;+ -ԛn윛I3\zɩ2D?8D6U~'8Cp$_+9z_(.פVx|2ߟ/9鞦ѧV܏nr:GX-ECbpZ8pv.ѵ, c9zM.JjIm1hHx'1M'bOBo3&.' Iktbme$K-\o_8/Ml6.ewlM& qyA }c^z6/ 2‘xnkҲ|Dľ_l u(x lk%)ug8qt -W}m=>1IJRӞKrӤ$GfT8)M ُ^88wok?,I 9\e*dx-Ji\ٮg7{gqҮ NI/"J>'h&wX{l˛!fݯ)kyR1Eܮ'XS#脍{i8 x 88B 0 7K8Zb$iLZ WfpC"S ]M6sO @GUEL^B]Ehe$ mC y OM;bUVN:'5ψI쓡PH2{ eH,3 !s}j]NG61xDzY1r& iI Ꝝ4 H#C Éo 4w$ 1Ah֧aoX3gM\$l@M-͸w^pK[9 y UB87ȅ3*zG)۴@u ibH*Q&jtI2XkG3D`xmJr"sY_t }d]ʘ'~VAΐk@96ZHpԀlՁ9pD 9覨%3nS[;-Ud1Dhf3Ҧ0K6%c41ٌ&i7iTʙnI@/x,NӛA]om'UGs3>"C>%:BKWJ;̘{DȘ dW`}z?Wٯ7U4:kC_cLcM*<2k1chn]8<6T唤t\0 )v=h<( Ugк^S׬BS#VF DmKQp_vgB|z7|z~y_"͆973;WӼc~;-#uv``yN*]! 2H8|F:]lhGw"g6t=-~9>Τ5 u7q[x@7Ml¥\pZaFiñl^~`̡%V{׭H3[l-or~z{FyK!@h` NZe̋+Xr c2#9s+!a=>4d+JE@9SyvK=19'z6ٔ1҆'O1H0 {fC q-.O[k؞bvi\ۉb4߉eR"#WC LTȸC d.WFWB|Hb\Z:0$7߿z]yV}?ʸize9?yE#wav!b2cRfU} v<0p>̿6n;JLfifwJ^Mﴱ 1Z5.YYz.s 2a̙f^;)'kL=Mk-hj~{jR|pȴ1Uf)fMvy%R'Nx{Zc#@?PT0$C2NߜYY[+ߡyEƃzPabv݂S2/a N>I*@טhRC&a.w3 'iK-!ʝ͐Mof5+7M^v-1guX/':%^,uKZ1p"!M2b7mhn\n-;ܺyՊZOLQhբ 9Ґ@re[TZ b4;q^l}ZYU cko_WrgnҽIst3a`pJhb RV h8KaW-cr"-OcW{Rɼ%Ƴi~w1bݭC_I慗*CofĹjoOcsq&2<N$r$tI`[T"~d|[әlUXE#JKC^vkm1.1xt&g؞eV~9!lQ%9 1,dװlKO7)=s cm!xS!#xKETΖ }?HR!vK̳qMxsvҖXJiXl4! ]ƹyUknB库LTuk&14./^hUj"%{MBhJ4<}x''ơg^aME*Ϲk4ܻ{9 VqJGy%=0͗H׽5H?.oUG2=̝*O6$u[yuiOq1KLdV{bwԾn#LecHEʩ \qY\VAQ"0bi9Wf2)#ΌggbF?bȀ$ARK89%֐R&@RE "az\h[+h-,0Fa3@hņvS8 ]cI tJ^8n::bӤc)d?V'0|]F#6W!r"=E! BrG)>b*pv mz%R[/C6޻7_߿çwz眑R䯴ڱ W%~̿49 gRb_,\V ͫo8܅[1 ]kH[te'wB ;9|e;4sm&%+ᚔ!z*-909󥕟-}!=7#llC R7w 1Zd~mWlS{ꚎWSguyCx_ OI!G$Mj!]EI"[a$فgbӲ1ryŏDߠv [+i}cmB&Ep:@~b_*CjlIxi9`a.:$kT%V5$/aI o@뤌nvgt.FtȜ"5t44kj1'J2 "-mB+ȏ4Lk5@¯k8@,;Ѻ&;t8$8WSf*E /2 !8f.-望tQd#CJV̎1w;D!6oE$?@W8'H'h' }7jkxE7nre\ 49T,OB5A"JtJh߿~f)ij|(0JW1L T(ðÅ&!GKoiAm*M*4ѭܐXV&͋p&d:^EI|2xn~Aب Fk+BD80dQo:?o}鸮& <YE^!]' g)6x85lj)99Cr#QǺ S7Ap@g+޲e4g> `(*re+К,shN0si WKΰ$h.y %f.I4,C= W`I2i.Bf^ɡX6=6vֆf.!o4x06\H%!P}Cl"W!zh's!=9!l8%験~ ̀RS%^rZ 23.*)o!tIgKrF%!'*;BMi2 aC0;[8ŸZE=O61 kf7,a^T<;x$}pyK9N-xS*~B L8!h8\Z!\4<-$LIBȣ &n I2K!d\>(/ yNhX˺ւH 25exG-g! s*ݙ(scol#~߯HMTE%RcK ix4 ܠ)pտ\lFmVfm;2 i8GBeINiբq .3asG7 So 3JI(p¢ \u3hl H+Iz s'ݧyŤ Jo l 4QB,aFyht8$@ & ud3N‡Şe|WSq33h/sc/yv?7鏟?xy_'3uϙUg54]̷,"CUf~^FwsI&2R@[`i3RH[|&kJ@d[f7ns*_s|7}\1J>_ EY~Lv!i Vߡ*H¥ B~4L) |$̽liy־OKv}. h|awOvw&m9*+% a#0INSC7QrBJ҉! {~ɞzv\fĔT ?6ㄐ:j5ɠ 1#½te"vltRx9 aG$&˝g3k6ΠK[BK1y1+ulI:5d3NG9›ˉ*Cpkx9ZӐ\h24$\@0T͇ qag RCju`\Z%1H c3LlMÏ+}&x3 =C^3Ҥ* 3J^Diu*II۝tq A]Tqk__|#-[%^ʜS>vpY*".}JCٛL~\\/⧁1?f)xtn-Pq7T Dۆu HtZ<1g}&c~0>,V+-'KӴ<69ǧY=;nB6k8a>i>;\b3 4~Ci9{/} f8|LjB~>{@Z%gvqpU I#3 J$1uHHF IDAf25~8iH[x5gIBMWCx2Ǖ[:v:FKː 4$EKR2HhG0fUphC`l M=qxi\\ nuA-k$W"0z͆H"3 GcȠI8 )QECFvB ")0fNB2X`F^g.qhyĖ D6܅Ka%x^{X,ѤșDF($פ{`lQ !! +Zr {R>axZkn 1T\#:v1 ;ȶzuc!>"N!%uT๪x4ύ ÁayoC{~oCWiI-]o˰DC8ȭ2èW='.!Dy'VӳY*tXd"MtUΒ\P qy!.i#>T ^,a ^L[mRrd'Rh D{ߕ>R n(D5?͔avʧ`(0\{+CJA%o%%J/R0ѯE8MO-)oQqd2ޝ,|fO滺7|Ne.fm$ٌ]8rUDȾZ#=rSnCG& 8 e3rӻO5ys!ʕ=KԤ$I g}DԔTbOTPSP&L}g0߰+âm놋 lwVf>pnٌmG~u0o6Y7&bb'}r!t'JH࣫5:b_pښ(0ZE\5iy y 2rH~ԃ6{Y(輮|':ʘݴNj_W"p{@>g{y''%79~!ILFhw!E\ 'x.WCXV=f^La "k DQ;ΐ4fR3gx9iRa Ɛ$C !$]4#,epLק!y $!=?t!]k7 HxkYgr͑Mr\n8}!| )a I~5}߈4!Jκ7[6ݫ fױZ)!4|b~-uol Qq!ԛ/6,Sb3e*yΧMށF˖Au֪ e=3P;AxO z ooR,,s_EC8Ou#;KhȘ aM;嬕^'?Ko>>射޾yλ[xRn9_7E{69]͕6lU۽> qc6@ew%(I7/.ROB ]>&ϟUpհRyOq 1 $P*2=Gy1u=Lf!QL:1ߪZ0GߟXwNSeŌA$ ]UoexdAUHІߋso_3s8[_vR6[oo*FJK(R$leqƱG[mOK[qU8&q ͵ LYǗ~?4_BfR6EB*"4 9K,ĩ< U;.X|YklǛnj=\pTR'3M>TpL *w4@̈md(=gξߞ G4dTՏyb Gb s~X)oӟ.o{!rVKW4:=t LJihrDOq1-}ڒd_|#Y澊!"`o%?`&5Eg qxȬkv͔#푁m;{;O_GAKy`n*WarZjq C  ڭ!Бw^&uh64a g$5*{ÐftUDΐdoDҖd8d}ݯh5[L̳ '㖮p`hQS0fș-&!\I&pO27 wf?8yTE%U*QRI:u_|hZ}vV4Z d *V6bf@N ƘYݙM~hzdM}(sȠPS硜喳l-=;fK1(׉y:kZbl3pޛ3?={~2{>?}|`Ӏݸd肋~閡]EƋG12]{YM<|GtwB8_Sj?5z#73j%1Ff yz"3}AYWO;W`o%0:T&,A2R\sۙe+0ż7NZC9+iEveN{} H3 4n/ke1mWvFs9ScXs6cf4n7FUe `4vإpi]BRc C=\l2DvI dvs50dl !g Cg!?ܰ4' G`ٸk aCz&yϼ CN`[{og 2sѼYp3(6ll|gzfyKB&1Aք!چo `r7-yEM_0Im2LNkH2i5TPђ^k>cf.Ԅ4 ˓4D0.$;!! 3dN\Do),\})63e Ri5}oFM!\ z`+w:aY1}bWk>EE׆@<\3凳ㇰe@ `RNtGp!*Mh*K( l ~l! 9<0;cdxͭm eNXI;j2\f|5ww:#-m^S֐kQK;;űn!a[z=?wҮ1-6&1Vђd^xqt Udn4*ڞ?ϸRL#<6?jxc'7E ]Sn elx-Z9 ;Z`b׵ qe%.⃜Z %XUaxMsC=y}2ΫSfyifbU(ZG{l^ gU/'Xәj!ïsI5XA<[ Z^=N}Yy;4:Y{+Lj1oӛly$}ȿK5%ZXs ;LJ!cSM!2'VwHdu[@ հ#i>6'pMfdg,e5pI_4'Nnt@!um%ؗQii'ߘ"u":Uye^S!Պڷ1^$!qv*Dycg]lXM5BK5s$w%, leI$ϲ=x] iiU8P0C*R^/%RA8ҐGrޘg:yC@DRAM| 4d@< _<%< ,KCp$C()dVM'!! \W_ϫޞ&(&Œ!Ye#CqN#B.0E$cdgeӟ;g+ޤ,=-;;  ƩF! Y0J0/ YΒ[$˩eX'U4da A'CيHR=f“e|Jv`96CچkȆ []x*Poef~T2~j' n4ˌs!d﮲+H[f} @Eppsmig)iz ˰l4< e#8!0c0y Ip5 toz?t__oRm2 ]C dyN ,|m a` 8;?𺉼 _fbw0>ggIiKf8{Aߒ̤ƿ+og2Ng8M+HŽsv0&= /p;!6z*RەS\|s\>?LZu@OyF|U˩5˾Ӟ@eAWNfn 4'vU am3]9iT*Lm`ǎd0_64];I!B1 ʹF𓆄-B>~Hf$ m$Cwo'+vZG_/j>mώ-P2oᆹi4/@>o,OݞBn#6/dc.XfJ0|(}?l6בWjb($8^!ecC@R3"h$'e^kcD& >}^ngϗJE;3 ')[=?2I]Iubrl^pES!go_F!_al 1L IfE p buBU!-$* 7 q\KpVưdwa!fepAN,;: 2 YTF5W/)]Bp0DR=pn 4ig2& H -V5 [INecֹj^V1 Mے0 IdR?$pp&U3ps I h\Ul:hb3{Pjy+U\b-). dpA_s[Ri e3 rc2g !d7/:}{̃ <.r۠/_ AIP3r! GJ_pr6oCx?7;+JIȄLOʟpL1+ B3t{2ȩ:f sNo%$BHIHƭC!S6kXu$fw5/DAdj oD:̂DuigswEES$,$mkC;QRokkhܶɐڲE0ta϶S쮛iM)}eq}Oly/\ 9Սb!|nU;Yd͐6dSl]rN읓=N,ZنszӧP~iP8(Ux$)3e 9x{YbVuF;Ѭp\Bb5< wˉnFbF`TL6Nbq0u &l: Q-PW[3}-Skg2pڰr9QNӥj4āХ:KN'@RH. uehN~ih;Gc|U!F\vZs옳V':??ILGM-%O%$t ;AK 9ټuU ㆓8'6tY4 r>ΖaX,F32J 'D@XfaEyf҅6OwZtA\M *U`S'Z6Onn+aȤ_}&lX$pftBJ99h\hްi>oS F*aPvJ5@˙- gTѐ.[m 3pN vnFiKexgijҏ.FM+}B!o\= mTِ#Ȇ. !ydsE0"L(\o  (=~ (˱0->_y7CЩ[*k j%1HcRk|IiK^YE[_G`~|?2yl6?/:SEp[n州z*Z&LN{-~hDV%IGL6Ι fa돱Lw8{ Q3HTMyܮ*Lx!*#0›9yɤm}B㈢KK|ݗa5o@A {}~.r \Pz5x<8Y@1f~ g:y|[!SXb{ \_zcx:B CuBxl:7])w6KCg. ".mp*}5hMrwa2y^lhdxv&#;A cH̑\ъ \aqi^.äkΈo&N[l؟t792$rhS2\C :o٤<< J@!lHBi;e5OX&K *#p_)L~{ssDs :"Uͺ#;pICiӕٍG`ߋ6yj<&.|i~٩>Чwke`!ͧOo ̣o8\9mN,r+NԦU7 Zp\BrtAcL-] v0ȆOI 3c19^G~$z+rbsWB}2+fvC!pb3]5@;SsNItް d3!{fң|!qHsJ\lN,aF4z ;FP]ql_4{_WG"&9BCF60wN˜ta&F0$=mu5&ͮ.&pC"[i2΅_W2 #1J~S=Ȱ}lĈ nCbr1CDa5z!G&S0SZbv\EeӛJ}T᪽$E;* B>axG]lB^֨JRT=0Ʀ 1;NM%|+::{Yo&k4@U2sLwP*Ğ\^Ƞ5S^'o!;w m eFɮaHΰIk4S3QҤP8kF{$8Q\ S]l %/0Ĥ!'oI|%ؿf+ZBt>%'f= W'Mnۚ.9̔ d ,$%HБр>|^#j&aiA1Kkɂu(`Jemq /2|u_eP˝՘;y _“i⤔>O-c/?~͛Pw(˟8n& 3cĿ'.&b_^bY :ʹk{wސ7s7AsOs7_Y}X@(:d- .D7,@k ,dݳr^s{P6ot-oeӝveh-AKpm #YηшUāKDᒦk!*cť3'T-)2pHBxC&d=3v&m kC{ (cJѦ2bO.SkI>r~K̺EB6.ApF?c")LZr)Aqd@rRtAݽh=a2+HV !̱,p;7Md!g ==]VYdWXB,G_KrOUƘCZmȘzѬ>7F$~8)@8@q)uIBj#Ts~R1 =m"B'"jch۷!8҅ -G]1;I5j(,׿epv]Q~cdž=}ds݌á6wH^M? ]qmf—cd] "DH[y%M?qlԴFXˈ)p^Q!Y9Yp⇈9 tWK6|JuEc<\{DW\pR^L:NqP E#v@EՙJυ~W*gC]º'kG~v\9~z_oϯs{:%)v:ky4ɠ' Sp $yp6M^繉ÌnAHHDՕvy@@v Pw']ڀMUPݎkmrlew׏?̕t-i8r=V֘5/( .(zo<3͛HlE4o*4m) iȑi$Yb&](w}WuH,D@#y|>\wb<N!pp bK~;dk itj+F '@m% H! #L)tg\9DL|e*c]o"a=|\ A³i q2)ksجFTӿ)wruunhQt.n@AtHF 4Iz.qަ$p l*S.&#õ!lW~ Pva , C ~奰 NUf>#MÇõ|Iz8[tR&o䂰Ƥt }w-Rxer_+e '&RpR٬JE3 u68Q J!gwFhEUFĦ]#CY0Bg*icN&slF,F06)$fwmc9m-hߌaBP;O:ileS aC癡}LswH)>n;"0g}^nuZK~zypٛsax~92isy~~^CŇϜ*OxfZ4_в+|K\ 4[Y/҉+-إEar^ EKwHO\㧏$v< ùt{W"a!8&RVUU+0 @R7r:QLQ}eR$\qGsNۿ`Sf=@;'h?0&@$N ͪr`MiQf@+vrr:Wul .A?8!e]DZ N8C`0Dd-*`jsgMfԬL}9ڝS׶xRYIt HODbn{Io(NghN##,3\drtG=q@Jqd0o!n6KPdcV1YВJf"%<$);DRf :P$q{_'xo3$xtwC93#SӂۑꆸJ/iMz&e,tѴtҜdM!L{ \9,k}l3(4KtRxSN.8"> ~GQ0'tf,/X+??Nr"ׯnۿ7fN,ΘNyN )ucxX@yS hҵJĝT-{f x#[,Qil=i.SLϤ2M`9uO}W;m_/a}u׭)k9:"[ˌNY쉋2COu42L>uڏ䱭8pۓ\˵܃qf8_M,~UlgX=۷6Upe(rIoKRmj; _/>B6%[y6>|v8>}CWV*m;^$IwG{!8;igѧS'd\(wCN__̋\s0sdZia)6-?`]|J ;)Bj%߻{2]?ɅR:XV"0sA&Be s!)ZĤT6 ʦѮw{_oo&ȞEO̠%fCRĢ+R# WwzQ\Nv^0ﺊ.W\{ uq2|%;J]tƪ=K'vӘ=1)s3a@Fߜb ⴨[{ѦdҢWb6-+oYe]/H$ ^3x{<@ʂNOt!qX^L!,sw-S )RLY^3!39=*79zI~%.CHa)Be-BVTB%6gKYnr׿鉻HݨUjBN%e C7w:a]kh) Ͼ;{bOV.B(L.~U=W$+~OLTr?% ^[v)Rr]þ{m%ӿ* &]y,$Ni_hG*mܮb^2":ή؄&])iS4ͺ L˜d"Su)G{h0>;ҏ< #= ZĐ|ѴjƁPdp;,Dh'#=k%Ls 'EH!p h(i{@s/1yV(wj)0/t$%k2hH S"'u"n 4h.@ft@bƯ;S=.@Ywd{9R}O Bf^1pL] (!UKZhWq ʋ-Su9KB]ʼ 3!|sI\F 9]Q'߿/~&paqu*/!r&yԥq8H0M{ ,N]"%$ZAL2?I ]҆VLZ5O[o>YXX "3h8]!Y x)xC56+O?ga qun|7 0U`8ꢵVLPeA@s,!Up.%q~ŧY't ;&%n()E;A8gD)1K 2\7t.rhէ"[`LM|U*S^9TdcL gt=y+ + PKNa[Ezv+yLh ɪͥ2U[Y[v8腠e-D vO{6| +wVJL܏ c!\->&mYv O7^Qo_}?ӯo8DsܘYifҮ hאÏne!F׷I79fdMy'5N &>r?m#Ol>t7sh墔qN¤JSץD­-=aI3mWh/CS=<<&Ӡǝ/z%ByQg 7:-^mfy8 6%dΓwH(U2ȼ>}m% hnbVtO=jL1+.=/| %II{>)fs.L8!C`2!Plh ߣS!"s6%Ht;vv<|RWGP`HJZVeg&'B.f"l]Vz]B($@E#DkNr?A@? ݒ@P֢~ I6^aV9]iFY}f|uYy: Ew#۪kN HIA.T${AbYəHG2TDKtxr&ȄU\$R5ƧWS[ :BZ&-P7SL>:`m^g&TbfR"TASѵ%6 IGgiSp8 Vr }wc~4)b)[֝pUq{F2ݚNT94@QH3*Vr`xDZY"L{"fqB?IH:)Dэ3%P{K2"!Yp!>8$S0 cLDo,ȤEiC ?Hɴcme-Ty^ B0usmUs/"VQ WRpHbY3R)(2r)v#}2:"O R$LB=BJdre9EoVR{hf`=X'GMKy_?w3TϫS_9̪挢%iVgLe4— 5Ake)2o\R@n )ԣz@<Ci @~$ seWHY]*yLTk|NrEO(yyV3rʀ D7 |=mGvD´W鍴HiV!E )@ʺ(m0dB$jqזbijBՐ+u!#K{)2RKarf턪Ha9 t9VydJ9)ӣ %!/}@{nkBfA#%8'`:Se#P 'QN,V.,XKu} 7y/峏/mu"Yq^ Y5 %w?Әl@:Xg A:]CҷO-GPN Q\&G<18D@"S%+;V+we||_eY;+gDSOYvB8 I!*5dH )8S*)e">0>ӄ9 !w"h nҴѴ**~&m)N(sᇠ@9s?NY?G9sn;K\v@Ѵ+o&Ja ]Y!8DE˥S),/0B&JtJBX!ԭe0sK '0s'Ll]x] /N\pKrOZǡZ9j#^v \$$P*1TpO3mtȩPōמUE]W;6 {eԍ)ZQ̵7T[U'}Q*挑25cvd-..#k, p{_ov67O޼_?˧cys? 8eɞX>6ל/d*Zg\LBi>C&t/KIkCįpB&ɫ%g\9f'S&V aTÉcwk @6 9/*&-&+P(=Xݮ/.w.jzTE  ъFox O2YS~"lt^ ABD axL9V:](;(]^LZ:cٹh#ѦKJK`"ǧ76G`ɣK 褒+ 4Aޣ@ bhrNs)t ^7\D]W/s9z;ۅ?5HseW+5ѯ gE4??Y+Aάh3YkdĽoRH~5šT'/E Uؖ(I@jhy5w9]Ra)GD΃ Rʼn G#%@mWPb_OQzn] ]t)wLf駟hfCunZ*.=:TvđQ4p_q 2"R~ws8x #!"t&m]-VJ Bhajr BHF˱D|2RXLd"/}N btsRN$.޾OVD7cYjJ#!XcaE("&'ӜWkqVmjr/K$03XɩQ"?+9꒐l6e&eߞ_|;/^~H}<% m=teSB~v#HEAs [/­ ݻR^GX+!Ԟ 2r[t@efmh|7tvgzûK";#h(+<a[DڴNOՕaN4-]En =G )? lRʣ#G0Z^t#rL)B [II&Z xtB]|| q(g_E)ttL Nª !箦PŁ?#U\'BZ`tC&^ҵd\{hXޭL_BxA:=G.tȧN!/a0_hy9j~Bc!h-AMqR%j)+¼x{I~3t``^h-Z_@/0>8U53O̷T\;[YѴg:",0O4]iMH&!L͛nLQ]P0}YRd @m;s㢀b,1J(eA3(JL&˰72+=.kMHݬZ"x[/=F.q|Y=ى.藏޽}xcBspr6] G5Z< N1C7tډǶTrŁpHd 2N;=F1y84"ܚ}LJa @Lt]T.Ch_? 4fBJ<}5e_\:{#4s  @Ǥ{hP@a(\]eyf Uat8C3 r`4 ǻ>j2_¦{It]'kÑkmٿ'eꦎ=juŜ L-~ Hl2i !񫍢mfԷP9:Oh)9>D!2Yzj.E຿:f!,SgK'{qf\=?pFRL /Š O'h!Z].!\(;bLK/Ƿ޾x|nH _7B5!dYڿ±kɼ!<,oͲSwopB́ǼS6 0_@ DNcщG.sʺh0N#2 $\m4r8ڃȌxf89r e EhE 2ia=d—6L$a敊rL/Xl$uwə*@0Iw| >Ŗv_IqSb B]Jd-B'?iOM5XǀwN΃cǔaZvY)g(/q:m7V; &EѦpLg:kڔ8KίG9r9#ErLŠhVޛ7nfHJqdGu ];|J }Q!l+;XkS=7'sL/"jgOKDvHƵ?xzªۤ\ICnCژM ;َ{9WӎV1p%$ǬyEюO s3y'ftm: n)~HYJ7)LZz@B՞yx3d@ݣ$h[۵p,Nt_O?{ck>zx>l~HDFhl =8^zYi\Ir/(d\gijv/L"A98a!3WK #~[ca%h?llfP {ßzF/_~fl|go_7 bj߶RP07s؛y\9gv.oklэl(˙%$GRmE!(XH0I{c;6`u}v[kq*L*rpzo%yiYy$)LP9qDzAF 3c&H9懶;+kch]fg63s\M.O\ZvE:RЅGdhy$rU@$9|U" 3f) a'"vW)؅7xU8}7]aПgywPT'A^O;u(޴+/Јn:`0_r*D(23L 8Gi~۟ 2ĘK7_?__~z87-M?ym~9A(WE }!*}loƵ1ĸkcxfA6ڽmFPlЋ$)<|#%l s\_Ka W܊X|>m\^e+DneVWT6V"j?)vN(:9S5g1ZzLS͎;eğhl0)\ Yt֣fHvKNQ9JmM!ZB! \U9l3@`=sB2 Q\ i-ʬK6\܆yٿ׹YOa5?~Tp֣'GkSF۹wE#f gT@,2DrW? 0DLneZIo\pqd8Y6X'=L!m~tR0 y& w%w^|vGE'"SKJ7$)LU jN)&xʻp"&C ; w[nw\gAksJ i]Vel*/PKdc8/eN!CJ^/h!Us,(g0p,b@XwBMՁvbhm Z?ץs˛Kcz\I~nuvP74:]Hx]~(\Y9b hCH.BUM~&-AsWCz݃\8s躺lzF}{$Z VRm{E7EK'FNSX9P\ns6\tBg ?ufTU¤a䞉++\'{ƧղN{t/yW*?%̈́IJ)@S,hΑѦ{TDDEw"T|hZx]V1 |&mD";QbReu\/NM܆8%(q!Ou1D Vdc aQEFW?Q? ~Y\7wMH[_+v-"TWzNtgTeJK2-&LC6p8"kSԙEU^Ipn͡D@J.uL}7hcߙ?՞93ͺp2@2Jp|˨;C]r8'ݢ%k ̏Dp[ࣩHUeC>^ZN<V$DEVCUGR{K} ~uSK$vBc14JH2EFp}VZN!Eog2d{Ih8FA龊Dɫ9r MO" KW[aDj$e(oW@Lǘ\MYgFqE1 qDa4+1:t^͝2aʥFJc҈3F f:.)-hx=TX]-K" 3HUD;lҸ6 F9!}ۘx#g~`?TK=vjmMQ"TFST{׼07|ˑrܼ󨍵[Qw(dY&Ƕ)E&L^! pqS7PoVPR{\a>Ro$4/. eBp ep uJ2Gm] dX}o (N .̄Wh!hbʕDNjYɆvB̃ 䥍-6%?[EN(,R8U<R " ?]^yr#'JMnRTR\8^j:>/LڲO@݁[BNct _|z/OPɿ2nkP^)%0_clKps5T^(^mu'| ӆC 'Hcn8t9֥0qrww8k.#;96( (NHN.]ゴj_l3PaWe4 򣕻EKɲJx<\ Spm%Lץs(wH}vY+/ݒa\ x91MApM)\HșǀB.Gp\w! W`zmݲpzVME+\FfM7 o@N;.X=<|)Ƈ3 N\("D5~zryٶl5STRNZšK$TSp )"-Q](60zAAµ'> ԒK4y$h#PJG!/$cS(Kq}{Irߘ$RT!%:^zܯ[ޛ~LmÜGࢌy0q PC?ك۴WuF>T,kq8#QIri7sroEnko\/>"l8[p2W:+y* frw$y+ѠVG!ik+;@mzbR6.tjLӻ7o-!fc vdXJ.tq RM락dJBޝs]Xl) MBFYy8 ߗ/e :ճz6"/VT7F2C[;`v | {^?)QN ][L1\ϯ<~qJZ?}9̓ޔP?}pl {)y琒V}k|_s~mAq32[eGNʩrtC֚y 1nʥF_A ΡR@:gf" f(ѦPURZ$-AP9$R-iCѬ#' :ݵ'Ieh͡"OuEU<7؟-߿XټjZ& c $4)a 1) E Oi^j- &GHb֚~NW|3` I 8mmK9C{L}ڜ)!e! M3>/9IіgfV*=2?}@i?Jw7p߳s9S*c[}z6̓\Es/W9mqZ䅿.+$Ξ O!쥪ʟo4:Dn]-+zOWfҚAFϏE"YyJhDY(1]tOі *{.fh ƐV*Le 8կHםE ?A\]PδuF̪V@ YvԊy|)l'Up %ږFJkSXDA" BѶ ?˼JAbΪȑ䓅>Rrαx']KC!_r"nVmrjf ^8`4OP|zgQ/^]< 04PGξo;eKqTQFݽm&!K<ųؗQƉcv@1N{D_\YV`#9JL4R4u!)k n/ .xT\QG2~Ma {(_9`%;%,gܪ'tqALܩY'bgqn45Dw>`Ef 89 R2ҵZMh SҵN2 k֫P%E6!" 굅õRXE(H*I }ySsn#%枒 i 6RPVڲ$LhI.duq D(ݙ:&G|g td 92i+@g: fS-'$X>J1DTҖDpNYCHR8K8]I=d ӓk9D0`&h[@$۰$0 L?1lKhnN t3P$$-x2WXuO{FAX+)<k#hnܳ Ӵy(%_9:K!nbT4F# rv<,p;H[)וq9UmMus'j_F['@2>|쒹|*x7pO:M;Wg^ضtQ^oCߟz8j%oUzo7$ZQy9qʘ;/{{^yywϧG|D>3̔__›=4 8?Ʒ2 NipzΙ==~"^fLe'd߂\?8fz,G9_8 kw{st6$_ nZ^RԶ%2?H1S7ƶgN?fz/_8w]-Ν ?ЖJaf*;At `^1t"/-"r`4ʸ=ɲY=s0FZ]/2r*ɜ-?Os<̋d'GV̼ڇ~}vİ^PSac cb5auL9fԁ nrqLb_${\}.H6>~u;XE$:5'@54|aG[(F:~S7Ü8Sp=X|rz{g \84. KД?{BۯXyqVڑ-kONG^'ͫ3_33(:3{ڔtImu̻rm޷Y+xlߥ2yU2vQն'@0s٬2G2v|7K8ut1Wf3'ӟ$t I8miOj {q5]_7yJA}xu$n- [uDmV6w"s]Gvhp(減3]ط;r"d_e0Ȓ&]49PvԱ9WJ}~= w)Y!xhZϔSr/1|NI{ kIdhItD"Rm 2NTXݬT-ȱhcdpRD7rNȉB &illCe]w&0H9?r:ÿ/XJC:|e p"l4xx΢sZ:P:sœ&BRT!>-D’Jur_\6,=/=yTShِB8a(S1gl*ZzA4p}}@H^1=>~!-zfQ)&&)#I&.ڔ}KC+/E[ oB3ƚujKݛ5ѝmg <.':{}k_RareI@hapָ8uP%?}B' sLms>7喑j |8P dF#D~4-_ⓘq(H e:|3U)..X8~.hwy?Lg*)bgiY qU6 8G+xIq9;j!t¤[G3);p dBƨ5|{_w_6W=ugBmpgbsM*dvZ9 yЙ)W"Hn,B!˜:/'N̘ۼg뜧Mլm()c.hT^bsњs" {vA~DNlEoV ]Qw.Ct-˞P6w!|Ī˜&ݬьȃ>4"Fh'1O_v⧭25LDLY UYSLLp:v3Cm {89)3mt&cO+r)RÑ{,L7NWIYb1}d;qx]+1PJ|"9NPw!&J 5]d6GIPr^m$Σ񪁒/XW-k`=ju]R,x/!55'Da[9fz@t9Z|ˮou!g&J;R913 Υ(UL#LGU@݂kΈ]\RR( wW*_A1Ab DwmWcL_!XPxQX#WTmY㘠;Mͤ3rԭeUQ(cBOAr~\D荚r粮y=%T+y'^ڨd8v39sp׵m?/<#}Rg'8Dh^6v=뢉iO>s.vK(?ߴ3UH %G/HK&(Bf2>hajCZګžg{kS%t>z`Z.VsW&j3uo%  0 $l:b:{Rp`%}<\w F>)8uy"d6%l'aB?G%L\)e>~%8Gaפ?_3? Aw{xg3qB22=?O͛@-emOpLEӝ { W+>'g M #<.ZxQxrbc`9a;RpI|+ԦDqݣ@[Լн#K`* NNAK2\k9)L +pU; $>dN躎l^gybl/^kRv8 OYv[/_>(ڐc8t5ع7Ss bmt:F4 aNfēWLQ7s͓y<ы~k=v·}8e^wu;;=[i->OP3ykS?ׯNU̬r~jwbdL]4[Ъ<;7-=0uT R3H3d?.;a :%L}#6Sךf.׌q'|Y;{A\d65RL!0:.rDݷ H>>K!%cpK'slܷeIz.^mWj81i38()v5 I!&v~}@ ^.^LX]: k.t%Q#Y!:a=ABF &w`Ui upJdKe,)6ZI!WY7wA/"` kE wHwD@]fEOINQV=EF񘾩`'4g%V"7WZ( g?\Ɨ`sݗYO"dm]U.UȔª]0XS͋Xڽr]V;Ė Ոl4ӊH;2/ELO?~2|_yko-MI_Fc6\~Bz"Lc;2}05`5H*fu +]ۑSj:nڔsϑ+$(\ )8M'"8r6FD'z PLwҫGOrR,AF M蘕GX#&2 xJ:M!d=&8=:pt)W }>z*ԱbV+9eP0įhB9kj`j?MI_wymL7α/_{Oӯ?3ly $_T/3 Qז= eP7ERpL& @$̼;ȔӜO :_#Xp hbeΔ5eB=a:Ǫ\`z ؈F&pSЯsvnpsGowbBW l2j|iɤQ"o `0;|e/x2Q3*GĘrf\N #Tϟ>·?k^G6Ԍ4/7Ih)0Ga ̞S>r̳ɂY'tM^)},J9Qğ9On\<n/_3s]Wfţ3aȢ vsc6KSYtm43_B*Ek?$l)!la2:7+p0]m̈\;LDY S>A?~{yh, w6ޘXW-䚴Q %+L٢$_w}R $8VDH)/y ޻ c`{1;>N7\;rre? FG{*2^Y6ot م})#wmI|5!DPtx}eQBkcIVr  1?q jҜ΅->0~G[mI ⦋"ua&!J+_2(L'_\Lk̀Кg]R)P1-&)Z. D=Yj9+̘ʜ~M +A(k"u2 up9*h#i"3U.ǺiPtKJ(')9LV", 24;)}F"7s}3KUpy(>^|!_7UIܴO{3NŇѕEPLp Л/ #KL\L.t8=i( ×.;+f)!L!+8hUR c. v-0i=z"ʷ}ȅ 8?đt07㴜~e4{=73fm".96 !B$X7 ўhG)CxT9=m~rf7(]89"V7%=+$zLN[CDjKM/DX6^y>e}}?7/GM6Q>eNE+zT0U;CعYH.RkOPYiN!̓K"};I3m \a\f[DhEhߟ-qvV&Uƌd>~[I,HtSRn џ)⋿΋MW}s:3̘Gg>m@v'|ٞs'ӳu8 dq'LS`e>ֻ\c kX,t_S3 Re,yA3/~)Zlkxlq%&.u.~ j GKrq,?Og-lEFO׎ҹ?rY^!%l!Sd" eю63?;/e;KJ"p*XJ/Bq̋ _U ^^!ۗ0\⿟ـ1)t9EPݣu )6¦. еꞀDzŐ +Wb̪7 `75͞htVRLړ8.D8 'PLyShY%w}ѵ/zm8MeЉBRJujKX&!NEj3y kYrl Âa*fٵI9xiy5DžD_'[e;u!G'ɧ`x[FsNšrL[F6]U/:ion;.`T ?yEJ߮2`f~s+8CU413 9S3S,t'nMŇ7~\D NVm'q!?v@&e$^/Tx t:^cRyrDaE2rMVN8onhڬ$Ʉr],E|!|% i\$&SH]q~d}VL7Qt&Dck1O .2bRr}؁ujO4LP!K󀄎gZh&N;w/s>x~?^|rݛO>LG$h˓Z9\:v&+f.Z ȋo.5S̞ u'˾@>Llc߬ ߺ"|TqgSD~x',<L-sgJx! v?(s+&|gqq *i؃ -]E`!8?& ~w }C @RDC&l] 3 >ֳSͧrgAg ]GLIe+mEEֺk}<vG= |EbLNI6jD̾Q%]("ε*KA(eA")SO˄.&/A4(c$Dioɦ,pepY!aA&? nO7N)woLQ5,_;^9v*r9Ɩ4{iHךjHg*qaVdVKxќ:Te$𹠼2=sXXk T v_S'B]=9C)3 V!K?̝\3eˢ/R "n|m̞yb\s)hpB/PRY)ST.)KID)N)*Lui.3-Q9;QYEӊ31'ךwfij˸9,alz6v9ev?W:!gHscq:m|^xpӟo%|2u^5qJ޿om A'B #m ̋=AHJ}3+s~. `@.#Ywo/\u&j ډ~mi޸0_I")sAvRI"J1QPn4tJńAȘJWy#FF 8{OKSq@ gCo8Lf/"[F㢄\s3S-)cERgMI!!PґWK)&B {CvhRȸݓ@%Mƹwu[Sڿr~yWy!kdvt''R7hMRPs!_\,fnmBdp|D9(=dyB4X7E^f 3j16}d l 'жMxȮN,cfL^ nvXE8`"ݝ< F96Zc_Zx3.JH^0eŬJMD6Id٠S("p#dV[0  Hh-aS7D|CtCNDBvWpBf+7;&8_u:J7 ?կ[WuBRRpFgjr6nsnL]Cxurp u昪oW9&6'kKѨCֈf ˵ړPF՝_;ȎZNN(p;) "RrJ[}.j#As∾S0iRtrDe vPy[[TC 276 ͠=7Ü<} K];`t-xdBf' Hsj֗{( elyBtҮ)U ĤcjLͷymLm2ڔnRd*Lbu;$Y=ǷܭbPҒ6柪ß՛ĹWtܼZ2r)E[9A<6O1@t^@z.C%kPR6v ':maG.od3;4?P6ag9^!GQl߾}|Ii*yM܀O\So %ϜHt=I5 rQՅ#ٯvS<eQD 2srD`;[sϷ}Tˤqӊ#hSN8+U>{#Ri~dbTfȆRKvp!tʔ=x5k$$x0 qk@0g2Sf"':spC@{ɖKF)hšx6xu3TMPʮ}1Nk֠8$\K@#pZ^fGlo_~?W--M\Y)&Ӵrok~ŽZK2^FJeFB!(YL} C)IIHa_ί.NL暢++'n|VȔtm)J ~a߾ڄY6|BBh'8k J祫0B E|P0)J08xh%͗W{y"1g%n(t )N^t-c@-/3C<90U-$p|.cY [p:Npvv/TLJm߾}~~z~!9JFQh RbWWn"BLgxlOsy-lNr>c`v@FZ2wwlfN@&m"5p&tQ1!G9mҬ#-\9vY lύ51Rtyс4Bu_a2y1ebNK-$ #(A;S 97&*yCjt(!Z䰌CeU@9k:?) [})+gizp?wGsu8'.s+-q7yB] =cYi([hU0rL#B-d "ڴ@!qc Hm)Y&ݥT,ZL=rM+3u˽6yU\sE|cj/y슜dN15n3[s*;fC(\XSr7hX@z`4ݬђC=&12l~3S7 E3Oٶe̠{3D4ŝ`qt5у;xE+VG1:-pц6/̭'7RO&sE0s } :Uvm"Fc;nl.Dr#lj3MuSHvN 5hD+,ï{,} Co ;'S5i#-G^d20|ߦ{z]A3r^@J:G[ S!%fȣ ;APSLx`q<] _!C _pJ.!N퓝T .&Sc h!6o"DJ$*_WmT`)mɸ;*zehQvZԪ}>]Z~n^{GvV.)BR$DIRQJı v$UV7| VkL *T(ڱީtǹ;&0~4Cdi "ZV\t[R@‹~ +xd4mR*t`& hi)0IyO.),_5-'/JVݪߍF+8B!1!f oS1aʅ4F8ڣ<(ZGq:N$-u`Q]yOz7/ۿo<|ꛧ[",@d">JXhhɖt*D8: ߜ4?{+3Sb7<{:Nȩx+z8iuu fyMN\*_?o)۟&LR gڸ{C56_8 w 8|??"nsc n)+ &y#rSDS9:MƼ1, ac>̳1Ӕd65y[ if"Rlwp*˜[&>n4x>֫[:) :ϑފS&npDtq|:+٩)ڼ2]4m5$ńԦd3)t1G\=Ll@ujöוq$-|/uv3&YʵOWpnjtLt|%K9lx11:)Af29Aտ7KzMЗfMP8L;B5f#g)a>Bmw%>9x]zvY㟹i*Cúo y=w_y~~͓7q3;m.]BH0Wi9ډ0cL6^xLxW%_\ El%WkM6O m. NE\Ḥ()>pUR*:ocdu-fBkw!NټܩvMsj;S]&&Kv[@ t~ |8O;'Guf`ډ#mYtK65 y({Uo3 Spd5eG̋}6#SDiT8 A2kzW8vsUqFyӒt|P[p99lwAE$;!p[V+y`q_3[M.c^GԳ]uk3HsҶmH0ؖZJm].D^!KdټH~Uu=sKV dm8ѭA!EBYxbU[I7 K_μ,3l edlF>s- ZE~W^'Rq&Nw?B#4.2{UAl7Dk RF/u0 NhV%ҵ޽kHP*P0 )Uya$lC\) !/f&5qw8jD8]CS r[(zΉLqܗXkxJ̬>|0 XyIS*a '1 Y7Z^ckLe,w03,%t)>郮 뎣cu^ƀ9$t-W#TSJzGG=B gXqa5ZS&^\> _^|r/|f3c/pL>_uOb5czqf.SFv̀v_56|Ўimu Pqa?m1x6˘F: 1't|Ü_$LW|g}]f}cr' ٝ5إ7lr}OydgK )[1B&VԾo~?/x~6ULNVM WP)d&793ⷫ-&-UލTCa"3$vkǍʾM+얛Qr]ޕHw8 WICF:~5~A'o:1 ܲ}[fOF"{ysb t]ġL{8]ф5NVgpmC˥Z`ZyM5цY>Jů,..E+&I!=Ѫ ^MWu2H&muKZwƮKxT=$+<~'TRݼ[ &'hsN<ȫcj7 {Ӟ͗j94B@;gyh\tү " W/H3,;?22a&2Zy kwh3}6wѴE52iC֪sdٙ]Ǟ'eS*#Tp(}F{H>' =F0T&.s̝aw`+CkG6Ƭ# T8fuY6}$N=#VY;+YHR@ O}{Гpm ZXB!*DЦNcyHt\oB'w׉r$wmbi!;7SL]9`-JaӞW?NuY}'^iP$OQ+3g9^o2{ gNT 2ȞX7>Qwf6[A&yo˻}M!"R@c`||*<8(IPMP]YTj}-Qwi\Ԕ׽e' N8 aL;$^2;0v; ͘x#PLh gOS|VuS #S`2Ԯe-f&l2d%ԦN: ,9ۇ?obmI.{uYlpE+E)j(FWem2ӥEV|]-]k4HM.ew^yzW?Q?; ӑ2eb0.s{ttI4Sz=}ZE7[S{n|Zo$ ܀#suҫ*Nnz”N{Y/?D8ћ$7g펚H.#9^ $h\L]E^?G ^;vAйߛį;Y_R=+u{(Ԟ ;dJbPs 6!%r%t Z]懄gJo}ZCajOBJowdN{rC$Cݹޣf?^)awL=Ù#//] OÉ3P2Awܬ"fҎ&yNk2Erpqr.r[r9LmZnzW {IًTAT{%7^](Ŧ$f,3U]!!-]A1F;PbVwwM(u9<}Lq_SW9Z`$/DtҾʴ.PBo>,hd 'L(moJCz<':hR!4ϟɿ|[g}"${a8ȕu3uIOsg0Gь&Vb"!2YYfZSj2؊8}yʹ&͌KR=L jCs':Mm0G!ϯ߈Ljg݉z6bs̬"2˽sE8@& s!l}{ fSl){SOn3dT_|8sYك5#^)^wWu5ZWOCÝ/w8UM/;kϷy,9wq6>8 Nƪp3u.ѷ],&,-LV7UL D`6-I&Em7Tg,/'lw+ &lY#EwNjYdK=/]mQN9$<2Zn)> ãc:fdRLhCLC6$GZ'Ik /\|ְ.t}unٮ3."\uev~u6+/A9{Nz׋k>ћ^D76BLGBqx%q+R8;2:n93óctw@Kg\({\u0sI!}rQ&dYV%7D3FʜV '6Qf/AI;p9h:cGɏC狳4ݣFL|omgSaKt>" XM3ZOpJA)\tItkx]at5/.17%q'|!SFʢ79 5,Ӹe9F0AS'ާfI2s+lqЬ~0}w[R1ae8ըetj]d8/pd < \G_xU D|Œh/( |=$<4{ Y v1Ll,I i rښMٳB@$IVr!!G:+IA'%n\Ƿtp:>iT}e=ŔTf 9̓9<jn̋ oٍBM9v +a,TQhzCCjo .?]Wkt% xc"?zUY|~_7{.? 'v˵Z&"h^[ҝBt=r{uƬVK {;elřvӕw;W_&\*WEvDnfo:Q ;}dxgXyGBz/7݄Hl,C2>_疹~ K+!_3ȅ cwFٺ7| t", a~ R(*!&=fD[AaZ0~+($SŐEka d@߇@eŬ`zY]Kp"#D>u|' cA -/b[L8S,/a2۳dydHVp'<΀1.b"n EaJ,6+2l 2uOűe(ţzz~9o]~{O_ͯK߾ F HIŽ( i%Mo* k</3ɽ[ȉ*Ygxp7'< U*"?Ax|/wpɷaHxݼfG~=K} j P+!,u!)81m9+tm`Yd 8zֳ" G ȚKHڄ!ŝu#4A"N%ln$p̘ |KH N%GҖem>priqVV _dBV(O#6E"f]S*nDHQVL<;3;Ǟy]ɢ ]:4_tbs:yM cKB mQ+rʼ {-pdeڤD#tu>ύ:)N nX pqrǡĜ]9ȱiprF N1!UaO]_FW  ,RJz3RyeiKG-ݑIo`~ZaoUzQeV'3yU srk2.׬ח[?_ ǝj8u$P]ծYJ|Nq%Rf xvĊo_?f~xw3 3uw&U[w /ؠ&-pm`pF?:⿃IqY$ < !N. bA#rb(8eqiOL̈́rs9zRG\+gPD EO ~1Riuh+O&OLk /^;eY&#hx|2:^YNڔ@6NwN}qGjo_3_M}=)n'MqIVJ3U AC!x]1Al!QBv4BX E7%Y`iǺr"ITgܫ+|ji,Ŝ6Tǃ^j-$4S E[9k=reU"T pB쾿 X؆?@)rչ;^!0LT[%8 PrZ0a ҵ!C(EKg=^{+WevO{F8;Q[5& (CQB +H&.  $yR| NL%ivD;Pw&,5#a[#SdFN =N]J %x\(@6S&`&m g>9yJ,֚B#ͱ׳Ԍ{&(k<͇34O4mNޜ*/b JIϝBBt>U9%)~ަ{/?ۖؕk};{snBF`ʔ S2iX-@p&tѤE -WVޓ}̌|b>s9|.`N?nF;iy)i_FXâX0,Sp džv)\QEk$aU![K^CT`Ǧ 8 !dv{}I{NG %QPp¯mG)#9f;#yͫLJ>k~Z^zQϠd]3;ˌG6'S&;DXݶwPV`toYI4tdq1kM 7鈲Jnp]؏25UlNiku莖}. )ZL b.hQ"{wfM5ʋttBN{[@%Gee-* 2rIfJ;RS>)"HHvUQWˌt̀+|nrIܮNڐ̠6].uK.,j.- C9~d:qP~1o*ox{3f>ˑ4͖d35B:Nv&}{Th,|1o0+gP&ʼn^Bj0AaPŜ/,|^Fj,pHS9l9Ϝ4ɕ]K`e+Ţ]jQjp-;ear7Xwb>pS(Y81&a$$oE ]27Eӻك0b!5?Ҷ€cIdٌ%ŕ1-Ap?=#5cvua;OyY8)d&uE%wZryH,ǫ  s)ѯ8,%rp/㟥K;+>_0M3ͩihuisJR;M抙HV* ],=շx`ٜlǮ= _O )EG3r¢1RS]Wj;U-׺"3{H\hWuF !,+ejN0Zw-Ӈ?yUeHf'^U CY7"'`k¢}zc4>8glRHr,p3a ՠDҼ.; //xX60^ԔjO䑃yIβ2^ZX.>ﮕ,Q 8]rty)ֿg3v\kf~ O% Γ_)i.-z+1Ya5}24"p>F l$Y  1)w:;$%9]0(XרTλp 㔹*F,2r͎aB6-~BUxU5N`*ޠ? Dv}/ɲ)dSJ@``F0[k^-Z9SUh\Yߢ*'ym 9iJAf^ kEFf¹(H{?-x*s9Ѯ:3{I/fT}3hgo}/~p~놻?f0␉ҎF<ƻuYv-@wTM,[Pe\!Q- $z [>Tjrt Ǎ&Zž)wR$H:af)#nzѝ2W+*$.)˒]ȼٲCrF橻e8m)`j>6='2߶vb0OQ"EUZI^eyDf?ŷr2;b6tE%rS_1ju/1ZPX`;pxv Q`Fo dx ~F'g˔e^!gtxF`H(B>^^CQh_:Y(LOn'DdPsmfZk/} 52F]$)sihIm!3;}Ot`- @(SNnȐ"tqmd]vӲdF)۠%K!bÔ$a&ɩ`m3_$;Hʗ;;eƣ>׌ӹ-ƘgXo@?_߼'G +;fRP%#''Uz^Hx]{{ /iwg`:feEYt.WbǗH*:t")+ET[RN}@%,V/unKCT{>`~XFHAȸ!55L 9ZOQ`5}g\ 9xe -``"V{f?c4f}ضk+Y< F0N\6kKiY ,Ţ;D+KUNṭ>|5OɈjC'>S6d2lt*8w&,UK_}) lMKpmVyx;kt_+#`Z iEe8&=>=ʎ K@Ϯ#Yw˱!,β9W}F\ӻSE?d \,Sly'r5b)gxJ!d`~riVܞ*$Rv8p3 ~U۽5[Yݻwpx7Ucj3 cAN0uY0XE:DRdE 6YY^N}FU5* \%)ta%? f!Bק}ؗT:w]Jzzx\t3Of3{Hm(bҖІ]%X2d8L\s(B(9z:IJk\Wu}W*%ahkE5@5,a+CG~[@ŤreLk1*TJ"6$oo^ջOn>ʷ}')A"v ?ѩ}VAzgfW)٫JF}2];ΐ}f2-Z0MFa2̄ sb O:^%dõ_/lgy;%;@4#¶S& t O'u =ZNHT%3"RCgA6l1r˸TzoFmy|> D2c19W:I@ݼ|g=>B'O5ۜ¨q$~rdt2Ŵ,!¼_:XrT%YmQA:n\2ů7?ͬbÔSK&Ο>t:vbVqX&qEI-+xl0TIdf6.Ԙ[rwQ2d_.LEnᆥv瞲u&7ThnvkC8Ǽ{!{O=$N s'W %trή+sv\ <䑭V,`]_DgZ3y Zlf8ǰ|1+y%LI_w˺4.Klٜ$R %@.%&c(2EQ {礘5YXR{Y ygxjdd̥7OO岒oY,GmRwC p]e%hCW}!?gxu^yˎ )[[khבw1^).\PHZ%,1j`rŝ'q9j,`%O U\SS=aݹX(~]^Y˥Bj\[[F !eR#V-|e/u̳Zm`nՁ*!lbfKo#1S0E˰n,)],lƦ0.c ~ZkNr ­E ۴il."Ht_y3#y)Op a4 y!;VW\93KfQ}5v^Tw|Sko{tdz(*a;سi%ts!Y9맏3LG*"v,=_<lmfmNY{EGߜ[9Sh1)&y޻H,j!# Ie@a10I!mbYv]d]e`æwxmȍmDWVi.t-v]z]ErFJ.`Y;4oDgo'[ЅW"| yK{B@;p5n++j juXZ)kpI>U5}mcf_WB0_nas=Ya|z|կz޾goAHY;zL1\!^:>VCG 2ua1aq5cʀN1?gJb)9yS[\Z!AgeWd\Ŭxi- }^kǘ9=Osvm*>.KBvG1XVvi-E 63#N*ҷ:CK.c3GK-*' \WyGA^8fo$]xy߮>YJ G&̄I~eT^}g3b\ Zj@K'aG!J͍8Ix9/d)/E]R퐘SǽfѶO.f} &Rg!֗>f-Y a,\pYt~s0 E Ɔ󲬝W d) oL]-]yDRN[ɐd 9_;p6 f|(ʟ́3.+?)rf/~.eY6NBP/pz/6dɂd(Kv"pOc&RM3ه8#}q$}*1/qd:塯=,. eT&@ds6cEpp \/E,dB w 4(Qt-,ڑpa[@`wE&ʄtK|";ZGFfT\cCn,UeZeH_kPnA XV|*!Sj @Y1{*]Er2ft2ڵ=vМ;@3Y KPԺ(}vuLt^\uǼFo=8wD<7{F/yN#NT@u e3FQ*e>}ίf}?I2YȦY F-Fnlqw᤼`ogӚN)`XDMW_< eC?PDSy44m~d%Z5Xղ-MՐ*ay@^uXCnWND1H1}]}g |hyvcȵSHetljJ$_8Uabx$z{3tEkSlA",vQ#t,jWwgݭ‡fNd80εGt ʲe9 \ k)MiH<}f8}ҙ7/Vm1 /'l/ۑɜ:߻_3"wBX('t[z[@Bo3)dґqϒjfgf`zY6-}ʼ{|z.j<6k@}٧+@uW7bG3T8„^7%ڰu&1Kt^l%eZ,C1=RF@=dQBL̻.-taf|i`;dZ}ùq2Wt2ή%YB .gF!K?>Wo޾ؗ4L[r/Ѩ6r̒{`2&}6/VVzfAt};%LBr3+S,GNɿD͗~. 2qZ.G'>JZIOLǮ@{~\CO21ycӳާi (]~LZjH\4tSDOWn܀adM BW^9"$z)I_^Y12R:"y:[Tс20V%D`^*Z2x3Z&] ]j.;~Kq'$܈law]E#@U<-rQwz衱@ r^ȼU_evSKg^*vyu+`$we\G \J4;)IRa ޹}foq26 B특^˟yTY_L/L=]ca׍LrŒEQ[,b쌺Hח#B5uD~@ 2IsNˉ=y׮)y0dcLX_._Sz?1h|rǝQۃU0rvu?hx8]'CH}BMn[e]6?6»8,f͋Ai@ 50.ڹ(d:;RԦZyMb)ÈЅ`X(=D(ILk뜉l?%2Z!ړ764Tآg9ۖ (2+qۓymHb'xA)e!EfZX#;"\Nșٵ^Z苚"ݺDz L>=c3H ²ylsrDۗ'͏Oo<}smkF%IFRmV3Y 5z^rkqmC{Кv=5o evgoqUpJ JLM)Ck`jm&ek_>Ymy 鹿Ga/$ý[i4L2/KJݛr/M;e=BV3̇Sw_ʔ:Q‡toQjdQwGX-Y7", eN75d0 ˖y m4sK.7CEMJ}0Erv|]e?$SP6ekJKN,j`-Jvz׺ @=6[VŜ,)K#$# :GDc`7Qljd0-vk`zTtw,0va#0lyK)Z$8}xBP[W=fugm|CPD]5]RⰉY} KGxr/[׫>KkvμsҹF-o畑3l6BM9L!CNGT|Q. ~:,,DI7zv^R3^S"]:\E(gTFS 7*VX> $!|p`,Fd,>²ucWf@]2uPSf>.{;WB24n2(ɸ}<]ʺyӥVA}0d$Y(.b}xQmt쐳3ɖmypz|ǟ_?<==|}ʫCw c)x$Kʲ~gpHoz aY8)k!>,5.%3aFۏ7Ն^Ng׵y#7on*[l+uSK؏,v:.*ʞm>{_Ppv`rldSe8߷r"fs6yI{U ,{xI;-I % 55op)]XIg&4^$Bx.R-XfYd W@IL3}jbůo,Z"l_keY(gf? C)!~!KXӝwv Z|k6Qk冰(RHGL Kמ#0?s r\_!>)HutfTgH\q}{CzͲe<ʢ(/v *L,Q,Ի" ˬvHcd$T-?ҨkkM_r,.pF eX^6jr;՘VsM!\&+$v>}kk-#| Y8wYOȔ $kX(W'=0Z N9.,n_!m,H$u)l6X6NwƸ/_>|x{ObmMe0D}s|Krϖ$_rv}AkVD/[(oIJhS0ݙ/Xˢ\-n O.tʊs\˅P;bxuJ%-c1Wٶ[GbLIt&j5.fגy}]-/aiA^εv]Yc *snْQ zpQ`B@\u83],#$ F2`jMʎ^E5/j.]aX%`bJqr uY@io?}ؾ>>zomƺbHeLOMnf69ԮվZ/J7ٳ=oj0f6٧OX/F5 ݜvځ՗O?7on҈>$hL,ZbA {UBp|t#%fk5t@.#gt7g4{i[LOO@; LIH*Чg!v{<\9;$En|IӖ!eTvxuXGO?]~gDG}k\ƞg `3[j뛽Hvt?6i@}%_R3/^Jx庄dRlOЫL".|s fsFG7/\)w۩քK0j)d ҰOOƂ9o%߷+" %rkCoBwt!bb\GSHClO5~ˋtB> ,pYhjJX;ojO>bw,ɐ-含AEVDPӾ,NY*GN+EaR`ʎkǞsP"9]+wiwQןRoܚ=# 擵cKط`dX 5,\S҉p0䊷sGf,vvnzʻVrE=Ǩ;;}xV8XluBaY\,3&wj̆+C&<[?>{$ 4O +iՐEj5bw㪕z-zN D/f%S7ቮ)KEхL t%LIf'[bx5є1,pY~|lYes`#2=jJ*Nz_w=:=d1LhK̵DyƝD:n'V_8 a^eH6&s~a``ưT* Ё6{5RX<.vʶ&H1HsA}_֕{q\n( {zXH iH[%={̢=@_ PG;%zcmǙ#0Zzyq#<[>Lnݤv*"R2 !VW=]\$5G~ρyq#gNtӎD֝Ɯ t?Ңzɑ4 0aQk{=_/ɏ =lgl4U>N _.dͺ$|Oj/^_'!LyjZz}.Ѹ+FcFxXb Km!Q}U!7IEٞ[)l64Biq̋.:y4T\2d?EF6ئ~2}0"|"y.BuJ"CR-qұ3S'9UGFMYȍ}K;]Fȉbb[3; 8@CZ=5-픲UhBą)aY. H%۵x4ʀm\v2i;ao +3D>F ;k֘2L#i Np6ݵ/YRe'y }̄eL!6voAnɕ]p?{߼ݯvO{lPwq\O3nRFz#3[w몪gf׸ve,MnM}hhF c)/y䥒j` okNR)IddjW i@a9sʛWyʡB';$ iHeP\DٵHJ%ɱo儡lbWs:aوsfg(g>>'_~\ʊY=cd9U.5@# +| }.Ķ0&Ӑ7l`{zkƀ ˟YIJп;Qf3Gv1YͶ\JgQw8!r%Q7o_2{]1[xk`9jC.p<M/5խ>c҉SF޳CB*<*3 ATs7&,\kvX)ksQBFW'JzSMT;4(Q+4(s>*1gs~UXa$SGoT5@wngrMGpjǓ>f6FV&"e`o Y& `3#Ix(դ{ L]i,o7䏖=巺f+_J~׋d/EvOIWX $~_x, oN43nVي> Ps1w'-L.#?K„'vN@dNnIT~dsUմ9'Lf;Zz !M {vF若uAaӛBش6أbv:AòDd^ moi+iyޞ,.vQ;cg,ocDF`4#YyZhT^uH?g|n'l%t9g%`oLQ=0v_q;z5j5?}`Jֵ˝r2* 8"<^/oi*̎̆ ]*O[=ۭq튤N.]\_E;3}{j ?3Dlz3\]S΃Ma;.sKJg֚ o"EVMJoS,;6Uk[Z&ߌkݶde4I;0G 9u.*6E[LwtzM ľHEw-=fekt  RrJ.ֳid$26e2dmtuIMos7sr1V'%\BxY6cI٤}mY|Bz12?4&ǏS}[@NJ,7P[~Lffrr0)&sњ%m𛷔xۆI!@YYH^>pC#SK^ϙ'ey >'=վ~p jMjwĹ!&@ I6K[t C>~Xksf)2׻S4W&1$.CPP_0Lc~Kܽ(Ԝ2OZpl}^{ԪmɸHPX`2sd}v!fs2*/dm"/Ƙtsp]3-!G\|9f7J٦9k<dԥ6U=WFxj&w%[,S:ZK4/dvF:%B^9tϐ!lm$+U\±N9+芚2-aFDa0SX}6,WJG " jKvx(iC*}~8vTɻgմ̂Zy!!kiVnN:I93@[+g~~`$Ug/c13j7BVIhF< Iq.;lyUK'2|.ɺbK@KXȨ. ֯H[Dߕ+ϥ4 $Rxj<N`]eg\+jWC.D7k犲?kC`0VȪl*~;K#t.FkaÇ3ה%K-5} k)6/E IH=Ol6֕05m޽}x|?>7?쁦tudWg; eiHo.&+<rnYz+˙$8;kptO&-_98 2|!Y xc2unX1"ʠ Z/+YQrYq[.4l_e|)n嚽 7}L.YQH݇LxtGx}3#y9b]1kM.ps~ƍ[${It{-Nw8zJܳA д Y4ǷO!4Ck5?߄K,5er-jgiwѕ l;@2#Y1ڝu'쉠l _kwJ^ZmZ!Z;LL9+NrEIw/e&˵ΙvmJ0-0 ]e㢜5¯! 92!]K7:m̹,Ƅ?kwg~hYlMQX =vB09<4{x)K]D_S>7j3,3N"մ?/?~{eoӉZOJ(ǧ#eH>`t)t DFQlRJp\t-}0&p,Cs @H+l& ?bg}_+؜J+rBok &0-a9lZ]ƲfD{Ej¨ƿ)&"|CN!cGq"{ε-o1dJ=xغQ+r`Qyiv>Y1Zlo.SXh)e+uV|긟btfΦb NtS[3,hM־ cb/revQjjH!t2Xbg?3e!ZB.v d#d:Ng二eOFXN2vq0x:i , #ȱ ,fF$n]R<O|+;0fESp OTІɜ\[MWXO4$aɕE0f'5 ݃[ %, XP@#%0i-ƉP; 3ԱP|ҐKt' y/l t6tQm hYRO.\I!WeC;k, $vAݥ=Y P킁9Z ]˨%c2`Z2ck-;ff.]\aYjJ?|ƌ/JK6Jvq:|;)@VɌ]>!.vJn)W2$e.QDkqZ2Ȓov~?w0x6|\{"=>Jphӄ^fɃkNg:kȯ53s/+F6ʲ'i^b+ t-lAR̽!G%,d fv2.ױ!-.\j \Hy'sx K)THL7'ܕ} g\mt$`.X _%uk_ ]*kM;#ٸ=0bt ,Zlq.bgHy?.6%#Y Eۨo(r=Pw 7?==>7?9|Z]c@u.ޓwv5oQ{vV% &~'ZF.T[ يPNtxQaS?eBfzzzYuLnQy_7K-:(ZI8/LĕJR5vhY@[eCxl&`)Hx0=w{c]. ,/ 0'oj6۹,,:x/lKk[F_}l1y:^ܙIǿQ1B:wTb4\rIb΅iӝb< ۂa]Hn#ӤLa 60(AWVڹbE. ːk)+$1VHlWT6ז(any~%L=R>(ea脝Έ2yÜsn%u]$猽yk:V&܏|o&)NM|ɒRF?[.K6׆$Ln%s4c"[2WF-#(vJ%450,\}50Ȝ2J#F=yIQj8R;hr5or.et,dUdۤ,s{d[Ly -'8']/({ !N3AjU+bD$p%=?i )4:(GYa$2}`ȘZF7"/̨݈ riͱFP6Q,d\GV,khTBdd oGo۽R@.V{-᥯4,/voc咒pKYoP`O!C|O;NkM{ ,zfkrvPڣ2V 31L]v~9}w1 ޯ?׾Dβ]b0.6"倳`Nr g$VX0e `2 ׼BHz %<i*-H6%{ uQT f@na̞h.eCN)*%ig K"7,XwѲl>)>eZ'J`0eM5"\5Y.wr> Lf%/[+_< |~o_5F'vI[]Urgn3M7XXL+W$灤5՗$ya !zLfp1[(> œܵfO\_3i#{qm`T>'FSj`AT&vl.0Y԰ى6fZz  q񖔲[`2ZYk8fzT^5otaυ1q`"`ctf0G|=tWQHm&yĚ(p6Unj1h3p&tn/3j! 9! ;L2jyuȌe!,2]SZQ/RVKP9Zj2}Avju Ȏ\bOt$7Z/e +(BKe\;X&r\1) -jeI>5#<0Ty  jۯm ߖ&n[t-ЬSja'WP\~+H^{4/V\1ܰuRd(1rIijʐ*"jբ% 6gsz@ ۷g gNY{%ͭY]_eL,S ~zC(GsSr\H~vv !YzcGɐ),t El"^'as0 NbѝQ{HM(IsK&t[tesx`FI3Q xb! Y \Xvfjqg{92ƘWPE|'7^{~gsH̓@;9kPgF'\ڬ ǏyjDb՛w=1o "pk0l.z^?VK3;9[3M7Z!jN y)P`>01^.)'3Rn|e Z.$Ӂ+^D^fO(P_%*:g`+23p']\w G1'd ިLr7A!,\qVF 0T,/e?!n´> .1 ]QPew2eCO2' $ !Ҫ^}y~|ݏ_WOo^q*O.1lզG%VCǞسRc͖lxm ,IT\#8ɭDzMlNo͂H늹ڿԤ7_3J?篁 O`eٗzR\2<9:,z-0bI˨˕ 6LskP8af,(%rsB_ 2W V}ɍEa9kOe]>sBZO<]ޔUfzcRF&?eZ9{?'yB%o5h&Iyb^49Y $Ffƛ,> J˸( uN20[ȕr屢$9Kvcaw |z%]_ǻ(.E΢՝]a!`Zbayx'\.d c$,ymjS.DcL Toegr~hҳf"Wp)meø>|]laƙ;8B(W߶9V2(0Lʓ5_Y rVH 9eu)0^ضH蒈]\H[~1Ξ"bK˾]NGNqgH˜ i&mT3@#[0@Ƭe9] +]_y]ftSͫ?r{}~'+Q/YZ*7ǐBԯ%B݇t$ a%2 1ۛ(#L=a4ǰ.J(~Um7V+ \Rp3ZQ=cOj~bga%ꡦ{&GQݑd'Wص(]ByVm JVQN$}qa ce.g;F_[΀ﯼ)_?9qP? Q\r7!BԊOwd~p?,& 92]hA3ܙIJaFb)"g,|v $\W%`@7$+sbIUYf 3'x]?5}<ʘ3'h\wE' *w "f7ײfRUI/s*2fzԡDmK_l!cIXskRl\,NSRU nH0;kHYqTj_\wDY̲Xt_&`C0KsBuG!;@u ]QpQf#;) QJdX;Hn=?pF!Ζ)I9a9D>D]&ebroU)i~? {NQN"GxC\jf,Ѩ$qe #Z"Xt`EN K]뫂6tΟ]oex(xt7rK> aC}ݺ0nB.HB/kpy8ϑ8Vw\֙%<;_nw5-6SncxF 듣՞Ctj5j9'z-)?43[>}n603J .e @ $zr)j%2;T)0,(sE 3 F\"`_; 4%]}`#c]Krtc^sK&a 0└9V;"& 9)9g`W6aLe&3TW}!Is SRo (EgQoc_ a/[e@Md,9qQyod:l$ЦkPumy vFal!'$JBԛ JHs=0gh TW}3V&:7(!69o, z~gix! V X:$(`Qax]h2vRatz)wS4 DUƯ}9g["zr&N4]1uM[@ndz a"ࣳX~vH"ʹ\n M;5Y}/1 59`9'|Rysi *H ӵpmɚpnZ8]w8F!?钥r&m-?jPJsyRI \s6 1'\޵$p~_;vv_)yi*Ƚșyf]% 0,IX;0- zB~-AXi{({n}6wƸ%#=f(jR|I5|l KNo<:yYdub3ZHU50EQf< mf ‰dzB7* ײ2= eMRf?%2/ׄ]w\g?uW8Gȥ%=J1ە2tYm@rM>}[pIEqcT0"pnhZ>rab#;>sV{/6X} sv ߅w3 댽iQbw-t;: `WQ2jglKFk3 N)/W|o޾ }PJ LS!|-ڗҥy ))bT@7KdQ,p2Z!ۘa J] vqEr{y^?]p:}VM pJQUx'M҇sk[Ϣ[x`Z]HK,dFxETJWlRldxs}D6v@xMo9yƿڎ\ZpOi&Lnt:y/K,6s)HffϷAB&;Wv=Ng{),3vFI0W {ƀe6r=c8^aՌʾ4g 鮂uO;41> =+8-;% )媽LLDgf\ىT,/zգ L{SOw$2ƕ2 ?!41Day}1I:X ^Afo#ǧM|14|вp.G)HzDƸX(dq;a"r'!K(z_+8˪ΨKs KJwB?x]8),٨dk4:ASdIaǫD SK ̑F_n]Y,+ HnqrQ7Ҁz͕q;J.q#-CﳃMLpz0/?B.Ul|(x]Xf^.vm{f Rl^6i`M s3]UB6!}]QBfid. 5*Ds :g]-]1?Ec;N3_ں_6&+%T,-WFUY88\3[M5srZT(MIV\󆶄kky a^ˆmxd˱22#`7@H/YDi2:1л CN$?az 2%UF&OU$]V퐊ݤ!2.ZBX<`Z0oXM>$3jOw:V,ȉttX`@!CADl j+Z_}ݛ_~Ûo.yE\?Y$sq]YJ]|]}\1Ox(u.Yw nge;$|0z9?5 +LЕ1$])n1ɲO''N',0]2NX*WXy'V)0bܰKV#l.el:N'/I,<cr5,E@8ӳ{Y'?}W07ϔ>%K["_oY䱴T|9=uS-gޟXve_N=P)?#\a藘U0eDYmĖB\,)?BLg'Sf kk/o#23Zݒ_0 Sfp~པG& H2%ۼȨT]EXbbYt{~O ~-1Udu\^ͫ_ӛ\,?UYk2.:.J}⚴S-nrP߀OY8ex8-RfttwYھїH(0Y"dLFR*ISCl?D0xëe2.,iϨ.K8e\`2]hȂ?ߖܳ gW1[c 0et^MIHx_w5!1Nx.1? } .}B'eD_Ȍte4>vwoy luǶJyRlW^2_T#QˌFG(N6jTP. xw8l˵v8a;KXwB1 d!*x͌)Lyߌ(K%ʯ3Y"ڗrҝ%tC>Oź ,e y"c">[bE2R%p0B+!Q"Ym2Vd:9rE8vkv`D% }Qw-(ZQCR) ό%ʸ_vMOmFYTgR,U{8k9CW9w@WtÜتn;/@v:EgNcehv7dY‡|L~u1>` Ae @3O bpӉpKH$`6vK2픆f蹒fI,bġҗ"`$3[pǼ.p!7cdQ槿V=)O@ ȫ}H7>̴u=C& .{`A(0T cQ$yY&J[/zm?F1@(!j3n/ QWׅIjK7mxEFsqxtzFF03N8\Z.dob W p.;"+OwDC?,ګtDW`B^kƦt3N$Ƒ@II0ݺ b&lP76IN|a.092dGBʡb,>τR.E?1I*ode٦0T߳k}={rL|!76T9{_u9 b9$1xRLje mc .%9J=㼁@ &&HDla24"_wݳZ](g}ݵ UvQ҈K3t]]k}VsgQV/ kUq-.x2Z2 h de 9YQ`o7WhQMoPGg'/߳dٟ? F~ ȻM,t-}!pl w~?<׿%K):WY koa[ L>8:]o,Z6zjȄ@ )8 pf[X*5.ȸRPlqĔi~> UI-Aeݐ/-$: g%SsIz^S3Fq 5 Rw^B-A-x<t⃵ p Wd-[,s7i9dm)7HQWm Jh4/m'Pw=ٝfc߀9L|`Ʃ=0}SpPs97fT1綳mhc6dݬlXdjx+B>Cs]ˤHwOU"W3jXB9,淁S %s͢E=6=U蝵6{-[OB/;QC^ !eE{J͵8<2iߏ=[1 -~l]{-%|0򒄿=z; 1-M)mlRh=e~EI‘s3AyJU?~<9d )vχWB.7]%W%1)O7pG+5Ɵx }gs2|;JJQ3.d ]&-5s>*/ۛnlH=x˖ D9 ȹ"^vJקwT0FnPng@N&YOfۜ)d*SUlڲ}5D8K{`(HUr)]!3Y{D^Ok*T}+~')>m-\ d.e'˔fN8˨d7.2~jMa:Y8X?0??{Rη~@?wt%ٛO;GmGOWG]: r/) OiǾ" }[SsW.oTk,0;ctdcmw.~X `s,]jHxgsU$q5/SO'g$#=R p-_rD2':vY[K BL.,b% ?U`Ay)쫇.Pe=dT^֬cRҙ{m[{\nkNaQ3d.3^g9O(ȐӝҎg[a1 wH-|\ :#1(pDݳ:E'G#ǹ#]sƲvO6c_~ZSB͊,0pP%kX$3h ߾RFr;vEKx*45ηW߶|&V< CP󄝅v=Jƞ%(67͓7]c} U҅g({zRŬ-ڜ¹MvmƎx*Az@vLˇ(ynaw*`<>=FRF$w?W OȽ`Lw| Avݳ`CƑ>2i %EK'VX}(' K8Wg cP1HF  v`K^.p k̈ju 6F{2cKQO[ oxbnH6RWV6Ju,a%&4i-40,Vg !=;a B]K/`okJg{YZ]FBX6g(DE pN?o X%"tHѦ#,eDG)UQ ̙{h u%|#Lnr2}[;aYS3=F#Sqޗ.s_ &]Ml2`fN\Dv^疀npx./ hX9|ܨ. ]tQY$ ~"2l}j*zޓsd#6)|Ǿ} f߿+X_*_a YKBq$:sX,!Ov\Ǧk$Ex씹;ON29gAݘ{6_6>;O6 )hC^ QeyFG(d:}]@,SF{rj@(! yN)wqN?^"K];2-o-%nt ]8e$=z7כ$#%æ.J];%]a`tF-YwFegҝjumu[g{ﺣh'KbI;Q+H2:;0tP0w}+ovK6dT^9VJSE(*\|Ψ; Ny7]'y@_C28Nj^G{]i6'9}U8<) "$13M¸ !bsƒŮaT0`%.̸RSaNF2ˆa$,R&x)cd@W=B3jt~\]:[5ڕ+ f&; ] &y0mC|}I?o᠌1?}x;_կ/4B*RrFRtCrUs, 9Cf:A_m1!yM;I}9\ h`vH 3<#v.]5;]3UX,_/ܙ%ǝeDw$g *<ˢ;/Y6 e] =,O'#ص3f+ccҏ3~bXr~1e.;u bdwݻ񥮀Q50 !u_bfўB?2)svYUXg7cf(3N]^u#ͫo^} @'+,=T;WUT٠) X5>e]`ؖh8*s)pa =jC# g܎`ǂ{j5ؐ΍Ib\˾Q{2HK!StcؗWP%4#)c[{83i#!m~ox_xS:/LE0w`Ni K r 6\}>EBzq?G?hJ0g+W?I/;~c_8|buhQ!nr2JHxiY`5˟eN,+=t5%!HbMa_a,HʲkY5@i%tH0aҹ(d)?\VUɌDwbL+l>#W LB }KTH~DHVO趼`ݙ!$ec<3Eq8=rї x:504U^ n Ma7uk"P6r#̃V~ZawE}I N݃'\;P2[e W`PEV-b'4^:00ѵa%#}"j5SNy)pɢFEYar钍k:&,k'+uT-<ŽA˸%`FKʔ^ʮ-/JE _Vu8J?vNs5wc>j CWj|ΆRPOyh1_,{9iҊ;Ȝb͇ԩW%([a6ӑf[HG0VkdӃφz+%l&8z ̻!kfluST")0Z#Bەq3[ƿx2f^=(ӯ ;# ]SƩxOYlgԌDryl.BfVʍHHW`sS&646:kj{9sӗ!ѯ_^o|;oz~~t$~I%dlW̹҉B uOue.K|g?d>_'P<D]wl*N9@.B jU sVܥ*dA`s'sJ++2K]y(Da} f]"<۾`V#E޳K),W\ץ}Bgz2K*IC2dZtJ:u.7BxhPv+k\ Lh]z?J-,M[ۅ'y~/~8 or섋ITaFUJ#j=Bd= {ÌD%oBejh2{vPvE'"|tQ6ݫ U M~1*NER:&DX#Tb!vN9a0;$_LsGA'\;h_P&' 2v {- xFGKt0$f%)`yN"7ޔ4rڳ0%6hu)'NqZfߓnxƦt _Ⱥ2>@X:f+{]-!Cx$ZW{ꡏ`˘T2Galj3 K-9t\0kE{쫟 r]m>wʝ랜K,-?_|ţ=SC~W6Wr)3ޣI\6RO k{\2[,5v~quo)㓋1zYK#b2 p/|ɆG"uW)0I߄ ҟκl&/}#64  "uOxcp5t BLf۠2#Y%ᡯeYtgx[DX2.,S'4}lB`t8C&ӽy֥SúCٵ XS axO,}Byג@@gR52b}O=%ŵatypzz/?sͯ ϱ$A X(AΏ冯*:/|Td c4M?&Q-e2&Z^,`zֲ챼53KlaNqbv<>=>Ic+癛 xYOμHIf p-;Mx7CEٚ@N"/u2},Q`[Y\_.,ǑK 7r[ NʳqS\?Q Er<<v|_>Ea!@SڟC\T~sI"¢]f:0ljf\+7n@Y&U"gϹ(d$ňNp0"D#'sw,Z0ፈZ vgEdr]t~< 3Ȳ:\><2tĸɌt![u'jG].f<0ݱySbtCQFP }.ř]92ujŝ$ByjcGN??㫿_zS(8\U#B= Z[HRm#E+DF.E;[tKi:$#\%,t6ץD9?}5dݟb 3]tt2L=ք- {+ oOўkWF X0,]On4nN^ $χn'u[=FzQcH Us&!N6v^rq(*)r%8/5UYU!&ϪRO~j^yr8CǮI|}mΎ8W>u\v S[wF"EVFN1cgkEoGLtlOJdQ8s-29sؼd;{#.r eYu0nꮒaP%wws5ec^q E@HxRptkɌ{~3)H2aзb4뵀MYTir/w$}oQ0P7IYE&JڴP~D69%U'RG;ˆ9;VPY`eraâ!`dYG# e$ˆ{Ed0ePV 0\\ҹ)ɕ(&N+`\tp, nhoײ2/er2ڗf*"pq nw)/J:] 0)2u\w>vʐȢKl^-˒5 vEQ9^bl&\d׽  tɔwdr 6hQ0ׇ?ӧ l߾އӧNOOW%i2ȓ~ȿ|?ro#?m߫Pt#ewM)ks sQ6_"I&ǏDžCbk$9BmSAq`zW@F\SڂGL,,Zl)qCJ`9foS`duNގlrn\ }:q++G! ~NQئn^.y7.3.13ZQC̵6!]7Cw0RfihbwL IQ@^$ /,vIגR~\_~OͻLJ&bx/%ZW_9EYN%lv),򃎆l8 ¸oh0Otε.~B@UF"d԰D1lyʦ]kWI=},j+<evFDAm%'ﮑS{\`gλ.==`;:}Ѿ8Q>@|wƷF'#sǟUB!e$,/)5Wm3MS˓_\/&ΒڇJ Ft 9^[,ђD L|] C2v2O~SV}S6O?7knVS6rF AK`Vgl Hqd x:au(r0X"Fc 3*_"H-eh6>V *7'7.KfdJW[\ŰJYpBvI2c3V'j7 T 0e׸)u}}}Qx!Ct՞$_  ,*ѲS&C2kX`׿ޖHwl8 eh0Y_VK|b2#<}.)]d_߾OK1hZ.#>f2fUt}}˪ ۈ2%IM|oq OOoϙom؛q,ft5Nr.6kRS\0&pE!uՎ!ə.e3F"3GO !j5h%nF"HC7B ҝ24/ HMoEr˂6y\!LF>ٱTdYV 9u. (l}xEcQKwRlocP/7jSr]Hz7Ig#SV௵`Qn7?ʃYbjE^᠏W T`L` rDO8k`oiQPTйD86Ö 3 S/Ը d0/"p2\-Ǹ2Z^֐WX\ }gؽ6Av /Gd[s\ q0Xr-?dS?㇏|}'v6) .G0ٶgXdĩKiy_ͷ_:i?䝒ZY5F֚>o7jW.|H8ջ*nzypZ.K0k?Ͽu2̺뮰"Sk.IB&ʴ \U)cN 碃-d]2.M'y  S},b(Ï?c#cWZE::qfkE]6rͨ#҅,,0 !08eDb#5YNw<3ΫJJ ?Qq $l ?? Ex{# |~`ƃ0d>1NariUBf<^]Y6k ξ( $gy8/kï1 մKۜE_VmL? yÇgvqTӠm+s%Oϟ>}Ϻ81Of|`.NyLS\!\m~UaӜYm}ƥoEտcT6%]s)c%3_?e<s;,HlmCu-jD7뒶M\Ӻʃ}Jr`Ev0j~uq.P K`|L7N'fSͲ9!S t INbϸe_N=^-(q2}j|Ϋ uZQuח/܎ڌܰ `CXP3 ې5>k~ۇoHYdhAUKp x >.I^iBbt29w*HҼt;0!iߟv8lQK!Nqn!.a@i »1Cۈrd|Rd1Yƭ W"=Jldvr\⾨˗Q[uA] ;45ve)^ꍽcO|y@,V˚nӃ$oL__W_'3j>)%\l7Nsa;r$sN??n9]"?ÉdձdZ3< K r$ jlq靑]24a$#ƞMљVGWOѩsY,6U]AbF5 e/sfRd? IN5mddT ?灜B=g'{_9^ */e rVrѹo3f`r_>7*ԃNJ8vz 0[&ML>葀.j)Iڐ$#Z nR"ENq#i:Xe,uϨ +F=/fFTϐM&Ekϰadw~dFa s UO BI7'=Ѷu +XfJf wVL ߣ]ͫ I:F]`2k~x,t0{WRZ`]^K qdh-rHRyz[p @E=I%d"?`*-K e,02lgA4{gw\wY0r1G?쟷XBW IքEH<êL@g%.v\ԕJ)YY^mW*p;YlZcbN Bry/u/ r̫l $v ܑF0 1 2ftаa7ɼƵU'#U1Ǡb[>,-%;߀(ӺT).3*- M$b;{[N9J:&E9e-*bqQIl`JDHyI\!4qmQ~xp i5nVBjزQ/O/v* K3dW@FcnS.hmƣAXcnmJJ|>:cvuLTL`,4i3D56!{'+KMf/f&x2d ^;=rcS`&1ey1Ƃ2I#_Zp FYS" IN3eʌ螀*[M3!!99.#!S Ncftြj}BE|hҔӃ{!XQ¸!WǗlnO}|y~/rC$@G&@'. ɱȏgJ$)`dVlNO"`t%vB fSK}rF`;gc\*Wc+^ cCMx`mtV2^HS2!tII+UDdGE:tpL"@x1hV%V`.P"Ǣ5ae@Wz)v'5&:nFcݟ0f BnŇRC3,r1trbFC nu0[ϝXI$H2A%ċ$s 88re$n-G 3p4u?0F -x](Ky<"JKVYÚ0зA8EHT"RلZvUD|)!RGu4sڨjG٨pA҉Ɓ*db݊cFq$J[+%V@DZҕũ7R@DP^=QҲd.Jp5)"a-U0 !CRTHK&.d/~?o?]]ZM⼪Xg[r,ƖO\"JL(m;@dnޑdΖ VrV xFf^ 76fJe3zr!^.$WKTE@̺^]=2ӤND\j1 z}}mt].Bxvn $(XM2d{&qZ%f'ryz HNTqnVw/77\P'͏GYRΩtyyuqy&N93h%pa~fՕ868R?ܳd&y_$(Os bPb*6x7LlU)oρtѓy1!epBkΙ? bʰ¿( R{aP&$3} .9߼=e(J2I;23FL9m7J9xozSCs鈼 MiTў&be&څ/b#+T HH:N pЎP]˥ᖪ$-o ><,eyƑv;Ě*xEHTk }Z M-e\$Tfc͊WyO҂ X}G*x:$_:ʒjuj} I@k="efmlMX* ș2g^( T@f]#XIFXB,bE7- aSƹ#XT/1"Z5Ue-,#5ԂiAYUZ(Jt8XD"q%bRm +PSᦔȘ QLlN4% Ii/7,=F[n5,yps;$}X`ޛ'inHdɣK#m4d!Zkm*A yO6@,I7eRbEf l5Hmv*.-VEx2"TҮ;~dz-^tbPI'S-DH9UT( =+ } AICH|JQQx)p izx6投jx|$VkEkX,WI" oAfs&w$@].!yT8gRhquzӥQ"#X7{U Yu%6|K2|a?n)TpXM#\oԢoc%\`)cpN2K4T\ UOϷwݻi{xL˚b4-=(|ݸT:gP5Z"!Oeoz %";7&.Z}ӧOgqJ㏗_|ubۭon |)'ܜO~˟~ϟ./?ܤMtuO˞5l0{?߰2J,aLt8ւjGp2vd2ABMlʯ`z-݆+6*G#@h-M"R+c| @Pp !YGI2Eu)HbnJɻNEg3K_FE$p'3#NSg5nx$AGXP10RJ4)PTyuٺ!JwC  lL{#E/Ҳ2JPb2N=RpZ*\KO ^'L4 (ܲjbҗ= B3`mY fK"W_+,\@J(Pq`>Z:YWS\CBEۖFBVre! +U;jRh2"(W Ċ(k= Op$ Fd˳ ^AH,_UHehZҋB R%.@q(S3 _0TּO6'Jb_*\lZdGTѕЉ=>>8Xp#D0b6w$I3Is@(շkM% vk2b8q%Hf; 2R.zpUP5R1%lQ{Js# Ocu*%tkr>QL},ɬu3iJᶔ}cU&!NX܇}Kf̂JWYmFcpBj(mmlM$bSUYe)-A=FJS'x-($ _R }!1Y0R;t5ejh F{FN/_ÿ7. o[j91O#"6$j5b{xVPlEo pZ<)U8nHe)AF 9;$AF~XN C.GLֲV_{K1N*}>75oK|h M'q- 0g' &_JǁȐ>\9Eדӳ/_ z>;zÓ^\?~xeҭdy@.... w^wU6w2 iIqwڒ$yTYx +ɨb"Bp@UZ"^U%1eV}(V0@L U|.ł/@gtEi7%m(4"#CM` SIΜRbV@cy佈dT]30BKU 9"LSFjKuXi;dQ-x\D2coj@N)#mt-VopjU8־lG6W@ b Mkd~n %R@4V#f; -63ļ)(% BzGxޒMw/Jh,#XEh77G-F5%}5JYʘȉU~ + 5P" ZJtHDT_>B R zRj))-($ت}x0*DʀUkJ/=ޒZ/@T {ŭ[.xTj0;TQQ&_~xg^cZʷJ;zV>ƽ"Aso")+Oxg.HNϰ `_sfHЖ ãꕰafhV5rx$!v@t‚a|`:9Jkkne "bg^rZGp T(!n @j?3lB"U=> \!Y"}3nUH0R\!ʨB^/ V[Be pe% @~ &+WHkrI+g^Hr:cT;=|/!oR=>h;RG;s,":t !V" 1KFuExR0hAf!z 'KGs2 A,cE-=t"BEP"wpLWۇ]G(p\Q^vG6.AB)$v @?}ۆ6r0Bjs:=8|x|;k6,g;7Ϩw7G's 1(] Rɘ<8$xvzc<#:CקG׷6ȔwYɿ"0VV}T-(752{VZ L&S 1)Oo1Q؉@(j_Kl,:~`j #XȖRYȆŋ#U@3/OO켧gg#fʌThP#g [MAEV!PO%;'JuTuO\ٌ8PK$?X=$\@QD\Y6*SIw"`_l߸CXZZ TJW@ EIFY%.EFhmo E,Jvt VWEb}& 1rp9m!^Ml [@ Z-@T!5舥>VAŁe4Z^5U$@&jk;%}$ TꮈjdPWg6d}clP֬A ` cϽEI9-~/e!:f ܼYT:N<&t3G̻Caӵ  qF]O] 9<=6򙮐:-m6ZBʅXzTȠ",#>[epjV!Qq/8.wץotQw| :( T ` RZVbJg8J[.6KA bTqU"]F@e`@iW*P ptib @"z:hegQ<6hwෟԶ[=dyfb1oN Ou8,~-d2nWVG=kY_]kn2= Ax9/.i0aD!ɜu{q+0cYrv~AZCrY!=?5':Y`9Sk{w24פ\ [r5/r_X\\\RU3\ مur_ qeC ӿKMӦ?,:Z?#j\1Gol¿6]*,/ELU%SDB >fGX  a*Uci[< >gSg'.ˏg|m^㺺inl$~'R^^+3>OstCNOcfhc  =*^[GR}ATV :*D*Ve@Cb6;G󼇖&>VUc5Tm$WU$UfkxX!JUKY@K[-8Zf%ʪ T^ugd0/NjL /R(E " a)3b;<%J6[RUTa؛4'{RSѥV-(X(Ce D UHaD6PmCV0+r [D~[]Zڢ]kH8 KGyaA^~blBtZ 9[OL$b w]>CG Z Fq±ļUF7Nv-uʗdS\F󫚋Vݱfΐ`ӌR&L"F={iʪוh7Tj2V-22;%ݓJ&0o6M}~z/o#^jqb:-H7+5T+Jt*32 )&Ćb-п#at* 7 @qg Ӛ嘅mQtAO=8?|78>x5s>/7uoi ! Yd⦑ *wKWֲx~/jH(+" ĉmb QqR:h 2Z[fU_t8dę5X/OV/7w_oeצOOG\lB;:#9}\Y v!MXtQ V~nmUv율R^㦙={>7@X痿4=ׯ:VO?3sss/c3Nǟ~2@>;&A^?<<yc.ߪ:LZ26LN4\+[M2:>b8dN>}AEkp@" )xaD6/ui%!QEkHUFF=, DU@RʼWͦ>t 5Bt3=v)Jv(Ah ,Og$9ۣZ^ȍ̀q)k/NBvY2LEÇwAOi)>ZU/TDo$1RU&'5z2 tKUbv)A,㖒)=`lvM7!*g+ՊUd*V/"p!5Lۘ"ZHcz֍!R H Z8ML )bI0[(uAlS-l+Abz7ekT"_4S{jgQBX+:@EY JAuɃQxΨb,HD06#sC@I ]a(HC*(T 9RP:Pӄt1@nE%[x;9/5N2%ɏ8G#"ގǝ&7f /*Vm/Q43TC5vm{Ȼ}&9\bIEG#d:[ )c\m.OZyD\ԂX;֑+0u/VI~f@IhƒX1":gq;:a*5ˋKޥ4']#2yb?B?t`!,0HҴ7ttA$ DpmTMnBZ~+_IP%kŸCd=5xX21b[>. &z@š66"a ]G.a.3.+wF(JfO tT=+ J)F(!QE(1uWWWAbUtH$ ,We.'Kx4 ֍Q>l%i];Let&]N5- H*^]v*L w ,$Ba.V,Dg5 DTV$Yk(Uٌʟ -1*SbJ1 ?%.na_i듆ĝíqFіY>3њy$3*5aQ?W{ob4&R 2,jl*rʐdGe-Tjv5O!w̯g+GrCt\`Dj"#l:eB zd(5Σ Xa>vwY!YyR*$k ,͇YY>,w٘;3Gya޸r욕/[u삕]e)}fSš'fs~k2#R!Misʏ⦆oFtݔBxY1 qއ<ۺ,bJ:X6!/NW7 3fhXDej 9LF&}(9S=婢5\L/'|p?:iOO18oU4n.ϝ-/Gލ(k1Sd!F8=9|WٰӇ3wҷ,%Q[S֗0Ch iXI&G`~>#W̥[6TvPBe TV5q3P к 4Ðgg-Ѱ*?:>L hJH(Ri i ktȨ\7\qzp?GHuƔ+ToMz笂AVhs!8'ڧ ]9W钲8NU3\TS Cޓ}0c5QRXZ4Y2+Ϭxxx`#DwT7oUPTe$3~ڊЭ @jRb`9.Ą*T+ clrBʭ)P#JU)N`AɹX[2T qѵ8qYwJZ32Yaǒ(}+s @*JGçW8;#o7ggRqJOmN8ӂY=͞4c3gzA/R _Pju:9?tzә&)+[_:KտJd6gM38f< å\*L\3onI"0M}$p*u{{Wl"owwB9[Dط$`-:58/!v ~C\p~n9srfȼO5Lwo]աvJ72&nn>\8b$ م38?ttr뜨-n0r ۻ~|qőKݽ3~|~9̜fA99#@u0?d(;_m0ɸqiz6kB)R#v¢0!d*jNW4XTB皵qg*ϱFlwVJL1xgNâE)!@8zV?^^/rud[Ȝ铋y\M `jܖNs*#eڛd)jX]H 3 ? ZFW& dVfR_"9$d׊|T:5wK|dܟ0ƛY 4$Jz3] `L92l3RUZG2ӹϚ`9#_J㬫5-*7x#IO#EGlYA6'MtX.@WVXtrŢ/nF,qU(i4uiN-ց|xM躹:ͣɾj{L<8>W]P %@it.arO6ςl?cLȴdL 0*M(-x{bhզB_EC;y065YefaؑhBXBݏ'eW x}䄡JC^ubRY銫-kP L5*bRJ*긲ZVQY;pHq䋠|b/B%KRq-EQT7ce/Kį :pW%`u [0/N/]O?|9|uh{D_oZkٙû;+XϚ HBܝs(MHlRg|+ Gɼ.W>Kj<>UW5kYdyʫ]s_~łELJGG6EndIpwDQ'up/юwn" Ϩ=3MM҉6**О&k6%!}& 2 xVaPy,8D\Jsy W# R<@;)Rq; u8y}|Ӆ]֦ӷA24g fe[\zzj9525F~:$dҜT6BQ-Ve &!tPuۜ%\ |:a|7!lHE y/#TsA?z {kƄ.If]%SUK,$GV]ÞU@$Umނ([$7y/"،gEir|tr~v>?::+6q0!5Rwxp~"WZ<cyZ='K[4\"rΰaR&~(L#&w|{wy_,(HCzyv"v#?<"6~#e.+CI*PuUdwnl{n1X.ezm db[V0S#eMd.#^%Uԣ`YIe#o"tP]XJKUeMA,#xh(}RgNhoB=7?w7.YbH=woygdb"/Ԅçe41 -al7mK0/;_KܦfŒ\_5lU(pq^ b{΍V@PkY<^gJIv?R Q;>n9wyiK3B6ف䗯onǧG&9+}Mftzn97?qN<>l|!wǟwww7 !щ0;0 d}秧GWbzE׍Q&_^oE^d{9: 5Go랖(\Jd'B(6e2 J'Z c$_{ .JŕI E*B$L=!ӐT(|H^lpH7̐b dQ YaHA^:SpPeeZ- We#-#j]hdĘAIeSfy"-ڡ`ԗW€mAj/XBJ.J6xŵR +QjQS%F,\2tN9>oQ\ ްEyw"#m WzC+M:7$B?̠KPQh;^NN?|QRZ<(*, EȒ4~<& ?Uڹ, rDa?`@E+kT%{$i|mc-nKj?$BnP rɔ`,]J-[UVjCA\…eaGp*+T`[Ƭ)bt ij`/wš3q`ʦQŒ3UvbVjZ}"oSrL<Ѯi: q⭶9Kf)S-gX JPBRŒ8H}8* q+v $ZV+P$;qyxf3;yrEq C"dvEAowUDHo+_JTV +FhBJT$Єl6U_@#\%";5Gq@"o2F:rt_>~8?:$s` # ;Qrp 1#>, P;/5yj>;AuW)ύ*Jtõ%n#)}i6t.H=1h|@HXlTcA[lFH#yv5KC >i\Y5^4Ě&ۯ׷ tǃ%!dLd77Ǭ>]]^tztqrpzv,wݣe6OaPF~[~77yY)k\DfVY]V䅸w'nvww:a$oÃWɧU^޻?|X~`CwyJ*Dtc&3.?|N/_'?}GFC9heLLCɯSMw7"ZNMBLU:(,0@FYA۷,t~oZ:C3Py*3 T4 IFd ZDPp}e>fӳ 0na#|+ǬD)^ *W*ꘟC"uLSbg9urn\NH-#ZsűxG Q؂6c^Dbk.ebGZYtX*̖[5^fkʃr+ʜ?Z2kv V=Y_ Y--UF% !XXXYgQ7_]8,06NcK2K~s=??p~|G]({M}m e_F +%*BF  7t6EpI&+^bŦ@ Tlb&iyÊ1ߢ6/i`"vL6T'm] jR#H(QQ2Zؙ0P77UD)T巋EP6Ze1"J*E l dU!LR$Reb+\^@ jQcS3{qdJV .jm(#+OמPmNj.gjZxlUۿ:} ^Ub_q&hmͥeۂfq5e Lc oP *ɡB:^ A(r~3c9C-heԾ(&X&[')N,}`VBeAc0ƅH^9=Nlӟ| >oԸnϲl祧Smҫͽ.K嫔 9.>=>9K2wOGg_\ &e38PěW!3+;Vb$MO/'tSÑED_|anw?^e`Guơ49Ch:!#'"33Q3A|-*R-R2-U%B9qL`K7VBu!5BUU-Wf:d)&lVj D\ lEHVLKU/ A C02O ͪ":kĒS;W>h4ըalUMkiskKQ3[yȏVWm& X5X],V- %T@.K i8;/1LK L%?(uJM⓷L3s|0υ6' y*Qջ"fҐl>mdֺ~F) 8Q)LJ']QcF\ %}HYaց !$̢@YjN%GLOMMw;e[j,u?H}`|B8{y}tiӎ]Mn6bRXp>]uc5\^fwm&&>}M8e '6A6ZsOUǓTTp҄ȸ>96 (X^jSYʰ5@T"_R B~4u&j-:`lh}h@=gXH`q3-bP>6P#Ys?w0G/'O.~yaNa,`,B xk P]aBmwk-7?INl+ЁWF:2"%%AlLG@FK. n>Gv<ɯ9xk#Ń,`d'MK#;w/\?ܟ?E5G'뛻sas<;?k(vp2vqa Ճ6װEvf_>㓣 1g$-usǟ0_+Ui>gRr5Em7ۇ~-ݍQsLҮQ]_+΋fKVO%7=@\4BVן~YTC%K 9 *ЪWDE_Y Ykc6kQ%,MNS KVcZ2й@L@Ti5Ry`IX* W/,Peh-_@lXiT-8%.u>s]rf-uzPE8%eF,x@(<ĂdLngnjdj$J (URťUPRmH!#S@e [Q%2RbsL;ZoD\X`XFI^W|O![x3b<~2MO0RU(O>V,X/oR 1b435ACI\95#eYA(-ʡצ}fzpǡ)_P83H% 'yU>HȮf6P-x-(K/R/# ,PJ"5^3 *pt8dCƨn=@ u[$2 *.W X??✀@:`68(kĮ|vjCsd[7y@%o B<;=Ɋtv ӘoBd $ABVS@RZTGsZDW+c}Uujb(yJ9PکGoT(?I0Sr5B& 4HMᆨw$qgE/K\墨 iEQm$VvFKkYj\vjpU/.E*bz, ЕX(ũ1_BG)LR-{XѝH$ 6~<ǣN_~ՅQy]o.1ϛX8e@*m5~&>HtzxȻ`fd}EqHE2p<: =K%yyv?է'}|qjF殯[w9gIK>]]3м?m{ |&6GI\^^c~NA"i2J|'k6یy?9G9?U\dהnnHHJCd:WZ9y~sܼS%_-*c?/$4O/on"\b ӳmH"#a?Tgbt(X(+^bTAݱۧnݡ߁{gvV pPRҴ$Z]:W@KKV5@F=Y5e~lN,-bnck<,GC!ԑZ\U " e)YG'aXUX`/$Ш  L"A %.]Pj!֚⒇`E(17R)Iyg}U=o 4ɈBt?/LJɕܵ k\R)+JY3!d\OҲ~%B!؜Xj@)<~nT@F|E%N8eU2ytFR*j@ zZD+L4D<#b`U% WV ꖫpmY.⤉<:Zm-;(Hí"(`\[$CބTq՘N|{>hڗWC 843oj't-wF=hbƤ2O>"2lO(ʝT@ Oc0yNT!&m63kLǭGLP2ᱩa&|Bbk+F⢭s8AgLFNedk@l@è"%*bѯZ@Q}@*,"|,/ 9ZZt 6@ȰT*V3*euA\P.xє"B׉˳/Wm?(]m F݁H W?7,,+;d O*(Y&ΠieeSjU÷3?֑OB>R7K}/|ɼ,7#,Jsb7wHE04α-YtKXRãǼŅ)4%Eznx{}./YR4JyE.?yyَ/'goyt\1EM}wǿ/n|\ )%}HsϑOWV/_,p3W;'ݭ.ppŖc5?29ӛ[Y=*vL|k~B3N*e6#OQ13tQBRm YCUV[^/x(^mށV z\rP= *ݪj x8l+{p7EAb%4YYpP\ ` 4X83#ğuBnqs9Y"溒5k_WUP kRT,8:#Jq~JGPVP"6BRX`M7GmVu]B뷎@ckC*\-URTR 438xK⤖q쭲b`Y.G%{SǑVrxBdOvhKcrJc`E MZK=TJ8r ?*w\^G:;TVgU "EO|@P*V}_6 mhbUPʄ7AN^2iHIt8 U,E@;>̀fI Z^)ZsXrfCDx%G e b\>ZMu`誀b]!;X8m1=rbC|2o3@V8~=@'yZ/V'2j]Pu3""Ŝ6s9/-:6LǧY8f +4} "n}{xΐ$~Zc?rpdڻ_  P/rSo,(y>:̓_= ߗIfMˑ36^_ߺhNrrLJy* oKR ܝ_\]\\ONmvz*|:ʻ tގRv/_n/4LY ,y3+W*7+c4{. gNzQPW׵yQH_ :'gvrowٹQ(|~vߟ9=;;wk3Rll\<Aի'/1KZUjH T[ %pйX1RBe H̦&BFFt =HXS&2$CmټE~F0{m& GFSN \dö2XVB#PV%͆j Κ͌rIeԸ !kT6{)G'-_ MS$0e':nq_KHrT5d=:fSfƐ>"{1 ON7?Z-lKsonN(K'Q$;w4XXpdo^)7=P1?f`4-Ǻtq9Qc33փ ${B܎g$RS@5Rݻ(ykSffŬT"sΥ)ДO'OZOdJa#R5jUx^e5t]4+HE:Hj1"j)s+SEEЗ |@[ >#lt)2bV'?;kW~!^Hpb P"X)Fx6I a(MioUNl"# &'_fix~)\\b1d9L  //bի`-TF|Ʀ"blɐT>__}䤛м'&qva͖@iN 7*U#,vd=XSP~~LJL.b(䱀j@E$ HshwO Ll* b^\rGz~!渔c@)!_i{|v!Gvv6@[vޛ"yI*aeUGLզSR\MvXr/_r|ssν GBnv7Ofg/Ҳ=]~87f,a_@>\J^>p9<˗ۻ/7hh<qm ˣnvʼ෿F+_27_{n7o{!]#ml'{𘏼2+e u̮$K_ȝ,sq5fe&sX؀(к8ڏ45*@Yʃ5XDPA̅LN9IVg ;\FjJYⲬ Diz6C妾fhM)|[ʆؒ&D) L-])z*sxU:+} 5H1 AmHBY@L&QqkG=Pɲœ>ix9~tT1+F&vȠ*t o>e!^ ̨wH(c)bW.Z2!f)9eocSRB"*_ժ@-܌ gʂb5Lw)#L-GJd< $L GC(mP%P\ kRZ %@WAVlZZ[/.J\*RXu]JRjfF:z*.P־[CQH> Z Ow[6yOT9V?IZggxlV,ppoad7=?ty|5%8NptWh}{}%dʈe#Y?Z(D.A,<|wj~Fqee!:wB)>m yOj)0߯ I$q8yLV 5U+F:aM/ /!t<\Je]1.µI HQ+_8:˕f.G,ŏ?W޸ذ\|[g>@9~g:> B$D |n~5_vb d]9i\"NCw?{xy]i 5V<eFQ"e:^~E%0_MM;,J^o]fO//ϵ9{W_^X>fLi"$e.tkF0?[uO˕n>_~yVQ!Kz C˭it}^暃`;8o$x{uX3fs=;=bwsƄ&/yQ*fo&t Dž3(g!A }-oRҮ mMV 8Et!P DD_Pb]I5UzY"9@UD+ { D @(]$LpRڲH~wX2;WYJlE1kf'ɴ@Օ4$vldThd6$2,9fc5m|jy m#< `kU6!^"@N%]H@Y͛ 5zTZF@moP(%NGdeZRl+ܔIBlMV 0R5 cQPgQbdvK+*YF\vpVZ~[..tՖ,p.T /T}qIxM!Q@ @7{S-t zye(7FSh@eІ_k(2kV/2cv&F؟f X3Yh;rT1;`@)J$76rw"?tSA7AFֺ W 9|箕Mb恖}rzo-'eYH{Y?|0donn xtby\{dϹ(ڲ::١U( zZ:ش(Mq^6/N==(*\'F2˔D"_O,0IC".kgV.rݐhG}c/BWk՗j@Y[1FdTUVjoJ# "x=m#a: mf4 =x;>|_~wuvl$1N :A^Zuls`4ՀE9GaE(GbϏ*J6Fl9i{|elW䛸EIޮ& /oO& K%ڥ i6A|]џaae .[dc^ݛyq.Qo5(aެ9 :{fNzx,ZNq뻻ggy6wyQ|C1s$SU^L2ud!U `ERtbfHS/<1mk`@U(*~j9 A@\b0=dXhV`r4,L(xol)~;Ė)9E]~.T,2cy&W! ϒOfH g-KTg1]U?ɷzh,#"=xhŇLF3c%;N+Dr۟Z%\8\}TbVyNMV-z/Z_<'iYjV-%JJ/qU^dcBjx$X8:F^R2k$+Q2R/yZeeVlP,C *RN eo?B%SbTe {ɦQY4j&yH!bN^}ț+N ʣ>\Xdllv3|M/8W bM3#eԠ?Ʊlg >{pwycd4iUZ%h%fUѫHkMV8b-}քyjb~qBBT-8I/lSRyIJAVKX;2b©,rQ8JeUlT,pBe.3a Uc<*v*<U>kb"^]})|J!+8;ym4sw( !D+_a,BGcvQ$ܨ'8U'W<<;׈r};u1q뜆gc'M6/.w\f<7ptg~᱔F{$ӼDwz~q~|rj9w/.uUxd8zpt{pw?g`̑J(BTIM~yb,=notEՇK1[2(񧯷N / TS.鴷S2{Eop!m)>;_?* ^_~܁њ*zʥWNK A#X̐{Ǣ.4w?;zù?m _4ZlY5I3r/yRm >IU3;A[GZيHЭ06 a-W>ۯcU TPEG3;6 edLJ2UV̢m PFڷ6@Tg]Ҫ@k1=ݐ]R18QRd286%A$7o$3xGF.kP&* DxT8Dn8(O(Pl5OX]0J2QLFLg`JL>TNJ 2Х|^z-Rb2|am(\f_Ul!{;JbC> b͛=K80 鎨Lڙ,< JLrRwꓙ 8PDu -8lf6">0.U C b)=Wm/Xe` uTZb)YFX"`Aj)Jٱ{a,0C q;7G5q Ȯh`њ \_G_w7F?'?/ol=t"M*cf4> ('gi8/tʮJ!ꢒNّd"6]HTކQ J"l[5-g{#,[2U!XAW"RD>+, %B0^<Us!ҋ,‹4R_ˆCXE9h$q2H3YŔ['H;APᄏͧCwrb9. 1y\06f-JH#I=Ev<"kVm+c$i6|% QiVaaq/w1V$ProDxzruKFRs7\{o30$~{9aBvq(!@KOV4nj/7?rÝ+Wї7RE|\s`xmhť&|[gݰN/_& 2xv~9\H~˗k8<ۡ|{ ș=~ўϖtw+5_x|r& x\Y/FMNWΒZܡi=i)j#(=`9CiVyfR”ICmHC#kkC{eF *=ҁ NGEFU40k6KN@:: SP$?qAcV4 p +* ^1n[xULGL N%S42 MG0%ͼΠ >P7~3e;[bYӳ.N& )MY|$ ƿ[eA65*@i B62Φ-7*Ol*>vz (sF,w iTe R-5vBE n*0&i㼄΂%f0*Mbb/H0{w&VϼIc^,1VDd(pM,{GK2ґa[SdLF8!_ڛ*]zY*0窓 ]z \zQk^$W%ė9}= y#ZbVZ\*xp~zz18ވg fn}QCvO\BNNi⏾>8O?#n~c|VV͗wgZv!EDt!_zԡ+Ӌ W>ᲆE}`;ì.e,̴>|pJ=9|ɉ^rpXfs /;#}a0`-YËRZ\HgGuqq^ƄzzzΪ^Z/Xfbr?l뷮/d=,'9I^noouzx4]6hpDMDHF@)`,Y+Wkn>̛ %6yICWFꨦ@Q0`*P}FYa1 N  #i,>p42:/u*5j3AU<,M̿y$Y|eXo%$#!`3kl$mĊ@ $zwƳ0Db76[2wK01PZ6T9TWOH4sr+:_ pPӅQ.?xϭ P(E.M]NR,p%6m.Zf`#FB3 ښޖGb00T"[)P4Qf7ȬP/ZTD&H>v_o WQ>|!X|<@p4w#I9Mt@6U@e%̗n|rK&,)(j$IR&iPr77)HW5;I^?Sv5BK9`,#%u"} TR1`E ' б/0R S;KАA7ZUt+5Q6Zȯ$XxuZV T2,PjqPẌ́(ѢF}ciA.+rJ[kfs4ϧ/Ϗ?_?^˨|gqvfCU >'7,R#eB &.jŦ# lWpj ֖* P)Ȉ.\ceN@y6/˧ ƙNʟ"#{8?jRpo/59n-uz}~:;u Hㄝ`=R'p9 ;rww_onݱv $ϧʸh߭s9`,ɧ\^ 'gGV2&;ɠ;l1ݬsGCk-%n$ v;8>|=;=' lL&}$JUfAVrޮ3Å;rB A}CP TřR=r UŋL7"H[uPՉJ1LZacݒ39rˎ |-* B-\Y2"1gp5ʜkJ V7 IKV L%͙֍ԷSZfr^HPb=!K?brd>>^dm5zaЋlٟŭ](燜rȑ)kHfpj/,bH;6DipGҜo'K%"F,6SbMt_R,JB8FFDŷsM7&;D b:"YF*pHB JՀ )qT7e-X\D-HU*0D]|p ;;@h3/65J^|4R5{mMHY0(* S NǩB: G0V3 ZV@w[E7woG$3_u??j9{ SM.nlCUw[ GakpE&"͂0t˝r ¼,, { L|])Er!\Gd^zfI[pk672Ar2D/ }|Ŏǻp3L q.M IHҫXp^07l*"]]\J!=<<-Kir'=y=MrakS6.SU.x%Ր `ı֦cD^"[wyg˗_v]\\|2υ6Č9݃dBjfEЌ3ŻSD@ƅ |=%b5ǎל "]}1lnA'9j= ZfoEpywEd9*I6_ҕuL.b(Z dG n+~["A9֌$MZuҙ-WV8>?3T!PÞJQ6UUDUzix{|#G3jDQs (ԠR$lSa4e:b} Bi,L,"(jDJqtHTa1_L7.yBqósT'ה3 Y^oa:iV׼OKp=a ( 2]3e"k.*RkN%zxs{s`:E?(v. n TIJ#p#pǫD$}}J)NzI~ԹluBxyE(Ǫ>)r۽)\3B}7 ;KB0g^\^D5s.=`vGy(#,8&O7-Pga/g=lng%JJG<<%rĢ0Ku˨{=fuNͼmˌ;cDo($$L1=C"z,uq!:s:=\tF-90hsݲǘyb W9A:q3:,"0.jtIDATfH43g$4J̣eDňM5"jpz/14+C<.}J2 ۄAH@ @CժzB UDŅW8ZG% N=fW,d%Ǒsv>KHD5hkzAi7*gĜFoO@f?ڔ3gmC!gM;:$8|B*RyJ2#U.ޅf|`֖Ǐ=4[O)5ӧOhyHRb~ysYIJe Mch{>ɫ@c;hؤ"I+zld"lvfXXmQAi@IDlL)Q0Ʀc#Ȝ;G8P%O5q*"< [0}ٲ*V ez)X5J$$kJ;4c[]m(SKriU;RQE˶&2bE-lcpw.?_~yۿy1鑩rzq{Ѩ-uej3ruF&8 +gVK]#%ۛloI%Ng2w9t;N#dg1do w{PvΫϫ1Y;{.T/ \.TT4={K0 QC5Ȕf"1W-H@L)P.0J. "B%Eb58[KA\i5ћO8J;{}{8>ڈXܰq\!lӊa ]s ku!(x"$AS;L|v㓆?,8Gu><=?׌ qN[779g#Ek! PmKI(NBv|Ԑ/t|pz/_\f\,>^@>_KP8Rt@ 3pT\ۥ %*M͉z\@P8J[WǩI-_=gA4ZhUJ7 L&1Hye GlW[KCGy?w?/Iv믢(-sY3\JggsĻvǶ'}:wj25^skmZ zx޸N\&MqӺq *X`XULj= wi? #Z@y8;^>P"ʨ~2EEo{OTWU-#=-*^MQ5~5U$3ZUҰJEp023&+Tѯښ֒Iu-CVoT@N~} .f%5@1 ,#"]D2\fW EH`r"WC"k(w*7jCD5BPBV0VKuo>e2bJDs(Ti"PA*NPⶪ\2kU7d Y"&*JĔ+q"tteAyh%w?% 1P \.T5_,Fel"&agq"V}CJzq+.pJl#=?>~|فI+mEy+XS3E؄XݗH@|Xkfxxpzsnɋ#r_ܞ_\ȋx bP)5&T*@l ."~k>7_)*JE9"7/T,\>s*}oY?XVL%KGQlIReEWdyX^(,rZ>mXV"26[Hq\/%(FlӨ`lk*) ꋈD2O_![SZsXyjV@#Q֡eQtD"͟0<^ 2݊ BpK Uk,Py,bQTZP"V[H]~UpPERa8` Њ،+[uF@C!}K+)feU J\ ke$gE.VR|)ѡO*^y0i5(W6Zĭ7[-G6J*P%H@$d2`MS)o6 -lF#.2؟\CK_2@!qU=ıO:]'~~NgaU2XE5c>EfNfZ6!1<:|=z]7?_^~KQ/^d ޳h^FHZDrR# T` DmI||Iϼ*ړ7-j$e.<0(EOToAGQNtik\~t+$͞˃$2FUb ﮗ~?#MV_~zӧ,/3n*E3*-i)v!ۍ˶DT'YiJ|LYHNo: زDaaâ~C.L/?_Q3^ N\L4ֶMu.XbOIp'Y7<s2$ t#4q&ľ%_)12}ZˤfVH;LYHurB5|[g a]XBB#71zi5DYDe򶺠AAج \,jZ|Tk HOxIƊ6o}2"rTyh "Te؛+dNeQ'S, gO?\_]8*쀅0X$J,-QKHʉjL4-U,H! (y/-TDxs&_ʢyrQx~O!=wvhAXL%).l+ #w|[o')nV-+UE3QrX㺣AyOcz$3HΑEG_]r}_#Kћ g fc]jC%%i-z+ooosj$g50*<5!MϘU @m Y Afm7ժuaO^ưɕf佔'?|/_\ ^.f݋s|;/?ݽXmU(SnpY㟮on]Q:IK]:q_}pM)5Fe{QTAlTP*/#$m)6!"YZP#UV6$@ M1Eх _Z+]qB)!~ Deu$e:p>_CJFGaHَJj:w+2`zd߭UcC=qy{Gq|Y!c,Z!K@@q,ـPTU {٬XrA鴪X)b%nOW5ea5d&_mFK,L0}EP#B `(@eP@تƔƠFE6}tײ:Ie8gǫ(|ps{? ׯ_@(.༌|{yz'y.'y.&Au7u z^:>`E,ض_3y4>ϜEҤg b_;6W \(UP܆6'^9I|%qe3>00UX,cyrQTX"X@\j %hn dtu,,O% z((-W@Ѽ/7O{2y$ARzDʎ:n`2xjq{PGr a=_6l$_]+SY9fp<6RN.E`V\m w̕BŊNp$trx}cӟDk3{CۍyRyӫTޤv/ߗ#`x/ݚċS+K_wUjeQ^Uz65ɭMbu/?? Cd"eyw'FӿY^/CvY+' DlaypֶkUI$ HUc_Y#x?B`S.rۙNӉ8Y@JKx9KU],=w#u%ItyyIRB(KHlo/?]}i(\&~/_Ӱoy=I7gz { 'U-* N97: n+P|BZ%d.yA#Wsh p"tk m.40%Gժk$m?hHx_UJD-J^vjPLy_.@ 4-=G`%i5+t7GU0E cȝrDžrBe#;Jsр8h"i,AoLG2$#q!+6lʔؘc{A edlߡhUأW}b46(8n}oHk,K[m pjK`s~i)'SzpU#p7[`eprۛ1P68X͆QbQZa?rcL>;0o/D瓃_㹋^͵Ebp^K alCkk }ӷٙ{3TLݴC̴rµDjZ8_'$9/UL y-7\J_a٢3pOOnY` +N>rY$Ь!Wy0zGy^BrD#7&Oa^KzyyŴ9j8[2gn5onqF=zpzd*wPA|ohV{0{Ҩ$2FYt8AildgTVUb8e[tpz0OUk|xxk>HXjy$z6OOO.///$trnY_>Ɖ-<=onH};sxtAtb2WÄ́!_|yG-(q.CfL"+ :$qN c={J%Ucgz'2G`14!( _f! rpZx/ 3P<$yk%K` |Aڲ2+C;ߌqf7CS86`XK D3kDUu nE(ɧi9 N\x[Aη@۩u$!S}]llx c*ZC53mitU60c ԃG$>OOhAMtppHRZ(+ebM>.@*?_"H([IJ"9F2;*dvCmRUH "VDUx$#M6|y$@C4ۼʸ'GF433 4N:o|u!glh7.ِ2zY>CuPI0"J&NqgYt9.S8N5ÍkΌwҲ-dg^B呸4ϛjac~G&OOdy^:gl&`R䤘/Qh\Kh+Ki#ꜤҭM%Zg^^w$d`Or24,9bZU CV/ggƇutwOg%Ȍ9uHģ9 V{}{7<>>6+&qqyqzaM8&dW:>9;ͷdUc@~wmtM'NdY~C3"nHxJ C̆~s?Ul,sq"iy`ʡ @y3`I1-p\li닩\B9<&1i!P/&@$?/-}cp/W= 60ȇ0c2#߆}><9U)T}i쬠]T>T*U&QhW<̋ڌj O  D ˲Ru\>*Ȫ2pJB|5*mZXlҢ|KbF8P|EVOaMGWc`12B WK"O^ؕ@ P|fQ1ȸ\Sr5@m2jxf-RdAĢ<ڰ=$"%eE.6b 0_^vw#p?l: 351$!fC0ځZ<:7e_w_~y=|.@}|y~vO?aZ4a^Caspbg5qLXXYC6~1hsD5["庒&"RZd.C'ra iZ5f`Lrʸs+}Kۛ^Sꑟ2$k gV:hX18aL~4ˏ0H}3 ә !#njfJ&~ kutlp$QM6/WmVk2y#G4إ/~fO3A[pmH%IСYb2SkDV?(d嗯7?x Я׷?%#^6і^\]_鏿Ptw2^\}3%뛛7%^Ϭ>9=0݋2){K|^mR̳\l1?m9ռTJSљys99=gUJ򉯷lFu`JccH,5ps'`=4(gu*mꆠRU@:o($U hH=yzLmJJ@L ac>7O10gXxN*LZ.2* +aQC,RɶCFKעUAb(p; MCbf*FF[4MK4O$YɪXtr6FDQI"6*uVKQ"T5!/ (_Z6encPr[De +n 6|KŅb@`V(.dDDfPGgXWpeD,8v77wa~`ɼn~>)nzzt翾wݟ~3=g:L-VAhC42$MZՖڅ (r\,>#ǂURt,1PeJ:*QWVx4!"qI i&yx%.LM;p̣"[,ad<xxD*($&`Y ;&)mߔ ҥ0XE@# "biEZl‘DRBX^: O]e>0Z5baL*WEjMV0b %3CAC'eYa,lMIu\8^C^ieE }7"7;c,qyEV+PtUӮ?2.#M?$ e5Fbnypjޠًv"Km;U'HV1 ^ ʪ͑|իXJUP?mYJdPe-ҫn H>==fH?¦M߁8oDLbti1c`;jٙH,a.j/nwwwz5۲0/v~ǎdSU,%%H Kw Qʤ†D q!CI,UY'O'"vyǻ֊d'c9ٯ/Nyz|Hevz%͙Eש er7İMÄq"i|<<ΰ=η6Y^~o5vsyu'S&MWo_;_ECTʼnq#XrtYd84E ||_N܈8r/3>Qt4N:{uq{i1iKC Ԗ8@Wstec j<ր)}W4L뜯fr,N lYcG *Qt*:[SL6eeq퉬_OY/R^_{ p7UڙNFfdgR^+}ϕ\@6`wu&)S`^s!^fBPYlJC"y4G!g}ZoMűkMѭJMR =r 6b̠#O&q[-˅'t:\u^-)lT5\L4_|:@'hߑ%UsܒWԵ!#y~{>~Ͽܪl4ܞ ri>L "$qIlXnBa: a]gy wNRg(#yC:61kǻ|탣g/4O>1vNmHIKC>vr[+%Y@={2`@4 >}Q)e9cAΊpz5WY bjE! xbinJRz7s{h aMu.1.0 7rYC 0CC8Mw;Br;CT@H'R,77ܓ!ʓ@bz!D=JRˠRsq@l Tw?k7n[P>4?.k+{;ҍW77}xί߽?{C凡 W y%΀32ŅkG4'KJw_Y&7IږڦM\ٮ/E#NYFC#2+a m^)IaOPb>z2y&4W^aPUzdhsC,|K=^Q2Cef2̐m6@D%0f?H8gCsɒ@* Q'Xzmم@@x^"DDϡpCQђ=O"P(桭ZI۞!Ɗd螬(PՙX񗭜JU4x*q ԰g*֬ꏳMcϠ RռlӘ+Xٔje8Z *JeH5XCw5T,eC,BIq ӒrL(xISд͔T"/07ѯ0Jt0ʖR xUᐱʫY΀rH$|~b4Y̺֒59Xǩ5Tk^S>~ڎ̵u}ONOO2&c|D%.'GNiyY31g*񥕧ܸ9ʍW9kwV$N6v5e©Q)'7٘odFsί$ l"6;ڋOTȼ.i9@˖W=}΍+U[4ڄXUo_MJمRƳT"(P[`#Dl AQ^r!B[AS:v4% .C63LV<lbtGt"¹fY[Jw6ß~fNDe;S_4~>ŜrYa2W\u+H}7=فӜ;f{,&6_DXajylwZ?;˺@]] tz˷~#z/ON>dޏvzWLZ3 yJBW54W`S۱;^RmH^cXɶ I${C-Ġ~L. ,F3 'hHDRkhE׎ e堻3+@UAM ݈GY6>ls c$}l/?haL7yG1H |s|9צ=U&U>;~\œޏOܯ?\]֧]Y$هڗЋۛÇWg./?΍|βФ͌[& blu+ʖbWJ/"Hk@!U'!E'iИ;2>3Gs<0ZXt5W~ҐVT'*6V[xjj*ZD2QLqsh!V~y}#6Gyg|2oN(ѱR ihʿq&mc(yo}k/FOR6Cљ__DLҒFE?9mv Z pM5!70)ΌJՙ6U$|(8A\T7$XYݨEt@18Vp<lx6sBjhC;TOJKѪ:LSUj)vG:~P!(DrZيcd)i^Ac#Nǥ(E[zFÝcśӃߜ}|ݦɼ(:.?:>Φkpi[2??>%vČ0s\IB*ȶfgާA-=2`}gN`YVc:N9 Ow[9Fgr8?|CڵBxKNj 3j*tRdG$bbݛ4txJO!V6>+Hu/?-1T8ޯQHqzdpMJC)x@K=~I!%aW3cPv'3m}l$LtЬJ<floT+51p?/ulÇyܻ/e9@^]}on羽$K|D<::|uvvrvvqyuy}okV [8i\qxF s^`e脳8`&*8yk`r_iB9f Yd*1JZ=TqDCz22P6M&u/KX= /ONr =u'rj:ͣPuA3?Z'5,|SPrq},M3:7`dtãs{X@G彎Ɲ$fɘj ('4%ZVh[ 7"JQ0(EaR;&bʾ|؉O %ޮT!vKW`"| -~BAFuB1hf hF!!Q7V-v( HWz8<,jWAD2B:j]NU)%I:Whkr4^Ykv9i}C/'"Z !P)֖EܐQ'!8@,U[VmFr=vn}3C':!\>Mp@"n :ԟRva\.tzq)Ec?{kqϿ<:22-ald7Gw^kc:;.,icG!$rafs၅ےȲ#l'1"+f&YX:i)V%Y&"U M=>M 6Yb:}U5߽m:2JsޙK% {;Qu[K%UXjeT2TOVAjki\ *U7Wk<[bfʼnӓuASF<\\Nr_ld4ո k<0c`؋h~|oN5L>V̷/dl3U3ZIq[ c\Ȼwe Vr CETa͟9heHr LƔm-A$gF(S޲mݧU 0Dx@FhbVNYPodg]7NIG`>о8%nvQ܈dLu״/qͰLDs<M|W3Be'+&C)[HK,ίUa7o@_}@Q%ӪRaP ŷT"`Xuo^>:>>9==6nÛM}2B6Ve ʀhjHCVAQvM ӀneSbjy -"1xWVxBSa+R l/F:V Otnʛ)9w/non½s^a`,kFDqƒm);sWMf///f:] o`?޴/C(>{CR,ե[֩܅s."mI9cAlM6Sö\]P|x<ȕZk!!4j5֜#Y6!0kB,g[igF֤ԪڦRDM 6WgxMdWb(uEjTl&f/Ei6Z6E'%mѲ[ER'̃2lFE:C!DR=}g?7WD o>ȮlES<@e1m=4lDY_d`"cdP˹6Cjfc@4OvVz4ww7|сyu$gt/ 5Ù6=.8mfB>?Zd4kA@)UMhFc™ZfVaWtj6s0ԥݱ *]^Z}Tǜ|yvlL n6-5=H il׍>ctQuՂ*8*mQ3>[Z):=&/ x׷{ 釼.TC /+'ܗ kd0>~p}uÇ>|[>Z] ?s`)T%AӿcKBpsc]R F*|8dJR!3H#5:) 'NXYH͝::3ޔ嶪"E Kx%h`$yAJWΕ" ][ 9^O^J9eaF.BaeQh3h͸ۦr>ynn?3=vuy(+Os癁0 H9d^U%6~ްH13=O8d7ؤ$[e,|Ct?t-d\GAљ03UC-P+9P$|k7R @^ʬƳg~Qɽ|$VM+GmئǙLfԆ CyJ*UM6v笃&Ј " pPyjjw,>, `r\\+)fg5Թk#co9-='k,;ˇeG'{?^!@>e~m$kO$w'LY_DZp"8.5s^f̿BM~6zm)dZGdh$পSF|~4{}zw Yמ׵9v2g(Z#2uʦ1=.'KӼ,4݌V7;MH@˂ <@֓yp*A7S^maʨȉl W(O7Jj]f01dQA4 q`-IFlÙb734"! KNN0zH P-bVֹOC4@oUn4ŞDr&YFs<9y/e?g_H (0)Ռľ߅O,km43*yȹ HPdoۗGF>67oN潚#~~ )Z|rOxsoRib)I[N*(9Fu|| fN3Kc*tІ.mS#L-9IA^sJ/ei2%fZ|VUҹK.F[@kjS~*gGn! :!.[vrgH,ز.wb7 sΑ1 F>mZO5pi1Niy Q5/.ԎQ+G,U#ZO|kp R:[@Ĩ^dga+sCƲ gxg7H!D>Jcǿgf # fHIjB:,k_,"RB-:!#9\Tќ4Z]8@e rն\&AzzOZLN\)GoNlP!A:{Xno/XP-;yyhEWL?~ã#cʼņ63L%yv5'|8#Si|;MU a-Y'{RMu Uz>=1 rOPQ:cSgAe! A/^tE"ekj8Y g|PWDg#jCVcfgyUU.*6 ^{Vي]R~O_pҰ;PNRf: )1QbU+R+ A#FrWKB%K/xS9Nߞ8?ݗR+oru~R'|xV: d4C|xXa L֬ q-oj3vœ́ 7/҂q>ltj%,!o|_{5"1LNnoG[N/'OyyB5oxV TAמ*R:Xz3zdR ktڦ=IЬIēZ΢\eXW3ΔҦQ;ui+LhYDBüyS^v|H-n⃲<zdPF8 (,؜TDH-:+yeA5(r<^ؠq>?\?/8V2LjSX~,*ŞMpw&E{3펙./.>~ͷ|I>nn,k& ƙ*8(Sgld,A " 2~ `#*'V`dsQdO\:eVH's8|~?~U٩"/!]\|x;dfț*?YE|ܫpeq!L(K, OPYF =f"mUWmk-e'3G4/s9.&r[MoTe;U;bM7ǏªK+b^2^""JnߺdߩJR-saz!N8>{tUKO'S*OYs<^,_0Y*}i%FxJ$*DXOsP*RP-3ؔGjy+*b n UVwM}x 9HNR+6C=OZQ$@ 4'FB]t Ģ6!GgeI f `o(:⨍"JFݡOܶy:}ĩLJA84#f'&) mVZ1fhkDW:qu$'dt`:9>^-DHo~vs{{'y~/ܷf12&/Fj#Q\q4|Ӕ<`.JGӻ3ڍĐgmB/rӳ̠>MXE*RelM4ij9QEf! ,F5+٪3G<.ZRHUb}W-DucZ^ЪRS=A,3j)h6  52DV0@> @4< K(rDaR23G?h4:{>zq/޼}ubbY=n2*·<Iy%2˓br~yxA 75A -..<_\et̰x'PS(j=$ԡiGN2 7JT%\AT-3t)*CKP9G(/<Y*漛iN:xΉ ъYuLΊ EPZ(sыo UY pڜ]Zk oyW~x'[qw4냦 tt)7?\0:_-yP{}}꣧Scc )΍bof3Q)YbVob򛯾w_/s$yYy&շߛ?61:,2{ Lq0 xQwaY=RÒ6B*e,)=eD0 ozQз앹)ՃhFJKtn[i@DA/xd-SG*gg1`l4I2VW.;COg{~ 1ʔC*۠J,o?.,UMƦj:>>i~%8q@ -Y!B{Ed4a^O0o☕!j)J j*DP~KpT&GPGfDi6@8Gwp<D:Og_N@[C$U5lsBWMb"%xi/h٨RlObP-C?豱Sd7 䒂8O[ڤ;JT2<+UegK˃ *F-b X?E?YAlL͙R'H ~?NVJy'O#G{")D}ئϖ˒ ĕq8/ _gXPk`zHUs L!QBfG| $(LNUq!,z(V!Q5Ԍꬆ)PJ8<*ب]R·SNQ&n"G *,ʉ+A7UMq՟.5̖@SIٙ]%bD(ck2rJiAqF%2_/!Ƒ 7KoZ5SI )eR>fƏR@)?+<(6h@lF( F4hETpdlW0?)A\I}Z4*hSifT&]mD,]JyRzyvJqmLӢjX|ihWvL D-~h EziXݠGYz*6`J" fmW9rd%={?i.dpzo߾nXvKI zܝrN̫B{{wyO>X_u g)ـfeWrg&߽TyIyAbTA yw*6?Zsצ)P l몈TӿUR2P ˘?7(7=R%[릳UR(uQk] j=n!ADALT2V+\1- u@+\k-'clϝ܀aY ww/_W?}{vx`#泽r~hEύB9p;JhS:x5R Rg+3<<74r{̇oI|e#! 4h$ɵ0exC*P[De"L++^zgpEks?mjrmX$Jg 0`nwwz6cTn%-qK,?bSs 7j8#--XI$qr>M  &5;`ZgPgP:@bT1T &̪p+17uU@@u*+y{CYWgB"T*^MrTЖ - ;ND P%acd7b)`ҲTVD *u 8A*"r}V^Pf h?=>:?elT)gN4߉(GzT| o& U"Uj]bV) 5P5AR68+1p 3r7*@\upey-5Ss}r&__K7wwy<>gr|͞7}ʥD#k#¥mW']́5%eN01!R]?Ӵ}L @: %B:KR"gJd|&ܦ@ߵPYGގ8`DCUnSU=2+516TJERA+P+1/T.M!o@nW~PQ1åv1jX{%($,eP&5ߜ٫ׯ>˫\ ˫k,*S([ ;SȽk(ZK4͠e odB|"_98򾻻\Cx)g>V"`|Fca>\~07y)KvF<М6I6,XVxM>*FDو dp̮Y2KuxyUJVRq?]'ͣ7C HU4ce/@lcPEL%9 '~,m,vZPj1+ͽrMō/nnF-zLxtm 6^JZqVq֟Um5NDM8P/e pqq}uͷ_]]ZHis25Wts[%,7DNi(-xZl%IN׏G7796½߼9}uJGW-"+ucKd"}c.doSK(L2wt܁W|_O۵gu C%+ժbdLZH >~")pAgIad, "epr`ӣ VsKllЦwqM]fytCXx@T!u= ]^LCd f+e[l5U@oP8KyXBQx=+Mød&-:Y9·m+1lU Pu *AIygte!enTz\)`G@~M(1*Zmu?aVQHd; kڵMD~$ԪZ+&iKuMYVW,ªDdBqSZ1B*[P*{ (dU?-̳%Q5.3^bUTX&ĄdJF{9R8މ}ǩp"kۼ%אh3)Ȋ)*!% 5~G=^e?:v̼A߄ұ4*Fy Q>Sa_2+ĕ !!j<ء(TR#? bt0/#`+hpCLCmE`wg*S|ZIڼEǀq WjTADRjsm":]Yz)OY>\ĪX5M:J\*vrrrH;;-=UU")\Nuwy1"}Dž;1bu\\0/:p=ucN>FU,mM;/v^\_瀓o+:Zrmzxȣ 򻣓㹨=R|{p &uqyrKZvU (QIIzv:72$7ctk8n )SlwHw'ƛx 6 C2O4ICΈG-bGCzOYz(yXun81У(6z ^*WE+һ7mJ hM=hRֱ"lz?üdﳻ/\uC3w4`pwh|~ˏ1L.||yqwx>c4ROjZ5L;DoUkqe)4q B[GtlT!2wu}oů~"SO[4w{< OWWGLJy[P(culMe<7Km݅72l%AeXU[`_ %8J5wQm7Z,G.U`+Z$6@j5dqu#5$Ё7,ͺ}ݗqM˸)a5ڰD#e3;>g[OStcv0`X: .ҌUDu&QzqS)O[9̄ eUK1J!|pgGb (1DrJlL/eI( &8P$̍3\ KCHlCԊ#ugfW FJT+[hWs[8~DyP6Tda<$yGN%{"TӼ jơ/8{7[2FvzCKA('Zޗ7<f&L8utLMğ/x[ӵvZ)*4qi2q^%Fï0l< {&mehZ5qS&{K\aܴ)\D ƥeqV 4e.QgE7txuU>]kh|SO8mMT!v08Fc8VYVYt5:8:x^x>9{u|p{ee)v!ڹJ_۲ڼ"ⅡF1o(掓pyǼp'i<sL䄯nnrQ|~柀Ewo$ӵe"{&-OcOϗ5F/2Ś/֟^ێ'h"H)P\僇{62iw<[S r ert0#;%n|ڗrn~YIj|cm,fu7muj[:jcbЦ҉JKjR1,4תQYS%_ Gل 6Wx'R8_6p7ʢ)I}7{yP1okآA"r4ˋ&ILBH,! 0J/$/-[P?sJ~.#bbe;nyB)@91/. e?7o(=RghpﴊEY" *+D  ǩ(ԉ/oG_Ja=KUK {YҫY!~;ݍ~^VmN9%g2*T;|2rC&6bz$gMvآbp ^'!Z![i =%Y6eaSp"eu*&"U %"-S1QLqMڈ& \dΜkQе@((&oi9Q_jɁ."u??@$&8=)`|i2Sf[9b"÷YFpjI?&:C!,% %aVo2' (]38$b+'8RȚQd B|&l>sG4M>V!? D#A9Iӭ% Ռ*L"H5-AȂna(}d# O:%ڙk:a+SSΜi?7 ZsRc*6/O~n;fkJF[m~@*ٓtN̓5eI8:< ]3=/6[u?',`mVY1 y9o]%ta;TZA>FzZ bnޓ5űiGg~Fon9n&y:tVFԂ'j¿TU !&:-V*Ύ .d%ɳ!!e8J16CМ3YP@3Kk1 k!8' iVTU- xVZ6$>d@ PiFMق7pRjnb]"!H֕kIM7079w!2`n8k"!%,$z7L@li<5`0 bsmz|wz·j]:r1Aʾ9Ç{_|WW+)?sȽYT@T& 4üIl)4]==w/'޲r3c0Y14[sIqpvqy}yuje7W'皚x#,%k*@ m-^mE*WUMՊGVlkVP%b!*6^< #dH,) at2iߢ ^]ND,}' ]jUAu13Y6WgrMa~ :9]Vq~UŒx'"lLS83d  p"+ɮ@4D-hcԓDH=|~|`O<1qx, ՠ)f&A?zXlFh!zv{>eHrx`gcaj ;Nrh9{o~@Vag6C{Njnw9ūW7ׂG9˶Ѥ3/ef21O46j^$36+<}I<&`;qi@TʣIg_7$$>Fž ^&_Hb=!qj-kEQ;aFR+UFc@E6N?PCYytN#^drvߐҿyU}Jv(WDT)uDg&EӢ @, b1e^tr,)D_fU'r;9H9yrZ"xyi ASk^/b%D1$R>XLpz6_/>,Cnj7 ]‘JU_&[,ܒ_ѹ0)Dt_TpC u'q!a+ 8ϷJlM蠆$(Cؐ^dOcPԄg<\C '37yCz~~ /rUk*Ne=̍!r&"MlQW"]=yX6ShڢCND'sPKg&a +p7)CtOzmqoL@姙*; ]__; Me,_oHYgq=_{/^>;`Cf5"ڿ8?Tj(℗lÜaö1H.7_~tǺ1C`?'6BkfNrt`#Yˋ.d}O6U!^ &<ǦaU plehJzoʯP XD6i/S-W&^NU[Yt\WTSED*#Qks1(8eCm4if(B'9V1mfs5FGgT3GPzbQSg=՚LrO|c뗂e Ⱦ=qMю\>2 v^5'il:‰CNҭ m^H>Od;jzhD0bD0!>q+xד&^m6#a(SKge 2hk&G>VڙO? Egw,/f#~I٫ b/ޫ+7+ی*f99oQщ-š-vc]RkYğ@&Q? .a6S/xU萤,߳׭h ҡ 0QWaU?)t ~PQR p Q0$iHUd[|ۼ }ҹS&D0]'%xɇ^JQ 6@Q"^m3dfUlΎgBC 6Oc>KO^) K\&[}=X.?BM ꈑӡ~8c}#3< ⑓QO˨.Y|ASj v8󖻎Rx"fb;cXL)^%gmۅ_sJrADȁ51,,sc0 a7I\i܅RaK[Y(8![l#dD&p FQ*T# $>kB0ZҢ#a)޼⏍ OO3^!{{w+][枃ۮ6i)-i 7[.ƾuf: ~bltqxIO8UKW=# 'ee7 J~X.}нե;5 i^WEl <<{?_<2 0QJfGj=q5[Sbf+\Ym*66HV$!|_P  jhcC EajDԆlLn5k2Za@̹j#,# G/ƿl"[?AKmѳ6(Φ@eQ₎3χdY Ym*` % ("n2m/hchc +Ե_z1!T"g2xMDmnY$-C˥s37u^g'"-Dzyˌ!pss'˂3_輟(OF>S3o[ƙYD.cϔ>GIB2dFYVW̛@0 SuF`#o:jCmmVHϲ@H utdGI'HhdV)IV (6idS97gZE@VQUYRc?c~seޅ'%tz4+=NՃիB,u{̪Q%E7flCWkg6Q JI+!P4alFS& VR?1Hy$xuhC0sQM&3 AOM68{7nf<߻|"\ߜ.r&dK @Wj9KFCeCQm>jc4Ԏ*z{鿺1fosj|\Oz8:SMq\xr|^Y3w{ f2SI%afִ!p%Q-Q0Q)?[JM5@5xR؈6HK/R";u#ՊnTd>M9k-qE!vUX4: F4J*t8E.cUUYκIQMW?coos^X{DR@j QbdI1aQҔv^ xd̜DŏxS`z ڢd)‰\3,1Va5-ReͲB.c~U[5O?Yxͅ:?:5‡%VGSfۚv8񰌓\a] (Rji_soBGmunɤfѧikxN={Ϻ6HL0z::>oȷ~=(k. ՙ!p^Ts m\)2ϵSE0陗 m6 5 M9=oIN>zgy7OCH\hgi\0("ߪVoAxRS L6z4LC[ (c17V7=&0>C#jbDX`\XVμxpΤ*MBD @ PZMOH~ x0i[9k]r\ۈqb2mVee`GS! -D&ٱxʯ9#R[EalQX0X~"xn*Iӄ5֍'1y,CN6L3; Y?g?}gn뀱$&f:sZ>f?x]]^o^]]\_QJ81\kR)50 x8.a2j2}E-nKv]L2f<ϋ<vVnu#!NNmᔿ (Az+I )2저sU RjSRhK!B6A F`u^kgQ[Pθ7N"m0\Y tVM;atT]+%U&H9AR/ PjKy۪7~mlw0os0^8;؆N8bE[Mru[AwtĚˏNN~6$O^,k'Uq ׳I"EV14F0ʋ| ,ἻG d׸91PA /gHk_lX,P.VPQTt jl+,q:,OѣV ;(c7ŷB M5ƽ6xb(R&I x?ͦTlpTԧuCte]pPx CI`20Z6A@[ 6*,[4D!ͺjTD ]UT۵un9pRs:0Adґ́uRW0x_W?sk܈%ˮ0?^X%?-݂4?}?zsyhx~o2,N!V,&u7/^`.^.->( b(J SUq!`WD;MjJT ^6rtڨ񬏛]oa)J 3pRI1z HKv·?(9ЄXt @X)JoRx%(Ý"\2L{@x9pZޭEc4 Dsv4r=Tл}J"iv`1]R DQ4)iZP#U^('к! .LmE,'F#6Jը 0'Ѻ@Y @x[@ AӖ֘_lW7N`W(sH Y bÉ??NV07X&Ƕ?pEb3fߘW% q+0wA#rN 4 )9ODSևj;s_ aފ̀tm1"Z\t#E g+8lZ3j% o Hո! Jx1Eٚ ^J[M-pq]S'X8R̠ʇ7tiU)X 4l 2&:U qbDH7045iH)Ձڊ6&@5jexهr2j&wW7F#F<o{ln7VG9ph-;kijw{wȌ¾9vXȑv77B*h3 EÇrVw'PKXS{YXs͛OZ%c$&ʙ#>tL-s.ol7(ESs[jHhPfrR0C$"~$E $QYYx$^3@oWNYJ{beXu:kf:ۊt7p24KQ%pId/eJeY*9gnYP: *MB!d7 bpo?bP2l *U$?ild13 ʬpt BVU^z?ּӱc_@3.>~<:8|ׯ}ß_|x}{n$⮮IeҨ5+MAަ?1so@%ѝV-./W77m{n\~&d8e/ kGw{P9v$<I|ի΃ёe+1δ9H, t`F& &/ltjF) Δ!34E/ϵ1:1Ը)\Y+ؚn%G2?n#֗;?0C>` NsW"U uyrƢY&~>}S %!Y"#|"|.t{]#M ]mY!GW%_Cʋa{$OR[gKU&E BbqF&VFMNq1Q(Q9YZ.N:B@D`$pz-"Ƅ+̿'EGv#ƊWen$y+غk U/ N<)L\b|Vq8&dU"یt֭\!dCuT&8%2s QKJ.&劈`^ q|74"Rm-Su w7JT96[MjeQǃLYoON- _淿_dN\0lGθ#q^G-bl}u]1&$/~#܇wʯin}tʨ 7?Jm[k (hRĦB(y+o8ԄG\ue wWp@Qf. T'VTYJDb5D5Mx tL,YF2ZƁCie6VKǣDdk2M0)[`Y1W3H2I7qb!ŷ Ahe+iÅ2c!DUMF-r$ Jf1:l+b$U t7k@,t*E~Vݤ`}iȉEREєv|HpbjD~N_=}ъCϸ̖3$U)%b됞8ĉ@*m]Nmˬ o "%IJXH0DMc;~2¨CEA&`'d6zx҉A@J]Z֑L.d<ܑʚӟ9#m䂤8>?ޟn!?==?؟ZZx&ƒ j2`c6vVJU% 6O@i]iZgӞkN+YI2JfL5E,pIj=@_%7Ka3oHloJR7yoe[~%R ^ NEP[L/煾Y{nSknKǁ򋴡)K[Pm(SxΝ'a~իWrv}sMfW7 ]ۻӓ|B/Ar}}[H^w{p<==q Lejxb'F;fYs!9????z{,y'gpJc[ ƤS7/^[9rg<IZ(3])'yz99c,@ =JC:/3Cq0 eN FdY?ñPA5q̒EW?9a<˦Ɏ3YF46V0靜)3w>~fAQHOT O,]fThH+FY'Cfolp'^ O$,.fz*jZ1bLzc ;Tx(٭rN焨B kƄI&!sTLV*kzV~#`3Xl y͌?;yI(9JBKa9'Ic$Gd'Fobpr8LwJ ;+=732璙_޽''g$+_ -D; lm2vqtj·慯lSL (Džѓ~L`NIHgdӚ)7hX? @@Ń9kZ[5}6+]VL#+A0 $J6-#uuF\,SU9&X>`p8<E ذhf!%AC Q|/><<ΏӉ.fC8|S=9;iSySs>lكȃypnhf&/(dɥ.F2>ⵋ 3T T?d+Q< C4369…= ׭MHqOr/g&sФcYa (cFKgz6Pyq,jNz(C&G"<)72Sw'Vg[W8MqX2bₗKD͌Ϝܫ@[oo]!kWCq I͸Mᰒ9W 8i$'B5G|Y6- du{Ù˥yҚ2Yĸ4-r_390diOI{n텤h2:CJ%?O>ψDrul% L0VB*s A}7YN@kc2 @ƵXgBؘWp+^}(Jx:1#λ;뛻W߾8r}1|֍"_N#6ۛ; .J6e*_z1.n9mGy\)2ϕ.ۘy1[ДbΑҤp ҖJ ˲g~OgL2O(J2*R:EdbEJ-~t]=R<Mr)HR}l ˏh@eP(ɤ_u멊:A]sB> Yf6iˋ ڷj_;u#_ڱԕåȒ4N% giDž07" s=MBz1Oq (:㝹ƫ٘w ,i@!<㉦ErM~ [S"ʦE 1d{ hUO[veцDU0+lZ"q3_3_*vtRZRQZ"D1CKueFrPn.hF-s("YV! ;\yoNj˫o?OOr5ZIs@>Art?`(|gz߰__Ϗ"RI7J:H^k-ofhz'tDU#>bLNFiD10V@NZg51L6Rxy 4^xZU lA~ZŬmS䳒( EYCuJR'-Za Hvh2w>I@XGKr-HU(-F7FTAC`SUyJfn8+noD 8*9`҈^U lBy:z=;9`TcSnJSLGU)0[CqJ{/Y%.m-|<-{x|0"˃ex @xl=Fwd#^"K%6|CoHqE5 }.cuo%timiQz!#"òdQ69e} 0dh[-8bkUPzآ<e2Lrzr"?Û09Gիr30_nA `` DH I@!G" z]٦]@TN.иHYA0`͠NX 4Aܗff*eÏAoi=<-ѐ9ouo]dSw-73.oDc+-ʂ"@f6n0_~ݻ??_Kg^Yd,j֛ye,a.FF3`.Z} kR&<,"1g/ǧǛ[:>91k.UpHs^ loQ~Mi(×FA@@¿V8H ul5d4DZ-::e A/'%@nW 9+!()ք^B 9/lRXn q*9ObK+dڀtKZz46[J<(PbAETK4)8QR)& 6VIN"0JPV@a[7nVbn+HkU9 u Tѷ^д ([a#BQ81O\Q>|1 [*(UAHڔHMLٕcb(=3R=)w'K@@|pї%ޝb6O;u,dq o*r p.QzssCފ?⛛o7OfZӳS2BConohn2\w }WY" 6DPE0d(LBm&yk# (hPD6^clZWwp{q7u l ٪D8?I uFj yb io+DP iR7I$jSnPscRF"3v*Q*RqoUYAq]6gė5obNDe%D?;zy/K:[`V풸im£M"5BZeRRppv WDfR'ވ9PR'* L͕bj5\}#b5/$?j'y:ʏ^@'J/S~ՓM2Ț$WmӦĆ #LQt';uR^gw2'˿sp}BƩtqwڋA<_%ۿlNθǏ__~yyy/.>|t5쳷?OX.y:C l75-(68XubU<)T.dI$\][a_=5nExY|.h=pW$Ϋ -X7E',yCتܥeHYQBX)M }8%@e1VfJd%$'ЪF,P*5Pa lh )S5Ui|p7|@5UTO-˳Ia ت&<`26DrƟ9lFkgU:襗$?8U!_p,@YMՆ`)W2i`I؅sq::wl}ݻ>|>yN`\et&@~BW47_\_|{?'Vg=΃z< G.O+do-fg[ibsby.wAXؘO<qa^кa4hRemp*zo$u&X%+k: !76YݘT[vBWVϷ YRnQNƒan7WrJUW=A щya"b^Q1Y|#JM׌m`7tZMuF X6Jv-%zXيll%,JQ|/ \a-EYӭUnI eE@ YY ;7s%BZQisYB5V2^mηd*zC9PWGl9hV D0@{ :޸49g>NOc2Y훷Yy3}<::CLN&,5*hV ެ@6"L=yêpѯ7Ӥ:AGVY]V%T9=J"x+Zڹ#QnwO2ae#k@D UʹKdKuituρW^}Eۻ˫ܚܝ6 Qgjhhn@n<ӱ%tH;8'4|Q鈯.6M@\,ng4фπ<}kr~|ܛ7/8Λ$_rZ_:Ѕ͘<$SEL~x}qL^]^?9}osAl4|JKCMBxȧ1fj)L]r89ietI72ׯ_KvbWSy*Z[ _Uع::h*^U%pV $VZ4[^ek:aS:d1MRf$.^DHyYJ wS֨ՏJBՊhw'UPYJĬlN]DR8;BmXaQ)XHL˧/R`ЄPΤb9R4A8 dS^Ȭ'!@6m% h^ASy`s8 y-UՊRU[M3DVVۆS;1ggpkccj‹hUj-z\'oGQ@CT)PR\4"bC(_:"XTH)2s 9x7gmgJ9m}?~vlz'Az7'.9ΗZx]n@i/$-?5g- 'E1͇4ftn<]/ּx?)_x.emYsRJʌ,HTUѣut kZNوJE!T)3vU2 DGQcbBU,WPJ9tb5T) I5~P*Cr S2`~%m MUSAJe(n&"D%- 9?΀J/\;&bř0!jgG[K9(l~ƍEl>CWnz@5"´oujā|K1u +T_ FCp饔 lFWV|84?RoJNaqif6)y?VQd'Vi_VyHT epF)O}1MͶ瞒ЊhhU3f7u$Wyi] eYղu ^X3UwARՒ@c:\O^:9yT7|޿RtLt"| Q@H j%Z>qچ5cpU]޴2zBp'U(؀*'|]V}"w[<ľys}_߿{wsk~\_rTn3Ģq ?<|Wr9qg.#/~+^fEGa6glgEBĮ-"ڄqzӛUZ?xx2MgwN OO'ב @HAy@Q%|ۿ`)6̤'CΞX%ٍ^3֛2U$j7F(m+xL~Yu3 6&iEȣJ$Ԋ-P@8_$M y3:noƥDfMn5h?oY!뿦ͫJ-AT ;` $+0Eͽ*QEYDLTapHK A( l3ʩx Z|2Zdm(Q:sPϷ"I!uS8z6f%]%hkE@!J ?$B̯M3eyEzjYV0Dkǥ'Ѥ AS=leFOlTT6"k/dN@=ҲiP-( [n (s2(|; 93S)KGtMw\Xi"exι\hMDW%R|c ̦Dm"oEu9'" "23=6CnҞGpe+AZʬ,4pDW"j(hdt!]ͅ!Zh)Gɜ8(4\at(1,jw:`]j4*7Zӊll9wm4!EUUYڨRvW^z_+N4cHDre">y3f=>P4۷E߿K.\ o?~lV1@ jےھ |gn J۹] r_E Cx0?i{:<ɏwIztyqwͷ2r}Wֆ "(c㇏L\["|zͯG7&˛ۛy?ɱEů~7~+lǙn"R֐ua*f p-3̨{nx>:|حtqEߚ 8 > xGry}(pE"4f>K<:'3!Q6ŠUȫ%5TՏ"XFw KiYE^,EYb %Xb[;^ kJD6mҹ*1PT[óXǎ4,"u[EjQQj Z&KW^;Ij)J)A4 _s!L&3QjBTPY68ve mtLI YҞ^ZLJm9dɡ`TRNRqH HZ*p2eMybe$akjw+ Al'YM`9nwP%*ģ[&r!JmIdPQCfK8<ٹ/#Is>w* Co-޻u, l,Pac?EB%ۜS7HRUQNe3"YLO%`4&>S9%bFK#Շ'ÓϕŕL𱽃zKL>{}}}yyy:rvvoN? ;TS>~e0 - ^LGlҕϔ $fAN< dXV2@Aht'?go޸. Z>=ϰk#ZڡhNYoѢ[fchzPҼ; y$ M6ڙ^R|(nFpbr[4&95X%Vh F g;ђ)Ԍ8Ed#9nHaƢD1 ,"i5K%9y+S醉1s2GilWږItH&*P/s$ ob!ZB\5sd8Gm]J'''@͖Tmq`vY暒{mVP!W?H%J_IFm<0Mc!i&\MZL?#@ĥX̞3̔Hv>z*ӍAj?.O ˒$91MBf KMtĹ5sPbp̓BR,^;\QCO~x)eqnQsR}}u{ww}lZ҃TG!b_R0?\ZNGGY4LܼhN=˷6>s `/3%3@UCm:0ok+(ebU)RnRZUp6VR@UhUSVغ0$p8LUpPzyT7fi2" ^x}{=4:2Z!V j"I)Q8"4#ˢ1+z+%lzkn5!ҦI_#2};lޅq|42;eAY4\OONmc96rts^3~uymi7{x =p^^]#no\h 7 4&Fq>A2)x2r߶%S2ov&tg]tNrVZP6H)xXEhRZ++pDx s_@tRU%T9D5V4TIJ2/YiGTq&wc /fzDb'Pb i,#8#Y4-,\frܠD̛أ|}9>fˡ1HӪ, 9`5Ҹ+X6DOaMڧQK%刻t3E %c&LOH @øȴ=$ӓsQ9x|:xgpxxl݋꽗O SToǐ<.5E8^d>;;@Cޮ=.I0^}4å]#@#"L]k̓Jlf:=eD<,}͔6zmBUBMxڪ K Oű!UU6)˶"UjU ᐪ-=8x`S2^fE Uې7ma|K~'ሠB$Zt`_٢HLlTm@/,u"~2֯5 6inl' bB!JG0*(W º6m"nVi7GL)[Yl^}(`% m]T7@(UgIYH_?%lx)P皔 pJ&CF`qM؊81d,:oN* 3 *9מ(1>K-aZot%!twR>oV0jL8AGT9P^Pr. O"8e&d|3).趵-9wj|<&(s5zqy啃v]d鸣74Ixyy}~ڥoCc肾8/~ nN//w?\򃻔/ 'EDaO=9VrFg Ӵ@oնp :25ޜσ8 ]#b=MMZVc+%BJSG44PmM4A ?[SOK\M@E [ )(p64Yň1OY6ZmFŪ192+AVօVFOIe: J\Γ/ 38yxpxhq:Igfڦ FDT ([NT'Mx(!%gi%бAG6@V%ʃXC` ÕРZ=oY17ŵJur]Y:1$d`nڊϨmUnkUCIaF k~H3z8X, BP#֊8<ȧHaȧP}ἦ*7,1n^|6I'W\N3Fׯ-tiiswgsl ;(Ziձ(DY\ĆP6&rBZ-EUOwYfg??2 n IP%ܛ֌͊j[-5Ks)%*DB֧(Y 20H KUSPS6hHnΚ&ң(/J6([DeD&^3Y:H/RfUȰjĤmU!3Lmk)jyt,>(ED`lS,2 VmmF٫HR{ur ǶqeCf?5UC @oD`T/~V!%Kg$邗R(%:VBW%<+7Hǥ揦- UVô [Uư EZm0ׇaF'_~)6۰W/|*x7Ixq3y9`Sbm/{ͪ|AZE&y7 ;aȒahE$(chR}*!z>C h+#FߚjO&&Rhuu*/ExJ]|tgҲP%vEk+ VR6CLX{R- 5 ަ{yן>laP=; M!RE MZ%;msNOj{ֳs:r峆\;w~N&X\__\?~,77w?;__՛WvevHt)0N3cS9Sxvd_IVRy4@^f pDM1-e fL zk+Z.MeքVvյ_ȕV?~1ZOu[ke)â{;;.zd &.mUe`v':jH0HNb4-I v NQ'* d0466)ki)6NJ &J]49c/柽ckg_)̭e$ja%7/ŢTnT$0}(Pbŗ@,LӊA>DX@ )fQQKNvC6^+&FJ='s#m0vLCʒg/51xoqgg?? {q󪦥g>훷I,nǎ7?q~*7#!${tx9d9f΁s_jgr+ߥe)h0z5Þ6Ϣ1FAYhO5]Mℽ 1:_i%ٝ#,04FIn)e[ V.MmguU1K K$. ()c5MbDN;M(zr϶!qdp2Bnnnu#aR(6mM,Zg~lTJp]vɑ¸0ņ0˔ RQKr(TrªS%,OuMYTbժ$-* LS-ӲeU)eG0-UAK=>c5``,SeVH78U%$A,*eIDZEV䤔:oG߼y֜5&_P *4bjWc<~%/UNMN,:9O^(+/ag*FH ^R4mq1̠.74tx:vQܲ#*rJ!PϖWNV77Wo~܄~}֔WWwvM}//u8t{G6b9,_o[hΟ4B@1#2M)a$Tvo?h[,ʮf>O'G_zm>DEQ|j/"NNHL'N1ZRyX@΢7SFX;5M$IPd)#!7B*kI8Jzc 5" ~lۅQXP\f 1J}-f8%[&0 i:$1&0 [L'pzs\Yk L]()v`V/ @o'kI>;iH jMat\`d d/FmTӴ(4aΦ|< YRʕR( {vt.!o:ToB()hk$/^Y·FRZ|6ۼ*,wr7ZZXKB*,e՘ʑr0'?. ւf7?ͻw&~2tnl(77A3όyxzx|y~t|N+<[u5.?DaȕGoWE;p_.a EYPcdt=9>~ \^D$% u14A RIӈiBm5B*BT өT՘حv_uŃXqe ʶKm7H/j(ZkNu _p1ksu52/]ZkSI.H0K9@Dn!?,s%iW$/Ðt ėiJuI=>|W5wAԾAG *ʔP]T!´Vʴ;Aj*)-*"u _L_>_**QU͌&JPikRiMÄoK D¨$˔pVe.) t:Kj9܉)%źeb Ӑ^xB6٦CZ .&|wwvOݭ~ӟi#Rl1`JT_Z;&$6Sf\ K#OWz u4B>, 'z?mN׏N!GKGy'yٳgNkWW7ono?|9 =yy\Q:99|uy;/|7;tt/^ǿYPɓf;X|q,Iow`찔0~xo}s{s;%1O:0$3U:#F>mc=}N#0仾$yx6Q6yK뢥RL#b,y,J2FOa)&]i@w3AbS£drb\Vy3)34(qנ2Rbd.vEbƥTf]k$XPTwxi1E0cGJ{vh' kew17~j;9Iד3E}|b΂j~Lr]RS1-5TXQ,-DrxP ъU||s9Ň7͇Nwe>=sy=RmI'5n/>kx&6sYFIP%zn$qc$*PaHGh$xUAZ%5̮G_H-GQ캼$&j->bUG{ūh>AsRpaej>$6#j:29>cG{"w&4F<+xPuηRYY|.8fΘu0Ʈi m0yR 'C_ 7j[}G,ncӦ"TrVS%A_SVXFNĆ*iU]Wav}Teߦ"/LahrD[/cwM"ȚҤ16%3`:$UUv_u Niӧ *b9?,hw,K#7jc&Ƿ;[ NN~>G<Ϝr҅ڡ,_j+O C.I_1efŦNxFA/U\0%0A]m//%oi)(a){`U!UKfvO%ɳ|#t%>*:v-lmB,(ׄi M/(i %aS$g d^" Q0"c2*pف!AT'L6AO|ٌ DpvAxр[Zb03[ۜ2ˑ8ud#Wm+! j+2$5{xFX-N Ef`ZglvҴ~#*f9r5Uv!0|md5%6!HLiUaGF߬~C3wN;I0L"ɿ7@jM8fzzqxыӿ}cH?;[cV Gk9;G77W>j<::qDM[{}Drlt#q$@Tros6 Q5Jӊڶoأi]IPl YVuq=Z] I* Fm UZ,U$4YvH"Y|0e_w2IDATHߗ&|g1V%ƀȗ}%«6+{*6|5[52ti9=靖I4քCc.Prj2 M-U"w񄡵 @}$СE22d^DY#'o+WV<;z^r]FIUdVHDZR)T˽14^!ĠV1]̇b 9S v?.pBNNKAHYuT'y۷o{/g))ᏏgەI,nOgg a'$~ #ƶ4馏WW?'M o;ՂLInʗTV/2DTG$gMcC)r]P%GnN FŬ!5/|s\4g4kX! Lxwsv2FcgWq߀1,^W>?Ǜ[%?|" JlGxP>nnI#Zhxtsp2D)?grՅǩyU f0k]EUm}cFl=;Eae"x05^820L 7-MqK1-֋t0n= ÕYPlu!vi,0'r@Xj($i%b}MLKf+2Q _$)F8i2Vr娃R;;Zvku"+0o^TNU1;T[Y0 bTm$u3{AV%GufFbp1%1X|m}UgUe}全iG*u%ErBTS+{`U] ]Z?aTCsd<{bpov5%<:=<W xdݳjTJQwz٪ĝO?sr(%on+i׼/?,d y RrK vyzy4''oS:o0,^Y*(k|1LoMr0%LnypYG"3BZS6u2x'x0 tGaM.~xޢ;lZs#HsĴ?s͵0v''aW?^\m {s᷏E &\ ˔/T*%0&5!&ʬ͕Hşr1VT]#?&[v,5|T *ߤqmmCQB17+`BW-WӯW1ru{ϿÏ?xҗ)>vCxHRv4$[dU2Io^~yaj~hv/tTrb4wM\l{ gah|vgb!9 M`"y<8&9p'*9^p$,M05onr3 E3/@HFY>dny S3 I!akMJdGVeG:f=V>j<%p~P^b3n *"QvK3Lt扡0@+[[1Td1Y6Ќ>\/s1Zq&<Mز2%"SNYE#]PR ;Grx$tn#m2+OԇciavagW j&zQ {" ߱NoJM[s̵ٜ4p1;)X-,EJ+ Zdk~I09fj0L-"Guj! ኊ$VquџYPw0 .;5ZkdsZ2]?IW lGfs:Y_XD?|p}}57ː']Zd:UF+34L{$,Li1?1BbH%g3<λjHڑj[T!"/iki)l._DWRju}$R/Ъ0K{VՆ6ftr!a iU;k 0T`yl0eh:??5P_2bxU=qqv7fœ7 a/NquC)td`...~XwDx嬱_RO_1G= !+]GѪnKuD$AR/Q+][OtWYZ%܅fFYfdV`߮o5rִ y^|?^}4/e5Oן~!o|9r[u$)ٜ)OΗR2j"q!K'٤.+!|xiYVM]B\HEL(2sIlhvAt,nN18Kuk M2V}s,fzA|Ymn;,;|z1M9k73}êx֐:m{T>2 UHO^b5DjZG?M gQ(d1MB.EpՓDOǺ\$D{-aYl?}LҽN 31M5hԦ7Wk`Ia4%.̖C xljrO5UXVmRv_Z>ӵגk. #7y ."0薏]z/,n<=Gˍ ^Bz>={:ɷN,0 yU-^ҡNDo44@o̍3y&>%Os.T9!L[(HFu~1jy vBS#xT]VhQT%LY|BR-UTˠ* K,43Q\Hk.01uHUjBTbA$Fh#Ev>G,YReD CnI.df%(Ofq>l;fh4V8r,#h+p=EZXS*oUYu f%*h_dU/~PI.]$3U:UrE̢!/tB_w>ef;ewU!b`vAkNëׯekm S"Sls6_`-6돺dF("kfykߖMcǏ%rUvoԪ%4~ȵ" ud"QJB,f^~Lޖ4BBepbpV-Aep ek1ZS^?{n;jډ)I~:g :k٪%57G'48wtQ{714f0v09a_U &y杷~@4*Uuu}oWeTH(ufa;KsI &mٮa66U J`?lӄi3t$~%$uM@JQEzhqڀaeBUdLg^'GLeJP %O8fݝza$ѹ _` :DwUaviDX1$ *Ő^67`qU ;Ú̥iI:3^B"D*-#.:bTV='Q1AI5JW`<̓A9gMpb_Ru$}Խ#}2` E}/lC1ةeBj1/"(**0fSbV6T1ƹ-l:M%nۺ&IO `>u|K>1Z|_tp|s{|r|u9_LgN_ȒZ'-w''Տ6Lz6|z~vlj﹕bgB!k w22J9dHX#g<3Ⱥg[9C`N,4S$RoW[nMC,gNHmEv%",YzQ!VI\xX; .ZFU,S#KBe F5lRi٫Q`7-e%T7HP1HT`ʗiJ_owO=IÙnDaQaTЪ*5jj+Z<$G܊~.?\ c T4<]Ȫ}¶x^>6QV.] -֣)g61/7ǨkpC1+lQV?sMz5Ҫ0K{ZeUey[կդw\r]= *TlT?7!J;wμKd:B^ZU3bO6qh|x|7ـBt ߔe"~KanٹdZĎGHrwfV\ggo՛>??OM?pj|nr<Ve VKWgWc~iMw~/|P50% cosQfrV""C$lM. ̱mj {̛9cāfzB0.%:F(5#\b/!wK Zˠ^<'ilղ*ib7(im5%b5F|&#|z4Yi<HRja .VXW–;iTdjZ$iS,`˷" Ji%eUjmƴ r٧0mI:fDr_Dg1GC5R{v՛o(!;7s!#/zz|8=>>=={s ޳L<yt['G35$4}7݉ק\lYH¸^ kPV&S,gA`Ⱦv/ wJʣz_5 *P.I1.u%vMeajrI F5B'\xʶZ`|Hm*굱,Wٲ]K`j+',S[s >SM!@u+JP)V;Xީtww?;=(#.72g3 N)lgo2K5[XR1FEm/ *o+&|-,ADke:]YRcP{rO^i+`TՀ #Le~Zt` ٮk^Zc:9R)2& 9:;;Rwߟg*bfkbJFNV~uu5R+_ V3fK|EM*rT"^)DDg<^I:n,w,|h]UbHۧ9 ǰ _~h55]x]}zdsbg/Zyx%*ɚ 'nz>￷ϚHby0 nqWwb9tW@pD%5FO οmUiYE 0F+T K5RP1PD unH$`_;`lQ2-#+Cyqx(b$6 eDv{'B+ Fe=U6HZc0mnQr}iU+ωaXMHsTb5>}hJkp{JFEYyQ:cn4L,ta!YKZ++ 1H#2 0)PU4{Vzsi*XKUv`naedI J0Z8"2խƫR҄4FtKh!+Z(S!Ac&0c_۱5{1m׸'u?dPŧ.Ӈ{.._::t&zMX?Z ]}? C.Г#ٳ[LJ;_YXºEhLU49g{fGHqI\p*wy`cP/J*YNTŴ# S -uuIvm* kZ"\..SkZFe#2Sƾ*yoV”uZ2Պ'#*!|*\ش-#nIɾԆ?jg5EZ^e%e (oYꚲTPy3GPmfTū_ xT60_ݗ/jVXɢJP-iB0UYac4Xh~ "cvTZVmʠ,qY0 @u[M[4)jeYg[h}ì}g/_dM% !>Xx!

~x;lSvvT:Ei~PL<[ f7ei6Vn!0[- 4ͱ̘.q]W;KNRoY n|Sʻ˄,g0(VH◼+ UT]Ef!gة\$FXaz;BRdUF|k iUT$V:Hd3n צJS/?f~﷖Tk] jo- L {IeTdZ0{^ acʌ5SuW IUn_1lإ}>UQ+H3@X f_$Mh5-Aof; VMKH^ ܿb4԰[\FZ]B1nä`x^J_֚u+StV[MlaufOyW>n&:jS(U9Cj"'Q+Sbx*ocJژQCgjg$H ԕ_@G]z[:oT+\%Ua3("nB䏺"uG݄ 5wt8 b}{vVmي8R)a>ڇ'O40(*6N%IevVߔ mWL_ҒͷFЦ#S2116KӸI[Iߠig*<̝#SD'H}y)f(d"q>՘Tt@J)}n+ #ecXt:$j]e,וO.(ZS$IW)fp[6W6u\{||X0oz)-ZPNf 7mh5$3@բlOҦ YӈӁWXv՝rф)ز Sjy?$Ֆ5KJ2ə[#҄W#BZk`1`*;t(UƐ7f.]U@x[cY+`~_~Y3QW3 Mp6aD(TWNOdϖBKC @lSk#M6u$7_CS2Z䂯ʾoy7-MxQ#*,l~ՄV.4 *)LT$mEK/]L)BrHsZ\LF5ŊW^*F7ydJ\|d[xf# (GWW9UErrHDw$ZðZ>?E ;گd1}\.m {s&Rb;/$Q°lDꔒ`fO{>s1wbn֍lcb2!bEmʨ&(mJ^~:49aul7OGTlicɱ-9'/4=~^4֙hfl0/C)JQQiu . }xA:^}Bդs`1Hڂ,"2Y0ŗ$88t`&nLpɢ1%9|KₑOESzLwB$UWHji0;Z@q-vJ0=9.</iid(FRBr"U~u"e*TTD)V>@^/`T:^voE#)M`d_SiVG>f2ڒKjdy|Oſs#MN^<>^w<},^zps,:ҽ9= n9v5c6Ҕ77>@ZWW7gs~=o@1œ_8|iu^w?taCeU,)b #_<|a%2YpUzZ@ jm#%|UmE 9ba"hZq0c JӄR[~ET+ UN1&Rb}#c<ǕKvltmlH-rW$J&Xh$w;>:v#.(/T.t,OQw5bwkF J4n^c}h"2ٲ"f{~ҒF{2BA nx鲃YDN,TmZV&>kJ^(;s1FZ=iQufe24=4G1[lm(Ql]L^]^^n)mU.:M|jžd|ʹaYuWb"IZ]* 2VRگU+;~CQK _]lH؎Ta_QCI6#"pBҙm@`ZNfN )gmaՌGz?6''zjk˗'ǹ9Ǒ|\jOkտx5_EiZ7 /7Ko߽ `M1%,0"LU%2FH]'y]u_I)1O/dɏ(YDɽ竫sʤͦhx] @f&o $€h.J sXX$=ݍ#RuTk[<&ROr^kPo=POçu.MP ;~8cQ j峺 b>i(ga&OV NC%[am:ܤ3NkllFH_S$ZBnWz/J&@M- f tls2\kjq)*,U䂙7~=vˁE=rLR2H̳[L,Yt}6߆gVLJ냓YR̚~s1׏O/.G98|~ۆUgyd5/o<=4mAV\='O½#{c+c$ x49ݻ[GA=5'PeIW`brxT8bԎW"H|)d֬dDBZn7$BT Um` U0L~gb#?ΪұeiC1:fʧ=:bh*TVbmz0kUx{㆕e` WZ2J|lmip"/8kNK4Z5z#1Et2#`3Q3I0pCsEFtXIAr?2vk21c-b:7W.yK9i+YAyJ*D/S~F7Y(B -x,QϽ0CsWY~WD=61NOϾI k;`xS{s_H0ӷ{Bo ׯ_nr̂-!hI#B|%[IDuSeM[&ݻo޿z\GA_Y>d[9!|!ڗwZ+]yi4X哽dyʣë/ON^ѝd_֐c;Rot*WBn:@t]YNׯ_:$Y&d߈\Wu`wtt(W #c^d"q9e_t:8d4p=WnNh8̯AkN짌B6N̤V.Y?c$U<8RA<><9bĞkKs%Uy2God K o<C>={|p(]y2VC`={ih24[-H-ƑO0O_gY ,>OkØ.ۉDVM7k׶#*# p0(. A93Bwa~{[^ċ){ϞnwϤ>Nw/pjf#dU]ڧnF/0x~؋dfm먦RD. 0H!@;g{GK-c2T$>Mxh6GHuEH b`NZ+b5pI͌5>nӥ@ PH$KuͱSA09&|rgwHͿ|zp7^ҏG.3ryy#v򗗩|;<͡.Nά8OBsua'y~ 6?wmn ƚ:cG=CtUC*1͋g&7WwwnOY^s$GFGl,(fШlܱlZ#p&*fY(.p3E5ئ,QVSJ<2 }h1%M%Z WIQ]zK@jcO>]$XC?,ax;UN󄲒4 [-#<*V Y4ɪ{]u%MB<(ՅĨU j)K^I%|[\/aԐۊj -}|P߯TPXpi4 |YMe*UKq;-pT6'2n_Bq.j/] t̰Ix7\$xW^HoOiBZdPN-kA+J;^SNkWsԎ *v(X@!W,QMHKx]pBH%aMHZUDT(hP9핻5?B6/e(E {ҡ)7LK11kvu} ʹΎO_~u~+Vz.9|pы&'B0|cn$7-^Wy&LˆS[|{ZkIńW)eߪhCZT(A^Ƀ#gN\ VPaPnmss +Z, ېj͟) Q&Y*F /Cɓ*t 3Qf9,Xigq~h7ܼDknf&IVdyKHX7v Yʒ!NHU1kBBct/ r6bTk5gP=Og㗺jSxGT:o :S0Z[ s_y->9=s\_h] yveE>>{7L:pzٮ<:]J#f%QNony W5}sfZfu8Wwm*y~:/ #*o)?/l~JF54?W)pMgg./}6vV@5:z$T&w0;F&r0 i'WNNyU]d]24ʝe42?r\dGnf&BR^ +3r\wԿ!nPHA1-6uSX&LP0DI7aO6EQkJ@"PγyF\ٶjUq!}Lc(:'kB+._]uX7O1sx[٧pym'ell>|yy||Bh26\.+OyQ~{ޏ{pbTU* 29glm )WXu`>DŽ 4F$EUZ*'Y1GE. %L%soQj-ZXr$ _7)[-,C%/>N6+;T@PA5̽V#FZ6iX8^4 CQ *#}J,Ʀ=rG#cR,S(,sU-^G_F Z-\W2-~"pI:RGE%̒AUylV0TUǔ*,x*JRm;b7w1T)͑ZJ0n;~yYNODnL1$sxphk ㇏77`m6ǖ394Ӵ{kMsj߼ {s1W >ΩFN76[%31Sӿ.t[ҥYLѠg=qrIRa} D:`ވ`XbTPiƛ* i+Ue +AFLʫERyeH>* _(TUr5;t%Ր;Xk`5TՄǨE\/0R˂h-*G[:T|ZTօY#_vmbT,`IT2_V WZ''y^}v~o=?,|fZ$&pR)C0\r^I/Qj7۵{>ڠt"ebꂤZI3LQ/S$Se,ۊwY} RJBKFBvFJZxDŗKj_Yd0;´DkMUycv4^kj#)L b ZeUÇ/'r}"6 I@NCZ`*arUB*}%, /Q}OE+\ f+]gj'LJNK:*nb41 rNgMMm0 @F#TnggE//ugR[mT y#U'-r^<8;Goq}[< +qyO 9^[.ӂmɮ`wbɯ'0*h 96tfj^=bv J'gNSΨD(YlMMFXIK1wLQP5 )Ovd!,Zv@՚WoZ̪^u䎱RP^+HkicHػO ,=1/R)Ruܣ )PЪ%09£%U,d潭ް??ZݾQWu1eAzJI91FW iDg2d n8v$BKPEꊿz' s>~Ɔ?(e>Vd'FX^؎^yqzq .E#ɨY.zx6SHB:=+L79n@f[9gu`sFon:nc1;uB* u֟xs_&#Qj!jk``~ߝsfNwڎ;B.O]i+ O E&muZɶem'JTĢfusdrFJa0ORv^;LFٵn(zY97IGMeH$r8QaJ(9UZn LJKf6b2cu F>Y&GW4Ok$O;/_E^̓f YRm~f;Ȉie"3ov@iw "/>u*n\ dvɓtUlJĦ b; Yw5]E9: <=}eF6w|@`&̸-#XhND\1 S*J}y9ENV.xe1 wohpNU&e*g9r PU#,E$]j|y~6tA#hDuN>.%ч^j"fM (WQ]ͯ0S߽~1۲if** //R 1`U_PMux򿧪/C"` XVy*޵3ZeZ% _y-WƮ x(G upwFav3]w҈}͛~]oA,ErbEwf]%QQ0ՔԹ.ׯ/|=^nԗR_MeC݂KV/}ܟ3`~k1?Qtc,ߋ1g[_YaD'Y_w4^KtFJ)2dt.B+aP`x a{3aJ;|8¦#7!FT-&BTAdM;_Kk6Qz|}rKw u%LZ,BT@^H0l2dp-C) $Nڌ FPmJt=J2ո mr61ȼlt۵{5ׄj-0*\2U|#5D>ͷj/ΚӃH|Q=/M(vwڴndOpu|Nxņp!,>FJS23v 3l&w7W?ttwBp"mG3N?}ëan;t;Nf|ӝ7oZD?>SxGx}ӓ|ix"`Teeȶ$iˉ(;l|9USP2egW`\9T]*"yr]5fgqG#|,c6$! Ly JkakbBK|Mդwqv/`^hy (Zu%²R%x%R×OF#8 t>>=cg19 qۗe0ȩlۦdIrT՞YxQ36]syׯshQT AfI)|[ 16Z"/=E})ƃl  +aP%qQ1 U,RYZMh'av[YHPk$ vvmZfFo3Z'0ںsYOE-&吙R|kK~yo?|C8< \ɦ )e:>μ7 -GDz{Oſq1NNm<1y/Q5+W+ώ:oߝDS|qS|Jθ'I+7˖7wUӓ۫+A ,qu4RR?9:mxHO")XEevCD: e%*+51nͭDUYpV Qx% 0U 3mVJLq8&TJtVH rP! $6Ԁ\FOken ^O77Y:yt+C l+ځ7׮@%N0#˴b=w t[XL5f_4`ܔ)={\w0R}`W=;fKR}UWU;`C )h.Ĕ3Md4LtT$ir+Iʪ fCHߏp-gw[///6L~5=kQ<9ɏ}3B{me?b22 $431W5NW5\W?2E4ČVy>BEfQ<ʙHKĕ]ZՄiB`E1MJre;Hkat'*Sժd+!Oq%ij߲R̬H/ȵl ^k՛UJ <]fqߔwRwޥztb'p{cVDߕ9V r8Nd/2mC+q:vVhkz XfLKJFέ-ՌO];BoӯY3BL`C\wsgSƛADZ'N%uM0yh5.FM 9A$oÉ\5Db"1@dPYϣׄjZ!Swg:M 9~UOcr2N)@(uʑy(;Y35*;YOJ-9CZ"#TB4]__=o>8%=<=~{\SV/O㗧޽z͑>V۟\P!XA&'|[޼~}zr&fw&X9gdA3lyu=yEV7KDa2T&%>ZJ1 kF>sb%$>IuSf8v!x.61TN0QGK;׹ PJ0$uN3wH%Z[f$OILyv4Xa'S텅|7"']/(ŗ) 3J!67n 3/ oo>~xxwD<7/N9*C]' XPҎT[..]]ղfKRooo*Gv0H>3\{Ц2ˋs/!Ĭ%DIuQJ)Uv^ v0c_k!vgNۅ p4T]<( cCe'j-9X) hJe^wr764(vLu5 7$2a'}O0pzzr~~ O]smV˫W>|\y2BЮ5T!MV*4jj"M+|E2:"xL !ouZ*—jĠ1BKa CKqieUujI wBǬTGX!^7?9HU)T4׮Sk\)KCl'Tg*FH]_1sM7@ E3(0#M&+y[ DOn:@La2=` 7fzd3MH T6#Z6 pI3BJ(?$_8ڙ$f24g{,Ou?\.؋E<=^/BT*R;} FZ)~b4jzږU陣x0uBB!Z;g|" $?Kl-L4je# P3F䬑QNsENH7H}Z6oF{l[7t5vsQ^_ٗ _>v-Z)Due~Sw߾|2WF:(d3{yq+QYC\@w?g~-9ã|@1/ ϑ.Mv 2R9aK2U>gQ~fW])OiȬɖNTc3ЌMhCR lV ZZmjU/SX`WP2 I댋&|[FX `5X^(U<—vYm]LioFn`u|k:JYwx70`CCKSj"TcCuO`}jSI??%gMs^:byA\ ;u{r+7*dSuԦ_ @ $#hklgcS aP^O,3ieQepkWeCB<-#3#El2(]H겪% A2f1&|=&#$b~j_o/*GҨO^WԦJUN +SIPyreMLܧ*ꮌrR`*~y0̒W]1K"]ހKJ]ֳeˌNLm6KۿU5Ȧ'g煓cR^F<w틉D^_HTA ``Xm_%?-ʉۖ˳|%qv♺ӘޖQɾ;==}rJD%&nB;LI$(Wv4٦PN!w'$\i[5"bNd[h 5Zp׸kM '4)UŴlrʬ'3sU~H5I:URIPF1QuQ~T +5$H+⁕^ D Mѝ|c{Th!(wz ʶ͡x09SRY$J!aVfxͶߞ7[ !FS]Uk/nc/}0JKfvR5f/0 `aZR]B7CC$-!u\lz~2`$1l,j4\Vfe)ʣh[?V yW?'r+]&y=]?)1iߚJc3:k~Ϋsbybf$̩1(;c}Lveʗ!AIL% 2r%a L¶{yw<cy RW2>K6~k$aw$bk#B̾`SbFcNrKpYM?~/&9k%_ub FKX+T\ܥh>4f.YsShnD U; g0 U}"a%a"YZSlQ'34+u5wC/ݡ*hZi8˔ vbb kKh[3 H\V́/&ɼTPд0,꨻@UY1+uv0I>3(3չ!&w}!9ji`*&T52T#w< }EMʭ>QU`jy5KkMPZ /-hw6a"|-8-7ihj3Ob3˗Wo޼_n\3sgˋvMwzׯ^c4Dzqvz]<,oSycgwFɀg(=$*c^f=#6+9iK#aE(3±QEL.QKYTc'qDc^fV+^meJY/c d, ]R:BvAHwG k9w /%aS–$ReMET9Hv4&L猪UVHCq5mʫ1L3aP_ Wňh9vMTR_OYacx9&C>T!9h%Pa\U\d7՝*ʹVb (X`Lqx_m+/b;l.iɀ_H<7(e}hN5ÖAb3LkdiR/҃$]eslɋGxkw35 n Jw \.?~t3쓔|y)]w7Yu5ysr޿8֓ 2BU-#I Zjp0O˅H q:"'TڲGSE^Y \ۤ/__Ֆc/ĩFn0|DxBak WjR~sAB"UTG¶] ʕ`?3،/@hY㢘%iY[E<dzTLKr Ȯ Y;)A#˔%0^z՘+xa3xJiMB0xKʁ-jg!UPҲ@XAm$JeL%J8<  (/,SG1*35D4 Ù3Ii᫈ANML?q>.fad$o^~ͩ*ݮ'ZIƤR Ի)Sl٥tf]mRb//Ύ4tޟy( s %Q2509av+.myrѼ?%*mWE.V5)q֢Sn w?~y6s$[ezgmǏonn·h$X/Y^:~Ebz&ƒIGB[xM7EkcyfŇvq+er%*BR1aUBEhmx\Y!*ն╪̪ѪVԌ>#kdOj-4ZBNI|Ց*y%Ju\w?g%#T,O{\,Z2Z50۩zQ.* SHȰn~a@*TCK $A\~{~+o5FՄ1AD"$BT#xL1 O|T\3''cT468_)z7Q,#5UXeq9ĸq^ TM*\70U:5b"}/ { f{xx.ׯܷY&͍ jxM(Z"$$m+FVj[~벃{.Z. x~ۊKO2*iIe\6dw2JyFQ#q1sm4/k>OQIuӳӳӓ?L_ys{k 0 v6_z~)UxQwαCbR$ϻ;2<~,pn4 8D5( 5|Fャ| &!?`)q&?!3T&3:ai;v;0*?H]yt慾1rKb{/,RO@b; \g*fK$UQmw/PM]u kY )ɂ!rSU T|2 R;:SVoLa5|fT^^Tj>1d%q%mhVrbq]& "ym!Żd3mD*W0rZFP[kSd-#QӋNSm]|[+\jRк*Zҗ+/b9T?ѩ,OIC >τ $Q>;|,_rpxtz_%+NG| +דӓWono b=:<<>:Œe<[fQͯrdR޽t'hS&ē9uS3?>/? Jg5&D"@a]~{7_9+o<11an,dL⛩xn:2Gc CH*W]U]P./fۺ)15apL'S̛>|xno2Y _Uve;Z Ֆ|ś{p|#3F;W ZDj3e(oP|8ީugjgkM/!@hV+VU![g$TfVk 05)R ~r yGeYik|&%Yzgt_-g ߼y0Z3/9ʅJ xeG1+ J-/ךL-WVZ[fR%|}-(@r2ќ WPC9T~&/}b LGnp{y/wNFwG/._zq~OV3v?4߿tgyL+ &ȼE*Tvy'g:\<=!7o˜͎sǧ|&pv{.(Xb'NBE9FvYJcU׽.tء(vÒEcE. 70;֥1u^h"I`5q{Ge嫺1h)SB"2haڊPk#3*!T EY&EfM1Iɬ)aB̀S Q,_;Miji*lg6 YcMO01` ̛l\C!A52| p{*a4#1y/nO1nN#x~0Y3eTҨ Lm]IoFXSH$(r4*l,y}-eMb:fV&x*.^޲InuGQ;藥>9[<ܹ-bltt6_b©pV{zx3Ӓ.'E>_ýKٳ>><=}: .N!_]]ct&m s'֤cu%0;?;$z44OH$f )fjln+@"rv0nBL҅0Qm%Gbh"T-S*5TEy, Sa-`*ERR5+_q&H5KeH;9^_;ĚZƳKڑ*\~ZP$b}]$|hT#Qmބ|}}looU@+E h9Lc' &xoBVib+Tmz2;9;'7M Q6(Kn+Sݣ ˯Ҳ9O֚w6"vdSS_>vSYX)imvWZbˮ >2ubu\C/3+͍`j=*񌯲`؇!|*! 6m*%)Z;Zh|1~1Ul.}QO弤!Lbx :-c|Ǐ αB2~ In3Ϟ?O?˯n^}__| Ż<:}̛&f6:'K>鯮3#7_^XpDG"OvvckįWNtrjY?uy0_ &9= $_aw bL!t-[Q'Mbkl]v7m䦭2vMRvLPjDL>&Ji[sLkM3_ Y`Lm[!;P_QYXV[Amu(WNz#\x|e "%3_IX+ޤt_;9oߥLPPpd!Nr+$a$ NN8 |B(T ;$*Z:^[~L&jbtE ͐R'6x4`|~|rxS5NeU˗T0O#nޫe3DmUYux5 y;O|ۊabƬa=={Z1 BYbItd(r 7k]KTfk:/="x@ǔ*o>d\/_Thwniu\kWX*ֈ_X&9~j9ֆh^ >=fY,i"MIY.ߛ i̇ڄۤ$,_a)o`U/Ղiڒ0RW&կ\hZjKjUh KٗLcMI 츸8{ٙ w"TݞnnNO؛z5گ9;4/Mo޼===;wC̷ᄑ1qrz}u7O6<۷3s#vζ~.HNPjNzSy6|eiZ`\>s^SJˋׯ/^]^+KǏgo߾iϘx$C=`ހ5ͨu]B8CL`tGS+8yVc09an!4fv.2|UNb?lvַut,\xD`$nhþmOw}ZsK<,m6F9`(orVR5h2_wʘ|>)ҝ]UY4Q r 7߄%Ez# Ѫ:E׮ٜ<}2h[ELW4D({ZK*ucZjB5K";3yܦ$/LkS>83 f999OTY$f XiiQOh֩aSdY|DP_fA- W+w*l4!ʬw/9W~YHc-F~g]wxNj>Xff,,4¼m_~~}yiٽEg'sy~9Jy_:9zӋKP7mզ IAɼϫ&_s"ԗ\H忪&b4T} 0F%|b<'ɚWK$Cdf愜Wd[cUAN C%yW3pg<5V ɆYSh7F &*U.",5ҏ\m5'lqo+#?s]7 |C}k"7r_We=`oY2-;vvX9&v+j 瑤MJrjۡdų0.Ԕ MJ_pabkld+`|TSťڰuy!02#u Ӳ T?DZkdTC5πiZ,RuK[v6LCSifϧ[vˋ/OO_]\}ڌ99>~Ņ[K#UzJ1ywIym0$-wk:uFȘ<;Rͷ߼|}_>/!9g~뛛WWwwGcPUiYɠf>'n 9z ]//Ν.ٵ%[J;jz͛3huVw߿3%3bv;N pwz/N9m+VpE!fK:*vAQKn Z C boæ-?כ+(o O{L&NQxVlH]Zh-Yղq-5'q|?V4aS>YM =ڏDgk7/Lwa$x9ܞ,L{]pn^co8Z ]PD`!U0?XhP*2&]vv؉tD:CREIix KR=h$à<ЈxCm^z5*Z0O$(&|X.2x$88޹nS06\V^M)?KTy|vizZ*x?= ;4|`#[=qQ9&O5;-$}p٤( 7ߕa>h!wtfB@sNUx .$_D,3w*wjÜae 2a6QilC5$'Aq4e &A.-%WFt5^˴*IIhQ aj*;[xL \nm?(^CѼ*o|v3_7vTI/ =ZM贬dCBGm KW*]Ѫ [fĭ5[Gl fw%`>;{q栛 [) *L -@% _$-I:UbePT^??O$x}y7o^89r!?g󳻛 as%Nc)D%; {(ß=y7o߽y$zIzv}}/[1?/nW7<gKIr5x̀c#q^ Gw8xrʋU㵳W޼w?>9!C3 a8w2 NաuxHET6n_>&CcDeQwFSo,?18"Q5ê}Y9e>;#' 9ۮJ\F%!M30I #yky87bB&xvB',ܙ$Hi3ڄ! }"Y*LR9 O:++j|N"Nfz8qH(A:7?Ut ӟLR wl 0'Da2YՎK^fn<6tya&H/1bP9*a뱭EDI*w8Zs@o OYIF&a/.ZmQݹ=UmgxT*9Wll*ckTbb cRvg}{EH W~[y:hMܘHWNI&盚2~O˗7};9̣k!ӧGݷ}w"sujܧE8Z*oon+/_^%_]]NB)7ህ|ޑ;>މH礯Km3MR&N4ѓٻdaFcaNj J3ےYp9Xyo>ȆVM}:k*Q"U|UHGL VDY溁,:DeiB\~~Zs[NsV3V d wزUS2 滶r<,:sg}6F7 ԻY "x6sr:w|/_x}??2?\]g!*otl>hFŰR!PS,M{eO`fd]3xWuM写$z}~u|+kt~~J&rÍ|2+`7}J;Z-[ʿL{B*ogJĨҼG833 ~WMI]s0J&AiҗջU,?_03>\ʳ|jCtJ94rD#IjEY4 ThV>,/JUkZڿ5i$[UA.=0[²VEDeZKH01e,>Z `EQ%v1-Y(u1!LCE R00#OLt?qIDsZ—n7k^btOhIjnUZV^^խV{lJJEl 0G'Vƒxg:9>Zno]=LRt9_vL^?~:=|qb|i~wD"cwif`uׯaH/(6\r%Ƨ{l+8kaXr 0 ܈J#QY7g8oiԝ0o86tF$ ;tWNNJώO.O߼|öˋ So?|0TusgFeIy9<ܜV^(g )<Ǜɛ}#ҽo~+(in8=?sɈ4Hׂg/ kIt_[Uf]Fz rCaC_&OnfV`7T &a\JoU˜Z5o[G7j2jP0`S5i1:en4K.5!Wc< Y%yޣ -a?JSRX ^3 e ķ0 *>cWsd/OgZV^kq}cZߒ20P`U7},*`YDB.*%L;ۜE87v| M9Yɩԑ+ž4142o-ӛcPŰoъvQTeB~4Tk>U)g3|"zOɘOOӹ뫏>NƷ> u_,G|W}Y W<q{vY_zzs")fZnwI.H MZAyg\jG50t#/uoQ_M@($pZ5)˜)aH|KiQ"vtx.kU.YEZe0[KQߧ"6 U @0^ jmU9w9k3EGqU/!lm\؜%OQ>* 6}IA&: iU2k¿HF 1@ ڴuӲKFI6:0a>riRRAr]A<%UBD8Ww;jUY-#^/Or B"MtduM/-&aׄU: q"BiaYi'>0~O͵]ފp֫W!MK3VT#ekI0JQ.L}R%eYR/S*_L+Hr.¡lӽQe.M덩In(iX SF7?Rppꕓuq:?;ˍAL.n]9h޼~ջoԁs?xŨffL_~Ʒ q/dNWf wy#׿`Fg^Lr:cSˋãGn9g|rF\.l/S%4F>G53۴O׽p3KnbI1~-L7ҋ_K&ʎ(wwy1c 9;HU?!˳W^a޼zuuG+]iw}{V%׆C ׎LXBR[~oN`y8%7Yd&"!dzNCs"aYHnd5?"+/D%%Yg/ n$!8 n_v #e-N\oyI-͏Y4G]_.3uhw3Xp[ $ 0Ǵ:fr,IH-f_Zn5G&Ʋj&9w by:;̢뱑TXҗ2|-Yf55Ko#O{#jIP#%]Dȕr6 %O ZVjaQ&fkDYA6j*k3/ۗNjdDX_FDc)a!S<0K}bH \vJk[,!o{J$l­-.+;m]`ڤwxsttgSGiEIh-w5OO._}o>>'yŔ<3?t8oirk>>;/Ύ;,f1(O< +,.{fR+ħ29J'0tG;yrART a|uϴ[Xn`:>#J۰SSxjUM{mW0MK$-JLCau y+aфYhόQ{^I|s{tofEM'],DmBt?s#im4vw ~>_24$] v"Eu⟲;P ۧZ*VY/Jج$Zj*\M6JT|ް˗i YZ,1ht 5lDFښKK8g\\\CfݜP[jՄz5 1Jxf۷7+_7_HMYߝ-l-|Q_(2uMg/Knb'_*p~rr]_Wǰkq.cLy%qѪ*%6bZcڡZ-fU"|lICr|[I:ITfiM\W.G=K6з77nL߽WE>K&F)>>>9;;7y51^C2ZOxqy>hvv{s{8za|ؔok8v1L]7k4 L;Wy5?H.^~t˫Q77yHʆE~NBXߤ8y!zjRˬ|JuOYPwgޓ Fhc!jCpqq_|:9vVe֪#!& \oY-~Q2s+^% ~p[+FΚo'ܧLMf1c,?mioSJYgĝs˧_~w΁jf(<:<ɋ\/?MO.Wnoo,L'G9DL3fY1ye))IFcQ&Iz &d ~#Af$bȋBA0٧ela\T{͢X*+)5 >j)JZU2 oUaS$U}),l,DA#0|R#82xR )s4, |X>+e{ļ͇j}4MCUO* 71P>nQ,nM4_-_ x6Gy%WQ6 Ѿ2\fڣjZ~ʧ.sx"Oy]FP%YIZr=k/QkB|6D r33D`QeLKrĆTvU{ǂAdf7:}NE $^_MyGlo7w?[KmO -Iy*l(fwiR`CC#UVwU+QHuQ%mX<T䫊ꢒD?3 =Q/we[A=9>pwsӓ|iI^1@Dyz<=; }xR.l?:|w7=><k0wss;ug|U2IjV?S4F*ɉ)o<486?Tg6Ϯ?1#y.dPl4@F]r*^_\ʯ S"emw1ʽC07Q5-&JUa Rc;z_1c8YTLqMл Ba6YROSK71b>|-N97d{ L'mĵL+<Hrvb1c0vde!pF|g;jd1 1DPkyB 5)kS&GѤƨ'Eej*V 2.$)fj0Z2j|Մ! @0$.0RyCE$^F Z]6FTk$da(mvʕJjWTD+҂+"B00+EV,Ǯ1#x51ѣyKBztt?_s]q0e"}twkB w3-V ,'?>;s=9wo޼>:d3 S3 |[lKv]fNIBp F-$-[ ٲ0014`0X`#WNs2۱*c0ȵkvkl wZ9ёJ7]]~~vܐ^g6oY t7o.&< 9MS:lXW\/D"yE|#hvR BÚP8"NHDƒjPz %Vh\V+RR:bg%]Al kA4ĒUMT;|Ntr!D~ǂى-',Bɱ@jYa %b"<_A d 54_wWH*zh6i7 ̮a*ltgqZה6G"7 `LTWm6qCD]UԛCgăѩC&oاy?IJt͹F<vrΏHj/rBg^qU\ {Ͱ2 '&)/B~? <jҭF"Ym7%M Ȅx P?212ZQ(u <4AlePi CAQم:P j*@0+3w/n>uܹi%֭FA.0ژ tՙC66=D`D,?@ $e⤛4U$+8\CURRYRlūSY ~verkULozi/ '?  t f< ǰ 2͍UVjY%/;2O8ͧw_vx$zv艓9MX{ߐ0vsb?}"Â9?;睼xwKH#dOȢdP[B[JfӉEhUk ۲jjg\-8:GVRbB9 k_%N$&N jMv4#=GVuGzT()X9hۮVJ#̦*|aAF`]Fߣ3\J΋T1vPceX)Ֆ(|KmAZ60LVzS~-,2qKKgf8d$]";@* |e95-s3#ع4W]yA+8ZeRёW5v?^~ĉ7~&&|͡K^%bx{7!O?:͟'ya$ռ7EPt-•Q(zj;\H TaMR+Hl qOE*HtaH9N9O51lC|,E^rɩyq=99$?|Ϩqc1P׿W/%؏F?𔆽jM6">K ;sۑّQ8?Mg~g!^ĖL:E%FJy2㕥(UhѤ)]nOT>>>aGAz?s z?/b7**,Um:ed Nͫg`)79l'Y#m\-249K' ,\4ZȻ%s/jpoWOte'pFBL뼆oSތl١vJ%ozgv.޸spaP[.D: u*QNBgVh+JJGېCQ^t߬KўL-&r(Ub 0 桌pDލ)vG5Y d{9D ޶-W1Y */}7I55Q?lㆿۄJD+PFh%觧]tW@@\Jt<׋]& UUdZ\ 270O?sQ7X –7~Og:Ha~UNjͮo1.''Bi1ϗGwO.[Q}::A$;t7yyC VJf2QJ, y,yLFɴRIILy3#11ŴvV0_LRTSVP:@զ}c*<ߗ)nclC0,#b R=cnp@H|e/"[b.V2}qPoz\\ē= ͿjDcRQv⏧F\Y2Uԣ)_zdrO &6+҆m1i9<<~;fΆs;I 0bCe2} j7 Si~KW>L$?tw7uKc3 g]p9 +O q+@ ͼ"́ t;]Tf5 n'#?=@QC(D:0JPJl9Z*R&@)07:**ճJᡄ}/e[4\U2?0r $1.+4,M\ǤZw=pVB0"z1:r.0L|rHO1Qk;:͗aܕb5_ g}9`8qJuĢ)R 8W ò(u/$1{]|q8!.;k{J1g мA*([q *f6iɇhDhVh};5x#gt|]-G.T:@-R@9+U'!P0D #sc9x=AY$RٲU檟l1@VC%2r}(q$Cb4|2 %b?F2yGApo:,TKɗ(Mge$g._~(qI|R&1K;>D!)}?رK f/?_(Sw,Ǧ|T}~\N_BIIZSke1(K(4\s Z^Ҵ3ȭa >zf"X_\JkĨWg3`L","PmOAB=pVd~ K+eUR全P63T˰ Fa+OYg(T𣷤TIjYqV7(qtI4g]8;;룠@708 Ýz2[7gx5 gyfeV?GҼ+0mJ)B֊l[-e+`xU@پ JI2"^!%[rp4PYg&!rk8m&*c 7:'EcTo84[L\]]Y,=R[?V5WD#xL?xyAExyyӻ}yS謴KW9:i(+5YW**d)(j3(n\WDIᡡ fFgsAs;y ʴ#ڽ6[Ri=q_s+?b+a ̶tnOv\"WlYLC}_iOm>0I=s][p"4x{DxvM 3tIᜮ39UoB 7wWWWW u>7[^}#X%JLnjqAɑ{v_$O޿{'OD5V员w_ 5]ʬԺRZEnjR 3($b(X6ygT(DC-j,:RlwɢtAUDkUP(^=`)Ȧ? CN18fsl}yNŹOLکb:{s^q 'GY\ĶFEv4A:l"iL x5sxŞUvēS yfg,c{]TiqDe~%eŋe1cCA8-*?\KpJm bwUZ٘s>(Lf;kbBh7y޲GW:z9cJ՘Ǔy^,GBm{9Oe{||rbÇ6?G?8Ϩ?20 }G_\"BGj`2] d»,ܚ長Iqi+Jf'%$ѶT`/8fP!m*T"Z 7M %$wY)Om-3[9 T)eũj$6WW/bDrQ`3!) eC5O׮!w%+C5kPc.2E`]"p·IQwW_eЦ:V ۪V5PڦۀRm__ں"MB qmUv,m%Y?AY^~t駟>]~$%,wwWRjUXW66XPũwV pAsg[ (yQAiHJTJzqfm''3e$/ws2F0_<>C@0%1+4v+iU)gv 3گsd`1ĺRK]<{qzzvڝ?s^̓!hsi˗/LO*36Wi.ܙ?yF42$4}یm,οn]"6Gr$&ȽPTS.\cRBʫG:Ցa іqՕ*hIYs8!n-eݛ,El"( rzΘCG`6$+̛yr/+X+xlyS|`fٴ:Cց&D%DP7)Yu ]VºQd~dCd \ڦX3MQjJmýZWzBW lkǂ5JkSxy)+X/Ԝ@R* 8o뇃\­c"?t6UCp8uż|p3?88>9/~A!sfuKהSeJdf=(L)M0ߡŋR݉٠ϲ"-TONξ,d?rf4~BycU0ZRuTA) U; &C{֖%9KS \ [ET!# lmc::<ȏޞ=G+yBógNV=G'N[0ЧggW0%=p{J,j.-f'K5A<Ӫ󋋣WsD~w}ۇ>]_6 \I&<JASr`&eV" N9MT4J% zZgYϞVm)-ꮠ>]_8!љuXݳjv8Ȼ!NQOĘD$B_n7Ӹpm2?y|c|nЩ=qI %m%(Y(q V~ek(CEUeBdnɤPt/srȆa󦆁7 ֽ J?M)Gh ߀M x9QZmLm+pܗp^")VE+I77GN&O41Y_FGT3|$L4bXy *G""ܠDF!V6Ɠ‰lxJIG/E9R2hj5+[ *ulU $N YZmՕj)a E+@ȊQ#1X;)jEB5|`K!Gy`y wY縓zP#y >bd$ڔ <:XO^|sW}o?r['kgp߲l{AĚap:Avv$FnE 6 ~ }FzX2! og$У^b8ɞׄщ m:o'U҃2\>~x[AuD,ߠP$ʉa."5 (P ε/%F[Upѕ/Rp@q%dżF{j\Ъ˚L5gxH(`FΡ6IV 4TI\B[:P|ET/z{4`%0:/18?]ڄL [EM+Y`jo(}eHat@(WtGlQ WV< T0\/ MsX| ۑUs΢1 d;=AE aQ1t`NIJ<ɔp1]Z1{Ogg?~'`鉣Z_^hYt3aĺOV֥ռѿ,V JQ~K@6:wp!kk9Gb)!-2SRl_edNO~իWl y/Ν|p'h>>91տ&0/*$wvnno?+o@sVq4[5Ӕ$|OPg*y[L9"a$[941 43CDeT(\|[?f%]qVyq4.̐0|ptyċTut 3jhYl$j?calOUoY +xBy /VV)дQ!m8'BYxd0O (gMq1LCV8DƈƳcl.$C$e7c"ig3(CZq}Μt8bףZ N ۄ&P*,MS46UYZNP&~yˣI @\_Y-$Y>| v/^7␃|tDWFG1Ky1hzQ΄iB3N.Ad)Jw̷4h'ty 1e>6 ։FE87&$#n!CѶZ `t(![r a `kj.T!k* J+ojPZ-%W_Y[ Ug(,X4;ٱ1ZbtJSg㖕ByVX9qV%Cyx_/Rgi:qN ZVHJO`%|%x (1a%~+*W"(isk_|Vw {ٳ3՛W/_,n/D,c:We{g'wJ&QPU]D߬gN$)ڴ*-^bbHLNGG.^z+clr`~xݚ${H$] W\7s*-Trϻ8 .K3f;k=yEF6kl;8mF(olq8:?9>i1=99='ʗwP&]LO%mQvYHb-/}9 DK @Nja-Sh+!1+YbX6de#b@ؐ\0V8mNU7Ω-*6̐:c{*Rx#"-ehJ]VVC&eX!k T3m@@j\@};%ʼJxԎ弿t'_ +KLJu9ʣ4:sO&2AA6 BG0F @3 F  U!;R#Uz@0k?ҋ{q2ǹzwql6' @E(IڠR@)+33^ .,`:l$EB*g5?pk8/>}b!~%_G,OŇ Yrvw~ٍ4_ĴR5ɍ;n>wِl-1sBϓ|> & x̍L~c`u13q5.&4Y g2'-x9?ɪQ}Z^:=O24MBt(9:Bk;Q<\6?Uozy1Zj%ֱSVD lߕp*'J4< M?2EOb8|>Mx:kdS??X婦ko`xeS?EգtZYӎd͏G%JoMft-b[X++^PʊQ*\Z] ~#RdhSqJZE'N^q$f*j順@iAgRDgy*Al F1͘m^Kdⅳ?9Xu1޿WK@hIjIRyl[{ Y~"{WYutpIX͵?meVeZikG!e*8DY5UX8jMl`av M~yyv߽yiK\|!"<99>7^NƘi%L\Eo/%ZUmpp$Rڻ'g?'gϞ_˂F@v~qkҟ^6Of%=r[+@u`u3=x S:8S,-;x;Ψ7W(yO¡*Snbq?5"ą|pr l)moU50>-H)Q$pᨶ;W!Tu[ݜ5_<7 = l`GD,ysXX'sbi~~\߼wf7X;]DXB63ʌ9N +;$ׅq YR ='z9 | /UMqi =eI&2es&Jj@ynPO_3 8W@\ٔm2`VM|PWԷ#8eSUVJ(@S ڪju&P9,\mT{{Ss?w2{Cyo]֔<_WGK}O?1Yphm[H>txt~ !|:# owDnVÃy8;=oYoV=Y\WZ+uhфC>4/DISfvL Y@~!| z 8 LC7K.'E|PŻ|GU-ΏaCdLP2cEt2#0|=cM(&iZU2ERZ6eU2ST˚#RK|?|Vo~c}}inb.WonBbK۬q`3lqD,fGM0lDB)+njrw r: VYmp.B,6b(UJY 2{UaY(u@y&V Gf մ yʍsh%81e qqAc3?8wWc{O9;YfY$ /~Q@dc}PЗãӳc.f0I3}h>sOү 84T9b,hU܆R:L/@+jR~dbkv$ =dgEiXVOWt|H}pr|N.=;;;#w0w.hnN.+`tKV ]Wm -WržG8D3!\|wth>NۅH<` ,#__K"5d^rBHraJ/&rl˚̆+m/^!jAV6"0_/s@go\h3"||UxY ohb3Ѧۍ7KYC4p&Z>K4M}OJasOzF T@&1$ђ;jNO`<_B:Q_tNw2&Z{z2QHoZ k&}-q;ݏQ T*Te~&ת l* <Ǡڦݲ+4F^)ȋUFI$Ebiwv?Ͽ< [TfY9[%ηyۻ=]'v5[}OV׼Xfe)megm~pgx3ғ|ːRorn5O/oi(61ﺤ6RKn=dXj;jj7&aޙy(2A)RV m-R(Ǽ}Y,}e<r&{vτt#ƍŇ' +U!Xpe{Zb)P|ES+7)OJij)j42#ۅ:̈zX0 l4K#dQkTYٖ\=`4%)/sd|,!DϕV}>9>HQ䜋nv5c*˝ѥ%yU޼WoJ!(ɟ?/Cy~v#zN9ETQ!X)kVB'6,l9:J`hTGat,v(j7`Pe(DO(hh8Wt[״4Vg눞?{tO7ˋ.3{GG T4u$--/1 .?N_l5='3/1]~X*jNX揎ݺ?Frv J2h.ŠoJytOb,']Kl<:>4-8Nܒ9oQZ5Uۗ7m9%l3yXdHePO79ãϞ z|Y{A?5,)U8Ñq{OhBtY-ҋƴN GiKmҰ#<I5ђTLY9<#9B/(>䶖-!F!>Ϻf^<ʾY;& )?O?y}Ó/'It e%Do{ @sk&c?'թÄ?@Ek+iS(iM5G/X :A$CC '`-gsaSH^_&zy;D'\Z SYMyBjz߇"H Oz&R9*=ȍқD>O'0:mf0xq_#H٪~o mE2ahVNZ+P;>- sMJx(QY8[0>K?dž}6?QZcYX=OB—/_)///^E>)'~s~wwp՛YfD>S%; =ҏϏz%& UO6<ͯDZ88p+ ZDtBUƴT\m@Ik(1BJzI'Bá͑Z$CQ+u+&eպVo&iȶR@HAeyU:S+U\)I(e[ժ6Ճ 5zn!!`A92 D ; B)1 HH?q'rouɢ7i'w59Pf1ɫr*֛Wk?p"[I/x=N2#%"C^e}P\"רSm3gV= 憌?*j!L5zAgVM;cN?߿x;!m9ey3AM)M`D6G@a_ZgeKG 2y^a:=7-t(j ?&XL!2()dD Ca7 czx5 0Ai1dxFW-51FۣB5ӣTCg"Uj,V|%VBRԓU &8fe[P%lҲ45"#XtelE,ob m^j\\JP=ߏ`g)ƞLмkGǓ;O_|_dy5Dd. HI{n{Xde̅:^xvܻ4F`Kx20cegݙ-Gk!M|3sb=BkƏd!mĶ>6M )X˼a& BV yj.MZov})v,yP9f3+%b(N 5ZsA+:Nj MVּ9>WTFMŷ%<7!Gup '9U! M˨Uh])?m)6Lx;^6`X-(]"67R}#PJ7s0-s? P֔;89v<4}Bu*Oܝ06|i'7V{'ѓgL}0oeL^yVT~TIX=VR uЪrVu1#I&Wftxuqųg'GycUⶮߊ/ś *0t*ȱͲ:>0LJ6 i[3“j7&9:5`W ˩G2DzeS]ݦd3B WY@kV6PK[H*s=ӏ?ۻۋgG:\ʈCTߢ S+Otoj{rT.>4q ۼts܎feDZꜮMoƙY07a1"9uvv6p*}ZK0ěQ@3Ր$؞ JQ!BNgՖhRje^(, mT(N]~y*Eԍ+`\6 <\(AY@cUaE@& 7@"ʊ'y<~FKB{kU7e_!Yjқ ⎌l{v0GYij)Cvn>wMًߟ)h<<ʊ^}&z{{mMףY[n>3OY^KȻ?3͝|&/L|Ҡ#C$7,_&ty)Z1O&i& xlDb, Nge:+1(;ù -iXK! <*[Eg [lS敭L:DT+[+UUR1ײ4@/˚ zgR7[5ͶLQPaVHqj"%;";gK:4ǂNgi3Ys,3[mtLONN^uhR$# y'3=|%;ePޖm|HϘZ _ٺ!Ԍxmq P-Us6xֺmZU @X$/q䅯 06м:wsJ@m#=)¬?XW (B¿dߚT19#3S>Y`*it % JӴZUQbPN[h W"/X%H*8:rUM[40dc%x6=Nf#64j`H'QΎHD6P!z5j+\ʚ haM+P6emwamF'hRH)MTZW&z1MVjYx5ms֓R@2JQl ?@WmŌ岍3mߏεIEWNH} ,@ũ';7ҔyS!Jbt|Mיx=bv;#R"f۷?^~xvvbv}}̟~%/gb** !`SPmK37Ff8/ B(J%M9"gӛ&ղp[-@ae ) @VzGC gRJMJD+R"SYq嶈r?͏h9L_Of̃OFJ hTQEɣƝYggӱ@$5MA1=V+ x%iIs̯nuh $={6}%n7Vi+J=므ZNSPʶ^6R+UmZ%^Xں %*WJԁްNI~)o{Zϝ&o-AOdty'#TƊ<͈516HpGxx+&刔8:\u,`T-v:KMPs8ofi܊Ta"B7l+x풭*Q.aA _?w2\<۷/Q65[;%B_\$OwigYvv^|ie:ՆOGp.5%K)a5Bq⭣]~ů~'.0?{C>}hgӁ8{O?ݏ>} )sV%Ū"g\rݗKVpbIr|rL?/9w왮oyK^ O ѱ=ב\O=X̞?x͋.^xͳ''lꗿx#){,-ğe8R&*Q'9Μ}CתN6(ܼ@ v8;? b qrF{.֙-Y?EPD4)&`YS_gPw.NÿLF6?C=fP jP75I[U<&ePO!4uDHAĦGYege }.KĕV{|R P sm+^"-ቲ^jҎa*lmoiʇ}Qɫ"-:-+R'[kv˰ҍ(=^./f~qi%y^wtRQvK+f>M JGxN"q=ܗwsJZǏ~sUF=)MU犀*p ٲKT uah tw"Y|/K_\p(t9槍^zW.c 3~Jv;|0]4{ey{3_ɐ/+?pp㏿ﻨrKgnATOo$[q?4wwCPs#ʧ$?uzw;̆'?QgkcyK;;?99sr:9=s <=S^~h2&o@4{dsvu}z7 %S0Arj 1?1{,$8Fds$(g]f $aT\ Q6>mIq-#5y-$y^~j|$%[4Z1ՠz с(!"plBT[2~R.Ofv_r4MB,>Wg;`zL-ϩ5צH. xݡu/+p>1>'O3)&夠 iRϋm*beg8BTR'ht\^n5`!Z]6--(J^)OˉiYYP=+s*UjYyJ/ [uDAu@2WPchr…t#Q@-וٕMv&1` w}wUt𕄉\QLd#;η~~|zw|/_\<ݝYAgٞU>>ex% 9/_|,7#$9'w_lJ|r#:ɘ%UIsRi ,dX5ܝx5dΩ=9W:IWM6 dqc֬"UZN[D/Wخ)M- jTy9oC08(  v ^AB:E]N-} UَXq@om6=U-eCqf WNbFتp[~Nk"kQ[9Hq!İS[~y)KY ɊAРK(}ÇV\!5p I ՗Ο0`M!ꩆznsצOlFDYq+(9M, &JoYA/pN6KnA[F_~qWXo޼o~ŎGon扌FEf5)'zrzb}̏h+9FY;j;p ]C'">++فu;Cmŋӧ<3oӧWWnbz}Ո9^/~AahjTE;w0D>';O["-Vgg!zsGwɱᚋ(Ó#><<>>}̴|6R2%F*W l77^'-."l3( 5[(3jK6eeI"{A JXeQiTm w!Ο]:[&ۙ` 1`GX[t `敱ZU1W'**[z!El:<D6OG*ԕ*hu\Qf1sa+Ӹ:-(q _(h Wy1QQ uX }qFڡ̜ T]!&IٻC8-g>ԦS(Ҳ:6QQ=;:|7O)jyocr..^e褕35##wןytOPr:@p-R Ǚ޻{pq{^r/xvt(*Y%{:7'Y &셸(#<%xz3#> YUBz6Y+gp%M8D5P@*=J"`xϪ+[=ѪIYtCP"@*Ҥ woZv8sJX7qgN>[1-#[U .E3Bf: @=1J)=DgN|f[g1-y2sr7 McX驮e$hSОPP^c@@#Ғ"- h`9'&|B+`ehQ2K74)mCڵ,sG+^"\*BJT\L+GS ǒuq^W#PF>M2%oe`7STXmApwm 5t4S{ӧ_xt*ǧ}+Iǚ$cr;ͷWOWI_>yjkx}seI;?ʧϗ?p(.:d/ [%~wG 1@  ع{3įwc-`|NN$c+&ȮKgK.ی*z&d\A(xp󋪶:kn) `)Abe68?6S`1،'=F}y77ׄkaA*7 _Ɯ0̲:MBֱ-wOP袸bQ@ٹN,N3nyB^Q͗Sko|ۈˉTu1a.Y eQoEԯe8[^q~w/? 3iJ0hI':>#oU̙D?Gk2ᎺN~C43SD'$8͡0N}Qo29G|8 ۴y}~7:96\2%;r ;ݴVެQ;VÃ[|z'̃Axsb9"8lZR"TRҲMTdR'?ZWU 0D܂&D?䬐 FFKOC7:N64m-4XҸDwtOQk{P:0s(yo|:OsDF͈74z 'JtFR,"b ^bb4GmC_ /`S"5RZxگ6|8 \z;χ|ɇ^$P{hX7(^X/"VCU>+ bf7Jϖ_+aK:n4Eyh/v3w 'N_ΧdUf}װ<=hK- gAI9D)!|cwa)"uxбAm]XpM0ktP"e6, (e|FAF%irww>~yW0`Fqȷy(|o~CeqDzw)F %[Kݝ|Eooo]i~ʤ dqPȍ*?/~陸Pvv~n. |,J*m蔸#p_rw}cNN0 HQd]F2GHXZba(埔OOsEuM۟>}~9<Α=CwOfcu:9C4-WN٤NDI' %jա joM'pVH8kgR05-< B3ڪgaϑqE@N? =T1+,.@yJ YXJ[AX2sÜd 3?褢af*5VUS֬xK~næbdxZrߘl80T'HJ3_:g4 ,>ANe^|H4JNfC]amnJVĹZ mO`(OS~M9HK $`PK^GE7%[TFjQDPչrZC`uK!D) VP&H 6BI8 *jM5QH'+E+i[zLG#,R'͒e!vjjufg*`5qz?:ܗw>v^lhV&x8w>xw'燹=ՌoU#_j7@\p?t{}0V 6"5\on.3fQ<ٜrR93`1@= cd2xFFĉ B60&ӏQЦ@,Q(DW)rV! J!v u\ߵBS%]8p/Hx, @s.888ȓ9HUMƓogJoDA@,%(³Ӽ=9f Mf jXqj-PWM&PdsF??C P'] -;p?6EYVM ZAa/6~1G&:N+\r~~n\L,Yu9?=AT5Ҋf3LiYX2]q%OhX5!:w\B<@P k8by ͨ"10Y=v@'JR5HyV[J:NY|:| Ĥ_lkČ|9t`a̱) NOBGGFϒoˡ^_) o]p _ఋ:ruqFӳãӓ|t19M>=N ϟkB4b&v/mJ hlɺDg6hiuߌpNgWEMCLn ֆs뻾Oo0Fm1y8:7 ü$΁5 9%g e<֦M,JL`!eY}eԔ֯\RPFV B³qԙZAD|7I` ThX9-ҀR@Vj!DҲhZJ\nE \Yb7-j8\T$%oIBKIB9n))BZn>]ZcAy? &N ]w#b$=q9kA2tK>Yv_p6NgfbtVM<{`~VG:Mc(w7/?R \z@!JD rB :DYx;?ty}]ͭɦO|7}=OPy)_?ߋ(.SWn[iIֽ++m^t,2CSHM@H60, O)E:I$(Go4+ BpUU]3YvA*"J.YuFP1lt4K}m]0۴rVQZrV*6R kJVtVeU 6ൢOeQ*AoʠCj{mUQcXe˦,eՌX W&<_@}bD8z4PX8Bah$8ġι5lF$L ?Ӓb-D9 $@dD6C#aT gWhUYaPCڝPx;WM`c>qםFW?4Wӛς'&Ԣ6O9Bcvl$@> |RY^0]ybxm:?8>:yONph=312,7^;co~߻E]qI69[mfӼ:Ǿ4Lxղ̗0]쩓 CM|hn>{0oޠsgzT5#)ʖPl(QDxWRWdDÏT@tj<:ʬ̥QOyyS(ػe`1gB8yw5*Idt"Ca5^T'[iw :Հ-(AfM/_FMݫ9l7Hh#H F  z.QY⅟Su:BŕqzJ:4+%VƥRZ-xnUIĥQ(P!D DJ@z|ѳ!fȅ!ʝzzO'/^䅦MѼd[p}̻V^.뤲8nԩ5J}G).p+Ê)d[?L1GH5mCUA*V^ҀmĦ1jimRS=DjY L2R l\]2 Iԏ?yw{x۷?o/gE-ğ;!?W$snNJMp[í//>Z*,YsPMsL&ohEE)cn0 `3+,v3%zȊ[G U.Jp: \[:]C~UU7eG*z7R\/ Sk "D_8kII!ny*кJ&:dB[˶*K8F6gur" /ßCP` L`gD eѹ-U)BhsnƸck,C OHE/iZ8Ɠx9! 9b`Ģ ) PޘhmH1l>Kt:đyY *ǁ>ۊKD습MBw|rR/9\˧}֓8;_X]SvqM_<{j /d\T? KR± gggV/y+WFYs̻6s_ܙiqmĂFAiZ)^XRO\Swm@(NBFS`h^zZ(NruU8Q P6!]%U?|m=j^hV2r ˰1[:sq\Cȡ6OONLMxҙe"m'r2W'qn?Zyq@1dp68eQ x"@)NrGM4VEBjkIO5@j36ьjWXREJD?)q&^m p X+#x;"+|*0+W@G*VY/}Q&"^?5uԀu`H6C\f|&_3G}nAęRS2yh3~5$n{q'vj頝*H)6JiY3pI7qX`ڿR fYt|>=;%xq斸[e~v~j-ɓy'ё1l98{vwkJ!TZ4ܱ;bi.J?z#Ŏ' 9շ0=/[& QVRkqs(/i͋ZI߿S/No?^Y2y1z)SE.lfC.ZGCvyuRL97&K7ZmSg7^%TW9`AA1a,km[RUѸiU"(ɬR|DUl\Mՠlke(`Z=E SU:ՑXS*XJ,ն* kZe?6fJS" HMwsib^m_TJegk +Qkjym+^$M 5(:mθñBs+FG(6Q4Du^+YE}jT{Bjo$WxŋP _򚓾GVUYZաG2x|Xc{;/k% nvQCG Qf-2a:4Y ~̭x^*["Ⱥ-eWhNqp0}`oyEUlYDcg5Ik81 (Qb#%A42<®3M@O6VtDDtpawhl¼ޕh(й` /68`5#Rf<%ѕ+_ZsP?RۯaK0,`Kæ8MJe6"+^spјyu̠7V3+d"^LwO^ FC,K5L;*󍯅df Q_r K/,Ayɇ)\!U+B6>]^~77:+ Yr AeaVZT;*8I2ecEJ7{pt|7dIG$5oօjL.W00z27Hu:EЕ0.0pu"(RYTYC%sTӚ.l&rêv2B@eAƮ B$((pUU]^=f??i803I@iSu)%0_ ԝyILfV?ƶns;9>_E@[&EX+<~/9ZO 6'gc3lՉ8X +3ZyʎH°<5v tSvo T֒$ܜ7TDT‹V%=@T'*$ղA/JT*(R'1(a*R(<04vb 1}/2}|q>sϗ̥ZwԘ']o]Tj< W )ZĪR|Gxf-DI*DV@7zEhƉE+-QH)eVJeP FjR|{!Rl:ERYՅL沁KX!_:"+򯀢mRZ-PT6#-rMꋳCqב3iN}Њ؎/ɁPe1/wÄ"dٲUOmE^б@vy_7ĵZ7[QŷپAD ޲"5uJFöJCV"z=x ?FQvk~˜tw> g޿pH~p8E^3CkLmY/o߽ϥ+&ɥc/aLtK{ٓSɜW(AOWVRꑋ %E8n6#BS6ڼConpJ6LFU鑳wgggyNhVp31ί2r\ Fx ˾xrtx =Fl}s'Ȁ%C$y[ ֥>R6"smJpDtfdӂeM+AW5EBFDEQYrE%VrT5x ^AZ-@D@@S68i\^=-W(eÿj(x[D e(RYHi24bg +G),K./f嗹,u tRmǬGزe#7TNhL\483˨|wOEFdz%EB3h8sr|>X;8Ӱ V6AgCTk0F p=,g߶ SEMlO1:,_|kb5}Z&f' ܁.\'+a~Q4r h8 ~4q??׎>dW:1"ɐ{"w ܎|Ji3! 3J?g_]? VYV6^(?Aʢ0e¼+ 3V8ِ! 2zG'GLJ&s^p)n/^MP"k?G(.uHq*Gy^8_)WWWz)Y'Mmu;WwS0DL<Jszx9d#Zz: BR Ml?GO'pH疈-C)@@ʰrQZ)o_~=])w—qzC^{ճ:2Ӊ;1Z)I$2jѐk2ݣI]^v=rK,G&soydON]˙}26=0VT={&2߿:ח6)v5]=<>/޼z33i]RX Σ*_CZWI*d@ښ?|u|r$ktD~ 9dqbqϪ~`ϭ{VG?Y^LQcp_<{ gEax`ߡ%yNZüd( J7d/Qk6 o֡(Q*W&QD%<[kč ۀLާó8N^41Q)̫X wxqu_}Zצv@}.MQZJ8 Vf!ыPRB加L6$۷MDfB6"tÁǜ aD&*`N9C՜ r&JF1 aS7K<[]`x1hUaDծ?럳g=QS-*[H`s' 63t6Jq%5f^g0U`2:C*(Ba̪Iv'E~Zѹ%$ϰMR(?םkDiAu-R$:}w9R><>nN_8ŁKӜU [=x~MĦíOBeyѱL9 ǡǧ9[]]\\uz?8;={V4yC>Ov`|uz!s+3 wC'D"7K3S\D{cbn+ˊ*GJTb.gay]=`imJhEod3kHbۛ^0x}s{F M1#׏YCkeu/6[%Λ;08R´S&,D\ҁP,ŀrCg.DJܩ]2'9%A|&v@DK- %M[&C46"ZGWVm7Ѱ`/D{0u?(u[5m|)~Pf~,RJ_eC g?&cZPHϗC{۾O.ɋ ~FfnAMΔ3b^^~zJv7>(P ŠM?GTyQ NKgy]:S(,_yZө?9>u~ӳۻcv~gk,uw"w89=dpgΖ{g뎹$ѝױo(CŴ>Esf4Ӂ8iM'_0ӢlgXrg ; |v׿׿$|||drJPy^}lϛ1pv~cQFv=F [=c0e+3yR`jD6+Zw?3f2ɩym69W/,'@nBJLim LWVPz5x<PAɄVRPU 4[@+(vTzFoB@m|j}rlT ҦPZIkY p PЉUUr AX)CR(JnbK{ڑ,[̵Ӊq5ުo8Vd u5=|!FR3DETóY VYU rHSɺp 䙎xʺUAۥ6mUnGBuIF[MTm?gB5}yL~Wdөy" c՝.H~g$C3)7o+oN ,!尥3'<Ӹb.( heZ?7u"jQVYʮH[W6}G ,~^;݊HUAYӍ:okxh:?\ܚz ~~G(ALýrvIdn@>=|Xn~d7CH1 vY!|{֔*;ݏ*F: ZQ>򐟙5}xlWih8_ PEgˏ2]qj5g6VvA\޷2הW1/ifJñdoZF. X>ɗRM;}t b:wxzd8,w7i 1#Z1lY[TץAѤD*ަR[a)^R2l3 a(^TCTCVV\Iss}`">''GLMw|EDO!KH ޹8|؁ k~'6t|1Ҽ/}gL*ҥQgD9Q&U};>>6|o~˗ϟw0[?V%d2IxN/©n&@z4 /?~T!fe7i6Y bob^#¢D.9>Sn7T~xM'PSciCsɾu^/C TpUiW4JBj4seЋT2un5h"'›:ͻ¦iq6&J6ֲRи WR Va:V%+OA<հM)N ZԍŦGRP:QҲĸ+*\z1léӾTz-dT[5Y!|I#B Jku2#U֖ʼnEVЄse e"Y)󕪠 ,sA^KF2VM4 t-b"Ph7!vZ8W蟎ˁwȪd3 Ƀ֙aXx88m^d% ʐ˫Is}&~r\.HǹUgRޓӳoל}̷S g;ck+ro8#h11'OLuV R4 GSaRuUm΀V*x+"hn.* )ɸ<=1|{;2{N2Q]5\|h[l/gϞoY?˜9\:hLFp0 dsl)%RƉ`t}@#d#~B:JقDOf Fnu5vI~-$:^>V 3j]ޯiP/zN3WHc AոLLq" 3IY 'z**޽zu}%nnrK=;P_pq~W;5V_tu=SCtru&!Ç{˛]O?Tm^]t.日/~WZh.ͪJ}/Ǯ&)W{P`7c`Rۯ'G8)(rKpPߔM-he@,ݩ?"z#P:C +]1jZ`,6!3}NDTibTH dݴVAUDZeW)lտJU-^r+R/s"?C4.%Os0oZ&UMU+_ <P$VeiY3́ACgD.R !6sU͜'协^OJSYr [:0q$/MGGT{px~;`>Jٌ?O3tFG<򆮧ߚ SI]^S:?ϛu''l嵱|F&G'T;:HcZ1La^ uI1QKr-AUdB{@Wζ0ǡ"4!jˏ[TQS=m*UDK%=L#XrqqxnXhNNI䕸 m2|I}lrV7|p!LJy}u,Թa:#1Y4n48G8YVG?%V+{.v Dңn_jԴ$üTe qQjģ\o`%~L,<W9!3n%-b=34s5v}Sah1mE[#UH[ch+Qځ\ӞD҄8 '_z3 u5z`$kzE0R9prr4(q:Mȧ-ljq\zPL;.Pz٪D@:@!4$D˯~K?p(Я߼ZO|,yޞKa%(Escˑ%ǏM&^Sא9k86]TCn)8;;{VToDA_`ݔ߼zsWP]]-tg"?B؝,ˬɴd{L(W'?{VlsZa^"PYI{ťKP1a@2'2^kAvu(*kVe6lAą:e` &cI_jwG.-CqC̑-䛦voUy%bd~]5Y) TkYdAEAζCDD% q4 [AҺ((E*n( ,smذ7HȘmӚ5VVZ0QGb0,?)&1}J3aY7OV 5 iYg 'c.C/PP$|*U C}>6 ]=e[_s7Erv{ržJnA{cXQyMLW/nqg6ölGcM-IzsYVZ~:ЅqPڦ"tgNˈ~X6%qh _JY؄.GcmA Gg.^|uxpd?/t#b m~Sy$<3=W,r)7|,",j6p WcnUK'S q),R(^q)FUVjg1C_]]E|xu&]0pAts:g`<@7\0g2ώ9vX\gg}_o'Hy|@ p ͅ"Wrqook_\<i]Jѝ+\';ӧ|420ܷoFuiA#=9:ڛ )o_zut|t{s'm}4+O-clnIơ5)JP5/\?ݺm#yRi|lҺyZ&V|X4+9Ze19na怦p[@6I4i֐V&Tj8-K@yXæ i!& yC*–ՆJAAOp![UT*RPUJlmo*Xbrc` RqV`JoS6S&HP$d-n+WUjCV`,"V#Q.@qlW2OՕyն6ý "O15yJQ#5ȐiFitaEtҎGJGlF2|aKZ\8zGp*4GA$ȖA 3{+/ΌЂy1xb{u<|}K"GXvw*}uzJxQ_NL 5 Ěi׋jgH2˲H/ (Kң[XlzdR JVNRcTDЦ&Vs#V;PqO?۷O щ($N\/<jOWÃ|p̷RZ>9:7ٝ궍ƊNn`Pݼ.ҵ6%ÕM ,68@ˌ($RYt@ "kD}NcFՈ?="-7!n,(M\MCȹ5񩋤S2 M>Fyo(aR E*ܠ>;vA49E2B=epIT a8FYd`qrK81eN/^駟\nyrrb\ pcs M@h+ ЕXxZo"k کMľuȊJ0,<%q6U?~'???Γ\)Ȗ'1Xr-7SOâWkçdpO-qOsaGIi3{}~직%.WYJc#|ZIجYssV^癖66A4c3{@wQC"|Vj*ίĖ/3`W J/*:!A <w9Kfg"_/v||ۀx]H9S{[;|ȋKq5dyۀ=ٔ7e’J{uhez (LcV'H.1 `>]P,O5-H^ hUbkY`qB%8y F%%dQ-J_Mӓ8"tZR&"mVTKɮ/;l( T$B9t"u?;;h2}[2 N:9I!;Q(}Kdr8GۛN0S/PAlϑ(@}&8pVkc1y'{co.nFv 4%p Ƈ*Q7P iS u pX_!H1͘!=5կԊjFٮm&ڳgi~G\}p3A?w54N5T( ?9ׯ^2=??9=:89>8>:8=9~tSA6j9MFZD%Kok3Tգ 6h zPv>O9 %ғ?ãdUtL-0o%8P M-1@õ_:"|mA@,>Bz*3=8dS VZ Ӵv8PÒs֕s4-Nn.&QX2%.4R6O5P=P[W&Fk4QyiВ5;5ݎOUmBQ-ͱ$M`s^ >ecXvsPB+3 P6 %Us?Oy5ΔFg~JIx3&@L[˭c[CGN" r X6 gteߺw_/g ָ9ҞC郉jrwycbDG|MS4dH0x̑wB!{]9/?%bDa,ѹL?ˋKZ!J"4CPַ ŀX%x թ|$q~=:>9>:ƽ'C}FU@h{rO9g>ɛp 376s^8ٸZP׾G4YuTy]N8.( W쬻}*KѕAp~ssc\jNӒ3Uu NHǏν`zU(O6UeK, wS׆9 KERH1ݎȴҔ{ggy:[ýr0],l*¦IVW/qΏ;3Ϟ>=~YޗSn*wbZ^3qfPNs.d1o͠uvZݗ7T22d];@A$hYo.ت$#8M$QkQuHXS0XȦ1EzTD91 oS, b#Q?~}u>%lĢm~M s׼>|_:~b_vHre"g1wjn)悩3ߕ輥TgڟH/M?2<׿O/\`Ã#?^ s{pݻ2vUޔDTĺpf慧,Ow]kON=9y_@!g%\6qggC۲t4<ݵgW9>NÙM'&fm})` [C:1]8zrvFETWb b剛Aԇȁ:iXLh%C]Kង-C)!4gr{j_DS2ePBu˯ R~+TCqP:()J8JuxTOd)ZmD!\YsJsj-lCKy;>X\~͍VV&5vZ,թaKF/8t)g#@EɍR:#Jpz qpɗjJd}(?m5I҅y*B `HHB`H;_,LtwuuUe<~'Weka ϕ=<<=<TDj"^_[:9c~٩YζЍM3Ԏ1W_]|!X(شiZ&{{wuyx/KDF= \Hl^Yl9u HChx֬-zݹUܔtIm垓}؞ z~/Wt ΩA7v.-PGl^iaiVQ:n`S|af*[Wy3?_\%$K~2 I" w"ʡcc3~A.::>yďٵ}zxeݧc$[Ui| q{2\v2̖a|Z7oBu=)K_dVjK C\)|BZahm:hgAFSAGˎyyx޸f[x&6)8H)6S}l~gQw|ppvpp[twqtdE6ps{ͻx79=[=59|1KHBkQL m/~߿cŗkQQK /~R|"4F3#5ShzP' ӔW"?P'ƦY @)ty؆"-t*?ȷ1(u yGGGG˗/zV$6gyH2]ý7vvxzϳ\e!bA֠yҕ,֝#63@@2Jst&@ yI֪޿O?~xnjӘÜ?>{)hobO1;?2RP~])KxEkdG+De߱  T+זÏ?̛aq|CH+a\4N hϐC<9o:s 4xo Ƴҭk&$pYRF׌r.Gq֖ tcXNQȁt%~ƬPG(>qį:;wfDh3e ^2CFiźTt*-ԍQj?.sK)"6/^dJ9 m]3"EP*+-QiyP ݴ8'8 ,gͨ iDBV)c/V'@dӎV1<0{#bjnFi-Y`j!FO+T gUT}8[*vi*r&1%TO׋͌iZ[:ido^C)[]YrB ½<gdƌ̴`W`=\~WK@Z<3ϻE\$(ouq@bH*-wkW:wDa| l:GX0-ۼ<@oCSs)eU0l7wO_櫯98SŒ4>͔)0б c "{/>ڙy<#nTTiA(|x7ecŒO6" N_Η<(ćEd\~5)o5 14ӡ~OIq0*ֽ ' ;W>$<KloR ХԼoZW[V\{_ē yDU'[͍ͭB&\41V2RXس}Q7v~ђq8GSקWW>|4@ r9HdL]40_% (- N2$u]^#JNT4*6kt !%66 z)"4v<2 ϣgd &Hg%mx33Ϡ`;(Tn W¡7aS{`C!JklZـ[ɗ:vr τY *OS$ׯyfB`4?|{Ʉ'.UsE_>lo]_g10=E D܌*#62Ije볷o ?>'*AU񽿷POggg}Sޮ##yovW!}߀HZ bϬ꥖aj({;MHVmm9YQ$HekQO"UTf#xlͺϓO&k" tV~@B_lmn T4{0'2s=iFVMJ|D+U 3":0<1/A.JTVk':81}."Ѫ+ *-H.F,Ya51rk._n[& J[a([>*TYj 5b"ia)jp_t)^fxgf8x1 +-sCIvV,MgO.G/[K5(\P!(-Z2 KZ}vtUl l"DZp.[xAiJ4q%H UAbm>;j Km:B s|~Cبa!m{r}6dDoFy,n5r]&-v H:K6Rzxۨ ť ,)!P30_[7o4ZMȅcMFKƓ痎y䟬G)3O6P*? )O\55@?-ѕzcj`Ox#]d H>NWXbt3Lu%쁋%  3vbzZ5*(,vw۱srqh~?~W޼obG47o fAx{w{u#թܡpviz54K9N>~4=../T!APvxs4&ymDkq <X_oJp].>η|zo="\I*;{qcNq0Yf]"2BS4V,)1XpEV)gԭB4` C0_R⇷U_RU 'MԒPzUTGiR,)q٦%@~U4-cv @\{Kp$c҅ua#7T $U Pe:'Z-Zx OKEJ(EA*'KJQ JIdAKK,QRNSf!K-{| 5~/]V=;3 l`Wǧ5%bNŵv0M1gwhnJ}tПf[6oq>f͏lŒ$MbHv.`\6ޛ;{'}+F< Q;[ySi̦i01zYTLQ3ϪI֜|,a1N1ςy]ulzBc;Tbwn0ɓ{JmS~IS7X>Wl^\>k\j"/z̛Q T!]lk_R ॴ6~W1`icȜ2HP-U?7יjr황ٳu<>XrgIEu{ tq!j~l_ujaT.[bȞ$}NI'&q9/b=%TQdik9BP "E(1cf1 ""3l+TovL!@w:-;7uk LfiT8?̶WTOdY^׆weamP/哼iz2ަC[.H' /.m^O=5чA)-|o??2@ * ֭!Mu~N  .y05J `S$VXR2(0*ƦonXJ.֔@G̵3ZdㅧSWNz sp 8HAn~k.oFm2މ%M%y%sV1www@q];mfZ Uۘ~X"ޚiuM7:ToΜ~$NM?sFH0bپGk668 ( [pOW7{y.%J`)i*hD4nyt.2sMM|>BF8WܙgvY P4 m=!E[QpnND.#]X)4! ^FUAj؂9UZKP xi?J{gAG 8u 3'2.ƄfGCKm --C!-.]B^(*tU:"tPz %b BãU-bvg͖7dtu}upo[___8mar5KGG @#'9*y\id8A@5KvRSS%3V|¶  -^@i"V,}AKN ?t~EӺMTO "+i85wr7' 9W #*$PJW:#H|etdŞmͫժ,cjWOy`5|J8'BZZ5:O44CjՔ=3 ;}(65RFQ0ae(-? (MՋ(ͪ !",#sl7tq\?oNN 86SwcZƼ;8?3Xi ۜG3AX(jH{0s4; \z\rh͏kM%V6?{UTd '>X H,M8Z}2GgtwEԝJIFfd>e?7C-qC&SʔVEEI2ѷW+Qd!jAӱ?SZ!APq\_8J2'i -2ԕ*-CU1Ub[b;KutJKVu !jS/ ZUe[Zxm(,a(d0LEX_D(pȤi[cup2ReU l`)B)"SO!5.]qt2W~DHRj5EQEUl׼L]V+dQq20RCYq3B8WbO:ʞťKeFi{1ӚsjjLf(MJy_S~|;Ef"GqOωBnmmJO?9;cWwbm}7/0m9>Yb߼ymƴnŘ\<;ظ8]#´>*ikzz*~:OivR cO.+1ƒlŪS(P4 3P(Q˥2 r93uB0R6HN &2':!;8d9'''6*̞*{}J0Zf|`J~}1v>RWفCei8UcV{16k{ ??B[u’?N o{Gs:wWQDf !K dfZr&-a^ÌA(`QINA)Zl3鋮8eNjEޙѹf7Ff08XAܽ6d7Y"MB!`$swoߝʻw<82DYZEe.?ߚe> pIz#g Bn,x2 Ճq-2u-%6팜V,.`w o޼;>9h2inC"W RH\}o+2*mqt!~}sQAlGʮdž'ZSeDZ5s1b*?7eQYsL(q@=Q@!Fg -m_ͪ+ߤ[sD0-F3=>wcɬ"`@ LN幟ȸvrsps6OfdoDSa͢-LGYu4+mZ@ۊyjiH=J[R*$=|{`LQGym>یlcV#i8W[w)eDA6$HBr ƐpmRbK7-MA Y*bVEe@PE8P4~yE,¥D&x7[ #AQ9 0 ɅH¢zW5)t&JA,f煛ht*+` (0㍡G/J3"s6p j9[ã50uUkh-U#N ^rN2#R>uAпKz1j]'Pp\"߃+%h,=Rf}U:@ o;\AArHei77Q߹)rTů Ae"onr~#5cd[1Wp-㓳݃$g[w,sÙo>c {+^IHJ,]6y)XMYt}smx,x,0/PbvgBLG?(+BfAnnmLUmȈ[: Q |@֦i.6,f`S} z椵s@KO7vC..?}d!nnly{FY扭IIݻw{}u܂;kO٠LwgR FxQ8ۮL[Tp)pss%|5774m٬Ab]+F Ԫ%۴k֜b?efժz/- a"7z0d毳>ۓ#}6G9,`0p{iG||uy|c0Bᙹrr~{wG>ww9Al_tRQUnn9t/5-hG !9X.ݯ޽{{Ҟ߁._smsADmlln q{"oF@Q֪͜\6Kւ}A^K)-RuF<TԀU^Dv>>>#đD_:?EF~O$/]$*y/291p/ h1.3ŵ}WǏUbV) 0ܴ`~yV`șdl>}W^~}ͺwRAF#j'Y(uL:9Ϋy)jfk͍ӐϟLAj͝.trdZ`i.3o߾;==Sxuyuǧ+ǰ!XMk:(Op9KbA\!!Wضw"j=aMXȪ3'ХX F8TuB#V`K1d@Օ6~ I+  s)olX@QE s _׆^i)Fi#E@D ﵨH* 5!ٔ0u.[<3cֶ !]a%Z+ddUTf֚P"ضoH(BVz% V ϼɿzZpR'_Ef^f" tJe1]_2A* JVg'cFJȒ@f(%amEl1]$˘2Z0l;&&9AD8Z6vݡD6n1?Yc 7RMlNeOۧgoONN9?{jUkeha~y$=82̖)ײݽURlՠԉxRAKzuw9h⅏_mnk`Vz^. \ngY;gi b¢tidשR]ҕEI:iEZJ_x(vB I%rO?V/HΫy>ɴ"#;ƩfS롇M1Ν9-Ky^ =&`n״GKFe($<tMK965.ѐ:ː3Ms) i]l8%Ƈ*H%wNN\ "[Q=co#<ԃ D͐\֓. up|8c@|w|減9cm:"63Hg W1"V: '!f?>dFIq"JJď/ҴRDYWߋ|_;MJA;/W@T \(uK..8l@Jl8B tlkW>%u~R~^Px#BVt'DE+A(+L:w"⍁y)Vo藎Gif"+nNTi-nͰU?oLY33ɗZ98mn{$Zŕ.K5ESUH $1?/:r5J3gAɼ=+I=g R77ViQK%7U^쌴W;sUkwo;FO}ʽ My~͓TɅYScQhI CaOl\8Gq< GpeW6Rzg2)H 4a\<=6ӛGQKt"bUH%*NMM(EyiݪUHБ٪sI |U,xG1Ֆ89l[tU1:2jh)\TK (J絙L'ۛ}{'x^9Img9Q<>5&v$>c{NlZܤİ HhC-$gՏ?$z#R̚ $w2U_O^0:AuIyחWyt@rFʴm|SC#ZuJŕl֣|OñڶTcTv0R9I"?K>34FKf6$s;"k=D_ӓVk汮Gd<w:*3{t{3[DOa^lǐ3!=\)g1c~&"+J&OZ$*i:Jy iz[Z.UZ -g,àpH{ ..l?r-;'B̞LjE3f!㝵pvҳJ2|Κ7kx0mN`dƄm.[u?LPu~nTCH$9{=ͭm, hU.3|wQĘ @Q}eOWDM6Dž@jjB,: PfY` EiUcͲ1z}}m1 £1ox %-« [w]_t2LՀ^Ҹj{ז`3Zy.Xii uC脧fmou*ôn&U÷~k3!K)3!7@ԕq@)Ɉ}!1I:|UZ~!c^Jͬi@%mtXpt)Nhi)PBE!Eu*!Y ٬ Çϟ?I~ˋ dK_?l~#,wωFI㜠 LY8#-nēc=T1}G%n߿+/ !'76NON ^sN vǞ?}_vu"\ IWW|4凘!k 7Nf<on9sE`ՕHooQ4A_r^Yf8fUerYmɑ=oqQ&ַ2:\hdkHx3ohdy{wgԢǰ3VT|dYJ1L&da&kb̀CXsWC~G@^:Ez1ZV@0 c J/HZVŒ.7P:72gu(I /Tzca^ hQj3Q^5M懆DXM/ݽ00+bґ@g>{'vΌQH*0]7[TN90;@m_*b2  X*E ORni%u-xU TAkVmL;6ߴ< %ʼn "-V L >2V"-eQRBA/9y~)UpZү")lUOTXO􊇣ЎyΜ cvšsӑ$s^Eӈq(taH#Œ4馥OݕYJG ,7VK[X͓RVd9Fli JZ=1:g; ERf7Mi?Y^ͫ\R\_&gAً):a{gQCA-?>QE59 1`˒iDZnHfJa88@Q7,UH2\. EJ,ŘҚgEDLig+ؚe2Y 82-Ԁ9V,l?Y·jsh]71ٵ6_TS(/YeA ' B$rK k]O=,Y3` R fUR\_X:zɿШQ6lW>MLt&rs$ L~R>暛65r|c\[:Z]]㓟g\@sa7sb~>̔m97QoSA6GHrnjJѝYN Rʃݑ̠ğ@ęS7%LCc*`C$dU6U۰PZ4fu6Y5cuk]A)[ bݲ-?%m<Æ߲f.T&H?ѹWQa[jG$XQ2'Vقǐ|zozD66߼{pxLݍīu{] i]0]lR#Sjf?04m]zڸl{ttzo<ͨH7MD,0'J!(*id'+1Œ?q:'h ÀRN VX! Ξ{q !JGjBm5_;9>EԶS9եxr*ƎU -YKp*HP%K_\ VҔ吲텗r<88<=}퐢Qz?n:;;sqoWb`BI@. gaʙffXf:cϏe9du!74J3^y/_Dm3u%`&U@@sUWW唨"o`D)$n#-3qR5)5rns d%vs56ݽe˄\ΜZ&h r}UFS.E$k㵧<_05Ru+ϵcCH7e9ȕ2Ü<N_Jxp5zxuTZ,eWc/ƮeC[X=(yf[ș>`֮3P*u9z;+ͷ,[0)6Sgb$@JJNKq*C.6tb>_C Ys&IW8V9#CQD["sc|@+H74/<0ޞq6{ 9)|o(慄]FlxQ ꊳxMݦ+/>ԦsFb|Hh{r1T-Ċtg3^*bLq@WDI▪p* dg, lg6vS몼;RHMZ`B ^/`)*HR*KK5 jHͫG`wloQ4NOO߿{&]uSAe6َ+1,/skE_ԡ=porگU^ tFr y x]afX' WRnQ幃3pܐywߝ})%Uy'x.{}O23:}[c\43ci\0N)]BDweM_>%Yا{[&~6y)p8?? b[f`f./V3Y{zr*r!G\RQCԢQ6Wjиw3=C{~!ѦrxV$emRǶˍB45 9M'$˴VfHW" (pxʑXM^NlpE@CH[pꑴҤlST5߶=m& 2YPby)J4tUϏ]'; >{x<ymsDQ2/ gNv^V2˧Yy+0OLlnLѶYw<p ĝk|; e3j{ KCB+sb9{}us8{ܼA$w_|={қO>2MEtJ1d<!dEFL몄 LG../ӌY(A;cB,RK y ZXFXJkOvQ%Ь& Ơxtrs^B w&W#(zH! |qB5vfcbqq̛qAPDj 93IU2Dv7Ͻ?L\J ȹQs|/rVd?n>Hs\}m}胦Cec3{<]#B%-9jȦC~cROޮߧ_掽8I`ߞ/e Ig)8|6J8pٌ'e#jS4qV~k{b́0 [LaSFrJ1`V5E!-R+HzlŎZ i8X0*va@X/5ؤp)8,_yW3Q3W⚶O[F~S!kcn+=QI+P+x0tXGKi) Di Dp"WGgW*%Nnj=N@dw`i]Ԗ)M$ :ʧ-QChNIr{WƤtnv?CNL0煱d3҃gdxL>E|ZSVl.CǸA@}I۬n Ih7 dew7_RrӘҥ1&QdO~ؽZZvN-}ff\sxx|z$ ̚ʼ-笧-7fԧM]+k>Q.%)rƧ$noX'h~z#?an;>923_\\hWYLKMOZ A̞`4DOVhY5/̦?㢩@a]b2V48Hq*)t)7Ua5~~Q$ iD9qyF{/n.@Ȼdw7W)uv[vEY_H <N<,wioZkجlc6}Vx6KiU@ݸgȴ8d_$*VSʫ!zqI(=<ЌaTNi}jErwoҢPb$4U6UG2;̕ߞB&lkMekh'P,^"Ͷ`^e6]9x-<* {rnq=hflv~Mu%tF*mӸzmζTѶӧO=)ܶR/iH_?Ꮪ`ջAiۛ˫ zV3:cO|*}QaiSӤdRmIst!^1z=T 7wYղCq`v:QM_!\EENȥjɚۻ?F \x*> !fG`ʳpҤY(*,3el* .Hq)NlI[NvGUq"*poVύ o3sKvgk1vVzw{3eo>w9tQ7,4_nR A\%򲮩0nl-wy3V{zBҴ}~!oN ;?on Cbӯo=3M7ᳳmL49j|xff-#;4*[8́<ͭ|X1=; ae~]ں+{˛Yɭ@9M7q6Ht+iC }YR,,V6__]|hY%3C.'֊|ܱkn>u=<9=>8=>"в>9T#t9̏R'MYmJ2LF]$͹5DtuuЦ pܙ%赂–Wgo>;;5*82@|.0⚛k$v /?۫9l}gkfebP&%3<21g1))1SA"5it<rN陨y~XϪ&E)!H;xvlSSe<1eHr.xKA)@]{9p̞ɶ.7;[=a MkE7 XU= iJJB1z6qSkR{|0S UT -HȲ698Vؖ:Ҍz1͛v'pMc-a9vL \:My{hDwHn}Rsߙpwv~jso*.{"C!g8HvDѓߨ[KV4kWE,iT6;;/:ngK1T=F\ Xz_ZUJ$,-!* 6bTFLdu<>>̍ټ2,OBI܋LJ@(U˙""< 7;sCXU>{,Bۄ" ?{t᰹ur|tYݭ''_wSxV$BB%c{̯e>lP i_ʀO6'36~ٟY'5ܨ1:yq>@ 7e>WĞ uDUeeVSjZ2o3)5RL_66- i[ٽJ6'ݝ}qtx(kUO)?c'fгTP Lk®&iPE:V5~J4xy?vUՒLɚP"Y GȞ V;J6 |5[n> s}%Fh^fO.}&<ͶnV"AB@K)d'36732RJ[#n̋ƪZR2 $egZU!-]@e)zIҰXӝoܚuauvYd2 5 /DimlGt*UlVj> .fvՈ=[}%z Rf2d_ Rr:*KK,N@ɄYed?QES!6KTIi15#|EIM+ѫA-ʏʵUCǬ(pwF/ƴ0pwcng.Q%Gh^ёHhs>>}sq~[w>zӑ3t3]Os bt;dqkޙ -)Wy@ ?lᳩnb[#LmcIiȄ:G?UL4D YB8)N[ڳQ((H"moZ93Ue][g^ImnTX ::< T'!u&`"A!qogSFY9lwg˗M覘I?ohA6} VJ͐!wޝ<v$G&4)K .'ϣbW RUs`* B31b\2b|NIdP\0__>ߚG8+"cLv dc"tDFYC^C'sBg9g3XBQV"-A_}54zkP1\N`]1 ,,i l8} Cl\,6/( 38w5ZZ|۾ì|籥)#_,E%Kd f)-eJVAl$*0:ŞiK-.t-77% {KV\GG&lA͛|΁(>f$NYbs՜2 Swi9o·lǼub榵sLg07}FŹݼ?G8$,F_e6fb .QUyBV!?>yͷٯ-~ϟ? < \22=:}9?]#']%RU,@v ؜F,ѕn4o|B1Z2~)Y<ï_FT [P:)A}۹/&Vs/XU?&H,Y4+.݈s9XAKh-)Qw<-B)@^|3/!u7iVs5ƏxhJ 'UlX|ao*YPSEi/ 58Xm"Ul% 0i*X/jR211Էl3*,N +U.zmfPN,h3+TX.Cf>*Qnt@o:-,سʬŒxS!D9=1Պ[A54ͮs (Vx[ cꅓ-~<^QC9f1ɡRcH }nNNwCuJ LoNSccOV_{?imfr屃Gqhf4sB_hWsv6Ǐ .)}粔8ٱ$Wrk&dGUkcWկg%h P+JIH`6^RDܾsT#(Hx4-#Yږ.Ydо/ZlMe.6-`O֒cڂ''yRYqCsWӺ3S.tgvi5"mgEfRO9W',A fJ!xHEDQa)jE#@d&"R_AY.ER88 C`.F E#CLG4'-{\| ۜKWzv$3qHm $4 ya<[fb01Y~AT hxLJ!,/3NrJEJV)J.qG3e*O#^Y<99db!g6]kl]ݙGs(ђXdJ Fu"nm;4>Ƽ NsH9>>ٝHLyOT` 97b,LA&|:=V24|)X3jF!;,8.?ʽ {kMcCz;uwW4w" s2̢;˜LqYGB\;D՜^0L}{7{>~$Seg07Cx zX@Dl>)>s,o7Ϳo_}ڸSǏ*džWwDNV>EɉcJ:DX~uvbA%0o;+>mf8$6ة'!ث <7$UֶKeg'ƳyR*l!c,[Ǽ)so]mmᤶ)12dMWRGZ ƙBږ޶Oi\Ħi,֚VdN+^DdX'JUlݲx QEh-m*eJv%W0%2R&Ry)XT:ΌRHИCI^ 1Aje>p=Fa-4VZ1vBֿh8#c4;5YB$F<32s9yExiNU)׍T(߳u6Ʋ ^ 'c"NdR佣ӳקoێD1'^]|ekggIl\Xt:P,nu7W_a -9?wr?a6lُZX/Y7ypӘTEHC8rj ævztXBcJWKJI#!@AL5>n3\ Yc(f1Ef;c+Қ0G^a,;w~SXG|w;~BMWlMV.ņt̽m ؋L<`&f)"1p,"JJ2f?)ĉvjY;eilB45di_2f˅(Kòk4$Tb=QfBizzYL%vVEؾFF{@Rs=JsX>)W b}UTd*LoVںśPNYj%dr]bP] Qj 0c(g-B״2C^ ŠҊ`)pzğ>oeC^3 d$PH2nEJu 8_zOa $ّP'ׁ8ڞFBbƪ)c ] 5#(< cHL噽z8Lk%H2#`%(XA])d2ׄ'p Us#.0"k$Q)EMKv4e *,=CoonщMf6z!zmf= Cv7ǞW Lc6o ;3ǤE*lj9x诞~f|=P D)vhK^ }N`%"F \^]v1NkO{5kV,{WW}v$y*+C;6ޅLln WEY>lŏ_Y A썌_H@|'sʆUZלvvݼ;z SbG1=մ35C`)nw_iP(B\4|C2t0kM\o΋"Mў1:๐B,c<#S">t% fXgd|⧘ pLZUEF☹0GdTLP'''߿#Oy@dA ^_:H!UB)R-mV!0@G"CO_ث;"ZLTenQwo_4eK~{{~tyoo=u|rXxe^ 8b9[2BviRbs9;=̞$W_ ?^\]ٮ HKKttE5Ps鯾7qԐ>f?Y"SGcDtt=$Ȅ}&X#{|zSė/MN`'FΙgFCC7oPj 8F%!W'ǧ u#'8pb]QngǩʂaIh\qqx+|Js{ ]g@42Lj1snufH`V-t)fkC* xs,aH1Ş5Qc4*Fat8s(ChӖ8ы'9-`V+F̦8C$ "`FTP [i(_%B"n=H6Pl)T ^ڐR8@=KT*E oiAYJ"d+bm5զpچA#Kplwtח YCZNIà"gZr( ;d(5Tq|8V4"jǨIAzgz=L5Yx$ChީZ.b&?ܹ+󴱕oqnmVCɜ0C :̹Vզ<dz}wp8?91[RXIUʖ8=GE}~~$m)[RaƶkJMR.Xjn>)nzq| "_jAMHD1.^B) ;#+&֙ (iHɶ^PlG˜j1(xHX,?~PA6h>?9?r(N{^6a_dOоNG !R`UP ҡTRGiHʧ { 9Lt?<;O<>ib\Xk1 W_|o.EW ˏfUo+yEgfx@5rf +e&F8'B(=3 fkd0On8v?iNY`C@VARcH;+DrDtc0i9{lM qcftu:]`L879;0\K퍣Z ޴]n?:aSy$Y͛33g_}K/;bx*W=L݉d539^.(>WijeDю5WMJ W tn\bRf}T9bVT5z/^ _"D,Q4m!Y6,B 1edW j;aGNQo^~px >Hexu~~ys~}} `u~?[9 %=SbEd$Z\pz!@݃=G'ݻgo?ןύu"J9n#ZS$>X#|B sĺſqvzW? Q;Wq'Gh)Xoor䟫sC'J駟>Pd?R c?w>Kф8|b/3 1 DZ: Ѝ8^f񊣑ٙ ʹ9m!K۾GKeG$j[ڄ\bUzU?N hv. 1rPً W)RHd9J 7(rpEEш𩒀1-MQ$6cT\H?HerHJ=[UnL3Y#(/3:|ղX.=(|f *9xrBP `U@WaMg 1l! ĩVOtL4iAl)3\_zHhwRAi|HG%/͚j9NHSSj Wͬ45T:UG~d$_6AuFfmS0ꬤ'ebϪ1ǔ⦔*i&s(2 +=>sʹd;8:S@yü1wC9 Fmn.B5[m׷y *w]3OD>^ x™b OD%ɮ{y9;ڱq-QnsiB'Ûiۆt-ֿ݃WJ(ne;LWÜy,-,!Jx?>UK֞r,;owlml' GؖnI)0!w9fVuuY+mTյ^SvdF$9Q˨7R6ZP'>col>Kj3rvw9Ez{;yJE:z39r wZ#E6dji[*||t,M`+ QhNxSݛ EE/hB(Oo=y_ D/^o2fyNYejSl8xINO__ٻn]S3mG\4k@<V%Jcz7hEl%j&Qxw||Our|l@69L)&gyggg?808$ۨ˹zW8m4deE6N lI|(o߼/_7m| 48YrS=sK&Ye^/~CO???:ppJ7EկE&m{x4OeDdBW+} |e\wVd6411/Mg| ƙe2s3M)!$+<K |b~!?LO( TLB,^ppQ QB$(el:٪T23Z1~>; +s瘙ՇRM6 1;o$PB0Ө2&HtG*q&lfjKDF)U ' ]I/K.X:bi"-Q.WJOjV+.RM2q:Uy&1,YX㮐DjN_靴@H pf\A*%|4?d#|ui#q:f+X9K))qizB7ƒM^^_#js[dnJҴy)K ZMX)[C$-KL7a)ZiVOɍ_᷎Abyx~MɩL_&>Uy iǽߞ}o}vl(Wr%x6+\N@Z:;nEin(\^qzouFXAiy0lƋu*pku7 l -fyW*~ʃ薚h*-Red1  4MPw]?<'5W...%!.W\黻>냃Coo//?}`8q]Qb5/C̜V;Xb\K;&X(z<9ZfgLA[s@R4Ң;@p؛mA43+mgV -ޕvfli5'.fu={r2@8mnt~lbwf[;~#'tcR,3qs (8QAS艔Wǹ*;&H>:Gy~Ge<2VMoNNN^gIH9=;?↣UMP.υ7mo&L#yQ,7[_o GF3L.jB"I`:DZ8#3@(ϽMi6xu}DlC/FTϥGZ(.C#gPfᑭW Dia^ECAHgp6OfHTE@&T @uQ0W( /AQrVY瀓r}UH͍X0vw|훳oZ\g9Jls[n$=峨X;<:8w:v*fU&,s,Z%j6[i ؼڛ&ܵ!w7rݻNN'//kSc7T ,R d[Kǻ9ZIpHhq*Boz"5dU2ٙuk~;`d2#vIWfU,ƪsƹU9;Y2ݧCc"T:Eʹ yMs~{vF~"C9sC}B/.\׊i0'^:ȥƜs5խ|(PmHɎ<#6S3gg,e5O'T_EMWiyg,c1D8;r4 d:"Zi[Ns-?(*0DT؃2ggTehAF!2_H|OΌKrQWTU0h˔f`ECDiE +Očv>I$#۠ЁQJ$Ob31Μ'/l"m(XRg,`)iQ6l|K1r%76E~B 8X\j#h/ne̤ Pu$0~K!V"a_oFJS Ynzm>+<Vo+7!1$fw씘Eq^%;Ԭf"bkR7 Mi7vNNd6k:(3)D=jذ)v΄Лc?Ӗ@p[PgPh"!m0bvpF̉7 uls+W.{6Q4~==so e2Rɋ*1a8"_s7CF]w^LZ{ͯك2)#S1Naг(^LC0|W2h]ѐ(hX+0D3)b󘚝 wb;uꑁh疏9%|޸r `QבsIƋcދӬ>H$K}]yRWnwsw9:qJrOiiG'Gm>ݳ ?29kj]c'p#AÜQ,B ¼Q$AV_xoK8$COvV~DCu-|ld+DO>XWIfpiUIQ* 6KeHXJJ\rYB (Zjvf6<xr55 domO/?|a&sp_=R6%M|BLH|@Hñizl+5^jdj#W/utJцPsrTi3ߩ#wFj5I4bѥV- RBEqX!sb.2wK_E 5LD]`9s m1xB a;rsK60D؏EњbE ̯i9nOb7ܘt22*jcfג'$v =v*lhŴj dK! *!`;iU.% K-*j)dH+q쌯LEfDP̦EAS d91TKɩ2-B\LRGxqHeRr}|ԴHՅHinv<*\'Ԫ#JE0@+MٱsVJ@I&!gUiFUaZPabV:S9rV0H1ҙ?KQ '3o6r+- "֕Y1:(1SϘSHSTaFP;PQ:81.E } _T]q {B;O#YNWY[seqj")v4,W=9frGܒ-ΫHw7dy&*0BV.i(7=U l:<=}kk-s&Ӥ9E۫ի;ə!VX S{wr>OYr5KgCslĻM/L-^2<9ꤎO.s"Fգu~#4 L݂mܒAI5-Re;?b t/Qy] Ax6Nn6Q>'v:/f77*{ĝd،'vi]/.yzOK!jcHb,~UTx53l"!m~GslЪ`<η4AT_Q ꉍfidc!ji^Kwћ6\J lzvwvq.om~7o")Jkvus"rܽ<ՓZF|.Ţ竛ˏ>xyq őOtbMvhsg'GFcaxw3Im8 iҥ8H:#I@P B[=IDAT\yz<=~NMJU7|9'u|HuyǔQvVY, D}*YP*\; Di)vvNj BZc@RHd@TBBJX%iZ XlCR39԰l8ߐo^͟}!}M"E6r{UVѳpNOh}Lc&m7}͸e>bϝ4vᦎ5QZ3p#fkRMP=£h˥֣CmW|ۓ՝%r6{~ jáShuٟG6\}s{ç~PWrÜc5M)\ fd?=b1\1 qNyɢ"0[V>^]]b8[EfgsI+f%RJ_5&&'lf\S~Ϛ#3#*k5;(725dbu dqbЎHhWldֱZeʌ)bR)Ss6m!b+%H䎖f %&<$&QDjH?fRE Z"&v e<(ux8`KVZAV1Xڥjb Q*8YJ5[!lPT/(]2Pv1 ГmM~4'ǰ1#ȘW2g YqA s9;Z,Di) ٩Vj9IVnEj\yVor"sKN q"X =.AE̶,NrqspǡO{&p}(({ glsqzn=.d/rۏ~ m w茧GI y?>) 193wN0|D`߯R{?r^ pǛ:wSV&- $Т_qȏ?aKUAY䀊ţ.//l5CZJuAe@\ RdgN8Zbay}nI.cV~ƫWj8xow ɺ`zG37oECp^r9?r-zc@gʩPo?OO9i?}Oō 3.JQd]y~)T]ޜe2!(g٪C7:-WUxtcȘ/Lszr-Clq,L@>u+ƛsc.8#FJsLEVKmSL >)^,lK]j!u8dYȿJWK_Jx RSb폡V#J[$q,t(J-k "l ,P kjiQSEEZ]YP$ Qq1CJ`>ؘM?HA}[ٕj@yg4F}j<|(G4fDWK"F|bꠢJzcPQR(.eS%"⥨] -{'X*P+iL*8lm ?"2c u`Ȩ OfRNA c"yMU'd*Eoң 3+,76Q#L~±]" b&N'>20zcD$caLa39EC)րVJ`{Sk'JTq3a#|3`(6yChj#frLAp[l4.I֦og6o߾1uΠM Rwn927o?}=LgׇGGkK?~wdg,fa<'V rY oV*˫RYpE$lre$Da(as"A"6qNp>Qs?J3 JYZ1ӊ jg,ėElyR04]QAX xiDT%^@!AR7cgW:;nyɹ1+ky>>9rrv/#մϹ\܋['^=;T ˜:6t n*|58Xģ[[9m7k aZX/p6W(R`7Ae:烢ļv^S|Yn=>zNG(_?76g/eq~z:8L9foOF#Xb$`Y1iR"^2.lb0( FuҁPݔ",4&aXA}X8= Q:}lƓOL NR&nȔU 8N]@<8-w$A*V`nZzZn[*z 82("VŊJ\`C&=8 54;"eA+Т f)IcU~YHX VnX;*ߺX'ʂ%CP͸ϳJJ"  і0̎H*ף[}آ Fƪ0/+.ȨKWAP)_J+J"1~@b XcYVW RZ5ө.t oũ^zՐ 漮vl l##xՋ5=^%;~vp*niã 2?ڑj̴1/Xr|?}b- UllZ|6iM9/&io>jHN'չmJߜ1kd'O.~[y@JՊ`J߂T\l J&JJ).i-B)5>FSnpNV u6ԯO8X5?}4N}~goڇ__^8uhG?zVXp's(41Fzs.`u nXAO'IJgDtaΗwd@$W79plMʌpی/b3eEjdl4/ Ӵڰ刽9$-_sɢ#uwC%;gΧ~䔟¾SzNӢ<-a*2Xj@?S9dH2 ~ct}} vlύֽ<)ӗۧ__[43bۭ3ud z}?Oa|rjJ:0X !.HٹТL\}a>A (!eE{'Wycleȧ "K,\ fbGX(kU#QQ-0;2G8*.Qg~甘#P6@9s{s7*KcbfKVЃC?V`o%6߸Ώ80Ɩ|t7oo6,gFybS2s'͗/o.F]1d8vbj0kj2Mgy80zko sYPݑqz*3;i(rh?/n)QƘEdȈʻ`C̀ߘK󦇭gnqe} Ww_7j;mC&# g0>KY6x!%rZ{8>=1/q֛7gZNNg} r!ӎXHR doqkmsA#` %Pvy^nδwgf̤z"ᡩ'y:Kr|GG{O<3JT11iBlx"g6x˼ DyD0$|q;hDp.p]Y:\Cd-CUZ803u`(3h_CPZDԁlS3d9H9•"/)ʗPZ\f⟐U`4-((*kek|_Y(u,~)* Sj0M$r3+,m樍/f }s6꧓aZ,^M@cE I"ʎ\_޲ň)giO)h4`Z2VZ "aO.%(Gܱ[&Ӹ;^̕Q ]42 Q%jAd ā0lP QLu9Bk$gCE#2Jf4[JBj:(;IYX_RWh}e];詀@Bnh/FʣOsG0I(dv]:IR]^ y֚'vh0 ӓ7'lͧ1 d4Ꝥc `7g8::iL ˱,WMi7=9ߜ tF<;ws,Kh[yP-pnfxf 4Q_g[4+ɀA?M\~=}%߫ŕ lk;Z{N3g_}g[;w>恷sk4 ins1SIwTI es|xw/_~_}ŏ!<>ŗvM^4#Q4CUyP#SONNO2`~8bcKx:̋Shgdǝ iY aAeH[N@S}x4,ؼ/Z|NTpes+@8ZI:qM<8od?ƭG1Fjq1@"6Ȓ %$kEsb`DEqxMj@8ʶb\xe-?%ز)I8e3'{s_7;0}+B4_Ke߾9ӫvr4lX9:<;C6˛ێ"yx~կQ*vs^hB5yG|ٞ$yb_"/e3K.07%7yq0=Uq _&=#Ե{@yd<ZȪ%ѷ/eKT@ accQj=G\vs5k y>Jl& $c?}-tQUe=oErkQ ancqԌWlxel{渽}`i=B"0嬝;LXB<;a;fP[)Si!B^;6r@1D.^S'' ֊d d3\Q霞U*P:vIY YsPm1@SS@)H |vA2#(gJl]ܡs3]Qavu;-!igw,Dnf7荞'v","tﰌ'wG3s*.kLxAS⥘_]Ed-"W5٩ckr<BeT+ӈ2E]ϵtZZYDYsSPsHjt,U1˸E#ʻ8?9=>>Qcӄā [ztMžD>jRRX3kXS޳ςeqL@RHtC\Iʲb o.6ZW0T.3 [d4W'΂[sM'źcfn~>ƽ>YZLTkB>Xb,b~J~~~v!/_XE>~דź1u+vT˜X[Llu`E;V,|7\#>C@Q 6f'>).41F%puZ{' 14}Q$Yb25S޾ /R s$Py}@Дuaz]ϯf[[I ̘bv<Ż\R7Y`sLclo\/N(,O D@̈e4_崋r.˟q Lsit?SmT΋sC YX%D:UF_R`0p,  &Wh"k ,/ډzM  v7W%f= Z3\U2 3?<7h?Z%lV+vR f*jIP8(mYf0s ֣IG*A%cF,jqOjZtP DrEMrLԆ\I )frMx4 _\8BKꇒxy@'9?P d? ҫ9]:@"7 ҟ!Z,j+QSeDkHUPfr䑲_%H53޶Y KrH)@Ո"6!WzAq Rr:‡wmᬒ*+c?lI[,2C1R e4_FH!i@Cbxks,ScGdAfZvw6-V=]D&"oei݌U/fs;8SKI*r\ c ܸ#~$?:rZl#*HRnB%5 ZVH:qP("f^dEdo@=:y!8?4_oup]__^]ȼ$^ڭ'vS HLT+1hI)EAy}Ñg,; yMut|d&K-8N$<@!\PC=oūR$ [xwEaj “W7JlF38K; 74w 7X&=j1{P`͙ȌJf %Ϲ9l`*r9:::&7?yi:^\^ J1Z$SL/D>xNf>*uy>WPdaIdQ(E!5BPU5҃*٠xQ{h͛7L & >B=YE} ⑯o.sFg/i|UK|a}?)|/A$)1= cf1jii/^ o-|+cnaC^6G_.{t/؛RNh N6<==X? {2V$:^mu DNH|Z&m6*p幸'D_KL Jx;;;j᫹H0VF_xdn Q*E\NU0Z>s7(+o}p;3en֞m~} NO"^Ck )EY^׫sURҪw^tChH fuzOzA+ Br V@,ԲL1Se cZ4@^M+ HD8b9չܚ42*3D hRhnR+=38BpJCqYZK*CZJS0 4fR("sJMy@RsEt)MrM<8nxͦzuȵu%򀩞5 r#th(%-3S\W -=(̚M@5m*).h*rzdt;R6D>T@64"+FRN/cX?gXikWM5FD;15ytj(bi5 ي2g*-#dK+*<+cCc3JX<Ը*,ݬ<]57|rΤq ͝"|~>8|z?׿V8N㇏ SA*ɉ KQ(kF{;fY/2AeFY$I\s`]_0=lhP\/A֔gv-]foL]DM`enԷPoY D &BPe{TL 8H@J0nR下Km-D"$Y6zcvss #c3)Ɠ Fa#wxZuE«KsȔE6ݙ^g*{.Oʼn&ΐ$XjQ.=k#&i@Bu(CRwGj0jY 7PԭiQiI g6"rwL*S!FyɆ_1THɊ./Q  9K}rWv hBCѡQФDɊ#f55n (%cAtRg/wEօ%+!r͢sJ]M h4T ΙUc$p֋rm!eU4U"sd2N!67]q;H66r2d"{+1 )gc$TZ_XakσS0|XoɧZ#Bb7I)KԴQ@:ИZK;}S*HG_'*Uf6 u26 "/N~ᛯqhap\ۻ*6g'Oe'݈<2^] zTEys[<O EE.?٢\1-k[1*NF;]r=DP~:; ]sÄ#`O j甌#w=]&'˿&p: %/b\qƒMP(2@gGSS6d2r2ɏq|`C+SBh\j}_gњ8u-sHC?PslUX[߷O#d76 N aqgCF 8UC.`8??3ԻO?YA''o߾CL@5/om,JUP65sg;7Hl,Zg n򵪖]$Ye|k,^ !HzGG-!:)*TOuFC/!YL=jr)Pkg,> &+@S8s;6#{ TW;oޠejJZZfS:5\wMHlaebGF $B@zGՄ1|&@CUݥK;^0#r Tg~k_gu+vӡT6jb L`ji(9R=Lei%|Z/એyf}#>")j|c2;K{lL!ՎNx G"Z#KovS DOYaF1kE Tlc-}8͆G LA[xxH﬷{\9>kN:T,Tjʇؐ.讠c.g yWX ci?ƅ8uFvƯ^kV^ pb/s27 D'LvA FrDP 7 &qMDYK|.7f 44uFؐ.HrȘltXpXcmQu.Q=>= ޵5Q}. $f5[u=v~yYW3˔,Ao ^|櫯|㧏B/z#^%5!IQ*\~"򅽽e)CA)]j+!2-n-X_ƪEg_w~qgh(ӝ폱d]&p MYB(5ɉQj;2Qw>⌼Ü4AC"#D&I40SJNAm(ל:\yV lmQrޭ-o)\1±ҪnTx$D B)dn*+sPxY6ld6;eF])w<* Id$@|[IʽB;y (׸AEB(L8]h (uEM>8H'}* M*E_  |ir!]hyn4~]"y |h7,. Y_V ljÏ)VG),)ᆳ.W/"("o(JdS ] ףUWRFM*hN)H:"[4'iVʡ2.tRBJ壕9>p:&k謝@K )~;89㽭mJF 7^{^f>,z"'S&# z[Fz޾g)Iw"^#jSS1P( '* e"Ů wAz@?ѝw!sjRҙ [;om!u+{ˣGRgG)!|w{:œȒȷzܹ5bpZ/ǜ&;y'J$Fޞ{ȧמ'13p#'4ngsvqvzlNޱj.KnjjeaQc~c eb"eZʧg &7n5~Z 5VF<e eA#_, d۹&u,>V.`fJHOӐc4 Okoz2-?JdCZ)#DFV9 ?nb"Rڛ7oLygB{? 1YPhO?~7vWʗB_y 3rן4V`@A2Wr4 ThθlUܟ7kռr(  /i ϤI ~/_>ʀ_amyt*M͓XI=+$^ Ԯ o)`2bYi獠lĮF 8i VB775GǰwtSULKh{X}3o|e*:aOFf.g6\=?|qqlwZM0,7yqb:IK/Ar4[<Õ m8os߾{΋ x*LB =k K/„O%d.ׂ\bjbF|16kn%T{5! Vffz,iUt*U{Uhq`˝Ks84IΛ 72ʚ[.N-GʦwY Jp \%|t :^f~R3F&ȍh].eGwS+%L *u型n*-E?7PбKʛf3%rw IdžT_$lF(ޛ jm*W '_R M~'yZC˻d;Q}R-qbӳӬ$bwv_pswP ;Lptt|t€H2w&iWpRC4a*'w2DNp`/Xs >(/OobfB*l?kq6?J4ʭm'1J\jV>]uyyn*ЯGkly~3CQ9}s*!?Ojvh" &{4w7A ׁ6Lৎ{{:9dMXJٮh>D8yu} bI o}jD!0VU,H'=>>lTLPu;>;?OH_R{8._AN 깣6ul4k5ziJVГ%:G' M:RmO@25gD"щA)M\SϦXY?N(VÃoK%~)0Q6BhAVav$lŢ'/6sahs{+k^D$0Lz/puq~=?}k_~ySV,HQ-~%`zk<88NڿiHrMNHjL*3EOe/^x5KW * ٓH̬B71'㢶ph'''Qf tnB}Z'Fv)w{|axͩ-D~||l P9(ˬGjY;Qk-L8)ȈDgpt[o8rj q(lm&?%]Z#ghr9WQkc2@]Z(W':$% b"&IaCQF_UmxN;B2*[j'e,_ )毚 5wmYmNjB$l|![&$6|j+!yl}fT]YK* &8,/`ɳZ;Sh !z/ Y]mLeYZ%e5'iL]]AFe y45~9 )zKt]+;5c_MGUl85n#NJ( !) wA #9싡THS]lrV@icwrGf;u\\@f:)vVXɛyC+6Ne %@ty|w! gڲ\)Q'#.Ny{s+4祔@hu%z\f*l!m yT^bV*MNi>͜+v9Nru0ȵ]w &_| ak:wo.?幗Yhtxx;"EfјK$e84I+rNP;WY)Wݼ$#P(U-7&O(r}x?P֜.;v3]]^,no"m+3D*R *J\Vg6bPy)>"Y\\ /cKݧL rS#zE,>RjEtcMDAU49y|8!d־ q憘̊s7*JwoONN>~5 ïQ+aK ) qtF]爫s39G4s#WH;;ˋ=%-l=A9j -BUGfK}y5s>hc[ވܸVK=αG̴;/xvLqIւ$B}E ?G?D#}d7fY"CᵵkRBA5ERYan\7~8]]pbtnvfWѯNUwL?/HU޽:;T˒rSrk*ÀR(HpZhrsuӭ1pB^x.:Z ?SvwԌך<@Eƚ~vG%)# '\_-TFN]yP֖SVwTmX]XApQ M 7AMJ.|Y}nj#!VS Z)ˡƗWK)Z ,̤@Wi[t*BvtBnC|ЀT[GnU^2KToV BQHmO3BxHPx\\d,jVR{V {XuDdjpD-pk}7w-W'!?b~\L^}!N_hr]˷GyTl}o2Χq773xm|~p>riNSјZ;ѧ<ʶ;ʯ<<&ӓ)佽zOk~Z& *'+D*CNT n]M:O*o]g6'q颺zH࿸O[|0ǹ!:;kL$iKuf7W`AkfH|+?2!aM6{x׿կǟ~ РQf^;^oĶ.`ub+:/Ootb`iYff!Mt?/O~񇻻EyA3Av|DYBL uG\4֭N}[Idg"Dll4an /0=&`A5c$w+Lf#k.s7^pz~ftlJ{y*мDn.hoMV5&+),eQk!5%vǹB~iԐ!6 NL{Mc3Œ7sbc7)y-4 g^%Z, \I0p''O9l7A? dI;ώ~oI?ܱɿxҩpuu֢6Dxsΐ#;9atdgu-3#(tI_~Gbǰ3sb?%ҧmPhފr3"9߯[ŹdJmW}:FN'^Q&I9q,\__"ZzmqAMo s*bRr2.\mt}h9gم0 v#d+KTC-ͣ"j9"e\?UPȌ6lXY.AT!.6Z'Uy# 썦x=qR2GMDi6.++4%\"5& GLХ@j$|.h eFN9&vO%UPLn*r@eyM=b'@- bYaU9u/5[!HeMR MB=d#7fQD^\AGM"ƀa[@'m IH=T _k%Fø<*F:6Cr/m{=Eug}JKtrW oI0 A`W0hIC 4ЬI57pY.Е[G?ıb![5x/RRQ22BRjg40(_ PP&" %'w_]kyi=1OikݑdCNdr*q混x2y?;T-u |91|b =1kjEu2A#rqǡ.]}˩`p]lR,}^]/):t(GwBvĕ,xjrBl~ã#G#)ce2|ͮ.?|$7aۼ|林jWawd>Usl@)4y*W;/h'~{2P|qcVVVke-!=f{ۋCBl8$8G\dq#Kwy8Ifʈk6xY잮x 8q/S~jC/X{]3_*vu]?q)̛<9Ynw4_̮ooPVhɺJ OD T- INUBL28Re+sLBr_TOE.YJxOQ Mr67.U+[XLVf5[4ApP3kѪl&=??3ĥ:YZw1̲y4vU(۟Up!dԇ3yוuf@W?9`-/Y|ei-acCA+UTCIS(,trVpˍM hʛTSI_.}3}/~n񀱍`zL>O&B{h!Hq3z֬OLnun:SKWwLw~;_lomE©=աjN㗯YŹGcT]夳AKTW+vͽ\lz1BkX7yܚ%/h2ɶ|WPhaCd1uaޒ۷<_rKZ;QOHbZq>qMk3#[?(7okc9} _Q ?sT;^eg-+Eъoš2Mk2=echŲ=<`Fjug[@M6zq/wrXT%jB53YK^k1,"߲B8[y)W7^ʍjq-[nP,d̲1U R[ѫuR[q]2-(3kps]cytӚ1jfkU7 iC6!TnTF!OXZ)JB[{M=YTWp-]WK?92"DkП*m, N1xRrxt9JFʹ9&_ҐS0,C2_W>ϷEQ%G PeGmw`ΜEo=h[S7pw73GH-O^!ڪ*JܘQjp,>4VU]x]ThGBq;SQ(P)Z4}֤2 *t/L?uܚWh<ɸ\;>>1ExwZÓ=M)4χ,n..v|G.x~IKv67F;;{ YE><<Ɨȷ'*ģPl~sOwZG˲a׺utSyq`[f3[L $`6fBE5)}>@uWjū1#Oy-6SGY_.W2 u0+~eȈr߿{ǂ,%Rи5[N']^|vuͷD2淯_9\_ :l2(=vDd(4Mˌ6\.~qЈ hg{lʏNj %l]^_\F&{m+6N ^,ưQIFBQ[j#Rqr}~<6UVh{zz~(@$_#`Fz3pDu "WOɏol%^m 4ih)J[{|h}ȳ"6&B q3&vpml.ZYĉs"iȝ[sq7/~ӟ޾g77x78--ɾv$Є7lc z%ҧ 3Qӳś{7O槽ַ-u:LJw=&b!LAJhwu ?i8k>ɇ[hBi'''G0BaP}"$-DDq|1Skᡂ^v=8bHb5`ֽ xUrnjH@1Vjk'mVISxVmol:N'ycn#AwgкS[."ym i,kv5f7sDPGoŰCUawyrtoto~B_w wogرp|lr|s)j ѱpSL0Z~ADߏ?T5 eі< =E>#REƪeSNP2 btJ+pZmM\X3IqjCjHԐg6neɉWcY>lFt:i)0O;8f\6gٍhv܋ BFpc?~g^?m !fŗ_~W@G*Nv>""u#&?wwVe+'%9 R.}OM}.[5}IGFqe̻jq#1rqq370ewZ>ԨVhzJ p)ꅩy*[ ]̐yz#V?DY *GTf%Uy.lma_.TVw'l;?6ۥO-`R#ה_A5.Wq;.0RBB3}%8= V (fU2Do ( `&Mw_{VY+f6BSn;AI;-C;]Iv"&^iOZ "'jw7|P qCW$f1G2x:=ب)z9$5d_tES?}狹s煫$](WZ88,@gNl?w:af.&Ucʹ;7OxtOsuog7`9e3D{S[$Ω qqЯlsbS*W?I,n[u -z]cyй>\D c`>XS`jra4Z9{ͽsnM̓=1~;]l8Oi o3*2bc"wxt+!"{{f!dNOшNbHAx46|| S>>`E ZZwJ[]]3Q_ӿ̝[:|wovm\0`HZ](AЛ} kG4Ÿ/k[ٛG_'0.8IBz;c!z䎥6oHx^揳(;\yd.?&|jz!A"^Z@sI-\k2d_}yF:eg }p~> c睍+Ln0I=y"¿8X;۷{ZhSOa(uQYc |?9"?h1׏~H怰/s{2V図PmŨJ2%KWBDAJt\=PVjwjjz 6j?j)*j1`@KiTr'Edlj+VA:ix' `{*R.+JpZ( \U.2Ti`zސ BƗVCQ MMGg.%r塩[8+%uA)oZ~`ḨeG,rj AqjtnV 8 J[U5EX6æ{\e@7 =r[ j-D@PkxӅgV 1tnq kuUg>ΫW&R(48A#ƅ3#n?Xky"QVAZP Br{!?w;8&sڬOjc \'׬j0 ǝu bԈrs *1"U^bήl1J&@.tE #r ԋL{}}8l. QIc@1@2$NTn M7>jFؘn9N27-}.e0rlN^}an y'Qh=_77󳫫+DT׳Yb0 x _nDbJ_jyz .Mf՗z ~~vP9Up攳"]_^]\<+k\N` 91O6zM+)80wuj>_BRM]2Ge0f[p}s>Pm=2Ai\Ah2ryqT#]#LZf=T$ vC8}Ow;&n^zџL*/h8}UTK Q@yRm֍<+wO{x+`~?g46/F<6XH3[tQ"?ai/gċB' ?$(XDԮm-H+-&$G!!_E^ .TќBC|0]jo`腫 j?P+gh4TuVqI54v.Kк ,]b&c~nLdfSf 7IUf>7$ztg>/卻\yZ;?;3` xΥ{_7W^ NOyַMO?~ [mD*ɮ,ã1OD=wypkkņejF&IFkM$эrk'F?1CujBT`q~ktyDǻK24t|cAّcFՠz||ڇ@ʦ=DwQ^ k"T*g!m9Qln#Q0zgKR<\(Ħ΢"fĦ֔~݃J;!U6U<'z*;8 @2`ZN40 Vd)o:SB)AV 8M4rH&hz!ઙ1 g ĺ2Jug/hVz#}[I@H})5VT#H MK.K Z !;U!4- @S]aZJ')4 Idžh7Z$ZiA/cij / .;(7q@tYIY,( PGY/;}hMDSU h^4.tkå.74Ir KHYʆ,FV~ߩ ;@cT u[hZSᗅB341AI"~3n~d 7t{4B YT6[ލã:S>^?m*xraw#O.d< #mi9we)V'nmyaTiqW9z&t-sF~Iv'gZYevaHvpW~sU>͞|)aĩm₄xr Nv|b{?_ڜ-n8Gt=Ůn1kg 'ɢJ1=:DJwvA#$GQ~7{6vG;Bۅ|n>*Lh<+|'nn+"iYsPgmb뛫Ǐ ~P~ (D/M•@66=|@boy>)07󈩺{,p@&/ygkG8Xp(Y>swrŒX}K:sU10 mk[/wbOu6SƆxs\&8|,oLlnw _'^wE &VjF4[acah0Ԕm&uTSM^)~7|+lă}8L&4Uճ!gxƞ}hrƢdH1S3aJE/ wK9[ԁ y'Xj1"&3 )Ll%E\G6/ݧRlXgMLe4(j$Q #.* WԪ;)C0:RPR 7?+D紾E@9E,GVt<kPMQ"pf uo;b[peۡܔќO-;E^PrR=_Ϯ߼1s ۟~iksg>{e^2BZŋ/ӽ}R,[P=,zur!;]~>L[k Z-VXe58Ss!Y&LQwngs1U笮.gg񆑦ÃVZ]uѡZn1[by'Zn"ې[=ŅTLÖ4 Frj/!M>A Ͳ#^zE Bp~8[ B82f P.J ʚS$U.8r jrS2JmIrp$>R,QBpba4`1vWDR\J (˳m hޚULpI -̬.5DVMM'!/F! }iC/y ҭ͌F/Wj`[ '5[Q0SjpdI9>I=['iDQꂦ 4Ԍխ]T0^Z!1IA[({8 e[6*oj @Jh`sؚ):#}UOސ@ ]7˖ S^C p*@8@ /)(}U:?5m8w(|u<mm߽ѭb3W_efAɁgҐ}s( # Z h cuE-!bQQ|OOEPؐ\'=? ζLSQTl}9[)Ն@)mӥ /7'/nRy.ɒ>뾶d"=LNNvrSeLy\˻1w 7g}3'EPQc&&c#k\>fWPA3|0 ur]/WFn[>ȭĞln(lr(^=_v5x?OaO.zCӁƈ,#_ԓytp6[e.7k$bnD _7_Z0pw?_~?Ç>~xEn$^__Gtqxn3{SI-T^8X1(`af,y5&$@u177V]msߕ^d8(VTɌq4 QgǘO~.w?NWoPkU7IFj]nTDwcR[Nة2RN.)ߟy3ƫׯ^_k/䦣llynqpsQaHS_^VecVWX hV/߿g ,-Kt'WsnZ퍳OsM{ɋi>iAvv./͉&8F0Cx{%fV1&fNϪR"0O}|(I'Oxs888o_z0|w Hv!.ggxŋ\xc=cV?+YD4ŢUAZrI)It]SATe`=%s%!{0)kJ!shs mYnuQ`XbJqU]ҫ٨>4SQ_6Y|iTr..r(b>p%LrSZt \F\u3N8>st{ggBgcg;_ DFח8y1pn Cv+OD19t?y憩\Kg.hw!(esg8pª\\Z"LrHB ܝ|+ƳD*U'3r):׵゘}->{so0kD,4c[x$rV4;Mw'{rѿ0畤2G( mމlwgbm ǧ]`:OY]g._LS|.{/D4O?ʼ,Dl˗vC&_9#,891w)p&#vkڝ($*|3WA6ȘGT΍&)8bS/p,XP^^x %+0]mOXYqIC2ݳ,:G-6Z&UN>Ji+)oR_ٞgÉӡ";UKf[T)uD޺W1Neh /EWPhph jE]KXmr@*vvR-?u=4u!? _ES::Une qvl)DW]. 5 TO9R8j${DԨ*|,qDS(Rj1F^4ܒxj,;q\b'nbi)>jX!$"bL[3V5Kl(PI>@Z͒2gYeze3әXAG(Rӑtdž%K9'ImvKj75y'[}5C㹧Vv+&B_W9~wXrGIlrlɆ|1ѡǘ=1tx9-IC,aw_'絩S4d]B>@LL=9Uoo%D3+(Zո:JO ys6OYEXYy*-vDT%UYgl(lzc8Lhe!mq5BHT\x%]؈Tƕ[ʓ{V9Fݒ1A+#/4;Y1wE<;J·P5n r>Yuxwp6cNh4rVqopEW^[lC՟ISdA*$v˥ņy 1O\uq|v;h4NL<)da)pI[.mIN'|d\ˋ38ENfHʣ|Uبo%2lr{֌,;JIvm/;7LhS(}=aTT6:1عK$hrqcwax~+i (4!].oN5Er/8%֌Bi.}-{>dbOƿ /_h]V81''6ME,>Xф; Sʀ/_E""C_]^׿o3l{s ;i=A쎽\b3uN Yn[VtD斛'Qa wZA£X&R tmfhRoPpaC]_ch9Ns:8XVw!gRdEjT ,5 Eju//`E)t{ I-]XOt|\@Q0 `8eF\Aa1/TH3 # "UyN LWP 7b!e,m{LR l|hb}mFG#'B0YAL 5Pd@ Ґ r.Rr7IёmՎ^Дe4z@o$nꖖ=1Y&!@c&uX~W[M|0PZ0G{V_ (Ic$AV.]m C9gG&Д;eh[U fEK@ZLfU-ة+p UDSRZ\ٺt*og3R:[}o~>O///jcBrC <Ma3✬ kcnDh㬔+!gM-xn1fQ\MI 2F$JCJr P5 e,c-y}i?7+)ݑ G1$Bã"R3CU?TK4p1ONӟt[:Ĕǧ&UnVCW;4i6Fڅ_RmHeP_8AlmIdoPTm2!y uّ*09~!1,LK`A!3M@2{P~އmXGGdž=F7_ǣwo~$]=ZE{x<<>a9ZMnBmNv/p#.|ad]onGBHv/; ko?=D#OO"3EjVM+{/8m;~yi25V=f8I)(dXl*XxFPcSЭ-,掘seǖi|y &݈QN}),x3rkڭXɼ,nXSCr섫ܶeZZĆrh6lW1.[N:V^ÃG/HYv*\:K.L zR~}sMݝ1%5. !vܢPrdQQV-Nɕ&.I4ƜkKi?cϱ<‘Y|F''/Ļ5,kK_:Qky*#y˺Ur+q#"y4ey hK5nes!ˏ_|7/^v,,?:9 >MoRI 02J7m/9 sW2!D_vC4;c)#±2͛/"cgE9a|%Ş놠Ⴣ :Q^K35$RlX5 W* mq-'FW\)ESe{]FW-&w(kL DkAk<"+3PC)KJCIZejކV{Yb.7HSvu9&eI*+P0Bϋ 5?/m1>KXz#1=m){ΐR?;D);Yvmuh# ĹbaUf[&B)zmM鳥C%8LB&^Ou;6*O"܊]lrm}}NyM dt"޶2,c74?"y\n_Qe¼0m 6|X:(#J6,3vלjbo,/#Qt})u81<|~zzfX% 7Ӊ?^),.Ɠ|??SQH 72]ni*sJE!r8 oed\e~(1/% 7BĊP1㊏) 0RR13:tGw4mU Ek2Ck|ml@0[ dǤD,'3页"8Ȯ2 Z lxhiI60à0^w61X.'/3\(:m5nd!r#=wCϻЕ7 Kf8fԒ dUDQU񕳴Bh)2/>O JjeURժ>64 y'_OZ2Zd 7@Arqsyw;˵4d~WldbY|CiaFOaɹWq!M%mp l F\pzb?0Dzy6vY֪qMy?<b+d:{Frz]m͎6Qj[)3xu虳esKPSB]k[p}]]eoO,*cfЙ̊khwRofRE&x$:owttOHWᢕ>uM 4D Qv`uw4F3Dk{{H Ԗ Ev aٌyM9UyM^Wn}z #~!b>4SU~aXVDS}ӂhw;HO IX*>JyGfO;;? Z)f5܁H6G/:Qm ,k/nXVab'J,z+u>o 5}e&rzޢkT!I 2 φ%lK*WEHe3$ JdDA]̃0nVj#¼I:Nonn..έl "D2+̣Sqb'TQ=J'e]hyB$ZIB}jDܲsT{Ȋ^RE:^}R>c63b]` lP)eɵb`=N.=MWCW? o~ャoSh"o\] %QvFkꡲ anAqeiD&6\%k֠ʳEDB&cf,;ZoLm$!;e\md //!0ٛ:i1xdwm|<{`oFg(R{Q.H]?Q cshAfq)~c8qSYjVm籾%c4.Y:$)ЂKvARF!rt/yP4ƌ1{ \]|n~s}>].fE>l)\_h#Np@EgtZ#_ 2ՏG朠 r<]Ķ} U/pXm ט:V'ӽ#w920zØԤc7:1JXy){& > L_2Ĺo)8T_#"R&uig[Th5<`is>fM5Zb51iVp'w{7Xrױm;y\pP9!S?+ȷn~7N΢; ゚7%`[F|,@!?K; Og ۛfNsmbQ#kI<g\++o_!6Q766Ky,1vZO&cF\]]?$|,s~##`P1*!=n$XpnLu6ޢr਺r\"KF%~XjW[FSl.8zs5 wxw/oddcu31+/_k'X>67`xVrҮ,%;*R>ū ~!4~_}o~susc.o~qrowwlŹx:%xoojׅKx,"Z9[$9&gS{:X"KԶxy}q@[Lc{Oϣћ7#pRR%Yk< bxgIR`PS9&@ JhT#4KƑ2ʖ~13e$ ӛ K6T %Tܔ#5s7`~o"m4QlDFǕl Lp jjeLSKVժ,* \Rwa.©TZR#tRq(!V#oBRM !^;J}XCr5lțZ[<%[QIB5r+vR@Ӹ ) MLjJuH F.@(*`Z,uǦ)B'Yjd ]NafhrNZK[--$VH][Bh٪,_>oʥظTtSuSF؍b pCneu=Rc6PnR'r ͤaB%MCYV~!b:?޿>|O^^_ߜW|Y8=(RO)p4Z_Wn_rQ!}b&7W,s3TT;]y9;_!B(;e  g\*`BlcOLJt~=:Cr>fP[jr|/B|( mX2a"qKP:!'t1 JZ^"ӨI k˯=_NE6Í# F %B1)j[4`(5a46e#yYe:Z{U($tϥsIboHPID1Iw9Hw`hNfAz lZNLVbdR+ۡ WC'773"Ӕq(FWŹ\qL&ٹ1Eu No.{nm4P]E(!\P?tQ>?i~X H[VG٦jFrcPF- `jSRe8մ?o-Ó/LW[ϪRFƨl`f`(""4<4:z̖T#IglTc/l"0=PMLG}*'`}%GYdJj"5D^6,M5D,S)P":anj?};RHk\s<4~󯌍!5n*eM1p2 s֐ƫ[5IjjRKMZ#R»aya8iЫ! A!>6ΐzhb>t(# .e(D]3ʱ[=5 hM^zEzD҆t2 ݤcSh~4uk^zAIjꮜa;B4L3z#jIR" MbЭTt\ 5IJ )nCxi Y#ɊUiw7Av?-3糫?}z{o?}ӇN?8<+Q<-ENVm>+cJ$JgB-`Ma' 0A&WD)v[L[վFBc uqtNPW2n/.>}¿g?û?}ROyΝa9r!YWݠV~JLYrrrQՎSӏ|IR|a&2V}ܶdsM(G@:Z&B6SH{  !\f!gg9ע\__b;w#s#&6 P#n0޳1qȍoz/?0sOyu'4C?xz6YYTU˯YwFUOǜbݠCc<=šZe0_DPK.B% tDJ)er~sԊy6 ze_ъmO\ԐVGC4<(CvL( (n#[zG2f9Q0Oø j}%_ӅٸGSyX)`Km~9XMʒ-B@ (g$ v k޿UE.UtU 0Jey&"MҌ2p ZSSfh|(h5. /L{_Uѩ5eeH'/~Rկ~w??Anz&]2/xMWO9%Ll;mۘ{`ED6SB_x1(mGKOg 6f$_|A]Jə>TÊWyiA|~3b싋sN8R#*M++HsBM\o_Qh@X3V"'7WH.Ȼ53DLg{{:¹ZHm z<욲l&h ݭ6e̚Mi0ʓ[agDȲ4nmxj tO߮#)[uAl,N%/c(9wɰK>RN1GlT(NtD(AC|F.B;/nYr4"g%kAr4Zy*ݻӣOJ~:ӧw?߿>Ǐ?o /sQicgs3ηe>(H~厹:J8%h41[tg {gDī+8)&@B0ٶ?6Q0¾VBӹwwKZxaF] nH]K|;$vٖrxKVV[;o UsƘc !yMٟ~==N/fL-JTgӆGhJȆ~>K4m\PVoMyH[7v؝1,D0-+a?"ɼ r`߬|a1Itjq{K{Fwz.ªJU"~.jyg4L,]vƇǯ^R]5Pycs><ΐ?* 9gnkcm~s{Ο< AE\>Imy3E@ X/lKZ}8Z[NE@'Nn?}enAwopxׯ-Oߓ`oݷ5휟Zb=Lc~*R,|Cqڧ-sv~f>~I3~P2><Su}|r{4rsۺΓ&"|y}^0wlWWg?|>߾Gq e~s9|.8m=o}VCh:0;FDcϜƯʋy#]YFM},)Sl|^?nE NKAb@f]YEuq#nTJf*v@/_KEP Z3rp7Zn"wA{ / Do;vV0eK<(lePRd"^E6gFysDƃ~'_}-i/..N3kI߻ܙNvr8w)0|\ZD4Ei8P!uk!,6;i9pPPX@j {8*}jm>cPW(Ί&ZJ%Iv51`n0g˾[Dn3!ZPX&Cx0..F=3eF wƻ;c^;i/OB~|Sĸ6=hvNjFUm.!T60"2 3GCStwssߑA9(Vazdԧ1 JpM*HkEkީ 0SWh\A(JWkBD/+mP-S-7*OvHe,'Г^4ɚb \N5z5mfpEvTjju8 CԚ܅%c~`Q0H ŘU/ =Z lMs:nBܴ]Rسr`O5h (@QJoqwoǏ =w^xln߿_VGiWx9b l"LJmYb< QemIy=rJ;{{+K /y1SͯB% ?R[vLY`FTlS~/#ٵm`EXO NG{qx=ZdxD#B'Rgs[ت&Nb \l2Ӳipx(r;* SP <*"*Ґj̸0fI"`/H@])ǛA d!]@hO`hB fF)VDh1 FNQMB<%13TU(+L\5jyS-%T*U;ﭩ'&)w*,*I=-cf/vs&;>Qmf\.7N#Th N;W&^_%B\Aj" YjӥI12䥖A2+VZv&%5\|IR,q$堕`'&>HͿ&+sūb0')&JW&θyViYvq}u}駷oO?OOÿ'oO?^^fv1jv;/Ab7$hQt US.%GM66 M!^9=uLF m:x&&o泇[ާ>w~wnauq3;Qx#e{u~*ā )n+t׽;Ώ=mwwB\`0 Gc~~pNd*TN,7gD^WʬNمikdy6-XR@VU_I力$*Q=F?[ݢ2>P4YSK&րtӧOW' SS|VY]M +| P?=>>^ˍXyiu'A2AZR MհK~LxzC5./)2E֌)H@\{|έ^89fnƜRM KRi(♍ƣCP~p}};er(p{w4%{~cC]11&&bq˸eht—2-^jG[99yMp}C_mӊxQZAhv#<*/#8Fzh[}'&i ̬Th60D2yd/_V}{BP^D'륗^8"ڑ,'!AC:)*`\_C夫6eVF? ]4ϛ2P@GgdJ,7tr5'_:Ha+Dok;ӧS F&>kFj Q 9J렜vAU͛/.rec@auuQ=~rO?ׯ-bcMh2H6E=;]F3fTy1Lpwqyמo拻E6| o'1jrjg ~l'^o\\l>}~ @'OLmBp86sfL3F|HA|)v°a4"QjQPCN7'?(W7e j.;y8"^y` 2M˂/FLf͹%Ri"Eю-5f>]7ח?\}<].N?}z/ϮOoluq+>Gް` 6܁5ӝx·c}:Gl>˥5APۧy!caN٧˳SNjO67k{sNsk:FqCȵD6T_ݝ֐HkHVۇ4'+Hn9uW>S0q0mm+e ˗ZUIӟ"!` FDyPζV>|:ƓӧA߽{N(_`eA ݚ  (l>_y+;+hm~}3:|_|qzz+jү.rpx l#KSbE5+g0싕X#EYb4hXe6&|Hc Ͷ-W8Q/hrh*H :N6T*,;03ŎC1'IB1:z4\] %K\ K'%jh*j(C;0kπlnE-0ae瘁hzU?hkV(IF0xF1j[q6'{Uݜ(7.+nNCVfژ2p ߸!tVʽ&5ڡB kqA&Eҫ6rABn "4a_ $+:v ZC-T_9GUCa"WwV4*$kFJDpdv7$2@i=Ul[VZυ=dD'BJ|`L.i1Xfg J]@DGA:L7yn![ 2 OA8oMD\ge絼g celmMkzL+dwsBDϟtç?wo?>~pyqXoFG 'CC{{kgغb;3I}&mWD{t2/i?5šϯ3YN?_~Ss \*#3O\`44[bGSp?no2i |2s|Ô -rBlid;n|枱^UwŖZli$ُ<3/ˏRTS%yx2A$E81fX%2Ǚ-g=aśgH AtKVRN&n YL "qv m0wțnB"3B&U?Gj1|: ȝ }n( }ջȦWQ/`:Ԫ¤z$ tm(vC;Ⱥnۭh" O?ÇϿ&<}w? ,~+Cß~o r7~|9̧ &m$5+7 &NyWK|0$tbp-,\o==;σT{">==7_rdw즞d~ݷw^]]O?~rY{2GV[zaXG>|^-w7en"O{)hn|^LFϬWWEY$KVo^<1^NO6=քr\'A&fKo[ZRo>t8O8e{M\$LO?|xqqa,>v9bO[r^9fJ<)Y=FgH8d[vu1olKZ <>1x qٮe'ۜ!v ar*븐x8V+N~~T "[6~ru(Wsw?0SWMcMЋqֈH,'t s;put4m @JL &w3.H_NNN9TNc~a5gFhV^ ;>#./=m;U'0~ ?q>G>~½+[?gm>g<*dY42N[۷7y6~BW_0=7]!G&mָΒiSs[c7F?AǏȪ*gaCqN0d%Y}{lI3B+)F5$Cb${legIp@Җ$,KI 1}UqSN[3M$[PkN% k8a+NK6l_tE%O"mWNP1r4!J_IUA aV6A4ɪAr=Mח&:^[_oT_9&EMбk[*#n%5U]ՆM .LKZ%-Ι6\]-چ48v^h:o@]U6JԦaUT8,U;-e JkXx[+6 țO < DS*6FjrpWrV*Pw[y h(51Jm>26G6(4b'0 j%M * ТJi>Y?6ycFF6F\Q.UgW Op 4fm$ kBa8%&]DkYN㲷H#N䵔Ƣja74!r]E.,DZ w⇜(-܅&gj)['9?$}aY3S[L:Qg%<KkH^s%.DWrj@V-!WGnjKٜNbלwIB# fߍ5Om:uD; ܰkmq? >Y`'G'p_{EoA,{\AtW?r O e<2'''Rل hDZ %2ࣱRےZWPM8c<"oo5E=sÁ[~ڀ,a5@$GbzFTݼ|%9JyTXƮDFd6RSy"+[lتRc.6.U5IrI*g>'mQǾe-C/:* ~4 F)5` 4p7i nV#w-ѡpϊ1bu]Z*RquѝsgS/8jog;yE(ja3LhiCCۯ[jXW!N(7>Vw׷Vwǫ˟~?__?Ã]a_~nlDR7/MsD೛+}p<ҊY+`)aZs_71PWHwzp| 7AEb>D Xd}Xqw?V}iX\_l~~g"K0;"xa!Cq]<&}5S9h2 a AH|P;Gg[4ug,,ѳU=|Pi=yއu4ϋ2}oXӥCg>;;C:l߾K/JMǧHt:;N)BP"ٖO;9|-Xsl.)&K IҽdDid3u!M ؈ZaRٌjT40V ;fSa&&>H4j鴦UUe㦙ϏNGぉJ2?Zsݫ+]먂%PcdX亠р jzyCƩtJt , 4f,C{cu u=ؓIu:Yr#dQaXKAb6A\,Cϐ%4 92TѳRq*NbHGMـF/B XSdBªdSY?b84^&0dӄ]4v*U1z/`3LXzSDɰ4q7,MQajP!pI6ΩɤfTM Qm!Fض hڱU0LZ!Ǫ.L>rkJ9.v)7A$R۩ax u4]+)GE,oOv[E4e" k/BF̶րQLl2 aRèilmZmRl4 hkŦSa FivoKn(l75 嚤1މ"܃z~ՓAw%- >//q囬uv'c׭1OW?W~鏟~?O?O??LJ%ur\p+o{5Iu`2?<zX׫݈ &?G(Bk3$׍2Hmpkc*oĠDZݬ//>;|\/Vۛkǟ<ːB27KPRDm 1 mO ÉCDhB+yYB%^@8 7-ʽ_ Cw0[ $š1&Odۖsz0Rt|roa '1 szg[93ʄɑ`gB,I Ӽs?MFMʙ16C[}+U)&ߦwk@&-;'oWNAqSsixo2F& &s,6;a2ljq{+t_ȼ L)&ꃿ $ Zaf=Y5MbdW8LX6ı>Hkl?@, NNW+Ebhءt'I&n T-ru]JrgX;,w'&)XGG?FH&1h ASTJrE&-P J]E=oM#RG'$_xEj@ZH$,P'5Ť\^xճdbfP 9|{oJ.'+|~ku2`wMðls&7~pG _]^Wzx|g?_~_uGёلH}F.7xWVt{H~g( tsS1Li 9?1zəxPϼYKP7Q:/whN63g2^h6ߺڒRU;xF$^jrLX %N ^ֿh8\.Ⱥe<4.eqK_Δv7kU5T*&/ȺbhUn+<ȏVY*I. "PAvLCQVRST,w2ʇGۈLǫ?r\/9Lj=hl(Ie{盉M$EàUR4  niY$KsL4jBVUJR5Ñs6m+GF}<F.If j`T0%:QUʸu]㴘KȊs/`/4 ж[4ޜ1rYN*dͰ5T5jd ,TA# nG*Z*4]&\AJEn Y%a6*3`Zx F|4}7?h>tK(+^= 3˵IDATC.bwS \%TKͳ.InU^!`libXٛeWw{s0g}tnWˋl$Ph1!GA Q2i䃧Z"T|9cK:$8b>?. uݑZq,lQȖ"V9Dէ?X,\ޟLQ@) `_&?\_9-jL0 VT-R5Dt{mC=6l+vDp;u6Y#rR{pJktF~|W)llFS&F9???>>&ᄏ8glQ"pxu/`[cǛJ|m|p˱.<>>,wOpco&q2f;hLme9Md$KeZ; ,i>3kT1j3"j&]Ç?ScgkzwqaЯG|soonw-qHHN8l69̑SƊ*0jԿY_yvw& &lx$}A1K>rW$-AHf{, d{cȊn 30: @B:4 D&d;f-BS|,ta|U jgsT @N~o`1u3~5Ha'U(Ўڈ"LH - (;u1mE.KMO/D!(rjqZ,>Ndž<3hZIi {f者yF^~ު8VU}0^cP)6\T)n3ajH-Pe ܾ KWl5<Ht/S[&҈ J%Nd*9$H@ 3Huhެڌͧw9˪`%0ƢHx_l2IiTn i$nF6Z0k{\?xsufS|gٮk%edӭubQt$!~i x2k$ĩEv~ػx[@h(c r 4'@(ߖj}-ٶRThI`X__K^훛2/qQQNt;2.'g8X "Hu4M|PXqCY=au!Ɂ6o\.+]?9ܦF&r$oVl\X{+dXjkKkLhUˏyܼ>mb&(N=#t7ʢ3sMJ WہoKA=ڔ$/q`r0UJ sM' riSG,fͬ->0۲XL)< GMj9>=w:::ЛN Eϐ9 o_NeJާ;ajְ0an>žo;&DP4Gy6&z@NXMjRb}BU4N_R)#1L ;.ZI%WKBx"|'Cr3fW8 GJdW\.#S*bUghFĹzdZ7no?uip}NP\ݼᄏ]<>>M/׏9Ը`;猝̫Oʷ鼳s\DX-=Z&?Vuc69 Z2G oM0@mQMƐ3 >j |i4#P$$Ŷj듵 u Y;UӗZH+ T[0x)L19$dSf Z|1xedK<W$†Z]1s1 5< KX5 A *[ҳ WJsH)TpUlV< cN,"A`JXIz"C1)q5iyaXh(R M ܫߘ -5Xl> kxV/ qhV_m(E2d`TU?ݼ&klV0; ؅<4FUHݰmLK#ЩǶj9b%  1W`kUio7V:Qw%u(͚Pt-<%0(O- f9CW~”)OJ&!'U^a#t-wGgwHm00KY'Dict bX|Zy쯒qk1p\1^Xkl R&Ƕ4[brPu< $4F_.;^7i[/Os=D㣣j5 I]]ŎQpn wa].NGo.~"D@\vUcLgG*:R*v}#$iN @(E){K&XgK5 \jy0i+q^\FIМ,ADOl+"k>__O?X 믾?v8MiYX͗uB cU4{w*X6~U??A<'թk] G}NϸViŸ^R&4C<'ynfCxy0fKU}`"`y Va-YR..I)ׅBUovrLD:j_("|wvyʮH^ 柿ͼuWU!k}S6M̗V$bRmzrMjx7;>|RoQ7NpҼ\ctZ 0l^j|kX5S\ZNH0-<{)5b:4#N;0&Y{{3BwÞbiAnv0M\eR6dl"Ojx&ܵ#S%=KBJDejnM 5+l> h3ْ_R #F[j1$Ndj/R @6IN$RHQm8ls[5 ]`$ 1iy&|??<=?:8AXwSSf\z@dJp<#!KP=#׎h(kKx8I]MO'8lH' RnKPYm*Z^ wMlv}uu{}-׏m9wgGL1Ncir;y@b WG/xFw (#ZE/ϏNo%0 FLݶ嘼uU&zc<9\E6S錈pޛ bĝQdH13}nl|&1fÍMQF{fyt_aAҿN=f &} aRĬT@hp;W IqQB_eL_SuAka]:h$iaW:??wƠd ;L2a2_~R(hzQ G 08iAY'~PÑVb.6F8mVQ"H 3$!u3 ᓫ>gKʿ4QҚɪk]ǧp +퇻ё𔓧L2lғNMV mDۛrv駟M˷|Z=ܯ1v?|zR۰GÇ{KK}3gon(G3M:>>M'b7:XQ?+8͎g 0B "Sd^7@ AYdUYSc<'641a~5Vj Pl\1akLDQ˫a@F eQ !z~-יÿľxߖ'&79)YxBٓҺ@bH aUb6fA40]ͰZVV<.ݤS.]7a99!gi*b_EPU&#S i0\2,`M'V#0vt] uM~I0"U94 xTYk)} Hcod1\ܦi:AZSxrȟpknS^'>1Ť5Jʻ7AnUϝ?.[^sqYcci3(%8ڧkzLj1RWMWdX)7fttN_mB~>`0D—a_ħ[Go/ԃ,9jٙNgCڎI8t+RVӳsGZlֽDӞtdW"A.7\.E>ݐy!EnljZÀu\/W'Ss9~ ġ.,&JًBʚn°>l6o& mXj憒b+Bb;ٿ?TUkD dC/W:A!G9xD6:;brau,@/ zpq1Ð_71ؙ@O+~q\;0yqFNf׺ ~4Ahj%ٽg8ENJ.ґi9x;LY Ɨq;<8<>:P{,EQ<ī35GK/!˛=O O3Z޽qN79 G04J,H[ Vny'n4˲Iߤ2e V7=1T7Mk3%N:u斵Ewem=2b"VrjB4LO5H%Eǿn{+oVAVX)[* Pm a JӦ%PF~~4WФrI#UvHmޡP<0#uC v=)U>p7g*&up0,\-R5LjƼǂM`4bL[~w1j}}u EVc_:;FWW*B{GOO=e>;W?f|2kekC5K]Oj]ښipkKE {sOeYPUwII̲_05i$O,I 7Ԛ:]n^P"(RRiZ Y%@QbLAFz%q0MJ5:S EɓkdWT9m[tc=J ]1 K_z&5 $EM,\7<ًa{,;Mvxj}-)kIdPcsErc=? #AqҶr1 *l͍gVє&MzmD)5,`:S7l[U1!CCe&SV>u6͗H( ->%rF)ZgsNٲ*@Zn`sHE)m#Ӏ9xB#iF.5|ăTRlF[F6 HPݍ/NL- MVt>[TXE5I]7Ӥ(o䊭]D#5UL3?OZ.MegbrK352fag%y Fu(ˮ1_ұ`ɯ{bR⚾@1SzuIVΛD㳿{pXБIxlM'd*<6qXyWwr(GzPXHe @ PezbhbSCq1{v.bŗ]/fV+E5ýe*YbKry%am1[_AI(yGV>gabrVSyIx!F=8\}$gv]9t.YIxAE<XqBby^D=U)'McÏvd/P6|f#A,Z1m/չTg6%2Ó]Gl.e̘Wh%eç?iOOe"yEtA}SGҋ.`I07oƏB6McISv@"`a_^vwOQ`fƚխUyb)4VNUGICsPbKp&Qu)ݑP/{d3m:ν|*r)58똵0Msrݏ>}5._jgc6bפYwxkpx 3D]^3Ńe)ԲxYv ƢS5pTݜ=mYHjmB@b>+{9LrR/]R ^_l5* ;O'|:KKwlL%PV0sh_:o,0:<0yBڲRz0 +{}^_0x|Wo/<㰚nomj;Glh6Vi0ݭr2jr%W)J`L Ƭ8wTPd^n =jY;e=~-q58CTc&AOD\H_vmdɷPqllz7_({0EW&GI y&Ӻ勉1lZc@Ėw[=6+ =Zìr0G-8N{~~wȳUg]&R&znI #NYIQbߐ4 a%G<&ŶXW)߸ 1Ϊ8:N4 Փw'l%~k|\h9W“lzl`̥Yuږd>Cm;v:sYYө~Zܼ{bC}==gv\]]~lZ.c]\68|ZXpxu؇ʽ͎'g&8Wv#)7ӍFq nUk}-P{P̂!dV "b9"RKc/'r [#AƦRN李BmR4SHIZ9O](;U y {Q]05[ ݃:*2l6.eR8#:’{HH1RslwM6Tբ7h(N%i&nX[^#o*$Pa$Ǎj·Cxf`$Ѯ 4* kqPl'zb e #GVެڮf\u8+ElGVt`ec>^M^o}wÜ7"p~2'U^$E1V~j\8꒻IG0_\'XQ^^oooK__&.\2d`$Zc[̒͞zD6Wr3O3#)H`|$ǡ:Ɠ{5xH@uv朙~ՄL kLݔ2u ,ү;ӳR]f1C9vz1%&祽[Lđ|:I8Qd42ZWk&-&N4Z=z.jZ*͍y7>1|Q\)j.g A7Y_3ܷBrZ3&t2M>=|VUӧOonnvw6m&:8; 8P-KEԖVف?jݎ( q_]Se5Ea^laêR>:#d솀jö A#rV9&+J[F\lD%X̔>ޜS'iE }ExW:"Jj&;}u+Ը|)%0bE|ۿOHjr] ZڐOeSj=ph,q„ djU*"*S:U=2õ:]BL#tn{?QU1r'~;pVXUϘi/V.AG1Aa 6%jC;u8vlԽ|u#I,!g*1"|E:5NdHl/2N$e, d1AWLy^5Hخgv* ;99jt ”Q&'ZM73ku^!Y2duH>::O*2 n{r$>6 |%p&H\B\`CAB0zY3 k ?Nꇕnnn2Tv&)5#kM~Qpt!@urrˆèB,- VWG5pG`tzt$8N(fBƞGGL:h4 UϦhݛO][@5F LӒ74MVa&oZfRݚD@VReum lXZ1ɱ2pݩ"ረLXDggXcc4& ;[\wVzWg]'5gS2Ӫ(!_6mk:PŌ&wdrͻ9-3iFm zkc3y7d&`A*ǽ"3zoEi[N(!rܿjkPgyJ-߶RU+%ʝC 4AF U WEH)wJ\H ˁ>LB^bOHIzdKҠ휪BIT L2l$] (1BW&k bU ݔچMM:CTCN.L44OmI+5ØI]nJ5U˱UU)l!Yt1$Qzi7g@LЧ6WzI à87φ14ݿ}K|enR5MVӂDMJSfj3moY `ZlM xbeuw3"uK$Z}r>2dyo~`W/#+L'OFsĊ%x5fqu0W"LIo?^U}sfәAyR%5ےY$Xfil2Ӵz1lc qת6\=JCWk'.tr;.HxeT@J[=oL{sukw_;aTJT:U2{{X}B 0l\W׆ ./?-w77yү^_m~I9aM6oFhW|zGK)eEMfmHW/?~ @C,& F|-FGЃ ziM}-Hs{&Q%q.bkR JPa%V6':*s9 -V֝]prv~zz.#C= yo}6-LM]<ӭf`J5+&7a"AV_uhg;6.4!8% &%;/n)<N"H\č4@/#0bn y >buJ !b\N_rM䪾+$זװTl4^3''@Ұ'6 [xz pwYr\-K>))F-['5FUϹ0ln,7p 9kEO>>2Ȩ]LYziϺ0Ĭ/ML>Y-|yܴHv鯡-ru3w7wP41X?# DC#3"793p, :"WA0{{_}SGÉV>jfy\uX% y4G{^.c%)B2Da)(c.v.edki +HUUӦ9 yI+eݡ"[QUC v.5j#CIu]PZ;3`wR/c#^_GfU#m1ݵ;Ú-R&iJކЛٜ "dØ4 ^ PWABW;&kS6LZ.M ֛ŊJX;(<RzC(6 rBv}0&OQZwEhf.S0LexfjxᏈä8;HlwTPtP,6!iI"6}.ǀUYu"Ȥ"M 2Ҷٜ4*[$$Cf7Y hr*k *Ww!5i{# )򏵊M<ǶH)BmfA0",^Ohڌ42s79+f ѽ"@Fyvkh0ɛH[ӓ^ܱϐB$:Jل=z g:?;=P/6m91q#V;;vQz5M2:ƿ aЁaVYBWg']t/>0sz\ n`ǢNIJ̏譞"ƒpjxwzz&֡eEq?V"xF(KrcNE-y ep̓9DeI%^TOkhEjR5(vחq#BAЮvrr+:_B<`a1sRԁ?o_L7 j_u #X !dvzSȚn{} e$45?f!͝<'{&lC&݋"e5dy"v=^"*dفR۱3Yݝ~a b4_}}} q՝J->`5CH/ @Lu-hMܒ[vbZY3X C|>gRWܴ+aJ=M9Ub/Ysdw)a ZSĭF&lnY&w(ٟ˿Ż y6vX~w|x̕뷭ӫ ]Pڑ"%o]ejY\++emֱ9HCOڬ}kzK[˰7N|b9 mS,X, ̒Q6@ȵ-@ی#F! dB͋<7Y$kYbyLgiky4n4S&UN{;MFzXo7sΑ"w9|ɒZ꟎bM)B[cO:،,MQjb9js0V1fRx)Q8jh# Qh#[7if2MXqR[w j"m_v[%VRC h b\K-V U鮒/9a(U5ܹƏQ 0I(Gn,)C/`6)J!@8!AZI#kJ [U*pX2ϰtjwwH[ڰKK ۣLYshѦwg-f!˛ߘ>)غ7(ҫxr-jN{ N`kSP]_9Oմz0ğ陚h^?; 5%*XG0iCd7q }ttirܝKYp^dkW=[8'ZI:>${Y^VŰ!???8f6foI͍]b: `[H[r lw6)5{"G+y\$ukgozt}}V t1"hU(3a$|Y?i"\zbSH6 T|5lϏ''G}.SS$.>ڼL՟`Nh\ 7b%l- 7ǽ9819H"?;;(ej4uxă귴5*FG%"-Yw||L?cO>~I+ׄwy5Jn1)2÷I"3B*Ԙ1qHG ܿk/&T(ro Z xx;$wGCupXhd">vʮW"U-sQ ßYJ#-;hV;n v=Dd Ӽkޑ1Or1s#>aYQvNm=xC4Õ,Io)ن0jKi:E<؛Ri"I] -}63~`R!P/W !S]*l&2fa+9uڽHH+I$NȲe ]\GJXbHx$U]ۺ[AUQ]%1~GRȊJB]ø"ũ$ΔVi[/*K:R{u2Ӑ/lmjzXՀ~ }V-UCAKHaU#%%/m*ͿkSUE=T, Đ UN8Ƒ &QDqUc"i^.jJܠ" HeM%ZHMr92VuLV5%vm!܋"v՘^ٸkiIgwwڊblk@JR@zَ92;rx*H~Ks!Zf5aHg)Cvښ"#Mne" *cRNqJ cKáDy4^VRة8][0!sA*̙Y,dE ڭ]a4Yl[qn 녍읖6'rпkȝP}'vϭXO߽⫃ýqO$MznSĖ ֦^}W9nKSjsa9>q"ǧS>Qa\Wvϲ%)Vd'\|=+)~X~OD١lr#܈;뫫K/S1q1'!$x!6#)u(L| mCBU[Gp k*Ty$tzɲP%]M0-꓃pV)W^UU)U/Y4HM _1nCV9L`XuUޛ:UBEUi8ĕs B]70E +˷Z4`-'`Jm7D#W۔ ?"pR5cUju#ʞ>0jos敆FA m\{˭5#3S?SJ㦁"0 㟝w98~a-E$^Y _~ ABI2 ]Me'Ä.m cg@><+~y;g6k߄7 ȍ+֛xyx,,72WWWKrТ\& 3 <AX&nT.A#2*i@"CsY֭םcZOE]y+kKVDo`.j9R7wLӳC~gS%I 'bil+/ӈAF3tg^z㐕VVQAII/wGkWk8fΏD 2\J ?o$]xQ]tb1OH}Y3]ͤGZ*7esW 8?O?9r||lGAC뷻H4/o9lmXVYa+8FXY-3oJWJ3yzS07\{KpԎF,]j?;葢/">MP Rb~X܁ =;W7W|?ݻaE|kC,_\ԗ۶ĥSNTc.}y k?ͺQyJkZwϿdF82`GSXRT{5O5`(wpl*7?{v- rm:s1yR'sg}ؐu f9 as\&86 3e,]I'8!qhmG]__;Nq@Y4hA"\jۅNNNЅB=YNbgVMV*G+42Yj 6ԑCY~~N/CVd;ƌ;r\LG"rIlM8 }kJhNb{?gUq[ 2*jm(c~l4KN)mQ/׿(9]\'\Z.޽q:g؇=;`\n\XRhf1Vb$'&XOA8z77|֪M{o~󛳳8?^ؓ0@m̞mB/QU*ǹ\Kn0V1W At[OmѨkwQrhL+eE;,ɍU?d*vDSuYh?dr8It]HYm-W{_vbBִlQJUIi4XA&#j(VQUkBπK'e0*M>xjA88o ăCլ̕K 2DPŭZueR86yH0'QӤRG ␷< A yq` D=)zcp#n\BIDj<@MϚ8jal98kjh1Tj˨)W#Υ۵D3̣ Yְ4TFwIOAsUA)(CKQ޺!2BO3̓}s|~dRd_{|犨d>eM6nN 'ZwUq Huy2푪 Td xt|2ScיBCMB:>5Y?N^G27)eXo;N굢QϿ'A&f+e 52$Ek{g99>֓Ss^jyWHXֶzw20'GrFe 'zѱV\s9Jbr|2Gf_޿75ۻlԟ>}"%`#*I?OhGs1q^-هracf|fe''5taj\ osT0S99x Br|ݕPeu8L ^Jz+7QG> [UAe97xÃBbXk*5ADUc?c^8X"Nf3ќFA:Yw>QIޡv|{nUψ'1 仗))gKJN,m+H_LdœmB|{{wC_~r4GL$4:>Mz~/R sPýzhTzҀKȌE˞?kdMEP 2Ԗgepsڿ τ0^Ulbmu 5"oc眹2e yLb Pm537%DNT %O/4C11{tFǁh& @yKN!U N }Y͗.ʉ*JIjOEPE=z͛vZ; {bHT,Y1kiFXysvzQ*SIoy"c Y|ֵeq~V9BGxxruRk *֝Fy5:Kk;k[JUiMh¤U AYYRJM4te87"TvӆIJMӲ*̵*sOVYW,:z'y6],|5J,'u6FE ;12ZQiTQPs 0CE}ј7UJC=XLuGY.:2FEQ gJ&v 6kK6T;oD%g՟Z#Q" ν+R(Bswz)b$l)×pyr86[2PMlbUӓL~C>D٧u; yzMcd97f/&SH1Ze`kؼ) |#+2"`_3:=9;9=?CB(Μ1Ƶ>yT ~uvSUl.f{mPj{Ua]g*{–CξS4WfZqn^aj t̞NQAݻ"0V~%Hy ?~IR f:?߽/SO_]__/8䤑aE 38R:Y>X,I¤[B6~ETrqΣ օ2RU* C)bLÉ,uu̎Yךs+VNj?sA1PчLx9R #yaT42_orL;$ 1)Ԯ>&˙9Xڊ%fA,&E= &ZB^4ʭjȠs,&4V뼙Wt(82~J iÖպ]ɜ8ԼP>Xh蠒ёã|Z#Pa͸ R"a.(T<_TGRDHL|FB[pʵwo:e{}5~W#I~;t|$}-7U*̘ۮJj; J7.sƋ2ͱ30ul{LslWUT0-W"S^z٤#ݦ2 ^{c ďk: M3)PY#M%lHZ;ÎzkNd $lѽ!jN#iϟ-$rO)s.i"HhؙZSx<߮X.W<)5]we9FGZlZ1!PgIɛ)P<2 OO/rs }`108'hsdaVዋ u.ӡǑ*[i{x[zicVRU`f|`Q_2jwv$68<۹Ôe"dz Y XCj+`zsZ h]~T9\7b&Ҍ0ENnoiͪINPݸ,n=" MZUq$tR$knh|wvit4?ww}vOqoHα}>׶rS͍<9ؿmFX|LjѰ93eԺ?#ӣ8`~3,jR=@b@fJAF@$GZaXͿFL]y4I2fo$w%}1p̪*L8_kZtp:ƗGj:Ob6x5ϡQo7ז1ШjW#d H#9=6BM[V-7L '˾+Vb+S.Cmu-kL !0p"bctQWǥFd,Y\V2׵R1T\E,iS$|adz1yUd=Q'r:/ 3IuaQN-dLÚ<ē2R`E 9l6"6:B5&rh&r5+vA>&a%u2S YM*r&sR4 T&~YwAHU)Zm\8CyM=J`<%$Om.H4.\-V:<ȃ;bgd׸u+Ls㍥DΈy7d[~,<[/i'ꂅqh\|Pо=vUc]Ϗș\pno֏qzl*%nW h^o ;ig@NA5|=Ɂ9aC-9Ճ]89?~ FB4 xs80;2pep"Y2#xLd`Ix>w#[-}F+r~xp.U6At\94m1m.+ŔƖ2y"K0r`;.z?׷W±t$^baneUUC{fvrqvzƣٌ%-*8$ZlfbPq~d.C ?|x|8;=,2Zú`WԽYTvvvfXH^-Į o[Sa7(֋FӮw0;o~Ͽx2ۭ*f\z|#ܥpKJlQȠ(Jx!@LI^4lplNǪx/oRb kS(GFF35ޘ .0]4KbP.h1, ;h?wbJ`DtV'5x)UMj$I~Pã#R VM=74K;¶d\: "o+A;Ey[Rvȴ .4 5`P }Iwz~~,b+ˇ'C@lEc /-@ɓ٤VF~R"W5_ u?` 8ULsKR= + l&ٚo"a<904X^ZsW);%ܬuFʟ꣫P: PUѨWQ`|f<? *mVfZ &6:5lwv1&4Yd5!stI\f(G::w9!Mzo RYM{.ثDSS 8U"uC>_xS{;F;LFh6`(X2+HE!kY23z5>Hyb,ZCp90j▎(xTE,.o`b Vj ekwLZ403VkIUW_0~I$05'9:E."oݽOm]Զ#yν o%T% s4n4g#g'DEMW%aWˇ-"ڙjqwX֨MGS͏?p}nÉPEf|]?>pj{y$L 0!3^sn@4U+{DM-znYQ<-p#@bDf4Z %|5DW AT[sxp0/gp( HaN# ՗*yu] O?'Rt5L$p{{˕Ln lҹ. la+BJjB8eUqΡ-J[!+Īe3Of^H{@ 6_.C uZ/ ,yIy:t9g Y(XMCq4e,V^&c6J YZMOSq6< fPbrE\sMЃ;7}0Ny:h֐ < ,&ZJqɆ8eX.ȤSMU۰ o=@6?}JFdpvb҇nY‚Ǐ;?=Y?s#%^ooW&PΎO*œB)N{CʝXkoOe]Բ8 jpmoe*_rME8ںk#VbKΥ,lsrp8=-\~yߚsyCs+.)2~}RX{^ qŰa$Q4 I )a"'d+ۿRDT/jvQNGy*Hr{`I:@/ a϶(4oDU`:GZ:^,9773i x^5n7Qj}-.?2jÃD~"-Bη霽+{Ӝ ZwNO'ځ/|\'~$qO\||rzf1EHt&1[Q )+j}-`o_ 0CU7TPurrbOu~(VYގFhS K@-=(5Tvtz׿oK|@ފȃ' ӆ:>d93@t60@F77W4\0S~gú]g4tIٶ19o'&h$l7~f|I̽: 7TUpV;"Fg,ͭ:kFv6?"8X=88Oyz1hKn1򔝢aÐ|9?;׷J$&N'IJ*ǐ hF8w"_=:Z֓NNNp6Mnz 0>:NJR>{ŅJ/08H@QjaL~u2$fͼz@ #SM [U앴TDٝ*6Twpm$ Qⷖ܃-PŮƎ=."j@'V+=$%ґ3n*| lL'VΚe֗ցÇ_?/7 RRFgSL[m"9908 Y1LLSڢDwHegaJ7;p+sY;fT1Q6DmI$}j^KҨ*bgn+5 5ڰ Ԗ)>YR;02Tw/{$z}3⑧\sEhUo\U^C4Cv FP^Vb;(bEd)X7 r R>^~#ҮUī:Hd.~FlP} {jKbգ,9RzMS*Jˀrء/idK ͿsmR!nӌ@Ջ,v#~ci/&#bM4iAMM. a$ya tbN6PVJE8ihZPZj*c̒( Ð֗ hU MX3E5׿2TKjJge= }|~˯ph/&Pd e03 2nQ&VDF2ay gw{Wj>-4z͓<5Wn縒=;? x@""uOԏh:PPh[T QKy&ZЄ Mi'g  $rn6YbRZqt?3> #ֹ`l wc^>~Y2/r쫠6os9ԗ_z5DN uzuiCX60}ww:fi6]c3&Ly@|!cv 9?>Y \cR2b7Za)`O} s^oQY-5-A\;?kȩfYd݈GɓIݬ,$}y7lu}Rr",K|U^54|s%3*ˊ1yڷ׎./_G"g+8[?8.ސ=>>/nw9Mfr)k^8G;<<9={ޙ{t9ID"{p8!hkc24wzy}OˣCN>p~RZ_m]K$(,2>P ){&&  HͧôV|&EY,͡%[ȟ1*23\+d~kn)4=+5C)aO [AlpUuj7 +!ܮ䈑I.Լ-_dTJPeR[ؽtCB6[}GĒhf0ip8ޭIQ84ښ-@-;kVᬐU ]C-'5 bݍ;=*Ncuwc[m 9u͟@Zug?nQ$L\n`U 2R ÌŦi*ܢO惀T6bIq(&"تWC5.ID{GU550m0&CY}Iɛ\aIE'x34a9\L7eMPp;$ ]Gf.a\HE4!/Ԇ $ 9\M tE^3qO+i]ʿ̯ةF5ĩ,|e@siS?ڟᑓ$1`ZC>?|tJ0@XG199=X,>:pPb yd:CRV[Ĉv X`i3kR 9af1 [ֽ896JlR¼IxtrdӃDTz<<8$ 94_==7'ʙr49۶' VFGm3,+\EiF9tGD.Im?BOc-8?.VZ&6l"bA~Iíy^G~O?izշ'N8o= 4Ɍ/OiF}}y[8@sPC#=<{;`wɂ˙_#QQwτg?xe/%:y,Tӈ✆WyY `f5pd"SZ:ڊkZC}ԇ!q67LLGLl2r_:":䥈\c rU~D2Kg wu#($ꫯr5Ԯ߱U.c*Kp i-/֖c r9*]+AmX0$FF@hmٻf2 ò :f yst~{{2'DZY@E~bJ*V3h5"@6Eif 53{yۓ.$9E:l٠rmMXxUX-7ph@oQ],t0YpE5VLdE-Tͱ gv/eBZ 2tEx7ƥi PyOPE#ZC ] Yj>Zg8u?L4Wא1E\S jt5!(3y )݄UISr6 ՂRp`4wyj\ qJsc~>EZ'TWƂ$7gs0_\OK*0+ ?f܁Eж˵2$pgRYrVS7E}NEּ*wYb mEs &L#O'dzt|u9ǩ"G)ҽSHWYա&i?!<88%H;lT_=>u*2Z'H~댱9=_spoT6lb;?>DαOc6aj/Qj˶!7 Y;PɎO/N"ZMbH?9Nݶ%o&2&(إf.|,Kl`s;E=5kj8ٶ=~o%fL_~˫T9x1[yi1-bIMg@H>7IY1 vR=h=W];oP''!"#P^Na29&/r*f߲h ]]9QSTV"?Q'ځ$ebF`;YN>6l̔}T9g(n.?SULsy|/Xv%yZ^ߺtprN-+`:Z3w}}'ʽpTekxa Yl3]Ad,!y/LWX7 %w)qzH NAMDY5GRa[jk'n&i@!Q!lշw%Ss (o:1](?GzX.W<:D~"0k3񺓣cwã`ղl1 h~L`2_gzt2M,fOl?QW*@͕ [W #E#l|3cR@0W}{"ffn,[p:>iVK=LXԀ9uZdeMZQA u"+AJݪm>A6Ys{tge7nqc+aHOrȭ@jGs<pg!e_WJ'Zu IDCx LnM1pIJӆl35$ K^KcOq%hC` !sjQ䭯bd`lQzUu$L8%nǐdy/6Y AaTjP@u< +~#ZnNr:0k"+FZfnI+;G ۏ4ةۋ''g (u$\ӵ*ZrDd$ZU(0!L =CɈR xn/>ɒM eRvO g[KC,PMs-"?x!M+BHg1i,_! $i+gb|~r;{N~@F:||عnQQ/gȬ L'!YldJb9n%rbiIcDX ̉BP-4 Fܫn+/.V{E\fGbw?| Hvn:J&n :DG=䇹SnnzxÇ>|x7t3}8')'b_{IKSؚxqqF׭L5cۻ3tձBjf:%suggBYIJ 5x뷋xYr`x&D``lďNO;0 Ab'Pgc8K7@,W+-VV /...qsew{W`kE67f2K^Y˛?hXPԀ*0XwL-`C2Ff"Uw5Zak ["2:e6 ?9)fExiV\/-E,+}ٌ1& KWI+YDw,C $ZDȳpo~L3p{w?{V4j>rꞜv~ae&~|byYÃ1ee9n~~ݝ- %vQ#.ĄWR<e%1GT[㴖mjB)QUW*U $Qo6pX in=1u"YAuYXMْ\ >_UL2#t@]h^e4d04~KMp*.h.ɬ2c#!sr9DSLy)~ bs6q#͊c}APUsN?MER3V9k& 0$x]ƹ yaY8m>kj΄L\д`+5ꜩu1U-@H"(Tfc TDt!i6L/z9b EJ6n_40rGm* % B|Hk [US|a;ⴅ Q_IJfGT9Ge&$Je 2vpc]R*=A';Yq-+kZN7i,Gb yO. r.K(.)6e`s=*CDHV]"p1_`/gLW@W a/i2,&ܡ%kF+_VWe\@^rx̐$K*'zRvԮK:֮QsqN&Yv\nNV+\_= 4m|hYzo_>}sҩ2$>}1<0dz9Ͼ~yvYu885.MK `o_.%"&e={5j3NM%}V-TP0獹}#83[< _x=g"d¬y$H͗2<9HHn.o]7j3W76zz&",KC$-hrrdB,)t4IME? bV9Fv>l]PɉYoN.淞!ٶj HX+6{fBжD.QƊ10|,v$Fg2Z19e 9fN)LPg1cG IET!%^27)'vp1[2ty(:2'Δ24)_!G9W#]Xl3#h去y!B#NM^U__G-1\ǿyʁw`QԒm c)oE5Ҋ[W HwYXau^2biڤ NU|m#o(؈ժrL F;yP%_:ܪiGGHũ'Oؔ+wGrʰ:);4s1srQH||%g'xig0bvAǦ$#e!y2tS|Rtr~]QFS~s&g6*8?>:2_z}]>::c=‡~j4)}','?rݽ# <щ%)VLdBɡ߼}co۫#pT(/=ooS9?nn) jH],qšF*8J#bN`ȗsfGlvACAsE6a:"!uOIk:MfFLЖxK#b&LYnɄmb0˟s1BjiSkȏFKr KZ9SUsdUNPPAL/d8V=Gu`紞H]!e^_v(HPͻ*}Q4|y#Ub!6cbgR/20M_ANy~L?de0$EhJnV2C\I!/=<V9YWr霛5NG&^_OB9ZwWחgWyb⁰[^[ J~(.`?RmKW~PBa2ȟfC̺);A6#gßYQ˙zB '0Q{/֘Rks<"W @GeC]+d"RN8N0D7PM`-AK*  +VڰDPQK(2YDSLY@F׈bi-iKKeO? .iNXHX:k ? xDqܠ?QR9uW) bh2Dz ?H#gp|/?7oTUq7c??(dl onoeBwܩ߃|csL~i.2ږ%<󲓌poFX^\_91͓Z5NV!ӄʼ;AÇRk )ftpvEyÇ?ؕ]*(!'v),q'fIr#1)1muB[ 9t%5J@!I3Mf91hNnzJNY@kתHFY(ʺ PC lb@QK: &*Pt8X[F5o$.+!j^_]-&ڃ6_2Uf<2N_YM&ZZCػ249b혎@S QzQhn*ZڒA+>)1WQjhy%wJpՑa.b(U"Dzpۢɰj!|{rˤۻfD{po]<[)/ x~8SXü˪|/nk_~xti9ϣcKU^'e=Oۥ rDBym^hm@ԶaĢw\0ڄ' nOjt&OC<qƅ 7@e ?NFu05FP@T7Ъ(3:0mP~# eYmHv֜p( 8(w=c;9^?YB0ϣ19kz.I,u: L|%Hhzr_cYjWjT9lm?ʛc&cQIl z03Q72(;Ky\?ߎ*6iRu}s-ۤeh^w'/:gH8:t6<3$]~ho_EjrᦱqۑNqy_q ضvX! 0CSݽ ElߢkbE{.ƺT3%9/f(csg߷rԇ-?gܮ^?x ,p[WƵO?| K#\^];Y6$/ɷ}W&I9fZvXHAܾJNsU |.tq~~ws4dYxͻ3ɭoR \>";·ƍB>j-:U!Rս]|=?Ð6?ezs? 3_omPFV똰(qcLaht9V4V־H;tvC'NlJv?iF uAis1}\5<_L12lVy F1 aG^ot4/LOsJڎpUs[ȔZ!g. 4Z ?b$.p 4gBJߣnegFB5~nzV BFS$fzA`v=9qg{m_؋󋳛+ꓢ3g*2mgg_./ۯw7u~=nP;ؿ㔘5K@Yh2}U9ro.*gEQ&U<< ξ1}r;a v+dgH_:8Ԋ,yUz Ĭݫ9p޶1A/g/^3zװeZل&,dA`2܀_ȰP/bQ p[[QD/P+GTmyh[PYpos _NewYl;gNf#P9*|[N p';[\ x`O0BGdt&RM1quyy) =ɏlL|Sss~~O~A`Ysv !XE^#;/ (Nr82zÀLȡq|旿d^ԡ QT(s $#d|_H( }0g/GM`t*_G&>G0(IATpZ}vJ9[b`|EF|@hXe&hМx6}re!0osvOړ&K6D8ׯ_?~/VX 27DjJhn<.D30 - NAG!L޻;{ s4Iݺ2Pi䴋% t%6ΦxbFxt= bUq-#~>ߵdѳ|cVf_™c@ӣ%#p۷6Cu ;ěw=WW+]Y==AaZ1b\=P r'B\U-̅3g(Go.@+D UTU(RUYJ\ )^(y0oZ0k2ȸOgg2jDFZ?9zV7BxP4X( 48O3nJJD@0D2(EdSfN=5n')tMiG8?s{m5Q6 6ڃ|2Idvw\4h,x>q:XwiSWg@ųe]G#jWGj@gU5u:چVޱy5*7l]șX:HzwgoXQc9jfh牲|2C +E˄ɅaVc}K)XS_L}{Ɩ=/)cǀ?>^}~ikc/[6_j;?;U-5W{Vya}t2P緰yJѶ;sSn\BZM䝜JQ>}/pƊ.ϗKʁ?p{ٓD/}ll&G 8J\&:fyVw$T;M# 1J-3!mn.3Que+G'c3Y c# E1+svã7bI9 itWF/ 6j&j#DcWW#b26)ILH|ww5CD\Dޕ!ĞgJ)2g a^ލ),~26.Ù_Mm>m6Fl[0\]].@̸m2!]"-8ӤC߶eN:2:m̈6,2qV^?c[{tBy\Utc|1¿38ؒVrW,8rfh3h CϑN+Wl$)du:#N4x2Ց̷Α&["GpA*Nβ):B2[՛5g0u??𔯘:D٢VL0Ug_ʒ tQtp80uHg17.%V s)s9Q.,2\0;;)lUE-˟۝b{>da j<9sXňgKU+Y!4gz|&(sXUҭ%% S,}zl 2 WڮMkPQm'&29̜"YsVrd@RL|3bɹިoWl˔(pKa.&H`>Iz b޽{wttd)3-Q|DD@Tf2kbPqamQ,ܭ(Q`OUf6 Cn\6t!.oMS]!䴍jif]'閺џp- G'l2ѾOSv0F4eVc_}N%" ^wJ,DLډX`I:PrdyKlbw$Lt9!1f $gB.\.)t3dQcmWVG]%+p\@Qomgkzx*\I))F6kxL{{og_Kon_E^G(e|ə;;y'NHp;4O`XMp?e:[%{ٮoD/7Z7fɘ`].Eƽ96sh\:Iݽ ~xMKCNNP8DT}XOujHlOwOEi尰CZ&֞ ][E|\P#wwO"so:'SBd51{]{~'s~NWKy<`''o9=dNZ擄ai^k, \8ߦ+?wl;lmo}|k,Qk؞yD-͏?$Ҙ- -T:=918sW֥gN̄d|E+ Kuq{֞o^_ʾ-TN򊈭WP*Ig:d-#g,Yvy17Jg5]4Q>ezS@$ 2e [0D,2Og'X> %7.$<=be5F1 =3ݝ=U훷us~v72*L:?e)*YɇH=ɀaY]_9"><~;;bޒ:K`\Ȫ( ۤzJ S`Bo YN1!&wgVV9"O Dp`طws.gz'“T@,EI~z?uuAљ2;UgoGIG$ C~YBX݆(/aζ4MņܤR l[ItQ]Y !%"N=,~3t ņGm]Җgf]&eS;V@Jm&Ҫ@e*THh$r*,Yh#N6ɷ4Gnh}3Y̲oKKBB{4w IsE2.3iXޢ`@FcTUldCq{dZ`YgCH_l,2_I3 ڄʛwNOdU/|KkzɆoy|lom &f޼:c-SOfPR0'`wx/º6jFOVz",Y/v>lYe%7<O #oWWy8>n|6Jhh˷ƃmlrjcgM=:9lHd;9\ɼ>ʶNE#L"o\]>N+\Hǻӓp޸8o_>fGy6QrqNgu%(Fb({?wbCns7R'Vg8<w(iC I/X>J~#k|h G0ÉK&sv"JR|ED:J./Μ Xn,~q#d\4OOz4zV *khK&R4{vD |a̝R%u2umivCôc)V JLY7L e+3)fW/O|RsH'!m[#<&z7}ooqX-VBشsCLoB %nƗ.;"EFRftrWM@ܙ8hMg-O=>>q!~4qˇu Poc#vrhw4lt]oߜ<9YhT5fKy#s)LP>8WUکE2-4J窀X Y2 5$GdF0/yy[$1 Y%:n;Mt7t7ìDu0H.[qSTn[YJՄA:QBWSϘG41U!(I6d!VvRh_sJB1AXaRd.؈,ޔ[4$f0: Ŝ547+9dr\L=u!nO6bGs3Xs5^!i,1aH EKݪU#em+L4ͳ8$'\F+OT 7([h!Z==f,ғOt\[qh䏴 `H ҕ`DE!E@5)}C8$1ŗm忩UVrAeUR :4 -#HZ- 6 Hveg}~gdp Yٓ%gQ "Z|3lOygP]aY, |#`ƹ3?bڿ{xoϮbGKyBmJHoe]y#Lt(Ü9\WC2EGg]DrkfDD/\` T~?o I>'kҵd./z{g|?37wqks+Ͽ~9@ ''NNGjOh1Kt[t$/ᤤVċ'N;ޞmmpWWyZ9(tL@h8xcysRnmggw~sM9CraG<8QD\TkE' Nʀ#щ&=HJ~ȸLʋ `PMbr^NC yFj@eJXC& iajLLKL02 vt,`Vfݜ&77[Ж2߾Lzqo#>|Tߴh1䓓c>>1qv!\g%q".D6խ Et/ rh'I M)DU$rp,ti a1De~Xswwߊ? X,YFPT*,sʤ,5XhYlFmŽ\'!̏L1V$f3?v4zW6X5Cl^&އ?<C[9/ƴz4D4 !(X,knZ.R@y0M,MiKJt*NN}Mb2۶%ۄLDh8t`L|}hwׯKM4~DeO|Hq)^6\BK-*3Bjks7H9ͲU)I6Õ$&GwسhKNʷ6f>֥#ӷ' 9<78+ g llPck{g}kY!Kq"o}6 {/_#թ3eKL*MJCfފ~>/{ȷM7޽{6lB+8)oovO)ݽ;G7q( y>fFjG?|=B5ALCyDJjȫr櫼00)҄<98 4:mCk88xuY!S7ꇴ}z6k#޾}+L.//͛v G?NGڤMdM͠"g- g7o\\\hDV4i.+iQ[eBOM*iv6m̞3|t^}.$Gx|nGDf<ͧ<^OsM]'iZT=̝zJKDW^# GT< NG!bκyӷ[GkI-tX|XQQV鷗(;S1~qx) [2haXYS68b\rVDŚ2^K]OVÃ]c%)_00ޞl5 By}v~BFv/e"W@ dɦc+ ]2giJ8N 2;|*j+%{1}n$5K>J Ǭ,U&.!G]@ D;tBNיb,RHy*P @H_֖ EeZ-5a%za4ZdXejBIPeG@paCt2ģբ0 R1*SrjҪ%),E-%23K,'su@eޅK\G+gbB?ǡᷧŞW78]V0{D~pz 4nhcfjPK}P)h-}4M&\m1;`om޸˼&ԛrsQ͛R얼/qVuzŹsލ  3! trvj9ɰn.Ν2"aRUԛ>X7I \1lO#]wrr||tؤYJ;z4g {񽸸t!Dyo9Pљ?cmecsÇGG{ 9-\_^8a93<̻ݝKO7ay ä=KΈ G2gs#ͷ{9MiO W&xs_ud6C}t|GH_[f5=]bhhy!Rm^5p:5C|t",*1M{" 8Q1g%:`_.خ^k$AS *A(ѫϨB44mXi=3V*k0"9@`Y#?o!s6N`yaqIf2!3UXf>;rk{/l[x"pr>q2s ϯ*4!ytxpgë+#hY 9?ױD 3ny꘶49|( 䧊_sODG+ԠD-+/bh@<BZ!iwh j84cAgMCdʆ|L2"swIaG3(넡scCv,g-<Đ ~(~Pΰ۷hYv ~'w߉--Q?yƖjm9SX@g_?Y坣*;8@!pY" :$U~|/J \rrJWï\6,93[l(F kޣ^>vnXno?}lLdYNȚX?% R,+?_B/UJK _۾rk]Зe-8kT:A? pFբtI`x! q";mƧB,#Qd Iy#vH$ydy$PFU)]c,kGn3 ƛ䲶y0g)3oBVSFK `.*ݮ =-C[|š+{IhK P m`ԥZfYH>"gsM}C0 ٩igc`VLɘۯX㒴.%Ђw7O85NZtr?QM8pBZG2!31[['c|#o 5C|*wԤߜr9>*|Gϟ?}9/`Z2574WW9J*Y os8|o\;sO|> p26ȷ6?_|6ߵVlrar h.çQ!2;(KSRU|+C?^ؘʷy ˷G+1oeV, IA0wPAl%mۥ`)T4M<.3ֲf r.\Ij=8==>8:> 僻Op=eb =RvƱe:Lrs09}a5)dnM\c @`1UkHC:xRb{+\9Y 4q^˪%hJ/BrE(ۤ\zh,ZFpKU uDr€ ӈr3號#X֒h=!ZR%e1RqP.+:j,_an<_U ԉL% Se0j+'2'ZHaҨt~~~uywEg<5(蟟L(ץi2;Q^ӧ˫3Y#7Hqyy~ɑqKD7#jd?;> |Es\S##Yp3xZ<Ɠ#(%iog7fk z=R\Gi;sdQs1xٝ5OWdFuc+o sW6Xbe,s'oA\&V*In9yEr)Y%UAU: G.Uʬf H!c/..~/o߽*6V?w7ih?ɼΓz!:U.w/PbʁGi󀆬e@3 (U@qǶQ* ݝm!|95_/wEWĥwu|5J9 B/kj[%gv mX",ƅW NRA! q9{ B*J1P"܊%mHk l %_&a/nFjgg܍Mt6 EIGmer/ -oUx47KuiVe{,eMN3FD.Qr2҈Ey @4!-PC/2KhY|ebyu=S1R9%QY(+S,RRMiR%=~SO>|` zy杌T`aMsrr"e?LJ8#`8<"AH͍'r,u?|F :ԊI,рb |ʡQg ϗr&I󳻻oy]ʕ7Qb?_n$ĜYT3)]U[RPc}4w9:1~o߼aT<^Œ9F3 g/ϼoolm6񶚽߿ 〼#˧OikVmDkclhrqv~;6 ̂3j>ռewqqAU'f*sGIڮO眷/; O ǪLJ}p}s}%!tps\4~0ƞؑC x/NH4$ҭ*ՒnJ+2 ]j_ED K(@$KP̃YUF7l?ϸwK&2m I ĮL@İ¢MJ)N]VLkn8 9"!JfLH7ñ qv8OOsԿK9rtt_@N'MsrLvK%|w7gk1&Vݝ-8 4іF.eY%z^½ҹJfP7nqBhxQ8jcSZ6-xUh̦]b ײ`e5K7ک @4l_L֜zl1!~R!>A&-TևZ?αdNs 5Nۻo߽3n)O1YHbQ*6&.UUmBT[p*>%ڤ Ee(C֒^60rdEҊ6 h|p O?Vx*DL:0] Hqi'R.f#K06Jъ@̐vQϣT+aqٰu!2(|) 7\2+: RGN]jD/˭A@݈W7V2Tz/Jee 5A)T9EH0!Mv7( IC^($%%SJխ{opeqea]@4~McZb[At4%zK£,UybycɆ9 JeV9p|҄cKKWfWB4#pYK}ѐy=l UK:,Kd; ZRe r )J)g1΍G10zpR"UJJ:BLNmR+}μPD!;`7Ds̃P@G46|-v9+wv*?O1U _#6!=mQd?&&߼:ݹjR+[YѡEFUxM-'ݿ+;3١my7]|W;=H8EIL{{y;ӪDS&?۠349>=>;+kn5sՑzh6"uwwxY Inm43j,2BV=M]4`U k/[8O?>/o}:]=~s O19hKd O&'mu?hHG&ܬ=ƽ٣(;3k@w`rDn2s9mGS2l[t%ФCz 4J Ic!^ և%n#,יg` 94qI$+LgRO/ 'sͿ ~QUQ{ DfMNGV1O^K!ge"]Q2fO\*VO 1dᮑ_cp @J\mkQL|x;u9"?`PUJ@P P/IDAT[NqLr-WTg?zM7ϲ(|ecJ#6=VZC$>]"jw~[ {%(£.Y _ iUe)D2R/ukA ⴑ@VrudQd}Kn /g~ O?GOl3kQ4ȧ cI"g%t8kքmfU*k'L^YS|݇{>xc~FZgw"Yh~Rq~g1h(_w.Y'foco^!nzV?FiT+v8Ng0wW8J$m32r A4%f©I0( 驳ӷM FkN\ ׳o-Ά tɮ7oɿn+^_qMڍEio\j,&+΁'ן wk;hga|`6K@=`KM~pusJQ% 7(3PVTLVMZ)L|O[[v^U2sJ7%N!!qCE%>aTydډ05&`jA4VU;hxmjyl/> 5$;P{{w,#KEF'cg4o"]9L}Ucf~?{{?Y>$jWds{sg7?Xg;pR9i\J9d]^Cwޣp۷&0=Pxk9 fr}DbLQN"2:2ٞ2LYrHR\  <%7?ga-v͙ ~+)sz&u-rH!UG*@ |Кw:ȼ(fNeum}$+?0?~lyΦLFM6/jWm"yZPGq#6E 3B\VlX6AQhJ ׌-tHlLs "P;*d)6R_|U'1[G,DȏpЮ["h \) Z%W`,y4FJ7(0IHj '0SRмJ?0rHE0Fzéԍ K #kEЋC{m.^=𪰊Ycì>}.`PxKbk,Oq;eGm,.\iVfQ]) tD& `DuKK{Yݔ#o) e.O4*qV vR$(]vmĜϲߩΈP}.99XWŹeeNs$힟͹ʋ\Y?88?_kp'OMո@ˠM#~b=:@6鬌h)|$ĸ4,ѝ}]~Rŏ/$ y斤YVn>Ɯ~а-s!,YeƷgchw''a!^_X:.ʦIn0ɉŷSMf~}su~~Đ&x$`>|D?<\\A!N<:0k8f?[9NUȜ"j^Ã#'Oͩ~>3~#mb,OY4ڵ]j AfݘSڤe B2{z hն1&!4Z8қcd4WkaoqSJ8DAYDd;X붷'fV?]]]%{3~% 8=}/9'.6f66./QxK>|ɇU&pS5e^=6: f͢=_\^ XH͹wooK:?}ẈO \R:GTe`j %)+TjFmm1l"dhe>53LVe̶Dd!ܵ:?8mraZ`9)yvlcMlI~xzrJI׿6|棆DD.TI.q#8T'WUl|U W(gm*:(!I'j2]<{<"%5`uoRkU8KM? VF%5/!6O!v ylW;ۇG;G'o޼սmUg̻b޺hie-I)(9K}\VʘFWM`s]6m/9ϟ@c 3G ʹ҆-? mlC2ǀ*9)CQnjg$Fw:yw)- *gY&D?j+7IАϙrc1+rZJ7]0'-gI?Ç_>EH^ p~n,>%~~@~97\k-8}}qqaވLU'&餫+lj>sPzo1dr ߊC Us~ CLaKJHH>'d c'bi]> d>~Lp^>9}ǟD§80ǁ7oP&}vvBTH, c[N+Y X JІ*e{ )[)dx0((F+DW ]B, Շ⤓EsQ- lcsd8!@ ITVEF۬+*AՈOUxj)Jز,,AUè6%s%"c.ФC cB U\]^]!|ȿ/̘͋9_b >$ B>D_uic̍#h Ī~=gFEFQ#2@ZyЀ>'EQa/Wƀa? Ҿͱ%'wrhbH)qb b>e}B==Xp ЋmȪG& -"y`rPOGjz^6 ʆqp G^"0}p_ WUi޲1f^gi9>#V)O&Qf_5@-8RF_gy 6/vA0,h2[P 6^N2m8k[b(We^O^A ,[;̩e@]"@S Uhpi`>:- $r3$_C;x¼Pj&ؔxJnzZ,%4Ta1Sv]9e(,uAK*-uYk~\&{lCr .^@/]Khk^9`YQS jK( :..%[F&6{#3B\t"k[lxŕ )x+eӷgaUj*fu7,7YU[Yk$!Mn)gS,U- nȷ\lywݼofx9eofPK6NFg?{ww~Sӕ%nC !bks0?e 4vlHmFa,z?"wK1f2lO,[s8=ӭtww7eE9<<$2P=?&Y|b# \qnxo?t~qv׷fr Hz[7jLrJiA L2ԇ(v~Ui(cm#q[06w${ÿ|u%̀M0dъ-[t*lNkF8iđ2"8 _|h8wk r搁('k?9 LvZM2R8;ы( KE|("@NiC^}B2s f2q16T)G+&UHXwɇA[Vp(M|49oN`0M=SՊN%Hu.rА;1kX5fL(bG(B_h fI9K,DH6א9=ysxyyu}sU;*PKYLGA O Q}h;|1ӧOjgM6+WӲ挍]U^e3>@6Boe YWW,ߞ,GVbu|WPf"\X[˚otZ<36Y-=No`!K0oҞTQPX| >)9er&7Z$xƪxxB@fL3![="?@efR8&-&K.^{µ歓pZzYw?_8?<;_>s]}%[&XG|?k2]%J)NH~2,a, Jlj !_2`dtXvVؔtA={v>bsYRZۈ*2!S–.AciنK!.*R|IT-JR~ R2AF+?.RzP@K%i(jq,Dx#tNmꢶ2lv%eR|% \—M/"PMF@S8 JRɭ#L PȃwU{UXd~pep*@[-5O]w)+1ԯT@B˹Kdm*b/.&w|cRTsտ_6ÊFž/O?+OlsuG1='Wqpxd?<haoe2 X&xHկ_^_]*cFV&*";A^'^C&Ntn{c^7O\\Y~-zG拪CGO6 I sFtlɄ(WG ?بmCz@! G_ 5v{u'> = P csT?zm?Iέ2ljy%V(L(zѱ3m}'Il2a`m$%rHDxF!iH1b5Ldc_^d&N&PUi(1[2ԑ8MXqHJI -W==Da2 :HV.~8:=ܻ32Hg.X:9>YƺEitƃwqST% ҚtG% Qq>6 log__^4gPhx稳HtwHj)e:}NeDghoi" Mr>1I9?=sfG)g&r_LΆ!弔Hļj¨g4b"`K\d1Jfփ쪪]~ztr'osonmuk?*޼}_~tw7c}#}W<*}>fh@X$M N[ՐӆkcJψh *«c2%Hp Qv]%?'l-Ĩc3wk;BΟ3C̲ t5 6sprj?@Mݮp/Z-h9F Qƺ\ZV-]ju.୥yEI fy8?V|4k i4Fҵu*Ak̽ū96ŵ Ӿj3unߠ\,-ΨE,,/)U)(K2n۶T*DUVlET-eK\A)<9 ~ģU%WħG.W⚃p{SU .ʬ>6+ 9T]e cGLRK!.ӯŠl$`e#\t0\ܑ iFQPKRA|,℠UDْQhrb!_͟Tae~o? eڐ̬ɘwN޼=(KC^-ZYߤVڊB`7=]x oٓIg5.!Vsj=Hr%><Ň`v~RD$d@qgd$璭|%_iL lx?%`P~rN\#7'`mp puu!3 m0cnJ 䠙S_?@~d2Ի}b[)Yf%pvEHz[v*ĩi,&F ?6 ʇ<15KyBve6MDWBc wv4:I,J4\y/ދ3KD"7 T5}$F$9S QN%taAUwMzwI 8;=pUM|QWB3#doo٠Ťw5gU|P 80spY_RSEB&c|8+N|V'E:H\}$CD%u|t$,ñm! fgQtGau!KҧӅ9 6{8!1RI2+ zc#D BwfI_Y@t`!SmOUs&{hgҮ\s2n|w?Xift3VOy,@Ch''L-ŧ@/u@C(_?VtPWKlh$-F(3l=bť*x(U圁^㴨 Kq8 bxcK/ 1ˆ@Nh;ܣ^{wIҗ0b i*PCP4YT ȉYHW؆. (JBz{oXJ/r&P~̠bQ]Zۙ (guCoraDdž@qlE(Hʠ,Rz1Qdlm+#G+Qb(^0-肊.C#9kxP 3e6kThKZ-j}zI7":2Y˨{\'5v(u) 3/CONwv]Yͻǧ绛{Z:rNGcQ/<"1dBz4 wwvV؀<)/I&; ~#~װל-1AoH)is+7uʖ @C~qוЭYf=ßbxlG0672?QtwG!ϑܲmԻ)i|gii;::;X[dZ;QȮmo5~|}y~GbZkG죴!Wgg|Co=;|wm7TWR_ģmyXp3?$D4輺x`?;<6]WjƐ<ݷ~~qoM18ysG5Nɥwh_bLXO`EG!(Q!C LTmb:5噼! ?:zFl\ClNNN55`i!>͸; .qG~ Ǐ&^*)Ö,B8Y4wwe`ݻwRqDC )|QjaW%G TZ,]Ѭ ʊ8"i9YжvgE?{Z7exk_s/<2G 1hLUδIbQtM g|1d3.&8|]__1y/rHicTKoi^RMՐld"C!Ñ؎!O1W_]]]-rR V tn0Mަzq_N,kOE8rA:yWyOtQ[Bø,|[IjK9m]QQ0V!]Cd }_YݿdK@_߬?}/ɋsڸz, 3ZRm^7ml[u&@i=hʥ*ƴE.UZTץj\ v=Ox/C۲ TKpÕqi9S[c-؄M<وUɋ2fyaۙy!E~7*$:s(iIBCcA <-K'o2G16 Kxھ*VEԔx[C!꧷TehO ahmH@aG*d&u@OQ EJPp%͇2g{KN@h@\". W2`FTU'~t< ewe[Nte[x 6>j)H.,XRpׂ *! 2+ѕ+c,_"h-P9@["ZVUWӪ^e\FP%X$ u[P\ T7jZ? M6\<,2V.fx&i;f]1wg/t23$,V^ j rc]]^^t[<>Z^xwNO߯X߶v~swwOTvON2`kbVon8$O)W[A7sZ%z9IuR<h-I͙(C+YqF )G1{'.#z'E3kyC c&ȶ6\oדqsr>s ,C|qs?ʷUu' = ]ސG1Ä|iˍ׿rm}0G..Ϯ [uζÄ u tz(^Iooikyo?qTWCo9n $/ܙWe^2lFm8ir .C#I 0<͍k©6d.nw-~% D.uVt!o߾c4cm^,yUaBH{$vu%G ,6TS,?*;ڪ0&N~hSr80C21:8r`ywѢ>[8 W3<<>(LS>M>0P16c<\]*czu"?ӧaAD:gY9 à noN#1\[/ָ祈!,m7s6#vrrL2ǜ۷Q 'ձsj:pϴ2D=>;/b$({{Ga:~_?]ޝ#VnP?fc$6~~^u t:@e^/ ZؑKͧUӳ?-5> EH*io%M`.?KU=˼בO*Գ…̘ei_0c8As< %i?EUVܳ$M!|)WDER )[9r|j9Z=,)=si> >{JqaO[ȉ.|Zfıew# %PR!^7 Du`Wt$Ii&E_)(-ԍ&SF@ ,Ȩ s6o#a{(q25KY,76U3?vm9N$:dا[sD1>?if3S/]*9nX}ics:?'+c;MjaFGY#>vYG^U$<1O^`M*#X_i5NRjE@CQq." qP"_&pWo3Ϳ午 8:>ٝO ٭-N><$O;F~sN, [R59-H[|!M[2 t}A7Kn†-kN09&Ï;1teyM($NOHoo~LcAcE>}v|ژ馯?|x m kd`|2+w HuF6'klVyccz"J[]gb͜mJbC~nnc5/_2v ̡&bAsm5,?U,xJ=mB^7LFVo߽}}~5k{˲^Y9?Q Q3)C jMFF_,p?g!7%}H[C}~ncZ˷ qxm'ѷMcfv3(#HV@(b+O$p-CRP̮9)_@&i7} .|9=*HfijWUlΣ>$LV4٪@맣oÃ컌7%u9<}NP従[u ` oY$NV6,CX: 4 Jc͕BeAJwޠaw~+kf<OV/,T,7RBQYj˯?Y. U/tеA[!~K\bf޵Ν7Ζ;闿_~?_~ٗ6#)_~9?Bpt"Wmdڥ,dQ_7*u~ucbRӓ7o޾=}stx$i[>;D79Z.?447jq`x4xR %βbHHP/ poPgN/el3,m8?)|uSzL`6,xDb$F~ҋI;! Vua2AR'x VqyBoiMIL>n)sF ÜFP*R^|}xX{yh7o٫K< uɯ0;[/rƎ_]__u8lԀ(uyűo7X> Ǹ}. yƶ%ۻ<uy!xʇ`YC Jm O/hcG.JRD<>dž[h)[w]ץl-{RARrrj;vWWy2W8\5{DnVSDɖ6PDm;.Z ,79ـh>y^\ ...mhΪ/eu[WYtr3/~=/×.u99D?~8\(gO) |bɌ+A܉eLʇ3^yP#FWotagӸe($<07R:Ou49;?WK8 熬#zDR:"D&J/d_菈Σl"wvʥP8^♏6O߼;[NMo߾{WWxva+1oon^s7 f4$M\G UQL=(DFMн?|x꽽c}Fg_/ο}#1ce(%ʛ .q]Bt5Lr "G i-b. =(PRڲ!(K@Q49{a&ANf?%"NcڐJXzWt5Y {s$[1'M qR7v}x,"ӈJ@:dJ@(+rCA隐YX!(fێȺ-$ !UP^DPk?_5E(2yZHBtZJ/EE-q R0 Hd:aP,+95osQpҡ{:DYi ӅWJψB W#嫨*\z:HƗ4B~04Pe*a-zA/0ҵCaFBf<6. ۂ!#-MV9Պ;fxuzCY'Hu3Rj%WY¥L?rkz4!Jv;Bg<ٕYDtwG $Ax0h^ipZ]^mt"a.-=bgks;Jln|1r [I?a 1lm/ԥZ"czCRc%苋>03bh}0ktфt2Ii"fvhmu brHs'|Arj| e@+1m݇ɭ ȿ+aɗL9v0w(%yʼ\k*3@&LP%&"Є:O6I FxClQ'uY,tV6T4!Mz fo,虥?5#5vmV* ׵$#.'G7^#y[,1WX!Uwuo//-}u\xGkkUi@ r)mi =hո"%:ڪxggvqX^ddh#ZJaV~xWח 43Q_Y|iAɋVs+$p?ټ/R6]C]'+hdq.Vw[;N8]LK\=?eͥ1[G;{V;۔YՊRp# 2^)ԥJU0 ]U9!%,qP6EtKT 8sT%mB0/4BۯVャc) 3R׶r;$ xZ o/fYK ^cf 1ϦAQߴ48X m+Ap-O"0,\J>G&Ŕ'2 I=~Ȫ)T\gq 1xʲ%Ur,HJ@$lE[꫅ үMrgأstGg<4''5nzWh8"/"-HY2[7?л&;' .ye36|Ԯw5?PG<ieDel(Ok[1)>Ambd E00粋2,^k]@-Y)Je]] Hvw?Xn"52.D>>jY Gt5i~_F&ƕ JdAwqü9#s;Xgv,S8PU;U~)7,L{y?rrfo*<S۩C,ĺ2lT"-3= LKtfaz^ZdPm9[L%>5 R>`D@V՝mHErGTs=P xCfo:Ⱦ(/} 7Ow7u&PL`TiHhtx~'Ɂu 谹勳WmcXMr|$\Em{9>9>!y&Ɣ#ol Wy]mW.j)1LL5Yy-[Ι!-ޞ]E&r.cf NDށ!p 1gN 9::F!s*7PH e45]$S8͒{ L jDZ&ðgLФ@)Z2bw!gm::}|8Py5LJ៞޼y|qb8_M1]0yȎ@L bןm lC*6:4Q!T3\_wͬ r{[,BM sW[{;77}𲽕#zp2Yo̺+^6.RmbJUQ{$ QaxIN2eo޽޳+ _>}~oPg ̀Lo^3ߩ*%XY^巄SVw?Z>[$Y9~d0۷~`T+/ɹ燻׏w71+5ߟ9yCP@'=Pj1^u&x2öyGm ߳)uRme\Af]Jm`鴗-ͻHoM}mQDJ )/ ReL,Mx{n6ͥ,CUJXXs;s{ysNo\䙽8cTAD){N\Yމ.7s7tɞ5 "biᡬԡvS-7/RxUZ~{usp`wŽ2o/Oy͜_d8*Ng2ZV6̋29 $V_2%{ad^)q|&VE8sQ>3AMHN:ȏdµ\4R#IR|s:^aZYf1+J=?BD6]Uۉ7kORkن>BFrp0@(SɎ~ ^..Gٌ&hF?(g-^1!城/M0~.B ܶ0sB{4Dn^] 9DVP{/_*p!_^\4.4pr^j_]]_\\^8{ɐ]‘&߿ߍņ]ߖ֜VVU}1Lp&uD߈\p1/QrzrrB"e\z0H:Db4Dsc'pU8%y(L= Ѫ̲Ȝ-RZs:pb%Ƽ8ojp$cyԅQ?@nSwn@h =`ݞ(ciT%ɃUV Q&<$bW@Ht­wTj"!a%PՊqpx,(61 O+VI'ʵw`PnQjeOL1#;H5@˚&4MКá3t֙caF,.гt"혲i͍|Id~-'ӝDVx侃s˜?w-[^˿'OX- | U5'].A61M뫎vWNMy`]V]7ˏǿvN67zҗo;6P|~fԳXʞoY>꾻gכo:[1(k[yS.!2,"i_|De[q]OE VL̂ H&yrN&1:]nؿԴ`x}uu!@Jg,M㲡#!NpK%ZĄETZj!OM"v&&?]+[~wo|YKd7lΐRP$hAҟ(@/AԈ&G=lʗֻFܸ>_MvO8De7!( y le pv~!]Ȅ?IY BQ +?WiM6}"&lO/:6 rg_dU .Ҵ &bs6"*Kʊ޺svkXJXk-KU&I4ʤhzJMQzƎK'dJ1dt]LZ3v]}5v|txzxp0˿xWt "g'G~46|+ݝx}~ʋ>;}1gqG>y7g3D|6Ȭ`&eFĖ\ɋ4F9ɽ[m]G6"ѱ ]4j}ʨ'd,yW8.'EΌqM:c؆0ӏ2m'zqѫ1ZM<7n< 6?(,"}ϝJϝfm؝}"qU|J35 BljSgeߤ '}ѭܭp]4%ib>9!"ڍvdqT+hs>oᝉhi ׋q$57>u2+Ĭ[G`9]ggg k3LNM 7}L҄]8#f_I$1L77djw]^(} 9l^(/)<l0 ]ZaLUVm?*A' 0amY=y++rlg ƣ&|G?ooß짺>>=owWnII~.W7ǷNcEqy<1qX\vm'u2UAA5e#sK[ o!*$\  Q\k._^0a\?^=TGH SO?D$naNAj%]$~vN8&F e]%@ğQiUYLE4/*iؑe KuOsj@Vn@XWU3<[JO@ޚ4 Z-{JZ)QX$|kRI4KY]"JTC6-gBԷBpOZed{pAThSY5ہL*Ӻ맀8ПFbO&dÖ2z$ܸg#JazL%"Zi+߶jihOdCUuUX骾/PHyJ?Ĉ1?LM&&V>t_FMw]ER[rKna\5,hebs `Ь l}j{'gtF:yJv?Odrs(*gZ/$cK 5Y,#mXzѨ$K-UxgaTf!l~?>8ۑ6ff=xwXeqwmqpMO'YP__\Yʮ/qjm L+ eMSZF\{L|)o'2]mk^koV^~jyO6~3(}or35G'GyyC} mYkv-^Z;f}~qѓi2mzMBF:qŇ QkgdQ4͏<$me,L_sL |qm4gh"\w2t27o\__V>Jl9œ7O/}rrvJ&b/bQht]Ș\M@,30>KWboFSvF;je1ODE<$2p8f/f3!G孍9JC$qN;ܕϋ.|69qIJWnXRo|OGyd^A2_!\MLE@][Ujeos/^!'+PNX z^]Q+̳kݚ˪3O6j7FP9$>F9[rZgfV7uAx36Jc#MMI5AHvyƎ++Vurs`hŋg_(67?8&Sl.6gw~?;OW_-nq`է?:><<8=~= 'fxg~y7]\8 2}=Sn:|2&ny{q~9#\2kMඬV|Scd..JA!7W;T!=C@o:itW}."n*6ǒ:Mꂍ#y#z-u_ s 3'Qm:aj$!sV?cA B 3]-wl)2j:] )'&ZE$'9h- RsdEr~(ui}@骦@\UJob#eHk 1On֭x8Wϥ^ObjaN=a\h^g\ۻ F7ڶ 6g!ifR鲡3yd|,j}e dXL?h Kx(ajɳռ،i'@ |u r1ӳ¿Ө*AHJS~T׸j'B*I$ sM؁ Ql #yF\ (pՎ佽|&…N¡oP Vק'wFc;6/.˛{|͵ mg}پ\{ >J!w[U&$8lx!sj6钋*2h49<<26Uy^n<8::_GRڷNM^`8sԡ @.j$'bjVp5z*iZLQo:Q7El>Q+0n |Hpa䨩K}S,^݁+G1&rͳpJ̤5nk[vj)C>VnSYʸd,nQ!Z5'Ő ݝkY̫;m7?9NjձĠ 4FH&l6OvU.Dm6/0nWR&7s!i4_q$  ~Rr>թ Ewt{Y"v.Z1<Ҫ`܃ @v|&G)axKz JaQ|e&lǭO_b|4N٦AK0ҟD%H}r/J}3](>^]]:r749=:X mrb ^2mnSS i:u(>[F_]]BU8h}?O~|>!r??77$;1n7_5~x7onwt ۷oweyj(W^t()r|mZ ~6MW=WF?J .4CNqdrK y@.hJ4@ʊux3Wݟָ]˽kp2V}<[l~Z0VיL2umK7 'C:[[Т ^ _UܖT$Al 3TN6'} M*ӽ E`" Wދk?Q.)MCm_%DX6ҽ  CCJkMÕ%3;krjh{:^A# J|TzpJEz -Ћt6`N%b[/Us+0 YS ~xѼCXMx˩Ɋz ] iW4Wv-Fe1  0)Ϭ\TrhoR-eVCD.BXjd~ m[ed.:ʼm1m9ZV$,{uI [CSEHAsp}܇~ m>1~7ڳMZb<&ˋ_|vF';6=/NN/>ظJc}i9ۛN^)ld3zcdQvW}HѮcR[4Q?JVpV{j{^'k-$Nm٦R&/;J:ڞw9:5٩.ᣥKnooŨ64.Tji>z (EI`uLenN$0 Q-s|7vx3tqyufn!$!WP:V(Mys49暽92^?8AVELT2,/g2;.).qe2 Vʲb.g?|xw[Enz0~x|}y3ջw[s[:ãd;g7v7><8`B⟾lck[ۤ(W!F|%i2QpE'ȯڿ8,̗z|Lo_]NzyL={qxx]'1'5.ood})Nr3o/ozPYھj+ c3TU]&:! wwr(rT.FOykJJ5uØ]{evѝJ&E_}Jt|}(*)'%g }t=P\R?)谥nR!Tі齲 F85 &/]MaH 4.\~>::&&N_p'pNŽ٬>sF|J%8lWsg.^]hdJcjl>~'2CgdN~btzYd[?nL.l|S/&=uJT&qNV"t$PuNأV]Cs< 34 ĠwHM*h9iݻZ%#%:6Oo&v @U5uhm,mǁy鑣=lQ^_?wΧXwӱs0뷳5Ğcx4q QmOH(߶2PN*V(u =$L\-r28INqvk;b[yxp> RrhoWΰ?͐fnN AqYivxI>!A-SA|fDx-*LOsJ..?}xW}[p/?dT5>۝O^_]"G~_GqÊC??o~׿zc{>y#!tWh֑]Ε좢r|]]lxqqjP<  4P97d|V͋u}6<ߵ- 1k+ By]RB󔡉nHTDxP6OVJ9/4w"aITR+)3ͺEWϟrVTZ*l9&t9q|u/[nojnU=偷|HӻZxr LHA7q]hJqJk -rb}U=+Yi50żn4hVmo75h*!k +[c1C[ٜ%yI34@R.Z*T JϩQ/kgA,|(C1hu 誜tfy ;Y\nvy̘|Tde秇ryPPڄA3Xy~ GAʃvv38t}:99z%&)fR݊se~7ofd>7DMl}\s$!oX7TPԶ42m]A=d`:-pl:e;8ȩѓs}:L=RoMn))KڜQTNQb63"; l/ἽFX,qE}0M$DCSɳʍr8֗j4W\v%f ajb\ZsyM_2IBCsq\?cq֠P!wYutt}}I/6Τ:.wNځr, m<:n.\mK17tK4vr)XyWD@[V/0Y,.hl1A9A%nb]}_FQ.Ԛ;I$ nP3UK+J8VKH2p‰Bl!.ftuԜe>m !4Hqqc8fl6Ηљ|稐!l3wNVPpEUzT!C-`ϩ(l(Y (%  yH/1c2'okRLg|Qm} ^ˑ^ɏ/gv@H"䫊w$wO3NGGryO\6v0ف4CNpexy5dz(.RL5 "be|oo!ymm}TP!?oo7vtorwgoWyp\Nox2!mm~sǟ/v^}cYQnwKÖe~VUJzV <.;X{ Dg\TZ2"|-3qH Q 8sS/q*dU]6%ƜGR47`&㉀qJϡ(`{((Ԓ=h!U+PGgt8C.tOkiN i^ڦ^an<JC_h!į(G2_݋|lF1µ2g}?7;䙒Vx6 zLR(.+c,nC9 -x}|j M aHq\a뮛4 Yg04VP# l4.hz3+i!V>摐" oMuY5C"U_*Nأ{ox-JDGZi%{w&fZB"W.?UmetMe7yS\cQVg,[92vwx|dxiD H zjZRl_8-Q1s XneҦ:MQUcTAa%*vp!_'?]'{ЇGS -?LO377:T ۏNN_~?g[rψX^ts_ǍsSsׯZ"@m_~Pzb`>Xdflj$ 9ѥD7ZG2 rzBL玾'&dzuN<$pz;)LoF tNs@~6,nB} ~[ayiTLRV#i1IiH8$CXZJRQ; żeBI R|9f j^')K2@=f78nw&d6u ,r66e^^]}b7 zphŦ_VOSc78N&8Vd?Ɋ3P.àΓ}Y\(ڑOr,sa8j* p AO g+DٔHH#Jx_*1g~d KTyݼ.pv||`vp!/ «̭|yMbeۑ.ׇd@ap.1 7D'Ӡ1̴ʼnURyAwċ-U˰Ymyc^B4B*LFz_&ù0^xJaRC-[MTV<Ф݋8(D p)*VMJUG'@HuOl͌4mZ96C# G[sG藢BFPTAm`s ',ZxV2 J2@Ωw4LDV7arݻN_2bg)҄Ȳkg|>Tu-_oz;gEx-3씏H0N'ψuv~=4/=  2_8vd֩P Ǡ6LU9s웉b [Bh|r|}F 5ZL!H \*Ht)yTt}pytty{'ldѹ8 ,MJV45p%bj2 T%cѫhHK4QZ,n;PԺZ5Dl޻J I&J|Ju GWWggĞޖDnń[^h2za dK8˒I%GV"ne* 2K1ۖayuI:FT4gIKpZ^?fYwGӺf:afUɗOpf~EhA``lM'& YܪȀՐhU՗\zXD`J"=cN5 M^peIJ18-.}ŏo}xὕA7˭?wy_?{Ǜ 7}5߽a'_Nw-r1!MdEQ=^[`RI':Ihu+lK[@i VʟM&J WSڭOqUMߔnНÊ'>0X?ub %?jRX8Ut.Y2\•&f8kڴnes ~c`Ѣ=S֕\6iᙤ5:*Q!5že1Zmb _eQvCE;yCaWZa#7bHڋ74=^DEٵMFZfM|P<;k k -<lD+ - (5rߍ$ *^<+%D557։RIWJ5-TIXʚؽs§Fe{|=:M 㫶!B_U2q_nވg3x Ŗ9!]6Ru)VkjmJI|y\ve?qc!/7I" [}b![B@@/oܬAr|uw\hZXt=<&ͦQnxIFcGRޅzz<[Nd޲438{5&a/[7)]{a,ESb4wݻ YQ/46Û ;q,2+وLcWL] yl3ȧY1Kﯮf+7Y H]L&ťQsnUy8ց:ͼD&/nȮ Y|5uIdy!t~dizM{߷X9T\as4]Xd6ڜthO&-9,׏[-ˇiy槾zU4)?3'Doݷ;h[nՏ$[ ͫO?C%[zmo*l7drrntUZcњ:3ۛ{c;ڝt&ۅsx1 QrXʑ,+S.brn3_@9;Qz[|mV>Е17"=F_xPNP ħYU N#JQ(;m +iD W-;V-"=Պl!ҙJ@UB Sݭ e>w&Acי G.Z.?ʤ }9x De~ rj݂GibϚ]fjO9UPJwÖ5ܫUWSbny4:.dUKޑ@esл 25P[xxG i΢CWRe;HFD4= \5 g{ }NF$Pکi@a]v5_CJs$>\W!,dw*RJsv?  Dԯ`.ӪМݶl92S>own]UZ0h1e"v 4c5 RXːYD]=ߜ6]?oW;<q:T}[r9WGyA \KCjgtt'r3;ArHdy9AN!Ҹ< 1qQr?}9 OuvbR-f:WBOZޑFmLU̘O󊹋52efX|+C03?\1~3<W+D>@6IQ:A> f[ק nlfx2#x*lYWQ,؂\l'񓓬U'DU|FnB(f0#I77jq>/lpʤ!19OevEhR灲zTR&e01'QMT1rW;+d$lnL*#đȲĎs]f94d} - WڹPnQ)mH,ERH."p6;_??2Mu|6"OAM/N+O;fI=33KNi؛uxa5&D\ϝ<\^^}Շ {ؒ:|q{>ylsy|r7~󗼳3ڛ˭݃FS2jF`ģH;nc;U/NN|t6!7R~XmL 0Y}b33Sҗ=@l.f(8x@_&j2qW}ּYK1gFGyVjql7 LQ_س>MmH+ֳnvMDiξĵ"?XX6Aܾ"%diuCkeqztSd:r!+xK,:Аې4G<(I/fVGNI!.TQfuQ£bI{z4jheOjҤ2cTAv+*%u-EӾT-PvՓK60̢iACC*w-h-Wxgeҍ` M0$ldg֊}o߾'*^DzrZ|Rǵ 5m521ĹT2*sDϭn=L$?j{ci?{>Oa[i6|\T_l16FIN8%٘B ^e6^'귒+:لMMAKdIJ3howb.[nF.'}O&S+)Ȥ/t(%%9fꔍjtnLz]io 9 nAhύdZĴt~1y+!Aǻjև|8M47g՘A4*35wg'S?_zU )⦞;2i^.9G峥LMd #'|8Q+y)ќ/EXԬ34Oab A^mW'˷Tș.ߚ5z6;<&3[Fb>gH0ֈ1[8<9Цt;n{۶G<*,@l "ϼϙ9/gL"! 䟊X`*CRZӣ͛ttl{s6ڙlo7~oW}$6~_٧ÿ_o>WE.6?=8X7ĖJʂ_3S(|*{rL_|g>;zq|t?;pd)!v,_$1ޥRV2 F#AB#ObI?@ Gͦv5[;yKΦn*d"tނUe<:bLkNABRD+O=VN@)qics7uJgaoD}e:j%=% 3Q1 a:[m8$ R3*5 ŒMK5ԗ|U#&^2%5iӴYR)]fW>*ǐ3'r/tIE*zep1kd2_&L[QV˔kkL: pn¬R~5P\l}UCmWd삉SB PφT@M'pb2؞^ņ5iӖ74Si+IiZ3̂55Ӯn'3&E3H`ʝtT"'ۆDE>11#!ݶ-j ښeqH;t%>!h(VQHLWp$l)k9H䵥~ ųV__յ4Yɐjbyw=D>3)j39QFMSqN`{yoo=6ѣD9kJZٝw]o WJ~}qqyiEzE6f;`l<1r1?> 10L¢a7p +U"!)WGT2Uc!LP=fmN#]wYfQk$7"&Xx8#iFd |{w 35wHUNM TZ'$2'C Y m"ճD1ӎ.'>w>9 hW}~qN3Oc{}1oKq6bQW[Ri^BqYPnVY*hMCJr0ߦ%8Gw@DZ%$`ӖGGG)%R'yԀ/CYK5|1-;h:eoeWeA<mxNN,TՌ&IALd62i: u(ca6@1IٲM"â|g2V?*8${,VZ&ԇI{u8f^L=Mu=? kwBFĉLiK(E- iʖt Dz>SMNM=̿V!IF|G7$ /.7-/cj"{N6ä]^_>nDD >y{{WN?o_|=!X\CLVK Y )cZ,uU־0jPK83H5h\Gl7h6VQ7nOƹh&.!9Im 1R+bWpS<ʎmHv3I(Q-j(Y=3q=AS:]ѡu JpTF'tR̬U{'hP1mZHkޗ`N>[ZAWe2(BP᠉tMl\lCMѹ{Qa(rc(U(]C޴DUX-! Re9̓\ƿjJU6΁ޗ"GJB'(FZkH]F>'лfФdkxn+fNdW=bڈi躉)誆: ZBpFy$Yx s/![-wUj]ۗ V quCqU\W5)Gw ]@(崀Ӹ!YCbhif ч5VOnux(;h&a[~7mL_?1tTd?R#m҈2ܼI/:Gyq~tOJ[ x_$py~&OϟWκwByJَm{חTaDn%,hHgi׎rɠlFP4js|r>kJh9G䬣.]t KVD oYEIWѩK%뎶R**%{s? jq\Rj]M$&%H0]w[=4ha]8~8BzE _Y)ODFRi`Hn(6k h>>4i5\#R^|e{&!Q=`u3* mkWJcJskC%ˮZ ؐCrƞ?4MDAR~hVBfnHNUl?8<4 ަ+;1B ]g$wqLwA5dk::8 ~Gé;^uf.`js:lVcg$<%Az2˫˗^\:z`7on-lw/??߼~{էnWgP7zzlrf꒞p' iloiCA/_O钑NNN GG^>GB}O1C=C;]-%(Ҫ Qa4Tw'FW6NKIުjs ל;]C,b&E' i^s"0 l*m£TB[A!FBUi!ِAr깒.Ju ALO[NO+Hwl-%*ۑ54D'r @SlE>}QY|7DNf-HAip iQQޥAhUb uhz"@](i1}hޜ=&=6Qe/O9 NK؊?4f2ŚԶe6g »!h}-žu(Ј))0T)ei9MI؀(ي4lfŸ℔`awHB(l +V+HE^˜ u5i\Bo_w IXɾ7L jKnm)xA9r,7sRe/Խ2|e_f$P"Pgϟ'_R-a;eeo/..'O>?5*>N޼/F't,T58<bl;4{͔dCc,MR59+@F{=Uk' 'Fd2Nn1VO|砾#pgu[GQQI @)֨# _sv(({Q +0:?#m8>!6hHlg t/jBb]BVgO a[k<"&j+B.ۻccoPס襏hW_^mb[9_FG'Z9N+Wbqy;cӤ|5 OvPmJ_qLi D1`OjZ,K'Ό/?ӟ_~ooG?|8ǣk׎4ѳ985BSY㮐"nrDKRVZԋpid-'K`05TJyV[T`ǻ'¦>tJj7=s{&+ܔ:V"jVh6#!VU(?1yfV: ;Bo֋9[xPUNC՝e·0jf%;P4A4w䶢5[K!:wk[fYi)x:Eԣ,OX+)%:nꮶ!iWaYlKIխ@&hzÀC-©RCUD~B,S2.AY<%v]MZr+cJ>| ZY] }=Zl6>*4 ] h]RzХ.0tJ3P*rZEJã*z#З AVr]v-e^Dshotxt2+0B0ifܵo*PveMsftbLW6~zrًo5Uw&}?!IkQ7__82ϸ7f1Oɪ`bK]US^Ź}}Vݦ6uuyGwϸcMdIr_ߑH *!@tٔpxjbSC+GV}1{M 1J"Y#b7:H+pU{]7Kyyö%mg_b8:tK`]S%!ǨoWm> H'N\@zAY0sH𵓡9t#D_??syu@Jf=x{rV*iYbq䙹po $uNKE~ vfsV7XPYʨE V"$hXC/}^ =vl Bz!ZVw2w6[ |o;$d(*|^HFKFi/% +zIJ%(-J 5lL-pIfY7j2Х39cp HI@I0? fuW/wi\Ӎ$`QƱ,1fALGl/tR;]lC͍bkˌN2TS'#h#%`erl޳m&'Z^^\gf`=L77q~|*p `w2D̴퓒!#,|~}&WbA o^ݛ?[f| p?GbԐѭ߫/^9^8qd'E,CW%VM$i9xQ.iXj8i$lM:0J^B_l\taIU}l4* -Uw* uQlwHfXT OF)ŧ&-VIݙ/J8~SҳH+E;#7qʆT+r%]FzȌ9ҒIʆV SU(]$|TQӲǥʼnͻk Deհ"Alȕ6i]4xI&UF?}C'/J#+eM19ꡇ؃hR2B0L%/3]H'»!)X hm]p5c#JP2] %|L&OCb6 SD _m썇~. AAX%;JNux8IvA;;A %w9TlQ-L !5.k{'|o+ДUtQ/vcxg!inlzkaN'j㷱ϻwo={?`E88]cH~)ۅL[FG[@of%޴\}kJR%Zv}}#VPJdR{_Tʓbg:'" ;B.hbɷ[~i^}I衲qV{{St`]Z ߝX\_5S)nZW<_;[MvK饓KvQ:Z!:΋7lut \ON$fDF(gVCs u $Noz84B00iBSe[6N-P_bD#qEySmZ({NnrB/uxqQ?WF5>2< 9%JKl8խ(ceҖau6BEV:.\vmrZ}:ȯWd 솝GUB&HkaJ=k=(Z1I@4x7&͇񉓇zVGuFU*V}\ <qBUO*}vLcm/INd[7Nef>QWp\(;*\Wg&!պ%ץuOx_yÈj`888AM&4x|P ͹/FA|~ؼ/?AQ<ۓr͂x7$dW ) /e@UI__ݠ-e߭͝tߺgroz1'هwiW/>\_s4nINWra"0em)&PF \m?o۱|>{v[`hT? h;85t6)pKԤ99c ~VkRnXѷBBD/C\,9#n"ԍG+fpk5qVA[Z9 Tr%s[zH-#4 $l ۦUkZl@&dQ[+A%A 5݊U &Kj(a֗&!v@DV$tft&6lƕ t)2I=ͯw"՗x-YIw!.i| N+YSmdZs6 5d6@,zAUv(?a5A.#UQig3\mK<%_3˄Rw&*CM+rBl!R(Mmq2Tel"9q~71ÛT~z;y~4wn[:Zn $3hC̒]s[& MبCg FcM]BՁlvqqa7G/GZ?{Ko_.O''QtsݛoJ1N`Cڪj2Or!kMlL&ߐlG7L}UZXGs ݝ=\ 1MC`u3odwuue__6Ipdph{ѐ$̷u (YcgeۘQ7D|nmN4r©lsq]Eq'p$^[g4x%QS=zwJ -*aƈ5MĔX́-7NZ= "x1@࿾xXֆ6ԑZtzjB\jHl·_ X]N.֞ϞsfKv'oмe&k]SԖRL-)V[2!xV9E-b~(՚Ķz1kRQ5%Q욟N+nGle@(xM䟶.KFie ̓׏f&Lqza)1 :z+biU eT@Դ[\LBTas{wT2\d QR}602#XPBɲ;LJ*INI.#tKTፋt4ObZW NiqCdlyXja3#軦jɒ },D~ 7PWjqک7G@38Le~uDpۖ`јYJ?Z ?/;Gه,oͫP鵎"ًg/noke^Aak3VIUYR!Y@%,`f]H|vtlľ_~x"cZϟOr|>9>Ͽmb625L@L0VKSkR: a KHI@vh"?@ZO"[ Jz%~Kɞ)oG6U֣8'RP Q4ޫiJm*EdYP\_PY ob .4 [q"* ?elnJMqm@Y[*kD\VvT^ٻ"D'QaҤ K󨅓qYn7;qF5.1&VHE@㮉t.ھkmZ& ZԊs!ѼoqhߢFmU` h_Uځ^t-:LpnRi.iiW>Q``huzTU AS]"Zz (ģ,wf@,١*NSհhsފ.@sx37/OGOhq]>Kh#/H^V$Qݙ{VԍS\p4m/|{ێCxbVNZ޾}ǵf,ޑ8l]9:: ˗#/.0s~vFc; \_]?KrtLL$@9hfX9nU\Й5V|Fvp*MھlP#W:.27y#WMbǩz*Əuʻw-R藗/^,' y@.9" {[D"*Iy5(y;Z ꉬ w74o9CC·V!N(.9!" 2Vҕys"D~'q}g'Sqv~_e5a^*)qqη5/re3Ʊ Ef4ŒNfOű'Ox!t' !*@tæ| ^>hh99󙈘+#v&T)E[ZWhecvU9FZDfpsNyt~aCί g9&S).<: eb9g.^Ve@ I*ƖI9j%GKX؏cU>G5Y\.oɋo+Î%W"zoXrWT؜ɟ2 旼pvQo82DSѢ-%n D]\'ã1n &ۻ?.j>t<ޟN4 ={ͷoT2sQW>3^}Bv$fSP*AT7%MJU'cHfw\3[l2ۻyZU899u$pC ~upqTfdmnл/,vEӨe赎Y/uͬIkj5N( DU&(*HYV%k#EZ"+Ƅ们ERdq {NDJAj%jf/L񇳻"\g(ixJǩo6 ]GJtf  $a}}ĪuD*Ұ[;}m.뻯.-mNe 9E@fҿF1 ($j"`jdAit @+#9?6е1t)$@hiPtC!4t¨8K`5QuĨL=_܆n=]=F z K2P@y}H*"[ ~t65I8v٢NG226r-Q\oQv, M'˫3-d:D\6Q1>^mRϩd=?|KnG7G9m<ί7:rA K2PdiBxn(4X=}/?.yY\GK{s|V3%{I]K>4~_npT3Q*Kv|}G}Ȥe9n}n3tC_%BU-fev#N6܈42C0foSuP@s&n7JQ1\ҡ>ܓ"C鶵5fWeϡ?zgyL i١NIId[\Cт^a>!g4^ + $ wKMZKpjٳg K|_NI17Qq?l0>)͝V }ukݹlm+l#nB4ዹ4u.i]iD w^^e#4j!|V/{2kPVES>.u|83Sv5탳Lv{2OZ~YQ. [:i_]/%fJw-mg$bu_̙T Y6?So1O/>|F- #&935锶B YjMDIFIjwt…np;88D.&Q_]^-7r^l$9^f3wS]N&ӓc>XcNNN66޽vquih4/zM7뫚T[Qkrbq}e60sZt0skXxS"t62l:3H8{x,Ze,*88A.'$%'F~z*;|^ؔ9֝~]wd{(ePwJgBUtrkDl[-zhr[xkcdMYehG?:Q 7˼f߄$#~;!BW23&Nc2=UؘnkvH5J9%?[.NY_Z : \ v?V~; C~y1"<4.Ht,&M$E .m ϧ@%<9=} n: Nɇ_+xkI2*XyttsB ٣nO,C!$cZwkk"9K3V6I*b; kæR_V]԰KYpD̊OZժw 5eV b3m \͘feܻspHٮ ^<:1!~53"!CV!gHy䟺-Dz '%r^ĬBCNiv#F阏y21Ү{W ȫ{;~~^}H#r==RF3|jUwʱv`bŰR'$Y)m+$Hwye T=1_c.pm زHlrFO])'A=7Us::~ON<|YI6w=rD!:eIV&̏kQi 4CyyeOaŪ;[ˍwўX@n!Omm;TYrLa,d2'r!=A6ϩ3茞=UlvTJv3GHS:6%-Y3kj{$2ѕj%+UsZf諥Pꬖ-nCxRPYC@5''Ԕ! winbki[7\)PV+QUAXIP6O<֦6,GbI8Ti ̭|0DlөhC"HU?eLȯNHgF-TVlɉuoĥl\U𵞈@Th$Co Q-Jxk :(r-?HơЪ'u+TDrk^v\k'YF&ɭFcmƬ̢Vmܴ%&)"p[/I1fYLE!tJ) vU6䲰XzFZaLˇk[3l5 Й$I^O:nmȽk4G0o[⊅Z>HWc/6R嘹}zzJUg6Z..OO?{ wohM')/F1_d:ybfy%bF ZZBrqj]_EvߎlRex<!afbqY8d缝nuٗM(ziבɠ@xZ.4wsyL:n(%啈}5<[( .ROl%9Q횀4_Um dL'̊ڱ)[OHH W[$MjGzQE)ƈuig ؈0߽̋:W},oMΜtjҴ%SF{Hv־ _TSpKʐ( &bTTry#PkYCFtuwTuy?_#ҙ'uWfڎ2/]Mл,|q;ԠG/x-z(̀NOU8 K +1$WŪb - Mlhʅ/T&KgwU2σuoD%KUR'^>qr'6믿?^Ȱde +eb=jMCaewEP-6u:<۸ %*PIC'+?c3#@ uWTt󇜲(sgWsCW}|?^xX>VQ]8HX$&%U$jiUJ8 -qV^MG&eP٪ٺ+٣H ݵt#]zp"j9AU7QekAiA9$Hp} Mr=#ٜZh[jA&#k-mEHe7L}6 WHEx0Y<+5w)6Cvu@jV<+eZM.m2[`J[8/)ifte&FPp'JgM@j7R%Ps f%p !֒ev-RקSVZVJW`h0C:oU͕z4bL=хS_ZW~qpݡ[-{ȶkWu{;[ݭH/4G"[Fï}bj2F =9uaz%YU-9P(77^:99o>XKm(ol7 tI}Y䧢8fEIxvN +0@hR!H@g̨d>C6(8j˂B|_'mCNr&F_UFgQTvMz %[.#>D&ņX%(uF*Ztbp#<] eN5i>. NC5@t,LfE rx-Cy*Ao)% @Lݍmr\9==~<#LG.j֖ $龪zJ4Je⬜!giϑ> źq4r@܌|8r;JuU_O(UPRmQEhkզ-k R-$Pv2.@ſ4O:ځRnjdWSi" A>hX Ҝ=͠,)[& k՗tn5XAs*t[{"w 6i͇*HS)Ih!xT5`v k!Mp`eJl o61#jAbUꪡᲛ44q`Yqlɮ(,p]=4%Kz!V+Rj^]Sh#:Ȯ, i]V~0+B@-zX8F0>999od,4..t~uu!/X-WWW>|lz0Ufөj0gM-gvnC(?C!wѱ .M|lY/%2yZ3(ͯ* $8+\7 |qupk0 1 ##CUCD:/j gX % ̘X9ZmlH@@p}pݡ_)5.*S8Wwhr߮"[(c ,=7/.lS"a [o;N Erxt5}!mX6e}2Ϊv)hE(NK#Efxf5ܑP&p<-D ޢ S V":(QlONoc؁% A<.[-3 :sh2@&@\Q 4BQR e3 #4MԶEaW<Қ)Yd8`lwm3ZUMIhڄq}!&љd>,wv~# YwV<5'oI*$!&J빲ф=L! fb?:]jk ?Y`.O(r`8='à D]Y.ּ:ܼML<}pp7/v?<8Y\;A"׵C7tʬDRix͍2`H'Eͷɷ߾YX%ƅ?YOIolM 416MpH[DtNMЉMz2ib@zJbVt_Z5l2;r"3zޥ&U jLCJµNV&S4D\jt t Fue0J7exҴ) -Ҕv -@&Ӳ\XK ҬTjJP9MA4+ MQv![]nn9vw)zKvnsH)ͻ 9Hwi)O9[,W<( bUP$;74O6pc$*BTu4O8:=΂h[,!vV/]aηd7|1[k#hۥD^4r71Y@WIr J54Ė0x7[#&6ɹXEP.9b8Mf<=#o:۷J-no߾2"ll@⇼۾R3ۨu9Qoje'l4zmWʝz={C|'u`?5or /{BN &su9]Uj.y&Fj+]Od@#bjs]w+[+aec LJ &r 9}Ӯ"37LޱqۿcI!֨ua[KU>Ͼۖ.ֱ u0a*k<2KF^~\"⁷J5`Յδ=((x]W~֌)~ʫ4Β: %~ZQQ ?2v{ssyqyx|lr,e^𬿃fM>X}mccu1jh5ZsGa 3p3Wkkߵrf] ]Q/תE7]E%k9p͹ZsBJpvo4<kB Z]%(IQGiU:85lZCyz 1utjdݽC݆[9``׶4^1( $@Lz]ur(ÙZW]q Ȕ9ݼk |(Öme]^T4У!)ciA;8@_QByiXyl,h! ˦I 'G( QwlGS?Sn]&9Zj[fd6=8:?86[Kc7׶{{d7 ۥ(?h^pb8'B[ nMgX&JI1&/laZv]ͯV/5Y67n?|{qqƩ,ҧ3$W)ǵ\nSQz,O'Ң N}μ)뛫_~~vvQn3~Ȋ<ȜSzX[!)MZ(Wa(-'?I@2yT/*睇b E@i<!Y͔UvXX hHh~@Ule2iJ:a+MoDJtٽahU!.)YI6AGqZ5 m캒fjR -=ص}@p% G4ZВACƛ .[ AIH_@O"YI|bAo6m[`7ii}K80T(e#0@#Ke?աemۛ%m)Mt٢І4Ҁ!JV(EHpHS Use;ne 9G{C"_ QV꧚eea";(D?]l9}<;k#@ZtQ^+fvNdvI uR5Th;1la>~/mqr ,bu vEȳDً*pzz}hgm 5>S|b 88<ѱwߞ}`g]ܥ`y9GGG6g5dF>RV1k30t QD`KEK. Q0:6CK[ m |>Z̯ Zt5HkRJ@"wgy"!.f JBs*;0dVK ٗ hD!$ YNNְ-bDjQZ7<֣ಫeL}pzw||<geޅPaۍO^Z,Gh0#h1Qncc.=ҚSq8NlLɱ*a復[4WrWk5R ,;O_$фZ2P(]'XqU3f Bp D׍WoʫhFaT }̝̻R[2lXwO%F?ʰ*$d|.C[/T˟6[ 6L-gw%zo<#"UI3y1yԫO&K]9LбK%HD&Y;@j%Et2:6*xSK&n=XM\x?X, ˭=Fr?Cl<:%senpq}Mt=Ri3zڜϞk5;8?\/j9ͬ*Ȇ[ *BΣ`@_V_=غPV`E|⨶/]kFfT?_oFWW9ғt< ,-l H{; 48Yu>~_ʍsVd+ y;Pݴwxp ߫W&ӨmhZ~56x?ыO>&;믿].juwZ?yb`I-y'tiG|b U:t rdshnQ%]ƍՑE:[F=A"C/J+2O <8mx <=yW쇣|ܑmP I4 @1tZ[Uʰ.'Z h.NS ~A`r\C@,Wm.{ @`: x+ qB F_lz}#d &"k3670P)qQsɐ t~09EJC&@$oR]88f9Y}pbl\oiK-ʥo E;Z)S-!(%93fk&ZLv8d6Z%6m$AlhݺLS Dsl:!lpl-Ć"xu7-) noQ֥ZdI>[mv,!2o$L`ɿ&gWGZEG;y(qf!D)8D[< j:uRTgd-@%Fqeo2ʈMo(2-9J-vܞL~Gw_֞fo9yENmؑivpn}as@4?N&'燧OO/?Tg`|$ '/wϞM$ڢF+0W!=-{qmkwûw77k✄H.JO]2UphVք j(qwnnfBg;@̂UYzL[f +N1ಇ9 &jo2E0 sន mj"LN(Ә[!N\^1 Sf"S )%C2/*5R6J(Ӷ]]۠/鷶^MiyB6 UנXt DkUM0ûKRٜz=I7 [ׂ΢AZBo&&<p%[80wʁJTJ7l0˦|<*+jKӷ&W[@׶4<\x(Hhe@ZiU`m Ӣn>YUUsYѻUYWO IR8Mo%-[墄MrY0Sʷ!Fxm9VeRU4IZUmeZ+<]"$"1&3Nxplʃ󶩻|6o 6o޼$.f3F<6AH*|pt6{|xkNNN,}>~|-^ށ|qr|2N?~qsT(9SeX?AgbS H5Hhs MlH7CЗdlw4N N*쯁624#9;;|~杳em+?!,HB&d6'k?/_'x~qy?|y=N^aˋd$i_^_9 OZ,dvx" Kdw羾s=.|<:y7TG⛯q}q9\ί$Z]v$#4ld-ߟND g;#FjP0!j&Wa\9vm,O%rŦmx?'XQ=CTudM4TK>ͳP:L|6[{(Y`ә2>IDATa6Ly=M5^d8t\,{7Ft$ wo~˫qYn2~ϒړ[Rq8'pqqNc,ïm ØZ5^C2%?k՛9V_4ƣRh7?ʜx糙v+=n䳓g{z|V?OYqxx(~)rwots{oe98R 6;4B@"L7,[ӛ e!@TۭV't9W_^Y7*f_/`b]Wfdzw..7s4fvdo6Ƣq[;ă=3P/gTHOa"PI>JUj($ R8:Iԓ'nqn#cH!Va1?'Bn1f s0m3,b' ցXDv2}ߪ^k{ :c{;U-nZB1Ҳ/]x[YW3,'9CVlcI%Mh ?zYD~Mҭ|hZMʺNOWFfT 9p>:tb*)yÐͭe,ʨe [|3_Qo1|F̂lB7P(@]~2Q!Ǯ9C0s3C93=ԣ gG|[ƨ:(Q/dBZ <*)a mFǥ`0p {\= IPG[8qVhq+QxWJW<ΘUyiDmeh^#qۙ!YQ?T/e^/nV_1U$P_ ő`t@ϴˮqK;<%HYX }*bE:eE5NT;+Y!Uƴqtٜ f{ͅ셰FEU7F&6'S#+*f~V( 3޸<՛;YNmJ^8^޲pYYm>|qu??n?f񷯿k=;W^_^V`y2P:/&9e%ʪ/66'ˬ7gã<0·i)`l2Yx{}*ɢW %ݹh h.0Ǥ:8HX lo4n}3kӮKl hiƇkMpraRuuĂ@{˾nD4uIm[1DH`cIws9PӼL=[v+}Յ#`yX?dfǗ+?xg}6N˯ 0l$v\>5tjcypx4qѡ+?$ %2p/_:ݾd\z3HBYL~w5}*Ҥ7uNЇR!@HFBu i%/l8)ӀޒK(I1NQ9{(uD[{yZ&w3fFDp }M%mѕdFYЕZе80B3֥mjMADIdһq#&i4秧'Nœ&Ԙ8-㊭-gyFpuv'H39Q/-RzC/,U [5lOc禮'V8![Nx ; r.cVN[, z8JbAZT@y (VNs5[GbEn$e:d۳UTXirі۵iSMx{;^یSOۉ+><%y?xexymNz;5]ICCL_/h'tl6 }owDO;8TE+qU1(b<簲= 6ȉ2d >(IP{ Ú;ey=%@l<E#Eۻ<`b,sn<ٟw<'{yyew7PUnS@džjOR겯.[AN4X;JF=_}Zav!b(ʕnǍо{ȸyW]+ewা0ho\OhtZI[n {E|>~$<{뙭}} /SN..,G빃aeBxx|jzq>oc%~vKf:i<<8|.KO^?f($'~aS'Lj$"ˑcA~vwkAu>_Jl=.uC(vl2H`wxt hI1tζO% CSZ)<\X-V@uQ"{$ !ծFaoHmvM>i$')Ǝ@Yv5 z~1|VI7m O~Ӫ>'ǧo޾UWn~}%ZtbiAk:z( d:_hdbTD7s1D1KN;ե&h]}w v4`*l.FGq@z7N#⩏*5$* ~,VIE=!5p]qfqeh-H&̮%%Io/OpR`lھG94I=dj؂ȷ.5}_vK]7-#E$ qŏl\TmehZk]gI_oM^JM}OeTg3ҟxWY5\.⍚Tq1:noa٣_ruEO@S Q9)V  e'_]#s-st]'w-A1e+Ipzh"gQ˛E,IOd"6?H"uec2]]`vVRG1t4oaitqZu#H[ycF^~t8sqϾ0{1DB~Dᜬrw#JwR6i%_`h3qlYv8Sjĕ-;u8-*&W`l g"2W{d|^@fX!f| "";oQ&Ƿ>4Zs/ph"qx?ՇCwI$G n?]^^}x!ڸ{[l>8.7߼~ݗ?9JmtksZa" o4@D!Ok rC*K{rA04gZ2LUK;]h;0-hN!l//6Ս(o!=vB5(Y!ッ}+'IRyBgSqkK0y~LONNQɇ/ws{MUf~>@#v~>H HAGrLӧrKuqTEe7Vp+V,lw|r|{uq壤&Kns~wVu||r{o~A}ZĦu⌓x'k 07 R!4X(9ޮ/ kRVDli^4[lj/le-FZ>IVJ}Ǔ?:8n;I =–}P?+wLS]`? %:mtDJJU5 Iōy!T$p,\qRrڴ}(2*0n69 WW|Oc3rU+ݝ%*YTwv߼QI$e|c=NӀP8_#<ʪ" 0h$T=l T cBM`ER8Vt9#/f6aM#n~  \ZD֤xٕ(2̴ޏrlJN60Z? rp_^⌰TS͟ܛ;^23ބ=p^r\v|(/Vy[^zp;멎>%1]VD}S%Epj f:_3S-_ <d{(i˿̑Nak7YdҴtuVo#<2M6'K /;DM9|vzf›3*T{vc×_xRɺzJ3۷77S/oέܙ/yo3g77w?9@%Wpwsy{uwc1<IYk(YHIjAzU{'g'eKfsb\TsC 92RE VԪj6ᚔZ :$l@gv"FceE+W򬄪ō51&t2Hm(Y—RYxstp9y-+lDTV21Qk%j]H0DπV`+GKS)e.C) ^򗮬rB$?Ǔ | %"RU G$%,`Zs: BMT+U[@k=<8 h7eFx *"="Xb Dj e)Ӫ(К*'P~h2'4S1qP.I9vvP='课͛s%Ʀ^;de_aW oƷ3yYe'''w9&R‘o~^Iw薊ۛ睯y;+7cuпf 577s 4-0@J⌂y,: gM&\shg4ZѼ}2%Oa|EҢi "d`Cg+Jk8߹n.ded3crUJ) Ԝ5K z!KqI93WBL3cWgRpLV҃N*͌x>?;s.`A5p#d>/Tp8IҏIUe2Ņ&<41.Ezz4a@F֬-/aA #f'"c èLb+1ǨMTCDMp *etOe ZUHP .{ }N^`VXyV>Q>cq9=_sCb1ot/18LҕBy¢sY:?+Iޅ,hR2Yd TQ"G2(%Z wǣ50_|rp3T1A'( DJ~X3k?KH͑""T?ùI'ifIɼ}7狛ݼ)A^:2L7ywf7#V[7{/?[?CfA~0%7ԝiGNL0iG<{//.Wv>_'o޼_>KFnח2r6O~d1W( ]μg>YCuՍ./ lX?`lGY(m < Oc*-ޡ jeP+ZZqa>'I$Y1䅈}Lae LjVzI$P|W޲ p1poe#G,T ZeXojD 6UPjԍfax+XXdmE'aY y  2ťUq(^VAmJP<,ȴ)z?HU98 "͖ B$?*AI/?dS{cvPu|L%UnͻxswoW8>>ѡs.n=+p|d;9d\_|sLx1IEw]~Z9Kn>y>&B,+f!^)mFI,)xՖ ,#f>IGys&cDD FعLKgn&|9A | >m#l^yheܐU6f*`o0m9T1%LU/GP$ AzdPHC1PHjZx1AD18ӷۑMOYBGǞ{#Rbn|R9N$OX7n/1i_ܔ4LBbT_-piϯJ'BZQ4CvU눉[LBzB~ڪA繲4.:bT('&s2dw&^?f0[Y_ǂޜ6{@kRNRQƷV9 ,gyO΋!Q!Œa 鱵2mс?K**t&N&oT߁_y([ Ɔι hm-6"2-b^đt)K ]^^8P U;^j"+R@gIG͵+ko{Ƈp[qz1/޹H$&./no.>!?ڗz\ZJz>K_y%/yǧ˗@}cˢ7_:|{wtz=wy>^^_]W7>n<24Hp,JH!6o߾>!|gɓR8 kz^,z\ZQX,K,BAWmPULjz<\0̎}nT|wyV\gHM[y - ,4ĬWb?*iP¸ Y<,*ФA| B^U8 x(DC>"qOe6)ѱ&Ssˀ8i)Hlu-Tx# JݍuRU,*,R&Ղk%Pkq" _K txJs5 Uȟv|i%Wj.ꈧ ꢹF V瘮 )RYH (,@ e4^mB08CʞdFAԓo;J9-HQKY:\B) Y nT\[wd켈oydrgKW(4qAV/vVÇ~@y|O]`[ScT!d?P'''W?~ f|+X9ZQ \土V.o~_>h}|~9>>qܡ |>#pǏl)>qNa6CwFed .@ĐUw^ *2ܔ(>8"VeqAj IBWY欸 dbR9N.{yW_}}|rjo40S(/}qN̯\&4KĬ;;( $ Z $pScA#7#b"+h_%1t=Evw'oÏ'y+los؊p%FFx uԖj\ծ]*?!]|#Ó*ޘ匮bՌHa}͡F9S"3UBx$:,gylw6lϜ>?ɘOhQY׉[7FqFҒ߸gbc4nvt|Ǩ9לK%Aȸ,%!A6Hr§!0n[P |'77@׊[~&yO}Krt>V9m<ddW SI$`H%zGtl% “zx(TUH*Ets= (x5Q:(Qf˚ЦЋFc~;1yPDd%\˃/V^x [{WDt!~EQR*HdQVPfHϳ.t-3Wuu!4ϹJҀHWPo%9>ϴҁl[SE([&.Kb(I=TݸF^q'M(ľ01=2pI_XGݶL!) fk5~`rF*67pnc6z2$lJ"}#5Nv`rvwf]!Rժ*o6EpaNg");Ԣ!󇏿Y"@V޷춠+ǻKF! Erc~˗g[C~+A Όag92i 8 d6ã/~}7}wurv7OwO7wWzu{{! HjUI]hAp c2Aw]V|:={v+m=?l휜u $瓥z2靰d%,Bɟ$-BT220Ĥm"K*i9)#z\l{$Oldh9_%#Ok0Cn翘P KmTz)ݤraȑ&qxJLzf@LyL,r̊е ʣJ9z(8.e;Wjd*)R b$PG񶞤mѶ9(/1Qe._uD1RRW[q,TkJn`Q%:TM&e[Q/24ʰe5'2򓆹l-KDZU,;ɾ5Lt-WYp%; ӽoiL^$hzj_C5,UUĶ"/%#jVb8pb784G`!G8y,]fKTuw*'yO4cߏ6| e!-jHnp'ڭ34I˧OIWt78Wz m:=ͷ烧gLJ_> |o09k>\|p?sqpu_\>~H3LbK֋ 0Cbp; AFB? P䒔5M{! d~R\lz}!51G|d6Mj ʪ1)'f r E^S?;}{:%|~ӯ':uq uC̜L"Y[H yU~B+jlt6]2ߊangߋ|pijRy*GQm)[ nV&蔓"d-p@9fOBbbx/2<`w -^1Mk}?x]*m!}x}u$ai3 } _Zd1 Ypc@j[o>נzg'_}ozzj)<{sΙݯ եa?:ܗ˚ :(Dx7s׍ >88||G;J@W.OP%sWYP'D:\S 'uuF ?#0k)fT*jYoLl}Ѳm5veM1MI܌M8hg(_h6#ߧ2)\E99H9D%Քv}-Xьy)&"! h7ak(Ct ^΂*"Pgum-UK7;i]+/iHARX@à0NE7 JE8G__(xYQ" 4a(<U=|aXWڅK%fU2(YKpC&zp•"cvSm %yPuxȍ?T<)5%o/23/Bix8QHAfiO"(O*u EY_W!RUHRoiL}i҆|(q .th {4djh `.2ӫOO>|I O磓c $rMqq Id037|?7`V/_d,O6 <%=x>2?9>_#-N]^^|Q.84Xxk;Я~o~b +R'`3^)խN]Q⌹y}6,m&DSj+%99g7AuЭlJDo=Qnx wA+޼}g޽ ׳ۛ;q_tՆ!Ot++K7 uAሐrCXt94Q@e:gIcERn_z 9gp: ;fNV,޹Lb8ϒ-+O^J)5T&NH- d)>r4( `,C"jjfhjTщ%\Q=7%"$L_,%bL^g0TmDzt;22 ooJছ9jʲ[L[>Vr [T̄OI½|N{b UFv}}?V`fVPH5m |Fi><}rGS.`!CGtw-n%BUѵ Z,;lNu%OMӼ5n絡+ԛٞ|aVw>tyh>c69?:z;Lgz,Bw77oUAr77Wn":?SזJ{b8; 0 AO(SuOMM9h \R/ YrHEם<7{hoeDsIӀd.~M!ML6MeU@RUSР5k U UePbU4< *NK)J JՂ TKjSvUAMݵZ'bGO,boAuF_$ L(35X-+5:PbZM6fGα3NPL+;p؜"$!6UUʷ?XG?Jjy4P(#7D UEbx[ J>](^$X*Յa%+w"kfmnQF7@ Խ!%.8"v߽9HtA˕VGC뫫hYwHL; LdnݔVw3_fCRQww߿,If雷Z8Jo1βju|rƇsk{~N^N:Wܢٓ<u*NEaUZ!BNW+h)q;Ďfu!!3;M A+-_qg" ΁C=2ʭۯOn;p|zmA ~51E6 F -1`[l .-҃`[m0XKJߕZ 7Ynn:JĉrI KW9Czv: Fmk(y]:MwL"l=bD,"693KiuMB'r"TE(RϸgH 9MC?_UDWlno0mg\qMJl›!3%|?s''s̬6Ynxw(HG:4;;-zhId0RF!Nt0.{妖{U,Q8wH/r6߳(+S4&q2ˑ(jZ-T*TR< K0}P5ZSYd1Wt(:8F}üߙwxi{7/HX0A\/\Y!NNB.!LgőഡG_V1EdXElk{}7_~󳫫+%N! CJyy}}%0MRv&d-}u Lߌ&}՗?|SfwE%L-3I_rI hR&! 4iYF2"oF3ټvӱo]*sr7 TX<{3zI df(B'V([86yh>Ȱ2S㣖.cP]JPsas׎R"A@:(^ATCAdTRFZm,vM! 8 l3.6{9ro?#/zm0ty2KʹUc+? ֱ.7w</Czir&\ӵU N\lP:!AwBv}R兵͍6RMv_:m9JdeF;* I[ݻ_;ٯ뻝>\آ"/3 ܤIkJ}s[Ã^J*CRrLk:g x6PҕJ?*qj"-*<ūS)^|+,E#Xø'TUp61g9BsdW77Wkck:4zvC]lD<3NggZ~@?MN=gV/_7Qj_)!cܕ2~W`|xsvvSy"m1)lFGG+-񼜚w1f9ЯݼYבkICUJݜyh% ɻ63YdI40tw}~/mSKq~Q$By,]^+T ck~春;{p~7?_ɟ`,_4/ ;9mS{JEbYwP`BTF ,:Q߼""蒪qR:'R:eDІ|;'Pާ1O 0Z})k]l=-qzUj]j' іwӵV0HbR%nCP(NDfolIZI'@R^UJn'ch[9kB4ؚu/ȖPe6&M&tfq5m׻s挘o?w ;DuG1y`UEy}i\V%e9 ^eUÛhP2PR%O!G綠%qecey!JUz(ԁh(Q0pV33z$TOy9el/~7_)w9 8߲g4k˝hVUr'RX\u^Yߣc;ʺn/yyr}:\]{riىuy8ҘGq`wwzsFĥ^8XH%ΧOn/=QTcyIabu9c7Nf,9Hu+UgY/rni*I !IGj @":k"AHP=Z!@yMW'3Rqtxld8V O+o1/9B-NPgCj!<,e]f:Gǜ)zz; Z4sĪջ2(92zh5NOOɢhFRpHn3 &+!Yji?S$U(fmtD&mzKمERFbd=Q9Im 6V<łڿ9iA74UT󦾜9{X!dY*MfRg 7;eՅSNd:m|HPOY25$Nead]Ca]+)%%PUaքϪk&Ǘ˿Αp=LʤG 4ԢR5nQ*uqE7?6IHq@<"R{GIßɟ[Jnn,}{X[0מçO\`\ono̰?޼ylo9XEpö^By(~t{}uVGǔw}ccuǂَr3_2Q"s"ʦ̻fs,|P$MO]H`NV:4A(33trQq@d vɟG\@GyѣB5^ lmFjeFf,E4 ehI0ɨ1:)x2@" q<m=yW+9"))e9X)hP]dѕE pM~6&(ՊnNwDyQlReP._(*RZU4p/,*6)[hYIJ)G/?FcZE$V)%sn_':PBq -qOς {9,,ڤl+tYgUj*Phk)Pz‰"J.@w_?wGXrgrfm_]]|IV{6+KI~E4i27K/_v>5^ʼtqy>rJDw7קg'.Btw6a5- BU~͆& #׏>( ?U~(Nupׄ_ScIH{oveV!huA X26(14WP>|ȯ貍 8]ݕJHm5’c{A*JɟWzա 1/W݋Ýuryż^3s:PtϠ9;9fB+AFUB5 A+i2jBdBCU.ÀMxwG@Қo<EOFe&U"L ,ڀt -G&e1_WڷVCp|]& y+JgJ2ȚVnpyJލLPᚱ/}LdJ1NYq#K^Ks;(pk?”;z!Crǫ\0B yxAE5$Jh1keI`yʩ> 8g,d3Fʔ1p ߸Ӂ \DuF?9\*% žlbo8eu &;6GUʁaթ6!b3U](ZuP5- ¹KK(ϋ"Ynw718Ⱦ{շ[ykCg~%wyHd$̳ 'WG0 @^2}x k+n R|U SlrtW?'g~:Z=\clyfߢaȮO`LŴ!~:"mquiܘLGѝtSTR8Щ=QVH/z8o.[xHfz 6qt^iHUcXq OJ(뭼ө]8Bi:]Ng:!Q Ɠ<)()K-m''Iua-F0D'AdS8jCL :DtV K pGp.XY*zު湲̬HOOn4)CHJX֢EWfi=$3vFfPzrZ~(͋BfGSå:smcesg6K 6hMFn~ؗYc!v>$xy?`DeC6ȍUf<6huq&З&#RZa-fe)j$@ψ? OR# 9tjF6oKVWVݼ[77ӿ˧d.c QACsP(0]=OW5,5mW=Aj8nVM%i>qIk7?ݵMDKonxz7Ob[//;q>}ḍ\#0%N$͗,K#t1I]՜K"\GDǙK jBI`Cc9kM8,Z60#Wrդ]n=;!KcѣDF;EfxEG}R5*?.ѬUE (- 1E+ (a1`? zԓ.i"[[h0D57 ųQ QRkBIlJ1l8@9&PDIJ *&v=5䀒zR Q*X@)R gq @wVECG@5̈HT& ,FQ"\R( UT٩TT TO-qV]d!𔳔6aC)iO]&R"J<mu힬Vj+=8G//U''L e: vļjwy_̕|NO|rpˬBGp;pzzbs}e7qkd뙞g缣# ~V|}>s]~t'A˞ n/oO(|o5mvjEn(|y:˅CFɐ>Y"#}x}}c9+y)<+fT$wBKm'Gٟ_7o$>6ZyڗO,%oﮬ۽5. 1fΎbuYkP^n"MVPE59p\Q;-50%Dd2;4c+'𣈳rN~(Iտ"#Ono+s'$1i9* z2?ce@gzeV囸r npKQ7&%&1x(YoTbčq)KCK[0: ~6UFMބcݔ1OrXBe92 ].)(m9+BBY嶣SF]+s};kV"+5:~+pVˤՠd&2hب1 y7t^=3w3ms˛2r ,Yf b1Qk g@mĹsF>f`MS7?s]prf֙޿'ߞ.w)d5X|,[֝!IE8oimmz٘N'Bj絇OSw':n.S ,H4Ǩ4'z"[o秧uhu杬^ܐvϟK|ɡۂ6c#MfN^Yw]%壵qbaO!Y3j+nꇫo޾zw v Qۄs@42)?\23#~7pS;UgÙo֛ա~a:8G[V]Gs_5]ɀqcL͓^M?Yzdݱ\_o.nod1riΎ3uAonf&he;p޿|W̅ӳ7sS⬦v3Kс]*3u}-g VN~ Ţv}ke֮F-+u7_PaNjm*rN|)[wJ+B+Z "(} X0k59:MN'!ʵDFUX=0 l D*Xo-jzI`94B8曛IlIwxՁu&9uFr=EyUǴN6a8MJdžhȑ_S{[gpfM 'y~ďn &J|KYSE]g /$}CR}YFTvl'2Ogc,881_ГATg0呙N$:gBP0SXye@#@J*CB>2ZY(Έ4hʙJ>U>"hu|[~d'6̎U|Ϥ=eJp߬!nRg^Ov(PwDLP&ؼpgM'sZ{ .wi6mI,N9e#MXx>и d3| [pcޒIG;G@t,oΡPl33ySܶ%rʐ=Ιe]!GfKY$o(˦ϩ QoIJ@Fm^WQ?-G%!U4g0d\Hysu LTA$dd2|$GCX$gDž(e@`0Ť 2°Y,,N 6q![*Ԥ,AKS̺@ 'qEDRHB0AT?[Ujee DSlm]LЂ /Z/=G=U^/4:t ;?KJ-A@:qtG(Z~@^,nUaT.JZ. YA@hR%dDM6|kf+Y,.NW w|+s?qxhjc9ը$Ƨoop.-޼{qI]s׿\JT WNljFN+)(_( RQi-">GyW1Kؼi~%6Y$1IY<"->aLP;h3K߼/?Ӛvw?|/9ȴڧ ;qYpuu~br(m*__I'Ϋ>]FXy<".m(~ugl PK:d§ 8 ҆.bmJT!")GW׀Lp͙3" jV'}2Ծ?y'I}3*D)Kps+iy葈 O7t瀞cZZ+䶙HL A< Ä`(v^l&tF'`^[㨥ͥH,TٹG:A9 eYI'dϔF]; ɏ_ [P9xt 3|@\LO9DO8& j4O./o?~7}M-[FB%d C:O3Oiy ء$N;D 4G2Dzr'"P2QBxiȵ2n邝.~R`|Oyr-RDBE]3[el>'_jqNڜY)Y39 h)xHrF`zDΗYu?=䰒~O7$̂nlwSK+!0T.Dq;2}%$&lr`qf; +_71l ($v¶~6Zk OuhajQ"y$"rVGI 2: :At (d Ū@(vjGf)ٰktOY K,֖GZIA)8:/e~Nqo/.uAtUM̠xDImQEf:p.VP%*TyK aUŁ6jh^ *" $(:Dg1*,ԴZUMXÈfh*Jz&R6 o=%bXp>XUPUۻ{w7-wyIq9ﰶCHE{|r[lY|y iFkBuv'y͛o߼qW_}O>~h[BǏ&扩.Rkq!N"nkQM6I3\kÚM'lբU(x֪+:I##||S D$\S#8+KT6+z1hM32XNI[q86T;%*CpSR\N 4(YTUJlL_ ogFVX H2A?me51Ϻ GPBV‰$}ٹZs^\ yAlREvfm$rM殺y{^QcG^ʭ?ÅL;;'zrzJTZfÃ/?ߺ& ]~26K '2zO{oUsҊ"CZ_2>=:We^|oz6伺g8OtEg27%;XH!y~~/ a~B!ϼ-f[ {z/?|&o(ʷϝfաCX :ZQ@gL|e|_SGh9 R0cZH aPNb{ß>qj@C9B%/P<5<ɵ<䗨r@w!Bڒ-VnMο8=;]^|˾/o! t(1ʖfa%A$z4Ѩ v9FCiG74Dje*t b]"40(qJ($Ux*WٲUMb #L+^~J&BccnZ)-iHqLVƽ[I)-o, r;=>ky7_Ѵd!+Փ7?B| >٦J!R꼷!A}).}|OuیRXG\&oe1cOYR@")&\1fϽb"y*Kqe0ķ_e)1;,{NQ6L`u hB0eYBJOo [:'GǶOC3#ENI_6|T7.HB^Mۗw%"/Ud%"8&ƨ:?{c%dOhxqx<޶ ~K|΁ }r)NސzN{fCԹ4Y3f u]`bI}r:?Y|m>k ,.rGΤ-&v;G^fNf (,׌LG"ÎZS~xG7HrKE BEԫ{B,76sFpފ/ [)l='JGijv̌lc=W' }QX6o+P"JePO)V)٥^6eESU:3:PZ$ #)kjw\DJ*N@MZUsp=7xjRa gU+dy:d@uuiˀ /ORֱ*,%ަx[kFuӓy`}ww㇧1[GGwvF3WW&onB77lZx=v$i?m_o?><>eq϶MNe +0ndJ'AxKB = T=#H1AS<J+4 TG{9+9eVn^_]!WoI02e\'2|:-IC`0 H 86N~e6wz 1)0$RȔ޽[LyQsw[زkL k7U+N#+HXִ;Gfq)ٻ ӝM,˨-wBB#f2g;(#%  ɺ[Є"J8$:_W2 @DW!d A`@v111ܪP=DhvrPjNY ccdJSl^oR/F.ũl@IJcCoKxWg(%* @xŰS^%U jAlkTG&'P?)\xJUSۚkV"kjREjh+VmE/Z=T&Z," * 8A us"J<8!$wbuZ hR^nnw_>lr:Kw]Llk>[i^HfV#w;PnS\Dc+ՌgzVGL"6w=\aHuC&=a"&VsAyMHUaV@:% 4lA'XD޵ פ*?+lEQJ9ߛÇL|,^hRgשaٹ盟߽{o@.no._>s \_=͇AZ_lP!PJǙiJlBYA"4REH5ՃRfHՖgDANt̯T`ӔJ'>߆jZ<х"1kYr@IhB{/Yɖ*E x"RVՅD_68e_8(":R*^YK S24#g@u~{$ooc8S=UJR /+wαN82%><펵ssUx^Ǣs@Ɨ؟jR^̿@~8 D%_ i_΃rb]&NЕ b $@lgNQU`UUC ҕ(I9lEƩy u3Xx0YJm]Aь԰nT Oz1 Fslm"dRͫ/wo~ӳK#Z'NV/'oo$iAPOoud<#6I8%3uJgYQ'==+%q~](4;ȞN5wwH/]LSfҚ^KXpFxxי8 rͼR2]/|C D«++U"0N4IX0^HMMޗ ú˼ar<%|P&y|dͳys;Ǿ;oY"juB/)D1ݺ#*dNpH!f DJeKPtZzH9MթK@36QЇAɶ"«m)5US3ݮPM4T▬;}\ 3TD*Qk d KFzN2Bĩ\*%%e+lyey"$lSToԢGhm &:4K+=b *69 00'x _vObCS۷goҁ< <䋹([Ջy/|ݟ=!#9pLU>MMIؽ&Tـ| zN=dnX㫵yso5U鷥uh?" (Q*oLʻq[hޙ^惰u \%A~!^__w}<М cD:_z6> ~_ק'lXkz4@Y2{l.({ ]R/%jeQP 1TȌ5/3=ZEB-zpΉ!IY AlGNE^ Bd/Uz >Ε5Ec?Mqy$U? Fn%z-jZ<~3nT!h9SنQUNKi:ۋ rfՍTEB{lfR4+œ ~ꛓ(Q^yXof嶴ky^| ddH>: &'c`n{֙$@NaZ6cW5'9NB)JT`ՂXɴiZq*8$LUFC:UŶC:f0"91ÅBsB#NET6S{FnGvxIXR>v4sFM b"nnww8%!󨞘RUӋ*S^?rU`Tc5>Dg0qN!ԝlj򟞺(VYsEɏ ^XHm~&Q@QNjF'OHpQ',~F s $%:(6/nSk`~v/= @x._ܢ"|!d$.1ҥ|qM`.RQ`نtsz:z'&'yL˽\ou:0\BÎpkyei32)r((qh م2DP"u,+ha%./ PCJ5DPs1nUmZMՆң (R@uu{1j)&jM<(`ak+&T%(KW1JZ-R⋠V8~M%*&xRU*3 <4 Äi!6KW)Ptl" h^\; XD8!dEx%zn8 9kuSA @P*ok/DeD0Bk-: vWA bDN14nŕ+ۢ{A [ܴ;?.lMZ301z|W{3Z#kvs2^- fJv||>0nDcR\9 sxB~˟U c0 rd$ҕN)/P@2o! ֲq&й;!cvd8^9 suȹVљ m=3  n+`կ~wq~oܠ>}rxq"SQ;MO;[څ^ֻc>)@ymG[diQV[mH-̀Vs XKTY Xحp R$z)U!>O-kG]+(djd*ň,x-̈́14u YsӺ@{t\T2-rKR~b=;\^P0L*8@lO Dc}VWH-qUˆsJej1)8(0×LVZbEmչw3|!N6q>?6@ZY)S;&P"Jl ;L,ZNuSe)G/0rʼt4&*G <BGDfpa] \uW.Usf#Ɲf/|Wtu:~jxoi<e♎䋞+y %v]BNOOxtyuen}sOSy_NGG6|Î3OVo T&`yչ0Upqyy9XQ:U*玏`RS_rhHLA􏞉yS rw4U:rJ0hVǒr[Xs//Ot(7/_7nO_..|<9 ˣr!ՁFFcN %l^ E?|ҥA#N!\PDü]$Wu9?"7@TI%TmON*-T[m 6j8/tVȊM:@lJx8: F_P%" L睈pOke⢐  u")I?BF4.Luh5n?gra-Qhoޜw4[nu;75:n@lx;P)t%'."p:+(v8= ;&g7qH/v|Cjl߽w}%+" Q$'|x|w >dɜ;w;2dnK3wsss7?4d R 7A=Ut:Y='v[ԇ6,q ]OU<hNCQNv*k8;{ϟ~W߻8}rX $A(k%UWCYBo/x8"$3ӚCTmUb,h77|WR#M4iH0xeʲU@ZmFBܻ;HO%6:p(J.Dgq[UAMaj,B1l{`C@ƺ "=N ;h,:RZ[bOP=~ͽ )g-Cft[Xstު/@5[) tK{Qe?'e\bjs |EW3yvm{v(tsBWjG݈ 'wsI<e]u.{Q6xwEɨǠdB  A)pQ<6TEDa+19m9U&|jluCIl mNd:將"""Lv/nnn//7Bቼx?g& Gr|c KTq+.bN'& Bd漈haqrSϒyM!Nޑg='ygphFZVZM{50ڀ ^[c:ʬ4\hq'u< 8ЬLb0!%Ò'NyFmanS0k#-i^IGt ëVƀ `>Tsq !Hx `?;?~{ ,M21Ȋ,טE0(H^9Sոow8ۖA'y p~?>Y;߽oγq1rr:ۗ{FNޝ<{sՇ厏݆nb27NOOO^0.)/Iy0dk{̛UUYA{;K2B! k(vQv⒓QєjaмXIzvI21F;g7Nμ~~ZII~1"7#a V9/kTTTpPAHjq&i69G8s2x4MxŇXt օ_'yMQ8hV93W'pBR"`^AVv:eXFEuArqK_R_B (ЎKc UҲPq!?xatx&47_,MzISw7ã|!UjCyCQ|49Tk(||^VoNOu,}gւx#Jc՗n&Dt5Gսۛϟ? 7<@cNk61Ӧ@|[eyKteH7f0lG:^t}30x qGÜrJ2 1%tʼn ]/o4Offye8a" N<1fɐ R _567ӣ7'Ww|ޞ/G{.X'Gvýݗݝ(tc4w7oNPN۳`,:?;9UH^ehܧ[y@On۳gПoniO5/uwbcOc"l0ZF=Rn˖0,rd6/[/w74qJf }"43R󇘡)pP Ws*>dSTXɐ%M3Cdl^H4N^S4??-^Gw`$eK&Ց[LbPHMy' ,) xWFYtGbzz ćm^*l# jHK9L(9pڢp RFĂaɯ?}g\D o:0h^:(~Ï?M= m%sBIL6@> NM=m4kkJF@l2Utx  0/)oSWQP(,4REJ. TP*V(ha2W_Ph+H6 3g4W^CTĶ d1DpJZ% 2m_I&={U^9ȼ1rcȫr :1[.L̤LwWt$sL6J>9?qŸDJ֮CtYM/0ZB^'ݞA" 涷dwO}/ްד%;IduɛD'?;[.$Vysjup||sgyM ONW_C'l)t:;[4 r:H^v^M]jjut(lwwׇ{{?潻éҞgꘞ]75Xvvr7oOzǗs3 u yzJ ʄ I^rY,2\ Q66-,N.l36rNy1C,1i 3&1Z]W5oP 'Uo㘋dn\30Xʪ*E)q[ϔ Śs@4>e&?]15 Ћ ."e^U&,pUg@e0dwWV-ri/?dg"D%hY8>4o_; /KNWGq0λnrʇ <1 W|KĻo|$Nwb">59#] -SKWf&5!^ ֮,(ft>Ǻrx@󪔰>&y\[ҥKY2L6OJ=͏N>ecs)jE'eR=Ϯ۩Hg-4Y)7> UUl '\E(]71TXJ=-%o  mVKq3?a|NXUz'4ZYU*K=Bh$=@-zZNRR IɖJoY6e8ik+"O6>iLOq@wDl&Psr4g6 Ѿ̓Jaq8:79i0Ͽs2Nq s 9Aw/T.Z>=20/ɶYzW.빴Bf34 Bf2Bz'DpV\o&\!̎cqe~>i4(+fF遌1BhǠ7.m6;j^YrY:ZUU}^w{6ӻՠdsKr]ghyTN9]} U]vnJ{I2. |hX(2An1??=t~z|{K[pxQ9.~c*IV.u|tLWyOrduʟvy|v|v.?򒩗源+%l3I5Ôɨ!NAh D0C2R0W3[3+2f"լ"V?Jbz&|Xge"T-UMe*fq`B^A^x!Yr%zoZ%QT,ݹ z3lyщ_jf!5aNe5_"YWɟSIqhޖ'^S@ %U6K@TӪ9덒.fWpѵg^٪ "Z8!XG< Еr0fU8`g\m+R7(KZ?KY QDT%9&JE9dAaS J4EjjyJ'WP€8$$zq"(*ШdY9I$},+*D4͔u:l;mx[M\:OD2WYUܭW&& AAW;4E)ǂCSs ʜ ã[v[Vf9[su2x`%G9} `4$gM٬](&[WMKi=ˏ?7*OV?!O5=%/EZy@&SN\/uػg.Rqyo?w* ,WŞ?߾=w%:+![7O$G|M䒣ɞ+qghEx<8;=rd^"+y!s;s+fvlYyy/ˌYrݕ[ׇ}b0O8ߞY/(F>juu{gd?t{J~*2UKV d&$#+ jf r^(Wv~1娗 'BJhB̅dVrb=43_ԓ~|.1TmF>gm(Q2e/84@6 (%rO%Jg lHa5?Ljz)88X[Hq`O q^^^긼Q5m$u/Ezeҋ\^j+c7B M!?*RՄ7#[^\Qds{ykFϧgN_..TO,y%5Lo j !yLwO /h!/֨gCў˘1NI6IPyI3`ws;^vD  ë˻[7|7D ,iT$8yhE0^v*gBdၟD :6+3w͇GF⻾j؈LMK(sb`{[?[Wf;L:_xH\%wGqT4Ь")UάԄ˿eU^aBV%6Vf ""rV)= @RuɉsΑP+Y"d68 hbiҝMl8KQ)Bh/ \J*E V̅93l!|0!J|[԰:8/_>\큀NV$-xX^'&u {\V5m&5)Bz-@|>_HjE)kE~ýg7N3rgE/YF}e\ZE`r6]+XrVyHHUqeY󞝪k1>]ƪUma8JAG] ]E!?5ysf(tt噼 uU)$Ci clͩۺZ1h Zմ($!r@ _3 R leVj .t*+tl_u邲ȁyhE37ںe ?ED%Jj(:]UDTFkbM,؁ߓu9 :mN+MZy+ NM0g|t1"c7+x ֓ێOzfoܝ\O/&׿ 1oIY5KJS-ǀyB Sh&Vc} w0ĂT|aL95!.w.yb}Xdi#~6i`8ydl̩ڄwJZ 07:7uk[@3:'z>+"=nYzAE*0`\JlV4)Ka]͈ىg%#0&}x6 2 Q@9K.X%(Y0(mԡpsI*C`ѪmJymRCh jY,%̾l&X~@31Fpov ;fTe<~̯D<_K>m#~9>>>Ͼ_$KOʢ? s3꓁m /|D}sp7Q(t qرlC*vDJGKsօP@b%2q̩w v30NIj97W(eo/W/Q<~pt||}::>1@?=Y^/|_v5ӭ;+N|%31:}sj94i\VMXD[z+09 Oɡ+<u: BܠPfNNR\&(|f.9 tFp51, Z6֤٤3,yd IP\Hyh.Jxܹ7^ ͇DNy1M'qJM:ȢαQüS$By9(HD'.`/]0!,Kq#__0"5K]#L)֞ QNxV&эhS2"5IlzD̈Uq P!sg0j1P.Rkvm/e狏.Y]܉Unޝɯ~Ͼ\f&Y4a%WӁ̷ Cz ]b3,*KsӋ N8ӭ'8ϛ۶9P湀M~deR~lݝakV$Sv>\^=\:/.o== Ý?Q;i^* <6q-:>96O߿99>}y̷H9{=@ %yRc0*%z3:<8, +,('Ve u^JB3*+yu+ctt<|OT#ݪ! ]XY?JڔU)ZKъ2JhXjjLڙ Fi 3dQRPUR8ޥG^=eZUR"0)m5W=4"P'j_FQ0]68:# *ϘWՄ,Gdy2: ՌgH,f_BY+թZUmsSU?c?~+<MbY"ΒVpZ>n''o|dz|yxL[Xqn)[ff9'@esptY\.1[huHC~$~)WǫF C8kp,,%wCSq0oNކdRdh[Oe[ռ}{sjA G΁1ߡ7ͻ{ Q;JáL5h?/QLȾN[V˜dW C'HT 8Q+1,d@aTQ(9x=F՜D_mr1)i%]Aqsiep4OWW7f~0:&m{ROVOޞXnnC.!Q^˛$~R)bW@]-[םe0L*}8In)$ɁD/=sht(m3L"qEv:.TTv?L!͛^_cB`)yhIyIN)DGgw ߾/~s}s[A1{:=>=sy:\?繤4G+޾P޶k5${pa2G+WoOwX6ĝt<3 qu$oY2o eyW2Ig'iRCuMBҡ*fiR FM/գ\- zu&=͠lfzqR=:% .0Ѐd1Ɖ1Rڋatu[-煦y޳[ v3볽5t*얲o>@MK No^r'0NXB-yXNFy5JO2 rM,N q8hG r3tF#*rK2}tGob֟ʚeߙLff(52 x oŸ/0܆@-VS"ETq(UYUy3Ǚ=EʛDDt fU&rnk{s9;(誚^]G/Y̕KZZ#R sx̫iՖkwboV0jd?G(O~׿e isv&Oy pW_/?W.Nw\u 5kQ&:ΐ3 y *%ӁKo]7!pկʄd$QXBgtN hr/ ߀/<2yc dz:+_D+3yTU|}ЎB5voIGpO'XC>ք|#O~8+25ϳS3]8>oo8O&\Ge)!#?fx~<;'?fmB';LQ $%dW&b3Yh߬J s1qT/]EU)Y2Ti+X,<45BCZj nrU؁L;unW@DYETkB}ĢRD @sDx9ZAlQ**\|կ 4touOx-bT o}u` *K/'U3D(&fR(BP["k6UaMU^Ug u/85d}MX@6'??,yW"uF 6)G:!V m**U *V'b[5ҫy%(ZFt/VTy xhRf%ig(Iř5$>?i,s[ksr|Bҥa=K "i3lr)*5|+5rhO{phS^zzGш/Q+dMdew2B™f}rw{{csbV26%b7+-ByL]5yG8yZ=m7ǯiPݹSlZWI DT[m+7չP#~ * ,4Gts t•wl0'wbq%HH\DF JPፓHO VIeUSVqBVm5D6F-fM~xxΎqv?Cde:<~7//>~J"pJ2Hއw*y[`\3pL 66>|~~=]^f|!o,Lf?&2d@%76X̟|7}0˖LڍF(3B2rlO{К9Vҧ ʜ29SEѧ/_~չ.(Ż }q|#]A| =cko~c&yU\vb:^2U lq>8|) 5ǥY /hҗoEJ^™&;"iuhX$Q=Uy c^8џd*@"/m,sة2o";ۇY/hDc-Vf[ӡqPu :)TnGefH>Z7琼|2S|<"Nѽ5f I 3& (<Ϸ<_4H6 kNVB; 6srkSyzq{4+MY(ӊfδQAٌ ֗]ϲ}oIM+ORFB9x&ZH}ה;><0[K:Is2N$KA*76WFi"&Qn r#>Щݜ=nZ~Fd칝3ql:gPMG k $W O-ΛQ0_97Wq42@>R$gA`B7O^?~??e?ω˻˓ӧӼwVLZN~8wc=w'RL0U<闣qN<;;9J4͌t/N~uG :>&2>O:mnuTЊ^)l vǷY"ϟv_3?O$O:le $_3aBƊtIh~)!a(R"=\% ) QePZkS)"}H23y* GgfH9QHY<Ɠ|k%إx )SFfIcŏGoXE3SX^^L4z 21q'p!28*8b"c\̥9;m?A$|Y_ <3x`^#نhWAn /wmS*RLwǧy)q>|~~f!iۊ$dg5΋qur~vr9zwљ2,B+o?.&炵p10煳GQ! qF'7&8D5)6:In4KiV(Bz⌉PefOg T{J+U6V"9x mjYdʻť/fƬ90Ј +[(VCXG?"DI+Fۊu̥+TRCMm]yHۤZ*^C+5+^Y&Zzjz `#S><l^zqrxP6F!X~Pڒ+qM+JW4*U}jqh]EêZiCPINxPdک5iN<\'`. YC'GVj(Z7GYK YM|v0KweP1!цs L,ƴ}0n[=Փ紓c+i>daݜ99fٳOO޿;_Hn~'|4dRRDtr+2;'|(!Gs:x/OyɆy }f-^%Be֛b4 6M{ƁhKJC'|6V+?ۺ7}lP;DacJx:(CL}$y}N.XJæQUEH6la[yKZ!A('DdP3JsM*@YҀ/}D}o!*Tݻwz(5ɊV 5}LyQ:2GC̐ "6Ho2Z*[@3|[:W('nMLu9̉s)z?BQf䬡Î٬֟ӓ|e~\!#21cMJ%݄4U$f)j1@–q фO|IES!LݕxR;C8E`l➲K6oXP=OOc^r?>]$ʖ"ӛc!-|ͪp0]8aj!|:1[}<ݙMc㓺W"2| f6C1w,4f\0=槴f?A%vU^I B1DƱ$bzJZrؠsjcE%6ؚ*e^̛%VmH^m)p8kQc 2E4w_b\5;T,뜋+" #GP''zTIYHNB28 A R /\t[(U% NAY!(Ϫ9>|QGV K/?bVW΅vl"y)I%*q 3XLy59^vp3\7yw>px!uN~fcUVXpz- (4"X%3C^N(9i?g8[+FX9fYi3/J 5!Φv‘Έۂ8_M(d5

?*b ھ"&f޳P>b32(\??e =Mր,s3WFm{7NKgB˘rmgj)\2Ndv gԥ' oe*qLiC+E3Vy5کJCI j jA$BB2n'4 La%4JnB4ц9BfWOa (90)oh h^)[x[(AyTpOI[G#6qHM;Y9>ʮClPd'%<\R`%,v5pRe'm*9KȒohrOAUsRjGE:j+@+)(I!V<>J45Ƶ$G]TNq uZ;+eˣs%e@O$W\<†. ,/ @C>51v 1TtAK폏sH>%7oo8_+3su^(兪B..θ0?;~(ugWaڔQfoQ 7xK4pM(J]\A_=儌,zƥVdqtJd~d#(m8h4$Hkro n/*㌿YvMQ% UՉ!*( *O9Zz8OXo T))CòV6>(dM3g4v}*Q8]UIPS/VЈ*eNQ/̂)9K^ ܣUkMx9zђl|hS*VLD^p:|ۛˏwO۬⫯-=v`3u>Y`~w'JNNOO,/N6 ]-.)gd&{ɓ5J$HKP:5zr+",#gZ;Rcj*^zR3Dž=9ՠ [0(+Zbþtt خ :V CQΪVu%6j.,$PW!ry]*8 NuU䳪&F6񽚣nOtsUbo Bp"u[hX5Aa %=!VUE+7Q: ]jΗoEQp*)U|VYUHq :\-byz҉˫]Z" @YUj,z)Jz9@DSʀU'@6igncV!X/SHqfC|3!y;ي"e8%/.[ R &B\\^q8/%;;<6tEkq0OƑpاJE ͛-3 M,{d.?o> D]^]u>_?ܣV!X+b6@v'"i 4Ba6IDATۅꁐmBkTy+ZmTZɛ0 hh& kO *2˼ M8/HVQB< B|a1jNHb%J_^C*WC^)n5T?J >s2ozK#\Q81cߡgj:՜\\oIˋӋOyF"SfuxuΖ>"_( "$4Q *4JapG>??7fnY1DF3%5D N.+>BPsf06&P=U7>qv̙'+R9b7I*[zlEڪ^ծ D6F :!" kؽ ](@mE,(KxT:\uCuB&e}nGjL5G*Q45E x=bϋkp3LQJoY7%"ѶEa]hʼIʱ|*1ql&7To]Ph[&}Gp&}6GC%/;8#5y49Ȳ`FXJgyC:$ C|d'S3˺^uB9?ΠWk̓Lӣq;Ɍ-TI~v- k (K:9@&+sYvq`Z]*Ɏ)Tn$5~hS*n t8lSq'sMUSCTν] QC3-SjFD? T:ݕ:n/Jd<8)d ˝ tGjeU"-m*Cy+"+^》AAKMIIPmOAIm[jUNKJl9H9k*GǢ^fEv!f"qz)mE+Jc!&ʰTU=|+,"PouG_bt%*[mo+!H `~{Tnr6og ~}>·2c6#Ꞑ'1rC7Ѓ^ 45p%)"Q9!\%TelVɇXҋ%ĸh%''<Ȥ@V1O[y;Ѡ5ebɐ)cˁyӉC/q`7,q̐ʂ(:DMvVlwOJ,1FI9iɟ:V3voa3(Q5#:iaFG+ƫ?Q[ 鎤yJf@ Q=.VZ.@D f,w0gz1%NI4V?-n`(2j|BYNxPBTVO [6Ar6P%f ԽWS4yy$<7?i 3mN6GNyY=؉`9>C93f2S&7%ʞ1+]U@a{UبAwU)bNI")^cTgTUdʬ?@gA89:d~]+)bKgc˒G|'ΘѼY[O|O%此ÓYCFRi>%x:qýorCΖ?jLJeDkH<3__197YGMf:QqYnJ3~IO$5<dT6vûJ)YS*~Iy}4Ry>D䫟ɼxp|d](R֍{9 v͢HB$y7yhJlj4g5ECX GnشZ\>5`v*:Nov+qTa!'j%aHIt: l>乑-sktPu$ytϏUy汛ponИ 5#'AQsHBP,/DNN6IB܈њE%}cg hO"ХZr5rX - A'+D~#Łj9M\4/R,cZ|öTy"%h[ iz2+ +yr5|P:Xu:qBq=lz7+ޠX3YD "R \"V%ʮR+PN i< G\\LRgTJt ΟfV͙ˊ$l7߷V@$<M^ :&& 8'7p',g(Ljy3E=+YO]  z$=Υ@y-yȭ ٩uʝBN_y)s)6AbɚK H6t7.ow2ŴHetD-!gG7gC[ "<"`_> h^RNdRNdt ŧevWV'g0Bo38IMDrYaOI恐 ӳ3'~5UtAW~z36;ǭqo.М4Is>d7?E7o]A8~ !e)!㗔N.BeX+zr4#A٪dӷA[QV6@2;2 DD,ъJ:> ~ix tnܻC(SPjTID|\~o%"(T bn %tգ L4 Z_A:N*vChG,! %wbJ_YeA8)2TIY=JJ !ţkS.ZC)'XP=(MR#2*lʼzRzTJ6 ֖C~kRu^m)P%V9 78Ds!(e`RJ1pqZ"(;J,jG=>Bkojy$gy<~ |_\Yw?XӬ5(\VG&MLuB:˨$8 c_y`ŀpq3VUҺ<\)c n\2'N ժOO7X@;֟>2m i-[?]! 7777׹|ϋV|P@&OC^=H}VD8욂|</2E[_V+4q՘7@mwAf[IDiVۑ ?3>{wAV9t$MWq£qdz0О4 q&o7#Lq^aHVZeY >>ͬIUkэq&f!l9WD N~7?OZWA&  pqQE?עhʪt%Ъa@XJkHjWXD _s:AFb65G!SUJ_=- NeNC+突omPx)m*0h]8#G} zDDcċ nMQ˿w]eyt{wwNKfױmu 1ma(UnE )nnvn^/wV|r)ĚC^(0 !d{:1\gHY0mdE';.l/SN=ѐ,9Cv|Ke!N9|ӶXDzzzr>9Mjt[E6L 1up/ۻ1m6g¬־̋rq6ƈϟ?~F:?}-^݄mךd~TʃβƸ<.#8&$°sSNĖwGsҀB FbRWgg7&O4]CQMftqY#rst4n7_fO\9h|d$6p]ϵv"]5ZG4AЈ$V 29)4 U +_6f$EaB9 43Iū~r|tsz5o uO'>gSsb^]zcO-ݑ$LXON 92?2 ۪Ԫ]FDjuav/Og`∕χPV͈v($Q4sa'MԬϥ+bs|y|޻ۺ<\}RԬpI5w6);lYA0'M[63tn݅onFYf*bݿzI8֢ktqq>˃T&4l]}~\E'y% *?' .),XQ'6+HP>  1!肚Nw3̗U,Z+@/J27V^b4=l6y8o[0WP8 KU.*+Zo&Dw_NOO/A<>B_Xy[TL%R$s@K ?uK/ ǣ!ϯ x]fQC,îdssK25滳/KotulllIB~TG:ir`GQ,IW$ x'7_V!b~eɛޖ,ee$f8L`em)QU(`cL ?/CRU efL5t>9Z V!H+Cm[L CH  #,e=7['3ĩyjZ)%QD7dMϰղ0W2#Ϛa*·lNx)9SpDOS9DZmsp1CK u Qˀ *Ga%i|x5&lѣ\"6%aU%FTӑ#U:^u@m( 2`?}o7ps@ˇS9Σ_}]6?^]2%닃|==:5}ms.bshm,I'3gZ! ^#">yKP)kR)occ.2UR+-@<=}V-fX/|8{(u졍;UjTs=u|hͨI,J 5(鬟B<;5myrQQDSDP& $8˥SST3+3d&+!bqH{h(ELF.ѸN[6E`'2Pv94ə_1o c*F)c.B'jౘe=)_<".@[\IdcሀD1HrBu)>Pr[(?PzBJ$JQԥRj3X:e vG6UYEr%*QVՇˬ ֠0'9BoʜǴϿō\S) PQyB'1/xxp}NeLED' cؿ vsf]6;X8Ln,_ϋuLg?kÑJ댧y+[9$e!h]sùfS%w~Oƽ1f[:byl8 _\c?;?;[Rix~ݿ׹Tls>Lf^hx$Oۿ-}7W6fؼ/8Yj7 R O x44R8A29#Eό *VD)CUͪ&DM4*`BKl{d<Ü弢$ 'ycί6E7l2N5X]kD&$%LǼB5K~uR=vkL$T2{qKd<3H0T]*Ue9jZ6 u"9E]H`w)0Rˇpt5H.6xI C(Q9McŖ1m_t_~^oc <|յd9}e N7N _\\!J7(y|$!B@cC,*ʫMYU-bXewy 3\!W2)uu?tʺ:֦'j\-::#Ňዖ]3li=^ fP 0_u`MZFȩޜ7/c#hU]x5, 4ʏ7Ư1V¤0n4_3`Gz8wB';('5S 4'&:ᢑ`rQal&8kk @ͧ&<a QA%r3 0 `("!Rݯ; 4R i{``$U:ʂ#BJ"'Hl,X"_u^.7c S"M/x5>_wˋs&-竛?ea}zV2x|*޿um3ULɒ~|ٷ;cVP ɩ6N?έ,q6gMe1w+/JMmJzʯl%޽ Ocs89;.ay #܇GeUb}Rml),V=dxw{{h;'%!8)`6q奜{w7OyΘ\4ڨUI*gje=a'?_먤%߳9x\ eNbHho8v?׍xn_'| ?h3SMGb>1lcƳΫn7;V7CĕODI2|\lBIt<ކR3kl6qy d>IidU 4MU* {i>J< g[">06C,'xb\7:AS3Eb p4g,9$UeSUOx%(!z3pdj0PeB \ 83[C:~ xM YqOx%dF*q xRk+~GW-xP~&8 #q Cy}sd! /.VF!A[Ck->崚{p&{o?o~WWw;_Ug)O\0Y^ܝ~qv|d1M.һsjAȖV%{%A9 \8V%ƾʐ yXئWU&U Ei,+Q (^D('KGc:YBT\ʫPzbxas)(@[7QhrjゃQXV~鸌ϝqǎy틱~\IJÀ:vsGAdU6\$!WGkg.cn`T ql V:lʗlsr:V (2MT5]Ŋ@0J )'ZY9N.IDFPBEJʌ?P2`('ӋZR7:Vj'ì[ /?UQbM@ae y 1눅kƴc8qqq&a 熳@cfdPŢ5̏WfP(im*f4z>Ǣ2E9lE>Ki(Y%e[NކudwuuV`)pDwݻw-w׼6PKƍK)J̒%. ƒT$\f1PmfsM̀`\3$0?)˻76Q󋤗KS3:0!d `qn(K:AwG!øܫ[+q'nHFySl6cYǓdbR\ (Qb;bs,-s T# >_) eԨA)'G)t"<-ASa;Ndʀ`fr~S~nȪҌA4A& Йbl!lpeatϣi\Ds$r ziswr'Y}@k!ϯNى%rqeGUOA98O3d/S ߯ʱlSҔa OYO#U4ә .4-H2y OQ)r;>ݷ?O[ AXI.%|2lGYu=A13rtp$'s []ec{ua'jWztkUGy<;ڳ3H&L?m'W2dZHUQ1Jz Әܭ>mȣq^.j2;-ke>MV̝nN5:N(4|<N\ܭ 9tݘ*kmBrA%wb -^TM u+]"Ğ70gɨD]u#[X`B&hcdZKd K(}%U"F.1/`hd hCDSU칫$V^x#Zw ⣎N!nD 0W#$ΠG$Vs~R ٹhW҄a}jkfFUH}=gK5Rq)*'R)ٹڄ\B$(::qXUxU3U;ۏyç_7x`Ҙ3GG'KI6LJ;kǗGc^ܛNָ.)[)ՃrssWW_K'TȚ <˦R*"v>"x0GkTT[iqR+-;ZI7x1Γ7Pg"E8|x޿y}ͅj¿ .dvTJMʉ*( <$tzl#bkBį``HmbER<۲gSUvE1U.yjѶs5D;PNO(IQA)C#rG!#{vц/Hzgt ƽ63)K+C J UXTYkSEZlpYj`!!HhHyB(1*41(_og6ڭpj;OD'EVxͩ2Qlk^R|dˌ T۸Q~* j2NMRWWdZXV(t{jE#i"̓E5R68DuaFn"R FxKgU\ =aydq춟5)ƬW_]?;M'7'|n6}}os'guPxA<ZŢ;l Ero0(zy2ޝC89ީ0$8 Ye(Ta-}iVZGQ%R 6h)S ] đ[ 4L]l<`&EQ-.JT?<8T.svzQ<楄=WQ,/Oy|) f$` *hzgO!FEJ8}-:L׼yQХ(9q*YH?&@Pc$&<íZeG\ nd y{>t}&/Q"Ҏ/rY˲ H.GR`3!Mf'0# H^J@#134p)^MC))U}\Ře OZ@ M̌d$Wh:Jd2<^Fs3 Kq^(碰,S$KnD0K5Ɍ `A _*2@X3]g, C3OdYWUB5X3&hMd)/UW rJi-U%0TkaA0m Eʠ̬wi`{woçۧ}gjul\|wggצ#B|Z2?nXn @7_dm}NU6K%W&jR;/e-ٲl9j0]+t b Eoq;z?YgXGj)K/ȒjYz[jop0!^E)?g&1*@jee &M  ֈu ;KC_V9MT6"t[98h'̣|R:DlcV]0S lD+qUV̈T!V`+F6tVjPDiBeknTfU,|2)]"j镧Mjk8"8J5Zײʣj۱A2ftt0e((ԥu8W@0&|pAJ/Μh{B,jaK{Ys:b鑇WC#r| 1HPc0_5vy1JVOCǜ8!Q5 Ic[3PiL2f~t {y!63h[ym6m^ 8MJkNĎ7(98*znvM>0'vb{ϯG{7׷Ϸ7onٝ\¶oXb[EOU pH[;vT5ՆG",v&&Ɂ"(pAlNl4Ǚӻޟ_ʰSOC24gbIJ//4XS>Uw 0gՕ4C:i pN3$&|wyv5i!?ͳ.yc̥E7L/̬ڿtz6Onۧ|OAdތ1EY 9_31בLWg%ߠ̛fࡀDI1=at[*1)VR(q/L ԊskE( BE!QµQA|t#jAJ*NІA^0q)121scE"+&U% uUM"Ze3`="5g6$*ɜr%*lIN=";O.I@D-gfDNx1PAX:}45P-1>< $ЌSbS 7ۅ𻻻:VDApMxs '#4nFW: KQFv mDIf@< fe7WJ|YMhg)i?ש|}7Oo~7ۇǣͻ/{g'v\*۪ZL.N~|+7&~&V|_NpGIg't"+"!/0 9: ڧn2m%(CjѾJ͉Ywc@yrG&F:,Psx ESqRf6pȫzڃcuhӔ%i ,:`^#ϒtو }px}2 Ĺ~Z}F _./ɽ%ᐠO\m]lڷWy1ͻ|32 Ou7Juɻ &)95fsy3./xs:~|x0tVI~ dE'n k[J3\7i\k櫥Rs95>mݚr1N\澝.t-?YHhyVtOni!gS<<.Pd>\TCXivΠmk$>t5RzzjR-}( &;FO7.TƬ2/-?PV"jgWxQ8b+ T4e86cUFm& #>=@BaE2'HoNAnL<W2ڣ3EI̅eeg%J%XdatzD*6 'Sb HW Ux;PX)nT}R}>^G^}sqwreb'PbHdyFcq8?w}lQ?{29 ਲmer(%lXJ2MDZ%EvʤIT++d.ps[+g$C:Zk|-GC%΅P &@)PbR +gVR ǔ_4V-Ș4#@1%܎g'%dP<;uM+숳UN6%YdTLkϿC떗&< g ]*3peFL4UU\ nJDTT(:vSfxβiRoiʂ:(J AM,Z`PFx`mzQFc! dBpY)Tu sqAjU<&kjA iR\'%M~uH_'Cv6:ɠ,2d+ݛϟ?:(aɍ >/:efΩ">;+!:Znj2ZrQ}}bQĿyc1y SAK /hۧۛ{ٰVX^{w z2s8yp?"uilj;aMgU. " yM8ço~/~ۇ۫97y>p]WI+D 2=ښ*JBB DbMClKU2h34YPиlQ?K~ɩhʇ?^_][J 7%z.dfHi%yʜ\3:24ߩCVd|ȗM`,}@d)+K'O\]Y^^NO77D/˷ǥ FMZI-)n{$RgfZ@%9ip3D'/7Ǜ<ڗ˿D鈃C3r[׸hbG'i%Cf/QJc3 ִ̀Q0_ )]hL5]=:'jTY6G1J3;0Ŀ F1@K%1\Ė=?rl%y>{@g-Ƀa=؞_߳А?2uCVbw k_I2) ff'\ꌘV=n~z :hyO6W̔@# cED:'X.kyKma54,ˤXie~烔V=G|(Y'#/3${E!H&tSiQIvM&YJi,Q50xg=L1{_3=l?<|ׇ'_>pw#6 `2[xaI?'O fsOMK`vh5dFda# ͸ r޶mi2KO:[pe  x#Adct%Rbg3ެ+ U9f<)ϪF2( PŜ@Qq,SGðK z6K}uΌ D(>v: mjx\mV(EղҬJodXBC]pqxL'E iP< QƻYBtQQ-.|t4UpFc;><@I ݘh,Vej|ciUOX\=UM+R%Úتfq8GY'H6ey mZB"⠜"2/WdVt" /!stϋ[Q DB>l '._}4]bt'Y6$DsND iҔ捠eTuRsvvֳ:*\)a7Lm(8=M#ٙ %ƈɞ1| (HX2ҧtSh9H෯G~7~w`UarK~OgQs upnXySBg+R,Dj!x./(<jqG]t#iz~]ο>磍Փ<_q~Om;={|*B^ʡX7޺ }7׃P|4iSH y~?qf $U5 "+ 4N.!5_8:PfFpx΍o!]]?6[DʱQXRVAʼn(Ɋee(mg|WŦ(fܤ1K̈́]S>Wyz" (iֹ[;V^+8l";c9Y[VvUe\ K*QE2,GԽX9>gDM_r ;04R"tUl] 't\Tl#FM@}"5 )2x8)-[yʜד`s!o2)RSS>2rRcUX8)tH:HѣUxtAL"#;,mS?:ݜ?<=c˒T} el|tss_?;Gf"v/؀w(Kt"IGhED@.^www(M 4 w yĥ(剋xΓM^#N!1+ 86xP֞|ksL x1dH@Q AIрBVy|yts#O[קf"7C<th>0cK~c I'0='I㉻Kt w̪vC%[. WqZImr4M^Lˆ1w~vnԽB d! 6Շ|ΊҁMHw[L(7qDKs$N0&ӷ7QAPmBI*~ h Gv.ZJKx ZZg1Ylm(CgZ=,BIHO.Zl%jh&2C߼#1hdϹѿ^ qC$gDCx0_ ,wB Z+bG!]xؒrp7I5^G\DBd]nb&t85%c${V&l/`3d %IWҖwʗ`y|^ ??( R6%jnU T'RhB)xc"3:Ifєź7$OVOS:zh/̤PI8ϋ [%A"Ҩ$R}nL3(Wjt3;rVOXwΏT_ A @o+N1Ls"ZMpP:b%ktJ 98.BG9+<"]uPՒQ bTJ<(FO@'NxE*m 7vΤqA.0/Q~ZC(DR G7)l<3Հ7r[mßw&r@zq2ݽcA>od.p.ְNfֱӴTe hZO>AЩ]!!:iNcb_;C9Rz+o jyur {ϯGG̺M-gv;.@%8u4ry_C>Tnh &U'&KC gW#%$ʠyH ޽b|!">˰5: 0 mU$K'ϯ{_}"9w:<7$-)_-~~Ӂe5I>  S]s1&Oo26cjf˷hClz0,9$m...ɖYvey*/#'lɘ?E~i xH[V:bnk'8TORyptX3YaQL4?3?+QyWXZ%}YlJP1ꓬk%PBh*Q+eyAXU͕mY6-[E" #b: JT!>IaaQhM]9·K< 4۱E`OJZNDU%U*DI4FӲ AoTUD@}hbE+IQ2ڴ#YO:4oP)z۟}]Ov(g /Z'Pp2<1ܗA+:ιX?U>WGVW jD,xa\Cq9=xQp)ղ'&A持x/8ӏUec1_J*ü/:hTEp?qzp/sMڹws+=qS-1\fTޯwI}2acV.p5D %?NqaXs o0}xs}'_^^:=~$/pc~=)pIڼ.M?qv^KvkFuSԽ0w~!NI`dUY͙8('"yD͋P?ū+s!uN X؜C4"gK[&E&4T!b"C(ӴK<}0w%$ {~׶OϷww7yC`"C<<<̉zXzD<R8 Rs@i*9/8zqBNCuV{A m %Bjړy i([)eQ7&j]t/h];sxD73JQhC(UEYbW<gLCh uhݮd;r:+أh3MʌsV1eW28 rVAZ㩙=Aa(rbSw4Uv/?<&wtPC[ ^mU]+˦ED# r vQ4fV*bJWjBLw ):撆Qű.=\}9ZFO1Vϟ w`vEU%#66j@yD4" 8ऻPE%F:|ü jɳ8y>~($nϨLt_f`'b1r&[я~_ootO?χpDX^lĺ3y}EE TZF KJw0d:<8x$|?~Nd/uKѽSniR!GȡMӧdK&gcsT'EGD޽aaOM(jiIE 9Yx*X P_XIڕ Q5"-qj2+7&h*Q @WSJ(xhY=8A_Yq~ RԪA@y4!Sʠ)\:((O Ml^ YbYylg'`dg]o@AJǁ<Ȅ*Y r*w fx75U\Aʯ5Fs9H6-PmmQ"ʲt7| VD}^#S(l]qsI~"]ZQ ^jlJM$FB+V*RZjUy<-NRоe3}:6Ve(Rf)M a^N٨%Єbu 4wHqaf-쬣cƣJp%jUW6<PAPYDUj *)}WXWP%@(J(XȪ$]S˳Jp"JP6ri, a:❜sEw*t}!f`<+V VKtAaX_l$Z}~r`{+ǀZ\-R_OUՋ:sVteǁ|gs:{1yޠ\Znn豝 V)b %ҿ曇7 wV{> ߄":c}s~|Gy̅~ 6AXFE.zM2{qq.d+~iKl䙿˔Ƙ&۽;=9o&yJ")d'U:.Aʆ^eZG*ppt"VMWeU yl-*6dqQ7VUp"uEjk:gUd 6ZZUU&Nt\;w E[OŭJ}-^ZVTj)A9A[[]5@7j٪ivZU3ɶ oy wElQc4Lڛ˸jjRX,GUR k@U R~F5+Ҧ,hNZ8JipZbr5)J;X1T*MklA,AhZҞ&"U[ XXBH%r6&H,'Iڪ| ٙ`eSF7ƜS&2<noAy&8G^% jguQv|oeY|C^&9ٜ `(pܺKk Ĺh" a qZFkYegH59aO5u ؃'➳{~|;ͯ>|~wNCqk+ELb> ZN>>\03ƅh羱~~=@my~W[nhu`L4A9SP0D}k߿W5ui/ϟ?|%oDJz>}H4"7Ɏa>7G98˰,vĶ)Huiй7ku\q]rz^3r;PÙq8(t!uU%?o:䭲Rgqo^=oקiTy&d q֚%[a8 kf9%V[eCX uf%b+T!~'rOϊ`H#bti|]:قf j-W-ssD /jB*)Za+`-Heĩ T3Ξ13^UDޭ&\%9˶68C+cTMk sMIj-Mh| m'Yp-DU2kӪBJlSu7:e+%sqï9v;>[޳]嵇.Nyv0W箿| nAUTTdleISM: ONOMhb}F|2QOcBL/^]m4Kî+W'|;YbGZ%t^-X"0\f2BwiAၱ(rp_o?=>:|ٜ|/\^_|B&{#k^/]d-qʈēa H,يX<+,F&?r"7'?9{\[͓6"[Os%}(^yظJ,r,[UxtAƏY2C <7K^PZY1EnN&]luTW7 ĊVxKKM,V[=X 28=<Ϙs4܌22G60׳6m-*AF7.boG'lgD'ZQooQ S5j啴*!疴tqy`4$h#҃{ͶHUaN*>fh%Ll6ZXRj2ijOs^"gr [v0aS+C3T~8ϴ\MITOūFヵR_Лm/ DaՌv5jICZx#N #Ah3__RRT(q1nuM,c"ez4F{]w)58*2Pd(Ha^;`G_Vjٍ^>S//03m)IńyH|lQksKWԶlˇw??ן~R7__?+bw{O[TI56\3EEԞr|`ޘsNw؇F f^ DAW# |s9׭iJo&pov˥oO:39sL>mvܫuv˟I1_ NK&PXYZf8i%Ѐ~my1>qFG'!$O.K񁎹13#ww>; O6LJ{-gdqnYMO{əfc$'' 2X^A~=t俙\Cm$=8]oD,T#Ji%MxMҵEAB#MkM CߜųZkg)َlW^QQ]'ZݰA#9H5'6Tү3(miXsΤ>Ucw9 ?x*$ﲄD oe(/4(v0b81$yV!`v2w#O6~l1<)5]@ܑ9`Xjw:ӧ`D(A)-R 0f`O"A<]>/iM3L $3-lJĕ [䇎Rj(rFf`mWx^C!G4l˨r&' m.3ޗUKG-v!kU@SP] Cʙ7KՏaAA̲0%M`)zT%qs)kC%vpw.DRoj\mmO@mc_;d9r?߼{{ߴs ql $R|/c/z[ҊsNlvQ,wT}qD}ͦFWsTy|aqVٶk$B($e| `pd^ jV%#{P|N6X:8:^>}z_?#}ݻſovn%]ކ `N~}ނxy.OڥNJ>h7 L"q)ްF|?b .mv=i_~UeSoɫb>=ۢ8ЎN:u zR6"~g@ v~@ky'E=׵a< ?r{x_s>#sG{Ooox99y~u]9q /;me5:lO㯧y$Hvew9R8B= Y'k%#J`YM2Y׉kΒ%8.fӄb@jUãj4? ѻ[g&NE4H6meyt"FZtѨ{(-G_`cH٪y ^U2yVFQ2 N4,bKm=A->Bw~% NO&V"%V)j,e,ZPzw bgJ\ktbthUɩSG}UTީY&շeU>ƙBuC^LPjMt)&JY{MJlD%@k<"XT/_fJ+3h[ pqvv:AGQ>oLᥣ(K%m+O<3Μ+w@ oa\>DiJj q|Z9g=8ߘX ڛk %H577wuO컻_۫O1V#/ ?-<M,wgr"8͗;#\bw L@I57]D\7Ǜq˫R5>iʋab2I~>4328 c^Z~[6⦒h[β*y+)ڭIS&\@WWN8tYQB6cKp`{½xr[vڴ"+PA1ϫ\Ҁ:E4P>߭ -hUN'[9Ԉ`jCgg|mȈfsUzƢR`F^>suss㴇 b)Vp6b]&קP6 šg> [H|۽Z2ab4 2O1g3喜bk0^fN8 xtkSRjv/y u52#y MO)Y@uxv@g'#\kR=Ǡl%gPesPқ[eT#p֍~RTQѹCuYS CVr|cl@iSq JM%R@-RVj[XMY Y1 @NXV@ʰU @ㄬ՝r X~U8j0`. U&>:Q!%UZi`hգRe.RwDk^z ծxH%lHbft ٱ<&FT&XCp05s R1L0v(IRJ̟NBe5G(z rӁr:TB1>47mpG-`5ϝ%w ў==]YNC~xV|ȐHΣxᗟL,7+6sڴ{c^_6777>rwsw:]]|?;t {3se^< =f'S%6x|$0}=~u~z|qqlxvr3pK_5ߑ&o4܈<} Jk\Bke9H~hOv!9^:|~=OZCH-X!H\ias^+ZBm>Z&rq/7 oFiqe{I2&t>)KKY*b-"xjUT#|"+]ݣ~&va8 ?U"?◹sVz1z#N–iKE.tH$aWfF𪱴xw,c3T؝Br[_7Bge34~P<:_KR9η_}_~cDW\q205v jq*Wr|:::_gLy<㘥a쟜x =EgJ?~xwwEvxLJy<(=HiWdx-BE>Q館˿!ʼnӌ.<9=>?;jF:T9?Ky0K0KBC`z"Ä-JAnPr(4-bLӼwv]n$ۋX3 ®9>OU[v;'i0'T&<:7;n⁙+WsePM?-y[Fi3;"ѮtfВ42ѹՓ@9j!u9oT ^__[UWa$FQ ڲV2sI3@\dN,݀Z#o%ҷnsy㣢/~ 1zZ~ı`8'i%*eU`X! ',3R ދѪĸUkkT$A;vj$CQm/g.v1fV<*<@rNfTlE0x~P*+.⵼ժ i#v$!\7+7i|ʷ{m mÕ  iU1PKR!j"--.$7Q [<@ec0!L,o*Մ _ TBb@&.FeH'+14A|u%ntN>wT`W:T5䛸E0E5׉Kۆ \7 Tg m]ɫ-CRVbѝ!mJ,E6 #B~h˫m㶻yWDăOO ax%mVq% U$ԋVY9Zvtc"aU}e~F+)v 9[x-[9-@mՖIi&h,ey#j9o[$cIvF~{pt}?w}Q o]{;߻7cO ctiϦsA˯y3zݻ$uᡮJh -X<]o|89|iKYV#䀯9Z&T%]lZBz^Cxц SO7ŢT[K -&J@Bxnw<d>8۬ǧwE0$qU];bJvynT80pVhx3_$Ǝy0 xCC OŻig6{/TſܣJWdZ$mW+29:3UHd _}R[XxۊFFJj_mЬiQ\v5"ەF&іG5/SOYYE<3Y݁`]NLyH  _|zOM` Q WZWb8BEiu# @Mפ]OߺHpknnmy8\C ?*Ýiu^|B j@UTeqqU llM*T;f"`> ^*E '-U,mOD gϲrLZg+X7߿wˇOwIXLnz/_ۧ۟~S%c;A[a+PƊo7_jׂǽ_ltzo8yJu=5);b}aNo/g7&~u{5 ٹ$!B.i =}x~A>=86[~Z+ɻۻ돖ϯ~kN߼҂.dϟoXw]wW<2md=Źsۋӓ=O*_CnGy=/XmSEwpwBـ61# f*S$\P*'Y{ ,+@P<ÿI|BLP-%=0_-T+ޑ P @Z݃3q9DATm$c[ݸh!3pض&A'j_Hw C[h-.Ct?-'-U06 ѧGrb2ɸtnmMɉ4ۑLtj;mC0) {l^ JVo\d5G[-$V+/L|w?Ybc.hË_ffS' mnSS*zdhs-#((( ȵBo5Lfoʰv`߈դ;HpmE,钳Phji슌 A+sG~kaRV1"1;{Yh!d!" yeitL3ӪK0 F!4?b^#`1jR]a+ZmFl;5 e[ |ITB5(*~QaT0JI.ykݚ!bR>|qb °j | * c ;(b=5B偼N-j \m#gy-o(-*DeїM%Aސ;j2b 3!| xzFf6"ikXXRZ-~1%]}f*7<`Zcjf5(VBs8>wH7m-s Ė_rixB|pߖOmYmCU.2"2yӒ-!U]q$Z}6zL[ay> ꨗm`< ]m)ηɐ!~rS|J␷=G#ۥ577WS$"1'7Ꝝ/oޱ.}9 dA¹.G{&yx_~W~}qFO%qJmʑgzD=~8yL}KrvvO?[yN{sv|qqڏ|8ox:r7SW:bN Y-N}ȶ…>狪2J\k4*7ӷm>)h5cuʏ)K0&Ӭ1O_0 $f9-Qla Y5F%)M8dEzP-k !l#D/t6&\^LL6^2zK/~hDst!Kj6H _"l$Vײ~#yeb4mVE1>3!lJ擣= @1 02lڴs 桛==pVfl֐߀f-OHfD;T~rn$.&A$߻{nͻp5LrhK?0D@eخ&~yp'YTEғcf8Ì; f&dG 1! sYA 3`mI=Ȑ]eG6 v116w}¯U5~.|k^>[ʧ}1td #H:CXQ= _NUY.BLw5{t`BN? QaD?+A? .V Y&DqJ$UVceȵ# wL]BV/!*,0 F;ާ>8DƟo=ǘ3hV&64/EtCt|O4}-C|gJ$np )ɇmQ/1}d#_E7ݦZ5!Hݔ7!Py?nndh;Ti0Ch)*ف|A~yJ{Ç;J.<mHn`?sh9/|7ӳ?}|~t~/'F<% p}rr}7_}ucy\]]e\CC&nׯݛ^/7M1t=8{u%t69_]^^\tOtmWW& mmYۉe<{sÃwO?yW/~W__y}TiI'Q7Lq{LԱh...1O--ser>G2og=z0َN.gᲩJ}J:NwOi49$^U\hY0(Ҹ7E#)w:ַڳxL~oznψM'S6]0d0y.Fd$$.hⱚ!2ȧfy懪m z+ʼnLʬl4VТ =Q3yQ)"deEykw %BSw,$f`rcB3~4s*g٢)S Ombk$ zfmZU[f.z hi'.\VqΖ+Ex@22gh 4oNoI//l) ^SܴRʓ)L%S f/;JjxCu41D&S%Y(k 4_y/o߾}˃+{yݽE|y{?x*sh]8N#,=_]\z:zzxtt;WW_'go^qqz`M]ܮ"nn :(L\T'wvvnѤbs!pBlC'ϳ Otjh-u!i3;.т):U%k]/-)3>@u T!28fe_nX*𴞫߿/t_E/ $IIzT%E/Erh6狹,3JJkф^ D  c-+5 5>1%9#}9j`(k]gs^H7YDQ U&Y?u v)%x2&-kn*]ғIz Ϧx<$H+N`1潈VG\@1C0co߉H`CZ6%~w6ج "܌W6jWw2ɧ"֚\nܭw"SYO! >& |A|>|߻HnD_x7lyiT@aJ$0]uѦ)jZtlƊT$n)·gR YW2y{"a[B5mQSfF7j˃͞qd%=8:͊ywWw}]?]_}S4~;A>uzQz/~__#w??[BBf^.Oy[ɼܧ%88>yEtw3p۴(U% $1dNJAEH@+GǏgP We~g%ZmN3o޾#ħg֓гWZ_7gHKfFztzt|7:?$uQ(ayric2Gme'tvrמdbE)a^նYJ8Wz|(7'w $,\uꘫ):)3 2H4\s < LϏCn7c{?S6ϓep3;L/7"l0CQ.M#tzP!в2DouQ@;&ޟbG-`+XuLUZ(i GE1:C`rs9R ).ulשIkp5=#R2i֡BN*_DJ碀R`IyH&Z$qI@Q I^ ~֜DXm/h34EVVں<}{#ɵ6V MI݀]^p}ēC˸9P`m/1'(iEKCdh(9'b>[yRr}d"/ \U="k+ҍ,E KYVVmVT! aI-$·-Sx̌!ۜ[j?RWmI*!f " /[-^M Sy1N dTU$IVo@.Oۗ{#6FоPIX!<¦QPnv]~hi1k1KjI@d&`b"P6TUC x1#ēn7%>CT%|ݖ: x--DSŏm8HUi;/:les, zijOAHQR /n\-H]K U2CoAOV)6~*ex0ivfenWoT$NѦsas'̓7 47`ЭK$W q5ڭ[7D?P~8Nnqx{w?O?\]^ l0w7կ=\]^__]WW{zoߋuCxz::,2|g_\Н@zU~ˇ ?J chSzf)7iO鵔/{s|t7_fGӓS>zə噿H{}w :K{{O8axvqq_o{rsu(h2 K'_>ϻ^yHY'ǧ.e. q. Ǐ?~x{.zoǼ:>y,GCm8?nC^ΩTڒW sG,)R2i*<1%Q6 *Tȣ㲕dv];ܼ~<8بgý4"6Ɲ̒K\^^~oONd7_+<(3IN$4&F:351D$=?ƶyl+ c^*dQLnFHNMC["4p+'O漡] S]K #L܉EOۇOXr/w[VܒKRLL+mnvD.v`b!!sUCq1N"L}"Z,3m]ɊU2.s*u%&=m%*rJϻ{NaI0UÕ?!@)U\ܒ16 0VO# ʝa0̬c6>ΒzQ i%35e9 , ?Րq# GK#d8 ͷidkCev J~w=O̎DwnbPJ6;%fuHmm @a{Z||+Gf:C eڮ~%B0^Z'PUyw.$[A$RE=K5o]+V7"Z2Z:Emi朻;zjQ MFvRr6')iUĬ==o?Ǜãe/=-\]}ꫯ޾#~/_?go<;79 JϷ''Τ_~_~o=;04UH}OO7UZd* g15iR~ϡ۫|T,f<&@Y?\VwhR|y{ZN批V?~zɧE=8̞'.gi7fFg·2}nf{N+ϭ%Vq<)2)Yŏ+0ؽѲHp(5M/[VYQy@mF z@l9c q\n<~Eu*mv߲γQ<8o MOz1FCNjTs;ms lL2 x^hrU6ݨ00 7 \`-nӭ !C(L \eDMK*x qQ*H뙴q+aiHM0ϨAN[)U`pًFN.Š6.d]`D[غB_zNBpo[.yX<4Q$oO2(fyƕ_^sWL\#l;xĤ~i.\jo'op7cn< ʐX3[8M ,F_Y +@g nΎbvI{M62 `(N%瑟B3Hq1泯q%'$!-JRumEt4Y-e. <']h<Ȍ}G8޲%J&5|f X5:^{۔+Z|34NAy+D|2tUbD m2/T%U򆉋ݜV8` n]?_o{Scݻ 7 ^IB)H73T䕬ttӮ5\-J~z^M BQ]w\Be^v, TkWm@5dQJ-Csqt{Sb{$?ӟ0y|rLJG'?xwwp}s_oӿ7,K?yCί~gąȯ1Pԫ|`T0#r[F UGMd@Uȯ\̈ya7ߞ?OS)ڜ x~z~|`W79Z7 D>w/?T]_^]^~ʉ*qt|zv_J8;Ý0ãnf o*w}}/,nO=e&wS#V!Ȭ;Npc;Q|ɭǼㄳ6xn>#5!SY1D[PdQ!,قa ۱-삛|㒴]&m ߶r<| Oc?C%h¨Pa_j1Td+j#Y٣ez_T[I>2$UErt*.CҴZj[[szU t+&P9*mKœco0U90"ĶT . 5yQeg,ӳJΕ%sh#xy2襈'۷_dm`2d= 7+͛6o] -|s6p%{" /e@ L)5 +cX&p\Y¤w8T2![tC*&` RFxA0",/&6dLU3HAȇ#L)X*"Gk}2,Z^D1$0V J@ۮ̩Xc$SU ;v#  C($)00{EYZYXh_PM4LNbfsM}0hFɔw5 -0 Q1I u3鶞S+j]{vi$ f(*51]Bxn/oK&x0%fe[^[SL)+`Oɨ.xҙ^*JD'y$Cm4!lށrr b*ӫ~w}1/3ߋ?:/o?go?=<> :-|Ao󛿸80ȗgOOqX9|xs}>yo_x}~qztt|Do7i,sa͝aZLk/?x~\=sW?Wmظ{dIJ'gÒ6NRe5RIu@Vx!,mJ4k9EƚTn-K,/vոfêI& 0j*C7|C;7OS[-^9DY䭩ʪ* `fk6bQf8^id2-Ur aaacTU1MތCS>/L50pXRSΞ"[Nʀ'@RUvz.og#U\fve/\ڙ)g7@ZHAZ ȀڕQNא+YL3 0py+OaRLy-6S0/ɨ$bFdST ek}Y m0+n K v/`뢥eaUCU<02nm lރ9Os >o'l)%|gvHL8HºwJ¹)nO)e ` 3ȳR:+<ڢigJ^[0aFb0z O%$e]L(9BP۩I'uLL+&9B$+R!FYrM_]41[.%&\ib*,RU]|"j(N#v h6+!VFZH@D0ۻ!Ju<z>|`Qʞzrt~rWkgo+71ƻ ۣg?Z WR.3Vb]Lk2G9??/O?,qx'H8tw}u}uy=zs1R†e{y9,|wUc}$lf17~e8t1喗irxK3N Z|}]XW{Ggޭz$ghGȐK%rwgE*! >kyLcksRٶPyيJjzญlfNp9)01^L68dš%ps;rnI6C-ʫGgkXL ,[.G2s올!#.ؽ w<-)X(Cw 7n~?{fzӇ?\E?_}տ/?;W~۷o.ꋿ߼uR}5$T Tʨl5߫=g:G0te:W_XͥRjԄ0f;SɎb$l.l"Ch.,M?sZгlI%0ݼ3LT М߽{QV^ $' ?lB˪ ,L:A1T/iPgtt9 EbF˙kԴ/*֬ūde`Ӵ+Ito;fPFR1KCHP5f#/ĎM!!O{xV\8tuQv]gC`ɜD s -[f՜4[H8Yt29yn hq@؏eEY]xǴܹLE`ԓ[.= ?г62|'`*ǰ r'Qv)y޿zp)z*?O\__|_՗_|__8$#݃ȨSV`5~,&o޼qv亻@D'7['˫?~0zװ̹<^83$s3ɗ_p6ɔ@u<>o K>`٣K2}\8qmSk#cז'|yh꾙gӓ틡tkdɓ`zs,E0!XFi0lc\'yl"i,p?u°kӑ6" ʋ?$2Q |-R9opۂ<8 [O[lΑwJq:?4EF2aIPsṍr|2U̗򜤒U(,:_J61E^r*-vwwjU7ay&ÓA3=iE` >]A]n.lZQxnxZHa֤&H%OlUs}1$ԖAh5V&ys7|.? ;k\`( m˚RV+@ DޗyLǎ2IZ#$`^Yv 3nQ@Tr/4V˒%ߕr00#]&}vI"H|[RU-V0rۦf~!"}c3ݽo;!z39>~3XHJ7=ݝ8W߿V'eZۍԈ=;ˊ)%!=0ݏ2u\9g kENx۫hp1'B)ƶ!IăsyOlrG$_+ Q-0; YFK:yx_" J3=zH*t;S"O3$]`j\IރR1~r*z1!!w9z"qtbO X!UlHW''S*s9[_?g|<]W??|oǿ'GGpx?l?<>#'9&;BMS C)I35=4V`3@cwe./ݳ԰/Jn 4=|Uu}qkR䒛yv <4 vP,жs!r9>J%2] Šy=ov۔]e,Gn1$[- (ח'uy]@^YI/ũ} տʊS^t!_w='wDE:nT5[2FdRDggnRf_Jk;!Պ:L,TR/W8E8;[Wtv9#ʤz3!AekW6 RIfy<ڥp5ǔ[?0`ZiWpnSRAa+uVEbVA8gUnaZ:l<"G5$ԚbyF[h +- ]LCD M޸jXymQݥEV F^~ Qh %5|ErǙ j>۩y^>=B8- ꔡ\REe D>`0$!ZmsfmS*%u / ",2r{6Tl>6Lxh,'hz3@^LXH5T+ 4V|NF5L޻wWEP@oPY&| Tt|Pani9[FDU /?T AX*r'Z~w?9~?QU޾}}t|oѾG;om堕>ma33J/E5+sw]IYS]TO [fna 6ƖH[''g'!_2wyApx/xd 8Ȩ}cS7oNώ.O:~G2OX\&j2S͐3_2`ul-9=gʣ{R% kɬ"ΡoŦkI enTT*(?K̖9E"*LKA"{s戶萜#ҳ h2$$eGNNtCL$vҿgVLgak'7ZW-Q0iynTZPGLb 0A "GMF ӊ VzIGRՂU¶rQSa 幰jˣ1$%~(*pNNl>5/*cX51FC$ZB$-ru`4 Ym= !$b%;뮲.UCdD^SL%swv Xwš(Meiw?ܿ{͵W_}˿kg䛣CC,))0 v_yKSzF&獮'KX+Oy骿xxpdgQ$#*'7n2d3}"UG2G.#NCj˜k$¶uқ ET0BTRۺʞ4fRz3] bn^_xvriDZ!|VΌy] Ԥ+$9bgLuK$Y*0>sRÌ*14nd e.T/yvԠͪrĐ+޹GP%nm ѹ¯д/ӕ\[-l i\V,0hv+׼cjnoo;e B2,-Q Ԝ TɁk[<աۈl1 0MRZ7v {oyDRf1$gb^Lcqg%UҘ$a1kK+/oa+0*ۈ;y>57qs_{ަy 2FU I_zn0Ŕddz^qs_Ī9}<9pfә/ȱv'%2S*-KsVfqiYD%o9$sã2 U8/w8×;8|+bW5EKV7̋jvU8NZ.$|M _' i{٠T a|kJ]<'۸XMb! ~@b hfgQ2h}řW1=u 7c{WYA =W߼9 }N!sڜ/޵ypzz~^ehw&!&7DC{xp|}{!&$|kmމ_]_8*bW_~f朥Ge_1׵I{MAݧӳ|O>'V,Շ<-PɯBF6cc#eyI'$})N8༿<;9et"ϻgy-UxL6?%դ 8W13;ėa`M\{f7`ʑ6W[}G1~;j;ĕ0)ޤ+m}'oLDnCB͙>vi L햸bO|Z3 Wkfʛ9Kf`Dm+)fMQPǠl CUڍq*ꗟ_\\`yE1fe̵4J21vMĹFc,Zm+vۣLt6bh„a+b7.:Ѷ$kL'u _ӖD Ws8;%̻O_>;Uru0+Y(N{ahnMnqs2?}7nx|nf'|e"DRdQydǾ1OpݭЙ?tys0@EЦK(_(<8bwחY*'jƳյkZO޾"I Luէós7c]6]5WWvmdq}]I%Oyv3mbb p|xrmLy6mq:G[jHC5) yQ΋ʈOz J ^nv|3A4VӶax" yz]_As%ZJiGIhJ8Y~H*)F،C$ y1bȱI&v lϙ>"`S2/Fʤm. To;]`Yُ^#9nqKIݶw.C~ȅ|y1 LaM[24yJhjF"E1OO-mvKxZm%EDaka:W\oZ*ЪֶȈy ^$M fSZj[9~U*AUUX0Z*|Aw%B"FPh?1gnnoOMPYEޕv."6,*}%xXUնDWHFJ::")2# @7T~]#Z]^u\x©H `\)!5Y@FFEYUU(|Rی\b j+d{'|ymy2!P#)"x'J STն!Z-*Ba3/~WhKDI+Y&32֡$'fWغ #W9 bIͬO EKyD]r̵|oc.;i_n1U CnOY@'Ȅӏ"m'+-L`V~TgD]ux-5cE!dl~`w8y5Xewww7_~_~rzJt+ ѱrJszgCv70U_~:9D~gUFexܺ=}^{OO_Vm9T5a9BwY|)?Y|2V$%|f$_RC h<%jI,S-P?ϰ'Zp/Vá);/qCH9oԱ`I@r{A?dh׵ۚNɞk`n#UU [qpS]pI` ҟAy\dPY(x3KČՍ?ƛ|J Bդ4tøW$I/F A=G(mS2ygv שx(ݾ#C$gܼOON秳 nrC47<:&y.bb>A;(Nȵx~QzW[#d D9ϥcCA"frIL`0Q-m3-[ HN]^G鴲^x ģ1x9D:Uů|l!(L IPv?9WZ|̇thllO/;"Eˤ|%o\awT% HSK>_=ғA<-ȍo1>} Ʉ˵ ʃ'mސ3m3AYۃQ!kUfA% ]A:P)ҢmIc-!gIe7}x2A$^0rbRiW떉OCl)ZnIjx"r;>D[[emVe6pXf_XA;LnnuR$q>3>3bռQH6]ZedZ`N2$j9ςɏ$?g5 7' ɠ9ǽ73 a۽K7b6qg*m 7 jjO\ov:}s>C@b2b_T Yy(odG0u0hiKODxȜibƗC-gTyÇLJsg)uΙ۵4a2qjgDsmƅك'7ernFX#JFyrgޜ>>==>@[-\ ئ2b+y%xLÑ g"xP;)xDU"lQY}rqI ,&<&@[V:m">Upj+@,ntSD?vtԺRLW uq!礆Ҏҭv⪴ABK\J0I?n2DychGd}7agG2{Hlh#*(^kF'aU#JΓ^&.M YKbϞ3:AuYJ{d[mCS Qcsங@ 0!sYMYB"ugD9Qe'@ֶӬr5>bti1CIJCr4 v 7!Lc!8,dU$[ز6֪QU V!~1L~- ^5Q|s$".ȗghFT 2 .voNJΏ8ZWUiC$Җar1rũ 6K~ 3B6v3$6M'f6uM'nVBPXHL%J;:{\oZ KpڪD tfd$!٫IfW\uvk w)A6YLr'|°B6uXۚiKU2Ϗn^z#v7-Ŭ;0@D˄*r'" b, O\|==yLj8rg}ȗ=;6kggy[:L\‰bˉ0yf_Dxj??%7=U +70ζJ!Gg7\Z<3TId,$ 4G&THnUU&ecSV50'ݝVpi{ i&ե"Ֆ9MaX?xmC3,f}?ֳ`Y3Dei,$ru!epä[ Clf jeE  F9ðI%|Fxp1j" L*U[ohxL5!R\7O*U7">Uնb *Bъ.X5 W"4F$5CP[ma+򮮶|MG0}ɪc)Ld4lj(r29o=9>><9D]'ZSw|sօ352l'C|säƒD$1 X!sLS.~ 9߆hY"J\LUZ0v*ɫ2yVՠ%No3"*ZD>Ժ-CW?pd>ݼ߶'ۛsK<yv)@QJO4Aփ'<%).Oץrn 7V9[UyӌTv5*8fTD GN9u} y0- f"ꓗ'D .8]ǚaQXDs3WWWq={M& 4yxg`{L<ϭ p_q ˃yƠtb^8Oj9"ɶ 7L 2.%L17`kgd$؃3lInȅä!!~NōUqk2 O旆1uRࢺ,C5aܘ !BK8޶di1liɋAJ#:=;svI\\5cv[xzY+7f:^o1W2lK=Ś.')縉Vۑbjb;"$HL WPo 5Aȵ[6 @[ VVPgI; ?l%ٔ{[.ݑ}ˢnͽM J$a*)kuߖ.^?P0a%u ºds+?YmumrPaD>O$x(zk?hP~ 6yNGyѭR\i/b ݭdY)lyҿx]y-7'G '<Q|Isj,Q4|pib+@oHmf6awHQ[|>BP2Rܭ&b X$YVH;x&۔lǫBmKu o!DgCݒLo哷: X$i2i<~"Ǐo^v[c,y%CO97VoQE0T+\ЀwzhGHpŀVϵKk|mQym*_)-R !\H6$IĤkݖmeIFɶp紽pH'YcoVK!te~ƴ$O(!nO? ܮhsҬB"+}$32ßYM|̠{'Vݬ/P2SC~9؄Cӕmڷ95j'՞oX3"Aez,[^?6dE<\'2֔? ͊STl1Y;IZ.23FW67ɓÔR>Myn-e`yFW׎Ӽ*&j%PyB3X//ˈVH gQ! )ۏ n≇4:;po5lc MiKl.Za@mg0*o_1dx~{Oy2d ԩLgggyTQ1!6٪[HC8, nMۖ?9kOBH]Zg($m1AnEn ZIaNu~V@-rGA[ rBؖVT%Jx2jtr.\aoO!|W*YB-uŗᶰ&oâJ*dEVbCSmV])@9C@,h%;06L$k'>򺍟'/vWޮ|u`\Uh%M ҶѩZzjU?hF@Hj^-wSs\$n L ukΖP9fui?릣ix•ZDr$%JVIiIͩ4世_Ȅ99c%>tPC}sW{gggsdZmZ2S߫y7CBD-+ KտD7 X2@ stc 0FNY 3QaU]]+DohlQ>NN$ ljrGuʴ 0ɤ{~w)/~Ó^&[&'@IL$6 ~L D'KTc\('n$smOMŜ\bWhۭQ,ծ\s[ 4|f}FTLc0+yS5e2/=/hrXR[Ck;*qG1ƿƴ;̚'rnh:oDDXO+N"P´0 A uٝ2 CjBlB?).0-jo3"YH}h7G™A|uEVTo0 0n y(滣N?Avl#s ζ9VU7 rޟ/̄q^M[R4Y<[0AgU,D)P1[~0/??ZO u6Rw3=X[BPh/+tS5*6?Q"_~vayBFH2C0-,l2% ]TmmH\BE2g*L 9a%LxozOڙW Ev&gԳ4G Vh֦J >~F;FUq1."L%6H^X*KɂdGBy*Àx //I4JDW ЭêsHfûf0'gKQS.B sQ$H ~\ߪ:7E$F:)cn%=I&%|D 0%v0YP$!#)i2PZz0R򠼞evMb= ZaxHDWmvZx+jvl1hD* ,/S`lT%59Hխv.U-<+{8jղ"Ky^-|VJ8흅 La$S$(ͤ#:$W?ѨGKX_ݎfkIXw嬥OHڢ:4فNbArR.VN'9y1 k^>L%TFL^i*?Nj!v\~uY0ڗ"ZmE˧;!W@EЮ -m`#*I4wjLӢ0ktcBMVnq1l1pG 1 ݈<:ׅ#ޞK| Ja} [|u2kzJAT2Q@tP;#걕|g $;͓rA8Hs6-峪4.mxL2Lt\1&ɞx(o|r߿xifON VaY0kBHyD;/(MSئWIAm) [K pJ_k ڌznKIe~g&RIG!-ϴ0\Z.)Y P.D^ֵz"٢6ߚiq'c5kDG<˭g`{~mgB$5l͇Urp~{tĐv:*G"($f޴='52l iI>dU$lW\ßilw?('_z0 .Lܚ ]7TzN]T@\/ ؃ݟ}4nnmbN-'Mc&DTX xVʋLz;yx~vOvX8;?χ;rBy "%0'mDiXǧ'v gm; 1^6}d?8%%܎H yoCMeyI?Fі)۽SJ" 9auaINfbVJ-p &GA-ܳt[`O{yqmq2xv]Y5N$DjRD{zuWITzaJ3Øqdș;0%c%l.'嵺0 쮩0~3um' 0)qNZ2C]gfSBY3{UPӮPI ;וPdEE8Ȝ1H0J% dX{[Rs d5ɐHS%1/rSv̚ m2"ZiH˔.^u~ے[u׃T~ӃQsJѲ4/N,Htet Is"]Ta֐-V/%mJ8Vja*׎V@|# +C^.ؒtYj@5Ǵ9/mYb[ $SM U._烃ܷ{L^0(H52g2LSQӢ0!`xUd$ن-2[ IWdac9A_÷֍۷Ј@QTajC)&v Tn'~-0$;CgQZ/,Uepsmgmzι%E2_`P337F3h;j 1=s:89 'i-nnonBWFn0G2#?s% 23(hʁ1لOSRmvI$m'+Pe.f9 $Z+0nv!]zisӁ`sN&8̘u7`$M6h0C3l>2+(UNXߧyXI;{*2ɧ18&! qjn0f_&e~m7`[]՜ϛטvO D5=TQPSS7\)5y Sm%rkRX:yyYmC6_g~7:\@9fR*|gUaTMD159i׊! *c"ʿB$$jh,BжY] o/vބFw1Jfn>yb>!eqq߼_, !}T[1*fjdLMBBto+O@gggI nk=u^70VVEP7tJK+An|c\ :[ն+„Fi!g !+H&YZ w =< DO$14DX CYy9i&9xo9l԰N*)F;+ Ve&u^:>9<: {q 5$HcQLLXJ|RLӤX7[ )oLvI5GpF䤑O$cªe]I&]V9shR<%0m7Nռl /..#u'&™<F@#jSa`8\ XgDT!04y%Pçy ɧ@A&+8p0/F t?/j 2d.JsMna$,$ yC1-B%6I`9 Un96SPdD,nD:s |nJ]VXxDޥ^? MZoDKXCU"3@˧>kˈouNX@ͿB|C0߿r9i>nb9|aEjqbvc9ZSL-)*N0J [~z"SCy™4lݷ'[Ԉh7wT̐>փ!Pc=z54ÜS{TSuuB6>B"ƫHlN9<2WVn:;y =< ĉR0LBL 6':v|; -u[aݘLtgvj2ud4?{Z%v尮eE -ꐄ!#$LF>_|V U1Ta`T XpmC0W/Aְ|4|"sWTp%- C-qRIkL0PEZ%,DˡhIcV`7~8?OfyڶxܼwW xwhi,7 )hEm lk+'+NAuS) [5jEk(X\l+%nom!jbUCr[-u*/`1-Lr啕!`LhƪF\kd  RB ~>gxk$ys$oKDMOۈZ] P20 ʡ~2!iBgȊ .[~/ȚMT6v +Jc^C+s&4Ы|{Jd4Hy*Ժb8 $HlĐ@>0-?qBeT"BB*U]qBJޜW 1 mUH [smj,\JxLտZ*-t-&-WZ DGyHnO/'Vt덶02ThI-_osʚУ;;b&%O+2W 3 C}r{5KZiEmR7#"O1ZZ? "j~U?>U-zNT* Y !*[] ] %\?uUF[v S ADZ<݂wqa1Z-ʶHC,CTyM$KT2@Ρ눝-Qzpؿ4 7Z5fyږÇ6^d9V @KXoI(%jPMEҒ8_|̞Q)4zN&/ar&t'̟.d-(!rdbIDvuRzn5`84w[6yb,\2`7DXj!3j39NrZ14BjׄrlkB|ۚ#KQW( &AW@B!·K^'F_~'nMm ۥR.* _ `k.!b-B,bYJq;VA>$1><'Ϲ3 \~n/ֆ7c .`.y~ɭjˊPۈjt9 ͛7(RF8[sE [?gl{"$E2Լc롘2ŠeH0֜1%GZz*{hy. M!o?煃 {/ &A'm<@,z)a mHyP@0&A90A>ծZrXTH{A 5r2l.m>yxx.EEksd ab0&m:WKpVrt7|xp>ජ[Z w$l1P3&ݳ%ފ4,L6c!4| Pvt#.hc5)ƍ O#bȵ[h˄ƝB-m yM3w^ .¢M7,V&Rj*/LV`x4*'J1 )g>p) 3p{,y4SP5an$&0F?gM$.̵i*ݪS"Nrޢ&Jiɓ% d "fb@|0,`uHqxBa CN8Of!BڎtIhn\͊ aIW(3$'FMX+ oPKˤpB#R2-eI!D.1`0rVTxqm%iaHbc!{%]Tx,*]-FO4}a ۫,BQ@NSt:eӕ YQڼ1i:CIa7+!e R ta OhjLsO+[j.Ix$4D][!I}@VPuee)*7L4ۇE]-x&aCխU]ui`IҶ t] ݅cN|j Ԛ(ɁG4q0$mZ%D$B/{+RT[.9OZ렻]c\uxv5+S\.L`+Sqe"`lD9OAk3-d P(A=}ȳon;"k62LR9~ f(11JGB+%*I:"x*Ű`82~>Y-hӷӱs^D;`os:5(~<`sօ]ʗ9*LHt1"x*J!uy~װ6J^7WuA FB,fcR51HڢJƒ[LwB5 Tj!IT-eA5p]˩H)' C[8s-?M_$틄Hy^*C," &?bGLJ|6Ҹ~\58+cVvMer953 !>\6湮ijV'aK5jאjM4+--yUS :Ffs`KiD^P:2ABCM{}g9>M7&H .a}1qKʍ\W3!y^Mg[_2* YZgKw.VK%!ae LQ6v90 f\uk n䙒#qMws\ nFr|f:gr@uNoI3LbNK yy-S<$%r분0+De*\nZZr `뽁*Dj.oCB "-JN/RU.'½hY4=5/R㿥6Tg Bjӧ$ <0-⤛&9 IIbT;a0ꈨ?q[B޺75~HR4wsSMLM=6}d V3,HV͹mhrB&+0q9ΖI޲n>>7[o0C1 0>'$CZsgz1ykMZ$<~^~˜Ǡ8E)xwف:g,)2^*@!<%'Y uUJD5eN ^ LCxn*DgrDLQWlB& b+ȰlK✖DRBvMfuL n[p"kHm<ĔhQY$+mic7Rrz*ɼ̻ üL*Xb6aCRR-i'-aFH1۷57 ^SjKJ) &˒d; p2  SQ-A6&/  ‹PUYU0WS]:m۶9~C -Z\S3! sJ>9$h9 g<n~G!dK.غq2'0csQEB~~ SV>~- ~!ڙ+h9u@!e!ya3ƥeKg Cv9@c&!TzBXǎ1-fm>7?^ˊ9F} <;XK,NLǼ4>(~<+ x5BˊJo1L{ -fr_Js;0%!Cς5; fx3s_;f0o3++! 9P;K3+BqIByme)@̜V!ZWw bKpD|g2j~r!!?4'l/EM8ɴI\3G+$ 4Մ+cvOc&Lv17? c!ۂ<9LՐZI湆4!E`m|_{ޅ/weVCrqHC. vA!fWv4$a&YQE7C"JE'HsjCvִ+JX,Oru-F!TsK&Ćgb ϊs.JEF{'Cb]9GWI՗eAO3y` !"4(&7CT1pLi Ń>Ey\lBoOZ#CɃOlhWy `oQP<iCFqWf6 yg`Vl#V!eu'0{}N V.uiG.W]q;xx2<+/H: p zXa@*"PH]Gb'cIhvo y2_1߯C :fqL2.\ptGl1d @Yi]ͮV@sG7 M)ђ/Mu:Ѐmْ\u=j% ] gx1ʹi'YKjIGi<-v1㖴iS8B}두Qt cHFVn(RB$6.(FiuN|b `hEWEaGu1jbZF$c_h&UslSV˕j()0ɏ9Hj8W8^ dH4[n0GOZIHJq'wNȊp3Шe11ځ39vP۬ZBh.&V;n] /=?y1ɡ-R&z3g?O?}}&v]SxFjY# (݀X~mꞏ}Rrֹ\"eqe%o;?QSD LÏ?U[fp!OIvA<:2[JInh?p!Zj:,dq8mKQO#r&odf*꾟9|ˠ>KR9xϊqY%JYEߚV1셻K]ea.+`srYK L_͑'G9{дy^ny?ƈ,+BOnOiS[s+*6%Pz\I*zk} D?]Uq@Q&΁98qrp}6ÊӼ+*3\csB$u W?!0g.q4h To&1oesye"2pFiCXJSZ- %g\׿SyH}qr{= h5Pq+hE@sw4Oܸz {!2qŚ7"KV #ϓ]kIL.р*@07>~-~7׈![B YK!HfZnޟC6ңu=B#O AQ&>LA #.>uyChۄx0Cƒw fB ~@~rj y{Fl/%#".xuX@I _^%,e0澷\܆/<qo7ݕߪ@{JJH,WGZn@Bu%̭ Cr/cYy7 LcSJP$_%SY:1kdeU7)%s4sUpn,nV5&|>ǂLeGsGݽù̈́?A j6Nʩ䛦y[% mt0~q4-hYNVO@ט.Cbԭ\5w& S$+tf q UO%O'sh3 ̒EE#I)sm|>!Z*\RݣT~ yr %A񈜷*$| ~ _8̝{篿{|LXLt90ww_TDA1$ y,T& q~2]\0h tќl-. xێAK;@0riPI.2Cb0fK>^8mKRd_M2fchZD5HJ([ +7]*<-$ZH637}5J[:7܂{jq[LV9s~Xy疃;צkԺlC 2+@\ȃ0`2Uh=YRszc7` 6Sd/!3zX)h;=1Zn~tڨ\&Pk85bT%Zsɏ!G`Z0*7fgYkDvL2瓕ïk Yb.MۼJj'`9FDu /!k?lE(#ļiMAJA!Q^Fy$$><`9kp$TSO 1N\ޚp|UL>!8t"4g.q╵|ƉưByup#LiF ˧!N -1b -fݛ۾h۝&Uwy0wo߽3rmV#^ žoH`\p/y u5ӔOuͦvj=tsoD˄Cc"y |Noea2hna d-F8-jwT\Q꺴Ud'F#09oKIx %ךYwM/r[s G ďv]#՚#Bi/c-Z0!P) b2aT@n(Z00%u\1*ڱ4fFjG$QHޟFhr}sZiP]4I xH­jUVFی&g5C-]h3unP$ ] 5O\?<w3.U.r#ܼF\py+ fγ;=|vN˛^VJ6NmΕy(ݣӢHrdt2I~}&=`I6>K\Bh٢H2feBqז\66!2̟?3fG1񳲩0|1CG@Hg_A(g Jv8A5$.` x 6 0T. D8Y s,cEeC<3P$.)b8GxU>] 29UFC}IaSOIoBaNd擬4OΞ ~SՈ*p`eoz & $nB?B\|ěkze퓇˿+ GH:Ic |>.磋ףz;0T٥ YY`GWA OJKopc8ᜅU]& 7vkIfLN˓f!I8F)v1%q3gߠy;N![R fQ0 O6(0"g>6+yJOo<}=͗/ ܹy9LRVJ$O:Obhވù]+39J|!'lMe\fNs8G NS>NS%gu_7-n<,+zy4q VRKRF7+_ 9JuutP_̔ic'Sq^*k4]6` j1ƪJ)X x&Ru+B&OхGģi!aBL:':)i^SIrD5fˍ ZZ"l]|w;OƣEqA G@LoLE.Yj'C̕ъ2f/!$ p?C<"\8ZE3Zڅ~'2U1Z`1ڋ&I_,<'B̦L7X95Rs-HQinC*#1>e$Hwq>aLjGˇ͂v;] uFu0\HD+:"1Tklɹ}dsl¹m8jn~K$N<;_ĺ`J#˴1v=9_LF +/_YFέ2a*>joC&âe#3WPVDsHE1hJҺ0rd6+WӒ/ MdZVVޱS%PyPOCUm3D1b!3Bj0_dTI.. xc u@/ ش1-zD6ju;b?L)ϟb j]$h:priWx%mJhW"NQm',S1<'Ulڔ$hk9 I"5ZrCԪ R"iq?'x>i<|Mj&蚙pt" `DK`xB 'Luu0Eˍ]咶;݃eN ?Ђ0ɐ$"25u&g2~rܐ _|y >fs8xƚ!'5ўfEt\I?N5pgMͶl+. 0$XMddyۛ|?<"v~l_Ē[cW^ᨣN$:ͻ`OHL*d R:ZfhgK>ϺZ& #e3-f$0!ބ=߰\o;*<r5gL ȯ1nZ +Qг|BrӶs(l֞A|\0鶀EE{;?5PhNqҞm8&d(rB8pg!FsؚWD52F;+Zڙty@Th-".O2- "|2\Đq&Ìڥ:۵dxqC>^E? Qi *N専v̷̄i̧vXprulkB-"eo;+eϥo9*LkyX'_:I')/ۯ!J3L@-dF`v$FN2cb\l.o]/֎h"&b漵3仁a .oz+$%pʽ!o3DXq1'˨  ޲ J"uF  ͏ɝ7ķ*0RG3LhFRa# n mno![v>"0̵IbW6Z*w_]ݦ޶PPȗ7X qy#Z>s;NZ$DFqIƄJr9@H,mbKc>~ﻻ՟gҸjQ%fba/j|DY5jQ-˵‘ϛv@x4ftWsHφ?CW3!F'1TIZAitrGi!;.%rDo Bd?Nu\VHףH!K`~cYata@$SK|tTiy˄&,VTHw˃JxL4(vhc#t`RO-~Qn{V̩!Ee@̧ 3};я W2@vt.xTD\3b˜'0R.3dLE~-n=dbR&ybpiaw%)m qo.shQ:| d;U@Ϋl]T;9n<9^#OY \/JJJv] vQ0MFFFːdp`U:؄xfQ3C<-FҝD]2hشS̜T!X * Yb Z` < I!1CLϺOry#iْ'˧cb$ѱZ>xd Ψp{8ょDW=ݜr!-~Yota1q$sZ}=N|cv[UR+rHJto sŋ#Z.h:Z&6rn3̃V{WEbSm.LLq;~f^,YUp>`QٕXq.^\*S&ZY4Qۙ_~0y"i:+vgD>'1`6\>jO&F\}1Hf˾ ltI㹈6jW=,N)n",}7L|Nv*4Qxx4$H&Q褔U6!ah55ܩ?5U#:I1L2Z(Z4+r[s8Ltsɝ"FY1k<'qiK!e>.<J i/U)>P}lV 7h$pٸ0Z*U/yUj2<ԈvʣՑЕ:~J|VKoǁWfyW^NRgެM 2)cTÕ.1CŐ7 0`1$b5cX$]ͳnt.O-esV ojjGN_q1Cl%DlgK=daޱgD݁&!}~te]he(bNm#8ᬈ+o^UXLL6*I'h|Zh%o޼+S $r5Dqs:wGih?9;;jͻծM6S[&_ Jlm|Y) [~qO)qsӽY'-±ujh%uEĘnBn-l_ĄJNg׼p +̜]NH:xP $3z&ڞc_ 0DV[BDjUXc*Z%$- Z-9Kc89/E m` Mt(6G͹L2i^k `>=.)=݇NiOE \IB\I2<'ޓ[ yZkG̅d\vHijŻk=I޼ыSPKiG vW$zP~gHLlmȻ-7P."#J|+D̹ 3|6VLra:(9Λ6f.4p:g\xUI{0h~]!;c^*K_kgUO h2&|]ӔrCy3OĄ|:d]Qxrm4t%.`VȤ_'HwCj2$=z(kW_o̕E훷*zeԅc"nT.| &n ~=8R,ٱ5vx!acd ,LO^F!bCzL*HnjCrRV^)(W=;uGKl}:9Ƹ9m,s90'AUԗ 8Nh\HMez'̾:͍32yu#q*&fR{"ﴷ z$w-)ْRj>I(sm_-u'K6l42Ӓ$Sq OvTLH14)+fD=O2`KErB> nWk\64 1&ZfXJw yޝW@K4 JLwCvHH֕Iaڒ3A j)Fվdrڳ~mg&;P?qotʪa:G~j/>1@,qѦԉau̮`iƒ3ȼo2' ٶ9t͏g738_gS%^\F=eZ&'gR>9ؕ oF9H_ M% A*CN}Y?i𓡞1㩴x ]3HWhyqSEmRس"s D^-,.\~e0ɼaZ!@Kng &pW)X# J,+AM3/lޓ'cFM`Wsw~+O?Z5_\1D4Z]--վu20LWXnm3p+e;" @8O/o?ZH2 VNzk GȮP5qmtgciWdP29g 5zwCX|NN^$L L& O*^\bZQ҈,/XGDq`Xaa␗N?RHG`f'X1ܢmOL4Lۖ; s$i vdގ5 Zܺ:KrlosqlMjϑµmDh;  If8$cy0(wpYX;Z\Y ؃~!., % $*v-oT%Xz~ <[*>o{=-7 go|y?zɤ781ǒv'ˇd)~|r@|~ț@_䁡y'+"7x;'z]N y0ET(7#ݭ4`UⵈCBuPQf qi|&?sv\9H~7]nRA^{]h/9ku%OA@BbќHCN`dڹ՝ UǼBmg2yR#lАl 0Z&~lt#5޿xI!_6-H(}n R[Iyn$ȃ k߶|A%!K*jqnėd ɩ~2tKIJoT-D,m>Y}OtO{wwV8/}<)fR"| o\ꔸɼ5`Ikp_z@<|\~(E`~+9c^0!w]P ?dB ;s"0WimEۢ\kmM<%g%~Xx.1w3Vs !;_piE|͋$wZrLP sb⊢5j4I0xIFOAVj;N|k[`^)YI+mճ Bh!aK&(l.)}n-0I6zwv?1%.8&7'J!]tt[&cV 6.āN<<ךu3,msK 'ӹy !s7j lK)JCߎy,*4g v҆la_L's/.ܯ7( `! m SUiuAR y4tXAttI-zd.Q'=Ibv$,B}}3y9K/FH2@%4Сe Ggyʬrؙ0lc_鹘S,KyPH VҒ,RҎy~mLdE&\ӁY|x*r ?Z.+$Tgt$-(T_aR҅/CEhn^`W0s2O5|i_~ɪ/xyZyۈ0$Cbȗմ `Fg&FƚC<.0+I!RMKn}$g]E5|[_~F@fhvNt%A he!!d ) B1\m b( I{BJDo.'!\}w MB9ro׼_?[iHKZ,Vse6ѝ\8Ruy!^8!aqahY̳ӡjT!!tySbVS W1zԵJJشV7oc1dTjI0S‚@+I[6q0 z3AMa?Ŧat ]PY}IpDҽovF`SE0j*FOδL;vi\gJ ij<9 {pNFsBBtD5'kh*s7sIDT޷UuDBYK;3 'o߾%"/%'V;EYR]Vj#㨧˟[>D\i`HRh|qe5Wr"j%6}Hf(Vx LC."j ;ô:<~h1i/ [-WgbAٱج1/p2EsKHOr0?+¥ֺҒ[΁͕~- DW A"diLΊABL"F'AI%'[ 7Ly^0:݁&EC^}~?>"~Jo N32=QHxB4[zMʼnTj!Z#]GϱMEھm~~m>3pӿM.8y|t&K>s8FLJhӧ~jUKJlE\3?+bH0I Υ/"aPWgNYQdAui1[|"\KV $:0^ #aj@TU@Cbd+ xp峝+r4 %pi;`$WJ$9CN";0% ä)bA"B@Xo!B_;Z޼yVjTKaƏ1-;x 53")éLs7H$L9 w<%0qó]% @/F+^ڈiKQ0UvZ;LhnhH0B݅vnѕ2_-%su>v] kԫ>nҩj'̖z6)!f8Zb[tB5bN2"w9,yY+:>3$C;ͩճ:^Gy:יIy+[I:}gBqe)as!\qr/$k'\OP𭫅bm!d^̼1\i 24QG@%ּhW.*-~Y 40&͍\Q/~  `9cvWKJ# }VLTZ*BK23GTl.$յ!9B*^$HcIP&\I^H۷~OlD0ۮJ]A V|rE^z7o~ݳ/hERY6Z#P+Gn`o>bM:z5rX&Fs׫2yo37[y>.sT%X6do)D[@Υ js*G/XF["SatXh>um`ThCcctgۈǕsr[V sMpr> *jx'X jV(皙pS|ο45(aOU\u J;1\;XI uwَlNO$ 뜷-hRQ0#xei{Jzvzѯpwի]v':V fBl?Oځى ȱ&u`314"9AnFxkb陵![-rhWy2BEy1`7"'Yj- \D> u9*"3G\>5nCB,1bݸͻ*ALhWdvά Ѯђs˧9cZ;<.FlJ[b> 'Y=3nm߃q?u[ qkyo28h͐Vvr[.7%;+%H}{\R (Jjl,"\ )GģD$x1h򃖛n*̴&RweX0Մ 8sZ`I|Cð%G\Kn3qT ]A/.f9@8؞@$S͕v# Q[CM5D;'3bL}㥪B7yS:1 m/>)H?m"!j|"?ǏbK!Nx (ZTa qm(DYȏ Ϡ% n)%c+n6Uw B\Dž 8[9]3#z֯eǀI?\ ɨ%ڈ;|1E^"g-u]q5$[8"2k{k ~hFCk+=VqD-i sN'2 4ɜ`=̊ ޽s}[]Y?7ۛ-ѕxOVN/^]iை#< f r$Q&bRYAӒ`vuyFb]G+ a L:Smn)-\8^;a ssKEw]_5f~w2L7pcP?~0n !L};y2*RΧ}ѧڳ4@h.~1&hȌ¦t%r}ZZ~1CE M1UD.|YiJYڈVs\1d!7HNo-ev9ͷ4M"p?Cnliww71Var`)zi~͒{G_?_VJ `B̺nvW(M< [:3"噧%]2i.nN{, eEx]gnfcR-`HYWzhxry*̺%8x= _~D[iys/_hKxVK?CV|=K@P$ $w߽|{*ZTN>FQ܃֥m)DII9ȜBH2CMv 'BsyFӿѲXNc@J"jsC$'dd %9kИ.96nRɚBpgi.i2xE XZh|hszS&W,Eggy!fᴼ%\"$K .(KA4I> ?r w9 @nP$B˗/>\!# ӺIĩ\]-T?U?+0y[@ޡYوpxmˋʫ2d e*ӑs0 1%Wѐ۽WG^d ,Rslvy`I`0(&^5V0?˵Utn{\(rs+Vx>,=HڒDuh'AxV3eGRE+|800Ъ$LɯHg}A3 hynww}?ߞ3K[.C"}t4ᪧ/Ŝ o9M 3~+9V82MO!41JOIɓ[n*]b"mB4Q{LN&H+ 5]YM0D ֫D_ЎtZ6(xmdﺜP#f$ d,7dP0%Vɸ4FEkjh4dK8}a0k5`H9{$ƗtZn3ȣ+j<;xe8!L@O  |7̃,s@4dYg߉7QKTs9٫w&W}7V2y ͬq]LŞbe.Eܵf `T+l82<ëT|~*Nw=#n;4!KZ.//uKڅ].r~:yܵx!?ǯOW6F}102crH5Rȍ54>w`Yx2.3odg)=58cE.|V/OyVbq (R &&M} yH1$lx&Y͡d̼y[ "m2'9!gѡ9_PB@Gn6&-Mp4`6nv5$ Ko+ӂ7?8$E!~$ E+#yZҕvyc! {hǐwE哼a3b3NA y)}.Nk<<˲Ȣk,++,o7٨4 fGnIȻwHLHؓt'!5( 3O?b2`yۆNxOο-J`Vs'q%첱gWd(DlG["HT%g!ŀ R JU"B6~j"B,GCtN5ϖdPct*Ii$YgogI`vZsٶdk?s"|g6!UFktL3>`#\X0c0(&bnb!?OD.Zcmcbi WA+̦)GxdE +q#^&T[„gۮ<5lK~+ Ϸ;Ѻo w ޞݽgZx]x*Y2O hq1ڌ\sB$J#r7ۇ|bC@y̳g7<Н[lmSg)ka:nqs{"Wp%cWTLjQev.pZq|="Nϴq) B~qhZ=wM2IhUPo! !'?i%6xher?ݭ6o4-I .\nx-:c9}*?UZ v{P߆ȭ:oxaìU%`tEylV#ALxNܒ9vnra *ӹc&"VU}J 0#~Nb絆GIPn~t6$dO%5_!E6{'[3E):`;eGmy-jV; W2A ['WB`.ϳХׯm۞v>k@95uoV-]QbVC -+E` gMݏwj, r8GKD(0\Q4 []V$^tb_*єDZK[y#Íb! -|!AS&/y.*}̣mAl)EqZc~AAa/HV0R36+|""P2ZTog̃=fYW(KE(z[B9L{`!yInt=uD7qKxHfyL?2.ĂG1g3 K& I7qrA;anns)dUdBPUv+\2"n^t`붋i1l-@q2s<+4WwK>YV9ه ?W=Zx ocJSJXCpϛ07E?lHS7çUu>yҎaD|b3~ahaHW ;'x0MyYcU6#=e0Zb3/v\ h]4Ր/uG<ԈZJh!M"ɗ̳wxe"yzmC0_9hOV&?$CTGsʄ欮YK6͒F_W vGYPrșL.΄pV;^1$P%!Ueǯgn%w?2!v aZ`LCe!ǵtbz ߹ F0 [~g79Z0[O?U컼XgE5ţ u=Lg\])Bk$Bn# mU,瀳l9g81Ę0}?~Q:?g%BB%VGp x4 S8e7+N6nD[c0ӮʳKmra/I`Ky9//Gh7F.+c\ign\rE$9LHj]jݹԂ^ "G0*IIwno9Kmo[AY(멟Y~w'e{psזO<յ 0Pu*,9ēkҢZ, U_66yR5wz<(WEaҠG ۶,N #C!cy2W6}ڦt3W^`Z19pP]x,GBɟO\A tx0- 83aCcKDA3p#Zwiw Ngk!QIs*!Ժt$ٻ]Q%vp?|)iZ&2Is0Y;xaL0{VyFIMï=6;#.J,~:Ml{^fRehi\$yp];?U5/ DXV QtV:e3dP{.?3ײdl&I9 rh@.F3 CxiL$0_~/sDNzۛOnR7dC_ iXCy9v[^Q"ްpoO?2 {̉m1JF$P"r‹HBxk!pVWgZ(xpMH'gtaN9F p8pw%Ī[;l^+!Gpj>jkxc0ӊi6Yw_hھTw+xI0-`, A\%¬΢X5OY/Rm^ēȍ BahZZ(lDT2g{6ҕ0B7 ]ʥ6c84! b-0ȋ4Pc/xFe-t^>VچFD2prD;ρ]y+TT f0Ā6f*טe F> c>"89!y9z:1nle,t;mk|3A_= C$9[la`xH)|&rsv] qK63GWϊa1ˁK,t!g5L%^h^4&䘫,.0&]Mbf0T rmH?3@h&|$~!>rIWy)39 t( k۝YfÜ Ȫd.; ,xz4` ;-s{߳Hu'c'{ov|Q_nY:aV7o~v4T_ꇽl`X| 7֫#ݧO]G߱`s S\Q1"'`}P.5  ָxC)ftȜ@ﰴeDL{:Ӱv=۴ ݐkjsla8Ps5Au_JZI>aVJkTB<B$FկZwCXZw$SEUk~|#SL5=e9j,ՙl#DŽ|1ۥT uip}*r.2$lE$ژ 30G%0_$0 9$+9-7 Gx*A7"yjQZpJtzíĀ n5Mkg2~~s4[Ʒ( 4WHw@P0ڳ?wn?|VȽҰuƪJ@>TrHLލg8ݍe7V[mGx""̼!wV;@ {-b-ceyF 1ocVՆO.BuGL0]:Q[=ի'O4&oX{m>|!'991ERÜv#(\1|rw{冔z1<;1կ~UA$My! ' Z)@ qw PM潙O`FEX BCrD18CnS[&YyfIJMHDI{Vb%OZo2-{[ML*ٳͨ\xutZ!lh0Tq2Au\J9?SA> e|%6#g5c.3BgϾO159`ZrQ@i\l_߿^@^s#[H#;0t]<;uǧݶ"z˷Kqb'k2w߿|~{bHfww;c/UqewG-3)j6Ry>#&uF]" Fi%#v TR1a.H$y@Ggʝ2g۳{a'^*o/yNGAu94TKuurE^}p&Rlu0Qޚ(Κ6E@;jQ ݆[v{"y.z|~]nH2>i'c<7KER[y#rcz#b&L;.PE0~NN&ʧ6?Xs5֞2ҹMtE } $ΥpR>3T,.&Ƨ @RxsgTX[TmRkg.:8^B"\*Ҹ&]:䳾&|5r,avMF|YsDT|vg<0q(rV%('祊^VSŝ)&eT{P$ߖ1l ˃]𮘈 #(S߫xYQu_7Vߙ4/IkIXJO fhʳ)I`mL[B$AnnRbFGi^p5YJkc`]fDwv;<U\JTPՖA`Hޜ5Wx|_/Jk>hJ9tH&;6NDQXCVFdh9⇉.p`Dd9bݗTt]~г21<=NF4Af"/F'F*(hBI&i51ܶ%9}YU&8^]aYՌ6Ѝ(&%n#bנxJLfK"R&~L<䑶 \W/ό(HQܰlPN5z ['woqwbc@CIX[gX O{pj8lR=Ɛ;5>˗FM8k#%D(]=}Pb+уg8n *W˺rM翔YB$bkO :!\[QW^7I-U#qSbh`eFى2VD@2c^qJG!/!;Zw Ivp'vS۷o^od<}6}̮K)mFYP̾g OS{u]֚1s˻X/m-`t̪9 ~l_ 0YZHYg*竔)|r0ح*OWK u7վ*ñxm}'U-'ܞgɧTd*6%$ᬘW.w|,oޛNY UymGi^.yҏtn^>f͹΃qe`]BȍkϐS#xy-(wȰo<(sBl5қVY6i%>!H2s ^+V%vi/д-2츬˚װO޾}g_y]7[~2"<;<,kxt{iŪr&"DkkFR{0-#js6P1$׽&.?!9Hy Z8,7F8ƕo^+mj"lg^6ҔwOȵޠ !`yLov>+ghVQ"=\wvTC'=K-ׂ90xdDD;na |V!8MPy=&Cd Tm_y?-'|R7%#M#NRRG_*ђR=73yZBo'Ǹ.Zns}82vCdڹ;ȠEukKe/!͏(ϞZ:YoJ&H1|4#Hڍxӎ!\WZ̲8Q8ɻpzuhDNg I0Mt(]W7`CSuQsS?y 7o'2ŷDǁm\5LJv~}-ۘ7[J|!9Y;Fl?/O<7!ǒՙ/N'nQG bIli#v+yLнNo= ji#&}CU'K <(Zd@5pԿE 42w6 6`].ϥE\]H'Yϫ8+rTo42MҼk4%_}r\`rTvR7>?>'7 ^$NVa,6I ,&axΊOr)],)Ameх%{N%oBC Kqj@(\E .CVkeBO^p'> zŽ00x?s|+ 0E ӷV6)7?#P9<XЌM]1&̂ݤ2Y FT Seώ!aV<&;Qdm:~+{{>N2H!\":NJ󢺄$Zq m^m$(12p|] "kzb. bgZ&1hRc 2J:)A9ϭ 9 # Py?@xjEB;]a\o߾-C!ys A@s8 iɇ`IVH.x \%!_ۤ$< F?gRd5sɩYۜ~6ko<8U.qMZ'> A158v"Y+U矷Bàm$N|,cµB h‹_J9"?\aVCc p_;|_)KbSi'A ֐`p,f~1 ;@.#J#`u'5^p1uf\'9EEOeP}KTg %@kmp3|f\heZ  89&=̬>.cjs-?9rYT(CR;8uBsp5h# w$1/@5HixkƜA9X`t7j I@77opyo/^.뷢 D2g AB:CH1=y.k1V%%G|R[x6t-aNJ%syk&d\õy8JKB\4И:r:RQQ\n7 |yVvF>7_a3:a /!ȑ听ڠJJmto|592FқP 3"x)j: M3|Nx^:Ok˼q2He+#g7o޼e2W/^zЄOEAs lJ>?u+}~9w< %0Rw޽xs˰1'^vߧMޱK<׿X܎YZv- `<؈CZOxCx]vXw1Sqa 50$V# rE#įn݂t9.2\8fQגw<޶Aנ C90 {EzQ<`$ig~: ;prsuk¸vTϛgۉ&7\wGC>>HDH{lso}3??Y!V K:jS kF)3v .I=vQ{'d3'xV1'2YqP+eBu.$w{:ςW%AEA"OVޜ~]uh$pv%zT&kk&gxRrI0v+`. n̒޽<|Z>v<ZYy^߿{߇ׯ^C­\!@|0sH9A3?\ ]K%eBD2rn1KOw$F_77.9V܃襴64s-s;o~y6]_hM-VŒ.43F0ڭ-`ZZ2!Z*a0W)W]= ۑ3rBsٜBYdr -Jr$% g饔-" p<+݋3RXfv\TCLơm, qt9z?oJd/ k߽||bf 3z$NZӬM2@}͑>/*lO2WGu<p>jմ/*v˕Xn+Al6h?}vKham)($E QƢ&|d LD'j!tմghIg;3$UٳXW:Β,_ ֟>~s #~H_>yFGӛޟ$7VGǬ<6Tʷo=T]y.UQ1䅙y+ιJ@ ]4,1fAyPkF1S ;6OS# xr49GInIx,+yӮwzseM.v yP[kY|KYlU%UaP.SIX`>J*fxlypK i98)IGa9_qgd|ou~^'J؞9_3b9e[O OCm,+c1mfUXK23(g?pUle>W?Lx3ڎ^\kO^rVx 2j*]̢/ s9n69]MWgr-Y% C*bY]gLV5cD3!\JΐԴχ4V,![!Zfxx玐˻ |wYiTFs=Wj<|Z< , |f Omhs}pnHpZQcgWdI?&eT :%sE/܀okUc 0匮Zv|s^r-3Ml 5LtO3#|jګrC K[nۅ͕P ӂ9<?3!2VaPVinDPZ-”10̻U6i`roFbXxښG]z6wN-2M 6x6/dfɵ94'k]MG%0K c,NPk0\,g4j:5_2;$V7sqv0\IfȋaAZ5~! KnI/͊ʗ'?͘"q :H%HǵgB5U"Q䣝 |,\06:oUT&us{mϟ9(K\;Trq‱ŝ8%tՍՍs| FALԪi屒I @yFww~ݮ:nLFۦ*҆ QShF(|1CMǮ ʬ!3w'N"IhMӼV|,4N &}"-:mYO>gc5P§<`˛9Ser<[˺Ȱ99! vB;-3.Q.]x$M*ɺhڵc*fn9\j}x>ӽh׎A$lUߓ(D2fi&!_zHWX̨U|j&k?jWrC"!.۔K"9%|<1 ۷@H͟uڅ{9pʍM@bqiiOrJ|BN՟-KǦ~#Cv|&Z:o'7'9~E9/~}] ],obOض΄nBu੼L&h0%\If?}rR7?9I 1@9A^lj!]msaxw.⃸;$hh?$0~҂m,*QMW+aoׄ,C]gBrÓRFhHT! ?T"]2ъ;K9+!Oj ʨ=$j 4N|9!zUf cƅ$Ԣط,V:ґ[x8Cnܨ<EKzûӳO(/_O宆ɬ݂V1.MkFCq'3w2krHV`&T_99DD- px] fbR(mP2xR~.RH#cp-!FPBU??cF{1h+h*r8ӒYV9iTq19%G ,]@!~;`݋0c|+t kFPn5 g' Ӡ9>=ep;4tk<^y>}:6h O*-ݻw"%8KY=ߦ}?W& g}Lc't!$تYN8H1_XrŕpC z :EO[.@펬0$|j)=sA!. =˲D:gܞg/sErQU{=/r 1\Vtʬ s}ۿ~^7ɸfC.X'iȼnLD=|%?r"3x*X-@JVsvێQ"ѮPT(%Zeʺ'D 4f1)nG0Q=o8/h'@zVmTVVw') \G^w!~$ 3?Or#*n 6I"g\Dvr%f$c s}0`ssPSIII`f&B\ gt1W,etRE O7.\IFsy^VJxY۞^~L`X A罾#in$aBnkKKŶT$ +BO50x&PK]U`Z-"F)$DX!¶\' m& )W*QGg!̜Xl20!PKq5՘iH%6r~3?0SE<0` vD"yIQSqҋE T3Xȑ'Zd'gbFmwCU4y$移|D*G@=m0`?Wo>=Ͽ߲;;k`*mï}r%3j [t5?Bӈ^>#[Y/2aTz"dM7q6I<%!L LRMw ?3ZԾ>0N8&02ɊhwZpv 0Q,X9a#Zs)ȟ~,ZZ6q̝,|ۘ|0WXRFRd6Y)eÇ?}bMׯ^|u@Rqn홧)K%ߖc?nEx܃"wFS%/oY7ʦN cj. ti #J;w]0<1+|ˇ_*ƧO޿W1ܾ|j/[kR2?Z+%.*zЮU`d|۹3ʲg"H3K ZtN_@ZVSJ<`n}9;hV?OUb`dt&\E'q}&n$^-P6C8սZn / ?5AƷ96"yexT354I{~ W0[-Ǐ^AW7e@X%k( YWy$Z^vWLK;,¼\yi/E<䏠;5hcEx H^tHHnܻ,iQE - vVmI@XwVuů.0e@(1B]h$Tۑ꼮9u]n+tϪyR= h}M M~\=j^%$;?s1H)LYd%V^wb{ds d>! e}ձ>tYs:YE6吞_1 d1,*s!C >l_n@j ̋,-89.%BL3XF?"vݺPpݒT$jȓsPu&!H"E|Q|ͬB ",acLps<_bPz% E n ZXH M $g#R/8Dj2Oa)7by -H[Xa[ɛ;|Ey)u#L^mȳĻS.< EOenkڈʣb&V28f9dYs]@ҫO't3/7'Z޼}[~}ύܺaHwz>)v uIhjP1!L\1TLn\$F )(GcO|aC**)Yrb%D.˼!];k>~}tԍa&'I٢/=!~/gPފ1^rc83^[O?׽Lmp7߾/^hgS$t < '^jmU !'Ua ;ZkCP Cv% LBRG^m60:N|bNF% ye̶öu((B2v6X7)G(G3cѯb+:쮫΄8 I46{MdBǩ%}!CeɢB4\f||8oUiԆ@2(਼̈#IWۨȼhI8P4D!x#I#&Pc`!&K;EO%6( ~.7{(d!ǘn>u;>Ü8l1-+&M_LW=I<ƎIϕ*E3;j̫QHXke: L0]̐ITvI* 6!Zƃ>6!P07͓ծ+b4y\Rj H߿tcOD@EoѽgNGYzęp8@fpUr C  R3Z8YfHkt>dlԪ!d/9=&슗 *2Veg'fmiQپpٱpMH_H7cguR/%/au)#o߽|uJ#پftKLEUӞ0=Mmb}ٛ>C>u"ۆps ZM ⥇n ƆXĸnX&R1|CFA=)UruZ ߿vjɼ-T*5Ec!*<؄b֌V+w!D&(aԧ(\H e@ ٍ=A?+JJ1)ʿFS*HSTsJ\67o:8^BSioӋ_ΑA2[?m.bGt,J-L;qR[Oyvt=Y?aJ2s*D0ʉ84IrRhY˃['3KՂLdkn5zAp||P}Ϣ6ʭy~J2P6F#Rh~%AauP/IP>LIEQ"d="sJhpmGD"VidloQKBAiLX+}%-)=?׮qKo<V*Cץ֐aqbЎGT\R!}&e4Z8c¤/@Bs>7 gP1CYG*AC޺Buƹ;mʹEE 99.N"p!UL FlŏX b̄Kp9AYL !@c(KvDHYJ*B@0* )?6M\8(?cexPd060 jp0YlE 1OVAUI|km.x- x{w:=;N_Ta$jhĀ'6{zpx*=:6WxHsdRwVպ^Jotd=iKNnUy_D|а)6<ޙMTstRfxU  GqoKnςg 41FSd19 ,!6fMx|޽{ Bt}ۚ &n4] mE0+B1b~77 m$2D <7h`xwD'k^ݽ{m7!t L?> =#]ȫ @d>XQzvt2Aa|8|FwK 4/j֙ӝ/bֵϵG,,"=B3 f'k`cC;s53̘k eQ;U`ĭt2ם eqkAJgw@:)×--j`H!KW蹘9- r>>GC[>0U˘'@q캠F2\nhpi3KϙÕx.`TÈ?NY52ϣ@l7,?'G<|$z%d0"BvtCM rI'%'Jg[HtZ% p|2t+Ns9v-keDz4%Sxp8GQ0qo32eXl8sg7F;h!*T?ɽ˧<+N9%,hO 0k[f1 x:%PLy6<-l]Ҹ% f^@,(M7#;8&{k:bM.il6 [yV2O).7eR'0\)_Tm_S_ݠR:&ZsS pMP:``5?P+)N>wpN1Bb`%˓Â#]eq]z!u fvtJ4,8~Ç w:[xec;~C'rb@&pK[9j^$]2!Yuϔ{8OWd"7q5o3;hwhtŧ7 OMr~Tm>lA,#щ43Y"<|8w6i]LΑ_se{sF! LgJEP$DkԒy옻!1hm|fn9Q'S{/DAR!9 y&8@guwQ~3IfĈb믬R`+h|0j)]Ssfttl:bVd>^9_ho'p6<:X+7IِwwDYxy{CyWc^Dz  =8JQtyFYyV-7H%/?:wHnCm E=v-$_a~z9t=xF@ )OPZJum| sx씃 eG!)Q8 fwئ|ژϵ#C=8kbACI@P rB eX z\C:%q A(b] B!ωϑ  gΓe{.q&Oܧvt çm!0A..`Sri5KQFGP>\A֐#;g9@.^WQ\ѭ4%>_0Xϻ[5ORc']igx ^Ynat(;Ћm.K] t. 0KB?S3ORds.IB |.ED@dJnD#i%rP9X #G!F͵W@F)zeA;+]2"n/CjxٗO:C.߳mz51E3_ #yt : nȅDX^i[]GA%j2"<̅W +DlyAiWo;Hv,|)VW]H6 1 se`CqKQ|1Arf0I/6r߇۸rp%DW џ~E2*UZM3 ڔe/|>w趁d{SaL _o' F 9*qo$#Vkyj:q6O+"Gq2kFX3{6|/\6Aw4[yw&F_׈'y_gK)[ #KҮmcꖝ)KD_֕2%RwwѤ$/W.[I0Z:<:#Oy.~}ΝD@_qD,ֽLEϹI$Ξ52%'SN@}?v/?^.K<>^cLߞfYEçO}q X|Ns< !3Zˍr<ޑ96U{NYr[ LL/K&)TTܗ/Ϭ 3tw>[DEHx|[7 0an_i$C&XFRmK|=|s8mC4@J8>eGx s@|i {C'On8T%%Ţ.e-]}ʨe):ˆ}2 3IެCt/=b3 sJ~wueA$g0:; S]2G6td#ŖxR ՝Յũ @& 6AxF Dh9Z]pYD88B32*d'5 8N`(@YS+Ãre2q"BDyz; r?(kh,>m9hbYyb@.u3t*٨,]t̕%r, ̈W,Gd[Ă .Qࣀzl @wnLV1͹n畟P\꒢>L.2?rlgӝ~`T2 IQx]!p?l0t<~19a'(ƈ0?29%+bfG l2C=B~\sԎ跟ȓLՖ8HPn 2Ri@sܜxТ7ـ~C_ވ: _={w QcęΤ]TBZr6](4o*T(q#LhS@"(ޒܙO!cQTm/d̼6J/ú{ͥ) OYN>E/~ɗnq2㘓n\{8E)spl)gx< ̝I'bT_&F"K*% R)J% ʩTzi!Μlt%K]2_J6ћɺ1!9StAuv('QWQY?㘅ÝG1u@0ciمCʺY?DȦ#HfR|%}=\+p1BFpۦB{CCK-@s@y#x ]=NF.M(tUΫQ @L*F w.9Wxnht茺7?13Нa>\D`%!~D|p&H֎OI<5'CZ0 PMϟI)= ;έ/ǀLGCg#w9U>Ћ#i^o|:-o9 ^,qxqΐCY bR>GIă䫄) u#2l1t;%S;fO 3LZv˘Ӓ9:8=R H.,Zˋ)qp;ѡC<{QΜ,lW_-DdX3MtB0aQ vۻ/.N`|n5"UgsUCh8K x1ԯ#RoBW:+}#Dzͬ6b%.v"{}g_ӷU۬PDD!0:s!Ulp>p<-0Fa+&^%G "j(-%Ҳ̠3 MYsuQs̊y '@N!ܨx./&#g|Zb@I0֎*]jކ !дEN3D7zJ&:ƫ<zb!g"LbmF !]͂ 7?}Dq6CkSV e2ö!\!"µ>Ɓ0G%ٙp:tPkt]ǯ&vg?bäw/*-Cɺn吪l6P(*n|z/tӶNǦImGo.h+Gc+◇/>w?l99⡮咿q {h||U ǖ_") R|WBDK!N 15J::I0#Sw"Mg:ycOPJi:_sc'iwQ^膩 9+TC~;-NmjU^й]ӶcD)kAΜk3va#b {k7jF9تpsLltґiC(7d]G왶}<.qՂt1s ]k1wI@p`!^놞| ?us/ x%.9o^lW\CFxq'5]!D mx8ӈ8扯emDyچO3y2uC) yN,B@m!s S]WraCOП?ž.Vit?&䟶a @"ԩB;OT4)1Bе`#^ΔFiQJҜf՝EO/`ؖș'L}ml97PGzV@J> &W~˱(J^:cœ< ) 0+m㐗}T?[_ m+#7?`xmRޙW54Μ_OLKWOUd|;N+ܩ\[ZJygKp{l.lޮr~/2O7D!A-#_?~4iC^'z; VCIo{Jqxe55No ɥE>#:tt< e}.ɆjXM (cxaP8˛7k2Cisc1W¢gc[ܣ@z{vuvC}*`3^銇&Ş;|?ӛob &b{":ywuk.Ltun[('cv.R/t (s|Q[8tK鹿Kf+p`;:ͪT'{yw\o*4'CB Cۻהؾ QT3=w5Ϋ᭟8f|ݨ̙,gN"D<k>˹RŹ<ᨔ]葍%.Њ[Q<~GMH5J3UO"#KFּ_x7o^Ӥd'd߽Ah5PNW?٤c 52_XG+7LJvB{HB.TԜfL>\ 6oUL9Y3!s#iZkkyM+͍ Y+{S%wSr Ƿt)4nxׯ#U27C Lj7cK@\"߳- e]M˜]nOjuD&u{y z]8"+T6<+"tum,`y)}D[xIxA̮KJqpvC _WQ qm>2|zqRËǶZbDhqOR@ág  M?0;j§:1 p-zK${ZB͝>t jH7}@+\]s` sآyl`x# H3==A2p_&19MgRSEV;)Gy6)9O9,ĹqHv&fAǣV0 @vt/_'83}c(m>XYg"j~'%b I9|N .c'٩Mi_OĘG$WǧGOG߼*%{9Y3U%;y+ȫ<=^%\ Y_'rK<ŒRz3cpZޣ pLEH+wkmŰt8,14}PRXh`K06HĴT< snHf,hr]"I@*r3}k ٥->~#ˌVUһNTx}9'<%+na(`f .zv$*UQ[߲hV81EdH U&dxb(:|77ĴG%s޽}ϋFOno޾'(;9bƟcI~jZ> zU7a|]ϛAg ެ~e$! 䞠%Ec] M!lY}' )3qBv1e@,=+!R(('آO1vFT0_=*u)CDV~6X #g|㎚pJZ$Iգ9`>=5z,z4D/b "ǻ]x~`$dS 17%x0#O)Ά :8)gݶƍ;YF|HPU/;[F+')/=w0Wʇ9@,bG2ԩP"dàJuðg|f0%f>jUL)p7Μ 1_)"r]s4c0V|+ :fjEeδ-~{Gx}2'["14sUGck)3م/3Zmć~Y<Q"PZtqg/ưɚ6x jIFK"c"NQbW2qчز';*;d(IA\FG3]2nH♖,#.K? 9[mœM7o0y+ (bEjAB!2n&cxvq1>BgRYxQ0sRFu85tE ?d"q%p bٖ=cf贻!-e-\P#s-X.NnjVB(Ң!>N .pJ#N<aSy-pCSwݳGBWy޴·Eλ)1@=N>|pQd&yv!ѿ};1WV,*Qo #r&{-Χ/_<UVl-If8j/7\jRC5f<%zqpngɹ4#$o{4)ldKy-F}G-] 1ʿcr4꥝u-p*s~Z9Hw1k$'sh3) 'YձOY"F.ݮh#NvwM\/!X3Ms& HcrgԄEL& Qಁj.s֔A&H51"lT{Dx0B/+8G#%gB=UM|D(lϻN.s(N_B`I&/!:oNTsUqceOX?@jp}YGѫXǓq~'yMmc<69\ ]sq U`%…3#.(+jS@95~\FYXLvq,T<b0. HAU|cjUe v8/䐑=4>!px&4]GFԥjjgu7i<‘%ΌB5aO~pxNm?8|0__߽~(WaXz{72 btCK,D<%MD\=`~c7Gi)0=F]עƻv{N/HW[^O? zW5)n78຺دu_2S(#{%]"zJO=AP_ԹwӖr|}ʓNl"Ҹ[6Է*'F6:$>`nh3/)f~C8ymI*!I; %5`H0Cm!l)SCJ&kr @ KEM,b8Bl)IPLw1To;'PNfr'7?xϽ';^Wy ԨvR4_s p3*\,\mds""r҅z{㈀ZJtӟiCe(>1zDΉ s22~G@'89l±"[O䒪VF g` g.?9Ó#ruEJL%2)s`x6G0Q F70Y,l0p<`v̈́20ѶK4w2to *ZY0{+L,Ǐ7o~$6g!89|LcG~x&Kew? h󾤪) {l!KneDu%j+z[hG|5=1_R 2_C&B& Іuo]"I!i(sʦ89T`jKdQofU3w9gND w"qEEK轙&?<|)lnܽwMS;8h=^nD;DTq;D.{5@Vr*R6K(s {S4g2eVxN̜DVUeE2}iJLȅ>Fpu>m,nHǰ Bs/ԍ/_k.߄ylLz%Snie9>~y4$JZ2 }̕?r=墑+WLKq$=nP^]_|JOk%YHآ%Є'YyKi+M'5s}I=NݞSP6dC* c?e as{7X&N# 7~lF'a(F\;Ob68Ff$M}5i)B1gl`"8! L纴qBե>w۝33%IA;Đi.l?Ȟ_68ppohǜiZ80ſWL8_>v/V5(`^1Y#'@(| D.qb@Gԍ6zQp1S]8B -FQDFe9 b"ez"PAy!nZCmQ.ӣx2kqZ!,)Ҏm@̘ )Gʺ!DjȈbf ̱%Co))sHNU$=N5*glZ~;n] }/N.Tz9%Z>dyל/RB>'/O~ iRH *L@OC-iÖtz;z1߳t% oW0 RUM&rWvpI\aUb@4_32b U-&SuucJ N> E>$/_r.[8wJwwsqvPrg۷߾ZL\L}c~sćLc2RoI.?R,I "j^hBRXaXMR\LnζjVG1S}i6R'YjWS }$+xyzG9b n(:w[ J8}ڍ'}g!>|׹9̗EQoMH6b9`"e+8OBUT lVćܐWgy8NɌc@0kkWq1)b @5c]CL)ȑg)5^XN^={n \i]/Df׷wn5PxI)\b'$FIϷ5Cԏ4$Σ?==~˒ >3 ;C"%ET-k:x<|Uk[XS;^DH> %L;Y秶Nt-#9bH|.N-Ϸ%NqE 66:y,eP4`CJjT(-'eRD!ҍCjɶ(+s gi?ZY;Na\yܹ ~CPׁKBGܾZDڋ +ق{D04 X*<}[& \]\u἖Чa06+0|7JVwuV#We?Q2dc|Sl4 XFVq wyԝ Q8&'.f8:=d<@wJ d-Źt%< B %{b7.Ņ*f-鶁I:ܕ] "u ͋ϏM^ɳcR3pLJzs$ O̞CN΀(b^уFvuN6SwHkruCAZ9Vwr,0=q0$e-t1 bߏN|A-྽ɪ{ w?2Kac|?aM B])rU]sQW=lt W4n^4&y4 R1%#rYЈ Sǎs(Gi#/$l9E0Z\1S>s'/E{oH+.tRNqN+I]g,DnEczvyl\f_m5c\,}>}w.)2%D8'ؔO_ggp<1n$c?-&oLs\ÌiF٤?|y:{tu !W 1;Ĺ$[DS(s D.IP]S2pFlq ( $̀b3at D-hw]W' dO`!F!d3,:9v]v0 jN(hS~Zt]n i9C0i>!B|lC0 aZFtYeJq<01Q6ٶe Cl{59"d`)&pJ]QBc'3dI6y>6h)&;Kpl8Ɛ|{|̒ȥVpzj-@q—^Ǡ5 D1cӖ?2CCPAQ;Y,^4`ҢGiaCL^P #l@W;1@,2>b+{ߺ5ڙTjb]{v; (F0|,~!ƆF|i_bigUS7(d+L^UG7O^_|y'7_&A㋇|6鳉,`R1nRsqU|-A('m9)0'Ӑjq9TE_d},4~kh3)ԅ\j!CS*6É`5~D8Ü$@R86 : I@ʾeVQnblD9ʅUغ1Gs5ϖ;hcu<_yQv$ÇKow7oAw V \`b !@ ׫ѫ%vcUUj8 a9Mc~c7*'Vxt*WU'/wJ2~0(42&s-gNxw?~`0s٭Sǯd:,:IuwZĠO[?21{^h3߷r{&(K_ ˑ7_;_>; @EˇoʄnUGqN?d.ZS88FhhdfTγcK8plz(tOvN]2ۖSʌ44jmGqX 1A\(NĽ]nrwѶ1a ( ,pW>xs%I*DnP+u!y=jnЭB rEwjėSonyȲlR (1+%.a?i9-:98":3 GtZtW 1[D@"Aaw/ npjQ=ю(Ld'rUH^3Z[/{ >E'W޾_){}ߣR^0)N6.aR~EKQ0D Sb3a0tf'\]LdAy!F*cicy#W Uqq-kyΊ!)KxG ̫oy/Xٰ"+QV=F[!:CGk i6(lW-/0*W LtntD-) G!˖AC$UŐD |rOH凶'5Sw!֫?@,cȱr!>P2FP`8?Ȋ}P.fCy왍=G&%wp-!"j0D8eN!0d.D N8c 2C4 ~1_-+!藂vq`\*(p:Vln !1@&utQs/D;U wWs`KU k:%r(c?DQY9?wJq9`TnP֌;闹>Gy`Mf #VC7$l>LbA{E4muDQ/` uIc~(?|݂kg@H (hi02]"sZ/uEZl=AN gzgSϡOy*{F'6Kk$)H+%0`];m]:I9WSdLfbHȱʎ ۲* 0,2Yt̬s:2'!t U] hFWX. %~Sx 49C`jtG3@_wCTN![ičS72K!uD4[O w>zAC SHQޠSˁ$ JK![wTmd<څ3mQh ?~% &@bp)(e]l5 !CbތkH A;Kܕ|b+<(rU㜈jCkeki3j \/ݖ@, Nmztөm<G "Im!L FpI`zT>pz p]2{M⏉h0gGNyW>^&9 +v+I&gy01b`l>0q wI8 +M.fa4`YBCG3sD` Wעuyo8>„-{`D"2d{Kc|3@kџ{DT_1C`;qES5D4؞3q{,q0UZ!P%o7y ]J[7c3D5c9Bt2[8%?N,a[] ty̯;f8ɎR5]`u]9Oş?k:{#ЧE]ֈg˖sq"wYt`Q O.5Iy1b#Oٜr`3٢2"l6hNZ}ZzLe:×Է xvsR ]Gnngh@>Wߊ T=9yNO9<r{a\=c&"Wғǿ3"OWo-ŔǛ׹9 Y-]Ji O&HZ2-絠FC,8qOU9YAyH0ZNo*pRʹ1̧D"I7.3t`Ze&B^%/oo(P8o1%(!_($I @>RHl, Iѓ=|NCBnmY(jqF[vdk] 1+)>TCLtM=Rfي v@)ڪwΆk?se/5J26֌sh<~ſV0 b`B<oyҼߥa|b]r%a:96D\~)EGS "_&P&·#g-gS.5pp[ L`q8ىCN  q"(tRpC!:Q̮!ݹ4N݁^94=jy~;CIuс.C&B'F!#q ,G h[wZUtDȔǩ!_-I(2u78^::<ݻY5S'X`|:'Vm T`{ɹZ8%N Rjz^h_|sh 1<ǟyyGKy\qc|O7x}R2A;شz܋=RYXz)#̉NacR*c1Ƈ.MnUBq-igLD21e(G;]+E8 8q*J c>Óᦑ<ӔQ=2x?62b6ODVc62+m8!O$N? GS~nrT8(S;h:Ő2̂hkvwt{H3h憸D!N?)ˆ ̯^>~ɛiy߄y)CTD=JZ ixO8Ɍd[9q{뷯-O[\kWyo5rU)3IQd8d;f6Q}Y[jskVA΀3˼4rq/b, 9I’vaxRQ}1W^K͋60û)$ HbnU:eH 駪bwt;JeY{z-pdmR[PHItgi;D~R{|?tv?gք-mv7I"it0IͥQhh# M8~ARr Ʀ(h^ yB2ؤK o$'A.+ 6 1(Mh Al0. 4z;g ψ039 e'.\{]C!3Nے ue2 =yR1GUHAm8 Y_e D-Jb2" FLƌ"=G!2 x ϕk@64TM-dӟ?LiLs <Ǩ(~_fb@[OShZG" 0c^&&];͊`aT عˬ.Y_8|" Ch",0%)g ڻw?c'NS,x(G1gh38_szAxwUʹ@$m d]tZDl1446#55 ŸYi FR{!6"|]:2m W]LB; p"jſ<1jӳZԆv:'xldHPv.YqWh52jpyԢ,kcAi[eiAZf ZWK!PylK;L Bla]{i+\00t/( :᪪?<ȔgR64Bjq+l 86QS.Ó*3`YV7xwsqOepf j ?n]n ڛo}0k i2KJ'҅T`yqN`/tQerNY<@?ZM?8&0"Ƴ⬰oosah\' Yab*`EqLMyk"\&h3'Qs,lVpd>_PWNGL'-;Ϲ?n:EXҕ,sػ:MN}xJ̀;*+}d$亘68;C.훷餪5m͗OFTD(ሥx&X*w^DW쬜|\UlmnptE)2LAi%AS 5 eNEOY"'Z%kf|aWmF<1{bȔOYɶذJCR *~ y֠Z f:镔ml/ѐSҲ9<̕(mbp 0Ag |k{ m38ipW< A|߲د@0g$sĀ=|ID`ġ02zcY#C폕Yal)?#t%NrQi%g(+c!]C}x~x298O(sbp ӃM@7:-)3sM\yO4,4xoL0dN{<"5@Av3Vֽ}]"s_O^:3 3]wA(Ո\E),L 9-U]6;`Pcm['sd|FC ׭3eJ˅a1Mxhroœlk99!}VlnwC]"<Ýg,¼T7Oǥ^ǡ,F͵ŨƱT9nrțɉv/mWhsH͛<óܔ9SD@?ى7Y]D0Ю;4e{&ZBH9Geu!}zVFxl0ìKGY$8u$,t"s0y|=eHAr0g >{r}уb9sc+ƕP( ChQCduD'D5 8MљHۋ bقNPa\`'} 8d),rXѹ=9?d<1>wi `R VW 2CF\. _B ڌoT`'E.hQ1m&\?2={dC-elfp 덍&G7+3R])z o޼~Q.n]8Q׹kD)m Uq yO B%8)uVDG!{\Ḵ$ 2e {]}yf7a *s27|߿LsXryAo )T1Q'E*ݸW<,,3kmq/ <䛷o˗N!VƏ&uM)|^Ҹ|O]_X1 қ$RpSUou:$;(Zt*W)u o7?bJ!b ]]1V?k'RyA/[îDDak,g&3yĎUri}*sԽ"N3щ~nvbG e)ؚ`ƃ{=kF#)Mm./ZnO; ėMɑidIBƪȍ X;#P;8fOd6-T92מ` s3 eLLŭfާF8lzE9Pzn'NDۙf3Gԁ ;Ӌ3ęXv4SDG[[?0))Ps9PIB8e^RSU7§%^rp&63;DE6$>tKTbOʝ-9Y0|}?m&S[׆z;+FR ִglA%u\@s[NVMeuf(@u3|{t/?S--yĕM $A4y>w z.[ŚJ-+eɖ/PhFoopDCrQZ:$B H0- 'B"jdt)B!9>sP_BnHi,V}&A$|8Vvĝk࿼%0b9)]pɢ};Miihz PughcHF]FQ~i\F2 M1H@ *C;L< "#ج\#ʌ凢a NL`0\qNlYyZs!.\Z87 غyJɞb6 'gm\JwC;?iZ֢PYs830uL˷7'94GZͽRH*4|[,0#B;@L3唛^ٓG6Dx+Z][}n(3xԱdfQIGUg!<%wF1+)*S7Hz߸+ jZl6_7PHU 3Q,-fWxpx7U}ȇЛ0Lp!0f]l7/5KI̙iH3KdBlrH'H>ʧ'7[*5髗!gZ] MsW1ƀ2葓ԣ<ڨyn{Mѡ&N(8b鵊ڰ͙#8_=Ṁ cNI *"-xk%??%La^] ۫ I59g8(Vx}QGJ.ܠqE9>[zyMb]n~Lwb9~\K;>d6dSf} O 9;G< 1<'nTn"]W=hɜ|V&bVB(Se4 AR W=4DWW+,Bφ.Cxd#v.`1;˘pV3fCZŃstDC8{'c.* 5:Uu/N[dQ҆1 sU ʹ hGY=<aT26fO }J!;֛R.mqrE5ÙՉh>r0@B휆!wn8s(|jנGjQ_`HMe4l\RB}p۷ȥCɟx,5-^O-vu1Or-:-CvOm.5Jio#]2c/4/fjyuSO 9 1qY~8U0>},6NDŹ)K>}o;_u/a4ys!`%jȀHd8`hIw@\(y6 3Zb)H^A>YE'y}hfVmDtڻz~笲.7hg: vύS.cV PKLZ#k4m| v'Jpl3 ?e';0j`-aor[-e>,¯.$֏(ДDd1~{kr_ؗ}qT$\gEr4A̦R{(gZ1oWL.-v΀,KH,4]jɷKXZtz Zl +3q& D|lvdAeJʩCx.Qh 'Hq;dO]Ngn tYKPʃ{p9y*RH7RBQnWRL5SW)i񼼅͛wד~<Wxd}f@2CC(``Kvל ;#'3ND{qNmzlO(Zpq)%"57ѥ%GEUpB_0ؠFFD`RHVKd b?+q`gnC톴{ ' 7:ptȺWK#],tkc҂)Ck!jiઝgC*)1S׎J_3P?9Qqu/o k^{ YHUS3slLƹ36ՅmUQCX]v*v{xXu5%$lJ9uE6;"'a@QFWx1dG;_[0Pv-⽑ P/'|<5 R-SşO bO<\oFѺ91УˁL}w/NUn|%ǧgO%T?w᧟q[.l1OXyNEتne"dHKޒ2~noHgCǶ.0ayM]V[Yt̲Q2T_a7ecr`260 ?aBsϱ&8ݵCĿNg} Ji ]cTmU}kTJ?AdWcC8++89OKv7KDةJj+-+<+aF)!3l q 9`ŧOII(7?|hsӼ;7!tkFϪi8/olf'ͻu'eM  !̢!hc 8Iӿ ͹#믿<͙O?l\~h]<2`q %S.&](#B@p`hqA/FR^sIV<=/oߏu^uzt(;a>Hɀ?˭/ŷ@{A%_Om`li{]@ j2$dDN/2Pl< Y5ma9spe鱍h*B.鹜T2ȺnDv?3uEW6Q.%8Y^Abk++4Fi':X´Q8I;<=U@ign *ʲFsQݱطE/Dx~!i!=itdKcYΌ6r4%(23CLlTg%bbC(N?ʺ4@.m tG1 0}& mQDK@_AZGo ͨv ^/Jh1\r"Nۗ&v9.g%_!,:]l.Cjh@V/&*V×[?1?Kͬ>כy\ "bd׭uiIw Of {u)jCqHO"7;%=/3$w:CK4Q\ѹ]#p!㉪@(2'ɺPB\{Ec} ,:ysCڽ&"*:j.] p9#G0EpmpKZJL?#zuQL%80O^)?~lN0s;g3sR^<=. :nw[,bbMX`PLoÆ2 :1ĥ@r$XQTt90GPl]ode6,"J%'< |eM*OoMZz\7tk?}:2H# -5b9ؒʗ\=ې,|Qh[РE'E>s}%>"FՋ|ϼ.u􋼲|ޟ7$ȵ6qw. L ^Py:p 1Az^׹`V|MW7oǜ5Z%<~sϿuwG߾|72TM=j>c!7Pг/"+; YyeX 4 $Tu Y݈dX1mHE'v$%03}'㌂JDjÇfwM(`^'"c's'x}#1|YW7)6&p%YݛylP>F?űr7ش #^U)`t6qt}!"dXfn%G_`&e ®Fy5$Pg3 >=0=%JaC&cP?3qܖvASSe:S!ӌS` S??圜9-0< qY|ޅQMd1S2u/u(o{>PQ ђs 3|.W:QE? \f0QӮՅ7?ݛ;>Nf~U-ݞr,1@| L|nZ¢ɔf2b j?jCZCS5Y5Nkhq,ĵB1"4lLjQӧ\FVRa6O-w$՗:o)F'>?~_15s~eY߿>|&AII.< [ 4Zf ?~,6YHjK^JԒаI1d%)aBFE-bO)A A%a;EU k)Gnq<ht˥xRܟJX<RCުãTp~&kV8|Hx%2G;d@AmW_ӡJ;! cҨ03*KJ/&͛7;fmonw-#bjiȪ2q<\ǘ <d|b8ZHpG/(GbuuKJ˗W|"o_ іrtap0m}j!S.eBQy0:[k)#/nպ!yëoJLv=H5d[Xט2Ze\c͘3{WgнWҔBo"@wcUU$f&˗ZCxD/=d\jU7-ǑUa0ṔSPMX\?/\JFDVDt*_TD t$'gOyetQyD-KԽW&}j?IvOk0 e)aey_w-H!se85.C(S\ ̦'>tOnc9%K 0$E(0!C|&@|?|Gأh6䴐I<&FKѥ|ja׮ -FYJLpt)Tz <Cc3gyu.G㗎8a&1 S5?e]t{Xn!{# rm"Or!󠽲'1jNB fT'2vt+ Ilㄳ5~QZ%BguwN14 ئsǀh| ó1ǀs'fǯb4q7ϧzm!eIU]JL![VqW'0S6N4zG YYӥB9>עvu@U OԢX}8!3˨B ~lY F#B6yE6t)(\A)dQ6Fs0)88\"W&8K3XԆ LO$>L06sQյِHr_Y3;o_[D Lp(K>ݾDNʍ$YiIoc <ԐKs PTXڿ;ۙ+ݻG+|$xoGqJ'p-jŎ""JBHJp ;a; 81|$=mhB g~A4n`#6)w s U":j]Clӷʩ(eﱩ5g3%Ϲtϖ oC30D"]]7"yVХ$f}$(+-'-_x+s!"c"r%_I6"jQ)o] pGs'+9mF@r%x\ e:W֞S3B aO>f4I:̄jLh}/mD3pq%xsQ[+z 7кׁSrWAOj;G'pYflc'v)GYn_ `J9 :\<ʷr?.J}E$KnM'3؆ύMDS1:ʓtPٔtÀ }w[vdmZ::Τ-17&|TcF4m?~rA * zb]!")?h24u!-jDos)W ԦUMQK2:pWv2bY^Q6Lh7Pqw=; F]idz{Tffg&b[E*¼IgLP4üI@ѭHs p퍯"|DoM)%4>ѹ7@{/f)m6RmAF*Z(SMرܢ)pm`GfdPNlF2YA[L3p1#(Í^li_>>N-p:U9Ǐ紁!nN1p ]No ]}2?C0n#c 5RQV~`1 9|Xm}/@6k~ qQ.Cá:mL_c<\nmvyQ(֢W)磤S`Hzw%"wޗ؍ԓ [y ,y"UǶI 5h$@U8-nqTO BU)O숱1냒 Ru$z,;ˏ,slc="\ 74_ 5tpH^7}\Zөŏ[(Nh߼y; vKۈغoG9?fHkeuCi,[\$QrZ eg_tx |cERU_2+۷2?p_~'V Vf|3vuO;+.!;$.WE sUI5y1q<f1TO17|QT;JhR4םTȒ:soh'f "f$I::e~w%Jr dY^JK7Jb!D +j,Ψ@c4/ B ^-7: C۷{> &kw̓R+! =ʃ8ϟ]ٲQ6N.(B\* 8)SlHP6n7^xH Cz_ٿzήbġ wREgyS+WtOgw?]/3'huu X9$RHnvnRh Eaa9#"jI@sp 5;{;ݢ>s{Uף\ol;{P]s̓>OB2quC9s&< ɕKJep颈Å{WU+Ȼҗ9|e6Z$QY~=wR|gq]DTQ^;fֲIyM@6I&gT޼_9mtnz FT!&, 汷L*y$x.0E.<ӷOnz܈TBg:.%t|E:UM7rߋzU%s&ߠ [0s/{?|EnHJRWJMyy̙-}3Cm6 ϧ̎Äjc_h˯O Ė"XA2 ȕ{`` QR:̟"]WЅ(Զ !0_3~ѷ:9V[@t|<ܥbJWxw<|||esl>$窽Sg-G}'S͐գm#Kԉ?6 7ofy $& cÖnW[qqkix2Ta`>fOWjۊ6m$yE:)]smdFz,ŴKB4K8Fy4ӓtaaҠd-C?6:3Pm\X7%mY7&GfX ,g<IC7 `k\#й2AJ#0%OQN `<-xGzYڢ ~_hYZC:!Q&5 b\"ab!Ljnc,1<_g ?$~<$8Сb].K$Hf>Ec6yTm 'W<,Rb y5|DmCJj"WN޿wҜfa`~7嬖(߸奧ĐPLy=v畯Wo߽{fhy}5ĭtaYbEі+7h~b́-6sIEM9Rp /[--\ʈgCs&E47߁vfw^po"X@Gy:,fs`z>ۋ1dśBkbg (vƒG#*rڥL3Z( ş"yyW)'jaq'xg,8:F%(tJfͱڵ~I1]ʼnBaO[y@'2tƋ("r;Ѻ] }, C.ۢ%ݩ:g̨mEZOq8 yuQ%,(4QՍ7_߾<F%KA ga8NZr & WfN^mT~[RBQ\hiZe  08b]2}Cy="g gZZrM1 صӧON瀾+1 R5U]j5YViĶ{R c2!F'!soR64k`ZR ; OAw:%g+Lb o_J>&u tLHꈅ /PkCX3}8s-"8`1}@NHΤRh,"jݹ2ƣ<l{ q;̓"%DnǿmbKQٙ t0'~H4/8_[3eaU =>g} cQTA68lyhr"9ftϙ8&nL衭kɋܲbJ`y m9"G^2>UpmM;9a qft\i0JV8G?ˉ/ m*?R%,$1zڔ\lWĵ1pso9&;VBo:yG~b{يlRx!$Rl!ֽ봆p"kC3AzvucUh&!:{o_~7=}6e%.L'Ly<;a|uۗz1Ldt"Ԟ\ QAII/r~C$=xLC,HGP4+Cc0TB(x3IbEsuG&(3 ΍_q9?cf(0]en^G;)N_FSfI̗/_ɹY>O֑Bvd}xxإ{sz?-'w?ls^xoI朗{ s;)ٹ줲:)7KY h{B0+Bi!%I֚ r 14`ASv8uSopj 3p>WJfaj.{#gayj]&'}?Isz|JG5!Ws:OahC^T!w {aФ;ǧ͕v_5L \q8oV=Sjsj3we6 eO^+C\͍NTgb%]ϒJбeu0Z]CEQF սO#W\GDGZ n9y(ge޸9Ŗc.hـk28y081gEq, d"[V!yZgR pMU*͗ϟu6sj5rZ;)FOzO|s>? 5pHF]A>z(xo@ǣ-iQfɖ(e*`HKt\ N]i@\;m‡1_<D(cev"yZ\wÁ1\tE\ Ƣkt 4q |8=wa< wE}0pS*ܒ:e+̣ C|F`x4XvW#ܥ-oqmQa;;t}mcbQyI곡A H-hDbr9$c9df]&7hsU0|ʀ7os1PxU@7S8i+NgE0Nh|F!nIK() Vn9l (}TE^80ቯ72Ԋ],x9-+l:\FU:[g .F nx jn."7*X5+a2dYWe[rej2s7[AT [*Y`%>X2X *pQeX4.}v5=3ߙ{g#F#bƜoD ȋP?[ޡ3okZpP<}}=l2//?:46<(tC&@E_zPc^ 'etu,3t`[-{iΎ(Nx,h3ykJrDhi g; h>ʈ{/dѓI!s3=ے.A8%@~'i30T-^?(·BD0dCvKc.RtR)>}2\U)7y.@]/Áw zs{.%mԸ Bjw(pjJzG9+8?9ZY;$seyqفNDŻ9yu;r)_r7)sm?nvJ-.j69rOǃ#-8Gs/iC!V&I9lPptQs1l2>i`&5ε 6ŧ&[DMks C%^!~S\5DߤkGGvqKi&Pã:%G` OWk*LU28i pNc O&\g;u ч07gvj`bOqRD&vd{yc@)YK]fHwh~18!2D0|V k;{(ېZNωA"`xP`@(o;6aJҎ)(.E? '68_ݼyw:sd8O Z;ۻA-M8w 3㡀U8fP\CIh3UDiT\? p9C0l(76혯Ԡz,K]^zչמ+3`f6hl͓%y/,ltxL<ٸ,c8yJcHUĽVg,jlGū!ϫiC_Z UCkOq]0?!2˞!e 7DT(f|T]( dIȈUs)Xƿ>sSeM 𾸺/$ iAT\mu򇿾IFS9P8k%9)kBt\Aye.y %'(>:{ Y[BA_C\f KwUx.$d8ko,/xm2 vsTU_K:E}ꍷl8fj3d^}ws6ߘ| 4}UnnG|j BgH5h{9~"4bx{{SdzrXe=sяD"bi\d,Xw(9ѥQs=FVB賘 %i.dKjbte1~(xqoĉѶ2d  4m_/k X=O;+^;hI(m}fҬ*ҹn.I:<<("pYm7e=}8j L0Y6q f^_z=\&^:5m)ޫDIbU(wIqF}lhkHQ5jH=>4qy؟`-T9'MKyFP 9h}[8e8ڹIųc'7\D%\U+NdWv-3Ib2L[f)-l4l\ (802fVzVv"uie7D tNN΄[88KdklaxffCBץd~Ys l8QeWZd9s2ʼԐ{j^`j>}ڭ8NsQ`r?>>~o |r%~[#KjTx8JIҭ59EDv~O?)2ׅǶ$EPY]gSB&Df M96܀}S}C46}044ax- $En<~WIB5!TEZ%d7 *I8-n$}=s[޼ug6 Դ΍S?q\6}cj Ld2"'ptKCeojǁ{xE˥]/0 ]/~l~'j'up^`sbFI9waįBVEv!)DK Ջ|3RÝ>H9`"pӋ|@!V{dq<i[LiceK*f r7w2B!3NhQ\d\ޕz4dy#a}ެOF ~OZMw;atDnqYa1AsDbUJ{09B;MQ{,FwRDeC?{)k9]L}r :bB2 KprB?~.J0 ЀBLI;&UfaD c®/:dS:=OgpD`GY 6;!gIi 2(6:D=\Ts i8B`LԴ3|7X$JBxult,f_zLE1 Ǧ;?r2“gsyWR b~!!8I = H# (Q$r}`S֞^aLnnlv2 I=Gv!D'eJWbSlhqlS- hk,rrD2h"9%4t% LfDwNZpSs8Dj嘦Q_7J.c(PN Z~bz :K~:@nC : U=Ʋ:k:_-|//$\Q[dSh}q%" . o~9.*v TwDOƐ$N ^79ӏd7uܾN:{Ƕ(֧D",ehf4Gɹ<)'UVr <[EO7=g3L1MPC.nhYYKWF'qkXHt‘H=?'/"; E[ӳP,.5+n0K-8c"|31xCv7-^RuH HP0_Nf.f(:-LnH{zq$NHpRl߁ԣ=+$j dav>>)VTZGs,cC$ʔij+zZ;65uLrڏFD8ʷW{-bAC\*R4|SÐآpDmCڼ%zȯUK}54"&)d>6c"X1G54MܐPU?'j$UxJ^lp?a~gфt>7 @k~{hʦĶUGD.Gb6uo 2+ 4HFD5xFHiGOy;@Ј4H+"(։ϟ[db_b᛼Rߪ~x01MQ2Tݶ}A}BdN+$/'&WbW20?[N,l-|7o޼y+!>%)E77uDiLv$+%bB3-LK!n5 =߬0h1ͮ|IVޅч_vᤲ5?̩D9sD~>2ݢe 7c+%]Ė? Y!nUa9`~3͜+)K1k B! Q\=:= Z`)!hTY8K|*l @g`d^܇u N?<};@&5sXo'E7YA"M0aJW!uj)\bbMmMHZ]D7,C2TԮvN9|s 6: 06G=>Y&>S3V"s,SL`'rR"?| -%CN4^ϐ.92wL)%(a4Lj-wTxM䃟¨r."z'I8y& pǰO %g&As 1=favg}/ӽAK$RW7_իͤ;d xŃ,rԊK˫dWw(9.*nrY#v8Fb`~zp+&[R`;,}LJ$L7n␫y#*]p2(orEU .pϟD[y=ȶEXkZBbAȪ0K{2bS2<;&y'%- d'&ώFO%E9u9[| sD›8 !퀛{TGzL7q&z"p<Ū̚MJ^uƠDi)QwMNNJFoeӧI~R'9."Fnh_v"~s3ۻr`ɔm_iXNX緧T%(rI26YG7N/xR 6*,>s;O尛9W92)Ь)1T=S&y[JYY&QYFg\ u)j-{#D?ɀC("9V+\;Ǜ1RSjWNVpIuFWi}ϼR͛B5!'!^:.leYޔ@kʦOLrB(D#3NsI[t!qKhIdiLP͇&Q D"8{r\) uQPl:wiWqtXN8W@ˤDEDu#N0$ZAXs-!;3AZlMBL-ΥŁ9T)T T~Gu9?Ι}S1<=u-@ˢA)K_Si^\kQdHgܻ`S*1e; LÐZԢOQNdz s_g#๔\ZW=x&gמ9vp sy hGu—wSm3Jִ'&_xX!YM/ QYk9*e;~N {2n,FW e&_{ [ ^_~MR2=P>ۉs+ͫ[%"7ХԽCoD^;u6@O7T*E- $ph[À9R_m/#v06s<7߆esdW֘Y"L wMCmU0בBLty;Z<΍SDo9y@t tc#-Emr;Uv)8D;Tx`  rIYӳx{qRcY>1f(9~^2b#Tj )C}:O gnK`JNgoZ,虡)kg 3TJUG沣MANDzt s8)tX>L\"NqO0w ,B&Hs늋S \R=,XdQ?2$δfP.0ȓ!8!W1msEG A08pw9Ҿu& 3,.ujJT8;SUHA6љTBk=t`VIc95l.I5 (QV(zTm5ŔKA1'!~0#x6\2q3:Y<(pш30E77D9F: T])ɎNv!PM5 q tGS2Н(FdQ\%Sev/5f8W<56DN+6wCr>O7n:}u+ _y8۲CksҲ|5Ċ `HUn$P c"%NGA sYf߳e^9#\kjI^xXZaj$99a+Mnoz?$(}Bb&ŀ[v^`>~{Q^F_qr{aHR0Ww}WűVHw n!' [Ҙ0 7hn 4b^8 <Cp݌4X5ECN 3)/N\Tw&{dSϜb:8л@Lqoon^%TA_G1$}-l]0d e6\mBp,Lµ"-SFOYijG8]׷~.[xj'! 9AaSib< ?p ?`]+D~Wæ@[bX : BH8ecmpx Wyf' %x–jKi/M8`yooC36zHK#DD e%U|bU`O:hbs ^SCwmzYn>9Cp]z7K s47.?(]UkV,Z?ordu!֘vEYHb<Ph abl[l NZPVmsoW.oy/z<\d8$em72b:o*<~e/N;׹@H.{Z5DR݃HqBdipyl/Ƈ.u4"qv#.xhF,PRԽy/ZO5g:(iSH<)Ac-OtRN ;^jx'舮f SA]@Ĩ. FD2g?0Tiy-wM.N;%iG+l^A]>Iwoܽ9}޾ˮ i8)ڦspX\,U^'G9 F9 -yǏ8Nd^7%5rUbcgn `ʧmQb'p#ElϪGBTRnZ ,]W ig3j7C=)!]{wjwB}} @w!!(Dݰ= aVz;)Vy/;LJ-f OgxN 6] ]5!֝CP;mc]<'sd#RA|AoO/~WiE+3\7#dfo[NW0'A=ɿzxzx?gyzuusV_ݜ'~pcd2"b祦e{Υ~BMIpi[mWge*bA N ]< 03@(3"Mӈ>kkADtE83[xtbb3E.'`<~Ji)A` 19Zn$/P7Z(vL]s `ptg7zat1GW2_ulOd`h=FcZp?gx)H"YKd!QB0­^::2f[v>ζ_#)Ŀ>[i_ݴ^=΄~rWubE>ޗ7 U pu[k {AcP 1Gy̅!!˘ѹԗȞ޹}rD KiJ`V I)^C1Q>09;qސ.H^V|"Iw7I1@UCaAqčJ#ǃ2eLi GDiW|x;s ΉqS(.06c3:+XU n 㑥7tܨ,(sc㤽f.:XawUGVNޝ{w{"[p4'ޛ`C1IJT7IN/(. "53}5)ZIv>=E-ieLHx$)W˪D&u? *h]1:MR\d%gkyC?d}A2]9}ˏK]o\F_W~)\XD&8?j. "?(,Bv}$.%$|A Y.L M62-@/ݻn j2I\5~Cx_V1cޣ1x׾"3ϷO)%'Bd-|Fǀ &K#WhNŕH8s&PMVue)L<\rxap'(8; ű@34UTS`8)bh&ntjpCyv1vЩ čN }Cp-zُ^ ͇ }Q( k?6(.있ՄxNQ .8P@,^61:O)LhĬkt酌>>~<> %aR[2W9vu-6<Ғ\v@Gh1 F4(ɁZgK9!d(g .-;ܷ>8Am+Y%ZPcMqNʢwAđ"Kr62$zչ$R:4U>q6 /tq,LAڕSУ|!W/wndǣ|STo/;9aX!J ?! g_y7ɔمSWNy SrSPnJMm(⌾`9fEw39Xg#O^Q)Kj:cD0p.ۓlFI [ VJhlעhm~)ǨٱE2..tsaÏSK[\My猿<y/WpW,c`袉ĵ;3O,%W8~%0.^R2ډ^ D"sw{я\&b)n,Fg!]e_or60H xPN` @vi_QhL(#17F'B m $&/2)'%&U]$tPAކyhOFmVNt еQU01i= d D܁<;٫?G+F8?{_KN=p)/Dˀk`6^+^q`G4ֽnί5N+F qzpB-W];'?`*FT>eBaV]:86T"޾}¡)`޿/CŭtD9^)ׯo9Փ+CnUώGdžBLDY|Evt/u#@\ѝxaz lh'M2|P($!1: rB_5^׀Beh/ ȧ{M\\(Q"#!C 'KvrMK]\`@¨ ژkE= dHxbŤ{$6/iFɖ-)BELz/6YXÐ;9Cg=s~rۅ̱?2f:;X]Q,A#B8 pyXmR*atz'xG c=vz橠w(sr\SER(S,Q>fH f=5(Uc]V;5(kCv|3'>eBkTKjN-2-P; ۘQ. \'>?o  4`<ˁsK1"^]9?W ?s,UH\7m-ǂ"7\Blb5dD^%p4Œ0 ym]MpR1)A$ vzNiJKxʑiM'QMb%C~cq`f ̙8K&6ASZt%Uw9S1>] 0q)1ݵQZ:y)9E&8_18WF\=hgW s QBZ?@,/GL[b2ph;9B;d&4{@lnׯsʰKW82vdkQ'2]ȸ$T[Eٷ 89(ǜ$/pvxx=ˁ#k]2աd]KzK=!9d2Lmֹ½p7SytlU  ˹Z~w7INRfWFy$_Zf.UwaQ;eHgD:&v*# §# ɔ%G~J'&/ %BDU S3ɴ|S^9ϞfeR(cܘh]XsC{ -uϣgstUX톝S"zPc_B=ET`~ Id3'3yGs:4B8p\Vr\N=1ʖע_FNf Ko4SU0E!HDvEi8>}.z4/oܽiǴbNhX=]/xdf ,[)lnEI4d(鍊xAwÝ%k$@ VB'Dg*0WjKM9ٿ:xFl fukuq,]Zy5Ͽ R6T4>>0Am9%-{nnoWU! 1 9`'G[囷o1>9܎{{sw_~O?[7QYW}ALMip}^&g]}|}mx?Hl䜽ODxno\&ITcT42VM-2 >< ur*/Iv0elM:H=_$saȚAm EzY Bd% Pt|I{.g H d5lJbQqc39īx* 6sq+&[JTZ9 ߧDPU }=$ЦAzFZ']W\Tgx)`X$5/ϣsn|߯vښ^3.3l+ZUzFs[8׳vn-KQB{I]P2gz˅T aYW_ 8J1D΋%ڊ`c @L#@d1C%ȈF(DG(*혵d0]u;Wt ۀԐI*=pGQlk< QzmaЅIXhb 47N='D0q&}QW;~x^S K'#/,aBgZDiOǥ.Xj?G#{(7Mה3.v#_9||OG+}M98SC%j1How.@WVj uUPd /̆[q^h1M(fK1_@'Rć~'>4z`H@Wqͳ~RW\(țd-Ϥs@^-!1eKqePO/?^aL"uػ,1}r7)ɭ*[ipv!Ois殎?cBHV^S} -1"JREɼ2'DEز19Ե ~u$-Zx(WqObjEs0 ۺ*+1vSy]VB! &vs^H|o:LhxLcJ1h=N%܍C\|H c޾}p ~`EIDAT7e7w-~.rqwSuUypR7ꊥĵܝkS]9qO_OĐ$1 2 Љ'4%2܊BRoBo34Jn/ZL=[%,^@1[[_'{5IhOVhIIpD0y 7zlT[94(F~(tRP`_{@œ>ߩBRdTI'X٨RNOsSgW e `<ƲR/E\?^"=#ćVS&$GupVU5p% iGy1rw_aI.G>@*YD  g@|뭸#x2t ~+@DTޞwBh[땵 Ofȿ&ʦn648N\!A|oT ] 90ʆ#@PrmƆ8sCFөb}#LSKp21[kof$GJD4cJng$t>'10F36t-ϺyaZ%A84>4@:qZTˋI?gWtg}&dEKhiEmdN&ji=[qUOZ=? YؙΌ&e`Q*|fW5un"QX6/>/RIUŞiɺru`kῸO,<<@WJyqfy^ pFˑ>b`_H}?,<]6Pūr( I3ALf;gWF[ !GMP*ISq6˕TcUL[\_uXav&E ķH[.% 7بy-2x,:o|2ҾLr[srZ@'{fφ[w{z]k6xx347!ܨc,dҳ?Wb#m´)ah\~V<@mh}D*&u[.ׯ{TvӖHu}pSe'CYBbuz2~É'ucGז72kKOYVN|}<Va[Ob/v? ,@ ͛7r/YO[8X]J, 9Wwy3!?& ]7ww6_>r3ƫw~AA_x<ϏLB%űZx4ƔS6J$X(bb> 2뛄WW?}q~܍$Ő5ˢ[lim+3N4n8`# ncVIl+E4mc|E&+<39 ٻ!|0]sXA$ HIJ}B6S :#ɫ^b+t }I )!\(TҒEN!zs'o7r]$ UܷuD=Yw6Ŷbn'¯$ ɕQܐ&]?<289wZoco{\~B&$2}<"*aܠ0Yz!E?K3$}/\z/߿W?>wd7A.QuɌʫ|IflxEIUɖV0p2=}-82hP0?}͛0N*)j%m=$ Nvt)p7 SYF\t_~Q?#ڎQ"{z 9fh^gRcra%n\i&1C^}}ͤ~}7]zY)K>a3¢}y1IM論8͝;Iba&YЎS=^~|/ō)TU :h pU}FZ8Li0be\H*|-z+(pП3bi:k4uh Yd0@l78|^AP f8œCpZ絫CfFt폼b2)ߪ\w|SIXC tp4́MDUv."\& Dd eT HEydaFpAx{;Tw: ٦蕫 31dڢ~-@y3 xF]fLv<"+:9;IYקi.Ϭ i !*.(aˆ:؟o>Kח!0 NsOݞF'{!0fH)ZZyh_Djخ5Se(]`A/S k"H?/{}j팣]o v> N' <~hj s qV ZKZTY-^RU?'Оfe pWrCIPڽ5<ܵ\}zTyH[F6Eǥh =(IF0׿zW[~Z$eug{@}Nu(p$"$H?QwWR鲹ŋ|d%&qbW :/f(0cjR?%Dgۄƾm Sy +]+k=cOw^eCSEI2hM(`ghۆS==>(PojzHˁj]Ag]nxx#m˒*Qq"۶ۻ̢{E^.ezݛ;nbF a%LWfE7XuNx C?%(̫~s(N;8%j %"=T8b, BKlm)}(:7!E ?0 tqR>d OvȔCT]qb}%a!ݮmCA"ZY]!O5lZF+5NuOwlTjqswGjn($HC\N (Vcw;p 60ÏO_^΃4;h1)+[/ṫ U}b42ftċߍY FqHU4{LiT b2~KIX}`LDh% 3nH^ztoj}ڌ/= Y^nq̣;k%=ߧ~jf9<fݙNb' P\Di0l uD6z:bψ}l0~" ?`eE Nj‡S(-0skO"U(3ʿ$'[m%-iɏx:[@x9FGtF"npRhq!:]ѥnzg{<~l;S F:yڀ>H "4"˫ 0 ,(RQ+['5[س5U'+Q0l#NiL%wr1LYKfknz&:W34Or" 9,W=@>L뛼]e#3=ӆ,UwkׯxETVv2"ܧ1=\t6)Iu8q# <(v, \|HDOkcVw܈orMcM:-.QG4@wVP&9ǭ D+ek73}-Ij㇇Oۻ78޾uqG]H&5u Q4pк8VIV_ӧR#꓁FmyW=&aHL FU>PZA8>0q6<8 :/W9d{&;R VH)<4̇)`8y G?)Sȉ>&1;[GȤh)pw?kNI3NKҿމ=$mu?sLCr\OOd !+x␙<\w 9)7#8_b7_~Qg)tT78mHN `N.OP<}WD=(TϧM +t /_-S Nb!5قZڱm6iv#x&Ņ8x3$ ;Tҥ)9Q(À Y;KIU>"P}D $Æ}J no b^\혷V]&@drP-RR} +w]ukQbKx=хDezY6P54% ڦ(WQ N! D Fl .lhGnhE#NII;Lx&2zxb 7rOJVGIH=cFy CrRwo`[J9@ S se I}IO$ a< LI K7t1aT }uu_n\uK&`.E?CJյ[pٞV4y'қoo Y_rwҁ:~n@  1ק]:3E7ZÆh^ A؝jE:ϷI%Sab1nHT@elc0~&:8C%DB'jQ(ش8up7TOb50YIGK_V 0pqNCϺ')~g>6J'=`Rl*!vQ1lf7Dʵhzwf^XcRp:̓L M\+icjytSqFĞG)B:/_~[%T4 f%mXNղآYz:˰ƇS@C.vB, %!9zgooñ0 mcнc=5:NNL.ms@,Fs "t'SrZ]CJg%fp8swulxx2U6q| -Vf R߁liuOez̑D'X8NRO|A- /2z*ƷiiQBL*R\ exs(Ӡ59tqiFYbou[N1CCသ2 Xdޢb<|ψTʫ!7!xd8r PB+eJ3a ~#~%vjIo҆d̷?87;7}cG lP}_*زѩ H+GJsI}||jybGk"AQ# aˌ{"QCJ 9vV<nj#;ʁU@p6Y'r{a{n޾} o_?xի7oFMjH]ET`$m_-jNVUf)Î˧ "æ~EN,".B6 [.Qp瘣A'#M {9kDJx~LoM,"`i.:f-9!8pz3xSه}?^l2Wp ?õy((7EZKvot'!sۓα_G@EʸM P/.,<_Z~L!' 0L y[\֐K[jxb1 ɟ 扶#[ĹƓ#\Pr}yIM}c$]G{b.BL1(-hJ`sLNSK ^23.f#!/_Yi[/>p>Z$6N4#Dcf:{ٔT_!"4%m1X }'t;g ۷+F,KWJ h;Jch۱о Hj,MY^:YI`s9=uBCcQXj!&XCghv餐pt*b8`/pgԠ7Nu`h%-6'd? 2)Gش`rZzA* #XhAsc4#r^zt?Smڋ4 i>1 i9Y|*?'Q<1VϴMp@(1KG~/1"1̙6#˄kY[x|4i҉\%OnE_N?$< ַ,sYի$₢ }>p.yIf LPaa:`Ҏ)tg4:uI1v0:AkTVшd60хi˽+\rFԛ)?UWJѯ,#?)$i٤(WJ՜;l"Nu0k.ESnʱzӋ1]lVoHG @iv~R]}uM2f,?gIv'ANk'V?=yB4t]A\茐@vÅVzP2qsn$&-xVPy"B+XF37[ڤ,7w\̈8j!+Œߤ,\5_=fQGa=F$Qd-Vy2rB+tz(-~ɍE`w;/_R՗ecy#8hI!̴wđx|DҬy`(z./ƕwud}=`I*2d%ffpgDr~d Q_G3\J3&OS!mh+a0D).qXȺ1Ɋ _l=mfBf3?իo_?o_{VUP˷jvt7EÌ3L%E&3NOIn9.fDϿ $7vnS3=46E;xoJ(܊`Fa?Eu]~G!4: x6H7nn^1I["2 D˶Nu 8;Ukfo,Wn=Hfh^}jd\LOQ_xmX)M%L>ǟ`͝o<6t!>9YM59.;G-F7wWkÓ^2-lmw^bQB3}ۤSU~}sS^~>0=^eFk)bet1Jadlla)q#؇.@QC`Ien(Tm&ٙiJBd&Lc98r l@,$-Sm%UԁIHI\f0ZAZ)x<8CR'ҕiM'?TGA o{uɌT*#BZd0PXR]Jg45Hb10o#͉rϩ@-Ɇ⹿.YC0LQ(b&_c syl 5g+4o:Kz:vI@āO1H6R?fe=$i@ O yyo)Uס-t!ƫ|(n8@e~.&᳿zbFc+RY7s@v/4޼}3B@SP>?Amʏ]Dś BԿd_!hrUҺ1|ZU\V(l~a& 7# ii ɿUi1].tkmGCGjCˆl{T.(WO](K":vbF eO/۫뼈Μ*lvg! EVh /r5SH ·.= d9#?ng9W|)m7דּWAm%fgXƎ(_x+HaT/tsld:6 iU¿%046$@(c/VB̷E_'|E5ox1+=ܸd5+"*-+[R!?bOx. "rqrh$6`ɫ~,7᤭eޔ[=F2w}w<ѵ@1/T Hszz6 I< SRpX_Loh0$U|3 ُ?'),!F־m]_NvY~kPdtNRے;IvImtI ݝC(L xِ[޼(4l^a:hsEI8 nF>| LZ42ɉnݚr}]iel"> UՔ!R18 aGC mWdzVqz*(re.lK(լɺ,8ؓJ{yK3~<>|yT$/rVf?<,̑'bc+).3k' )̮HtIc6$F1CDPNmzwʍ9@_k*}p%wa2FBg5IBߟ͈TbxL߈Ur;strK,lt-ɠ!L_^YC5uH, i㿅T.ch5giՍҎY.P.Ā]q`ِBr-^o249]v.B!ٚ=֨vC` cd 8 څ0Uc8!3a:b7Ód I.e$qujyS|MOu䂔Ȓ}fSL11-R9sb[ ϟn.r>⑲" %Ϗ Kaku\ &VSI*KVrcț;꣦[)dMFA4-EHM3áKg;2ٌlFQGRJ-xR KŌzp;sJ9'! ?},/fҟ~&WSɏC*,3Iќle$l7a/7oޜh(1^T_zv"Ji!@/,@RQb;Z<;he ;4tzeGdEÇi06rygXVm3.rT(Bf+!$ǹxoB~so_qJ {G(k3Mg!e#SWȾpSuxCqR'ɫտ˿ӻLJO6G_yGsRՏV3.@.9 N"'k{R.6 JKEgBpRt:ƺ &t)]>D _DS265coxr"(`UcP)WY8hnKZ-`t>˟|<ݠAY=.ۗINM'vz%MKѦ7|oyMni 5l,%+Xz2y`5M5p~iR0)!>0D)wJq]Q2:oC7QO;Sftq>*xO)7ӹ2JA0&LIf7+n`#|~/H)n0E!| Q؞P5 tB8Kf*<(0bq@xs8^ %8I?\+݀!ES*ީ( tvf7J.P%vca%*B$5@rƲ ݆px,NFsL3nЗ x,GUtwwej1_ӬQ&G%N.(Q-3ZRxFG>:;nBHD^1!Ї{m/_QM[G4/ͷuC/0̏F^gvyzk#\.:1P 㩐 'QhОx0/l#nhI5:'d1"4[څg-;=jpdG&y8mĻ=Q{WQvMKCZ!S{IܺKIBrKQD >]̊QHE_\IG+4s2|si[qs@nâ+S@<@RPvm[>_>F~  󑧼klQ][[wXsTr2@>sYa ȽUF3MZ=mF?c>%)/t&_m~:S}wR2/~fU GCdo@zb'@p˞uIP^;_vv(ڣl? fɤ2w=y̨_ηdSA^ު< ˶.(?ljsR ˲*@-D)Au̯͝N9)4vF8Te$f2o *P( Iˋԓ8!7/{~W3m&weQl~wʽ#Ja fӑ%Q uaz%W${3̳nΥf(8=c(=*|C2C\jw2ILڱICX)>hNq`|kɩ6K4kWn>D%ct ֺ0lMϟ۰qHh)_J'2l\}SC}v#Z0Rp}˯|reKhɶ!e˽eڒO?߻BGC !u*AԬi|AGg=ZPj-=U:UaḒ<ڵ*%4Qnf -֬>WWW'?1/i_^aIZ3^p G`?sH65|郿4G$qF]G,Mcu`~3E)Gȍ~-=%C(zSDTvq*9~r밽3*)\أ:ݼlEdhMs=h#u/-=vk>C.fx i#~X&p{3M77_>~?y."w a8= =~qX-C^6M2z98鎭̯+4Sm#.̨flo߾eѮtgUs~as'F0g p6uέ{hyLh-~7$YA7o1lG˰j,nlI`9X\79Glx1Ԩ4ꫫLJ~,*soݕG 8K;.gX$~'`NyEs#:/z5z-%$elB$|.?g'_.Gw7?/fqRq:PH{+,m2*;!Vc7wN|s~o $x;#Քc4QX9 [D]3Oݰ)n䵫뻼9UT˹_1t䠍p8 pV"mYDSN9gp<4VtA;b!qׄ5y!"y{Bdcjtm^5l|^|V(9˼Nh+<<ײ(<@5<m^8o8D[Us:2ZJN]EBzlN ^Y-aL2͓X_)t'@ wAE3ڒltŸX3 rRF!#ZkB'"AAEAoH ^*4 JQICp^iQF\ N' O.ߩC;ѵs Iwҩ=9|?`Y Ll8ΕT M{9\c 7144{X.Eّr6GDq1,k.פ-s=E}+N2#D=5\?eŠV\: +I  Gg+,C_mu4;Lb|X xBԄP$lH]Ѿ'ߞsԥ"^, ./%9e<NJEE`SM5`or( 9Ib7 y >fF=?UvISwr f N( Lu€8 <[)eOnR\w7wܛeUpm>S\|ȽtZʭor-?ܽ9> D*~q!=ق;- DP@t"9g$ %ոĨBzzT0Exr!?'<$SKrʠ<1鳘z4Z32g #/APR|x9xc!ga*^Å.I7d FATvބ/ XWTB᣶+3 &yس&)†,:E{kF6Zˬi .!U \LJm NB 0bΑ:M3"7I&k I$.ںEp-E˻F1~-QM]rLA\A"{rDӿ2ːSi]kKԠHm>Tbjʃ$Y}<>ZUW;D] @]m4T3fyN}BL|AD<պB݉8):Yr'*r%g9_MW7"ӣT@×'PdM< wh{Y:ͤ5*Epy\'dlZi9(AqbRnVQKEaavQ]Lʴ$AfKчr 3,BIF%bjW(j3:C&&eN 1~R&G,\ OrfAHU`v# geC(+5nFQl ǩO "HAdhHŷfaV53Z_̆I, EĆhAO~}Lg.r6zu喭1Vk1F(. '9r-2s0>Sptq:'n!\/˗) SV[~7EL$A4VK.;j^RFFͬ :tpVjVRfFs϶p]%ȜQq`@H \j{]s׷.;D_"Vr̍ 6[_!ΡCt|* zPE"h3^͓X2%Wgzcvጓx=̐. t'uzG} 8L),4DNy]1ˏnQ 51-"hZRoܣtU9}r81/.HbSB?"&\{ D%]l}xɉI盷L4 C҈-^`tCCV3MC!u(']_6q Sv[IÉZ@ݽ}S O$p-yGB ({ÈNBƠ(_,D܆(hlq#Uur"?9+fM&U.0 x2_w9ayEh%Zq1E}}B?JRݺ3G [p,uZ@vI )?ϡNq['\6lq}D/"~3M5=bX -V θ59 8GQ5ӧN->f-@/.s"jٟۛ)i&{Ym/?BU+ZjN\Ybb~BںZRjwMC]또ub3?EzZO߿[(=~{rj\\sw "8,XĈg[=-"mR/jlGP#0Iբ>Ȩf";zN>,sFns2&xMr tW-Zn'y1JD)=€“qFk~,oCʴ"֣u-)d(Ǵc@ˬ>͐3Y;S PH SK&/PB6I[^<+`Fsnd:͛C^w"1%/m])]!Vѳ ud^O_i!{;\y\Q\99)F4Fk3H 7lMZtvSD_NÍ+^9cGq搷DC5NٵCc@y2B^M>5xxQ\+ "v-g'~i^g;V8bLB(Q6ŧ8]%Q3-:w /7iMo/TzESӌ*@UP".@Oy]fv9׼ehGǓBL>[6ff:b6w>`*&܍,O|z:A:rtM O Yڢ2y|Wz@p̜O""( ,N,]CDh3Qnub(x™ a' -dD2Z 34 <&-P(Zj|lHy$WKСUrb}RӠ7Lq4@! O (1 6dhTyD]`rMjHFˀ2 ҲC]ujfTS-F-F[Hn6[q+cFqxԬp"B5kKs`a*j ,6MǨL!X콓C6\0U9G[6QӬEYL?FsCҢ<Ķ, GYRy5$A&H<,e]'ctte yuӔW$Ds`+MNt~SKD^^^e}׫wwFZ_.erF[]Tg"QL8wA3jUI3G^oLSSbf;avοCTsٗxRXb)X"9V7V5ccǹ!hՎS?|~s槟@e3bIeY@CUygsq9:tVdrD ™r 75 b GOvCjwٵZ^C8-ĤE^.q-  nNRW8W\)BfRU,C4m'ٜZi.%2IHgڦC4Uyx~Rʌ.btJIzBKRUR #mqCUTu}O3hr/O0 RJ`nt{v]0m1X73&SߕB/nI|S _]rķ5,gV(6gE^O6 tmc2b#ՍZaz*@RfD \ a\:!p#BBI 9(G\e2ZҞfv25Qs(PQVHHd6"5ga^bžWGAx .ijt~d {Bۡ2߅ =Fz -LjS`N!47xfc7=M; ,1%fCUk.y$%(Z,Z* g'Ŕ3a)zO)5#V b !4RE,vZI^޹cvZչs)>PxrMeƶ̼x%k7}Kb J.Lf:GK8]ۥVSjX~HFs)Fxr,+ !yrkt$ͬՙXXxC72&h6G9+'QۛCjo{f~y־9&_g^sf[\(IKxܨZޘB۬+ )(Ȑ) c({);↲VWTwAG~쳽>9p(LA@Cɪz\圼"œ('B\X8*xJ $/8(:Dݗ->3_ k)j~Up^1b|s\0$&c(D:Eg+6.%2_s*uVR,{鳍g>ѽe2Y0jH{Ch8{%=S%jt $JE[\S˙ÖgtHl,iJ,T. {DH PJ?9!d=t-0UkB?!9v)<7?-UJeX.lUT0Cby̗O??Kgrsd2#}Ç 'aՕ4"uiTS7.(vvdnfLP>F1WNMs.j`Y*,5TmzR,?)bf1+=>ו#FkkIP@FGŕ1$MqNز~I.efU!-%KIYG&uG>S+m]`9 %߹[a?)ٖe|> ~xvĖNBELŧ ޤ9' Qt^s*ds`\˟#ታ7|5۸1I$|m%x-o[ F nʲ]@΅l<d %bF1s*9]˧m~9ܽJ/}{E[tCR]{xu'Iͱ+`Mfơ|]zM!E(F^\rd'^kϣ F* S;[LLΡ1v(JhUb {I86RI<:Fq/S * l,!(98U}\2D*ùw?rFo+5Nr$Tw,Pbu>؆D:I< hi~qmUBMsWr1֣*W^,3MR0$/%vt Ͷ+*l(2=*:A&D0ΪxklC,ݜ@G2s"͡3[1/O/΋J 7C}0 tQA7:_s>P [* WUfdJSBrv,Rݏ {Pwwol/SIVsİ{lj3rE.wkϡNaGx_h!sDj,ZJB“88; &?*Jxxw:G drͽeUppq){ w[FC&ĥ'_77NK5`(ɿ}7ls߾:S*_~~xQ|7c*P9w77n-E&7aqY--H v=AS:I S$`d_ ǽZsMz<7:X%P{(ޚ߿}s;~ksmD쌨%w8䞎s 9bm}vpMl':? ^!+'q&epJV*NV60e}AC'Qd1٬{pHQi1ñVU& Q"r3Ldf>hic Q+T/R] ȦxIF\ޢ*װWJE.gtfgru}uN׉\Y\H2 < 96.hovd53,zB<۽z*Sa0`.ig>'N!& 6s枓{Q'a2)y"rƪg|L hECQz Ғ b@- ,߾yXx&OvwEvOsl}?|pC6*x;l孂 iWqW7} kNSfT?B|8W! h){LJ07ȫ̗G ΋WpI%2 RK-%Ñ @%oܽ"kسbP!D,sO'x뾷8Swnã|_uz{G/A&$s*C6׷ܝ}NZޯ_g:'-8$8 |X !ښ@Y6uLH~ v;`1#;_&]Mq-I4'-)\vs ̇ŏ>@9%4BdNstqpc/O/ZsP, L7~ztu'cp" w!/ʲL/_dioPt&z)YK|Rөp0'Q=5axSЪvݓdL!,ݼJFpeWAv]n*$/C&=#sXϷh.+bD_ s !ONEW[3fw˛te!ܜgmD"mRY'cR=Z622|jמ8+z&\e Obv+ʠ}#8Fg D + V_]⹶/ߙQ<6=SVQhz!ҖaL CӒ8eƷj)eNT#.:F1q;>BiKyI_~uA2f 1_FRK$.^.Қ,2C9{6cot]픜jKEcAoI"6|P&H~.tprBtqZ+sUwrlqz/Y$ mv97!^e4 E{ ȹ9ݻ=  0G8bWn&p:5ԴBK4u̯y M8 tm{&gܥ| O*|Nud;9·ns<{/~X߲fmQx,s`,}ܷD?MEM&wh9zfE4ac^w"h^34nl;1XH.~7[fR{E`!O|2z^)۽Щu(/p L A>}2SKi'P'z;l,{iHt$0:,%DWv2@C"8N*z:R(8pXFZ*Ys9:͜WULTi[cMKQ>4"CҨHXLX i@'0Dk(||[tN-]lA DIakj}*8 i';yòb/{mHe1ahTm~4]yF$!R}; kޜ{O{KB7G *،L']9IMpOx8bU&p"ʛƸWPgY؎a ""~ӗSnjH\3 ;&K)UM76YBxEY,nm9߉dRITܐnbz\f"31Y_|X^I~rQl n)҂PTcsE~w1Nz;.*s1+_2]449yP6ؚTg6W޼}.]&U~v&=@/]ncy-H'?|r>vmkOP|̱0HTEAu B/;bo/S0,\Sj/Ikf]GmXt/c'@fPu8[tX2%7=]򈀟ãa|[b섙cKR5^_~SeOFOUg0J\O ' U0oPj.cf`\! 䑙:PnxSbT(y"JUp ͵mx#F[=.*HD󼆲iE&Q;!c.=^٠ʙ4뎩\$ۖC|ʵp\6r^qŠfhtg+ [0'E?XmLp$^Gү(UEL|5qŇ{su&`H=9y]-xqB6խ/>V< 9SJYGڭ9 v/Ӣw0`N^. 16(Jee.?e@ij!ޮT9!,WyiҔ>ɣA2 ?P!eE6Xn;˫Cfcw4$I#~m4nEv_$OEi[O|MxNfh\ n7&ty}idD *1_XKlZ~<"CܘG%:|hY͎?Us0" X+_:'X-W@Tm\M0dzѕ.d '8Dy,$wIW 5dd@$X̦H8&k A %yX\bZ_9Qcy;P6##Wy/˷Nt5f$CX}}fH@7~c3L)8lrIG-̤_r5|ܧr{8[Dmry&L:ju'(JW"ټ1hm^a{x!P"S-L <[u2}ζ~~w %fJH^^mɹkcA0Џ+mr4#o>[o%_unQ7g]W3 )XAn8_qbf_/>EL$Y>疷ur@sڍ`Jy*ch\P-i1;̿db 05P]RTnD~/SЦ!kog=t?P5| 9{'IG|>!amK.w) Bt9]7)DOu8s&S1H T<)+xlZ.q߽l+b- [g8վ2QE *2 S}@\,^b2 GRDd@qRA}*'eKj` cN,CTc=f4khT;(cb DN64jtn<`xxE|c Od1qBEL>1-iPrg0߿IUMCuLzxׁ;v\3;l30g$d2n> Y-ӊ/iOEg(BnB? YXX$ f( &A99#reM% .ժj\:HoV(~qM\2>-!.qBfْ5&٫\ּd. Y*$?_EŗUЭÚ׶u= r+ =%eb=x鬊Rz8ߟlRriSd VrHR.g6R!Iم[ YX;>&0r"u%3t?$lxl0iLO_OFӏ~ŀyȞ-͉O_Kː1/ #8S!Ec,hrFNG!KٿbDX7 J/SDdc[?a@VDEW|MRvgT#ռHn 5@ߏ7XNWlɉVxGm$m'% @0-AUp0KfZu-6BqA9J Gxc""EZ6 !b f"2Odm3ALh}~|O2F=u(a_?Qce6%l!yzpC/ISY.Qw9jj, 1Z$8&];[6oNL4MkrhO-eڏzy23Zq2scbj?o-ޛ儷ۍHe7fV%92Ў]$ų52Zn$B ~ZTcxfY"CL/IİJ󷻧˓ǏD9(;2 #v2lӅmVNe, Yڳ0`S,t%3f{ ߿`0 Z^YFCJP!Df0ݰY#@js␋ȤVXZބi<; bD %l=I1Ppy:_!NH:߉~Нν l0#:ңڜ1dNڕsDWÄ $+'DHNx-@DF^c ss:M&PIZ4PG .3 "D$+ry" |2WޒĎx5%Z, y0v e(f'c?Oؘ 3x4L~ EgژuHGs(?.O,$d !ɴa2F4:Æκ?NjoziHs<g?&H.7y1=OkUuI?O|_OV(8kYNQ xoA8G(?7zg$8a0 M20%pb|m37̀ & IENDB`http-types-2.12.0/tests/fixtures/unknown.custom000064400000000000000000000000370072674642500200250ustar 00000000000000this is an unknown text format http-types-2.12.0/tests/headers.rs000064400000000000000000000004200072674642500151760ustar 00000000000000// use http_types::{Response, StatusCode}; // #[test] // fn headers_cmp() { // let mut res = Response::new(StatusCode::Ok); // res.insert_header("content-type", "application/json"); // assert_eq!(res.header("content-type").unwrap(), "application/json"); // } http-types-2.12.0/tests/mime.rs000064400000000000000000000034120072674642500145160ustar 00000000000000#[cfg(features = "fs")] mod tests { use async_std::fs; use async_std::io; use http_types::{mime, Body, Response}; #[async_std::test] async fn guess_plain_text_mime() -> io::Result<()> { let body = Body::from_file("tests/fixtures/index.html").await?; let mut res = Response::new(200); res.set_body(body); assert_eq!(res.content_type(), Some(mime::HTML)); Ok(()) } #[async_std::test] async fn guess_binary_mime() -> http_types::Result<()> { let body = Body::from_file("tests/fixtures/nori.png").await?; let mut res = Response::new(200); res.set_body(body); assert_eq!(res.content_type(), Some(mime::PNG)); // Assert the file is correctly reset after we've peeked the bytes let left = fs::read("tests/fixtures/nori.png").await?; let right = res.body_bytes().await?; assert_eq!(left, right); Ok(()) } #[async_std::test] async fn guess_mime_fallback() -> io::Result<()> { let body = Body::from_file("tests/fixtures/unknown.custom").await?; let mut res = Response::new(200); res.set_body(body); assert_eq!(res.content_type(), Some(mime::BYTE_STREAM)); Ok(()) } #[async_std::test] async fn parse_empty_files() -> http_types::Result<()> { let body = Body::from_file("tests/fixtures/empty.custom").await?; let mut res = Response::new(200); res.set_body(body); assert_eq!(res.content_type(), Some(mime::BYTE_STREAM)); Ok(()) } // #[test] // fn match_mime_types() { // let req = Request::get("https://example.com"); // match req.content_type() { // Some(mime::JSON) => {} // _ => {} // } // } } http-types-2.12.0/tests/querystring.rs000064400000000000000000000026610072674642500161700ustar 00000000000000use http_types::{url::Url, Method}; use serde::Deserialize; #[derive(Deserialize)] struct Params { msg: String, } #[derive(Deserialize)] struct OptionalParams { _msg: Option, _time: Option, } #[test] fn successfully_deserialize_query() { let req = http_types::Request::new( Method::Get, Url::parse("http://example.com/?msg=Hello").unwrap(), ); let params = req.query::(); assert!(params.is_ok()); assert_eq!(params.unwrap().msg, "Hello"); } #[test] fn unsuccessfully_deserialize_query() { let req = http_types::Request::new(Method::Get, Url::parse("http://example.com/").unwrap()); let params = req.query::(); assert!(params.is_err()); assert_eq!( params.err().unwrap().to_string(), "failed with reason: missing field `msg`" ); } #[test] fn malformatted_query() { let req = http_types::Request::new( Method::Get, Url::parse("http://example.com/?error=should_fail").unwrap(), ); let params = req.query::(); assert!(params.is_err()); assert_eq!( params.err().unwrap().to_string(), "failed with reason: missing field `msg`" ); } #[test] fn empty_query_string_for_struct_with_no_required_fields() { let req = http_types::Request::new(Method::Get, Url::parse("http://example.com").unwrap()); let params = req.query::(); assert!(params.is_ok()); } http-types-2.12.0/tests/req_res_body.rs000064400000000000000000000016570072674642500162550ustar 00000000000000use futures_lite::{future, AsyncReadExt}; use http_types::{Body, Method, Request, Response, StatusCode, Url}; #[test] fn test_req_res_set_body() { let mut req = Request::new(Method::Get, Url::parse("http://example.com/").unwrap()); req.set_body(Body::empty()); let mut res = Response::new(StatusCode::Ok); res.set_body(req); let body = future::block_on(async move { let mut body = Vec::new(); res.read_to_end(&mut body).await.unwrap(); body }); assert!(body.is_empty()); } #[test] fn test_req_res_take_replace_body() { let mut req = Request::new(Method::Get, Url::parse("http://example.com/").unwrap()); req.take_body(); let mut res = Response::new(StatusCode::Ok); res.replace_body(req); let body = future::block_on(async move { let mut body = Vec::new(); res.read_to_end(&mut body).await.unwrap(); body }); assert!(body.is_empty()); } http-types-2.12.0/tests/security.rs000064400000000000000000000014460072674642500154430ustar 00000000000000use http_types::{security, Response, StatusCode}; #[test] fn security_test() { let mut policy = security::ContentSecurityPolicy::new(); policy .default_src(security::Source::SameOrigin) .default_src("areweasyncyet.rs") .script_src(security::Source::SameOrigin) .script_src(security::Source::UnsafeInline) .object_src(security::Source::None) .base_uri(security::Source::None) .upgrade_insecure_requests(); let mut res = Response::new(StatusCode::Ok); res.set_body("Hello, Chashu!"); security::default(&mut res); policy.apply(&mut res); assert_eq!(res["content-security-policy"], "base-uri 'none'; default-src 'self' areweasyncyet.rs; object-src 'none'; script-src 'self' 'unsafe-inline'; upgrade-insecure-requests"); }